fix: keep library scans quiet by default (#916)

`Scanner` no longer prints each open socket to stdout by default, so library users can embed it and use the returned sockets without unsolicited output. The CLI opts in via the new `Scanner::with_open_port_output()` builder. The UDP socket bind error is now logged at debug level instead of printed.

Fixes #706.
This commit is contained in:
Andrew Barnes authored and GitHub committed 2026-10-01 19:52:04 +09:00
1 parent 1c2a982320
commit 002f736073
2 files changed
+53 -5

No files matched your search

+2 -1
View File
@@ -94,7 +94,8 @@ fn main() {
opts.accessible,
opts.exclude_ports.unwrap_or_default(),
opts.udp,
);
)
.with_open_port_output();
debug!("Scanner finished building: {scanner:?}");
let mut portscan_bench = NamedTimer::start("Portscan");
+51 -4
View File
@@ -38,6 +38,7 @@ pub struct Scanner {
accessible: bool,
exclude_ports: Vec<u16>,
udp: bool,
print_open_ports: bool,
}
// Allowing too many arguments for clippy.
@@ -64,9 +65,19 @@ impl Scanner {
accessible,
exclude_ports,
udp,
print_open_ports: false,
}
}
/// Enables the CLI's incremental open-port output.
///
/// Library callers are quiet by default and can inspect the sockets returned by [`Self::run`].
#[must_use]
pub fn with_open_port_output(mut self) -> Self {
self.print_open_ports = true;
self
}
/// Runs scan_range with chunk sizes
/// If you want to run RustScan normally, this is the entry point used
/// Returns all open ports as `Vec<u16>`
@@ -286,7 +297,7 @@ impl Scanner {
}
}
Err(e) => {
println_safe(format_args!("Err E binding sock {e:?}"));
debug!("Error binding UDP socket: {e:?}");
Err(e)
}
}
@@ -294,7 +305,7 @@ impl Scanner {
/// Formats and prints the port status
fn fmt_ports(&self, socket: SocketAddr) {
if !self.greppable {
if self.print_open_ports && !self.greppable {
if self.accessible {
println_safe(format_args!("Open {socket}"));
} else {
@@ -308,8 +319,44 @@ impl Scanner {
mod tests {
use super::*;
// These tests only inspect the payload table generated by build.rs;
// they never open sockets.
use crate::input::{PortRange, ScanOrder};
// These tests never open sockets: they only build a `Scanner` or inspect
// the payload table generated by build.rs.
fn test_scanner() -> Scanner {
let addrs = vec!["127.0.0.1".parse::<IpAddr>().unwrap()];
let strategy = PortStrategy::pick(
&Some(PortRange { start: 1, end: 1 }),
None,
ScanOrder::Serial,
);
Scanner::new(
&addrs,
1,
Duration::from_millis(100),
1,
false,
strategy,
false,
Vec::new(),
false,
)
}
#[test]
fn library_scanner_is_quiet_by_default() {
let scanner = test_scanner();
assert!(!scanner.print_open_ports);
}
#[test]
fn cli_can_enable_open_port_output() {
let scanner = test_scanner().with_open_port_output();
assert!(scanner.print_open_ports);
}
/// Regression test for https://github.com/bee-san/RustScan/issues/933:
/// the SNMP public-walk probe must be the exact 33-byte BER packet, with