diff --git a/internal/util/link/outbound.go b/internal/util/link/outbound.go index 8179a8475..22ef6b6e6 100644 --- a/internal/util/link/outbound.go +++ b/internal/util/link/outbound.go @@ -157,9 +157,8 @@ func parseVmess(link string) (*ParseResult, error) { // Map known fields (best effort, matching frontend parser coverage) switch network { case "ws": - if host, ok := j["host"].(string); ok { - setWS(stream, host, getString(j, "path", "/")) - } + host, _ := j["host"].(string) + setWS(stream, host, getString(j, "path", "/")) case "grpc": svc := getString(j, "path", "") if auth, ok := j["authority"].(string); ok && auth != "" { @@ -452,7 +451,7 @@ func parseHysteria2(link string) (*ParseResult, error) { "alpn": splitCommaOrDefault(params.Get("alpn"), []string{"h3"}), "fingerprint": params.Get("fp"), "echConfigList": params.Get("ech"), - "verifyPeerCertByName": "", + "verifyPeerCertByName": params.Get("vcn"), "pinnedPeerCertSha256": params.Get("pinSHA256"), }, } diff --git a/internal/util/link/outbound_helpers_test.go b/internal/util/link/outbound_helpers_test.go index ee0b2dbc6..08e91b2c7 100644 --- a/internal/util/link/outbound_helpers_test.go +++ b/internal/util/link/outbound_helpers_test.go @@ -177,6 +177,32 @@ func TestParse_XhttpExtraAndSnakeCaseFields(t *testing.T) { } } +func TestParse_VmessWSPathWithoutHostKey(t *testing.T) { + // Some generators omit "host" when it equals the address; the path must + // still be honored (matching the TS parser), not dropped to the default. + inner := `{"v":"2","add":"h","port":443,"id":"11111111-2222-4333-8444-555555555555","net":"ws","path":"/api","tls":"tls"}` + link := "vmess://" + base64.StdEncoding.EncodeToString([]byte(inner)) + res, err := ParseLink(link) + if err != nil { + t.Fatalf("parse: %v", err) + } + wss := streamSub(t, res, "wsSettings") + if wss["path"] != "/api" { + t.Errorf("wsSettings path = %v, want /api (dropped when host key absent)", wss["path"]) + } +} + +func TestParse_Hysteria2VerifyPeerCertByName(t *testing.T) { + res, err := ParseLink("hysteria2://auth@h.com:443?security=tls&sni=decoy.com&vcn=real-cert.com#r") + if err != nil { + t.Fatalf("parse: %v", err) + } + tls := streamSub(t, res, "tlsSettings") + if tls["verifyPeerCertByName"] != "real-cert.com" { + t.Errorf("verifyPeerCertByName = %v, want real-cert.com (vcn param ignored)", tls["verifyPeerCertByName"]) + } +} + func TestParse_TCPHTTPHeader(t *testing.T) { res, err := ParseLink("vless://uuid@h.com:443?type=tcp&headerType=http&host=ex.com&path=%2F") if err != nil {