ws2_32/tests: Add SO_ERROR [set|get]sockopt tests.
[wine] / dlls / iphlpapi / ipstats.c
1 /*
2  * Copyright (C) 2003,2006 Juan Lang
3  * Copyright (C) 2007 TransGaming Technologies Inc.
4  * Copyright (C) 2009 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #ifdef HAVE_DIRENT_H
31 #include <dirent.h>
32 #endif
33 #ifdef HAVE_ALIAS_H
34 #include <alias.h>
35 #endif
36 #ifdef HAVE_SYS_SOCKET_H
37 #include <sys/socket.h>
38 #endif
39 #ifdef HAVE_SYS_SOCKETVAR_H
40 #include <sys/socketvar.h>
41 #endif
42 #ifdef HAVE_SYS_TIMEOUT_H
43 #include <sys/timeout.h>
44 #endif
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif
48 #ifdef HAVE_NETINET_IN_SYSTM_H
49 #include <netinet/in_systm.h>
50 #endif
51 #ifdef HAVE_ARPA_INET_H
52 #include <arpa/inet.h>
53 #endif
54 #ifdef HAVE_NET_IF_H
55 #include <net/if.h>
56 #endif
57 #ifdef HAVE_NET_IF_DL_H
58 #include <net/if_dl.h>
59 #endif
60 #ifdef HAVE_NET_IF_TYPES_H
61 #include <net/if_types.h>
62 #endif
63 #ifdef HAVE_NET_ROUTE_H
64 #include <net/route.h>
65 #endif
66 #ifdef HAVE_NET_IF_ARP_H
67 #include <net/if_arp.h>
68 #endif
69 #ifdef HAVE_NETINET_IF_ETHER_H
70 #include <netinet/if_ether.h>
71 #endif
72 #ifdef HAVE_NETINET_IF_INARP_H
73 #include <netinet/if_inarp.h>
74 #endif
75 #ifdef HAVE_NETINET_IP_H
76 #include <netinet/ip.h>
77 #endif
78 #ifdef HAVE_NETINET_TCP_H
79 #include <netinet/tcp.h>
80 #endif
81 #ifdef HAVE_NETINET_IP_VAR_H
82 #include <netinet/ip_var.h>
83 #endif
84 #ifdef HAVE_NETINET_TCP_FSM_H
85 #include <netinet/tcp_fsm.h>
86 #endif
87 #ifdef HAVE_NETINET_IN_PCB_H
88 #include <netinet/in_pcb.h>
89 #endif
90 #ifdef HAVE_NETINET_TCP_TIMER_H
91 #include <netinet/tcp_timer.h>
92 #endif
93 #ifdef HAVE_NETINET_TCP_VAR_H
94 #include <netinet/tcp_var.h>
95 #endif
96 #ifdef HAVE_NETINET_IP_ICMP_H
97 #include <netinet/ip_icmp.h>
98 #endif
99 #ifdef HAVE_NETINET_ICMP_VAR_H
100 #include <netinet/icmp_var.h>
101 #endif
102 #ifdef HAVE_NETINET_UDP_H
103 #include <netinet/udp.h>
104 #endif
105 #ifdef HAVE_NETINET_UDP_VAR_H
106 #include <netinet/udp_var.h>
107 #endif
108 #ifdef HAVE_SYS_PROTOSW_H
109 #include <sys/protosw.h>
110 #endif
111 #ifdef HAVE_SYS_SYSCTL_H
112 #include <sys/sysctl.h>
113 #endif
114 #ifdef HAVE_KSTAT_H
115 #include <kstat.h>
116 #endif
117 #ifdef HAVE_INET_MIB2_H
118 #include <inet/mib2.h>
119 #endif
120 #ifdef HAVE_STROPTS_H
121 #include <stropts.h>
122 #endif
123 #ifdef HAVE_SYS_TIHDR_H
124 #include <sys/tihdr.h>
125 #endif
126
127 #ifndef ROUNDUP
128 #define ROUNDUP(a) \
129         ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
130 #endif
131 #ifndef ADVANCE
132 #define ADVANCE(x, n) (x += ROUNDUP(((struct sockaddr *)n)->sa_len))
133 #endif
134
135 #include "ntstatus.h"
136 #define WIN32_NO_STATUS
137 #define NONAMELESSUNION
138 #include "ifenum.h"
139 #include "ipstats.h"
140
141 #include "wine/debug.h"
142 #include "wine/server.h"
143
144 #ifndef HAVE_NETINET_TCP_FSM_H
145 #define TCPS_ESTABLISHED  1
146 #define TCPS_SYN_SENT     2
147 #define TCPS_SYN_RECEIVED 3
148 #define TCPS_FIN_WAIT_1   4
149 #define TCPS_FIN_WAIT_2   5
150 #define TCPS_TIME_WAIT    6
151 #define TCPS_CLOSED       7
152 #define TCPS_CLOSE_WAIT   8
153 #define TCPS_LAST_ACK     9
154 #define TCPS_LISTEN      10
155 #define TCPS_CLOSING     11
156 #endif
157
158 #ifndef RTF_MULTICAST
159 #define RTF_MULTICAST 0 /* Not available on NetBSD/OpenBSD */
160 #endif
161
162 #ifndef RTF_LLINFO
163 #define RTF_LLINFO 0 /* Not available on FreeBSD 8 and above */
164 #endif
165
166 WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
167
168 #ifdef HAVE_LIBKSTAT
169 static DWORD kstat_get_ui32( kstat_t *ksp, const char *name )
170 {
171     unsigned int i;
172     kstat_named_t *data = ksp->ks_data;
173
174     for (i = 0; i < ksp->ks_ndata; i++)
175         if (!strcmp( data[i].name, name )) return data[i].value.ui32;
176     return 0;
177 }
178
179 static ULONGLONG kstat_get_ui64( kstat_t *ksp, const char *name )
180 {
181     unsigned int i;
182     kstat_named_t *data = ksp->ks_data;
183
184     for (i = 0; i < ksp->ks_ndata; i++)
185         if (!strcmp( data[i].name, name )) return data[i].value.ui64;
186     return 0;
187 }
188 #endif
189
190 #if defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
191 static int open_streams_mib( const char *proto )
192 {
193     int fd;
194     struct strbuf buf;
195     struct request
196     {
197         struct T_optmgmt_req req_header;
198         struct opthdr        opt_header;
199     } request;
200
201     if ((fd = open( "/dev/arp", O_RDWR )) == -1)
202     {
203         WARN( "could not open /dev/arp: %s\n", strerror(errno) );
204         return -1;
205     }
206     if (proto) ioctl( fd, I_PUSH, proto );
207
208     request.req_header.PRIM_type  = T_SVR4_OPTMGMT_REQ;
209     request.req_header.OPT_length = sizeof(request.opt_header);
210     request.req_header.OPT_offset = FIELD_OFFSET( struct request, opt_header );
211     request.req_header.MGMT_flags = T_CURRENT;
212     request.opt_header.level      = MIB2_IP;
213     request.opt_header.name       = 0;
214     request.opt_header.len        = 0;
215
216     buf.len = sizeof(request);
217     buf.buf = (caddr_t)&request;
218     if (putmsg( fd, &buf, NULL, 0 ) == -1)
219     {
220         WARN( "putmsg: %s\n", strerror(errno) );
221         close( fd );
222         fd = -1;
223     }
224     return fd;
225 }
226
227 static void *read_mib_entry( int fd, int level, int name, int *len )
228 {
229     struct strbuf buf;
230     void *data;
231     int ret, flags = 0;
232
233     struct reply
234     {
235         struct T_optmgmt_ack ack_header;
236         struct opthdr        opt_header;
237     } reply;
238
239     for (;;)
240     {
241         buf.maxlen = sizeof(reply);
242         buf.buf = (caddr_t)&reply;
243         if ((ret = getmsg( fd, &buf, NULL, &flags )) < 0) return NULL;
244         if (!(ret & MOREDATA)) return NULL;
245         if (reply.ack_header.PRIM_type != T_OPTMGMT_ACK) return NULL;
246         if (buf.len < sizeof(reply.ack_header)) return NULL;
247         if (reply.ack_header.OPT_length < sizeof(reply.opt_header)) return NULL;
248
249         if (!(data = HeapAlloc( GetProcessHeap(), 0, reply.opt_header.len ))) return NULL;
250         buf.maxlen = reply.opt_header.len;
251         buf.buf = (caddr_t)data;
252         flags = 0;
253         if (getmsg( fd, NULL, &buf, &flags ) >= 0 &&
254             reply.opt_header.level == level &&
255             reply.opt_header.name == name)
256         {
257             *len = buf.len;
258             return data;
259         }
260         HeapFree( GetProcessHeap(), 0, data );
261     }
262 }
263 #endif /* HAVE_SYS_TIHDR_H && T_OPTMGMT_ACK */
264
265 DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry)
266 {
267     DWORD ret = ERROR_NOT_SUPPORTED;
268
269     if (!name || !entry) return ERROR_INVALID_PARAMETER;
270
271 #ifdef __linux__
272     {
273         FILE *fp;
274
275         if ((fp = fopen("/proc/net/dev", "r")))
276         {
277             DWORD skip;
278             char buf[512], *ptr;
279             int nameLen = strlen(name);
280
281             while ((ptr = fgets(buf, sizeof(buf), fp)))
282             {
283                 while (*ptr && isspace(*ptr)) ptr++;
284                 if (strncasecmp(ptr, name, nameLen) == 0 && *(ptr + nameLen) == ':')
285                 {
286                     ptr += nameLen + 1;
287                     sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u",
288                             &entry->dwInOctets, &entry->dwInUcastPkts,
289                             &entry->dwInErrors, &entry->dwInDiscards,
290                             &skip, &skip, &skip,
291                             &entry->dwInNUcastPkts, &entry->dwOutOctets,
292                             &entry->dwOutUcastPkts, &entry->dwOutErrors,
293                             &entry->dwOutDiscards );
294                     break;
295                 }
296             }
297             fclose(fp);
298             ret = NO_ERROR;
299         }
300     }
301 #elif defined(HAVE_LIBKSTAT)
302     {
303         kstat_ctl_t *kc;
304         kstat_t *ksp;
305
306         if ((kc = kstat_open()) &&
307             (ksp = kstat_lookup( kc, NULL, -1, (char *)name )) &&
308             kstat_read( kc, ksp, NULL ) != -1 &&
309             ksp->ks_type == KSTAT_TYPE_NAMED)
310         {
311             entry->dwMtu             = 1500;  /* FIXME */
312             entry->dwSpeed           = min( kstat_get_ui64( ksp, "ifspeed" ), ~0u );
313             entry->dwInOctets        = kstat_get_ui32( ksp, "rbytes" );
314             entry->dwInNUcastPkts    = kstat_get_ui32( ksp, "multircv" );
315             entry->dwInNUcastPkts   += kstat_get_ui32( ksp, "brdcstrcv" );
316             entry->dwInUcastPkts     = kstat_get_ui32( ksp, "ipackets" ) - entry->dwInNUcastPkts;
317             entry->dwInDiscards      = kstat_get_ui32( ksp, "norcvbuf" );
318             entry->dwInErrors        = kstat_get_ui32( ksp, "ierrors" );
319             entry->dwInUnknownProtos = kstat_get_ui32( ksp, "unknowns" );
320             entry->dwOutOctets       = kstat_get_ui32( ksp, "obytes" );
321             entry->dwOutNUcastPkts   = kstat_get_ui32( ksp, "multixmt" );
322             entry->dwOutNUcastPkts  += kstat_get_ui32( ksp, "brdcstxmt" );
323             entry->dwOutUcastPkts    = kstat_get_ui32( ksp, "opackets" ) - entry->dwOutNUcastPkts;
324             entry->dwOutDiscards     = 0;  /* FIXME */
325             entry->dwOutErrors       = kstat_get_ui32( ksp, "oerrors" );
326             entry->dwOutQLen         = kstat_get_ui32( ksp, "noxmtbuf" );
327             ret = NO_ERROR;
328         }
329         if (kc) kstat_close( kc );
330     }
331 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST)
332     {
333         int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, if_nametoindex(name)};
334 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
335
336         size_t needed;
337         char *buf = NULL, *end;
338         struct if_msghdr *ifm;
339         struct if_data ifdata;
340
341         if(sysctl(mib, MIB_LEN, NULL, &needed, NULL, 0) == -1)
342         {
343             ERR ("failed to get size of iflist\n");
344             goto done;
345         }
346         buf = HeapAlloc (GetProcessHeap (), 0, needed);
347         if (!buf)
348         {
349             ret = ERROR_OUTOFMEMORY;
350             goto done;
351         }
352         if(sysctl(mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
353         {
354             ERR ("failed to get iflist\n");
355             goto done;
356         }
357         for ( end = buf + needed; buf < end; buf += ifm->ifm_msglen)
358         {
359             ifm = (struct if_msghdr *) buf;
360             if(ifm->ifm_type == RTM_IFINFO)
361             {
362                 ifdata = ifm->ifm_data;
363                 entry->dwMtu = ifdata.ifi_mtu;
364                 entry->dwSpeed = ifdata.ifi_baudrate;
365                 entry->dwInOctets = ifdata.ifi_ibytes;
366                 entry->dwInErrors = ifdata.ifi_ierrors;
367                 entry->dwInDiscards = ifdata.ifi_iqdrops;
368                 entry->dwInUcastPkts = ifdata.ifi_ipackets;
369                 entry->dwInNUcastPkts = ifdata.ifi_imcasts;
370                 entry->dwOutOctets = ifdata.ifi_obytes;
371                 entry->dwOutUcastPkts = ifdata.ifi_opackets;
372                 entry->dwOutErrors = ifdata.ifi_oerrors;
373                 ret = NO_ERROR;
374                 break;
375             }
376         }
377     done:
378         HeapFree (GetProcessHeap (), 0, buf);
379     }
380 #else
381     FIXME( "unimplemented\n" );
382 #endif
383     return ret;
384 }
385
386
387 /******************************************************************
388  *    GetIcmpStatistics (IPHLPAPI.@)
389  *
390  * Get the ICMP statistics for the local computer.
391  *
392  * PARAMS
393  *  stats [Out] buffer for ICMP statistics
394  *
395  * RETURNS
396  *  Success: NO_ERROR
397  *  Failure: error code from winerror.h
398  */
399 DWORD WINAPI GetIcmpStatistics(PMIB_ICMP stats)
400 {
401     DWORD ret = ERROR_NOT_SUPPORTED;
402
403     if (!stats) return ERROR_INVALID_PARAMETER;
404     memset( stats, 0, sizeof(MIB_ICMP) );
405
406 #ifdef __linux__
407     {
408         FILE *fp;
409
410         if ((fp = fopen("/proc/net/snmp", "r")))
411         {
412             static const char hdr[] = "Icmp:";
413             char buf[512], *ptr;
414
415             while ((ptr = fgets(buf, sizeof(buf), fp)))
416             {
417                 if (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue;
418                 /* last line was a header, get another */
419                 if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
420                 if (!strncasecmp(buf, hdr, sizeof(hdr) - 1))
421                 {
422                     ptr += sizeof(hdr);
423                     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",
424                             &stats->stats.icmpInStats.dwMsgs,
425                             &stats->stats.icmpInStats.dwErrors,
426                             &stats->stats.icmpInStats.dwDestUnreachs,
427                             &stats->stats.icmpInStats.dwTimeExcds,
428                             &stats->stats.icmpInStats.dwParmProbs,
429                             &stats->stats.icmpInStats.dwSrcQuenchs,
430                             &stats->stats.icmpInStats.dwRedirects,
431                             &stats->stats.icmpInStats.dwEchoReps,
432                             &stats->stats.icmpInStats.dwTimestamps,
433                             &stats->stats.icmpInStats.dwTimestampReps,
434                             &stats->stats.icmpInStats.dwAddrMasks,
435                             &stats->stats.icmpInStats.dwAddrMaskReps,
436                             &stats->stats.icmpOutStats.dwMsgs,
437                             &stats->stats.icmpOutStats.dwErrors,
438                             &stats->stats.icmpOutStats.dwDestUnreachs,
439                             &stats->stats.icmpOutStats.dwTimeExcds,
440                             &stats->stats.icmpOutStats.dwParmProbs,
441                             &stats->stats.icmpOutStats.dwSrcQuenchs,
442                             &stats->stats.icmpOutStats.dwRedirects,
443                             &stats->stats.icmpOutStats.dwEchoReps,
444                             &stats->stats.icmpOutStats.dwTimestamps,
445                             &stats->stats.icmpOutStats.dwTimestampReps,
446                             &stats->stats.icmpOutStats.dwAddrMasks,
447                             &stats->stats.icmpOutStats.dwAddrMaskReps );
448                     break;
449                 }
450             }
451             fclose(fp);
452             ret = NO_ERROR;
453         }
454     }
455 #elif defined(HAVE_LIBKSTAT)
456     {
457         static char ip[] = "ip", icmp[] = "icmp";
458         kstat_ctl_t *kc;
459         kstat_t *ksp;
460
461         if ((kc = kstat_open()) &&
462             (ksp = kstat_lookup( kc, ip, 0, icmp )) &&
463             kstat_read( kc, ksp, NULL ) != -1 &&
464             ksp->ks_type == KSTAT_TYPE_NAMED)
465         {
466             stats->stats.icmpInStats.dwMsgs           = kstat_get_ui32( ksp, "inMsgs" );
467             stats->stats.icmpInStats.dwErrors         = kstat_get_ui32( ksp, "inErrors" );
468             stats->stats.icmpInStats.dwDestUnreachs   = kstat_get_ui32( ksp, "inDestUnreachs" );
469             stats->stats.icmpInStats.dwTimeExcds      = kstat_get_ui32( ksp, "inTimeExcds" );
470             stats->stats.icmpInStats.dwParmProbs      = kstat_get_ui32( ksp, "inParmProbs" );
471             stats->stats.icmpInStats.dwSrcQuenchs     = kstat_get_ui32( ksp, "inSrcQuenchs" );
472             stats->stats.icmpInStats.dwRedirects      = kstat_get_ui32( ksp, "inRedirects" );
473             stats->stats.icmpInStats.dwEchos          = kstat_get_ui32( ksp, "inEchos" );
474             stats->stats.icmpInStats.dwEchoReps       = kstat_get_ui32( ksp, "inEchoReps" );
475             stats->stats.icmpInStats.dwTimestamps     = kstat_get_ui32( ksp, "inTimestamps" );
476             stats->stats.icmpInStats.dwTimestampReps  = kstat_get_ui32( ksp, "inTimestampReps" );
477             stats->stats.icmpInStats.dwAddrMasks      = kstat_get_ui32( ksp, "inAddrMasks" );
478             stats->stats.icmpInStats.dwAddrMaskReps   = kstat_get_ui32( ksp, "inAddrMaskReps" );
479             stats->stats.icmpOutStats.dwMsgs          = kstat_get_ui32( ksp, "outMsgs" );
480             stats->stats.icmpOutStats.dwErrors        = kstat_get_ui32( ksp, "outErrors" );
481             stats->stats.icmpOutStats.dwDestUnreachs  = kstat_get_ui32( ksp, "outDestUnreachs" );
482             stats->stats.icmpOutStats.dwTimeExcds     = kstat_get_ui32( ksp, "outTimeExcds" );
483             stats->stats.icmpOutStats.dwParmProbs     = kstat_get_ui32( ksp, "outParmProbs" );
484             stats->stats.icmpOutStats.dwSrcQuenchs    = kstat_get_ui32( ksp, "outSrcQuenchs" );
485             stats->stats.icmpOutStats.dwRedirects     = kstat_get_ui32( ksp, "outRedirects" );
486             stats->stats.icmpOutStats.dwEchos         = kstat_get_ui32( ksp, "outEchos" );
487             stats->stats.icmpOutStats.dwEchoReps      = kstat_get_ui32( ksp, "outEchoReps" );
488             stats->stats.icmpOutStats.dwTimestamps    = kstat_get_ui32( ksp, "outTimestamps" );
489             stats->stats.icmpOutStats.dwTimestampReps = kstat_get_ui32( ksp, "outTimestampReps" );
490             stats->stats.icmpOutStats.dwAddrMasks     = kstat_get_ui32( ksp, "outAddrMasks" );
491             stats->stats.icmpOutStats.dwAddrMaskReps  = kstat_get_ui32( ksp, "outAddrMaskReps" );
492             ret = NO_ERROR;
493         }
494         if (kc) kstat_close( kc );
495     }
496 #elif defined(HAVE_SYS_SYSCTL_H) && defined(ICMPCTL_STATS) && defined(HAVE_STRUCT_ICMPSTAT_ICPS_INHIST)
497     {
498         int mib[] = {CTL_NET, PF_INET, IPPROTO_ICMP, ICMPCTL_STATS};
499 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
500         struct icmpstat icmp_stat;
501         size_t needed  = sizeof(icmp_stat);
502         int i;
503
504         if(sysctl(mib, MIB_LEN, &icmp_stat, &needed, NULL, 0) != -1)
505         {
506             /*in stats */
507             stats->stats.icmpInStats.dwMsgs = icmp_stat.icps_badcode + icmp_stat.icps_checksum + icmp_stat.icps_tooshort + icmp_stat.icps_badlen;
508             for(i = 0; i <= ICMP_MAXTYPE; i++)
509                 stats->stats.icmpInStats.dwMsgs += icmp_stat.icps_inhist[i];
510
511             stats->stats.icmpInStats.dwErrors = icmp_stat.icps_badcode + icmp_stat.icps_tooshort + icmp_stat.icps_checksum + icmp_stat.icps_badlen;
512
513             stats->stats.icmpInStats.dwDestUnreachs = icmp_stat.icps_inhist[ICMP_UNREACH];
514             stats->stats.icmpInStats.dwTimeExcds = icmp_stat.icps_inhist[ICMP_TIMXCEED];
515             stats->stats.icmpInStats.dwParmProbs = icmp_stat.icps_inhist[ICMP_PARAMPROB];
516             stats->stats.icmpInStats.dwSrcQuenchs = icmp_stat.icps_inhist[ICMP_SOURCEQUENCH];
517             stats->stats.icmpInStats.dwRedirects = icmp_stat.icps_inhist[ICMP_REDIRECT];
518             stats->stats.icmpInStats.dwEchos = icmp_stat.icps_inhist[ICMP_ECHO];
519             stats->stats.icmpInStats.dwEchoReps = icmp_stat.icps_inhist[ICMP_ECHOREPLY];
520             stats->stats.icmpInStats.dwTimestamps = icmp_stat.icps_inhist[ICMP_TSTAMP];
521             stats->stats.icmpInStats.dwTimestampReps = icmp_stat.icps_inhist[ICMP_TSTAMPREPLY];
522             stats->stats.icmpInStats.dwAddrMasks = icmp_stat.icps_inhist[ICMP_MASKREQ];
523             stats->stats.icmpInStats.dwAddrMaskReps = icmp_stat.icps_inhist[ICMP_MASKREPLY];
524
525 #ifdef HAVE_STRUCT_ICMPSTAT_ICPS_OUTHIST
526             /* out stats */
527             stats->stats.icmpOutStats.dwMsgs = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;
528             for(i = 0; i <= ICMP_MAXTYPE; i++)
529                 stats->stats.icmpOutStats.dwMsgs += icmp_stat.icps_outhist[i];
530
531             stats->stats.icmpOutStats.dwErrors = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;
532
533             stats->stats.icmpOutStats.dwDestUnreachs = icmp_stat.icps_outhist[ICMP_UNREACH];
534             stats->stats.icmpOutStats.dwTimeExcds = icmp_stat.icps_outhist[ICMP_TIMXCEED];
535             stats->stats.icmpOutStats.dwParmProbs = icmp_stat.icps_outhist[ICMP_PARAMPROB];
536             stats->stats.icmpOutStats.dwSrcQuenchs = icmp_stat.icps_outhist[ICMP_SOURCEQUENCH];
537             stats->stats.icmpOutStats.dwRedirects = icmp_stat.icps_outhist[ICMP_REDIRECT];
538             stats->stats.icmpOutStats.dwEchos = icmp_stat.icps_outhist[ICMP_ECHO];
539             stats->stats.icmpOutStats.dwEchoReps = icmp_stat.icps_outhist[ICMP_ECHOREPLY];
540             stats->stats.icmpOutStats.dwTimestamps = icmp_stat.icps_outhist[ICMP_TSTAMP];
541             stats->stats.icmpOutStats.dwTimestampReps = icmp_stat.icps_outhist[ICMP_TSTAMPREPLY];
542             stats->stats.icmpOutStats.dwAddrMasks = icmp_stat.icps_outhist[ICMP_MASKREQ];
543             stats->stats.icmpOutStats.dwAddrMaskReps = icmp_stat.icps_outhist[ICMP_MASKREPLY];
544 #endif /* HAVE_STRUCT_ICMPSTAT_ICPS_OUTHIST */
545             ret = NO_ERROR;
546         }
547     }
548 #else /* ICMPCTL_STATS */
549     FIXME( "unimplemented\n" );
550 #endif
551     return ret;
552 }
553
554 /******************************************************************
555  *    GetIcmpStatisticsEx (IPHLPAPI.@)
556  *
557  * Get the IPv4 and IPv6 ICMP statistics for the local computer.
558  *
559  * PARAMS
560  *  stats [Out] buffer for ICMP statistics
561  *  family [In] specifies whether IPv4 or IPv6 statistics are returned
562  *
563  * RETURNS
564  *  Success: NO_ERROR
565  *  Failure: error code from winerror.h
566  */
567 DWORD WINAPI GetIcmpStatisticsEx(PMIB_ICMP_EX stats, DWORD family)
568 {
569     DWORD ret = ERROR_NOT_SUPPORTED;
570     MIB_ICMP ipv4stats;
571
572     if (!stats) return ERROR_INVALID_PARAMETER;
573     if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
574     memset( stats, 0, sizeof(MIB_ICMP_EX) );
575
576     if (family == WS_AF_INET6)
577     {
578 #ifdef __linux__
579         {
580             FILE *fp;
581
582             if ((fp = fopen("/proc/net/snmp6", "r")))
583             {
584                 struct icmpstatstruct{
585                     const char *name;
586                     DWORD pos;
587                 };
588                 static const struct icmpstatstruct icmpinstatlist[] = {
589                     { "Icmp6InDestUnreachs",           ICMP6_DST_UNREACH },
590                     { "Icmp6InPktTooBigs",             ICMP6_PACKET_TOO_BIG },
591                     { "Icmp6InTimeExcds",              ICMP6_TIME_EXCEEDED },
592                     { "Icmp6InParmProblems",           ICMP6_PARAM_PROB },
593                     { "Icmp6InEchos",                  ICMP6_ECHO_REQUEST },
594                     { "Icmp6InEchoReplies",            ICMP6_ECHO_REPLY },
595                     { "Icmp6InGroupMembQueries",       ICMP6_MEMBERSHIP_QUERY },
596                     { "Icmp6InGroupMembResponses",     ICMP6_MEMBERSHIP_REPORT },
597                     { "Icmp6InGroupMembReductions",    ICMP6_MEMBERSHIP_REDUCTION },
598                     { "Icmp6InRouterSolicits",         ND_ROUTER_SOLICIT },
599                     { "Icmp6InRouterAdvertisements",   ND_ROUTER_ADVERT },
600                     { "Icmp6InNeighborSolicits",       ND_NEIGHBOR_SOLICIT },
601                     { "Icmp6InNeighborAdvertisements", ND_NEIGHBOR_ADVERT },
602                     { "Icmp6InRedirects",              ND_REDIRECT },
603                     { "Icmp6InMLDv2Reports",           ICMP6_V2_MEMBERSHIP_REPORT },
604                 };
605                 static const struct icmpstatstruct icmpoutstatlist[] = {
606                     { "Icmp6OutDestUnreachs",           ICMP6_DST_UNREACH },
607                     { "Icmp6OutPktTooBigs",             ICMP6_PACKET_TOO_BIG },
608                     { "Icmp6OutTimeExcds",              ICMP6_TIME_EXCEEDED },
609                     { "Icmp6OutParmProblems",           ICMP6_PARAM_PROB },
610                     { "Icmp6OutEchos",                  ICMP6_ECHO_REQUEST },
611                     { "Icmp6OutEchoReplies",            ICMP6_ECHO_REPLY },
612                     { "Icmp6OutGroupMembQueries",       ICMP6_MEMBERSHIP_QUERY },
613                     { "Icmp6OutGroupMembResponses",     ICMP6_MEMBERSHIP_REPORT },
614                     { "Icmp6OutGroupMembReductions",    ICMP6_MEMBERSHIP_REDUCTION },
615                     { "Icmp6OutRouterSolicits",         ND_ROUTER_SOLICIT },
616                     { "Icmp6OutRouterAdvertisements",   ND_ROUTER_ADVERT },
617                     { "Icmp6OutNeighborSolicits",       ND_NEIGHBOR_SOLICIT },
618                     { "Icmp6OutNeighborAdvertisements", ND_NEIGHBOR_ADVERT },
619                     { "Icmp6OutRedirects",              ND_REDIRECT },
620                     { "Icmp6OutMLDv2Reports",           ICMP6_V2_MEMBERSHIP_REPORT },
621                 };
622                 char buf[512], *ptr, *value;
623                 DWORD res, i;
624
625                 while ((ptr = fgets(buf, sizeof(buf), fp)))
626                 {
627                     if (!(value = strchr(buf, ' ')))
628                         continue;
629
630                     /* terminate the valuename */
631                     ptr = value - 1;
632                     *(ptr + 1) = '\0';
633
634                     /* and strip leading spaces from value */
635                     value += 1;
636                     while (*value==' ') value++;
637                     if ((ptr = strchr(value, '\n')))
638                         *ptr='\0';
639
640                     if (!strcasecmp(buf, "Icmp6InMsgs"))
641                     {
642                         if (sscanf(value, "%d", &res)) stats->icmpInStats.dwMsgs = res;
643                         continue;
644                     }
645
646                     if (!strcasecmp(buf, "Icmp6InErrors"))
647                     {
648                         if (sscanf(value, "%d", &res)) stats->icmpInStats.dwErrors = res;
649                         continue;
650                     }
651
652                     for (i = 0; i < sizeof(icmpinstatlist)/sizeof(icmpinstatlist[0]); i++)
653                     {
654                         if (!strcasecmp(buf, icmpinstatlist[i].name))
655                         {
656                             if (sscanf(value, "%d", &res))
657                                 stats->icmpInStats.rgdwTypeCount[icmpinstatlist[i].pos] = res;
658                             break;
659                         }
660                     }
661
662                     if (!strcasecmp(buf, "Icmp6OutMsgs"))
663                     {
664                         if (sscanf(value, "%d", &res)) stats->icmpOutStats.dwMsgs = res;
665                         continue;
666                     }
667
668                     if (!strcasecmp(buf, "Icmp6OutErrors"))
669                     {
670                         if (sscanf(value, "%d", &res)) stats->icmpOutStats.dwErrors = res;
671                         continue;
672                     }
673
674                     for (i = 0; i < sizeof(icmpoutstatlist)/sizeof(icmpoutstatlist[0]); i++)
675                     {
676                         if (!strcasecmp(buf, icmpoutstatlist[i].name))
677                         {
678                             if (sscanf(value, "%d", &res))
679                                 stats->icmpOutStats.rgdwTypeCount[icmpoutstatlist[i].pos] = res;
680                             break;
681                         }
682                     }
683
684                 }
685                 fclose(fp);
686                 ret = NO_ERROR;
687             }
688         }
689 #else
690         FIXME( "unimplemented for IPv6\n" );
691 #endif
692         return ret;
693     }
694
695     ret = GetIcmpStatistics(&ipv4stats);
696     if SUCCEEDED(ret)
697     {
698         stats->icmpInStats.dwMsgs = ipv4stats.stats.icmpInStats.dwMsgs;
699         stats->icmpInStats.dwErrors = ipv4stats.stats.icmpInStats.dwErrors;
700         stats->icmpInStats.rgdwTypeCount[ICMP4_DST_UNREACH] = ipv4stats.stats.icmpInStats.dwDestUnreachs;
701         stats->icmpInStats.rgdwTypeCount[ICMP4_SOURCE_QUENCH] = ipv4stats.stats.icmpInStats.dwSrcQuenchs;
702         stats->icmpInStats.rgdwTypeCount[ICMP4_REDIRECT] = ipv4stats.stats.icmpInStats.dwRedirects;
703         stats->icmpInStats.rgdwTypeCount[ICMP4_ECHO_REQUEST] = ipv4stats.stats.icmpInStats.dwEchos;
704         stats->icmpInStats.rgdwTypeCount[ICMP4_TIME_EXCEEDED] = ipv4stats.stats.icmpInStats.dwTimeExcds;
705         stats->icmpInStats.rgdwTypeCount[ICMP4_PARAM_PROB] = ipv4stats.stats.icmpInStats.dwParmProbs;
706         stats->icmpInStats.rgdwTypeCount[ICMP4_TIMESTAMP_REQUEST] = ipv4stats.stats.icmpInStats.dwTimestamps;
707         stats->icmpInStats.rgdwTypeCount[ICMP4_TIMESTAMP_REPLY] = ipv4stats.stats.icmpInStats.dwTimestampReps;
708         stats->icmpInStats.rgdwTypeCount[ICMP4_MASK_REQUEST] = ipv4stats.stats.icmpInStats.dwAddrMasks;
709         stats->icmpInStats.rgdwTypeCount[ICMP4_MASK_REPLY] = ipv4stats.stats.icmpInStats.dwAddrMaskReps;
710
711         stats->icmpOutStats.dwMsgs = ipv4stats.stats.icmpOutStats.dwMsgs;
712         stats->icmpOutStats.dwErrors = ipv4stats.stats.icmpOutStats.dwErrors;
713         stats->icmpOutStats.rgdwTypeCount[ICMP4_DST_UNREACH] = ipv4stats.stats.icmpOutStats.dwDestUnreachs;
714         stats->icmpOutStats.rgdwTypeCount[ICMP4_SOURCE_QUENCH] = ipv4stats.stats.icmpOutStats.dwSrcQuenchs;
715         stats->icmpOutStats.rgdwTypeCount[ICMP4_REDIRECT] = ipv4stats.stats.icmpOutStats.dwRedirects;
716         stats->icmpOutStats.rgdwTypeCount[ICMP4_ECHO_REQUEST] = ipv4stats.stats.icmpOutStats.dwEchos;
717         stats->icmpOutStats.rgdwTypeCount[ICMP4_TIME_EXCEEDED] = ipv4stats.stats.icmpOutStats.dwTimeExcds;
718         stats->icmpOutStats.rgdwTypeCount[ICMP4_PARAM_PROB] = ipv4stats.stats.icmpOutStats.dwParmProbs;
719         stats->icmpOutStats.rgdwTypeCount[ICMP4_TIMESTAMP_REQUEST] = ipv4stats.stats.icmpOutStats.dwTimestamps;
720         stats->icmpOutStats.rgdwTypeCount[ICMP4_TIMESTAMP_REPLY] = ipv4stats.stats.icmpOutStats.dwTimestampReps;
721         stats->icmpOutStats.rgdwTypeCount[ICMP4_MASK_REQUEST] = ipv4stats.stats.icmpOutStats.dwAddrMasks;
722         stats->icmpOutStats.rgdwTypeCount[ICMP4_MASK_REPLY] = ipv4stats.stats.icmpOutStats.dwAddrMaskReps;
723     }
724     return ret;
725 }
726
727 /******************************************************************
728  *    GetIpStatisticsEx (IPHLPAPI.@)
729  *
730  * Get the IPv4 and IPv6 statistics for the local computer.
731  *
732  * PARAMS
733  *  stats [Out] buffer for IP statistics
734  *  family [In] specifies whether IPv4 or IPv6 statistics are returned
735  *
736  * RETURNS
737  *  Success: NO_ERROR
738  *  Failure: error code from winerror.h
739  */
740 DWORD WINAPI GetIpStatisticsEx(PMIB_IPSTATS stats, DWORD family)
741 {
742     DWORD ret = ERROR_NOT_SUPPORTED;
743     MIB_IPFORWARDTABLE *fwd_table;
744
745     if (!stats) return ERROR_INVALID_PARAMETER;
746     if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
747     memset( stats, 0, sizeof(*stats) );
748
749     stats->dwNumIf = stats->dwNumAddr = getNumInterfaces();
750     if (!AllocateAndGetIpForwardTableFromStack( &fwd_table, FALSE, GetProcessHeap(), 0 ))
751     {
752         stats->dwNumRoutes = fwd_table->dwNumEntries;
753         HeapFree( GetProcessHeap(), 0, fwd_table );
754     }
755
756     if (family == WS_AF_INET6)
757     {
758 #ifdef __linux__
759         {
760             FILE *fp;
761
762             if ((fp = fopen("/proc/net/snmp6", "r")))
763             {
764                 struct {
765                     const char *name;
766                     DWORD *elem;
767                 } ipstatlist[] = {
768                     { "Ip6InReceives",       &stats->dwInReceives },
769                     { "Ip6InHdrErrors",      &stats->dwInHdrErrors },
770                     { "Ip6InAddrErrors",     &stats->dwInAddrErrors },
771                     { "Ip6OutForwDatagrams", &stats->dwForwDatagrams },
772                     { "Ip6InUnknownProtos",  &stats->dwInUnknownProtos },
773                     { "Ip6InDiscards",       &stats->dwInDiscards },
774                     { "Ip6InDelivers",       &stats->dwInDelivers },
775                     { "Ip6OutRequests",      &stats->dwOutRequests },
776                     { "Ip6OutDiscards",      &stats->dwOutDiscards },
777                     { "Ip6OutNoRoutes",      &stats->dwOutNoRoutes },
778                     { "Ip6ReasmTimeout",     &stats->dwReasmTimeout },
779                     { "Ip6ReasmReqds",       &stats->dwReasmReqds },
780                     { "Ip6ReasmOKs",         &stats->dwReasmOks },
781                     { "Ip6ReasmFails",       &stats->dwReasmFails },
782                     { "Ip6FragOKs",          &stats->dwFragOks },
783                     { "Ip6FragFails",        &stats->dwFragFails },
784                     { "Ip6FragCreates",      &stats->dwFragCreates },
785                     /* hmm, no routingDiscards, defaultTTL and forwarding? */
786                 };
787                 char buf[512], *ptr, *value;
788                 DWORD res, i;
789
790                 while ((ptr = fgets(buf, sizeof(buf), fp)))
791                 {
792                     if (!(value = strchr(buf, ' ')))
793                         continue;
794
795                     /* terminate the valuename */
796                     ptr = value - 1;
797                     *(ptr + 1) = '\0';
798
799                     /* and strip leading spaces from value */
800                     value += 1;
801                     while (*value==' ') value++;
802                     if ((ptr = strchr(value, '\n')))
803                         *ptr='\0';
804
805                     for (i = 0; i < sizeof(ipstatlist)/sizeof(ipstatlist[0]); i++)
806                         if (!strcasecmp(buf, ipstatlist[i].name))
807                         {
808                             if (sscanf(value, "%d", &res)) *ipstatlist[i].elem = res;
809                             continue;
810                         }
811                 }
812                 fclose(fp);
813                 ret = NO_ERROR;
814             }
815         }
816 #else
817         FIXME( "unimplemented for IPv6\n" );
818 #endif
819         return ret;
820     }
821
822 #ifdef __linux__
823     {
824         FILE *fp;
825
826         if ((fp = fopen("/proc/net/snmp", "r")))
827         {
828             static const char hdr[] = "Ip:";
829             char buf[512], *ptr;
830
831             while ((ptr = fgets(buf, sizeof(buf), fp)))
832             {
833                 if (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue;
834                 /* last line was a header, get another */
835                 if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
836                 if (!strncasecmp(buf, hdr, sizeof(hdr) - 1))
837                 {
838                     ptr += sizeof(hdr);
839                     sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
840                             &stats->u.dwForwarding,
841                             &stats->dwDefaultTTL,
842                             &stats->dwInReceives,
843                             &stats->dwInHdrErrors,
844                             &stats->dwInAddrErrors,
845                             &stats->dwForwDatagrams,
846                             &stats->dwInUnknownProtos,
847                             &stats->dwInDiscards,
848                             &stats->dwInDelivers,
849                             &stats->dwOutRequests,
850                             &stats->dwOutDiscards,
851                             &stats->dwOutNoRoutes,
852                             &stats->dwReasmTimeout,
853                             &stats->dwReasmReqds,
854                             &stats->dwReasmOks,
855                             &stats->dwReasmFails,
856                             &stats->dwFragOks,
857                             &stats->dwFragFails,
858                             &stats->dwFragCreates );
859                     /* hmm, no routingDiscards */
860                     break;
861                 }
862             }
863             fclose(fp);
864             ret = NO_ERROR;
865         }
866     }
867 #elif defined(HAVE_LIBKSTAT)
868     {
869         static char ip[] = "ip";
870         kstat_ctl_t *kc;
871         kstat_t *ksp;
872
873         if ((kc = kstat_open()) &&
874             (ksp = kstat_lookup( kc, ip, 0, ip )) &&
875             kstat_read( kc, ksp, NULL ) != -1 &&
876             ksp->ks_type == KSTAT_TYPE_NAMED)
877         {
878             stats->u.dwForwarding    = kstat_get_ui32( ksp, "forwarding" );
879             stats->dwDefaultTTL      = kstat_get_ui32( ksp, "defaultTTL" );
880             stats->dwInReceives      = kstat_get_ui32( ksp, "inReceives" );
881             stats->dwInHdrErrors     = kstat_get_ui32( ksp, "inHdrErrors" );
882             stats->dwInAddrErrors    = kstat_get_ui32( ksp, "inAddrErrors" );
883             stats->dwForwDatagrams   = kstat_get_ui32( ksp, "forwDatagrams" );
884             stats->dwInUnknownProtos = kstat_get_ui32( ksp, "inUnknownProtos" );
885             stats->dwInDiscards      = kstat_get_ui32( ksp, "inDiscards" );
886             stats->dwInDelivers      = kstat_get_ui32( ksp, "inDelivers" );
887             stats->dwOutRequests     = kstat_get_ui32( ksp, "outRequests" );
888             stats->dwRoutingDiscards = kstat_get_ui32( ksp, "routingDiscards" );
889             stats->dwOutDiscards     = kstat_get_ui32( ksp, "outDiscards" );
890             stats->dwOutNoRoutes     = kstat_get_ui32( ksp, "outNoRoutes" );
891             stats->dwReasmTimeout    = kstat_get_ui32( ksp, "reasmTimeout" );
892             stats->dwReasmReqds      = kstat_get_ui32( ksp, "reasmReqds" );
893             stats->dwReasmOks        = kstat_get_ui32( ksp, "reasmOKs" );
894             stats->dwReasmFails      = kstat_get_ui32( ksp, "reasmFails" );
895             stats->dwFragOks         = kstat_get_ui32( ksp, "fragOKs" );
896             stats->dwFragFails       = kstat_get_ui32( ksp, "fragFails" );
897             stats->dwFragCreates     = kstat_get_ui32( ksp, "fragCreates" );
898             ret = NO_ERROR;
899         }
900         if (kc) kstat_close( kc );
901     }
902 #elif defined(HAVE_SYS_SYSCTL_H) && defined(IPCTL_STATS) && (defined(HAVE_STRUCT_IPSTAT_IPS_TOTAL) || defined(HAVE_STRUCT_IP_STATS_IPS_TOTAL))
903     {
904         int mib[] = {CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS};
905 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
906         int ip_ttl, ip_forwarding;
907 #if defined(HAVE_STRUCT_IPSTAT_IPS_TOTAL)
908         struct ipstat ip_stat;
909 #elif defined(HAVE_STRUCT_IP_STATS_IPS_TOTAL)
910         struct ip_stats ip_stat;
911 #endif
912         size_t needed;
913
914         needed = sizeof(ip_stat);
915         if(sysctl(mib, MIB_LEN, &ip_stat, &needed, NULL, 0) == -1)
916         {
917             ERR ("failed to get ipstat\n");
918             return ERROR_NOT_SUPPORTED;
919         }
920
921         needed = sizeof(ip_ttl);
922         if (sysctlbyname ("net.inet.ip.ttl", &ip_ttl, &needed, NULL, 0) == -1)
923         {
924             ERR ("failed to get ip Default TTL\n");
925             return ERROR_NOT_SUPPORTED;
926         }
927
928         needed = sizeof(ip_forwarding);
929         if (sysctlbyname ("net.inet.ip.forwarding", &ip_forwarding, &needed, NULL, 0) == -1)
930         {
931             ERR ("failed to get ip forwarding\n");
932             return ERROR_NOT_SUPPORTED;
933         }
934
935         stats->u.dwForwarding = ip_forwarding;
936         stats->dwDefaultTTL = ip_ttl;
937         stats->dwInDelivers = ip_stat.ips_delivered;
938         stats->dwInHdrErrors = ip_stat.ips_badhlen + ip_stat.ips_badsum + ip_stat.ips_tooshort + ip_stat.ips_badlen;
939         stats->dwInAddrErrors = ip_stat.ips_cantforward;
940         stats->dwInReceives = ip_stat.ips_total;
941         stats->dwForwDatagrams = ip_stat.ips_forward;
942         stats->dwInUnknownProtos = ip_stat.ips_noproto;
943         stats->dwInDiscards = ip_stat.ips_fragdropped;
944         stats->dwOutDiscards = ip_stat.ips_odropped;
945         stats->dwReasmOks = ip_stat.ips_reassembled;
946         stats->dwFragOks = ip_stat.ips_fragmented;
947         stats->dwFragFails = ip_stat.ips_cantfrag;
948         stats->dwReasmTimeout = ip_stat.ips_fragtimeout;
949         stats->dwOutNoRoutes = ip_stat.ips_noroute;
950         stats->dwOutRequests = ip_stat.ips_localout;
951         stats->dwReasmReqds = ip_stat.ips_fragments;
952         ret = NO_ERROR;
953     }
954 #else
955     FIXME( "unimplemented for IPv4\n" );
956 #endif
957     return ret;
958 }
959
960 /******************************************************************
961  *    GetIpStatistics (IPHLPAPI.@)
962  *
963  * Get the IP statistics for the local computer.
964  *
965  * PARAMS
966  *  stats [Out] buffer for IP statistics
967  *
968  * RETURNS
969  *  Success: NO_ERROR
970  *  Failure: error code from winerror.h
971  */
972 DWORD WINAPI GetIpStatistics(PMIB_IPSTATS stats)
973 {
974     return GetIpStatisticsEx(stats, WS_AF_INET);
975 }
976
977 /******************************************************************
978  *    GetTcpStatisticsEx (IPHLPAPI.@)
979  *
980  * Get the IPv4 and IPv6 TCP statistics for the local computer.
981  *
982  * PARAMS
983  *  stats [Out] buffer for TCP statistics
984  *  family [In] specifies whether IPv4 or IPv6 statistics are returned
985  *
986  * RETURNS
987  *  Success: NO_ERROR
988  *  Failure: error code from winerror.h
989  */
990 DWORD WINAPI GetTcpStatisticsEx(PMIB_TCPSTATS stats, DWORD family)
991 {
992     DWORD ret = ERROR_NOT_SUPPORTED;
993
994     if (!stats) return ERROR_INVALID_PARAMETER;
995     if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
996     memset( stats, 0, sizeof(*stats) );
997
998     if (family == WS_AF_INET6)
999     {
1000         FIXME( "unimplemented for IPv6\n" );
1001         return ret;
1002     }
1003
1004 #ifdef __linux__
1005     {
1006         FILE *fp;
1007
1008         if ((fp = fopen("/proc/net/snmp", "r")))
1009         {
1010             static const char hdr[] = "Tcp:";
1011             MIB_TCPTABLE *tcp_table;
1012             char buf[512], *ptr;
1013
1014             while ((ptr = fgets(buf, sizeof(buf), fp)))
1015             {
1016                 if (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue;
1017                 /* last line was a header, get another */
1018                 if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
1019                 if (!strncasecmp(buf, hdr, sizeof(hdr) - 1))
1020                 {
1021                     ptr += sizeof(hdr);
1022                     sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u",
1023                             &stats->u.dwRtoAlgorithm,
1024                             &stats->dwRtoMin,
1025                             &stats->dwRtoMax,
1026                             &stats->dwMaxConn,
1027                             &stats->dwActiveOpens,
1028                             &stats->dwPassiveOpens,
1029                             &stats->dwAttemptFails,
1030                             &stats->dwEstabResets,
1031                             &stats->dwCurrEstab,
1032                             &stats->dwInSegs,
1033                             &stats->dwOutSegs,
1034                             &stats->dwRetransSegs,
1035                             &stats->dwInErrs,
1036                             &stats->dwOutRsts );
1037                     break;
1038                 }
1039             }
1040             if (!AllocateAndGetTcpTableFromStack( &tcp_table, FALSE, GetProcessHeap(), 0 ))
1041             {
1042                 stats->dwNumConns = tcp_table->dwNumEntries;
1043                 HeapFree( GetProcessHeap(), 0, tcp_table );
1044             }
1045             fclose(fp);
1046             ret = NO_ERROR;
1047         }
1048     }
1049 #elif defined(HAVE_LIBKSTAT)
1050     {
1051         static char tcp[] = "tcp";
1052         kstat_ctl_t *kc;
1053         kstat_t *ksp;
1054
1055         if ((kc = kstat_open()) &&
1056             (ksp = kstat_lookup( kc, tcp, 0, tcp )) &&
1057             kstat_read( kc, ksp, NULL ) != -1 &&
1058             ksp->ks_type == KSTAT_TYPE_NAMED)
1059         {
1060             stats->u.dwRtoAlgorithm = kstat_get_ui32( ksp, "rtoAlgorithm" );
1061             stats->dwRtoMin       = kstat_get_ui32( ksp, "rtoMin" );
1062             stats->dwRtoMax       = kstat_get_ui32( ksp, "rtoMax" );
1063             stats->dwMaxConn      = kstat_get_ui32( ksp, "maxConn" );
1064             stats->dwActiveOpens  = kstat_get_ui32( ksp, "activeOpens" );
1065             stats->dwPassiveOpens = kstat_get_ui32( ksp, "passiveOpens" );
1066             stats->dwAttemptFails = kstat_get_ui32( ksp, "attemptFails" );
1067             stats->dwEstabResets  = kstat_get_ui32( ksp, "estabResets" );
1068             stats->dwCurrEstab    = kstat_get_ui32( ksp, "currEstab" );
1069             stats->dwInSegs       = kstat_get_ui32( ksp, "inSegs" );
1070             stats->dwOutSegs      = kstat_get_ui32( ksp, "outSegs" );
1071             stats->dwRetransSegs  = kstat_get_ui32( ksp, "retransSegs" );
1072             stats->dwInErrs       = kstat_get_ui32( ksp, "inErrs" );
1073             stats->dwOutRsts      = kstat_get_ui32( ksp, "outRsts" );
1074             stats->dwNumConns     = kstat_get_ui32( ksp, "connTableSize" );
1075             ret = NO_ERROR;
1076         }
1077         if (kc) kstat_close( kc );
1078     }
1079 #elif defined(HAVE_SYS_SYSCTL_H) && defined(TCPCTL_STATS) && (defined(HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT) || defined(HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT))
1080     {
1081 #ifndef TCPTV_MIN  /* got removed in Mac OS X for some reason */
1082 #define TCPTV_MIN 2
1083 #define TCPTV_REXMTMAX 128
1084 #endif
1085         int mib[] = {CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS};
1086 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
1087 #define hz 1000
1088 #if defined(HAVE_STRUCT_TCPSTAT_TCPS_CONNATTEMPT)
1089         struct tcpstat tcp_stat;
1090 #elif defined(HAVE_STRUCT_TCP_STATS_TCPS_CONNATTEMPT)
1091         struct tcp_stats tcp_stat;
1092 #endif
1093         size_t needed = sizeof(tcp_stat);
1094
1095         if(sysctl(mib, MIB_LEN, &tcp_stat, &needed, NULL, 0) != -1)
1096         {
1097             stats->u.RtoAlgorithm = MIB_TCP_RTO_VANJ;
1098             stats->dwRtoMin = TCPTV_MIN;
1099             stats->dwRtoMax = TCPTV_REXMTMAX;
1100             stats->dwMaxConn = -1;
1101             stats->dwActiveOpens = tcp_stat.tcps_connattempt;
1102             stats->dwPassiveOpens = tcp_stat.tcps_accepts;
1103             stats->dwAttemptFails = tcp_stat.tcps_conndrops;
1104             stats->dwEstabResets = tcp_stat.tcps_drops;
1105             stats->dwCurrEstab = 0;
1106             stats->dwInSegs = tcp_stat.tcps_rcvtotal;
1107             stats->dwOutSegs = tcp_stat.tcps_sndtotal - tcp_stat.tcps_sndrexmitpack;
1108             stats->dwRetransSegs = tcp_stat.tcps_sndrexmitpack;
1109             stats->dwInErrs = tcp_stat.tcps_rcvbadsum + tcp_stat.tcps_rcvbadoff + tcp_stat.tcps_rcvmemdrop + tcp_stat.tcps_rcvshort;
1110             stats->dwOutRsts = tcp_stat.tcps_sndctrl - tcp_stat.tcps_closed;
1111             stats->dwNumConns = tcp_stat.tcps_connects;
1112             ret = NO_ERROR;
1113         }
1114         else ERR ("failed to get tcpstat\n");
1115     }
1116 #else
1117     FIXME( "unimplemented\n" );
1118 #endif
1119     return ret;
1120 }
1121
1122 /******************************************************************
1123  *    GetTcpStatistics (IPHLPAPI.@)
1124  *
1125  * Get the TCP statistics for the local computer.
1126  *
1127  * PARAMS
1128  *  stats [Out] buffer for TCP statistics
1129  *
1130  * RETURNS
1131  *  Success: NO_ERROR
1132  *  Failure: error code from winerror.h
1133  */
1134 DWORD WINAPI GetTcpStatistics(PMIB_TCPSTATS stats)
1135 {
1136     return GetTcpStatisticsEx(stats, WS_AF_INET);
1137 }
1138
1139 /******************************************************************
1140  *    GetUdpStatistics (IPHLPAPI.@)
1141  *
1142  * Get the IPv4 and IPv6 UDP statistics for the local computer.
1143  *
1144  * PARAMS
1145  *  stats [Out] buffer for UDP statistics
1146  *  family [In] specifies whether IPv4 or IPv6 statistics are returned
1147  *
1148  * RETURNS
1149  *  Success: NO_ERROR
1150  *  Failure: error code from winerror.h
1151  */
1152 DWORD WINAPI GetUdpStatisticsEx(PMIB_UDPSTATS stats, DWORD family)
1153 {
1154     DWORD ret = ERROR_NOT_SUPPORTED;
1155
1156     if (!stats) return ERROR_INVALID_PARAMETER;
1157     if (family != WS_AF_INET && family != WS_AF_INET6) return ERROR_INVALID_PARAMETER;
1158     memset( stats, 0, sizeof(*stats) );
1159
1160     stats->dwNumAddrs = getNumInterfaces();
1161
1162     if (family == WS_AF_INET6)
1163     {
1164 #ifdef __linux__
1165         {
1166             FILE *fp;
1167
1168             if ((fp = fopen("/proc/net/snmp6", "r")))
1169             {
1170                 struct {
1171                     const char *name;
1172                     DWORD *elem;
1173                 } udpstatlist[] = {
1174                     { "Udp6InDatagrams",  &stats->dwInDatagrams },
1175                     { "Udp6NoPorts",      &stats->dwNoPorts },
1176                     { "Udp6InErrors",     &stats->dwInErrors },
1177                     { "Udp6OutDatagrams", &stats->dwOutDatagrams },
1178                 };
1179                 char buf[512], *ptr, *value;
1180                 DWORD res, i;
1181
1182                 while ((ptr = fgets(buf, sizeof(buf), fp)))
1183                 {
1184                     if (!(value = strchr(buf, ' ')))
1185                         continue;
1186
1187                     /* terminate the valuename */
1188                     ptr = value - 1;
1189                     *(ptr + 1) = '\0';
1190
1191                     /* and strip leading spaces from value */
1192                     value += 1;
1193                     while (*value==' ') value++;
1194                     if ((ptr = strchr(value, '\n')))
1195                         *ptr='\0';
1196
1197                     for (i = 0; i < sizeof(udpstatlist)/sizeof(udpstatlist[0]); i++)
1198                         if (!strcasecmp(buf, udpstatlist[i].name))
1199                         {
1200                             if (sscanf(value, "%d", &res)) *udpstatlist[i].elem = res;
1201                             continue;
1202                         }
1203                 }
1204                 fclose(fp);
1205                 ret = NO_ERROR;
1206             }
1207         }
1208 #else
1209         FIXME( "unimplemented for IPv6\n" );
1210 #endif
1211         return ret;
1212     }
1213
1214 #ifdef __linux__
1215     {
1216         FILE *fp;
1217
1218         if ((fp = fopen("/proc/net/snmp", "r")))
1219         {
1220             static const char hdr[] = "Udp:";
1221             char buf[512], *ptr;
1222
1223             while ((ptr = fgets(buf, sizeof(buf), fp)))
1224             {
1225                 if (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue;
1226                 /* last line was a header, get another */
1227                 if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
1228                 if (!strncasecmp(buf, hdr, sizeof(hdr) - 1))
1229                 {
1230                     ptr += sizeof(hdr);
1231                     sscanf( ptr, "%u %u %u %u %u",
1232                             &stats->dwInDatagrams, &stats->dwNoPorts,
1233                             &stats->dwInErrors, &stats->dwOutDatagrams, &stats->dwNumAddrs );
1234                     break;
1235                 }
1236             }
1237             fclose(fp);
1238             ret = NO_ERROR;
1239         }
1240     }
1241 #elif defined(HAVE_LIBKSTAT)
1242     {
1243         static char udp[] = "udp";
1244         kstat_ctl_t *kc;
1245         kstat_t *ksp;
1246         MIB_UDPTABLE *udp_table;
1247
1248         if ((kc = kstat_open()) &&
1249             (ksp = kstat_lookup( kc, udp, 0, udp )) &&
1250             kstat_read( kc, ksp, NULL ) != -1 &&
1251             ksp->ks_type == KSTAT_TYPE_NAMED)
1252         {
1253             stats->dwInDatagrams  = kstat_get_ui32( ksp, "inDatagrams" );
1254             stats->dwNoPorts      = 0;  /* FIXME */
1255             stats->dwInErrors     = kstat_get_ui32( ksp, "inErrors" );
1256             stats->dwOutDatagrams = kstat_get_ui32( ksp, "outDatagrams" );
1257             if (!AllocateAndGetUdpTableFromStack( &udp_table, FALSE, GetProcessHeap(), 0 ))
1258             {
1259                 stats->dwNumAddrs = udp_table->dwNumEntries;
1260                 HeapFree( GetProcessHeap(), 0, udp_table );
1261             }
1262             ret = NO_ERROR;
1263         }
1264         if (kc) kstat_close( kc );
1265     }
1266 #elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS) && defined(HAVE_STRUCT_UDPSTAT_UDPS_IPACKETS)
1267     {
1268         int mib[] = {CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_STATS};
1269 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
1270         struct udpstat udp_stat;
1271         MIB_UDPTABLE *udp_table;
1272         size_t needed = sizeof(udp_stat);
1273
1274         if(sysctl(mib, MIB_LEN, &udp_stat, &needed, NULL, 0) != -1)
1275         {
1276             stats->dwInDatagrams = udp_stat.udps_ipackets;
1277             stats->dwOutDatagrams = udp_stat.udps_opackets;
1278             stats->dwNoPorts = udp_stat.udps_noport;
1279             stats->dwInErrors = udp_stat.udps_hdrops + udp_stat.udps_badsum + udp_stat.udps_fullsock + udp_stat.udps_badlen;
1280             if (!AllocateAndGetUdpTableFromStack( &udp_table, FALSE, GetProcessHeap(), 0 ))
1281             {
1282                 stats->dwNumAddrs = udp_table->dwNumEntries;
1283                 HeapFree( GetProcessHeap(), 0, udp_table );
1284             }
1285             ret = NO_ERROR;
1286         }
1287         else ERR ("failed to get udpstat\n");
1288     }
1289 #else
1290     FIXME( "unimplemented for IPv4\n" );
1291 #endif
1292     return ret;
1293 }
1294
1295 /******************************************************************
1296  *    GetUdpStatistics (IPHLPAPI.@)
1297  *
1298  * Get the UDP statistics for the local computer.
1299  *
1300  * PARAMS
1301  *  stats [Out] buffer for UDP statistics
1302  *
1303  * RETURNS
1304  *  Success: NO_ERROR
1305  *  Failure: error code from winerror.h
1306  */
1307 DWORD WINAPI GetUdpStatistics(PMIB_UDPSTATS stats)
1308 {
1309     return GetUdpStatisticsEx(stats, WS_AF_INET);
1310 }
1311
1312 static MIB_IPFORWARDTABLE *append_ipforward_row( HANDLE heap, DWORD flags, MIB_IPFORWARDTABLE *table,
1313                                                  DWORD *count, const MIB_IPFORWARDROW *row )
1314 {
1315     if (table->dwNumEntries >= *count)
1316     {
1317         MIB_IPFORWARDTABLE *new_table;
1318         DWORD new_count = table->dwNumEntries * 2;
1319
1320         if (!(new_table = HeapReAlloc( heap, flags, table,
1321                                        FIELD_OFFSET(MIB_IPFORWARDTABLE, table[new_count] ))))
1322         {
1323             HeapFree( heap, 0, table );
1324             return NULL;
1325         }
1326         *count = new_count;
1327         table = new_table;
1328     }
1329     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1330     return table;
1331 }
1332
1333 static int compare_ipforward_rows(const void *a, const void *b)
1334 {
1335     const MIB_IPFORWARDROW *rowA = a;
1336     const MIB_IPFORWARDROW *rowB = b;
1337     int ret;
1338
1339     if ((ret = rowA->dwForwardDest - rowB->dwForwardDest) != 0) return ret;
1340     if ((ret = rowA->u2.dwForwardProto - rowB->u2.dwForwardProto) != 0) return ret;
1341     if ((ret = rowA->dwForwardPolicy - rowB->dwForwardPolicy) != 0) return ret;
1342     return rowA->dwForwardNextHop - rowB->dwForwardNextHop;
1343 }
1344
1345 /******************************************************************
1346  *    AllocateAndGetIpForwardTableFromStack (IPHLPAPI.@)
1347  *
1348  * Get the route table.
1349  * Like GetIpForwardTable(), but allocate the returned table from heap.
1350  *
1351  * PARAMS
1352  *  ppIpForwardTable [Out] pointer into which the MIB_IPFORWARDTABLE is
1353  *                         allocated and returned.
1354  *  bOrder           [In]  whether to sort the table
1355  *  heap             [In]  heap from which the table is allocated
1356  *  flags            [In]  flags to HeapAlloc
1357  *
1358  * RETURNS
1359  *  ERROR_INVALID_PARAMETER if ppIfTable is NULL, other error codes
1360  *  on failure, NO_ERROR on success.
1361  */
1362 DWORD WINAPI AllocateAndGetIpForwardTableFromStack(PMIB_IPFORWARDTABLE *ppIpForwardTable, BOOL bOrder,
1363                                                    HANDLE heap, DWORD flags)
1364 {
1365     MIB_IPFORWARDTABLE *table;
1366     MIB_IPFORWARDROW row;
1367     DWORD ret = NO_ERROR, count = 16;
1368
1369     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppIpForwardTable, bOrder, heap, flags);
1370
1371     if (!ppIpForwardTable) return ERROR_INVALID_PARAMETER;
1372
1373     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_IPFORWARDTABLE, table[count] ))))
1374         return ERROR_OUTOFMEMORY;
1375
1376     table->dwNumEntries = 0;
1377
1378 #ifdef __linux__
1379     {
1380         FILE *fp;
1381
1382         if ((fp = fopen("/proc/net/route", "r")))
1383         {
1384             char buf[512], *ptr;
1385             DWORD flags;
1386
1387             /* skip header line */
1388             ptr = fgets(buf, sizeof(buf), fp);
1389             while ((ptr = fgets(buf, sizeof(buf), fp)))
1390             {
1391                 memset( &row, 0, sizeof(row) );
1392
1393                 while (!isspace(*ptr)) ptr++;
1394                 *ptr++ = 0;
1395                 if (getInterfaceIndexByName(buf, &row.dwForwardIfIndex) != NO_ERROR)
1396                     continue;
1397
1398                 row.dwForwardDest = strtoul(ptr, &ptr, 16);
1399                 row.dwForwardNextHop = strtoul(ptr + 1, &ptr, 16);
1400                 flags = strtoul(ptr + 1, &ptr, 16);
1401
1402                 if (!(flags & RTF_UP)) row.u1.ForwardType = MIB_IPROUTE_TYPE_INVALID;
1403                 else if (flags & RTF_GATEWAY) row.u1.ForwardType = MIB_IPROUTE_TYPE_INDIRECT;
1404                 else row.u1.ForwardType = MIB_IPROUTE_TYPE_DIRECT;
1405
1406                 strtoul(ptr + 1, &ptr, 16); /* refcount, skip */
1407                 strtoul(ptr + 1, &ptr, 16); /* use, skip */
1408                 row.dwForwardMetric1 = strtoul(ptr + 1, &ptr, 16);
1409                 row.dwForwardMask = strtoul(ptr + 1, &ptr, 16);
1410                 /* FIXME: other protos might be appropriate, e.g. the default
1411                  * route is typically set with MIB_IPPROTO_NETMGMT instead */
1412                 row.u2.ForwardProto = MIB_IPPROTO_LOCAL;
1413
1414                 if (!(table = append_ipforward_row( heap, flags, table, &count, &row )))
1415                     break;
1416             }
1417             fclose(fp);
1418         }
1419         else ret = ERROR_NOT_SUPPORTED;
1420     }
1421 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1422     {
1423         void *data;
1424         int fd, len, namelen;
1425         mib2_ipRouteEntry_t *entry;
1426         char name[64];
1427
1428         if ((fd = open_streams_mib( NULL )) != -1)
1429         {
1430             if ((data = read_mib_entry( fd, MIB2_IP, MIB2_IP_ROUTE, &len )))
1431             {
1432                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
1433                 {
1434                     row.dwForwardDest      = entry->ipRouteDest;
1435                     row.dwForwardMask      = entry->ipRouteMask;
1436                     row.dwForwardPolicy    = 0;
1437                     row.dwForwardNextHop   = entry->ipRouteNextHop;
1438                     row.u1.dwForwardType   = entry->ipRouteType;
1439                     row.u2.dwForwardProto  = entry->ipRouteProto;
1440                     row.dwForwardAge       = entry->ipRouteAge;
1441                     row.dwForwardNextHopAS = 0;
1442                     row.dwForwardMetric1   = entry->ipRouteMetric1;
1443                     row.dwForwardMetric2   = entry->ipRouteMetric2;
1444                     row.dwForwardMetric3   = entry->ipRouteMetric3;
1445                     row.dwForwardMetric4   = entry->ipRouteMetric4;
1446                     row.dwForwardMetric5   = entry->ipRouteMetric5;
1447                     namelen = min( sizeof(name) - 1, entry->ipRouteIfIndex.o_length );
1448                     memcpy( name, entry->ipRouteIfIndex.o_bytes, namelen );
1449                     name[namelen] = 0;
1450                     getInterfaceIndexByName( name, &row.dwForwardIfIndex );
1451                     if (!(table = append_ipforward_row( heap, flags, table, &count, &row ))) break;
1452                 }
1453                 HeapFree( GetProcessHeap(), 0, data );
1454             }
1455             close( fd );
1456         }
1457         else ret = ERROR_NOT_SUPPORTED;
1458     }
1459 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1460     {
1461        int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
1462        size_t needed;
1463        char *buf = NULL, *lim, *next, *addrPtr;
1464        struct rt_msghdr *rtm;
1465
1466        if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
1467        {
1468           ERR ("sysctl 1 failed!\n");
1469           ret = ERROR_NOT_SUPPORTED;
1470           goto done;
1471        }
1472
1473        buf = HeapAlloc (GetProcessHeap (), 0, needed);
1474        if (!buf)
1475        {
1476           ret = ERROR_OUTOFMEMORY;
1477           goto done;
1478        }
1479
1480        if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
1481        {
1482           ret = ERROR_NOT_SUPPORTED;
1483           goto done;
1484        }
1485
1486        lim = buf + needed;
1487        for (next = buf; next < lim; next += rtm->rtm_msglen)
1488        {
1489           int i;
1490
1491           rtm = (struct rt_msghdr *)next;
1492
1493           if (rtm->rtm_type != RTM_GET)
1494           {
1495              WARN ("Got unexpected message type 0x%x!\n",
1496                    rtm->rtm_type);
1497              continue;
1498           }
1499
1500           /* Ignore gateway routes which are multicast */
1501           if ((rtm->rtm_flags & RTF_GATEWAY) && (rtm->rtm_flags & RTF_MULTICAST))
1502              continue;
1503
1504           memset( &row, 0, sizeof(row) );
1505           row.dwForwardIfIndex = rtm->rtm_index;
1506           row.u1.ForwardType = (rtm->rtm_flags & RTF_GATEWAY) ? MIB_IPROUTE_TYPE_INDIRECT : MIB_IPROUTE_TYPE_DIRECT;
1507           row.dwForwardMetric1 = rtm->rtm_rmx.rmx_hopcount;
1508           row.u2.ForwardProto = MIB_IPPROTO_LOCAL;
1509
1510           addrPtr = (char *)(rtm + 1);
1511
1512           for (i = 1; i; i <<= 1)
1513           {
1514              struct sockaddr *sa;
1515              DWORD addr;
1516
1517              if (!(i & rtm->rtm_addrs))
1518                 continue;
1519
1520              sa = (struct sockaddr *)addrPtr;
1521              ADVANCE (addrPtr, sa);
1522
1523              /* default routes are encoded by length-zero sockaddr */
1524              if (sa->sa_len == 0) {
1525                 addr = 0;
1526              }else {
1527                  switch(sa->sa_family) {
1528                  case AF_INET: {
1529                      struct sockaddr_in *sin = (struct sockaddr_in *)sa;
1530                      addr = sin->sin_addr.s_addr;
1531                      break;
1532                  }
1533 #ifdef AF_LINK
1534                  case AF_LINK:
1535                      if(i == RTA_GATEWAY && row.u1.ForwardType == MIB_IPROUTE_TYPE_DIRECT) {
1536                          /* For direct route we may simply use dest addr as next hop */
1537                          C_ASSERT(RTA_DST < RTA_GATEWAY);
1538                          addr = row.dwForwardDest;
1539                          break;
1540                      }
1541                      /* fallthrough */
1542 #endif
1543                  default:
1544                      WARN ("Received unsupported sockaddr family 0x%x\n", sa->sa_family);
1545                      addr = 0;
1546                  }
1547              }
1548
1549              switch (i)
1550              {
1551                 case RTA_DST:     row.dwForwardDest = addr; break;
1552                 case RTA_GATEWAY: row.dwForwardNextHop = addr; break;
1553                 case RTA_NETMASK: row.dwForwardMask = addr; break;
1554                 default:
1555                    WARN ("Unexpected address type 0x%x\n", i);
1556              }
1557           }
1558
1559           if (!(table = append_ipforward_row( heap, flags, table, &count, &row )))
1560               break;
1561        }
1562 done:
1563       HeapFree( GetProcessHeap (), 0, buf );
1564     }
1565 #else
1566     FIXME( "not implemented\n" );
1567     ret = ERROR_NOT_SUPPORTED;
1568 #endif
1569
1570     if (!table) return ERROR_OUTOFMEMORY;
1571     if (!ret)
1572     {
1573         if (bOrder && table->dwNumEntries)
1574             qsort( table->table, table->dwNumEntries, sizeof(row), compare_ipforward_rows );
1575         *ppIpForwardTable = table;
1576     }
1577     else HeapFree( heap, flags, table );
1578     TRACE( "returning ret %u table %p\n", ret, table );
1579     return ret;
1580 }
1581
1582 static MIB_IPNETTABLE *append_ipnet_row( HANDLE heap, DWORD flags, MIB_IPNETTABLE *table,
1583                                          DWORD *count, const MIB_IPNETROW *row )
1584 {
1585     if (table->dwNumEntries >= *count)
1586     {
1587         MIB_IPNETTABLE *new_table;
1588         DWORD new_count = table->dwNumEntries * 2;
1589
1590         if (!(new_table = HeapReAlloc( heap, flags, table,
1591                                        FIELD_OFFSET(MIB_IPNETTABLE, table[new_count] ))))
1592         {
1593             HeapFree( heap, 0, table );
1594             return NULL;
1595         }
1596         *count = new_count;
1597         table = new_table;
1598     }
1599     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1600     return table;
1601 }
1602
1603 static int compare_ipnet_rows(const void *a, const void *b)
1604 {
1605     const MIB_IPNETROW *rowA = a;
1606     const MIB_IPNETROW *rowB = b;
1607
1608     return ntohl(rowA->dwAddr) - ntohl(rowB->dwAddr);
1609 }
1610
1611
1612 /******************************************************************
1613  *    AllocateAndGetIpNetTableFromStack (IPHLPAPI.@)
1614  *
1615  * Get the IP-to-physical address mapping table.
1616  * Like GetIpNetTable(), but allocate the returned table from heap.
1617  *
1618  * PARAMS
1619  *  ppIpNetTable [Out] pointer into which the MIB_IPNETTABLE is
1620  *                     allocated and returned.
1621  *  bOrder       [In]  whether to sort the table
1622  *  heap         [In]  heap from which the table is allocated
1623  *  flags        [In]  flags to HeapAlloc
1624  *
1625  * RETURNS
1626  *  ERROR_INVALID_PARAMETER if ppIpNetTable is NULL, other error codes
1627  *  on failure, NO_ERROR on success.
1628  */
1629 DWORD WINAPI AllocateAndGetIpNetTableFromStack(PMIB_IPNETTABLE *ppIpNetTable, BOOL bOrder,
1630                                                HANDLE heap, DWORD flags)
1631 {
1632     MIB_IPNETTABLE *table;
1633     MIB_IPNETROW row;
1634     DWORD ret = NO_ERROR, count = 16;
1635
1636     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppIpNetTable, bOrder, heap, flags);
1637
1638     if (!ppIpNetTable) return ERROR_INVALID_PARAMETER;
1639
1640     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_IPNETTABLE, table[count] ))))
1641         return ERROR_OUTOFMEMORY;
1642
1643     table->dwNumEntries = 0;
1644
1645 #ifdef __linux__
1646     {
1647         FILE *fp;
1648
1649         if ((fp = fopen("/proc/net/arp", "r")))
1650         {
1651             char buf[512], *ptr;
1652             DWORD flags;
1653
1654             /* skip header line */
1655             ptr = fgets(buf, sizeof(buf), fp);
1656             while ((ptr = fgets(buf, sizeof(buf), fp)))
1657             {
1658                 memset( &row, 0, sizeof(row) );
1659
1660                 row.dwAddr = inet_addr(ptr);
1661                 while (*ptr && !isspace(*ptr)) ptr++;
1662                 strtoul(ptr + 1, &ptr, 16); /* hw type (skip) */
1663                 flags = strtoul(ptr + 1, &ptr, 16);
1664
1665 #ifdef ATF_COM
1666                 if (flags & ATF_COM) row.u.Type = MIB_IPNET_TYPE_DYNAMIC;
1667                 else
1668 #endif
1669 #ifdef ATF_PERM
1670                 if (flags & ATF_PERM) row.u.Type = MIB_IPNET_TYPE_STATIC;
1671                 else
1672 #endif
1673                     row.u.Type = MIB_IPNET_TYPE_OTHER;
1674
1675                 while (*ptr && isspace(*ptr)) ptr++;
1676                 while (*ptr && !isspace(*ptr))
1677                 {
1678                     row.bPhysAddr[row.dwPhysAddrLen++] = strtoul(ptr, &ptr, 16);
1679                     if (*ptr) ptr++;
1680                 }
1681                 while (*ptr && isspace(*ptr)) ptr++;
1682                 while (*ptr && !isspace(*ptr)) ptr++;   /* mask (skip) */
1683                 while (*ptr && isspace(*ptr)) ptr++;
1684                 getInterfaceIndexByName(ptr, &row.dwIndex);
1685
1686                 if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
1687                     break;
1688             }
1689             fclose(fp);
1690         }
1691         else ret = ERROR_NOT_SUPPORTED;
1692     }
1693 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1694     {
1695         void *data;
1696         int fd, len, namelen;
1697         mib2_ipNetToMediaEntry_t *entry;
1698         char name[64];
1699
1700         if ((fd = open_streams_mib( NULL )) != -1)
1701         {
1702             if ((data = read_mib_entry( fd, MIB2_IP, MIB2_IP_MEDIA, &len )))
1703             {
1704                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
1705                 {
1706                     row.dwPhysAddrLen = min( entry->ipNetToMediaPhysAddress.o_length, MAXLEN_PHYSADDR );
1707                     memcpy( row.bPhysAddr, entry->ipNetToMediaPhysAddress.o_bytes, row.dwPhysAddrLen );
1708                     row.dwAddr = entry->ipNetToMediaNetAddress;
1709                     row.u.Type = entry->ipNetToMediaType;
1710                     namelen = min( sizeof(name) - 1, entry->ipNetToMediaIfIndex.o_length );
1711                     memcpy( name, entry->ipNetToMediaIfIndex.o_bytes, namelen );
1712                     name[namelen] = 0;
1713                     getInterfaceIndexByName( name, &row.dwIndex );
1714                     if (!(table = append_ipnet_row( heap, flags, table, &count, &row ))) break;
1715                 }
1716                 HeapFree( GetProcessHeap(), 0, data );
1717             }
1718             close( fd );
1719         }
1720         else ret = ERROR_NOT_SUPPORTED;
1721     }
1722 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1723     {
1724       int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO};
1725 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
1726       size_t needed;
1727       char *buf = NULL, *lim, *next;
1728       struct rt_msghdr *rtm;
1729       struct sockaddr_inarp *sinarp;
1730       struct sockaddr_dl *sdl;
1731
1732       if (sysctl (mib, MIB_LEN,  NULL, &needed, NULL, 0) == -1)
1733       {
1734          ERR ("failed to get arp table\n");
1735          ret = ERROR_NOT_SUPPORTED;
1736          goto done;
1737       }
1738
1739       buf = HeapAlloc (GetProcessHeap (), 0, needed);
1740       if (!buf)
1741       {
1742           ret = ERROR_OUTOFMEMORY;
1743           goto done;
1744       }
1745
1746       if (sysctl (mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
1747       {
1748          ret = ERROR_NOT_SUPPORTED;
1749          goto done;
1750       }
1751
1752       lim = buf + needed;
1753       next = buf;
1754       while(next < lim)
1755       {
1756           rtm = (struct rt_msghdr *)next;
1757           sinarp=(struct sockaddr_inarp *)(rtm + 1);
1758           sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len));
1759           if(sdl->sdl_alen) /* arp entry */
1760           {
1761               memset( &row, 0, sizeof(row) );
1762               row.dwAddr = sinarp->sin_addr.s_addr;
1763               row.dwIndex = sdl->sdl_index;
1764               row.dwPhysAddrLen = min( 8, sdl->sdl_alen );
1765               memcpy( row.bPhysAddr, &sdl->sdl_data[sdl->sdl_nlen], row.dwPhysAddrLen );
1766               if(rtm->rtm_rmx.rmx_expire == 0) row.u.Type = MIB_IPNET_TYPE_STATIC;
1767               else if(sinarp->sin_other & SIN_PROXY) row.u.Type = MIB_IPNET_TYPE_OTHER;
1768               else if(rtm->rtm_rmx.rmx_expire != 0) row.u.Type = MIB_IPNET_TYPE_DYNAMIC;
1769               else row.u.Type = MIB_IPNET_TYPE_INVALID;
1770
1771               if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
1772                   break;
1773           }
1774           next += rtm->rtm_msglen;
1775       }
1776 done:
1777       HeapFree( GetProcessHeap (), 0, buf );
1778     }
1779 #else
1780     FIXME( "not implemented\n" );
1781     ret = ERROR_NOT_SUPPORTED;
1782 #endif
1783
1784     if (!table) return ERROR_OUTOFMEMORY;
1785     if (!ret)
1786     {
1787         if (bOrder && table->dwNumEntries)
1788             qsort( table->table, table->dwNumEntries, sizeof(row), compare_ipnet_rows );
1789         *ppIpNetTable = table;
1790     }
1791     else HeapFree( heap, flags, table );
1792     TRACE( "returning ret %u table %p\n", ret, table );
1793     return ret;
1794 }
1795
1796 static DWORD get_tcp_table_sizes( TCP_TABLE_CLASS class, DWORD row_count, DWORD *row_size )
1797 {
1798     DWORD table_size;
1799
1800     switch (class)
1801     {
1802     case TCP_TABLE_BASIC_LISTENER:
1803     case TCP_TABLE_BASIC_CONNECTIONS:
1804     case TCP_TABLE_BASIC_ALL:
1805     {
1806         table_size = FIELD_OFFSET(MIB_TCPTABLE, table[row_count]);
1807         if (row_size) *row_size = sizeof(MIB_TCPROW);
1808         break;
1809     }
1810     case TCP_TABLE_OWNER_PID_LISTENER:
1811     case TCP_TABLE_OWNER_PID_CONNECTIONS:
1812     case TCP_TABLE_OWNER_PID_ALL:
1813     {
1814         table_size = FIELD_OFFSET(MIB_TCPTABLE_OWNER_PID, table[row_count]);
1815         if (row_size) *row_size = sizeof(MIB_TCPROW_OWNER_PID);
1816         break;
1817     }
1818     default:
1819         ERR("unhandled class %u\n", class);
1820         return 0;
1821     }
1822     return table_size;
1823 }
1824
1825 static MIB_TCPTABLE *append_tcp_row( TCP_TABLE_CLASS class, HANDLE heap, DWORD flags,
1826                                      MIB_TCPTABLE *table, DWORD *count,
1827                                      const MIB_TCPROW_OWNER_PID *row, DWORD row_size )
1828 {
1829     if (table->dwNumEntries >= *count)
1830     {
1831         MIB_TCPTABLE *new_table;
1832         DWORD new_count = table->dwNumEntries * 2, new_table_size;
1833
1834         new_table_size = get_tcp_table_sizes( class, new_count, NULL );
1835         if (!(new_table = HeapReAlloc( heap, flags, table, new_table_size )))
1836         {
1837             HeapFree( heap, 0, table );
1838             return NULL;
1839         }
1840         *count = new_count;
1841         table = new_table;
1842     }
1843     memcpy( (char *)table->table + (table->dwNumEntries * row_size), row, row_size );
1844     table->dwNumEntries++;
1845     return table;
1846 }
1847
1848
1849 /* Why not a lookup table? Because the TCPS_* constants are different
1850    on different platforms */
1851 static inline MIB_TCP_STATE TCPStateToMIBState (int state)
1852 {
1853    switch (state)
1854    {
1855       case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB;
1856       case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT;
1857       case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD;
1858       case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1;
1859       case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2;
1860       case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT;
1861       case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT;
1862       case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK;
1863       case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN;
1864       case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING;
1865       default:
1866       case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED;
1867    }
1868 }
1869
1870 static int compare_tcp_rows(const void *a, const void *b)
1871 {
1872     const MIB_TCPROW *rowA = a;
1873     const MIB_TCPROW *rowB = b;
1874     int ret;
1875
1876     if ((ret = ntohl (rowA->dwLocalAddr) - ntohl (rowB->dwLocalAddr)) != 0) return ret;
1877     if ((ret = ntohs ((unsigned short)rowA->dwLocalPort) -
1878                ntohs ((unsigned short)rowB->dwLocalPort)) != 0) return ret;
1879     if ((ret = ntohl (rowA->dwRemoteAddr) - ntohl (rowB->dwRemoteAddr)) != 0) return ret;
1880     return ntohs ((unsigned short)rowA->dwRemotePort) - ntohs ((unsigned short)rowB->dwRemotePort);
1881 }
1882
1883 struct pid_map
1884 {
1885     unsigned int pid;
1886     unsigned int unix_pid;
1887 };
1888
1889 static struct pid_map *get_pid_map( unsigned int *num_entries )
1890 {
1891     HANDLE snapshot = NULL;
1892     struct pid_map *map;
1893     unsigned int i = 0, count = 16, size = count * sizeof(*map);
1894     NTSTATUS ret;
1895
1896     if (!(map = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL;
1897
1898     SERVER_START_REQ( create_snapshot )
1899     {
1900         req->flags      = SNAP_PROCESS;
1901         req->attributes = 0;
1902         if (!(ret = wine_server_call( req )))
1903             snapshot = wine_server_ptr_handle( reply->handle );
1904     }
1905     SERVER_END_REQ;
1906
1907     *num_entries = 0;
1908     while (ret == STATUS_SUCCESS)
1909     {
1910         SERVER_START_REQ( next_process )
1911         {
1912             req->handle = wine_server_obj_handle( snapshot );
1913             req->reset = (i == 0);
1914             if (!(ret = wine_server_call( req )))
1915             {
1916                 if (i >= count)
1917                 {
1918                     struct pid_map *new_map;
1919                     count *= 2;
1920                     size = count * sizeof(*new_map);
1921
1922                     if (!(new_map = HeapReAlloc( GetProcessHeap(), 0, map, size )))
1923                     {
1924                         HeapFree( GetProcessHeap(), 0, map );
1925                         map = NULL;
1926                         goto done;
1927                     }
1928                     map = new_map;
1929                 }
1930                 map[i].pid = reply->pid;
1931                 map[i].unix_pid = reply->unix_pid;
1932                 (*num_entries)++;
1933                 i++;
1934             }
1935         }
1936         SERVER_END_REQ;
1937     }
1938
1939 done:
1940     NtClose( snapshot );
1941     return map;
1942 }
1943
1944 static unsigned int find_owning_pid( struct pid_map *map, unsigned int num_entries, int inode )
1945 {
1946 #ifdef __linux__
1947     unsigned int i, len_socket;
1948     char socket[32];
1949
1950     sprintf( socket, "socket:[%d]", inode );
1951     len_socket = strlen( socket );
1952     for (i = 0; i < num_entries; i++)
1953     {
1954         char dir[32];
1955         struct dirent *dirent;
1956         DIR *dirfd;
1957
1958         sprintf( dir, "/proc/%u/fd", map[i].unix_pid );
1959         if ((dirfd = opendir( dir )))
1960         {
1961             while ((dirent = readdir( dirfd )))
1962             {
1963                 char link[sizeof(dirent->d_name) + 32], name[32];
1964                 int len;
1965
1966                 sprintf( link, "/proc/%u/fd/%s", map[i].unix_pid, dirent->d_name );
1967                 if ((len = readlink( link, name, 32 )) > 0) name[len] = 0;
1968                 if (len == len_socket && !strcmp( socket, name ))
1969                 {
1970                     closedir( dirfd );
1971                     return map[i].pid;
1972                 }
1973             }
1974             closedir( dirfd );
1975         }
1976     }
1977     return 0;
1978 #else
1979     FIXME( "not implemented\n" );
1980     return 0;
1981 #endif
1982 }
1983
1984 DWORD build_tcp_table( TCP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags,
1985                        DWORD *size )
1986 {
1987     MIB_TCPTABLE *table;
1988     MIB_TCPROW_OWNER_PID row;
1989     DWORD ret = NO_ERROR, count = 16, table_size, row_size;
1990
1991     if (!(table_size = get_tcp_table_sizes( class, count, &row_size )))
1992         return ERROR_INVALID_PARAMETER;
1993
1994     if (!(table = HeapAlloc( heap, flags, table_size )))
1995         return ERROR_OUTOFMEMORY;
1996
1997     table->dwNumEntries = 0;
1998
1999 #ifdef __linux__
2000     {
2001         FILE *fp;
2002
2003         if ((fp = fopen("/proc/net/tcp", "r")))
2004         {
2005             char buf[512], *ptr;
2006             struct pid_map *map = NULL;
2007             unsigned int dummy, num_entries = 0;
2008             int inode;
2009
2010             if (class == TCP_TABLE_OWNER_PID_ALL) map = get_pid_map( &num_entries );
2011
2012             /* skip header line */
2013             ptr = fgets(buf, sizeof(buf), fp);
2014             while ((ptr = fgets(buf, sizeof(buf), fp)))
2015             {
2016                 if (sscanf( ptr, "%x: %x:%x %x:%x %x %*s %*s %*s %*s %*s %d", &dummy,
2017                             &row.dwLocalAddr, &row.dwLocalPort, &row.dwRemoteAddr,
2018                             &row.dwRemotePort, &row.dwState, &inode ) != 7)
2019                     continue;
2020                 row.dwLocalPort = htons( row.dwLocalPort );
2021                 row.dwRemotePort = htons( row.dwRemotePort );
2022                 row.dwState = TCPStateToMIBState( row.dwState );
2023                 if (class == TCP_TABLE_OWNER_PID_ALL)
2024                     row.dwOwningPid = find_owning_pid( map, num_entries, inode );
2025
2026                 if (!(table = append_tcp_row( class, heap, flags, table, &count, &row, row_size )))
2027                     break;
2028             }
2029             HeapFree( GetProcessHeap(), 0, map );
2030             fclose( fp );
2031         }
2032         else ret = ERROR_NOT_SUPPORTED;
2033     }
2034 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
2035     {
2036         void *data;
2037         int fd, len;
2038         mib2_tcpConnEntry_t *entry;
2039
2040         if ((fd = open_streams_mib( "tcp" )) != -1)
2041         {
2042             if ((data = read_mib_entry( fd, MIB2_TCP, MIB2_TCP_CONN, &len )))
2043             {
2044                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
2045                 {
2046                     row.dwLocalAddr = entry->tcpConnLocalAddress;
2047                     row.dwLocalPort = htons( entry->tcpConnLocalPort );
2048                     row.dwRemoteAddr = entry->tcpConnRemAddress;
2049                     row.dwRemotePort = htons( entry->tcpConnRemPort );
2050                     row.dwState = entry->tcpConnState;
2051                     if (!(table = append_tcp_row( class, heap, flags, table, &count, &row, row_size )))
2052                         break;
2053                 }
2054                 HeapFree( GetProcessHeap(), 0, data );
2055             }
2056             close( fd );
2057         }
2058         else ret = ERROR_NOT_SUPPORTED;
2059     }
2060 #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
2061     {
2062         size_t Len = 0;
2063         char *Buf = NULL;
2064         struct xinpgen *pXIG, *pOrigXIG;
2065
2066         if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0)
2067         {
2068             ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
2069             ret = ERROR_NOT_SUPPORTED;
2070             goto done;
2071         }
2072
2073         Buf = HeapAlloc (GetProcessHeap (), 0, Len);
2074         if (!Buf)
2075         {
2076             ret = ERROR_OUTOFMEMORY;
2077             goto done;
2078         }
2079
2080         if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0)
2081         {
2082             ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
2083             ret = ERROR_NOT_SUPPORTED;
2084             goto done;
2085         }
2086
2087         /* Might be nothing here; first entry is just a header it seems */
2088         if (Len <= sizeof (struct xinpgen)) goto done;
2089
2090         pOrigXIG = (struct xinpgen *)Buf;
2091         pXIG = pOrigXIG;
2092
2093         for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
2094              pXIG->xig_len > sizeof (struct xinpgen);
2095              pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
2096         {
2097             struct tcpcb *pTCPData = NULL;
2098             struct inpcb *pINData;
2099             struct xsocket *pSockData;
2100
2101             pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
2102             pINData = &((struct xtcpcb *)pXIG)->xt_inp;
2103             pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
2104
2105             /* Ignore sockets for other protocols */
2106             if (pSockData->xso_protocol != IPPROTO_TCP)
2107                 continue;
2108
2109             /* Ignore PCBs that were freed while generating the data */
2110             if (pINData->inp_gencnt > pOrigXIG->xig_gen)
2111                 continue;
2112
2113             /* we're only interested in IPv4 addresses */
2114             if (!(pINData->inp_vflag & INP_IPV4) ||
2115                 (pINData->inp_vflag & INP_IPV6))
2116                 continue;
2117
2118             /* If all 0's, skip it */
2119             if (!pINData->inp_laddr.s_addr &&
2120                 !pINData->inp_lport &&
2121                 !pINData->inp_faddr.s_addr &&
2122                 !pINData->inp_fport)
2123                 continue;
2124
2125             /* Fill in structure details */
2126             row.dwLocalAddr = pINData->inp_laddr.s_addr;
2127             row.dwLocalPort = pINData->inp_lport;
2128             row.dwRemoteAddr = pINData->inp_faddr.s_addr;
2129             row.dwRemotePort = pINData->inp_fport;
2130             row.dwState = TCPStateToMIBState (pTCPData->t_state);
2131             if (!(table = append_tcp_row( class, heap, flags, table, &count, &row, row_size )))
2132                 break;
2133         }
2134
2135     done:
2136         HeapFree (GetProcessHeap (), 0, Buf);
2137     }
2138 #else
2139     FIXME( "not implemented\n" );
2140     ret = ERROR_NOT_SUPPORTED;
2141 #endif
2142
2143     if (!table) return ERROR_OUTOFMEMORY;
2144     if (!ret)
2145     {
2146         if (order && table->dwNumEntries)
2147             qsort( table->table, table->dwNumEntries, row_size, compare_tcp_rows );
2148         *tablep = table;
2149     }
2150     else HeapFree( heap, flags, table );
2151     if (size) *size = get_tcp_table_sizes( class, count, NULL );
2152     TRACE( "returning ret %u table %p\n", ret, table );
2153     return ret;
2154 }
2155
2156 /******************************************************************
2157  *    AllocateAndGetTcpTableFromStack (IPHLPAPI.@)
2158  *
2159  * Get the TCP connection table.
2160  * Like GetTcpTable(), but allocate the returned table from heap.
2161  *
2162  * PARAMS
2163  *  ppTcpTable [Out] pointer into which the MIB_TCPTABLE is
2164  *                   allocated and returned.
2165  *  bOrder     [In]  whether to sort the table
2166  *  heap       [In]  heap from which the table is allocated
2167  *  flags      [In]  flags to HeapAlloc
2168  *
2169  * RETURNS
2170  *  ERROR_INVALID_PARAMETER if ppTcpTable is NULL, whatever GetTcpTable()
2171  *  returns otherwise.
2172  */
2173 DWORD WINAPI AllocateAndGetTcpTableFromStack( PMIB_TCPTABLE *ppTcpTable, BOOL bOrder,
2174                                               HANDLE heap, DWORD flags )
2175 {
2176     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppTcpTable, bOrder, heap, flags);
2177
2178     if (!ppTcpTable) return ERROR_INVALID_PARAMETER;
2179     return build_tcp_table( TCP_TABLE_BASIC_ALL, (void **)ppTcpTable, bOrder, heap, flags, NULL );
2180 }
2181
2182 static DWORD get_udp_table_sizes( UDP_TABLE_CLASS class, DWORD row_count, DWORD *row_size )
2183 {
2184     DWORD table_size;
2185
2186     switch (class)
2187     {
2188     case UDP_TABLE_BASIC:
2189     {
2190         table_size = FIELD_OFFSET(MIB_UDPTABLE, table[row_count]);
2191         if (row_size) *row_size = sizeof(MIB_UDPROW);
2192         break;
2193     }
2194     case UDP_TABLE_OWNER_PID:
2195     {
2196         table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_PID, table[row_count]);
2197         if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_PID);
2198         break;
2199     }
2200     case UDP_TABLE_OWNER_MODULE:
2201     {
2202         table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_MODULE, table[row_count]);
2203         if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_MODULE);
2204         break;
2205     }
2206     default:
2207         ERR("unhandled class %u\n", class);
2208         return 0;
2209     }
2210     return table_size;
2211 }
2212
2213 static MIB_UDPTABLE *append_udp_row( UDP_TABLE_CLASS class, HANDLE heap, DWORD flags,
2214                                      MIB_UDPTABLE *table, DWORD *count,
2215                                      const MIB_UDPROW_OWNER_MODULE *row, DWORD row_size )
2216 {
2217     if (table->dwNumEntries >= *count)
2218     {
2219         MIB_UDPTABLE *new_table;
2220         DWORD new_count = table->dwNumEntries * 2, new_table_size;
2221
2222         new_table_size = get_udp_table_sizes( class, new_count, NULL );
2223         if (!(new_table = HeapReAlloc( heap, flags, table, new_table_size )))
2224         {
2225             HeapFree( heap, 0, table );
2226             return NULL;
2227         }
2228         *count = new_count;
2229         table = new_table;
2230     }
2231     memcpy( (char *)table->table + (table->dwNumEntries * row_size), row, row_size );
2232     table->dwNumEntries++;
2233     return table;
2234 }
2235
2236 static int compare_udp_rows(const void *a, const void *b)
2237 {
2238     const MIB_UDPROW *rowA = a;
2239     const MIB_UDPROW *rowB = b;
2240     int ret;
2241
2242     if ((ret = rowA->dwLocalAddr - rowB->dwLocalAddr) != 0) return ret;
2243     return rowA->dwLocalPort - rowB->dwLocalPort;
2244 }
2245
2246 DWORD build_udp_table( UDP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags,
2247                        DWORD *size )
2248 {
2249     MIB_UDPTABLE *table;
2250     MIB_UDPROW_OWNER_MODULE row;
2251     DWORD ret = NO_ERROR, count = 16, table_size, row_size;
2252
2253     if (!(table_size = get_udp_table_sizes( class, count, &row_size )))
2254         return ERROR_INVALID_PARAMETER;
2255
2256     if (!(table = HeapAlloc( heap, flags, table_size )))
2257          return ERROR_OUTOFMEMORY;
2258
2259     table->dwNumEntries = 0;
2260     memset( &row, 0, sizeof(row) );
2261
2262 #ifdef __linux__
2263     {
2264         FILE *fp;
2265
2266         if ((fp = fopen( "/proc/net/udp", "r" )))
2267         {
2268             char buf[512], *ptr;
2269             struct pid_map *map = NULL;
2270             unsigned int dummy, num_entries = 0;
2271             int inode;
2272
2273             if (class == UDP_TABLE_OWNER_PID || class == UDP_TABLE_OWNER_MODULE)
2274                 map = get_pid_map( &num_entries );
2275
2276             /* skip header line */
2277             ptr = fgets( buf, sizeof(buf), fp );
2278             while ((ptr = fgets( buf, sizeof(buf), fp )))
2279             {
2280                 if (sscanf( ptr, "%u: %x:%x %*s %*s %*s %*s %*s %*s %*s %d", &dummy,
2281                     &row.dwLocalAddr, &row.dwLocalPort, &inode ) != 4)
2282                     continue;
2283                 row.dwLocalPort = htons( row.dwLocalPort );
2284                 if (class == UDP_TABLE_OWNER_PID || class == UDP_TABLE_OWNER_MODULE)
2285                     row.dwOwningPid = find_owning_pid( map, num_entries, inode );
2286                 if (!(table = append_udp_row( class, heap, flags, table, &count, &row, row_size )))
2287                     break;
2288             }
2289             HeapFree( GetProcessHeap(), 0, map );
2290             fclose( fp );
2291         }
2292         else ret = ERROR_NOT_SUPPORTED;
2293     }
2294 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
2295     {
2296         void *data;
2297         int fd, len;
2298         mib2_udpEntry_t *entry;
2299
2300         if ((fd = open_streams_mib( "udp" )) != -1)
2301         {
2302             if ((data = read_mib_entry( fd, MIB2_UDP, MIB2_UDP_ENTRY, &len )))
2303             {
2304                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
2305                 {
2306                     row.dwLocalAddr = entry->udpLocalAddress;
2307                     row.dwLocalPort = htons( entry->udpLocalPort );
2308                     if (!(table = append_udp_row( class, heap, flags, table, &count, &row, row_size ))) break;
2309                 }
2310                 HeapFree( GetProcessHeap(), 0, data );
2311             }
2312             close( fd );
2313         }
2314         else ret = ERROR_NOT_SUPPORTED;
2315     }
2316 #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
2317     {
2318         size_t Len = 0;
2319         char *Buf = NULL;
2320         struct xinpgen *pXIG, *pOrigXIG;
2321
2322         if (sysctlbyname ("net.inet.udp.pcblist", NULL, &Len, NULL, 0) < 0)
2323         {
2324             ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n");
2325             ret = ERROR_NOT_SUPPORTED;
2326             goto done;
2327         }
2328
2329         Buf = HeapAlloc (GetProcessHeap (), 0, Len);
2330         if (!Buf)
2331         {
2332             ret = ERROR_OUTOFMEMORY;
2333             goto done;
2334         }
2335
2336         if (sysctlbyname ("net.inet.udp.pcblist", Buf, &Len, NULL, 0) < 0)
2337         {
2338             ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n");
2339             ret = ERROR_NOT_SUPPORTED;
2340             goto done;
2341         }
2342
2343         /* Might be nothing here; first entry is just a header it seems */
2344         if (Len <= sizeof (struct xinpgen)) goto done;
2345
2346         pOrigXIG = (struct xinpgen *)Buf;
2347         pXIG = pOrigXIG;
2348
2349         for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
2350              pXIG->xig_len > sizeof (struct xinpgen);
2351              pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
2352         {
2353             struct inpcb *pINData;
2354             struct xsocket *pSockData;
2355
2356             pINData = &((struct xinpcb *)pXIG)->xi_inp;
2357             pSockData = &((struct xinpcb *)pXIG)->xi_socket;
2358
2359             /* Ignore sockets for other protocols */
2360             if (pSockData->xso_protocol != IPPROTO_UDP)
2361                 continue;
2362
2363             /* Ignore PCBs that were freed while generating the data */
2364             if (pINData->inp_gencnt > pOrigXIG->xig_gen)
2365                 continue;
2366
2367             /* we're only interested in IPv4 addresses */
2368             if (!(pINData->inp_vflag & INP_IPV4) ||
2369                 (pINData->inp_vflag & INP_IPV6))
2370                 continue;
2371
2372             /* If all 0's, skip it */
2373             if (!pINData->inp_laddr.s_addr &&
2374                 !pINData->inp_lport)
2375                 continue;
2376
2377             /* Fill in structure details */
2378             row.dwLocalAddr = pINData->inp_laddr.s_addr;
2379             row.dwLocalPort = pINData->inp_lport;
2380             if (!(table = append_udp_row( class, heap, flags, table, &count, &row, row_size ))) break;
2381         }
2382
2383     done:
2384         HeapFree (GetProcessHeap (), 0, Buf);
2385     }
2386 #else
2387     FIXME( "not implemented\n" );
2388     ret = ERROR_NOT_SUPPORTED;
2389 #endif
2390
2391     if (!table) return ERROR_OUTOFMEMORY;
2392     if (!ret)
2393     {
2394         if (order && table->dwNumEntries)
2395             qsort( table->table, table->dwNumEntries, row_size, compare_udp_rows );
2396         *tablep = table;
2397     }
2398     else HeapFree( heap, flags, table );
2399     if (size) *size = get_udp_table_sizes( class, count, NULL );
2400     TRACE( "returning ret %u table %p\n", ret, table );
2401     return ret;
2402 }
2403
2404 /******************************************************************
2405  *    AllocateAndGetUdpTableFromStack (IPHLPAPI.@)
2406  *
2407  * Get the UDP listener table.
2408  * Like GetUdpTable(), but allocate the returned table from heap.
2409  *
2410  * PARAMS
2411  *  ppUdpTable [Out] pointer into which the MIB_UDPTABLE is
2412  *                   allocated and returned.
2413  *  bOrder     [In]  whether to sort the table
2414  *  heap       [In]  heap from which the table is allocated
2415  *  flags      [In]  flags to HeapAlloc
2416  *
2417  * RETURNS
2418  *  ERROR_INVALID_PARAMETER if ppUdpTable is NULL, whatever GetUdpTable()
2419  *  returns otherwise.
2420  */
2421 DWORD WINAPI AllocateAndGetUdpTableFromStack(PMIB_UDPTABLE *ppUdpTable, BOOL bOrder,
2422                                              HANDLE heap, DWORD flags)
2423 {
2424     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppUdpTable, bOrder, heap, flags);
2425
2426     if (!ppUdpTable) return ERROR_INVALID_PARAMETER;
2427     return build_udp_table( UDP_TABLE_BASIC, (void **)ppUdpTable, bOrder, heap, flags, NULL );
2428 }