include: Assorted spelling fixes.
[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 all entries except for gateway routes which aren't
1501              multicast */
1502           if (!(rtm->rtm_flags & RTF_GATEWAY) ||
1503               (rtm->rtm_flags & RTF_MULTICAST))
1504              continue;
1505
1506           memset( &row, 0, sizeof(row) );
1507           row.dwForwardIfIndex = rtm->rtm_index;
1508           row.u1.ForwardType = MIB_IPROUTE_TYPE_INDIRECT;
1509           row.dwForwardMetric1 = rtm->rtm_rmx.rmx_hopcount;
1510           row.u2.ForwardProto = MIB_IPPROTO_LOCAL;
1511
1512           addrPtr = (char *)(rtm + 1);
1513
1514           for (i = 1; i; i <<= 1)
1515           {
1516              struct sockaddr *sa;
1517              DWORD addr;
1518
1519              if (!(i & rtm->rtm_addrs))
1520                 continue;
1521
1522              sa = (struct sockaddr *)addrPtr;
1523              ADVANCE (addrPtr, sa);
1524
1525              /* default routes are encoded by length-zero sockaddr */
1526              if (sa->sa_len == 0)
1527                 addr = 0;
1528              else if (sa->sa_family != AF_INET)
1529              {
1530                 WARN ("Received unsupported sockaddr family 0x%x\n",
1531                      sa->sa_family);
1532                 addr = 0;
1533              }
1534              else
1535              {
1536                 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
1537
1538                 addr = sin->sin_addr.s_addr;
1539              }
1540
1541              switch (i)
1542              {
1543                 case RTA_DST:     row.dwForwardDest = addr; break;
1544                 case RTA_GATEWAY: row.dwForwardNextHop = addr; break;
1545                 case RTA_NETMASK: row.dwForwardMask = addr; break;
1546                 default:
1547                    WARN ("Unexpected address type 0x%x\n", i);
1548              }
1549           }
1550
1551           if (!(table = append_ipforward_row( heap, flags, table, &count, &row )))
1552               break;
1553        }
1554 done:
1555       HeapFree( GetProcessHeap (), 0, buf );
1556     }
1557 #else
1558     FIXME( "not implemented\n" );
1559     ret = ERROR_NOT_SUPPORTED;
1560 #endif
1561
1562     if (!table) return ERROR_OUTOFMEMORY;
1563     if (!ret)
1564     {
1565         if (bOrder && table->dwNumEntries)
1566             qsort( table->table, table->dwNumEntries, sizeof(row), compare_ipforward_rows );
1567         *ppIpForwardTable = table;
1568     }
1569     else HeapFree( heap, flags, table );
1570     TRACE( "returning ret %u table %p\n", ret, table );
1571     return ret;
1572 }
1573
1574 static MIB_IPNETTABLE *append_ipnet_row( HANDLE heap, DWORD flags, MIB_IPNETTABLE *table,
1575                                          DWORD *count, const MIB_IPNETROW *row )
1576 {
1577     if (table->dwNumEntries >= *count)
1578     {
1579         MIB_IPNETTABLE *new_table;
1580         DWORD new_count = table->dwNumEntries * 2;
1581
1582         if (!(new_table = HeapReAlloc( heap, flags, table,
1583                                        FIELD_OFFSET(MIB_IPNETTABLE, table[new_count] ))))
1584         {
1585             HeapFree( heap, 0, table );
1586             return NULL;
1587         }
1588         *count = new_count;
1589         table = new_table;
1590     }
1591     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1592     return table;
1593 }
1594
1595 static int compare_ipnet_rows(const void *a, const void *b)
1596 {
1597     const MIB_IPNETROW *rowA = a;
1598     const MIB_IPNETROW *rowB = b;
1599
1600     return ntohl(rowA->dwAddr) - ntohl(rowB->dwAddr);
1601 }
1602
1603
1604 /******************************************************************
1605  *    AllocateAndGetIpNetTableFromStack (IPHLPAPI.@)
1606  *
1607  * Get the IP-to-physical address mapping table.
1608  * Like GetIpNetTable(), but allocate the returned table from heap.
1609  *
1610  * PARAMS
1611  *  ppIpNetTable [Out] pointer into which the MIB_IPNETTABLE is
1612  *                     allocated and returned.
1613  *  bOrder       [In]  whether to sort the table
1614  *  heap         [In]  heap from which the table is allocated
1615  *  flags        [In]  flags to HeapAlloc
1616  *
1617  * RETURNS
1618  *  ERROR_INVALID_PARAMETER if ppIpNetTable is NULL, other error codes
1619  *  on failure, NO_ERROR on success.
1620  */
1621 DWORD WINAPI AllocateAndGetIpNetTableFromStack(PMIB_IPNETTABLE *ppIpNetTable, BOOL bOrder,
1622                                                HANDLE heap, DWORD flags)
1623 {
1624     MIB_IPNETTABLE *table;
1625     MIB_IPNETROW row;
1626     DWORD ret = NO_ERROR, count = 16;
1627
1628     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppIpNetTable, bOrder, heap, flags);
1629
1630     if (!ppIpNetTable) return ERROR_INVALID_PARAMETER;
1631
1632     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_IPNETTABLE, table[count] ))))
1633         return ERROR_OUTOFMEMORY;
1634
1635     table->dwNumEntries = 0;
1636
1637 #ifdef __linux__
1638     {
1639         FILE *fp;
1640
1641         if ((fp = fopen("/proc/net/arp", "r")))
1642         {
1643             char buf[512], *ptr;
1644             DWORD flags;
1645
1646             /* skip header line */
1647             ptr = fgets(buf, sizeof(buf), fp);
1648             while ((ptr = fgets(buf, sizeof(buf), fp)))
1649             {
1650                 memset( &row, 0, sizeof(row) );
1651
1652                 row.dwAddr = inet_addr(ptr);
1653                 while (*ptr && !isspace(*ptr)) ptr++;
1654                 strtoul(ptr + 1, &ptr, 16); /* hw type (skip) */
1655                 flags = strtoul(ptr + 1, &ptr, 16);
1656
1657 #ifdef ATF_COM
1658                 if (flags & ATF_COM) row.u.Type = MIB_IPNET_TYPE_DYNAMIC;
1659                 else
1660 #endif
1661 #ifdef ATF_PERM
1662                 if (flags & ATF_PERM) row.u.Type = MIB_IPNET_TYPE_STATIC;
1663                 else
1664 #endif
1665                     row.u.Type = MIB_IPNET_TYPE_OTHER;
1666
1667                 while (*ptr && isspace(*ptr)) ptr++;
1668                 while (*ptr && !isspace(*ptr))
1669                 {
1670                     row.bPhysAddr[row.dwPhysAddrLen++] = strtoul(ptr, &ptr, 16);
1671                     if (*ptr) ptr++;
1672                 }
1673                 while (*ptr && isspace(*ptr)) ptr++;
1674                 while (*ptr && !isspace(*ptr)) ptr++;   /* mask (skip) */
1675                 while (*ptr && isspace(*ptr)) ptr++;
1676                 getInterfaceIndexByName(ptr, &row.dwIndex);
1677
1678                 if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
1679                     break;
1680             }
1681             fclose(fp);
1682         }
1683         else ret = ERROR_NOT_SUPPORTED;
1684     }
1685 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1686     {
1687         void *data;
1688         int fd, len, namelen;
1689         mib2_ipNetToMediaEntry_t *entry;
1690         char name[64];
1691
1692         if ((fd = open_streams_mib( NULL )) != -1)
1693         {
1694             if ((data = read_mib_entry( fd, MIB2_IP, MIB2_IP_MEDIA, &len )))
1695             {
1696                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
1697                 {
1698                     row.dwPhysAddrLen = min( entry->ipNetToMediaPhysAddress.o_length, MAXLEN_PHYSADDR );
1699                     memcpy( row.bPhysAddr, entry->ipNetToMediaPhysAddress.o_bytes, row.dwPhysAddrLen );
1700                     row.dwAddr = entry->ipNetToMediaNetAddress;
1701                     row.u.Type = entry->ipNetToMediaType;
1702                     namelen = min( sizeof(name) - 1, entry->ipNetToMediaIfIndex.o_length );
1703                     memcpy( name, entry->ipNetToMediaIfIndex.o_bytes, namelen );
1704                     name[namelen] = 0;
1705                     getInterfaceIndexByName( name, &row.dwIndex );
1706                     if (!(table = append_ipnet_row( heap, flags, table, &count, &row ))) break;
1707                 }
1708                 HeapFree( GetProcessHeap(), 0, data );
1709             }
1710             close( fd );
1711         }
1712         else ret = ERROR_NOT_SUPPORTED;
1713     }
1714 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1715     {
1716       int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO};
1717 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
1718       size_t needed;
1719       char *buf = NULL, *lim, *next;
1720       struct rt_msghdr *rtm;
1721       struct sockaddr_inarp *sinarp;
1722       struct sockaddr_dl *sdl;
1723
1724       if (sysctl (mib, MIB_LEN,  NULL, &needed, NULL, 0) == -1)
1725       {
1726          ERR ("failed to get arp table\n");
1727          ret = ERROR_NOT_SUPPORTED;
1728          goto done;
1729       }
1730
1731       buf = HeapAlloc (GetProcessHeap (), 0, needed);
1732       if (!buf)
1733       {
1734           ret = ERROR_OUTOFMEMORY;
1735           goto done;
1736       }
1737
1738       if (sysctl (mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
1739       {
1740          ret = ERROR_NOT_SUPPORTED;
1741          goto done;
1742       }
1743
1744       lim = buf + needed;
1745       next = buf;
1746       while(next < lim)
1747       {
1748           rtm = (struct rt_msghdr *)next;
1749           sinarp=(struct sockaddr_inarp *)(rtm + 1);
1750           sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len));
1751           if(sdl->sdl_alen) /* arp entry */
1752           {
1753               memset( &row, 0, sizeof(row) );
1754               row.dwAddr = sinarp->sin_addr.s_addr;
1755               row.dwIndex = sdl->sdl_index;
1756               row.dwPhysAddrLen = min( 8, sdl->sdl_alen );
1757               memcpy( row.bPhysAddr, &sdl->sdl_data[sdl->sdl_nlen], row.dwPhysAddrLen );
1758               if(rtm->rtm_rmx.rmx_expire == 0) row.u.Type = MIB_IPNET_TYPE_STATIC;
1759               else if(sinarp->sin_other & SIN_PROXY) row.u.Type = MIB_IPNET_TYPE_OTHER;
1760               else if(rtm->rtm_rmx.rmx_expire != 0) row.u.Type = MIB_IPNET_TYPE_DYNAMIC;
1761               else row.u.Type = MIB_IPNET_TYPE_INVALID;
1762
1763               if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
1764                   break;
1765           }
1766           next += rtm->rtm_msglen;
1767       }
1768 done:
1769       HeapFree( GetProcessHeap (), 0, buf );
1770     }
1771 #else
1772     FIXME( "not implemented\n" );
1773     ret = ERROR_NOT_SUPPORTED;
1774 #endif
1775
1776     if (!table) return ERROR_OUTOFMEMORY;
1777     if (!ret)
1778     {
1779         if (bOrder && table->dwNumEntries)
1780             qsort( table->table, table->dwNumEntries, sizeof(row), compare_ipnet_rows );
1781         *ppIpNetTable = table;
1782     }
1783     else HeapFree( heap, flags, table );
1784     TRACE( "returning ret %u table %p\n", ret, table );
1785     return ret;
1786 }
1787
1788 static DWORD get_tcp_table_sizes( TCP_TABLE_CLASS class, DWORD row_count, DWORD *row_size )
1789 {
1790     DWORD table_size;
1791
1792     switch (class)
1793     {
1794     case TCP_TABLE_BASIC_LISTENER:
1795     case TCP_TABLE_BASIC_CONNECTIONS:
1796     case TCP_TABLE_BASIC_ALL:
1797     {
1798         table_size = FIELD_OFFSET(MIB_TCPTABLE, table[row_count]);
1799         if (row_size) *row_size = sizeof(MIB_TCPROW);
1800         break;
1801     }
1802     case TCP_TABLE_OWNER_PID_LISTENER:
1803     case TCP_TABLE_OWNER_PID_CONNECTIONS:
1804     case TCP_TABLE_OWNER_PID_ALL:
1805     {
1806         table_size = FIELD_OFFSET(MIB_TCPTABLE_OWNER_PID, table[row_count]);
1807         if (row_size) *row_size = sizeof(MIB_TCPROW_OWNER_PID);
1808         break;
1809     }
1810     default:
1811         ERR("unhandled class %u\n", class);
1812         return 0;
1813     }
1814     return table_size;
1815 }
1816
1817 static MIB_TCPTABLE *append_tcp_row( TCP_TABLE_CLASS class, HANDLE heap, DWORD flags,
1818                                      MIB_TCPTABLE *table, DWORD *count,
1819                                      const MIB_TCPROW_OWNER_PID *row, DWORD row_size )
1820 {
1821     if (table->dwNumEntries >= *count)
1822     {
1823         MIB_TCPTABLE *new_table;
1824         DWORD new_count = table->dwNumEntries * 2, new_table_size;
1825
1826         new_table_size = get_tcp_table_sizes( class, new_count, NULL );
1827         if (!(new_table = HeapReAlloc( heap, flags, table, new_table_size )))
1828         {
1829             HeapFree( heap, 0, table );
1830             return NULL;
1831         }
1832         *count = new_count;
1833         table = new_table;
1834     }
1835     memcpy( (char *)table->table + (table->dwNumEntries * row_size), row, row_size );
1836     table->dwNumEntries++;
1837     return table;
1838 }
1839
1840
1841 /* Why not a lookup table? Because the TCPS_* constants are different
1842    on different platforms */
1843 static inline MIB_TCP_STATE TCPStateToMIBState (int state)
1844 {
1845    switch (state)
1846    {
1847       case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB;
1848       case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT;
1849       case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD;
1850       case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1;
1851       case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2;
1852       case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT;
1853       case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT;
1854       case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK;
1855       case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN;
1856       case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING;
1857       default:
1858       case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED;
1859    }
1860 }
1861
1862 static int compare_tcp_rows(const void *a, const void *b)
1863 {
1864     const MIB_TCPROW *rowA = a;
1865     const MIB_TCPROW *rowB = b;
1866     int ret;
1867
1868     if ((ret = ntohl (rowA->dwLocalAddr) - ntohl (rowB->dwLocalAddr)) != 0) return ret;
1869     if ((ret = ntohs ((unsigned short)rowA->dwLocalPort) -
1870                ntohs ((unsigned short)rowB->dwLocalPort)) != 0) return ret;
1871     if ((ret = ntohl (rowA->dwRemoteAddr) - ntohl (rowB->dwRemoteAddr)) != 0) return ret;
1872     return ntohs ((unsigned short)rowA->dwRemotePort) - ntohs ((unsigned short)rowB->dwRemotePort);
1873 }
1874
1875 struct pid_map
1876 {
1877     unsigned int pid;
1878     unsigned int unix_pid;
1879 };
1880
1881 static struct pid_map *get_pid_map( unsigned int *num_entries )
1882 {
1883     HANDLE snapshot = NULL;
1884     struct pid_map *map;
1885     unsigned int i = 0, count = 16, size = count * sizeof(*map);
1886     NTSTATUS ret;
1887
1888     if (!(map = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL;
1889
1890     SERVER_START_REQ( create_snapshot )
1891     {
1892         req->flags      = SNAP_PROCESS;
1893         req->attributes = 0;
1894         if (!(ret = wine_server_call( req )))
1895             snapshot = wine_server_ptr_handle( reply->handle );
1896     }
1897     SERVER_END_REQ;
1898
1899     *num_entries = 0;
1900     while (ret == STATUS_SUCCESS)
1901     {
1902         SERVER_START_REQ( next_process )
1903         {
1904             req->handle = wine_server_obj_handle( snapshot );
1905             req->reset = (i == 0);
1906             if (!(ret = wine_server_call( req )))
1907             {
1908                 if (i >= count)
1909                 {
1910                     struct pid_map *new_map;
1911                     count *= 2;
1912                     size = count * sizeof(*new_map);
1913
1914                     if (!(new_map = HeapReAlloc( GetProcessHeap(), 0, map, size )))
1915                     {
1916                         HeapFree( GetProcessHeap(), 0, map );
1917                         map = NULL;
1918                         goto done;
1919                     }
1920                     map = new_map;
1921                 }
1922                 map[i].pid = reply->pid;
1923                 map[i].unix_pid = reply->unix_pid;
1924                 (*num_entries)++;
1925                 i++;
1926             }
1927         }
1928         SERVER_END_REQ;
1929     }
1930
1931 done:
1932     NtClose( snapshot );
1933     return map;
1934 }
1935
1936 static unsigned int find_owning_pid( struct pid_map *map, unsigned int num_entries, int inode )
1937 {
1938 #ifdef __linux__
1939     unsigned int i, len_socket;
1940     char socket[32];
1941
1942     sprintf( socket, "socket:[%d]", inode );
1943     len_socket = strlen( socket );
1944     for (i = 0; i < num_entries; i++)
1945     {
1946         char dir[32];
1947         struct dirent *dirent;
1948         DIR *dirfd;
1949
1950         sprintf( dir, "/proc/%u/fd", map[i].unix_pid );
1951         if ((dirfd = opendir( dir )))
1952         {
1953             while ((dirent = readdir( dirfd )))
1954             {
1955                 char link[sizeof(dirent->d_name) + 32], name[32];
1956                 int len;
1957
1958                 sprintf( link, "/proc/%u/fd/%s", map[i].unix_pid, dirent->d_name );
1959                 if ((len = readlink( link, name, 32 )) > 0) name[len] = 0;
1960                 if (len == len_socket && !strcmp( socket, name ))
1961                 {
1962                     closedir( dirfd );
1963                     return map[i].pid;
1964                 }
1965             }
1966             closedir( dirfd );
1967         }
1968     }
1969     return 0;
1970 #else
1971     FIXME( "not implemented\n" );
1972     return 0;
1973 #endif
1974 }
1975
1976 DWORD build_tcp_table( TCP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags,
1977                        DWORD *size )
1978 {
1979     MIB_TCPTABLE *table;
1980     MIB_TCPROW_OWNER_PID row;
1981     DWORD ret = NO_ERROR, count = 16, table_size, row_size;
1982
1983     if (!(table_size = get_tcp_table_sizes( class, count, &row_size )))
1984         return ERROR_INVALID_PARAMETER;
1985
1986     if (!(table = HeapAlloc( heap, flags, table_size )))
1987         return ERROR_OUTOFMEMORY;
1988
1989     table->dwNumEntries = 0;
1990
1991 #ifdef __linux__
1992     {
1993         FILE *fp;
1994
1995         if ((fp = fopen("/proc/net/tcp", "r")))
1996         {
1997             char buf[512], *ptr;
1998             struct pid_map *map = NULL;
1999             unsigned int dummy, num_entries = 0;
2000             int inode;
2001
2002             if (class == TCP_TABLE_OWNER_PID_ALL) map = get_pid_map( &num_entries );
2003
2004             /* skip header line */
2005             ptr = fgets(buf, sizeof(buf), fp);
2006             while ((ptr = fgets(buf, sizeof(buf), fp)))
2007             {
2008                 if (sscanf( ptr, "%x: %x:%x %x:%x %x %*s %*s %*s %*s %*s %d", &dummy,
2009                             &row.dwLocalAddr, &row.dwLocalPort, &row.dwRemoteAddr,
2010                             &row.dwRemotePort, &row.dwState, &inode ) != 7)
2011                     continue;
2012                 row.dwLocalPort = htons( row.dwLocalPort );
2013                 row.dwRemotePort = htons( row.dwRemotePort );
2014                 row.dwState = TCPStateToMIBState( row.dwState );
2015                 if (class == TCP_TABLE_OWNER_PID_ALL)
2016                     row.dwOwningPid = find_owning_pid( map, num_entries, inode );
2017
2018                 if (!(table = append_tcp_row( class, heap, flags, table, &count, &row, row_size )))
2019                     break;
2020             }
2021             HeapFree( GetProcessHeap(), 0, map );
2022             fclose( fp );
2023         }
2024         else ret = ERROR_NOT_SUPPORTED;
2025     }
2026 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
2027     {
2028         void *data;
2029         int fd, len;
2030         mib2_tcpConnEntry_t *entry;
2031
2032         if ((fd = open_streams_mib( "tcp" )) != -1)
2033         {
2034             if ((data = read_mib_entry( fd, MIB2_TCP, MIB2_TCP_CONN, &len )))
2035             {
2036                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
2037                 {
2038                     row.dwLocalAddr = entry->tcpConnLocalAddress;
2039                     row.dwLocalPort = htons( entry->tcpConnLocalPort );
2040                     row.dwRemoteAddr = entry->tcpConnRemAddress;
2041                     row.dwRemotePort = htons( entry->tcpConnRemPort );
2042                     row.dwState = entry->tcpConnState;
2043                     if (!(table = append_tcp_row( class, heap, flags, table, &count, &row, row_size )))
2044                         break;
2045                 }
2046                 HeapFree( GetProcessHeap(), 0, data );
2047             }
2048             close( fd );
2049         }
2050         else ret = ERROR_NOT_SUPPORTED;
2051     }
2052 #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
2053     {
2054         size_t Len = 0;
2055         char *Buf = NULL;
2056         struct xinpgen *pXIG, *pOrigXIG;
2057
2058         if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0)
2059         {
2060             ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
2061             ret = ERROR_NOT_SUPPORTED;
2062             goto done;
2063         }
2064
2065         Buf = HeapAlloc (GetProcessHeap (), 0, Len);
2066         if (!Buf)
2067         {
2068             ret = ERROR_OUTOFMEMORY;
2069             goto done;
2070         }
2071
2072         if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0)
2073         {
2074             ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
2075             ret = ERROR_NOT_SUPPORTED;
2076             goto done;
2077         }
2078
2079         /* Might be nothing here; first entry is just a header it seems */
2080         if (Len <= sizeof (struct xinpgen)) goto done;
2081
2082         pOrigXIG = (struct xinpgen *)Buf;
2083         pXIG = pOrigXIG;
2084
2085         for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
2086              pXIG->xig_len > sizeof (struct xinpgen);
2087              pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
2088         {
2089             struct tcpcb *pTCPData = NULL;
2090             struct inpcb *pINData;
2091             struct xsocket *pSockData;
2092
2093             pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
2094             pINData = &((struct xtcpcb *)pXIG)->xt_inp;
2095             pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
2096
2097             /* Ignore sockets for other protocols */
2098             if (pSockData->xso_protocol != IPPROTO_TCP)
2099                 continue;
2100
2101             /* Ignore PCBs that were freed while generating the data */
2102             if (pINData->inp_gencnt > pOrigXIG->xig_gen)
2103                 continue;
2104
2105             /* we're only interested in IPv4 addresses */
2106             if (!(pINData->inp_vflag & INP_IPV4) ||
2107                 (pINData->inp_vflag & INP_IPV6))
2108                 continue;
2109
2110             /* If all 0's, skip it */
2111             if (!pINData->inp_laddr.s_addr &&
2112                 !pINData->inp_lport &&
2113                 !pINData->inp_faddr.s_addr &&
2114                 !pINData->inp_fport)
2115                 continue;
2116
2117             /* Fill in structure details */
2118             row.dwLocalAddr = pINData->inp_laddr.s_addr;
2119             row.dwLocalPort = pINData->inp_lport;
2120             row.dwRemoteAddr = pINData->inp_faddr.s_addr;
2121             row.dwRemotePort = pINData->inp_fport;
2122             row.dwState = TCPStateToMIBState (pTCPData->t_state);
2123             if (!(table = append_tcp_row( class, heap, flags, table, &count, &row, row_size )))
2124                 break;
2125         }
2126
2127     done:
2128         HeapFree (GetProcessHeap (), 0, Buf);
2129     }
2130 #else
2131     FIXME( "not implemented\n" );
2132     ret = ERROR_NOT_SUPPORTED;
2133 #endif
2134
2135     if (!table) return ERROR_OUTOFMEMORY;
2136     if (!ret)
2137     {
2138         if (order && table->dwNumEntries)
2139             qsort( table->table, table->dwNumEntries, row_size, compare_tcp_rows );
2140         *tablep = table;
2141     }
2142     else HeapFree( heap, flags, table );
2143     if (size) *size = get_tcp_table_sizes( class, count, NULL );
2144     TRACE( "returning ret %u table %p\n", ret, table );
2145     return ret;
2146 }
2147
2148 /******************************************************************
2149  *    AllocateAndGetTcpTableFromStack (IPHLPAPI.@)
2150  *
2151  * Get the TCP connection table.
2152  * Like GetTcpTable(), but allocate the returned table from heap.
2153  *
2154  * PARAMS
2155  *  ppTcpTable [Out] pointer into which the MIB_TCPTABLE is
2156  *                   allocated and returned.
2157  *  bOrder     [In]  whether to sort the table
2158  *  heap       [In]  heap from which the table is allocated
2159  *  flags      [In]  flags to HeapAlloc
2160  *
2161  * RETURNS
2162  *  ERROR_INVALID_PARAMETER if ppTcpTable is NULL, whatever GetTcpTable()
2163  *  returns otherwise.
2164  */
2165 DWORD WINAPI AllocateAndGetTcpTableFromStack( PMIB_TCPTABLE *ppTcpTable, BOOL bOrder,
2166                                               HANDLE heap, DWORD flags )
2167 {
2168     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppTcpTable, bOrder, heap, flags);
2169
2170     if (!ppTcpTable) return ERROR_INVALID_PARAMETER;
2171     return build_tcp_table( TCP_TABLE_BASIC_ALL, (void **)ppTcpTable, bOrder, heap, flags, NULL );
2172 }
2173
2174 static DWORD get_udp_table_sizes( UDP_TABLE_CLASS class, DWORD row_count, DWORD *row_size )
2175 {
2176     DWORD table_size;
2177
2178     switch (class)
2179     {
2180     case UDP_TABLE_BASIC:
2181     {
2182         table_size = FIELD_OFFSET(MIB_UDPTABLE, table[row_count]);
2183         if (row_size) *row_size = sizeof(MIB_UDPROW);
2184         break;
2185     }
2186     case UDP_TABLE_OWNER_PID:
2187     {
2188         table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_PID, table[row_count]);
2189         if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_PID);
2190         break;
2191     }
2192     case UDP_TABLE_OWNER_MODULE:
2193     {
2194         table_size = FIELD_OFFSET(MIB_UDPTABLE_OWNER_MODULE, table[row_count]);
2195         if (row_size) *row_size = sizeof(MIB_UDPROW_OWNER_MODULE);
2196         break;
2197     }
2198     default:
2199         ERR("unhandled class %u\n", class);
2200         return 0;
2201     }
2202     return table_size;
2203 }
2204
2205 static MIB_UDPTABLE *append_udp_row( UDP_TABLE_CLASS class, HANDLE heap, DWORD flags,
2206                                      MIB_UDPTABLE *table, DWORD *count,
2207                                      const MIB_UDPROW_OWNER_MODULE *row, DWORD row_size )
2208 {
2209     if (table->dwNumEntries >= *count)
2210     {
2211         MIB_UDPTABLE *new_table;
2212         DWORD new_count = table->dwNumEntries * 2, new_table_size;
2213
2214         new_table_size = get_udp_table_sizes( class, new_count, NULL );
2215         if (!(new_table = HeapReAlloc( heap, flags, table, new_table_size )))
2216         {
2217             HeapFree( heap, 0, table );
2218             return NULL;
2219         }
2220         *count = new_count;
2221         table = new_table;
2222     }
2223     memcpy( (char *)table->table + (table->dwNumEntries * row_size), row, row_size );
2224     table->dwNumEntries++;
2225     return table;
2226 }
2227
2228 static int compare_udp_rows(const void *a, const void *b)
2229 {
2230     const MIB_UDPROW *rowA = a;
2231     const MIB_UDPROW *rowB = b;
2232     int ret;
2233
2234     if ((ret = rowA->dwLocalAddr - rowB->dwLocalAddr) != 0) return ret;
2235     return rowA->dwLocalPort - rowB->dwLocalPort;
2236 }
2237
2238 DWORD build_udp_table( UDP_TABLE_CLASS class, void **tablep, BOOL order, HANDLE heap, DWORD flags,
2239                        DWORD *size )
2240 {
2241     MIB_UDPTABLE *table;
2242     MIB_UDPROW_OWNER_MODULE row;
2243     DWORD ret = NO_ERROR, count = 16, table_size, row_size;
2244
2245     if (!(table_size = get_udp_table_sizes( class, count, &row_size )))
2246         return ERROR_INVALID_PARAMETER;
2247
2248     if (!(table = HeapAlloc( heap, flags, table_size )))
2249          return ERROR_OUTOFMEMORY;
2250
2251     table->dwNumEntries = 0;
2252     memset( &row, 0, sizeof(row) );
2253
2254 #ifdef __linux__
2255     {
2256         FILE *fp;
2257
2258         if ((fp = fopen( "/proc/net/udp", "r" )))
2259         {
2260             char buf[512], *ptr;
2261             struct pid_map *map = NULL;
2262             unsigned int dummy, num_entries = 0;
2263             int inode;
2264
2265             if (class == UDP_TABLE_OWNER_PID || class == UDP_TABLE_OWNER_MODULE)
2266                 map = get_pid_map( &num_entries );
2267
2268             /* skip header line */
2269             ptr = fgets( buf, sizeof(buf), fp );
2270             while ((ptr = fgets( buf, sizeof(buf), fp )))
2271             {
2272                 if (sscanf( ptr, "%u: %x:%x %*s %*s %*s %*s %*s %*s %*s %d", &dummy,
2273                     &row.dwLocalAddr, &row.dwLocalPort, &inode ) != 4)
2274                     continue;
2275                 row.dwLocalPort = htons( row.dwLocalPort );
2276                 if (class == UDP_TABLE_OWNER_PID || class == UDP_TABLE_OWNER_MODULE)
2277                     row.dwOwningPid = find_owning_pid( map, num_entries, inode );
2278                 if (!(table = append_udp_row( class, heap, flags, table, &count, &row, row_size )))
2279                     break;
2280             }
2281             HeapFree( GetProcessHeap(), 0, map );
2282             fclose( fp );
2283         }
2284         else ret = ERROR_NOT_SUPPORTED;
2285     }
2286 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
2287     {
2288         void *data;
2289         int fd, len;
2290         mib2_udpEntry_t *entry;
2291
2292         if ((fd = open_streams_mib( "udp" )) != -1)
2293         {
2294             if ((data = read_mib_entry( fd, MIB2_UDP, MIB2_UDP_ENTRY, &len )))
2295             {
2296                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
2297                 {
2298                     row.dwLocalAddr = entry->udpLocalAddress;
2299                     row.dwLocalPort = htons( entry->udpLocalPort );
2300                     if (!(table = append_udp_row( class, heap, flags, table, &count, &row, row_size ))) break;
2301                 }
2302                 HeapFree( GetProcessHeap(), 0, data );
2303             }
2304             close( fd );
2305         }
2306         else ret = ERROR_NOT_SUPPORTED;
2307     }
2308 #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
2309     {
2310         size_t Len = 0;
2311         char *Buf = NULL;
2312         struct xinpgen *pXIG, *pOrigXIG;
2313
2314         if (sysctlbyname ("net.inet.udp.pcblist", NULL, &Len, NULL, 0) < 0)
2315         {
2316             ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n");
2317             ret = ERROR_NOT_SUPPORTED;
2318             goto done;
2319         }
2320
2321         Buf = HeapAlloc (GetProcessHeap (), 0, Len);
2322         if (!Buf)
2323         {
2324             ret = ERROR_OUTOFMEMORY;
2325             goto done;
2326         }
2327
2328         if (sysctlbyname ("net.inet.udp.pcblist", Buf, &Len, NULL, 0) < 0)
2329         {
2330             ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n");
2331             ret = ERROR_NOT_SUPPORTED;
2332             goto done;
2333         }
2334
2335         /* Might be nothing here; first entry is just a header it seems */
2336         if (Len <= sizeof (struct xinpgen)) goto done;
2337
2338         pOrigXIG = (struct xinpgen *)Buf;
2339         pXIG = pOrigXIG;
2340
2341         for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
2342              pXIG->xig_len > sizeof (struct xinpgen);
2343              pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
2344         {
2345             struct inpcb *pINData;
2346             struct xsocket *pSockData;
2347
2348             pINData = &((struct xinpcb *)pXIG)->xi_inp;
2349             pSockData = &((struct xinpcb *)pXIG)->xi_socket;
2350
2351             /* Ignore sockets for other protocols */
2352             if (pSockData->xso_protocol != IPPROTO_UDP)
2353                 continue;
2354
2355             /* Ignore PCBs that were freed while generating the data */
2356             if (pINData->inp_gencnt > pOrigXIG->xig_gen)
2357                 continue;
2358
2359             /* we're only interested in IPv4 addresses */
2360             if (!(pINData->inp_vflag & INP_IPV4) ||
2361                 (pINData->inp_vflag & INP_IPV6))
2362                 continue;
2363
2364             /* If all 0's, skip it */
2365             if (!pINData->inp_laddr.s_addr &&
2366                 !pINData->inp_lport)
2367                 continue;
2368
2369             /* Fill in structure details */
2370             row.dwLocalAddr = pINData->inp_laddr.s_addr;
2371             row.dwLocalPort = pINData->inp_lport;
2372             if (!(table = append_udp_row( class, heap, flags, table, &count, &row, row_size ))) break;
2373         }
2374
2375     done:
2376         HeapFree (GetProcessHeap (), 0, Buf);
2377     }
2378 #else
2379     FIXME( "not implemented\n" );
2380     ret = ERROR_NOT_SUPPORTED;
2381 #endif
2382
2383     if (!table) return ERROR_OUTOFMEMORY;
2384     if (!ret)
2385     {
2386         if (order && table->dwNumEntries)
2387             qsort( table->table, table->dwNumEntries, row_size, compare_udp_rows );
2388         *tablep = table;
2389     }
2390     else HeapFree( heap, flags, table );
2391     if (size) *size = get_udp_table_sizes( class, count, NULL );
2392     TRACE( "returning ret %u table %p\n", ret, table );
2393     return ret;
2394 }
2395
2396 /******************************************************************
2397  *    AllocateAndGetUdpTableFromStack (IPHLPAPI.@)
2398  *
2399  * Get the UDP listener table.
2400  * Like GetUdpTable(), but allocate the returned table from heap.
2401  *
2402  * PARAMS
2403  *  ppUdpTable [Out] pointer into which the MIB_UDPTABLE is
2404  *                   allocated and returned.
2405  *  bOrder     [In]  whether to sort the table
2406  *  heap       [In]  heap from which the table is allocated
2407  *  flags      [In]  flags to HeapAlloc
2408  *
2409  * RETURNS
2410  *  ERROR_INVALID_PARAMETER if ppUdpTable is NULL, whatever GetUdpTable()
2411  *  returns otherwise.
2412  */
2413 DWORD WINAPI AllocateAndGetUdpTableFromStack(PMIB_UDPTABLE *ppUdpTable, BOOL bOrder,
2414                                              HANDLE heap, DWORD flags)
2415 {
2416     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppUdpTable, bOrder, heap, flags);
2417
2418     if (!ppUdpTable) return ERROR_INVALID_PARAMETER;
2419     return build_udp_table( UDP_TABLE_BASIC, (void **)ppUdpTable, bOrder, heap, flags, NULL );
2420 }