2013-07-04 20:56:19 +00:00
/*
2019-08-23 16:23:39 +00:00
* Copyright ( c ) 2019 ZeroTier , Inc .
2013-07-04 20:56:19 +00:00
*
2019-08-23 16:23:39 +00:00
* Use of this software is governed by the Business Source License included
* in the LICENSE . TXT file in the project ' s root directory .
2013-07-04 20:56:19 +00:00
*
2020-08-20 19:51:39 +00:00
* Change Date : 2025 - 01 - 01
2013-07-04 20:56:19 +00:00
*
2019-08-23 16:23:39 +00:00
* On the date above , in accordance with the Business Source License , use
* of this software will be governed by version 2.0 of the Apache License .
2013-07-04 20:56:19 +00:00
*/
2019-08-23 16:23:39 +00:00
/****/
2013-07-04 20:56:19 +00:00
2014-04-08 19:00:21 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <stdint.h>
# include <string.h>
2021-12-27 19:07:35 +00:00
# include <winsock2.h>
# include <windows.h>
2014-04-08 19:00:21 +00:00
# include <tchar.h>
2015-07-30 18:31:38 +00:00
# include <malloc.h>
2014-04-08 19:00:21 +00:00
# include <winreg.h>
# include <wchar.h>
# include <ws2ipdef.h>
2021-12-27 19:07:35 +00:00
# include <ws2tcpip.h>
2014-04-08 19:00:21 +00:00
# include <IPHlpApi.h>
# include <nldef.h>
# include <netioapi.h>
2014-08-13 00:20:34 +00:00
# include <atlbase.h>
# include <netlistmgr.h>
# include <nldef.h>
2015-07-30 18:31:38 +00:00
# include <SetupAPI.h>
# include <newdev.h>
# include <cfgmgr32.h>
2014-08-13 00:20:34 +00:00
# include <iostream>
2015-04-24 22:05:28 +00:00
# include <set>
2014-04-08 19:00:21 +00:00
2014-07-28 16:08:56 +00:00
# include "../node/Constants.hpp"
# include "../node/Utils.hpp"
# include "../node/Mutex.hpp"
2014-04-08 19:00:21 +00:00
2015-04-09 02:03:30 +00:00
# include "WindowsEthernetTap.hpp"
2015-04-24 22:05:28 +00:00
# include "OSUtils.hpp"
2015-04-09 02:03:30 +00:00
2015-05-08 23:31:50 +00:00
# include "..\windows\TapDriver6\tap-windows.h"
2020-08-12 01:55:42 +00:00
# include "WinDNSHelper.hpp"
2013-07-06 19:56:12 +00:00
2019-07-16 22:16:49 +00:00
# include <netcon.h>
2015-07-30 18:31:38 +00:00
// Create a fake unused default route to force detection of network type on networks without gateways
2014-08-13 00:20:34 +00:00
# define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE
2015-07-30 18:31:38 +00:00
// Function signatures of dynamically loaded functions, from newdev.h, setupapi.h, and cfgmgr32.h
typedef BOOL ( WINAPI * UpdateDriverForPlugAndPlayDevicesA_t ) ( _In_opt_ HWND hwndParent , _In_ LPCSTR HardwareId , _In_ LPCSTR FullInfPath , _In_ DWORD InstallFlags , _Out_opt_ PBOOL bRebootRequired ) ;
typedef BOOL ( WINAPI * SetupDiGetINFClassA_t ) ( _In_ PCSTR InfName , _Out_ LPGUID ClassGuid , _Out_writes_ ( ClassNameSize ) PSTR ClassName , _In_ DWORD ClassNameSize , _Out_opt_ PDWORD RequiredSize ) ;
typedef HDEVINFO ( WINAPI * SetupDiCreateDeviceInfoList_t ) ( _In_opt_ CONST GUID * ClassGuid , _In_opt_ HWND hwndParent ) ;
typedef BOOL ( WINAPI * SetupDiCreateDeviceInfoA_t ) ( _In_ HDEVINFO DeviceInfoSet , _In_ PCSTR DeviceName , _In_ CONST GUID * ClassGuid , _In_opt_ PCSTR DeviceDescription , _In_opt_ HWND hwndParent , _In_ DWORD CreationFlags , _Out_opt_ PSP_DEVINFO_DATA DeviceInfoData ) ;
typedef BOOL ( WINAPI * SetupDiSetDeviceRegistryPropertyA_t ) ( _In_ HDEVINFO DeviceInfoSet , _Inout_ PSP_DEVINFO_DATA DeviceInfoData , _In_ DWORD Property , _In_reads_bytes_opt_ ( PropertyBufferSize ) CONST BYTE * PropertyBuffer , _In_ DWORD PropertyBufferSize ) ;
typedef BOOL ( WINAPI * SetupDiCallClassInstaller_t ) ( _In_ DI_FUNCTION InstallFunction , _In_ HDEVINFO DeviceInfoSet , _In_opt_ PSP_DEVINFO_DATA DeviceInfoData ) ;
typedef BOOL ( WINAPI * SetupDiDestroyDeviceInfoList_t ) ( _In_ HDEVINFO DeviceInfoSet ) ;
typedef HDEVINFO ( WINAPI * SetupDiGetClassDevsExA_t ) ( _In_opt_ CONST GUID * ClassGuid , _In_opt_ PCSTR Enumerator , _In_opt_ HWND hwndParent , _In_ DWORD Flags , _In_opt_ HDEVINFO DeviceInfoSet , _In_opt_ PCSTR MachineName , _Reserved_ PVOID Reserved ) ;
typedef BOOL ( WINAPI * SetupDiOpenDeviceInfoA_t ) ( _In_ HDEVINFO DeviceInfoSet , _In_ PCSTR DeviceInstanceId , _In_opt_ HWND hwndParent , _In_ DWORD OpenFlags , _Out_opt_ PSP_DEVINFO_DATA DeviceInfoData ) ;
typedef BOOL ( WINAPI * SetupDiEnumDeviceInfo_t ) ( _In_ HDEVINFO DeviceInfoSet , _In_ DWORD MemberIndex , _Out_ PSP_DEVINFO_DATA DeviceInfoData ) ;
typedef BOOL ( WINAPI * SetupDiSetClassInstallParamsA_t ) ( _In_ HDEVINFO DeviceInfoSet , _In_opt_ PSP_DEVINFO_DATA DeviceInfoData , _In_reads_bytes_opt_ ( ClassInstallParamsSize ) PSP_CLASSINSTALL_HEADER ClassInstallParams , _In_ DWORD ClassInstallParamsSize ) ;
typedef CONFIGRET ( WINAPI * CM_Get_Device_ID_ExA_t ) ( _In_ DEVINST dnDevInst , _Out_writes_ ( BufferLen ) PSTR Buffer , _In_ ULONG BufferLen , _In_ ULONG ulFlags , _In_opt_ HMACHINE hMachine ) ;
2015-08-13 22:52:54 +00:00
typedef BOOL ( WINAPI * SetupDiGetDeviceInstanceIdA_t ) ( _In_ HDEVINFO DeviceInfoSet , _In_ PSP_DEVINFO_DATA DeviceInfoData , _Out_writes_opt_ ( DeviceInstanceIdSize ) PSTR DeviceInstanceId , _In_ DWORD DeviceInstanceIdSize , _Out_opt_ PDWORD RequiredSize ) ;
2013-07-04 20:56:19 +00:00
namespace ZeroTier {
2015-04-24 22:05:28 +00:00
namespace {
2015-07-30 18:31:38 +00:00
// Static/singleton class that when initialized loads a bunch of environment information and a few dynamically loaded DLLs
2015-04-24 22:05:28 +00:00
class WindowsEthernetTapEnv
{
public :
WindowsEthernetTapEnv ( )
{
# ifdef _WIN64
is64Bit = TRUE ;
2015-07-30 18:31:38 +00:00
tapDriverPath = " \\ tap-windows \\ x64 \\ zttap300.inf " ;
2015-04-24 22:05:28 +00:00
# else
is64Bit = FALSE ;
IsWow64Process ( GetCurrentProcess ( ) , & is64Bit ) ;
2015-07-30 18:31:38 +00:00
if ( is64Bit ) {
fprintf ( stderr , " FATAL: you must use the 64-bit ZeroTier One service on 64-bit Windows systems \r \n " ) ;
_exit ( 1 ) ;
}
tapDriverPath = " \\ tap-windows \\ x86 \\ zttap300.inf " ;
2015-04-24 22:05:28 +00:00
# endif
2015-07-30 18:31:38 +00:00
tapDriverName = " zttap300 " ;
setupApiMod = LoadLibraryA ( " setupapi.dll " ) ;
if ( ! setupApiMod ) {
fprintf ( stderr , " FATAL: unable to dynamically load setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiGetINFClassA = ( SetupDiGetINFClassA_t ) GetProcAddress ( setupApiMod , " SetupDiGetINFClassA " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiGetINFClassA not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiCreateDeviceInfoList = ( SetupDiCreateDeviceInfoList_t ) GetProcAddress ( setupApiMod , " SetupDiCreateDeviceInfoList " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiCreateDeviceInfoList not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiCreateDeviceInfoA = ( SetupDiCreateDeviceInfoA_t ) GetProcAddress ( setupApiMod , " SetupDiCreateDeviceInfoA " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiCreateDeviceInfoA not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiSetDeviceRegistryPropertyA = ( SetupDiSetDeviceRegistryPropertyA_t ) GetProcAddress ( setupApiMod , " SetupDiSetDeviceRegistryPropertyA " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiSetDeviceRegistryPropertyA not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiCallClassInstaller = ( SetupDiCallClassInstaller_t ) GetProcAddress ( setupApiMod , " SetupDiCallClassInstaller " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiCallClassInstaller not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiDestroyDeviceInfoList = ( SetupDiDestroyDeviceInfoList_t ) GetProcAddress ( setupApiMod , " SetupDiDestroyDeviceInfoList " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiDestroyDeviceInfoList not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiGetClassDevsExA = ( SetupDiGetClassDevsExA_t ) GetProcAddress ( setupApiMod , " SetupDiGetClassDevsExA " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiGetClassDevsExA not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiOpenDeviceInfoA = ( SetupDiOpenDeviceInfoA_t ) GetProcAddress ( setupApiMod , " SetupDiOpenDeviceInfoA " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiOpenDeviceInfoA not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiEnumDeviceInfo = ( SetupDiEnumDeviceInfo_t ) GetProcAddress ( setupApiMod , " SetupDiEnumDeviceInfo " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiEnumDeviceInfo not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > SetupDiSetClassInstallParamsA = ( SetupDiSetClassInstallParamsA_t ) GetProcAddress ( setupApiMod , " SetupDiSetClassInstallParamsA " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiSetClassInstallParamsA not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
2015-08-13 22:52:54 +00:00
if ( ! ( this - > SetupDiGetDeviceInstanceIdA = ( SetupDiGetDeviceInstanceIdA_t ) GetProcAddress ( setupApiMod , " SetupDiGetDeviceInstanceIdA " ) ) ) {
fprintf ( stderr , " FATAL: SetupDiGetDeviceInstanceIdA not found in setupapi.dll \r \n " ) ;
_exit ( 1 ) ;
}
2015-07-30 18:31:38 +00:00
newDevMod = LoadLibraryA ( " newdev.dll " ) ;
if ( ! newDevMod ) {
fprintf ( stderr , " FATAL: unable to dynamically load newdev.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > UpdateDriverForPlugAndPlayDevicesA = ( UpdateDriverForPlugAndPlayDevicesA_t ) GetProcAddress ( newDevMod , " UpdateDriverForPlugAndPlayDevicesA " ) ) ) {
fprintf ( stderr , " FATAL: UpdateDriverForPlugAndPlayDevicesA not found in newdev.dll \r \n " ) ;
_exit ( 1 ) ;
}
cfgMgrMod = LoadLibraryA ( " cfgmgr32.dll " ) ;
if ( ! cfgMgrMod ) {
fprintf ( stderr , " FATAL: unable to dynamically load cfgmgr32.dll \r \n " ) ;
_exit ( 1 ) ;
}
if ( ! ( this - > CM_Get_Device_ID_ExA = ( CM_Get_Device_ID_ExA_t ) GetProcAddress ( cfgMgrMod , " CM_Get_Device_ID_ExA " ) ) ) {
fprintf ( stderr , " FATAL: CM_Get_Device_ID_ExA not found in cfgmgr32.dll \r \n " ) ;
_exit ( 1 ) ;
}
2015-04-24 22:05:28 +00:00
}
2015-07-30 18:31:38 +00:00
BOOL is64Bit ; // is the system 64-bit, regardless of whether this binary is or not
std : : string tapDriverPath ;
std : : string tapDriverName ;
UpdateDriverForPlugAndPlayDevicesA_t UpdateDriverForPlugAndPlayDevicesA ;
SetupDiGetINFClassA_t SetupDiGetINFClassA ;
SetupDiCreateDeviceInfoList_t SetupDiCreateDeviceInfoList ;
SetupDiCreateDeviceInfoA_t SetupDiCreateDeviceInfoA ;
SetupDiSetDeviceRegistryPropertyA_t SetupDiSetDeviceRegistryPropertyA ;
SetupDiCallClassInstaller_t SetupDiCallClassInstaller ;
SetupDiDestroyDeviceInfoList_t SetupDiDestroyDeviceInfoList ;
SetupDiGetClassDevsExA_t SetupDiGetClassDevsExA ;
SetupDiOpenDeviceInfoA_t SetupDiOpenDeviceInfoA ;
SetupDiEnumDeviceInfo_t SetupDiEnumDeviceInfo ;
SetupDiSetClassInstallParamsA_t SetupDiSetClassInstallParamsA ;
2015-08-13 22:52:54 +00:00
SetupDiGetDeviceInstanceIdA_t SetupDiGetDeviceInstanceIdA ;
2015-07-30 18:31:38 +00:00
CM_Get_Device_ID_ExA_t CM_Get_Device_ID_ExA ;
private :
HMODULE setupApiMod ;
HMODULE newDevMod ;
HMODULE cfgMgrMod ;
2015-04-24 22:05:28 +00:00
} ;
static const WindowsEthernetTapEnv WINENV ;
2014-04-08 23:10:48 +00:00
// Only create or delete devices one at a time
2014-04-08 19:00:21 +00:00
static Mutex _systemTapInitLock ;
2014-01-27 06:47:08 +00:00
2015-07-30 18:31:38 +00:00
// Only perform installation or uninstallation options one at a time
static Mutex _systemDeviceManagementLock ;
2015-05-08 23:31:50 +00:00
} // anonymous namespace
2015-08-13 22:52:54 +00:00
std : : string WindowsEthernetTap : : addNewPersistentTapDevice ( const char * pathToInf , std : : string & deviceInstanceId )
2015-07-30 18:31:38 +00:00
{
Mutex : : Lock _l ( _systemDeviceManagementLock ) ;
GUID classGuid ;
2015-11-13 20:14:28 +00:00
char className [ 1024 ] ;
2015-07-30 18:31:38 +00:00
if ( ! WINENV . SetupDiGetINFClassA ( pathToInf , & classGuid , className , sizeof ( className ) , ( PDWORD ) 0 ) ) {
return std : : string ( " SetupDiGetINFClassA() failed -- unable to read zttap driver INF file " ) ;
}
HDEVINFO deviceInfoSet = WINENV . SetupDiCreateDeviceInfoList ( & classGuid , ( HWND ) 0 ) ;
if ( deviceInfoSet = = INVALID_HANDLE_VALUE ) {
return std : : string ( " SetupDiCreateDeviceInfoList() failed " ) ;
}
SP_DEVINFO_DATA deviceInfoData ;
memset ( & deviceInfoData , 0 , sizeof ( deviceInfoData ) ) ;
deviceInfoData . cbSize = sizeof ( deviceInfoData ) ;
if ( ! WINENV . SetupDiCreateDeviceInfoA ( deviceInfoSet , className , & classGuid , ( PCSTR ) 0 , ( HWND ) 0 , DICD_GENERATE_ID , & deviceInfoData ) ) {
WINENV . SetupDiDestroyDeviceInfoList ( deviceInfoSet ) ;
return std : : string ( " SetupDiCreateDeviceInfoA() failed " ) ;
}
if ( ! WINENV . SetupDiSetDeviceRegistryPropertyA ( deviceInfoSet , & deviceInfoData , SPDRP_HARDWAREID , ( const BYTE * ) WINENV . tapDriverName . c_str ( ) , ( DWORD ) ( WINENV . tapDriverName . length ( ) + 1 ) ) ) {
WINENV . SetupDiDestroyDeviceInfoList ( deviceInfoSet ) ;
return std : : string ( " SetupDiSetDeviceRegistryPropertyA() failed " ) ;
}
if ( ! WINENV . SetupDiCallClassInstaller ( DIF_REGISTERDEVICE , deviceInfoSet , & deviceInfoData ) ) {
WINENV . SetupDiDestroyDeviceInfoList ( deviceInfoSet ) ;
return std : : string ( " SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed " ) ;
}
2015-07-31 00:00:57 +00:00
// HACK: During upgrades, this can fail while the installer is still running. So make 60 attempts
// with a 1s delay between each attempt.
bool driverInstalled = false ;
for ( int retryCounter = 0 ; retryCounter < 60 ; + + retryCounter ) {
BOOL rebootRequired = FALSE ;
if ( WINENV . UpdateDriverForPlugAndPlayDevicesA ( ( HWND ) 0 , WINENV . tapDriverName . c_str ( ) , pathToInf , INSTALLFLAG_FORCE | INSTALLFLAG_NONINTERACTIVE , & rebootRequired ) ) {
driverInstalled = true ;
break ;
} else Sleep ( 1000 ) ;
}
if ( ! driverInstalled ) {
2015-07-30 18:31:38 +00:00
WINENV . SetupDiDestroyDeviceInfoList ( deviceInfoSet ) ;
2015-07-31 00:00:57 +00:00
return std : : string ( " UpdateDriverForPlugAndPlayDevices() failed (made 60 attempts) " ) ;
2015-07-30 18:31:38 +00:00
}
2015-08-13 22:52:54 +00:00
char iidbuf [ 1024 ] ;
DWORD iidReqSize = sizeof ( iidbuf ) ;
if ( WINENV . SetupDiGetDeviceInstanceIdA ( deviceInfoSet , & deviceInfoData , iidbuf , sizeof ( iidbuf ) , & iidReqSize ) ) {
deviceInstanceId = iidbuf ;
} // failure here is not fatal since we only need this on Vista and 2008 -- other versions fill it into the registry automatically
2015-07-30 18:31:38 +00:00
WINENV . SetupDiDestroyDeviceInfoList ( deviceInfoSet ) ;
return std : : string ( ) ;
}
std : : string WindowsEthernetTap : : destroyAllLegacyPersistentTapDevices ( )
{
2015-11-13 20:14:28 +00:00
char subkeyName [ 1024 ] ;
char subkeyClass [ 1024 ] ;
char data [ 1024 ] ;
2015-07-30 18:31:38 +00:00
std : : set < std : : string > instanceIdPathsToRemove ;
{
HKEY nwAdapters ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ Control \\ Class \\ {4D36E972-E325-11CE-BFC1-08002BE10318} " , 0 , KEY_READ | KEY_WRITE , & nwAdapters ) ! = ERROR_SUCCESS )
return std : : string ( " Could not open registry key " ) ;
for ( DWORD subkeyIndex = 0 ; ; + + subkeyIndex ) {
DWORD type ;
DWORD dataLen ;
DWORD subkeyNameLen = sizeof ( subkeyName ) ;
DWORD subkeyClassLen = sizeof ( subkeyClass ) ;
FILETIME lastWriteTime ;
if ( RegEnumKeyExA ( nwAdapters , subkeyIndex , subkeyName , & subkeyNameLen , ( DWORD * ) 0 , subkeyClass , & subkeyClassLen , & lastWriteTime ) = = ERROR_SUCCESS ) {
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " ComponentId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
data [ dataLen ] = ' \0 ' ;
if ( ( ! strnicmp ( data , " zttap " , 5 ) ) & & ( WINENV . tapDriverName ! = data ) ) {
std : : string instanceIdPath ;
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " DeviceInstanceID " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS )
instanceIdPath . assign ( data , dataLen ) ;
if ( instanceIdPath . length ( ) ! = 0 )
instanceIdPathsToRemove . insert ( instanceIdPath ) ;
}
}
} else break ; // end of list or failure
}
RegCloseKey ( nwAdapters ) ;
}
2015-07-31 00:00:57 +00:00
std : : string errlist ;
2015-07-30 18:31:38 +00:00
for ( std : : set < std : : string > : : iterator iidp ( instanceIdPathsToRemove . begin ( ) ) ; iidp ! = instanceIdPathsToRemove . end ( ) ; + + iidp ) {
std : : string err = deletePersistentTapDevice ( iidp - > c_str ( ) ) ;
2015-07-31 00:00:57 +00:00
if ( err . length ( ) > 0 ) {
if ( errlist . length ( ) > 0 )
errlist . push_back ( ' , ' ) ;
errlist . append ( err ) ;
}
2015-07-30 18:31:38 +00:00
}
2015-07-31 00:00:57 +00:00
return errlist ;
2015-07-30 18:31:38 +00:00
}
std : : string WindowsEthernetTap : : destroyAllPersistentTapDevices ( )
{
2015-11-13 20:14:28 +00:00
char subkeyName [ 1024 ] ;
char subkeyClass [ 1024 ] ;
char data [ 1024 ] ;
2015-07-30 18:31:38 +00:00
std : : set < std : : string > instanceIdPathsToRemove ;
{
HKEY nwAdapters ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ Control \\ Class \\ {4D36E972-E325-11CE-BFC1-08002BE10318} " , 0 , KEY_READ | KEY_WRITE , & nwAdapters ) ! = ERROR_SUCCESS )
return std : : string ( " Could not open registry key " ) ;
for ( DWORD subkeyIndex = 0 ; ; + + subkeyIndex ) {
DWORD type ;
DWORD dataLen ;
DWORD subkeyNameLen = sizeof ( subkeyName ) ;
DWORD subkeyClassLen = sizeof ( subkeyClass ) ;
FILETIME lastWriteTime ;
if ( RegEnumKeyExA ( nwAdapters , subkeyIndex , subkeyName , & subkeyNameLen , ( DWORD * ) 0 , subkeyClass , & subkeyClassLen , & lastWriteTime ) = = ERROR_SUCCESS ) {
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " ComponentId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
data [ dataLen ] = ' \0 ' ;
if ( ! strnicmp ( data , " zttap " , 5 ) ) {
std : : string instanceIdPath ;
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " DeviceInstanceID " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS )
instanceIdPath . assign ( data , dataLen ) ;
if ( instanceIdPath . length ( ) ! = 0 )
instanceIdPathsToRemove . insert ( instanceIdPath ) ;
}
}
} else break ; // end of list or failure
}
RegCloseKey ( nwAdapters ) ;
}
2015-07-31 00:00:57 +00:00
std : : string errlist ;
2015-07-30 18:31:38 +00:00
for ( std : : set < std : : string > : : iterator iidp ( instanceIdPathsToRemove . begin ( ) ) ; iidp ! = instanceIdPathsToRemove . end ( ) ; + + iidp ) {
std : : string err = deletePersistentTapDevice ( iidp - > c_str ( ) ) ;
2015-07-31 00:00:57 +00:00
if ( err . length ( ) > 0 ) {
if ( errlist . length ( ) > 0 )
errlist . push_back ( ' , ' ) ;
errlist . append ( err ) ;
}
2015-07-30 18:31:38 +00:00
}
2015-07-31 00:00:57 +00:00
return errlist ;
2015-07-30 18:31:38 +00:00
}
std : : string WindowsEthernetTap : : deletePersistentTapDevice ( const char * instanceId )
{
char iid [ 256 ] ;
SP_REMOVEDEVICE_PARAMS rmdParams ;
memset ( & rmdParams , 0 , sizeof ( rmdParams ) ) ;
rmdParams . ClassInstallHeader . cbSize = sizeof ( SP_CLASSINSTALL_HEADER ) ;
rmdParams . ClassInstallHeader . InstallFunction = DIF_REMOVE ;
rmdParams . Scope = DI_REMOVEDEVICE_GLOBAL ;
rmdParams . HwProfile = 0 ;
Mutex : : Lock _l ( _systemDeviceManagementLock ) ;
HDEVINFO devInfo = WINENV . SetupDiGetClassDevsExA ( ( const GUID * ) 0 , ( PCSTR ) 0 , ( HWND ) 0 , DIGCF_ALLCLASSES , ( HDEVINFO ) 0 , ( PCSTR ) 0 , ( PVOID ) 0 ) ;
if ( devInfo = = INVALID_HANDLE_VALUE )
return std : : string ( " SetupDiGetClassDevsExA() failed " ) ;
WINENV . SetupDiOpenDeviceInfoA ( devInfo , instanceId , ( HWND ) 0 , 0 , ( PSP_DEVINFO_DATA ) 0 ) ;
SP_DEVINFO_DATA devInfoData ;
memset ( & devInfoData , 0 , sizeof ( devInfoData ) ) ;
devInfoData . cbSize = sizeof ( devInfoData ) ;
for ( DWORD devIndex = 0 ; WINENV . SetupDiEnumDeviceInfo ( devInfo , devIndex , & devInfoData ) ; devIndex + + ) {
if ( ( WINENV . CM_Get_Device_ID_ExA ( devInfoData . DevInst , iid , sizeof ( iid ) , 0 , ( HMACHINE ) 0 ) = = CR_SUCCESS ) & & ( ! strcmp ( iid , instanceId ) ) ) {
if ( ! WINENV . SetupDiSetClassInstallParamsA ( devInfo , & devInfoData , & rmdParams . ClassInstallHeader , sizeof ( rmdParams ) ) ) {
WINENV . SetupDiDestroyDeviceInfoList ( devInfo ) ;
return std : : string ( " SetupDiSetClassInstallParams() failed " ) ;
}
if ( ! WINENV . SetupDiCallClassInstaller ( DIF_REMOVE , devInfo , & devInfoData ) ) {
WINENV . SetupDiDestroyDeviceInfoList ( devInfo ) ;
return std : : string ( " SetupDiCallClassInstaller(DIF_REMOVE) failed " ) ;
}
WINENV . SetupDiDestroyDeviceInfoList ( devInfo ) ;
return std : : string ( ) ;
}
}
WINENV . SetupDiDestroyDeviceInfoList ( devInfo ) ;
return std : : string ( " instance ID not found " ) ;
}
bool WindowsEthernetTap : : setPersistentTapDeviceState ( const char * instanceId , bool enabled )
{
char iid [ 256 ] ;
SP_PROPCHANGE_PARAMS params ;
Mutex : : Lock _l ( _systemDeviceManagementLock ) ;
HDEVINFO devInfo = WINENV . SetupDiGetClassDevsExA ( ( const GUID * ) 0 , ( PCSTR ) 0 , ( HWND ) 0 , DIGCF_ALLCLASSES , ( HDEVINFO ) 0 , ( PCSTR ) 0 , ( PVOID ) 0 ) ;
if ( devInfo = = INVALID_HANDLE_VALUE )
return false ;
WINENV . SetupDiOpenDeviceInfoA ( devInfo , instanceId , ( HWND ) 0 , 0 , ( PSP_DEVINFO_DATA ) 0 ) ;
SP_DEVINFO_DATA devInfoData ;
memset ( & devInfoData , 0 , sizeof ( devInfoData ) ) ;
devInfoData . cbSize = sizeof ( devInfoData ) ;
for ( DWORD devIndex = 0 ; WINENV . SetupDiEnumDeviceInfo ( devInfo , devIndex , & devInfoData ) ; devIndex + + ) {
if ( ( WINENV . CM_Get_Device_ID_ExA ( devInfoData . DevInst , iid , sizeof ( iid ) , 0 , ( HMACHINE ) 0 ) = = CR_SUCCESS ) & & ( ! strcmp ( iid , instanceId ) ) ) {
memset ( & params , 0 , sizeof ( params ) ) ;
params . ClassInstallHeader . cbSize = sizeof ( SP_CLASSINSTALL_HEADER ) ;
params . ClassInstallHeader . InstallFunction = DIF_PROPERTYCHANGE ;
params . StateChange = enabled ? DICS_ENABLE : DICS_DISABLE ;
params . Scope = DICS_FLAG_GLOBAL ;
params . HwProfile = 0 ;
WINENV . SetupDiSetClassInstallParamsA ( devInfo , & devInfoData , & params . ClassInstallHeader , sizeof ( params ) ) ;
WINENV . SetupDiCallClassInstaller ( DIF_PROPERTYCHANGE , devInfo , & devInfoData ) ;
memset ( & params , 0 , sizeof ( params ) ) ;
params . ClassInstallHeader . cbSize = sizeof ( SP_CLASSINSTALL_HEADER ) ;
params . ClassInstallHeader . InstallFunction = DIF_PROPERTYCHANGE ;
params . StateChange = enabled ? DICS_ENABLE : DICS_DISABLE ;
params . Scope = DICS_FLAG_CONFIGSPECIFIC ;
params . HwProfile = 0 ;
WINENV . SetupDiSetClassInstallParamsA ( devInfo , & devInfoData , & params . ClassInstallHeader , sizeof ( params ) ) ;
WINENV . SetupDiCallClassInstaller ( DIF_PROPERTYCHANGE , devInfo , & devInfoData ) ;
WINENV . SetupDiDestroyDeviceInfoList ( devInfo ) ;
return true ;
}
}
WINENV . SetupDiDestroyDeviceInfoList ( devInfo ) ;
return false ;
}
2014-04-08 19:00:21 +00:00
WindowsEthernetTap : : WindowsEthernetTap (
2015-04-24 22:05:28 +00:00
const char * hp ,
2013-08-14 17:23:25 +00:00
const MAC & mac ,
unsigned int mtu ,
2014-08-05 04:48:59 +00:00
unsigned int metric ,
uint64_t nwid ,
const char * friendlyName ,
2017-03-28 00:03:17 +00:00
void ( * handler ) ( void * , void * , uint64_t , const MAC & , const MAC & , unsigned int , unsigned int , const void * , unsigned int ) ,
2014-08-05 04:48:59 +00:00
void * arg ) :
2013-08-14 17:23:25 +00:00
_handler ( handler ) ,
2013-08-25 22:18:02 +00:00
_arg ( arg ) ,
2015-04-24 22:05:28 +00:00
_mac ( mac ) ,
2014-08-13 00:20:34 +00:00
_nwid ( nwid ) ,
2017-05-05 02:31:50 +00:00
_mtu ( mtu ) ,
2013-08-25 22:18:02 +00:00
_tap ( INVALID_HANDLE_VALUE ) ,
2017-10-16 19:16:05 +00:00
_friendlyName ( friendlyName ) ,
2013-08-25 22:18:02 +00:00
_injectSemaphore ( INVALID_HANDLE_VALUE ) ,
2015-04-24 22:05:28 +00:00
_pathToHelpers ( hp ) ,
2014-01-28 07:13:36 +00:00
_run ( true ) ,
2014-04-08 19:00:21 +00:00
_initialized ( false ) ,
2014-04-08 22:47:33 +00:00
_enabled ( true )
2013-08-14 17:23:25 +00:00
{
2015-11-13 20:14:28 +00:00
char subkeyName [ 1024 ] ;
char subkeyClass [ 1024 ] ;
char data [ 1024 ] ;
2014-08-05 04:48:59 +00:00
char tag [ 24 ] ;
2013-08-26 21:22:20 +00:00
2015-07-30 18:31:38 +00:00
// We "tag" registry entries with the network ID to identify persistent devices
2017-07-06 23:11:11 +00:00
OSUtils : : ztsnprintf ( tag , sizeof ( tag ) , " %.16llx " , ( unsigned long long ) nwid ) ;
2013-08-22 18:30:55 +00:00
2015-07-30 18:31:38 +00:00
Mutex : : Lock _l ( _systemTapInitLock ) ;
2015-04-25 22:22:43 +00:00
2013-08-21 14:18:05 +00:00
HKEY nwAdapters ;
2013-08-22 18:30:55 +00:00
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ Control \\ Class \\ {4D36E972-E325-11CE-BFC1-08002BE10318} " , 0 , KEY_READ | KEY_WRITE , & nwAdapters ) ! = ERROR_SUCCESS )
2013-08-21 14:18:05 +00:00
throw std : : runtime_error ( " unable to open registry key for network adapter enumeration " ) ;
2014-08-05 04:48:59 +00:00
// Look for the tap instance that corresponds with this network
2014-03-06 22:06:31 +00:00
for ( DWORD subkeyIndex = 0 ; ; + + subkeyIndex ) {
2013-08-22 18:30:55 +00:00
DWORD type ;
DWORD dataLen ;
2013-08-21 14:18:05 +00:00
DWORD subkeyNameLen = sizeof ( subkeyName ) ;
DWORD subkeyClassLen = sizeof ( subkeyClass ) ;
FILETIME lastWriteTime ;
2014-03-06 22:06:31 +00:00
if ( RegEnumKeyExA ( nwAdapters , subkeyIndex , subkeyName , & subkeyNameLen , ( DWORD * ) 0 , subkeyClass , & subkeyClassLen , & lastWriteTime ) = = ERROR_SUCCESS ) {
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " ComponentId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
2015-07-30 18:31:38 +00:00
data [ dataLen ] = ( char ) 0 ;
if ( WINENV . tapDriverName = = data ) {
2014-03-06 22:06:31 +00:00
std : : string instanceId ;
type = 0 ;
dataLen = sizeof ( data ) ;
2015-07-31 00:00:57 +00:00
if ( RegGetValueA ( nwAdapters , subkeyName , " NetCfgInstanceId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS )
2014-03-06 22:06:31 +00:00
instanceId . assign ( data , dataLen ) ;
2013-08-22 18:30:55 +00:00
2014-03-06 22:06:31 +00:00
std : : string instanceIdPath ;
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " DeviceInstanceID " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS )
instanceIdPath . assign ( data , dataLen ) ;
2014-04-08 19:00:21 +00:00
if ( ( _netCfgInstanceId . length ( ) = = 0 ) & & ( instanceId . length ( ) ! = 0 ) & & ( instanceIdPath . length ( ) ! = 0 ) ) {
2013-08-26 21:48:47 +00:00
type = 0 ;
dataLen = sizeof ( data ) ;
2014-03-06 22:06:31 +00:00
if ( RegGetValueA ( nwAdapters , subkeyName , " _ZeroTierTapIdentifier " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
data [ dataLen ] = ' \0 ' ;
if ( ! strcmp ( data , tag ) ) {
2014-04-08 19:00:21 +00:00
_netCfgInstanceId = instanceId ;
_deviceInstanceId = instanceIdPath ;
2014-08-05 04:48:59 +00:00
2017-05-05 02:31:50 +00:00
_mySubkeyName = subkeyName ;
2014-03-06 22:06:31 +00:00
break ; // found it!
2013-08-22 18:30:55 +00:00
}
}
}
2013-08-21 14:18:05 +00:00
}
2014-03-06 22:06:31 +00:00
}
} else break ; // no more subkeys or error occurred enumerating them
2013-08-22 18:30:55 +00:00
}
// If there is no device, try to create one
2014-08-13 00:20:34 +00:00
bool creatingNewDevice = ( _netCfgInstanceId . length ( ) = = 0 ) ;
2015-08-13 22:52:54 +00:00
std : : string newDeviceInstanceId ;
2014-08-13 00:20:34 +00:00
if ( creatingNewDevice ) {
2015-07-31 00:00:57 +00:00
for ( int getNewAttemptCounter = 0 ; getNewAttemptCounter < 2 ; + + getNewAttemptCounter ) {
for ( DWORD subkeyIndex = 0 ; ; + + subkeyIndex ) {
DWORD type ;
DWORD dataLen ;
DWORD subkeyNameLen = sizeof ( subkeyName ) ;
DWORD subkeyClassLen = sizeof ( subkeyClass ) ;
FILETIME lastWriteTime ;
if ( RegEnumKeyExA ( nwAdapters , subkeyIndex , subkeyName , & subkeyNameLen , ( DWORD * ) 0 , subkeyClass , & subkeyClassLen , & lastWriteTime ) = = ERROR_SUCCESS ) {
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " ComponentId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
data [ dataLen ] = ' \0 ' ;
2015-07-30 18:31:38 +00:00
2015-07-31 00:00:57 +00:00
if ( WINENV . tapDriverName = = data ) {
type = 0 ;
dataLen = sizeof ( data ) ;
if ( ( RegGetValueA ( nwAdapters , subkeyName , " _ZeroTierTapIdentifier " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) ! = ERROR_SUCCESS ) | | ( dataLen < = 0 ) ) {
2014-03-06 22:06:31 +00:00
type = 0 ;
dataLen = sizeof ( data ) ;
2015-07-31 00:00:57 +00:00
if ( RegGetValueA ( nwAdapters , subkeyName , " NetCfgInstanceId " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS ) {
RegSetKeyValueA ( nwAdapters , subkeyName , " _ZeroTierTapIdentifier " , REG_SZ , tag , ( DWORD ) ( strlen ( tag ) + 1 ) ) ;
2014-03-06 22:06:31 +00:00
2015-07-31 00:00:57 +00:00
_netCfgInstanceId . assign ( data , dataLen ) ;
2014-03-06 22:06:31 +00:00
2015-07-31 00:00:57 +00:00
type = 0 ;
dataLen = sizeof ( data ) ;
if ( RegGetValueA ( nwAdapters , subkeyName , " DeviceInstanceID " , RRF_RT_ANY , & type , ( PVOID ) data , & dataLen ) = = ERROR_SUCCESS )
_deviceInstanceId . assign ( data , dataLen ) ;
2017-05-05 02:31:50 +00:00
_mySubkeyName = subkeyName ;
2015-07-31 00:00:57 +00:00
// Disable DHCP by default on new devices
HKEY tcpIpInterfaces ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ services \\ Tcpip \\ Parameters \\ Interfaces " , 0 , KEY_READ | KEY_WRITE , & tcpIpInterfaces ) = = ERROR_SUCCESS ) {
DWORD enable = 0 ;
RegSetKeyValueA ( tcpIpInterfaces , _netCfgInstanceId . c_str ( ) , " EnableDHCP " , REG_DWORD , & enable , sizeof ( enable ) ) ;
RegCloseKey ( tcpIpInterfaces ) ;
}
break ; // found an unused zttap device
}
2013-08-22 18:30:55 +00:00
}
}
}
2015-07-31 00:00:57 +00:00
} else break ; // no more keys or error occurred
}
if ( _netCfgInstanceId . length ( ) > 0 ) {
break ; // found an unused zttap device
} else {
// no unused zttap devices, so create one
2015-08-13 22:52:54 +00:00
std : : string errm = addNewPersistentTapDevice ( ( std : : string ( _pathToHelpers ) + WINENV . tapDriverPath ) . c_str ( ) , newDeviceInstanceId ) ;
2015-07-31 00:00:57 +00:00
if ( errm . length ( ) > 0 )
throw std : : runtime_error ( std : : string ( " unable to create new device instance: " ) + errm ) ;
}
2013-08-21 14:18:05 +00:00
}
}
2014-04-08 19:00:21 +00:00
if ( _netCfgInstanceId . length ( ) > 0 ) {
2014-08-05 04:48:59 +00:00
char tmps [ 64 ] ;
2017-07-06 23:11:11 +00:00
unsigned int tmpsl = OSUtils : : ztsnprintf ( tmps , sizeof ( tmps ) , " %.2X-%.2X-%.2X-%.2X-%.2X-%.2X " , ( unsigned int ) mac [ 0 ] , ( unsigned int ) mac [ 1 ] , ( unsigned int ) mac [ 2 ] , ( unsigned int ) mac [ 3 ] , ( unsigned int ) mac [ 4 ] , ( unsigned int ) mac [ 5 ] ) + 1 ;
2017-05-05 02:31:50 +00:00
RegSetKeyValueA ( nwAdapters , _mySubkeyName . c_str ( ) , " NetworkAddress " , REG_SZ , tmps , tmpsl ) ;
RegSetKeyValueA ( nwAdapters , _mySubkeyName . c_str ( ) , " MAC " , REG_SZ , tmps , tmpsl ) ;
2017-07-06 23:11:11 +00:00
tmpsl = OSUtils : : ztsnprintf ( tmps , sizeof ( tmps ) , " %d " , mtu ) ;
2017-05-05 02:31:50 +00:00
RegSetKeyValueA ( nwAdapters , _mySubkeyName . c_str ( ) , " MTU " , REG_SZ , tmps , tmpsl ) ;
2014-08-13 00:20:34 +00:00
2016-11-30 23:18:38 +00:00
DWORD tmp = 0 ;
2017-05-05 02:31:50 +00:00
RegSetKeyValueA ( nwAdapters , _mySubkeyName . c_str ( ) , " *NdisDeviceType " , REG_DWORD , ( LPCVOID ) & tmp , sizeof ( tmp ) ) ;
2014-08-13 00:20:34 +00:00
tmp = IF_TYPE_ETHERNET_CSMACD ;
2017-05-05 02:31:50 +00:00
RegSetKeyValueA ( nwAdapters , _mySubkeyName . c_str ( ) , " *IfType " , REG_DWORD , ( LPCVOID ) & tmp , sizeof ( tmp ) ) ;
2014-08-13 00:20:34 +00:00
if ( creatingNewDevice ) {
2015-08-13 22:52:54 +00:00
// Vista/2008 does not set this
if ( newDeviceInstanceId . length ( ) > 0 )
2017-05-05 02:31:50 +00:00
RegSetKeyValueA ( nwAdapters , _mySubkeyName . c_str ( ) , " DeviceInstanceID " , REG_SZ , newDeviceInstanceId . c_str ( ) , ( DWORD ) newDeviceInstanceId . length ( ) ) ;
2015-08-13 22:52:54 +00:00
2015-07-30 18:31:38 +00:00
// Set EnableDHCP to 0 by default on new devices
2014-08-13 00:20:34 +00:00
tmp = 0 ;
2017-05-05 02:31:50 +00:00
RegSetKeyValueA ( nwAdapters , _mySubkeyName . c_str ( ) , " EnableDHCP " , REG_DWORD , ( LPCVOID ) & tmp , sizeof ( tmp ) ) ;
2014-08-13 00:20:34 +00:00
}
2016-01-12 22:04:55 +00:00
RegCloseKey ( nwAdapters ) ;
2014-03-06 22:06:31 +00:00
} else {
2016-01-12 22:04:55 +00:00
RegCloseKey ( nwAdapters ) ;
2014-03-06 22:06:31 +00:00
throw std : : runtime_error ( " unable to find or create tap adapter " ) ;
2013-08-23 21:39:21 +00:00
}
2013-08-26 21:22:20 +00:00
{
2015-07-30 18:31:38 +00:00
char nobraces [ 128 ] ; // strip braces from GUID before converting it, because Windows
2014-04-08 19:00:21 +00:00
const char * nbtmp1 = _netCfgInstanceId . c_str ( ) ;
2013-08-26 21:22:20 +00:00
char * nbtmp2 = nobraces ;
while ( * nbtmp1 ) {
if ( ( * nbtmp1 ! = ' { ' ) & & ( * nbtmp1 ! = ' } ' ) )
* nbtmp2 + + = * nbtmp1 ;
+ + nbtmp1 ;
}
* nbtmp2 = ( char ) 0 ;
if ( UuidFromStringA ( ( RPC_CSTR ) nobraces , & _deviceGuid ) ! = RPC_S_OK )
throw std : : runtime_error ( " unable to convert instance ID GUID to native GUID (invalid NetCfgInstanceId in registry?) " ) ;
}
2015-07-30 18:31:38 +00:00
// Get the LUID, which is one of like four fucking ways to refer to a network device in Windows
2014-08-05 04:48:59 +00:00
if ( ConvertInterfaceGuidToLuid ( & _deviceGuid , & _deviceLuid ) ! = NO_ERROR )
throw std : : runtime_error ( " unable to convert device interface GUID to LUID " ) ;
2017-01-05 19:43:26 +00:00
//_initialized = true;
2015-05-08 23:31:50 +00:00
2014-08-05 04:48:59 +00:00
if ( friendlyName )
setFriendlyName ( friendlyName ) ;
2013-08-25 22:18:02 +00:00
_injectSemaphore = CreateSemaphore ( NULL , 0 , 1 , NULL ) ;
_thread = Thread : : start ( this ) ;
2013-08-14 17:23:25 +00:00
}
2014-04-08 19:00:21 +00:00
WindowsEthernetTap : : ~ WindowsEthernetTap ( )
2013-08-14 17:23:25 +00:00
{
2020-08-12 01:55:42 +00:00
WinDNSHelper : : removeDNS ( _nwid ) ;
2013-08-25 22:18:02 +00:00
_run = false ;
ReleaseSemaphore ( _injectSemaphore , 1 , NULL ) ;
Thread : : join ( _thread ) ;
CloseHandle ( _injectSemaphore ) ;
2015-07-30 18:31:38 +00:00
setPersistentTapDeviceState ( _deviceInstanceId . c_str ( ) , false ) ;
2014-04-08 19:00:21 +00:00
}
2014-01-21 21:07:22 +00:00
2014-04-08 19:00:21 +00:00
void WindowsEthernetTap : : setEnabled ( bool en )
{
2014-04-08 22:47:33 +00:00
_enabled = en ;
2013-08-14 17:23:25 +00:00
}
2014-04-08 19:00:21 +00:00
bool WindowsEthernetTap : : enabled ( ) const
{
return _enabled ;
}
2015-04-24 22:05:28 +00:00
bool WindowsEthernetTap : : addIp ( const InetAddress & ip )
2013-08-14 17:23:25 +00:00
{
2014-01-23 22:15:00 +00:00
if ( ! ip . netmaskBits ( ) ) // sanity check... netmask of 0.0.0.0 is WUT?
2013-08-26 21:22:20 +00:00
return false ;
2016-12-09 19:36:01 +00:00
2015-07-30 21:10:32 +00:00
Mutex : : Lock _l ( _assignedIps_m ) ;
if ( std : : find ( _assignedIps . begin ( ) , _assignedIps . end ( ) , ip ) ! = _assignedIps . end ( ) )
return true ;
_assignedIps . push_back ( ip ) ;
_syncIps ( ) ;
2014-04-08 23:10:48 +00:00
return true ;
2013-08-14 17:23:25 +00:00
}
2015-04-24 22:05:28 +00:00
bool WindowsEthernetTap : : removeIp ( const InetAddress & ip )
2013-08-14 17:23:25 +00:00
{
2016-12-09 19:36:01 +00:00
if ( ip . isV6 ( ) )
return true ;
2015-07-30 21:10:32 +00:00
{
Mutex : : Lock _l ( _assignedIps_m ) ;
std : : vector < InetAddress > : : iterator aip ( std : : find ( _assignedIps . begin ( ) , _assignedIps . end ( ) , ip ) ) ;
if ( aip ! = _assignedIps . end ( ) )
_assignedIps . erase ( aip ) ;
}
2014-01-28 07:13:36 +00:00
if ( ! _initialized )
return false ;
2015-07-30 21:10:32 +00:00
2013-08-26 21:22:20 +00:00
try {
MIB_UNICASTIPADDRESS_TABLE * ipt = ( MIB_UNICASTIPADDRESS_TABLE * ) 0 ;
if ( GetUnicastIpAddressTable ( AF_UNSPEC , & ipt ) = = NO_ERROR ) {
2016-03-03 03:06:29 +00:00
if ( ( ipt ) & & ( ipt - > NumEntries > 0 ) ) {
for ( DWORD i = 0 ; i < ( DWORD ) ipt - > NumEntries ; + + i ) {
if ( ipt - > Table [ i ] . InterfaceLuid . Value = = _deviceLuid . Value ) {
InetAddress addr ;
switch ( ipt - > Table [ i ] . Address . si_family ) {
case AF_INET :
addr . set ( & ( ipt - > Table [ i ] . Address . Ipv4 . sin_addr . S_un . S_addr ) , 4 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
break ;
case AF_INET6 :
addr . set ( ipt - > Table [ i ] . Address . Ipv6 . sin6_addr . u . Byte , 16 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
if ( addr . ipScope ( ) = = InetAddress : : IP_SCOPE_LINK_LOCAL )
continue ; // can't remove link-local IPv6 addresses
2014-08-13 00:20:34 +00:00
break ;
}
2016-03-03 03:06:29 +00:00
if ( addr = = ip ) {
DeleteUnicastIpAddressEntry ( & ( ipt - > Table [ i ] ) ) ;
FreeMibTable ( ipt ) ;
2017-07-06 23:29:11 +00:00
if ( ip . isV4 ( ) ) {
std : : vector < std : : string > regIps ( _getRegistryIPv4Value ( " IPAddress " ) ) ;
std : : vector < std : : string > regSubnetMasks ( _getRegistryIPv4Value ( " SubnetMask " ) ) ;
char ipbuf [ 64 ] ;
std : : string ipstr ( ip . toIpString ( ipbuf ) ) ;
for ( std : : vector < std : : string > : : iterator rip ( regIps . begin ( ) ) , rm ( regSubnetMasks . begin ( ) ) ; ( ( rip ! = regIps . end ( ) ) & & ( rm ! = regSubnetMasks . end ( ) ) ) ; + + rip , + + rm ) {
if ( * rip = = ipstr ) {
regIps . erase ( rip ) ;
regSubnetMasks . erase ( rm ) ;
_setRegistryIPv4Value ( " IPAddress " , regIps ) ;
_setRegistryIPv4Value ( " SubnetMask " , regSubnetMasks ) ;
break ;
}
}
}
2014-08-13 00:20:34 +00:00
2016-03-03 03:06:29 +00:00
return true ;
}
2013-08-26 21:22:20 +00:00
}
}
}
2014-01-23 22:15:00 +00:00
FreeMibTable ( ( PVOID ) ipt ) ;
2013-08-26 21:22:20 +00:00
}
2014-08-05 04:48:59 +00:00
} catch ( . . . ) { }
2013-08-14 17:23:25 +00:00
return false ;
}
2015-04-24 22:05:28 +00:00
std : : vector < InetAddress > WindowsEthernetTap : : ips ( ) const
2013-08-26 21:22:20 +00:00
{
2017-07-06 23:29:11 +00:00
static const InetAddress linkLocalLoopback ( " fe80::1/64 " ) ; // what is this and why does Windows assign it?
2015-04-24 22:05:28 +00:00
std : : vector < InetAddress > addrs ;
2013-08-26 21:22:20 +00:00
2014-01-28 07:13:36 +00:00
if ( ! _initialized )
return addrs ;
2013-08-26 21:22:20 +00:00
try {
MIB_UNICASTIPADDRESS_TABLE * ipt = ( MIB_UNICASTIPADDRESS_TABLE * ) 0 ;
if ( GetUnicastIpAddressTable ( AF_UNSPEC , & ipt ) = = NO_ERROR ) {
2016-03-03 03:06:29 +00:00
if ( ( ipt ) & & ( ipt - > NumEntries > 0 ) ) {
for ( DWORD i = 0 ; i < ( DWORD ) ipt - > NumEntries ; + + i ) {
if ( ipt - > Table [ i ] . InterfaceLuid . Value = = _deviceLuid . Value ) {
switch ( ipt - > Table [ i ] . Address . si_family ) {
case AF_INET : {
InetAddress ip ( & ( ipt - > Table [ i ] . Address . Ipv4 . sin_addr . S_un . S_addr ) , 4 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
if ( ip ! = InetAddress : : LO4 )
addrs . push_back ( ip ) ;
} break ;
case AF_INET6 : {
InetAddress ip ( ipt - > Table [ i ] . Address . Ipv6 . sin6_addr . u . Byte , 16 , ipt - > Table [ i ] . OnLinkPrefixLength ) ;
if ( ( ip ! = linkLocalLoopback ) & & ( ip ! = InetAddress : : LO6 ) )
addrs . push_back ( ip ) ;
} break ;
}
2013-08-26 21:22:20 +00:00
}
}
}
2013-08-27 15:15:14 +00:00
FreeMibTable ( ipt ) ;
2013-08-26 21:22:20 +00:00
}
2014-04-08 19:00:21 +00:00
} catch ( . . . ) { } // sanity check, shouldn't happen unless out of memory
2013-08-26 21:22:20 +00:00
2015-04-24 22:05:28 +00:00
std : : sort ( addrs . begin ( ) , addrs . end ( ) ) ;
2016-03-03 03:06:29 +00:00
addrs . erase ( std : : unique ( addrs . begin ( ) , addrs . end ( ) ) , addrs . end ( ) ) ;
2015-04-24 22:05:28 +00:00
2013-08-26 21:22:20 +00:00
return addrs ;
}
2014-04-08 19:00:21 +00:00
void WindowsEthernetTap : : put ( const MAC & from , const MAC & to , unsigned int etherType , const void * data , unsigned int len )
2013-08-14 17:23:25 +00:00
{
2017-05-05 02:31:50 +00:00
if ( ( ! _initialized ) | | ( ! _enabled ) | | ( _tap = = INVALID_HANDLE_VALUE ) | | ( len > _mtu ) )
2014-01-28 07:13:36 +00:00
return ;
2014-08-05 04:48:59 +00:00
Mutex : : Lock _l ( _injectPending_m ) ;
2018-01-31 00:07:41 +00:00
_injectPending . emplace ( ) ;
_injectPending . back ( ) . len = len + 14 ;
char * const d = _injectPending . back ( ) . data ;
2014-08-05 04:48:59 +00:00
to . copyTo ( d , 6 ) ;
from . copyTo ( d + 6 , 6 ) ;
d [ 12 ] = ( char ) ( ( etherType > > 8 ) & 0xff ) ;
d [ 13 ] = ( char ) ( etherType & 0xff ) ;
memcpy ( d + 14 , data , len ) ;
2013-08-25 22:18:02 +00:00
ReleaseSemaphore ( _injectSemaphore , 1 , NULL ) ;
2013-08-14 17:23:25 +00:00
}
2014-04-08 19:00:21 +00:00
std : : string WindowsEthernetTap : : deviceName ( ) const
2013-08-14 17:23:25 +00:00
{
2014-08-05 04:48:59 +00:00
char tmp [ 1024 ] ;
if ( ConvertInterfaceLuidToNameA ( & _deviceLuid , tmp , sizeof ( tmp ) ) ! = NO_ERROR )
return std : : string ( " [ConvertInterfaceLuidToName() failed] " ) ;
return std : : string ( tmp ) ;
2013-08-14 17:23:25 +00:00
}
2014-08-05 04:48:59 +00:00
void WindowsEthernetTap : : setFriendlyName ( const char * dn )
2014-01-27 06:47:08 +00:00
{
2014-08-05 04:48:59 +00:00
if ( ! _initialized )
return ;
2021-03-16 14:12:55 +00:00
2014-08-05 04:48:59 +00:00
HKEY ifp ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , ( std : : string ( " SYSTEM \\ CurrentControlSet \\ Control \\ Network \\ {4D36E972-E325-11CE-BFC1-08002BE10318} \\ " ) + _netCfgInstanceId ) . c_str ( ) , 0 , KEY_READ | KEY_WRITE , & ifp ) = = ERROR_SUCCESS ) {
RegSetKeyValueA ( ifp , " Connection " , " Name " , REG_SZ , ( LPCVOID ) dn , ( DWORD ) ( strlen ( dn ) + 1 ) ) ;
RegCloseKey ( ifp ) ;
}
2019-07-16 22:16:49 +00:00
2020-11-26 04:06:43 +00:00
HRESULT hr = S_OK ;
2019-07-16 22:16:49 +00:00
INetSharingManager * nsm ;
hr = CoCreateInstance ( __uuidof ( NetSharingManager ) , NULL , CLSCTX_ALL , __uuidof ( INetSharingManager ) , ( void * * ) & nsm ) ;
if ( hr ! = S_OK ) return ;
bool found = false ;
INetSharingEveryConnectionCollection * nsecc = nullptr ;
hr = nsm - > get_EnumEveryConnection ( & nsecc ) ;
if ( ! nsecc ) {
fprintf ( stderr , " Failed to get NSM connections " ) ;
return ;
}
IEnumVARIANT * ev = nullptr ;
IUnknown * unk = nullptr ;
hr = nsecc - > get__NewEnum ( & unk ) ;
if ( unk ) {
hr = unk - > QueryInterface ( __uuidof ( IEnumVARIANT ) , ( void * * ) & ev ) ;
unk - > Release ( ) ;
}
if ( ev ) {
VARIANT v ;
VariantInit ( & v ) ;
while ( ( S_OK = = ev - > Next ( 1 , & v , NULL ) ) & & found = = FALSE ) {
if ( V_VT ( & v ) = = VT_UNKNOWN ) {
INetConnection * nc = nullptr ;
V_UNKNOWN ( & v ) - > QueryInterface ( __uuidof ( INetConnection ) , ( void * * ) & nc ) ;
if ( nc ) {
NETCON_PROPERTIES * ncp = nullptr ;
nc - > GetProperties ( & ncp ) ;
2023-03-02 02:00:59 +00:00
if ( ncp ! = nullptr ) {
GUID curId = ncp - > guidId ;
if ( curId = = _deviceGuid ) {
wchar_t wtext [ 255 ] ;
mbstowcs ( wtext , dn , strlen ( dn ) + 1 ) ;
nc - > Rename ( wtext ) ;
found = true ;
}
2019-07-16 22:16:49 +00:00
}
nc - > Release ( ) ;
}
}
VariantClear ( & v ) ;
}
ev - > Release ( ) ;
}
nsecc - > Release ( ) ;
2021-03-16 14:12:55 +00:00
_friendlyName_m . lock ( ) ;
_friendlyName = dn ;
_friendlyName_m . unlock ( ) ;
}
std : : string WindowsEthernetTap : : friendlyName ( ) const
{
Mutex : : Lock l ( _friendlyName_m ) ;
return _friendlyName ;
2014-01-27 06:47:08 +00:00
}
2015-04-24 22:05:28 +00:00
void WindowsEthernetTap : : scanMulticastGroups ( std : : vector < MulticastGroup > & added , std : : vector < MulticastGroup > & removed )
2013-08-14 17:23:25 +00:00
{
2014-01-28 07:13:36 +00:00
if ( ! _initialized )
2015-04-24 22:05:28 +00:00
return ;
2014-04-08 22:47:33 +00:00
HANDLE t = _tap ;
if ( t = = INVALID_HANDLE_VALUE )
2015-04-24 22:05:28 +00:00
return ;
2013-08-27 20:45:22 +00:00
2015-04-24 22:05:28 +00:00
std : : vector < MulticastGroup > newGroups ;
2013-08-27 20:45:22 +00:00
2014-01-21 21:07:22 +00:00
// The ZT1 tap driver supports an IOCTL to get multicast memberships at the L2
// level... something Windows does not seem to expose ordinarily. This lets
// pretty much anything work... IPv4, IPv6, IPX, oldskool Netbios, who knows...
unsigned char mcastbuf [ TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE ] ;
DWORD bytesReturned = 0 ;
2018-02-23 21:42:02 +00:00
if ( DeviceIoControl ( t , TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS , ( LPVOID ) mcastbuf , sizeof ( mcastbuf ) , ( LPVOID ) mcastbuf , sizeof ( mcastbuf ) , & bytesReturned , NULL ) ) {
2016-03-03 03:06:29 +00:00
if ( ( bytesReturned > 0 ) & & ( bytesReturned < = TAP_WIN_IOCTL_GET_MULTICAST_MEMBERSHIPS_OUTPUT_BUF_SIZE ) ) { // sanity check
MAC mac ;
DWORD i = 0 ;
while ( ( i + 6 ) < = bytesReturned ) {
mac . setTo ( mcastbuf + i , 6 ) ;
i + = 6 ;
if ( ( mac . isMulticast ( ) ) & & ( ! mac . isBroadcast ( ) ) ) {
// exclude the nulls that may be returned or any other junk Windows puts in there
newGroups . push_back ( MulticastGroup ( mac , 0 ) ) ;
}
2014-01-21 21:07:22 +00:00
}
}
}
2013-08-27 20:45:22 +00:00
2015-04-24 22:05:28 +00:00
std : : vector < InetAddress > allIps ( ips ( ) ) ;
for ( std : : vector < InetAddress > : : iterator ip ( allIps . begin ( ) ) ; ip ! = allIps . end ( ) ; + + ip )
newGroups . push_back ( MulticastGroup : : deriveMulticastGroupForAddressResolution ( * ip ) ) ;
2014-01-21 21:07:22 +00:00
2015-04-24 22:05:28 +00:00
std : : sort ( newGroups . begin ( ) , newGroups . end ( ) ) ;
2016-03-03 03:06:29 +00:00
newGroups . erase ( std : : unique ( newGroups . begin ( ) , newGroups . end ( ) ) , newGroups . end ( ) ) ;
2015-04-24 22:05:28 +00:00
for ( std : : vector < MulticastGroup > : : iterator m ( newGroups . begin ( ) ) ; m ! = newGroups . end ( ) ; + + m ) {
if ( ! std : : binary_search ( _multicastGroups . begin ( ) , _multicastGroups . end ( ) , * m ) )
added . push_back ( * m ) ;
2013-08-27 20:45:22 +00:00
}
2015-04-24 22:05:28 +00:00
for ( std : : vector < MulticastGroup > : : iterator m ( _multicastGroups . begin ( ) ) ; m ! = _multicastGroups . end ( ) ; + + m ) {
if ( ! std : : binary_search ( newGroups . begin ( ) , newGroups . end ( ) , * m ) )
removed . push_back ( * m ) ;
2013-08-27 20:45:22 +00:00
}
2015-04-24 22:05:28 +00:00
_multicastGroups . swap ( newGroups ) ;
2014-10-03 18:59:50 +00:00
}
2017-05-05 02:31:50 +00:00
void WindowsEthernetTap : : setMtu ( unsigned int mtu )
{
if ( mtu ! = _mtu ) {
2017-05-05 02:50:02 +00:00
_mtu = mtu ;
HKEY nwAdapters ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ Control \\ Class \\ {4D36E972-E325-11CE-BFC1-08002BE10318} " , 0 , KEY_READ | KEY_WRITE , & nwAdapters ) = = ERROR_SUCCESS ) {
char tmps [ 64 ] ;
2017-07-06 23:11:11 +00:00
unsigned int tmpsl = OSUtils : : ztsnprintf ( tmps , sizeof ( tmps ) , " %d " , mtu ) ;
2017-05-05 02:50:02 +00:00
RegSetKeyValueA ( nwAdapters , _mySubkeyName . c_str ( ) , " MTU " , REG_SZ , tmps , tmpsl ) ;
RegCloseKey ( nwAdapters ) ;
}
2017-05-05 02:31:50 +00:00
}
}
2016-06-21 20:54:03 +00:00
NET_IFINDEX WindowsEthernetTap : : interfaceIndex ( ) const
{
NET_IFINDEX idx = - 1 ;
if ( ConvertInterfaceLuidToIndex ( & _deviceLuid , & idx ) = = NO_ERROR )
return idx ;
return - 1 ;
}
2014-04-08 19:00:21 +00:00
void WindowsEthernetTap : : threadMain ( )
2013-08-23 21:39:21 +00:00
throw ( )
2013-08-14 17:23:25 +00:00
{
2020-11-19 22:10:34 +00:00
HRESULT hres = CoInitializeEx ( 0 , COINIT_MULTITHREADED ) ;
if ( FAILED ( hres ) ) {
fprintf ( stderr , " WinEthernetTap: COM initialization failed " ) ;
return ;
}
2017-05-05 02:31:50 +00:00
char tapReadBuf [ ZT_MAX_MTU + 32 ] ;
2015-06-12 14:02:04 +00:00
char tapPath [ 128 ] ;
2013-08-25 22:18:02 +00:00
HANDLE wait4 [ 3 ] ;
2015-06-12 14:02:04 +00:00
OVERLAPPED tapOvlRead , tapOvlWrite ;
2014-04-08 22:47:33 +00:00
2017-07-06 23:11:11 +00:00
OSUtils : : ztsnprintf ( tapPath , sizeof ( tapPath ) , " \\ \\ . \\ Global \\ %s.tap " , _netCfgInstanceId . c_str ( ) ) ;
2015-06-12 14:02:04 +00:00
try {
while ( _run ) {
2015-07-30 18:57:48 +00:00
// Because Windows
2016-03-03 02:37:24 +00:00
Sleep ( 250 ) ;
2015-07-30 18:57:48 +00:00
setPersistentTapDeviceState ( _deviceInstanceId . c_str ( ) , false ) ;
2016-03-03 02:37:24 +00:00
Sleep ( 250 ) ;
2015-07-30 18:57:48 +00:00
setPersistentTapDeviceState ( _deviceInstanceId . c_str ( ) , true ) ;
2016-03-03 02:37:24 +00:00
Sleep ( 250 ) ;
2015-07-30 18:57:48 +00:00
setPersistentTapDeviceState ( _deviceInstanceId . c_str ( ) , false ) ;
2016-03-03 02:37:24 +00:00
Sleep ( 250 ) ;
2015-07-30 18:31:38 +00:00
setPersistentTapDeviceState ( _deviceInstanceId . c_str ( ) , true ) ;
2016-03-03 02:37:24 +00:00
Sleep ( 250 ) ;
2015-05-20 01:13:20 +00:00
2015-06-12 14:02:04 +00:00
_tap = CreateFileA ( tapPath , GENERIC_READ | GENERIC_WRITE , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED , NULL ) ;
if ( _tap = = INVALID_HANDLE_VALUE ) {
2016-03-03 02:37:24 +00:00
Sleep ( 250 ) ;
2015-06-12 14:02:04 +00:00
continue ;
}
2014-04-08 22:47:33 +00:00
2015-06-12 14:02:04 +00:00
{
uint32_t tmpi = 1 ;
DWORD bytesReturned = 0 ;
DeviceIoControl ( _tap , TAP_WIN_IOCTL_SET_MEDIA_STATUS , & tmpi , sizeof ( tmpi ) , & tmpi , sizeof ( tmpi ) , & bytesReturned , NULL ) ;
}
2014-08-22 00:49:05 +00:00
# ifdef ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE
2015-06-12 14:02:04 +00:00
{
/* This inserts a fake default route and a fake ARP entry, forcing
* Windows to detect this as a " real " network and apply proper
* firewall rules .
*
* This hack is completely stupid , but Windows made me do it
* by being broken and insane .
*
* Background : Windows tries to detect its network location by
* matching it to the ARP address of the default route . Networks
* without default routes are " unidentified networks " and cannot
* have their firewall classification changed by the user ( easily ) .
*
* Yes , you read that right .
*
* The common workaround is to set * NdisDeviceType to 1 , which
* totally disables all Windows firewall functionality . This is
* the answer you ' ll find on most forums for things like OpenVPN .
*
* Yes , you read that right .
*
* The default route workaround is also known , but for this to
* work there must be a known default IP that resolves to a known
* ARP address . This works for an OpenVPN tunnel , but not here
* because this isn ' t a tunnel . It ' s a mesh . There is no " other
* end , " or any other known always on IP.
*
* So let ' s make a fake one and shove it in there along with its
* fake static ARP entry . Also makes it instant - on and static .
*
* We ' ll have to see what DHCP does with this . In the future we
* probably will not want to do this on DHCP - enabled networks , so
* when we enable DHCP we will go in and yank this wacko hacko from
* the routing table before doing so .
*
* Like Jesse Pinkman would say : " YEEEEAAH BITCH! " */
const uint32_t fakeIp = htonl ( 0x19fffffe ) ; // 25.255.255.254 -- unrouted IPv4 block
for ( int i = 0 ; i < 8 ; + + i ) {
MIB_IPNET_ROW2 ipnr ;
memset ( & ipnr , 0 , sizeof ( ipnr ) ) ;
ipnr . Address . si_family = AF_INET ;
ipnr . Address . Ipv4 . sin_addr . s_addr = fakeIp ;
ipnr . InterfaceLuid . Value = _deviceLuid . Value ;
ipnr . PhysicalAddress [ 0 ] = _mac [ 0 ] ^ 0x10 ; // just make something up that's consistent and not part of this net
ipnr . PhysicalAddress [ 1 ] = 0x00 ;
ipnr . PhysicalAddress [ 2 ] = ( UCHAR ) ( ( _deviceGuid . Data1 > > 24 ) & 0xff ) ;
ipnr . PhysicalAddress [ 3 ] = ( UCHAR ) ( ( _deviceGuid . Data1 > > 16 ) & 0xff ) ;
ipnr . PhysicalAddress [ 4 ] = ( UCHAR ) ( ( _deviceGuid . Data1 > > 8 ) & 0xff ) ;
ipnr . PhysicalAddress [ 5 ] = ( UCHAR ) ( _deviceGuid . Data1 & 0xff ) ;
ipnr . PhysicalAddressLength = 6 ;
ipnr . State = NlnsPermanent ;
ipnr . IsRouter = 1 ;
ipnr . IsUnreachable = 0 ;
ipnr . ReachabilityTime . LastReachable = 0x0fffffff ;
ipnr . ReachabilityTime . LastUnreachable = 1 ;
DWORD result = CreateIpNetEntry2 ( & ipnr ) ;
if ( result ! = NO_ERROR )
2016-03-03 02:37:24 +00:00
Sleep ( 250 ) ;
2015-06-12 14:02:04 +00:00
else break ;
}
for ( int i = 0 ; i < 8 ; + + i ) {
MIB_IPFORWARD_ROW2 nr ;
memset ( & nr , 0 , sizeof ( nr ) ) ;
InitializeIpForwardEntry ( & nr ) ;
nr . InterfaceLuid . Value = _deviceLuid . Value ;
nr . DestinationPrefix . Prefix . si_family = AF_INET ; // rest is left as 0.0.0.0/0
nr . NextHop . si_family = AF_INET ;
nr . NextHop . Ipv4 . sin_addr . s_addr = fakeIp ;
nr . Metric = 9999 ; // do not use as real default route
nr . Protocol = MIB_IPPROTO_NETMGMT ;
DWORD result = CreateIpForwardEntry2 ( & nr ) ;
if ( result ! = NO_ERROR )
2016-03-03 02:37:24 +00:00
Sleep ( 250 ) ;
2015-06-12 14:02:04 +00:00
else break ;
}
2014-08-22 00:49:05 +00:00
}
# endif
2015-05-08 23:31:50 +00:00
2015-07-30 21:10:32 +00:00
// Assign or re-assign any should-be-assigned IPs in case we have restarted
{
Mutex : : Lock _l ( _assignedIps_m ) ;
_syncIps ( ) ;
}
2015-06-12 14:02:04 +00:00
memset ( & tapOvlRead , 0 , sizeof ( tapOvlRead ) ) ;
tapOvlRead . hEvent = CreateEvent ( NULL , TRUE , FALSE , NULL ) ;
memset ( & tapOvlWrite , 0 , sizeof ( tapOvlWrite ) ) ;
tapOvlWrite . hEvent = CreateEvent ( NULL , TRUE , FALSE , NULL ) ;
wait4 [ 0 ] = _injectSemaphore ;
wait4 [ 1 ] = tapOvlRead . hEvent ;
wait4 [ 2 ] = tapOvlWrite . hEvent ; // only included if writeInProgress is true
ReadFile ( _tap , tapReadBuf , sizeof ( tapReadBuf ) , NULL , & tapOvlRead ) ;
bool writeInProgress = false ;
ULONGLONG timeOfLastBorkCheck = GetTickCount64 ( ) ;
2017-05-05 02:31:50 +00:00
_initialized = true ;
2017-05-05 02:50:02 +00:00
unsigned int oldmtu = _mtu ;
2017-01-05 19:43:26 +00:00
2017-10-16 19:16:05 +00:00
setFriendlyName ( _friendlyName . c_str ( ) ) ;
2015-06-12 14:02:04 +00:00
while ( _run ) {
DWORD waitResult = WaitForMultipleObjectsEx ( writeInProgress ? 3 : 2 , wait4 , FALSE , 2500 , TRUE ) ;
2017-05-05 02:50:02 +00:00
if ( ! _run ) break ; // will also break outer while(_run) since _run is false
// Check for changes in MTU and break to restart tap device to reconfigure in this case
if ( _mtu ! = oldmtu )
break ;
2015-06-12 14:02:04 +00:00
// Check for issues with adapter and close/reopen if any are detected. This
// check fixes a while boatload of Windows adapter 'coma' issues after
// sleep/wake and when adapters are added/removed. Basically if the tap
// device is borked, whack it.
{
ULONGLONG tc = GetTickCount64 ( ) ;
if ( ( tc - timeOfLastBorkCheck ) > = 2500 ) {
timeOfLastBorkCheck = tc ;
2015-07-07 00:58:04 +00:00
char aabuf [ 16384 ] ;
ULONG aalen = sizeof ( aabuf ) ;
if ( GetAdaptersAddresses ( AF_UNSPEC , GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME , ( void * ) 0 , reinterpret_cast < PIP_ADAPTER_ADDRESSES > ( aabuf ) , & aalen ) = = NO_ERROR ) {
2015-06-12 14:02:04 +00:00
bool isBorked = false ;
2015-07-07 00:58:04 +00:00
PIP_ADAPTER_ADDRESSES aa = reinterpret_cast < PIP_ADAPTER_ADDRESSES > ( aabuf ) ;
while ( aa ) {
if ( _deviceLuid . Value = = aa - > Luid . Value ) {
isBorked = ( aa - > OperStatus ! = IfOperStatusUp ) ;
2015-06-12 14:02:04 +00:00
break ;
}
2015-07-07 00:58:04 +00:00
aa = aa - > Next ;
2015-06-12 14:02:04 +00:00
}
2015-07-07 00:58:04 +00:00
2015-06-12 14:02:04 +00:00
if ( isBorked ) {
// Close and reopen tap device if there's an issue (outer loop)
break ;
}
}
2015-05-08 23:31:50 +00:00
}
2013-08-25 22:18:02 +00:00
}
2016-03-03 02:37:24 +00:00
if ( ( waitResult = = WAIT_TIMEOUT ) | | ( waitResult = = WAIT_FAILED ) ) {
Sleep ( 250 ) ; // guard against spinning under some conditions
2015-06-12 14:02:04 +00:00
continue ;
2016-03-03 02:37:24 +00:00
}
2015-06-12 14:02:04 +00:00
if ( HasOverlappedIoCompleted ( & tapOvlRead ) ) {
DWORD bytesRead = 0 ;
if ( GetOverlappedResult ( _tap , & tapOvlRead , & bytesRead , FALSE ) ) {
if ( ( bytesRead > 14 ) & & ( _enabled ) ) {
MAC to ( tapReadBuf , 6 ) ;
MAC from ( tapReadBuf + 6 , 6 ) ;
unsigned int etherType = ( ( ( ( unsigned int ) tapReadBuf [ 12 ] ) & 0xff ) < < 8 ) | ( ( ( unsigned int ) tapReadBuf [ 13 ] ) & 0xff ) ;
try {
2017-03-28 00:03:17 +00:00
_handler ( _arg , ( void * ) 0 , _nwid , from , to , etherType , 0 , tapReadBuf + 14 , bytesRead - 14 ) ;
2015-06-12 14:02:04 +00:00
} catch ( . . . ) { } // handlers should not throw
}
}
2017-05-05 02:31:50 +00:00
ReadFile ( _tap , tapReadBuf , ZT_MAX_MTU + 32 , NULL , & tapOvlRead ) ;
2015-06-12 14:02:04 +00:00
}
2015-05-08 23:31:50 +00:00
2015-06-12 14:02:04 +00:00
if ( writeInProgress ) {
if ( HasOverlappedIoCompleted ( & tapOvlWrite ) ) {
writeInProgress = false ;
_injectPending_m . lock ( ) ;
_injectPending . pop ( ) ;
} else continue ; // still writing, so skip code below and wait
} else _injectPending_m . lock ( ) ;
if ( ! _injectPending . empty ( ) ) {
2018-01-31 00:07:41 +00:00
WriteFile ( _tap , _injectPending . front ( ) . data , _injectPending . front ( ) . len , NULL , & tapOvlWrite ) ;
2015-06-12 14:02:04 +00:00
writeInProgress = true ;
}
2013-08-25 22:18:02 +00:00
2015-06-12 14:02:04 +00:00
_injectPending_m . unlock ( ) ;
}
2013-08-25 22:18:02 +00:00
2015-06-12 14:02:04 +00:00
CancelIo ( _tap ) ;
2014-04-08 22:47:33 +00:00
2015-06-12 14:02:04 +00:00
CloseHandle ( tapOvlRead . hEvent ) ;
CloseHandle ( tapOvlWrite . hEvent ) ;
CloseHandle ( _tap ) ;
_tap = INVALID_HANDLE_VALUE ;
2014-04-08 22:47:33 +00:00
2015-06-12 14:02:04 +00:00
// We will restart and re-open the tap unless _run == false
}
} catch ( . . . ) { } // catch unexpected exceptions -- this should not happen but would prevent program crash or other weird issues since threads should not throw
2020-11-19 22:12:24 +00:00
CoUninitialize ( ) ;
2013-08-14 17:23:25 +00:00
}
2014-08-05 04:48:59 +00:00
NET_IFINDEX WindowsEthernetTap : : _getDeviceIndex ( )
{
MIB_IF_TABLE2 * ift = ( MIB_IF_TABLE2 * ) 0 ;
if ( GetIfTable2Ex ( MibIfTableRaw , & ift ) ! = NO_ERROR )
throw std : : runtime_error ( " GetIfTable2Ex() failed " ) ;
2014-02-28 23:04:50 +00:00
2016-03-03 03:06:29 +00:00
if ( ift - > NumEntries > 0 ) {
for ( ULONG i = 0 ; i < ift - > NumEntries ; + + i ) {
if ( ift - > Table [ i ] . InterfaceLuid . Value = = _deviceLuid . Value ) {
NET_IFINDEX idx = ift - > Table [ i ] . InterfaceIndex ;
FreeMibTable ( ift ) ;
return idx ;
}
2014-08-05 04:48:59 +00:00
}
2014-02-28 23:04:50 +00:00
}
2014-08-05 04:48:59 +00:00
FreeMibTable ( & ift ) ;
throw std : : runtime_error ( " interface not found " ) ;
2014-02-28 23:04:50 +00:00
}
2014-08-13 00:20:34 +00:00
std : : vector < std : : string > WindowsEthernetTap : : _getRegistryIPv4Value ( const char * regKey )
{
std : : vector < std : : string > value ;
HKEY tcpIpInterfaces ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ services \\ Tcpip \\ Parameters \\ Interfaces " , 0 , KEY_READ | KEY_WRITE , & tcpIpInterfaces ) = = ERROR_SUCCESS ) {
char buf [ 16384 ] ;
DWORD len = sizeof ( buf ) ;
DWORD kt = REG_MULTI_SZ ;
if ( RegGetValueA ( tcpIpInterfaces , _netCfgInstanceId . c_str ( ) , regKey , 0 , & kt , & buf , & len ) = = ERROR_SUCCESS ) {
switch ( kt ) {
case REG_SZ :
if ( len > 0 )
value . push_back ( std : : string ( buf ) ) ;
break ;
case REG_MULTI_SZ : {
for ( DWORD k = 0 , s = 0 ; k < len ; + + k ) {
if ( ! buf [ k ] ) {
if ( s < k ) {
value . push_back ( std : : string ( buf + s ) ) ;
s = k + 1 ;
} else break ;
}
}
} break ;
}
}
RegCloseKey ( tcpIpInterfaces ) ;
}
return value ;
}
void WindowsEthernetTap : : _setRegistryIPv4Value ( const char * regKey , const std : : vector < std : : string > & value )
{
std : : string regMulti ;
for ( std : : vector < std : : string > : : const_iterator s ( value . begin ( ) ) ; s ! = value . end ( ) ; + + s ) {
regMulti . append ( * s ) ;
regMulti . push_back ( ( char ) 0 ) ;
}
HKEY tcpIpInterfaces ;
if ( RegOpenKeyExA ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ services \\ Tcpip \\ Parameters \\ Interfaces " , 0 , KEY_READ | KEY_WRITE , & tcpIpInterfaces ) = = ERROR_SUCCESS ) {
if ( regMulti . length ( ) > 0 ) {
regMulti . push_back ( ( char ) 0 ) ;
RegSetKeyValueA ( tcpIpInterfaces , _netCfgInstanceId . c_str ( ) , regKey , REG_MULTI_SZ , regMulti . data ( ) , ( DWORD ) regMulti . length ( ) ) ;
} else {
RegDeleteKeyValueA ( tcpIpInterfaces , _netCfgInstanceId . c_str ( ) , regKey ) ;
}
RegCloseKey ( tcpIpInterfaces ) ;
}
}
2015-07-30 21:10:32 +00:00
void WindowsEthernetTap : : _syncIps ( )
{
// assumes _assignedIps_m is locked
if ( ! _initialized )
return ;
std : : vector < InetAddress > haveIps ( ips ( ) ) ;
for ( std : : vector < InetAddress > : : const_iterator aip ( _assignedIps . begin ( ) ) ; aip ! = _assignedIps . end ( ) ; + + aip ) {
if ( std : : find ( haveIps . begin ( ) , haveIps . end ( ) , * aip ) = = haveIps . end ( ) ) {
MIB_UNICASTIPADDRESS_ROW ipr ;
InitializeUnicastIpAddressEntry ( & ipr ) ;
if ( aip - > isV4 ( ) ) {
ipr . Address . Ipv4 . sin_family = AF_INET ;
ipr . Address . Ipv4 . sin_addr . S_un . S_addr = * ( ( const uint32_t * ) aip - > rawIpData ( ) ) ;
ipr . OnLinkPrefixLength = aip - > netmaskBits ( ) ;
if ( ipr . OnLinkPrefixLength > = 32 )
continue ;
} else if ( aip - > isV6 ( ) ) {
ipr . Address . Ipv6 . sin6_family = AF_INET6 ;
memcpy ( ipr . Address . Ipv6 . sin6_addr . u . Byte , aip - > rawIpData ( ) , 16 ) ;
ipr . OnLinkPrefixLength = aip - > netmaskBits ( ) ;
if ( ipr . OnLinkPrefixLength > = 128 )
continue ;
} else continue ;
ipr . PrefixOrigin = IpPrefixOriginManual ;
ipr . SuffixOrigin = IpSuffixOriginManual ;
ipr . ValidLifetime = 0xffffffff ;
ipr . PreferredLifetime = 0xffffffff ;
ipr . InterfaceLuid = _deviceLuid ;
ipr . InterfaceIndex = _getDeviceIndex ( ) ;
CreateUnicastIpAddressEntry ( & ipr ) ;
}
2017-07-06 23:29:11 +00:00
if ( aip - > isV4 ( ) ) {
char ipbuf [ 64 ] ;
std : : string ipStr ( aip - > toIpString ( ipbuf ) ) ;
std : : vector < std : : string > regIps ( _getRegistryIPv4Value ( " IPAddress " ) ) ;
if ( std : : find ( regIps . begin ( ) , regIps . end ( ) , ipStr ) = = regIps . end ( ) ) {
std : : vector < std : : string > regSubnetMasks ( _getRegistryIPv4Value ( " SubnetMask " ) ) ;
regIps . push_back ( ipStr ) ;
regSubnetMasks . push_back ( aip - > netmask ( ) . toIpString ( ipbuf ) ) ;
_setRegistryIPv4Value ( " IPAddress " , regIps ) ;
_setRegistryIPv4Value ( " SubnetMask " , regSubnetMasks ) ;
}
}
2015-07-30 21:10:32 +00:00
}
}
2020-08-05 23:04:02 +00:00
void WindowsEthernetTap : : setDns ( const char * domain , const std : : vector < InetAddress > & servers )
{
2020-08-12 01:55:42 +00:00
WinDNSHelper : : setDNS ( _nwid , domain , servers ) ;
2020-08-05 23:04:02 +00:00
}
2013-08-14 17:23:25 +00:00
} // namespace ZeroTier