diff --git a/Cargo.lock b/Cargo.lock index c929ce575478..63031c21c64d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3456,6 +3456,7 @@ dependencies = [ "talpid-tunnel", "talpid-tunnel-config-client", "talpid-types", + "talpid-windows", "talpid-windows-net", "talpid-wireguard", "tokio", @@ -3621,6 +3622,13 @@ dependencies = [ "zeroize", ] +[[package]] +name = "talpid-windows" +version = "0.0.0" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "talpid-windows-net" version = "0.0.0" diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index e39b07f24462..40dae6b27b7e 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -61,6 +61,7 @@ winreg = { version = "0.51", features = ["transactions"] } memoffset = "0.6" windows-service = "0.6.0" talpid-windows-net = { path = "../talpid-windows-net" } +talpid-windows = { path = "../talpid-windows" } [target.'cfg(windows)'.dependencies.windows-sys] workspace = true diff --git a/talpid-core/src/split_tunnel/windows/driver.rs b/talpid-core/src/split_tunnel/windows/driver.rs index c8688345189a..eea79f188f13 100644 --- a/talpid-core/src/split_tunnel/windows/driver.rs +++ b/talpid-core/src/split_tunnel/windows/driver.rs @@ -1,6 +1,6 @@ use super::windows::{ - get_device_path, get_process_creation_time, get_process_device_path, open_process, Event, - Overlapped, ProcessAccess, ProcessSnapshot, + get_device_path, get_process_creation_time, get_process_device_path, open_process, + ProcessAccess, ProcessSnapshot, }; use bitflags::bitflags; use memoffset::offset_of; @@ -22,6 +22,7 @@ use std::{ time::Duration, }; use talpid_types::ErrorExt; +use talpid_windows::{io::Overlapped, sync::Event}; use windows_sys::Win32::{ Foundation::{ ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND, ERROR_INVALID_PARAMETER, ERROR_IO_PENDING, @@ -877,7 +878,7 @@ pub fn get_overlapped_result( let event = overlapped.get_event().unwrap(); // SAFETY: This is a valid event object. - unsafe { wait_for_single_object(event.as_handle(), None) }?; + unsafe { wait_for_single_object(event.as_raw(), None) }?; // SAFETY: The handle and overlapped object are valid. let mut returned_bytes = 0u32; diff --git a/talpid-core/src/split_tunnel/windows/mod.rs b/talpid-core/src/split_tunnel/windows/mod.rs index 40d7c340ad3c..dcabda7c8e68 100644 --- a/talpid-core/src/split_tunnel/windows/mod.rs +++ b/talpid-core/src/split_tunnel/windows/mod.rs @@ -105,7 +105,7 @@ pub struct SplitTunnel { runtime: tokio::runtime::Handle, request_tx: RequestTx, event_thread: Option>, - quit_event: Arc, + quit_event: Arc, excluded_processes: Arc>>, _route_change_callback: Option, daemon_tx: Weak>, @@ -191,14 +191,21 @@ impl SplitTunnel { fn spawn_event_listener( handle: Arc, excluded_processes: Arc>>, - ) -> Result<(std::thread::JoinHandle<()>, Arc), Error> { - let mut event_overlapped = windows::Overlapped::new(Some( - windows::Event::new(true, false).map_err(Error::EventThreadError)?, + ) -> Result< + ( + std::thread::JoinHandle<()>, + Arc, + ), + Error, + > { + let mut event_overlapped = talpid_windows::io::Overlapped::new(Some( + talpid_windows::sync::Event::new(true, false).map_err(Error::EventThreadError)?, )) .map_err(Error::EventThreadError)?; - let quit_event = - Arc::new(windows::Event::new(true, false).map_err(Error::EventThreadError)?); + let quit_event = Arc::new( + talpid_windows::sync::Event::new(true, false).map_err(Error::EventThreadError)?, + ); let quit_event_copy = quit_event.clone(); let event_thread = std::thread::spawn(move || { @@ -237,11 +244,11 @@ impl SplitTunnel { fn fetch_next_event( device: &Arc, - quit_event: &windows::Event, - overlapped: &mut windows::Overlapped, + quit_event: &talpid_windows::sync::Event, + overlapped: &mut talpid_windows::io::Overlapped, data_buffer: &mut Vec, ) -> io::Result { - if unsafe { driver::wait_for_single_object(quit_event.as_handle(), Some(Duration::ZERO)) } + if unsafe { driver::wait_for_single_object(quit_event.as_raw(), Some(Duration::ZERO)) } .is_ok() { return Ok(EventResult::Quit); @@ -268,8 +275,8 @@ impl SplitTunnel { })?; let event_objects = [ - overlapped.get_event().unwrap().as_handle(), - quit_event.as_handle(), + overlapped.get_event().unwrap().as_raw(), + quit_event.as_raw(), ]; let signaled_object = @@ -283,7 +290,7 @@ impl SplitTunnel { }, )?; - if signaled_object == quit_event.as_handle() { + if signaled_object == quit_event.as_raw() { // Quit event was signaled return Ok(EventResult::Quit); } diff --git a/talpid-core/src/split_tunnel/windows/windows.rs b/talpid-core/src/split_tunnel/windows/windows.rs index 77fafdc199bd..423426c38590 100644 --- a/talpid-core/src/split_tunnel/windows/windows.rs +++ b/talpid-core/src/split_tunnel/windows/windows.rs @@ -13,7 +13,7 @@ use std::{ }; use windows_sys::Win32::{ Foundation::{ - CloseHandle, BOOL, ERROR_INSUFFICIENT_BUFFER, ERROR_NO_MORE_FILES, FILETIME, HANDLE, + CloseHandle, ERROR_INSUFFICIENT_BUFFER, ERROR_NO_MORE_FILES, FILETIME, HANDLE, INVALID_HANDLE_VALUE, }, Storage::FileSystem::{GetFinalPathNameByHandleW, QueryDosDeviceW}, @@ -22,11 +22,8 @@ use windows_sys::Win32::{ CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, }, ProcessStatus::GetProcessImageFileNameW, - Threading::{ - CreateEventW, GetProcessTimes, OpenProcess, SetEvent, PROCESS_QUERY_LIMITED_INFORMATION, - }, + Threading::{GetProcessTimes, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION}, WindowsProgramming::VOLUME_NAME_NT, - IO::OVERLAPPED, }, }; @@ -299,95 +296,3 @@ fn get_process_device_path_inner( Ok(OsStringExt::from_wide(&buffer)) } - -/// Abstraction over `OVERLAPPED`, which is used for async I/O. -pub struct Overlapped { - overlapped: OVERLAPPED, - event: Option, -} - -unsafe impl Send for Overlapped {} -unsafe impl Sync for Overlapped {} - -impl Overlapped { - /// Creates an `OVERLAPPED` object with `hEvent` set. - pub fn new(event: Option) -> io::Result { - let mut overlapped = Overlapped { - overlapped: unsafe { mem::zeroed() }, - event: None, - }; - overlapped.set_event(event); - Ok(overlapped) - } - - /// Borrows the underlying `OVERLAPPED` object. - pub fn as_mut_ptr(&mut self) -> *mut OVERLAPPED { - &mut self.overlapped - } - - /// Returns a reference to the associated event. - pub fn get_event(&self) -> Option<&Event> { - self.event.as_ref() - } - - /// Sets the event object for the underlying `OVERLAPPED` object (i.e., `hEvent`) - fn set_event(&mut self, event: Option) { - match event { - Some(event) => { - self.overlapped.hEvent = event.0; - self.event = Some(event); - } - None => { - self.overlapped.hEvent = 0; - self.event = None; - } - } - } -} - -/// Abstraction over a Windows event object. -pub struct Event(HANDLE); - -unsafe impl Send for Event {} -unsafe impl Sync for Event {} - -impl Event { - pub fn new(manual_reset: bool, initial_state: bool) -> io::Result { - let event = unsafe { - CreateEventW( - ptr::null_mut(), - bool_to_winbool(manual_reset), - bool_to_winbool(initial_state), - ptr::null(), - ) - }; - if event == 0 { - return Err(io::Error::last_os_error()); - } - Ok(Self(event)) - } - - pub fn set(&self) -> io::Result<()> { - if unsafe { SetEvent(self.0) } == 0 { - return Err(io::Error::last_os_error()); - } - Ok(()) - } - - pub fn as_handle(&self) -> HANDLE { - self.0 - } -} - -impl Drop for Event { - fn drop(&mut self) { - unsafe { CloseHandle(self.0) }; - } -} - -const fn bool_to_winbool(val: bool) -> BOOL { - match val { - true => 1, - false => 0, - } -} diff --git a/talpid-windows/Cargo.toml b/talpid-windows/Cargo.toml new file mode 100644 index 000000000000..20a4d1ae3e4b --- /dev/null +++ b/talpid-windows/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "talpid-windows" +description = "Nice abstractions for Windows" +version.workspace = true +authors.workspace = true +repository.workspace = true +license.workspace = true +edition.workspace = true +publish.workspace = true + +[target.'cfg(windows)'.dependencies.windows-sys] +workspace = true +features = [ + "Win32_Foundation", + "Win32_Globalization", + "Win32_System_Com", + "Win32_System_IO", + "Win32_Networking_WinSock", + "Win32_NetworkManagement_IpHelper", + "Win32_NetworkManagement_Ndis", +] diff --git a/talpid-windows/src/io.rs b/talpid-windows/src/io.rs new file mode 100644 index 000000000000..1bcffb30d345 --- /dev/null +++ b/talpid-windows/src/io.rs @@ -0,0 +1,49 @@ +use std::{io, mem}; +use windows_sys::Win32::System::IO::OVERLAPPED; + +use crate::sync::Event; + +/// Abstraction over `OVERLAPPED`. +pub struct Overlapped { + overlapped: OVERLAPPED, + event: Option, +} + +unsafe impl Send for Overlapped {} +unsafe impl Sync for Overlapped {} + +impl Overlapped { + /// Creates an `OVERLAPPED` object with `hEvent` set. + pub fn new(event: Option) -> io::Result { + let mut overlapped = Overlapped { + overlapped: unsafe { mem::zeroed() }, + event: None, + }; + overlapped.set_event(event); + Ok(overlapped) + } + + /// Borrows the underlying `OVERLAPPED` object. + pub fn as_mut_ptr(&mut self) -> *mut OVERLAPPED { + &mut self.overlapped + } + + /// Returns a reference to the associated event. + pub fn get_event(&self) -> Option<&Event> { + self.event.as_ref() + } + + /// Sets the event object for the underlying `OVERLAPPED` object (i.e., `hEvent`) + fn set_event(&mut self, event: Option) { + match event { + Some(event) => { + self.overlapped.hEvent = event.as_raw(); + self.event = Some(event); + } + None => { + self.overlapped.hEvent = 0; + self.event = None; + } + } + } +} diff --git a/talpid-windows/src/lib.rs b/talpid-windows/src/lib.rs new file mode 100644 index 000000000000..edc471aa3353 --- /dev/null +++ b/talpid-windows/src/lib.rs @@ -0,0 +1,12 @@ +//! Interface with low-level windows specific bits. + +#![deny(missing_docs)] +#![deny(rust_2018_idioms)] + +/// Windows I/O +#[cfg(windows)] +pub mod io; + +/// Synchronization (event objects, etc.) +#[cfg(windows)] +pub mod sync; diff --git a/talpid-windows/src/sync.rs b/talpid-windows/src/sync.rs new file mode 100644 index 000000000000..7b4ed59be3d6 --- /dev/null +++ b/talpid-windows/src/sync.rs @@ -0,0 +1,75 @@ +use std::{io, ptr}; +use windows_sys::Win32::{ + Foundation::{CloseHandle, DuplicateHandle, BOOL, DUPLICATE_SAME_ACCESS, HANDLE}, + System::Threading::{CreateEventW, GetCurrentProcess, SetEvent}, +}; + +/// Windows event object +pub struct Event(HANDLE); + +unsafe impl Send for Event {} +unsafe impl Sync for Event {} + +impl Event { + /// Create a new event object using `CreateEventW` + pub fn new(manual_reset: bool, initial_state: bool) -> io::Result { + let event = unsafe { + CreateEventW( + ptr::null_mut(), + bool_to_winbool(manual_reset), + bool_to_winbool(initial_state), + ptr::null(), + ) + }; + if event == 0 { + return Err(io::Error::last_os_error()); + } + Ok(Self(event)) + } + + /// Signal the event object + pub fn set(&self) -> io::Result<()> { + if unsafe { SetEvent(self.0) } == 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + } + + /// Return raw event object + pub fn as_raw(&self) -> HANDLE { + self.0 + } + + /// Duplicate the event object with `DuplicateHandle()` + pub fn duplicate(&self) -> io::Result { + let mut new_event = 0; + let status = unsafe { + DuplicateHandle( + GetCurrentProcess(), + self.0, + GetCurrentProcess(), + &mut new_event, + 0, + 0, + DUPLICATE_SAME_ACCESS, + ) + }; + if status == 0 { + return Err(io::Error::last_os_error()); + } + Ok(Event(new_event)) + } +} + +impl Drop for Event { + fn drop(&mut self) { + unsafe { CloseHandle(self.0) }; + } +} + +const fn bool_to_winbool(val: bool) -> BOOL { + match val { + true => 1, + false => 0, + } +}