Procházet zdrojové kódy

Add dns reverse mapping

世界 před 2 roky
rodič
revize
52b776b561

+ 9 - 2
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.
+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.

+ 9 - 2
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 缓存过期。
+禁用 DNS 缓存过期。
+
+#### reverse_mapping
+
+在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。
+
+由于此过程依赖于应用程序在发出请求之前解析域名的行为,因此在 macOS 等 DNS 由系统代理和缓存的环境中可能会出现问题。

+ 4 - 3
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
 }
 

+ 14 - 0
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 {

+ 32 - 0
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
 }