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)
|
||||
},
|
||||
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 {
|
||||
|
|
|
|||
12
src/cli.yml
12
src/cli.yml
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
82
src/nmap.rs
82
src/nmap.rs
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,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();
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue