Improve DNS caching

This commit is contained in:
世界 2023-04-26 04:53:25 +08:00
parent f949ddc0ab
commit 6f1b258501
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
9 changed files with 55 additions and 30 deletions

View File

@ -4,6 +4,7 @@ import (
"io" "io"
"net" "net"
"github.com/sagernet/sing/common/bufio"
"github.com/sagernet/sing/common/x/list" "github.com/sagernet/sing/common/x/list"
) )
@ -42,7 +43,7 @@ func (c *PacketConn) Close() error {
} }
func (c *PacketConn) Upstream() any { func (c *PacketConn) Upstream() any {
return c.PacketConn return bufio.NewPacketConn(c.PacketConn)
} }
func (c *PacketConn) ReaderReplaceable() bool { func (c *PacketConn) ReaderReplaceable() bool {

View File

@ -11,6 +11,7 @@
"strategy": "", "strategy": "",
"disable_cache": false, "disable_cache": false,
"disable_expire": false, "disable_expire": false,
"independent_cache": false,
"reverse_mapping": false, "reverse_mapping": false,
"fakeip": {} "fakeip": {}
} }
@ -48,6 +49,10 @@ Disable dns cache.
Disable dns cache expire. Disable dns cache expire.
#### independent_cache
Make each DNS server's cache independent for special purposes. If enabled, will slightly degrade performance.
#### reverse_mapping #### reverse_mapping
Stores a reverse mapping of IP addresses after responding to a DNS query in order to provide domain names when routing. Stores a reverse mapping of IP addresses after responding to a DNS query in order to provide domain names when routing.

View File

@ -11,6 +11,7 @@
"strategy": "", "strategy": "",
"disable_cache": false, "disable_cache": false,
"disable_expire": false, "disable_expire": false,
"independent_cache": false,
"reverse_mapping": false, "reverse_mapping": false,
"fakeip": {} "fakeip": {}
} }
@ -47,6 +48,10 @@
禁用 DNS 缓存过期。 禁用 DNS 缓存过期。
#### independent_cache
使每个 DNS 服务器的缓存独立,以满足特殊目的。如果启用,将轻微降低性能。
#### reverse_mapping #### reverse_mapping
在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。 在响应 DNS 查询后存储 IP 地址的反向映射以为路由目的提供域名。

4
go.mod
View File

@ -24,8 +24,8 @@ require (
github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035 github.com/sagernet/gomobile v0.0.0-20230413023804-244d7ff07035
github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32 github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.2.5-0.20230425122720-bf0aaacc6754 github.com/sagernet/sing v0.2.5-0.20230501044132-8365dd48a17a
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc 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-mux v0.0.0-20230517134606-1ebe6bb26646
github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507
github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b

8
go.sum
View File

@ -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/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.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.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.20230501044132-8365dd48a17a h1:s2kkd/eR3mWGkYioknxhgQzG8uft4VRx9skhqxxeyVQ=
github.com/sagernet/sing v0.2.5-0.20230425122720-bf0aaacc6754/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= 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.20230415085626-111ecf799dfc h1:hmbuqKv48SAjiKPoqtJGvS5pEHVPZjTHq9CPwQY2cZ4= github.com/sagernet/sing-dns v0.1.5-0.20230426113254-25d948c44223 h1:L4eMuM07iSHY3UCknFnuFuHoe5clZuF2Xnf2wwA6Lwc=
github.com/sagernet/sing-dns v0.1.5-0.20230415085626-111ecf799dfc/go.mod h1:ZKuuqgsHRxDahYrzgSgy4vIAGGuKPlIf4hLcNzYzLkY= 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 h1:X3ADfMqeGns1Q1FlXc9kaL9FwW1UM6D6tEQo8jFstpc=
github.com/sagernet/sing-mux v0.0.0-20230517134606-1ebe6bb26646/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI= 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= github.com/sagernet/sing-shadowsocks v0.2.2-0.20230417102954-f77257340507 h1:bAHZCdWqJkb8LEW98+YsMVDXGRMUVjka8IC+St6ot88=

View File

@ -20,9 +20,10 @@ type DNSServerOptions struct {
} }
type DNSClientOptions struct { type DNSClientOptions struct {
Strategy DomainStrategy `json:"strategy,omitempty"` Strategy DomainStrategy `json:"strategy,omitempty"`
DisableCache bool `json:"disable_cache,omitempty"` DisableCache bool `json:"disable_cache,omitempty"`
DisableExpire bool `json:"disable_expire,omitempty"` DisableExpire bool `json:"disable_expire,omitempty"`
IndependentCache bool `json:"independent_cache,omitempty"`
} }
type DNSFakeIPOptions struct { type DNSFakeIPOptions struct {

View File

@ -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 { func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
var reader N.PacketReader = conn var reader N.PacketReader = conn
var counters []N.CountFunc var counters []N.CountFunc
var cachedBuffer []*N.PacketBuffer var cachedPackets []*N.PacketBuffer
for { for {
reader, counters = N.UnwrapCountPacketReader(reader, counters) reader, counters = N.UnwrapCountPacketReader(reader, counters)
if cachedReader, isCached := reader.(N.CachedPacketReader); isCached { if cachedReader, isCached := reader.(N.CachedPacketReader); isCached {
packet := cachedReader.ReadCachedPacket() packet := cachedReader.ReadCachedPacket()
if packet != nil { if packet != nil {
cachedBuffer = append([]*N.PacketBuffer{packet}, cachedBuffer...) cachedPackets = append(cachedPackets, packet)
continue continue
} }
} }
if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created { 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 break
} }

View File

@ -111,7 +111,12 @@ func NewRouter(
defaultMark: options.DefaultMark, defaultMark: options.DefaultMark,
platformInterface: platformInterface, 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 { for i, ruleOptions := range options.Rules {
routeRule, err := NewRule(router, router.logger, ruleOptions) routeRule, err := NewRule(router, router.logger, ruleOptions)
if err != nil { if err != nil {

View File

@ -73,23 +73,31 @@ func (r *Router) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, er
if len(message.Question) > 0 { if len(message.Question) > 0 {
r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String())) r.dnsLogger.DebugContext(ctx, "exchange ", formatQuestion(message.Question[0].String()))
} }
ctx, metadata := adapter.AppendContext(ctx) var (
if len(message.Question) > 0 { response *mDNS.Msg
metadata.QueryType = message.Question[0].Qtype cached bool
switch metadata.QueryType { err error
case mDNS.TypeA: )
metadata.IPVersion = 4 response, cached = r.dnsClient.ExchangeCache(ctx, message)
case mDNS.TypeAAAA: if !cached {
metadata.IPVersion = 6 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 { 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)