pub mod cli;
pub mod port_scanner;
use chrono::Local;
use clap::Parser;
use dashmap::DashMap;
use env_logger::Builder;
use log::{Level, LevelFilter};
use rand::{seq::SliceRandom, thread_rng};
use std::io::Write;
use std::sync::Arc;
use cli::Cli;
use port_scanner::PortScanner;
#[cfg(windows)]
macro_rules! PATH_SEPARATOR {
() => {
r"\"
};
}
#[cfg(unix)]
macro_rules! PATH_SEPARATOR {
() => {
r"/"
};
}
pub fn get_common_ports_string() -> Result<String, std::str::Utf8Error> {
let file = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
PATH_SEPARATOR!(),
"common_ports.csv"
));
Ok(std::str::from_utf8(file)?.to_string())
}
pub fn init_logger() {
Builder::new()
.format(|buf, record| {
if record.level().eq(&Level::Info) {
writeln!(buf, "{}", record.args())
} else {
writeln!(
buf,
"{} {}: {}",
record.level(),
Local::now().format("%Y-%m-%d %H:%M:%S%.3f"),
record.args()
)
}
})
.filter(None, LevelFilter::Info)
.init();
}
#[allow(clippy::cast_possible_truncation)]
pub fn count_open_ports(hashmap: &Arc<DashMap<u16, bool>>) -> u16 {
hashmap.as_ref().into_iter().filter(|x| *x.value()).count() as u16
}
pub async fn run(cli_option: Option<Cli>) {
init_logger();
let cli = if cli_option.is_some() {
cli_option.unwrap()
} else {
Cli::parse()
};
if !cli.greppable {
log::info!("Scanning on addr {}", cli.addr);
}
let mut port_scanner = PortScanner::from(&cli);
let (start_port, end_port) = cli.get_ports();
let mut ports: Vec<u16> = if cli.common_ports {
let mut v = vec![];
let str = match get_common_ports_string() {
Ok(s) => s,
Err(e) => {
log::error!(
"error reading common ports list that gets embedded during compilation {e}"
);
String::new()
}
};
for i in str.split(",\n") {
if i.parse::<u16>().is_ok() {
v.push(i.parse().unwrap());
}
}
v
} else {
(start_port..=end_port).collect()
};
if !cli.sequential {
ports.shuffle(&mut thread_rng());
}
smol::block_on(async { port_scanner.scan_ports(&ports, Some(&cli)).await });
if !cli.greppable {
log::info!(
"{} open ports found!",
count_open_ports(&port_scanner.port_map)
);
}
}