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

@ -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.

Binary file not shown.

Binary file not shown.

View File

@ -62,8 +62,6 @@
<ROW Component="WdfCoinstaller01011.dll" ComponentId="{2E3DD7BE-00C0-44A1-ABA9-6F0468FC9EAA}" Directory_="x64_Dir" Attributes="256" Condition="VersionNT64" KeyPath="WdfCoinstaller01011.dll"/> <ROW Component="WdfCoinstaller01011.dll" ComponentId="{2E3DD7BE-00C0-44A1-ABA9-6F0468FC9EAA}" Directory_="x64_Dir" Attributes="256" Condition="VersionNT64" KeyPath="WdfCoinstaller01011.dll"/>
<ROW Component="WdfCoinstaller01011.dll_1" ComponentId="{137E5A80-62C3-4B30-9900-131919675AC9}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="WdfCoinstaller01011.dll_1"/> <ROW Component="WdfCoinstaller01011.dll_1" ComponentId="{137E5A80-62C3-4B30-9900-131919675AC9}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="WdfCoinstaller01011.dll_1"/>
<ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/> <ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/>
<ROW Component="devcon_x64.exe" ComponentId="{0711ACF9-EEF5-48B0-95D7-8421B74AE314}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="devcon_x64.exe"/>
<ROW Component="devcon_x86.exe" ComponentId="{335F6945-AC5D-40DD-B671-C9BA9C304623}" Directory_="One_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="devcon_x86.exe"/>
<ROW Component="index.html" ComponentId="{24AB46DC-56EA-4F3C-A8B7-95957509CDD1}" Directory_="ui_Dir" Attributes="0" KeyPath="index.html" Type="0"/> <ROW Component="index.html" ComponentId="{24AB46DC-56EA-4F3C-A8B7-95957509CDD1}" Directory_="ui_Dir" Attributes="0" KeyPath="index.html" Type="0"/>
<ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/> <ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
<ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/> <ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/>
@ -73,15 +71,13 @@
<ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/> <ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
<ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify ProductInformation WdfCoinstaller01011.dll WdfCoinstaller01011.dll_1 ZeroTierOne.exe devcon_x64.exe devcon_x86.exe index.html networks.d regid.201001.com.zerotier zerotierone_x64.exe zerotierone_x86.exe zttap300.cat zttap300.cat_1"/> <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify ProductInformation WdfCoinstaller01011.dll WdfCoinstaller01011.dll_1 ZeroTierOne.exe index.html networks.d regid.201001.com.zerotier zerotierone_x64.exe zerotierone_x86.exe zttap300.cat zttap300.cat_1"/>
<ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/> <ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
<ROW File="WdfCoinstaller01011.dll" Component_="WdfCoinstaller01011.dll" FileName="WDFCOI~1.DLL|WdfCoinstaller01011.dll" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\WdfCoinstaller01011.dll" SelfReg="false" NextFile="WdfCoinstaller01011.dll_1"/> <ROW File="WdfCoinstaller01011.dll" Component_="WdfCoinstaller01011.dll" FileName="WDFCOI~1.DLL|WdfCoinstaller01011.dll" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\WdfCoinstaller01011.dll" SelfReg="false" NextFile="WdfCoinstaller01011.dll_1"/>
<ROW File="WdfCoinstaller01011.dll_1" Component_="WdfCoinstaller01011.dll_1" FileName="WDFCOI~1.DLL|WdfCoinstaller01011.dll" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\WdfCoinstaller01011.dll" SelfReg="false" NextFile="zttap300.cat_2"/> <ROW File="WdfCoinstaller01011.dll_1" Component_="WdfCoinstaller01011.dll_1" FileName="WDFCOI~1.DLL|WdfCoinstaller01011.dll" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\WdfCoinstaller01011.dll" SelfReg="false" NextFile="zttap300.cat_2"/>
<ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\bin\win-ui-wrapper\ZeroTier One.exe" SelfReg="false" NextFile="WdfCoinstaller01011.dll"/> <ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\bin\win-ui-wrapper\ZeroTier One.exe" SelfReg="false" NextFile="WdfCoinstaller01011.dll"/>
<ROW File="devcon_x64.exe" Component_="devcon_x64.exe" FileName="DEVCON~1.EXE|devcon_x64.exe" Attributes="0" SourcePath="..\..\bin\devcon\devcon_x64.exe" SelfReg="false" NextFile="devcon_x86.exe"/>
<ROW File="devcon_x86.exe" Component_="devcon_x86.exe" FileName="DEVCON~2.EXE|devcon_x86.exe" Attributes="0" SourcePath="..\..\bin\devcon\devcon_x86.exe" SelfReg="false" NextFile="zerotierone_x86.exe"/>
<ROW File="index.html" Component_="index.html" FileName="INDEX~1.HTM|index.html" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\index.html" SelfReg="false" NextFile="main.js"/> <ROW File="index.html" Component_="index.html" FileName="INDEX~1.HTM|index.html" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\index.html" SelfReg="false" NextFile="main.js"/>
<ROW File="main.js" Component_="index.html" FileName="main.js" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\main.js" SelfReg="false" NextFile="react.min.js"/> <ROW File="main.js" Component_="index.html" FileName="main.js" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\main.js" SelfReg="false" NextFile="react.min.js"/>
<ROW File="react.min.js" Component_="index.html" FileName="REACTM~1.JS|react.min.js" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\react.min.js" SelfReg="false" NextFile="simpleajax.min.js"/> <ROW File="react.min.js" Component_="index.html" FileName="REACTM~1.JS|react.min.js" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\ui\react.min.js" SelfReg="false" NextFile="simpleajax.min.js"/>
@ -234,10 +230,8 @@
<ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFilesFolder]ZeroTier\One"/> <ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFilesFolder]ZeroTier\One"/>
<ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]" MultiBuildTarget="DefaultBuild:[ProgramMenuFolder]"/> <ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]" MultiBuildTarget="DefaultBuild:[ProgramMenuFolder]"/>
<ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/> <ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
<ROW Action="TapDeviceRemove32_NDIS5" Type="3154" Source="devcon_x86.exe" Target="remove zttap200"/> <ROW Action="TapDeviceRemove32" Type="3154" Source="zerotierone_x86.exe" Target="-D"/>
<ROW Action="TapDeviceRemove32_NDIS6" Type="3154" Source="devcon_x86.exe" Target="remove zttap300"/> <ROW Action="TapDeviceRemove64" Type="3154" Source="zerotierone_x64.exe" Target="-D"/>
<ROW Action="TapDeviceRemove64_NDIS5" Type="3154" Source="devcon_x64.exe" Target="remove zttap200"/>
<ROW Action="TapDeviceRemove64_NDIS6" Type="3154" Source="devcon_x64.exe" Target="remove zttap300"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent">
<ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/> <ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/>
@ -259,10 +253,8 @@
<ROW Action="InstallFinalize" Sequence="6596" SeqType="0" MsiKey="InstallFinalize"/> <ROW Action="InstallFinalize" Sequence="6596" SeqType="0" MsiKey="InstallFinalize"/>
<ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE=&quot;ALL&quot;) AND ((VersionNT &gt; 500) OR((VersionNT = 500) AND (ServicePackLevel &gt;= 4)))" Sequence="1501"/> <ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE=&quot;ALL&quot;) AND ((VersionNT &gt; 500) OR((VersionNT = 500) AND (ServicePackLevel &gt;= 4)))" Sequence="1501"/>
<ROW Action="AI_GetArpIconPath" Sequence="1401"/> <ROW Action="AI_GetArpIconPath" Sequence="1401"/>
<ROW Action="TapDeviceRemove32_NDIS6" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1602"/> <ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1601"/>
<ROW Action="TapDeviceRemove64_NDIS6" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1604"/> <ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1602"/>
<ROW Action="TapDeviceRemove32_NDIS5" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1601"/>
<ROW Action="TapDeviceRemove64_NDIS5" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1603"/>
<ROW Action="AI_PrepareChainers" Condition="VersionMsi &gt;= &quot;4.05&quot;" Sequence="5851"/> <ROW Action="AI_PrepareChainers" Condition="VersionMsi &gt;= &quot;4.05&quot;" Sequence="5851"/>
<ROW Action="AI_FwInstall" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5802"/> <ROW Action="AI_FwInstall" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5802"/>
<ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5801"/> <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5801"/>
@ -306,6 +298,11 @@
<ROW Registry="VersionMajor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMajor" Value="#0" Component_="AI_CustomARPName"/> <ROW Registry="VersionMajor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMajor" Value="#0" Component_="AI_CustomARPName"/>
<ROW Registry="VersionMinor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMinor" Value="#7" Component_="AI_CustomARPName"/> <ROW Registry="VersionMinor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMinor" Value="#7" Component_="AI_CustomARPName"/>
</COMPONENT> </COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiRemoveFileComponent">
<ROW FileKey="devcon.log" Component_="ProductInformation" FileName="devcon.log" DirProperty="One_Dir" InstallMode="3"/>
<ROW FileKey="devcon_x64.exe" Component_="ProductInformation" FileName="devcon_x64.exe" DirProperty="One_Dir" InstallMode="3"/>
<ROW FileKey="devcon_x86.exe" Component_="ProductInformation" FileName="devcon_x86.exe" DirProperty="One_Dir" InstallMode="3"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiServCtrlComponent"> <COMPONENT cid="caphyon.advinst.msicomp.MsiServCtrlComponent">
<ROW ServiceControl="zerotierone_x64.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x64.exe"/> <ROW ServiceControl="zerotierone_x64.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x64.exe"/>
<ROW ServiceControl="zerotierone_x86.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x86.exe"/> <ROW ServiceControl="zerotierone_x86.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x86.exe"/>

