Files

233 lines
6.0 KiB
Go

package common
import (
"errors"
"net"
"strconv"
"strings"
"sync"
"github.com/shadow1ng/fscan/common/i18n"
)
/*
globals.go - 全局配置变量
运行时数据和必要的全局状态。
命令行参数现通过 GetFlagVars() 访问,配置通过 GetGlobalConfig() 访问。
*/
// =============================================================================
// 核心数据结构
// =============================================================================
// HostInfo 主机信息结构 - 最核心的数据结构
type HostInfo struct {
Host string // 主机地址
Port int // 端口号(单个端口)
URL string // URL地址
Info []string // 附加信息
}
// Target 返回 host:port 格式字符串
func (h *HostInfo) Target() string {
host := h.Host
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
host = strings.TrimPrefix(strings.TrimSuffix(host, "]"), "[")
}
return net.JoinHostPort(host, strconv.Itoa(h.Port))
}
// =============================================================================
// 默认配置常量
// =============================================================================
const (
// DefaultThreadNum 默认线程数
DefaultThreadNum = 600
// DefaultTimeout 默认超时时间(秒)
DefaultTimeout = 3
// DefaultScanMode 默认扫描模式
DefaultScanMode = "all"
// DefaultLanguage 默认语言
DefaultLanguage = "zh"
// DefaultLogLevel 默认日志级别
DefaultLogLevel = "base"
)
// 日志级别常量
const (
LogLevelAll = "all"
LogLevelError = "error"
LogLevelBase = "base"
LogLevelInfo = "info"
LogLevelSuccess = "success"
LogLevelDebug = "debug"
LogLevelInfoSuccess = "info,success"
LogLevelBaseInfoSuccess = "base,info,success"
)
// 版本信息,通过 ldflags 注入
var (
version = "2.2.1"
commit = "unknown"
date = "unknown"
)
func GetVersion() string { return version }
// 运行时数据已迁移到Config对象中,使用GetGlobalConfig()访问
// Shell状态已迁移到State对象中,使用GetGlobalState()访问
// POC配置、输出控制、发包控制、初始化已迁移到Config/State对象中
// =============================================================================
// 发包限制错误类型
// =============================================================================
// 哨兵错误 - 用于 errors.Is 判断
var (
ErrMaxPacketReached = errors.New("max packet count reached")
ErrPacketRateLimited = errors.New("packet rate limited")
)
// PacketLimitError 发包限制错误(包含详情)
type PacketLimitError struct {
Sentinel error // ErrMaxPacketReached 或 ErrPacketRateLimited
Limit int64
Current int64
}
func (e *PacketLimitError) Error() string {
if e.Sentinel == ErrMaxPacketReached {
return i18n.Tr("packet_limit_max_reached", e.Limit)
}
return i18n.Tr("packet_limit_rate_limited", e.Limit)
}
func (e *PacketLimitError) Unwrap() error {
return e.Sentinel
}
// =============================================================================
// 发包频率控制功能
// =============================================================================
// CanSendPacketWith 检查是否可以发包 - 同时检查频率限制和总数限制
// 返回值: (可以发包, 错误)
func CanSendPacketWith(config *Config, state *State) (bool, error) {
// 检查总数限制
maxPacketCount := config.Network.MaxPacketCount
if maxPacketCount > 0 {
currentTotal := state.GetPacketCount()
if currentTotal >= maxPacketCount {
return false, &PacketLimitError{
Sentinel: ErrMaxPacketReached,
Limit: maxPacketCount,
Current: currentTotal,
}
}
}
// 检查频率限制
return state.CheckAndIncrementPacketRate(config.Network.PacketRateLimit)
}
// CanSendPacket 便捷API - 使用全局配置和状态
// 内部调用 CanSendPacketWith,保持向后兼容(返回string)
func CanSendPacket() (bool, string) {
ok, err := CanSendPacketWith(GetGlobalConfig(), GetGlobalState())
if err != nil {
return ok, err.Error()
}
return ok, ""
}
// =============================================================================
// 全局 Config 和 State 实例(新架构)
// =============================================================================
var (
// globalConfig 全局配置实例(小写,不直接暴露)
globalConfig *Config
// globalState 全局状态实例(小写,不直接暴露)
globalState *State
// globalMu 保护全局变量的读写锁
globalMu sync.RWMutex
)
// GetGlobalConfig 获取全局配置实例(线程安全)
// 使用读写锁保护,避免竞态条件
func GetGlobalConfig() *Config {
globalMu.RLock()
cfg := globalConfig
globalMu.RUnlock()
if cfg != nil {
return cfg
}
// 需要初始化,获取写锁
globalMu.Lock()
defer globalMu.Unlock()
// 双重检查,避免重复初始化
if globalConfig == nil {
globalConfig = NewConfig()
}
return globalConfig
}
// SetGlobalConfig 设置全局配置实例(线程安全)
func SetGlobalConfig(cfg *Config) {
globalMu.Lock()
globalConfig = cfg
globalMu.Unlock()
}
// GetGlobalState 获取全局状态实例(线程安全)
// 使用读写锁保护,避免竞态条件
func GetGlobalState() *State {
globalMu.RLock()
st := globalState
globalMu.RUnlock()
if st != nil {
return st
}
// 需要初始化,获取写锁
globalMu.Lock()
defer globalMu.Unlock()
// 双重检查,避免重复初始化
if globalState == nil {
globalState = NewState()
}
return globalState
}
// SetGlobalState 设置全局状态实例(线程安全)
func SetGlobalState(state *State) {
globalMu.Lock()
globalState = state
globalMu.Unlock()
}
// =============================================================================
// 字符串工具函数
// =============================================================================
// ContainsAny 检查字符串是否包含任意一个子串
func ContainsAny(s string, substrs ...string) bool {
for _, substr := range substrs {
if strings.Contains(s, substr) {
return true
}
}
return false
}