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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
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]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
@@ -2504,6 +2513,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
"socket2 0.6.1",
|
"socket2 0.6.1",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.61.2",
|
"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 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 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 --duration 10s --filter example.com
|
||||||
|
wtfn dns watch --follow
|
||||||
wtfn dns leak status
|
wtfn dns leak status
|
||||||
wtfn dns leak watch --duration 10s --profile proxy-stub
|
wtfn dns leak watch --duration 10s --profile proxy-stub
|
||||||
|
wtfn dns leak watch --follow
|
||||||
wtfn dns leak report report.json
|
wtfn dns leak report report.json
|
||||||
|
|
||||||
# TLS
|
# TLS
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ clap = { version = "4", features = ["derive"] }
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
time = { version = "0.3", features = ["formatting", "parsing"] }
|
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-core = { path = "../wtfnet-core" }
|
||||||
wtfnet-calc = { path = "../wtfnet-calc" }
|
wtfnet-calc = { path = "../wtfnet-calc" }
|
||||||
wtfnet-geoip = { path = "../wtfnet-geoip" }
|
wtfnet-geoip = { path = "../wtfnet-geoip" }
|
||||||
|
|||||||
@@ -386,6 +386,8 @@ struct DnsDetectArgs {
|
|||||||
struct DnsWatchArgs {
|
struct DnsWatchArgs {
|
||||||
#[arg(long, default_value = "30s", help = "Capture duration")]
|
#[arg(long, default_value = "30s", help = "Capture duration")]
|
||||||
duration: String,
|
duration: String,
|
||||||
|
#[arg(long, help = "Keep running until Ctrl-C")]
|
||||||
|
follow: bool,
|
||||||
#[arg(long, help = "Capture interface name")]
|
#[arg(long, help = "Capture interface name")]
|
||||||
iface: Option<String>,
|
iface: Option<String>,
|
||||||
#[arg(long, help = "Filter by domain substring")]
|
#[arg(long, help = "Filter by domain substring")]
|
||||||
@@ -404,6 +406,8 @@ struct DnsLeakStatusArgs {
|
|||||||
struct DnsLeakWatchArgs {
|
struct DnsLeakWatchArgs {
|
||||||
#[arg(long, default_value = "10s", help = "Capture duration")]
|
#[arg(long, default_value = "10s", help = "Capture duration")]
|
||||||
duration: String,
|
duration: String,
|
||||||
|
#[arg(long, help = "Keep running until Ctrl-C")]
|
||||||
|
follow: bool,
|
||||||
#[arg(long, help = "Capture interface name")]
|
#[arg(long, help = "Capture interface name")]
|
||||||
iface: Option<String>,
|
iface: Option<String>,
|
||||||
#[arg(long, help = "Policy profile (full-tunnel|proxy-stub|split)")]
|
#[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 {
|
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,
|
Ok(value) => value,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("{err}");
|
eprintln!("{err}");
|
||||||
@@ -2019,11 +2023,27 @@ async fn handle_dns_watch(cli: &Cli, args: DnsWatchArgs) -> i32 {
|
|||||||
filter: args.filter.clone(),
|
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) => {
|
Ok(report) => {
|
||||||
if cli.json {
|
if cli.json {
|
||||||
let meta = Meta::new("wtfnet", env!("CARGO_PKG_VERSION"), false);
|
let meta = Meta::new("wtfnet", env!("CARGO_PKG_VERSION"), false);
|
||||||
let mut command_args = vec!["--duration".to_string(), args.duration];
|
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 {
|
if let Some(iface) = args.iface {
|
||||||
command_args.push("--iface".to_string());
|
command_args.push("--iface".to_string());
|
||||||
command_args.push(iface);
|
command_args.push(iface);
|
||||||
@@ -2154,7 +2174,7 @@ async fn handle_dns_leak_watch(cli: &Cli, args: DnsLeakWatchArgs) -> i32 {
|
|||||||
if args.iface_diag {
|
if args.iface_diag {
|
||||||
return handle_dns_leak_iface_diag(cli).await;
|
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,
|
Ok(value) => value,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("{err}");
|
eprintln!("{err}");
|
||||||
@@ -2189,11 +2209,28 @@ async fn handle_dns_leak_watch(cli: &Cli, args: DnsLeakWatchArgs) -> i32 {
|
|||||||
include_events: !args.summary_only,
|
include_events: !args.summary_only,
|
||||||
};
|
};
|
||||||
|
|
||||||
let report = match wtfnet_dnsleak::watch(options, Some(&*platform.flow_owner)).await {
|
let watch_task = wtfnet_dnsleak::watch(options, Some(&*platform.flow_owner));
|
||||||
Ok(report) => report,
|
let report = if args.follow {
|
||||||
Err(err) => {
|
match tokio::select! {
|
||||||
eprintln!("dns leak watch failed: {err}");
|
result = watch_task => result,
|
||||||
return ExitKind::Failed.code();
|
_ = 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".to_string());
|
||||||
command_args.push(iface);
|
command_args.push(iface);
|
||||||
}
|
}
|
||||||
|
if args.follow {
|
||||||
|
command_args.push("--follow".to_string());
|
||||||
|
}
|
||||||
if let Some(profile) = args.profile {
|
if let Some(profile) = args.profile {
|
||||||
command_args.push("--profile".to_string());
|
command_args.push("--profile".to_string());
|
||||||
command_args.push(profile);
|
command_args.push(profile);
|
||||||
@@ -3122,6 +3162,13 @@ fn parse_duration_ms(value: &str) -> Result<u64, String> {
|
|||||||
Ok(ms)
|
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 {
|
fn format_answers_geoip(answers: &[DnsAnswerGeoIp]) -> String {
|
||||||
if answers.is_empty() {
|
if answers.is_empty() {
|
||||||
return "-".to_string();
|
return "-".to_string();
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ This document lists CLI commands and supported flags. Output defaults to text; u
|
|||||||
## dns
|
## 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 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 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 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>`
|
- `dns leak report` flags: `<path>`, `--privacy <full|redacted|minimal>`
|
||||||
|
|
||||||
## http
|
## http
|
||||||
|
|||||||
@@ -156,6 +156,11 @@ Add under `dns` command group:
|
|||||||
- summary report (human) by default
|
- summary report (human) by default
|
||||||
- `--json` returns structured report with events list
|
- `--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
|
## 9) Recommended incremental build plan
|
||||||
|
|
||||||
Phase 1 (core passive detection):
|
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 watch`
|
||||||
- `dns leak report`
|
- `dns leak report`
|
||||||
- `dns leak watch --iface-diag` (diagnostics for capture-capable interfaces).
|
- `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:
|
- Interface selection:
|
||||||
- per-interface open timeout to avoid capture hangs
|
- per-interface open timeout to avoid capture hangs
|
||||||
- ordered scan prefers non-loopback + named ethernet/wlan and interfaces with IPs
|
- ordered scan prefers non-loopback + named ethernet/wlan and interfaces with IPs
|
||||||
|
|||||||
Reference in New Issue
Block a user