'if 0' wrapping of config.h no longer required;
[metze/wireshark/wip.git] / capture_win_ifnames.c
1 /* capture_win_ifnames.c
2 * Routines supporting the use of Windows friendly interface names within Wireshark
3 * Copyright 2011-2012, Mike Garratt <wireshark@evn.co.nz>
4 *
5 * $Id$
6 *
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 */
25
26 #include "config.h"
27
28 #ifdef _WIN32
29
30 #include <winsock2.h>
31 #include <windows.h>
32 #include <iphlpapi.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35
36 #include <wtap.h>
37 #include <libpcap.h>
38 #include <glib.h>
39
40 #include <ntddndis.h>
41
42 #ifndef NDIS_IF_MAX_STRING_SIZE
43 #define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE   /* =256 in <ifdef.h> */
44 #endif
45
46 #ifndef NETIO_STATUS
47 #define NETIO_STATUS DWORD
48 #endif
49
50 #include "log.h"
51
52 #include "capture_ifinfo.h"
53 #include "capture_win_ifnames.h"
54 #include "wsutil/file_util.h"
55
56 /**********************************************************************************/
57 gboolean IsWindowsVistaOrLater()
58 {
59     OSVERSIONINFO osvi;
60
61     SecureZeroMemory(&osvi, sizeof(OSVERSIONINFO));
62     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
63
64     if(GetVersionEx(&osvi)){
65         return osvi.dwMajorVersion >= 6;
66     }
67     return FALSE;
68 }
69 /**********************************************************************************/
70 /* The wireshark gui doesn't appear at this stage to support having logging messages
71 * returned using g_log() before the interface list.
72 * Below is a generic logging function that can be easily ripped out or configured to
73 * redirect to g_log() if the behaviour changes in the future.
74 */
75 static void ifnames_log(const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...)
76 {
77     char buf[16384];
78     va_list args;
79
80     if(log_level!=G_LOG_LEVEL_ERROR){
81         return;
82     }
83
84     va_start(args, format);
85     vsnprintf(buf, 16383, format, args);
86     va_end(args);
87
88     fprintf(stderr,"%s\r\n",buf);
89
90 }
91
92 #define g_log ifnames_log
93 /**********************************************************************************/
94 /* Get the Connection Name for the given GUID */
95 static int GetInterfaceFriendlyNameFromDeviceGuid(__in GUID *guid, __out char **Name)
96 {
97     HMODULE hIPHlpApi;
98     HRESULT status;
99     WCHAR wName[NDIS_IF_MAX_STRING_SIZE + 1];
100     HRESULT hr;
101     gboolean fallbackToUnpublishedApi=TRUE;
102     gboolean haveInterfaceFriendlyName=FALSE;
103
104     /* check we have a parameter */
105     if(Name==NULL){
106         return -1;
107     }
108
109     /* Load the ip helper api DLL */
110     hIPHlpApi = LoadLibrary(TEXT("iphlpapi.dll"));
111     if (hIPHlpApi == NULL) {
112         /* Load failed - DLL should always be available in XP+*/
113         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_ERROR,
114             "Failed to load iphlpapi.dll library for interface name lookups, errorcode=0x%08x\n", GetLastError());
115         return -1;
116     }
117
118     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Loaded iphlpapi.dll library for interface friendly name lookups");
119
120     /* Need to convert an Interface GUID to the interface friendly name (e.g. "Local Area Connection")
121     * The functions required to do this all reside within iphlpapi.dll
122     * - The preferred approach is to use published API functions (Available since Windows Vista)
123     * - We do however fallback to trying undocumented API if the published API is not available (Windows XP/2k3 scenario)
124     */
125
126     if(IsWindowsVistaOrLater()){
127         /* Published API function prototypes (for Windows Vista/Windows Server 2008+) */
128         typedef NETIO_STATUS (WINAPI *ProcAddr_CIG2L) (__in CONST GUID *InterfaceGuid, __out PNET_LUID InterfaceLuid);
129         typedef NETIO_STATUS (WINAPI *ProcAddr_CIL2A) ( __in CONST NET_LUID *InterfaceLuid,__out_ecount(Length) PWSTR InterfaceAlias, __in SIZE_T Length);
130
131         /* Attempt to do the conversion using Published API functions */
132         ProcAddr_CIG2L proc_ConvertInterfaceGuidToLuid=(ProcAddr_CIG2L) GetProcAddress(hIPHlpApi, "ConvertInterfaceGuidToLuid");
133         if(proc_ConvertInterfaceGuidToLuid!=NULL){
134             ProcAddr_CIL2A Proc_ConvertInterfaceLuidToAlias=(ProcAddr_CIL2A) GetProcAddress(hIPHlpApi, "ConvertInterfaceLuidToAlias");
135             if(Proc_ConvertInterfaceLuidToAlias!=NULL){
136                 /* we have our functions ready to go, attempt to convert interface guid->luid->friendlyname */
137                 NET_LUID InterfaceLuid;
138                 hr = proc_ConvertInterfaceGuidToLuid(guid, &InterfaceLuid);
139                 if(hr==NO_ERROR){
140                     /* guid->luid success */
141                     hr = Proc_ConvertInterfaceLuidToAlias(&InterfaceLuid, wName, NDIS_IF_MAX_STRING_SIZE+1);
142
143                     if(hr==NO_ERROR){
144                         /* luid->friendly name success */
145                         haveInterfaceFriendlyName=TRUE; /* success */
146                         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
147                             "converted interface guid to friendly name.");
148                     }else{
149                         /* luid->friendly name failed */
150                         fallbackToUnpublishedApi=FALSE;
151                         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE,
152                             "ConvertInterfaceLuidToAlias failed to convert interface luid to a friendly name, LastErrorCode=0x%08x.", GetLastError());
153                     }
154                 }else{
155                     fallbackToUnpublishedApi=FALSE;
156                     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE,
157                         "ConvertInterfaceGuidToLuid failed to convert interface guid to a luid, LastErrorCode=0x%08x.", GetLastError());
158                 }
159
160             }else{
161                 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_ERROR,
162                     "Failed to find address of ConvertInterfaceLuidToAlias in iphlpapi.dll, LastErrorCode=0x%08x.", GetLastError());
163             }
164         }else{
165             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_ERROR,
166                 "Failed to find address of ConvertInterfaceGuidToLuid in iphlpapi.dll, LastErrorCode=0x%08x.", GetLastError());
167         }
168     }
169
170
171     if(fallbackToUnpublishedApi && !haveInterfaceFriendlyName){
172         /* Didn't manage to get the friendly name using published api functions
173         * (most likely cause wireshark is running on Windows XP/Server 2003)
174         * Retry using nhGetInterfaceNameFromGuid (an older unpublished API function) */
175         typedef HRESULT (WINAPI *ProcAddr_nhGINFG) (__in GUID *InterfaceGuid, __out PCWSTR InterfaceAlias, __inout DWORD *LengthAddress, wchar_t *a4, wchar_t *a5);
176
177         ProcAddr_nhGINFG Proc_nhGetInterfaceNameFromGuid = NULL;
178         Proc_nhGetInterfaceNameFromGuid = (ProcAddr_nhGINFG) GetProcAddress(hIPHlpApi, "NhGetInterfaceNameFromGuid");
179         if (Proc_nhGetInterfaceNameFromGuid!= NULL) {
180             wchar_t *p4=NULL, *p5=NULL;
181             DWORD NameSize;
182
183             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
184                 "Unpublished NhGetInterfaceNameFromGuid function located in iphlpapi.dll, looking up friendly name from guid");
185
186             /* testing of nhGetInterfaceNameFromGuid indicates the unpublished API function expects the 3rd parameter
187             * to be the available space in bytes (as compared to wchar's) available in the second parameter buffer
188             * to receive the friendly name (in unicode format) including the space for the nul termination.*/
189             NameSize = sizeof(wName);
190
191             /* do the guid->friendlyname lookup */
192             status = Proc_nhGetInterfaceNameFromGuid(guid, wName, &NameSize, p4, p5);
193
194             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
195                 "nhGetInterfaceNameFromGuidProc status =%d, p4=%d, p5=%d, namesize=%d\n", status, (int)p4, (int)p5, NameSize);
196             if(status==0){
197                 haveInterfaceFriendlyName=TRUE; /* success */
198                 g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
199                     "Converted interface guid to friendly name.");
200             }
201
202         }else{
203             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_ERROR,
204                 "Failed to locate unpublished NhGetInterfaceNameFromGuid function located in iphlpapi.dll, "
205                 "for looking up interface friendly name, LastErrorCode=0x%08x.", GetLastError());
206         }
207
208     }
209
210     /* we have finished with iphlpapi.dll - release it */
211     FreeLibrary(hIPHlpApi);
212
213     if(!haveInterfaceFriendlyName){
214         /* failed to get the friendly name, nothing further to do */
215         return -1;
216     }
217
218     /* Get the required buffer size, and then convert the string */
219     {
220         int size = WideCharToMultiByte(CP_UTF8, 0, wName, -1, NULL, 0, NULL, NULL);
221         char *name = (char *) g_malloc(size);
222         if (name == NULL){
223             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_ERROR,
224                 "Failed to allocate memory to convert format of interface friendly name, LastErrorCode=0x%08x.", GetLastError());
225             return -1;
226         }
227         size=WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, size, NULL, NULL);
228         if(size==0){
229             /* bytes written == 0, indicating some form of error*/
230             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_ERROR,
231                 "Error converting format of interface friendly name, LastErrorCode=0x%08x.", GetLastError());
232             g_free(name);
233             return -1;
234         }
235         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Friendly name is '%s'", name);
236
237         *Name = name;
238     }
239     return 0;
240 }
241
242 static int gethexdigit(const char *p)
243 {
244     if(*p >= '0' && *p <= '9'){
245         return *p - '0';
246     }else if(*p >= 'A' && *p <= 'F'){
247         return *p - 'A' + 0xA;
248     }else if(*p >= 'a' && *p <= 'f'){
249         return *p - 'a' + 0xa;
250     }else{
251         return -1; /* Not a hex digit */
252     }
253 }
254
255 static gboolean get8hexdigits(const char *p, DWORD *d)
256 {
257     int digit;
258     DWORD val;
259     int i;
260
261     val = 0;
262     for(i = 0; i < 8; i++){
263         digit = gethexdigit(p++);
264         if(digit == -1){
265             return FALSE; /* Not a hex digit */
266         }
267         val = (val << 4) | digit;
268     }
269     *d = val;
270     return TRUE;
271 }
272
273 static gboolean get4hexdigits(const char *p, WORD *w)
274 {
275     int digit;
276     WORD val;
277     int i;
278
279     val = 0;
280     for(i = 0; i < 4; i++){
281         digit = gethexdigit(p++);
282         if(digit == -1){
283             return FALSE; /* Not a hex digit */
284         }
285         val = (val << 4) | digit;
286     }
287     *w = val;
288     return TRUE;
289 }
290
291 /**********************************************************************************/
292 /* returns the interface friendly name for a device name, if it is unable to
293 * resolve the name, "" is returned */
294 void get_windows_interface_friendlyname(/* IN */ char *interface_devicename, /* OUT */char **interface_friendlyname)
295 {
296     const char* guid_text;
297     GUID guid;
298     int i;
299     int digit1, digit2;
300
301     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "test, 1,2,3");
302
303     /* ensure we can return a result */
304     if(interface_friendlyname==NULL){
305         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_ERROR,
306             "invalid interface_friendlyname parameter to get_windows_interface_friendlyname() function.");
307         return;
308     }
309     /* start on the basis we know nothing */
310     *interface_friendlyname=NULL;
311
312     /* Extract the guid text from the interface device name */
313     if(strncmp("\\Device\\NPF_", interface_devicename, 12)==0){
314         guid_text=interface_devicename+12; /* skip over the '\Device\NPF_' prefix, assume the rest is the guid text */
315     }else{
316         guid_text=interface_devicename;
317     }
318
319     /*
320      * If what follows is a GUID in {}, then convert it to a GUID structure
321      * and use that to look up the interface to get its friendly name.
322      */
323     if(*guid_text != '{'){
324         return; /* Nope, not enclosed in {} */
325     }
326     guid_text++;
327     /* There must be 8 hex digits; if so, they go into guid.Data1 */
328     if(!get8hexdigits(guid_text, &guid.Data1)){
329         return; /* nope, not 8 hex digits */
330     }
331     guid_text += 8;
332     /* Now there must be a hyphen */
333     if(*guid_text != '-'){
334         return; /* Nope */
335     }
336     guid_text++;
337     /* There must be 4 hex digits; if so, they go into guid.Data2 */
338     if(!get4hexdigits(guid_text, &guid.Data2)){
339         return; /* nope, not 4 hex digits */
340     }
341     guid_text += 4;
342     /* Now there must be a hyphen */
343     if(*guid_text != '-'){
344         return; /* Nope */
345     }
346     guid_text++;
347     /* There must be 4 hex digits; if so, they go into guid.Data3 */
348     if(!get4hexdigits(guid_text, &guid.Data3)){
349         return; /* nope, not 4 hex digits */
350     }
351     guid_text += 4;
352     /* Now there must be a hyphen */
353     if(*guid_text != '-'){
354         return; /* Nope */
355     }
356     guid_text++;
357     /*
358      * There must be 4 hex digits; if so, they go into the first 2 bytes
359      * of guid.Data4.
360      */
361     for(i = 0; i < 2; i++){
362         digit1 = gethexdigit(guid_text);
363         if(digit1 == -1){
364             return; /* Not a hex digit */
365         }
366         guid_text++;
367         digit2 = gethexdigit(guid_text);
368         if(digit2 == -1){
369             return; /* Not a hex digit */
370         }
371         guid_text++;
372         guid.Data4[i] = (digit1 << 4)|(digit2);
373     }
374     /* Now there must be a hyphen */
375     if(*guid_text != '-'){
376         return; /* Nope */
377     }
378     guid_text++;
379     /*
380      * There must be 12 hex digits; if so,t hey go into the next 6 bytes
381      * of guid.Data4.
382      */
383     for(i = 0; i < 6; i++){
384         digit1 = gethexdigit(guid_text);
385         if(digit1 == -1){
386             return; /* Not a hex digit */
387         }
388         guid_text++;
389         digit2 = gethexdigit(guid_text);
390         if(digit2 == -1){
391             return; /* Not a hex digit */
392         }
393         guid_text++;
394         guid.Data4[i+2] = (digit1 << 4)|(digit2);
395     }
396     /* Now there must be a closing } */
397     if(*guid_text != '}'){
398         return; /* Nope */
399     }
400     guid_text++;
401     /* And that must be the end of the string */
402     if(*guid_text != '\0'){
403         return; /* Nope */
404     }
405
406     /* guid okay, get the interface friendly name associated with the guid */
407     {
408         int r=GetInterfaceFriendlyNameFromDeviceGuid(&guid, interface_friendlyname);
409         if(r!=NO_ERROR){
410             /* A message has been logged by GetInterfaceFriendlyNameFromDeviceGuid() */
411             *interface_friendlyname=NULL; /* failed to get friendly name, ensure the ultimate result is NULL */
412             return;
413         }
414     }
415
416     /* success */
417     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE,
418         "\nInterface %s => '%s'\n\n\n", interface_devicename, *interface_friendlyname);
419
420     return;
421 }
422
423 #undef g_log
424
425 /**************************************************************************************/
426 #endif
427