diff options
| -rw-r--r-- | Cargo.lock | 111 | ||||
| -rw-r--r-- | Cargo.toml | 5 | ||||
| -rw-r--r-- | build.ps1 | 4 | ||||
| -rw-r--r-- | crates/windivert/Cargo.toml | 63 | ||||
| -rw-r--r-- | crates/windivert/README.md | 50 | ||||
| -rw-r--r-- | crates/windivert/src/address.rs | 421 | ||||
| -rw-r--r-- | crates/windivert/src/divert/blocking.rs | 353 | ||||
| -rw-r--r-- | crates/windivert/src/divert/mod.rs | 229 | ||||
| -rw-r--r-- | crates/windivert/src/error.rs | 129 | ||||
| -rw-r--r-- | crates/windivert/src/layer.rs | 46 | ||||
| -rw-r--r-- | crates/windivert/src/lib.rs | 28 | ||||
| -rw-r--r-- | crates/windivert/src/packet.rs | 90 | ||||
| -rw-r--r-- | src/main.rs | 8 | ||||
| -rw-r--r-- | src/platform.rs | 6 | ||||
| -rw-r--r-- | src/platform/windows.rs | 173 |
15 files changed, 1681 insertions, 35 deletions
@@ -224,6 +224,7 @@ dependencies = [ "socket2", "thiserror 2.0.18", "windivert", + "windows 0.62.2", "windows-services", "winres", ] @@ -729,13 +730,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windivert" version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6b6833a760d1c36b489314a5541a12a39d162dc8341d8f6f400212b96d3df1" dependencies = [ "etherparse 0.13.0", "thiserror 1.0.69", "windivert-sys", - "windows", + "windows 0.48.0", ] [[package]] @@ -746,7 +745,7 @@ checksum = "832bc4af9272458a8a64395b3aabe10dc4089546486fcbd0e19b9b6d28ba6e54" dependencies = [ "cc", "thiserror 1.0.69", - "windows", + "windows 0.48.0", ] [[package]] @@ -759,12 +758,98 @@ dependencies = [ ] [[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] name = "windows-services" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -774,6 +859,15 @@ dependencies = [ ] [[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -798,6 +892,15 @@ dependencies = [ ] [[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -28,6 +28,8 @@ repository = "https://github.com/dilluti0n/dpibreak.git" homepage = "https://github.com/dilluti0n/dpibreak" readme = "README.md" +[workspace] +members = ["crates/windivert/"] [dependencies] anyhow = "1" @@ -41,8 +43,9 @@ libc = "0.2" thiserror = "2.0.18" [target.'cfg(windows)'.dependencies] -windivert = { version = "0.6.0" } +windivert = { path = "crates/windivert" } windows-services = "0.26" +windows = { version = "0.62", features = ["Win32", "Win32_System", "Win32_System_Services"] } [build-dependencies] winres = "0.1" @@ -43,7 +43,9 @@ $ReleaseDir = Join-Path $TargetDir $BuildTarget "release" # --- Distribution Variables --- # Get version info from cargo metadata. No jq needed. try { - $ProjectVersion = (cargo metadata --format-version=1 --no-deps | ConvertFrom-Json).packages[0].version + $ProjectVersion = (cargo metadata --format-version=1 --no-deps | ConvertFrom-Json).packages ` + | Where-Object { $_.name -eq 'dpibreak' } ` + | Select-Object -ExpandProperty version } catch { Write-Error "Failed to run cargo metadata. Ensure Rust and Cargo are installed and in your PATH." exit 1 diff --git a/crates/windivert/Cargo.toml b/crates/windivert/Cargo.toml new file mode 100644 index 0000000..a5c5e1f --- /dev/null +++ b/crates/windivert/Cargo.toml @@ -0,0 +1,63 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "windivert" +version = "0.6.0" +authors = ["Ruben Serrano Izquierdo <[email protected]>"] +description = "Wrapper library around windivert-sys" +homepage = "https://github.com/Rubensei/windivert-rust" +readme = "README.md" +keywords = [ + "ffi", + "windivert", + "bindings", + "driver", +] +categories = ["external-ffi-bindings"] +license = "LGPL-3.0-or-later" +repository = "https://github.com/Rubensei/windivert-rust.git" + +[package.metadata.docs.rs] +default-target = "x86_64-pc-windows-msvc" + +[dependencies.etherparse] +version = "0.13" + +[dependencies.thiserror] +version = "1" + +[dependencies.windivert-sys] +version = "0.10.0" + +[dependencies.windows] +version = "0.48" +features = [ + "Devices_Custom", + "Win32_Devices", + "Win32_Foundation", + "Win32_Security", + "Win32_Storage_FileSystem", + "Win32_System_Diagnostics", + "Win32_System_IO", + "Win32_System_Ioctl", + "Win32_System_Services", + "Win32_System_Threading", +] + +[features] +default = [] +static = [ + "vendored", + "windivert-sys/static", +] +vendored = ["windivert-sys/vendored"] diff --git a/crates/windivert/README.md b/crates/windivert/README.md new file mode 100644 index 0000000..62dd2a4 --- /dev/null +++ b/crates/windivert/README.md @@ -0,0 +1,50 @@ +# WinDivert 2 Rust Wrapper + +[](https://raw.githubusercontent.com/Rubensei/windivert-rust/master/LICENSE) + +**Note**: This is a work in process, so the crates won't follow semantic +versioning until 1.0.0 release, so any version change below 1.0.0 might +introduce breaking changes in the API or the crate usage in general. + +This projects allows you to use +[WinDivert](https://www.reqrypt.org/windivert.html) from rust. It consists of +two crates: + +- `windivert-sys` + [](https://crates.io/crates/windivert-sys) + [](https://docs.rs/windivert-sys/) + [](https://deps.rs/repo/github/Rubensei/windivert-rust?path=windivert-sys): + Crate providing raw bindings to the WinDivert user mode library. +- `windivert` + [](https://crates.io/crates/windivert) + [](https://docs.rs/windivert/) + [](https://deps.rs/repo/github/Rubensei/windivert-rust?path=windivert): + (WIP) Built on top of `windivert-sys` and providing a friendlier Rust API and + some abstractions. + +# Build + +To be able to build `windivert-sys` you require WinDivert library files: + +- It's recommended to specify the path of the folder containing downloaded dll, + lib & sys files using the `WINDIVERT_PATH` environment variable. +- As a fallback windivert dll & lib files can be compiled from source if the + **vendored** feature is enabled. To avoid multiple compilations set + `WINDIVERT_DLL_OUTPUT` environment variable to save the generated build. +- It's possible to compile for statically linking to the windivert library by + enabling the **static** feature. Static linking can also be enabled if the + `WINDIVERT_STATIC` is set and it takes priority over the crate features. +- **Any vendoring method will only compile the library. Sys files must always be + provided.** + +# Usage + +- `windivert-sys` shares the same API the native library uses. Read + [official documentation](https://www.reqrypt.org/windivert-doc.html) for more + details. +- `windivert` WIP + +**Note:** WinDivert dll expects the corresponding driver sys file to be located +on the same folder. Since the dll lib & sys files come in the same folder when +downloading from [official web](https://www.reqrypt.org/windivert.html) +`windivert-sys` will search for it on the path provided with `WINDIVERT_PATH`. diff --git a/crates/windivert/src/address.rs b/crates/windivert/src/address.rs new file mode 100644 index 0000000..f896e03 --- /dev/null +++ b/crates/windivert/src/address.rs @@ -0,0 +1,421 @@ +use std::{ + marker::PhantomData, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, +}; + +use crate::{layer, prelude::*}; +use windivert_sys::address::*; + +/// Newtype wrapper around [`WINDIVERT_ADDRESS`] using typestate to provide a safe interface. +#[repr(transparent)] +#[derive(Debug, Clone)] +pub struct WinDivertAddress<L: layer::WinDivertLayerTrait> { + data: WINDIVERT_ADDRESS, + _layer: PhantomData<L>, +} + +impl<L: layer::WinDivertLayerTrait> WinDivertAddress<L> { + #[inline] + pub(crate) fn from_raw(data: WINDIVERT_ADDRESS) -> Self { + Self { + data, + _layer: PhantomData, + } + } + + /// Timestamp of the event. Uses same clock as `QueryPerformanceCounter()` + #[inline] + pub fn event_timestamp(&self) -> i64 { + self.data.timestamp + } + + /// Type of captured event + #[inline] + pub fn event(&self) -> WinDivertEvent { + self.data.event() + } + + /// The handle's layer + #[inline] + pub fn event_layer(&self) -> WinDivertLayer { + self.data.layer() + } + + /// Set to `true` if the event was sniffed (i.e., not blocked), `false` otherwise + #[inline] + pub fn sniffed(&self) -> bool { + self.data.sniffed() + } + + /// Set to `true` for outbound packets/event, `false` for inbound or otherwise + #[inline] + pub fn outbound(&self) -> bool { + self.data.outbound() + } + + /// Outbound setter + #[inline] + pub fn set_outbound(&mut self, value: bool) { + self.data.set_outbound(value) + } + + /// Set to `true` for loopback packets, `false` otherwise + #[inline] + pub fn loopback(&self) -> bool { + self.data.loopback() + } + + /// Set to `true` for impostor packets, `false` otherwise. + #[inline] + pub fn impostor(&self) -> bool { + self.data.impostor() + } + + /// Impostor setter + #[inline] + pub fn set_impostor(&mut self, value: bool) { + self.data.set_impostor(value) + } + + /// Set to `true` for IPv6 packets/events, `false` otherwise + #[inline] + pub fn ipv6(&self) -> bool { + self.data.ipv6() + } + + /// Set to `true` if the IPv4 checksum is valid, `false` otherwise. + #[inline] + pub fn ip_checksum(&self) -> bool { + self.data.ipchecksum() + } + + /// IPv4 checksum setter + #[inline] + pub fn set_ip_checksum(&mut self, value: bool) { + self.data.set_ipchecksum(value) + } + + /// Set to `true` if the TCP checksum is valid, `false` otherwise. + #[inline] + pub fn tcp_checksum(&self) -> bool { + self.data.tcpchecksum() + } + + /// TCP checksum setter + #[inline] + pub fn set_tcp_checksum(&mut self, value: bool) { + self.data.set_tcpchecksum(value) + } + + /// Set to `true` if the UDP checksum is valid, `false` otherwise. + #[inline] + pub fn udp_checksum(&self) -> bool { + self.data.udpchecksum() + } + + /// UDP checksum setter + #[inline] + pub fn set_udp_checksum(&mut self, value: bool) { + self.data.set_udpchecksum(value) + } +} + +impl<L: layer::WinDivertLayerTrait> AsRef<WINDIVERT_ADDRESS> for WinDivertAddress<L> { + #[inline] + fn as_ref(&self) -> &WINDIVERT_ADDRESS { + &self.data + } +} + +impl<L: layer::WinDivertLayerTrait> AsMut<WINDIVERT_ADDRESS> for WinDivertAddress<L> { + #[inline] + fn as_mut(&mut self) -> &mut WINDIVERT_ADDRESS { + &mut self.data + } +} + +impl WinDivertAddress<layer::NetworkLayer> { + /// Create a new [`WinDivertAddress`] to inject new packets. + /// # Safety + /// The default value for address is zeroed memory, caller must fill with valid data before sending. + pub unsafe fn new() -> Self { + Self { + data: Default::default(), + _layer: PhantomData, + } + } + + #[inline] + fn data(&self) -> &WINDIVERT_DATA_NETWORK { + // SAFETY: Thanks to typestate, we know that self is a network layer address + unsafe { &self.data.union_field.Network } + } + + #[inline] + fn data_mut(&mut self) -> &mut WINDIVERT_DATA_NETWORK { + // SAFETY: Thanks to typestate, we know that self is a network layer address + unsafe { &mut self.data.union_field.Network } + } + + /// The interface index on which the packet arrived (for inbound packets), or is to be sent (for outbound packets) + #[inline] + pub fn interface_index(&self) -> u32 { + self.data().interface_id + } + + /// Interface index setter + #[inline] + pub fn set_interface_index(&mut self, value: u32) { + self.data_mut().interface_id = value + } + + /// The sub-interface index for `interface_id()` + #[inline] + pub fn subinterface_index(&self) -> u32 { + self.data().subinterface_id + } + + /// Sub interface index setter + #[inline] + pub fn set_subinterface_index(&mut self, value: u32) { + self.data_mut().subinterface_id = value + } +} + +impl WinDivertAddress<layer::ForwardLayer> { + /// Create a new [`WinDivertAddress`] to inject new packets. + /// # Safety + /// The default value for address is zeroed memory, caller must fill with valid data before sending. + pub unsafe fn new() -> Self { + Self { + data: Default::default(), + _layer: PhantomData, + } + } + + #[inline] + fn data(&self) -> &WINDIVERT_DATA_NETWORK { + // SAFETY: Thanks to typestate, we know that self is a network layer address + unsafe { &self.data.union_field.Network } + } + + #[inline] + fn data_mut(&mut self) -> &mut WINDIVERT_DATA_NETWORK { + // SAFETY: Thanks to typestate, we know that self is a network layer address + unsafe { &mut self.data.union_field.Network } + } + + /// The interface index on which the packet arrived (for inbound packets), or is to be sent (for outbound packets) + #[inline] + pub fn interface_index(&self) -> u32 { + self.data().interface_id + } + + /// Interface index setter + #[inline] + pub fn set_interface_index(&mut self, value: u32) { + self.data_mut().interface_id = value + } + + /// The sub-interface index for `interface_id()` + #[inline] + pub fn subinterface_index(&self) -> u32 { + self.data().subinterface_id + } + + /// Sub interface index setter + #[inline] + pub fn set_subinterface_index(&mut self, value: u32) { + self.data_mut().subinterface_id = value + } +} + +impl WinDivertAddress<layer::FlowLayer> { + #[inline] + fn data(&self) -> &WINDIVERT_DATA_FLOW { + // SAFETY: Thanks to typestate, we know that self is a flow layer address + unsafe { &self.data.union_field.Flow } + } + + /// The endpoint ID of the flow + #[inline] + pub fn endpoint_id(&self) -> u64 { + self.data().endpoint_id + } + + /// The parent endpoint ID of the flow + #[inline] + pub fn parent_endpoint_id(&self) -> u64 { + self.data().parent_endpoint_id + } + + /// The parent endpoint ID of the flow + #[inline] + pub fn process_id(&self) -> u32 { + self.data().process_id + } + + /// The local address associated with the flow + #[inline] + pub fn local_address(&self) -> IpAddr { + if self.data.ipv6() { + IpAddr::V6(Ipv6Addr::from( + self.data() + .local_addr + .iter() + .rev() + .fold(0u128, |acc, &x| acc << 32 | (x as u128)), + )) + } else { + IpAddr::V4(Ipv4Addr::from(self.data().local_addr[0])) + } + } + + /// The remote address associated with the flow + #[inline] + pub fn remote_address(&self) -> IpAddr { + if self.data.ipv6() { + IpAddr::V6(Ipv6Addr::from( + self.data() + .remote_addr + .iter() + .rev() + .fold(0u128, |acc, &x| acc << 32 | (x as u128)), + )) + } else { + IpAddr::V4(Ipv4Addr::from(self.data().remote_addr[0])) + } + } + + /// The locla port associated with the flow + #[inline] + pub fn local_port(&self) -> u16 { + self.data().local_port + } + + /// The remote port associated with the flow + #[inline] + pub fn remote_port(&self) -> u16 { + self.data().remote_port + } + + /// The protocol associated with the flow + #[inline] + pub fn protocol(&self) -> u8 { + self.data().protocol + } +} + +impl WinDivertAddress<layer::SocketLayer> { + #[inline] + fn data(&self) -> &WINDIVERT_DATA_FLOW { + // SAFETY: Thanks to typestate, we know that self is a flow layer address + unsafe { &self.data.union_field.Flow } + } + + /// The endpoint ID of the flow + #[inline] + pub fn endpoint_id(&self) -> u64 { + self.data().endpoint_id + } + + /// The parent endpoint ID of the flow + #[inline] + pub fn parent_endpoint_id(&self) -> u64 { + self.data().parent_endpoint_id + } + + /// The parent endpoint ID of the flow + #[inline] + pub fn process_id(&self) -> u32 { + self.data().process_id + } + + /// The local address associated with the flow + #[inline] + pub fn local_address(&self) -> IpAddr { + if self.data.ipv6() { + IpAddr::V6(Ipv6Addr::from( + self.data() + .local_addr + .iter() + .rev() + .fold(0u128, |acc, &x| acc << 32 | (x as u128)), + )) + } else { + IpAddr::V4(Ipv4Addr::from(self.data().local_addr[0])) + } + } + + /// The remote address associated with the flow + #[inline] + pub fn remote_address(&self) -> IpAddr { + if self.data.ipv6() { + IpAddr::V6(Ipv6Addr::from( + self.data() + .remote_addr + .iter() + .rev() + .fold(0u128, |acc, &x| acc << 32 | (x as u128)), + )) + } else { + IpAddr::V4(Ipv4Addr::from(self.data().remote_addr[0])) + } + } + + /// The locla port associated with the flow + #[inline] + pub fn local_port(&self) -> u16 { + self.data().local_port + } + + /// The remote port associated with the flow + #[inline] + pub fn remote_port(&self) -> u16 { + self.data().remote_port + } + + /// The protocol associated with the flow + #[inline] + pub fn protocol(&self) -> u8 { + self.data().protocol + } +} + +impl WinDivertAddress<layer::ReflectLayer> { + #[inline] + fn data(&self) -> &WINDIVERT_DATA_REFLECT { + // SAFETY: Thanks to typestate, we know that self is a reflect layer address + unsafe { &self.data.union_field.Reflect } + } + + /// A timestamp indicating when the handle was opened + #[inline] + pub fn timestamp(&self) -> i64 { + self.data().timestamp + } + + /// The ID of the process that opened the handle + #[inline] + pub fn process_id(&self) -> u32 { + self.data().process_id + } + + /// The layer of the opened handle + #[inline] + pub fn layer(&self) -> WinDivertLayer { + self.data().layer + } + + /// The flags of the opened handle + #[inline] + pub fn flags(&self) -> WinDivertFlags { + self.data().flags + } + + /// The priority of the opened handle + #[inline] + pub fn priority(&self) -> i16 { + self.data().priority + } +} diff --git a/crates/windivert/src/divert/blocking.rs b/crates/windivert/src/divert/blocking.rs new file mode 100644 index 0000000..c003246 --- /dev/null +++ b/crates/windivert/src/divert/blocking.rs @@ -0,0 +1,353 @@ +use std::borrow::Cow; +use std::{ffi::c_void, mem::MaybeUninit}; + +use crate::address::WinDivertAddress; +use crate::layer; +use crate::prelude::*; +use etherparse::{InternetSlice, SlicedPacket}; +use sys::address::WINDIVERT_ADDRESS; +use windivert_sys as sys; + +const ADDR_SIZE: usize = std::mem::size_of::<WINDIVERT_ADDRESS>(); + +impl<L: layer::WinDivertLayerTrait> WinDivert<L> { + fn internal_recv<'a>( + &self, + buffer: Option<&'a mut [u8]>, + ) -> Result<WinDivertPacket<'a, L>, WinDivertError> { + let mut packet_length = 0; + let mut addr = MaybeUninit::uninit(); + let (buffer_ptr, buffer_len) = if let Some(ref buffer) = buffer { + (buffer.as_ptr(), buffer.len()) + } else { + (std::ptr::null(), 0) + }; + + let res = unsafe { + sys::WinDivertRecv( + self.handle, + buffer_ptr as *mut c_void, + buffer_len as u32, + &mut packet_length, + addr.as_mut_ptr(), + ) + }; + + if res.as_bool() { + Ok(WinDivertPacket { + address: WinDivertAddress::<L>::from_raw(unsafe { addr.assume_init() }), + data: buffer + .map(|b| Cow::Borrowed(&b[..packet_length as usize])) + .unwrap_or_default(), + }) + } else { + let recv_err = WinDivertRecvError::try_from(std::io::Error::last_os_error())?; + Err(recv_err.into()) + } + } + + fn internal_recv_ex<'a>( + &self, + buffer: Option<&'a mut [u8]>, + packet_count: usize, + ) -> Result<(Option<&'a [u8]>, Vec<WINDIVERT_ADDRESS>), WinDivertError> { + let mut packet_length = 0; + + let mut addr_len = (ADDR_SIZE * packet_count) as u32; + let mut addr_buffer: Vec<WINDIVERT_ADDRESS> = + vec![WINDIVERT_ADDRESS::default(); packet_count]; + + let (buffer_ptr, buffer_len) = if let Some(buffer) = &buffer { + (buffer.as_ptr(), buffer.len()) + } else { + (std::ptr::null(), 0) + }; + + let res = unsafe { + sys::WinDivertRecvEx( + self.handle, + buffer_ptr as *mut c_void, + buffer_len as u32, + &mut packet_length, + 0, + addr_buffer.as_mut_ptr(), + &mut addr_len, + std::ptr::null_mut(), + ) + }; + + if res.as_bool() { + addr_buffer.truncate((addr_len / ADDR_SIZE as u32) as usize); + Ok(( + buffer.map(|buffer| &buffer[..packet_length as usize]), + addr_buffer, + )) + } else { + let recv_err = WinDivertRecvError::try_from(std::io::Error::last_os_error())?; + Err(recv_err.into()) + } + } + + fn internal_send(&self, packet: &WinDivertPacket<L>) -> Result<u32, WinDivertError> { + let mut injected_length = 0; + + let res = unsafe { + sys::WinDivertSend( + self.handle, + packet.data.as_ptr() as *const c_void, + packet.data.len() as u32, + &mut injected_length, + packet.address.as_ref(), + ) + }; + + if !res.as_bool() { + return Err(std::io::Error::last_os_error().into()); + } + + Ok(injected_length) + } + + fn internal_send_ex<'data, 'packets, P>(&self, packets: P) -> Result<u32, WinDivertError> + where + P: ExactSizeIterator<Item = &'packets WinDivertPacket<'data, L>>, + 'data: 'packets, + L: 'packets, + { + let packet_count = packets.len(); + let mut injected_length = 0; + let mut packet_buffer: Vec<u8> = Vec::new(); + let mut address_buffer: Vec<WINDIVERT_ADDRESS> = Vec::with_capacity(packet_count); + packets.for_each(|packet: &'packets WinDivertPacket<'data, L>| { + packet_buffer.extend(&packet.data[..]); + address_buffer.push(*packet.address.as_ref()); + }); + + let res = unsafe { + sys::WinDivertSendEx( + self.handle, + packet_buffer.as_ptr() as *const c_void, + packet_buffer.len() as u32, + &mut injected_length, + 0, + address_buffer.as_ptr(), + (std::mem::size_of::<WINDIVERT_ADDRESS>() * packet_count) as u32, + std::ptr::null_mut(), + ) + }; + + if !res.as_bool() { + return Err(std::io::Error::last_os_error().into()); + } + + Ok(injected_length) + } +} + +impl WinDivert<layer::NetworkLayer> { + /// Single packet blocking recv function. + pub fn recv<'a>( + &self, + buffer: Option<&'a mut [u8]>, + ) -> Result<WinDivertPacket<'a, layer::NetworkLayer>, WinDivertError> { + self.internal_recv(buffer) + } + + /// Batched blocking recv function. + pub fn recv_ex<'a>( + &self, + buffer: Option<&'a mut [u8]>, + packet_count: usize, + ) -> Result<Vec<WinDivertPacket<'a, layer::NetworkLayer>>, WinDivertError> { + let (mut buffer, addresses) = self.internal_recv_ex(buffer, packet_count)?; + let mut packets = Vec::with_capacity(addresses.len()); + for addr in addresses.into_iter() { + packets.push(WinDivertPacket { + address: WinDivertAddress::<layer::NetworkLayer>::from_raw(addr), + data: buffer + .map(|inner_buffer| { + let headers = SlicedPacket::from_ip(inner_buffer) + .expect("WinDivert can't capture anything below ip"); + let offset = match headers.ip.unwrap() { + InternetSlice::Ipv4(ip_header, _) => ip_header.total_len() as usize, + InternetSlice::Ipv6(ip6header, _) => { + ip6header.payload_length() as usize + 40 + } + }; + let (data, tail) = inner_buffer.split_at(offset); + buffer = Some(tail); + Cow::Borrowed(data) + }) + .unwrap_or_default(), + }); + } + Ok(packets) + } + + /// Single packet send function. + pub fn send( + &self, + packet: &WinDivertPacket<layer::NetworkLayer>, + ) -> Result<u32, WinDivertError> { + self.internal_send(packet) + } + + /// Batched packet send function. + pub fn send_ex<'data, 'packets, P, I>(&self, packets: P) -> Result<u32, WinDivertError> + where + P: IntoIterator<IntoIter = I>, + I: ExactSizeIterator<Item = &'packets WinDivertPacket<'data, layer::NetworkLayer>>, + 'data: 'packets, + { + self.internal_send_ex(packets.into_iter()) + } +} + +impl WinDivert<layer::ForwardLayer> { + /// Single packet blocking recv function. + pub fn recv<'a>( + &self, + buffer: Option<&'a mut [u8]>, + ) -> Result<WinDivertPacket<'a, layer::ForwardLayer>, WinDivertError> { + self.internal_recv(buffer) + } + + /// Batched blocking recv function. + pub fn recv_ex<'a>( + &self, + buffer: Option<&'a mut [u8]>, + packet_count: usize, + ) -> Result<Vec<WinDivertPacket<'a, layer::NetworkLayer>>, WinDivertError> { + let (mut buffer, addresses) = self.internal_recv_ex(buffer, packet_count)?; + let mut packets = Vec::with_capacity(addresses.len()); + for addr in addresses.into_iter() { + packets.push(WinDivertPacket { + address: WinDivertAddress::<layer::NetworkLayer>::from_raw(addr), + data: buffer + .map(|inner_buffer| { + let headers = SlicedPacket::from_ip(inner_buffer) + .expect("WinDivert can't capture anything below ip"); + let offset = match headers.ip.unwrap() { + InternetSlice::Ipv4(ip_header, _) => ip_header.total_len() as usize, + InternetSlice::Ipv6(ip6header, _) => { + ip6header.payload_length() as usize + 40 + } + }; + let (data, tail) = inner_buffer.split_at(offset); + buffer = Some(tail); + Cow::Borrowed(data) + }) + .unwrap_or_default(), + }); + } + Ok(packets) + } + + /// Single packet send function. + pub fn send( + &self, + packet: &WinDivertPacket<layer::ForwardLayer>, + ) -> Result<u32, WinDivertError> { + self.internal_send(packet) + } + + /// Batched packet send function. + pub fn send_ex<'data, 'packets, P, I>(&self, packets: P) -> Result<u32, WinDivertError> + where + P: IntoIterator<IntoIter = I>, + I: ExactSizeIterator<Item = &'packets WinDivertPacket<'data, layer::ForwardLayer>>, + 'data: 'packets, + { + self.internal_send_ex(packets.into_iter()) + } +} + +impl WinDivert<layer::FlowLayer> { + /// Single packet blocking recv function. + pub fn recv<'a>( + &self, + buffer: Option<&'a mut [u8]>, + ) -> Result<WinDivertPacket<'a, layer::FlowLayer>, WinDivertError> { + self.internal_recv(buffer) + } + + /// Batched blocking recv function. + pub fn recv_ex<'a>( + &self, + packet_count: usize, + ) -> Result<Vec<WinDivertPacket<'a, layer::FlowLayer>>, WinDivertError> { + let (_, addresses) = self.internal_recv_ex(None, packet_count)?; + let mut packets = Vec::with_capacity(addresses.len()); + for addr in addresses.into_iter() { + packets.push(WinDivertPacket::<layer::FlowLayer> { + address: WinDivertAddress::<layer::FlowLayer>::from_raw(addr), + data: Default::default(), + }); + } + Ok(packets) + } +} + +impl WinDivert<layer::SocketLayer> { + /// Single packet blocking recv function. + pub fn recv<'a>( + &self, + buffer: Option<&'a mut [u8]>, + ) -> Result<WinDivertPacket<'a, layer::SocketLayer>, WinDivertError> { + self.internal_recv(buffer) + } + + /// Batched blocking recv function. + pub fn recv_ex<'a>( + &self, + packet_count: usize, + ) -> Result<Vec<WinDivertPacket<'a, layer::SocketLayer>>, WinDivertError> { + let (_, addresses) = self.internal_recv_ex(None, packet_count)?; + let mut packets = Vec::with_capacity(addresses.len()); + for addr in addresses.into_iter() { + packets.push(WinDivertPacket::<layer::SocketLayer> { + address: WinDivertAddress::<layer::SocketLayer>::from_raw(addr), + data: Default::default(), + }); + } + Ok(packets) + } +} + +impl WinDivert<layer::ReflectLayer> { + /// Single packet blocking recv function. + pub fn recv<'a>( + &self, + buffer: Option<&'a mut [u8]>, + ) -> Result<WinDivertPacket<'a, layer::ReflectLayer>, WinDivertError> { + self.internal_recv(buffer) + } + + /// Batched blocking recv function. + pub fn recv_ex<'a>( + &self, + buffer: Option<&'a mut [u8]>, + packet_count: usize, + ) -> Result<Vec<WinDivertPacket<'a, layer::ReflectLayer>>, WinDivertError> { + let (mut buffer, addresses) = self.internal_recv_ex(buffer, packet_count)?; + let mut packets = Vec::with_capacity(addresses.len()); + for addr in addresses.into_iter() { + packets.push(WinDivertPacket { + address: WinDivertAddress::<layer::ReflectLayer>::from_raw(addr), + data: buffer + .map(|inner_buffer| { + let (data, tail) = inner_buffer.split_at( + inner_buffer + .iter() + .position(|&x| x == b'\0') + .expect("CStrings always end in null"), + ); + buffer = Some(tail); + Cow::Borrowed(data) + }) + .unwrap_or_default(), + }); + } + Ok(packets) + } +} diff --git a/crates/windivert/src/divert/mod.rs b/crates/windivert/src/divert/mod.rs new file mode 100644 index 0000000..2bce256 --- /dev/null +++ b/crates/windivert/src/divert/mod.rs @@ -0,0 +1,229 @@ +mod blocking; + +use std::{ + ffi::{c_void, CString}, + marker::PhantomData, + mem::MaybeUninit, +}; + +use crate::layer; +use crate::prelude::*; +use sys::{WinDivertParam, WinDivertShutdownMode}; +use windivert_sys as sys; + +use windows::{ + core::{Error as WinError, Result as WinResult, PCSTR}, + Win32::{ + Foundation::{GetLastError, HANDLE}, + System::{ + Services::{ + CloseServiceHandle, ControlService, OpenSCManagerA, OpenServiceA, + SC_MANAGER_ALL_ACCESS, SERVICE_CONTROL_STOP, SERVICE_STATUS, + }, + Threading::{CreateEventA, TlsAlloc, TlsGetValue, TlsSetValue}, + }, + }, +}; + +/// Main wrapper struct around windivert functionalities. +#[non_exhaustive] +pub struct WinDivert<L: layer::WinDivertLayerTrait> { + handle: HANDLE, + _tls_idx: u32, + _layer: PhantomData<L>, +} + +/// Recv implementations +impl<L: layer::WinDivertLayerTrait> WinDivert<L> { + /// Open a handle using the specified parameters. + fn new( + filter: &str, + layer: WinDivertLayer, + priority: i16, + flags: WinDivertFlags, + ) -> Result<Self, WinDivertError> { + let filter = CString::new(filter)?; + let windivert_tls_idx = unsafe { TlsAlloc() }; + let handle = unsafe { sys::WinDivertOpen(filter.as_ptr(), layer, priority, flags) }; + if handle.is_invalid() { + let open_err = WinDivertOpenError::try_from(std::io::Error::last_os_error())?; + Err(open_err.into()) + } else { + Ok(Self { + handle, + _tls_idx: windivert_tls_idx, + _layer: PhantomData::<L>, + }) + } + } + + pub(crate) fn _get_event(tls_idx: u32) -> Result<HANDLE, WinDivertError> { + let mut event = HANDLE::default(); + unsafe { + event.0 = TlsGetValue(tls_idx) as isize; + if event.is_invalid() { + event = CreateEventA(None, false, false, None)?; + TlsSetValue(tls_idx, Some(event.0 as *mut c_void)); + } + } + Ok(event) + } + + /// Methods that allows to query the driver for parameters. + pub fn get_param(&self, param: WinDivertParam) -> Result<u64, WinDivertError> { + let mut value = 0; + let res = unsafe { sys::WinDivertGetParam(self.handle, param, &mut value) }; + if !res.as_bool() { + return Err(std::io::Error::last_os_error().into()); + } + Ok(value) + } + + /// Method that allows setting driver parameters. + pub fn set_param(&self, param: WinDivertParam, value: u64) -> Result<(), WinDivertError> { + match param { + WinDivertParam::VersionMajor | WinDivertParam::VersionMinor => { + Err(WinDivertError::Parameter(param, value)) + } + _ => unsafe { sys::WinDivertSetParam(self.handle, param, value) } + .ok() + .map_err(|_| std::io::Error::last_os_error().into()), + } + } + + /// Handle close function. + pub fn close(&mut self, action: CloseAction) -> WinResult<()> { + let res = unsafe { sys::WinDivertClose(self.handle) }; + if !res.as_bool() { + return Err(WinError::from(unsafe { GetLastError() })); + } + match action { + CloseAction::Uninstall => WinDivert::uninstall(), + CloseAction::Nothing => Ok(()), + } + } + + /// Shutdown function. + pub fn shutdown(&self, mode: WinDivertShutdownMode) -> WinResult<()> { + let res = unsafe { sys::WinDivertShutdown(self.handle, mode) }; + if !res.as_bool() { + return Err(WinError::from(unsafe { GetLastError() })); + } + Ok(()) + } +} + +impl WinDivert<layer::NetworkLayer> { + /// WinDivert constructor for network layer. + pub fn network( + filter: impl AsRef<str>, + priority: i16, + flags: WinDivertFlags, + ) -> Result<Self, WinDivertError> { + Self::new(filter.as_ref(), WinDivertLayer::Network, priority, flags) + } +} + +impl WinDivert<layer::ForwardLayer> { + /// WinDivert constructor for forward layer. + pub fn forward( + filter: impl AsRef<str>, + priority: i16, + flags: WinDivertFlags, + ) -> Result<Self, WinDivertError> { + Self::new(filter.as_ref(), WinDivertLayer::Forward, priority, flags) + } +} + +impl WinDivert<layer::FlowLayer> { + /// WinDivert constructor for flow layer. + pub fn flow( + filter: &str, + priority: i16, + flags: WinDivertFlags, + ) -> Result<Self, WinDivertError> { + Self::new( + filter, + WinDivertLayer::Flow, + priority, + flags.set_recv_only().set_sniff(), + ) + } +} + +impl WinDivert<layer::SocketLayer> { + /// WinDivert constructor for socket layer. + pub fn socket( + filter: impl AsRef<str>, + priority: i16, + flags: WinDivertFlags, + ) -> Result<Self, WinDivertError> { + Self::new( + filter.as_ref(), + WinDivertLayer::Socket, + priority, + flags.set_recv_only(), + ) + } +} + +impl WinDivert<layer::ReflectLayer> { + /// WinDivert constructor for reflect layer. + pub fn reflect( + filter: impl AsRef<str>, + priority: i16, + flags: WinDivertFlags, + ) -> Result<Self, WinDivertError> { + Self::new( + filter.as_ref(), + WinDivertLayer::Reflect, + priority, + flags.set_recv_only().set_sniff(), + ) + } +} + +impl WinDivert<()> { + /// Maximum number of packets that can be captured/sent in a single batched operation + pub const MAX_BATCH: u8 = windivert_sys::WINDIVERT_BATCH_MAX as u8; + + /// Method that tries to uninstall WinDivert driver. + pub fn uninstall() -> WinResult<()> { + let mut status = MaybeUninit::<SERVICE_STATUS>::uninit(); + unsafe { + let manager = OpenSCManagerA(None, None, SC_MANAGER_ALL_ACCESS)?; + let service = OpenServiceA( + manager, + PCSTR::from_raw(c"WinDivert".as_ptr() as *const u8), + SC_MANAGER_ALL_ACCESS, + )?; + let res = ControlService(service, SERVICE_CONTROL_STOP, status.as_mut_ptr()); + if !res.as_bool() { + return Err(WinError::from(GetLastError())); + } + let res = CloseServiceHandle(service); + if !res.as_bool() { + return Err(WinError::from(GetLastError())); + } + let res = CloseServiceHandle(manager); + if !res.as_bool() { + return Err(WinError::from(GetLastError())); + } + } + Ok(()) + } +} + +/// Action parameter for [`WinDivert::close()`](`fn@WinDivert::close`) +pub enum CloseAction { + /// Close the handle and try to uninstall the WinDivert driver. + Uninstall, + /// Close the handle without uninstalling the driver. + Nothing, +} + +impl Default for CloseAction { + fn default() -> Self { + Self::Nothing + } +} diff --git a/crates/windivert/src/error.rs b/crates/windivert/src/error.rs new file mode 100644 index 0000000..2f37dd0 --- /dev/null +++ b/crates/windivert/src/error.rs @@ -0,0 +1,129 @@ +use std::convert::TryFrom; +use std::ffi::NulError; + +use thiserror::Error; +use windivert_sys::{WinDivertParam, WinDivertValueError}; + +/** +WinDivert error type. +*/ +#[derive(Debug, Error)] +pub enum WinDivertError { + /// Unexpected value in type conversions. + #[error(transparent)] + Value(#[from] WinDivertValueError), + /// Specific errors for divert constructor invocation. + #[error(transparent)] + Open(#[from] WinDivertOpenError), + /// Specific errors for [`WinDivert::recv()`](fn@super::WinDivert::<L>::recv). + #[error(transparent)] + Recv(#[from] WinDivertRecvError), + /// Error for nul terminated filter strings. + #[error(transparent)] + NullError(#[from] NulError), + /// Generic IO error. + #[error(transparent)] + IOError(#[from] std::io::Error), + /// Generic OS error. + #[error(transparent)] + OSError(#[from] windows::core::Error), + /// Error indicating that a wrong parameter was used in [`set_param()`](fn@crate::WinDivert::set_param) + #[error("Invalid parameter for set_param(). Parameter: {0:?}, Value: {1}")] + Parameter(WinDivertParam, u64), +} + +/** +Possible errors for [`WinDivertOpen()`](fn@windivert_sys::WinDivertOpen) +*/ +#[derive(Debug, Error)] +pub enum WinDivertOpenError { + /// The driver files WinDivert32.sys or WinDivert64.sys were not found. + #[error("SYS driver file not found")] + MissingSYS, // 2 + /// The calling application does not have Administrator privileges. + #[error("Running without elevated access rights")] + AccessDenied, // 5 + /// This indicates an invalid packet filter string, layer, priority, or flags. + #[error("Invalid parameter (filter string, layer, priority, or flags)")] + InvalidParameter, // 87 + /// The WinDivert32.sys or WinDivert64.sys driver does not have a valid digital signature. + #[error("SYS driver file has invalid digital signature")] + InvalidImageHash, // 577 + /// An incompatible version of the WinDivert driver is currently loaded. + #[error("An incompatible version of the WinDivert driver is currently loaded")] + IncompatibleVersion, // 654 + /// The handle was opened with the WINDIVERT_FLAG_NO_INSTALL flag and the WinDivert driver is not already installed. + #[error("The handle was opened with the WINDIVERT_FLAG_NO_INSTALL flag and the WinDivert driver is not already installed")] + MissingInstall, // 1060 + /// The WinDivert driver is blocked by security software or you are using a virtualization environment that does not support drivers. + #[error("WinDivert driver is blocked by security software or you are using a virtualization environment that does not support drivers")] + DriverBlocked, // 1257 + /// This error occurs when the Base Filtering Engine service has been disabled. + #[error("Base Filtering Engine service has been disabled")] + BaseFilteringEngineDisabled, // 1753 +} + +impl TryFrom<i32> for WinDivertOpenError { + type Error = std::io::Error; + + fn try_from(value: i32) -> Result<Self, Self::Error> { + match value { + 2 => Ok(WinDivertOpenError::MissingSYS), + 5 => Ok(WinDivertOpenError::AccessDenied), + 87 => Ok(WinDivertOpenError::InvalidParameter), + 577 => Ok(WinDivertOpenError::InvalidImageHash), + 654 => Ok(WinDivertOpenError::IncompatibleVersion), + 1060 => Ok(WinDivertOpenError::MissingInstall), + 1257 => Ok(WinDivertOpenError::DriverBlocked), + 1753 => Ok(WinDivertOpenError::BaseFilteringEngineDisabled), + _ => Err(std::io::Error::from_raw_os_error(value)), + } + } +} + +impl TryFrom<std::io::Error> for WinDivertOpenError { + type Error = std::io::Error; + + fn try_from(error: std::io::Error) -> Result<Self, Self::Error> { + error + .raw_os_error() + .map(WinDivertOpenError::try_from) + .unwrap_or(Err(error)) + } +} + +/** +Possible errors for [`WinDivertRecv()`](fn@windivert_sys::WinDivertRecv) +*/ +#[derive(Debug, Error)] +pub enum WinDivertRecvError { + /// The captured packet is larger than the provided buffer. + #[error("Captured packet is larger than the provided buffer")] + InsufficientBuffer, // 122 + /// The handle has been shutdown and the packet queue is empty. + #[error("Not possible to get more data. Packet queue is empty and handle has been shutdown")] + NoData, // 232 +} + +impl TryFrom<i32> for WinDivertRecvError { + type Error = std::io::Error; + + fn try_from(value: i32) -> Result<Self, Self::Error> { + match value { + 122 => Ok(WinDivertRecvError::InsufficientBuffer), + 232 => Ok(WinDivertRecvError::NoData), + _ => Err(std::io::Error::from_raw_os_error(value)), + } + } +} + +impl TryFrom<std::io::Error> for WinDivertRecvError { + type Error = std::io::Error; + + fn try_from(error: std::io::Error) -> Result<Self, Self::Error> { + error + .raw_os_error() + .map(WinDivertRecvError::try_from) + .unwrap_or(Err(error)) + } +} diff --git a/crates/windivert/src/layer.rs b/crates/windivert/src/layer.rs new file mode 100644 index 0000000..3c71588 --- /dev/null +++ b/crates/windivert/src/layer.rs @@ -0,0 +1,46 @@ +use windivert_sys::WinDivertLayer; + +/// Network type for typestate pattern. +#[derive(Debug, Clone)] +pub enum NetworkLayer {} +/// Forward type for typestate pattern. +#[derive(Debug, Clone)] +pub enum ForwardLayer {} +/// Flow type for typestate pattern. +#[derive(Debug, Clone)] +pub enum FlowLayer {} +/// Socket type for typestate pattern. +#[derive(Debug, Clone)] +pub enum SocketLayer {} +/// Reflect type for typestate pattern. +#[derive(Debug, Clone)] +pub enum ReflectLayer {} + +/// Trait for typestate pattern. +pub trait WinDivertLayerTrait: sealed::Sealed + std::fmt::Debug + std::clone::Clone {} + +impl WinDivertLayerTrait for NetworkLayer {} + +impl WinDivertLayerTrait for ForwardLayer {} + +impl WinDivertLayerTrait for FlowLayer {} + +impl WinDivertLayerTrait for SocketLayer {} + +impl WinDivertLayerTrait for ReflectLayer {} + +impl WinDivertLayerTrait for WinDivertLayer {} + +impl WinDivertLayerTrait for () {} + +mod sealed { + pub trait Sealed {} + + impl Sealed for () {} + impl Sealed for super::NetworkLayer {} + impl Sealed for super::ForwardLayer {} + impl Sealed for super::FlowLayer {} + impl Sealed for super::SocketLayer {} + impl Sealed for super::ReflectLayer {} + impl Sealed for super::WinDivertLayer {} +} diff --git a/crates/windivert/src/lib.rs b/crates/windivert/src/lib.rs new file mode 100644 index 0000000..c4f5a05 --- /dev/null +++ b/crates/windivert/src/lib.rs @@ -0,0 +1,28 @@ +#![deny(missing_docs)] +/*! +Wrapper around [`windivert_sys`] ffi crate. +*/ + +/// WinDivert address data structures +pub mod address; +mod divert; +/// WinDivert error types +pub mod error; +/// Layer types used for typestate pattern +pub mod layer; +/// WinDivert packet types +pub mod packet; + +pub use divert::*; + +/// Prelude module for [`WinDivert`]. +pub mod prelude { + pub use windivert_sys::{ + WinDivertEvent, WinDivertFlags, WinDivertLayer, WinDivertParam, WinDivertShutdownMode, + }; + + pub use crate::divert::*; + pub use crate::error::*; + pub use crate::layer::*; + pub use crate::packet::*; +} diff --git a/crates/windivert/src/packet.rs b/crates/windivert/src/packet.rs new file mode 100644 index 0000000..ea81fe9 --- /dev/null +++ b/crates/windivert/src/packet.rs @@ -0,0 +1,90 @@ +use windivert_sys::{ChecksumFlags, WinDivertHelperCalcChecksums}; + +use crate::{address::WinDivertAddress, layer, prelude::WinDivertError}; + +use std::{ + borrow::{BorrowMut, Cow}, + ffi::c_void, + fmt::Debug, +}; + +/// Raw captured packet +#[derive(Debug, Clone)] +pub struct WinDivertPacket<'a, L: layer::WinDivertLayerTrait> { + /// Address data + pub address: WinDivertAddress<L>, + /// Raw captured data + pub data: Cow<'a, [u8]>, +} + +impl<'a> WinDivertPacket<'a, layer::NetworkLayer> { + /// Create a new network packet from a raw buffer + /// # Safety + /// `address` is zeroed, user must fill it with correct data before sending. + pub unsafe fn new(data: Vec<u8>) -> Self { + Self { + address: WinDivertAddress::<layer::NetworkLayer>::new(), + data: Cow::from(data), + } + } + + /// Recalculate the checksums of the packet + /// This is a noop if the packet is not owned. + pub fn recalculate_checksums(&mut self, flags: ChecksumFlags) -> Result<(), WinDivertError> { + if let Cow::Owned(ref mut data) = self.data.borrow_mut() { + let res = unsafe { + WinDivertHelperCalcChecksums( + data.as_mut_ptr() as *mut c_void, + data.len() as u32, + self.address.as_mut(), + flags, + ) + }; + if !res.as_bool() { + return Err(WinDivertError::from(windows::core::Error::from_win32())); + } + } + Ok(()) + } +} + +impl<'a> WinDivertPacket<'a, layer::ForwardLayer> { + /// Create a new network forward packet from a raw buffer + /// # Safety + /// `address` is zeroed, user must fill it with correct data before sending. + pub unsafe fn new(data: Vec<u8>) -> Self { + Self { + address: WinDivertAddress::<layer::ForwardLayer>::new(), + data: Cow::from(data), + } + } + + /// Recalculate the checksums of the packet + /// This is a noop if the packet is not owned. + pub fn recalculate_checksums(&mut self, flags: ChecksumFlags) -> Result<(), WinDivertError> { + if let Cow::Owned(ref mut data) = self.data.borrow_mut() { + let res = unsafe { + WinDivertHelperCalcChecksums( + data.as_mut_ptr() as *mut c_void, + data.len() as u32, + self.address.as_mut(), + flags, + ) + }; + if !res.as_bool() { + return Err(WinDivertError::from(windows::core::Error::from_win32())); + } + } + Ok(()) + } +} + +impl<'a, L: layer::WinDivertLayerTrait> WinDivertPacket<'a, L> { + /// Create an owned packet from a borrowed packet + pub fn into_owned(self) -> WinDivertPacket<'static, L> { + WinDivertPacket { + address: self.address, + data: self.data.into_owned().into(), + } + } +} diff --git a/src/main.rs b/src/main.rs index 0250044..e967d7f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,17 +47,15 @@ fn main_1() -> Result<()> { } fn main() { - let code = match main_1() { - Ok(()) => 0, + match main_1() { + Ok(()) => { std::process::exit(0); } Err(e) => { crate::error!("{e}"); for (i, cause) in e.chain().skip(1).enumerate() { crate::error!("caused by[{i}]: {cause}"); } - 1 + platform::paexit(1); } }; - - platform::paexit(code); } diff --git a/src/platform.rs b/src/platform.rs index 076308c..87595f2 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -34,6 +34,10 @@ pub use linux::{bootstrap, run, local_time, send_to_raw, is_kernel_filtered_clie /// pause before exit on windows to print information in console before it is closed. pub fn paexit(code: i32) -> ! { - #[cfg(windows)] pause(); + // On windows, this is true when program enters service controller + // entry point. + if !crate::opt::daemon() { + #[cfg(windows)] pause(); + } std::process::exit(code); } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 239449f..9c7875e 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -16,12 +16,9 @@ // along with DPIBreak. If not, see <https://www.gnu.org/licenses/>. use anyhow::Result; -use windivert::{ - WinDivert, - layer::NetworkLayer, - prelude -}; -use std::sync::{LazyLock, Mutex}; +use windivert::{WinDivert, layer::NetworkLayer, prelude}; +use windivert::prelude::{WinDivertError, WinDivertRecvError, WinDivertShutdownMode}; +use std::sync::{Arc, LazyLock, Mutex, OnceLock}; use std::thread; use crate::{opt, pkt}; use super::paexit; @@ -33,6 +30,37 @@ pub fn pause() { unsafe { _getch(); } } +static RECV_HANDLES: LazyLock<Mutex<Vec<Arc<WinDivert<NetworkLayer>>>>> = + LazyLock::new(|| Mutex::new(Vec::new())); + +fn open_recv_handle(filter: &str, flags: prelude::WinDivertFlags) -> Arc<WinDivert<NetworkLayer>> { + let h = Arc::new(open_handle(filter, flags)); + RECV_HANDLES.lock().expect("mutex poisoned").push(h.clone()); + h +} + +fn shutdown_all() { + for h in RECV_HANDLES.lock().expect("mutex poisoned").iter() { + if let Err(e) = h.shutdown(WinDivertShutdownMode::Both) { + crate::warn!("windivert: shutdown: {e}"); + } + } +} + +fn cleanup_all() { + let handles: Vec<_> = RECV_HANDLES.lock().unwrap().drain(..).collect(); + for h in handles { + match Arc::try_unwrap(h) { + Ok(mut wd) => { + _ = wd.close(windivert::CloseAction::Nothing); + }, + Err(_still_shared) => { + crate::warn!("windivert: handle still referenced, skipping close"); + } + } + } +} + fn open_handle(filter: &str, flags: prelude::WinDivertFlags) -> WinDivert<NetworkLayer> { use windivert::*; @@ -43,7 +71,7 @@ fn open_handle(filter: &str, flags: prelude::WinDivertFlags) -> WinDivert<Networ }, Err(e) => { crate::error!("windivert: cannot open {filter}: {e}"); - paexit(1); + paexit(1); } }; h @@ -54,15 +82,27 @@ pub fn bootstrap() -> Result<()> { service_main(); } + install_ctrl_handler(); + Ok(()) } -static SEND_HANDLE: LazyLock<Mutex<WinDivert<NetworkLayer>>> = LazyLock::new(|| { - let flags = prelude::WinDivertFlags::new() - .set_send_only(); +static SEND_HANDLE: OnceLock<Mutex<WinDivert<NetworkLayer>>> = OnceLock::new(); + +fn send_handle() -> &'static Mutex<WinDivert<NetworkLayer>> { + SEND_HANDLE.get_or_init(|| { + let flags = prelude::WinDivertFlags::new().set_send_only(); + Mutex::new(open_handle("false", flags)) + }) +} - Mutex::new(open_handle("false", flags)) -}); +fn close_send_handle() { + if let Some(m) = SEND_HANDLE.get() && let Ok(mut wd) = m.lock() { + if let Err(e) = wd.close(windivert::CloseAction::Nothing) { + crate::warn!("windivert: close send handle: {e}"); + } + } +} fn send_to_raw_1(pkt: &[u8]) -> Result<()> { use windivert::*; @@ -74,7 +114,7 @@ fn send_to_raw_1(pkt: &[u8]) -> Result<()> { p.address.set_tcp_checksum(false); // For badsum; anyway it is already calculated p.address.set_impostor(true); // to prevent inf loop - SEND_HANDLE.lock().expect("mutex poisoned").send(&p)?; + send_handle().lock().expect("mutex poisoned").send(&p)?; Ok(()) } @@ -89,24 +129,102 @@ macro_rules! recv_loop { loop { match $handle.recv(Some(&mut buf)) { Ok($pkt) => { $body } + // Check if it is shutdowned with WinDivertShutdown() + Err(WinDivertError::Recv(WinDivertRecvError::NoData)) => { + crate::info!("windivert: recv shutdown"); + break; + } Err(e) => { crate::warn!("windivert: recv: {}", e); } } } }; } +fn install_ctrl_handler() { + unsafe extern "system" { + fn SetConsoleCtrlHandler( + handler: Option<unsafe extern "system" fn(u32) -> i32>, + add: i32, + ) -> i32; + } + + unsafe extern "system" fn sighandler(ctrl_type: u32) -> i32 { + // CTRL_C_EVENT=0, CTRL_BREAK_EVENT=1, CTRL_CLOSE_EVENT=2, + // CTRL_LOGOFF_EVENT=5, CTRL_SHUTDOWN_EVENT=6 + match ctrl_type { + 0 | 1 | 5 | 6 => { shutdown_all(); 1 } + 2 => { + shutdown_all(); + + // When the user closes the console window by clicking the 'X' button, + // Windows terminates the process immediately after the thread ends; + // therefore, the program must wait at this point for `close_all` to + // execute. + loop { std::thread::sleep(std::time::Duration::from_millis(30)); } + } + _ => 0, // FALSE + } + } + + let ok = unsafe { SetConsoleCtrlHandler(Some(sighandler), 1) }; + if ok == 0 { + crate::warn!("SetConsoleCtrlHandler() failed"); + } + + crate::info!("cleanup handler installed"); +} + +/// Touch windivert service to avoid Error 1058 on WinDivertOpen() +/// See https://github.com/basil00/WinDivert/issues/406 +fn touch_windivert() { + use windows::Win32::System::Services::*; + use windows::core::w; + + unsafe { + match OpenSCManagerW(None, None, SC_MANAGER_CONNECT) { + Ok(scm) => { + match OpenServiceW(scm, w!("WinDivert"), SERVICE_QUERY_STATUS) { + Ok(svc) => { + crate::info!("Touched WinDivert service"); + + // I really dont know why, but just opening + // the handle is not enough. Performing the + // exact action executed by `sc query + // windivert` resolves the bad state issue + // described on the link above. + Let mut status = SERVICE_STATUS::default(); + let q = QueryServiceStatus(svc, &mut status); + crate::debug!("OpenService ok, query={:?}, state={:?}", + q, status.dwCurrentState); + _ = CloseServiceHandle(svc); + } + Err(e) => { + crate::debug!("No service is good service. OpenService failed: {:?}", e); + } + } + _ = CloseServiceHandle(scm); + } + Err(e) => crate::debug!("OpenSCManager failed: {:?}", e), + } + } +} + pub fn run() -> Result<()> { + touch_windivert(); + let mut buf = Vec::<u8>::with_capacity(super::PACKET_SIZE_CAP); - if opt::fake_autottl() { - let handle = open_handle( + let sniff_thread = if opt::fake_autottl() { + let handle = open_recv_handle( "!outbound and tcp and tcp.SrcPort == 443 and tcp.Syn and tcp.Ack", prelude::WinDivertFlags::new().set_sniff() ); - thread::spawn(move || { recv_loop!(handle, pkt => pkt::put_hop(&pkt.data)); }); - } + Some(thread::spawn(move || { recv_loop!(handle, pkt => pkt::put_hop(&pkt.data)); })) + } else { + None + }; - let divert = open_handle( + let divert = open_recv_handle( concat!( "outbound and tcp and tcp.DstPort == 443", " ", "and tcp.Payload[0] == 22", @@ -125,13 +243,24 @@ pub fn run() -> Result<()> { rejected => send_to_raw_1(&pkt.data)? ) }); + drop(divert); + if let Some(jh) = sniff_thread && jh.join().is_err() { + crate::warn!("join for sniff thread failed: thread paniced"); + } + cleanup_all(); + close_send_handle(); + if let Err(e) = windivert::WinDivert::uninstall() { + crate::warn!("windivert: uninstall failed: {e}"); + } + + Ok(()) } fn service_run() { use std::process::exit; if run().is_err() { - exit(1); + exit(1); } exit(0); } @@ -143,10 +272,8 @@ fn service_main() { .can_stop() .run(|_, command| { match command { - Command::Start => { - std::thread::spawn(|| service_run()); - } - Command::Stop => {} + Command::Start => { std::thread::spawn(|| service_run()); } + Command::Stop => { shutdown_all(); } _ => {} } }) { |
