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
The sshd handler performed a concrete type assertion on the accepted
connection, but the SSH listener's Accept() wraps each connection with
a traffic limiter (limitConn), obscuring the underlying *DirectForwardConn
type and causing 'sshd: wrong connection type' whenever a limiter was
configured.
Add an UnwrapConn() method to the traffic limiter wrapper and an
unwrapConn() helper in the handler that peels through wrapper layers
before the type switch. The handler uses the unwrapped conn only for
the type-specific DstAddr() call, while the original limiter-wrapped
conn drives data transfer so rate limiting remains effective.
Also track the last accept/bind error on service Status so callers can
retrieve the cause when a service enters the failed state.
- Add NewLimiterWithBurst(rate, burst) for independent burst control
- Extend parseLimit to accept optional 4th field as burst size
- Add DroppedPacketCounter interface with DroppedPackets() for packet/udp conns
- Export ErrRateLimited sentinel for rate-limit error checks
- Fix IP-level cache expiration (NoExpiration → defaultExpiration) to prevent
unbounded growth; reset TTL on access in In/Out
- Short-circuit single-limiter path to avoid limiterGroup allocation
- Add nil guard in cachedTrafficLimiter when both old and new values are nil
- Remove unused ctx field from wrapper structs