Skip to content

Commit

Permalink
Allow ports ranges, lists, or both using the --ports option.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Edu4rdSHL committed May 9, 2021
1 parent bb8bade commit 3d24415
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 131 deletions.
5 changes: 2 additions & 3 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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 {
Expand Down
12 changes: 3 additions & 9 deletions src/cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
43 changes: 11 additions & 32 deletions src/networking.rs
Original file line number Diff line number Diff line change
@@ -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()
}
Expand All @@ -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,
Expand Down
82 changes: 32 additions & 50 deletions src/nmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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![
"nmap",
"-n",
"--dns-servers",
&NMAP_DNS_RESOLVERS,
"-Pn",
"--host-timeout",
"5m",
"--min-rate",
&min_rate,
"-sS",
"--open",
"-dd",
"-T4",
"--max-retries",
"2",
"-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,
]
};
let mut nmap_args = vec![
"nmap",
"--dns-servers",
&NMAP_DNS_RESOLVERS,
"-Pn",
"-sS",
"--open",
"-dd",
"-T4",
"--max-retries",
"3",
"-oX",
filename,
];

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)
}
}
Expand Down
56 changes: 23 additions & 33 deletions src/resolver_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use {
net::Ipv4Addr,
time::Duration,
},
trust_dns_resolver::{config::ResolverOpts, proto::rr::RecordType},
trust_dns_resolver::config::ResolverOpts,
};

lazy_static! {
Expand Down Expand Up @@ -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![
Expand Down Expand Up @@ -111,47 +111,44 @@ 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()
&& !args.quiet_flag
{
error!(
"An error occurred while writing the output file {}.\n",
args.file_name
)
}
if args.with_output
&& !args.targets.is_empty()
&& files::table_to_file(&table, files::return_output_file(&args)).is_err()
&& !args.quiet_flag
{
error!(
"An error occurred while writing the output file {}.\n",
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();
Expand All @@ -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
Expand Down
5 changes: 2 additions & 3 deletions src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 3d24415

Please # to comment.