From c1d954d6895ff5e4899aaf63a787db8dab7081c0 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Fri, 26 Jun 2026 20:17:49 +0800 Subject: [PATCH] fix(handler/auto): sniff SOCKS4/SOCKS5 with 1-byte peek before 5-byte Sniff() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sniff() calls Peek(5) which blocks on SOCKS5 greeting (3 bytes: VER, NMETHODS, METHODS). The SOCKS5 connector sends those 3 bytes and then waits for the server's method-selection reply — deadlock. Restore the original 1-byte peek for SOCKS4 (0x04) / SOCKS5 (0x05) detection first, routing them to their handlers immediately. Only call Sniff() for non-SOCKS traffic (TLS, HTTP, SSH) where 5 bytes are guaranteed. Fixes go-gost/gost#879 --- handler/auto/handler.go | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/handler/auto/handler.go b/handler/auto/handler.go index e78e980c..e33187b0 100644 --- a/handler/auto/handler.go +++ b/handler/auto/handler.go @@ -98,16 +98,13 @@ func (h *autoHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler } br := bufio.NewReader(conn) - proto, _ := sniffing.Sniff(ctx, br) - conn = xnet.NewReadWriteConn(br, conn, conn) - - if proto == sniffing.ProtoTLS { - return h.handleTLS(ctx, conn, log) - } - - // Fall back to 1-byte peek for SOCKS4 (0x04) vs SOCKS5 (0x05) vs HTTP. - // Sniff() already read 5 bytes, but the buffered reader exposes them. + // Peek 1 byte first to detect small protocols (SOCKS4/SOCKS5) + // that send fewer bytes than the 5-byte TLS record header that + // Sniff() needs. If we called Sniff() first, its Peek(5) would + // block indefinitely on a SOCKS5 greeting (3 bytes: VER, NMETHODS, + // METHODS), deadlocking with the client that is itself waiting for + // the server's method-selection reply. b, err := br.Peek(1) if err != nil { log.Error(err) @@ -116,18 +113,32 @@ func (h *autoHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler } switch b[0] { - case gosocks4.Ver4: // socks4 + case gosocks4.Ver4: + conn = xnet.NewReadWriteConn(br, conn, conn) if h.socks4Handler != nil { return h.socks4Handler.Handle(ctx, conn) } - case gosocks5.Ver5: // socks5 + return nil + case gosocks5.Ver5: + conn = xnet.NewReadWriteConn(br, conn, conn) if h.socks5Handler != nil { return h.socks5Handler.Handle(ctx, conn) } - default: // http - if h.httpHandler != nil { - return h.httpHandler.Handle(ctx, conn) - } + return nil + } + + // Not SOCKS — sniff for TLS, HTTP, or SSH. + proto, _ := sniffing.Sniff(ctx, br) + + conn = xnet.NewReadWriteConn(br, conn, conn) + + if proto == sniffing.ProtoTLS { + return h.handleTLS(ctx, conn, log) + } + + // Default to HTTP. + if h.httpHandler != nil { + return h.httpHandler.Handle(ctx, conn) } return nil }