From 679102fa3bb4326c3abdea87e117eb073c6f007c Mon Sep 17 00:00:00 2001 From: nite07 Date: Sat, 9 Mar 2024 15:33:26 +0800 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20vmes/vless=20?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/proxy_vmess.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/model/proxy_vmess.go b/model/proxy_vmess.go index 1d1b2ab..f9c5116 100644 --- a/model/proxy_vmess.go +++ b/model/proxy_vmess.go @@ -1,18 +1,18 @@ package model type HTTPOptions struct { - Method string `proxy:"method,omitempty"` - Path []string `proxy:"path,omitempty"` - Headers map[string][]string `proxy:"headers,omitempty"` + Method string `yaml:"method,omitempty"` + Path []string `yaml:"path,omitempty"` + Headers map[string][]string `yaml:"headers,omitempty"` } type HTTP2Options struct { - Host []string `proxy:"host,omitempty"` - Path string `proxy:"path,omitempty"` + Host []string `yaml:"host,omitempty"` + Path string `yaml:"path,omitempty"` } type GrpcOptions struct { - GrpcServiceName string `proxy:"grpc-service-name,omitempty"` + GrpcServiceName string `yaml:"grpc-service-name,omitempty"` } type RealityOptions struct { @@ -21,10 +21,10 @@ type RealityOptions struct { } type WSOptions struct { - Path string `proxy:"path,omitempty"` - Headers map[string]string `proxy:"headers,omitempty"` - MaxEarlyData int `proxy:"max-early-data,omitempty"` - EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"` + Path string `yaml:"path,omitempty"` + Headers map[string]string `yaml:"headers,omitempty"` + MaxEarlyData int `yaml:"max-early-data,omitempty"` + EarlyDataHeaderName string `yaml:"early-data-header-name,omitempty"` } type VmessJson struct { From 70f5c63fe29c7ea48b4ce7bdfa11681b20affee4 Mon Sep 17 00:00:00 2001 From: nite07 Date: Sat, 9 Mar 2024 15:39:47 +0800 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20vmess/vless=20?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E5=90=8D=E7=A7=B0=E6=9C=AAURL=E8=A7=A3?= =?UTF-8?q?=E7=A0=81=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- parser/vless.go | 10 ++++++++-- parser/vmess.go | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/parser/vless.go b/parser/vless.go index ff54a7a..8e967b6 100644 --- a/parser/vless.go +++ b/parser/vless.go @@ -72,10 +72,16 @@ func ParseVless(proxy string) (model.Proxy, error) { if strings.Contains(serverInfo[1], "|") { result.Name = strings.SplitN(serverInfo[1], "|", 2)[1] } else { - result.Name = serverInfo[1] + result.Name, err = url.QueryUnescape(serverInfo[1]) + if err != nil { + return model.Proxy{}, err + } } } else { - result.Name = serverAndPort[0] + result.Name, err = url.QueryUnescape(serverAndPort[0]) + if err != nil { + return model.Proxy{}, err + } } return result, nil } diff --git a/parser/vmess.go b/parser/vmess.go index b7a1e9b..6250799 100644 --- a/parser/vmess.go +++ b/parser/vmess.go @@ -3,6 +3,7 @@ package parser import ( "encoding/json" "errors" + "net/url" "strconv" "strings" "sub2clash/model" @@ -56,9 +57,13 @@ func ParseVmess(proxy string) (model.Proxy, error) { if vmess.Net == "ws" && vmess.Host == "" { vmess.Host = vmess.Add } + name, err := url.QueryUnescape(vmess.Ps) + if err != nil { + name = vmess.Ps + } // 返回结果 result := model.Proxy{ - Name: vmess.Ps, + Name: name, Type: "vmess", Server: vmess.Add, Port: port, From d8a81e44b61cc29fd01bd26a95273f8918d84066 Mon Sep 17 00:00:00 2001 From: nite07 Date: Sat, 9 Mar 2024 15:58:16 +0800 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=87=8D=E5=90=8D?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=B2=A1=E6=9C=89=E6=AD=A3=E7=A1=AE=E9=87=8D?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/controller/default.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/controller/default.go b/api/controller/default.go index 20bca76..4ff52a4 100644 --- a/api/controller/default.go +++ b/api/controller/default.go @@ -173,9 +173,11 @@ func BuildSub(clashType model.ClashType, query validator.SubValidator, template names := make(map[string]int) for i := range proxyList { if _, exist := names[proxyList[i].Name]; exist { + names[proxyList[i].Name] = names[proxyList[i].Name] + 1 proxyList[i].Name = proxyList[i].Name + " " + strconv.Itoa(names[proxyList[i].Name]) + } else { + names[proxyList[i].Name] = 0 } - names[proxyList[i].Name] = names[proxyList[i].Name] + 1 } // trim for i := range proxyList { From 68269fd499a1bddb75e74e98041f5a6e5142cad0 Mon Sep 17 00:00:00 2001 From: nite07 Date: Sat, 9 Mar 2024 17:01:52 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20Hysteria=20?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/proxy.go | 6 ++++ model/proxy_hysteria.go | 53 +++++++++++++++++++++++++++ parser/hysteria.go | 79 +++++++++++++++++++++++++++++++++++++++++ parser/hysteria2.go | 7 ++-- utils/proxy.go | 5 ++- 5 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 model/proxy_hysteria.go create mode 100644 parser/hysteria.go diff --git a/model/proxy.go b/model/proxy.go index 6c4f9e0..c1e6e8d 100644 --- a/model/proxy.go +++ b/model/proxy.go @@ -50,6 +50,12 @@ type Proxy struct { CustomCA string `yaml:"ca,omitempty"` CustomCAString string `yaml:"ca-str,omitempty"` CWND int `yaml:"cwnd,omitempty"` + Auth string `yaml:"auth,omitempty"` + ReceiveWindowConn int `yaml:"recv-window-conn,omitempty"` + ReceiveWindow int `yaml:"recv-window,omitempty"` + DisableMTUDiscovery bool `yaml:"disable-mtu-discovery,omitempty"` + FastOpen bool `yaml:"fast-open,omitempty"` + HopInterval int `yaml:"hop-interval,omitempty"` } func (p Proxy) MarshalYAML() (interface{}, error) { diff --git a/model/proxy_hysteria.go b/model/proxy_hysteria.go new file mode 100644 index 0000000..21e1e1e --- /dev/null +++ b/model/proxy_hysteria.go @@ -0,0 +1,53 @@ +package model + +type Hysteria struct { + Type string `yaml:"type"` + Name string `yaml:"name"` + Server string `yaml:"server"` + Port int `yaml:"port,omitempty"` + Ports string `yaml:"ports,omitempty"` + Protocol string `yaml:"protocol,omitempty"` + ObfsProtocol string `yaml:"obfs-protocol,omitempty"` // compatible with Stash + Up string `yaml:"up"` + UpSpeed int `yaml:"up-speed,omitempty"` // compatible with Stash + Down string `yaml:"down"` + DownSpeed int `yaml:"down-speed,omitempty"` // compatible with Stash + Auth string `yaml:"auth,omitempty"` + AuthString string `yaml:"auth-str,omitempty"` + Obfs string `yaml:"obfs,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"` + ReceiveWindowConn int `yaml:"recv-window-conn,omitempty"` + ReceiveWindow int `yaml:"recv-window,omitempty"` + DisableMTUDiscovery bool `yaml:"disable-mtu-discovery,omitempty"` + FastOpen bool `yaml:"fast-open,omitempty"` + HopInterval int `yaml:"hop-interval,omitempty"` +} + +func ProxyToHysteria(p Proxy) Hysteria { + return Hysteria{ + Type: "hysteria", + Name: p.Name, + Server: p.Server, + Port: p.Port, + Up: p.Up, + Down: p.Down, + Auth: p.Auth, + Obfs: p.Obfs, + SNI: p.Sni, + SkipCertVerify: p.SkipCertVerify, + Fingerprint: p.Fingerprint, + ALPN: p.Alpn, + CustomCA: p.CustomCA, + CustomCAString: p.CustomCAString, + ReceiveWindowConn: p.ReceiveWindowConn, + ReceiveWindow: p.ReceiveWindow, + DisableMTUDiscovery: p.DisableMTUDiscovery, + FastOpen: p.FastOpen, + HopInterval: p.HopInterval, + } +} diff --git a/parser/hysteria.go b/parser/hysteria.go new file mode 100644 index 0000000..c7c9942 --- /dev/null +++ b/parser/hysteria.go @@ -0,0 +1,79 @@ +package parser + +import ( + "errors" + "net/url" + "strconv" + "strings" + "sub2clash/model" +) + +//hysteria://host:port?protocol=udp&auth=123456&peer=sni.domain&insecure=1&upmbps=100&downmbps=100&alpn=hysteria&obfs=xplus&obfsParam=123456#remarks +// +//- host: hostname or IP address of the server to connect to (required) +//- port: port of the server to connect to (required) +//- protocol: protocol to use ("udp", "wechat-video", "faketcp") (optional, default: "udp") +//- auth: authentication payload (string) (optional) +//- peer: SNI for TLS (optional) +//- insecure: ignore certificate errors (optional) +//- upmbps: upstream bandwidth in Mbps (required) +//- downmbps: downstream bandwidth in Mbps (required) +//- alpn: QUIC ALPN (optional) +//- obfs: Obfuscation mode (optional, empty or "xplus") +//- obfsParam: Obfuscation password (optional) +//- remarks: remarks (optional) + +func ParseHysteria(proxy string) (model.Proxy, error) { + // 判断是否以 hysteria:// 开头 + if !strings.HasPrefix(proxy, "hysteria://") { + return model.Proxy{}, errors.New("invalid hysteria Url") + } + // 分割 + parts := strings.SplitN(strings.TrimPrefix(proxy, "hysteria://"), "?", 2) + serverInfo := strings.SplitN(parts[0], ":", 2) + if len(serverInfo) != 2 { + return model.Proxy{}, errors.New("invalid hysteria Url") + } + params, err := url.ParseQuery(parts[1]) + if err != nil { + return model.Proxy{}, errors.New("invalid hysteria Url") + } + host := serverInfo[0] + port, err := strconv.Atoi(serverInfo[1]) + if err != nil { + return model.Proxy{}, errors.New("invalid hysteria Url") + } + protocol := params.Get("protocol") + auth := params.Get("auth") + peer := params.Get("peer") + insecure := params.Get("insecure") + upmbps := params.Get("upmbps") + downmbps := params.Get("downmbps") + alpn := params.Get("alpn") + obfs := params.Get("obfs") + obfsParam := params.Get("obfsParam") + remarks := "" + if strings.Contains(parts[1], "#") { + r := strings.Split(parts[1], "#") + remarks = r[len(r)-1] + } else { + remarks = serverInfo[0] + ":" + serverInfo[1] + } + // 返回结果 + result := model.Proxy{ + Type: "hysteria", + Name: remarks, + Server: host, + Port: port, + Up: upmbps, + Down: downmbps, + Auth: auth, + Obfs: obfs, + Sni: peer, + SkipCertVerify: insecure == "1", + Alpn: strings.Split(alpn, ","), + ObfsParam: obfsParam, + Protocol: protocol, + } + return result, nil +} diff --git a/parser/hysteria2.go b/parser/hysteria2.go index e627b1d..89814e2 100644 --- a/parser/hysteria2.go +++ b/parser/hysteria2.go @@ -8,16 +8,15 @@ import ( "sub2clash/model" ) +// hysteria2://letmein@example.com/?insecure=1&obfs=salamander&obfs-password=gawrgura&pinSHA256=deadbeef&sni=real.example.com + func ParseHysteria2(proxy string) (model.Proxy, error) { // 判断是否以 hysteria2:// 开头 - if !strings.HasPrefix(proxy, "hysteria2://") { + if !strings.HasPrefix(proxy, "hysteria2://") && !strings.HasPrefix(proxy, "hy2://") { 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) diff --git a/utils/proxy.go b/utils/proxy.go index 7766ec8..16ed5ff 100644 --- a/utils/proxy.go +++ b/utils/proxy.go @@ -127,9 +127,12 @@ func ParseProxy(proxies ...string) []model.Proxy { if strings.HasPrefix(proxy, "ssr://") { proxyItem, err = parser.ParseShadowsocksR(proxy) } - if strings.HasPrefix(proxy, "hysteria2://") { + if strings.HasPrefix(proxy, "hysteria2://") || strings.HasPrefix(proxy, "hy2://") { proxyItem, err = parser.ParseHysteria2(proxy) } + if strings.HasPrefix(proxy, "hysteria://") { + proxyItem, err = parser.ParseHysteria(proxy) + } if err == nil { result = append(result, proxyItem) } else { From fd7c460f95f9c3280df85798e4a21af6ace6c5e6 Mon Sep 17 00:00:00 2001 From: nite07 Date: Sat, 9 Mar 2024 17:17:57 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20Hysteria=20?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + model/clash.go | 1 + model/proxy.go | 2 ++ 3 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 39f0f6d..9bdbdc1 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - Vmess - Vless (Clash.Meta) - Trojan + - Hysteria (Clash.Meta) - Hysteria2 (Clash.Meta) ## 使用 diff --git a/model/clash.go b/model/clash.go index 308f6a3..5159709 100644 --- a/model/clash.go +++ b/model/clash.go @@ -23,6 +23,7 @@ func GetSupportProxyTypes(clashType ClashType) map[string]bool { "vmess": true, "trojan": true, "vless": true, + "hysteria": true, "hysteria2": true, } } diff --git a/model/proxy.go b/model/proxy.go index c1e6e8d..caed364 100644 --- a/model/proxy.go +++ b/model/proxy.go @@ -70,6 +70,8 @@ func (p Proxy) MarshalYAML() (interface{}, error) { return ProxyToVless(p), nil case "trojan": return ProxyToTrojan(p), nil + case "hysteria": + return ProxyToHysteria(p), nil case "hysteria2": return ProxyToHysteria2(p), nil }