api: port GetInterface member from wireguard-go

Mind that this also fixes the order of adapter detection checks. A fast
test to eliminate non-Wintun adapters from iteration to speed things up
rendered the method incapable of detecting a non-Wintun adapter with the
name we are looking for.

ERROR_OBJECT_NOT_FOUND was replaced with ERROR_FILE_NOT_FOUND.

Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
Simon Rozman 2020-07-07 15:42:39 +02:00 committed by Jason A. Donenfeld
parent 3fa45fec71
commit d3a63116ba
5 changed files with 776 additions and 1 deletions

View File

@ -36,3 +36,21 @@ NciInit();
void
NciCleanup();
#define MAX_POOL 256
#define MAX_INSTANCE_ID MAX_PATH /* TODO: Is MAX_PATH always enough? */
typedef struct _WINTUN_ADAPTER
{
GUID CfgInstanceID;
WCHAR DevInstanceID[MAX_INSTANCE_ID];
DWORD LuidIndex;
DWORD IfType;
WCHAR Pool[MAX_POOL];
} WINTUN_ADAPTER;
VOID WINAPI
WintunFreeAdapter(_In_ WINTUN_ADAPTER *Adapter);
_Return_type_success_(return == 0) DWORD WINAPI
WintunGetAdapter(_In_z_count_c_(MAX_POOL) LPCWSTR Pool, _In_z_ LPCWSTR IfName, _Out_ WINTUN_ADAPTER **Adapter);

View File

@ -120,7 +120,7 @@
<AdditionalIncludeDirectories>..\$(WintunPlatform)\$(Configuration);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
<Link>
<AdditionalDependencies>Bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Bcrypt.lib;Setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>exports.def</ModuleDefinitionFile>
<SubSystem>Windows</SubSystem>
</Link>
@ -159,6 +159,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="api.c" />
<ClCompile Include="devmgmt.c" />
<ClCompile Include="namespace.c" />
<ClCompile Include="nci.c" />
</ItemGroup>

View File

@ -36,6 +36,9 @@
<ClCompile Include="namespace.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="devmgmt.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="nci.c">
<Filter>Source Files</Filter>
</ClCompile>

750
api/devmgmt.c Normal file
View File

