Files
fscan/core/alive_scanner.go
T
ZacharyZcR d7749c4766 refactor: context 穿透扫描生命周期,修复长驻插件阻塞和 Web Stop 无效
- RunScan 接受 context.Context,创建可取消上下文并穿透到所有策略和插件
- 长驻插件(forwardshell/socks5proxy/reverseshell)不再进入 scan WaitGroup,
  通过 ctx.Done() 管理生命周期,解除 wg.Wait() 死锁
- Web Stop API 从 stopChan 改为 context.CancelFunc,取消信号真正传播到扫描链路
- ExecuteScanTasks 和 executeScanTask 支持 context 取消检查,停止分发新任务
- CLI 模式传 context.Background(),行为完全不变
2026-04-27 19:04:26 +08:00

126 lines
3.4 KiB
Go

package core
import (
"context"
"fmt"
"sync"
"time"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/common/parsers"
)
/*
AliveScanner.go - 存活探测扫描器
专门用于主机存活探测,仅执行ICMP/Ping检测,
快速识别网络中的存活主机,不进行端口扫描。
*/
// AliveScanStrategy 存活探测扫描策略
type AliveScanStrategy struct {
*BaseScanStrategy
startTime time.Time
stats AliveStats
}
// AliveStats 存活探测统计信息
type AliveStats struct {
TotalHosts int // 总主机数
AliveHosts int // 存活主机数
DeadHosts int // 死亡主机数
ScanDuration time.Duration // 扫描耗时
SuccessRate float64 // 成功率
AliveHostList []string // 存活主机列表
}
// NewAliveScanStrategy 创建新的存活探测扫描策略
func NewAliveScanStrategy() *AliveScanStrategy {
return &AliveScanStrategy{
BaseScanStrategy: NewBaseScanStrategy("存活探测", FilterNone),
startTime: time.Now(),
}
}
// Name 返回策略名称
func (s *AliveScanStrategy) Name() string {
return i18n.GetText("scan_strategy_alive_name")
}
// Description 返回策略描述
func (s *AliveScanStrategy) Description() string {
return i18n.GetText("scan_strategy_alive_desc")
}
// Execute 执行存活探测扫描策略
func (s *AliveScanStrategy) Execute(_ context.Context, config *common.Config, state *common.State, info common.HostInfo, ch chan struct{}, wg *sync.WaitGroup) {
// 验证扫描目标(需要同时检查 -h 和 -hf 参数)
fv := common.GetFlagVars()
if info.Host == "" && fv.HostsFile == "" {
common.LogError(i18n.GetText("parse_error_target_empty"))
return
}
// 执行存活探测
s.performAliveScan(info, config, state)
// 输出统计信息
s.outputStats()
}
// performAliveScan 执行存活探测
func (s *AliveScanStrategy) performAliveScan(info common.HostInfo, config *common.Config, state *common.State) {
// 解析目标主机
fv := common.GetFlagVars()
hosts, err := parsers.ParseIP(info.Host, fv.HostsFile, fv.ExcludeHosts)
if err != nil {
common.LogError(i18n.Tr("parse_target_failed", err))
return
}
if len(hosts) == 0 {
common.LogError(i18n.GetText("parse_error_no_hosts"))
return
}
// 初始化统计信息
s.stats.TotalHosts = len(hosts)
s.stats.AliveHosts = 0
s.stats.DeadHosts = 0
// 执行存活检测
aliveList := CheckLive(hosts, false, config, state) // 使用ICMP探测
// 更新统计信息
s.stats.AliveHosts = len(aliveList)
s.stats.DeadHosts = s.stats.TotalHosts - s.stats.AliveHosts
s.stats.ScanDuration = time.Since(s.startTime)
s.stats.AliveHostList = aliveList // 存储存活主机列表
if s.stats.TotalHosts > 0 {
s.stats.SuccessRate = float64(s.stats.AliveHosts) / float64(s.stats.TotalHosts) * 100
}
}
// outputStats 输出统计信息(精简版)
func (s *AliveScanStrategy) outputStats() {
// 只输出存活主机列表,不输出冗余统计
for _, host := range s.stats.AliveHostList {
common.LogSuccess(fmt.Sprintf("alive %s", host))
}
}
// PrepareTargets 存活探测不需要准备扫描目标
func (s *AliveScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo {
// 存活探测不需要返回目标列表,因为它不进行后续扫描
return nil
}
// GetPlugins 存活探测不使用插件
func (s *AliveScanStrategy) GetPlugins(config *common.Config) ([]string, bool) {
return []string{}, false
}