fix: SOCKS5ProxyDialer 使用 ContextDialer 避免 Docker 内本地 DNS 解析失败

- 原实现 proxy.SOCKS5(..., proxy.Direct) 会先在本地做 DNS 解析
  Docker 容器内无法解析 platform.claude.com 导致 30s 超时
- 改用 &net.Dialer{} + DialContext 让域名直接发给代理端远端解析
- 同时影响 OAuth token exchange 和 API 请求的 SOCKS5 路由
This commit is contained in:
win 2026-04-01 12:55:13 +08:00
parent 1a6a077743
commit 78f91da858
2 changed files with 9 additions and 6 deletions

View File

@ -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)

View File

@ -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)
}