/* Copyright (C) 2003,2006 Juan Lang
 * Copyright (C) 2007 TransGaming Technologies Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 * This file implements statistics getting using the /proc filesystem exported
 * by Linux, and maybe other OSes.
 */

#include "config.h"
#include "wine/port.h"
#include "wine/debug.h"

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_ALIAS_H
#include <alias.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_SOCKETVAR_H
#include <sys/socketvar.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_NET_IF_DL_H
#include <net/if_dl.h>
#endif
#ifdef HAVE_NET_IF_TYPES_H
#include <net/if_types.h>
#endif
#ifdef HAVE_NET_ROUTE_H
#include <net/route.h>
#endif
#ifdef HAVE_NET_IF_ARP_H
#include <net/if_arp.h>
#endif
#ifdef HAVE_NETINET_IF_ETHER_H
#include <netinet/if_ether.h>
#endif
#ifdef HAVE_NETINET_IP_H
#include <netinet/ip.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_NETINET_TCP_FSM_H
#include <netinet/tcp_fsm.h>
#endif
#ifdef HAVE_NETINET_IN_PCB_H
#include <netinet/in_pcb.h>
#endif
#ifdef HAVE_NETINET_TCP_VAR_H
#include <netinet/tcp_var.h>
#endif
#ifdef HAVE_NETINET_TCP_TIMER_H
#include <netinet/tcp_timer.h>
#endif
#ifdef HAVE_NETINET_IN_SYSTM_H
#include <netinet/in_systm.h>
#endif
#ifdef HAVE_NETINET_IP_ICMP_H
#include <netinet/ip_icmp.h>
#endif
#ifdef HAVE_NETINET_ICMP_VAR_H
#include <netinet/icmp_var.h>
#endif
#ifdef HAVE_NETINET_IP_VAR_H
#include <netinet/ip_var.h>
#endif
#ifdef HAVE_NETINET_UDP_H
#include <netinet/udp.h>
#endif
#ifdef HAVE_NETINET_UDP_VAR_H
#include <netinet/udp_var.h>
#endif

#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif

#ifndef ROUNDUP
#define ROUNDUP(a) \
	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#endif
#ifndef ADVANCE
#define ADVANCE(x, n) (x += ROUNDUP(((struct sockaddr *)n)->sa_len))
#endif

#include "windef.h"
#include "winbase.h"
#include "iprtrmib.h"
#include "ifenum.h"
#include "ipstats.h"

#ifndef HAVE_NETINET_TCP_FSM_H
#define TCPS_ESTABLISHED  1
#define TCPS_SYN_SENT     2
#define TCPS_SYN_RECEIVED 3
#define TCPS_FIN_WAIT_1   4
#define TCPS_FIN_WAIT_2   5
#define TCPS_TIME_WAIT    6
#define TCPS_CLOSED       7
#define TCPS_CLOSE_WAIT   8
#define TCPS_LAST_ACK     9
#define TCPS_LISTEN      10
#define TCPS_CLOSING     11
#endif

WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);

DWORD getInterfaceStatsByName(const char *name, PMIB_IFROW entry)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_IFLIST)
  int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_IFLIST, if_nametoindex(name)};
