mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-26 21:21:53 +08:00
perf: 四项扫描性能优化
- SO_LINGER=0 快速释放连接,减少 TIME_WAIT 堆积 - 服务探测超时自适应,RTT 采样约束读超时上限 - 端口扫描结果流式传递,pipeline 并行端口扫描和插件执行 - ICMP 批量预构建包和地址,减少发送循环开销
This commit is contained in:
@@ -56,6 +56,11 @@ func (s *ScanSession) DialTCP(ctx context.Context, network, address string, time
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SO_LINGER=0: 连接关闭时立即发送 RST,避免 TIME_WAIT 堆积
|
||||||
|
if tc, ok := conn.(*net.TCPConn); ok {
|
||||||
|
_ = tc.SetLinger(0)
|
||||||
|
}
|
||||||
|
|
||||||
s.State.IncrementTCPSuccessPacketCount()
|
s.State.IncrementTCPSuccessPacketCount()
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-5
@@ -383,13 +383,22 @@ func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string,
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 发送ICMP请求(应用令牌桶限速)
|
// 发送ICMP请求(批量预构建 + 令牌桶限速)
|
||||||
limiter := state.GetICMPLimiter(config.Network.ICMPRate)
|
// 预构建所有 ICMP 包和目标地址,减少发送循环中的开销
|
||||||
|
type icmpPacket struct {
|
||||||
|
data []byte
|
||||||
|
dst net.Addr
|
||||||
|
}
|
||||||
|
packets := make([]icmpPacket, 0, len(hostslist))
|
||||||
for _, host := range hostslist {
|
for _, host := range hostslist {
|
||||||
limiter.Wait(1) // 等待令牌,控制发包速率
|
|
||||||
dst, _ := net.ResolveIPAddr("ip", host)
|
dst, _ := net.ResolveIPAddr("ip", host)
|
||||||
IcmpByte := makemsg(host)
|
packets = append(packets, icmpPacket{data: makemsg(host), dst: dst})
|
||||||
_, _ = conn.WriteTo(IcmpByte, dst)
|
}
|
||||||
|
|
||||||
|
limiter := state.GetICMPLimiter(config.Network.ICMPRate)
|
||||||
|
for i := range packets {
|
||||||
|
limiter.Wait(1)
|
||||||
|
_, _ = conn.WriteTo(packets[i].data, packets[i].dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自适应等待响应
|
// 自适应等待响应
|
||||||
|
|||||||
+18
-3
@@ -43,12 +43,14 @@ var resourceExhaustedPatterns = []string{
|
|||||||
type resultCollector struct {
|
type resultCollector struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
addrs map[string]struct{}
|
addrs map[string]struct{}
|
||||||
|
stream chan<- string // 可选:流式通知 channel
|
||||||
}
|
}
|
||||||
|
|
||||||
// newResultCollector 创建结果收集器
|
// newResultCollector 创建结果收集器
|
||||||
func newResultCollector() *resultCollector {
|
func newResultCollector(stream chan<- string) *resultCollector {
|
||||||
return &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.mu.Lock()
|
||||||
c.addrs[addr] = struct{}{}
|
c.addrs[addr] = struct{}{}
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
if c.stream != nil {
|
||||||
|
c.stream <- addr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAll 获取所有结果
|
// GetAll 获取所有结果
|
||||||
@@ -111,7 +116,8 @@ func (f *failedPortCollector) Count() int {
|
|||||||
|
|
||||||
// EnhancedPortScan 高性能端口扫描函数
|
// 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
|
config := session.Config
|
||||||
state := session.State
|
state := session.State
|
||||||
common.LogDebug(fmt.Sprintf("[PortScan] 开始: %d个主机, 线程数=%d", len(hosts), config.ThreadNum))
|
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
|
to := time.Duration(timeout) * time.Second
|
||||||
adaptiveTO := NewAdaptiveTimeout(to)
|
adaptiveTO := NewAdaptiveTimeout(to)
|
||||||
var count int64
|
var count int64
|
||||||
collector := newResultCollector()
|
collector := newResultCollector(stream)
|
||||||
failedCollector := &failedPortCollector{}
|
failedCollector := &failedPortCollector{}
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
@@ -210,6 +216,11 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout
|
|||||||
// 收集结果
|
// 收集结果
|
||||||
aliveAddrs := collector.GetAll()
|
aliveAddrs := collector.GetAll()
|
||||||
|
|
||||||
|
// 关闭流式通知 channel
|
||||||
|
if stream != nil {
|
||||||
|
close(stream)
|
||||||
|
}
|
||||||
|
|
||||||
// 完成端口扫描进度条
|
// 完成端口扫描进度条
|
||||||
if common.IsProgressActive() {
|
if common.IsProgressActive() {
|
||||||
common.FinishProgressBar()
|
common.FinishProgressBar()
|
||||||
@@ -396,6 +407,10 @@ func scanSinglePort(ctx context.Context, host string, port int, addr string, ada
|
|||||||
|
|
||||||
// 步骤3:服务识别(Scanner负责关闭连接,包括探测中可能创建的新连接)
|
// 步骤3:服务识别(Scanner负责关闭连接,包括探测中可能创建的新连接)
|
||||||
scanner := NewSmartPortInfoScanner(ctx, host, port, conn, timeout, config, session)
|
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()
|
defer scanner.Close()
|
||||||
serviceInfo, _ := scanner.SmartIdentify()
|
serviceInfo, _ := scanner.SmartIdentify()
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ type Info struct {
|
|||||||
config *common.Config // 配置引用
|
config *common.Config // 配置引用
|
||||||
session *common.ScanSession // 会话引用
|
session *common.ScanSession // 会话引用
|
||||||
readTimeoutMS int // 当前读取超时时间(毫秒)
|
readTimeoutMS int // 当前读取超时时间(毫秒)
|
||||||
|
maxReadTimeoutMS int // RTT 自适应上限(毫秒),0 表示不限制
|
||||||
}
|
}
|
||||||
|
|
||||||
// SmartPortInfoScanner 智能服务识别器:保持nmap准确性,优化网络交互
|
// SmartPortInfoScanner 智能服务识别器:保持nmap准确性,优化网络交互
|
||||||
@@ -488,10 +489,14 @@ func (i *Info) setReadTimeout(ms int) {
|
|||||||
|
|
||||||
// getReadTimeout 获取当前读取超时时间
|
// getReadTimeout 获取当前读取超时时间
|
||||||
func (i *Info) getReadTimeout() time.Duration {
|
func (i *Info) getReadTimeout() time.Duration {
|
||||||
|
ms := defaultReadTimeoutMS
|
||||||
if i.readTimeoutMS > 0 {
|
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 默认读写超时时间(秒)
|
// WrTimeout 默认读写超时时间(秒)
|
||||||
|
|||||||
+62
-7
@@ -139,17 +139,72 @@ func (s *ServiceScanStrategy) Execute(ctx context.Context, session *common.ScanS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// performHostScan 执行主机扫描的完整流程
|
// performHostScan 执行主机扫描的完整流程
|
||||||
|
// pipeline 模式:端口扫描和插件执行并行,扫到开放端口立即开始跑插件
|
||||||
func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *common.ScanSession, info common.HostInfo, ch chan struct{}, wg *sync.WaitGroup) {
|
func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *common.ScanSession, info common.HostInfo, ch chan struct{}, wg *sync.WaitGroup) {
|
||||||
// 发现目标主机和端口
|
config := session.Config
|
||||||
targetInfos, err := s.discoverTargets(ctx, info.Host, info, session)
|
state := session.State
|
||||||
|
|
||||||
|
// 解析目标主机
|
||||||
|
hosts, err := parsers.ParseIP(info.Host, session.Params.HostsFile, session.Params.ExcludeHosts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(err.Error())
|
common.LogError(fmt.Sprintf("%s: %v", i18n.GetText("parse_target_failed"), err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行漏洞扫描
|
// 主机存活检测
|
||||||
if len(targetInfos) > 0 {
|
if s.shouldPerformLivenessCheck(hosts, config) {
|
||||||
ExecuteScanTasks(ctx, session, targetInfos, s, ch, wg)
|
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 {
|
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 注入)
|
// 合并预设的 host:port(项目缓存 / CLI 注入)
|
||||||
|
|||||||
Reference in New Issue
Block a user