From e6e556215b52d02b63e4b05bf1b85cc46a208ace 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 --- option/dns.go | 7 ++++--- route/router.go | 14 ++++++++++++++ route/router_dns.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) 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 6dbb1922..95ce3fc5 100644 --- a/route/router.go +++ b/route/router.go @@ -89,6 +89,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 @@ -267,6 +268,10 @@ func NewRouter( router.transportMap = transportMap router.transportDomainStrategy = transportDomainStrategy + if dnsOptions.ReverseMapping { + router.dnsReverseMapping = NewDNSReverseMapping() + } + needInterfaceMonitor := platformInterface == nil && (options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool { return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute })) @@ -627,6 +632,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 }