// NMAPRS — PARALLEL NETWORK SCANNER WITH NMAP-COMPATIBLE CLI

nmaprs v0.1.8 · Rust + tokio + pnet · IPv4 + IPv6 raw half-open TCP · pipelined recv + 16-shard send per family · nmap-os-db MatchPoints scoring · nmap-service-probes (TLS via rustls)

128 CLI options · nmap-compatible XML / -oN / -oG / -oS / -oA · --resume JSON checkpoints · SOCKS4 + HTTP CONNECT proxies · hickory-resolver DNS

Report GitHub Issues
// Color scheme

>_NMAPRS — NMAP, REWRITTEN IN RUST

Speaks nmap's CLI dialect: every flag, every probe, every output format your scripts already know. Real TCP connect, raw IPv4 + IPv6 SYN / NULL / FIN / Xmas / ACK / Window / Maimon, UDP with ICMP unreachable listener, SCTP (INIT / COOKIE_ECHO), IP-protocol scan (-sO), idle scan via zombie (-sI), FTP bounce (-b), traceroute, --resume JSON checkpoints, ARP ping on local subnets, custom DNS via hickory-resolver, SOCKS4 + HTTP CONNECT proxies, OS detection scored against nmap-os-db MatchPoints, version detection against nmap-service-probes (TLS via rustls). What it intentionally does not ship: the full NSE Lua runtime.

Quickstart

Install from crates.io or source. The binary is published twice — nmaprs (long) and nms (short, quicker to type):

# install
cargo install nmaprs

# from source
git clone https://github.com/MenkeTechnologies/nmaprs
cd nmaprs && cargo build --release
sudo cp target/release/nmaprs /usr/local/sbin/

# everyday scans (privileged forms use sudo)
nmaprs scanme.nmap.org                   # default TCP connect, top-1000
sudo nmaprs -sS -F 192.168.1.0/24        # raw SYN, top-100 ports
sudo nmaprs -A -T4 -oA scan target       # aggressive (-O + -sV + -sC + traceroute)
sudo nmaprs -sU --top-ports 100 target   # UDP top-100
sudo nmaprs -sY -p 80,443 sctp-host      # SCTP INIT
sudo nmaprs -sO target                   # IP protocol scan (0..=255)
sudo nmaprs -sI zombie.host:65535 target # idle scan
sudo nmaprs --iL hosts.txt -oG out.gnmap # batch w/ grepable output

# resume after Ctrl-C
sudo nmaprs --resume nmaprs.checkpoint.json

# nmap-compatible pipelines
sudo nmaprs -oX - target | xmllint --format -
sudo nmaprs -oG - target | grep '/open/'

Full reference: README or man nmaprsall (1028-line full reference under man/man1/nmaprsall.1). Short form: man nmaprs. Zsh completions ship via nmaprs --completions zsh if built.

Scan techniques — one card per probe

-sT · TCP connect

Default when unprivileged. Async, parallel, timeout-bound. Goes through SOCKS4 / HTTP CONNECT when --proxies is set.

-sS · TCP SYN (raw)

Default when root. Raw IPv4 + IPv6 via pnet. Pipelined recv + main-thread sends; sharded up to 16 concurrent pipelines per family. Falls back to connect on raw failure.

-sN / -sF / -sX / -sM

NULL, FIN, Xmas, Maimon. Same raw sharded pipeline as -sS. Maimon sends FIN+ACK.

-sA / -sW

ACK (firewall mapping: RST → unfiltered) and Window (RST nonzero-win → open). No connect fallback.

-sU · UDP

Reply → open; ICMP port-unreachable → closed; other unreachable → filtered. Single poll(2)+burst-recv listener thread on Unix.

-sY / -sZ · SCTP

INIT or COOKIE_ECHO. CRC32c segments; IPv4 layer-3, IPv6 raw SCTP (proto 132). Pipelined + sharded per family. INIT-ACK / COOKIE-ACK → open, ABORT → closed.

-sO · IP protocol

Probes 0..=255 (or just nmap-protocols with -F). IPv4: raw IPv4 headers; IPv6: IPPROTO_RAW+IPV6_HDRINCL. ICMPv4 / ICMPv6 protocol-unreachableclosed.

-sI · Idle scan

Spoofed SYN with source = zombie; sequential IP-ID delta sampling. Delta ≥ 2 → open. IPv4 only, privileged.

-b · FTP bounce

Parallel buffer_unordered sessions, one control conn per probe. IPv4 only (PORT). Maps 150/125/250 vs 425/426/421 to open/closed.

-sn · Ping scan

Raw ICMP echo via pnet, falls back to system ping / ping6. Output paths (-oN / -oG / -oX / -oS) write host-up lines like the port-scan path.

--traceroute

System traceroute; up to min(parallelism, 32) hosts concurrent. Output stays in target order.

Discovery (-P*)