#define MIB_LEN (sizeof(mib) / sizeof(mib[0]))

  size_t needed;
  char *buf, *end;
  struct if_msghdr *ifm;
  struct if_data ifdata;
  if (!name || !entry)
    return ERROR_INVALID_PARAMETER;

  if(sysctl(mib, MIB_LEN, NULL, &needed, NULL, 0) == -1)
  {
      ERR ("failed to get size of iflist\n");
      return ERROR_NOT_SUPPORTED;
  }
  buf = HeapAlloc (GetProcessHeap (), 0, needed);
  if (!buf) return ERROR_NOT_SUPPORTED;
  if(sysctl(mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
  {
      ERR ("failed to get iflist\n");
      HeapFree (GetProcessHeap (), 0, buf);
      return ERROR_NOT_SUPPORTED;
  }
  else
      for ( end = buf + needed; buf < end; buf += ifm->ifm_msglen)
      {
          ifm = (struct if_msghdr *) buf;
          if(ifm->ifm_type == RTM_IFINFO && ifm->ifm_data.ifi_type == IFT_ETHER)
          {
              ifdata = ifm->ifm_data;
              entry->dwMtu = ifdata.ifi_mtu;
              entry->dwSpeed = ifdata.ifi_baudrate;
              entry->dwInOctets = ifdata.ifi_ibytes;
              entry->dwInErrors = ifdata.ifi_ierrors;
              entry->dwInDiscards = ifdata.ifi_iqdrops;
              entry->dwInUcastPkts = ifdata.ifi_ipackets;
              entry->dwInNUcastPkts = ifdata.ifi_imcasts;
              entry->dwOutOctets = ifdata.ifi_obytes;
              entry->dwOutUcastPkts = ifdata.ifi_opackets;
              entry->dwOutErrors = ifdata.ifi_oerrors;
              HeapFree (GetProcessHeap (), 0, buf);
              return NO_ERROR;
          }
      }
      HeapFree (GetProcessHeap (), 0, buf);
      return ERROR_NOT_SUPPORTED;
#else
  /* get interface stats from /proc/net/dev, no error if can't
     no inUnknownProtos, outNUcastPkts, outQLen */
  FILE *fp;

  if (!name || !entry)
    return ERROR_INVALID_PARAMETER;
  fp = fopen("/proc/net/dev", "r");
  if (fp) {
    char buf[512] = { 0 }, *ptr;
    int nameLen = strlen(name), nameFound = 0;


    ptr = fgets(buf, sizeof(buf), fp);
    while (ptr && !nameFound) {
      while (*ptr && isspace(*ptr))
        ptr++;
      if (strncasecmp(ptr, name, nameLen) == 0 && *(ptr + nameLen) == ':')
        nameFound = 1;
      else
        ptr = fgets(buf, sizeof(buf), fp);
    }
    if (nameFound) {
      char *endPtr;

      ptr += nameLen + 1;
      if (ptr && *ptr) {
        entry->dwInOctets = strtoul(ptr, &endPtr, 10);
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        entry->dwInUcastPkts = strtoul(ptr, &endPtr, 10);
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        entry->dwInErrors = strtoul(ptr, &endPtr, 10);
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        entry->dwInDiscards = strtoul(ptr, &endPtr, 10);
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        strtoul(ptr, &endPtr, 10); /* skip */
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        strtoul(ptr, &endPtr, 10); /* skip */
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        strtoul(ptr, &endPtr, 10); /* skip */
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        entry->dwInNUcastPkts = strtoul(ptr, &endPtr, 10);
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        entry->dwOutOctets = strtoul(ptr, &endPtr, 10);
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        entry->dwOutUcastPkts = strtoul(ptr, &endPtr, 10);
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        entry->dwOutErrors = strtoul(ptr, &endPtr, 10);
        ptr = endPtr;
      }
      if (ptr && *ptr) {
        entry->dwOutDiscards = strtoul(ptr, &endPtr, 10);
        ptr = endPtr;
      }
    }
    fclose(fp);
  }
  else
  {
     ERR ("unimplemented!\n");
     return ERROR_NOT_SUPPORTED;
  }

  return NO_ERROR;
#endif
}

DWORD getICMPStats(MIB_ICMP *stats)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(ICMPCTL_STATS)
  int mib[] = {CTL_NET, PF_INET, IPPROTO_ICMP, ICMPCTL_STATS};
#define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
  size_t needed;
  struct icmpstat icmp_stat;
  int i;

  if (!stats)
    return ERROR_INVALID_PARAMETER;

  needed = sizeof(icmp_stat);
  if(sysctl(mib, MIB_LEN, &icmp_stat, &needed, NULL, 0) == -1)
  {
      ERR ("failed to get icmpstat\n");
      return ERROR_NOT_SUPPORTED;
  }


  /*in stats */
  stats->stats.icmpInStats.dwMsgs = icmp_stat.icps_badcode + icmp_stat.icps_checksum + icmp_stat.icps_tooshort + icmp_stat.icps_badlen;
  for(i = 0; i <= ICMP_MAXTYPE; i++)
      stats->stats.icmpInStats.dwMsgs += icmp_stat.icps_inhist[i];

  stats->stats.icmpInStats.dwErrors = icmp_stat.icps_badcode + icmp_stat.icps_tooshort + icmp_stat.icps_checksum + icmp_stat.icps_badlen;

  stats->stats.icmpInStats.dwDestUnreachs = icmp_stat.icps_inhist[ICMP_UNREACH];
  stats->stats.icmpInStats.dwTimeExcds = icmp_stat.icps_inhist[ICMP_TIMXCEED];
  stats->stats.icmpInStats.dwParmProbs = icmp_stat.icps_inhist[ICMP_PARAMPROB];
  stats->stats.icmpInStats.dwSrcQuenchs = icmp_stat.icps_inhist[ICMP_SOURCEQUENCH];
  stats->stats.icmpInStats.dwRedirects = icmp_stat.icps_inhist[ICMP_REDIRECT];
  stats->stats.icmpInStats.dwEchos = icmp_stat.icps_inhist[ICMP_ECHO];
  stats->stats.icmpInStats.dwEchoReps = icmp_stat.icps_inhist[ICMP_ECHOREPLY];
  stats->stats.icmpInStats.dwTimestamps = icmp_stat.icps_inhist[ICMP_TSTAMP];
  stats->stats.icmpInStats.dwTimestampReps = icmp_stat.icps_inhist[ICMP_TSTAMPREPLY];
  stats->stats.icmpInStats.dwAddrMasks = icmp_stat.icps_inhist[ICMP_MASKREQ];
  stats->stats.icmpInStats.dwAddrMaskReps = icmp_stat.icps_inhist[ICMP_MASKREPLY];


  /* out stats */
  stats->stats.icmpOutStats.dwMsgs = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;
  for(i = 0; i <= ICMP_MAXTYPE; i++)
  stats->stats.icmpOutStats.dwMsgs += icmp_stat.icps_outhist[i];

  stats->stats.icmpOutStats.dwErrors = icmp_stat.icps_oldshort + icmp_stat.icps_oldicmp;

  stats->stats.icmpOutStats.dwDestUnreachs = icmp_stat.icps_outhist[ICMP_UNREACH];
  stats->stats.icmpOutStats.dwTimeExcds = icmp_stat.icps_outhist[ICMP_TIMXCEED];
  stats->stats.icmpOutStats.dwParmProbs = icmp_stat.icps_outhist[ICMP_PARAMPROB];
  stats->stats.icmpOutStats.dwSrcQuenchs = icmp_stat.icps_outhist[ICMP_SOURCEQUENCH];
  stats->stats.icmpOutStats.dwRedirects = icmp_stat.icps_outhist[ICMP_REDIRECT];
  stats->stats.icmpOutStats.dwEchos = icmp_stat.icps_outhist[ICMP_ECHO];
  stats->stats.icmpOutStats.dwEchoReps = icmp_stat.icps_outhist[ICMP_ECHOREPLY];
  stats->stats.icmpOutStats.dwTimestamps = icmp_stat.icps_outhist[ICMP_TSTAMP];
  stats->stats.icmpOutStats.dwTimestampReps = icmp_stat.icps_outhist[ICMP_TSTAMPREPLY];
  stats->stats.icmpOutStats.dwAddrMasks = icmp_stat.icps_outhist[ICMP_MASKREQ];
  stats->stats.icmpOutStats.dwAddrMaskReps = icmp_stat.icps_outhist[ICMP_MASKREPLY];

  return NO_ERROR;
#else
  FILE *fp;

  if (!stats)
    return ERROR_INVALID_PARAMETER;

  memset(stats, 0, sizeof(MIB_ICMP));
  /* get most of these stats from /proc/net/snmp, no error if can't */
  fp = fopen("/proc/net/snmp", "r");
  if (fp) {
    static const char hdr[] = "Icmp:";
    char buf[512] = { 0 }, *ptr;

    do {
      ptr = fgets(buf, sizeof(buf), fp);
    } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
    if (ptr) {
      /* last line was a header, get another */
      ptr = fgets(buf, sizeof(buf), fp);
      if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
        char *endPtr;

        ptr += sizeof(hdr);
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwMsgs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwErrors = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwRedirects = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpInStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwMsgs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwErrors = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwDestUnreachs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwTimeExcds = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwParmProbs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwSrcQuenchs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwRedirects = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwEchoReps = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwTimestamps = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwTimestampReps = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwAddrMasks = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->stats.icmpOutStats.dwAddrMaskReps = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
      }
    }
    fclose(fp);
  }
  else
  {
     ERR ("unimplemented!\n");
     return ERROR_NOT_SUPPORTED;
  }

  return NO_ERROR;
