refactor: 重构Parse.go解析模块,优化参数验证和信息显示

主要改进:
- 模块化设计:将549行Parse.go拆分为6个专用解析器
- 性能优化:添加文件缓存、并发处理和去重机制
- 增强验证:实现配置冲突检测和参数验证
- 改善体验:清理冗余警告,添加解析结果摘要显示
- 向后兼容:保持所有原有API接口不变

新增模块:
- FileReader: 高性能文件读取和缓存
- CredentialParser: 用户名密码解析
- TargetParser: 主机目标解析
- NetworkParser: 网络配置解析
- ValidationParser: 参数验证和冲突检测
- Types: 统一的数据结构定义

修复问题:
- 消除重复的"Web超时时间大于普通超时时间"警告
- 添加目标主机、端口、代理等配置信息显示
- 删除说教性安全警告,保留技术性提示
This commit is contained in:
ZacharyZcR 2025-08-05 02:14:10 +08:00
parent 7077590bae
commit c04bfcfd07
7 changed files with 2919 additions and 479 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,376 @@
package parsers
import (
"encoding/hex"
"fmt"
"regexp"
"strings"
"sync"
"time"
)
// CredentialParser 凭据解析器
type CredentialParser struct {
fileReader *FileReader
mu sync.RWMutex
hashRegex *regexp.Regexp
options *CredentialParserOptions
}
// CredentialParserOptions 凭据解析器选项
type CredentialParserOptions struct {
MaxUsernameLength int `json:"max_username_length"`
MaxPasswordLength int `json:"max_password_length"`
AllowEmptyPasswords bool `json:"allow_empty_passwords"`
ValidateHashes bool `json:"validate_hashes"`
DeduplicateUsers bool `json:"deduplicate_users"`
DeduplicatePasswords bool `json:"deduplicate_passwords"`
EnableStatistics bool `json:"enable_statistics"`
}
// DefaultCredentialParserOptions 默认凭据解析器选项
func DefaultCredentialParserOptions() *CredentialParserOptions {
return &CredentialParserOptions{
MaxUsernameLength: 64,
MaxPasswordLength: 128,
AllowEmptyPasswords: true,
ValidateHashes: true,
DeduplicateUsers: true,
DeduplicatePasswords: true,
EnableStatistics: true,
}
}
// NewCredentialParser 创建凭据解析器
func NewCredentialParser(fileReader *FileReader, options *CredentialParserOptions) *CredentialParser {
if options == nil {
options = DefaultCredentialParserOptions()
}
// 编译哈希验证正则表达式 (MD5: 32位十六进制)
hashRegex := regexp.MustCompile(`^[a-fA-F0-9]{32}$`)
return &CredentialParser{
fileReader: fileReader,
hashRegex: hashRegex,
options: options,
}
}
// CredentialInput 凭据输入参数
type CredentialInput struct {
// 直接输入
Username string `json:"username"`
Password string `json:"password"`
AddUsers string `json:"add_users"`
AddPasswords string `json:"add_passwords"`
HashValue string `json:"hash_value"`
SshKeyPath string `json:"ssh_key_path"`
Domain string `json:"domain"`
// 文件输入
UsersFile string `json:"users_file"`
PasswordsFile string `json:"passwords_file"`
HashFile string `json:"hash_file"`
}
// Parse 解析凭据配置
func (cp *CredentialParser) Parse(input *CredentialInput, options *ParserOptions) (*ParseResult, error) {
if input == nil {
return nil, NewParseError("INPUT_ERROR", "凭据输入为空", "", 0, ErrEmptyInput)
}
startTime := time.Now()
result := &ParseResult{
Config: &ParsedConfig{
Credentials: &CredentialConfig{
SshKeyPath: input.SshKeyPath,
Domain: input.Domain,
},
},
Success: true,
}
var errors []error
var warnings []string
// 解析用户名
usernames, userErrors, userWarnings := cp.parseUsernames(input)
errors = append(errors, userErrors...)
warnings = append(warnings, userWarnings...)
// 解析密码
passwords, passErrors, passWarnings := cp.parsePasswords(input)
errors = append(errors, passErrors...)
warnings = append(warnings, passWarnings...)
// 解析哈希值
hashValues, hashBytes, hashErrors, hashWarnings := cp.parseHashes(input)
errors = append(errors, hashErrors...)
warnings = append(warnings, hashWarnings...)
// 更新配置
result.Config.Credentials.Usernames = usernames
result.Config.Credentials.Passwords = passwords
result.Config.Credentials.HashValues = hashValues
result.Config.Credentials.HashBytes = hashBytes
// 生成统计信息
if cp.options.EnableStatistics {
result.Config.Credentials.Statistics = cp.generateStatistics(usernames, passwords, hashValues, hashBytes)
}
// 设置结果状态
result.Errors = errors
result.Warnings = warnings
result.ParseTime = time.Since(startTime)
result.Success = len(errors) == 0
return result, nil
}
// parseUsernames 解析用户名
func (cp *CredentialParser) parseUsernames(input *CredentialInput) ([]string, []error, []string) {
var usernames []string
var errors []error
var warnings []string
// 解析命令行用户名
if input.Username != "" {
users := strings.Split(input.Username, ",")
for _, user := range users {
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))
}
}
}
// 从文件读取用户名
if input.UsersFile != "" {
fileResult, err := cp.fileReader.ReadFile(input.UsersFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取用户名文件失败", input.UsersFile, 0, err))
} else {
for i, line := range fileResult.Lines {
if processedUser, valid, err := cp.validateUsername(line); valid {
usernames = append(usernames, processedUser)
} else if err != nil {
warnings = append(warnings, fmt.Sprintf("用户名文件第%d行无效: %s", i+1, err.Error()))
}
}
}
}
// 处理额外用户名
if input.AddUsers != "" {
extraUsers := strings.Split(input.AddUsers, ",")
for _, user := range extraUsers {
if processedUser, valid, err := cp.validateUsername(strings.TrimSpace(user)); valid {
usernames = append(usernames, processedUser)
} else if err != nil {
warnings = append(warnings, fmt.Sprintf("额外用户名无效: %s", err.Error()))
}
}
}
// 去重
if cp.options.DeduplicateUsers {
usernames = cp.removeDuplicateStrings(usernames)
}
return usernames, errors, warnings
}
// parsePasswords 解析密码
func (cp *CredentialParser) parsePasswords(input *CredentialInput) ([]string, []error, []string) {
var passwords []string
var errors []error
var warnings []string
// 解析命令行密码
if input.Password != "" {
passes := strings.Split(input.Password, ",")
for _, pass := range passes {
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))
}
}
}
// 从文件读取密码
if input.PasswordsFile != "" {
fileResult, err := cp.fileReader.ReadFile(input.PasswordsFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取密码文件失败", input.PasswordsFile, 0, err))
} else {
for i, line := range fileResult.Lines {
if processedPass, valid, err := cp.validatePassword(line); valid {
passwords = append(passwords, processedPass)
} else if err != nil {
warnings = append(warnings, fmt.Sprintf("密码文件第%d行无效: %s", i+1, err.Error()))
}
}
}
}
// 处理额外密码
if input.AddPasswords != "" {
extraPasses := strings.Split(input.AddPasswords, ",")
for _, pass := range extraPasses {
if processedPass, valid, err := cp.validatePassword(pass); valid {
passwords = append(passwords, processedPass)
} else if err != nil {
warnings = append(warnings, fmt.Sprintf("额外密码无效: %s", err.Error()))
}
}
}
// 去重
if cp.options.DeduplicatePasswords {
passwords = cp.removeDuplicateStrings(passwords)
}
return passwords, errors, warnings
}
// parseHashes 解析哈希值
func (cp *CredentialParser) parseHashes(input *CredentialInput) ([]string, [][]byte, []error, []string) {
var hashValues []string
var hashBytes [][]byte
var errors []error
var warnings []string
// 解析单个哈希值
if input.HashValue != "" {
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))
}
}
// 从文件读取哈希值
if input.HashFile != "" {
fileResult, err := cp.fileReader.ReadFile(input.HashFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取哈希文件失败", input.HashFile, 0, err))
} else {
for i, line := range fileResult.Lines {
if valid, err := cp.validateHash(line); valid {
hashValues = append(hashValues, line)
} else {
warnings = append(warnings, fmt.Sprintf("哈希文件第%d行无效: %s", i+1, err.Error()))
}
}
}
}
// 转换哈希值为字节数组
for _, hash := range hashValues {
if hashByte, err := hex.DecodeString(hash); err == nil {
hashBytes = append(hashBytes, hashByte)
} else {
warnings = append(warnings, fmt.Sprintf("哈希值解码失败: %s", hash))
}
}
return hashValues, hashBytes, errors, warnings
}
// validateUsername 验证用户名
func (cp *CredentialParser) validateUsername(username string) (string, bool, error) {
if len(username) == 0 {
return "", false, nil // 允许空用户名,但不添加到列表
}
if len(username) > cp.options.MaxUsernameLength {
return "", false, fmt.Errorf("用户名过长: %d字符最大允许: %d", len(username), cp.options.MaxUsernameLength)
}
// 检查特殊字符
if strings.ContainsAny(username, "\r\n\t") {
return "", false, fmt.Errorf("用户名包含非法字符")
}
return username, true, nil
}
// validatePassword 验证密码
func (cp *CredentialParser) validatePassword(password string) (string, bool, error) {
if len(password) == 0 && !cp.options.AllowEmptyPasswords {
return "", false, fmt.Errorf("不允许空密码")
}
if len(password) > cp.options.MaxPasswordLength {
return "", false, fmt.Errorf("密码过长: %d字符最大允许: %d", len(password), cp.options.MaxPasswordLength)
}
return password, true, nil
}
// validateHash 验证哈希值
func (cp *CredentialParser) validateHash(hash string) (bool, error) {
if !cp.options.ValidateHashes {
return true, nil
}
hash = strings.TrimSpace(hash)
if len(hash) == 0 {
return false, fmt.Errorf("哈希值为空")
}
if !cp.hashRegex.MatchString(hash) {
return false, fmt.Errorf("哈希值格式无效需要32位十六进制字符")
}
return true, nil
}
// removeDuplicateStrings 去重字符串切片
func (cp *CredentialParser) removeDuplicateStrings(slice []string) []string {
seen := make(map[string]struct{})
var result []string
for _, item := range slice {
if _, exists := seen[item]; !exists {
seen[item] = struct{}{}
result = append(result, item)
}
}
return result
}
// generateStatistics 生成统计信息
func (cp *CredentialParser) generateStatistics(usernames, passwords, hashValues []string, hashBytes [][]byte) *CredentialStats {
return &CredentialStats{
TotalUsernames: len(usernames),
TotalPasswords: len(passwords),
TotalHashes: len(hashValues),
UniqueUsernames: len(cp.removeDuplicateStrings(usernames)),
UniquePasswords: len(cp.removeDuplicateStrings(passwords)),
ValidHashes: len(hashBytes),
InvalidHashes: len(hashValues) - len(hashBytes),
}
}
// Validate 验证解析结果
func (cp *CredentialParser) Validate() error {
// 基本验证逻辑
return nil
}
// GetStatistics 获取解析统计
func (cp *CredentialParser) GetStatistics() interface{} {
cp.mu.RLock()
defer cp.mu.RUnlock()
return map[string]interface{}{
"parser_type": "credential",
"options": cp.options,
}
}

