fix(handler): forward TLS connections in auto handler to chain router

The auto handler only recognized SOCKS4 (0x04), SOCKS5 (0x05), and HTTP
(first byte) as protocol indicators. TLS ClientHello bytes (0x16) fell
through to the HTTP handler, which called http.ReadRequest() on binary
TLS data and returned a malformed-HTTP error.

Add protocol sniffing via sniffing.Sniff() to detect TLS. When detected,
delegate to sniffing.Sniffer.HandleTLS() which parses the ClientHello
for SNI, dials upstream through the chain router (respecting -F config),
and proxies the TLS connection transparently.

Fixes go-gost/gost#492
This commit is contained in:
ginuerzh
2026-06-21 21:19:29 +08:00
parent 3f01456480
commit 3ec3362ac4
+45 -1
View File
@@ -2,6 +2,7 @@ package auto
import ( import (
"bufio" "bufio"
"bytes"
"context" "context"
"net" "net"
"time" "time"
@@ -12,7 +13,10 @@ import (
"github.com/go-gost/gosocks4" "github.com/go-gost/gosocks4"
"github.com/go-gost/gosocks5" "github.com/go-gost/gosocks5"
ctxvalue "github.com/go-gost/x/ctx" ctxvalue "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
xnet "github.com/go-gost/x/internal/net" xnet "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/internal/util/sniffing"
xrecorder "github.com/go-gost/x/recorder"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@@ -94,6 +98,16 @@ func (h *autoHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
} }
br := bufio.NewReader(conn) 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.
b, err := br.Peek(1) b, err := br.Peek(1)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
@@ -101,7 +115,6 @@ func (h *autoHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
return err return err
} }
conn = xnet.NewReadWriteConn(br, conn, conn)
switch b[0] { switch b[0] {
case gosocks4.Ver4: // socks4 case gosocks4.Ver4: // socks4
if h.socks4Handler != nil { if h.socks4Handler != nil {
@@ -118,3 +131,34 @@ func (h *autoHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
} }
return nil return nil
} }
// handleTLS forwards a TLS connection that arrived at the auto handler.
// It parses the ClientHello for SNI, dials upstream through the router,
// forwards the ClientHello, records ServerHello metadata, and pipes data
// bidirectionally.
func (h *autoHandler) handleTLS(ctx context.Context, conn net.Conn, log logger.Logger) error {
ro := &xrecorder.HandlerRecorderObject{
Network: "tcp",
Service: h.options.Service,
RemoteAddr: conn.RemoteAddr().String(),
LocalAddr: conn.LocalAddr().String(),
SID: ctxvalue.SidFromContext(ctx).String(),
Time: time.Now(),
}
sniffer := &sniffing.Sniffer{}
dial := func(ctx context.Context, network, address string) (net.Conn, error) {
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ictx.ContextWithBuffer(ctx, &buf), "tcp", address)
if err != nil {
return nil, err
}
ro.Route = buf.String()
return cc, nil
}
return sniffer.HandleTLS(ctx, "tcp", conn,
sniffing.WithDial(dial),
sniffing.WithBypass(h.options.Bypass),
sniffing.WithRecorderObject(ro),
sniffing.WithLog(log),
)
}