OpenVPN
tap.c
Go to the documentation of this file.
1 /*
2  * tapctl -- Utility to manipulate TUN/TAP adapters on Windows
3  * https://community.openvpn.net/openvpn/wiki/Tapctl
4  *
5  * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2
9  * as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #elif defined(_MSC_VER)
24 #include <config-msvc.h>
25 #endif
26 
27 #include "tap.h"
28 #include "error.h"
29 
30 #include <windows.h>
31 #include <cfgmgr32.h>
32 #include <objbase.h>
33 #include <setupapi.h>
34 #include <stdio.h>
35 #include <tchar.h>
36 #include <newdev.h>
37 
38 #ifdef _MSC_VER
39 #pragma comment(lib, "advapi32.lib")
40 #pragma comment(lib, "ole32.lib")
41 #pragma comment(lib, "setupapi.lib")
42 #pragma comment(lib, "newdev.lib")
43 #endif
44 
45 
46 const static GUID GUID_DEVCLASS_NET = { 0x4d36e972L, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } };
47 
48 const static TCHAR szAdapterRegKeyPathTemplate[] = TEXT("SYSTEM\\CurrentControlSet\\Control\\Network\\%") TEXT(PRIsLPOLESTR) TEXT("\\%") TEXT(PRIsLPOLESTR) TEXT("\\Connection");
49 #define ADAPTER_REGKEY_PATH_MAX (_countof(TEXT("SYSTEM\\CurrentControlSet\\Control\\Network\\")) - 1 + 38 + _countof(TEXT("\\")) - 1 + 38 + _countof(TEXT("\\Connection")))
50 
64 static void *
65 find_function(const WCHAR *libname, const char *funcname, HMODULE *m)
66 {
67  WCHAR libpath[MAX_PATH];
68  void *fptr = NULL;
69 
70  /* Make sure the dll is loaded from the system32 folder */
71  if (!GetSystemDirectoryW(libpath, _countof(libpath)))
72  {
73  return NULL;
74  }
75 
76  /* +1 for the path seperator '\' */
77  const size_t path_length = wcslen(libpath) + 1 + wcslen(libname);
78  if (path_length >= _countof(libpath))
79  {
80  SetLastError(ERROR_INSUFFICIENT_BUFFER);
81  return NULL;
82  }
83  wcscat_s(libpath, _countof(libpath), L"\\");
84  wcscat_s(libpath, _countof(libpath), libname);
85 
86  *m = LoadLibraryW(libpath);
87  if (*m == NULL)
88  {
89  return NULL;
90  }
91  fptr = GetProcAddress(*m, funcname);
92  if (!fptr)
93  {
94  FreeLibrary(*m);
95  *m = NULL;
96  return NULL;
97  }
98  return fptr;
99 }
100 
108 static inline size_t
109 _tcszlen(_In_z_ LPCTSTR szz)
110 {
111  LPCTSTR s;
112  for (s = szz; s[0]; s += _tcslen(s) + 1)
113  {
114  }
115  return s - szz;
116 }
117 
118 
129 static LPCTSTR
130 _tcszistr(_In_z_ LPCTSTR szzHay, _In_z_ LPCTSTR szNeedle)
131 {
132  for (LPCTSTR s = szzHay; s[0]; s += _tcslen(s) + 1)
133  {
134  if (_tcsicmp(s, szNeedle) == 0)
135  {
136  return s;
137  }
138  }
139 
140  return NULL;
141 }
142 
143 
160 typedef DWORD (*devop_func_t)(
161  _In_ HDEVINFO hDeviceInfoSet,
162  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
163  _Inout_ LPBOOL pbRebootRequired);
164 
165 
182 static DWORD
184  _In_ HDEVINFO hDeviceInfoSet,
185  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
186  _Inout_ LPBOOL pbRebootRequired)
187 {
188  if (pbRebootRequired == NULL)
189  {
190  return ERROR_BAD_ARGUMENTS;
191  }
192 
193  SP_DEVINSTALL_PARAMS devinstall_params = { .cbSize = sizeof(SP_DEVINSTALL_PARAMS) };
194  if (!SetupDiGetDeviceInstallParams(
195  hDeviceInfoSet,
196  pDeviceInfoData,
197  &devinstall_params))
198  {
199  DWORD dwResult = GetLastError();
200  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceInstallParams failed", __FUNCTION__);
201  return dwResult;
202  }
203 
204  if ((devinstall_params.Flags & (DI_NEEDREBOOT | DI_NEEDRESTART)) != 0)
205  {
206  *pbRebootRequired = TRUE;
207  }
208 
209  return ERROR_SUCCESS;
210 }
211 
212 
229 static DWORD
231  _In_ HDEVINFO hDeviceInfoSet,
232  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
233  _Inout_ LPBOOL pbRebootRequired)
234 {
235  SP_REMOVEDEVICE_PARAMS params =
236  {
237  .ClassInstallHeader =
238  {
239  .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
240  .InstallFunction = DIF_REMOVE,
241  },
242  .Scope = DI_REMOVEDEVICE_GLOBAL,
243  .HwProfile = 0,
244  };
245 
246  /* Set class installer parameters for DIF_REMOVE. */
247  if (!SetupDiSetClassInstallParams(
248  hDeviceInfoSet,
249  pDeviceInfoData,
250  &params.ClassInstallHeader,
251  sizeof(SP_REMOVEDEVICE_PARAMS)))
252  {
253  DWORD dwResult = GetLastError();
254  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
255  return dwResult;
256  }
257 
258  /* Call appropriate class installer. */
259  if (!SetupDiCallClassInstaller(
260  DIF_REMOVE,
261  hDeviceInfoSet,
262  pDeviceInfoData))
263  {
264  DWORD dwResult = GetLastError();
265  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_REMOVE) failed", __FUNCTION__);
266  return dwResult;
267  }
268 
269  /* Check if a system reboot is required. */
270  check_reboot(hDeviceInfoSet, pDeviceInfoData, pbRebootRequired);
271  return ERROR_SUCCESS;
272 }
273 
274 
293 static DWORD
295  _In_ HDEVINFO hDeviceInfoSet,
296  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
297  _In_ BOOL bEnable,
298  _Inout_ LPBOOL pbRebootRequired)
299 {
300  SP_PROPCHANGE_PARAMS params =
301  {
302  .ClassInstallHeader =
303  {
304  .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
305  .InstallFunction = DIF_PROPERTYCHANGE,
306  },
307  .StateChange = bEnable ? DICS_ENABLE : DICS_DISABLE,
308  .Scope = DICS_FLAG_GLOBAL,
309  .HwProfile = 0,
310  };
311 
312  /* Set class installer parameters for DIF_PROPERTYCHANGE. */
313  if (!SetupDiSetClassInstallParams(
314  hDeviceInfoSet,
315  pDeviceInfoData,
316  &params.ClassInstallHeader,
317  sizeof(SP_PROPCHANGE_PARAMS)))
318  {
319  DWORD dwResult = GetLastError();
320  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
321  return dwResult;
322  }
323 
324  /* Call appropriate class installer. */
325  if (!SetupDiCallClassInstaller(
326  DIF_PROPERTYCHANGE,
327  hDeviceInfoSet,
328  pDeviceInfoData))
329  {
330  DWORD dwResult = GetLastError();
331  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_PROPERTYCHANGE) failed", __FUNCTION__);
332  return dwResult;
333  }
334 
335  /* Check if a system reboot is required. */
336  check_reboot(hDeviceInfoSet, pDeviceInfoData, pbRebootRequired);
337  return ERROR_SUCCESS;
338 }
339 
340 
357 static DWORD
359  _In_ HDEVINFO hDeviceInfoSet,
360  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
361  _Inout_ LPBOOL pbRebootRequired)
362 {
363  return change_device_state(hDeviceInfoSet, pDeviceInfoData, TRUE, pbRebootRequired);
364 }
365 
366 
383 static DWORD
385  _In_ HDEVINFO hDeviceInfoSet,
386  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
387  _Inout_ LPBOOL pbRebootRequired)
388 {
389  return change_device_state(hDeviceInfoSet, pDeviceInfoData, FALSE, pbRebootRequired);
390 }
391 
392 
407 static DWORD
409  _In_ HKEY hKey,
410  _In_ LPCTSTR szName,
411  _Out_ LPTSTR *pszValue)
412 {
413  if (pszValue == NULL)
414  {
415  return ERROR_BAD_ARGUMENTS;
416  }
417 
418  DWORD dwValueType = REG_NONE, dwSize = 0;
419  DWORD dwResult = RegQueryValueEx(
420  hKey,
421  szName,
422  NULL,
423  &dwValueType,
424  NULL,
425  &dwSize);
426  if (dwResult != ERROR_SUCCESS)
427  {
428  SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we do have an error code. Set last error manually. */
429  msg(M_NONFATAL | M_ERRNO, "%s: enumerating \"%" PRIsLPTSTR "\" registry value failed", __FUNCTION__, szName);
430  return dwResult;
431  }
432 
433  switch (dwValueType)
434  {
435  case REG_SZ:
436  case REG_EXPAND_SZ:
437  {
438  /* Read value. */
439  LPTSTR szValue = (LPTSTR)malloc(dwSize);
440  if (szValue == NULL)
441  {
442  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwSize);
443  return ERROR_OUTOFMEMORY;
444  }
445 
446  dwResult = RegQueryValueEx(
447  hKey,
448  szName,
449  NULL,
450  NULL,
451  (LPBYTE)szValue,
452  &dwSize);
453  if (dwResult != ERROR_SUCCESS)
454  {
455  SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we do have an error code. Set last error manually. */
456  msg(M_NONFATAL | M_ERRNO, "%s: reading \"%" PRIsLPTSTR "\" registry value failed", __FUNCTION__, szName);
457  free(szValue);
458  return dwResult;
459  }
460 
461  if (dwValueType == REG_EXPAND_SZ)
462  {
463  /* Expand the environment strings. */
464  DWORD
465  dwSizeExp = dwSize * 2,
466  dwCountExp =
467 #ifdef UNICODE
468  dwSizeExp / sizeof(TCHAR);
469 #else
470  dwSizeExp / sizeof(TCHAR) - 1; /* Note: ANSI version requires one extra char. */
471 #endif
472  LPTSTR szValueExp = (LPTSTR)malloc(dwSizeExp);
473  if (szValueExp == NULL)
474  {
475  free(szValue);
476  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwSizeExp);
477  return ERROR_OUTOFMEMORY;
478  }
479 
480  DWORD dwCountExpResult = ExpandEnvironmentStrings(
481  szValue,
482  szValueExp, dwCountExp
483  );
484  if (dwCountExpResult == 0)
485  {
486  msg(M_NONFATAL | M_ERRNO, "%s: expanding \"%" PRIsLPTSTR "\" registry value failed", __FUNCTION__, szName);
487  free(szValueExp);
488  free(szValue);
489  return dwResult;
490  }
491  else if (dwCountExpResult <= dwCountExp)
492  {
493  /* The buffer was big enough. */
494  free(szValue);
495  *pszValue = szValueExp;
496  return ERROR_SUCCESS;
497  }
498  else
499  {
500  /* Retry with a bigger buffer. */
501  free(szValueExp);
502 #ifdef UNICODE
503  dwSizeExp = dwCountExpResult * sizeof(TCHAR);
504 #else
505  /* Note: ANSI version requires one extra char. */
506  dwSizeExp = (dwCountExpResult + 1) * sizeof(TCHAR);
507 #endif
508  dwCountExp = dwCountExpResult;
509  szValueExp = (LPTSTR)malloc(dwSizeExp);
510  if (szValueExp == NULL)
511  {
512  free(szValue);
513  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwSizeExp);
514  return ERROR_OUTOFMEMORY;
515  }
516 
517  dwCountExpResult = ExpandEnvironmentStrings(
518  szValue,
519  szValueExp, dwCountExp);
520  free(szValue);
521  *pszValue = szValueExp;
522  return ERROR_SUCCESS;
523  }
524  }
525  else
526  {
527  *pszValue = szValue;
528  return ERROR_SUCCESS;
529  }
530  }
531 
532  default:
533  msg(M_NONFATAL, "%s: \"%" PRIsLPTSTR "\" registry value is not string (type %u)", __FUNCTION__, dwValueType);
534  return ERROR_UNSUPPORTED_TYPE;
535  }
536 }
537 
538 
557 static DWORD
559  _In_ HDEVINFO hDeviceInfoSet,
560  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
561  _In_ int iNumAttempts,
562  _Out_ LPGUID pguidAdapter)
563 {
564  DWORD dwResult = ERROR_BAD_ARGUMENTS;
565 
566  if (pguidAdapter == NULL || iNumAttempts < 1)
567  {
568  return ERROR_BAD_ARGUMENTS;
569  }
570 
571  /* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class<class><id> registry key. */
572  HKEY hKey = SetupDiOpenDevRegKey(
573  hDeviceInfoSet,
574  pDeviceInfoData,
575  DICS_FLAG_GLOBAL,
576  0,
577  DIREG_DRV,
578  KEY_READ);
579  if (hKey == INVALID_HANDLE_VALUE)
580  {
581  dwResult = GetLastError();
582  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiOpenDevRegKey failed", __FUNCTION__);
583  return dwResult;
584  }
585 
586  while (iNumAttempts > 0)
587  {
588  /* Query the NetCfgInstanceId value. Using get_reg_string() right on might clutter the output with error messages while the registry is still being populated. */
589  LPTSTR szCfgGuidString = NULL;
590  dwResult = RegQueryValueEx(hKey, TEXT("NetCfgInstanceId"), NULL, NULL, NULL, NULL);
591  if (dwResult != ERROR_SUCCESS)
592  {
593  if (dwResult == ERROR_FILE_NOT_FOUND && --iNumAttempts > 0)
594  {
595  /* Wait and retry. */
596  Sleep(1000);
597  continue;
598  }
599 
600  SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we do have an error code. Set last error manually. */
601  msg(M_NONFATAL | M_ERRNO, "%s: querying \"NetCfgInstanceId\" registry value failed", __FUNCTION__);
602  break;
603  }
604 
605  /* Read the NetCfgInstanceId value now. */
606  dwResult = get_reg_string(
607  hKey,
608  TEXT("NetCfgInstanceId"),
609  &szCfgGuidString);
610  if (dwResult != ERROR_SUCCESS)
611  {
612  break;
613  }
614 
615  dwResult = SUCCEEDED(CLSIDFromString(szCfgGuidString, (LPCLSID)pguidAdapter)) ? ERROR_SUCCESS : ERROR_INVALID_DATA;
616  free(szCfgGuidString);
617  break;
618  }
619 
620  RegCloseKey(hKey);
621  return dwResult;
622 }
623 
624 
646 static DWORD
648  _In_ HDEVINFO hDeviceInfoSet,
649  _In_ PSP_DEVINFO_DATA pDeviceInfoData,
650  _In_ DWORD dwProperty,
651  _Out_opt_ LPDWORD pdwPropertyRegDataType,
652  _Out_ LPVOID *ppData)
653 {
654  DWORD dwResult = ERROR_BAD_ARGUMENTS;
655 
656  if (ppData == NULL)
657  {
658  return ERROR_BAD_ARGUMENTS;
659  }
660 
661  /* Try with stack buffer first. */
662  BYTE bBufStack[128];
663  DWORD dwRequiredSize = 0;
664  if (SetupDiGetDeviceRegistryProperty(
665  hDeviceInfoSet,
666  pDeviceInfoData,
667  dwProperty,
668  pdwPropertyRegDataType,
669  bBufStack,
670  sizeof(bBufStack),
671  &dwRequiredSize))
672  {
673  /* Copy from stack. */
674  *ppData = malloc(dwRequiredSize);
675  if (*ppData == NULL)
676  {
677  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwRequiredSize);
678  return ERROR_OUTOFMEMORY;
679  }
680 
681  memcpy(*ppData, bBufStack, dwRequiredSize);
682  return ERROR_SUCCESS;
683  }
684  else
685  {
686  dwResult = GetLastError();
687  if (dwResult == ERROR_INSUFFICIENT_BUFFER)
688  {
689  /* Allocate on heap and retry. */
690  *ppData = malloc(dwRequiredSize);
691  if (*ppData == NULL)
692  {
693  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwRequiredSize);
694  return ERROR_OUTOFMEMORY;
695  }
696 
697  if (SetupDiGetDeviceRegistryProperty(
698  hDeviceInfoSet,
699  pDeviceInfoData,
700  dwProperty,
701  pdwPropertyRegDataType,
702  *ppData,
703  dwRequiredSize,
704  &dwRequiredSize))
705  {
706  return ERROR_SUCCESS;
707  }
708  else
709  {
710  dwResult = GetLastError();
711  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceRegistryProperty(%u) failed", __FUNCTION__, dwProperty);
712  return dwResult;
713  }
714  }
715  else
716  {
717  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceRegistryProperty(%u) failed", __FUNCTION__, dwProperty);
718  return dwResult;
719  }
720  }
721 }
722 
723 
724 DWORD
726  _In_opt_ HWND hwndParent,
727  _In_opt_ LPCTSTR szDeviceDescription,
728  _In_ LPCTSTR szHwId,
729  _Inout_ LPBOOL pbRebootRequired,
730  _Out_ LPGUID pguidAdapter)
731 {
732  DWORD dwResult;
733  HMODULE libnewdev = NULL;
734 
735  if (szHwId == NULL
736  || pbRebootRequired == NULL
737  || pguidAdapter == NULL)
738  {
739  return ERROR_BAD_ARGUMENTS;
740  }
741 
742  /* Create an empty device info set for network adapter device class. */
743  HDEVINFO hDevInfoList = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_NET, hwndParent);
744  if (hDevInfoList == INVALID_HANDLE_VALUE)
745  {
746  dwResult = GetLastError();
747  msg(M_NONFATAL, "%s: SetupDiCreateDeviceInfoList failed", __FUNCTION__);
748  return dwResult;
749  }
750 
751  /* Get the device class name from GUID. */
752  TCHAR szClassName[MAX_CLASS_NAME_LEN];
753  if (!SetupDiClassNameFromGuid(
755  szClassName,
756  _countof(szClassName),
757  NULL))
758  {
759  dwResult = GetLastError();
760  msg(M_NONFATAL, "%s: SetupDiClassNameFromGuid failed", __FUNCTION__);
761  goto cleanup_hDevInfoList;
762  }
763 
764  /* Create a new device info element and add it to the device info set. */
765  SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
766  if (!SetupDiCreateDeviceInfo(
767  hDevInfoList,
768  szClassName,
770  szDeviceDescription,
771  hwndParent,
772  DICD_GENERATE_ID,
773  &devinfo_data))
774  {
775  dwResult = GetLastError();
776  msg(M_NONFATAL, "%s: SetupDiCreateDeviceInfo failed", __FUNCTION__);
777  goto cleanup_hDevInfoList;
778  }
779 
780  /* Set a device information element as the selected member of a device information set. */
781  if (!SetupDiSetSelectedDevice(
782  hDevInfoList,
783  &devinfo_data))
784  {
785  dwResult = GetLastError();
786  msg(M_NONFATAL, "%s: SetupDiSetSelectedDevice failed", __FUNCTION__);
787  goto cleanup_hDevInfoList;
788  }
789 
790  /* Set Plug&Play device hardware ID property. */
791  if (!SetupDiSetDeviceRegistryProperty(
792  hDevInfoList,
793  &devinfo_data,
794  SPDRP_HARDWAREID,
795  (const BYTE *)szHwId, (DWORD)((_tcslen(szHwId) + 1) * sizeof(TCHAR))))
796  {
797  dwResult = GetLastError();
798  msg(M_NONFATAL, "%s: SetupDiSetDeviceRegistryProperty failed", __FUNCTION__);
799  goto cleanup_hDevInfoList;
800  }
801 
802  /* Register the device instance with the PnP Manager */
803  if (!SetupDiCallClassInstaller(
804  DIF_REGISTERDEVICE,
805  hDevInfoList,
806  &devinfo_data))
807  {
808  dwResult = GetLastError();
809  msg(M_NONFATAL, "%s: SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed", __FUNCTION__);
810  goto cleanup_hDevInfoList;
811  }
812 
813  /* Install the device using DiInstallDevice()
814  * We instruct the system to use the best driver in the driver store
815  * by setting the drvinfo argument of DiInstallDevice as NULL. This
816  * assumes a driver is already installed in the driver store.
817  */
818 #ifdef HAVE_DIINSTALLDEVICE
819  if (!DiInstallDevice(hwndParent, hDevInfoList, &devinfo_data, NULL, 0, pbRebootRequired))
820 #else
821  /* mingw does not resolve DiInstallDevice, so load it at run time. */
822  typedef BOOL (WINAPI *DiInstallDeviceFn) (HWND, HDEVINFO, SP_DEVINFO_DATA *,
823  SP_DRVINFO_DATA *, DWORD, BOOL *);
824  DiInstallDeviceFn installfn
825  = find_function (L"newdev.dll", "DiInstallDevice", &libnewdev);
826 
827  if (!installfn)
828  {
829  dwResult = GetLastError();
830  msg(M_NONFATAL | M_ERRNO, "%s: Failed to locate DiInstallDevice()", __FUNCTION__);
831  goto cleanup_hDevInfoList;
832  }
833 
834  if (!installfn(hwndParent, hDevInfoList, &devinfo_data, NULL, 0, pbRebootRequired))
835 #endif
836  {
837  dwResult = GetLastError();
838  msg(M_NONFATAL | M_ERRNO, "%s: DiInstallDevice failed", __FUNCTION__);
839  goto cleanup_remove_device;
840  }
841 
842  /* Get network adapter ID from registry. Retry for max 30sec. */
843  dwResult = get_net_adapter_guid(hDevInfoList, &devinfo_data, 30, pguidAdapter);
844 
845 cleanup_remove_device:
846  if (dwResult != ERROR_SUCCESS)
847  {
848  /* The adapter was installed. But, the adapter ID was unobtainable. Clean-up. */
849  SP_REMOVEDEVICE_PARAMS removedevice_params =
850  {
851  .ClassInstallHeader =
852  {
853  .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
854  .InstallFunction = DIF_REMOVE,
855  },
856  .Scope = DI_REMOVEDEVICE_GLOBAL,
857  .HwProfile = 0,
858  };
859 
860  /* Set class installer parameters for DIF_REMOVE. */
861  if (SetupDiSetClassInstallParams(
862  hDevInfoList,
863  &devinfo_data,
864  &removedevice_params.ClassInstallHeader,
865  sizeof(SP_REMOVEDEVICE_PARAMS)))
866  {
867  /* Call appropriate class installer. */
868  if (SetupDiCallClassInstaller(
869  DIF_REMOVE,
870  hDevInfoList,
871  &devinfo_data))
872  {
873  /* Check if a system reboot is required. */
874  check_reboot(hDevInfoList, &devinfo_data, pbRebootRequired);
875  }
876  else
877  {
878  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_REMOVE) failed", __FUNCTION__);
879  }
880  }
881  else
882  {
883  msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
884  }
885  }
886 
887 cleanup_hDevInfoList:
888  if (libnewdev)
889  {
890  FreeLibrary(libnewdev);
891  }
892  SetupDiDestroyDeviceInfoList(hDevInfoList);
893  return dwResult;
894 }
895 
896 
917 static DWORD
919  _In_opt_ HWND hwndParent,
920  _In_ LPCGUID pguidAdapter,
921  _In_ devop_func_t funcOperation,
922  _Inout_ LPBOOL pbRebootRequired)
923 {
924  DWORD dwResult;
925 
926  if (pguidAdapter == NULL)
927  {
928  return ERROR_BAD_ARGUMENTS;
929  }
930 
931  /* Create a list of network devices. */
932  HDEVINFO hDevInfoList = SetupDiGetClassDevsEx(
934  NULL,
935  hwndParent,
936  DIGCF_PRESENT,
937  NULL,
938  NULL,
939  NULL);
940  if (hDevInfoList == INVALID_HANDLE_VALUE)
941  {
942  dwResult = GetLastError();
943  msg(M_NONFATAL, "%s: SetupDiGetClassDevsEx failed", __FUNCTION__);
944  return dwResult;
945  }
946 
947  /* Retrieve information associated with a device information set. */
948  SP_DEVINFO_LIST_DETAIL_DATA devinfo_list_detail_data = { .cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA) };
949  if (!SetupDiGetDeviceInfoListDetail(hDevInfoList, &devinfo_list_detail_data))
950  {
951  dwResult = GetLastError();
952  msg(M_NONFATAL, "%s: SetupDiGetDeviceInfoListDetail failed", __FUNCTION__);
953  goto cleanup_hDevInfoList;
954  }
955 
956  /* Iterate. */
957  for (DWORD dwIndex = 0;; dwIndex++)
958  {
959  /* Get the device from the list. */
960  SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
961  if (!SetupDiEnumDeviceInfo(
962  hDevInfoList,
963  dwIndex,
964  &devinfo_data))
965  {
966  if (GetLastError() == ERROR_NO_MORE_ITEMS)
967  {
968  LPOLESTR szAdapterId = NULL;
969  StringFromIID((REFIID)pguidAdapter, &szAdapterId);
970  msg(M_NONFATAL, "%s: Adapter %" PRIsLPOLESTR " not found", __FUNCTION__, szAdapterId);
971  CoTaskMemFree(szAdapterId);
972  dwResult = ERROR_FILE_NOT_FOUND;
973  goto cleanup_hDevInfoList;
974  }
975  else
976  {
977  /* Something is wrong with this device. Skip it. */
978  msg(M_WARN | M_ERRNO, "%s: SetupDiEnumDeviceInfo(%u) failed", __FUNCTION__, dwIndex);
979  continue;
980  }
981  }
982 
983  /* Get adapter GUID. */
984  GUID guidAdapter;
985  dwResult = get_net_adapter_guid(hDevInfoList, &devinfo_data, 1, &guidAdapter);
986  if (dwResult != ERROR_SUCCESS)
987  {
988  /* Something is wrong with this device. Skip it. */
989  continue;
990  }
991 
992  /* Compare GUIDs. */
993  if (memcmp(pguidAdapter, &guidAdapter, sizeof(GUID)) == 0)
994  {
995  dwResult = funcOperation(hDevInfoList, &devinfo_data, pbRebootRequired);
996  break;
997  }
998  }
999 
1000 cleanup_hDevInfoList:
1001  SetupDiDestroyDeviceInfoList(hDevInfoList);
1002  return dwResult;
1003 }
1004 
1005 
1006 DWORD
1008  _In_opt_ HWND hwndParent,
1009  _In_ LPCGUID pguidAdapter,
1010  _Inout_ LPBOOL pbRebootRequired)
1011 {
1012  return execute_on_first_adapter(hwndParent, pguidAdapter, delete_device, pbRebootRequired);
1013 }
1014 
1015 
1016 DWORD
1018  _In_opt_ HWND hwndParent,
1019  _In_ LPCGUID pguidAdapter,
1020  _In_ BOOL bEnable,
1021  _Inout_ LPBOOL pbRebootRequired)
1022 {
1023  return execute_on_first_adapter(hwndParent, pguidAdapter, bEnable ? enable_device : disable_device, pbRebootRequired);
1024 }
1025 
1026 /* stripped version of ExecCommand in interactive.c */
1027 static DWORD
1028 ExecCommand(const WCHAR* cmdline)
1029 {
1030  DWORD exit_code;
1031  STARTUPINFOW si;
1032  PROCESS_INFORMATION pi;
1033  DWORD proc_flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
1034  WCHAR* cmdline_dup = NULL;
1035 
1036  ZeroMemory(&si, sizeof(si));
1037  ZeroMemory(&pi, sizeof(pi));
1038 
1039  si.cb = sizeof(si);
1040 
1041  /* CreateProcess needs a modifiable cmdline: make a copy */
1042  cmdline_dup = _wcsdup(cmdline);
1043  if (cmdline_dup && CreateProcessW(NULL, cmdline_dup, NULL, NULL, FALSE,
1044  proc_flags, NULL, NULL, &si, &pi))
1045  {
1046  WaitForSingleObject(pi.hProcess, INFINITE);
1047  if (!GetExitCodeProcess(pi.hProcess, &exit_code))
1048  {
1049  exit_code = GetLastError();
1050  }
1051 
1052  CloseHandle(pi.hProcess);
1053  CloseHandle(pi.hThread);
1054  }
1055  else
1056  {
1057  exit_code = GetLastError();
1058  }
1059 
1060  free(cmdline_dup);
1061  return exit_code;
1062 }
1063 
1064 DWORD
1066  _In_ LPCGUID pguidAdapter,
1067  _In_ LPCTSTR szName,
1068  _In_ BOOL bSilent)
1069 {
1070  DWORD dwResult;
1071  int msg_flag = bSilent ? M_WARN : M_NONFATAL;
1072  msg_flag |= M_ERRNO;
1073 
1074  if (pguidAdapter == NULL || szName == NULL)
1075  {
1076  return ERROR_BAD_ARGUMENTS;
1077  }
1078 
1079  /* Get the device class GUID as string. */
1080  LPOLESTR szDevClassNetId = NULL;
1081  StringFromIID((REFIID)&GUID_DEVCLASS_NET, &szDevClassNetId);
1082 
1083  /* Get the adapter GUID as string. */
1084  LPOLESTR szAdapterId = NULL;
1085  StringFromIID((REFIID)pguidAdapter, &szAdapterId);
1086 
1087  /* Render registry key path. */
1088  TCHAR szRegKey[ADAPTER_REGKEY_PATH_MAX];
1089  _stprintf_s(
1090  szRegKey, _countof(szRegKey),
1092  szDevClassNetId,
1093  szAdapterId);
1094 
1095  /* Open network adapter registry key. */
1096  HKEY hKey = NULL;
1097  dwResult = RegOpenKeyEx(
1098  HKEY_LOCAL_MACHINE,
1099  szRegKey,
1100  0,
1101  KEY_QUERY_VALUE,
1102  &hKey);
1103  if (dwResult != ERROR_SUCCESS)
1104  {
1105  SetLastError(dwResult); /* MSDN does not mention RegOpenKeyEx() to set GetLastError(). But we do have an error code. Set last error manually. */
1106  msg(msg_flag, "%s: RegOpenKeyEx(HKLM, \"%" PRIsLPTSTR "\") failed", __FUNCTION__, szRegKey);
1107  goto cleanup_szAdapterId;
1108  }
1109 
1110  LPTSTR szOldName = NULL;
1111  dwResult = get_reg_string(hKey, TEXT("Name"), &szOldName);
1112  if (dwResult != ERROR_SUCCESS)
1113  {
1114  SetLastError(dwResult);
1115  msg(msg_flag, "%s: Error reading adapter name", __FUNCTION__);
1116  goto cleanup_hKey;
1117  }
1118 
1119  /* rename adapter via netsh call */
1120  const TCHAR* szFmt = TEXT("netsh interface set interface name=\"%")
1121  TEXT(PRIsLPTSTR) TEXT("\" newname=\"%") TEXT(PRIsLPTSTR) TEXT("\"");
1122  size_t ncmdline = _tcslen(szFmt) + _tcslen(szOldName) + _tcslen(szName) + 1;
1123  WCHAR* szCmdLine = malloc(ncmdline * sizeof(TCHAR));
1124  _stprintf_s(szCmdLine, ncmdline, szFmt, szOldName, szName);
1125 
1126  free(szOldName);
1127 
1128  dwResult = ExecCommand(szCmdLine);
1129  free(szCmdLine);
1130 
1131  if (dwResult != ERROR_SUCCESS)
1132  {
1133  SetLastError(dwResult);
1134  msg(msg_flag, "%s: Error renaming adapter", __FUNCTION__);
1135  goto cleanup_hKey;
1136  }
1137 
1138 cleanup_hKey:
1139  RegCloseKey(hKey);
1140 cleanup_szAdapterId:
1141  CoTaskMemFree(szAdapterId);
1142  CoTaskMemFree(szDevClassNetId);
1143  return dwResult;
1144 }
1145 
1146 
1147 DWORD
1149  _In_opt_ HWND hwndParent,
1150  _In_opt_ LPCTSTR szzHwIDs,
1151  _Out_ struct tap_adapter_node **ppAdapter)
1152 {
1153  DWORD dwResult;
1154 
1155  if (ppAdapter == NULL)
1156  {
1157  return ERROR_BAD_ARGUMENTS;
1158  }
1159 
1160  /* Create a list of network devices. */
1161  HDEVINFO hDevInfoList = SetupDiGetClassDevsEx(
1163  NULL,
1164  hwndParent,
1165  DIGCF_PRESENT,
1166  NULL,
1167  NULL,
1168  NULL);
1169  if (hDevInfoList == INVALID_HANDLE_VALUE)
1170  {
1171  dwResult = GetLastError();
1172  msg(M_NONFATAL, "%s: SetupDiGetClassDevsEx failed", __FUNCTION__);
1173  return dwResult;
1174  }
1175 
1176  /* Retrieve information associated with a device information set. */
1177  SP_DEVINFO_LIST_DETAIL_DATA devinfo_list_detail_data = { .cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA) };
1178  if (!SetupDiGetDeviceInfoListDetail(hDevInfoList, &devinfo_list_detail_data))
1179  {
1180  dwResult = GetLastError();
1181  msg(M_NONFATAL, "%s: SetupDiGetDeviceInfoListDetail failed", __FUNCTION__);
1182  goto cleanup_hDevInfoList;
1183  }
1184 
1185  /* Get the device class GUID as string. */
1186  LPOLESTR szDevClassNetId = NULL;
1187  StringFromIID((REFIID)&GUID_DEVCLASS_NET, &szDevClassNetId);
1188 
1189  /* Iterate. */
1190  *ppAdapter = NULL;
1191  struct tap_adapter_node *pAdapterTail = NULL;
1192  for (DWORD dwIndex = 0;; dwIndex++)
1193  {
1194  /* Get the device from the list. */
1195  SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
1196  if (!SetupDiEnumDeviceInfo(
1197  hDevInfoList,
1198  dwIndex,
1199  &devinfo_data))
1200  {
1201  if (GetLastError() == ERROR_NO_MORE_ITEMS)
1202  {
1203  break;
1204  }
1205  else
1206  {
1207  /* Something is wrong with this device. Skip it. */
1208  msg(M_WARN | M_ERRNO, "%s: SetupDiEnumDeviceInfo(%u) failed", __FUNCTION__, dwIndex);
1209  continue;
1210  }
1211  }
1212 
1213  /* Get device hardware ID(s). */
1214  DWORD dwDataType = REG_NONE;
1215  LPTSTR szzDeviceHardwareIDs = NULL;
1216  dwResult = get_device_reg_property(
1217  hDevInfoList,
1218  &devinfo_data,
1219  SPDRP_HARDWAREID,
1220  &dwDataType,
1221  (LPVOID)&szzDeviceHardwareIDs);
1222  if (dwResult != ERROR_SUCCESS)
1223  {
1224  /* Something is wrong with this device. Skip it. */
1225  continue;
1226  }
1227 
1228  /* Check that hardware ID is REG_SZ/REG_MULTI_SZ, and optionally if it matches ours. */
1229  if (dwDataType == REG_SZ)
1230  {
1231  if (szzHwIDs && !_tcszistr(szzHwIDs, szzDeviceHardwareIDs))
1232  {
1233  /* This is not our device. Skip it. */
1234  goto cleanup_szzDeviceHardwareIDs;
1235  }
1236  }
1237  else if (dwDataType == REG_MULTI_SZ)
1238  {
1239  if (szzHwIDs)
1240  {
1241  for (LPTSTR s = szzDeviceHardwareIDs;; s += _tcslen(s) + 1)
1242  {
1243  if (s[0] == 0)
1244  {
1245  /* This is not our device. Skip it. */
1246  goto cleanup_szzDeviceHardwareIDs;
1247  }
1248  else if (_tcszistr(szzHwIDs, s))
1249  {
1250  /* This is our device. */
1251  break;
1252  }
1253  }
1254  }
1255  }
1256  else
1257  {
1258  /* Unexpected hardware ID format. Skip device. */
1259  goto cleanup_szzDeviceHardwareIDs;
1260  }
1261 
1262  /* Get adapter GUID. */
1263  GUID guidAdapter;
1264  dwResult = get_net_adapter_guid(hDevInfoList, &devinfo_data, 1, &guidAdapter);
1265  if (dwResult != ERROR_SUCCESS)
1266  {
1267  /* Something is wrong with this device. Skip it. */
1268  goto cleanup_szzDeviceHardwareIDs;
1269  }
1270 
1271  /* Get the adapter GUID as string. */
1272  LPOLESTR szAdapterId = NULL;
1273  StringFromIID((REFIID)&guidAdapter, &szAdapterId);
1274 
1275  /* Render registry key path. */
1276  TCHAR szRegKey[ADAPTER_REGKEY_PATH_MAX];
1277  _stprintf_s(
1278  szRegKey, _countof(szRegKey),
1280  szDevClassNetId,
1281  szAdapterId);
1282 
1283  /* Open network adapter registry key. */
1284  HKEY hKey = NULL;
1285  dwResult = RegOpenKeyEx(
1286  HKEY_LOCAL_MACHINE,
1287  szRegKey,
1288  0,
1289  KEY_READ,
1290  &hKey);
1291  if (dwResult != ERROR_SUCCESS)
1292  {
1293  SetLastError(dwResult); /* MSDN does not mention RegOpenKeyEx() to set GetLastError(). But we do have an error code. Set last error manually. */
1294  msg(M_WARN | M_ERRNO, "%s: RegOpenKeyEx(HKLM, \"%" PRIsLPTSTR "\") failed", __FUNCTION__, szRegKey);
1295  goto cleanup_szAdapterId;
1296  }
1297 
1298  /* Read adapter name. */
1299  LPTSTR szName = NULL;
1300  dwResult = get_reg_string(
1301  hKey,
1302  TEXT("Name"),
1303  &szName);
1304  if (dwResult != ERROR_SUCCESS)
1305  {
1306  SetLastError(dwResult);
1307  msg(M_WARN | M_ERRNO, "%s: Cannot determine %" PRIsLPOLESTR " adapter name", __FUNCTION__, szAdapterId);
1308  goto cleanup_hKey;
1309  }
1310 
1311  /* Append to the list. */
1312  size_t hwid_size = (_tcszlen(szzDeviceHardwareIDs) + 1) * sizeof(TCHAR);
1313  size_t name_size = (_tcslen(szName) + 1) * sizeof(TCHAR);
1314  struct tap_adapter_node *node = (struct tap_adapter_node *)malloc(sizeof(struct tap_adapter_node) + hwid_size + name_size);
1315  if (node == NULL)
1316  {
1317  msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, sizeof(struct tap_adapter_node) + hwid_size + name_size);
1318  dwResult = ERROR_OUTOFMEMORY; goto cleanup_szName;
1319  }
1320 
1321  memcpy(&node->guid, &guidAdapter, sizeof(GUID));
1322  node->szzHardwareIDs = (LPTSTR)(node + 1);
1323  memcpy(node->szzHardwareIDs, szzDeviceHardwareIDs, hwid_size);
1324  node->szName = (LPTSTR)((LPBYTE)node->szzHardwareIDs + hwid_size);
1325  memcpy(node->szName, szName, name_size);
1326  node->pNext = NULL;
1327  if (pAdapterTail)
1328  {
1329  pAdapterTail->pNext = node;
1330  pAdapterTail = node;
1331  }
1332  else
1333  {
1334  *ppAdapter = pAdapterTail = node;
1335  }
1336 
1337 cleanup_szName:
1338  free(szName);
1339 cleanup_hKey:
1340  RegCloseKey(hKey);
1341 cleanup_szAdapterId:
1342  CoTaskMemFree(szAdapterId);
1343 cleanup_szzDeviceHardwareIDs:
1344  free(szzDeviceHardwareIDs);
1345  }
1346 
1347  dwResult = ERROR_SUCCESS;
1348 
1349  CoTaskMemFree(szDevClassNetId);
1350 cleanup_hDevInfoList:
1351  SetupDiDestroyDeviceInfoList(hDevInfoList);
1352  return dwResult;
1353 }
1354 
1355 
1356 void
1358  _In_ struct tap_adapter_node *pAdapterList)
1359 {
1360  /* Iterate over all nodes of the list. */
1361  while (pAdapterList)
1362  {
1363  struct tap_adapter_node *node = pAdapterList;
1364  pAdapterList = pAdapterList->pNext;
1365 
1366  /* Free the adapter node. */
1367  free(node);
1368  }
1369 }
#define M_NONFATAL
Definition: error.h:99
static DWORD get_net_adapter_guid(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _In_ int iNumAttempts, _Out_ LPGUID pguidAdapter)
Returns network adapter ID.
Definition: tap.c:558
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:183
static DWORD enable_device(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _Inout_ LPBOOL pbRebootRequired)
Enables the device.
Definition: tap.c:358
DWORD tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCTSTR szName, _In_ BOOL bSilent)
Sets adapter name.
Definition: tap.c:1065
static const TCHAR szAdapterRegKeyPathTemplate[]
Definition: tap.c:48
#define PRIsLPTSTR
Definition: basic.h:29
static const GUID GUID_DEVCLASS_NET
Definition: tap.c:46
static DWORD delete_device(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _Inout_ LPBOOL pbRebootRequired)
Deletes the device.
Definition: tap.c:230
static DWORD ExecCommand(const WCHAR *cmdline)
Definition: tap.c:1028
static void * find_function(const WCHAR *libname, const char *funcname, HMODULE *m)
Dynamically load a library and find a function in it.
Definition: tap.c:65
DWORD tap_delete_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _Inout_ LPBOOL pbRebootRequired)
Deletes an adapter.
Definition: tap.c:1007
DWORD(* devop_func_t)(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _Inout_ LPBOOL pbRebootRequired)
Function that performs a specific task on a device.
Definition: tap.c:160
DWORD tap_enable_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _In_ BOOL bEnable, _Inout_ LPBOOL pbRebootRequired)
Enables or disables an adapter.
Definition: tap.c:1017
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:647
#define malloc
Definition: cmocka.c:1795
static DWORD execute_on_first_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _In_ devop_func_t funcOperation, _Inout_ LPBOOL pbRebootRequired)
Performs a given task on an adapter.
Definition: tap.c:918
#define _Out_
Definition: basic.h:57
#define _In_
Definition: basic.h:42
DWORD tap_create_adapter(_In_opt_ HWND hwndParent, _In_opt_ LPCTSTR szDeviceDescription, _In_ LPCTSTR szHwId, _Inout_ LPBOOL pbRebootRequired, _Out_ LPGUID pguidAdapter)
Creates a TUN/TAP adapter.
Definition: tap.c:725
#define PRIsLPOLESTR
Definition: basic.h:30
#define _Inout_
Definition: basic.h:51
struct tap_adapter_node * pNext
Adapter name.
Definition: tap.h:142
static size_t _tcszlen(_In_z_ LPCTSTR szz)
Returns length of string of strings.
Definition: tap.c:109
static LPCTSTR _tcszistr(_In_z_ LPCTSTR szzHay, _In_z_ LPCTSTR szNeedle)
Checks if string is contained in the string of strings.
Definition: tap.c:130
DWORD tap_list_adapters(_In_opt_ HWND hwndParent, _In_opt_ LPCTSTR szzHwIDs, _Out_ struct tap_adapter_node **ppAdapter)
Creates a list of existing network adapters.
Definition: tap.c:1148
#define M_ERRNO
Definition: error.h:103
void tap_free_adapter_list(_In_ struct tap_adapter_node *pAdapterList)
Frees a list of network adapters.
Definition: tap.c:1357
LPTSTR szzHardwareIDs
Adapter GUID.
Definition: tap.h:139
static DWORD get_reg_string(_In_ HKEY hKey, _In_ LPCTSTR szName, _Out_ LPTSTR *pszValue)
Reads string value from registry key.
Definition: tap.c:408
#define _Out_opt_
Definition: basic.h:60
#define msg(flags,...)
Definition: error.h:153
#define _In_opt_
Definition: basic.h:45
#define _In_z_
Definition: basic.h:48
#define M_FATAL
Definition: error.h:98
#define M_WARN
Definition: error.h:100
#define free
Definition: cmocka.c:1850
Network adapter list node.
Definition: tap.h:136
#define ADAPTER_REGKEY_PATH_MAX
Definition: tap.c:49
static DWORD disable_device(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _Inout_ LPBOOL pbRebootRequired)
Disables the device.
Definition: tap.c:384
LPTSTR szName
Device hardware ID(s)
Definition: tap.h:140
static DWORD change_device_state(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _In_ BOOL bEnable, _Inout_ LPBOOL pbRebootRequired)
Changes the device state.
Definition: tap.c:294
GUID guid
Definition: tap.h:138