mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-09-23 16:30:25 +00:00
Inverted rules whose address filter conditions come from rule sets or logical rules were rejected during pre-lookup matching, so the lookup was never sent and the rules never matched; for plain ip rule sets this was a regression from the rule-set invert fix in 1.12.22. Pre-lookup matching now tracks whether the result depends on deferred address filter conditions and defers such rules to the response phase, while results already determined by evaluated conditions are still decided during pre-lookup.
563 lines
19 KiB
Go
563 lines
19 KiB
Go
package rule
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
C "github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing-box/option"
|
|
"github.com/sagernet/sing/common"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
"github.com/sagernet/sing/service"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
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() {
|
|
return nil, E.New("missing conditions")
|
|
}
|
|
if !checkServer && options.DefaultOptions.Action == C.RuleActionTypeEvaluate {
|
|
return nil, E.New(options.DefaultOptions.Action, " is only allowed on top-level DNS rules")
|
|
}
|
|
err := validateDNSRuleAction(options.DefaultOptions.DNSRuleAction)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if options.DefaultOptions.Race && !options.DefaultOptions.MatchResponse.IsEnabled() {
|
|
return nil, E.New("`race` requires `match_response`")
|
|
}
|
|
switch options.DefaultOptions.Action {
|
|
case "", C.RuleActionTypeRoute:
|
|
if options.DefaultOptions.RouteOptions.Server == "" && checkServer {
|
|
return nil, E.New("missing server field")
|
|
}
|
|
case C.RuleActionTypeEvaluate:
|
|
if options.DefaultOptions.EvaluateOptions.Server == "" && checkServer {
|
|
return nil, E.New("missing server field")
|
|
}
|
|
}
|
|
return NewDefaultDNSRule(ctx, logger, options.DefaultOptions, legacyDNSMode)
|
|
case C.RuleTypeLogical:
|
|
if !options.LogicalOptions.IsValid() {
|
|
return nil, E.New("missing conditions")
|
|
}
|
|
if !checkServer && options.LogicalOptions.Action == C.RuleActionTypeEvaluate {
|
|
return nil, E.New(options.LogicalOptions.Action, " is only allowed on top-level DNS rules")
|
|
}
|
|
err := validateDNSRuleAction(options.LogicalOptions.DNSRuleAction)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch options.LogicalOptions.Action {
|
|
case "", C.RuleActionTypeRoute:
|
|
if options.LogicalOptions.RouteOptions.Server == "" && checkServer {
|
|
return nil, E.New("missing server field")
|
|
}
|
|
case C.RuleActionTypeEvaluate:
|
|
if options.LogicalOptions.EvaluateOptions.Server == "" && checkServer {
|
|
return nil, E.New("missing server field")
|
|
}
|
|
}
|
|
return NewLogicalDNSRule(ctx, logger, options.LogicalOptions, legacyDNSMode)
|
|
default:
|
|
return nil, E.New("unknown rule type: ", options.Type)
|
|
}
|
|
}
|
|
|
|
func validateDNSRuleAction(action option.DNSRuleAction) error {
|
|
if action.Action == C.RuleActionTypeReject && action.RejectOptions.Method == C.RuleActionRejectMethodReply {
|
|
return E.New("reject method `reply` is not supported for DNS rules")
|
|
}
|
|
var routeOptions option.AbstractDNSRouteActionOptions
|
|
switch action.Action {
|
|
case "", C.RuleActionTypeRoute:
|
|
routeOptions = action.RouteOptions.AbstractDNSRouteActionOptions
|
|
case C.RuleActionTypeEvaluate:
|
|
routeOptions = action.EvaluateOptions.AbstractDNSRouteActionOptions
|
|
case C.RuleActionTypeRouteOptions:
|
|
routeOptions = option.AbstractDNSRouteActionOptions(action.RouteOptionsOptions)
|
|
}
|
|
if routeOptions.RemoveClientSubnet && routeOptions.ClientSubnet != nil {
|
|
return E.New("`client_subnet` and `remove_client_subnet` are mutually exclusive")
|
|
}
|
|
if action.Race {
|
|
switch action.Action {
|
|
case "", C.RuleActionTypeRoute, C.RuleActionTypeRespond, C.RuleActionTypeReject, C.RuleActionTypePredefined:
|
|
default:
|
|
return E.New("`race` requires a final action")
|
|
}
|
|
if action.RouteOptions.Speculative {
|
|
return E.New("`race` and `speculative` cannot be combined on the same rule")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var _ adapter.DNSRule = (*DefaultDNSRule)(nil)
|
|
|
|
type DefaultDNSRule struct {
|
|
abstractDefaultRule
|
|
matchResponse bool
|
|
matchResponseTag string
|
|
race bool
|
|
}
|
|
|
|
func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options option.DefaultDNSRule, legacyDNSMode bool) (*DefaultDNSRule, error) {
|
|
rule := &DefaultDNSRule{
|
|
abstractDefaultRule: abstractDefaultRule{
|
|
invert: options.Invert,
|
|
action: NewDNSRuleAction(logger, options.DNSRuleAction),
|
|
},
|
|
matchResponse: options.MatchResponse.IsEnabled(),
|
|
matchResponseTag: options.MatchResponse.ResponseTag(),
|
|
race: options.Race,
|
|
}
|
|
if len(options.Inbound) > 0 {
|
|
item := NewInboundRule(options.Inbound)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
router := service.FromContext[adapter.Router](ctx)
|
|
networkManager := service.FromContext[adapter.NetworkManager](ctx)
|
|
if options.IPVersion > 0 {
|
|
switch options.IPVersion {
|
|
case 4, 6:
|
|
item := NewIPVersionItem(options.IPVersion == 6)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
default:
|
|
return nil, E.New("invalid ip version: ", options.IPVersion)
|
|
}
|
|
}
|
|
if len(options.QueryType) > 0 {
|
|
item := NewQueryTypeItem(options.QueryType)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.QueryClientSubnet) > 0 {
|
|
item := NewQueryClientSubnetItem(options.QueryClientSubnet)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.QueryDNSSEC {
|
|
item := NewQueryDNSSECItem()
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.Network) > 0 {
|
|
item := NewNetworkItem(options.Network)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.AuthUser) > 0 {
|
|
item := NewAuthUserItem(options.AuthUser)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.Protocol) > 0 {
|
|
item := NewProtocolItem(options.Protocol)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 {
|
|
item, err := NewDomainItem(options.Domain, options.DomainSuffix)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.DomainKeyword) > 0 {
|
|
item := NewDomainKeywordItem(options.DomainKeyword)
|
|
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.DomainRegex) > 0 {
|
|
item, err := NewDomainRegexItem(options.DomainRegex)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "domain_regex")
|
|
}
|
|
rule.destinationAddressItems = append(rule.destinationAddressItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.Geosite) > 0 { //nolint:staticcheck
|
|
return nil, E.New("geosite database is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0")
|
|
}
|
|
if len(options.SourceGeoIP) > 0 {
|
|
return nil, E.New("geoip database is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0")
|
|
}
|
|
if len(options.GeoIP) > 0 {
|
|
return nil, E.New("geoip database is deprecated in sing-box 1.8.0 and removed in sing-box 1.12.0")
|
|
}
|
|
if len(options.SourceIPCIDR) > 0 {
|
|
item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "source_ip_cidr")
|
|
}
|
|
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.IPCIDR) > 0 {
|
|
item, err := NewIPCIDRItem(false, options.IPCIDR)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "ip_cidr")
|
|
}
|
|
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.SourceIPIsPrivate {
|
|
item := NewIPIsPrivateItem(true)
|
|
rule.sourceAddressItems = append(rule.sourceAddressItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.IPIsPrivate {
|
|
item := NewIPIsPrivateItem(false)
|
|
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.IPAcceptAny {
|
|
item := NewIPAcceptAnyItem()
|
|
rule.destinationIPCIDRItems = append(rule.destinationIPCIDRItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.ResponseRcode != nil {
|
|
item := NewDNSResponseRCodeItem(int(*options.ResponseRcode))
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.ResponseAnswer) > 0 {
|
|
item := NewDNSResponseRecordItem("response_answer", options.ResponseAnswer, dnsResponseAnswers)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.ResponseNs) > 0 {
|
|
item := NewDNSResponseRecordItem("response_ns", options.ResponseNs, dnsResponseNS)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.ResponseExtra) > 0 {
|
|
item := NewDNSResponseRecordItem("response_extra", options.ResponseExtra, dnsResponseExtra)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.SourcePort) > 0 {
|
|
item := NewPortItem(true, options.SourcePort)
|
|
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.SourcePortRange) > 0 {
|
|
item, err := NewPortRangeItem(true, options.SourcePortRange)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "source_port_range")
|
|
}
|
|
rule.sourcePortItems = append(rule.sourcePortItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.Port) > 0 {
|
|
item := NewPortItem(false, options.Port)
|
|
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.PortRange) > 0 {
|
|
item, err := NewPortRangeItem(false, options.PortRange)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "port_range")
|
|
}
|
|
rule.destinationPortItems = append(rule.destinationPortItems, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.ProcessName) > 0 {
|
|
item := NewProcessItem(options.ProcessName)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.ProcessPath) > 0 {
|
|
item := NewProcessPathItem(options.ProcessPath)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.ProcessPathRegex) > 0 {
|
|
item, err := NewProcessPathRegexItem(options.ProcessPathRegex)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "process_path_regex")
|
|
}
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.PackageName) > 0 {
|
|
item := NewPackageNameItem(options.PackageName)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.PackageNameRegex) > 0 {
|
|
item, err := NewPackageNameRegexItem(options.PackageNameRegex)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "package_name_regex")
|
|
}
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.User) > 0 {
|
|
item := NewUserItem(options.User)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.UserID) > 0 {
|
|
item := NewUserIDItem(options.UserID)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.Outbound) > 0 {
|
|
item := NewOutboundRule(ctx, options.Outbound)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.ClashMode != "" {
|
|
item := NewClashModeItem(ctx, options.ClashMode)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.NetworkType) > 0 {
|
|
item := NewNetworkTypeItem(networkManager, common.Map(options.NetworkType, option.InterfaceType.Build))
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.NetworkIsExpensive {
|
|
item := NewNetworkIsExpensiveItem(networkManager)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.NetworkIsConstrained {
|
|
item := NewNetworkIsConstrainedItem(networkManager)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.WIFISSID) > 0 {
|
|
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.WIFIBSSID) > 0 {
|
|
item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.InterfaceAddress != nil && options.InterfaceAddress.Size() > 0 {
|
|
item := NewInterfaceAddressItem(networkManager, options.InterfaceAddress)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.NetworkInterfaceAddress != nil && options.NetworkInterfaceAddress.Size() > 0 {
|
|
item := NewNetworkInterfaceAddressItem(networkManager, options.NetworkInterfaceAddress)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.DefaultInterfaceAddress) > 0 {
|
|
item := NewDefaultInterfaceAddressItem(networkManager, options.DefaultInterfaceAddress)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.SourceMACAddress) > 0 {
|
|
item := NewSourceMACAddressItem(options.SourceMACAddress)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.SourceHostname) > 0 {
|
|
item := NewSourceHostnameItem(options.SourceHostname)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if len(options.PreferredBy) > 0 {
|
|
item := NewPreferredByDNSItem(ctx, options.PreferredBy)
|
|
rule.items = append(rule.items, item)
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
if options.RuleSetIPCIDRAcceptEmpty { //nolint:staticcheck
|
|
if legacyDNSMode {
|
|
deprecated.Report(ctx, deprecated.OptionRuleSetIPCIDRAcceptEmpty)
|
|
} else {
|
|
return nil, E.New(deprecated.OptionRuleSetIPCIDRAcceptEmpty.MessageWithLink())
|
|
}
|
|
}
|
|
if len(options.RuleSet) > 0 {
|
|
//nolint:staticcheck
|
|
if options.Deprecated_RulesetIPCIDRMatchSource {
|
|
return nil, E.New("rule_set_ipcidr_match_source is deprecated in sing-box 1.10.0 and removed in sing-box 1.11.0")
|
|
}
|
|
var matchSource bool
|
|
if options.RuleSetIPCIDRMatchSource {
|
|
matchSource = true
|
|
}
|
|
item := NewRuleSetItem(router, options.RuleSet, matchSource, options.RuleSetIPCIDRAcceptEmpty) //nolint:staticcheck
|
|
rule.ruleSetItem = item
|
|
rule.allItems = append(rule.allItems, item)
|
|
}
|
|
return rule, nil
|
|
}
|
|
|
|
func (r *DefaultDNSRule) Action() adapter.RuleAction {
|
|
return r.action
|
|
}
|
|
|
|
func (r *DefaultDNSRule) WithAddressLimit() bool {
|
|
if len(r.destinationIPCIDRItems) > 0 {
|
|
return true
|
|
}
|
|
return r.ruleSetItem != nil && r.ruleSetItem.ContainsDestinationIPCIDRRule()
|
|
}
|
|
|
|
func (r *DefaultDNSRule) Match(metadata *adapter.InboundContext) bool {
|
|
return r.matchForMatch(metadata)
|
|
}
|
|
|
|
func (r *DefaultDNSRule) LegacyPreMatch(metadata *adapter.InboundContext) bool {
|
|
if r.matchResponse {
|
|
return false
|
|
}
|
|
metadata.IgnoreDestinationIPCIDRMatch = true
|
|
defer func() { metadata.IgnoreDestinationIPCIDRMatch = false }()
|
|
return r.abstractDefaultRule.Match(metadata)
|
|
}
|
|
|
|
func (r *DefaultDNSRule) MatchResponseTag() string {
|
|
return r.matchResponseTag
|
|
}
|
|
|
|
func (r *DefaultDNSRule) MatchResponseTags() []string {
|
|
if r.matchResponseTag == "" {
|
|
return nil
|
|
}
|
|
return []string{r.matchResponseTag}
|
|
}
|
|
|
|
func (r *DefaultDNSRule) MatchResponseAnonymous() bool {
|
|
return r.matchResponse && r.matchResponseTag == ""
|
|
}
|
|
|
|
func (r *DefaultDNSRule) Race() bool {
|
|
return r.race
|
|
}
|
|
|
|
func (r *DefaultDNSRule) matchForMatch(metadata *adapter.InboundContext) bool {
|
|
if r.matchResponse {
|
|
response := metadata.DNSResponse
|
|
if r.matchResponseTag != "" {
|
|
response = metadata.NamedDNSResponses[r.matchResponseTag]
|
|
}
|
|
if response == nil {
|
|
return r.invert
|
|
}
|
|
matchMetadata := *metadata
|
|
matchMetadata.DNSResponse = response
|
|
matchMetadata.DestinationAddressMatchFromResponse = true
|
|
return r.abstractDefaultRule.Match(&matchMetadata)
|
|
}
|
|
return r.abstractDefaultRule.Match(metadata)
|
|
}
|
|
|
|
func (r *DefaultDNSRule) MatchAddressLimit(metadata *adapter.InboundContext, response *dns.Msg) bool {
|
|
matchMetadata := *metadata
|
|
matchMetadata.ResetRuleCache()
|
|
matchMetadata.DNSResponse = response
|
|
matchMetadata.DestinationAddressMatchFromResponse = true
|
|
return r.abstractDefaultRule.Match(&matchMetadata)
|
|
}
|
|
|
|
var _ adapter.DNSRule = (*LogicalDNSRule)(nil)
|
|
|
|
type LogicalDNSRule struct {
|
|
abstractLogicalRule
|
|
matchResponseTags []string
|
|
matchResponseAnonymous bool
|
|
race bool
|
|
}
|
|
|
|
func (r *LogicalDNSRule) MatchResponseTag() string {
|
|
return ""
|
|
}
|
|
|
|
func (r *LogicalDNSRule) MatchResponseTags() []string {
|
|
return r.matchResponseTags
|
|
}
|
|
|
|
func (r *LogicalDNSRule) MatchResponseAnonymous() bool {
|
|
return r.matchResponseAnonymous
|
|
}
|
|
|
|
func (r *LogicalDNSRule) Race() bool {
|
|
return r.race
|
|
}
|
|
|
|
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)),
|
|
invert: options.Invert,
|
|
action: NewDNSRuleAction(logger, options.DNSRuleAction),
|
|
},
|
|
race: options.Race,
|
|
}
|
|
switch options.Mode {
|
|
case C.LogicalTypeAnd:
|
|
r.mode = C.LogicalTypeAnd
|
|
case C.LogicalTypeOr:
|
|
r.mode = C.LogicalTypeOr
|
|
default:
|
|
return nil, E.New("unknown logical mode: ", options.Mode)
|
|
}
|
|
for i, subRule := range options.Rules {
|
|
err := validateNoNestedDNSRuleActions(subRule, true)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "sub rule[", i, "]")
|
|
}
|
|
rule, err := NewDNSRule(ctx, logger, subRule, false, legacyDNSMode)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "sub rule[", i, "]")
|
|
}
|
|
r.rules[i] = rule
|
|
}
|
|
for _, subRule := range r.rules {
|
|
if dnsRule, isDNSRule := subRule.(adapter.DNSRule); isDNSRule {
|
|
r.matchResponseTags = append(r.matchResponseTags, dnsRule.MatchResponseTags()...)
|
|
r.matchResponseAnonymous = r.matchResponseAnonymous || dnsRule.MatchResponseAnonymous()
|
|
}
|
|
}
|
|
r.matchResponseTags = common.Uniq(r.matchResponseTags)
|
|
if r.race && len(r.matchResponseTags) == 0 && !r.matchResponseAnonymous {
|
|
return nil, E.New("`race` requires `match_response` in sub-rules")
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
func (r *LogicalDNSRule) Action() adapter.RuleAction {
|
|
return r.action
|
|
}
|
|
|
|
func (r *LogicalDNSRule) WithAddressLimit() bool {
|
|
for _, rawRule := range r.rules {
|
|
if dnsRule, isDNSRule := rawRule.(adapter.DNSRule); isDNSRule && dnsRule.WithAddressLimit() {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (r *LogicalDNSRule) LegacyPreMatch(metadata *adapter.InboundContext) bool {
|
|
metadata.IgnoreDestinationIPCIDRMatch = true
|
|
defer func() { metadata.IgnoreDestinationIPCIDRMatch = false }()
|
|
return r.abstractLogicalRule.Match(metadata)
|
|
}
|
|
|
|
func (r *LogicalDNSRule) MatchAddressLimit(metadata *adapter.InboundContext, response *dns.Msg) bool {
|
|
matchMetadata := *metadata
|
|
matchMetadata.ResetRuleCache()
|
|
matchMetadata.DNSResponse = response
|
|
matchMetadata.DestinationAddressMatchFromResponse = true
|
|
return r.abstractLogicalRule.Match(&matchMetadata)
|
|
}
|