enhance: 增强进度条显示和扫描配置功能

- 在扫描开始时显示详细配置信息(-t, -time, -mt, -gt参数)
- 端口扫描进度条显示线程数配置
- 进度条实时显示插件和连接并发状态
- 添加ConcurrencyMonitor并发监控系统
- 提升mt参数默认值从10到50以提高扫描性能
- 移除端口范围限制,支持全端口扫描(1-65535)
- 完善中英文国际化支持
This commit is contained in:
ZacharyZcR 2025-08-07 14:48:32 +08:00
parent de286026e8
commit 8a2c9737f3
10 changed files with 269 additions and 12 deletions

View File

@ -0,0 +1,191 @@
package common
import (
"fmt"
"sync"
"sync/atomic"
"github.com/shadow1ng/fscan/common/i18n"
)
/*
ConcurrencyMonitor.go - 并发监控器
监控两个层级的并发
1. 主扫描器线程数 (-t 参数控制)
2. 插件内连接线程数 (-mt 参数控制)
*/
// ConcurrencyMonitor 并发监控器
type ConcurrencyMonitor struct {
// 主扫描器层级
activePluginTasks int64 // 当前活跃的插件任务数
totalPluginTasks int64 // 总插件任务数
// 插件内连接层级 (每个插件的连接线程数)
pluginConnections sync.Map // map[string]*PluginConnectionInfo
mu sync.RWMutex
}
// PluginConnectionInfo 单个插件的连接信息
type PluginConnectionInfo struct {
PluginName string // 插件名称
Target string // 目标地址
ActiveConnections int64 // 当前活跃连接数
TotalConnections int64 // 总连接数
}
var (
globalConcurrencyMonitor *ConcurrencyMonitor
concurrencyMutex sync.Once
)
// GetConcurrencyMonitor 获取全局并发监控器
func GetConcurrencyMonitor() *ConcurrencyMonitor {
concurrencyMutex.Do(func() {
globalConcurrencyMonitor = &ConcurrencyMonitor{
activePluginTasks: 0,
totalPluginTasks: 0,
}
})
return globalConcurrencyMonitor
}
// =============================================================================
// 主扫描器层级监控
// =============================================================================
// StartPluginTask 开始插件任务
func (m *ConcurrencyMonitor) StartPluginTask() {
atomic.AddInt64(&m.activePluginTasks, 1)
atomic.AddInt64(&m.totalPluginTasks, 1)
}
// FinishPluginTask 完成插件任务
func (m *ConcurrencyMonitor) FinishPluginTask() {
atomic.AddInt64(&m.activePluginTasks, -1)
}
// GetPluginTaskStats 获取插件任务统计
func (m *ConcurrencyMonitor) GetPluginTaskStats() (active int64, total int64) {
return atomic.LoadInt64(&m.activePluginTasks), atomic.LoadInt64(&m.totalPluginTasks)
}
// =============================================================================
// 插件内连接层级监控
// =============================================================================
// StartConnection 开始连接
func (m *ConcurrencyMonitor) StartConnection(pluginName, target string) {
key := fmt.Sprintf("%s@%s", pluginName, target)
value, _ := m.pluginConnections.LoadOrStore(key, &PluginConnectionInfo{
PluginName: pluginName,
Target: target,
})
info := value.(*PluginConnectionInfo)
atomic.AddInt64(&info.ActiveConnections, 1)
atomic.AddInt64(&info.TotalConnections, 1)
}
// FinishConnection 完成连接
func (m *ConcurrencyMonitor) FinishConnection(pluginName, target string) {
key := fmt.Sprintf("%s@%s", pluginName, target)
if value, ok := m.pluginConnections.Load(key); ok {
info := value.(*PluginConnectionInfo)
atomic.AddInt64(&info.ActiveConnections, -1)
}
}
// GetConnectionStats 获取所有插件连接统计
func (m *ConcurrencyMonitor) GetConnectionStats() map[string]*PluginConnectionInfo {
stats := make(map[string]*PluginConnectionInfo)
m.pluginConnections.Range(func(key, value interface{}) bool {
keyStr := key.(string)
info := value.(*PluginConnectionInfo)
// 只返回当前活跃的连接
if atomic.LoadInt64(&info.ActiveConnections) > 0 {
stats[keyStr] = &PluginConnectionInfo{
PluginName: info.PluginName,
Target: info.Target,
ActiveConnections: atomic.LoadInt64(&info.ActiveConnections),
TotalConnections: atomic.LoadInt64(&info.TotalConnections),
}
}
return true
})
return stats
}
// GetTotalActiveConnections 获取总活跃连接数
func (m *ConcurrencyMonitor) GetTotalActiveConnections() int64 {
var total int64
m.pluginConnections.Range(func(key, value interface{}) bool {
info := value.(*PluginConnectionInfo)
total += atomic.LoadInt64(&info.ActiveConnections)
return true
})
return total
}
// Reset 重置监控器
func (m *ConcurrencyMonitor) Reset() {
atomic.StoreInt64(&m.activePluginTasks, 0)
atomic.StoreInt64(&m.totalPluginTasks, 0)
m.pluginConnections.Range(func(key, value interface{}) bool {
m.pluginConnections.Delete(key)
return true
})
}
// GetConcurrencyStatus 获取并发状态字符串
func (m *ConcurrencyMonitor) GetConcurrencyStatus() string {
activePlugins, _ := m.GetPluginTaskStats()
totalConnections := m.GetTotalActiveConnections()
if activePlugins == 0 && totalConnections == 0 {
return ""
}
if totalConnections == 0 {
return fmt.Sprintf("%s:%d", i18n.GetText("concurrency_plugin"), activePlugins)
}
return fmt.Sprintf("%s:%d %s:%d",
i18n.GetText("concurrency_plugin"), activePlugins,
i18n.GetText("concurrency_connection"), totalConnections)
}
// GetDetailedStatus 获取详细的并发状态
func (m *ConcurrencyMonitor) GetDetailedStatus() string {
activePlugins, _ := m.GetPluginTaskStats()
connectionStats := m.GetConnectionStats()
if activePlugins == 0 && len(connectionStats) == 0 {
return i18n.GetText("concurrency_no_active_tasks")
}
status := fmt.Sprintf("%s: %d", i18n.GetText("concurrency_plugin_tasks"), activePlugins)
if len(connectionStats) > 0 {
status += " | " + i18n.GetText("concurrency_connection_details") + ": "
first := true
for _, info := range connectionStats {
if !first {
status += ", "
}
status += fmt.Sprintf("%s@%s:%d", info.PluginName, info.Target, info.ActiveConnections)
first = false
}
}
return status
}

