summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-11-30 18:29:14 +0100
committerDavid Lönnhager <david.l@mullvad.net>2020-12-03 11:31:35 +0100
commit3c896f3b98fa740fcba6b9ae1409395cf3f4a4bb (patch)
tree04ac3681deae3d5f8aa3460636f2de9797972ad8
parent1dc755dc3e1a809798a565619f9dcfbc4a67fc46 (diff)
downloadmullvadvpn-3c896f3b98fa740fcba6b9ae1409395cf3f4a4bb.tar.xz
mullvadvpn-3c896f3b98fa740fcba6b9ae1409395cf3f4a4bb.zip
Create OpenVPN Wintun adapter on the fly
-rw-r--r--talpid-core/src/tunnel/mod.rs13
-rw-r--r--talpid-core/src/tunnel/openvpn/mod.rs146
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"),