snortal 1.0.8

Detect captive portal URLs on a Linux network
mod detectors;
mod install;
mod output;
mod types;

#[derive(clap::Parser, Debug)]
#[command(
    name = "snortal",
    about = "Detect captive portal URLs on the current Linux network",
    after_help = "\
Examples:
  snortal                                    Run all detectors with defaults
  snortal --verbose                          Include SSID and empty detectors
  snortal --json                             Machine-readable output
  snortal --timeout 10                       Raise HTTP/subprocess timeout
  snortal -e http://captive.apple.com/       Use a custom probe endpoint
  snortal --gateway-ip 192.168.0.1           Skip route auto-detection
  snortal install-deps                       Install nmcli and wpa_cli",
    version
)]
struct Cli {
    /// Timeout in seconds for HTTP requests and subprocess calls
    #[arg(short, long, default_value = "5")]
    timeout: u64,

    /// Show verbose output including SSID and detectors with no results
    #[arg(short, long)]
    verbose: bool,

    /// Output results as JSON
    #[arg(long)]
    json: bool,

    /// HTTP probe endpoints to use instead of the built-in defaults
    /// (e.g. http://captive.apple.com/ http://connectivity-check.ubuntu.com./)
    #[arg(short = 'e', long = "probe-endpoint", value_name = "URL")]
    probe_endpoints: Vec<String>,

    /// DHCP lease files to check instead of the built-in defaults
    /// (e.g. /var/lib/dhcp/dhclient.leases)
    #[arg(short = 'd', long = "dhcp-file", value_name = "PATH")]
    dhcp_files: Vec<String>,

    /// Gateway IP to probe directly, skipping /proc/net/route auto-detection
    /// (e.g. 192.168.1.1)
    #[arg(short = 'g', long = "gateway-ip", value_name = "IP")]
    gateway_ip: Option<String>,

    #[command(subcommand)]
    command: Option<Subcommand>,
}

#[derive(clap::Subcommand, Debug)]
enum Subcommand {
    /// Install optional system dependencies (nmcli, wpa_cli) using the system package manager
    InstallDeps,
}

#[tokio::main]
async fn main() {
    let cli = <Cli as clap::Parser>::parse();

    if let Some(Subcommand::InstallDeps) = cli.command {
        install::run();
        return;
    }

    let results = detectors::run_all(&detectors::DetectorConfig {
        timeout_secs: cli.timeout,
        verbose: cli.verbose,
        probe_endpoints: cli.probe_endpoints,
        dhcp_files: cli.dhcp_files,
        gateway_ip: cli.gateway_ip,
    })
    .await;
    output::print_results(results.urls, results.notes, cli.json, cli.verbose);
}