first commit
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
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
This commit is contained in:
174
internal/parser/http.go
Normal file
174
internal/parser/http.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/subconverter-go/internal/logging"
|
||||
)
|
||||
|
||||
// HTTPParser HTTP协议解析器
|
||||
// 实现HTTP代理配置的解析功能
|
||||
type HTTPParser struct {
|
||||
logger *logging.Logger
|
||||
}
|
||||
|
||||
// NewHTTPParser 创建新的HTTP解析器
|
||||
// 返回初始化好的HTTPParser实例
|
||||
func NewHTTPParser(logger *logging.Logger) *HTTPParser {
|
||||
return &HTTPParser{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse 解析HTTP配置
|
||||
// 支持http://和https://格式
|
||||
func (p *HTTPParser) Parse(input string) (*ProxyConfig, error) {
|
||||
p.logger.Debugf("Parsing HTTP configuration")
|
||||
|
||||
// 检查是否已经是URL格式
|
||||
var parsedURL *url.URL
|
||||
var err error
|
||||
|
||||
if strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://") {
|
||||
parsedURL, err = url.Parse(input)
|
||||
} else {
|
||||
// 尝试添加http://前缀
|
||||
parsedURL, err = url.Parse("http://" + input)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse HTTP URL: %v", err)
|
||||
}
|
||||
|
||||
config := &ProxyConfig{
|
||||
Type: "http",
|
||||
Protocol: "http",
|
||||
Server: parsedURL.Hostname(),
|
||||
}
|
||||
|
||||
// 解析端口
|
||||
port, err := strconv.Atoi(parsedURL.Port())
|
||||
if err != nil {
|
||||
// 如果没有指定端口,使用默认端口
|
||||
if parsedURL.Scheme == "https" {
|
||||
config.Port = 443
|
||||
} else {
|
||||
config.Port = 80
|
||||
}
|
||||
} else {
|
||||
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("http-%s:%d", config.Server, config.Port)
|
||||
}
|
||||
|
||||
// 创建设置映射
|
||||
settings := map[string]interface{}{
|
||||
"udp": false, // HTTP代理不支持UDP
|
||||
}
|
||||
|
||||
// 添加认证信息
|
||||
if username != "" {
|
||||
settings["username"] = username
|
||||
if password != "" {
|
||||
settings["password"] = password
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是HTTPS,添加TLS配置
|
||||
if parsedURL.Scheme == "https" {
|
||||
settings["tls"] = true
|
||||
}
|
||||
|
||||
config.Settings = settings
|
||||
config.UDP = false
|
||||
|
||||
p.logger.Infof("Successfully parsed HTTP configuration for server: %s:%d", config.Server, config.Port)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Validate 验证HTTP配置
|
||||
func (p *HTTPParser) Validate(config *ProxyConfig) error {
|
||||
p.logger.Debugf("Validating HTTP 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")
|
||||
}
|
||||
|
||||
// 验证TLS配置
|
||||
tlsEnabled := p.getTLS(config)
|
||||
if tlsEnabled && config.Port != 443 {
|
||||
p.logger.Warnf("HTTPS proxy detected but port is not 443: %d", config.Port)
|
||||
}
|
||||
|
||||
p.logger.Debug("HTTP configuration validation passed")
|
||||
return nil
|
||||
}
|
||||
|
||||
// getUsername 从配置中获取用户名
|
||||
func (p *HTTPParser) getUsername(config *ProxyConfig) string {
|
||||
if username, exists := config.Settings["username"]; exists {
|
||||
if usernameStr, ok := username.(string); ok {
|
||||
return usernameStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getPassword 从配置中获取密码
|
||||
func (p *HTTPParser) getPassword(config *ProxyConfig) string {
|
||||
if password, exists := config.Settings["password"]; exists {
|
||||
if passwordStr, ok := password.(string); ok {
|
||||
return passwordStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getTLS 从配置中获取TLS设置
|
||||
func (p *HTTPParser) getTLS(config *ProxyConfig) bool {
|
||||
if tls, exists := config.Settings["tls"]; exists {
|
||||
if tlsBool, ok := tls.(bool); ok {
|
||||
return tlsBool
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetSupportedProtocols 获取支持的协议
|
||||
func (p *HTTPParser) GetSupportedProtocols() []string {
|
||||
return []string{"http", "https"}
|
||||
}
|
||||
254
internal/parser/manager.go
Normal file
254
internal/parser/manager.go
Normal file
@@ -0,0 +1,254 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/subconverter-go/internal/config"
|
||||
"github.com/subconverter-go/internal/logging"
|
||||
)
|
||||
|
||||
// Parser 解析器接口
|
||||
// 定义了所有代理协议解析器必须实现的方法
|
||||
type Parser interface {
|
||||
// Parse 解析代理配置
|
||||
// input: 输入的代理配置字符串
|
||||
// 返回解析后的代理配置和错误信息
|
||||
Parse(input string) (*ProxyConfig, error)
|
||||
|
||||
// Validate 验证代理配置
|
||||
// config: 需要验证的代理配置
|
||||
// 返回验证结果和错误信息
|
||||
Validate(config *ProxyConfig) error
|
||||
|
||||
// GetSupportedProtocols 获取支持的协议
|
||||
// 返回该解析器支持的协议列表
|
||||
GetSupportedProtocols() []string
|
||||
}
|
||||
|
||||
// ProxyConfig 代理配置结构体
|
||||
// 包含所有代理协议的通用配置信息
|
||||
type ProxyConfig struct {
|
||||
// 基本信息
|
||||
Name string `json:"name"` // 代理名称
|
||||
Server string `json:"server"` // 服务器地址
|
||||
Port int `json:"port"` // 服务器端口
|
||||
Type string `json:"type"` // 代理类型
|
||||
|
||||
// 协议特定配置
|
||||
Protocol string `json:"protocol"` // 协议类型 (ss, ssr, vmess, trojan, http, socks5)
|
||||
Settings map[string]interface{} `json:"settings"` // 协议特定设置
|
||||
|
||||
// 扩展信息
|
||||
Remarks string `json:"remarks"` // 备注信息
|
||||
Group string `json:"group"` // 分组名称
|
||||
Location string `json:"location"` // 地理位置
|
||||
UDP bool `json:"udp"` // 是否支持UDP
|
||||
|
||||
// 性能和质量指标
|
||||
Latency int `json:"latency"` // 延迟 (ms)
|
||||
Score int `json:"score"` // 质量评分
|
||||
}
|
||||
|
||||
// ParserManager 解析器管理器
|
||||
// 管理所有代理协议解析器,提供统一的解析接口
|
||||
type ParserManager struct {
|
||||
logger *logging.Logger
|
||||
configMgr *config.ConfigManager
|
||||
parsers map[string]Parser // 协议名称到解析器的映射
|
||||
}
|
||||
|
||||
// NewParserManager 创建新的解析器管理器
|
||||
// 返回初始化好的ParserManager实例
|
||||
func NewParserManager(logger *logging.Logger, configMgr *config.ConfigManager) *ParserManager {
|
||||
pm := &ParserManager{
|
||||
logger: logger,
|
||||
configMgr: configMgr,
|
||||
parsers: make(map[string]Parser),
|
||||
}
|
||||
|
||||
// 注册所有解析器
|
||||
pm.registerParsers()
|
||||
|
||||
return pm
|
||||
}
|
||||
|
||||
// registerParsers 注册所有解析器
|
||||
// 将各种代理协议解析器注册到管理器中
|
||||
func (pm *ParserManager) registerParsers() {
|
||||
// 注册Shadowsocks解析器
|
||||
pm.parsers["ss"] = NewShadowsocksParser(pm.logger)
|
||||
|
||||
// 注册ShadowsocksR解析器
|
||||
pm.parsers["ssr"] = NewShadowsocksRParser(pm.logger)
|
||||
|
||||
// 注册VMess解析器
|
||||
pm.parsers["vmess"] = NewVMessParser(pm.logger)
|
||||
|
||||
// 注册Trojan解析器
|
||||
pm.parsers["trojan"] = NewTrojanParser(pm.logger)
|
||||
|
||||
// 注册HTTP解析器
|
||||
pm.parsers["http"] = NewHTTPParser(pm.logger)
|
||||
|
||||
// 注册Socks5解析器
|
||||
pm.parsers["socks5"] = NewSocks5Parser(pm.logger)
|
||||
|
||||
pm.logger.Info("Registered parsers: ss, ssr, vmess, trojan, http, socks5")
|
||||
}
|
||||
|
||||
// Parse 解析代理配置
|
||||
// 自动识别协议并调用相应的解析器
|
||||
func (pm *ParserManager) Parse(input string) (*ProxyConfig, error) {
|
||||
pm.logger.Debugf("Parsing proxy configuration: %s", input[:min(len(input), 50)]+"...")
|
||||
|
||||
// 尝试自动检测协议
|
||||
protocol, err := pm.detectProtocol(input)
|
||||
if err != nil {
|
||||
pm.logger.WithError(err).Error("Failed to detect protocol")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取对应的解析器
|
||||
parser, exists := pm.parsers[protocol]
|
||||
if !exists {
|
||||
pm.logger.Errorf("No parser found for protocol: %s", protocol)
|
||||
return nil, fmt.Errorf("unsupported protocol: %s", protocol)
|
||||
}
|
||||
|
||||
// 调用解析器进行解析
|
||||
config, err := parser.Parse(input)
|
||||
if err != nil {
|
||||
pm.logger.WithError(err).Errorf("Failed to parse %s configuration", protocol)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 设置协议类型
|
||||
config.Protocol = protocol
|
||||
|
||||
pm.logger.Infof("Successfully parsed %s configuration for server: %s:%d", protocol, config.Server, config.Port)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// detectProtocol 检测输入字符串的协议类型
|
||||
// 通过前缀和格式识别代理协议
|
||||
func (pm *ParserManager) detectProtocol(input string) (string, error) {
|
||||
// 去除前后空格
|
||||
input = strings.TrimSpace(input)
|
||||
|
||||
lower := strings.ToLower(input)
|
||||
|
||||
// 检查URL格式
|
||||
switch {
|
||||
case strings.HasPrefix(lower, "ss://"):
|
||||
return "ss", nil
|
||||
case strings.HasPrefix(lower, "ssr://"):
|
||||
return "ssr", nil
|
||||
case strings.HasPrefix(lower, "vmess://"), strings.HasPrefix(lower, "vmess1://"):
|
||||
return "vmess", nil
|
||||
case strings.HasPrefix(lower, "trojan://"):
|
||||
return "trojan", nil
|
||||
case strings.HasPrefix(lower, "socks5://"), strings.HasPrefix(lower, "socks://"), strings.HasPrefix(lower, "https://t.me/socks"), strings.HasPrefix(lower, "tg://socks"):
|
||||
return "socks5", nil
|
||||
case strings.HasPrefix(lower, "http://"), strings.HasPrefix(lower, "https://"), strings.HasPrefix(lower, "tg://http"), strings.HasPrefix(lower, "https://t.me/http"):
|
||||
return "http", nil
|
||||
}
|
||||
|
||||
// 如果没有明确的协议前缀,尝试从内容推断
|
||||
if strings.Contains(lower, "ssr://") {
|
||||
return "ssr", nil
|
||||
}
|
||||
if strings.Contains(lower, "vmess://") || strings.Contains(lower, "vmess1://") {
|
||||
return "vmess", nil
|
||||
}
|
||||
if strings.Contains(lower, "ss://") {
|
||||
return "ss", nil
|
||||
}
|
||||
if strings.Contains(lower, "trojan://") {
|
||||
return "trojan", nil
|
||||
}
|
||||
if strings.Contains(lower, "socks5://") || strings.Contains(lower, "socks://") {
|
||||
return "socks5", nil
|
||||
}
|
||||
if strings.Contains(lower, "http://") || strings.Contains(lower, "https://") {
|
||||
return "http", nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("unknown protocol for input: %s", input)
|
||||
}
|
||||
|
||||
// GetSupportedProtocols 获取所有支持的协议
|
||||
// 返回管理器支持的所有代理协议列表
|
||||
func (pm *ParserManager) GetSupportedProtocols() []string {
|
||||
protocols := make([]string, 0, len(pm.parsers))
|
||||
for protocol := range pm.parsers {
|
||||
protocols = append(protocols, protocol)
|
||||
}
|
||||
|
||||
// 按字母顺序排序
|
||||
sort.Strings(protocols)
|
||||
return protocols
|
||||
}
|
||||
|
||||
// GetParser 获取指定协议的解析器
|
||||
// 返回对应协议的解析器实例
|
||||
func (pm *ParserManager) GetParser(protocol string) (Parser, error) {
|
||||
parser, exists := pm.parsers[protocol]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("parser not found for protocol: %s", protocol)
|
||||
}
|
||||
return parser, nil
|
||||
}
|
||||
|
||||
// Validate 验证代理配置
|
||||
// 使用对应的解析器验证配置的有效性
|
||||
func (pm *ParserManager) Validate(config *ProxyConfig) error {
|
||||
parser, err := pm.GetParser(config.Protocol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return parser.Validate(config)
|
||||
}
|
||||
|
||||
// ParseBatch 批量解析代理配置
|
||||
// 支持从包含多个代理配置的字符串中解析所有配置
|
||||
func (pm *ParserManager) ParseBatch(input string) ([]*ProxyConfig, error) {
|
||||
pm.logger.Debugf("Parsing batch proxy configuration")
|
||||
|
||||
// 按行分割输入
|
||||
lines := strings.Split(input, "\n")
|
||||
var configs []*ProxyConfig
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 尝试解析每一行
|
||||
config, err := pm.Parse(line)
|
||||
if err != nil {
|
||||
pm.logger.WithError(err).Warnf("Failed to parse line: %s", line)
|
||||
continue
|
||||
}
|
||||
|
||||
configs = append(configs, config)
|
||||
}
|
||||
|
||||
if len(configs) == 0 {
|
||||
return nil, fmt.Errorf("no valid proxy configurations found")
|
||||
}
|
||||
|
||||
pm.logger.Infof("Successfully parsed %d proxy configurations", len(configs))
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// 辅助函数
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
280
internal/parser/shadowsocks.go
Normal file
280
internal/parser/shadowsocks.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/subconverter-go/internal/logging"
|
||||
)
|
||||
|
||||
// ShadowsocksParser Shadowsocks协议解析器
|
||||
// 实现Shadowsocks代理配置的解析功能
|
||||
type ShadowsocksParser struct {
|
||||
logger *logging.Logger
|
||||
}
|
||||
|
||||
// NewShadowsocksParser 创建新的Shadowsocks解析器
|
||||
// 返回初始化好的ShadowsocksParser实例
|
||||
func NewShadowsocksParser(logger *logging.Logger) *ShadowsocksParser {
|
||||
return &ShadowsocksParser{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse 解析Shadowsocks配置
|
||||
// 支持标准格式和Base64编码格式
|
||||
func (p *ShadowsocksParser) Parse(input string) (*ProxyConfig, error) {
|
||||
p.logger.Debugf("Parsing Shadowsocks configuration")
|
||||
|
||||
// 去除前缀
|
||||
input = strings.TrimPrefix(input, "ss://")
|
||||
|
||||
// 解码Base64部分
|
||||
var decodedBytes []byte
|
||||
var err error
|
||||
|
||||
// 尝试直接解析
|
||||
if strings.Contains(input, "@") {
|
||||
decodedBytes = []byte(input)
|
||||
} else {
|
||||
// 尝试Base64解码
|
||||
decodedBytes, err = base64.StdEncoding.DecodeString(input)
|
||||
if err != nil {
|
||||
// 尝试URL安全的Base64解码
|
||||
decodedBytes, err = base64.URLEncoding.DecodeString(input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode Shadowsocks configuration: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decoded := string(decodedBytes)
|
||||
|
||||
// 解析方法:密码@服务器:端口#备注
|
||||
parts := strings.Split(decoded, "@")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("invalid Shadowsocks configuration format")
|
||||
}
|
||||
|
||||
// 解析认证信息
|
||||
authInfo := parts[0]
|
||||
serverInfo := parts[1]
|
||||
|
||||
// 解析服务器信息
|
||||
serverParts := strings.Split(serverInfo, "#")
|
||||
if len(serverParts) == 0 {
|
||||
return nil, fmt.Errorf("invalid server information")
|
||||
}
|
||||
|
||||
// 解析地址和端口
|
||||
addrParts := strings.Split(serverParts[0], ":")
|
||||
if len(addrParts) != 2 {
|
||||
return nil, fmt.Errorf("invalid server address format")
|
||||
}
|
||||
|
||||
config := &ProxyConfig{
|
||||
Type: "ss",
|
||||
}
|
||||
|
||||
// 解析认证信息
|
||||
authParts := strings.Split(authInfo, ":")
|
||||
if len(authParts) == 1 {
|
||||
// 可能是完整的URL格式
|
||||
return p.parseURLFormat(input)
|
||||
}
|
||||
|
||||
if len(authParts) != 2 {
|
||||
return nil, fmt.Errorf("invalid authentication information format")
|
||||
}
|
||||
|
||||
// 创建设置映射
|
||||
settings := map[string]interface{}{
|
||||
"method": authParts[0],
|
||||
"password": authParts[1],
|
||||
"udp": true,
|
||||
}
|
||||
|
||||
// 设置配置
|
||||
config.Server = addrParts[0]
|
||||
|
||||
// 解析端口
|
||||
port, err := strconv.Atoi(addrParts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid port number: %v", err)
|
||||
}
|
||||
config.Port = port
|
||||
|
||||
// 解析备注
|
||||
if len(serverParts) > 1 {
|
||||
config.Name = serverParts[1]
|
||||
} else {
|
||||
config.Name = fmt.Sprintf("ss-%s:%d", config.Server, config.Port)
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
config.UDP = true
|
||||
config.Protocol = "ss"
|
||||
config.Settings = settings
|
||||
|
||||
p.logger.Infof("Successfully parsed Shadowsocks configuration for server: %s:%d", config.Server, config.Port)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// parseURLFormat 解析URL格式的Shadowsocks配置
|
||||
func (p *ShadowsocksParser) parseURLFormat(input string) (*ProxyConfig, error) {
|
||||
// 添加协议前缀
|
||||
if !strings.HasPrefix(input, "ss://") {
|
||||
input = "ss://" + input
|
||||
}
|
||||
|
||||
// 解析URL
|
||||
parsedURL, err := url.Parse(input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Shadowsocks URL: %v", err)
|
||||
}
|
||||
|
||||
config := &ProxyConfig{
|
||||
Type: "ss",
|
||||
}
|
||||
|
||||
// 解码用户信息
|
||||
userInfoBytes, err := base64.StdEncoding.DecodeString(parsedURL.User.String())
|
||||
if err != nil {
|
||||
// 尝试URL安全的Base64解码
|
||||
userInfoBytes, err = base64.URLEncoding.DecodeString(parsedURL.User.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode user info: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
userInfo := string(userInfoBytes)
|
||||
|
||||
// 解析方法和密码
|
||||
authParts := strings.Split(userInfo, ":")
|
||||
if len(authParts) != 2 {
|
||||
return nil, fmt.Errorf("invalid authentication information")
|
||||
}
|
||||
|
||||
config.Server = parsedURL.Hostname()
|
||||
|
||||
// 解析端口
|
||||
port, err := strconv.Atoi(parsedURL.Port())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid port number: %v", err)
|
||||
}
|
||||
config.Port = port
|
||||
|
||||
// 解析查询参数
|
||||
query := parsedURL.Query()
|
||||
if plugin := query.Get("plugin"); plugin != "" {
|
||||
config.Settings = map[string]interface{}{
|
||||
"method": authParts[0],
|
||||
"password": authParts[1],
|
||||
"plugin": plugin,
|
||||
"udp": true,
|
||||
}
|
||||
} else {
|
||||
config.Settings = map[string]interface{}{
|
||||
"method": authParts[0],
|
||||
"password": authParts[1],
|
||||
"udp": true,
|
||||
}
|
||||
}
|
||||
|
||||
// 解析备注
|
||||
if parsedURL.Fragment != "" {
|
||||
config.Name = parsedURL.Fragment
|
||||
} else {
|
||||
config.Name = fmt.Sprintf("ss-%s:%d", config.Server, config.Port)
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
config.UDP = true
|
||||
config.Protocol = "ss"
|
||||
|
||||
p.logger.Infof("Successfully parsed Shadowsocks URL configuration for server: %s:%d", config.Server, config.Port)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Validate 验证Shadowsocks配置
|
||||
func (p *ShadowsocksParser) Validate(config *ProxyConfig) error {
|
||||
p.logger.Debugf("Validating Shadowsocks configuration")
|
||||
|
||||
// 检查必要字段
|
||||
method := p.getMethod(config)
|
||||
if method == "" {
|
||||
return fmt.Errorf("encryption method is required")
|
||||
}
|
||||
|
||||
password := p.getPassword(config)
|
||||
if password == "" {
|
||||
return fmt.Errorf("password is required")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 验证加密方法
|
||||
supportedMethods := []string{
|
||||
"aes-256-cfb", "aes-128-cfb", "aes-192-cfb",
|
||||
"aes-256-ctr", "aes-128-ctr", "aes-192-ctr",
|
||||
"aes-256-gcm", "aes-128-gcm", "aes-192-gcm",
|
||||
"chacha20-ietf-poly1305", "xchacha20-ietf-poly1305",
|
||||
"rc4-md5", "salsa20", "chacha20",
|
||||
}
|
||||
|
||||
methodSupported := false
|
||||
for _, method := range supportedMethods {
|
||||
if method == method {
|
||||
methodSupported = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !methodSupported {
|
||||
p.logger.Warnf("Unsupported encryption method: %s", method)
|
||||
}
|
||||
|
||||
// 验证插件配置
|
||||
if plugin, exists := config.Settings["plugin"]; exists {
|
||||
if pluginStr, ok := plugin.(string); ok && pluginStr != "" {
|
||||
p.logger.Debugf("Shadowsocks plugin detected: %s", pluginStr)
|
||||
}
|
||||
}
|
||||
|
||||
p.logger.Debug("Shadowsocks configuration validation passed")
|
||||
return nil
|
||||
}
|
||||
|
||||
// getMethod 从配置中获取加密方法
|
||||
func (p *ShadowsocksParser) getMethod(config *ProxyConfig) string {
|
||||
if method, exists := config.Settings["method"]; exists {
|
||||
if methodStr, ok := method.(string); ok {
|
||||
return methodStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getPassword 从配置中获取密码
|
||||
func (p *ShadowsocksParser) getPassword(config *ProxyConfig) string {
|
||||
if password, exists := config.Settings["password"]; exists {
|
||||
if passwordStr, ok := password.(string); ok {
|
||||
return passwordStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetSupportedProtocols 获取支持的协议
|
||||
func (p *ShadowsocksParser) GetSupportedProtocols() []string {
|
||||
return []string{"ss"}
|
||||
}
|
||||
278
internal/parser/shadowsocksr.go
Normal file
278
internal/parser/shadowsocksr.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/subconverter-go/internal/logging"
|
||||
)
|
||||
|
||||
// ShadowsocksRParser ShadowsocksR协议解析器
|
||||
// 实现ShadowsocksR代理配置的解析功能
|
||||
type ShadowsocksRParser struct {
|
||||
logger *logging.Logger
|
||||
}
|
||||
|
||||
// NewShadowsocksRParser 创建新的ShadowsocksR解析器
|
||||
// 返回初始化好的ShadowsocksRParser实例
|
||||
func NewShadowsocksRParser(logger *logging.Logger) *ShadowsocksRParser {
|
||||
return &ShadowsocksRParser{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse 解析ShadowsocksR配置
|
||||
// 支持标准格式和Base64编码格式
|
||||
func (p *ShadowsocksRParser) Parse(input string) (*ProxyConfig, error) {
|
||||
p.logger.Debugf("Parsing ShadowsocksR configuration")
|
||||
|
||||
// 去除前缀
|
||||
input = strings.TrimPrefix(input, "ssr://")
|
||||
|
||||
// 解码Base64
|
||||
decoded, err := base64.StdEncoding.DecodeString(input)
|
||||
if err != nil {
|
||||
// 尝试URL安全的Base64解码
|
||||
decoded, err = base64.URLEncoding.DecodeString(input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode ShadowsocksR configuration: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 解析配置字符串: server:port:protocol:method:obfs:password_base64/remarks_base64/group_base64/obfsparam_base64/protocolparam_base64
|
||||
parts := strings.Split(string(decoded), "/")
|
||||
if len(parts) < 1 {
|
||||
return nil, fmt.Errorf("invalid ShadowsocksR configuration format")
|
||||
}
|
||||
|
||||
// 解析主要配置
|
||||
mainParts := strings.Split(parts[0], ":")
|
||||
if len(mainParts) < 6 {
|
||||
return nil, fmt.Errorf("invalid ShadowsocksR main configuration format")
|
||||
}
|
||||
|
||||
config := &ProxyConfig{
|
||||
Type: "ssr",
|
||||
Protocol: "ssr",
|
||||
}
|
||||
|
||||
// 解析服务器和端口
|
||||
config.Server = mainParts[0]
|
||||
port, err := strconv.Atoi(mainParts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid port number: %v", err)
|
||||
}
|
||||
config.Port = port
|
||||
|
||||
// 解码密码
|
||||
password, err := base64.StdEncoding.DecodeString(mainParts[5])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode password: %v", err)
|
||||
}
|
||||
|
||||
// 创建设置映射
|
||||
settings := map[string]interface{}{
|
||||
"protocol": mainParts[2],
|
||||
"method": mainParts[3],
|
||||
"obfs": mainParts[4],
|
||||
"password": string(password),
|
||||
"udp": true,
|
||||
"protocolParam": "",
|
||||
"obfsParam": "",
|
||||
}
|
||||
|
||||
// 解析可选参数
|
||||
if len(parts) > 1 {
|
||||
// 解析备注
|
||||
remarks, err := base64.StdEncoding.DecodeString(parts[1])
|
||||
if err == nil {
|
||||
config.Name = string(remarks)
|
||||
}
|
||||
|
||||
// 解析分组
|
||||
if len(parts) > 2 {
|
||||
group, err := base64.StdEncoding.DecodeString(parts[2])
|
||||
if err == nil {
|
||||
config.Group = string(group)
|
||||
}
|
||||
}
|
||||
|
||||
// 解析obfs参数
|
||||
if len(parts) > 3 {
|
||||
obfsParam, err := base64.StdEncoding.DecodeString(parts[3])
|
||||
if err == nil {
|
||||
settings["obfsParam"] = string(obfsParam)
|
||||
}
|
||||
}
|
||||
|
||||
// 解析protocol参数
|
||||
if len(parts) > 4 {
|
||||
protocolParam, err := base64.StdEncoding.DecodeString(parts[4])
|
||||
if err == nil {
|
||||
settings["protocolParam"] = string(protocolParam)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置默认名称
|
||||
if config.Name == "" {
|
||||
config.Name = fmt.Sprintf("ssr-%s:%d", config.Server, config.Port)
|
||||
}
|
||||
|
||||
config.UDP = true
|
||||
config.Settings = settings
|
||||
|
||||
p.logger.Infof("Successfully parsed ShadowsocksR configuration for server: %s:%d", config.Server, config.Port)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Validate 验证ShadowsocksR配置
|
||||
func (p *ShadowsocksRParser) Validate(config *ProxyConfig) error {
|
||||
p.logger.Debugf("Validating ShadowsocksR 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)
|
||||
}
|
||||
|
||||
method := p.getMethod(config)
|
||||
if method == "" {
|
||||
return fmt.Errorf("encryption method is required")
|
||||
}
|
||||
|
||||
password := p.getPassword(config)
|
||||
if password == "" {
|
||||
return fmt.Errorf("password is required")
|
||||
}
|
||||
|
||||
protocol := p.getProtocol(config)
|
||||
if protocol == "" {
|
||||
return fmt.Errorf("protocol is required")
|
||||
}
|
||||
|
||||
obfs := p.getObfs(config)
|
||||
if obfs == "" {
|
||||
return fmt.Errorf("obfs is required")
|
||||
}
|
||||
|
||||
// 验证加密方法
|
||||
if err := p.validateMethod(method); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证协议
|
||||
if err := p.validateProtocol(protocol); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证混淆
|
||||
if err := p.validateObfs(obfs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.logger.Debug("ShadowsocksR configuration validation passed")
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateMethod 验证加密方法
|
||||
func (p *ShadowsocksRParser) validateMethod(method string) error {
|
||||
supportedMethods := []string{
|
||||
"none", "table", "rc4", "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb",
|
||||
"aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb",
|
||||
"camellia-192-cfb", "camellia-256-cfb", "cast5-cfb", "des-cfb", "idea-cfb",
|
||||
"rc2-cfb", "seed-cfb", "salsa20", "chacha20", "chacha20-ietf",
|
||||
}
|
||||
|
||||
for _, supported := range supportedMethods {
|
||||
if method == supported {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
p.logger.Warnf("Unsupported encryption method: %s", method)
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateProtocol 验证协议
|
||||
func (p *ShadowsocksRParser) validateProtocol(protocol string) error {
|
||||
supportedProtocols := []string{
|
||||
"origin", "verify_deflate", "auth_sha1_v4", "auth_aes128_md5",
|
||||
"auth_aes128_sha1", "auth_chain_a", "auth_chain_b",
|
||||
}
|
||||
|
||||
for _, supported := range supportedProtocols {
|
||||
if protocol == supported {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
p.logger.Warnf("Unsupported protocol: %s", protocol)
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateObfs 验证混淆
|
||||
func (p *ShadowsocksRParser) validateObfs(obfs string) error {
|
||||
supportedObfs := []string{
|
||||
"plain", "http_simple", "http_post", "random_head", "tls1.2_ticket_auth",
|
||||
}
|
||||
|
||||
for _, supported := range supportedObfs {
|
||||
if obfs == supported {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
p.logger.Warnf("Unsupported obfs: %s", obfs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// getMethod 从配置中获取加密方法
|
||||
func (p *ShadowsocksRParser) getMethod(config *ProxyConfig) string {
|
||||
if method, exists := config.Settings["method"]; exists {
|
||||
if methodStr, ok := method.(string); ok {
|
||||
return methodStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getPassword 从配置中获取密码
|
||||
func (p *ShadowsocksRParser) getPassword(config *ProxyConfig) string {
|
||||
if password, exists := config.Settings["password"]; exists {
|
||||
if passwordStr, ok := password.(string); ok {
|
||||
return passwordStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getProtocol 从配置中获取协议
|
||||
func (p *ShadowsocksRParser) getProtocol(config *ProxyConfig) string {
|
||||
if protocol, exists := config.Settings["protocol"]; exists {
|
||||
if protocolStr, ok := protocol.(string); ok {
|
||||
return protocolStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getObfs 从配置中获取混淆
|
||||
func (p *ShadowsocksRParser) getObfs(config *ProxyConfig) string {
|
||||
if obfs, exists := config.Settings["obfs"]; exists {
|
||||
if obfsStr, ok := obfs.(string); ok {
|
||||
return obfsStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetSupportedProtocols 获取支持的协议
|
||||
func (p *ShadowsocksRParser) GetSupportedProtocols() []string {
|
||||
return []string{"ssr"}
|
||||
}
|
||||
214
internal/parser/socks.go
Normal file
214
internal/parser/socks.go
Normal file
@@ -0,0 +1,214 @@
|
||||
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"}
|
||||
}
|
||||
266
internal/parser/trojan.go
Normal file
266
internal/parser/trojan.go
Normal file
@@ -0,0 +1,266 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/subconverter-go/internal/logging"
|
||||
)
|
||||
|
||||
// TrojanParser Trojan协议解析器
|
||||
// 实现Trojan代理配置的解析功能
|
||||
type TrojanParser struct {
|
||||
logger *logging.Logger
|
||||
}
|
||||
|
||||
// NewTrojanParser 创建新的Trojan解析器
|
||||
// 返回初始化好的TrojanParser实例
|
||||
func NewTrojanParser(logger *logging.Logger) *TrojanParser {
|
||||
return &TrojanParser{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Parse 解析Trojan配置
|
||||
// 支持标准URL格式
|
||||
func (p *TrojanParser) Parse(input string) (*ProxyConfig, error) {
|
||||
p.logger.Debugf("Parsing Trojan configuration")
|
||||
|
||||
// 去除前缀
|
||||
input = strings.TrimPrefix(input, "trojan://")
|
||||
|
||||
// 解析URL
|
||||
parsedURL, err := url.Parse("trojan://" + input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Trojan URL: %v", err)
|
||||
}
|
||||
|
||||
// 获取密码
|
||||
password := parsedURL.User.String()
|
||||
if password == "" {
|
||||
return nil, fmt.Errorf("password is required")
|
||||
}
|
||||
|
||||
config := &ProxyConfig{
|
||||
Type: "trojan",
|
||||
Protocol: "trojan",
|
||||
Server: parsedURL.Hostname(),
|
||||
}
|
||||
|
||||
// 解析端口
|
||||
port, err := strconv.Atoi(parsedURL.Port())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid port number: %v", err)
|
||||
}
|
||||
config.Port = port
|
||||
|
||||
// 解析备注
|
||||
if parsedURL.Fragment != "" {
|
||||
config.Name = parsedURL.Fragment
|
||||
} else {
|
||||
config.Name = fmt.Sprintf("trojan-%s:%d", config.Server, config.Port)
|
||||
}
|
||||
|
||||
// 创建设置映射
|
||||
settings := map[string]interface{}{
|
||||
"password": password,
|
||||
"udp": true,
|
||||
}
|
||||
|
||||
// 解析查询参数
|
||||
query := parsedURL.Query()
|
||||
|
||||
// 解析allowInsecure
|
||||
if allowInsecure := query.Get("allowInsecure"); allowInsecure != "" {
|
||||
settings["allowInsecure"] = p.parseBool(allowInsecure)
|
||||
}
|
||||
|
||||
// 解析sni
|
||||
if sni := query.Get("sni"); sni != "" {
|
||||
settings["sni"] = sni
|
||||
}
|
||||
|
||||
// 解析peer
|
||||
if peer := query.Get("peer"); peer != "" {
|
||||
settings["peer"] = peer
|
||||
}
|
||||
|
||||
// 解析network类型
|
||||
network := query.Get("type")
|
||||
if network == "" {
|
||||
network = "tcp"
|
||||
}
|
||||
|
||||
// 添加网络特定配置
|
||||
switch network {
|
||||
case "ws":
|
||||
settings["network"] = "ws"
|
||||
wsSettings := map[string]interface{}{
|
||||
"path": "/",
|
||||
}
|
||||
|
||||
if host := query.Get("host"); host != "" {
|
||||
wsSettings["headers"] = map[string]string{
|
||||
"Host": host,
|
||||
}
|
||||
}
|
||||
|
||||
if path := query.Get("path"); path != "" {
|
||||
wsSettings["path"] = path
|
||||
}
|
||||
|
||||
settings["wsSettings"] = wsSettings
|
||||
|
||||
case "grpc":
|
||||
settings["network"] = "grpc"
|
||||
grpcSettings := map[string]interface{}{
|
||||
"serviceName": query.Get("serviceName"),
|
||||
"multiMode": false,
|
||||
}
|
||||
|
||||
if service := query.Get("serviceName"); service != "" {
|
||||
grpcSettings["serviceName"] = service
|
||||
}
|
||||
|
||||
settings["grpcSettings"] = grpcSettings
|
||||
|
||||
case "kcp":
|
||||
settings["network"] = "kcp"
|
||||
kcpSettings := map[string]interface{}{
|
||||
"header": map[string]string{
|
||||
"type": query.Get("headerType"),
|
||||
},
|
||||
}
|
||||
|
||||
if headerType := query.Get("headerType"); headerType != "" {
|
||||
kcpSettings["header"].(map[string]string)["type"] = headerType
|
||||
}
|
||||
|
||||
settings["kcpSettings"] = kcpSettings
|
||||
|
||||
default:
|
||||
settings["network"] = "tcp"
|
||||
}
|
||||
|
||||
// 解析TLS配置
|
||||
if network != "tcp" || query.Get("security") == "tls" {
|
||||
settings["security"] = "tls"
|
||||
}
|
||||
|
||||
config.Settings = settings
|
||||
config.UDP = true
|
||||
|
||||
p.logger.Infof("Successfully parsed Trojan configuration for server: %s:%d", config.Server, config.Port)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// parseBool 解析布尔值
|
||||
func (p *TrojanParser) parseBool(value string) bool {
|
||||
switch strings.ToLower(value) {
|
||||
case "1", "true", "yes", "on":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Validate 验证Trojan配置
|
||||
func (p *TrojanParser) Validate(config *ProxyConfig) error {
|
||||
p.logger.Debugf("Validating Trojan 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)
|
||||
}
|
||||
|
||||
password := p.getPassword(config)
|
||||
if password == "" {
|
||||
return fmt.Errorf("password is required")
|
||||
}
|
||||
|
||||
// 验证密码格式(应该是一个44字符的Base64编码字符串)
|
||||
if len(password) != 44 {
|
||||
p.logger.Warnf("Trojan password length is not 44 characters: %d", len(password))
|
||||
}
|
||||
|
||||
// 验证网络协议
|
||||
network := p.getNetwork(config)
|
||||
if err := p.validateNetwork(network); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证SNI(如果启用TLS)
|
||||
if p.getSecurity(config) == "tls" {
|
||||
sni := p.getSNI(config)
|
||||
if sni == "" {
|
||||
p.logger.Debug("SNI not specified, using server address")
|
||||
}
|
||||
}
|
||||
|
||||
p.logger.Debug("Trojan configuration validation passed")
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateNetwork 验证网络协议
|
||||
func (p *TrojanParser) validateNetwork(network string) error {
|
||||
supportedNetworks := []string{"tcp", "ws", "grpc", "kcp"}
|
||||
|
||||
for _, supported := range supportedNetworks {
|
||||
if network == supported {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("unsupported network type: %s", network)
|
||||
}
|
||||
|
||||
// getPassword 从配置中获取密码
|
||||
func (p *TrojanParser) getPassword(config *ProxyConfig) string {
|
||||
if password, exists := config.Settings["password"]; exists {
|
||||
if passwordStr, ok := password.(string); ok {
|
||||
return passwordStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getNetwork 从配置中获取网络协议
|
||||
func (p *TrojanParser) getNetwork(config *ProxyConfig) string {
|
||||
if network, exists := config.Settings["network"]; exists {
|
||||
if networkStr, ok := network.(string); ok {
|
||||
return networkStr
|
||||
}
|
||||
}
|
||||
return "tcp"
|
||||
}
|
||||
|
||||
// getSecurity 从配置中获取安全设置
|
||||
func (p *TrojanParser) getSecurity(config *ProxyConfig) string {
|
||||
if security, exists := config.Settings["security"]; exists {
|
||||
if securityStr, ok := security.(string); ok {
|
||||
return securityStr
|
||||
}
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
|
||||
// getSNI 从配置中获取SNI
|
||||
func (p *TrojanParser) getSNI(config *ProxyConfig) string {
|
||||
if sni, exists := config.Settings["sni"]; exists {
|
||||
if sniStr, ok := sni.(string); ok {
|
||||
return sniStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetSupportedProtocols 获取支持的协议
|
||||
func (p *TrojanParser) GetSupportedProtocols() []string {
|
||||
return []string{"trojan"}
|
||||
}
|
||||
314
internal/parser/vmess.go
Normal file
314
internal/parser/vmess.go
Normal file
@@ -0,0 +1,314 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/subconverter-go/internal/logging"
|
||||
)
|
||||
|
||||
// VMessParser VMess协议解析器
|
||||
// 实现VMess代理配置的解析功能
|
||||
type VMessParser struct {
|
||||
logger *logging.Logger
|
||||
}
|
||||
|
||||
// NewVMessParser 创建新的VMess解析器
|
||||
// 返回初始化好的VMessParser实例
|
||||
func NewVMessParser(logger *logging.Logger) *VMessParser {
|
||||
return &VMessParser{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// VMessConfig VMess配置结构
|
||||
type VMessConfig struct {
|
||||
V string `json:"v"` // 版本
|
||||
PS string `json:"ps"` // 备注
|
||||
Add string `json:"add"` // 服务器地址
|
||||
Port string `json:"port"` // 服务器端口
|
||||
ID string `json:"id"` // 用户ID
|
||||
Aid string `json:"aid"` // 额外ID (alterId)
|
||||
Net string `json:"net"` // 传输协议
|
||||
Type string `json:"type"` // 伪装类型
|
||||
Host string `json:"host"` // 主机名/域名
|
||||
Path string `json:"path"` // 路径
|
||||
TLS string `json:"tls"` // TLS加密
|
||||
}
|
||||
|
||||
// Parse 解析VMess配置
|
||||
// 支持标准Base64编码格式
|
||||
func (p *VMessParser) Parse(input string) (*ProxyConfig, error) {
|
||||
p.logger.Debugf("Parsing VMess configuration")
|
||||
|
||||
lower := strings.ToLower(input)
|
||||
if strings.HasPrefix(lower, "vmess1://") {
|
||||
input = "vmess://" + input[len("vmess1://"):]
|
||||
}
|
||||
|
||||
// 去除前缀
|
||||
input = strings.TrimPrefix(input, "vmess://")
|
||||
|
||||
// 解码Base64
|
||||
decoded, err := base64.StdEncoding.DecodeString(input)
|
||||
if err != nil {
|
||||
// 尝试URL安全的Base64解码
|
||||
decoded, err = base64.URLEncoding.DecodeString(input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode VMess configuration: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 解析JSON配置
|
||||
var vmessConfig VMessConfig
|
||||
if err := json.Unmarshal(decoded, &vmessConfig); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse VMess JSON configuration: %v", err)
|
||||
}
|
||||
|
||||
// 验证必要字段
|
||||
if vmessConfig.Add == "" || vmessConfig.Port == "" || vmessConfig.ID == "" {
|
||||
return nil, fmt.Errorf("invalid VMess configuration: missing required fields")
|
||||
}
|
||||
|
||||
config := &ProxyConfig{
|
||||
Type: "vmess",
|
||||
Protocol: "vmess",
|
||||
Server: vmessConfig.Add,
|
||||
}
|
||||
|
||||
// 解析端口
|
||||
port, err := strconv.Atoi(vmessConfig.Port)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid port number: %v", err)
|
||||
}
|
||||
config.Port = port
|
||||
|
||||
// 设置备注
|
||||
if vmessConfig.PS != "" {
|
||||
config.Name = vmessConfig.PS
|
||||
} else {
|
||||
config.Name = fmt.Sprintf("vmess-%s:%d", config.Server, config.Port)
|
||||
}
|
||||
|
||||
// 创建设置映射
|
||||
settings := map[string]interface{}{
|
||||
"uuid": vmessConfig.ID,
|
||||
"alterId": p.parseAlterId(vmessConfig.Aid),
|
||||
"network": p.parseNetwork(vmessConfig.Net),
|
||||
"tls": p.parseTLS(vmessConfig.TLS),
|
||||
"udp": true,
|
||||
}
|
||||
|
||||
// 添加网络特定配置
|
||||
switch vmessConfig.Net {
|
||||
case "ws":
|
||||
settings["wsSettings"] = map[string]interface{}{
|
||||
"path": p.parsePath(vmessConfig.Path),
|
||||
"headers": map[string]string{
|
||||
"Host": p.parseHost(vmessConfig.Host),
|
||||
},
|
||||
}
|
||||
case "http":
|
||||
settings["httpSettings"] = map[string]interface{}{
|
||||
"path": p.parsePath(vmessConfig.Path),
|
||||
"host": []string{p.parseHost(vmessConfig.Host)},
|
||||
}
|
||||
case "quic":
|
||||
settings["quicSettings"] = map[string]interface{}{
|
||||
"security": p.parseType(vmessConfig.Type),
|
||||
"key": p.parseHost(vmessConfig.Host),
|
||||
"header": map[string]string{
|
||||
"type": p.parseType(vmessConfig.Type),
|
||||
},
|
||||
}
|
||||
case "grpc":
|
||||
settings["grpcSettings"] = map[string]interface{}{
|
||||
"serviceName": p.parsePath(vmessConfig.Path),
|
||||
"multiMode": false,
|
||||
}
|
||||
}
|
||||
|
||||
config.Settings = settings
|
||||
config.UDP = true
|
||||
|
||||
p.logger.Infof("Successfully parsed VMess configuration for server: %s:%d", config.Server, config.Port)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// parseAlterId 解析额外ID
|
||||
func (p *VMessParser) parseAlterId(aid string) int {
|
||||
if aid == "" {
|
||||
return 0
|
||||
}
|
||||
alterId, err := strconv.Atoi(aid)
|
||||
if err != nil {
|
||||
p.logger.Warnf("Invalid alterId: %s, using default 0", aid)
|
||||
return 0
|
||||
}
|
||||
return alterId
|
||||
}
|
||||
|
||||
// parseNetwork 解析网络协议
|
||||
func (p *VMessParser) parseNetwork(net string) string {
|
||||
switch net {
|
||||
case "tcp", "kcp", "ws", "http", "quic", "grpc":
|
||||
return net
|
||||
default:
|
||||
p.logger.Warnf("Unknown network type: %s, using tcp", net)
|
||||
return "tcp"
|
||||
}
|
||||
}
|
||||
|
||||
// parseTLS 解析TLS配置
|
||||
func (p *VMessParser) parseTLS(tls string) string {
|
||||
switch tls {
|
||||
case "tls", "1", "true":
|
||||
return "tls"
|
||||
default:
|
||||
return "none"
|
||||
}
|
||||
}
|
||||
|
||||
// parseType 解析伪装类型
|
||||
func (p *VMessParser) parseType(typ string) string {
|
||||
if typ == "" {
|
||||
return "none"
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// parseHost 解析主机名
|
||||
func (p *VMessParser) parseHost(host string) string {
|
||||
if host == "" {
|
||||
return ""
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
// parsePath 解析路径
|
||||
func (p *VMessParser) parsePath(path string) string {
|
||||
if path == "" {
|
||||
return "/"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// Validate 验证VMess配置
|
||||
func (p *VMessParser) Validate(config *ProxyConfig) error {
|
||||
p.logger.Debugf("Validating VMess 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)
|
||||
}
|
||||
|
||||
uuid := p.getUUID(config)
|
||||
if uuid == "" {
|
||||
return fmt.Errorf("UUID is required")
|
||||
}
|
||||
|
||||
// 验证UUID格式
|
||||
if err := p.validateUUID(uuid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证网络协议
|
||||
network := p.getNetwork(config)
|
||||
if err := p.validateNetwork(network); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证额外ID
|
||||
alterId := p.getAlterId(config)
|
||||
if err := p.validateAlterId(alterId); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.logger.Debug("VMess configuration validation passed")
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateUUID 验证UUID格式
|
||||
func (p *VMessParser) validateUUID(uuid string) error {
|
||||
// 简单的UUID格式验证
|
||||
if len(uuid) != 36 {
|
||||
return fmt.Errorf("invalid UUID length: %d", len(uuid))
|
||||
}
|
||||
|
||||
if strings.Count(uuid, "-") != 4 {
|
||||
return fmt.Errorf("invalid UUID format: incorrect hyphen count")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateNetwork 验证网络协议
|
||||
func (p *VMessParser) validateNetwork(network string) error {
|
||||
supportedNetworks := []string{"tcp", "kcp", "ws", "http", "quic", "grpc"}
|
||||
|
||||
for _, supported := range supportedNetworks {
|
||||
if network == supported {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("unsupported network type: %s", network)
|
||||
}
|
||||
|
||||
// validateAlterId 验证额外ID
|
||||
func (p *VMessParser) validateAlterId(alterId int) error {
|
||||
if alterId < 0 {
|
||||
return fmt.Errorf("alterId cannot be negative: %d", alterId)
|
||||
}
|
||||
|
||||
if alterId > 65535 {
|
||||
return fmt.Errorf("alterId too large: %d", alterId)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getUUID 从配置中获取UUID
|
||||
func (p *VMessParser) getUUID(config *ProxyConfig) string {
|
||||
if uuid, exists := config.Settings["uuid"]; exists {
|
||||
if uuidStr, ok := uuid.(string); ok {
|
||||
return uuidStr
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getNetwork 从配置中获取网络协议
|
||||
func (p *VMessParser) getNetwork(config *ProxyConfig) string {
|
||||
if network, exists := config.Settings["network"]; exists {
|
||||
if networkStr, ok := network.(string); ok {
|
||||
return networkStr
|
||||
}
|
||||
}
|
||||
return "tcp"
|
||||
}
|
||||
|
||||
// getAlterId 从配置中获取额外ID
|
||||
func (p *VMessParser) getAlterId(config *ProxyConfig) int {
|
||||
if alterId, exists := config.Settings["alterId"]; exists {
|
||||
if alterIdInt, ok := alterId.(int); ok {
|
||||
return alterIdInt
|
||||
}
|
||||
if alterIdFloat, ok := alterId.(float64); ok {
|
||||
return int(alterIdFloat)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetSupportedProtocols 获取支持的协议
|
||||
func (p *VMessParser) GetSupportedProtocols() []string {
|
||||
return []string{"vmess"}
|
||||
}
|
||||
Reference in New Issue
Block a user