infra/conf: reject conflicting routing and DNS options when script is configured

This commit is contained in:
Meo597
2026-10-12 04:33:31 +08:00
parent 8d79265bce
commit ab90334502
4 changed files with 93 additions and 4 deletions
+11
View File
@@ -272,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,
+41 -2
View File
@@ -2,6 +2,7 @@ package conf_test
import (
"encoding/json"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
@@ -28,7 +29,6 @@ func TestDNSConfigParsing(t *testing.T) {
testCases := []TestCase{
{
Input: `{
"script": "base64:%",
"servers": [{
"address": "8.8.8.8",
"port": 5353,
@@ -53,7 +53,6 @@ func TestDNSConfigParsing(t *testing.T) {
}`,
Parser: parserCreator(),
Output: &dns.Config{
Script: "base64:%",
NameServer: []*dns.NameServer{
{
Address: &net.Endpoint{
@@ -138,3 +137,43 @@ func TestDNSNameServerID(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":"base64:%"}`},
{name: "compatible fields", input: `{"script":"base64:%","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":"base64:%","disableFallback":false,"disableFallbackIfMatch":false,"enableParallelQuery":false,"servers":[{"address":"1.1.1.1","domains":[],"skipFallback":false,"finalQuery":false}]}`},
{name: "domains", input: `{"script":"base64:%","servers":[{"address":"1.1.1.1","domains":["domain:example.com"]}]}`, wantError: "domains"},
{name: "skip fallback", input: `{"script":"base64:%","servers":[{"address":"1.1.1.1","skipFallback":true}]}`, wantError: "skipFallback"},
{name: "final query", input: `{"script":"base64:%","servers":[{"address":"1.1.1.1","finalQuery":true}]}`, wantError: "finalQuery"},
{name: "later server", input: `{"script":"base64:%","servers":["1.1.1.1",{"address":"8.8.8.8","finalQuery":true}]}`, wantError: "server 1"},
{name: "disable fallback", input: `{"script":"base64:%","disableFallback":true}`, wantError: "disableFallback"},
{name: "disable fallback if match", input: `{"script":"base64:%","disableFallbackIfMatch":true}`, wantError: "disableFallbackIfMatch"},
{name: "parallel query", input: `{"script":"base64:%","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
View File
@@ -94,6 +94,9 @@ func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
func (c *RouterConfig) Build() (*router.Config, error) {
config := new(router.Config)
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()
+38 -2
View File
@@ -2,6 +2,7 @@ package conf_test
import (
"encoding/json"
"strings"
"testing"
"time"
_ "unsafe"
@@ -29,7 +30,6 @@ func TestRouterConfig(t *testing.T) {
runMultiTestCase(t, []TestCase{
{
Input: `{
"script": "missing.lua",
"domainStrategy": "AsIs",
"rules": [
{
@@ -91,7 +91,6 @@ func TestRouterConfig(t *testing.T) {
}`,
Parser: createParser(),
Output: &router.Config{
Script: "missing.lua",
DomainStrategy: router.Config_AsIs,
BalancingRule: []*router.BalancingRule{
{
@@ -238,3 +237,40 @@ func TestRouterConfig(t *testing.T) {
},
})
}
func TestRouterScriptConfig(t *testing.T) {
for _, test := range []struct {
name string
input string
wantError bool
}{
{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(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 != 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)
}
})
}
}