mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 00:21:30 +00:00
Add support for client-subnet
DNS options
This commit is contained in:
parent
0517ceef76
commit
f24a2aed7d
|
@ -86,6 +86,7 @@ type DNSRule interface {
|
||||||
Rule
|
Rule
|
||||||
DisableCache() bool
|
DisableCache() bool
|
||||||
RewriteTTL() *uint32
|
RewriteTTL() *uint32
|
||||||
|
ClientSubnet() *netip.Addr
|
||||||
WithAddressLimit() bool
|
WithAddressLimit() bool
|
||||||
MatchAddressLimit(metadata *InboundContext) bool
|
MatchAddressLimit(metadata *InboundContext) bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.9.0"
|
||||||
|
|
||||||
|
:material-plus: [client_subnet](#client_subnet)
|
||||||
|
|
||||||
# DNS
|
# DNS
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
@ -13,6 +21,7 @@
|
||||||
"disable_expire": false,
|
"disable_expire": false,
|
||||||
"independent_cache": false,
|
"independent_cache": false,
|
||||||
"reverse_mapping": false,
|
"reverse_mapping": false,
|
||||||
|
"client_subnet": "",
|
||||||
"fakeip": {}
|
"fakeip": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +69,10 @@ Stores a reverse mapping of IP addresses after responding to a DNS query in orde
|
||||||
Since this process relies on the act of resolving domain names by an application before making a request, it can be
|
Since this process relies on the act of resolving domain names by an application before making a request, it can be
|
||||||
problematic in environments such as macOS, where DNS is proxied and cached by the system.
|
problematic in environments such as macOS, where DNS is proxied and cached by the system.
|
||||||
|
|
||||||
#### fakeip
|
#### client_subnet
|
||||||
|
|
||||||
[FakeIP](./fakeip/) settings.
|
!!! question "Since sing-box 1.9.0"
|
||||||
|
|
||||||
|
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||||
|
|
||||||
|
Can be overrides by `servers.[].client_subnet` or `rules.[].client_subnet`.
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.9.0 中的更改"
|
||||||
|
|
||||||
|
:material-plus: [client_subnet](#client_subnet)
|
||||||
|
|
||||||
# DNS
|
# DNS
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
@ -13,6 +21,7 @@
|
||||||
"disable_expire": false,
|
"disable_expire": false,
|
||||||
"independent_cache": false,
|
"independent_cache": false,
|
||||||
"reverse_mapping": false,
|
"reverse_mapping": false,
|
||||||
|
"client_subnet": "",
|
||||||
"fakeip": {}
|
"fakeip": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +67,14 @@
|
||||||
|
|
||||||
由于此过程依赖于应用程序在发出请求之前解析域名的行为,因此在 macOS 等 DNS 由系统代理和缓存的环境中可能会出现问题。
|
由于此过程依赖于应用程序在发出请求之前解析域名的行为,因此在 macOS 等 DNS 由系统代理和缓存的环境中可能会出现问题。
|
||||||
|
|
||||||
|
#### client_subnet
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||||
|
|
||||||
|
可以被 `servers.[].client_subnet` 或 `rules.[].client_subnet` 覆盖。
|
||||||
|
|
||||||
#### fakeip
|
#### fakeip
|
||||||
|
|
||||||
[FakeIP](./fakeip/) 设置。
|
[FakeIP](./fakeip/) 设置。
|
||||||
|
|
|
@ -7,6 +7,7 @@ icon: material/new-box
|
||||||
:material-plus: [geoip](#geoip)
|
:material-plus: [geoip](#geoip)
|
||||||
:material-plus: [ip_cidr](#ip_cidr)
|
:material-plus: [ip_cidr](#ip_cidr)
|
||||||
:material-plus: [ip_is_private](#ip_is_private)
|
:material-plus: [ip_is_private](#ip_is_private)
|
||||||
|
:material-plus: [client_subnet](#client_subnet)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.8.0"
|
!!! quote "Changes in sing-box 1.8.0"
|
||||||
|
|
||||||
|
@ -121,7 +122,8 @@ icon: material/new-box
|
||||||
],
|
],
|
||||||
"server": "local",
|
"server": "local",
|
||||||
"disable_cache": false,
|
"disable_cache": false,
|
||||||
"rewrite_ttl": 100
|
"rewrite_ttl": 100,
|
||||||
|
"client_subnet": "127.0.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "logical",
|
"type": "logical",
|
||||||
|
@ -129,7 +131,8 @@ icon: material/new-box
|
||||||
"rules": [],
|
"rules": [],
|
||||||
"server": "local",
|
"server": "local",
|
||||||
"disable_cache": false,
|
"disable_cache": false,
|
||||||
"rewrite_ttl": 100
|
"rewrite_ttl": 100,
|
||||||
|
"client_subnet": "127.0.0.1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -280,8 +283,6 @@ Match Clash mode.
|
||||||
|
|
||||||
#### wifi_ssid
|
#### wifi_ssid
|
||||||
|
|
||||||
<!-- md:version 1.7.0-beta.4 -->
|
|
||||||
|
|
||||||
!!! quote ""
|
!!! quote ""
|
||||||
|
|
||||||
Only supported in graphical clients on Android and iOS.
|
Only supported in graphical clients on Android and iOS.
|
||||||
|
@ -326,6 +327,14 @@ Disable cache and save cache in this query.
|
||||||
|
|
||||||
Rewrite TTL in DNS responses.
|
Rewrite TTL in DNS responses.
|
||||||
|
|
||||||
|
#### client_subnet
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.9.0"
|
||||||
|
|
||||||
|
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||||
|
|
||||||
|
Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||||
|
|
||||||
### Address Filter Fields
|
### 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 IP address requests. When the query results do not match the address filtering rule items, the current rule will be skipped.
|
||||||
|
|
|
@ -7,6 +7,7 @@ icon: material/new-box
|
||||||
:material-plus: [geoip](#geoip)
|
:material-plus: [geoip](#geoip)
|
||||||
:material-plus: [ip_cidr](#ip_cidr)
|
:material-plus: [ip_cidr](#ip_cidr)
|
||||||
:material-plus: [ip_is_private](#ip_is_private)
|
:material-plus: [ip_is_private](#ip_is_private)
|
||||||
|
:material-plus: [client_subnet](#client_subnet)
|
||||||
|
|
||||||
!!! quote "sing-box 1.8.0 中的更改"
|
!!! quote "sing-box 1.8.0 中的更改"
|
||||||
|
|
||||||
|
@ -120,14 +121,16 @@ icon: material/new-box
|
||||||
"direct"
|
"direct"
|
||||||
],
|
],
|
||||||
"server": "local",
|
"server": "local",
|
||||||
"disable_cache": false
|
"disable_cache": false,
|
||||||
|
"client_subnet": "127.0.0.1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "logical",
|
"type": "logical",
|
||||||
"mode": "and",
|
"mode": "and",
|
||||||
"rules": [],
|
"rules": [],
|
||||||
"server": "local",
|
"server": "local",
|
||||||
"disable_cache": false
|
"disable_cache": false,
|
||||||
|
"client_subnet": "127.0.0.1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -322,6 +325,14 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||||
|
|
||||||
重写 DNS 回应中的 TTL。
|
重写 DNS 回应中的 TTL。
|
||||||
|
|
||||||
|
#### client_subnet
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||||
|
|
||||||
|
将覆盖 `dns.client_subnet` 与 `servers.[].client_subnet`。
|
||||||
|
|
||||||
### 地址筛选字段
|
### 地址筛选字段
|
||||||
|
|
||||||
仅对IP地址请求生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
|
仅对IP地址请求生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.9.0"
|
||||||
|
|
||||||
|
:material-plus: [client_subnet](#client_subnet)
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -5,17 +13,17 @@
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"tag": "google",
|
"tag": "",
|
||||||
"address": "tls://dns.google",
|
"address": "",
|
||||||
"address_resolver": "local",
|
"address_resolver": "",
|
||||||
"address_strategy": "prefer_ipv4",
|
"address_strategy": "",
|
||||||
"strategy": "ipv4_only",
|
"strategy": "",
|
||||||
"detour": "direct"
|
"detour": "",
|
||||||
|
"client_subnet": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fields
|
### Fields
|
||||||
|
@ -80,10 +88,20 @@ Default domain strategy for resolving the domain names.
|
||||||
|
|
||||||
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
|
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
|
||||||
|
|
||||||
Take no effect if override by other settings.
|
Take no effect if overridden by other settings.
|
||||||
|
|
||||||
#### detour
|
#### detour
|
||||||
|
|
||||||
Tag of an outbound for connecting to the dns server.
|
Tag of an outbound for connecting to the dns server.
|
||||||
|
|
||||||
Default outbound will be used if empty.
|
Default outbound will be used if empty.
|
||||||
|
|
||||||
|
#### client_subnet
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.9.0"
|
||||||
|
|
||||||
|
Append a `edns0-subnet` OPT extra record with the specified IP address to every query by default.
|
||||||
|
|
||||||
|
Can be overrides by `rules.[].client_subnet`.
|
||||||
|
|
||||||
|
Will overrides `dns.client_subnet`.
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.9.0 中的更改"
|
||||||
|
|
||||||
|
:material-plus: [client_subnet](#client_subnet)
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -5,17 +13,17 @@
|
||||||
"dns": {
|
"dns": {
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
"tag": "google",
|
"tag": "",
|
||||||
"address": "tls://dns.google",
|
"address": "",
|
||||||
"address_resolver": "local",
|
"address_resolver": "",
|
||||||
"address_strategy": "prefer_ipv4",
|
"address_strategy": "",
|
||||||
"strategy": "ipv4_only",
|
"strategy": "",
|
||||||
"detour": "direct"
|
"detour": "",
|
||||||
|
"client_subnet": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 字段
|
### 字段
|
||||||
|
@ -87,3 +95,13 @@ DNS 服务器的地址。
|
||||||
用于连接到 DNS 服务器的出站的标签。
|
用于连接到 DNS 服务器的出站的标签。
|
||||||
|
|
||||||
如果为空,将使用默认出站。
|
如果为空,将使用默认出站。
|
||||||
|
|
||||||
|
#### client_subnet
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
||||||
|
默认情况下,将带有指定 IP 地址的 `edns0-subnet` OPT 附加记录附加到每个查询。
|
||||||
|
|
||||||
|
可以被 `rules.[].client_subnet` 覆盖。
|
||||||
|
|
||||||
|
将覆盖 `dns.client_subnet`。
|
||||||
|
|
|
@ -338,9 +338,7 @@ flowchart TB
|
||||||
|
|
||||||
=== ":material-dns: DNS rules (1.9.0+)"
|
=== ":material-dns: DNS rules (1.9.0+)"
|
||||||
|
|
||||||
!!! warning "DNS leaks"
|
=== ":material-shield-off: With DNS Leaks"
|
||||||
|
|
||||||
The new DNS feature allows you to more precisely bypass Chinese websites via **DNS leaks**. Do not use plain local DNS if using this method.
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -407,6 +405,65 @@ flowchart TB
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
=== ":material-security: Without DNS Leaks (1.9.0-alpha.2+)"
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns": {
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"tag": "google",
|
||||||
|
"address": "tls://8.8.8.8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "local",
|
||||||
|
"address": "https://223.5.5.5/dns-query",
|
||||||
|
"detour": "direct"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"outbound": "any",
|
||||||
|
"server": "local"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clash_mode": "Direct",
|
||||||
|
"server": "local"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"clash_mode": "Global",
|
||||||
|
"server": "google"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rule_set": "geosite-geolocation-cn",
|
||||||
|
"server": "local"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rule_set": "geoip-cn",
|
||||||
|
"server": "google",
|
||||||
|
"client_subnet": "114.114.114.114" // Any China client IP address
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"route": {
|
||||||
|
"rule_set": [
|
||||||
|
{
|
||||||
|
"type": "remote",
|
||||||
|
"tag": "geosite-geolocation-cn",
|
||||||
|
"format": "binary",
|
||||||
|
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-geolocation-cn.srs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "remote",
|
||||||
|
"tag": "geoip-cn",
|
||||||
|
"format": "binary",
|
||||||
|
"url": "https://raw.githubusercontent.com/SagerNet/sing-geoip/rule-set/geoip-cn.srs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
=== ":material-router-network: Route rules"
|
=== ":material-router-network: Route rules"
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
|
@ -9,9 +9,7 @@ import (
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
"github.com/sagernet/sing/common/task"
|
"github.com/sagernet/sing/common/task"
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
|
@ -25,9 +23,11 @@ type LocalDNSTransport interface {
|
||||||
|
|
||||||
func RegisterLocalDNSTransport(transport LocalDNSTransport) {
|
func RegisterLocalDNSTransport(transport LocalDNSTransport) {
|
||||||
if transport == nil {
|
if transport == nil {
|
||||||
dns.RegisterTransport([]string{"local"}, dns.CreateLocalTransport)
|
dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||||
|
return dns.NewLocalTransport(options), nil
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
dns.RegisterTransport([]string{"local"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
dns.RegisterTransport([]string{"local"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||||
return &platformLocalDNSTransport{
|
return &platformLocalDNSTransport{
|
||||||
iif: transport,
|
iif: transport,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -3,16 +3,12 @@
|
||||||
package include
|
package include
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
dns.RegisterTransport([]string{"dhcp"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
dns.RegisterTransport([]string{"dhcp"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||||
return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`)
|
return nil, E.New(`DHCP is not included in this build, rebuild with -tags with_dhcp`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,12 @@ import (
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/v2ray"
|
"github.com/sagernet/sing-box/transport/v2ray"
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
dns.RegisterTransport([]string{"quic", "h3"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
dns.RegisterTransport([]string{"quic", "h3"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||||
return nil, C.ErrQUICNotIncluded
|
return nil, C.ErrQUICNotIncluded
|
||||||
})
|
})
|
||||||
v2ray.RegisterQUICConstructor(
|
v2ray.RegisterQUICConstructor(
|
||||||
|
|
|
@ -19,6 +19,7 @@ type DNSServerOptions struct {
|
||||||
AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
|
AddressFallbackDelay Duration `json:"address_fallback_delay,omitempty"`
|
||||||
Strategy DomainStrategy `json:"strategy,omitempty"`
|
Strategy DomainStrategy `json:"strategy,omitempty"`
|
||||||
Detour string `json:"detour,omitempty"`
|
Detour string `json:"detour,omitempty"`
|
||||||
|
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DNSClientOptions struct {
|
type DNSClientOptions struct {
|
||||||
|
@ -26,6 +27,7 @@ type DNSClientOptions struct {
|
||||||
DisableCache bool `json:"disable_cache,omitempty"`
|
DisableCache bool `json:"disable_cache,omitempty"`
|
||||||
DisableExpire bool `json:"disable_expire,omitempty"`
|
DisableExpire bool `json:"disable_expire,omitempty"`
|
||||||
IndependentCache bool `json:"independent_cache,omitempty"`
|
IndependentCache bool `json:"independent_cache,omitempty"`
|
||||||
|
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DNSFakeIPOptions struct {
|
type DNSFakeIPOptions struct {
|
||||||
|
|
|
@ -100,6 +100,7 @@ type DefaultDNSRule struct {
|
||||||
Server string `json:"server,omitempty"`
|
Server string `json:"server,omitempty"`
|
||||||
DisableCache bool `json:"disable_cache,omitempty"`
|
DisableCache bool `json:"disable_cache,omitempty"`
|
||||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||||
|
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r DefaultDNSRule) IsValid() bool {
|
func (r DefaultDNSRule) IsValid() bool {
|
||||||
|
@ -108,6 +109,7 @@ func (r DefaultDNSRule) IsValid() bool {
|
||||||
defaultValue.Server = r.Server
|
defaultValue.Server = r.Server
|
||||||
defaultValue.DisableCache = r.DisableCache
|
defaultValue.DisableCache = r.DisableCache
|
||||||
defaultValue.RewriteTTL = r.RewriteTTL
|
defaultValue.RewriteTTL = r.RewriteTTL
|
||||||
|
defaultValue.ClientSubnet = r.ClientSubnet
|
||||||
return !reflect.DeepEqual(r, defaultValue)
|
return !reflect.DeepEqual(r, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +120,7 @@ type LogicalDNSRule struct {
|
||||||
Server string `json:"server,omitempty"`
|
Server string `json:"server,omitempty"`
|
||||||
DisableCache bool `json:"disable_cache,omitempty"`
|
DisableCache bool `json:"disable_cache,omitempty"`
|
||||||
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
|
||||||
|
ClientSubnet *ListenAddress `json:"client_subnet,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r LogicalDNSRule) IsValid() bool {
|
func (r LogicalDNSRule) IsValid() bool {
|
||||||
|
|
|
@ -225,7 +225,20 @@ func NewRouter(
|
||||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transport, err := dns.CreateTransport(tag, ctx, logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")), detour, server.Address)
|
var clientSubnet netip.Addr
|
||||||
|
if server.ClientSubnet != nil {
|
||||||
|
clientSubnet = server.ClientSubnet.Build()
|
||||||
|
} else if dnsOptions.ClientSubnet != nil {
|
||||||
|
clientSubnet = dnsOptions.ClientSubnet.Build()
|
||||||
|
}
|
||||||
|
transport, err := dns.CreateTransport(dns.TransportOptions{
|
||||||
|
Context: ctx,
|
||||||
|
Logger: logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")),
|
||||||
|
Name: tag,
|
||||||
|
Dialer: detour,
|
||||||
|
Address: server.Address,
|
||||||
|
ClientSubnet: clientSubnet,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
||||||
}
|
}
|
||||||
|
@ -265,7 +278,12 @@ func NewRouter(
|
||||||
}
|
}
|
||||||
if defaultTransport == nil {
|
if defaultTransport == nil {
|
||||||
if len(transports) == 0 {
|
if len(transports) == 0 {
|
||||||
transports = append(transports, dns.NewLocalTransport("local", N.SystemDialer))
|
transports = append(transports, common.Must1(dns.CreateTransport(dns.TransportOptions{
|
||||||
|
Context: ctx,
|
||||||
|
Name: "local",
|
||||||
|
Address: "local",
|
||||||
|
Dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{})),
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
defaultTransport = transports[0]
|
defaultTransport = transports[0]
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,9 @@ func (r *Router) matchDNS(ctx context.Context, allowFakeIP bool, index int) (con
|
||||||
if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil {
|
if rewriteTTL := rule.RewriteTTL(); rewriteTTL != nil {
|
||||||
ctx = dns.ContextWithRewriteTTL(ctx, *rewriteTTL)
|
ctx = dns.ContextWithRewriteTTL(ctx, *rewriteTTL)
|
||||||
}
|
}
|
||||||
|
if clientSubnet := rule.ClientSubnet(); clientSubnet != nil {
|
||||||
|
ctx = dns.ContextWithClientSubnet(ctx, *clientSubnet)
|
||||||
|
}
|
||||||
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
if domainStrategy, dsLoaded := r.transportDomainStrategy[transport]; dsLoaded {
|
||||||
return ctx, transport, domainStrategy, rule, ruleIndex
|
return ctx, transport, domainStrategy, rule, ruleIndex
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package route
|
package route
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -38,6 +40,7 @@ type DefaultDNSRule struct {
|
||||||
abstractDefaultRule
|
abstractDefaultRule
|
||||||
disableCache bool
|
disableCache bool
|
||||||
rewriteTTL *uint32
|
rewriteTTL *uint32
|
||||||
|
clientSubnet *netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
|
func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options option.DefaultDNSRule) (*DefaultDNSRule, error) {
|
||||||
|
@ -48,6 +51,7 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||||
},
|
},
|
||||||
disableCache: options.DisableCache,
|
disableCache: options.DisableCache,
|
||||||
rewriteTTL: options.RewriteTTL,
|
rewriteTTL: options.RewriteTTL,
|
||||||
|
clientSubnet: (*netip.Addr)(options.ClientSubnet),
|
||||||
}
|
}
|
||||||
if len(options.Inbound) > 0 {
|
if len(options.Inbound) > 0 {
|
||||||
item := NewInboundRule(options.Inbound)
|
item := NewInboundRule(options.Inbound)
|
||||||
|
@ -230,6 +234,10 @@ func (r *DefaultDNSRule) RewriteTTL() *uint32 {
|
||||||
return r.rewriteTTL
|
return r.rewriteTTL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *DefaultDNSRule) ClientSubnet() *netip.Addr {
|
||||||
|
return r.clientSubnet
|
||||||
|
}
|
||||||
|
|
||||||
func (r *DefaultDNSRule) WithAddressLimit() bool {
|
func (r *DefaultDNSRule) WithAddressLimit() bool {
|
||||||
if len(r.destinationIPCIDRItems) > 0 {
|
if len(r.destinationIPCIDRItems) > 0 {
|
||||||
return true
|
return true
|
||||||
|
@ -264,6 +272,7 @@ type LogicalDNSRule struct {
|
||||||
abstractLogicalRule
|
abstractLogicalRule
|
||||||
disableCache bool
|
disableCache bool
|
||||||
rewriteTTL *uint32
|
rewriteTTL *uint32
|
||||||
|
clientSubnet *netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
|
func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options option.LogicalDNSRule) (*LogicalDNSRule, error) {
|
||||||
|
@ -275,6 +284,7 @@ func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options
|
||||||
},
|
},
|
||||||
disableCache: options.DisableCache,
|
disableCache: options.DisableCache,
|
||||||
rewriteTTL: options.RewriteTTL,
|
rewriteTTL: options.RewriteTTL,
|
||||||
|
clientSubnet: (*netip.Addr)(options.ClientSubnet),
|
||||||
}
|
}
|
||||||
switch options.Mode {
|
switch options.Mode {
|
||||||
case C.LogicalTypeAnd:
|
case C.LogicalTypeAnd:
|
||||||
|
@ -302,6 +312,10 @@ func (r *LogicalDNSRule) RewriteTTL() *uint32 {
|
||||||
return r.rewriteTTL
|
return r.rewriteTTL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *LogicalDNSRule) ClientSubnet() *netip.Addr {
|
||||||
|
return r.clientSubnet
|
||||||
|
}
|
||||||
|
|
||||||
func (r *LogicalDNSRule) WithAddressLimit() bool {
|
func (r *LogicalDNSRule) WithAddressLimit() bool {
|
||||||
for _, rawRule := range r.rules {
|
for _, rawRule := range r.rules {
|
||||||
switch rule := rawRule.(type) {
|
switch rule := rawRule.(type) {
|
||||||
|
|
|
@ -21,9 +21,6 @@ import (
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
"github.com/sagernet/sing/common/control"
|
"github.com/sagernet/sing/common/control"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
"github.com/sagernet/sing/common/task"
|
"github.com/sagernet/sing/common/task"
|
||||||
"github.com/sagernet/sing/common/x/list"
|
"github.com/sagernet/sing/common/x/list"
|
||||||
|
|
||||||
|
@ -32,14 +29,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
dns.RegisterTransport([]string{"dhcp"}, NewTransport)
|
dns.RegisterTransport([]string{"dhcp"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||||
|
return NewTransport(options)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transport struct {
|
type Transport struct {
|
||||||
name string
|
options dns.TransportOptions
|
||||||
ctx context.Context
|
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
logger logger.Logger
|
|
||||||
interfaceName string
|
interfaceName string
|
||||||
autoInterface bool
|
autoInterface bool
|
||||||
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
|
interfaceCallback *list.Element[tun.DefaultInterfaceUpdateCallback]
|
||||||
|
@ -48,23 +45,20 @@ type Transport struct {
|
||||||
updatedAt time.Time
|
updatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransport(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
func NewTransport(options dns.TransportOptions) (*Transport, error) {
|
||||||
linkURL, err := url.Parse(link)
|
linkURL, err := url.Parse(options.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if linkURL.Host == "" {
|
if linkURL.Host == "" {
|
||||||
return nil, E.New("missing interface name for DHCP")
|
return nil, E.New("missing interface name for DHCP")
|
||||||
}
|
}
|
||||||
router := adapter.RouterFromContext(ctx)
|
router := adapter.RouterFromContext(options.Context)
|
||||||
if router == nil {
|
if router == nil {
|
||||||
return nil, E.New("missing router in context")
|
return nil, E.New("missing router in context")
|
||||||
}
|
}
|
||||||
transport := &Transport{
|
transport := &Transport{
|
||||||
name: name,
|
|
||||||
ctx: ctx,
|
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
|
||||||
interfaceName: linkURL.Host,
|
interfaceName: linkURL.Host,
|
||||||
autoInterface: linkURL.Host == "auto",
|
autoInterface: linkURL.Host == "auto",
|
||||||
}
|
}
|
||||||
|
@ -72,7 +66,7 @@ func NewTransport(name string, ctx context.Context, logger logger.ContextLogger,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) Name() string {
|
func (t *Transport) Name() string {
|
||||||
return t.name
|
return t.options.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) Start() error {
|
func (t *Transport) Start() error {
|
||||||
|
@ -158,8 +152,8 @@ func (t *Transport) updateServers() error {
|
||||||
return E.Cause(err, "dhcp: prepare interface")
|
return E.Cause(err, "dhcp: prepare interface")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.logger.Info("dhcp: query DNS servers on ", iface.Name)
|
t.options.Logger.Info("dhcp: query DNS servers on ", iface.Name)
|
||||||
fetchCtx, cancel := context.WithTimeout(t.ctx, C.DHCPTimeout)
|
fetchCtx, cancel := context.WithTimeout(t.options.Context, C.DHCPTimeout)
|
||||||
err = t.fetchServers0(fetchCtx, iface)
|
err = t.fetchServers0(fetchCtx, iface)
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -175,7 +169,7 @@ func (t *Transport) updateServers() error {
|
||||||
func (t *Transport) interfaceUpdated(int) {
|
func (t *Transport) interfaceUpdated(int) {
|
||||||
err := t.updateServers()
|
err := t.updateServers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.logger.Error("update servers: ", err)
|
t.options.Logger.Error("update servers: ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +181,7 @@ func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) err
|
||||||
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||||
listenAddr = "255.255.255.255:68"
|
listenAddr = "255.255.255.255:68"
|
||||||
}
|
}
|
||||||
packetConn, err := listener.ListenPacket(t.ctx, "udp4", listenAddr)
|
packetConn, err := listener.ListenPacket(t.options.Context, "udp4", listenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -225,17 +219,17 @@ func (t *Transport) fetchServersResponse(iface *net.Interface, packetConn net.Pa
|
||||||
|
|
||||||
dhcpPacket, err := dhcpv4.FromBytes(buffer.Bytes())
|
dhcpPacket, err := dhcpv4.FromBytes(buffer.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.logger.Trace("dhcp: parse DHCP response: ", err)
|
t.options.Logger.Trace("dhcp: parse DHCP response: ", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if dhcpPacket.MessageType() != dhcpv4.MessageTypeOffer {
|
if dhcpPacket.MessageType() != dhcpv4.MessageTypeOffer {
|
||||||
t.logger.Trace("dhcp: expected OFFER response, but got ", dhcpPacket.MessageType())
|
t.options.Logger.Trace("dhcp: expected OFFER response, but got ", dhcpPacket.MessageType())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if dhcpPacket.TransactionID != transactionID {
|
if dhcpPacket.TransactionID != transactionID {
|
||||||
t.logger.Trace("dhcp: expected transaction ID ", transactionID, ", but got ", dhcpPacket.TransactionID)
|
t.options.Logger.Trace("dhcp: expected transaction ID ", transactionID, ", but got ", dhcpPacket.TransactionID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,20 +249,22 @@ func (t *Transport) fetchServersResponse(iface *net.Interface, packetConn net.Pa
|
||||||
|
|
||||||
func (t *Transport) recreateServers(iface *net.Interface, serverAddrs []netip.Addr) error {
|
func (t *Transport) recreateServers(iface *net.Interface, serverAddrs []netip.Addr) error {
|
||||||
if len(serverAddrs) > 0 {
|
if len(serverAddrs) > 0 {
|
||||||
t.logger.Info("dhcp: updated DNS servers from ", iface.Name, ": [", strings.Join(common.Map(serverAddrs, func(it netip.Addr) string {
|
t.options.Logger.Info("dhcp: updated DNS servers from ", iface.Name, ": [", strings.Join(common.Map(serverAddrs, func(it netip.Addr) string {
|
||||||
return it.String()
|
return it.String()
|
||||||
}), ","), "]")
|
}), ","), "]")
|
||||||
}
|
}
|
||||||
|
|
||||||
serverDialer := common.Must1(dialer.NewDefault(t.router, option.DialerOptions{
|
serverDialer := common.Must1(dialer.NewDefault(t.router, option.DialerOptions{
|
||||||
BindInterface: iface.Name,
|
BindInterface: iface.Name,
|
||||||
UDPFragmentDefault: true,
|
UDPFragmentDefault: true,
|
||||||
}))
|
}))
|
||||||
var transports []dns.Transport
|
var transports []dns.Transport
|
||||||
for _, serverAddr := range serverAddrs {
|
for _, serverAddr := range serverAddrs {
|
||||||
serverTransport, err := dns.NewUDPTransport(t.name, t.ctx, serverDialer, M.Socksaddr{Addr: serverAddr, Port: 53})
|
newOptions := t.options
|
||||||
|
newOptions.Address = serverAddr.String()
|
||||||
|
newOptions.Dialer = serverDialer
|
||||||
|
serverTransport, err := dns.NewUDPTransport(newOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return E.Cause(err, "create UDP transport from DHCP result: ", serverAddr)
|
||||||
}
|
}
|
||||||
transports = append(transports, serverTransport)
|
transports = append(transports, serverTransport)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
mDNS "github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
@ -20,7 +19,9 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
dns.RegisterTransport([]string{"fakeip"}, NewTransport)
|
dns.RegisterTransport([]string{"fakeip"}, func(options dns.TransportOptions) (dns.Transport, error) {
|
||||||
|
return NewTransport(options)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transport struct {
|
type Transport struct {
|
||||||
|
@ -30,15 +31,15 @@ type Transport struct {
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransport(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
|
func NewTransport(options dns.TransportOptions) (*Transport, error) {
|
||||||
router := adapter.RouterFromContext(ctx)
|
router := adapter.RouterFromContext(options.Context)
|
||||||
if router == nil {
|
if router == nil {
|
||||||
return nil, E.New("missing router in context")
|
return nil, E.New("missing router in context")
|
||||||
}
|
}
|
||||||
return &Transport{
|
return &Transport{
|
||||||
name: name,
|
name: options.Name,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: options.Logger,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue