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