summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock111
-rw-r--r--Cargo.toml5
-rw-r--r--build.ps14
-rw-r--r--crates/windivert/Cargo.toml63
-rw-r--r--crates/windivert/README.md50
-rw-r--r--crates/windivert/src/address.rs421
-rw-r--r--crates/windivert/src/divert/blocking.rs353
-rw-r--r--crates/windivert/src/divert/mod.rs229
-rw-r--r--crates/windivert/src/error.rs129
-rw-r--r--crates/windivert/src/layer.rs46
-rw-r--r--crates/windivert/src/lib.rs28
-rw-r--r--crates/windivert/src/packet.rs90
-rw-r--r--src/main.rs8
-rw-r--r--src/platform.rs6
-rw-r--r--src/platform/windows.rs173
15 files changed, 1681 insertions, 35 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 041930b..091fa7e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 5a5dab8..2ade2e2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/build.ps1 b/build.ps1
index ba61245..4db5977 100644
--- a/build.ps1
+++ b/build.ps1
@@ -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
+
+[![GitHub](https://img.shields.io/github/license/Rubensei/windivert-rust?color=blue)](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`
+ [![crates.io](https://img.shields.io/crates/v/windivert-sys)](https://crates.io/crates/windivert-sys)
+ [![docs](https://docs.rs/windivert-sys/badge.svg)](https://docs.rs/windivert-sys/)
+ [![dependency status](https://deps.rs/repo/github/Rubensei/windivert-rust/status.svg?path=windivert-sys)](https://deps.rs/repo/github/Rubensei/windivert-rust?path=windivert-sys):
+ Crate providing raw bindings to the WinDivert user mode library.
+- `windivert`
+ [![crates.io](https://img.shields.io/crates/v/windivert)](https://crates.io/crates/windivert)
+ [![docs](https://docs.rs/windivert/badge.svg)](https://docs.rs/windivert/)
+ [![dependency status](https://deps.rs/repo/github/Rubensei/windivert-rust/status.svg?path=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(); }
_ => {}
}
}) {