From 17ea7f983b0291aadd58833c01de801fd9ef9f2a Mon Sep 17 00:00:00 2001 From: Eduard Tolosa Date: Fri, 11 Oct 2024 04:24:02 -0500 Subject: [PATCH] Use new arguments handling implementation. --- src/args.rs | 36 ++++++++++++++ src/main.rs | 137 +++++++++------------------------------------------- 2 files changed, 60 insertions(+), 113 deletions(-) create mode 100644 src/args.rs diff --git a/src/args.rs b/src/args.rs new file mode 100644 index 0000000..23d35f8 --- /dev/null +++ b/src/args.rs @@ -0,0 +1,36 @@ +use clap::Parser; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +pub struct Cli { + #[clap(short, long, default_value = "50")] + /// Number of threads to use + pub threads: usize, + #[clap(long, default_value = "3")] + /// Timeout in seconds + pub timeout: u64, + #[clap(short, long)] + /// Show HTTP status codes + pub show_codes: bool, + #[clap(short, long)] + /// Domain to check - can be omitted if using stdin + pub domain: Option, + #[clap(short, long, default_value = "1")] + /// Number of retries + pub retries: usize, + #[clap(short = 'L', long, default_value = "10")] + /// Maximum number of redirects + pub max_redirects: usize, + #[clap(short, long)] + /// Enable bruteforce mode + pub bruteforce: bool, + #[clap(short, long)] + /// Filter status codes. A comma separated list can be used + pub filter_codes: Option, + #[clap(short, long)] + /// Exclude status codes. A comma separated list can be used + pub exclude_codes: Option, + #[clap(short, long)] + /// Quiet mode. This will suppress all fancy output except for the final results + pub quiet: bool, +} diff --git a/src/main.rs b/src/main.rs index d30f9ab..788e81c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use { - clap::{value_t, App, Arg}, - fhc::{httplib, structs::LibOptions, utils}, + clap::Parser, + fhc::{args, httplib, structs::LibOptions, utils}, std::collections::HashSet, tokio::io::{self, AsyncReadExt}, }; @@ -8,114 +8,16 @@ use { #[tokio::main] async fn main() -> Result<(), Box> { // Eval args - let matches = App::new("FHC") - .version(clap::crate_version!()) - .author("Eduard Tolosa ") - .about("Fast HTTP Checker.") - .arg( - Arg::with_name("threads") - .short("t") - .long("threads") - .takes_value(true) - .help("Number of threads. Default: 50"), - ) - .arg( - Arg::with_name("timeout") - .long("timeout") - .takes_value(true) - .help("Timeout in seconds. Default: 3"), - ) - .arg( - Arg::with_name("show-codes") - .short("s") - .long("show-codes") - .takes_value(false) - .help("Show status codes for discovered hosts."), - ) - .arg( - Arg::with_name("domain") - .short("d") - .long("domain") - .takes_value(true) - .help("Target domain. When it's specified, a wordlist can be used from stdin for bruteforcing."), - ) - .arg( - Arg::with_name("retries") - .short("r") - .long("retries") - .takes_value(true) - .help("Max number of http probes per target."), - ).arg( - Arg::with_name("max-redirects") - .short("L") - .long("max-redirects") - .takes_value(true) - .help("Max number of redirects. Default: 0"), - ) - .arg( - Arg::with_name("bruteforce") - .short("b") - .long("bruteforce") - .takes_value(false) - .help("Bruteforce subdomains."), - ) - .arg( - Arg::with_name("1xx") - .short("1") - .long("1xx") - .takes_value(false) - .help("Show URLs with 100-199 response codes only."), - ) - .arg( - Arg::with_name("2xx") - .short("2") - .long("2xx") - .takes_value(false) - .help("Show URLs with 200-299 response codes only."), - ) - .arg( - Arg::with_name("3xx") - .short("3") - .long("3xx") - .takes_value(false) - .help("Show URLs with 300-399 response codes only."), - ) - .arg( - Arg::with_name("4xx") - .short("4") - .long("4xx") - .takes_value(false) - .help("Show URLs with 400-499 response codes only."), - ) - .arg( - Arg::with_name("5xx") - .short("5") - .long("5xx") - .takes_value(false) - .help("Show URLs with 500-599 response codes only."), - ) - .get_matches(); + let args = args::Cli::parse(); - // Assign values or use defaults - let conditional_response_code = if matches.is_present("1xx") { - 100 - } else if matches.is_present("2xx") { - 200 - } else if matches.is_present("3xx") { - 300 - } else if matches.is_present("4xx") { - 400 - } else if matches.is_present("5xx") { - 500 - } else { - 0 - }; - let threads = value_t!(matches.value_of("threads"), usize).unwrap_or_else(|_| 50); - let retries = value_t!(matches.value_of("retries"), usize).unwrap_or_else(|_| 1); - let timeout = value_t!(matches.value_of("timeout"), u64).unwrap_or_else(|_| 3); - let max_redirects = value_t!(matches.value_of("max-redirects"), usize).unwrap_or_else(|_| 0); + let filter_codes = args.filter_codes; + let exclude_codes = args.exclude_codes; + let threads = args.threads; + let retries = args.retries; + let timeout = args.timeout; + let max_redirects = args.max_redirects; let user_agents_list = utils::user_agents(); - let show_status_codes = matches.is_present("show-codes"); + let show_status_codes = args.show_codes; let client = httplib::return_http_client(timeout, max_redirects); @@ -123,9 +25,9 @@ async fn main() -> Result<(), Box> { let mut hosts = HashSet::new(); - if matches.is_present("domain") { - let domain = value_t!(matches, "domain", String).unwrap(); - if matches.is_present("bruteforce") { + if args.domain.is_some() { + let domain = args.domain.expect("Error getting domain"); + if args.bruteforce { io::stdin().read_to_string(&mut buffer).await?; hosts = buffer .lines() @@ -145,12 +47,21 @@ async fn main() -> Result<(), Box> { user_agents: user_agents_list, retries, threads, - conditional_response_code, + filter_codes, + exclude_codes, show_status_codes, ..Default::default() }; - httplib::return_http_data(&lib_options).await; + if !args.quiet { + if show_status_codes { + println!("DOMAIN,[FINAL_URL],[STATUS_CODE]"); + } else { + println!("DOMAIN,[FINAL_URL]"); + } + } + + let _ = httplib::return_http_data(&lib_options).await; Ok(()) }