Browse Source

Merge branch 'master' into android-jni

Grant Limberg 10 years ago
parent
commit
0fd6808e21

+ 0 - 7
ext/bin/devcon/README.txt

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

BIN
ext/bin/devcon/devcon_x64.exe


BIN
ext/bin/devcon/devcon_x86.exe


+ 22 - 25
ext/installfiles/windows/ZeroTier One.aip

@@ -23,7 +23,7 @@
     <ROW Property="CTRLS" Value="2"/>
     <ROW Property="CTRLS" Value="2"/>
     <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
     <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
     <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
     <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
-    <ROW Property="ProductCode" Value="1033:{04B3161E-427B-4C7D-B8FF-B38DE1DDC7F0} " Type="16"/>
+    <ROW Property="ProductCode" Value="1033:{6E2EC2C9-F3B2-474F-9176-54E029E0442F} " Type="16"/>
     <ROW Property="ProductLanguage" Value="1033"/>
     <ROW Property="ProductLanguage" Value="1033"/>
     <ROW Property="ProductName" Value="ZeroTier One"/>
     <ROW Property="ProductName" Value="ZeroTier One"/>
     <ROW Property="ProductVersion" Value="1.0.4" Type="32"/>
     <ROW Property="ProductVersion" Value="1.0.4" Type="32"/>
@@ -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"/>
@@ -105,8 +101,8 @@
     <ATTRIBUTE name="Enable" value="false"/>
     <ATTRIBUTE name="Enable" value="false"/>
   </COMPONENT>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageComponent">
   <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageComponent">
-    <ROW ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Order="1" Options="108" InstallCondition="((NOT Installed) AND (VersionNT64))" RemoveCondition="((REMOVE=&quot;ALL&quot;) AND (NOT UPGRADINGPRODUCTCODE) AND (VersionNT64))"/>
-    <ROW ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Order="2" Options="108" InstallCondition="((NOT Installed) AND (NOT VersionNT64))" RemoveCondition="((REMOVE=&quot;ALL&quot;) AND (NOT UPGRADINGPRODUCTCODE) AND (NOT VersionNT64))"/>
+    <ROW ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Order="1" Options="108" InstallCondition="(VersionNT64)" RemoveCondition="((REMOVE=&quot;ALL&quot;) AND (NOT UPGRADINGPRODUCTCODE) AND (VersionNT64))"/>
+    <ROW ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Order="2" Options="108" InstallCondition="(NOT VersionNT64)" RemoveCondition="((REMOVE=&quot;ALL&quot;) AND (NOT UPGRADINGPRODUCTCODE) AND (NOT VersionNT64))"/>
   </COMPONENT>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageFileComponent">
   <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageFileComponent">
     <ROW FileId="ZeroTierOne_NDIS6_x64.msi" ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x64.msi" Content="..\..\bin\tap-windows-ndis6\x64\ZeroTierOne_NDIS6_x64.msi"/>
     <ROW FileId="ZeroTierOne_NDIS6_x64.msi" ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x64.msi" Content="..\..\bin\tap-windows-ndis6\x64\ZeroTierOne_NDIS6_x64.msi"/>
@@ -120,10 +116,7 @@
     <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" Thumbprint="2ad023dc7aa92bf4265b33852a2ed2406d2bee86 Subject: ZeroTier Networks LLC&#10;Issuer: DigiCert High Assurance Code Signing CA-1&#10;Valid from 04/24/2015 to 04/01/2016"/>
     <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" Thumbprint="2ad023dc7aa92bf4265b33852a2ed2406d2bee86 Subject: ZeroTier Networks LLC&#10;Issuer: DigiCert High Assurance Code Signing CA-1&#10;Valid from 04/24/2015 to 04/01/2016"/>
   </COMPONENT>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
   <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
-    <ROW FirewallException="ZeroTierOne" DisplayName="ZeroTier One (UDP_9993)" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
-    <ROW FirewallException="ZeroTierOneService_x64" DisplayName="ZeroTier One (Service_x64)" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="((?zerotierone_x64.exe=2) AND ($zerotierone_x64.exe=3))" Profiles="7" AppPath="[#zerotierone_x64.exe]" Protocol="ANY"/>
-    <ROW FirewallException="ZeroTierOneService_x86" DisplayName="ZeroTier One (Service_x86)" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="((?zerotierone_x86.exe=2) AND ($zerotierone_x86.exe=3))" Profiles="7" AppPath="[#zerotierone_x86.exe]" Protocol="ANY"/>
-    <ROW FirewallException="ZeroTierOneTCP_9993" DisplayName="ZeroTier One (TCP_9993)" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="TCP"/>
+    <ROW FirewallException="ZeroTierOneUDP9993" DisplayName="ZeroTier One UDP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
   </COMPONENT>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
   <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
     <ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
     <ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
