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