iphlpapi: Moved AllocateAndGetTcpTableFromStack implementation to ipstats.c.
[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
697   if (!stats)
698     return ERROR_INVALID_PARAMETER;
699
700   memset(stats, 0, sizeof(MIB_TCPSTATS));
701
702   /* get from /proc/net/snmp, no error if can't */
703   fp = fopen("/proc/net/snmp", "r");
704   if (fp) {
705     static const char hdr[] = "Tcp:";
706     char buf[512] = { 0 }, *ptr;
707
708
709     do {
710       ptr = fgets(buf, sizeof(buf), fp);
711     } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
712     if (ptr) {
713       /* last line was a header, get another */
714       ptr = fgets(buf, sizeof(buf), fp);
715       if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
716         char *endPtr;
717
718         ptr += sizeof(hdr);
719         if (ptr && *ptr) {
720           stats->dwRtoAlgorithm = strtoul(ptr, &endPtr, 10);
721           ptr = endPtr;
722         }
723         if (ptr && *ptr) {
724           stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
725           ptr = endPtr;
726         }
727         if (ptr && *ptr) {
728           stats->dwRtoMax = strtoul(ptr, &endPtr, 10);
729           ptr = endPtr;
730         }
731         if (ptr && *ptr) {
732           stats->dwMaxConn = strtoul(ptr, &endPtr, 10);
733           ptr = endPtr;
734         }
735         if (ptr && *ptr) {
736           stats->dwActiveOpens = strtoul(ptr, &endPtr, 10);
737           ptr = endPtr;
738         }
739         if (ptr && *ptr) {
740           stats->dwPassiveOpens = strtoul(ptr, &endPtr, 10);
741           ptr = endPtr;
742         }
743         if (ptr && *ptr) {
744           stats->dwAttemptFails = strtoul(ptr, &endPtr, 10);
745           ptr = endPtr;
746         }
747         if (ptr && *ptr) {
748           stats->dwEstabResets = strtoul(ptr, &endPtr, 10);
749           ptr = endPtr;
750         }
751         if (ptr && *ptr) {
752           stats->dwCurrEstab = strtoul(ptr, &endPtr, 10);
753           ptr = endPtr;
754         }
755         if (ptr && *ptr) {
756           stats->dwInSegs = strtoul(ptr, &endPtr, 10);
757           ptr = endPtr;
758         }
759         if (ptr && *ptr) {
760           stats->dwOutSegs = strtoul(ptr, &endPtr, 10);
761           ptr = endPtr;
762         }
763         if (ptr && *ptr) {
764           stats->dwRetransSegs = strtoul(ptr, &endPtr, 10);
765           ptr = endPtr;
766         }
767         if (ptr && *ptr) {
768           stats->dwInErrs = strtoul(ptr, &endPtr, 10);
769           ptr = endPtr;
770         }
771         if (ptr && *ptr) {
772           stats->dwOutRsts = strtoul(ptr, &endPtr, 10);
773           ptr = endPtr;
774         }
775         stats->dwNumConns = getNumTcpEntries();
776       }
777     }
778     fclose(fp);
779   }
780   else
781   {
782      ERR ("unimplemented!\n");
783      return ERROR_NOT_SUPPORTED;
784   }
785
786   return NO_ERROR;
787 #endif
788 }
789
790 DWORD getUDPStats(MIB_UDPSTATS *stats)
791 {
792 #if defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS)
793   int mib[] = {CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_STATS};
794 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
795   struct udpstat udp_stat;
796   size_t needed;
797   if (!stats)
798       return ERROR_INVALID_PARAMETER;
799
800   needed = sizeof(udp_stat);
801
802   if(sysctl(mib, MIB_LEN, &udp_stat, &needed, NULL, 0) == -1)
803   {
804       ERR ("failed to get udpstat\n");
805       return ERROR_NOT_SUPPORTED;
806   }
807
808   stats->dwInDatagrams = udp_stat.udps_ipackets;
809   stats->dwOutDatagrams = udp_stat.udps_opackets;
810   stats->dwNoPorts = udp_stat.udps_noport;
811   stats->dwInErrors = udp_stat.udps_hdrops + udp_stat.udps_badsum + udp_stat.udps_fullsock + udp_stat.udps_badlen;
812   stats->dwNumAddrs = getNumUdpEntries();
813
814   return NO_ERROR;
815 #else
816   FILE *fp;
817
818   if (!stats)
819     return ERROR_INVALID_PARAMETER;
820
821   memset(stats, 0, sizeof(MIB_UDPSTATS));
822
823   /* get from /proc/net/snmp, no error if can't */
824   fp = fopen("/proc/net/snmp", "r");
825   if (fp) {
826     static const char hdr[] = "Udp:";
827     char buf[512] = { 0 }, *ptr;
828
829
830     do {
831       ptr = fgets(buf, sizeof(buf), fp);
832     } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
833     if (ptr) {
834       /* last line was a header, get another */
835       ptr = fgets(buf, sizeof(buf), fp);
836       if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
837         char *endPtr;
838
839         ptr += sizeof(hdr);
840         if (ptr && *ptr) {
841           stats->dwInDatagrams = strtoul(ptr, &endPtr, 10);
842           ptr = endPtr;
843         }
844         if (ptr && *ptr) {
845           stats->dwNoPorts = strtoul(ptr, &endPtr, 10);
846           ptr = endPtr;
847         }
848         if (ptr && *ptr) {
849           stats->dwInErrors = strtoul(ptr, &endPtr, 10);
850           ptr = endPtr;
851         }
852         if (ptr && *ptr) {
853           stats->dwOutDatagrams = strtoul(ptr, &endPtr, 10);
854           ptr = endPtr;
855         }
856         if (ptr && *ptr) {
857           stats->dwNumAddrs = strtoul(ptr, &endPtr, 10);
858           ptr = endPtr;
859         }
860       }
861     }
862     fclose(fp);
863   }
864   else
865   {
866      ERR ("unimplemented!\n");
867      return ERROR_NOT_SUPPORTED;
868   }
869
870   return NO_ERROR;
871 #endif
872 }
873
874 static DWORD getNumWithOneHeader(const char *filename)
875 {
876 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
877    size_t Len = 0;
878    char *Buf;
879    struct xinpgen *pXIG, *pOrigXIG;
880    int Protocol;
881    DWORD NumEntries = 0;
882
883    if (!strcmp (filename, "net.inet.tcp.pcblist"))
884       Protocol = IPPROTO_TCP;
885    else if (!strcmp (filename, "net.inet.udp.pcblist"))
886       Protocol = IPPROTO_UDP;
887    else
888    {
889       ERR ("Unsupported mib '%s', needs protocol mapping\n",
890            filename);
891       return 0;
892    }
893
894    if (sysctlbyname (filename, NULL, &Len, NULL, 0) < 0)
895    {
896       WARN ("Unable to read '%s' via sysctlbyname\n", filename);
897       return 0;
898    }
899
900    Buf = HeapAlloc (GetProcessHeap (), 0, Len);
901    if (!Buf)
902    {
903       ERR ("Out of memory!\n");
904       return 0;
905    }
906
907    if (sysctlbyname (filename, Buf, &Len, NULL, 0) < 0)
908    {
909       ERR ("Failure to read '%s' via sysctlbyname!\n", filename);
910       HeapFree (GetProcessHeap (), 0, Buf);
911       return 0;
912    }
913
914    /* Might be nothing here; first entry is just a header it seems */
915    if (Len <= sizeof (struct xinpgen))
916    {
917       HeapFree (GetProcessHeap (), 0, Buf);
918       return 0;
919    }
920
921    pOrigXIG = (struct xinpgen *)Buf;
922    pXIG = pOrigXIG;
923
924    for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
925         pXIG->xig_len > sizeof (struct xinpgen);
926         pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
927    {
928       struct tcpcb *pTCPData = NULL;
929       struct inpcb *pINData;
930       struct xsocket *pSockData;
931
932       if (Protocol == IPPROTO_TCP)
933       {
934          pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
935          pINData = &((struct xtcpcb *)pXIG)->xt_inp;
936          pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
937       }
938       else
939       {
940          pINData = &((struct xinpcb *)pXIG)->xi_inp;
941          pSockData = &((struct xinpcb *)pXIG)->xi_socket;
942       }
943
944       /* Ignore sockets for other protocols */
945       if (pSockData->xso_protocol != Protocol)
946          continue;
947
948       /* Ignore PCBs that were freed while generating the data */
949       if (pINData->inp_gencnt > pOrigXIG->xig_gen)
950          continue;
951
952       /* we're only interested in IPv4 addresses */
953       if (!(pINData->inp_vflag & INP_IPV4) ||
954           (pINData->inp_vflag & INP_IPV6))
955          continue;
956
957       /* If all 0's, skip it */
958       if (!pINData->inp_laddr.s_addr &&
959           !pINData->inp_lport &&
960           !pINData->inp_faddr.s_addr &&
961           !pINData->inp_fport)
962          continue;
963
964       NumEntries++;
965    }
966
967    HeapFree (GetProcessHeap (), 0, Buf);
968    return NumEntries;
969 #else
970   FILE *fp;
971   int ret = 0;
972
973   fp = fopen(filename, "r");
974   if (fp) {
975     char buf[512] = { 0 }, *ptr;
976
977
978     ptr = fgets(buf, sizeof(buf), fp);
979     if (ptr) {
980       do {
981         ptr = fgets(buf, sizeof(buf), fp);
982         if (ptr)
983           ret++;
984       } while (ptr);
985     }
986     fclose(fp);
987   }
988   else
989      ERR ("Unable to open '%s' to count entries!\n", filename);
990
991   return ret;
992 #endif
993 }
994
995 DWORD getNumRoutes(void)
996 {
997 #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
998    int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
999    size_t needed;
1000    char *buf, *lim, *next;
1001    struct rt_msghdr *rtm;
1002    DWORD RouteCount = 0;
1003
1004    if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
1005    {
1006       ERR ("sysctl 1 failed!\n");
1007       return 0;
1008    }
1009
1010    buf = HeapAlloc (GetProcessHeap (), 0, needed);
1011    if (!buf) return 0;
1012
1013    if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
1014    {
1015       ERR ("sysctl 2 failed!\n");
1016       HeapFree (GetProcessHeap (), 0, buf);
1017       return 0;
1018    }
1019
1020    lim = buf + needed;
1021    for (next = buf; next < lim; next += rtm->rtm_msglen)
1022    {
1023       rtm = (struct rt_msghdr *)next;
1024
1025       if (rtm->rtm_type != RTM_GET)
1026       {
1027          WARN ("Got unexpected message type 0x%x!\n",
1028                rtm->rtm_type);
1029          continue;
1030       }
1031
1032       /* Ignore all entries except for gateway routes which aren't
1033          multicast */
1034       if (!(rtm->rtm_flags & RTF_GATEWAY) || (rtm->rtm_flags & RTF_MULTICAST))
1035          continue;
1036
1037       RouteCount++;
1038    }
1039
1040    HeapFree (GetProcessHeap (), 0, buf);
1041    return RouteCount;
1042 #else
1043    return getNumWithOneHeader("/proc/net/route");
1044 #endif
1045 }
1046
1047 static MIB_IPFORWARDTABLE *append_ipforward_row( HANDLE heap, DWORD flags, MIB_IPFORWARDTABLE *table,
1048                                                  DWORD *count, const MIB_IPFORWARDROW *row )
1049 {
1050     if (table->dwNumEntries >= *count)
1051     {
1052         MIB_IPFORWARDTABLE *new_table;
1053         DWORD new_count = table->dwNumEntries * 2;
1054
1055         if (!(new_table = HeapReAlloc( heap, flags, table,
1056                                        FIELD_OFFSET(MIB_IPFORWARDTABLE, table[new_count] ))))
1057         {
1058             HeapFree( heap, 0, table );
1059             return NULL;
1060         }
1061         *count = new_count;
1062         table = new_table;
1063     }
1064     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1065     return table;
1066 }
1067
1068 DWORD getRouteTable(PMIB_IPFORWARDTABLE *ppIpForwardTable, HANDLE heap, DWORD flags)
1069 {
1070     MIB_IPFORWARDTABLE *table;
1071     MIB_IPFORWARDROW row;
1072     DWORD ret = NO_ERROR, count = 16;
1073
1074     if (!ppIpForwardTable) return ERROR_INVALID_PARAMETER;
1075
1076     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_IPFORWARDTABLE, table[count] ))))
1077         return ERROR_OUTOFMEMORY;
1078
1079     table->dwNumEntries = 0;
1080
1081 #ifdef __linux__
1082     {
1083         FILE *fp;
1084
1085         if ((fp = fopen("/proc/net/route", "r")))
1086         {
1087             char buf[512], *ptr;
1088             DWORD flags;
1089
1090             /* skip header line */
1091             ptr = fgets(buf, sizeof(buf), fp);
1092             while ((ptr = fgets(buf, sizeof(buf), fp)))
1093             {
1094                 memset( &row, 0, sizeof(row) );
1095
1096                 while (!isspace(*ptr)) ptr++;
1097                 *ptr++ = 0;
1098                 if (getInterfaceIndexByName(buf, &row.dwForwardIfIndex) != NO_ERROR)
1099                     continue;
1100
1101                 row.dwForwardDest = strtoul(ptr, &ptr, 16);
1102                 row.dwForwardNextHop = strtoul(ptr + 1, &ptr, 16);
1103                 flags = strtoul(ptr + 1, &ptr, 16);
1104
1105                 if (!(flags & RTF_UP)) row.dwForwardType = MIB_IPROUTE_TYPE_INVALID;
1106                 else if (flags & RTF_GATEWAY) row.dwForwardType = MIB_IPROUTE_TYPE_INDIRECT;
1107                 else row.dwForwardType = MIB_IPROUTE_TYPE_DIRECT;
1108
1109                 strtoul(ptr + 1, &ptr, 16); /* refcount, skip */
1110                 strtoul(ptr + 1, &ptr, 16); /* use, skip */
1111                 row.dwForwardMetric1 = strtoul(ptr + 1, &ptr, 16);
1112                 row.dwForwardMask = strtoul(ptr + 1, &ptr, 16);
1113                 /* FIXME: other protos might be appropriate, e.g. the default
1114                  * route is typically set with MIB_IPPROTO_NETMGMT instead */
1115                 row.dwForwardProto = MIB_IPPROTO_LOCAL;
1116
1117                 if (!(table = append_ipforward_row( heap, flags, table, &count, &row )))
1118                     break;
1119             }
1120             fclose(fp);
1121         }
1122         else ret = ERROR_NOT_SUPPORTED;
1123     }
1124 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1125     {
1126        int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
1127        size_t needed;
1128        char *buf = NULL, *lim, *next, *addrPtr;
1129        struct rt_msghdr *rtm;
1130
1131        if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
1132        {
1133           ERR ("sysctl 1 failed!\n");
1134           ret = ERROR_NOT_SUPPORTED;
1135           goto done;
1136        }
1137
1138        buf = HeapAlloc (GetProcessHeap (), 0, needed);
1139        if (!buf)
1140        {
1141           ret = ERROR_OUTOFMEMORY;
1142           goto done;
1143        }
1144
1145        if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
1146        {
1147           ret = ERROR_NOT_SUPPORTED;
1148           goto done;
1149        }
1150
1151        lim = buf + needed;
1152        for (next = buf; next < lim; next += rtm->rtm_msglen)
1153        {
1154           int i;
1155
1156           rtm = (struct rt_msghdr *)next;
1157
1158           if (rtm->rtm_type != RTM_GET)
1159           {
1160              WARN ("Got unexpected message type 0x%x!\n",
1161                    rtm->rtm_type);
1162              continue;
1163           }
1164
1165           /* Ignore all entries except for gateway routes which aren't
1166              multicast */
1167           if (!(rtm->rtm_flags & RTF_GATEWAY) ||
1168               (rtm->rtm_flags & RTF_MULTICAST))
1169              continue;
1170
1171           memset( &row, 0, sizeof(row) );
1172           row.dwForwardIfIndex = rtm->rtm_index;
1173           row.dwForwardType = MIB_IPROUTE_TYPE_INDIRECT;
1174           row.dwForwardMetric1 = rtm->rtm_rmx.rmx_hopcount;
1175           row.dwForwardProto = MIB_IPPROTO_LOCAL;
1176
1177           addrPtr = (char *)(rtm + 1);
1178
1179           for (i = 1; i; i <<= 1)
1180           {
1181              struct sockaddr *sa;
1182              DWORD addr;
1183
1184              if (!(i & rtm->rtm_addrs))
1185                 continue;
1186
1187              sa = (struct sockaddr *)addrPtr;
1188              ADVANCE (addrPtr, sa);
1189
1190              /* default routes are encoded by length-zero sockaddr */
1191              if (sa->sa_len == 0)
1192                 addr = 0;
1193              else if (sa->sa_family != AF_INET)
1194              {
1195                 WARN ("Received unsupported sockaddr family 0x%x\n",
1196                      sa->sa_family);
1197                 addr = 0;
1198              }
1199              else
1200              {
1201                 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
1202
1203                 addr = sin->sin_addr.s_addr;
1204              }
1205
1206              switch (i)
1207              {
1208                 case RTA_DST:     row.dwForwardDest = addr; break;
1209                 case RTA_GATEWAY: row.dwForwardNextHop = addr; break;
1210                 case RTA_NETMASK: row.dwForwardMask = addr; break;
1211                 default:
1212                    WARN ("Unexpected address type 0x%x\n", i);
1213              }
1214           }
1215
1216           if (!(table = append_ipforward_row( heap, flags, table, &count, &row )))
1217               break;
1218        }
1219 done:
1220       HeapFree( GetProcessHeap (), 0, buf );
1221     }
1222 #else
1223     FIXME( "not implemented\n" );
1224     ret = ERROR_NOT_SUPPORTED;
1225 #endif
1226
1227     if (!table) return ERROR_OUTOFMEMORY;
1228     if (!ret) *ppIpForwardTable = table;
1229     else HeapFree( heap, flags, table );
1230     return ret;
1231 }
1232
1233 DWORD getNumArpEntries(void)
1234 {
1235 #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1236   int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO};
1237 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
1238   DWORD arpEntries = 0;
1239   size_t needed;
1240   char *buf, *lim, *next;
1241   struct rt_msghdr *rtm;
1242   struct sockaddr_inarp *sinarp;
1243   struct sockaddr_dl *sdl;
1244
1245   if (sysctl (mib, MIB_LEN,  NULL, &needed, NULL, 0) == -1)
1246   {
1247      ERR ("failed to get size of arp table\n");
1248      return 0;
1249   }
1250
1251   buf = HeapAlloc (GetProcessHeap (), 0, needed);
1252   if (!buf) return 0;
1253
1254   if (sysctl (mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
1255   {
1256      ERR ("failed to get arp table\n");
1257      HeapFree (GetProcessHeap (), 0, buf);
1258      return 0;
1259   }
1260
1261   lim = buf + needed;
1262   next = buf;
1263   while(next < lim)
1264   {
1265       rtm = (struct rt_msghdr *)next;
1266       sinarp=(struct sockaddr_inarp *)(rtm + 1);
1267       sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len));
1268       if(sdl->sdl_alen) /* arp entry */
1269       arpEntries++;
1270       next += rtm->rtm_msglen;
1271   }
1272   HeapFree (GetProcessHeap (), 0, buf);
1273   return arpEntries;
1274 #endif
1275   return getNumWithOneHeader("/proc/net/arp");
1276 }
1277
1278 static MIB_IPNETTABLE *append_ipnet_row( HANDLE heap, DWORD flags, MIB_IPNETTABLE *table,
1279                                          DWORD *count, const MIB_IPNETROW *row )
1280 {
1281     if (table->dwNumEntries >= *count)
1282     {
1283         MIB_IPNETTABLE *new_table;
1284         DWORD new_count = table->dwNumEntries * 2;
1285
1286         if (!(new_table = HeapReAlloc( heap, flags, table,
1287                                        FIELD_OFFSET(MIB_IPNETTABLE, table[new_count] ))))
1288         {
1289             HeapFree( heap, 0, table );
1290             return NULL;
1291         }
1292         *count = new_count;
1293         table = new_table;
1294     }
1295     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1296     return table;
1297 }
1298
1299 DWORD getArpTable(PMIB_IPNETTABLE *ppIpNetTable, HANDLE heap, DWORD flags)
1300 {
1301     MIB_IPNETTABLE *table;
1302     MIB_IPNETROW row;
1303     DWORD ret = NO_ERROR, count = 16;
1304
1305     if (!ppIpNetTable) return ERROR_INVALID_PARAMETER;
1306
1307     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_IPNETTABLE, table[count] ))))
1308         return ERROR_OUTOFMEMORY;
1309
1310     table->dwNumEntries = 0;
1311
1312 #ifdef __linux__
1313     {
1314         FILE *fp;
1315
1316         if ((fp = fopen("/proc/net/arp", "r")))
1317         {
1318             char buf[512], *ptr;
1319             DWORD flags;
1320
1321             /* skip header line */
1322             ptr = fgets(buf, sizeof(buf), fp);
1323             while ((ptr = fgets(buf, sizeof(buf), fp)))
1324             {
1325                 memset( &row, 0, sizeof(row) );
1326
1327                 row.dwAddr = inet_addr(ptr);
1328                 while (*ptr && !isspace(*ptr)) ptr++;
1329                 strtoul(ptr + 1, &ptr, 16); /* hw type (skip) */
1330                 flags = strtoul(ptr + 1, &ptr, 16);
1331
1332 #ifdef ATF_COM
1333                 if (flags & ATF_COM) row.dwType = MIB_IPNET_TYPE_DYNAMIC;
1334                 else
1335 #endif
1336 #ifdef ATF_PERM
1337                 if (flags & ATF_PERM) row.dwType = MIB_IPNET_TYPE_STATIC;
1338                 else
1339 #endif
1340                     row.dwType = MIB_IPNET_TYPE_OTHER;
1341
1342                 while (*ptr && isspace(*ptr)) ptr++;
1343                 while (*ptr && !isspace(*ptr))
1344                 {
1345                     row.bPhysAddr[row.dwPhysAddrLen++] = strtoul(ptr, &ptr, 16);
1346                     if (*ptr) ptr++;
1347                 }
1348                 while (*ptr && isspace(*ptr)) ptr++;
1349                 while (*ptr && !isspace(*ptr)) ptr++;   /* mask (skip) */
1350                 while (*ptr && isspace(*ptr)) ptr++;
1351                 getInterfaceIndexByName(ptr, &row.dwIndex);
1352
1353                 if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
1354                     break;
1355             }
1356             fclose(fp);
1357         }
1358         else ret = ERROR_NOT_SUPPORTED;
1359     }
1360 #elif defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1361     {
1362       int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO};
1363 #define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
1364       size_t needed;
1365       char *buf = NULL, *lim, *next;
1366       struct rt_msghdr *rtm;
1367       struct sockaddr_inarp *sinarp;
1368       struct sockaddr_dl *sdl;
1369
1370       if (sysctl (mib, MIB_LEN,  NULL, &needed, NULL, 0) == -1)
1371       {
1372          ERR ("failed to get arp table\n");
1373          ret = ERROR_NOT_SUPPORTED;
1374          goto done;
1375       }
1376
1377       buf = HeapAlloc (GetProcessHeap (), 0, needed);
1378       if (!buf)
1379       {
1380           ret = ERROR_OUTOFMEMORY;
1381           goto done;
1382       }
1383
1384       if (sysctl (mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
1385       {
1386          ret = ERROR_NOT_SUPPORTED;
1387          goto done;
1388       }
1389
1390       lim = buf + needed;
1391       next = buf;
1392       while(next < lim)
1393       {
1394           rtm = (struct rt_msghdr *)next;
1395           sinarp=(struct sockaddr_inarp *)(rtm + 1);
1396           sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len));
1397           if(sdl->sdl_alen) /* arp entry */
1398           {
1399               memset( &row, 0, sizeof(row) );
1400               row.dwAddr = sinarp->sin_addr.s_addr;
1401               row.dwIndex = sdl->sdl_index;
1402               row.dwPhysAddrLen = min( 8, sdl->sdl_alen );
1403               memcpy( row.bPhysAddr, &sdl->sdl_data[sdl->sdl_nlen], row.dwPhysAddrLen );
1404               if(rtm->rtm_rmx.rmx_expire == 0) row.dwType = MIB_IPNET_TYPE_STATIC;
1405               else if(sinarp->sin_other & SIN_PROXY) row.dwType = MIB_IPNET_TYPE_OTHER;
1406               else if(rtm->rtm_rmx.rmx_expire != 0) row.dwType = MIB_IPNET_TYPE_DYNAMIC;
1407               else row.dwType = MIB_IPNET_TYPE_INVALID;
1408
1409               if (!(table = append_ipnet_row( heap, flags, table, &count, &row )))
1410                   break;
1411           }
1412           next += rtm->rtm_msglen;
1413       }
1414 done:
1415       HeapFree( GetProcessHeap (), 0, buf );
1416     }
1417 #else
1418     FIXME( "not implemented\n" );
1419     ret = ERROR_NOT_SUPPORTED;
1420 #endif
1421
1422     if (!table) return ERROR_OUTOFMEMORY;
1423     if (!ret) *ppIpNetTable = table;
1424     else HeapFree( heap, flags, table );
1425     return ret;
1426 }
1427
1428 DWORD getNumUdpEntries(void)
1429 {
1430 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
1431    return getNumWithOneHeader ("net.inet.udp.pcblist");
1432 #else
1433   return getNumWithOneHeader("/proc/net/udp");
1434 #endif
1435 }
1436
1437 static MIB_UDPTABLE *append_udp_row( HANDLE heap, DWORD flags, MIB_UDPTABLE *table,
1438                                      DWORD *count, const MIB_UDPROW *row )
1439 {
1440     if (table->dwNumEntries >= *count)
1441     {
1442         MIB_UDPTABLE *new_table;
1443         DWORD new_count = table->dwNumEntries * 2;
1444
1445         if (!(new_table = HeapReAlloc( heap, flags, table, FIELD_OFFSET(MIB_UDPTABLE, table[new_count] ))))
1446         {
1447             HeapFree( heap, 0, table );
1448             return NULL;
1449         }
1450         *count = new_count;
1451         table = new_table;
1452     }
1453     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1454     return table;
1455 }
1456
1457 static int compare_udp_rows(const void *a, const void *b)
1458 {
1459     const MIB_UDPROW *rowA = a;
1460     const MIB_UDPROW *rowB = b;
1461     int ret;
1462
1463     if ((ret = rowA->dwLocalAddr - rowB->dwLocalAddr) != 0) return ret;
1464     return rowA->dwLocalPort - rowB->dwLocalPort;
1465 }
1466
1467
1468 /******************************************************************
1469  *    AllocateAndGetUdpTableFromStack (IPHLPAPI.@)
1470  *
1471  * Get the UDP listener table.
1472  * Like GetUdpTable(), but allocate the returned table from heap.
1473  *
1474  * PARAMS
1475  *  ppUdpTable [Out] pointer into which the MIB_UDPTABLE is
1476  *                   allocated and returned.
1477  *  bOrder     [In]  whether to sort the table
1478  *  heap       [In]  heap from which the table is allocated
1479  *  flags      [In]  flags to HeapAlloc
1480  *
1481  * RETURNS
1482  *  ERROR_INVALID_PARAMETER if ppUdpTable is NULL, whatever GetUdpTable()
1483  *  returns otherwise.
1484  */
1485 DWORD WINAPI AllocateAndGetUdpTableFromStack(PMIB_UDPTABLE *ppUdpTable, BOOL bOrder,
1486                                              HANDLE heap, DWORD flags)
1487 {
1488     MIB_UDPTABLE *table;
1489     MIB_UDPROW row;
1490     DWORD ret = NO_ERROR, count = 16;
1491
1492     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppUdpTable, bOrder, heap, flags);
1493
1494     if (!ppUdpTable) return ERROR_INVALID_PARAMETER;
1495
1496     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_UDPTABLE, table[count] ))))
1497         return ERROR_OUTOFMEMORY;
1498
1499     table->dwNumEntries = 0;
1500
1501 #ifdef __linux__
1502     {
1503         FILE *fp;
1504
1505         if ((fp = fopen("/proc/net/udp", "r")))
1506         {
1507             char buf[512], *ptr;
1508             DWORD dummy;
1509
1510             /* skip header line */
1511             ptr = fgets(buf, sizeof(buf), fp);
1512             while ((ptr = fgets(buf, sizeof(buf), fp)))
1513             {
1514                 if (sscanf( ptr, "%u: %x:%x", &dummy, &row.dwLocalAddr, &row.dwLocalPort ) != 3)
1515                     continue;
1516                 row.dwLocalPort = htons( row.dwLocalPort );
1517                 if (!(table = append_udp_row( heap, flags, table, &count, &row )))
1518                     break;
1519             }
1520             fclose(fp);
1521         }
1522         else ret = ERROR_NOT_SUPPORTED;
1523     }
1524 #else
1525     FIXME( "not implemented\n" );
1526     ret = ERROR_NOT_SUPPORTED;
1527 #endif
1528
1529     if (!table) return ERROR_OUTOFMEMORY;
1530     if (!ret)
1531     {
1532         if (bOrder && table->dwNumEntries)
1533             qsort( table->table, table->dwNumEntries, sizeof(row), compare_udp_rows );
1534         *ppUdpTable = table;
1535     }
1536     else HeapFree( heap, flags, table );
1537     TRACE( "returning ret %u table %p\n", ret, table );
1538     return ret;
1539 }
1540
1541
1542 DWORD getNumTcpEntries(void)
1543 {
1544 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
1545    return getNumWithOneHeader ("net.inet.tcp.pcblist");
1546 #else
1547    return getNumWithOneHeader ("/proc/net/tcp");
1548 #endif
1549 }
1550
1551 static MIB_TCPTABLE *append_tcp_row( HANDLE heap, DWORD flags, MIB_TCPTABLE *table,
1552                                      DWORD *count, const MIB_TCPROW *row )
1553 {
1554     if (table->dwNumEntries >= *count)
1555     {
1556         MIB_TCPTABLE *new_table;
1557         DWORD new_count = table->dwNumEntries * 2;
1558
1559         if (!(new_table = HeapReAlloc( heap, flags, table, FIELD_OFFSET(MIB_TCPTABLE, table[new_count] ))))
1560         {
1561             HeapFree( heap, 0, table );
1562             return NULL;
1563         }
1564         *count = new_count;
1565         table = new_table;
1566     }
1567     memcpy( &table->table[table->dwNumEntries++], row, sizeof(*row) );
1568     return table;
1569 }
1570
1571
1572 /* Why not a lookup table? Because the TCPS_* constants are different
1573    on different platforms */
1574 static DWORD TCPStateToMIBState (int state)
1575 {
1576    switch (state)
1577    {
1578       case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB;
1579       case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT;
1580       case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD;
1581       case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1;
1582       case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2;
1583       case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT;
1584       case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT;
1585       case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK;
1586       case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN;
1587       case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING;
1588       default:
1589       case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED;
1590    }
1591 }
1592
1593
1594 static int compare_tcp_rows(const void *a, const void *b)
1595 {
1596     const MIB_TCPROW *rowA = a;
1597     const MIB_TCPROW *rowB = b;
1598     int ret;
1599
1600     if ((ret = ntohl (rowA->dwLocalAddr) - ntohl (rowB->dwLocalAddr)) != 0) return ret;
1601     if ((ret = ntohs ((unsigned short)rowA->dwLocalPort) -
1602                ntohs ((unsigned short)rowB->dwLocalPort)) != 0) return ret;
1603     if ((ret = ntohl (rowA->dwRemoteAddr) - ntohl (rowB->dwRemoteAddr)) != 0) return ret;
1604     return ntohs ((unsigned short)rowA->dwRemotePort) - ntohs ((unsigned short)rowB->dwRemotePort);
1605 }
1606
1607
1608 /******************************************************************
1609  *    AllocateAndGetTcpTableFromStack (IPHLPAPI.@)
1610  *
1611  * Get the TCP connection table.
1612  * Like GetTcpTable(), but allocate the returned table from heap.
1613  *
1614  * PARAMS
1615  *  ppTcpTable [Out] pointer into which the MIB_TCPTABLE is
1616  *                   allocated and returned.
1617  *  bOrder     [In]  whether to sort the table
1618  *  heap       [In]  heap from which the table is allocated
1619  *  flags      [In]  flags to HeapAlloc
1620  *
1621  * RETURNS
1622  *  ERROR_INVALID_PARAMETER if ppTcpTable is NULL, whatever GetTcpTable()
1623  *  returns otherwise.
1624  */
1625 DWORD WINAPI AllocateAndGetTcpTableFromStack( PMIB_TCPTABLE *ppTcpTable, BOOL bOrder,
1626                                               HANDLE heap, DWORD flags)
1627 {
1628     MIB_TCPTABLE *table;
1629     MIB_TCPROW row;
1630     DWORD ret = NO_ERROR, count = 16;
1631
1632     TRACE("table %p, bOrder %d, heap %p, flags 0x%08x\n", ppTcpTable, bOrder, heap, flags);
1633
1634     if (!ppTcpTable) return ERROR_INVALID_PARAMETER;
1635
1636     if (!(table = HeapAlloc( heap, flags, FIELD_OFFSET(MIB_TCPTABLE, table[count] ))))
1637         return ERROR_OUTOFMEMORY;
1638
1639     table->dwNumEntries = 0;
1640
1641 #ifdef __linux__
1642     {
1643         FILE *fp;
1644
1645         if ((fp = fopen("/proc/net/tcp", "r")))
1646         {
1647             char buf[512], *ptr;
1648             DWORD dummy;
1649
1650             /* skip header line */
1651             ptr = fgets(buf, sizeof(buf), fp);
1652             while ((ptr = fgets(buf, sizeof(buf), fp)))
1653             {
1654                 if (sscanf( ptr, "%x: %x:%x %x:%x %x", &dummy, &row.dwLocalAddr, &row.dwLocalPort,
1655                             &row.dwRemoteAddr, &row.dwRemotePort, &row.dwState ) != 6)
1656                     continue;
1657                 row.dwLocalPort = htons( row.dwLocalPort );
1658                 row.dwRemotePort = htons( row.dwRemotePort );
1659                 row.dwState = TCPStateToMIBState( row.dwState );
1660                 if (!(table = append_tcp_row( heap, flags, table, &count, &row )))
1661                     break;
1662             }
1663             fclose( fp );
1664         }
1665         else ret = ERROR_NOT_SUPPORTED;
1666     }
1667 #elif defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_STRUCT_XINPGEN)
1668     {
1669         size_t Len = 0;
1670         char *Buf = NULL;
1671         struct xinpgen *pXIG, *pOrigXIG;
1672
1673         if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0)
1674         {
1675             ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1676             ret = ERROR_NOT_SUPPORTED;
1677             goto done;
1678         }
1679
1680         Buf = HeapAlloc (GetProcessHeap (), 0, Len);
1681         if (!Buf)
1682         {
1683             ret = ERROR_OUTOFMEMORY;
1684             goto done;
1685         }
1686
1687         if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0)
1688         {
1689             ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1690             ret = ERROR_NOT_SUPPORTED;
1691             goto done;
1692         }
1693
1694         /* Might be nothing here; first entry is just a header it seems */
1695         if (Len <= sizeof (struct xinpgen)) goto done;
1696
1697         pOrigXIG = (struct xinpgen *)Buf;
1698         pXIG = pOrigXIG;
1699
1700         for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
1701              pXIG->xig_len > sizeof (struct xinpgen);
1702              pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
1703         {
1704             struct tcpcb *pTCPData = NULL;
1705             struct inpcb *pINData;
1706             struct xsocket *pSockData;
1707
1708             pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
1709             pINData = &((struct xtcpcb *)pXIG)->xt_inp;
1710             pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
1711
1712             /* Ignore sockets for other protocols */
1713             if (pSockData->xso_protocol != IPPROTO_TCP)
1714                 continue;
1715
1716             /* Ignore PCBs that were freed while generating the data */
1717             if (pINData->inp_gencnt > pOrigXIG->xig_gen)
1718                 continue;
1719
1720             /* we're only interested in IPv4 addresses */
1721             if (!(pINData->inp_vflag & INP_IPV4) ||
1722                 (pINData->inp_vflag & INP_IPV6))
1723                 continue;
1724
1725             /* If all 0's, skip it */
1726             if (!pINData->inp_laddr.s_addr &&
1727                 !pINData->inp_lport &&
1728                 !pINData->inp_faddr.s_addr &&
1729                 !pINData->inp_fport)
1730                 continue;
1731
1732             /* Fill in structure details */
1733             row.dwLocalAddr = pINData->inp_laddr.s_addr;
1734             row.dwLocalPort = pINData->inp_lport;
1735             row.dwRemoteAddr = pINData->inp_faddr.s_addr;
1736             row.dwRemotePort = pINData->inp_fport;
1737             row.dwState = TCPStateToMIBState (pTCPData->t_state);
1738             if (!(table = append_tcp_row( heap, flags, table, &count, &row ))) break;
1739         }
1740
1741     done:
1742         HeapFree (GetProcessHeap (), 0, Buf);
1743     }
1744 #else
1745     FIXME( "not implemented\n" );
1746     ret = ERROR_NOT_SUPPORTED;
1747 #endif
1748
1749     if (!table) return ERROR_OUTOFMEMORY;
1750     if (!ret)
1751     {
1752         if (bOrder && table->dwNumEntries)
1753             qsort( table->table, table->dwNumEntries, sizeof(row), compare_tcp_rows );
1754         *ppTcpTable = table;
1755     }
1756     else HeapFree( heap, flags, table );
1757     TRACE( "returning ret %u table %p\n", ret, table );
1758     return ret;
1759 }