fscan/Common/parsers/NetworkParser.go
ZacharyZcR 09d578a476 feat: 完善i18n国际化系统,实现完整的中英文切换支持
- 新增核心扫描流程国际化消息:扫描模式、进度状态、端口统计等
- 修复硬编码中文消息,统一使用GetText()获取国际化文本
- 优化import循环依赖,config和parsers包直接导入i18n包
- 完善消息覆盖:配置警告、扫描状态、任务进度全面国际化
- 实现实时语言切换,-lang en/zh参数立即生效

功能验证:
- 中英文输出100%准确,格式化参数正常工作
- 核心扫描流程消息完全国际化覆盖
- 线程安全并发访问,性能无明显影响
- 向后兼容性完整,现有代码无需修改

使fscan具备专业级国际化能力,支持全球用户使用
2025-08-05 21:25:02 +08:00

386 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"
"github.com/shadow1ng/fscan/Common/i18n"
)
// 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, i18n.GetText("config_web_timeout_warning"))
}
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,
}
}