Meow
2026-10-12 16:58:20 +00:00
committed by GitHub
parent 2bea2df6a1
commit 5a64db009b
27 changed files with 698 additions and 153 deletions
+12 -9
View File
@@ -14,7 +14,6 @@ import (
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform"
)
type NameServerConfig struct {
@@ -273,6 +272,17 @@ func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) {
// Build implements Buildable
func (c *DNSConfig) Build() (*dns.Config, error) {
if c.Script != "" {
if c.DisableFallback || c.DisableFallbackIfMatch || c.EnableParallelQuery {
return nil, errors.New("DNS script cannot be used with disableFallback, disableFallbackIfMatch or enableParallelQuery")
}
for i, server := range c.Servers {
if len(server.Domains) > 0 || server.SkipFallback || server.FinalQuery {
return nil, errors.New("DNS script cannot be used with domains, skipFallback or finalQuery in server ", i)
}
}
}
config := &dns.Config{
Tag: c.Tag,
DisableCache: c.DisableCache,
@@ -282,14 +292,7 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
DisableFallbackIfMatch: c.DisableFallbackIfMatch,
EnableParallelQuery: c.EnableParallelQuery,
QueryStrategy: resolveQueryStrategy(c.QueryStrategy),
}
if c.Script != "" {
path, err := platform.ResolveLuaFile(c.Script)
if err != nil {
return nil, errors.New("failed to resolve DNS script: ", c.Script).Base(err)
}
config.Script = path
Script: c.Script,
}
if c.ClientIP != nil {
+42 -37
View File
@@ -2,8 +2,7 @@ package conf_test
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
@@ -125,41 +124,7 @@ func TestDNSConfigParsing(t *testing.T) {
}
}
func TestDNSScriptConfig(t *testing.T) {
dir := t.TempDir()
t.Setenv("xray.location.confdir", dir)
path := filepath.Join(dir, "lookup.lua")
if err := os.WriteFile(path, []byte("function HandleDNSQuery(domain, ipv4, ipv6, fake) end"), 0o600); err != nil {
t.Fatal(err)
}
for _, tc := range []struct {
name string
script string
wantError bool
}{
{"relative", "lookup.lua", false},
{"absolute", path, false},
{"missing", "missing.lua", true},
{"directory", dir, true},
} {
t.Run(tc.name, func(t *testing.T) {
built, err := (&DNSConfig{Script: tc.script}).Build()
if tc.wantError {
if err == nil {
t.Fatal("Build accepted an invalid script path")
}
return
}
if err != nil {
t.Fatal(err)
}
if built.Script != path {
t.Fatalf("script path = %q, want %q", built.Script, path)
}
})
}
func TestDNSNameServerID(t *testing.T) {
var parsed DNSConfig
if err := json.Unmarshal([]byte(`{"servers":[{"id":"primary","address":"1.1.1.1"}]}`), &parsed); err != nil {
t.Fatal(err)
@@ -172,3 +137,43 @@ func TestDNSScriptConfig(t *testing.T) {
t.Fatalf("nameserver IDs = %v, want primary", built.NameServer)
}
}
func TestDNSScriptConfig(t *testing.T) {
for _, test := range []struct {
name string
input string
wantError string
}{
{name: "script only", input: `{"script":"script.lua"}`},
{name: "compatible fields", input: `{"script":"script.lua","servers":["1.1.1.1",{"id":"primary","address":"8.8.8.8","expectedIPs":["8.8.8.8"],"queryStrategy":"UseIPv4","disableCache":true}],"hosts":{"example.com":"127.0.0.1"},"disableCache":true}`},
{name: "disabled options", input: `{"script":"script.lua","disableFallback":false,"disableFallbackIfMatch":false,"enableParallelQuery":false,"servers":[{"address":"1.1.1.1","domains":[],"skipFallback":false,"finalQuery":false}]}`},
{name: "domains", input: `{"script":"script.lua","servers":[{"address":"1.1.1.1","domains":["domain:example.com"]}]}`, wantError: "domains"},
{name: "skip fallback", input: `{"script":"script.lua","servers":[{"address":"1.1.1.1","skipFallback":true}]}`, wantError: "skipFallback"},
{name: "final query", input: `{"script":"script.lua","servers":[{"address":"1.1.1.1","finalQuery":true}]}`, wantError: "finalQuery"},
{name: "later server", input: `{"script":"script.lua","servers":["1.1.1.1",{"address":"8.8.8.8","finalQuery":true}]}`, wantError: "server 1"},
{name: "disable fallback", input: `{"script":"script.lua","disableFallback":true}`, wantError: "disableFallback"},
{name: "disable fallback if match", input: `{"script":"script.lua","disableFallbackIfMatch":true}`, wantError: "disableFallbackIfMatch"},
{name: "parallel query", input: `{"script":"script.lua","enableParallelQuery":true}`, wantError: "enableParallelQuery"},
{name: "empty script", input: `{"script":"","disableFallback":true,"disableFallbackIfMatch":true,"enableParallelQuery":true,"servers":[{"address":"1.1.1.1","domains":["example.com"],"skipFallback":true,"finalQuery":true}]}`},
} {
t.Run(test.name, func(t *testing.T) {
var config DNSConfig
if err := json.Unmarshal([]byte(test.input), &config); err != nil {
t.Fatal(err)
}
built, err := config.Build()
if test.wantError != "" {
if built != nil || err == nil || !strings.Contains(err.Error(), "DNS script cannot be used with ") || !strings.Contains(err.Error(), test.wantError) {
t.Fatalf("Build() = %v, %v; want nil config and script conflict error mentioning %s", built, err, test.wantError)
}
return
}
if err != nil {
t.Fatal(err)
}
if built.Script != config.Script || len(built.NameServer) != len(config.Servers) {
t.Fatalf("Build() did not preserve script or servers: %v", built)
}
})
}
}
+3 -7
View File
@@ -7,7 +7,6 @@ import (
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/geodata"
"github.com/xtls/xray-core/common/platform"
"github.com/xtls/xray-core/common/serial"
"google.golang.org/protobuf/proto"
@@ -95,13 +94,10 @@ func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
func (c *RouterConfig) Build() (*router.Config, error) {
config := new(router.Config)
if c.Script != "" {
path, err := platform.ResolveLuaFile(c.Script)
if err != nil {
return nil, errors.New("failed to resolve routing script").Base(err)
}
config.Script = path
if c.Script != "" && (c.DomainStrategy != nil || len(c.RuleList) > 0) {
return nil, errors.New("routing script cannot be used with domainStrategy or rules")
}
config.Script = c.Script
config.DomainStrategy = c.getDomainStrategy()
+22 -22
View File
@@ -2,8 +2,7 @@ package conf_test
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"time"
_ "unsafe"
@@ -240,36 +239,37 @@ func TestRouterConfig(t *testing.T) {
}
func TestRouterScriptConfig(t *testing.T) {
dir := t.TempDir()
t.Setenv("xray.location.confdir", dir)
path := filepath.Join(dir, "route.lua")
if err := os.WriteFile(path, []byte("function HandleRoute() end"), 0o600); err != nil {
t.Fatal(err)
}
for _, tc := range []struct {
for _, test := range []struct {
name string
script string
input string
wantError bool
}{
{"relative", "route.lua", false},
{"absolute", path, false},
{"missing", "missing.lua", true},
{"directory", dir, true},
{name: "script only", input: `{"script":"missing.lua"}`},
{name: "empty rules", input: `{"script":"missing.lua","rules":[]}`},
{name: "balancers", input: `{"script":"missing.lua","balancers":[{"tag":"b1","selector":["direct"]}]}`},
{name: "default domain strategy", input: `{"script":"missing.lua","domainStrategy":"AsIs"}`, wantError: true},
{name: "IP domain strategy", input: `{"script":"missing.lua","domainStrategy":"IPIfNonMatch"}`, wantError: true},
{name: "empty domain strategy", input: `{"script":"missing.lua","domainStrategy":""}`, wantError: true},
{name: "rules", input: `{"script":"missing.lua","rules":[{"outboundTag":"direct","domain":["example.com"]}]}`, wantError: true},
{name: "empty script", input: `{"script":"","domainStrategy":"IPOnDemand","rules":[{"outboundTag":"direct","domain":["example.com"]}]}`},
} {
t.Run(tc.name, func(t *testing.T) {
built, err := (&RouterConfig{Script: tc.script}).Build()
if tc.wantError {
if err == nil {
t.Fatal("Build accepted invalid script path")
t.Run(test.name, func(t *testing.T) {
var config RouterConfig
if err := json.Unmarshal([]byte(test.input), &config); err != nil {
t.Fatal(err)
}
built, err := config.Build()
if test.wantError {
if built != nil || err == nil || !strings.Contains(err.Error(), "routing script cannot be used with domainStrategy or rules") {
t.Fatalf("Build() = %v, %v; want nil config and script conflict error", built, err)
}
return
}
if err != nil {
t.Fatal(err)
}
if built.Script != path {
t.Fatalf("script path = %q, want %q", built.Script, path)
if built.Script != config.Script || len(built.Rule) != len(config.RuleList) || len(built.BalancingRule) != len(config.Balancers) {
t.Fatalf("Build() did not preserve script, rules or balancers: %v", built)
}
})
}