fix(sniffing): re-dial upstream on HTTP Host change + surface no-SNI errors

sniffer_http:
When DNS override directs multiple domains to the same proxy IP, the
browser may reuse a keep-alive connection for a different host. The
HTTP keep-alive loop now detects Host header changes and re-dials a
new upstream connection with correct node selection, preventing CDN
errors (Fastly unknown domain, CloudFront 403).

sniffer_tls:
Return a descriptive error when TLS ClientHello has no SNI, instead
of silently returning nil. This makes the connection drop visible in
logs and recorder output.

Fixes go-gost/gost#479
This commit is contained in:
ginuerzh
2026-06-21 16:37:47 +08:00
parent a102f9c6cf
commit e814852ca1
4 changed files with 64 additions and 4 deletions
+38 -1
View File
@@ -81,7 +81,9 @@ func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleO
if err != nil {
return err
}
defer cc.Close()
defer func() { cc.Close() }()
upstreamHost := normalizeHost(ro.HTTP.Host, "80")
ho.log = log.WithFields(map[string]any{"src": cc.LocalAddr().String(), "dst": cc.RemoteAddr().String()})
log = ho.log
@@ -112,6 +114,41 @@ func (h *Sniffer) HandleHTTP(ctx context.Context, conn net.Conn, opts ...HandleO
log.Trace(string(dump))
}
// When DNS override directs multiple domains to the same proxy IP,
// the browser may reuse a keep-alive connection for a different host.
// Re-dial to ensure requests reach the correct upstream.
if reqHost := normalizeHost(req.Host, "80"); reqHost != "" && reqHost != upstreamHost {
cc.Close()
newNode, res, resolveErr := resolveHTTPNode(ctx, reqHost, req, &ho)
if resolveErr != nil {
ro.HTTP.StatusCode = res.StatusCode
res.Write(conn)
return resolveErr
}
dial := ho.dial
if dial == nil {
dial = (&net.Dialer{}).DialContext
}
newCC, dialErr := dial(ctx, "tcp", newNode.Addr)
if dialErr != nil {
return dialErr
}
newCC = tlsWrapConn(newCC, newNode.Options().TLS)
upstreamHost = reqHost
node = newNode
cc = newCC
ro.Host = reqHost
ro.SrcAddr = cc.LocalAddr().String()
ro.DstAddr = cc.RemoteAddr().String()
log = log.WithFields(map[string]any{
"host": reqHost,
"node": node.Name,
"dst": node.Addr,
"src": cc.LocalAddr().String(),
})
ho.log = log
}
if shouldClose, err := h.httpRoundTrip(ctx, xio.NewReadWriteCloser(br, conn, conn), cc, node, req, &pStats, &ho); err != nil || shouldClose {
return err
}