From ba8194141b581498e85e284139103f82b81c5cc7 Mon Sep 17 00:00:00 2001 From: Nikita Nemirovsky Date: Thu, 2 Apr 2026 11:25:17 +0800 Subject: [PATCH] fix(sub): support Reality params for fallback inbounds via externalProxy Extract Reality settings (pbk, sni, sid, fp, spx) from stream config unconditionally rather than only when security is "reality". This allows inbounds behind a VLESS fallback (security: "none") to generate correct subscription links when externalProxy overrides security to "reality" via forceTls. Also fix genAllLinks crash when remarkModel contains characters without a corresponding entry in the orders map (e.g., 'o' when externalProxy is empty). --- sub/subService.go | 110 +++++++++++++++++---------------- web/assets/js/model/inbound.js | 4 +- 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/sub/subService.go b/sub/subService.go index 818f193b..f718411a 100644 --- a/sub/subService.go +++ b/sub/subService.go @@ -435,34 +435,37 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { } } + // Extract Reality params unconditionally so they're available when + // externalProxy overrides security to "reality" via forceTls. + realitySetting, _ := stream["realitySettings"].(map[string]any) + realitySettings, _ := searchKey(realitySetting, "settings") + if realitySetting != nil { + if sniValue, ok := searchKey(realitySetting, "serverNames"); ok { + sNames, _ := sniValue.([]any) + params["sni"] = sNames[random.Num(len(sNames))].(string) + } + if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { + params["pbk"], _ = pbkValue.(string) + } + if sidValue, ok := searchKey(realitySetting, "shortIds"); ok { + shortIds, _ := sidValue.([]any) + params["sid"] = shortIds[random.Num(len(shortIds))].(string) + } + if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { + if fp, ok := fpValue.(string); ok && len(fp) > 0 { + params["fp"] = fp + } + } + if pqvValue, ok := searchKey(realitySettings, "mldsa65Verify"); ok { + if pqv, ok := pqvValue.(string); ok && len(pqv) > 0 { + params["pqv"] = pqv + } + } + params["spx"] = "/" + random.Seq(15) + } + if security == "reality" { params["security"] = "reality" - realitySetting, _ := stream["realitySettings"].(map[string]any) - realitySettings, _ := searchKey(realitySetting, "settings") - if realitySetting != nil { - if sniValue, ok := searchKey(realitySetting, "serverNames"); ok { - sNames, _ := sniValue.([]any) - params["sni"] = sNames[random.Num(len(sNames))].(string) - } - if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { - params["pbk"], _ = pbkValue.(string) - } - if sidValue, ok := searchKey(realitySetting, "shortIds"); ok { - shortIds, _ := sidValue.([]any) - params["sid"] = shortIds[random.Num(len(shortIds))].(string) - } - if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { - if fp, ok := fpValue.(string); ok && len(fp) > 0 { - params["fp"] = fp - } - } - if pqvValue, ok := searchKey(realitySettings, "mldsa65Verify"); ok { - if pqv, ok := pqvValue.(string); ok && len(pqv) > 0 { - params["pqv"] = pqv - } - } - params["spx"] = "/" + random.Seq(15) - } if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { params["flow"] = clients[clientIndex].Flow @@ -627,34 +630,37 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string } } + // Extract Reality params unconditionally so they're available when + // externalProxy overrides security to "reality" via forceTls. + realitySetting, _ := stream["realitySettings"].(map[string]any) + realitySettings, _ := searchKey(realitySetting, "settings") + if realitySetting != nil { + if sniValue, ok := searchKey(realitySetting, "serverNames"); ok { + sNames, _ := sniValue.([]any) + params["sni"] = sNames[random.Num(len(sNames))].(string) + } + if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { + params["pbk"], _ = pbkValue.(string) + } + if sidValue, ok := searchKey(realitySetting, "shortIds"); ok { + shortIds, _ := sidValue.([]any) + params["sid"] = shortIds[random.Num(len(shortIds))].(string) + } + if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { + if fp, ok := fpValue.(string); ok && len(fp) > 0 { + params["fp"] = fp + } + } + if pqvValue, ok := searchKey(realitySettings, "mldsa65Verify"); ok { + if pqv, ok := pqvValue.(string); ok && len(pqv) > 0 { + params["pqv"] = pqv + } + } + params["spx"] = "/" + random.Seq(15) + } + if security == "reality" { params["security"] = "reality" - realitySetting, _ := stream["realitySettings"].(map[string]any) - realitySettings, _ := searchKey(realitySetting, "settings") - if realitySetting != nil { - if sniValue, ok := searchKey(realitySetting, "serverNames"); ok { - sNames, _ := sniValue.([]any) - params["sni"] = sNames[random.Num(len(sNames))].(string) - } - if pbkValue, ok := searchKey(realitySettings, "publicKey"); ok { - params["pbk"], _ = pbkValue.(string) - } - if sidValue, ok := searchKey(realitySetting, "shortIds"); ok { - shortIds, _ := sidValue.([]any) - params["sid"] = shortIds[random.Num(len(shortIds))].(string) - } - if fpValue, ok := searchKey(realitySettings, "fingerprint"); ok { - if fp, ok := fpValue.(string); ok && len(fp) > 0 { - params["fp"] = fp - } - } - if pqvValue, ok := searchKey(realitySettings, "mldsa65Verify"); ok { - if pqv, ok := pqvValue.(string); ok && len(pqv) > 0 { - params["pqv"] = pqv - } - } - params["spx"] = "/" + random.Seq(15) - } if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 { params["flow"] = clients[clientIndex].Flow diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js index b6059cf7..111ee1ae 100644 --- a/web/assets/js/model/inbound.js +++ b/web/assets/js/model/inbound.js @@ -1738,7 +1738,7 @@ class Inbound extends XrayCommonClass { 'o': '', }; if (ObjectUtil.isArrEmpty(this.stream.externalProxy)) { - let r = orderChars.split('').map(char => orders[char]).filter(x => x.length > 0).join(separationChar); + let r = orderChars.split('').map(char => orders[char]).filter(x => x && x.length > 0).join(separationChar); result.push({ remark: r, link: this.genLink(addr, port, 'same', r, client) @@ -1746,7 +1746,7 @@ class Inbound extends XrayCommonClass { } else { this.stream.externalProxy.forEach((ep) => { orders['o'] = ep.remark; - let r = orderChars.split('').map(char => orders[char]).filter(x => x.length > 0).join(separationChar); + let r = orderChars.split('').map(char => orders[char]).filter(x => x && x.length > 0).join(separationChar); result.push({ remark: r, link: this.genLink(ep.dest, ep.port, ep.forceTls, r, client)