From 8182022d52ee21743384bac68895c2073576ef28 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 20 Aug 2015 18:27:58 +0100 Subject: [PATCH 1/8] refactor dns handlers introduce a handler struct, which - enables lifting of all the handle* closures to methods - eliminates the passing around of defaultMaxResponseSize - reduces the number of free-floating functions --- nameserver/dns.go | 252 +++++++++++++++++++++-------------------- nameserver/dns_test.go | 3 +- 2 files changed, 130 insertions(+), 125 deletions(-) diff --git a/nameserver/dns.go b/nameserver/dns.go index 8764939e..849881a1 100644 --- a/nameserver/dns.go +++ b/nameserver/dns.go @@ -112,144 +112,147 @@ func (d *DNSServer) errorResponse(r *dns.Msg, code int, w dns.ResponseWriter) { } } +type handler struct { + *DNSServer + maxResponseSize int + client *dns.Client +} + func (d *DNSServer) createMux(client *dns.Client, defaultMaxResponseSize int) *dns.ServeMux { m := dns.NewServeMux() - m.HandleFunc(d.domain, d.handleLocal(defaultMaxResponseSize)) - m.HandleFunc(reverseDNSdomain, d.handleReverse(client, defaultMaxResponseSize)) - m.HandleFunc(topDomain, d.handleRecursive(client, defaultMaxResponseSize)) + h := &handler{ + DNSServer: d, + maxResponseSize: defaultMaxResponseSize, + client: client, + } + m.HandleFunc(d.domain, h.handleLocal) + m.HandleFunc(reverseDNSdomain, h.handleReverse) + m.HandleFunc(topDomain, h.handleRecursive) return m } -func (d *DNSServer) handleLocal(defaultMaxResponseSize int) func(dns.ResponseWriter, *dns.Msg) { - return func(w dns.ResponseWriter, req *dns.Msg) { - d.ns.debugf("local request: %+v", *req) - if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypeA { - d.errorResponse(req, dns.RcodeNameError, w) - return - } +func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) { + h.ns.debugf("local request: %+v", *req) + if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypeA { + h.errorResponse(req, dns.RcodeNameError, w) + return + } + hostname := dns.Fqdn(req.Question[0].Name) + if strings.Count(hostname, ".") == 1 { + hostname = hostname + h.domain + } + + addrs := h.ns.Lookup(hostname) + if len(addrs) == 0 { + h.errorResponse(req, dns.RcodeNameError, w) + return + } + + response := dns.Msg{} + response.RecursionAvailable = true + response.Authoritative = true + response.SetReply(req) + response.Answer = make([]dns.RR, len(addrs)) + + header := dns.RR_Header{ + Name: req.Question[0].Name, + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: h.ttl, + } + + for i, addr := range addrs { + ip := addr.IP4() + response.Answer[i] = &dns.A{Hdr: header, A: ip} + } + + shuffleAnswers(&response.Answer) + h.truncateResponse(req, &response) + + h.ns.debugf("response: %+v", response) + if err := w.WriteMsg(&response); err != nil { + h.ns.infof("error responding: %v", err) + } +} + +func (h *handler) handleReverse(w dns.ResponseWriter, req *dns.Msg) { + h.ns.debugf("reverse request: %+v", *req) + if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypePTR { + h.errorResponse(req, dns.RcodeNameError, w) + return + } + + ipStr := strings.TrimSuffix(req.Question[0].Name, "."+reverseDNSdomain) + ip, err := address.ParseIP(ipStr) + if err != nil { + h.errorResponse(req, dns.RcodeNameError, w) + return + } + + hostname, err := h.ns.ReverseLookup(ip.Reverse()) + if err != nil { + h.handleRecursive(w, req) + return + } + + response := dns.Msg{} + response.RecursionAvailable = true + response.Authoritative = true + response.SetReply(req) + + header := dns.RR_Header{ + Name: req.Question[0].Name, + Rrtype: dns.TypePTR, + Class: dns.ClassINET, + Ttl: h.ttl, + } + + response.Answer = []dns.RR{&dns.PTR{ + Hdr: header, + Ptr: hostname, + }} + + h.truncateResponse(req, &response) + + h.ns.debugf("response: %+v", response) + if err := w.WriteMsg(&response); err != nil { + h.ns.infof("error responding: %v", err) + } +} + +func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) { + h.ns.debugf("recursive request: %+v", *req) + + // Resolve unqualified names locally + if len(req.Question) == 1 && req.Question[0].Qtype == dns.TypeA { hostname := dns.Fqdn(req.Question[0].Name) if strings.Count(hostname, ".") == 1 { - hostname = hostname + d.domain - } - - addrs := d.ns.Lookup(hostname) - if len(addrs) == 0 { - d.errorResponse(req, dns.RcodeNameError, w) + h.handleLocal(w, req) return } - - response := dns.Msg{} - response.RecursionAvailable = true - response.Authoritative = true - response.SetReply(req) - response.Answer = make([]dns.RR, len(addrs)) - - header := dns.RR_Header{ - Name: req.Question[0].Name, - Rrtype: dns.TypeA, - Class: dns.ClassINET, - Ttl: d.ttl, - } - - for i, addr := range addrs { - ip := addr.IP4() - response.Answer[i] = &dns.A{Hdr: header, A: ip} - } - - shuffleAnswers(&response.Answer) - maxResponseSize := getMaxResponseSize(req, defaultMaxResponseSize) - truncateResponse(&response, maxResponseSize) - - d.ns.debugf("response: %+v", response) - if err := w.WriteMsg(&response); err != nil { - d.ns.infof("error responding: %v", err) - } } -} -func (d *DNSServer) handleReverse(client *dns.Client, defaultMaxResponseSize int) func(dns.ResponseWriter, *dns.Msg) { - return func(w dns.ResponseWriter, req *dns.Msg) { - d.ns.debugf("reverse request: %+v", *req) - if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypePTR { - d.errorResponse(req, dns.RcodeNameError, w) - return + for _, server := range h.upstream.Servers { + reqCopy := req.Copy() + reqCopy.Id = dns.Id() + response, _, err := h.client.Exchange(reqCopy, fmt.Sprintf("%s:%s", server, h.upstream.Port)) + if err != nil || response == nil { + h.ns.debugf("error trying %s: %v", server, err) + continue } - - ipStr := strings.TrimSuffix(req.Question[0].Name, "."+reverseDNSdomain) - ip, err := address.ParseIP(ipStr) - if err != nil { - d.errorResponse(req, dns.RcodeNameError, w) - return + h.ns.debugf("response: %+v", response) + response.Id = req.Id + if response.Len() > h.getMaxResponseSize(req) { + response.Compress = true } - - hostname, err := d.ns.ReverseLookup(ip.Reverse()) - if err != nil { - d.handleRecursive(client, defaultMaxResponseSize)(w, req) - return - } - - response := dns.Msg{} - response.RecursionAvailable = true - response.Authoritative = true - response.SetReply(req) - - header := dns.RR_Header{ - Name: req.Question[0].Name, - Rrtype: dns.TypePTR, - Class: dns.ClassINET, - Ttl: d.ttl, - } - - response.Answer = []dns.RR{&dns.PTR{ - Hdr: header, - Ptr: hostname, - }} - - maxResponseSize := getMaxResponseSize(req, defaultMaxResponseSize) - truncateResponse(&response, maxResponseSize) - - d.ns.debugf("response: %+v", response) - if err := w.WriteMsg(&response); err != nil { - d.ns.infof("error responding: %v", err) + if err := w.WriteMsg(response); err != nil { + h.ns.infof("error responding: %v", err) } + return } -} -func (d *DNSServer) handleRecursive(client *dns.Client, defaultMaxResponseSize int) func(dns.ResponseWriter, *dns.Msg) { - return func(w dns.ResponseWriter, req *dns.Msg) { - d.ns.debugf("recursive request: %+v", *req) - - // Resolve unqualified names locally - if len(req.Question) == 1 && req.Question[0].Qtype == dns.TypeA { - hostname := dns.Fqdn(req.Question[0].Name) - if strings.Count(hostname, ".") == 1 { - d.handleLocal(defaultMaxResponseSize)(w, req) - return - } - } - - for _, server := range d.upstream.Servers { - reqCopy := req.Copy() - reqCopy.Id = dns.Id() - response, _, err := client.Exchange(reqCopy, fmt.Sprintf("%s:%s", server, d.upstream.Port)) - if err != nil || response == nil { - d.ns.debugf("error trying %s: %v", server, err) - continue - } - d.ns.debugf("response: %+v", response) - response.Id = req.Id - if response.Len() > getMaxResponseSize(req, defaultMaxResponseSize) { - response.Compress = true - } - if err := w.WriteMsg(response); err != nil { - d.ns.infof("error responding: %v", err) - } - return - } - - d.errorResponse(req, dns.RcodeServerFailure, w) - } + h.errorResponse(req, dns.RcodeServerFailure, w) } func shuffleAnswers(answers *[]dns.RR) { @@ -263,7 +266,8 @@ func shuffleAnswers(answers *[]dns.RR) { } } -func truncateResponse(response *dns.Msg, maxSize int) { +func (h *handler) truncateResponse(req, response *dns.Msg) { + maxSize := h.getMaxResponseSize(req) if len(response.Answer) <= 1 || maxSize <= 0 { return } @@ -286,9 +290,9 @@ func truncateResponse(response *dns.Msg, maxSize int) { response.Truncated = true } -func getMaxResponseSize(req *dns.Msg, defaultMaxResponseSize int) int { +func (h *handler) getMaxResponseSize(req *dns.Msg) int { if opt := req.IsEdns0(); opt != nil { return int(opt.UDPSize()) } - return defaultMaxResponseSize + return h.maxResponseSize } diff --git a/nameserver/dns_test.go b/nameserver/dns_test.go index 20fd7cc7..c7152238 100644 --- a/nameserver/dns_test.go +++ b/nameserver/dns_test.go @@ -93,7 +93,8 @@ func TestTruncateResponse(t *testing.T) { // pick a random max size, truncate response to that, check it maxSize := 512 + rand.Intn(response.Len()-512) - truncateResponse(response, maxSize) + h := handler{maxResponseSize: maxSize} + h.truncateResponse(&dns.Msg{}, response) require.True(t, response.Len() <= maxSize) } } From 69986e77dd8cf53de35980b889bced836a9783bc Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 20 Aug 2015 18:46:55 +0100 Subject: [PATCH 2/8] cosmetic --- nameserver/dns.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/nameserver/dns.go b/nameserver/dns.go index 849881a1..23aefda7 100644 --- a/nameserver/dns.go +++ b/nameserver/dns.go @@ -166,8 +166,8 @@ func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) { ip := addr.IP4() response.Answer[i] = &dns.A{Hdr: header, A: ip} } - shuffleAnswers(&response.Answer) + h.truncateResponse(req, &response) h.ns.debugf("response: %+v", response) @@ -255,17 +255,6 @@ func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) { h.errorResponse(req, dns.RcodeServerFailure, w) } -func shuffleAnswers(answers *[]dns.RR) { - if len(*answers) <= 1 { - return - } - - for i := range *answers { - j := rand.Intn(i + 1) - (*answers)[i], (*answers)[j] = (*answers)[j], (*answers)[i] - } -} - func (h *handler) truncateResponse(req, response *dns.Msg) { maxSize := h.getMaxResponseSize(req) if len(response.Answer) <= 1 || maxSize <= 0 { @@ -296,3 +285,14 @@ func (h *handler) getMaxResponseSize(req *dns.Msg) int { } return h.maxResponseSize } + +func shuffleAnswers(answers *[]dns.RR) { + if len(*answers) <= 1 { + return + } + + for i := range *answers { + j := rand.Intn(i + 1) + (*answers)[i], (*answers)[j] = (*answers)[j], (*answers)[i] + } +} From 7c7c30292032931f674fad328a28a01689e7c632 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 20 Aug 2015 19:17:03 +0100 Subject: [PATCH 3/8] extract response construction --- nameserver/dns.go | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/nameserver/dns.go b/nameserver/dns.go index 23aefda7..c633af2a 100644 --- a/nameserver/dns.go +++ b/nameserver/dns.go @@ -149,29 +149,22 @@ func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) { return } - response := dns.Msg{} - response.RecursionAvailable = true - response.Authoritative = true - response.SetReply(req) - response.Answer = make([]dns.RR, len(addrs)) - header := dns.RR_Header{ Name: req.Question[0].Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: h.ttl, } - + answers := make([]dns.RR, len(addrs)) for i, addr := range addrs { ip := addr.IP4() - response.Answer[i] = &dns.A{Hdr: header, A: ip} + answers[i] = &dns.A{Hdr: header, A: ip} } - shuffleAnswers(&response.Answer) - - h.truncateResponse(req, &response) + shuffleAnswers(&answers) + response := h.makeResponse(req, answers) h.ns.debugf("response: %+v", response) - if err := w.WriteMsg(&response); err != nil { + if err := w.WriteMsg(response); err != nil { h.ns.infof("error responding: %v", err) } } @@ -196,27 +189,20 @@ func (h *handler) handleReverse(w dns.ResponseWriter, req *dns.Msg) { return } - response := dns.Msg{} - response.RecursionAvailable = true - response.Authoritative = true - response.SetReply(req) - header := dns.RR_Header{ Name: req.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: h.ttl, } - - response.Answer = []dns.RR{&dns.PTR{ + answers := []dns.RR{&dns.PTR{ Hdr: header, Ptr: hostname, }} - h.truncateResponse(req, &response) - + response := h.makeResponse(req, answers) h.ns.debugf("response: %+v", response) - if err := w.WriteMsg(&response); err != nil { + if err := w.WriteMsg(response); err != nil { h.ns.infof("error responding: %v", err) } } @@ -255,6 +241,16 @@ func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) { h.errorResponse(req, dns.RcodeServerFailure, w) } +func (h *handler) makeResponse(req *dns.Msg, answers []dns.RR) *dns.Msg { + response := &dns.Msg{} + response.SetReply(req) + response.RecursionAvailable = true + response.Authoritative = true + response.Answer = answers + h.truncateResponse(req, response) + return response +} + func (h *handler) truncateResponse(req, response *dns.Msg) { maxSize := h.getMaxResponseSize(req) if len(response.Answer) <= 1 || maxSize <= 0 { From 322441d5c8ea802480cc8067120b3d7fdb34774d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 20 Aug 2015 19:24:36 +0100 Subject: [PATCH 4/8] inline truncateResponse --- nameserver/dns.go | 12 +++--------- nameserver/dns_test.go | 11 +++++------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/nameserver/dns.go b/nameserver/dns.go index c633af2a..cf2eefb6 100644 --- a/nameserver/dns.go +++ b/nameserver/dns.go @@ -247,19 +247,12 @@ func (h *handler) makeResponse(req *dns.Msg, answers []dns.RR) *dns.Msg { response.RecursionAvailable = true response.Authoritative = true response.Answer = answers - h.truncateResponse(req, response) - return response -} -func (h *handler) truncateResponse(req, response *dns.Msg) { maxSize := h.getMaxResponseSize(req) if len(response.Answer) <= 1 || maxSize <= 0 { - return + return response } - // take a copy of answers, as we're going to mutate response - answers := response.Answer - // search for smallest i that is too big i := sort.Search(len(response.Answer), func(i int) bool { // return true if too big @@ -268,11 +261,12 @@ func (h *handler) truncateResponse(req, response *dns.Msg) { }) if i == len(answers) { response.Answer = answers - return + return response } response.Answer = answers[:i] response.Truncated = true + return response } func (h *handler) getMaxResponseSize(req *dns.Msg) int { diff --git a/nameserver/dns_test.go b/nameserver/dns_test.go index c7152238..9352b24c 100644 --- a/nameserver/dns_test.go +++ b/nameserver/dns_test.go @@ -83,18 +83,17 @@ func TestTruncateResponse(t *testing.T) { } for i := 0; i < 10000; i++ { - // generate a random msg - response := &dns.Msg{} + // generate a random answer set numAnswers := 40 + rand.Intn(200) - response.Answer = make([]dns.RR, numAnswers) + answers := make([]dns.RR, numAnswers) for j := 0; j < numAnswers; j++ { - response.Answer[j] = &dns.A{Hdr: header, A: address.Address(j).IP4()} + answers[j] = &dns.A{Hdr: header, A: address.Address(j).IP4()} } // pick a random max size, truncate response to that, check it - maxSize := 512 + rand.Intn(response.Len()-512) + maxSize := 512 + rand.Intn(2*512) h := handler{maxResponseSize: maxSize} - h.truncateResponse(&dns.Msg{}, response) + response := h.makeResponse(&dns.Msg{}, answers) require.True(t, response.Len() <= maxSize) } } From 2ea27ec08f95785e91ace7552c3faec5b3a7d47d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 20 Aug 2015 19:30:44 +0100 Subject: [PATCH 5/8] simplify response truncation --- nameserver/dns.go | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/nameserver/dns.go b/nameserver/dns.go index cf2eefb6..a436aac6 100644 --- a/nameserver/dns.go +++ b/nameserver/dns.go @@ -246,26 +246,24 @@ func (h *handler) makeResponse(req *dns.Msg, answers []dns.RR) *dns.Msg { response.SetReply(req) response.RecursionAvailable = true response.Authoritative = true - response.Answer = answers maxSize := h.getMaxResponseSize(req) - if len(response.Answer) <= 1 || maxSize <= 0 { - return response - } - - // search for smallest i that is too big - i := sort.Search(len(response.Answer), func(i int) bool { - // return true if too big - response.Answer = answers[:i+1] - return response.Len() > maxSize - }) - if i == len(answers) { + if len(answers) <= 1 || maxSize <= 0 { response.Answer = answers return response } + // search for smallest i that is too big + i := sort.Search(len(answers), func(i int) bool { + // return true if too big + response.Answer = answers[:i+1] + return response.Len() > maxSize + }) + response.Answer = answers[:i] - response.Truncated = true + if i < len(answers) { + response.Truncated = true + } return response } From 9a7abe68fd8fc8e7773a7a3add270210440c9e70 Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 20 Aug 2015 20:18:56 +0100 Subject: [PATCH 6/8] optimise response construction when no truncation is required no need to do a binary search if all the answers fit --- nameserver/dns.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nameserver/dns.go b/nameserver/dns.go index a436aac6..e927cf99 100644 --- a/nameserver/dns.go +++ b/nameserver/dns.go @@ -246,10 +246,10 @@ func (h *handler) makeResponse(req *dns.Msg, answers []dns.RR) *dns.Msg { response.SetReply(req) response.RecursionAvailable = true response.Authoritative = true + response.Answer = answers maxSize := h.getMaxResponseSize(req) - if len(answers) <= 1 || maxSize <= 0 { - response.Answer = answers + if len(answers) <= 1 || maxSize <= 0 || response.Len() <= maxSize { return response } From 0b5f5fd74eaa7707d184b11496810bdf311cea3b Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Thu, 20 Aug 2015 20:24:12 +0100 Subject: [PATCH 7/8] extract response writing --- nameserver/dns.go | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/nameserver/dns.go b/nameserver/dns.go index e927cf99..7e4b9fb0 100644 --- a/nameserver/dns.go +++ b/nameserver/dns.go @@ -162,11 +162,7 @@ func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) { } shuffleAnswers(&answers) - response := h.makeResponse(req, answers) - h.ns.debugf("response: %+v", response) - if err := w.WriteMsg(response); err != nil { - h.ns.infof("error responding: %v", err) - } + h.respond(w, h.makeResponse(req, answers)) } func (h *handler) handleReverse(w dns.ResponseWriter, req *dns.Msg) { @@ -200,11 +196,7 @@ func (h *handler) handleReverse(w dns.ResponseWriter, req *dns.Msg) { Ptr: hostname, }} - response := h.makeResponse(req, answers) - h.ns.debugf("response: %+v", response) - if err := w.WriteMsg(response); err != nil { - h.ns.infof("error responding: %v", err) - } + h.respond(w, h.makeResponse(req, answers)) } func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) { @@ -227,14 +219,11 @@ func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) { h.ns.debugf("error trying %s: %v", server, err) continue } - h.ns.debugf("response: %+v", response) response.Id = req.Id if response.Len() > h.getMaxResponseSize(req) { response.Compress = true } - if err := w.WriteMsg(response); err != nil { - h.ns.infof("error responding: %v", err) - } + h.respond(w, response) return } @@ -274,6 +263,13 @@ func (h *handler) getMaxResponseSize(req *dns.Msg) int { return h.maxResponseSize } +func (h *handler) respond(w dns.ResponseWriter, response *dns.Msg) { + h.ns.debugf("response: %+v", response) + if err := w.WriteMsg(response); err != nil { + h.ns.infof("error responding: %v", err) + } +} + func shuffleAnswers(answers *[]dns.RR) { if len(*answers) <= 1 { return From 2284bc21203ad13bd67baae059fe337eeeabfd6d Mon Sep 17 00:00:00 2001 From: Matthias Radestock Date: Fri, 21 Aug 2015 12:09:22 +0100 Subject: [PATCH 8/8] move error response construction to handler and implement it in terms of existing functions, plus provide a convenience method for sending the rather common NameError. --- nameserver/dns.go | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/nameserver/dns.go b/nameserver/dns.go index 7e4b9fb0..438210e3 100644 --- a/nameserver/dns.go +++ b/nameserver/dns.go @@ -100,18 +100,6 @@ func (d *DNSServer) Stop() error { return nil } -func (d *DNSServer) errorResponse(r *dns.Msg, code int, w dns.ResponseWriter) { - m := dns.Msg{} - m.SetReply(r) - m.RecursionAvailable = true - m.Rcode = code - - d.ns.debugf("error response: %+v", m) - if err := w.WriteMsg(&m); err != nil { - d.ns.infof("error responding: %v", err) - } -} - type handler struct { *DNSServer maxResponseSize int @@ -134,7 +122,7 @@ func (d *DNSServer) createMux(client *dns.Client, defaultMaxResponseSize int) *d func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) { h.ns.debugf("local request: %+v", *req) if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypeA { - h.errorResponse(req, dns.RcodeNameError, w) + h.nameError(w, req) return } @@ -145,7 +133,7 @@ func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) { addrs := h.ns.Lookup(hostname) if len(addrs) == 0 { - h.errorResponse(req, dns.RcodeNameError, w) + h.nameError(w, req) return } @@ -168,14 +156,14 @@ func (h *handler) handleLocal(w dns.ResponseWriter, req *dns.Msg) { func (h *handler) handleReverse(w dns.ResponseWriter, req *dns.Msg) { h.ns.debugf("reverse request: %+v", *req) if len(req.Question) != 1 || req.Question[0].Qtype != dns.TypePTR { - h.errorResponse(req, dns.RcodeNameError, w) + h.nameError(w, req) return } ipStr := strings.TrimSuffix(req.Question[0].Name, "."+reverseDNSdomain) ip, err := address.ParseIP(ipStr) if err != nil { - h.errorResponse(req, dns.RcodeNameError, w) + h.nameError(w, req) return } @@ -227,7 +215,7 @@ func (h *handler) handleRecursive(w dns.ResponseWriter, req *dns.Msg) { return } - h.errorResponse(req, dns.RcodeServerFailure, w) + h.respond(w, h.makeErrorResponse(req, dns.RcodeServerFailure)) } func (h *handler) makeResponse(req *dns.Msg, answers []dns.RR) *dns.Msg { @@ -256,11 +244,12 @@ func (h *handler) makeResponse(req *dns.Msg, answers []dns.RR) *dns.Msg { return response } -func (h *handler) getMaxResponseSize(req *dns.Msg) int { - if opt := req.IsEdns0(); opt != nil { - return int(opt.UDPSize()) - } - return h.maxResponseSize +func (h *handler) makeErrorResponse(req *dns.Msg, code int) *dns.Msg { + response := &dns.Msg{} + response.SetReply(req) + response.RecursionAvailable = true + response.Rcode = code + return response } func (h *handler) respond(w dns.ResponseWriter, response *dns.Msg) { @@ -270,6 +259,17 @@ func (h *handler) respond(w dns.ResponseWriter, response *dns.Msg) { } } +func (h *handler) nameError(w dns.ResponseWriter, req *dns.Msg) { + h.respond(w, h.makeErrorResponse(req, dns.RcodeNameError)) +} + +func (h *handler) getMaxResponseSize(req *dns.Msg) int { + if opt := req.IsEdns0(); opt != nil { + return int(opt.UDPSize()) + } + return h.maxResponseSize +} + func shuffleAnswers(answers *[]dns.RR) { if len(*answers) <= 1 { return