2879 lines
101 KiB
C
2879 lines
101 KiB
C
/*
|
|
* Copyright (C) 2003,2006 Juan Lang
|
|
* Copyright (C) 2007 TransGaming Technologies Inc.
|
|
* Copyright (C) 2009 Alexandre Julliard
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_DIRENT_H
|
|
#include <dirent.h>
|
|
#endif
|
|
#ifdef HAVE_ALIAS_H
|
|
#include <alias.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_SOCKETVAR_H
|
|
#include <sys/socketvar.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TIMEOUT_H
|
|
#include <sys/timeout.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IN_SYSTM_H
|
|
#include <netinet/in_systm.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_IF_DL_H
|
|
#include <net/if_dl.h>
|
|
#endif
|
|
#ifdef HAVE_NET_IF_TYPES_H
|
|
#include <net/if_types.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_IF_ETHER_H
|
|
#include <netinet/if_ether.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IF_INARP_H
|
|
#include <netinet/if_inarp.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IP_H
|
|
#include <netinet/ip.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_TCP_H
|
|
#include <netinet/tcp.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IP_VAR_H
|
|
#include <netinet/ip_var.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_TIMER_H
|
|
#include <netinet/tcp_timer.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_TCP_VAR_H
|
|
#include <netinet/tcp_var.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IP_ICMP_H
|
|
#include <netinet/ip_icmp.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_ICMP_VAR_H
|
|
#include <netinet/icmp_var.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_UDP_H
|
|
#include <netinet/udp.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_UDP_VAR_H
|
|
#include <netinet/udp_var.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_PROTOSW_H
|
|
#include <sys/protosw.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_SYSCTL_H
|
|
#include <sys/sysctl.h>
|
|
#endif
|
|
#ifdef HAVE_KSTAT_H
|
|
#include <kstat.h>
|
|
#endif
|
|
#ifdef HAVE_INET_MIB2_H
|
|
#include <inet/mib2.h>
|
|
#endif
|
|
#ifdef HAVE_STROPTS_H
|
|
#include <stropts.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TIHDR_H
|
|
#include <sys/tihdr.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
#include <sys/param.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_QUEUE_H
|
|
#include <sys/queue.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_USER_H
|
|
/* Make sure the definitions of struct kinfo_proc are the same. */
|
|
#include <sys/user.h>
|
|
#endif
|
|
#ifdef HAVE_LIBPROCSTAT_H
|
|
#include <libprocstat.h>
|
|
#endif
|
|
#ifdef HAVE_LIBPROC_H
|
|
#include <libproc.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 "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#define NONAMELESSUNION
|
|
#define USE_WS_PREFIX
|
|
#include "winsock2.h"
|
|
#include "ws2ipdef.h"
|
|
#include "ifenum.h"
|
|
#include "ipstats.h"
|
|
#include "iphlpapi.h"
|
|
|
|
#include "wine/debug.h"
|
|
#include "wine/server.h"
|
|
#include "wine/unicode.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
|
|
|
|
#ifndef RTF_MULTICAST
|
|
#define RTF_MULTICAST 0 /* Not available on NetBSD/OpenBSD */
|
|
#endif
|
|
|
|
#ifndef RTF_LLINFO
|
|
#define RTF_LLINFO 0 /* Not available on FreeBSD 8 and above */
|
|
#endif
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
|
|
|
|
#ifdef HAVE_LIBKSTAT
|
|
static DWORD kstat_get_ui32( kstat_t *ksp, const char *name )
|
|
{
|
|
unsigned int i;
|
|
kstat_named_t *data = ksp->ks_data;
|
|
|
|
for (i = 0; i < ksp->ks_ndata; i++)
|
|
if (!strcmp( data[i].name, name )) return data[i].value.ui32;
|
|
return 0;
|
|
}
|
|
|
|
static ULONGLONG kstat_get_ui64( kstat_t *ksp, const char *name )
|
|
{
|
|
unsigned int i;
|
|
kstat_named_t *data = ksp->ks_data;
|
|
|
|
for (i = 0; i < ksp->ks_ndata; i++)
|
|
if (!strcmp( data[i].name, name )) return data[i].value.ui64;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
|
|
static int open_streams_mib( const char *proto )
|
|
{
|
|
int fd;
|
|
struct strbuf buf;
|
|
struct request
|
|
{
|
|
struct T_optmgmt_req req_header;
|
|
struct opthdr opt_header;
|
|
} request;
|
|
|
|
if ((fd = open( "/dev/arp", O_RDWR )) == -1)
|
|
{
|
|
WARN( "could not open /dev/arp: %s\n", strerror(errno) );
|
|
return -1;
|
|
}
|
|
if (proto) ioctl( fd, I_PUSH, proto );
|
|
|
|
request.req_header.PRIM_type = T_SVR4_OPTMGMT_REQ;
|
|
request.req_header.OPT_length = sizeof(request.opt_header);
|
|
request.req_header.OPT_offset = FIELD_OFFSET( struct request, opt_header );
|
|
request.req_header.MGMT_flags = T_CURRENT;
|
|
request.opt_header.level = MIB2_IP;
|
|
request.opt_header.name = 0;
|
|
request.opt_header.len = 0;
|
|
|
|
buf.len = sizeof(request);
|
|
buf.buf = (caddr_t)&request;
|
|
if (putmsg( fd, &buf, NULL, 0 ) == -1)
|
|
{
|
|
WARN( "putmsg: %s\n", strerror(errno) );
|
|
close( fd );
|
|
fd = -1;
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
static void *read_mib_entry( int fd, int level, int name, int *len )
|
|
{
|
|
struct strbuf buf;
|
|
void *data;
|
|
int ret, flags = 0;
|
|
|
|
struct reply
|
|
{
|
|
struct T_optmgmt_ack ack_header;
|
|
struct opthdr opt_header;
|
|
} reply;
|
|
|
|
for (;;)
|
|
{
|
|
buf.maxlen = sizeof(reply);
|
|
buf.buf = (caddr_t)&reply;
|
|
if ((ret = getmsg( fd, &buf, NULL, &flags )) < 0) return NULL;
|
|
if (!(ret & MOREDATA)) return NULL;
|
|
if (reply.ack_header.PRIM_type != T_OPTMGMT_ACK) return NULL;
|
|
if (buf.len < sizeof(reply.ack_header)) return NULL;
|
|
if (reply.ack_header.OPT_length < sizeof(reply.opt_header)) return NULL;
|
|
|
|
if (!(data = HeapAlloc( GetProcessHeap(), 0, reply.opt_header.len ))) return NULL;
|
|
buf.maxlen = reply.opt_header.len;
|
|
buf.buf = (caddr_t)data;
|
|
flags = 0;
|
|
if (getmsg( fd, NULL, &buf, &flags ) >= 0 &&
|
|
reply.opt_header.level == level &&
|
|
reply.opt_header.name == name)
|
|
{
|
|
*len = buf.len;
|
|
return data;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, data );
|
|
}
|
|
}
|
|
#endif /* HAVE_SYS_TIHDR_H && T_OPTMGMT_ACK */
|
|
|
|
DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry)
|
|
{
|
|
DWORD ret = ERROR_NOT_SUPPORTED;
|
|
|
|
if (!name || !entry) return ERROR_INVALID_PARAMETER;
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/dev", "r")))
|
|
{
|
|
DWORD skip;
|
|
char buf[512], *ptr;
|
|
int nameLen = strlen(name);
|
|
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
while (*ptr && isspace(*ptr)) ptr++;
|
|
if (_strnicmp(ptr, name, nameLen) == 0 && *(ptr + nameLen) == ':')
|
|
{
|
|
ptr += nameLen + 1;
|
|
sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u",
|
|
&entry->dwInOctets, &entry->dwInUcastPkts,
|
|
&entry->dwInErrors, &entry->dwInDiscards,
|
|
&skip, &skip, &skip,
|
|
&entry->dwInNUcastPkts, &entry->dwOutOctets,
|
|
&entry->dwOutUcastPkts, &entry->dwOutErrors,
|
|
&entry->dwOutDiscards );
|
|
break;
|
|
}
|
|
}
|
|
fclose(fp);
|
|
ret = NO_ERROR;
|
|
}
|
|
}
|
|
#elif defined(HAVE_LIBKSTAT)
|
|
{
|
|
kstat_ctl_t *kc;
|
|
kstat_t *ksp;
|
|
|
|
if ((kc = kstat_open()) &&
|
|
(ksp = kstat_lookup( kc, NULL, -1, (char *)name )) &&
|
|
kstat_read( kc, ksp, NULL ) != -1 &&
|
|
ksp->ks_type == KSTAT_TYPE_NAMED)
|
|
{
|
|
entry->dwMtu = 1500; /* FIXME */
|
|
entry->dwSpeed = min( kstat_get_ui64( ksp, "ifspeed" ), ~0u );
|
|
entry->dwInOctets = kstat_get_ui32( ksp, "rbytes" );
|
|
entry->dwInNUcastPkts = kstat_get_ui32( ksp, "multircv" );
|
|
entry->dwInNUcastPkts += kstat_get_ui32( ksp, "brdcstrcv" );
|
|
entry->dwInUcastPkts = kstat_get_ui32( ksp, "ipackets" ) - entry->dwInNUcastPkts;
|
|
entry->dwInDiscards = kstat_get_ui32( ksp, "norcvbuf" );
|
|
entry->dwInErrors = kstat_get_ui32( ksp, "ierrors" );
|
|
entry->dwInUnknownProtos = kstat_get_ui32( ksp, "unknowns" );
|
|
entry->dwOutOctets = kstat_get_ui32( ksp, "obytes" );
|
|
entry->dwOutNUcastPkts = kstat_get_ui32( ksp, "multixmt" );
|
|
entry->dwOutNUcastPkts += kstat_get_ui32( ksp, "brdcstxmt" );
|
|
entry->dwOutUcastPkts = kstat_get_ui32( ksp, "opackets" ) - entry->dwOutNUcastPkts;
|
|
entry->dwOutDiscards = 0; /* FIXME */
|
|
entry->dwOutErrors = kstat_get_ui32( ksp, "oerrors" );
|
|
entry->dwOutQLen = kstat_get_ui32( ksp, "noxmtbuf" );
|
|
ret = NO_ERROR;
|
|
}
|
|
if (kc) kstat_close( kc );
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST)
|
|
{
|
|
int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, if_nametoindex(name)};
|
|
size_t needed;
|
|
char *buf = NULL, *end;
|
|
struct if_msghdr *ifm;
|
|
struct if_data ifdata;
|
|
|
|
if(sysctl(mib, ARRAY_SIZE(mib), NULL, &needed, NULL, 0) == -1)
|
|
{
|
|
ERR ("failed to get size of iflist\n");
|
|
goto done;
|
|
}
|
|
buf = HeapAlloc (GetProcessHeap (), 0, needed);
|
|
if (!buf)
|
|
{
|
|
ret = ERROR_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
if(sysctl(mib, ARRAY_SIZE(mib), buf, &needed, NULL, 0) == -1)
|
|
{
|
|
ERR ("failed to get iflist\n");
|
|
goto done;
|
|
}
|
|
for ( end = buf + needed; buf < end; buf += ifm->ifm_msglen)
|
|
{
|
|
ifm = (struct if_msghdr *) buf;
|
|
if(ifm->ifm_type == RTM_IFINFO)
|
|
{
|
|
ifdata = ifm->ifm_data;
|
|
entry->dwMtu = ifdata.ifi_mtu;
|
|
entry->dwSpeed = ifdata.ifi_baudrate;
|
|
entry->dwInOctets = ifdata.ifi_ibytes;
|
|
entry->dwInErrors = ifdata.ifi_ierrors;
|
|
entry->dwInDiscards = ifdata.ifi_iqdrops;
|
|
entry->dwInUcastPkts = ifdata.ifi_ipackets;
|
|
entry->dwInNUcastPkts = ifdata.ifi_imcasts;
|
|
entry->dwOutOctets = ifdata.ifi_obytes;
|
|
entry->dwOutUcastPkts = ifdata.ifi_opackets;
|
|
entry->dwOutErrors = ifdata.ifi_oerrors;
|
|
ret = NO_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
done:
|
|
HeapFree (GetProcessHeap (), 0, buf);
|
|
}
|
|
#else
|
|
FIXME( "unimplemented\n" );
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
* GetIcmpStatistics (IPHLPAPI.@)
|
|
*
|
|
* Get the ICMP statistics for the local computer.
|
|
*
|
|
* PARAMS
|
|
* stats [Out] buffer for ICMP statistics
|
|
*
|
|
* RETURNS
|
|
* Success: NO_ERROR
|
|
* Failure: error code from winerror.h
|
|
*/
|
|
DWORD WINAPI GetIcmpStatistics(PMIB_ICMP stats)
|
|
{
|
|
DWORD ret = ERROR_NOT_SUPPORTED;
|
|
|
|
if (!stats) return ERROR_INVALID_PARAMETER;
|
|
memset( stats, 0, sizeof(MIB_ICMP) );
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/snmp", "r")))
|
|
{
|
|
static const char hdr[] = "Icmp:";
|
|
char buf[512], *ptr;
|
|
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
if (_strnicmp(buf, hdr, sizeof(hdr) - 1)) continue;
|
|
/* last line was a header, get another */
|
|
if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
|
|
if (!_strnicmp(buf, hdr, sizeof(hdr) - 1))
|
|
{
|
|
ptr += sizeof(hdr);
|
|
sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
|
|
&stats->stats.icmpInStats.dwMsgs,
|
|
&stats->stats.icmpInStats.dwErrors,
|
|
&stats->stats.icmpInStats.dwDestUnreachs,
|
|
&stats->stats.icmpInStats.dwTimeExcds,
|
|
&stats->stats.icmpInStats.dwParmProbs,
|
|
&stats->stats.icmpInStats.dwSrcQuenchs,
|
|
&stats->stats.icmpInStats.dwRedirects,
|
|
&stats->stats.icmpInStats.dwEchoReps,
|
|
&stats->stats.icmpInStats.dwTimestamps,
|
|
&stats->stats.icmpInStats.dwTimestampReps,
|
|
&stats->stats.icmpInStats.dwAddrMasks,
|
|
&stats->stats.icmpInStats.dwAddrMaskReps,
|
|
&stats->stats.icmpOutStats.dwMsgs,
|
|
&stats->stats.icmpOutStats.dwErrors,
|
|
&stats->stats.icmpOutStats.dwDestUnreachs,
|
|
&stats->stats.icmpOutStats.dwTimeExcds,
|
|
&stats->stats.icmpOutStats.dwParmProbs,
|
|
&stats->stats.icmpOutStats.dwSrcQuenchs,
|
|
&stats->stats.icmpOutStats.dwRedirects,
|
|
&stats->stats.icmpOutStats.dwEchoReps,
|
|
&stats->stats.icmpOutStats.dwTimestamps,
|
|
&stats->stats.icmpOutStats.dwTimestampReps,
|
|
&stats->stats.icmpOutStats.dwAddrMasks,
|
|
&stats->stats.icmpOutStats.dwAddrMaskReps );
|
|
break;
|
|
}
|
|
}
|
|
fclose(fp);
|
|
ret = NO_ERROR;
|
|
}
|
|
}
|
|
#elif defined(HAVE_LIBKSTAT)
|
|
{
|
|
static char ip[] = "ip", icmp[] = "icmp";
|
|
kstat_ctl_t *kc;
|
|
kstat_t *ksp;
|
|
|
|
if ((kc = kstat_open()) &&
|
|
(ksp = kstat_lookup( kc, ip, 0, icmp )) &&
|
|
kstat_read( kc, ksp, NULL ) != -1 &&
|
|
ksp->ks_type == KSTAT_TYPE_NAMED)
|
|
{
|
|
stats->stats.icmpInStats.dwMsgs = kstat_get_ui32( ksp, "inMsgs" );
|
|
stats->stats.icmpInStats.dwErrors = kstat_get_ui32( ksp, "inErrors" );
|
|
stats->stats.icmpInStats.dwDestUnreachs = kstat_get_ui32( ksp, "inDestUnreachs" );
|
|
stats->stats.icmpInStats.dwTimeExcds = kstat_get_ui32( ksp, "inTimeExcds" );
|
|
stats->stats.icmpInStats.dwParmProbs = kstat_get_ui32( ksp, "inParmProbs" );
|
|
stats->stats.icmpInStats.dwSrcQuenchs = kstat_get_ui32( ksp, "inSrcQuenchs" );
|
|
stats->stats.icmpInStats.dwRedirects = kstat_get_ui32( ksp, "inRedirects" );
|
|
stats->stats.icmpInStats.dwEchos = kstat_get_ui32( ksp, "inEchos" );
|
|
stats->stats.icmpInStats.dwEchoReps = kstat_get_ui32( ksp, "inEchoReps" );
|
|
stats->stats.icmpInStats.dwTimestamps = kstat_get_ui32( ksp, "inTimestamps" );
|
|
stats->stats.icmpInStats.dwTimestampReps = kstat_get_ui32( ksp, "inTimestampReps" );
|
|
stats->stats.icmpInStats.dwAddrMasks = kstat_get_ui32( ksp, "inAddrMasks" );
|
|
stats->stats.icmpInStats.dwAddrMaskReps = kstat_get_ui32( ksp, "inAddrMaskReps" );
|
|
stats->stats.icmpOutStats.dwMsgs = kstat_get_ui32( ksp, "outMsgs" );
|
|
stats->stats.icmpOutStats.dwErrors = kstat_get_ui32( ksp, "outErrors" );
|
|
stats->stats.icmpOutStats.dwDestUnreachs = kstat_get_ui32( ksp, "outDestUnreachs" );
|
|
stats->stats.icmpOutStats.dwTimeExcds = kstat_get_ui32( ksp, "outTimeExcds" );
|
|
stats->stats.icmpOutStats.dwParmProbs = kstat_get_ui32( ksp, "outParmProbs" );
|
|
stats->stats.icmpOutStats.dwSrcQuenchs = kstat_get_ui32( ksp, "outSrcQuenchs" );
|
|
stats->stats.icmpOutStats.dwRedirects = kstat_get_ui32( ksp, "outRedirects" );
|
|
stats->stats.icmpOutStats.dwEchos = kstat_get_ui32( ksp, "outEchos" );
|
|
stats->stats.icmpOutStats.dwEchoReps = kstat_get_ui32( ksp, "outEchoReps" );
|
|
stats->stats.icmpOutStats.dwTimestamps = kstat_get_ui32( ksp, "outTimestamps" );
|
|
stats->stats.icmpOutStats.dwTimestampReps = kstat_get_ui32( ksp, "outTimestampReps" );
|
|
stats->stats.icmpOutStats.dwAddrMasks = kstat_get_ui32( ksp, "outAddrMasks" );
|
|
stats->stats.icmpOutStats.dwAddrMaskReps = kstat_get_ui32( ksp, "outAddrMaskReps" );
|
|
ret = NO_ERROR;
|
|
}
|
|
if (kc) kstat_close( kc );
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(ICMPCTL_STATS) && (defined(HAVE_STRUCT_ICMPSTAT_ICPS_INHIST) || defined(HAVE_STRUCT_ICMPSTAT_ICPS_OUTHIST))
|
|
{
|
|
int mib[] = {CTL_NET, PF_INET, IPPROTO_ICMP, ICMPCTL_STATS};
|
|
struct icmpstat icmp_stat;
|
|
size_t needed = sizeof(icmp_stat);
|
|
int i;
|
|
|
|
if(sysctl(mib, ARRAY_SIZE(mib), &icmp_stat, &needed, NULL, 0) != -1)
|
|
{
|
|
#ifdef HAVE_STRUCT_ICMPSTAT_ICPS_INHIST
|
|
/*in stats */
|
|
stats->stats.icmpInStats.dwMsgs = icmp_stat.icps_badcode + icmp_stat.icps_checksum + icmp_stat.icps_tooshort + icmp_stat.icps_badlen;
|
|
for(i = 0; i <= ICMP_MAXTYPE; i++)
|
|
stats->stats.icmpInStats.dwMsgs += icmp_stat.icps_inhist[i];
|
|
|
|
stats->stats.icmpInStats.dwErrors = icmp_stat.icps_badcode + icmp_stat.icps_tooshort + icmp_stat.icps_checksum + icmp_stat.icps_badlen;
|
|
|
|
stats->stats.icmpInStats.dwDestUnreachs = icmp_stat.icps_inhist[ICMP_UNREACH];
|
|
stats->stats.icmpInStats.dwTimeExcds = icmp_stat.icps_inhist[ICMP_TIMXCEED];
|
|
stats->stats.icmpInStats.dwParmProbs = icmp_stat.icps_inhist[ICMP_PARAMPROB];
|
|
stats->stats.icmpInStats.dwSrcQuenchs = icmp_stat.icps_inhist[ICMP_SOURCEQUENCH];
|
|
stats->stats.icmpInStats.dwRedirects = icmp_stat.icps_inhist[ICMP_REDIRECT];
|
|
stats->stats.icmpInStats.dwEchos = icmp_stat.icps_inhist[ICMP_ECHO];
|
|
stats->stats.icmpInStats.dwEchoReps = icmp_stat.icps_inhist[ICMP_ECHOREPLY];
|
|
stats->stats.icmpInStats.dwTimestamps = icmp_stat.icps_inhist[ICMP_TSTAMP];
|
|
stats->stats.icmpInStats.dwTimestampReps = icmp_stat.icps_inhist[ICMP_TSTAMPREPLY];
|
|
stats->stats.icmpInStats.dwAddrMasks = icmp_stat.icps_inhist[ICMP_MASKREQ];
|
|
stats->stats.icmpInStats.dwAddrMaskReps = icmp_stat.icps_inhist[ICMP_MASKREPLY];
|
|
#endif
|
|
|
|
#ifdef HAVE_STRUCT_ICMPSTAT_ICPS_OUTHIST
|
|
/* out stats */
|
|
stats->stats.icmpOutStats.dwMsgs = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;
|
|
for(i = 0; i <= ICMP_MAXTYPE; i++)
|
|
stats->stats.icmpOutStats.dwMsgs += icmp_stat.icps_outhist[i];
|
|
|
|
stats->stats.icmpOutStats.dwErrors = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;
|
|
|
|
stats->stats.icmpOutStats.dwDestUnreachs = icmp_stat.icps_outhist[ICMP_UNREACH];
|
|
stats->stats.icmpOutStats.dwTimeExcds = icmp_stat.icps_outhist[ICMP_TIMXCEED];
|
|
stats->stats.icmpOutStats.dwParmProbs = icmp_stat.icps_outhist[ICMP_PARAMPROB];
|
|
stats->stats.icmpOutStats.dwSrcQuenchs = icmp_stat.icps_outhist[ICMP_SOURCEQUENCH];
|
|
stats->stats.icmpOutStats.dwRedirects = icmp_stat.icps_outhist[ICMP_REDIRECT];
|
|
stats->stats.icmpOutStats.dwEchos = icmp_stat.icps_outhist[ICMP_ECHO];
|
|
stats->stats.icmpOutStats.dwEchoReps = icmp_stat.icps_outhist[ICMP_ECHOREPLY];
|
|
stats->stats.icmpOutStats.dwTimestamps = icmp_stat.icps_outhist[ICMP_TSTAMP];
|
|
stats->stats.icmpOutStats.dwTimestampReps = icmp_stat.icps_outhist[ICMP_TSTAMPREPLY];
|
|
stats->stats.icmpOutStats.dwAddrMasks = icmp_stat.icps_outhist[ICMP_MASKREQ];
|
|
stats->stats.icmpOutStats.dwAddrMaskReps = icmp_stat.icps_outhist[ICMP_MASKREPLY];
|
|
#endif /* HAVE_STRUCT_ICMPSTAT_ICPS_OUTHIST */
|
|
ret = NO_ERROR;
|
|
}
|
|
}
|
|
#else /* ICMPCTL_STATS */
|
|
FIXME( "unimplemented\n" );
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************
|
|
* GetIcmpStatisticsEx (IPHLPAPI.@)
|
|
*
|
|
* Get the IPv4 and IPv6 ICMP statistics for the local computer.
|
|
*
|
|
* PARAMS
|
|
* stats [Out] buffer for ICMP statistics
|
|
* family [In] specifies whether IPv4 or IPv6 statistics are returned
|
|
*
|
|
* RETURNS
|
|
* Success: NO_ERROR
|
|
* Failure: error code from winerror.h
|
|
*/
|
|
DWORD WINAPI GetIcmpStatisticsEx(PMIB_ICMP_EX stats, DWORD family)
|
|
{
|
|
DWORD ret = ERROR_NOT_SUPPORTED;
|
|
MIB_ICMP ipv4stats;
|
|
|
|
if (!stats) return ERROR_INVALID_PARAMETER;
|
|
if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
|
|
memset( stats, 0, sizeof(MIB_ICMP_EX) );
|
|
|
|
if (family == WS_AF_INET6)
|
|
{
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/snmp6", "r")))
|
|
{
|
|
struct icmpstatstruct{
|
|
const char *name;
|
|
DWORD pos;
|
|
};
|
|
static const struct icmpstatstruct icmpinstatlist[] = {
|
|
{ "Icmp6InDestUnreachs", ICMP6_DST_UNREACH },
|
|
{ "Icmp6InPktTooBigs", ICMP6_PACKET_TOO_BIG },
|
|
{ "Icmp6InTimeExcds", ICMP6_TIME_EXCEEDED },
|
|
{ "Icmp6InParmProblems", ICMP6_PARAM_PROB },
|
|
{ "Icmp6InEchos", ICMP6_ECHO_REQUEST },
|
|
{ "Icmp6InEchoReplies", ICMP6_ECHO_REPLY },
|
|
{ "Icmp6InGroupMembQueries", ICMP6_MEMBERSHIP_QUERY },
|
|
{ "Icmp6InGroupMembResponses", ICMP6_MEMBERSHIP_REPORT },
|
|
{ "Icmp6InGroupMembReductions", ICMP6_MEMBERSHIP_REDUCTION },
|
|
{ "Icmp6InRouterSolicits", ND_ROUTER_SOLICIT },
|
|
{ "Icmp6InRouterAdvertisements", ND_ROUTER_ADVERT },
|
|
{ "Icmp6InNeighborSolicits", ND_NEIGHBOR_SOLICIT },
|
|
{ "Icmp6InNeighborAdvertisements", ND_NEIGHBOR_ADVERT },
|
|
{ "Icmp6InRedirects", ND_REDIRECT },
|
|
{ "Icmp6InMLDv2Reports", ICMP6_V2_MEMBERSHIP_REPORT },
|
|
};
|
|
static const struct icmpstatstruct icmpoutstatlist[] = {
|
|
{ "Icmp6OutDestUnreachs", ICMP6_DST_UNREACH },
|
|
{ "Icmp6OutPktTooBigs", ICMP6_PACKET_TOO_BIG },
|
|
{ "Icmp6OutTimeExcds", ICMP6_TIME_EXCEEDED },
|
|
{ "Icmp6OutParmProblems", ICMP6_PARAM_PROB },
|
|
{ "Icmp6OutEchos", ICMP6_ECHO_REQUEST },
|
|
{ "Icmp6OutEchoReplies", ICMP6_ECHO_REPLY },
|
|
{ "Icmp6OutGroupMembQueries", ICMP6_MEMBERSHIP_QUERY },
|
|
{ "Icmp6OutGroupMembResponses", ICMP6_MEMBERSHIP_REPORT },
|
|
{ "Icmp6OutGroupMembReductions", ICMP6_MEMBERSHIP_REDUCTION },
|
|
{ "Icmp6OutRouterSolicits", ND_ROUTER_SOLICIT },
|
|
{ "Icmp6OutRouterAdvertisements", ND_ROUTER_ADVERT },
|
|
{ "Icmp6OutNeighborSolicits", ND_NEIGHBOR_SOLICIT },
|
|
{ "Icmp6OutNeighborAdvertisements", ND_NEIGHBOR_ADVERT },
|
|
{ "Icmp6OutRedirects", ND_REDIRECT },
|
|
{ "Icmp6OutMLDv2Reports", ICMP6_V2_MEMBERSHIP_REPORT },
|
|
};
|
|
char buf[512], *ptr, *value;
|
|
DWORD res, i;
|
|
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
if (!(value = strchr(buf, ' ')))
|
|
continue;
|
|
|
|
/* terminate the valuename */
|
|
ptr = value - 1;
|
|
*(ptr + 1) = '\0';
|
|
|
|
/* and strip leading spaces from value */
|
|
value += 1;
|
|
while (*value==' ') value++;
|
|
if ((ptr = strchr(value, '\n')))
|
|
*ptr='\0';
|
|
|
|
if (!_strnicmp(buf, "Icmp6InMsgs", -1))
|
|
{
|
|
if (sscanf(value, "%d", &res)) stats->icmpInStats.dwMsgs = res;
|
|
continue;
|
|
}
|
|
|
|
if (!_strnicmp(buf, "Icmp6InErrors", -1))
|
|
{
|
|
if (sscanf(value, "%d", &res)) stats->icmpInStats.dwErrors = res;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(icmpinstatlist); i++)
|
|
{
|
|
if (!_strnicmp(buf, icmpinstatlist[i].name, -1))
|
|
{
|
|
if (sscanf(value, "%d", &res))
|
|
stats->icmpInStats.rgdwTypeCount[icmpinstatlist[i].pos] = res;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!_strnicmp(buf, "Icmp6OutMsgs", -1))
|
|
{
|
|
if (sscanf(value, "%d", &res)) stats->icmpOutStats.dwMsgs = res;
|
|
continue;
|
|
}
|
|
|
|
if (!_strnicmp(buf, "Icmp6OutErrors", -1))
|
|
{
|
|
if (sscanf(value, "%d", &res)) stats->icmpOutStats.dwErrors = res;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(icmpoutstatlist); i++)
|
|
{
|
|
if (!_strnicmp(buf, icmpoutstatlist[i].name, -1))
|
|
{
|
|
if (sscanf(value, "%d", &res))
|
|
stats->icmpOutStats.rgdwTypeCount[icmpoutstatlist[i].pos] = res;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
fclose(fp);
|
|
ret = NO_ERROR;
|
|
}
|
|
}
|
|
#else
|
|
FIXME( "unimplemented for IPv6\n" );
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
ret = GetIcmpStatistics(&ipv4stats);
|
|
if (!ret)
|
|
{
|
|
stats->icmpInStats.dwMsgs = ipv4stats.stats.icmpInStats.dwMsgs;
|
|
stats->icmpInStats.dwErrors = ipv4stats.stats.icmpInStats.dwErrors;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_DST_UNREACH] = ipv4stats.stats.icmpInStats.dwDestUnreachs;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_SOURCE_QUENCH] = ipv4stats.stats.icmpInStats.dwSrcQuenchs;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_REDIRECT] = ipv4stats.stats.icmpInStats.dwRedirects;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_ECHO_REQUEST] = ipv4stats.stats.icmpInStats.dwEchos;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_TIME_EXCEEDED] = ipv4stats.stats.icmpInStats.dwTimeExcds;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_PARAM_PROB] = ipv4stats.stats.icmpInStats.dwParmProbs;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_TIMESTAMP_REQUEST] = ipv4stats.stats.icmpInStats.dwTimestamps;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_TIMESTAMP_REPLY] = ipv4stats.stats.icmpInStats.dwTimestampReps;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_MASK_REQUEST] = ipv4stats.stats.icmpInStats.dwAddrMasks;
|
|
stats->icmpInStats.rgdwTypeCount[ICMP4_MASK_REPLY] = ipv4stats.stats.icmpInStats.dwAddrMaskReps;
|
|
|
|
stats->icmpOutStats.dwMsgs = ipv4stats.stats.icmpOutStats.dwMsgs;
|
|
stats->icmpOutStats.dwErrors = ipv4stats.stats.icmpOutStats.dwErrors;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_DST_UNREACH] = ipv4stats.stats.icmpOutStats.dwDestUnreachs;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_SOURCE_QUENCH] = ipv4stats.stats.icmpOutStats.dwSrcQuenchs;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_REDIRECT] = ipv4stats.stats.icmpOutStats.dwRedirects;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_ECHO_REQUEST] = ipv4stats.stats.icmpOutStats.dwEchos;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_TIME_EXCEEDED] = ipv4stats.stats.icmpOutStats.dwTimeExcds;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_PARAM_PROB] = ipv4stats.stats.icmpOutStats.dwParmProbs;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_TIMESTAMP_REQUEST] = ipv4stats.stats.icmpOutStats.dwTimestamps;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_TIMESTAMP_REPLY] = ipv4stats.stats.icmpOutStats.dwTimestampReps;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_MASK_REQUEST] = ipv4stats.stats.icmpOutStats.dwAddrMasks;
|
|
stats->icmpOutStats.rgdwTypeCount[ICMP4_MASK_REPLY] = ipv4stats.stats.icmpOutStats.dwAddrMaskReps;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************
|
|
* GetIpStatisticsEx (IPHLPAPI.@)
|
|
*
|
|
* Get the IPv4 and IPv6 statistics for the local computer.
|
|
*
|
|
* PARAMS
|
|
* stats [Out] buffer for IP statistics
|
|
* family [In] specifies whether IPv4 or IPv6 statistics are returned
|
|
*
|
|
* RETURNS
|
|
* Success: NO_ERROR
|
|
* Failure: error code from winerror.h
|
|
*/
|
|
DWORD WINAPI GetIpStatisticsEx(PMIB_IPSTATS stats, DWORD family)
|
|
{
|
|
DWORD ret = ERROR_NOT_SUPPORTED;
|
|
MIB_IPFORWARDTABLE *fwd_table;
|
|
|
|
if (!stats) return ERROR_INVALID_PARAMETER;
|
|
if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
|
|
memset( stats, 0, sizeof(*stats) );
|
|
|
|
stats->dwNumIf = stats->dwNumAddr = get_interface_indices( FALSE, NULL );
|
|
if (!AllocateAndGetIpForwardTableFromStack( &fwd_table, FALSE, GetProcessHeap(), 0 ))
|
|
{
|
|
stats->dwNumRoutes = fwd_table->dwNumEntries;
|
|
HeapFree( GetProcessHeap(), 0, fwd_table );
|
|
}
|
|
|
|
if (family == WS_AF_INET6)
|
|
{
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/snmp6", "r")))
|
|
{
|
|
struct {
|
|
const char *name;
|
|
DWORD *elem;
|
|
} ipstatlist[] = {
|
|
{ "Ip6InReceives", &stats->dwInReceives },
|
|
{ "Ip6InHdrErrors", &stats->dwInHdrErrors },
|
|
{ "Ip6InAddrErrors", &stats->dwInAddrErrors },
|
|
{ "Ip6OutForwDatagrams", &stats->dwForwDatagrams },
|
|
{ "Ip6InUnknownProtos", &stats->dwInUnknownProtos },
|
|
{ "Ip6InDiscards", &stats->dwInDiscards },
|
|
{ "Ip6InDelivers", &stats->dwInDelivers },
|
|
{ "Ip6OutRequests", &stats->dwOutRequests },
|
|
{ "Ip6OutDiscards", &stats->dwOutDiscards },
|
|
{ "Ip6OutNoRoutes", &stats->dwOutNoRoutes },
|
|
{ "Ip6ReasmTimeout", &stats->dwReasmTimeout },
|
|
{ "Ip6ReasmReqds", &stats->dwReasmReqds },
|
|
{ "Ip6ReasmOKs", &stats->dwReasmOks },
|
|
{ "Ip6ReasmFails", &stats->dwReasmFails },
|
|
{ "Ip6FragOKs", &stats->dwFragOks },
|
|
{ "Ip6FragFails", &stats->dwFragFails },
|
|
{ "Ip6FragCreates", &stats->dwFragCreates },
|
|
/* hmm, no routingDiscards, defaultTTL and forwarding? */
|
|
};
|
|
char buf[512], *ptr, *value;
|
|
DWORD res, i;
|
|
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
if (!(value = strchr(buf, ' ')))
|
|
continue;
|
|
|
|
/* terminate the valuename */
|
|
ptr = value - 1;
|
|
*(ptr + 1) = '\0';
|
|
|
|
/* and strip leading spaces from value */
|
|
value += 1;
|
|
while (*value==' ') value++;
|
|
if ((ptr = strchr(value, '\n')))
|
|
*ptr='\0';
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ipstatlist); i++)
|
|
if (!_strnicmp(buf, ipstatlist[i].name, -1) && sscanf(value, "%d", &res))
|
|
*ipstatlist[i].elem = res;
|
|
}
|
|
fclose(fp);
|
|
ret = NO_ERROR;
|
|
}
|
|
}
|
|
#else
|
|
FIXME( "unimplemented for IPv6\n" );
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/snmp", "r")))
|
|
{
|
|
static const char hdr[] = "Ip:";
|
|
char buf[512], *ptr;
|
|
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
if (_strnicmp(buf, hdr, sizeof(hdr) - 1)) continue;
|
|
/* last line was a header, get another */
|
|
if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
|
|
if (!_strnicmp(buf, hdr, sizeof(hdr) - 1))
|
|
{
|
|
ptr += sizeof(hdr);
|
|
sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
|
|
&stats->u.dwForwarding,
|
|
&stats->dwDefaultTTL,
|
|
&stats->dwInReceives,
|
|
&stats->dwInHdrErrors,
|
|
&stats->dwInAddrErrors,
|
|
&stats->dwForwDatagrams,
|
|
&stats->dwInUnknownProtos,
|
|
&stats->dwInDiscards,
|
|
&stats->dwInDelivers,
|
|
&stats->dwOutRequests,
|
|
&stats->dwOutDiscards,
|
|
&stats->dwOutNoRoutes,
|
|
&stats->dwReasmTimeout,
|
|
&stats->dwReasmReqds,
|
|
&stats->dwReasmOks,
|
|
&stats->dwReasmFails,
|
|
&stats->dwFragOks,
|
|
&stats->dwFragFails,
|
|
&stats->dwFragCreates );
|
|
/* hmm, no routingDiscards */
|
|
break;
|
|
}
|
|
}
|
|
fclose(fp);
|
|
ret = NO_ERROR;
|
|
}
|
|
}
|
|
#elif defined(HAVE_LIBKSTAT)
|
|
{
|
|
static char ip[] = "ip";
|
|
kstat_ctl_t *kc;
|
|
kstat_t *ksp;
|
|
|
|
if ((kc = kstat_open()) &&
|
|
(ksp = kstat_lookup( kc, ip, 0, ip )) &&
|
|
kstat_read( kc, ksp, NULL ) != -1 &&
|
|
ksp->ks_type == KSTAT_TYPE_NAMED)
|
|
{
|
|
stats->u.dwForwarding = kstat_get_ui32( ksp, "forwarding" );
|
|
stats->dwDefaultTTL = kstat_get_ui32( ksp, "defaultTTL" );
|
|
stats->dwInReceives = kstat_get_ui32( ksp, "inReceives" );
|
|
stats->dwInHdrErrors = kstat_get_ui32( ksp, "inHdrErrors" );
|
|
stats->dwInAddrErrors = kstat_get_ui32( ksp, "inAddrErrors" );
|
|
stats->dwForwDatagrams = kstat_get_ui32( ksp, "forwDatagrams" );
|
|
stats->dwInUnknownProtos = kstat_get_ui32( ksp, "inUnknownProtos" );
|
|
stats->dwInDiscards = kstat_get_ui32( ksp, "inDiscards" );
|
|
stats->dwInDelivers = kstat_get_ui32( ksp, "inDelivers" );
|
|
stats->dwOutRequests = kstat_get_ui32( ksp, "outRequests" );
|
|
stats->dwRoutingDiscards = kstat_get_ui32( ksp, "routingDiscards" );
|
|
stats->dwOutDiscards = kstat_get_ui32( ksp, "outDiscards" );
|
|
stats->dwOutNoRoutes = kstat_get_ui32( ksp, "outNoRoutes" );
|
|
stats->dwReasmTimeout = kstat_get_ui32( ksp, "reasmTimeout" );
|
|
stats->dwReasmReqds = kstat_get_ui32( ksp, "reasmReqds" );
|
|
stats->dwReasmOks = kstat_get_ui32( ksp, "reasmOKs" );
|
|
stats->dwReasmFails = kstat_get_ui32( ksp, "reasmFails" );
|
|
stats->dwFragOks = kstat_get_ui32( ksp, "fragOKs" );
|
|
stats->dwFragFails = kstat_get_ui32( ksp, "fragFails" );
|
|
stats->dwFragCreates = kstat_get_ui32( ksp, "fragCreates" );
|
|
ret = NO_ERROR;
|
|
}
|
|
if (kc) kstat_close( kc );
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(IPCTL_STATS) && (defined(HAVE_STRUCT_IPSTAT_IPS_TOTAL) || defined(HAVE_STRUCT_IP_STATS_IPS_TOTAL))
|
|
{
|
|
int mib[] = {CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS};
|
|
int ip_ttl, ip_forwarding;
|
|
#if defined(HAVE_STRUCT_IPSTAT_IPS_TOTAL)
|
|
struct ipstat ip_stat;
|
|
#elif defined(HAVE_STRUCT_IP_STATS_IPS_TOTAL)
|
|
struct ip_stats ip_stat;
|
|
#endif
|
|
size_t needed;
|
|
|
|
needed = sizeof(ip_stat);
|
|
if(sysctl(mib, ARRAY_SIZE(mib), &ip_stat, &needed, NULL, 0) == -1)
|
|
{
|
|
ERR ("failed to get ipstat\n");
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
needed = sizeof(ip_ttl);
|
|
if (sysctlbyname ("net.inet.ip.ttl", &ip_ttl, &needed, NULL, 0) == -1)
|
|
{
|
|
ERR ("failed to get ip Default TTL\n");
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
needed = sizeof(ip_forwarding);
|
|
if (sysctlbyname ("net.inet.ip.forwarding", &ip_forwarding, &needed, NULL, 0) == -1)
|
|
{
|
|
ERR ("failed to get ip forwarding\n");
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
/* ip.forwarding is 0 or 1 on BSD */
|
|
stats->u.dwForwarding = ip_forwarding+1;
|
|
stats->dwDefaultTTL = ip_ttl;
|
|
stats->dwInReceives = ip_stat.ips_total;
|
|
stats->dwInHdrErrors = ip_stat.ips_badhlen + ip_stat.ips_badsum + ip_stat.ips_tooshort + ip_stat.ips_badlen +
|
|
ip_stat.ips_badvers + ip_stat.ips_badoptions;
|
|
/* ips_badaddr also includes outgoing packets with a bad address, but we can't account for that right now */
|
|
stats->dwInAddrErrors = ip_stat.ips_cantforward + ip_stat.ips_badaddr + ip_stat.ips_notmember;
|
|
stats->dwForwDatagrams = ip_stat.ips_forward;
|
|
stats->dwInUnknownProtos = ip_stat.ips_noproto;
|
|
stats->dwInDiscards = ip_stat.ips_fragdropped;
|
|
stats->dwInDelivers = ip_stat.ips_delivered;
|
|
stats->dwOutRequests = ip_stat.ips_localout;
|
|
/*stats->dwRoutingDiscards = 0;*/ /* FIXME */
|
|
stats->dwOutDiscards = ip_stat.ips_odropped;
|
|
stats->dwOutNoRoutes = ip_stat.ips_noroute;
|
|
stats->dwReasmTimeout = ip_stat.ips_fragtimeout;
|
|
stats->dwReasmReqds = ip_stat.ips_fragments;
|
|
stats->dwReasmOks = ip_stat.ips_reassembled;
|
|
stats->dwReasmFails = ip_stat.ips_fragments - ip_stat.ips_reassembled;
|
|
stats->dwFragOks = ip_stat.ips_fragmented;
|
|
stats->dwFragFails = ip_stat.ips_cantfrag;
|
|
stats->dwFragCreates = ip_stat.ips_ofragments;
|
|
ret = NO_ERROR;
|
|
}
|
|
#else
|
|
FIXME( "unimplemented for IPv4\n" );
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************
|
|
* GetIpStatistics (IPHLPAPI.@)
|
|
*
|
|
* Get the IP statistics for the local computer.
|
|
*
|
|
* PARAMS
|
|
* stats [Out] buffer for IP statistics
|
|
*
|
|
* RETURNS
|
|
* Success: NO_ERROR
|
|
* Failure: error code from winerror.h
|
|
*/
|
|
DWORD WINAPI GetIpStatistics(PMIB_IPSTATS stats)
|
|
{
|
|
return GetIpStatisticsEx(stats, WS_AF_INET);
|
|
}
|
|
|
|
/******************************************************************
|
|
* GetTcpStatisticsEx (IPHLPAPI.@)
|
|
*
|
|
* Get the IPv4 and IPv6 TCP statistics for the local computer.
|
|
*
|
|
* PARAMS
|
|
* stats [Out] buffer for TCP statistics
|
|
* family [In] specifies whether IPv4 or IPv6 statistics are returned
|
|
*
|
|
* RETURNS
|
|
* Success: NO_ERROR
|
|
* Failure: error code from winerror.h
|
|
*/
|
|
DWORD WINAPI GetTcpStatisticsEx(PMIB_TCPSTATS stats, DWORD family)
|
|
{
|
|
DWORD ret = ERROR_NOT_SUPPORTED;
|
|
|
|
if (!stats) return ERROR_INVALID_PARAMETER;
|
|
if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
|
|
memset( stats, 0, sizeof(*stats) );
|
|
|
|
if (family == WS_AF_INET6)
|
|
{
|
|
FIXME( "unimplemented for IPv6\n" );
|
|
return ret;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/snmp", "r")))
|
|
{
|
|
static const char hdr[] = "Tcp:";
|
|
MIB_TCPTABLE *tcp_table;
|
|
char buf[512], *ptr;
|
|
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
if (_strnicmp(buf, hdr, sizeof(hdr) - 1)) continue;
|
|
/* last line was a header, get another */
|
|
if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
|
|
if (!_strnicmp(buf, hdr, sizeof(hdr) - 1))
|
|
{
|
|
ptr += sizeof(hdr);
|
|
sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u",
|
|
&stats->u.dwRtoAlgorithm,
|
|
&stats->dwRtoMin,
|
|
&stats->dwRtoMax,
|
|
&stats->dwMaxConn,
|
|
&stats->dwActiveOpens,
|
|
&stats->dwPassiveOpens,
|
|
&stats->dwAttemptFails,
|
|
&stats->dwEstabResets,
|
|
&stats->dwCurrEstab,
|
|
&stats->dwInSegs,
|
|
&stats->dwOutSegs,
|
|
&stats->dwRetransSegs,
|
|
&stats->dwInErrs,
|
|
&stats->dwOutRsts );
|
|
break;
|
|
}
|
|
}
|
|
if (!AllocateAndGetTcpTableFromStack( &tcp_table, FALSE, GetProcessHeap(), 0 ))
|
|
{
|
|
stats->dwNumConns = tcp_table->dwNumEntries;
|
|
HeapFree( GetProcessHeap(), 0, tcp_table );
|
|
}
|
|
fclose(fp);
|
|
ret = NO_ERROR;
|
|
}
|
|
}
|
|
#elif defined(HAVE_LIBKSTAT)
|
|
{
|
|
static char tcp[] = "tcp";
|
|
kstat_ctl_t *kc;
|
|
kstat_t *ksp;
|
|
|
|
if ((kc = kstat_open()) &&
|
|
(ksp = kstat_lookup( kc, tcp, 0, tcp )) &&
|
|
kstat_read( kc, ksp, NULL ) != -1 &&
|
|
ksp->ks_type == KSTAT_TYPE_NAMED)
|
|
{
|
|
stats->u.dwRtoAlgorithm = kstat_get_ui32( ksp, "rtoAlgorithm" );
|
|
stats->dwRtoMin = kstat_get_ui32( ksp, "rtoMin" );
|
|
stats->dwRtoMax = kstat_get_ui32( ksp, "rtoMax" );
|
|
stats->dwMaxConn = kstat_get_ui32( ksp, "maxConn" );
|
|
stats->dwActiveOpens = kstat_get_ui32( ksp, "activeOpens" );
|
|
stats->dwPassiveOpens = kstat_get_ui32( ksp, "passiveOpens" );
|
|
stats->dwAttemptFails = kstat_get_ui32( ksp, "attemptFails" );
|
|
stats->dwEstabResets = kstat_get_ui32( ksp, "estabResets" );
|
|
stats->dwCurrEstab = kstat_get_ui32( ksp, "currEstab" );
|
|
stats->dwInSegs = kstat_get_ui32( ksp, "inSegs" );
|
|
stats->dwOutSegs = kstat_get_ui32( ksp, "outSegs" );
|
|
stats->dwRetransSegs = kstat_get_ui32( ksp, "retransSegs" );
|
|
stats->dwInErrs = kstat_get_ui32( ksp, "inErrs" );
|
|
stats->dwOutRsts = kstat_get_ui32( ksp, "outRsts" );
|
|
stats->dwNumConns = kstat_get_ui32( ksp, "connTableSize" );
|
|
ret = NO_ERROR;
|
|
}
|
|
if (kc) kstat_close( kc );
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(TCPCTL_STATS) && (defined(HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT) || defined(HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT))
|
|
{
|
|
#ifndef TCPTV_MIN /* got removed in Mac OS X for some reason */
|
|
#define TCPTV_MIN 2
|
|
#define TCPTV_REXMTMAX 128
|
|
#endif
|
|
int mib[] = {CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS};
|
|
#define hz 1000
|
|
#if defined(HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT)
|
|
struct tcpstat tcp_stat;
|
|
#elif defined(HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT)
|
|
struct tcp_stats tcp_stat;
|
|
#endif
|
|
size_t needed = sizeof(tcp_stat);
|
|
|
|
if(sysctl(mib, ARRAY_SIZE(mib), &tcp_stat, &needed, NULL, 0) != -1)
|
|
{
|
|
stats->u.RtoAlgorithm = MIB_TCP_RTO_VANJ;
|
|
stats->dwRtoMin = TCPTV_MIN;
|
|
stats->dwRtoMax = TCPTV_REXMTMAX;
|
|
stats->dwMaxConn = -1;
|
|
stats->dwActiveOpens = tcp_stat.tcps_connattempt;
|
|
stats->dwPassiveOpens = tcp_stat.tcps_accepts;
|
|
stats->dwAttemptFails = tcp_stat.tcps_conndrops;
|
|
stats->dwEstabResets = tcp_stat.tcps_drops;
|
|
stats->dwCurrEstab = 0;
|
|
stats->dwInSegs = tcp_stat.tcps_rcvtotal;
|
|
stats->dwOutSegs = tcp_stat.tcps_sndtotal - tcp_stat.tcps_sndrexmitpack;
|
|
stats->dwRetransSegs = tcp_stat.tcps_sndrexmitpack;
|
|
stats->dwInErrs = tcp_stat.tcps_rcvbadsum + tcp_stat.tcps_rcvbadoff + tcp_stat.tcps_rcvmemdrop + tcp_stat.tcps_rcvshort;
|
|
stats->dwOutRsts = tcp_stat.tcps_sndctrl - tcp_stat.tcps_closed;
|
|
stats->dwNumConns = tcp_stat.tcps_connects;
|
|
ret = NO_ERROR;
|
|
}
|
|
else ERR ("failed to get tcpstat\n");
|
|
}
|
|
#else
|
|
FIXME( "unimplemented\n" );
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************
|
|
* GetTcpStatistics (IPHLPAPI.@)
|
|
*
|
|
* Get the TCP statistics for the local computer.
|
|
*
|
|
* PARAMS
|
|
* stats [Out] buffer for TCP statistics
|
|
*
|
|
* RETURNS
|
|
* Success: NO_ERROR
|
|
* Failure: error code from winerror.h
|
|
*/
|
|
DWORD WINAPI GetTcpStatistics(PMIB_TCPSTATS stats)
|
|
{
|
|
return GetTcpStatisticsEx(stats, WS_AF_INET);
|
|
}
|
|
|
|
/******************************************************************
|
|
* GetUdpStatistics (IPHLPAPI.@)
|
|
*
|
|
* Get the IPv4 and IPv6 UDP statistics for the local computer.
|
|
*
|
|
* PARAMS
|
|
* stats [Out] buffer for UDP statistics
|
|
* family [In] specifies whether IPv4 or IPv6 statistics are returned
|
|
*
|
|
* RETURNS
|
|
* Success: NO_ERROR
|
|
* Failure: error code from winerror.h
|
|
*/
|
|
DWORD WINAPI GetUdpStatisticsEx(PMIB_UDPSTATS stats, DWORD family)
|
|
{
|
|
DWORD ret = ERROR_NOT_SUPPORTED;
|
|
|
|
if (!stats) return ERROR_INVALID_PARAMETER;
|
|
if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
|
|
memset( stats, 0, sizeof(*stats) );
|
|
|
|
stats->dwNumAddrs = get_interface_indices( FALSE, NULL );
|
|
|
|
if (family == WS_AF_INET6)
|
|
{
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/snmp6", "r")))
|
|
{
|
|
struct {
|
|
const char *name;
|
|
DWORD *elem;
|
|
} udpstatlist[] = {
|
|
{ "Udp6InDatagrams", &stats->dwInDatagrams },
|
|
{ "Udp6NoPorts", &stats->dwNoPorts },
|
|
{ "Udp6InErrors", &stats->dwInErrors },
|
|
{ "Udp6OutDatagrams", &stats->dwOutDatagrams },
|
|
};
|
|
char buf[512], *ptr, *value;
|
|
DWORD res, i;
|
|
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
if (!(value = strchr(buf, ' ')))
|
|
continue;
|
|
|
|
/* terminate the valuename */
|
|
ptr = value - 1;
|
|
*(ptr + 1) = '\0';
|
|
|
|
/* and strip leading spaces from value */
|
|
value += 1;
|
|
while (*value==' ') value++;
|
|
if ((ptr = strchr(value, '\n')))
|
|
*ptr='\0';
|
|
|
|
for (i = 0; i < ARRAY_SIZE(udpstatlist); i++)
|
|
if (!_strnicmp(buf, udpstatlist[i].name, -1) && sscanf(value, "%d", &res))
|
|
*udpstatlist[i].elem = res;
|
|
}
|
|
fclose(fp);
|
|
ret = NO_ERROR;
|
|
}
|
|
}
|
|
#else
|
|
FIXME( "unimplemented for IPv6\n" );
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/snmp", "r")))
|
|
{
|
|
static const char hdr[] = "Udp:";
|
|
char buf[512], *ptr;
|
|
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
if (_strnicmp(buf, hdr, sizeof(hdr) - 1)) continue;
|
|
/* last line was a header, get another */
|
|
if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
|
|
if (!_strnicmp(buf, hdr, sizeof(hdr) - 1))
|
|
{
|
|
ptr += sizeof(hdr);
|
|
sscanf( ptr, "%u %u %u %u %u",
|
|
&stats->dwInDatagrams, &stats->dwNoPorts,
|
|
&stats->dwInErrors, &stats->dwOutDatagrams, &stats->dwNumAddrs );
|
|
break;
|
|
}
|
|
}
|
|
fclose(fp);
|
|
ret = NO_ERROR;
|
|
}
|
|
}
|
|
#elif defined(HAVE_LIBKSTAT)
|
|
{
|
|
static char udp[] = "udp";
|
|
kstat_ctl_t *kc;
|
|
kstat_t *ksp;
|
|
MIB_UDPTABLE *udp_table;
|
|
|
|
if ((kc = kstat_open()) &&
|
|
(ksp = kstat_lookup( kc, udp, 0, udp )) &&
|
|
kstat_read( kc, ksp, NULL ) != -1 &&
|
|
ksp->ks_type == KSTAT_TYPE_NAMED)
|
|
{
|
|
stats->dwInDatagrams = kstat_get_ui32( ksp, "inDatagrams" );
|
|
stats->dwNoPorts = 0; /* FIXME */
|
|
stats->dwInErrors = kstat_get_ui32( ksp, "inErrors" );
|
|
stats->dwOutDatagrams = kstat_get_ui32( ksp, "outDatagrams" );
|
|
if (!AllocateAndGetUdpTableFromStack( &udp_table, FALSE, GetProcessHeap(), 0 ))
|
|
{
|
|
stats->dwNumAddrs = udp_table->dwNumEntries;
|
|
HeapFree( GetProcessHeap(), 0, udp_table );
|
|
}
|
|
ret = NO_ERROR;
|
|
}
|
|
if (kc) kstat_close( kc );
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS) && defined(HAVE_STRUCT_UDPSTAT_UDPS_IPACKETS)
|
|
{
|
|
int mib[] = {CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_STATS};
|
|
struct udpstat udp_stat;
|
|
MIB_UDPTABLE *udp_table;
|
|
size_t needed = sizeof(udp_stat);
|
|
|
|
if(sysctl(mib, ARRAY_SIZE(mib), &udp_stat, &needed, NULL, 0) != -1)
|
|
{
|
|
stats->dwInDatagrams = udp_stat.udps_ipackets;
|
|
stats->dwOutDatagrams = udp_stat.udps_opackets;
|
|
stats->dwNoPorts = udp_stat.udps_noport;
|
|
stats->dwInErrors = udp_stat.udps_hdrops + udp_stat.udps_badsum + udp_stat.udps_fullsock + udp_stat.udps_badlen;
|
|
if (!AllocateAndGetUdpTableFromStack( &udp_table, FALSE, GetProcessHeap(), 0 ))
|
|
{
|
|
stats->dwNumAddrs = udp_table->dwNumEntries;
|
|
HeapFree( GetProcessHeap(), 0, udp_table );
|
|
}
|
|
ret = NO_ERROR;
|
|
}
|
|
else ERR ("failed to get udpstat\n");
|
|
}
|
|
#else
|
|
FIXME( "unimplemented for IPv4\n" );
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************
|
|
* GetUdpStatistics (IPHLPAPI.@)
|
|
*
|
|
* Get the UDP statistics for the local computer.
|
|
*
|
|
* PARAMS
|
|
* stats [Out] buffer for UDP statistics
|
|
*
|
|
* RETURNS
|
|
* Success: NO_ERROR
|
|
* Failure: error code from winerror.h
|
|
*/
|
|
DWORD WINAPI GetUdpStatistics(PMIB_UDPSTATS stats)
|
|
{
|
|
return GetUdpStatisticsEx(stats, WS_AF_INET);
|
|
}
|
|
|
|
static MIB_IPFORWARDTABLE *append_ipforward_row( HANDLE heap, DWORD flags, MIB_IPFORWARDTABLE *table,
|
|
DWORD *count, const MIB_IPFORWARDROW *row )
|
|
{
|
|
if (table->dwNumEntries >= *count)
|
|
{
|
|
MIB_IPFORWARDTABLE *new_table;
|
|
DWORD new_count = table->dwNumEntries * 2;
|
|
|
|
if (!(new_table = HeapReAlloc( heap, flags, table,
|
|
FIELD_OFFSET(MIB_IPFORWARDTABLE, table[new_count] ))))
|
|
{
|
|
HeapFree( heap, 0, table );
|
|
return NULL;
|
|
}
|
|
*count = new_count;
|
|
table = new_table;
|
|
}
|
|
memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
|
|
return table;
|
|
}
|
|
|
|
static int compare_ipforward_rows(const void *a, const void *b)
|
|
{
|
|
const MIB_IPFORWARDROW *rowA = a;
|
|
const MIB_IPFORWARDROW *rowB = b;
|
|
int ret;
|
|
|
|
if ((ret = rowA->dwForwardDest - rowB->dwForwardDest) != 0) return ret;
|
|
if ((ret = rowA->u2.dwForwardProto - rowB->u2.dwForwardProto) != 0) return ret;
|
|
if ((ret = rowA->dwForwardPolicy - rowB->dwForwardPolicy) != 0) return ret;
|
|
return rowA->dwForwardNextHop - rowB->dwForwardNextHop;
|
|
}
|
|
|
|
/******************************************************************
|
|
* AllocateAndGetIpForwardTableFromStack (IPHLPAPI.@)
|
|
*
|
|
* Get the route table.
|
|
* Like GetIpForwardTable(), but allocate the returned table from heap.
|
|
*
|
|
* PARAMS
|
|
* ppIpForwardTable [Out] pointer into which the MIB_IPFORWARDTABLE is
|
|
* allocated and returned.
|
|
* bOrder [In] whether to sort the table
|
|
* heap [In] heap from which the table is allocated
|
|
* flags [In] flags to HeapAlloc
|
|
*
|
|
* RETURNS
|
|
* ERROR_INVALID_PARAMETER if ppIfTable is NULL, other error codes
|
|
* on failure, NO_ERROR on success.
|
|
*/
|
|
DWORD WINAPI AllocateAndGetIpForwardTableFromStack(PMIB_IPFORWARDTABLE *ppIpForwardTable, BOOL bOrder,
|
|
HANDLE heap, DWORD flags)
|
|
{
|
|
MIB_IPFORWARDTABLE *table;
|
|
MIB_IPFORWARDROW row;
|
|
DWORD ret = NO_ERROR, count = 16;
|
|
|
|
TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppIpForwardTable, bOrder, heap, flags);
|
|
|
|
if (!ppIpForwardTable) return ERROR_INVALID_PARAMETER;
|
|
|
|
if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_IPFORWARDTABLE, table[count] ))))
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
table->dwNumEntries = 0;
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/route", "r")))
|
|
{
|
|
char buf[512], *ptr;
|
|
DWORD flags;
|
|
|
|
/* skip header line */
|
|
ptr = fgets(buf, sizeof(buf), fp);
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
memset( &row, 0, sizeof(row) );
|
|
|
|
while (!isspace(*ptr)) ptr++;
|
|
*ptr++ = 0;
|
|
if (getInterfaceIndexByName(buf, &row.dwForwardIfIndex) != NO_ERROR)
|
|
continue;
|
|
|
|
row.dwForwardDest = strtoul(ptr, &ptr, 16);
|
|
row.dwForwardNextHop = strtoul(ptr + 1, &ptr, 16);
|
|
flags = strtoul(ptr + 1, &ptr, 16);
|
|
|
|
if (!(flags & RTF_UP)) row.u1.ForwardType = MIB_IPROUTE_TYPE_INVALID;
|
|
else if (flags & RTF_GATEWAY) row.u1.ForwardType = MIB_IPROUTE_TYPE_INDIRECT;
|
|
else row.u1.ForwardType = MIB_IPROUTE_TYPE_DIRECT;
|
|
|
|
strtoul(ptr + 1, &ptr, 16); /* refcount, skip */
|
|
strtoul(ptr + 1, &ptr, 16); /* use, skip */
|
|
row.dwForwardMetric1 = strtoul(ptr + 1, &ptr, 16);
|
|
row.dwForwardMask = strtoul(ptr + 1, &ptr, 16);
|
|
/* FIXME: other protos might be appropriate, e.g. the default
|
|
* route is typically set with MIB_IPPROTO_NETMGMT instead */
|
|
row.u2.ForwardProto = MIB_IPPROTO_LOCAL;
|
|
|
|
if (!(table = append_ipforward_row( heap, flags, table, &count, &row )))
|
|
break;
|
|
}
|
|
fclose(fp);
|
|
}
|
|
else ret = ERROR_NOT_SUPPORTED;
|
|
}
|
|
#elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
|
|
{
|
|
void *data;
|
|
int fd, len, namelen;
|
|
mib2_ipRouteEntry_t *entry;
|
|
char name[64];
|
|
|
|
if ((fd = open_streams_mib( NULL )) != -1)
|
|
{
|
|
if ((data = read_mib_entry( fd, MIB2_IP, MIB2_IP_ROUTE, &len )))
|
|
{
|
|
for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
|
|
{
|
|
row.dwForwardDest = entry->ipRouteDest;
|
|
row.dwForwardMask = entry->ipRouteMask;
|
|
row.dwForwardPolicy = 0;
|
|
row.dwForwardNextHop = entry->ipRouteNextHop;
|
|
row.u1.dwForwardType = entry->ipRouteType;
|
|
row.u2.dwForwardProto = entry->ipRouteProto;
|
|
row.dwForwardAge = entry->ipRouteAge;
|
|
row.dwForwardNextHopAS = 0;
|
|
row.dwForwardMetric1 = entry->ipRouteMetric1;
|
|
row.dwForwardMetric2 = entry->ipRouteMetric2;
|
|
row.dwForwardMetric3 = entry->ipRouteMetric3;
|
|
row.dwForwardMetric4 = entry->ipRouteMetric4;
|
|
row.dwForwardMetric5 = entry->ipRouteMetric5;
|
|
namelen = min( sizeof(name) - 1, entry->ipRouteIfIndex.o_length );
|
|
memcpy( name, entry->ipRouteIfIndex.o_bytes, namelen );
|
|
name[namelen] = 0;
|
|
getInterfaceIndexByName( name, &row.dwForwardIfIndex );
|
|
if (!(table = append_ipforward_row( heap, flags, table, &count, &row ))) break;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, data );
|
|
}
|
|
close( fd );
|
|
}
|
|
else ret = ERROR_NOT_SUPPORTED;
|
|
}
|
|
#elif 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 = NULL, *lim, *next, *addrPtr;
|
|
struct rt_msghdr *rtm;
|
|
|
|
if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
|
|
{
|
|
ERR ("sysctl 1 failed!\n");
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
goto done;
|
|
}
|
|
|
|
buf = HeapAlloc (GetProcessHeap (), 0, needed);
|
|
if (!buf)
|
|
{
|
|
ret = ERROR_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
|
|
{
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
goto done;
|
|
}
|
|
|
|
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 gateway routes which are multicast */
|
|
if ((rtm->rtm_flags & RTF_GATEWAY) && (rtm->rtm_flags & RTF_MULTICAST))
|
|
continue;
|
|
|
|
memset( &row, 0, sizeof(row) );
|
|
row.dwForwardIfIndex = rtm->rtm_index;
|
|
row.u1.ForwardType = (rtm->rtm_flags & RTF_GATEWAY) ? MIB_IPROUTE_TYPE_INDIRECT : MIB_IPROUTE_TYPE_DIRECT;
|
|
row.dwForwardMetric1 = rtm->rtm_rmx.rmx_hopcount;
|
|
row.u2.ForwardProto = 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 {
|
|
switch(sa->sa_family) {
|
|
case AF_INET: {
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
|
addr = sin->sin_addr.s_addr;
|
|
break;
|
|
}
|
|
#ifdef AF_LINK
|
|
case AF_LINK:
|
|
if(i == RTA_GATEWAY && row.u1.ForwardType == MIB_IPROUTE_TYPE_DIRECT) {
|
|
/* For direct route we may simply use dest addr as next hop */
|
|
C_ASSERT(RTA_DST < RTA_GATEWAY);
|
|
addr = row.dwForwardDest;
|
|
break;
|
|
}
|
|
/* fallthrough */
|
|
#endif
|
|
default:
|
|
WARN ("Received unsupported sockaddr family 0x%x\n", sa->sa_family);
|
|
addr = 0;
|
|
}
|
|
}
|
|
|
|
switch (i)
|
|
{
|
|
case RTA_DST: row.dwForwardDest = addr; break;
|
|
case RTA_GATEWAY: row.dwForwardNextHop = addr; break;
|
|
case RTA_NETMASK: row.dwForwardMask = addr; break;
|
|
default:
|
|
WARN ("Unexpected address type 0x%x\n", i);
|
|
}
|
|
}
|
|
|
|
if (!(table = append_ipforward_row( heap, flags, table, &count, &row )))
|
|
break;
|
|
}
|
|
done:
|
|
HeapFree( GetProcessHeap (), 0, buf );
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
#endif
|
|
|
|
if (!table) return ERROR_OUTOFMEMORY;
|
|
if (!ret)
|
|
{
|
|
if (bOrder && table->dwNumEntries)
|
|
qsort( table->table, table->dwNumEntries, sizeof(row), compare_ipforward_rows );
|
|
*ppIpForwardTable = table;
|
|
}
|
|
else HeapFree( heap, flags, table );
|
|
TRACE( "returning ret %u table %p\n", ret, table );
|
|
return ret;
|
|
}
|
|
|
|
static MIB_IPNETTABLE *append_ipnet_row( HANDLE heap, DWORD flags, MIB_IPNETTABLE *table,
|
|
DWORD *count, const MIB_IPNETROW *row )
|
|
{
|
|
if (table->dwNumEntries >= *count)
|
|
{
|
|
MIB_IPNETTABLE *new_table;
|
|
DWORD new_count = table->dwNumEntries * 2;
|
|
|
|
if (!(new_table = HeapReAlloc( heap, flags, table,
|
|
FIELD_OFFSET(MIB_IPNETTABLE, table[new_count] ))))
|
|
{
|
|
HeapFree( heap, 0, table );
|
|
return NULL;
|
|
}
|
|
*count = new_count;
|
|
table = new_table;
|
|
}
|
|
memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
|
|
return table;
|
|
}
|
|
|
|
static int compare_ipnet_rows(const void *a, const void *b)
|
|
{
|
|
const MIB_IPNETROW *rowA = a;
|
|
const MIB_IPNETROW *rowB = b;
|
|
|
|
return ntohl(rowA->dwAddr) - ntohl(rowB->dwAddr);
|
|
}
|
|
|
|
|
|
/******************************************************************
|
|
* AllocateAndGetIpNetTableFromStack (IPHLPAPI.@)
|
|
*
|
|
* Get the IP-to-physical address mapping table.
|
|
* Like GetIpNetTable(), but allocate the returned table from heap.
|
|
*
|
|
* PARAMS
|
|
* ppIpNetTable [Out] pointer into which the MIB_IPNETTABLE is
|
|
* allocated and returned.
|
|
* bOrder [In] whether to sort the table
|
|
* heap [In] heap from which the table is allocated
|
|
* flags [In] flags to HeapAlloc
|
|
*
|
|
* RETURNS
|
|
* ERROR_INVALID_PARAMETER if ppIpNetTable is NULL, other error codes
|
|
* on failure, NO_ERROR on success.
|
|
*/
|
|
DWORD WINAPI AllocateAndGetIpNetTableFromStack(PMIB_IPNETTABLE *ppIpNetTable, BOOL bOrder,
|
|
HANDLE heap, DWORD flags)
|
|
{
|
|
MIB_IPNETTABLE *table;
|
|
MIB_IPNETROW row;
|
|
DWORD ret = NO_ERROR, count = 16;
|
|
|
|
TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppIpNetTable, bOrder, heap, flags);
|
|
|
|
if (!ppIpNetTable) return ERROR_INVALID_PARAMETER;
|
|
|
|
if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_IPNETTABLE, table[count] ))))
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
table->dwNumEntries = 0;
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/arp", "r")))
|
|
{
|
|
char buf[512], *ptr;
|
|
DWORD flags;
|
|
|
|
/* skip header line */
|
|
ptr = fgets(buf, sizeof(buf), fp);
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
memset( &row, 0, sizeof(row) );
|
|
|
|
row.dwAddr = inet_addr(ptr);
|
|
while (*ptr && !isspace(*ptr)) ptr++;
|
|
strtoul(ptr + 1, &ptr, 16); /* hw type (skip) */
|
|
flags = strtoul(ptr + 1, &ptr, 16);
|
|
|
|
#ifdef ATF_COM
|
|
if (flags & ATF_COM) row.u.Type = MIB_IPNET_TYPE_DYNAMIC;
|
|
else
|
|
#endif
|
|
#ifdef ATF_PERM
|
|
if (flags & ATF_PERM) row.u.Type = MIB_IPNET_TYPE_STATIC;
|
|
else
|
|
#endif
|
|
row.u.Type = MIB_IPNET_TYPE_OTHER;
|
|
|
|
while (*ptr && isspace(*ptr)) ptr++;
|
|
while (*ptr && !isspace(*ptr))
|
|
{
|
|
row.bPhysAddr[row.dwPhysAddrLen++] = strtoul(ptr, &ptr, 16);
|
|
if (*ptr) ptr++;
|
|
}
|
|
while (*ptr && isspace(*ptr)) ptr++;
|
|
while (*ptr && !isspace(*ptr)) ptr++; /* mask (skip) */
|
|
while (*ptr && isspace(*ptr)) ptr++;
|
|
getInterfaceIndexByName(ptr, &row.dwIndex);
|
|
|
|
if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
|
|
break;
|
|
}
|
|
fclose(fp);
|
|
}
|
|
else ret = ERROR_NOT_SUPPORTED;
|
|
}
|
|
#elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
|
|
{
|
|
void *data;
|
|
int fd, len, namelen;
|
|
mib2_ipNetToMediaEntry_t *entry;
|
|
char name[64];
|
|
|
|
if ((fd = open_streams_mib( NULL )) != -1)
|
|
{
|
|
if ((data = read_mib_entry( fd, MIB2_IP, MIB2_IP_MEDIA, &len )))
|
|
{
|
|
for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
|
|
{
|
|
row.dwPhysAddrLen = min( entry->ipNetToMediaPhysAddress.o_length, MAXLEN_PHYSADDR );
|
|
memcpy( row.bPhysAddr, entry->ipNetToMediaPhysAddress.o_bytes, row.dwPhysAddrLen );
|
|
row.dwAddr = entry->ipNetToMediaNetAddress;
|
|
row.u.Type = entry->ipNetToMediaType;
|
|
namelen = min( sizeof(name) - 1, entry->ipNetToMediaIfIndex.o_length );
|
|
memcpy( name, entry->ipNetToMediaIfIndex.o_bytes, namelen );
|
|
name[namelen] = 0;
|
|
getInterfaceIndexByName( name, &row.dwIndex );
|
|
if (!(table = append_ipnet_row( heap, flags, table, &count, &row ))) break;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, data );
|
|
}
|
|
close( fd );
|
|
}
|
|
else ret = ERROR_NOT_SUPPORTED;
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
|
|
{
|
|
int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO};
|
|
size_t needed;
|
|
char *buf = NULL, *lim, *next;
|
|
struct rt_msghdr *rtm;
|
|
struct sockaddr_inarp *sinarp;
|
|
struct sockaddr_dl *sdl;
|
|
|
|
if (sysctl (mib, ARRAY_SIZE(mib), NULL, &needed, NULL, 0) == -1)
|
|
{
|
|
ERR ("failed to get arp table\n");
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
goto done;
|
|
}
|
|
|
|
buf = HeapAlloc (GetProcessHeap (), 0, needed);
|
|
if (!buf)
|
|
{
|
|
ret = ERROR_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
if (sysctl (mib, ARRAY_SIZE(mib), buf, &needed, NULL, 0) == -1)
|
|
{
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
goto done;
|
|
}
|
|
|
|
lim = buf + needed;
|
|
next = buf;
|
|
while(next < lim)
|
|
{
|
|
rtm = (struct rt_msghdr *)next;
|
|
sinarp=(struct sockaddr_inarp *)(rtm + 1);
|
|
sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len));
|
|
if(sdl->sdl_alen) /* arp entry */
|
|
{
|
|
memset( &row, 0, sizeof(row) );
|
|
row.dwAddr = sinarp->sin_addr.s_addr;
|
|
row.dwIndex = sdl->sdl_index;
|
|
row.dwPhysAddrLen = min( 8, sdl->sdl_alen );
|
|
memcpy( row.bPhysAddr, &sdl->sdl_data[sdl->sdl_nlen], row.dwPhysAddrLen );
|
|
if(rtm->rtm_rmx.rmx_expire == 0) row.u.Type = MIB_IPNET_TYPE_STATIC;
|
|
else if(sinarp->sin_other & SIN_PROXY) row.u.Type = MIB_IPNET_TYPE_OTHER;
|
|
else if(rtm->rtm_rmx.rmx_expire != 0) row.u.Type = MIB_IPNET_TYPE_DYNAMIC;
|
|
else row.u.Type = MIB_IPNET_TYPE_INVALID;
|
|
|
|
if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
|
|
break;
|
|
}
|
|
next += rtm->rtm_msglen;
|
|
}
|
|
done:
|
|
HeapFree( GetProcessHeap (), 0, buf );
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
#endif
|
|
|
|
if (!table) return ERROR_OUTOFMEMORY;
|
|
if (!ret)
|
|
{
|
|
if (bOrder && table->dwNumEntries)
|
|
qsort( table->table, table->dwNumEntries, sizeof(row), compare_ipnet_rows );
|
|
*ppIpNetTable = table;
|
|
}
|
|
else HeapFree( heap, flags, table );
|
|
TRACE( "returning ret %u table %p\n", ret, table );
|
|
return ret;
|
|
}
|
|
|
|
static DWORD get_tcp_table_sizes( TCP_TABLE_CLASS class, DWORD row_count, DWORD *row_size )
|
|
{
|
|
DWORD table_size;
|
|
|
|
switch (class)
|
|
{
|
|
case TCP_TABLE_BASIC_LISTENER:
|
|
case TCP_TABLE_BASIC_CONNECTIONS:
|
|
case TCP_TABLE_BASIC_ALL:
|
|
{
|
|
table_size = FIELD_OFFSET(MIB_TCPTABLE, table[row_count]);
|
|
if (row_size) *row_size = sizeof(MIB_TCPROW);
|
|
break;
|
|
}
|
|
case TCP_TABLE_OWNER_PID_LISTENER:
|
|
case TCP_TABLE_OWNER_PID_CONNECTIONS:
|
|
case TCP_TABLE_OWNER_PID_ALL:
|
|
{
|
|
table_size = FIELD_OFFSET(MIB_TCPTABLE_OWNER_PID, table[row_count]);
|
|
if (row_size) *row_size = sizeof(MIB_TCPROW_OWNER_PID);
|
|
break;
|
|
}
|
|
case TCP_TABLE_OWNER_MODULE_LISTENER:
|
|
case TCP_TABLE_OWNER_MODULE_CONNECTIONS:
|
|
case TCP_TABLE_OWNER_MODULE_ALL:
|
|
{
|
|
table_size = FIELD_OFFSET(MIB_TCPTABLE_OWNER_MODULE, table[row_count]);
|
|
if (row_size) *row_size = sizeof(MIB_TCPROW_OWNER_MODULE);
|
|
break;
|
|
}
|
|
default:
|
|
ERR("unhandled class %u\n", class);
|
|
return 0;
|
|
}
|
|
return table_size;
|
|
}
|
|
|
|
static MIB_TCPTABLE *append_tcp_row( TCP_TABLE_CLASS class, HANDLE heap, DWORD flags,
|
|
MIB_TCPTABLE *table, DWORD *count,
|
|
const MIB_TCPROW_OWNER_MODULE *row, DWORD row_size )
|
|
{
|
|
if (table->dwNumEntries >= *count)
|
|
{
|
|
MIB_TCPTABLE *new_table;
|
|
DWORD new_count = table->dwNumEntries * 2, new_table_size;
|
|
|
|
new_table_size = get_tcp_table_sizes( class, new_count, NULL );
|
|
if (!(new_table = HeapReAlloc( heap, flags, table, new_table_size )))
|
|
{
|
|
HeapFree( heap, 0, table );
|
|
return NULL;
|
|
}
|
|
*count = new_count;
|
|
table = new_table;
|
|
}
|
|
memcpy( (char *)table->table + (table->dwNumEntries * row_size), row, row_size );
|
|
table->dwNumEntries++;
|
|
return table;
|
|
}
|
|
|
|
|
|
/* Why not a lookup table? Because the TCPS_* constants are different
|
|
on different platforms */
|
|
static inline MIB_TCP_STATE 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;
|
|
}
|
|
}
|
|
|
|
static int compare_tcp_rows(const void *a, const void *b)
|
|
{
|
|
const MIB_TCPROW *rowA = a;
|
|
const MIB_TCPROW *rowB = b;
|
|
int ret;
|
|
|
|
if ((ret = ntohl (rowA->dwLocalAddr) - ntohl (rowB->dwLocalAddr)) != 0) return ret;
|
|
if ((ret = ntohs ((unsigned short)rowA->dwLocalPort) -
|
|
ntohs ((unsigned short)rowB->dwLocalPort)) != 0) return ret;
|
|
if ((ret = ntohl (rowA->dwRemoteAddr) - ntohl (rowB->dwRemoteAddr)) != 0) return ret;
|
|
return ntohs ((unsigned short)rowA->dwRemotePort) - ntohs ((unsigned short)rowB->dwRemotePort);
|
|
}
|
|
|
|
struct pid_map
|
|
{
|
|
unsigned int pid;
|
|
unsigned int unix_pid;
|
|
};
|
|
|
|
static struct pid_map *get_pid_map( unsigned int *num_entries )
|
|
{
|
|
HANDLE snapshot = NULL;
|
|
struct pid_map *map;
|
|
unsigned int i = 0, count = 16, size = count * sizeof(*map);
|
|
NTSTATUS ret;
|
|
|
|
if (!(map = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL;
|
|
|
|
SERVER_START_REQ( create_snapshot )
|
|
{
|
|
req->flags = SNAP_PROCESS;
|
|
req->attributes = 0;
|
|
if (!(ret = wine_server_call( req )))
|
|
snapshot = wine_server_ptr_handle( reply->handle );
|
|
}
|
|
SERVER_END_REQ;
|
|
|
|
*num_entries = 0;
|
|
while (ret == STATUS_SUCCESS)
|
|
{
|
|
SERVER_START_REQ( next_process )
|
|
{
|
|
req->handle = wine_server_obj_handle( snapshot );
|
|
req->reset = (i == 0);
|
|
if (!(ret = wine_server_call( req )))
|
|
{
|
|
if (i >= count)
|
|
{
|
|
struct pid_map *new_map;
|
|
count *= 2;
|
|
size = count * sizeof(*new_map);
|
|
|
|
if (!(new_map = HeapReAlloc( GetProcessHeap(), 0, map, size )))
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, map );
|
|
map = NULL;
|
|
goto done;
|
|
}
|
|
map = new_map;
|
|
}
|
|
map[i].pid = reply->pid;
|
|
map[i].unix_pid = reply->unix_pid;
|
|
(*num_entries)++;
|
|
i++;
|
|
}
|
|
}
|
|
SERVER_END_REQ;
|
|
}
|
|
|
|
done:
|
|
NtClose( snapshot );
|
|
return map;
|
|
}
|
|
|
|
static unsigned int find_owning_pid( struct pid_map *map, unsigned int num_entries, UINT_PTR inode )
|
|
{
|
|
#ifdef __linux__
|
|
unsigned int i, len_socket;
|
|
char socket[32];
|
|
|
|
sprintf( socket, "socket:[%lu]", inode );
|
|
len_socket = strlen( socket );
|
|
for (i = 0; i < num_entries; i++)
|
|
{
|
|
char dir[32];
|
|
struct dirent *dirent;
|
|
DIR *dirfd;
|
|
|
|
sprintf( dir, "/proc/%u/fd", map[i].unix_pid );
|
|
if ((dirfd = opendir( dir )))
|
|
{
|
|
while ((dirent = readdir( dirfd )))
|
|
{
|
|
char link[sizeof(dirent->d_name) + 32], name[32];
|
|
int len;
|
|
|
|
sprintf( link, "/proc/%u/fd/%s", map[i].unix_pid, dirent->d_name );
|
|
if ((len = readlink( link, name, sizeof(name) - 1 )) > 0) name[len] = 0;
|
|
if (len == len_socket && !strcmp( socket, name ))
|
|
{
|
|
closedir( dirfd );
|
|
return map[i].pid;
|
|
}
|
|
}
|
|
closedir( dirfd );
|
|
}
|
|
}
|
|
return 0;
|
|
#elif defined(HAVE_LIBPROCSTAT)
|
|
struct procstat *pstat;
|
|
struct kinfo_proc *proc;
|
|
struct filestat_list *fds;
|
|
struct filestat *fd;
|
|
struct sockstat sock;
|
|
unsigned int i, proc_count;
|
|
|
|
pstat = procstat_open_sysctl();
|
|
if (!pstat) return 0;
|
|
|
|
for (i = 0; i < num_entries; i++)
|
|
{
|
|
proc = procstat_getprocs( pstat, KERN_PROC_PID, map[i].unix_pid, &proc_count );
|
|
if (!proc || proc_count < 1) continue;
|
|
|
|
fds = procstat_getfiles( pstat, proc, 0 );
|
|
if (!fds)
|
|
{
|
|
procstat_freeprocs( pstat, proc );
|
|
continue;
|
|
}
|
|
|
|
STAILQ_FOREACH( fd, fds, next )
|
|
{
|
|
char errbuf[_POSIX2_LINE_MAX];
|
|
|
|
if (fd->fs_type != PS_FST_TYPE_SOCKET) continue;
|
|
|
|
procstat_get_socket_info( pstat, fd, &sock, errbuf );
|
|
|
|
if (sock.so_pcb == inode)
|
|
{
|
|
procstat_freefiles( pstat, fds );
|
|
procstat_freeprocs( pstat, proc );
|
|
procstat_close( pstat );
|
|
return map[i].pid;
|
|
}
|
|
}
|
|
|
|
procstat_freefiles( pstat, fds );
|
|
procstat_freeprocs( pstat, proc );
|
|
}
|
|
|
|
procstat_close( pstat );
|
|
return 0;
|
|
#elif defined(HAVE_PROC_PIDINFO)
|
|
struct proc_fdinfo *fds;
|
|
struct socket_fdinfo sock;
|
|
unsigned int i, j, n;
|
|
|
|
for (i = 0; i < num_entries; i++)
|
|
{
|
|
int fd_len = proc_pidinfo( map[i].unix_pid, PROC_PIDLISTFDS, 0, NULL, 0 );
|
|
if (fd_len <= 0) continue;
|
|
|
|
fds = HeapAlloc( GetProcessHeap(), 0, fd_len );
|
|
if (!fds) continue;
|
|
|
|
proc_pidinfo( map[i].unix_pid, PROC_PIDLISTFDS, 0, fds, fd_len );
|
|
n = fd_len / sizeof(struct proc_fdinfo);
|
|
for (j = 0; j < n; j++)
|
|
{
|
|
if (fds[j].proc_fdtype != PROX_FDTYPE_SOCKET) continue;
|
|
|
|
proc_pidfdinfo( map[i].unix_pid, fds[j].proc_fd, PROC_PIDFDSOCKETINFO, &sock, sizeof(sock) );
|
|
if (sock.psi.soi_pcb == inode)
|
|
{
|
|
HeapFree( GetProcessHeap(), 0, fds );
|
|
return map[i].pid;
|
|
}
|
|
}
|
|
|
|
HeapFree( GetProcessHeap(), 0, fds );
|
|
}
|
|
return 0;
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static BOOL match_class( TCP_TABLE_CLASS class, MIB_TCP_STATE state )
|
|
{
|
|
switch (class)
|
|
{
|
|
case TCP_TABLE_BASIC_ALL:
|
|
case TCP_TABLE_OWNER_PID_ALL:
|
|
case TCP_TABLE_OWNER_MODULE_ALL:
|
|
return TRUE;
|
|
|
|
case TCP_TABLE_BASIC_LISTENER:
|
|
case TCP_TABLE_OWNER_PID_LISTENER:
|
|
case TCP_TABLE_OWNER_MODULE_LISTENER:
|
|
if (state == MIB_TCP_STATE_LISTEN) return TRUE;
|
|
return FALSE;
|
|
|
|
case TCP_TABLE_BASIC_CONNECTIONS:
|
|
case TCP_TABLE_OWNER_PID_CONNECTIONS:
|
|
case TCP_TABLE_OWNER_MODULE_CONNECTIONS:
|
|
if (state == MIB_TCP_STATE_ESTAB) return TRUE;
|
|
return FALSE;
|
|
|
|
default:
|
|
ERR( "unhandled class %u\n", class );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
DWORD build_tcp_table( TCP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags,
|
|
DWORD *size )
|
|
{
|
|
MIB_TCPTABLE *table;
|
|
MIB_TCPROW_OWNER_MODULE row;
|
|
DWORD ret = NO_ERROR, count = 16, table_size, row_size;
|
|
|
|
if (!(table_size = get_tcp_table_sizes( class, count, &row_size )))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (!(table = HeapAlloc( heap, flags, table_size )))
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
table->dwNumEntries = 0;
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen("/proc/net/tcp", "r")))
|
|
{
|
|
char buf[512], *ptr;
|
|
struct pid_map *map = NULL;
|
|
unsigned int dummy, num_entries = 0;
|
|
int inode;
|
|
|
|
if (class >= TCP_TABLE_OWNER_PID_LISTENER) map = get_pid_map( &num_entries );
|
|
|
|
/* skip header line */
|
|
ptr = fgets(buf, sizeof(buf), fp);
|
|
while ((ptr = fgets(buf, sizeof(buf), fp)))
|
|
{
|
|
if (sscanf( ptr, "%x: %x:%x %x:%x %x %*s %*s %*s %*s %*s %d", &dummy,
|
|
&row.dwLocalAddr, &row.dwLocalPort, &row.dwRemoteAddr,
|
|
&row.dwRemotePort, &row.dwState, &inode ) != 7)
|
|
continue;
|
|
row.dwLocalPort = htons( row.dwLocalPort );
|
|
row.dwRemotePort = htons( row.dwRemotePort );
|
|
row.dwState = TCPStateToMIBState( row.dwState );
|
|
if (!match_class( class, row.dwState )) continue;
|
|
|
|
if (class >= TCP_TABLE_OWNER_PID_LISTENER)
|
|
row.dwOwningPid = find_owning_pid( map, num_entries, inode );
|
|
if (class >= TCP_TABLE_OWNER_MODULE_LISTENER)
|
|
{
|
|
row.liCreateTimestamp.QuadPart = 0; /* FIXME */
|
|
memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) );
|
|
}
|
|
if (!(table = append_tcp_row( class, heap, flags, table, &count, &row, row_size )))
|
|
break;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, map );
|
|
fclose( fp );
|
|
}
|
|
else ret = ERROR_NOT_SUPPORTED;
|
|
}
|
|
#elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
|
|
{
|
|
void *data;
|
|
int fd, len;
|
|
mib2_tcpConnEntry_t *entry;
|
|
|
|
if ((fd = open_streams_mib( "tcp" )) != -1)
|
|
{
|
|
if ((data = read_mib_entry( fd, MIB2_TCP, MIB2_TCP_CONN, &len )))
|
|
{
|
|
for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
|
|
{
|
|
row.dwLocalAddr = entry->tcpConnLocalAddress;
|
|
row.dwLocalPort = htons( entry->tcpConnLocalPort );
|
|
row.dwRemoteAddr = entry->tcpConnRemAddress;
|
|
row.dwRemotePort = htons( entry->tcpConnRemPort );
|
|
row.dwState = entry->tcpConnState;
|
|
if (!match_class( class, row.dwState )) continue;
|
|
if (!(table = append_tcp_row( class, heap, flags, table, &count, &row, row_size )))
|
|
break;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, data );
|
|
}
|
|
close( fd );
|
|
}
|
|
else ret = ERROR_NOT_SUPPORTED;
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
|
|
{
|
|
size_t Len = 0;
|
|
char *Buf = NULL;
|
|
struct xinpgen *pXIG, *pOrigXIG;
|
|
struct pid_map *pMap = NULL;
|
|
unsigned NumEntries;
|
|
|
|
if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0)
|
|
{
|
|
ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
goto done;
|
|
}
|
|
|
|
Buf = HeapAlloc (GetProcessHeap (), 0, Len);
|
|
if (!Buf)
|
|
{
|
|
ret = ERROR_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0)
|
|
{
|
|
ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
goto done;
|
|
}
|
|
|
|
if (class >= TCP_TABLE_OWNER_PID_LISTENER) pMap = get_pid_map( &NumEntries );
|
|
|
|
/* Might be nothing here; first entry is just a header it seems */
|
|
if (Len <= sizeof (struct xinpgen)) goto done;
|
|
|
|
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))
|
|
{
|
|
#if __FreeBSD_version >= 1200026
|
|
struct xtcpcb *pTCPData = (struct xtcpcb *)pXIG;
|
|
struct xinpcb *pINData = &pTCPData->xt_inp;
|
|
struct xsocket *pSockData = &pINData->xi_socket;
|
|
#else
|
|
struct tcpcb *pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
|
|
struct inpcb *pINData = &((struct xtcpcb *)pXIG)->xt_inp;
|
|
struct xsocket *pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
|
|
#endif
|
|
|
|
/* 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 */
|
|
row.dwLocalAddr = pINData->inp_laddr.s_addr;
|
|
row.dwLocalPort = pINData->inp_lport;
|
|
row.dwRemoteAddr = pINData->inp_faddr.s_addr;
|
|
row.dwRemotePort = pINData->inp_fport;
|
|
row.dwState = TCPStateToMIBState (pTCPData->t_state);
|
|
if (!match_class( class, row.dwState )) continue;
|
|
if (class >= TCP_TABLE_OWNER_PID_LISTENER)
|
|
row.dwOwningPid = find_owning_pid( pMap, NumEntries, (UINT_PTR)pSockData->so_pcb );
|
|
if (class >= TCP_TABLE_OWNER_MODULE_LISTENER)
|
|
{
|
|
row.liCreateTimestamp.QuadPart = 0; /* FIXME */
|
|
memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) );
|
|
}
|
|
if (!(table = append_tcp_row( class, heap, flags, table, &count, &row, row_size )))
|
|
break;
|
|
}
|
|
|
|
done:
|
|
HeapFree( GetProcessHeap(), 0, pMap );
|
|
HeapFree (GetProcessHeap (), 0, Buf);
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
#endif
|
|
|
|
if (!table) return ERROR_OUTOFMEMORY;
|
|
if (!ret)
|
|
{
|
|
if (order && table->dwNumEntries)
|
|
qsort( table->table, table->dwNumEntries, row_size, compare_tcp_rows );
|
|
*tablep = table;
|
|
}
|
|
else HeapFree( heap, flags, table );
|
|
if (size) *size = get_tcp_table_sizes( class, count, NULL );
|
|
TRACE( "returning ret %u table %p\n", ret, table );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************
|
|
* AllocateAndGetTcpTableFromStack (IPHLPAPI.@)
|
|
*
|
|
* Get the TCP connection table.
|
|
* Like GetTcpTable(), but allocate the returned table from heap.
|
|
*
|
|
* PARAMS
|
|
* ppTcpTable [Out] pointer into which the MIB_TCPTABLE is
|
|
* allocated and returned.
|
|
* bOrder [In] whether to sort the table
|
|
* heap [In] heap from which the table is allocated
|
|
* flags [In] flags to HeapAlloc
|
|
*
|
|
* RETURNS
|
|
* ERROR_INVALID_PARAMETER if ppTcpTable is NULL, whatever GetTcpTable()
|
|
* returns otherwise.
|
|
*/
|
|
DWORD WINAPI AllocateAndGetTcpTableFromStack( PMIB_TCPTABLE *ppTcpTable, BOOL bOrder,
|
|
HANDLE heap, DWORD flags )
|
|
{
|
|
TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppTcpTable, bOrder, heap, flags);
|
|
|
|
if (!ppTcpTable) return ERROR_INVALID_PARAMETER;
|
|
return build_tcp_table( TCP_TABLE_BASIC_ALL, (void **)ppTcpTable, bOrder, heap, flags, NULL );
|
|
}
|
|
|
|
/******************************************************************
|
|
* AllocateAndGetTcpExTableFromStack (IPHLPAPI.@)
|
|
*
|
|
* Get the TCP connection table.
|
|
* Like GetTcpTable(), but allocate the returned table from heap.
|
|
*
|
|
* PARAMS
|
|
* ppTcpTable [Out] pointer into which the MIB_TCPTABLE_EX is
|
|
* allocated and returned.
|
|
* bOrder [In] whether to sort the table
|
|
* heap [In] heap from which the table is allocated
|
|
* flags [In] flags to HeapAlloc
|
|
* family [In] address family [AF_INET|AF_INET6]
|
|
*
|
|
* RETURNS
|
|
* ERROR_INVALID_PARAMETER if ppTcpTable is NULL, whatever GetTcpTable()
|
|
* returns otherwise.
|
|
*/
|
|
DWORD WINAPI AllocateAndGetTcpExTableFromStack( VOID **ppTcpTable, BOOL bOrder,
|
|
HANDLE heap, DWORD flags, DWORD family )
|
|
{
|
|
TRACE("table %p, bOrder %d, heap %p, flags 0x%08x, family %u\n",
|
|
ppTcpTable, bOrder, heap, flags, family);
|
|
|
|
if (!ppTcpTable || !family)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (family != WS_AF_INET)
|
|
{
|
|
FIXME( "family = %u not supported\n", family );
|
|
return ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return build_tcp_table( TCP_TABLE_OWNER_PID_ALL, ppTcpTable, bOrder, heap, flags, NULL );
|
|
}
|
|
|
|
static DWORD get_udp_table_sizes( UDP_TABLE_CLASS class, DWORD row_count, DWORD *row_size )
|
|
{
|
|
DWORD table_size;
|
|
|
|
switch (class)
|
|
{
|
|
case UDP_TABLE_BASIC:
|
|
{
|
|
table_size = FIELD_OFFSET(MIB_UDPTABLE, table[row_count]);
|
|
if (row_size) *row_size = sizeof(MIB_UDPROW);
|
|
break;
|
|
}
|
|
case UDP_TABLE_OWNER_PID:
|
|
{
|
|
table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_PID, table[row_count]);
|
|
if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_PID);
|
|
break;
|
|
}
|
|
case UDP_TABLE_OWNER_MODULE:
|
|
{
|
|
table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_MODULE, table[row_count]);
|
|
if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_MODULE);
|
|
break;
|
|
}
|
|
default:
|
|
ERR("unhandled class %u\n", class);
|
|
return 0;
|
|
}
|
|
return table_size;
|
|
}
|
|
|
|
static MIB_UDPTABLE *append_udp_row( UDP_TABLE_CLASS class, HANDLE heap, DWORD flags,
|
|
MIB_UDPTABLE *table, DWORD *count,
|
|
const MIB_UDPROW_OWNER_MODULE *row, DWORD row_size )
|
|
{
|
|
if (table->dwNumEntries >= *count)
|
|
{
|
|
MIB_UDPTABLE *new_table;
|
|
DWORD new_count = table->dwNumEntries * 2, new_table_size;
|
|
|
|
new_table_size = get_udp_table_sizes( class, new_count, NULL );
|
|
if (!(new_table = HeapReAlloc( heap, flags, table, new_table_size )))
|
|
{
|
|
HeapFree( heap, 0, table );
|
|
return NULL;
|
|
}
|
|
*count = new_count;
|
|
table = new_table;
|
|
}
|
|
memcpy( (char *)table->table + (table->dwNumEntries * row_size), row, row_size );
|
|
table->dwNumEntries++;
|
|
return table;
|
|
}
|
|
|
|
static int compare_udp_rows(const void *a, const void *b)
|
|
{
|
|
const MIB_UDPROW *rowA = a;
|
|
const MIB_UDPROW *rowB = b;
|
|
int ret;
|
|
|
|
if ((ret = rowA->dwLocalAddr - rowB->dwLocalAddr) != 0) return ret;
|
|
return rowA->dwLocalPort - rowB->dwLocalPort;
|
|
}
|
|
|
|
DWORD build_udp_table( UDP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags,
|
|
DWORD *size )
|
|
{
|
|
MIB_UDPTABLE *table;
|
|
MIB_UDPROW_OWNER_MODULE row;
|
|
DWORD ret = NO_ERROR, count = 16, table_size, row_size;
|
|
|
|
if (!(table_size = get_udp_table_sizes( class, count, &row_size )))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (!(table = HeapAlloc( heap, flags, table_size )))
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
table->dwNumEntries = 0;
|
|
memset( &row, 0, sizeof(row) );
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen( "/proc/net/udp", "r" )))
|
|
{
|
|
char buf[512], *ptr;
|
|
struct pid_map *map = NULL;
|
|
unsigned int dummy, num_entries = 0;
|
|
int inode;
|
|
|
|
if (class >= UDP_TABLE_OWNER_PID) map = get_pid_map( &num_entries );
|
|
|
|
/* skip header line */
|
|
ptr = fgets( buf, sizeof(buf), fp );
|
|
while ((ptr = fgets( buf, sizeof(buf), fp )))
|
|
{
|
|
if (sscanf( ptr, "%u: %x:%x %*s %*s %*s %*s %*s %*s %*s %d", &dummy,
|
|
&row.dwLocalAddr, &row.dwLocalPort, &inode ) != 4)
|
|
continue;
|
|
row.dwLocalPort = htons( row.dwLocalPort );
|
|
|
|
if (class >= UDP_TABLE_OWNER_PID)
|
|
row.dwOwningPid = find_owning_pid( map, num_entries, inode );
|
|
if (class >= UDP_TABLE_OWNER_MODULE)
|
|
{
|
|
row.liCreateTimestamp.QuadPart = 0; /* FIXME */
|
|
row.u.dwFlags = 0;
|
|
memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) );
|
|
}
|
|
if (!(table = append_udp_row( class, heap, flags, table, &count, &row, row_size )))
|
|
break;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, map );
|
|
fclose( fp );
|
|
}
|
|
else ret = ERROR_NOT_SUPPORTED;
|
|
}
|
|
#elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
|
|
{
|
|
void *data;
|
|
int fd, len;
|
|
mib2_udpEntry_t *entry;
|
|
|
|
if ((fd = open_streams_mib( "udp" )) != -1)
|
|
{
|
|
if ((data = read_mib_entry( fd, MIB2_UDP, MIB2_UDP_ENTRY, &len )))
|
|
{
|
|
for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
|
|
{
|
|
row.dwLocalAddr = entry->udpLocalAddress;
|
|
row.dwLocalPort = htons( entry->udpLocalPort );
|
|
if (!(table = append_udp_row( class, heap, flags, table, &count, &row, row_size ))) break;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, data );
|
|
}
|
|
close( fd );
|
|
}
|
|
else ret = ERROR_NOT_SUPPORTED;
|
|
}
|
|
#elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
|
|
{
|
|
size_t Len = 0;
|
|
char *Buf = NULL;
|
|
struct xinpgen *pXIG, *pOrigXIG;
|
|
struct pid_map *pMap = NULL;
|
|
unsigned NumEntries;
|
|
|
|
if (sysctlbyname ("net.inet.udp.pcblist", NULL, &Len, NULL, 0) < 0)
|
|
{
|
|
ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n");
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
goto done;
|
|
}
|
|
|
|
Buf = HeapAlloc (GetProcessHeap (), 0, Len);
|
|
if (!Buf)
|
|
{
|
|
ret = ERROR_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
if (sysctlbyname ("net.inet.udp.pcblist", Buf, &Len, NULL, 0) < 0)
|
|
{
|
|
ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n");
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
goto done;
|
|
}
|
|
|
|
if (class >= UDP_TABLE_OWNER_PID)
|
|
pMap = get_pid_map( &NumEntries );
|
|
|
|
/* Might be nothing here; first entry is just a header it seems */
|
|
if (Len <= sizeof (struct xinpgen)) goto done;
|
|
|
|
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))
|
|
{
|
|
#if __FreeBSD_version >= 1200026
|
|
struct xinpcb *pINData = (struct xinpcb *)pXIG;
|
|
struct xsocket *pSockData = &pINData->xi_socket;
|
|
#else
|
|
struct inpcb *pINData = &((struct xinpcb *)pXIG)->xi_inp;
|
|
struct xsocket *pSockData = &((struct xinpcb *)pXIG)->xi_socket;
|
|
#endif
|
|
|
|
/* Ignore sockets for other protocols */
|
|
if (pSockData->xso_protocol != IPPROTO_UDP)
|
|
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)
|
|
continue;
|
|
|
|
/* Fill in structure details */
|
|
row.dwLocalAddr = pINData->inp_laddr.s_addr;
|
|
row.dwLocalPort = pINData->inp_lport;
|
|
if (class >= UDP_TABLE_OWNER_PID)
|
|
row.dwOwningPid = find_owning_pid( pMap, NumEntries, (UINT_PTR)pSockData->so_pcb );
|
|
if (class >= UDP_TABLE_OWNER_MODULE)
|
|
{
|
|
row.liCreateTimestamp.QuadPart = 0; /* FIXME */
|
|
row.u.dwFlags = 0;
|
|
row.u.SpecificPortBind = !(pINData->inp_flags & INP_ANONPORT);
|
|
memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) );
|
|
}
|
|
if (!(table = append_udp_row( class, heap, flags, table, &count, &row, row_size ))) break;
|
|
}
|
|
|
|
done:
|
|
HeapFree( GetProcessHeap(), 0, pMap );
|
|
HeapFree (GetProcessHeap (), 0, Buf);
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
#endif
|
|
|
|
if (!table) return ERROR_OUTOFMEMORY;
|
|
if (!ret)
|
|
{
|
|
if (order && table->dwNumEntries)
|
|
qsort( table->table, table->dwNumEntries, row_size, compare_udp_rows );
|
|
*tablep = table;
|
|
}
|
|
else HeapFree( heap, flags, table );
|
|
if (size) *size = get_udp_table_sizes( class, count, NULL );
|
|
TRACE( "returning ret %u table %p\n", ret, table );
|
|
return ret;
|
|
}
|
|
|
|
static DWORD get_udp6_table_sizes( UDP_TABLE_CLASS class, DWORD row_count, DWORD *row_size )
|
|
{
|
|
DWORD table_size;
|
|
|
|
switch (class)
|
|
{
|
|
case UDP_TABLE_BASIC:
|
|
{
|
|
table_size = FIELD_OFFSET(MIB_UDP6TABLE, table[row_count]);
|
|
if (row_size) *row_size = sizeof(MIB_UDP6ROW);
|
|
break;
|
|
}
|
|
case UDP_TABLE_OWNER_PID:
|
|
{
|
|
table_size = FIELD_OFFSET(MIB_UDP6TABLE_OWNER_PID, table[row_count]);
|
|
if (row_size) *row_size = sizeof(MIB_UDP6ROW_OWNER_PID);
|
|
break;
|
|
}
|
|
case UDP_TABLE_OWNER_MODULE:
|
|
{
|
|
table_size = FIELD_OFFSET(MIB_UDP6TABLE_OWNER_MODULE, table[row_count]);
|
|
if (row_size) *row_size = sizeof(MIB_UDP6ROW_OWNER_MODULE);
|
|
break;
|
|
}
|
|
default:
|
|
ERR("unhandled class %u\n", class);
|
|
return 0;
|
|
}
|
|
return table_size;
|
|
}
|
|
|
|
static MIB_UDP6TABLE *append_udp6_row( UDP_TABLE_CLASS class, HANDLE heap, DWORD flags,
|
|
MIB_UDP6TABLE *table, DWORD *count,
|
|
const MIB_UDP6ROW_OWNER_MODULE *row, DWORD row_size )
|
|
{
|
|
if (table->dwNumEntries >= *count)
|
|
{
|
|
MIB_UDP6TABLE *new_table;
|
|
DWORD new_count = table->dwNumEntries * 2, new_table_size;
|
|
|
|
new_table_size = get_udp6_table_sizes( class, new_count, NULL );
|
|
if (!(new_table = HeapReAlloc( heap, flags, table, new_table_size )))
|
|
{
|
|
HeapFree( heap, 0, table );
|
|
return NULL;
|
|
}
|
|
*count = new_count;
|
|
table = new_table;
|
|
}
|
|
memcpy( (char *)table->table + (table->dwNumEntries * row_size), row, row_size );
|
|
table->dwNumEntries++;
|
|
return table;
|
|
}
|
|
|
|
static int compare_udp6_rows(const void *a, const void *b)
|
|
{
|
|
const MIB_UDP6ROW *rowA = a;
|
|
const MIB_UDP6ROW *rowB = b;
|
|
int ret;
|
|
|
|
if ((ret = memcmp(&rowA->dwLocalAddr, &rowB->dwLocalAddr, sizeof(rowA->dwLocalAddr)) != 0)) return ret;
|
|
if ((ret = rowA->dwLocalScopeId - rowB->dwLocalScopeId) != 0) return ret;
|
|
return rowA->dwLocalPort - rowB->dwLocalPort;
|
|
}
|
|
|
|
struct ipv6_addr_scope
|
|
{
|
|
IN6_ADDR addr;
|
|
DWORD scope;
|
|
};
|
|
|
|
static struct ipv6_addr_scope *get_ipv6_addr_scope_table(unsigned int *size)
|
|
{
|
|
struct ipv6_addr_scope *table = NULL;
|
|
unsigned int table_size = 0;
|
|
|
|
if (!(table = HeapAlloc( GetProcessHeap(), 0, sizeof(table[0]) )))
|
|
return NULL;
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
char buf[512], *ptr;
|
|
|
|
if (!(fp = fopen( "/proc/net/if_inet6", "r" )))
|
|
goto failed;
|
|
|
|
while ((ptr = fgets( buf, sizeof(buf), fp )))
|
|
{
|
|
WORD a[8];
|
|
DWORD scope;
|
|
struct ipv6_addr_scope *new_table;
|
|
struct ipv6_addr_scope *entry;
|
|
unsigned int i;
|
|
|
|
if (sscanf( ptr, "%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx %*s %*s %x",
|
|
&a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7], &scope ) != 9)
|
|
continue;
|
|
|
|
table_size++;
|
|
if (!(new_table = HeapReAlloc( GetProcessHeap(), 0, table, table_size * sizeof(table[0]) )))
|
|
{
|
|
fclose(fp);
|
|
goto failed;
|
|
}
|
|
|
|
table = new_table;
|
|
entry = &table[table_size - 1];
|
|
|
|
i = 0;
|
|
while (i < 8)
|
|
{
|
|
entry->addr.u.Word[i] = htons(a[i]);
|
|
i++;
|
|
}
|
|
|
|
entry->scope = htons(scope);
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
goto failed;
|
|
#endif
|
|
|
|
*size = table_size;
|
|
return table;
|
|
|
|
failed:
|
|
HeapFree( GetProcessHeap(), 0, table );
|
|
return NULL;
|
|
}
|
|
|
|
static DWORD find_ipv6_addr_scope(const IN6_ADDR *addr, const struct ipv6_addr_scope *table, unsigned int size)
|
|
{
|
|
const BYTE multicast_scope_mask = 0x0F;
|
|
const BYTE multicast_scope_shift = 0;
|
|
unsigned int i = 0;
|
|
|
|
if (WS_IN6_IS_ADDR_UNSPECIFIED(addr))
|
|
return 0;
|
|
|
|
if (WS_IN6_IS_ADDR_MULTICAST(addr))
|
|
return htons((addr->u.Byte[1] & multicast_scope_mask) >> multicast_scope_shift);
|
|
|
|
if (!table)
|
|
return -1;
|
|
|
|
while (i < size)
|
|
{
|
|
if (memcmp(&table[i].addr, addr, sizeof(table[i].addr)) == 0)
|
|
return table[i].scope;
|
|
i++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
DWORD build_udp6_table( UDP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags,
|
|
DWORD *size )
|
|
{
|
|
MIB_UDP6TABLE *table;
|
|
MIB_UDP6ROW_OWNER_MODULE row;
|
|
DWORD ret = NO_ERROR, count = 16, table_size, row_size;
|
|
|
|
if (!(table_size = get_udp6_table_sizes( class, count, &row_size )))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (!(table = HeapAlloc( heap, flags, table_size )))
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
table->dwNumEntries = 0;
|
|
memset( &row, 0, sizeof(row) );
|
|
|
|
#ifdef __linux__
|
|
{
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen( "/proc/net/udp6", "r" )))
|
|
{
|
|
char buf[512], *ptr;
|
|
struct pid_map *map = NULL;
|
|
unsigned int num_entries = 0;
|
|
struct ipv6_addr_scope *addr_scopes;
|
|
unsigned int addr_scopes_size = 0;
|
|
unsigned int dummy;
|
|
int inode;
|
|
|
|
addr_scopes = get_ipv6_addr_scope_table(&addr_scopes_size);
|
|
|
|
if (class >= UDP_TABLE_OWNER_PID) map = get_pid_map( &num_entries );
|
|
|
|
/* skip header line */
|
|
ptr = fgets( buf, sizeof(buf), fp );
|
|
while ((ptr = fgets( buf, sizeof(buf), fp )))
|
|
{
|
|
DWORD in6_addr32[4];
|
|
|
|
if (sscanf( ptr, "%u: %8x%8x%8x%8x:%x %*s %*s %*s %*s %*s %*s %*s %d", &dummy,
|
|
&in6_addr32[0], &in6_addr32[1], &in6_addr32[2], &in6_addr32[3],
|
|
&row.dwLocalPort, &inode ) != 7)
|
|
continue;
|
|
memcpy(&row.ucLocalAddr, in6_addr32, sizeof(row.ucLocalAddr));
|
|
row.dwLocalScopeId = find_ipv6_addr_scope((const IN6_ADDR *)&row.ucLocalAddr, addr_scopes, addr_scopes_size);
|
|
row.dwLocalPort = htons( row.dwLocalPort );
|
|
|
|
if (class >= UDP_TABLE_OWNER_PID)
|
|
row.dwOwningPid = find_owning_pid( map, num_entries, inode );
|
|
if (class >= UDP_TABLE_OWNER_MODULE)
|
|
{
|
|
row.liCreateTimestamp.QuadPart = 0; /* FIXME */
|
|
row.u.dwFlags = 0;
|
|
memset( &row.OwningModuleInfo, 0, sizeof(row.OwningModuleInfo) );
|
|
}
|
|
if (!(table = append_udp6_row( class, heap, flags, table, &count, &row, row_size )))
|
|
break;
|
|
}
|
|
HeapFree( GetProcessHeap(), 0, map );
|
|
HeapFree( GetProcessHeap(), 0, addr_scopes );
|
|
fclose( fp );
|
|
}
|
|
else ret = ERROR_NOT_SUPPORTED;
|
|
}
|
|
#else
|
|
FIXME( "not implemented\n" );
|
|
ret = ERROR_NOT_SUPPORTED;
|
|
#endif
|
|
|
|
if (!table) return ERROR_OUTOFMEMORY;
|
|
if (!ret)
|
|
{
|
|
if (order && table->dwNumEntries)
|
|
qsort( table->table, table->dwNumEntries, row_size, compare_udp6_rows );
|
|
*tablep = table;
|
|
}
|
|
else HeapFree( heap, flags, table );
|
|
if (size) *size = get_udp6_table_sizes( class, count, NULL );
|
|
TRACE( "returning ret %u table %p\n", ret, table );
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************
|
|
* AllocateAndGetUdpTableFromStack (IPHLPAPI.@)
|
|
*
|
|
* Get the UDP listener table.
|
|
* Like GetUdpTable(), but allocate the returned table from heap.
|
|
*
|
|
* PARAMS
|
|
* ppUdpTable [Out] pointer into which the MIB_UDPTABLE is
|
|
* allocated and returned.
|
|
* bOrder [In] whether to sort the table
|
|
* heap [In] heap from which the table is allocated
|
|
* flags [In] flags to HeapAlloc
|
|
*
|
|
* RETURNS
|
|
* ERROR_INVALID_PARAMETER if ppUdpTable is NULL, whatever GetUdpTable()
|
|
* returns otherwise.
|
|
*/
|
|
DWORD WINAPI AllocateAndGetUdpTableFromStack(PMIB_UDPTABLE *ppUdpTable, BOOL bOrder,
|
|
HANDLE heap, DWORD flags)
|
|
{
|
|
TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppUdpTable, bOrder, heap, flags);
|
|
|
|
if (!ppUdpTable) return ERROR_INVALID_PARAMETER;
|
|
return build_udp_table( UDP_TABLE_BASIC, (void **)ppUdpTable, bOrder, heap, flags, NULL );
|
|
}
|