From cc737d789ee4828b47b2dfba261d1c3f50590114 Mon Sep 17 00:00:00 2001 From: win Date: Wed, 1 Apr 2026 12:55:13 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20SOCKS5ProxyDialer=20=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20ContextDialer=20=E9=81=BF=E5=85=8D=20Docker=20=E5=86=85?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=20DNS=20=E8=A7=A3=E6=9E=90=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 原实现 proxy.SOCKS5(..., proxy.Direct) 会先在本地做 DNS 解析 Docker 容器内无法解析 platform.claude.com 导致 30s 超时 - 改用 &net.Dialer{} + DialContext 让域名直接发给代理端远端解析 - 同时影响 OAuth token exchange 和 API 请求的 SOCKS5 路由 --- backend/internal/pkg/tlsfingerprint/dialer.go | 8 ++++++-- backend/internal/repository/claude_oauth_service.go | 7 +++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/backend/internal/pkg/tlsfingerprint/dialer.go b/backend/internal/pkg/tlsfingerprint/dialer.go index c8d8369f..cc78cd14 100644 --- a/backend/internal/pkg/tlsfingerprint/dialer.go +++ b/backend/internal/pkg/tlsfingerprint/dialer.go @@ -160,7 +160,11 @@ func (d *SOCKS5ProxyDialer) DialTLSContext(ctx context.Context, network, addr st proxyAddr = net.JoinHostPort(d.proxyURL.Hostname(), "1080") // Default SOCKS5 port } - socksDialer, err := proxy.SOCKS5("tcp", proxyAddr, auth, proxy.Direct) + // Use a TCP-only forward dialer (no DNS resolution) so the SOCKS5 protocol + // sends the target hostname to the proxy for remote DNS resolution (socks5h semantics). + // proxy.Direct would attempt local DNS first, which fails inside Docker. + tcpDialer := &net.Dialer{} + socksDialer, err := proxy.SOCKS5("tcp", proxyAddr, auth, tcpDialer) if err != nil { slog.Debug("tls_fingerprint_socks5_dialer_failed", "error", err) return nil, fmt.Errorf("create SOCKS5 dialer: %w", err) @@ -168,7 +172,7 @@ func (d *SOCKS5ProxyDialer) DialTLSContext(ctx context.Context, network, addr st // Step 2: Establish SOCKS5 tunnel to target slog.Debug("tls_fingerprint_socks5_establishing_tunnel", "target", addr) - conn, err := socksDialer.Dial("tcp", addr) + conn, err := socksDialer.(proxy.ContextDialer).DialContext(ctx, "tcp", addr) if err != nil { slog.Debug("tls_fingerprint_socks5_connect_failed", "error", err) return nil, fmt.Errorf("SOCKS5 connect: %w", err) diff --git a/backend/internal/repository/claude_oauth_service.go b/backend/internal/repository/claude_oauth_service.go index c27150ed..72bcbd81 100644 --- a/backend/internal/repository/claude_oauth_service.go +++ b/backend/internal/repository/claude_oauth_service.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "net/http" "net/url" "strings" @@ -287,11 +288,9 @@ func createReqClient(proxyURL string) (*req.Client, error) { return nil, err } - logger.LegacyPrintf("repository.claude_oauth", "[OAuth] createReqClient: proxyURL=%q trimmed=%q", logredact.RedactProxyURL(proxyURL), logredact.RedactProxyURL(trimmed)) - if trimmed != "" && parsedProxy != nil { scheme := strings.ToLower(parsedProxy.Scheme) - logger.LegacyPrintf("repository.claude_oauth", "[OAuth] createReqClient: using proxy scheme=%s host=%s", scheme, parsedProxy.Hostname()) + slog.Info("oauth_create_client", "proxy_scheme", scheme, "proxy_host", parsedProxy.Hostname()) switch scheme { case "socks5", "socks5h": socks5Dialer := tlsfingerprint.NewSOCKS5ProxyDialer(profile, parsedProxy) @@ -303,7 +302,7 @@ func createReqClient(proxyURL string) (*req.Client, error) { client.SetProxyURL(trimmed) } } else { - logger.LegacyPrintf("repository.claude_oauth", "[OAuth] createReqClient: no proxy, using direct connection with utls") + slog.Info("oauth_create_client", "proxy_scheme", "none", "raw_proxy_url", proxyURL) dialer := tlsfingerprint.NewDialer(profile, nil) client.SetDialTLS(dialer.DialTLSContext) }