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-2025 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, see <https://www.gnu.org/licenses/>.
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include "tap.h"
25#include "error.h"
26
27#include <windows.h>
28#include <cfgmgr32.h>
29#include <objbase.h>
30#include <setupapi.h>
31#include <stdio.h>
32#include <wchar.h>
33#include <newdev.h>
34
35#ifdef _MSC_VER
36#pragma comment(lib, "advapi32.lib")
37#pragma comment(lib, "ole32.lib")
38#pragma comment(lib, "setupapi.lib")
39#pragma comment(lib, "newdev.lib")
40#endif
41
42
43const static GUID GUID_DEVCLASS_NET = {
44 0x4d36e972L, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 }
45};
46
47const static WCHAR szAdapterRegKeyPathTemplate[] =
48 L"SYSTEM\\CurrentControlSet\\Control\\Network\\%ls\\%ls\\Connection";
49#define ADAPTER_REGKEY_PATH_MAX \
50 (_countof(L"SYSTEM\\CurrentControlSet\\Control\\Network\\") - 1 + 38 + _countof(L"\\") - 1 \
51 + 38 + _countof(L"\\Connection"))
52
66static void *
67find_function(const WCHAR *libname, const char *funcname, HMODULE *m)
68{
69 WCHAR libpath[MAX_PATH];
70 void *fptr = NULL;
71
72 /* Make sure the dll is loaded from the system32 folder */
73 if (!GetSystemDirectoryW(libpath, _countof(libpath)))
74 {
75 return NULL;
76 }
77
78 /* +1 for the path seperator '\' */
79 const size_t path_length = wcslen(libpath) + 1 + wcslen(libname);
80 if (path_length >= _countof(libpath))
81 {
82 SetLastError(ERROR_INSUFFICIENT_BUFFER);
83 return NULL;
84 }
85 wcscat_s(libpath, _countof(libpath), L"\\");
86 wcscat_s(libpath, _countof(libpath), libname);
87
88 *m = LoadLibraryW(libpath);
89 if (*m == NULL)
90 {
91 return NULL;
92 }
93 fptr = GetProcAddress(*m, funcname);
94 if (!fptr)
95 {
96 FreeLibrary(*m);
97 *m = NULL;
98 return NULL;
99 }
100 return fptr;
101}
102
110static inline size_t
111wcszlen(_In_z_ LPCWSTR szz)
112{
113 LPCWSTR s;
114 for (s = szz; s[0]; s += wcslen(s) + 1)
115 {
116 }
117 return s - szz;
118}
119
120
131static LPCWSTR
132wcszistr(_In_z_ LPCWSTR szzHay, _In_z_ LPCWSTR szNeedle)
133{
134 for (LPCWSTR s = szzHay; s[0]; s += wcslen(s) + 1)
135 {
136 if (wcsicmp(s, szNeedle) == 0)
137 {
138 return s;
139 }
140 }
141
142 return NULL;
143}
144
145
162typedef DWORD (*devop_func_t)(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData,
163 _Inout_ LPBOOL pbRebootRequired);
164
165
182static DWORD
183check_reboot(_In_ HDEVINFO hDeviceInfoSet, _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(hDeviceInfoSet, pDeviceInfoData, &devinstall_params))
193 {
194 DWORD dwResult = GetLastError();
195 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceInstallParams failed", __FUNCTION__);
196 return dwResult;
197 }
198
199 if ((devinstall_params.Flags & (DI_NEEDREBOOT | DI_NEEDRESTART)) != 0)
200 {
201 *pbRebootRequired = TRUE;
202 }
203
204 return ERROR_SUCCESS;
205}
206
207
224static DWORD
225delete_device(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData,
226 _Inout_ LPBOOL pbRebootRequired)
227{
228 SP_REMOVEDEVICE_PARAMS params = {
229 .ClassInstallHeader = {
230 .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
231 .InstallFunction = DIF_REMOVE,
232 },
233 .Scope = DI_REMOVEDEVICE_GLOBAL,
234 .HwProfile = 0,
235 };
236
237 /* Set class installer parameters for DIF_REMOVE. */
238 if (!SetupDiSetClassInstallParams(hDeviceInfoSet, pDeviceInfoData, &params.ClassInstallHeader,
239 sizeof(SP_REMOVEDEVICE_PARAMS)))
240 {
241 DWORD dwResult = GetLastError();
242 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
243 return dwResult;
244 }
245
246 /* Call appropriate class installer. */
247 if (!SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfoSet, pDeviceInfoData))
248 {
249 DWORD dwResult = GetLastError();
250 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_REMOVE) failed", __FUNCTION__);
251 return dwResult;
252 }
253
254 /* Check if a system reboot is required. */
255 check_reboot(hDeviceInfoSet, pDeviceInfoData, pbRebootRequired);
256 return ERROR_SUCCESS;
257}
258
259
278static DWORD
279change_device_state(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData,
280 _In_ BOOL bEnable, _Inout_ LPBOOL pbRebootRequired)
281{
282 SP_PROPCHANGE_PARAMS params = {
283 .ClassInstallHeader = {
284 .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
285 .InstallFunction = DIF_PROPERTYCHANGE,
286 },
287 .StateChange = bEnable ? DICS_ENABLE : DICS_DISABLE,
288 .Scope = DICS_FLAG_GLOBAL,
289 .HwProfile = 0,
290 };
291
292 /* Set class installer parameters for DIF_PROPERTYCHANGE. */
293 if (!SetupDiSetClassInstallParams(hDeviceInfoSet, pDeviceInfoData, &params.ClassInstallHeader,
294 sizeof(SP_PROPCHANGE_PARAMS)))
295 {
296 DWORD dwResult = GetLastError();
297 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
298 return dwResult;
299 }
300
301 /* Call appropriate class installer. */
302 if (!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDeviceInfoSet, pDeviceInfoData))
303 {
304 DWORD dwResult = GetLastError();
305 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_PROPERTYCHANGE) failed",
306 __FUNCTION__);
307 return dwResult;
308 }
309
310 /* Check if a system reboot is required. */
311 check_reboot(hDeviceInfoSet, pDeviceInfoData, pbRebootRequired);
312 return ERROR_SUCCESS;
313}
314
315
332static DWORD
333enable_device(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData,
334 _Inout_ LPBOOL pbRebootRequired)
335{
336 return change_device_state(hDeviceInfoSet, pDeviceInfoData, TRUE, pbRebootRequired);
337}
338
339
356static DWORD
357disable_device(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData,
358 _Inout_ LPBOOL pbRebootRequired)
359{
360 return change_device_state(hDeviceInfoSet, pDeviceInfoData, FALSE, pbRebootRequired);
361}
362
363
378static DWORD
379get_reg_string(_In_ HKEY hKey, _In_ LPCWSTR szName, _Out_ LPWSTR *pszValue)
380{
381 if (pszValue == NULL)
382 {
383 return ERROR_BAD_ARGUMENTS;
384 }
385
386 DWORD dwValueType = REG_NONE, dwSize = 0;
387 DWORD dwResult = RegQueryValueEx(hKey, szName, NULL, &dwValueType, NULL, &dwSize);
388 if (dwResult != ERROR_SUCCESS)
389 {
390 SetLastError(dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError().
391 But we do have an error code. Set last error manually. */
392 msg(M_NONFATAL | M_ERRNO, "%s: enumerating \"%ls\" registry value failed", __FUNCTION__,
393 szName);
394 return dwResult;
395 }
396
397 switch (dwValueType)
398 {
399 case REG_SZ:
400 case REG_EXPAND_SZ:
401 {
402 /* Read value. */
403 LPWSTR szValue = (LPWSTR)malloc(dwSize);
404 if (szValue == NULL)
405 {
406 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwSize);
407 return ERROR_OUTOFMEMORY;
408 }
409
410 dwResult = RegQueryValueEx(hKey, szName, NULL, NULL, (LPBYTE)szValue, &dwSize);
411 if (dwResult != ERROR_SUCCESS)
412 {
413 SetLastError(
414 dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But
415 we do have an error code. Set last error manually. */
416 msg(M_NONFATAL | M_ERRNO, "%s: reading \"%ls\" registry value failed", __FUNCTION__,
417 szName);
418 free(szValue);
419 return dwResult;
420 }
421
422 if (dwValueType == REG_EXPAND_SZ)
423 {
424 /* Expand the environment strings. */
425 DWORD
426 dwSizeExp = dwSize * 2, dwCountExp =
427#ifdef UNICODE
428 dwSizeExp / sizeof(WCHAR);
429#else
430 dwSizeExp / sizeof(WCHAR)
431 - 1; /* Note: ANSI version requires one extra char. */
432#endif
433 LPWSTR szValueExp = (LPWSTR)malloc(dwSizeExp);
434 if (szValueExp == NULL)
435 {
436 free(szValue);
437 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwSizeExp);
438 return ERROR_OUTOFMEMORY;
439 }
440
441 DWORD dwCountExpResult = ExpandEnvironmentStrings(szValue, szValueExp, dwCountExp);
442 if (dwCountExpResult == 0)
443 {
444 msg(M_NONFATAL | M_ERRNO, "%s: expanding \"%ls\" registry value failed",
445 __FUNCTION__, szName);
446 free(szValueExp);
447 free(szValue);
448 return dwResult;
449 }
450 else if (dwCountExpResult <= dwCountExp)
451 {
452 /* The buffer was big enough. */
453 free(szValue);
454 *pszValue = szValueExp;
455 return ERROR_SUCCESS;
456 }
457 else
458 {
459 /* Retry with a bigger buffer. */
460 free(szValueExp);
461#ifdef UNICODE
462 dwSizeExp = dwCountExpResult * sizeof(WCHAR);
463#else
464 /* Note: ANSI version requires one extra char. */
465 dwSizeExp = (dwCountExpResult + 1) * sizeof(WCHAR);
466#endif
467 dwCountExp = dwCountExpResult;
468 szValueExp = (LPWSTR)malloc(dwSizeExp);
469 if (szValueExp == NULL)
470 {
471 free(szValue);
472 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwSizeExp);
473 return ERROR_OUTOFMEMORY;
474 }
475
476 dwCountExpResult = ExpandEnvironmentStrings(szValue, szValueExp, dwCountExp);
477 free(szValue);
478 *pszValue = szValueExp;
479 return ERROR_SUCCESS;
480 }
481 }
482 else
483 {
484 *pszValue = szValue;
485 return ERROR_SUCCESS;
486 }
487 }
488
489 default:
490 msg(M_NONFATAL, "%s: \"%ls\" registry value is not string (type %u)", __FUNCTION__,
491 dwValueType);
492 return ERROR_UNSUPPORTED_TYPE;
493 }
494}
495
496
515static DWORD
516get_net_adapter_guid(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData,
517 _In_ int iNumAttempts, _Out_ LPGUID pguidAdapter)
518{
519 DWORD dwResult = ERROR_BAD_ARGUMENTS;
520
521 if (pguidAdapter == NULL || iNumAttempts < 1)
522 {
523 return ERROR_BAD_ARGUMENTS;
524 }
525
526 /* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class<class><id> registry key. */
527 HKEY hKey = SetupDiOpenDevRegKey(hDeviceInfoSet, pDeviceInfoData, DICS_FLAG_GLOBAL, 0,
528 DIREG_DRV, KEY_READ);
529 if (hKey == INVALID_HANDLE_VALUE)
530 {
531 dwResult = GetLastError();
532 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiOpenDevRegKey failed", __FUNCTION__);
533 return dwResult;
534 }
535
536 while (iNumAttempts > 0)
537 {
538 /* Query the NetCfgInstanceId value. Using get_reg_string() right on might clutter the
539 * output with error messages while the registry is still being populated. */
540 LPWSTR szCfgGuidString = NULL;
541 dwResult = RegQueryValueEx(hKey, L"NetCfgInstanceId", NULL, NULL, NULL, NULL);
542 if (dwResult != ERROR_SUCCESS)
543 {
544 if (dwResult == ERROR_FILE_NOT_FOUND && --iNumAttempts > 0)
545 {
546 /* Wait and retry. */
547 Sleep(1000);
548 continue;
549 }
550
551 SetLastError(
552 dwResult); /* MSDN does not mention RegQueryValueEx() to set GetLastError(). But we
553 do have an error code. Set last error manually. */
554 msg(M_NONFATAL | M_ERRNO, "%s: querying \"NetCfgInstanceId\" registry value failed",
555 __FUNCTION__);
556 break;
557 }
558
559 /* Read the NetCfgInstanceId value now. */
560 dwResult = get_reg_string(hKey, L"NetCfgInstanceId", &szCfgGuidString);
561 if (dwResult != ERROR_SUCCESS)
562 {
563 break;
564 }
565
566 dwResult = SUCCEEDED(CLSIDFromString(szCfgGuidString, (LPCLSID)pguidAdapter))
567 ? ERROR_SUCCESS
568 : ERROR_INVALID_DATA;
569 free(szCfgGuidString);
570 break;
571 }
572
573 RegCloseKey(hKey);
574 return dwResult;
575}
576
577
599static DWORD
600get_device_reg_property(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData,
601 _In_ DWORD dwProperty, _Out_opt_ LPDWORD pdwPropertyRegDataType,
602 _Out_ LPVOID *ppData)
603{
604 DWORD dwResult = ERROR_BAD_ARGUMENTS;
605
606 if (ppData == NULL)
607 {
608 return ERROR_BAD_ARGUMENTS;
609 }
610
611 /* Try with stack buffer first. */
612 BYTE bBufStack[128];
613 DWORD dwRequiredSize = 0;
614 if (SetupDiGetDeviceRegistryProperty(hDeviceInfoSet, pDeviceInfoData, dwProperty,
615 pdwPropertyRegDataType, bBufStack, sizeof(bBufStack),
616 &dwRequiredSize))
617 {
618 /* Copy from stack. */
619 *ppData = malloc(dwRequiredSize);
620 if (*ppData == NULL)
621 {
622 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwRequiredSize);
623 return ERROR_OUTOFMEMORY;
624 }
625
626 memcpy(*ppData, bBufStack, dwRequiredSize);
627 return ERROR_SUCCESS;
628 }
629 else
630 {
631 dwResult = GetLastError();
632 if (dwResult == ERROR_INSUFFICIENT_BUFFER)
633 {
634 /* Allocate on heap and retry. */
635 *ppData = malloc(dwRequiredSize);
636 if (*ppData == NULL)
637 {
638 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwRequiredSize);
639 return ERROR_OUTOFMEMORY;
640 }
641
642 if (SetupDiGetDeviceRegistryProperty(hDeviceInfoSet, pDeviceInfoData, dwProperty,
643 pdwPropertyRegDataType, *ppData, dwRequiredSize,
644 &dwRequiredSize))
645 {
646 return ERROR_SUCCESS;
647 }
648 else
649 {
650 dwResult = GetLastError();
651 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceRegistryProperty(%u) failed",
652 __FUNCTION__, dwProperty);
653 return dwResult;
654 }
655 }
656 else
657 {
658 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiGetDeviceRegistryProperty(%u) failed",
659 __FUNCTION__, dwProperty);
660 return dwResult;
661 }
662 }
663}
664
665
666DWORD
667tap_create_adapter(_In_opt_ HWND hwndParent, _In_opt_ LPCWSTR szDeviceDescription,
668 _In_ LPCWSTR szHwId, _Inout_ LPBOOL pbRebootRequired, _Out_ LPGUID pguidAdapter)
669{
670 DWORD dwResult;
671 HMODULE libnewdev = NULL;
672
673 if (szHwId == NULL || pbRebootRequired == NULL || pguidAdapter == NULL)
674 {
675 return ERROR_BAD_ARGUMENTS;
676 }
677
678 /* Create an empty device info set for network adapter device class. */
679 HDEVINFO hDevInfoList = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_NET, hwndParent);
680 if (hDevInfoList == INVALID_HANDLE_VALUE)
681 {
682 dwResult = GetLastError();
683 msg(M_NONFATAL, "%s: SetupDiCreateDeviceInfoList failed", __FUNCTION__);
684 return dwResult;
685 }
686
687 /* Get the device class name from GUID. */
688 WCHAR szClassName[MAX_CLASS_NAME_LEN];
689 if (!SetupDiClassNameFromGuid(&GUID_DEVCLASS_NET, szClassName, _countof(szClassName), NULL))
690 {
691 dwResult = GetLastError();
692 msg(M_NONFATAL, "%s: SetupDiClassNameFromGuid failed", __FUNCTION__);
693 goto cleanup_hDevInfoList;
694 }
695
696 /* Create a new device info element and add it to the device info set. */
697 SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
698 if (!SetupDiCreateDeviceInfo(hDevInfoList, szClassName, &GUID_DEVCLASS_NET, szDeviceDescription,
699 hwndParent, DICD_GENERATE_ID, &devinfo_data))
700 {
701 dwResult = GetLastError();
702 msg(M_NONFATAL, "%s: SetupDiCreateDeviceInfo failed", __FUNCTION__);
703 goto cleanup_hDevInfoList;
704 }
705
706 /* Set a device information element as the selected member of a device information set. */
707 if (!SetupDiSetSelectedDevice(hDevInfoList, &devinfo_data))
708 {
709 dwResult = GetLastError();
710 msg(M_NONFATAL, "%s: SetupDiSetSelectedDevice failed", __FUNCTION__);
711 goto cleanup_hDevInfoList;
712 }
713
714 /* Set Plug&Play device hardware ID property. */
715 if (!SetupDiSetDeviceRegistryProperty(hDevInfoList, &devinfo_data, SPDRP_HARDWAREID,
716 (const BYTE *)szHwId,
717 (DWORD)((wcslen(szHwId) + 1) * sizeof(WCHAR))))
718 {
719 dwResult = GetLastError();
720 msg(M_NONFATAL, "%s: SetupDiSetDeviceRegistryProperty failed", __FUNCTION__);
721 goto cleanup_hDevInfoList;
722 }
723
724 /* Register the device instance with the PnP Manager */
725 if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, hDevInfoList, &devinfo_data))
726 {
727 dwResult = GetLastError();
728 msg(M_NONFATAL, "%s: SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed", __FUNCTION__);
729 goto cleanup_hDevInfoList;
730 }
731
732 /* Install the device using DiInstallDevice()
733 * We instruct the system to use the best driver in the driver store
734 * by setting the drvinfo argument of DiInstallDevice as NULL. This
735 * assumes a driver is already installed in the driver store.
736 */
737#ifdef HAVE_DIINSTALLDEVICE
738 if (!DiInstallDevice(hwndParent, hDevInfoList, &devinfo_data, NULL, 0, pbRebootRequired))
739#else
740 /* mingw does not resolve DiInstallDevice, so load it at run time. */
741 typedef BOOL(WINAPI * DiInstallDeviceFn)(HWND, HDEVINFO, SP_DEVINFO_DATA *, SP_DRVINFO_DATA *,
742 DWORD, BOOL *);
743 DiInstallDeviceFn installfn = find_function(L"newdev.dll", "DiInstallDevice", &libnewdev);
744
745 if (!installfn)
746 {
747 dwResult = GetLastError();
748 msg(M_NONFATAL | M_ERRNO, "%s: Failed to locate DiInstallDevice()", __FUNCTION__);
749 goto cleanup_hDevInfoList;
750 }
751
752 if (!installfn(hwndParent, hDevInfoList, &devinfo_data, NULL, 0, pbRebootRequired))
753#endif
754 {
755 dwResult = GetLastError();
756 msg(M_NONFATAL | M_ERRNO, "%s: DiInstallDevice failed", __FUNCTION__);
757 goto cleanup_remove_device;
758 }
759
760 /* Get network adapter ID from registry. Retry for max 30sec. */
761 dwResult = get_net_adapter_guid(hDevInfoList, &devinfo_data, 30, pguidAdapter);
762
763cleanup_remove_device:
764 if (dwResult != ERROR_SUCCESS)
765 {
766 /* The adapter was installed. But, the adapter ID was unobtainable. Clean-up. */
767 SP_REMOVEDEVICE_PARAMS removedevice_params = {
768 .ClassInstallHeader = {
769 .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
770 .InstallFunction = DIF_REMOVE,
771 },
772 .Scope = DI_REMOVEDEVICE_GLOBAL,
773 .HwProfile = 0,
774 };
775
776 /* Set class installer parameters for DIF_REMOVE. */
777 if (SetupDiSetClassInstallParams(hDevInfoList, &devinfo_data,
778 &removedevice_params.ClassInstallHeader,
779 sizeof(SP_REMOVEDEVICE_PARAMS)))
780 {
781 /* Call appropriate class installer. */
782 if (SetupDiCallClassInstaller(DIF_REMOVE, hDevInfoList, &devinfo_data))
783 {
784 /* Check if a system reboot is required. */
785 check_reboot(hDevInfoList, &devinfo_data, pbRebootRequired);
786 }
787 else
788 {
789 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiCallClassInstaller(DIF_REMOVE) failed",
790 __FUNCTION__);
791 }
792 }
793 else
794 {
795 msg(M_NONFATAL | M_ERRNO, "%s: SetupDiSetClassInstallParams failed", __FUNCTION__);
796 }
797 }
798
799cleanup_hDevInfoList:
800 if (libnewdev)
801 {
802 FreeLibrary(libnewdev);
803 }
804 SetupDiDestroyDeviceInfoList(hDevInfoList);
805 return dwResult;
806}
807
808
829static DWORD
830execute_on_first_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter,
831 _In_ devop_func_t funcOperation, _Inout_ LPBOOL pbRebootRequired)
832{
833 DWORD dwResult;
834
835 if (pguidAdapter == NULL)
836 {
837 return ERROR_BAD_ARGUMENTS;
838 }
839
840 /* Create a list of network devices. */
841 HDEVINFO hDevInfoList = SetupDiGetClassDevsEx(&GUID_DEVCLASS_NET, NULL, hwndParent,
842 DIGCF_PRESENT, NULL, NULL, NULL);
843 if (hDevInfoList == INVALID_HANDLE_VALUE)
844 {
845 dwResult = GetLastError();
846 msg(M_NONFATAL, "%s: SetupDiGetClassDevsEx failed", __FUNCTION__);
847 return dwResult;
848 }
849
850 /* Retrieve information associated with a device information set. */
851 SP_DEVINFO_LIST_DETAIL_DATA devinfo_list_detail_data = { .cbSize = sizeof(
852 SP_DEVINFO_LIST_DETAIL_DATA) };
853 if (!SetupDiGetDeviceInfoListDetail(hDevInfoList, &devinfo_list_detail_data))
854 {
855 dwResult = GetLastError();
856 msg(M_NONFATAL, "%s: SetupDiGetDeviceInfoListDetail failed", __FUNCTION__);
857 goto cleanup_hDevInfoList;
858 }
859
860 /* Iterate. */
861 for (DWORD dwIndex = 0;; dwIndex++)
862 {
863 /* Get the device from the list. */
864 SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
865 if (!SetupDiEnumDeviceInfo(hDevInfoList, dwIndex, &devinfo_data))
866 {
867 if (GetLastError() == ERROR_NO_MORE_ITEMS)
868 {
869 LPOLESTR szAdapterId = NULL;
870 StringFromIID((REFIID)pguidAdapter, &szAdapterId);
871 msg(M_NONFATAL, "%s: Adapter %ls not found", __FUNCTION__, szAdapterId);
872 CoTaskMemFree(szAdapterId);
873 dwResult = ERROR_FILE_NOT_FOUND;
874 goto cleanup_hDevInfoList;
875 }
876 else
877 {
878 /* Something is wrong with this device. Skip it. */
879 msg(M_WARN | M_ERRNO, "%s: SetupDiEnumDeviceInfo(%u) failed", __FUNCTION__,
880 dwIndex);
881 continue;
882 }
883 }
884
885 /* Get adapter GUID. */
886 GUID guidAdapter;
887 dwResult = get_net_adapter_guid(hDevInfoList, &devinfo_data, 1, &guidAdapter);
888 if (dwResult != ERROR_SUCCESS)
889 {
890 /* Something is wrong with this device. Skip it. */
891 continue;
892 }
893
894 /* Compare GUIDs. */
895 if (memcmp(pguidAdapter, &guidAdapter, sizeof(GUID)) == 0)
896 {
897 dwResult = funcOperation(hDevInfoList, &devinfo_data, pbRebootRequired);
898 break;
899 }
900 }
901
902cleanup_hDevInfoList:
903 SetupDiDestroyDeviceInfoList(hDevInfoList);
904 return dwResult;
905}
906
907
908DWORD
909tap_delete_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter,
910 _Inout_ LPBOOL pbRebootRequired)
911{
912 return execute_on_first_adapter(hwndParent, pguidAdapter, delete_device, pbRebootRequired);
913}
914
915
916DWORD
917tap_enable_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _In_ BOOL bEnable,
918 _Inout_ LPBOOL pbRebootRequired)
919{
920 return execute_on_first_adapter(hwndParent, pguidAdapter,
921 bEnable ? enable_device : disable_device, pbRebootRequired);
922}
923
924/* stripped version of ExecCommand in interactive.c */
925static DWORD
926ExecCommand(const WCHAR *cmdline)
927{
928 DWORD exit_code;
929 STARTUPINFOW si;
930 PROCESS_INFORMATION pi;
931 DWORD proc_flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
932 WCHAR *cmdline_dup = NULL;
933
934 ZeroMemory(&si, sizeof(si));
935 ZeroMemory(&pi, sizeof(pi));
936
937 si.cb = sizeof(si);
938
939 /* CreateProcess needs a modifiable cmdline: make a copy */
940 cmdline_dup = _wcsdup(cmdline);
941 if (cmdline_dup
942 && CreateProcessW(NULL, cmdline_dup, NULL, NULL, FALSE, proc_flags, NULL, NULL, &si, &pi))
943 {
944 WaitForSingleObject(pi.hProcess, INFINITE);
945 if (!GetExitCodeProcess(pi.hProcess, &exit_code))
946 {
947 exit_code = GetLastError();
948 }
949
950 CloseHandle(pi.hProcess);
951 CloseHandle(pi.hThread);
952 }
953 else
954 {
955 exit_code = GetLastError();
956 }
957
958 free(cmdline_dup);
959 return exit_code;
960}
961
962DWORD
963tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCWSTR szName, _In_ BOOL bSilent)
964{
965 DWORD dwResult;
966 int msg_flag = bSilent ? M_WARN : M_NONFATAL;
967 msg_flag |= M_ERRNO;
968
969 if (pguidAdapter == NULL || szName == NULL)
970 {
971 return ERROR_BAD_ARGUMENTS;
972 }
973
974 /* Get the device class GUID as string. */
975 LPOLESTR szDevClassNetId = NULL;
976 StringFromIID((REFIID)&GUID_DEVCLASS_NET, &szDevClassNetId);
977
978 /* Get the adapter GUID as string. */
979 LPOLESTR szAdapterId = NULL;
980 StringFromIID((REFIID)pguidAdapter, &szAdapterId);
981
982 /* Render registry key path. */
983 WCHAR szRegKey[ADAPTER_REGKEY_PATH_MAX];
984 swprintf_s(szRegKey, _countof(szRegKey), szAdapterRegKeyPathTemplate, szDevClassNetId,
985 szAdapterId);
986
987 /* Open network adapter registry key. */
988 HKEY hKey = NULL;
989 dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_QUERY_VALUE, &hKey);
990 if (dwResult != ERROR_SUCCESS)
991 {
992 SetLastError(dwResult); /* MSDN does not mention RegOpenKeyEx() to set GetLastError(). But
993 we do have an error code. Set last error manually. */
994 msg(msg_flag, "%s: RegOpenKeyEx(HKLM, \"%ls\") failed", __FUNCTION__, szRegKey);
995 goto cleanup_szAdapterId;
996 }
997
998 LPWSTR szOldName = NULL;
999 dwResult = get_reg_string(hKey, L"Name", &szOldName);
1000 if (dwResult != ERROR_SUCCESS)
1001 {
1002 SetLastError(dwResult);
1003 msg(msg_flag, "%s: Error reading adapter name", __FUNCTION__);
1004 goto cleanup_hKey;
1005 }
1006
1007 /* rename adapter via netsh call */
1008 const WCHAR *szFmt = L"netsh interface set interface name=\"%"
1009 L"ls\" newname=\"%ls\"";
1010 size_t ncmdline = wcslen(szFmt) + wcslen(szOldName) + wcslen(szName) + 1;
1011 WCHAR *szCmdLine = malloc(ncmdline * sizeof(WCHAR));
1012 swprintf_s(szCmdLine, ncmdline, szFmt, szOldName, szName);
1013
1014 free(szOldName);
1015
1016 dwResult = ExecCommand(szCmdLine);
1017 free(szCmdLine);
1018
1019 if (dwResult != ERROR_SUCCESS)
1020 {
1021 SetLastError(dwResult);
1022 msg(msg_flag, "%s: Error renaming adapter", __FUNCTION__);
1023 goto cleanup_hKey;
1024 }
1025
1026cleanup_hKey:
1027 RegCloseKey(hKey);
1028cleanup_szAdapterId:
1029 CoTaskMemFree(szAdapterId);
1030 CoTaskMemFree(szDevClassNetId);
1031 return dwResult;
1032}
1033
1034
1035DWORD
1036tap_list_adapters(_In_opt_ HWND hwndParent, _In_opt_ LPCWSTR szzHwIDs,
1037 _Out_ struct tap_adapter_node **ppAdapter)
1038{
1039 DWORD dwResult;
1040
1041 if (ppAdapter == NULL)
1042 {
1043 return ERROR_BAD_ARGUMENTS;
1044 }
1045
1046 /* Create a list of network devices. */
1047 HDEVINFO hDevInfoList = SetupDiGetClassDevsEx(&GUID_DEVCLASS_NET, NULL, hwndParent,
1048 DIGCF_PRESENT, NULL, NULL, NULL);
1049 if (hDevInfoList == INVALID_HANDLE_VALUE)
1050 {
1051 dwResult = GetLastError();
1052 msg(M_NONFATAL, "%s: SetupDiGetClassDevsEx failed", __FUNCTION__);
1053 return dwResult;
1054 }
1055
1056 /* Retrieve information associated with a device information set. */
1057 SP_DEVINFO_LIST_DETAIL_DATA devinfo_list_detail_data = { .cbSize = sizeof(
1058 SP_DEVINFO_LIST_DETAIL_DATA) };
1059 if (!SetupDiGetDeviceInfoListDetail(hDevInfoList, &devinfo_list_detail_data))
1060 {
1061 dwResult = GetLastError();
1062 msg(M_NONFATAL, "%s: SetupDiGetDeviceInfoListDetail failed", __FUNCTION__);
1063 goto cleanup_hDevInfoList;
1064 }
1065
1066 /* Get the device class GUID as string. */
1067 LPOLESTR szDevClassNetId = NULL;
1068 StringFromIID((REFIID)&GUID_DEVCLASS_NET, &szDevClassNetId);
1069
1070 /* Iterate. */
1071 *ppAdapter = NULL;
1072 struct tap_adapter_node *pAdapterTail = NULL;
1073 for (DWORD dwIndex = 0;; dwIndex++)
1074 {
1075 /* Get the device from the list. */
1076 SP_DEVINFO_DATA devinfo_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
1077 if (!SetupDiEnumDeviceInfo(hDevInfoList, dwIndex, &devinfo_data))
1078 {
1079 if (GetLastError() == ERROR_NO_MORE_ITEMS)
1080 {
1081 break;
1082 }
1083 else
1084 {
1085 /* Something is wrong with this device. Skip it. */
1086 msg(M_WARN | M_ERRNO, "%s: SetupDiEnumDeviceInfo(%u) failed", __FUNCTION__,
1087 dwIndex);
1088 continue;
1089 }
1090 }
1091
1092 /* Get device hardware ID(s). */
1093 DWORD dwDataType = REG_NONE;
1094 LPWSTR szzDeviceHardwareIDs = NULL;
1095 dwResult = get_device_reg_property(hDevInfoList, &devinfo_data, SPDRP_HARDWAREID,
1096 &dwDataType, (LPVOID)&szzDeviceHardwareIDs);
1097 if (dwResult != ERROR_SUCCESS)
1098 {
1099 /* Something is wrong with this device. Skip it. */
1100 continue;
1101 }
1102
1103 /* Check that hardware ID is REG_SZ/REG_MULTI_SZ, and optionally if it matches ours. */
1104 if (dwDataType == REG_SZ)
1105 {
1106 if (szzHwIDs && !wcszistr(szzHwIDs, szzDeviceHardwareIDs))
1107 {
1108 /* This is not our device. Skip it. */
1109 goto cleanup_szzDeviceHardwareIDs;
1110 }
1111 }
1112 else if (dwDataType == REG_MULTI_SZ)
1113 {
1114 if (szzHwIDs)
1115 {
1116 for (LPWSTR s = szzDeviceHardwareIDs;; s += wcslen(s) + 1)
1117 {
1118 if (s[0] == 0)
1119 {
1120 /* This is not our device. Skip it. */
1121 goto cleanup_szzDeviceHardwareIDs;
1122 }
1123 else if (wcszistr(szzHwIDs, s))
1124 {
1125 /* This is our device. */
1126 break;
1127 }
1128 }
1129 }
1130 }
1131 else
1132 {
1133 /* Unexpected hardware ID format. Skip device. */
1134 goto cleanup_szzDeviceHardwareIDs;
1135 }
1136
1137 /* Get adapter GUID. */
1138 GUID guidAdapter;
1139 dwResult = get_net_adapter_guid(hDevInfoList, &devinfo_data, 1, &guidAdapter);
1140 if (dwResult != ERROR_SUCCESS)
1141 {
1142 /* Something is wrong with this device. Skip it. */
1143 goto cleanup_szzDeviceHardwareIDs;
1144 }
1145
1146 /* Get the adapter GUID as string. */
1147 LPOLESTR szAdapterId = NULL;
1148 StringFromIID((REFIID)&guidAdapter, &szAdapterId);
1149
1150 /* Render registry key path. */
1151 WCHAR szRegKey[ADAPTER_REGKEY_PATH_MAX];
1152 swprintf_s(szRegKey, _countof(szRegKey), szAdapterRegKeyPathTemplate, szDevClassNetId,
1153 szAdapterId);
1154
1155 /* Open network adapter registry key. */
1156 HKEY hKey = NULL;
1157 dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_READ, &hKey);
1158 if (dwResult != ERROR_SUCCESS)
1159 {
1160 SetLastError(dwResult); /* MSDN does not mention RegOpenKeyEx() to set GetLastError().
1161 But we do have an error code. Set last error manually. */
1162 msg(M_WARN | M_ERRNO, "%s: RegOpenKeyEx(HKLM, \"%ls\") failed", __FUNCTION__, szRegKey);
1163 goto cleanup_szAdapterId;
1164 }
1165
1166 /* Read adapter name. */
1167 LPWSTR szName = NULL;
1168 dwResult = get_reg_string(hKey, L"Name", &szName);
1169 if (dwResult != ERROR_SUCCESS)
1170 {
1171 SetLastError(dwResult);
1172 msg(M_WARN | M_ERRNO, "%s: Cannot determine %ls adapter name", __FUNCTION__,
1173 szAdapterId);
1174 goto cleanup_hKey;
1175 }
1176
1177 /* Append to the list. */
1178 size_t hwid_size = (wcszlen(szzDeviceHardwareIDs) + 1) * sizeof(WCHAR);
1179 size_t name_size = (wcslen(szName) + 1) * sizeof(WCHAR);
1180 struct tap_adapter_node *node = (struct tap_adapter_node *)malloc(
1181 sizeof(struct tap_adapter_node) + hwid_size + name_size);
1182 if (node == NULL)
1183 {
1184 msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__,
1185 sizeof(struct tap_adapter_node) + hwid_size + name_size);
1186 dwResult = ERROR_OUTOFMEMORY;
1187 goto cleanup_szName;
1188 }
1189
1190 memcpy(&node->guid, &guidAdapter, sizeof(GUID));
1191 node->szzHardwareIDs = (LPWSTR)(node + 1);
1192 memcpy(node->szzHardwareIDs, szzDeviceHardwareIDs, hwid_size);
1193 node->szName = (LPWSTR)((LPBYTE)node->szzHardwareIDs + hwid_size);
1194 memcpy(node->szName, szName, name_size);
1195 node->pNext = NULL;
1196 if (pAdapterTail)
1197 {
1198 pAdapterTail->pNext = node;
1199 pAdapterTail = node;
1200 }
1201 else
1202 {
1203 *ppAdapter = pAdapterTail = node;
1204 }
1205
1206cleanup_szName:
1207 free(szName);
1208cleanup_hKey:
1209 RegCloseKey(hKey);
1210cleanup_szAdapterId:
1211 CoTaskMemFree(szAdapterId);
1212cleanup_szzDeviceHardwareIDs:
1213 free(szzDeviceHardwareIDs);
1214 }
1215
1216 dwResult = ERROR_SUCCESS;
1217
1218 CoTaskMemFree(szDevClassNetId);
1219cleanup_hDevInfoList:
1220 SetupDiDestroyDeviceInfoList(hDevInfoList);
1221 return dwResult;
1222}
1223
1224
1225void
1227{
1228 /* Iterate over all nodes of the list. */
1229 while (pAdapterList)
1230 {
1231 struct tap_adapter_node *node = pAdapterList;
1232 pAdapterList = pAdapterList->pNext;
1233
1234 /* Free the adapter node. */
1235 free(node);
1236 }
1237}
#define M_FATAL
Definition error.h:88
#define M_NONFATAL
Definition error.h:89
#define msg(flags,...)
Definition error.h:150
#define M_WARN
Definition error.h:90
#define M_ERRNO
Definition error.h:93
Network adapter list node.
Definition tap.h:124
LPWSTR szzHardwareIDs
Device hardware ID(s)
Definition tap.h:126
struct tap_adapter_node * pNext
Pointer to next adapter.
Definition tap.h:129
LPWSTR szName
Adapter name.
Definition tap.h:127
GUID guid
Adapter GUID.
Definition tap.h:125
DWORD tap_list_adapters(_In_opt_ HWND hwndParent, _In_opt_ LPCWSTR szzHwIDs, _Out_ struct tap_adapter_node **ppAdapter)
Creates a list of existing network adapters.
Definition tap.c:1036
static DWORD ExecCommand(const WCHAR *cmdline)
Definition tap.c:926
static DWORD enable_device(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _Inout_ LPBOOL pbRebootRequired)
Enables the device.
Definition tap.c:333
static LPCWSTR wcszistr(_In_z_ LPCWSTR szzHay, _In_z_ LPCWSTR szNeedle)
Checks if string is contained in the string of strings.
Definition tap.c:132
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:830
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:162
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:516
static DWORD disable_device(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _Inout_ LPBOOL pbRebootRequired)
Disables the device.
Definition tap.c:357
DWORD tap_set_adapter_name(_In_ LPCGUID pguidAdapter, _In_ LPCWSTR szName, _In_ BOOL bSilent)
Sets adapter name.
Definition tap.c:963
static const WCHAR szAdapterRegKeyPathTemplate[]
Definition tap.c:47
#define ADAPTER_REGKEY_PATH_MAX
Definition tap.c:49
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:600
static size_t wcszlen(_In_z_ LPCWSTR szz)
Returns length of string of strings.
Definition tap.c:111
DWORD tap_create_adapter(_In_opt_ HWND hwndParent, _In_opt_ LPCWSTR szDeviceDescription, _In_ LPCWSTR szHwId, _Inout_ LPBOOL pbRebootRequired, _Out_ LPGUID pguidAdapter)
Creates a TUN/TAP adapter.
Definition tap.c:667
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:67
DWORD tap_delete_adapter(_In_opt_ HWND hwndParent, _In_ LPCGUID pguidAdapter, _Inout_ LPBOOL pbRebootRequired)
Deletes an adapter.
Definition tap.c:909
static DWORD delete_device(_In_ HDEVINFO hDeviceInfoSet, _In_ PSP_DEVINFO_DATA pDeviceInfoData, _Inout_ LPBOOL pbRebootRequired)
Deletes the device.
Definition tap.c:225
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
void tap_free_adapter_list(_In_ struct tap_adapter_node *pAdapterList)
Frees a list of network adapters.
Definition tap.c:1226
static const GUID GUID_DEVCLASS_NET
Definition tap.c:43
static DWORD get_reg_string(_In_ HKEY hKey, _In_ LPCWSTR szName, _Out_ LPWSTR *pszValue)
Reads string value from registry key.
Definition tap.c:379
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:917
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:279
#define _Out_opt_
Definition basic.h:59
#define _Inout_
Definition basic.h:50
#define _In_z_
Definition basic.h:47
#define _Out_
Definition basic.h:56
#define _In_
Definition basic.h:41
#define _In_opt_
Definition basic.h:44