mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-14 01:45:00 +03:00
73a028f52d
Move the three Xray binary pins (DockerInit.sh, both release.yml
fetches) to v26.10.10, in lockstep with the xtls/xray-core module that
82c77111 already bumped to 701af60772cd.
xdns finalmask (#7090): a domain now takes a "names" list instead of a
single "name", and a resolver is an "addrs" list of udp:// or tcp://
URLs instead of {type, settings:{addr}}. The loader ignores the old
keys rather than failing, so every mask the 26.9.30 seeder wrote would
load with no domain and no resolver and the tunnel would carry nothing.
maskcompat.UpgradeLegacyXdns now lifts both the pre-26.9.30 string lists
and the 26.9.30 objects straight to names/addrs, dropping entries the
old loader refused. A new seeder (XdnsFinalmaskNamesAddrsFix) reruns it
over every stored finalmask, and the save path, GetXrayConfig, both
link importers and the mask editor's mount lift go through it. Template
outbounds now get the build-time heal too: a 26.9.30 template pasted
after the seeder passes the save check, since the core loads that shape
without error. The editor edits names and addrs as tag lists and no
longer requires record types, which the core now defaults (TXT on a
client, A/CNAME/TXT/AAAA on a server). The wire format did not change,
but clients on an older core cannot read the new config shape.
Lua scripts (#6823): dns.script, routing.script and dns servers[].id
are new. The xray page parsed the dns block with a strict schema, so
loading it dropped script and id and the next save deleted them; the
schema now keeps both and the DNS server modal carries the id through
an edit. The hot routing apply built routing in-process, and that build
now resolves the script file from the panel's working directory, so a
script beside the xray binary failed every rule change into a full
restart and broke the node bridge. RoutingService cannot change the
script anyway, so the panel no longer resolves it there.
TLS client roots (#7105): the core now verifies against its bundled
Mozilla roots instead of the OS store unless tlsSettings.useSystemCA is
set. The outbound TLS form gets a switch for it, so an outbound to a
server signed by a privately installed CA can opt back in.
166 lines
4.9 KiB
Go
166 lines
4.9 KiB
Go
package maskcompat
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
// legacyXdnsEDNS0 is the EDNS0 payload the pre-26.9.30 xdns always negotiated;
|
|
// the object shape makes it opt-in and caps every answer at 512 bytes without it.
|
|
const legacyXdnsEDNS0 = 1232
|
|
|
|
var legacyXdnsRecordTypes = map[string]int{"": 16, "txt": 16, "a": 1, "aaaa": 28}
|
|
|
|
// UpgradeLegacyXdns rewrites xdns masks into the names/addrs lists xray-core 26.10.10
|
|
// (#7090) parses: it ignores 26.9.30's name/type+settings keys and fails the string lists.
|
|
func UpgradeLegacyXdns(finalmask any) bool {
|
|
fm, _ := finalmask.(map[string]any)
|
|
masks, _ := fm["udp"].([]any)
|
|
changed := false
|
|
for _, entry := range masks {
|
|
mask, _ := entry.(map[string]any)
|
|
if maskType, _ := mask["type"].(string); !strings.EqualFold(maskType, "xdns") {
|
|
continue
|
|
}
|
|
if settings, ok := mask["settings"].(map[string]any); ok && upgradeLegacyXdnsSettings(settings) {
|
|
changed = true
|
|
}
|
|
}
|
|
return changed
|
|
}
|
|
|
|
// upgradeLegacyXdnsSettings maps a bare name to TXT, the type legacy clients queried by
|
|
// default, and drops entries the old core refused instead of keeping them to fail again.
|
|
func upgradeLegacyXdnsSettings(settings map[string]any) bool {
|
|
rawDomains, _ := settings["domains"].([]any)
|
|
rawResolvers, _ := settings["resolvers"].([]any)
|
|
if !slices.ContainsFunc(rawDomains, isLegacyXdnsDomain) && !slices.ContainsFunc(rawResolvers, isLegacyXdnsResolver) {
|
|
return false
|
|
}
|
|
domains := make([]any, 0, len(rawDomains))
|
|
listed := map[string]bool{}
|
|
addDomain := func(domain map[string]any) {
|
|
names, _ := domain["names"].([]any)
|
|
for _, name := range names {
|
|
if s, ok := name.(string); ok {
|
|
listed[strings.ToLower(s)] = true
|
|
}
|
|
}
|
|
domains = append(domains, domain)
|
|
}
|
|
addLegacyDomain := func(domain map[string]any) {
|
|
if name, _ := domain["names"].([]any)[0].(string); !listed[strings.ToLower(name)] {
|
|
addDomain(domain)
|
|
}
|
|
}
|
|
for _, entry := range rawDomains {
|
|
switch value := entry.(type) {
|
|
case map[string]any:
|
|
addDomain(upgradeXdnsDomainObject(value))
|
|
case string:
|
|
if domain, ok := legacyXdnsDomain(value); ok {
|
|
addLegacyDomain(domain)
|
|
}
|
|
}
|
|
}
|
|
resolvers := make([]any, 0, len(rawResolvers))
|
|
for _, entry := range rawResolvers {
|
|
switch value := entry.(type) {
|
|
case string:
|
|
head, addr, found := strings.Cut(value, "+udp://")
|
|
addr = strings.TrimSpace(addr)
|
|
domain, valid := legacyXdnsDomain(head)
|
|
if !found || addr == "" || !valid {
|
|
continue
|
|
}
|
|
addLegacyDomain(domain)
|
|
resolvers = append(resolvers, map[string]any{"addrs": []any{"udp://" + addr}})
|
|
case map[string]any:
|
|
if !isLegacyXdnsResolver(value) {
|
|
resolvers = append(resolvers, value)
|
|
} else if addr, ok := legacyXdnsResolverAddr(value); ok {
|
|
resolvers = append(resolvers, map[string]any{"addrs": []any{addr}})
|
|
}
|
|
default:
|
|
resolvers = append(resolvers, entry)
|
|
}
|
|
}
|
|
settings["domains"] = domains
|
|
if len(resolvers) > 0 {
|
|
settings["resolvers"] = resolvers
|
|
} else {
|
|
delete(settings, "resolvers")
|
|
}
|
|
return true
|
|
}
|
|
|
|
// upgradeXdnsDomainObject folds a 26.9.30 domain's single "name" into the "names" list.
|
|
func upgradeXdnsDomainObject(domain map[string]any) map[string]any {
|
|
raw, legacy := domain["name"]
|
|
if !legacy {
|
|
return domain
|
|
}
|
|
delete(domain, "name")
|
|
name, _ := raw.(string)
|
|
name = strings.TrimSpace(name)
|
|
names, _ := domain["names"].([]any)
|
|
if name != "" && !slices.Contains(names, any(name)) {
|
|
names = append([]any{name}, names...)
|
|
}
|
|
if names == nil {
|
|
names = []any{}
|
|
}
|
|
domain["names"] = names
|
|
return domain
|
|
}
|
|
|
|
// legacyXdnsResolverAddr turns a 26.9.30 {type, settings:{addr}} resolver into the URL
|
|
// the addrs list takes; that loader refused every type but udp and tcp.
|
|
func legacyXdnsResolverAddr(resolver map[string]any) (string, bool) {
|
|
kind, _ := resolver["type"].(string)
|
|
kind = strings.ToLower(strings.TrimSpace(kind))
|
|
settings, _ := resolver["settings"].(map[string]any)
|
|
addr, _ := settings["addr"].(string)
|
|
addr = strings.TrimSpace(addr)
|
|
if (kind != "udp" && kind != "tcp") || addr == "" {
|
|
return "", false
|
|
}
|
|
return kind + "://" + addr, true
|
|
}
|
|
|
|
// legacyXdnsDomain parses the "name[:txt|a|aaaa]" spec both legacy lists used.
|
|
func legacyXdnsDomain(spec string) (map[string]any, bool) {
|
|
name, method := strings.TrimSpace(spec), ""
|
|
if i := strings.LastIndex(name, ":"); i >= 0 {
|
|
name, method = name[:i], strings.ToLower(name[i+1:])
|
|
}
|
|
name = strings.Trim(name, ".")
|
|
recordType, known := legacyXdnsRecordTypes[method]
|
|
if name == "" || !known {
|
|
return nil, false
|
|
}
|
|
return map[string]any{"names": []any{name}, "types": []any{recordType}, "edns0": legacyXdnsEDNS0}, true
|
|
}
|
|
|
|
func isLegacyXdnsDomain(value any) bool {
|
|
switch domain := value.(type) {
|
|
case string:
|
|
return true
|
|
case map[string]any:
|
|
_, legacy := domain["name"]
|
|
return legacy
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isLegacyXdnsResolver(value any) bool {
|
|
switch resolver := value.(type) {
|
|
case string:
|
|
return true
|
|
case map[string]any:
|
|
_, current := resolver["addrs"]
|
|
return !current
|
|
}
|
|
return false
|
|
}
|