b7d807b464
stats.WrapUDPConn returned nil when pStats was nil (introduced in 7c95d40),
but callers in the kcp and rudp listeners passed the result directly to
admission.WrapUDPConn and limiter.WrapUDPConn, which wrapped a nil PacketConn.
When service.Serve() called conn.LocalAddr(), the auto-generated method
delegated to the nil PacketConn, causing a panic.
Root cause: WrapUDPConn behavior was inconsistent with WrapConn and
WrapPacketConn in the same package, which both return the original connection
instead of nil when the optional config is absent.
Fixes:
1. listener/kcp, listener/rudp — guard stats.WrapUDPConn with Stats != nil
(consistent with quic and wt listeners which already had this guard).
2. observer/stats/wrapper — when pStats is nil, pass through the original
connection instead of returning nil (matches WrapConn/WrapPacketConn).
3. admission/wrapper, limiter/traffic/wrapper — add defensive nil guards:
nil pc → return nil; nil config → pass through original connection.
All four WrapUDPConn implementations now share the same nil semantics.
4. listener/udp_wrapper_safety_test.go — regression test that verifies the
full metrics→stats→admission→limiter chain with all optional configs nil
does not produce a wrapper with a nil internal PacketConn.
Fixes: go-gost/gost#873