summaryrefslogtreecommitdiffhomepage
path: root/crates/windivert/src/divert
diff options
context:
space:
mode:
Diffstat (limited to 'crates/windivert/src/divert')
-rw-r--r--crates/windivert/src/divert/blocking.rs353
-rw-r--r--crates/windivert/src/divert/mod.rs229
2 files changed, 582 insertions, 0 deletions
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
+ }
+}