Add: flag to make watch keep running
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -2230,6 +2230,15 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.8"
|
||||
@@ -2504,6 +2513,7 @@ dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2 0.6.1",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
|
||||
@@ -46,8 +46,10 @@ wtfn dns query example.com A --transport doh --server 1.1.1.1 --tls-name cloudfl
|
||||
wtfn dns query example.com A --transport dot --server 1.1.1.1 --tls-name cloudflare-dns.com --socks5 socks5://127.0.0.1:9909
|
||||
wtfn dns detect example.com --transport doh --servers 1.1.1.1 --tls-name cloudflare-dns.com
|
||||
wtfn dns watch --duration 10s --filter example.com
|
||||
wtfn dns watch --follow
|
||||
wtfn dns leak status
|
||||
wtfn dns leak watch --duration 10s --profile proxy-stub
|
||||
wtfn dns leak watch --follow
|
||||
wtfn dns leak report report.json
|
||||
|
||||
# TLS
|
||||
|
||||
@@ -12,7 +12,7 @@ clap = { version = "4", features = ["derive"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
time = { version = "0.3", features = ["formatting", "parsing"] }
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] }
|
||||
wtfnet-core = { path = "../wtfnet-core" }
|
||||
wtfnet-calc = { path = "../wtfnet-calc" }
|
||||
wtfnet-geoip = { path = "../wtfnet-geoip" }
|
||||
|
||||
@@ -386,6 +386,8 @@ struct DnsDetectArgs {
|
||||
struct DnsWatchArgs {
|
||||
#[arg(long, default_value = "30s", help = "Capture duration")]
|
||||
duration: String,
|
||||
#[arg(long, help = "Keep running until Ctrl-C")]
|
||||
follow: bool,
|
||||
#[arg(long, help = "Capture interface name")]
|
||||
iface: Option<String>,
|
||||
#[arg(long, help = "Filter by domain substring")]
|
||||
@@ -404,6 +406,8 @@ struct DnsLeakStatusArgs {
|
||||
struct DnsLeakWatchArgs {
|
||||
#[arg(long, default_value = "10s", help = "Capture duration")]
|
||||
duration: String,
|
||||
#[arg(long, help = "Keep running until Ctrl-C")]
|
||||
follow: bool,
|
||||
#[arg(long, help = "Capture interface name")]
|
||||
iface: Option<String>,
|
||||
#[arg(long, help = "Policy profile (full-tunnel|proxy-stub|split)")]
|
||||
@@ -2006,7 +2010,7 @@ async fn handle_dns_detect(cli: &Cli, args: DnsDetectArgs) -> i32 {
|
||||
}
|
||||
|
||||
async fn handle_dns_watch(cli: &Cli, args: DnsWatchArgs) -> i32 {
|
||||
let duration_ms = match parse_duration_ms(&args.duration) {
|
||||
let duration_ms = match resolve_follow_duration(args.follow, &args.duration) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
eprintln!("{err}");
|
||||
@@ -2019,11 +2023,27 @@ async fn handle_dns_watch(cli: &Cli, args: DnsWatchArgs) -> i32 {
|
||||
filter: args.filter.clone(),
|
||||
};
|
||||
|
||||
match wtfnet_dns::watch(options).await {
|
||||
let watch_task = wtfnet_dns::watch(options);
|
||||
let report = if args.follow {
|
||||
tokio::select! {
|
||||
result = watch_task => result,
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
eprintln!("dns watch interrupted by Ctrl-C");
|
||||
return ExitKind::Ok.code();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
watch_task.await
|
||||
};
|
||||
|
||||
match report {
|
||||
Ok(report) => {
|
||||
if cli.json {
|
||||
let meta = Meta::new("wtfnet", env!("CARGO_PKG_VERSION"), false);
|
||||
let mut command_args = vec!["--duration".to_string(), args.duration];
|
||||
if args.follow {
|
||||
command_args.push("--follow".to_string());
|
||||
}
|
||||
if let Some(iface) = args.iface {
|
||||
command_args.push("--iface".to_string());
|
||||
command_args.push(iface);
|
||||
@@ -2154,7 +2174,7 @@ async fn handle_dns_leak_watch(cli: &Cli, args: DnsLeakWatchArgs) -> i32 {
|
||||
if args.iface_diag {
|
||||
return handle_dns_leak_iface_diag(cli).await;
|
||||
}
|
||||
let duration_ms = match parse_duration_ms(&args.duration) {
|
||||
let duration_ms = match resolve_follow_duration(args.follow, &args.duration) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
eprintln!("{err}");
|
||||
@@ -2189,11 +2209,28 @@ async fn handle_dns_leak_watch(cli: &Cli, args: DnsLeakWatchArgs) -> i32 {
|
||||
include_events: !args.summary_only,
|
||||
};
|
||||
|
||||
let report = match wtfnet_dnsleak::watch(options, Some(&*platform.flow_owner)).await {
|
||||
Ok(report) => report,
|
||||
Err(err) => {
|
||||
eprintln!("dns leak watch failed: {err}");
|
||||
return ExitKind::Failed.code();
|
||||
let watch_task = wtfnet_dnsleak::watch(options, Some(&*platform.flow_owner));
|
||||
let report = if args.follow {
|
||||
match tokio::select! {
|
||||
result = watch_task => result,
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
eprintln!("dns leak watch interrupted by Ctrl-C");
|
||||
return ExitKind::Ok.code();
|
||||
}
|
||||
} {
|
||||
Ok(report) => report,
|
||||
Err(err) => {
|
||||
eprintln!("dns leak watch failed: {err}");
|
||||
return ExitKind::Failed.code();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match watch_task.await {
|
||||
Ok(report) => report,
|
||||
Err(err) => {
|
||||
eprintln!("dns leak watch failed: {err}");
|
||||
return ExitKind::Failed.code();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2213,6 +2250,9 @@ async fn handle_dns_leak_watch(cli: &Cli, args: DnsLeakWatchArgs) -> i32 {
|
||||
command_args.push("--iface".to_string());
|
||||
command_args.push(iface);
|
||||
}
|
||||
if args.follow {
|
||||
command_args.push("--follow".to_string());
|
||||
}
|
||||
if let Some(profile) = args.profile {
|
||||
command_args.push("--profile".to_string());
|
||||
command_args.push(profile);
|
||||
@@ -3122,6 +3162,13 @@ fn parse_duration_ms(value: &str) -> Result<u64, String> {
|
||||
Ok(ms)
|
||||
}
|
||||
|
||||
fn resolve_follow_duration(follow: bool, duration: &str) -> Result<u64, String> {
|
||||
if follow {
|
||||
return Ok(u64::MAX / 4);
|
||||
}
|
||||
parse_duration_ms(duration)
|
||||
}
|
||||
|
||||
fn format_answers_geoip(answers: &[DnsAnswerGeoIp]) -> String {
|
||||
if answers.is_empty() {
|
||||
return "-".to_string();
|
||||
|
||||
@@ -42,9 +42,9 @@ This document lists CLI commands and supported flags. Output defaults to text; u
|
||||
## dns
|
||||
- `dns query <domain> <type>` flags: `--server <ip[:port]>`, `--transport <udp|tcp|dot|doh>`, `--tls-name <name>`, `--socks5 <url>`, `--prefer-ipv4`, `--timeout-ms <n>`
|
||||
- `dns detect <domain>` flags: `--servers <csv>`, `--transport <udp|tcp|dot|doh>`, `--tls-name <name>`, `--socks5 <url>`, `--prefer-ipv4`, `--repeat <n>`, `--timeout-ms <n>`
|
||||
- `dns watch` flags: `--duration <Ns|Nms>`, `--iface <name>`, `--filter <pattern>`
|
||||
- `dns watch` flags: `--duration <Ns|Nms>`, `--follow` (run until Ctrl-C), `--iface <name>`, `--filter <pattern>`
|
||||
- `dns leak status` flags: `--profile <full-tunnel|proxy-stub|split>`, `--policy <path>`
|
||||
- `dns leak watch` flags: `--duration <Ns|Nms>`, `--iface <name>`, `--profile <full-tunnel|proxy-stub|split>`, `--policy <path>`, `--privacy <full|redacted|minimal>`, `--out <path>`, `--summary-only`, `--iface-diag` (list capture-capable interfaces)
|
||||
- `dns leak watch` flags: `--duration <Ns|Nms>`, `--follow` (run until Ctrl-C), `--iface <name>`, `--profile <full-tunnel|proxy-stub|split>`, `--policy <path>`, `--privacy <full|redacted|minimal>`, `--out <path>`, `--summary-only`, `--iface-diag` (list capture-capable interfaces)
|
||||
- `dns leak report` flags: `<path>`, `--privacy <full|redacted|minimal>`
|
||||
|
||||
## http
|
||||
|
||||
@@ -156,6 +156,11 @@ Add under `dns` command group:
|
||||
- summary report (human) by default
|
||||
- `--json` returns structured report with events list
|
||||
|
||||
`--follow` keeps the watch running by resolving the duration to a large
|
||||
placeholder (one year in milliseconds) and then racing the watch against
|
||||
`tokio::signal::ctrl_c()`; Ctrl-C returns early with a clean exit code so the
|
||||
outer loop stops.
|
||||
|
||||
## 9) Recommended incremental build plan
|
||||
|
||||
Phase 1 (core passive detection):
|
||||
|
||||
@@ -24,6 +24,8 @@ This document tracks the current DNS leak detector implementation against the de
|
||||
- `dns leak watch`
|
||||
- `dns leak report`
|
||||
- `dns leak watch --iface-diag` (diagnostics for capture-capable interfaces).
|
||||
- `dns leak watch --follow` runs until Ctrl-C by combining a long duration with
|
||||
a `tokio::signal::ctrl_c()` early-exit path.
|
||||
- Interface selection:
|
||||
- per-interface open timeout to avoid capture hangs
|
||||
- ordered scan prefers non-loopback + named ethernet/wlan and interfaces with IPs
|
||||
|
||||
Reference in New Issue
Block a user