aboutsummaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/adapter.c950
-rw-r--r--api/adapter.h137
-rw-r--r--api/adapter_win7.h351
-rw-r--r--api/api.vcxproj95
-rw-r--r--api/api.vcxproj.filters97
-rw-r--r--api/driver.c519
-rw-r--r--api/driver.h34
-rw-r--r--api/exports.def16
-rw-r--r--api/extract-driverver.js17
-rw-r--r--api/logger.c134
-rw-r--r--api/logger.h175
-rw-r--r--api/main.c134
-rw-r--r--api/main.h38
-rw-r--r--api/namespace.c154
-rw-r--r--api/namespace.h30
-rw-r--r--api/nci.def4
-rw-r--r--api/nci.h31
-rw-r--r--api/ntdll.h62
-rw-r--r--api/registry.c157
-rw-r--r--api/registry.h79
-rw-r--r--api/resource.c129
-rw-r--r--api/resource.h50
-rw-r--r--api/resources.rc65
-rw-r--r--api/rundll32.c398
-rw-r--r--api/rundll32.h26
-rw-r--r--api/session.c309
-rw-r--r--api/wintun.h274
27 files changed, 4465 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);
+}
diff --git a/api/adapter.h b/api/adapter.h
new file mode 100644
index 0000000..cfa22fd
--- /dev/null
+++ b/api/adapter.h
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include "wintun.h"
+#include <IPExport.h>
+#include <SetupAPI.h>
+#include <cfgmgr32.h>
+#include <Windows.h>
+
+#define WINTUN_HWID L"Wintun"
+#define WINTUN_ENUMERATOR (IsWindows7 ? L"ROOT\\" WINTUN_HWID : L"SWD\\" WINTUN_HWID)
+
+extern const DEVPROPKEY DEVPKEY_Wintun_Name;
+
+typedef struct HSWDEVICE__ *HSWDEVICE;
+
+/**
+ * Wintun adapter descriptor.
+ */
+typedef struct _WINTUN_ADAPTER
+{
+ HSWDEVICE SwDevice;
+ HDEVINFO DevInfo;
+ SP_DEVINFO_DATA DevInfoData;
+ WCHAR *InterfaceFilename;
+ GUID CfgInstanceID;
+ WCHAR DevInstanceID[MAX_DEVICE_ID_LEN];
+ DWORD LuidIndex;
+ DWORD IfType;
+ DWORD IfIndex;
+} WINTUN_ADAPTER;
+/**
+ * @copydoc WINTUN_CREATE_ADAPTER_FUNC
+ */
+WINTUN_CREATE_ADAPTER_FUNC WintunCreateAdapter;
+
+/**
+ * @copydoc WINTUN_OPEN_ADAPTER_FUNC
+ */
+WINTUN_OPEN_ADAPTER_FUNC WintunOpenAdapter;
+
+/**
+ * @copydoc WINTUN_CLOSE_ADAPTER_FUNC
+ */
+WINTUN_CLOSE_ADAPTER_FUNC WintunCloseAdapter;
+
+/**
+ * @copydoc WINTUN_GET_ADAPTER_LUID_FUNC
+ */
+WINTUN_GET_ADAPTER_LUID_FUNC WintunGetAdapterLUID;
+
+/**
+ * Returns a handle to the adapter device object.
+ *
+ * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter.
+ *
+ * @return If the function succeeds, the return value is adapter device object handle.
+ * If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error
+ * information, call GetLastError.
+ */
+_Return_type_success_(return != INVALID_HANDLE_VALUE)
+HANDLE WINAPI
+AdapterOpenDeviceObject(_In_ const WINTUN_ADAPTER *Adapter);
+
+/**
+ * Returns the device object file name for an adapter instance ID.
+ *
+ * @param InstanceID The device instance ID of the adapter.
+ *
+ * @return If the function succeeds, the return value is the filename of the device object, which
+ * must be freed with Free(). If the function fails, the return value is INVALID_HANDLE_VALUE.
+ * To get extended error information, call GetLastError.
+ */
+_Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+LPWSTR
+AdapterGetDeviceObjectFileName(_In_z_ LPCWSTR InstanceId);
+
+/**
+ * Cleans up adapters with no attached process.
+ */
+VOID AdapterCleanupOrphanedDevices(VOID);
+
+/**
+ * Cleans up adapters that use the old enumerator.
+ */
+VOID AdapterCleanupLegacyDevices(VOID);
+
+/**
+ * Removes the specified device instance.
+ *
+ * @param DevInfo Device info handle from SetupAPI.
+ * @param DevInfoData Device info data specifying which device.
+ *
+ * @return If the function succeeds, the return value is TRUE. If the
+ * function fails, the return value is FALSE. To get extended
+ * error information, call GetLastError.
+ */
+
+_Return_type_success_(return != FALSE)
+BOOL
+AdapterRemoveInstance(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData);
+
+/**
+ * Enables the specified device instance.
+ *
+ * @param DevInfo Device info handle from SetupAPI.
+ * @param DevInfoData Device info data specifying which device.
+ *
+ * @return If the function succeeds, the return value is TRUE. If the
+ * function fails, the return value is FALSE. To get extended
+ * error information, call GetLastError.
+ */
+
+_Return_type_success_(return != FALSE)
+BOOL
+AdapterEnableInstance(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData);
+
+/**
+ * Disables the specified device instance.
+ *
+ * @param DevInfo Device info handle from SetupAPI.
+ * @param DevInfoData Device info data specifying which device.
+ *
+ * @return If the function succeeds, the return value is TRUE. If the
+ * function fails, the return value is FALSE. To get extended
+ * error information, call GetLastError.
+ */
+
+_Return_type_success_(return != FALSE)
+BOOL
+AdapterDisableInstance(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData);
diff --git a/api/adapter_win7.h b/api/adapter_win7.h
new file mode 100644
index 0000000..affbd09
--- /dev/null
+++ b/api/adapter_win7.h
@@ -0,0 +1,351 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+static const DEVPROPKEY DEVPKEY_Wintun_OwningProcess = {
+ { 0x3361c968, 0x2f2e, 0x4660, { 0xb4, 0x7e, 0x69, 0x9c, 0xdc, 0x4c, 0x32, 0xb9 } },
+ DEVPROPID_FIRST_USABLE + 3
+};
+
+typedef struct _OWNING_PROCESS
+{
+ DWORD ProcessId;
+ FILETIME CreationTime;
+} OWNING_PROCESS;
+
+_Must_inspect_result_
+static _Return_type_success_(return != FALSE)
+BOOL
+WaitForInterfaceWin7(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData, _In_ LPCWSTR DevInstanceId)
+{
+ ULONG Status, Number;
+ DWORD ValType, Zero;
+ WCHAR *FileName = NULL;
+ HKEY Key = INVALID_HANDLE_VALUE;
+ HANDLE FileHandle = INVALID_HANDLE_VALUE;
+ BOOLEAN Ret = FALSE;
+ for (DWORD Tries = 0; Tries < 1500; ++Tries)
+ {
+ if (Tries)
+ Sleep(10);
+ if (Key == INVALID_HANDLE_VALUE)
+ Key = SetupDiOpenDevRegKey(DevInfo, DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
+ if (!FileName)
+ FileName = AdapterGetDeviceObjectFileName(DevInstanceId);
+ if (FileName && FileHandle == INVALID_HANDLE_VALUE)
+ FileHandle = CreateFileW(
+ FileName,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ Zero = 0;
+ if (FileName && FileHandle != INVALID_HANDLE_VALUE && Key != INVALID_HANDLE_VALUE && Key &&
+ RegQueryValueExW(Key, L"NetCfgInstanceId", NULL, &ValType, NULL, &Zero) != ERROR_MORE_DATA &&
+ CM_Get_DevNode_Status(&Status, &Number, DevInfoData->DevInst, 0) == CR_SUCCESS &&
+ !(Status & DN_HAS_PROBLEM) && !Number)
+ {
+ Ret = TRUE;
+ break;
+ }
+ }
+ if (Key != INVALID_HANDLE_VALUE && Key)
+ RegCloseKey(Key);
+ if (FileHandle != INVALID_HANDLE_VALUE)
+ CloseHandle(FileHandle);
+ Free(FileName);
+ return Ret;
+}
+
+_Must_inspect_result_
+static _Return_type_success_(return != FALSE)
+BOOL
+CreateAdapterWin7(_Inout_ WINTUN_ADAPTER *Adapter, _In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelTypeName)
+{
+ DWORD LastError = ERROR_SUCCESS;
+
+ HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create empty device information set");
+ goto cleanup;
+ }
+ SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) };
+
+#ifdef MAYBE_WOW64
+ if (NativeMachine != IMAGE_FILE_PROCESS)
+ {
+ if (!CreateInstanceWin7ViaRundll32(Adapter->DevInstanceID))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create device instance");
+ goto cleanup;
+ }
+ if (!SetupDiOpenDeviceInfoW(DevInfo, Adapter->DevInstanceID, NULL, DIOD_INHERIT_CLASSDRVS, &DevInfoData))
+ {
+ LastError = GetLastError();
+ goto cleanupDevInfo;
+ }
+ goto resumeAfterInstance;
+ }
+#endif
+
+ if (!SetupDiCreateDeviceInfoW(
+ DevInfo, WINTUN_HWID, &GUID_DEVCLASS_NET, TunnelTypeName, NULL, DICD_GENERATE_ID, &DevInfoData))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create new device information element");
+ goto cleanupDevInfo;
+ }
+ SP_DEVINSTALL_PARAMS_W DevInstallParams = { .cbSize = sizeof(DevInstallParams) };
+ if (!SetupDiGetDeviceInstallParamsW(DevInfo, &DevInfoData, &DevInstallParams))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to retrieve adapter device installation parameters");
+ goto cleanupDevInfo;
+ }
+ DevInstallParams.Flags |= DI_QUIETINSTALL;
+ if (!SetupDiSetDeviceInstallParamsW(DevInfo, &DevInfoData, &DevInstallParams))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to set adapter device installation parameters");
+ goto cleanupDevInfo;
+ }
+ if (!SetupDiSetSelectedDevice(DevInfo, &DevInfoData))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to select adapter 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)))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to set adapter hardware ID");
+ goto cleanupDevInfo;
+ }
+ if (!SetupDiBuildDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed building adapter driver info list");
+ goto cleanupDevInfo;
+ }
+ SP_DRVINFO_DATA_W DrvInfoData = { .cbSize = sizeof(SP_DRVINFO_DATA_W) };
+ if (!SetupDiEnumDriverInfoW(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER, 0, &DrvInfoData) ||
+ !SetupDiSetSelectedDriverW(DevInfo, &DevInfoData, &DrvInfoData))
+ {
+ LastError = LOG_ERROR(ERROR_DRIVER_INSTALL_BLOCKED, L"Failed to select a driver");
+ goto cleanupDriverInfo;
+ }
+
+ if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, DevInfo, &DevInfoData))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to register adapter device");
+ goto cleanupDevInfo;
+ }
+ if (!SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, DevInfo, &DevInfoData))
+ LOG_LAST_ERROR(L"Failed to register adapter coinstallers");
+ if (!SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, DevInfo, &DevInfoData))
+ LOG_LAST_ERROR(L"Failed to install adapter interfaces");
+ if (!SetupDiCallClassInstaller(DIF_INSTALLDEVICE, DevInfo, &DevInfoData))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to install adapter device");
+ goto cleanupDevice;
+ }
+
+#ifdef MAYBE_WOW64
+resumeAfterInstance:;
+#endif
+
+ OWNING_PROCESS OwningProcess = { .ProcessId = GetCurrentProcessId() };
+ FILETIME Unused;
+ if (!GetProcessTimes(GetCurrentProcess(), &OwningProcess.CreationTime, &Unused, &Unused, &Unused))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to get process creation time");
+ goto cleanupDevice;
+ }
+
+ if (!SetupDiSetDeviceRegistryPropertyW(
+ DevInfo,
+ &DevInfoData,
+ SPDRP_FRIENDLYNAME,
+ (PBYTE)TunnelTypeName,
+ (DWORD)((wcslen(TunnelTypeName) + 1) * sizeof(TunnelTypeName[0]))) ||
+ !SetupDiSetDeviceRegistryPropertyW(
+ DevInfo,
+ &DevInfoData,
+ SPDRP_DEVICEDESC,
+ (PBYTE)TunnelTypeName,
+ (DWORD)((wcslen(TunnelTypeName) + 1) * sizeof(TunnelTypeName[0]))) ||
+ !SetupDiSetDevicePropertyW(
+ DevInfo,
+ &DevInfoData,
+ &DEVPKEY_Wintun_Name,
+ DEVPROP_TYPE_STRING,
+ (PBYTE)Name,
+ (DWORD)((wcslen(Name) + 1) * sizeof(Name[0])),
+ 0) ||
+ !SetupDiSetDevicePropertyW(
+ DevInfo,
+ &DevInfoData,
+ &DEVPKEY_Wintun_OwningProcess,
+ DEVPROP_TYPE_BINARY,
+ (PBYTE)&OwningProcess,
+ sizeof(OwningProcess),
+ 0))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to set device properties");
+ goto cleanupDevice;
+ }
+
+ 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 cleanupDevice;
+ }
+
+ if (!WaitForInterfaceWin7(DevInfo, &DevInfoData, Adapter->DevInstanceID))
+ {
+ DEVPROPTYPE PropertyType = 0;
+ INT32 ProblemCode = 0;
+ if (!SetupDiGetDevicePropertyW(
+ DevInfo,
+ &DevInfoData,
+ &DEVPKEY_Device_ProblemCode,
+ &PropertyType,
+ (PBYTE)&ProblemCode,
+ sizeof(ProblemCode),
+ NULL,
+ 0) ||
+ (PropertyType != DEVPROP_TYPE_INT32 && PropertyType != DEVPROP_TYPE_UINT32))
+ ProblemCode = 0;
+ LastError = LOG_ERROR(
+ ERROR_DEVICE_REINITIALIZATION_NEEDED, L"Failed to setup adapter (problem code: 0x%x)", ProblemCode);
+ goto cleanupDevice;
+ }
+
+cleanupDevice:
+ if (LastError != ERROR_SUCCESS)
+ AdapterRemoveInstance(DevInfo, &DevInfoData);
+cleanupDriverInfo:
+ SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER);
+cleanupDevInfo:
+ SetupDiDestroyDeviceInfoList(DevInfo);
+cleanup:
+ return RET_ERROR(TRUE, LastError);
+}
+
+static VOID
+CreateAdapterPostWin7(_Inout_ WINTUN_ADAPTER *Adapter, _In_z_ LPCWSTR TunnelTypeName)
+{
+ SetupDiSetDeviceRegistryPropertyW(
+ Adapter->DevInfo,
+ &Adapter->DevInfoData,
+ SPDRP_FRIENDLYNAME,
+ (PBYTE)TunnelTypeName,
+ (DWORD)((wcslen(TunnelTypeName) + 1) * sizeof(TunnelTypeName[0])));
+ SetupDiSetDeviceRegistryPropertyW(
+ Adapter->DevInfo,
+ &Adapter->DevInfoData,
+ SPDRP_DEVICEDESC,
+ (PBYTE)TunnelTypeName,
+ (DWORD)((wcslen(TunnelTypeName) + 1) * sizeof(TunnelTypeName[0])));
+}
+
+static BOOL
+ProcessIsStale(_In_ OWNING_PROCESS *OwningProcess)
+{
+ HANDLE Process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, OwningProcess->ProcessId);
+ if (!Process)
+ return TRUE;
+ FILETIME CreationTime, Unused;
+ BOOL Ret = GetProcessTimes(Process, &CreationTime, &Unused, &Unused, &Unused);
+ CloseHandle(Process);
+ if (!Ret)
+ return FALSE;
+ return !!memcmp(&CreationTime, &OwningProcess->CreationTime, sizeof(CreationTime));
+}
+
+VOID AdapterCleanupOrphanedDevicesWin7(VOID)
+{
+ HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, 0, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ {
+ if (GetLastError() != ERROR_INVALID_DATA)
+ LOG_LAST_ERROR(L"Failed to get adapters");
+ return;
+ }
+
+ 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;
+ }
+
+ OWNING_PROCESS OwningProcess;
+ DEVPROPTYPE PropType;
+ if (SetupDiGetDevicePropertyW(
+ DevInfo,
+ &DevInfoData,
+ &DEVPKEY_Wintun_OwningProcess,
+ &PropType,
+ (PBYTE)&OwningProcess,
+ sizeof(OwningProcess),
+ NULL,
+ 0) &&
+ PropType == DEVPROP_TYPE_BINARY && !ProcessIsStale(&OwningProcess))
+ continue;
+
+ 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);
+}
+
+VOID AdapterCleanupLegacyDevices(VOID)
+{
+ HDEVINFO DevInfo = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, L"ROOT\\NET", NULL, 0, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ return;
+ 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;
+ }
+ WCHAR HardwareIDs[0x400] = { 0 };
+ DWORD ValueType, Size = sizeof(HardwareIDs) - sizeof(HardwareIDs[0]);
+ if (!SetupDiGetDeviceRegistryPropertyW(
+ DevInfo, &DevInfoData, SPDRP_HARDWAREID, &ValueType, (PBYTE)HardwareIDs, Size, &Size) ||
+ Size > sizeof(HardwareIDs) - sizeof(HardwareIDs[0]))
+ continue;
+ Size /= sizeof(HardwareIDs[0]);
+ for (WCHAR *P = HardwareIDs; P < HardwareIDs + Size; P += wcslen(P) + 1)
+ {
+ if (!_wcsicmp(P, WINTUN_HWID))
+ {
+ AdapterRemoveInstance(DevInfo, &DevInfoData);
+ break;
+ }
+ }
+ }
+ SetupDiDestroyDeviceInfoList(DevInfo);
+} \ No newline at end of file
diff --git a/api/api.vcxproj b/api/api.vcxproj
new file mode 100644
index 0000000..d0d0dcb
--- /dev/null
+++ b/api/api.vcxproj
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{897F02E3-3EAA-40AF-A6DC-17EB2376EDAF}</ProjectGuid>
+ <RootNamespace>api</RootNamespace>
+ <ProjectName>api</ProjectName>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="..\wintun.props" />
+ <PropertyGroup>
+ <TargetName>wintun</TargetName>
+ <IgnoreImportLibrary>true</IgnoreImportLibrary>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <PreprocessorDefinitions>_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Platform)'=='Win32'">MAYBE_WOW64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Platform)'=='x64'">MAYBE_WOW64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Platform)'=='ARM'">MAYBE_WOW64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Platform)'=='ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalOptions>/volatile:iso %(AdditionalOptions)</AdditionalOptions>
+ <DisableSpecificWarnings>4100;4201;$(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>..\$(Configuration)\$(WintunPlatform);..\$(Configuration);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions Condition="Exists('..\$(Configuration)\arm64\driver\wintun.sys') And Exists('..\$(Configuration)\arm64\setupapihost.dll')">BUILT_ARM64_WOW64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="Exists('..\$(Configuration)\amd64\driver\wintun.sys') And Exists('..\$(Configuration)\amd64\setupapihost.dll')">BUILT_AMD64_WOW64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Platform)'=='Win32'">WANT_ARM64_WOW64;WANT_AMD64_WOW64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Platform)'=='x64'">WANT_ARM64_WOW64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Platform)'=='ARM'">WANT_ARM64_WOW64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <DelayLoadDLLs>advapi32.dll;api-ms-win-devices-query-l1-1-0.dll;api-ms-win-devices-swdevice-l1-1-0.dll;cfgmgr32.dll;iphlpapi.dll;ole32.dll;nci.dll;setupapi.dll;shlwapi.dll;version.dll</DelayLoadDLLs>
+ <DelayLoadDLLs Condition="'$(Platform)'!='ARM64'">shell32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <AdditionalDependencies>Cfgmgr32.lib;Iphlpapi.lib;onecore.lib;$(IntDir)nci.lib;ntdll.lib;Setupapi.lib;shlwapi.lib;swdevice.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resources.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="exports.def" />
+ <None Include="nci.def" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="adapter_win7.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="adapter.h" />
+ <ClInclude Include="driver.h" />
+ <ClInclude Include="logger.h" />
+ <ClInclude Include="namespace.h" />
+ <ClInclude Include="nci.h" />
+ <ClInclude Include="ntdll.h" />
+ <ClInclude Include="registry.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="rundll32.h" />
+ <ClInclude Include="wintun.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="main.c" />
+ <ClCompile Include="adapter.c" />
+ <ClCompile Include="driver.c" />
+ <ClCompile Include="logger.c" />
+ <ClCompile Include="namespace.c" />
+ <ClCompile Include="registry.c" />
+ <ClCompile Include="resource.c" />
+ <ClCompile Include="session.c" />
+ <ClCompile Include="rundll32.c" />
+ </ItemGroup>
+ <Import Project="..\wintun.props.user" Condition="exists('..\wintun.props.user')" />
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets" />
+ <PropertyGroup>
+ <CleanDependsOn>CleanInfVersion;CleanNci;$(CleanDependsOn)</CleanDependsOn>
+ </PropertyGroup>
+ <Target Name="BuildInfVersion" BeforeTargets="ClCompile" Inputs="$(OutDir)driver\wintun.inf" Outputs="$(IntDir)wintun-inf.h">
+ <Exec Command="cscript.exe /nologo &quot;extract-driverver.js&quot; &lt; &quot;$(OutDir)driver\wintun.inf&quot; &gt; &quot;$(IntDir)wintun-inf.h&quot;" />
+ </Target>
+ <Target Name="CleanInfVersion">
+ <Delete Files="$(IntDir)wintun-inf.h" />
+ </Target>
+ <Target Name="BuildNci" BeforeTargets="Link" Inputs="$(ProjectDir)nci.def;$(ProjectDir)nci.h" Outputs="$(IntDir)nci.lib">
+ <Exec Command="cl.exe /nologo /DGENERATE_LIB /Ob0 /c /Fo&quot;$(IntDir)nci.obj&quot; /Tc &quot;nci.h&quot;" />
+ <Exec Command="lib.exe /def:&quot;$(ProjectDir)nci.def&quot; /out:&quot;$(IntDir)nci.lib&quot; /machine:$(PlatformTarget) /nologo &quot;$(IntDir)nci.obj&quot;" />
+ </Target>
+ <Target Name="CleanNci">
+ <Delete Files="$(IntDir)nci.obj;$(IntDir)nci.lib" />
+ </Target>
+</Project>
diff --git a/api/api.vcxproj.filters b/api/api.vcxproj.filters
new file mode 100644
index 0000000..5fb8b10
--- /dev/null
+++ b/api/api.vcxproj.filters
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resources.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="exports.def">
+ <Filter>Source Files</Filter>
+ </None>
+ <None Include="nci.def">
+ <Filter>Source Files</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="nci.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="namespace.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="registry.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="logger.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="adapter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wintun.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ntdll.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="rundll32.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="driver.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="adapter_win7.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="namespace.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="rundll32.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="logger.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="resource.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="adapter.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="session.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="driver.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="registry.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
diff --git a/api/driver.c b/api/driver.c
new file mode 100644
index 0000000..4ed0d76
--- /dev/null
+++ b/api/driver.c
@@ -0,0 +1,519 @@
+/* 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 <SetupAPI.h>
+#include <devguid.h>
+#include <ndisguid.h>
+#include <Shlwapi.h>
+#include <shellapi.h>
+#include <wchar.h>
+
+#include "driver.h"
+#include "adapter.h"
+#include "logger.h"
+#include "namespace.h"
+#include "resource.h"
+#include "registry.h"
+#include "ntdll.h"
+#include "rundll32.h"
+#include "wintun-inf.h"
+
+#pragma warning(disable : 4221) /* nonstandard: address of automatic in initializer */
+
+struct _SP_DEVINFO_DATA_LIST
+{
+ SP_DEVINFO_DATA Data;
+ struct _SP_DEVINFO_DATA_LIST *Next;
+};
+
+static _Return_type_success_(return != FALSE)
+BOOL
+DisableAllOurAdapters(_In_ HDEVINFO DevInfo, _Inout_ SP_DEVINFO_DATA_LIST **DisabledAdapters)
+{
+ DWORD LastError = ERROR_SUCCESS;
+ for (DWORD EnumIndex = 0;; ++EnumIndex)
+ {
+ SP_DEVINFO_DATA_LIST *DeviceNode = Zalloc(sizeof(*DeviceNode));
+ if (!DeviceNode)
+ return FALSE;
+ DeviceNode->Data.cbSize = sizeof(SP_DEVINFO_DATA);
+ if (!SetupDiEnumDeviceInfo(DevInfo, EnumIndex, &DeviceNode->Data))
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ {
+ Free(DeviceNode);
+ break;
+ }
+ goto cleanupDeviceNode;
+ }
+
+ DEVPROPTYPE PropType;
+ WCHAR Name[MAX_ADAPTER_NAME] = L"<unknown>";
+ SetupDiGetDevicePropertyW(
+ DevInfo,
+ &DeviceNode->Data,
+ &DEVPKEY_Wintun_Name,
+ &PropType,
+ (PBYTE)Name,
+ MAX_ADAPTER_NAME * sizeof(Name[0]),
+ NULL,
+ 0);
+
+ 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 cleanupDeviceNode;
+
+ LOG(WINTUN_LOG_INFO, L"Disabling adapter \"%s\"", Name);
+ if (!AdapterDisableInstance(DevInfo, &DeviceNode->Data))
+ {
+ LOG_LAST_ERROR(L"Failed to disable adapter \"%s\"", Name);
+ LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError();
+ goto cleanupDeviceNode;
+ }
+
+ DeviceNode->Next = *DisabledAdapters;
+ *DisabledAdapters = DeviceNode;
+ continue;
+
+ cleanupDeviceNode:
+ Free(DeviceNode);
+ }
+ return RET_ERROR(TRUE, LastError);
+}
+
+static _Return_type_success_(return != FALSE)
+BOOL
+EnableAllOurAdapters(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA_LIST *AdaptersToEnable)
+{
+ DWORD LastError = ERROR_SUCCESS;
+ for (SP_DEVINFO_DATA_LIST *DeviceNode = AdaptersToEnable; DeviceNode; DeviceNode = DeviceNode->Next)
+ {
+ DEVPROPTYPE PropType;
+ WCHAR Name[MAX_ADAPTER_NAME] = L"<unknown>";
+ SetupDiGetDevicePropertyW(
+ DevInfo,
+ &DeviceNode->Data,
+ &DEVPKEY_Wintun_Name,
+ &PropType,
+ (PBYTE)Name,
+ MAX_ADAPTER_NAME * sizeof(Name[0]),
+ NULL,
+ 0);
+
+ LOG(WINTUN_LOG_INFO, L"Enabling adapter \"%s\"", Name);
+ if (!AdapterEnableInstance(DevInfo, &DeviceNode->Data))
+ {
+ LOG_LAST_ERROR(L"Failed to enable adapter \"%s\"", Name);
+ LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError();
+ }
+ }
+ return RET_ERROR(TRUE, LastError);
+}
+
+static BOOL
+IsNewer(
+ _In_ const FILETIME *DriverDate1,
+ _In_ DWORDLONG DriverVersion1,
+ _In_ const FILETIME *DriverDate2,
+ _In_ DWORDLONG DriverVersion2)
+{
+ if (DriverDate1->dwHighDateTime > DriverDate2->dwHighDateTime)
+ return TRUE;
+ if (DriverDate1->dwHighDateTime < DriverDate2->dwHighDateTime)
+ return FALSE;
+
+ if (DriverDate1->dwLowDateTime > DriverDate2->dwLowDateTime)
+ return TRUE;
+ if (DriverDate1->dwLowDateTime < DriverDate2->dwLowDateTime)
+ return FALSE;
+
+ if (DriverVersion1 > DriverVersion2)
+ return TRUE;
+ if (DriverVersion1 < DriverVersion2)
+ return FALSE;
+
+ return FALSE;
+}
+
+static _Return_type_success_(return != 0)
+DWORD
+VersionOfFile(_In_z_ LPCWSTR Filename)
+{
+ DWORD Zero;
+ DWORD Len = GetFileVersionInfoSizeW(Filename, &Zero);
+ if (!Len)
+ {
+ LOG_LAST_ERROR(L"Failed to query %s version info size", Filename);
+ return 0;
+ }
+ VOID *VersionInfo = Alloc(Len);
+ if (!VersionInfo)
+ return 0;
+ DWORD LastError = ERROR_SUCCESS, Version = 0;
+ VS_FIXEDFILEINFO *FixedInfo;
+ UINT FixedInfoLen = sizeof(*FixedInfo);
+ if (!GetFileVersionInfoW(Filename, 0, Len, VersionInfo))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to get %s version info", Filename);
+ goto out;
+ }
+ if (!VerQueryValueW(VersionInfo, L"\\", &FixedInfo, &FixedInfoLen))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to get %s version info root", Filename);
+ goto out;
+ }
+ Version = FixedInfo->dwFileVersionMS;
+ if (!Version)
+ {
+ LOG(WINTUN_LOG_WARN, L"Determined version of %s, but was v0.0, so returning failure", Filename);
+ LastError = ERROR_VERSION_PARSE_ERROR;
+ }
+out:
+ Free(VersionInfo);
+ return RET_ERROR(Version, LastError);
+}
+
+static DWORD WINAPI
+MaybeGetRunningDriverVersion(BOOL ReturnOneIfRunningInsteadOfVersion)
+{
+ PRTL_PROCESS_MODULES Modules;
+ ULONG BufferSize = 128 * 1024;
+ for (;;)
+ {
+ Modules = Alloc(BufferSize);
+ if (!Modules)
+ return 0;
+ NTSTATUS Status = NtQuerySystemInformation(SystemModuleInformation, Modules, BufferSize, &BufferSize);
+ if (NT_SUCCESS(Status))
+ break;
+ Free(Modules);
+ if (Status == STATUS_INFO_LENGTH_MISMATCH)
+ continue;
+ LOG(WINTUN_LOG_ERR, L"Failed to enumerate drivers (status: 0x%x)", Status);
+ SetLastError(RtlNtStatusToDosError(Status));
+ return 0;
+ }
+ DWORD LastError = ERROR_SUCCESS, Version = 0;
+ for (ULONG i = Modules->NumberOfModules; i-- > 0;)
+ {
+ LPCSTR NtPath = (LPCSTR)Modules->Modules[i].FullPathName;
+ if (!_stricmp(&NtPath[Modules->Modules[i].OffsetToFileName], "wintun.sys"))
+ {
+ if (ReturnOneIfRunningInsteadOfVersion)
+ {
+ Version = 1;
+ goto cleanupModules;
+ }
+ WCHAR FilePath[MAX_PATH * 3 + 15];
+ if (_snwprintf_s(FilePath, _countof(FilePath), _TRUNCATE, L"\\\\?\\GLOBALROOT%S", NtPath) == -1)
+ continue;
+ Version = VersionOfFile(FilePath);
+ if (!Version)
+ LastError = GetLastError();
+ goto cleanupModules;
+ }
+ }
+ LastError = ERROR_FILE_NOT_FOUND;
+cleanupModules:
+ Free(Modules);
+ return RET_ERROR(Version, LastError);
+}
+
+_Use_decl_annotations_
+DWORD WINAPI WintunGetRunningDriverVersion(VOID)
+{
+ return MaybeGetRunningDriverVersion(FALSE);
+}
+
+static BOOL EnsureWintunUnloaded(VOID)
+{
+ BOOL Loaded;
+ for (DWORD Tries = 0; Tries < 1500; ++Tries)
+ {
+ if (Tries)
+ Sleep(50);
+ Loaded = MaybeGetRunningDriverVersion(TRUE) != 0;
+ if (!Loaded)
+ break;
+ }
+ return !Loaded;
+}
+
+_Use_decl_annotations_
+VOID
+DriverInstallDeferredCleanup(HDEVINFO DevInfoExistingAdapters, SP_DEVINFO_DATA_LIST *ExistingAdapters)
+{
+ if (ExistingAdapters)
+ {
+ EnableAllOurAdapters(DevInfoExistingAdapters, ExistingAdapters);
+ while (ExistingAdapters)
+ {
+ SP_DEVINFO_DATA_LIST *Next = ExistingAdapters->Next;
+ Free(ExistingAdapters);
+ ExistingAdapters = Next;
+ }
+ }
+ if (DevInfoExistingAdapters != INVALID_HANDLE_VALUE)
+ SetupDiDestroyDeviceInfoList(DevInfoExistingAdapters);
+}
+
+_Use_decl_annotations_
+BOOL
+DriverInstall(HDEVINFO *DevInfoExistingAdaptersForCleanup, SP_DEVINFO_DATA_LIST **ExistingAdaptersForCleanup)
+{
+ static const FILETIME OurDriverDate = WINTUN_INF_FILETIME;
+ static const DWORDLONG OurDriverVersion = WINTUN_INF_VERSION;
+ HANDLE DriverInstallationLock = NamespaceTakeDriverInstallationMutex();
+ if (!DriverInstallationLock)
+ {
+ LOG(WINTUN_LOG_ERR, L"Failed to take driver installation mutex");
+ return FALSE;
+ }
+ DWORD LastError = ERROR_SUCCESS;
+ HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create empty device information set");
+ goto cleanupDriverInstallationLock;
+ }
+ SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) };
+ if (!SetupDiCreateDeviceInfoW(DevInfo, WINTUN_HWID, &GUID_DEVCLASS_NET, NULL, NULL, DICD_GENERATE_ID, &DevInfoData))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create new device information element");
+ 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)))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to set adapter hardware ID");
+ goto cleanupDevInfo;
+ }
+ if (!SetupDiBuildDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed building adapter driver info list");
+ goto cleanupDevInfo;
+ }
+ FILETIME DriverDate = { 0 };
+ DWORDLONG DriverVersion = 0;
+ HDEVINFO DevInfoExistingAdapters = INVALID_HANDLE_VALUE;
+ SP_DEVINFO_DATA_LIST *ExistingAdapters = NULL;
+ for (DWORD EnumIndex = 0;; ++EnumIndex)
+ {
+ 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;
+ }
+ if (IsNewer(&OurDriverDate, OurDriverVersion, &DrvInfoData.DriverDate, DrvInfoData.DriverVersion))
+ {
+ if (DevInfoExistingAdapters == INVALID_HANDLE_VALUE)
+ {
+ DevInfoExistingAdapters = SetupDiGetClassDevsExW(
+ &GUID_DEVCLASS_NET, WINTUN_ENUMERATOR, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
+ if (DevInfoExistingAdapters == INVALID_HANDLE_VALUE)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to get present adapters");
+ SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER);
+ goto cleanupExistingAdapters;
+ }
+ _Analysis_assume_(DevInfoExistingAdapters != NULL);
+ DisableAllOurAdapters(DevInfoExistingAdapters, &ExistingAdapters);
+ LOG(WINTUN_LOG_INFO, L"Waiting for existing driver to unload from kernel");
+ if (!EnsureWintunUnloaded())
+ LOG(WINTUN_LOG_WARN,
+ L"Failed to unload existing driver, which means a reboot will likely be required");
+ }
+ LOG(WINTUN_LOG_INFO,
+ L"Removing existing driver %u.%u",
+ (DWORD)((DrvInfoData.DriverVersion & 0xffff000000000000) >> 48),
+ (DWORD)((DrvInfoData.DriverVersion & 0x0000ffff00000000) >> 32));
+ BYTE LargeBuffer[0x2000];
+ DWORD Size = sizeof(LargeBuffer);
+ SP_DRVINFO_DETAIL_DATA_W *DrvInfoDetailData = (SP_DRVINFO_DETAIL_DATA_W *)LargeBuffer;
+ DrvInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_W);
+ if (!SetupDiGetDriverInfoDetailW(DevInfo, &DevInfoData, &DrvInfoData, DrvInfoDetailData, Size, &Size))
+ {
+ LOG(WINTUN_LOG_WARN, L"Failed getting adapter driver info detail");
+ continue;
+ }
+ LPWSTR InfFileName = PathFindFileNameW(DrvInfoDetailData->InfFileName);
+ if (!SetupUninstallOEMInfW(InfFileName, SUOI_FORCEDELETE, NULL))
+ LOG_LAST_ERROR(L"Unable to remove existing driver %s", InfFileName);
+ continue;
+ }
+ if (!IsNewer(&DrvInfoData.DriverDate, DrvInfoData.DriverVersion, &DriverDate, DriverVersion))
+ continue;
+ DriverDate = DrvInfoData.DriverDate;
+ DriverVersion = DrvInfoData.DriverVersion;
+ }
+ SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER);
+
+ if (DriverVersion)
+ {
+ LOG(WINTUN_LOG_INFO,
+ L"Using existing driver %u.%u",
+ (DWORD)((DriverVersion & 0xffff000000000000) >> 48),
+ (DWORD)((DriverVersion & 0x0000ffff00000000) >> 32));
+ LastError = ERROR_SUCCESS;
+ goto cleanupExistingAdapters;
+ }
+
+ LOG(WINTUN_LOG_INFO,
+ L"Installing driver %u.%u",
+ (DWORD)((OurDriverVersion & 0xffff000000000000) >> 48),
+ (DWORD)((OurDriverVersion & 0x0000ffff00000000) >> 32));
+ WCHAR RandomTempSubDirectory[MAX_PATH];
+ if (!ResourceCreateTemporaryDirectory(RandomTempSubDirectory))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create temporary folder %s", RandomTempSubDirectory);
+ goto cleanupExistingAdapters;
+ }
+
+ 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"))
+ {
+ LastError = ERROR_BUFFER_OVERFLOW;
+ goto cleanupDirectory;
+ }
+
+ WCHAR *CatSource, *SysSource, *InfSource;
+ if (NativeMachine == IMAGE_FILE_PROCESS)
+ {
+ CatSource = L"wintun.cat";
+ SysSource = L"wintun.sys";
+ InfSource = L"wintun.inf";
+ }
+ else if (NativeMachine == IMAGE_FILE_MACHINE_AMD64)
+ {
+ CatSource = L"wintun-amd64.cat";
+ SysSource = L"wintun-amd64.sys";
+ InfSource = L"wintun-amd64.inf";
+ }
+ else if (NativeMachine == IMAGE_FILE_MACHINE_ARM64)
+ {
+ CatSource = L"wintun-arm64.cat";
+ SysSource = L"wintun-arm64.sys";
+ InfSource = L"wintun-arm64.inf";
+ }
+ else
+ {
+ LastError = LOG_ERROR(ERROR_NOT_SUPPORTED, L"Unsupported platform 0x%x", NativeMachine);
+ goto cleanupDirectory;
+ }
+
+ LOG(WINTUN_LOG_INFO, L"Extracting driver");
+ if (!ResourceCopyToFile(CatPath, CatSource) || !ResourceCopyToFile(SysPath, SysSource) ||
+ !ResourceCopyToFile(InfPath, InfSource))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to extract driver");
+ goto cleanupDelete;
+ }
+
+ LOG(WINTUN_LOG_INFO, L"Installing driver");
+ if (!SetupCopyOEMInfW(InfPath, NULL, SPOST_NONE, 0, NULL, 0, NULL, NULL))
+ LastError = LOG_LAST_ERROR(L"Could not install driver %s to store", InfPath);
+
+cleanupDelete:
+ DeleteFileW(CatPath);
+ DeleteFileW(SysPath);
+ DeleteFileW(InfPath);
+cleanupDirectory:
+ RemoveDirectoryW(RandomTempSubDirectory);
+cleanupExistingAdapters:
+ if (LastError == ERROR_SUCCESS)
+ {
+ *DevInfoExistingAdaptersForCleanup = DevInfoExistingAdapters;
+ *ExistingAdaptersForCleanup = ExistingAdapters;
+ }
+ else
+ DriverInstallDeferredCleanup(DevInfoExistingAdapters, ExistingAdapters);
+cleanupDevInfo:
+ SetupDiDestroyDeviceInfoList(DevInfo);
+cleanupDriverInstallationLock:
+ NamespaceReleaseMutex(DriverInstallationLock);
+ return RET_ERROR(TRUE, LastError);
+}
+
+_Use_decl_annotations_
+BOOL WINAPI WintunDeleteDriver(VOID)
+{
+ DWORD LastError = ERROR_SUCCESS;
+
+ AdapterCleanupOrphanedDevices();
+
+ HANDLE DriverInstallationLock = NamespaceTakeDriverInstallationMutex();
+ if (!DriverInstallationLock)
+ {
+ LastError = LOG(WINTUN_LOG_ERR, L"Failed to take driver installation mutex");
+ goto cleanup;
+ }
+
+ HDEVINFO DevInfo = SetupDiCreateDeviceInfoListExW(&GUID_DEVCLASS_NET, NULL, NULL, NULL);
+ if (DevInfo == INVALID_HANDLE_VALUE)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create empty device information set");
+ goto cleanupDriverInstallationLock;
+ }
+ SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(DevInfoData) };
+ if (!SetupDiCreateDeviceInfoW(DevInfo, WINTUN_HWID, &GUID_DEVCLASS_NET, NULL, NULL, DICD_GENERATE_ID, &DevInfoData))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create new device information element");
+ 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)))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to set adapter hardware ID");
+ goto cleanupDevInfo;
+ }
+ if (!SetupDiBuildDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed building adapter driver info list");
+ goto cleanupDevInfo;
+ }
+ for (DWORD EnumIndex = 0;; ++EnumIndex)
+ {
+ 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;
+ }
+ BYTE LargeBuffer[0x2000];
+ DWORD Size = sizeof(LargeBuffer);
+ SP_DRVINFO_DETAIL_DATA_W *DrvInfoDetailData = (SP_DRVINFO_DETAIL_DATA_W *)LargeBuffer;
+ DrvInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_W);
+ if (!SetupDiGetDriverInfoDetailW(DevInfo, &DevInfoData, &DrvInfoData, DrvInfoDetailData, Size, &Size))
+ {
+ LOG(WINTUN_LOG_WARN, L"Failed getting adapter driver info detail");
+ continue;
+ }
+ LPCWSTR Path = PathFindFileNameW(DrvInfoDetailData->InfFileName);
+ LOG(WINTUN_LOG_INFO, L"Removing driver %s", Path);
+ if (!SetupUninstallOEMInfW(Path, 0, NULL))
+ {
+ LOG_LAST_ERROR(L"Unable to remove driver %s", Path);
+ LastError = LastError != ERROR_SUCCESS ? LastError : GetLastError();
+ }
+ }
+ SetupDiDestroyDriverInfoList(DevInfo, &DevInfoData, SPDIT_COMPATDRIVER);
+cleanupDevInfo:
+ SetupDiDestroyDeviceInfoList(DevInfo);
+cleanupDriverInstallationLock:
+ NamespaceReleaseMutex(DriverInstallationLock);
+cleanup:
+ return RET_ERROR(TRUE, LastError);
+}
diff --git a/api/driver.h b/api/driver.h
new file mode 100644
index 0000000..dae4e9b
--- /dev/null
+++ b/api/driver.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include "wintun.h"
+#include <Windows.h>
+#include <SetupAPI.h>
+
+#define WINTUN_HWID L"Wintun"
+
+typedef struct _SP_DEVINFO_DATA_LIST SP_DEVINFO_DATA_LIST;
+
+VOID
+DriverInstallDeferredCleanup(_In_ HDEVINFO DevInfoExistingAdapters, _In_opt_ SP_DEVINFO_DATA_LIST *ExistingAdapters);
+
+_Must_inspect_result_
+_Return_type_success_(return != FALSE)
+BOOL
+DriverInstall(
+ _Out_ HDEVINFO *DevInfoExistingAdaptersForCleanup,
+ _Out_ SP_DEVINFO_DATA_LIST **ExistingAdaptersForCleanup);
+
+/**
+ * @copydoc WINTUN_DELETE_DRIVER_FUNC
+ */
+WINTUN_DELETE_DRIVER_FUNC WintunDeleteDriver;
+
+/**
+ * @copydoc WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC
+ */
+WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC WintunGetRunningDriverVersion;
diff --git a/api/exports.def b/api/exports.def
new file mode 100644
index 0000000..c12db57
--- /dev/null
+++ b/api/exports.def
@@ -0,0 +1,16 @@
+LIBRARY wintun.dll
+EXPORTS
+ WintunAllocateSendPacket
+ WintunCreateAdapter
+ WintunEndSession
+ WintunOpenAdapter
+ WintunCloseAdapter
+ WintunGetAdapterLUID
+ WintunGetReadWaitEvent
+ WintunGetRunningDriverVersion
+ WintunReceivePacket
+ WintunReleaseReceivePacket
+ WintunSendPacket
+ WintunDeleteDriver
+ WintunSetLogger
+ WintunStartSession
diff --git a/api/extract-driverver.js b/api/extract-driverver.js
new file mode 100644
index 0000000..ee60b8a
--- /dev/null
+++ b/api/extract-driverver.js
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+while (!WScript.StdIn.AtEndOfStream) {
+ var line = WScript.StdIn.ReadLine();
+ if (line.substr(0, 12) != "DriverVer = ")
+ continue;
+ var val = line.substr(12).split(",");
+ var date = val[0].split("/");
+ var ver = val[1].split(".");
+ var time = Date.UTC(date[2], date[0] - 1, date[1]).toString()
+ WScript.Echo("#define WINTUN_INF_FILETIME { (DWORD)((" + time + "0000ULL + 116444736000000000ULL) & 0xffffffffU), (DWORD)((" + time + "0000ULL + 116444736000000000ULL) >> 32) }")
+ WScript.Echo("#define WINTUN_INF_VERSION ((" + ver[0] + "ULL << 48) | (" + ver[1] + "ULL << 32) | (" + ver[2] + "ULL << 16) | (" + ver[3] + "ULL << 0))")
+ break;
+}
diff --git a/api/logger.c b/api/logger.c
new file mode 100644
index 0000000..94ce525
--- /dev/null
+++ b/api/logger.c
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#include "logger.h"
+#include "adapter.h"
+#include "ntdll.h"
+#include <Windows.h>
+#include <iphlpapi.h>
+#include <winternl.h>
+#include <wchar.h>
+#include <stdlib.h>
+
+static BOOL CALLBACK
+NopLogger(_In_ WINTUN_LOGGER_LEVEL Level, _In_ DWORD64 Timestamp, _In_z_ LPCWSTR LogLine)
+{
+ return TRUE;
+}
+
+WINTUN_LOGGER_CALLBACK Logger = NopLogger;
+
+static DWORD64 Now(VOID)
+{
+ LARGE_INTEGER Timestamp;
+ NtQuerySystemTime(&Timestamp);
+ return Timestamp.QuadPart;
+}
+
+_Use_decl_annotations_
+VOID WINAPI
+WintunSetLogger(WINTUN_LOGGER_CALLBACK NewLogger)
+{
+ if (!NewLogger)
+ NewLogger = NopLogger;
+ Logger = NewLogger;
+}
+
+static VOID
+StrTruncate(_Inout_count_(StrChars) LPWSTR Str, _In_ SIZE_T StrChars)
+{
+ Str[StrChars - 2] = L'\u2026'; /* Horizontal Ellipsis */
+ Str[StrChars - 1] = 0;
+}
+
+_Use_decl_annotations_
+DWORD
+LoggerLog(WINTUN_LOGGER_LEVEL Level, LPCWSTR LogLine)
+{
+ DWORD LastError = GetLastError();
+ Logger(Level, Now(), LogLine);
+ SetLastError(LastError);
+ return LastError;
+}
+
+_Use_decl_annotations_
+DWORD
+LoggerLogV(WINTUN_LOGGER_LEVEL Level, LPCWSTR Format, va_list Args)
+{
+ DWORD LastError = GetLastError();
+ WCHAR LogLine[0x400];
+ if (_vsnwprintf_s(LogLine, _countof(LogLine), _TRUNCATE, Format, Args) == -1)
+ StrTruncate(LogLine, _countof(LogLine));
+ Logger(Level, Now(), LogLine);
+ SetLastError(LastError);
+ return LastError;
+}
+
+_Use_decl_annotations_
+DWORD
+LoggerError(DWORD Error, LPCWSTR Prefix)
+{
+ LPWSTR SystemMessage = NULL, FormattedMessage = NULL;
+ FormatMessageW(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL,
+ HRESULT_FROM_SETUPAPI(Error),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (VOID *)&SystemMessage,
+ 0,
+ NULL);
+ FormatMessageW(
+ FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY |
+ FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ SystemMessage ? L"%1: %3(Code 0x%2!08X!)" : L"%1: Code 0x%2!08X!",
+ 0,
+ 0,
+ (VOID *)&FormattedMessage,
+ 0,
+ (va_list *)(DWORD_PTR[]){ (DWORD_PTR)Prefix, (DWORD_PTR)Error, (DWORD_PTR)SystemMessage });
+ if (FormattedMessage)
+ Logger(WINTUN_LOG_ERR, Now(), FormattedMessage);
+ LocalFree(FormattedMessage);
+ LocalFree(SystemMessage);
+ return Error;
+}
+
+_Use_decl_annotations_
+DWORD
+LoggerErrorV(DWORD Error, LPCWSTR Format, va_list Args)
+{
+ WCHAR LogLine[0x400];
+ if (_vsnwprintf_s(LogLine, _countof(LogLine), _TRUNCATE, Format, Args) == -1)
+ StrTruncate(LogLine, _countof(LogLine));
+ return LoggerError(Error, LogLine);
+}
+
+_Use_decl_annotations_
+VOID
+LoggerGetRegistryKeyPath(HKEY Key, LPWSTR Path)
+{
+ DWORD LastError = GetLastError();
+ if (Key == NULL)
+ {
+ wcsncpy_s(Path, MAX_REG_PATH, L"<null>", _TRUNCATE);
+ goto out;
+ }
+ if (_snwprintf_s(Path, MAX_REG_PATH, _TRUNCATE, L"0x%p", Key) == -1)
+ StrTruncate(Path, MAX_REG_PATH);
+ union
+ {
+ KEY_NAME_INFORMATION KeyNameInfo;
+ WCHAR Data[offsetof(KEY_NAME_INFORMATION, Name) + MAX_REG_PATH];
+ } Buffer;
+ DWORD Size;
+ if (!NT_SUCCESS(NtQueryKey(Key, 3, &Buffer, sizeof(Buffer), &Size)) ||
+ Size < offsetof(KEY_NAME_INFORMATION, Name) || Buffer.KeyNameInfo.NameLength >= MAX_REG_PATH * sizeof(WCHAR))
+ goto out;
+ Buffer.KeyNameInfo.NameLength /= sizeof(WCHAR);
+ wmemcpy_s(Path, MAX_REG_PATH, Buffer.KeyNameInfo.Name, Buffer.KeyNameInfo.NameLength);
+ Path[Buffer.KeyNameInfo.NameLength] = L'\0';
+out:
+ SetLastError(LastError);
+}
diff --git a/api/logger.h b/api/logger.h
new file mode 100644
index 0000000..d83839e
--- /dev/null
+++ b/api/logger.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include "wintun.h"
+#include "main.h"
+#include "registry.h"
+#include <Windows.h>
+#include <intsafe.h>
+#include <stdarg.h>
+#include <wchar.h>
+
+extern WINTUN_LOGGER_CALLBACK Logger;
+
+/**
+ * @copydoc WINTUN_SET_LOGGER_FUNC
+ */
+WINTUN_SET_LOGGER_FUNC WintunSetLogger;
+
+_Post_equals_last_error_
+DWORD
+LoggerLog(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ LPCWSTR LogLine);
+
+_Post_equals_last_error_
+DWORD
+LoggerLogV(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ _Printf_format_string_ LPCWSTR Format, _In_ va_list Args);
+
+_Post_equals_last_error_
+static inline DWORD
+LoggerLogFmt(_In_ WINTUN_LOGGER_LEVEL Level, _In_z_ _Printf_format_string_ LPCWSTR Format, ...)
+{
+ va_list Args;
+ va_start(Args, Format);
+ DWORD LastError = LoggerLogV(Level, Format, Args);
+ va_end(Args);
+ return LastError;
+}
+
+_Post_equals_last_error_
+DWORD
+LoggerError(_In_ DWORD Error, _In_z_ LPCWSTR Prefix);
+
+_Post_equals_last_error_
+DWORD
+LoggerErrorV(_In_ DWORD Error, _In_z_ _Printf_format_string_ LPCWSTR Format, _In_ va_list Args);
+
+_Post_equals_last_error_
+static inline DWORD
+LoggerErrorFmt(_In_ DWORD Error, _In_z_ _Printf_format_string_ LPCWSTR Format, ...)
+{
+ va_list Args;
+ va_start(Args, Format);
+ DWORD LastError = LoggerErrorV(Error, Format, Args);
+ va_end(Args);
+ return LastError;
+}
+
+_Post_equals_last_error_
+static inline DWORD
+LoggerLastErrorV(_In_z_ _Printf_format_string_ LPCWSTR Format, _In_ va_list Args)
+{
+ DWORD LastError = GetLastError();
+ LoggerErrorV(LastError, Format, Args);
+ SetLastError(LastError);
+ return LastError;
+}
+
+_Post_equals_last_error_
+static inline DWORD
+LoggerLastErrorFmt(_In_z_ _Printf_format_string_ LPCWSTR Format, ...)
+{
+ va_list Args;
+ va_start(Args, Format);
+ DWORD LastError = LoggerLastErrorV(Format, Args);
+ va_end(Args);
+ return LastError;
+}
+
+VOID
+LoggerGetRegistryKeyPath(_In_ HKEY Key, _Out_writes_z_(MAX_REG_PATH) LPWSTR Path);
+
+#define LOG(lvl, msg, ...) (LoggerLogFmt((lvl), msg, __VA_ARGS__))
+#define LOG_ERROR(err, msg, ...) (LoggerErrorFmt((err), msg, __VA_ARGS__))
+#define LOG_LAST_ERROR(msg, ...) (LoggerLastErrorFmt(msg, __VA_ARGS__))
+
+#define RET_ERROR(Ret, Error) ((Error) == ERROR_SUCCESS ? (Ret) : (SetLastError(Error), 0))
+
+_Must_inspect_result_
+DECLSPEC_ALLOCATOR
+static inline _Return_type_success_(return != NULL)
+_Post_maybenull_
+_Post_writable_byte_size_(Size)
+VOID *
+LoggerAlloc(_In_z_ LPCWSTR Function, _In_ DWORD Flags, _In_ SIZE_T Size)
+{
+ VOID *Data = HeapAlloc(ModuleHeap, Flags, Size);
+ if (!Data)
+ {
+ LoggerLogFmt(WINTUN_LOG_ERR, Function, L"Out of memory (flags: 0x%x, requested size: 0x%zx)", Flags, Size);
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+ return Data;
+}
+_Must_inspect_result_
+DECLSPEC_ALLOCATOR
+static inline _Return_type_success_(return != NULL)
+_Post_maybenull_
+_Post_writable_byte_size_(Size)
+VOID *
+LoggerReAlloc(_In_z_ LPCWSTR Function, _In_ DWORD Flags, _Frees_ptr_opt_ LPVOID Mem, _In_ SIZE_T Size)
+{
+ VOID *Data = Mem ? HeapReAlloc(ModuleHeap, Flags, Mem, Size) : HeapAlloc(ModuleHeap, Flags, Size);
+ if (!Data)
+ {
+ LoggerLogFmt(WINTUN_LOG_ERR, Function, L"Out of memory (flags: 0x%x, requested size: 0x%zx)", Flags, Size);
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+ return Data;
+}
+
+#define __L(x) L##x
+#define _L(x) __L(x)
+#define Alloc(Size) LoggerAlloc(_L(__FUNCTION__), 0, Size)
+#define ReAlloc(Mem, Size) LoggerReAlloc(_L(__FUNCTION__), 0, Mem, Size)
+#define Zalloc(Size) LoggerAlloc(_L(__FUNCTION__), HEAP_ZERO_MEMORY, Size)
+#define ReZalloc(Mem, Size) LoggerReAlloc(_L(__FUNCTION__), HEAP_ZERO_MEMORY, Mem, Size)
+
+_Must_inspect_result_
+DECLSPEC_ALLOCATOR
+static inline _Return_type_success_(return != NULL)
+_Post_maybenull_
+_Post_writable_byte_size_((NumberOfElements) * (SizeOfOneElement))
+VOID *
+LoggerAllocArray(_In_z_ LPCWSTR Function, _In_ DWORD Flags, _In_ SIZE_T NumberOfElements, _In_ SIZE_T SizeOfOneElement)
+{
+ SIZE_T Size;
+ if (FAILED(SIZETMult(NumberOfElements, SizeOfOneElement, &Size)))
+ return NULL;
+ return LoggerAlloc(Function, Flags, Size);
+}
+_Must_inspect_result_
+DECLSPEC_ALLOCATOR
+static inline _Return_type_success_(return != NULL)
+_Post_maybenull_
+_Post_writable_byte_size_((NumberOfElements) * (SizeOfOneElement))
+VOID *
+LoggerReAllocArray(
+ _In_z_ LPCWSTR Function,
+ _In_ DWORD Flags,
+ _Frees_ptr_opt_ LPVOID Mem,
+ _In_ SIZE_T NumberOfElements,
+ _In_ SIZE_T SizeOfOneElement)
+{
+ SIZE_T Size;
+ if (FAILED(SIZETMult(NumberOfElements, SizeOfOneElement, &Size)))
+ return NULL;
+ return LoggerReAlloc(Function, Flags, Mem, Size);
+}
+#define AllocArray(Count, Size) LoggerAllocArray(_L(__FUNCTION__), 0, Count, Size)
+#define ReAllocArray(Mem, Count, Size) LoggerReAllocArray(_L(__FUNCTION__), 0, Mem, Count, Size)
+#define ZallocArray(Count, Size) LoggerAllocArray(_L(__FUNCTION__), HEAP_ZERO_MEMORY, Count, Size)
+#define ReZallocArray(Mem, Count, Size) LoggerReAllocArray(_L(__FUNCTION__), HEAP_ZERO_MEMORY, Mem, Count, Size)
+
+static inline VOID
+Free(_Frees_ptr_opt_ VOID *Ptr)
+{
+ if (!Ptr)
+ return;
+ DWORD LastError = GetLastError();
+ HeapFree(ModuleHeap, 0, Ptr);
+ SetLastError(LastError);
+}
diff --git a/api/main.c b/api/main.c
new file mode 100644
index 0000000..032225e
--- /dev/null
+++ b/api/main.c
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#include "logger.h"
+#include "adapter.h"
+#include "main.h"
+#include "namespace.h"
+#include "registry.h"
+#include "ntdll.h"
+
+#include <Windows.h>
+#include <delayimp.h>
+#include <sddl.h>
+#include <winefs.h>
+#include <stdlib.h>
+
+HINSTANCE ResourceModule;
+HANDLE ModuleHeap;
+SECURITY_ATTRIBUTES SecurityAttributes = { .nLength = sizeof(SECURITY_ATTRIBUTES) };
+BOOL IsLocalSystem;
+USHORT NativeMachine = IMAGE_FILE_PROCESS;
+
+#if NTDDI_VERSION == NTDDI_WIN7
+BOOL IsWindows7;
+#endif
+#if NTDDI_VERSION < NTDDI_WIN10
+BOOL IsWindows10;
+#endif
+
+static FARPROC WINAPI
+DelayedLoadLibraryHook(unsigned dliNotify, PDelayLoadInfo pdli)
+{
+ if (dliNotify != dliNotePreLoadLibrary)
+ return NULL;
+ HMODULE Library = LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (!Library)
+ abort();
+ return (FARPROC)Library;
+}
+
+const PfnDliHook __pfnDliNotifyHook2 = DelayedLoadLibraryHook;
+
+static BOOL InitializeSecurityObjects(VOID)
+{
+ BYTE LocalSystemSid[MAX_SID_SIZE];
+ DWORD RequiredBytes = sizeof(LocalSystemSid);
+ HANDLE CurrentProcessToken;
+ struct
+ {
+ TOKEN_USER MaybeLocalSystem;
+ CHAR LargeEnoughForLocalSystem[MAX_SID_SIZE];
+ } TokenUserBuffer;
+ BOOL Ret = FALSE;
+
+ if (!CreateWellKnownSid(WinLocalSystemSid, NULL, LocalSystemSid, &RequiredBytes))
+ return FALSE;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &CurrentProcessToken))
+ return FALSE;
+
+ if (!GetTokenInformation(CurrentProcessToken, TokenUser, &TokenUserBuffer, sizeof(TokenUserBuffer), &RequiredBytes))
+ goto cleanupProcessToken;
+
+ IsLocalSystem = EqualSid(TokenUserBuffer.MaybeLocalSystem.User.Sid, LocalSystemSid);
+ Ret = ConvertStringSecurityDescriptorToSecurityDescriptorW(
+ IsLocalSystem ? L"O:SYD:P(A;;GA;;;SY)(A;;GA;;;BA)S:(ML;;NWNRNX;;;HI)"
+ : L"O:BAD:P(A;;GA;;;SY)(A;;GA;;;BA)S:(ML;;NWNRNX;;;HI)",
+ SDDL_REVISION_1,
+ &SecurityAttributes.lpSecurityDescriptor,
+ NULL);
+
+cleanupProcessToken:
+ CloseHandle(CurrentProcessToken);
+ return Ret;
+}
+
+static void EnvInit(VOID)
+{
+ DWORD MajorVersion, MinorVersion;
+ RtlGetNtVersionNumbers(&MajorVersion, &MinorVersion, NULL);
+
+#if NTDDI_VERSION == NTDDI_WIN7
+ IsWindows7 = MajorVersion == 6 && MinorVersion == 1;
+#endif
+#if NTDDI_VERSION < NTDDI_WIN10
+ IsWindows10 = MajorVersion >= 10;
+#endif
+
+#ifdef MAYBE_WOW64
+ HANDLE Kernel32;
+ BOOL(WINAPI * IsWow64Process2)
+ (_In_ HANDLE Process, _Out_ USHORT * ProcessMachine, _Out_opt_ USHORT * NativeMachine);
+ USHORT ProcessMachine;
+ if ((Kernel32 = GetModuleHandleW(L"kernel32.dll")) == NULL ||
+ (*(FARPROC *)&IsWow64Process2 = GetProcAddress(Kernel32, "IsWow64Process2")) == NULL ||
+ !IsWow64Process2(GetCurrentProcess(), &ProcessMachine, &NativeMachine))
+ {
+ BOOL IsWoW64;
+ NativeMachine =
+ IsWow64Process(GetCurrentProcess(), &IsWoW64) && IsWoW64 ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_PROCESS;
+ }
+#endif
+}
+
+BOOL APIENTRY
+DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ ResourceModule = hinstDLL;
+ ModuleHeap = HeapCreate(0, 0, 0);
+ if (!ModuleHeap)
+ return FALSE;
+ if (!InitializeSecurityObjects())
+ {
+ HeapDestroy(ModuleHeap);
+ return FALSE;
+ }
+ EnvInit();
+ NamespaceInit();
+ AdapterCleanupLegacyDevices();
+ break;
+
+ case DLL_PROCESS_DETACH:
+ NamespaceDone();
+ LocalFree(SecurityAttributes.lpSecurityDescriptor);
+ HeapDestroy(ModuleHeap);
+ break;
+ }
+ return TRUE;
+}
diff --git a/api/main.h b/api/main.h
new file mode 100644
index 0000000..04ba7dd
--- /dev/null
+++ b/api/main.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include <Windows.h>
+
+#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
+
+extern HINSTANCE ResourceModule;
+extern HANDLE ModuleHeap;
+extern SECURITY_ATTRIBUTES SecurityAttributes;
+extern BOOL IsLocalSystem;
+extern USHORT NativeMachine;
+
+#if NTDDI_VERSION > NTDDI_WIN7
+# define IsWindows7 FALSE
+#else
+extern BOOL IsWindows7;
+#endif
+
+#if NTDDI_VERSION >= NTDDI_WIN10
+# define IsWindows10 TRUE
+#else
+extern BOOL IsWindows10;
+#endif \ No newline at end of file
diff --git a/api/namespace.c b/api/namespace.c
new file mode 100644
index 0000000..3248edb
--- /dev/null
+++ b/api/namespace.c
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#include "logger.h"
+#include "main.h"
+#include "namespace.h"
+
+#include <Windows.h>
+#include <winternl.h>
+#include <winefs.h>
+#include <wchar.h>
+#include <stdlib.h>
+
+static HANDLE PrivateNamespace = NULL;
+static HANDLE BoundaryDescriptor = NULL;
+static CRITICAL_SECTION Initializing;
+
+static _Return_type_success_(return != FALSE)
+BOOL NamespaceRuntimeInit(VOID)
+{
+ DWORD LastError;
+
+ EnterCriticalSection(&Initializing);
+ if (PrivateNamespace)
+ {
+ LeaveCriticalSection(&Initializing);
+ return TRUE;
+ }
+
+ BYTE Sid[MAX_SID_SIZE];
+ DWORD SidSize = sizeof(Sid);
+ if (!CreateWellKnownSid(IsLocalSystem ? WinLocalSystemSid : WinBuiltinAdministratorsSid, NULL, Sid, &SidSize))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create SID");
+ goto cleanupLeaveCriticalSection;
+ }
+
+ BoundaryDescriptor = CreateBoundaryDescriptorW(L"Wintun", 0);
+ if (!BoundaryDescriptor)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create boundary descriptor");
+ goto cleanupLeaveCriticalSection;
+ }
+ if (!AddSIDToBoundaryDescriptor(&BoundaryDescriptor, Sid))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to add SID to boundary descriptor");
+ goto cleanupBoundaryDescriptor;
+ }
+
+ for (;;)
+ {
+ if ((PrivateNamespace = CreatePrivateNamespaceW(&SecurityAttributes, BoundaryDescriptor, L"Wintun")) != NULL)
+ break;
+ if ((LastError = GetLastError()) == ERROR_ALREADY_EXISTS)
+ {
+ if ((PrivateNamespace = OpenPrivateNamespaceW(BoundaryDescriptor, L"Wintun")) != NULL)
+ break;
+ if ((LastError = GetLastError()) == ERROR_PATH_NOT_FOUND)
+ continue;
+ LOG_ERROR(LastError, L"Failed to open private namespace");
+ }
+ else
+ LOG_ERROR(LastError, L"Failed to create private namespace");
+ goto cleanupBoundaryDescriptor;
+ }
+
+ LeaveCriticalSection(&Initializing);
+ return TRUE;
+
+cleanupBoundaryDescriptor:
+ DeleteBoundaryDescriptor(BoundaryDescriptor);
+cleanupLeaveCriticalSection:
+ LeaveCriticalSection(&Initializing);
+ SetLastError(LastError);
+ return FALSE;
+}
+
+_Use_decl_annotations_
+HANDLE
+NamespaceTakeDriverInstallationMutex(VOID)
+{
+ if (!NamespaceRuntimeInit())
+ return NULL;
+ HANDLE Mutex = CreateMutexW(&SecurityAttributes, FALSE, L"Wintun\\Wintun-Driver-Installation-Mutex");
+ if (!Mutex)
+ {
+ LOG_LAST_ERROR(L"Failed to create mutex");
+ return NULL;
+ }
+ DWORD Result = WaitForSingleObject(Mutex, INFINITE);
+ switch (Result)
+ {
+ case WAIT_OBJECT_0:
+ case WAIT_ABANDONED:
+ return Mutex;
+ }
+ LOG(WINTUN_LOG_ERR, L"Failed to get mutex (status: 0x%x)", Result);
+ CloseHandle(Mutex);
+ SetLastError(ERROR_GEN_FAILURE);
+ return NULL;
+}
+
+_Use_decl_annotations_
+HANDLE
+NamespaceTakeDeviceInstallationMutex(VOID)
+{
+ if (!NamespaceRuntimeInit())
+ return NULL;
+ HANDLE Mutex = CreateMutexW(&SecurityAttributes, FALSE, L"Wintun\\Wintun-Device-Installation-Mutex");
+ if (!Mutex)
+ {
+ LOG_LAST_ERROR(L"Failed to create mutex");
+ return NULL;
+ }
+ DWORD Result = WaitForSingleObject(Mutex, INFINITE);
+ switch (Result)
+ {
+ case WAIT_OBJECT_0:
+ case WAIT_ABANDONED:
+ return Mutex;
+ }
+ LOG(WINTUN_LOG_ERR, L"Failed to get mutex (status: 0x%x)", Result);
+ CloseHandle(Mutex);
+ SetLastError(ERROR_GEN_FAILURE);
+ return NULL;
+}
+
+_Use_decl_annotations_
+VOID
+NamespaceReleaseMutex(HANDLE Mutex)
+{
+ ReleaseMutex(Mutex);
+ CloseHandle(Mutex);
+}
+
+VOID NamespaceInit(VOID)
+{
+ InitializeCriticalSection(&Initializing);
+}
+
+VOID NamespaceDone(VOID)
+{
+ EnterCriticalSection(&Initializing);
+ if (PrivateNamespace)
+ {
+ ClosePrivateNamespace(PrivateNamespace, 0);
+ DeleteBoundaryDescriptor(BoundaryDescriptor);
+ PrivateNamespace = NULL;
+ }
+ LeaveCriticalSection(&Initializing);
+ DeleteCriticalSection(&Initializing);
+}
diff --git a/api/namespace.h b/api/namespace.h
new file mode 100644
index 0000000..a0397be
--- /dev/null
+++ b/api/namespace.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include <Windows.h>
+
+_Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+_Acquires_lock_(_Curr_)
+HANDLE
+NamespaceTakeDriverInstallationMutex(VOID);
+
+_Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+_Acquires_lock_(_Curr_)
+HANDLE
+NamespaceTakeDeviceInstallationMutex(VOID);
+
+_Releases_lock_(Mutex)
+VOID
+NamespaceReleaseMutex(_In_ HANDLE Mutex);
+
+VOID NamespaceInit(VOID);
+
+VOID NamespaceDone(VOID);
diff --git a/api/nci.def b/api/nci.def
new file mode 100644
index 0000000..db484b7
--- /dev/null
+++ b/api/nci.def
@@ -0,0 +1,4 @@
+LIBRARY nci.dll
+EXPORTS
+ NciGetConnectionName
+ NciSetConnectionName
diff --git a/api/nci.h b/api/nci.h
new file mode 100644
index 0000000..ba99fa6
--- /dev/null
+++ b/api/nci.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include <Windows.h>
+
+#ifdef GENERATE_LIB
+# define DECLSPEC __declspec(dllexport)
+# define STUB \
+ { \
+ return 0; \
+ }
+#else
+# define DECLSPEC __declspec(dllimport)
+# define STUB ;
+#endif
+
+EXTERN_C
+DECLSPEC DWORD WINAPI
+NciSetConnectionName(_In_ const GUID *Guid, _In_z_ LPCWSTR NewName) STUB
+
+ EXTERN_C
+DECLSPEC DWORD WINAPI
+NciGetConnectionName(
+ _In_ const GUID *Guid,
+ _Out_z_bytecap_(InDestNameBytes) LPWSTR Name,
+ _In_ DWORD InDestNameBytes,
+ _Out_opt_ DWORD *OutDestNameBytes) STUB \ No newline at end of file
diff --git a/api/ntdll.h b/api/ntdll.h
new file mode 100644
index 0000000..2eb2786
--- /dev/null
+++ b/api/ntdll.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include <Windows.h>
+
+enum
+{
+ SystemModuleInformation = 11
+};
+
+typedef struct _RTL_PROCESS_MODULE_INFORMATION
+{
+ HANDLE Section;
+ PVOID MappedBase;
+ PVOID ImageBase;
+ ULONG ImageSize;
+ ULONG Flags;
+ USHORT LoadOrderIndex;
+ USHORT InitOrderIndex;
+ USHORT LoadCount;
+ USHORT OffsetToFileName;
+ UCHAR FullPathName[256];
+} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
+
+typedef struct _RTL_PROCESS_MODULES
+{
+ ULONG NumberOfModules;
+ RTL_PROCESS_MODULE_INFORMATION Modules[1];
+} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
+
+typedef struct _KEY_NAME_INFORMATION
+{
+ ULONG NameLength;
+ WCHAR Name[1];
+} KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION;
+
+#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) // TODO: #include <ntstatus.h> instead of this
+
+/* 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_C
+DECLSPEC_IMPORT VOID NTAPI
+RtlGetNtVersionNumbers(_Out_opt_ DWORD *MajorVersion, _Out_opt_ DWORD *MinorVersion, _Out_opt_ DWORD *BuildNumber);
+
+EXTERN_C
+DECLSPEC_IMPORT DWORD NTAPI
+NtQueryKey(
+ _In_ HANDLE KeyHandle,
+ _In_ int KeyInformationClass,
+ _Out_bytecap_post_bytecount_(Length, *ResultLength) PVOID KeyInformation,
+ _In_ ULONG Length,
+ _Out_ PULONG ResultLength);
diff --git a/api/registry.c b/api/registry.c
new file mode 100644
index 0000000..4f1001c
--- /dev/null
+++ b/api/registry.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#include "logger.h"
+#include "registry.h"
+#include <Windows.h>
+#include <wchar.h>
+#include <stdlib.h>
+#include <strsafe.h>
+
+_Use_decl_annotations_
+BOOL
+RegistryGetString(LPWSTR *Buf, DWORD Len, DWORD ValueType)
+{
+ if (wcsnlen(*Buf, Len) >= Len)
+ {
+ /* String is missing zero-terminator. */
+ LPWSTR BufZ = ReZallocArray(*Buf, (SIZE_T)Len + 1, sizeof(*BufZ));
+ if (!BufZ)
+ return FALSE;
+ _Analysis_assume_((wmemset(BufZ, L'A', (SIZE_T)Len + 1), TRUE));
+ *Buf = BufZ;
+ }
+
+ if (ValueType != REG_EXPAND_SZ)
+ return TRUE;
+
+ /* ExpandEnvironmentStringsW() returns strlen on success or 0 on error. Bail out on empty input strings to
+ * disambiguate. */
+ if (!(*Buf)[0])
+ return TRUE;
+
+ for (;;)
+ {
+ LPWSTR Expanded = AllocArray(Len, sizeof(*Expanded));
+ if (!Expanded)
+ return FALSE;
+ DWORD Result = ExpandEnvironmentStringsW(*Buf, Expanded, Len);
+ if (!Result)
+ {
+ LOG_LAST_ERROR(L"Failed to expand environment variables: %s", *Buf);
+ Free(Expanded);
+ return FALSE;
+ }
+ if (Result > Len)
+ {
+ Free(Expanded);
+ Len = Result;
+ continue;
+ }
+ Free(*Buf);
+ *Buf = Expanded;
+ return TRUE;
+ }
+}
+
+_Must_inspect_result_
+static _Return_type_success_(return != NULL)
+_Post_maybenull_
+_Post_writable_byte_size_(*BufLen)
+VOID *
+RegistryQuery(_In_ HKEY Key, _In_opt_z_ LPCWSTR Name, _Out_opt_ DWORD *ValueType, _Inout_ DWORD *BufLen, _In_ BOOL Log)
+{
+ for (;;)
+ {
+ BYTE *p = Alloc(*BufLen);
+ if (!p)
+ return NULL;
+ LSTATUS LastError = RegQueryValueExW(Key, Name, NULL, ValueType, p, BufLen);
+ if (LastError == ERROR_SUCCESS)
+ return p;
+ Free(p);
+ if (LastError != ERROR_MORE_DATA)
+ {
+ if (Log)
+ {
+ WCHAR RegPath[MAX_REG_PATH];
+ LoggerGetRegistryKeyPath(Key, RegPath);
+ LOG_ERROR(LastError, L"Failed to query registry value %.*s\\%s", MAX_REG_PATH, RegPath, Name);
+ }
+ SetLastError(LastError);
+ return NULL;
+ }
+ }
+}
+
+_Use_decl_annotations_
+LPWSTR
+RegistryQueryString(HKEY Key, LPCWSTR Name, BOOL Log)
+{
+ DWORD LastError, ValueType, Size = 256 * sizeof(WCHAR);
+ LPWSTR Value = RegistryQuery(Key, Name, &ValueType, &Size, Log);
+ if (!Value)
+ return NULL;
+ switch (ValueType)
+ {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ case REG_MULTI_SZ:
+ if (RegistryGetString(&Value, Size / sizeof(*Value), ValueType))
+ return Value;
+ LastError = GetLastError();
+ break;
+ default: {
+ WCHAR RegPath[MAX_REG_PATH];
+ LoggerGetRegistryKeyPath(Key, RegPath);
+ LOG(WINTUN_LOG_ERR,
+ L"Registry value %.*s\\%s is not a string (type: %u)",
+ MAX_REG_PATH,
+ RegPath,
+ Name,
+ ValueType);
+ LastError = ERROR_INVALID_DATATYPE;
+ }
+ }
+ Free(Value);
+ SetLastError(LastError);
+ return NULL;
+}
+
+_Use_decl_annotations_
+BOOL
+RegistryQueryDWORD(HKEY Key, LPCWSTR Name, DWORD *Value, BOOL Log)
+{
+ DWORD ValueType, Size = sizeof(DWORD);
+ DWORD LastError = RegQueryValueExW(Key, Name, NULL, &ValueType, (BYTE *)Value, &Size);
+ if (LastError != ERROR_SUCCESS)
+ {
+ if (Log)
+ {
+ WCHAR RegPath[MAX_REG_PATH];
+ LoggerGetRegistryKeyPath(Key, RegPath);
+ LOG_ERROR(LastError, L"Failed to query registry value %.*s\\%s", MAX_REG_PATH, RegPath, Name);
+ }
+ SetLastError(LastError);
+ return FALSE;
+ }
+ if (ValueType != REG_DWORD)
+ {
+ WCHAR RegPath[MAX_REG_PATH];
+ LoggerGetRegistryKeyPath(Key, RegPath);
+ LOG(WINTUN_LOG_ERR, L"Value %.*s\\%s is not a DWORD (type: %u)", MAX_REG_PATH, RegPath, Name, ValueType);
+ SetLastError(ERROR_INVALID_DATATYPE);
+ return FALSE;
+ }
+ if (Size != sizeof(DWORD))
+ {
+ WCHAR RegPath[MAX_REG_PATH];
+ LoggerGetRegistryKeyPath(Key, RegPath);
+ LOG(WINTUN_LOG_ERR, L"Value %.*s\\%s size is not 4 bytes (size: %u)", MAX_REG_PATH, RegPath, Name, Size);
+ SetLastError(ERROR_INVALID_DATA);
+ return FALSE;
+ }
+ return TRUE;
+}
diff --git a/api/registry.h b/api/registry.h
new file mode 100644
index 0000000..6beed2f
--- /dev/null
+++ b/api/registry.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include "wintun.h"
+#include <Windows.h>
+
+#define MAX_REG_PATH \
+ 256 /* Maximum registry path length \
+ https://support.microsoft.com/en-us/help/256986/windows-registry-information-for-advanced-users */
+
+/**
+ * Validates and/or sanitizes string value read from registry.
+ *
+ * @param Buf On input, it contains a pointer to pointer where the data is stored. The data must be allocated
+ * using HeapAlloc(ModuleHeap, 0). On output, it contains a pointer to pointer where the sanitized
+ * data is stored. It must be released with HeapFree(ModuleHeap, 0, *Buf) after use.
+ *
+ * @param Len Length of data string in wide characters.
+ *
+ * @param ValueType Type of data. Must be either REG_SZ or REG_EXPAND_SZ. REG_MULTI_SZ is treated like REG_SZ; only
+ * the first string of a multi-string is to be used.
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+_Must_inspect_result_
+_Return_type_success_(return != FALSE)
+BOOL
+RegistryGetString(_Inout_ LPWSTR *Buf, _In_ DWORD Len, _In_ DWORD ValueType);
+
+/**
+ * Reads string value from registry key.
+ *
+ * @param Key Handle of the registry key to read from. Must be opened with read access.
+ *
+ * @param Name Name of the value to read.
+ *
+ * @param Value Pointer to string to retrieve registry value. If the value type is REG_EXPAND_SZ the value is
+ * expanded using ExpandEnvironmentStrings(). If the value type is REG_MULTI_SZ, only the first
+ * string from the multi-string is returned. The string must be released with
+ * HeapFree(ModuleHeap, 0, Value) after use.
+ *
+ * @Log Set to TRUE to log all failures; FALSE to skip logging the innermost errors. Skipping innermost
+ * errors reduces log clutter when we are using RegistryQueryString() from
+ * RegistryQueryStringWait() and some errors are expected to occur.
+ *
+ * @return String with registry value on success; If the function fails, the return value is zero. To get extended error
+ * information, call GetLastError.
+ */
+_Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+LPWSTR
+RegistryQueryString(_In_ HKEY Key, _In_opt_z_ LPCWSTR Name, _In_ BOOL Log);
+
+/**
+ * Reads a 32-bit DWORD value from registry key.
+ *
+ * @param Key Handle of the registry key to read from. Must be opened with read access.
+ *
+ * @param Name Name of the value to read.
+ *
+ * @param Value Pointer to DWORD to retrieve registry value.
+ *
+ * @Log Set to TRUE to log all failures; FALSE to skip logging the innermost errors. Skipping innermost
+ * errors reduces log clutter when we are using RegistryQueryDWORD() from
+ * RegistryQueryDWORDWait() and some errors are expected to occur.
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+_Must_inspect_result_
+_Return_type_success_(return != FALSE)
+BOOL
+RegistryQueryDWORD(_In_ HKEY Key, _In_opt_z_ LPCWSTR Name, _Out_ DWORD *Value, _In_ BOOL Log);
diff --git a/api/resource.c b/api/resource.c
new file mode 100644
index 0000000..648f568
--- /dev/null
+++ b/api/resource.c
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#include "logger.h"
+#include "main.h"
+#include "resource.h"
+#include <Windows.h>
+#include <Shlwapi.h>
+#include <NTSecAPI.h>
+
+_Use_decl_annotations_
+const VOID *
+ResourceGetAddress(LPCWSTR ResourceName, DWORD *Size)
+{
+ HRSRC FoundResource = FindResourceW(ResourceModule, ResourceName, RT_RCDATA);
+ if (!FoundResource)
+ {
+ LOG_LAST_ERROR(L"Failed to find resource %s", ResourceName);
+ return NULL;
+ }
+ *Size = SizeofResource(ResourceModule, FoundResource);
+ if (!*Size)
+ {
+ LOG_LAST_ERROR(L"Failed to query resource %s size", ResourceName);
+ return NULL;
+ }
+ HGLOBAL LoadedResource = LoadResource(ResourceModule, FoundResource);
+ if (!LoadedResource)
+ {
+ LOG_LAST_ERROR(L"Failed to load resource %s", ResourceName);
+ return NULL;
+ }
+ BYTE *Address = LockResource(LoadedResource);
+ if (!Address)
+ {
+ LOG(WINTUN_LOG_ERR, L"Failed to lock resource %s", ResourceName);
+ SetLastError(ERROR_LOCK_FAILED);
+ return NULL;
+ }
+ return Address;
+}
+
+_Use_decl_annotations_
+BOOL
+ResourceCopyToFile(LPCWSTR DestinationPath, LPCWSTR ResourceName)
+{
+ DWORD SizeResource;
+ const VOID *LockedResource = ResourceGetAddress(ResourceName, &SizeResource);
+ if (!LockedResource)
+ {
+ LOG(WINTUN_LOG_ERR, L"Failed to locate resource %s", ResourceName);
+ return FALSE;
+ }
+ HANDLE DestinationHandle = CreateFileW(
+ DestinationPath,
+ GENERIC_WRITE,
+ 0,
+ &SecurityAttributes,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY,
+ NULL);
+ if (DestinationHandle == INVALID_HANDLE_VALUE)
+ {
+ LOG_LAST_ERROR(L"Failed to create file %s", DestinationPath);
+ return FALSE;
+ }
+ DWORD BytesWritten;
+ DWORD LastError;
+ if (!WriteFile(DestinationHandle, LockedResource, SizeResource, &BytesWritten, NULL))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to write file %s", DestinationPath);
+ goto cleanupDestinationHandle;
+ }
+ if (BytesWritten != SizeResource)
+ {
+ LOG(WINTUN_LOG_ERR,
+ L"Incomplete write to %s (written: %u, expected: %u)",
+ DestinationPath,
+ BytesWritten,
+ SizeResource);
+ LastError = ERROR_WRITE_FAULT;
+ goto cleanupDestinationHandle;
+ }
+ LastError = ERROR_SUCCESS;
+cleanupDestinationHandle:
+ CloseHandle(DestinationHandle);
+ return RET_ERROR(TRUE, LastError);
+}
+
+_Return_type_success_(return != FALSE)
+BOOL
+ResourceCreateTemporaryDirectory(_Out_writes_z_(MAX_PATH) LPWSTR RandomTempSubDirectory)
+{
+ WCHAR WindowsDirectory[MAX_PATH];
+ if (!GetWindowsDirectoryW(WindowsDirectory, _countof(WindowsDirectory)))
+ {
+ LOG_LAST_ERROR(L"Failed to get Windows folder");
+ return FALSE;
+ }
+ WCHAR WindowsTempDirectory[MAX_PATH];
+ if (!PathCombineW(WindowsTempDirectory, WindowsDirectory, L"Temp"))
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return FALSE;
+ }
+ UCHAR RandomBytes[32] = { 0 };
+ if (!RtlGenRandom(RandomBytes, sizeof(RandomBytes)))
+ {
+ LOG(WINTUN_LOG_ERR, L"Failed to generate random");
+ SetLastError(ERROR_GEN_FAILURE);
+ return FALSE;
+ }
+ 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))
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return FALSE;
+ }
+ if (!CreateDirectoryW(RandomTempSubDirectory, &SecurityAttributes))
+ {
+ LOG_LAST_ERROR(L"Failed to create temporary folder %s", RandomTempSubDirectory);
+ return FALSE;
+ }
+ return TRUE;
+}
diff --git a/api/resource.h b/api/resource.h
new file mode 100644
index 0000000..ae1210b
--- /dev/null
+++ b/api/resource.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include "wintun.h"
+#include <Windows.h>
+
+/**
+ * Locates RT_RCDATA resource memory address and size.
+ *
+ * @param ResourceName Name of the RT_RCDATA resource. Use MAKEINTRESOURCEW to locate resource by ID.
+ *
+ * @param Size Pointer to a variable to receive resource size.
+ *
+ * @return Resource address on success. If the function fails, the return value is NULL. To get extended error
+ * information, call GetLastError.
+ */
+_Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+_Post_readable_byte_size_(*Size) const VOID *ResourceGetAddress(_In_z_ LPCWSTR ResourceName, _Out_ DWORD *Size);
+
+/**
+ * Copies resource to a file.
+ *
+ * @param DestinationPath File path
+ *
+ * @param ResourceName Name of the RT_RCDATA resource. Use MAKEINTRESOURCEW to locate resource by ID.
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+_Return_type_success_(return != FALSE)
+BOOL
+ResourceCopyToFile(_In_z_ LPCWSTR DestinationPath, _In_z_ LPCWSTR ResourceName);
+
+/**
+ * Creates a temporary directory.
+ *
+ * @param RandomTempSubDirectory Name of random temporary directory.
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+_Return_type_success_(return != FALSE)
+BOOL
+ResourceCreateTemporaryDirectory(_Out_writes_z_(MAX_PATH) LPWSTR RandomTempSubDirectory);
diff --git a/api/resources.rc b/api/resources.rc
new file mode 100644
index 0000000..e7fab9c
--- /dev/null
+++ b/api/resources.rc
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#include <windows.h>
+#include <ntverp.h>
+
+#pragma code_page(1252)
+
+wintun.cat RCDATA "driver\\wintun.cat"
+wintun.inf RCDATA "driver\\wintun.inf"
+wintun.sys RCDATA "driver\\wintun.sys"
+
+#if defined(WANT_AMD64_WOW64)
+# if defined(BUILT_AMD64_WOW64)
+wintun-amd64.cat RCDATA "amd64\\driver\\wintun.cat"
+wintun-amd64.inf RCDATA "amd64\\driver\\wintun.inf"
+wintun-amd64.sys RCDATA "amd64\\driver\\wintun.sys"
+setupapihost-amd64.dll RCDATA "amd64\\setupapihost.dll"
+# else
+# pragma message("AMD64 wintun.sys was not built, so this will not work from WOW64")
+# endif
+#endif
+#if defined(WANT_ARM64_WOW64)
+# if defined(BUILT_ARM64_WOW64)
+wintun-arm64.cat RCDATA "arm64\\driver\\wintun.cat"
+wintun-arm64.inf RCDATA "arm64\\driver\\wintun.inf"
+wintun-arm64.sys RCDATA "arm64\\driver\\wintun.sys"
+setupapihost-arm64.dll RCDATA "arm64\\setupapihost.dll"
+# else
+# pragma message("ARM64 wintun.sys was not built, so this will not work from WOW64")
+# endif
+#endif
+
+#define STRINGIZE(x) #x
+#define EXPAND(x) STRINGIZE(x)
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION WINTUN_VERSION_MAJ, WINTUN_VERSION_MIN, WINTUN_VERSION_REL, 0
+PRODUCTVERSION WINTUN_VERSION_MAJ, WINTUN_VERSION_MIN, WINTUN_VERSION_REL, 0
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_DLL
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "WireGuard LLC"
+ VALUE "FileDescription", "Wintun API Library"
+ VALUE "FileVersion", EXPAND(WINTUN_VERSION)
+ VALUE "InternalName", "wintun.dll"
+ VALUE "LegalCopyright", "Copyright \xa9 2018-2021 WireGuard LLC. All Rights Reserved."
+ VALUE "OriginalFilename", "wintun.dll"
+ VALUE "ProductName", "Wintun Driver"
+ VALUE "ProductVersion", EXPAND(WINTUN_VERSION)
+ VALUE "Comments", "https://www.wintun.net/"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/api/rundll32.c b/api/rundll32.c
new file mode 100644
index 0000000..5ea2b15
--- /dev/null
+++ b/api/rundll32.c
@@ -0,0 +1,398 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#include "rundll32.h"
+#include "adapter.h"
+#include "main.h"
+#include "logger.h"
+#include "resource.h"
+#include <Windows.h>
+#include <shellapi.h>
+#include <Shlwapi.h>
+#include <cfgmgr32.h>
+#include <objbase.h>
+#include <assert.h>
+
+#ifdef MAYBE_WOW64
+
+_Return_type_success_(return != FALSE)
+static BOOL
+AppendToBuffer(_Inout_ LPWSTR *Buffer, _In_ CONST WCHAR Addition, _Inout_ SIZE_T *BufferPos, _Inout_ SIZE_T *BufferLen)
+{
+ SIZE_T NewPos;
+ if (FAILED(SIZETAdd(*BufferPos, sizeof(Addition), &NewPos)))
+ return FALSE;
+ if (NewPos >= *BufferLen)
+ {
+ SIZE_T NewLen;
+ if (FAILED(SIZETMult(NewPos, 3, &NewLen)))
+ return FALSE;
+ LPWSTR NewBuffer = ReZalloc(*Buffer, NewLen);
+ if (!NewBuffer)
+ return FALSE;
+ *Buffer = NewBuffer;
+ *BufferLen = NewLen;
+ }
+ SIZE_T NewIndex = *BufferPos / sizeof(**Buffer);
+ if (*Buffer + NewIndex < *Buffer)
+ return FALSE;
+ (*Buffer)[NewIndex] = Addition;
+ *BufferPos = NewPos;
+ return TRUE;
+}
+
+_Must_inspect_result_
+static _Return_type_success_(return != NULL)
+_Post_maybenull_
+LPWSTR
+ArgvToCommandLineW(_In_ SIZE_T ArgCount, ...)
+{
+ LPWSTR Output = NULL;
+ SIZE_T BufferPos = 0, BufferLen = 0;
+# define Append(Char) \
+ do \
+ { \
+ if (!AppendToBuffer(&Output, Char, &BufferPos, &BufferLen)) \
+ goto cleanupBuffer; \
+ } while (0)
+
+ va_list Args;
+ va_start(Args, ArgCount);
+ for (SIZE_T i = 0; i < ArgCount; ++i)
+ {
+ LPCWSTR Arg = va_arg(Args, LPCWSTR);
+ SIZE_T ArgLen = wcslen(Arg);
+ if (ArgLen >= DWORD_MAX >> 3)
+ goto cleanupBuffer;
+ if (i)
+ Append(L' ');
+ Append(L'"');
+ for (SIZE_T j = 0;; ++j)
+ {
+ SIZE_T NumberBackslashes = 0;
+
+ while (j < ArgLen && Arg[j] == L'\\')
+ {
+ ++j;
+ ++NumberBackslashes;
+ }
+ if (j >= ArgLen)
+ {
+ for (SIZE_T k = 0; k < NumberBackslashes * 2; ++k)
+ Append(L'\\');
+ break;
+ }
+ else if (Arg[j] == L'"')
+ {
+ for (SIZE_T k = 0; k < NumberBackslashes * 2 + 1; ++k)
+ Append(L'\\');
+ Append(Arg[j]);
+ }
+ else
+ {
+ for (SIZE_T k = 0; k < NumberBackslashes; ++k)
+ Append(L'\\');
+ Append(Arg[j]);
+ }
+ }
+ Append(L'"');
+ }
+ va_end(Args);
+ return Output;
+
+cleanupBuffer:
+ Free(Output);
+ return NULL;
+# undef Append
+}
+
+typedef struct _PROCESS_STDOUT_STATE
+{
+ HANDLE Stdout;
+ LPWSTR Response;
+ DWORD ResponseCapacity;
+} PROCESS_STDOUT_STATE;
+
+_Return_type_success_(return != ERROR_SUCCESS)
+static DWORD WINAPI
+ProcessStdout(_Inout_ PROCESS_STDOUT_STATE *State)
+{
+ for (DWORD Offset = 0, MaxLen = State->ResponseCapacity - 1; Offset < MaxLen;)
+ {
+ DWORD Size;
+ if (FAILED(DWordMult(MaxLen - Offset, sizeof(WCHAR), &Size)))
+ return ERROR_BUFFER_OVERFLOW;
+ if (!ReadFile(State->Stdout, State->Response + Offset, Size, &Size, NULL))
+ return ERROR_SUCCESS;
+ if (Size % sizeof(WCHAR))
+ return ERROR_INVALID_DATA;
+ Offset += Size / sizeof(WCHAR);
+ State->Response[Offset] = 0;
+ }
+ return ERROR_BUFFER_OVERFLOW;
+}
+
+static DWORD WINAPI
+ProcessStderr(_In_ HANDLE Stderr)
+{
+ WCHAR Msg[0x200], Buf[0x220], LevelRune;
+ DWORD64 Timestamp;
+ DWORD SizeRead;
+ WINTUN_LOGGER_LEVEL Level;
+ for (;;)
+ {
+ if (!ReadFile(Stderr, Buf, sizeof(Buf), &SizeRead, NULL) || !SizeRead)
+ return ERROR_SUCCESS;
+ if (SizeRead % sizeof(*Buf))
+ return ERROR_INVALID_DATA;
+ Msg[0] = Buf[SizeRead / sizeof(*Buf) - 1] = L'\0';
+ if (swscanf_s(Buf, L"[%c %I64u] %[^\n]", &LevelRune, 1, &Timestamp, Msg, (DWORD)_countof(Msg)) != 3 || !Msg[0])
+ return ERROR_INVALID_DATA;
+ if (!((Level = WINTUN_LOG_INFO, LevelRune == L'+') || (Level = WINTUN_LOG_WARN, LevelRune == L'-') ||
+ (Level = WINTUN_LOG_ERR, LevelRune == L'!')))
+ return ERROR_INVALID_DATA;
+ Logger(Level, Timestamp, Msg);
+ }
+}
+
+static _Return_type_success_(return != FALSE)
+BOOL
+ExecuteRunDll32(
+ _In_z_ LPCWSTR Function,
+ _In_z_ LPCWSTR Arguments,
+ _Out_z_cap_c_(ResponseCapacity) LPWSTR Response,
+ _In_ DWORD ResponseCapacity)
+{
+ WCHAR WindowsDirectory[MAX_PATH];
+ if (!GetWindowsDirectoryW(WindowsDirectory, _countof(WindowsDirectory)))
+ {
+ LOG_LAST_ERROR(L"Failed to get Windows folder");
+ return FALSE;
+ }
+ WCHAR RunDll32Path[MAX_PATH];
+ if (!PathCombineW(RunDll32Path, WindowsDirectory, L"Sysnative\\rundll32.exe"))
+ {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return FALSE;
+ }
+
+ DWORD LastError;
+ WCHAR RandomTempSubDirectory[MAX_PATH];
+ if (!ResourceCreateTemporaryDirectory(RandomTempSubDirectory))
+ {
+ LOG(WINTUN_LOG_ERR, L"Failed to create temporary folder");
+ return FALSE;
+ }
+ WCHAR DllPath[MAX_PATH] = { 0 };
+ if (!PathCombineW(DllPath, RandomTempSubDirectory, L"setupapihost.dll"))
+ {
+ LastError = ERROR_BUFFER_OVERFLOW;
+ goto cleanupDirectory;
+ }
+ LPCWSTR WintunDllResourceName;
+ switch (NativeMachine)
+ {
+ case IMAGE_FILE_MACHINE_AMD64:
+ WintunDllResourceName = L"setupapihost-amd64.dll";
+ break;
+ case IMAGE_FILE_MACHINE_ARM64:
+ WintunDllResourceName = L"setupapihost-arm64.dll";
+ break;
+ default:
+ LOG(WINTUN_LOG_ERR, L"Unsupported platform 0x%x", NativeMachine);
+ LastError = ERROR_NOT_SUPPORTED;
+ goto cleanupDirectory;
+ }
+ if (!ResourceCopyToFile(DllPath, WintunDllResourceName))
+ {
+ LastError = LOG(WINTUN_LOG_ERR, L"Failed to copy resource %s to %s", WintunDllResourceName, DllPath);
+ goto cleanupDelete;
+ }
+ size_t CommandLineLen = 10 + MAX_PATH + 2 + wcslen(Arguments) + 1 + wcslen(Function) + 1;
+ LPWSTR CommandLine = AllocArray(CommandLineLen, sizeof(*CommandLine));
+ if (!CommandLine)
+ {
+ LastError = GetLastError();
+ goto cleanupDelete;
+ }
+ if (_snwprintf_s(
+ CommandLine,
+ CommandLineLen,
+ _TRUNCATE,
+ L"rundll32 \"%.*s\",%s %s",
+ MAX_PATH,
+ DllPath,
+ Function,
+ Arguments) == -1)
+ {
+ LOG(WINTUN_LOG_ERR, L"Command line too long");
+ LastError = ERROR_INVALID_PARAMETER;
+ goto cleanupDelete;
+ }
+ HANDLE StreamRStdout = INVALID_HANDLE_VALUE, StreamRStderr = INVALID_HANDLE_VALUE,
+ StreamWStdout = INVALID_HANDLE_VALUE, StreamWStderr = INVALID_HANDLE_VALUE;
+ if (!CreatePipe(&StreamRStdout, &StreamWStdout, &SecurityAttributes, 0) ||
+ !CreatePipe(&StreamRStderr, &StreamWStderr, &SecurityAttributes, 0))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create pipes");
+ goto cleanupPipes;
+ }
+ if (!SetHandleInformation(StreamWStdout, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) ||
+ !SetHandleInformation(StreamWStderr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to set handle info");
+ goto cleanupPipes;
+ }
+ if (ResponseCapacity)
+ Response[0] = 0;
+ PROCESS_STDOUT_STATE ProcessStdoutState = { .Stdout = StreamRStdout,
+ .Response = Response,
+ .ResponseCapacity = ResponseCapacity };
+ HANDLE ThreadStdout = NULL, ThreadStderr = NULL;
+ if ((ThreadStdout = CreateThread(NULL, 0, ProcessStdout, &ProcessStdoutState, 0, NULL)) == NULL ||
+ (ThreadStderr = CreateThread(NULL, 0, ProcessStderr, StreamRStderr, 0, NULL)) == NULL)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to spawn readers");
+ goto cleanupThreads;
+ }
+ STARTUPINFOW si = { .cb = sizeof(STARTUPINFO),
+ .dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES,
+ .wShowWindow = SW_HIDE,
+ .hStdOutput = StreamWStdout,
+ .hStdError = StreamWStderr };
+ PROCESS_INFORMATION pi;
+ if (!CreateProcessW(RunDll32Path, CommandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create process: %s", CommandLine);
+ goto cleanupThreads;
+ }
+ LastError = ERROR_SUCCESS;
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+cleanupThreads:
+ if (ThreadStderr)
+ {
+ CloseHandle(StreamWStderr);
+ StreamWStderr = INVALID_HANDLE_VALUE;
+ WaitForSingleObject(ThreadStderr, INFINITE);
+ CloseHandle(ThreadStderr);
+ }
+ if (ThreadStdout)
+ {
+ CloseHandle(StreamWStdout);
+ StreamWStdout = INVALID_HANDLE_VALUE;
+ WaitForSingleObject(ThreadStdout, INFINITE);
+ DWORD ThreadResult;
+ if (!GetExitCodeThread(ThreadStdout, &ThreadResult))
+ LastError = LOG_LAST_ERROR(L"Failed to retrieve stdout reader result");
+ else if (ThreadResult != ERROR_SUCCESS)
+ LastError = LOG_ERROR(ThreadResult, L"Failed to read process output");
+ CloseHandle(ThreadStdout);
+ }
+cleanupPipes:
+ CloseHandle(StreamRStderr);
+ CloseHandle(StreamWStderr);
+ CloseHandle(StreamRStdout);
+ CloseHandle(StreamWStdout);
+ Free(CommandLine);
+cleanupDelete:
+ DeleteFileW(DllPath);
+cleanupDirectory:
+ RemoveDirectoryW(RandomTempSubDirectory);
+ return RET_ERROR(TRUE, LastError);
+}
+
+static _Return_type_success_(return != FALSE)
+BOOL
+InvokeClassInstaller(_In_ LPCWSTR Action, _In_ LPCWSTR Function, _In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData)
+{
+ LOG(WINTUN_LOG_INFO, L"Spawning native process to %s instance", Action);
+
+ WCHAR InstanceId[MAX_DEVICE_ID_LEN];
+ DWORD RequiredChars = _countof(InstanceId);
+ if (!SetupDiGetDeviceInstanceIdW(DevInfo, DevInfoData, InstanceId, RequiredChars, &RequiredChars))
+ {
+ LOG_LAST_ERROR(L"Failed to get adapter instance ID");
+ return FALSE;
+ }
+ LPWSTR Arguments = ArgvToCommandLineW(1, InstanceId);
+ if (!Arguments)
+ {
+ SetLastError(LOG_ERROR(ERROR_INVALID_PARAMETER, L"Command line too long"));
+ return FALSE;
+ }
+ DWORD LastError;
+ WCHAR Response[8 + 1];
+ if (!ExecuteRunDll32(Function, Arguments, Response, _countof(Response)))
+ {
+ LastError = LOG_LAST_ERROR(L"Error executing worker process: %s", Arguments);
+ goto cleanupArguments;
+ }
+ int Argc;
+ LPWSTR *Argv = CommandLineToArgvW(Response, &Argc);
+ if (Argc < 1)
+ {
+ LastError = LOG_ERROR(ERROR_INVALID_PARAMETER, L"Incomplete response: %s", Response);
+ goto cleanupArgv;
+ }
+ LastError = wcstoul(Argv[0], NULL, 16);
+cleanupArgv:
+ LocalFree(Argv);
+cleanupArguments:
+ Free(Arguments);
+ return RET_ERROR(TRUE, LastError);
+}
+
+_Use_decl_annotations_
+BOOL
+RemoveInstanceViaRundll32(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
+{
+ return InvokeClassInstaller(L"remove", L"RemoveInstance", DevInfo, DevInfoData);
+}
+
+_Use_decl_annotations_
+BOOL
+EnableInstanceViaRundll32(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
+{
+ return InvokeClassInstaller(L"enable", L"EnableInstance", DevInfo, DevInfoData);
+}
+
+_Use_decl_annotations_
+BOOL
+DisableInstanceViaRundll32(HDEVINFO DevInfo, SP_DEVINFO_DATA *DevInfoData)
+{
+ return InvokeClassInstaller(L"disable", L"DisableInstance", DevInfo, DevInfoData);
+}
+
+_Use_decl_annotations_
+BOOL
+CreateInstanceWin7ViaRundll32(LPWSTR InstanceId)
+{
+ LOG(WINTUN_LOG_INFO, L"Spawning native process to create instance");
+
+ DWORD LastError;
+ WCHAR Response[MAX_DEVICE_ID_LEN + 1];
+ if (!ExecuteRunDll32(L"CreateInstanceWin7", L"", Response, _countof(Response)))
+ {
+ LastError = LOG_LAST_ERROR(L"Error executing worker process");
+ goto cleanup;
+ }
+ int Argc;
+ LPWSTR *Argv = CommandLineToArgvW(Response, &Argc);
+ if (Argc < 2)
+ {
+ LastError = LOG_ERROR(ERROR_INVALID_PARAMETER, L"Incomplete response: %s", Response);
+ goto cleanupArgv;
+ }
+ LastError = wcstoul(Argv[0], NULL, 16);
+ if (LastError == ERROR_SUCCESS)
+ wcsncpy_s(InstanceId, MAX_DEVICE_ID_LEN, Argv[1], _TRUNCATE);
+cleanupArgv:
+ LocalFree(Argv);
+cleanup:
+ return RET_ERROR(TRUE, LastError);
+}
+#endif
diff --git a/api/rundll32.h b/api/rundll32.h
new file mode 100644
index 0000000..762bfcf
--- /dev/null
+++ b/api/rundll32.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include <Windows.h>
+#include <SetupAPI.h>
+#include "adapter.h"
+
+_Return_type_success_(return != FALSE)
+BOOL
+RemoveInstanceViaRundll32(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData);
+
+_Return_type_success_(return != FALSE)
+BOOL
+EnableInstanceViaRundll32(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData);
+
+_Return_type_success_(return != FALSE)
+BOOL
+DisableInstanceViaRundll32(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData);
+
+_Return_type_success_(return != FALSE)
+BOOL
+CreateInstanceWin7ViaRundll32(_Out_writes_z_(MAX_DEVICE_ID_LEN) LPWSTR InstanceId); \ No newline at end of file
diff --git a/api/session.c b/api/session.c
new file mode 100644
index 0000000..ab96c64
--- /dev/null
+++ b/api/session.c
@@ -0,0 +1,309 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#include "adapter.h"
+#include "logger.h"
+#include "main.h"
+#include "wintun.h"
+#include <Windows.h>
+#include <devioctl.h>
+#include <stdlib.h>
+
+#pragma warning(disable : 4200) /* nonstandard: zero-sized array in struct/union */
+
+#define TUN_ALIGNMENT sizeof(ULONG)
+#define TUN_ALIGN(Size) (((ULONG)(Size) + ((ULONG)TUN_ALIGNMENT - 1)) & ~((ULONG)TUN_ALIGNMENT - 1))
+#define TUN_IS_ALIGNED(Size) (!((ULONG)(Size) & ((ULONG)TUN_ALIGNMENT - 1)))
+#define TUN_MAX_PACKET_SIZE TUN_ALIGN(sizeof(TUN_PACKET) + WINTUN_MAX_IP_PACKET_SIZE)
+#define TUN_RING_CAPACITY(Size) ((Size) - sizeof(TUN_RING) - (TUN_MAX_PACKET_SIZE - TUN_ALIGNMENT))
+#define TUN_RING_SIZE(Capacity) (sizeof(TUN_RING) + (Capacity) + (TUN_MAX_PACKET_SIZE - TUN_ALIGNMENT))
+#define TUN_RING_WRAP(Value, Capacity) ((Value) & (Capacity - 1))
+#define LOCK_SPIN_COUNT 0x10000
+#define TUN_PACKET_RELEASE ((DWORD)0x80000000)
+
+typedef struct _TUN_PACKET
+{
+ ULONG Size;
+ UCHAR Data[];
+} TUN_PACKET;
+
+typedef struct _TUN_RING
+{
+ volatile ULONG Head;
+ volatile ULONG Tail;
+ volatile LONG Alertable;
+ UCHAR Data[];
+} TUN_RING;
+
+#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
+
+typedef struct _TUN_REGISTER_RINGS
+{
+ struct
+ {
+ ULONG RingSize;
+ TUN_RING *Ring;
+ HANDLE TailMoved;
+ } Send, Receive;
+} TUN_REGISTER_RINGS;
+
+typedef struct _TUN_SESSION
+{
+ ULONG Capacity;
+ struct
+ {
+ ULONG Tail;
+ ULONG TailRelease;
+ ULONG PacketsToRelease;
+ CRITICAL_SECTION Lock;
+ } Receive;
+ struct
+ {
+ ULONG Head;
+ ULONG HeadRelease;
+ ULONG PacketsToRelease;
+ CRITICAL_SECTION Lock;
+ } Send;
+ TUN_REGISTER_RINGS Descriptor;
+ HANDLE Handle;
+} TUN_SESSION;
+
+WINTUN_START_SESSION_FUNC WintunStartSession;
+_Use_decl_annotations_
+TUN_SESSION *WINAPI
+WintunStartSession(WINTUN_ADAPTER *Adapter, DWORD Capacity)
+{
+ DWORD LastError;
+ TUN_SESSION *Session = Zalloc(sizeof(TUN_SESSION));
+ if (!Session)
+ {
+ LastError = GetLastError();
+ goto cleanup;
+ }
+ const ULONG RingSize = TUN_RING_SIZE(Capacity);
+ BYTE *AllocatedRegion = VirtualAlloc(0, (size_t)RingSize * 2, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ if (!AllocatedRegion)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to allocate ring memory (requested size: 0x%zx)", (size_t)RingSize * 2);
+ goto cleanupRings;
+ }
+ Session->Descriptor.Send.RingSize = RingSize;
+ Session->Descriptor.Send.Ring = (TUN_RING *)AllocatedRegion;
+ Session->Descriptor.Send.TailMoved = CreateEventW(&SecurityAttributes, FALSE, FALSE, NULL);
+ if (!Session->Descriptor.Send.TailMoved)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create send event");
+ goto cleanupAllocatedRegion;
+ }
+
+ Session->Descriptor.Receive.RingSize = RingSize;
+ Session->Descriptor.Receive.Ring = (TUN_RING *)(AllocatedRegion + RingSize);
+ Session->Descriptor.Receive.TailMoved = CreateEventW(&SecurityAttributes, FALSE, FALSE, NULL);
+ if (!Session->Descriptor.Receive.TailMoved)
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to create receive event");
+ goto cleanupSendTailMoved;
+ }
+
+ Session->Handle = AdapterOpenDeviceObject(Adapter);
+ if (Session->Handle == INVALID_HANDLE_VALUE)
+ {
+ LastError = LOG(WINTUN_LOG_ERR, L"Failed to open adapter device object");
+ goto cleanupReceiveTailMoved;
+ }
+ DWORD BytesReturned;
+ if (!DeviceIoControl(
+ Session->Handle,
+ TUN_IOCTL_REGISTER_RINGS,
+ &Session->Descriptor,
+ sizeof(TUN_REGISTER_RINGS),
+ NULL,
+ 0,
+ &BytesReturned,
+ NULL))
+ {
+ LastError = LOG_LAST_ERROR(L"Failed to register rings");
+ goto cleanupHandle;
+ }
+ Session->Capacity = Capacity;
+ (VOID) InitializeCriticalSectionAndSpinCount(&Session->Receive.Lock, LOCK_SPIN_COUNT);
+ (VOID) InitializeCriticalSectionAndSpinCount(&Session->Send.Lock, LOCK_SPIN_COUNT);
+ return Session;
+cleanupHandle:
+ CloseHandle(Session->Handle);
+cleanupReceiveTailMoved:
+ CloseHandle(Session->Descriptor.Receive.TailMoved);
+cleanupSendTailMoved:
+ CloseHandle(Session->Descriptor.Send.TailMoved);
+cleanupAllocatedRegion:
+ VirtualFree(AllocatedRegion, 0, MEM_RELEASE);
+cleanupRings:
+ Free(Session);
+cleanup:
+ SetLastError(LastError);
+ return NULL;
+}
+
+WINTUN_END_SESSION_FUNC WintunEndSession;
+_Use_decl_annotations_
+VOID WINAPI
+WintunEndSession(TUN_SESSION *Session)
+{
+ DeleteCriticalSection(&Session->Send.Lock);
+ DeleteCriticalSection(&Session->Receive.Lock);
+ CloseHandle(Session->Handle);
+ CloseHandle(Session->Descriptor.Send.TailMoved);
+ CloseHandle(Session->Descriptor.Receive.TailMoved);
+ VirtualFree(Session->Descriptor.Send.Ring, 0, MEM_RELEASE);
+ Free(Session);
+}
+
+WINTUN_GET_READ_WAIT_EVENT_FUNC WintunGetReadWaitEvent;
+_Use_decl_annotations_
+HANDLE WINAPI
+WintunGetReadWaitEvent(TUN_SESSION *Session)
+{
+ return Session->Descriptor.Send.TailMoved;
+}
+
+WINTUN_RECEIVE_PACKET_FUNC WintunReceivePacket;
+_Use_decl_annotations_
+BYTE *WINAPI
+WintunReceivePacket(TUN_SESSION *Session, DWORD *PacketSize)
+{
+ DWORD LastError;
+ EnterCriticalSection(&Session->Send.Lock);
+ if (Session->Send.Head >= Session->Capacity)
+ {
+ LastError = ERROR_HANDLE_EOF;
+ goto cleanup;
+ }
+ const ULONG BuffTail = ReadULongAcquire(&Session->Descriptor.Send.Ring->Tail);
+ if (BuffTail >= Session->Capacity)
+ {
+ LastError = ERROR_HANDLE_EOF;
+ goto cleanup;
+ }
+ if (Session->Send.Head == BuffTail)
+ {
+ LastError = ERROR_NO_MORE_ITEMS;
+ goto cleanup;
+ }
+ const ULONG BuffContent = TUN_RING_WRAP(BuffTail - Session->Send.Head, Session->Capacity);
+ if (BuffContent < sizeof(TUN_PACKET))
+ {
+ LastError = ERROR_INVALID_DATA;
+ goto cleanup;
+ }
+ TUN_PACKET *BuffPacket = (TUN_PACKET *)&Session->Descriptor.Send.Ring->Data[Session->Send.Head];
+ if (BuffPacket->Size > WINTUN_MAX_IP_PACKET_SIZE)
+ {
+ LastError = ERROR_INVALID_DATA;
+ goto cleanup;
+ }
+ const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + BuffPacket->Size);
+ if (AlignedPacketSize > BuffContent)
+ {
+ LastError = ERROR_INVALID_DATA;
+ goto cleanup;
+ }
+ *PacketSize = BuffPacket->Size;
+ BYTE *Packet = BuffPacket->Data;
+ Session->Send.Head = TUN_RING_WRAP(Session->Send.Head + AlignedPacketSize, Session->Capacity);
+ Session->Send.PacketsToRelease++;
+ LeaveCriticalSection(&Session->Send.Lock);
+ return Packet;
+cleanup:
+ LeaveCriticalSection(&Session->Send.Lock);
+ SetLastError(LastError);
+ return NULL;
+}
+
+WINTUN_RELEASE_RECEIVE_PACKET_FUNC WintunReleaseReceivePacket;
+_Use_decl_annotations_
+VOID WINAPI
+WintunReleaseReceivePacket(TUN_SESSION *Session, const BYTE *Packet)
+{
+ EnterCriticalSection(&Session->Send.Lock);
+ TUN_PACKET *ReleasedBuffPacket = (TUN_PACKET *)(Packet - offsetof(TUN_PACKET, Data));
+ ReleasedBuffPacket->Size |= TUN_PACKET_RELEASE;
+ while (Session->Send.PacketsToRelease)
+ {
+ const TUN_PACKET *BuffPacket = (TUN_PACKET *)&Session->Descriptor.Send.Ring->Data[Session->Send.HeadRelease];
+ if ((BuffPacket->Size & TUN_PACKET_RELEASE) == 0)
+ break;
+ const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + (BuffPacket->Size & ~TUN_PACKET_RELEASE));
+ Session->Send.HeadRelease = TUN_RING_WRAP(Session->Send.HeadRelease + AlignedPacketSize, Session->Capacity);
+ Session->Send.PacketsToRelease--;
+ }
+ WriteULongRelease(&Session->Descriptor.Send.Ring->Head, Session->Send.HeadRelease);
+ LeaveCriticalSection(&Session->Send.Lock);
+}
+
+WINTUN_ALLOCATE_SEND_PACKET_FUNC WintunAllocateSendPacket;
+_Use_decl_annotations_
+BYTE *WINAPI
+WintunAllocateSendPacket(TUN_SESSION *Session, DWORD PacketSize)
+{
+ DWORD LastError;
+ EnterCriticalSection(&Session->Receive.Lock);
+ if (Session->Receive.Tail >= Session->Capacity)
+ {
+ LastError = ERROR_HANDLE_EOF;
+ goto cleanup;
+ }
+ const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + PacketSize);
+ const ULONG BuffHead = ReadULongAcquire(&Session->Descriptor.Receive.Ring->Head);
+ if (BuffHead >= Session->Capacity)
+ {
+ LastError = ERROR_HANDLE_EOF;
+ goto cleanup;
+ }
+ const ULONG BuffSpace = TUN_RING_WRAP(BuffHead - Session->Receive.Tail - TUN_ALIGNMENT, Session->Capacity);
+ if (AlignedPacketSize > BuffSpace)
+ {
+ LastError = ERROR_BUFFER_OVERFLOW;
+ goto cleanup;
+ }
+ TUN_PACKET *BuffPacket = (TUN_PACKET *)&Session->Descriptor.Receive.Ring->Data[Session->Receive.Tail];
+ BuffPacket->Size = PacketSize | TUN_PACKET_RELEASE;
+ BYTE *Packet = BuffPacket->Data;
+ Session->Receive.Tail = TUN_RING_WRAP(Session->Receive.Tail + AlignedPacketSize, Session->Capacity);
+ Session->Receive.PacketsToRelease++;
+ LeaveCriticalSection(&Session->Receive.Lock);
+ return Packet;
+cleanup:
+ LeaveCriticalSection(&Session->Receive.Lock);
+ SetLastError(LastError);
+ return NULL;
+}
+
+WINTUN_SEND_PACKET_FUNC WintunSendPacket;
+_Use_decl_annotations_
+VOID WINAPI
+WintunSendPacket(TUN_SESSION *Session, const BYTE *Packet)
+{
+ EnterCriticalSection(&Session->Receive.Lock);
+ TUN_PACKET *ReleasedBuffPacket = (TUN_PACKET *)(Packet - offsetof(TUN_PACKET, Data));
+ ReleasedBuffPacket->Size &= ~TUN_PACKET_RELEASE;
+ while (Session->Receive.PacketsToRelease)
+ {
+ const TUN_PACKET *BuffPacket =
+ (TUN_PACKET *)&Session->Descriptor.Receive.Ring->Data[Session->Receive.TailRelease];
+ if (BuffPacket->Size & TUN_PACKET_RELEASE)
+ break;
+ const ULONG AlignedPacketSize = TUN_ALIGN(sizeof(TUN_PACKET) + BuffPacket->Size);
+ Session->Receive.TailRelease =
+ TUN_RING_WRAP(Session->Receive.TailRelease + AlignedPacketSize, Session->Capacity);
+ Session->Receive.PacketsToRelease--;
+ }
+ if (Session->Descriptor.Receive.Ring->Tail != Session->Receive.TailRelease)
+ {
+ WriteULongRelease(&Session->Descriptor.Receive.Ring->Tail, Session->Receive.TailRelease);
+ if (ReadAcquire(&Session->Descriptor.Receive.Ring->Alertable))
+ SetEvent(Session->Descriptor.Receive.TailMoved);
+ }
+ LeaveCriticalSection(&Session->Receive.Lock);
+}
diff --git a/api/wintun.h b/api/wintun.h
new file mode 100644
index 0000000..bcf5ccc
--- /dev/null
+++ b/api/wintun.h
@@ -0,0 +1,274 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#pragma once
+
+#include <winsock2.h>
+#include <windows.h>
+#include <ipexport.h>
+#include <ifdef.h>
+#include <ws2ipdef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ALIGNED
+# if defined(_MSC_VER)
+# define ALIGNED(n) __declspec(align(n))
+# elif defined(__GNUC__)
+# define ALIGNED(n) __attribute__((aligned(n)))
+# else
+# error "Unable to define ALIGNED"
+# endif
+#endif
+
+/* MinGW is missing this one, unfortunately. */
+#ifndef _Post_maybenull_
+# define _Post_maybenull_
+#endif
+
+#if defined(_MSC_VER)
+# pragma warning(push)
+# pragma warning(disable : 4324) /* structure was padded due to alignment specifier */
+#endif
+
+/**
+ * A handle representing Wintun adapter
+ */
+typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE;
+
+/**
+ * Creates a new Wintun adapter.
+ *
+ * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
+ * characters.
+ *
+ * @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1
+ * characters.
+ *
+ * @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically.
+ * If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is
+ * created for each new adapter. It is called "requested" GUID because the API it uses is
+ * completely undocumented, and so there could be minor interesting complications with its usage.
+ *
+ * @return If the function succeeds, the return value is the adapter handle. Must be released with
+ * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
+ * GetLastError.
+ */
+typedef _Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC)
+(_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID);
+
+/**
+ * Opens an existing Wintun adapter.
+ *
+ * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1
+ * characters.
+ *
+ * @return If the function succeeds, the return value is the adapter handle. Must be released with
+ * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call
+ * GetLastError.
+ */
+typedef _Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name);
+
+/**
+ * Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter.
+ *
+ * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter.
+ */
+typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter);
+
+/**
+ * Deletes the Wintun driver if there are no more adapters in use.
+ *
+ * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To
+ * get extended error information, call GetLastError.
+ */
+typedef _Return_type_success_(return != FALSE)
+BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID);
+
+/**
+ * Returns the LUID of the adapter.
+ *
+ * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter
+ *
+ * @param Luid Pointer to LUID to receive adapter LUID.
+ */
+typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid);
+
+/**
+ * Determines the version of the Wintun driver currently loaded.
+ *
+ * @return If the function succeeds, the return value is the version number. If the function fails, the return value is
+ * zero. To get extended error information, call GetLastError. Possible errors include the following:
+ * ERROR_FILE_NOT_FOUND Wintun not loaded
+ */
+typedef _Return_type_success_(return != 0)
+DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID);
+
+/**
+ * Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK.
+ */
+typedef enum
+{
+ WINTUN_LOG_INFO, /**< Informational */
+ WINTUN_LOG_WARN, /**< Warning */
+ WINTUN_LOG_ERR /**< Error */
+} WINTUN_LOGGER_LEVEL;
+
+/**
+ * Called by internal logger to report diagnostic messages
+ *
+ * @param Level Message level.
+ *
+ * @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC.
+ *
+ * @param Message Message text.
+ */
+typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)(
+ _In_ WINTUN_LOGGER_LEVEL Level,
+ _In_ DWORD64 Timestamp,
+ _In_z_ LPCWSTR Message);
+
+/**
+ * Sets logger callback function.
+ *
+ * @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various
+ * threads concurrently. Should the logging require serialization, you must handle serialization in
+ * NewLogger. Set to NULL to disable.
+ */
+typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger);
+
+/**
+ * Minimum ring capacity.
+ */
+#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
+
+/**
+ * Maximum ring capacity.
+ */
+#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
+
+/**
+ * A handle representing Wintun session
+ */
+typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE;
+
+/**
+ * Starts Wintun session.
+ *
+ * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter
+ *
+ * @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.)
+ * Must be a power of two.
+ *
+ * @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is
+ * NULL. To get extended error information, call GetLastError.
+ */
+typedef _Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity);
+
+/**
+ * Ends Wintun session.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ */
+typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
+
+/**
+ * Gets Wintun session's read-wait event handle.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @return Pointer to receive event handle to wait for available data when reading. Should
+ * WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy
+ * load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call
+ * CloseHandle on this event - it is managed by the session.
+ */
+typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session);
+
+/**
+ * Maximum IP packet size
+ */
+#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF
+
+/**
+ * Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned
+ * from this function to release internal buffer. This function is thread-safe.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param PacketSize Pointer to receive packet size.
+ *
+ * @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the
+ * return value is NULL. To get extended error information, call GetLastError. Possible errors include the
+ * following:
+ * ERROR_HANDLE_EOF Wintun adapter is terminating;
+ * ERROR_NO_MORE_ITEMS Wintun buffer is exhausted;
+ * ERROR_INVALID_DATA Wintun buffer is corrupt
+ */
+typedef _Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+_Post_writable_byte_size_(*PacketSize)
+BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize);
+
+/**
+ * Releases internal buffer after the received packet has been processed by the client. This function is thread-safe.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param Packet Packet obtained with WintunReceivePacket
+ */
+typedef VOID(
+ WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
+
+/**
+ * Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send
+ * and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of
+ * calls define the packet sending order.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE.
+ *
+ * @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails,
+ * the return value is NULL. To get extended error information, call GetLastError. Possible errors include the
+ * following:
+ * ERROR_HANDLE_EOF Wintun adapter is terminating;
+ * ERROR_BUFFER_OVERFLOW Wintun buffer is full;
+ */
+typedef _Must_inspect_result_
+_Return_type_success_(return != NULL)
+_Post_maybenull_
+_Post_writable_byte_size_(PacketSize)
+BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize);
+
+/**
+ * Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket
+ * order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the
+ * WintunSendPacket yet.
+ *
+ * @param Session Wintun session handle obtained with WintunStartSession
+ *
+ * @param Packet Packet obtained with WintunAllocateSendPacket
+ */
+typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet);
+
+#if defined(_MSC_VER)
+# pragma warning(pop)
+#endif
+
+#ifdef __cplusplus
+}
+#endif