diff --git a/Makefile b/Makefile index c39d5f671f9..dc826732523 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ godeps: go get -u github.com/prometheus/client_golang/prometheus/promhttp go get -u github.com/prometheus/client_golang/prometheus (cd $(GOPATH)/src/github.com/mholt/caddy && git checkout -q v0.11.1) - (cd $(GOPATH)/src/github.com/miekg/dns && git checkout -q v1.1.0) + (cd $(GOPATH)/src/github.com/miekg/dns && git checkout -q v1.1.1) (cd $(GOPATH)/src/github.com/prometheus/client_golang && git checkout -q v0.9.1) .PHONY: travis diff --git a/plugin/backend_lookup.go b/plugin/backend_lookup.go index 090f362156f..95ec467fde8 100644 --- a/plugin/backend_lookup.go +++ b/plugin/backend_lookup.go @@ -425,7 +425,6 @@ func BackendError(b ServiceBackend, zone string, rcode int, state request.Reques m.Authoritative, m.RecursionAvailable = true, true m.Ns, _ = SOA(b, zone, state, opt) - state.SizeAndDo(m) state.W.WriteMsg(m) // Return success as the rcode to signal we have written to the client. return dns.RcodeSuccess, err diff --git a/plugin/chaos/chaos.go b/plugin/chaos/chaos.go index 60b002bdb25..a66c5f003e3 100644 --- a/plugin/chaos/chaos.go +++ b/plugin/chaos/chaos.go @@ -46,7 +46,6 @@ func (c Chaos) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( } m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{trim(hostname)}}} } - state.SizeAndDo(m) w.WriteMsg(m) return 0, nil } diff --git a/plugin/dnssec/handler.go b/plugin/dnssec/handler.go index c8bddc01c47..6153bf33110 100644 --- a/plugin/dnssec/handler.go +++ b/plugin/dnssec/handler.go @@ -33,7 +33,6 @@ func (d Dnssec) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) if qname == z { resp := d.getDNSKEY(state, z, do, server) resp.Authoritative = true - state.SizeAndDo(resp) w.WriteMsg(resp) return dns.RcodeSuccess, nil } diff --git a/plugin/dnssec/handler_test.go b/plugin/dnssec/handler_test.go index 35444eecb51..8e997bca812 100644 --- a/plugin/dnssec/handler_test.go +++ b/plugin/dnssec/handler_test.go @@ -26,7 +26,7 @@ var dnssecTestCases = []test.Case{ test.DNSKEY("miek.nl. 3600 IN DNSKEY 257 3 13 0J8u0XJ9GNGFEBXuAmLu04taHG4"), test.RRSIG("miek.nl. 3600 IN RRSIG DNSKEY 13 2 3600 20160503150844 20160425120844 18512 miek.nl. Iw/kNOyM"), }, - Extra: []dns.RR{test.OPT(4096, true)}, + /* Extra: []dns.RR{test.OPT(4096, true)}, this has moved to the server and can't be test here */ }, } diff --git a/plugin/erratic/erratic.go b/plugin/erratic/erratic.go index f60e605d15c..3b22e225d38 100644 --- a/plugin/erratic/erratic.go +++ b/plugin/erratic/erratic.go @@ -97,7 +97,6 @@ func (e *Erratic) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg time.Sleep(e.duration) } - state.SizeAndDo(m) w.WriteMsg(m) return 0, nil diff --git a/plugin/file/file.go b/plugin/file/file.go index ee4d64da0cb..57ce3fd170d 100644 --- a/plugin/file/file.go +++ b/plugin/file/file.go @@ -51,7 +51,6 @@ func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i m := new(dns.Msg) m.SetReply(r) m.Authoritative, m.RecursionAvailable = true, true - state.SizeAndDo(m) w.WriteMsg(m) log.Infof("Notify from %s for %s: checking transfer", state.IP(), zone) diff --git a/plugin/log/log.go b/plugin/log/log.go index 98f00d76232..d963a134fd1 100644 --- a/plugin/log/log.go +++ b/plugin/log/log.go @@ -43,7 +43,6 @@ func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) } else { answer := new(dns.Msg) answer.SetRcode(r, rc) - state.SizeAndDo(answer) vars.Report(ctx, state, vars.Dropped, rcode.ToString(rc), answer.Len(), time.Now()) diff --git a/plugin/pkg/edns/edns.go b/plugin/pkg/edns/edns.go index 3f0ea5e16d0..d86d6965a3b 100644 --- a/plugin/pkg/edns/edns.go +++ b/plugin/pkg/edns/edns.go @@ -3,10 +3,37 @@ package edns import ( "errors" + "sync" "github.com/miekg/dns" ) +var sup = &supported{m: make(map[uint16]struct{})} + +type supported struct { + m map[uint16]struct{} + sync.RWMutex +} + +// SetSupportedOption adds a new supported option the set of EDNS0 options that we supported. Plugins typically call +// this in their setup code to signal support for a new option. +// By default we supoort: +// dns.EDNS0NSID, dns.EDNS0EXPIRE, dns.EDNS0COOKIE, dns.EDNS0TCPKEEPALIVE, dns.EDNS0PADDING. These +// values are not in this map and checked directly in the server. +func SetSupportedOption(option uint16) { + sup.Lock() + sup.m[option] = struct{}{} + sup.Unlock() +} + +// SupportedOption returns true if e is supported as an extra EDNS0 option. +func SupportedOption(option uint16) bool { + sup.RLock() + _, ok := sup.m[option] + sup.RUnlock() + return ok +} + // Version checks the EDNS version in the request. If error // is nil everything is OK and we can invoke the plugin. If non-nil, the // returned Msg is valid to be returned to the client (and should). For some diff --git a/plugin/rewrite/edns0.go b/plugin/rewrite/edns0.go index e97f04d955e..34aaf3d6772 100644 --- a/plugin/rewrite/edns0.go +++ b/plugin/rewrite/edns0.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/coredns/coredns/plugin/metadata" + "github.com/coredns/coredns/plugin/pkg/edns" "github.com/coredns/coredns/request" "github.com/miekg/dns" @@ -159,6 +160,10 @@ func newEdns0LocalRule(mode, action, code, data string) (*edns0LocalRule, error) return nil, err } } + + // Add this code to the ones the server supports. + edns.SetSupportedOption(uint16(c)) + return &edns0LocalRule{mode: mode, action: action, code: uint16(c), data: decoded}, nil } @@ -172,6 +177,10 @@ func newEdns0VariableRule(mode, action, code, variable string) (*edns0VariableRu if !isValidVariable(variable) { return nil, fmt.Errorf("unsupported variable name %q", variable) } + + // Add this code to the ones the server supports. + edns.SetSupportedOption(uint16(c)) + return &edns0VariableRule{mode: mode, action: action, code: uint16(c), variable: variable}, nil } diff --git a/plugin/whoami/whoami.go b/plugin/whoami/whoami.go index b2ba25e5eec..155880eae03 100644 --- a/plugin/whoami/whoami.go +++ b/plugin/whoami/whoami.go @@ -49,7 +49,6 @@ func (wh Whoami) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) a.Extra = []dns.RR{rr, srv} - state.SizeAndDo(a) w.WriteMsg(a) return 0, nil diff --git a/request/edns0.go b/request/edns0.go new file mode 100644 index 00000000000..07e41c8ff88 --- /dev/null +++ b/request/edns0.go @@ -0,0 +1,31 @@ +package request + +import ( + "github.com/coredns/coredns/plugin/pkg/edns" + + "github.com/miekg/dns" +) + +func supportedOptions(o []dns.EDNS0) []dns.EDNS0 { + var supported = make([]dns.EDNS0, 0, 3) + // For as long as possible try looking up in the map, because that need an Rlock. + for _, opt := range o { + switch code := opt.Option(); code { + case dns.EDNS0NSID: + fallthrough + case dns.EDNS0EXPIRE: + fallthrough + case dns.EDNS0COOKIE: + fallthrough + case dns.EDNS0TCPKEEPALIVE: + fallthrough + case dns.EDNS0PADDING: + supported = append(supported, opt) + default: + if edns.SupportedOption(code) { + supported = append(supported, opt) + } + } + } + return supported +} diff --git a/request/request.go b/request/request.go index 105cd852873..52bf8629b80 100644 --- a/request/request.go +++ b/request/request.go @@ -194,7 +194,7 @@ func (r *Request) Size() int { // SizeAndDo adds an OPT record that the reflects the intent from request. // The returned bool indicated if an record was found and normalised. func (r *Request) SizeAndDo(m *dns.Msg) bool { - o := r.Req.IsEdns0() // TODO(miek): speed this up + o := r.Req.IsEdns0() if o == nil { return false } @@ -208,6 +208,10 @@ func (r *Request) SizeAndDo(m *dns.Msg) bool { mo.SetUDPSize(o.UDPSize()) mo.Hdr.Ttl &= 0xff00 // clear flags + if len(o.Option) > 0 { + o.Option = supportedOptions(o.Option) + } + if odo { mo.SetDo() } @@ -219,6 +223,10 @@ func (r *Request) SizeAndDo(m *dns.Msg) bool { o.SetVersion(0) o.Hdr.Ttl &= 0xff00 // clear flags + if len(o.Option) > 0 { + o.Option = supportedOptions(o.Option) + } + if odo { o.SetDo() } @@ -305,7 +313,6 @@ func (r *Request) Scrub(reply *dns.Msg) *dns.Msg { } if rl <= size { - r.SizeAndDo(reply) return reply } @@ -341,7 +348,6 @@ func (r *Request) Scrub(reply *dns.Msg) *dns.Msg { // this extra m-1 step does make it fit in the client's buffer however. } - r.SizeAndDo(reply) reply.Truncated = true return reply } diff --git a/request/request_test.go b/request/request_test.go index 5e814f76ee8..4411c6a8246 100644 --- a/request/request_test.go +++ b/request/request_test.go @@ -123,10 +123,6 @@ func TestRequestScrubExtraEdns0(t *testing.T) { if reply.Truncated { t.Errorf("Want scrub to not set truncated bit") } - opt := reply.Extra[len(reply.Extra)-1] - if opt.Header().Rrtype != dns.TypeOPT { - t.Errorf("Last RR must be OPT record") - } } func TestRequestScrubExtraRegression(t *testing.T) { @@ -153,10 +149,6 @@ func TestRequestScrubExtraRegression(t *testing.T) { if reply.Truncated { t.Errorf("Want scrub to not set truncated bit") } - opt := reply.Extra[len(reply.Extra)-1] - if opt.Header().Rrtype != dns.TypeOPT { - t.Errorf("Last RR must be OPT record") - } } func TestTruncation(t *testing.T) { diff --git a/request/writer.go b/request/writer.go index ffbbe93e3cd..67be53ebbe7 100644 --- a/request/writer.go +++ b/request/writer.go @@ -15,6 +15,8 @@ func NewScrubWriter(req *dns.Msg, w dns.ResponseWriter) *ScrubWriter { return &S // scrub on the message m and will then write it to the client. func (s *ScrubWriter) WriteMsg(m *dns.Msg) error { state := Request{Req: s.req, W: s.ResponseWriter} + n := state.Scrub(m) + state.SizeAndDo(n) return s.ResponseWriter.WriteMsg(n) } diff --git a/test/edns0_test.go b/test/edns0_test.go new file mode 100644 index 00000000000..6cb1af958e7 --- /dev/null +++ b/test/edns0_test.go @@ -0,0 +1,33 @@ +package test + +import ( + "testing" + + "github.com/miekg/dns" +) + +func TestEDNS0(t *testing.T) { + corefile := `.:0 { + whoami +} +` + + i, udp, _, err := CoreDNSServerAndPorts(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + defer i.Stop() + + m := new(dns.Msg) + m.SetQuestion("example.org.", dns.TypeSOA) + m.SetEdns0(4096, true) + + resp, err := dns.Exchange(m, udp) + if err != nil { + t.Fatalf("Expected to receive reply, but didn't: %v", err) + } + opt := resp.Extra[len(resp.Extra)-1] + if opt.Header().Rrtype != dns.TypeOPT { + t.Errorf("Last RR must be OPT record") + } +}