mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 11:15:03 +03:00
fix(sub): carry REALITY ML-KEM hint in VLESS links (#6712)
* fix(sub): carry REALITY ML-KEM hint in VLESS links Keep raw share links in parity with Clash subscriptions for Xray 26.9.8+. Preserve the URI hint through Go and frontend imports, expose it in the outbound editor, and update the documentation tooling. * fix(link): accept REALITY ML-KEM boolean aliases * test(frontend): isolate Happ preset notifications * fix(link): keep the ML-KEM hint out of Xray REALITY settings support-x25519mlkem768 is a Mihomo reality-opts option; xray-core's REALITYConfig (infra/conf/transport_security.go) has no such field and its JSON loader drops unknown keys silently. The PR also stored it as realitySettings.supportX25519Mlkem768 in Xray outbounds (form switch, Go and TS link import, docs outbound builders) and as an inbound settings default that is stripped before Xray and read by no link generator. The outbound switch therefore did nothing, and imported links carried a dead key into the JSON subscription. The share-link hint itself stays: Go, frontend and docs still emit support-x25519mlkem768=true on VLESS REALITY links and drop it on a TLS host override. --------- Co-authored-by: libmur-dev <333915961+libmur-dev@users.noreply.github.com> Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
@@ -98,7 +98,15 @@ func dropBaseRealityParams(params map[string]string, baseSecurity, securityToApp
|
||||
}
|
||||
// sni and fp name the master's reality dest, not this endpoint's own
|
||||
// certificate; the host's values are re-applied right after this.
|
||||
for _, k := range []string{"pbk", "sid", "spx", "pqv", "sni", "fp"} {
|
||||
for _, k := range []string{
|
||||
"pbk",
|
||||
"sid",
|
||||
"spx",
|
||||
"pqv",
|
||||
"support-x25519mlkem768",
|
||||
"sni",
|
||||
"fp",
|
||||
} {
|
||||
delete(params, k)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,7 +434,13 @@ func TestSub_HostTlsOverRealityDropsRealityParams(t *testing.T) {
|
||||
if !strings.Contains(joined, "security=tls") {
|
||||
t.Fatalf("host forces tls, link must say so: %s", joined)
|
||||
}
|
||||
for _, leaked := range []string{"pbk=", "sid=", "spx=", "sni=master-dest.example.com"} {
|
||||
for _, leaked := range []string{
|
||||
"pbk=",
|
||||
"sid=",
|
||||
"spx=",
|
||||
"support-x25519mlkem768=",
|
||||
"sni=master-dest.example.com",
|
||||
} {
|
||||
if strings.Contains(joined, leaked) {
|
||||
t.Fatalf("reality parameter %q survived a tls host override: %s", leaked, joined)
|
||||
}
|
||||
|
||||
@@ -1240,6 +1240,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
applyShareTLSParams(stream, params)
|
||||
case "reality":
|
||||
applyShareRealityParams(stream, params, subKey(client))
|
||||
params["support-x25519mlkem768"] = "true"
|
||||
default:
|
||||
params["security"] = "none"
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ func TestGenVlessLink_RealityParamsMapped(t *testing.T) {
|
||||
|
||||
wants := []string{
|
||||
"security=reality",
|
||||
"support-x25519mlkem768=true",
|
||||
"sni=reality.example.com",
|
||||
"pbk=PBKvalue",
|
||||
"sid=ab12cd",
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
)
|
||||
|
||||
func TestDefaultPort(t *testing.T) {
|
||||
@@ -111,7 +115,7 @@ func streamSub(t *testing.T, res *ParseResult, key string) map[string]any {
|
||||
}
|
||||
|
||||
func TestParse_RealitySecurityMapped(t *testing.T) {
|
||||
res, err := ParseLink("vless://uuid@h.com:443?type=tcp&security=reality&pbk=PBK&sid=SID&sni=SNI&fp=firefox&spx=%2Fspx&pqv=PQV")
|
||||
res, err := ParseLink("vless://uuid@h.com:443?type=tcp&security=reality&pbk=PBK&sid=SID&sni=SNI&fp=firefox&spx=%2Fspx&pqv=PQV&support-x25519mlkem768=true")
|
||||
if err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
@@ -123,6 +127,24 @@ func TestParse_RealitySecurityMapped(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Xray-core drops unknown JSON keys silently, so a key its REALITYConfig lacks
|
||||
// would reach the outbound as a setting that does nothing.
|
||||
func TestParse_RealitySettingsAreXrayFields(t *testing.T) {
|
||||
res, err := ParseLink("vless://uuid@h.com:443?type=tcp&security=reality&pbk=PBK&sid=SID&sni=SNI&fp=firefox&spx=%2Fspx&pqv=PQV&support-x25519mlkem768=true")
|
||||
if err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
raw, err := json.Marshal(streamSub(t, res, "realitySettings"))
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&conf.REALITYConfig{}); err != nil {
|
||||
t.Fatalf("realitySettings %s is not an xray-core REALITY config: %v", raw, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_TLSSecurityMapped(t *testing.T) {
|
||||
res, err := ParseLink("trojan://pw@h.com:443?type=tcp&security=tls&sni=SNI&fp=chrome&alpn=h2,http/1.1&ech=ECH&vcn=VCN&pcs=PCS")
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user