include: Update the nldef.h and *mib.h headers.
[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_ALIAS_H
31 #include <alias.h>
32 #endif
33 #ifdef HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
35 #endif
36 #ifdef HAVE_SYS_SOCKETVAR_H
37 #include <sys/socketvar.h>
38 #endif
39 #ifdef HAVE_SYS_TIMEOUT_H
40 #include <sys/timeout.h>
41 #endif
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
45 #ifdef HAVE_NETINET_IN_SYSTM_H
46 #include <netinet/in_systm.h>
47 #endif
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
50 #endif
51 #ifdef HAVE_NET_IF_H
52 #include <net/if.h>
53 #endif
54 #ifdef HAVE_NET_IF_DL_H
55 #include <net/if_dl.h>
56 #endif
57 #ifdef HAVE_NET_IF_TYPES_H
58 #include <net/if_types.h>
59 #endif
60 #ifdef HAVE_NET_ROUTE_H
61 #include <net/route.h>
62 #endif
63 #ifdef HAVE_NET_IF_ARP_H
64 #include <net/if_arp.h>
65 #endif
66 #ifdef HAVE_NETINET_IF_ETHER_H
67 #include <netinet/if_ether.h>
68 #endif
69 #ifdef HAVE_NETINET_IF_INARP_H
70 #include <netinet/if_inarp.h>
71 #endif
72 #ifdef HAVE_NETINET_IP_H
73 #include <netinet/ip.h>
74 #endif
75 #ifdef HAVE_NETINET_TCP_H
76 #include <netinet/tcp.h>
77 #endif
78 #ifdef HAVE_NETINET_IP_VAR_H
79 #include <netinet/ip_var.h>
80 #endif
81 #ifdef HAVE_NETINET_TCP_FSM_H
82 #include <netinet/tcp_fsm.h>
83 #endif
84 #ifdef HAVE_NETINET_IN_PCB_H
85 #include <netinet/in_pcb.h>
86 #endif
87 #ifdef HAVE_NETINET_TCP_TIMER_H
88 #include <netinet/tcp_timer.h>
89 #endif
90 #ifdef HAVE_NETINET_TCP_VAR_H
91 #include <netinet/tcp_var.h>
92 #endif
93 #ifdef HAVE_NETINET_IP_ICMP_H
94 #include <netinet/ip_icmp.h>
95 #endif
96 #ifdef HAVE_NETINET_ICMP_VAR_H
97 #include <netinet/icmp_var.h>
98 #endif
99 #ifdef HAVE_NETINET_UDP_H
100 #include <netinet/udp.h>
101 #endif
102 #ifdef HAVE_NETINET_UDP_VAR_H
103 #include <netinet/udp_var.h>
104 #endif
105 #ifdef HAVE_SYS_PROTOSW_H
106 #include <sys/protosw.h>
107 #endif
108 #ifdef HAVE_SYS_SYSCTL_H
109 #include <sys/sysctl.h>
110 #endif
111 #ifdef HAVE_KSTAT_H
112 #include <kstat.h>
113 #endif
114 #ifdef HAVE_INET_MIB2_H
115 #include <inet/mib2.h>
116 #endif
117 #ifdef HAVE_STROPTS_H
118 #include <stropts.h>
119 #endif
120 #ifdef HAVE_SYS_TIHDR_H
121 #include <sys/tihdr.h>
122 #endif
123
124 #ifndef ROUNDUP
125 #define ROUNDUP(a) \
126         ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
127 #endif
128 #ifndef ADVANCE
129 #define ADVANCE(x, n) (x += ROUNDUP(((struct sockaddr *)n)->sa_len))
130 #endif
131
132 #define NONAMELESSUNION
133 #include "ifenum.h"
134 #include "ipstats.h"
135
136 #include "wine/debug.h"
137
138 #ifndef HAVE_NETINET_TCP_FSM_H
139 #define TCPS_ESTABLISHED  1
140 #define TCPS_SYN_SENT     2
141 #define TCPS_SYN_RECEIVED 3
142 #define TCPS_FIN_WAIT_1   4
143 #define TCPS_FIN_WAIT_2   5
144 #define TCPS_TIME_WAIT    6
145 #define TCPS_CLOSED       7
146 #define TCPS_CLOSE_WAIT   8
147 #define TCPS_LAST_ACK     9
148 #define TCPS_LISTEN      10
149 #define TCPS_CLOSING     11
150 #endif
151
152 #ifndef RTF_MULTICAST
153 #define RTF_MULTICAST 0 /* Not available on NetBSD/OpenBSD */
154 #endif
155
156 #ifndef RTF_LLINFO
157 #define RTF_LLINFO 0 /* Not available on FreeBSD 8 and above */
158 #endif
159
160 WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
161
162 #ifdef HAVE_LIBKSTAT
163 static DWORD kstat_get_ui32( kstat_t *ksp, const char *name )
164 {
165     unsigned int i;
166     kstat_named_t *data = ksp->ks_data;
167
168     for (i = 0; i < ksp->ks_ndata; i++)
169         if (!strcmp( data[i].name, name )) return data[i].value.ui32;
170     return 0;
171 }
172
173 static ULONGLONG kstat_get_ui64( kstat_t *ksp, const char *name )
174 {
175     unsigned int i;
176     kstat_named_t *data = ksp->ks_data;
177
178     for (i = 0; i < ksp->ks_ndata; i++)
179         if (!strcmp( data[i].name, name )) return data[i].value.ui64;
180     return 0;
181 }
182 #endif
183
184 #if defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
185 static int open_streams_mib( const char *proto )
186 {
187     int fd;
188     struct strbuf buf;
189     struct request
190     {
191         struct T_optmgmt_req req_header;
192         struct opthdr        opt_header;
193     } request;
194
195     if ((fd = open( "/dev/arp", O_RDWR )) == -1)
196     {
197         WARN( "could not open /dev/arp: %s\n", strerror(errno) );
198         return -1;
199     }
200     if (proto) ioctl( fd, I_PUSH, proto );
201
202     request.req_header.PRIM_type  = T_SVR4_OPTMGMT_REQ;
203     request.req_header.OPT_length = sizeof(request.opt_header);
204     request.req_header.OPT_offset = FIELD_OFFSET( struct request, opt_header );
205     request.req_header.MGMT_flags = T_CURRENT;
206     request.opt_header.level      = MIB2_IP;
207     request.opt_header.name       = 0;
208     request.opt_header.len        = 0;
209
210     buf.len = sizeof(request);
211     buf.buf = (caddr_t)&request;
212     if (putmsg( fd, &buf, NULL, 0 ) == -1)
213     {
214         WARN( "putmsg: %s\n", strerror(errno) );
215         close( fd );
216         fd = -1;
217     }
218     return fd;
219 }
220
221 static void *read_mib_entry( int fd, int level, int name, int *len )
222 {
223     struct strbuf buf;
224     void *data;
225     int ret, flags = 0;
226
227     struct reply
228     {
229         struct T_optmgmt_ack ack_header;
230         struct opthdr        opt_header;
231     } reply;
232
233     for (;;)
234     {
235         buf.maxlen = sizeof(reply);
236         buf.buf = (caddr_t)&reply;
237         if ((ret = getmsg( fd, &buf, NULL, &flags )) < 0) return NULL;
238         if (!(ret & MOREDATA)) return NULL;
239         if (reply.ack_header.PRIM_type != T_OPTMGMT_ACK) return NULL;
240         if (buf.len < sizeof(reply.ack_header)) return NULL;
241         if (reply.ack_header.OPT_length < sizeof(reply.opt_header)) return NULL;
242
243         if (!(data = HeapAlloc( GetProcessHeap(), 0, reply.opt_header.len ))) return NULL;
244         buf.maxlen = reply.opt_header.len;
245         buf.buf = (caddr_t)data;
246         flags = 0;
247         if (getmsg( fd, NULL, &buf, &flags ) >= 0 &&
248             reply.opt_header.level == level &&
249             reply.opt_header.name == name)
250         {
251             *len = buf.len;
252             return data;
253         }
254         HeapFree( GetProcessHeap(), 0, data );
255     }
256 }
257 #endif /* HAVE_SYS_TIHDR_H && T_OPTMGMT_ACK */
258
259 DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry)
260 {
261     DWORD ret = ERROR_NOT_SUPPORTED;
262
263     if (!name || !entry) return ERROR_INVALID_PARAMETER;
264
265 #ifdef __linux__
266     {
267         FILE *fp;
268
269         if ((fp = fopen("/proc/net/dev", "r")))
270         {
271             DWORD skip;
272             char buf[512], *ptr;
273             int nameLen = strlen(name);
274
275             while ((ptr = fgets(buf, sizeof(buf), fp)))
276             {
277                 while (*ptr && isspace(*ptr)) ptr++;
278                 if (strncasecmp(ptr, name, nameLen) == 0 && *(ptr + nameLen) == ':')
279                 {
280                     ptr += nameLen + 1;
281                     sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u",
282                             &entry->dwInOctets, &entry->dwInUcastPkts,
283                             &entry->dwInErrors, &entry->dwInDiscards,
284                             &skip, &skip, &skip,
285                             &entry->dwInNUcastPkts, &entry->dwOutOctets,
286                             &entry->dwOutUcastPkts, &entry->dwOutErrors,
287                             &entry->dwOutDiscards );
288                     break;
289                 }
290             }
291             fclose(fp);
292             ret = NO_ERROR;
293         }
294     }
295 #elif defined(HAVE_LIBKSTAT)
296     {
297         kstat_ctl_t *kc;
298         kstat_t *ksp;
299
300         if ((kc = kstat_open()) &&
301             (ksp = kstat_lookup( kc, NULL, -1, (char *)name )) &&
302             kstat_read( kc, ksp, NULL ) != -1 &&
303             ksp->ks_type == KSTAT_TYPE_NAMED)
304         {
305             entry->dwMtu             = 1500;  /* FIXME */
306             entry->dwSpeed           = min( kstat_get_ui64( ksp, "ifspeed" ), ~0u );
307             entry->dwInOctets        = kstat_get_ui32( ksp, "rbytes" );
308             entry->dwInNUcastPkts    = kstat_get_ui32( ksp, "multircv" );
309             entry->dwInNUcastPkts   += kstat_get_ui32( ksp, "brdcstrcv" );
310             entry->dwInUcastPkts     = kstat_get_ui32( ksp, "ipackets" ) - entry->dwInNUcastPkts;
311             entry->dwInDiscards      = kstat_get_ui32( ksp, "norcvbuf" );
312             entry->dwInErrors        = kstat_get_ui32( ksp, "ierrors" );
313             entry->dwInUnknownProtos = kstat_get_ui32( ksp, "unknowns" );
314             entry->dwOutOctets       = kstat_get_ui32( ksp, "obytes" );
315             entry->dwOutNUcastPkts   = kstat_get_ui32( ksp, "multixmt" );
316             entry->dwOutNUcastPkts  += kstat_get_ui32( ksp, "brdcstxmt" );
317             entry->dwOutUcastPkts    = kstat_get_ui32( ksp, "opackets" ) - entry->dwOutNUcastPkts;
318             entry->dwOutDiscards     = 0;  /* FIXME */
319             entry->dwOutErrors       = kstat_get_ui32( ksp, "oerrors" );
320             entry->dwOutQLen         = kstat_get_ui32( ksp, "noxmtbuf" );
321             ret = NO_ERROR;
322         }
323         if (kc) kstat_close( kc );
324     }
325 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST)
326     {
327         int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, if_nametoindex(name)};
328 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
329
330         size_t needed;
331         char *buf = NULL, *end;
332         struct if_msghdr *ifm;
333         struct if_data ifdata;
334
335         if(sysctl(mib, MIB_LEN, NULL, &needed, NULL, 0) == -1)
336         {
337             ERR ("failed to get size of iflist\n");
338             goto done;
339         }
340         buf = HeapAlloc (GetProcessHeap (), 0, needed);
341         if (!buf)
342         {
343             ret = ERROR_OUTOFMEMORY;
344             goto done;
345         }
346         if(sysctl(mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
347         {
348             ERR ("failed to get iflist\n");
349             goto done;
350         }
351         for ( end = buf + needed; buf < end; buf += ifm->ifm_msglen)
352         {
353             ifm = (struct if_msghdr *) buf;
354             if(ifm->ifm_type == RTM_IFINFO)
355             {
356                 ifdata = ifm->ifm_data;
357                 entry->dwMtu = ifdata.ifi_mtu;
358                 entry->dwSpeed = ifdata.ifi_baudrate;
359                 entry->dwInOctets = ifdata.ifi_ibytes;
360                 entry->dwInErrors = ifdata.ifi_ierrors;
361                 entry->dwInDiscards = ifdata.ifi_iqdrops;
362                 entry->dwInUcastPkts = ifdata.ifi_ipackets;
363                 entry->dwInNUcastPkts = ifdata.ifi_imcasts;
364                 entry->dwOutOctets = ifdata.ifi_obytes;
365                 entry->dwOutUcastPkts = ifdata.ifi_opackets;
366                 entry->dwOutErrors = ifdata.ifi_oerrors;
367                 ret = NO_ERROR;
368                 break;
369             }
370         }
371     done:
372         HeapFree (GetProcessHeap (), 0, buf);
373     }
374 #else
375     FIXME( "unimplemented\n" );
376 #endif
377     return ret;
378 }
379
380
381 /******************************************************************
382  *    GetIcmpStatistics (IPHLPAPI.@)
383  *
384  * Get the ICMP statistics for the local computer.
385  *
386  * PARAMS
387  *  stats [Out] buffer for ICMP statistics
388  *
389  * RETURNS
390  *  Success: NO_ERROR
391  *  Failure: error code from winerror.h
392  */
393 DWORD WINAPI GetIcmpStatistics(PMIB_ICMP stats)
394 {
395     DWORD ret = ERROR_NOT_SUPPORTED;
396
397     if (!stats) return ERROR_INVALID_PARAMETER;
398     memset( stats, 0, sizeof(MIB_ICMP) );
399
400 #ifdef __linux__
401     {
402         FILE *fp;
403
404         if ((fp = fopen("/proc/net/snmp", "r")))
405         {
406             static const char hdr[] = "Icmp:";
407             char buf[512], *ptr;
408
409             while ((ptr = fgets(buf, sizeof(buf), fp)))
410             {
411                 if (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue;
412                 /* last line was a header, get another */
413                 if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
414                 if (!strncasecmp(buf, hdr, sizeof(hdr) - 1))
415                 {
416                     ptr += sizeof(hdr);
417                     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",
418                             &stats->stats.icmpInStats.dwMsgs,
419                             &stats->stats.icmpInStats.dwErrors,
420                             &stats->stats.icmpInStats.dwDestUnreachs,
421                             &stats->stats.icmpInStats.dwTimeExcds,
422                             &stats->stats.icmpInStats.dwParmProbs,
423                             &stats->stats.icmpInStats.dwSrcQuenchs,
424                             &stats->stats.icmpInStats.dwRedirects,
425                             &stats->stats.icmpInStats.dwEchoReps,
426                             &stats->stats.icmpInStats.dwTimestamps,
427                             &stats->stats.icmpInStats.dwTimestampReps,
428                             &stats->stats.icmpInStats.dwAddrMasks,
429                             &stats->stats.icmpInStats.dwAddrMaskReps,
430                             &stats->stats.icmpOutStats.dwMsgs,
431                             &stats->stats.icmpOutStats.dwErrors,
432                             &stats->stats.icmpOutStats.dwDestUnreachs,
433                             &stats->stats.icmpOutStats.dwTimeExcds,
434                             &stats->stats.icmpOutStats.dwParmProbs,
435                             &stats->stats.icmpOutStats.dwSrcQuenchs,
436                             &stats->stats.icmpOutStats.dwRedirects,
437                             &stats->stats.icmpOutStats.dwEchoReps,
438                             &stats->stats.icmpOutStats.dwTimestamps,
439                             &stats->stats.icmpOutStats.dwTimestampReps,
440                             &stats->stats.icmpOutStats.dwAddrMasks,
441                             &stats->stats.icmpOutStats.dwAddrMaskReps );
442                     break;
443                 }
444             }
445             fclose(fp);
446             ret = NO_ERROR;
447         }
448     }
449 #elif defined(HAVE_LIBKSTAT)
450     {
451         static char ip[] = "ip", icmp[] = "icmp";
452         kstat_ctl_t *kc;
453         kstat_t *ksp;
454
455         if ((kc = kstat_open()) &&
456             (ksp = kstat_lookup( kc, ip, 0, icmp )) &&
457             kstat_read( kc, ksp, NULL ) != -1 &&
458             ksp->ks_type == KSTAT_TYPE_NAMED)
459         {
460             stats->stats.icmpInStats.dwMsgs           = kstat_get_ui32( ksp, "inMsgs" );
461             stats->stats.icmpInStats.dwErrors         = kstat_get_ui32( ksp, "inErrors" );
462             stats->stats.icmpInStats.dwDestUnreachs   = kstat_get_ui32( ksp, "inDestUnreachs" );
463             stats->stats.icmpInStats.dwTimeExcds      = kstat_get_ui32( ksp, "inTimeExcds" );
464             stats->stats.icmpInStats.dwParmProbs      = kstat_get_ui32( ksp, "inParmProbs" );
465             stats->stats.icmpInStats.dwSrcQuenchs     = kstat_get_ui32( ksp, "inSrcQuenchs" );
466             stats->stats.icmpInStats.dwRedirects      = kstat_get_ui32( ksp, "inRedirects" );
467             stats->stats.icmpInStats.dwEchos          = kstat_get_ui32( ksp, "inEchos" );
468             stats->stats.icmpInStats.dwEchoReps       = kstat_get_ui32( ksp, "inEchoReps" );
469             stats->stats.icmpInStats.dwTimestamps     = kstat_get_ui32( ksp, "inTimestamps" );
470             stats->stats.icmpInStats.dwTimestampReps  = kstat_get_ui32( ksp, "inTimestampReps" );
471             stats->stats.icmpInStats.dwAddrMasks      = kstat_get_ui32( ksp, "inAddrMasks" );
472             stats->stats.icmpInStats.dwAddrMaskReps   = kstat_get_ui32( ksp, "inAddrMaskReps" );
473             stats->stats.icmpOutStats.dwMsgs          = kstat_get_ui32( ksp, "outMsgs" );
474             stats->stats.icmpOutStats.dwErrors        = kstat_get_ui32( ksp, "outErrors" );
475             stats->stats.icmpOutStats.dwDestUnreachs  = kstat_get_ui32( ksp, "outDestUnreachs" );
476             stats->stats.icmpOutStats.dwTimeExcds     = kstat_get_ui32( ksp, "outTimeExcds" );
477             stats->stats.icmpOutStats.dwParmProbs     = kstat_get_ui32( ksp, "outParmProbs" );
478             stats->stats.icmpOutStats.dwSrcQuenchs    = kstat_get_ui32( ksp, "outSrcQuenchs" );
479             stats->stats.icmpOutStats.dwRedirects     = kstat_get_ui32( ksp, "outRedirects" );
480             stats->stats.icmpOutStats.dwEchos         = kstat_get_ui32( ksp, "outEchos" );
481             stats->stats.icmpOutStats.dwEchoReps      = kstat_get_ui32( ksp, "outEchoReps" );
482             stats->stats.icmpOutStats.dwTimestamps    = kstat_get_ui32( ksp, "outTimestamps" );
483             stats->stats.icmpOutStats.dwTimestampReps = kstat_get_ui32( ksp, "outTimestampReps" );
484             stats->stats.icmpOutStats.dwAddrMasks     = kstat_get_ui32( ksp, "outAddrMasks" );
485             stats->stats.icmpOutStats.dwAddrMaskReps  = kstat_get_ui32( ksp, "outAddrMaskReps" );
486             ret = NO_ERROR;
487         }
488         if (kc) kstat_close( kc );
489     }
490 #elif defined(HAVE_SYS_SYSCTL_H) && defined(ICMPCTL_STATS)
491     {
492         int mib[] = {CTL_NET, PF_INET, IPPROTO_ICMP, ICMPCTL_STATS};
493 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
494         struct icmpstat icmp_stat;
495         size_t needed  = sizeof(icmp_stat);
496         int i;
497
498         if(sysctl(mib, MIB_LEN, &icmp_stat, &needed, NULL, 0) != -1)
499         {
500             /*in stats */
501             stats->stats.icmpInStats.dwMsgs = icmp_stat.icps_badcode + icmp_stat.icps_checksum + icmp_stat.icps_tooshort + icmp_stat.icps_badlen;
502             for(i = 0; i <= ICMP_MAXTYPE; i++)
503                 stats->stats.icmpInStats.dwMsgs += icmp_stat.icps_inhist[i];
504
505             stats->stats.icmpInStats.dwErrors = icmp_stat.icps_badcode + icmp_stat.icps_tooshort + icmp_stat.icps_checksum + icmp_stat.icps_badlen;
506
507             stats->stats.icmpInStats.dwDestUnreachs = icmp_stat.icps_inhist[ICMP_UNREACH];
508             stats->stats.icmpInStats.dwTimeExcds = icmp_stat.icps_inhist[ICMP_TIMXCEED];
509             stats->stats.icmpInStats.dwParmProbs = icmp_stat.icps_inhist[ICMP_PARAMPROB];
510             stats->stats.icmpInStats.dwSrcQuenchs = icmp_stat.icps_inhist[ICMP_SOURCEQUENCH];
511             stats->stats.icmpInStats.dwRedirects = icmp_stat.icps_inhist[ICMP_REDIRECT];
512             stats->stats.icmpInStats.dwEchos = icmp_stat.icps_inhist[ICMP_ECHO];
513             stats->stats.icmpInStats.dwEchoReps = icmp_stat.icps_inhist[ICMP_ECHOREPLY];
514             stats->stats.icmpInStats.dwTimestamps = icmp_stat.icps_inhist[ICMP_TSTAMP];
515             stats->stats.icmpInStats.dwTimestampReps = icmp_stat.icps_inhist[ICMP_TSTAMPREPLY];
516             stats->stats.icmpInStats.dwAddrMasks = icmp_stat.icps_inhist[ICMP_MASKREQ];
517             stats->stats.icmpInStats.dwAddrMaskReps = icmp_stat.icps_inhist[ICMP_MASKREPLY];
518
519 #ifdef HAVE_ICPS_OUTHIST
520             /* out stats */
521             stats->stats.icmpOutStats.dwMsgs = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;
522             for(i = 0; i <= ICMP_MAXTYPE; i++)
523                 stats->stats.icmpOutStats.dwMsgs += icmp_stat.icps_outhist[i];
524
525             stats->stats.icmpOutStats.dwErrors = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;
526
527             stats->stats.icmpOutStats.dwDestUnreachs = icmp_stat.icps_outhist[ICMP_UNREACH];
528             stats->stats.icmpOutStats.dwTimeExcds = icmp_stat.icps_outhist[ICMP_TIMXCEED];
529             stats->stats.icmpOutStats.dwParmProbs = icmp_stat.icps_outhist[ICMP_PARAMPROB];
530             stats->stats.icmpOutStats.dwSrcQuenchs = icmp_stat.icps_outhist[ICMP_SOURCEQUENCH];
531             stats->stats.icmpOutStats.dwRedirects = icmp_stat.icps_outhist[ICMP_REDIRECT];
532             stats->stats.icmpOutStats.dwEchos = icmp_stat.icps_outhist[ICMP_ECHO];
533             stats->stats.icmpOutStats.dwEchoReps = icmp_stat.icps_outhist[ICMP_ECHOREPLY];
534             stats->stats.icmpOutStats.dwTimestamps = icmp_stat.icps_outhist[ICMP_TSTAMP];
535             stats->stats.icmpOutStats.dwTimestampReps = icmp_stat.icps_outhist[ICMP_TSTAMPREPLY];
536             stats->stats.icmpOutStats.dwAddrMasks = icmp_stat.icps_outhist[ICMP_MASKREQ];
537             stats->stats.icmpOutStats.dwAddrMaskReps = icmp_stat.icps_outhist[ICMP_MASKREPLY];
538 #endif /* ICPS_OUTHIST */
539             ret = NO_ERROR;
540         }
541     }
542 #else /* ICMPCTL_STATS */
543     FIXME( "unimplemented\n" );
544 #endif
545     return ret;
546 }
547
548
549 /******************************************************************
550  *    GetIpStatistics (IPHLPAPI.@)
551  *
552  * Get the IP statistics for the local computer.
553  *
554  * PARAMS
555  *  stats [Out] buffer for IP statistics
556  *
557  * RETURNS
558  *  Success: NO_ERROR
559  *  Failure: error code from winerror.h
560  */
561 DWORD WINAPI GetIpStatistics(PMIB_IPSTATS stats)
562 {
563     DWORD ret = ERROR_NOT_SUPPORTED;
564     MIB_IPFORWARDTABLE *fwd_table;
565
566     if (!stats) return ERROR_INVALID_PARAMETER;
567     memset( stats, 0, sizeof(*stats) );
568
569     stats->dwNumIf = stats->dwNumAddr = getNumInterfaces();
570     if (!AllocateAndGetIpForwardTableFromStack( &fwd_table, FALSE, GetProcessHeap(), 0 ))
571     {
572         stats->dwNumRoutes = fwd_table->dwNumEntries;
573         HeapFree( GetProcessHeap(), 0, fwd_table );
574     }
575
576 #ifdef __linux__
577     {
578         FILE *fp;
579
580         if ((fp = fopen("/proc/net/snmp", "r")))
581         {
582             static const char hdr[] = "Ip:";
583             char buf[512], *ptr;
584
585             while ((ptr = fgets(buf, sizeof(buf), fp)))
586             {
587                 if (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue;
588                 /* last line was a header, get another */
589                 if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
590                 if (!strncasecmp(buf, hdr, sizeof(hdr) - 1))
591                 {
592                     ptr += sizeof(hdr);
593                     sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u",
594                             &stats->u.dwForwarding,
595                             &stats->dwDefaultTTL,
596                             &stats->dwInReceives,
597                             &stats->dwInHdrErrors,
598                             &stats->dwInAddrErrors,
599                             &stats->dwForwDatagrams,
600                             &stats->dwInUnknownProtos,
601                             &stats->dwInDiscards,
602                             &stats->dwInDelivers,
603                             &stats->dwOutRequests,
604                             &stats->dwOutDiscards,
605                             &stats->dwOutNoRoutes,
606                             &stats->dwReasmTimeout,
607                             &stats->dwReasmReqds,
608                             &stats->dwReasmOks,
609                             &stats->dwReasmFails,
610                             &stats->dwFragOks,
611                             &stats->dwFragFails,
612                             &stats->dwFragCreates );
613                     /* hmm, no routingDiscards */
614                     break;
615                 }
616             }
617             fclose(fp);
618             ret = NO_ERROR;
619         }
620     }
621 #elif defined(HAVE_LIBKSTAT)
622     {
623         static char ip[] = "ip";
624         kstat_ctl_t *kc;
625         kstat_t *ksp;
626
627         if ((kc = kstat_open()) &&
628             (ksp = kstat_lookup( kc, ip, 0, ip )) &&
629             kstat_read( kc, ksp, NULL ) != -1 &&
630             ksp->ks_type == KSTAT_TYPE_NAMED)
631         {
632             stats->u.dwForwarding    = kstat_get_ui32( ksp, "forwarding" );
633             stats->dwDefaultTTL      = kstat_get_ui32( ksp, "defaultTTL" );
634             stats->dwInReceives      = kstat_get_ui32( ksp, "inReceives" );
635             stats->dwInHdrErrors     = kstat_get_ui32( ksp, "inHdrErrors" );
636             stats->dwInAddrErrors    = kstat_get_ui32( ksp, "inAddrErrors" );
637             stats->dwForwDatagrams   = kstat_get_ui32( ksp, "forwDatagrams" );
638             stats->dwInUnknownProtos = kstat_get_ui32( ksp, "inUnknownProtos" );
639             stats->dwInDiscards      = kstat_get_ui32( ksp, "inDiscards" );
640             stats->dwInDelivers      = kstat_get_ui32( ksp, "inDelivers" );
641             stats->dwOutRequests     = kstat_get_ui32( ksp, "outRequests" );
642             stats->dwRoutingDiscards = kstat_get_ui32( ksp, "routingDiscards" );
643             stats->dwOutDiscards     = kstat_get_ui32( ksp, "outDiscards" );
644             stats->dwOutNoRoutes     = kstat_get_ui32( ksp, "outNoRoutes" );
645             stats->dwReasmTimeout    = kstat_get_ui32( ksp, "reasmTimeout" );
646             stats->dwReasmReqds      = kstat_get_ui32( ksp, "reasmReqds" );
647             stats->dwReasmOks        = kstat_get_ui32( ksp, "reasmOKs" );
648             stats->dwReasmFails      = kstat_get_ui32( ksp, "reasmFails" );
649             stats->dwFragOks         = kstat_get_ui32( ksp, "fragOKs" );
650             stats->dwFragFails       = kstat_get_ui32( ksp, "fragFails" );
651             stats->dwFragCreates     = kstat_get_ui32( ksp, "fragCreates" );
652             ret = NO_ERROR;
653         }
654         if (kc) kstat_close( kc );
655     }
656 #elif defined(HAVE_SYS_SYSCTL_H) && defined(IPCTL_STATS)
657     {
658         int mib[] = {CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS};
659 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
660         int ip_ttl, ip_forwarding;
661         struct ipstat ip_stat;
662         size_t needed;
663
664         needed = sizeof(ip_stat);
665         if(sysctl(mib, MIB_LEN, &ip_stat, &needed, NULL, 0) == -1)
666         {
667             ERR ("failed to get ipstat\n");
668             return ERROR_NOT_SUPPORTED;
669         }
670
671         needed = sizeof(ip_ttl);
672         if (sysctlbyname ("net.inet.ip.ttl", &ip_ttl, &needed, NULL, 0) == -1)
673         {
674             ERR ("failed to get ip Default TTL\n");
675             return ERROR_NOT_SUPPORTED;
676         }
677
678         needed = sizeof(ip_forwarding);
679         if (sysctlbyname ("net.inet.ip.forwarding", &ip_forwarding, &needed, NULL, 0) == -1)
680         {
681             ERR ("failed to get ip forwarding\n");
682             return ERROR_NOT_SUPPORTED;
683         }
684
685         stats->u.dwForwarding = ip_forwarding;
686         stats->dwDefaultTTL = ip_ttl;
687         stats->dwInDelivers = ip_stat.ips_delivered;
688         stats->dwInHdrErrors = ip_stat.ips_badhlen + ip_stat.ips_badsum + ip_stat.ips_tooshort + ip_stat.ips_badlen;
689         stats->dwInAddrErrors = ip_stat.ips_cantforward;
690         stats->dwInReceives = ip_stat.ips_total;
691         stats->dwForwDatagrams = ip_stat.ips_forward;
692         stats->dwInUnknownProtos = ip_stat.ips_noproto;
693         stats->dwInDiscards = ip_stat.ips_fragdropped;
694         stats->dwOutDiscards = ip_stat.ips_odropped;
695         stats->dwReasmOks = ip_stat.ips_reassembled;
696         stats->dwFragOks = ip_stat.ips_fragmented;
697         stats->dwFragFails = ip_stat.ips_cantfrag;
698         stats->dwReasmTimeout = ip_stat.ips_fragtimeout;
699         stats->dwOutNoRoutes = ip_stat.ips_noroute;
700         stats->dwOutRequests = ip_stat.ips_localout;
701         stats->dwReasmReqds = ip_stat.ips_fragments;
702         ret = NO_ERROR;
703     }
704 #else
705     FIXME( "unimplemented\n" );
706 #endif
707     return ret;
708 }
709
710
711 /******************************************************************
712  *    GetTcpStatistics (IPHLPAPI.@)
713  *
714  * Get the TCP statistics for the local computer.
715  *
716  * PARAMS
717  *  stats [Out] buffer for TCP statistics
718  *
719  * RETURNS
720  *  Success: NO_ERROR
721  *  Failure: error code from winerror.h
722  */
723 DWORD WINAPI GetTcpStatistics(PMIB_TCPSTATS stats)
724 {
725     DWORD ret = ERROR_NOT_SUPPORTED;
726
727     if (!stats) return ERROR_INVALID_PARAMETER;
728     memset( stats, 0, sizeof(*stats) );
729
730 #ifdef __linux__
731     {
732         FILE *fp;
733
734         if ((fp = fopen("/proc/net/snmp", "r")))
735         {
736             static const char hdr[] = "Tcp:";
737             MIB_TCPTABLE *tcp_table;
738             char buf[512], *ptr;
739
740             while ((ptr = fgets(buf, sizeof(buf), fp)))
741             {
742                 if (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue;
743                 /* last line was a header, get another */
744                 if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
745                 if (!strncasecmp(buf, hdr, sizeof(hdr) - 1))
746                 {
747                     ptr += sizeof(hdr);
748                     sscanf( ptr, "%u %u %u %u %u %u %u %u %u %u %u %u %u %u",
749                             &stats->u.dwRtoAlgorithm,
750                             &stats->dwRtoMin,
751                             &stats->dwRtoMax,
752                             &stats->dwMaxConn,
753                             &stats->dwActiveOpens,
754                             &stats->dwPassiveOpens,
755                             &stats->dwAttemptFails,
756                             &stats->dwEstabResets,
757                             &stats->dwCurrEstab,
758                             &stats->dwInSegs,
759                             &stats->dwOutSegs,
760                             &stats->dwRetransSegs,
761                             &stats->dwInErrs,
762                             &stats->dwOutRsts );
763                     break;
764                 }
765             }
766             if (!AllocateAndGetTcpTableFromStack( &tcp_table, FALSE, GetProcessHeap(), 0 ))
767             {
768                 stats->dwNumConns = tcp_table->dwNumEntries;
769                 HeapFree( GetProcessHeap(), 0, tcp_table );
770             }
771             fclose(fp);
772             ret = NO_ERROR;
773         }
774     }
775 #elif defined(HAVE_LIBKSTAT)
776     {
777         static char tcp[] = "tcp";
778         kstat_ctl_t *kc;
779         kstat_t *ksp;
780
781         if ((kc = kstat_open()) &&
782             (ksp = kstat_lookup( kc, tcp, 0, tcp )) &&
783             kstat_read( kc, ksp, NULL ) != -1 &&
784             ksp->ks_type == KSTAT_TYPE_NAMED)
785         {
786             stats->u.dwRtoAlgorithm = kstat_get_ui32( ksp, "rtoAlgorithm" );
787             stats->dwRtoMin       = kstat_get_ui32( ksp, "rtoMin" );
788             stats->dwRtoMax       = kstat_get_ui32( ksp, "rtoMax" );
789             stats->dwMaxConn      = kstat_get_ui32( ksp, "maxConn" );
790             stats->dwActiveOpens  = kstat_get_ui32( ksp, "activeOpens" );
791             stats->dwPassiveOpens = kstat_get_ui32( ksp, "passiveOpens" );
792             stats->dwAttemptFails = kstat_get_ui32( ksp, "attemptFails" );
793             stats->dwEstabResets  = kstat_get_ui32( ksp, "estabResets" );
794             stats->dwCurrEstab    = kstat_get_ui32( ksp, "currEstab" );
795             stats->dwInSegs       = kstat_get_ui32( ksp, "inSegs" );
796             stats->dwOutSegs      = kstat_get_ui32( ksp, "outSegs" );
797             stats->dwRetransSegs  = kstat_get_ui32( ksp, "retransSegs" );
798             stats->dwInErrs       = kstat_get_ui32( ksp, "inErrs" );
799             stats->dwOutRsts      = kstat_get_ui32( ksp, "outRsts" );
800             stats->dwNumConns     = kstat_get_ui32( ksp, "connTableSize" );
801             ret = NO_ERROR;
802         }
803         if (kc) kstat_close( kc );
804     }
805 #elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS)
806     {
807 #ifndef TCPTV_MIN  /* got removed in Mac OS X for some reason */
808 #define TCPTV_MIN 2
809 #define TCPTV_REXMTMAX 128
810 #endif
811         int mib[] = {CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS};
812 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
813 #define hz 1000
814         struct tcpstat tcp_stat;
815         size_t needed = sizeof(tcp_stat);
816
817         if(sysctl(mib, MIB_LEN, &tcp_stat, &needed, NULL, 0) != -1)
818         {
819             stats->u.RtoAlgorithm = MIB_TCP_RTO_VANJ;
820             stats->dwRtoMin = TCPTV_MIN;
821             stats->dwRtoMax = TCPTV_REXMTMAX;
822             stats->dwMaxConn = -1;
823             stats->dwActiveOpens = tcp_stat.tcps_connattempt;
824             stats->dwPassiveOpens = tcp_stat.tcps_accepts;
825             stats->dwAttemptFails = tcp_stat.tcps_conndrops;
826             stats->dwEstabResets = tcp_stat.tcps_drops;
827             stats->dwCurrEstab = 0;
828             stats->dwInSegs = tcp_stat.tcps_rcvtotal;
829             stats->dwOutSegs = tcp_stat.tcps_sndtotal - tcp_stat.tcps_sndrexmitpack;
830             stats->dwRetransSegs = tcp_stat.tcps_sndrexmitpack;
831             stats->dwInErrs = tcp_stat.tcps_rcvbadsum + tcp_stat.tcps_rcvbadoff + tcp_stat.tcps_rcvmemdrop + tcp_stat.tcps_rcvshort;
832             stats->dwOutRsts = tcp_stat.tcps_sndctrl - tcp_stat.tcps_closed;
833             stats->dwNumConns = tcp_stat.tcps_connects;
834             ret = NO_ERROR;
835         }
836         else ERR ("failed to get tcpstat\n");
837     }
838 #else
839     FIXME( "unimplemented\n" );
840 #endif
841     return ret;
842 }
843
844
845 /******************************************************************
846  *    GetUdpStatistics (IPHLPAPI.@)
847  *
848  * Get the UDP statistics for the local computer.
849  *
850  * PARAMS
851  *  stats [Out] buffer for UDP statistics
852  *
853  * RETURNS
854  *  Success: NO_ERROR
855  *  Failure: error code from winerror.h
856  */
857 DWORD WINAPI GetUdpStatistics(PMIB_UDPSTATS stats)
858 {
859     DWORD ret = ERROR_NOT_SUPPORTED;
860
861     if (!stats) return ERROR_INVALID_PARAMETER;
862     memset( stats, 0, sizeof(*stats) );
863
864 #ifdef __linux__
865     {
866         FILE *fp;
867
868         if ((fp = fopen("/proc/net/snmp", "r")))
869         {
870             static const char hdr[] = "Udp:";
871             char buf[512], *ptr;
872
873             while ((ptr = fgets(buf, sizeof(buf), fp)))
874             {
875                 if (strncasecmp(buf, hdr, sizeof(hdr) - 1)) continue;
876                 /* last line was a header, get another */
877                 if (!(ptr = fgets(buf, sizeof(buf), fp))) break;
878                 if (!strncasecmp(buf, hdr, sizeof(hdr) - 1))
879                 {
880                     ptr += sizeof(hdr);
881                     sscanf( ptr, "%u %u %u %u %u",
882                             &stats->dwInDatagrams, &stats->dwNoPorts,
883                             &stats->dwInErrors, &stats->dwOutDatagrams, &stats->dwNumAddrs );
884                     break;
885                 }
886             }
887             fclose(fp);
888             ret = NO_ERROR;
889         }
890     }
891 #elif defined(HAVE_LIBKSTAT)
892     {
893         static char udp[] = "udp";
894         kstat_ctl_t *kc;
895         kstat_t *ksp;
896         MIB_UDPTABLE *udp_table;
897
898         if ((kc = kstat_open()) &&
899             (ksp = kstat_lookup( kc, udp, 0, udp )) &&
900             kstat_read( kc, ksp, NULL ) != -1 &&
901             ksp->ks_type == KSTAT_TYPE_NAMED)
902         {
903             stats->dwInDatagrams  = kstat_get_ui32( ksp, "inDatagrams" );
904             stats->dwNoPorts      = 0;  /* FIXME */
905             stats->dwInErrors     = kstat_get_ui32( ksp, "inErrors" );
906             stats->dwOutDatagrams = kstat_get_ui32( ksp, "outDatagrams" );
907             if (!AllocateAndGetUdpTableFromStack( &udp_table, FALSE, GetProcessHeap(), 0 ))
908             {
909                 stats->dwNumAddrs = udp_table->dwNumEntries;
910                 HeapFree( GetProcessHeap(), 0, udp_table );
911             }
912             ret = NO_ERROR;
913         }
914         if (kc) kstat_close( kc );
915     }
916 #elif defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS)
917     {
918         int mib[] = {CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_STATS};
919 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
920         struct udpstat udp_stat;
921         MIB_UDPTABLE *udp_table;
922         size_t needed = sizeof(udp_stat);
923
924         if(sysctl(mib, MIB_LEN, &udp_stat, &needed, NULL, 0) != -1)
925         {
926             stats->dwInDatagrams = udp_stat.udps_ipackets;
927             stats->dwOutDatagrams = udp_stat.udps_opackets;
928             stats->dwNoPorts = udp_stat.udps_noport;
929             stats->dwInErrors = udp_stat.udps_hdrops + udp_stat.udps_badsum + udp_stat.udps_fullsock + udp_stat.udps_badlen;
930             if (!AllocateAndGetUdpTableFromStack( &udp_table, FALSE, GetProcessHeap(), 0 ))
931             {
932                 stats->dwNumAddrs = udp_table->dwNumEntries;
933                 HeapFree( GetProcessHeap(), 0, udp_table );
934             }
935             ret = NO_ERROR;
936         }
937         else ERR ("failed to get udpstat\n");
938     }
939 #else
940     FIXME( "unimplemented\n" );
941 #endif
942     return ret;
943 }
944
945
946 static MIB_IPFORWARDTABLE *append_ipforward_row( HANDLE heap, DWORD flags, MIB_IPFORWARDTABLE *table,
947                                                  DWORD *count, const MIB_IPFORWARDROW *row )
948 {
949     if (table->dwNumEntries >= *count)
950     {
951         MIB_IPFORWARDTABLE *new_table;
952         DWORD new_count = table->dwNumEntries * 2;
953
954         if (!(new_table = HeapReAlloc( heap, flags, table,
955                                        FIELD_OFFSET(MIB_IPFORWARDTABLE, table[new_count] ))))
956         {
957             HeapFree( heap, 0, table );
958             return NULL;
959         }
960         *count = new_count;
961         table = new_table;
962     }
963     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
964     return table;
965 }
966
967 static int compare_ipforward_rows(const void *a, const void *b)
968 {
969     const MIB_IPFORWARDROW *rowA = a;
970     const MIB_IPFORWARDROW *rowB = b;
971     int ret;
972
973     if ((ret = rowA->dwForwardDest - rowB->dwForwardDest) != 0) return ret;
974     if ((ret = rowA->u2.dwForwardProto - rowB->u2.dwForwardProto) != 0) return ret;
975     if ((ret = rowA->dwForwardPolicy - rowB->dwForwardPolicy) != 0) return ret;
976     return rowA->dwForwardNextHop - rowB->dwForwardNextHop;
977 }
978
979 /******************************************************************
980  *    AllocateAndGetIpForwardTableFromStack (IPHLPAPI.@)
981  *
982  * Get the route table.
983  * Like GetIpForwardTable(), but allocate the returned table from heap.
984  *
985  * PARAMS
986  *  ppIpForwardTable [Out] pointer into which the MIB_IPFORWARDTABLE is
987  *                         allocated and returned.
988  *  bOrder           [In]  whether to sort the table
989  *  heap             [In]  heap from which the table is allocated
990  *  flags            [In]  flags to HeapAlloc
991  *
992  * RETURNS
993  *  ERROR_INVALID_PARAMETER if ppIfTable is NULL, other error codes
994  *  on failure, NO_ERROR on success.
995  */
996 DWORD WINAPI AllocateAndGetIpForwardTableFromStack(PMIB_IPFORWARDTABLE *ppIpForwardTable, BOOL bOrder,
997                                                    HANDLE heap, DWORD flags)
998 {
999     MIB_IPFORWARDTABLE *table;
1000     MIB_IPFORWARDROW row;
1001     DWORD ret = NO_ERROR, count = 16;
1002
1003     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppIpForwardTable, bOrder, heap, flags);
1004
1005     if (!ppIpForwardTable) return ERROR_INVALID_PARAMETER;
1006
1007     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_IPFORWARDTABLE, table[count] ))))
1008         return ERROR_OUTOFMEMORY;
1009
1010     table->dwNumEntries = 0;
1011
1012 #ifdef __linux__
1013     {
1014         FILE *fp;
1015
1016         if ((fp = fopen("/proc/net/route", "r")))
1017         {
1018             char buf[512], *ptr;
1019             DWORD flags;
1020
1021             /* skip header line */
1022             ptr = fgets(buf, sizeof(buf), fp);
1023             while ((ptr = fgets(buf, sizeof(buf), fp)))
1024             {
1025                 memset( &row, 0, sizeof(row) );
1026
1027                 while (!isspace(*ptr)) ptr++;
1028                 *ptr++ = 0;
1029                 if (getInterfaceIndexByName(buf, &row.dwForwardIfIndex) != NO_ERROR)
1030                     continue;
1031
1032                 row.dwForwardDest = strtoul(ptr, &ptr, 16);
1033                 row.dwForwardNextHop = strtoul(ptr + 1, &ptr, 16);
1034                 flags = strtoul(ptr + 1, &ptr, 16);
1035
1036                 if (!(flags & RTF_UP)) row.u1.ForwardType = MIB_IPROUTE_TYPE_INVALID;
1037                 else if (flags & RTF_GATEWAY) row.u1.ForwardType = MIB_IPROUTE_TYPE_INDIRECT;
1038                 else row.u1.ForwardType = MIB_IPROUTE_TYPE_DIRECT;
1039
1040                 strtoul(ptr + 1, &ptr, 16); /* refcount, skip */
1041                 strtoul(ptr + 1, &ptr, 16); /* use, skip */
1042                 row.dwForwardMetric1 = strtoul(ptr + 1, &ptr, 16);
1043                 row.dwForwardMask = strtoul(ptr + 1, &ptr, 16);
1044                 /* FIXME: other protos might be appropriate, e.g. the default
1045                  * route is typically set with MIB_IPPROTO_NETMGMT instead */
1046                 row.u2.ForwardProto = MIB_IPPROTO_LOCAL;
1047
1048                 if (!(table = append_ipforward_row( heap, flags, table, &count, &row )))
1049                     break;
1050             }
1051             fclose(fp);
1052         }
1053         else ret = ERROR_NOT_SUPPORTED;
1054     }
1055 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1056     {
1057         void *data;
1058         int fd, len, namelen;
1059         mib2_ipRouteEntry_t *entry;
1060         char name[64];
1061
1062         if ((fd = open_streams_mib( NULL )) != -1)
1063         {
1064             if ((data = read_mib_entry( fd, MIB2_IP, MIB2_IP_ROUTE, &len )))
1065             {
1066                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
1067                 {
1068                     row.dwForwardDest      = entry->ipRouteDest;
1069                     row.dwForwardMask      = entry->ipRouteMask;
1070                     row.dwForwardPolicy    = 0;
1071                     row.dwForwardNextHop   = entry->ipRouteNextHop;
1072                     row.u1.dwForwardType   = entry->ipRouteType;
1073                     row.u2.dwForwardProto  = entry->ipRouteProto;
1074                     row.dwForwardAge       = entry->ipRouteAge;
1075                     row.dwForwardNextHopAS = 0;
1076                     row.dwForwardMetric1   = entry->ipRouteMetric1;
1077                     row.dwForwardMetric2   = entry->ipRouteMetric2;
1078                     row.dwForwardMetric3   = entry->ipRouteMetric3;
1079                     row.dwForwardMetric4   = entry->ipRouteMetric4;
1080                     row.dwForwardMetric5   = entry->ipRouteMetric5;
1081                     namelen = min( sizeof(name) - 1, entry->ipRouteIfIndex.o_length );
1082                     memcpy( name, entry->ipRouteIfIndex.o_bytes, namelen );
1083                     name[namelen] = 0;
1084                     getInterfaceIndexByName( name, &row.dwForwardIfIndex );
1085                     if (!(table = append_ipforward_row( heap, flags, table, &count, &row ))) break;
1086                 }
1087                 HeapFree( GetProcessHeap(), 0, data );
1088             }
1089             close( fd );
1090         }
1091         else ret = ERROR_NOT_SUPPORTED;
1092     }
1093 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1094     {
1095        int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
1096        size_t needed;
1097        char *buf = NULL, *lim, *next, *addrPtr;
1098        struct rt_msghdr *rtm;
1099
1100        if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
1101        {
1102           ERR ("sysctl 1 failed!\n");
1103           ret = ERROR_NOT_SUPPORTED;
1104           goto done;
1105        }
1106
1107        buf = HeapAlloc (GetProcessHeap (), 0, needed);
1108        if (!buf)
1109        {
1110           ret = ERROR_OUTOFMEMORY;
1111           goto done;
1112        }
1113
1114        if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
1115        {
1116           ret = ERROR_NOT_SUPPORTED;
1117           goto done;
1118        }
1119
1120        lim = buf + needed;
1121        for (next = buf; next < lim; next += rtm->rtm_msglen)
1122        {
1123           int i;
1124
1125           rtm = (struct rt_msghdr *)next;
1126
1127           if (rtm->rtm_type != RTM_GET)
1128           {
1129              WARN ("Got unexpected message type 0x%x!\n",
1130                    rtm->rtm_type);
1131              continue;
1132           }
1133
1134           /* Ignore all entries except for gateway routes which aren't
1135              multicast */
1136           if (!(rtm->rtm_flags & RTF_GATEWAY) ||
1137               (rtm->rtm_flags & RTF_MULTICAST))
1138              continue;
1139
1140           memset( &row, 0, sizeof(row) );
1141           row.dwForwardIfIndex = rtm->rtm_index;
1142           row.u1.ForwardType = MIB_IPROUTE_TYPE_INDIRECT;
1143           row.dwForwardMetric1 = rtm->rtm_rmx.rmx_hopcount;
1144           row.u2.ForwardProto = MIB_IPPROTO_LOCAL;
1145
1146           addrPtr = (char *)(rtm + 1);
1147
1148           for (i = 1; i; i <<= 1)
1149           {
1150              struct sockaddr *sa;
1151              DWORD addr;
1152
1153              if (!(i & rtm->rtm_addrs))
1154                 continue;
1155
1156              sa = (struct sockaddr *)addrPtr;
1157              ADVANCE (addrPtr, sa);
1158
1159              /* default routes are encoded by length-zero sockaddr */
1160              if (sa->sa_len == 0)
1161                 addr = 0;
1162              else if (sa->sa_family != AF_INET)
1163              {
1164                 WARN ("Received unsupported sockaddr family 0x%x\n",
1165                      sa->sa_family);
1166                 addr = 0;
1167              }
1168              else
1169              {
1170                 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
1171
1172                 addr = sin->sin_addr.s_addr;
1173              }
1174
1175              switch (i)
1176              {
1177                 case RTA_DST:     row.dwForwardDest = addr; break;
1178                 case RTA_GATEWAY: row.dwForwardNextHop = addr; break;
1179                 case RTA_NETMASK: row.dwForwardMask = addr; break;
1180                 default:
1181                    WARN ("Unexpected address type 0x%x\n", i);
1182              }
1183           }
1184
1185           if (!(table = append_ipforward_row( heap, flags, table, &count, &row )))
1186               break;
1187        }
1188 done:
1189       HeapFree( GetProcessHeap (), 0, buf );
1190     }
1191 #else
1192     FIXME( "not implemented\n" );
1193     ret = ERROR_NOT_SUPPORTED;
1194 #endif
1195
1196     if (!table) return ERROR_OUTOFMEMORY;
1197     if (!ret)
1198     {
1199         if (bOrder && table->dwNumEntries)
1200             qsort( table->table, table->dwNumEntries, sizeof(row), compare_ipforward_rows );
1201         *ppIpForwardTable = table;
1202     }
1203     else HeapFree( heap, flags, table );
1204     TRACE( "returning ret %u table %p\n", ret, table );
1205     return ret;
1206 }
1207
1208 static MIB_IPNETTABLE *append_ipnet_row( HANDLE heap, DWORD flags, MIB_IPNETTABLE *table,
1209                                          DWORD *count, const MIB_IPNETROW *row )
1210 {
1211     if (table->dwNumEntries >= *count)
1212     {
1213         MIB_IPNETTABLE *new_table;
1214         DWORD new_count = table->dwNumEntries * 2;
1215
1216         if (!(new_table = HeapReAlloc( heap, flags, table,
1217                                        FIELD_OFFSET(MIB_IPNETTABLE, table[new_count] ))))
1218         {
1219             HeapFree( heap, 0, table );
1220             return NULL;
1221         }
1222         *count = new_count;
1223         table = new_table;
1224     }
1225     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1226     return table;
1227 }
1228
1229 static int compare_ipnet_rows(const void *a, const void *b)
1230 {
1231     const MIB_IPNETROW *rowA = a;
1232     const MIB_IPNETROW *rowB = b;
1233
1234     return ntohl(rowA->dwAddr) - ntohl(rowB->dwAddr);
1235 }
1236
1237
1238 /******************************************************************
1239  *    AllocateAndGetIpNetTableFromStack (IPHLPAPI.@)
1240  *
1241  * Get the IP-to-physical address mapping table.
1242  * Like GetIpNetTable(), but allocate the returned table from heap.
1243  *
1244  * PARAMS
1245  *  ppIpNetTable [Out] pointer into which the MIB_IPNETTABLE is
1246  *                     allocated and returned.
1247  *  bOrder       [In]  whether to sort the table
1248  *  heap         [In]  heap from which the table is allocated
1249  *  flags        [In]  flags to HeapAlloc
1250  *
1251  * RETURNS
1252  *  ERROR_INVALID_PARAMETER if ppIpNetTable is NULL, other error codes
1253  *  on failure, NO_ERROR on success.
1254  */
1255 DWORD WINAPI AllocateAndGetIpNetTableFromStack(PMIB_IPNETTABLE *ppIpNetTable, BOOL bOrder,
1256                                                HANDLE heap, DWORD flags)
1257 {
1258     MIB_IPNETTABLE *table;
1259     MIB_IPNETROW row;
1260     DWORD ret = NO_ERROR, count = 16;
1261
1262     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppIpNetTable, bOrder, heap, flags);
1263
1264     if (!ppIpNetTable) return ERROR_INVALID_PARAMETER;
1265
1266     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_IPNETTABLE, table[count] ))))
1267         return ERROR_OUTOFMEMORY;
1268
1269     table->dwNumEntries = 0;
1270
1271 #ifdef __linux__
1272     {
1273         FILE *fp;
1274
1275         if ((fp = fopen("/proc/net/arp", "r")))
1276         {
1277             char buf[512], *ptr;
1278             DWORD flags;
1279
1280             /* skip header line */
1281             ptr = fgets(buf, sizeof(buf), fp);
1282             while ((ptr = fgets(buf, sizeof(buf), fp)))
1283             {
1284                 memset( &row, 0, sizeof(row) );
1285
1286                 row.dwAddr = inet_addr(ptr);
1287                 while (*ptr && !isspace(*ptr)) ptr++;
1288                 strtoul(ptr + 1, &ptr, 16); /* hw type (skip) */
1289                 flags = strtoul(ptr + 1, &ptr, 16);
1290
1291 #ifdef ATF_COM
1292                 if (flags & ATF_COM) row.u.Type = MIB_IPNET_TYPE_DYNAMIC;
1293                 else
1294 #endif
1295 #ifdef ATF_PERM
1296                 if (flags & ATF_PERM) row.u.Type = MIB_IPNET_TYPE_STATIC;
1297                 else
1298 #endif
1299                     row.u.Type = MIB_IPNET_TYPE_OTHER;
1300
1301                 while (*ptr && isspace(*ptr)) ptr++;
1302                 while (*ptr && !isspace(*ptr))
1303                 {
1304                     row.bPhysAddr[row.dwPhysAddrLen++] = strtoul(ptr, &ptr, 16);
1305                     if (*ptr) ptr++;
1306                 }
1307                 while (*ptr && isspace(*ptr)) ptr++;
1308                 while (*ptr && !isspace(*ptr)) ptr++;   /* mask (skip) */
1309                 while (*ptr && isspace(*ptr)) ptr++;
1310                 getInterfaceIndexByName(ptr, &row.dwIndex);
1311
1312                 if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
1313                     break;
1314             }
1315             fclose(fp);
1316         }
1317         else ret = ERROR_NOT_SUPPORTED;
1318     }
1319 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1320     {
1321         void *data;
1322         int fd, len, namelen;
1323         mib2_ipNetToMediaEntry_t *entry;
1324         char name[64];
1325
1326         if ((fd = open_streams_mib( NULL )) != -1)
1327         {
1328             if ((data = read_mib_entry( fd, MIB2_IP, MIB2_IP_MEDIA, &len )))
1329             {
1330                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
1331                 {
1332                     row.dwPhysAddrLen = min( entry->ipNetToMediaPhysAddress.o_length, MAXLEN_PHYSADDR );
1333                     memcpy( row.bPhysAddr, entry->ipNetToMediaPhysAddress.o_bytes, row.dwPhysAddrLen );
1334                     row.dwAddr = entry->ipNetToMediaNetAddress;
1335                     row.u.Type = entry->ipNetToMediaType;
1336                     namelen = min( sizeof(name) - 1, entry->ipNetToMediaIfIndex.o_length );
1337                     memcpy( name, entry->ipNetToMediaIfIndex.o_bytes, namelen );
1338                     name[namelen] = 0;
1339                     getInterfaceIndexByName( name, &row.dwIndex );
1340                     if (!(table = append_ipnet_row( heap, flags, table, &count, &row ))) break;
1341                 }
1342                 HeapFree( GetProcessHeap(), 0, data );
1343             }
1344             close( fd );
1345         }
1346         else ret = ERROR_NOT_SUPPORTED;
1347     }
1348 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1349     {
1350       int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO};
1351 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
1352       size_t needed;
1353       char *buf = NULL, *lim, *next;
1354       struct rt_msghdr *rtm;
1355       struct sockaddr_inarp *sinarp;
1356       struct sockaddr_dl *sdl;
1357
1358       if (sysctl (mib, MIB_LEN,  NULL, &needed, NULL, 0) == -1)
1359       {
1360          ERR ("failed to get arp table\n");
1361          ret = ERROR_NOT_SUPPORTED;
1362          goto done;
1363       }
1364
1365       buf = HeapAlloc (GetProcessHeap (), 0, needed);
1366       if (!buf)
1367       {
1368           ret = ERROR_OUTOFMEMORY;
1369           goto done;
1370       }
1371
1372       if (sysctl (mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
1373       {
1374          ret = ERROR_NOT_SUPPORTED;
1375          goto done;
1376       }
1377
1378       lim = buf + needed;
1379       next = buf;
1380       while(next < lim)
1381       {
1382           rtm = (struct rt_msghdr *)next;
1383           sinarp=(struct sockaddr_inarp *)(rtm + 1);
1384           sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len));
1385           if(sdl->sdl_alen) /* arp entry */
1386           {
1387               memset( &row, 0, sizeof(row) );
1388               row.dwAddr = sinarp->sin_addr.s_addr;
1389               row.dwIndex = sdl->sdl_index;
1390               row.dwPhysAddrLen = min( 8, sdl->sdl_alen );
1391               memcpy( row.bPhysAddr, &sdl->sdl_data[sdl->sdl_nlen], row.dwPhysAddrLen );
1392               if(rtm->rtm_rmx.rmx_expire == 0) row.u.Type = MIB_IPNET_TYPE_STATIC;
1393               else if(sinarp->sin_other & SIN_PROXY) row.u.Type = MIB_IPNET_TYPE_OTHER;
1394               else if(rtm->rtm_rmx.rmx_expire != 0) row.u.Type = MIB_IPNET_TYPE_DYNAMIC;
1395               else row.u.Type = MIB_IPNET_TYPE_INVALID;
1396
1397               if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
1398                   break;
1399           }
1400           next += rtm->rtm_msglen;
1401       }
1402 done:
1403       HeapFree( GetProcessHeap (), 0, buf );
1404     }
1405 #else
1406     FIXME( "not implemented\n" );
1407     ret = ERROR_NOT_SUPPORTED;
1408 #endif
1409
1410     if (!table) return ERROR_OUTOFMEMORY;
1411     if (!ret)
1412     {
1413         if (bOrder && table->dwNumEntries)
1414             qsort( table->table, table->dwNumEntries, sizeof(row), compare_ipnet_rows );
1415         *ppIpNetTable = table;
1416     }
1417     else HeapFree( heap, flags, table );
1418     TRACE( "returning ret %u table %p\n", ret, table );
1419     return ret;
1420 }
1421
1422
1423 static MIB_UDPTABLE *append_udp_row( HANDLE heap, DWORD flags, MIB_UDPTABLE *table,
1424                                      DWORD *count, const MIB_UDPROW *row )
1425 {
1426     if (table->dwNumEntries >= *count)
1427     {
1428         MIB_UDPTABLE *new_table;
1429         DWORD new_count = table->dwNumEntries * 2;
1430
1431         if (!(new_table = HeapReAlloc( heap, flags, table, FIELD_OFFSET(MIB_UDPTABLE, table[new_count] ))))
1432         {
1433             HeapFree( heap, 0, table );
1434             return NULL;
1435         }
1436         *count = new_count;
1437         table = new_table;
1438     }
1439     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1440     return table;
1441 }
1442
1443 static int compare_udp_rows(const void *a, const void *b)
1444 {
1445     const MIB_UDPROW *rowA = a;
1446     const MIB_UDPROW *rowB = b;
1447     int ret;
1448
1449     if ((ret = rowA->dwLocalAddr - rowB->dwLocalAddr) != 0) return ret;
1450     return rowA->dwLocalPort - rowB->dwLocalPort;
1451 }
1452
1453
1454 /******************************************************************
1455  *    AllocateAndGetUdpTableFromStack (IPHLPAPI.@)
1456  *
1457  * Get the UDP listener table.
1458  * Like GetUdpTable(), but allocate the returned table from heap.
1459  *
1460  * PARAMS
1461  *  ppUdpTable [Out] pointer into which the MIB_UDPTABLE is
1462  *                   allocated and returned.
1463  *  bOrder     [In]  whether to sort the table
1464  *  heap       [In]  heap from which the table is allocated
1465  *  flags      [In]  flags to HeapAlloc
1466  *
1467  * RETURNS
1468  *  ERROR_INVALID_PARAMETER if ppUdpTable is NULL, whatever GetUdpTable()
1469  *  returns otherwise.
1470  */
1471 DWORD WINAPI AllocateAndGetUdpTableFromStack(PMIB_UDPTABLE *ppUdpTable, BOOL bOrder,
1472                                              HANDLE heap, DWORD flags)
1473 {
1474     MIB_UDPTABLE *table;
1475     MIB_UDPROW row;
1476     DWORD ret = NO_ERROR, count = 16;
1477
1478     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppUdpTable, bOrder, heap, flags);
1479
1480     if (!ppUdpTable) return ERROR_INVALID_PARAMETER;
1481
1482     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_UDPTABLE, table[count] ))))
1483         return ERROR_OUTOFMEMORY;
1484
1485     table->dwNumEntries = 0;
1486
1487 #ifdef __linux__
1488     {
1489         FILE *fp;
1490
1491         if ((fp = fopen("/proc/net/udp", "r")))
1492         {
1493             char buf[512], *ptr;
1494             DWORD dummy;
1495
1496             /* skip header line */
1497             ptr = fgets(buf, sizeof(buf), fp);
1498             while ((ptr = fgets(buf, sizeof(buf), fp)))
1499             {
1500                 if (sscanf( ptr, "%u: %x:%x", &dummy, &row.dwLocalAddr, &row.dwLocalPort ) != 3)
1501                     continue;
1502                 row.dwLocalPort = htons( row.dwLocalPort );
1503                 if (!(table = append_udp_row( heap, flags, table, &count, &row )))
1504                     break;
1505             }
1506             fclose(fp);
1507         }
1508         else ret = ERROR_NOT_SUPPORTED;
1509     }
1510 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1511     {
1512         void *data;
1513         int fd, len;
1514         mib2_udpEntry_t *entry;
1515
1516         if ((fd = open_streams_mib( "udp" )) != -1)
1517         {
1518             if ((data = read_mib_entry( fd, MIB2_UDP, MIB2_UDP_ENTRY, &len )))
1519             {
1520                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
1521                 {
1522                     row.dwLocalAddr = entry->udpLocalAddress;
1523                     row.dwLocalPort = htons( entry->udpLocalPort );
1524                     if (!(table = append_udp_row( heap, flags, table, &count, &row ))) break;
1525                 }
1526                 HeapFree( GetProcessHeap(), 0, data );
1527             }
1528             close( fd );
1529         }
1530         else ret = ERROR_NOT_SUPPORTED;
1531     }
1532 #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
1533     {
1534         size_t Len = 0;
1535         char *Buf = NULL;
1536         struct xinpgen *pXIG, *pOrigXIG;
1537
1538         if (sysctlbyname ("net.inet.udp.pcblist", NULL, &Len, NULL, 0) < 0)
1539         {
1540             ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n");
1541             ret = ERROR_NOT_SUPPORTED;
1542             goto done;
1543         }
1544
1545         Buf = HeapAlloc (GetProcessHeap (), 0, Len);
1546         if (!Buf)
1547         {
1548             ret = ERROR_OUTOFMEMORY;
1549             goto done;
1550         }
1551
1552         if (sysctlbyname ("net.inet.udp.pcblist", Buf, &Len, NULL, 0) < 0)
1553         {
1554             ERR ("Failure to read net.inet.udp.pcblist via sysctlbyname!\n");
1555             ret = ERROR_NOT_SUPPORTED;
1556             goto done;
1557         }
1558
1559         /* Might be nothing here; first entry is just a header it seems */
1560         if (Len <= sizeof (struct xinpgen)) goto done;
1561
1562         pOrigXIG = (struct xinpgen *)Buf;
1563         pXIG = pOrigXIG;
1564
1565         for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
1566              pXIG->xig_len > sizeof (struct xinpgen);
1567              pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
1568         {
1569             struct inpcb *pINData;
1570             struct xsocket *pSockData;
1571
1572             pINData = &((struct xinpcb *)pXIG)->xi_inp;
1573             pSockData = &((struct xinpcb *)pXIG)->xi_socket;
1574
1575             /* Ignore sockets for other protocols */
1576             if (pSockData->xso_protocol != IPPROTO_UDP)
1577                 continue;
1578
1579             /* Ignore PCBs that were freed while generating the data */
1580             if (pINData->inp_gencnt > pOrigXIG->xig_gen)
1581                 continue;
1582
1583             /* we're only interested in IPv4 addresses */
1584             if (!(pINData->inp_vflag & INP_IPV4) ||
1585                 (pINData->inp_vflag & INP_IPV6))
1586                 continue;
1587
1588             /* If all 0's, skip it */
1589             if (!pINData->inp_laddr.s_addr &&
1590                 !pINData->inp_lport)
1591                 continue;
1592
1593             /* Fill in structure details */
1594             row.dwLocalAddr = pINData->inp_laddr.s_addr;
1595             row.dwLocalPort = pINData->inp_lport;
1596             if (!(table = append_udp_row( heap, flags, table, &count, &row ))) break;
1597         }
1598
1599     done:
1600         HeapFree (GetProcessHeap (), 0, Buf);
1601     }
1602 #else
1603     FIXME( "not implemented\n" );
1604     ret = ERROR_NOT_SUPPORTED;
1605 #endif
1606
1607     if (!table) return ERROR_OUTOFMEMORY;
1608     if (!ret)
1609     {
1610         if (bOrder && table->dwNumEntries)
1611             qsort( table->table, table->dwNumEntries, sizeof(row), compare_udp_rows );
1612         *ppUdpTable = table;
1613     }
1614     else HeapFree( heap, flags, table );
1615     TRACE( "returning ret %u table %p\n", ret, table );
1616     return ret;
1617 }
1618
1619
1620 static MIB_TCPTABLE *append_tcp_row( HANDLE heap, DWORD flags, MIB_TCPTABLE *table,
1621                                      DWORD *count, const MIB_TCPROW *row )
1622 {
1623     if (table->dwNumEntries >= *count)
1624     {
1625         MIB_TCPTABLE *new_table;
1626         DWORD new_count = table->dwNumEntries * 2;
1627
1628         if (!(new_table = HeapReAlloc( heap, flags, table, FIELD_OFFSET(MIB_TCPTABLE, table[new_count] ))))
1629         {
1630             HeapFree( heap, 0, table );
1631             return NULL;
1632         }
1633         *count = new_count;
1634         table = new_table;
1635     }
1636     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1637     return table;
1638 }
1639
1640
1641 /* Why not a lookup table? Because the TCPS_* constants are different
1642    on different platforms */
1643 static inline MIB_TCP_STATE TCPStateToMIBState (int state)
1644 {
1645    switch (state)
1646    {
1647       case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB;
1648       case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT;
1649       case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD;
1650       case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1;
1651       case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2;
1652       case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT;
1653       case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT;
1654       case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK;
1655       case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN;
1656       case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING;
1657       default:
1658       case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED;
1659    }
1660 }
1661
1662
1663 static int compare_tcp_rows(const void *a, const void *b)
1664 {
1665     const MIB_TCPROW *rowA = a;
1666     const MIB_TCPROW *rowB = b;
1667     int ret;
1668
1669     if ((ret = ntohl (rowA->dwLocalAddr) - ntohl (rowB->dwLocalAddr)) != 0) return ret;
1670     if ((ret = ntohs ((unsigned short)rowA->dwLocalPort) -
1671                ntohs ((unsigned short)rowB->dwLocalPort)) != 0) return ret;
1672     if ((ret = ntohl (rowA->dwRemoteAddr) - ntohl (rowB->dwRemoteAddr)) != 0) return ret;
1673     return ntohs ((unsigned short)rowA->dwRemotePort) - ntohs ((unsigned short)rowB->dwRemotePort);
1674 }
1675
1676
1677 /******************************************************************
1678  *    AllocateAndGetTcpTableFromStack (IPHLPAPI.@)
1679  *
1680  * Get the TCP connection table.
1681  * Like GetTcpTable(), but allocate the returned table from heap.
1682  *
1683  * PARAMS
1684  *  ppTcpTable [Out] pointer into which the MIB_TCPTABLE is
1685  *                   allocated and returned.
1686  *  bOrder     [In]  whether to sort the table
1687  *  heap       [In]  heap from which the table is allocated
1688  *  flags      [In]  flags to HeapAlloc
1689  *
1690  * RETURNS
1691  *  ERROR_INVALID_PARAMETER if ppTcpTable is NULL, whatever GetTcpTable()
1692  *  returns otherwise.
1693  */
1694 DWORD WINAPI AllocateAndGetTcpTableFromStack( PMIB_TCPTABLE *ppTcpTable, BOOL bOrder,
1695                                               HANDLE heap, DWORD flags)
1696 {
1697     MIB_TCPTABLE *table;
1698     MIB_TCPROW row;
1699     DWORD ret = NO_ERROR, count = 16;
1700
1701     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppTcpTable, bOrder, heap, flags);
1702
1703     if (!ppTcpTable) return ERROR_INVALID_PARAMETER;
1704
1705     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_TCPTABLE, table[count] ))))
1706         return ERROR_OUTOFMEMORY;
1707
1708     table->dwNumEntries = 0;
1709
1710 #ifdef __linux__
1711     {
1712         FILE *fp;
1713
1714         if ((fp = fopen("/proc/net/tcp", "r")))
1715         {
1716             char buf[512], *ptr;
1717             DWORD dummy;
1718
1719             /* skip header line */
1720             ptr = fgets(buf, sizeof(buf), fp);
1721             while ((ptr = fgets(buf, sizeof(buf), fp)))
1722             {
1723                 if (sscanf( ptr, "%x: %x:%x %x:%x %x", &dummy, &row.dwLocalAddr, &row.dwLocalPort,
1724                             &row.dwRemoteAddr, &row.dwRemotePort, &row.u.dwState ) != 6)
1725                     continue;
1726                 row.dwLocalPort = htons( row.dwLocalPort );
1727                 row.dwRemotePort = htons( row.dwRemotePort );
1728                 row.u.State = TCPStateToMIBState( row.u.dwState );
1729                 if (!(table = append_tcp_row( heap, flags, table, &count, &row )))
1730                     break;
1731             }
1732             fclose( fp );
1733         }
1734         else ret = ERROR_NOT_SUPPORTED;
1735     }
1736 #elif defined(HAVE_SYS_TIHDR_H) && defined(T_OPTMGMT_ACK)
1737     {
1738         void *data;
1739         int fd, len;
1740         mib2_tcpConnEntry_t *entry;
1741
1742         if ((fd = open_streams_mib( "tcp" )) != -1)
1743         {
1744             if ((data = read_mib_entry( fd, MIB2_TCP, MIB2_TCP_CONN, &len )))
1745             {
1746                 for (entry = data; (char *)(entry + 1) <= (char *)data + len; entry++)
1747                 {
1748                     row.dwLocalAddr = entry->tcpConnLocalAddress;
1749                     row.dwLocalPort = htons( entry->tcpConnLocalPort );
1750                     row.dwRemoteAddr = entry->tcpConnRemAddress;
1751                     row.dwRemotePort = htons( entry->tcpConnRemPort );
1752                     row.u.dwState = entry->tcpConnState;
1753                     if (!(table = append_tcp_row( heap, flags, table, &count, &row ))) break;
1754                 }
1755                 HeapFree( GetProcessHeap(), 0, data );
1756             }
1757             close( fd );
1758         }
1759         else ret = ERROR_NOT_SUPPORTED;
1760     }
1761 #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
1762     {
1763         size_t Len = 0;
1764         char *Buf = NULL;
1765         struct xinpgen *pXIG, *pOrigXIG;
1766
1767         if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0)
1768         {
1769             ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1770             ret = ERROR_NOT_SUPPORTED;
1771             goto done;
1772         }
1773
1774         Buf = HeapAlloc (GetProcessHeap (), 0, Len);
1775         if (!Buf)
1776         {
1777             ret = ERROR_OUTOFMEMORY;
1778             goto done;
1779         }
1780
1781         if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0)
1782         {
1783             ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1784             ret = ERROR_NOT_SUPPORTED;
1785             goto done;
1786         }
1787
1788         /* Might be nothing here; first entry is just a header it seems */
1789         if (Len <= sizeof (struct xinpgen)) goto done;
1790
1791         pOrigXIG = (struct xinpgen *)Buf;
1792         pXIG = pOrigXIG;
1793
1794         for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
1795              pXIG->xig_len > sizeof (struct xinpgen);
1796              pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
1797         {
1798             struct tcpcb *pTCPData = NULL;
1799             struct inpcb *pINData;
1800             struct xsocket *pSockData;
1801
1802             pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
1803             pINData = &((struct xtcpcb *)pXIG)->xt_inp;
1804             pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
1805
1806             /* Ignore sockets for other protocols */
1807             if (pSockData->xso_protocol != IPPROTO_TCP)
1808                 continue;
1809
1810             /* Ignore PCBs that were freed while generating the data */
1811             if (pINData->inp_gencnt > pOrigXIG->xig_gen)
1812                 continue;
1813
1814             /* we're only interested in IPv4 addresses */
1815             if (!(pINData->inp_vflag & INP_IPV4) ||
1816                 (pINData->inp_vflag & INP_IPV6))
1817                 continue;
1818
1819             /* If all 0's, skip it */
1820             if (!pINData->inp_laddr.s_addr &&
1821                 !pINData->inp_lport &&
1822                 !pINData->inp_faddr.s_addr &&
1823                 !pINData->inp_fport)
1824                 continue;
1825
1826             /* Fill in structure details */
1827             row.dwLocalAddr = pINData->inp_laddr.s_addr;
1828             row.dwLocalPort = pINData->inp_lport;
1829             row.dwRemoteAddr = pINData->inp_faddr.s_addr;
1830             row.dwRemotePort = pINData->inp_fport;
1831             row.u.State = TCPStateToMIBState (pTCPData->t_state);
1832             if (!(table = append_tcp_row( heap, flags, table, &count, &row ))) break;
1833         }
1834
1835     done:
1836         HeapFree (GetProcessHeap (), 0, Buf);
1837     }
1838 #else
1839     FIXME( "not implemented\n" );
1840     ret = ERROR_NOT_SUPPORTED;
1841 #endif
1842
1843     if (!table) return ERROR_OUTOFMEMORY;
1844     if (!ret)
1845     {
1846         if (bOrder && table->dwNumEntries)
1847             qsort( table->table, table->dwNumEntries, sizeof(row), compare_tcp_rows );
1848         *ppTcpTable = table;
1849     }
1850     else HeapFree( heap, flags, table );
1851     TRACE( "returning ret %u table %p\n", ret, table );
1852     return ret;
1853 }