aboutsummaryrefslogtreecommitdiffstats
path: root/api/adapter.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--api/adapter.c950
1 files changed, 950 insertions, 0 deletions
diff --git a/api/adapter.c b/api/adapter.c
new file mode 100644
index 0000000..0dd8c42
--- /dev/null
+++ b/api/adapter.c
@@ -0,0 +1,950 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#include <Windows.h>
+#include <winternl.h>
+#include <cfgmgr32.h>
+#include <devguid.h>
+#include <iphlpapi.h>
+#include <objbase.h>
+#include <ndisguid.h>
+#include <SetupAPI.h>
+#include <Shlwapi.h>
+#include <devioctl.h>
+#include <wchar.h>
+#include <initguid.h> /* Keep these two at bottom in this order, so that we only generate extra GUIDs for devpkey. The other keys we'll get from uuid.lib like usual. */
+#include <devpkey.h>
+
+/* We pretend we're Windows 8, and then hack around the limitation in Windows 7 below. */
+#if NTDDI_VERSION == NTDDI_WIN7
+# undef NTDDI_VERSION
+# define NTDDI_VERSION NTDDI_WIN8
+# include <devquery.h>
+# include <swdevice.h>
+# undef NTDDI_VERSION
+# define NTDDI_VERSION NTDDI_WIN7
+#else
+# include <devquery.h>
+# include <swdevice.h>
+#endif
+
+#include "adapter.h"
+#include "driver.h"
+#include "logger.h"
+#include "main.h"
+#include "namespace.h"
+#include "nci.h"
+#include "ntdll.h"
+#include "rundll32.h"
+#include "registry.h"
+#include "adapter_win7.h"
+
+#pragma warning(disable : 4221) /* nonstandard: address of automatic in initializer */
+
+const DEVPROPKEY DEVPKEY_Wintun_Name = {
+ { 0x3361c968, 0x2f2e, 0x4660, { 0xb4, 0x7e, 0x69, 0x9c, 0xdc, 0x4c, 0x32, 0xb9 } },
+ DEVPROPID_FIRST_USABLE + 1
+};
+
+_Must_inspect_result_
+static _Return_type_success_(return != FALSE)
+BOOL
+PopulateAdapterData(_Inout_ WINTUN_ADAPTER *Adapter)
+{
+ DWORD LastError = ERROR_SUCCESS;
+
+ /* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> registry key. */
+ HKEY Key =
+ SetupDiOpenDevRegKey(Adapter->DevInfo, &Adapter->DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
+ if (Key == INVALID_HANDLE_VALUE)
+ {
+ LOG_LAST_ERROR(L"Failed to open adapter device registry key");
+ return FALSE;
+ }
+
+ LPWSTR ValueStr = RegistryQueryString(Key, L"NetCfgInstanceId", TRUE);
+ if (!ValueStr)
+ {
+ WCHAR RegPath[MAX_REG_PATH];
+ LoggerGetRegistryKeyPath(Key, RegPath);
+ LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\NetCfgInstanceId", MAX_REG_PATH, RegPath);
+ goto cleanupKey;
+ }
+ if (FAILED(CLSIDFromString(ValueStr, &Adapter->CfgInstanceID)))
+ {
+ WCHAR RegPath[MAX_REG_PATH];
+ LoggerGetRegistryKeyPath(Key, RegPath);
+ LastError = LOG(WINTUN_LOG_ERR, L"%.*s\\NetCfgInstanceId is not a GUID: %s", MAX_REG_PATH, RegPath, ValueStr);
+ Free(ValueStr);
+ goto cleanupKey;
+ }
+ Free(ValueStr);
+
+ if (!RegistryQueryDWORD(Key, L"NetLuidIndex", &Adapter->LuidIndex, TRUE))
+ {
+ WCHAR RegPath[MAX_REG_PATH];
+ LoggerGetRegistryKeyPath(Key, RegPath);
+ LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\NetLuidIndex", MAX_REG_PATH, RegPath);
+ goto cleanupKey;
+ }
+
+ if (!RegistryQueryDWORD(Key, L"*IfType", &Adapter->IfType, TRUE))
+ {
+ WCHAR RegPath[MAX_REG_PATH];
+ LoggerGetRegistryKeyPath(Key, RegPath);
+ LastError = LOG(WINTUN_LOG_ERR, L"Failed to get %.*s\\*IfType", MAX_REG_PATH, RegPath);
+ goto cleanupKey;
+ }
+
+ Adapter->InterfaceFilename = AdapterGetDeviceObjectFileName(Adapter->DevInstanceID);
+ if (!Adapter->InterfaceFilename)
+ {
+ LastError = LOG_LAST_ERROR(L"Unable to determine device object file name");
+ goto cleanupKey;
+ }
+
+cleanupKey:
+ RegCloseKey(Key);
+ return RET_ERROR(TRUE, LastError);
+}
+
+static volatile LONG OrphanThreadIsWorking = FALSE;
+
+static DWORD
+DoOrphanedDeviceCleanup(_In_opt_ LPVOID Ctx)
+{
+ AdapterCleanupOrphanedDevices();
+ OrphanThreadIsWorking = FALSE;
+ return 0;
+}
+
+static VOID QueueUpOrphanedDeviceCleanupRoutine(VOID)
+{
+ if (InterlockedCompareExchange(&OrphanThreadIsWorking, TRUE, FALSE) == FALSE)
+ QueueUserWorkItem(DoOrphanedDeviceCleanup, NULL, 0);
+}
+
+VOID AdapterCleanupOrphanedDevices(VOID)
+{
+ HANDLE DeviceInstallationMutex = NamespaceTakeDeviceInstallationMutex();
+ if (!DeviceInstallationMutex)
+ {
+ LOG_LAST_ERROR(L"Failed to take device installation mutex");
+ return;
+ }
+
+ if (IsWindows7)
+ {
+ AdapterCleanupOrphanedDevicesWin7();
+ goto cleanupDeviceInstallationMutex;
+ }
+
+ HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, 0, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ {
+ LOG_LAST_ERROR(L"Failed to get adapters");
+ goto cleanupDeviceInstallationMutex;
+ }
+
+ SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) };
+ for (DWORD EnumIndex = 0;; ++EnumIndex)
+ {
+ if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+ ULONG Status, Code;
+ if (CM_Get_DevNode_Status(&Status, &Code, DevInfoData.DevInst, 0) == CR_SUCCESS && !(Status & DN_HAS_PROBLEM))
+ continue;
+
+ DEVPROPTYPE PropType;
+ WCHAR Name[MAX_ADAPTER_NAME] = L"<unknown>";
+ SetupDiGetDevicePropertyW(
+ DevInfo,
+ &DevInfoData,
+ &DEVPKEY_Wintun_Name,
+ &PropType,
+ (PBYTE)Name,
+ MAX_ADAPTER_NAME * sizeof(Name[0]),
+ NULL,
+ 0);
+ if (!AdapterRemoveInstance(DevInfo, &DevInfoData))
+ {
+ LOG_LAST_ERROR(L"Failed to remove orphaned adapter \"%s\"", Name);
+ continue;
+ }
+ LOG(WINTUN_LOG_INFO, L"Removed orphaned adapter \"%s\"", Name);
+ }
+ SetupDiDestroyDeviceInfoList(DevInfo);
+cleanupDeviceInstallationMutex:
+ NamespaceReleaseMutex(DeviceInstallationMutex);
+}
+
+_Use_decl_annotations_
+VOID WINAPI
+WintunCloseAdapter(WINTUN_ADAPTER *Adapter)
+{
+ if (!Adapter)
+ return;
+ Free(Adapter->InterfaceFilename);
+ if (Adapter->SwDevice)
+ SwDeviceClose(Adapter->SwDevice);
+ if (Adapter->DevInfo)
+ {
+ if (!AdapterRemoveInstance(Adapter->DevInfo, &Adapter->DevInfoData))
+ LOG_LAST_ERROR(L"Failed to remove adapter when closing");
+ SetupDiDestroyDeviceInfoList(Adapter->DevInfo);
+ }
+ Free(Adapter);
+ QueueUpOrphanedDeviceCleanupRoutine();
+}
+
+static _Return_type_success_(return != FALSE)
+BOOL
+RenameByNetGUID(_In_ GUID *Guid, _In_reads_or_z_(MAX_ADAPTER_NAME) LPCWSTR Name)
+{
+ DWORD LastError = ERROR_NOT_FOUND;
+ HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, 0, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ {
+ LastError = GetLastError();
+ goto cleanup;
+ }
+
+ SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) };
+ for (DWORD EnumIndex = 0;; ++EnumIndex)
+ {
+ if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ HKEY Key = SetupDiOpenDevRegKey(DevInfo, &DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
+ if (Key == INVALID_HANDLE_VALUE)
+ continue;
+ LPWSTR ValueStr = RegistryQueryString(Key, L"NetCfgInstanceId", TRUE);
+ RegCloseKey(Key);
+ if (!ValueStr)
+ continue;
+ GUID Guid2;
+ HRESULT HRet = CLSIDFromString(ValueStr, &Guid2);
+ Free(ValueStr);
+ if (FAILED(HRet) || memcmp(Guid, &Guid2, sizeof(*Guid)))
+ continue;
+ LastError = SetupDiSetDevicePropertyW(
+ DevInfo,
+ &DevInfoData,
+ &DEVPKEY_Wintun_Name,
+ DEVPROP_TYPE_STRING,
+ (PBYTE)Name,
+ (DWORD)((wcslen(Name) + 1) * sizeof(Name[0])),
+ 0)
+ ? ERROR_SUCCESS
+ : GetLastError();
+ break;
+ }
+ SetupDiDestroyDeviceInfoList(DevInfo);
+cleanup:
+ return RET_ERROR(TRUE, LastError);
+}
+
+_Must_inspect_result_
+static _Return_type_success_(return != FALSE)
+BOOL
+ConvertInterfaceAliasToGuid(_In_z_ LPCWSTR Name, _Out_ GUID *Guid)
+{
+ NET_LUID Luid;
+ DWORD LastError = ConvertInterfaceAliasToLuid(Name, &Luid);
+ if (LastError != NO_ERROR)
+ {
+ SetLastError(LOG_ERROR(LastError, L"Failed convert interface %s name to the locally unique identifier", Name));
+ return FALSE;
+ }
+ LastError = ConvertInterfaceLuidToGuid(&Luid, Guid);
+ if (LastError != NO_ERROR)
+ {
+ SetLastError(LOG_ERROR(LastError, L"Failed to convert interface %s LUID (%I64u) to GUID", Name, Luid.Value));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static _Return_type_success_(return != FALSE)
+BOOL
+NciSetAdapterName(_In_ GUID *Guid, _In_reads_or_z_(MAX_ADAPTER_NAME) LPCWSTR Name)
+{
+ const int MaxSuffix = 1000;
+ WCHAR AvailableName[MAX_ADAPTER_NAME];
+ if (wcsncpy_s(AvailableName, _countof(AvailableName), Name, _TRUNCATE) == STRUNCATE)
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return FALSE;
+ }
+ for (int i = 0;; ++i)
+ {
+ DWORD LastError = NciSetConnectionName(Guid, AvailableName);
+ if (LastError == ERROR_DUP_NAME)
+ {
+ GUID Guid2;
+ if (ConvertInterfaceAliasToGuid(AvailableName, &Guid2))
+ {
+ for (int j = 0; j < MaxSuffix; ++j)
+ {
+ WCHAR Proposal[MAX_ADAPTER_NAME];
+ if (_snwprintf_s(Proposal, _countof(Proposal), _TRUNCATE, L"%s %d", Name, j + 1) == -1)
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return FALSE;
+ }
+ if (_wcsnicmp(Proposal, AvailableName, MAX_ADAPTER_NAME) == 0)
+ continue;
+ DWORD LastError2 = NciSetConnectionName(&Guid2, Proposal);
+ if (LastError2 == ERROR_DUP_NAME)
+ continue;
+ if (!RenameByNetGUID(&Guid2, Proposal))
+ LOG_LAST_ERROR(L"Failed to set foreign adapter name to \"%s\"", Proposal);
+ if (LastError2 == ERROR_SUCCESS)
+ {
+ LastError = NciSetConnectionName(Guid, AvailableName);
+ if (LastError == ERROR_SUCCESS)
+ break;
+ }
+ break;
+ }
+ }
+ }
+ if (LastError == ERROR_SUCCESS)
+ break;
+ if (i >= MaxSuffix || LastError != ERROR_DUP_NAME)
+ {
+ SetLastError(LastError);
+ return FALSE;
+ }
+ if (_snwprintf_s(AvailableName, _countof(AvailableName), _TRUNCATE, L"%s %d", Name, i + 1) == -1)
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+_Use_decl_annotations_
+VOID WINAPI
+WintunGetAdapterLUID(WINTUN_ADAPTER *Adapter, NET_LUID *Luid)
+{
+ Luid->Info.Reserved = 0;
+ Luid->Info.NetLuidIndex = Adapter->LuidIndex;
+ Luid->Info.IfType = Adapter->IfType;
+}
+
+_Use_decl_annotations_
+HANDLE WINAPI
+AdapterOpenDeviceObject(const WINTUN_ADAPTER *Adapter)
+{
+ HANDLE Handle = CreateFileW(
+ Adapter->InterfaceFilename,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ if (Handle == INVALID_HANDLE_VALUE)
+ LOG_LAST_ERROR(L"Failed to connect to adapter interface %s", Adapter->InterfaceFilename);
+ return Handle;
+}
+
+_Use_decl_annotations_
+LPWSTR
+AdapterGetDeviceObjectFileName(LPCWSTR InstanceId)
+{
+ ULONG InterfacesLen;
+ DWORD LastError = CM_MapCrToWin32Err(
+ CM_Get_Device_Interface_List_SizeW(
+ &InterfacesLen,
+ (GUID *)&GUID_DEVINTERFACE_NET,
+ (DEVINSTID_W)InstanceId,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT),
+ ERROR_GEN_FAILURE);
+ if (LastError != ERROR_SUCCESS)
+ {
+ SetLastError(LOG_ERROR(LastError, L"Failed to query adapter %s associated instances size", InstanceId));
+ return NULL;
+ }
+ LPWSTR Interfaces = AllocArray(InterfacesLen, sizeof(*Interfaces));
+ if (!Interfaces)
+ return NULL;
+ LastError = CM_MapCrToWin32Err(
+ CM_Get_Device_Interface_ListW(
+ (GUID *)&GUID_DEVINTERFACE_NET,
+ (DEVINSTID_W)InstanceId,
+ Interfaces,
+ InterfacesLen,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT),
+ ERROR_GEN_FAILURE);
+ if (LastError != ERROR_SUCCESS)
+ {
+ LOG_ERROR(LastError, L"Failed to get adapter %s associated instances", InstanceId);
+ Free(Interfaces);
+ SetLastError(LastError);
+ return NULL;
+ }
+ if (!Interfaces[0])
+ {
+ Free(Interfaces);
+ SetLastError(ERROR_DEVICE_NOT_AVAILABLE);
+ return NULL;
+ }
+ return Interfaces;
+}
+
+typedef struct _WAIT_FOR_INTERFACE_CTX
+{
+ HANDLE Event;
+ DWORD LastError;
+} WAIT_FOR_INTERFACE_CTX;
+
+static VOID WINAPI
+WaitForInterfaceCallback(
+ _In_ HDEVQUERY DevQuery,
+ _Inout_ PVOID Context,
+ _In_ const DEV_QUERY_RESULT_ACTION_DATA *ActionData)
+{
+ WAIT_FOR_INTERFACE_CTX *Ctx = Context;
+ DWORD Ret = ERROR_SUCCESS;
+ switch (ActionData->Action)
+ {
+ case DevQueryResultStateChange:
+ if (ActionData->Data.State != DevQueryStateAborted)
+ return;
+ Ret = ERROR_DEVICE_NOT_AVAILABLE;
+ case DevQueryResultAdd:
+ case DevQueryResultUpdate:
+ break;
+ default:
+ return;
+ }
+ Ctx->LastError = Ret;
+ SetEvent(Ctx->Event);
+}
+
+_Must_inspect_result_
+static _Return_type_success_(return != FALSE)
+BOOL
+WaitForInterface(_In_ WCHAR *InstanceId)
+{
+ if (IsWindows7)
+ return TRUE;
+
+ DWORD LastError = ERROR_SUCCESS;
+ static const DEVPROP_BOOLEAN DevPropTrue = DEVPROP_TRUE;
+ const DEVPROP_FILTER_EXPRESSION Filters[] = { { .Operator = DEVPROP_OPERATOR_EQUALS_IGNORE_CASE,
+ .Property.CompKey.Key = DEVPKEY_Device_InstanceId,
+ .Property.CompKey.Store = DEVPROP_STORE_SYSTEM,
+ .Property.Type = DEVPROP_TYPE_STRING,
+ .Property.Buffer = InstanceId,
+ .Property.BufferSize =
+ (ULONG)((wcslen(InstanceId) + 1) * sizeof(InstanceId[0])) },
+ { .Operator = DEVPROP_OPERATOR_EQUALS,
+ .Property.CompKey.Key = DEVPKEY_DeviceInterface_Enabled,
+ .Property.CompKey.Store = DEVPROP_STORE_SYSTEM,
+ .Property.Type = DEVPROP_TYPE_BOOLEAN,
+ .Property.Buffer = (PVOID)&DevPropTrue,
+ .Property.BufferSize = sizeof(DevPropTrue) },
+ { .Operator = DEVPROP_OPERATOR_EQUALS,
+ .Property.CompKey.Key = DEVPKEY_DeviceInterface_ClassGuid,
+ .Property.CompKey.Store = DEVPROP_STORE_SYSTEM,
+ .Property.Type = DEVPROP_TYPE_GUID,
+ .Property.Buffer = (PVOID)&GUID_DEVINTERFACE_NET,
+ .Property.BufferSize = sizeof(GUID_DEVINTERFACE_NET) } };
+ WAIT_FOR_INTERFACE_CTX Ctx = { .Event = CreateEventW(NULL, FALSE, FALSE, NULL) };
+ if (!Ctx.Event)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create event");
+ goto cleanup;
+ }
+ HDEVQUERY Query;
+ HRESULT HRet = DevCreateObjectQuery(
+ DevObjectTypeDeviceInterface,
+ DevQueryFlagUpdateResults,
+ 0,
+ NULL,
+ _countof(Filters),
+ Filters,
+ WaitForInterfaceCallback,
+ &Ctx,
+ &Query);
+ if (FAILED(HRet))
+ {
+ LastError = LOG_ERROR(HRet, L"Failed to create device query");
+ goto cleanupEvent;
+ }
+ LastError = WaitForSingleObject(Ctx.Event, 15000);
+ if (LastError != WAIT_OBJECT_0)
+ {
+ if (LastError == WAIT_FAILED)
+ LastError = LOG_LAST_ERROR(L"Failed to wait for device query");
+ else
+ LastError = LOG_ERROR(LastError, L"Timed out waiting for device query");
+ goto cleanupQuery;
+ }
+ LastError = Ctx.LastError;
+ if (LastError != ERROR_SUCCESS)
+ LastError = LOG_ERROR(LastError, L"Failed to get enabled device");
+cleanupQuery:
+ DevCloseObjectQuery(Query);
+cleanupEvent:
+ CloseHandle(Ctx.Event);
+cleanup:
+ return RET_ERROR(TRUE, LastError);
+}
+
+typedef struct _SW_DEVICE_CREATE_CTX
+{
+ HRESULT CreateResult;
+ WCHAR *DeviceInstanceId;
+ HANDLE Triggered;
+} SW_DEVICE_CREATE_CTX;
+
+static VOID
+DeviceCreateCallback(
+ _In_ HSWDEVICE SwDevice,
+ _In_ HRESULT CreateResult,
+ _In_ VOID *Context,
+ _In_opt_ PCWSTR DeviceInstanceId)
+{
+ SW_DEVICE_CREATE_CTX *Ctx = Context;
+ Ctx->CreateResult = CreateResult;
+ if (DeviceInstanceId)
+ wcsncpy_s(Ctx->DeviceInstanceId, MAX_DEVICE_ID_LEN, DeviceInstanceId, _TRUNCATE);
+ SetEvent(Ctx->Triggered);
+}
+
+_Use_decl_annotations_
+WINTUN_ADAPTER_HANDLE WINAPI
+WintunCreateAdapter(LPCWSTR Name, LPCWSTR TunnelType, const GUID *RequestedGUID)
+{
+ DWORD LastError = ERROR_SUCCESS;
+ WINTUN_ADAPTER *Adapter = NULL;
+
+ HANDLE DeviceInstallationMutex = NamespaceTakeDeviceInstallationMutex();
+ if (!DeviceInstallationMutex)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to take device installation mutex");
+ goto cleanup;
+ }
+
+ HDEVINFO DevInfoExistingAdapters;
+ SP_DEVINFO_DATA_LIST *ExistingAdapters;
+ if (!DriverInstall(&DevInfoExistingAdapters, &ExistingAdapters))
+ {
+ LastError = GetLastError();
+ goto cleanupDeviceInstallationMutex;
+ }
+
+ LOG(WINTUN_LOG_INFO, L"Creating adapter");
+
+ Adapter = Zalloc(sizeof(*Adapter));
+ if (!Adapter)
+ goto cleanupDriverInstall;
+
+ WCHAR TunnelTypeName[MAX_ADAPTER_NAME + 8];
+ if (_snwprintf_s(TunnelTypeName, _countof(TunnelTypeName), _TRUNCATE, L"%s Tunnel", TunnelType) == -1)
+ {
+ LastError = ERROR_BUFFER_OVERFLOW;
+ goto cleanupAdapter;
+ }
+
+ DEVINST RootNode;
+ WCHAR RootNodeName[200 /* rasmans.dll uses 200 hard coded instead of calling CM_Get_Device_ID_Size. */];
+ CONFIGRET ConfigRet;
+ if ((ConfigRet = CM_Locate_DevNodeW(&RootNode, NULL, CM_LOCATE_DEVNODE_NORMAL)) != CR_SUCCESS ||
+ (ConfigRet = CM_Get_Device_IDW(RootNode, RootNodeName, _countof(RootNodeName), 0)) != CR_SUCCESS)
+ {
+ LastError = LOG_ERROR(CM_MapCrToWin32Err(ConfigRet, ERROR_GEN_FAILURE), L"Failed to get root node name");
+ goto cleanupAdapter;
+ }
+
+ GUID InstanceId;
+ HRESULT HRet = S_OK;
+ if (RequestedGUID)
+ memcpy(&InstanceId, RequestedGUID, sizeof(InstanceId));
+ else
+ HRet = CoCreateGuid(&InstanceId);
+ WCHAR InstanceIdStr[MAX_GUID_STRING_LEN];
+ if (FAILED(HRet) || !StringFromGUID2(&InstanceId, InstanceIdStr, _countof(InstanceIdStr)))
+ {
+ LastError = LOG_ERROR(HRet, L"Failed to convert GUID");
+ goto cleanupAdapter;
+ }
+ SW_DEVICE_CREATE_CTX CreateContext = { .DeviceInstanceId = Adapter->DevInstanceID,
+ .Triggered = CreateEventW(NULL, FALSE, FALSE, NULL) };
+ if (!CreateContext.Triggered)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create event trigger");
+ goto cleanupAdapter;
+ }
+
+ if (IsWindows7)
+ {
+ if (!CreateAdapterWin7(Adapter, Name, TunnelTypeName))
+ {
+ LastError = GetLastError();
+ goto cleanupCreateContext;
+ }
+ goto skipSwDevice;
+ }
+ if (!IsWindows10)
+ goto skipStub;
+
+ SW_DEVICE_CREATE_INFO StubCreateInfo = { .cbSize = sizeof(StubCreateInfo),
+ .pszInstanceId = InstanceIdStr,
+ .pszzHardwareIds = L"",
+ .CapabilityFlags =
+ SWDeviceCapabilitiesSilentInstall | SWDeviceCapabilitiesDriverRequired,
+ .pszDeviceDescription = TunnelTypeName };
+ DEVPROPERTY StubDeviceProperties[] = { { .CompKey = { .Key = DEVPKEY_Device_ClassGuid,
+ .Store = DEVPROP_STORE_SYSTEM },
+ .Type = DEVPROP_TYPE_GUID,
+ .Buffer = (PVOID)&GUID_DEVCLASS_NET,
+ .BufferSize = sizeof(GUID_DEVCLASS_NET) } };
+ HRet = SwDeviceCreate(
+ WINTUN_HWID,
+ RootNodeName,
+ &StubCreateInfo,
+ _countof(StubDeviceProperties),
+ StubDeviceProperties,
+ DeviceCreateCallback,
+ &CreateContext,
+ &Adapter->SwDevice);
+ if (FAILED(HRet))
+ {
+ LastError = LOG_ERROR(HRet, L"Failed to initiate stub device creation");
+ goto cleanupCreateContext;
+ }
+ if (WaitForSingleObject(CreateContext.Triggered, INFINITE) != WAIT_OBJECT_0)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to wait for stub device creation trigger");
+ goto cleanupCreateContext;
+ }
+ if (FAILED(CreateContext.CreateResult))
+ {
+ LastError = LOG_ERROR(CreateContext.CreateResult, L"Failed to create stub device");
+ goto cleanupCreateContext;
+ }
+ DEVINST DevInst;
+ CONFIGRET CRet = CM_Locate_DevNodeW(&DevInst, Adapter->DevInstanceID, CM_LOCATE_DEVNODE_PHANTOM);
+ if (CRet != CR_SUCCESS)
+ {
+ LastError =
+ LOG_ERROR(CM_MapCrToWin32Err(CRet, ERROR_DEVICE_ENUMERATION_ERROR), L"Failed to make stub device list");
+ goto cleanupCreateContext;
+ }
+ HKEY DriverKey;
+ CRet = CM_Open_DevNode_Key(DevInst, KEY_SET_VALUE, 0, RegDisposition_OpenAlways, &DriverKey, CM_REGISTRY_SOFTWARE);
+ if (CRet != CR_SUCCESS)
+ {
+ LastError =
+ LOG_ERROR(CM_MapCrToWin32Err(CRet, ERROR_PNP_REGISTRY_ERROR), L"Failed to create software registry key");
+ goto cleanupCreateContext;
+ }
+ LastError =
+ RegSetValueExW(DriverKey, L"SuggestedInstanceId", 0, REG_BINARY, (const BYTE *)&InstanceId, sizeof(InstanceId));
+ RegCloseKey(DriverKey);
+ if (LastError != ERROR_SUCCESS)
+ {
+ LastError = LOG_ERROR(LastError, L"Failed to set SuggestedInstanceId to %s", InstanceIdStr);
+ goto cleanupCreateContext;
+ }
+ SwDeviceClose(Adapter->SwDevice);
+ Adapter->SwDevice = NULL;
+
+skipStub:;
+ static const WCHAR Hwids[_countof(WINTUN_HWID) + 1 /*Multi-string terminator*/] = WINTUN_HWID;
+ SW_DEVICE_CREATE_INFO CreateInfo = { .cbSize = sizeof(CreateInfo),
+ .pszInstanceId = InstanceIdStr,
+ .pszzHardwareIds = Hwids,
+ .CapabilityFlags =
+ SWDeviceCapabilitiesSilentInstall | SWDeviceCapabilitiesDriverRequired,
+ .pszDeviceDescription = TunnelTypeName };
+ DEVPROPERTY DeviceProperties[] = {
+ { .CompKey = { .Key = DEVPKEY_Wintun_Name, .Store = DEVPROP_STORE_SYSTEM },
+ .Type = DEVPROP_TYPE_STRING,
+ .Buffer = (WCHAR *)Name,
+ .BufferSize = (ULONG)((wcslen(Name) + 1) * sizeof(*Name)) },
+ { .CompKey = { .Key = DEVPKEY_Device_FriendlyName, .Store = DEVPROP_STORE_SYSTEM },
+ .Type = DEVPROP_TYPE_STRING,
+ .Buffer = TunnelTypeName,
+ .BufferSize = (ULONG)((wcslen(TunnelTypeName) + 1) * sizeof(*TunnelTypeName)) },
+ { .CompKey = { .Key = DEVPKEY_Device_DeviceDesc, .Store = DEVPROP_STORE_SYSTEM },
+ .Type = DEVPROP_TYPE_STRING,
+ .Buffer = TunnelTypeName,
+ .BufferSize = (ULONG)((wcslen(TunnelTypeName) + 1) * sizeof(*TunnelTypeName)) }
+ };
+
+ HRet = SwDeviceCreate(
+ WINTUN_HWID,
+ RootNodeName,
+ &CreateInfo,
+ _countof(DeviceProperties),
+ DeviceProperties,
+ DeviceCreateCallback,
+ &CreateContext,
+ &Adapter->SwDevice);
+ if (FAILED(HRet))
+ {
+ LastError = LOG_ERROR(HRet, L"Failed to initiate device creation");
+ goto cleanupCreateContext;
+ }
+ if (WaitForSingleObject(CreateContext.Triggered, INFINITE) != WAIT_OBJECT_0)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to wait for device creation trigger");
+ goto cleanupCreateContext;
+ }
+ if (FAILED(CreateContext.CreateResult))
+ {
+ LastError = LOG_ERROR(CreateContext.CreateResult, L"Failed to create device");
+ goto cleanupCreateContext;
+ }
+
+ if (!WaitForInterface(Adapter->DevInstanceID))
+ {
+ LastError = GetLastError();
+ DEVPROPTYPE PropertyType = 0;
+ NTSTATUS NtStatus = 0;
+ INT32 ProblemCode = 0;
+ Adapter->DevInfo = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL);
+ if (Adapter->DevInfo == INVALID_HANDLE_VALUE)
+ {
+ Adapter->DevInfo = NULL;
+ goto cleanupCreateContext;
+ }
+ Adapter->DevInfoData.cbSize = sizeof(Adapter->DevInfoData);
+ if (!SetupDiOpenDeviceInfoW(
+ Adapter->DevInfo, Adapter->DevInstanceID, NULL, DIOD_INHERIT_CLASSDRVS, &Adapter->DevInfoData))
+ {
+ SetupDiDestroyDeviceInfoList(Adapter->DevInfo);
+ Adapter->DevInfo = NULL;
+ goto cleanupCreateContext;
+ }
+ if (!SetupDiGetDevicePropertyW(
+ Adapter->DevInfo,
+ &Adapter->DevInfoData,
+ &DEVPKEY_Device_ProblemStatus,
+ &PropertyType,
+ (PBYTE)&NtStatus,
+ sizeof(NtStatus),
+ NULL,
+ 0) ||
+ PropertyType != DEVPROP_TYPE_NTSTATUS)
+ NtStatus = 0;
+ if (!SetupDiGetDevicePropertyW(
+ Adapter->DevInfo,
+ &Adapter->DevInfoData,
+ &DEVPKEY_Device_ProblemCode,
+ &PropertyType,
+ (PBYTE)&ProblemCode,
+ sizeof(ProblemCode),
+ NULL,
+ 0) ||
+ (PropertyType != DEVPROP_TYPE_INT32 && PropertyType != DEVPROP_TYPE_UINT32))
+ ProblemCode = 0;
+ LastError = RtlNtStatusToDosError(NtStatus);
+ if (LastError == ERROR_SUCCESS)
+ LastError = ERROR_DEVICE_NOT_AVAILABLE;
+ LOG_ERROR(LastError, L"Failed to setup adapter (problem code: 0x%X, ntstatus: 0x%X)", ProblemCode, NtStatus);
+ goto cleanupCreateContext;
+ }
+
+skipSwDevice:
+ Adapter->DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL);
+ if (Adapter->DevInfo == INVALID_HANDLE_VALUE)
+ {
+ Adapter->DevInfo = NULL;
+ LastError = LOG_LAST_ERROR(L"Failed to make device list");
+ goto cleanupCreateContext;
+ }
+ Adapter->DevInfoData.cbSize = sizeof(Adapter->DevInfoData);
+ if (!SetupDiOpenDeviceInfoW(
+ Adapter->DevInfo, Adapter->DevInstanceID, NULL, DIOD_INHERIT_CLASSDRVS, &Adapter->DevInfoData))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to open device instance ID %s", Adapter->DevInstanceID);
+ SetupDiDestroyDeviceInfoList(Adapter->DevInfo);
+ Adapter->DevInfo = NULL;
+ goto cleanupCreateContext;
+ }
+
+ if (!PopulateAdapterData(Adapter))
+ {
+ LastError = LOG(WINTUN_LOG_ERR, L"Failed to populate adapter data");
+ goto cleanupCreateContext;
+ }
+
+ if (!NciSetAdapterName(&Adapter->CfgInstanceID, Name))
+ {
+ LastError = LOG(WINTUN_LOG_ERR, L"Failed to set adapter name \"%s\"", Name);
+ goto cleanupCreateContext;
+ }
+
+ if (IsWindows7)
+ CreateAdapterPostWin7(Adapter, TunnelTypeName);
+
+cleanupCreateContext:
+ CloseHandle(CreateContext.Triggered);
+cleanupAdapter:
+ if (LastError != ERROR_SUCCESS)
+ {
+ WintunCloseAdapter(Adapter);
+ Adapter = NULL;
+ }
+cleanupDriverInstall:
+ DriverInstallDeferredCleanup(DevInfoExistingAdapters, ExistingAdapters);
+cleanupDeviceInstallationMutex:
+ NamespaceReleaseMutex(DeviceInstallationMutex);
+cleanup:
+ QueueUpOrphanedDeviceCleanupRoutine();
+ return RET_ERROR(Adapter, LastError);
+}
+
+_Use_decl_annotations_
+WINTUN_ADAPTER_HANDLE WINAPI
+WintunOpenAdapter(LPCWSTR Name)
+{
+ DWORD LastError = ERROR_SUCCESS;
+ WINTUN_ADAPTER *Adapter = NULL;
+
+ HANDLE DeviceInstallationMutex = NamespaceTakeDeviceInstallationMutex();
+ if (!DeviceInstallationMutex)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to take device installation mutex");
+ goto cleanup;
+ }
+
+ Adapter = Zalloc(sizeof(*Adapter));
+ if (!Adapter)
+ goto cleanupDeviceInstallationMutex;
+
+ HDEVINFO DevInfo =
+ SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to get present adapters");
+ goto cleanupAdapter;
+ }
+
+ SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) };
+ BOOL Found = FALSE;
+ for (DWORD EnumIndex = 0; !Found; ++EnumIndex)
+ {
+ if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DevInfoData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ DEVPROPTYPE PropType;
+ WCHAR OtherName[MAX_ADAPTER_NAME];
+ Found = SetupDiGetDevicePropertyW(
+ DevInfo,
+ &DevInfoData,
+ &DEVPKEY_Wintun_Name,
+ &PropType,
+ (PBYTE)OtherName,
+ MAX_ADAPTER_NAME * sizeof(OtherName[0]),
+ NULL,
+ 0) &&
+ PropType == DEVPROP_TYPE_STRING && !_wcsicmp(Name, OtherName);
+ }
+ if (!Found)
+ {
+ LastError = LOG_ERROR(ERROR_NOT_FOUND, L"Failed to find matching adapter name");
+ goto cleanupDevInfo;
+ }
+ DWORD RequiredChars = _countof(Adapter->DevInstanceID);
+ if (!SetupDiGetDeviceInstanceIdW(DevInfo, &DevInfoData, Adapter->DevInstanceID, RequiredChars, &RequiredChars))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to get adapter instance ID");
+ goto cleanupDevInfo;
+ }
+ Adapter->DevInfo = DevInfo;
+ Adapter->DevInfoData = DevInfoData;
+ BOOL Ret = WaitForInterface(Adapter->DevInstanceID) && PopulateAdapterData(Adapter);
+ Adapter->DevInfo = NULL;
+ if (!Ret)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to populate adapter");
+ goto cleanupDevInfo;
+ }
+
+cleanupDevInfo:
+ SetupDiDestroyDeviceInfoList(DevInfo);
+cleanupAdapter:
+ if (LastError != ERROR_SUCCESS)
+ {
+ WintunCloseAdapter(Adapter);
+ Adapter = NULL;
+ }
+cleanupDeviceInstallationMutex:
+ NamespaceReleaseMutex(DeviceInstallationMutex);
+cleanup:
+ QueueUpOrphanedDeviceCleanupRoutine();
+ return RET_ERROR(Adapter, LastError);
+}
+
+_Use_decl_annotations_
+BOOL
+AdapterRemoveInstance(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
+{
+#ifdef MAYBE_WOW64
+ if (NativeMachine != IMAGE_FILE_PROCESS)
+ return RemoveInstanceViaRundll32(DevInfo, DevInfoData);
+#endif
+
+ SP_REMOVEDEVICE_PARAMS RemoveDeviceParams = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
+ .InstallFunction = DIF_REMOVE },
+ .Scope = DI_REMOVEDEVICE_GLOBAL };
+ return SetupDiSetClassInstallParamsW(
+ DevInfo, DevInfoData, &RemoveDeviceParams.ClassInstallHeader, sizeof(RemoveDeviceParams)) &&
+ SetupDiCallClassInstaller(DIF_REMOVE, DevInfo, DevInfoData);
+}
+
+_Use_decl_annotations_
+BOOL
+AdapterEnableInstance(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
+{
+#ifdef MAYBE_WOW64
+ if (NativeMachine != IMAGE_FILE_PROCESS)
+ return EnableInstanceViaRundll32(DevInfo, DevInfoData);
+#endif
+
+ SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
+ .InstallFunction = DIF_PROPERTYCHANGE },
+ .StateChange = DICS_ENABLE,
+ .Scope = DICS_FLAG_GLOBAL };
+ return SetupDiSetClassInstallParamsW(DevInfo, DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) &&
+ SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, DevInfoData);
+}
+
+_Use_decl_annotations_
+BOOL
+AdapterDisableInstance(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
+{
+#ifdef MAYBE_WOW64
+ if (NativeMachine != IMAGE_FILE_PROCESS)
+ return DisableInstanceViaRundll32(DevInfo, DevInfoData);
+#endif
+ SP_PROPCHANGE_PARAMS Params = { .ClassInstallHeader = { .cbSize = sizeof(SP_CLASSINSTALL_HEADER),
+ .InstallFunction = DIF_PROPERTYCHANGE },
+ .StateChange = DICS_DISABLE,
+ .Scope = DICS_FLAG_GLOBAL };
+ return SetupDiSetClassInstallParamsW(DevInfo, DevInfoData, &Params.ClassInstallHeader, sizeof(Params)) &&
+ SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, DevInfo, DevInfoData);
+}