From 52b776b561164bad6406af3df930a7769aee397f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Thu, 23 Mar 2023 19:08:48 +0800 Subject: [PATCH] Add dns reverse mapping --- docs/configuration/dns/index.md | 11 ++++++++-- docs/configuration/dns/index.zh.md | 11 ++++++++-- option/dns.go | 7 ++++--- route/router.go | 14 +++++++++++++ route/router_dns.go | 32 ++++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 7 deletions(-) diff --git a/docs/configuration/dns/index.md b/docs/configuration/dns/index.md index f6889dd7..4f85942f 100644 --- a/docs/configuration/dns/index.md +++ b/docs/configuration/dns/index.md @@ -10,7 +10,8 @@ "final": "", "strategy": "", "disable_cache": false, - "disable_expire": false + "disable_expire": false, + "reverse_mapping": false } } @@ -43,4 +44,10 @@ Disable dns cache. #### disable_expire -Disable dns cache expire. \ No newline at end of file +Disable dns cache expire. + +#### reverse_mapping + +Stores a reverse mapping of IP addresses after responding to a DNS query in order to provide domain names when routing. + +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. diff --git a/docs/configuration/dns/index.zh.md b/docs/configuration/dns/index.zh.md index 234739d3..c721ed52 100644 --- a/docs/configuration/dns/index.zh.md +++ b/docs/configuration/dns/index.zh.md @@ -10,7 +10,8 @@ "final": "", "strategy": "", "disable_cache": false, - "disable_expire": false + "disable_expire": false, + "reverse_mapping": false } } @@ -43,4 +44,10 @@ #### disable_expire -禁用 DNS 缓存过期。 \ No newline at end of file +禁用 DNS 缓存过期。 + +#### reverse_mapping + +在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。 + +由于此过程依赖于应用程序在发出请求之前解析域名的行为,因此在 macOS 等 DNS 由系统代理和缓存的环境中可能会出现问题。 diff --git a/option/dns.go b/option/dns.go index 637f2c05..d376f433 100644 --- a/option/dns.go +++ b/option/dns.go @@ -10,9 +10,10 @@ import ( ) type DNSOptions struct { - Servers []DNSServerOptions `json:"servers,omitempty"` - Rules []DNSRule `json:"rules,omitempty"` - Final string `json:"final,omitempty"` + Servers []DNSServerOptions `json:"servers,omitempty"` + Rules []DNSRule `json:"rules,omitempty"` + Final string `json:"final,omitempty"` + ReverseMapping bool `json:"reverse_mapping,omitempty"` DNSClientOptions } diff --git a/route/router.go b/route/router.go index 5534cea0..d988b70c 100644 --- a/route/router.go +++ b/route/router.go @@ -69,6 +69,7 @@ type Router struct { transports []dns.Transport transportMap map[string]dns.Transport transportDomainStrategy map[dns.Transport]dns.DomainStrategy + dnsReverseMapping *DNSReverseMapping interfaceFinder myInterfaceFinder autoDetectInterface bool defaultInterface string @@ -237,6 +238,10 @@ func NewRouter( router.transportMap = transportMap router.transportDomainStrategy = transportDomainStrategy + if dnsOptions.ReverseMapping { + router.dnsReverseMapping = NewDNSReverseMapping() + } + usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor() needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool { return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute @@ -631,6 +636,15 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad buffer.Release() } } + + if r.dnsReverseMapping != nil && metadata.Domain == "" { + domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr) + if loaded { + metadata.Domain = domain + r.logger.DebugContext(ctx, "found reserve mapped domain: ", metadata.Domain) + } + } + if metadata.Destination.IsFqdn() && dns.DomainStrategy(metadata.InboundOptions.DomainStrategy) != dns.DomainStrategyAsIS { addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)) if err != nil { diff --git a/route/router_dns.go b/route/router_dns.go index e320daec..11c02c87 100644 --- a/route/router_dns.go +++ b/route/router_dns.go @@ -4,17 +4,39 @@ import ( "context" "net/netip" "strings" + "time" "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-dns" + "github.com/sagernet/sing/common/cache" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" + M "github.com/sagernet/sing/common/metadata" mDNS "github.com/miekg/dns" ) +type DNSReverseMapping struct { + cache *cache.LruCache[netip.Addr, string] +} + +func NewDNSReverseMapping() *DNSReverseMapping { + return &DNSReverseMapping{ + cache: cache.New[netip.Addr, string](), + } +} + +func (m *DNSReverseMapping) Save(address netip.Addr, domain string, ttl int) { + m.cache.StoreWithExpire(address, domain, time.Now().Add(time.Duration(ttl)*time.Second)) +} + +func (m *DNSReverseMapping) Query(address netip.Addr) (string, bool) { + domain, loaded := m.cache.Load(address) + return domain, loaded +} + func (r *Router) matchDNS(ctx context.Context) (context.Context, dns.Transport, dns.DomainStrategy) { metadata := adapter.ContextFrom(ctx) if metadata == nil { @@ -69,6 +91,16 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er if len(message.Question) > 0 && response != nil { LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer) } + if r.dnsReverseMapping != nil && len(message.Question) > 0 && response != nil && len(response.Answer) > 0 { + for _, answer := range response.Answer { + switch record := answer.(type) { + case *mDNS.A: + r.dnsReverseMapping.Save(M.AddrFromIP(record.A), fqdnToDomain(record.Hdr.Name), int(record.Hdr.Ttl)) + case *mDNS.AAAA: + r.dnsReverseMapping.Save(M.AddrFromIP(record.AAAA), fqdnToDomain(record.Hdr.Name), int(record.Hdr.Ttl)) + } + } + } return response, err }