diff --git a/ext/bin/devcon/README.txt b/ext/bin/devcon/README.txt deleted file mode 100644 index 15cf1478c..000000000 --- a/ext/bin/devcon/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -This is the Microsoft "devcon" utility, which as far as I know is -fair game to redistribute. It's packaged with OpenVPN and several -other things and also distributed in source code form as an example -program by Microsoft. - -It's called by zerotier-one.exe to automagically install and remove -instances of the tap device. diff --git a/ext/bin/devcon/devcon_x64.exe b/ext/bin/devcon/devcon_x64.exe deleted file mode 100644 index 5181aeb67..000000000 Binary files a/ext/bin/devcon/devcon_x64.exe and /dev/null differ diff --git a/ext/bin/devcon/devcon_x86.exe b/ext/bin/devcon/devcon_x86.exe deleted file mode 100644 index ce752b6e9..000000000 Binary files a/ext/bin/devcon/devcon_x86.exe and /dev/null differ diff --git a/ext/installfiles/windows/ZeroTier One.aip b/ext/installfiles/windows/ZeroTier One.aip index 8b26171cf..5bdebe40c 100644 --- a/ext/installfiles/windows/ZeroTier One.aip +++ b/ext/installfiles/windows/ZeroTier One.aip @@ -62,8 +62,6 @@ - - @@ -73,15 +71,13 @@ - + - - @@ -234,10 +230,8 @@ - - - - + + @@ -259,10 +253,8 @@ - - - - + + @@ -306,6 +298,11 @@ + + + + + diff --git a/one.cpp b/one.cpp index d63459193..d384270d3 100644 --- a/one.cpp +++ b/one.cpp @@ -42,6 +42,7 @@ #include #include #include +#include "osdep/WindowsEthernetTap.hpp" #include "windows/ZeroTierOne/ServiceInstaller.h" #include "windows/ZeroTierOne/ServiceBase.h" #include "windows/ZeroTierOne/ZeroTierOneService.h" @@ -914,17 +915,20 @@ static void printHelp(const char *cn,FILE *out) fprintf(out," -U - Run as unprivileged user (skip privilege check)"ZT_EOL_S); fprintf(out," -p - Port for UDP and TCP/HTTP (default: 9993)"ZT_EOL_S); //fprintf(out," -T - Override root topology, do not authenticate or update"ZT_EOL_S); + #ifdef __UNIX_LIKE__ fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)"ZT_EOL_S); #endif // __UNIX_LIKE__ - fprintf(out," -i - Generate and manage identities (zerotier-idtool)"ZT_EOL_S); - fprintf(out," -q - Query API (zerotier-cli)"ZT_EOL_S); + #ifdef __WINDOWS__ fprintf(out," -C - Run from command line instead of as service (Windows)"ZT_EOL_S); fprintf(out," -I - Install Windows service (Windows)"ZT_EOL_S); fprintf(out," -R - Uninstall Windows service (Windows)"ZT_EOL_S); - fprintf(out," -D - Load tap driver into system driver store (Windows)"ZT_EOL_S); + fprintf(out," -D - Remove all instances of Windows tap device (Windows)"ZT_EOL_S); #endif // __WINDOWS__ + + fprintf(out," -i - Generate and manage identities (zerotier-idtool)"ZT_EOL_S); + fprintf(out," -q - Query API (zerotier-cli)"ZT_EOL_S); } #ifdef __WINDOWS__ @@ -1059,26 +1063,15 @@ int main(int argc,char **argv) return 0; } break; -#if 0 - case 'D': { // Install Windows driver (since PNPUTIL.EXE seems to be weirdly unreliable) - std::string pathToInf; -#ifdef _WIN64 - pathToInf = ZT_DEFAULTS.defaultHomePath + "\\tap-windows\\x64\\zttap200.inf"; -#else - pathToInf = ZT_DEFAULTS.defaultHomePath + "\\tap-windows\\x86\\zttap200.inf"; -#endif - printf("Installing ZeroTier One virtual Ethernet port driver."ZT_EOL_S""ZT_EOL_S"NOTE: If you don't see a confirmation window to allow driver installation,"ZT_EOL_S"check to make sure it didn't appear under the installer."ZT_EOL_S); - BOOL needReboot = FALSE; - if (DiInstallDriverA(NULL,pathToInf.c_str(),DIIRFLAG_FORCE_INF,&needReboot)) { - printf("%s: driver successfully installed from %s"ZT_EOL_S,argv[0],pathToInf.c_str()); - return 0; - } else { - printf("%s: failed installing %s: %d"ZT_EOL_S,argv[0],pathToInf.c_str(),(int)GetLastError()); + case 'D': { + std::string err = WindowsEthernetTap::destroyAllPersistentTapDevices(); + if (err.length() > 0) { + fprintf(stderr,"%s: unable to uninstall one or more persistent tap devices: %s"ZT_EOL_S,argv[0],err.c_str()); return 3; } + return 0; } break; #endif // __WINDOWS__ -#endif case 'h': case '?': @@ -1134,6 +1127,10 @@ int main(int argc,char **argv) #endif // __UNIX_LIKE__ #ifdef __WINDOWS__ + // Uninstall legacy tap devices. New devices will automatically be installed and configured + // when tap instances are created. + WindowsEthernetTap::destroyAllLegacyPersistentTapDevices(); + if (winRunFromCommandLine) { // Running in "interactive" mode (mostly for debugging) if (IsCurrentUserLocalAdministrator() != TRUE) { diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp index b2d3ed8b8..d8ba1f982 100644 --- a/osdep/WindowsEthernetTap.cpp +++ b/osdep/WindowsEthernetTap.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,9 @@ #include #include #include +#include +#include +#include #include #include @@ -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 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::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 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::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(¶ms,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,¶ms.ClassInstallHeader,sizeof(params)); + WINENV.SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,devInfo,&devInfoData); + + memset(¶ms,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,¶ms.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 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 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 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::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; diff --git a/osdep/WindowsEthernetTap.hpp b/osdep/WindowsEthernetTap.hpp index 944b53f33..97113d973 100644 --- a/osdep/WindowsEthernetTap.hpp +++ b/osdep/WindowsEthernetTap.hpp @@ -48,6 +48,45 @@ namespace ZeroTier { class WindowsEthernetTap { public: + /** + * Installs a new instance of the ZT tap driver + * + * @param pathToInf Path to zttap driver .inf file + * @return Empty string on success, otherwise an error message + */ + static std::string addNewPersistentTapDevice(const char *pathToInf); + + /** + * Uninstalls all persistent tap devices that have legacy drivers + * + * @return Empty string on success, otherwise an error message + */ + static std::string destroyAllLegacyPersistentTapDevices(); + + /** + * Uninstalls all persistent tap devices on the system + * + * @return Empty string on success, otherwise an error message + */ + static std::string destroyAllPersistentTapDevices(); + + /** + * Uninstall a specific persistent tap device by instance ID + * + * @param instanceId Device instance ID + * @return Empty string on success, otherwise an error message + */ + static std::string deletePersistentTapDevice(const char *instanceId); + + /** + * Disable a persistent tap device by instance ID + * + * @param instanceId Device instance ID + * @param enabled Enable device? + * @return True if device was found and disabled + */ + static bool setPersistentTapDeviceState(const char *instanceId,bool enabled); + WindowsEthernetTap( const char *hp, const MAC &mac, @@ -77,9 +116,6 @@ public: void threadMain() throw(); - static void destroyAllPersistentTapDevices(const char *pathToHelpers); - static void deletePersistentTapDevice(const char *pathToHelpers,const char *instanceId); - private: bool _disableTapDevice(); bool _enableTapDevice(); diff --git a/service/OneService.cpp b/service/OneService.cpp index 06e37a45f..4ee473f87 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -982,7 +982,7 @@ public: _tapAssignedIps.erase(nwid); #ifdef __WINDOWS__ if ((op == ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY)&&(winInstanceId.length() > 0)) - WindowsEthernetTap::deletePersistentTapDevice(_homePath.c_str(),winInstanceId.c_str()); + WindowsEthernetTap::deletePersistentTapDevice(winInstanceId.c_str()); #endif } break; diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index 14bf7c3eb..0a43a6f60 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -217,7 +217,7 @@ true - $(SolutionDir)..\ext\bin\miniupnpc\windows-x86\miniupnpc.lib;wsock32.lib;ws2_32.lib;newdev.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) + $(SolutionDir)..\ext\bin\miniupnpc\windows-x86\miniupnpc.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) false @@ -232,7 +232,7 @@ true - $(SolutionDir)..\ext\bin\miniupnpc\windows-x64\miniupnpc.lib;wsock32.lib;ws2_32.lib;newdev.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) + $(SolutionDir)..\ext\bin\miniupnpc\windows-x64\miniupnpc.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) false @@ -257,7 +257,7 @@ true true true - $(SolutionDir)..\ext\bin\miniupnpc\windows-x86\miniupnpc.lib;wsock32.lib;ws2_32.lib;newdev.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) + $(SolutionDir)..\ext\bin\miniupnpc\windows-x86\miniupnpc.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) false @@ -282,7 +282,7 @@ true true true - $(SolutionDir)..\ext\bin\miniupnpc\windows-x64\miniupnpc.lib;wsock32.lib;ws2_32.lib;newdev.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) + $(SolutionDir)..\ext\bin\miniupnpc\windows-x64\miniupnpc.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies) false