- The sower frame checksum now covers the command and port in addition to
the target, using HMAC-SHA256 (length-extension resistant) so a
man-in-the-middle on a plaintext link cannot retarget a captured frame
to an arbitrary port.
- Unwrap rejects empty or control-character target hosts.
- The SOCKS5 server writes the RFC 1928 METHOD=0xFF failure response
instead of leaving the peer hanging on an unusable negotiation.
- DHCP discovery validates the reply (matching xid, BOOTREPLY, message
type OFFER) and skips unrelated packets instead of trusting the first
datagram, closing a LAN-side DNS-hijack vector; the socket also sets
SO_BROADCAST explicitly (Go 1.26 removed UDPConn.SetBroadcast).
- The uTLS handshake gets a bounded default deadline when the dialer has
no timeout, so it cannot hang open forever.
- The suffix tree trims every trailing dot so dirty rule files cannot
build unreachable empty-label subtrees.
MatchRule resolves the matching rule in tree depth (most specific wins)
instead of a linear scan, and the router gains hit/miss observers so
per-rule counters can be fed without coupling the router to admin.
- countingConn batches per-domain attribution (32KiB/500ms) so the relay
hot path pays two atomics per I/O instead of a global mutex; Close drains
- Precompute OTel byte attributes to drop per-I/O allocations
- suffixtree: indexMap only for nodes with >=8 children; Match stays
read-only for concurrent queries
- Soft Go memory limit (128MiB default, GOMEMLIMIT/SOWER_MEMORY_LIMIT_MB
override) cuts resident memory from ~240MB to ~130MB on the default
ad/china/gfw rule sets
- Split stats.go into snapshot/eviction/counting by responsibility