diff --git a/benches/benchmark_portscan.rs b/benches/benchmark_portscan.rs index 42fcd6cc5..2da66e022 100644 --- a/benches/benchmark_portscan.rs +++ b/benches/benchmark_portscan.rs @@ -24,7 +24,7 @@ fn bench_port_strategy() { start: 1, end: 1_000, }; - let _strategy = PortStrategy::pick(&Some(range.clone()), None, ScanOrder::Serial); + let _strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Serial); } fn bench_address_parsing() { @@ -51,8 +51,8 @@ fn criterion_benchmark(c: &mut Criterion) { start: 1, end: 1_000, }; - let strategy_tcp = PortStrategy::pick(&Some(range.clone()), None, ScanOrder::Serial); - let strategy_udp = PortStrategy::pick(&Some(range.clone()), None, ScanOrder::Serial); + let strategy_tcp = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Serial); + let strategy_udp = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Serial); let scanner_tcp = Scanner::new( &addrs, diff --git a/src/input.rs b/src/input.rs index 26605317a..268d393c1 100644 --- a/src/input.rs +++ b/src/input.rs @@ -34,28 +34,31 @@ pub struct PortRange { pub end: u16, } +#[cfg(not(tarpaulin_include))] +// Parse a single range token like "10-100" into PortRange and validate start <= end. #[cfg(not(tarpaulin_include))] fn parse_range(input: &str) -> Result { - let range = input - .split('-') - .map(str::parse) - .collect::, std::num::ParseIntError>>(); - - if range.is_err() { + let parts = input.split('-').collect::>(); + if parts.len() != 2 { return Err(String::from( "the range format must be 'start-end'. Example: 1-1000.", )); } - match range.unwrap().as_slice() { - [start, end] => Ok(PortRange { - start: *start, - end: *end, - }), - _ => Err(String::from( - "the range format must be 'start-end'. Example: 1-1000.", - )), + let start = parts[0].parse::().map_err(|_| { + String::from("the range format must be 'start-end' where start and end are numbers.") + })?; + let end = parts[1].parse::().map_err(|_| { + String::from("the range format must be 'start-end' where start and end are numbers.") + })?; + + if start > end { + return Err(String::from( + "range start must be less than or equal to range end.", + )); } + + Ok(PortRange { start, end }) } #[derive(Parser, Debug, Clone)] @@ -80,9 +83,9 @@ pub struct Opts { #[arg(short, long, value_delimiter = ',')] pub ports: Option>, - /// A range of ports with format start-end. Example: 1-1000. - #[arg(short, long, conflicts_with = "ports", value_parser = parse_range)] - pub range: Option, + /// A comma-separated list of ranges with format start-end. Example: 1-1000,2000-3000. + #[arg(short, long, conflicts_with = "ports", value_delimiter = ',', value_parser = parse_range)] + pub range: Option>, /// Whether to ignore the configuration file or not. #[arg(short, long)] @@ -169,10 +172,10 @@ impl Opts { let mut opts = Opts::parse(); if opts.ports.is_none() && opts.range.is_none() { - opts.range = Some(PortRange { + opts.range = Some(vec![PortRange { start: LOWEST_PORT_NUMBER, end: TOP_PORT_NUMBER, - }); + }]); } opts @@ -259,7 +262,7 @@ impl Default for Opts { pub struct Config { addresses: Option>, ports: Option>, - range: Option, + range: Option>, greppable: Option, accessible: Option, batch_size: Option, @@ -412,10 +415,10 @@ mod tests { fn opts_merge_optional_arguments() { let mut opts = Opts::default(); let mut config = Config::default(); - config.range = Some(PortRange { + config.range = Some(vec![PortRange { start: 1, end: 1_000, - }); + }]); config.ulimit = Some(1_000); config.resolver = Some("1.1.1.1".to_owned()); diff --git a/src/lib.rs b/src/lib.rs index e335de58a..b793090bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ //! start: 1, //! end: 1_000, //! }; -//! let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); // can be serial, random or manual https://github.com/RustScan/RustScan/blob/master/src/port_strategy/mod.rs +//! let strategy = PortStrategy::pick(&Some(vec![range]), None, ScanOrder::Random); // can be serial, random or manual https://github.com/RustScan/RustScan/blob/master/src/port_strategy/mod.rs //! let scanner = Scanner::new( //! &addrs, // the addresses to scan //! 10, // batch_size is how many ports at a time should be scanned diff --git a/src/port_strategy/mod.rs b/src/port_strategy/mod.rs index cf989d8cb..92635ee9a 100644 --- a/src/port_strategy/mod.rs +++ b/src/port_strategy/mod.rs @@ -17,30 +17,56 @@ pub enum PortStrategy { } impl PortStrategy { - pub fn pick(range: &Option, ports: Option>, order: ScanOrder) -> Self { - match order { - ScanOrder::Serial if ports.is_none() => { - let range = range.as_ref().unwrap(); - PortStrategy::Serial(SerialRange { - start: range.start, - end: range.end, - }) + pub fn pick(range: &Option>, ports: Option>, order: ScanOrder) -> Self { + // If ports are specified, use them (shuffle if Random) + if let Some(mut ports_vec) = ports { + return match order { + ScanOrder::Serial => PortStrategy::Manual(ports_vec), + ScanOrder::Random => { + let mut rng = rng(); + ports_vec.shuffle(&mut rng); + PortStrategy::Manual(ports_vec) + } + }; + } + + // No explicit ports provided: fall back to ranges (one or many) + if let Some(ranges) = range { + if ranges.len() == 1 { + let r = &ranges[0]; + return match order { + ScanOrder::Serial => PortStrategy::Serial(SerialRange { + start: r.start, + end: r.end, + }), + ScanOrder::Random => PortStrategy::Random(RandomRange { + start: r.start, + end: r.end, + }), + }; } - ScanOrder::Random if ports.is_none() => { - let range = range.as_ref().unwrap(); - PortStrategy::Random(RandomRange { - start: range.start, - end: range.end, - }) + + // Multiple ranges: expand into a single Vec + let mut combined: Vec = Vec::new(); + for r in ranges { + combined.extend(r.start..=r.end); } - ScanOrder::Serial => PortStrategy::Manual(ports.unwrap()), - ScanOrder::Random => { + + // For Random order, shuffle the combined vector + if let ScanOrder::Random = order { let mut rng = rng(); - let mut ports = ports.unwrap(); - ports.shuffle(&mut rng); - PortStrategy::Manual(ports) + combined.shuffle(&mut rng); } + + return PortStrategy::Manual(combined); } + + // No ranges or ports provided: this should not happen because Opts::read() + // sets a default range, but handle defensively. + PortStrategy::Serial(SerialRange { + start: 1, + end: 65_535, + }) } pub fn order(&self) -> Vec { @@ -103,7 +129,7 @@ mod tests { #[test] fn serial_strategy_with_range() { let range = PortRange { start: 1, end: 100 }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Serial); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Serial); let result = strategy.order(); let expected_range = (1..=100).collect::>(); assert_eq!(expected_range, result); @@ -111,7 +137,7 @@ mod tests { #[test] fn random_strategy_with_range() { let range = PortRange { start: 1, end: 100 }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let mut result = strategy.order(); let expected_range = (1..=100).collect::>(); assert_ne!(expected_range, result); diff --git a/src/scanner/mod.rs b/src/scanner/mod.rs index 92038eacd..0ce8e95cd 100644 --- a/src/scanner/mod.rs +++ b/src/scanner/mod.rs @@ -70,13 +70,26 @@ impl Scanner { /// If you want to run RustScan normally, this is the entry point used /// Returns all open ports as `Vec` pub async fn run(&self) -> Vec { + // let ports: Vec = self + // .port_strategy + // .order() + // .iter() + // .filter(|&port| !self.exclude_ports.contains(port)) + // .copied() + // .collect(); + + let mut seen = HashSet::new(); let ports: Vec = self .port_strategy .order() - .iter() - .filter(|&port| !self.exclude_ports.contains(port)) - .copied() + .into_iter() + .filter(|port| seen.insert(*port)) + .filter(|port| !self.exclude_ports.contains(port)) .collect(); + // ports.retain(|port| seen.insert(*port)); + // ports.retain(|port| !self.exclude_ports.contains(port)); + // let ports = ports; + let mut socket_iterator: SocketIterator = SocketIterator::new(&self.ips, &ports); let mut open_sockets: Vec = Vec::new(); let mut ftrs = FuturesUnordered::new(); @@ -321,7 +334,7 @@ mod tests { start: 1, end: 1_000, }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let scanner = Scanner::new( &addrs, 10, @@ -345,7 +358,7 @@ mod tests { start: 1, end: 1_000, }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let scanner = Scanner::new( &addrs, 10, @@ -368,7 +381,7 @@ mod tests { start: 1, end: 1_000, }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let scanner = Scanner::new( &addrs, 10, @@ -390,7 +403,7 @@ mod tests { start: 400, end: 445, }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let scanner = Scanner::new( &addrs, 10, @@ -415,7 +428,7 @@ mod tests { start: 400, end: 600, }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let scanner = Scanner::new( &addrs, 10, @@ -439,7 +452,7 @@ mod tests { start: 1, end: 1_000, }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let scanner = Scanner::new( &addrs, 10, @@ -463,7 +476,7 @@ mod tests { start: 1, end: 1_000, }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let scanner = Scanner::new( &addrs, 10, @@ -486,7 +499,7 @@ mod tests { start: 1, end: 1_000, }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let scanner = Scanner::new( &addrs, 10, @@ -505,10 +518,10 @@ mod tests { fn udp_google_dns_runs() { let addrs = vec!["8.8.8.8".parse::().unwrap()]; let range = PortRange { - start: 100, - end: 150, + start: 400, + end: 445, }; - let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random); + let strategy = PortStrategy::pick(&Some(vec![range.clone()]), None, ScanOrder::Random); let scanner = Scanner::new( &addrs, 10,