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

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

修复问题:
- 消除重复的"Web超时时间大于普通超时时间"警告
- 添加目标主机、端口、代理等配置信息显示
- 删除说教性安全警告,保留技术性提示
2025-08-05 02:14:10 +08:00

384 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
}
}