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