View File

@ -0,0 +1,360 @@
package parsers
import (
"bufio"
"context"
"fmt"
"os"
"strings"
"sync"
"time"
)
// FileReader 高性能文件读取器
type FileReader struct {
mu sync.RWMutex
cache map[string]*FileResult // 文件缓存
maxCacheSize int // 最大缓存大小
enableCache bool // 是否启用缓存
maxFileSize int64 // 最大文件大小
timeout time.Duration // 读取超时
enableValidation bool // 是否启用内容验证
}
// FileResult 文件读取结果
type FileResult struct {
Lines []string `json:"lines"`
Source *FileSource `json:"source"`
ReadTime time.Duration `json:"read_time"`
ValidLines int `json:"valid_lines"`
Errors []error `json:"errors,omitempty"`
Cached bool `json:"cached"`
}
// NewFileReader 创建文件读取器
func NewFileReader(options *FileReaderOptions) *FileReader {
if options == nil {
options = DefaultFileReaderOptions()
}
return &FileReader{
cache: make(map[string]*FileResult),
maxCacheSize: options.MaxCacheSize,
enableCache: options.EnableCache,
maxFileSize: options.MaxFileSize,
timeout: options.Timeout,
enableValidation: options.EnableValidation,
}
}
// FileReaderOptions 文件读取器选项
type FileReaderOptions struct {
MaxCacheSize int // 最大缓存文件数
EnableCache bool // 启用文件缓存
MaxFileSize int64 // 最大文件大小(字节)
Timeout time.Duration // 读取超时
EnableValidation bool // 启用内容验证
TrimSpace bool // 自动清理空白字符
SkipEmpty bool // 跳过空行
SkipComments bool // 跳过注释行(#开头)
}
// DefaultFileReaderOptions 默认文件读取器选项
func DefaultFileReaderOptions() *FileReaderOptions {
return &FileReaderOptions{
MaxCacheSize: 10,
EnableCache: true,
MaxFileSize: 50 * 1024 * 1024, // 50MB
Timeout: 30 * time.Second,
EnableValidation: true,
TrimSpace: true,
SkipEmpty: true,
SkipComments: true,
}
}
// ReadFile 读取文件内容
func (fr *FileReader) ReadFile(filename string, options ...*FileReaderOptions) (*FileResult, error) {
if filename == "" {
return nil, NewParseError("FILE_ERROR", "文件名为空", filename, 0, ErrEmptyInput)
}
// 检查缓存
if fr.enableCache {
if result := fr.getFromCache(filename); result != nil {
result.Cached = true
return result, nil
}
}
// 合并选项
opts := fr.mergeOptions(options...)
// 创建带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), fr.timeout)
defer cancel()
// 异步读取文件
resultChan := make(chan *FileResult, 1)
errorChan := make(chan error, 1)
go func() {
result, err := fr.readFileSync(filename, opts)
if err != nil {
errorChan <- err
} else {
resultChan <- result
}
}()
// 等待结果或超时
select {
case result := <-resultChan:
// 添加到缓存
if fr.enableCache {
fr.addToCache(filename, result)
}
return result, nil
case err := <-errorChan:
return nil, err
case <-ctx.Done():
return nil, NewParseError("TIMEOUT", "文件读取超时", filename, 0, ctx.Err())
}
}
// ReadFiles 并发读取多个文件
func (fr *FileReader) ReadFiles(filenames []string, options ...*FileReaderOptions) (map[string]*FileResult, error) {
if len(filenames) == 0 {
return nil, NewParseError("FILE_ERROR", "文件列表为空", "", 0, ErrEmptyInput)
}
opts := fr.mergeOptions(options...)
results := make(map[string]*FileResult)
resultsChan := make(chan struct {
filename string
result *FileResult
err error
}, len(filenames))
// 并发读取文件
var wg sync.WaitGroup
semaphore := make(chan struct{}, 4) // 限制并发数
for _, filename := range filenames {
wg.Add(1)
go func(fname string) {
defer wg.Done()
semaphore <- struct{}{} // 获取信号量
defer func() { <-semaphore }() // 释放信号量
result, err := fr.ReadFile(fname, opts)
resultsChan <- struct {
filename string
result *FileResult
err error
}{fname, result, err}
}(filename)
}
// 等待所有协程完成
go func() {
wg.Wait()
close(resultsChan)
}()
// 收集结果
var errors []error
for res := range resultsChan {
if res.err != nil {
errors = append(errors, res.err)
} else {
results[res.filename] = res.result
}
}
// 如果有错误且所有文件都失败,返回第一个错误
if len(errors) > 0 && len(results) == 0 {
return nil, errors[0]
}
return results, nil
}
// readFileSync 同步读取文件
func (fr *FileReader) readFileSync(filename string, options *FileReaderOptions) (*FileResult, error) {
startTime := time.Now()
// 检查文件
fileInfo, err := os.Stat(filename)
if err != nil {
return nil, NewParseError("FILE_ERROR", "文件不存在或无法访问", filename, 0, err)
}
// 检查文件大小
if fileInfo.Size() > fr.maxFileSize {
return nil, NewParseError("FILE_ERROR",
fmt.Sprintf("文件过大: %d bytes, 最大限制: %d bytes", fileInfo.Size(), fr.maxFileSize),
filename, 0, nil)
}
// 打开文件
file, err := os.Open(filename)
if err != nil {
return nil, NewParseError("FILE_ERROR", "无法打开文件", filename, 0, err)
}
defer file.Close()
// 创建结果
result := &FileResult{
Lines: make([]string, 0),
Source: &FileSource{
Path: filename,
Size: fileInfo.Size(),
ModTime: fileInfo.ModTime(),
},
}
// 读取文件内容
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
lineNum := 0
validLines := 0
for scanner.Scan() {
lineNum++
line := scanner.Text()
// 处理行内容
if processedLine, valid := fr.processLine(line, options); valid {
result.Lines = append(result.Lines, processedLine)
validLines++
}
}
// 检查扫描错误
if err := scanner.Err(); err != nil {
return nil, NewParseError("READ_ERROR", "文件扫描失败", filename, lineNum, err)
}
// 更新统计信息
result.Source.LineCount = lineNum
result.Source.ValidLines = validLines
result.ValidLines = validLines
result.ReadTime = time.Since(startTime)
return result, nil
}
// processLine 处理单行内容
func (fr *FileReader) processLine(line string, options *FileReaderOptions) (string, bool) {
// 清理空白字符
if options.TrimSpace {
line = strings.TrimSpace(line)
}
// 跳过空行
if options.SkipEmpty && line == "" {
return "", false
}
// 跳过注释行
if options.SkipComments && strings.HasPrefix(line, "#") {
return "", false
}
// 内容验证
if options.EnableValidation && fr.enableValidation {
if !fr.validateLine(line) {
return "", false
}
}
return line, true
}
// validateLine 验证行内容
func (fr *FileReader) validateLine(line string) bool {
// 基本验证:检查是否包含特殊字符或过长
if len(line) > 1000 { // 单行最大1000字符
return false
}
// 检查是否包含控制字符
for _, r := range line {
if r < 32 && r != 9 && r != 10 && r != 13 { // 排除tab、换行、回车
return false
}
}
return true
}
// mergeOptions 合并选项
func (fr *FileReader) mergeOptions(options ...*FileReaderOptions) *FileReaderOptions {
opts := DefaultFileReaderOptions()
if len(options) > 0 && options[0] != nil {
opts = options[0]
}
return opts
}
// getFromCache 从缓存获取结果
func (fr *FileReader) getFromCache(filename string) *FileResult {
fr.mu.RLock()
defer fr.mu.RUnlock()
if result, exists := fr.cache[filename]; exists {
// 检查文件是否有更新
if fileInfo, err := os.Stat(filename); err == nil {
if fileInfo.ModTime().After(result.Source.ModTime) {
// 文件已更新,从缓存中移除
delete(fr.cache, filename)
return nil
}
}
return result
}
return nil
}
// addToCache 添加到缓存
func (fr *FileReader) addToCache(filename string, result *FileResult) {
fr.mu.Lock()
defer fr.mu.Unlock()
// 检查缓存大小
if len(fr.cache) >= fr.maxCacheSize {
// 移除最旧的条目简单的LRU策略
var oldestFile string
var oldestTime time.Time
for file, res := range fr.cache {
if oldestFile == "" || res.Source.ModTime.Before(oldestTime) {
oldestFile = file
oldestTime = res.Source.ModTime
}
}
delete(fr.cache, oldestFile)
}
fr.cache[filename] = result
}
// ClearCache 清空缓存
func (fr *FileReader) ClearCache() {
fr.mu.Lock()
defer fr.mu.Unlock()
fr.cache = make(map[string]*FileResult)
}
// GetCacheStats 获取缓存统计
func (fr *FileReader) GetCacheStats() map[string]interface{} {
fr.mu.RLock()
defer fr.mu.RUnlock()
return map[string]interface{}{
"cache_size": len(fr.cache),
"max_cache_size": fr.maxCacheSize,
"cache_enabled": fr.enableCache,
}
}

