wldap32: Set error code in error cases (Coverity).
[wine.git] / programs / netstat / netstat.c
blob2c1ef89c231f530b57b3f691b2228e480fb2400a
1 /*
2 * Copyright 2011-2013 André Hentschel
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.
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.
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
19 #define NONAMELESSUNION
20 #include <stdio.h>
21 #include "netstat.h"
22 #include <winsock2.h>
23 #include <iphlpapi.h>
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(netstat);
28 static const WCHAR ipW[] = {'I', 'P', 0};
29 static const WCHAR ipv6W[] = {'I', 'P', 'v', '6', 0};
30 static const WCHAR icmpW[] = {'I', 'C', 'M', 'P', 0};
31 static const WCHAR icmpv6W[] = {'I', 'C', 'M', 'P', 'v', '6', 0};
32 static const WCHAR tcpW[] = {'T', 'C', 'P', 0};
33 static const WCHAR tcpv6W[] = {'T', 'C', 'P', 'v', '6', 0};
34 static const WCHAR udpW[] = {'U', 'D', 'P', 0};
35 static const WCHAR udpv6W[] = {'U', 'D', 'P', 'v', '6', 0};
37 static const WCHAR fmtport[] = {'%', 'd', 0};
38 static const WCHAR fmtip[] = {'%', 'd', '.', '%', 'd', '.', '%', 'd', '.', '%', 'd', 0};
39 static const WCHAR fmtn[] = {'\n', 0};
40 static const WCHAR fmtnn[] = {'\n', '%', 's', '\n', 0};
41 static const WCHAR fmtcolon[] = {'%', 's', ':', '%', 's', 0};
42 static const WCHAR fmttcpout[] = {' ', ' ', '%', '-', '6', 's', ' ', '%', '-', '2', '2', 's', ' ', '%', '-', '2', '2', 's', ' ', '%', 's', '\n', 0};
43 static const WCHAR fmtudpout[] = {' ', ' ', '%', '-', '6', 's', ' ', '%', '-', '2', '2', 's', ' ', '*', ':', '*', '\n', 0};
44 static const WCHAR fmtethout[] = {'%', '-', '2', '0', 's', ' ', '%', '1', '4', 'l', 'u', ' ', '%', '1', '5', 'l', 'u', '\n', 0};
45 static const WCHAR fmtethoutu[] = {'%', '-', '2', '0', 's', ' ', '%', '1', '4', 'l', 'u', '\n', '\n', 0};
46 static const WCHAR fmtethheader[] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
47 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
48 ' ', '%', '-', '1', '9', 's', ' ', '%', 's', '\n', '\n', 0};
49 static const WCHAR fmttcpstat[] = {' ', ' ', '%', '-', '3', '5', 's', ' ', '=', ' ', '%', 'l', 'u', '\n', 0};
50 static const WCHAR fmtudpstat[] = {' ', ' ', '%', '-', '2', '1', 's', ' ', '=', ' ', '%', 'l', 'u', '\n', 0};
52 static const WCHAR tcpstatesW[][16] = {
53 {'?', '?', '?', 0},
54 {'C', 'L', 'O', 'S', 'E', 'D', 0},
55 {'L', 'I', 'S', 'T', 'E', 'N', 'I', 'N', 'G', 0},
56 {'S', 'Y', 'N', '_', 'S', 'E', 'N', 'T', 0},
57 {'S', 'Y', 'N', '_', 'R', 'C', 'V', 'D', 0},
58 {'E', 'S', 'T', 'A', 'B', 'L', 'I', 'S', 'H', 'E', 'D', 0},
59 {'F', 'I', 'N', '_', 'W', 'A', 'I', 'T', '1', 0},
60 {'F', 'I', 'N', '_', 'W', 'A', 'I', 'T', '2', 0},
61 {'C', 'L', 'O', 'S', 'E', '_', 'W', 'A', 'I', 'T', 0},
62 {'C', 'L', 'O', 'S', 'I', 'N', 'G', 0},
63 {'L', 'A', 'S', 'T', '_', 'A', 'C', 'K', 0},
64 {'T', 'I', 'M', 'E', '_', 'W', 'A', 'I', 'T', 0},
65 {'D', 'E', 'L', 'E', 'T', 'E', '_', 'T', 'C', 'B', 0},
68 /* =========================================================================
69 * Output a unicode string. Ideally this will go to the console
70 * and hence required WriteConsoleW to output it, however if file i/o is
71 * redirected, it needs to be WriteFile'd using OEM (not ANSI) format
72 * ========================================================================= */
73 static int WINAPIV NETSTAT_wprintf(const WCHAR *format, ...)
75 static WCHAR *output_bufW = NULL;
76 static char *output_bufA = NULL;
77 static BOOL toConsole = TRUE;
78 static BOOL traceOutput = FALSE;
79 #define MAX_WRITECONSOLE_SIZE 65535
81 va_list parms;
82 DWORD nOut;
83 int len;
84 DWORD res = 0;
87 * Allocate buffer to use when writing to console
88 * Note: Not freed - memory will be allocated once and released when
89 * netstat ends
92 if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
93 MAX_WRITECONSOLE_SIZE*sizeof(WCHAR));
94 if (!output_bufW) {
95 WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
96 return 0;
99 va_start(parms, format);
100 len = wvsprintfW(output_bufW, format, parms);
101 va_end(parms);
103 /* Try to write as unicode all the time we think it's a console */
104 if (toConsole) {
105 res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
106 output_bufW, len, &nOut, NULL);
109 /* If writing to console has failed (ever) we assume it's file
110 i/o so convert to OEM codepage and output */
111 if (!res) {
112 BOOL usedDefaultChar = FALSE;
113 DWORD convertedChars;
115 toConsole = FALSE;
118 * Allocate buffer to use when writing to file. Not freed, as above
120 if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
121 MAX_WRITECONSOLE_SIZE);
122 if (!output_bufA) {
123 WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
124 return 0;
127 /* Convert to OEM, then output */
128 convertedChars = WideCharToMultiByte(GetOEMCP(), 0, output_bufW,
129 len, output_bufA, MAX_WRITECONSOLE_SIZE,
130 "?", &usedDefaultChar);
131 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
132 &nOut, FALSE);
135 /* Trace whether screen or console */
136 if (!traceOutput) {
137 WINE_TRACE("Writing to console? (%d)\n", toConsole);
138 traceOutput = TRUE;
140 return nOut;
143 static WCHAR *NETSTAT_load_message(UINT id) {
144 static WCHAR msg[2048];
145 static const WCHAR failedW[] = {'F','a','i','l','e','d','!','\0'};
147 if (!LoadStringW(GetModuleHandleW(NULL), id, msg, ARRAY_SIZE(msg))) {
148 WINE_FIXME("LoadString failed with %ld\n", GetLastError());
149 lstrcpyW(msg, failedW);
151 return msg;
154 static WCHAR *NETSTAT_port_name(UINT port, WCHAR name[])
156 /* FIXME: can we get the name? */
157 swprintf(name, 32, fmtport, htons((WORD)port));
158 return name;
161 static WCHAR *NETSTAT_host_name(UINT ip, WCHAR name[])
163 UINT nip;
165 /* FIXME: can we get the name? */
166 nip = htonl(ip);
167 swprintf(name, MAX_HOSTNAME_LEN, fmtip,
168 (nip >> 24) & 0xFF, (nip >> 16) & 0xFF, (nip >> 8) & 0xFF, (nip) & 0xFF);
169 return name;
172 static void NETSTAT_conn_header(void)
174 WCHAR local[22], remote[22], state[22];
175 NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_TCP_ACTIVE_CONN));
176 NETSTAT_wprintf(fmtn);
177 lstrcpyW(local, NETSTAT_load_message(IDS_TCP_LOCAL_ADDR));
178 lstrcpyW(remote, NETSTAT_load_message(IDS_TCP_REMOTE_ADDR));
179 lstrcpyW(state, NETSTAT_load_message(IDS_TCP_STATE));
180 NETSTAT_wprintf(fmttcpout, NETSTAT_load_message(IDS_TCP_PROTO), local, remote, state);
183 static void NETSTAT_eth_stats(void)
185 PMIB_IFTABLE table;
186 DWORD err, size, i;
187 DWORD octets[2], ucastpkts[2], nucastpkts[2], discards[2], errors[2], unknown;
188 WCHAR recv[19];
190 size = sizeof(MIB_IFTABLE);
193 table = HeapAlloc(GetProcessHeap(), 0, size);
194 err = GetIfTable(table, &size, FALSE);
195 if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
196 } while (err == ERROR_INSUFFICIENT_BUFFER);
198 if (err) return;
200 NETSTAT_wprintf(NETSTAT_load_message(IDS_ETH_STAT));
201 NETSTAT_wprintf(fmtn);
202 NETSTAT_wprintf(fmtn);
203 lstrcpyW(recv, NETSTAT_load_message(IDS_ETH_RECV));
204 NETSTAT_wprintf(fmtethheader, recv, NETSTAT_load_message(IDS_ETH_SENT));
206 octets[0] = octets[1] = 0;
207 ucastpkts[0] = ucastpkts[1] = 0;
208 nucastpkts[0] = nucastpkts[1] = 0;
209 discards[0] = discards[1] = 0;
210 errors[0] = errors[1] = 0;
211 unknown = 0;
213 for (i = 0; i < table->dwNumEntries; i++)
215 octets[0] += table->table[i].dwInOctets;
216 octets[1] += table->table[i].dwOutOctets;
217 ucastpkts[0] += table->table[i].dwInUcastPkts;
218 ucastpkts[1] += table->table[i].dwOutUcastPkts;
219 nucastpkts[0] += table->table[i].dwInNUcastPkts;
220 nucastpkts[1] += table->table[i].dwOutNUcastPkts;
221 discards[0] += table->table[i].dwInDiscards;
222 discards[1] += table->table[i].dwOutDiscards;
223 errors[0] += table->table[i].dwInErrors;
224 errors[1] += table->table[i].dwOutErrors;
225 unknown += table->table[i].dwInUnknownProtos;
228 NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_BYTES), octets[0], octets[1]);
229 NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_UNICAST), ucastpkts[0], ucastpkts[1]);
230 NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_NUNICAST), nucastpkts[0], nucastpkts[1]);
231 NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_DISCARDS), discards[0], discards[1]);
232 NETSTAT_wprintf(fmtethout, NETSTAT_load_message(IDS_ETH_ERRORS), errors[0], errors[1]);
233 NETSTAT_wprintf(fmtethoutu, NETSTAT_load_message(IDS_ETH_UNKNOWN), unknown);
235 HeapFree(GetProcessHeap(), 0, table);
238 static void NETSTAT_tcp_table(void)
240 PMIB_TCPTABLE table;
241 DWORD err, size, i;
242 WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
243 WCHAR RemoteIp[MAX_HOSTNAME_LEN], RemotePort[32];
244 WCHAR Host[MAX_HOSTNAME_LEN + 32];
245 WCHAR Remote[MAX_HOSTNAME_LEN + 32];
247 size = sizeof(MIB_TCPTABLE);
250 table = HeapAlloc(GetProcessHeap(), 0, size);
251 err = GetTcpTable(table, &size, TRUE);
252 if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
253 } while (err == ERROR_INSUFFICIENT_BUFFER);
255 if (err) return;
257 for (i = 0; i < table->dwNumEntries; i++)
259 if ((table->table[i].u.dwState == MIB_TCP_STATE_CLOSE_WAIT) ||
260 (table->table[i].u.dwState == MIB_TCP_STATE_ESTAB) ||
261 (table->table[i].u.dwState == MIB_TCP_STATE_TIME_WAIT))
263 NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
264 NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
265 NETSTAT_host_name(table->table[i].dwRemoteAddr, RemoteIp);
266 NETSTAT_port_name(table->table[i].dwRemotePort, RemotePort);
268 swprintf(Host, ARRAY_SIZE(Host), fmtcolon, HostIp, HostPort);
269 swprintf(Remote, ARRAY_SIZE(Remote), fmtcolon, RemoteIp, RemotePort);
270 NETSTAT_wprintf(fmttcpout, tcpW, Host, Remote, tcpstatesW[table->table[i].u.dwState]);
273 HeapFree(GetProcessHeap(), 0, table);
276 static void NETSTAT_tcp_stats(void)
278 MIB_TCPSTATS stats;
280 if (GetTcpStatistics(&stats) == NO_ERROR)
282 NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_TCP_STAT));
283 NETSTAT_wprintf(fmtn);
284 NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_ACTIVE_OPEN), stats.dwActiveOpens);
285 NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_PASSIV_OPEN), stats.dwPassiveOpens);
286 NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_FAILED_CONN), stats.dwAttemptFails);
287 NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_RESET_CONN), stats.dwEstabResets);
288 NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_CURR_CONN), stats.dwCurrEstab);
289 NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_SEGM_RECV), stats.dwInSegs);
290 NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_SEGM_SENT), stats.dwOutSegs);
291 NETSTAT_wprintf(fmttcpstat, NETSTAT_load_message(IDS_TCP_SEGM_RETRAN), stats.dwRetransSegs);
295 static void NETSTAT_udp_table(void)
297 PMIB_UDPTABLE table;
298 DWORD err, size, i;
299 WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
300 WCHAR Host[MAX_HOSTNAME_LEN + 32];
302 size = sizeof(MIB_UDPTABLE);
305 table = HeapAlloc(GetProcessHeap(), 0, size);
306 err = GetUdpTable(table, &size, TRUE);
307 if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
308 } while (err == ERROR_INSUFFICIENT_BUFFER);
310 if (err) return;
312 for (i = 0; i < table->dwNumEntries; i++)
314 NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
315 NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
317 swprintf(Host, ARRAY_SIZE(Host), fmtcolon, HostIp, HostPort);
318 NETSTAT_wprintf(fmtudpout, udpW, Host);
320 HeapFree(GetProcessHeap(), 0, table);
323 static void NETSTAT_udp_stats(void)
325 MIB_UDPSTATS stats;
327 if (GetUdpStatistics(&stats) == NO_ERROR)
329 NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_UDP_STAT));
330 NETSTAT_wprintf(fmtn);
331 NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_DGRAMS_RECV), stats.dwInDatagrams);
332 NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_NO_PORTS), stats.dwNoPorts);
333 NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_RECV_ERRORS), stats.dwInErrors);
334 NETSTAT_wprintf(fmtudpstat, NETSTAT_load_message(IDS_UDP_DGRAMS_SENT), stats.dwOutDatagrams);
338 static NETSTATPROTOCOLS NETSTAT_get_protocol(WCHAR name[])
340 if (!wcsicmp(name, ipW)) return PROT_IP;
341 if (!wcsicmp(name, ipv6W)) return PROT_IPV6;
342 if (!wcsicmp(name, icmpW)) return PROT_ICMP;
343 if (!wcsicmp(name, icmpv6W)) return PROT_ICMPV6;
344 if (!wcsicmp(name, tcpW)) return PROT_TCP;
345 if (!wcsicmp(name, tcpv6W)) return PROT_TCPV6;
346 if (!wcsicmp(name, udpW)) return PROT_UDP;
347 if (!wcsicmp(name, udpv6W)) return PROT_UDPV6;
348 return PROT_UNKNOWN;
351 int __cdecl wmain(int argc, WCHAR *argv[])
353 WSADATA wsa_data;
354 BOOL output_stats = FALSE;
356 if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
358 WINE_ERR("WSAStartup failed: %d\n", WSAGetLastError());
359 return 1;
362 if (argc == 1)
364 /* No options */
365 NETSTAT_conn_header();
366 NETSTAT_tcp_table();
367 return 0;
370 while (argv[1] && argv[1][0] == '-')
372 switch (argv[1][1])
374 case 'a':
375 NETSTAT_conn_header();
376 NETSTAT_tcp_table();
377 NETSTAT_udp_table();
378 return 0;
379 case 'e':
380 NETSTAT_eth_stats();
381 return 0;
382 case 's':
383 output_stats = TRUE;
384 break;
385 case 'p':
386 argv++; argc--;
387 if (argc == 1) return 1;
388 switch (NETSTAT_get_protocol(argv[1]))
390 case PROT_TCP:
391 if (output_stats)
392 NETSTAT_tcp_stats();
393 NETSTAT_conn_header();
394 NETSTAT_tcp_table();
395 break;
396 case PROT_UDP:
397 if (output_stats)
398 NETSTAT_udp_stats();
399 NETSTAT_conn_header();
400 NETSTAT_udp_table();
401 break;
402 default:
403 WINE_FIXME("Protocol not yet implemented: %s\n", debugstr_w(argv[1]));
405 return 0;
406 default:
407 WINE_FIXME("Unknown option: %s\n", debugstr_w(argv[1]));
408 return 1;
410 argv++; argc--;
413 if (output_stats)
415 NETSTAT_tcp_stats();
416 NETSTAT_udp_stats();
419 return 0;