mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 00:21:30 +00:00
Add rejected DNS response cache support
This commit is contained in:
parent
f24a2aed7d
commit
93ae3f7a1e
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
|
"github.com/sagernet/sing-dns"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
)
|
)
|
||||||
|
@ -30,6 +31,9 @@ type CacheFile interface {
|
||||||
StoreFakeIP() bool
|
StoreFakeIP() bool
|
||||||
FakeIPStorage
|
FakeIPStorage
|
||||||
|
|
||||||
|
StoreRDRC() bool
|
||||||
|
dns.RDRCStore
|
||||||
|
|
||||||
LoadMode() string
|
LoadMode() string
|
||||||
StoreMode(mode string) error
|
StoreMode(mode string) error
|
||||||
LoadSelected(group string) string
|
LoadSelected(group string) string
|
||||||
|
|
|
@ -339,10 +339,14 @@ Will overrides `dns.client_subnet` and `servers.[].client_subnet`.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
!!! note ""
|
!!! info ""
|
||||||
|
|
||||||
`ip_cidr` items in included rule sets also takes effect as an address filtering field.
|
`ip_cidr` items in included rule sets also takes effect as an address filtering field.
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
Enable `experimental.cache_file.store_rdrc` to cache results.
|
||||||
|
|
||||||
#### geoip
|
#### geoip
|
||||||
|
|
||||||
!!! question "Since sing-box 1.9.0"
|
!!! question "Since sing-box 1.9.0"
|
||||||
|
|
|
@ -337,10 +337,14 @@ DNS 查询类型。值可以为整数或者类型名称字符串。
|
||||||
|
|
||||||
仅对IP地址请求生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
|
仅对IP地址请求生效。 当查询结果与地址筛选规则项不匹配时,将跳过当前规则。
|
||||||
|
|
||||||
!!! note ""
|
!!! info ""
|
||||||
|
|
||||||
引用的规则集中的 `ip_cidr` 项也作为地址筛选字段生效。
|
引用的规则集中的 `ip_cidr` 项也作为地址筛选字段生效。
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
启用 `experimental.cache_file.store_rdrc` 以缓存结果。
|
||||||
|
|
||||||
#### geoip
|
#### geoip
|
||||||
|
|
||||||
!!! question "自 sing-box 1.9.0 起"
|
!!! question "自 sing-box 1.9.0 起"
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
!!! question "Since sing-box 1.8.0"
|
!!! question "Since sing-box 1.8.0"
|
||||||
|
|
||||||
|
!!! quote "Changes in sing-box 1.9.0"
|
||||||
|
|
||||||
|
:material-plus: [store_rdrc](#store_rdrc)
|
||||||
|
:material-plus: [rdrc_timeout](#rdrc_timeout)
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -7,7 +16,9 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"path": "",
|
"path": "",
|
||||||
"cache_id": "",
|
"cache_id": "",
|
||||||
"store_fakeip": false
|
"store_fakeip": false,
|
||||||
|
"store_rdrc": false,
|
||||||
|
"rdrc_timeout": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -25,6 +36,23 @@ Path to the cache file.
|
||||||
|
|
||||||
#### cache_id
|
#### cache_id
|
||||||
|
|
||||||
Identifier in cache file.
|
Identifier in the cache file
|
||||||
|
|
||||||
If not empty, configuration specified data will use a separate store keyed by it.
|
If not empty, configuration specified data will use a separate store keyed by it.
|
||||||
|
|
||||||
|
#### store_fakeip
|
||||||
|
|
||||||
|
Store fakeip in the cache file
|
||||||
|
|
||||||
|
#### store_rdrc
|
||||||
|
|
||||||
|
Store rejected DNS response cache in the cache file
|
||||||
|
|
||||||
|
The check results of [Address filter DNS rule items](/configuration/dns/rule/#address-filter-fields)
|
||||||
|
will be cached until expiration.
|
||||||
|
|
||||||
|
#### rdrc_timeout
|
||||||
|
|
||||||
|
Timeout of rejected DNS response cache.
|
||||||
|
|
||||||
|
`7d` is used by default.
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
|
---
|
||||||
|
icon: material/new-box
|
||||||
|
---
|
||||||
|
|
||||||
!!! question "自 sing-box 1.8.0 起"
|
!!! question "自 sing-box 1.8.0 起"
|
||||||
|
|
||||||
|
!!! quote "sing-box 1.9.0 中的更改"
|
||||||
|
|
||||||
|
:material-plus: [store_rdrc](#store_rdrc)
|
||||||
|
:material-plus: [rdrc_timeout](#rdrc_timeout)
|
||||||
|
|
||||||
### 结构
|
### 结构
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -7,7 +16,9 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"path": "",
|
"path": "",
|
||||||
"cache_id": "",
|
"cache_id": "",
|
||||||
"store_fakeip": false
|
"store_fakeip": false,
|
||||||
|
"store_rdrc": false,
|
||||||
|
"rdrc_timeout": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -26,3 +37,19 @@
|
||||||
缓存文件中的标识符。
|
缓存文件中的标识符。
|
||||||
|
|
||||||
如果不为空,配置特定的数据将使用由其键控的单独存储。
|
如果不为空,配置特定的数据将使用由其键控的单独存储。
|
||||||
|
|
||||||
|
#### store_fakeip
|
||||||
|
|
||||||
|
将 fakeip 存储在缓存文件中。
|
||||||
|
|
||||||
|
#### store_rdrc
|
||||||
|
|
||||||
|
将拒绝的 DNS 响应缓存存储在缓存文件中。
|
||||||
|
|
||||||
|
[地址筛选 DNS 规则项](/zh/configuration/dns/rule/#_3) 的检查结果将被缓存至过期。
|
||||||
|
|
||||||
|
#### rdrc_timeout
|
||||||
|
|
||||||
|
拒绝的 DNS 响应缓存超时。
|
||||||
|
|
||||||
|
默认使用 `7d`。
|
||||||
|
|
|
@ -29,6 +29,7 @@ var (
|
||||||
string(bucketExpand),
|
string(bucketExpand),
|
||||||
string(bucketMode),
|
string(bucketMode),
|
||||||
string(bucketRuleSet),
|
string(bucketRuleSet),
|
||||||
|
string(bucketRDRC),
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheIDDefault = []byte("default")
|
cacheIDDefault = []byte("default")
|
||||||
|
@ -41,13 +42,21 @@ type CacheFile struct {
|
||||||
path string
|
path string
|
||||||
cacheID []byte
|
cacheID []byte
|
||||||
storeFakeIP bool
|
storeFakeIP bool
|
||||||
|
storeRDRC bool
|
||||||
|
rdrcTimeout time.Duration
|
||||||
DB *bbolt.DB
|
DB *bbolt.DB
|
||||||
saveAccess sync.RWMutex
|
saveMetadataTimer *time.Timer
|
||||||
|
saveFakeIPAccess sync.RWMutex
|
||||||
saveDomain map[netip.Addr]string
|
saveDomain map[netip.Addr]string
|
||||||
saveAddress4 map[string]netip.Addr
|
saveAddress4 map[string]netip.Addr
|
||||||
saveAddress6 map[string]netip.Addr
|
saveAddress6 map[string]netip.Addr
|
||||||
saveMetadataTimer *time.Timer
|
saveRDRCAccess sync.RWMutex
|
||||||
|
saveRDRC map[saveRDRCCacheKey]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type saveRDRCCacheKey struct {
|
||||||
|
TransportName string
|
||||||
|
QuestionName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
||||||
|
@ -61,14 +70,25 @@ func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
||||||
if options.CacheID != "" {
|
if options.CacheID != "" {
|
||||||
cacheIDBytes = append([]byte{0}, []byte(options.CacheID)...)
|
cacheIDBytes = append([]byte{0}, []byte(options.CacheID)...)
|
||||||
}
|
}
|
||||||
|
var rdrcTimeout time.Duration
|
||||||
|
if options.StoreRDRC {
|
||||||
|
if options.RDRCTimeout > 0 {
|
||||||
|
rdrcTimeout = time.Duration(options.RDRCTimeout)
|
||||||
|
} else {
|
||||||
|
rdrcTimeout = 7 * 24 * time.Hour
|
||||||
|
}
|
||||||
|
}
|
||||||
return &CacheFile{
|
return &CacheFile{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
path: filemanager.BasePath(ctx, path),
|
path: filemanager.BasePath(ctx, path),
|
||||||
cacheID: cacheIDBytes,
|
cacheID: cacheIDBytes,
|
||||||
storeFakeIP: options.StoreFakeIP,
|
storeFakeIP: options.StoreFakeIP,
|
||||||
|
storeRDRC: options.StoreRDRC,
|
||||||
|
rdrcTimeout: rdrcTimeout,
|
||||||
saveDomain: make(map[netip.Addr]string),
|
saveDomain: make(map[netip.Addr]string),
|
||||||
saveAddress4: make(map[string]netip.Addr),
|
saveAddress4: make(map[string]netip.Addr),
|
||||||
saveAddress6: make(map[string]netip.Addr),
|
saveAddress6: make(map[string]netip.Addr),
|
||||||
|
saveRDRC: make(map[saveRDRCCacheKey]bool),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) {
|
func (c *CacheFile) FakeIPStoreAsync(address netip.Addr, domain string, logger logger.Logger) {
|
||||||
c.saveAccess.Lock()
|
c.saveFakeIPAccess.Lock()
|
||||||
if oldDomain, loaded := c.saveDomain[address]; loaded {
|
if oldDomain, loaded := c.saveDomain[address]; loaded {
|
||||||
if address.Is4() {
|
if address.Is4() {
|
||||||
delete(c.saveAddress4, oldDomain)
|
delete(c.saveAddress4, oldDomain)
|
||||||
|
@ -111,27 +111,27 @@ func (c *CacheFile) FakeIPStoreAsync(address netip.Addr, domain string, logger l
|
||||||
} else {
|
} else {
|
||||||
c.saveAddress6[domain] = address
|
c.saveAddress6[domain] = address
|
||||||
}
|
}
|
||||||
c.saveAccess.Unlock()
|
c.saveFakeIPAccess.Unlock()
|
||||||
go func() {
|
go func() {
|
||||||
err := c.FakeIPStore(address, domain)
|
err := c.FakeIPStore(address, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn("save FakeIP address pair: ", err)
|
logger.Warn("save FakeIP cache: ", err)
|
||||||
}
|
}
|
||||||
c.saveAccess.Lock()
|
c.saveFakeIPAccess.Lock()
|
||||||
delete(c.saveDomain, address)
|
delete(c.saveDomain, address)
|
||||||
if address.Is4() {
|
if address.Is4() {
|
||||||
delete(c.saveAddress4, domain)
|
delete(c.saveAddress4, domain)
|
||||||
} else {
|
} else {
|
||||||
delete(c.saveAddress6, domain)
|
delete(c.saveAddress6, domain)
|
||||||
}
|
}
|
||||||
c.saveAccess.Unlock()
|
c.saveFakeIPAccess.Unlock()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
|
func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
|
||||||
c.saveAccess.RLock()
|
c.saveFakeIPAccess.RLock()
|
||||||
cachedDomain, cached := c.saveDomain[address]
|
cachedDomain, cached := c.saveDomain[address]
|
||||||
c.saveAccess.RUnlock()
|
c.saveFakeIPAccess.RUnlock()
|
||||||
if cached {
|
if cached {
|
||||||
return cachedDomain, true
|
return cachedDomain, true
|
||||||
}
|
}
|
||||||
|
@ -152,13 +152,13 @@ func (c *CacheFile) FakeIPLoadDomain(domain string, isIPv6 bool) (netip.Addr, bo
|
||||||
cachedAddress netip.Addr
|
cachedAddress netip.Addr
|
||||||
cached bool
|
cached bool
|
||||||
)
|
)
|
||||||
c.saveAccess.RLock()
|
c.saveFakeIPAccess.RLock()
|
||||||
if !isIPv6 {
|
if !isIPv6 {
|
||||||
cachedAddress, cached = c.saveAddress4[domain]
|
cachedAddress, cached = c.saveAddress4[domain]
|
||||||
} else {
|
} else {
|
||||||
cachedAddress, cached = c.saveAddress6[domain]
|
cachedAddress, cached = c.saveAddress6[domain]
|
||||||
}
|
}
|
||||||
c.saveAccess.RUnlock()
|
c.saveFakeIPAccess.RUnlock()
|
||||||
if cached {
|
if cached {
|
||||||
return cachedAddress, true
|
return cachedAddress, true
|
||||||
}
|
}
|
||||||
|
|
101
experimental/cachefile/rdrc.go
Normal file
101
experimental/cachefile/rdrc.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package cachefile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/bbolt"
|
||||||
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bucketRDRC = []byte("rdrc")
|
||||||
|
|
||||||
|
func (c *CacheFile) StoreRDRC() bool {
|
||||||
|
return c.storeRDRC
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) RDRCTimeout() time.Duration {
|
||||||
|
return c.rdrcTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) LoadRDRC(transportName string, qName string) (rejected bool) {
|
||||||
|
c.saveRDRCAccess.RLock()
|
||||||
|
rejected, cached := c.saveRDRC[saveRDRCCacheKey{transportName, qName}]
|
||||||
|
c.saveRDRCAccess.RUnlock()
|
||||||
|
if cached {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var deleteCache bool
|
||||||
|
err := c.DB.View(func(tx *bbolt.Tx) error {
|
||||||
|
bucket := c.bucket(tx, bucketRDRC)
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bucket = bucket.Bucket([]byte(transportName))
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
content := bucket.Get([]byte(qName))
|
||||||
|
if content == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
expiresAt := time.Unix(int64(binary.BigEndian.Uint64(content)), 0)
|
||||||
|
if time.Now().After(expiresAt) {
|
||||||
|
deleteCache = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rejected = true
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if deleteCache {
|
||||||
|
c.DB.Update(func(tx *bbolt.Tx) error {
|
||||||
|
bucket := c.bucket(tx, bucketRDRC)
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bucket = bucket.Bucket([]byte(transportName))
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return bucket.Delete([]byte(qName))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) SaveRDRC(transportName string, qName string) error {
|
||||||
|
return c.DB.Batch(func(tx *bbolt.Tx) error {
|
||||||
|
bucket, err := c.createBucket(tx, bucketRDRC)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bucket, err = bucket.CreateBucketIfNotExists([]byte(transportName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
expiresAt := buf.Get(8)
|
||||||
|
defer buf.Put(expiresAt)
|
||||||
|
binary.BigEndian.PutUint64(expiresAt, uint64(time.Now().Add(c.rdrcTimeout).Unix()))
|
||||||
|
return bucket.Put([]byte(qName), expiresAt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) SaveRDRCAsync(transportName string, qName string, logger logger.Logger) {
|
||||||
|
saveKey := saveRDRCCacheKey{transportName, qName}
|
||||||
|
c.saveRDRCAccess.Lock()
|
||||||
|
c.saveRDRC[saveKey] = true
|
||||||
|
c.saveRDRCAccess.Unlock()
|
||||||
|
go func() {
|
||||||
|
err := c.SaveRDRC(transportName, qName)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("save RDRC: ", err)
|
||||||
|
}
|
||||||
|
c.saveRDRCAccess.Lock()
|
||||||
|
delete(c.saveRDRC, saveKey)
|
||||||
|
c.saveRDRCAccess.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
|
@ -12,6 +12,8 @@ type CacheFileOptions struct {
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
CacheID string `json:"cache_id,omitempty"`
|
CacheID string `json:"cache_id,omitempty"`
|
||||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||||
|
StoreRDRC bool `json:"store_rdrc,omitempty"`
|
||||||
|
RDRCTimeout Duration `json:"rdrc_timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClashAPIOptions struct {
|
type ClashAPIOptions struct {
|
||||||
|
|
|
@ -139,6 +139,16 @@ func NewRouter(
|
||||||
DisableCache: dnsOptions.DNSClientOptions.DisableCache,
|
DisableCache: dnsOptions.DNSClientOptions.DisableCache,
|
||||||
DisableExpire: dnsOptions.DNSClientOptions.DisableExpire,
|
DisableExpire: dnsOptions.DNSClientOptions.DisableExpire,
|
||||||
IndependentCache: dnsOptions.DNSClientOptions.IndependentCache,
|
IndependentCache: dnsOptions.DNSClientOptions.IndependentCache,
|
||||||
|
RDRC: func() dns.RDRCStore {
|
||||||
|
cacheFile := service.FromContext[adapter.CacheFile](ctx)
|
||||||
|
if cacheFile == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !cacheFile.StoreRDRC() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return cacheFile
|
||||||
|
},
|
||||||
Logger: router.dnsLogger,
|
Logger: router.dnsLogger,
|
||||||
})
|
})
|
||||||
for i, ruleOptions := range options.Rules {
|
for i, ruleOptions := range options.Rules {
|
||||||
|
@ -625,6 +635,11 @@ func (r *Router) Start() error {
|
||||||
return E.Cause(err, "initialize rule[", i, "]")
|
return E.Cause(err, "initialize rule[", i, "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monitor.Start("initialize DNS client")
|
||||||
|
r.dnsClient.Start()
|
||||||
|
monitor.Finish()
|
||||||
|
|
||||||
for i, rule := range r.dnsRules {
|
for i, rule := range r.dnsRules {
|
||||||
monitor.Start("initialize DNS rule[", i, "]")
|
monitor.Start("initialize DNS rule[", i, "]")
|
||||||
err := rule.Start()
|
err := rule.Start()
|
||||||
|
|
|
@ -139,7 +139,9 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||||
}
|
}
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, dns.ErrResponseRejected) {
|
if errors.Is(err, dns.ErrResponseRejectedCached) {
|
||||||
|
r.dnsLogger.DebugContext(ctx, E.Cause(err, "response rejected for ", formatQuestion(message.Question[0].String())), " (cached)")
|
||||||
|
} else if errors.Is(err, dns.ErrResponseRejected) {
|
||||||
r.dnsLogger.DebugContext(ctx, E.Cause(err, "response rejected for ", formatQuestion(message.Question[0].String())))
|
r.dnsLogger.DebugContext(ctx, E.Cause(err, "response rejected for ", formatQuestion(message.Question[0].String())))
|
||||||
} else if len(message.Question) > 0 {
|
} else if len(message.Question) > 0 {
|
||||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String())))
|
||||||
|
@ -166,6 +168,15 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
|
func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
|
||||||
|
var (
|
||||||
|
responseAddrs []netip.Addr
|
||||||
|
cached bool
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
responseAddrs, cached = r.dnsClient.LookupCache(ctx, domain, strategy)
|
||||||
|
if cached {
|
||||||
|
return responseAddrs, nil
|
||||||
|
}
|
||||||
r.dnsLogger.DebugContext(ctx, "lookup domain ", domain)
|
r.dnsLogger.DebugContext(ctx, "lookup domain ", domain)
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
ctx, metadata := adapter.AppendContext(ctx)
|
||||||
metadata.Domain = domain
|
metadata.Domain = domain
|
||||||
|
@ -174,8 +185,6 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
||||||
transportStrategy dns.DomainStrategy
|
transportStrategy dns.DomainStrategy
|
||||||
rule adapter.DNSRule
|
rule adapter.DNSRule
|
||||||
ruleIndex int
|
ruleIndex int
|
||||||
resultAddrs []netip.Addr
|
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
ruleIndex = -1
|
ruleIndex = -1
|
||||||
for {
|
for {
|
||||||
|
@ -193,22 +202,24 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
||||||
dnsCtx, cancel = context.WithTimeout(dnsCtx, C.DNSTimeout)
|
dnsCtx, cancel = context.WithTimeout(dnsCtx, C.DNSTimeout)
|
||||||
if rule != nil && rule.WithAddressLimit() {
|
if rule != nil && rule.WithAddressLimit() {
|
||||||
addressLimit = true
|
addressLimit = true
|
||||||
resultAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, strategy, func(responseAddrs []netip.Addr) bool {
|
responseAddrs, err = r.dnsClient.LookupWithResponseCheck(dnsCtx, transport, domain, strategy, func(responseAddrs []netip.Addr) bool {
|
||||||
metadata.DestinationAddresses = responseAddrs
|
metadata.DestinationAddresses = responseAddrs
|
||||||
return rule.MatchAddressLimit(metadata)
|
return rule.MatchAddressLimit(metadata)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
addressLimit = false
|
addressLimit = false
|
||||||
resultAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, strategy)
|
responseAddrs, err = r.dnsClient.Lookup(dnsCtx, transport, domain, strategy)
|
||||||
}
|
}
|
||||||
cancel()
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, dns.ErrResponseRejected) {
|
if errors.Is(err, dns.ErrResponseRejectedCached) {
|
||||||
|
r.dnsLogger.DebugContext(ctx, "response rejected for ", domain, " (cached)")
|
||||||
|
} else if errors.Is(err, dns.ErrResponseRejected) {
|
||||||
r.dnsLogger.DebugContext(ctx, "response rejected for ", domain)
|
r.dnsLogger.DebugContext(ctx, "response rejected for ", domain)
|
||||||
} else {
|
} else {
|
||||||
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
|
r.dnsLogger.ErrorContext(ctx, E.Cause(err, "lookup failed for ", domain))
|
||||||
}
|
}
|
||||||
} else if len(resultAddrs) == 0 {
|
} else if len(responseAddrs) == 0 {
|
||||||
r.dnsLogger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result")
|
r.dnsLogger.ErrorContext(ctx, "lookup failed for ", domain, ": empty result")
|
||||||
err = dns.RCodeNameError
|
err = dns.RCodeNameError
|
||||||
}
|
}
|
||||||
|
@ -216,10 +227,10 @@ func (r *Router) Lookup(ctx context.Context, domain string, strategy dns.DomainS
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(resultAddrs) > 0 {
|
if len(responseAddrs) > 0 {
|
||||||
r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(resultAddrs), " "))
|
r.dnsLogger.InfoContext(ctx, "lookup succeed for ", domain, ": ", strings.Join(F.MapToString(responseAddrs), " "))
|
||||||
}
|
}
|
||||||
return resultAddrs, err
|
return responseAddrs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) {
|
func (r *Router) LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error) {
|
||||||
|
|
|
@ -58,6 +58,7 @@ func NewTransport(options dns.TransportOptions) (*Transport, error) {
|
||||||
return nil, E.New("missing router in context")
|
return nil, E.New("missing router in context")
|
||||||
}
|
}
|
||||||
transport := &Transport{
|
transport := &Transport{
|
||||||
|
options: options,
|
||||||
router: router,
|
router: router,
|
||||||
interfaceName: linkURL.Host,
|
interfaceName: linkURL.Host,
|
||||||
autoInterface: linkURL.Host == "auto",
|
autoInterface: linkURL.Host == "auto",
|
||||||
|
|
Loading…
Reference in a new issue