print.c and ps.c are in libwirshark now.
[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 static int gethexdigit(const char *p)
57 {
58     if(*p >= '0' && *p <= '9'){
59         return *p - '0';
60     }else if(*p >= 'A' && *p <= 'F'){
61         return *p - 'A' + 0xA;
62     }else if(*p >= 'a' && *p <= 'f'){
63         return *p - 'a' + 0xa;
64     }else{
65         return -1; /* Not a hex digit */
66     }
67 }
68
69 static gboolean get8hexdigits(const char *p, DWORD *d)
70 {
71     int digit;
72     DWORD val;
73     int i;
74
75     val = 0;
76     for(i = 0; i < 8; i++){
77         digit = gethexdigit(p++);
78         if(digit == -1){
79             return FALSE; /* Not a hex digit */
80         }
81         val = (val << 4) | digit;
82     }
83     *d = val;
84     return TRUE;
85 }
86
87 static gboolean get4hexdigits(const char *p, WORD *w)
88 {
89     int digit;
90     WORD val;
91     int i;
92
93     val = 0;
94     for(i = 0; i < 4; i++){
95         digit = gethexdigit(p++);
96         if(digit == -1){
97             return FALSE; /* Not a hex digit */
98         }
99         val = (val << 4) | digit;
100     }
101     *w = val;
102     return TRUE;
103 }
104
105 /*
106  * If a string is a GUID in {}, fill in a GUID structure with the GUID
107  * value and return TRUE; otherwise, if the string is not a valid GUID
108  * in {}, return FALSE.
109  */
110 gboolean
111 parse_as_guid(const char *guid_text, GUID *guid)
112 {
113     int i;
114     int digit1, digit2;
115
116     if(*guid_text != '{'){
117         return FALSE; /* Nope, not enclosed in {} */
118     }
119     guid_text++;
120     /* There must be 8 hex digits; if so, they go into guid->Data1 */
121     if(!get8hexdigits(guid_text, &guid->Data1)){
122         return FALSE; /* nope, not 8 hex digits */
123     }
124     guid_text += 8;
125     /* Now there must be a hyphen */
126     if(*guid_text != '-'){
127         return FALSE; /* Nope */
128     }
129     guid_text++;
130     /* There must be 4 hex digits; if so, they go into guid->Data2 */
131     if(!get4hexdigits(guid_text, &guid->Data2)){
132         return FALSE; /* nope, not 4 hex digits */
133     }
134     guid_text += 4;
135     /* Now there must be a hyphen */
136     if(*guid_text != '-'){
137         return FALSE; /* Nope */
138     }
139     guid_text++;
140     /* There must be 4 hex digits; if so, they go into guid->Data3 */
141     if(!get4hexdigits(guid_text, &guid->Data3)){
142         return FALSE; /* nope, not 4 hex digits */
143     }
144     guid_text += 4;
145     /* Now there must be a hyphen */
146     if(*guid_text != '-'){
147         return FALSE; /* Nope */
148     }
149     guid_text++;
150     /*
151      * There must be 4 hex digits; if so, they go into the first 2 bytes
152      * of guid->Data4.
153      */
154     for(i = 0; i < 2; i++){
155         digit1 = gethexdigit(guid_text);
156         if(digit1 == -1){
157             return FALSE; /* Not a hex digit */
158         }
159         guid_text++;
160         digit2 = gethexdigit(guid_text);
161         if(digit2 == -1){
162             return FALSE; /* Not a hex digit */
163         }
164         guid_text++;
165         guid->Data4[i] = (digit1 << 4)|(digit2);
166     }
167     /* Now there must be a hyphen */
168     if(*guid_text != '-'){
169         return FALSE; /* Nope */
170     }
171     guid_text++;
172     /*
173      * There must be 12 hex digits; if so,t hey go into the next 6 bytes
174      * of guid->Data4.
175      */
176     for(i = 0; i < 6; i++){
177         digit1 = gethexdigit(guid_text);
178         if(digit1 == -1){
179             return FALSE; /* Not a hex digit */
180         }
181         guid_text++;
182         digit2 = gethexdigit(guid_text);
183         if(digit2 == -1){
184             return FALSE; /* Not a hex digit */
185         }
186         guid_text++;
187         guid->Data4[i+2] = (digit1 << 4)|(digit2);
188     }
189     /* Now there must be a closing } */
190     if(*guid_text != '}'){
191         return FALSE; /* Nope */
192     }
193     guid_text++;
194     /* And that must be the end of the string */
195     if(*guid_text != '\0'){
196         return FALSE; /* Nope */
197     }
198     return TRUE;
199 }
200
201 /**********************************************************************************/
202 gboolean IsWindowsVistaOrLater()
203 {
204     OSVERSIONINFO osvi;
205
206     SecureZeroMemory(&osvi, sizeof(OSVERSIONINFO));
207     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
208
209     if(GetVersionEx(&osvi)){
210         return osvi.dwMajorVersion >= 6;
211     }
212     return FALSE;
213 }
214
215 /**********************************************************************************/
216 /* Get the friendly name for the given GUID */
217 char *
218 get_interface_friendly_name_from_device_guid(__in GUID *guid)
219 {
220     HMODULE hIPHlpApi;
221     HRESULT status;
222     WCHAR wName[NDIS_IF_MAX_STRING_SIZE + 1];
223     HRESULT hr;
224     gboolean fallbackToUnpublishedApi=TRUE;
225     gboolean haveInterfaceFriendlyName=FALSE;
226     int size;
227     char *name;
228
229     /* Load the ip helper api DLL */
230     hIPHlpApi = LoadLibrary(TEXT("iphlpapi.dll"));
231     if (hIPHlpApi == NULL) {
232         /* Load failed - DLL should always be available in XP+*/
233         return NULL;
234     }
235
236     /* Need to convert an Interface GUID to the interface friendly name (e.g. "Local Area Connection")
237     * The functions required to do this all reside within iphlpapi.dll
238     * - The preferred approach is to use published API functions (Available since Windows Vista)
239     * - We do however fallback to trying undocumented API if the published API is not available (Windows XP/2k3 scenario)
240     */
241
242     if(IsWindowsVistaOrLater()){
243         /* Published API function prototypes (for Windows Vista/Windows Server 2008+) */
244         typedef NETIO_STATUS (WINAPI *ProcAddr_CIG2L) (__in CONST GUID *InterfaceGuid, __out PNET_LUID InterfaceLuid);
245         typedef NETIO_STATUS (WINAPI *ProcAddr_CIL2A) ( __in CONST NET_LUID *InterfaceLuid,__out_ecount(Length) PWSTR InterfaceAlias, __in SIZE_T Length);
246
247         /* Attempt to do the conversion using Published API functions */
248         ProcAddr_CIG2L proc_ConvertInterfaceGuidToLuid=(ProcAddr_CIG2L) GetProcAddress(hIPHlpApi, "ConvertInterfaceGuidToLuid");
249         if(proc_ConvertInterfaceGuidToLuid!=NULL){
250             ProcAddr_CIL2A Proc_ConvertInterfaceLuidToAlias=(ProcAddr_CIL2A) GetProcAddress(hIPHlpApi, "ConvertInterfaceLuidToAlias");
251             if(Proc_ConvertInterfaceLuidToAlias!=NULL){
252                 /* we have our functions ready to go, attempt to convert interface guid->luid->friendlyname */
253                 NET_LUID InterfaceLuid;
254                 hr = proc_ConvertInterfaceGuidToLuid(guid, &InterfaceLuid);
255                 if(hr==NO_ERROR){
256                     /* guid->luid success */
257                     hr = Proc_ConvertInterfaceLuidToAlias(&InterfaceLuid, wName, NDIS_IF_MAX_STRING_SIZE+1);
258
259                     if(hr==NO_ERROR){
260                         /* luid->friendly name success */
261                         haveInterfaceFriendlyName=TRUE; /* success */
262                     }else{
263                         /* luid->friendly name failed */
264                         fallbackToUnpublishedApi=FALSE;
265                     }
266                 }else{
267                     fallbackToUnpublishedApi=FALSE;
268                 }
269
270             }
271         }
272     }
273
274
275     if(fallbackToUnpublishedApi && !haveInterfaceFriendlyName){
276         /* Didn't manage to get the friendly name using published api functions
277         * (most likely cause wireshark is running on Windows XP/Server 2003)
278         * Retry using nhGetInterfaceNameFromGuid (an older unpublished API function) */
279         typedef HRESULT (WINAPI *ProcAddr_nhGINFG) (__in GUID *InterfaceGuid, __out PCWSTR InterfaceAlias, __inout DWORD *LengthAddress, wchar_t *a4, wchar_t *a5);
280
281         ProcAddr_nhGINFG Proc_nhGetInterfaceNameFromGuid = NULL;
282         Proc_nhGetInterfaceNameFromGuid = (ProcAddr_nhGINFG) GetProcAddress(hIPHlpApi, "NhGetInterfaceNameFromGuid");
283         if (Proc_nhGetInterfaceNameFromGuid!= NULL) {
284             wchar_t *p4=NULL, *p5=NULL;
285             DWORD NameSize;
286
287             /* testing of nhGetInterfaceNameFromGuid indicates the unpublished API function expects the 3rd parameter
288             * to be the available space in bytes (as compared to wchar's) available in the second parameter buffer
289             * to receive the friendly name (in unicode format) including the space for the nul termination.*/
290             NameSize = sizeof(wName);
291
292             /* do the guid->friendlyname lookup */
293             status = Proc_nhGetInterfaceNameFromGuid(guid, wName, &NameSize, p4, p5);
294
295             if(status==0){
296                 haveInterfaceFriendlyName=TRUE; /* success */
297             }
298         }
299     }
300
301     /* we have finished with iphlpapi.dll - release it */
302     FreeLibrary(hIPHlpApi);
303
304     if(!haveInterfaceFriendlyName){
305         /* failed to get the friendly name, nothing further to do */
306         return NULL;
307     }
308
309     /* Get the required buffer size, and then convert the string
310     * from UTF-16 to UTF-8. */
311     size=WideCharToMultiByte(CP_UTF8, 0, wName, -1, NULL, 0, NULL, NULL);
312     name=(char *) g_malloc(size);
313     if (name == NULL){
314         return NULL;
315     }
316     size=WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, size, NULL, NULL);
317     if(size==0){
318         /* bytes written == 0, indicating some form of error*/
319         g_free(name);
320         return NULL;
321     }
322     return name;
323 }
324
325 /*
326  * Given an interface name, try to extract the GUID from it and parse it.
327  * If that fails, return NULL; if that succeeds, attempt to get the
328  * friendly name for the interface in question.  If that fails, return
329  * NULL, otherwise return the friendly name, allocated with g_malloc()
330  * (so that it must be freed with g_free()).
331  */
332 char *
333 get_windows_interface_friendly_name(const char *interface_devicename)
334 {
335     const char* guid_text;
336     GUID guid;
337
338     /* Extract the guid text from the interface device name */
339     if(strncmp("\\Device\\NPF_", interface_devicename, 12)==0){
340         guid_text=interface_devicename+12; /* skip over the '\Device\NPF_' prefix, assume the rest is the guid text */
341     }else{
342         guid_text=interface_devicename;
343     }
344
345     if (!parse_as_guid(guid_text, &guid)){
346         return NULL; /* not a GUID, so no friendly name */
347     }
348
349     /* guid okay, get the interface friendly name associated with the guid */
350     return get_interface_friendly_name_from_device_guid(&guid);
351 }
352
353 /**************************************************************************************/
354 #endif
355