From 1495f24e60dbcf052854b54c7da954cc4fa100f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 29 Mar 2026 00:27:59 +0800 Subject: [PATCH] dns: make rule strategy legacy-only --- dns/repro_test.go | 53 +-- dns/router.go | 360 ++++++++---------- dns/router_test.go | 448 +++++++++-------------- docs/configuration/dns/rule_action.md | 6 +- docs/configuration/dns/rule_action.zh.md | 6 +- docs/deprecated.md | 7 + experimental/deprecated/constants.go | 9 + route/rule/rule_dns.go | 20 +- 8 files changed, 378 insertions(+), 531 deletions(-) diff --git a/dns/repro_test.go b/dns/repro_test.go index 2569a58d7..8455b088c 100644 --- a/dns/repro_test.go +++ b/dns/repro_test.go @@ -15,62 +15,31 @@ import ( "github.com/stretchr/testify/require" ) -func TestReproLookupWithRulesIgnoresRouteStrategy(t *testing.T) { +func TestReproLookupWithRulesUsesRequestStrategy(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{ - Domain: badoption.Listable[string]{"example.com"}, - }, - DNSRuleAction: option.DNSRuleAction{ - Action: C.RuleActionTypeEvaluate, - RouteOptions: option.DNSRouteActionOptions{Server: "default"}, - }, - }, - }, - { - Type: C.RuleTypeDefault, - DefaultOptions: option.DefaultDNSRule{ - RawDefaultDNSRule: option.RawDefaultDNSRule{ - MatchResponse: true, - }, - DNSRuleAction: option.DNSRuleAction{ - Action: C.RuleActionTypeRoute, - RouteOptions: option.DNSRouteActionOptions{ - Server: "selected", - Strategy: option.DomainStrategy(C.DomainStrategyIPv4Only), - }, - }, - }, - }, - }, &fakeDNSTransportManager{ + var qTypes []uint16 + router := newTestRouter(t, nil, &fakeDNSTransportManager{ defaultTransport: defaultTransport, transports: map[string]adapter.DNSTransport{ - "default": defaultTransport, - "selected": &fakeDNSTransport{tag: "selected", transportType: C.DNSTypeUDP}, + "default": defaultTransport, }, }, &fakeDNSClient{ exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) { - if transport.Tag() == "default" { - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("1.1.1.1")}, 60), nil - } - switch message.Question[0].Qtype { - case mDNS.TypeA: + qTypes = append(qTypes, 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 - case mDNS.TypeAAAA: - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::1")}, 60), nil - default: - return nil, errors.New("unexpected qtype") } + return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::1")}, 60), nil }, }) - addrs, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{}) + addrs, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{ + Strategy: C.DomainStrategyIPv4Only, + }) require.NoError(t, err) + require.Equal(t, []uint16{mDNS.TypeA}, qTypes) require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, addrs) } diff --git a/dns/router.go b/dns/router.go index ba29281f8..13ffd0a0a 100644 --- a/dns/router.go +++ b/dns/router.go @@ -38,22 +38,23 @@ type dnsRuleSetCallback struct { } type Router struct { - ctx context.Context - logger logger.ContextLogger - transport adapter.DNSTransportManager - outbound adapter.OutboundManager - client adapter.DNSClient - rawRules []option.DNSRule - rules []adapter.DNSRule - defaultDomainStrategy C.DomainStrategy - dnsReverseMapping freelru.Cache[netip.Addr, string] - platformInterface adapter.PlatformInterface - legacyAddressFilterMode bool - rulesAccess sync.RWMutex - closing bool - ruleSetCallbacks []dnsRuleSetCallback - runtimeRuleError error - deprecatedReported bool + ctx context.Context + logger logger.ContextLogger + transport adapter.DNSTransportManager + outbound adapter.OutboundManager + client adapter.DNSClient + rawRules []option.DNSRule + rules []adapter.DNSRule + defaultDomainStrategy C.DomainStrategy + dnsReverseMapping freelru.Cache[netip.Addr, string] + platformInterface adapter.PlatformInterface + legacyDNSMode bool + rulesAccess sync.RWMutex + closing bool + ruleSetCallbacks []dnsRuleSetCallback + runtimeRuleError error + addressFilterDeprecatedReported bool + ruleStrategyDeprecatedReported bool } func NewRouter(ctx context.Context, logFactory log.Factory, options option.DNSOptions) *Router { @@ -152,17 +153,21 @@ func (r *Router) rebuildRules(startRules bool) error { if r.isClosing() { return nil } - newRules, legacyAddressFilterMode, err := r.buildRules(startRules) + newRules, legacyDNSMode, err := r.buildRules(startRules) if err != nil { if r.isClosing() { return nil } return err } - shouldReportDeprecated := startRules && - legacyAddressFilterMode && - !r.deprecatedReported && + shouldReportAddressFilterDeprecated := startRules && + legacyDNSMode && + !r.addressFilterDeprecatedReported && common.Any(newRules, func(rule adapter.DNSRule) bool { return rule.WithAddressLimit() }) + shouldReportRuleStrategyDeprecated := startRules && + legacyDNSMode && + !r.ruleStrategyDeprecatedReported && + hasDNSRuleActionStrategy(r.rawRules) r.rulesAccess.Lock() if r.closing { r.rulesAccess.Unlock() @@ -171,16 +176,22 @@ func (r *Router) rebuildRules(startRules bool) error { } oldRules := r.rules r.rules = newRules - r.legacyAddressFilterMode = legacyAddressFilterMode + r.legacyDNSMode = legacyDNSMode r.runtimeRuleError = nil - if shouldReportDeprecated { - r.deprecatedReported = true + if shouldReportAddressFilterDeprecated { + r.addressFilterDeprecatedReported = true + } + if shouldReportRuleStrategyDeprecated { + r.ruleStrategyDeprecatedReported = true } r.rulesAccess.Unlock() closeRules(oldRules) - if shouldReportDeprecated { + if shouldReportAddressFilterDeprecated { deprecated.Report(r.ctx, deprecated.OptionLegacyDNSAddressFilter) } + if shouldReportRuleStrategyDeprecated { + deprecated.Report(r.ctx, deprecated.OptionLegacyDNSRuleStrategy) + } return nil } @@ -192,19 +203,19 @@ func (r *Router) isClosing() bool { func (r *Router) buildRules(startRules bool) ([]adapter.DNSRule, bool, error) { router := service.FromContext[adapter.Router](r.ctx) - legacyAddressFilterMode, err := resolveLegacyAddressFilterMode(router, r.rawRules) + legacyDNSMode, err := resolveLegacyDNSMode(router, r.rawRules) if err != nil { return nil, false, err } - if !legacyAddressFilterMode { - err = validateNonLegacyAddressFilterRules(r.rawRules) + if !legacyDNSMode { + err = validateLegacyDNSModeDisabledRules(r.rawRules) if err != nil { return nil, false, err } } newRules := make([]adapter.DNSRule, 0, len(r.rawRules)) for i, ruleOptions := range r.rawRules { - dnsRule, err := R.NewDNSRule(r.ctx, r.logger, ruleOptions, true, legacyAddressFilterMode) + dnsRule, err := R.NewDNSRule(r.ctx, r.logger, ruleOptions, true, legacyDNSMode) if err != nil { closeRules(newRules) return nil, false, E.Cause(err, "parse dns rule[", i, "]") @@ -220,7 +231,7 @@ func (r *Router) buildRules(startRules bool) ([]adapter.DNSRule, bool, error) { } } } - return newRules, legacyAddressFilterMode, nil + return newRules, legacyDNSMode, nil } func closeRules(rules []adapter.DNSRule) { @@ -349,12 +360,7 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, ruleIndex int, return transport, nil, -1 } -func (r *Router) applyDNSRouteOptions(options *adapter.DNSQueryOptions, routeOptions R.RuleActionDNSRouteOptions) bool { - var strategyOverridden bool - if routeOptions.Strategy != C.DomainStrategyAsIS { - options.Strategy = routeOptions.Strategy - strategyOverridden = true - } +func (r *Router) applyDNSRouteOptions(options *adapter.DNSQueryOptions, routeOptions R.RuleActionDNSRouteOptions) { if routeOptions.DisableCache { options.DisableCache = true } @@ -364,7 +370,6 @@ func (r *Router) applyDNSRouteOptions(options *adapter.DNSQueryOptions, routeOpt if routeOptions.ClientSubnet.IsValid() { options.ClientSubnet = routeOptions.ClientSubnet } - return strategyOverridden } type dnsRouteStatus uint8 @@ -375,20 +380,20 @@ const ( dnsRouteStatusResolved ) -func (r *Router) resolveDNSRoute(action *R.RuleActionDNSRoute, allowFakeIP bool, options *adapter.DNSQueryOptions) (adapter.DNSTransport, dnsRouteStatus, bool) { +func (r *Router) resolveDNSRoute(action *R.RuleActionDNSRoute, allowFakeIP bool, options *adapter.DNSQueryOptions) (adapter.DNSTransport, dnsRouteStatus) { transport, loaded := r.transport.Transport(action.Server) if !loaded { - return nil, dnsRouteStatusMissing, false + return nil, dnsRouteStatusMissing } isFakeIP := transport.Type() == C.DNSTypeFakeIP if isFakeIP && !allowFakeIP { - return transport, dnsRouteStatusSkipped, false + return transport, dnsRouteStatusSkipped } - strategyOverridden := r.applyDNSRouteOptions(options, action.RuleActionDNSRouteOptions) + r.applyDNSRouteOptions(options, action.RuleActionDNSRouteOptions) if isFakeIP { options.DisableCache = true } - return transport, dnsRouteStatusResolved, strategyOverridden + return transport, dnsRouteStatusResolved } func (r *Router) logRuleMatch(ctx context.Context, ruleIndex int, currentRule adapter.DNSRule) { @@ -400,12 +405,10 @@ func (r *Router) logRuleMatch(ctx context.Context, ruleIndex int, currentRule ad } type exchangeWithRulesResult struct { - response *mDNS.Msg - transport adapter.DNSTransport - queryOptions adapter.DNSQueryOptions - strategyOverridden bool - rejectAction *R.RuleActionReject - err error + response *mDNS.Msg + transport adapter.DNSTransport + rejectAction *R.RuleActionReject + err error } func (r *Router) exchangeWithRules(ctx context.Context, message *mDNS.Msg, options adapter.DNSQueryOptions, allowFakeIP bool) exchangeWithRulesResult { @@ -414,7 +417,6 @@ func (r *Router) exchangeWithRules(ctx context.Context, message *mDNS.Msg, optio panic("no context") } effectiveOptions := options - effectiveStrategyOverridden := false var savedResponse *mDNS.Msg for currentRuleIndex, currentRule := range r.rules { metadata.ResetRuleCache() @@ -426,10 +428,10 @@ func (r *Router) exchangeWithRules(ctx context.Context, message *mDNS.Msg, optio r.logRuleMatch(ctx, currentRuleIndex, currentRule) switch action := currentRule.Action().(type) { case *R.RuleActionDNSRouteOptions: - effectiveStrategyOverridden = r.applyDNSRouteOptions(&effectiveOptions, *action) || effectiveStrategyOverridden + r.applyDNSRouteOptions(&effectiveOptions, *action) case *R.RuleActionEvaluate: queryOptions := effectiveOptions - transport, status, _ := r.resolveDNSRoute(&R.RuleActionDNSRoute{ + transport, status := r.resolveDNSRoute(&R.RuleActionDNSRoute{ Server: action.Server, RuleActionDNSRouteOptions: action.RuleActionDNSRouteOptions, }, allowFakeIP, &queryOptions) @@ -454,7 +456,7 @@ func (r *Router) exchangeWithRules(ctx context.Context, message *mDNS.Msg, optio savedResponse = response case *R.RuleActionDNSRoute: queryOptions := effectiveOptions - transport, status, strategyOverridden := r.resolveDNSRoute(action, allowFakeIP, &queryOptions) + transport, status := r.resolveDNSRoute(action, allowFakeIP, &queryOptions) switch status { case dnsRouteStatusMissing: r.logger.ErrorContext(ctx, "transport not found: ", action.Server) @@ -468,11 +470,9 @@ func (r *Router) exchangeWithRules(ctx context.Context, message *mDNS.Msg, optio } response, err := r.client.Exchange(adapter.OverrideContext(ctx), transport, message, exchangeOptions, nil) return exchangeWithRulesResult{ - response: response, - transport: transport, - queryOptions: queryOptions, - strategyOverridden: effectiveStrategyOverridden || strategyOverridden, - err: err, + response: response, + transport: transport, + err: err, } case *R.RuleActionReject: switch action.Method { @@ -486,23 +486,17 @@ func (r *Router) exchangeWithRules(ctx context.Context, message *mDNS.Msg, optio }, Question: []mDNS.Question{message.Question[0]}, }, - queryOptions: effectiveOptions, - strategyOverridden: effectiveStrategyOverridden, - rejectAction: action, + rejectAction: action, } case C.RuleActionRejectMethodDrop: return exchangeWithRulesResult{ - queryOptions: effectiveOptions, - strategyOverridden: effectiveStrategyOverridden, - rejectAction: action, - err: tun.ErrDrop, + rejectAction: action, + err: tun.ErrDrop, } } case *R.RuleActionPredefined: return exchangeWithRulesResult{ - response: action.Response(message), - queryOptions: effectiveOptions, - strategyOverridden: effectiveStrategyOverridden, + response: action.Response(message), } } } @@ -514,36 +508,20 @@ func (r *Router) exchangeWithRules(ctx context.Context, message *mDNS.Msg, optio } response, err := r.client.Exchange(adapter.OverrideContext(ctx), transport, message, exchangeOptions, nil) return exchangeWithRulesResult{ - response: response, - transport: transport, - queryOptions: queryOptions, - strategyOverridden: effectiveStrategyOverridden, - err: err, + response: response, + transport: transport, + err: err, } } type lookupWithRulesResponse struct { - addresses []netip.Addr - strategy C.DomainStrategy - explicitStrategy C.DomainStrategy + addresses []netip.Addr } -func lookupInputStrategy(options adapter.DNSQueryOptions) C.DomainStrategy { +func (r *Router) resolveLookupStrategy(options adapter.DNSQueryOptions) C.DomainStrategy { if options.LookupStrategy != C.DomainStrategyAsIS { return options.LookupStrategy } - return options.Strategy -} - -func (r *Router) resolveLookupStrategy(options adapter.DNSQueryOptions, strategies ...C.DomainStrategy) C.DomainStrategy { - if options.LookupStrategy != C.DomainStrategyAsIS { - return options.LookupStrategy - } - for _, strategy := range strategies { - if strategy != C.DomainStrategyAsIS { - return strategy - } - } if options.Strategy != C.DomainStrategyAsIS { return options.Strategy } @@ -561,60 +539,6 @@ func lookupStrategyAllowsQueryType(strategy C.DomainStrategy, qType uint16) bool } } -func lookupStrategyOverride(queryOptions adapter.DNSQueryOptions, strategyOverridden bool) C.DomainStrategy { - if !strategyOverridden { - return C.DomainStrategyAsIS - } - return queryOptions.Strategy -} - -func isSingleFamilyLookupStrategy(strategy C.DomainStrategy) bool { - return strategy == C.DomainStrategyIPv4Only || strategy == C.DomainStrategyIPv6Only -} - -func resolveExplicitLookupStrategy(strategies ...C.DomainStrategy) (C.DomainStrategy, bool) { - var resolvedStrategy C.DomainStrategy - for _, strategy := range strategies { - if strategy == C.DomainStrategyAsIS { - continue - } - if resolvedStrategy == C.DomainStrategyAsIS { - resolvedStrategy = strategy - continue - } - if resolvedStrategy != strategy { - return C.DomainStrategyAsIS, true - } - } - return resolvedStrategy, false -} - -func (r *Router) resolveLookupOutputStrategies(options adapter.DNSQueryOptions, explicitStrategies ...C.DomainStrategy) (C.DomainStrategy, C.DomainStrategy) { - inputStrategy := lookupInputStrategy(options) - if inputStrategy != C.DomainStrategyAsIS { - return inputStrategy, inputStrategy - } - explicitStrategy, explicitConflict := resolveExplicitLookupStrategy(explicitStrategies...) - sortStrategy := r.defaultDomainStrategy - if !explicitConflict && explicitStrategy != C.DomainStrategyAsIS { - sortStrategy = explicitStrategy - } - filterStrategy := C.DomainStrategyAsIS - if explicitConflict { - return sortStrategy, filterStrategy - } - if explicitStrategy != C.DomainStrategyAsIS { - if isSingleFamilyLookupStrategy(explicitStrategy) { - filterStrategy = explicitStrategy - } - return sortStrategy, filterStrategy - } - if isSingleFamilyLookupStrategy(sortStrategy) { - filterStrategy = sortStrategy - } - return sortStrategy, filterStrategy -} - func withLookupQueryMetadata(ctx context.Context, qType uint16) context.Context { ctx, metadata := adapter.ExtendContext(ctx) metadata.QueryType = qType @@ -644,15 +568,16 @@ func filterAddressesByQueryType(addresses []netip.Addr, qType uint16) []netip.Ad } func (r *Router) lookupWithRules(ctx context.Context, domain string, options adapter.DNSQueryOptions) ([]netip.Addr, error) { + strategy := r.resolveLookupStrategy(options) lookupOptions := options - if options.LookupStrategy != C.DomainStrategyAsIS { - lookupOptions.Strategy = options.LookupStrategy + if strategy != C.DomainStrategyAsIS { + lookupOptions.Strategy = strategy } - if options.LookupStrategy == C.DomainStrategyIPv4Only { + if strategy == C.DomainStrategyIPv4Only { response, err := r.lookupWithRulesType(ctx, domain, mDNS.TypeA, lookupOptions) return response.addresses, err } - if options.LookupStrategy == C.DomainStrategyIPv6Only { + if strategy == C.DomainStrategyIPv6Only { response, err := r.lookupWithRulesType(ctx, domain, mDNS.TypeAAAA, lookupOptions) return response.addresses, err } @@ -672,17 +597,10 @@ func (r *Router) lookupWithRules(ctx context.Context, domain string, options ada return err }) err := group.Run(ctx) - sortStrategy, filterStrategy := r.resolveLookupOutputStrategies(options, response4.explicitStrategy, response6.explicitStrategy) - if !lookupStrategyAllowsQueryType(filterStrategy, mDNS.TypeA) { - response4.addresses = nil - } - if !lookupStrategyAllowsQueryType(filterStrategy, mDNS.TypeAAAA) { - response6.addresses = nil - } if len(response4.addresses) == 0 && len(response6.addresses) == 0 { return nil, err } - return sortAddresses(response4.addresses, response6.addresses, sortStrategy), nil + return sortAddresses(response4.addresses, response6.addresses, strategy), nil } func (r *Router) lookupWithRulesType(ctx context.Context, domain string, qType uint16, options adapter.DNSQueryOptions) (lookupWithRulesResponse, error) { @@ -697,11 +615,7 @@ func (r *Router) lookupWithRulesType(ctx context.Context, domain string, qType u }}, } exchangeResult := r.exchangeWithRules(withLookupQueryMetadata(ctx, qType), request, options, false) - explicitStrategy := lookupStrategyOverride(exchangeResult.queryOptions, exchangeResult.strategyOverridden) - result := lookupWithRulesResponse{ - strategy: r.resolveLookupStrategy(options, explicitStrategy), - explicitStrategy: explicitStrategy, - } + result := lookupWithRulesResponse{} if exchangeResult.rejectAction != nil { return result, exchangeResult.rejectAction.Error(ctx) } @@ -711,7 +625,7 @@ func (r *Router) lookupWithRulesType(ctx context.Context, domain string, qType u if exchangeResult.response.Rcode != mDNS.RcodeSuccess { return result, RcodeError(exchangeResult.response.Rcode) } - if !lookupStrategyAllowsQueryType(result.strategy, qType) { + if !lookupStrategyAllowsQueryType(r.resolveLookupStrategy(options), qType) { return result, nil } result.addresses = filterAddressesByQueryType(MessageToAddresses(exchangeResult.response), qType) @@ -761,7 +675,7 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg, options adapte options.Strategy = r.defaultDomainStrategy } response, err = r.client.Exchange(ctx, transport, message, options, nil) - } else if !r.legacyAddressFilterMode { + } else if !r.legacyDNSMode { exchangeResult := r.exchangeWithRules(ctx, message, options, true) response, transport, err = exchangeResult.response, exchangeResult.transport, exchangeResult.err } else { @@ -878,7 +792,7 @@ func (r *Router) Lookup(ctx context.Context, domain string, options adapter.DNSQ options.Strategy = r.defaultDomainStrategy } responseAddrs, err = r.client.Lookup(ctx, transport, domain, options, nil) - } else if !r.legacyAddressFilterMode { + } else if !r.legacyDNSMode { responseAddrs, err = r.lookupWithRules(ctx, domain, options) } else { var ( @@ -975,7 +889,7 @@ func (r *Router) ResetNetwork() { } } -func hasDirectLegacyAddressFilterItemsInDefaultRule(rule option.DefaultDNSRule) bool { +func defaultRuleNeedsLegacyDNSModeFromAddressFilter(rule option.DefaultDNSRule) bool { if rule.IPAcceptAny || rule.RuleSetIPCIDRAcceptEmpty { return true } @@ -989,7 +903,7 @@ func hasResponseMatchFields(rule option.DefaultDNSRule) bool { len(rule.ResponseExtra) > 0 } -func defaultRuleForcesNewDNSPath(rule option.DefaultDNSRule) bool { +func defaultRuleDisablesLegacyDNSMode(rule option.DefaultDNSRule) bool { return rule.MatchResponse || hasResponseMatchFields(rule) || rule.Action == C.RuleActionTypeEvaluate || @@ -997,76 +911,84 @@ func defaultRuleForcesNewDNSPath(rule option.DefaultDNSRule) bool { len(rule.QueryType) > 0 } -func resolveLegacyAddressFilterMode(router adapter.Router, rules []option.DNSRule) (bool, error) { - forceNew, needsLegacy, err := dnsRuleModeRequirements(router, rules) +func resolveLegacyDNSMode(router adapter.Router, rules []option.DNSRule) (bool, error) { + legacyDNSModeDisabled, needsLegacyDNSMode, needsLegacyDNSModeFromStrategy, err := dnsRuleModeRequirements(router, rules) if err != nil { return false, err } - if forceNew { + if legacyDNSModeDisabled && needsLegacyDNSModeFromStrategy { + return false, E.New("DNS rule action strategy is only supported in legacyDNSMode") + } + if legacyDNSModeDisabled { return false, nil } - return needsLegacy, nil + return needsLegacyDNSMode, nil } -func dnsRuleModeRequirements(router adapter.Router, rules []option.DNSRule) (bool, bool, error) { - var forceNew bool - var needsLegacy bool +func dnsRuleModeRequirements(router adapter.Router, rules []option.DNSRule) (bool, bool, bool, error) { + var legacyDNSModeDisabled bool + var needsLegacyDNSMode bool + var needsLegacyDNSModeFromStrategy bool for i, rule := range rules { - ruleForceNew, ruleNeedsLegacy, err := dnsRuleModeRequirementsInRule(router, rule) + ruleLegacyDNSModeDisabled, ruleNeedsLegacyDNSMode, ruleNeedsLegacyDNSModeFromStrategy, err := dnsRuleModeRequirementsInRule(router, rule) if err != nil { - return false, false, E.Cause(err, "dns rule[", i, "]") + return false, false, false, E.Cause(err, "dns rule[", i, "]") } - forceNew = forceNew || ruleForceNew - needsLegacy = needsLegacy || ruleNeedsLegacy + legacyDNSModeDisabled = legacyDNSModeDisabled || ruleLegacyDNSModeDisabled + needsLegacyDNSMode = needsLegacyDNSMode || ruleNeedsLegacyDNSMode + needsLegacyDNSModeFromStrategy = needsLegacyDNSModeFromStrategy || ruleNeedsLegacyDNSModeFromStrategy } - return forceNew, needsLegacy, nil + return legacyDNSModeDisabled, needsLegacyDNSMode, needsLegacyDNSModeFromStrategy, nil } -func dnsRuleModeRequirementsInRule(router adapter.Router, rule option.DNSRule) (bool, bool, error) { +func dnsRuleModeRequirementsInRule(router adapter.Router, rule option.DNSRule) (bool, bool, bool, error) { switch rule.Type { case "", C.RuleTypeDefault: return dnsRuleModeRequirementsInDefaultRule(router, rule.DefaultOptions) case C.RuleTypeLogical: - forceNew := dnsRuleActionType(rule) == C.RuleActionTypeEvaluate - var needsLegacy bool + legacyDNSModeDisabled := dnsRuleActionType(rule) == C.RuleActionTypeEvaluate + needsLegacyDNSModeFromStrategy := dnsRuleActionHasStrategy(rule.LogicalOptions.DNSRuleAction) + needsLegacyDNSMode := needsLegacyDNSModeFromStrategy for i, subRule := range rule.LogicalOptions.Rules { - subForceNew, subNeedsLegacy, err := dnsRuleModeRequirementsInRule(router, subRule) + subLegacyDNSModeDisabled, subNeedsLegacyDNSMode, subNeedsLegacyDNSModeFromStrategy, err := dnsRuleModeRequirementsInRule(router, subRule) if err != nil { - return false, false, E.Cause(err, "sub rule[", i, "]") + return false, false, false, E.Cause(err, "sub rule[", i, "]") } - forceNew = forceNew || subForceNew - needsLegacy = needsLegacy || subNeedsLegacy + legacyDNSModeDisabled = legacyDNSModeDisabled || subLegacyDNSModeDisabled + needsLegacyDNSMode = needsLegacyDNSMode || subNeedsLegacyDNSMode + needsLegacyDNSModeFromStrategy = needsLegacyDNSModeFromStrategy || subNeedsLegacyDNSModeFromStrategy } - return forceNew, needsLegacy, nil + return legacyDNSModeDisabled, needsLegacyDNSMode, needsLegacyDNSModeFromStrategy, nil default: - return false, false, nil + return false, false, false, nil } } -func dnsRuleModeRequirementsInDefaultRule(router adapter.Router, rule option.DefaultDNSRule) (bool, bool, error) { - forceNew := defaultRuleForcesNewDNSPath(rule) - needsLegacy := hasDirectLegacyAddressFilterItemsInDefaultRule(rule) +func dnsRuleModeRequirementsInDefaultRule(router adapter.Router, rule option.DefaultDNSRule) (bool, bool, bool, error) { + legacyDNSModeDisabled := defaultRuleDisablesLegacyDNSMode(rule) + needsLegacyDNSModeFromStrategy := dnsRuleActionHasStrategy(rule.DNSRuleAction) + needsLegacyDNSMode := defaultRuleNeedsLegacyDNSModeFromAddressFilter(rule) || needsLegacyDNSModeFromStrategy if len(rule.RuleSet) == 0 { - return forceNew, needsLegacy, nil + return legacyDNSModeDisabled, needsLegacyDNSMode, needsLegacyDNSModeFromStrategy, nil } if router == nil { - return false, false, E.New("router service not found") + return false, false, false, E.New("router service not found") } for _, tag := range rule.RuleSet { ruleSet, loaded := router.RuleSet(tag) if !loaded { - return false, false, E.New("rule-set not found: ", tag) + return false, false, false, E.New("rule-set not found: ", tag) } metadata := ruleSet.Metadata() // Rule sets are built from headless rules, so query_type is the only // per-query DNS predicate they can contribute here. ip_version is not a // headless-rule item and is therefore intentionally absent from metadata. - forceNew = forceNew || metadata.ContainsDNSQueryTypeRule + legacyDNSModeDisabled = legacyDNSModeDisabled || metadata.ContainsDNSQueryTypeRule if !rule.RuleSetIPCIDRMatchSource && metadata.ContainsIPCIDRRule { - needsLegacy = true + needsLegacyDNSMode = true } } - return forceNew, needsLegacy, nil + return legacyDNSModeDisabled, needsLegacyDNSMode, needsLegacyDNSModeFromStrategy, nil } func referencedDNSRuleSetTags(rules []option.DNSRule) []string { @@ -1096,9 +1018,9 @@ func referencedDNSRuleSetTags(rules []option.DNSRule) []string { return tags } -func validateNonLegacyAddressFilterRules(rules []option.DNSRule) error { +func validateLegacyDNSModeDisabledRules(rules []option.DNSRule) error { for i, rule := range rules { - consumesResponse, err := validateNonLegacyAddressFilterRuleTree(rule) + consumesResponse, err := validateLegacyDNSModeDisabledRuleTree(rule) if err != nil { return E.Cause(err, "validate dns rule[", i, "]") } @@ -1110,14 +1032,14 @@ func validateNonLegacyAddressFilterRules(rules []option.DNSRule) error { return nil } -func validateNonLegacyAddressFilterRuleTree(rule option.DNSRule) (bool, error) { +func validateLegacyDNSModeDisabledRuleTree(rule option.DNSRule) (bool, error) { switch rule.Type { case "", C.RuleTypeDefault: - return validateNonLegacyAddressFilterDefaultRule(rule.DefaultOptions) + return validateLegacyDNSModeDisabledDefaultRule(rule.DefaultOptions) case C.RuleTypeLogical: var consumesResponse bool for i, subRule := range rule.LogicalOptions.Rules { - subConsumesResponse, err := validateNonLegacyAddressFilterRuleTree(subRule) + subConsumesResponse, err := validateLegacyDNSModeDisabledRuleTree(subRule) if err != nil { return false, E.Cause(err, "sub rule[", i, "]") } @@ -1129,13 +1051,13 @@ func validateNonLegacyAddressFilterRuleTree(rule option.DNSRule) (bool, error) { } } -func validateNonLegacyAddressFilterDefaultRule(rule option.DefaultDNSRule) (bool, error) { +func validateLegacyDNSModeDisabledDefaultRule(rule option.DefaultDNSRule) (bool, error) { hasResponseRecords := hasResponseMatchFields(rule) if hasResponseRecords && !rule.MatchResponse { return false, E.New("response_* items require match_response") } if (len(rule.IPCIDR) > 0 || rule.IPIsPrivate) && !rule.MatchResponse { - return false, E.New("ip_cidr and ip_is_private require match_response in DNS evaluate mode") + return false, E.New("ip_cidr and ip_is_private require match_response when legacyDNSMode is disabled") } // Intentionally do not reject rule_set here. A referenced rule set may mix // destination-IP predicates with pre-response predicates such as domain items. @@ -1143,14 +1065,48 @@ func validateNonLegacyAddressFilterDefaultRule(rule option.DefaultDNSRule) (bool // pre-response evaluation instead of consuming DNS response state, while sibling // non-response branches remain matchable. if rule.IPAcceptAny { - return false, E.New("ip_accept_any is removed in DNS evaluate mode, use ip_cidr with match_response") + return false, E.New("ip_accept_any is removed when legacyDNSMode is disabled, use ip_cidr with match_response") } if rule.RuleSetIPCIDRAcceptEmpty { - return false, E.New("rule_set_ip_cidr_accept_empty is removed in DNS evaluate mode") + return false, E.New("rule_set_ip_cidr_accept_empty is removed when legacyDNSMode is disabled") } return rule.MatchResponse, nil } +func hasDNSRuleActionStrategy(rules []option.DNSRule) bool { + for _, rule := range rules { + if dnsRuleHasActionStrategy(rule) { + return true + } + } + return false +} + +func dnsRuleHasActionStrategy(rule option.DNSRule) bool { + switch rule.Type { + case "", C.RuleTypeDefault: + return dnsRuleActionHasStrategy(rule.DefaultOptions.DNSRuleAction) + case C.RuleTypeLogical: + if dnsRuleActionHasStrategy(rule.LogicalOptions.DNSRuleAction) { + return true + } + return hasDNSRuleActionStrategy(rule.LogicalOptions.Rules) + default: + return false + } +} + +func dnsRuleActionHasStrategy(action option.DNSRuleAction) bool { + switch action.Action { + case "", C.RuleActionTypeRoute, C.RuleActionTypeEvaluate: + return C.DomainStrategy(action.RouteOptions.Strategy) != C.DomainStrategyAsIS + case C.RuleActionTypeRouteOptions: + return C.DomainStrategy(action.RouteOptionsOptions.Strategy) != C.DomainStrategyAsIS + default: + return false + } +} + func dnsRuleActionType(rule option.DNSRule) string { switch rule.Type { case "", C.RuleTypeDefault: diff --git a/dns/router_test.go b/dns/router_test.go index cfd8dea3d..71cbfc960 100644 --- a/dns/router_test.go +++ b/dns/router_test.go @@ -296,10 +296,10 @@ func fixedHTTPSHintResponse(question mDNS.Question, addresses ...netip.Addr) *mD return response } -func TestValidateNewDNSRules_RequireMatchResponseForDirectIPCIDR(t *testing.T) { +func TestValidateLegacyDNSModeDisabledRules_RequireMatchResponseForDirectIPCIDR(t *testing.T) { t.Parallel() - err := validateNonLegacyAddressFilterRules([]option.DNSRule{{ + err := validateLegacyDNSModeDisabledRules([]option.DNSRule{{ Type: C.RuleTypeDefault, DefaultOptions: option.DefaultDNSRule{ RawDefaultDNSRule: option.RawDefaultDNSRule{ @@ -316,10 +316,10 @@ func TestValidateNewDNSRules_RequireMatchResponseForDirectIPCIDR(t *testing.T) { require.ErrorContains(t, err, "ip_cidr and ip_is_private require match_response") } -func TestValidateNewDNSRules_AllowMatchResponseWithoutEvaluate(t *testing.T) { +func TestValidateLegacyDNSModeDisabledRules_AllowMatchResponseWithoutEvaluate(t *testing.T) { t.Parallel() - err := validateNonLegacyAddressFilterRules([]option.DNSRule{{ + err := validateLegacyDNSModeDisabledRules([]option.DNSRule{{ Type: C.RuleTypeDefault, DefaultOptions: option.DefaultDNSRule{ RawDefaultDNSRule: option.RawDefaultDNSRule{ @@ -419,7 +419,7 @@ func TestInitializeRejectsDirectLegacyRuleWhenRuleSetForcesNew(t *testing.T) { require.ErrorContains(t, err, "ip_cidr and ip_is_private require match_response") } -func TestLookupLegacyModeDefersRuleSetDestinationIPMatch(t *testing.T) { +func TestLookupLegacyDNSModeDefersRuleSetDestinationIPMatch(t *testing.T) { t.Parallel() ctx := context.Background() @@ -470,7 +470,7 @@ func TestLookupLegacyModeDefersRuleSetDestinationIPMatch(t *testing.T) { }, }) - require.True(t, router.legacyAddressFilterMode) + require.True(t, router.legacyDNSMode) addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{ LookupStrategy: C.DomainStrategyIPv4Only, @@ -566,7 +566,7 @@ func TestRuleSetUpdateSetsRuntimeErrorWhenRebuildFails(t *testing.T) { }, }) - require.True(t, router.legacyAddressFilterMode) + require.True(t, router.legacyDNSMode) fakeSet.updateMetadata(adapter.RuleSetMetadata{ ContainsDNSQueryTypeRule: true, @@ -642,7 +642,7 @@ func TestCloseIgnoresSnapshottedRuleSetCallback(t *testing.T) { require.NoError(t, router.runtimeRuleError) } -func TestLookupLegacyModeDefersDirectDestinationIPMatch(t *testing.T) { +func TestLookupLegacyDNSModeDefersDirectDestinationIPMatch(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -680,7 +680,7 @@ func TestLookupLegacyModeDefersDirectDestinationIPMatch(t *testing.T) { }, }, client) - require.True(t, router.legacyAddressFilterMode) + require.True(t, router.legacyDNSMode) addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{ LookupStrategy: C.DomainStrategyIPv4Only, @@ -689,7 +689,7 @@ func TestLookupLegacyModeDefersDirectDestinationIPMatch(t *testing.T) { require.Equal(t, []netip.Addr{netip.MustParseAddr("10.0.0.1")}, addresses) } -func TestLookupLegacyModeFallsBackAfterRejectedAddressLimitResponse(t *testing.T) { +func TestLookupLegacyDNSModeFallsBackAfterRejectedAddressLimitResponse(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -738,7 +738,7 @@ func TestLookupLegacyModeFallsBackAfterRejectedAddressLimitResponse(t *testing.T require.Equal(t, []string{"private", "default"}, lookups) } -func TestLookupLegacyModeRuleSetAcceptEmptyDoesNotTreatMismatchAsEmpty(t *testing.T) { +func TestLookupLegacyDNSModeRuleSetAcceptEmptyDoesNotTreatMismatchAsEmpty(t *testing.T) { t.Parallel() ctx := context.Background() @@ -810,7 +810,7 @@ func TestLookupLegacyModeRuleSetAcceptEmptyDoesNotTreatMismatchAsEmpty(t *testin }, }) - require.True(t, router.legacyAddressFilterMode) + require.True(t, router.legacyDNSMode) addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{ LookupStrategy: C.DomainStrategyIPv4Only, @@ -831,7 +831,7 @@ func TestDNSResponseAddressesMatchesMessageToAddressesForHTTPSHints(t *testing.T require.Equal(t, MessageToAddresses(response), adapter.DNSResponseAddresses(response)) } -func TestExchangeNewModeEvaluateMatchResponseRoute(t *testing.T) { +func TestExchangeLegacyDNSModeDisabledEvaluateMatchResponseRoute(t *testing.T) { t.Parallel() transportManager := &fakeDNSTransportManager{ @@ -890,7 +890,7 @@ func TestExchangeNewModeEvaluateMatchResponseRoute(t *testing.T) { require.Equal(t, []netip.Addr{netip.MustParseAddr("8.8.8.8")}, MessageToAddresses(response)) } -func TestExchangeNewModeEvaluateMatchResponseRouteIgnoresTTL(t *testing.T) { +func TestExchangeLegacyDNSModeDisabledEvaluateMatchResponseRouteIgnoresTTL(t *testing.T) { t.Parallel() transportManager := &fakeDNSTransportManager{ @@ -949,7 +949,7 @@ func TestExchangeNewModeEvaluateMatchResponseRouteIgnoresTTL(t *testing.T) { require.Equal(t, []netip.Addr{netip.MustParseAddr("8.8.8.8")}, MessageToAddresses(response)) } -func TestExchangeNewModeEvaluateMatchResponseRouteWithHTTPSHints(t *testing.T) { +func TestExchangeLegacyDNSModeDisabledEvaluateMatchResponseRouteWithHTTPSHints(t *testing.T) { t.Parallel() transportManager := &fakeDNSTransportManager{ @@ -1008,7 +1008,7 @@ func TestExchangeNewModeEvaluateMatchResponseRouteWithHTTPSHints(t *testing.T) { require.Equal(t, []netip.Addr{netip.MustParseAddr("8.8.8.8")}, MessageToAddresses(response)) } -func TestExchangeNewModeEvaluateDoesNotLeakAddressesToNextQuery(t *testing.T) { +func TestExchangeLegacyDNSModeDisabledEvaluateDoesNotLeakAddressesToNextQuery(t *testing.T) { t.Parallel() transportManager := &fakeDNSTransportManager{ @@ -1079,7 +1079,7 @@ func TestExchangeNewModeEvaluateDoesNotLeakAddressesToNextQuery(t *testing.T) { require.Equal(t, []netip.Addr{netip.MustParseAddr("8.8.8.8")}, MessageToAddresses(response)) } -func TestExchangeNewModeEvaluateRouteResolutionFailureClearsResponse(t *testing.T) { +func TestExchangeLegacyDNSModeDisabledEvaluateRouteResolutionFailureClearsResponse(t *testing.T) { t.Parallel() transportManager := &fakeDNSTransportManager{ @@ -1152,7 +1152,7 @@ func TestExchangeNewModeEvaluateRouteResolutionFailureClearsResponse(t *testing. require.Equal(t, []netip.Addr{netip.MustParseAddr("4.4.4.4")}, MessageToAddresses(response)) } -func TestExchangeNewModeEvaluateExchangeFailureUsesMatchResponseBooleanSemantics(t *testing.T) { +func TestExchangeLegacyDNSModeDisabledEvaluateExchangeFailureUsesMatchResponseBooleanSemantics(t *testing.T) { t.Parallel() testCases := []struct { @@ -1235,7 +1235,7 @@ func TestExchangeNewModeEvaluateExchangeFailureUsesMatchResponseBooleanSemantics } } -func TestLookupNewModeAllowsPartialSuccess(t *testing.T) { +func TestLookupLegacyDNSModeDisabledAllowsPartialSuccess(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -1257,14 +1257,14 @@ func TestLookupNewModeAllowsPartialSuccess(t *testing.T) { } }, }) - router.legacyAddressFilterMode = false + router.legacyDNSMode = false addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{}) require.NoError(t, err) require.Equal(t, []netip.Addr{netip.MustParseAddr("1.1.1.1")}, addresses) } -func TestLookupNewModeSkipsFakeIPRule(t *testing.T) { +func TestLookupLegacyDNSModeDisabledSkipsFakeIPRule(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -1294,14 +1294,14 @@ func TestLookupNewModeSkipsFakeIPRule(t *testing.T) { return FixedResponse(0, message.Question[0], nil, 60), nil }, }) - router.legacyAddressFilterMode = false + router.legacyDNSMode = false 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 TestLookupNewModeEvaluateSkipFakeIPPreservesResponse(t *testing.T) { +func TestLookupLegacyDNSModeDisabledEvaluateSkipFakeIPPreservesResponse(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -1374,14 +1374,14 @@ func TestLookupNewModeEvaluateSkipFakeIPPreservesResponse(t *testing.T) { } }, }) - router.legacyAddressFilterMode = false + router.legacyDNSMode = false 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 TestLookupNewModeUsesQueryTypeRule(t *testing.T) { +func TestLookupLegacyDNSModeDisabledUsesQueryTypeRule(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -1417,14 +1417,14 @@ func TestLookupNewModeUsesQueryTypeRule(t *testing.T) { } }, }) - require.False(t, router.legacyAddressFilterMode) + require.False(t, router.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 TestLookupNewModeUsesRuleSetQueryTypeRule(t *testing.T) { +func TestLookupLegacyDNSModeDisabledUsesRuleSetQueryTypeRule(t *testing.T) { t.Parallel() ctx := context.Background() @@ -1483,7 +1483,7 @@ func TestLookupNewModeUsesRuleSetQueryTypeRule(t *testing.T) { } }, }) - require.False(t, router.legacyAddressFilterMode) + require.False(t, router.legacyDNSMode) addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{}) require.NoError(t, err) @@ -1493,7 +1493,7 @@ func TestLookupNewModeUsesRuleSetQueryTypeRule(t *testing.T) { }, addresses) } -func TestLookupNewModeUsesIPVersionRule(t *testing.T) { +func TestLookupLegacyDNSModeDisabledUsesIPVersionRule(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -1532,73 +1532,113 @@ func TestLookupNewModeUsesIPVersionRule(t *testing.T) { } }, }) - require.False(t, router.legacyAddressFilterMode) + require.False(t, router.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 TestLookupNewModeAppliesRouteStrategyAfterEvaluate(t *testing.T) { +func TestInitializeRejectsDNSRuleStrategyWhenLegacyDNSModeIsDisabledByEvaluate(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), + rules: make([]adapter.DNSRule, 0, 1), + defaultDomainStrategy: C.DomainStrategyAsIS, + } + err := router.Initialize([]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: "default", + Strategy: option.DomainStrategy(C.DomainStrategyIPv4Only), + }, + }, + }, + }}) + require.ErrorContains(t, err, "legacyDNSMode") +} + +func TestInitializeRejectsDNSRuleStrategyWhenLegacyDNSModeIsDisabledByMatchResponse(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), + rules: make([]adapter.DNSRule, 0, 1), + defaultDomainStrategy: C.DomainStrategyAsIS, + } + err := router.Initialize([]option.DNSRule{{ + Type: C.RuleTypeDefault, + DefaultOptions: option.DefaultDNSRule{ + RawDefaultDNSRule: option.RawDefaultDNSRule{ + MatchResponse: true, + }, + DNSRuleAction: option.DNSRuleAction{ + Action: C.RuleActionTypeRouteOptions, + RouteOptionsOptions: option.DNSRouteOptionsActionOptions{ + Strategy: option.DomainStrategy(C.DomainStrategyIPv4Only), + }, + }, + }, + }}) + require.ErrorContains(t, err, "legacyDNSMode") +} + +func TestLookupLegacyDNSModeUsesRouteStrategy(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{ - Domain: badoption.Listable[string]{"example.com"}, - }, - DNSRuleAction: option.DNSRuleAction{ - Action: C.RuleActionTypeEvaluate, - RouteOptions: option.DNSRouteActionOptions{Server: "default"}, + 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), }, }, }, - { - Type: C.RuleTypeDefault, - DefaultOptions: option.DefaultDNSRule{ - RawDefaultDNSRule: option.RawDefaultDNSRule{ - MatchResponse: true, - }, - DNSRuleAction: option.DNSRuleAction{ - Action: C.RuleActionTypeRoute, - RouteOptions: option.DNSRouteActionOptions{ - Server: "selected", - Strategy: option.DomainStrategy(C.DomainStrategyIPv4Only), - }, - }, - }, - }, - }, &fakeDNSTransportManager{ + }}, &fakeDNSTransportManager{ defaultTransport: defaultTransport, transports: map[string]adapter.DNSTransport{ "default": defaultTransport, - "selected": &fakeDNSTransport{tag: "selected", transportType: C.DNSTypeUDP}, + "selected": selectedTransport, }, }, &fakeDNSClient{ - exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) { - if transport.Tag() == "default" { - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("1.1.1.1")}, 60), nil - } - switch message.Question[0].Qtype { - case mDNS.TypeA: - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2.2.2.2")}, 60), nil - case mDNS.TypeAAAA: - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::1")}, 60), nil - default: - return nil, errors.New("unexpected qtype") - } + 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.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 TestLookupNewModeReturnsRejectedErrorForRejectAction(t *testing.T) { +func TestLookupLegacyDNSModeDisabledReturnsRejectedErrorForRejectAction(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -1623,7 +1663,7 @@ func TestLookupNewModeReturnsRejectedErrorForRejectAction(t *testing.T) { "default": defaultTransport, }, }, &fakeDNSClient{}) - require.False(t, router.legacyAddressFilterMode) + require.False(t, router.legacyDNSMode) addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{}) require.Nil(t, addresses) @@ -1631,7 +1671,7 @@ func TestLookupNewModeReturnsRejectedErrorForRejectAction(t *testing.T) { require.True(t, rulepkg.IsRejected(err)) } -func TestExchangeNewModeReturnsRefusedResponseForRejectAction(t *testing.T) { +func TestExchangeLegacyDNSModeDisabledReturnsRefusedResponseForRejectAction(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -1656,7 +1696,7 @@ func TestExchangeNewModeReturnsRefusedResponseForRejectAction(t *testing.T) { "default": defaultTransport, }, }, &fakeDNSClient{}) - require.False(t, router.legacyAddressFilterMode) + require.False(t, router.legacyDNSMode) response, err := router.Exchange(context.Background(), &mDNS.Msg{ Question: []mDNS.Question{fixedQuestion("example.com", mDNS.TypeA)}, @@ -1666,7 +1706,7 @@ func TestExchangeNewModeReturnsRefusedResponseForRejectAction(t *testing.T) { require.Equal(t, []mDNS.Question{fixedQuestion("example.com", mDNS.TypeA)}, response.Question) } -func TestLookupNewModeFiltersPerQueryTypeAddressesBeforeMerging(t *testing.T) { +func TestLookupLegacyDNSModeDisabledFiltersPerQueryTypeAddressesBeforeMerging(t *testing.T) { t.Parallel() defaultTransport := &fakeDNSTransport{tag: "default", transportType: C.DNSTypeUDP} @@ -1694,7 +1734,7 @@ func TestLookupNewModeFiltersPerQueryTypeAddressesBeforeMerging(t *testing.T) { "default": defaultTransport, }, }, &fakeDNSClient{}) - require.False(t, router.legacyAddressFilterMode) + require.False(t, router.legacyDNSMode) addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{}) require.NoError(t, err) @@ -1704,241 +1744,64 @@ func TestLookupNewModeFiltersPerQueryTypeAddressesBeforeMerging(t *testing.T) { }, addresses) } -func TestLookupNewModePrefersExplicitBranchStrategyOverDefault(t *testing.T) { +func TestLookupLegacyDNSModeDisabledUsesInputStrategy(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{ - 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. IN AAAA 2001:db8::1")}, - }, - DNSRuleAction: option.DNSRuleAction{ - Action: C.RuleActionTypeRoute, - RouteOptions: option.DNSRouteActionOptions{ - Server: "selected", - Strategy: option.DomainStrategy(C.DomainStrategyIPv6Only), - }, - }, - }, - }, - }, &fakeDNSTransportManager{ + var qTypes []uint16 + router := newTestRouter(t, nil, &fakeDNSTransportManager{ defaultTransport: defaultTransport, transports: map[string]adapter.DNSTransport{ - "default": defaultTransport, - "upstream": &fakeDNSTransport{tag: "upstream", transportType: C.DNSTypeUDP}, - "selected": &fakeDNSTransport{tag: "selected", transportType: C.DNSTypeUDP}, + "default": defaultTransport, }, }, &fakeDNSClient{ exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) { - switch transport.Tag() { - case "upstream": - if message.Question[0].Qtype == mDNS.TypeA { - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("1.1.1.1")}, 60), nil - } - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::1")}, 60), nil - case "selected": - 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 - case "default": - if message.Question[0].Qtype == mDNS.TypeA { - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("4.4.4.4")}, 60), nil - } - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::4")}, 60), nil - default: - return nil, errors.New("unexpected transport") - } - }, - }) - router.defaultDomainStrategy = C.DomainStrategyIPv4Only - - addresses, err := router.Lookup(context.Background(), "example.com", adapter.DNSQueryOptions{}) - require.NoError(t, err) - require.Equal(t, []netip.Addr{netip.MustParseAddr("2001:db8::2")}, addresses) -} - -func TestLookupNewModeKeepsExplicitBranchStrategyMatchingInput(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{ - 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. IN A 1.1.1.1")}, - }, - DNSRuleAction: option.DNSRuleAction{ - Action: C.RuleActionTypeRoute, - RouteOptions: option.DNSRouteActionOptions{ - Server: "selected4", - Strategy: option.DomainStrategy(C.DomainStrategyIPv4Only), - }, - }, - }, - }, - { - Type: C.RuleTypeDefault, - DefaultOptions: option.DefaultDNSRule{ - RawDefaultDNSRule: option.RawDefaultDNSRule{ - MatchResponse: true, - ResponseAnswer: badoption.Listable[option.DNSRecordOptions]{mustRecord(t, "example.com. IN AAAA 2001:db8::1")}, - }, - DNSRuleAction: option.DNSRuleAction{ - Action: C.RuleActionTypeRoute, - RouteOptions: option.DNSRouteActionOptions{ - Server: "selected6", - Strategy: option.DomainStrategy(C.DomainStrategyIPv6Only), - }, - }, - }, - }, - }, &fakeDNSTransportManager{ - defaultTransport: defaultTransport, - transports: map[string]adapter.DNSTransport{ - "default": defaultTransport, - "upstream": &fakeDNSTransport{tag: "upstream", transportType: C.DNSTypeUDP}, - "selected4": &fakeDNSTransport{tag: "selected4", transportType: C.DNSTypeUDP}, - "selected6": &fakeDNSTransport{tag: "selected6", transportType: C.DNSTypeUDP}, - }, - }, &fakeDNSClient{ - exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) { - switch transport.Tag() { - case "upstream": - if message.Question[0].Qtype == mDNS.TypeA { - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("1.1.1.1")}, 60), nil - } - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::1")}, 60), nil - case "selected4": + qTypes = append(qTypes, 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 - case "selected6": - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::2")}, 60), nil - default: - return nil, errors.New("unexpected transport") } + return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::2")}, 60), nil }, }) - router.legacyAddressFilterMode = false + router.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}, qTypes) require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, addresses) } -func TestLookupNewModeKeepsConflictingExplicitBranchStrategies(t *testing.T) { +func TestLookupLegacyDNSModeDisabledUsesDefaultStrategy(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{ - 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. IN A 1.1.1.1")}, - }, - DNSRuleAction: option.DNSRuleAction{ - Action: C.RuleActionTypeRoute, - RouteOptions: option.DNSRouteActionOptions{ - Server: "selected4", - Strategy: option.DomainStrategy(C.DomainStrategyIPv4Only), - }, - }, - }, - }, - { - Type: C.RuleTypeDefault, - DefaultOptions: option.DefaultDNSRule{ - RawDefaultDNSRule: option.RawDefaultDNSRule{ - MatchResponse: true, - ResponseAnswer: badoption.Listable[option.DNSRecordOptions]{mustRecord(t, "example.com. IN AAAA 2001:db8::1")}, - }, - DNSRuleAction: option.DNSRuleAction{ - Action: C.RuleActionTypeRoute, - RouteOptions: option.DNSRouteActionOptions{ - Server: "selected6", - Strategy: option.DomainStrategy(C.DomainStrategyIPv6Only), - }, - }, - }, - }, - }, &fakeDNSTransportManager{ + var qTypes []uint16 + router := newTestRouter(t, nil, &fakeDNSTransportManager{ defaultTransport: defaultTransport, transports: map[string]adapter.DNSTransport{ - "default": defaultTransport, - "upstream": &fakeDNSTransport{tag: "upstream", transportType: C.DNSTypeUDP}, - "selected4": &fakeDNSTransport{tag: "selected4", transportType: C.DNSTypeUDP}, - "selected6": &fakeDNSTransport{tag: "selected6", transportType: C.DNSTypeUDP}, + "default": defaultTransport, }, }, &fakeDNSClient{ exchange: func(transport adapter.DNSTransport, message *mDNS.Msg) (*mDNS.Msg, error) { - switch transport.Tag() { - case "upstream": - if message.Question[0].Qtype == mDNS.TypeA { - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("1.1.1.1")}, 60), nil - } - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::1")}, 60), nil - case "selected4": + qTypes = append(qTypes, 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 - case "selected6": - return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::2")}, 60), nil - default: - return nil, errors.New("unexpected transport") } + return FixedResponse(0, message.Question[0], []netip.Addr{netip.MustParseAddr("2001:db8::2")}, 60), nil }, }) - router.legacyAddressFilterMode = false + router.defaultDomainStrategy = C.DomainStrategyIPv4Only + router.legacyDNSMode = false 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"), netip.MustParseAddr("2001:db8::2")}, addresses) + require.Equal(t, []uint16{mDNS.TypeA}, qTypes) + require.Equal(t, []netip.Addr{netip.MustParseAddr("2.2.2.2")}, addresses) } -func TestExchangeNewModeLogicalMatchResponseIPCIDRFallsThrough(t *testing.T) { +func TestExchangeLegacyDNSModeDisabledLogicalMatchResponseIPCIDRFallsThrough(t *testing.T) { t.Parallel() transportManager := &fakeDNSTransportManager{ @@ -2007,7 +1870,7 @@ func TestExchangeNewModeLogicalMatchResponseIPCIDRFallsThrough(t *testing.T) { require.Equal(t, []netip.Addr{netip.MustParseAddr("4.4.4.4")}, MessageToAddresses(response)) } -func TestOldModeReportsLegacyAddressFilterDeprecation(t *testing.T) { +func TestLegacyDNSModeReportsLegacyAddressFilterDeprecation(t *testing.T) { t.Parallel() manager := &fakeDeprecatedManager{} @@ -2038,3 +1901,38 @@ func TestOldModeReportsLegacyAddressFilterDeprecation(t *testing.T) { require.Len(t, manager.features, 1) require.Equal(t, deprecated.OptionLegacyDNSAddressFilter.Name, manager.features[0].Name) } + +func TestLegacyDNSModeReportsDNSRuleStrategyDeprecation(t *testing.T) { + t.Parallel() + + manager := &fakeDeprecatedManager{} + ctx := service.ContextWith[deprecated.Manager](context.Background(), manager) + router := &Router{ + ctx: ctx, + logger: log.NewNOPFactory().NewLogger("dns"), + client: &fakeDNSClient{}, + rules: make([]adapter.DNSRule, 0, 1), + defaultDomainStrategy: C.DomainStrategyAsIS, + } + err := router.Initialize([]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: "default", + Strategy: option.DomainStrategy(C.DomainStrategyIPv4Only), + }, + }, + }, + }}) + require.NoError(t, err) + + err = router.Start(adapter.StartStateStart) + require.NoError(t, err) + require.Len(t, manager.features, 1) + require.Equal(t, deprecated.OptionLegacyDNSRuleStrategy.Name, manager.features[0].Name) +} diff --git a/docs/configuration/dns/rule_action.md b/docs/configuration/dns/rule_action.md index db9033f8b..1c291445a 100644 --- a/docs/configuration/dns/rule_action.md +++ b/docs/configuration/dns/rule_action.md @@ -34,7 +34,11 @@ Tag of target server. !!! question "Since sing-box 1.12.0" -Set domain strategy for this query. +!!! warning + + `strategy` is deprecated and only supported in `legacyDNSMode`. + +Set domain strategy for this query in `legacyDNSMode`. One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`. diff --git a/docs/configuration/dns/rule_action.zh.md b/docs/configuration/dns/rule_action.zh.md index 9e59c6bd2..f26691b14 100644 --- a/docs/configuration/dns/rule_action.zh.md +++ b/docs/configuration/dns/rule_action.zh.md @@ -34,7 +34,11 @@ icon: material/new-box !!! question "自 sing-box 1.12.0 起" -为此查询设置域名策略。 +!!! warning + + `strategy` 已废弃,且仅在 `legacyDNSMode` 中可用。 + +在 `legacyDNSMode` 中为此查询设置域名策略。 可选项:`prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`。 diff --git a/docs/deprecated.md b/docs/deprecated.md index 1e6d54eec..0d2e78d0e 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -14,6 +14,13 @@ check [Migration](../migration/#migrate-inline-acme-to-certificate-provider). Old fields will be removed in sing-box 1.16.0. +#### `strategy` in DNS rule actions + +`strategy` in DNS rule actions is deprecated +and only supported in `legacyDNSMode`. + +Old fields will be removed in sing-box 1.16.0. + ## 1.12.0 #### Legacy DNS server formats diff --git a/experimental/deprecated/constants.go b/experimental/deprecated/constants.go index f08491c46..81ed14e04 100644 --- a/experimental/deprecated/constants.go +++ b/experimental/deprecated/constants.go @@ -117,6 +117,14 @@ var OptionLegacyDNSAddressFilter = Note{ MigrationLink: "https://sing-box.sagernet.org/configuration/dns/rule/", } +var OptionLegacyDNSRuleStrategy = Note{ + Name: "legacy-dns-rule-strategy", + Description: "`strategy` in DNS rule actions", + DeprecatedVersion: "1.14.0", + ScheduledVersion: "1.16.0", + MigrationLink: "https://sing-box.sagernet.org/configuration/dns/rule_action/", +} + var Options = []Note{ OptionOutboundDNSRuleItem, OptionMissingDomainResolver, @@ -125,4 +133,5 @@ var Options = []Note{ OptionIPAcceptAny, OptionRuleSetIPCIDRAcceptEmpty, OptionLegacyDNSAddressFilter, + OptionLegacyDNSRuleStrategy, } diff --git a/route/rule/rule_dns.go b/route/rule/rule_dns.go index 6a84a4e77..0cff49221 100644 --- a/route/rule/rule_dns.go +++ b/route/rule/rule_dns.go @@ -15,7 +15,7 @@ import ( "github.com/miekg/dns" ) -func NewDNSRule(ctx context.Context, logger log.ContextLogger, options option.DNSRule, checkServer bool, legacyAddressFilter bool) (adapter.DNSRule, error) { +func NewDNSRule(ctx context.Context, logger log.ContextLogger, options option.DNSRule, checkServer bool, legacyDNSMode bool) (adapter.DNSRule, error) { switch options.Type { case "", C.RuleTypeDefault: if !options.DefaultOptions.IsValid() { @@ -30,7 +30,7 @@ func NewDNSRule(ctx context.Context, logger log.ContextLogger, options option.DN return nil, E.New("missing server field") } } - return NewDefaultDNSRule(ctx, logger, options.DefaultOptions, legacyAddressFilter) + return NewDefaultDNSRule(ctx, logger, options.DefaultOptions, legacyDNSMode) case C.RuleTypeLogical: if !options.LogicalOptions.IsValid() { return nil, E.New("missing conditions") @@ -44,7 +44,7 @@ func NewDNSRule(ctx context.Context, logger log.ContextLogger, options option.DN return nil, E.New("missing server field") } } - return NewLogicalDNSRule(ctx, logger, options.LogicalOptions, legacyAddressFilter) + return NewLogicalDNSRule(ctx, logger, options.LogicalOptions, legacyDNSMode) default: return nil, E.New("unknown rule type: ", options.Type) } @@ -61,7 +61,7 @@ func (r *DefaultDNSRule) matchStates(metadata *adapter.InboundContext) ruleMatch return r.abstractDefaultRule.matchStates(metadata) } -func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options option.DefaultDNSRule, legacyAddressFilter bool) (*DefaultDNSRule, error) { +func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options option.DefaultDNSRule, legacyDNSMode bool) (*DefaultDNSRule, error) { rule := &DefaultDNSRule{ abstractDefaultRule: abstractDefaultRule{ invert: options.Invert, @@ -163,10 +163,10 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op rule.allItems = append(rule.allItems, item) } if options.IPAcceptAny { - if legacyAddressFilter { + if legacyDNSMode { deprecated.Report(ctx, deprecated.OptionIPAcceptAny) } else { - return nil, E.New("ip_accept_any is removed in DNS evaluate mode, use ip_cidr with match_response") + return nil, E.New("ip_accept_any is removed when legacyDNSMode is disabled, use ip_cidr with match_response") } item := NewIPAcceptAnyItem() rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item) @@ -321,10 +321,10 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op matchSource = true } if options.RuleSetIPCIDRAcceptEmpty { - if legacyAddressFilter { + if legacyDNSMode { deprecated.Report(ctx, deprecated.OptionRuleSetIPCIDRAcceptEmpty) } else { - return nil, E.New("rule_set_ip_cidr_accept_empty is removed in DNS evaluate mode") + return nil, E.New("rule_set_ip_cidr_accept_empty is removed when legacyDNSMode is disabled") } } item := NewRuleSetItem(router, options.RuleSet, matchSource, options.RuleSetIPCIDRAcceptEmpty) @@ -450,7 +450,7 @@ func (r *LogicalDNSRule) matchStatesForMatch(metadata *adapter.InboundContext) r return stateSet } -func NewLogicalDNSRule(ctx context.Context, logger log.ContextLogger, options option.LogicalDNSRule, legacyAddressFilter bool) (*LogicalDNSRule, error) { +func NewLogicalDNSRule(ctx context.Context, logger log.ContextLogger, options option.LogicalDNSRule, legacyDNSMode bool) (*LogicalDNSRule, error) { r := &LogicalDNSRule{ abstractLogicalRule: abstractLogicalRule{ rules: make([]adapter.HeadlessRule, len(options.Rules)), @@ -467,7 +467,7 @@ func NewLogicalDNSRule(ctx context.Context, logger log.ContextLogger, options op return nil, E.New("unknown logical mode: ", options.Mode) } for i, subRule := range options.Rules { - rule, err := NewDNSRule(ctx, logger, subRule, false, legacyAddressFilter) + rule, err := NewDNSRule(ctx, logger, subRule, false, legacyDNSMode) if err != nil { return nil, E.Cause(err, "sub rule[", i, "]") }