test: remove low-value DNS WHAT tests

This commit is contained in:
世界
2026-04-01 16:39:12 +08:00
parent 1362e5d4fc
commit aaa11fbdf1
5 changed files with 0 additions and 1018 deletions

View File

@@ -78,11 +78,6 @@ type fakeDNSClient struct {
lookup func(transport adapter.DNSTransport, domain string, options adapter.DNSQueryOptions) ([]netip.Addr, *mDNS.Msg, error)
}
type recordingExchangeDNSClient struct {
beforeExchange func(ctx context.Context, transport adapter.DNSTransport, message *mDNS.Msg, options adapter.DNSQueryOptions)
exchange func(transport adapter.DNSTransport, message *mDNS.Msg, options adapter.DNSQueryOptions) (*mDNS.Msg, error)
}
type fakeDeprecatedManager struct {
features []deprecated.Note
}
@@ -261,26 +256,8 @@ func (c *fakeDNSClient) Lookup(_ context.Context, transport adapter.DNSTransport
return MessageToAddresses(response), nil
}
func (c *recordingExchangeDNSClient) Start() {}
func (c *recordingExchangeDNSClient) Exchange(ctx context.Context, transport adapter.DNSTransport, message *mDNS.Msg, options adapter.DNSQueryOptions, _ func(*mDNS.Msg) bool) (*mDNS.Msg, error) {
if c.beforeExchange != nil {
c.beforeExchange(ctx, transport, message, options)
}
if c.exchange == nil {
return nil, E.New("unused client exchange")
}
return c.exchange(transport, message, options)
}
func (c *recordingExchangeDNSClient) Lookup(context.Context, adapter.DNSTransport, string, adapter.DNSQueryOptions, func(*mDNS.Msg) bool) ([]netip.Addr, error) {
return nil, E.New("unused client lookup")
}
func (c *fakeDNSClient) ClearCache() {}
func (c *recordingExchangeDNSClient) ClearCache() {}
func newTestRouter(t *testing.T, rules []option.DNSRule, transportManager *fakeDNSTransportManager, client *fakeDNSClient) *Router {
router := newTestRouterWithContext(t, context.Background(), rules, transportManager, client)
t.Cleanup(func() {
@@ -289,28 +266,6 @@ func newTestRouter(t *testing.T, rules []option.DNSRule, transportManager *fakeD
return router
}
func newTestRouterWithDNSClient(t *testing.T, rules []option.DNSRule, transportManager *fakeDNSTransportManager, client adapter.DNSClient) *Router {
router := &Router{
ctx: context.Background(),
logger: log.NewNOPFactory().NewLogger("dns"),
transport: transportManager,
client: client,
rawRules: make([]option.DNSRule, 0, len(rules)),
defaultDomainStrategy: C.DomainStrategyAsIS,
}
router.currentRules.Store(newRulesSnapshot(make([]adapter.DNSRule, 0, len(rules)), false))
if rules != nil {
err := router.Initialize(rules)
require.NoError(t, err)
err = router.Start(adapter.StartStateStart)
require.NoError(t, err)
}
t.Cleanup(func() {
router.Close()
})
return router
}
func newTestRouterWithContext(t *testing.T, ctx context.Context, rules []option.DNSRule, transportManager *fakeDNSTransportManager, client *fakeDNSClient) *Router {
return newTestRouterWithContextAndLogger(t, ctx, rules, transportManager, client, log.NewNOPFactory().NewLogger("dns"))
}
@@ -370,131 +325,6 @@ func mustRecord(t *testing.T, record string) option.DNSRecordOptions {
return value
}
func fixedHTTPSHintResponse(question mDNS.Question, addresses ...netip.Addr) *mDNS.Msg {
response := &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{
Response: true,
Rcode: mDNS.RcodeSuccess,
},
Question: []mDNS.Question{question},
Answer: []mDNS.RR{
&mDNS.HTTPS{
SVCB: mDNS.SVCB{
Hdr: mDNS.RR_Header{
Name: question.Name,
Rrtype: mDNS.TypeHTTPS,
Class: mDNS.ClassINET,
Ttl: 60,
},
Priority: 1,
Target: ".",
},
},
},
}
https := response.Answer[0].(*mDNS.HTTPS)
var (
hints4 []net.IP
hints6 []net.IP
)
for _, address := range addresses {
if address.Is4() {
hints4 = append(hints4, net.IP(append([]byte(nil), address.AsSlice()...)))
} else {
hints6 = append(hints6, net.IP(append([]byte(nil), address.AsSlice()...)))
}
}
if len(hints4) > 0 {
https.SVCB.Value = append(https.SVCB.Value, &mDNS.SVCBIPv4Hint{Hint: hints4})
}
if len(hints6) > 0 {
https.SVCB.Value = append(https.SVCB.Value, &mDNS.SVCBIPv6Hint{Hint: hints6})
}
return response
}
func fixedHTTPSHintResponseWithRawHints(question mDNS.Question, ipv4Hints []net.IP, ipv6Hints []net.IP) *mDNS.Msg {
response := fixedHTTPSHintResponse(question)
https := response.Answer[0].(*mDNS.HTTPS)
if len(ipv4Hints) > 0 {
hints := make([]net.IP, 0, len(ipv4Hints))
for _, ip := range ipv4Hints {
hints = append(hints, net.IP(append([]byte(nil), ip...)))
}
https.SVCB.Value = append(https.SVCB.Value, &mDNS.SVCBIPv4Hint{Hint: hints})
}
if len(ipv6Hints) > 0 {
hints := make([]net.IP, 0, len(ipv6Hints))
for _, ip := range ipv6Hints {
hints = append(hints, net.IP(append([]byte(nil), ip...)))
}
https.SVCB.Value = append(https.SVCB.Value, &mDNS.SVCBIPv6Hint{Hint: hints})
}
return response
}
func TestValidateLegacyDNSModeDisabledRules_RequireMatchResponseForDirectIPCIDR(t *testing.T) {
t.Parallel()
err := validateLegacyDNSModeDisabledRules([]option.DNSRule{{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
IPCIDR: badoption.Listable[string]{"1.1.1.0/24"},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{
Server: "default",
},
},
},
}})
require.ErrorContains(t, err, "ip_cidr and ip_is_private require match_response")
}
func TestValidateLegacyDNSModeDisabledRules_AllowMatchResponseWithoutEvaluate(t *testing.T) {
t.Parallel()
err := validateLegacyDNSModeDisabledRules([]option.DNSRule{{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
MatchResponse: true,
IPCIDR: badoption.Listable[string]{"1.1.1.0/24"},
},
},
}})
require.NoError(t, err)
}
func TestInitializeRejectsInvalidDNSRuleParseError(t *testing.T) {
t.Parallel()
router := &Router{
ctx: context.Background(),
logger: log.NewNOPFactory().NewLogger("dns"),
transport: &fakeDNSTransportManager{},
client: &fakeDNSClient{},
rawRules: make([]option.DNSRule, 0, 1),
defaultDomainStrategy: C.DomainStrategyAsIS,
}
router.currentRules.Store(newRulesSnapshot(make([]adapter.DNSRule, 0, 1), false))
err := router.Initialize([]option.DNSRule{{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
DomainRegex: badoption.Listable[string]{"("},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{Server: "default"},
},
},
}})
require.ErrorContains(t, err, "domain_regex")
}
func TestInitializeRejectsDirectLegacyRuleWhenRuleSetForcesNew(t *testing.T) {
t.Parallel()
@@ -1561,17 +1391,6 @@ func TestLookupLegacyDNSModeRuleSetAcceptEmptyDoesNotTreatMismatchAsEmpty(t *tes
require.Equal(t, []string{"private", "default"}, currentLookupTags())
}
func TestDNSResponseAddressesMatchesMessageToAddressesForHTTPSHints(t *testing.T) {
t.Parallel()
response := fixedHTTPSHintResponse(fixedQuestion("example.com", mDNS.TypeHTTPS),
netip.MustParseAddr("1.1.1.1"),
netip.MustParseAddr("2001:db8::1"),
)
require.Equal(t, MessageToAddresses(response), adapter.DNSResponseAddresses(response))
}
func TestExchangeLegacyDNSModeDisabledEvaluateMatchResponseRoute(t *testing.T) {
t.Parallel()
@@ -1831,183 +1650,6 @@ func TestExchangeLegacyDNSModeDisabledEvaluateMatchResponseExtraRoute(t *testing
require.Equal(t, []netip.Addr{netip.MustParseAddr("8.8.8.8")}, MessageToAddresses(response))
}
func TestExchangeLegacyDNSModeDisabledEvaluateMatchResponseRouteIgnoresTTL(t *testing.T) {
t.Parallel()
transportManager := &fakeDNSTransportManager{
defaultTransport: &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP},
transports: map[string]adapter.DNSTransport{
"upstream": &fakeDNSTransport{tag: "upstream", transportType: C.DNSTypeUDP},
"selected": &fakeDNSTransport{tag: "selected", transportType: C.DNSTypeUDP},
"default": &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP},
},
}
client := &fakeDNSClient{
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) {
switch transport.Tag() {
case "upstream":
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("1.1.1.1")}, 30), nil
case "selected":
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("8.8.8.8")}, 60), nil
default:
return nil, E.New("unexpected transport")
}
},
}
rules := []option.DNSRule{
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
Domain: badoption.Listable[string]{"example.com"},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeEvaluate,
RouteOptions: option.DNSRouteActionOptions{Server: "upstream"},
},
},
},
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
MatchResponse: true,
ResponseAnswer: badoption.Listable[option.DNSRecordOptions]{mustRecord(t, "example.com. 60 IN A 1.1.1.1")},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{Server: "selected"},
},
},
},
}
router := newTestRouter(t, rules, transportManager, client)
response, err := router.Exchange(context.Background(), &mDNS.Msg{
Question: []mDNS.Question{fixedQuestion("example.com", mDNS.TypeA)},
}, adapter.DNSQueryOptions{})
require.NoError(t, err)
require.Equal(t, []netip.Addr{netip.MustParseAddr("8.8.8.8")}, MessageToAddresses(response))
}
func TestExchangeLegacyDNSModeDisabledEvaluateMatchResponseRouteWithHTTPSHints(t *testing.T) {
t.Parallel()
transportManager := &fakeDNSTransportManager{
defaultTransport: &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP},
transports: map[string]adapter.DNSTransport{
"upstream": &fakeDNSTransport{tag: "upstream", transportType: C.DNSTypeUDP},
"selected": &fakeDNSTransport{tag: "selected", transportType: C.DNSTypeUDP},
"default": &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP},
},
}
client := &fakeDNSClient{
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) {
switch transport.Tag() {
case "upstream":
return fixedHTTPSHintResponse(message.Question[0], netip.MustParseAddr("1.1.1.1")), nil
case "selected":
return fixedHTTPSHintResponse(message.Question[0], netip.MustParseAddr("8.8.8.8")), nil
default:
return nil, E.New("unexpected transport")
}
},
}
rules := []option.DNSRule{
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
Domain: badoption.Listable[string]{"example.com"},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeEvaluate,
RouteOptions: option.DNSRouteActionOptions{Server: "upstream"},
},
},
},
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
MatchResponse: true,
IPCIDR: badoption.Listable[string]{"1.1.1.0/24"},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{Server: "selected"},
},
},
},
}
router := newTestRouter(t, rules, transportManager, client)
response, err := router.Exchange(context.Background(), &mDNS.Msg{
Question: []mDNS.Question{fixedQuestion("example.com", mDNS.TypeHTTPS)},
}, adapter.DNSQueryOptions{})
require.NoError(t, err)
require.Equal(t, []netip.Addr{netip.MustParseAddr("8.8.8.8")}, MessageToAddresses(response))
}
func TestExchangeLegacyDNSModeDisabledEvaluateMatchResponseRouteWithMappedHTTPSIPv4Hints(t *testing.T) {
t.Parallel()
transportManager := &fakeDNSTransportManager{
defaultTransport: &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP},
transports: map[string]adapter.DNSTransport{
"upstream": &fakeDNSTransport{tag: "upstream", transportType: C.DNSTypeUDP},
"selected": &fakeDNSTransport{tag: "selected", transportType: C.DNSTypeUDP},
"default": &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP},
},
}
client := &fakeDNSClient{
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) {
switch transport.Tag() {
case "upstream":
return fixedHTTPSHintResponseWithRawHints(message.Question[0], []net.IP{net.ParseIP("1.1.1.1")}, nil), nil
case "selected":
return fixedHTTPSHintResponse(message.Question[0], netip.MustParseAddr("8.8.8.8")), nil
default:
return nil, E.New("unexpected transport")
}
},
}
rules := []option.DNSRule{
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
Domain: badoption.Listable[string]{"example.com"},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeEvaluate,
RouteOptions: option.DNSRouteActionOptions{Server: "upstream"},
},
},
},
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
MatchResponse: true,
IPCIDR: badoption.Listable[string]{"1.1.1.0/24"},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{Server: "selected"},
},
},
},
}
router := newTestRouter(t, rules, transportManager, client)
response, err := router.Exchange(context.Background(), &mDNS.Msg{
Question: []mDNS.Question{fixedQuestion("example.com", mDNS.TypeHTTPS)},
}, adapter.DNSQueryOptions{})
require.NoError(t, err)
require.Equal(t, []netip.Addr{netip.MustParseAddr("8.8.8.8")}, MessageToAddresses(response))
}
func TestExchangeLegacyDNSModeDisabledEvaluateDoesNotLeakAddressesToNextQuery(t *testing.T) {
t.Parallel()
@@ -2472,164 +2114,6 @@ func TestLookupLegacyDNSModeDisabledEvaluateSkipFakeIPPreservesResponse(t *testi
require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, addresses)
}
func TestLookupLegacyDNSModeDisabledUsesQueryTypeRule(t *testing.T) {
t.Parallel()
defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP}
router := newTestRouter(t, []option.DNSRule{{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
QueryType: badoption.Listable[option.DNSQueryType]{option.DNSQueryType(mDNS.TypeA)},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{Server: "only-a"},
},
},
}}, &fakeDNSTransportManager{
defaultTransport: defaultTransport,
transports: map[string]adapter.DNSTransport{
"default": defaultTransport,
"only-a": &fakeDNSTransport{tag: "only-a", transportType: C.DNSTypeUDP},
},
}, &fakeDNSClient{
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) {
switch transport.Tag() {
case "default":
if message.Question[0].Qtype == mDNS.TypeA {
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("3.3.3.3")}, 60), nil
}
return FixedResponse(0, message.Question[0], nil, 60), nil
case "only-a":
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("9.9.9.9")}, 60), nil
default:
return nil, E.New("unexpected transport")
}
},
})
require.False(t, router.currentRules.Load().legacyDNSMode)
addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{})
require.NoError(t, err)
require.Equal(t, []netip.Addr{netip.MustParseAddr("9.9.9.9")}, addresses)
}
func TestLookupLegacyDNSModeDisabledUsesRuleSetQueryTypeRule(t *testing.T) {
t.Parallel()
ctx := context.Background()
ruleSet, err := rulepkg.NewRuleSet(ctx, log.NewNOPFactory().NewLogger("router"), option.RuleSet{
Type: C.RuleSetTypeInline,
Tag: "query-set",
InlineOptions: option.PlainRuleSet{
Rules: []option.HeadlessRule{{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultHeadlessRule{
QueryType: badoption.Listable[option.DNSQueryType]{option.DNSQueryType(mDNS.TypeA)},
},
}},
},
})
require.NoError(t, err)
ctx = service.ContextWith[adapter.Router](ctx, &fakeRouter{
ruleSets: map[string]adapter.RuleSet{
"query-set": ruleSet,
},
})
defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP}
router := newTestRouterWithContext(t, ctx, []option.DNSRule{{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
RuleSet: badoption.Listable[string]{"query-set"},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{Server: "only-a"},
},
},
}}, &fakeDNSTransportManager{
defaultTransport: defaultTransport,
transports: map[string]adapter.DNSTransport{
"default": defaultTransport,
"only-a": &fakeDNSTransport{tag: "only-a", transportType: C.DNSTypeUDP},
},
}, &fakeDNSClient{
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) {
switch transport.Tag() {
case "default":
if message.Question[0].Qtype == mDNS.TypeA {
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("3.3.3.3")}, 60), nil
}
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::4")}, 60), nil
case "only-a":
if message.Question[0].Qtype == mDNS.TypeA {
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("9.9.9.9")}, 60), nil
}
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::9")}, 60), nil
default:
return nil, E.New("unexpected transport")
}
},
})
require.False(t, router.currentRules.Load().legacyDNSMode)
addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{})
require.NoError(t, err)
require.Equal(t, []netip.Addr{
netip.MustParseAddr("9.9.9.9"),
netip.MustParseAddr("2001:db8::4"),
}, addresses)
}
func TestLookupLegacyDNSModeDisabledUsesIPVersionRule(t *testing.T) {
t.Parallel()
defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP}
router := newTestRouter(t, []option.DNSRule{{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
IPVersion: 6,
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{Server: "only-v6"},
},
},
}}, &fakeDNSTransportManager{
defaultTransport: defaultTransport,
transports: map[string]adapter.DNSTransport{
"default": defaultTransport,
"only-v6": &fakeDNSTransport{tag: "only-v6", transportType: C.DNSTypeUDP},
},
}, &fakeDNSClient{
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) {
switch transport.Tag() {
case "default":
if message.Question[0].Qtype == mDNS.TypeA {
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("3.3.3.3")}, 60), nil
}
return FixedResponse(0, message.Question[0], nil, 60), nil
case "only-v6":
if message.Question[0].Qtype == mDNS.TypeAAAA {
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::9")}, 60), nil
}
return FixedResponse(0, message.Question[0], nil, 60), nil
default:
return nil, E.New("unexpected transport")
}
},
})
require.False(t, router.currentRules.Load().legacyDNSMode)
addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{})
require.NoError(t, err)
require.Equal(t, []netip.Addr{netip.MustParseAddr("3.3.3.3"), netip.MustParseAddr("2001:db8::9")}, addresses)
}
func TestInitializeRejectsDNSRuleStrategyWhenLegacyDNSModeIsDisabledByEvaluate(t *testing.T) {
t.Parallel()
@@ -2689,108 +2173,6 @@ func TestInitializeRejectsDNSRuleStrategyWhenLegacyDNSModeIsDisabledByMatchRespo
require.ErrorContains(t, err, "legacyDNSMode")
}
func TestExchangeLegacyDNSModeDisabledRouteOptionsApplyQueryOptions(t *testing.T) {
t.Parallel()
defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP}
rewriteTTL := uint32(30)
var capturedOptions adapter.DNSQueryOptions
router := newTestRouterWithDNSClient(t, []option.DNSRule{
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
QueryType: badoption.Listable[option.DNSQueryType]{option.DNSQueryType(mDNS.TypeA)},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRouteOptions,
RouteOptionsOptions: option.DNSRouteOptionsActionOptions{
DisableCache: true,
RewriteTTL: &rewriteTTL,
},
},
},
},
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
Domain: badoption.Listable[string]{"example.com"},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{Server: "selected"},
},
},
},
}, &fakeDNSTransportManager{
defaultTransport: defaultTransport,
transports: map[string]adapter.DNSTransport{
"default": defaultTransport,
"selected": &fakeDNSTransport{tag: "selected", transportType: C.DNSTypeUDP},
},
}, &recordingExchangeDNSClient{
beforeExchange: func(ctx context.Context, transport adapter.DNSTransport, message *mDNS.Msg, options adapter.DNSQueryOptions) {
require.Equal(t, "selected", transport.Tag())
require.Equal(t, []mDNS.Question{fixedQuestion("example.com", mDNS.TypeA)}, message.Question)
capturedOptions = options
},
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg, options adapter.DNSQueryOptions) (*mDNS.Msg, error) {
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2.2.2.2")}, 60), nil
},
})
require.False(t, router.currentRules.Load().legacyDNSMode)
response, err := router.Exchange(context.Background(), &mDNS.Msg{
Question: []mDNS.Question{fixedQuestion("example.com", mDNS.TypeA)},
}, adapter.DNSQueryOptions{})
require.NoError(t, err)
require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, MessageToAddresses(response))
require.True(t, capturedOptions.DisableCache)
require.NotNil(t, capturedOptions.RewriteTTL)
require.Equal(t, rewriteTTL, *capturedOptions.RewriteTTL)
}
func TestLookupLegacyDNSModeUsesRouteStrategy(t *testing.T) {
t.Parallel()
defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP}
selectedTransport := &fakeDNSTransport{tag: "selected", transportType: C.DNSTypeUDP}
router := newTestRouter(t, []option.DNSRule{{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
RawDefaultDNSRule: option.RawDefaultDNSRule{
Domain: badoption.Listable[string]{"example.com"},
},
DNSRuleAction: option.DNSRuleAction{
Action: C.RuleActionTypeRoute,
RouteOptions: option.DNSRouteActionOptions{
Server: "selected",
Strategy: option.DomainStrategy(C.DomainStrategyIPv4Only),
},
},
},
}}, &fakeDNSTransportManager{
defaultTransport: defaultTransport,
transports: map[string]adapter.DNSTransport{
"default": defaultTransport,
"selected": selectedTransport,
},
}, &fakeDNSClient{
lookup: func(transport adapter.DNSTransport, domain string, options adapter.DNSQueryOptions) ([]netip.Addr, *mDNS.Msg, error) {
require.Equal(t, "selected", transport.Tag())
require.Equal(t, C.DomainStrategyIPv4Only, options.Strategy)
return []netip.Addr{netip.MustParseAddr("2.2.2.2")}, nil, nil
},
})
require.True(t, router.currentRules.Load().legacyDNSMode)
addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{})
require.NoError(t, err)
require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, addresses)
}
func TestLookupLegacyDNSModeDisabledReturnsRejectedErrorForRejectAction(t *testing.T) {
t.Parallel()
@@ -2931,85 +2313,6 @@ func TestLookupLegacyDNSModeDisabledFiltersPerQueryTypeAddressesBeforeMerging(t
}, addresses)
}
func TestLookupLegacyDNSModeDisabledUsesInputStrategy(t *testing.T) {
t.Parallel()
defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP}
var queryTypeAccess sync.Mutex
var queryTypes []uint16
recordQueryType := func(queryType uint16) {
queryTypeAccess.Lock()
queryTypes = append(queryTypes, queryType)
queryTypeAccess.Unlock()
}
currentQueryTypes := func() []uint16 {
queryTypeAccess.Lock()
defer queryTypeAccess.Unlock()
return append([]uint16(nil), queryTypes...)
}
router := newTestRouter(t, nil, &fakeDNSTransportManager{
defaultTransport: defaultTransport,
transports: map[string]adapter.DNSTransport{
"default": defaultTransport,
},
}, &fakeDNSClient{
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) {
recordQueryType(message.Question[0].Qtype)
if message.Question[0].Qtype == mDNS.TypeA {
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2.2.2.2")}, 60), nil
}
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::2")}, 60), nil
},
})
router.currentRules.Load().legacyDNSMode = false
addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{
Strategy: C.DomainStrategyIPv4Only,
})
require.NoError(t, err)
require.Equal(t, []uint16{mDNS.TypeA}, currentQueryTypes())
require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, addresses)
}
func TestLookupLegacyDNSModeDisabledUsesDefaultStrategy(t *testing.T) {
t.Parallel()
defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP}
var queryTypeAccess sync.Mutex
var queryTypes []uint16
recordQueryType := func(queryType uint16) {
queryTypeAccess.Lock()
queryTypes = append(queryTypes, queryType)
queryTypeAccess.Unlock()
}
currentQueryTypes := func() []uint16 {
queryTypeAccess.Lock()
defer queryTypeAccess.Unlock()
return append([]uint16(nil), queryTypes...)
}
router := newTestRouter(t, nil, &fakeDNSTransportManager{
defaultTransport: defaultTransport,
transports: map[string]adapter.DNSTransport{
"default": defaultTransport,
},
}, &fakeDNSClient{
exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) {
recordQueryType(message.Question[0].Qtype)
if message.Question[0].Qtype == mDNS.TypeA {
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2.2.2.2")}, 60), nil
}
return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::2")}, 60), nil
},
})
router.defaultDomainStrategy = C.DomainStrategyIPv4Only
router.currentRules.Load().legacyDNSMode = false
addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{})
require.NoError(t, err)
require.Equal(t, []uint16{mDNS.TypeA}, currentQueryTypes())
require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, addresses)
}
func TestExchangeLegacyDNSModeDisabledLogicalMatchResponseIPCIDRFallsThrough(t *testing.T) {
t.Parallel()

View File

@@ -14,18 +14,6 @@ func mustRecordOptions(t *testing.T, record string) DNSRecordOptions {
return value
}
func TestDNSRecordOptionsUnmarshalJSONAcceptsFullyQualifiedNames(t *testing.T) {
t.Parallel()
for _, record := range []string{
"example.com. A 1.1.1.1",
"www.example.com. IN CNAME example.com.",
} {
value := mustRecordOptions(t, record)
require.NotNil(t, value.RR)
}
}
func TestDNSRecordOptionsUnmarshalJSONRejectsRelativeNames(t *testing.T) {
t.Parallel()

View File

@@ -52,40 +52,3 @@ func TestDNSServerOptionsRejectsLegacyFormats(t *testing.T) {
require.EqualError(t, err, legacyDNSServerRemovedMessage)
}
}
func TestDNSOptionsAcceptsTypedServers(t *testing.T) {
t.Parallel()
ctx := service.ContextWith[DNSTransportOptionsRegistry](context.Background(), stubDNSTransportOptionsRegistry{})
var options DNSOptions
err := json.UnmarshalContext(ctx, []byte(`{
"servers": [
{"type": "udp", "tag": "default", "server": "1.1.1.1"},
{"type": "fakeip", "tag": "fake", "inet4_range": "198.18.0.0/15"}
]
}`), &options)
require.NoError(t, err)
require.Len(t, options.Servers, 2)
require.Equal(t, C.DNSTypeUDP, options.Servers[0].Type)
require.Equal(t, "1.1.1.1", options.Servers[0].Options.(*RemoteDNSServerOptions).Server)
require.Equal(t, C.DNSTypeFakeIP, options.Servers[1].Type)
}
func TestDNSRuleActionEvaluateRoundTrip(t *testing.T) {
t.Parallel()
action := DNSRuleAction{
Action: C.RuleActionTypeEvaluate,
RouteOptions: DNSRouteActionOptions{
Server: "default",
},
}
content, err := json.Marshal(action)
require.NoError(t, err)
var decoded DNSRuleAction
err = json.UnmarshalContext(context.Background(), content, &decoded)
require.NoError(t, err)
require.Equal(t, action, decoded)
}

View File

@@ -4,7 +4,6 @@ import (
"context"
"testing"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing/common/json"
"github.com/stretchr/testify/require"
@@ -24,107 +23,6 @@ func TestRuleRejectsNestedDefaultRuleAction(t *testing.T) {
require.ErrorContains(t, err, RouteRuleActionNestedUnsupportedMessage)
}
func TestRuleRejectsNestedLogicalRuleAction(t *testing.T) {
t.Parallel()
var rule Rule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{
"type": "logical",
"mode": "or",
"action": "route",
"outbound": "direct",
"rules": [{"domain": "example.com"}]
}
]
}`), &rule)
require.ErrorContains(t, err, RouteRuleActionNestedUnsupportedMessage)
}
func TestRuleRejectsNestedDefaultRuleZeroValueOutbound(t *testing.T) {
t.Parallel()
var rule Rule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{"domain": "example.com", "outbound": ""}
]
}`), &rule)
require.ErrorContains(t, err, RouteRuleActionNestedUnsupportedMessage)
}
func TestRuleRejectsNestedDefaultRuleZeroValueRouteOption(t *testing.T) {
t.Parallel()
var rule Rule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{"domain": "example.com", "udp_connect": false}
]
}`), &rule)
require.ErrorContains(t, err, RouteRuleActionNestedUnsupportedMessage)
}
func TestRuleRejectsNestedLogicalRuleZeroValueAction(t *testing.T) {
t.Parallel()
var rule Rule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{
"type": "logical",
"mode": "or",
"action": "",
"rules": [{"domain": "example.com"}]
}
]
}`), &rule)
require.ErrorContains(t, err, RouteRuleActionNestedUnsupportedMessage)
}
func TestRuleRejectsNestedLogicalRuleZeroValueRouteOption(t *testing.T) {
t.Parallel()
var rule Rule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{
"type": "logical",
"mode": "or",
"override_port": 0,
"rules": [{"domain": "example.com"}]
}
]
}`), &rule)
require.ErrorContains(t, err, RouteRuleActionNestedUnsupportedMessage)
}
func TestRuleAllowsTopLevelLogicalAction(t *testing.T) {
t.Parallel()
var rule Rule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"outbound": "direct",
"rules": [{"domain": "example.com"}]
}`), &rule)
require.NoError(t, err)
require.Equal(t, C.RuleActionTypeRoute, rule.LogicalOptions.Action)
require.Equal(t, "direct", rule.LogicalOptions.RouteOptions.Outbound)
}
func TestRuleLeavesUnknownNestedKeysToNormalValidation(t *testing.T) {
t.Parallel()
@@ -154,107 +52,6 @@ func TestDNSRuleRejectsNestedDefaultRuleAction(t *testing.T) {
require.ErrorContains(t, err, DNSRuleActionNestedUnsupportedMessage)
}
func TestDNSRuleRejectsNestedLogicalRuleAction(t *testing.T) {
t.Parallel()
var rule DNSRule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{
"type": "logical",
"mode": "or",
"action": "route",
"server": "default",
"rules": [{"domain": "example.com"}]
}
]
}`), &rule)
require.ErrorContains(t, err, DNSRuleActionNestedUnsupportedMessage)
}
func TestDNSRuleRejectsNestedDefaultRuleZeroValueServer(t *testing.T) {
t.Parallel()
var rule DNSRule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{"domain": "example.com", "server": ""}
]
}`), &rule)
require.ErrorContains(t, err, DNSRuleActionNestedUnsupportedMessage)
}
func TestDNSRuleRejectsNestedDefaultRuleZeroValueRouteOption(t *testing.T) {
t.Parallel()
var rule DNSRule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{"domain": "example.com", "disable_cache": false}
]
}`), &rule)
require.ErrorContains(t, err, DNSRuleActionNestedUnsupportedMessage)
}
func TestDNSRuleRejectsNestedLogicalRuleZeroValueAction(t *testing.T) {
t.Parallel()
var rule DNSRule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{
"type": "logical",
"mode": "or",
"action": "",
"rules": [{"domain": "example.com"}]
}
]
}`), &rule)
require.ErrorContains(t, err, DNSRuleActionNestedUnsupportedMessage)
}
func TestDNSRuleRejectsNestedLogicalRuleZeroValueRouteOption(t *testing.T) {
t.Parallel()
var rule DNSRule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{
"type": "logical",
"mode": "or",
"disable_cache": false,
"rules": [{"domain": "example.com"}]
}
]
}`), &rule)
require.ErrorContains(t, err, DNSRuleActionNestedUnsupportedMessage)
}
func TestDNSRuleAllowsTopLevelLogicalAction(t *testing.T) {
t.Parallel()
var rule DNSRule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"server": "default",
"rules": [{"domain": "example.com"}]
}`), &rule)
require.NoError(t, err)
require.Equal(t, C.RuleActionTypeRoute, rule.LogicalOptions.Action)
require.Equal(t, "default", rule.LogicalOptions.RouteOptions.Server)
}
func TestDNSRuleLeavesUnknownNestedKeysToNormalValidation(t *testing.T) {
t.Parallel()

View File

@@ -7,45 +7,10 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/json"
"github.com/stretchr/testify/require"
)
func TestNewRulePreservesImplicitTopLevelDefaultAction(t *testing.T) {
t.Parallel()
var options option.Rule
err := json.UnmarshalContext(context.Background(), []byte(`{
"domain": "example.com"
}`), &options)
require.NoError(t, err)
rule, err := NewRule(context.Background(), log.NewNOPFactory().NewLogger("router"), options, false)
require.NoError(t, err)
require.NotNil(t, rule.Action())
require.Equal(t, C.RuleActionTypeRoute, rule.Action().Type())
}
func TestNewRuleAllowsNestedRuleWithoutAction(t *testing.T) {
t.Parallel()
var options option.Rule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{"domain": "example.com"}
]
}`), &options)
require.NoError(t, err)
rule, err := NewRule(context.Background(), log.NewNOPFactory().NewLogger("router"), options, false)
require.NoError(t, err)
require.NotNil(t, rule.Action())
require.Equal(t, C.RuleActionTypeRoute, rule.Action().Type())
}
func TestNewRuleRejectsNestedRuleAction(t *testing.T) {
t.Parallel()
@@ -71,40 +36,6 @@ func TestNewRuleRejectsNestedRuleAction(t *testing.T) {
require.ErrorContains(t, err, option.RouteRuleActionNestedUnsupportedMessage)
}
func TestNewDNSRulePreservesImplicitTopLevelDefaultAction(t *testing.T) {
t.Parallel()
var options option.DNSRule
err := json.UnmarshalContext(context.Background(), []byte(`{
"domain": "example.com"
}`), &options)
require.NoError(t, err)
rule, err := NewDNSRule(context.Background(), log.NewNOPFactory().NewLogger("dns"), options, false, false)
require.NoError(t, err)
require.NotNil(t, rule.Action())
require.Equal(t, C.RuleActionTypeRoute, rule.Action().Type())
}
func TestNewDNSRuleAllowsNestedRuleWithoutAction(t *testing.T) {
t.Parallel()
var options option.DNSRule
err := json.UnmarshalContext(context.Background(), []byte(`{
"type": "logical",
"mode": "and",
"rules": [
{"domain": "example.com"}
]
}`), &options)
require.NoError(t, err)
rule, err := NewDNSRule(context.Background(), log.NewNOPFactory().NewLogger("dns"), options, false, false)
require.NoError(t, err)
require.NotNil(t, rule.Action())
require.Equal(t, C.RuleActionTypeRoute, rule.Action().Type())
}
func TestNewDNSRuleRejectsNestedRuleAction(t *testing.T) {
t.Parallel()