netstat.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. /*
  2. * Copyright 2011-2013 André Hentschel
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  17. */
  18. #define NONAMELESSUNION
  19. #include <stdio.h>
  20. #include "netstat.h"
  21. #include <winsock2.h>
  22. #include <iphlpapi.h>
  23. #include "wine/debug.h"
  24. WINE_DEFAULT_DEBUG_CHANNEL(netstat);
  25. static const WCHAR ipW[] = {'I', 'P', 0};
  26. static const WCHAR ipv6W[] = {'I', 'P', 'v', '6', 0};
  27. static const WCHAR icmpW[] = {'I', 'C', 'M', 'P', 0};
  28. static const WCHAR icmpv6W[] = {'I', 'C', 'M', 'P', 'v', '6', 0};
  29. static const WCHAR tcpW[] = {'T', 'C', 'P', 0};
  30. static const WCHAR tcpv6W[] = {'T', 'C', 'P', 'v', '6', 0};
  31. static const WCHAR udpW[] = {'U', 'D', 'P', 0};
  32. static const WCHAR udpv6W[] = {'U', 'D', 'P', 'v', '6', 0};
  33. static const WCHAR fmtport[] = {'%', 'd', 0};
  34. static const WCHAR fmtip[] = {'%', 'd', '.', '%', 'd', '.', '%', 'd', '.', '%', 'd', 0};
  35. static const WCHAR fmtn[] = {'\n', 0};
  36. static const WCHAR fmtnn[] = {'\n', '%', 's', '\n', 0};
  37. static const WCHAR fmtcolon[] = {'%', 's', ':', '%', 's', 0};
  38. static const WCHAR fmttcpout[] = {' ', ' ', '%', '-', '6', 's', ' ', '%', '-', '2', '2', 's', ' ', '%', '-', '2', '2', 's', ' ', '%', 's', '\n', 0};
  39. static const WCHAR fmtudpout[] = {' ', ' ', '%', '-', '6', 's', ' ', '%', '-', '2', '2', 's', ' ', '*', ':', '*', '\n', 0};
  40. static const WCHAR fmtethout[] = {'%', '-', '2', '0', 's', ' ', '%', '1', '4', 'l', 'u', ' ', '%', '1', '5', 'l', 'u', '\n', 0};
  41. static const WCHAR fmtethoutu[] = {'%', '-', '2', '0', 's', ' ', '%', '1', '4', 'l', 'u', '\n', '\n', 0};
  42. static const WCHAR fmtethheader[] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
  43. ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
  44. ' ', '%', '-', '1', '9', 's', ' ', '%', 's', '\n', '\n', 0};
  45. static const WCHAR fmttcpstat[] = {' ', ' ', '%', '-', '3', '5', 's', ' ', '=', ' ', '%', 'l', 'u', '\n', 0};
  46. static const WCHAR fmtudpstat[] = {' ', ' ', '%', '-', '2', '1', 's', ' ', '=', ' ', '%', 'l', 'u', '\n', 0};
  47. static const WCHAR tcpstatesW[][16] = {
  48. {'?', '?', '?', 0},
  49. {'C', 'L', 'O', 'S', 'E', 'D', 0},
  50. {'L', 'I', 'S', 'T', 'E', 'N', 'I', 'N', 'G', 0},
  51. {'S', 'Y', 'N', '_', 'S', 'E', 'N', 'T', 0},
  52. {'S', 'Y', 'N', '_', 'R', 'C', 'V', 'D', 0},
  53. {'E', 'S', 'T', 'A', 'B', 'L', 'I', 'S', 'H', 'E', 'D', 0},
  54. {'F', 'I', 'N', '_', 'W', 'A', 'I', 'T', '1', 0},
  55. {'F', 'I', 'N', '_', 'W', 'A', 'I', 'T', '2', 0},
  56. {'C', 'L', 'O', 'S', 'E', '_', 'W', 'A', 'I', 'T', 0},
  57. {'C', 'L', 'O', 'S', 'I', 'N', 'G', 0},
  58. {'L', 'A', 'S', 'T', '_', 'A', 'C', 'K', 0},
  59. {'T', 'I', 'M', 'E', '_', 'W', 'A', 'I', 'T', 0},
  60. {'D', 'E', 'L', 'E', 'T', 'E', '_', 'T', 'C', 'B', 0},
  61. };
  62. /* =========================================================================
  63. * Output a unicode string. Ideally this will go to the console
  64. * and hence required WriteConsoleW to output it, however if file i/o is
  65. * redirected, it needs to be WriteFile'd using OEM (not ANSI) format
  66. * ========================================================================= */
  67. static int WINAPIV NETSTAT_wprintf(const WCHAR *format, ...)
  68. {
  69. static WCHAR *output_bufW = NULL;
  70. static char *output_bufA = NULL;
  71. static BOOL toConsole = TRUE;
  72. static BOOL traceOutput = FALSE;
  73. #define MAX_WRITECONSOLE_SIZE 65535
  74. va_list parms;
  75. DWORD nOut;
  76. int len;
  77. DWORD res = 0;
  78. /*
  79. * Allocate buffer to use when writing to console
  80. * Note: Not freed - memory will be allocated once and released when
  81. * netstat ends
  82. */
  83. if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
  84. MAX_WRITECONSOLE_SIZE*sizeof(WCHAR));
  85. if (!output_bufW) {
  86. WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
  87. return 0;
  88. }
  89. va_start(parms, format);
  90. len = wvsprintfW(output_bufW, format, parms);
  91. va_end(parms);
  92. /* Try to write as unicode all the time we think it's a console */
  93. if (toConsole) {
  94. res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
  95. output_bufW, len, &nOut, NULL);
  96. }
  97. /* If writing to console has failed (ever) we assume it's file
  98. i/o so convert to OEM codepage and output */
  99. if (!res) {
  100. BOOL usedDefaultChar = FALSE;
  101. DWORD convertedChars;
  102. toConsole = FALSE;
  103. /*
  104. * Allocate buffer to use when writing to file. Not freed, as above
  105. */
  106. if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
  107. MAX_WRITECONSOLE_SIZE);
  108. if (!output_bufA) {
  109. WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
  110. return 0;
  111. }
  112. /* Convert to OEM, then output */
  113. convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
  114. len, output_bufA, MAX_WRITECONSOLE_SIZE,
  115. "?", &usedDefaultChar);
  116. WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
  117. &nOut, FALSE);
  118. }
  119. /* Trace whether screen or console */
  120. if (!traceOutput) {
  121. WINE_TRACE("Writing to console? (%d)\n", toConsole);
  122. traceOutput = TRUE;
  123. }
  124. return nOut;
  125. }
  126. static WCHAR *NETSTAT_load_message(UINT id) {
  127. static WCHAR msg[2048];
  128. static const WCHAR failedW[] = {'F','a','i','l','e','d','!','\0'};
  129. if (!LoadStringW(GetModuleHandleW(NULL), id, msg, ARRAY_SIZE(msg))) {
  130. WINE_FIXME("LoadString failed with %d\n", GetLastError());
  131. lstrcpyW(msg, failedW);
  132. }
  133. return msg;
  134. }
  135. static WCHAR *NETSTAT_port_name(UINT port, WCHAR name[])
  136. {
  137. /* FIXME: can we get the name? */
  138. swprintf(name, 32, fmtport, htons((WORD)port));
  139. return name;
  140. }
  141. static WCHAR *NETSTAT_host_name(UINT ip, WCHAR name[])
  142. {
  143. UINT nip;
  144. /* FIXME: can we get the name? */
  145. nip = htonl(ip);
  146. swprintf(name, MAX_HOSTNAME_LEN, fmtip,
  147. (nip >> 24) & 0xFF, (nip >> 16) & 0xFF, (nip >> 8) & 0xFF, (nip) & 0xFF);
  148. return name;
  149. }
  150. static void NETSTAT_conn_header(void)
  151. {
  152. WCHAR local[22], remote[22], state[22];
  153. NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_TCP_ACTIVE_CONN));
  154. NETSTAT_wprintf(fmtn);
  155. lstrcpyW(local, NETSTAT_load_message(IDS_TCP_LOCAL_ADDR));
  156. lstrcpyW(remote, NETSTAT_load_message(IDS_TCP_REMOTE_ADDR));
  157. lstrcpyW(state, NETSTAT_load_message(IDS_TCP_STATE));
  158. NETSTAT_wprintf(fmttcpout, NETSTAT_load_message(IDS_TCP_PROTO), local, remote, state);
  159. }
  160. static void NETSTAT_eth_stats(void)
  161. {
  162. PMIB_IFTABLE table;
  163. DWORD err, size, i;
  164. DWORD octets[2], ucastpkts[2], nucastpkts[2], discards[2], errors[2], unknown;
  165. WCHAR recv[19];
  166. size = sizeof(MIB_IFTABLE);
  167. do
  168. {
  169. table = HeapAlloc(GetProcessHeap(), 0, size);
  170. err = GetIfTable(table, &size, FALSE);
  171. if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
  172. } while (err == ERROR_INSUFFICIENT_BUFFER);
  173. if (err) return;
  174. NETSTAT_wprintf(NETSTAT_load_message(IDS_ETH_STAT));
  175. NETSTAT_wprintf(fmtn);
  176. NETSTAT_wprintf(fmtn);
  177. lstrcpyW(recv, NETSTAT_load_message(IDS_ETH_RECV));
  178. NETSTAT_wprintf(fmtethheader, recv, NETSTAT_load_message(IDS_ETH_SENT));
  179. octets[0] = octets[1] = 0;
  180. ucastpkts[0] = ucastpkts[1] = 0;
  181. nucastpkts[0] = nucastpkts[1] = 0;
  182. discards[0] = discards[1] = 0;
  183. errors[0] = errors[1] = 0;
  184. unknown = 0;
  185. for (i = 0; i < table->dwNumEntries; i++)
  186. {
  187. octets[0] += table->table[i].dwInOctets;
  188. octets[1] += table->table[i].dwOutOctets;
  189. ucastpkts[0] += table->table[i].dwInUcastPkts;
  190. ucastpkts[1] += table->table[i].dwOutUcastPkts;
  191. nucastpkts[0] += table->table[i].dwInNUcastPkts;
  192. nucastpkts[1] += table->table[i].dwOutNUcastPkts;
  193. discards[0] += table->table[i].dwInDiscards;
  194. discards[1] += table->table[i].dwOutDiscards;
  195. errors[0] += table->table[i].dwInErrors;
  196. errors[1] += table->table[i].dwOutErrors;
  197. unknown += table->table[i].dwInUnknownProtos;
  198. }
  199. NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_BYTES), octets[0], octets[1]);
  200. NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_UNICAST), ucastpkts[0], ucastpkts[1]);
  201. NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_NUNICAST), nucastpkts[0], nucastpkts[1]);
  202. NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_DISCARDS), discards[0], discards[1]);
  203. NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_ERRORS), errors[0], errors[1]);
  204. NETSTAT_wprintf(fmtethoutu, NETSTAT_load_message(IDS_ETH_UNKNOWN), unknown);
  205. HeapFree(GetProcessHeap(), 0, table);
  206. }
  207. static void NETSTAT_tcp_table(void)
  208. {
  209. PMIB_TCPTABLE table;
  210. DWORD err, size, i;
  211. WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
  212. WCHAR RemoteIp[MAX_HOSTNAME_LEN], RemotePort[32];
  213. WCHAR Host[MAX_HOSTNAME_LEN + 32];
  214. WCHAR Remote[MAX_HOSTNAME_LEN + 32];
  215. size = sizeof(MIB_TCPTABLE);
  216. do
  217. {
  218. table = HeapAlloc(GetProcessHeap(), 0, size);
  219. err = GetTcpTable(table, &size, TRUE);
  220. if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
  221. } while (err == ERROR_INSUFFICIENT_BUFFER);
  222. if (err) return;
  223. for (i = 0; i < table->dwNumEntries; i++)
  224. {
  225. if ((table->table[i].u.dwState == MIB_TCP_STATE_CLOSE_WAIT) ||
  226. (table->table[i].u.dwState == MIB_TCP_STATE_ESTAB) ||
  227. (table->table[i].u.dwState == MIB_TCP_STATE_TIME_WAIT))
  228. {
  229. NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
  230. NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
  231. NETSTAT_host_name(table->table[i].dwRemoteAddr, RemoteIp);
  232. NETSTAT_port_name(table->table[i].dwRemotePort, RemotePort);
  233. swprintf(Host, ARRAY_SIZE(Host), fmtcolon, HostIp, HostPort);
  234. swprintf(Remote, ARRAY_SIZE(Remote), fmtcolon, RemoteIp, RemotePort);
  235. NETSTAT_wprintf(fmttcpout, tcpW, Host, Remote, tcpstatesW[table->table[i].u.dwState]);
  236. }
  237. }
  238. HeapFree(GetProcessHeap(), 0, table);
  239. }
  240. static void NETSTAT_tcp_stats(void)
  241. {
  242. MIB_TCPSTATS stats;
  243. if (GetTcpStatistics(&stats) == NO_ERROR)
  244. {
  245. NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_TCP_STAT));
  246. NETSTAT_wprintf(fmtn);
  247. NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_ACTIVE_OPEN), stats.dwActiveOpens);
  248. NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_PASSIV_OPEN), stats.dwPassiveOpens);
  249. NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_FAILED_CONN), stats.dwAttemptFails);
  250. NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_RESET_CONN), stats.dwEstabResets);
  251. NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_CURR_CONN), stats.dwCurrEstab);
  252. NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_SEGM_RECV), stats.dwInSegs);
  253. NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_SEGM_SENT), stats.dwOutSegs);
  254. NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_SEGM_RETRAN), stats.dwRetransSegs);
  255. }
  256. }
  257. static void NETSTAT_udp_table(void)
  258. {
  259. PMIB_UDPTABLE table;
  260. DWORD err, size, i;
  261. WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
  262. WCHAR Host[MAX_HOSTNAME_LEN + 32];
  263. size = sizeof(MIB_UDPTABLE);
  264. do
  265. {
  266. table = HeapAlloc(GetProcessHeap(), 0, size);
  267. err = GetUdpTable(table, &size, TRUE);
  268. if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
  269. } while (err == ERROR_INSUFFICIENT_BUFFER);
  270. if (err) return;
  271. for (i = 0; i < table->dwNumEntries; i++)
  272. {
  273. NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
  274. NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
  275. swprintf(Host, ARRAY_SIZE(Host), fmtcolon, HostIp, HostPort);
  276. NETSTAT_wprintf(fmtudpout, udpW, Host);
  277. }
  278. HeapFree(GetProcessHeap(), 0, table);
  279. }
  280. static void NETSTAT_udp_stats(void)
  281. {
  282. MIB_UDPSTATS stats;
  283. if (GetUdpStatistics(&stats) == NO_ERROR)
  284. {
  285. NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_UDP_STAT));
  286. NETSTAT_wprintf(fmtn);
  287. NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_DGRAMS_RECV), stats.dwInDatagrams);
  288. NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_NO_PORTS), stats.dwNoPorts);
  289. NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_RECV_ERRORS), stats.dwInErrors);
  290. NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_DGRAMS_SENT), stats.dwOutDatagrams);
  291. }
  292. }
  293. static NETSTATPROTOCOLS NETSTAT_get_protocol(WCHAR name[])
  294. {
  295. if (!wcsicmp(name, ipW)) return PROT_IP;
  296. if (!wcsicmp(name, ipv6W)) return PROT_IPV6;
  297. if (!wcsicmp(name, icmpW)) return PROT_ICMP;
  298. if (!wcsicmp(name, icmpv6W)) return PROT_ICMPV6;
  299. if (!wcsicmp(name, tcpW)) return PROT_TCP;
  300. if (!wcsicmp(name, tcpv6W)) return PROT_TCPV6;
  301. if (!wcsicmp(name, udpW)) return PROT_UDP;
  302. if (!wcsicmp(name, udpv6W)) return PROT_UDPV6;
  303. return PROT_UNKNOWN;
  304. }
  305. int __cdecl wmain(int argc, WCHAR *argv[])
  306. {
  307. WSADATA wsa_data;
  308. BOOL output_stats = FALSE;
  309. if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
  310. {
  311. WINE_ERR("WSAStartup failed: %d\n", WSAGetLastError());
  312. return 1;
  313. }
  314. if (argc == 1)
  315. {
  316. /* No options */
  317. NETSTAT_conn_header();
  318. NETSTAT_tcp_table();
  319. return 0;
  320. }
  321. while (argv[1] && argv[1][0] == '-')
  322. {
  323. switch (argv[1][1])
  324. {
  325. case 'a':
  326. NETSTAT_conn_header();
  327. NETSTAT_tcp_table();
  328. NETSTAT_udp_table();
  329. return 0;
  330. case 'e':
  331. NETSTAT_eth_stats();
  332. return 0;
  333. case 's':
  334. output_stats = TRUE;
  335. break;
  336. case 'p':
  337. argv++; argc--;
  338. if (argc == 1) return 1;
  339. switch (NETSTAT_get_protocol(argv[1]))
  340. {
  341. case PROT_TCP:
  342. if (output_stats)
  343. NETSTAT_tcp_stats();
  344. NETSTAT_conn_header();
  345. NETSTAT_tcp_table();
  346. break;
  347. case PROT_UDP:
  348. if (output_stats)
  349. NETSTAT_udp_stats();
  350. NETSTAT_conn_header();
  351. NETSTAT_udp_table();
  352. break;
  353. default:
  354. WINE_FIXME("Protocol not yet implemented: %s\n", debugstr_w(argv[1]));
  355. }
  356. return 0;
  357. default:
  358. WINE_FIXME("Unknown option: %s\n", debugstr_w(argv[1]));
  359. return 1;
  360. }
  361. argv++; argc--;
  362. }
  363. if (output_stats)
  364. {
  365. NETSTAT_tcp_stats();
  366. NETSTAT_udp_stats();
  367. }
  368. return 0;
  369. }