Fix memory leaks
This commit is contained in:
+19
-8
@@ -833,18 +833,19 @@ func handleDNSTTStream(stream *smux.Stream, conv uint32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// streamConn adapts a smux.Stream to the net.Conn interface expected by
|
// streamConn adapts a smux.Stream to the net.Conn interface expected by
|
||||||
// handleConn. smux.Stream already implements Read and Write, but does not
|
// handleConn. smux.Stream implements Read, Write, and (via its embedded
|
||||||
// satisfy net.Conn because it lacks methods for deadlines and addresses. We
|
// *stream) real deadline methods, but its addresses reflect the underlying
|
||||||
// implement those methods with no‑ops and placeholder addresses.
|
// KCP/Noise DummyAddr, so we only override LocalAddr/RemoteAddr with a stable
|
||||||
|
// "dnstt" placeholder for logging. The deadline methods are intentionally NOT
|
||||||
|
// overridden: handleConn's sshHandshakeTimeout guard relies on SetReadDeadline
|
||||||
|
// actually working, otherwise a stream that opens but never completes the SSH
|
||||||
|
// handshake wedges a goroutine until MaxStreams is hit.
|
||||||
type streamConn struct {
|
type streamConn struct {
|
||||||
*smux.Stream
|
*smux.Stream
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *streamConn) LocalAddr() net.Addr { return dummyAddr{} }
|
func (s *streamConn) LocalAddr() net.Addr { return dummyAddr{} }
|
||||||
func (s *streamConn) RemoteAddr() net.Addr { return dummyAddr{} }
|
func (s *streamConn) RemoteAddr() net.Addr { return dummyAddr{} }
|
||||||
func (s *streamConn) SetDeadline(t time.Time) error { return nil }
|
|
||||||
func (s *streamConn) SetReadDeadline(t time.Time) error { return nil }
|
|
||||||
func (s *streamConn) SetWriteDeadline(t time.Time) error { return nil }
|
|
||||||
|
|
||||||
// dummyAddr is a stand‑in net.Addr implementation for dnstt streams. It
|
// dummyAddr is a stand‑in net.Addr implementation for dnstt streams. It
|
||||||
// reports a generic network and address; this satisfies the net.Conn
|
// reports a generic network and address; this satisfies the net.Conn
|
||||||
@@ -859,11 +860,21 @@ func (d dummyAddr) String() string { return "dnstt" }
|
|||||||
// Session, then waits for smux streams. Each stream is passed to
|
// Session, then waits for smux streams. Each stream is passed to
|
||||||
// handleDNSTTStream. Any errors from the Noise or smux layers are returned.
|
// handleDNSTTStream. Any errors from the Noise or smux layers are returned.
|
||||||
func acceptDNSTTStreams(conn *kcp.UDPSession, privkey []byte, limits dnsttRuntimeLimits) error {
|
func acceptDNSTTStreams(conn *kcp.UDPSession, privkey []byte, limits dnsttRuntimeLimits) error {
|
||||||
|
// Bound the Noise handshake read. A client can complete the KCP accept and
|
||||||
|
// then go silent (UDP: no FIN) before sending the Noise handshake; with no
|
||||||
|
// deadline, noise.NewServer's blocking ReadFull would park this goroutine
|
||||||
|
// and its KCP session forever (nothing frees them — smux keepalive cannot
|
||||||
|
// help because smux is not constructed until after the handshake). idleTimeout
|
||||||
|
// is generous: a real client sends the handshake immediately after connecting.
|
||||||
|
_ = conn.SetReadDeadline(time.Now().Add(idleTimeout))
|
||||||
// Put a Noise channel on top of the KCP conn.
|
// Put a Noise channel on top of the KCP conn.
|
||||||
rw, err := noise.NewServer(conn, privkey)
|
rw, err := noise.NewServer(conn, privkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Clear the handshake deadline; the live session is governed by smux
|
||||||
|
// KeepAliveTimeout and KCP's own dead-link detection from here on.
|
||||||
|
_ = conn.SetReadDeadline(time.Time{})
|
||||||
|
|
||||||
// Put an smux session on top of the encrypted Noise channel.
|
// Put an smux session on top of the encrypted Noise channel.
|
||||||
smuxConfig := smux.DefaultConfig()
|
smuxConfig := smux.DefaultConfig()
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Hard timeouts to ensure half-open connections don't leak goroutines.
|
// Hard timeouts to ensure half-open connections don't leak goroutines.
|
||||||
sshHandshakeTimeout = 15 * time.Second
|
// This also bounds DNSTT SSH handshakes (streamConn now honors deadlines),
|
||||||
|
// which travel over DNS and need more headroom than a direct TCP handshake;
|
||||||
|
// 45s is generous for slow/lossy DNS paths while still reaping wedged streams.
|
||||||
|
sshHandshakeTimeout = 45 * time.Second
|
||||||
tlsHandshakeTimeout = 15 * time.Second
|
tlsHandshakeTimeout = 15 * time.Second
|
||||||
// Dial timeout for direct-tcpip backend connections.
|
// Dial timeout for direct-tcpip backend connections.
|
||||||
directTCPIPDialTimeout = 10 * time.Second
|
directTCPIPDialTimeout = 10 * time.Second
|
||||||
|
|||||||
Reference in New Issue
Block a user