35
one.cpp
View File

@ -42,6 +42,7 @@
#include <lmcons.h> #include <lmcons.h>
#include <newdev.h> #include <newdev.h>
#include <atlbase.h> #include <atlbase.h>
#include "osdep/WindowsEthernetTap.hpp"
#include "windows/ZeroTierOne/ServiceInstaller.h" #include "windows/ZeroTierOne/ServiceInstaller.h"
#include "windows/ZeroTierOne/ServiceBase.h" #include "windows/ZeroTierOne/ServiceBase.h"
#include "windows/ZeroTierOne/ZeroTierOneService.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," -U - Run as unprivileged user (skip privilege check)"ZT_EOL_S);
fprintf(out," -p<port> - Port for UDP and TCP/HTTP (default: 9993)"ZT_EOL_S); fprintf(out," -p<port> - Port for UDP and TCP/HTTP (default: 9993)"ZT_EOL_S);
//fprintf(out," -T<path> - Override root topology, do not authenticate or update"ZT_EOL_S); //fprintf(out," -T<path> - Override root topology, do not authenticate or update"ZT_EOL_S);
#ifdef __UNIX_LIKE__ #ifdef __UNIX_LIKE__
fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)"ZT_EOL_S); fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)"ZT_EOL_S);
#endif // __UNIX_LIKE__ #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__ #ifdef __WINDOWS__
fprintf(out," -C - Run from command line instead of as service (Windows)"ZT_EOL_S); 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," -I - Install Windows service (Windows)"ZT_EOL_S);
fprintf(out," -R - Uninstall 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__ #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__ #ifdef __WINDOWS__
@ -1059,26 +1063,15 @@ int main(int argc,char **argv)
return 0; return 0;
} break; } break;
#if 0 case 'D': {
case 'D': { // Install Windows driver (since PNPUTIL.EXE seems to be weirdly unreliable) std::string err = WindowsEthernetTap::destroyAllPersistentTapDevices();
std::string pathToInf; if (err.length() > 0) {
#ifdef _WIN64 fprintf(stderr,"%s: unable to uninstall one or more persistent tap devices: %s"ZT_EOL_S,argv[0],err.c_str());
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());
return 3; return 3;
} }
return 0;
} break; } break;
#endif // __WINDOWS__ #endif // __WINDOWS__
#endif
case 'h': case 'h':
case '?': case '?':
@ -1134,6 +1127,10 @@ int main(int argc,char **argv)
#endif // __UNIX_LIKE__ #endif // __UNIX_LIKE__
#ifdef __WINDOWS__ #ifdef __WINDOWS__
// Uninstall legacy tap devices. New devices will automatically be installed and configured
// when tap instances are created.
WindowsEthernetTap::destroyAllLegacyPersistentTapDevices();
if (winRunFromCommandLine) { if (winRunFromCommandLine) {
// Running in "interactive" mode (mostly for debugging) // Running in "interactive" mode (mostly for debugging)
if (IsCurrentUserLocalAdministrator() != TRUE) { if (IsCurrentUserLocalAdministrator() != TRUE) {

View File

@ -32,6 +32,7 @@
#include <WinSock2.h> #include <WinSock2.h>
#include <Windows.h> #include <Windows.h>
#include <tchar.h> #include <tchar.h>
#include <malloc.h>
#include <winreg.h> #include <winreg.h>
#include <wchar.h> #include <wchar.h>
#include <ws2ipdef.h> #include <ws2ipdef.h>
@ -42,6 +43,9 @@
#include <atlbase.h> #include <atlbase.h>
#include <netlistmgr.h> #include <netlistmgr.h>
#include <nldef.h> #include <nldef.h>
#include <SetupAPI.h>
#include <newdev.h>
#include <cfgmgr32.h>
#include <iostream> #include <iostream>
#include <set> #include <set>
@ -55,15 +59,28 @@
#include "..\windows\TapDriver6\tap-windows.h" #include "..\windows\TapDriver6\tap-windows.h"
// ff:ff:ff:ff:ff:ff with no ADI // Create a fake unused default route to force detection of network type on networks without gateways
//static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
#define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE #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 ZeroTier {
namespace { namespace {
// Static/singleton class that when initialized loads a bunch of environment information and a few dynamically loaded DLLs
class WindowsEthernetTapEnv class WindowsEthernetTapEnv
{ {
public: public:
@ -71,29 +88,349 @@ public:
{ {
#ifdef _WIN64 #ifdef _WIN64
is64Bit = TRUE; is64Bit = TRUE;
devcon = "\\devcon_x64.exe"; tapDriverPath = "\\tap-windows\\x64\\zttap300.inf";
tapDriverNdis5 = "\\tap-windows\\x64\\zttap200.inf";
tapDriverNdis6 = "\\tap-windows\\x64\\zttap300.inf";
#else #else
is64Bit = FALSE; is64Bit = FALSE;
IsWow64Process(GetCurrentProcess(),&is64Bit); IsWow64Process(GetCurrentProcess(),&is64Bit);
devcon = ((is64Bit == TRUE) ? "\\devcon_x64.exe" : "\\devcon_x86.exe"); if (is64Bit) {
tapDriverNdis5 = ((is64Bit == TRUE) ? "\\tap-windows\\x64\\zttap200.inf" : "\\tap-windows\\x86\\zttap200.inf"); fprintf(stderr,"FATAL: you must use the 64-bit ZeroTier One service on 64-bit Windows systems\r\n");
tapDriverNdis6 = ((is64Bit == TRUE) ? "\\tap-windows\\x64\\zttap300.inf" : "\\tap-windows\\x86\\zttap300.inf"); _exit(1);
#endif
} }
BOOL is64Bit; tapDriverPath = "\\tap-windows\\x86\\zttap300.inf";
const char *devcon; #endif
const char *tapDriverNdis5; tapDriverName = "zttap300";
const char *tapDriverNdis6;
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; // 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; static const WindowsEthernetTapEnv WINENV;
// Only create or delete devices one at a time // Only create or delete devices one at a time
static Mutex _systemTapInitLock; static Mutex _systemTapInitLock;
// Only perform installation or uninstallation options one at a time
static Mutex _systemDeviceManagementLock;
} // anonymous namespace } // 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( WindowsEthernetTap::WindowsEthernetTap(
const char *hp, const char *hp,
const MAC &mac, const MAC &mac,
@ -118,35 +455,21 @@ WindowsEthernetTap::WindowsEthernetTap(
char subkeyClass[4096]; char subkeyClass[4096];
char data[4096]; char data[4096];
char tag[24]; char tag[24];
std::set<std::string> existingDeviceInstances;
std::string mySubkeyName;
if (mtu > 2800) if (mtu > 2800)
throw std::runtime_error("MTU too large for Windows tap"); 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 Mutex::Lock _l(_systemTapInitLock);
// 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");
}
}
HKEY nwAdapters; HKEY nwAdapters;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",0,KEY_READ|KEY_WRITE,&nwAdapters) != ERROR_SUCCESS) 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"); 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 // Look for the tap instance that corresponds with this network
for(DWORD subkeyIndex=0;;++subkeyIndex) { for(DWORD subkeyIndex=0;;++subkeyIndex) {
DWORD type; DWORD type;
@ -158,8 +481,9 @@ WindowsEthernetTap::WindowsEthernetTap(
type = 0; type = 0;
dataLen = sizeof(data); dataLen = sizeof(data);
if (RegGetValueA(nwAdapters,subkeyName,"ComponentId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { if (RegGetValueA(nwAdapters,subkeyName,"ComponentId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
data[dataLen] = '\0'; data[dataLen] = (char)0;
if (!strnicmp(data,"zttap",5)) {
if (WINENV.tapDriverName == data) {
std::string instanceId; std::string instanceId;
type = 0; type = 0;
dataLen = sizeof(data); dataLen = sizeof(data);
@ -196,34 +520,9 @@ WindowsEthernetTap::WindowsEthernetTap(
// If there is no device, try to create one // If there is no device, try to create one
bool creatingNewDevice = (_netCfgInstanceId.length() == 0); bool creatingNewDevice = (_netCfgInstanceId.length() == 0);
if (creatingNewDevice) { if (creatingNewDevice) {
// Log devcon output to a file std::string errm = addNewPersistentTapDevice((std::string(_pathToHelpers) + WINENV.tapDriverPath).c_str());
HANDLE devconLog = CreateFileA((_pathToHelpers + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if (errm.length() != 0)
if (devconLog != INVALID_HANDLE_VALUE) throw std::runtime_error(errm);
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);
// Scan for the new instance by simply looking for taps that weren't originally there... // Scan for the new instance by simply looking for taps that weren't originally there...
for(DWORD subkeyIndex=0;;++subkeyIndex) { for(DWORD subkeyIndex=0;;++subkeyIndex) {
@ -237,7 +536,8 @@ WindowsEthernetTap::WindowsEthernetTap(
dataLen = sizeof(data); dataLen = sizeof(data);
if (RegGetValueA(nwAdapters,subkeyName,"ComponentId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { if (RegGetValueA(nwAdapters,subkeyName,"ComponentId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
data[dataLen] = '\0'; data[dataLen] = '\0';
if (!strnicmp(data,"zttap",5)) {
if (WINENV.tapDriverName == data) {
type = 0; type = 0;
dataLen = sizeof(data); dataLen = sizeof(data);
if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) { 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)); RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"*IfType",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp));
if (creatingNewDevice) { if (creatingNewDevice) {
// Set EnableDHCP to 0 by default on new devices
tmp = 0; tmp = 0;
RegSetKeyValueA(nwAdapters,mySubkeyName.c_str(),"EnableDHCP",REG_DWORD,(LPCVOID)&tmp,sizeof(tmp)); 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(); const char *nbtmp1 = _netCfgInstanceId.c_str();
char *nbtmp2 = nobraces; char *nbtmp2 = nobraces;
while (*nbtmp1) { 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?)"); 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) if (ConvertInterfaceGuidToLuid(&_deviceGuid,&_deviceLuid) != NO_ERROR)
throw std::runtime_error("unable to convert device interface GUID to LUID"); throw std::runtime_error("unable to convert device interface GUID to LUID");
// Certain functions can now work (e.g. ips())
_initialized = true; _initialized = true;
if (friendlyName) if (friendlyName)
setFriendlyName(friendlyName); setFriendlyName(friendlyName);
// Start background thread that actually performs I/O
_injectSemaphore = CreateSemaphore(NULL,0,1,NULL); _injectSemaphore = CreateSemaphore(NULL,0,1,NULL);
_thread = Thread::start(this); _thread = Thread::start(this);
} }
@ -325,7 +624,7 @@ WindowsEthernetTap::~WindowsEthernetTap()
ReleaseSemaphore(_injectSemaphore,1,NULL); ReleaseSemaphore(_injectSemaphore,1,NULL);
Thread::join(_thread); Thread::join(_thread);
CloseHandle(_injectSemaphore); CloseHandle(_injectSemaphore);
_disableTapDevice(); setPersistentTapDeviceState(_deviceInstanceId.c_str(),false);
} }
void WindowsEthernetTap::setEnabled(bool en) void WindowsEthernetTap::setEnabled(bool en)
@ -572,14 +871,15 @@ void WindowsEthernetTap::threadMain()
try { try {
while (_run) { while (_run) {
_enableTapDevice(); setPersistentTapDeviceState(_deviceInstanceId.c_str(),true);
Sleep(500); Sleep(500);
_tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL); _tap = CreateFileA(tapPath,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,NULL);
if (_tap == INVALID_HANDLE_VALUE) { if (_tap == INVALID_HANDLE_VALUE) {
_disableTapDevice(); setPersistentTapDeviceState(_deviceInstanceId.c_str(),false);
_enableTapDevice(); Sleep(500);
Sleep(1000); setPersistentTapDeviceState(_deviceInstanceId.c_str(),true);
Sleep(500);
continue; 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 } 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() NET_IFINDEX WindowsEthernetTap::_getDeviceIndex()
{ {
MIB_IF_TABLE2 *ift = (MIB_IF_TABLE2 *)0; MIB_IF_TABLE2 *ift = (MIB_IF_TABLE2 *)0;

View File

@ -48,6 +48,45 @@ namespace ZeroTier {
class WindowsEthernetTap class WindowsEthernetTap
{ {
public: 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( WindowsEthernetTap(
const char *hp, const char *hp,
const MAC &mac, const MAC &mac,
@ -77,9 +116,6 @@ public:
void threadMain() void threadMain()
throw(); throw();
static void destroyAllPersistentTapDevices(const char *pathToHelpers);
static void deletePersistentTapDevice(const char *pathToHelpers,const char *instanceId);
private: private:
bool _disableTapDevice(); bool _disableTapDevice();
bool _enableTapDevice(); bool _enableTapDevice();

View File

@ -982,7 +982,7 @@ public:
_tapAssignedIps.erase(nwid); _tapAssignedIps.erase(nwid);
#ifdef __WINDOWS__ #ifdef __WINDOWS__
if ((op == ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY)&&(winInstanceId.length() > 0)) 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 #endif
} }
break; break;

View File

@ -217,7 +217,7 @@
</ClCompile> </ClCompile>
<Link> <Link>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>$(SolutionDir)..\ext\bin\miniupnpc\windows-x86\miniupnpc.lib;wsock32.lib;ws2_32.lib;newdev.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>$(SolutionDir)..\ext\bin\miniupnpc\windows-x86\miniupnpc.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
@ -232,7 +232,7 @@
</ClCompile> </ClCompile>
<Link> <Link>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>$(SolutionDir)..\ext\bin\miniupnpc\windows-x64\miniupnpc.lib;wsock32.lib;ws2_32.lib;newdev.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>$(SolutionDir)..\ext\bin\miniupnpc\windows-x64\miniupnpc.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
@ -257,7 +257,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>$(SolutionDir)..\ext\bin\miniupnpc\windows-x86\miniupnpc.lib;wsock32.lib;ws2_32.lib;newdev.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>$(SolutionDir)..\ext\bin\miniupnpc\windows-x86\miniupnpc.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
@ -282,7 +282,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>$(SolutionDir)..\ext\bin\miniupnpc\windows-x64\miniupnpc.lib;wsock32.lib;ws2_32.lib;newdev.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>$(SolutionDir)..\ext\bin\miniupnpc\windows-x64\miniupnpc.lib;wsock32.lib;ws2_32.lib;Iphlpapi.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>