diff options
Diffstat (limited to 'crates')
| -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 |
9 files changed, 1409 insertions, 0 deletions
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(), + } + } +} |
