OpenVPN
tap.c
Go to the documentation of this file.
1 /*
2  * tapctl -- Utility to manipulate TUN/TAP interfaces on Windows
3  *
4  * Copyright (C) 2018 Simon Rozman <simon@rozman.si>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #elif defined(_MSC_VER)
23 #include <config-msvc.h>
24 #endif
25 
26 #include "tap.h"
27 #include "error.h"
28 
29 #include <windows.h>
30 #include <cfgmgr32.h>
31 #include <objbase.h>
32 #include <setupapi.h>
33 #include <tchar.h>
34 
35 #ifdef _MSC_VER
36 #pragma comment(lib, "advapi32.lib")
37 #pragma comment(lib, "ole32.lib")
38 #pragma comment(lib, "setupapi.lib")
39 #endif
40 
41 const static GUID GUID_DEVCLASS_NET = { 0x4d36e972L, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } };
42 
43 const static TCHAR szzHardwareIDs[] = TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0");
44 
45 const static TCHAR szInterfaceRegKeyPathTemplate[] = TEXT("SYSTEM\\CurrentControlSet\\Control\\Network\\%") TEXT(PRIsLPOLESTR) TEXT("\\%") TEXT(PRIsLPOLESTR) TEXT("\\Connection");
46 #define INTERFACE_REGKEY_PATH_MAX (_countof(TEXT("SYSTEM\\CurrentControlSet\\Control\\Network\\")) - 1 + 38 + _countof(TEXT("\\")) - 1 + 38 + _countof(TEXT("\\Connection")))
47 
48 
65 static DWORD
67  _In_ HDEVINFO hDeviceInfoSet,
68  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
69  _Inout_ LPBOOL pbRebootRequired)
70 {
71  if (pbRebootRequired == NULL)
72  {
73  return ERROR_BAD_ARGUMENTS;
74  }
75 
76  SP_DEVINSTALL_PARAMS devinstall_params = { .cbSize = sizeof(SP_DEVINSTALL_PARAMS) };
77  if (!SetupDiGetDeviceInstallParams(
78  hDeviceInfoSet,
79  pDeviceInfoData,
80  &devinstall_params))
81  {
82  DWORD dwResult = GetLastError();
83  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceInstallParams failed", __FUNCTION__);
84  return dwResult;
85  }
86 
87  if ((devinstall_params.Flags & (DI_NEEDREBOOT | DI_NEEDRESTART)) != 0)
88  {
89  *pbRebootRequired = TRUE;
90  }
91 
92  return ERROR_SUCCESS;
93 }
94 
95 
110 static DWORD
112  _In_ HKEY hKey,
113  _In_ LPCTSTR szName,
114  _Out_ LPTSTR *pszValue)
115 {
116  if (pszValue == NULL)
117  {
118  return ERROR_BAD_ARGUMENTS;
119  }
120 
121  DWORD dwValueType = REG_NONE, dwSize = 0;
122  DWORD dwResult = RegQueryValueEx(
123  hKey,
124  szName,
125  NULL,
126  &dwValueType,
127  NULL,
128  &dwSize);
129  if (dwResult != ERROR_SUCCESS)
130  {
131  SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we do have an error code. Set last error manually. */
132  msg(M_NONFATAL | M_ERRNO, "%s: enumerating \"%" PRIsLPTSTR "\" registry value failed", __FUNCTION__, szName);
133  return dwResult;
134  }
135 
136  switch (dwValueType)
137  {
138  case REG_SZ:
139  case REG_EXPAND_SZ:
140  {
141  /* Read value. */
142  LPTSTR szValue = (LPTSTR)malloc(dwSize);
143  dwResult = RegQueryValueEx(
144  hKey,
145  szName,
146  NULL,
147  NULL,
148  (LPBYTE)szValue,
149  &dwSize);
150  if (dwResult != ERROR_SUCCESS)
151  {
152  SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we do have an error code. Set last error manually. */
153  msg(M_NONFATAL | M_ERRNO, "%s: reading \"%" PRIsLPTSTR "\" registry value failed", __FUNCTION__, szName);
154  free(szValue);
155  return dwResult;
156  }
157 
158  if (dwValueType == REG_EXPAND_SZ)
159  {
160  /* Expand the environment strings. */
161  DWORD
162  dwSizeExp = dwSize * 2,
163  dwCountExp =
164 #ifdef UNICODE
165  dwSizeExp / sizeof(TCHAR);
166 #else
167  dwSizeExp / sizeof(TCHAR) - 1; /* Note: ANSI version requires one extra char. */
168 #endif
169  LPTSTR szValueExp = (LPTSTR)malloc(dwSizeExp);
170  DWORD dwCountExpResult = ExpandEnvironmentStrings(
171  szValue,
172  szValueExp, dwCountExp
173  );
174  if (dwCountExpResult == 0)
175  {
176  msg(M_NONFATAL | M_ERRNO, "%s: expanding \"%" PRIsLPTSTR "\" registry value failed", __FUNCTION__, szName);
177  free(szValueExp);
178  free(szValue);
179  return dwResult;
180  }
181  else if (dwCountExpResult <= dwCountExp)
182  {
183  /* The buffer was big enough. */
184  free(szValue);
185  *pszValue = szValueExp;
186  return ERROR_SUCCESS;
187  }
188  else
189  {
190  /* Retry with a bigger buffer. */
191  free(szValueExp);
192 #ifdef UNICODE
193  dwSizeExp = dwCountExpResult * sizeof(TCHAR);
194 #else
195  /* Note: ANSI version requires one extra char. */
196  dwSizeExp = (dwCountExpResult + 1) * sizeof(TCHAR);
197 #endif
198  dwCountExp = dwCountExpResult;
199  szValueExp = (LPTSTR)malloc(dwSizeExp);
200  dwCountExpResult = ExpandEnvironmentStrings(
201  szValue,
202  szValueExp, dwCountExp);
203  free(szValue);
204  *pszValue = szValueExp;
205  return ERROR_SUCCESS;
206  }
207  }
208  else
209  {
210  *pszValue = szValue;
211  return ERROR_SUCCESS;
212  }
213  }
214 
215  default:
216  msg(M_NONFATAL, "%s: \"%" PRIsLPTSTR "\" registry value is not string (type %u)", __FUNCTION__, dwValueType);
217  return ERROR_UNSUPPORTED_TYPE;
218  }
219 }
220 
221 
240 static DWORD
242  _In_ HDEVINFO hDeviceInfoSet,
243  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
244  _In_ int iNumAttempts,
245  _Out_ LPGUID pguidInterface)
246 {
247  DWORD dwResult = ERROR_BAD_ARGUMENTS;
248 
249  if (pguidInterface == NULL || iNumAttempts < 1)
250  {
251  return ERROR_BAD_ARGUMENTS;
252  }
253 
254  /* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class<class><id> registry key. */
255  HKEY hKey = SetupDiOpenDevRegKey(
256  hDeviceInfoSet,
257  pDeviceInfoData,
258  DICS_FLAG_GLOBAL,
259  0,
260  DIREG_DRV,
261  KEY_READ);
262  if (hKey == INVALID_HANDLE_VALUE)
263  {
264  dwResult = GetLastError();
265  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiOpenDevRegKey failed", __FUNCTION__);
266  return dwResult;
267  }
268 
269  while (iNumAttempts > 0)
270  {
271  /* Query the NetCfgInstanceId value. Using get_reg_string() right on might clutter the output with error messages while the registry is still being populated. */
272  LPTSTR szCfgGuidString = NULL;
273  dwResult = RegQueryValueEx(hKey, TEXT("NetCfgInstanceId"), NULL, NULL, NULL, NULL);
274  if (dwResult != ERROR_SUCCESS)
275  {
276  if (dwResult == ERROR_FILE_NOT_FOUND && --iNumAttempts > 0)
277  {
278  /* Wait and retry. */
279  Sleep(1000);
280  continue;
281  }
282 
283  SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we do have an error code. Set last error manually. */
284  msg(M_NONFATAL | M_ERRNO, "%s: querying \"NetCfgInstanceId\" registry value failed", __FUNCTION__);
285  break;
286  }
287 
288  /* Read the NetCfgInstanceId value now. */
289  dwResult = get_reg_string(
290  hKey,
291  TEXT("NetCfgInstanceId"),
292  &szCfgGuidString);
293  if (dwResult != ERROR_SUCCESS)
294  {
295  break;
296  }
297 
298  dwResult = SUCCEEDED(CLSIDFromString(szCfgGuidString, (LPCLSID)pguidInterface)) ? ERROR_SUCCESS : ERROR_INVALID_DATA;
299  free(szCfgGuidString);
300  break;
301  }
302 
303  RegCloseKey(hKey);
304  return dwResult;
305 }
306 
307 
330 static DWORD
332  _In_ HDEVINFO hDeviceInfoSet,
333  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
334  _In_ DWORD dwProperty,
335  _Out_opt_ LPDWORD pdwPropertyRegDataType,
336  _Out_ LPVOID *ppData)
337 {
338  DWORD dwResult = ERROR_BAD_ARGUMENTS;
339 
340  if (ppData == NULL)
341  {
342  return ERROR_BAD_ARGUMENTS;
343  }
344 
345  /* Try with stack buffer first. */
346  BYTE bBufStack[128];
347  DWORD dwRequiredSize = 0;
348  if (SetupDiGetDeviceRegistryProperty(
349  hDeviceInfoSet,
350  pDeviceInfoData,
351  dwProperty,
352  pdwPropertyRegDataType,
353  bBufStack,
354  sizeof(bBufStack),
355  &dwRequiredSize))
356  {
357  /* Copy from stack. */
358  *ppData = malloc(dwRequiredSize);
359  memcpy(*ppData, bBufStack, dwRequiredSize);
360  return ERROR_SUCCESS;
361  }
362  else
363  {
364  dwResult = GetLastError();
365  if (dwResult == ERROR_INSUFFICIENT_BUFFER)
366  {
367  /* Allocate on heap and retry. */
368  *ppData = malloc(dwRequiredSize);
369  if (SetupDiGetDeviceRegistryProperty(
370  hDeviceInfoSet,
371  pDeviceInfoData,
372  dwProperty,
373  pdwPropertyRegDataType,
374  *ppData,
375  dwRequiredSize,
376  &dwRequiredSize))
377  {
378  return ERROR_SUCCESS;
379  }
380  else
381  {
382  dwResult = GetLastError();
383  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceRegistryProperty(%u) failed", __FUNCTION__, dwProperty);
384  return dwResult;
385  }
386  }
387  else
388  {
389  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceRegistryProperty(%u) failed", __FUNCTION__, dwProperty);
390  return dwResult;
391  }
392  }
393 }
394 
395 
403 static inline size_t
404 _tcszlen(_In_ LPCTSTR str)
405 {
406  LPCTSTR s;
407  for (s = str; s[0]; s += _tcslen(s) + 1)
408  {
409  }
410  return s - str;
411 }
412 
413 
414 DWORD
416  _In_opt_ HWND hwndParent,
417  _In_opt_ LPCTSTR szDeviceDescription,
418  _Inout_ LPBOOL pbRebootRequired,
419  _Out_ LPGUID pguidInterface)
420 {
421  DWORD dwResult;
422 
423  if (pbRebootRequired == NULL
424  || pguidInterface == NULL)
425  {
426  return ERROR_BAD_ARGUMENTS;
427  }
428 
429  /* Create an empty device info set for network adapter device class. */
430  HDEVINFO hDevInfoList = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_NET, hwndParent);
431  if (hDevInfoList == INVALID_HANDLE_VALUE)
432  {
433  dwResult = GetLastError();
434  msg(M_NONFATAL, "%s: SetupDiCreateDeviceInfoList failed", __FUNCTION__);
435  return dwResult;
436  }
437 
438  /* Get the device class name from GUID. */
439  TCHAR szClassName[MAX_CLASS_NAME_LEN];
440  if (!SetupDiClassNameFromGuid(
442  szClassName,
443  _countof(szClassName),
444  NULL))
445  {
446  dwResult = GetLastError();
447  msg(M_NONFATAL, "%s: SetupDiClassNameFromGuid failed", __FUNCTION__);
448  goto cleanup_hDevInfoList;
449  }
450 
451  /* Create a new device info element and add it to the device info set. */
452  SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
453  if (!SetupDiCreateDeviceInfo(
454  hDevInfoList,
455  szClassName,
457  szDeviceDescription,
458  hwndParent,
459  DICD_GENERATE_ID,
460  &devinfo_data))
461  {
462  dwResult = GetLastError();
463  msg(M_NONFATAL, "%s: SetupDiCreateDeviceInfo failed", __FUNCTION__);
464  goto cleanup_hDevInfoList;
465  }
466 
467  /* Set a device information element as the selected member of a device information set. */
468  if (!SetupDiSetSelectedDevice(
469  hDevInfoList,
470  &devinfo_data))
471  {
472  dwResult = GetLastError();
473  msg(M_NONFATAL, "%s: SetupDiSetSelectedDevice failed", __FUNCTION__);
474  goto cleanup_hDevInfoList;
475  }
476 
477  /* Set Plug&Play device hardware ID property. */
478  if (!SetupDiSetDeviceRegistryProperty(
479  hDevInfoList,
480  &devinfo_data,
481  SPDRP_HARDWAREID,
482  (const BYTE *)szzHardwareIDs, sizeof(szzHardwareIDs)))
483  {
484  dwResult = GetLastError();
485  msg(M_NONFATAL, "%s: SetupDiSetDeviceRegistryProperty failed", __FUNCTION__);
486  goto cleanup_hDevInfoList;
487  }
488 
489  /* Search for the driver. */
490  if (!SetupDiBuildDriverInfoList(
491  hDevInfoList,
492  &devinfo_data,
493  SPDIT_CLASSDRIVER))
494  {
495  dwResult = GetLastError();
496  msg(M_NONFATAL, "%s: SetupDiBuildDriverInfoList failed", __FUNCTION__);
497  goto cleanup_hDevInfoList;
498  }
499  DWORDLONG dwlDriverVersion = 0;
500  DWORD drvinfo_detail_data_size = sizeof(SP_DRVINFO_DETAIL_DATA) + 0x100;
501  SP_DRVINFO_DETAIL_DATA *drvinfo_detail_data = (SP_DRVINFO_DETAIL_DATA *)malloc(drvinfo_detail_data_size);
502  for (DWORD dwIndex = 0;; dwIndex++)
503  {
504  /* Get a driver from the list. */
505  SP_DRVINFO_DATA drvinfo_data = { .cbSize = sizeof(SP_DRVINFO_DATA) };
506  if (!SetupDiEnumDriverInfo(
507  hDevInfoList,
508  &devinfo_data,
509  SPDIT_CLASSDRIVER,
510  dwIndex,
511  &drvinfo_data))
512  {
513  if (GetLastError() == ERROR_NO_MORE_ITEMS)
514  {
515  break;
516  }
517  else
518  {
519  /* Something is wrong with this driver. Skip it. */
520  msg(M_WARN | M_ERRNO, "%s: SetupDiEnumDriverInfo(%u) failed", __FUNCTION__, dwIndex);
521  continue;
522  }
523  }
524 
525  /* Get driver info details. */
526  DWORD dwSize;
527  drvinfo_detail_data->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
528  if (!SetupDiGetDriverInfoDetail(
529  hDevInfoList,
530  &devinfo_data,
531  &drvinfo_data,
532  drvinfo_detail_data,
533  drvinfo_detail_data_size,
534  &dwSize))
535  {
536  if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
537  {
538  /* (Re)allocate buffer. */
539  if (drvinfo_detail_data)
540  {
541  free(drvinfo_detail_data);
542  }
543 
544  drvinfo_detail_data_size = dwSize;
545  drvinfo_detail_data = (SP_DRVINFO_DETAIL_DATA *)malloc(drvinfo_detail_data_size);
546 
547  /* Re-get driver info details. */
548  drvinfo_detail_data->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
549  if (!SetupDiGetDriverInfoDetail(
550  hDevInfoList,
551  &devinfo_data,
552  &drvinfo_data,
553  drvinfo_detail_data,
554  drvinfo_detail_data_size,
555  &dwSize))
556  {
557  /* Something is wrong with this driver. Skip it. */
558  continue;
559  }
560  }
561  else
562  {
563  /* Something is wrong with this driver. Skip it. */
564  msg(M_WARN | M_ERRNO, "%s: SetupDiGetDriverInfoDetail(\"%hs\") failed", __FUNCTION__, drvinfo_data.Description);
565  continue;
566  }
567  }
568 
569  /* Check the driver version first, since the check is trivial and will save us iterating over hardware IDs for any driver versioned prior our best match. */
570  if (dwlDriverVersion < drvinfo_data.DriverVersion)
571  {
572  /* Search the list of hardware IDs. */
573  for (LPTSTR szHwdID = drvinfo_detail_data->HardwareID; szHwdID && szHwdID[0]; szHwdID += _tcslen(szHwdID) + 1)
574  {
575  if (_tcsicmp(szHwdID, szzHardwareIDs) == 0)
576  {
577  /* Matching hardware ID found. Select the driver. */
578  if (!SetupDiSetSelectedDriver(
579  hDevInfoList,
580  &devinfo_data,
581  &drvinfo_data))
582  {
583  /* Something is wrong with this driver. Skip it. */
584  msg(M_WARN | M_ERRNO, "%s: SetupDiSetSelectedDriver(\"%hs\") failed", __FUNCTION__, drvinfo_data.Description);
585  break;
586  }
587 
588  dwlDriverVersion = drvinfo_data.DriverVersion;
589  break;
590  }
591  }
592  }
593  }
594  if (drvinfo_detail_data)
595  {
596  free(drvinfo_detail_data);
597  }
598 
599  if (dwlDriverVersion == 0)
600  {
601  dwResult = ERROR_NOT_FOUND;
602  msg(M_NONFATAL, "%s: No driver for device \"%" PRIsLPTSTR "\" installed.", __FUNCTION__, szzHardwareIDs);
603  goto cleanup_DriverInfoList;
604  }
605 
606  /* Call appropriate class installer. */
607  if (!SetupDiCallClassInstaller(
608  DIF_REGISTERDEVICE,
609  hDevInfoList,
610  &devinfo_data))
611  {
612  dwResult = GetLastError();
613  msg(M_NONFATAL, "%s: SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed", __FUNCTION__);
614  goto cleanup_DriverInfoList;
615  }
616 
617  /* Register device co-installers if any. */
618  if (!SetupDiCallClassInstaller(
619  DIF_REGISTER_COINSTALLERS,
620  hDevInfoList,
621  &devinfo_data))
622  {
623  dwResult = GetLastError();
624  msg(M_WARN | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS) failed", __FUNCTION__);
625  }
626 
627  /* Install interfaces if any. */
628  if (!SetupDiCallClassInstaller(
629  DIF_INSTALLINTERFACES,
630  hDevInfoList,
631  &devinfo_data))
632  {
633  dwResult = GetLastError();
634  msg(M_WARN | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_INSTALLINTERFACES) failed", __FUNCTION__);
635  }
636 
637  /* Install the device. */
638  if (!SetupDiCallClassInstaller(
639  DIF_INSTALLDEVICE,
640  hDevInfoList,
641  &devinfo_data))
642  {
643  dwResult = GetLastError();
644  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed", __FUNCTION__);
645  goto cleanup_remove_device;
646  }
647 
648  /* Check if a system reboot is required. (Ignore errors) */
649  check_reboot(hDevInfoList, &devinfo_data, pbRebootRequired);
650 
651  /* Get network interface ID from registry. Retry for max 30sec. */
652  dwResult = get_net_interface_guid(hDevInfoList, &devinfo_data, 30, pguidInterface);
653 
654 cleanup_remove_device:
655  if (dwResult != ERROR_SUCCESS)
656  {
657  /* The interface was installed. But, the interface ID was unobtainable. Clean-up. */
658  SP_REMOVEDEVICE_PARAMS removedevice_params =
659  {
660  .ClassInstallHeader =
661  {
662  .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
663  .InstallFunction = DIF_REMOVE,
664  },
665  .Scope = DI_REMOVEDEVICE_GLOBAL,
666  .HwProfile = 0,
667  };
668 
669  /* Set class installer parameters for DIF_REMOVE. */
670  if (SetupDiSetClassInstallParams(
671  hDevInfoList,
672  &devinfo_data,
673  &removedevice_params.ClassInstallHeader,
674  sizeof(SP_REMOVEDEVICE_PARAMS)))
675  {
676  /* Call appropriate class installer. */
677  if (SetupDiCallClassInstaller(
678  DIF_REMOVE,
679  hDevInfoList,
680  &devinfo_data))
681  {
682  /* Check if a system reboot is required. */
683  check_reboot(hDevInfoList, &devinfo_data, pbRebootRequired);
684  }
685  else
686  {
687  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_REMOVE) failed", __FUNCTION__);
688  }
689  }
690  else
691  {
692  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
693  }
694  }
695 
696 cleanup_DriverInfoList:
697  SetupDiDestroyDriverInfoList(
698  hDevInfoList,
699  &devinfo_data,
700  SPDIT_CLASSDRIVER);
701 
702 cleanup_hDevInfoList:
703  SetupDiDestroyDeviceInfoList(hDevInfoList);
704  return dwResult;
705 }
706 
707 
708 DWORD
710  _In_opt_ HWND hwndParent,
711  _In_ LPCGUID pguidInterface,
712  _Inout_ LPBOOL pbRebootRequired)
713 {
714  DWORD dwResult;
715 
716  if (pguidInterface == NULL)
717  {
718  return ERROR_BAD_ARGUMENTS;
719  }
720 
721  /* Create a list of network devices. */
722  HDEVINFO hDevInfoList = SetupDiGetClassDevsEx(
724  NULL,
725  hwndParent,
726  DIGCF_PRESENT,
727  NULL,
728  NULL,
729  NULL);
730  if (hDevInfoList == INVALID_HANDLE_VALUE)
731  {
732  dwResult = GetLastError();
733  msg(M_NONFATAL, "%s: SetupDiGetClassDevsEx failed", __FUNCTION__);
734  return dwResult;
735  }
736 
737  /* Retrieve information associated with a device information set. */
738  SP_DEVINFO_LIST_DETAIL_DATA devinfo_list_detail_data = { .cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA) };
739  if (!SetupDiGetDeviceInfoListDetail(hDevInfoList, &devinfo_list_detail_data))
740  {
741  dwResult = GetLastError();
742  msg(M_NONFATAL, "%s: SetupDiGetDeviceInfoListDetail failed", __FUNCTION__);
743  goto cleanup_hDevInfoList;
744  }
745 
746  /* Iterate. */
747  for (DWORD dwIndex = 0;; dwIndex++)
748  {
749  /* Get the device from the list. */
750  SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
751  if (!SetupDiEnumDeviceInfo(
752  hDevInfoList,
753  dwIndex,
754  &devinfo_data))
755  {
756  if (GetLastError() == ERROR_NO_MORE_ITEMS)
757  {
758  LPOLESTR szInterfaceId = NULL;
759  StringFromIID((REFIID)pguidInterface, &szInterfaceId);
760  msg(M_NONFATAL, "%s: Interface %" PRIsLPOLESTR " not found", __FUNCTION__, szInterfaceId);
761  CoTaskMemFree(szInterfaceId);
762  dwResult = ERROR_FILE_NOT_FOUND;
763  goto cleanup_hDevInfoList;
764  }
765  else
766  {
767  /* Something is wrong with this device. Skip it. */
768  msg(M_WARN | M_ERRNO, "%s: SetupDiEnumDeviceInfo(%u) failed", __FUNCTION__, dwIndex);
769  continue;
770  }
771  }
772 
773  /* Get interface GUID. */
774  GUID guidInterface;
775  dwResult = get_net_interface_guid(hDevInfoList, &devinfo_data, 1, &guidInterface);
776  if (dwResult != ERROR_SUCCESS)
777  {
778  /* Something is wrong with this device. Skip it. */
779  continue;
780  }
781 
782  /* Compare GUIDs. */
783  if (memcmp(pguidInterface, &guidInterface, sizeof(GUID)) == 0)
784  {
785  /* Remove the device. */
786  SP_REMOVEDEVICE_PARAMS removedevice_params =
787  {
788  .ClassInstallHeader =
789  {
790  .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
791  .InstallFunction = DIF_REMOVE,
792  },
793  .Scope = DI_REMOVEDEVICE_GLOBAL,
794  .HwProfile = 0,
795  };
796 
797  /* Set class installer parameters for DIF_REMOVE. */
798  if (!SetupDiSetClassInstallParams(
799  hDevInfoList,
800  &devinfo_data,
801  &removedevice_params.ClassInstallHeader,
802  sizeof(SP_REMOVEDEVICE_PARAMS)))
803  {
804  dwResult = GetLastError();
805  msg(M_NONFATAL, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
806  goto cleanup_hDevInfoList;
807  }
808 
809  /* Call appropriate class installer. */
810  if (!SetupDiCallClassInstaller(
811  DIF_REMOVE,
812  hDevInfoList,
813  &devinfo_data))
814  {
815  dwResult = GetLastError();
816  msg(M_NONFATAL, "%s: SetupDiCallClassInstaller(DIF_REMOVE) failed", __FUNCTION__);
817  goto cleanup_hDevInfoList;
818  }
819 
820  /* Check if a system reboot is required. */
821  check_reboot(hDevInfoList, &devinfo_data, pbRebootRequired);
822  dwResult = ERROR_SUCCESS;
823  break;
824  }
825  }
826 
827 cleanup_hDevInfoList:
828  SetupDiDestroyDeviceInfoList(hDevInfoList);
829  return dwResult;
830 }
831 
832 
833 DWORD
835  _In_ LPCGUID pguidInterface,
836  _In_ LPCTSTR szName)
837 {
838  DWORD dwResult;
839 
840  if (pguidInterface == NULL || szName == NULL)
841  {
842  return ERROR_BAD_ARGUMENTS;
843  }
844 
845  /* Get the device class GUID as string. */
846  LPOLESTR szDevClassNetId = NULL;
847  StringFromIID((REFIID)&GUID_DEVCLASS_NET, &szDevClassNetId);
848 
849  /* Get the interface GUID as string. */
850  LPOLESTR szInterfaceId = NULL;
851  StringFromIID((REFIID)pguidInterface, &szInterfaceId);
852 
853  /* Render registry key path. */
854  TCHAR szRegKey[INTERFACE_REGKEY_PATH_MAX];
855  _stprintf_s(
856  szRegKey, _countof(szRegKey),
858  szDevClassNetId,
859  szInterfaceId);
860 
861  /* Open network interface registry key. */
862  HKEY hKey = NULL;
863  dwResult = RegOpenKeyEx(
864  HKEY_LOCAL_MACHINE,
865  szRegKey,
866  0,
867  KEY_SET_VALUE,
868  &hKey);
869  if (dwResult != ERROR_SUCCESS)
870  {
871  SetLastError(dwResult); /* MSDN does not mention RegOpenKeyEx() to set GetLastError(). But we do have an error code. Set last error manually. */
872  msg(M_NONFATAL | M_ERRNO, "%s: RegOpenKeyEx(HKLM, \"%" PRIsLPTSTR "\") failed", __FUNCTION__, szRegKey);
873  goto cleanup_szInterfaceId;
874  }
875 
876  /* Set the interface name. */
877  size_t sizeName = ((_tcslen(szName) + 1) * sizeof(TCHAR));
878 #ifdef _WIN64
879  if (sizeName > DWORD_MAX)
880  {
881  dwResult = ERROR_BAD_ARGUMENTS;
882  msg(M_NONFATAL, "%s: string too big (size %u).", __FUNCTION__, sizeName);
883  goto cleanup_hKey;
884  }
885 #endif
886  dwResult = RegSetKeyValue(
887  hKey,
888  NULL,
889  TEXT("Name"),
890  REG_SZ,
891  szName,
892  (DWORD)sizeName);
893  if (dwResult != ERROR_SUCCESS)
894  {
895  SetLastError(dwResult); /* MSDN does not mention RegSetKeyValue() to set GetLastError(). But we do have an error code. Set last error manually. */
896  msg(M_NONFATAL | M_ERRNO, "%s: RegSetKeyValue(\"Name\") failed", __FUNCTION__);
897  goto cleanup_hKey;
898  }
899 
900 cleanup_hKey:
901  RegCloseKey(hKey);
902 cleanup_szInterfaceId:
903  CoTaskMemFree(szInterfaceId);
904  CoTaskMemFree(szDevClassNetId);
905  return dwResult;
906 }
907 
908 
909 DWORD
911  _In_opt_ HWND hwndParent,
912  _Out_ struct tap_interface_node **ppInterface)
913 {
914  DWORD dwResult;
915 
916  if (ppInterface == NULL)
917  {
918  return ERROR_BAD_ARGUMENTS;
919  }
920 
921  /* Create a list of network devices. */
922  HDEVINFO hDevInfoList = SetupDiGetClassDevsEx(
924  NULL,
925  hwndParent,
926  DIGCF_PRESENT,
927  NULL,
928  NULL,
929  NULL);
930  if (hDevInfoList == INVALID_HANDLE_VALUE)
931  {
932  dwResult = GetLastError();
933  msg(M_NONFATAL, "%s: SetupDiGetClassDevsEx failed", __FUNCTION__);
934  return dwResult;
935  }
936 
937  /* Retrieve information associated with a device information set. */
938  SP_DEVINFO_LIST_DETAIL_DATA devinfo_list_detail_data = { .cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA) };
939  if (!SetupDiGetDeviceInfoListDetail(hDevInfoList, &devinfo_list_detail_data))
940  {
941  dwResult = GetLastError();
942  msg(M_NONFATAL, "%s: SetupDiGetDeviceInfoListDetail failed", __FUNCTION__);
943  goto cleanup_hDevInfoList;
944  }
945 
946  /* Get the device class GUID as string. */
947  LPOLESTR szDevClassNetId = NULL;
948  StringFromIID((REFIID)&GUID_DEVCLASS_NET, &szDevClassNetId);
949 
950  /* Iterate. */
951  *ppInterface = NULL;
952  struct tap_interface_node *pInterfaceTail = NULL;
953  for (DWORD dwIndex = 0;; dwIndex++)
954  {
955  /* Get the device from the list. */
956  SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
957  if (!SetupDiEnumDeviceInfo(
958  hDevInfoList,
959  dwIndex,
960  &devinfo_data))
961  {
962  if (GetLastError() == ERROR_NO_MORE_ITEMS)
963  {
964  break;
965  }
966  else
967  {
968  /* Something is wrong with this device. Skip it. */
969  msg(M_WARN | M_ERRNO, "%s: SetupDiEnumDeviceInfo(%u) failed", __FUNCTION__, dwIndex);
970  continue;
971  }
972  }
973 
974  /* Get interface GUID. */
975  GUID guidInterface;
976  dwResult = get_net_interface_guid(hDevInfoList, &devinfo_data, 1, &guidInterface);
977  if (dwResult != ERROR_SUCCESS)
978  {
979  /* Something is wrong with this device. Skip it. */
980  continue;
981  }
982 
983  /* Get the interface GUID as string. */
984  LPOLESTR szInterfaceId = NULL;
985  StringFromIID((REFIID)&guidInterface, &szInterfaceId);
986 
987  /* Get device hardware ID(s). */
988  DWORD dwDataType = REG_NONE;
989  LPTSTR szzDeviceHardwareIDs = NULL;
990  dwResult = get_device_reg_property(
991  hDevInfoList,
992  &devinfo_data,
993  SPDRP_HARDWAREID,
994  &dwDataType,
995  (LPVOID)&szzDeviceHardwareIDs);
996  if (dwResult != ERROR_SUCCESS)
997  {
998  goto cleanup_szInterfaceId;
999  }
1000 
1001  /* Render registry key path. */
1002  TCHAR szRegKey[INTERFACE_REGKEY_PATH_MAX];
1003  _stprintf_s(
1004  szRegKey, _countof(szRegKey),
1006  szDevClassNetId,
1007  szInterfaceId);
1008 
1009  /* Open network interface registry key. */
1010  HKEY hKey = NULL;
1011  dwResult = RegOpenKeyEx(
1012  HKEY_LOCAL_MACHINE,
1013  szRegKey,
1014  0,
1015  KEY_READ,
1016  &hKey);
1017  if (dwResult != ERROR_SUCCESS)
1018  {
1019  SetLastError(dwResult); /* MSDN does not mention RegOpenKeyEx() to set GetLastError(). But we do have an error code. Set last error manually. */
1020  msg(M_WARN | M_ERRNO, "%s: RegOpenKeyEx(HKLM, \"%" PRIsLPTSTR "\") failed", __FUNCTION__, szRegKey);
1021  goto cleanup_szzDeviceHardwareIDs;
1022  }
1023 
1024  /* Read interface name. */
1025  LPTSTR szName = NULL;
1026  dwResult = get_reg_string(
1027  hKey,
1028  TEXT("Name"),
1029  &szName);
1030  if (dwResult != ERROR_SUCCESS)
1031  {
1032  SetLastError(dwResult);
1033  msg(M_WARN | M_ERRNO, "%s: Cannot determine %" PRIsLPOLESTR " interface name", __FUNCTION__, szInterfaceId);
1034  goto cleanup_hKey;
1035  }
1036 
1037  /* Append to the list. */
1038  size_t hwid_size = (_tcszlen(szzDeviceHardwareIDs) + 1) * sizeof(TCHAR);
1039  size_t name_size = (_tcslen(szName) + 1) * sizeof(TCHAR);
1040  struct tap_interface_node *node = (struct tap_interface_node *)malloc(sizeof(struct tap_interface_node) + hwid_size + name_size);
1041  memcpy(&node->guid, &guidInterface, sizeof(GUID));
1042  node->szzHardwareIDs = (LPTSTR)(node + 1);
1043  memcpy(node->szzHardwareIDs, szzDeviceHardwareIDs, hwid_size);
1044  node->szName = (LPTSTR)((LPBYTE)node->szzHardwareIDs + hwid_size);
1045  memcpy(node->szName, szName, name_size);
1046  node->pNext = NULL;
1047  if (pInterfaceTail)
1048  {
1049  pInterfaceTail->pNext = node;
1050  pInterfaceTail = node;
1051  }
1052  else
1053  {
1054  *ppInterface = pInterfaceTail = node;
1055  }
1056 
1057  free(szName);
1058 cleanup_hKey:
1059  RegCloseKey(hKey);
1060 cleanup_szzDeviceHardwareIDs:
1061  free(szzDeviceHardwareIDs);
1062 cleanup_szInterfaceId:
1063  CoTaskMemFree(szInterfaceId);
1064  }
1065 
1066  dwResult = ERROR_SUCCESS;
1067 
1068  CoTaskMemFree(szDevClassNetId);
1069 cleanup_hDevInfoList:
1070  SetupDiDestroyDeviceInfoList(hDevInfoList);
1071  return dwResult;
1072 }
1073 
1074 
1075 void
1077  _In_ struct tap_interface_node *pInterfaceList)
1078 {
1079  /* Iterate over all nodes of the list. */
1080  while (pInterfaceList)
1081  {
1082  struct tap_interface_node *node = pInterfaceList;
1083  pInterfaceList = pInterfaceList->pNext;
1084 
1085  /* Free the interface node. */
1086  free(node);
1087  }
1088 }
#define M_NONFATAL
Definition: error.h:95
static DWORD check_reboot(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _Inout_ LPBOOL pbRebootRequired)
Checks device install parameters if a system reboot is required.
Definition: tap.c:66
#define INTERFACE_REGKEY_PATH_MAX
Definition: tap.c:46
LPCTSTR szName
Definition: openvpnmsica.c:66
DWORD tap_delete_interface(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidInterface, _Inout_ LPBOOL pbRebootRequired)
Deletes a TUN/TAP interface.
Definition: tap.c:709
DWORD tap_set_interface_name(_In_ LPCGUID pguidInterface, _In_ LPCTSTR szName)
Sets interface name.
Definition: tap.c:834
static const TCHAR szzHardwareIDs[]
Definition: tap.c:43
#define PRIsLPTSTR
Definition: basic.h:28
static const GUID GUID_DEVCLASS_NET
Definition: tap.c:41
LPTSTR szName
Device hardware ID(s)
Definition: tap.h:103
static DWORD get_net_interface_guid(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _In_ int iNumAttempts, _Out_ LPGUID pguidInterface)
Returns network interface ID.
Definition: tap.c:241
static const TCHAR szInterfaceRegKeyPathTemplate[]
Definition: tap.c:45
static DWORD get_device_reg_property(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _In_ DWORD dwProperty, _Out_opt_ LPDWORD pdwPropertyRegDataType, _Out_ LPVOID *ppData)
Returns a specified Plug and Play device property.
Definition: tap.c:331
#define malloc
Definition: cmocka.c:1795
#define _Out_
Definition: basic.h:45
static size_t _tcszlen(_In_ LPCTSTR str)
Returns length of list of strings.
Definition: tap.c:404
#define _In_
Definition: basic.h:33
DWORD tap_list_interfaces(_In_opt_ HWND hwndParent, _Out_ struct tap_interface_node **ppInterface)
Creates a list of available network interfaces.
Definition: tap.c:910
#define PRIsLPOLESTR
Definition: basic.h:29
struct tap_interface_node * pNext
Interface name.
Definition: tap.h:105
#define _Inout_
Definition: basic.h:42
#define TAP_WIN_COMPONENT_ID
Definition: config.h:790
#define M_ERRNO
Definition: error.h:99
#define msg
Definition: error.h:173
static DWORD get_reg_string(_In_ HKEY hKey, _In_ LPCTSTR szName, _Out_ LPTSTR *pszValue)
Reads string value from registry key.
Definition: tap.c:111
#define _Out_opt_
Definition: basic.h:48
LPTSTR szzHardwareIDs
Interface GUID.
Definition: tap.h:102
DWORD tap_create_interface(_In_opt_ HWND hwndParent, _In_opt_ LPCTSTR szDeviceDescription, _Inout_ LPBOOL pbRebootRequired, _Out_ LPGUID pguidInterface)
Creates a TUN/TAP interface.
Definition: tap.c:415
Network interface list node.
Definition: tap.h:99
void tap_free_interface_list(_In_ struct tap_interface_node *pInterfaceList)
Frees a list of network interfaces.
Definition: tap.c:1076
#define _In_opt_
Definition: basic.h:36
#define M_WARN
Definition: error.h:96
#define free
Definition: cmocka.c:1850