Add: dns leak detection

This commit is contained in:
DaZuo0122
2026-01-17 18:45:24 +08:00
parent ccd4a31d21
commit cfa96bde08
30 changed files with 3973 additions and 16 deletions

View File

@@ -0,0 +1,102 @@
mod classify;
mod policy;
mod privacy;
mod report;
mod route;
mod rules;
mod sensor;
use crate::classify::ClassifiedEvent;
use crate::sensor::capture_events;
use std::time::Instant;
use thiserror::Error;
use tracing::debug;
use wtfnet_platform::{FlowOwnerProvider, FlowTuple};
pub use crate::policy::{LeakPolicy, LeakPolicyProfile, PolicySummary};
pub use crate::privacy::{apply_privacy, PrivacyMode};
pub use crate::report::{LeakEvent, LeakReport, LeakSummary, LeakTransport, RouteClass, Severity};
pub use crate::sensor::{iface_diagnostics, IfaceDiag};
#[derive(Debug, Error)]
pub enum DnsLeakError {
#[error("not supported: {0}")]
NotSupported(String),
#[error("io error: {0}")]
Io(String),
#[error("policy error: {0}")]
Policy(String),
}
#[derive(Debug, Clone)]
pub struct LeakWatchOptions {
pub duration_ms: u64,
pub iface: Option<String>,
pub policy: LeakPolicy,
pub privacy: PrivacyMode,
pub include_events: bool,
}
pub async fn watch(
options: LeakWatchOptions,
flow_owner: Option<&dyn FlowOwnerProvider>,
) -> Result<LeakReport, DnsLeakError> {
debug!(
duration_ms = options.duration_ms,
iface = ?options.iface,
include_events = options.include_events,
"dns leak watch start"
);
let start = Instant::now();
let events = capture_events(&options).await?;
let mut leak_events = Vec::new();
for event in events {
let enriched = enrich_event(event, flow_owner).await;
if let Some(decision) = rules::evaluate(&enriched, &options.policy) {
let mut leak_event = report::LeakEvent::from_decision(enriched, decision);
privacy::apply_privacy(&mut leak_event, options.privacy);
leak_events.push(leak_event);
}
}
let summary = LeakSummary::from_events(&leak_events);
let report = LeakReport {
duration_ms: start.elapsed().as_millis() as u64,
policy: options.policy.summary(),
summary,
events: if options.include_events {
leak_events
} else {
Vec::new()
},
};
Ok(report)
}
async fn enrich_event(
event: ClassifiedEvent,
flow_owner: Option<&dyn FlowOwnerProvider>,
) -> report::EnrichedEvent {
let mut enriched = route::enrich_route(event);
if let Some(provider) = flow_owner {
let flow = FlowTuple {
proto: enriched.proto,
src_ip: enriched.src_ip,
src_port: enriched.src_port,
dst_ip: enriched.dst_ip,
dst_port: enriched.dst_port,
};
match provider.owner_of(flow).await {
Ok(result) => {
enriched.owner = result.owner;
enriched.owner_confidence = result.confidence;
enriched.owner_failure = result.failure_reason;
}
Err(err) => {
enriched.owner_failure = Some(err.message);
}
}
}
enriched
}