mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-05-15 00:50:25 +02:00
all: add local caching
This commit adds config params to enable local DNS response caching and control its behavior, allow tweaking the cache size, ttl override and serving stale response.
This commit is contained in:
committed by
Cuong Manh Le
parent
fa3c3e8a29
commit
30fefe7ab9
@@ -13,8 +13,11 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/Control-D-Inc/ctrld"
|
||||
"github.com/Control-D-Inc/ctrld/internal/dnscache"
|
||||
)
|
||||
|
||||
const staleTTL = 60 * time.Second
|
||||
|
||||
func (p *prog) serveUDP(listenerNum string) error {
|
||||
listenerConfig := p.cfg.Listener[listenerNum]
|
||||
// make sure ip is allocated
|
||||
@@ -123,6 +126,22 @@ func (p *prog) upstreamFor(ctx context.Context, defaultUpstreamNum string, lc *c
|
||||
}
|
||||
|
||||
func (p *prog) proxy(ctx context.Context, upstreams []string, failoverRcodes []int, msg *dns.Msg) *dns.Msg {
|
||||
var staleAnswer *dns.Msg
|
||||
serveStaleCache := p.cache != nil && p.cfg.Service.CacheServeStale
|
||||
// Inverse query should not be cached: https://www.rfc-editor.org/rfc/rfc1035#section-7.4
|
||||
if p.cache != nil && msg.Question[0].Qtype != dns.TypePTR {
|
||||
if cachedValue := p.cache.Get(dnscache.NewKey(msg)); cachedValue != nil {
|
||||
answer := cachedValue.Msg.Copy()
|
||||
answer.SetReply(msg)
|
||||
now := time.Now()
|
||||
if cachedValue.Expire.After(now) {
|
||||
ctrld.Log(ctx, proxyLog.Debug(), "hit cached response")
|
||||
setCachedAnswerTTL(answer, now, cachedValue.Expire)
|
||||
return answer
|
||||
}
|
||||
staleAnswer = answer
|
||||
}
|
||||
}
|
||||
upstreamConfigs := p.upstreamConfigsFromUpstreamNumbers(upstreams)
|
||||
resolve := func(n int, upstreamConfig *ctrld.UpstreamConfig, msg *dns.Msg) *dns.Msg {
|
||||
ctrld.Log(ctx, proxyLog.Debug(), "sending query to %s: %s", upstreams[n], upstreamConfig.Name)
|
||||
@@ -148,12 +167,29 @@ func (p *prog) proxy(ctx context.Context, upstreams []string, failoverRcodes []i
|
||||
for n, upstreamConfig := range upstreamConfigs {
|
||||
answer := resolve(n, upstreamConfig, msg)
|
||||
if answer == nil {
|
||||
if serveStaleCache && staleAnswer != nil {
|
||||
ctrld.Log(ctx, proxyLog.Debug(), "serving stale cached response")
|
||||
now := time.Now()
|
||||
setCachedAnswerTTL(staleAnswer, now, now.Add(staleTTL))
|
||||
return staleAnswer
|
||||
}
|
||||
continue
|
||||
}
|
||||
if answer.Rcode != dns.RcodeSuccess && len(upstreamConfigs) > 1 && containRcode(failoverRcodes, answer.Rcode) {
|
||||
ctrld.Log(ctx, proxyLog.Debug(), "failover rcode matched, process to next upstream")
|
||||
continue
|
||||
}
|
||||
if p.cache != nil {
|
||||
ttl := ttlFromMsg(answer)
|
||||
now := time.Now()
|
||||
expired := now.Add(time.Duration(ttl) * time.Second)
|
||||
if cachedTTL := p.cfg.Service.CacheTTLOverride; cachedTTL > 0 {
|
||||
expired = now.Add(time.Duration(cachedTTL) * time.Second)
|
||||
}
|
||||
setCachedAnswerTTL(answer, now, expired)
|
||||
p.cache.Add(dnscache.NewKey(msg), dnscache.NewValue(answer, expired))
|
||||
ctrld.Log(ctx, proxyLog.Debug(), "add cached response")
|
||||
}
|
||||
return answer
|
||||
}
|
||||
ctrld.Log(ctx, proxyLog.Error(), "all upstreams failed")
|
||||
@@ -229,6 +265,35 @@ func containRcode(rcodes []int, rcode int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func setCachedAnswerTTL(answer *dns.Msg, now, expiredTime time.Time) {
|
||||
ttl := uint32(expiredTime.Sub(now).Seconds())
|
||||
if ttl < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, rr := range answer.Answer {
|
||||
rr.Header().Ttl = ttl
|
||||
}
|
||||
for _, rr := range answer.Ns {
|
||||
rr.Header().Ttl = ttl
|
||||
}
|
||||
for _, rr := range answer.Extra {
|
||||
if rr.Header().Rrtype != dns.TypeOPT {
|
||||
rr.Header().Ttl = ttl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ttlFromMsg(msg *dns.Msg) uint32 {
|
||||
for _, rr := range msg.Answer {
|
||||
return rr.Header().Ttl
|
||||
}
|
||||
for _, rr := range msg.Ns {
|
||||
return rr.Header().Ttl
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var osUpstreamConfig = &ctrld.UpstreamConfig{
|
||||
Name: "OS resolver",
|
||||
Type: "os",
|
||||
|
||||
+11
-1
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/Control-D-Inc/ctrld"
|
||||
"github.com/Control-D-Inc/ctrld/internal/dnscache"
|
||||
)
|
||||
|
||||
var errWindowsAddrInUse = syscall.Errno(0x2740)
|
||||
@@ -22,7 +23,8 @@ var svcConfig = &service.Config{
|
||||
}
|
||||
|
||||
type prog struct {
|
||||
cfg *ctrld.Config
|
||||
cfg *ctrld.Config
|
||||
cache dnscache.Cacher
|
||||
}
|
||||
|
||||
func (p *prog) Start(s service.Service) error {
|
||||
@@ -32,6 +34,14 @@ func (p *prog) Start(s service.Service) error {
|
||||
}
|
||||
|
||||
func (p *prog) run() {
|
||||
if p.cfg.Service.CacheEnable {
|
||||
cacher, err := dnscache.NewLRUCache(p.cfg.Service.CacheSize)
|
||||
if err != nil {
|
||||
mainLog.Error().Err(err).Msg("failed to create cacher, caching is disabled")
|
||||
} else {
|
||||
p.cache = cacher
|
||||
}
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(p.cfg.Listener))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user