* fix(ports): block the whole loopback range; warn on native-target overflow
Two activator findings from the codebase audit.
The localhost port blocker only rejected 127.0.0.1, so a proxy/VPN daemon bound
to the wildcard 0.0.0.0 (the common allow-lan / TUN setup for Clash, sing-box,
V2Ray) stayed reachable on any other 127.x.x.x alias — an observer could
connect(127.0.0.2:port) and get a handshake a clean device would refuse, a
positive fingerprint. Reject the whole 127.0.0.0/8 block instead (::1 already is
the entire IPv6 loopback).
The native-target cap (64) was restated as a bare literal in three places (the
activator and both C backends) and projection truncated the overflow with a
silent .take(), dropping the highest-UID apps from protection with no diagnostic.
Export the cap once from the shared protocol crate, alias it in the activator,
cross-reference it from the C #defines, and warn on stderr when the target set
exceeds it.
* style: rustfmt activator loopback comment
* docs(changelog): note the native-target overflow warning
* fix(native): drop dead proc diagnostics, fix misaligned reads, parser parity
Three native-correctness fixes from the codebase audit.
Diagnostics: /proc/net/{tcp,tcp6,udp,udp6,fib_trie} expose the VPN only as a
hex local address, never as an interface name, so the name-matching probe could
only ever report a green "no VPN entries" regardless of an actual leak — and
from inside the targeted (self-hidden) process there is no reference tunnel
address to decode and compare. Remove those five checks rather than keep a
meaningless always-pass; the address-leak vector on these files is covered by
zygisk's socket-table filters. /proc/net/{route,ipv6_route,if_inet6,dev}, which
do carry interface names, are unchanged.
UB: SIOCGIFCONF and the netlink dumps reinterpret the kernel's bytes as ifreq /
nlmsghdr / rtattr over plain [u8; N] buffers (align 1), so forming those refs is
undefined behaviour. Back the three buffers with an 8-byte-aligned wrapper so
every such reference is well-formed.
Protocol parity: the Rust parse_header skipped a non-ASCII first significant
line and searched on, while C (kmod/KPM) and Kotlin (LSPosed) reject the whole
payload — same wire bytes, different accept decision across the mutually
exclusive backends. Distinguish "blank/comment" (skip) from "significant but
non-ASCII" (reject) so all three agree. Clarify the §4.2 corner in the spec and
add header-position golden vectors (cfg + kind) that all three parsers now pass.
* style: rustfmt protocol parse_header