perf: 四项扫描性能优化

- SO_LINGER=0 快速释放连接,减少 TIME_WAIT 堆积
- 服务探测超时自适应,RTT 采样约束读超时上限
- 端口扫描结果流式传递,pipeline 并行端口扫描和插件执行
- ICMP 批量预构建包和地址,减少发送循环开销
This commit is contained in:
ZacharyZcR
2026-05-13 00:21:23 +08:00
parent 3739768c45
commit 9092416485
5 changed files with 118 additions and 29 deletions
+5
View File
@@ -56,6 +56,11 @@ func (s *ScanSession) DialTCP(ctx context.Context, network, address string, time
return nil, err
}
// SO_LINGER=0: 连接关闭时立即发送 RST,避免 TIME_WAIT 堆积
if tc, ok := conn.(*net.TCPConn); ok {
_ = tc.SetLinger(0)
}
s.State.IncrementTCPSuccessPacketCount()
return conn, nil
}
+14 -5
View File
@@ -383,13 +383,22 @@ func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string,
}
}()
// 发送ICMP请求(应用令牌桶限速)
limiter := state.GetICMPLimiter(config.Network.ICMPRate)
// 发送ICMP请求(批量预构建 + 令牌桶限速)
// 预构建所有 ICMP 包和目标地址,减少发送循环中的开销
type icmpPacket struct {
data []byte
dst net.Addr
}
packets := make([]icmpPacket, 0, len(hostslist))
for _, host := range hostslist {
limiter.Wait(1) // 等待令牌,控制发包速率
dst, _ := net.ResolveIPAddr("ip", host)
IcmpByte := makemsg(host)
_, _ = conn.WriteTo(IcmpByte, dst)
packets = append(packets, icmpPacket{data: makemsg(host), dst: dst})
}
limiter := state.GetICMPLimiter(config.Network.ICMPRate)
for i := range packets {
limiter.Wait(1)
_, _ = conn.WriteTo(packets[i].data, packets[i].dst)
}
// 自适应等待响应
+21 -6
View File
@@ -41,14 +41,16 @@ var resourceExhaustedPatterns = []string{
// resultCollector 结果收集器,用于并发安全地收集扫描结果
// 使用 map 实现:O(1) 的添加和删除,无顺序依赖问题
type resultCollector struct {
mu sync.Mutex
addrs map[string]struct{}
mu sync.Mutex
addrs map[string]struct{}
stream chan<- string // 可选:流式通知 channel
}
// newResultCollector 创建结果收集器
func newResultCollector() *resultCollector {
func newResultCollector(stream chan<- string) *resultCollector {
return &resultCollector{
addrs: make(map[string]struct{}),
addrs: make(map[string]struct{}),
stream: stream,
}
}
@@ -57,6 +59,9 @@ func (c *resultCollector) Add(addr string) {
c.mu.Lock()
c.addrs[addr] = struct{}{}
c.mu.Unlock()
if c.stream != nil {
c.stream <- addr
}
}
// GetAll 获取所有结果
@@ -111,7 +116,8 @@ func (f *failedPortCollector) Count() int {
// EnhancedPortScan 高性能端口扫描函数
// 使用滑动窗口调度 + 自适应线程池 + 流式迭代器
func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout int64, session *common.ScanSession) []string {
// stream: 可选,非 nil 时每发现开放端口立即发送 addr,扫描结束后关闭
func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout int64, session *common.ScanSession, stream chan<- string) []string {
config := session.Config
state := session.State
common.LogDebug(fmt.Sprintf("[PortScan] 开始: %d个主机, 线程数=%d", len(hosts), config.ThreadNum))
@@ -175,7 +181,7 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout
to := time.Duration(timeout) * time.Second
adaptiveTO := NewAdaptiveTimeout(to)
var count int64
collector := newResultCollector()
collector := newResultCollector(stream)
failedCollector := &failedPortCollector{}
var wg sync.WaitGroup
@@ -210,6 +216,11 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout
// 收集结果
aliveAddrs := collector.GetAll()
// 关闭流式通知 channel
if stream != nil {
close(stream)
}
// 完成端口扫描进度条
if common.IsProgressActive() {
common.FinishProgressBar()
@@ -396,6 +407,10 @@ func scanSinglePort(ctx context.Context, host string, port int, addr string, ada
// 步骤3:服务识别(Scanner负责关闭连接,包括探测中可能创建的新连接)
scanner := NewSmartPortInfoScanner(ctx, host, port, conn, timeout, config, session)
// 服务探测超时自适应:用 RTT 采样值约束读超时上限
if rttTO := adaptiveTO.Timeout(); rttTO < timeout {
scanner.info.maxReadTimeoutMS = int(rttTO.Milliseconds()) * 6
}
defer scanner.Close()
serviceInfo, _ := scanner.SmartIdentify()
+16 -11
View File
@@ -71,15 +71,16 @@ type Service struct {
// Info 定义单个端口探测的上下文信息
type Info struct {
Address string // 目标IP地址
Port int // 目标端口
Conn net.Conn // 网络连接
Result Result // 探测结果
Found bool // 是否成功识别服务
ctx context.Context // 扫描级 context
config *common.Config // 配置引用
session *common.ScanSession // 会话引用
readTimeoutMS int // 当前读取超时时间(毫秒)
Address string // 目标IP地址
Port int // 目标端口
Conn net.Conn // 网络连接
Result Result // 探测结果
Found bool // 是否成功识别服务
ctx context.Context // 扫描级 context
config *common.Config // 配置引用
session *common.ScanSession // 会话引用
readTimeoutMS int // 当前读取超时时间(毫秒)
maxReadTimeoutMS int // RTT 自适应上限(毫秒),0 表示不限制
}
// SmartPortInfoScanner 智能服务识别器:保持nmap准确性,优化网络交互
@@ -488,10 +489,14 @@ func (i *Info) setReadTimeout(ms int) {
// getReadTimeout 获取当前读取超时时间
func (i *Info) getReadTimeout() time.Duration {
ms := defaultReadTimeoutMS
if i.readTimeoutMS > 0 {
return time.Duration(i.readTimeoutMS) * time.Millisecond
ms = i.readTimeoutMS
}
return time.Duration(defaultReadTimeoutMS) * time.Millisecond
if i.maxReadTimeoutMS > 0 && ms > i.maxReadTimeoutMS {
ms = i.maxReadTimeoutMS
}
return time.Duration(ms) * time.Millisecond
}
// WrTimeout 默认读写超时时间(秒)
+62 -7
View File
@@ -139,17 +139,72 @@ func (s *ServiceScanStrategy) Execute(ctx context.Context, session *common.ScanS
}
// performHostScan 执行主机扫描的完整流程
// pipeline 模式:端口扫描和插件执行并行,扫到开放端口立即开始跑插件
func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *common.ScanSession, info common.HostInfo, ch chan struct{}, wg *sync.WaitGroup) {
// 发现目标主机和端口
targetInfos, err := s.discoverTargets(ctx, info.Host, info, session)
config := session.Config
state := session.State
// 解析目标主机
hosts, err := parsers.ParseIP(info.Host, session.Params.HostsFile, session.Params.ExcludeHosts)
if err != nil {
common.LogError(err.Error())
common.LogError(fmt.Sprintf("%s: %v", i18n.GetText("parse_target_failed"), err))
return
}
// 执行漏洞扫描
if len(targetInfos) > 0 {
ExecuteScanTasks(ctx, session, targetInfos, s, ch, wg)
// 主机存活检测
if s.shouldPerformLivenessCheck(hosts, config) {
hosts = CheckLive(ctx, hosts, false, session)
common.LogInfo(i18n.Tr("alive_hosts_count_info", len(hosts)))
}
if len(hosts) == 0 && len(state.GetHostPorts()) == 0 {
return
}
// 流式 channel:端口扫描发现开放端口后立即通知插件执行
stream := make(chan string, 64)
// 启动端口扫描 goroutine
go func() {
if len(hosts) > 0 {
EnhancedPortScan(ctx, hosts, config.Target.Ports, int64(config.Timeout.Seconds()), session, stream)
} else {
close(stream)
}
}()
// pipeline 消费:边收开放端口边执行插件
pluginsToRun, isCustomMode := s.GetPlugins(config)
for addr := range stream {
select {
case <-ctx.Done():
return
default:
}
infos := s.convertToTargetInfos([]string{addr}, info)
for _, target := range infos {
for _, pluginName := range pluginsToRun {
if s.IsPluginApplicableByName(pluginName, target.Host, target.Port, isCustomMode, config) {
executeScanTask(ctx, session, pluginName, target, ch, wg)
}
}
}
}
// 合并预设的 host:port
hostPorts := state.GetHostPorts()
if len(hostPorts) > 0 {
merged := mergeHostPorts(nil, hostPorts)
targets := s.convertToTargetInfos(merged, info)
for _, target := range targets {
for _, pluginName := range pluginsToRun {
if s.IsPluginApplicableByName(pluginName, target.Host, target.Port, isCustomMode, config) {
executeScanTask(ctx, session, pluginName, target, ch, wg)
}
}
}
state.ClearHostPorts()
}
}
@@ -258,7 +313,7 @@ func (s *ServiceScanStrategy) discoverAlivePorts(ctx context.Context, hosts []st
// 正常端口扫描
if len(hosts) > 0 {
alivePorts = EnhancedPortScan(ctx, hosts, config.Target.Ports, int64(config.Timeout.Seconds()), session)
alivePorts = EnhancedPortScan(ctx, hosts, config.Target.Ports, int64(config.Timeout.Seconds()), session, nil)
}
// 合并预设的 host:port(项目缓存 / CLI 注入)