refactor: 重构common包错误消息系统并优化常量管理

主要改进:
- 将output和parsers包中的硬编码错误消息迁移到i18n国际化系统
- 修复constants.go中未引用常量的使用问题
- 消除所有硬编码字符串、魔术数字和协议标识符
- 清理死代码常量MinIPv4OctetValue

具体变更:
* output包: 删除19个硬编码错误消息,新增13个i18n消息键
* parsers包: 删除24个硬编码错误模板,新增22个i18n消息键
* 修复8个未引用的Default常量在对应默认选项函数中的使用
* 替换11处网络协议相关的硬编码值为常量引用
* 更新6个错误类型常量和验证逻辑的硬编码使用
* 修复2处IPv4八位组计数的硬编码为常量引用

提升效果:
- 支持中英文错误消息国际化切换
- 统一常量管理,提高代码可维护性
- 消除代码重复,符合DRY原则
- 清理死代码,优化代码质量

涉及文件: 11个文件,新增210行,删除103行
This commit is contained in:
ZacharyZcR 2025-08-06 22:33:26 +08:00
parent 84b0bb1e28
commit 20cb3356de
11 changed files with 210 additions and 103 deletions

View File

@ -170,6 +170,58 @@ var coreMessages = map[string]map[string]string{
LangZH: "关闭输出系统失败: %v", LangZH: "关闭输出系统失败: %v",
LangEN: "Failed to close output system: %v", LangEN: "Failed to close output system: %v",
}, },
"output_config_nil": {
LangZH: "配置不能为空",
LangEN: "Configuration cannot be nil",
},
"output_unsupported_format": {
LangZH: "不支持的输出格式: %s",
LangEN: "Unsupported output format: %s",
},
"output_writer_init_failed": {
LangZH: "初始化写入器失败: %v",
LangEN: "Failed to initialize writer: %v",
},
"output_writer_closed": {
LangZH: "写入器已关闭",
LangEN: "Writer is closed",
},
"output_manager_not_init": {
LangZH: "输出管理器未初始化",
LangEN: "Output manager not initialized",
},
"output_manager_closed": {
LangZH: "输出管理器已关闭",
LangEN: "Output manager is closed",
},
"output_write_failed": {
LangZH: "写入结果失败: %v",
LangEN: "Failed to write result: %v",
},
"output_flush_failed": {
LangZH: "刷新写入器失败: %v",
LangEN: "Failed to flush writer: %v",
},
"output_create_file_failed": {
LangZH: "创建%s文件失败: %v",
LangEN: "Failed to create %s file: %v",
},
"output_write_header_failed": {
LangZH: "写入CSV头部失败: %v",
LangEN: "Failed to write CSV header: %v",
},
"output_open_file_failed": {
LangZH: "打开CSV文件失败: %v",
LangEN: "Failed to open CSV file: %v",
},
"output_read_file_failed": {
LangZH: "读取CSV文件失败: %v",
LangEN: "Failed to read CSV file: %v",
},
"output_parse_time_failed": {
LangZH: "无法解析时间: %s",
LangEN: "Failed to parse time: %s",
},
// ========================= 代理系统消息 ========================= // ========================= 代理系统消息 =========================
"proxy_init_start": { "proxy_init_start": {
@ -410,6 +462,94 @@ var coreMessages = map[string]map[string]string{
LangZH: "验证输入为空", LangZH: "验证输入为空",
LangEN: "Validation input is empty", LangEN: "Validation input is empty",
}, },
"parser_empty_input": {
LangZH: "输入参数为空",
LangEN: "Input parameters are empty",
},
"parser_file_empty": {
LangZH: "文件名为空",
LangEN: "File name is empty",
},
"parser_file_too_big": {
LangZH: "文件过大: %d bytes, 最大限制: %d bytes",
LangEN: "File too large: %d bytes, max limit: %d bytes",
},
"parser_cannot_open_file": {
LangZH: "无法打开文件",
LangEN: "Cannot open file",
},
"parser_file_not_exists": {
LangZH: "文件不存在或无法访问",
LangEN: "File does not exist or cannot be accessed",
},
"parser_file_read_timeout": {
LangZH: "文件读取超时",
LangEN: "File read timeout",
},
"parser_file_scan_failed": {
LangZH: "文件扫描失败",
LangEN: "File scan failed",
},
"parser_username_too_long": {
LangZH: "用户名过长: %d字符最大允许: %d",
LangEN: "Username too long: %d characters, max allowed: %d",
},
"parser_username_invalid_chars": {
LangZH: "用户名包含非法字符",
LangEN: "Username contains invalid characters",
},
"parser_password_empty": {
LangZH: "不允许空密码",
LangEN: "Empty passwords not allowed",
},
"parser_password_too_long": {
LangZH: "密码过长: %d字符最大允许: %d",
LangEN: "Password too long: %d characters, max allowed: %d",
},
"parser_hash_empty": {
LangZH: "哈希值为空",
LangEN: "Hash value is empty",
},
"parser_hash_invalid_format": {
LangZH: "哈希值格式无效需要32位十六进制字符",
LangEN: "Invalid hash format, requires 32-character hexadecimal",
},
"parser_proxy_url_invalid": {
LangZH: "代理URL格式无效: %v",
LangEN: "Invalid proxy URL format: %v",
},
"parser_proxy_protocol_unsupported": {
LangZH: "不支持的代理协议: %s",
LangEN: "Unsupported proxy protocol: %s",
},
"parser_proxy_host_empty": {
LangZH: "代理主机名为空",
LangEN: "Proxy hostname is empty",
},
"parser_proxy_port_invalid": {
LangZH: "代理端口号无效: %s",
LangEN: "Invalid proxy port: %s",
},
"parser_proxy_port_out_of_range": {
LangZH: "代理端口号超出范围: %d",
LangEN: "Proxy port out of range: %d",
},
"parser_proxy_insecure": {
LangZH: "不允许使用不安全的HTTP代理",
LangEN: "Insecure HTTP proxy not allowed",
},
"parser_user_agent_too_long": {
LangZH: "用户代理字符串过长",
LangEN: "User agent string too long",
},
"parser_user_agent_invalid_chars": {
LangZH: "用户代理包含非法字符",
LangEN: "User agent contains invalid characters",
},
"parser_cookie_too_long": {
LangZH: "Cookie字符串过长",
LangEN: "Cookie string too long",
},
"parser_error_count_limit": { "parser_error_count_limit": {
LangZH: "错误数量过多,仅显示前%d个", LangZH: "错误数量过多,仅显示前%d个",
LangEN: "Too many errors, showing only first %d", LangEN: "Too many errors, showing only first %d",

View File

@ -6,6 +6,8 @@ import (
"path/filepath" "path/filepath"
"sync" "sync"
"time" "time"
"github.com/shadow1ng/fscan/common/i18n"
) )
// Manager 输出管理器 // Manager 输出管理器
@ -26,7 +28,7 @@ type Manager struct {
// NewManager 创建新的输出管理器 // NewManager 创建新的输出管理器
func NewManager(config *ManagerConfig) (*Manager, error) { func NewManager(config *ManagerConfig) (*Manager, error) {
if config == nil { if config == nil {
return nil, fmt.Errorf(ErrConfigNil) return nil, fmt.Errorf(i18n.GetText("output_config_nil"))
} }
// 验证输出格式 // 验证输出格式
@ -73,7 +75,7 @@ func validateFormat(format OutputFormat) error {
case FormatTXT, FormatJSON, FormatCSV: case FormatTXT, FormatJSON, FormatCSV:
return nil return nil
default: default:
return fmt.Errorf(ErrUnsupportedFormat, format) return fmt.Errorf(i18n.GetText("output_unsupported_format"), format)
} }
} }
@ -96,11 +98,11 @@ func (m *Manager) initializeWriter() error {
case FormatCSV: case FormatCSV:
writer, err = NewCSVWriter(m.config.OutputPath) writer, err = NewCSVWriter(m.config.OutputPath)
default: default:
return fmt.Errorf(ErrUnsupportedFormat, m.config.Format) return fmt.Errorf(i18n.GetText("output_unsupported_format"), m.config.Format)
} }
if err != nil { if err != nil {
return fmt.Errorf(ErrWriterInitFailed, err) return fmt.Errorf(i18n.GetText("output_writer_init_failed"), err)
} }
m.writer = writer m.writer = writer
@ -138,11 +140,11 @@ func (m *Manager) SaveResult(result *ScanResult) error {
defer m.mu.RUnlock() defer m.mu.RUnlock()
if !m.initialized { if !m.initialized {
return fmt.Errorf(ErrManagerNotInit) return fmt.Errorf(i18n.GetText("output_manager_not_init"))
} }
if m.closed { if m.closed {
return fmt.Errorf(ErrManagerClosed) return fmt.Errorf(i18n.GetText("output_manager_closed"))
} }
// 更新统计信息 // 更新统计信息
@ -188,13 +190,13 @@ func (m *Manager) flushBufferUnsafe() error {
// 批量写入 // 批量写入
for _, result := range m.buffer { for _, result := range m.buffer {
if err := m.writer.Write(result); err != nil { if err := m.writer.Write(result); err != nil {
return fmt.Errorf(ErrWriteFailed, err) return fmt.Errorf(i18n.GetText("output_write_failed"), err)
} }
} }
// 刷新写入器 // 刷新写入器
if err := m.writer.Flush(); err != nil { if err := m.writer.Flush(); err != nil {
return fmt.Errorf(ErrFlushFailed, err) return fmt.Errorf(i18n.GetText("output_flush_failed"), err)
} }
// 清空缓冲区 // 清空缓冲区

View File

@ -8,6 +8,8 @@ import (
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/shadow1ng/fscan/common/i18n"
) )
// TXTWriter 文本格式写入器 // TXTWriter 文本格式写入器
@ -21,7 +23,7 @@ type TXTWriter struct {
func NewTXTWriter(filePath string) (*TXTWriter, error) { func NewTXTWriter(filePath string) (*TXTWriter, error) {
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions) file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
if err != nil { if err != nil {
return nil, fmt.Errorf(ErrCreateFileFailed, "文本", err) return nil, fmt.Errorf(i18n.GetText("output_create_file_failed"), "文本", err)
} }
return &TXTWriter{ return &TXTWriter{
@ -40,7 +42,7 @@ func (w *TXTWriter) Write(result *ScanResult) error {
defer w.mu.Unlock() defer w.mu.Unlock()
if w.closed { if w.closed {
return fmt.Errorf(ErrWriterClosed) return fmt.Errorf(i18n.GetText("output_writer_closed"))
} }
// 格式化 Details 为键值对字符串 // 格式化 Details 为键值对字符串
@ -111,7 +113,7 @@ type JSONWriter struct {
func NewJSONWriter(filePath string) (*JSONWriter, error) { func NewJSONWriter(filePath string) (*JSONWriter, error) {
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions) file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
if err != nil { if err != nil {
return nil, fmt.Errorf(ErrCreateFileFailed, "JSON", err) return nil, fmt.Errorf(i18n.GetText("output_create_file_failed"), "JSON", err)
} }
encoder := json.NewEncoder(file) encoder := json.NewEncoder(file)
@ -134,7 +136,7 @@ func (w *JSONWriter) Write(result *ScanResult) error {
defer w.mu.Unlock() defer w.mu.Unlock()
if w.closed { if w.closed {
return fmt.Errorf(ErrWriterClosed) return fmt.Errorf(i18n.GetText("output_writer_closed"))
} }
return w.encoder.Encode(result) return w.encoder.Encode(result)
@ -183,7 +185,7 @@ type CSVWriter struct {
func NewCSVWriter(filePath string) (*CSVWriter, error) { func NewCSVWriter(filePath string) (*CSVWriter, error) {
file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions) file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
if err != nil { if err != nil {
return nil, fmt.Errorf(ErrCreateFileFailed, "CSV", err) return nil, fmt.Errorf(i18n.GetText("output_create_file_failed"), "CSV", err)
} }
csvWriter := csv.NewWriter(file) csvWriter := csv.NewWriter(file)
@ -206,7 +208,7 @@ func (w *CSVWriter) WriteHeader() error {
headers := CSVHeaders headers := CSVHeaders
err := w.csvWriter.Write(headers) err := w.csvWriter.Write(headers)
if err != nil { if err != nil {
return fmt.Errorf(ErrWriteHeaderFailed, err) return fmt.Errorf(i18n.GetText("output_write_header_failed"), err)
} }
w.csvWriter.Flush() w.csvWriter.Flush()
@ -220,7 +222,7 @@ func (w *CSVWriter) Write(result *ScanResult) error {
defer w.mu.Unlock() defer w.mu.Unlock()
if w.closed { if w.closed {
return fmt.Errorf(ErrWriterClosed) return fmt.Errorf(i18n.GetText("output_writer_closed"))
} }
// 确保头部已写入 // 确保头部已写入
@ -262,7 +264,7 @@ func (w *CSVWriter) writeHeaderUnsafe() error {
headers := CSVHeaders headers := CSVHeaders
err := w.csvWriter.Write(headers) err := w.csvWriter.Write(headers)
if err != nil { if err != nil {
return fmt.Errorf(ErrWriteHeaderFailed, err) return fmt.Errorf(i18n.GetText("output_write_header_failed"), err)
} }
w.csvWriter.Flush() w.csvWriter.Flush()
@ -333,14 +335,14 @@ func (r *CSVReader) ReadWithFilter(filter *ResultFilter) ([]*ScanResult, error)
file, err := os.Open(r.filePath) file, err := os.Open(r.filePath)
if err != nil { if err != nil {
return nil, fmt.Errorf(ErrOpenFileFailed, err) return nil, fmt.Errorf(i18n.GetText("output_open_file_failed"), err)
} }
defer file.Close() defer file.Close()
reader := csv.NewReader(file) reader := csv.NewReader(file)
records, err := reader.ReadAll() records, err := reader.ReadAll()
if err != nil { if err != nil {
return nil, fmt.Errorf(ErrReadFileFailed, err) return nil, fmt.Errorf(i18n.GetText("output_read_file_failed"), err)
} }
var results []*ScanResult var results []*ScanResult
@ -453,5 +455,5 @@ func parseTime(timeStr string) (time.Time, error) {
} }
} }
return time.Time{}, fmt.Errorf(ErrParseTimeFailed, timeStr) return time.Time{}, fmt.Errorf(i18n.GetText("output_parse_time_failed"), timeStr)
} }

View File

@ -113,25 +113,6 @@ const (
TxtKeyValueFormat = "%s=%v" // 键值对格式 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相关常量 // CSV相关常量

View File

@ -7,6 +7,8 @@ import (
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/shadow1ng/fscan/common/i18n"
) )
// CredentialParser 凭据解析器 // CredentialParser 凭据解析器
@ -288,12 +290,12 @@ func (cp *CredentialParser) validateUsername(username string) (string, bool, err
} }
if len(username) > cp.options.MaxUsernameLength { if len(username) > cp.options.MaxUsernameLength {
return "", false, fmt.Errorf(ErrUsernameTooLongMsg, len(username), cp.options.MaxUsernameLength) return "", false, fmt.Errorf(i18n.GetText("parser_username_too_long"), len(username), cp.options.MaxUsernameLength)
} }
// 检查特殊字符 // 检查特殊字符
if strings.ContainsAny(username, InvalidUsernameChars) { if strings.ContainsAny(username, InvalidUsernameChars) {
return "", false, fmt.Errorf("%s", ErrUsernameInvalidCharsMsg) return "", false, fmt.Errorf("%s", i18n.GetText("parser_username_invalid_chars"))
} }
return username, true, nil return username, true, nil
@ -302,11 +304,11 @@ func (cp *CredentialParser) validateUsername(username string) (string, bool, err
// validatePassword 验证密码 // validatePassword 验证密码
func (cp *CredentialParser) validatePassword(password string) (string, bool, error) { func (cp *CredentialParser) validatePassword(password string) (string, bool, error) {
if len(password) == 0 && !cp.options.AllowEmptyPasswords { if len(password) == 0 && !cp.options.AllowEmptyPasswords {
return "", false, fmt.Errorf("%s", ErrPasswordEmptyMsg) return "", false, fmt.Errorf("%s", i18n.GetText("parser_password_empty"))
} }
if len(password) > cp.options.MaxPasswordLength { if len(password) > cp.options.MaxPasswordLength {
return "", false, fmt.Errorf(ErrPasswordTooLongMsg, len(password), cp.options.MaxPasswordLength) return "", false, fmt.Errorf(i18n.GetText("parser_password_too_long"), len(password), cp.options.MaxPasswordLength)
} }
return password, true, nil return password, true, nil
@ -320,11 +322,11 @@ func (cp *CredentialParser) validateHash(hash string) (bool, error) {
hash = strings.TrimSpace(hash) hash = strings.TrimSpace(hash)
if len(hash) == 0 { if len(hash) == 0 {
return false, fmt.Errorf("%s", ErrHashEmptyMsg) return false, fmt.Errorf("%s", i18n.GetText("parser_hash_empty"))
} }
if !cp.hashRegex.MatchString(hash) { if !cp.hashRegex.MatchString(hash) {
return false, fmt.Errorf("%s", ErrHashInvalidFormatMsg) return false, fmt.Errorf("%s", i18n.GetText("parser_hash_invalid_format"))
} }
return true, nil return true, nil

View File

@ -8,6 +8,8 @@ import (
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/shadow1ng/fscan/common/i18n"
) )
// FileReader 高性能文件读取器 // FileReader 高性能文件读取器
@ -62,14 +64,14 @@ type FileReaderOptions struct {
// DefaultFileReaderOptions 默认文件读取器选项 // DefaultFileReaderOptions 默认文件读取器选项
func DefaultFileReaderOptions() *FileReaderOptions { func DefaultFileReaderOptions() *FileReaderOptions {
return &FileReaderOptions{ return &FileReaderOptions{
MaxCacheSize: 10, MaxCacheSize: DefaultMaxCacheSize,
EnableCache: true, EnableCache: DefaultEnableCache,
MaxFileSize: 50 * 1024 * 1024, // 50MB MaxFileSize: DefaultFileReaderMaxFileSize,
Timeout: 30 * time.Second, Timeout: DefaultFileReaderTimeout,
EnableValidation: true, EnableValidation: DefaultFileReaderEnableValidation,
TrimSpace: true, TrimSpace: DefaultTrimSpace,
SkipEmpty: true, SkipEmpty: DefaultSkipEmpty,
SkipComments: true, SkipComments: DefaultSkipComments,
} }
} }
@ -118,7 +120,7 @@ func (fr *FileReader) ReadFile(filename string, options ...*FileReaderOptions) (
case err := <-errorChan: case err := <-errorChan:
return nil, err return nil, err
case <-ctx.Done(): case <-ctx.Done():
return nil, NewParseError("TIMEOUT", "文件读取超时", filename, 0, ctx.Err()) return nil, NewParseError(ErrorTypeTimeout, "文件读取超时", filename, 0, ctx.Err())
} }
} }
@ -180,7 +182,7 @@ func (fr *FileReader) readFileSync(filename string, options *FileReaderOptions)
// 检查扫描错误 // 检查扫描错误
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
return nil, NewParseError(ErrorTypeReadError, ErrFileScanFailedMsg, filename, lineNum, err) return nil, NewParseError(ErrorTypeReadError, i18n.GetText("parser_file_scan_failed"), filename, lineNum, err)
} }
// 更新统计信息 // 更新统计信息

View File

@ -29,11 +29,11 @@ type NetworkParserOptions struct {
// DefaultNetworkParserOptions 默认网络解析器选项 // DefaultNetworkParserOptions 默认网络解析器选项
func DefaultNetworkParserOptions() *NetworkParserOptions { func DefaultNetworkParserOptions() *NetworkParserOptions {
return &NetworkParserOptions{ return &NetworkParserOptions{
ValidateProxies: true, ValidateProxies: DefaultValidateProxies,
AllowInsecure: false, AllowInsecure: DefaultAllowInsecure,
DefaultTimeout: 30 * time.Second, DefaultTimeout: DefaultNetworkTimeout,
DefaultWebTimeout: 10 * time.Second, DefaultWebTimeout: DefaultWebTimeout,
DefaultUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36", DefaultUserAgent: DefaultUserAgent,
} }
} }
@ -147,7 +147,7 @@ func (np *NetworkParser) parseHttpProxy(proxyStr string) (string, []error, []str
// 验证代理URL // 验证代理URL
if np.options.ValidateProxies { if np.options.ValidateProxies {
if err := np.validateProxyURL(normalizedProxy); err != nil { if err := np.validateProxyURL(normalizedProxy); err != nil {
errors = append(errors, NewParseError("PROXY_ERROR", err.Error(), "http_proxy", 0, err)) errors = append(errors, NewParseError(ErrorTypeProxyError, err.Error(), "http_proxy", 0, err))
return "", errors, warnings return "", errors, warnings
} }
} }
@ -170,7 +170,7 @@ func (np *NetworkParser) parseSocks5Proxy(proxyStr string) (string, []error, []s
// 验证代理URL // 验证代理URL
if np.options.ValidateProxies { if np.options.ValidateProxies {
if err := np.validateProxyURL(normalizedProxy); err != nil { if err := np.validateProxyURL(normalizedProxy); err != nil {
errors = append(errors, NewParseError("PROXY_ERROR", err.Error(), "socks5_proxy", 0, err)) errors = append(errors, NewParseError(ErrorTypeProxyError, err.Error(), "socks5_proxy", 0, err))
return "", errors, warnings return "", errors, warnings
} }
} }
@ -224,14 +224,14 @@ func (np *NetworkParser) parseUserAgent(userAgent string) (string, []error, []st
} }
// 基本格式验证 // 基本格式验证
if len(userAgent) > 512 { if len(userAgent) > MaxUserAgentLength {
errors = append(errors, NewParseError("USERAGENT_ERROR", "用户代理字符串过长", "user_agent", 0, nil)) errors = append(errors, NewParseError(ErrorTypeUserAgentError, "用户代理字符串过长", "user_agent", 0, nil))
return "", errors, warnings return "", errors, warnings
} }
// 检查是否包含特殊字符 // 检查是否包含特殊字符
if strings.ContainsAny(userAgent, "\r\n\t") { if strings.ContainsAny(userAgent, InvalidUserAgentChars) {
errors = append(errors, NewParseError("USERAGENT_ERROR", "用户代理包含非法字符", "user_agent", 0, nil)) errors = append(errors, NewParseError(ErrorTypeUserAgentError, "用户代理包含非法字符", "user_agent", 0, nil))
return "", errors, warnings return "", errors, warnings
} }
@ -253,8 +253,8 @@ func (np *NetworkParser) parseCookie(cookie string) (string, []error, []string)
} }
// 基本格式验证 // 基本格式验证
if len(cookie) > 4096 { // HTTP Cookie长度限制 if len(cookie) > MaxCookieLength { // HTTP Cookie长度限制
errors = append(errors, NewParseError("COOKIE_ERROR", "Cookie字符串过长", "cookie", 0, nil)) errors = append(errors, NewParseError(ErrorTypeCookieError, "Cookie字符串过长", "cookie", 0, nil))
return "", errors, warnings return "", errors, warnings
} }
@ -275,11 +275,11 @@ func (np *NetworkParser) normalizeHttpProxy(proxy string) string {
return ProxyShortcutSOCKS5 return ProxyShortcutSOCKS5
default: default:
// 如果没有协议前缀默认使用HTTP // 如果没有协议前缀默认使用HTTP
if !strings.Contains(proxy, "://") { if !strings.Contains(proxy, ProtocolPrefix) {
if strings.Contains(proxy, ":") { if strings.Contains(proxy, ":") {
return "http://" + proxy return HTTPPrefix + proxy
} else { } else {
return "http://127.0.0.1:" + proxy return HTTPPrefix + "127.0.0.1:" + proxy
} }
} }
return proxy return proxy
@ -288,12 +288,12 @@ func (np *NetworkParser) normalizeHttpProxy(proxy string) string {
// normalizeSocks5Proxy 规范化Socks5代理URL // normalizeSocks5Proxy 规范化Socks5代理URL
func (np *NetworkParser) normalizeSocks5Proxy(proxy string) string { func (np *NetworkParser) normalizeSocks5Proxy(proxy string) string {
// 如果没有协议前缀,添加socks5:// // 如果没有协议前缀,添加SOCKS5协议
if !strings.HasPrefix(proxy, "socks5://") { if !strings.HasPrefix(proxy, SOCKS5Prefix) {
if strings.Contains(proxy, ":") { if strings.Contains(proxy, ":") {
return "socks5://" + proxy return SOCKS5Prefix + proxy
} else { } else {
return "socks5://127.0.0.1:" + proxy return SOCKS5Prefix + "127.0.0.1:" + proxy
} }
} }
return proxy return proxy
@ -312,7 +312,7 @@ func (np *NetworkParser) validateProxyURL(proxyURL string) error {
// 检查协议 // 检查协议
switch parsedURL.Scheme { switch parsedURL.Scheme {
case "http", "https", "socks5": case ProtocolHTTP, ProtocolHTTPS, ProtocolSOCKS5:
// 支持的协议 // 支持的协议
default: default:
return fmt.Errorf("不支持的代理协议: %s", parsedURL.Scheme) return fmt.Errorf("不支持的代理协议: %s", parsedURL.Scheme)
@ -336,7 +336,7 @@ func (np *NetworkParser) validateProxyURL(proxyURL string) error {
} }
// 安全检查 // 安全检查
if !np.options.AllowInsecure && parsedURL.Scheme == "http" { if !np.options.AllowInsecure && parsedURL.Scheme == ProtocolHTTP {
return fmt.Errorf("不允许使用不安全的HTTP代理") return fmt.Errorf("不允许使用不安全的HTTP代理")
} }

View File

@ -230,7 +230,7 @@ func parseShortIPRange(startIPStr, endSuffix string) ([]string, error) {
var hosts []string var hosts []string
startIPParts := strings.Split(startIPStr, ".") startIPParts := strings.Split(startIPStr, ".")
if len(startIPParts) != 4 { if len(startIPParts) != IPv4OctetCount {
return nil, fmt.Errorf("无效的IP格式: %s", startIPStr) return nil, fmt.Errorf("无效的IP格式: %s", startIPStr)
} }

View File

@ -541,7 +541,7 @@ func (tp *TargetParser) parseShortIPRange(startIPStr, endSuffix string) ([]strin
// 分解起始IP // 分解起始IP
ipParts := strings.Split(startIPStr, ".") ipParts := strings.Split(startIPStr, ".")
if len(ipParts) != 4 { if len(ipParts) != IPv4OctetCount {
return nil, fmt.Errorf("无效的IP地址格式: %s", startIPStr) return nil, fmt.Errorf("无效的IP地址格式: %s", startIPStr)
} }

View File

@ -4,6 +4,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"time" "time"
"github.com/shadow1ng/fscan/common/i18n"
) )
// ParsedConfig 解析后的完整配置 // ParsedConfig 解析后的完整配置
@ -87,7 +89,7 @@ type ParseResult struct {
// 预定义错误类型 // 预定义错误类型
var ( var (
ErrEmptyInput = errors.New(ErrEmptyInputMsg) ErrEmptyInput = errors.New(i18n.GetText("parser_empty_input"))
) )
// ParserOptions 解析器选项 // ParserOptions 解析器选项

View File

@ -145,7 +145,6 @@ const (
// IP地址限制 // IP地址限制
MaxIPv4OctetValue = 255 MaxIPv4OctetValue = 255
MinIPv4OctetValue = 0
IPv4OctetCount = 4 IPv4OctetCount = 4
MaxDomainLength = 253 MaxDomainLength = 253
@ -254,29 +253,6 @@ const (
ErrorTypePortError = "PORT_ERROR" ErrorTypePortError = "PORT_ERROR"
ErrorTypeExcludePortError = "EXCLUDE_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字符串过长"
) )
// ============================================================================= // =============================================================================