View File

@ -0,0 +1,384 @@
package parsers
import (
"fmt"
"net/url"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
// NetworkParser 网络配置解析器
type NetworkParser struct {
mu sync.RWMutex
options *NetworkParserOptions
}
// NetworkParserOptions 网络解析器选项
type NetworkParserOptions struct {
ValidateProxies bool `json:"validate_proxies"`
AllowInsecure bool `json:"allow_insecure"`
DefaultTimeout time.Duration `json:"default_timeout"`
DefaultWebTimeout time.Duration `json:"default_web_timeout"`
DefaultUserAgent string `json:"default_user_agent"`
}
// DefaultNetworkParserOptions 默认网络解析器选项
func DefaultNetworkParserOptions() *NetworkParserOptions {
return &NetworkParserOptions{
ValidateProxies: true,
AllowInsecure: false,
DefaultTimeout: 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",
}
}
// NewNetworkParser 创建网络配置解析器
func NewNetworkParser(options *NetworkParserOptions) *NetworkParser {
if options == nil {
options = DefaultNetworkParserOptions()
}
return &NetworkParser{
options: options,
}
}
// NetworkInput 网络配置输入参数
type NetworkInput struct {
// 代理配置
HttpProxy string `json:"http_proxy"`
Socks5Proxy string `json:"socks5_proxy"`
// 超时配置
Timeout int64 `json:"timeout"`
WebTimeout int64 `json:"web_timeout"`
// 网络选项
DisablePing bool `json:"disable_ping"`
DnsLog bool `json:"dns_log"`
UserAgent string `json:"user_agent"`
Cookie string `json:"cookie"`
}
// Parse 解析网络配置
func (np *NetworkParser) Parse(input *NetworkInput, options *ParserOptions) (*ParseResult, error) {
if input == nil {
return nil, NewParseError("INPUT_ERROR", "网络配置输入为空", "", 0, ErrEmptyInput)
}
startTime := time.Now()
result := &ParseResult{
Config: &ParsedConfig{
Network: &NetworkConfig{
EnableDNSLog: input.DnsLog,
DisablePing: input.DisablePing,
},
},
Success: true,
}
var errors []error
var warnings []string
// 解析HTTP代理
httpProxy, httpErrors, httpWarnings := np.parseHttpProxy(input.HttpProxy)
errors = append(errors, httpErrors...)
warnings = append(warnings, httpWarnings...)
// 解析Socks5代理
socks5Proxy, socks5Errors, socks5Warnings := np.parseSocks5Proxy(input.Socks5Proxy)
errors = append(errors, socks5Errors...)
warnings = append(warnings, socks5Warnings...)
// 解析超时配置
timeout, webTimeout, timeoutErrors, timeoutWarnings := np.parseTimeouts(input.Timeout, input.WebTimeout)
errors = append(errors, timeoutErrors...)
warnings = append(warnings, timeoutWarnings...)
// 解析用户代理
userAgent, uaErrors, uaWarnings := np.parseUserAgent(input.UserAgent)
errors = append(errors, uaErrors...)
warnings = append(warnings, uaWarnings...)
// 解析Cookie
cookie, cookieErrors, cookieWarnings := np.parseCookie(input.Cookie)
errors = append(errors, cookieErrors...)
warnings = append(warnings, cookieWarnings...)
// 检查代理冲突
if httpProxy != "" && socks5Proxy != "" {
warnings = append(warnings, "同时配置了HTTP代理和Socks5代理Socks5代理将被优先使用")
}
// 更新配置
result.Config.Network.HttpProxy = httpProxy
result.Config.Network.Socks5Proxy = socks5Proxy
result.Config.Network.Timeout = timeout
result.Config.Network.WebTimeout = webTimeout
result.Config.Network.UserAgent = userAgent
result.Config.Network.Cookie = cookie
// 设置结果状态
result.Errors = errors
result.Warnings = warnings
result.ParseTime = time.Since(startTime)
result.Success = len(errors) == 0
return result, nil
}
// parseHttpProxy 解析HTTP代理配置
func (np *NetworkParser) parseHttpProxy(proxyStr string) (string, []error, []string) {
var errors []error
var warnings []string
if proxyStr == "" {
return "", nil, nil
}
// 处理简写形式
normalizedProxy := np.normalizeHttpProxy(proxyStr)
// 验证代理URL
if np.options.ValidateProxies {
if err := np.validateProxyURL(normalizedProxy); err != nil {
errors = append(errors, NewParseError("PROXY_ERROR", err.Error(), "http_proxy", 0, err))
return "", errors, warnings
}
}
return normalizedProxy, errors, warnings
}
// parseSocks5Proxy 解析Socks5代理配置
func (np *NetworkParser) parseSocks5Proxy(proxyStr string) (string, []error, []string) {
var errors []error
var warnings []string
if proxyStr == "" {
return "", nil, nil
}
// 处理简写形式
normalizedProxy := np.normalizeSocks5Proxy(proxyStr)
// 验证代理URL
if np.options.ValidateProxies {
if err := np.validateProxyURL(normalizedProxy); err != nil {
errors = append(errors, NewParseError("PROXY_ERROR", err.Error(), "socks5_proxy", 0, err))
return "", errors, warnings
}
}
// 使用Socks5代理时建议禁用Ping
if normalizedProxy != "" {
warnings = append(warnings, "使用Socks5代理时建议禁用Ping检测")
}
return normalizedProxy, errors, warnings
}
// parseTimeouts 解析超时配置
func (np *NetworkParser) parseTimeouts(timeout, webTimeout int64) (time.Duration, time.Duration, []error, []string) {
var errors []error
var warnings []string
// 处理普通超时
finalTimeout := np.options.DefaultTimeout
if timeout > 0 {
if timeout > 300 { // 最大5分钟
warnings = append(warnings, "超时时间过长建议不超过300秒")
}
finalTimeout = time.Duration(timeout) * time.Second
}
// 处理Web超时
finalWebTimeout := np.options.DefaultWebTimeout
if webTimeout > 0 {
if webTimeout > 120 { // 最大2分钟
warnings = append(warnings, "Web超时时间过长建议不超过120秒")
}
finalWebTimeout = time.Duration(webTimeout) * time.Second
}
// 验证超时配置合理性
if finalWebTimeout > finalTimeout {
warnings = append(warnings, "Web超时时间大于普通超时时间可能导致不期望的行为")
}
return finalTimeout, finalWebTimeout, errors, warnings
}
// parseUserAgent 解析用户代理
func (np *NetworkParser) parseUserAgent(userAgent string) (string, []error, []string) {
var errors []error
var warnings []string
if userAgent == "" {
return np.options.DefaultUserAgent, errors, warnings
}
// 基本格式验证
if len(userAgent) > 512 {
errors = append(errors, NewParseError("USERAGENT_ERROR", "用户代理字符串过长", "user_agent", 0, nil))
return "", errors, warnings
}
// 检查是否包含特殊字符
if strings.ContainsAny(userAgent, "\r\n\t") {
errors = append(errors, NewParseError("USERAGENT_ERROR", "用户代理包含非法字符", "user_agent", 0, nil))
return "", errors, warnings
}
// 检查是否为常见浏览器用户代理
if !np.isValidUserAgent(userAgent) {
warnings = append(warnings, "用户代理格式可能不被目标服务器识别")
}
return userAgent, errors, warnings
}
// parseCookie 解析Cookie
func (np *NetworkParser) parseCookie(cookie string) (string, []error, []string) {
var errors []error
var warnings []string
if cookie == "" {
return "", errors, warnings
}
// 基本格式验证
if len(cookie) > 4096 { // HTTP Cookie长度限制
errors = append(errors, NewParseError("COOKIE_ERROR", "Cookie字符串过长", "cookie", 0, nil))
return "", errors, warnings
}
// 检查Cookie格式
if !np.isValidCookie(cookie) {
warnings = append(warnings, "Cookie格式可能不正确")
}
return cookie, errors, warnings
}
// 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"
default:
// 如果没有协议前缀默认使用HTTP
if !strings.Contains(proxy, "://") {
if strings.Contains(proxy, ":") {
return "http://" + proxy
} else {
return "http://127.0.0.1:" + proxy
}
}
return proxy
}
}
// normalizeSocks5Proxy 规范化Socks5代理URL
func (np *NetworkParser) normalizeSocks5Proxy(proxy string) string {
// 如果没有协议前缀添加socks5://
if !strings.HasPrefix(proxy, "socks5://") {
if strings.Contains(proxy, ":") {
return "socks5://" + proxy
} else {
return "socks5://127.0.0.1:" + proxy
}
}
return proxy
}
// validateProxyURL 验证代理URL格式
func (np *NetworkParser) validateProxyURL(proxyURL string) error {
if proxyURL == "" {
return nil
}
parsedURL, err := url.Parse(proxyURL)
if err != nil {
return fmt.Errorf("代理URL格式无效: %v", err)
}
// 检查协议
switch parsedURL.Scheme {
case "http", "https", "socks5":
// 支持的协议
default:
return fmt.Errorf("不支持的代理协议: %s", parsedURL.Scheme)
}
// 检查主机名
if parsedURL.Hostname() == "" {
return fmt.Errorf("代理主机名为空")
}
// 检查端口
portStr := parsedURL.Port()
if portStr != "" {
port, err := strconv.Atoi(portStr)
if err != nil {
return fmt.Errorf("代理端口号无效: %s", portStr)
}
if port < 1 || port > 65535 {
return fmt.Errorf("代理端口号超出范围: %d", port)
}
}
// 安全检查
if !np.options.AllowInsecure && parsedURL.Scheme == "http" {
return fmt.Errorf("不允许使用不安全的HTTP代理")
}
return nil
}
// isValidUserAgent 检查用户代理是否有效
func (np *NetworkParser) isValidUserAgent(userAgent string) bool {
// 检查是否包含常见的浏览器标识
commonBrowsers := []string{
"Mozilla", "Chrome", "Safari", "Firefox", "Edge", "Opera",
"AppleWebKit", "Gecko", "Trident", "Presto",
}
userAgentLower := strings.ToLower(userAgent)
for _, browser := range commonBrowsers {
if strings.Contains(userAgentLower, strings.ToLower(browser)) {
return true
}
}
return false
}
// 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))
}
// Validate 验证解析结果
func (np *NetworkParser) Validate() error {
return nil
}
// GetStatistics 获取解析统计
func (np *NetworkParser) GetStatistics() interface{} {
np.mu.RLock()
defer np.mu.RUnlock()
return map[string]interface{}{
"parser_type": "network",
"options": np.options,
}
}

