mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-16 08:42:22 +08:00
Add dns reverse mapping
This commit is contained in:
parent
c74d3a53d4
commit
52b776b561
|
@ -10,7 +10,8 @@
|
||||||
"final": "",
|
"final": "",
|
||||||
"strategy": "",
|
"strategy": "",
|
||||||
"disable_cache": false,
|
"disable_cache": false,
|
||||||
"disable_expire": false
|
"disable_expire": false,
|
||||||
|
"reverse_mapping": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,3 +45,9 @@ Disable dns cache.
|
||||||
#### disable_expire
|
#### 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.
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
"final": "",
|
"final": "",
|
||||||
"strategy": "",
|
"strategy": "",
|
||||||
"disable_cache": false,
|
"disable_cache": false,
|
||||||
"disable_expire": false
|
"disable_expire": false,
|
||||||
|
"reverse_mapping": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,3 +45,9 @@
|
||||||
#### disable_expire
|
#### disable_expire
|
||||||
|
|
||||||
禁用 DNS 缓存过期。
|
禁用 DNS 缓存过期。
|
||||||
|
|
||||||
|
#### reverse_mapping
|
||||||
|
|
||||||
|
在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。
|
||||||
|
|
||||||
|
由于此过程依赖于应用程序在发出请求之前解析域名的行为,因此在 macOS 等 DNS 由系统代理和缓存的环境中可能会出现问题。
|
||||||
|
|
|
@ -10,9 +10,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type DNSOptions struct {
|
type DNSOptions struct {
|
||||||
Servers []DNSServerOptions `json:"servers,omitempty"`
|
Servers []DNSServerOptions `json:"servers,omitempty"`
|
||||||
Rules []DNSRule `json:"rules,omitempty"`
|
Rules []DNSRule `json:"rules,omitempty"`
|
||||||
Final string `json:"final,omitempty"`
|
Final string `json:"final,omitempty"`
|
||||||
|
ReverseMapping bool `json:"reverse_mapping,omitempty"`
|
||||||
DNSClientOptions
|
DNSClientOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ type Router struct {
|
||||||
transports []dns.Transport
|
transports []dns.Transport
|
||||||
transportMap map[string]dns.Transport
|
transportMap map[string]dns.Transport
|
||||||
transportDomainStrategy map[dns.Transport]dns.DomainStrategy
|
transportDomainStrategy map[dns.Transport]dns.DomainStrategy
|
||||||
|
dnsReverseMapping *DNSReverseMapping
|
||||||
interfaceFinder myInterfaceFinder
|
interfaceFinder myInterfaceFinder
|
||||||
autoDetectInterface bool
|
autoDetectInterface bool
|
||||||
defaultInterface string
|
defaultInterface string
|
||||||
|
@ -237,6 +238,10 @@ func NewRouter(
|
||||||
router.transportMap = transportMap
|
router.transportMap = transportMap
|
||||||
router.transportDomainStrategy = transportDomainStrategy
|
router.transportDomainStrategy = transportDomainStrategy
|
||||||
|
|
||||||
|
if dnsOptions.ReverseMapping {
|
||||||
|
router.dnsReverseMapping = NewDNSReverseMapping()
|
||||||
|
}
|
||||||
|
|
||||||
usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor()
|
usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor()
|
||||||
needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
needInterfaceMonitor := options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
|
||||||
return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
|
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()
|
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 {
|
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))
|
addresses, err := r.Lookup(adapter.WithContext(ctx, &metadata), metadata.Destination.Fqdn, dns.DomainStrategy(metadata.InboundOptions.DomainStrategy))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -4,17 +4,39 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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"
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
|
"github.com/sagernet/sing/common/cache"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
|
||||||
mDNS "github.com/miekg/dns"
|
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) {
|
func (r *Router) matchDNS(ctx context.Context) (context.Context, dns.Transport, dns.DomainStrategy) {
|
||||||
metadata := adapter.ContextFrom(ctx)
|
metadata := adapter.ContextFrom(ctx)
|
||||||
if metadata == nil {
|
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 {
|
if len(message.Question) > 0 && response != nil {
|
||||||
LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer)
|
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
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user