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:
parent
3fa45fec71
commit
d3a63116ba
18
api/api.h
18
api/api.h
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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
750
api/devmgmt.c
Normal 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;
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
WintunFreeAdapter
|
||||
WintunGetAdapter
|
Loading…
Reference in New Issue
Block a user