From 6f1b25850140b620795eabe768980311a56f619e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Wed, 26 Apr 2023 04:53:25 +0800 Subject: [PATCH] Improve DNS caching --- common/dialer/conntrack/packet_conn.go | 3 +- docs/configuration/dns/index.md | 5 ++++ docs/configuration/dns/index.zh.md | 5 ++++ go.mod | 4 +-- go.sum | 8 +++--- option/dns.go | 7 +++-- outbound/dns.go | 6 ++-- route/router.go | 7 ++++- route/router_dns.go | 40 +++++++++++++++----------- 9 files changed, 55 insertions(+), 30 deletions(-) diff --git a/common/dialer/conntrack/packet_conn.go b/common/dialer/conntrack/packet_conn.go index 3ae3f39d..6e2d925b 100644 --- a/common/dialer/conntrack/packet_conn.go +++ b/common/dialer/conntrack/packet_conn.go @@ -4,6 +4,7 @@ import ( "io" "net" + "github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/x/list" ) @@ -42,7 +43,7 @@ func (c *PacketConn) Close() error { } func (c *PacketConn) Upstream() any { - return c.PacketConn + return bufio.NewPacketConn(c.PacketConn) } func (c *PacketConn) ReaderReplaceable() bool { diff --git a/docs/configuration/dns/index.md b/docs/configuration/dns/index.md index 40b26473..5f8b2547 100644 --- a/docs/configuration/dns/index.md +++ b/docs/configuration/dns/index.md @@ -11,6 +11,7 @@ "strategy": "", "disable_cache": false, "disable_expire": false, + "independent_cache": false, "reverse_mapping": false, "fakeip": {} } @@ -48,6 +49,10 @@ Disable dns cache. Disable dns cache expire. +#### independent_cache + +Make each DNS server's cache independent for special purposes. If enabled, will slightly degrade performance. + #### reverse_mapping Stores a reverse mapping of IP addresses after responding to a DNS query in order to provide domain names when routing. diff --git a/docs/configuration/dns/index.zh.md b/docs/configuration/dns/index.zh.md index 034d8dd3..b84f9528 100644 --- a/docs/configuration/dns/index.zh.md +++ b/docs/configuration/dns/index.zh.md @@ -11,6 +11,7 @@ "strategy": "", "disable_cache": false, "disable_expire": false, + "independent_cache": false, "reverse_mapping": false, "fakeip": {} } @@ -47,6 +48,10 @@ 禁用 DNS 缓存过期。 +#### independent_cache + +使每个 DNS 服务器的缓存独立,以满足特殊目的。如果启用,将轻微降低性能。 + #### reverse_mapping 在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。 diff --git a/go.mod b/go.mod index 744ffa87..cf0b1a63 100644 --- a/go.mod +++ b/go.mod @@ -24,8 +24,8 @@ require ( github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035 github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.2.5-0.20230425122720-bf0aaacc6754 - github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc + github.com/sagernet/sing v0.2.5-0.20230501044132-8365dd48a17a + github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223 github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646 github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b diff --git a/go.sum b/go.sum index c4343ca1..8506d45e 100644 --- a/go.sum +++ b/go.sum @@ -111,10 +111,10 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byL github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.5-0.20230425122720-bf0aaacc6754 h1:y89Ntm1rrZPQVb1f+TKd4DH6NwX5XCyMIwoseTQd/5U= -github.com/sagernet/sing v0.2.5-0.20230425122720-bf0aaacc6754/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= -github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4= -github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY= +github.com/sagernet/sing v0.2.5-0.20230501044132-8365dd48a17a h1:s2kkd/eR3mWGkYioknxhgQzG8uft4VRx9skhqxxeyVQ= +github.com/sagernet/sing v0.2.5-0.20230501044132-8365dd48a17a/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223 h1:L4eMuM07iSHY3UCknFnuFuHoe5clZuF2Xnf2wwA6Lwc= +github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY= github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646 h1:X3ADfMqeGns1Q1FlXc9kaL9FwW1UM6D6tEQo8jFstpc= github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI= github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 h1:bAHZCdWqJkb8LEW98+YsMVDXGRMUVjka8IC+St6ot88= diff --git a/option/dns.go b/option/dns.go index d700252c..1e73fb5f 100644 --- a/option/dns.go +++ b/option/dns.go @@ -20,9 +20,10 @@ type DNSServerOptions struct { } type DNSClientOptions struct { - Strategy DomainStrategy `json:"strategy,omitempty"` - DisableCache bool `json:"disable_cache,omitempty"` - DisableExpire bool `json:"disable_expire,omitempty"` + Strategy DomainStrategy `json:"strategy,omitempty"` + DisableCache bool `json:"disable_cache,omitempty"` + DisableExpire bool `json:"disable_expire,omitempty"` + IndependentCache bool `json:"independent_cache,omitempty"` } type DNSFakeIPOptions struct { diff --git a/outbound/dns.go b/outbound/dns.go index ad9bec45..07773ab5 100644 --- a/outbound/dns.go +++ b/outbound/dns.go @@ -104,18 +104,18 @@ func (d *DNS) handleConnection(ctx context.Context, conn net.Conn, metadata adap func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { var reader N.PacketReader = conn var counters []N.CountFunc - var cachedBuffer []*N.PacketBuffer + var cachedPackets []*N.PacketBuffer for { reader, counters = N.UnwrapCountPacketReader(reader, counters) if cachedReader, isCached := reader.(N.CachedPacketReader); isCached { packet := cachedReader.ReadCachedPacket() if packet != nil { - cachedBuffer = append([]*N.PacketBuffer{packet}, cachedBuffer...) + cachedPackets = append(cachedPackets, packet) continue } } if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created { - return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedBuffer, metadata) + return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata) } break } diff --git a/route/router.go b/route/router.go index c1e3ba50..fc12039c 100644 --- a/route/router.go +++ b/route/router.go @@ -111,7 +111,12 @@ func NewRouter( defaultMark: options.DefaultMark, platformInterface: platformInterface, } - router.dnsClient = dns.NewClient(dnsOptions.DNSClientOptions.DisableCache, dnsOptions.DNSClientOptions.DisableExpire, router.dnsLogger) + router.dnsClient = dns.NewClient(dns.ClientOptions{ + DisableCache: dnsOptions.DNSClientOptions.DisableCache, + DisableExpire: dnsOptions.DNSClientOptions.DisableExpire, + IndependentCache: dnsOptions.DNSClientOptions.IndependentCache, + Logger: router.dnsLogger, + }) for i, ruleOptions := range options.Rules { routeRule, err := NewRule(router, router.logger, ruleOptions) if err != nil { diff --git a/route/router_dns.go b/route/router_dns.go index d343fb8b..0b693371 100644 --- a/route/router_dns.go +++ b/route/router_dns.go @@ -73,23 +73,31 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er if len(message.Question) > 0 { r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String())) } - ctx, metadata := adapter.AppendContext(ctx) - if len(message.Question) > 0 { - metadata.QueryType = message.Question[0].Qtype - switch metadata.QueryType { - case mDNS.TypeA: - metadata.IPVersion = 4 - case mDNS.TypeAAAA: - metadata.IPVersion = 6 + var ( + response *mDNS.Msg + cached bool + err error + ) + response, cached = r.dnsClient.ExchangeCache(ctx, message) + if !cached { + ctx, metadata := adapter.AppendContext(ctx) + if len(message.Question) > 0 { + metadata.QueryType = message.Question[0].Qtype + switch metadata.QueryType { + case mDNS.TypeA: + metadata.IPVersion = 4 + case mDNS.TypeAAAA: + metadata.IPVersion = 6 + } + metadata.Domain = fqdnToDomain(message.Question[0].Name) + } + ctx, transport, strategy := r.matchDNS(ctx) + ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout) + defer cancel() + response, err = r.dnsClient.Exchange(ctx, transport, message, strategy) + if err != nil && len(message.Question) > 0 { + r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String()))) } - metadata.Domain = fqdnToDomain(message.Question[0].Name) - } - ctx, transport, strategy := r.matchDNS(ctx) - ctx, cancel := context.WithTimeout(ctx, C.DNSTimeout) - defer cancel() - response, err := r.dnsClient.Exchange(ctx, transport, message, strategy) - if err != nil && len(message.Question) > 0 { - r.dnsLogger.ErrorContext(ctx, E.Cause(err, "exchange failed for ", formatQuestion(message.Question[0].String()))) } if len(message.Question) > 0 && response != nil { LogDNSAnswers(r.dnsLogger, ctx, message.Question[0].Name, response.Answer)