summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOdd Stranne <odd@mullvad.net>2018-11-13 19:55:04 +0100
committerOdd Stranne <odd@mullvad.net>2018-11-14 15:20:55 +0100
commit4e70130ef0d684c276615a97fb7748dc4eb88e2b (patch)
treeb306967b573f2f864f03799dcb802c657df61f8d
parent8a6bae399969ebd57be39ba502630120983fc995 (diff)
downloadmullvadvpn-4e70130ef0d684c276615a97fb7748dc4eb88e2b.tar.xz
mullvadvpn-4e70130ef0d684c276615a97fb7748dc4eb88e2b.zip
Complement 'offline' module with code for Windows
-rw-r--r--Cargo.lock1
-rw-r--r--talpid-core/Cargo.toml1
-rw-r--r--talpid-core/src/offline/mod.rs6
-rw-r--r--talpid-core/src/offline/windows.rs190
-rw-r--r--talpid-core/src/tunnel_state_machine/mod.rs3
5 files changed, 199 insertions, 2 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5d127fb999..7e7c6ce73d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1705,6 +1705,7 @@ dependencies = [
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"which 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"widestring 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index 7dfe49219a..323c3026ab 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -44,6 +44,7 @@ system-configuration = "0.2"
[target.'cfg(windows)'.dependencies]
widestring = "0.3"
winreg = "0.5"
+winapi = { version = "0.3.6", features = ["handleapi", "libloaderapi", "synchapi", "winbase", "winuser"] }
[dev-dependencies]
tempfile = "3.0"
diff --git a/talpid-core/src/offline/mod.rs b/talpid-core/src/offline/mod.rs
index 696e7225b3..6cbbf04e9e 100644
--- a/talpid-core/src/offline/mod.rs
+++ b/talpid-core/src/offline/mod.rs
@@ -2,7 +2,11 @@
#[path = "macos.rs"]
mod imp;
-#[cfg(not(target_os = "macos"))]
+#[cfg(target_os = "windows")]
+#[path = "windows.rs"]
+mod imp;
+
+#[cfg(not(any(windows, target_os = "macos")))]
#[path = "dummy.rs"]
mod imp;
diff --git a/talpid-core/src/offline/windows.rs b/talpid-core/src/offline/windows.rs
new file mode 100644
index 0000000000..47d5fdda9d
--- /dev/null
+++ b/talpid-core/src/offline/windows.rs
@@ -0,0 +1,190 @@
+//! # License
+//!
+//! Copyright (C) 2018 Amagicom AB
+//!
+//! This program is free software: you can redistribute it and/or modify it under the terms of the
+//! GNU General Public License as published by the Free Software Foundation, either version 3 of
+//! the License, or (at your option) any later version.
+
+extern crate winapi;
+
+use self::winapi::shared::basetsd::LONG_PTR;
+use self::winapi::shared::minwindef::{DWORD, LPARAM, LRESULT, UINT, WPARAM};
+use self::winapi::shared::windef::HWND;
+use self::winapi::um::handleapi::CloseHandle;
+use self::winapi::um::libloaderapi::GetModuleHandleW;
+use self::winapi::um::processthreadsapi::GetThreadId;
+use self::winapi::um::synchapi::WaitForSingleObject;
+use self::winapi::um::winbase::INFINITE;
+use self::winapi::um::winuser::{
+ CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetMessageW,
+ GetWindowLongPtrW, PostQuitMessage, PostThreadMessageW, SetWindowLongPtrW, GWLP_USERDATA,
+ GWLP_WNDPROC, PBT_APMRESUMEAUTOMATIC, PBT_APMSUSPEND, WM_DESTROY, WM_POWERBROADCAST, WM_USER,
+};
+use futures::sync::mpsc::UnboundedSender;
+use log::debug;
+use std::ffi::c_void;
+use std::mem::zeroed;
+use std::os::windows::io::IntoRawHandle;
+use std::os::windows::io::RawHandle;
+use std::ptr;
+use std::thread;
+use std::time::Duration;
+use tunnel_state_machine::TunnelCommand;
+
+const CLASS_NAME: &[u8] = b"S\0T\0A\0T\0I\0C\0\0\0";
+const REQUEST_THREAD_SHUTDOWN: UINT = WM_USER + 1;
+
+error_chain!{
+ errors {
+ ThreadCreationError {
+ description("Unable to create listener thread")
+ }
+ }
+}
+
+pub struct BroadcastListener {
+ thread_handle: RawHandle,
+ thread_id: DWORD,
+}
+
+unsafe impl Send for BroadcastListener {}
+
+impl BroadcastListener {
+ pub fn start<F>(client_callback: F) -> Result<Self>
+ where
+ F: Fn(UINT, WPARAM, LPARAM) + 'static + Send,
+ {
+ let join_handle = thread::Builder::new()
+ .spawn(move || unsafe {
+ Self::message_pump(client_callback);
+ })
+ .chain_err(|| ErrorKind::ThreadCreationError)?;
+
+ let real_handle = join_handle.into_raw_handle();
+
+ Ok(BroadcastListener {
+ thread_handle: real_handle,
+ thread_id: unsafe { GetThreadId(real_handle) },
+ })
+ }
+
+ unsafe fn message_pump<F>(client_callback: F)
+ where
+ F: Fn(UINT, WPARAM, LPARAM),
+ {
+ let dummy_window = CreateWindowExW(
+ 0,
+ CLASS_NAME.as_ptr() as *const u16,
+ ptr::null_mut(),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ GetModuleHandleW(ptr::null_mut()),
+ ptr::null_mut(),
+ );
+
+ // Move callback information to the heap.
+ // This enables us to reach the callback through a "thin pointer".
+ let boxed_callback = Box::new(client_callback);
+
+ // Detach callback from Box.
+ let raw_callback = Box::into_raw(boxed_callback) as *mut c_void;
+
+ SetWindowLongPtrW(dummy_window, GWLP_USERDATA, raw_callback as LONG_PTR);
+ SetWindowLongPtrW(
+ dummy_window,
+ GWLP_WNDPROC,
+ Self::window_procedure::<F> as LONG_PTR,
+ );
+
+ let mut msg = zeroed();
+
+ loop {
+ let status = GetMessageW(&mut msg, 0 as HWND, 0, 0);
+
+ if status < 0 {
+ continue;
+ }
+ if status == 0 {
+ break;
+ }
+
+ if msg.hwnd.is_null() {
+ if msg.message == REQUEST_THREAD_SHUTDOWN {
+ DestroyWindow(dummy_window);
+ }
+ } else {
+ DispatchMessageW(&mut msg);
+ }
+ }
+
+ // Reattach callback to Box for proper clean-up.
+ let _ = Box::from_raw(raw_callback as *mut F);
+ }
+
+ unsafe extern "system" fn window_procedure<F>(
+ window: HWND,
+ message: UINT,
+ wparam: WPARAM,
+ lparam: LPARAM,
+ ) -> LRESULT
+ where
+ F: Fn(UINT, WPARAM, LPARAM),
+ {
+ let raw_callback = GetWindowLongPtrW(window, GWLP_USERDATA);
+
+ if raw_callback != 0 {
+ let typed_callback = &mut *(raw_callback as *mut F);
+ typed_callback(message, wparam, lparam);
+ }
+
+ if message == WM_DESTROY {
+ PostQuitMessage(0);
+ return 0;
+ }
+
+ DefWindowProcW(window, message, wparam, lparam)
+ }
+}
+
+impl Drop for BroadcastListener {
+ fn drop(&mut self) {
+ unsafe {
+ PostThreadMessageW(self.thread_id, REQUEST_THREAD_SHUTDOWN, 0, 0);
+ WaitForSingleObject(self.thread_handle, INFINITE);
+ CloseHandle(self.thread_handle);
+ }
+ }
+}
+
+pub fn spawn_monitor(sender: UnboundedSender<TunnelCommand>) -> Result<BroadcastListener> {
+ let listener =
+ BroadcastListener::start(move |message: UINT, wparam: WPARAM, _lparam: LPARAM| {
+ if message == WM_POWERBROADCAST {
+ if wparam == PBT_APMSUSPEND {
+ debug!("Machine is preparing to enter sleep mode");
+ let _ = sender.unbounded_send(TunnelCommand::IsOffline(true));
+ } else if wparam == PBT_APMRESUMEAUTOMATIC {
+ debug!("Machine is returning from sleep mode");
+ let cloned_sender = sender.clone();
+ thread::spawn(move || {
+ // TAP will be unavailable for approximately 2 seconds on a healthy machine.
+ thread::sleep(Duration::from_secs(2));
+ debug!("TAP is presumed to have been re-initialized");
+ let _ = cloned_sender.unbounded_send(TunnelCommand::IsOffline(false));
+ });
+ }
+ }
+ })?;
+
+ Ok(listener)
+}
+
+pub fn is_offline() -> bool {
+ false
+}
diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs
index d952e458f2..9f7f34b58b 100644
--- a/talpid-core/src/tunnel_state_machine/mod.rs
+++ b/talpid-core/src/tunnel_state_machine/mod.rs
@@ -55,7 +55,7 @@ where
T: From<TunnelStateTransition> + Send + 'static,
{
let (command_tx, command_rx) = mpsc::unbounded();
- offline::spawn_monitor(command_tx.clone())
+ let offline_monitor = offline::spawn_monitor(command_tx.clone())
.chain_err(|| "Unable to spawn offline state monitor")?;
let is_offline = offline::is_offline();
@@ -89,6 +89,7 @@ where
.expect("Failed to send startup error");
}
}
+ std::mem::drop(offline_monitor);
});
startup_result_rx