use {
crate::{
misc::{eval_resolved_or_ip_present, sanitize_target_string, validate_target},
resolvers,
},
clap::{load_yaml, value_t, App},
std::{
collections::{HashMap, HashSet},
env::current_exe,
path::Path,
time::Instant,
},
};
pub struct Args {
pub target: String,
pub file_name: String,
pub postgres_connection: String,
pub discord_webhook: String,
pub slack_webhook: String,
pub telegram_bot_token: String,
pub telegram_webhook: String,
pub telegram_chat_id: String,
pub version: String,
pub current_executable_path: String,
pub spyse_access_token: String,
pub facebook_access_token: String,
pub virustotal_access_token: String,
pub securitytrails_access_token: String,
pub c99_api_key: String,
pub threads: usize,
pub database_checker_counter: usize,
pub commit_to_db_counter: usize,
pub only_resolved: bool,
pub with_ip: bool,
pub with_output: bool,
pub unique_output_flag: bool,
pub monitoring_flag: bool,
pub from_file_flag: bool,
pub quiet_flag: bool,
pub query_database: bool,
pub with_imported_subdomains: bool,
pub enable_dot: bool,
pub ipv6_only: bool,
pub enable_empty_push: bool,
pub check_updates: bool,
pub as_resolver: bool,
pub bruteforce: bool,
pub disable_wildcard_check: bool,
pub custom_resolvers: bool,
pub files: Vec<String>,
pub subdomains: HashSet<String>,
pub wordlists_data: HashSet<String>,
pub wilcard_ips: HashSet<String>,
pub excluded_sources: HashSet<String>,
pub import_subdomains_from: Vec<String>,
pub wordlists: Vec<String>,
pub resolvers: Vec<String>,
pub time_wasted: Instant,
}
pub fn get_args() -> Args {
let yaml = load_yaml!("cli.yml");
let matches = App::from_yaml(yaml)
.version(clap::crate_version!())
.get_matches();
let settings: HashMap<String, String> =
return_settings(&matches, &mut config::Config::default());
Args {
target: {
let target = sanitize_target_string(
value_t!(matches, "target", String).unwrap_or_else(|_| String::new()),
);
if validate_target(&target) {
target
} else {
String::new()
}
},
file_name: if matches.is_present("output") && matches.is_present("target") {
format!(
"{}.txt",
sanitize_target_string(matches.value_of("target").unwrap().to_string())
)
} else if matches.is_present("unique-output") {
matches.value_of("unique-output").unwrap().to_string()
} else {
String::new()
},
files: if matches.is_present("files") {
matches
.values_of("files")
.unwrap()
.map(str::to_owned)
.collect()
} else {
Vec::new()
},
postgres_connection: {
let database_connection = format!(
"postgresql://{}:{}@{}:{}/{}",
value_t!(matches, "postgres-user", String)
.unwrap_or_else(|_| "postgres".to_string()),
value_t!(matches, "postgres-password", String)
.unwrap_or_else(|_| "postgres".to_string()),
value_t!(matches, "postgres-host", String)
.unwrap_or_else(|_| "localhost".to_string()),
value_t!(matches, "postgres-port", usize).unwrap_or_else(|_| 5432),
value_t!(matches, "postgres-database", String).unwrap_or_else(|_| String::new()),
);
return_value_or_default(&settings, "postgres_connection", database_connection)
},
discord_webhook: return_value_or_default(&settings, "discord_webhook", String::new()),
slack_webhook: return_value_or_default(&settings, "slack_webhook", String::new()),
telegram_bot_token: return_value_or_default(&settings, "telegrambot_token", String::new()),
telegram_webhook: String::new(),
telegram_chat_id: return_value_or_default(&settings, "telegram_chat_id", String::new()),
spyse_access_token: return_value_or_default(&settings, "spyse_token", String::new()),
facebook_access_token: return_value_or_default(&settings, "fb_token", String::new()),
virustotal_access_token: return_value_or_default(
&settings,
"virustotal_token",
String::new(),
),
securitytrails_access_token: return_value_or_default(
&settings,
"securitytrails_token",
String::new(),
),
c99_api_key: return_value_or_default(&settings, "c99_api_key", String::new()),
threads: value_t!(matches, "threads", usize).unwrap_or_else(|_| 50),
version: clap::crate_version!().to_string(),
current_executable_path: current_exe().unwrap().display().to_string(),
database_checker_counter: 0,
commit_to_db_counter: 0,
only_resolved: matches.is_present("resolved"),
with_ip: matches.is_present("ip"),
with_output: matches.is_present("output") || matches.is_present("unique-output"),
unique_output_flag: matches.is_present("unique-output"),
monitoring_flag: matches.is_present("monitoring-flag"),
from_file_flag: matches.is_present("files"),
quiet_flag: matches.is_present("quiet"),
with_imported_subdomains: matches.is_present("import-subdomains"),
query_database: matches.is_present("query-database"),
enable_dot: eval_resolved_or_ip_present(
matches.is_present("enable-dot"),
matches.is_present("ip") || matches.is_present("ipv6-only"),
matches.is_present("resolved"),
),
ipv6_only: matches.is_present("ipv6-only"),
enable_empty_push: matches.is_present("enable-empty-push"),
check_updates: matches.is_present("check-updates"),
as_resolver: matches.is_present("as-resolver"),
bruteforce: matches.is_present("wordlists"),
disable_wildcard_check: matches.is_present("no-wildcards"),
custom_resolvers: matches.is_present("custom-resolvers"),
subdomains: HashSet::new(),
wordlists_data: HashSet::new(),
wilcard_ips: HashSet::new(),
excluded_sources: if matches.is_present("exclude-sources") {
return_matches_hashset(&matches, "exclude-sources")
} else {
return_value_or_default(&settings, "exclude_sources", String::new())
.split_whitespace()
.map(str::to_owned)
.collect()
},
import_subdomains_from: if matches.is_present("import-subdomains") {
matches
.values_of("import-subdomains")
.unwrap()
.map(str::to_owned)
.collect()
} else {
Vec::new()
},
wordlists: if matches.is_present("wordlists") {
matches
.values_of("wordlists")
.unwrap()
.map(str::to_owned)
.collect()
} else {
Vec::new()
},
time_wasted: Instant::now(),
resolvers: if matches.is_present("custom-resolvers") {
return_matches_vec(&matches, "custom-resolvers")
} else {
resolvers::return_ipv4_resolvers()
},
}
}
fn return_settings(
matches: &clap::ArgMatches,
settings: &mut config::Config,
) -> HashMap<String, String> {
if matches.is_present("config-file") {
match settings.merge(config::File::with_name(
&value_t!(matches, "config-file", String).unwrap(),
)) {
Ok(settings) => match settings.merge(config::Environment::with_prefix("FINDOMAIN")) {
Ok(settings) => settings
.clone()
.try_into::<HashMap<String, String>>()
.unwrap(),
Err(e) => {
eprintln!("Error merging environment variables into settings: {}\n", e);
std::process::exit(1)
}
},
Err(e) => {
eprintln!("Error reading config file: {}\n", e);
std::process::exit(1)
}
}
} else if Path::new("findomain.toml").exists()
|| Path::new("findomain.json").exists()
|| Path::new("findomain.hjson").exists()
|| Path::new("findomain.ini").exists()
|| Path::new("findomain.yml").exists()
{
match settings.merge(config::File::with_name("findomain")) {
Ok(settings) => match settings.merge(config::Environment::with_prefix("FINDOMAIN")) {
Ok(settings) => settings
.clone()
.try_into::<HashMap<String, String>>()
.unwrap(),
Err(e) => {
eprintln!("Error merging environment variables into settings: {}\n", e);
std::process::exit(1)
}
},
Err(e) => {
eprintln!("Error reading config file: {}\n", e);
std::process::exit(1)
}
}
} else {
match settings.merge(config::Environment::with_prefix("FINDOMAIN")) {
Ok(settings) => settings
.clone()
.try_into::<HashMap<String, String>>()
.unwrap(),
Err(e) => {
eprintln!("Error merging environment variables into settings: {}\n", e);
std::process::exit(1)
}
}
}
}
fn return_value_or_default(
settings: &HashMap<String, String>,
value: &str,
default_value: String,
) -> String {
settings
.get(value)
.unwrap_or_else(|| &default_value)
.to_string()
}
fn return_matches_hashset(matches: &clap::ArgMatches, value: &str) -> HashSet<String> {
if matches.is_present(value) {
matches
.values_of(value)
.unwrap()
.map(str::to_owned)
.collect()
} else {
HashSet::new()
}
}
pub fn return_matches_vec(matches: &clap::ArgMatches, value: &str) -> Vec<String> {
if matches.is_present(value) {
matches
.values_of(value)
.unwrap()
.map(str::to_owned)
.collect()
} else {
Vec::new()
}
}