mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 11:15:03 +03:00
87749c1ee4
xray-core 26.10.31 (#7288) widened a TUN inbound's autoSystemWfpBlockLeak: "dns" now also blocks DNS over TLS/QUIC (port 853), "windnsclient" covers only Windows' DNS Client service, and any "tcp/<port>" or "udp/<port>" keeps that port inside the TUN. Anything else, such as "tcp/0" or "icmp/1", stops the core at startup. The form offers windnsclient and takes typed port values, the hint explains them in all 14 locales, and the inbound and template save paths run the core's own ExpandWfpBlockLeak so a value it refuses is refused at save instead of taking every inbound down.
183 lines
6.3 KiB
Go
183 lines
6.3 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"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/xray"
|
|
|
|
"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"
|
|
}
|
|
|
|
// validateTunWfpBlockLeak refuses at save the autoSystemWfpBlockLeak values xray-core 26.10.31
|
|
// (#7288) refuses when the core starts, which stops every inbound with it.
|
|
func validateTunWfpBlockLeak(protocol, settings string) error {
|
|
if protocol != "tun" {
|
|
return nil
|
|
}
|
|
var tunSettings struct {
|
|
AutoSystemWfpBlockLeak []string `json:"autoSystemWfpBlockLeak"`
|
|
}
|
|
if json.Unmarshal([]byte(settings), &tunSettings) != nil {
|
|
return nil
|
|
}
|
|
if err := xray.ValidateTunWfpBlockLeak(tunSettings.AutoSystemWfpBlockLeak); err != nil {
|
|
return common.NewError("TUN autoSystemWfpBlockLeak: ", err)
|
|
}
|
|
return nil
|
|
}
|