mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-06 14:12:09 +03:00
62423cacd1
Bump xtls/xray-core to b26a91de4f (v26.9.30) and the three binary pins (DockerInit.sh, release.yml Linux + Windows) in lockstep. No deleted symbols; the sing and sing-shadowsocks indirect deps drop out with the SS2022 rewrite. XDNS finalmask (#6718) replaced its string lists with objects: domains are {name, types, edns0, lenLimit, labelLimit} and client resolvers {type, settings.addr}. The loader no longer parses the old lists, so a single stored xdns mask keeps the whole core from starting. The new leaf package internal/util/maskcompat converts them: "name[:type]" becomes a domain and "name[:type]+udp://addr" a domain plus a udp resolver. A bare name maps to TXT, the type legacy clients queried by default, and each converted domain keeps the 1232-byte EDNS0 the old code always used (without it the server caps answers at 512). It runs from: - the XdnsFinalmaskObjectsFix seeder, over inbound streams, hosts, the xray template, the global sub-JSON mask and cached subscription outbounds; - inbound save (normalizeStreamSettings) and GetXrayConfig, for rows that never went through the seeder; - both link importers, since fm= from an older panel carries the lists. The finalmask form edits the object shape (every key needs a registered field, or the finalmask watch drops it on save) and lifts legacy masks on open. The udp-mask golden fixture moves to the object shape, which TestGoldenStreamFixturesBuildInXray now builds through the core. The wire format changed as well, so pre-upgrade clients need the new core. WireGuard outbound (#6771) dropped settings.domainStrategy and the remoteDNS "local" mode. The endpoint lookup now follows sockopt.domainStrategy and in-tunnel targets the outbound's targetStrategy. The old key is silently ignored, which undid the IPv4-first endpoint lookup the WARP outbound depends on (#5205), and "local" now panics the core at startup because remoteDNS goes through netip.MustParseAddr. The WireguardDomainStrategyFix seeder moves a stored family preference to both keys (a value already set wins) and turns "local" into targetStrategy; the outbound form lifts legacy rows the same way and drops its select, the WARP modal writes the new placement, and the inbound form loses a field the server never read. ValidateOutboundConfig now refuses a non-IP remoteDNS entry, which conf.Build() lets through, on template save and for outbound subscriptions. Noise finalmask items accept type "exp" (#6862), a tag expression. The form offers it for noise items only: header-custom items go through the core's PraseByteSlice, which refuses it. TUN gained autoSystemDnsToGateway (Linux) and autoSystemWfpBlockLeak (Windows). Both pass through the settings schema so a value set in JSON survives the next form save. MASQUE (inbound, outbound, transport) and the XDRIVE transport are new protocols the panel does not offer yet; their new loader refusals only cover configs the panel never generates. The FakeDNS IPv6 pool default, the SS2022 rewrite (same gRPC account; emails are now deduped case-insensitively, as the panel already does), the restored udphop interval default and the rest change no panel-facing config.
92 lines
3.3 KiB
Go
92 lines
3.3 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
const legacyXdnsStream = `{"network":"kcp","security":"none","kcpSettings":{"mtu":900},"finalmask":{"udp":[{"type":"xdns","settings":{"domains":["t.example.com"]}}]}}`
|
|
|
|
func firstXdnsDomain(t *testing.T, stream map[string]any) any {
|
|
t.Helper()
|
|
finalmask, _ := stream["finalmask"].(map[string]any)
|
|
udp, _ := finalmask["udp"].([]any)
|
|
if len(udp) != 1 {
|
|
t.Fatalf("finalmask.udp = %v, want one mask", finalmask["udp"])
|
|
}
|
|
mask, _ := udp[0].(map[string]any)
|
|
settings, _ := mask["settings"].(map[string]any)
|
|
domains, _ := settings["domains"].([]any)
|
|
if len(domains) != 1 {
|
|
t.Fatalf("xdns domains = %v, want one", settings["domains"])
|
|
}
|
|
return domains[0]
|
|
}
|
|
|
|
// An API client can still post the pre-26.9.30 string lists; stored as sent they would
|
|
// reach the sub links and the form in a shape the core no longer parses.
|
|
func TestAddInbound_StoresXdnsMaskInObjectShape(t *testing.T) {
|
|
setupConflictDB(t)
|
|
in := &model.Inbound{
|
|
Tag: "in-45300-kcp", Enable: true, Listen: "0.0.0.0", Port: 45300, Protocol: model.VLESS,
|
|
Settings: `{"clients":[],"decryption":"none"}`, StreamSettings: legacyXdnsStream,
|
|
}
|
|
if _, _, err := (&InboundService{}).AddInbound(in); err != nil {
|
|
t.Fatalf("AddInbound: %v", err)
|
|
}
|
|
|
|
var stored model.Inbound
|
|
if err := database.GetDB().Where("tag = ?", "in-45300-kcp").First(&stored).Error; err != nil {
|
|
t.Fatalf("reload: %v", err)
|
|
}
|
|
var stream map[string]any
|
|
if err := json.Unmarshal([]byte(stored.StreamSettings), &stream); err != nil {
|
|
t.Fatalf("stored stream is not JSON: %v", err)
|
|
}
|
|
domain, ok := firstXdnsDomain(t, stream).(map[string]any)
|
|
if !ok || domain["name"] != "t.example.com" {
|
|
t.Fatalf("stored xdns domain = %#v, want an object named t.example.com", firstXdnsDomain(t, stream))
|
|
}
|
|
}
|
|
|
|
// A row that never went through the save path (restored backup, node sync, direct DB
|
|
// edit) must still reach the core in a shape it builds, or it keeps every inbound down.
|
|
func TestGetXrayConfig_UpgradesLegacyXdnsMask(t *testing.T) {
|
|
setupConflictDB(t)
|
|
seedInboundConflict(t, "in-45301-kcp", "127.0.0.1", 45301, model.VLESS,
|
|
legacyXdnsStream, `{"clients":[],"decryption":"none"}`)
|
|
|
|
var legacy map[string]any
|
|
if err := json.Unmarshal([]byte(`{"tag":"in-45301-kcp","listen":"127.0.0.1","port":45301,"protocol":"vless",
|
|
"settings":{"clients":[],"decryption":"none"},"streamSettings":`+legacyXdnsStream+`}`), &legacy); err != nil {
|
|
t.Fatalf("decode legacy inbound: %v", err)
|
|
}
|
|
if err := buildGoldenInbound(t, legacy); err == nil {
|
|
t.Fatal("xray-core accepted the legacy xdns lists; the heal is no longer needed")
|
|
}
|
|
|
|
cfg, err := (&XrayService{}).GetXrayConfig()
|
|
if err != nil {
|
|
t.Fatalf("GetXrayConfig: %v", err)
|
|
}
|
|
for i := range cfg.InboundConfigs {
|
|
if cfg.InboundConfigs[i].Tag != "in-45301-kcp" {
|
|
continue
|
|
}
|
|
raw, err := json.Marshal(cfg.InboundConfigs[i])
|
|
if err != nil {
|
|
t.Fatalf("marshal emitted inbound: %v", err)
|
|
}
|
|
var emitted map[string]any
|
|
if err := json.Unmarshal(raw, &emitted); err != nil {
|
|
t.Fatalf("decode emitted inbound: %v", err)
|
|
}
|
|
assertXrayAccepts(t, "the healed xdns inbound", buildGoldenInbound(t, emitted))
|
|
return
|
|
}
|
|
t.Fatal("inbound in-45301-kcp not found in the generated config")
|
|
}
|