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:
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"}
|
||||
}
|
||||
Reference in New Issue
Block a user