fscan/Core/Scanner.go
ZacharyZcR a250fd7e22 refactor: 重构Scanner.go架构并完善国际化支持
架构优化:
- 简化ScanStrategy接口,从8个方法减少到3个
- 移除不必要的Scanner结构体包装,改为直接函数调用
- 合并scheduleScanTask和executeSingleScan为单一函数
- 移除prepareScanTasks中间层,采用流式处理避免预构建任务列表
- 移除ScanTask结构体,减少内存分配

性能提升:
- 流式任务执行,降低内存占用
- 统一资源管理,简化并发控制
- 减少函数调用链层次,提高执行效率

国际化完善:
- 新增5个扫描流程相关的双语消息定义
- 所有硬编码中文字符串替换为i18n调用
- 完整支持中英文扫描模式选择和错误提示

代码质量:
- 从187行减少到166行,函数数量从9个减少到7个
- 职责更清晰,耦合度更低,可维护性提升
- 保持向后兼容,核心功能完整
2025-08-07 08:27:12 +08:00

167 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package core
import (
"fmt"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/webscan/lib"
"strconv"
"sync"
"sync/atomic"
)
// ScanStrategy 定义扫描策略接口(简化版)
type ScanStrategy interface {
// 执行扫描的主要方法
Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup)
// 插件管理方法
GetPlugins() ([]string, bool)
IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool
}
// selectStrategy 根据扫描配置选择适当的扫描策略
func selectStrategy(info common.HostInfo) ScanStrategy {
switch {
case common.LocalMode:
common.LogBase(i18n.GetText("scan_mode_local_selected"))
return NewLocalScanStrategy()
case len(common.URLs) > 0:
common.LogBase(i18n.GetText("scan_mode_web_selected"))
return NewWebScanStrategy()
default:
common.LogBase(i18n.GetText("scan_mode_service_selected"))
return NewServiceScanStrategy()
}
}
// RunScan 执行整体扫描流程(简化版,移除不必要的包装)
func RunScan(info common.HostInfo) {
common.LogBase(i18n.GetText("scan_info_start"))
lib.Inithttp()
// 选择策略
strategy := selectStrategy(info)
// 并发控制初始化
ch := make(chan struct{}, common.ThreadNum)
wg := sync.WaitGroup{}
// 执行策略
strategy.Execute(info, &ch, &wg)
// 等待所有扫描完成
wg.Wait()
// 完成扫描
finishScan()
}
// finishScan 完成扫描并输出结果
func finishScan() {
// 确保进度条正确完成
if common.IsProgressActive() {
common.FinishProgressBar()
}
// 输出扫描完成信息
common.LogBase(i18n.GetText("scan_task_complete", common.End, common.Num))
}
// ExecuteScanTasks 任务执行通用框架(流式处理,无预构建任务列表)
func ExecuteScanTasks(targets []common.HostInfo, strategy ScanStrategy, ch *chan struct{}, wg *sync.WaitGroup) {
// 获取要执行的插件
pluginsToRun, isCustomMode := strategy.GetPlugins()
// 预计算任务数量用于进度条
taskCount := countApplicableTasks(targets, pluginsToRun, isCustomMode, strategy)
// 初始化进度条
if taskCount > 0 && common.ShowProgress {
description := i18n.GetText("progress_scanning_description")
common.InitProgressBar(int64(taskCount), description)
}
// 流式执行任务,避免预构建大量任务对象
for _, target := range targets {
targetPort := 0
if target.Ports != "" {
targetPort, _ = strconv.Atoi(target.Ports)
}
for _, pluginName := range pluginsToRun {
plugin, exists := common.PluginManager[pluginName]
if !exists {
continue
}
// 检查插件是否适用于当前目标
if strategy.IsPluginApplicable(plugin, targetPort, isCustomMode) {
executeScanTask(pluginName, target, ch, wg)
}
}
}
}
// countApplicableTasks 计算适用的任务数量(用于进度条初始化)
func countApplicableTasks(targets []common.HostInfo, pluginsToRun []string, isCustomMode bool, strategy ScanStrategy) int {
count := 0
for _, target := range targets {
targetPort := 0
if target.Ports != "" {
targetPort, _ = strconv.Atoi(target.Ports)
}
for _, pluginName := range pluginsToRun {
plugin, exists := common.PluginManager[pluginName]
if exists && strategy.IsPluginApplicable(plugin, targetPort, isCustomMode) {
count++
}
}
}
return count
}
// executeScanTask 执行单个扫描任务(合并原 scheduleScanTask 和 executeSingleScan
func executeScanTask(pluginName string, target common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
wg.Add(1)
*ch <- struct{}{} // 获取并发槽位
go func() {
defer func() {
// 捕获并记录任何可能的panic
if r := recover(); r != nil {
common.LogError(fmt.Sprintf(i18n.GetText("scan_plugin_panic"),
pluginName, target.Host, target.Ports, r))
}
// 完成任务,释放资源
wg.Done()
<-*ch // 释放并发槽位
}()
// 更新统计和进度
atomic.AddInt64(&common.Num, 1)
common.UpdateProgressBar(1)
// 执行扫描
plugin, exists := common.PluginManager[pluginName]
if !exists {
common.LogBase(fmt.Sprintf(i18n.GetText("scan_plugin_not_found"), pluginName))
return
}
if err := plugin.ScanFunc(&target); err != nil {
common.LogError(fmt.Sprintf(i18n.GetText("scan_plugin_error"), target.Host, target.Ports, err))
}
}()
}
// Scan 入口函数,向后兼容旧的调用方式
func Scan(info common.HostInfo) {
RunScan(info)
}