mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-26 13:11:53 +08:00
perf: 端口扫描自适应超时,基于 RTT 采样动态调整连接超时
This commit is contained in:
@@ -0,0 +1,92 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AdaptiveTimeout 基于 RTT 采样的自适应超时计算器
|
||||||
|
// 算法:timeout = mean(RTT) + 4 * stddev(RTT),clamp 到 [min, max]
|
||||||
|
// 冷启动阶段(样本不足)返回用户配置的固定超时
|
||||||
|
type AdaptiveTimeout struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
samples []float64 // 环形缓冲区,单位 ms
|
||||||
|
pos int // 写入位置
|
||||||
|
count int // 已采集总数
|
||||||
|
size int // 缓冲区容量
|
||||||
|
minTO time.Duration
|
||||||
|
maxTO time.Duration
|
||||||
|
warmup int // 冷启动所需最小样本数
|
||||||
|
cachedTO time.Duration
|
||||||
|
dirty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAdaptiveTimeout 创建自适应超时计算器
|
||||||
|
// maxTimeout: 用户配置的超时上限(即原始固定超时)
|
||||||
|
func NewAdaptiveTimeout(maxTimeout time.Duration) *AdaptiveTimeout {
|
||||||
|
return &AdaptiveTimeout{
|
||||||
|
samples: make([]float64, 64),
|
||||||
|
size: 64,
|
||||||
|
minTO: 100 * time.Millisecond,
|
||||||
|
maxTO: maxTimeout,
|
||||||
|
warmup: 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record 记录一次成功连接的 RTT
|
||||||
|
func (a *AdaptiveTimeout) Record(rtt time.Duration) {
|
||||||
|
a.mu.Lock()
|
||||||
|
a.samples[a.pos%a.size] = float64(rtt.Milliseconds())
|
||||||
|
a.pos++
|
||||||
|
a.count++
|
||||||
|
a.dirty = true
|
||||||
|
a.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout 获取当前推荐超时值
|
||||||
|
// 样本不足时返回 maxTO(冷启动)
|
||||||
|
func (a *AdaptiveTimeout) Timeout() time.Duration {
|
||||||
|
a.mu.Lock()
|
||||||
|
defer a.mu.Unlock()
|
||||||
|
|
||||||
|
if a.count < a.warmup {
|
||||||
|
return a.maxTO
|
||||||
|
}
|
||||||
|
|
||||||
|
if !a.dirty {
|
||||||
|
return a.cachedTO
|
||||||
|
}
|
||||||
|
|
||||||
|
n := a.size
|
||||||
|
if a.count < a.size {
|
||||||
|
n = a.count
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum float64
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
sum += a.samples[i]
|
||||||
|
}
|
||||||
|
mean := sum / float64(n)
|
||||||
|
|
||||||
|
var variance float64
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
d := a.samples[i] - mean
|
||||||
|
variance += d * d
|
||||||
|
}
|
||||||
|
stddev := math.Sqrt(variance / float64(n))
|
||||||
|
|
||||||
|
ms := mean + 4*stddev
|
||||||
|
to := time.Duration(ms) * time.Millisecond
|
||||||
|
|
||||||
|
if to < a.minTO {
|
||||||
|
to = a.minTO
|
||||||
|
}
|
||||||
|
if to > a.maxTO {
|
||||||
|
to = a.maxTO
|
||||||
|
}
|
||||||
|
|
||||||
|
a.cachedTO = to
|
||||||
|
a.dirty = false
|
||||||
|
return to
|
||||||
|
}
|
||||||
+6
-2
@@ -173,6 +173,7 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout
|
|||||||
|
|
||||||
// 初始化并发控制
|
// 初始化并发控制
|
||||||
to := time.Duration(timeout) * time.Second
|
to := time.Duration(timeout) * time.Second
|
||||||
|
adaptiveTO := NewAdaptiveTimeout(to)
|
||||||
var count int64
|
var count int64
|
||||||
collector := newResultCollector()
|
collector := newResultCollector()
|
||||||
failedCollector := &failedPortCollector{}
|
failedCollector := &failedPortCollector{}
|
||||||
@@ -191,7 +192,7 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
addr := fmt.Sprintf("%s:%d", taskInfo.host, taskInfo.port)
|
addr := fmt.Sprintf("%s:%d", taskInfo.host, taskInfo.port)
|
||||||
scanSinglePort(ctx, taskInfo.host, taskInfo.port, addr, to, &count, collector, failedCollector, session)
|
scanSinglePort(ctx, taskInfo.host, taskInfo.port, addr, adaptiveTO, &count, collector, failedCollector, session)
|
||||||
common.UpdateProgressBar(1)
|
common.UpdateProgressBar(1)
|
||||||
}, state)
|
}, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -356,14 +357,17 @@ func buildServiceLogMessage(addr string, serviceInfo *ServiceInfo, isWeb bool) s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// scanSinglePort 扫描单个端口并进行服务识别(重构后的简洁版本)
|
// scanSinglePort 扫描单个端口并进行服务识别(重构后的简洁版本)
|
||||||
func scanSinglePort(ctx context.Context, host string, port int, addr string, timeout time.Duration, count *int64, collector *resultCollector, failedCollector *failedPortCollector, session *common.ScanSession) {
|
func scanSinglePort(ctx context.Context, host string, port int, addr string, adaptiveTO *AdaptiveTimeout, count *int64, collector *resultCollector, failedCollector *failedPortCollector, session *common.ScanSession) {
|
||||||
config := session.Config
|
config := session.Config
|
||||||
|
timeout := adaptiveTO.Timeout()
|
||||||
// 步骤1:建立连接
|
// 步骤1:建立连接
|
||||||
|
start := time.Now()
|
||||||
conn, err := connectWithRetry(ctx, session, addr, timeout, 2)
|
conn, err := connectWithRetry(ctx, session, addr, timeout, 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleConnectionFailure(err, host, port, addr, failedCollector)
|
handleConnectionFailure(err, host, port, addr, failedCollector)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
adaptiveTO.Record(time.Since(start))
|
||||||
|
|
||||||
// 步骤1.5:代理连接深度验证(防止透明代理/全回显代理的假连接问题)
|
// 步骤1.5:代理连接深度验证(防止透明代理/全回显代理的假连接问题)
|
||||||
valid, verifyMethod := verifyProxyConnectionDeep(conn, addr)
|
valid, verifyMethod := verifyProxyConnectionDeep(conn, addr)
|
||||||
|
|||||||
Reference in New Issue
Block a user