diff options
| author | David Lönnhager <david.l@mullvad.net> | 2020-11-30 18:29:14 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2020-12-03 11:31:35 +0100 |
| commit | 3c896f3b98fa740fcba6b9ae1409395cf3f4a4bb (patch) | |
| tree | 04ac3681deae3d5f8aa3460636f2de9797972ad8 | |
| parent | 1dc755dc3e1a809798a565619f9dcfbc4a67fc46 (diff) | |
| download | mullvadvpn-3c896f3b98fa740fcba6b9ae1409395cf3f4a4bb.tar.xz mullvadvpn-3c896f3b98fa740fcba6b9ae1409395cf3f4a4bb.zip | |
Create OpenVPN Wintun adapter on the fly
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 13 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/openvpn/mod.rs | 146 |
2 files changed, 132 insertions, 27 deletions
diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 3e27b792c8..1dca634f00 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -383,18 +383,7 @@ fn try_enabling_ipv6(tunnel_parameters: &TunnelParameters) -> Result<()> { return Err(Error::EnableIpv6Error); } - let guid_string: String; - - let guid = match tunnel_parameters { - TunnelParameters::OpenVpn(..) => { - let alias = crate::winnet::get_interface_alias().map_err(Error::WinnetError)?; - guid_string = - crate::winnet::interface_alias_to_guid(&alias).map_err(Error::WinnetError)?; - &guid_string - } - TunnelParameters::Wireguard(..) => "{AFE43773-E1F8-4EBB-8536-576AB86AFE9A}", - }; - + let guid = "{AFE43773-E1F8-4EBB-8536-576AB86AFE9A}"; crate::winnet::enable_ipv6_for_adapter(&guid).map_err(Error::WinnetError) } diff --git a/talpid-core/src/tunnel/openvpn/mod.rs b/talpid-core/src/tunnel/openvpn/mod.rs index fbcd4db391..3ab8e8dc30 100644 --- a/talpid-core/src/tunnel/openvpn/mod.rs +++ b/talpid-core/src/tunnel/openvpn/mod.rs @@ -35,6 +35,10 @@ use talpid_types::ErrorExt; use tokio::task; #[cfg(target_os = "linux")] use which; +#[cfg(windows)] +use widestring::U16CString; +#[cfg(windows)] +use winapi::shared::{guiddef::GUID, winerror::ERROR_FILE_NOT_FOUND}; #[cfg(windows)] mod windows; @@ -42,8 +46,21 @@ mod windows; lazy_static! { static ref ENV_ROUTE_ENTRY: Regex = Regex::new(r"route_(ipv6_)?(\w+)_(\d+)").unwrap(); + + #[cfg(windows)] + static ref ADAPTER_ALIAS: U16CString = U16CString::from_str("Mullvad").unwrap(); + #[cfg(windows)] + static ref ADAPTER_POOL: U16CString = U16CString::from_str("Mullvad").unwrap(); } +#[cfg(windows)] +const ADAPTER_GUID: GUID = GUID { + Data1: 0xAFE43773, + Data2: 0xE1F8, + Data3: 0x4EBB, + Data4: [0x85, 0x36, 0x57, 0x6A, 0xB8, 0x6A, 0xFE, 0x9A], +}; + /// Results from fallible operations on the OpenVPN tunnel. pub type Result<T> = std::result::Result<T, Error>; @@ -83,6 +100,21 @@ pub enum Error { #[error(display = "The virtual adapter appears to be disabled")] DisabledVirtualAdapter, + /// cannot load wintun.dll + #[cfg(windows)] + #[error(display = "Failed to load wintun.dll")] + WintunDllError(#[error(source)] io::Error), + + /// cannot create a wintun interface + #[cfg(windows)] + #[error(display = "Failed to create Wintun adapter")] + WintunError(#[error(source)] io::Error), + + /// cannot create a wintun interface + #[cfg(windows)] + #[error(display = "Failed to delete existing Wintun adapter")] + WintunDeleteExistingError(#[error(source)] io::Error), + /// OpenVPN process died unexpectedly #[error(display = "OpenVPN process died unexpectedly")] ChildProcessDied, @@ -179,6 +211,9 @@ pub struct OpenVpnMonitor<C: OpenVpnBuilder = OpenVpnCommand> { runtime: tokio::runtime::Runtime, event_server_abort_tx: triggered::Trigger, server_join_handle: Option<task::JoinHandle<std::result::Result<(), event_server::Error>>>, + + #[cfg(windows)] + wintun_adapter: Option<windows::TemporaryWintunAdapter>, } @@ -273,6 +308,42 @@ impl OpenVpnMonitor<OpenVpnCommand> { let plugin_path = Self::get_plugin_path(resource_dir)?; + #[cfg(windows)] + let wintun_adapter = { + let dll = + Arc::new(windows::WintunDll::new(resource_dir).map_err(Error::WintunDllError)?); + + { + match windows::WintunAdapter::open(dll.clone(), &*ADAPTER_ALIAS, &*ADAPTER_POOL) { + Ok(adapter) => { + // Delete existing adapter in case it has residual config + adapter + .delete(false) + .map_err(Error::WintunDeleteExistingError)?; + } + Err(error) => { + if error.raw_os_error() != Some(ERROR_FILE_NOT_FOUND as i32) { + return Err(Error::WintunError(error)); + } + } + } + } + + let (adapter, reboot_required) = windows::TemporaryWintunAdapter::create( + dll.clone(), + &*ADAPTER_ALIAS, + &*ADAPTER_POOL, + Some(ADAPTER_GUID.clone()), + ) + .map_err(Error::WintunError)?; + + if reboot_required { + log::warn!("You may need to restart Windows to complete the install of Wintun"); + } + + adapter + }; + Self::new_internal( cmd, on_openvpn_event, @@ -281,6 +352,8 @@ impl OpenVpnMonitor<OpenVpnCommand> { user_pass_file, proxy_auth_file, proxy_monitor, + #[cfg(windows)] + Some(wintun_adapter), ) } } @@ -333,6 +406,7 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { user_pass_file: mktemp::TempFile, proxy_auth_file: Option<mktemp::TempFile>, proxy_monitor: Option<Box<dyn ProxyMonitor>>, + #[cfg(windows)] wintun_adapter: Option<windows::TemporaryWintunAdapter>, ) -> Result<OpenVpnMonitor<C>> where L: Fn(openvpn_plugin::EventType, HashMap<String, String>) + Send + Sync + 'static, @@ -386,6 +460,9 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { runtime, event_server_abort_tx, server_join_handle: Some(server_join_handle), + + #[cfg(windows)] + wintun_adapter, }) } @@ -604,9 +681,8 @@ impl<C: OpenVpnBuilder + 'static> OpenVpnMonitor<C> { .ca(resource_dir.join("ca.crt")); #[cfg(windows)] { - cmd.tunnel_alias(Some( - crate::winnet::get_interface_alias().map_err(Error::WinnetError)?, - )); + use std::ffi::OsString; + cmd.tunnel_alias(Some(OsString::from("Mullvad"))); cmd.windows_driver(Some(crate::process::openvpn::WindowsDriver::Wintun)); } if let Some(proxy_settings) = params.proxy.clone().take() { @@ -913,6 +989,8 @@ mod tests { TempFile::new(), None, None, + #[cfg(windows)] + None, ); assert_eq!( Some(PathBuf::from("./my_test_plugin")), @@ -931,6 +1009,8 @@ mod tests { TempFile::new(), None, None, + #[cfg(windows)] + None, ); assert_eq!( Some(PathBuf::from("./my_test_log_file")), @@ -942,9 +1022,18 @@ mod tests { fn exit_successfully() { let mut builder = TestOpenVpnBuilder::default(); builder.process_handle = Some(TestProcessHandle(0)); - let testee = - OpenVpnMonitor::new_internal(builder, |_, _| {}, "", None, TempFile::new(), None, None) - .unwrap(); + let testee = OpenVpnMonitor::new_internal( + builder, + |_, _| {}, + "", + None, + TempFile::new(), + None, + None, + #[cfg(windows)] + None, + ) + .unwrap(); assert!(testee.wait().is_ok()); } @@ -952,9 +1041,18 @@ mod tests { fn exit_error() { let mut builder = TestOpenVpnBuilder::default(); builder.process_handle = Some(TestProcessHandle(1)); - let testee = - OpenVpnMonitor::new_internal(builder, |_, _| {}, "", None, TempFile::new(), None, None) - .unwrap(); + let testee = OpenVpnMonitor::new_internal( + builder, + |_, _| {}, + "", + None, + TempFile::new(), + None, + None, + #[cfg(windows)] + None, + ) + .unwrap(); assert!(testee.wait().is_err()); } @@ -962,9 +1060,18 @@ mod tests { fn wait_closed() { let mut builder = TestOpenVpnBuilder::default(); builder.process_handle = Some(TestProcessHandle(1)); - let testee = - OpenVpnMonitor::new_internal(builder, |_, _| {}, "", None, TempFile::new(), None, None) - .unwrap(); + let testee = OpenVpnMonitor::new_internal( + builder, + |_, _| {}, + "", + None, + TempFile::new(), + None, + None, + #[cfg(windows)] + None, + ) + .unwrap(); testee.close_handle().close().unwrap(); assert!(testee.wait().is_ok()); } @@ -972,9 +1079,18 @@ mod tests { #[test] fn failed_process_start() { let builder = TestOpenVpnBuilder::default(); - let error = - OpenVpnMonitor::new_internal(builder, |_, _| {}, "", None, TempFile::new(), None, None) - .unwrap_err(); + let error = OpenVpnMonitor::new_internal( + builder, + |_, _| {}, + "", + None, + TempFile::new(), + None, + None, + #[cfg(windows)] + None, + ) + .unwrap_err(); match error { Error::ChildProcessError(..) => (), _ => panic!("Wrong error"), |
