Files
dnsproxy/proxy/recursiondetector.go
T
Eugene Burkov 076a1de865 Pull request 332: AGDNS-1982 Improve code vol.2
Squashed commit of the following:

commit 7ee49cd6c3b90fe056d8c6145fe5e02c78258e6e
Merge: c9e3f2c feea26e
Author: Eugene Burkov <[email protected]>
Date:   Thu Mar 28 13:50:57 2024 +0300

    Merge branch 'master' into imp-code-vol.2

commit c9e3f2c09ce3dae650a448540c0d83ba1f3281b8
Author: Eugene Burkov <[email protected]>
Date:   Wed Mar 27 13:13:01 2024 +0300

    proxy: imp code, docs

commit 1ecf28820d7cd95ef8363c51ec3abae418509187
Author: Eugene Burkov <[email protected]>
Date:   Mon Mar 25 16:06:08 2024 +0300

    proxy: add recursion detector, imp code

commit e4f41d44cce7d8fcdba4448fb4ec9968e438536b
Author: Eugene Burkov <[email protected]>
Date:   Mon Mar 25 13:45:17 2024 +0300

    proxy: imp code
2024-03-28 15:34:14 +03:00

98 lines
2.4 KiB
Go

package proxy
import (
"encoding/binary"
"time"
glcache "github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/netutil"
"github.com/miekg/dns"
)
// uint* sizes in bytes to improve readability.
//
// TODO(e.burkov): Remove when there will be a more regardful way to define
// those. See https://github.com/golang/go/issues/29982.
const (
uint16sz = 2
uint64sz = 8
)
// TODO(e.burkov): Consider making configurable.
const (
// recursionTTL is the time recursive request is cached for.
recursionTTL = 1 * time.Second
// cachedRecurrentReqNum is the maximum number of cached recurrent requests.
cachedRecurrentReqNum = 1000
)
// recursionDetector detects recursion in DNS forwarding.
type recursionDetector struct {
recentRequests glcache.Cache
ttl time.Duration
}
// check checks if the passed req was already sent by the server.
func (rd *recursionDetector) check(msg *dns.Msg) (ok bool) {
if len(msg.Question) == 0 {
return false
}
key := msgToSignature(msg)
expireData := rd.recentRequests.Get(key)
if expireData == nil {
return false
}
expire := time.Unix(0, int64(binary.BigEndian.Uint64(expireData)))
return time.Now().Before(expire)
}
// add caches the msg if it has anything in the questions section.
func (rd *recursionDetector) add(msg *dns.Msg) {
now := time.Now()
if len(msg.Question) == 0 {
return
}
key := msgToSignature(msg)
expire64 := uint64(now.Add(rd.ttl).UnixNano())
expire := make([]byte, uint64sz)
binary.BigEndian.PutUint64(expire, expire64)
rd.recentRequests.Set(key, expire)
}
// clear clears the recent requests cache.
func (rd *recursionDetector) clear() {
rd.recentRequests.Clear()
}
// newRecursionDetector returns the initialized *recursionDetector.
func newRecursionDetector(ttl time.Duration, suspectsNum uint) (rd *recursionDetector) {
return &recursionDetector{
recentRequests: glcache.New(glcache.Config{
EnableLRU: true,
MaxCount: suspectsNum,
}),
ttl: ttl,
}
}
// msgToSignature converts msg into it's signature represented in bytes.
func msgToSignature(msg *dns.Msg) (sig []byte) {
sig = make([]byte, uint16sz*2+netutil.MaxDomainNameLen)
// The binary.BigEndian byte order is used everywhere except when the real
// machine's endianness is needed.
byteOrder := binary.BigEndian
byteOrder.PutUint16(sig[0:], msg.Id)
q := msg.Question[0]
byteOrder.PutUint16(sig[uint16sz:], q.Qtype)
copy(sig[2*uint16sz:], []byte(q.Name))
return sig
}