fscan/Core/Scanner.go
ZacharyZcR 78f81610cd feat: 实现存活探测模式(-ao)支持快速主机存活状态检测
新增功能:
- 添加-ao参数启用存活探测模式,专注于ICMP ping检测
- 实现AliveScanner专用扫描器,提供详细统计信息
- 集成到Scanner架构,支持与其他扫描模式无缝切换
- 完善i18n国际化支持,覆盖中英文界面

技术实现:
- 新增core/AliveScanner.go实现专用存活检测逻辑
- 扩展Scanner.go选择策略支持存活探测模式
- 优化目标解析和错误处理机制
- 提供成功率、耗时等详细扫描统计

使用场景:
- 快速批量主机存活性验证
- 网络拓扑发现前期探测
- 大规模网络资产盘点预检
2025-08-07 09:28:11 +08:00

170 lines
4.5 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.AliveOnly:
common.LogBase(i18n.GetText("scan_mode_alive_selected"))
return NewAliveScanStrategy()
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)
}