Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions benches/benchmark_portscan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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,
Expand Down
47 changes: 25 additions & 22 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PortRange, String> {
let range = input
.split('-')
.map(str::parse)
.collect::<Result<Vec<u16>, std::num::ParseIntError>>();

if range.is_err() {
let parts = input.split('-').collect::<Vec<&str>>();
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::<u16>().map_err(|_| {
String::from("the range format must be 'start-end' where start and end are numbers.")
})?;
let end = parts[1].parse::<u16>().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)]
Expand All @@ -80,9 +83,9 @@ pub struct Opts {
#[arg(short, long, value_delimiter = ',')]
pub ports: Option<Vec<u16>>,

/// A range of ports with format start-end. Example: 1-1000.
#[arg(short, long, conflicts_with = "ports", value_parser = parse_range)]
pub range: Option<PortRange>,
/// 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<Vec<PortRange>>,

/// Whether to ignore the configuration file or not.
#[arg(short, long)]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -259,7 +262,7 @@ impl Default for Opts {
pub struct Config {
addresses: Option<Vec<String>>,
ports: Option<Vec<u16>>,
range: Option<PortRange>,
range: Option<Vec<PortRange>>,
greppable: Option<bool>,
accessible: Option<bool>,
batch_size: Option<u16>,
Expand Down Expand Up @@ -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());

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
68 changes: 47 additions & 21 deletions src/port_strategy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,56 @@ pub enum PortStrategy {
}

impl PortStrategy {
pub fn pick(range: &Option<PortRange>, ports: Option<Vec<u16>>, 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<Vec<PortRange>>, ports: Option<Vec<u16>>, 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<u16>
let mut combined: Vec<u16> = 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<u16> {
Expand Down Expand Up @@ -103,15 +129,15 @@ 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::<Vec<u16>>();
assert_eq!(expected_range, result);
}
#[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::<Vec<u16>>();
assert_ne!(expected_range, result);
Expand Down
41 changes: 27 additions & 14 deletions src/scanner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u16>`
pub async fn run(&self) -> Vec<SocketAddr> {
// let ports: Vec<u16> = self
// .port_strategy
// .order()
// .iter()
// .filter(|&port| !self.exclude_ports.contains(port))
// .copied()
// .collect();

let mut seen = HashSet::new();
let ports: Vec<u16> = 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<SocketAddr> = Vec::new();
let mut ftrs = FuturesUnordered::new();
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -505,10 +518,10 @@ mod tests {
fn udp_google_dns_runs() {
let addrs = vec!["8.8.8.8".parse::<IpAddr>().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,
Expand Down