SYN / ACK / UDP / SCTP / IP-proto / ICMP-echo / timestamp / mask / ARP. Auto ARP on local IPv4 subnets unless --disable-arp-ping.

Selection & port grammar

nmaprs scanme.nmap.org 10.0.0.0/24 2001:db8::/64    # hostnames + CIDR (-6 for IPv6)
nmaprs --iL targets.txt                              # file (one per line, multiple -iL OK)
nmaprs --iR 100                                      # 100 random hosts
nmaprs --exclude 10.0.0.0/16,192.0.2.7               # skip these
nmaprs --excludefile skip.txt
nmaprs --resolve-all api.example.com                 # scan every DNS-resolved address

# port spec
nmaprs -p 22,80,443        # explicit list
nmaprs -p 1-65535          # range (or -p -)
nmaprs -p U:53,T:22,8000-9000  # mixed UDP/TCP
nmaprs -p T:22,U:53 -sS -sU    # SYN scan TCP, UDP scan UDP
nmaprs -F                   # top-100 (embedded freq list)
nmaprs --top-ports 1000     # top-N by frequency
nmaprs --port-ratio 0.001   # all ports with ratio ≥ threshold

# raw TCP flag override (only when scan type is raw TCP)
nmaprs -sS --scanflags 'SYN ACK URG'    # custom flag set

Output formats — nmap-compatible

-oN · Normal

Human-readable text (default to stdout). Matches nmap's interactive output shape.

-oG · Grepable

One host per line; easy grep '/open/' / awk '$5 ~ /open/' pipelines.

-oX · XML

Nmap-compatible <nmaprun> root with <scaninfo>, <host>, <status>, <address>, <hostnames>, <ports>, <extraports>, <service>, <runstats>. Existing nmap XML parsers should ingest it.

-oA · All three

Writes BASE.nmap / BASE.gnmap / BASE.xml in one go.

-oS · Script kiddie

1337-speak mirror of -oN lines. Stdout stays normal — only the file is leet.

-oM / -oH

Partial parity: -oM currently writes the same lines as -oG; -oH is a placeholder for full hex.

Timing & performance dials

# timing template (T0..T5: paranoid / sneaky / polite / normal / aggressive / insane)
nmaprs -T4 target

# global probe-rate caps (one limiter; shared across IPv4+IPv6)
nmaprs --min-rate 100 --max-rate 5000 target

# parallelism bounds (overrides timing template when set)
nmaprs --min-parallelism 4 --max-parallelism 256 target
nmaprs -M 256 target                  # short for --max-parallelism

# host batching (random in [min, max] when both set)
nmaprs --min-hostgroup 16 --max-hostgroup 64 --iL big-list.txt

# per-probe RTT bounds (adaptive timeout floors/ceilings)
nmaprs --min-rtt-timeout 50ms --initial-rtt-timeout 200ms --max-rtt-timeout 2s target

# per-host wall clock + retries
nmaprs --host-timeout 5m --max-retries 2 target

# scan delay (uniform random in [min, max] when both set)
nmaprs --scan-delay 100ms --max-scan-delay 500ms target

# rate-limit-aware
nmaprs --defeat-rst-ratelimit --defeat-icmp-ratelimit target

# periodic progress to stderr
nmaprs -sS --stats-every 30s target

Evasion (raw scans only)

All flags below are wired at the packet level for raw TCP scans (-sS, -sN, -sF, -sX, -sM, -sA, -sW):

nmaprs -sS -g 53 target                  # spoofed source port (DNS-friendly)
nmaprs -sS --ttl 33 target               # custom IP TTL
nmaprs -sS --badsum target               # bad TCP/UDP/SCTP checksum
nmaprs -sS -D RND:5,ME,1.2.3.4 target    # decoy cloak (RND:N = N random)
nmaprs -sS -S 192.0.2.1 -e eth0 target   # spoof source IP (needs --send-eth)
nmaprs -sS --data DEADBEEFCAFE target    # raw hex payload appended
nmaprs -sS --data-string 'GET / HTTP/1.0' target
nmaprs -sS --data-length 64 target       # N random payload bytes
nmaprs -sS -f target                     # fragment (8-byte chunks)
nmaprs -sS --mtu 24 target               # custom fragment MTU
nmaprs -sS --spoof-mac 00:11:22:33:44:55 target   # spoof source MAC on ARP frames

Parity matrix vs nmap (abridged)

