kernel32/tests: Get the test to run on Windows 95.
[wine] / dlls / iphlpapi / ipstats.c
1 /* Copyright (C) 2003,2006 Juan Lang
2  * Copyright (C) 2007 TransGaming Technologies Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  * This file implements statistics getting using the /proc filesystem exported
19  * by Linux, and maybe other OSes.
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24 #include "wine/debug.h"
25
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
33 #endif
34 #ifdef HAVE_SYS_SOCKETVAR_H
35 #include <sys/socketvar.h>
36 #endif
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
42 #endif
43 #ifdef HAVE_NET_IF_H
44 #include <net/if.h>
45 #endif
46 #ifdef HAVE_NET_ROUTE_H
47 #include <net/route.h>
48 #endif
49 #ifdef HAVE_NET_IF_ARP_H
50 #include <net/if_arp.h>
51 #endif
52 #ifdef HAVE_NETINET_TCP_H
53 #include <netinet/tcp.h>
54 #endif
55 #ifdef HAVE_NETINET_TCP_FSM_H
56 #include <netinet/tcp_fsm.h>
57 #endif
58
59 #ifdef HAVE_NETINET_IN_PCB_H
60 #include <netinet/in_pcb.h>
61 #endif
62 #ifdef HAVE_NETINET_TCP_VAR_H
63 #include <netinet/tcp_var.h>
64 #endif
65 #ifdef HAVE_NETINET_IP_VAR_H
66 #include <netinet/ip_var.h>
67 #endif
68
69 #ifdef HAVE_SYS_SYSCTL_H
70 #include <sys/sysctl.h>
71 #endif
72
73 #ifndef ROUNDUP
74 #define ROUNDUP(a) \
75         ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
76 #endif
77 #ifndef ADVANCE
78 #define ADVANCE(x, n) (x += ROUNDUP(((struct sockaddr *)n)->sa_len))
79 #endif
80
81 #include "windef.h"
82 #include "winbase.h"
83 #include "iprtrmib.h"
84 #include "ifenum.h"
85 #include "ipstats.h"
86
87 #ifndef HAVE_NETINET_TCP_FSM_H
88 #define TCPS_ESTABLISHED  1
89 #define TCPS_SYN_SENT     2
90 #define TCPS_SYN_RECEIVED 3
91 #define TCPS_FIN_WAIT_1   4
92 #define TCPS_FIN_WAIT_2   5
93 #define TCPS_TIME_WAIT    6
94 #define TCPS_CLOSED       7
95 #define TCPS_CLOSE_WAIT   8
96 #define TCPS_LAST_ACK     9
97 #define TCPS_LISTEN      10
98 #define TCPS_CLOSING     11
99 #endif
100
101 WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
102
103 DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry)
104 {
105   FILE *fp;
106
107   if (!name)
108     return ERROR_INVALID_PARAMETER;
109   if (!entry)
110     return ERROR_INVALID_PARAMETER;
111
112   /* get interface stats from /proc/net/dev, no error if can't
113      no inUnknownProtos, outNUcastPkts, outQLen */
114   fp = fopen("/proc/net/dev", "r");
115   if (fp) {
116     char buf[512] = { 0 }, *ptr;
117     int nameLen = strlen(name), nameFound = 0;
118
119
120     ptr = fgets(buf, sizeof(buf), fp);
121     while (ptr && !nameFound) {
122       while (*ptr && isspace(*ptr))
123         ptr++;
124       if (strncasecmp(ptr, name, nameLen) == 0 && *(ptr + nameLen) == ':')
125         nameFound = 1;
126       else
127         ptr = fgets(buf, sizeof(buf), fp);
128     }
129     if (nameFound) {
130       char *endPtr;
131
132       ptr += nameLen + 1;
133       if (ptr && *ptr) {
134         entry->dwInOctets = strtoul(ptr, &endPtr, 10);
135         ptr = endPtr;
136       }
137       if (ptr && *ptr) {
138         entry->dwInUcastPkts = strtoul(ptr, &endPtr, 10);
139         ptr = endPtr;
140       }
141       if (ptr && *ptr) {
142         entry->dwInErrors = strtoul(ptr, &endPtr, 10);
143         ptr = endPtr;
144       }
145       if (ptr && *ptr) {
146         entry->dwInDiscards = strtoul(ptr, &endPtr, 10);
147         ptr = endPtr;
148       }
149       if (ptr && *ptr) {
150         strtoul(ptr, &endPtr, 10); /* skip */
151         ptr = endPtr;
152       }
153       if (ptr && *ptr) {
154         strtoul(ptr, &endPtr, 10); /* skip */
155         ptr = endPtr;
156       }
157       if (ptr && *ptr) {
158         strtoul(ptr, &endPtr, 10); /* skip */
159         ptr = endPtr;
160       }
161       if (ptr && *ptr) {
162         entry->dwInNUcastPkts = strtoul(ptr, &endPtr, 10);
163         ptr = endPtr;
164       }
165       if (ptr && *ptr) {
166         entry->dwOutOctets = strtoul(ptr, &endPtr, 10);
167         ptr = endPtr;
168       }
169       if (ptr && *ptr) {
170         entry->dwOutUcastPkts = strtoul(ptr, &endPtr, 10);
171         ptr = endPtr;
172       }
173       if (ptr && *ptr) {
174         entry->dwOutErrors = strtoul(ptr, &endPtr, 10);
175         ptr = endPtr;
176       }
177       if (ptr && *ptr) {
178         entry->dwOutDiscards = strtoul(ptr, &endPtr, 10);
179         ptr = endPtr;
180       }
181     }
182     fclose(fp);
183   }
184   else
185   {
186      ERR ("unimplemented!\n");
187      return ERROR_NOT_SUPPORTED;
188   }
189
190   return NO_ERROR;
191 }
192
193 DWORD getICMPStats(MIB_ICMP *stats)
194 {
195   FILE *fp;
196
197   if (!stats)
198     return ERROR_INVALID_PARAMETER;
199
200   memset(stats, 0, sizeof(MIB_ICMP));
201   /* get most of these stats from /proc/net/snmp, no error if can't */
202   fp = fopen("/proc/net/snmp", "r");
203   if (fp) {
204     static const char hdr[] = "Icmp:";
205     char buf[512] = { 0 }, *ptr;
206
207     do {
208       ptr = fgets(buf, sizeof(buf), fp);
209     } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
210     if (ptr) {
211       /* last line was a header, get another */
212       ptr = fgets(buf, sizeof(buf), fp);
213       if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
214         char *endPtr;
215
216         ptr += sizeof(hdr);
217         if (ptr && *ptr) {
218           stats->stats.icmpInStats.dwMsgs = strtoul(ptr, &endPtr, 10);
219           ptr = endPtr;
220         }
221         if (ptr && *ptr) {
222           stats->stats.icmpInStats.dwErrors = strtoul(ptr, &endPtr, 10);
223           ptr = endPtr;
224         }
225         if (ptr && *ptr) {
226           stats->stats.icmpInStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
227           ptr = endPtr;
228         }
229         if (ptr && *ptr) {
230           stats->stats.icmpInStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
231           ptr = endPtr;
232         }
233         if (ptr && *ptr) {
234           stats->stats.icmpInStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
235           ptr = endPtr;
236         }
237         if (ptr && *ptr) {
238           stats->stats.icmpInStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
239           ptr = endPtr;
240         }
241         if (ptr && *ptr) {
242           stats->stats.icmpInStats.dwRedirects = strtoul(ptr, &endPtr, 10);
243           ptr = endPtr;
244         }
245         if (ptr && *ptr) {
246           stats->stats.icmpInStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
247           ptr = endPtr;
248         }
249         if (ptr && *ptr) {
250           stats->stats.icmpInStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
251           ptr = endPtr;
252         }
253         if (ptr && *ptr) {
254           stats->stats.icmpInStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
255           ptr = endPtr;
256         }
257         if (ptr && *ptr) {
258           stats->stats.icmpInStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
259           ptr = endPtr;
260         }
261         if (ptr && *ptr) {
262           stats->stats.icmpInStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
263           ptr = endPtr;
264         }
265         if (ptr && *ptr) {
266           stats->stats.icmpOutStats.dwMsgs = strtoul(ptr, &endPtr, 10);
267           ptr = endPtr;
268         }
269         if (ptr && *ptr) {
270           stats->stats.icmpOutStats.dwErrors = strtoul(ptr, &endPtr, 10);
271           ptr = endPtr;
272         }
273         if (ptr && *ptr) {
274           stats->stats.icmpOutStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
275           ptr = endPtr;
276         }
277         if (ptr && *ptr) {
278           stats->stats.icmpOutStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
279           ptr = endPtr;
280         }
281         if (ptr && *ptr) {
282           stats->stats.icmpOutStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
283           ptr = endPtr;
284         }
285         if (ptr && *ptr) {
286           stats->stats.icmpOutStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
287           ptr = endPtr;
288         }
289         if (ptr && *ptr) {
290           stats->stats.icmpOutStats.dwRedirects = strtoul(ptr, &endPtr, 10);
291           ptr = endPtr;
292         }
293         if (ptr && *ptr) {
294           stats->stats.icmpOutStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
295           ptr = endPtr;
296         }
297         if (ptr && *ptr) {
298           stats->stats.icmpOutStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
299           ptr = endPtr;
300         }
301         if (ptr && *ptr) {
302           stats->stats.icmpOutStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
303           ptr = endPtr;
304         }
305         if (ptr && *ptr) {
306           stats->stats.icmpOutStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
307           ptr = endPtr;
308         }
309         if (ptr && *ptr) {
310           stats->stats.icmpOutStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
311           ptr = endPtr;
312         }
313       }
314     }
315     fclose(fp);
316   }
317   else
318   {
319      ERR ("unimplemented!\n");
320      return ERROR_NOT_SUPPORTED;
321   }
322
323   return NO_ERROR;
324 }
325
326 DWORD getIPStats(PMIB_IPSTATS stats)
327 {
328   FILE *fp;
329
330   if (!stats)
331     return ERROR_INVALID_PARAMETER;
332
333   memset(stats, 0, sizeof(MIB_IPSTATS));
334   stats->dwNumIf = stats->dwNumAddr = getNumInterfaces();
335   stats->dwNumRoutes = getNumRoutes();
336
337   /* get most of these stats from /proc/net/snmp, no error if can't */
338   fp = fopen("/proc/net/snmp", "r");
339   if (fp) {
340     static const char hdr[] = "Ip:";
341     char buf[512] = { 0 }, *ptr;
342
343     do {
344       ptr = fgets(buf, sizeof(buf), fp);
345     } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
346     if (ptr) {
347       /* last line was a header, get another */
348       ptr = fgets(buf, sizeof(buf), fp);
349       if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
350         char *endPtr;
351
352         ptr += sizeof(hdr);
353         if (ptr && *ptr) {
354           stats->dwForwarding = strtoul(ptr, &endPtr, 10);
355           ptr = endPtr;
356         }
357         if (ptr && *ptr) {
358           stats->dwDefaultTTL = strtoul(ptr, &endPtr, 10);
359           ptr = endPtr;
360         }
361         if (ptr && *ptr) {
362           stats->dwInReceives = strtoul(ptr, &endPtr, 10);
363           ptr = endPtr;
364         }
365         if (ptr && *ptr) {
366           stats->dwInHdrErrors = strtoul(ptr, &endPtr, 10);
367           ptr = endPtr;
368         }
369         if (ptr && *ptr) {
370           stats->dwInAddrErrors = strtoul(ptr, &endPtr, 10);
371           ptr = endPtr;
372         }
373         if (ptr && *ptr) {
374           stats->dwForwDatagrams = strtoul(ptr, &endPtr, 10);
375           ptr = endPtr;
376         }
377         if (ptr && *ptr) {
378           stats->dwInUnknownProtos = strtoul(ptr, &endPtr, 10);
379           ptr = endPtr;
380         }
381         if (ptr && *ptr) {
382           stats->dwInDiscards = strtoul(ptr, &endPtr, 10);
383           ptr = endPtr;
384         }
385         if (ptr && *ptr) {
386           stats->dwInDelivers = strtoul(ptr, &endPtr, 10);
387           ptr = endPtr;
388         }
389         if (ptr && *ptr) {
390           stats->dwOutRequests = strtoul(ptr, &endPtr, 10);
391           ptr = endPtr;
392         }
393         if (ptr && *ptr) {
394           stats->dwOutDiscards = strtoul(ptr, &endPtr, 10);
395           ptr = endPtr;
396         }
397         if (ptr && *ptr) {
398           stats->dwOutNoRoutes = strtoul(ptr, &endPtr, 10);
399           ptr = endPtr;
400         }
401         if (ptr && *ptr) {
402           stats->dwReasmTimeout = strtoul(ptr, &endPtr, 10);
403           ptr = endPtr;
404         }
405         if (ptr && *ptr) {
406           stats->dwReasmReqds = strtoul(ptr, &endPtr, 10);
407           ptr = endPtr;
408         }
409         if (ptr && *ptr) {
410           stats->dwReasmOks = strtoul(ptr, &endPtr, 10);
411           ptr = endPtr;
412         }
413         if (ptr && *ptr) {
414           stats->dwReasmFails = strtoul(ptr, &endPtr, 10);
415           ptr = endPtr;
416         }
417         if (ptr && *ptr) {
418           stats->dwFragOks = strtoul(ptr, &endPtr, 10);
419           ptr = endPtr;
420         }
421         if (ptr && *ptr) {
422           stats->dwFragFails = strtoul(ptr, &endPtr, 10);
423           ptr = endPtr;
424         }
425         if (ptr && *ptr) {
426           stats->dwFragCreates = strtoul(ptr, &endPtr, 10);
427           ptr = endPtr;
428         }
429         /* hmm, no routingDiscards */
430       }
431     }
432     fclose(fp);
433   }
434   else
435   {
436      ERR ("unimplemented!\n");
437      return ERROR_NOT_SUPPORTED;
438   }
439
440   return NO_ERROR;
441 }
442
443 DWORD getTCPStats(MIB_TCPSTATS *stats)
444 {
445   FILE *fp;
446
447   if (!stats)
448     return ERROR_INVALID_PARAMETER;
449
450   memset(stats, 0, sizeof(MIB_TCPSTATS));
451
452   /* get from /proc/net/snmp, no error if can't */
453   fp = fopen("/proc/net/snmp", "r");
454   if (fp) {
455     static const char hdr[] = "Tcp:";
456     char buf[512] = { 0 }, *ptr;
457
458
459     do {
460       ptr = fgets(buf, sizeof(buf), fp);
461     } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
462     if (ptr) {
463       /* last line was a header, get another */
464       ptr = fgets(buf, sizeof(buf), fp);
465       if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
466         char *endPtr;
467
468         ptr += sizeof(hdr);
469         if (ptr && *ptr) {
470           stats->dwRtoAlgorithm = strtoul(ptr, &endPtr, 10);
471           ptr = endPtr;
472         }
473         if (ptr && *ptr) {
474           stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
475           ptr = endPtr;
476         }
477         if (ptr && *ptr) {
478           stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
479           ptr = endPtr;
480         }
481         if (ptr && *ptr) {
482           stats->dwMaxConn = strtoul(ptr, &endPtr, 10);
483           ptr = endPtr;
484         }
485         if (ptr && *ptr) {
486           stats->dwActiveOpens = strtoul(ptr, &endPtr, 10);
487           ptr = endPtr;
488         }
489         if (ptr && *ptr) {
490           stats->dwPassiveOpens = strtoul(ptr, &endPtr, 10);
491           ptr = endPtr;
492         }
493         if (ptr && *ptr) {
494           stats->dwAttemptFails = strtoul(ptr, &endPtr, 10);
495           ptr = endPtr;
496         }
497         if (ptr && *ptr) {
498           stats->dwEstabResets = strtoul(ptr, &endPtr, 10);
499           ptr = endPtr;
500         }
501         if (ptr && *ptr) {
502           stats->dwCurrEstab = strtoul(ptr, &endPtr, 10);
503           ptr = endPtr;
504         }
505         if (ptr && *ptr) {
506           stats->dwInSegs = strtoul(ptr, &endPtr, 10);
507           ptr = endPtr;
508         }
509         if (ptr && *ptr) {
510           stats->dwOutSegs = strtoul(ptr, &endPtr, 10);
511           ptr = endPtr;
512         }
513         if (ptr && *ptr) {
514           stats->dwRetransSegs = strtoul(ptr, &endPtr, 10);
515           ptr = endPtr;
516         }
517         if (ptr && *ptr) {
518           stats->dwInErrs = strtoul(ptr, &endPtr, 10);
519           ptr = endPtr;
520         }
521         if (ptr && *ptr) {
522           stats->dwOutRsts = strtoul(ptr, &endPtr, 10);
523           ptr = endPtr;
524         }
525         stats->dwNumConns = getNumTcpEntries();
526       }
527     }
528     fclose(fp);
529   }
530   else
531   {
532      ERR ("unimplemented!\n");
533      return ERROR_NOT_SUPPORTED;
534   }
535
536   return NO_ERROR;
537 }
538
539 DWORD getUDPStats(MIB_UDPSTATS *stats)
540 {
541   FILE *fp;
542
543   if (!stats)
544     return ERROR_INVALID_PARAMETER;
545
546   memset(stats, 0, sizeof(MIB_UDPSTATS));
547
548   /* get from /proc/net/snmp, no error if can't */
549   fp = fopen("/proc/net/snmp", "r");
550   if (fp) {
551     static const char hdr[] = "Udp:";
552     char buf[512] = { 0 }, *ptr;
553
554
555     do {
556       ptr = fgets(buf, sizeof(buf), fp);
557     } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
558     if (ptr) {
559       /* last line was a header, get another */
560       ptr = fgets(buf, sizeof(buf), fp);
561       if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
562         char *endPtr;
563
564         ptr += sizeof(hdr);
565         if (ptr && *ptr) {
566           stats->dwInDatagrams = strtoul(ptr, &endPtr, 10);
567           ptr = endPtr;
568         }
569         if (ptr && *ptr) {
570           stats->dwNoPorts = strtoul(ptr, &endPtr, 10);
571           ptr = endPtr;
572         }
573         if (ptr && *ptr) {
574           stats->dwInErrors = strtoul(ptr, &endPtr, 10);
575           ptr = endPtr;
576         }
577         if (ptr && *ptr) {
578           stats->dwOutDatagrams = strtoul(ptr, &endPtr, 10);
579           ptr = endPtr;
580         }
581         if (ptr && *ptr) {
582           stats->dwNumAddrs = strtoul(ptr, &endPtr, 10);
583           ptr = endPtr;
584         }
585       }
586     }
587     fclose(fp);
588   }
589   else
590   {
591      ERR ("unimplemented!\n");
592      return ERROR_NOT_SUPPORTED;
593   }
594
595   return NO_ERROR;
596 }
597
598 static DWORD getNumWithOneHeader(const char *filename)
599 {
600 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
601    size_t Len = 0;
602    char *Buf;
603    struct xinpgen *pXIG, *pOrigXIG;
604    int Protocol;
605    DWORD NumEntries = 0;
606
607    if (!strcmp (filename, "net.inet.tcp.pcblist"))
608       Protocol = IPPROTO_TCP;
609    else if (!strcmp (filename, "net.inet.udp.pcblist"))
610       Protocol = IPPROTO_UDP;
611    else
612    {
613       ERR ("Unsupported mib '%s', needs protocol mapping\n",
614            filename);
615       return 0;
616    }
617
618    if (sysctlbyname (filename, NULL, &Len, NULL, 0) < 0)
619    {
620       WARN ("Unable to read '%s' via sysctlbyname\n", filename);
621       return 0;
622    }
623
624    Buf = HeapAlloc (GetProcessHeap (), 0, Len);
625    if (!Buf)
626    {
627       ERR ("Out of memory!\n");
628       return 0;
629    }
630
631    if (sysctlbyname (filename, Buf, &Len, NULL, 0) < 0)
632    {
633       ERR ("Failure to read '%s' via sysctlbyname!\n", filename);
634       HeapFree (GetProcessHeap (), 0, Buf);
635       return 0;
636    }
637
638    /* Might be nothing here; first entry is just a header it seems */
639    if (Len <= sizeof (struct xinpgen))
640    {
641       HeapFree (GetProcessHeap (), 0, Buf);
642       return 0;
643    }
644
645    pOrigXIG = (struct xinpgen *)Buf;
646    pXIG = pOrigXIG;
647
648    for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
649         pXIG->xig_len > sizeof (struct xinpgen);
650         pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
651    {
652       struct tcpcb *pTCPData = NULL;
653       struct inpcb *pINData;
654       struct xsocket *pSockData;
655
656       if (Protocol == IPPROTO_TCP)
657       {
658          pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
659          pINData = &((struct xtcpcb *)pXIG)->xt_inp;
660          pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
661       }
662       else
663       {
664          pINData = &((struct xinpcb *)pXIG)->xi_inp;
665          pSockData = &((struct xinpcb *)pXIG)->xi_socket;
666       }
667
668       /* Ignore sockets for other protocols */
669       if (pSockData->xso_protocol != Protocol)
670          continue;
671
672       /* Ignore PCBs that were freed while generating the data */
673       if (pINData->inp_gencnt > pOrigXIG->xig_gen)
674          continue;
675
676       /* we're only interested in IPv4 addresses */
677       if (!(pINData->inp_vflag & INP_IPV4) ||
678           (pINData->inp_vflag & INP_IPV6))
679          continue;
680
681       /* If all 0's, skip it */
682       if (!pINData->inp_laddr.s_addr &&
683           !pINData->inp_lport &&
684           !pINData->inp_faddr.s_addr &&
685           !pINData->inp_fport)
686          continue;
687
688       NumEntries++;
689    }
690
691    HeapFree (GetProcessHeap (), 0, Buf);
692    return NumEntries;
693 #else
694   FILE *fp;
695   int ret = 0;
696
697   fp = fopen(filename, "r");
698   if (fp) {
699     char buf[512] = { 0 }, *ptr;
700
701
702     ptr = fgets(buf, sizeof(buf), fp);
703     if (ptr) {
704       do {
705         ptr = fgets(buf, sizeof(buf), fp);
706         if (ptr)
707           ret++;
708       } while (ptr);
709     }
710     fclose(fp);
711   }
712   else
713      ERR ("Unable to open '%s' to count entries!\n", filename);
714
715   return ret;
716 #endif
717 }
718
719 DWORD getNumRoutes(void)
720 {
721 #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
722    int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
723    size_t needed;
724    char *buf, *lim, *next;
725    struct rt_msghdr *rtm;
726    DWORD RouteCount = 0;
727
728    if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
729    {
730       ERR ("sysctl 1 failed!\n");
731       return 0;
732    }
733
734    buf = HeapAlloc (GetProcessHeap (), 0, needed);
735    if (!buf) return 0;
736
737    if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
738    {
739       ERR ("sysctl 2 failed!\n");
740       HeapFree (GetProcessHeap (), 0, buf);
741       return 0;
742    }
743
744    lim = buf + needed;
745    for (next = buf; next < lim; next += rtm->rtm_msglen)
746    {
747       rtm = (struct rt_msghdr *)next;
748
749       if (rtm->rtm_type != RTM_GET)
750       {
751          WARN ("Got unexpected message type 0x%x!\n",
752                rtm->rtm_type);
753          continue;
754       }
755
756       /* Ignore all entries except for gateway routes which aren't
757          multicast */
758       if (!(rtm->rtm_flags & RTF_GATEWAY) || (rtm->rtm_flags & RTF_MULTICAST))
759          continue;
760
761       RouteCount++;
762    }
763
764    HeapFree (GetProcessHeap (), 0, buf);
765    return RouteCount;
766 #else
767    return getNumWithOneHeader("/proc/net/route");
768 #endif
769 }
770
771 DWORD getRouteTable(PMIB_IPFORWARDTABLE *ppIpForwardTable, HANDLE heap,
772  DWORD flags)
773 {
774   DWORD ret;
775
776   if (!ppIpForwardTable)
777     ret = ERROR_INVALID_PARAMETER;
778   else {
779     DWORD numRoutes = getNumRoutes();
780     DWORD size = sizeof(MIB_IPFORWARDTABLE);
781     PMIB_IPFORWARDTABLE table;
782
783     if (numRoutes > 1)
784       size += (numRoutes - 1) * sizeof(MIB_IPFORWARDROW);
785     table = HeapAlloc(heap, flags, size);
786     if (table) {
787 #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
788        int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
789        size_t needed;
790        char *buf, *lim, *next, *addrPtr;
791        struct rt_msghdr *rtm;
792
793        if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
794        {
795           ERR ("sysctl 1 failed!\n");
796           HeapFree (GetProcessHeap (), 0, table);
797           return NO_ERROR;
798        }
799
800        buf = HeapAlloc (GetProcessHeap (), 0, needed);
801        if (!buf)
802        {
803           HeapFree (GetProcessHeap (), 0, table);
804           return ERROR_OUTOFMEMORY;
805        }
806
807        if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
808        {
809           ERR ("sysctl 2 failed!\n");
810           HeapFree (GetProcessHeap (), 0, table);
811           HeapFree (GetProcessHeap (), 0, buf);
812           return NO_ERROR;
813        }
814
815        *ppIpForwardTable = table;
816        table->dwNumEntries = 0;
817
818        lim = buf + needed;
819        for (next = buf; next < lim; next += rtm->rtm_msglen)
820        {
821           int i;
822
823           rtm = (struct rt_msghdr *)next;
824
825           if (rtm->rtm_type != RTM_GET)
826           {
827              WARN ("Got unexpected message type 0x%x!\n",
828                    rtm->rtm_type);
829              continue;
830           }
831
832           /* Ignore all entries except for gateway routes which aren't
833              multicast */
834           if (!(rtm->rtm_flags & RTF_GATEWAY) ||
835               (rtm->rtm_flags & RTF_MULTICAST))
836              continue;
837
838           memset (&table->table[table->dwNumEntries], 0,
839                   sizeof (MIB_IPFORWARDROW));
840           table->table[table->dwNumEntries].dwForwardIfIndex = rtm->rtm_index;
841           table->table[table->dwNumEntries].dwForwardType =
842              MIB_IPROUTE_TYPE_INDIRECT;
843           table->table[table->dwNumEntries].dwForwardMetric1 =
844              rtm->rtm_rmx.rmx_hopcount;
845           table->table[table->dwNumEntries].dwForwardProto =
846              MIB_IPPROTO_LOCAL;
847
848           addrPtr = (char *)(rtm + 1);
849
850           for (i = 1; i; i <<= 1)
851           {
852              struct sockaddr *sa;
853              DWORD addr;
854
855              if (!(i & rtm->rtm_addrs))
856                 continue;
857
858              sa = (struct sockaddr *)addrPtr;
859              ADVANCE (addrPtr, sa);
860
861              /* default routes are encoded by length-zero sockaddr */
862              if (sa->sa_len == 0)
863                 addr = 0;
864              else if (sa->sa_family != AF_INET)
865              {
866                 ERR ("Received unsupported sockaddr family 0x%x\n",
867                      sa->sa_family);
868                 addr = 0;
869              }
870              else
871              {
872                 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
873
874                 addr = sin->sin_addr.s_addr;
875              }
876
877              switch (i)
878              {
879                 case RTA_DST:
880                    table->table[table->dwNumEntries].dwForwardDest = addr;
881                    break;
882
883                 case RTA_GATEWAY:
884                    table->table[table->dwNumEntries].dwForwardNextHop = addr;
885                    break;
886
887                 case RTA_NETMASK:
888                    table->table[table->dwNumEntries].dwForwardMask = addr;
889                    break;
890
891                 default:
892                    ERR ("Unexpected address type 0x%x\n", i);
893              }
894           }
895
896           table->dwNumEntries++;
897        }
898
899        HeapFree (GetProcessHeap (), 0, buf);
900        ret = NO_ERROR;
901 #else
902       FILE *fp;
903
904       ret = NO_ERROR;
905       *ppIpForwardTable = table;
906       table->dwNumEntries = 0;
907       /* get from /proc/net/route, no error if can't */
908       fp = fopen("/proc/net/route", "r");
909       if (fp) {
910         char buf[512] = { 0 }, *ptr;
911
912         /* skip header line */
913         ptr = fgets(buf, sizeof(buf), fp);
914         while (ptr && table->dwNumEntries < numRoutes) {
915           memset(&table->table[table->dwNumEntries], 0,
916            sizeof(MIB_IPFORWARDROW));
917           ptr = fgets(buf, sizeof(buf), fp);
918           if (ptr) {
919             DWORD index;
920
921             while (!isspace(*ptr))
922               ptr++;
923             *ptr = '\0';
924             ptr++;
925             if (getInterfaceIndexByName(buf, &index) == NO_ERROR) {
926               char *endPtr;
927
928               table->table[table->dwNumEntries].dwForwardIfIndex = index;
929               if (*ptr) {
930                 table->table[table->dwNumEntries].dwForwardDest =
931                  strtoul(ptr, &endPtr, 16);
932                 ptr = endPtr;
933               }
934               if (ptr && *ptr) {
935                 table->table[table->dwNumEntries].dwForwardNextHop =
936                  strtoul(ptr, &endPtr, 16);
937                 ptr = endPtr;
938               }
939               if (ptr && *ptr) {
940                 DWORD flags = strtoul(ptr, &endPtr, 16);
941
942                 if (!(flags & RTF_UP))
943                   table->table[table->dwNumEntries].dwForwardType =
944                    MIB_IPROUTE_TYPE_INVALID;
945                 else if (flags & RTF_GATEWAY)
946                   table->table[table->dwNumEntries].dwForwardType =
947                    MIB_IPROUTE_TYPE_INDIRECT;
948                 else
949                   table->table[table->dwNumEntries].dwForwardType =
950                    MIB_IPROUTE_TYPE_DIRECT;
951                 ptr = endPtr;
952               }
953               if (ptr && *ptr) {
954                 strtoul(ptr, &endPtr, 16); /* refcount, skip */
955                 ptr = endPtr;
956               }
957               if (ptr && *ptr) {
958                 strtoul(ptr, &endPtr, 16); /* use, skip */
959                 ptr = endPtr;
960               }
961               if (ptr && *ptr) {
962                 table->table[table->dwNumEntries].dwForwardMetric1 =
963                  strtoul(ptr, &endPtr, 16);
964                 ptr = endPtr;
965               }
966               if (ptr && *ptr) {
967                 table->table[table->dwNumEntries].dwForwardMask =
968                  strtoul(ptr, &endPtr, 16);
969                 ptr = endPtr;
970               }
971               /* FIXME: other protos might be appropriate, e.g. the default
972                * route is typically set with MIB_IPPROTO_NETMGMT instead */
973               table->table[table->dwNumEntries].dwForwardProto =
974                MIB_IPPROTO_LOCAL;
975               table->dwNumEntries++;
976             }
977           }
978         }
979         fclose(fp);
980       }
981       else
982       {
983         ERR ("unimplemented!\n");
984         return ERROR_NOT_SUPPORTED;
985       }
986 #endif
987     }
988     else
989       ret = ERROR_OUTOFMEMORY;
990   }
991   return ret;
992 }
993
994 DWORD getNumArpEntries(void)
995 {
996   return getNumWithOneHeader("/proc/net/arp");
997 }
998
999 DWORD getArpTable(PMIB_IPNETTABLE *ppIpNetTable, HANDLE heap, DWORD flags)
1000 {
1001   DWORD ret;
1002
1003 #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1004   ERR ("unimplemented!\n");
1005   return ERROR_NOT_SUPPORTED;
1006 #endif
1007
1008   if (!ppIpNetTable)
1009     ret = ERROR_INVALID_PARAMETER;
1010   else {
1011     DWORD numEntries = getNumArpEntries();
1012     DWORD size = sizeof(MIB_IPNETTABLE);
1013     PMIB_IPNETTABLE table;
1014
1015     if (numEntries > 1)
1016       size += (numEntries - 1) * sizeof(MIB_IPNETROW);
1017     table = HeapAlloc(heap, flags, size);
1018     if (table) {
1019       FILE *fp;
1020
1021       ret = NO_ERROR;
1022       *ppIpNetTable = table;
1023       table->dwNumEntries = 0;
1024       /* get from /proc/net/arp, no error if can't */
1025       fp = fopen("/proc/net/arp", "r");
1026       if (fp) {
1027         char buf[512] = { 0 }, *ptr;
1028
1029         /* skip header line */
1030         ptr = fgets(buf, sizeof(buf), fp);
1031         while (ptr && table->dwNumEntries < numEntries) {
1032           ptr = fgets(buf, sizeof(buf), fp);
1033           if (ptr) {
1034             char *endPtr;
1035
1036             memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_IPNETROW));
1037             table->table[table->dwNumEntries].dwAddr = inet_addr(ptr);
1038             while (ptr && *ptr && !isspace(*ptr))
1039               ptr++;
1040
1041             if (ptr && *ptr) {
1042               strtoul(ptr, &endPtr, 16); /* hw type (skip) */
1043               ptr = endPtr;
1044             }
1045             if (ptr && *ptr) {
1046               DWORD flags = strtoul(ptr, &endPtr, 16);
1047
1048 #ifdef ATF_COM
1049               if (flags & ATF_COM)
1050                 table->table[table->dwNumEntries].dwType =
1051                  MIB_IPNET_TYPE_DYNAMIC;
1052               else
1053 #endif
1054 #ifdef ATF_PERM
1055               if (flags & ATF_PERM)
1056                 table->table[table->dwNumEntries].dwType =
1057                  MIB_IPNET_TYPE_STATIC;
1058               else
1059 #endif
1060                 table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_OTHER;
1061
1062               ptr = endPtr;
1063             }
1064             while (ptr && *ptr && isspace(*ptr))
1065               ptr++;
1066             while (ptr && *ptr && !isspace(*ptr)) {
1067               DWORD byte = strtoul(ptr, &endPtr, 16);
1068
1069               if (endPtr && *endPtr) {
1070                 endPtr++;
1071                 table->table[table->dwNumEntries].bPhysAddr[
1072                  table->table[table->dwNumEntries].dwPhysAddrLen++] =
1073                  byte & 0x0ff;
1074               }
1075               ptr = endPtr;
1076             }
1077             if (ptr && *ptr) {
1078               strtoul(ptr, &endPtr, 16); /* mask (skip) */
1079               ptr = endPtr;
1080             }
1081             getInterfaceIndexByName(ptr,
1082              &table->table[table->dwNumEntries].dwIndex);
1083             table->dwNumEntries++;
1084           }
1085         }
1086         fclose(fp);
1087       }
1088       else
1089         ret = ERROR_NOT_SUPPORTED;
1090     }
1091     else
1092       ret = ERROR_OUTOFMEMORY;
1093   }
1094   return ret;
1095 }
1096
1097 DWORD getNumUdpEntries(void)
1098 {
1099   return getNumWithOneHeader("/proc/net/udp");
1100 }
1101
1102 DWORD getUdpTable(PMIB_UDPTABLE *ppUdpTable, HANDLE heap, DWORD flags)
1103 {
1104   DWORD ret;
1105
1106 #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1107   ERR ("unimplemented!\n");
1108   return ERROR_NOT_SUPPORTED;
1109 #endif
1110
1111   if (!ppUdpTable)
1112     ret = ERROR_INVALID_PARAMETER;
1113   else {
1114     DWORD numEntries = getNumUdpEntries();
1115     DWORD size = sizeof(MIB_UDPTABLE);
1116     PMIB_UDPTABLE table;
1117
1118     if (numEntries > 1)
1119       size += (numEntries - 1) * sizeof(MIB_UDPROW);
1120     table = HeapAlloc(heap, flags, size);
1121     if (table) {
1122       FILE *fp;
1123
1124       ret = NO_ERROR;
1125       *ppUdpTable = table;
1126       table->dwNumEntries = 0;
1127       /* get from /proc/net/udp, no error if can't */
1128       fp = fopen("/proc/net/udp", "r");
1129       if (fp) {
1130         char buf[512] = { 0 }, *ptr;
1131
1132         /* skip header line */
1133         ptr = fgets(buf, sizeof(buf), fp);
1134         while (ptr && table->dwNumEntries < numEntries) {
1135           memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_UDPROW));
1136           ptr = fgets(buf, sizeof(buf), fp);
1137           if (ptr) {
1138             char *endPtr;
1139
1140             if (ptr && *ptr) {
1141               strtoul(ptr, &endPtr, 16); /* skip */
1142               ptr = endPtr;
1143             }
1144             if (ptr && *ptr) {
1145               ptr++;
1146               table->table[table->dwNumEntries].dwLocalAddr = strtoul(ptr,
1147                &endPtr, 16);
1148               ptr = endPtr;
1149             }
1150             if (ptr && *ptr) {
1151               ptr++;
1152               table->table[table->dwNumEntries].dwLocalPort = strtoul(ptr,
1153                &endPtr, 16);
1154               ptr = endPtr;
1155             }
1156             table->dwNumEntries++;
1157           }
1158         }
1159         fclose(fp);
1160       }
1161       else
1162         ret = ERROR_NOT_SUPPORTED;
1163     }
1164     else
1165       ret = ERROR_OUTOFMEMORY;
1166   }
1167   return ret;
1168 }
1169
1170
1171 DWORD getNumTcpEntries(void)
1172 {
1173 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
1174    return getNumWithOneHeader ("net.inet.tcp.pcblist");
1175 #else
1176    return getNumWithOneHeader ("/proc/net/tcp");
1177 #endif
1178 }
1179
1180
1181 /* Why not a lookup table? Because the TCPS_* constants are different
1182    on different platforms */
1183 static DWORD TCPStateToMIBState (int state)
1184 {
1185    switch (state)
1186    {
1187       case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB;
1188       case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT;
1189       case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD;
1190       case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1;
1191       case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2;
1192       case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT;
1193       case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT;
1194       case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK;
1195       case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN;
1196       case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING;
1197       default:
1198       case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED;
1199    }
1200 }
1201
1202
1203 DWORD getTcpTable(PMIB_TCPTABLE *ppTcpTable, DWORD maxEntries, HANDLE heap,
1204                   DWORD flags)
1205 {
1206    DWORD numEntries;
1207    PMIB_TCPTABLE table;
1208 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
1209    size_t Len = 0;
1210    char *Buf;
1211    struct xinpgen *pXIG, *pOrigXIG;
1212 #else
1213    FILE *fp;
1214    char buf[512] = { 0 }, *ptr;
1215 #endif
1216
1217    if (!ppTcpTable)
1218       return ERROR_INVALID_PARAMETER;
1219
1220    numEntries = getNumTcpEntries ();
1221
1222    if (!*ppTcpTable)
1223    {
1224       DWORD size = sizeof(MIB_TCPTABLE);
1225
1226       if (numEntries > 1)
1227          size += (numEntries - 1) * sizeof (MIB_TCPROW);
1228       *ppTcpTable = HeapAlloc (heap, flags, size);
1229       if (!*ppTcpTable)
1230       {
1231          ERR ("Out of memory!\n");
1232          return ERROR_OUTOFMEMORY;
1233       }
1234       maxEntries = numEntries;
1235    }
1236
1237    table = *ppTcpTable;
1238    table->dwNumEntries = 0;
1239    if (!numEntries)
1240       return NO_ERROR;
1241
1242 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
1243
1244    if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0)
1245    {
1246       ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1247       return ERROR_OUTOFMEMORY;
1248    }
1249
1250    Buf = HeapAlloc (GetProcessHeap (), 0, Len);
1251    if (!Buf)
1252    {
1253       ERR ("Out of memory!\n");
1254       return ERROR_OUTOFMEMORY;
1255    }
1256
1257    if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0)
1258    {
1259       ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1260       HeapFree (GetProcessHeap (), 0, Buf);
1261       return ERROR_OUTOFMEMORY;
1262    }
1263
1264    /* Might be nothing here; first entry is just a header it seems */
1265    if (Len <= sizeof (struct xinpgen))
1266    {
1267       HeapFree (GetProcessHeap (), 0, Buf);
1268       return NO_ERROR;
1269    }
1270
1271    pOrigXIG = (struct xinpgen *)Buf;
1272    pXIG = pOrigXIG;
1273
1274    for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
1275         (pXIG->xig_len > sizeof (struct xinpgen)) &&
1276            (table->dwNumEntries < maxEntries);
1277         pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
1278    {
1279       struct tcpcb *pTCPData = NULL;
1280       struct inpcb *pINData;
1281       struct xsocket *pSockData;
1282
1283       pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
1284       pINData = &((struct xtcpcb *)pXIG)->xt_inp;
1285       pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
1286
1287       /* Ignore sockets for other protocols */
1288       if (pSockData->xso_protocol != IPPROTO_TCP)
1289          continue;
1290
1291       /* Ignore PCBs that were freed while generating the data */
1292       if (pINData->inp_gencnt > pOrigXIG->xig_gen)
1293          continue;
1294
1295       /* we're only interested in IPv4 addresses */
1296       if (!(pINData->inp_vflag & INP_IPV4) ||
1297           (pINData->inp_vflag & INP_IPV6))
1298          continue;
1299
1300       /* If all 0's, skip it */
1301       if (!pINData->inp_laddr.s_addr &&
1302           !pINData->inp_lport &&
1303           !pINData->inp_faddr.s_addr &&
1304           !pINData->inp_fport)
1305          continue;
1306
1307       /* Fill in structure details */
1308       table->table[table->dwNumEntries].dwLocalAddr =
1309          pINData->inp_laddr.s_addr;
1310       table->table[table->dwNumEntries].dwLocalPort =
1311          pINData->inp_lport;
1312       table->table[table->dwNumEntries].dwRemoteAddr =
1313          pINData->inp_faddr.s_addr;
1314       table->table[table->dwNumEntries].dwRemotePort =
1315          pINData->inp_fport;
1316       table->table[table->dwNumEntries].dwState =
1317          TCPStateToMIBState (pTCPData->t_state);
1318
1319       table->dwNumEntries++;
1320    }
1321
1322    HeapFree (GetProcessHeap (), 0, Buf);
1323 #else
1324    /* get from /proc/net/tcp, no error if can't */
1325    fp = fopen("/proc/net/tcp", "r");
1326    if (!fp)
1327       return ERROR_NOT_SUPPORTED;
1328
1329    /* skip header line */
1330    ptr = fgets(buf, sizeof(buf), fp);
1331    while (ptr && table->dwNumEntries < maxEntries) {
1332       memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_TCPROW));
1333       ptr = fgets(buf, sizeof(buf), fp);
1334       if (ptr) {
1335          char *endPtr;
1336
1337          while (ptr && *ptr && *ptr != ':')
1338             ptr++;
1339          if (ptr && *ptr)
1340             ptr++;
1341          if (ptr && *ptr) {
1342             table->table[table->dwNumEntries].dwLocalAddr =
1343                strtoul(ptr, &endPtr, 16);
1344             ptr = endPtr;
1345          }
1346          if (ptr && *ptr) {
1347             ptr++;
1348             table->table[table->dwNumEntries].dwLocalPort =
1349                htons ((unsigned short)strtoul(ptr, &endPtr, 16));
1350             ptr = endPtr;
1351          }
1352          if (ptr && *ptr) {
1353             table->table[table->dwNumEntries].dwRemoteAddr =
1354                strtoul(ptr, &endPtr, 16);
1355             ptr = endPtr;
1356          }
1357          if (ptr && *ptr) {
1358             ptr++;
1359             table->table[table->dwNumEntries].dwRemotePort =
1360                htons ((unsigned short)strtoul(ptr, &endPtr, 16));
1361             ptr = endPtr;
1362          }
1363          if (ptr && *ptr) {
1364             DWORD state = strtoul(ptr, &endPtr, 16);
1365
1366             table->table[table->dwNumEntries].dwState =
1367                TCPStateToMIBState (state);
1368             ptr = endPtr;
1369          }
1370          table->dwNumEntries++;
1371       }
1372    }
1373    fclose(fp);
1374 #endif
1375
1376    return NO_ERROR;
1377 }