diff --git a/README.md b/README.md index 495e4abaf..b7462a0cf 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,8 @@ OPTIONS: ascending order while the "random" option will scan ports randomly [default: serial] [possible values: Serial, Random] -t, --timeout The timeout in milliseconds before a port is assumed to be closed [default: 1500] + --tries The number of tries before a port is assumed to be closed. If set to 0, rustscan + will correct it to 1 [default: 1] -u, --ulimit Automatically ups the ULIMIT with the value you provided ARGS: @@ -260,7 +262,7 @@ Your operating system may not support this, but it is worth it to play around an ## Configuration file -This binary accepts a configuration file that is read from the home directory of the user. It follows the TOML format +This binary accepts a configuration file, named `.rustscan.toml`, that is read from the home directory of the user. It follows the TOML format and accepts the following fields: - `addresses` @@ -272,6 +274,7 @@ and accepts the following fields: - `greppable` - `batch-size` - `timeout` +- `tries` - `ulimit` ### Format example @@ -286,6 +289,7 @@ accessible = true scan_order = "Serial" batch_size = 1000 timeout = 1000 +tries = 3 ulimit = 1000 ``` diff --git a/src/input.rs b/src/input.rs index ead4088d7..c1f3a9b2a 100644 --- a/src/input.rs +++ b/src/input.rs @@ -96,6 +96,11 @@ pub struct Opts { #[structopt(short, long, default_value = "1500")] pub timeout: u32, + /// The number of tries before a port is assumed to be closed. + /// If set to 0, rustscan will correct it to 1. + #[structopt(long, default_value = "1")] + pub tries: u8, + /// Automatically ups the ULIMIT with the value you provided. #[structopt(short, long)] pub ulimit: Option, @@ -154,7 +159,9 @@ impl Opts { } } - merge_required!(addresses, greppable, accessible, batch_size, timeout, scan_order, command); + merge_required!( + addresses, greppable, accessible, batch_size, timeout, tries, scan_order, command + ); } fn merge_optional(&mut self, config: &Config) { @@ -196,6 +203,7 @@ pub struct Config { accessible: Option, batch_size: Option, timeout: Option, + tries: Option, no_nmap: Option, ulimit: Option, scan_order: Option, @@ -253,6 +261,7 @@ mod tests { greppable: Some(true), batch_size: Some(25_000), timeout: Some(1_000), + tries: Some(1), ulimit: None, no_nmap: Some(false), command: Some(vec!["-A".to_owned()]), @@ -271,6 +280,7 @@ mod tests { greppable: true, batch_size: 0, timeout: 0, + tries: 0, ulimit: None, command: vec![], accessible: false, diff --git a/src/main.rs b/src/main.rs index 41b48545f..f97638eaa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,6 +74,7 @@ fn main() { &ips, batch_size, Duration::from_millis(opts.timeout.into()), + opts.tries, opts.greppable, PortStrategy::pick(opts.range, opts.ports, opts.scan_order), opts.accessible, diff --git a/src/scanner/mod.rs b/src/scanner/mod.rs index 2bc800167..9d901cb11 100644 --- a/src/scanner/mod.rs +++ b/src/scanner/mod.rs @@ -11,6 +11,7 @@ use futures::stream::FuturesUnordered; use std::{ io::ErrorKind, net::{IpAddr, Shutdown, SocketAddr}, + num::NonZeroU8, time::Duration, }; @@ -26,6 +27,7 @@ pub struct Scanner { ips: Vec, batch_size: u16, timeout: Duration, + tries: NonZeroU8, greppable: bool, port_strategy: PortStrategy, accessible: bool, @@ -36,6 +38,7 @@ impl Scanner { ips: &[IpAddr], batch_size: u16, timeout: Duration, + tries: u8, greppable: bool, port_strategy: PortStrategy, accessible: bool, @@ -43,6 +46,7 @@ impl Scanner { Self { batch_size, timeout, + tries: NonZeroU8::new(std::cmp::max(tries, 1)).unwrap(), greppable, port_strategy, ips: ips.iter().map(|ip| ip.to_owned()).collect(), @@ -87,54 +91,66 @@ impl Scanner { open_sockets } - /// Given a port, scan it. + /// Given a socket, scan it self.tries times. /// Turns the address into a SocketAddr /// Deals with the type /// If it experiences error ErrorKind::Other then too many files are open and it Panics! - /// ese any other error, it returns the error in Result as a string - /// If no errors occur, it returns the port number in Result to signify the port is open. + /// Else any other error, it returns the error in Result as a string + /// If no errors occur, it returns the port number in Result to signify the port is open. /// This function mainly deals with the logic of Results handling. /// # Example /// - /// self.scan_port(10:u16) + /// self.scan_socket(socket) /// /// Note: `self` must contain `self.ip`. async fn scan_socket(&self, socket: SocketAddr) -> io::Result { - match self.connect(socket).await { - Ok(x) => { - debug!( - "Connection was successful, shutting down stream {}", - &socket - ); - match x.shutdown(Shutdown::Both) { - Err(e) => debug!("Shutdown stream error {}", &e), - _ => {} - } - if !self.greppable { - if self.accessible { - println!("Open {}", socket.to_string()); - } else { - println!("Open {}", socket.to_string().purple()); + let tries = self.tries.get(); + + debug!("self.tries: {}", tries); + + for nr_try in 1..=tries { + debug!("Try number: {}", nr_try); + + match self.connect(socket).await { + Ok(x) => { + debug!( + "Connection was successful, shutting down stream {}", + &socket + ); + if let Err(e) = x.shutdown(Shutdown::Both) { + debug!("Shutdown stream error {}", &e); + } + if !self.greppable { + if self.accessible { + println!("Open {}", socket.to_string()); + } else { + println!("Open {}", socket.to_string().purple()); + } } + + debug!("Return Ok after {} tries", nr_try); + return Ok(socket); } + Err(e) => { + let error_string = e.to_string(); - Ok(socket) - } - Err(e) => match e.kind() { - ErrorKind::Other => { - if e.to_string().contains("No route to host") - || e.to_string().contains("Network is unreachable") - { - debug!("Socket connect error: {} {}", &e.to_string(), &socket); - Err(io::Error::new(io::ErrorKind::Other, e.to_string())) - } else { - debug!("Socket connect error: {} {}", &e.to_string(), &socket); - panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500."); + if e.kind() == ErrorKind::Other { + debug!("Socket connect error: {} {}", &error_string, &socket); + + if !error_string.contains("No route to host") + && !error_string.contains("Network is unreachable") + { + panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500."); + } + } + + if nr_try == tries { + return Err(io::Error::new(io::ErrorKind::Other, error_string)); } } - _ => Err(io::Error::new(io::ErrorKind::Other, e.to_string())), - }, + }; } + unreachable!(); } /// Performs the connection to the socket with timeout @@ -174,7 +190,15 @@ mod tests { end: 1_000, }; let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true); + let scanner = Scanner::new( + &addrs, + 10, + Duration::from_millis(100), + 1, + true, + strategy, + true, + ); block_on(scanner.run()); // if the scan fails, it wouldn't be able to assert_eq! as it panicked! assert_eq!(1, 1); @@ -188,7 +212,15 @@ mod tests { end: 1_000, }; let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true); + let scanner = Scanner::new( + &addrs, + 10, + Duration::from_millis(100), + 1, + true, + strategy, + true, + ); block_on(scanner.run()); // if the scan fails, it wouldn't be able to assert_eq! as it panicked! assert_eq!(1, 1); @@ -201,7 +233,15 @@ mod tests { end: 1_000, }; let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true); + let scanner = Scanner::new( + &addrs, + 10, + Duration::from_millis(100), + 1, + true, + strategy, + true, + ); block_on(scanner.run()); assert_eq!(1, 1); } @@ -213,7 +253,15 @@ mod tests { end: 445, }; let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true); + let scanner = Scanner::new( + &addrs, + 10, + Duration::from_millis(100), + 1, + true, + strategy, + true, + ); block_on(scanner.run()); assert_eq!(1, 1); } @@ -228,7 +276,15 @@ mod tests { end: 600, }; let strategy = PortStrategy::pick(Some(range), None, ScanOrder::Random); - let scanner = Scanner::new(&addrs, 10, Duration::from_millis(100), true, strategy, true); + let scanner = Scanner::new( + &addrs, + 10, + Duration::from_millis(100), + 1, + true, + strategy, + true, + ); block_on(scanner.run()); assert_eq!(1, 1); }