Files
subconverter-go/internal/parser/socks.go
Rogee 7fcabe0225
Some checks failed
CI/CD Pipeline / Test (push) Failing after 22m19s
CI/CD Pipeline / Security Scan (push) Failing after 5m57s
CI/CD Pipeline / Build (amd64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (amd64, linux) (push) Has been skipped
CI/CD Pipeline / Build (amd64, windows) (push) Has been skipped
CI/CD Pipeline / Build (arm64, darwin) (push) Has been skipped
CI/CD Pipeline / Build (arm64, linux) (push) Has been skipped
CI/CD Pipeline / Build Docker Image (push) Has been skipped
CI/CD Pipeline / Create Release (push) Has been skipped
first commit
2025-09-28 10:05:07 +08:00

215 lines
5.0 KiB
Go

package parser
import (
"fmt"
"net/url"
"strconv"
"strings"
"github.com/subconverter-go/internal/logging"
)
// Socks5Parser Socks5协议解析器
// 实现Socks5代理配置的解析功能
type Socks5Parser struct {
logger *logging.Logger
}
// NewSocks5Parser 创建新的Socks5解析器
// 返回初始化好的Socks5Parser实例
func NewSocks5Parser(logger *logging.Logger) *Socks5Parser {
return &Socks5Parser{
logger: logger,
}
}
// Parse 解析Socks5配置
// 支持标准URL格式
func (p *Socks5Parser) Parse(input string) (*ProxyConfig, error) {
p.logger.Debugf("Parsing Socks5 configuration")
normalized, err := p.normalizeInput(input)
if err != nil {
return nil, err
}
// 去除前缀
trimmed := strings.TrimPrefix(normalized, "socks5://")
// 解析URL
parsedURL, err := url.Parse("socks5://" + trimmed)
if err != nil {
return nil, fmt.Errorf("failed to parse Socks5 URL: %v", err)
}
config := &ProxyConfig{
Type: "socks5",
Protocol: "socks5",
Server: parsedURL.Hostname(),
}
// 解析端口
port, err := strconv.Atoi(parsedURL.Port())
if err != nil {
return nil, fmt.Errorf("invalid port number: %v", err)
}
config.Port = port
// 解析用户名和密码
var username, password string
if parsedURL.User != nil {
username = parsedURL.User.Username()
password, _ = parsedURL.User.Password()
}
// 解析备注
if parsedURL.Fragment != "" {
config.Name = parsedURL.Fragment
} else {
config.Name = fmt.Sprintf("socks5-%s:%d", config.Server, config.Port)
}
// 创建设置映射
settings := map[string]interface{}{
"udp": true,
}
// 添加认证信息
if username != "" {
settings["username"] = username
if password != "" {
settings["password"] = password
}
}
config.Settings = settings
config.UDP = true
p.logger.Infof("Successfully parsed Socks5 configuration for server: %s:%d", config.Server, config.Port)
return config, nil
}
func (p *Socks5Parser) normalizeInput(input string) (string, error) {
trimmed := strings.TrimSpace(input)
if trimmed == "" {
return "", fmt.Errorf("empty socks proxy string")
}
lower := strings.ToLower(trimmed)
switch {
case strings.HasPrefix(lower, "socks5://"):
return trimmed, nil
case strings.HasPrefix(lower, "socks://"):
return "socks5://" + trimmed[len("socks://"):], nil
case strings.HasPrefix(lower, "tg://socks"), strings.HasPrefix(lower, "https://t.me/socks"):
return p.normalizeTelegramLink(trimmed)
default:
return trimmed, nil
}
}
func (p *Socks5Parser) normalizeTelegramLink(link string) (string, error) {
parsed, err := url.Parse(link)
if err != nil {
return "", fmt.Errorf("failed to parse Telegram socks link: %w", err)
}
values := parsed.Query()
host := values.Get("server")
if host == "" {
host = values.Get("host")
}
port := values.Get("port")
if port == "" {
return "", fmt.Errorf("missing port in Telegram socks link")
}
username := values.Get("user")
if username == "" {
username = values.Get("username")
}
password := values.Get("pass")
if password == "" {
password = values.Get("password")
}
if host == "" {
return "", fmt.Errorf("missing server in Telegram socks link")
}
credentials := ""
if username != "" {
var userInfo *url.Userinfo
if password != "" {
userInfo = url.UserPassword(username, password)
} else {
userInfo = url.User(username)
}
credentials = userInfo.String() + "@"
}
fragment := parsed.Fragment
if fragment != "" {
fragment = "#" + fragment
}
return fmt.Sprintf("socks5://%s%s:%s%s", credentials, host, port, fragment), nil
}
// Validate 验证Socks5配置
func (p *Socks5Parser) Validate(config *ProxyConfig) error {
p.logger.Debugf("Validating Socks5 configuration")
// 检查必要字段
if config.Server == "" {
return fmt.Errorf("server address is required")
}
if config.Port <= 0 || config.Port > 65535 {
return fmt.Errorf("invalid port number: %d", config.Port)
}
// 验证认证信息
username := p.getUsername(config)
password := p.getPassword(config)
// 如果提供了用户名,验证密码是否也提供
if username != "" && password == "" {
p.logger.Warn("Username provided but password is empty")
}
// 如果没有提供用户名,确保也没有密码
if username == "" && password != "" {
return fmt.Errorf("password provided without username")
}
p.logger.Debug("Socks5 configuration validation passed")
return nil
}
// getUsername 从配置中获取用户名
func (p *Socks5Parser) getUsername(config *ProxyConfig) string {
if username, exists := config.Settings["username"]; exists {
if usernameStr, ok := username.(string); ok {
return usernameStr
}
}
return ""
}
// getPassword 从配置中获取密码
func (p *Socks5Parser) getPassword(config *ProxyConfig) string {
if password, exists := config.Settings["password"]; exists {
if passwordStr, ok := password.(string); ok {
return passwordStr
}
}
return ""
}
// GetSupportedProtocols 获取支持的协议
func (p *Socks5Parser) GetSupportedProtocols() []string {
return []string{"socks5"}
}