Featured
Port Sniffer
A fast, multi-threaded TCP port scanner CLI written in Rust, with domain resolution, port ranges, and live progress reporting.
- Stack
- RustClapNative Threads
The problem
Scanning a host for open ports sequentially is slow — checking all 65535 ports one at a time, each with its own connection timeout, can take minutes. A useful scanner needs to parallelize the work, handle unresolvable domains and unreachable hosts gracefully, and give the user visibility into progress without hiding the final result behind a wall of noise.
Approach
Port Sniffer splits the port space across a configurable pool of native OS threads. Each thread scans a disjoint slice of the port list — worker i scans ports at indices i, i + n, i + 2n, ... where n is the thread count — so no coordination or work-stealing is needed once the list is built.
- CLI parsing via
clap, supporting a hostname/IP target, thread count, timeout, and port ranges (single ports, whitespace-separated lists, or dash ranges) - Domain resolution through
dns-lookup, falling back cleanly when a domain doesn't resolve - Deduplication of the requested port set using a
HashSetbefore scanning starts - Concurrent scanning with
TcpStream::connect_timeoutper port, results streamed back over anmpscchannel - Live progress via
indicatif, showing scanned/open/closed counts as threads report in - Graceful interruption — a
Ctrl+Chandler drains whatever results have arrived and prints them before exiting
Key features
Multi-threaded scanning — Ports are distributed evenly across threads so scan time scales down roughly linearly with thread count, bounded by the connection timeout.
Flexible port selection — Scan everything by default, or target specific ports and ranges with -p, e.g. -p 22 80 8000-9000.
Bare output mode — --bare strips formatting to newline-separated port numbers, making results easy to pipe into other tools.
Ctrl+C handling — Interrupting a long scan still prints whatever open ports were found rather than losing the results.
What was interesting
Index-striped work distribution. Rather than a work queue, each thread owns a fixed stride through the port list. It's a simple scheme with no locking on the read side, and it naturally balances load since ports are shuffled into a HashSet before scanning (so no thread gets stuck scanning a contiguous "quiet" range).
Signal handling with shared state. The Ctrl+C handler runs on its own execution context but needs to read results that worker threads are still writing. Using Arc<Mutex<Receiver>> lets both the handler and the main thread drain the channel safely without a redesign of the scanning loop.
Progress reporting without slowing down scanning. The progress bar updates on every port scanned, which could become a bottleneck at high thread counts. Keeping the bar update on the same thread that already holds the scan result (rather than a separate reporting thread) avoids adding synchronization overhead just for UI.
Architecture
src/
├── main.rs # CLI entry point, thread orchestration, Ctrl+C handling, output
├── models.rs # IpOrDomain, PortRange, LogLevel — parsing and validation types
├── constants.rs # Default thread count, timeout, and port bounds
└── lib.rs # Crate root, re-exports
Domain-specific parsing (IP/domain resolution, port range syntax) lives in dedicated types with their own clap value parsers, keeping main.rs focused on orchestration.
Result
A single-binary CLI, installable via cargo install, that scans all 65535 ports on a host in a fraction of the time a sequential scanner would take, with clear progress feedback and results that pipe cleanly into other tools.