@@ -202,16 +195,16 @@
     <ROW Action="AI_CommitChainers" Type="11841" Source="chainersupport.dll" Target="CommitChainedPackages" WithoutSeq="true"/>
     <ROW Action="AI_CommitChainers" Type="11841" Source="chainersupport.dll" Target="CommitChainedPackages" WithoutSeq="true"/>
     <ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[~]"/>
     <ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[~]"/>
     <ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="[~]"/>
     <ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="[~]"/>
+    <ROW Action="AI_DATA_SETTER_2" Type="51" Source="CustomActionData" Target="[~]"/>
     <ROW Action="AI_DATA_SETTER_3" Type="51" Source="CustomActionData" Target="[~]"/>
     <ROW Action="AI_DATA_SETTER_3" Type="51" Source="CustomActionData" Target="[~]"/>
-    <ROW Action="AI_DATA_SETTER_4" Type="51" Source="CustomActionData" Target="[~]"/>
     <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
     <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
     <ROW Action="AI_DoRemoveExternalUIStub" Type="3585" Source="ExternalUICleaner.dll" Target="DoRemoveExternalUIStub" WithoutSeq="true"/>
     <ROW Action="AI_DoRemoveExternalUIStub" Type="3585" Source="ExternalUICleaner.dll" Target="DoRemoveExternalUIStub" WithoutSeq="true"/>
     <ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
     <ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
     <ROW Action="AI_FwConfig" Type="11265" Source="NetFirewall.dll" Target="OnFwConfig" WithoutSeq="true"/>
     <ROW Action="AI_FwConfig" Type="11265" Source="NetFirewall.dll" Target="OnFwConfig" WithoutSeq="true"/>
-    <ROW Action="AI_FwInstall" Type="1" Source="NetFirewall.dll" Target="OnFwInstall" AdditionalSeq="AI_DATA_SETTER_3"/>
+    <ROW Action="AI_FwInstall" Type="1" Source="NetFirewall.dll" Target="OnFwInstall" AdditionalSeq="AI_DATA_SETTER_2"/>
     <ROW Action="AI_FwRemove" Type="11265" Source="NetFirewall.dll" Target="OnFwRemove" WithoutSeq="true"/>
     <ROW Action="AI_FwRemove" Type="11265" Source="NetFirewall.dll" Target="OnFwRemove" WithoutSeq="true"/>
     <ROW Action="AI_FwRollback" Type="11521" Source="NetFirewall.dll" Target="OnFwRollback" WithoutSeq="true"/>
     <ROW Action="AI_FwRollback" Type="11521" Source="NetFirewall.dll" Target="OnFwRollback" WithoutSeq="true"/>
-    <ROW Action="AI_FwUninstall" Type="1" Source="NetFirewall.dll" Target="OnFwUninstall" AdditionalSeq="AI_DATA_SETTER_4"/>
+    <ROW Action="AI_FwUninstall" Type="1" Source="NetFirewall.dll" Target="OnFwUninstall" AdditionalSeq="AI_DATA_SETTER_3"/>
     <ROW Action="AI_GetArpIconPath" Type="1" Source="aicustact.dll" Target="GetArpIconPath"/>
     <ROW Action="AI_GetArpIconPath" Type="1" Source="aicustact.dll" Target="GetArpIconPath"/>
     <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
     <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
     <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/>
     <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/>
@@ -234,10 +227,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_NDIS6" Type="3154" Source="devcon_x86.exe" Target="remove zttap300"/>
-    <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"/>
+    <ROW Action="TapDeviceRemove32" Type="3154" Source="zerotierone_x86.exe" Target="-D"/>
+    <ROW Action="TapDeviceRemove64" Type="3154" Source="zerotierone_x64.exe" Target="-D"/>
   </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,15 +250,13 @@
     <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="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="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="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" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1602"/>
     <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_2" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5801"/>
     <ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/>
     <ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/>
-    <ROW Action="AI_DATA_SETTER_4" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/>
+    <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/>
   </COMPONENT>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
   <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
     <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
     <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
@@ -306,6 +295,14 @@
     <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"/>
+    <ROW FileKey="node.log" Component_="ProductInformation" FileName="node.log" DirProperty="One_Dir" InstallMode="3"/>
+    <ROW FileKey="node.log.old" Component_="ProductInformation" FileName="node.log.old" DirProperty="One_Dir" InstallMode="3"/>
+    <ROW FileKey="roottopology" Component_="ProductInformation" FileName="root-topology" 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"/>

