Port 79 mostly attracts HTTP/SIP probes, TLS handshakes, and username
guessers -- none of which resolve to a plan file. Treat any request that
fails to read a plan as an "offense" and timestamp it against the source
IP.
Add BanTracker (ban.hpp/ban.cpp): a per-IP rolling-window offender list.
When an IP has more than 3 offenses still inside a 24h window, its
connections are dropped without being read or answered; timestamps older
than the window are pruned so a blocked IP frees itself automatically.
State is in-memory (single io_context thread, no locking); the clock is
injected for testability. A periodic sweeper keeps the map bounded.
Legitimate lookups that hit a real plan never count, which also
frustrates username enumeration. Unit tests in test_ban.cpp.