From 382d47ea127cbf07199e94defbc0c4b1032ddad4 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Mon, 1 Jun 2026 18:28:01 +0800 Subject: [PATCH] fix: nil HTTP recorder ref in WebSocket goroutines, add tunnel loop detection - handler/http/websocket, forwarder/sniffer_ws, sniffing/sniffer_ws: nil ro2.HTTP in WebSocket copy goroutines to avoid data races on the shared HTTP recorder object - handler/tunnel/bind: remove ingress rule check that incorrectly overrode the endpoint host for non-matching ingress rules - handler/tunnel/entrypoint: add forwarding loop detection by checking Gost-Forwarded-Node header for the entrypoint's own node ID --- handler/http/websocket.go | 1 + handler/tunnel/bind.go | 4 +--- handler/tunnel/entrypoint.go | 18 ++++++++++++++++++ internal/util/forwarder/sniffer_ws.go | 2 ++ internal/util/sniffing/sniffer_ws.go | 2 ++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/handler/http/websocket.go b/handler/http/websocket.go index 0bba5d84..6fd4c6d8 100644 --- a/handler/http/websocket.go +++ b/handler/http/websocket.go @@ -51,6 +51,7 @@ func (h *httpHandler) copyWebsocketDirection(ctx context.Context, r io.Reader, w } ro2 := &xrecorder.HandlerRecorderObject{} *ro2 = *ro + ro2.HTTP = nil r2 := ro2 limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate)) diff --git a/handler/tunnel/bind.go b/handler/tunnel/bind.go index 77bb47f2..ca0dfc93 100644 --- a/handler/tunnel/bind.go +++ b/handler/tunnel/bind.go @@ -41,9 +41,7 @@ func (h *tunnelHandler) handleBind(ctx context.Context, conn net.Conn, network, if host == "" || h.md.ingress == nil { host = endpoint } else if host != endpoint { - if rule := h.md.ingress.GetRule(ctx, host, ingress.WithService(h.options.Service)); rule != nil && rule.Endpoint != tunnelID.String() { - host = endpoint - } + host = endpoint } addr := net.JoinHostPort(host, port) diff --git a/handler/tunnel/entrypoint.go b/handler/tunnel/entrypoint.go index 3811bfd1..3aa825ca 100644 --- a/handler/tunnel/entrypoint.go +++ b/handler/tunnel/entrypoint.go @@ -278,6 +278,24 @@ func (ep *entrypoint) httpRoundTrip(ctx context.Context, rw io.ReadWriteCloser, } else { req.Header.Set(httpHeaderSID, ro.SID) } + + // Loop detection: if Gost-Forwarded-Node contains our own node ID, + // the request has already passed through this entrypoint and is looping. + for _, node := range strings.Split(req.Header.Get(httpHeaderForwardedNode), ",") { + if strings.TrimSpace(node) == ep.node { + log.Warn("forwarding loop detected, rejecting request") + res := &http.Response{ + ProtoMajor: req.ProtoMajor, + ProtoMinor: req.ProtoMinor, + Header: http.Header{}, + StatusCode: http.StatusServiceUnavailable, + } + ro.HTTP.StatusCode = res.StatusCode + res.Write(rw) + return nil + } + } + req.Header.Set(httpHeaderForwardedNode, ro.Node) host := req.Host diff --git a/internal/util/forwarder/sniffer_ws.go b/internal/util/forwarder/sniffer_ws.go index 42c167fb..7db35512 100644 --- a/internal/util/forwarder/sniffer_ws.go +++ b/internal/util/forwarder/sniffer_ws.go @@ -31,6 +31,7 @@ func (h *Sniffer) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWrit go func() { ro2 := &xrecorder.HandlerRecorderObject{} *ro2 = *ro + ro2.HTTP = nil ro := ro2 limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate)) @@ -57,6 +58,7 @@ func (h *Sniffer) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWrit go func() { ro2 := &xrecorder.HandlerRecorderObject{} *ro2 = *ro + ro2.HTTP = nil ro := ro2 limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate)) diff --git a/internal/util/sniffing/sniffer_ws.go b/internal/util/sniffing/sniffer_ws.go index d657f212..ae136bfb 100644 --- a/internal/util/sniffing/sniffer_ws.go +++ b/internal/util/sniffing/sniffer_ws.go @@ -30,6 +30,7 @@ func (h *Sniffer) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWrit go func() { ro2 := &xrecorder.HandlerRecorderObject{} *ro2 = *ro + ro2.HTTP = nil ro := ro2 limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate)) @@ -56,6 +57,7 @@ func (h *Sniffer) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWrit go func() { ro2 := &xrecorder.HandlerRecorderObject{} *ro2 = *ro + ro2.HTTP = nil ro := ro2 limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))