+ 1 - 1
node/Network.cpp

@@ -533,7 +533,7 @@ public:
 
 
 	inline void operator()(Topology &t,const SharedPtr<Peer> &p)
 	inline void operator()(Topology &t,const SharedPtr<Peer> &p)
 	{
 	{
-		if ( ( (p->hasActiveDirectPath(_now)) && (_network->_isAllowed(p->address())) ) || (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) ) {
+		if ( ( (p->hasActiveDirectPath(_now)) && ( (_network->_isAllowed(p->address())) || (p->address() == _network->controller()) ) ) || (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) ) {
 			Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
 			Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
 
 
 			for(std::vector<MulticastGroup>::iterator mg(_allMulticastGroups.begin());mg!=_allMulticastGroups.end();++mg) {
 			for(std::vector<MulticastGroup>::iterator mg(_allMulticastGroups.begin());mg!=_allMulticastGroups.end();++mg) {

+ 0 - 3
node/Node.hpp

@@ -248,9 +248,6 @@ private:
 	ZT1_VirtualNetworkConfigFunction _virtualNetworkConfigFunction;
 	ZT1_VirtualNetworkConfigFunction _virtualNetworkConfigFunction;
 	ZT1_EventCallback _eventCallback;
 	ZT1_EventCallback _eventCallback;
 
 
-	//Dictionary _localConfig; // persisted as local.conf
-	//Mutex _localConfig_m;
-
 	std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
 	std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
 	Mutex _networks_m;
 	Mutex _networks_m;
 
 

+ 3 - 3
node/Peer.cpp

@@ -133,7 +133,7 @@ void Peer::received(
 			Packet outp(_id.address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
 			Packet outp(_id.address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
 			const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks());
 			const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks());
 			for(std::vector< SharedPtr<Network> >::const_iterator n(networks.begin());n!=networks.end();++n) {
 			for(std::vector< SharedPtr<Network> >::const_iterator n(networks.begin());n!=networks.end();++n) {
-				if ( (isRoot) || ((*n)->isAllowed(_id.address())) ) {
+				if ( (isRoot) || ((*n)->isAllowed(_id.address())) || (_id.address() == (*n)->controller()) ) {
 					const std::vector<MulticastGroup> mgs((*n)->allMulticastGroups());
 					const std::vector<MulticastGroup> mgs((*n)->allMulticastGroups());
 					for(std::vector<MulticastGroup>::const_iterator mg(mgs.begin());mg!=mgs.end();++mg) {
 					for(std::vector<MulticastGroup>::const_iterator mg(mgs.begin());mg!=mgs.end();++mg) {
 						if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) {
 						if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) {
@@ -211,7 +211,7 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &at
 void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now)
 void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now)
 {
 {
 	RemotePath *const bestPath = getBestPath(now);
 	RemotePath *const bestPath = getBestPath(now);
-	if ((bestPath)&&(bestPath->active(now))) {
+	if (bestPath) {
 		if ((now - bestPath->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) {
 		if ((now - bestPath->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) {
 			TRACE("PING %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str());
 			TRACE("PING %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str());
 			attemptToContactAt(RR,bestPath->address(),now);
 			attemptToContactAt(RR,bestPath->address(),now);
@@ -239,7 +239,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_
 					ps.push_back(',');
 					ps.push_back(',');
 				ps.append(p->address().toString());
 				ps.append(p->address().toString());
 			}
 			}
-			TRACE("pushing %u direct paths (local interface addresses) to %s: %s",(unsigned int)dps.size(),_id.address().toString().c_str(),ps.c_str());
+			TRACE("pushing %u direct paths to %s: %s",(unsigned int)dps.size(),_id.address().toString().c_str(),ps.c_str());
 		}
 		}
 #endif
 #endif
 
 

+ 22 - 28
one.cpp

@@ -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"
@@ -765,8 +766,6 @@ static BOOL WINAPI _winConsoleCtrlHandler(DWORD dwCtrlType)
 	return FALSE;
 	return FALSE;
 }
 }
 
 
-// Pokes a hole in the Windows firewall (advfirewall) for the running program
-/* -- now done by Advanced Installer
 static void _winPokeAHole()
 static void _winPokeAHole()
 {
 {
 	char myPath[MAX_PATH];
 	char myPath[MAX_PATH];
@@ -778,7 +777,7 @@ static void _winPokeAHole()
 		startupInfo.cb = sizeof(startupInfo);
 		startupInfo.cb = sizeof(startupInfo);
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
-		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall delete rule name=\"ZeroTier One\" program=\"") + myPath + "\"").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
+		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall delete rule name=\"ZeroTier One\" program=\"") + myPath + "\"").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hThread);
 			CloseHandle(processInfo.hThread);
@@ -787,7 +786,7 @@ static void _winPokeAHole()
 		startupInfo.cb = sizeof(startupInfo);
 		startupInfo.cb = sizeof(startupInfo);
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
-		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall add rule name=\"ZeroTier One\" dir=in action=allow program=\"") + myPath + "\" enable=yes").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
+		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall add rule name=\"ZeroTier One\" dir=in action=allow program=\"") + myPath + "\" enable=yes").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hThread);
 			CloseHandle(processInfo.hThread);
@@ -796,14 +795,13 @@ static void _winPokeAHole()
 		startupInfo.cb = sizeof(startupInfo);
 		startupInfo.cb = sizeof(startupInfo);
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
-		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall add rule name=\"ZeroTier One\" dir=out action=allow program=\"") + myPath + "\" enable=yes").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
+		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall add rule name=\"ZeroTier One\" dir=out action=allow program=\"") + myPath + "\" enable=yes").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hThread);
 			CloseHandle(processInfo.hThread);
 		}
 		}
 	}
 	}
 }
 }
-*/
 
 
 // Returns true if this is running as the local administrator
 // Returns true if this is running as the local administrator
 static BOOL IsCurrentUserLocalAdministrator(void)
 static BOOL IsCurrentUserLocalAdministrator(void)
@@ -906,7 +904,7 @@ static void printHelp(const char *cn,FILE *out)
 	fprintf(out,"Licensed under the GNU General Public License v3"ZT_EOL_S""ZT_EOL_S);
 	fprintf(out,"Licensed under the GNU General Public License v3"ZT_EOL_S""ZT_EOL_S);
 	std::string updateUrl(OneService::autoUpdateUrl());
 	std::string updateUrl(OneService::autoUpdateUrl());
 	if (updateUrl.length())
 	if (updateUrl.length())
-		fprintf(out,"Automatic update enabled:"ZT_EOL_S"  %s"ZT_EOL_S""ZT_EOL_S,updateUrl.c_str());
+		fprintf(out,"Automatic updates enabled:"ZT_EOL_S"  %s"ZT_EOL_S"  (all updates are securely authenticated by 256-bit ECDSA signature)"ZT_EOL_S""ZT_EOL_S,updateUrl.c_str());
 	fprintf(out,"Usage: %s [-switches] [home directory]"ZT_EOL_S""ZT_EOL_S,cn);
 	fprintf(out,"Usage: %s [-switches] [home directory]"ZT_EOL_S""ZT_EOL_S,cn);
 	fprintf(out,"Available switches:"ZT_EOL_S);
 	fprintf(out,"Available switches:"ZT_EOL_S);
 	fprintf(out,"  -h                - Display this help"ZT_EOL_S);
 	fprintf(out,"  -h                - Display this help"ZT_EOL_S);
@@ -914,17 +912,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 +1060,15 @@ int main(int argc,char **argv)
 						return 0;
 						return 0;
 					} break;
 					} 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 3;
 						}
 						}
+						return 0;
 					} break;
 					} break;
 #endif // __WINDOWS__
 #endif // __WINDOWS__
-#endif
 
 
 				case 'h':
 				case 'h':
 				case '?':
 				case '?':
@@ -1134,6 +1124,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) {
@@ -1142,13 +1136,13 @@ int main(int argc,char **argv)
 				return 1;
 				return 1;
 			}
 			}
 		} else {
 		} else {
-			//_winPokeAHole();
+			_winPokeAHole();
 		}
 		}
 		SetConsoleCtrlHandler(&_winConsoleCtrlHandler,TRUE);
 		SetConsoleCtrlHandler(&_winConsoleCtrlHandler,TRUE);
 		// continues on to ordinary command line execution code below...
 		// continues on to ordinary command line execution code below...
 	} else {
 	} else {
 		// Running from service manager
 		// Running from service manager
-		//_winPokeAHole();
+		_winPokeAHole();
 		ZeroTierOneService zt1Service;
 		ZeroTierOneService zt1Service;
 		if (CServiceBase::Run(zt1Service) == TRUE) {
 		if (CServiceBase::Run(zt1Service) == TRUE) {
 			return 0;
 			return 0;

+ 2 - 2
osdep/OSUtils.cpp

@@ -125,7 +125,7 @@ void OSUtils::lockDownFile(const char *path,bool isDir)
 		startupInfo.cb = sizeof(startupInfo);
 		startupInfo.cb = sizeof(startupInfo);
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
-		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /inheritance:d /Q").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
+		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /inheritance:d /Q").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hThread);
 			CloseHandle(processInfo.hThread);
@@ -134,7 +134,7 @@ void OSUtils::lockDownFile(const char *path,bool isDir)
 		startupInfo.cb = sizeof(startupInfo);
 		startupInfo.cb = sizeof(startupInfo);
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&startupInfo,0,sizeof(STARTUPINFOA));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
 		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
-		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /remove *S-1-5-32-545 /Q").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
+		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /remove *S-1-5-32-545 /Q").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			WaitForSingleObject(processInfo.hProcess,INFINITE);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hProcess);
 			CloseHandle(processInfo.hThread);
 			CloseHandle(processInfo.hThread);

+ 503 - 279
osdep/WindowsEthernetTap.cpp

@@ -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
-//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
 #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,364 @@ public:
 	{
 	{
 #ifdef _WIN64
 #ifdef _WIN64
 		is64Bit = TRUE;
 		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
 #else
 		is64Bit = FALSE;
 		is64Bit = FALSE;
 		IsWow64Process(GetCurrentProcess(),&is64Bit);
 		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
 #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;
 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");
+	}
+
+	// HACK: During upgrades, this can fail while the installer is still running. So make 60 attempts
+	// with a 1s delay between each attempt.
+	bool driverInstalled = false;
+	for(int retryCounter=0;retryCounter<60;++retryCounter) {
+		BOOL rebootRequired = FALSE;
+		if (WINENV.UpdateDriverForPlugAndPlayDevicesA((HWND)0,WINENV.tapDriverName.c_str(),pathToInf,INSTALLFLAG_FORCE|INSTALLFLAG_NONINTERACTIVE,&rebootRequired)) {
+			driverInstalled = true;
+			break;
+		} else Sleep(1000);
+	}
+	if (!driverInstalled) {
+		WINENV.SetupDiDestroyDeviceInfoList(deviceInfoSet);
+		return std::string("UpdateDriverForPlugAndPlayDevices() failed (made 60 attempts)");
+	}
+
+	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);
+	}
+
+	std::string errlist;
+	for(std::set<std::string>::iterator iidp(instanceIdPathsToRemove.begin());iidp!=instanceIdPathsToRemove.end();++iidp) {
+		std::string err = deletePersistentTapDevice(iidp->c_str());
+		if (err.length() > 0) {
+			if (errlist.length() > 0)
+				errlist.push_back(',');
+			errlist.append(err);
+		}
+	}
+	return errlist;
+}
+
+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);
+	}
+
+	std::string errlist;
+	for(std::set<std::string>::iterator iidp(instanceIdPathsToRemove.begin());iidp!=instanceIdPathsToRemove.end();++iidp) {
+		std::string err = deletePersistentTapDevice(iidp->c_str());
+		if (err.length() > 0) {
+			if (errlist.length() > 0)
+				errlist.push_back(',');
+			errlist.append(err);
+		}
+	}
+	return errlist;
+}
+
+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 +470,20 @@ WindowsEthernetTap::WindowsEthernetTap(
 	char subkeyClass[4096];
 	char subkeyClass[4096];
 	char data[4096];
 	char data[4096];
 	char tag[24];
 	char tag[24];
+	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
-	// 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;
 	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,15 +495,14 @@ 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';
-				if (!strnicmp(data,"zttap",5)) {
+				data[dataLen] = (char)0;
+
+				if (WINENV.tapDriverName == data) {
 					std::string instanceId;
 					std::string instanceId;
 					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)
 						instanceId.assign(data,dataLen);
 						instanceId.assign(data,dataLen);
-						existingDeviceInstances.insert(instanceId);
-					}
 
 
 					std::string instanceIdPath;
 					std::string instanceIdPath;
 					type = 0;
 					type = 0;
@@ -196,74 +532,61 @@ 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
-		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);
-
-		// Scan for the new instance by simply looking for taps that weren't originally there...
-		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)) {
-						type = 0;
-						dataLen = sizeof(data);
-						if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
-							if (existingDeviceInstances.count(std::string(data,dataLen)) == 0) {
-								RegSetKeyValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",REG_SZ,tag,(DWORD)(strlen(tag)+1));
-								_netCfgInstanceId.assign(data,dataLen);
+		for(int getNewAttemptCounter=0;getNewAttemptCounter<2;++getNewAttemptCounter) {
+			for(DWORD subkeyIndex=0;;++subkeyIndex) {
+				DWORD type;
+				DWORD dataLen;
+				DWORD subkeyNameLen = sizeof(subkeyName);
+				DWORD subkeyClassLen = sizeof(subkeyClass);
+				FILETIME lastWriteTime;
+				if (RegEnumKeyExA(nwAdapters,subkeyIndex,subkeyName,&subkeyNameLen,(DWORD *)0,subkeyClass,&subkeyClassLen,&lastWriteTime) == ERROR_SUCCESS) {
+					type = 0;
+					dataLen = sizeof(data);
+					if (RegGetValueA(nwAdapters,subkeyName,"ComponentId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
+						data[dataLen] = '\0';
+
+						if (WINENV.tapDriverName == data) {
+							type = 0;
+							dataLen = sizeof(data);
+							if ((RegGetValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",RRF_RT_ANY,&type,(PVOID)data,&dataLen) != ERROR_SUCCESS)||(dataLen <= 0)) {
 								type = 0;
 								type = 0;
 								dataLen = sizeof(data);
 								dataLen = sizeof(data);
-								if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS)
-									_deviceInstanceId.assign(data,dataLen);
-								mySubkeyName = subkeyName;
+								if (RegGetValueA(nwAdapters,subkeyName,"NetCfgInstanceId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
+									RegSetKeyValueA(nwAdapters,subkeyName,"_ZeroTierTapIdentifier",REG_SZ,tag,(DWORD)(strlen(tag)+1));
 
 
-								// Disable DHCP by default on newly created devices
-								HKEY tcpIpInterfaces;
-								if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) {
-									DWORD enable = 0;
-									RegSetKeyValueA(tcpIpInterfaces,_netCfgInstanceId.c_str(),"EnableDHCP",REG_DWORD,&enable,sizeof(enable));
-									RegCloseKey(tcpIpInterfaces);
-								}
+									_netCfgInstanceId.assign(data,dataLen);
 
 
-								break; // found it!
+									type = 0;
+									dataLen = sizeof(data);
+									if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS)
+										_deviceInstanceId.assign(data,dataLen);
+
+									mySubkeyName = subkeyName;
+
+									// Disable DHCP by default on new devices
+									HKEY tcpIpInterfaces;
+									if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) {
+										DWORD enable = 0;
+										RegSetKeyValueA(tcpIpInterfaces,_netCfgInstanceId.c_str(),"EnableDHCP",REG_DWORD,&enable,sizeof(enable));
+										RegCloseKey(tcpIpInterfaces);
+									}
+
+									break; // found an unused zttap device
+								}
 							}
 							}
 						}
 						}
 					}
 					}
-				}
-			} else break; // no more keys or error occurred
+				} else break; // no more keys or error occurred
+			}
+
+			if (_netCfgInstanceId.length() > 0) {
+				break; // found an unused zttap device
+			} else {
+				// no unused zttap devices, so create one
+				std::string errm = addNewPersistentTapDevice((std::string(_pathToHelpers) + WINENV.tapDriverPath).c_str());
+				if (errm.length() > 0)
+					throw std::runtime_error(std::string("unable to create new device instance: ")+errm);
+			}
 		}
 		}
 	}
 	}
 
 
