Kill the devcon.exe dependency by dynamically loading cfgmgr32, newdev, and setupapi and using these functions directly.

This commit is contained in:
Adam Ierymenko 2015-07-30 11:31:38 -07:00
parent 7cd3c419ee
commit 8169b35482
9 changed files with 442 additions and 244 deletions

View file

@ -32,6 +32,7 @@
#include <WinSock2.h>
#include <Windows.h>
#include <tchar.h>
#include <malloc.h>
#include <winreg.h>
#include <wchar.h>
#include <ws2ipdef.h>
@ -42,6 +43,9 @@
#include <atlbase.h>
#include <netlistmgr.h>
#include <nldef.h>
#include <SetupAPI.h>
#include <newdev.h>
#include <cfgmgr32.h>
#include <iostream>
#include <set>
@ -55,15 +59,28 @@
#include "..\windows\TapDriver6\tap-windows.h"
// ff:ff:ff:ff:ff:ff with no ADI
//static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
// Create a fake unused default route to force detection of network type on networks without gateways
#define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE
// 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);
namespace ZeroTier {
namespace {
// Static/singleton class that when initialized loads a bunch of environment information and a few dynamically loaded DLLs
class WindowsEthernetTapEnv
{
public:
@ -71,29 +88,349 @@ public:
{
#ifdef _WIN64
is64Bit = TRUE;
devcon = "\\devcon_x64.exe";
tapDriverNdis5 = "\\tap-windows\\x64\\zttap200.inf";
tapDriverNdis6 = "\\tap-windows\\x64\\zttap300.inf";
tapDriverPath = "\\tap-windows\\x64\\zttap300.inf";
#else
is64Bit = FALSE;
IsWow64Process(GetCurrentProcess(),&is64Bit);
devcon = ((is64Bit == TRUE) ? "\\devcon_x64.exe" : "\\devcon_x86.exe");
tapDriverNdis5 = ((is64Bit == TRUE) ? "\\tap-windows\\x64\\zttap200.inf" : "\\tap-windows\\x86\\zttap200.inf");
tapDriverNdis6 = ((is64Bit == TRUE) ? "\\tap-windows\\x64\\zttap300.inf" : "\\tap-windows\\x86\\zttap300.inf");
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";
#endif
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);
}
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);
}
}
BOOL is64Bit;
const char *devcon;
const char *tapDriverNdis5;
const char *tapDriverNdis6;
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;
CM_Get_Device_ID_ExA_t CM_Get_Device_ID_ExA;
private:
HMODULE setupApiMod;
HMODULE newDevMod;
HMODULE cfgMgrMod;
};
static const WindowsEthernetTapEnv WINENV;
// Only create or delete devices one at a time
static Mutex _systemTapInitLock;
// Only perform installation or uninstallation options one at a time
static Mutex _systemDeviceManagementLock;
} // anonymous namespace
std::string WindowsEthernetTap::addNewPersistentTapDevice(const char *pathToInf)
{
Mutex::Lock _l(_systemDeviceManagementLock);
GUID classGuid;
char className[4096];
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");
}
BOOL rebootRequired = FALSE;
if (!WINENV.UpdateDriverForPlugAndPlayDevicesA((HWND)0,WINENV.tapDriverName.c_str(),pathToInf,INSTALLFLAG_FORCE|INSTALLFLAG_NONINTERACTIVE,&rebootRequired)) {
WINENV.SetupDiDestroyDeviceInfoList(deviceInfoSet);
return std::string("UpdateDriverForPlugAndPlayDevices() failed -- unable to install driver on device");
}
WINENV.SetupDiDestroyDeviceInfoList(deviceInfoSet);
return std::string();
}
std::string WindowsEthernetTap::destroyAllLegacyPersistentTapDevices()
{
char subkeyName[4096];
char subkeyClass[4096];
char data[4096];
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);
}
for(std::set<std::string>::iterator iidp(instanceIdPathsToRemove.begin());iidp!=instanceIdPathsToRemove.end();++iidp) {
std::string err = deletePersistentTapDevice(iidp->c_str());
if (err.length() > 0)
return err;
}
return std::string();
}
std::string WindowsEthernetTap::destroyAllPersistentTapDevices()
{
char subkeyName[4096];
char subkeyClass[4096];
char data[4096];
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);
}
for(std::set<std::string>::iterator iidp(instanceIdPathsToRemove.begin());iidp!=instanceIdPathsToRemove.end();++iidp) {
std::string err = deletePersistentTapDevice(iidp->c_str());
if (err.length() > 0)
return err;
}
return std::string();
}
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;
}
WindowsEthernetTap::WindowsEthernetTap(
const char *hp,
const MAC &mac,
@ -118,35 +455,21 @@ WindowsEthernetTap::WindowsEthernetTap(
char subkeyClass[4096];
char data[4096];
char tag[24];
std::set<std::string> existingDeviceInstances;
std::string mySubkeyName;
if (mtu > 2800)
throw std::runtime_error("MTU too large for Windows tap");
Mutex::Lock _l(_systemTapInitLock);
// We "tag" registry entries with the network ID to identify persistent devices
Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
// Use NDIS5 if it's installed, since we don't want to switch out the driver on
// pre-existing installs (yet). We won't ship NDIS5 anymore so new installs will
// use NDIS6.
std::string tapDriverPath(_pathToHelpers + WINENV.tapDriverNdis5);
const char *tapDriverName = "zttap200";
if (::PathFileExistsA(tapDriverPath.c_str()) == FALSE) {
tapDriverPath = _pathToHelpers + WINENV.tapDriverNdis6;
tapDriverName = "zttap300";
if (::PathFileExistsA(tapDriverPath.c_str()) == FALSE) {
throw std::runtime_error("no tap driver available: cannot find zttap300.inf (NDIS6) or zttap200.inf (NDIS5) under home path");
}
}
Mutex::Lock _l(_systemTapInitLock);
HKEY nwAdapters;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",0,KEY_READ|KEY_WRITE,&nwAdapters) != ERROR_SUCCESS)
throw std::runtime_error("unable to open registry key for network adapter enumeration");
std::set<std::string> existingDeviceInstances;
std::string mySubkeyName;
// We "tag" registry entries with the network ID to identify persistent devices
Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
// Look for the tap instance that corresponds with this network
for(DWORD subkeyIndex=0;;++subkeyIndex) {
DWORD type;
@ -158,8 +481,9 @@ WindowsEthernetTap::WindowsEthernetTap(
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)) {
data[dataLen] = (char)0;
if (WINENV.tapDriverName == data) {
std::string instanceId;
type = 0;
dataLen = sizeof(data);
@ -196,34 +520,9 @@ WindowsEthernetTap::WindowsEthernetTap(
// If there is no device, try to create one
bool creatingNewDevice = (_netCfgInstanceId.length() == 0);
if (creatingNewDevice) {
// Log devcon output to a file
HANDLE devconLog = CreateFileA((_pathToHelpers + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if (devconLog != INVALID_HANDLE_VALUE)
SetFilePointer(devconLog,0,0,FILE_END);
// Execute devcon to create a new tap device
STARTUPINFOA startupInfo;
startupInfo.cb = sizeof(startupInfo);
if (devconLog != INVALID_HANDLE_VALUE) {
SetFilePointer(devconLog,0,0,FILE_END);
startupInfo.hStdOutput = devconLog;
startupInfo.hStdError = devconLog;
}
PROCESS_INFORMATION processInfo;
memset(&startupInfo,0,sizeof(STARTUPINFOA));
memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WINENV.devcon + "\" install \"" + tapDriverPath + "\" " + tapDriverName).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
RegCloseKey(nwAdapters);
if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog);
throw std::runtime_error(std::string("unable to find or execute devcon at ") + WINENV.devcon);
}
WaitForSingleObject(processInfo.hProcess,INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog);
std::string errm = addNewPersistentTapDevice((std::string(_pathToHelpers) + WINENV.tapDriverPath).c_str());
if (errm.length() != 0)
throw std::runtime_error(errm);
// Scan for the new instance by simply looking for taps that weren't originally there...
for(DWORD subkeyIndex=0;;++subkeyIndex) {
@ -237,7 +536,8 @@ WindowsEthernetTap::WindowsEthernetTap(
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)) {
if (WINENV.tapDriverName == data) {
type = 0;
dataLen = sizeof(data);
if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
@ -281,6 +581,7 @@ WindowsEthernetTap::WindowsEthernetTap(
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
if (creatingNewDevice) {
// Set EnableDHCP to 0 by default on new devices
tmp = 0;
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
}
@ -291,7 +592,7 @@ WindowsEthernetTap::WindowsEthernetTap(
}
{
char nobraces[128];
char nobraces[128]; // strip braces from GUID before converting it, because Windows
const char *nbtmp1 = _netCfgInstanceId.c_str();
char *nbtmp2 = nobraces;
while (*nbtmp1) {
@ -304,17 +605,15 @@ WindowsEthernetTap::WindowsEthernetTap(
throw std::runtime_error("unable to convert instance ID GUID to native GUID (invalid NetCfgInstanceId in registry?)");
}
// Look up interface LUID... why are there (at least) four fucking ways to refer to a network device in Windows?
// Get the LUID, which is one of like four fucking ways to refer to a network device in Windows
if (ConvertInterfaceGuidToLuid(&_deviceGuid,&_deviceLuid) != NO_ERROR)
throw std::runtime_error("unable to convert device interface GUID to LUID");
// Certain functions can now work (e.g. ips())
_initialized = true;
if (friendlyName)
setFriendlyName(friendlyName);
// Start background thread that actually performs I/O
_injectSemaphore = CreateSemaphore(NULL,0,1,NULL);
_thread = Thread::start(this);
}
@ -325,7 +624,7 @@ WindowsEthernetTap::~WindowsEthernetTap()
ReleaseSemaphore(_injectSemaphore,1,NULL);
Thread::join(_thread);
CloseHandle(_injectSemaphore);
_disableTapDevice();
setPersistentTapDeviceState(_deviceInstanceId.c_str(),false);
}
void WindowsEthernetTap::setEnabled(bool en)
@ -572,14 +871,15 @@ void WindowsEthernetTap::threadMain()
try {
while (_run) {
_enableTapDevice();
setPersistentTapDeviceState(_deviceInstanceId.c_str(),true);
Sleep(500);
_tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL);
if (_tap == INVALID_HANDLE_VALUE) {
_disableTapDevice();
_enableTapDevice();
Sleep(1000);
setPersistentTapDeviceState(_deviceInstanceId.c_str(),false);
Sleep(500);
setPersistentTapDeviceState(_deviceInstanceId.c_str(),true);
Sleep(500);
continue;
}
@ -761,131 +1061,6 @@ void WindowsEthernetTap::threadMain()
} catch ( ... ) {} // catch unexpected exceptions -- this should not happen but would prevent program crash or other weird issues since threads should not throw
}
void WindowsEthernetTap::destroyAllPersistentTapDevices(const char *pathToHelpers)
{
char subkeyName[4096];
char subkeyClass[4096];
char data[4096];
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;
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);
}
for(std::set<std::string>::iterator iidp(instanceIdPathsToRemove.begin());iidp!=instanceIdPathsToRemove.end();++iidp)
deletePersistentTapDevice(pathToHelpers,iidp->c_str());
}
void WindowsEthernetTap::deletePersistentTapDevice(const char *pathToHelpers,const char *instanceId)
{
HANDLE devconLog = CreateFileA((std::string(pathToHelpers) + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
STARTUPINFOA startupInfo;
startupInfo.cb = sizeof(startupInfo);
if (devconLog != INVALID_HANDLE_VALUE) {
SetFilePointer(devconLog,0,0,FILE_END);
startupInfo.hStdOutput = devconLog;
startupInfo.hStdError = devconLog;
}
PROCESS_INFORMATION processInfo;
memset(&startupInfo,0,sizeof(STARTUPINFOA));
memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
if (CreateProcessA(NULL,(LPSTR)(std::string("\"") + pathToHelpers + WINENV.devcon + "\" remove @" + instanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
WaitForSingleObject(processInfo.hProcess,INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog);
}
bool WindowsEthernetTap::_disableTapDevice()
{
HANDLE devconLog = CreateFileA((_pathToHelpers + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if (devconLog != INVALID_HANDLE_VALUE)
SetFilePointer(devconLog,0,0,FILE_END);
STARTUPINFOA startupInfo;
startupInfo.cb = sizeof(startupInfo);
if (devconLog != INVALID_HANDLE_VALUE) {
startupInfo.hStdOutput = devconLog;
startupInfo.hStdError = devconLog;
}
PROCESS_INFORMATION processInfo;
memset(&startupInfo,0,sizeof(STARTUPINFOA));
memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WINENV.devcon + "\" disable @" + _deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog);
return false;
}
WaitForSingleObject(processInfo.hProcess,INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog);
return true;
}
bool WindowsEthernetTap::_enableTapDevice()
{
HANDLE devconLog = CreateFileA((_pathToHelpers + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if (devconLog != INVALID_HANDLE_VALUE)
SetFilePointer(devconLog,0,0,FILE_END);
STARTUPINFOA startupInfo;
startupInfo.cb = sizeof(startupInfo);
if (devconLog != INVALID_HANDLE_VALUE) {
startupInfo.hStdOutput = devconLog;
startupInfo.hStdError = devconLog;
}
PROCESS_INFORMATION processInfo;
memset(&startupInfo,0,sizeof(STARTUPINFOA));
memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WINENV.devcon + "\" enable @" + _deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog);
return false;
}
WaitForSingleObject(processInfo.hProcess,INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog);
return true;
}
NET_IFINDEX WindowsEthernetTap::_getDeviceIndex()
{
MIB_IF_TABLE2 *ift = (MIB_IF_TABLE2 *)0;