text: /*
   * Copyright 2011-2013 André Hentschel
   *
   * 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
   */
  
  #define NONAMELESSUNION
  #include "netstat.h"
  #include <winsock2.h>
  #include <iphlpapi.h>
  #include "wine/unicode.h"
  #include "wine/debug.h"
  
  WINE_DEFAULT_DEBUG_CHANNEL(netstat);
  
  static const WCHAR ipW[] = {'I', 'P', 0};
  static const WCHAR ipv6W[] = {'I', 'P', 'v', '6', 0};
  static const WCHAR icmpW[] = {'I', 'C', 'M', 'P', 0};
  static const WCHAR icmpv6W[] = {'I', 'C', 'M', 'P', 'v', '6', 0};
  static const WCHAR tcpW[] = {'T', 'C', 'P', 0};
  static const WCHAR tcpv6W[] = {'T', 'C', 'P', 'v', '6', 0};
  static const WCHAR udpW[] = {'U', 'D', 'P', 0};
  static const WCHAR udpv6W[] = {'U', 'D', 'P', 'v', '6', 0};
  
  static const WCHAR fmtport[] = {'%', 'd', 0};
  static const WCHAR fmtip[] = {'%', 'd', '.', '%', 'd', '.', '%', 'd', '.', '%', 'd', 0};
  static const WCHAR fmtn[] = {'\n', 0};
  static const WCHAR fmtnn[] = {'\n', '%', 's', '\n', 0};
  static const WCHAR fmtcolon[] = {'%', 's', ':', '%', 's', 0};
  static const WCHAR fmttcpout[] = {' ', ' ', '%', '-', '6', 's', ' ', '%', '-', '2', '2', 's', ' ', '%', '-', '2', '2', 's', ' ', '%', 's', '\n', 0};
  static const WCHAR fmtudpout[] = {' ', ' ', '%', '-', '6', 's', ' ', '%', '-', '2', '2', 's', ' ', '*', ':', '*', '\n', 0};
  static const WCHAR fmtethout[] = {'%', '-', '2', '0', 's', ' ', '%', '1', '4', 'l', 'u', ' ', '%', '1', '5', 'l', 'u', '\n', 0};
  static const WCHAR fmtethoutu[] = {'%', '-', '2', '0', 's', ' ', '%', '1', '4', 'l', 'u', '\n', '\n', 0};
  static const WCHAR fmtethheader[] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                                       ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
                                       ' ', '%', '-', '1', '9', 's', ' ', '%', 's', '\n', '\n', 0};
  static const WCHAR fmttcpstat[] = {' ', ' ', '%', '-', '3', '5', 's', ' ', '=', ' ', '%', 'l', 'u', '\n', 0};
  static const WCHAR fmtudpstat[] = {' ', ' ', '%', '-', '2', '1', 's', ' ', '=', ' ', '%', 'l', 'u', '\n', 0};
  
  static const WCHAR tcpstatesW[][16] = {
      {'?', '?', '?', 0},
      {'C', 'L', 'O', 'S', 'E', 'D', 0},
      {'L', 'I', 'S', 'T', 'E', 'N', 'I', 'N', 'G', 0},
      {'S', 'Y', 'N', '_', 'S', 'E', 'N', 'T', 0},
      {'S', 'Y', 'N', '_', 'R', 'C', 'V', 'D', 0},
      {'E', 'S', 'T', 'A', 'B', 'L', 'I', 'S', 'H', 'E', 'D', 0},
      {'F', 'I', 'N', '_', 'W', 'A', 'I', 'T', '1', 0},
      {'F', 'I', 'N', '_', 'W', 'A', 'I', 'T', '2', 0},
      {'C', 'L', 'O', 'S', 'E', '_', 'W', 'A', 'I', 'T', 0},
      {'C', 'L', 'O', 'S', 'I', 'N', 'G', 0},
      {'L', 'A', 'S', 'T', '_', 'A', 'C', 'K', 0},
      {'T', 'I', 'M', 'E', '_', 'W', 'A', 'I', 'T', 0},
      {'D', 'E', 'L', 'E', 'T', 'E', '_', 'T', 'C', 'B', 0},
  };
  
  /* =========================================================================
   *  Output a unicode string. Ideally this will go to the console
   *  and hence required WriteConsoleW to output it, however if file i/o is
   *  redirected, it needs to be WriteFile'd using OEM (not ANSI) format
   * ========================================================================= */
  static int __cdecl NETSTAT_wprintf(const WCHAR *format, ...)
  {
      static WCHAR *output_bufW = NULL;
      static char  *output_bufA = NULL;
      static BOOL  toConsole    = TRUE;
      static BOOL  traceOutput  = FALSE;
  #define MAX_WRITECONSOLE_SIZE 65535
  
      __ms_va_list parms;
      DWORD   nOut;
      int len;
      DWORD   res = 0;
  
      /*
       * Allocate buffer to use when writing to console
       * Note: Not freed - memory will be allocated once and released when
       *         xcopy ends
       */
  
      if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
                                                MAX_WRITECONSOLE_SIZE);
      if (!output_bufW) {
          WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
          return 0;
      }
  
      __ms_va_start(parms, format);
      len = wvsprintfW(output_bufW, format, parms);
      __ms_va_end(parms);
  
      /* Try to write as unicode all the time we think its a console */
      if (toConsole) {
          res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
                              output_bufW, len, &nOut, NULL);
      }
  
      /* If writing to console has failed (ever) we assume its file
         i/o so convert to OEM codepage and output                  */
      if (!res) {
          BOOL usedDefaultChar = FALSE;
          DWORD convertedChars;
  
          toConsole = FALSE;
  
          /*
           * Allocate buffer to use when writing to file. Not freed, as above
           */
          if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
                                                    MAX_WRITECONSOLE_SIZE);
          if (!output_bufA) {
              WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
              return 0;
          }
  
          /* Convert to OEM, then output */
          convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
                                               len, output_bufA, MAX_WRITECONSOLE_SIZE,
                                               "?", &usedDefaultChar);
          WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
                    &nOut, FALSE);
      }
  
      /* Trace whether screen or console */
      if (!traceOutput) {
          WINE_TRACE("Writing to console? (%d)\n", toConsole);
          traceOutput = TRUE;
      }
      return nOut;
  }
  
  static WCHAR *NETSTAT_load_message(UINT id) {
      static WCHAR msg[2048];
      static const WCHAR failedW[]  = {'F','a','i','l','e','d','!','\0'};
  
      if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
          WINE_FIXME("LoadString failed with %d\n", GetLastError());
          strcpyW(msg, failedW);
      }
      return msg;
  }
  
  static WCHAR *NETSTAT_port_name(UINT port, WCHAR name[])
  {
      /* FIXME: can we get the name? */
      sprintfW(name, fmtport, htons((WORD)port));
      return name;
  }
  
  static WCHAR *NETSTAT_host_name(UINT ip, WCHAR name[])
  {
      UINT nip;
  
      /* FIXME: can we get the name? */
      nip = htonl(ip);
      sprintfW(name, fmtip, (nip >> 24) & 0xFF, (nip >> 16) & 0xFF, (nip >> 8) & 0xFF, (nip) & 0xFF);
      return name;
  }
  
  static void NETSTAT_conn_header(void)
  {
      WCHAR local[22], remote[22], state[22];
      NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_TCP_ACTIVE_CONN));
      NETSTAT_wprintf(fmtn);
      strcpyW(local, NETSTAT_load_message(IDS_TCP_LOCAL_ADDR));
      strcpyW(remote, NETSTAT_load_message(IDS_TCP_REMOTE_ADDR));
      strcpyW(state, NETSTAT_load_message(IDS_TCP_STATE));
      NETSTAT_wprintf(fmttcpout, NETSTAT_load_message(IDS_TCP_PROTO), local, remote, state);
  }
  
  static void NETSTAT_eth_stats(void)
  {
      PMIB_IFTABLE table;
      DWORD err, size, i;
      DWORD octets[2], ucastpkts[2], nucastpkts[2], discards[2], errors[2], unknown;
      WCHAR recv[19];
  
      size = sizeof(MIB_IFTABLE);
      do
      {
          table = (PMIB_IFTABLE)HeapAlloc(GetProcessHeap(), 0, size);
          err = GetIfTable(table, &size, FALSE);
          if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
      } while (err == ERROR_INSUFFICIENT_BUFFER);
  
      if (err) return;
  
      NETSTAT_wprintf(NETSTAT_load_message(IDS_ETH_STAT));
      NETSTAT_wprintf(fmtn);
      NETSTAT_wprintf(fmtn);
      strcpyW(recv, NETSTAT_load_message(IDS_ETH_RECV));
      NETSTAT_wprintf(fmtethheader, recv, NETSTAT_load_message(IDS_ETH_SENT));
  
      octets[0] = octets[1] = 0;
      ucastpkts[0] = ucastpkts[1] = 0;
      nucastpkts[0] = nucastpkts[1] = 0;
      discards[0] = discards[1] = 0;
      errors[0] = errors[1] = 0;
      unknown = 0;
  
      for (i = 0; i < table->dwNumEntries; i++)
      {
          octets[0] += table->table[i].dwInOctets;
          octets[1] += table->table[i].dwOutOctets;
          ucastpkts[0] += table->table[i].dwInUcastPkts;
          ucastpkts[1] += table->table[i].dwOutUcastPkts;
          nucastpkts[0] += table->table[i].dwInNUcastPkts;
          nucastpkts[1] += table->table[i].dwOutNUcastPkts;
          discards[0] += table->table[i].dwInDiscards;
          discards[1] += table->table[i].dwOutDiscards;
          errors[0] += table->table[i].dwInErrors;
          errors[1] += table->table[i].dwOutErrors;
          unknown += table->table[i].dwInUnknownProtos;
      }
  
      NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_BYTES), octets[0], octets[1]);
      NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_UNICAST), ucastpkts[0], ucastpkts[1]);
      NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_NUNICAST), nucastpkts[0], nucastpkts[1]);
      NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_DISCARDS), discards[0], discards[1]);
      NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_ERRORS), errors[0], errors[1]);
      NETSTAT_wprintf(fmtethoutu, NETSTAT_load_message(IDS_ETH_UNKNOWN), unknown);
  
      HeapFree(GetProcessHeap(), 0, table);
  }
  
  static void NETSTAT_tcp_table(void)
  {
      PMIB_TCPTABLE table;
      DWORD err, size, i;
      WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
      WCHAR RemoteIp[MAX_HOSTNAME_LEN], RemotePort[32];
      WCHAR Host[MAX_HOSTNAME_LEN + 32];
      WCHAR Remote[MAX_HOSTNAME_LEN + 32];
  
      size = sizeof(MIB_TCPTABLE);
      do
      {
          table = (PMIB_TCPTABLE)HeapAlloc(GetProcessHeap(), 0, size);
          err = GetTcpTable(table, &size, TRUE);
          if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
      } while (err == ERROR_INSUFFICIENT_BUFFER);
  
      if (err) return;
  
      for (i = 0; i < table->dwNumEntries; i++)
      {
          if ((table->table[i].u.dwState ==  MIB_TCP_STATE_CLOSE_WAIT) ||
              (table->table[i].u.dwState ==  MIB_TCP_STATE_ESTAB) ||
              (table->table[i].u.dwState ==  MIB_TCP_STATE_TIME_WAIT))
          {
              NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
              NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
              NETSTAT_host_name(table->table[i].dwRemoteAddr, RemoteIp);
              NETSTAT_port_name(table->table[i].dwRemotePort, RemotePort);
  
              sprintfW(Host, fmtcolon, HostIp, HostPort);
              sprintfW(Remote, fmtcolon, RemoteIp, RemotePort);
              NETSTAT_wprintf(fmttcpout, tcpW, Host, Remote, tcpstatesW[table->table[i].u.dwState]);
          }
      }
      HeapFree(GetProcessHeap(), 0, table);
  }
  
  static void NETSTAT_tcp_stats(void)
  {
      PMIB_TCPSTATS stats;
  
      stats = (PMIB_TCPSTATS)HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_TCPSTATS));
  
      if (GetTcpStatistics(stats) == NO_ERROR)
      {
          NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_TCP_STAT));
          NETSTAT_wprintf(fmtn);
          NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_ACTIVE_OPEN), stats->dwActiveOpens);
          NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_PASSIV_OPEN), stats->dwPassiveOpens);
          NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_FAILED_CONN), stats->dwAttemptFails);
          NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_RESET_CONN),  stats->dwEstabResets);
          NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_CURR_CONN),   stats->dwCurrEstab);
          NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_SEGM_RECV),   stats->dwInSegs);
          NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_SEGM_SENT),   stats->dwOutSegs);
          NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_SEGM_RETRAN), stats->dwRetransSegs);
      }
  
      HeapFree(GetProcessHeap(), 0, stats);
  }
  
  static void NETSTAT_udp_table(void)
  {
      PMIB_UDPTABLE table;
      DWORD err, size, i;
      WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
      WCHAR Host[MAX_HOSTNAME_LEN + 32];
  
      size = sizeof(MIB_UDPTABLE);
      do
      {
          table = (PMIB_UDPTABLE)HeapAlloc(GetProcessHeap(), 0, size);
          err = GetUdpTable(table, &size, TRUE);
          if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
      } while (err == ERROR_INSUFFICIENT_BUFFER);
  
      if (err) return;
  
      for (i = 0; i < table->dwNumEntries; i++)
      {
          NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
          NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
  
          sprintfW(Host, fmtcolon, HostIp, HostPort);
          NETSTAT_wprintf(fmtudpout, udpW, Host);
      }
      HeapFree(GetProcessHeap(), 0, table);
  }
  
  static void NETSTAT_udp_stats(void)
  {
      PMIB_UDPSTATS stats;
  
      stats = (PMIB_UDPSTATS)HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_UDPSTATS));
  
      if (GetUdpStatistics(stats) == NO_ERROR)
      {
          NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_UDP_STAT));
          NETSTAT_wprintf(fmtn);
          NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_DGRAMS_RECV), stats->dwInDatagrams);
          NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_NO_PORTS), stats->dwNoPorts);
          NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_RECV_ERRORS), stats->dwInErrors);
          NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_DGRAMS_SENT),  stats->dwOutDatagrams);
      }
  
      HeapFree(GetProcessHeap(), 0, stats);
  }
  
  static NETSTATPROTOCOLS NETSTAT_get_protocol(WCHAR name[])
  {
      if (!strcmpiW(name, ipW)) return PROT_IP;
      if (!strcmpiW(name, ipv6W)) return PROT_IPV6;
      if (!strcmpiW(name, icmpW)) return PROT_ICMP;
      if (!strcmpiW(name, icmpv6W)) return PROT_ICMPV6;
      if (!strcmpiW(name, tcpW)) return PROT_TCP;
      if (!strcmpiW(name, tcpv6W)) return PROT_TCPV6;
      if (!strcmpiW(name, udpW)) return PROT_UDP;
      if (!strcmpiW(name, udpv6W)) return PROT_UDPV6;
      return PROT_UNKNOWN;
  }
  
  int wmain(int argc, WCHAR *argv[])
  {
      WSADATA wsa_data;
      BOOL output_stats = FALSE;
  
      if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
      {
          WINE_ERR("WSAStartup failed: %d\n", WSAGetLastError());
          return 1;
      }
  
      if (argc == 1)
      {
          /* No options */
          NETSTAT_conn_header();
          NETSTAT_tcp_table();
          return 0;
      }
  
      while (argv[1] && argv[1][0] == '-')
      {
          switch (argv[1][1])
          {
          case 'a':
              NETSTAT_conn_header();
              NETSTAT_tcp_table();
              NETSTAT_udp_table();
              return 0;
          case 'e':
              NETSTAT_eth_stats();
              return 0;
          case 's':
              output_stats = TRUE;
              break;
          case 'p':
              argv++; argc--;
              if (argc == 1) return 1;
              switch (NETSTAT_get_protocol(argv[1]))
              {
                  case PROT_TCP:
                      if (output_stats)
                          NETSTAT_tcp_stats();
                      NETSTAT_conn_header();
                      NETSTAT_tcp_table();
                      break;
                  case PROT_UDP:
                      if (output_stats)
                          NETSTAT_udp_stats();
                      NETSTAT_conn_header();
                      NETSTAT_udp_table();
                      break;
                  default:
                      WINE_FIXME("Protocol not yet implemented: %s\n", debugstr_w(argv[1]));
              }
              return 0;
          default:
              WINE_FIXME("Unknown option: %s\n", debugstr_w(argv[1]));
              return 1;
          }
          argv++; argc--;
      }
  
      if (output_stats)
      {
          NETSTAT_tcp_stats();
          NETSTAT_udp_stats();
      }
  
      return 0;
  }
  