mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-10-10 04:03:11 +08:00
chore: converter map hysteria2 port hopping and realm URI (#2947)
This commit is contained in:
@@ -69,8 +69,10 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||
|
||||
proxies = append(proxies, hysteria)
|
||||
|
||||
case "hysteria2", "hy2":
|
||||
urlHysteria2, err := url.Parse(line)
|
||||
case "hysteria2", "hy2", "hysteria2+realm", "hy2+realm":
|
||||
realmMode := strings.HasSuffix(scheme, "+realm")
|
||||
hopLine, ports := splitHysteria2Ports(line)
|
||||
urlHysteria2, err := url.Parse(hopLine)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -87,6 +89,9 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||
} else {
|
||||
hysteria2["port"] = "443"
|
||||
}
|
||||
if ports != "" {
|
||||
hysteria2["ports"] = ports
|
||||
}
|
||||
hysteria2["obfs"] = query.Get("obfs")
|
||||
hysteria2["obfs-password"] = query.Get("obfs-password")
|
||||
hysteria2["sni"] = query.Get("sni")
|
||||
@@ -94,12 +99,19 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
||||
if alpn := query.Get("alpn"); alpn != "" {
|
||||
hysteria2["alpn"] = strings.Split(alpn, ",")
|
||||
}
|
||||
if auth := urlHysteria2.User.String(); auth != "" {
|
||||
hysteria2["password"] = auth
|
||||
}
|
||||
hysteria2["fingerprint"] = query.Get("pinSHA256")
|
||||
hysteria2["down"] = query.Get("down")
|
||||
hysteria2["up"] = query.Get("up")
|
||||
if realmMode {
|
||||
token, _ := url.PathUnescape(urlHysteria2.User.String())
|
||||
realmID, _ := url.PathUnescape(strings.TrimPrefix(urlHysteria2.EscapedPath(), "/"))
|
||||
hysteria2["realm-opts"] = buildRealmOpts("https://"+urlHysteria2.Host, token, realmID, query["stun"])
|
||||
if auth := query.Get("auth"); auth != "" {
|
||||
hysteria2["password"] = auth
|
||||
}
|
||||
} else if auth := urlHysteria2.User.String(); auth != "" {
|
||||
hysteria2["password"] = auth
|
||||
}
|
||||
|
||||
proxies = append(proxies, hysteria2)
|
||||
|
||||
|
||||
@@ -40,6 +40,37 @@ func TestConvertsV2Ray_normal(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestConvertsV2Ray_hysteria2PortHopping(t *testing.T) {
|
||||
uri := "hysteria2://letmein@example.com:443,5000-6000/?sni=example.com#hop"
|
||||
proxies, err := ConvertsV2Ray([]byte(uri))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "example.com", proxies[0]["server"])
|
||||
assert.Equal(t, "443", proxies[0]["port"])
|
||||
assert.Equal(t, "443,5000-6000", proxies[0]["ports"])
|
||||
|
||||
_, err = adapter.ParseProxy(proxies[0])
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestConvertsV2Ray_hysteria2RealmScheme(t *testing.T) {
|
||||
uri := "hysteria2+realm://tok3n@rendezvous.example.com:8443/rid42?auth=letmein&stun=stun1:3478&stun=stun2:3478&sni=example.com#realm"
|
||||
proxies, err := ConvertsV2Ray([]byte(uri))
|
||||
assert.Nil(t, err)
|
||||
p := proxies[0]
|
||||
assert.Equal(t, "hysteria2", p["type"])
|
||||
assert.Equal(t, "letmein", p["password"])
|
||||
assert.Equal(t, map[string]any{
|
||||
"enable": true,
|
||||
"server-url": "https://rendezvous.example.com:8443",
|
||||
"token": "tok3n",
|
||||
"realm-id": "rid42",
|
||||
"stun-servers": []string{"stun1:3478", "stun2:3478"},
|
||||
}, p["realm-opts"])
|
||||
|
||||
_, err = adapter.ParseProxy(p)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestConvertsV2RayMieru(t *testing.T) {
|
||||
mierusTest := "mierus://user:pass@1.2.3.4?handshake-mode=HANDSHAKE_NO_WAIT&mtu=1400&multiplexing=MULTIPLEXING_HIGH&port=6666&port=9998-9999&port=6489&port=4896&profile=default&protocol=TCP&protocol=TCP&protocol=UDP&protocol=UDP&traffic-pattern=CCoQARoECAEQCiIYCAMQASoIMDAwMTAyMDMqCDA0MDUwNjA3"
|
||||
|
||||
|
||||
@@ -363,3 +363,52 @@ func parseXHTTPExtra(extra map[string]any, opts map[string]any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildRealmOpts(serverURL, token, realmID string, stunServers []string) map[string]any {
|
||||
opts := map[string]any{"enable": true, "server-url": serverURL}
|
||||
if token != "" {
|
||||
opts["token"] = token
|
||||
}
|
||||
if realmID != "" {
|
||||
opts["realm-id"] = realmID
|
||||
}
|
||||
if len(stunServers) > 0 {
|
||||
opts["stun-servers"] = stunServers
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func splitHysteria2Ports(line string) (string, string) {
|
||||
i := strings.Index(line, "://")
|
||||
if i < 0 {
|
||||
return line, ""
|
||||
}
|
||||
head, rest := line[:i+3], line[i+3:]
|
||||
auth, tail := rest, ""
|
||||
if j := strings.IndexAny(rest, "/?#"); j >= 0 {
|
||||
auth, tail = rest[:j], rest[j:]
|
||||
}
|
||||
userinfo := ""
|
||||
if at := strings.LastIndex(auth, "@"); at >= 0 {
|
||||
userinfo, auth = auth[:at+1], auth[at+1:]
|
||||
}
|
||||
if strings.Contains(auth, "]") {
|
||||
return line, ""
|
||||
}
|
||||
colon := strings.LastIndex(auth, ":")
|
||||
if colon < 0 {
|
||||
return line, ""
|
||||
}
|
||||
host, ports := auth[:colon], auth[colon+1:]
|
||||
if !strings.ContainsAny(ports, ",-") {
|
||||
return line, ""
|
||||
}
|
||||
first := ports
|
||||
if k := strings.IndexAny(ports, ",-"); k >= 0 {
|
||||
first = ports[:k]
|
||||
}
|
||||
if first == "" {
|
||||
return line, ""
|
||||
}
|
||||
return head + userinfo + host + ":" + first + tail, ports
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user