Turn the scan from sequential TX-then-RX into two io.concurrent engines on
std.Io.Threaded, fixing the M4 bug where replies arriving during transmit were
dropped and the RX deadline (anchored at socket-open) expired before draining.
The main thread is the non-blocking Io.Queue consumer and dashboard renderer.
- rx: Receiver keyed off a shared tx_done atomic; new pure planDrain anchors the
drain window at TX-completion, not socket-open, killing the quiet-gap early exit
during a slow TX. Hard-cap safety backstop anchored at drain start.
- output (new): cache-line-padded atomic Stats, truecolor/256/none palette
(violet gradient, neon-green, muted-gray), in-place multi-line live dashboard,
Unicode results table, NDJSON to stdout with visuals on stderr, full
NO_COLOR/CLICOLOR_FORCE/COLORTERM/--color chain plus a narrow-terminal fallback.
- tx: wall-clock deadline so a stalled TX ring can no longer hang the scan.
- cli: banner recolored to the violet gradient and routed through the color chain.
- targets: add 192.88.99.0/24 (6to4 relay anycast) to the RFC 6890 exclude floor.
78/78 unit tests pass under Debug and ReleaseSafe; proven end-to-end (open/closed,
dedup, dashboard, NDJSON) in an unshare -r -n netns under ReleaseSafe.
classify.zig pure frame parser: SYN-ACK -> open, RST/ACK -> closed,
ICMP type-3 {1,2,3,9,10,13} -> filtered, every verdict gated on a
wrapping SipHash cookie re-check (ack == seq+1 for replies, inner seq
== cookie for ICMP) so spoofed/stale frames are dropped state-free;
byte-offset readInt parsing, fully length-guarded. dedup.zig power-of-2
open-addressed (ip,port) set, single-owned allocator, grows at load
factor with a bounded probe (never wedges). rx.zig receive engine
generic over an injected Source + Sink (proven over hand-built frames),
Io.Queue found-host handoff across producer/consumer fibers, and a
privileged AF_PACKET Receiver (poll-bounded drain with a hard deadline,
ignore-outgoing, EINTR-retry, errno-gated, fail-closed on missing
CAP_NET_RAW). scan subcommand shares one per-run cookie across the TX
template + RX validation; consumer uses a dedicated allocator to keep
the producer/consumer fibers race-free. netutil.zig factors the shared
arg-parse/iface-resolve helpers out of txcmd. Proven end-to-end with a
live SYN scan inside a unshare -r -n netns over a veth pair
(open/closed against a real listener, dedup verified). Green Debug +
ReleaseSafe, leak-free, 61 unit tests.
PACKET_TX_RING (PACKET_MMAP, TPACKET_V2) over AF_PACKET with
PACKET_QDISC_BYPASS: socket + version + TX_RING setsockopt + mmap'd ring,
errno-gated, fail-closed on missing CAP_NET_RAW. ratelimit.zig integer
token bucket (banked-nanoseconds, no float, saturating, exact KATs).
template.zig SYN frame template: stamp dst IP/port + SipHash seq, recompute
IP + TCP checksums (reuses the M1 RFC 1071 + cookie code, both self-verify).
afpacket.zig pure Ring slot bookkeeping (inline tpacket2_hdr, comptime size
assert, reserve/fill/atomic-status, self-defending bounds, kernel-drain
reuse) split from the privileged Backend so the hot-path accounting is
unit-tested over an in-process buffer. tx.zig transmit engine generic over
an injected Sink + Clock: the M2 cyclic-group bijection is proven to survive
stamp -> ratelimit -> submit end-to-end via a fake sink + fake clock.
Privileged tx subcommand for on-hardware benchmarking (setcap + tcpdump/perf).
Green Debug + ReleaseSafe, leak-free, 45/45 tests.
Zig 0.16 module graph (build.zig DAG with per-module addTest, run/smoke
steps), Juicy Main entry, truecolor tty-gated banner, --version/--help.
Wire-exact extern eth/ip/tcp headers with comptime @sizeOf asserts and
an RFC 1071 checksum proven by the canonical 0xb861 IPv4 KAT. AF_PACKET
raw socket that hand-builds and sends one SYN, checking errno not fd<0.
4/4 tests green under Debug and ReleaseSafe.