summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-10-13 14:40:53 +0200
committerDavid Lönnhager <david.l@mullvad.net>2021-07-02 09:54:19 +0200
commit9cc3585e99c9ba798e8b2dab983b3aad96450180 (patch)
tree6cdac39671831983649d3d49625682c9f8608830
parente5baa0e08816d535a031b3d8575701b8d43fb0c2 (diff)
downloadmullvadvpn-9cc3585e99c9ba798e8b2dab983b3aad96450180.tar.xz
mullvadvpn-9cc3585e99c9ba798e8b2dab983b3aad96450180.zip
Log error events from split tunnel driver
-rw-r--r--talpid-core/src/split_tunnel/windows/driver.rs199
-rw-r--r--talpid-core/src/split_tunnel/windows/mod.rs86
2 files changed, 265 insertions, 20 deletions
diff --git a/talpid-core/src/split_tunnel/windows/driver.rs b/talpid-core/src/split_tunnel/windows/driver.rs
index 26495a5877..093012224b 100644
--- a/talpid-core/src/split_tunnel/windows/driver.rs
+++ b/talpid-core/src/split_tunnel/windows/driver.rs
@@ -11,7 +11,7 @@ use std::{
mem::{self, size_of},
net::{Ipv4Addr, Ipv6Addr},
os::windows::{
- ffi::OsStrExt,
+ ffi::{OsStrExt, OsStringExt},
fs::OpenOptionsExt,
io::{AsRawHandle, RawHandle},
},
@@ -68,6 +68,43 @@ pub enum DriverState {
Terminating = 5,
}
+#[repr(u32)]
+#[derive(Clone, Copy)]
+#[allow(dead_code)]
+pub enum EventId {
+ StartSplittingProcess = 0,
+ StopSplittingProcess,
+
+ // ErrorFlag = 0x80000000,
+ ErrorStartSplittingProcess = 0x80000001,
+ ErrorStopSplittingProcess,
+}
+
+pub struct Event {
+ event_id: EventId,
+ body: EventBody,
+}
+
+pub enum EventBody {
+ SplittingEvent {
+ process_id: u32,
+ reason: SplittingChangeReason,
+ image: OsString,
+ },
+ SplittingError {
+ process_id: u32,
+ image: OsString,
+ },
+}
+
+#[repr(u32)]
+#[derive(Debug)]
+#[allow(dead_code)]
+pub enum SplittingChangeReason {
+ ByInheritance = 0,
+ ByConfig = 1,
+}
+
pub struct DeviceHandle {
handle: fs::File,
}
@@ -226,6 +263,40 @@ impl DeviceHandle {
Ok(())
}
+
+ pub fn deque_event(&self, buffer: &mut Vec<u8>) -> io::Result<(EventId, EventBody)> {
+ deque_event(self.handle.as_raw_handle(), buffer)
+ }
+}
+
+impl AsRawHandle for DeviceHandle {
+ fn as_raw_handle(&self) -> RawHandle {
+ self.handle.as_raw_handle()
+ }
+}
+
+pub fn deque_event(handle: RawHandle, buffer: &mut Vec<u8>) -> io::Result<(EventId, EventBody)> {
+ device_io_control_buffer(
+ handle,
+ DriverIoctlCode::DequeEvent as u32,
+ None,
+ Some(buffer),
+ )?;
+
+ let mut event_header: EventHeader = unsafe { mem::zeroed() };
+
+ unsafe {
+ ptr::copy_nonoverlapping(
+ &buffer[0],
+ &mut event_header as *mut _ as *mut u8,
+ mem::size_of_val(&event_header),
+ )
+ };
+
+ Ok((
+ event_header.event_id,
+ parse_event_buffer(&event_header, buffer),
+ ))
}
#[repr(C)]
@@ -440,6 +511,91 @@ fn serialize_process_tree(processes: Vec<ProcessInfo>) -> Result<Vec<u8>, io::Er
Ok(buffer)
}
+#[repr(C)]
+struct EventHeader {
+ event_id: EventId,
+ event_size: usize,
+}
+
+#[repr(C)]
+struct SplittingEventHeader {
+ process_id: u32,
+ reason: SplittingChangeReason,
+ image_name_length: u16,
+}
+
+#[repr(C)]
+struct SplittingErrorEventHeader {
+ process_id: u32,
+ image_name_length: u16,
+}
+
+fn parse_event_buffer(event_header: &EventHeader, buffer: &Vec<u8>) -> EventBody {
+ match event_header.event_id {
+ EventId::StartSplittingProcess | EventId::StopSplittingProcess => {
+ let mut event: SplittingEventHeader = unsafe { mem::zeroed() };
+ unsafe {
+ ptr::copy_nonoverlapping(
+ &buffer[mem::size_of_val(event_header)],
+ &mut event as *mut _ as *mut u8,
+ mem::size_of_val(&event),
+ )
+ };
+
+ let mut image_name = Vec::new();
+ image_name.resize(
+ event.image_name_length as usize / mem::size_of::<u16>(),
+ 0u16,
+ );
+
+ unsafe {
+ ptr::copy_nonoverlapping(
+ &buffer[mem::size_of_val(event_header) + mem::size_of_val(&event)] as *const _
+ as *const u16,
+ image_name.as_mut_ptr(),
+ image_name.len(),
+ )
+ };
+
+ EventBody::SplittingEvent {
+ process_id: event.process_id,
+ reason: event.reason,
+ image: OsStringExt::from_wide(&image_name),
+ }
+ }
+ EventId::ErrorStartSplittingProcess | EventId::ErrorStopSplittingProcess => {
+ let mut event: SplittingErrorEventHeader = unsafe { mem::zeroed() };
+ unsafe {
+ ptr::copy_nonoverlapping(
+ &buffer[mem::size_of_val(event_header)],
+ &mut event as *mut _ as *mut u8,
+ mem::size_of_val(&event),
+ )
+ };
+
+ let mut image_name = Vec::new();
+ image_name.resize(
+ event.image_name_length as usize / mem::size_of::<u16>(),
+ 0u16,
+ );
+
+ unsafe {
+ ptr::copy_nonoverlapping(
+ &buffer[mem::size_of_val(event_header) + mem::size_of_val(&event)] as *const _
+ as *const u16,
+ image_name.as_mut_ptr(),
+ image_name.len(),
+ )
+ };
+
+ EventBody::SplittingError {
+ process_id: event.process_id,
+ image: OsStringExt::from_wide(&image_name),
+ }
+ }
+ }
+}
+
/// Send an IOCTL code to the given device handle.
/// `input` specifies an optional buffer to send.
/// Upon success, a buffer of size `output_size` is returned, or None if `output_size` is 0.
@@ -449,22 +605,39 @@ pub fn device_io_control(
input: Option<&[u8]>,
output_size: u32,
) -> Result<Option<Vec<u8>>, io::Error> {
- let input_ptr = match input {
- Some(input) => input as *const _ as *mut _,
- None => ptr::null_mut(),
- };
- let input_len = input.map(|input| input.len()).unwrap_or(0);
-
let mut out_buffer = if output_size > 0 {
Some(Vec::with_capacity(output_size as usize))
} else {
None
};
- let out_ptr = match out_buffer {
- Some(ref mut out_buffer) => out_buffer.as_mut_ptr() as *mut _,
+ device_io_control_buffer(device, ioctl_code, input, out_buffer.as_mut()).map(|()| out_buffer)
+}
+
+/// Send an IOCTL code to the given device handle.
+/// `input` specifies an optional buffer to send.
+/// Upon success, `output` buffer will contain at most `output.capacity()` bytes of data.
+pub fn device_io_control_buffer(
+ device: RawHandle,
+ ioctl_code: u32,
+ input: Option<&[u8]>,
+ mut output: Option<&mut Vec<u8>>,
+) -> Result<(), io::Error> {
+ let input_ptr = match input {
+ Some(input) => input as *const _ as *mut _,
None => ptr::null_mut(),
};
+ let input_len = input.map(|input| input.len()).unwrap_or(0);
+
+ let out_ptr = match output {
+ Some(ref mut output) => output.as_mut_ptr() as *mut _,
+ None => ptr::null_mut(),
+ };
+ let output_size = if let Some(ref output) = output {
+ output.capacity()
+ } else {
+ 0
+ };
let mut returned_bytes = 0u32;
@@ -475,18 +648,18 @@ pub fn device_io_control(
input_ptr,
input_len as u32,
out_ptr,
- output_size,
+ output_size as u32,
&mut returned_bytes as *mut _,
ptr::null_mut(), // TODO
)
};
- if let Some(ref mut out_buffer) = out_buffer {
- unsafe { out_buffer.set_len(returned_bytes as usize) };
+ if let Some(ref mut output) = output {
+ unsafe { output.set_len(returned_bytes as usize) };
}
if result != 0 {
- Ok(out_buffer)
+ Ok(())
} else {
Err(io::Error::last_os_error())
}
diff --git a/talpid-core/src/split_tunnel/windows/mod.rs b/talpid-core/src/split_tunnel/windows/mod.rs
index c6b8fae332..857a20435d 100644
--- a/talpid-core/src/split_tunnel/windows/mod.rs
+++ b/talpid-core/src/split_tunnel/windows/mod.rs
@@ -5,8 +5,15 @@ use std::{
ffi::OsStr,
io,
net::{Ipv4Addr, Ipv6Addr},
+ os::windows::{
+ io::{AsRawHandle, IntoRawHandle, RawHandle},
+ thread,
+ },
};
use talpid_types::ErrorExt;
+use winapi::um::processthreadsapi::TerminateThread;
+
+const DRIVER_EVENT_BUFFER_SIZE: usize = 2048;
/// Errors that may occur in [`SplitTunnel`].
#[derive(err_derive::Error, Debug)]
@@ -26,22 +33,80 @@ pub enum Error {
}
/// Manages applications whose traffic to exclude from the tunnel.
-pub struct SplitTunnel(driver::DeviceHandle);
+pub struct SplitTunnel {
+ handle: driver::DeviceHandle,
+ event_thread: Option<std::thread::JoinHandle<()>>,
+}
+
+struct HandleContainer {
+ handle: RawHandle,
+}
+// FIXME: ! This is not safe. The handle will be invalidated when SplitTunnel is dropped
+unsafe impl Send for HandleContainer {}
impl SplitTunnel {
/// Initialize the driver.
pub fn new() -> Result<Self, Error> {
- Ok(SplitTunnel(
- driver::DeviceHandle::new().map_err(Error::InitializationFailed)?,
- ))
+ // TODO: spawn event monitor
+ let handle = driver::DeviceHandle::new().map_err(Error::InitializationFailed)?;
+
+ // FIXME: Want to use same pointer, but must be certain that the thread dies after this dies
+
+ let raw_handle = HandleContainer {
+ handle: handle.as_raw_handle(),
+ };
+
+ let event_thread = std::thread::spawn(move || {
+ use driver::{EventBody, EventId};
+
+ let mut data_buffer = Vec::with_capacity(DRIVER_EVENT_BUFFER_SIZE);
+
+ loop {
+ match driver::deque_event(raw_handle.handle, &mut data_buffer) {
+ Ok((event_id, event_body)) => {
+ let event_str = match &event_id {
+ EventId::StartSplittingProcess
+ | EventId::ErrorStartSplittingProcess => "Start splitting process",
+ EventId::StopSplittingProcess | EventId::ErrorStopSplittingProcess => {
+ "Stop splitting process"
+ }
+ };
+
+ match event_body {
+ EventBody::SplittingError { process_id, image } => {
+ log::error!(
+ "FAILED: {}:\n\tpid: {}\n\timage: {:?}",
+ event_str,
+ process_id,
+ image,
+ );
+ }
+ _ => (),
+ }
+ }
+ Err(error) => {
+ log::error!("{}", error.display_chain_with_msg("deque_event failed"));
+ }
+ }
+
+ // TODO: Quit when signaled. Overlapping + WaitForMultipleObjects?
+ }
+ });
+
+ Ok(SplitTunnel {
+ handle,
+ event_thread: Some(event_thread),
+ })
}
/// Set a list of applications to exclude from the tunnel.
pub fn set_paths<T: AsRef<OsStr>>(&self, paths: &[T]) -> Result<(), Error> {
if paths.len() > 0 {
- self.0.set_config(paths).map_err(Error::SetConfiguration)
+ self.handle
+ .set_config(paths)
+ .map_err(Error::SetConfiguration)
} else {
- self.0.clear_config().map_err(Error::SetConfiguration)
+ self.handle.clear_config().map_err(Error::SetConfiguration)
}
}
@@ -53,7 +118,7 @@ impl SplitTunnel {
internet_ipv4: Ipv4Addr,
internet_ipv6: Option<Ipv6Addr>,
) -> Result<(), Error> {
- self.0
+ self.handle
.register_ips(tunnel_ipv4, tunnel_ipv6, internet_ipv4, internet_ipv6)
.map_err(Error::RegisterIps)
}
@@ -61,6 +126,13 @@ impl SplitTunnel {
impl Drop for SplitTunnel {
fn drop(&mut self) {
+ // FIXME: Use signals to close the thread gracefully, followed by a join
+ if let Some(event_thread) = self.event_thread.take() {
+ unsafe {
+ TerminateThread(event_thread.into_raw_handle(), 0);
+ }
+ }
+
let paths: [&OsStr; 0] = [];
if let Err(error) = self.set_paths(&paths) {
log::error!("{}", error.display_chain());