The issue #777 analysis was written against v3.2.4; commits 5484717 and
9fab332 already fixed the global mutex stall, the no-timeout OpenStreamSync,
and the missing IsClosed reuse check. This addresses the remaining real
problems:
- P1 (HIGH): quicDialer.Dial received the Router's 15s-timeout ctx but
GetConn built its own context.Background()+30s, dropping request
cancellation/deadline. Thread ctx into GetConn so OpenStreamSync honors
the upstream deadline. Mirrored to the icmp dialer (identical structure).
- P2 (MEDIUM): listener QUIC keepalive defaulted OFF while the dialer
defaulted ON, so a server behind NAT without an explicit keepalive=true
could let its return-path mapping expire. Make the listener default ON
like the dialer.
- P3 (LOW): set an explicit MaxIdleTimeout default (90s) on both dialer
and listener so dead sessions are detected sooner; still overridable via
maxIdleTimeout=.
Docs updated to mark the already-fixed root causes and rename the remaining
items P1-P3.
Fixesgo-gost/gost#777
The ICMP dialer required explicit keepalive:true to send QUIC PING frames,
while the regular QUIC dialer enabled keepalive by default (10s period).
Without PING frames, quic-go's 30s idle timeout silently killed connections,
causing HTTP+ICMP tunnels to hang after periods of inactivity.
Fixesgo-gost/gost#352
Also in this commit:
- Release session mutex before calling GetConn() in both ICMP and QUIC
dialers, so a dead session doesn't block all Dial() calls while
OpenStreamSync blocks
- Add 30s timeout context to OpenStreamSync to prevent indefinite blocking
if the QUIC session dies between liveness check and stream open
- Guard session cache deletion with pointer identity check to avoid
evicting a newly created session during a race
When a QUIC session's MaxIdleTimeout expires, the underlying *quic.Conn dies
but the dead quicSession stays in the sessions map. The next Dial() finds
it and calls session.GetConn() which calls OpenStreamSync(context.Background())
against the dead connection — blocking indefinitely.
Add IsClosed() to both quicSession types (using the session context's Done()
channel) and add the liveness guard in both Dial() methods, matching the
existing pattern in KCP and MASQUE dialers.
Fixesgo-gost/gost#225
The QUIC listener and the QUIC/ICMP dialers unconditionally set
tlsCfg.NextProtos = ["h3", "quic/v1"], discarding any user-specified
ALPN values from TLS options (via tls.options.alpn in config).
Clone the TLS config and only apply the default NextProtos when none are
already configured, matching the existing pattern in the MASQUE HTTP/3
dialer. This enables non-HTTP/3 QUIC use cases like SMB-over-QUIC which
requires ALPN "smb".
Fixesgo-gost/gost#872
On Windows, icmp.ListenPacket("ip4:icmp", "0.0.0.0") does not reliably
deliver ICMP packets (golang/go#38427). Add a platform-specific
ListenPacket helper that binds to a discovered interface address and
enables SIO_RCVALL via WSAIoctl for promiscuous receive.
Fixesgo-gost/x#36