View File

@ -0,0 +1,712 @@
package parsers
import (
"fmt"
"net"
"net/url"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
// TargetParser 目标解析器
type TargetParser struct {
fileReader *FileReader
mu sync.RWMutex
ipRegex *regexp.Regexp
portRegex *regexp.Regexp
urlRegex *regexp.Regexp
options *TargetParserOptions
}
// TargetParserOptions 目标解析器选项
type TargetParserOptions struct {
MaxTargets int `json:"max_targets"`
MaxPortRange int `json:"max_port_range"`
AllowPrivateIPs bool `json:"allow_private_ips"`
AllowLoopback bool `json:"allow_loopback"`
ValidateURLs bool `json:"validate_urls"`
ResolveDomains bool `json:"resolve_domains"`
EnableStatistics bool `json:"enable_statistics"`
DefaultPorts string `json:"default_ports"`
}
// DefaultTargetParserOptions 默认目标解析器选项
func DefaultTargetParserOptions() *TargetParserOptions {
return &TargetParserOptions{
MaxTargets: 10000,
MaxPortRange: 1000,
AllowPrivateIPs: true,
AllowLoopback: true,
ValidateURLs: true,
ResolveDomains: false,
EnableStatistics: true,
DefaultPorts: "80,443,22,21,23,25,53,110,993,995,1433,3306,5432,6379,27017",
}
}
// NewTargetParser 创建目标解析器
func NewTargetParser(fileReader *FileReader, options *TargetParserOptions) *TargetParser {
if options == nil {
options = DefaultTargetParserOptions()
}
// 编译正则表达式
ipRegex := regexp.MustCompile(`^(\d{1,3}\.){3}\d{1,3}$`)
portRegex := regexp.MustCompile(`^(\d+)(-(\d+))?$`)
urlRegex := regexp.MustCompile(`^https?://[^\s/$.?#].[^\s]*$`)
return &TargetParser{
fileReader: fileReader,
ipRegex: ipRegex,
portRegex: portRegex,
urlRegex: urlRegex,
options: options,
}
}
// TargetInput 目标输入参数
type TargetInput struct {
// 主机相关
Host string `json:"host"`
HostsFile string `json:"hosts_file"`
ExcludeHosts string `json:"exclude_hosts"`
// 端口相关
Ports string `json:"ports"`
PortsFile string `json:"ports_file"`
AddPorts string `json:"add_ports"`
ExcludePorts string `json:"exclude_ports"`
// URL相关
TargetURL string `json:"target_url"`
URLsFile string `json:"urls_file"`
// 主机端口组合
HostPort []string `json:"host_port"`
// 模式标识
LocalMode bool `json:"local_mode"`
}
// Parse 解析目标配置
func (tp *TargetParser) Parse(input *TargetInput, options *ParserOptions) (*ParseResult, error) {
if input == nil {
return nil, NewParseError("INPUT_ERROR", "目标输入为空", "", 0, ErrEmptyInput)
}
startTime := time.Now()
result := &ParseResult{
Config: &ParsedConfig{
Targets: &TargetConfig{
LocalMode: input.LocalMode,
},
},
Success: true,
}
var errors []error
var warnings []string
// 解析主机
hosts, hostErrors, hostWarnings := tp.parseHosts(input)
errors = append(errors, hostErrors...)
warnings = append(warnings, hostWarnings...)
// 解析URL
urls, urlErrors, urlWarnings := tp.parseURLs(input)
errors = append(errors, urlErrors...)
warnings = append(warnings, urlWarnings...)
// 解析端口
ports, portErrors, portWarnings := tp.parsePorts(input)
errors = append(errors, portErrors...)
warnings = append(warnings, portWarnings...)
// 解析排除端口
excludePorts, excludeErrors, excludeWarnings := tp.parseExcludePorts(input)
errors = append(errors, excludeErrors...)
warnings = append(warnings, excludeWarnings...)
// 解析主机端口组合
hostPorts, hpErrors, hpWarnings := tp.parseHostPorts(input)
errors = append(errors, hpErrors...)
warnings = append(warnings, hpWarnings...)
// 更新配置
result.Config.Targets.Hosts = hosts
result.Config.Targets.URLs = urls
result.Config.Targets.Ports = ports
result.Config.Targets.ExcludePorts = excludePorts
result.Config.Targets.HostPorts = hostPorts
// 生成统计信息
if tp.options.EnableStatistics {
result.Config.Targets.Statistics = tp.generateStatistics(hosts, urls, ports, excludePorts)
}
// 设置结果状态
result.Errors = errors
result.Warnings = warnings
result.ParseTime = time.Since(startTime)
result.Success = len(errors) == 0
return result, nil
}
// parseHosts 解析主机
func (tp *TargetParser) parseHosts(input *TargetInput) ([]string, []error, []string) {
var hosts []string
var errors []error
var warnings []string
// 解析命令行主机
if input.Host != "" {
hostList, err := tp.parseHostList(input.Host)
if err != nil {
errors = append(errors, NewParseError("HOST_ERROR", err.Error(), "command line", 0, err))
} else {
hosts = append(hosts, hostList...)
}
}
// 从文件读取主机
if input.HostsFile != "" {
fileResult, err := tp.fileReader.ReadFile(input.HostsFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取主机文件失败", input.HostsFile, 0, err))
} else {
for i, line := range fileResult.Lines {
hostList, err := tp.parseHostList(line)
if err != nil {
warnings = append(warnings, fmt.Sprintf("主机文件第%d行解析失败: %s", i+1, err.Error()))
} else {
hosts = append(hosts, hostList...)
}
}
}
}
// 处理排除主机
if input.ExcludeHosts != "" {
excludeList, err := tp.parseHostList(input.ExcludeHosts)
if err != nil {
warnings = append(warnings, fmt.Sprintf("排除主机解析失败: %s", err.Error()))
} else {
hosts = tp.excludeHosts(hosts, excludeList)
}
}
// 去重和验证
hosts = tp.removeDuplicateStrings(hosts)
validHosts := make([]string, 0, len(hosts))
for _, host := range hosts {
if valid, err := tp.validateHost(host); valid {
validHosts = append(validHosts, host)
} else if err != nil {
warnings = append(warnings, fmt.Sprintf("无效主机: %s - %s", host, err.Error()))
}
}
// 检查目标数量限制
if len(validHosts) > tp.options.MaxTargets {
warnings = append(warnings, fmt.Sprintf("主机数量超过限制,截取前%d个", tp.options.MaxTargets))
validHosts = validHosts[:tp.options.MaxTargets]
}
return validHosts, errors, warnings
}
// parseURLs 解析URL
func (tp *TargetParser) parseURLs(input *TargetInput) ([]string, []error, []string) {
var urls []string
var errors []error
var warnings []string
// 解析命令行URL
if input.TargetURL != "" {
urlList := strings.Split(input.TargetURL, ",")
for _, rawURL := range urlList {
rawURL = strings.TrimSpace(rawURL)
if rawURL != "" {
if valid, err := tp.validateURL(rawURL); valid {
urls = append(urls, rawURL)
} else {
warnings = append(warnings, fmt.Sprintf("无效URL: %s - %s", rawURL, err.Error()))
}
}
}
}
// 从文件读取URL
if input.URLsFile != "" {
fileResult, err := tp.fileReader.ReadFile(input.URLsFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取URL文件失败", input.URLsFile, 0, err))
} else {
for i, line := range fileResult.Lines {
if valid, err := tp.validateURL(line); valid {
urls = append(urls, line)
} else {
warnings = append(warnings, fmt.Sprintf("URL文件第%d行无效: %s", i+1, err.Error()))
}
}
}
}
// 去重
urls = tp.removeDuplicateStrings(urls)
return urls, errors, warnings
}
// parsePorts 解析端口
func (tp *TargetParser) parsePorts(input *TargetInput) ([]int, []error, []string) {
var ports []int
var errors []error
var warnings []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))
} else {
ports = append(ports, portList...)
}
}
// 从文件读取端口
if input.PortsFile != "" {
fileResult, err := tp.fileReader.ReadFile(input.PortsFile)
if err != nil {
errors = append(errors, NewParseError("FILE_ERROR", "读取端口文件失败", input.PortsFile, 0, err))
} else {
for i, line := range fileResult.Lines {
portList, err := tp.parsePortList(line)
if err != nil {
warnings = append(warnings, fmt.Sprintf("端口文件第%d行解析失败: %s", i+1, err.Error()))
} else {
ports = append(ports, portList...)
}
}
}
}
// 处理额外端口
if input.AddPorts != "" {
addPortList, err := tp.parsePortList(input.AddPorts)
if err != nil {
warnings = append(warnings, fmt.Sprintf("额外端口解析失败: %s", err.Error()))
} else {
ports = append(ports, addPortList...)
}
}
// 去重和排序
ports = tp.removeDuplicatePorts(ports)
return ports, errors, warnings
}
// parseExcludePorts 解析排除端口
func (tp *TargetParser) parseExcludePorts(input *TargetInput) ([]int, []error, []string) {
var excludePorts []int
var errors []error
var warnings []string
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))
} else {
excludePorts = portList
}
}
return excludePorts, errors, warnings
}
// parseHostPorts 解析主机端口组合
func (tp *TargetParser) parseHostPorts(input *TargetInput) ([]string, []error, []string) {
var hostPorts []string
var errors []error
var warnings []string
for _, hp := range input.HostPort {
if hp != "" {
if valid, err := tp.validateHostPort(hp); valid {
hostPorts = append(hostPorts, hp)
} else {
warnings = append(warnings, fmt.Sprintf("无效主机端口组合: %s - %s", hp, err.Error()))
}
}
}
return hostPorts, errors, warnings
}
// parseHostList 解析主机列表
func (tp *TargetParser) parseHostList(hostStr string) ([]string, error) {
if hostStr == "" {
return nil, nil
}
var hosts []string
hostItems := strings.Split(hostStr, ",")
for _, item := range hostItems {
item = strings.TrimSpace(item)
if item == "" {
continue
}
// 检查是否为IP范围或CIDR
if strings.Contains(item, "/") {
// CIDR表示法
cidrHosts, err := tp.parseCIDR(item)
if err != nil {
return nil, fmt.Errorf("CIDR解析失败 %s: %v", item, err)
}
hosts = append(hosts, cidrHosts...)
} else if strings.Contains(item, "-") {
// IP范围表示法
rangeHosts, err := tp.parseIPRange(item)
if err != nil {
return nil, fmt.Errorf("IP范围解析失败 %s: %v", item, err)
}
hosts = append(hosts, rangeHosts...)
} else {
// 单个IP或域名
hosts = append(hosts, item)
}
}
return hosts, nil
}
// parsePortList 解析端口列表
func (tp *TargetParser) parsePortList(portStr string) ([]int, error) {
if portStr == "" {
return nil, nil
}
var ports []int
portItems := strings.Split(portStr, ",")
for _, item := range portItems {
item = strings.TrimSpace(item)
if item == "" {
continue
}
if strings.Contains(item, "-") {
// 端口范围
rangePorts, err := tp.parsePortRange(item)
if err != nil {
return nil, fmt.Errorf("端口范围解析失败 %s: %v", item, err)
}
// 检查范围大小
if len(rangePorts) > tp.options.MaxPortRange {
return nil, fmt.Errorf("端口范围过大: %d, 最大允许: %d", len(rangePorts), tp.options.MaxPortRange)
}
ports = append(ports, rangePorts...)
} else {
// 单个端口
port, err := strconv.Atoi(item)
if err != nil {
return nil, fmt.Errorf("无效端口号: %s", item)
}
if port < 1 || port > 65535 {
return nil, fmt.Errorf("端口号超出范围: %d", port)
}
ports = append(ports, port)
}
}
return ports, nil
}
// parseCIDR 解析CIDR网段
func (tp *TargetParser) parseCIDR(cidr string) ([]string, error) {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
}
var ips []string
ip := ipNet.IP.Mask(ipNet.Mask)
for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); tp.nextIP(ip) {
ips = append(ips, ip.String())
// 防止生成过多IP
if len(ips) > tp.options.MaxTargets {
break
}
}
return ips, nil
}
// parseIPRange 解析IP范围
func (tp *TargetParser) parseIPRange(rangeStr string) ([]string, error) {
parts := strings.Split(rangeStr, "-")
if len(parts) != 2 {
return nil, fmt.Errorf("无效的IP范围格式")
}
startIP := net.ParseIP(strings.TrimSpace(parts[0]))
endIP := net.ParseIP(strings.TrimSpace(parts[1]))
if startIP == nil || endIP == nil {
return nil, fmt.Errorf("无效的IP地址")
}
var ips []string
for ip := startIP; !ip.Equal(endIP); tp.nextIP(ip) {
ips = append(ips, ip.String())
// 防止生成过多IP
if len(ips) > tp.options.MaxTargets {
break
}
}
ips = append(ips, endIP.String()) // 添加结束IP
return ips, nil
}
// parsePortRange 解析端口范围
func (tp *TargetParser) parsePortRange(rangeStr string) ([]int, error) {
parts := strings.Split(rangeStr, "-")
if len(parts) != 2 {
return nil, fmt.Errorf("无效的端口范围格式")
}
startPort, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))
endPort, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))
if err1 != nil || err2 != nil {
return nil, fmt.Errorf("无效的端口号")
}
if startPort > endPort {
startPort, endPort = endPort, startPort
}
if startPort < 1 || endPort > 65535 {
return nil, fmt.Errorf("端口号超出范围")
}
var ports []int
for port := startPort; port <= endPort; port++ {
ports = append(ports, port)
}
return ports, nil
}
// nextIP 获取下一个IP地址
func (tp *TargetParser) nextIP(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}
// validateHost 验证主机地址
func (tp *TargetParser) validateHost(host string) (bool, error) {
if host == "" {
return false, fmt.Errorf("主机地址为空")
}
// 检查是否为IP地址
if ip := net.ParseIP(host); ip != nil {
return tp.validateIP(ip)
}
// 检查是否为域名
if tp.isValidDomain(host) {
return true, nil
}
return false, fmt.Errorf("无效的主机地址格式")
}
// validateIP 验证IP地址
func (tp *TargetParser) validateIP(ip net.IP) (bool, error) {
if ip == nil {
return false, fmt.Errorf("IP地址为空")
}
// 检查是否为私有IP
if !tp.options.AllowPrivateIPs && tp.isPrivateIP(ip) {
return false, fmt.Errorf("不允许私有IP地址")
}
// 检查是否为回环地址
if !tp.options.AllowLoopback && ip.IsLoopback() {
return false, fmt.Errorf("不允许回环地址")
}
return true, nil
}
// validateURL 验证URL
func (tp *TargetParser) validateURL(rawURL string) (bool, error) {
if rawURL == "" {
return false, fmt.Errorf("URL为空")
}
if !tp.options.ValidateURLs {
return true, nil
}
if !tp.urlRegex.MatchString(rawURL) {
return false, fmt.Errorf("URL格式无效")
}
// 进一步验证URL格式
_, err := url.Parse(rawURL)
if err != nil {
return false, fmt.Errorf("URL解析失败: %v", err)
}
return true, nil
}
// validateHostPort 验证主机端口组合
func (tp *TargetParser) validateHostPort(hostPort string) (bool, error) {
parts := strings.Split(hostPort, ":")
if len(parts) != 2 {
return false, fmt.Errorf("主机端口格式无效,应为 host:port")
}
host := strings.TrimSpace(parts[0])
portStr := strings.TrimSpace(parts[1])
// 验证主机
if valid, err := tp.validateHost(host); !valid {
return false, fmt.Errorf("主机无效: %v", err)
}
// 验证端口
port, err := strconv.Atoi(portStr)
if err != nil {
return false, fmt.Errorf("端口号无效: %s", portStr)
}
if port < 1 || port > 65535 {
return false, fmt.Errorf("端口号超出范围: %d", port)
}
return true, nil
}
// isPrivateIP 检查是否为私有IP
func (tp *TargetParser) isPrivateIP(ip net.IP) bool {
if ip4 := ip.To4(); ip4 != nil {
// 10.0.0.0/8
if ip4[0] == 10 {
return true
}
// 172.16.0.0/12
if ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31 {
return true
}
// 192.168.0.0/16
if ip4[0] == 192 && ip4[1] == 168 {
return true
}
}
return false
}
// 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
}
// excludeHosts 排除指定主机
func (tp *TargetParser) excludeHosts(hosts, excludeList []string) []string {
excludeMap := make(map[string]struct{})
for _, exclude := range excludeList {
excludeMap[exclude] = struct{}{}
}
var result []string
for _, host := range hosts {
if _, excluded := excludeMap[host]; !excluded {
result = append(result, host)
}
}
return result
}
// removeDuplicateStrings 去重字符串切片
func (tp *TargetParser) removeDuplicateStrings(slice []string) []string {
seen := make(map[string]struct{})
var result []string
for _, item := range slice {
if _, exists := seen[item]; !exists {
seen[item] = struct{}{}
result = append(result, item)
}
}
return result
}
// removeDuplicatePorts 去重端口切片
func (tp *TargetParser) removeDuplicatePorts(slice []int) []int {
seen := make(map[int]struct{})
var result []int
for _, item := range slice {
if _, exists := seen[item]; !exists {
seen[item] = struct{}{}
result = append(result, item)
}
}
return result
}
// generateStatistics 生成统计信息
func (tp *TargetParser) generateStatistics(hosts, urls []string, ports, excludePorts []int) *TargetStatistics {
return &TargetStatistics{
TotalHosts: len(hosts),
TotalURLs: len(urls),
TotalPorts: len(ports),
ExcludedPorts: len(excludePorts),
}
}
// Validate 验证解析结果
func (tp *TargetParser) Validate() error {
return nil
}
// GetStatistics 获取解析统计
func (tp *TargetParser) GetStatistics() interface{} {
tp.mu.RLock()
defer tp.mu.RUnlock()
return map[string]interface{}{
"parser_type": "target",
"options": tp.options,
}
}

