user32: Constify some variables.
[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     PMIB_IPFORWARDTABLE table = HeapAlloc(heap, flags,
781      sizeof(MIB_IPFORWARDTABLE) + (numRoutes - 1) * sizeof(MIB_IPFORWARDROW));
782
783     if (table) {
784 #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
785        int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
786        size_t needed;
787        char *buf, *lim, *next, *addrPtr;
788        struct rt_msghdr *rtm;
789
790        if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
791        {
792           ERR ("sysctl 1 failed!\n");
793           HeapFree (GetProcessHeap (), 0, table);
794           return NO_ERROR;
795        }
796
797        buf = HeapAlloc (GetProcessHeap (), 0, needed);
798        if (!buf)
799        {
800           HeapFree (GetProcessHeap (), 0, table);
801           return ERROR_OUTOFMEMORY;
802        }
803
804        if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
805        {
806           ERR ("sysctl 2 failed!\n");
807           HeapFree (GetProcessHeap (), 0, table);
808           HeapFree (GetProcessHeap (), 0, buf);
809           return NO_ERROR;
810        }
811
812        *ppIpForwardTable = table;
813        table->dwNumEntries = 0;
814
815        lim = buf + needed;
816        for (next = buf; next < lim; next += rtm->rtm_msglen)
817        {
818           int i;
819
820           rtm = (struct rt_msghdr *)next;
821
822           if (rtm->rtm_type != RTM_GET)
823           {
824              WARN ("Got unexpected message type 0x%x!\n",
825                    rtm->rtm_type);
826              continue;
827           }
828
829           /* Ignore all entries except for gateway routes which aren't
830              multicast */
831           if (!(rtm->rtm_flags & RTF_GATEWAY) ||
832               (rtm->rtm_flags & RTF_MULTICAST))
833              continue;
834
835           memset (&table->table[table->dwNumEntries], 0,
836                   sizeof (MIB_IPFORWARDROW));
837           table->table[table->dwNumEntries].dwForwardIfIndex = rtm->rtm_index;
838           table->table[table->dwNumEntries].dwForwardType =
839              MIB_IPROUTE_TYPE_INDIRECT;
840           table->table[table->dwNumEntries].dwForwardMetric1 =
841              rtm->rtm_rmx.rmx_hopcount;
842           table->table[table->dwNumEntries].dwForwardProto =
843              MIB_IPPROTO_LOCAL;
844
845           addrPtr = (char *)(rtm + 1);
846
847           for (i = 1; i; i <<= 1)
848           {
849              struct sockaddr *sa;
850              DWORD addr;
851
852              if (!(i & rtm->rtm_addrs))
853                 continue;
854
855              sa = (struct sockaddr *)addrPtr;
856              ADVANCE (addrPtr, sa);
857
858              /* default routes are encoded by length-zero sockaddr */
859              if (sa->sa_len == 0)
860                 addr = 0;
861              else if (sa->sa_family != AF_INET)
862              {
863                 ERR ("Received unsupported sockaddr family 0x%x\n",
864                      sa->sa_family);
865                 addr = 0;
866              }
867              else
868              {
869                 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
870
871                 addr = sin->sin_addr.s_addr;
872              }
873
874              switch (i)
875              {
876                 case RTA_DST:
877                    table->table[table->dwNumEntries].dwForwardDest = addr;
878                    break;
879
880                 case RTA_GATEWAY:
881                    table->table[table->dwNumEntries].dwForwardNextHop = addr;
882                    break;
883
884                 case RTA_NETMASK:
885                    table->table[table->dwNumEntries].dwForwardMask = addr;
886                    break;
887
888                 default:
889                    ERR ("Unexpected address type 0x%x\n", i);
890              }
891           }
892
893           table->dwNumEntries++;
894        }
895
896        HeapFree (GetProcessHeap (), 0, buf);
897        ret = NO_ERROR;
898 #else
899       FILE *fp;
900
901       ret = NO_ERROR;
902       *ppIpForwardTable = table;
903       table->dwNumEntries = 0;
904       /* get from /proc/net/route, no error if can't */
905       fp = fopen("/proc/net/route", "r");
906       if (fp) {
907         char buf[512] = { 0 }, *ptr;
908
909         /* skip header line */
910         ptr = fgets(buf, sizeof(buf), fp);
911         while (ptr && table->dwNumEntries < numRoutes) {
912           memset(&table->table[table->dwNumEntries], 0,
913            sizeof(MIB_IPFORWARDROW));
914           ptr = fgets(buf, sizeof(buf), fp);
915           if (ptr) {
916             DWORD index;
917
918             while (!isspace(*ptr))
919               ptr++;
920             *ptr = '\0';
921             ptr++;
922             if (getInterfaceIndexByName(buf, &index) == NO_ERROR) {
923               char *endPtr;
924
925               table->table[table->dwNumEntries].dwForwardIfIndex = index;
926               if (*ptr) {
927                 table->table[table->dwNumEntries].dwForwardDest =
928                  strtoul(ptr, &endPtr, 16);
929                 ptr = endPtr;
930               }
931               if (ptr && *ptr) {
932                 table->table[table->dwNumEntries].dwForwardNextHop =
933                  strtoul(ptr, &endPtr, 16);
934                 ptr = endPtr;
935               }
936               if (ptr && *ptr) {
937                 DWORD flags = strtoul(ptr, &endPtr, 16);
938
939                 if (!(flags & RTF_UP))
940                   table->table[table->dwNumEntries].dwForwardType =
941                    MIB_IPROUTE_TYPE_INVALID;
942                 else if (flags & RTF_GATEWAY)
943                   table->table[table->dwNumEntries].dwForwardType =
944                    MIB_IPROUTE_TYPE_INDIRECT;
945                 else
946                   table->table[table->dwNumEntries].dwForwardType =
947                    MIB_IPROUTE_TYPE_DIRECT;
948                 ptr = endPtr;
949               }
950               if (ptr && *ptr) {
951                 strtoul(ptr, &endPtr, 16); /* refcount, skip */
952                 ptr = endPtr;
953               }
954               if (ptr && *ptr) {
955                 strtoul(ptr, &endPtr, 16); /* use, skip */
956                 ptr = endPtr;
957               }
958               if (ptr && *ptr) {
959                 table->table[table->dwNumEntries].dwForwardMetric1 =
960                  strtoul(ptr, &endPtr, 16);
961                 ptr = endPtr;
962               }
963               if (ptr && *ptr) {
964                 table->table[table->dwNumEntries].dwForwardMask =
965                  strtoul(ptr, &endPtr, 16);
966                 ptr = endPtr;
967               }
968               /* FIXME: other protos might be appropriate, e.g. the default
969                * route is typically set with MIB_IPPROTO_NETMGMT instead */
970               table->table[table->dwNumEntries].dwForwardProto =
971                MIB_IPPROTO_LOCAL;
972               table->dwNumEntries++;
973             }
974           }
975         }
976         fclose(fp);
977       }
978       else
979       {
980         ERR ("unimplemented!\n");
981         return ERROR_NOT_SUPPORTED;
982       }
983 #endif
984     }
985     else
986       ret = ERROR_OUTOFMEMORY;
987   }
988   return ret;
989 }
990
991 DWORD getNumArpEntries(void)
992 {
993   return getNumWithOneHeader("/proc/net/arp");
994 }
995
996 DWORD getArpTable(PMIB_IPNETTABLE *ppIpNetTable, HANDLE heap, DWORD flags)
997 {
998   DWORD ret;
999
1000 #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1001   ERR ("unimplemented!\n");
1002   return ERROR_NOT_SUPPORTED;
1003 #endif
1004
1005   if (!ppIpNetTable)
1006     ret = ERROR_INVALID_PARAMETER;
1007   else {
1008     DWORD numEntries = getNumArpEntries();
1009     PMIB_IPNETTABLE table = HeapAlloc(heap, flags,
1010      sizeof(MIB_IPNETTABLE) + (numEntries - 1) * sizeof(MIB_IPNETROW));
1011
1012     if (table) {
1013       FILE *fp;
1014
1015       ret = NO_ERROR;
1016       *ppIpNetTable = table;
1017       table->dwNumEntries = 0;
1018       /* get from /proc/net/arp, no error if can't */
1019       fp = fopen("/proc/net/arp", "r");
1020       if (fp) {
1021         char buf[512] = { 0 }, *ptr;
1022
1023         /* skip header line */
1024         ptr = fgets(buf, sizeof(buf), fp);
1025         while (ptr && table->dwNumEntries < numEntries) {
1026           ptr = fgets(buf, sizeof(buf), fp);
1027           if (ptr) {
1028             char *endPtr;
1029
1030             memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_IPNETROW));
1031             table->table[table->dwNumEntries].dwAddr = inet_addr(ptr);
1032             while (ptr && *ptr && !isspace(*ptr))
1033               ptr++;
1034
1035             if (ptr && *ptr) {
1036               strtoul(ptr, &endPtr, 16); /* hw type (skip) */
1037               ptr = endPtr;
1038             }
1039             if (ptr && *ptr) {
1040               DWORD flags = strtoul(ptr, &endPtr, 16);
1041
1042 #ifdef ATF_COM
1043               if (flags & ATF_COM)
1044                 table->table[table->dwNumEntries].dwType =
1045                  MIB_IPNET_TYPE_DYNAMIC;
1046               else
1047 #endif
1048 #ifdef ATF_PERM
1049               if (flags & ATF_PERM)
1050                 table->table[table->dwNumEntries].dwType =
1051                  MIB_IPNET_TYPE_STATIC;
1052               else
1053 #endif
1054                 table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_OTHER;
1055
1056               ptr = endPtr;
1057             }
1058             while (ptr && *ptr && isspace(*ptr))
1059               ptr++;
1060             while (ptr && *ptr && !isspace(*ptr)) {
1061               DWORD byte = strtoul(ptr, &endPtr, 16);
1062
1063               if (endPtr && *endPtr) {
1064                 endPtr++;
1065                 table->table[table->dwNumEntries].bPhysAddr[
1066                  table->table[table->dwNumEntries].dwPhysAddrLen++] =
1067                  byte & 0x0ff;
1068               }
1069               ptr = endPtr;
1070             }
1071             if (ptr && *ptr) {
1072               strtoul(ptr, &endPtr, 16); /* mask (skip) */
1073               ptr = endPtr;
1074             }
1075             getInterfaceIndexByName(ptr,
1076              &table->table[table->dwNumEntries].dwIndex);
1077             table->dwNumEntries++;
1078           }
1079         }
1080         fclose(fp);
1081       }
1082       else
1083         ret = ERROR_NOT_SUPPORTED;
1084     }
1085     else
1086       ret = ERROR_OUTOFMEMORY;
1087   }
1088   return ret;
1089 }
1090
1091 DWORD getNumUdpEntries(void)
1092 {
1093   return getNumWithOneHeader("/proc/net/udp");
1094 }
1095
1096 DWORD getUdpTable(PMIB_UDPTABLE *ppUdpTable, HANDLE heap, DWORD flags)
1097 {
1098   DWORD ret;
1099
1100 #if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
1101   ERR ("unimplemented!\n");
1102   return ERROR_NOT_SUPPORTED;
1103 #endif
1104
1105   if (!ppUdpTable)
1106     ret = ERROR_INVALID_PARAMETER;
1107   else {
1108     DWORD numEntries = getNumUdpEntries();
1109     PMIB_UDPTABLE table = HeapAlloc(heap, flags,
1110      sizeof(MIB_UDPTABLE) + (numEntries - 1) * sizeof(MIB_UDPROW));
1111
1112     if (table) {
1113       FILE *fp;
1114
1115       ret = NO_ERROR;
1116       *ppUdpTable = table;
1117       table->dwNumEntries = 0;
1118       /* get from /proc/net/udp, no error if can't */
1119       fp = fopen("/proc/net/udp", "r");
1120       if (fp) {
1121         char buf[512] = { 0 }, *ptr;
1122
1123         /* skip header line */
1124         ptr = fgets(buf, sizeof(buf), fp);
1125         while (ptr && table->dwNumEntries < numEntries) {
1126           memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_UDPROW));
1127           ptr = fgets(buf, sizeof(buf), fp);
1128           if (ptr) {
1129             char *endPtr;
1130
1131             if (ptr && *ptr) {
1132               strtoul(ptr, &endPtr, 16); /* skip */
1133               ptr = endPtr;
1134             }
1135             if (ptr && *ptr) {
1136               ptr++;
1137               table->table[table->dwNumEntries].dwLocalAddr = strtoul(ptr,
1138                &endPtr, 16);
1139               ptr = endPtr;
1140             }
1141             if (ptr && *ptr) {
1142               ptr++;
1143               table->table[table->dwNumEntries].dwLocalPort = strtoul(ptr,
1144                &endPtr, 16);
1145               ptr = endPtr;
1146             }
1147             table->dwNumEntries++;
1148           }
1149         }
1150         fclose(fp);
1151       }
1152       else
1153         ret = ERROR_NOT_SUPPORTED;
1154     }
1155     else
1156       ret = ERROR_OUTOFMEMORY;
1157   }
1158   return ret;
1159 }
1160
1161
1162 DWORD getNumTcpEntries(void)
1163 {
1164 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
1165    return getNumWithOneHeader ("net.inet.tcp.pcblist");
1166 #else
1167    return getNumWithOneHeader ("/proc/net/tcp");
1168 #endif
1169 }
1170
1171
1172 /* Why not a lookup table? Because the TCPS_* constants are different
1173    on different platforms */
1174 static DWORD TCPStateToMIBState (int state)
1175 {
1176    switch (state)
1177    {
1178       case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB;
1179       case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT;
1180       case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD;
1181       case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1;
1182       case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2;
1183       case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT;
1184       case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT;
1185       case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK;
1186       case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN;
1187       case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING;
1188       default:
1189       case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED;
1190    }
1191 }
1192
1193
1194 DWORD getTcpTable(PMIB_TCPTABLE *ppTcpTable, DWORD maxEntries, HANDLE heap,
1195                   DWORD flags)
1196 {
1197    DWORD numEntries;
1198    PMIB_TCPTABLE table;
1199 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
1200    size_t Len = 0;
1201    char *Buf;
1202    struct xinpgen *pXIG, *pOrigXIG;
1203 #else
1204    FILE *fp;
1205    char buf[512] = { 0 }, *ptr;
1206 #endif
1207
1208    if (!ppTcpTable)
1209       return ERROR_INVALID_PARAMETER;
1210
1211    numEntries = getNumTcpEntries ();
1212
1213    if (!*ppTcpTable)
1214    {
1215       *ppTcpTable = HeapAlloc (heap, flags,
1216                                sizeof (MIB_TCPTABLE) +
1217                                (numEntries - 1) * sizeof (MIB_TCPROW));
1218       if (!*ppTcpTable)
1219       {
1220          ERR ("Out of memory!\n");
1221          return ERROR_OUTOFMEMORY;
1222       }
1223       maxEntries = numEntries;
1224    }
1225
1226    table = *ppTcpTable;
1227    table->dwNumEntries = 0;
1228    if (!numEntries)
1229       return NO_ERROR;
1230
1231 #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
1232
1233    if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0)
1234    {
1235       ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1236       return ERROR_OUTOFMEMORY;
1237    }
1238
1239    Buf = HeapAlloc (GetProcessHeap (), 0, Len);
1240    if (!Buf)
1241    {
1242       ERR ("Out of memory!\n");
1243       return ERROR_OUTOFMEMORY;
1244    }
1245
1246    if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0)
1247    {
1248       ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
1249       HeapFree (GetProcessHeap (), 0, Buf);
1250       return ERROR_OUTOFMEMORY;
1251    }
1252
1253    /* Might be nothing here; first entry is just a header it seems */
1254    if (Len <= sizeof (struct xinpgen))
1255    {
1256       HeapFree (GetProcessHeap (), 0, Buf);
1257       return NO_ERROR;
1258    }
1259
1260    pOrigXIG = (struct xinpgen *)Buf;
1261    pXIG = pOrigXIG;
1262
1263    for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
1264         (pXIG->xig_len > sizeof (struct xinpgen)) &&
1265            (table->dwNumEntries < maxEntries);
1266         pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
1267    {
1268       struct tcpcb *pTCPData = NULL;
1269       struct inpcb *pINData;
1270       struct xsocket *pSockData;
1271
1272       pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
1273       pINData = &((struct xtcpcb *)pXIG)->xt_inp;
1274       pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
1275
1276       /* Ignore sockets for other protocols */
1277       if (pSockData->xso_protocol != IPPROTO_TCP)
1278          continue;
1279
1280       /* Ignore PCBs that were freed while generating the data */
1281       if (pINData->inp_gencnt > pOrigXIG->xig_gen)
1282          continue;
1283
1284       /* we're only interested in IPv4 addresses */
1285       if (!(pINData->inp_vflag & INP_IPV4) ||
1286           (pINData->inp_vflag & INP_IPV6))
1287          continue;
1288
1289       /* If all 0's, skip it */
1290       if (!pINData->inp_laddr.s_addr &&
1291           !pINData->inp_lport &&
1292           !pINData->inp_faddr.s_addr &&
1293           !pINData->inp_fport)
1294          continue;
1295
1296       /* Fill in structure details */
1297       table->table[table->dwNumEntries].dwLocalAddr =
1298          pINData->inp_laddr.s_addr;
1299       table->table[table->dwNumEntries].dwLocalPort =
1300          pINData->inp_lport;
1301       table->table[table->dwNumEntries].dwRemoteAddr =
1302          pINData->inp_faddr.s_addr;
1303       table->table[table->dwNumEntries].dwRemotePort =
1304          pINData->inp_fport;
1305       table->table[table->dwNumEntries].dwState =
1306          TCPStateToMIBState (pTCPData->t_state);
1307
1308       table->dwNumEntries++;
1309    }
1310
1311    HeapFree (GetProcessHeap (), 0, Buf);
1312 #else
1313    /* get from /proc/net/tcp, no error if can't */
1314    fp = fopen("/proc/net/tcp", "r");
1315    if (!fp)
1316       return ERROR_NOT_SUPPORTED;
1317
1318    /* skip header line */
1319    ptr = fgets(buf, sizeof(buf), fp);
1320    while (ptr && table->dwNumEntries < maxEntries) {
1321       memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_TCPROW));
1322       ptr = fgets(buf, sizeof(buf), fp);
1323       if (ptr) {
1324          char *endPtr;
1325
1326          while (ptr && *ptr && *ptr != ':')
1327             ptr++;
1328          if (ptr && *ptr)
1329             ptr++;
1330          if (ptr && *ptr) {
1331             table->table[table->dwNumEntries].dwLocalAddr =
1332                strtoul(ptr, &endPtr, 16);
1333             ptr = endPtr;
1334          }
1335          if (ptr && *ptr) {
1336             ptr++;
1337             table->table[table->dwNumEntries].dwLocalPort =
1338                htons ((unsigned short)strtoul(ptr, &endPtr, 16));
1339             ptr = endPtr;
1340          }
1341          if (ptr && *ptr) {
1342             table->table[table->dwNumEntries].dwRemoteAddr =
1343                strtoul(ptr, &endPtr, 16);
1344             ptr = endPtr;
1345          }
1346          if (ptr && *ptr) {
1347             ptr++;
1348             table->table[table->dwNumEntries].dwRemotePort =
1349                htons ((unsigned short)strtoul(ptr, &endPtr, 16));
1350             ptr = endPtr;
1351          }
1352          if (ptr && *ptr) {
1353             DWORD state = strtoul(ptr, &endPtr, 16);
1354
1355             table->table[table->dwNumEntries].dwState =
1356                TCPStateToMIBState (state);
1357             ptr = endPtr;
1358          }
1359          table->dwNumEntries++;
1360       }
1361    }
1362    fclose(fp);
1363 #endif
1364
1365    return NO_ERROR;
1366 }