#endif
}

DWORD getIPStats(PMIB_IPSTATS stats)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(IPCTL_STATS)
  int mib[] = {CTL_NET, PF_INET, IPPROTO_IP, IPCTL_STATS};
#define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
  int ip_ttl, ip_forwarding;
  struct ipstat ip_stat;
  size_t needed;

  if (!stats)
      return ERROR_INVALID_PARAMETER;

  needed = sizeof(ip_stat);
  if(sysctl(mib, MIB_LEN, &ip_stat, &needed, NULL, 0) == -1)
  {
      ERR ("failed to get ipstat\n");
      return ERROR_NOT_SUPPORTED;
  }

  needed = sizeof(ip_ttl);
  if (sysctlbyname ("net.inet.ip.ttl", &ip_ttl, &needed, NULL, 0) == -1)
  {
      ERR ("failed to get ip Default TTL\n");
      return ERROR_NOT_SUPPORTED;
  }

  needed = sizeof(ip_forwarding);
  if (sysctlbyname ("net.inet.ip.forwarding", &ip_forwarding, &needed, NULL, 0) == -1)
  {
      ERR ("failed to get ip forwarding\n");
      return ERROR_NOT_SUPPORTED;
  }

  stats->dwForwarding = ip_forwarding;
  stats->dwDefaultTTL = ip_ttl;
  stats->dwInDelivers = ip_stat.ips_delivered;
  stats->dwInHdrErrors = ip_stat.ips_badhlen + ip_stat.ips_badsum + ip_stat.ips_tooshort + ip_stat.ips_badlen;
  stats->dwInAddrErrors = ip_stat.ips_cantforward;
  stats->dwInReceives = ip_stat.ips_total;
  stats->dwForwDatagrams = ip_stat.ips_forward;
  stats->dwInUnknownProtos = ip_stat.ips_noproto;
  stats->dwInDiscards = ip_stat.ips_fragdropped;
  stats->dwOutDiscards = ip_stat.ips_odropped;
  stats->dwReasmOks = ip_stat.ips_reassembled;
  stats->dwFragOks = ip_stat.ips_fragmented;
  stats->dwFragFails = ip_stat.ips_cantfrag;
  stats->dwReasmTimeout = ip_stat.ips_fragtimeout;
  stats->dwOutNoRoutes = ip_stat.ips_noroute;
  stats->dwOutRequests = ip_stat.ips_localout;
  stats->dwReasmReqds = ip_stat.ips_fragments;

  return NO_ERROR;
#else
  FILE *fp;

  if (!stats)
    return ERROR_INVALID_PARAMETER;

  memset(stats, 0, sizeof(MIB_IPSTATS));
  stats->dwNumIf = stats->dwNumAddr = getNumInterfaces();
  stats->dwNumRoutes = getNumRoutes();

  /* get most of these stats from /proc/net/snmp, no error if can't */
  fp = fopen("/proc/net/snmp", "r");
  if (fp) {
    static const char hdr[] = "Ip:";
    char buf[512] = { 0 }, *ptr;

    do {
      ptr = fgets(buf, sizeof(buf), fp);
    } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
    if (ptr) {
      /* last line was a header, get another */
      ptr = fgets(buf, sizeof(buf), fp);
      if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
        char *endPtr;

        ptr += sizeof(hdr);
        if (ptr && *ptr) {
          stats->dwForwarding = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwDefaultTTL = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwInReceives = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwInHdrErrors = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwInAddrErrors = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwForwDatagrams = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwInUnknownProtos = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwInDiscards = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwInDelivers = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwOutRequests = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwOutDiscards = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwOutNoRoutes = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwReasmTimeout = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwReasmReqds = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwReasmOks = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwReasmFails = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwFragOks = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwFragFails = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwFragCreates = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        /* hmm, no routingDiscards */
      }
    }
    fclose(fp);
  }
  else
  {
     ERR ("unimplemented!\n");
     return ERROR_NOT_SUPPORTED;
  }

  return NO_ERROR;
#endif
}

DWORD getTCPStats(MIB_TCPSTATS *stats)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS)
#ifndef TCPTV_MIN  /* got removed in Mac OS X for some reason */
#define TCPTV_MIN 2
#define TCPTV_REXMTMAX 128
#endif
  int mib[] = {CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS};
#define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
#define hz 1000
  struct tcpstat tcp_stat;
  size_t needed;

  if (!stats)
    return ERROR_INVALID_PARAMETER;
  needed = sizeof(tcp_stat);

  if(sysctl(mib, MIB_LEN, &tcp_stat, &needed, NULL, 0) == -1)
  {
      ERR ("failed to get tcpstat\n");
      return ERROR_NOT_SUPPORTED;
  }

  stats->dwRtoAlgorithm = MIB_TCP_RTO_VANJ;
  stats->dwRtoMin = TCPTV_MIN;
  stats->dwRtoMax = TCPTV_REXMTMAX;
  stats->dwMaxConn = -1;
  stats->dwActiveOpens = tcp_stat.tcps_connattempt;
  stats->dwPassiveOpens = tcp_stat.tcps_accepts;
  stats->dwAttemptFails = tcp_stat.tcps_conndrops;
  stats->dwEstabResets = tcp_stat.tcps_drops;
  stats->dwCurrEstab = 0;
  stats->dwInSegs = tcp_stat.tcps_rcvtotal;
  stats->dwOutSegs = tcp_stat.tcps_sndtotal - tcp_stat.tcps_sndrexmitpack;
  stats->dwRetransSegs = tcp_stat.tcps_sndrexmitpack;
  stats->dwInErrs = tcp_stat.tcps_rcvbadsum + tcp_stat.tcps_rcvbadoff + tcp_stat.tcps_rcvmemdrop + tcp_stat.tcps_rcvshort;
  stats->dwOutRsts = tcp_stat.tcps_sndctrl - tcp_stat.tcps_closed;
  stats->dwNumConns = tcp_stat.tcps_connects;

  return NO_ERROR;