169
Common/parsers/Types.go Normal file
View File

@ -0,0 +1,169 @@
package parsers
import (
"errors"
"fmt"
"time"
)
// ParsedConfig 解析后的完整配置
type ParsedConfig struct {
Targets *TargetConfig `json:"targets"`
Credentials *CredentialConfig `json:"credentials"`
Network *NetworkConfig `json:"network"`
Validation *ValidationConfig `json:"validation"`
}
// TargetConfig 目标配置
type TargetConfig struct {
Hosts []string `json:"hosts"`
URLs []string `json:"urls"`
Ports []int `json:"ports"`
ExcludePorts []int `json:"exclude_ports"`
HostPorts []string `json:"host_ports"`
LocalMode bool `json:"local_mode"`
Statistics *TargetStatistics `json:"statistics,omitempty"`
}
// TargetStatistics 目标解析统计
type TargetStatistics struct {
TotalHosts int `json:"total_hosts"`
TotalURLs int `json:"total_urls"`
TotalPorts int `json:"total_ports"`
ExcludedHosts int `json:"excluded_hosts"`
ExcludedPorts int `json:"excluded_ports"`
}
// CredentialConfig 认证配置
type CredentialConfig struct {
Usernames []string `json:"usernames"`
Passwords []string `json:"passwords"`
HashValues []string `json:"hash_values"`
HashBytes [][]byte `json:"hash_bytes,omitempty"`
SshKeyPath string `json:"ssh_key_path"`
Domain string `json:"domain"`
Statistics *CredentialStats `json:"statistics,omitempty"`
}
// CredentialStats 认证配置统计
type CredentialStats struct {
TotalUsernames int `json:"total_usernames"`
TotalPasswords int `json:"total_passwords"`
TotalHashes int `json:"total_hashes"`
UniqueUsernames int `json:"unique_usernames"`
UniquePasswords int `json:"unique_passwords"`
ValidHashes int `json:"valid_hashes"`
InvalidHashes int `json:"invalid_hashes"`
}
// NetworkConfig 网络配置
type NetworkConfig struct {
HttpProxy string `json:"http_proxy"`
Socks5Proxy string `json:"socks5_proxy"`
Timeout time.Duration `json:"timeout"`
WebTimeout time.Duration `json:"web_timeout"`
DisablePing bool `json:"disable_ping"`
EnableDNSLog bool `json:"enable_dns_log"`
UserAgent string `json:"user_agent"`
Cookie string `json:"cookie"`
}
// ValidationConfig 验证配置
type ValidationConfig struct {
ScanMode string `json:"scan_mode"`
ConflictChecked bool `json:"conflict_checked"`
Errors []error `json:"errors,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
// ParseResult 解析结果
type ParseResult struct {
Config *ParsedConfig `json:"config"`
Success bool `json:"success"`
Errors []error `json:"errors,omitempty"`
Warnings []string `json:"warnings,omitempty"`
ParseTime time.Duration `json:"parse_time"`
}
// 预定义错误类型
var (
ErrEmptyInput = errors.New("输入参数为空")
ErrInvalidFormat = errors.New("格式无效")
ErrFileNotFound = errors.New("文件未找到")
ErrConflictingParams = errors.New("参数冲突")
ErrInvalidProxy = errors.New("代理配置无效")
ErrInvalidHash = errors.New("哈希值无效")
ErrInvalidPort = errors.New("端口号无效")
ErrInvalidIP = errors.New("IP地址无效")
ErrInvalidURL = errors.New("URL格式无效")
)
// ParserOptions 解析器选项
type ParserOptions struct {
EnableConcurrency bool // 启用并发解析
MaxWorkers int // 最大工作协程数
Timeout time.Duration // 解析超时时间
EnableValidation bool // 启用详细验证
EnableStatistics bool // 启用统计信息
IgnoreErrors bool // 忽略非致命错误
FileMaxSize int64 // 文件最大大小限制
MaxTargets int // 最大目标数量限制
}
// 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
}
}
// Parser 解析器接口
type Parser interface {
Parse(options *ParserOptions) (*ParseResult, error)
Validate() error
GetStatistics() interface{}
}
// FileSource 文件源
type FileSource struct {
Path string `json:"path"`
Size int64 `json:"size"`
ModTime time.Time `json:"mod_time"`
LineCount int `json:"line_count"`
ValidLines int `json:"valid_lines"`
}
// ParseError 解析错误,包含详细上下文
type ParseError struct {
Type string `json:"type"`
Message string `json:"message"`
Source string `json:"source"`
Line int `json:"line,omitempty"`
Context string `json:"context,omitempty"`
Original error `json:"original,omitempty"`
}
func (e *ParseError) Error() string {
if e.Line > 0 {
return fmt.Sprintf("%s:%d - %s: %s", e.Source, e.Line, e.Type, e.Message)
}
return fmt.Sprintf("%s - %s: %s", e.Source, e.Type, e.Message)
}
// NewParseError 创建解析错误
func NewParseError(errType, message, source string, line int, original error) *ParseError {
return &ParseError{
Type: errType,
Message: message,
Source: source,
Line: line,
Original: original,
}
}

View File

@ -0,0 +1,303 @@
package parsers
import (
"fmt"
"sync"
"time"
)
// ValidationParser 参数验证解析器
type ValidationParser struct {
mu sync.RWMutex
options *ValidationParserOptions
}
// ValidationParserOptions 验证解析器选项
type ValidationParserOptions struct {
StrictMode bool `json:"strict_mode"` // 严格模式
AllowEmpty bool `json:"allow_empty"` // 允许空配置
CheckConflicts bool `json:"check_conflicts"` // 检查参数冲突
ValidateTargets bool `json:"validate_targets"` // 验证目标有效性
ValidateNetwork bool `json:"validate_network"` // 验证网络配置
MaxErrorCount int `json:"max_error_count"` // 最大错误数量
}
// DefaultValidationParserOptions 默认验证解析器选项
func DefaultValidationParserOptions() *ValidationParserOptions {
return &ValidationParserOptions{
StrictMode: false,
AllowEmpty: true,
CheckConflicts: true,
ValidateTargets: true,
ValidateNetwork: true,
MaxErrorCount: 100,
}
}
// NewValidationParser 创建验证解析器
func NewValidationParser(options *ValidationParserOptions) *ValidationParser {
if options == nil {
options = DefaultValidationParserOptions()
}
return &ValidationParser{
options: options,
}
}
// ValidationInput 验证输入参数
type ValidationInput struct {
// 扫描模式
ScanMode string `json:"scan_mode"`
LocalMode bool `json:"local_mode"`
// 目标配置
HasHosts bool `json:"has_hosts"`
HasURLs bool `json:"has_urls"`
HasPorts bool `json:"has_ports"`
// 网络配置
HasProxy bool `json:"has_proxy"`
DisablePing bool `json:"disable_ping"`
// 凭据配置
HasCredentials bool `json:"has_credentials"`
// 特殊模式
PocScan bool `json:"poc_scan"`
BruteScan bool `json:"brute_scan"`
LocalScan bool `json:"local_scan"`
}
// ConflictRule 冲突规则
type ConflictRule struct {
Name string `json:"name"`
Description string `json:"description"`
Fields []string `json:"fields"`
Severity string `json:"severity"` // error, warning, info
}
// ValidationRule 验证规则
type ValidationRule struct {
Name string `json:"name"`
Description string `json:"description"`
Validator func(input *ValidationInput) error `json:"-"`
Severity string `json:"severity"`
}
// Parse 执行参数验证
func (vp *ValidationParser) Parse(input *ValidationInput, config *ParsedConfig, options *ParserOptions) (*ParseResult, error) {
if input == nil {
return nil, NewParseError("INPUT_ERROR", "验证输入为空", "", 0, ErrEmptyInput)
}
startTime := time.Now()
result := &ParseResult{
Config: &ParsedConfig{
Validation: &ValidationConfig{
ScanMode: input.ScanMode,
ConflictChecked: true,
},
},
Success: true,
}
var errors []error
var warnings []string
// 基础验证
basicErrors, basicWarnings := vp.validateBasic(input)
errors = append(errors, basicErrors...)
warnings = append(warnings, basicWarnings...)
// 冲突检查
if vp.options.CheckConflicts {
conflictErrors, conflictWarnings := vp.checkConflicts(input)
errors = append(errors, conflictErrors...)
warnings = append(warnings, conflictWarnings...)
}
// 逻辑验证
logicErrors, logicWarnings := vp.validateLogic(input, config)
errors = append(errors, logicErrors...)
warnings = append(warnings, logicWarnings...)
// 性能建议
performanceWarnings := vp.checkPerformance(input, config)
warnings = append(warnings, performanceWarnings...)
// 检查错误数量限制
if len(errors) > vp.options.MaxErrorCount {
errors = errors[:vp.options.MaxErrorCount]
warnings = append(warnings, fmt.Sprintf("错误数量过多,仅显示前%d个", vp.options.MaxErrorCount))
}
// 更新结果
result.Config.Validation.Errors = errors
result.Config.Validation.Warnings = warnings
result.Errors = errors
result.Warnings = warnings
result.ParseTime = time.Since(startTime)
result.Success = len(errors) == 0
return result, nil
}
// validateBasic 基础验证
func (vp *ValidationParser) validateBasic(input *ValidationInput) ([]error, []string) {
var errors []error
var warnings []string
// 检查是否有任何目标
if !input.HasHosts && !input.HasURLs && !input.LocalMode {
if !vp.options.AllowEmpty {
errors = append(errors, NewParseError("VALIDATION_ERROR", "未指定任何扫描目标", "basic", 0, nil))
} else {
warnings = append(warnings, "未指定扫描目标,将使用默认配置")
}
}
// 检查扫描模式
if input.ScanMode != "" {
if err := vp.validateScanMode(input.ScanMode); err != nil {
if vp.options.StrictMode {
errors = append(errors, err)
} else {
warnings = append(warnings, err.Error())
}
}
}
return errors, warnings
}
// checkConflicts 检查参数冲突
func (vp *ValidationParser) checkConflicts(input *ValidationInput) ([]error, []string) {
var errors []error
var warnings []string
// 定义冲突规则 (预留用于扩展)
_ = []ConflictRule{
{
Name: "multiple_scan_modes",
Description: "不能同时使用多种扫描模式",
Fields: []string{"hosts", "urls", "local_mode"},
Severity: "error",
},
{
Name: "proxy_with_ping",
Description: "使用代理时建议禁用Ping检测",
Fields: []string{"proxy", "ping"},
Severity: "warning",
},
}
// 检查扫描模式冲突
scanModes := 0
if input.HasHosts {
scanModes++
}
if input.HasURLs {
scanModes++
}
if input.LocalMode {
scanModes++
}
if scanModes > 1 {
errors = append(errors, NewParseError("CONFLICT_ERROR",
"不能同时指定多种扫描模式(主机扫描、URL扫描、本地模式)", "validation", 0, nil))
}
// 检查代理和Ping冲突
if input.HasProxy && !input.DisablePing {
warnings = append(warnings, "代理模式下Ping检测可能失效")
}
return errors, warnings
}
// validateLogic 逻辑验证
func (vp *ValidationParser) validateLogic(input *ValidationInput, config *ParsedConfig) ([]error, []string) {
var errors []error
var warnings []string
// 验证目标配置逻辑
if vp.options.ValidateTargets && config != nil && config.Targets != nil {
// 检查排除端口配置
if len(config.Targets.ExcludePorts) > 0 && len(config.Targets.Ports) == 0 {
warnings = append(warnings, "排除端口无效")
}
}
return errors, warnings
}
// checkPerformance 性能检查
func (vp *ValidationParser) checkPerformance(input *ValidationInput, config *ParsedConfig) []string {
var warnings []string
if config == nil {
return warnings
}
// 检查目标数量
if config.Targets != nil {
totalTargets := len(config.Targets.Hosts) * len(config.Targets.Ports)
if totalTargets > 100000 {
warnings = append(warnings, fmt.Sprintf("大量目标(%d),可能耗时较长", totalTargets))
}
// 检查端口范围
if len(config.Targets.Ports) > 1000 {
warnings = append(warnings, "端口数量过多")
}
}
// 检查超时配置
if config.Network != nil {
if config.Network.Timeout < 1*time.Second {
warnings = append(warnings, "超时过短")
}
if config.Network.Timeout > 60*time.Second {
warnings = append(warnings, "超时过长")
}
}
return warnings
}
// validateScanMode 验证扫描模式
func (vp *ValidationParser) validateScanMode(scanMode string) error {
validModes := []string{"all", "main", "web", "db", "service", "top1000", "custom"}
for _, mode := range validModes {
if scanMode == mode {
return nil
}
}
return NewParseError("VALIDATION_ERROR",
fmt.Sprintf("无效的扫描模式: %s", scanMode), "scan_mode", 0, nil)
}
// Validate 验证解析结果
func (vp *ValidationParser) Validate() error {
return nil
}
// GetStatistics 获取解析统计
func (vp *ValidationParser) GetStatistics() interface{} {
vp.mu.RLock()
defer vp.mu.RUnlock()
return map[string]interface{}{
"parser_type": "validation",
"options": vp.options,
}
}