diff --git a/Common/Parse.go b/Common/Parse.go index 2118217..16c6add 100644 --- a/Common/Parse.go +++ b/Common/Parse.go @@ -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) diff --git a/Common/logging/Formatter.go b/Common/logging/Formatter.go index cab2580..73a141b 100644 --- a/Common/logging/Formatter.go +++ b/Common/logging/Formatter.go @@ -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 } } diff --git a/Common/logging/Logger.go b/Common/logging/Logger.go index dc474ba..3c3510f 100644 --- a/Common/logging/Logger.go +++ b/Common/logging/Logger.go @@ -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) } } diff --git a/Common/logging/Types.go b/Common/logging/Types.go index 21ef77a..3800a0f 100644 --- a/Common/logging/Types.go +++ b/Common/logging/Types.go @@ -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 // 读写互斥锁 diff --git a/Common/logging/constants.go b/Common/logging/constants.go new file mode 100644 index 0000000..84b3dc2 --- /dev/null +++ b/Common/logging/constants.go @@ -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, // 调试日志显示白色 + } +} \ No newline at end of file diff --git a/Common/output/Manager.go b/Common/output/Manager.go index c6ecec1..25024b2 100644 --- a/Common/output/Manager.go +++ b/Common/output/Manager.go @@ -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) } // 清空缓冲区 diff --git a/Common/output/Types.go b/Common/output/Types.go index cd95eea..612d977 100644 --- a/Common/output/Types.go +++ b/Common/output/Types.go @@ -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, } } diff --git a/Common/output/Writers.go b/Common/output/Writers.go index 34f8096..3405012 100644 --- a/Common/output/Writers.go +++ b/Common/output/Writers.go @@ -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) } \ No newline at end of file diff --git a/Common/output/constants.go b/Common/output/constants.go new file mode 100644 index 0000000..d5e6b6c --- /dev/null +++ b/Common/output/constants.go @@ -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 // 详细信息列索引 +) \ No newline at end of file diff --git a/Common/parsers/CredentialParser.go b/Common/parsers/CredentialParser.go index b47af8f..dd2f72e 100644 --- a/Common/parsers/CredentialParser.go +++ b/Common/parsers/CredentialParser.go @@ -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 diff --git a/Common/parsers/FileReader.go b/Common/parsers/FileReader.go index a8c5529..1e1a762 100644 --- a/Common/parsers/FileReader.go +++ b/Common/parsers/FileReader.go @@ -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 } } diff --git a/Common/parsers/NetworkParser.go b/Common/parsers/NetworkParser.go index 7687abf..45c71b4 100644 --- a/Common/parsers/NetworkParser.go +++ b/Common/parsers/NetworkParser.go @@ -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)) } // ============================================================================================= diff --git a/Common/parsers/Simple.go b/Common/parsers/Simple.go index 6bbb22d..f093493 100644 --- a/Common/parsers/Simple.go +++ b/Common/parsers/Simple.go @@ -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 { diff --git a/Common/parsers/TargetParser.go b/Common/parsers/TargetParser.go index cc3c58e..a12ab33 100644 --- a/Common/parsers/TargetParser.go +++ b/Common/parsers/TargetParser.go @@ -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 排除指定主机 diff --git a/Common/parsers/Types.go b/Common/parsers/Types.go index 0d452e7..e54708b 100644 --- a/Common/parsers/Types.go +++ b/Common/parsers/Types.go @@ -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, } } diff --git a/Common/parsers/ValidationParser.go b/Common/parsers/ValidationParser.go index ec1e849..6ef3c68 100644 --- a/Common/parsers/ValidationParser.go +++ b/Common/parsers/ValidationParser.go @@ -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, "超时过长") } } diff --git a/Common/parsers/constants.go b/Common/parsers/constants.go new file mode 100644 index 0000000..eb5e3a2 --- /dev/null +++ b/Common/parsers/constants.go @@ -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) +} \ No newline at end of file diff --git a/Common/proxy/Global.go b/Common/proxy/Global.go index 515672d..9c581ff 100644 --- a/Common/proxy/Global.go +++ b/Common/proxy/Global.go @@ -1,17 +1,12 @@ package proxy -import ( - "sync" -) - -var ( - globalManager ProxyManager - globalMutex sync.RWMutex - once sync.Once -) +// 已清理未使用的导入和全局变量 // ============================================================================================= // 已删除的死代码(未使用): +// - globalManager: 全局代理管理器变量 +// - globalMutex: 全局互斥锁 +// - once: 全局初始化once变量 // - InitGlobalProxy: 初始化全局代理管理器 // - GetGlobalProxy: 获取全局代理管理器 // - UpdateGlobalProxyConfig: 更新全局代理配置 diff --git a/Common/proxy/HTTPDialer.go b/Common/proxy/HTTPDialer.go index d66c272..8239e7c 100644 --- a/Common/proxy/HTTPDialer.go +++ b/Common/proxy/HTTPDialer.go @@ -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 diff --git a/Common/proxy/Manager.go b/Common/proxy/Manager.go index 2008a7a..d9f82a1 100644 --- a/Common/proxy/Manager.go +++ b/Common/proxy/Manager.go @@ -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) diff --git a/Common/proxy/TLSDialer.go b/Common/proxy/TLSDialer.go index 830c3a1..ae119bd 100644 --- a/Common/proxy/TLSDialer.go +++ b/Common/proxy/TLSDialer.go @@ -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,让上层代码管理超时 diff --git a/Common/proxy/Types.go b/Common/proxy/Types.go index ae4c02d..75f2c55 100644 --- a/Common/proxy/Types.go +++ b/Common/proxy/Types.go @@ -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" -) \ No newline at end of file +// 预定义错误类型已迁移到constants.go \ No newline at end of file diff --git a/Common/proxy/constants.go b/Common/proxy/constants.go new file mode 100644 index 0000000..6b60a9d --- /dev/null +++ b/Common/proxy/constants.go @@ -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" +) \ No newline at end of file