mirror of
https://github.com/edu4rdshl/unimap.git
synced 2026-07-17 23:24:49 +00:00
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:
parent
bb8badecf8
commit
3d24415c9a
7 changed files with 74 additions and 131 deletions
|
|
@ -50,8 +50,7 @@ pub fn get_args() -> Args {
|
||||||
value_t!(matches, "threads", usize).unwrap_or_else(|_| 50)
|
value_t!(matches, "threads", usize).unwrap_or_else(|_| 50)
|
||||||
},
|
},
|
||||||
version: clap::crate_version!().to_string(),
|
version: clap::crate_version!().to_string(),
|
||||||
initial_port: value_t!(matches, "initial-port", usize).unwrap_or_else(|_| 1),
|
ports: value_t!(matches, "ports", String).unwrap_or_else(|_| String::new()),
|
||||||
last_port: value_t!(matches, "last-port", usize).unwrap_or_else(|_| 65535),
|
|
||||||
with_output: matches.is_present("output") || matches.is_present("unique-output"),
|
with_output: matches.is_present("output") || matches.is_present("unique-output"),
|
||||||
unique_output_flag: matches.is_present("unique-output"),
|
unique_output_flag: matches.is_present("unique-output"),
|
||||||
from_file_flag: matches.is_present("files"),
|
from_file_flag: matches.is_present("files"),
|
||||||
|
|
@ -61,7 +60,7 @@ pub fn get_args() -> Args {
|
||||||
fast_scan: matches.is_present("fast-scan"),
|
fast_scan: matches.is_present("fast-scan"),
|
||||||
keep_nmap_logs: matches.is_present("keep-nmap-logs"),
|
keep_nmap_logs: matches.is_present("keep-nmap-logs"),
|
||||||
files: return_matches_vec(&matches, "files"),
|
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") {
|
resolvers: if matches.is_present("custom-resolvers") {
|
||||||
return_matches_vec(&matches, "custom-resolvers")
|
return_matches_vec(&matches, "custom-resolvers")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
12
src/cli.yml
12
src/cli.yml
|
|
@ -55,15 +55,9 @@ args:
|
||||||
takes_value: true
|
takes_value: true
|
||||||
multiple: true
|
multiple: true
|
||||||
|
|
||||||
- initial-port:
|
- ports:
|
||||||
help: Initial port to scan. Default 0.
|
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: iport
|
long: ports
|
||||||
takes_value: true
|
|
||||||
multiple: false
|
|
||||||
|
|
||||||
- last-port:
|
|
||||||
help: Last port to scan. Default 1000.
|
|
||||||
long: lport
|
|
||||||
takes_value: true
|
takes_value: true
|
||||||
multiple: false
|
multiple: false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ fn run() -> Result<()> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if !arguments.target.is_empty() || !arguments.files.is_empty() {
|
if !arguments.target.is_empty() || !arguments.files.is_empty() {
|
||||||
resolver_engine::async_resolver_all(&mut arguments)
|
resolver_engine::parallel_resolver_all(&mut arguments)
|
||||||
} else {
|
} else {
|
||||||
error!("Error: Target is empty or invalid!\n");
|
error!("Error: Target is empty or invalid!\n");
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,19 @@
|
||||||
use {
|
use {
|
||||||
log::error,
|
log::error,
|
||||||
rand::Rng,
|
rand::{seq::SliceRandom, thread_rng as rng},
|
||||||
std::net::{IpAddr, Ipv4Addr},
|
std::net::{IpAddr, Ipv4Addr},
|
||||||
trust_dns_resolver::{
|
trust_dns_resolver::{
|
||||||
config::{NameServerConfigGroup, ResolverConfig, ResolverOpts},
|
config::{NameServerConfigGroup, ResolverConfig, ResolverOpts},
|
||||||
proto::rr::RecordType,
|
|
||||||
Resolver,
|
Resolver,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn get_records(resolver: &Resolver, domain: &str, record_type: RecordType) -> String {
|
pub fn get_records(resolver: &Resolver, domain: &str) -> String {
|
||||||
if let Ok(rdata) = resolver.lookup(&domain, record_type) {
|
if let Ok(ips) = resolver.ipv4_lookup(domain) {
|
||||||
let mut record_data: Vec<String> = Vec::new();
|
ips.iter()
|
||||||
if record_type == RecordType::AAAA {
|
.map(|x| x.to_string())
|
||||||
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()
|
|
||||||
.next()
|
.next()
|
||||||
.expect("Failed retrieving records data.")
|
.expect("Failed to get IPV4.")
|
||||||
.to_owned()
|
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
|
|
@ -51,9 +26,13 @@ pub fn get_resolver(resolvers_ips: &[Ipv4Addr], opts: &ResolverOpts) -> Resolver
|
||||||
vec![],
|
vec![],
|
||||||
NameServerConfigGroup::from_ips_clear(
|
NameServerConfigGroup::from_ips_clear(
|
||||||
&[IpAddr::V4(
|
&[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,
|
53,
|
||||||
|
false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
*opts,
|
*opts,
|
||||||
|
|
|
||||||
82
src/nmap.rs
82
src/nmap.rs
|
|
@ -111,65 +111,47 @@ pub struct Service {
|
||||||
pub fn get_nmap_data(
|
pub fn get_nmap_data(
|
||||||
filename: &str,
|
filename: &str,
|
||||||
host: &str,
|
host: &str,
|
||||||
min_rate: usize,
|
min_rate: &str,
|
||||||
initial_port: usize,
|
ports: &str,
|
||||||
last_port: usize,
|
|
||||||
fast_scan: bool,
|
fast_scan: bool,
|
||||||
) -> Result<Nmaprun, serde_xml_rs::Error> {
|
) -> Result<Nmaprun, serde_xml_rs::Error> {
|
||||||
let ports_range = format!("{}-{}", initial_port, last_port);
|
|
||||||
let min_rate = min_rate.to_string();
|
let min_rate = min_rate.to_string();
|
||||||
let nmap_args = if fast_scan {
|
let mut nmap_args = vec![
|
||||||
vec![
|
"nmap",
|
||||||
"nmap",
|
"--dns-servers",
|
||||||
"-n",
|
&NMAP_DNS_RESOLVERS,
|
||||||
"--dns-servers",
|
"-Pn",
|
||||||
&NMAP_DNS_RESOLVERS,
|
"-sS",
|
||||||
"-Pn",
|
"--open",
|
||||||
"--host-timeout",
|
"-dd",
|
||||||
"5m",
|
"-T4",
|
||||||
"--min-rate",
|
"--max-retries",
|
||||||
&min_rate,
|
"3",
|
||||||
"-sS",
|
"-oX",
|
||||||
"--open",
|
filename,
|
||||||
"-dd",
|
];
|
||||||
"-T4",
|
|
||||||
"--max-retries",
|
if !min_rate.is_empty() {
|
||||||
"2",
|
nmap_args.append(&mut vec!["--min-rate", &min_rate])
|
||||||
"-oX",
|
}
|
||||||
filename,
|
|
||||||
host,
|
if fast_scan {
|
||||||
]
|
nmap_args.append(&mut vec!["--host-timeout", "20m"])
|
||||||
} else {
|
}
|
||||||
vec![
|
|
||||||
"nmap",
|
if !ports.is_empty() {
|
||||||
"-n",
|
nmap_args.append(&mut vec!["-p", &ports])
|
||||||
"--dns-servers",
|
}
|
||||||
&NMAP_DNS_RESOLVERS,
|
|
||||||
"-Pn",
|
nmap_args.push(host);
|
||||||
"--host-timeout",
|
|
||||||
"10m",
|
|
||||||
"-sV",
|
|
||||||
"--min-rate",
|
|
||||||
&min_rate,
|
|
||||||
"-sS",
|
|
||||||
"-p",
|
|
||||||
&ports_range,
|
|
||||||
"--open",
|
|
||||||
"-dd",
|
|
||||||
"-T4",
|
|
||||||
"--max-retries",
|
|
||||||
"2",
|
|
||||||
"-oX",
|
|
||||||
filename,
|
|
||||||
host,
|
|
||||||
]
|
|
||||||
};
|
|
||||||
match Command::new("nmap").args(&nmap_args).output() {
|
match Command::new("nmap").args(&nmap_args).output() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
if Path::new(&filename).exists() && Path::new(&filename).is_file() {
|
if Path::new(&filename).exists() && Path::new(&filename).is_file() {
|
||||||
serde_xml_rs::from_str(&std::fs::read_to_string(filename).unwrap_or_default())
|
serde_xml_rs::from_str(&std::fs::read_to_string(filename).unwrap_or_default())
|
||||||
} else {
|
} else {
|
||||||
error!("Error executing nmap, possible causes: Nmap is not installed or you need root/administrator permissions. Leaving.\n");
|
error!("Error executing nmap, possible causes: Nmap is not installed or you need root/administrator permissions. Leaving.\n");
|
||||||
|
println!();
|
||||||
std::process::exit(1)
|
std::process::exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use {
|
||||||
net::Ipv4Addr,
|
net::Ipv4Addr,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
},
|
},
|
||||||
trust_dns_resolver::{config::ResolverOpts, proto::rr::RecordType},
|
trust_dns_resolver::config::ResolverOpts,
|
||||||
};
|
};
|
||||||
|
|
||||||
lazy_static! {
|
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);
|
files::check_full_path(&args.logs_dir);
|
||||||
|
|
||||||
if !args.quiet_flag {
|
if !args.quiet_flag {
|
||||||
info!(
|
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
|
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();
|
let mut table = Table::new();
|
||||||
table.set_titles(row![
|
table.set_titles(row![
|
||||||
|
|
@ -111,47 +111,44 @@ pub fn async_resolver_all(args: &mut Args) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.with_output && !args.targets.is_empty() {
|
if args.with_output
|
||||||
if files::table_to_file(&table, files::return_output_file(&args)).is_err()
|
&& !args.targets.is_empty()
|
||||||
&& !args.quiet_flag
|
&& 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",
|
error!(
|
||||||
args.file_name
|
"An error occurred while writing the output file {}.\n",
|
||||||
)
|
args.file_name
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
if !args.quiet_flag {
|
if !args.quiet_flag {
|
||||||
table.printstd();
|
table.printstd();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((args.with_output && !args.unique_output_flag) || args.unique_output_flag)
|
if (args.with_output || args.unique_output_flag) && !args.quiet_flag {
|
||||||
&& !args.quiet_flag
|
|
||||||
{
|
|
||||||
info!(
|
info!(
|
||||||
"Job finished in {} seconds.\n",
|
"Job finished in {} seconds.\n",
|
||||||
args.time_wasted.elapsed().as_secs()
|
args.time_wasted.elapsed().as_secs()
|
||||||
);
|
);
|
||||||
info!("Logfile saved in {}\n\n", args.file_name);
|
info!("Logfile saved in {}\n\n", args.file_name);
|
||||||
}
|
}
|
||||||
|
println!();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn async_resolver_engine(args: &Args, targets: HashSet<String>) -> HashMap<String, ResolvData> {
|
fn parallel_resolver_engine(args: &Args, targets: HashSet<String>) -> HashMap<String, ResolvData> {
|
||||||
let mut opts = ResolverOpts::default();
|
let opts = ResolverOpts {
|
||||||
opts.timeout = Duration::from_secs(2);
|
timeout: Duration::from_secs(2),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
let resolv_data: HashMap<String, ResolvData> = targets
|
let resolv_data: HashMap<String, ResolvData> = targets
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|target| {
|
.map(|target| {
|
||||||
let fqdn_target = format!("{}.", target);
|
let fqdn_target = format!("{}.", target);
|
||||||
let mut resolv_data = ResolvData::default();
|
let mut resolv_data = ResolvData::default();
|
||||||
resolv_data.ip = networking::get_records(
|
resolv_data.ip =
|
||||||
&networking::get_resolver(&RESOLVERS, &opts),
|
networking::get_records(&networking::get_resolver(&RESOLVERS, &opts), &fqdn_target);
|
||||||
&fqdn_target,
|
|
||||||
RecordType::A,
|
|
||||||
);
|
|
||||||
(target.to_owned(), resolv_data)
|
(target.to_owned(), resolv_data)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -167,14 +164,7 @@ fn async_resolver_engine(args: &Args, targets: HashSet<String>) -> HashMap<Strin
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|ip| {
|
.map(|ip| {
|
||||||
let filename = format!("{}/{}.xml", &args.logs_dir, &ip);
|
let filename = format!("{}/{}.xml", &args.logs_dir, &ip);
|
||||||
match nmap::get_nmap_data(
|
match nmap::get_nmap_data(&filename, &ip, &args.min_rate, &args.ports, args.fast_scan) {
|
||||||
&filename,
|
|
||||||
&ip,
|
|
||||||
args.min_rate,
|
|
||||||
args.initial_port,
|
|
||||||
args.last_port,
|
|
||||||
args.fast_scan,
|
|
||||||
) {
|
|
||||||
Ok(nmap_data) => {
|
Ok(nmap_data) => {
|
||||||
nmap_data
|
nmap_data
|
||||||
.host
|
.host
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,10 @@ pub struct Args {
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub logs_dir: String,
|
pub logs_dir: String,
|
||||||
pub threads: usize,
|
pub threads: usize,
|
||||||
pub initial_port: usize,
|
pub ports: String,
|
||||||
pub last_port: usize,
|
|
||||||
pub with_output: bool,
|
pub with_output: bool,
|
||||||
pub unique_output_flag: bool,
|
pub unique_output_flag: bool,
|
||||||
pub min_rate: usize,
|
pub min_rate: String,
|
||||||
pub from_file_flag: bool,
|
pub from_file_flag: bool,
|
||||||
pub quiet_flag: bool,
|
pub quiet_flag: bool,
|
||||||
pub custom_resolvers: bool,
|
pub custom_resolvers: bool,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue