ole32: Forward BSTR functions to oleaut32.
[wine] / programs / ipconfig / ipconfig.c
1 /*
2  * IP configuration utility
3  *
4  * Copyright 2008 Andrew Riedi
5  * Copyright 2010 Andrew Nguyen
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <winsock2.h>
23 #include <windows.h>
24 #include <iphlpapi.h>
25 #include <wine/unicode.h>
26
27 #include "ipconfig.h"
28
29 static int ipconfig_vprintfW(const WCHAR *msg, __ms_va_list va_args)
30 {
31     int wlen;
32     DWORD count, ret;
33     WCHAR msg_buffer[8192];
34
35     wlen = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, msg, 0, 0, msg_buffer,
36                           sizeof(msg_buffer)/sizeof(*msg_buffer), &va_args);
37
38     ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), msg_buffer, wlen, &count, NULL);
39     if (!ret)
40     {
41         DWORD len;
42         char *msgA;
43
44         /* On Windows WriteConsoleW() fails if the output is redirected. So fall
45          * back to WriteFile(), assuming the console encoding is still the right
46          * one in that case.
47          */
48         len = WideCharToMultiByte(GetConsoleOutputCP(), 0, msg_buffer, wlen,
49             NULL, 0, NULL, NULL);
50         msgA = HeapAlloc(GetProcessHeap(), 0, len);
51         if (!msgA)
52             return 0;
53
54         WideCharToMultiByte(GetConsoleOutputCP(), 0, msg_buffer, wlen, msgA, len,
55             NULL, NULL);
56         WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
57         HeapFree(GetProcessHeap(), 0, msgA);
58     }
59
60     return count;
61 }
62
63 static int CDECL ipconfig_printfW(const WCHAR *msg, ...)
64 {
65     __ms_va_list va_args;
66     int len;
67
68     __ms_va_start(va_args, msg);
69     len = ipconfig_vprintfW(msg, va_args);
70     __ms_va_end(va_args);
71
72     return len;
73 }
74
75 static int CDECL ipconfig_message_printfW(int msg, ...)
76 {
77     __ms_va_list va_args;
78     WCHAR msg_buffer[8192];
79     int len;
80
81     LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer,
82         sizeof(msg_buffer)/sizeof(WCHAR));
83
84     __ms_va_start(va_args, msg);
85     len = ipconfig_vprintfW(msg_buffer, va_args);
86     __ms_va_end(va_args);
87
88     return len;
89 }
90
91 static int ipconfig_message(int msg)
92 {
93     static const WCHAR formatW[] = {'%','1',0};
94     WCHAR msg_buffer[8192];
95
96     LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer,
97         sizeof(msg_buffer)/sizeof(WCHAR));
98
99     return ipconfig_printfW(formatW, msg_buffer);
100 }
101
102 static const WCHAR *iftype_to_string(DWORD type)
103 {
104     static WCHAR msg_buffer[50];
105
106     int msg;
107
108     switch (type)
109     {
110     case IF_TYPE_ETHERNET_CSMACD:
111     /* The loopback adapter appears as an Ethernet device. */
112     case IF_TYPE_SOFTWARE_LOOPBACK:
113         msg = STRING_ETHERNET;
114         break;
115     default:
116         msg = STRING_UNKNOWN;
117     }
118
119     LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer,
120         sizeof(msg_buffer)/sizeof(WCHAR));
121
122     return msg_buffer;
123 }
124
125 static void print_field(int msg, const WCHAR *value)
126 {
127     static const WCHAR formatW[] = {' ',' ',' ',' ','%','1',':',' ','%','2','\n',0};
128
129     WCHAR field[] = {'.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',
130                      ' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ',0};
131     WCHAR name_buffer[sizeof(field)/sizeof(WCHAR)];
132
133     LoadStringW(GetModuleHandleW(NULL), msg, name_buffer, sizeof(name_buffer)/sizeof(WCHAR));
134     memcpy(field, name_buffer, sizeof(WCHAR) * min(strlenW(name_buffer), sizeof(field)/sizeof(WCHAR) - 1));
135
136     ipconfig_printfW(formatW, field, value);
137 }
138
139 static void print_value(const WCHAR *value)
140 {
141     static const WCHAR formatW[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
142                                     ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
143                                     ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
144                                     ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
145                                     '%','1','\n',0};
146
147     ipconfig_printfW(formatW, value);
148 }
149
150 static BOOL socket_address_to_string(WCHAR *buf, DWORD len, SOCKET_ADDRESS *addr)
151 {
152     return WSAAddressToStringW(addr->lpSockaddr,
153                                addr->iSockaddrLength, NULL,
154                                buf, &len) == 0;
155 }
156
157 static void print_basic_information(void)
158 {
159     IP_ADAPTER_ADDRESSES *adapters;
160     ULONG out = 0;
161
162     if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_ALL_GATEWAYS,
163                              NULL, NULL, &out) == ERROR_BUFFER_OVERFLOW)
164     {
165         adapters = HeapAlloc(GetProcessHeap(), 0, out);
166         if (!adapters)
167             exit(1);
168
169         if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_ALL_GATEWAYS,
170                                  NULL, adapters, &out) == ERROR_SUCCESS)
171         {
172             IP_ADAPTER_ADDRESSES *p;
173
174             for (p = adapters; p; p = p->Next)
175             {
176                 static const WCHAR newlineW[] = {'\n',0};
177                 static const WCHAR emptyW[] = {0};
178
179                 IP_ADAPTER_UNICAST_ADDRESS *addr;
180                 IP_ADAPTER_GATEWAY_ADDRESS_LH *gateway;
181                 WCHAR addr_buf[54];
182
183                 ipconfig_message_printfW(STRING_ADAPTER_FRIENDLY, iftype_to_string(p->IfType), p->FriendlyName);
184                 ipconfig_printfW(newlineW);
185                 print_field(STRING_CONN_DNS_SUFFIX, p->DnsSuffix);
186
187                 for (addr = p->FirstUnicastAddress; addr; addr = addr->Next)
188                 {
189                     if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &addr->Address))
190                         print_field(STRING_IP_ADDRESS, addr_buf);
191                     /* FIXME: Output corresponding subnet mask. */
192                 }
193
194                 if (p->FirstGatewayAddress)
195                 {
196                     if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &p->FirstGatewayAddress->Address))
197                         print_field(STRING_DEFAULT_GATEWAY, addr_buf);
198
199                     for (gateway = p->FirstGatewayAddress->Next; gateway; gateway = gateway->Next)
200                     {
201                         if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &gateway->Address))
202                             print_value(addr_buf);
203                     }
204                 }
205                 else
206                     print_field(STRING_DEFAULT_GATEWAY, emptyW);
207
208                 ipconfig_printfW(newlineW);
209             }
210         }
211
212         HeapFree(GetProcessHeap(), 0, adapters);
213     }
214 }
215
216 static const WCHAR *nodetype_to_string(DWORD type)
217 {
218     static WCHAR msg_buffer[50];
219
220     int msg;
221
222     switch (type)
223     {
224     case BROADCAST_NODETYPE:
225         msg = STRING_BROADCAST;
226         break;
227     case PEER_TO_PEER_NODETYPE:
228         msg = STRING_PEER_TO_PEER;
229         break;
230     case MIXED_NODETYPE:
231         msg = STRING_MIXED;
232         break;
233     case HYBRID_NODETYPE:
234         msg = STRING_HYBRID;
235         break;
236     default:
237         msg = STRING_UNKNOWN;
238     }
239
240     LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer,
241         sizeof(msg_buffer)/sizeof(WCHAR));
242
243     return msg_buffer;
244 }
245
246 static WCHAR *physaddr_to_string(WCHAR *buf, BYTE *addr, DWORD len)
247 {
248     static const WCHAR fmtW[] = {'%','0','2','X','-',0};
249     static const WCHAR fmt2W[] = {'%','0','2','X',0};
250
251     if (!len)
252         *buf = '\0';
253     else
254     {
255         WCHAR *p = buf;
256         DWORD i;
257
258         for (i = 0; i < len - 1; i++)
259         {
260             sprintfW(p, fmtW, addr[i]);
261             p += 3;
262         }
263         sprintfW(p, fmt2W, addr[i]);
264     }
265
266     return buf;
267 }
268
269 static const WCHAR *boolean_to_string(int value)
270 {
271     static WCHAR msg_buffer[15];
272
273     LoadStringW(GetModuleHandleW(NULL), value ? STRING_YES : STRING_NO,
274         msg_buffer, sizeof(msg_buffer)/sizeof(WCHAR));
275
276     return msg_buffer;
277 }
278
279 static void print_full_information(void)
280 {
281     static const WCHAR newlineW[] = {'\n',0};
282     static const WCHAR emptyW[] = {0};
283
284     FIXED_INFO *info;
285     IP_ADAPTER_ADDRESSES *adapters;
286     ULONG out = 0;
287
288     if (GetNetworkParams(NULL, &out) == ERROR_BUFFER_OVERFLOW)
289     {
290         info = HeapAlloc(GetProcessHeap(), 0, out);
291         if (!info)
292             exit(1);
293
294         if (GetNetworkParams(info, &out) == ERROR_SUCCESS)
295         {
296             WCHAR hostnameW[MAX_HOSTNAME_LEN + 4];
297
298             MultiByteToWideChar(CP_ACP, 0, info->HostName, -1, hostnameW, sizeof(hostnameW)/sizeof(hostnameW[0]));
299             print_field(STRING_HOSTNAME, hostnameW);
300
301             /* FIXME: Output primary DNS suffix. */
302
303             print_field(STRING_NODE_TYPE, nodetype_to_string(info->NodeType));
304             print_field(STRING_IP_ROUTING, boolean_to_string(info->EnableRouting));
305
306             /* FIXME: Output WINS proxy status and DNS suffix search list. */
307
308             ipconfig_printfW(newlineW);
309         }
310
311         HeapFree(GetProcessHeap(), 0, info);
312     }
313
314     if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_ALL_GATEWAYS,
315                              NULL, NULL, &out) == ERROR_BUFFER_OVERFLOW)
316     {
317         adapters = HeapAlloc(GetProcessHeap(), 0, out);
318         if (!adapters)
319             exit(1);
320
321         if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_ALL_GATEWAYS,
322                                  NULL, adapters, &out) == ERROR_SUCCESS)
323         {
324             IP_ADAPTER_ADDRESSES *p;
325
326             for (p = adapters; p; p = p->Next)
327             {
328                 IP_ADAPTER_UNICAST_ADDRESS *addr;
329                 WCHAR physaddr_buf[3 * MAX_ADAPTER_ADDRESS_LENGTH];
330                 IP_ADAPTER_GATEWAY_ADDRESS_LH *gateway;
331                 WCHAR addr_buf[54];
332
333                 ipconfig_message_printfW(STRING_ADAPTER_FRIENDLY, iftype_to_string(p->IfType), p->FriendlyName);
334                 ipconfig_printfW(newlineW);
335                 print_field(STRING_CONN_DNS_SUFFIX, p->DnsSuffix);
336                 print_field(STRING_DESCRIPTION, p->Description);
337                 print_field(STRING_PHYS_ADDR, physaddr_to_string(physaddr_buf, p->PhysicalAddress, p->PhysicalAddressLength));
338                 print_field(STRING_DHCP_ENABLED, boolean_to_string(p->Flags & IP_ADAPTER_DHCP_ENABLED));
339
340                 /* FIXME: Output autoconfiguration status. */
341
342                 for (addr = p->FirstUnicastAddress; addr; addr = addr->Next)
343                 {
344                     if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &addr->Address))
345                         print_field(STRING_IP_ADDRESS, addr_buf);
346                     /* FIXME: Output corresponding subnet mask. */
347                 }
348
349                 if (p->FirstGatewayAddress)
350                 {
351                     if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &p->FirstGatewayAddress->Address))
352                         print_field(STRING_DEFAULT_GATEWAY, addr_buf);
353
354                     for (gateway = p->FirstGatewayAddress->Next; gateway; gateway = gateway->Next)
355                     {
356                         if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &gateway->Address))
357                             print_value(addr_buf);
358                     }
359                 }
360                 else
361                     print_field(STRING_DEFAULT_GATEWAY, emptyW);
362
363                 ipconfig_printfW(newlineW);
364             }
365         }
366
367         HeapFree(GetProcessHeap(), 0, adapters);
368     }
369 }
370
371 int wmain(int argc, WCHAR *argv[])
372 {
373     static const WCHAR slashHelp[] = {'/','?',0};
374     static const WCHAR slashAll[] = {'/','a','l','l',0};
375
376     WSADATA data;
377
378     if (WSAStartup(MAKEWORD(2, 2), &data))
379         return 1;
380
381     if (argc > 1)
382     {
383         if (!strcmpW(slashHelp, argv[1]))
384         {
385             ipconfig_message(STRING_USAGE);
386             WSACleanup();
387             return 1;
388         }
389         else if (!strcmpiW(slashAll, argv[1]))
390         {
391             if (argv[2])
392             {
393                 ipconfig_message(STRING_INVALID_CMDLINE);
394                 ipconfig_message(STRING_USAGE);
395                 WSACleanup();
396                 return 1;
397             }
398
399             print_full_information();
400         }
401         else
402         {
403             ipconfig_message(STRING_INVALID_CMDLINE);
404             ipconfig_message(STRING_USAGE);
405             WSACleanup();
406             return 1;
407         }
408     }
409     else
410         print_basic_information();
411
412     WSACleanup();
413     return 0;
414 }