View File

@ -142,7 +142,7 @@ func Flag(Info *HostInfo) {
flag.StringVar(&ScanMode, "m", "all", i18n.GetText("flag_scan_mode"))
flag.IntVar(&ThreadNum, "t", 600, i18n.GetText("flag_thread_num"))
flag.Int64Var(&Timeout, "time", 3, i18n.GetText("flag_timeout"))
flag.IntVar(&ModuleThreadNum, "mt", 10, i18n.GetText("flag_module_thread_num"))
flag.IntVar(&ModuleThreadNum, "mt", 50, i18n.GetText("flag_module_thread_num"))
flag.Int64Var(&GlobalTimeout, "gt", 180, i18n.GetText("flag_global_timeout"))
// LiveTop 参数已移除,改为智能控制
flag.BoolVar(&DisablePing, "np", false, i18n.GetText("flag_disable_ping"))

View File

@ -392,6 +392,12 @@ func showParseSummary(config *parsers.ParsedConfig) {
}
}
// 显示扫描配置
LogBase(i18n.GetText("scan_config_thread_num", ThreadNum))
LogBase(i18n.GetText("scan_config_timeout", Timeout))
LogBase(i18n.GetText("scan_config_module_thread_num", ModuleThreadNum))
LogBase(i18n.GetText("scan_config_global_timeout", GlobalTimeout))
// 显示网络配置
if config.Network != nil {
if config.Network.HttpProxy != "" {
@ -400,9 +406,6 @@ func showParseSummary(config *parsers.ParsedConfig) {
if config.Network.Socks5Proxy != "" {
LogBase(i18n.GetText("network_socks5_proxy", config.Network.Socks5Proxy))
}
if config.Network.Timeout > 0 {
LogBase(i18n.GetText("network_timeout", config.Network.Timeout))
}
if config.Network.WebTimeout > 0 {
LogBase(i18n.GetText("network_web_timeout", config.Network.WebTimeout))
}

View File

@ -146,6 +146,9 @@ func (pm *ProgressManager) generateProgressBar() string {
percentage := float64(pm.current) / float64(pm.total) * 100
elapsed := time.Since(pm.startTime)
// 获取并发状态
concurrencyStatus := GetConcurrencyMonitor().GetConcurrencyStatus()
// 计算预估剩余时间
var eta string
if pm.current > 0 {
@ -194,8 +197,16 @@ func (pm *ProgressManager) generateProgressBar() string {
bar += "|"
}
return fmt.Sprintf("%s %6.1f%% %s (%d/%d)%s%s",
// 构建基础进度条
baseProgress := fmt.Sprintf("%s %6.1f%% %s (%d/%d)%s%s",
pm.description, percentage, bar, pm.current, pm.total, speedStr, eta)
// 添加并发状态
if concurrencyStatus != "" {
return fmt.Sprintf("%s [%s]", baseProgress, concurrencyStatus)
}
return baseProgress
}
// showCompletionInfo 显示完成信息

View File

@ -224,6 +224,10 @@ var ScanMessages = map[string]map[string]string{
LangZH: "端口扫描",
LangEN: "Port Scanning",
},
"progress_port_scanning_with_threads": {
LangZH: "端口扫描 (线程:%d)",
LangEN: "Port Scanning (Threads:%d)",
},
"progress_scan_completed": {
LangZH: "扫描完成:",
LangEN: "Scan Completed:",
@ -236,4 +240,44 @@ var ScanMessages = map[string]map[string]string{
LangZH: "开放端口",
LangEN: "Open Ports",
},
// ========================= 并发状态消息 =========================
"concurrency_plugin": {
LangZH: "插件",
LangEN: "Plugins",
},
"concurrency_connection": {
LangZH: "连接",
LangEN: "Conns",
},
"concurrency_plugin_tasks": {
LangZH: "活跃插件任务",
LangEN: "Active Plugin Tasks",
},
"concurrency_connection_details": {
LangZH: "连接详情",
LangEN: "Connection Details",
},
"concurrency_no_active_tasks": {
LangZH: "无活跃任务",
LangEN: "No Active Tasks",
},
// ========================= 扫描配置消息 =========================
"scan_config_thread_num": {
LangZH: "端口扫描线程数: %d",
LangEN: "Port scan threads: %d",
},
"scan_config_timeout": {
LangZH: "连接超时: %ds",
LangEN: "Connection timeout: %ds",
},
"scan_config_module_thread_num": {
LangZH: "插件内线程数: %d",
LangEN: "Plugin threads: %d",
},
"scan_config_global_timeout": {
LangZH: "单个插件全局超时: %ds",
LangEN: "Plugin global timeout: %ds",
},
}

View File

@ -290,11 +290,6 @@ func parsePortRange(rangeStr string) []int {
var ports []int
for i := start; i <= end; i++ {
ports = append(ports, i)
// 限制端口范围大小
if len(ports) > SimpleMaxPortRange {
break
}
}
return ports

View File

@ -184,7 +184,6 @@ func GetCommonSecondOctets() []int {
const (
// 端口和主机限制
SimpleMaxHosts = 10000
SimpleMaxPortRange = 5000
// 网段简写展开
DefaultGatewayLastOctet = 1

View File

@ -43,7 +43,7 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
// 初始化端口扫描进度条
if totalTasks > 0 && common.ShowProgress {
description := i18n.GetText("progress_port_scanning")
description := i18n.GetText("progress_port_scanning_with_threads", common.ThreadNum)
common.InitProgressBar(int64(totalTasks), description)
}

View File

@ -133,6 +133,10 @@ func executeScanTask(pluginName string, target common.HostInfo, ch *chan struct{
*ch <- struct{}{} // 获取并发槽位
go func() {
// 开始监控插件任务
monitor := common.GetConcurrencyMonitor()
monitor.StartPluginTask()
defer func() {
// 捕获并记录任何可能的panic
if r := recover(); r != nil {
@ -141,6 +145,7 @@ func executeScanTask(pluginName string, target common.HostInfo, ch *chan struct{
}
// 完成任务,释放资源
monitor.FinishPluginTask()
wg.Done()
<-*ch // 释放并发槽位
}()

View File

@ -116,7 +116,16 @@ func ConcurrentCredentialScan(
case <-scanCtx.Done():
return
default:
// 开始监控连接
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
monitor := common.GetConcurrencyMonitor()
monitor.StartConnection("credential", target)
result := scanCredentialWithRetry(scanCtx, scanner, info, credential, config)
// 完成连接监控
monitor.FinishConnection("credential", target)
if result != nil && result.Success {
select {
case resultChan <- result: