浏览代码

Add accept empty DNS rule option

世界 1 年之前
父节点
当前提交
93cf134995

+ 4 - 1
adapter/inbound.go

@@ -51,7 +51,9 @@ type InboundContext struct {
 
 	// rule cache
 
-	IPCIDRMatchSource            bool
+	IPCIDRMatchSource bool
+	IPCIDRAcceptEmpty bool
+
 	SourceAddressMatch           bool
 	SourcePortMatch              bool
 	DestinationAddressMatch      bool
@@ -62,6 +64,7 @@ type InboundContext struct {
 
 func (c *InboundContext) ResetRuleCache() {
 	c.IPCIDRMatchSource = false
+	c.IPCIDRAcceptEmpty = false
 	c.SourceAddressMatch = false
 	c.SourcePortMatch = false
 	c.DestinationAddressMatch = false

+ 27 - 2
docs/configuration/dns/rule.md

@@ -2,6 +2,12 @@
 icon: material/new-box
 ---
 
+!!! quote "Changes in sing-box 1.10.0"
+
+    :material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)  
+    :material-plus: [rule_set_ip_cidr_match_source](#rule_set_ip_cidr_match_source)  
+    :material-plus: [rule_set_ip_cidr_accept_empty](#rule_set_ip_cidr_accept_empty)
+
 !!! quote "Changes in sing-box 1.9.0"
 
     :material-plus: [geoip](#geoip)  
@@ -117,7 +123,10 @@ icon: material/new-box
           "geoip-cn",
           "geosite-cn"
         ],
+        // deprecated
         "rule_set_ipcidr_match_source": false,
+        "rule_set_ip_cidr_match_source": false,
+        "rule_set_ip_cidr_accept_empty": false,
         "invert": false,
         "outbound": [
           "direct"
@@ -309,7 +318,17 @@ Match [Rule Set](/configuration/route/#rule_set).
 
 !!! question "Since sing-box 1.9.0"
 
-Make `ipcidr` in rule sets match the source IP.
+!!! failure "Deprecated in sing-box 1.10.0"
+    
+    `rule_set_ipcidr_match_source` is renamed to `rule_set_ip_cidr_match_source` and will be remove in sing-box 1.11.0.
+
+Make `ip_cidr` rule items in rule sets match the source IP.
+
+#### rule_set_ip_cidr_match_source
+
+!!! question "Since sing-box 1.10.0"
+
+Make `ip_cidr` rule items in rule sets match the source IP.
 
 #### invert
 
@@ -347,7 +366,7 @@ Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
 
 ### Address Filter Fields
 
-Only takes effect for IP address requests. When the query results do not match the address filtering rule items, the current rule will be skipped.
+Only takes effect for address requests (A/AAAA/HTTPS). When the query results do not match the address filtering rule items, the current rule will be skipped.
 
 !!! info ""
 
@@ -375,6 +394,12 @@ Match IP CIDR with query response.
 
 Match private IP with query response.
 
+#### rule_set_ip_cidr_accept_empty
+
+!!! question "Since sing-box 1.10.0"
+
+Make `ip_cidr` rules in rule sets accept empty query response.
+
 ### Logical Fields
 
 #### type

+ 28 - 3
docs/configuration/dns/rule.zh.md

@@ -2,6 +2,12 @@
 icon: material/new-box
 ---
 
+!!! quote "sing-box 1.10.0 中的更改"
+
+    :material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)  
+    :material-plus: [rule_set_ip_cidr_match_source](#rule_set_ip_cidr_match_source)  
+    :material-plus: [rule_set_ip_cidr_accept_empty](#rule_set_ip_cidr_accept_empty)
+
 !!! quote "sing-box 1.9.0 中的更改"
 
     :material-plus: [geoip](#geoip)  
@@ -117,7 +123,10 @@ icon: material/new-box
           "geoip-cn",
           "geosite-cn"
         ],
+        // 已弃用
         "rule_set_ipcidr_match_source": false,
+        "rule_set_ip_cidr_match_source": false,
+        "rule_set_ip_cidr_accept_empty": false,
         "invert": false,
         "outbound": [
           "direct"
@@ -307,7 +316,17 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
 
 !!! question "自 sing-box 1.9.0 起"
 
-使规则集中的 `ipcidr` 规则匹配源 IP。
+!!! failure "已在 sing-box 1.10.0 废弃"
+
+    `rule_set_ipcidr_match_source` 已重命名为 `rule_set_ip_cidr_match_source` 且将在 sing-box 1.11.0 移除。
+
+使规则集中的 `ip_cidr` 规则匹配源 IP。
+
+#### rule_set_ip_cidr_match_source
+
+!!! question "自 sing-box 1.10.0 起"
+
+使规则集中的 `ip_cidr` 规则匹配源 IP。
 
 #### invert
 
@@ -345,7 +364,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
 
 ### 地址筛选字段
 
-仅对IP地址请求生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
+仅对地址请求 (A/AAAA/HTTPS) 生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
 
 !!! info ""
 
@@ -365,7 +384,7 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
 
 !!! question "自 sing-box 1.9.0 起"
 
-与查询应匹配 IP CIDR。
+与查询应匹配 IP CIDR。
 
 #### ip_is_private
 
@@ -373,6 +392,12 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
 
 与查询响应匹配非公开 IP。
 
+#### rule_set_ip_cidr_accept_empty
+
+!!! question "自 sing-box 1.10.0 起"
+
+使规则集中的 `ip_cidr` 规则接受空查询响应。
+
 ### 逻辑字段
 
 #### type

+ 6 - 6
docs/configuration/inbound/tun.zh.md

@@ -168,7 +168,7 @@ tun 接口的 IPv4 和 IPv6 前缀。
 
 !!! failure "已在 sing-box 1.10.0 废弃"
 
-    `inet4_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除.
+    `inet4_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除
 
 ==必填==
 
@@ -178,7 +178,7 @@ tun 接口的 IPv4 前缀。
 
 !!! failure "已在 sing-box 1.10.0 废弃"
 
-    `inet6_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除.
+    `inet6_address` 已合并到 `address` 且将在 sing-box 1.11.0 移除
 
 tun 接口的 IPv6 前缀。
 
@@ -288,7 +288,7 @@ tun 接口的 IPv6 前缀。
 
 !!! failure "已在 sing-box 1.10.0 废弃"
 
-    `inet4_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除.
+    `inet4_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除
 
 启用 `auto_route` 时使用自定义路由而不是默认路由。
 
@@ -296,7 +296,7 @@ tun 接口的 IPv6 前缀。
 
 !!! failure "已在 sing-box 1.10.0 废弃"
 
-    `inet6_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除.
+    `inet6_route_address` 已合并到 `route_address` 且将在 sing-box 1.11.0 移除
 
 启用 `auto_route` 时使用自定义路由而不是默认路由。
 
@@ -310,7 +310,7 @@ tun 接口的 IPv6 前缀。
 
 !!! failure "已在 sing-box 1.10.0 废弃"
 
-    `inet4_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除.
+    `inet4_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除
 
 启用 `auto_route` 时排除自定义路由。
 
@@ -318,7 +318,7 @@ tun 接口的 IPv6 前缀。
 
 !!! failure "已在 sing-box 1.10.0 废弃"
 
-    `inet6_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除.
+    `inet6_route_exclude_address` 已合并到 `route_exclude_address` 且将在 sing-box 1.11.0 移除
 
 启用 `auto_route` 时排除自定义路由。
 

+ 22 - 1
docs/configuration/route/rule.md

@@ -1,3 +1,12 @@
+---
+icon: material/alert-decagram
+---
+
+!!! quote "Changes in sing-box 1.10.0"
+
+    :material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)  
+    :material-plus: [rule_set_ip_cidr_match_source](#rule_set_ip_cidr_match_source)  
+
 !!! quote "Changes in sing-box 1.8.0"
 
     :material-plus: [rule_set](#rule_set)  
@@ -105,7 +114,9 @@
           "geoip-cn",
           "geosite-cn"
         ],
+        // deprecated
         "rule_set_ipcidr_match_source": false,
+        "rule_set_ip_cidr_match_source": false,
         "invert": false,
         "outbound": "direct"
       },
@@ -303,7 +314,17 @@ Match [Rule Set](/configuration/route/#rule_set).
 
 !!! question "Since sing-box 1.8.0"
 
-Make `ipcidr` in rule sets match the source IP.
+!!! failure "Deprecated in sing-box 1.10.0"
+
+    `rule_set_ipcidr_match_source` is renamed to `rule_set_ip_cidr_match_source` and will be remove in sing-box 1.11.0.
+
+Make `ip_cidr` in rule sets match the source IP.
+
+#### rule_set_ip_cidr_match_source
+
+!!! question "Since sing-box 1.10.0"
+
+Make `ip_cidr` in rule sets match the source IP.
 
 #### invert
 

+ 22 - 1
docs/configuration/route/rule.zh.md

@@ -1,3 +1,12 @@
+---
+icon: material/alert-decagram
+---
+
+!!! quote "sing-box 1.10.0 中的更改"
+
+    :material-delete-clock: [rule_set_ipcidr_match_source](#rule_set_ipcidr_match_source)  
+    :material-plus: [rule_set_ip_cidr_match_source](#rule_set_ip_cidr_match_source)  
+
 !!! quote "sing-box 1.8.0 中的更改"
 
     :material-plus: [rule_set](#rule_set)  
@@ -103,7 +112,9 @@
           "geoip-cn",
           "geosite-cn"
         ],
+        // 已弃用
         "rule_set_ipcidr_match_source": false,
+        "rule_set_ip_cidr_match_source": false,
         "invert": false,
         "outbound": "direct"
       },
@@ -301,7 +312,17 @@
 
 !!! question "自 sing-box 1.8.0 起"
 
-使规则集中的 `ipcidr` 规则匹配源 IP。
+!!! failure "已在 sing-box 1.10.0 废弃"
+
+    `rule_set_ipcidr_match_source` 已重命名为 `rule_set_ip_cidr_match_source` 且将在 sing-box 1.11.0 移除。
+
+使规则集中的 `ip_cidr` 规则匹配源 IP。
+
+#### rule_set_ip_cidr_match_source
+
+!!! question "自 sing-box 1.10.0 起"
+
+使规则集中的 `ip_cidr` 规则匹配源 IP。
 
 #### invert
 

+ 22 - 3
option/rule.go

@@ -64,7 +64,7 @@ func (r Rule) IsValid() bool {
 	}
 }
 
-type DefaultRule struct {
+type _DefaultRule struct {
 	Inbound                  Listable[string] `json:"inbound,omitempty"`
 	IPVersion                int              `json:"ip_version,omitempty"`
 	Network                  Listable[string] `json:"network,omitempty"`
@@ -94,12 +94,31 @@ type DefaultRule struct {
 	WIFISSID                 Listable[string] `json:"wifi_ssid,omitempty"`
 	WIFIBSSID                Listable[string] `json:"wifi_bssid,omitempty"`
 	RuleSet                  Listable[string] `json:"rule_set,omitempty"`
-	RuleSetIPCIDRMatchSource bool             `json:"rule_set_ipcidr_match_source,omitempty"`
+	RuleSetIPCIDRMatchSource bool             `json:"rule_set_ip_cidr_match_source,omitempty"`
 	Invert                   bool             `json:"invert,omitempty"`
 	Outbound                 string           `json:"outbound,omitempty"`
+
+	// Deprecated: renamed to rule_set_ip_cidr_match_source
+	Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
+}
+
+type DefaultRule _DefaultRule
+
+func (r *DefaultRule) UnmarshalJSON(bytes []byte) error {
+	err := json.Unmarshal(bytes, (*_DefaultRule)(r))
+	if err != nil {
+		return err
+	}
+	//nolint:staticcheck
+	//goland:noinspection GoDeprecation
+	if r.Deprecated_RulesetIPCIDRMatchSource {
+		r.Deprecated_RulesetIPCIDRMatchSource = false
+		r.RuleSetIPCIDRMatchSource = true
+	}
+	return nil
 }
 
-func (r DefaultRule) IsValid() bool {
+func (r *DefaultRule) IsValid() bool {
 	var defaultValue DefaultRule
 	defaultValue.Invert = r.Invert
 	defaultValue.Outbound = r.Outbound

+ 23 - 3
option/rule_dns.go

@@ -64,7 +64,7 @@ func (r DNSRule) IsValid() bool {
 	}
 }
 
-type DefaultDNSRule struct {
+type _DefaultDNSRule struct {
 	Inbound                  Listable[string]       `json:"inbound,omitempty"`
 	IPVersion                int                    `json:"ip_version,omitempty"`
 	QueryType                Listable[DNSQueryType] `json:"query_type,omitempty"`
@@ -96,15 +96,35 @@ type DefaultDNSRule struct {
 	WIFISSID                 Listable[string]       `json:"wifi_ssid,omitempty"`
 	WIFIBSSID                Listable[string]       `json:"wifi_bssid,omitempty"`
 	RuleSet                  Listable[string]       `json:"rule_set,omitempty"`
-	RuleSetIPCIDRMatchSource bool                   `json:"rule_set_ipcidr_match_source,omitempty"`
+	RuleSetIPCIDRMatchSource bool                   `json:"rule_set_ip_cidr_match_source,omitempty"`
+	RuleSetIPCIDRAcceptEmpty bool                   `json:"rule_set_ip_cidr_accept_empty,omitempty"`
 	Invert                   bool                   `json:"invert,omitempty"`
 	Server                   string                 `json:"server,omitempty"`
 	DisableCache             bool                   `json:"disable_cache,omitempty"`
 	RewriteTTL               *uint32                `json:"rewrite_ttl,omitempty"`
 	ClientSubnet             *AddrPrefix            `json:"client_subnet,omitempty"`
+
+	// Deprecated: renamed to rule_set_ip_cidr_match_source
+	Deprecated_RulesetIPCIDRMatchSource bool `json:"rule_set_ipcidr_match_source,omitempty"`
+}
+
+type DefaultDNSRule _DefaultDNSRule
+
+func (r *DefaultDNSRule) UnmarshalJSON(bytes []byte) error {
+	err := json.UnmarshalDisallowUnknownFields(bytes, (*_DefaultDNSRule)(r))
+	if err != nil {
+		return err
+	}
+	//nolint:staticcheck
+	//goland:noinspection GoDeprecation
+	if r.Deprecated_RulesetIPCIDRMatchSource {
+		r.Deprecated_RulesetIPCIDRMatchSource = false
+		r.RuleSetIPCIDRMatchSource = true
+	}
+	return nil
 }
 
-func (r DefaultDNSRule) IsValid() bool {
+func (r *DefaultDNSRule) IsValid() bool {
 	var defaultValue DefaultDNSRule
 	defaultValue.Invert = r.Invert
 	defaultValue.Server = r.Server

+ 11 - 6
route/router_dns.go

@@ -104,7 +104,8 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
 	response, cached = r.dnsClient.ExchangeCache(ctx, message)
 	if !cached {
 		var metadata *adapter.InboundContext
-		ctx, metadata = adapter.AppendContext(ctx)
+		ctx, metadata = adapter.ExtendContext(ctx)
+		metadata.Destination = M.Socksaddr{}
 		if len(message.Question) > 0 {
 			metadata.QueryType = message.Question[0].Qtype
 			switch metadata.QueryType {
@@ -126,12 +127,16 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
 				dnsCtx       context.Context
 				addressLimit bool
 			)
-
 			dnsCtx, transport, strategy, rule, ruleIndex = r.matchDNS(ctx, true, ruleIndex, isAddressQuery(message))
+			dnsCtx = adapter.OverrideContext(dnsCtx)
 			if rule != nil && rule.WithAddressLimit() {
 				addressLimit = true
 				response, err = r.dnsClient.ExchangeWithResponseCheck(dnsCtx, transport, message, strategy, func(response *mDNS.Msg) bool {
-					metadata.DestinationAddresses, _ = dns.MessageToAddresses(response)
+					addresses, addrErr := dns.MessageToAddresses(response)
+					if addrErr != nil {
+						return false
+					}
+					metadata.DestinationAddresses = addresses
 					return rule.MatchAddressLimit(metadata)
 				})
 			} else {
@@ -190,7 +195,8 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
 		return responseAddrs, nil
 	}
 	r.dnsLogger.DebugContext(ctx, "lookup domain ", domain)
-	ctx, metadata := adapter.AppendContext(ctx)
+	ctx, metadata := adapter.ExtendContext(ctx)
+	metadata.Destination = M.Socksaddr{}
 	metadata.Domain = domain
 	var (
 		transport         dns.Transport
@@ -204,9 +210,8 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
 			dnsCtx       context.Context
 			addressLimit bool
 		)
-		metadata.ResetRuleCache()
-		metadata.DestinationAddresses = nil
 		dnsCtx, transport, transportStrategy, rule, ruleIndex = r.matchDNS(ctx, false, ruleIndex, true)
+		dnsCtx = adapter.OverrideContext(dnsCtx)
 		if strategy == dns.DomainStrategyAsIS {
 			strategy = transportStrategy
 		}

+ 1 - 1
route/rule_default.go

@@ -205,7 +205,7 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
 		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.RuleSet) > 0 {
-		item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource)
+		item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource, false)
 		rule.items = append(rule.items, item)
 		rule.allItems = append(rule.allItems, item)
 	}

+ 1 - 1
route/rule_dns.go

@@ -219,7 +219,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
 		rule.allItems = append(rule.allItems, item)
 	}
 	if len(options.RuleSet) > 0 {
-		item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource)
+		item := NewRuleSetItem(router, options.RuleSet, options.RuleSetIPCIDRMatchSource, options.RuleSetIPCIDRAcceptEmpty)
 		rule.items = append(rule.items, item)
 		rule.allItems = append(rule.allItems, item)
 	}

+ 10 - 9
route/rule_item_cidr.go

@@ -75,18 +75,19 @@ func NewRawIPCIDRItem(isSource bool, ipSet *netipx.IPSet) *IPCIDRItem {
 func (r *IPCIDRItem) Match(metadata *adapter.InboundContext) bool {
 	if r.isSource || metadata.IPCIDRMatchSource {
 		return r.ipSet.Contains(metadata.Source.Addr)
-	} else {
-		if metadata.Destination.IsIP() {
-			return r.ipSet.Contains(metadata.Destination.Addr)
-		} else {
-			for _, address := range metadata.DestinationAddresses {
-				if r.ipSet.Contains(address) {
-					return true
-				}
+	}
+	if metadata.Destination.IsIP() {
+		return r.ipSet.Contains(metadata.Destination.Addr)
+	}
+	if len(metadata.DestinationAddresses) > 0 {
+		for _, address := range metadata.DestinationAddresses {
+			if r.ipSet.Contains(address) {
+				return true
 			}
 		}
+		return false
 	}
-	return false
+	return metadata.IPCIDRAcceptEmpty
 }
 
 func (r *IPCIDRItem) String() string {

+ 8 - 5
route/rule_item_rule_set.go

@@ -15,14 +15,16 @@ type RuleSetItem struct {
 	router            adapter.Router
 	tagList           []string
 	setList           []adapter.RuleSet
-	ipcidrMatchSource bool
+	ipCidrMatchSource bool
+	ipCidrAcceptEmpty bool
 }
 
-func NewRuleSetItem(router adapter.Router, tagList []string, ipCIDRMatchSource bool) *RuleSetItem {
+func NewRuleSetItem(router adapter.Router, tagList []string, ipCIDRMatchSource bool, ipCidrAcceptEmpty bool) *RuleSetItem {
 	return &RuleSetItem{
 		router:            router,
 		tagList:           tagList,
-		ipcidrMatchSource: ipCIDRMatchSource,
+		ipCidrMatchSource: ipCIDRMatchSource,
+		ipCidrAcceptEmpty: ipCidrAcceptEmpty,
 	}
 }
 
@@ -39,7 +41,8 @@ func (r *RuleSetItem) Start() error {
 }
 
 func (r *RuleSetItem) Match(metadata *adapter.InboundContext) bool {
-	metadata.IPCIDRMatchSource = r.ipcidrMatchSource
+	metadata.IPCIDRMatchSource = r.ipCidrMatchSource
+	metadata.IPCIDRAcceptEmpty = r.ipCidrAcceptEmpty
 	for _, ruleSet := range r.setList {
 		if ruleSet.Match(metadata) {
 			return true
@@ -49,7 +52,7 @@ func (r *RuleSetItem) Match(metadata *adapter.InboundContext) bool {
 }
 
 func (r *RuleSetItem) ContainsDestinationIPCIDRRule() bool {
-	if r.ipcidrMatchSource {
+	if r.ipCidrMatchSource {
 		return false
 	}
 	return common.Any(r.setList, func(ruleSet adapter.RuleSet) bool {