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