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