mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 11:15:03 +03:00
fix(xray): refuse xhttp downloadSettings the core's dialer cannot use
ValidateOutboundConfig, the save-time gate shared by template saves, API saves and outbound subscription refreshes, relied on conf.Build, which accepts a downloadSettings block with no address or on another transport. The core's XHTTP dialer dereferences that address and asserts that transport on every dial, so an imported link or a subscription carrying such a block panicked Xray, and every user's connections with it, whenever traffic was routed to that outbound. The built sender settings are now checked for both, and the outbound is refused with a message that names the missing part.
This commit is contained in:
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/common"
|
||||
wgutil "github.com/mhsanaei/3x-ui/v3/internal/util/wireguard"
|
||||
|
||||
"github.com/xtls/xray-core/app/proxyman"
|
||||
"github.com/xtls/xray-core/app/proxyman/command"
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
routerService "github.com/xtls/xray-core/app/router/command"
|
||||
@@ -42,6 +43,7 @@ import (
|
||||
vlessOutbound "github.com/xtls/xray-core/proxy/vless/outbound"
|
||||
"github.com/xtls/xray-core/proxy/vmess"
|
||||
wireguard "github.com/xtls/xray-core/proxy/wireguard"
|
||||
"github.com/xtls/xray-core/transport/internet/splithttp"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
@@ -221,9 +223,44 @@ func ValidateOutboundConfig(outbound []byte) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateXhttpDownloadSettings(built.SenderSettings); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateHandlerSettings(built.ProxySettings)
|
||||
}
|
||||
|
||||
// validateXhttpDownloadSettings refuses what conf.Build() lets through but the core's XHTTP
|
||||
// dialer panics on at every dial: a downloadSettings block without an address or not on xhttp.
|
||||
func validateXhttpDownloadSettings(settings *serial.TypedMessage) error {
|
||||
if settings == nil {
|
||||
return nil
|
||||
}
|
||||
instance, err := settings.GetInstance()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
sender, ok := instance.(*proxyman.SenderConfig)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
transport, err := sender.StreamSettings.GetEffectiveTransportSettings()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
xhttp, ok := transport.(*splithttp.Config)
|
||||
if !ok || xhttp.DownloadSettings == nil {
|
||||
return nil
|
||||
}
|
||||
if xhttp.DownloadSettings.Address == nil {
|
||||
return common.NewError("xhttp downloadSettings has no address")
|
||||
}
|
||||
transport, _ = xhttp.DownloadSettings.GetEffectiveTransportSettings()
|
||||
if _, ok := transport.(*splithttp.Config); !ok {
|
||||
return common.NewError("xhttp downloadSettings network is not xhttp")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateHandlerSettings refuses what conf.Build() lets through but the core parses only
|
||||
// in the handler constructor, where one failure aborts the whole core start.
|
||||
func validateHandlerSettings(settings *serial.TypedMessage) error {
|
||||
|
||||
@@ -71,6 +71,42 @@ func TestValidateOutboundConfig_RejectsWireguardRemoteDNSThatIsNotAnIP(t *testin
|
||||
}
|
||||
}
|
||||
|
||||
// The core's XHTTP dialer dereferences downloadSettings' address and casts its transport to
|
||||
// XHTTP on every dial, so a block lacking either panics the whole core; conf.Build() lets it through.
|
||||
func TestValidateOutboundConfig_RejectsXhttpDownloadSettingsTheDialerPanicsOn(t *testing.T) {
|
||||
outbound := func(network, xhttpSettings string) []byte {
|
||||
return []byte(`{
|
||||
"protocol": "vless",
|
||||
"settings": {"address": "1.2.3.4", "port": 443, "id": "b831381d-6324-4d53-ad4f-8cda48b30811", "encryption": "none"},
|
||||
"streamSettings": {"network": "` + network + `", "security": "tls", "xhttpSettings": ` + xhttpSettings + `}
|
||||
}`)
|
||||
}
|
||||
for _, rejected := range []string{
|
||||
`{"downloadSettings": {}}`,
|
||||
`{"downloadSettings": {"port": 443, "network": "xhttp"}}`,
|
||||
`{"downloadSettings": {"address": null, "port": 443, "network": "xhttp"}}`,
|
||||
`{"downloadSettings": {"address": "5.6.7.8", "port": 443}}`,
|
||||
`{"downloadSettings": {"address": "5.6.7.8", "port": 443, "network": "tcp"}}`,
|
||||
`{"downloadSettings": {"address": "5.6.7.8", "port": 443, "method": "ws"}}`,
|
||||
`{"DOWNLOADSETTINGS": {}}`,
|
||||
} {
|
||||
err := ValidateOutboundConfig(outbound("xhttp", rejected))
|
||||
if err == nil || !strings.Contains(err.Error(), "downloadSettings") {
|
||||
t.Errorf("xhttpSettings %s: want a downloadSettings refusal, got %v", rejected, err)
|
||||
}
|
||||
}
|
||||
for _, accepted := range [][]byte{
|
||||
outbound("xhttp", `{"path": "/up"}`),
|
||||
outbound("xhttp", `{"downloadSettings": {"address": "5.6.7.8", "port": 443, "network": "xhttp"}}`),
|
||||
outbound("xhttp", `{"downloadSettings": {"address": "down.example", "port": 443, "network": "splithttp", "security": "tls", "xhttpSettings": {"path": "/down"}}}`),
|
||||
outbound("tcp", `{"downloadSettings": {}}`),
|
||||
} {
|
||||
if err := ValidateOutboundConfig(accepted); err != nil {
|
||||
t.Errorf("outbound %s must stay valid, got: %v", accepted, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// xray-core 26.10.17 compiles a noise mask's Lua script inside Build (#7124), where the panel would
|
||||
// look beside its own binary; the xray child finds a relative script in its bin folder.
|
||||
func TestValidateOutboundConfig_FindsANoiseScriptBesideTheXrayBinary(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user