From e65211e777eff9ffec676720b47b8ea1a7a9984a Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Sun, 18 Jan 2026 03:07:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(proxy):=20=E4=BF=AE=E5=A4=8D=E9=80=8F?= =?UTF-8?q?=E6=98=8E=E4=BB=A3=E7=90=86=E5=AF=BC=E8=87=B4=E8=BE=93=E5=87=BA?= =?UTF-8?q?=E5=85=A8=E7=AB=AF=E5=8F=A3=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在代理初始化时主动探测代理行为,通过连接 RFC 5737 保留的 测试地址来检测是否存在"全回显"问题。如果探测到代理不可靠, 则在端口扫描时跳过所有端口,避免误报。 - 新增 proxyReliable 标志位标记代理可靠性 - 新增 ProbeProxyBehavior 函数探测代理行为 - 端口扫描前检查代理可靠性并输出警告 Fixes #495 --- common/network.go | 5 +++++ common/proxy/detector.go | 33 +++++++++++++++++++++++++++++++-- common/proxy/manager.go | 37 ++++++++++++++++++++++++++++++++++++- core/port_scan.go | 11 +++++++++++ 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/common/network.go b/common/network.go index 6e707e3..b1e62c0 100644 --- a/common/network.go +++ b/common/network.go @@ -153,6 +153,11 @@ func IsProxyEnabled() bool { return proxy.IsProxyEnabled() } +// IsProxyReliable 检查代理是否可靠(不存在全回显问题) +func IsProxyReliable() bool { + return proxy.IsProxyReliable() +} + // SafeHTTPDo 带发包控制的HTTP请求 func SafeHTTPDo(client *http.Client, req *http.Request) (*http.Response, error) { // 检查发包限制 diff --git a/common/proxy/detector.go b/common/proxy/detector.go index beb999d..fe6e7c6 100644 --- a/common/proxy/detector.go +++ b/common/proxy/detector.go @@ -13,6 +13,12 @@ var ( // proxyInitialized 标记代理是否已初始化 proxyInitialized atomic.Bool + + // proxyReliable 标记代理是否可靠(不存在全回显问题) + proxyReliable atomic.Bool + + // proxyProbed 标记代理是否已经探测过(避免重复探测) + proxyProbed atomic.Bool ) // SetProxyEnabled 设置代理启用状态 @@ -45,6 +51,26 @@ func IsProxyInitialized() bool { return proxyInitialized.Load() } +// SetProxyReliable 设置代理可靠性状态 +func SetProxyReliable(reliable bool) { + proxyReliable.Store(reliable) +} + +// IsProxyReliable 检查代理是否可靠(不存在全回显问题) +func IsProxyReliable() bool { + return proxyReliable.Load() +} + +// SetProxyProbed 设置代理已探测标志 +func SetProxyProbed(probed bool) { + proxyProbed.Store(probed) +} + +// IsProxyProbed 检查代理是否已探测过 +func IsProxyProbed() bool { + return proxyProbed.Load() +} + // AutoConfigureProxy 自动配置代理相关行为 // 根据代理类型和状态自动调整扫描策略 func AutoConfigureProxy(config *ProxyConfig) { @@ -52,19 +78,22 @@ func AutoConfigureProxy(config *ProxyConfig) { SetProxyEnabled(false) SetSOCKS5Standard(false) SetProxyInitialized(false) + SetProxyReliable(true) // 无代理时默认可靠 return } // 启用代理标记 SetProxyEnabled(true) - // SOCKS5代理默认假设非标准(后续可以动态探测) + // SOCKS5代理默认假设非标准(后续由探测函数验证) if config.Type == ProxyTypeSOCKS5 { SetSOCKS5Standard(false) + SetProxyReliable(true) // 默认可靠,后续由 ProbeProxyBehavior 更新 } - // HTTP/HTTPS代理视为标准 + // HTTP/HTTPS代理视为标准且可靠 if config.Type == ProxyTypeHTTP || config.Type == ProxyTypeHTTPS { SetSOCKS5Standard(true) + SetProxyReliable(true) } } diff --git a/common/proxy/manager.go b/common/proxy/manager.go index 6fb599d..b1cb945 100644 --- a/common/proxy/manager.go +++ b/common/proxy/manager.go @@ -33,7 +33,7 @@ func NewProxyManager(config *ProxyConfig) ProxyManager { // 自动配置代理行为 AutoConfigureProxy(config) - return &manager{ + m := &manager{ config: config, stats: &ProxyStats{ ProxyType: config.Type.String(), @@ -42,6 +42,19 @@ func NewProxyManager(config *ProxyConfig) ProxyManager { dialerCache: make(map[string]Dialer), cacheExpiry: time.Now().Add(DefaultCacheExpiry), } + + // 对 SOCKS5 代理进行行为探测,检测是否存在"全回显"问题 + // 只探测一次,避免重复输出警告 + if config.Type == ProxyTypeSOCKS5 && !IsProxyProbed() { + SetProxyProbed(true) + dialer, err := m.createSOCKS5Dialer() + if err == nil { + reliable := ProbeProxyBehavior(dialer, config.Timeout) + SetProxyReliable(reliable) + } + } + + return m } // GetDialer 获取普通拨号器 @@ -350,3 +363,25 @@ func (s *socks5Dialer) updateAverageConnectTime(duration time.Duration) { s.stats.AverageConnectTime = (s.stats.AverageConnectTime + duration) / 2 } } + +// ProbeProxyBehavior 探测代理是否存在"全回显"问题 +// 通过连接一个几乎肯定不可达的地址来判断代理行为 +// 返回 true 表示代理可靠,false 表示代理存在全回显问题 +func ProbeProxyBehavior(dialer Dialer, timeout time.Duration) bool { + // 使用 RFC 5737 保留的测试 IP (TEST-NET-1) + 高端口 + // 192.0.2.1 是文档专用地址,保证不会路由到真实主机 + testAddr := "192.0.2.1:65533" + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + conn, err := dialer.DialContext(ctx, "tcp", testAddr) + if err != nil { + // 连接失败 = 正常代理行为,代理可靠 + return true + } + + // 连接"成功" = 代理存在全回显问题,不可靠 + _ = conn.Close() + return false +} diff --git a/core/port_scan.go b/core/port_scan.go index 5993c1e..9b78318 100644 --- a/core/port_scan.go +++ b/core/port_scan.go @@ -148,6 +148,11 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64, config *commo exclude[p] = struct{}{} } + // 检查代理可靠性,如果存在全回显问题则警告 + if common.IsProxyEnabled() && !common.IsProxyReliable() { + common.LogBase("[!] 检测到代理存在全回显问题,端口扫描结果可能不准确") + } + // 创建流式迭代器(O(1) 内存,端口喷洒策略) iter := NewSocketIterator(hosts, portList, exclude) totalTasks := iter.Total() @@ -375,6 +380,12 @@ func verifyProxyConnection(conn net.Conn, addr string) bool { return true } + // 如果代理不可靠(存在全回显问题),直接返回 false + if !common.IsProxyReliable() { + common.LogDebug(fmt.Sprintf("代理不可靠,跳过端口 %s", addr)) + return false + } + // 设置短超时进行连接验证(100ms) // 如果目标端口真的开放,不会在这么短时间内收到错误 // 如果目标不可达,非标准代理可能会立即返回错误