Pull request: 3185 detecting recursion
Merge in DNS/adguard-home from 3185-recursion to master
Closes #3185.
Squashed commit of the following:
commit 2fa44223f533c471f2b8c0e17d8550bf4ff73c7b
Merge: 7975957c 7a48e92e
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Thu May 27 19:04:44 2021 +0300
Merge branch 'master' into 3185-recursion
commit 7975957cceb840f76eef0e2e434f4163a122ac34
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Thu May 27 17:36:22 2021 +0300
dnsforward: imp docs
commit 1af7131a5b7c1fefed2d1eb8ee24ebfd3602dc77
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Thu May 27 17:15:00 2021 +0300
dnsforward: imp code, tests, docs
commit f3f9145fb5e1174fab87ca6890da9df722cfebf0
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Thu May 27 15:45:44 2021 +0300
dnsforward: add recursion detector
This commit is contained in:
115
internal/dnsforward/recursiondetector.go
Normal file
115
internal/dnsforward/recursiondetector.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package dnsforward
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/golibs/cache"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"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
|
||||
)
|
||||
|
||||
// recursionDetector detects recursion in DNS forwarding.
|
||||
type recursionDetector struct {
|
||||
recentRequests cache.Cache
|
||||
ttl time.Duration
|
||||
}
|
||||
|
||||
// check checks if the passed req was already sent by s.
|
||||
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: cache.New(cache.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+aghnet.MaxDomainNameLen)
|
||||
// The binary.BigEndian byte order is used everywhere except when the
|
||||
// real machine's endianess 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
|
||||
}
|
||||
|
||||
// msgToSignatureSlow converts msg into it's signature represented in bytes in
|
||||
// the less efficient way.
|
||||
//
|
||||
// See BenchmarkMsgToSignature.
|
||||
func msgToSignatureSlow(msg dns.Msg) (sig []byte) {
|
||||
type msgSignature struct {
|
||||
name [aghnet.MaxDomainNameLen]byte
|
||||
id uint16
|
||||
qtype uint16
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer(sig)
|
||||
q := msg.Question[0]
|
||||
signature := msgSignature{
|
||||
id: msg.Id,
|
||||
qtype: q.Qtype,
|
||||
}
|
||||
copy(signature.name[:], q.Name)
|
||||
if err := binary.Write(b, binary.BigEndian, signature); err != nil {
|
||||
log.Debug("writing message signature: %s", err)
|
||||
}
|
||||
|
||||
return b.Bytes()
|
||||
}
|
||||
Reference in New Issue
Block a user