158 lines
3.5 KiB
Go
158 lines
3.5 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"math/rand"
|
||
|
"net"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/miekg/dns"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
dnsStringToType sync.Map
|
||
|
dnsExtraQtype = make(map[uint16]bool)
|
||
|
|
||
|
dnsTtlRange uint32
|
||
|
)
|
||
|
|
||
|
func setupDns() {
|
||
|
for _, v := range []uint16{
|
||
|
dns.TypePTR, dns.TypeMX, dns.TypeTXT, dns.TypeSRV, dns.TypeLOC, dns.TypeHINFO, dns.TypeSPF,
|
||
|
dns.TypeSIG, dns.TypeKEY, dns.TypeKX, dns.TypeDS, dns.TypeRRSIG, dns.TypeNSEC, dns.TypeDNSKEY, dns.TypeNSEC3, dns.TypeNSEC3PARAM, dns.TypeTLSA, dns.TypeCDS, dns.TypeCDNSKEY, dns.TypeZONEMD, dns.TypeTKEY, dns.TypeTSIG, dns.TypeCAA,
|
||
|
dns.TypeSVCB, dns.TypeHTTPS, dns.TypeURI,
|
||
|
} {
|
||
|
dnsExtraQtype[v] = true
|
||
|
}
|
||
|
|
||
|
// warmup
|
||
|
for _, v := range []string{"SOA", "NS", "A", "AAAA", "ANY", "PTR", "CNAME"} {
|
||
|
dnsQtypeStringToValue(v)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func dnsIsAllowedExtraQtype(qtype uint16) bool {
|
||
|
_, found := dnsExtraQtype[qtype]
|
||
|
return found
|
||
|
}
|
||
|
|
||
|
func dnsQtypeStringToValue(qtype string) uint16 {
|
||
|
x, found := dnsStringToType.Load(qtype)
|
||
|
if found {
|
||
|
return x.(uint16)
|
||
|
}
|
||
|
|
||
|
for k, v := range dns.TypeToString {
|
||
|
if v == qtype {
|
||
|
dnsStringToType.Store(qtype, k)
|
||
|
return k
|
||
|
}
|
||
|
}
|
||
|
|
||
|
log.Printf("qtype %#v is not known or found", qtype)
|
||
|
return dns.TypeNone
|
||
|
}
|
||
|
|
||
|
func dnsCustomResolve(qname string, qtype uint16) (*dns.Msg, error) {
|
||
|
qtype_s, known := dns.TypeToString[qtype]
|
||
|
if !known {
|
||
|
log.Printf("qtype is not known (%v) for %v", qtype, qname)
|
||
|
return nil, fmt.Errorf("qtype is not known: %v", qtype)
|
||
|
}
|
||
|
|
||
|
c := new(dns.Client)
|
||
|
c.Net = cfgResolverProto
|
||
|
c.Dialer = &net.Dialer{
|
||
|
Timeout: cfgResolverTimeout,
|
||
|
}
|
||
|
|
||
|
req := new(dns.Msg)
|
||
|
req.Id = dns.Id()
|
||
|
req.RecursionDesired = true
|
||
|
req.Question = make([]dns.Question, 1)
|
||
|
req.Question[0] = dns.Question{Name: qname, Qtype: qtype, Qclass: dns.ClassINET}
|
||
|
|
||
|
resp, rtt, err := c.Exchange(req, cfgResolverEndpoint)
|
||
|
if err != nil {
|
||
|
log.Printf("resolving %v/%v (rtt %v) with error: %v", qname, qtype_s, rtt, err)
|
||
|
return nil, err
|
||
|
}
|
||
|
log.Printf("resolved %v/%v (rtt %v)", qname, qtype_s, rtt)
|
||
|
|
||
|
return resp, nil
|
||
|
}
|
||
|
|
||
|
func dnsClipTtl(ttl uint32) uint32 {
|
||
|
if ttl < cfgTtlMin {
|
||
|
return cfgTtlMin
|
||
|
}
|
||
|
if ttl > cfgTtlMax {
|
||
|
return cfgTtlMax
|
||
|
}
|
||
|
return ttl
|
||
|
}
|
||
|
|
||
|
func randUint32(v uint32) uint32 {
|
||
|
if v < 2 {
|
||
|
return rand.Uint32()
|
||
|
}
|
||
|
return rand.Uint32() % v
|
||
|
}
|
||
|
|
||
|
func dnsFuzzClipTtl() uint32 {
|
||
|
return dnsClipTtl(cfgTtlMin + 1 + randUint32(dnsTtlRange*2) - dnsTtlRange)
|
||
|
}
|
||
|
|
||
|
type PowerDnsAnswer struct {
|
||
|
Qname string `json:"qname"`
|
||
|
Qtype string `json:"qtype"`
|
||
|
Ttl uint32 `json:"ttl"`
|
||
|
Content string `json:"content"`
|
||
|
}
|
||
|
|
||
|
func dnsRrToPowerDnsAnswer(rr dns.RR) (PowerDnsAnswer, error) {
|
||
|
qname := rr.Header().Name
|
||
|
|
||
|
rrtype := rr.Header().Rrtype
|
||
|
qtype_s, ok := dns.TypeToString[rrtype]
|
||
|
if !ok {
|
||
|
return PowerDnsAnswer{}, fmt.Errorf("record %v: unknown rrtype: %v", qname, rrtype)
|
||
|
}
|
||
|
|
||
|
cont, ok := strings.CutPrefix(rr.String(), rr.Header().String())
|
||
|
if !ok {
|
||
|
return PowerDnsAnswer{}, fmt.Errorf("record %v/%v: unable to produce content", qname, qtype_s)
|
||
|
}
|
||
|
|
||
|
return PowerDnsAnswer{
|
||
|
Qname: qname,
|
||
|
Qtype: qtype_s,
|
||
|
Ttl: dnsClipTtl(rr.Header().Ttl),
|
||
|
Content: strings.TrimSpace(cont),
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func dnsToPowerDnsAnswer(resp *dns.Msg) ([]PowerDnsAnswer, error) {
|
||
|
result := make([]PowerDnsAnswer, 0, len(resp.Answer)+len(resp.Extra))
|
||
|
for i := range resp.Answer {
|
||
|
r, err := dnsRrToPowerDnsAnswer(resp.Answer[i])
|
||
|
if err != nil {
|
||
|
log.Printf("%v", err)
|
||
|
continue
|
||
|
}
|
||
|
result = append(result, r)
|
||
|
}
|
||
|
for i := range resp.Extra {
|
||
|
r, err := dnsRrToPowerDnsAnswer(resp.Extra[i])
|
||
|
if err != nil {
|
||
|
log.Printf("%v", err)
|
||
|
continue
|
||
|
}
|
||
|
result = append(result, r)
|
||
|
}
|
||
|
|
||
|
return result, nil
|
||
|
}
|