netstat: Implement the UDP table output.
[wine] / programs / netstat / netstat.c
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
19 #include "netstat.h"
20 #include <winsock2.h>
21 #include <iphlpapi.h>
22 #include "wine/unicode.h"
23 #include "wine/debug.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(netstat);
26
27 static const WCHAR ipW[] = {'I', 'P', 0};
28 static const WCHAR ipv6W[] = {'I', 'P', 'v', '6', 0};
29 static const WCHAR icmpW[] = {'I', 'C', 'M', 'P', 0};
30 static const WCHAR icmpv6W[] = {'I', 'C', 'M', 'P', 'v', '6', 0};
31 static const WCHAR tcpW[] = {'T', 'C', 'P', 0};
32 static const WCHAR tcpv6W[] = {'T', 'C', 'P', 'v', '6', 0};
33 static const WCHAR udpW[] = {'U', 'D', 'P', 0};
34 static const WCHAR udpv6W[] = {'U', 'D', 'P', 'v', '6', 0};
35
36 static const WCHAR fmtport[] = {'%', 'd', 0};
37 static const WCHAR fmtip[] = {'%', 'd', '.', '%', 'd', '.', '%', 'd', '.', '%', 'd', 0};
38 static const WCHAR fmtn[] = {'\n', 0};
39 static const WCHAR fmtnn[] = {'\n', '%', 's', '\n', 0};
40 static const WCHAR fmtcolon[] = {'%', 's', ':', '%', 's', 0};
41 static const WCHAR fmttcpout[] = {' ', ' ', '%', '-', '6', 's', ' ', '%', '-', '2', '2', 's', ' ', '%', '-', '2', '2', 's', ' ', '%', 's', '\n', 0};
42 static const WCHAR fmtudpout[] = {' ', ' ', '%', '-', '6', 's', ' ', '%', '-', '2', '2', 's', ' ', '*', ':', '*', '\n', 0};
43
44 /* =========================================================================
45  *  Output a unicode string. Ideally this will go to the console
46  *  and hence required WriteConsoleW to output it, however if file i/o is
47  *  redirected, it needs to be WriteFile'd using OEM (not ANSI) format
48  * ========================================================================= */
49 static int __cdecl NETSTAT_wprintf(const WCHAR *format, ...)
50 {
51     static WCHAR *output_bufW = NULL;
52     static char  *output_bufA = NULL;
53     static BOOL  toConsole    = TRUE;
54     static BOOL  traceOutput  = FALSE;
55 #define MAX_WRITECONSOLE_SIZE 65535
56
57     __ms_va_list parms;
58     DWORD   nOut;
59     int len;
60     DWORD   res = 0;
61
62     /*
63      * Allocate buffer to use when writing to console
64      * Note: Not freed - memory will be allocated once and released when
65      *         xcopy ends
66      */
67
68     if (!output_bufW) output_bufW = HeapAlloc(GetProcessHeap(), 0,
69                                               MAX_WRITECONSOLE_SIZE);
70     if (!output_bufW) {
71         WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
72         return 0;
73     }
74
75     __ms_va_start(parms, format);
76     len = wvsprintfW(output_bufW, format, parms);
77     __ms_va_end(parms);
78
79     /* Try to write as unicode all the time we think its a console */
80     if (toConsole) {
81         res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
82                             output_bufW, len, &nOut, NULL);
83     }
84
85     /* If writing to console has failed (ever) we assume its file
86        i/o so convert to OEM codepage and output                  */
87     if (!res) {
88         BOOL usedDefaultChar = FALSE;
89         DWORD convertedChars;
90
91         toConsole = FALSE;
92
93         /*
94          * Allocate buffer to use when writing to file. Not freed, as above
95          */
96         if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
97                                                   MAX_WRITECONSOLE_SIZE);
98         if (!output_bufA) {
99             WINE_FIXME("Out of memory - could not allocate 2 x 64K buffers\n");
100             return 0;
101         }
102
103         /* Convert to OEM, then output */
104         convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, output_bufW,
105                                              len, output_bufA, MAX_WRITECONSOLE_SIZE,
106                                              "?", &usedDefaultChar);
107         WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
108                   &nOut, FALSE);
109     }
110
111     /* Trace whether screen or console */
112     if (!traceOutput) {
113         WINE_TRACE("Writing to console? (%d)\n", toConsole);
114         traceOutput = TRUE;
115     }
116     return nOut;
117 }
118
119 static WCHAR *NETSTAT_load_message(UINT id) {
120     static WCHAR msg[2048];
121     static const WCHAR failedW[]  = {'F','a','i','l','e','d','!','\0'};
122
123     if (!LoadStringW(GetModuleHandleW(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
124         WINE_FIXME("LoadString failed with %d\n", GetLastError());
125         strcpyW(msg, failedW);
126     }
127     return msg;
128 }
129
130 static WCHAR *NETSTAT_port_name(UINT port, WCHAR name[])
131 {
132     /* FIXME: can we get the name? */
133     sprintfW(name, fmtport, htons((WORD)port));
134     return name;
135 }
136
137 static WCHAR *NETSTAT_host_name(UINT ip, WCHAR name[])
138 {
139     UINT nip;
140
141     /* FIXME: can we get the name? */
142     nip = htonl(ip);
143     sprintfW(name, fmtip, (nip >> 24) & 0xFF, (nip >> 16) & 0xFF, (nip >> 8) & 0xFF, (nip) & 0xFF);
144     return name;
145 }
146
147 static void NETSTAT_tcp_header(void)
148 {
149     WCHAR local[22], remote[22], state[22];
150     NETSTAT_wprintf(fmtnn, NETSTAT_load_message(IDS_TCP_ACTIVE_CONN));
151     NETSTAT_wprintf(fmtn);
152     strcpyW(local, NETSTAT_load_message(IDS_TCP_LOCAL_ADDR));
153     strcpyW(remote, NETSTAT_load_message(IDS_TCP_REMOTE_ADDR));
154     strcpyW(state, NETSTAT_load_message(IDS_TCP_STATE));
155     NETSTAT_wprintf(fmttcpout, NETSTAT_load_message(IDS_TCP_PROTO), local, remote, state);
156 }
157
158 static void NETSTAT_tcp_table(void)
159 {
160     PMIB_TCPTABLE table;
161     DWORD err, size, i;
162     WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
163     WCHAR RemoteIp[MAX_HOSTNAME_LEN], RemotePort[32];
164     WCHAR Host[MAX_HOSTNAME_LEN + 32];
165     WCHAR Remote[MAX_HOSTNAME_LEN + 32];
166
167     size = sizeof(MIB_TCPTABLE);
168     do
169     {
170         table = (PMIB_TCPTABLE)HeapAlloc(GetProcessHeap(), 0, size);
171         err = GetTcpTable(table, &size, TRUE);
172         if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
173     } while (err == ERROR_INSUFFICIENT_BUFFER);
174
175     if (err) return;
176
177     NETSTAT_tcp_header();
178
179     for (i = 0; i < table->dwNumEntries; i++)
180     {
181         if ((table->table[i].dwState ==  MIB_TCP_STATE_CLOSE_WAIT) ||
182             (table->table[i].dwState ==  MIB_TCP_STATE_ESTAB) ||
183             (table->table[i].dwState ==  MIB_TCP_STATE_TIME_WAIT))
184         {
185             NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
186             NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
187             NETSTAT_host_name(table->table[i].dwRemoteAddr, RemoteIp);
188             NETSTAT_port_name(table->table[i].dwRemotePort, RemotePort);
189
190             sprintfW(Host, fmtcolon, HostIp, HostPort);
191             sprintfW(Remote, fmtcolon, RemoteIp, RemotePort);
192             NETSTAT_wprintf(fmttcpout, tcpW, Host, Remote, NETSTAT_load_message(table->table[i].dwState));
193         }
194     }
195     HeapFree(GetProcessHeap(), 0, table);
196 }
197
198 static void NETSTAT_udp_table(void)
199 {
200     PMIB_UDPTABLE table;
201     DWORD err, size, i;
202     WCHAR HostIp[MAX_HOSTNAME_LEN], HostPort[32];
203     WCHAR Host[MAX_HOSTNAME_LEN + 32];
204
205     size = sizeof(MIB_UDPTABLE);
206     do
207     {
208         table = (PMIB_UDPTABLE)HeapAlloc(GetProcessHeap(), 0, size);
209         err = GetUdpTable(table, &size, TRUE);
210         if (err != NO_ERROR) HeapFree(GetProcessHeap(), 0, table);
211     } while (err == ERROR_INSUFFICIENT_BUFFER);
212
213     if (err) return;
214
215     NETSTAT_tcp_header();
216
217     for (i = 0; i < table->dwNumEntries; i++)
218     {
219         NETSTAT_host_name(table->table[i].dwLocalAddr, HostIp);
220         NETSTAT_port_name(table->table[i].dwLocalPort, HostPort);
221
222         sprintfW(Host, fmtcolon, HostIp, HostPort);
223         NETSTAT_wprintf(fmtudpout, udpW, Host);
224     }
225     HeapFree(GetProcessHeap(), 0, table);
226 }
227
228 static NETSTATPROTOCOLS NETSTAT_get_protocol(WCHAR name[])
229 {
230     if (!strcmpiW(name, ipW)) return PROT_IP;
231     if (!strcmpiW(name, ipv6W)) return PROT_IPV6;
232     if (!strcmpiW(name, icmpW)) return PROT_ICMP;
233     if (!strcmpiW(name, icmpv6W)) return PROT_ICMPV6;
234     if (!strcmpiW(name, tcpW)) return PROT_TCP;
235     if (!strcmpiW(name, tcpv6W)) return PROT_TCPV6;
236     if (!strcmpiW(name, udpW)) return PROT_UDP;
237     if (!strcmpiW(name, udpv6W)) return PROT_UDPV6;
238     return PROT_UNKNOWN;
239 }
240
241 int wmain(int argc, WCHAR *argv[])
242 {
243     WSADATA wsa_data;
244
245     if (WSAStartup(MAKEWORD(2, 2), &wsa_data))
246     {
247         WINE_ERR("WSAStartup failed: %d\n", WSAGetLastError());
248         return 1;
249     }
250
251     if (argc == 1)
252     {
253         /* No options */
254         NETSTAT_tcp_table();
255         return 0;
256     }
257
258     while (argv[1] && argv[1][0] == '-')
259     {
260         switch (argv[1][1])
261         {
262         case 'p':
263             argv++; argc--;
264             if (argc == 1) return 1;
265             switch (NETSTAT_get_protocol(argv[1]))
266             {
267                 case PROT_TCP:
268                     NETSTAT_tcp_table();
269                     break;
270                 case PROT_UDP:
271                     NETSTAT_udp_table();
272                     break;
273                 default:
274                     WINE_FIXME("Protocol not yet implemented: %s\n", debugstr_w(argv[1]));
275             }
276             break;
277         default:
278             WINE_FIXME("Unknown option: %s\n", debugstr_w(argv[1]));
279             return 1;
280         }
281         argv++; argc--;
282     }
283
284     return 0;
285 }