#else
  FILE *fp;

  if (!stats)
    return ERROR_INVALID_PARAMETER;

  memset(stats, 0, sizeof(MIB_TCPSTATS));

  /* get from /proc/net/snmp, no error if can't */
  fp = fopen("/proc/net/snmp", "r");
  if (fp) {
    static const char hdr[] = "Tcp:";
    char buf[512] = { 0 }, *ptr;


    do {
      ptr = fgets(buf, sizeof(buf), fp);
    } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
    if (ptr) {
      /* last line was a header, get another */
      ptr = fgets(buf, sizeof(buf), fp);
      if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
        char *endPtr;

        ptr += sizeof(hdr);
        if (ptr && *ptr) {
          stats->dwRtoAlgorithm = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwRtoMin = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwRtoMax = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwMaxConn = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwActiveOpens = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwPassiveOpens = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwAttemptFails = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwEstabResets = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwCurrEstab = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwInSegs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwOutSegs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwRetransSegs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwInErrs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwOutRsts = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        stats->dwNumConns = getNumTcpEntries();
      }
    }
    fclose(fp);
  }
  else
  {
     ERR ("unimplemented!\n");
     return ERROR_NOT_SUPPORTED;
  }

  return NO_ERROR;
#endif
}

DWORD getUDPStats(MIB_UDPSTATS *stats)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(UDPCTL_STATS)
  int mib[] = {CTL_NET, PF_INET, IPPROTO_UDP, UDPCTL_STATS};
#define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
  struct udpstat udp_stat;
  size_t needed;
  if (!stats)
      return ERROR_INVALID_PARAMETER;

  needed = sizeof(udp_stat);

  if(sysctl(mib, MIB_LEN, &udp_stat, &needed, NULL, 0) == -1)
  {
      ERR ("failed to get udpstat\n");
      return ERROR_NOT_SUPPORTED;
  }

  stats->dwInDatagrams = udp_stat.udps_ipackets;
  stats->dwOutDatagrams = udp_stat.udps_opackets;
  stats->dwNoPorts = udp_stat.udps_noport;
  stats->dwInErrors = udp_stat.udps_hdrops + udp_stat.udps_badsum + udp_stat.udps_fullsock + udp_stat.udps_badlen;
  stats->dwNumAddrs = getNumUdpEntries();

  return NO_ERROR;
#else
  FILE *fp;

  if (!stats)
    return ERROR_INVALID_PARAMETER;

  memset(stats, 0, sizeof(MIB_UDPSTATS));

  /* get from /proc/net/snmp, no error if can't */
  fp = fopen("/proc/net/snmp", "r");
  if (fp) {
    static const char hdr[] = "Udp:";
    char buf[512] = { 0 }, *ptr;


    do {
      ptr = fgets(buf, sizeof(buf), fp);
    } while (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1));
    if (ptr) {
      /* last line was a header, get another */
      ptr = fgets(buf, sizeof(buf), fp);
      if (ptr && strncasecmp(buf, hdr, sizeof(hdr) - 1) == 0) {
        char *endPtr;

        ptr += sizeof(hdr);
        if (ptr && *ptr) {
          stats->dwInDatagrams = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwNoPorts = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwInErrors = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwOutDatagrams = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
        if (ptr && *ptr) {
          stats->dwNumAddrs = strtoul(ptr, &endPtr, 10);
          ptr = endPtr;
        }
      }
    }
    fclose(fp);
  }
  else
  {
     ERR ("unimplemented!\n");
     return ERROR_NOT_SUPPORTED;
  }

  return NO_ERROR;
#endif
}

static DWORD getNumWithOneHeader(const char *filename)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
   size_t Len = 0;
   char *Buf;
   struct xinpgen *pXIG, *pOrigXIG;
   int Protocol;
   DWORD NumEntries = 0;

   if (!strcmp (filename, "net.inet.tcp.pcblist"))
      Protocol = IPPROTO_TCP;
   else if (!strcmp (filename, "net.inet.udp.pcblist"))
      Protocol = IPPROTO_UDP;
   else
   {
      ERR ("Unsupported mib '%s', needs protocol mapping\n",
           filename);
      return 0;
   }

   if (sysctlbyname (filename, NULL, &Len, NULL, 0) < 0)
   {
      WARN ("Unable to read '%s' via sysctlbyname\n", filename);
      return 0;
   }

   Buf = HeapAlloc (GetProcessHeap (), 0, Len);
   if (!Buf)
   {
      ERR ("Out of memory!\n");
      return 0;
   }

   if (sysctlbyname (filename, Buf, &Len, NULL, 0) < 0)
   {
      ERR ("Failure to read '%s' via sysctlbyname!\n", filename);
      HeapFree (GetProcessHeap (), 0, Buf);
      return 0;
   }

   /* Might be nothing here; first entry is just a header it seems */
   if (Len <= sizeof (struct xinpgen))
   {
      HeapFree (GetProcessHeap (), 0, Buf);
      return 0;
   }

   pOrigXIG = (struct xinpgen *)Buf;
   pXIG = pOrigXIG;

   for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
        pXIG->xig_len > sizeof (struct xinpgen);
        pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
   {
      struct tcpcb *pTCPData = NULL;
      struct inpcb *pINData;
      struct xsocket *pSockData;

      if (Protocol == IPPROTO_TCP)
      {
         pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
         pINData = &((struct xtcpcb *)pXIG)->xt_inp;
         pSockData = &((struct xtcpcb *)pXIG)->xt_socket;
      }
      else
      {
         pINData = &((struct xinpcb *)pXIG)->xi_inp;
         pSockData = &((struct xinpcb *)pXIG)->xi_socket;
      }

      /* Ignore sockets for other protocols */
      if (pSockData->xso_protocol != Protocol)
         continue;

      /* Ignore PCBs that were freed while generating the data */
      if (pINData->inp_gencnt > pOrigXIG->xig_gen)
         continue;

      /* we're only interested in IPv4 addresses */
      if (!(pINData->inp_vflag & INP_IPV4) ||
          (pINData->inp_vflag & INP_IPV6))
         continue;

      /* If all 0's, skip it */
      if (!pINData->inp_laddr.s_addr &&
          !pINData->inp_lport &&
          !pINData->inp_faddr.s_addr &&
          !pINData->inp_fport)
         continue;

      NumEntries++;
   }

   HeapFree (GetProcessHeap (), 0, Buf);
   return NumEntries;
