From ae04e8cc399afdc0103c0d67e360e975eed2774e Mon Sep 17 00:00:00 2001 From: demrom <47463752+demrom@users.noreply.github.com> Date: Wed, 21 Oct 2026 02:08:01 +0300 Subject: [PATCH] fix(main): print the no-open-ports hint once, only when nothing was found A subnet sweep for one port printed the batch-size hint for every host without that port. When any host answered with an open port, or a closed one under --closed, replies got through at this batch size and timeout, so batch size and timeout are unlikely to be the cause and the hint is skipped. When nothing was found it is printed once for all targets. Fixes #795 --- src/main.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index d0c8add8..ee797ca9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,20 +128,7 @@ fn main() { map.entry(socket.ip()).or_default().push(socket.port()); } - for ip in ips { - if ports_per_ip.contains_key(&ip) { - continue; - } - - // If we got here it means the IP was not found within the HashMap, this - // means the scan couldn't find any open ports for it. - - let x = format!("Looks like I didn't find any open ports for {:?}. This is usually caused by a high batch size. - \n*I used {} batch size, consider lowering it with {} or a comfortable number for your system. - \n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n", - ip, - batch_size, - "'rustscan -b -a '"); + if let Some(x) = no_open_ports_warning(&ips, &ports_per_ip, &closed_ports_per_ip, batch_size) { warning!(x, opts.greppable, opts.accessible); } @@ -224,6 +211,35 @@ fn main() { info!("{}", benchmarks.summary()); } +/// Builds the hint printed when the scan found no open ports. +/// +/// A host without open ports is normal when another host answered (a subnet +/// sweep for one port): replies got through at this batch size and timeout, +/// so batch size and timeout are unlikely to be the cause. The hint is printed +/// once, and only when no host answered with an open or, under `--closed`, a +/// closed port. +fn no_open_ports_warning( + ips: &[IpAddr], + ports_per_ip: &HashMap>, + closed_ports_per_ip: &HashMap>, + batch_size: usize, +) -> Option { + if !ports_per_ip.is_empty() || !closed_ports_per_ip.is_empty() { + return None; + } + + let target = match ips { + [ip] => format!("for {ip:?}"), + _ => format!("on any of the {} addresses", ips.len()), + }; + Some(format!("Looks like I didn't find any open ports {}. This is usually caused by a high batch size. + \n*I used {} batch size, consider lowering it with {} or a comfortable number for your system. + \n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n", + target, + batch_size, + "'rustscan -b -a '")) +} + /// Determines the actual batch size used by the scanner. /// /// Unix systems may reduce the requested batch size according to the process @@ -373,7 +389,48 @@ mod tests { use super::effective_batch_size; #[cfg(unix)] use super::{adjust_ulimit_size, infer_batch_size}; - use super::{print_opening, Opts}; + use super::{no_open_ports_warning, print_opening, Opts}; + use std::collections::HashMap; + use std::net::IpAddr; + + fn hosts(n: u8) -> Vec { + (1..=n).map(|i| IpAddr::from([192, 0, 2, i])).collect() + } + + #[test] + fn no_open_ports_silent_when_another_host_answered() { + // Sweeping a subnet for one port: one host has it open, the rest do not. + let ips = hosts(254); + let ports_per_ip = HashMap::from([(ips[0], vec![22])]); + + assert!(no_open_ports_warning(&ips, &ports_per_ip, &HashMap::new(), 4500).is_none()); + } + + #[test] + fn no_open_ports_silent_when_a_host_answered_closed() { + // With --closed, a refused connection also proves replies get through. + let ips = hosts(254); + let closed_ports_per_ip = HashMap::from([(ips[0], vec![443])]); + + assert!(no_open_ports_warning(&ips, &HashMap::new(), &closed_ports_per_ip, 4500).is_none()); + } + + #[test] + fn no_open_ports_warns_once_when_nothing_found() { + let warning = no_open_ports_warning(&hosts(254), &HashMap::new(), &HashMap::new(), 4500); + + let warning = warning.expect("one hint when nothing was found"); + assert!(warning.contains("on any of the 254 addresses.")); + assert!(warning.contains("4500 batch size")); + } + + #[test] + fn no_open_ports_names_the_single_host() { + let warning = no_open_ports_warning(&hosts(1), &HashMap::new(), &HashMap::new(), 4500); + + let warning = warning.expect("one hint for the single host"); + assert!(warning.contains("for 192.0.2.1.")); + } #[test] #[cfg(unix)]