netstat.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  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. #include <stdio.h>
  19. #include "netstat.h"
  20. #include <winsock2.h>
  21. #include <iphlpapi.h>
  22. #include "wine/debug.h"
  23. WINE_DEFAULT_DEBUG_CHANNEL(netstat);
  24. static const WCHAR tcpstatesW[][16] = {
  25. L"???",
  26. L"CLOSED",
  27. L"LISTENING",
  28. L"SYN_SENT",
  29. L"SYN_RCVD",
  30. L"ESTABLISHED",
  31. L"FIN_WAIT1",
  32. L"FIN_WAIT2",
  33. L"CLOSE_WAIT",
  34. L"CLOSING",
  35. L"LAST_ACK",
  36. L"TIME_WAIT",
  37. L"DELETE_TCB",
  38. };
  39. /* =========================================================================
  40. * Output a unicode string. Ideally this will go to the console
  41. * and hence required WriteConsoleW to output it, however if file i/o is
  42. * redirected, it needs to be WriteFile'd using OEM (not ANSI) format
  43. * ========================================================================= */
  44. static int WINAPIV NETSTAT_wprintf(const WCHAR *format, ...)
  45. {
  46. static WCHAR *output_bufW = NULL;
  47. static char *output_bufA = NULL;
  48. static BOOL toConsole = TRUE;
  49. static BOOL traceOutput = FALSE;
  50. #define MAX_WRITECONSOLE_SIZE 65535
  51. va_list parms;
  52. DWORD nOut;
  53. int len;
  54. DWORD res = 0;
  55. /*
  56. * Allocate buffer to use when writing to console
  57. * Note: Not freed - memory will be allocated once and released when
  58. * netstat ends
  59. */
  60. if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
  61. MAX_WRITECONSOLE_SIZE*sizeof(WCHAR));
  62. if (!output_bufW) {
  63. WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
  64. return 0;
  65. }
  66. va_start(parms, format);
  67. len = wvsprintfW(output_bufW, format, parms);
  68. va_end(parms);
  69. /* Try to write as unicode all the time we think it's a console */
  70. if (toConsole) {
  71. res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
  72. output_bufW, len, &nOut, NULL);
  73. }
  74. /* If writing to console has failed (ever) we assume it's file
  75. i/o so convert to OEM codepage and output */
  76. if (!res) {
  77. BOOL usedDefaultChar = FALSE;
  78. DWORD convertedChars;
  79. toConsole = FALSE;
  80. /*
  81. * Allocate buffer to use when writing to file. Not freed, as above
  82. */
  83. if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
  84. MAX_WRITECONSOLE_SIZE);
  85. if (!output_bufA) {
  86. WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
  87. return 0;
  88. }
  89. /* Convert to OEM, then output */
  90. convertedChars = WideCharToMultiByte(GetOEMCP(), 0, output_bufW,
  91. len, output_bufA, MAX_WRITECONSOLE_SIZE,
  92. "?", &usedDefaultChar);
  93. WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
  94. &nOut, FALSE);
  95. }
  96. /* Trace whether screen or console */
  97. if (!traceOutput) {
  98. WINE_TRACE("Writing to console? (%d)\n", toConsole);
  99. traceOutput = TRUE;
  100. }
  101. return nOut;
  102. }
  103. static WCHAR *NETSTAT_load_message(UINT id) {
  104. static WCHAR msg[2048];
  105. if (!LoadStringW(GetModuleHandleW(NULL), id, msg, ARRAY_SIZE(msg))) {
  106. WINE_FIXME("LoadString failed with %ld\n", GetLastError());
  107. lstrcpyW(msg, L"Failed!");
  108. }
  109. return msg;
  110. }
  111. static WCHAR *NETSTAT_port_name(UINT port, WCHAR name[])
  112. {
  113. /* FIXME: can we get the name? */
  114. swprintf(name, 32, L"%d", htons((WORD)port));
  115. return name;
  116. }
  117. static WCHAR *NETSTAT_host_name(UINT ip, WCHAR name[])
  118. {
  119. UINT nip;
  120. /* FIXME: can we get the name? */
  121. nip = htonl(ip);
  122. swprintf(name, MAX_HOSTNAME_LEN, L"%d.%d.%d.%d",
  123. (nip >> 24) & 0xFF, (nip >> 16) & 0xFF, (nip >> 8) & 0xFF, (nip) & 0xFF);
  124. return name;
  125. }
  126. static void NETSTAT_conn_header(void)
  127. {
  128. WCHAR local[22], remote[22], state[22];
  129. NETSTAT_wprintf(L"\n%s\n\n", NETSTAT_load_message(IDS_TCP_ACTIVE_CONN));
  130. lstrcpyW(local, NETSTAT_load_message(IDS_TCP_LOCAL_ADDR));
  131. lstrcpyW(remote, NETSTAT_load_message(IDS_TCP_REMOTE_ADDR));
  132. lstrcpyW(state, NETSTAT_load_message(IDS_TCP_STATE));
  133. NETSTAT_wprintf(L" %-6s %-22s %-22s %s\n", NETSTAT_load_message(IDS_TCP_PROTO), local, remote, state);
  134. }
  135. static void NETSTAT_eth_stats(void)
  136. {
  137. PMIB_IFTABLE table;
  138. DWORD err, size, i;
  139. DWORD octets[2], ucastpkts[2], nucastpkts[2], discards[2], errors[2], unknown;
  140. WCHAR recv[19];
  141. size = sizeof(MIB_IFTABLE);
  142. do
  143. {
  144. table = HeapAlloc(GetProcessHeap(), 0, size);
  145. err = GetIfTable(table, &size, FALSE);
  146. if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
  147. } while (err == ERROR_INSUFFICIENT_BUFFER);
  148. if (err) return;
  149. NETSTAT_wprintf(NETSTAT_load_message(IDS_ETH_STAT));
  150. NETSTAT_wprintf(L"\n\n");
  151. lstrcpyW(recv, NETSTAT_load_message(IDS_ETH_RECV));
  152. NETSTAT_wprintf(L" %-19s %s\n\n", recv, NETSTAT_load_message(IDS_ETH_SENT));
  153. octets[0] = octets[1] = 0;
  154. ucastpkts[0] = ucastpkts[1] = 0;
  155. nucastpkts[0] = nucastpkts[1] = 0;
  156. discards[0] = discards[1] = 0;
  157. errors[0] = errors[1] = 0;
  158. unknown = 0;
  159. for (i = 0; i < table->dwNumEntries; i++)
  160. {
  161. octets[0] += table->table[i].dwInOctets;
  162. octets[1] += table->table[i].dwOutOctets;
  163. ucastpkts[0] += table->table[i].dwInUcastPkts;
  164. ucastpkts[1] += table->table[i].dwOutUcastPkts;
  165. nucastpkts[0] += table->table[i].dwInNUcastPkts;
  166. nucastpkts[1] += table->table[i].dwOutNUcastPkts;
  167. discards[0] += table->table[i].dwInDiscards;
  168. discards[1] += table->table[i].dwOutDiscards;
  169. errors[0] += table->table[i].dwInErrors;
  170. errors[1] += table->table[i].dwOutErrors;
  171. unknown += table->table[i].dwInUnknownProtos;
  172. }
  173. NETSTAT_wprintf(L"%-20s %14lu %15lu\n", NETSTAT_load_message(IDS_ETH_BYTES), octets[0], octets[1]);
  174. NETSTAT_wprintf(L"%-20s %14lu %15lu\n", NETSTAT_load_message(IDS_ETH_UNICAST), ucastpkts[0], ucastpkts[1]);
  175. NETSTAT_wprintf(L"%-20s %14lu %15lu\n", NETSTAT_load_message(IDS_ETH_NUNICAST), nucastpkts[0], nucastpkts[1]);
  176. NETSTAT_wprintf(L"%-20s %14lu %15lu\n", NETSTAT_load_message(IDS_ETH_DISCARDS), discards[0], discards[1]);
  177. NETSTAT_wprintf(L"%-20s %14lu %15lu\n", NETSTAT_load_message(IDS_ETH_ERRORS), errors[0], errors[1]);
  178. NETSTAT_wprintf(L"%-20s %14lu\n\n", NETSTAT_load_message(IDS_ETH_UNKNOWN), unknown);
  179. HeapFree(GetProcessHeap(), 0, table);
  180. }
  181. static void NETSTAT_tcp_table(void)
  182. {
  183. PMIB_TCPTABLE table;
  184. DWORD err, size, i;
  185. WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
  186. WCHAR RemoteIp[MAX_HOSTNAME_LEN], RemotePort[32];
  187. WCHAR Host[MAX_HOSTNAME_LEN + 32];
  188. WCHAR Remote[MAX_HOSTNAME_LEN + 32];
  189. size = sizeof(MIB_TCPTABLE);
  190. do
  191. {
  192. table = HeapAlloc(GetProcessHeap(), 0, size);
  193. err = GetTcpTable(table, &size, TRUE);
  194. if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
  195. } while (err == ERROR_INSUFFICIENT_BUFFER);
  196. if (err) return;
  197. for (i = 0; i < table->dwNumEntries; i++)
  198. {
  199. if ((table->table[i].dwState == MIB_TCP_STATE_CLOSE_WAIT) ||
  200. (table->table[i].dwState == MIB_TCP_STATE_ESTAB) ||
  201. (table->table[i].dwState == MIB_TCP_STATE_TIME_WAIT))
  202. {
  203. NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
  204. NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
  205. NETSTAT_host_name(table->table[i].dwRemoteAddr, RemoteIp);
  206. NETSTAT_port_name(table->table[i].dwRemotePort, RemotePort);
  207. swprintf(Host, ARRAY_SIZE(Host), L"%s:%s", HostIp, HostPort);
  208. swprintf(Remote, ARRAY_SIZE(Remote), L"%s:%s", RemoteIp, RemotePort);
  209. NETSTAT_wprintf(L" %-6s %-22s %-22s %s\n", L"TCP", Host, Remote, tcpstatesW[table->table[i].dwState]);
  210. }
  211. }
  212. HeapFree(GetProcessHeap(), 0, table);
  213. }
  214. static void NETSTAT_tcp_stats(void)
  215. {
  216. MIB_TCPSTATS stats;
  217. if (GetTcpStatistics(&stats) == NO_ERROR)
  218. {
  219. NETSTAT_wprintf(L"\n%s\n\n", NETSTAT_load_message(IDS_TCP_STAT));
  220. NETSTAT_wprintf(L" %-35s = %lu\n", NETSTAT_load_message(IDS_TCP_ACTIVE_OPEN), stats.dwActiveOpens);
  221. NETSTAT_wprintf(L" %-35s = %lu\n", NETSTAT_load_message(IDS_TCP_PASSIV_OPEN), stats.dwPassiveOpens);
  222. NETSTAT_wprintf(L" %-35s = %lu\n", NETSTAT_load_message(IDS_TCP_FAILED_CONN), stats.dwAttemptFails);
  223. NETSTAT_wprintf(L" %-35s = %lu\n", NETSTAT_load_message(IDS_TCP_RESET_CONN), stats.dwEstabResets);
  224. NETSTAT_wprintf(L" %-35s = %lu\n", NETSTAT_load_message(IDS_TCP_CURR_CONN), stats.dwCurrEstab);
  225. NETSTAT_wprintf(L" %-35s = %lu\n", NETSTAT_load_message(IDS_TCP_SEGM_RECV), stats.dwInSegs);
  226. NETSTAT_wprintf(L" %-35s = %lu\n", NETSTAT_load_message(IDS_TCP_SEGM_SENT), stats.dwOutSegs);
  227. NETSTAT_wprintf(L" %-35s = %lu\n", NETSTAT_load_message(IDS_TCP_SEGM_RETRAN), stats.dwRetransSegs);
  228. }
  229. }
  230. static void NETSTAT_udp_table(void)
  231. {
  232. PMIB_UDPTABLE table;
  233. DWORD err, size, i;
  234. WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
  235. WCHAR Host[MAX_HOSTNAME_LEN + 32];
  236. size = sizeof(MIB_UDPTABLE);
  237. do
  238. {
  239. table = HeapAlloc(GetProcessHeap(), 0, size);
  240. err = GetUdpTable(table, &size, TRUE);
  241. if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
  242. } while (err == ERROR_INSUFFICIENT_BUFFER);
  243. if (err) return;
  244. for (i = 0; i < table->dwNumEntries; i++)
  245. {
  246. NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
  247. NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
  248. swprintf(Host, ARRAY_SIZE(Host), L"%s:%s", HostIp, HostPort);
  249. NETSTAT_wprintf(L" %-6s %-22s *:*\n", L"UDP", Host);
  250. }
  251. HeapFree(GetProcessHeap(), 0, table);
  252. }
  253. static void NETSTAT_udp_stats(void)
  254. {
  255. MIB_UDPSTATS stats;
  256. if (GetUdpStatistics(&stats) == NO_ERROR)
  257. {
  258. NETSTAT_wprintf(L"\n%s\n\n", NETSTAT_load_message(IDS_UDP_STAT));
  259. NETSTAT_wprintf(L" %-21s = %lu\n", NETSTAT_load_message(IDS_UDP_DGRAMS_RECV), stats.dwInDatagrams);
  260. NETSTAT_wprintf(L" %-21s = %lu\n", NETSTAT_load_message(IDS_UDP_NO_PORTS), stats.dwNoPorts);
  261. NETSTAT_wprintf(L" %-21s = %lu\n", NETSTAT_load_message(IDS_UDP_RECV_ERRORS), stats.dwInErrors);
  262. NETSTAT_wprintf(L" %-21s = %lu\n", NETSTAT_load_message(IDS_UDP_DGRAMS_SENT), stats.dwOutDatagrams);
  263. }
  264. }
  265. static NETSTATPROTOCOLS NETSTAT_get_protocol(WCHAR name[])
  266. {
  267. if (!wcsicmp(name, L"IP")) return PROT_IP;
  268. if (!wcsicmp(name, L"IPv6")) return PROT_IPV6;
  269. if (!wcsicmp(name, L"ICMP")) return PROT_ICMP;
  270. if (!wcsicmp(name, L"ICMPv6")) return PROT_ICMPV6;
  271. if (!wcsicmp(name, L"TCP")) return PROT_TCP;
  272. if (!wcsicmp(name, L"TCPv6")) return PROT_TCPV6;
  273. if (!wcsicmp(name, L"UDP")) return PROT_UDP;
  274. if (!wcsicmp(name, L"UDPv6")) return PROT_UDPV6;
  275. return PROT_UNKNOWN;
  276. }
  277. int __cdecl wmain(int argc, WCHAR *argv[])
  278. {
  279. WSADATA wsa_data;
  280. BOOL output_stats = FALSE;
  281. if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
  282. {
  283. WINE_ERR("WSAStartup failed: %d\n", WSAGetLastError());
  284. return 1;
  285. }
  286. if (argc == 1)
  287. {
  288. /* No options */
  289. NETSTAT_conn_header();
  290. NETSTAT_tcp_table();
  291. return 0;
  292. }
  293. while (argv[1] && argv[1][0] == '-')
  294. {
  295. switch (argv[1][1])
  296. {
  297. case 'a':
  298. NETSTAT_conn_header();
  299. NETSTAT_tcp_table();
  300. NETSTAT_udp_table();
  301. return 0;
  302. case 'e':
  303. NETSTAT_eth_stats();
  304. return 0;
  305. case 's':
  306. output_stats = TRUE;
  307. break;
  308. case 'p':
  309. argv++; argc--;
  310. if (argc == 1) return 1;
  311. switch (NETSTAT_get_protocol(argv[1]))
  312. {
  313. case PROT_TCP:
  314. if (output_stats)
  315. NETSTAT_tcp_stats();
  316. NETSTAT_conn_header();
  317. NETSTAT_tcp_table();
  318. break;
  319. case PROT_UDP:
  320. if (output_stats)
  321. NETSTAT_udp_stats();
  322. NETSTAT_conn_header();
  323. NETSTAT_udp_table();
  324. break;
  325. default:
  326. WINE_FIXME("Protocol not yet implemented: %s\n", debugstr_w(argv[1]));
  327. }
  328. return 0;
  329. default:
  330. WINE_FIXME("Unknown option: %s\n", debugstr_w(argv[1]));
  331. return 1;
  332. }
  333. argv++; argc--;
  334. }
  335. if (output_stats)
  336. {
  337. NETSTAT_tcp_stats();
  338. NETSTAT_udp_stats();
  339. }
  340. return 0;
  341. }