wintun/api/adapter.c
Simon Rozman 660a61b865 api: use catalog file for signing certificate extraction
The .sys file of the driver does not need to be digitally signed. It is
the .cat file that Windows is checking for complete driver .inf+.sys+
.cat bundle.

Signed-off-by: Simon Rozman <simon@rozman.si>
2020-10-31 10:15:15 +01:00

1685 lines
60 KiB
C

/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
*/
#include "pch.h"
#pragma warning(disable : 4221) /* nonstandard: address of automatic in initializer */
#define WAIT_FOR_REGISTRY_TIMEOUT 10000 /* ms */
#define MAX_POOL_DEVICE_TYPE (MAX_POOL + 8) /* Should accommodate a pool name with " Tunnel" appended */
#if defined(_M_IX86)
# define IMAGE_FILE_PROCESS IMAGE_FILE_MACHINE_I386
#elif defined(_M_AMD64)
# define IMAGE_FILE_PROCESS IMAGE_FILE_MACHINE_AMD64
#elif defined(_M_ARM)
# define IMAGE_FILE_PROCESS IMAGE_FILE_MACHINE_ARMNT
#elif defined(_M_ARM64)
# define IMAGE_FILE_PROCESS IMAGE_FILE_MACHINE_ARM64
#else
# error Unsupported architecture
#endif
static _locale_t Locale;
static USHORT NativeMachine = IMAGE_FILE_PROCESS;
WINTUN_STATUS
AdapterGetDrvInfoDetail(
_In_ HDEVINFO DevInfo,
_In_opt_ SP_DEVINFO_DATA *DevInfoData,
_In_ SP_DRVINFO_DATA_W *DrvInfoData,
_Out_ SP_DRVINFO_DETAIL_DATA_W **DrvInfoDetailData)
{
DWORD Size = sizeof(SP_DRVINFO_DETAIL_DATA_W) + 0x100;
for (;;)
{
*DrvInfoDetailData = HeapAlloc(ModuleHeap, 0, Size);
if (!*DrvInfoDetailData)
return LOG(WINTUN_LOG_ERR, L"Out of memory"), ERROR_OUTOFMEMORY;
(*DrvInfoDetailData)->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_W);
if (SetupDiGetDriverInfoDetailW(DevInfo, DevInfoData, DrvInfoData, *DrvInfoDetailData, Size, &Size))
return ERROR_SUCCESS;
DWORD Result = GetLastError();
HeapFree(ModuleHeap, 0, *DrvInfoDetailData);
if (Result != ERROR_INSUFFICIENT_BUFFER)
return LOG_ERROR(L"Failed", Result);
}
}
static WINTUN_STATUS
GetDeviceRegistryProperty(
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_In_ DWORD Property,
_Out_opt_ DWORD *ValueType,
_Out_ void **Buf,
_Inout_ DWORD *BufLen)
{
for (;;)
{
*Buf = HeapAlloc(ModuleHeap, 0, *BufLen);
if (!*Buf)
return LOG(WINTUN_LOG_ERR, L"Out of memory"), ERROR_OUTOFMEMORY;
if (SetupDiGetDeviceRegistryPropertyW(DevInfo, DevInfoData, Property, ValueType, *Buf, *BufLen, BufLen))
return ERROR_SUCCESS;
DWORD Result = GetLastError();
HeapFree(ModuleHeap, 0, *Buf);
if (Result != ERROR_INSUFFICIENT_BUFFER)
return LOG_ERROR(L"Querying property failed", Result);
}
}
static WINTUN_STATUS
GetDeviceRegistryString(
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_In_ DWORD Property,
_Out_ WCHAR **Buf)
{
DWORD Result, ValueType, Size = 256 * sizeof(WCHAR);
Result = GetDeviceRegistryProperty(DevInfo, DevInfoData, Property, &ValueType, Buf, &Size);
if (Result != ERROR_SUCCESS)
return Result;
switch (ValueType)
{
case REG_SZ:
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
Result = RegistryGetString(Buf, Size / sizeof(WCHAR), ValueType);
if (Result != ERROR_SUCCESS)
HeapFree(ModuleHeap, 0, *Buf);
return Result;
default:
LOG(WINTUN_LOG_ERR, L"Property is not a string");
HeapFree(ModuleHeap, 0, *Buf);
return ERROR_INVALID_DATATYPE;
}
}
static WINTUN_STATUS
GetDeviceRegistryMultiString(
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_In_ DWORD Property,
_Out_ WCHAR **Buf)
{
DWORD Result, ValueType, Size = 256 * sizeof(WCHAR);
Result = GetDeviceRegistryProperty(DevInfo, DevInfoData, Property, &ValueType, Buf, &Size);
if (Result != ERROR_SUCCESS)
return Result;
switch (ValueType)
{
case REG_SZ:
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
Result = RegistryGetMultiString(Buf, Size / sizeof(WCHAR), ValueType);
if (Result != ERROR_SUCCESS)
HeapFree(ModuleHeap, 0, *Buf);
return Result;
default:
LOG(WINTUN_LOG_ERR, L"Property is not a string");
HeapFree(ModuleHeap, 0, *Buf);
return ERROR_INVALID_DATATYPE;
}
}
static WINTUN_STATUS
IsOurAdapter(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData, _Out_ BOOL *IsOurs)
{
WCHAR *Hwids;
DWORD Result = GetDeviceRegistryMultiString(DevInfo, DevInfoData, SPDRP_HARDWAREID, &Hwids);
if (Result != ERROR_SUCCESS)
return LOG(WINTUN_LOG_ERR, L"Failed to query hardware ID"), Result;
*IsOurs = DriverIsOurHardwareID(Hwids);
return ERROR_SUCCESS;
}
static WINTUN_STATUS
GetDeviceObject(_In_opt_z_ const WCHAR *InstanceId, _Out_ HANDLE *Handle)
{
ULONG InterfacesLen;
DWORD Result = CM_Get_Device_Interface_List_SizeW(
&InterfacesLen, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)InstanceId, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (Result != CR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to get device associated device instances size");
return ERROR_GEN_FAILURE;
}
WCHAR *Interfaces = HeapAlloc(ModuleHeap, 0, InterfacesLen * sizeof(WCHAR));
if (!Interfaces)
return LOG(WINTUN_LOG_ERR, L"Out of memory"), ERROR_OUTOFMEMORY;
Result = CM_Get_Device_Interface_ListW(
(GUID *)&GUID_DEVINTERFACE_NET,
(DEVINSTID_W)InstanceId,
Interfaces,
InterfacesLen,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (Result != CR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to get device associated device instances");
Result = ERROR_GEN_FAILURE;
goto cleanupBuf;
}
*Handle = CreateFileW(
Interfaces,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
0,
NULL);
Result = *Handle != INVALID_HANDLE_VALUE ? ERROR_SUCCESS : LOG_LAST_ERROR(L"Failed to connect to device");
cleanupBuf:
HeapFree(ModuleHeap, 0, Interfaces);
return Result;
}
#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
static WINTUN_STATUS
ForceCloseWintunAdapterHandle(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData)
{
DWORD Result = ERROR_SUCCESS;
DWORD RequiredBytes;
if (SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, NULL, 0, &RequiredBytes) ||
(Result = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
return LOG_ERROR(L"Failed to query device instance ID size", Result);
WCHAR *InstanceId = HeapAlloc(ModuleHeap, HEAP_ZERO_MEMORY, sizeof(*InstanceId) * RequiredBytes);
if (!InstanceId)
return LOG(WINTUN_LOG_ERR, L"Out of memory"), ERROR_OUTOFMEMORY;
if (!SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, InstanceId, RequiredBytes, &RequiredBytes))
{
Result = LOG_LAST_ERROR(L"Failed to get device instance ID");
goto out;
}
HANDLE NdisHandle;
Result = GetDeviceObject(InstanceId, &NdisHandle);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to get adapter device object");
goto out;
}
Result = DeviceIoControl(NdisHandle, TUN_IOCTL_FORCE_CLOSE_HANDLES, NULL, 0, NULL, 0, &RequiredBytes, NULL)
? ERROR_SUCCESS
: LOG_LAST_ERROR(L"Failed to perform ioctl");
CloseHandle(NdisHandle);
out:
HeapFree(ModuleHeap, 0, InstanceId);
return Result;
}
WINTUN_STATUS
AdapterDisableAllOurs(_In_ HDEVINFO DevInfo, _Inout_ SP_DEVINFO_DATA_LIST **DisabledAdapters)
{
SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
.InstallFunction = DIF_PROPERTYCHANGE },
.StateChange = DICS_DISABLE,
.Scope = DICS_FLAG_GLOBAL };
DWORD Result = ERROR_SUCCESS;
for (DWORD EnumIndex = 0;; ++EnumIndex)
{
SP_DEVINFO_DATA_LIST *DeviceNode = HeapAlloc(ModuleHeap, 0, sizeof(SP_DEVINFO_DATA_LIST));
if (!DeviceNode)
return LOG(WINTUN_LOG_ERR, L"Out of memory"), ERROR_OUTOFMEMORY;
DeviceNode->Data.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DeviceNode->Data))
{
if (GetLastError() == ERROR_NO_MORE_ITEMS)
{
HeapFree(ModuleHeap, 0, DeviceNode);
break;
}
goto cleanupDeviceInfoData;
}
BOOL IsOurs;
if (IsOurAdapter(DevInfo, &DeviceNode->Data, &IsOurs) != ERROR_SUCCESS || !IsOurs)
goto cleanupDeviceInfoData;
ULONG Status, ProblemCode;
if (CM_Get_DevNode_Status(&Status, &ProblemCode, DeviceNode->Data.DevInst, 0) != CR_SUCCESS ||
((Status & DN_HAS_PROBLEM) && ProblemCode == CM_PROB_DISABLED))
goto cleanupDeviceInfoData;
LOG(WINTUN_LOG_INFO, L"Force closing all open handles for existing adapter");
if (ForceCloseWintunAdapterHandle(DevInfo, &DeviceNode->Data) != ERROR_SUCCESS)
LOG(WINTUN_LOG_WARN, L"Failed to force close adapter handles");
Sleep(200);
LOG(WINTUN_LOG_INFO, L"Disabling existing adapter");
if (!SetupDiSetClassInstallParamsW(DevInfo, &DeviceNode->Data, &Params.ClassInstallHeader, sizeof(Params)) ||
!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, &DeviceNode->Data))
{
LOG_LAST_ERROR(L"Unable to disable existing adapter");
Result = Result != ERROR_SUCCESS ? Result : GetLastError();
goto cleanupDeviceInfoData;
}
DeviceNode->Next = *DisabledAdapters;
*DisabledAdapters = DeviceNode;
continue;
cleanupDeviceInfoData:
HeapFree(ModuleHeap, 0, &DeviceNode->Data);
}
return Result;
}
WINTUN_STATUS
AdapterEnableAll(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA_LIST *AdaptersToEnable)
{
SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
.InstallFunction = DIF_PROPERTYCHANGE },
.StateChange = DICS_ENABLE,
.Scope = DICS_FLAG_GLOBAL };
DWORD Result = ERROR_SUCCESS;
for (SP_DEVINFO_DATA_LIST *DeviceNode = AdaptersToEnable; DeviceNode; DeviceNode = DeviceNode->Next)
{
LOG(WINTUN_LOG_INFO, L"Enabling existing adapter");
if (!SetupDiSetClassInstallParamsW(DevInfo, &DeviceNode->Data, &Params.ClassInstallHeader, sizeof(Params)) ||
!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, &DeviceNode->Data))
{
LOG_LAST_ERROR(L"Unable to enable existing adapter");
Result = Result != ERROR_SUCCESS ? Result : GetLastError();
}
}
return Result;
}
WINTUN_STATUS
AdapterDeleteAllOurs()
{
DWORD Result = ERROR_SUCCESS;
HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (DevInfo == INVALID_HANDLE_VALUE)
return LOG_LAST_ERROR(L"Failed to get present class devices");
SP_REMOVEDEVICE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
.InstallFunction = DIF_REMOVE },
.Scope = DI_REMOVEDEVICE_GLOBAL };
for (DWORD EnumIndex = 0;; ++EnumIndex)
{
SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(SP_DEVINFO_DATA) };
if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData))
{
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
continue;
}
BOOL IsOurs;
if (IsOurAdapter(DevInfo, &DevInfoData, &IsOurs) != ERROR_SUCCESS || !IsOurs)
continue;
LOG(WINTUN_LOG_INFO, L"Force closing all open handles for existing adapter");
if (ForceCloseWintunAdapterHandle(DevInfo, &DevInfoData) != ERROR_SUCCESS)
LOG(WINTUN_LOG_WARN, L"Failed to force close adapter handles");
Sleep(200);
LOG(WINTUN_LOG_INFO, L"Removing existing adapter");
if (!SetupDiSetClassInstallParamsW(DevInfo, &DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) ||
!SetupDiCallClassInstaller(DIF_REMOVE, DevInfo, &DevInfoData))
{
LOG_LAST_ERROR(L"Unable to remove existing adapter");
Result = Result != ERROR_SUCCESS ? Result : GetLastError();
}
}
SetupDiDestroyDeviceInfoList(DevInfo);
return Result;
}
void
AdapterInit()
{
Locale = _wcreate_locale(LC_ALL, L"");
#if defined(_M_IX86) || defined(_M_ARM)
typedef BOOL(WINAPI * IsWow64Process2_t)(
_In_ HANDLE hProcess, _Out_ USHORT * pProcessMachine, _Out_opt_ USHORT * pNativeMachine);
HANDLE Kernel32;
IsWow64Process2_t IsWow64Process2;
USHORT ProcessMachine;
if ((Kernel32 = GetModuleHandleW(L"kernel32.dll")) == NULL ||
(IsWow64Process2 = (IsWow64Process2_t)GetProcAddress(Kernel32, "IsWow64Process2")) == NULL ||
!IsWow64Process2(GetCurrentProcess(), &ProcessMachine, &NativeMachine))
{
BOOL IsWoW64;
NativeMachine =
IsWow64Process(GetCurrentProcess(), &IsWoW64) && IsWoW64 ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_PROCESS;
}
#endif
}
void
AdapterCleanup()
{
_free_locale(Locale);
}
static BOOL
CheckReboot(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData)
{
SP_DEVINSTALL_PARAMS_W DevInstallParams = { .cbSize = sizeof(SP_DEVINSTALL_PARAMS_W) };
if (!SetupDiGetDeviceInstallParamsW(DevInfo, DevInfoData, &DevInstallParams))
{
LOG_LAST_ERROR(L"Retrieving device installation parameters failed");
return FALSE;
}
return (DevInstallParams.Flags & (DI_NEEDREBOOT | DI_NEEDRESTART)) != 0;
}
static WINTUN_STATUS
SetQuietInstall(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData)
{
SP_DEVINSTALL_PARAMS_W DevInstallParams = { .cbSize = sizeof(SP_DEVINSTALL_PARAMS_W) };
if (!SetupDiGetDeviceInstallParamsW(DevInfo, DevInfoData, &DevInstallParams))
return LOG_LAST_ERROR(L"Retrieving device installation parameters failed");
DevInstallParams.Flags |= DI_QUIETINSTALL;
if (!SetupDiSetDeviceInstallParamsW(DevInfo, DevInfoData, &DevInstallParams))
return LOG_LAST_ERROR(L"Setting device installation parameters failed");
return ERROR_SUCCESS;
}
static WINTUN_STATUS
GetNetCfgInstanceId(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData, _Out_ GUID *CfgInstanceID)
{
HKEY Key = SetupDiOpenDevRegKey(DevInfo, DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
if (Key == INVALID_HANDLE_VALUE)
return LOG_LAST_ERROR(L"Opening device registry key failed");
WCHAR *ValueStr;
DWORD Result = RegistryQueryString(Key, L"NetCfgInstanceId", &ValueStr, TRUE);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query NetCfgInstanceId value");
goto cleanupKey;
}
if (FAILED(CLSIDFromString(ValueStr, CfgInstanceID)))
{
LOG(WINTUN_LOG_ERR, L"NetCfgInstanceId is not a GUID");
Result = ERROR_INVALID_DATA;
}
else
Result = ERROR_SUCCESS;
HeapFree(ModuleHeap, 0, ValueStr);
cleanupKey:
RegCloseKey(Key);
return Result;
}
static WINTUN_STATUS
GetDevInfoData(_In_ const GUID *CfgInstanceID, _Out_ HDEVINFO *DevInfo, _Out_ SP_DEVINFO_DATA *DevInfoData)
{
*DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (!*DevInfo)
return LOG_LAST_ERROR(L"Failed to get present class devices");
for (DWORD EnumIndex = 0;; ++EnumIndex)
{
DevInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(*DevInfo, EnumIndex, DevInfoData))
{
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
continue;
}
GUID CfgInstanceID2;
if (GetNetCfgInstanceId(*DevInfo, DevInfoData, &CfgInstanceID2) == ERROR_SUCCESS &&
!memcmp(CfgInstanceID, &CfgInstanceID2, sizeof(GUID)))
return ERROR_SUCCESS;
}
SetupDiDestroyDeviceInfoList(*DevInfo);
return ERROR_FILE_NOT_FOUND;
}
static void
RemoveNumberedSuffix(_In_z_ const WCHAR *Name, _Out_ WCHAR *Removed)
{
size_t Len = wcslen(Name);
if (Len && (Name[Len - 1] < L'0' || Name[Len - 1] > L'9'))
{
wmemcpy(Removed, Name, Len + 1);
return;
}
for (size_t i = Len; i--;)
{
if (Name[i] >= L'0' && Name[i] <= L'9')
continue;
if (Name[i] == L' ')
{
wmemcpy(Removed, Name, i);
Removed[i] = 0;
return;
}
break;
}
wmemcpy(Removed, Name, Len + 1);
}
static void
GetPoolDeviceTypeName(_In_z_count_c_(MAX_POOL) const WCHAR *Pool, _Out_cap_c_(MAX_POOL_DEVICE_TYPE) WCHAR *Name)
{
_snwprintf_s(Name, MAX_POOL_DEVICE_TYPE, _TRUNCATE, L"%.*s Tunnel", MAX_POOL, Pool);
}
static WINTUN_STATUS
IsPoolMember(
_In_z_count_c_(MAX_POOL) const WCHAR *Pool,
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_Out_ BOOL *IsMember)
{
WCHAR *DeviceDesc, *FriendlyName;
DWORD Result = GetDeviceRegistryString(DevInfo, DevInfoData, SPDRP_DEVICEDESC, &DeviceDesc);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query device description property");
return Result;
}
Result = GetDeviceRegistryString(DevInfo, DevInfoData, SPDRP_FRIENDLYNAME, &FriendlyName);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query friendly name property");
goto cleanupDeviceDesc;
}
WCHAR PoolDeviceTypeName[MAX_POOL_DEVICE_TYPE];
GetPoolDeviceTypeName(Pool, PoolDeviceTypeName);
if (!_wcsicmp_l(FriendlyName, PoolDeviceTypeName, Locale) || !_wcsicmp_l(DeviceDesc, PoolDeviceTypeName, Locale))
{
*IsMember = TRUE;
goto cleanupFriendlyName;
}
RemoveNumberedSuffix(FriendlyName, FriendlyName);
RemoveNumberedSuffix(DeviceDesc, DeviceDesc);
if (!_wcsicmp_l(FriendlyName, PoolDeviceTypeName, Locale) || !_wcsicmp_l(DeviceDesc, PoolDeviceTypeName, Locale))
{
*IsMember = TRUE;
goto cleanupFriendlyName;
}
*IsMember = FALSE;
cleanupFriendlyName:
HeapFree(ModuleHeap, 0, FriendlyName);
cleanupDeviceDesc:
HeapFree(ModuleHeap, 0, DeviceDesc);
return Result;
}
static WINTUN_STATUS
CreateAdapterData(
_In_z_count_c_(MAX_POOL) const WCHAR *Pool,
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_Out_ WINTUN_ADAPTER **Adapter)
{
DWORD Result;
/* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> registry key. */
HKEY Key = SetupDiOpenDevRegKey(DevInfo, DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
if (Key == INVALID_HANDLE_VALUE)
return LOG_LAST_ERROR(L"Opening device registry key failed");
*Adapter = HeapAlloc(ModuleHeap, 0, sizeof(WINTUN_ADAPTER));
if (!*Adapter)
{
LOG(WINTUN_LOG_ERR, L"Out of memory");
Result = ERROR_OUTOFMEMORY;
goto cleanupKey;
}
/* Read the NetCfgInstanceId value and convert to GUID. */
WCHAR *ValueStr;
Result = RegistryQueryString(Key, L"NetCfgInstanceId", &ValueStr, TRUE);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query NetCfgInstanceId value");
goto cleanupAdapter;
}
if (FAILED(CLSIDFromString(ValueStr, &(*Adapter)->CfgInstanceID)))
{
LOG(WINTUN_LOG_ERR, L"NetCfgInstanceId is not a GUID");
HeapFree(ModuleHeap, 0, ValueStr);
Result = ERROR_INVALID_DATA;
goto cleanupAdapter;
}
HeapFree(ModuleHeap, 0, ValueStr);
/* Read the NetLuidIndex value. */
Result = RegistryQueryDWORD(Key, L"NetLuidIndex", &(*Adapter)->LuidIndex);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query NetLuidIndex value");
goto cleanupAdapter;
}
/* Read the NetLuidIndex value. */
Result = RegistryQueryDWORD(Key, L"*IfType", &(*Adapter)->IfType);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query *IfType value");
goto cleanupAdapter;
}
DWORD Size;
if (!SetupDiGetDeviceInstanceIdW(
DevInfo, DevInfoData, (*Adapter)->DevInstanceID, _countof((*Adapter)->DevInstanceID), &Size))
{
Result = LOG_LAST_ERROR(L"Failed to get device instance ID");
goto cleanupAdapter;
}
wcsncpy_s((*Adapter)->Pool, _countof((*Adapter)->Pool), Pool, _TRUNCATE);
Result = ERROR_SUCCESS;
cleanupAdapter:
if (Result != ERROR_SUCCESS)
HeapFree(ModuleHeap, 0, *Adapter);
cleanupKey:
RegCloseKey(Key);
return Result;
}
static void
GetDeviceRegPath(_In_ const WINTUN_ADAPTER *Adapter, _Out_cap_c_(MAX_REG_PATH) WCHAR *Path)
{
_snwprintf_s(
Path,
MAX_REG_PATH,
_TRUNCATE,
L"SYSTEM\\CurrentControlSet\\Enum\\%.*s",
MAX_INSTANCE_ID,
Adapter->DevInstanceID);
}
void WINAPI
WintunFreeAdapter(_In_ WINTUN_ADAPTER *Adapter)
{
HeapFree(ModuleHeap, 0, Adapter);
}
WINTUN_STATUS WINAPI
WintunGetAdapter(
_In_z_count_c_(MAX_POOL) const WCHAR *Pool,
_In_z_count_c_(MAX_ADAPTER_NAME) const WCHAR *Name,
_Out_ WINTUN_ADAPTER **Adapter)
{
DWORD Result;
HANDLE Mutex = NamespaceTakeMutex(Pool);
if (!Mutex)
return ERROR_INVALID_HANDLE;
HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (DevInfo == INVALID_HANDLE_VALUE)
{
Result = LOG_LAST_ERROR(L"Failed to get present class devices");
goto cleanupMutex;
}
for (DWORD EnumIndex = 0;; ++EnumIndex)
{
SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(SP_DEVINFO_DATA) };
if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData))
{
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
continue;
}
GUID CfgInstanceID;
if (GetNetCfgInstanceId(DevInfo, &DevInfoData, &CfgInstanceID) != ERROR_SUCCESS)
continue;
/* TODO: is there a better way than comparing ifnames? */
WCHAR Name2[MAX_ADAPTER_NAME];
if (NciGetConnectionName(&CfgInstanceID, Name2, sizeof(Name2), NULL) != ERROR_SUCCESS)
continue;
Name2[_countof(Name2) - 1] = 0;
if (_wcsicmp_l(Name, Name2, Locale))
{
RemoveNumberedSuffix(Name2, Name2);
if (_wcsicmp_l(Name, Name2, Locale))
continue;
}
/* Check the Hardware ID to make sure it's a real Wintun device. */
BOOL IsOurs;
Result = IsOurAdapter(DevInfo, &DevInfoData, &IsOurs);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to determine hardware ID");
goto cleanupDevInfo;
}
if (!IsOurs)
{
LOG(WINTUN_LOG_ERR, L"Foreign adapter with the same name exists");
Result = ERROR_ALREADY_EXISTS;
goto cleanupDevInfo;
}
BOOL IsMember;
Result = IsPoolMember(Pool, DevInfo, &DevInfoData, &IsMember);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to determine pool membership");
goto cleanupDevInfo;
}
if (!IsMember)
{
LOG(WINTUN_LOG_ERR, L"Wintun adapter with the same name exists in another pool");
Result = ERROR_ALREADY_EXISTS;
goto cleanupDevInfo;
}
Result = CreateAdapterData(Pool, DevInfo, &DevInfoData, Adapter);
if (Result != ERROR_SUCCESS)
LOG(WINTUN_LOG_ERR, L"Failed to create adapter data");
goto cleanupDevInfo;
}
Result = ERROR_FILE_NOT_FOUND;
cleanupDevInfo:
SetupDiDestroyDeviceInfoList(DevInfo);
cleanupMutex:
NamespaceReleaseMutex(Mutex);
return Result;
}
WINTUN_STATUS WINAPI
WintunGetAdapterName(_In_ const WINTUN_ADAPTER *Adapter, _Out_cap_c_(MAX_ADAPTER_NAME) WCHAR *Name)
{
return NciGetConnectionName(&Adapter->CfgInstanceID, Name, MAX_ADAPTER_NAME * sizeof(WCHAR), NULL);
}
static WINTUN_STATUS
ConvertInterfaceAliasToGuid(_In_z_ const WCHAR *Name, _Out_ GUID *Guid)
{
NET_LUID Luid;
DWORD Result = ConvertInterfaceAliasToLuid(Name, &Luid);
if (Result != NO_ERROR)
return LOG_ERROR(L"Failed convert interface alias name to the locally unique identifier", Result);
return ConvertInterfaceLuidToGuid(&Luid, Guid);
}
WINTUN_STATUS WINAPI
WintunSetAdapterName(_In_ const WINTUN_ADAPTER *Adapter, _In_z_count_c_(MAX_ADAPTER_NAME) const WCHAR *Name)
{
DWORD Result;
const int MaxSuffix = 1000;
WCHAR AvailableName[MAX_ADAPTER_NAME];
wcsncpy_s(AvailableName, _countof(AvailableName), Name, _TRUNCATE);
for (int i = 0;; ++i)
{
Result = NciSetConnectionName(&Adapter->CfgInstanceID, AvailableName);
if (Result == ERROR_DUP_NAME)
{
GUID Guid2;
DWORD Result2 = ConvertInterfaceAliasToGuid(AvailableName, &Guid2);
if (Result2 == ERROR_SUCCESS)
{
for (int j = 0; j < MaxSuffix; ++j)
{
WCHAR Proposal[MAX_ADAPTER_NAME];
_snwprintf_s(Proposal, _countof(Proposal), _TRUNCATE, L"%.*s %d", MAX_ADAPTER_NAME, Name, j + 1);
if (_wcsnicmp(Proposal, AvailableName, MAX_ADAPTER_NAME) == 0)
continue;
Result2 = NciSetConnectionName(&Guid2, Proposal);
if (Result2 == ERROR_DUP_NAME)
continue;
if (Result2 == ERROR_SUCCESS)
{
Result = NciSetConnectionName(&Adapter->CfgInstanceID, AvailableName);
if (Result == ERROR_SUCCESS)
break;
}
break;
}
}
}
if (Result == ERROR_SUCCESS)
break;
if (i > MaxSuffix || Result != ERROR_DUP_NAME)
return LOG_ERROR(L"Setting adapter name failed", Result);
_snwprintf_s(AvailableName, _countof(AvailableName), _TRUNCATE, L"%.*s %d", MAX_ADAPTER_NAME, Name, i + 1);
}
/* TODO: This should use NetSetup2 so that it doesn't get unset. */
HKEY DeviceRegKey;
WCHAR DeviceRegPath[MAX_REG_PATH];
GetDeviceRegPath(Adapter, DeviceRegPath);
Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, DeviceRegPath, 0, KEY_SET_VALUE, &DeviceRegKey);
if (Result != ERROR_SUCCESS)
return LOG_ERROR(L"Failed to open registry key", Result);
WCHAR PoolDeviceTypeName[MAX_POOL_DEVICE_TYPE];
GetPoolDeviceTypeName(Adapter->Pool, PoolDeviceTypeName);
Result = RegSetKeyValueW(
DeviceRegKey,
NULL,
L"FriendlyName",
REG_SZ,
PoolDeviceTypeName,
(DWORD)((wcslen(PoolDeviceTypeName) + 1) * sizeof(WCHAR)));
RegCloseKey(DeviceRegKey);
return Result;
}
void WINAPI
WintunGetAdapterGUID(_In_ const WINTUN_ADAPTER *Adapter, _Out_ GUID *Guid)
{
memcpy(Guid, &Adapter->CfgInstanceID, sizeof(GUID));
}
void WINAPI
WintunGetAdapterLUID(_In_ const WINTUN_ADAPTER *Adapter, _Out_ LUID *Luid)
{
*(LONGLONG *)Luid = (((LONGLONG)Adapter->LuidIndex & ((1 << 24) - 1)) << 24) |
(((LONGLONG)Adapter->IfType & ((1 << 16) - 1)) << 48);
}
WINTUN_STATUS WINAPI
WintunGetAdapterDeviceObject(_In_ const WINTUN_ADAPTER *Adapter, _Out_ HANDLE *Handle)
{
return GetDeviceObject(Adapter->DevInstanceID, Handle);
}
#if defined(HAVE_EV) || defined(HAVE_WHQL)
/* We can't use RtlGetVersion, because appcompat's aclayers.dll shims it to report Vista
* when run from legacy contexts. So, we instead use the undocumented RtlGetNtVersionNumbers.
*
* Another way would be reading from the PEB directly:
* ((DWORD *)NtCurrentTeb()->ProcessEnvironmentBlock)[sizeof(void *) == 8 ? 70 : 41]
* Or just read from KUSER_SHARED_DATA the same way on 32-bit and 64-bit:
* *(DWORD *)0x7FFE026C
*/
extern VOID NTAPI
RtlGetNtVersionNumbers(_Out_opt_ DWORD *MajorVersion, _Out_opt_ DWORD *MinorVersion, _Out_opt_ DWORD *BuildNumber);
static BOOL
HaveWHQL()
{
# if defined(HAVE_EV) && defined(HAVE_WHQL)
DWORD MajorVersion;
RtlGetNtVersionNumbers(&MajorVersion, NULL, NULL);
return MajorVersion >= 10;
# elif defined(HAVE_EV)
return FALSE;
# elif defined(HAVE_WHQL)
return TRUE;
# endif
}
static WINTUN_STATUS
InstallCertificate(_In_z_ const WCHAR *SignedResource)
{
LOG(WINTUN_LOG_INFO, L"Trusting code signing certificate");
const void *LockedResource;
DWORD SizeResource;
DWORD Result = ResourceGetAddress(SignedResource, &LockedResource, &SizeResource);
if (Result != ERROR_SUCCESS)
return LOG(WINTUN_LOG_ERR, L"Failed to locate resource"), Result;
const CERT_BLOB CertBlob = { .cbData = SizeResource, .pbData = (BYTE *)LockedResource };
HCERTSTORE QueriedStore;
if (!CryptQueryObject(
CERT_QUERY_OBJECT_BLOB,
&CertBlob,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_ALL,
0,
0,
0,
0,
&QueriedStore,
0,
NULL))
return LOG_LAST_ERROR(L"Failed to find certificate");
HCERTSTORE TrustedStore =
CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"TrustedPublisher");
if (!TrustedStore)
{
Result = LOG_LAST_ERROR(L"Failed to open store");
goto cleanupQueriedStore;
}
LPSTR CodeSigningOid[] = { szOID_PKIX_KP_CODE_SIGNING };
CERT_ENHKEY_USAGE EnhancedUsage = { .cUsageIdentifier = 1, .rgpszUsageIdentifier = CodeSigningOid };
for (const CERT_CONTEXT *CertContext = NULL; (CertContext = CertFindCertificateInStore(
QueriedStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
CERT_FIND_ENHKEY_USAGE,
&EnhancedUsage,
CertContext)) != NULL;)
{
CERT_EXTENSION *Ext = CertFindExtension(
szOID_BASIC_CONSTRAINTS2, CertContext->pCertInfo->cExtension, CertContext->pCertInfo->rgExtension);
CERT_BASIC_CONSTRAINTS2_INFO Constraints;
DWORD Size = sizeof(Constraints);
if (Ext &&
CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_BASIC_CONSTRAINTS2,
Ext->Value.pbData,
Ext->Value.cbData,
0,
NULL,
&Constraints,
&Size) &&
!Constraints.fCA)
if (!CertAddCertificateContextToStore(TrustedStore, CertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL))
{
LOG_LAST_ERROR(L"Failed to add certificate to store");
Result = Result != ERROR_SUCCESS ? Result : GetLastError();
}
}
CertCloseStore(TrustedStore, 0);
cleanupQueriedStore:
CertCloseStore(QueriedStore, 0);
return Result;
}
#endif
#if defined(HAVE_EV) || defined(HAVE_WHQL)
static BOOL
IsNewer(_In_ const SP_DRVINFO_DATA_W *DrvInfoData, _In_ const FILETIME *DriverDate, _In_ DWORDLONG DriverVersion)
{
if (DrvInfoData->DriverDate.dwHighDateTime > DriverDate->dwHighDateTime)
return TRUE;
if (DrvInfoData->DriverDate.dwHighDateTime < DriverDate->dwHighDateTime)
return FALSE;
if (DrvInfoData->DriverDate.dwLowDateTime > DriverDate->dwLowDateTime)
return TRUE;
if (DrvInfoData->DriverDate.dwLowDateTime < DriverDate->dwLowDateTime)
return FALSE;
if (DrvInfoData->DriverVersion > DriverVersion)
return TRUE;
if (DrvInfoData->DriverVersion < DriverVersion)
return FALSE;
return FALSE;
}
static void
GetTcpipAdapterRegPath(_In_ const WINTUN_ADAPTER *Adapter, _Out_cap_c_(MAX_REG_PATH) WCHAR *Path)
{
WCHAR Guid[MAX_GUID_STRING_LEN];
_snwprintf_s(
Path,
MAX_REG_PATH,
_TRUNCATE,
L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Adapters\\%.*s",
StringFromGUID2(&Adapter->CfgInstanceID, Guid, _countof(Guid)),
Guid);
}
static WINTUN_STATUS
GetTcpipInterfaceRegPath(_In_ const WINTUN_ADAPTER *Adapter, _Out_cap_c_(MAX_REG_PATH) WCHAR *Path)
{
DWORD Result;
HKEY TcpipAdapterRegKey;
WCHAR TcpipAdapterRegPath[MAX_REG_PATH];
GetTcpipAdapterRegPath(Adapter, TcpipAdapterRegPath);
Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TcpipAdapterRegPath, 0, KEY_QUERY_VALUE, &TcpipAdapterRegKey);
if (Result != ERROR_SUCCESS)
return LOG_ERROR(L"Failed to open registry key", Result);
WCHAR *Paths;
Result = RegistryQueryString(TcpipAdapterRegKey, L"IpConfig", &Paths, TRUE);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query IpConfig value");
goto cleanupTcpipAdapterRegKey;
}
if (!Paths[0])
{
LOG(WINTUN_LOG_ERR, L"IpConfig is empty");
Result = ERROR_INVALID_DATA;
goto cleanupPaths;
}
_snwprintf_s(Path, MAX_REG_PATH, _TRUNCATE, L"SYSTEM\\CurrentControlSet\\Services\\%s", Paths);
cleanupPaths:
HeapFree(ModuleHeap, 0, Paths);
cleanupTcpipAdapterRegKey:
RegCloseKey(TcpipAdapterRegKey);
return Result;
}
static WINTUN_STATUS
CreateAdapter(
_In_z_count_c_(MAX_PATH) const WCHAR *InfPath,
_In_z_count_c_(MAX_POOL) const WCHAR *Pool,
_In_z_count_c_(MAX_ADAPTER_NAME) const WCHAR *Name,
_In_opt_ const GUID *RequestedGUID,
_Out_ WINTUN_ADAPTER **Adapter,
_Inout_ BOOL *RebootRequired)
{
DWORD Result;
HANDLE Mutex = NamespaceTakeMutex(Pool);
if (!Mutex)
return ERROR_INVALID_HANDLE;
HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL);
if (DevInfo == INVALID_HANDLE_VALUE)
{
Result = LOG_LAST_ERROR(L"Creating empty device information set failed");
goto cleanupMutex;
}
WCHAR ClassName[MAX_CLASS_NAME_LEN];
if (!SetupDiClassNameFromGuidExW(&GUID_DEVCLASS_NET, ClassName, _countof(ClassName), NULL, NULL, NULL))
{
Result = LOG_LAST_ERROR(L"Retrieving class name associated with class GUID failed");
goto cleanupDevInfo;
}
WCHAR PoolDeviceTypeName[MAX_POOL_DEVICE_TYPE];
GetPoolDeviceTypeName(Pool, PoolDeviceTypeName);
SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(SP_DEVINFO_DATA) };
if (!SetupDiCreateDeviceInfoW(
DevInfo, ClassName, &GUID_DEVCLASS_NET, PoolDeviceTypeName, NULL, DICD_GENERATE_ID, &DevInfoData))
{
Result = LOG_LAST_ERROR(L"Creating new device information element failed");
goto cleanupDevInfo;
}
SP_DEVINSTALL_PARAMS_W DevInstallParams = { .cbSize = sizeof(SP_DEVINSTALL_PARAMS_W) };
if (!SetupDiGetDeviceInstallParamsW(DevInfo, &DevInfoData, &DevInstallParams))
{
Result = LOG_LAST_ERROR(L"Retrieving device installation parameters failed");
goto cleanupDevInfo;
}
DevInstallParams.Flags |= DI_QUIETINSTALL | DI_ENUMSINGLEINF;
wcscpy_s(DevInstallParams.DriverPath, _countof(DevInstallParams.DriverPath), InfPath);
if (!SetupDiSetDeviceInstallParamsW(DevInfo, &DevInfoData, &DevInstallParams))
{
Result = LOG_LAST_ERROR(L"Setting device installation parameters failed");
goto cleanupDevInfo;
}
if (!SetupDiSetSelectedDevice(DevInfo, &DevInfoData))
{
Result = LOG_LAST_ERROR(L"Failed selecting device");
goto cleanupDevInfo;
}
static const WCHAR Hwids[_countof(WINTUN_HWID) + 1 /*Multi-string terminator*/] = WINTUN_HWID;
if (!SetupDiSetDeviceRegistryPropertyW(DevInfo, &DevInfoData, SPDRP_HARDWAREID, (const BYTE *)Hwids, sizeof(Hwids)))
{
Result = LOG_LAST_ERROR(L"Failed setting hardware ID");
goto cleanupDevInfo;
}
if (!SetupDiBuildDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER)) /* TODO: This takes ~510ms */
{
Result = LOG_LAST_ERROR(L"Failed building driver info list");
goto cleanupDevInfo;
}
FILETIME DriverDate = { 0, 0 };
DWORDLONG DriverVersion = 0;
for (DWORD EnumIndex = 0;; ++EnumIndex) /* TODO: This loop takes ~600ms */
{
SP_DRVINFO_DATA_W DrvInfoData = { .cbSize = sizeof(SP_DRVINFO_DATA_W) };
if (!SetupDiEnumDriverInfoW(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER, EnumIndex, &DrvInfoData))
{
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
continue;
}
/* Check the driver version first, since the check is trivial and will save us iterating over hardware IDs for
* any driver versioned prior our best match. */
if (!IsNewer(&DrvInfoData, &DriverDate, DriverVersion))
continue;
SP_DRVINFO_DETAIL_DATA_W *DrvInfoDetailData;
if (AdapterGetDrvInfoDetail(DevInfo, &DevInfoData, &DrvInfoData, &DrvInfoDetailData) != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_WARN, L"Failed getting driver info detail");
continue;
}
if (!DriverIsOurDrvInfoDetail(DrvInfoDetailData))
{
HeapFree(ModuleHeap, 0, DrvInfoDetailData);
continue;
}
HeapFree(ModuleHeap, 0, DrvInfoDetailData);
if (!SetupDiSetSelectedDriverW(DevInfo, &DevInfoData, &DrvInfoData))
continue;
DriverDate = DrvInfoData.DriverDate;
DriverVersion = DrvInfoData.DriverVersion;
}
if (!DriverVersion)
{
LOG(WINTUN_LOG_ERR, L"No appropriate drivers found");
Result = ERROR_FILE_NOT_FOUND;
goto cleanupDriverInfoList;
}
if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, DevInfo, &DevInfoData))
{
Result = LOG_LAST_ERROR(L"Registering device failed");
goto cleanupDevice;
}
if (!SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, DevInfo, &DevInfoData))
LOG_LAST_ERROR(L"Registering coinstallers failed");
HKEY NetDevRegKey = INVALID_HANDLE_VALUE;
const int PollTimeout = 50 /* ms */;
for (int i = 0; NetDevRegKey == INVALID_HANDLE_VALUE && i < WAIT_FOR_REGISTRY_TIMEOUT / PollTimeout; ++i)
{
if (i)
Sleep(PollTimeout);
NetDevRegKey = SetupDiOpenDevRegKey(
DevInfo, &DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_SET_VALUE | KEY_QUERY_VALUE | KEY_NOTIFY);
}
if (NetDevRegKey == INVALID_HANDLE_VALUE)
{
Result = LOG_LAST_ERROR(L"Failed to open device-specific registry key");
goto cleanupDevice;
}
if (RequestedGUID)
{
WCHAR RequestedGUIDStr[MAX_GUID_STRING_LEN];
Result = RegSetValueExW(
NetDevRegKey,
L"NetSetupAnticipatedInstanceId",
0,
REG_SZ,
(const BYTE *)RequestedGUIDStr,
StringFromGUID2(RequestedGUID, RequestedGUIDStr, _countof(RequestedGUIDStr)) * sizeof(WCHAR));
if (Result != ERROR_SUCCESS)
{
LOG_LAST_ERROR(L"Failed to set NetSetupAnticipatedInstanceId");
goto cleanupNetDevRegKey;
}
}
if (!SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, DevInfo, &DevInfoData))
LOG_LAST_ERROR(L"Installing interfaces failed");
if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE, DevInfo, &DevInfoData))
{
Result = LOG_LAST_ERROR(L"Installing device failed");
goto cleanupNetDevRegKey;
}
*RebootRequired = *RebootRequired || CheckReboot(DevInfo, &DevInfoData);
if (!SetupDiSetDeviceRegistryPropertyW(
DevInfo,
&DevInfoData,
SPDRP_DEVICEDESC,
(const BYTE *)PoolDeviceTypeName,
(DWORD)((wcslen(PoolDeviceTypeName) + 1) * sizeof(WCHAR))))
{
Result = LOG_LAST_ERROR(L"Failed to set device description");
goto cleanupNetDevRegKey;
}
/* DIF_INSTALLDEVICE returns almost immediately, while the device installation continues in the background. It might
* take a while, before all registry keys and values are populated. */
WCHAR *DummyStr;
Result = RegistryQueryStringWait(NetDevRegKey, L"NetCfgInstanceId", WAIT_FOR_REGISTRY_TIMEOUT, &DummyStr);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query NetCfgInstanceId value");
goto cleanupNetDevRegKey;
}
HeapFree(ModuleHeap, 0, DummyStr);
DWORD DummyDWORD;
Result = RegistryQueryDWORDWait(NetDevRegKey, L"NetLuidIndex", WAIT_FOR_REGISTRY_TIMEOUT, &DummyDWORD);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query NetLuidIndex value");
goto cleanupNetDevRegKey;
}
Result = RegistryQueryDWORDWait(NetDevRegKey, L"*IfType", WAIT_FOR_REGISTRY_TIMEOUT, &DummyDWORD);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query *IfType value");
goto cleanupNetDevRegKey;
}
Result = CreateAdapterData(Pool, DevInfo, &DevInfoData, Adapter);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to create adapter data");
goto cleanupNetDevRegKey;
}
HKEY TcpipAdapterRegKey;
WCHAR TcpipAdapterRegPath[MAX_REG_PATH];
GetTcpipAdapterRegPath(*Adapter, TcpipAdapterRegPath);
Result = RegistryOpenKeyWait(
HKEY_LOCAL_MACHINE,
TcpipAdapterRegPath,
KEY_QUERY_VALUE | KEY_NOTIFY,
WAIT_FOR_REGISTRY_TIMEOUT,
&TcpipAdapterRegKey);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to open adapter-specific TCP/IP adapter registry key");
goto cleanupAdapter;
}
Result = RegistryQueryStringWait(TcpipAdapterRegKey, L"IpConfig", WAIT_FOR_REGISTRY_TIMEOUT, &DummyStr);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to query IpConfig value");
goto cleanupTcpipAdapterRegKey;
}
HeapFree(ModuleHeap, 0, DummyStr);
HKEY TcpipInterfaceRegKey;
WCHAR TcpipInterfaceRegPath[MAX_REG_PATH];
Result = GetTcpipInterfaceRegPath(*Adapter, TcpipInterfaceRegPath);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to determine interface-specific TCP/IP network registry key path");
goto cleanupTcpipAdapterRegKey;
}
Result = RegistryOpenKeyWait(
HKEY_LOCAL_MACHINE,
TcpipInterfaceRegPath,
KEY_QUERY_VALUE | KEY_SET_VALUE,
WAIT_FOR_REGISTRY_TIMEOUT,
&TcpipInterfaceRegKey);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to open interface-specific TCP/IP network registry key");
goto cleanupTcpipAdapterRegKey;
}
static const DWORD EnableDeadGWDetect = 0;
Result = RegSetKeyValueW(
TcpipInterfaceRegKey, NULL, L"EnableDeadGWDetect", REG_DWORD, &EnableDeadGWDetect, sizeof(EnableDeadGWDetect));
if (Result != ERROR_SUCCESS)
LOG_ERROR(L"Failed to set EnableDeadGWDetect", Result);
Result = WintunSetAdapterName(*Adapter, Name);
if (Result != ERROR_SUCCESS)
LOG_ERROR(L"Failed to set adapter name", Result);
RegCloseKey(TcpipInterfaceRegKey);
cleanupTcpipAdapterRegKey:
RegCloseKey(TcpipAdapterRegKey);
cleanupAdapter:
if (Result != ERROR_SUCCESS)
HeapFree(ModuleHeap, 0, *Adapter);
cleanupNetDevRegKey:
RegCloseKey(NetDevRegKey);
cleanupDevice:
if (Result != ERROR_SUCCESS)
{
/* The adapter failed to install, or the adapter ID was unobtainable. Clean-up. */
SP_REMOVEDEVICE_PARAMS RemoveDeviceParams = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
.InstallFunction = DIF_REMOVE },
.Scope = DI_REMOVEDEVICE_GLOBAL };
if (SetupDiSetClassInstallParamsW(
DevInfo, &DevInfoData, &RemoveDeviceParams.ClassInstallHeader, sizeof(RemoveDeviceParams)) &&
SetupDiCallClassInstaller(DIF_REMOVE, DevInfo, &DevInfoData))
*RebootRequired = *RebootRequired || CheckReboot(DevInfo, &DevInfoData);
}
cleanupDriverInfoList:
SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER);
cleanupDevInfo:
SetupDiDestroyDeviceInfoList(DevInfo);
cleanupMutex:
NamespaceReleaseMutex(Mutex);
return Result;
}
#endif
static WINTUN_STATUS
CreateTemporaryDirectory(_Out_cap_c_(MAX_PATH) WCHAR *RandomTempSubDirectory)
{
WCHAR WindowsDirectory[MAX_PATH];
if (!GetWindowsDirectoryW(WindowsDirectory, _countof(WindowsDirectory)))
return LOG_LAST_ERROR(L"Failed to get Windows folder");
WCHAR WindowsTempDirectory[MAX_PATH];
if (!PathCombineW(WindowsTempDirectory, WindowsDirectory, L"Temp"))
return ERROR_BUFFER_OVERFLOW;
UCHAR RandomBytes[32] = { 0 };
#pragma warning(suppress : 6387)
if (!RtlGenRandom(RandomBytes, sizeof(RandomBytes)))
return LOG_LAST_ERROR(L"Failed to generate random");
WCHAR RandomSubDirectory[sizeof(RandomBytes) * 2 + 1];
for (int i = 0; i < sizeof(RandomBytes); ++i)
swprintf_s(&RandomSubDirectory[i * 2], 3, L"%02x", RandomBytes[i]);
if (!PathCombineW(RandomTempSubDirectory, WindowsTempDirectory, RandomSubDirectory))
return ERROR_BUFFER_OVERFLOW;
if (!CreateDirectoryW(RandomTempSubDirectory, SecurityAttributes))
return LOG_LAST_ERROR(L"Failed to create temporary folder");
return ERROR_SUCCESS;
}
#if defined(_M_IX86) || defined(_M_ARM)
typedef struct _PROCESS_STDOUT_STATE
{
HANDLE Stdout;
WCHAR *Response;
DWORD ResponseCapacity;
} PROCESS_STDOUT_STATE;
static DWORD WINAPI
ProcessStdout(_Inout_ PROCESS_STDOUT_STATE *State)
{
for (DWORD Offset = 0, MaxLen = State->ResponseCapacity - 1; Offset < MaxLen;)
{
DWORD SizeRead;
if (!ReadFile(State->Stdout, State->Response + Offset, sizeof(WCHAR) * (MaxLen - Offset), &SizeRead, NULL))
return 0;
if (SizeRead % sizeof(WCHAR))
return 1;
Offset += SizeRead / sizeof(WCHAR);
State->Response[Offset] = 0;
}
return 2;
}
static DWORD WINAPI
ProcessStderr(_In_ HANDLE Stderr)
{
enum
{
OnNone,
OnLevelStart,
OnLevel,
OnLevelEnd,
OnSpace,
OnMsg
} State = OnNone;
WCHAR Msg[0x200];
DWORD Count = 0;
WINTUN_LOGGER_LEVEL Level = WINTUN_LOG_INFO;
for (;;)
{
WCHAR Buf[0x200];
DWORD SizeRead;
if (!ReadFile(Stderr, Buf, sizeof(Buf), &SizeRead, NULL))
return 0;
if (SizeRead % sizeof(WCHAR))
return 1;
SizeRead /= sizeof(WCHAR);
for (DWORD i = 0; i < SizeRead; ++i)
{
WCHAR c = Buf[i];
if (State == OnNone && c == L'[')
State = OnLevelStart;
else if (
State == OnLevelStart && ((Level = WINTUN_LOG_INFO, c == L'+') ||
(Level = WINTUN_LOG_WARN, c == L'-') || (Level = WINTUN_LOG_ERR, c == L'!')))
State = OnLevelEnd;
else if (State == OnLevelEnd && c == L']')
State = OnSpace;
else if (State == OnSpace && !iswspace(c) || State == OnMsg && c != L'\r' && c != L'\n')
{
if (Count < _countof(Msg) - 1)
Msg[Count++] = c;
State = OnMsg;
}
else if (State == OnMsg && c == L'\n')
{
Msg[Count] = 0;
Logger(Level, Msg);
State = OnNone;
Count = 0;
}
}
}
}
static WINTUN_STATUS
ExecuteRunDll32(
_In_z_ const WCHAR *Arguments,
_Out_z_cap_c_(ResponseCapacity) WCHAR *Response,
_In_ DWORD ResponseCapacity)
{
WCHAR WindowsDirectory[MAX_PATH];
if (!GetWindowsDirectoryW(WindowsDirectory, _countof(WindowsDirectory)))
return LOG_LAST_ERROR(L"Failed to get Windows folder");
WCHAR RunDll32Path[MAX_PATH];
if (!PathCombineW(RunDll32Path, WindowsDirectory, L"Sysnative\\rundll32.exe"))
return ERROR_BUFFER_OVERFLOW;
DWORD Result;
WCHAR RandomTempSubDirectory[MAX_PATH];
if ((Result = CreateTemporaryDirectory(RandomTempSubDirectory)) != ERROR_SUCCESS)
return LOG(WINTUN_LOG_ERR, L"Failed to create temporary folder"), Result;
WCHAR DllPath[MAX_PATH] = { 0 };
if (!PathCombineW(DllPath, RandomTempSubDirectory, L"wintun.dll"))
{
Result = ERROR_BUFFER_OVERFLOW;
goto cleanupDirectory;
}
if ((Result = ResourceCopyToFile(
DllPath, NativeMachine == IMAGE_FILE_MACHINE_ARM64 ? L"wintun-arm64.dll" : L"wintun-amd64.dll")) !=
ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to copy resource");
goto cleanupDelete;
}
size_t CommandLineLen = 10 + MAX_PATH + 2 + wcslen(Arguments) + 1;
WCHAR *CommandLine = HeapAlloc(ModuleHeap, 0, CommandLineLen * sizeof(WCHAR));
if (!CommandLine)
{
LOG(WINTUN_LOG_ERR, L"Out of memory");
Result = ERROR_OUTOFMEMORY;
goto cleanupDelete;
}
_snwprintf_s(CommandLine, CommandLineLen, _TRUNCATE, L"rundll32 \"%.*s\",%s", MAX_PATH, DllPath, Arguments);
SECURITY_ATTRIBUTES sa = { .nLength = sizeof(SECURITY_ATTRIBUTES),
.bInheritHandle = TRUE,
.lpSecurityDescriptor =
SecurityAttributes ? SecurityAttributes->lpSecurityDescriptor : NULL };
typedef enum
{
Stdout = 0,
Stderr
} stdid_t;
HANDLE StreamR[] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE },
StreamW[] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
if (!CreatePipe(&StreamR[Stdout], &StreamW[Stdout], &sa, 0) ||
!CreatePipe(&StreamR[Stderr], &StreamW[Stderr], &sa, 0))
{
Result = LOG_LAST_ERROR(L"Failed to create pipes");
goto cleanupPipes;
}
if (!SetHandleInformation(StreamR[Stdout], HANDLE_FLAG_INHERIT, 0) ||
!SetHandleInformation(StreamR[Stderr], HANDLE_FLAG_INHERIT, 0))
{
Result = LOG_LAST_ERROR(L"Failed to set handle info");
goto cleanupPipes;
}
if (ResponseCapacity)
Response[0] = 0;
PROCESS_STDOUT_STATE ProcessStdoutState = { .Stdout = StreamR[Stdout],
.Response = Response,
.ResponseCapacity = ResponseCapacity };
HANDLE Thread[] = { NULL, NULL };
if ((Thread[Stdout] = CreateThread(SecurityAttributes, 0, ProcessStdout, &ProcessStdoutState, 0, NULL)) == NULL ||
(Thread[Stderr] = CreateThread(SecurityAttributes, 0, ProcessStderr, StreamR[Stderr], 0, NULL)) == NULL)
{
Result = LOG_LAST_ERROR(L"Failed to spawn reader threads");
goto cleanupThreads;
}
STARTUPINFOW si = { .cb = sizeof(STARTUPINFO),
.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES,
.wShowWindow = SW_HIDE,
.hStdOutput = StreamW[Stdout],
.hStdError = StreamW[Stderr] };
PROCESS_INFORMATION pi;
if (!CreateProcessW(RunDll32Path, CommandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
Result = LOG_LAST_ERROR(L"Creating process failed");
goto cleanupThreads;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
cleanupThreads:
for (size_t i = _countof(Thread); i--;)
if (Thread[i])
{
CloseHandle(StreamW[i]);
StreamW[i] = INVALID_HANDLE_VALUE;
WaitForSingleObject(Thread[i], INFINITE);
CloseHandle(Thread[i]);
}
cleanupPipes:
CloseHandle(StreamR[Stderr]);
CloseHandle(StreamW[Stderr]);
CloseHandle(StreamR[Stdout]);
CloseHandle(StreamW[Stdout]);
HeapFree(ModuleHeap, 0, CommandLine);
cleanupDelete:
DeleteFileW(DllPath);
cleanupDirectory:
RemoveDirectoryW(RandomTempSubDirectory);
return Result;
}
static WINTUN_STATUS
GetAdapter(_In_z_count_c_(MAX_POOL) const WCHAR *Pool, _In_ const GUID *CfgInstanceID, _Out_ WINTUN_ADAPTER **Adapter)
{
HANDLE Mutex = NamespaceTakeMutex(Pool);
if (!Mutex)
return ERROR_INVALID_HANDLE;
HDEVINFO DevInfo;
SP_DEVINFO_DATA DevInfoData;
DWORD Result = GetDevInfoData(CfgInstanceID, &DevInfo, &DevInfoData);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to locate adapter");
goto cleanupMutex;
}
Result = CreateAdapterData(Pool, DevInfo, &DevInfoData, Adapter);
if (Result != ERROR_SUCCESS)
LOG(WINTUN_LOG_ERR, L"Failed to create adapter data");
SetupDiDestroyDeviceInfoList(DevInfo);
cleanupMutex:
NamespaceReleaseMutex(Mutex);
return Result;
}
#endif
WINTUN_STATUS WINAPI
WintunCreateAdapter(
_In_z_count_c_(MAX_POOL) const WCHAR *Pool,
_In_z_count_c_(MAX_ADAPTER_NAME) const WCHAR *Name,
_In_opt_ const GUID *RequestedGUID,
_Out_ WINTUN_ADAPTER **Adapter,
_Inout_ BOOL *RebootRequired)
{
#if defined(_M_IX86) || defined(_M_ARM)
if (NativeMachine != IMAGE_FILE_PROCESS)
{
LOG(WINTUN_LOG_INFO, L"Spawning native process for the job");
WCHAR RequestedGUIDStr[MAX_GUID_STRING_LEN];
WCHAR Arguments[15 + MAX_POOL + 3 + MAX_ADAPTER_NAME + 2 + MAX_GUID_STRING_LEN + 1];
_snwprintf_s(
Arguments,
_countof(Arguments),
_TRUNCATE,
RequestedGUID ? L"CreateAdapter \"%.*s\" \"%.*s\" %.*s" : L"CreateAdapter \"%.*s\" \"%.*s\"",
MAX_POOL,
Pool,
MAX_ADAPTER_NAME,
Name,
RequestedGUID ? StringFromGUID2(RequestedGUID, RequestedGUIDStr, _countof(RequestedGUIDStr)) : 0,
RequestedGUIDStr);
WCHAR Response[8 + 1 + MAX_GUID_STRING_LEN + 1 + 8 + 1];
DWORD Result = ExecuteRunDll32(Arguments, Response, _countof(Response));
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Error executing worker process");
return Result;
}
int Argc;
WCHAR **Argv = CommandLineToArgvW(Response, &Argc);
GUID CfgInstanceID;
if (Argc < 3 || FAILED(CLSIDFromString(Argv[1], &CfgInstanceID)))
{
LOG(WINTUN_LOG_ERR, L"Incomplete or invalid response");
Result = ERROR_INVALID_PARAMETER;
goto cleanupArgv;
}
Result = wcstoul(Argv[0], NULL, 16);
if (Result == ERROR_SUCCESS && GetAdapter(Pool, &CfgInstanceID, Adapter) != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to get adapter");
Result = ERROR_FILE_NOT_FOUND;
}
if (wcstoul(Argv[2], NULL, 16))
*RebootRequired = TRUE;
cleanupArgv:
LocalFree(Argv);
return Result;
}
#endif
#if defined(HAVE_EV) || defined(HAVE_WHQL)
DWORD Result = ERROR_SUCCESS;
WCHAR RandomTempSubDirectory[MAX_PATH];
if ((Result = CreateTemporaryDirectory(RandomTempSubDirectory)) != ERROR_SUCCESS)
return LOG(WINTUN_LOG_ERR, L"Failed to create temporary folder"), Result;
WCHAR CatPath[MAX_PATH] = { 0 };
WCHAR SysPath[MAX_PATH] = { 0 };
WCHAR InfPath[MAX_PATH] = { 0 };
if (!PathCombineW(CatPath, RandomTempSubDirectory, L"wintun.cat") ||
!PathCombineW(SysPath, RandomTempSubDirectory, L"wintun.sys") ||
!PathCombineW(InfPath, RandomTempSubDirectory, L"wintun.inf"))
{
Result = ERROR_BUFFER_OVERFLOW;
goto cleanupDirectory;
}
BOOL UseWHQL = HaveWHQL();
if (!UseWHQL && (Result = InstallCertificate(L"wintun.cat")) != ERROR_SUCCESS)
LOG(WINTUN_LOG_WARN, L"Unable to install code signing certificate");
LOG(WINTUN_LOG_INFO, L"Copying resources to temporary path");
if ((Result = ResourceCopyToFile(CatPath, UseWHQL ? L"wintun-whql.cat" : L"wintun.cat")) != ERROR_SUCCESS ||
(Result = ResourceCopyToFile(SysPath, UseWHQL ? L"wintun-whql.sys" : L"wintun.sys")) != ERROR_SUCCESS ||
(Result = ResourceCopyToFile(InfPath, UseWHQL ? L"wintun-whql.inf" : L"wintun.inf")) != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to copy resources");
goto cleanupDelete;
}
LOG(WINTUN_LOG_INFO, L"Installing driver");
if (!SetupCopyOEMInfW(InfPath, NULL, SPOST_PATH, 0, NULL, 0, NULL, NULL))
{
Result = LOG_LAST_ERROR(L"Could not install driver to store");
goto cleanupDelete;
}
Result = CreateAdapter(InfPath, Pool, Name, RequestedGUID, Adapter, RebootRequired);
DriverRemoveAllOurs();
cleanupDelete:
DeleteFileW(CatPath);
DeleteFileW(SysPath);
DeleteFileW(InfPath);
cleanupDirectory:
RemoveDirectoryW(RandomTempSubDirectory);
return Result;
#else
return ERROR_NOT_SUPPORTED;
#endif
}
WINTUN_STATUS WINAPI
WintunDeleteAdapter(_In_ const WINTUN_ADAPTER *Adapter, _Inout_ BOOL *RebootRequired)
{
#if defined(_M_IX86) || defined(_M_ARM)
if (NativeMachine != IMAGE_FILE_PROCESS)
{
LOG(WINTUN_LOG_INFO, L"Spawning native process for the job");
WCHAR GuidStr[MAX_GUID_STRING_LEN];
WCHAR Arguments[14 + MAX_GUID_STRING_LEN + 1];
_snwprintf_s(
Arguments,
_countof(Arguments),
_TRUNCATE,
L"DeleteAdapter %.*s",
StringFromGUID2(&Adapter->CfgInstanceID, GuidStr, _countof(GuidStr)),
GuidStr);
WCHAR Response[8 + 1 + 8 + 1];
DWORD Result = ExecuteRunDll32(Arguments, Response, _countof(Response));
if (Result != ERROR_SUCCESS)
LOG(WINTUN_LOG_ERR, L"Error executing worker process");
int Argc;
WCHAR **Argv = CommandLineToArgvW(Response, &Argc);
if (Argc < 2)
{
LOG(WINTUN_LOG_ERR, L"Incomplete or invalid response");
Result = ERROR_INVALID_PARAMETER;
goto cleanupArgv;
}
Result = wcstoul(Argv[0], NULL, 16);
if (wcstoul(Argv[1], NULL, 16))
*RebootRequired = TRUE;
cleanupArgv:
LocalFree(Argv);
return Result;
}
#endif
HDEVINFO DevInfo;
SP_DEVINFO_DATA DevInfoData;
DWORD Result = GetDevInfoData(&Adapter->CfgInstanceID, &DevInfo, &DevInfoData);
if (Result == ERROR_FILE_NOT_FOUND)
return ERROR_SUCCESS;
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to get device info data");
return Result;
}
SetQuietInstall(DevInfo, &DevInfoData);
SP_REMOVEDEVICE_PARAMS RemoveDeviceParams = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
.InstallFunction = DIF_REMOVE },
.Scope = DI_REMOVEDEVICE_GLOBAL };
if (SetupDiSetClassInstallParamsW(
DevInfo, &DevInfoData, &RemoveDeviceParams.ClassInstallHeader, sizeof(RemoveDeviceParams)) &&
SetupDiCallClassInstaller(DIF_REMOVE, DevInfo, &DevInfoData))
*RebootRequired = *RebootRequired || CheckReboot(DevInfo, &DevInfoData);
else
Result = LOG_LAST_ERROR(L"Unable to remove existing adapter");
SetupDiDestroyDeviceInfoList(DevInfo);
return Result;
}
WINTUN_STATUS WINAPI
WintunEnumAdapters(_In_z_count_c_(MAX_POOL) const WCHAR *Pool, _In_ WINTUN_ENUM_FUNC Func, _In_ LPARAM Param)
{
HANDLE Mutex = NamespaceTakeMutex(Pool);
if (!Mutex)
return ERROR_INVALID_HANDLE;
DWORD Result = ERROR_SUCCESS;
HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (DevInfo == INVALID_HANDLE_VALUE)
{
Result = LOG_LAST_ERROR(L"Failed to get present class devices");
goto cleanupMutex;
}
BOOL Continue = TRUE;
for (DWORD EnumIndex = 0; Continue; ++EnumIndex)
{
SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(SP_DEVINFO_DATA) };
if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData))
{
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
continue;
}
BOOL IsOurs;
if (IsOurAdapter(DevInfo, &DevInfoData, &IsOurs) != ERROR_SUCCESS || !IsOurs)
continue;
BOOL IsMember;
Result = IsPoolMember(Pool, DevInfo, &DevInfoData, &IsMember);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to determine pool membership");
break;
}
if (!IsMember)
continue;
WINTUN_ADAPTER *Adapter;
Result = CreateAdapterData(Pool, DevInfo, &DevInfoData, &Adapter);
if (Result != ERROR_SUCCESS)
{
LOG(WINTUN_LOG_ERR, L"Failed to create adapter data");
break;
}
Continue = Func(Adapter, Param);
HeapFree(ModuleHeap, 0, Adapter);
}
SetupDiDestroyDeviceInfoList(DevInfo);
cleanupMutex:
NamespaceReleaseMutex(Mutex);
return Result;
}