diff --git a/api/controller/default.go b/api/controller/default.go index 2503f4c..780d429 100644 --- a/api/controller/default.go +++ b/api/controller/default.go @@ -72,7 +72,7 @@ func BuildSub(clashType model.ClashType, query validator.SubValidator, template err = yaml.Unmarshal(data, &sub) newProxies := make([]model.Proxy, 0) if err != nil { - reg, _ := regexp.Compile("(ssr|ss|vmess|trojan|vless)://") + reg, _ := regexp.Compile("(ssr|ss|vmess|trojan|vless|hysteria)://") if reg.Match(data) { p := utils.ParseProxy(strings.Split(string(data), "\n")...) newProxies = p diff --git a/model/clash.go b/model/clash.go index 4425fc3..308f6a3 100644 --- a/model/clash.go +++ b/model/clash.go @@ -18,11 +18,12 @@ func GetSupportProxyTypes(clashType ClashType) map[string]bool { } if clashType == ClashMeta { return map[string]bool{ - "ss": true, - "ssr": true, - "vmess": true, - "trojan": true, - "vless": true, + "ss": true, + "ssr": true, + "vmess": true, + "trojan": true, + "vless": true, + "hysteria2": true, } } return nil diff --git a/model/proxy.go b/model/proxy.go index a8faee1..6c4f9e0 100644 --- a/model/proxy.go +++ b/model/proxy.go @@ -45,6 +45,11 @@ type Proxy struct { UDPOverTCP bool `yaml:"udp-over-tcp,omitempty"` UDPOverTCPVersion int `yaml:"udp-over-tcp-version,omitempty"` SubName string `yaml:"-"` + Up string `yaml:"up,omitempty"` + Down string `yaml:"down,omitempty"` + CustomCA string `yaml:"ca,omitempty"` + CustomCAString string `yaml:"ca-str,omitempty"` + CWND int `yaml:"cwnd,omitempty"` } func (p Proxy) MarshalYAML() (interface{}, error) { @@ -59,6 +64,8 @@ func (p Proxy) MarshalYAML() (interface{}, error) { return ProxyToVless(p), nil case "trojan": return ProxyToTrojan(p), nil + case "hysteria2": + return ProxyToHysteria2(p), nil } return nil, nil } diff --git a/model/proxy_hysteria2.go b/model/proxy_hysteria2.go new file mode 100644 index 0000000..b97b656 --- /dev/null +++ b/model/proxy_hysteria2.go @@ -0,0 +1,41 @@ +package model + +type Hysteria2 struct { + Type string `yaml:"type"` + Name string `yaml:"name"` + Server string `yaml:"server"` + Port int `yaml:"port"` + Up string `yaml:"up,omitempty"` + Down string `yaml:"down,omitempty"` + Password string `yaml:"password,omitempty"` + Obfs string `yaml:"obfs,omitempty"` + ObfsPassword string `yaml:"obfs-password,omitempty"` + SNI string `yaml:"sni,omitempty"` + SkipCertVerify bool `yaml:"skip-cert-verify,omitempty"` + Fingerprint string `yaml:"fingerprint,omitempty"` + ALPN []string `yaml:"alpn,omitempty"` + CustomCA string `yaml:"ca,omitempty"` + CustomCAString string `yaml:"ca-str,omitempty"` + CWND int `yaml:"cwnd,omitempty"` +} + +func ProxyToHysteria2(p Proxy) Hysteria2 { + return Hysteria2{ + Type: "hysteria2", + Name: p.Name, + Server: p.Server, + Port: p.Port, + Up: p.Up, + Down: p.Down, + Password: p.Password, + Obfs: p.Obfs, + ObfsPassword: p.ObfsParam, + SNI: p.Sni, + SkipCertVerify: p.SkipCertVerify, + Fingerprint: p.Fingerprint, + ALPN: p.Alpn, + CustomCA: p.CustomCA, + CustomCAString: p.CustomCAString, + CWND: p.CWND, + } +} diff --git a/parser/hysteria2.go b/parser/hysteria2.go new file mode 100644 index 0000000..36d133a --- /dev/null +++ b/parser/hysteria2.go @@ -0,0 +1,50 @@ +package parser + +import ( + "errors" + "net/url" + "strconv" + "strings" + "sub2clash/model" +) + +func ParseHysteria2(proxy string) (model.Proxy, error) { + // 判断是否以 hysteria2:// 开头 + if !strings.HasPrefix(proxy, "hysteria2://") { + return model.Proxy{}, errors.New("invalid hysteria2 Url") + } + // 分割 + parts := strings.SplitN(strings.TrimPrefix(proxy, "hysteria2://"), "@", 2) + if len(parts) != 2 { + return model.Proxy{}, errors.New("invalid hysteria2 Url") + } + // 分割 + serverInfo := strings.SplitN(parts[1], "/?", 2) + serverAndPort := strings.SplitN(serverInfo[0], ":", 2) + if len(serverAndPort) == 1 { + serverAndPort = append(serverAndPort, "443") + } else if len(serverAndPort) != 2 { + return model.Proxy{}, errors.New("invalid hysteria2 Url") + } + params, err := url.ParseQuery(serverInfo[1]) + if err != nil { + return model.Proxy{}, errors.New("invalid hysteria2 Url") + } + // 获取端口 + port, err := strconv.Atoi(serverAndPort[1]) + if err != nil { + return model.Proxy{}, errors.New("invalid hysteria2 Url") + } + // 返回结果 + result := model.Proxy{ + Type: "hysteria2", + Name: params.Get("name"), + Server: serverAndPort[0], + Port: port, + Password: parts[0], + Obfs: params.Get("obfs"), + ObfsParam: params.Get("obfs-password"), + Sni: params.Get("sni"), + } + return result, nil +} diff --git a/utils/proxy.go b/utils/proxy.go index a6ba7bd..3301f8d 100644 --- a/utils/proxy.go +++ b/utils/proxy.go @@ -126,6 +126,9 @@ func ParseProxy(proxies ...string) []model.Proxy { if strings.HasPrefix(proxy, "ssr://") { proxyItem, err = parser.ParseShadowsocksR(proxy) } + if strings.HasPrefix(proxy, "hysteria2://") { + proxyItem, err = parser.ParseHysteria2(proxy) + } if err == nil { result = append(result, proxyItem) } else {