#else
  FILE *fp;
  int ret = 0;

  fp = fopen(filename, "r");
  if (fp) {
    char buf[512] = { 0 }, *ptr;


    ptr = fgets(buf, sizeof(buf), fp);
    if (ptr) {
      do {
        ptr = fgets(buf, sizeof(buf), fp);
        if (ptr)
          ret++;
      } while (ptr);
    }
    fclose(fp);
  }
  else
     ERR ("Unable to open '%s' to count entries!\n", filename);

  return ret;
#endif
}

DWORD getNumRoutes(void)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
   int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
   size_t needed;
   char *buf, *lim, *next;
   struct rt_msghdr *rtm;
   DWORD RouteCount = 0;

   if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
   {
      ERR ("sysctl 1 failed!\n");
      return 0;
   }

   buf = HeapAlloc (GetProcessHeap (), 0, needed);
   if (!buf) return 0;

   if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
   {
      ERR ("sysctl 2 failed!\n");
      HeapFree (GetProcessHeap (), 0, buf);
      return 0;
   }

   lim = buf + needed;
   for (next = buf; next < lim; next += rtm->rtm_msglen)
   {
      rtm = (struct rt_msghdr *)next;

      if (rtm->rtm_type != RTM_GET)
      {
         WARN ("Got unexpected message type 0x%x!\n",
               rtm->rtm_type);
         continue;
      }

      /* Ignore all entries except for gateway routes which aren't
         multicast */
      if (!(rtm->rtm_flags & RTF_GATEWAY) || (rtm->rtm_flags & RTF_MULTICAST))
         continue;

      RouteCount++;
   }

   HeapFree (GetProcessHeap (), 0, buf);
   return RouteCount;
#else
   return getNumWithOneHeader("/proc/net/route");
#endif
}

DWORD getRouteTable(PMIB_IPFORWARDTABLE *ppIpForwardTable, HANDLE heap,
 DWORD flags)
{
  DWORD ret;

  if (!ppIpForwardTable)
    ret = ERROR_INVALID_PARAMETER;
  else {
    DWORD numRoutes = getNumRoutes();
    DWORD size = sizeof(MIB_IPFORWARDTABLE);
    PMIB_IPFORWARDTABLE table;

    if (numRoutes > 1)
      size += (numRoutes - 1) * sizeof(MIB_IPFORWARDROW);
    table = HeapAlloc(heap, flags, size);
    if (table) {
#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
       int mib[6] = {CTL_NET, PF_ROUTE, 0, PF_INET, NET_RT_DUMP, 0};
       size_t needed;
       char *buf, *lim, *next, *addrPtr;
       struct rt_msghdr *rtm;

       if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
       {
          ERR ("sysctl 1 failed!\n");
          HeapFree (GetProcessHeap (), 0, table);
          return NO_ERROR;
       }

       buf = HeapAlloc (GetProcessHeap (), 0, needed);
       if (!buf)
       {
          HeapFree (GetProcessHeap (), 0, table);
          return ERROR_OUTOFMEMORY;
       }

       if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0)
       {
          ERR ("sysctl 2 failed!\n");
          HeapFree (GetProcessHeap (), 0, table);
          HeapFree (GetProcessHeap (), 0, buf);
          return NO_ERROR;
       }

       *ppIpForwardTable = table;
       table->dwNumEntries = 0;

       lim = buf + needed;
       for (next = buf; next < lim; next += rtm->rtm_msglen)
       {
          int i;

          rtm = (struct rt_msghdr *)next;

          if (rtm->rtm_type != RTM_GET)
          {
             WARN ("Got unexpected message type 0x%x!\n",
                   rtm->rtm_type);
             continue;
          }

          /* Ignore all entries except for gateway routes which aren't
             multicast */
          if (!(rtm->rtm_flags & RTF_GATEWAY) ||
              (rtm->rtm_flags & RTF_MULTICAST))
             continue;

          memset (&table->table[table->dwNumEntries], 0,
                  sizeof (MIB_IPFORWARDROW));
          table->table[table->dwNumEntries].dwForwardIfIndex = rtm->rtm_index;
          table->table[table->dwNumEntries].dwForwardType =
             MIB_IPROUTE_TYPE_INDIRECT;
          table->table[table->dwNumEntries].dwForwardMetric1 =
             rtm->rtm_rmx.rmx_hopcount;
          table->table[table->dwNumEntries].dwForwardProto =
             MIB_IPPROTO_LOCAL;

          addrPtr = (char *)(rtm + 1);

          for (i = 1; i; i <<= 1)
          {
             struct sockaddr *sa;
             DWORD addr;

             if (!(i & rtm->rtm_addrs))
                continue;

             sa = (struct sockaddr *)addrPtr;
             ADVANCE (addrPtr, sa);

             /* default routes are encoded by length-zero sockaddr */
             if (sa->sa_len == 0)
                addr = 0;
             else if (sa->sa_family != AF_INET)
             {
                WARN ("Received unsupported sockaddr family 0x%x\n",
                     sa->sa_family);
                addr = 0;
             }
             else
             {
                struct sockaddr_in *sin = (struct sockaddr_in *)sa;

                addr = sin->sin_addr.s_addr;
             }

             switch (i)
             {
                case RTA_DST:
                   table->table[table->dwNumEntries].dwForwardDest = addr;
                   break;

                case RTA_GATEWAY:
                   table->table[table->dwNumEntries].dwForwardNextHop = addr;
                   break;

                case RTA_NETMASK:
                   table->table[table->dwNumEntries].dwForwardMask = addr;
                   break;

                default:
                   WARN ("Unexpected address type 0x%x\n", i);
             }
          }

          table->dwNumEntries++;
       }

       HeapFree (GetProcessHeap (), 0, buf);
       ret = NO_ERROR;
#else
      FILE *fp;

      ret = NO_ERROR;
      *ppIpForwardTable = table;
      table->dwNumEntries = 0;
      /* get from /proc/net/route, no error if can't */
      fp = fopen("/proc/net/route", "r");
      if (fp) {
        char buf[512] = { 0 }, *ptr;

        /* skip header line */
        ptr = fgets(buf, sizeof(buf), fp);
        while (ptr && table->dwNumEntries < numRoutes) {
          memset(&table->table[table->dwNumEntries], 0,
           sizeof(MIB_IPFORWARDROW));
          ptr = fgets(buf, sizeof(buf), fp);
          if (ptr) {
            DWORD index;

            while (!isspace(*ptr))
              ptr++;
            *ptr = '\0';
            ptr++;
            if (getInterfaceIndexByName(buf, &index) == NO_ERROR) {
              char *endPtr;

              table->table[table->dwNumEntries].dwForwardIfIndex = index;
              if (*ptr) {
                table->table[table->dwNumEntries].dwForwardDest =
                 strtoul(ptr, &endPtr, 16);
                ptr = endPtr;
              }
              if (ptr && *ptr) {
                table->table[table->dwNumEntries].dwForwardNextHop =
                 strtoul(ptr, &endPtr, 16);
                ptr = endPtr;
              }
              if (ptr && *ptr) {
                DWORD flags = strtoul(ptr, &endPtr, 16);

                if (!(flags & RTF_UP))
                  table->table[table->dwNumEntries].dwForwardType =
                   MIB_IPROUTE_TYPE_INVALID;
                else if (flags & RTF_GATEWAY)
                  table->table[table->dwNumEntries].dwForwardType =
                   MIB_IPROUTE_TYPE_INDIRECT;
                else
                  table->table[table->dwNumEntries].dwForwardType =
                   MIB_IPROUTE_TYPE_DIRECT;
                ptr = endPtr;
              }
              if (ptr && *ptr) {
                strtoul(ptr, &endPtr, 16); /* refcount, skip */
                ptr = endPtr;
              }
              if (ptr && *ptr) {
                strtoul(ptr, &endPtr, 16); /* use, skip */
                ptr = endPtr;
              }
              if (ptr && *ptr) {
                table->table[table->dwNumEntries].dwForwardMetric1 =
                 strtoul(ptr, &endPtr, 16);
                ptr = endPtr;
              }
              if (ptr && *ptr) {
                table->table[table->dwNumEntries].dwForwardMask =
                 strtoul(ptr, &endPtr, 16);
                ptr = endPtr;
              }
              /* FIXME: other protos might be appropriate, e.g. the default
               * route is typically set with MIB_IPPROTO_NETMGMT instead */
              table->table[table->dwNumEntries].dwForwardProto =
               MIB_IPPROTO_LOCAL;
              table->dwNumEntries++;
            }
          }
        }
        fclose(fp);
      }
      else
      {
        ERR ("unimplemented!\n");
        return ERROR_NOT_SUPPORTED;
      }
#endif
    }
    else
      ret = ERROR_OUTOFMEMORY;
  }
  return ret;
}

