| /* | |
| * 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 | |
| #include "ifenum.h" | |
| #include "ipstats.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 (strncasecmp(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)}; | |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) | |
| size_t needed; | |
| char *buf = NULL, *end; | |
| struct if_msghdr *ifm; | |
| struct if_data ifdata; | |
| if(sysctl(mib, MIB_LEN, 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, MIB_LEN, 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 (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue; | |
| /* last line was a header, get another */ | |
| if (!(ptr = fgets(buf, sizeof(buf), fp))) break; | |
| if (!strncasecmp(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}; | |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) | |
| struct icmpstat icmp_stat; | |
| size_t needed = sizeof(icmp_stat); | |
| int i; | |
| if(sysctl(mib, MIB_LEN, &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 (!strcasecmp(buf, "Icmp6InMsgs")) | |
| { | |
| if (sscanf(value, "%d", &res)) stats->icmpInStats.dwMsgs = res; | |
| continue; | |
| } | |
| if (!strcasecmp(buf, "Icmp6InErrors")) | |
| { | |
| if (sscanf(value, "%d", &res)) stats->icmpInStats.dwErrors = res; | |
| continue; | |
| } | |
| for (i = 0; i < sizeof(icmpinstatlist)/sizeof(icmpinstatlist[0]); i++) | |
| { | |
| if (!strcasecmp(buf, icmpinstatlist[i].name)) | |
| { | |
| if (sscanf(value, "%d", &res)) | |
| stats->icmpInStats.rgdwTypeCount[icmpinstatlist[i].pos] = res; | |
| break; | |
| } | |
| } | |
| if (!strcasecmp(buf, "Icmp6OutMsgs")) | |
| { | |
| if (sscanf(value, "%d", &res)) stats->icmpOutStats.dwMsgs = res; | |
| continue; | |
| } | |
| if (!strcasecmp(buf, "Icmp6OutErrors")) | |
| { | |
| if (sscanf(value, "%d", &res)) stats->icmpOutStats.dwErrors = res; | |
| continue; | |
| } | |
| for (i = 0; i < sizeof(icmpoutstatlist)/sizeof(icmpoutstatlist[0]); i++) | |
| { | |
| if (!strcasecmp(buf, icmpoutstatlist[i].name)) | |
| { | |
| 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 < sizeof(ipstatlist)/sizeof(ipstatlist[0]); i++) | |
| if (!strcasecmp(buf, ipstatlist[i].name)) | |
| { | |
| if (sscanf(value, "%d", &res)) *ipstatlist[i].elem = res; | |
| continue; | |
| } | |
| } | |
| 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 (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue; | |
| /* last line was a header, get another */ | |
| if (!(ptr = fgets(buf, sizeof(buf), fp))) break; | |
| if (!strncasecmp(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}; | |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) | |
| 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, MIB_LEN, &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; | |
| } | |
| stats->u.dwForwarding = ip_forwarding; | |
| stats->dwDefaultTTL = ip_ttl; | |
| stats->dwInDelivers = ip_stat.ips_delivered; | |
| stats->dwInHdrErrors = ip_stat.ips_badhlen + ip_stat.ips_badsum + ip_stat.ips_tooshort + ip_stat.ips_badlen; | |
| stats->dwInAddrErrors = ip_stat.ips_cantforward; | |
| stats->dwInReceives = ip_stat.ips_total; | |
| stats->dwForwDatagrams = ip_stat.ips_forward; | |
| stats->dwInUnknownProtos = ip_stat.ips_noproto; | |
| stats->dwInDiscards = ip_stat.ips_fragdropped; | |
| stats->dwOutDiscards = ip_stat.ips_odropped; | |
| stats->dwReasmOks = ip_stat.ips_reassembled; | |
| stats->dwFragOks = ip_stat.ips_fragmented; | |
| stats->dwFragFails = ip_stat.ips_cantfrag; | |
| stats->dwReasmTimeout = ip_stat.ips_fragtimeout; | |
| stats->dwOutNoRoutes = ip_stat.ips_noroute; | |
| stats->dwOutRequests = ip_stat.ips_localout; | |
| stats->dwReasmReqds = ip_stat.ips_fragments; | |
| 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 (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue; | |
| /* last line was a header, get another */ | |
| if (!(ptr = fgets(buf, sizeof(buf), fp))) break; | |
| if (!strncasecmp(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 MIB_LEN (sizeof(mib) / sizeof(mib[0])) | |
| #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, MIB_LEN, &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 < sizeof(udpstatlist)/sizeof(udpstatlist[0]); i++) | |
| if (!strcasecmp(buf, udpstatlist[i].name)) | |
| { | |
| if (sscanf(value, "%d", &res)) *udpstatlist[i].elem = res; | |
| continue; | |
| } | |
| } | |
| 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 (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue; | |
| /* last line was a header, get another */ | |
| if (!(ptr = fgets(buf, sizeof(buf), fp))) break; | |
| if (!strncasecmp(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}; | |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) | |
| struct udpstat udp_stat; | |
| MIB_UDPTABLE *udp_table; | |
| size_t needed = sizeof(udp_stat); | |
| if(sysctl(mib, MIB_LEN, &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}; | |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) | |
| size_t needed; | |
| char *buf = NULL, *lim, *next; | |
| struct rt_msghdr *rtm; | |
| struct sockaddr_inarp *sinarp; | |
| struct sockaddr_dl *sdl; | |
| if (sysctl (mib, MIB_LEN, 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, MIB_LEN, 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, fd_len; | |
| for (i = 0; i < num_entries; i++) | |
| { | |
| 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)) | |
| { | |
| 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 */ | |
| 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 ); | |
| } | |
| 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)) | |
| { | |
| struct inpcb *pINData; | |
| struct xsocket *pSockData; | |
| pINData = &((struct xinpcb *)pXIG)->xi_inp; | |
| pSockData = &((struct xinpcb *)pXIG)->xi_socket; | |
| /* 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; | |
| 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; | |
| } | |
| /****************************************************************** | |
| * 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 ); | |
| } |