use anyhow::Result;
use windivert::{
WinDivert,
layer::NetworkLayer,
prelude
};
use std::sync::{LazyLock, Mutex};
use std::thread;
use crate::{opt, pkt};
fn open_handle(filter: &str, flags: prelude::WinDivertFlags) -> WinDivert<NetworkLayer> {
use windivert::*;
let h = match WinDivert::network(&filter, 0, flags) {
Ok(h) => {
crate::info!("windivert: open: {}", filter);
h
},
Err(e) => {
crate::error!("windivert: {}", e);
crate::error!("windivert: {}", filter);
std::process::exit(1);
}
};
h
}
pub fn bootstrap() -> Result<()> {
if opt::daemon() {
service_main();
}
Ok(())
}
static SEND_HANDLE: LazyLock<Mutex<WinDivert<NetworkLayer>>> = LazyLock::new(|| {
let flags = prelude::WinDivertFlags::new()
.set_send_only();
Mutex::new(open_handle("false", flags))
});
fn send_to_raw_1(pkt: &[u8]) -> Result<()> {
use windivert::*;
let mut p = unsafe { packet::WinDivertPacket::<NetworkLayer>::new(pkt.to_vec()) };
p.address.set_outbound(true);
p.address.set_ip_checksum(false);
p.address.set_tcp_checksum(false); p.address.set_impostor(true);
SEND_HANDLE.lock().expect("mutex poisoned").send(&p)?;
Ok(())
}
pub fn send_to_raw(pkt: &[u8], _dst: std::net::IpAddr) -> Result<()> {
send_to_raw_1(pkt)
}
macro_rules! recv_loop {
($handle:expr, $pkt:ident => $body:expr) => {
let mut buf = vec![0u8; 65536];
loop {
match $handle.recv(Some(&mut buf)) {
Ok($pkt) => { $body }
Err(e) => { crate::warn!("windivert: recv: {}", e); }
}
}
};
}
pub fn run() -> Result<()> {
let mut buf = Vec::<u8>::with_capacity(super::PACKET_SIZE_CAP);
if opt::fake_autottl() {
let handle = open_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)); });
}
let divert = open_handle(
concat!(
"outbound and tcp and tcp.DstPort == 443",
" ", "and tcp.Payload[0] == 22",
" ", "and tcp.Payload[5] == 1 and !impostor"
),
prelude::WinDivertFlags::new()
);
crate::splash!("{}", super::MESSAGE_AT_RUN);
recv_loop!(divert, pkt => {
crate::handle_packet!(
&pkt.data,
&mut buf,
handled => {},
rejected => send_to_raw_1(&pkt.data)?
)
});
}
fn service_run() {
if run().is_err() {
std::process::exit(1);
}
std::process::exit(0);
}
fn service_main() {
use windows_services::Command;
match windows_services::Service::new()
.can_stop()
.run(|_, command| {
match command {
Command::Start => {
std::thread::spawn(|| service_run());
}
Command::Stop => {}
_ => {}
}
}) {
Ok(_) => {}
Err(e) => {
println!("{e}");
std::process::exit(1);
}
};
}