DWORD getNumArpEntries(void)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
  int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO};
#define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
  DWORD arpEntries = 0;
  size_t needed;
  char *buf, *lim, *next;
  struct rt_msghdr *rtm;
  struct sockaddr_inarp *sinarp;
  struct sockaddr_dl *sdl;

  if (sysctl (mib, MIB_LEN,  NULL, &needed, NULL, 0) == -1)
  {
     ERR ("failed to get size of arp table\n");
     return 0;
  }

  buf = HeapAlloc (GetProcessHeap (), 0, needed);
  if (!buf) return 0;

  if (sysctl (mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
  {
     ERR ("failed to get arp table\n");
     HeapFree (GetProcessHeap (), 0, buf);
     return 0;
  }

  lim = buf + needed;
  next = buf;
  while(next < lim)
  {
      rtm = (struct rt_msghdr *)next;
      sinarp=(struct sockaddr_inarp *)(rtm + 1);
      sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len));
      if(sdl->sdl_alen) /* arp entry */
      arpEntries++;
      next += rtm->rtm_msglen;
  }
  HeapFree (GetProcessHeap (), 0, buf);
  return arpEntries;
#endif
  return getNumWithOneHeader("/proc/net/arp");
}

DWORD getArpTable(PMIB_IPNETTABLE *ppIpNetTable, HANDLE heap, DWORD flags)
{
  DWORD ret = NO_ERROR;
  if (!ppIpNetTable)
    ret = ERROR_INVALID_PARAMETER;
  else {
    DWORD numEntries = getNumArpEntries();
    DWORD size = sizeof(MIB_IPNETTABLE);
    PMIB_IPNETTABLE table;

    if (numEntries > 1)
      size += (numEntries - 1) * sizeof(MIB_IPNETROW);
    table = HeapAlloc(heap, flags, size);
#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
    if (table)
    {
      int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO};
#define MIB_LEN (sizeof(mib) / sizeof(mib[0]))
      size_t needed;
      char *buf, *lim, *next;
      struct rt_msghdr *rtm;
      struct sockaddr_inarp *sinarp;
      struct sockaddr_dl *sdl;

      *ppIpNetTable = table;
      table->dwNumEntries = 0;

      if (sysctl (mib, MIB_LEN,  NULL, &needed, NULL, 0) == -1)
      {
         ERR ("failed to get size of arp table\n");
         return ERROR_NOT_SUPPORTED;
      }

      buf = HeapAlloc (GetProcessHeap (), 0, needed);
      if (!buf) return ERROR_OUTOFMEMORY;

      if (sysctl (mib, MIB_LEN, buf, &needed, NULL, 0) == -1)
      {
         ERR ("failed to get arp table\n");
         HeapFree (GetProcessHeap (), 0, buf);
         return ERROR_NOT_SUPPORTED;
      }

      lim = buf + needed;
      next = buf;
      while(next < lim)
      {
          rtm = (struct rt_msghdr *)next;
          sinarp=(struct sockaddr_inarp *)(rtm + 1);
          sdl = (struct sockaddr_dl *)((char *)sinarp + ROUNDUP(sinarp->sin_len));
          if(sdl->sdl_alen) /* arp entry */
          {
              DWORD byte = strtoul(&sdl->sdl_data[sdl->sdl_alen], NULL, 16);
              memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_IPNETROW));
              table->table[table->dwNumEntries].dwAddr = sinarp->sin_addr.s_addr;
              table->table[table->dwNumEntries].dwIndex = sdl->sdl_index;
              table->table[table->dwNumEntries].dwPhysAddrLen = sdl->sdl_alen;

              table->table[table->dwNumEntries].bPhysAddr[
                  table->table[table->dwNumEntries].dwPhysAddrLen++] =
                  byte & 0x0ff;
              if(rtm->rtm_rmx.rmx_expire == 0)
                  table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_STATIC;
              else if(sinarp->sin_other & SIN_PROXY)
                  table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_OTHER;
              else if(rtm->rtm_rmx.rmx_expire != 0)
                  table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_DYNAMIC;
              else
                  table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_INVALID;

              table->dwNumEntries++;
          }
          next += rtm->rtm_msglen;
      }
      HeapFree (GetProcessHeap (), 0, buf);
    }
    else
        ret = ERROR_OUTOFMEMORY;
  return ret;
