mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-20 10:00:58 +00:00
feat(reality): derive a stable per-client spiderX for shared links
The inbound's spiderX now acts as a per-client seed: exports emit sha256(seed|subKey) truncated to a 15-hex "/path", so a client's spx no longer changes on every subscription fetch (#5718) while different clients stop sharing one fingerprintable value. The form gains a regenerate button that rotates every client's path at once. The frontend link builders derive through the same function (lib/xray/spider-x.ts, @noble/hashes) keyed on subId-then-email like the Go subKey, so panel QR/copy links and subscription output agree — cross-language vector tests lock both sides byte-for-byte. streamData now tolerates malformed stored stream settings (unparseable JSON, null tls/reality settings) instead of panicking the subscription request.
This commit is contained in:
+25
-7
@@ -683,7 +683,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
case "tls":
|
||||
applyShareTLSParams(stream, params)
|
||||
case "reality":
|
||||
applyShareRealityParams(stream, params)
|
||||
applyShareRealityParams(stream, params, subKey(clients[clientIndex]))
|
||||
default:
|
||||
params["security"] = "none"
|
||||
}
|
||||
@@ -734,7 +734,7 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||
case "tls":
|
||||
applyShareTLSParams(stream, params)
|
||||
case "reality":
|
||||
applyShareRealityParams(stream, params)
|
||||
applyShareRealityParams(stream, params, subKey(clients[clientIndex]))
|
||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||
params["flow"] = clients[clientIndex].Flow
|
||||
}
|
||||
@@ -1330,7 +1330,7 @@ func hysteriaPinHex(pin string) string {
|
||||
return pin
|
||||
}
|
||||
|
||||
func applyShareRealityParams(stream map[string]any, params map[string]string) {
|
||||
func applyShareRealityParams(stream map[string]any, params map[string]string, clientKey string) {
|
||||
params["security"] = "reality"
|
||||
realitySetting, _ := stream["realitySettings"].(map[string]any)
|
||||
realitySettings, _ := searchKey(realitySetting, "settings")
|
||||
@@ -1356,15 +1356,33 @@ func applyShareRealityParams(stream map[string]any, params map[string]string) {
|
||||
params["pqv"] = pqv
|
||||
}
|
||||
}
|
||||
params["spx"] = "/" + random.Seq(15)
|
||||
seed := ""
|
||||
if spxValue, ok := searchKey(realitySettings, "spiderX"); ok {
|
||||
if spx, ok := spxValue.(string); ok && len(spx) > 0 {
|
||||
params["spx"] = spx
|
||||
}
|
||||
seed, _ = spxValue.(string)
|
||||
}
|
||||
params["spx"] = deriveSpiderX(seed, clientKey)
|
||||
}
|
||||
}
|
||||
|
||||
// subKey returns a stable per-client identity for deterministic derivations,
|
||||
// preferring the subscription id and falling back to the (unique) email.
|
||||
func subKey(c model.Client) string {
|
||||
if c.SubID != "" {
|
||||
return c.SubID
|
||||
}
|
||||
return c.Email
|
||||
}
|
||||
|
||||
// deriveSpiderX maps the inbound's spiderX seed plus a stable client key to a
|
||||
// deterministic per-client "/path"; frontend/src/lib/xray/spider-x.ts mirrors it.
|
||||
func deriveSpiderX(seed, clientKey string) string {
|
||||
if seed == "" && clientKey == "" {
|
||||
return "/" + random.Seq(15)
|
||||
}
|
||||
sum := sha256.Sum256([]byte(seed + "|" + clientKey))
|
||||
return "/" + hex.EncodeToString(sum[:])[:15]
|
||||
}
|
||||
|
||||
func buildVmessLink(obj map[string]any) string {
|
||||
jsonStr, _ := json.MarshalIndent(obj, "", " ")
|
||||
return "vmess://" + base64.StdEncoding.EncodeToString(jsonStr)
|
||||
|
||||
Reference in New Issue
Block a user