| /* |
| * 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_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 |
| |
| #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 |
| |
| #define NONAMELESSUNION |
| #include "ifenum.h" |
| #include "ipstats.h" |
| |
| #include "wine/debug.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) |
| { |
| 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) |
| { |
| /*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]; |
| |
| #ifdef HAVE_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 /* ICPS_OUTHIST */ |
| ret = NO_ERROR; |
| } |
| } |
| #else /* ICMPCTL_STATS */ |
| FIXME( "unimplemented\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) |
| { |
| DWORD ret = ERROR_NOT_SUPPORTED; |
| MIB_IPFORWARDTABLE *fwd_table; |
| |
| if (!stats) return ERROR_INVALID_PARAMETER; |
| memset( stats, 0, sizeof(*stats) ); |
| |
| stats->dwNumIf = stats->dwNumAddr = getNumInterfaces(); |
| if (!AllocateAndGetIpForwardTableFromStack( &fwd_table, FALSE, GetProcessHeap(), 0 )) |
| { |
| stats->dwNumRoutes = fwd_table->dwNumEntries; |
| HeapFree( GetProcessHeap(), 0, fwd_table ); |
| } |
| |
| #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) |
| { |
| int mib[] = {CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS}; |
| #define MIB_LEN (sizeof(mib) / sizeof(mib[0])) |
| int ip_ttl, ip_forwarding; |
| struct ipstat ip_stat; |
| 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\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) |
| { |
| DWORD ret = ERROR_NOT_SUPPORTED; |
| |
| if (!stats) return ERROR_INVALID_PARAMETER; |
| memset( stats, 0, sizeof(*stats) ); |
| |
| #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(UDPCTL_STATS) |
| { |
| #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 |
| struct tcpstat tcp_stat; |
| 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; |
| } |
| |
| |
| /****************************************************************** |
| * 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) |
| { |
| DWORD ret = ERROR_NOT_SUPPORTED; |
| |
| if (!stats) return ERROR_INVALID_PARAMETER; |
| memset( stats, 0, sizeof(*stats) ); |
| |
| #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) |
| { |
| 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\n" ); |
| #endif |
| return ret; |
| } |
| |
| |
| 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 all entries except for gateway routes which aren't |
| 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 = MIB_IPROUTE_TYPE_INDIRECT; |
| 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 if (sa->sa_family != AF_INET) |
| { |
| WARN ("Received unsupported sockaddr family 0x%x\n", |
| sa->sa_family); |
| addr = 0; |
| } |
| else |
| { |
| struct sockaddr_in *sin = (struct sockaddr_in *)sa; |
| |
| addr = sin->sin_addr.s_addr; |
| } |
| |
| switch (i) |
| { |
| case RTA_DST: 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 MIB_UDPTABLE *append_udp_row( HANDLE heap, DWORD flags, MIB_UDPTABLE *table, |
| DWORD *count, const MIB_UDPROW *row ) |
| { |
| if (table->dwNumEntries >= *count) |
| { |
| MIB_UDPTABLE *new_table; |
| DWORD new_count = table->dwNumEntries * 2; |
| |
| if (!(new_table = HeapReAlloc( heap, flags, table, FIELD_OFFSET(MIB_UDPTABLE, 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_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; |
| } |
| |
| |
| /****************************************************************** |
| * 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) |
| { |
| MIB_UDPTABLE *table; |
| MIB_UDPROW row; |
| DWORD ret = NO_ERROR, count = 16; |
| |
| TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppUdpTable, bOrder, heap, flags); |
| |
| if (!ppUdpTable) return ERROR_INVALID_PARAMETER; |
| |
| if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_UDPTABLE, table[count] )))) |
| return ERROR_OUTOFMEMORY; |
| |
| table->dwNumEntries = 0; |
| |
| #ifdef __linux__ |
| { |
| FILE *fp; |
| |
| if ((fp = fopen("/proc/net/udp", "r"))) |
| { |
| char buf[512], *ptr; |
| DWORD dummy; |
| |
| /* skip header line */ |
| ptr = fgets(buf, sizeof(buf), fp); |
| while ((ptr = fgets(buf, sizeof(buf), fp))) |
| { |
| if (sscanf( ptr, "%u: %x:%x", &dummy, &row.dwLocalAddr, &row.dwLocalPort ) != 3) |
| continue; |
| row.dwLocalPort = htons( row.dwLocalPort ); |
| if (!(table = append_udp_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; |
| 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( heap, flags, table, &count, &row ))) 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; |
| |
| 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; |
| } |
| |
| /* 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 (!(table = append_udp_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_udp_rows ); |
| *ppUdpTable = table; |
| } |
| else HeapFree( heap, flags, table ); |
| TRACE( "returning ret %u table %p\n", ret, table ); |
| return ret; |
| } |
| |
| |
| static MIB_TCPTABLE *append_tcp_row( HANDLE heap, DWORD flags, MIB_TCPTABLE *table, |
| DWORD *count, const MIB_TCPROW *row ) |
| { |
| if (table->dwNumEntries >= *count) |
| { |
| MIB_TCPTABLE *new_table; |
| DWORD new_count = table->dwNumEntries * 2; |
| |
| if (!(new_table = HeapReAlloc( heap, flags, table, FIELD_OFFSET(MIB_TCPTABLE, 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; |
| } |
| |
| |
| /* 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); |
| } |
| |
| |
| /****************************************************************** |
| * 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) |
| { |
| MIB_TCPTABLE *table; |
| MIB_TCPROW row; |
| DWORD ret = NO_ERROR, count = 16; |
| |
| TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppTcpTable, bOrder, heap, flags); |
| |
| if (!ppTcpTable) return ERROR_INVALID_PARAMETER; |
| |
| if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_TCPTABLE, table[count] )))) |
| return ERROR_OUTOFMEMORY; |
| |
| table->dwNumEntries = 0; |
| |
| #ifdef __linux__ |
| { |
| FILE *fp; |
| |
| if ((fp = fopen("/proc/net/tcp", "r"))) |
| { |
| char buf[512], *ptr; |
| DWORD dummy; |
| |
| /* 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", &dummy, &row.dwLocalAddr, &row.dwLocalPort, |
| &row.dwRemoteAddr, &row.dwRemotePort, &row.u.dwState ) != 6) |
| continue; |
| row.dwLocalPort = htons( row.dwLocalPort ); |
| row.dwRemotePort = htons( row.dwRemotePort ); |
| row.u.State = TCPStateToMIBState( row.u.dwState ); |
| if (!(table = append_tcp_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; |
| 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.u.dwState = entry->tcpConnState; |
| if (!(table = append_tcp_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(HAVE_STRUCT_XINPGEN) |
| { |
| size_t Len = 0; |
| char *Buf = NULL; |
| struct xinpgen *pXIG, *pOrigXIG; |
| |
| 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; |
| } |
| |
| /* 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.u.State = TCPStateToMIBState (pTCPData->t_state); |
| if (!(table = append_tcp_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_tcp_rows ); |
| *ppTcpTable = table; |
| } |
| else HeapFree( heap, flags, table ); |
| TRACE( "returning ret %u table %p\n", ret, table ); |
| return ret; |
| } |