summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOdd Stranne <odd@mullvad.net>2018-07-10 13:14:57 +0200
committerOdd Stranne <odd@mullvad.net>2018-07-10 13:14:57 +0200
commita3013b89a520c3517476ea6544f912ae0c93e00c (patch)
tree89d2677fb478c581daeb9be203cb5a481b827dc8
parent5b3a8ee27257f7347e371f92b13a868f266bca93 (diff)
parent40fd243114fbe5a5df703936d179d86c8ba42749 (diff)
downloadmullvadvpn-a3013b89a520c3517476ea6544f912ae0c93e00c.tar.xz
mullvadvpn-a3013b89a520c3517476ea6544f912ae0c93e00c.zip
Merge branch 'rename-tunnel-interface'
-rw-r--r--CHANGELOG.md3
-rw-r--r--build_windows_modules.sh60
m---------dist-assets/binaries0
-rw-r--r--dist-assets/windows/installer.nsh81
-rw-r--r--mullvad-daemon/src/main.rs13
-rw-r--r--talpid-core/src/process/openvpn.rs19
-rw-r--r--talpid-core/src/tunnel/mod.rs7
-rw-r--r--windows/nsis-plugins/nsis-plugins.sln33
-rw-r--r--windows/nsis-plugins/readme.txt22
-rw-r--r--windows/nsis-plugins/src/driverlogic/context.cpp132
-rw-r--r--windows/nsis-plugins/src/driverlogic/context.h51
-rw-r--r--windows/nsis-plugins/src/driverlogic/dllmain.cpp11
-rw-r--r--windows/nsis-plugins/src/driverlogic/driverlogic.cpp163
-rw-r--r--windows/nsis-plugins/src/driverlogic/driverlogic.def6
-rw-r--r--windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj124
-rw-r--r--windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj.filters17
-rw-r--r--windows/nsis-plugins/src/driverlogic/stdafx.cpp8
-rw-r--r--windows/nsis-plugins/src/driverlogic/stdafx.h16
-rw-r--r--windows/nsis-plugins/src/driverlogic/targetver.h12
19 files changed, 746 insertions, 32 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f87e0af5f..02e136323b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,9 @@ Line wrap the file at 100 chars. Th
- Add OpenVPN logs at the top of the problem report instead of middle, to aid support work.
- Lower per log size limit in the problem report to 128 kiB.
+#### Windows
+- Rename tunnel interface to "Mullvad".
+
### Fixed
- Disable account input when logging in.
- Keep the user input in problem report form while the app runs, or until the report is successfully
diff --git a/build_windows_modules.sh b/build_windows_modules.sh
index aa5a5a1daf..9121a07e0e 100644
--- a/build_windows_modules.sh
+++ b/build_windows_modules.sh
@@ -1,15 +1,10 @@
set -eu
-# List of solutions to build
-WINFW_SOLUTIONS=${WINFW_SOLUTIONS:-"winfw"}
-WINDNS_SOLUTIONS=${WINDNS_SOLUTIONS:-"windns"}
-WINROUTE_SOLUTIONS=${WINROUTE_SOLUTIONS:-"winroute"}
-
-# Override this variable to set your own list of build configurations. Set this
-# to "Release" to build release versions.
+# List of solution configurations to build.
+# Default configurations generated by Visual Studio are "Release" and "Debug".
CPP_BUILD_MODES=${CPP_BUILD_MODES:-"Debug"}
-# Override this variable to set different target platforms. Add "x86" to build
-# win32 versions.
+# List of target platforms to build for.
+# Common platforms include "x86" and "x64".
CPP_BUILD_TARGETS=${CPP_BUILD_TARGETS:-"x64"}
# Override this to set a different cargo target directory
CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-"./target/"}
@@ -18,25 +13,40 @@ if [[ "${1:-""}" == "--dev-build" ]]; then
DEV_BUILD=true
fi
-# Builds visual studio projects
-function build_project
+function clean_solution
{
local path="$1"
- local sln="$1/$2"
- local solutions="$3"
-
+
if [[ -z "${DEV_BUILD+x}" ]]; then
# Clean all intermediate and output files
rm -r $path/bin/* || true
fi
+}
+
+function build_solution_config
+{
+ local sln="$1"
+ local config="$2"
+ local platform="$3"
set -x
+ cmd.exe "/c msbuild.exe /m $(to_win_path $sln) /p:Configuration=$config /p:Platform=$platform"
+ set +x
+}
+
+# Builds visual studio solution in all (specified) configurations
+function build_solution
+{
+ local path="$1"
+ local sln="$1/$2"
+
+ clean_solution $path
+
for mode in $CPP_BUILD_MODES; do
for target in $CPP_BUILD_TARGETS; do
- cmd.exe "/c msbuild.exe /m $(to_win_path $sln) /p:Configuration=$mode /p:Platform=$target /t:$solutions"
+ build_solution_config $sln $mode $target
done
done
- set +x
}
function to_win_path
@@ -110,7 +120,6 @@ function copy_outputs
done
done
done
-
}
# Since Microsoft likes to name their architectures differently from Rust, this
@@ -140,20 +149,29 @@ function rustc_host_arch
| tr -d '"'
}
-function main
+function build_nsis_plugins
{
+ local nsis_root_path=${CPP_ROOT_PATH:-"./windows/nsis-plugins"}
+ clean_solution "$nsis_root_path"
+ build_solution_config "$nsis_root_path/nsis-plugins.sln" "Release" "x86"
+}
+
+function main
+{
local winfw_root_path=${CPP_ROOT_PATH:-"./windows/winfw"}
local windns_root_path=${CPP_ROOT_PATH:-"./windows/windns"}
local winroute_root_path=${CPP_ROOT_PATH:-"./windows/winroute"}
- build_project "$winfw_root_path" "winfw.sln" "$WINFW_SOLUTIONS"
- build_project "$windns_root_path" "windns.sln" "$WINDNS_SOLUTIONS"
- build_project "$winroute_root_path" "winroute.sln" "$WINROUTE_SOLUTIONS"
+ build_solution "$winfw_root_path" "winfw.sln"
+ build_solution "$windns_root_path" "windns.sln"
+ build_solution "$winroute_root_path" "winroute.sln"
copy_outputs $winfw_root_path "winfw.dll"
copy_outputs $windns_root_path "windns.dll"
copy_outputs $winroute_root_path "winroute.dll"
+
+ build_nsis_plugins
}
main
diff --git a/dist-assets/binaries b/dist-assets/binaries
-Subproject bf46caf014706ba6dc30e7c304ae74bc359e43b
+Subproject a2425a4aab0d30cf0f711bfe453d8763080f79e
diff --git a/dist-assets/windows/installer.nsh b/dist-assets/windows/installer.nsh
index 4b29fe1df3..ba66c6650f 100644
--- a/dist-assets/windows/installer.nsh
+++ b/dist-assets/windows/installer.nsh
@@ -1,6 +1,7 @@
!include stdutils.nsh
!include winver.nsh
-#!include strcontains.nsh
+
+!addplugindir "${PROJECT_DIR}\windows\nsis-plugins\bin\Win32-Release"
#
# NOTES
@@ -18,6 +19,16 @@
!define SERVICE_STARTED 0
!define SERVICE_START_PENDING 2
+# Return codes from driverlogic::EstablishBaseline
+!define EB_GENERAL_ERROR 0
+!define EB_NO_INTERFACES_PRESENT 1
+!define EB_SOME_INTERFACES_PRESENT 2
+!define EB_MULLVAD_INTERFACE_PRESENT 3
+
+# Return codes from driverlogic::IdentifyNewInterface
+!define INI_GENERAL_ERROR 0
+!define INI_SUCCESS 1
+
#
# BreakInstallation
#
@@ -29,7 +40,7 @@
#
!macro BreakInstallation
- Delete "$INSTDIR\mullvadvpn.exe"
+ Delete "$INSTDIR\mullvad vpn.exe"
!macroend
@@ -65,6 +76,8 @@
#
!macro InstallDriver
+ Var /GLOBAL InstallDriver_BaselineStatus
+
Push $0
Push $1
@@ -74,16 +87,29 @@
Pop $1
${If} $0 != 0
- StrCpy $R0 "Failed to list hardware IDs: error $0"
+ StrCpy $R0 "Failed to list virtual adapters: error $0"
Goto InstallDriver_return
${EndIf}
- # If the driver is already installed, the hardware ID will be echoed in the command output
- # $1 holds the output from "tapinstall hwids"
- ${StrContains} $0 ${TAP_HARDWARE_ID} $1
- StrCmp $0 "" InstallDriver_install_driver
+ driverlogic::EstablishBaseline $1
+
+ Pop $0
+ Pop $1
+
+ ${If} $0 == ${EB_GENERAL_ERROR}
+ StrCpy $R0 "Failed to parse virtual adapter data: $1"
+ Goto InstallDriver_return
+ ${EndIf}
- # Update driver
+ Push $0
+ Pop $InstallDriver_BaselineStatus
+
+ IntCmp $0 ${EB_NO_INTERFACES_PRESENT} InstallDriver_install_driver
+
+ #
+ # Driver is already installed and there are one or several virtual adapters present.
+ # Update driver.
+ #
nsExec::ExecToStack '"$TEMP\driver\tapinstall.exe" update "$TEMP\driver\OemVista.inf" ${TAP_HARDWARE_ID}'
Pop $0
@@ -94,10 +120,14 @@
Goto InstallDriver_return
${EndIf}
- Goto InstallDriver_return_success
+ IntCmp $InstallDriver_BaselineStatus ${EB_MULLVAD_INTERFACE_PRESENT} InstallDriver_return_success
InstallDriver_install_driver:
+ #
+ # Install driver and create a virtual adapter.
+ # If the driver is already installed, this just creates another virtual adapter.
+ #
nsExec::ExecToStack '"$TEMP\driver\tapinstall.exe" install "$TEMP\driver\OemVista.inf" ${TAP_HARDWARE_ID}'
Pop $0
@@ -107,7 +137,40 @@
StrCpy $R0 "Failed to install TAP driver: error $0"
Goto InstallDriver_return
${EndIf}
+
+ nsExec::ExecToStack '"$TEMP\driver\tapinstall.exe" hwids ${TAP_HARDWARE_ID}'
+
+ Pop $0
+ Pop $1
+
+ ${If} $0 != 0
+ StrCpy $R0 "Failed to list virtual adapters: error $0"
+ Goto InstallDriver_return
+ ${EndIf}
+
+ driverlogic::IdentifyNewInterface $1
+ Pop $0
+ Pop $1
+
+ ${If} $0 != ${INI_SUCCESS}
+ StrCpy $R0 "Failed to identify virtual adapter: $1"
+ Goto InstallDriver_return
+ ${EndIf}
+
+ #
+ # Rename the newly added virtual adapter to "Mullvad".
+ #
+ nsExec::ExecToStack '"netsh.exe" interface set interface name = "$1" newname = "Mullvad"'
+
+ Pop $0
+ Pop $1
+
+ ${If} $0 != 0
+ StrCpy $R0 "Failed to rename virtual adapter: error $0"
+ Goto InstallDriver_return
+ ${EndIf}
+
InstallDriver_return_success:
Push 0
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index d2dc1da4f7..abfc83b764 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -129,6 +129,8 @@ const DAEMON_LOG_FILENAME: &str = "daemon.log";
const OPENVPN_LOG_FILENAME: &str = "openvpn.log";
const WIREGUARD_LOG_FILENAME: &str = "wireguard.log";
+#[cfg(windows)]
+const TUNNEL_INTERFACE_ALIAS: &str = "Mullvad";
/// All events that can happen in the daemon. Sent from various threads and exposed interfaces.
pub enum DaemonEvent {
@@ -742,9 +744,20 @@ impl Daemon {
};
let tunnel_options = self.settings.get_tunnel_options();
+ let tunnel_alias = {
+ #[cfg(windows)]
+ {
+ Some(TUNNEL_INTERFACE_ALIAS.into())
+ }
+ #[cfg(not(windows))]
+ {
+ None
+ }
+ };
TunnelMonitor::new(
tunnel_endpoint,
&tunnel_options,
+ tunnel_alias,
account_token,
tunnel_log.as_ref().map(PathBuf::as_path),
&self.resource_dir,
diff --git a/talpid-core/src/process/openvpn.rs b/talpid-core/src/process/openvpn.rs
index 527e45f3f3..0a5e93a084 100644
--- a/talpid-core/src/process/openvpn.rs
+++ b/talpid-core/src/process/openvpn.rs
@@ -16,7 +16,10 @@ use talpid_types::net;
static BASE_ARGUMENTS: &[&[&str]] = &[
&["--client"],
&["--nobind"],
+ #[cfg(not(windows))]
&["--dev", "tun"],
+ #[cfg(windows)]
+ &["--dev-type", "tun"],
&["--ping", "3"],
&["--ping-exit", "15"],
&["--connect-retry", "0", "0"],
@@ -48,6 +51,7 @@ pub struct OpenVpnCommand {
plugin: Option<(PathBuf, Vec<String>)>,
log: Option<PathBuf>,
tunnel_options: net::OpenVpnTunnelOptions,
+ tunnel_alias: Option<OsString>,
}
impl OpenVpnCommand {
@@ -64,6 +68,7 @@ impl OpenVpnCommand {
plugin: None,
log: None,
tunnel_options: net::OpenVpnTunnelOptions::default(),
+ tunnel_alias: None,
}
}
@@ -117,11 +122,18 @@ impl OpenVpnCommand {
}
/// Sets extra options
- pub fn set_tunnel_options(&mut self, tunnel_options: &net::OpenVpnTunnelOptions) -> &mut Self {
+ pub fn tunnel_options(&mut self, tunnel_options: &net::OpenVpnTunnelOptions) -> &mut Self {
self.tunnel_options = *tunnel_options;
self
}
+ /// Sets the tunnel alias which will be used to identify a tunnel device that will be used by
+ /// OpenVPN.
+ pub fn tunnel_alias(&mut self, tunnel_alias: Option<OsString>) -> &mut Self {
+ self.tunnel_alias = tunnel_alias;
+ self
+ }
+
/// Returns all arguments that the subprocess would be spawned with.
pub fn get_arguments(&self) -> Vec<OsString> {
let mut args: Vec<OsString> = Self::base_arguments().iter().map(OsString::from).collect();
@@ -159,6 +171,11 @@ impl OpenVpnCommand {
args.push(OsString::from(mssfix.to_string()));
}
+ if let Some(ref tunnel_device) = self.tunnel_alias {
+ args.push(OsString::from("--dev-node"));
+ args.push(tunnel_device.clone());
+ }
+
args.extend(Self::security_arguments().iter().map(OsString::from));
args
diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs
index 3c3e9979f2..cd90924ac1 100644
--- a/talpid-core/src/tunnel/mod.rs
+++ b/talpid-core/src/tunnel/mod.rs
@@ -5,6 +5,7 @@ use openvpn_plugin::types::OpenVpnPluginEvent;
use process::openvpn::OpenVpnCommand;
use std::collections::HashMap;
+use std::ffi::OsString;
use std::fs;
use std::io::{self, Write};
use std::net::Ipv4Addr;
@@ -132,6 +133,7 @@ impl TunnelMonitor {
pub fn new<L>(
tunnel_endpoint: TunnelEndpoint,
tunnel_options: &TunnelOptions,
+ tunnel_alias: Option<OsString>,
account_token: &str,
log: Option<&Path>,
resource_dir: &Path,
@@ -148,6 +150,7 @@ impl TunnelMonitor {
.chain_err(|| ErrorKind::CredentialsWriteError)?;
let cmd = Self::create_openvpn_cmd(
tunnel_endpoint.to_endpoint(),
+ tunnel_alias,
&tunnel_options.openvpn,
user_pass_file.as_ref(),
log,
@@ -179,6 +182,7 @@ impl TunnelMonitor {
fn create_openvpn_cmd(
remote: Endpoint,
+ tunnel_alias: Option<OsString>,
options: &OpenVpnTunnelOptions,
user_pass_file: &Path,
log: Option<&Path>,
@@ -190,7 +194,8 @@ impl TunnelMonitor {
}
cmd.remote(remote)
.user_pass(user_pass_file)
- .set_tunnel_options(&options)
+ .tunnel_options(&options)
+ .tunnel_alias(tunnel_alias)
.ca(resource_dir.join("ca.crt"))
.crl(resource_dir.join("crl.pem"));
if let Some(log) = log {
diff --git a/windows/nsis-plugins/nsis-plugins.sln b/windows/nsis-plugins/nsis-plugins.sln
new file mode 100644
index 0000000000..3b3e7a7f62
--- /dev/null
+++ b/windows/nsis-plugins/nsis-plugins.sln
@@ -0,0 +1,33 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27130.2027
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "driverlogic", "src\driverlogic\driverlogic.vcxproj", "{AABA9AB7-A7D0-4BB5-A1FA-92F566023E0D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "..\windows-libraries\src\libcommon\libcommon.vcxproj", "{B52E2D10-A94A-4605-914A-2DCEF6A757EF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AABA9AB7-A7D0-4BB5-A1FA-92F566023E0D}.Debug|x86.ActiveCfg = Debug|Win32
+ {AABA9AB7-A7D0-4BB5-A1FA-92F566023E0D}.Debug|x86.Build.0 = Debug|Win32
+ {AABA9AB7-A7D0-4BB5-A1FA-92F566023E0D}.Release|x86.ActiveCfg = Release|Win32
+ {AABA9AB7-A7D0-4BB5-A1FA-92F566023E0D}.Release|x86.Build.0 = Release|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.ActiveCfg = Debug|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.Build.0 = Debug|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.ActiveCfg = Release|Win32
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {11627485-765E-49C6-B059-B031D99CDD7B}
+ EndGlobalSection
+EndGlobal
diff --git a/windows/nsis-plugins/readme.txt b/windows/nsis-plugins/readme.txt
new file mode 100644
index 0000000000..16ae863512
--- /dev/null
+++ b/windows/nsis-plugins/readme.txt
@@ -0,0 +1,22 @@
+Mullvad notes for creating an NSIS plug-in
+==
+
+Using `Visual Studio`, create a new DLL project.
+
+Remove the x64 build configurations. Do this both for the solution and the project.
+ AMD64 support is spotty in NSIS and may not be supported at all when using NSIS via electron-builder.
+
+Update `targetver.h` with an appropriate value for `_WIN32_WINNT`.
+
+Disable /SafeSEH (it's under Linker -> Advanced).
+
+Add the parent of the plugin SDK folder as an include directory for header files (it's under C/C++ -> General).
+
+Add the plugin SDK folder as an include directory for linker libraries (it's under Linker -> General).
+
+Add `pluginapi-x86-unicode.lib` to the set of linker libraries (it's under Linker -> Input).
+
+Add `libc.lib` as an ignored linker library. `pluginapi-x86-unicode.lib` has a reference to `libc.lib`, which is
+ apparently not needed. The setting is under Linker -> Input.
+
+Change to static CRT linkage for the Release configuration (it's under C/C++ -> Code Generation).
diff --git a/windows/nsis-plugins/src/driverlogic/context.cpp b/windows/nsis-plugins/src/driverlogic/context.cpp
new file mode 100644
index 0000000000..d2481e3e67
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/context.cpp
@@ -0,0 +1,132 @@
+#include "stdafx.h"
+#include "context.h"
+#include <libcommon/string.h>
+#include <libcommon/wmi/connection.h>
+#include <libcommon/wmi/resultset.h>
+#include <libcommon/wmi/wmi.h>
+#include <vector>
+#include <stdexcept>
+#include <algorithm>
+#include <memory>
+#include <sstream>
+
+using namespace common;
+
+namespace
+{
+
+std::vector<std::wstring> BlockToRows(const std::wstring &textBlock)
+{
+ //
+ // This is such a hack :-(
+ //
+ // It only works because the tokenizer is greedy and because we don't care about
+ // empty lines for this usage.
+ //
+ return common::string::Tokenize(textBlock, L"\r\n");
+}
+
+} // anonymous namespace
+
+Context::BaselineStatus Context::establishBaseline(const std::wstring &textBlock)
+{
+ m_baseline = ParseVirtualNics(textBlock);
+
+ if (m_baseline.empty())
+ {
+ return BaselineStatus::NO_INTERFACES_PRESENT;
+ }
+
+ for (const auto &nic : m_baseline)
+ {
+ if (0 == _wcsicmp(nic.alias.c_str(), L"mullvad"))
+ {
+ return BaselineStatus::MULLVAD_INTERFACE_PRESENT;
+ }
+ }
+
+ return BaselineStatus::SOME_INTERFACES_PRESENT;
+}
+
+void Context::recordCurrentState(const std::wstring &textBlock)
+{
+ m_currentState = ParseVirtualNics(textBlock);
+}
+
+Context::VirtualNic Context::getNewAdapter()
+{
+ std::vector<VirtualNic> added;
+
+ for (const auto &nic : m_currentState)
+ {
+ if (m_baseline.end() == m_baseline.find(nic))
+ {
+ added.push_back(nic);
+ }
+ }
+
+ if (added.size() != 1)
+ {
+ throw std::runtime_error("Unable to identify newly added virtual adapter");
+ }
+
+ return added[0];
+}
+
+//static
+std::set<Context::VirtualNic> Context::ParseVirtualNics(const std::wstring &textBlock)
+{
+ // ROOT\NET\0000
+ // Name: TAP - Windows Adapter V9
+ // Hardware IDs :
+ // tap0901
+ // 1 matching device(s) found.
+
+ std::set<VirtualNic> nics;
+
+ auto text = BlockToRows(textBlock);
+
+ size_t line = 0;
+
+ while (nullptr != wcschr(text.at(line).c_str(), L'\\'))
+ {
+ auto nameDelimiter = wcschr(text.at(line + 1).c_str(), L':');
+
+ if (nullptr == nameDelimiter)
+ {
+ throw std::runtime_error("Unexpected formatting in input data");
+ }
+
+ VirtualNic nic;
+
+ nic.node = text.at(line);
+ nic.name = std::wstring(nameDelimiter + 2);
+ nic.alias = GetNicAlias(nic.name);
+
+ nics.emplace(std::move(nic));
+ line += 4;
+ }
+
+ return nics;
+}
+
+//static
+std::wstring Context::GetNicAlias(const std::wstring &name)
+{
+ static wmi::Connection connection(wmi::Connection::Namespace::Cimv2);
+
+ std::wstringstream ss;
+
+ ss << L"SELECT * from Win32_NetworkAdapter WHERE Name = \"" << name << L"\"";
+
+ auto resultset = connection.query(ss.str().c_str());
+
+ if (false == resultset.advance())
+ {
+ throw std::runtime_error("Unable to look up virtual adapter using WMI");
+ }
+
+ auto alias = wmi::WmiGetPropertyAlways(resultset.result(), L"NetConnectionID");
+
+ return V_BSTR(&alias);
+}
diff --git a/windows/nsis-plugins/src/driverlogic/context.h b/windows/nsis-plugins/src/driverlogic/context.h
new file mode 100644
index 0000000000..53d2ddeedc
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/context.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <set>
+#include <string>
+
+class Context
+{
+public:
+
+ struct VirtualNic
+ {
+ std::wstring node;
+ std::wstring name;
+ std::wstring alias;
+
+ bool operator<(const VirtualNic &rhs) const
+ {
+ return _wcsicmp(node.c_str(), rhs.node.c_str()) < 0;
+ }
+ };
+
+ enum class BaselineStatus
+ {
+ NO_INTERFACES_PRESENT,
+ SOME_INTERFACES_PRESENT,
+ MULLVAD_INTERFACE_PRESENT
+ };
+
+ //
+ // Invoke with the output from "tapinstall hwids tap0901"
+ //
+ BaselineStatus establishBaseline(const std::wstring &textBlock);
+
+ //
+ // Invoke with the output from "tapinstall hwids tap0901"
+ //
+ void recordCurrentState(const std::wstring &textBlock);
+
+ //
+ // Identify a single new interface
+ //
+ VirtualNic getNewAdapter();
+
+private:
+
+ static std::set<VirtualNic> ParseVirtualNics(const std::wstring &textBlock);
+ static std::wstring GetNicAlias(const std::wstring &name);
+
+ std::set<VirtualNic> m_baseline;
+ std::set<VirtualNic> m_currentState;
+};
diff --git a/windows/nsis-plugins/src/driverlogic/dllmain.cpp b/windows/nsis-plugins/src/driverlogic/dllmain.cpp
new file mode 100644
index 0000000000..a5a44613dd
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/dllmain.cpp
@@ -0,0 +1,11 @@
+#include "stdafx.h"
+#include <windows.h>
+
+BOOL APIENTRY DllMain(HMODULE, DWORD reason, LPVOID)
+{
+ //
+ // Avoid doing work in DllMain since the loader lock is held
+ //
+
+ return TRUE;
+}
diff --git a/windows/nsis-plugins/src/driverlogic/driverlogic.cpp b/windows/nsis-plugins/src/driverlogic/driverlogic.cpp
new file mode 100644
index 0000000000..5604001645
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/driverlogic.cpp
@@ -0,0 +1,163 @@
+#include <stdafx.h>
+#include "context.h"
+#include <libcommon/string.h>
+#include <windows.h>
+#include <nsis/pluginapi.h>
+#include <string>
+#include <vector>
+
+Context g_context;
+
+namespace
+{
+
+std::wstring PopString()
+{
+ //
+ // NSIS functions popstring() and popstringn() require that you definitely size the buffer
+ // before popping the string. Let's do it ourselves instead.
+ //
+
+ if (!g_stacktop || !*g_stacktop)
+ {
+ throw std::runtime_error("NSIS variable stack is corrupted");
+ }
+
+ stack_t *th = *g_stacktop;
+
+ std::wstring copy(th->text);
+
+ *g_stacktop = th->next;
+ GlobalFree((HGLOBAL)th);
+
+ return copy;
+}
+
+EXTERN_C IMAGE_DOS_HEADER __ImageBase;
+
+void PinDll()
+{
+ //
+ // Apparently NSIS loads and unloads the plugin module for EVERY call it makes to the plugin.
+ // This makes it kind of difficult to maintain state.
+ //
+ // We can work around this by incrementing the module reference count.
+ // When NSIS calls FreeLibrary() the reference count decrements and becomes one.
+ //
+
+ wchar_t self[MAX_PATH];
+
+ if (0 == GetModuleFileNameW((HINSTANCE)&__ImageBase, self, _countof(self)))
+ {
+ throw std::runtime_error("Failed to pin plugin module");
+ }
+
+ LoadLibraryW(self);
+}
+
+} // anonymous namespace
+
+//
+// EstablishBaseline
+//
+// Invoke with the output from "tapinstall hwids tap0901"
+// e.g.: driverlogic::EstablishBaseline $1
+//
+enum class EstablishBaselineStatus
+{
+ GENERAL_ERROR = 0,
+ NO_INTERFACES_PRESENT,
+ SOME_INTERFACES_PRESENT,
+ MULLVAD_INTERFACE_PRESENT
+};
+
+void __declspec(dllexport) NSISCALL EstablishBaseline
+(
+ HWND hwndParent,
+ int string_size,
+ LPTSTR variables,
+ stack_t **stacktop,
+ extra_parameters *extra,
+ ...
+)
+{
+ EXDLL_INIT();
+
+ try
+ {
+ PinDll();
+
+ auto status = EstablishBaselineStatus::GENERAL_ERROR;
+
+ switch (g_context.establishBaseline(PopString()))
+ {
+ case Context::BaselineStatus::NO_INTERFACES_PRESENT:
+ {
+ status = EstablishBaselineStatus::NO_INTERFACES_PRESENT;
+ break;
+ }
+ case Context::BaselineStatus::SOME_INTERFACES_PRESENT:
+ {
+ status = EstablishBaselineStatus::SOME_INTERFACES_PRESENT;
+ break;
+ }
+ case Context::BaselineStatus::MULLVAD_INTERFACE_PRESENT:
+ {
+ status = EstablishBaselineStatus::MULLVAD_INTERFACE_PRESENT;
+ break;
+ }
+ default:
+ {
+ throw std::runtime_error("Missing case handler in switch clause");
+ }
+ }
+
+ pushstring(L"");
+ pushint(status);
+ }
+ catch (std::exception &err)
+ {
+ pushstring(common::string::ToWide(err.what()).c_str());
+ pushint(EstablishBaselineStatus::GENERAL_ERROR);
+ }
+}
+
+//
+// IdentifyNewInterface
+//
+// Invoke with the output from "tapinstall hwids tap0901"
+// e.g.: driverlogic::IdentifyNewInterface $1
+//
+enum class IdentifyNewInterfaceStatus
+{
+ GENERAL_ERROR = 0,
+ SUCCESS
+};
+
+void __declspec(dllexport) NSISCALL IdentifyNewInterface
+(
+ HWND hwndParent,
+ int string_size,
+ LPTSTR variables,
+ stack_t **stacktop,
+ extra_parameters *extra,
+ ...
+)
+{
+ EXDLL_INIT();
+
+ try
+ {
+ g_context.recordCurrentState(PopString());
+
+ auto nic = g_context.getNewAdapter();
+
+ pushstring(nic.alias.c_str());
+ pushint(IdentifyNewInterfaceStatus::SUCCESS);
+ }
+ catch (std::exception &err)
+ {
+ pushstring(common::string::ToWide(err.what()).c_str());
+ pushint(IdentifyNewInterfaceStatus::GENERAL_ERROR);
+ }
+}
diff --git a/windows/nsis-plugins/src/driverlogic/driverlogic.def b/windows/nsis-plugins/src/driverlogic/driverlogic.def
new file mode 100644
index 0000000000..f65de5b6a7
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/driverlogic.def
@@ -0,0 +1,6 @@
+LIBRARY driverlogic
+
+EXPORTS
+
+EstablishBaseline
+IdentifyNewInterface
diff --git a/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj b/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj
new file mode 100644
index 0000000000..1e6e89d2e2
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{AABA9AB7-A7D0-4BB5-A1FA-92F566023E0D}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>driverlogic</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;DRIVERLOGIC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../../../../dist-assets/binaries/windows/;$(ProjectDir)../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>$(ProjectDir)../../../../dist-assets/binaries/windows/nsis/;$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;pluginapi-x86-unicode.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <IgnoreSpecificDefaultLibraries>libc.lib</IgnoreSpecificDefaultLibraries>
+ <ModuleDefinitionFile>driverlogic.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;DRIVERLOGIC_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../../../../dist-assets/binaries/windows/;$(ProjectDir)../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>$(ProjectDir)../../../../dist-assets/binaries/windows/nsis/;$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;pluginapi-x86-unicode.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <IgnoreSpecificDefaultLibraries>libc.lib</IgnoreSpecificDefaultLibraries>
+ <ModuleDefinitionFile>driverlogic.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="context.h" />
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="driverlogic.cpp" />
+ <ClCompile Include="context.cpp" />
+ <ClCompile Include="stdafx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="driverlogic.def" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj.filters b/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj.filters
new file mode 100644
index 0000000000..2c25d275f1
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj.filters
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ <ClInclude Include="context.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="driverlogic.cpp" />
+ <ClCompile Include="stdafx.cpp" />
+ <ClCompile Include="context.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="driverlogic.def" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/windows/nsis-plugins/src/driverlogic/stdafx.cpp b/windows/nsis-plugins/src/driverlogic/stdafx.cpp
new file mode 100644
index 0000000000..3b6341d106
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// driverlogic.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/windows/nsis-plugins/src/driverlogic/stdafx.h b/windows/nsis-plugins/src/driverlogic/stdafx.h
new file mode 100644
index 0000000000..f3a07375c7
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/stdafx.h
@@ -0,0 +1,16 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include <windows.h>
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/windows/nsis-plugins/src/driverlogic/targetver.h b/windows/nsis-plugins/src/driverlogic/targetver.h
new file mode 100644
index 0000000000..ae4a5c032c
--- /dev/null
+++ b/windows/nsis-plugins/src/driverlogic/targetver.h
@@ -0,0 +1,12 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <WinSDKVer.h>
+
+#define _WIN32_WINNT _WIN32_WINNT_WIN7
+
+#include <SDKDDKVer.h>