wine/dlls/iphlpapi/ipstats.c

1378 lines
37 KiB
C

/* Copyright (C) 2003,2006 Juan Lang
* Copyright (C) 2007 TransGaming Technologies Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* This file implements statistics getting using the /proc filesystem exported
* by Linux, and maybe other OSes.
*/
#include "config.h"
#include "wine/port.h"
#include "wine/debug.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_SOCKETVAR_H
#include <sys/socketvar.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_NET_ROUTE_H
#include <net/route.h>
#endif
#ifdef HAVE_NET_IF_ARP_H
#include <net/if_arp.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_NETINET_TCP_FSM_H
#include <netinet/tcp_fsm.h>
#endif
#ifdef HAVE_NETINET_IN_PCB_H
#include <netinet/in_pcb.h>
#endif
#ifdef HAVE_NETINET_TCP_VAR_H
#include <netinet/tcp_var.h>
#endif
#ifdef HAVE_NETINET_IP_VAR_H
#include <netinet/ip_var.h>
#endif
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif
#ifndef ROUNDUP
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#endif
#ifndef ADVANCE
#define ADVANCE(x, n) (x += ROUNDUP(((struct sockaddr *)n)->sa_len))
#endif
#include "windef.h"
#include "winbase.h"
#include "iprtrmib.h"
#include "ifenum.h"
#include "ipstats.h"
#ifndef HAVE_NETINET_TCP_FSM_H
#define TCPS_ESTABLISHED 1
#define TCPS_SYN_SENT 2
#define TCPS_SYN_RECEIVED 3
#define TCPS_FIN_WAIT_1 4
#define TCPS_FIN_WAIT_2 5
#define TCPS_TIME_WAIT 6
#define TCPS_CLOSED 7
#define TCPS_CLOSE_WAIT 8
#define TCPS_LAST_ACK 9
#define TCPS_LISTEN 10
#define TCPS_CLOSING 11
#endif
WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry)
{
FILE *fp;
if (!name)
return ERROR_INVALID_PARAMETER;
if (!entry)
return ERROR_INVALID_PARAMETER;
/* get interface stats from /proc/net/dev, no error if can't
no inUnknownProtos, outNUcastPkts, outQLen */
fp = fopen("/proc/net/dev", "r");
if (fp) {
char buf[512] = { 0 }, *ptr;
int nameLen = strlen(name), nameFound = 0;
ptr = fgets(buf, sizeof(buf), fp);
while (ptr && !nameFound) {
while (*ptr && isspace(*ptr))
ptr++;
if (strncasecmp(ptr, name, nameLen) == 0 && *(ptr + nameLen) == ':')
nameFound = 1;
else
ptr = fgets(buf, sizeof(buf), fp);
}
if (nameFound) {
char *endPtr;
ptr += nameLen + 1;
if (ptr && *ptr) {
entry->dwInOctets = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
entry->dwInUcastPkts = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
entry->dwInErrors = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
entry->dwInDiscards = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
strtoul(ptr, &endPtr, 10); /* skip */
ptr = endPtr;
}
if (ptr && *ptr) {
strtoul(ptr, &endPtr, 10); /* skip */
ptr = endPtr;
}
if (ptr && *ptr) {
strtoul(ptr, &endPtr, 10); /* skip */
ptr = endPtr;
}
if (ptr && *ptr) {
entry->dwInNUcastPkts = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
entry->dwOutOctets = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
entry->dwOutUcastPkts = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
entry->dwOutErrors = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
entry->dwOutDiscards = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
}
fclose(fp);
}
else
{
ERR ("unimplemented!\n");
return ERROR_NOT_SUPPORTED;
}
return NO_ERROR;
}
DWORD getICMPStats(MIB_ICMP *stats)
{
FILE *fp;
if (!stats)
return ERROR_INVALID_PARAMETER;
memset(stats, 0, sizeof(MIB_ICMP));
/* get most of these stats from /proc/net/snmp, no error if can't */
fp = fopen("/proc/net/snmp", "r");
if (fp) {
static const char hdr[] = "Icmp:";
char buf[512] = { 0 }, *ptr;
do {
ptr = fgets(buf, sizeof(buf), fp);
} while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
if (ptr) {
/* last line was a header, get another */
ptr = fgets(buf, sizeof(buf), fp);
if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
char *endPtr;
ptr += sizeof(hdr);
if (ptr && *ptr) {
stats->stats.icmpInStats.dwMsgs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwErrors = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwRedirects = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpInStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwMsgs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwErrors = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwRedirects = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->stats.icmpOutStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
}
}
fclose(fp);
}
else
{
ERR ("unimplemented!\n");
return ERROR_NOT_SUPPORTED;
}
return NO_ERROR;
}
DWORD getIPStats(PMIB_IPSTATS stats)
{
FILE *fp;
if (!stats)
return ERROR_INVALID_PARAMETER;
memset(stats, 0, sizeof(MIB_IPSTATS));
stats->dwNumIf = stats->dwNumAddr = getNumInterfaces();
stats->dwNumRoutes = getNumRoutes();
/* get most of these stats from /proc/net/snmp, no error if can't */
fp = fopen("/proc/net/snmp", "r");
if (fp) {
static const char hdr[] = "Ip:";
char buf[512] = { 0 }, *ptr;
do {
ptr = fgets(buf, sizeof(buf), fp);
} while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
if (ptr) {
/* last line was a header, get another */
ptr = fgets(buf, sizeof(buf), fp);
if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
char *endPtr;
ptr += sizeof(hdr);
if (ptr && *ptr) {
stats->dwForwarding = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwDefaultTTL = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwInReceives = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwInHdrErrors = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwInAddrErrors = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwForwDatagrams = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwInUnknownProtos = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwInDiscards = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwInDelivers = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwOutRequests = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwOutDiscards = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwOutNoRoutes = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwReasmTimeout = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwReasmReqds = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwReasmOks = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwReasmFails = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwFragOks = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwFragFails = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwFragCreates = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
/* hmm, no routingDiscards */
}
}
fclose(fp);
}
else
{
ERR ("unimplemented!\n");
return ERROR_NOT_SUPPORTED;
}
return NO_ERROR;
}
DWORD getTCPStats(MIB_TCPSTATS *stats)
{
FILE *fp;
if (!stats)
return ERROR_INVALID_PARAMETER;
memset(stats, 0, sizeof(MIB_TCPSTATS));
/* get from /proc/net/snmp, no error if can't */
fp = fopen("/proc/net/snmp", "r");
if (fp) {
static const char hdr[] = "Tcp:";
char buf[512] = { 0 }, *ptr;
do {
ptr = fgets(buf, sizeof(buf), fp);
} while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
if (ptr) {
/* last line was a header, get another */
ptr = fgets(buf, sizeof(buf), fp);
if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
char *endPtr;
ptr += sizeof(hdr);
if (ptr && *ptr) {
stats->dwRtoAlgorithm = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwMaxConn = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwActiveOpens = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwPassiveOpens = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwAttemptFails = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwEstabResets = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwCurrEstab = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwInSegs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwOutSegs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwRetransSegs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwInErrs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwOutRsts = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
stats->dwNumConns = getNumTcpEntries();
}
}
fclose(fp);
}
else
{
ERR ("unimplemented!\n");
return ERROR_NOT_SUPPORTED;
}
return NO_ERROR;
}
DWORD getUDPStats(MIB_UDPSTATS *stats)
{
FILE *fp;
if (!stats)
return ERROR_INVALID_PARAMETER;
memset(stats, 0, sizeof(MIB_UDPSTATS));
/* get from /proc/net/snmp, no error if can't */
fp = fopen("/proc/net/snmp", "r");
if (fp) {
static const char hdr[] = "Udp:";
char buf[512] = { 0 }, *ptr;
do {
ptr = fgets(buf, sizeof(buf), fp);
} while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
if (ptr) {
/* last line was a header, get another */
ptr = fgets(buf, sizeof(buf), fp);
if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
char *endPtr;
ptr += sizeof(hdr);
if (ptr && *ptr) {
stats->dwInDatagrams = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwNoPorts = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwInErrors = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwOutDatagrams = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
if (ptr && *ptr) {
stats->dwNumAddrs = strtoul(ptr, &endPtr, 10);
ptr = endPtr;
}
}
}
fclose(fp);
}
else
{
ERR ("unimplemented!\n");
return ERROR_NOT_SUPPORTED;
}
return NO_ERROR;
}
static DWORD getNumWithOneHeader(const char *filename)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
size_t Len = 0;
char *Buf;
struct xinpgen *pXIG, *pOrigXIG;
int Protocol;
DWORD NumEntries = 0;
if (!strcmp (filename, "net.inet.tcp.pcblist"))
Protocol = IPPROTO_TCP;
else if (!strcmp (filename, "net.inet.udp.pcblist"))
Protocol = IPPROTO_UDP;
else
{
ERR ("Unsupported mib '%s', needs protocol mapping\n",
filename);
return 0;
}
if (sysctlbyname (filename, NULL, &Len, NULL, 0) < 0)
{
WARN ("Unable to read '%s' via sysctlbyname\n", filename);
return 0;
}
Buf = HeapAlloc (GetProcessHeap (), 0, Len);
if (!Buf)
{
ERR ("Out of memory!\n");
return 0;
}
if (sysctlbyname (filename, Buf, &Len, NULL, 0) < 0)
{
ERR ("Failure to read '%s' via sysctlbyname!\n", filename);
HeapFree (GetProcessHeap (), 0, Buf);
return 0;
}
/* Might be nothing here; first entry is just a header it seems */
if (Len <= sizeof (struct xinpgen))
{
HeapFree (GetProcessHeap (), 0, Buf);
return 0;
}
pOrigXIG = (struct xinpgen *)Buf;
pXIG = pOrigXIG;
for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
pXIG->xig_len > sizeof (struct xinpgen);
pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
{
struct tcpcb *pTCPData = NULL;
struct inpcb *pINData;
struct xsocket *pSockData;
if (Protocol == IPPROTO_TCP)
{
pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
pINData = &((struct xtcpcb *)pXIG)->xt_inp;
pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
}
else
{
pINData = &((struct xinpcb *)pXIG)->xi_inp;
pSockData = &((struct xinpcb *)pXIG)->xi_socket;
}
/* Ignore sockets for other protocols */
if (pSockData->xso_protocol != Protocol)
continue;
/* Ignore PCBs that were freed while generating the data */
if (pINData->inp_gencnt > pOrigXIG->xig_gen)
continue;
/* we're only interested in IPv4 addresses */
if (!(pINData->inp_vflag & INP_IPV4) ||
(pINData->inp_vflag & INP_IPV6))
continue;
/* If all 0's, skip it */
if (!pINData->inp_laddr.s_addr &&
!pINData->inp_lport &&
!pINData->inp_faddr.s_addr &&
!pINData->inp_fport)
continue;
NumEntries++;
}
HeapFree (GetProcessHeap (), 0, Buf);
return NumEntries;
#else
FILE *fp;
int ret = 0;
fp = fopen(filename, "r");
if (fp) {
char buf[512] = { 0 }, *ptr;
ptr = fgets(buf, sizeof(buf), fp);
if (ptr) {
do {
ptr = fgets(buf, sizeof(buf), fp);
if (ptr)
ret++;
} while (ptr);
}
fclose(fp);
}
else
ERR ("Unable to open '%s' to count entries!\n", filename);
return ret;
#endif
}
DWORD getNumRoutes(void)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
size_t needed;
char *buf, *lim, *next;
struct rt_msghdr *rtm;
DWORD RouteCount = 0;
if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
{
ERR ("sysctl 1 failed!\n");
return 0;
}
buf = HeapAlloc (GetProcessHeap (), 0, needed);
if (!buf) return 0;
if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
{
ERR ("sysctl 2 failed!\n");
HeapFree (GetProcessHeap (), 0, buf);
return 0;
}
lim = buf + needed;
for (next = buf; next < lim; next += rtm->rtm_msglen)
{
rtm = (struct rt_msghdr *)next;
if (rtm->rtm_type != RTM_GET)
{
WARN ("Got unexpected message type 0x%x!\n",
rtm->rtm_type);
continue;
}
/* Ignore all entries except for gateway routes which aren't
multicast */
if (!(rtm->rtm_flags & RTF_GATEWAY) || (rtm->rtm_flags & RTF_MULTICAST))
continue;
RouteCount++;
}
HeapFree (GetProcessHeap (), 0, buf);
return RouteCount;
#else
return getNumWithOneHeader("/proc/net/route");
#endif
}
DWORD getRouteTable(PMIB_IPFORWARDTABLE *ppIpForwardTable, HANDLE heap,
DWORD flags)
{
DWORD ret;
if (!ppIpForwardTable)
ret = ERROR_INVALID_PARAMETER;
else {
DWORD numRoutes = getNumRoutes();
DWORD size = sizeof(MIB_IPFORWARDTABLE);
PMIB_IPFORWARDTABLE table;
if (numRoutes > 1)
size += (numRoutes - 1) * sizeof(MIB_IPFORWARDROW);
table = HeapAlloc(heap, flags, size);
if (table) {
#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
size_t needed;
char *buf, *lim, *next, *addrPtr;
struct rt_msghdr *rtm;
if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
{
ERR ("sysctl 1 failed!\n");
HeapFree (GetProcessHeap (), 0, table);
return NO_ERROR;
}
buf = HeapAlloc (GetProcessHeap (), 0, needed);
if (!buf)
{
HeapFree (GetProcessHeap (), 0, table);
return ERROR_OUTOFMEMORY;
}
if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
{
ERR ("sysctl 2 failed!\n");
HeapFree (GetProcessHeap (), 0, table);
HeapFree (GetProcessHeap (), 0, buf);
return NO_ERROR;
}
*ppIpForwardTable = table;
table->dwNumEntries = 0;
lim = buf + needed;
for (next = buf; next < lim; next += rtm->rtm_msglen)
{
int i;
rtm = (struct rt_msghdr *)next;
if (rtm->rtm_type != RTM_GET)
{
WARN ("Got unexpected message type 0x%x!\n",
rtm->rtm_type);
continue;
}
/* Ignore all entries except for gateway routes which aren't
multicast */
if (!(rtm->rtm_flags & RTF_GATEWAY) ||
(rtm->rtm_flags & RTF_MULTICAST))
continue;
memset (&table->table[table->dwNumEntries], 0,
sizeof (MIB_IPFORWARDROW));
table->table[table->dwNumEntries].dwForwardIfIndex = rtm->rtm_index;
table->table[table->dwNumEntries].dwForwardType =
MIB_IPROUTE_TYPE_INDIRECT;
table->table[table->dwNumEntries].dwForwardMetric1 =
rtm->rtm_rmx.rmx_hopcount;
table->table[table->dwNumEntries].dwForwardProto =
MIB_IPPROTO_LOCAL;
addrPtr = (char *)(rtm + 1);
for (i = 1; i; i <<= 1)
{
struct sockaddr *sa;
DWORD addr;
if (!(i & rtm->rtm_addrs))
continue;
sa = (struct sockaddr *)addrPtr;
ADVANCE (addrPtr, sa);
/* default routes are encoded by length-zero sockaddr */
if (sa->sa_len == 0)
addr = 0;
else if (sa->sa_family != AF_INET)
{
ERR ("Received unsupported sockaddr family 0x%x\n",
sa->sa_family);
addr = 0;
}
else
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
addr = sin->sin_addr.s_addr;
}
switch (i)
{
case RTA_DST:
table->table[table->dwNumEntries].dwForwardDest = addr;
break;
case RTA_GATEWAY:
table->table[table->dwNumEntries].dwForwardNextHop = addr;
break;
case RTA_NETMASK:
table->table[table->dwNumEntries].dwForwardMask = addr;
break;
default:
ERR ("Unexpected address type 0x%x\n", i);
}
}
table->dwNumEntries++;
}
HeapFree (GetProcessHeap (), 0, buf);
ret = NO_ERROR;
#else
FILE *fp;
ret = NO_ERROR;
*ppIpForwardTable = table;
table->dwNumEntries = 0;
/* get from /proc/net/route, no error if can't */
fp = fopen("/proc/net/route", "r");
if (fp) {
char buf[512] = { 0 }, *ptr;
/* skip header line */
ptr = fgets(buf, sizeof(buf), fp);
while (ptr && table->dwNumEntries < numRoutes) {
memset(&table->table[table->dwNumEntries], 0,
sizeof(MIB_IPFORWARDROW));
ptr = fgets(buf, sizeof(buf), fp);
if (ptr) {
DWORD index;
while (!isspace(*ptr))
ptr++;
*ptr = '\0';
ptr++;
if (getInterfaceIndexByName(buf, &index) == NO_ERROR) {
char *endPtr;
table->table[table->dwNumEntries].dwForwardIfIndex = index;
if (*ptr) {
table->table[table->dwNumEntries].dwForwardDest =
strtoul(ptr, &endPtr, 16);
ptr = endPtr;
}
if (ptr && *ptr) {
table->table[table->dwNumEntries].dwForwardNextHop =
strtoul(ptr, &endPtr, 16);
ptr = endPtr;
}
if (ptr && *ptr) {
DWORD flags = strtoul(ptr, &endPtr, 16);
if (!(flags & RTF_UP))
table->table[table->dwNumEntries].dwForwardType =
MIB_IPROUTE_TYPE_INVALID;
else if (flags & RTF_GATEWAY)
table->table[table->dwNumEntries].dwForwardType =
MIB_IPROUTE_TYPE_INDIRECT;
else
table->table[table->dwNumEntries].dwForwardType =
MIB_IPROUTE_TYPE_DIRECT;
ptr = endPtr;
}
if (ptr && *ptr) {
strtoul(ptr, &endPtr, 16); /* refcount, skip */
ptr = endPtr;
}
if (ptr && *ptr) {
strtoul(ptr, &endPtr, 16); /* use, skip */
ptr = endPtr;
}
if (ptr && *ptr) {
table->table[table->dwNumEntries].dwForwardMetric1 =
strtoul(ptr, &endPtr, 16);
ptr = endPtr;
}
if (ptr && *ptr) {
table->table[table->dwNumEntries].dwForwardMask =
strtoul(ptr, &endPtr, 16);
ptr = endPtr;
}
/* FIXME: other protos might be appropriate, e.g. the default
* route is typically set with MIB_IPPROTO_NETMGMT instead */
table->table[table->dwNumEntries].dwForwardProto =
MIB_IPPROTO_LOCAL;
table->dwNumEntries++;
}
}
}
fclose(fp);
}
else
{
ERR ("unimplemented!\n");
return ERROR_NOT_SUPPORTED;
}
#endif
}
else
ret = ERROR_OUTOFMEMORY;
}
return ret;
}
DWORD getNumArpEntries(void)
{
return getNumWithOneHeader("/proc/net/arp");
}
DWORD getArpTable(PMIB_IPNETTABLE *ppIpNetTable, HANDLE heap, DWORD flags)
{
DWORD ret;
#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
ERR ("unimplemented!\n");
return ERROR_NOT_SUPPORTED;
#endif
if (!ppIpNetTable)
ret = ERROR_INVALID_PARAMETER;
else {
DWORD numEntries = getNumArpEntries();
DWORD size = sizeof(MIB_IPNETTABLE);
PMIB_IPNETTABLE table;
if (numEntries > 1)
size += (numEntries - 1) * sizeof(MIB_IPNETROW);
table = HeapAlloc(heap, flags, size);
if (table) {
FILE *fp;
ret = NO_ERROR;
*ppIpNetTable = table;
table->dwNumEntries = 0;
/* get from /proc/net/arp, no error if can't */
fp = fopen("/proc/net/arp", "r");
if (fp) {
char buf[512] = { 0 }, *ptr;
/* skip header line */
ptr = fgets(buf, sizeof(buf), fp);
while (ptr && table->dwNumEntries < numEntries) {
ptr = fgets(buf, sizeof(buf), fp);
if (ptr) {
char *endPtr;
memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_IPNETROW));
table->table[table->dwNumEntries].dwAddr = inet_addr(ptr);
while (ptr && *ptr && !isspace(*ptr))
ptr++;
if (ptr && *ptr) {
strtoul(ptr, &endPtr, 16); /* hw type (skip) */
ptr = endPtr;
}
if (ptr && *ptr) {
DWORD flags = strtoul(ptr, &endPtr, 16);
#ifdef ATF_COM
if (flags & ATF_COM)
table->table[table->dwNumEntries].dwType =
MIB_IPNET_TYPE_DYNAMIC;
else
#endif
#ifdef ATF_PERM
if (flags & ATF_PERM)
table->table[table->dwNumEntries].dwType =
MIB_IPNET_TYPE_STATIC;
else
#endif
table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_OTHER;
ptr = endPtr;
}
while (ptr && *ptr && isspace(*ptr))
ptr++;
while (ptr && *ptr && !isspace(*ptr)) {
DWORD byte = strtoul(ptr, &endPtr, 16);
if (endPtr && *endPtr) {
endPtr++;
table->table[table->dwNumEntries].bPhysAddr[
table->table[table->dwNumEntries].dwPhysAddrLen++] =
byte & 0x0ff;
}
ptr = endPtr;
}
if (ptr && *ptr) {
strtoul(ptr, &endPtr, 16); /* mask (skip) */
ptr = endPtr;
}
getInterfaceIndexByName(ptr,
&table->table[table->dwNumEntries].dwIndex);
table->dwNumEntries++;
}
}
fclose(fp);
}
else
ret = ERROR_NOT_SUPPORTED;
}
else
ret = ERROR_OUTOFMEMORY;
}
return ret;
}
DWORD getNumUdpEntries(void)
{
return getNumWithOneHeader("/proc/net/udp");
}
DWORD getUdpTable(PMIB_UDPTABLE *ppUdpTable, HANDLE heap, DWORD flags)
{
DWORD ret;
#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
ERR ("unimplemented!\n");
return ERROR_NOT_SUPPORTED;
#endif
if (!ppUdpTable)
ret = ERROR_INVALID_PARAMETER;
else {
DWORD numEntries = getNumUdpEntries();
DWORD size = sizeof(MIB_UDPTABLE);
PMIB_UDPTABLE table;
if (numEntries > 1)
size += (numEntries - 1) * sizeof(MIB_UDPROW);
table = HeapAlloc(heap, flags, size);
if (table) {
FILE *fp;
ret = NO_ERROR;
*ppUdpTable = table;
table->dwNumEntries = 0;
/* get from /proc/net/udp, no error if can't */
fp = fopen("/proc/net/udp", "r");
if (fp) {
char buf[512] = { 0 }, *ptr;
/* skip header line */
ptr = fgets(buf, sizeof(buf), fp);
while (ptr && table->dwNumEntries < numEntries) {
memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_UDPROW));
ptr = fgets(buf, sizeof(buf), fp);
if (ptr) {
char *endPtr;
if (ptr && *ptr) {
strtoul(ptr, &endPtr, 16); /* skip */
ptr = endPtr;
}
if (ptr && *ptr) {
ptr++;
table->table[table->dwNumEntries].dwLocalAddr = strtoul(ptr,
&endPtr, 16);
ptr = endPtr;
}
if (ptr && *ptr) {
ptr++;
table->table[table->dwNumEntries].dwLocalPort = strtoul(ptr,
&endPtr, 16);
ptr = endPtr;
}
table->dwNumEntries++;
}
}
fclose(fp);
}
else
ret = ERROR_NOT_SUPPORTED;
}
else
ret = ERROR_OUTOFMEMORY;
}
return ret;
}
DWORD getNumTcpEntries(void)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
return getNumWithOneHeader ("net.inet.tcp.pcblist");
#else
return getNumWithOneHeader ("/proc/net/tcp");
#endif
}
/* Why not a lookup table? Because the TCPS_* constants are different
on different platforms */
static DWORD TCPStateToMIBState (int state)
{
switch (state)
{
case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB;
case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT;
case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD;
case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1;
case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2;
case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT;
case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT;
case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK;
case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN;
case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING;
default:
case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED;
}
}
DWORD getTcpTable(PMIB_TCPTABLE *ppTcpTable, DWORD maxEntries, HANDLE heap,
DWORD flags)
{
DWORD numEntries;
PMIB_TCPTABLE table;
#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
size_t Len = 0;
char *Buf;
struct xinpgen *pXIG, *pOrigXIG;
#else
FILE *fp;
char buf[512] = { 0 }, *ptr;
#endif
if (!ppTcpTable)
return ERROR_INVALID_PARAMETER;
numEntries = getNumTcpEntries ();
if (!*ppTcpTable)
{
DWORD size = sizeof(MIB_TCPTABLE);
if (numEntries > 1)
size += (numEntries - 1) * sizeof (MIB_TCPROW);
*ppTcpTable = HeapAlloc (heap, flags, size);
if (!*ppTcpTable)
{
ERR ("Out of memory!\n");
return ERROR_OUTOFMEMORY;
}
maxEntries = numEntries;
}
table = *ppTcpTable;
table->dwNumEntries = 0;
if (!numEntries)
return NO_ERROR;
#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0)
{
ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
return ERROR_OUTOFMEMORY;
}
Buf = HeapAlloc (GetProcessHeap (), 0, Len);
if (!Buf)
{
ERR ("Out of memory!\n");
return ERROR_OUTOFMEMORY;
}
if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0)
{
ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
HeapFree (GetProcessHeap (), 0, Buf);
return ERROR_OUTOFMEMORY;
}
/* Might be nothing here; first entry is just a header it seems */
if (Len <= sizeof (struct xinpgen))
{
HeapFree (GetProcessHeap (), 0, Buf);
return NO_ERROR;
}
pOrigXIG = (struct xinpgen *)Buf;
pXIG = pOrigXIG;
for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
(pXIG->xig_len > sizeof (struct xinpgen)) &&
(table->dwNumEntries < maxEntries);
pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
{
struct tcpcb *pTCPData = NULL;
struct inpcb *pINData;
struct xsocket *pSockData;
pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
pINData = &((struct xtcpcb *)pXIG)->xt_inp;
pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
/* Ignore sockets for other protocols */
if (pSockData->xso_protocol != IPPROTO_TCP)
continue;
/* Ignore PCBs that were freed while generating the data */
if (pINData->inp_gencnt > pOrigXIG->xig_gen)
continue;
/* we're only interested in IPv4 addresses */
if (!(pINData->inp_vflag & INP_IPV4) ||
(pINData->inp_vflag & INP_IPV6))
continue;
/* If all 0's, skip it */
if (!pINData->inp_laddr.s_addr &&
!pINData->inp_lport &&
!pINData->inp_faddr.s_addr &&
!pINData->inp_fport)
continue;
/* Fill in structure details */
table->table[table->dwNumEntries].dwLocalAddr =
pINData->inp_laddr.s_addr;
table->table[table->dwNumEntries].dwLocalPort =
pINData->inp_lport;
table->table[table->dwNumEntries].dwRemoteAddr =
pINData->inp_faddr.s_addr;
table->table[table->dwNumEntries].dwRemotePort =
pINData->inp_fport;
table->table[table->dwNumEntries].dwState =
TCPStateToMIBState (pTCPData->t_state);
table->dwNumEntries++;
}
HeapFree (GetProcessHeap (), 0, Buf);
#else
/* get from /proc/net/tcp, no error if can't */
fp = fopen("/proc/net/tcp", "r");
if (!fp)
return ERROR_NOT_SUPPORTED;
/* skip header line */
ptr = fgets(buf, sizeof(buf), fp);
while (ptr && table->dwNumEntries < maxEntries) {
memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_TCPROW));
ptr = fgets(buf, sizeof(buf), fp);
if (ptr) {
char *endPtr;
while (ptr && *ptr && *ptr != ':')
ptr++;
if (ptr && *ptr)
ptr++;
if (ptr && *ptr) {
table->table[table->dwNumEntries].dwLocalAddr =
strtoul(ptr, &endPtr, 16);
ptr = endPtr;
}
if (ptr && *ptr) {
ptr++;
table->table[table->dwNumEntries].dwLocalPort =
htons ((unsigned short)strtoul(ptr, &endPtr, 16));
ptr = endPtr;
}
if (ptr && *ptr) {
table->table[table->dwNumEntries].dwRemoteAddr =
strtoul(ptr, &endPtr, 16);
ptr = endPtr;
}
if (ptr && *ptr) {
ptr++;
table->table[table->dwNumEntries].dwRemotePort =
htons ((unsigned short)strtoul(ptr, &endPtr, 16));
ptr = endPtr;
}
if (ptr && *ptr) {
DWORD state = strtoul(ptr, &endPtr, 16);
table->table[table->dwNumEntries].dwState =
TCPStateToMIBState (state);
ptr = endPtr;
}
table->dwNumEntries++;
}
}
fclose(fp);
#endif
return NO_ERROR;
}