#endif

    if (table) {
      FILE *fp;
      *ppIpNetTable = table;
      table->dwNumEntries = 0;
      /* get from /proc/net/arp, no error if can't */
      fp = fopen("/proc/net/arp", "r");
      if (fp) {
        char buf[512] = { 0 }, *ptr;

        /* skip header line */
        ptr = fgets(buf, sizeof(buf), fp);
        while (ptr && table->dwNumEntries < numEntries) {
          ptr = fgets(buf, sizeof(buf), fp);
          if (ptr) {
            char *endPtr;

            memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_IPNETROW));
            table->table[table->dwNumEntries].dwAddr = inet_addr(ptr);
            while (ptr && *ptr && !isspace(*ptr))
              ptr++;

            if (ptr && *ptr) {
              strtoul(ptr, &endPtr, 16); /* hw type (skip) */
              ptr = endPtr;
            }
            if (ptr && *ptr) {
              DWORD flags = strtoul(ptr, &endPtr, 16);

#ifdef ATF_COM
              if (flags & ATF_COM)
                table->table[table->dwNumEntries].dwType =
                 MIB_IPNET_TYPE_DYNAMIC;
              else
#endif
#ifdef ATF_PERM
              if (flags & ATF_PERM)
                table->table[table->dwNumEntries].dwType =
                 MIB_IPNET_TYPE_STATIC;
              else
#endif
                table->table[table->dwNumEntries].dwType = MIB_IPNET_TYPE_OTHER;

              ptr = endPtr;
            }
            while (ptr && *ptr && isspace(*ptr))
              ptr++;
            while (ptr && *ptr && !isspace(*ptr)) {
              DWORD byte = strtoul(ptr, &endPtr, 16);

              if (endPtr && *endPtr) {
                endPtr++;
                table->table[table->dwNumEntries].bPhysAddr[
                 table->table[table->dwNumEntries].dwPhysAddrLen++] =
                 byte & 0x0ff;
              }
              ptr = endPtr;
            }
            if (ptr && *ptr) {
              strtoul(ptr, &endPtr, 16); /* mask (skip) */
              ptr = endPtr;
            }
            getInterfaceIndexByName(ptr,
             &table->table[table->dwNumEntries].dwIndex);
            table->dwNumEntries++;
          }
        }
        fclose(fp);
      }
      else
        ret = ERROR_NOT_SUPPORTED;
    }
    else
      ret = ERROR_OUTOFMEMORY;
  }
  return ret;
}

DWORD getNumUdpEntries(void)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
   return getNumWithOneHeader ("net.inet.udp.pcblist");
#else
  return getNumWithOneHeader("/proc/net/udp");
#endif
}

DWORD getUdpTable(PMIB_UDPTABLE *ppUdpTable, HANDLE heap, DWORD flags)
{
  DWORD ret;

#if defined(HAVE_SYS_SYSCTL_H) && defined(NET_RT_DUMP)
  ERR ("unimplemented!\n");
  return ERROR_NOT_SUPPORTED;
#endif

  if (!ppUdpTable)
    ret = ERROR_INVALID_PARAMETER;
  else {
    DWORD numEntries = getNumUdpEntries();
    DWORD size = sizeof(MIB_UDPTABLE);
    PMIB_UDPTABLE table;

    if (numEntries > 1)
      size += (numEntries - 1) * sizeof(MIB_UDPROW);
    table = HeapAlloc(heap, flags, size);
    if (table) {
      FILE *fp;

      ret = NO_ERROR;
      *ppUdpTable = table;
      table->dwNumEntries = 0;
      /* get from /proc/net/udp, no error if can't */
      fp = fopen("/proc/net/udp", "r");
      if (fp) {
        char buf[512] = { 0 }, *ptr;

        /* skip header line */
        ptr = fgets(buf, sizeof(buf), fp);
        while (ptr && table->dwNumEntries < numEntries) {
          memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_UDPROW));
          ptr = fgets(buf, sizeof(buf), fp);
          if (ptr) {
            char *endPtr;

            if (ptr && *ptr) {
              strtoul(ptr, &endPtr, 16); /* skip */
              ptr = endPtr;
            }
            if (ptr && *ptr) {
              ptr++;
              table->table[table->dwNumEntries].dwLocalAddr = strtoul(ptr,
               &endPtr, 16);
              ptr = endPtr;
            }
            if (ptr && *ptr) {
              ptr++;
              table->table[table->dwNumEntries].dwLocalPort = strtoul(ptr,
               &endPtr, 16);
              ptr = endPtr;
            }
            table->dwNumEntries++;
          }
        }
        fclose(fp);
      }
      else
        ret = ERROR_NOT_SUPPORTED;
    }
    else
      ret = ERROR_OUTOFMEMORY;
  }
  return ret;
}


DWORD getNumTcpEntries(void)
{
#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
   return getNumWithOneHeader ("net.inet.tcp.pcblist");
#else
   return getNumWithOneHeader ("/proc/net/tcp");
#endif
}


/* Why not a lookup table? Because the TCPS_* constants are different
   on different platforms */
static DWORD TCPStateToMIBState (int state)
{
   switch (state)
   {
      case TCPS_ESTABLISHED: return MIB_TCP_STATE_ESTAB;
      case TCPS_SYN_SENT: return MIB_TCP_STATE_SYN_SENT;
      case TCPS_SYN_RECEIVED: return MIB_TCP_STATE_SYN_RCVD;
      case TCPS_FIN_WAIT_1: return MIB_TCP_STATE_FIN_WAIT1;
      case TCPS_FIN_WAIT_2: return MIB_TCP_STATE_FIN_WAIT2;
      case TCPS_TIME_WAIT: return MIB_TCP_STATE_TIME_WAIT;
      case TCPS_CLOSE_WAIT: return MIB_TCP_STATE_CLOSE_WAIT;
      case TCPS_LAST_ACK: return MIB_TCP_STATE_LAST_ACK;
      case TCPS_LISTEN: return MIB_TCP_STATE_LISTEN;
      case TCPS_CLOSING: return MIB_TCP_STATE_CLOSING;
      default:
      case TCPS_CLOSED: return MIB_TCP_STATE_CLOSED;
   }
}