@ -0,0 +1,750 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2018-2020 WireGuard LLC. All Rights Reserved.
*/
#include "api.h"
#include <objbase.h>
#include <SetupAPI.h>
#include <wchar.h>
#define WINTUN_HWID L"Wintun"
#define WAIT_FOR_REGISTRY_TIMEOUT 10000 /* ms */
const static GUID CLASS_NET_GUID = { 0x4d36e972L, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } };
const static GUID ADAPTER_NET_GUID = { 0xcac88484L,
0x7515,
0x4c03,
{ 0x82, 0xe6, 0x71, 0xa8, 0x7a, 0xba, 0xc3, 0x61 } };
/**
* Validate and/or sanitize string value read from registry.
*
* @param Buf On input, it contains pointer to pointer where the data is stored. The data must be
* allocated using HeapAlloc(GetProcessHeap(), 0).
* On output, it contains pointer to pointer where the sanitized data is stored. It must be
* released with HeapFree(GetProcessHeap(), 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.
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise
*/
static _Return_type_success_(return == 0) DWORD GetRegString(_Inout_ LPWSTR *Buf, _In_ DWORD Len, _In_ DWORD ValueType)
{
HANDLE Heap = GetProcessHeap();
if (wcsnlen(*Buf, Len) >= Len)
{
/* String is missing zero-terminator. */
LPWSTR BufZ = HeapAlloc(Heap, 0, ((size_t)Len + 1) * sizeof(WCHAR));
if (!BufZ)
return ERROR_OUTOFMEMORY;
wmemcpy(BufZ, *Buf, Len);
BufZ[Len] = 0;
HeapFree(Heap, 0, *Buf);
*Buf = BufZ;
}
if (ValueType != REG_EXPAND_SZ)
return ERROR_SUCCESS;
/* ExpandEnvironmentStringsW() returns strlen on success or 0 on error. Bail out on empty input strings to
* disambiguate. */
if (!(*Buf)[0])
return ERROR_SUCCESS;
Len = Len * 2 + 64;
for (;;)
{
LPWSTR Expanded = HeapAlloc(Heap, 0, Len * sizeof(WCHAR));
if (!Expanded)
return ERROR_OUTOFMEMORY;
DWORD Result = ExpandEnvironmentStringsW(*Buf, Expanded, Len);
if (!Result)
{
Result = GetLastError();
HeapFree(Heap, 0, Expanded);
return Result;
}
if (Result > Len)
{
HeapFree(Heap, 0, Expanded);
Len = Result;
continue;
}
HeapFree(Heap, 0, *Buf);
*Buf = Expanded;
return ERROR_SUCCESS;
}
}
/**
* Validate and/or sanitize multi-string value read from registry.
*
* @param Buf On input, it contains pointer to pointer where the data is stored. The data must be
* allocated using HeapAlloc(GetProcessHeap(), 0).
* On output, it contains pointer to pointer where the sanitized data is stored. It must be
* released with HeapFree(GetProcessHeap(), 0, *Buf) after use.
*
* @param Len Length of data string in wide characters
*
* @param ValueType Type of data. Must be one of REG_MULTI_SZ, REG_SZ or REG_EXPAND_SZ.
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise
*/
static _Return_type_success_(return == 0) DWORD
GetRegMultiString(_Inout_ LPWSTR *Buf, _In_ DWORD Len, _In_ DWORD ValueType)
{
HANDLE Heap = GetProcessHeap();
if (ValueType == REG_MULTI_SZ)
{
for (size_t i = 0;; i += wcsnlen(*Buf + i, Len - i) + 1)
{
if (i > Len)
{
/* Missing string and list terminators. */
LPWSTR BufZ = HeapAlloc(Heap, 0, ((size_t)Len + 2) * sizeof(WCHAR));
if (!BufZ)
return ERROR_OUTOFMEMORY;
wmemcpy(BufZ, *Buf, Len);
BufZ[Len] = 0;
BufZ[Len + 1] = 0;
HeapFree(Heap, 0, *Buf);
*Buf = BufZ;
return ERROR_SUCCESS;
}
if (i == Len)
{
/* Missing list terminator. */
LPWSTR BufZ = HeapAlloc(Heap, 0, ((size_t)Len + 1) * sizeof(WCHAR));
if (!BufZ)
return ERROR_OUTOFMEMORY;
wmemcpy(BufZ, *Buf, Len);
BufZ[Len] = 0;
HeapFree(Heap, 0, *Buf);
*Buf = BufZ;
return ERROR_SUCCESS;
}
if (!(*Buf)[i])
return ERROR_SUCCESS;
}
}
/* Sanitize REG_SZ/REG_EXPAND_SZ and append a list terminator to make a multi-string. */
DWORD Result = GetRegString(Buf, Len, ValueType);
if (Result != ERROR_SUCCESS)
return Result;
Len = (DWORD)wcslen(*Buf) + 1;
LPWSTR BufZ = HeapAlloc(Heap, 0, ((size_t)Len + 1) * sizeof(WCHAR));
if (!BufZ)
return ERROR_OUTOFMEMORY;
wmemcpy(BufZ, *Buf, Len);
BufZ[Len] = 0;
HeapFree(Heap, 0, *Buf);
*Buf = BufZ;
return ERROR_SUCCESS;
}
/**
* 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().
* The string must be released with HeapFree(GetProcessHeap(), 0, Value)
* after use.
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise
*/
static _Return_type_success_(return == 0) DWORD
RegQueryString(_In_ HKEY Key, _In_opt_z_ LPCWSTR Name, _Out_ LPWSTR *Value)
{
HANDLE Heap = GetProcessHeap();
DWORD Size = 256;
for (;;)
{
*Value = HeapAlloc(Heap, 0, Size);
if (!*Value)
return ERROR_OUTOFMEMORY;
DWORD ValueType;
DWORD Result = RegQueryValueExW(Key, Name, NULL, &ValueType, (BYTE *)*Value, &Size);
if (Result == ERROR_MORE_DATA)
{
HeapFree(Heap, 0, *Value);
continue;
}
if (Result != ERROR_SUCCESS)
{
HeapFree(Heap, 0, *Value);
return Result;
}
switch (ValueType)
{
case REG_SZ:
case REG_EXPAND_SZ:
Result = GetRegString(Value, Size / sizeof(WCHAR), ValueType);
if (Result != ERROR_SUCCESS)
HeapFree(Heap, 0, *Value);
return Result;
default:
HeapFree(Heap, 0, *Value);
return ERROR_INVALID_DATATYPE;
}
}
}
/**
* 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
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise
*/
static _Return_type_success_(return == 0) DWORD
RegQueryDWORD(_In_ HKEY Key, _In_opt_z_ LPCWSTR Name, _Out_ DWORD *Value)
{
DWORD ValueType, Size = sizeof(DWORD);
DWORD Result = RegQueryValueExW(Key, Name, NULL, &ValueType, (BYTE *)Value, &Size);
if (Result != ERROR_SUCCESS)
return Result;
if (ValueType != REG_DWORD)
return ERROR_INVALID_DATATYPE;
if (Size != sizeof(DWORD))
return ERROR_INVALID_DATA;
return ERROR_SUCCESS;
}
/**
* Retrieves a specified Plug and Play device property.
*
* @param DevInfo A handle to the device information set that contains a device information element that
* represents the device for which to open a registry key.
*
* @param DevInfoData A pointer to a structure that specifies the device information element in DeviceInfoSet.
*
* @param Property The property to be retrieved. One of the SPDRP_* constants.
*
* @param PropertyRegDataType A pointer to a variable that receives the data type of the property that is being
* retrieved. This is one of the standard registry data types. This parameter is optional
* and can be NULL.
*
* @param PropertyBuffer A pointer to a buffer that receives the property that is being retrieved. Must be
* released with HeapFree(GetProcessHeap(), 0, Value) after use.
*
* @param PropertySize A pointer to a variable of type DWORD that receives the property size, in bytes, of the
* PropertyBuffer buffer. This parameter is optional and can be NULL.
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise
*/
static _Return_type_success_(return == 0) DWORD GetDeviceRegistryProperty(
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_In_ DWORD Property,
_Out_opt_ DWORD *PropertyRegDataType,
_Out_ void **PropertyBuffer,
_Out_opt_ DWORD *PropertySize)
{
HANDLE Heap = GetProcessHeap();
DWORD Result, Size = 256;
for (;;)
{
void *Buf = HeapAlloc(Heap, 0, Size);
if (!Buf)
return ERROR_OUTOFMEMORY;
DWORD ValueType;
if (!SetupDiGetDeviceRegistryPropertyW(DevInfo, DevInfoData, Property, &ValueType, Buf, Size, &Size))
{
Result = GetLastError();
HeapFree(Heap, 0, Buf);
if (Result == ERROR_INSUFFICIENT_BUFFER)
continue;
return Result;
}
if (PropertyRegDataType)
*PropertyRegDataType = ValueType;
*PropertyBuffer = Buf;
if (PropertySize)
*PropertySize = Size;
return ERROR_SUCCESS;
}
}
/**
* Retrieves a specified Plug and Play device property string.
*
* @param DevInfo A handle to the device information set that contains a device information element that
* represents the device for which to open a registry key.
*
* @param DevInfoData A pointer to a structure that specifies the device information element in DeviceInfoSet.
*
* @param Property The property to be retrieved. One of the SPDRP_* constants.
*
* @param PropertyBuffer A pointer to a string that receives the string that is being retrieved. Must be
* released with HeapFree(GetProcessHeap(), 0, Value) after use.
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise
*/
static _Return_type_success_(return == 0) DWORD GetDeviceRegistryString(
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_In_ DWORD Property,
_Out_ LPWSTR *PropertyBuffer)
{
DWORD Result, ValueType, Size;
Result = GetDeviceRegistryProperty(DevInfo, DevInfoData, Property, &ValueType, PropertyBuffer, &Size);
if (Result != ERROR_SUCCESS)
return Result;
switch (ValueType)
{
case REG_SZ:
case REG_EXPAND_SZ:
Result = GetRegString(PropertyBuffer, Size / sizeof(WCHAR), ValueType);
if (Result != ERROR_SUCCESS)
HeapFree(GetProcessHeap(), 0, *PropertyBuffer);
return Result;
default:
HeapFree(GetProcessHeap(), 0, *PropertyBuffer);
return ERROR_INVALID_DATATYPE;
}
}
/**
* Retrieves a specified Plug and Play device property multi-string.
*
* @param DevInfo A handle to the device information set that contains a device information element that
* represents the device for which to open a registry key.
*
* @param DevInfoData A pointer to a structure that specifies the device information element in DeviceInfoSet.
*
* @param Property The property to be retrieved. One of the SPDRP_* constants.
*
* @param PropertyBuffer A pointer to a multi-string that receives the string that is being retrieved. Must be
* released with HeapFree(GetProcessHeap(), 0, Value) after use.
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise
*/
static _Return_type_success_(return == 0) DWORD GetDeviceRegistryMultiString(
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_In_ DWORD Property,
_Out_ LPWSTR *PropertyBuffer)
{
DWORD Result, ValueType, Size;
Result = GetDeviceRegistryProperty(DevInfo, DevInfoData, Property, &ValueType, PropertyBuffer, &Size);
if (Result != ERROR_SUCCESS)
return Result;
switch (ValueType)
{
case REG_SZ:
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
Result = GetRegMultiString(PropertyBuffer, Size / sizeof(WCHAR), ValueType);
if (Result != ERROR_SUCCESS)
HeapFree(GetProcessHeap(), 0, *PropertyBuffer);
return Result;
default:
HeapFree(GetProcessHeap(), 0, *PropertyBuffer);
return ERROR_INVALID_DATATYPE;
}
}
/**
* Removes numbered suffix from adapter name.
*/
static void
RemoveNumberedSuffix(_In_z_ LPCWSTR IfName, _Out_ LPWSTR Removed)
{
size_t Len = wcslen(IfName);
if (Len && IfName[Len - 1] < L'0' || IfName[Len - 1] > L'9')
{
wmemcpy(Removed, IfName, Len + 1);
return;
}
for (size_t i = Len; i--;)
{
if (IfName[i] >= L'0' && IfName[i] <= L'9')
continue;
if (IfName[i] == L' ')
{
wmemcpy(Removed, IfName, i);
Removed[i] = 0;
return;
}
break;
}
wmemcpy(Removed, IfName, Len + 1);
}
/**
* Tests if any of the hardware IDs match ours.
*
* @param Hwids Multi-string containing a list of hardware IDs
*
* @return TRUE on match; FALSE otherwise.
*/
static BOOL
IsOurHardwareID(_In_z_ LPWSTR Hwids)
{
for (; Hwids[0]; Hwids += wcslen(Hwids) + 1)
if (!_wcsicmp(Hwids, WINTUN_HWID))
return TRUE;
return FALSE;
}
/**
* Returns pool-specific device type name.
*/
static _Return_type_success_(return == 0) DWORD
GetPoolDeviceTypeName(_In_z_count_c_(MAX_POOL) LPCWSTR Pool, _Out_ LPWSTR *Name)
{
HANDLE Heap = GetProcessHeap();
int Len = 256;
for (;;)
{
*Name = HeapAlloc(Heap, 0, Len * sizeof(WCHAR));
if (!*Name)
return ERROR_OUTOFMEMORY;
if (_snwprintf_s(*Name, Len, _TRUNCATE, L"%s Tunnel", Pool) < 0)
{
HeapFree(Heap, 0, *Name);
Len *= 2;
continue;
}
return ERROR_SUCCESS;
}
}
/**
* Checks if SPDRP_DEVICEDESC or SPDRP_FRIENDLYNAME match device type name.
*/
static _Return_type_success_(return == 0) DWORD IsPoolMember(
_In_z_count_c_(MAX_POOL) LPCWSTR Pool,
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_Out_ BOOL *IsMember)
{
HANDLE Heap = GetProcessHeap();
LPWSTR DeviceDesc, FriendlyName, PoolDeviceTypeName;
DWORD Result = GetDeviceRegistryString(DevInfo, DevInfoData, SPDRP_DEVICEDESC, &DeviceDesc);
if (Result != ERROR_SUCCESS)
return Result;
Result = GetDeviceRegistryString(DevInfo, DevInfoData, SPDRP_FRIENDLYNAME, &FriendlyName);
if (Result != ERROR_SUCCESS)
goto cleanupDeviceDesc;
Result = GetPoolDeviceTypeName(Pool, &PoolDeviceTypeName);
if (Result != ERROR_SUCCESS)
goto cleanupFriendlyName;
if (!_wcsicmp(FriendlyName, PoolDeviceTypeName) || !_wcsicmp(DeviceDesc, PoolDeviceTypeName))
{
*IsMember = TRUE;
goto cleanupPoolDeviceTypeName;
}
RemoveNumberedSuffix(FriendlyName, FriendlyName);
RemoveNumberedSuffix(DeviceDesc, DeviceDesc);
if (!_wcsicmp(FriendlyName, PoolDeviceTypeName) || !_wcsicmp(DeviceDesc, PoolDeviceTypeName))
{
*IsMember = TRUE;
goto cleanupPoolDeviceTypeName;
}
*IsMember = FALSE;
cleanupPoolDeviceTypeName:
HeapFree(Heap, 0, PoolDeviceTypeName);
cleanupFriendlyName:
HeapFree(Heap, 0, FriendlyName);
cleanupDeviceDesc:
HeapFree(Heap, 0, DeviceDesc);
return Result;
}
/**
* Retrieves driver information detail for a device information set or a particular device information element in the
* device information set.
*
* @param DevInfo A handle to the device information set that contains a device information element that
* represents the device for which to open a registry key.
*
* @param DevInfoData A pointer to a structure that specifies the device information element in DeviceInfoSet.
*
* @param DriverData A pointer to a structure that specifies the driver information element that represents the
* driver for which to retrieve details.
*
* @param DriverDetailData A pointer to a structure that receives detailed information about the specified driver.
* Must be released with HeapFree(GetProcessHeap(), 0, Value) after use.
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise
*/
static _Return_type_success_(return == 0) DWORD GetDriverInfoDetail(
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_In_ SP_DRVINFO_DATA_W *DriverData,
_Out_ SP_DRVINFO_DETAIL_DATA_W **DriverDetailData)
{
HANDLE Heap = GetProcessHeap();
DWORD Size = 2048;
for (;;)
{
*DriverDetailData = HeapAlloc(Heap, 0, Size);
if (!*DriverDetailData)
return ERROR_OUTOFMEMORY;
(*DriverDetailData)->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA_W);
if (!SetupDiGetDriverInfoDetailW(DevInfo, DevInfoData, DriverData, *DriverDetailData, Size, &Size))
{
DWORD Result = GetLastError();
HeapFree(Heap, 0, *DriverDetailData);
if (Result == ERROR_INSUFFICIENT_BUFFER)
continue;
return Result;
}
return ERROR_SUCCESS;
}
}
/**
* Check if the device is using Wintun driver.
*/
static _Return_type_success_(return == 0) DWORD
IsUsingOurDriver(_In_ HDEVINFO DevInfo, _In_ SP_DEVINFO_DATA *DevInfoData, _Out_ BOOL *IsOurDriver)
{
if (!SetupDiBuildDriverInfoList(DevInfo, DevInfoData, SPDIT_COMPATDRIVER))
return GetLastError();
*IsOurDriver = FALSE;
HANDLE Heap = GetProcessHeap();
for (DWORD DriverIndex = 0;; ++DriverIndex)
{
SP_DRVINFO_DATA_W DriverData = { .cbSize = sizeof(SP_DRVINFO_DATA_W) };
if (!SetupDiEnumDriverInfoW(DevInfo, DevInfoData, SPDIT_COMPATDRIVER, DriverIndex, &DriverData))
{
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
continue;
}
SP_DRVINFO_DETAIL_DATA_W *DriverDetailData;
if (GetDriverInfoDetail(DevInfo, DevInfoData, &DriverData, &DriverDetailData) != ERROR_SUCCESS)
continue;
if (DriverDetailData->CompatIDsOffset > 1 && !_wcsicmp(DriverDetailData->HardwareID, WINTUN_HWID) ||
DriverDetailData->CompatIDsLength &&
IsOurHardwareID(DriverDetailData->HardwareID + DriverDetailData->CompatIDsOffset))
{
HeapFree(Heap, 0, DriverDetailData);
*IsOurDriver = TRUE;
break;
}
HeapFree(Heap, 0, DriverDetailData);
}
SetupDiDestroyDriverInfoList(DevInfo, DevInfoData, SPDIT_COMPATDRIVER);
return ERROR_SUCCESS;
}
/**
* Creates a Wintun interface descriptor and populates it from the device's registry key.
*
* @param DevInfo A handle to the device information set that contains a device information element that
* represents the device for which to open a registry key.
*
* @param DevInfoData A pointer to a structure that specifies the device information element in DeviceInfoSet.
*
* @param Pool Name of the adapter pool
*
* @param Adapter A pointer to a Wintun adapter descriptor
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise
*/
static _Return_type_success_(return == 0) DWORD InitAdapterData(
_In_ HDEVINFO DevInfo,
_In_ SP_DEVINFO_DATA *DevInfoData,
_In_z_count_c_(MAX_POOL) LPCWSTR Pool,
_Out_ WINTUN_ADAPTER *Adapter)
{
DWORD Result;
/* Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> registry key. */
HKEY Key = SetupDiOpenDevRegKey(DevInfo, DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
if (Key == INVALID_HANDLE_VALUE)
return GetLastError();
/* Read the NetCfgInstanceId value and convert to GUID. */
LPWSTR ValueStr;
Result = RegQueryString(Key, L"NetCfgInstanceId", &ValueStr);
if (Result != ERROR_SUCCESS)
goto cleanupKey;
if (FAILED(CLSIDFromString(ValueStr, &Adapter->CfgInstanceID)))
{
HeapFree(GetProcessHeap(), 0, ValueStr);
Result = ERROR_INVALID_DATA;
goto cleanupKey;
}
HeapFree(GetProcessHeap(), 0, ValueStr);
/* Read the NetLuidIndex value. */
Result = RegQueryDWORD(Key, L"NetLuidIndex", &Adapter->LuidIndex);
if (Result != ERROR_SUCCESS)
goto cleanupKey;
/* Read the NetLuidIndex value. */
Result = RegQueryDWORD(Key, L"*IfType", &Adapter->IfType);
if (Result != ERROR_SUCCESS)
goto cleanupKey;
DWORD Size;
if (!SetupDiGetDeviceInstanceIdW(
DevInfo, DevInfoData, Adapter->DevInstanceID, _countof(Adapter->DevInstanceID), &Size))
{
Result = GetLastError();
goto cleanupKey;
}
wcscpy_s(Adapter->Pool, _countof(Adapter->Pool), Pool);
Result = ERROR_SUCCESS;
cleanupKey:
RegCloseKey(Key);
return Result;
}
/**
* Releases Wintun adapter resources.
*
* @param Adapter Adapter handle obtained with WintunGetAdapter or WintunCreateAdapter
*/
VOID WINAPI
WintunFreeAdapter(_In_ WINTUN_ADAPTER *Adapter)
{
HeapFree(GetProcessHeap(), 0, Adapter);
}
/**
* Finds a Wintun adapter by its name.
*
* @param Pool Name of the adapter pool
*
* @param IfName Adapter name
*
* @param Adapter Pointer to a handle to receive the adapter handle. Must be released with
* WintunFreeAdapter.
*
* @return ERROR_SUCCESS on success; Win32 error code otherwise;
* ERROR_FILE_NOT_FOUND if adapter with given name is not found;
* ERROR_ALREADY_EXISTS if adapter is found but not a Wintun-class or not a member of the pool
*/
_Return_type_success_(return == 0) DWORD WINAPI
WintunGetAdapter(_In_z_count_c_(MAX_POOL) LPCWSTR Pool, _In_z_ LPCWSTR IfName, _Out_ WINTUN_ADAPTER **Adapter)
{
DWORD Result;
HANDLE Mutex = TakeNameMutex(Pool);
if (!Mutex)
return ERROR_GEN_FAILURE;
HDEVINFO DevInfo = SetupDiGetClassDevsExW(&CLASS_NET_GUID, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (DevInfo == INVALID_HANDLE_VALUE)
{
Result = GetLastError();
goto cleanupMutex;
}
HANDLE Heap = GetProcessHeap();
for (DWORD MemberIndex = 0;; ++MemberIndex)
{
SP_DEVINFO_DATA DevInfoData = { .cbSize = sizeof(SP_DEVINFO_DATA) };
if (!SetupDiEnumDeviceInfo(DevInfo, MemberIndex, &DevInfoData))
{
if (GetLastError() == ERROR_NO_MORE_ITEMS)
break;
continue;
}
GUID CfgInstanceID;
HKEY Key = SetupDiOpenDevRegKey(DevInfo, &DevInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE);
if (Key != INVALID_HANDLE_VALUE)
{
LPWSTR CfgInstanceIDStr;
Result = RegQueryString(Key, L"NetCfgInstanceId", &CfgInstanceIDStr);
if (Result == ERROR_SUCCESS)
{
Result =
SUCCEEDED(CLSIDFromString(CfgInstanceIDStr, &CfgInstanceID)) ? ERROR_SUCCESS : ERROR_INVALID_DATA;
HeapFree(Heap, 0, CfgInstanceIDStr);
}
RegCloseKey(Key);
}
else
Result = GetLastError();
if (Result != ERROR_SUCCESS)
continue;
/* TODO: is there a better way than comparing ifnames? */
WCHAR IfName2[0x400], IfName3[0x400]; /* TODO: Make dynamic. */
if (NciGetConnectionName(&CfgInstanceID, IfName2, sizeof(IfName2), NULL) != ERROR_SUCCESS)
continue;
IfName2[_countof(IfName2) - 1] = 0;
RemoveNumberedSuffix(IfName2, IfName3);
if (_wcsicmp(IfName, IfName2) && _wcsicmp(IfName, IfName3))
continue;
/* Check the Hardware ID to make sure it's a real Wintun device. This avoids doing slow operations on non-Wintun
* devices. */
LPWSTR Hwids;
Result = GetDeviceRegistryMultiString(DevInfo, &DevInfoData, SPDRP_HARDWAREID, &Hwids);
if (Result != ERROR_SUCCESS)
goto cleanupDevInfo;
if (!IsOurHardwareID(Hwids))
{
HeapFree(Heap, 0, Hwids);
Result = ERROR_ALREADY_EXISTS;
goto cleanupDevInfo;
}
HeapFree(Heap, 0, Hwids);
BOOL IsOurDriver;
Result = IsUsingOurDriver(DevInfo, &DevInfoData, &IsOurDriver);
if (Result != ERROR_SUCCESS)
goto cleanupDevInfo;
if (!IsOurDriver)
{
Result = ERROR_ALREADY_EXISTS;
goto cleanupDevInfo;
}
BOOL IsMember;
Result = IsPoolMember(Pool, DevInfo, &DevInfoData, &IsMember);
if (Result != ERROR_SUCCESS)
goto cleanupDevInfo;
if (!IsMember)
{
Result = ERROR_ALREADY_EXISTS;
goto cleanupDevInfo;
}
*Adapter = HeapAlloc(Heap, 0, sizeof(WINTUN_ADAPTER));
if (!*Adapter)
{
Result = ERROR_OUTOFMEMORY;
goto cleanupDevInfo;
}
Result = InitAdapterData(DevInfo, &DevInfoData, Pool, *Adapter);
if (Result != ERROR_SUCCESS)
HeapFree(Heap, 0, *Adapter);
goto cleanupDevInfo;
}
Result = ERROR_FILE_NOT_FOUND;
cleanupDevInfo:
SetupDiDestroyDeviceInfoList(DevInfo);
cleanupMutex:
ReleaseNameMutex(Mutex);
return Result;
}

View File

@ -0,0 +1,3 @@
EXPORTS
WintunFreeAdapter
WintunGetAdapter