2024-06-04 03:20:21 +03:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
2024-06-30 11:51:09 +03:00
|
|
|
"time"
|
2024-06-04 03:20:21 +03:00
|
|
|
|
|
|
|
"github.com/miekg/dns"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
dnsStringToType sync.Map
|
|
|
|
)
|
|
|
|
|
|
|
|
func dnsQtypeStringToValue(qtype string) uint16 {
|
|
|
|
x, found_qtype := dnsStringToType.Load(qtype)
|
|
|
|
if found_qtype {
|
|
|
|
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, qtype_known := dns.TypeToString[qtype]
|
|
|
|
if !qtype_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}
|
|
|
|
|
2024-06-30 11:51:09 +03:00
|
|
|
var resp *dns.Msg
|
|
|
|
var rtt time.Duration
|
|
|
|
var err error
|
|
|
|
for i := 0; i < 2; i++ {
|
|
|
|
resp, rtt, err = c.Exchange(req, cfgResolverEndpoint)
|
|
|
|
if err == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2024-06-04 03:20:21 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
type PowerDnsAnswer struct {
|
|
|
|
Qname string `json:"qname"`
|
|
|
|
Qtype string `json:"qtype"`
|
|
|
|
Ttl uint32 `json:"ttl"`
|
|
|
|
Content string `json:"content"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func dnsToPowerDnsAnswer(response *dns.Msg) ([]PowerDnsAnswer, error) {
|
|
|
|
if response == nil {
|
|
|
|
return nil, errors.New("response is nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
result := make([]PowerDnsAnswer, 0, len(response.Answer))
|
|
|
|
for i := range response.Answer {
|
|
|
|
cont, ok := strings.CutPrefix(response.Answer[i].String(), response.Answer[i].Header().String())
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
qtype, ok := dns.TypeToString[response.Answer[i].Header().Rrtype]
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
rec := PowerDnsAnswer{
|
|
|
|
Qname: response.Answer[i].Header().Name,
|
|
|
|
Qtype: qtype,
|
|
|
|
Ttl: dnsClipTtl(response.Answer[i].Header().Ttl),
|
|
|
|
Content: cont,
|
|
|
|
}
|
|
|
|
result = append(result, rec)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|