DWORD getTcpTable(PMIB_TCPTABLE *ppTcpTable, DWORD maxEntries, HANDLE heap,
                  DWORD flags)
{
   DWORD numEntries;
   PMIB_TCPTABLE table;
#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)
   size_t Len = 0;
   char *Buf;
   struct xinpgen *pXIG, *pOrigXIG;
#else
   FILE *fp;
   char buf[512] = { 0 }, *ptr;
#endif

   if (!ppTcpTable)
      return ERROR_INVALID_PARAMETER;

   numEntries = getNumTcpEntries ();

   if (!*ppTcpTable)
   {
      DWORD size = sizeof(MIB_TCPTABLE);

      if (numEntries > 1)
         size += (numEntries - 1) * sizeof (MIB_TCPROW);
      *ppTcpTable = HeapAlloc (heap, flags, size);
      if (!*ppTcpTable)
      {
         ERR ("Out of memory!\n");
         return ERROR_OUTOFMEMORY;
      }
      maxEntries = numEntries;
   }

   table = *ppTcpTable;
   table->dwNumEntries = 0;
   if (!numEntries)
      return NO_ERROR;

#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_NETINET_IN_PCB_H)

   if (sysctlbyname ("net.inet.tcp.pcblist", NULL, &Len, NULL, 0) < 0)
   {
      ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
      return ERROR_OUTOFMEMORY;
   }

   Buf = HeapAlloc (GetProcessHeap (), 0, Len);
   if (!Buf)
   {
      ERR ("Out of memory!\n");
      return ERROR_OUTOFMEMORY;
   }

   if (sysctlbyname ("net.inet.tcp.pcblist", Buf, &Len, NULL, 0) < 0)
   {
      ERR ("Failure to read net.inet.tcp.pcblist via sysctlbyname!\n");
      HeapFree (GetProcessHeap (), 0, Buf);
      return ERROR_OUTOFMEMORY;
   }

   /* Might be nothing here; first entry is just a header it seems */
   if (Len <= sizeof (struct xinpgen))
   {
      HeapFree (GetProcessHeap (), 0, Buf);
      return NO_ERROR;
   }

   pOrigXIG = (struct xinpgen *)Buf;
   pXIG = pOrigXIG;

   for (pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len);
        (pXIG->xig_len > sizeof (struct xinpgen)) &&
           (table->dwNumEntries < maxEntries);
        pXIG = (struct xinpgen *)((char *)pXIG + pXIG->xig_len))
   {
      struct tcpcb *pTCPData = NULL;
      struct inpcb *pINData;
      struct xsocket *pSockData;

      pTCPData = &((struct xtcpcb *)pXIG)->xt_tp;
      pINData = &((struct xtcpcb *)pXIG)->xt_inp;
      pSockData = &((struct xtcpcb *)pXIG)->xt_socket;

      /* Ignore sockets for other protocols */
      if (pSockData->xso_protocol != IPPROTO_TCP)
         continue;

      /* Ignore PCBs that were freed while generating the data */
      if (pINData->inp_gencnt > pOrigXIG->xig_gen)
         continue;

      /* we're only interested in IPv4 addresses */
      if (!(pINData->inp_vflag & INP_IPV4) ||
          (pINData->inp_vflag & INP_IPV6))
         continue;

      /* If all 0's, skip it */
      if (!pINData->inp_laddr.s_addr &&
          !pINData->inp_lport &&
          !pINData->inp_faddr.s_addr &&
          !pINData->inp_fport)
         continue;

      /* Fill in structure details */
      table->table[table->dwNumEntries].dwLocalAddr =
         pINData->inp_laddr.s_addr;
      table->table[table->dwNumEntries].dwLocalPort =
         pINData->inp_lport;
      table->table[table->dwNumEntries].dwRemoteAddr =
         pINData->inp_faddr.s_addr;
      table->table[table->dwNumEntries].dwRemotePort =
         pINData->inp_fport;
      table->table[table->dwNumEntries].dwState =
         TCPStateToMIBState (pTCPData->t_state);

      table->dwNumEntries++;
   }

   HeapFree (GetProcessHeap (), 0, Buf);
#else
   /* get from /proc/net/tcp, no error if can't */
   fp = fopen("/proc/net/tcp", "r");
   if (!fp)
      return ERROR_NOT_SUPPORTED;

   /* skip header line */
   ptr = fgets(buf, sizeof(buf), fp);
   while (ptr && table->dwNumEntries < maxEntries) {
      memset(&table->table[table->dwNumEntries], 0, sizeof(MIB_TCPROW));
      ptr = fgets(buf, sizeof(buf), fp);
      if (ptr) {
         char *endPtr;

         while (ptr && *ptr && *ptr != ':')
            ptr++;
         if (ptr && *ptr)
            ptr++;
         if (ptr && *ptr) {
            table->table[table->dwNumEntries].dwLocalAddr =
               strtoul(ptr, &endPtr, 16);
            ptr = endPtr;
         }
         if (ptr && *ptr) {
            ptr++;
            table->table[table->dwNumEntries].dwLocalPort =
               htons ((unsigned short)strtoul(ptr, &endPtr, 16));
            ptr = endPtr;
         }
         if (ptr && *ptr) {
            table->table[table->dwNumEntries].dwRemoteAddr =
               strtoul(ptr, &endPtr, 16);
            ptr = endPtr;
         }
         if (ptr && *ptr) {
            ptr++;
            table->table[table->dwNumEntries].dwRemotePort =
               htons ((unsigned short)strtoul(ptr, &endPtr, 16));
            ptr = endPtr;
         }
         if (ptr && *ptr) {
            DWORD state = strtoul(ptr, &endPtr, 16);

            table->table[table->dwNumEntries].dwState =
               TCPStateToMIBState (state);
            ptr = endPtr;
         }
         table->dwNumEntries++;
      }
   }
   fclose(fp);
#endif

   return NO_ERROR;
}
