diff options
| author | Odd Stranne <odd@mullvad.net> | 2018-07-10 13:14:57 +0200 |
|---|---|---|
| committer | Odd Stranne <odd@mullvad.net> | 2018-07-10 13:14:57 +0200 |
| commit | a3013b89a520c3517476ea6544f912ae0c93e00c (patch) | |
| tree | 89d2677fb478c581daeb9be203cb5a481b827dc8 | |
| parent | 5b3a8ee27257f7347e371f92b13a868f266bca93 (diff) | |
| parent | 40fd243114fbe5a5df703936d179d86c8ba42749 (diff) | |
| download | mullvadvpn-a3013b89a520c3517476ea6544f912ae0c93e00c.tar.xz mullvadvpn-a3013b89a520c3517476ea6544f912ae0c93e00c.zip | |
Merge branch 'rename-tunnel-interface'
| -rw-r--r-- | CHANGELOG.md | 3 | ||||
| -rw-r--r-- | build_windows_modules.sh | 60 | ||||
| m--------- | dist-assets/binaries | 0 | ||||
| -rw-r--r-- | dist-assets/windows/installer.nsh | 81 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 13 | ||||
| -rw-r--r-- | talpid-core/src/process/openvpn.rs | 19 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 7 | ||||
| -rw-r--r-- | windows/nsis-plugins/nsis-plugins.sln | 33 | ||||
| -rw-r--r-- | windows/nsis-plugins/readme.txt | 22 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/context.cpp | 132 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/context.h | 51 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/dllmain.cpp | 11 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/driverlogic.cpp | 163 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/driverlogic.def | 6 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj | 124 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/driverlogic.vcxproj.filters | 17 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/stdafx.cpp | 8 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/stdafx.h | 16 | ||||
| -rw-r--r-- | windows/nsis-plugins/src/driverlogic/targetver.h | 12 |
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> |
