Allow ports ranges, lists, or both using the --ports option.

Update the code to match the new dependencies versions.
Simplify some functions.
Improve IP discovery speed calling ipv4_lookup() to search only for A records.

Signed-off-by: Edu4rdSHL <edu4rdshl@protonmail.com>
This commit is contained in:
Edu4rdSHL 2021-05-09 02:13:21 -05:00
parent bb8badecf8
commit 3d24415c9a
No known key found for this signature in database
GPG key ID: 3A574A4009F553E5
7 changed files with 74 additions and 131 deletions

View file

@ -50,8 +50,7 @@ pub fn get_args() -> Args {
value_t!(matches, "threads", usize).unwrap_or_else(|_| 50)
},
version: clap::crate_version!().to_string(),
initial_port: value_t!(matches, "initial-port", usize).unwrap_or_else(|_| 1),
last_port: value_t!(matches, "last-port", usize).unwrap_or_else(|_| 65535),
ports: value_t!(matches, "ports", String).unwrap_or_else(|_| String::new()),
with_output: matches.is_present("output") || matches.is_present("unique-output"),
unique_output_flag: matches.is_present("unique-output"),
from_file_flag: matches.is_present("files"),
@ -61,7 +60,7 @@ pub fn get_args() -> Args {
fast_scan: matches.is_present("fast-scan"),
keep_nmap_logs: matches.is_present("keep-nmap-logs"),
files: return_matches_vec(&matches, "files"),
min_rate: value_t!(matches, "min-rate", usize).unwrap_or_else(|_| 30000),
min_rate: value_t!(matches, "min-rate", String).unwrap_or_else(|_| String::new()),
resolvers: if matches.is_present("custom-resolvers") {
return_matches_vec(&matches, "custom-resolvers")
} else {

View file

@ -55,15 +55,9 @@ args:
takes_value: true
multiple: true
- initial-port:
help: Initial port to scan. Default 0.
long: iport
takes_value: true
multiple: false
- last-port:
help: Last port to scan. Default 1000.
long: lport
- ports:
help: 'Ports to scan. You can specify a range of ports, a list, or both. Put them inside double quotes, for example: "22, 80, 443, 1000-5000"'
long: ports
takes_value: true
multiple: false

View file

@ -28,7 +28,7 @@ fn run() -> Result<()> {
.unwrap();
if !arguments.target.is_empty() || !arguments.files.is_empty() {
resolver_engine::async_resolver_all(&mut arguments)
resolver_engine::parallel_resolver_all(&mut arguments)
} else {
error!("Error: Target is empty or invalid!\n");
std::process::exit(1)

View file

@ -1,44 +1,19 @@
use {
log::error,
rand::Rng,
rand::{seq::SliceRandom, thread_rng as rng},
std::net::{IpAddr, Ipv4Addr},
trust_dns_resolver::{
config::{NameServerConfigGroup, ResolverConfig, ResolverOpts},
proto::rr::RecordType,
Resolver,
},
};
pub fn get_records(resolver: &Resolver, domain: &str, record_type: RecordType) -> String {
if let Ok(rdata) = resolver.lookup(&domain, record_type) {
let mut record_data: Vec<String> = Vec::new();
if record_type == RecordType::AAAA {
record_data = rdata
.iter()
.filter_map(|rdata| rdata.as_aaaa())
.map(|ipv6| ipv6.to_string())
.collect();
} else if record_type == RecordType::A {
record_data = rdata
.iter()
.filter_map(|rdata| rdata.as_a())
.map(|ipv4| ipv4.to_string())
.collect();
} else if record_type == RecordType::CNAME {
record_data = rdata
.iter()
.filter_map(|rdata| rdata.as_cname())
.map(|name| {
let name = name.to_string();
name[..name.len() - 1].to_owned()
})
.collect();
}
record_data
.iter()
pub fn get_records(resolver: &Resolver, domain: &str) -> String {
if let Ok(ips) = resolver.ipv4_lookup(domain) {
ips.iter()
.map(|x| x.to_string())
.next()
.expect("Failed retrieving records data.")
.to_owned()
.expect("Failed to get IPV4.")
} else {
String::new()
}
@ -51,9 +26,13 @@ pub fn get_resolver(resolvers_ips: &[Ipv4Addr], opts: &ResolverOpts) -> Resolver
vec![],
NameServerConfigGroup::from_ips_clear(
&[IpAddr::V4(
resolvers_ips[rand::thread_rng().gen_range(0, resolvers_ips.len())],
resolvers_ips
.choose(&mut rng())
.expect("failed to read ipv4 string")
.to_owned(),
)],
53,
false,
),
),
*opts,

View file

@ -111,65 +111,47 @@ pub struct Service {
pub fn get_nmap_data(
filename: &str,
host: &str,
min_rate: usize,
initial_port: usize,
last_port: usize,
min_rate: &str,
ports: &str,
fast_scan: bool,
) -> Result<Nmaprun, serde_xml_rs::Error> {
let ports_range = format!("{}-{}", initial_port, last_port);
let min_rate = min_rate.to_string();
let nmap_args = if fast_scan {
vec![
let mut nmap_args = vec![
"nmap",
"-n",
"--dns-servers",
&NMAP_DNS_RESOLVERS,
"-Pn",
"--host-timeout",
"5m",
"--min-rate",
&min_rate,
"-sS",
"--open",
"-dd",
"-T4",
"--max-retries",
"2",
"3",
"-oX",
filename,
host,
]
} else {
vec![
"nmap",
"-n",
"--dns-servers",
&NMAP_DNS_RESOLVERS,
"-Pn",
"--host-timeout",
"10m",
"-sV",
"--min-rate",
&min_rate,
"-sS",
"-p",
&ports_range,
"--open",
"-dd",
"-T4",
"--max-retries",
"2",
"-oX",
filename,
host,
]
};
];
if !min_rate.is_empty() {
nmap_args.append(&mut vec!["--min-rate", &min_rate])
}
if fast_scan {
nmap_args.append(&mut vec!["--host-timeout", "20m"])
}
if !ports.is_empty() {
nmap_args.append(&mut vec!["-p", &ports])
}
nmap_args.push(host);
match Command::new("nmap").args(&nmap_args).output() {
Ok(_) => {
if Path::new(&filename).exists() && Path::new(&filename).is_file() {
serde_xml_rs::from_str(&std::fs::read_to_string(filename).unwrap_or_default())
} else {
error!("Error executing nmap, possible causes: Nmap is not installed or you need root/administrator permissions. Leaving.\n");
println!();
std::process::exit(1)
}
}

View file

@ -15,7 +15,7 @@ use {
net::Ipv4Addr,
time::Duration,
},
trust_dns_resolver::{config::ResolverOpts, proto::rr::RecordType},
trust_dns_resolver::config::ResolverOpts,
};
lazy_static! {
@ -47,17 +47,17 @@ lazy_static! {
};
}
pub fn async_resolver_all(args: &mut Args) -> Result<()> {
pub fn parallel_resolver_all(args: &mut Args) -> Result<()> {
files::check_full_path(&args.logs_dir);
if !args.quiet_flag {
info!(
"Performing asynchronous resolution for {} targets with {} threads, it will take a while...\n",
"Performing parallel resolution for {} targets with {} threads, it will take a while...\n",
args.targets.len(), args.threads
)
}
let data = async_resolver_engine(&args, args.targets.clone());
let data = parallel_resolver_engine(&args, args.targets.clone());
let mut table = Table::new();
table.set_titles(row![
@ -111,8 +111,9 @@ pub fn async_resolver_all(args: &mut Args) -> Result<()> {
}
}
if args.with_output && !args.targets.is_empty() {
if files::table_to_file(&table, files::return_output_file(&args)).is_err()
if args.with_output
&& !args.targets.is_empty()
&& files::table_to_file(&table, files::return_output_file(&args)).is_err()
&& !args.quiet_flag
{
error!(
@ -120,38 +121,34 @@ pub fn async_resolver_all(args: &mut Args) -> Result<()> {
args.file_name
)
}
}
if !args.quiet_flag {
table.printstd();
}
if ((args.with_output && !args.unique_output_flag) || args.unique_output_flag)
&& !args.quiet_flag
{
if (args.with_output || args.unique_output_flag) && !args.quiet_flag {
info!(
"Job finished in {} seconds.\n",
args.time_wasted.elapsed().as_secs()
);
info!("Logfile saved in {}\n\n", args.file_name);
}
println!();
Ok(())
}
fn async_resolver_engine(args: &Args, targets: HashSet<String>) -> HashMap<String, ResolvData> {
let mut opts = ResolverOpts::default();
opts.timeout = Duration::from_secs(2);
fn parallel_resolver_engine(args: &Args, targets: HashSet<String>) -> HashMap<String, ResolvData> {
let opts = ResolverOpts {
timeout: Duration::from_secs(2),
..Default::default()
};
let resolv_data: HashMap<String, ResolvData> = targets
.par_iter()
.map(|target| {
let fqdn_target = format!("{}.", target);
let mut resolv_data = ResolvData::default();
resolv_data.ip = networking::get_records(
&networking::get_resolver(&RESOLVERS, &opts),
&fqdn_target,
RecordType::A,
);
resolv_data.ip =
networking::get_records(&networking::get_resolver(&RESOLVERS, &opts), &fqdn_target);
(target.to_owned(), resolv_data)
})
.collect();
@ -167,14 +164,7 @@ fn async_resolver_engine(args: &Args, targets: HashSet<String>) -> HashMap<Strin
.par_iter()
.map(|ip| {
let filename = format!("{}/{}.xml", &args.logs_dir, &ip);
match nmap::get_nmap_data(
&filename,
&ip,
args.min_rate,
args.initial_port,
args.last_port,
args.fast_scan,
) {
match nmap::get_nmap_data(&filename, &ip, &args.min_rate, &args.ports, args.fast_scan) {
Ok(nmap_data) => {
nmap_data
.host

View file

@ -10,11 +10,10 @@ pub struct Args {
pub version: String,
pub logs_dir: String,
pub threads: usize,
pub initial_port: usize,
pub last_port: usize,
pub ports: String,
pub with_output: bool,
pub unique_output_flag: bool,
pub min_rate: usize,
pub min_rate: String,
pub from_file_flag: bool,
pub quiet_flag: bool,
pub custom_resolvers: bool,