@@ -281,6 +604,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 +615,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 +628,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 +647,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)
@@ -340,63 +662,28 @@ bool WindowsEthernetTap::enabled() const
 
 
 bool WindowsEthernetTap::addIp(const InetAddress &ip)
 bool WindowsEthernetTap::addIp(const InetAddress &ip)
 {
 {
-	if (!_initialized)
-		return false;
 	if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
 	if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
 		return false;
 		return false;
-
-	std::vector<InetAddress> haveIps(ips());
-
-	try {
-		// Add IP to interface at the netlink level if not already assigned.
-		if (!std::binary_search(haveIps.begin(),haveIps.end(),ip)) {
-			MIB_UNICASTIPADDRESS_ROW ipr;
-
-			InitializeUnicastIpAddressEntry(&ipr);
-			if (ip.isV4()) {
-				ipr.Address.Ipv4.sin_family = AF_INET;
-				ipr.Address.Ipv4.sin_addr.S_un.S_addr = *((const uint32_t *)ip.rawIpData());
-				ipr.OnLinkPrefixLength = ip.port();
-				if (ipr.OnLinkPrefixLength >= 32)
-					return false;
-			} else if (ip.isV6()) {
-				ipr.Address.Ipv6.sin6_family = AF_INET6;
-				memcpy(ipr.Address.Ipv6.sin6_addr.u.Byte,ip.rawIpData(),16);
-				ipr.OnLinkPrefixLength = ip.port();
-				if (ipr.OnLinkPrefixLength >= 128)
-					return false;
-			} else return false;
-
-			ipr.PrefixOrigin = IpPrefixOriginManual;
-			ipr.SuffixOrigin = IpSuffixOriginManual;
-			ipr.ValidLifetime = 0xffffffff;
-			ipr.PreferredLifetime = 0xffffffff;
-
-			ipr.InterfaceLuid = _deviceLuid;
-			ipr.InterfaceIndex = _getDeviceIndex();
-
-			if (CreateUnicastIpAddressEntry(&ipr) != NO_ERROR)
-				return false;
-		}
-
-		std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
-		if (std::find(regIps.begin(),regIps.end(),ip.toIpString()) == regIps.end()) {
-			std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
-			regIps.push_back(ip.toIpString());
-			regSubnetMasks.push_back(ip.netmask().toIpString());
-			_setRegistryIPv4Value("IPAddress",regIps);
-			_setRegistryIPv4Value("SubnetMask",regSubnetMasks);
-		}
-	} catch ( ... ) {
-		return false;
-	}
+	Mutex::Lock _l(_assignedIps_m);
+	if (std::find(_assignedIps.begin(),_assignedIps.end(),ip) != _assignedIps.end())
+		return true;
+	_assignedIps.push_back(ip);
+	_syncIps();
 	return true;
 	return true;
 }
 }
 
 
 bool WindowsEthernetTap::removeIp(const InetAddress &ip)
 bool WindowsEthernetTap::removeIp(const InetAddress &ip)
 {
 {
+	{
+		Mutex::Lock _l(_assignedIps_m);
+		std::vector<InetAddress>::iterator aip(std::find(_assignedIps.begin(),_assignedIps.end(),ip));
+		if (aip != _assignedIps.end())
+			_assignedIps.erase(aip);
+	}
+
 	if (!_initialized)
 	if (!_initialized)
 		return false;
 		return false;
+
 	try {
 	try {
 		MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
 		MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
 		if (GetUnicastIpAddressTable(AF_UNSPEC,&ipt) == NO_ERROR) {
 		if (GetUnicastIpAddressTable(AF_UNSPEC,&ipt) == NO_ERROR) {
@@ -572,13 +859,18 @@ void WindowsEthernetTap::threadMain()
 
 
 	try {
 	try {
 		while (_run) {
 		while (_run) {
-			_enableTapDevice();
+			// Because Windows
+			setPersistentTapDeviceState(_deviceInstanceId.c_str(),false);
+			Sleep(500);
+			setPersistentTapDeviceState(_deviceInstanceId.c_str(),true);
+			Sleep(500);
+			setPersistentTapDeviceState(_deviceInstanceId.c_str(),false);
+			Sleep(500);
+			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();
-				_enableTapDevice();
 				Sleep(1000);
 				Sleep(1000);
 				continue;
 				continue;
 			}
 			}
@@ -668,6 +960,12 @@ void WindowsEthernetTap::threadMain()
 			}
 			}
 #endif
 #endif
 
 
+			// Assign or re-assign any should-be-assigned IPs in case we have restarted
+			{
+				Mutex::Lock _l(_assignedIps_m);
+				_syncIps();
+			}
+
 			memset(&tapOvlRead,0,sizeof(tapOvlRead));
 			memset(&tapOvlRead,0,sizeof(tapOvlRead));
 			tapOvlRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
 			tapOvlRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
 			memset(&tapOvlWrite,0,sizeof(tapOvlWrite));
 			memset(&tapOvlWrite,0,sizeof(tapOvlWrite));
@@ -761,131 +1059,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;
@@ -956,4 +1129,55 @@ void WindowsEthernetTap::_setRegistryIPv4Value(const char *regKey,const std::vec
 	}
 	}
 }
 }
 
 
+void WindowsEthernetTap::_syncIps()
+{
+	// assumes _assignedIps_m is locked
+
+	if (!_initialized)
+		return;
+
+	std::vector<InetAddress> haveIps(ips());
+
+	for(std::vector<InetAddress>::const_iterator aip(_assignedIps.begin());aip!=_assignedIps.end();++aip) {
+		if (std::find(haveIps.begin(),haveIps.end(),*aip) == haveIps.end()) {
+			MIB_UNICASTIPADDRESS_ROW ipr;
+
+			InitializeUnicastIpAddressEntry(&ipr);
+			if (aip->isV4()) {
+				ipr.Address.Ipv4.sin_family = AF_INET;
+				ipr.Address.Ipv4.sin_addr.S_un.S_addr = *((const uint32_t *)aip->rawIpData());
+				ipr.OnLinkPrefixLength = aip->netmaskBits();
+				if (ipr.OnLinkPrefixLength >= 32)
+					continue;
+			} else if (aip->isV6()) {
+				ipr.Address.Ipv6.sin6_family = AF_INET6;
+				memcpy(ipr.Address.Ipv6.sin6_addr.u.Byte,aip->rawIpData(),16);
+				ipr.OnLinkPrefixLength = aip->netmaskBits();
+				if (ipr.OnLinkPrefixLength >= 128)
+					continue;
+			} else continue;
+
+			ipr.PrefixOrigin = IpPrefixOriginManual;
+			ipr.SuffixOrigin = IpSuffixOriginManual;
+			ipr.ValidLifetime = 0xffffffff;
+			ipr.PreferredLifetime = 0xffffffff;
+
+			ipr.InterfaceLuid = _deviceLuid;
+			ipr.InterfaceIndex = _getDeviceIndex();
+
+			CreateUnicastIpAddressEntry(&ipr);
+		}
+
+		std::string ipStr(aip->toString());
+		std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
+		if (std::find(regIps.begin(),regIps.end(),ipStr) == regIps.end()) {
+			std::vector<std::string> regSubnetMasks(_getRegistryIPv4Value("SubnetMask"));
+			regIps.push_back(ipStr);
+			regSubnetMasks.push_back(aip->netmask().toIpString());
+			_setRegistryIPv4Value("IPAddress",regIps);
+			_setRegistryIPv4Value("SubnetMask",regSubnetMasks);
+		}
+	}
+}
+
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 44 - 5
osdep/WindowsEthernetTap.hpp

@@ -41,6 +41,7 @@
 #include "../node/Mutex.hpp"
 #include "../node/Mutex.hpp"
 #include "../node/Array.hpp"
 #include "../node/Array.hpp"
 #include "../node/MulticastGroup.hpp"
 #include "../node/MulticastGroup.hpp"
+#include "../node/InetAddress.hpp"
 #include "../osdep/Thread.hpp"
 #include "../osdep/Thread.hpp"
 
 
 namespace ZeroTier {
 namespace ZeroTier {
@@ -48,6 +49,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,15 +117,11 @@ 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 _enableTapDevice();
 	NET_IFINDEX _getDeviceIndex(); // throws on failure
 	NET_IFINDEX _getDeviceIndex(); // throws on failure
 	std::vector<std::string> _getRegistryIPv4Value(const char *regKey);
 	std::vector<std::string> _getRegistryIPv4Value(const char *regKey);
 	void _setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value);
 	void _setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value);
+	void _syncIps();
 
 
 	void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
 	void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
 	void *_arg;
 	void *_arg;
@@ -101,6 +137,9 @@ private:
 	std::string _netCfgInstanceId;
 	std::string _netCfgInstanceId;
 	std::string _deviceInstanceId;
 	std::string _deviceInstanceId;
 
 
+	std::vector<InetAddress> _assignedIps; // IPs assigned with addIp
+	Mutex _assignedIps_m;
+
 	std::vector<MulticastGroup> _multicastGroups;
 	std::vector<MulticastGroup> _multicastGroups;
 
 
 	std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
 	std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;

+ 12 - 1
service/OneService.cpp

@@ -943,6 +943,17 @@ public:
 							friendlyName,
 							friendlyName,
 							StapFrameHandler,
 							StapFrameHandler,
 							(void *)this))).first;
 							(void *)this))).first;
+					} catch (std::exception &exc) {
+#ifdef __WINDOWS__
+						FILE *tapFailLog = fopen((_homePath + ZT_PATH_SEPARATOR_S"port_error_log.txt").c_str(),"a");
+						if (tapFailLog) {
+							fprintf(tapFailLog,"%.16llx: %s"ZT_EOL_S,(unsigned long long)nwid,exc.what());
+							fclose(tapFailLog);
+						}
+#else
+						fprintf(stderr,"ERROR: unable to configure virtual network port: %s"ZT_EOL_S,exc.what());
+#endif
+						return -999;
 					} catch ( ... ) {
 					} catch ( ... ) {
 						return -999; // tap init failed
 						return -999; // tap init failed
 					}
 					}
@@ -982,7 +993,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;

+ 4 - 4
windows/ZeroTierOne/ZeroTierOne.vcxproj

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