AreaStatus
TCP connect (-sT)Implemented
TCP SYN raw (-sS, IPv4 + IPv6, sharded pipeline)Implemented
NULL / FIN / Xmas / Maimon / ACK / WindowImplemented
UDP (-sU) with ICMP unreach listenerImplemented
SCTP INIT / COOKIE-ECHO (-sY / -sZ)Implemented
Idle scan (-sI)Implemented (IPv4 only)
FTP bounce (-b)Implemented (IPv4 only)
IP protocol scan (-sO)Implemented (IPv4 + IPv6 on Unix)
Host discovery (-P*, ARP auto)Implemented
IPv6 (-6)Implemented
Traceroute (system binary, concurrent)Implemented
--resume JSON checkpointImplemented
Evasion (-g, --ttl, --badsum, -D, -S, --data*, -f/--mtu, --spoof-mac)Implemented (packet-level)
--proxies / --proxy (SOCKS4 + HTTP CONNECT)Implemented
--dns-servers (hickory-resolver)Implemented
Auto privilege detect (geteuid)Implemented
Output: -oN / -oG / -oX / -oA / -oSImplemented (nmap-compatible XML)
Timing template / rate caps / parallelism / hostgroup / RTT / retries / scan-delay / stats-everyImplemented
OS detection (-O)Partial — IPv4 raw + nmap-os-db MatchPoints; IPv6 TTL only
Service / version (-sV)Partial — no Perl-only regex features; rest implemented
--script / -sCPartial — default + banner builtins only
-oM / -oHPartial — -oM = grepable; -oH placeholder
NSE Lua runtimeNot implemented (use nmap)

Full parity table (50+ rows) lives in the README "TRUTH TABLE" section.

Architecture in one screen

src/
├── main.rs              # CLI dispatch (clap), privilege detect, top-level scan loop
├── cli.rs               # 128 clap args, nmap-compatible long/short option set
├── lib.rs               # public API for programmatic use (re-exports core types)
├── bin/nms.rs           # short-name launcher (alias)
├── scan/
│   ├── tcp_connect.rs   # async tokio TcpStream (incl. SOCKS4 / HTTP CONNECT)
│   ├── raw_tcp.rs       # SYN/NULL/FIN/Xmas/ACK/Window/Maimon (pnet, sharded pipeline)
│   ├── udp.rs           # UDP probe + raw ICMP unreach listener
│   ├── sctp.rs          # SCTP INIT / COOKIE_ECHO (CRC32c)
│   ├── ip_proto.rs      # -sO (IPv4 + IPv6 raw)
│   ├── idle.rs          # -sI zombie IP-ID sampling
│   ├── ftp_bounce.rs    # -b parallel control sessions
│   ├── ping.rs          # -sn + -P* discovery dispatch
│   └── arp.rs           # ARP on local IPv4 subnets
├── service_probes.rs    # nmap-service-probes parser + matcher (regex crate)
├── os_db.rs             # nmap-os-db MatchPoints scoring (subject fingerprint)
├── output/
│   ├── normal.rs        # -oN
│   ├── grepable.rs      # -oG / -oM
│   ├── xml.rs           # -oX (nmap-compatible <nmaprun>)
│   └── skiddie.rs       # -oS
├── resume.rs            # JSON checkpoint format
├── rate.rs              # --min-rate / --max-rate global limiter
├── resolver.rs          # hickory-resolver wrapper + --dns-servers
├── parallelism.rs       # timing template ↔ parallelism inference
└── data/                # embedded nmap-services, nmap-protocols, ...
man/man1/
├── nmaprs.1             # man page — short reference (roff)
└── nmaprsall.1          # man page — full reference (1028 lines)

Two binaries (nmaprs + nms — same code, short alias), comprehensive man pages (man nmaprs + man nmaprsall).

Pipelined raw I/O — why this is fast

Per address family (IPv4 + IPv6), raw scans spin up:

  • Dedicated recv thread — one poll(2) blocking on the raw socket, dispatches frames to per-target oneshot channels.
  • Main-thread sends — serial within a shard, but up to 16 shards per family run concurrently (bounded by effective_probe_concurrency()).
  • Mixed v4+v6 targets run both families concurrently via tokio::join.
  • Recv-key registration before send — eliminates the classic "reply landed before we knew to listen" race that ad-hoc raw scanners hit at high parallelism.
  • One ICMP unreach listener thread per scan (not per batch) for UDP / SCTP / IP-proto.
  • Global rate limiter shared across IPv4 + IPv6 — --min-rate / --max-rate behave like nmap, not per-family.

For OS detection, IPv4 raw probes build a subject fingerprint (SEQ / OPS / WIN subset; more in progress) and score it against nmap-os-db entries with the same MatchPoints + expr_match algorithm nmap uses.

Compatibility

PlatformStatus
Linux (x86_64 + aarch64)All scan types, raw + connect
macOS (arm64 + x86_64)All scan types; IPv6 -sO requires Unix raw (works)
FreeBSDAll scan types
WindowsTCP connect + UDP via system socket only; raw modes not built (no pnet raw)

Security & ethics

Run nmaprs only against networks and hosts you own or have explicit written permission to scan. Unauthorized scanning is illegal in many jurisdictions. The author and contributors disclaim responsibility for misuse.

This tool exists because Rust gives us a memory-safe, parallel-first foundation to rebuild one of the most useful tools in the security community. The CLI parity goal is to let your existing nmap scripts, integrations, and pipelines keep working.