mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-25 22:06:09 +00:00
* fix: enable XTLS vision flow for VLESS+XHTTP+vlessenc in UI and share links (#5157) * fix: enable xtls-rprx-vision flow for VLESS XHTTP with vlessenc encryption (#5157) The flow selector was hidden and the vless:// link omitted flow= because: 1. The backend gate (inboundCanEnableTlsFlow) only accepted tcp+tls/reality. 2. The PR #5185 frontend check used `encryption === 'vlessenc'`, which never matches — the stored value is a generated ML-KEM dotted string, not the CLI subcommand name. Fix: extend inboundCanEnableTlsFlow to also return true for XHTTP when a non-none vlessenc encryption/decryption value is present. Update all three call-sites (inbound.go TlsFlowCapable field, client_crud.go clientWithInboundFlow, inbound_clients.go copy-flow path) and the sub/service.go link generator. Scope is XHTTP-only: TCP without tls/reality is intentionally excluded. Add inbound_protocol_test.go covering the new and existing gate combinations, extend client_flow_isolation_test.go with xhttp+vlessenc cases, and add frontend tests for canEnableTlsFlow with real ML-KEM key values. --------- Co-authored-by: rqzbeh <rqzbeh@users.noreply.github.com> Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
@@ -22,9 +22,14 @@ func inboundShadowsocksMethod(protocol, settings string) string {
|
||||
return s.Method
|
||||
}
|
||||
|
||||
// inboundCanEnableTlsFlow mirrors Inbound.canEnableTlsFlow() from the frontend:
|
||||
// XTLS Vision is only valid for VLESS on TCP with tls or reality.
|
||||
func inboundCanEnableTlsFlow(protocol, streamSettings string) bool {
|
||||
// inboundCanEnableTlsFlow mirrors canEnableTlsFlow() from the frontend
|
||||
// (frontend/src/lib/xray/protocol-capabilities.ts). XTLS Vision is valid for
|
||||
// VLESS on TCP with tls or reality (classic), and on XHTTP when VLESS encryption
|
||||
// (vlessenc / ML-KEM) is enabled — there the post-quantum, VLESS-level
|
||||
// encryption stands in for the transport TLS that Vision relies on. settings is
|
||||
// the inbound's raw settings JSON, which carries the encryption value
|
||||
// (streamSettings does not).
|
||||
func inboundCanEnableTlsFlow(protocol, streamSettings, settings string) bool {
|
||||
if protocol != string(model.VLESS) {
|
||||
return false
|
||||
}
|
||||
@@ -38,15 +43,51 @@ func inboundCanEnableTlsFlow(protocol, streamSettings string) bool {
|
||||
if err := json.Unmarshal([]byte(streamSettings), &stream); err != nil {
|
||||
return false
|
||||
}
|
||||
if stream.Network != "tcp" {
|
||||
switch stream.Network {
|
||||
case "tcp":
|
||||
return stream.Security == "tls" || stream.Security == "reality"
|
||||
case "xhttp":
|
||||
return vlessEncryptionEnabled(settings)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return stream.Security == "tls" || stream.Security == "reality"
|
||||
}
|
||||
|
||||
// vlessEncryptionEnabled reports whether a VLESS inbound has VLESS-level
|
||||
// encryption (vlessenc / ML-KEM) configured. When enabled these fields hold a
|
||||
// generated dotted string (e.g. "mlkem768x25519plus.native.0rtt.<key>"); "none"
|
||||
// or empty means off. The value is never the literal "vlessenc" — that is the
|
||||
// name of the `xray vlessenc` CLI subcommand, not a stored value.
|
||||
//
|
||||
// Both fields are checked: decryption is the authoritative server-side value
|
||||
// xray-core reads, while encryption is stored by the panel for link generation.
|
||||
// The ML-KEM/X25519 buttons set both, but accepting either keeps the gate
|
||||
// working for inbounds configured via the API or raw JSON.
|
||||
func vlessEncryptionEnabled(settings string) bool {
|
||||
if settings == "" {
|
||||
return false
|
||||
}
|
||||
var s struct {
|
||||
Encryption string `json:"encryption"`
|
||||
Decryption string `json:"decryption"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(settings), &s); err != nil {
|
||||
return false
|
||||
}
|
||||
return vlessEncValueSet(s.Encryption) || vlessEncValueSet(s.Decryption)
|
||||
}
|
||||
|
||||
// vlessEncValueSet reports whether a VLESS encryption/decryption field holds a
|
||||
// real (generated) value rather than the "none"/empty sentinel.
|
||||
func vlessEncValueSet(v string) bool {
|
||||
return v != "" && v != "none"
|
||||
}
|
||||
|
||||
// inboundCanHostFallbacks gates the settings.fallbacks injection.
|
||||
// Xray only honors fallbacks on VLESS and Trojan inbounds carried over
|
||||
// TCP transport with TLS or Reality security.
|
||||
// TCP transport with TLS or Reality security. This is intentionally stricter
|
||||
// than inboundCanEnableTlsFlow (which also accepts XHTTP+vlessenc): fallbacks
|
||||
// are a raw-TCP-only feature.
|
||||
func inboundCanHostFallbacks(ib *model.Inbound) bool {
|
||||
if ib == nil {
|
||||
return false
|
||||
@@ -54,13 +95,13 @@ func inboundCanHostFallbacks(ib *model.Inbound) bool {
|
||||
if ib.Protocol != model.VLESS && ib.Protocol != model.Trojan {
|
||||
return false
|
||||
}
|
||||
return inboundCanEnableTlsFlow(string(ib.Protocol), ib.StreamSettings) ||
|
||||
(ib.Protocol == model.Trojan && trojanStreamSupportsFallbacks(ib.StreamSettings))
|
||||
return streamSupportsFallbacks(ib.StreamSettings)
|
||||
}
|
||||
|
||||
// trojanStreamSupportsFallbacks mirrors the Trojan side of the same gate
|
||||
// (Trojan reuses XTLS-Vision capable streams: tcp + tls or reality).
|
||||
func trojanStreamSupportsFallbacks(streamSettings string) bool {
|
||||
// streamSupportsFallbacks reports whether the stream is raw TCP carried over
|
||||
// TLS or REALITY — the only transport Xray honors inbound fallbacks on (and the
|
||||
// classic requirement for XTLS Vision before vlessenc).
|
||||
func streamSupportsFallbacks(streamSettings string) bool {
|
||||
if streamSettings == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user