mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00
refactor: 完成common包常量提取和代码重构优化
- 新增constants.go文件统一管理各包常量定义 - 提取logging、output、parsers、proxy包中的硬编码值 - 将30+个魔法数字替换为语义化常量 - 统一错误代码和消息格式 - 清理死代码和未使用变量 - 优化代码可维护性和可读性 - 保持完全向后兼容性 涉及包: - common/logging: 日志级别和格式常量 - common/output: 输出配置和格式常量 - common/parsers: 解析器配置和验证常量 - common/proxy: 代理协议和错误常量
This commit is contained in:
parent
2f9c213e80
commit
84b0bb1e28
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 // 读写互斥锁
|
||||
|
86
Common/logging/constants.go
Normal file
86
Common/logging/constants.go
Normal 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, // 调试日志显示白色
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
// 清空缓冲区
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
148
Common/output/constants.go
Normal 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 // 详细信息列索引
|
||||
)
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
// =============================================================================================
|
||||
|
@ -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 {
|
||||
|
@ -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 排除指定主机
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
304
Common/parsers/constants.go
Normal 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)
|
||||
}
|
@ -1,17 +1,12 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
globalManager ProxyManager
|
||||
globalMutex sync.RWMutex
|
||||
once sync.Once
|
||||
)
|
||||
// 已清理未使用的导入和全局变量
|
||||
|
||||
// =============================================================================================
|
||||
// 已删除的死代码(未使用):
|
||||
// - globalManager: 全局代理管理器变量
|
||||
// - globalMutex: 全局互斥锁
|
||||
// - once: 全局初始化once变量
|
||||
// - InitGlobalProxy: 初始化全局代理管理器
|
||||
// - GetGlobalProxy: 获取全局代理管理器
|
||||
// - UpdateGlobalProxyConfig: 更新全局代理配置
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,让上层代码管理超时
|
||||
|
@ -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
179
Common/proxy/constants.go
Normal 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"
|
||||
)
|
Loading…
Reference in New Issue
Block a user