From d101f8afdc7d3e04d9e675c4efa1e30172054fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82az=CC=87ej=20Pankowski?= <86720177+pblazej@users.noreply.github.com> Date: Fri, 25 Sep 2026 16:00:54 +0200 Subject: [PATCH] Keep the in-flight read when an injected packet wins nextPacket An injected (DTLS-in-STUN) packet winning the select left the spawned nextConn read running. Whatever it read next went into a channel nobody received from, so each piggybacked handshake packet silently dropped one later packet from the peer (SCTP DATA, SACKs and their retransmissions), stalling data channels for seconds after a SPED handshake. Co-Authored-By: Claude Opus 5.5 (1M context) --- conn.go | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/conn.go b/conn.go index d73aa9258..42e6718ec 100644 --- a/conn.go +++ b/conn.go @@ -91,6 +91,7 @@ type Conn struct { reading chan struct{} handshakeRecv chan recvHandshakeState inboundPacketInject chan addrPkt + pendingRead chan readResult // in-flight nextConn read, owned by the read loop cancelHandshaker func() cancelHandshakeReader func() @@ -964,12 +965,33 @@ func (c *Conn) InjectInboundPacket(p []byte, rAddr net.Addr) { c.inboundPacketInject <- addrPkt{rAddr, p} } +type readResult struct { + data []byte + rAddr net.Addr + err error +} + func (c *Conn) nextPacket(ctx context.Context) ([]byte, net.Addr, error) { - type readResult struct { - data []byte - rAddr net.Addr - err error + // An injected packet can win the select below. Keep the network read in + // flight for the next call instead of abandoning it, or whatever it reads + // next is dropped. + if c.pendingRead == nil { + c.pendingRead = c.startRead(ctx) } + + select { + case p := <-c.inboundPacketInject: + return p.data, p.rAddr, nil + case p := <-c.pendingRead: + c.pendingRead = nil + + return p.data, p.rAddr, p.err + case <-ctx.Done(): + return nil, nil, ctx.Err() + } +} + +func (c *Conn) startRead(ctx context.Context) chan readResult { readCh := make(chan readResult, 1) go func() { @@ -998,14 +1020,8 @@ func (c *Conn) nextPacket(ctx context.Context) ([]byte, net.Addr, error) { rAddr: rAddr, } }() - select { - case p := <-c.inboundPacketInject: - return p.data, p.rAddr, nil - case p := <-readCh: - return p.data, p.rAddr, p.err - case <-ctx.Done(): - return nil, nil, ctx.Err() - } + + return readCh } func (c *Conn) readAndBuffer(ctx context.Context) error { //nolint:cyclop