Add: description in help message

This commit is contained in:
DaZuo0122
2026-01-17 19:49:53 +08:00
parent 7f6ee839b2
commit f349d4b4fa
3 changed files with 183 additions and 148 deletions

View File

@@ -16,21 +16,21 @@ use wtfnet_platform::{Platform, PlatformError};
arg_required_else_help = true
)]
struct Cli {
#[arg(long)]
#[arg(long, help = "Emit JSON output")]
json: bool,
#[arg(long)]
#[arg(long, help = "Pretty-print JSON output")]
pretty: bool,
#[arg(long)]
#[arg(long, help = "Disable ANSI colors")]
no_color: bool,
#[arg(long)]
#[arg(long, help = "Reduce stdout output")]
quiet: bool,
#[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count)]
#[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count, help = "Increase log verbosity (-v, -vv)")]
verbose: u8,
#[arg(long)]
#[arg(long, help = "Set log level (error|warn|info|debug|trace)")]
log_level: Option<String>,
#[arg(long)]
#[arg(long, help = "Set log format (text|json)")]
log_format: Option<String>,
#[arg(long)]
#[arg(long, help = "Write logs to a file")]
log_file: Option<PathBuf>,
#[command(subcommand)]
command: Commands,
@@ -38,98 +38,130 @@ struct Cli {
#[derive(Subcommand, Debug)]
enum Commands {
/// System snapshot: interfaces, IPs, routes, DNS
Sys {
#[command(subcommand)]
command: SysCommand,
},
/// Ports and socket ownership
Ports {
#[command(subcommand)]
command: PortsCommand,
},
/// Neighbor table (ARP/NDP)
Neigh {
#[command(subcommand)]
command: NeighCommand,
},
/// Certificate roots and baselines
Cert {
#[command(subcommand)]
command: CertCommand,
},
/// GeoIP lookup helpers
Geoip {
#[command(subcommand)]
command: GeoIpCommand,
},
/// Probing tools (ping/tcping/trace)
Probe {
#[command(subcommand)]
command: ProbeCommand,
},
/// DNS query, detect, watch, and leak detection
Dns {
#[command(subcommand)]
command: DnsCommand,
},
/// Subnet calculator
Calc {
#[command(subcommand)]
command: CalcCommand,
},
/// HTTP head/get diagnostics
Http {
#[command(subcommand)]
command: HttpCommand,
},
/// TLS handshake and certificate analysis
Tls {
#[command(subcommand)]
command: TlsCommand,
},
/// Local network discovery (mDNS/SSDP/LLMNR/NBNS)
Discover {
#[command(subcommand)]
command: DiscoverCommand,
},
/// Bundle a diagnostic report
Diag(DiagArgs),
}
#[derive(Subcommand, Debug)]
enum SysCommand {
/// List network interfaces
Ifaces,
/// Show IP addresses
Ip(SysIpArgs),
/// Show route table
Route(SysRouteArgs),
/// Show DNS configuration
Dns,
}
#[derive(Subcommand, Debug)]
enum PortsCommand {
/// List listening sockets
Listen(PortsListenArgs),
/// Find socket owners for a port
Who(PortsWhoArgs),
/// List active TCP connections
Conns(PortsConnsArgs),
}
#[derive(Subcommand, Debug)]
enum NeighCommand {
/// List ARP/NDP neighbors
List(NeighListArgs),
}
#[derive(Subcommand, Debug)]
enum CertCommand {
/// List trusted root certificates
Roots,
/// Write a baseline file of trusted roots
Baseline(CertBaselineArgs),
/// Diff trusted roots against a baseline
Diff(CertDiffArgs),
}
#[derive(Subcommand, Debug)]
enum GeoIpCommand {
/// Lookup GeoIP for an IP address
Lookup(GeoIpLookupArgs),
/// Show GeoIP database status
Status,
}
#[derive(Subcommand, Debug)]
enum ProbeCommand {
/// ICMP ping
Ping(ProbePingArgs),
/// TCP ping (connect timing)
Tcping(ProbeTcpingArgs),
/// Traceroute
Trace(ProbeTraceArgs),
}
#[derive(Subcommand, Debug)]
enum DnsCommand {
/// DNS query
Query(DnsQueryArgs),
/// DNS poisoning detection (active)
Detect(DnsDetectArgs),
/// DNS passive watch (pcap)
Watch(DnsWatchArgs),
/// DNS leak detection
Leak {
#[command(subcommand)]
command: DnsLeakCommand,
@@ -138,345 +170,381 @@ enum DnsCommand {
#[derive(Subcommand, Debug)]
enum DnsLeakCommand {
/// Show current policy and interface snapshot
Status(DnsLeakStatusArgs),
/// Passive leak detection watch
Watch(DnsLeakWatchArgs),
/// Summarize a saved leak report
Report(DnsLeakReportArgs),
}
#[derive(Subcommand, Debug)]
enum CalcCommand {
/// Subnet info
Subnet(CalcSubnetArgs),
/// Check CIDR containment
Contains(CalcContainsArgs),
/// Check CIDR overlap
Overlap(CalcOverlapArgs),
/// Summarize CIDRs
Summarize(CalcSummarizeArgs),
}
#[derive(Subcommand, Debug)]
enum HttpCommand {
/// HTTP HEAD request
Head(HttpRequestArgs),
/// HTTP GET request
Get(HttpRequestArgs),
}
#[derive(Subcommand, Debug)]
enum TlsCommand {
/// TLS handshake summary
Handshake(TlsArgs),
/// TLS certificate details
Cert(TlsArgs),
/// TLS verification
Verify(TlsArgs),
/// TLS ALPN negotiation
Alpn(TlsArgs),
}
#[derive(Subcommand, Debug)]
enum DiscoverCommand {
/// mDNS discovery
Mdns(DiscoverMdnsArgs),
/// SSDP discovery
Ssdp(DiscoverSsdpArgs),
/// LLMNR discovery
Llmnr(DiscoverLlmnrArgs),
/// NBNS discovery
Nbns(DiscoverNbnsArgs),
}
#[derive(Parser, Debug, Clone)]
struct SysIpArgs {
#[arg(long)]
#[arg(long, help = "Show all addresses (including link-local)")]
all: bool,
#[arg(long)]
#[arg(long, help = "Filter by interface name")]
iface: Option<String>,
}
#[derive(Parser, Debug, Clone)]
struct SysRouteArgs {
#[arg(long)]
#[arg(long, help = "Show IPv4 routes")]
ipv4: bool,
#[arg(long)]
#[arg(long, help = "Show IPv6 routes")]
ipv6: bool,
#[arg(long)]
#[arg(long, help = "Filter routes by destination")]
to: Option<String>,
}
#[derive(Parser, Debug, Clone)]
struct PortsListenArgs {
#[arg(long)]
#[arg(long, help = "Show TCP listeners")]
tcp: bool,
#[arg(long)]
#[arg(long, help = "Show UDP listeners")]
udp: bool,
#[arg(long)]
#[arg(long, help = "Filter by port")]
port: Option<u16>,
}
#[derive(Parser, Debug, Clone)]
struct PortsWhoArgs {
#[arg(help = "Target port number")]
target: String,
}
#[derive(Parser, Debug, Clone)]
struct PortsConnsArgs {
#[arg(long)]
#[arg(long, help = "Show top N remote endpoints")]
top: Option<usize>,
#[arg(long)]
#[arg(long, help = "Group by process")]
by_process: bool,
}
#[derive(Parser, Debug, Clone)]
struct NeighListArgs {
#[arg(long)]
#[arg(long, help = "Show IPv4 neighbors")]
ipv4: bool,
#[arg(long)]
#[arg(long, help = "Show IPv6 neighbors")]
ipv6: bool,
#[arg(long)]
#[arg(long, help = "Filter by interface name")]
iface: Option<String>,
}
#[derive(Parser, Debug, Clone)]
struct GeoIpLookupArgs {
#[arg(help = "Target IP address")]
target: String,
}
#[derive(Parser, Debug, Clone)]
struct CertBaselineArgs {
#[arg(help = "Path to write baseline JSON")]
path: PathBuf,
}
#[derive(Parser, Debug, Clone)]
struct CertDiffArgs {
#[arg(help = "Path to baseline JSON")]
path: PathBuf,
}
#[derive(Parser, Debug, Clone)]
struct ProbePingArgs {
#[arg(help = "Target hostname or IP")]
target: String,
#[arg(long, default_value_t = 4)]
#[arg(long, default_value_t = 4, help = "Ping count")]
count: u32,
#[arg(long, default_value_t = 800)]
#[arg(long, default_value_t = 800, help = "Timeout per ping (ms)")]
timeout_ms: u64,
#[arg(long, default_value_t = 200)]
#[arg(long, default_value_t = 200, help = "Interval between pings (ms)")]
interval_ms: u64,
#[arg(long)]
#[arg(long, help = "Disable GeoIP enrichment")]
no_geoip: bool,
}
#[derive(Parser, Debug, Clone)]
struct ProbeTcpingArgs {
#[arg(help = "Target host:port")]
target: String,
#[arg(long, default_value_t = 4)]
#[arg(long, default_value_t = 4, help = "Ping count")]
count: u32,
#[arg(long, default_value_t = 800)]
#[arg(long, default_value_t = 800, help = "Timeout per connect (ms)")]
timeout_ms: u64,
#[arg(long)]
#[arg(long, help = "SOCKS5 proxy URL")]
socks5: Option<String>,
#[arg(long)]
#[arg(long, help = "Prefer IPv4 resolution")]
prefer_ipv4: bool,
#[arg(long)]
#[arg(long, help = "Disable GeoIP enrichment")]
no_geoip: bool,
}
#[derive(Parser, Debug, Clone)]
struct ProbeTraceArgs {
#[arg(help = "Target host:port")]
target: String,
#[arg(long, default_value_t = 30)]
#[arg(long, default_value_t = 30, help = "Maximum hops")]
max_hops: u8,
#[arg(long, default_value_t = 3)]
#[arg(long, default_value_t = 3, help = "Probes per hop")]
per_hop: u32,
#[arg(long, default_value_t = 800)]
#[arg(long, default_value_t = 800, help = "Timeout per hop (ms)")]
timeout_ms: u64,
#[arg(long)]
#[arg(long, help = "Use UDP traceroute")]
udp: bool,
#[arg(long, default_value_t = 33434)]
#[arg(long, default_value_t = 33434, help = "Destination port for UDP/TCP trace")]
port: u16,
#[arg(long)]
#[arg(long, help = "Reverse DNS lookup per hop")]
rdns: bool,
#[arg(long)]
#[arg(long, help = "Disable GeoIP enrichment")]
no_geoip: bool,
}
#[derive(Parser, Debug, Clone)]
struct DnsQueryArgs {
#[arg(help = "Domain name to query")]
domain: String,
#[arg(help = "Record type (A, AAAA, MX, TXT, ...)")]
record_type: String,
#[arg(long)]
#[arg(long, help = "DNS server IP[:port]")]
server: Option<String>,
#[arg(long, default_value = "udp")]
#[arg(long, default_value = "udp", help = "Transport (udp|tcp|dot|doh)")]
transport: String,
#[arg(long)]
#[arg(long, help = "TLS server name for DoT/DoH")]
tls_name: Option<String>,
#[arg(long)]
#[arg(long, help = "SOCKS5 proxy URL")]
socks5: Option<String>,
#[arg(long)]
#[arg(long, help = "Prefer IPv4 resolution")]
prefer_ipv4: bool,
#[arg(long, default_value_t = 2000)]
#[arg(long, default_value_t = 2000, help = "Timeout (ms)")]
timeout_ms: u64,
}
#[derive(Parser, Debug, Clone)]
struct DnsDetectArgs {
#[arg(help = "Domain name to test")]
domain: String,
#[arg(long)]
#[arg(long, help = "Comma-separated DNS servers")]
servers: Option<String>,
#[arg(long, default_value = "udp")]
#[arg(long, default_value = "udp", help = "Transport (udp|tcp|dot|doh)")]
transport: String,
#[arg(long)]
#[arg(long, help = "TLS server name for DoT/DoH")]
tls_name: Option<String>,
#[arg(long)]
#[arg(long, help = "SOCKS5 proxy URL")]
socks5: Option<String>,
#[arg(long)]
#[arg(long, help = "Prefer IPv4 resolution")]
prefer_ipv4: bool,
#[arg(long, default_value_t = 3)]
#[arg(long, default_value_t = 3, help = "Repeat count per server")]
repeat: u32,
#[arg(long, default_value_t = 2000)]
#[arg(long, default_value_t = 2000, help = "Timeout (ms)")]
timeout_ms: u64,
}
#[derive(Parser, Debug, Clone)]
struct DnsWatchArgs {
#[arg(long, default_value = "30s")]
#[arg(long, default_value = "30s", help = "Capture duration")]
duration: String,
#[arg(long)]
#[arg(long, help = "Capture interface name")]
iface: Option<String>,
#[arg(long)]
#[arg(long, help = "Filter by domain substring")]
filter: Option<String>,
}
#[derive(Parser, Debug, Clone)]
struct DnsLeakStatusArgs {
#[arg(long)]
#[arg(long, help = "Policy profile (full-tunnel|proxy-stub|split)")]
profile: Option<String>,
#[arg(long)]
#[arg(long, help = "Path to policy JSON")]
policy: Option<PathBuf>,
}
#[derive(Parser, Debug, Clone)]
struct DnsLeakWatchArgs {
#[arg(long, default_value = "10s")]
#[arg(long, default_value = "10s", help = "Capture duration")]
duration: String,
#[arg(long)]
#[arg(long, help = "Capture interface name")]
iface: Option<String>,
#[arg(long)]
#[arg(long, help = "Policy profile (full-tunnel|proxy-stub|split)")]
profile: Option<String>,
#[arg(long)]
#[arg(long, help = "Path to policy JSON")]
policy: Option<PathBuf>,
#[arg(long, default_value = "redacted")]
#[arg(long, default_value = "redacted", help = "Privacy mode (full|redacted|minimal)")]
privacy: String,
#[arg(long)]
#[arg(long, help = "Write JSON report to file")]
out: Option<PathBuf>,
#[arg(long)]
#[arg(long, help = "Only print summary (no events)")]
summary_only: bool,
#[arg(long)]
#[arg(long, help = "List capture-capable interfaces and exit")]
iface_diag: bool,
}
#[derive(Parser, Debug, Clone)]
struct DnsLeakReportArgs {
#[arg(help = "Path to leak report JSON")]
path: PathBuf,
#[arg(long, default_value = "redacted")]
#[arg(long, default_value = "redacted", help = "Privacy mode (full|redacted|minimal)")]
privacy: String,
}
#[derive(Parser, Debug, Clone)]
struct CalcSubnetArgs {
#[arg(help = "CIDR or IP + mask")]
input: Vec<String>,
}
#[derive(Parser, Debug, Clone)]
struct CalcContainsArgs {
#[arg(help = "CIDR A")]
a: String,
#[arg(help = "CIDR B")]
b: String,
}
#[derive(Parser, Debug, Clone)]
struct CalcOverlapArgs {
#[arg(help = "CIDR A")]
a: String,
#[arg(help = "CIDR B")]
b: String,
}
#[derive(Parser, Debug, Clone)]
struct CalcSummarizeArgs {
#[arg(help = "CIDR list to summarize")]
cidrs: Vec<String>,
}
#[derive(Parser, Debug, Clone)]
struct HttpRequestArgs {
#[arg(help = "Target URL")]
url: String,
#[arg(long, default_value_t = 3000)]
#[arg(long, default_value_t = 3000, help = "Request timeout (ms)")]
timeout_ms: u64,
#[arg(long)]
#[arg(long, help = "Follow redirects (limit)")]
follow_redirects: Option<u32>,
#[arg(long)]
#[arg(long, help = "Include response headers")]
show_headers: bool,
#[arg(long)]
#[arg(long, help = "Include response body")]
show_body: bool,
#[arg(long, default_value_t = 8192)]
#[arg(long, default_value_t = 8192, help = "Max body bytes")]
max_body_bytes: usize,
#[arg(long)]
#[arg(long, help = "Force HTTP/1.1 only")]
http1_only: bool,
#[arg(long)]
#[arg(long, help = "Force HTTP/2 prior knowledge")]
http2_only: bool,
#[arg(long)]
#[arg(long, help = "Enable HTTP/3 (feature gated)")]
http3: bool,
#[arg(long)]
#[arg(long, help = "Require HTTP/3 (feature gated)")]
http3_only: bool,
#[arg(long)]
#[arg(long, help = "Enable GeoIP enrichment")]
geoip: bool,
#[arg(long)]
#[arg(long, help = "SOCKS5 proxy URL")]
socks5: Option<String>,
}
#[derive(Parser, Debug, Clone)]
struct TlsArgs {
#[arg(help = "Target host:port")]
target: String,
#[arg(long)]
#[arg(long, help = "Override SNI")]
sni: Option<String>,
#[arg(long)]
#[arg(long, help = "ALPN protocols (comma-separated)")]
alpn: Option<String>,
#[arg(long, default_value_t = 3000)]
#[arg(long, default_value_t = 3000, help = "Timeout (ms)")]
timeout_ms: u64,
#[arg(long)]
#[arg(long, help = "Skip TLS verification")]
insecure: bool,
#[arg(long)]
#[arg(long, help = "SOCKS5 proxy URL")]
socks5: Option<String>,
#[arg(long)]
#[arg(long, help = "Prefer IPv4 resolution")]
prefer_ipv4: bool,
#[arg(long)]
#[arg(long, help = "Show X.509 extensions")]
show_extensions: bool,
#[arg(long)]
#[arg(long, help = "Show OCSP stapling if available")]
ocsp: bool,
}
#[derive(Parser, Debug, Clone)]
struct DiscoverMdnsArgs {
#[arg(long, default_value = "3s")]
#[arg(long, default_value = "3s", help = "Capture duration")]
duration: String,
#[arg(long)]
#[arg(long, help = "Service type filter")]
service: Option<String>,
}
#[derive(Parser, Debug, Clone)]
struct DiscoverSsdpArgs {
#[arg(long, default_value = "3s")]
#[arg(long, default_value = "3s", help = "Capture duration")]
duration: String,
}
#[derive(Parser, Debug, Clone)]
struct DiscoverLlmnrArgs {
#[arg(long, default_value = "3s")]
#[arg(long, default_value = "3s", help = "Capture duration")]
duration: String,
#[arg(long)]
#[arg(long, help = "Query name (default: wpad)")]
name: Option<String>,
}
#[derive(Parser, Debug, Clone)]
struct DiscoverNbnsArgs {
#[arg(long, default_value = "3s")]
#[arg(long, default_value = "3s", help = "Capture duration")]
duration: String,
}
#[derive(Parser, Debug, Clone)]
struct DiagArgs {
#[arg(long)]
#[arg(long, help = "Write JSON report to file")]
out: Option<PathBuf>,
#[arg(long)]
#[arg(long, help = "Write zip bundle to file")]
bundle: Option<PathBuf>,
#[arg(long)]
#[arg(long, help = "Run DNS detect on domain")]
dns_detect: Option<String>,
#[arg(long, default_value_t = 2000)]
#[arg(long, default_value_t = 2000, help = "DNS detect timeout (ms)")]
dns_timeout_ms: u64,
#[arg(long, default_value_t = 3)]
#[arg(long, default_value_t = 3, help = "DNS detect repeat count")]
dns_repeat: u32,
}