diff --git a/src/input.rs b/src/input.rs index a38752118..de74f48aa 100644 --- a/src/input.rs +++ b/src/input.rs @@ -154,6 +154,10 @@ pub struct Opts { /// UDP scanning mode, finds UDP ports that send back responses #[arg(long)] pub udp: bool, + + /// Scan interval per port (in seconds) + #[structopt(long, default_value = "0")] + pub interval: u64, } #[cfg(not(tarpaulin_include))] @@ -242,6 +246,7 @@ impl Default for Opts { config_path: None, exclude_ports: None, udp: false, + interval: 0, } } } @@ -267,6 +272,7 @@ pub struct Config { scripts: Option, exclude_ports: Option>, udp: Option, + interval: Option, } #[cfg(not(tarpaulin_include))] @@ -341,6 +347,7 @@ mod tests { scripts: None, exclude_ports: None, udp: Some(false), + interval: None, } } } diff --git a/src/main.rs b/src/main.rs index 181831fd4..17654a47e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -88,6 +88,8 @@ fn main() { // // Added by brendanglancy - 5/19/2024: // udp is an option to do a udp scan + // Added by onsali - 09/12/2024: + // interval is an interval defined in seconds between each port scan let scanner = Scanner::new( &ips, batch_size, @@ -98,6 +100,7 @@ fn main() { opts.accessible, opts.exclude_ports.unwrap_or_default(), opts.udp, + Duration::from_secs(opts.interval), ); debug!("Scanner finished building: {:?}", scanner); @@ -201,16 +204,16 @@ fn main() { fn print_opening(opts: &Opts) { debug!("Printing opening"); let s = r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-. -| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| | -| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ | -`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-' -The Modern Day Port Scanner."#; + | {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| | + | .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ | + `-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-' + The Modern Day Port Scanner."#; println!("{}", s.gradient(Color::Green).bold()); let info = r#"________________________________________ -: http://discord.skerritt.blog : -: https://github.com/RustScan/RustScan : - --------------------------------------"#; + : http://discord.skerritt.blog : + : https://github.com/RustScan/RustScan : + --------------------------------------"#; println!("{}", info.gradient(Color::Yellow).bold()); funny_opening!(); diff --git a/src/scanner/mod.rs b/src/scanner/mod.rs index a4a0fb916..91ce05346 100644 --- a/src/scanner/mod.rs +++ b/src/scanner/mod.rs @@ -8,6 +8,7 @@ use socket_iterator::SocketIterator; use async_std::net::TcpStream; use async_std::prelude::*; +use async_std::task::sleep; use async_std::{io, net::UdpSocket}; use colored::Colorize; use futures::stream::FuturesUnordered; @@ -25,6 +26,8 @@ use std::{ /// batch_size is how many ports at a time should be scanned /// Timeout is the time RustScan should wait before declaring a port closed. As datatype Duration. /// greppable is whether or not RustScan should print things, or wait until the end to print only the ip and open ports. +/// Added by ons - 12/09/2024: +/// interval is a user defined cli flag for time between scans on each port /// Added by wasuaje - 01/26/2024: /// exclude_ports is an exclusion port list #[cfg(not(tarpaulin_include))] @@ -39,6 +42,7 @@ pub struct Scanner { accessible: bool, exclude_ports: Vec, udp: bool, + interval: Duration, } // Allowing too many arguments for clippy. @@ -54,6 +58,7 @@ impl Scanner { accessible: bool, exclude_ports: Vec, udp: bool, + interval: Duration, ) -> Self { Self { batch_size, @@ -65,6 +70,7 @@ impl Scanner { accessible, exclude_ports, udp, + interval, } } @@ -81,41 +87,46 @@ impl Scanner { .filter(|&port| !self.exclude_ports.contains(port)) .copied() .collect(); - let mut socket_iterator: SocketIterator = SocketIterator::new(&self.ips, &ports); + let mut open_sockets: Vec = Vec::new(); - let mut ftrs = FuturesUnordered::new(); let mut errors: HashSet = HashSet::new(); let udp_map = get_parsed_data(); - for _ in 0..self.batch_size { - if let Some(socket) = socket_iterator.next() { - ftrs.push(self.scan_socket(socket, udp_map.clone())); - } else { - break; - } - } - debug!("Start scanning sockets. \nBatch size {}\nNumber of ip-s {}\nNumber of ports {}\nTargets all together {} ", self.batch_size, self.ips.len(), &ports.len(), (self.ips.len() * ports.len())); - while let Some(result) = ftrs.next().await { - if let Some(socket) = socket_iterator.next() { + // Scan one port at a time + for port in ports { + let mut ftrs = FuturesUnordered::new(); + + // Scan this port across all IPs simultaneously + for ip in &self.ips { + let socket = SocketAddr::new(*ip, port); ftrs.push(self.scan_socket(socket, udp_map.clone())); } - match result { - Ok(socket) => open_sockets.push(socket), - Err(e) => { - let error_string = e.to_string(); - if errors.len() < self.ips.len() * 1000 { - errors.insert(error_string); + // Wait for all IPs to complete for this port + while let Some(result) = ftrs.next().await { + match result { + Ok(socket) => open_sockets.push(socket), + Err(e) => { + let error_string = e.to_string(); + if errors.len() < self.ips.len() * 1000 { + errors.insert(error_string); + } } } } + + // Wait for interval before moving to next port + if self.interval > Duration::from_secs(0) { + sleep(self.interval).await; + } } + debug!("Typical socket connection errors {:?}", errors); debug!("Open Sockets found: {:?}", &open_sockets); open_sockets