refactor: 完成common包常量提取和代码重构优化

- 新增constants.go文件统一管理各包常量定义
- 提取logging、output、parsers、proxy包中的硬编码值
- 将30+个魔法数字替换为语义化常量
- 统一错误代码和消息格式
- 清理死代码和未使用变量
- 优化代码可维护性和可读性
- 保持完全向后兼容性

涉及包:
- common/logging: 日志级别和格式常量
- common/output: 输出配置和格式常量
- common/parsers: 解析器配置和验证常量
- common/proxy: 代理协议和错误常量
This commit is contained in:
ZacharyZcR 2025-08-06 21:29:30 +08:00
parent 2f9c213e80
commit 84b0bb1e28
23 changed files with 963 additions and 309 deletions

View File

@ -5,7 +5,6 @@ import (
"sync"
"time"
"github.com/fatih/color"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/common/logging"
"github.com/shadow1ng/fscan/common/parsers"
@ -487,13 +486,7 @@ func applyLogLevel() {
SlowOutput: SlowLogOutput,
ShowProgress: true,
StartTime: StartTime,
LevelColors: map[logging.LogLevel]color.Attribute{
logging.LevelError: color.FgBlue,
logging.LevelBase: color.FgYellow,
logging.LevelInfo: color.FgGreen,
logging.LevelSuccess: color.FgRed,
logging.LevelDebug: color.FgWhite,
},
LevelColors: logging.GetDefaultLevelColors(),
}
newLogger := logging.NewLogger(config)

View File

@ -34,13 +34,13 @@ func (f *StandardFormatter) Format(entry *LogEntry) string {
// formatElapsedTime 格式化经过的时间
func (f *StandardFormatter) formatElapsedTime(elapsed time.Duration) string {
switch {
case elapsed < time.Second:
case elapsed < MaxMillisecondDisplay:
// 毫秒显示,不需要小数
return fmt.Sprintf("%dms", elapsed.Milliseconds())
case elapsed < time.Minute:
case elapsed < MaxSecondDisplay:
// 秒显示,保留一位小数
return fmt.Sprintf("%.1fs", elapsed.Seconds())
case elapsed < time.Hour:
case elapsed < MaxMinuteDisplay:
// 分钟和秒显示
minutes := int(elapsed.Minutes())
seconds := int(elapsed.Seconds()) % 60
@ -58,13 +58,13 @@ func (f *StandardFormatter) formatElapsedTime(elapsed time.Duration) string {
func (f *StandardFormatter) getLevelPrefix(level LogLevel) string {
switch level {
case LevelSuccess:
return "[+]"
return PrefixSuccess
case LevelInfo:
return "[*]"
return PrefixInfo
case LevelError:
return "[-]"
return PrefixError
default:
return " "
return PrefixDefault
}
}

View File

@ -175,7 +175,7 @@ func (l *Logger) handleLogEntry(entry *LogEntry) {
func (l *Logger) clearProgress() {
if l.progressBar != nil {
l.progressBar.Clear() // 忽略错误
time.Sleep(10 * time.Millisecond)
time.Sleep(ProgressClearDelay)
}
}
@ -207,10 +207,9 @@ func (l *Logger) Error(content string, metadata ...map[string]interface{}) {
l.Log(LevelError, content, metadata...)
}
// GetScanStatus 获取扫描状态管理器
func (l *Logger) GetScanStatus() *ScanStatus {
return l.scanStatus
}
// =============================================================================================
// 已删除的死代码未使用GetScanStatus 获取扫描状态管理器
// =============================================================================================
// Initialize 初始化日志系统(兼容原接口)
func (l *Logger) Initialize() {
@ -255,8 +254,12 @@ func (h *ConsoleHandler) Handle(entry *LogEntry) {
if h.coordinatedOutput != nil {
if h.config.EnableColor {
if colorAttr, ok := h.config.LevelColors[entry.Level]; ok {
coloredMsg := color.New(colorAttr).Sprint(logMsg)
h.coordinatedOutput(coloredMsg)
if attr, ok := colorAttr.(color.Attribute); ok {
coloredMsg := color.New(attr).Sprint(logMsg)
h.coordinatedOutput(coloredMsg)
} else {
h.coordinatedOutput(logMsg)
}
} else {
h.coordinatedOutput(logMsg)
}
@ -267,7 +270,11 @@ func (h *ConsoleHandler) Handle(entry *LogEntry) {
// 回到原来的直接输出方式
if h.config.EnableColor {
if colorAttr, ok := h.config.LevelColors[entry.Level]; ok {
color.New(colorAttr).Println(logMsg)
if attr, ok := colorAttr.(color.Attribute); ok {
color.New(attr).Println(logMsg)
} else {
fmt.Println(logMsg)
}
} else {
fmt.Println(logMsg)
}
@ -278,7 +285,7 @@ func (h *ConsoleHandler) Handle(entry *LogEntry) {
// 根据慢速输出设置决定是否添加延迟
if h.config.SlowOutput {
time.Sleep(50 * time.Millisecond)
time.Sleep(SlowOutputDelay)
}
}

View File

@ -3,24 +3,8 @@ package logging
import (
"sync"
"time"
"github.com/fatih/color"
)
// LogLevel 日志级别类型
type LogLevel string
// 定义系统支持的日志级别常量
const (
LevelAll LogLevel = "ALL" // 显示所有级别日志
LevelError LogLevel = "ERROR" // 仅显示错误日志
LevelBase LogLevel = "BASE" // 仅显示基础信息日志
LevelInfo LogLevel = "INFO" // 仅显示信息日志
LevelSuccess LogLevel = "SUCCESS" // 仅显示成功日志
LevelDebug LogLevel = "DEBUG" // 仅显示调试日志
LevelInfoSuccess LogLevel = "INFO_SUCCESS" // 仅显示信息和成功日志
LevelBaseInfoSuccess LogLevel = "BASE_INFO_SUCCESS" // 显示基础、信息和成功日志
)
// LogEntry 定义单条日志的结构
type LogEntry struct {
@ -51,27 +35,30 @@ type LoggerConfig struct {
SlowOutput bool `json:"slow_output"` // 是否启用慢速输出
ShowProgress bool `json:"show_progress"` // 是否显示进度条
StartTime time.Time `json:"start_time"` // 开始时间
LevelColors map[LogLevel]color.Attribute `json:"-"` // 级别颜色映射
LevelColors map[LogLevel]interface{} `json:"-"` // 级别颜色映射
}
// DefaultLoggerConfig 默认日志器配置
func DefaultLoggerConfig() *LoggerConfig {
return &LoggerConfig{
Level: LevelAll,
EnableColor: true,
SlowOutput: false,
ShowProgress: true,
Level: DefaultLevel,
EnableColor: DefaultEnableColor,
SlowOutput: DefaultSlowOutput,
ShowProgress: DefaultShowProgress,
StartTime: time.Now(),
LevelColors: map[LogLevel]color.Attribute{
LevelError: color.FgBlue, // 错误日志显示蓝色
LevelBase: color.FgYellow, // 基础日志显示黄色
LevelInfo: color.FgGreen, // 信息日志显示绿色
LevelSuccess: color.FgRed, // 成功日志显示红色
LevelDebug: color.FgWhite, // 调试日志显示白色
},
LevelColors: convertColorMap(GetDefaultLevelColors()),
}
}
// convertColorMap 将color.Attribute映射转换为interface{}映射
func convertColorMap(colorMap map[LogLevel]interface{}) map[LogLevel]interface{} {
result := make(map[LogLevel]interface{})
for k, v := range colorMap {
result[k] = v
}
return result
}
// ScanStatus 扫描状态管理器
type ScanStatus struct {
mu sync.RWMutex // 读写互斥锁

View File

@ -0,0 +1,86 @@
package logging
/*
constants.go - 日志系统常量定义
统一管理common/logging包中的所有常量便于查看和编辑
*/
import (
"time"
"github.com/fatih/color"
)
// =============================================================================
// 日志级别常量 (从Types.go迁移)
// =============================================================================
// LogLevel 日志级别类型
type LogLevel string
// 定义系统支持的日志级别常量
const (
LevelAll LogLevel = "ALL" // 显示所有级别日志
LevelError LogLevel = "ERROR" // 仅显示错误日志
LevelBase LogLevel = "BASE" // 仅显示基础信息日志
LevelInfo LogLevel = "INFO" // 仅显示信息日志
LevelSuccess LogLevel = "SUCCESS" // 仅显示成功日志
LevelDebug LogLevel = "DEBUG" // 仅显示调试日志
LevelInfoSuccess LogLevel = "INFO_SUCCESS" // 仅显示信息和成功日志
LevelBaseInfoSuccess LogLevel = "BASE_INFO_SUCCESS" // 显示基础、信息和成功日志
)
// =============================================================================
// 时间显示常量 (从Formatter.go迁移)
// =============================================================================
const (
// 时间显示阈值
MaxMillisecondDisplay = time.Second // 毫秒显示的最大时长
MaxSecondDisplay = time.Minute // 秒显示的最大时长
MaxMinuteDisplay = time.Hour // 分钟显示的最大时长
// 慢速输出延迟
SlowOutputDelay = 50 * time.Millisecond
// 进度条清除延迟
ProgressClearDelay = 10 * time.Millisecond
)
// =============================================================================
// 日志前缀常量 (从Formatter.go迁移)
// =============================================================================
const (
PrefixSuccess = "[+]" // 成功日志前缀
PrefixInfo = "[*]" // 信息日志前缀
PrefixError = "[-]" // 错误日志前缀
PrefixDefault = " " // 默认日志前缀
)
// =============================================================================
// 默认配置常量
// =============================================================================
const (
DefaultLevel = LevelAll // 默认日志级别
DefaultEnableColor = true // 默认启用彩色输出
DefaultSlowOutput = false // 默认不启用慢速输出
DefaultShowProgress = true // 默认显示进度条
)
// =============================================================================
// 默认颜色映射
// =============================================================================
// GetDefaultLevelColors 获取默认的日志级别颜色映射
func GetDefaultLevelColors() map[LogLevel]interface{} {
return map[LogLevel]interface{}{
LevelError: color.FgBlue, // 错误日志显示蓝色
LevelBase: color.FgYellow, // 基础日志显示黄色
LevelInfo: color.FgGreen, // 信息日志显示绿色
LevelSuccess: color.FgRed, // 成功日志显示红色
LevelDebug: color.FgWhite, // 调试日志显示白色
}
}

View File

@ -26,7 +26,7 @@ type Manager struct {
// NewManager 创建新的输出管理器
func NewManager(config *ManagerConfig) (*Manager, error) {
if config == nil {
return nil, fmt.Errorf("配置不能为空")
return nil, fmt.Errorf(ErrConfigNil)
}
// 验证输出格式
@ -73,14 +73,14 @@ func validateFormat(format OutputFormat) error {
case FormatTXT, FormatJSON, FormatCSV:
return nil
default:
return fmt.Errorf("不支持的输出格式: %s", format)
return fmt.Errorf(ErrUnsupportedFormat, format)
}
}
// createOutputDir 创建输出目录
func createOutputDir(outputPath string) error {
dir := filepath.Dir(outputPath)
return os.MkdirAll(dir, 0755)
return os.MkdirAll(dir, DefaultDirPermissions)
}
// initializeWriter 初始化写入器
@ -96,11 +96,11 @@ func (m *Manager) initializeWriter() error {
case FormatCSV:
writer, err = NewCSVWriter(m.config.OutputPath)
default:
return fmt.Errorf("不支持的输出格式: %s", m.config.Format)
return fmt.Errorf(ErrUnsupportedFormat, m.config.Format)
}
if err != nil {
return fmt.Errorf("初始化写入器失败: %v", err)
return fmt.Errorf(ErrWriterInitFailed, err)
}
m.writer = writer
@ -138,11 +138,11 @@ func (m *Manager) SaveResult(result *ScanResult) error {
defer m.mu.RUnlock()
if !m.initialized {
return fmt.Errorf("输出管理器未初始化")
return fmt.Errorf(ErrManagerNotInit)
}
if m.closed {
return fmt.Errorf("输出管理器已关闭")
return fmt.Errorf(ErrManagerClosed)
}
// 更新统计信息
@ -188,13 +188,13 @@ func (m *Manager) flushBufferUnsafe() error {
// 批量写入
for _, result := range m.buffer {
if err := m.writer.Write(result); err != nil {
return fmt.Errorf("写入结果失败: %v", err)
return fmt.Errorf(ErrWriteFailed, err)
}
}
// 刷新写入器
if err := m.writer.Flush(); err != nil {
return fmt.Errorf("刷新写入器失败: %v", err)
return fmt.Errorf(ErrFlushFailed, err)
}
// 清空缓冲区

View File

@ -5,24 +5,7 @@ import (
"time"
)
// OutputFormat 输出格式类型
type OutputFormat string
const (
FormatTXT OutputFormat = "txt" // 文本格式
FormatJSON OutputFormat = "json" // JSON格式
FormatCSV OutputFormat = "csv" // CSV格式
)
// ResultType 定义结果类型
type ResultType string
const (
TypeHost ResultType = "HOST" // 主机存活
TypePort ResultType = "PORT" // 端口开放
TypeService ResultType = "SERVICE" // 服务识别
TypeVuln ResultType = "VULN" // 漏洞发现
)
// ScanResult 扫描结果结构
type ScanResult struct {
@ -79,10 +62,10 @@ func DefaultManagerConfig(outputPath string, format OutputFormat) *ManagerConfig
return &ManagerConfig{
OutputPath: outputPath,
Format: format,
EnableBuffer: true,
BufferSize: 100,
AutoFlush: true,
FlushInterval: 5 * time.Second,
EnableBuffer: DefaultEnableBuffer,
BufferSize: DefaultBufferSize,
AutoFlush: DefaultAutoFlush,
FlushInterval: DefaultFlushInterval,
}
}

View File

@ -19,9 +19,9 @@ type TXTWriter struct {
// NewTXTWriter 创建文本写入器
func NewTXTWriter(filePath string) (*TXTWriter, error) {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
if err != nil {
return nil, fmt.Errorf("创建文本文件失败: %v", err)
return nil, fmt.Errorf(ErrCreateFileFailed, "文本", err)
}
return &TXTWriter{
@ -40,7 +40,7 @@ func (w *TXTWriter) Write(result *ScanResult) error {
defer w.mu.Unlock()
if w.closed {
return fmt.Errorf("写入器已关闭")
return fmt.Errorf(ErrWriterClosed)
}
// 格式化 Details 为键值对字符串
@ -48,22 +48,22 @@ func (w *TXTWriter) Write(result *ScanResult) error {
if len(result.Details) > 0 {
pairs := make([]string, 0, len(result.Details))
for k, v := range result.Details {
pairs = append(pairs, fmt.Sprintf("%s=%v", k, v))
pairs = append(pairs, fmt.Sprintf(TxtKeyValueFormat, k, v))
}
details = strings.Join(pairs, ", ")
details = strings.Join(pairs, TxtDetailsSeparator)
}
// 使用类似原有格式的文本输出
txt := fmt.Sprintf("[%s] [%s] %s - %s",
result.Time.Format("2006-01-02 15:04:05"),
txt := fmt.Sprintf(TxtOutputTemplate,
result.Time.Format(TxtTimeFormat),
result.Type,
result.Target,
result.Status,
)
if details != "" {
txt += fmt.Sprintf(" (%s)", details)
txt += fmt.Sprintf(TxtDetailsFormat, details)
}
txt += "\n"
txt += TxtNewline
_, err := w.file.WriteString(txt)
return err
@ -109,13 +109,13 @@ type JSONWriter struct {
// NewJSONWriter 创建JSON写入器
func NewJSONWriter(filePath string) (*JSONWriter, error) {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
if err != nil {
return nil, fmt.Errorf("创建JSON文件失败: %v", err)
return nil, fmt.Errorf(ErrCreateFileFailed, "JSON", err)
}
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
encoder.SetIndent(JSONIndentPrefix, JSONIndentString)
return &JSONWriter{
file: file,
@ -134,7 +134,7 @@ func (w *JSONWriter) Write(result *ScanResult) error {
defer w.mu.Unlock()
if w.closed {
return fmt.Errorf("写入器已关闭")
return fmt.Errorf(ErrWriterClosed)
}
return w.encoder.Encode(result)
@ -181,9 +181,9 @@ type CSVWriter struct {
// NewCSVWriter 创建CSV写入器
func NewCSVWriter(filePath string) (*CSVWriter, error) {
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
if err != nil {
return nil, fmt.Errorf("创建CSV文件失败: %v", err)
return nil, fmt.Errorf(ErrCreateFileFailed, "CSV", err)
}
csvWriter := csv.NewWriter(file)
@ -203,10 +203,10 @@ func (w *CSVWriter) WriteHeader() error {
return nil
}
headers := []string{"Time", "Type", "Target", "Status", "Details"}
headers := CSVHeaders
err := w.csvWriter.Write(headers)
if err != nil {
return fmt.Errorf("写入CSV头部失败: %v", err)
return fmt.Errorf(ErrWriteHeaderFailed, err)
}
w.csvWriter.Flush()
@ -220,7 +220,7 @@ func (w *CSVWriter) Write(result *ScanResult) error {
defer w.mu.Unlock()
if w.closed {
return fmt.Errorf("写入器已关闭")
return fmt.Errorf(ErrWriterClosed)
}
// 确保头部已写入
@ -233,11 +233,11 @@ func (w *CSVWriter) Write(result *ScanResult) error {
// 序列化Details为JSON字符串
details, err := json.Marshal(result.Details)
if err != nil {
details = []byte("{}")
details = []byte(EmptyJSONObject)
}
record := []string{
result.Time.Format("2006-01-02 15:04:05"),
result.Time.Format(DefaultTimeFormat),
string(result.Type),
result.Target,
result.Status,
@ -259,10 +259,10 @@ func (w *CSVWriter) writeHeaderUnsafe() error {
return nil
}
headers := []string{"Time", "Type", "Target", "Status", "Details"}
headers := CSVHeaders
err := w.csvWriter.Write(headers)
if err != nil {
return fmt.Errorf("写入CSV头部失败: %v", err)
return fmt.Errorf(ErrWriteHeaderFailed, err)
}
w.csvWriter.Flush()
@ -333,23 +333,23 @@ func (r *CSVReader) ReadWithFilter(filter *ResultFilter) ([]*ScanResult, error)
file, err := os.Open(r.filePath)
if err != nil {
return nil, fmt.Errorf("打开CSV文件失败: %v", err)
return nil, fmt.Errorf(ErrOpenFileFailed, err)
}
defer file.Close()
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
return nil, fmt.Errorf("读取CSV文件失败: %v", err)
return nil, fmt.Errorf(ErrReadFileFailed, err)
}
var results []*ScanResult
for i, row := range records {
// 跳过CSV头部
if i == 0 {
if i == CSVHeaderRowIndex {
continue
}
if len(row) < 5 {
if len(row) < CSVMinColumns {
continue // 数据不完整
}
@ -377,22 +377,22 @@ func (r *CSVReader) ReadWithFilter(filter *ResultFilter) ([]*ScanResult, error)
// parseCSVRow 解析CSV行
func (r *CSVReader) parseCSVRow(row []string) (*ScanResult, error) {
// 解析时间
t, err := parseTime(row[0])
t, err := parseTime(row[CSVTimeColumnIndex])
if err != nil {
return nil, err
}
// 解析Details
var details map[string]interface{}
if err := json.Unmarshal([]byte(row[4]), &details); err != nil {
if err := json.Unmarshal([]byte(row[CSVDetailsColumnIndex]), &details); err != nil {
details = make(map[string]interface{})
}
return &ScanResult{
Time: t,
Type: ResultType(row[1]),
Target: row[2],
Status: row[3],
Type: ResultType(row[CSVTypeColumnIndex]),
Target: row[CSVTargetColumnIndex],
Status: row[CSVStatusColumnIndex],
Details: details,
}, nil
}
@ -445,11 +445,7 @@ func (r *CSVReader) Close() error {
// parseTime 解析时间字符串
func parseTime(timeStr string) (time.Time, error) {
// 尝试多种时间格式
formats := []string{
"2006-01-02 15:04:05",
"2006-01-02T15:04:05Z07:00",
"2006-01-02T15:04:05.000Z07:00",
}
formats := GetSupportedTimeFormats()
for _, format := range formats {
if t, err := time.Parse(format, timeStr); err == nil {
@ -457,5 +453,5 @@ func parseTime(timeStr string) (time.Time, error) {
}
}
return time.Time{}, fmt.Errorf("无法解析时间: %s", timeStr)
return time.Time{}, fmt.Errorf(ErrParseTimeFailed, timeStr)
}

148
Common/output/constants.go Normal file
View File

@ -0,0 +1,148 @@
package output
import (
"os"
"time"
)
/*
constants.go - 输出系统常量定义
统一管理common/output包中的所有常量便于查看和编辑
*/
// =============================================================================
// 输出格式常量 (从Types.go迁移)
// =============================================================================
// OutputFormat 输出格式类型
type OutputFormat string
const (
FormatTXT OutputFormat = "txt" // 文本格式
FormatJSON OutputFormat = "json" // JSON格式
FormatCSV OutputFormat = "csv" // CSV格式
)
// =============================================================================
// 结果类型常量 (从Types.go迁移)
// =============================================================================
// ResultType 定义结果类型
type ResultType string
const (
TypeHost ResultType = "HOST" // 主机存活
TypePort ResultType = "PORT" // 端口开放
TypeService ResultType = "SERVICE" // 服务识别
TypeVuln ResultType = "VULN" // 漏洞发现
)
// =============================================================================
// 默认配置常量
// =============================================================================
const (
// 默认缓冲区配置
DefaultBufferSize = 100 // 默认缓冲区大小
DefaultEnableBuffer = true // 默认启用缓冲
DefaultAutoFlush = true // 默认启用自动刷新
DefaultFlushInterval = 5 * time.Second // 默认刷新间隔
// 文件权限常量
DefaultFilePermissions = 0644 // 默认文件权限
DefaultDirPermissions = 0755 // 默认目录权限
// 文件操作标志
DefaultFileFlags = os.O_CREATE | os.O_WRONLY | os.O_APPEND // 默认文件打开标志
)
// =============================================================================
// CSV格式常量 (从Writers.go迁移)
// =============================================================================
var (
// CSV头部字段
CSVHeaders = []string{"Time", "Type", "Target", "Status", "Details"}
)
// =============================================================================
// 时间格式常量 (从Writers.go迁移)
// =============================================================================
const (
// 时间格式
DefaultTimeFormat = "2006-01-02 15:04:05" // 默认时间格式
ISO8601TimeFormat = "2006-01-02T15:04:05Z07:00" // ISO8601时间格式
ISO8601MilliFormat = "2006-01-02T15:04:05.000Z07:00" // ISO8601毫秒格式
)
// GetSupportedTimeFormats 获取支持的时间格式列表
func GetSupportedTimeFormats() []string {
return []string{
DefaultTimeFormat,
ISO8601TimeFormat,
ISO8601MilliFormat,
}
}
// =============================================================================
// JSON常量
// =============================================================================
const (
// JSON格式化
JSONIndentPrefix = "" // JSON缩进前缀
JSONIndentString = " " // JSON缩进字符串
// 空JSON对象
EmptyJSONObject = "{}"
)
// =============================================================================
// 文本格式常量 (从Writers.go迁移)
// =============================================================================
const (
// 文本输出格式
TxtTimeFormat = "2006-01-02 15:04:05" // 文本时间格式
TxtOutputTemplate = "[%s] [%s] %s - %s" // 文本输出模板
TxtDetailsFormat = " (%s)" // 详细信息格式
TxtNewline = "\n" // 换行符
TxtDetailsSeparator = ", " // 详细信息分隔符
TxtKeyValueFormat = "%s=%v" // 键值对格式
)
// =============================================================================
// 错误消息常量
// =============================================================================
const (
ErrConfigNil = "配置不能为空"
ErrUnsupportedFormat = "不支持的输出格式: %s"
ErrWriterInitFailed = "初始化写入器失败: %v"
ErrWriterClosed = "写入器已关闭"
ErrManagerNotInit = "输出管理器未初始化"
ErrManagerClosed = "输出管理器已关闭"
ErrWriteFailed = "写入结果失败: %v"
ErrFlushFailed = "刷新写入器失败: %v"
ErrCreateFileFailed = "创建%s文件失败: %v"
ErrWriteHeaderFailed = "写入CSV头部失败: %v"
ErrOpenFileFailed = "打开CSV文件失败: %v"
ErrReadFileFailed = "读取CSV文件失败: %v"
ErrParseTimeFailed = "无法解析时间: %s"
)
// =============================================================================
// CSV相关常量
// =============================================================================
const (
CSVMinColumns = 5 // CSV最小列数
CSVHeaderRowIndex = 0 // CSV头部行索引
CSVTimeColumnIndex = 0 // 时间列索引
CSVTypeColumnIndex = 1 // 类型列索引
CSVTargetColumnIndex = 2 // 目标列索引
CSVStatusColumnIndex = 3 // 状态列索引
CSVDetailsColumnIndex = 4 // 详细信息列索引
)

View File

@ -12,7 +12,7 @@ import (
// CredentialParser 凭据解析器
type CredentialParser struct {
fileReader *FileReader
mu sync.RWMutex
mu sync.RWMutex //nolint:unused // reserved for future thread safety
hashRegex *regexp.Regexp
options *CredentialParserOptions
}
@ -31,13 +31,13 @@ type CredentialParserOptions struct {
// DefaultCredentialParserOptions 默认凭据解析器选项
func DefaultCredentialParserOptions() *CredentialParserOptions {
return &CredentialParserOptions{
MaxUsernameLength: 64,
MaxPasswordLength: 128,
AllowEmptyPasswords: true,
ValidateHashes: true,
DeduplicateUsers: true,
DeduplicatePasswords: true,
EnableStatistics: true,
MaxUsernameLength: DefaultMaxUsernameLength,
MaxPasswordLength: DefaultMaxPasswordLength,
AllowEmptyPasswords: DefaultAllowEmptyPasswords,
ValidateHashes: DefaultValidateHashes,
DeduplicateUsers: DefaultDeduplicateUsers,
DeduplicatePasswords: DefaultDeduplicatePasswords,
EnableStatistics: DefaultCredentialsEnableStatistics,
}
}
@ -48,7 +48,7 @@ func NewCredentialParser(fileReader *FileReader, options *CredentialParserOption
}
// 编译哈希验证正则表达式 (MD5: 32位十六进制)
hashRegex := regexp.MustCompile(`^[a-fA-F0-9]{32}$`)
hashRegex := CompiledHashRegex
return &CredentialParser{
fileReader: fileReader,
@ -77,7 +77,7 @@ type CredentialInput struct {
// Parse 解析凭据配置
func (cp *CredentialParser) Parse(input *CredentialInput, options *ParserOptions) (*ParseResult, error) {
if input == nil {
return nil, NewParseError("INPUT_ERROR", "凭据输入为空", "", 0, ErrEmptyInput)
return nil, NewParseError(ErrorTypeInputError, "凭据输入为空", "", 0, ErrEmptyInput)
}
startTime := time.Now()
@ -142,7 +142,7 @@ func (cp *CredentialParser) parseUsernames(input *CredentialInput) ([]string, []
if processedUser, valid, err := cp.validateUsername(strings.TrimSpace(user)); valid {
usernames = append(usernames, processedUser)
} else if err != nil {
errors = append(errors, NewParseError("USERNAME_ERROR", err.Error(), "command line", 0, err))
errors = append(errors, NewParseError(ErrorTypeUsernameError, err.Error(), "command line", 0, err))
}
}
}
@ -151,7 +151,7 @@ func (cp *CredentialParser) parseUsernames(input *CredentialInput) ([]string, []
if input.UsersFile != "" {
fileResult, err := cp.fileReader.ReadFile(input.UsersFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取用户名文件失败", input.UsersFile, 0, err))
errors = append(errors, NewParseError(ErrorTypeFileError, "读取用户名文件失败", input.UsersFile, 0, err))
} else {
for i, line := range fileResult.Lines {
if processedUser, valid, err := cp.validateUsername(line); valid {
@ -196,7 +196,7 @@ func (cp *CredentialParser) parsePasswords(input *CredentialInput) ([]string, []
if processedPass, valid, err := cp.validatePassword(pass); valid {
passwords = append(passwords, processedPass)
} else if err != nil {
errors = append(errors, NewParseError("PASSWORD_ERROR", err.Error(), "command line", 0, err))
errors = append(errors, NewParseError(ErrorTypePasswordError, err.Error(), "command line", 0, err))
}
}
}
@ -205,7 +205,7 @@ func (cp *CredentialParser) parsePasswords(input *CredentialInput) ([]string, []
if input.PasswordsFile != "" {
fileResult, err := cp.fileReader.ReadFile(input.PasswordsFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取密码文件失败", input.PasswordsFile, 0, err))
errors = append(errors, NewParseError(ErrorTypeFileError, "读取密码文件失败", input.PasswordsFile, 0, err))
} else {
for i, line := range fileResult.Lines {
if processedPass, valid, err := cp.validatePassword(line); valid {
@ -249,7 +249,7 @@ func (cp *CredentialParser) parseHashes(input *CredentialInput) ([]string, [][]b
if valid, err := cp.validateHash(input.HashValue); valid {
hashValues = append(hashValues, input.HashValue)
} else {
errors = append(errors, NewParseError("HASH_ERROR", err.Error(), "command line", 0, err))
errors = append(errors, NewParseError(ErrorTypeHashError, err.Error(), "command line", 0, err))
}
}
@ -257,7 +257,7 @@ func (cp *CredentialParser) parseHashes(input *CredentialInput) ([]string, [][]b
if input.HashFile != "" {
fileResult, err := cp.fileReader.ReadFile(input.HashFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取哈希文件失败", input.HashFile, 0, err))
errors = append(errors, NewParseError(ErrorTypeFileError, "读取哈希文件失败", input.HashFile, 0, err))
} else {
for i, line := range fileResult.Lines {
if valid, err := cp.validateHash(line); valid {
@ -288,12 +288,12 @@ func (cp *CredentialParser) validateUsername(username string) (string, bool, err
}
if len(username) > cp.options.MaxUsernameLength {
return "", false, fmt.Errorf("用户名过长: %d字符最大允许: %d", len(username), cp.options.MaxUsernameLength)
return "", false, fmt.Errorf(ErrUsernameTooLongMsg, len(username), cp.options.MaxUsernameLength)
}
// 检查特殊字符
if strings.ContainsAny(username, "\r\n\t") {
return "", false, fmt.Errorf("用户名包含非法字符")
if strings.ContainsAny(username, InvalidUsernameChars) {
return "", false, fmt.Errorf("%s", ErrUsernameInvalidCharsMsg)
}
return username, true, nil
@ -302,11 +302,11 @@ func (cp *CredentialParser) validateUsername(username string) (string, bool, err
// validatePassword 验证密码
func (cp *CredentialParser) validatePassword(password string) (string, bool, error) {
if len(password) == 0 && !cp.options.AllowEmptyPasswords {
return "", false, fmt.Errorf("不允许空密码")
return "", false, fmt.Errorf("%s", ErrPasswordEmptyMsg)
}
if len(password) > cp.options.MaxPasswordLength {
return "", false, fmt.Errorf("密码过长: %d字符最大允许: %d", len(password), cp.options.MaxPasswordLength)
return "", false, fmt.Errorf(ErrPasswordTooLongMsg, len(password), cp.options.MaxPasswordLength)
}
return password, true, nil
@ -320,11 +320,11 @@ func (cp *CredentialParser) validateHash(hash string) (bool, error) {
hash = strings.TrimSpace(hash)
if len(hash) == 0 {
return false, fmt.Errorf("哈希值为空")
return false, fmt.Errorf("%s", ErrHashEmptyMsg)
}
if !cp.hashRegex.MatchString(hash) {
return false, fmt.Errorf("哈希值格式无效需要32位十六进制字符")
return false, fmt.Errorf("%s", ErrHashInvalidFormatMsg)
}
return true, nil

View File

@ -180,7 +180,7 @@ func (fr *FileReader) readFileSync(filename string, options *FileReaderOptions)
// 检查扫描错误
if err := scanner.Err(); err != nil {
return nil, NewParseError("READ_ERROR", "文件扫描失败", filename, lineNum, err)
return nil, NewParseError(ErrorTypeReadError, ErrFileScanFailedMsg, filename, lineNum, err)
}
// 更新统计信息
@ -205,7 +205,7 @@ func (fr *FileReader) processLine(line string, options *FileReaderOptions) (stri
}
// 跳过注释行
if options.SkipComments && strings.HasPrefix(line, "#") {
if options.SkipComments && strings.HasPrefix(line, CommentPrefix) {
return "", false
}
@ -222,13 +222,13 @@ func (fr *FileReader) processLine(line string, options *FileReaderOptions) (stri
// validateLine 验证行内容
func (fr *FileReader) validateLine(line string) bool {
// 基本验证:检查是否包含特殊字符或过长
if len(line) > 1000 { // 单行最大1000字符
if len(line) > MaxLineLength { // 单行最大字符数
return false
}
// 检查是否包含控制字符
for _, r := range line {
if r < 32 && r != 9 && r != 10 && r != 13 { // 排除tab、换行、回车
if r < MaxValidRune && r != TabRune && r != NewlineRune && r != CarriageReturnRune { // 排除tab、换行、回车
return false
}
}

View File

@ -3,7 +3,6 @@ package parsers
import (
"fmt"
"net/url"
"regexp"
"strconv"
"strings"
"sync"
@ -14,7 +13,7 @@ import (
// NetworkParser 网络配置解析器
type NetworkParser struct {
mu sync.RWMutex
mu sync.RWMutex //nolint:unused // reserved for future thread safety
options *NetworkParserOptions
}
@ -192,7 +191,7 @@ func (np *NetworkParser) parseTimeouts(timeout, webTimeout int64) (time.Duration
// 处理普通超时
finalTimeout := np.options.DefaultTimeout
if timeout > 0 {
if timeout > 300 { // 最大5分钟
if timeout > MaxTimeoutSeconds {
warnings = append(warnings, "超时时间过长建议不超过300秒")
}
finalTimeout = time.Duration(timeout) * time.Second
@ -201,7 +200,7 @@ func (np *NetworkParser) parseTimeouts(timeout, webTimeout int64) (time.Duration
// 处理Web超时
finalWebTimeout := np.options.DefaultWebTimeout
if webTimeout > 0 {
if webTimeout > 120 { // 最大2分钟
if webTimeout > MaxWebTimeoutSeconds {
warnings = append(warnings, "Web超时时间过长建议不超过120秒")
}
finalWebTimeout = time.Duration(webTimeout) * time.Second
@ -270,10 +269,10 @@ func (np *NetworkParser) parseCookie(cookie string) (string, []error, []string)
// normalizeHttpProxy 规范化HTTP代理URL
func (np *NetworkParser) normalizeHttpProxy(proxy string) string {
switch strings.ToLower(proxy) {
case "1":
return "http://127.0.0.1:8080"
case "2":
return "socks5://127.0.0.1:1080"
case ProxyShortcut1:
return ProxyShortcutHTTP
case ProxyShortcut2:
return ProxyShortcutSOCKS5
default:
// 如果没有协议前缀默认使用HTTP
if !strings.Contains(proxy, "://") {
@ -347,10 +346,7 @@ func (np *NetworkParser) validateProxyURL(proxyURL string) error {
// isValidUserAgent 检查用户代理是否有效
func (np *NetworkParser) isValidUserAgent(userAgent string) bool {
// 检查是否包含常见的浏览器标识
commonBrowsers := []string{
"Mozilla", "Chrome", "Safari", "Firefox", "Edge", "Opera",
"AppleWebKit", "Gecko", "Trident", "Presto",
}
commonBrowsers := GetCommonBrowsers()
userAgentLower := strings.ToLower(userAgent)
for _, browser := range commonBrowsers {
@ -365,8 +361,7 @@ func (np *NetworkParser) isValidUserAgent(userAgent string) bool {
// isValidCookie 检查Cookie格式是否有效
func (np *NetworkParser) isValidCookie(cookie string) bool {
// 基本Cookie格式检查 (name=value; name2=value2)
cookieRegex := regexp.MustCompile(`^[^=;\s]+(=[^;\s]*)?(\s*;\s*[^=;\s]+(=[^;\s]*)?)*$`)
return cookieRegex.MatchString(strings.TrimSpace(cookie))
return CompiledCookieRegex.MatchString(strings.TrimSpace(cookie))
}
// =============================================================================================

View File

@ -94,7 +94,7 @@ func SimpleParsePort(ports string) []int {
} else {
// 单个端口
if port, err := strconv.Atoi(portStr); err == nil {
if port >= 1 && port <= 65535 {
if port >= MinPort && port <= MaxPort {
result = append(result, port)
}
}
@ -130,7 +130,7 @@ func readHostsFromFile(filename string) ([]string, error) {
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line != "" && !strings.HasPrefix(line, "#") {
if line != "" && !strings.HasPrefix(line, CommentPrefix) {
hosts = append(hosts, line)
}
}
@ -184,7 +184,7 @@ func parseCIDR(cidr string) ([]string, error) {
hosts = append(hosts, ip.String())
// 限制最大主机数量,防止内存溢出
if len(hosts) > 10000 {
if len(hosts) > SimpleMaxHosts {
break
}
}
@ -237,7 +237,7 @@ func parseShortIPRange(startIPStr, endSuffix string) ([]string, error) {
baseIP := strings.Join(startIPParts[:3], ".")
startNum, _ := strconv.Atoi(startIPParts[3])
for i := startNum; i <= endNum && i <= 254; i++ {
for i := startNum; i <= endNum && i <= RouterSwitchLastOctet; i++ {
hosts = append(hosts, fmt.Sprintf("%s.%d", baseIP, i))
}
@ -263,7 +263,7 @@ func parseFullIPRange(startIPStr, endIPStr string) ([]string, error) {
}
// 限制最大主机数量
if len(hosts) > 10000 {
if len(hosts) > SimpleMaxHosts {
break
}
@ -283,7 +283,7 @@ func parsePortRange(rangeStr string) []int {
start, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))
end, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))
if err1 != nil || err2 != nil || start < 1 || end > 65535 || start > end {
if err1 != nil || err2 != nil || start < MinPort || end > MaxPort || start > end {
return nil
}
@ -292,7 +292,7 @@ func parsePortRange(rangeStr string) []int {
ports = append(ports, i)
// 限制端口范围大小
if len(ports) > 5000 {
if len(ports) > SimpleMaxPortRange {
break
}
}
@ -302,13 +302,8 @@ func parsePortRange(rangeStr string) []int {
// expandPortGroups 展开端口组
func expandPortGroups(ports string) string {
// 定义端口组
portGroups := map[string]string{
"web": "80,81,82,83,84,85,86,87,88,89,90,443,8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8443,9000,9001,9002,9080,9090",
"main": "21,22,23,80,81,110,135,139,143,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616",
"database": "1433,1521,3306,5432,6379,11211,27017",
"common": "21,22,23,25,53,80,110,135,139,143,443,445,993,995,1723,3389,5060,5985,5986",
}
// 使用预定义的端口组
portGroups := GetPortGroups()
result := ports
for group, portList := range portGroups {

View File

@ -14,7 +14,7 @@ import (
// TargetParser 目标解析器
type TargetParser struct {
fileReader *FileReader
mu sync.RWMutex
mu sync.RWMutex //nolint:unused // reserved for future thread safety
ipRegex *regexp.Regexp
portRegex *regexp.Regexp
urlRegex *regexp.Regexp
@ -36,14 +36,14 @@ type TargetParserOptions struct {
// DefaultTargetParserOptions 默认目标解析器选项
func DefaultTargetParserOptions() *TargetParserOptions {
return &TargetParserOptions{
MaxTargets: 10000,
MaxPortRange: 1000,
AllowPrivateIPs: true,
AllowLoopback: true,
ValidateURLs: true,
ResolveDomains: false,
EnableStatistics: true,
DefaultPorts: "21,22,23,80,81,110,135,139,143,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616",
MaxTargets: DefaultTargetMaxTargets,
MaxPortRange: DefaultMaxPortRange,
AllowPrivateIPs: DefaultAllowPrivateIPs,
AllowLoopback: DefaultAllowLoopback,
ValidateURLs: DefaultValidateURLs,
ResolveDomains: DefaultResolveDomains,
EnableStatistics: DefaultTargetEnableStatistics,
DefaultPorts: DefaultPorts,
}
}
@ -53,10 +53,10 @@ func NewTargetParser(fileReader *FileReader, options *TargetParserOptions) *Targ
options = DefaultTargetParserOptions()
}
// 编译正则表达式
ipRegex := regexp.MustCompile(`^(\d{1,3}\.){3}\d{1,3}$`)
portRegex := regexp.MustCompile(`^(\d+)(-(\d+))?$`)
urlRegex := regexp.MustCompile(`^https?://[^\s/$.?#].[^\s]*$`)
// 使用预编译正则表达式
ipRegex := CompiledIPv4Regex
portRegex := CompiledPortRegex
urlRegex := CompiledURLRegex
return &TargetParser{
fileReader: fileReader,
@ -94,7 +94,7 @@ type TargetInput struct {
// Parse 解析目标配置
func (tp *TargetParser) Parse(input *TargetInput, options *ParserOptions) (*ParseResult, error) {
if input == nil {
return nil, NewParseError("INPUT_ERROR", "目标输入为空", "", 0, ErrEmptyInput)
return nil, NewParseError(ErrorTypeInputError, "目标输入为空", "", 0, ErrEmptyInput)
}
startTime := time.Now()
@ -166,7 +166,7 @@ func (tp *TargetParser) parseHosts(input *TargetInput) ([]string, []error, []str
if input.Host != "" {
hostList, err := tp.parseHostList(input.Host)
if err != nil {
errors = append(errors, NewParseError("HOST_ERROR", err.Error(), "command line", 0, err))
errors = append(errors, NewParseError(ErrorTypeHostError, err.Error(), "command line", 0, err))
} else {
hosts = append(hosts, hostList...)
}
@ -176,7 +176,7 @@ func (tp *TargetParser) parseHosts(input *TargetInput) ([]string, []error, []str
if input.HostsFile != "" {
fileResult, err := tp.fileReader.ReadFile(input.HostsFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取主机文件失败", input.HostsFile, 0, err))
errors = append(errors, NewParseError(ErrorTypeFileError, "读取主机文件失败", input.HostsFile, 0, err))
} else {
for i, line := range fileResult.Lines {
hostList, err := tp.parseHostList(line)
@ -245,7 +245,7 @@ func (tp *TargetParser) parseURLs(input *TargetInput) ([]string, []error, []stri
if input.URLsFile != "" {
fileResult, err := tp.fileReader.ReadFile(input.URLsFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取URL文件失败", input.URLsFile, 0, err))
errors = append(errors, NewParseError(ErrorTypeFileError, "读取URL文件失败", input.URLsFile, 0, err))
} else {
for i, line := range fileResult.Lines {
if valid, err := tp.validateURL(line); valid {
@ -273,7 +273,7 @@ func (tp *TargetParser) parsePorts(input *TargetInput) ([]int, []error, []string
if input.Ports != "" {
portList, err := tp.parsePortList(input.Ports)
if err != nil {
errors = append(errors, NewParseError("PORT_ERROR", err.Error(), "command line", 0, err))
errors = append(errors, NewParseError(ErrorTypePortError, err.Error(), "command line", 0, err))
} else {
ports = append(ports, portList...)
}
@ -283,7 +283,7 @@ func (tp *TargetParser) parsePorts(input *TargetInput) ([]int, []error, []string
if input.PortsFile != "" {
fileResult, err := tp.fileReader.ReadFile(input.PortsFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取端口文件失败", input.PortsFile, 0, err))
errors = append(errors, NewParseError(ErrorTypeFileError, "读取端口文件失败", input.PortsFile, 0, err))
} else {
for i, line := range fileResult.Lines {
portList, err := tp.parsePortList(line)
@ -321,7 +321,7 @@ func (tp *TargetParser) parseExcludePorts(input *TargetInput) ([]int, []error, [
if input.ExcludePorts != "" {
portList, err := tp.parsePortList(input.ExcludePorts)
if err != nil {
errors = append(errors, NewParseError("EXCLUDE_PORT_ERROR", err.Error(), "command line", 0, err))
errors = append(errors, NewParseError(ErrorTypeExcludePortError, err.Error(), "command line", 0, err))
} else {
excludePorts = portList
}
@ -366,23 +366,23 @@ func (tp *TargetParser) parseHostList(hostStr string) ([]string, error) {
// 检查各种IP格式
switch {
case item == "192":
case item == PrivateNetwork192:
// 常用内网段简写
cidrHosts, err := tp.parseCIDR("192.168.0.0/16")
cidrHosts, err := tp.parseCIDR(PrivateNetwork192CIDR)
if err != nil {
return nil, fmt.Errorf("192网段解析失败: %v", err)
}
hosts = append(hosts, cidrHosts...)
case item == "172":
case item == PrivateNetwork172:
// 常用内网段简写
cidrHosts, err := tp.parseCIDR("172.16.0.0/12")
cidrHosts, err := tp.parseCIDR(PrivateNetwork172CIDR)
if err != nil {
return nil, fmt.Errorf("172网段解析失败: %v", err)
}
hosts = append(hosts, cidrHosts...)
case item == "10":
case item == PrivateNetwork10:
// 常用内网段简写
cidrHosts, err := tp.parseCIDR("10.0.0.0/8")
cidrHosts, err := tp.parseCIDR(PrivateNetwork10CIDR)
if err != nil {
return nil, fmt.Errorf("10网段解析失败: %v", err)
}
@ -452,7 +452,7 @@ func (tp *TargetParser) parsePortList(portStr string) ([]int, error) {
return nil, fmt.Errorf("无效端口号: %s", item)
}
if port < 1 || port > 65535 {
if port < MinPort || port > MaxPort {
return nil, fmt.Errorf("端口号超出范围: %d", port)
}
@ -465,13 +465,8 @@ func (tp *TargetParser) parsePortList(portStr string) ([]int, error) {
// expandPortGroups 展开预定义端口组
func (tp *TargetParser) expandPortGroups(portStr string) string {
portGroups := map[string]string{
"service": "21,22,23,25,110,135,139,143,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613",
"db": "1433,1521,3306,5432,5672,6379,7687,9042,9093,9200,11211,27017,61616",
"web": "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018",
"all": "1-65535",
"main": "21,22,23,80,81,110,135,139,143,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616",
}
// 使用预定义的端口组
portGroups := GetTargetPortGroups()
if expandedPorts, exists := portGroups[portStr]; exists {
return expandedPorts
@ -540,7 +535,7 @@ func (tp *TargetParser) parseIPRange(rangeStr string) ([]string, error) {
func (tp *TargetParser) parseShortIPRange(startIPStr, endSuffix string) ([]string, error) {
// 将结束段转换为数字
endNum, err := strconv.Atoi(endSuffix)
if err != nil || endNum > 255 {
if err != nil || endNum > MaxIPv4OctetValue {
return nil, fmt.Errorf("无效的IP范围结束值: %s", endSuffix)
}
@ -576,8 +571,8 @@ func (tp *TargetParser) parseFullIPRange(startIP, endIP net.IP) ([]string, error
}
// 计算IP地址的整数表示
startInt := (int(start4[0]) << 24) | (int(start4[1]) << 16) | (int(start4[2]) << 8) | int(start4[3])
endInt := (int(end4[0]) << 24) | (int(end4[1]) << 16) | (int(end4[2]) << 8) | int(end4[3])
startInt := (int(start4[0]) << IPFirstOctetShift) | (int(start4[1]) << IPSecondOctetShift) | (int(start4[2]) << IPThirdOctetShift) | int(start4[3])
endInt := (int(end4[0]) << IPFirstOctetShift) | (int(end4[1]) << IPSecondOctetShift) | (int(end4[2]) << IPThirdOctetShift) | int(end4[3])
// 检查范围的有效性
if startInt > endInt {
@ -593,10 +588,10 @@ func (tp *TargetParser) parseFullIPRange(startIP, endIP net.IP) ([]string, error
var allIP []string
for ipInt := startInt; ipInt <= endInt; ipInt++ {
ip := fmt.Sprintf("%d.%d.%d.%d",
(ipInt>>24)&0xFF,
(ipInt>>16)&0xFF,
(ipInt>>8)&0xFF,
ipInt&0xFF)
(ipInt>>IPFirstOctetShift)&IPOctetMask,
(ipInt>>IPSecondOctetShift)&IPOctetMask,
(ipInt>>IPThirdOctetShift)&IPOctetMask,
ipInt&IPOctetMask)
allIP = append(allIP, ip)
}
@ -616,28 +611,27 @@ func (tp *TargetParser) parseSubnet8(subnet string) []string {
var sampleIPs []string
// 对常用网段进行更全面的扫描
commonSecondOctets := []int{0, 1, 2, 10, 100, 200, 254}
commonSecondOctets := GetCommonSecondOctets()
// 对于每个选定的第二段,采样部分第三段
for _, secondOctet := range commonSecondOctets {
for thirdOctet := 0; thirdOctet < 256; thirdOctet += 10 {
for thirdOctet := 0; thirdOctet < 256; thirdOctet += Subnet8ThirdOctetStep {
// 添加常见的网关和服务器IP
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.1", firstOctet, secondOctet, thirdOctet)) // 默认网关
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.254", firstOctet, secondOctet, thirdOctet)) // 通常用于路由器/交换机
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, DefaultGatewayLastOctet)) // 默认网关
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, RouterSwitchLastOctet)) // 通常用于路由器/交换机
// 随机采样不同范围的主机IP
fourthOctet := tp.randomInt(2, 253)
fourthOctet := tp.randomInt(SamplingMinHost, SamplingMaxHost)
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, fourthOctet))
}
}
// 对其他二级网段进行稀疏采样
samplingStep := 32 // 每32个二级网段采样1个
for secondOctet := 0; secondOctet < 256; secondOctet += samplingStep {
for thirdOctet := 0; thirdOctet < 256; thirdOctet += samplingStep {
for secondOctet := 0; secondOctet < 256; secondOctet += Subnet8SamplingStep {
for thirdOctet := 0; thirdOctet < 256; thirdOctet += Subnet8SamplingStep {
// 对于采样的网段取几个代表性IP
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.1", firstOctet, secondOctet, thirdOctet))
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, tp.randomInt(2, 253)))
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, DefaultGatewayLastOctet))
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, tp.randomInt(SamplingMinHost, SamplingMaxHost)))
}
}
@ -675,7 +669,7 @@ func (tp *TargetParser) parsePortRange(rangeStr string) ([]int, error) {
startPort, endPort = endPort, startPort
}
if startPort < 1 || endPort > 65535 {
if startPort < MinPort || endPort > MaxPort {
return nil, fmt.Errorf("端口号超出范围")
}
@ -779,7 +773,7 @@ func (tp *TargetParser) validateHostPort(hostPort string) (bool, error) {
return false, fmt.Errorf("端口号无效: %s", portStr)
}
if port < 1 || port > 65535 {
if port < MinPort || port > MaxPort {
return false, fmt.Errorf("端口号超出范围: %d", port)
}
@ -794,11 +788,11 @@ func (tp *TargetParser) isPrivateIP(ip net.IP) bool {
return true
}
// 172.16.0.0/12
if ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31 {
if ip4[0] == 172 && ip4[1] >= Private172StartSecondOctet && ip4[1] <= Private172EndSecondOctet {
return true
}
// 192.168.0.0/16
if ip4[0] == 192 && ip4[1] == 168 {
if ip4[0] == 192 && ip4[1] == Private192SecondOctet {
return true
}
}
@ -807,8 +801,7 @@ func (tp *TargetParser) isPrivateIP(ip net.IP) bool {
// isValidDomain 检查是否为有效域名
func (tp *TargetParser) isValidDomain(domain string) bool {
domainRegex := regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$`)
return domainRegex.MatchString(domain) && len(domain) <= 253
return CompiledDomainRegex.MatchString(domain) && len(domain) <= MaxDomainLength
}
// excludeHosts 排除指定主机

View File

@ -87,7 +87,7 @@ type ParseResult struct {
// 预定义错误类型
var (
ErrEmptyInput = errors.New("输入参数为空")
ErrEmptyInput = errors.New(ErrEmptyInputMsg)
)
// ParserOptions 解析器选项
@ -105,14 +105,14 @@ type ParserOptions struct {
// DefaultParserOptions 返回默认解析器选项
func DefaultParserOptions() *ParserOptions {
return &ParserOptions{
EnableConcurrency: true,
MaxWorkers: 4,
Timeout: 30 * time.Second,
EnableValidation: true,
EnableStatistics: true,
IgnoreErrors: false,
FileMaxSize: 100 * 1024 * 1024, // 100MB
MaxTargets: 10000, // 10K targets
EnableConcurrency: DefaultEnableConcurrency,
MaxWorkers: DefaultMaxWorkers,
Timeout: DefaultTimeout,
EnableValidation: DefaultEnableValidation,
EnableStatistics: DefaultEnableStatistics,
IgnoreErrors: DefaultIgnoreErrors,
FileMaxSize: DefaultFileMaxSize,
MaxTargets: DefaultMaxTargets,
}
}

View File

@ -8,7 +8,7 @@ import (
// ValidationParser 参数验证解析器
type ValidationParser struct {
mu sync.RWMutex
mu sync.RWMutex //nolint:unused // reserved for future thread safety
options *ValidationParserOptions
}
@ -25,12 +25,12 @@ type ValidationParserOptions struct {
// DefaultValidationParserOptions 默认验证解析器选项
func DefaultValidationParserOptions() *ValidationParserOptions {
return &ValidationParserOptions{
StrictMode: false,
AllowEmpty: true,
CheckConflicts: true,
ValidateTargets: true,
ValidateNetwork: true,
MaxErrorCount: 100,
StrictMode: DefaultStrictMode,
AllowEmpty: DefaultAllowEmpty,
CheckConflicts: DefaultCheckConflicts,
ValidateTargets: DefaultValidateTargets,
ValidateNetwork: DefaultValidateNetwork,
MaxErrorCount: DefaultMaxErrorCount,
}
}
@ -88,7 +88,7 @@ type ValidationRule struct {
// Parse 执行参数验证
func (vp *ValidationParser) Parse(input *ValidationInput, config *ParsedConfig, options *ParserOptions) (*ParseResult, error) {
if input == nil {
return nil, NewParseError("INPUT_ERROR", "验证输入为空", "", 0, ErrEmptyInput)
return nil, NewParseError(ErrorTypeInputError, "验证输入为空", "", 0, ErrEmptyInput)
}
startTime := time.Now()
@ -248,22 +248,22 @@ func (vp *ValidationParser) checkPerformance(input *ValidationInput, config *Par
// 检查目标数量
if config.Targets != nil {
totalTargets := len(config.Targets.Hosts) * len(config.Targets.Ports)
if totalTargets > 100000 {
if totalTargets > MaxTargetsThreshold {
warnings = append(warnings, fmt.Sprintf("大量目标(%d),可能耗时较长", totalTargets))
}
// 检查端口范围
if len(config.Targets.Ports) > 1000 {
if len(config.Targets.Ports) > DefaultMaxPortRange {
warnings = append(warnings, "端口数量过多")
}
}
// 检查超时配置
if config.Network != nil {
if config.Network.Timeout < 1*time.Second {
if config.Network.Timeout < MinTimeoutThreshold {
warnings = append(warnings, "超时过短")
}
if config.Network.Timeout > 60*time.Second {
if config.Network.Timeout > MaxTimeoutThreshold {
warnings = append(warnings, "超时过长")
}
}

304
Common/parsers/constants.go Normal file
View File

@ -0,0 +1,304 @@
package parsers
import (
"regexp"
"time"
)
/*
constants.go - 解析器系统常量定义
统一管理common/parsers包中的所有常量便于查看和编辑
*/
// =============================================================================
// 默认解析器选项常量 (从Types.go迁移)
// =============================================================================
const (
// 解析器默认配置
DefaultEnableConcurrency = true
DefaultMaxWorkers = 4
DefaultTimeout = 30 * time.Second
DefaultEnableValidation = true
DefaultEnableStatistics = true
DefaultIgnoreErrors = false
DefaultFileMaxSize = 100 * 1024 * 1024 // 100MB
DefaultMaxTargets = 10000 // 10K targets
)
// =============================================================================
// 文件读取器常量 (从FileReader.go迁移)
// =============================================================================
const (
// 文件读取器默认配置
DefaultMaxCacheSize = 10
DefaultEnableCache = true
DefaultFileReaderMaxFileSize = 50 * 1024 * 1024 // 50MB
DefaultFileReaderTimeout = 30 * time.Second
DefaultFileReaderEnableValidation = true
DefaultTrimSpace = true
DefaultSkipEmpty = true
DefaultSkipComments = true
// 文件内容验证
MaxLineLength = 1000 // 单行最大字符数
MaxValidRune = 32 // 最小有效字符ASCII值
TabRune = 9 // Tab字符
NewlineRune = 10 // 换行符
CarriageReturnRune = 13 // 回车符
CommentPrefix = "#" // 注释前缀
)
// =============================================================================
// 凭据解析器常量 (从CredentialParser.go迁移)
// =============================================================================
const (
// 凭据验证限制
DefaultMaxUsernameLength = 64
DefaultMaxPasswordLength = 128
DefaultAllowEmptyPasswords = true
DefaultValidateHashes = true
DefaultDeduplicateUsers = true
DefaultDeduplicatePasswords = true
DefaultCredentialsEnableStatistics = true
// 哈希验证
HashRegexPattern = `^[a-fA-F0-9]{32}$` // MD5哈希正则表达式
HashValidationLength = 32 // 有效哈希长度
InvalidUsernameChars = "\r\n\t" // 无效用户名字符
)
// =============================================================================
// 网络解析器常量 (从NetworkParser.go迁移)
// =============================================================================
const (
// 网络配置默认值
DefaultValidateProxies = true
DefaultAllowInsecure = false
DefaultNetworkTimeout = 30 * time.Second
DefaultWebTimeout = 10 * time.Second
DefaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
// 超时限制
MaxTimeoutSeconds = 300 // 最大超时5分钟
MaxWebTimeoutSeconds = 120 // 最大Web超时2分钟
// 字符串长度限制
MaxUserAgentLength = 512 // 最大用户代理长度
MaxCookieLength = 4096 // 最大Cookie长度
// 代理快捷配置
ProxyShortcut1 = "1"
ProxyShortcut2 = "2"
ProxyShortcutHTTP = "http://127.0.0.1:8080"
ProxyShortcutSOCKS5 = "socks5://127.0.0.1:1080"
// 协议支持
ProtocolHTTP = "http"
ProtocolHTTPS = "https"
ProtocolSOCKS5 = "socks5"
ProtocolPrefix = "://"
SOCKS5Prefix = "socks5://"
HTTPPrefix = "http://"
// 端口范围
MinPort = 1
MaxPort = 65535
// 无效字符集
InvalidUserAgentChars = "\r\n\t"
)
// GetCommonBrowsers 获取常见浏览器标识列表
func GetCommonBrowsers() []string {
return []string{
"Mozilla", "Chrome", "Safari", "Firefox", "Edge", "Opera",
"AppleWebKit", "Gecko", "Trident", "Presto",
}
}
// =============================================================================
// 目标解析器常量 (从TargetParser.go迁移)
// =============================================================================
const (
// 目标解析器默认配置
DefaultTargetMaxTargets = 10000
DefaultMaxPortRange = 1000
DefaultAllowPrivateIPs = true
DefaultAllowLoopback = true
DefaultValidateURLs = true
DefaultResolveDomains = false
DefaultTargetEnableStatistics = true
DefaultPorts = "21,22,23,80,81,110,135,139,143,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616"
// 正则表达式模式
IPv4RegexPattern = `^(\d{1,3}\.){3}\d{1,3}$`
PortRangeRegexPattern = `^(\d+)(-(\d+))?$`
URLValidationRegexPattern = `^https?://[^\s/$.?#].[^\s]*$`
DomainRegexPattern = `^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$`
CookieRegexPattern = `^[^=;\s]+(=[^;\s]*)?(\s*;\s*[^=;\s]+(=[^;\s]*)?)*$`
// IP地址限制
MaxIPv4OctetValue = 255
MinIPv4OctetValue = 0
IPv4OctetCount = 4
MaxDomainLength = 253
// CIDR网段简写
PrivateNetwork192 = "192"
PrivateNetwork172 = "172"
PrivateNetwork10 = "10"
PrivateNetwork192CIDR = "192.168.0.0/16"
PrivateNetwork172CIDR = "172.16.0.0/12"
PrivateNetwork10CIDR = "10.0.0.0/8"
// 私有网络范围
Private172StartSecondOctet = 16
Private172EndSecondOctet = 31
Private192SecondOctet = 168
// /8网段采样配置
Subnet8SamplingStep = 32
Subnet8ThirdOctetStep = 10
// IP地址计算位移
IPFirstOctetShift = 24
IPSecondOctetShift = 16
IPThirdOctetShift = 8
IPOctetMask = 0xFF
)
// GetCommonSecondOctets 获取常用第二段IP
func GetCommonSecondOctets() []int {
return []int{0, 1, 2, 10, 100, 200, 254}
}
// =============================================================================
// 简化解析器常量 (从Simple.go迁移)
// =============================================================================
const (
// 端口和主机限制
SimpleMaxHosts = 10000
SimpleMaxPortRange = 5000
// 网段简写展开
DefaultGatewayLastOctet = 1
RouterSwitchLastOctet = 254
SamplingMinHost = 2
SamplingMaxHost = 253
)
// GetPortGroups 获取预定义端口组映射
func GetPortGroups() map[string]string {
return map[string]string{
"web": "80,81,82,83,84,85,86,87,88,89,90,443,8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8443,9000,9001,9002,9080,9090",
"main": "21,22,23,80,81,110,135,139,143,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616",
"database": "1433,1521,3306,5432,6379,11211,27017",
"common": "21,22,23,25,53,80,110,135,139,143,443,445,993,995,1723,3389,5060,5985,5986",
}
}
// GetTargetPortGroups 获取目标解析器端口组映射
func GetTargetPortGroups() map[string]string {
return map[string]string{
"service": "21,22,23,25,110,135,139,143,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613",
"db": "1433,1521,3306,5432,5672,6379,7687,9042,9093,9200,11211,27017,61616",
"web": "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018",
"all": "1-65535",
"main": "21,22,23,80,81,110,135,139,143,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616",
}
}
// =============================================================================
// 验证解析器常量 (从ValidationParser.go迁移)
// =============================================================================
const (
// 验证解析器默认配置
DefaultMaxErrorCount = 100
DefaultStrictMode = false
DefaultAllowEmpty = true
DefaultCheckConflicts = true
DefaultValidateTargets = true
DefaultValidateNetwork = true
// 性能警告阈值
MaxTargetsThreshold = 100000 // 最大目标数量阈值
MinTimeoutThreshold = 1 * time.Second // 最小超时阈值
MaxTimeoutThreshold = 60 * time.Second // 最大超时阈值
)
// =============================================================================
// 错误类型常量
// =============================================================================
const (
// 解析错误类型
ErrorTypeInputError = "INPUT_ERROR"
ErrorTypeFileError = "FILE_ERROR"
ErrorTypeTimeout = "TIMEOUT"
ErrorTypeReadError = "READ_ERROR"
ErrorTypeUsernameError = "USERNAME_ERROR"
ErrorTypePasswordError = "PASSWORD_ERROR"
ErrorTypeHashError = "HASH_ERROR"
ErrorTypeProxyError = "PROXY_ERROR"
ErrorTypeUserAgentError = "USERAGENT_ERROR"
ErrorTypeCookieError = "COOKIE_ERROR"
ErrorTypeHostError = "HOST_ERROR"
ErrorTypePortError = "PORT_ERROR"
ErrorTypeExcludePortError = "EXCLUDE_PORT_ERROR"
// 错误消息模板
ErrEmptyInputMsg = "输入参数为空"
ErrFileEmptyMsg = "文件名为空"
ErrFileTooBigMsg = "文件过大: %d bytes, 最大限制: %d bytes"
ErrCannotOpenFileMsg = "无法打开文件"
ErrFileNotExistsMsg = "文件不存在或无法访问"
ErrFileReadTimeoutMsg = "文件读取超时"
ErrFileScanFailedMsg = "文件扫描失败"
ErrUsernameTooLongMsg = "用户名过长: %d字符最大允许: %d"
ErrUsernameInvalidCharsMsg = "用户名包含非法字符"
ErrPasswordEmptyMsg = "不允许空密码"
ErrPasswordTooLongMsg = "密码过长: %d字符最大允许: %d"
ErrHashEmptyMsg = "哈希值为空"
ErrHashInvalidFormatMsg = "哈希值格式无效需要32位十六进制字符"
ErrProxyURLInvalidMsg = "代理URL格式无效: %v"
ErrProxyProtocolUnsupportedMsg = "不支持的代理协议: %s"
ErrProxyHostEmptyMsg = "代理主机名为空"
ErrProxyPortInvalidMsg = "代理端口号无效: %s"
ErrProxyPortOutOfRangeMsg = "代理端口号超出范围: %d"
ErrProxyInsecureMsg = "不允许使用不安全的HTTP代理"
ErrUserAgentTooLongMsg = "用户代理字符串过长"
ErrUserAgentInvalidCharsMsg = "用户代理包含非法字符"
ErrCookieTooLongMsg = "Cookie字符串过长"
)
// =============================================================================
// 编译时正则表达式
// =============================================================================
var (
// 预编译的正则表达式,提高性能
CompiledHashRegex *regexp.Regexp
CompiledIPv4Regex *regexp.Regexp
CompiledPortRegex *regexp.Regexp
CompiledURLRegex *regexp.Regexp
CompiledDomainRegex *regexp.Regexp
CompiledCookieRegex *regexp.Regexp
)
// 在包初始化时编译正则表达式
func init() {
CompiledHashRegex = regexp.MustCompile(HashRegexPattern)
CompiledIPv4Regex = regexp.MustCompile(IPv4RegexPattern)
CompiledPortRegex = regexp.MustCompile(PortRangeRegexPattern)
CompiledURLRegex = regexp.MustCompile(URLValidationRegexPattern)
CompiledDomainRegex = regexp.MustCompile(DomainRegexPattern)
CompiledCookieRegex = regexp.MustCompile(CookieRegexPattern)
}

View File

@ -1,17 +1,12 @@
package proxy
import (
"sync"
)
var (
globalManager ProxyManager
globalMutex sync.RWMutex
once sync.Once
)
// 已清理未使用的导入和全局变量
// =============================================================================================
// 已删除的死代码(未使用):
// - globalManager: 全局代理管理器变量
// - globalMutex: 全局互斥锁
// - once: 全局初始化once变量
// - InitGlobalProxy: 初始化全局代理管理器
// - GetGlobalProxy: 获取全局代理管理器
// - UpdateGlobalProxyConfig: 更新全局代理配置

View File

@ -27,11 +27,11 @@ func (h *httpDialer) DialContext(ctx context.Context, network, address string) (
atomic.AddInt64(&h.stats.TotalConnections, 1)
// 连接到HTTP代理服务器
proxyConn, err := h.baseDial.DialContext(ctx, "tcp", h.config.Address)
proxyConn, err := h.baseDial.DialContext(ctx, NetworkTCP, h.config.Address)
if err != nil {
atomic.AddInt64(&h.stats.FailedConnections, 1)
h.stats.LastError = err.Error()
return nil, NewProxyError(ErrTypeConnection, "连接HTTP代理服务器失败", 4001, err)
return nil, NewProxyError(ErrTypeConnection, ErrMsgHTTPConnFailed, ErrCodeHTTPConnFailed, err)
}
// 发送CONNECT请求
@ -56,43 +56,43 @@ func (h *httpDialer) DialContext(ctx context.Context, network, address string) (
// sendConnectRequest 发送HTTP CONNECT请求
func (h *httpDialer) sendConnectRequest(conn net.Conn, address string) error {
// 构建CONNECT请求
req := fmt.Sprintf("CONNECT %s HTTP/1.1\r\nHost: %s\r\n", address, address)
req := fmt.Sprintf(HTTPConnectRequestFormat, address, address)
// 添加认证头
if h.config.Username != "" {
auth := base64.StdEncoding.EncodeToString(
[]byte(h.config.Username + ":" + h.config.Password))
req += fmt.Sprintf("Proxy-Authorization: Basic %s\r\n", auth)
[]byte(h.config.Username + AuthSeparator + h.config.Password))
req += fmt.Sprintf(HTTPAuthHeaderFormat, auth)
}
req += "\r\n"
req += HTTPRequestEndFormat
// 设置写超时
if err := conn.SetWriteDeadline(time.Now().Add(h.config.Timeout)); err != nil {
return NewProxyError(ErrTypeTimeout, "设置写超时失败", 4002, err)
return NewProxyError(ErrTypeTimeout, ErrMsgHTTPSetWriteTimeout, ErrCodeHTTPSetWriteTimeout, err)
}
// 发送请求
if _, err := conn.Write([]byte(req)); err != nil {
return NewProxyError(ErrTypeConnection, "发送CONNECT请求失败", 4003, err)
return NewProxyError(ErrTypeConnection, ErrMsgHTTPSendConnectFail, ErrCodeHTTPSendConnectFail, err)
}
// 设置读超时
if err := conn.SetReadDeadline(time.Now().Add(h.config.Timeout)); err != nil {
return NewProxyError(ErrTypeTimeout, "设置读超时失败", 4004, err)
return NewProxyError(ErrTypeTimeout, ErrMsgHTTPSetReadTimeout, ErrCodeHTTPSetReadTimeout, err)
}
// 读取响应
resp, err := http.ReadResponse(bufio.NewReader(conn), nil)
if err != nil {
return NewProxyError(ErrTypeProtocol, "读取HTTP响应失败", 4005, err)
return NewProxyError(ErrTypeProtocol, ErrMsgHTTPReadRespFailed, ErrCodeHTTPReadRespFailed, err)
}
defer resp.Body.Close()
// 检查响应状态
if resp.StatusCode != 200 {
if resp.StatusCode != HTTPStatusOK {
return NewProxyError(ErrTypeAuth,
fmt.Sprintf("HTTP代理连接失败状态码: %d", resp.StatusCode), 4006, nil)
fmt.Sprintf(ErrMsgHTTPProxyAuthFailed, resp.StatusCode), ErrCodeHTTPProxyAuthFailed, nil)
}
// 清除deadline

View File

@ -37,7 +37,7 @@ func NewProxyManager(config *ProxyConfig) ProxyManager {
ProxyAddress: config.Address,
},
dialerCache: make(map[string]Dialer),
cacheExpiry: time.Now().Add(5 * time.Minute),
cacheExpiry: time.Now().Add(DefaultCacheExpiry),
}
}
@ -55,7 +55,7 @@ func (m *manager) GetDialer() (Dialer, error) {
case ProxyTypeHTTP, ProxyTypeHTTPS:
return m.createHTTPDialer()
default:
return nil, NewProxyError(ErrTypeConfig, "不支持的代理类型", 1001, nil)
return nil, NewProxyError(ErrTypeConfig, ErrMsgUnsupportedProxyType, ErrCodeUnsupportedProxyType, nil)
}
}
@ -76,7 +76,7 @@ func (m *manager) GetTLSDialer() (TLSDialer, error) {
// UpdateConfig 更新配置
func (m *manager) UpdateConfig(config *ProxyConfig) error {
if config == nil {
return NewProxyError(ErrTypeConfig, "配置不能为空", 1002, nil)
return NewProxyError(ErrTypeConfig, ErrMsgEmptyConfig, ErrCodeEmptyConfig, nil)
}
m.mu.Lock()
@ -89,7 +89,7 @@ func (m *manager) UpdateConfig(config *ProxyConfig) error {
// 清理缓存
m.cacheMu.Lock()
m.dialerCache = make(map[string]Dialer)
m.cacheExpiry = time.Now().Add(5 * time.Minute)
m.cacheExpiry = time.Now().Add(DefaultCacheExpiry)
m.cacheMu.Unlock()
return nil
@ -125,7 +125,7 @@ func (m *manager) createDirectDialer() Dialer {
// createSOCKS5Dialer 创建SOCKS5拨号器
func (m *manager) createSOCKS5Dialer() (Dialer, error) {
// 检查缓存
cacheKey := fmt.Sprintf("socks5_%s", m.config.Address)
cacheKey := fmt.Sprintf(CacheKeySOCKS5, m.config.Address)
m.cacheMu.RLock()
if time.Now().Before(m.cacheExpiry) {
if cached, exists := m.dialerCache[cacheKey]; exists {
@ -136,15 +136,15 @@ func (m *manager) createSOCKS5Dialer() (Dialer, error) {
m.cacheMu.RUnlock()
// 解析代理地址
proxyURL := fmt.Sprintf("socks5://%s", m.config.Address)
proxyURL := fmt.Sprintf(SOCKS5URLFormat, m.config.Address)
if m.config.Username != "" {
proxyURL = fmt.Sprintf("socks5://%s:%s@%s",
proxyURL = fmt.Sprintf(SOCKS5URLAuthFormat,
m.config.Username, m.config.Password, m.config.Address)
}
u, err := url.Parse(proxyURL)
if err != nil {
return nil, NewProxyError(ErrTypeConfig, "SOCKS5代理地址解析失败", 2001, err)
return nil, NewProxyError(ErrTypeConfig, ErrMsgSOCKS5ParseFailed, ErrCodeSOCKS5ParseFailed, err)
}
// 创建基础拨号器
@ -164,9 +164,9 @@ func (m *manager) createSOCKS5Dialer() (Dialer, error) {
}
}
socksDialer, err := proxy.SOCKS5("tcp", u.Host, auth, baseDial)
socksDialer, err := proxy.SOCKS5(NetworkTCP, u.Host, auth, baseDial)
if err != nil {
return nil, NewProxyError(ErrTypeConnection, "SOCKS5拨号器创建失败", 2002, err)
return nil, NewProxyError(ErrTypeConnection, ErrMsgSOCKS5CreateFailed, ErrCodeSOCKS5CreateFailed, err)
}
dialer := &socks5Dialer{
@ -178,7 +178,7 @@ func (m *manager) createSOCKS5Dialer() (Dialer, error) {
// 更新缓存
m.cacheMu.Lock()
m.dialerCache[cacheKey] = dialer
m.cacheExpiry = time.Now().Add(5 * time.Minute)
m.cacheExpiry = time.Now().Add(DefaultCacheExpiry)
m.cacheMu.Unlock()
return dialer, nil
@ -187,7 +187,7 @@ func (m *manager) createSOCKS5Dialer() (Dialer, error) {
// createHTTPDialer 创建HTTP代理拨号器
func (m *manager) createHTTPDialer() (Dialer, error) {
// 检查缓存
cacheKey := fmt.Sprintf("http_%s", m.config.Address)
cacheKey := fmt.Sprintf(CacheKeyHTTP, m.config.Address)
m.cacheMu.RLock()
if time.Now().Before(m.cacheExpiry) {
if cached, exists := m.dialerCache[cacheKey]; exists {
@ -209,7 +209,7 @@ func (m *manager) createHTTPDialer() (Dialer, error) {
// 更新缓存
m.cacheMu.Lock()
m.dialerCache[cacheKey] = dialer
m.cacheExpiry = time.Now().Add(5 * time.Minute)
m.cacheExpiry = time.Now().Add(DefaultCacheExpiry)
m.cacheMu.Unlock()
return dialer, nil
@ -241,7 +241,7 @@ func (d *directDialer) DialContext(ctx context.Context, network, address string)
if err != nil {
atomic.AddInt64(&d.stats.FailedConnections, 1)
d.stats.LastError = err.Error()
return nil, NewProxyError(ErrTypeConnection, "直连失败", 3001, err)
return nil, NewProxyError(ErrTypeConnection, ErrMsgDirectConnFailed, ErrCodeDirectConnFailed, err)
}
atomic.AddInt64(&d.stats.ActiveConnections, 1)
@ -296,7 +296,7 @@ func (s *socks5Dialer) DialContext(ctx context.Context, network, address string)
case <-dialCtx.Done():
atomic.AddInt64(&s.stats.FailedConnections, 1)
s.stats.LastError = dialCtx.Err().Error()
return nil, NewProxyError(ErrTypeTimeout, "SOCKS5连接超时", 3002, dialCtx.Err())
return nil, NewProxyError(ErrTypeTimeout, ErrMsgSOCKS5ConnTimeout, ErrCodeSOCKS5ConnTimeout, dialCtx.Err())
case result := <-connChan:
duration := time.Since(start)
s.stats.LastConnectTime = start
@ -304,7 +304,7 @@ func (s *socks5Dialer) DialContext(ctx context.Context, network, address string)
if result.err != nil {
atomic.AddInt64(&s.stats.FailedConnections, 1)
s.stats.LastError = result.err.Error()
return nil, NewProxyError(ErrTypeConnection, "SOCKS5连接失败", 3003, result.err)
return nil, NewProxyError(ErrTypeConnection, ErrMsgSOCKS5ConnFailed, ErrCodeSOCKS5ConnFailed, result.err)
}
atomic.AddInt64(&s.stats.ActiveConnections, 1)

View File

@ -33,7 +33,7 @@ func (t *tlsDialerWrapper) DialTLSContext(ctx context.Context, network, address
// 首先建立TCP连接
tcpConn, err := t.dialer.DialContext(ctx, network, address)
if err != nil {
return nil, NewProxyError(ErrTypeConnection, "建立TCP连接失败", 5001, err)
return nil, NewProxyError(ErrTypeConnection, ErrMsgTLSTCPConnFailed, ErrCodeTLSTCPConnFailed, err)
}
// 创建TLS连接
@ -51,7 +51,7 @@ func (t *tlsDialerWrapper) DialTLSContext(ctx context.Context, network, address
tcpConn.Close()
atomic.AddInt64(&t.stats.FailedConnections, 1)
t.stats.LastError = err.Error()
return nil, NewProxyError(ErrTypeConnection, "TLS握手失败", 5002, err)
return nil, NewProxyError(ErrTypeConnection, ErrMsgTLSHandshakeFailed, ErrCodeTLSHandshakeFailed, err)
}
// 清除deadline让上层代码管理超时

View File

@ -21,15 +21,15 @@ const (
func (pt ProxyType) String() string {
switch pt {
case ProxyTypeNone:
return "none"
return ProxyTypeStringNone
case ProxyTypeHTTP:
return "http"
return ProxyTypeStringHTTP
case ProxyTypeHTTPS:
return "https"
return ProxyTypeStringHTTPS
case ProxyTypeSOCKS5:
return "socks5"
return ProxyTypeStringSOCKS5
default:
return "unknown"
return ProxyTypeStringUnknown
}
}
@ -50,11 +50,11 @@ type ProxyConfig struct {
func DefaultProxyConfig() *ProxyConfig {
return &ProxyConfig{
Type: ProxyTypeNone,
Timeout: 30 * time.Second,
MaxRetries: 3,
KeepAlive: 30 * time.Second,
IdleTimeout: 90 * time.Second,
MaxIdleConns: 10,
Timeout: DefaultProxyTimeout,
MaxRetries: DefaultProxyMaxRetries,
KeepAlive: DefaultProxyKeepAlive,
IdleTimeout: DefaultProxyIdleTimeout,
MaxIdleConns: DefaultProxyMaxIdleConns,
}
}
@ -131,11 +131,4 @@ func NewProxyError(errType, message string, code int, cause error) *ProxyError {
}
}
// 预定义错误类型
const (
ErrTypeConfig = "config_error"
ErrTypeConnection = "connection_error"
ErrTypeAuth = "auth_error"
ErrTypeTimeout = "timeout_error"
ErrTypeProtocol = "protocol_error"
)
// 预定义错误类型已迁移到constants.go

179
Common/proxy/constants.go Normal file
View File

@ -0,0 +1,179 @@
package proxy
import (
"time"
)
/*
constants.go - 代理系统常量定义
统一管理common/proxy包中的所有常量便于查看和编辑
*/
// =============================================================================
// 代理类型常量 (从Types.go迁移)
// =============================================================================
const (
// 代理类型字符串
ProxyTypeStringNone = "none"
ProxyTypeStringHTTP = "http"
ProxyTypeStringHTTPS = "https"
ProxyTypeStringSOCKS5 = "socks5"
ProxyTypeStringUnknown = "unknown"
)
// =============================================================================
// 默认配置常量 (从Types.go迁移)
// =============================================================================
const (
// 默认代理配置值
DefaultProxyTimeout = 30 * time.Second // 默认超时时间
DefaultProxyMaxRetries = 3 // 默认最大重试次数
DefaultProxyKeepAlive = 30 * time.Second // 默认保持连接时间
DefaultProxyIdleTimeout = 90 * time.Second // 默认空闲超时时间
DefaultProxyMaxIdleConns = 10 // 默认最大空闲连接数
)
// =============================================================================
// 错误类型常量 (从Types.go迁移)
// =============================================================================
const (
// 预定义错误类型
ErrTypeConfig = "config_error"
ErrTypeConnection = "connection_error"
ErrTypeAuth = "auth_error"
ErrTypeTimeout = "timeout_error"
ErrTypeProtocol = "protocol_error"
)
// =============================================================================
// 缓存管理常量 (从Manager.go迁移)
// =============================================================================
const (
// 缓存配置
DefaultCacheExpiry = 5 * time.Minute // 默认缓存过期时间
)
// =============================================================================
// 错误代码常量 (从Manager.go和其他文件迁移)
// =============================================================================
const (
// Manager错误代码
ErrCodeUnsupportedProxyType = 1001
ErrCodeEmptyConfig = 1002
// SOCKS5错误代码
ErrCodeSOCKS5ParseFailed = 2001
ErrCodeSOCKS5CreateFailed = 2002
// 直连错误代码
ErrCodeDirectConnFailed = 3001
ErrCodeSOCKS5ConnTimeout = 3002
ErrCodeSOCKS5ConnFailed = 3003
// HTTP代理错误代码
ErrCodeHTTPConnFailed = 4001
ErrCodeHTTPSetWriteTimeout = 4002
ErrCodeHTTPSendConnectFail = 4003
ErrCodeHTTPSetReadTimeout = 4004
ErrCodeHTTPReadRespFailed = 4005
ErrCodeHTTPProxyAuthFailed = 4006
// TLS错误代码
ErrCodeTLSTCPConnFailed = 5001
ErrCodeTLSHandshakeFailed = 5002
)
// =============================================================================
// HTTP协议常量 (从HTTPDialer.go迁移)
// =============================================================================
const (
// HTTP响应状态码
HTTPStatusOK = 200
// HTTP协议常量
HTTPVersion = "HTTP/1.1"
HTTPMethodConnect = "CONNECT"
// HTTP头部常量
HTTPHeaderHost = "Host"
HTTPHeaderProxyAuth = "Proxy-Authorization"
HTTPHeaderAuthBasic = "Basic"
)
// =============================================================================
// 网络协议常量 (从各文件迁移)
// =============================================================================
const (
// 网络协议
NetworkTCP = "tcp"
// 代理协议前缀
ProxyProtocolSOCKS5 = "socks5"
// 认证分隔符
AuthSeparator = ":"
)
// =============================================================================
// 错误消息常量
// =============================================================================
const (
// Manager错误消息
ErrMsgUnsupportedProxyType = "不支持的代理类型"
ErrMsgEmptyConfig = "配置不能为空"
// SOCKS5错误消息
ErrMsgSOCKS5ParseFailed = "SOCKS5代理地址解析失败"
ErrMsgSOCKS5CreateFailed = "SOCKS5拨号器创建失败"
ErrMsgSOCKS5ConnTimeout = "SOCKS5连接超时"
ErrMsgSOCKS5ConnFailed = "SOCKS5连接失败"
// 直连错误消息
ErrMsgDirectConnFailed = "直连失败"
// HTTP代理错误消息
ErrMsgHTTPConnFailed = "连接HTTP代理服务器失败"
ErrMsgHTTPSetWriteTimeout = "设置写超时失败"
ErrMsgHTTPSendConnectFail = "发送CONNECT请求失败"
ErrMsgHTTPSetReadTimeout = "设置读超时失败"
ErrMsgHTTPReadRespFailed = "读取HTTP响应失败"
ErrMsgHTTPProxyAuthFailed = "HTTP代理连接失败状态码: %d"
// TLS错误消息
ErrMsgTLSTCPConnFailed = "建立TCP连接失败"
ErrMsgTLSHandshakeFailed = "TLS握手失败"
)
// =============================================================================
// 缓存键前缀常量 (从Manager.go迁移)
// =============================================================================
const (
// 缓存键前缀
CacheKeySOCKS5 = "socks5_%s"
CacheKeyHTTP = "http_%s"
)
// =============================================================================
// 格式化字符串常量 (从各文件迁移)
// =============================================================================
const (
// SOCKS5 URL格式
SOCKS5URLFormat = "socks5://%s"
SOCKS5URLAuthFormat = "socks5://%s:%s@%s"
// HTTP CONNECT请求格式
HTTPConnectRequestFormat = "CONNECT %s HTTP/1.1\r\nHost: %s\r\n"
HTTPAuthHeaderFormat = "Proxy-Authorization: Basic %s\r\n"
HTTPRequestEndFormat = "\r\n"
)