Files
weave/nameserver/dns.go
T

118 lines
2.5 KiB
Go

package nameserver
import (
"math/rand"
"time"
"github.com/miekg/dns"
)
const (
minUDPSize = 512
maxUDPSize = 65535
)
func makeHeader(r *dns.Msg, q *dns.Question, ttl int) *dns.RR_Header {
return &dns.RR_Header{
Name: q.Name,
Rrtype: q.Qtype,
Class: dns.ClassINET,
Ttl: uint32(ttl),
}
}
func makeReply(r *dns.Msg, as []dns.RR) *dns.Msg {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.Answer = as
return m
}
func makeTruncatedReply(r *dns.Msg) *dns.Msg {
// for truncated response, we create a minimal reply with the Truncated bit set
reply := new(dns.Msg)
reply.SetReply(r)
reply.Truncated = true
return reply
}
type DNSResponseBuilder func(r *dns.Msg, q *dns.Question, addrs []ZoneRecord, ttl int) *dns.Msg
func makeAddressReply(r *dns.Msg, q *dns.Question, addrs []ZoneRecord, ttl int) *dns.Msg {
answers := make([]dns.RR, len(addrs))
header := makeHeader(r, q, ttl)
count := 0
for _, addr := range addrs {
ip := addr.IP()
ip4 := ip.To4()
switch q.Qtype {
case dns.TypeA:
if ip4 != nil {
answers[count] = &dns.A{Hdr: *header, A: ip}
count++
}
case dns.TypeAAAA:
if ip4 == nil {
answers[count] = &dns.AAAA{Hdr: *header, AAAA: ip}
count++
}
}
}
return makeReply(r, answers[:count])
}
func makePTRReply(r *dns.Msg, q *dns.Question, names []ZoneRecord, ttl int) *dns.Msg {
answers := make([]dns.RR, len(names))
header := makeHeader(r, q, ttl)
for i, name := range names {
answers[i] = &dns.PTR{Hdr: *header, Ptr: name.Name()}
}
return makeReply(r, answers)
}
func makeDNSFailResponse(r *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetReply(r)
m.RecursionAvailable = true
m.Rcode = dns.RcodeNameError
return m
}
// get the maximum UDP-reply length
func getMaxReplyLen(r *dns.Msg, proto dnsProtocol) int {
maxLen := minUDPSize
if proto == protTCP {
maxLen = maxUDPSize
} else if opt := r.IsEdns0(); opt != nil {
maxLen = int(opt.UDPSize())
}
return maxLen
}
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
// shuffleAnswers reorders answers for very basic load balancing
func shuffleAnswers(answers []dns.RR) []dns.RR {
if len(answers) > 1 {
for i := range answers {
j := rand.Intn(i + 1)
answers[i], answers[j] = answers[j], answers[i]
}
}
return answers
}
// only take the first `num` answers
func pruneAnswers(answers []dns.RR, num int) []dns.RR {
if num > 0 && len(answers) > num {
// TODO: we should have some prefer locally-introduced answers, etc...
return answers[:num]
}
return answers
}