From 9092416485dbe3df8744908f798cdeec2850e7f7 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Wed, 13 May 2026 00:21:23 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E5=9B=9B=E9=A1=B9=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SO_LINGER=0 快速释放连接,减少 TIME_WAIT 堆积 - 服务探测超时自适应,RTT 采样约束读超时上限 - 端口扫描结果流式传递,pipeline 并行端口扫描和插件执行 - ICMP 批量预构建包和地址,减少发送循环开销 --- common/session.go | 5 +++ core/icmp.go | 19 +++++++++--- core/port_scan.go | 27 ++++++++++++---- core/service_probe.go | 27 +++++++++------- core/service_scanner.go | 69 ++++++++++++++++++++++++++++++++++++----- 5 files changed, 118 insertions(+), 29 deletions(-) diff --git a/common/session.go b/common/session.go index 79d0c4c..18e0328 100644 --- a/common/session.go +++ b/common/session.go @@ -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 } diff --git a/core/icmp.go b/core/icmp.go index 47f1663..8039883 100644 --- a/core/icmp.go +++ b/core/icmp.go @@ -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) } // 自适应等待响应 diff --git a/core/port_scan.go b/core/port_scan.go index 6632196..8c96539 100644 --- a/core/port_scan.go +++ b/core/port_scan.go @@ -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() diff --git a/core/service_probe.go b/core/service_probe.go index 55519c9..baeeb83 100644 --- a/core/service_probe.go +++ b/core/service_probe.go @@ -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 默认读写超时时间(秒) diff --git a/core/service_scanner.go b/core/service_scanner.go index cc6bc53..35ca412 100644 --- a/core/service_scanner.go +++ b/core/service_scanner.go @@ -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 注入)