Closed ShayBox closed 1 year ago
Currently it's not possible, a dll is the final object when compiling for dynamic linking. Static linking requires a different compilation process that produces a different object (a .lib
different from the one currently being generated when vendoring, which is an import library file)
I'm currently working in a static
feature that overrides the vendored
one and builds the library for static linking. It's taking some time since I don't know too much about msvc and mingw toolchains and compiler/linker arguments, specially since statically compiling mingw it's not officially supported by WinDivert.
Related to the vendored
feature not being enabled by default, you still need to provide the .sys
file. It's not really possible to make it seamless to use in any case, and at that point I think it makes more sense to manually opt in instead of having it as the default behavior. I should add top level documentation about it in both crates though.
Build scripts should not make changes outside OUT_DIR
. For development cargo run
can properly run the binary using either WINDIVERT_PATH
or the vendored compile output. For release it's possible to use WINDIVERT_DLL_OUTPUT
to copy the files to a known path and automate the rest.
I realized after I tried it that the sys file was needed and that the driver has to be signed anyway, so vendored is really only useful to get the library to compile without manually setting up the required files, maybe vendored can additionally download the signed files and extract the sys, or use all the files from the archive instead of compiling which would make it completely autonomous.
I'm looking forward to the static feature, would that still require the sys file as-well? if so, maybe that previous suggestion would be a separate feature instead of being bundled into vendored so it works with static and vendored, if it gets added.
Library build scripts shouldn't but considering WinDivert has no standard install / install location at all and the files are required to be with the binary, the functionality makes sense to have for ease of use as an example file/script to include in your own project, but the above script needs refining.
Downloading in build scripts it's generally a bad practice that might break builds with no apparent reason (outdated/broken link, link updated to point to another incompatible version, isolated build environments with no internet connection or connection issues in general, etc)
Bundling the files with the library also has its cons, being the main one that it makes it harder for the user of the library to replace the used version with a compatible new one. It's something I might be open to add once I give it a good though and research a bit how it might impact the library usage and version management.
The driver sys
file will be required even if statically linking, furthermore it will make it mandatory for the sys
file to be in the same folder as the binary, since windivert dll library code only searches for the driver in the same folder the library itself is located.
For now I'm just gonna use my fork which replaces the WINDIVERT_DLL_OUTPUT
env with a relative OUT_DIR
path (just so I don't have to figure out a way to set an env or set it globally, not ideal) and switch to static if/when that comes out, and I'll move the downloading code from my build.rs to runtime and only have to download the sys file.
If there's a way to prevent the binary from crashing without the DLL file immediately then the downloading of the DLL file could also be handled in runtime (maybe with a download manager?) but for now, it crashes before any execution without the DLL, but not the sys file.
For now I'm just gonna use my fork...
I've just uploaded the WIP changes to feature/static-link, you can add the dependency via git instead of crates.io. It should work with msvc compiler, the gnu part needs more work.
... just so I don't have to figure out a way to set an env or set it globally ...
Most IDE allow for setting env vars for the current project quite easily. Special mention to vscode that allows to set it up for both the terminal and rust-analyzer
If there's a way to prevent the binary from crashing without the DLL file immediately then the downloading of the DLL file could also be handled in runtime
Although the sys file it's only required at runtime, the dll it's required for linking in the build process so it's neither possible nor something I'd consider adding, not even for the sys file.
Special mention to vscode that allows to set it up for both the terminal and rust-analyzer
I wasn't aware you could do that, only that you could set them via tasks, very useful!
The feature works for me, though without the sys file the error no longer says the SYS missing error, just Error: The system cannot find the path specified. (os error 3)
which may not be as helpful.
EDIT: Nevermind, this was an unrelated issue, I needed to delete HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinDivert
This is exactly what I needed 😄 Thank you
I'll keep this open until I finish the work on the static
feature.
When there is a crash and the service does not properly stop, you can use sc stop WinDivert
from an elevated command line to do it manually.
I still cannot decide whether or not I should add a Drop
implementation that closes the handle since it is a fallible function and any error in drop has to be silently ignored.
I found that somewhere else but the windivert service doesn't exist for me
EDIT: Using uninstall can cause the driver to break and require a reboot to work again for some reason, I just let my program crash and it always works again the next time I start it without uninstalling as long as the sys file is in the same location
(Unrelated to static linking)
You wouldn't happen to have an example or library that could help parse HTTP packets, or a resource/video on how to do that, I'm having trouble using httparse
/http_types
to parse packets, I'm trying to sniff http/https json data requests/responses.
use smoltcp::wire::{IpProtocol, IpVersion, Ipv4Packet, TcpPacket};
use windivert::prelude::*;
fn main() {
let filters = [
"ip",
"tcp",
"tcp.PayloadLength > 1",
"(tcp.DstPort == 80 || tcp.DstPort == 443)",
];
let filter = filters.join(" && ");
let priority = 0;
let flags = WinDivertFlags::new().set_sniff();
let windivert = match WinDivert::network(&filter, priority, flags) {
Ok(windivert) => Ok(windivert),
Err(error) => match &error {
WinDivertError::Open(WinDivertOpenError::MissingSYS) => {
download_and_extract_driver().expect("Failed to get driver");
WinDivert::network(&filter, priority, flags)
}
_ => Err(error),
},
}
.expect("Failed to initialize WinDivert<NetworkLayer>");
loop {
// ? I don't know what to set max as for buffer or packet count...
const MAX: usize = u8::MAX as usize;
let mut buffer = [0; MAX];
while let Ok(packets) = windivert.recv_ex(Some(&mut buffer), MAX) {
for packet in packets {
// Theoretically these filters shouldn't be printing anything
// Because WinDivert is pre-filtering the same things
let payload = packet.data;
let Ok(ip_version) = IpVersion::of_packet(&payload) else {
eprintln!("Failed to parse IpVersion");
continue;
};
if ip_version != IpVersion::Ipv4 {
eprintln!("Filtered out non-ipv4 version: {ip_version}");
continue;
}
let Ok(ipv4_packet) = Ipv4Packet::new_checked(&payload) else {
eprintln!("Failed to parse Ipv4Packet");
continue;
};
let protocol = ipv4_packet.next_header();
if protocol != IpProtocol::Tcp {
eprintln!("Filtered out non-tcp protocol: {protocol}");
continue;
}
let payload = ipv4_packet.payload();
let Ok(tcp_packet) = TcpPacket::new_checked(payload) else {
eprintln!("Failed to parse TcpPacket");
continue;
};
let payload = tcp_packet.payload();
let payload_length = payload.len();
if payload_length <= 1 {
eprintln!("Filtered out empty tcp payload: {payload_length}");
continue;
}
let destination_port = tcp_packet.dst_port();
if ![80, 443].contains(&destination_port) {
eprintln!("Filtered out non-http destination port: {destination_port}");
continue;
}
println!("Payload: {payload:?}");
// TODO: Parse HTTP?
}
}
}
}
Released 0.10.0
of the sys crate and 0.6.0
of the wrapper crate with static
feature
Is there a way to statically link to my binary so I don't need the WinDivert.dll with it? I don't understand the build system and linking, it's possible to statically link WinDivert here and here
EDIT: The
vendored
feature should be enabled by default to make the library buildable and usable by default and reduce required intervention and setup by default, the alternative already requires many additional steps to download, extract, compile, and set an environment variable anyway, optionally disabling default features (vendored) is not a concern, you could also leave the logic the same which would allow providingWINDIVERT_PATH
even while vendored is still enabled, not requiring the user to disable or enable features at all.Copying the built
WinDivert.dll
to the binary directory relative toOUT_DIR
(up a few parent directories) by default would make binaries usable by default (in addition to the above change) without any additional intervention (setting an arbitraryWINDIVERT_PATH
environment variable).EDIT2: Maybe a guard to uninstall the driver on drop/crash would be a good improvement.
Unrelated:
I created a
build.rs
for anyone that wants to use the officially built and signed binaries easier