Files
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

511 lines
12 KiB
Go
Raw Permalink 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 config
import (
"encoding/base64"
"encoding/json"
"fmt"
"net/url"
"strconv"
"strings"
)
// ProxyConfig 表示代理节点的配置
// 该结构体包含各种代理协议的通用和特定配置
type ProxyConfig struct {
// 基本信息
Name string `yaml:"name" json:"name"`
Type string `yaml:"type" json:"type"` // ss, ssr, vmess, trojan, http, socks5
// 服务器信息
Server string `yaml:"server" json:"server"`
Port int `yaml:"port" json:"port"`
Password string `yaml:"password" json:"password,omitempty"`
// 加密和协议
Method string `yaml:"method" json:"method,omitempty"` // ss, ssr
Protocol string `yaml:"protocol" json:"protocol,omitempty"` // ssr
Obfs string `yaml:"obfs" json:"obfs,omitempty"` // ssr
ObfsParam string `yaml:"obfs_param" json:"obfs_param,omitempty"` // ssr
SSRProtocol string `yaml:"ssr_protocol" json:"ssr_protocol,omitempty"` // ssr (兼容性字段)
// VMess特定配置
VMessConfig *VMessConfig `yaml:"vmess_config,omitempty" json:"vmess_config,omitempty"`
// TLS配置
TLS bool `yaml:"tls" json:"tls"`
SNI string `yaml:"sni" json:"sni,omitempty"`
Alpn string `yaml:"alpn" json:"alpn,omitempty"`
SkipVerify bool `yaml:"skip_verify" json:"skip_verify"`
// WebSocket配置
WSPath string `yaml:"ws_path" json:"ws_path,omitempty"`
WSHeaders map[string]string `yaml:"ws_headers" json:"ws_headers,omitempty"`
// HTTP/2配置
H2Host string `yaml:"h2_host" json:"h2_host,omitempty"`
H2Path string `yaml:"h2_path" json:"h2_path,omitempty"`
// QUIC配置
QUICSecurity string `yaml:"quic_security" json:"quic_security,omitempty"`
QUICKey string `yaml:"quic_key" json:"quic_key,omitempty"`
// gRPC配置
GRPCServiceName string `yaml:"grpc_service_name" json:"grpc_service_name,omitempty"`
GRPCMultiMode bool `yaml:"grpc_multi_mode" json:"grpc_multi_mode,omitempty"`
// 其他配置
UDP bool `yaml:"udp" json:"udp"`
TFO bool `yaml:"tfo" json:"tfo"` // TCP Fast Open
Mux bool `yaml:"mux" json:"mux"` // 多路复用
Scramble bool `yaml:"scramble" json:"scramble"`
Plugin string `yaml:"plugin" json:"plugin,omitempty"`
PluginOpts string `yaml:"plugin_opts" json:"plugin_opts,omitempty"`
// 节点属性
Location string `yaml:"location" json:"location,omitempty"`
Provider string `yaml:"provider" json:"provider,omitempty"`
Ping int `yaml:"ping" json:"ping,omitempty"` // 延迟(ms)
}
// VMessConfig VMess协议特定配置
type VMessConfig struct {
VMessUUID string `yaml:"vmess_uuid" json:"vmess_uuid"`
VMessAlterID string `yaml:"vmess_alter_id" json:"vmess_alter_id"`
VMessSecurity string `yaml:"vmess_security" json:"vmess_security"`
}
// NewProxyConfig 创建新的代理配置
// 返回包含基本默认值的ProxyConfig结构体
func NewProxyConfig() *ProxyConfig {
return &ProxyConfig{
Name: "",
Type: "",
Server: "",
Port: 0,
Password: "",
Method: "",
Protocol: "",
Obfs: "",
ObfsParam: "",
TLS: false,
SNI: "",
Alpn: "",
SkipVerify: false,
WSPath: "",
WSHeaders: make(map[string]string),
H2Host: "",
H2Path: "",
QUICSecurity: "",
QUICKey: "",
GRPCServiceName: "",
GRPCMultiMode: false,
UDP: false,
TFO: false,
Mux: false,
Scramble: false,
Plugin: "",
PluginOpts: "",
Location: "",
Provider: "",
Ping: 0,
}
}
// Validate 验证代理配置的有效性
// 返回error如果配置无效nil表示配置有效
func (p *ProxyConfig) Validate() error {
// 基本字段验证
if p.Name == "" {
return fmt.Errorf("proxy name cannot be empty")
}
if p.Type == "" {
return fmt.Errorf("proxy type cannot be empty")
}
if p.Server == "" {
return fmt.Errorf("proxy server cannot be empty")
}
if p.Port < 1 || p.Port > 65535 {
return fmt.Errorf("proxy port must be between 1 and 65535, got: %d", p.Port)
}
// 根据类型验证特定字段
switch strings.ToLower(p.Type) {
case "ss", "shadowsocks":
if p.Password == "" {
return fmt.Errorf("shadowsocks password cannot be empty")
}
if p.Method == "" {
return fmt.Errorf("shadowsocks method cannot be empty")
}
case "ssr", "shadowsocksr":
if p.Password == "" {
return fmt.Errorf("shadowsocksr password cannot be empty")
}
if p.Method == "" {
return fmt.Errorf("shadowsocksr method cannot be empty")
}
if p.Protocol == "" {
return fmt.Errorf("shadowsocksr protocol cannot be empty")
}
case "vmess":
if p.VMessConfig == nil || p.VMessConfig.VMessUUID == "" {
return fmt.Errorf("vmess uuid cannot be empty")
}
if p.VMessConfig.VMessAlterID == "" {
return fmt.Errorf("vmess alter id cannot be empty")
}
if p.VMessConfig.VMessSecurity == "" {
return fmt.Errorf("vmess security cannot be empty")
}
case "trojan":
if p.Password == "" {
return fmt.Errorf("trojan password cannot be empty")
}
case "http", "https":
if p.Password == "" && p.Type == "https" {
return fmt.Errorf("https proxy requires authentication")
}
case "socks5":
// SOCKS5可以无认证
default:
return fmt.Errorf("unsupported proxy type: %s", p.Type)
}
// TLS配置验证
if p.TLS {
if p.SNI == "" {
p.SNI = p.Server // 默认使用服务器地址作为SNI
}
}
// WebSocket配置验证
if p.WSPath != "" && p.WSHeaders == nil {
p.WSHeaders = make(map[string]string)
}
// HTTP/2配置验证
if p.H2Path != "" && p.H2Host == "" {
return fmt.Errorf("h2 host cannot be empty when h2 path is specified")
}
// QUIC配置验证
if p.QUICKey != "" && p.QUICSecurity == "" {
return fmt.Errorf("quic security cannot be empty when quic key is specified")
}
// gRPC配置验证
if p.GRPCServiceName != "" && !p.TLS {
return fmt.Errorf("grpc requires tls to be enabled")
}
return nil
}
// ToURL 将代理配置转换为URL格式
// 返回代理URL字符串适用于分享和导入
func (p *ProxyConfig) ToURL() (string, error) {
switch strings.ToLower(p.Type) {
case "ss", "shadowsocks":
return p.toSSURL()
case "ssr", "shadowsocksr":
return p.toSSRURL()
case "vmess":
return p.toVMessURL()
case "trojan":
return p.toTrojanURL()
case "http", "https":
return p.toHTTPURL()
case "socks5":
return p.toSocks5URL()
default:
return "", fmt.Errorf("unsupported proxy type: %s", p.Type)
}
}
// FromURL 从URL解析代理配置
// 根据URL类型自动识别并解析代理配置
func (p *ProxyConfig) FromURL(proxyURL string) error {
u, err := url.Parse(proxyURL)
if err != nil {
return fmt.Errorf("failed to parse proxy URL: %v", err)
}
switch u.Scheme {
case "ss":
return p.fromSSURL(u)
case "ssr":
return p.fromSSRURL(u)
case "vmess":
return p.fromVMessURL(u)
case "trojan":
return p.fromTrojanURL(u)
case "http", "https":
return p.fromHTTPURL(u)
case "socks5":
return p.fromSocks5URL(u)
default:
return fmt.Errorf("unsupported proxy URL scheme: %s", u.Scheme)
}
}
// toSSURL 转换为SS URL格式
func (p *ProxyConfig) toSSURL() (string, error) {
if p.Type != "ss" && p.Type != "shadowsocks" {
return "", fmt.Errorf("not a shadowsocks proxy")
}
// SS URL格式: ss://method:password@server:port#name
host := fmt.Sprintf("%s:%d", p.Server, p.Port)
u := &url.URL{
Scheme: "ss",
User: url.UserPassword(p.Method, p.Password),
Host: host,
}
if p.Name != "" {
u.Fragment = p.Name
}
return u.String(), nil
}
// toSSRURL 转换为SSR URL格式
func (p *ProxyConfig) toSSRURL() (string, error) {
if p.Type != "ssr" && p.Type != "shadowsocksr" {
return "", fmt.Errorf("not a shadowsocksr proxy")
}
// SSR URL格式: ssr://base64(server:port:protocol:method:obfs:password/?obfsparam=...&protoparam=...&remarks=...)
params := make(url.Values)
params.Set("obfsparam", p.ObfsParam)
params.Set("protoparam", "")
params.Set("remarks", p.Name)
params.Set("group", "")
params.Set("udpport", "0")
params.Set("uot", "0")
baseInfo := fmt.Sprintf("%s:%d:%s:%s:%s:%s",
p.Server, p.Port, p.Protocol, p.Method, p.Obfs, p.Password)
encodedInfo := url.QueryEscape(base64Encode(baseInfo))
encodedParams := url.QueryEscape(params.Encode())
return fmt.Sprintf("ssr://%s/?%s", encodedInfo, encodedParams), nil
}
// toVMessURL 转换为VMess URL格式
func (p *ProxyConfig) toVMessURL() (string, error) {
if p.Type != "vmess" {
return "", fmt.Errorf("not a vmess proxy")
}
// VMess URL格式: vmess://base64(json_config)
config := map[string]interface{}{
"v": "2",
"ps": p.Name,
"add": p.Server,
"port": p.Port,
"id": p.VMessConfig.VMessUUID,
"aid": p.VMessConfig.VMessAlterID,
"net": "tcp", // 简化版本
"type": "none",
"host": "",
"path": "",
"tls": "",
}
if p.TLS {
config["tls"] = "tls"
}
configJSON, err := json.Marshal(config)
if err != nil {
return "", fmt.Errorf("failed to marshal vmess config: %v", err)
}
return "vmess://" + base64Encode(string(configJSON)), nil
}
// toTrojanURL 转换为Trojan URL格式
func (p *ProxyConfig) toTrojanURL() (string, error) {
if p.Type != "trojan" {
return "", fmt.Errorf("not a trojan proxy")
}
// Trojan URL格式: trojan://password@server:port?allowinsecure=1#name
u := &url.URL{
Scheme: "trojan",
User: url.UserPassword(p.Password, ""),
Host: fmt.Sprintf("%s:%d", p.Server, p.Port),
}
params := u.Query()
if p.SkipVerify {
params.Set("allowinsecure", "1")
}
if p.SNI != "" {
params.Set("sni", p.SNI)
}
u.RawQuery = params.Encode()
if p.Name != "" {
u.Fragment = p.Name
}
return u.String(), nil
}
// toHTTPURL 转换为HTTP/HTTPS URL格式
func (p *ProxyConfig) toHTTPURL() (string, error) {
if p.Type != "http" && p.Type != "https" {
return "", fmt.Errorf("not an http proxy")
}
scheme := "http"
if p.Type == "https" {
scheme = "https"
}
u := &url.URL{
Scheme: scheme,
Host: fmt.Sprintf("%s:%d", p.Server, p.Port),
}
if p.Password != "" {
u.User = url.UserPassword("", p.Password)
}
if p.Name != "" {
u.Fragment = p.Name
}
return u.String(), nil
}
// toSocks5URL 转换为SOCKS5 URL格式
func (p *ProxyConfig) toSocks5URL() (string, error) {
if p.Type != "socks5" {
return "", fmt.Errorf("not a socks5 proxy")
}
u := &url.URL{
Scheme: "socks5",
Host: fmt.Sprintf("%s:%d", p.Server, p.Port),
}
if p.Password != "" {
u.User = url.UserPassword("", p.Password)
}
if p.Name != "" {
u.Fragment = p.Name
}
return u.String(), nil
}
// 以下是FromURL的各种实现方法由于篇幅限制这里只提供框架
func (p *ProxyConfig) fromSSURL(u *url.URL) error {
// 解析SS URL的实现
p.Type = "ss"
p.Server = u.Hostname()
if port, err := strconv.Atoi(u.Port()); err == nil {
p.Port = port
}
p.Name = u.Fragment
if u.User != nil {
p.Method = u.User.Username()
if password, ok := u.User.Password(); ok {
p.Password = password
}
}
return p.Validate()
}
func (p *ProxyConfig) fromSSRURL(u *url.URL) error {
// 解析SSR URL的实现
p.Type = "ssr"
// 实现SSR URL解析逻辑
return p.Validate()
}
func (p *ProxyConfig) fromVMessURL(u *url.URL) error {
// 解析VMess URL的实现
p.Type = "vmess"
// 实现VMess URL解析逻辑
return p.Validate()
}
func (p *ProxyConfig) fromTrojanURL(u *url.URL) error {
// 解析Trojan URL的实现
p.Type = "trojan"
p.Server = u.Hostname()
if port, err := strconv.Atoi(u.Port()); err == nil {
p.Port = port
}
p.Name = u.Fragment
if u.User != nil {
p.Password = u.User.Username()
}
return p.Validate()
}
func (p *ProxyConfig) fromHTTPURL(u *url.URL) error {
// 解析HTTP URL的实现
p.Type = u.Scheme
p.Server = u.Hostname()
if port, err := strconv.Atoi(u.Port()); err == nil {
p.Port = port
}
p.Name = u.Fragment
if u.User != nil {
if password, ok := u.User.Password(); ok {
p.Password = password
}
}
return p.Validate()
}
func (p *ProxyConfig) fromSocks5URL(u *url.URL) error {
// 解析SOCKS5 URL的实现
p.Type = "socks5"
p.Server = u.Hostname()
if port, err := strconv.Atoi(u.Port()); err == nil {
p.Port = port
}
p.Name = u.Fragment
if u.User != nil {
if password, ok := u.User.Password(); ok {
p.Password = password
}
}
return p.Validate()
}
// 辅助函数
func base64Encode(s string) string {
return base64.StdEncoding.EncodeToString([]byte(s))
}
func base64Decode(s string) (string, error) {
data, err := base64.StdEncoding.DecodeString(s)
return string(data), err
}