mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-23 06:15:01 +03:00
61b129d5b3
Bump xtls/xray-core to 1b1647cd9ddf (v26.10.21) and move the three binary pins (DockerInit.sh, both release.yml fetches) in lockstep. The v26.10.10 release was pulled upstream, so the old pins already 404. XDRIVE (#7182): the core now runs XDRIVE only for VLESS inside VLESS encryption, with no flow, and an outbound only over Mux; it refuses the whole config otherwise. The panel offers XDRIVE only for VLESS, refuses to save an inbound without decryption, never emits a client flow on an XDRIVE stream (config, JSON subscription, forms), and turns Mux on when an outbound picks XDRIVE. An inbound or template outbound stored before the upgrade that the core would refuse is left out of the generated config with a warning, so it no longer stops every other inbound. The service "Google Drive" is now "google-drive" (old name read as a deprecated alias); saves, the schema and both form adapters store the new name. XDRIVE and MASQUE gain a client xmux; both schemas keep it instead of stripping it on an inbound save. Lua scripts (#7124, #7136): the core refuses a routing script beside rules or domainStrategy, and a dns script beside the fallback/parallel switches or a domain-scoped server. The panel always keeps its api routing rule, so a template save now refuses any routing script and a dns script in a refused combination, and a stored one is dropped from the generated config so the core still starts. Noise masks gain a Lua script that the loader compiles inside Build: the in-process build (template save check, hot add) now resolves a relative script the way the xray child does, beside the xray binary, instead of failing it. The mask editor gets a Script field that replaces the noise list. XDNS (#7140): a resolver takes https:// DoH addresses with worker, tlsConfig and quicConfig. The mask editor registered only addrs, so a save dropped the rest; it now edits all four. Finalmask filter (#7206): a new TCP/UDP mask screens an inbound's source IPs. The inbound mask editor offers it with its allow and block lists; it is a no-op on a dialer and an older client core refuses the type, so the share link and JSON subscription drop it like the Go link already does.
164 lines
5.6 KiB
Go
164 lines
5.6 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/util/common"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/util/version"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// inboundShadowsocksMethod extracts settings.method for Shadowsocks inbounds so
|
|
// the client UI can generate a valid PSK (base64 of the method's key length)
|
|
// for Shadowsocks 2022 ciphers. Returns "" for non-Shadowsocks inbounds.
|
|
func inboundShadowsocksMethod(protocol, settings string) string {
|
|
if protocol != string(model.Shadowsocks) || settings == "" {
|
|
return ""
|
|
}
|
|
var s struct {
|
|
Method string `json:"method"`
|
|
}
|
|
if err := json.Unmarshal([]byte(settings), &s); err != nil {
|
|
return ""
|
|
}
|
|
return s.Method
|
|
}
|
|
|
|
// inboundCanEnableTlsFlow mirrors the frontend's canEnableTlsFlow: xray-core runs Vision over
|
|
// TLS/REALITY on raw TCP, or inside VLESS encryption on any transport but XDRIVE (#7182).
|
|
func inboundCanEnableTlsFlow(protocol, streamSettings, settings string) bool {
|
|
if protocol != string(model.VLESS) {
|
|
return false
|
|
}
|
|
var stream struct {
|
|
Network string `json:"network"`
|
|
Security string `json:"security"`
|
|
}
|
|
_ = json.Unmarshal([]byte(streamSettings), &stream)
|
|
if stream.Network == "xdrive" {
|
|
return false
|
|
}
|
|
if stream.Network == "tcp" && (stream.Security == "tls" || stream.Security == "reality") {
|
|
return true
|
|
}
|
|
return vlessEncryptionEnabled(settings)
|
|
}
|
|
|
|
// nodeEligibleProtocols mirrors the frontend's NODE_ELIGIBLE_PROTOCOLS. A sidecar
|
|
// protocol's row is local on the node it is pushed to, so that panel runs it.
|
|
var nodeEligibleProtocols = map[model.Protocol]bool{
|
|
model.VLESS: true,
|
|
model.VMESS: true,
|
|
model.Trojan: true,
|
|
model.Shadowsocks: true,
|
|
model.Hysteria: true,
|
|
model.WireGuard: true,
|
|
model.MTProto: true,
|
|
model.AmneziaWG: true,
|
|
model.TUIC: true,
|
|
model.MASQUE: true,
|
|
}
|
|
|
|
// nodeProtocolFirstRelease is the panel release that introduced each protocol
|
|
// newer than node support itself; an older node would hand it to Xray as-is.
|
|
var nodeProtocolFirstRelease = map[model.Protocol]string{
|
|
model.MTProto: "v3.5.0",
|
|
model.AmneziaWG: "v3.7.0",
|
|
model.TUIC: "v3.8.0",
|
|
model.MASQUE: "v3.9.1",
|
|
}
|
|
|
|
// checkNodeCanHostProtocol refuses assigning protocol to nodeID unless the
|
|
// protocol may live on a node and that node's panel is new enough to run it.
|
|
func checkNodeCanHostProtocol(db *gorm.DB, nodeID int, protocol model.Protocol) error {
|
|
if !nodeEligibleProtocols[protocol] {
|
|
return common.NewErrorf("%s inbounds cannot be assigned to a node", protocol)
|
|
}
|
|
firstRelease, ok := nodeProtocolFirstRelease[protocol]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
var node model.Node
|
|
if err := db.Select("id", "name", "panel_version").First(&node, nodeID).Error; err != nil {
|
|
return err
|
|
}
|
|
if strings.TrimSpace(node.PanelVersion) == "" {
|
|
return common.NewErrorf("node %q has not reported its panel version yet; %s inbounds need %s or newer",
|
|
node.Name, protocol, firstRelease)
|
|
}
|
|
// A dev build reports "dev+<sha>": it tracks main, which carries every protocol.
|
|
if cmp, ok := version.Compare(node.PanelVersion, firstRelease); ok && cmp < 0 {
|
|
return common.NewErrorf("node %q runs panel %s; %s inbounds need %s or newer",
|
|
node.Name, node.PanelVersion, protocol, firstRelease)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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. This is intentionally stricter
|
|
// than inboundCanEnableTlsFlow (which also accepts any vlessenc stream): fallbacks
|
|
// are a raw-TCP-only feature.
|
|
func inboundCanHostFallbacks(ib *model.Inbound) bool {
|
|
if ib == nil {
|
|
return false
|
|
}
|
|
if ib.Protocol != model.VLESS && ib.Protocol != model.Trojan {
|
|
return false
|
|
}
|
|
return streamSupportsFallbacks(ib.StreamSettings)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
var stream struct {
|
|
Network string `json:"network"`
|
|
Security string `json:"security"`
|
|
}
|
|
if err := json.Unmarshal([]byte(streamSettings), &stream); err != nil {
|
|
return false
|
|
}
|
|
if stream.Network != "tcp" {
|
|
return false
|
|
}
|
|
return stream.Security == "tls" || stream.Security == "reality"
|
|
}
|