diff --git a/common/i18n/locales/en.yaml b/common/i18n/locales/en.yaml index 7e7d2ac..dfe4b96 100644 --- a/common/i18n/locales/en.yaml +++ b/common/i18n/locales/en.yaml @@ -429,6 +429,10 @@ port_open_http: other: "Port open {{.Arg1}} [http](HTTP probe)" port_scan_no_alive_subnet: other: "Subnet probe found no alive subnets, skipping port scan" +port_scan_task_dropped: + other: "[PortScan] task dropped: {{.Arg1}} ({{.Arg2}}), port may be missed" +port_scan_tasks_dropped_total: + other: "[PortScan] {{.Arg1}} tasks dropped total, these ports may be missed" network_rate_limited_pattern: other: "Rate limited" port_scan_debug_start: diff --git a/common/i18n/locales/zh.yaml b/common/i18n/locales/zh.yaml index 9e40f41..035e476 100644 --- a/common/i18n/locales/zh.yaml +++ b/common/i18n/locales/zh.yaml @@ -429,6 +429,10 @@ port_open_http: other: "端口开放 {{.Arg1}} [http](HTTP探测)" port_scan_no_alive_subnet: other: "网段预筛未发现存活子网,跳过端口扫描" +port_scan_task_dropped: + other: "[PortScan] 任务被丢弃: {{.Arg1}} ({{.Arg2}}),该端口可能被漏扫" +port_scan_tasks_dropped_total: + other: "[PortScan] 共有 {{.Arg1}} 个任务被丢弃,这些端口可能被漏扫" network_rate_limited_pattern: other: "发包受限" port_scan_debug_start: diff --git a/core/adaptive_timeout.go b/core/adaptive_timeout.go index f7d98c5..51ce5bd 100644 --- a/core/adaptive_timeout.go +++ b/core/adaptive_timeout.go @@ -25,10 +25,17 @@ type AdaptiveTimeout struct { // NewAdaptiveTimeout 创建自适应超时计算器 // maxTimeout: 用户配置的超时上限(即原始固定超时) func NewAdaptiveTimeout(maxTimeout time.Duration) *AdaptiveTimeout { + // minTO: 自适应超时下限,取 max(500ms, maxTimeout/5) + // 依据:高并发下 TCP 握手存在尾延迟(OS 调度抖动、backlog 溢出、端口竞争), + // 过低的下限会导致开放端口被误判为关闭(issue #503) + minTO := maxTimeout / 5 + if minTO < 500*time.Millisecond { + minTO = 500 * time.Millisecond + } return &AdaptiveTimeout{ samples: make([]float64, 64), size: 64, - minTO: 100 * time.Millisecond, + minTO: minTO, maxTO: maxTimeout, warmup: 10, } diff --git a/core/port_scan.go b/core/port_scan.go index 9a95820..3a34b6a 100644 --- a/core/port_scan.go +++ b/core/port_scan.go @@ -290,6 +290,7 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout // slidingWindowSchedule 滑动窗口调度器 // ants.PoolWithFunc.Invoke 在池满时阻塞,天然提供反压,无需额外 semaphore func slidingWindowSchedule(iter *SocketIterator, pool *AdaptivePool, wg *sync.WaitGroup) { + var dropped int64 for { host, port, ok := iter.Next() if !ok { @@ -304,11 +305,17 @@ func slidingWindowSchedule(iter *SocketIterator, pool *AdaptivePool, wg *sync.Wa } if err := pool.Invoke(task); err != nil { wg.Done() + dropped++ + common.LogError(i18n.Tr("port_scan_task_dropped", task.addr, err)) } } // 等待所有任务完成 wg.Wait() + + if dropped > 0 { + common.LogError(i18n.Tr("port_scan_tasks_dropped_total", dropped)) + } } // fmtPort 无分配的端口号格式化 @@ -327,7 +334,10 @@ func fmtPort(port int) string { return string(buf[i:]) } -// connectWithRetry 带重试的TCP连接 - 只对资源耗尽错误重试 +// connectWithRetry 带重试的TCP连接 +// - 资源耗尽错误:指数退避重试(maxRetries 次) +// - 其他错误(如 connection refused、timeout):直接返回 +// timeout 是正常的扫描结果(防火墙 drop / filtered),不盲目重试 func connectWithRetry(ctx context.Context, session *common.ScanSession, addr string, timeout time.Duration, maxRetries int) (net.Conn, error) { var lastErr error @@ -340,9 +350,9 @@ func connectWithRetry(ctx context.Context, session *common.ScanSession, addr str lastErr = err - // 只对资源耗尽类错误重试,端口关闭直接返回 + // 只对资源耗尽类错误重试,端口关闭或超时直接返回 if !isResourceExhaustedError(err) { - return nil, err + return nil, lastErr } // 记录资源耗尽错误 diff --git a/core/real_network_test.go b/core/real_network_test.go index 74b396b..df95647 100644 --- a/core/real_network_test.go +++ b/core/real_network_test.go @@ -440,11 +440,15 @@ func TestReal_AdaptiveTimeout_Convergence(t *testing.T) { if converged >= 3*time.Second { t.Errorf("采样后 Timeout = %v, 应该 < 3s", converged) } - if converged < 100*time.Millisecond { - t.Logf("Timeout 收敛到 %v(localhost,正常)", converged) + + // minTO 下限断言:max(500ms, 3s/5) = 600ms + // localhost RTT 极低,收敛值应贴在地板上(issue #503) + minFloor := 600 * time.Millisecond + if converged < minFloor { + t.Errorf("收敛后 Timeout = %v, 不应低于 minTO 下限 %v", converged, minFloor) } - t.Logf("AdaptiveTimeout 收敛: 3s -> %v (%d 个样本)", converged, 20) + t.Logf("AdaptiveTimeout 收敛: 3s -> %v (%d 个样本), minTO=%v", converged, 20, minFloor) } // =============================================================================