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
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] = {
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
87 * Allocate buffer to use when writing to console
88 * Note: Not freed - memory will be allocated once and released when
92 if (!output_bufW
) output_bufW
= HeapAlloc(GetProcessHeap(), 0,
93 MAX_WRITECONSOLE_SIZE
*sizeof(WCHAR
));
95 WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
99 __ms_va_start(parms
, format
);
100 len
= wvsprintfW(output_bufW
, format
, parms
);
103 /* Try to write as unicode all the time we think it's a console */
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 */
112 BOOL usedDefaultChar
= FALSE
;
113 DWORD convertedChars
;
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
);
123 WINE_FIXME("Out of memory - could not allocate 2 x 64 KB buffers\n");
127 /* Convert to OEM, then output */
128 convertedChars
= WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW
,
129 len
, output_bufA
, MAX_WRITECONSOLE_SIZE
,
130 "?", &usedDefaultChar
);
131 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE
), output_bufA
, convertedChars
,
135 /* Trace whether screen or console */
137 WINE_TRACE("Writing to console? (%d)\n", toConsole
);
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 %d\n", GetLastError());
149 lstrcpyW(msg
, failedW
);
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
));
161 static WCHAR
*NETSTAT_host_name(UINT ip
, WCHAR name
[])
165 /* FIXME: can we get the name? */
167 swprintf(name
, MAX_HOSTNAME_LEN
, fmtip
,
168 (nip
>> 24) & 0xFF, (nip
>> 16) & 0xFF, (nip
>> 8) & 0xFF, (nip
) & 0xFF);
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)
187 DWORD octets
[2], ucastpkts
[2], nucastpkts
[2], discards
[2], errors
[2], unknown
;
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
);
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;
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)
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
);
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)
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)
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
);
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)
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
;
351 int __cdecl
wmain(int argc
, WCHAR
*argv
[])
354 BOOL output_stats
= FALSE
;
356 if (WSAStartup(MAKEWORD(2, 2), &wsa_data
))
358 WINE_ERR("WSAStartup failed: %d\n", WSAGetLastError());
365 NETSTAT_conn_header();
370 while (argv
[1] && argv
[1][0] == '-')
375 NETSTAT_conn_header();
387 if (argc
== 1) return 1;
388 switch (NETSTAT_get_protocol(argv
[1]))
393 NETSTAT_conn_header();
399 NETSTAT_conn_header();
403 WINE_FIXME("Protocol not yet implemented: %s\n", debugstr_w(argv
[1]));
407 WINE_FIXME("Unknown option: %s\n", debugstr_w(argv
[1]));