The client-facing UDP relay listener (cc) in handleUDP was bound to the
wildcard address, so replies sent from it used the kernel-selected source
IP (the interface's primary IP), which mismatches the BND.ADDR advertised
to the client (the TCP control connection's local IP). Per RFC 1928 6,
compliant clients drop such replies, breaking SOCKS5 UDP associate under
multiple IP aliases where metadata.interface differs from the primary IP.
Bind cc to the TCP control connection's local IP so its reply source IP
always matches BND.ADDR. The outbound relay socket (pc) already binds the
interface IP via Router.Dial -> dialer.ListenUDP(laddr).
The SOCKS5 handler recorder only tracked TCP traffic metrics. UDP
associations (CmdUdp) overwrote TCP byte counts with UDP counts, and
UDP tunnels (CmdUDPTun) recorded zero bytes.
Changes:
- udp.go: change UDP stats assignment (=) to accumulation (+=) so the
TCP control connection bytes tracked by Handle() are preserved.
- udp_tun.go: add xstats.Stats{} wrapper around the PacketConn with
deferred accumulation into HandlerRecorderObject, mirroring the
handleUDP pattern.