diff --git a/core/setup/etcd.go b/core/setup/etcd.go index 8e7740e12..80df2ae4d 100644 --- a/core/setup/etcd.go +++ b/core/setup/etcd.go @@ -68,6 +68,8 @@ func etcdParse(c *Controller) (etcd.Etcd, bool, error) { switch c.Val() { case "stubzones": stubzones = true + case "debug": + etc.Debug = true case "path": if !c.NextArg() { return etcd.Etcd{}, false, c.ArgErr() @@ -103,6 +105,8 @@ func etcdParse(c *Controller) (etcd.Etcd, bool, error) { switch c.Val() { case "stubzones": stubzones = true + case "debug": + etc.Debug = true case "path": if !c.NextArg() { return etcd.Etcd{}, false, c.ArgErr() diff --git a/middleware/etcd/README.md b/middleware/etcd/README.md index f70135837..f5a373811 100644 --- a/middleware/etcd/README.md +++ b/middleware/etcd/README.md @@ -27,6 +27,7 @@ etcd [zones...] { endpoint endpoint... upstream address... tls cert key cacert + debug } ~~~ @@ -38,6 +39,13 @@ etcd [zones...] { pointing to external names. If you want CoreDNS to act as a proxy for clients you'll need to add the proxy middleware. * `tls` followed the cert, key and the CA's cert filenames. +* `debug` allow debug queries. Prefix the name with `o-o.debug.` to reveive extra information in the + additional section of the reply in the form of text records: + + skydns.test.skydns.dom.a. 300 CH TXT "127.0.0.1:0(10,0,,false)[0,]" + + This shows the complete key as the owername, the rdata of the TXT record has: + `host:port(priority,weight,txt content,mail)[targetstrip,group]`. ## Examples diff --git a/middleware/etcd/debug.go b/middleware/etcd/debug.go new file mode 100644 index 000000000..b8ca3344f --- /dev/null +++ b/middleware/etcd/debug.go @@ -0,0 +1,38 @@ +package etcd + +import ( + "strings" + + "github.com/miekg/coredns/middleware/etcd/msg" + + "github.com/miekg/dns" +) + +const debugName = "o-o.debug." + +// isDebug checks if name is a debugging name, i.e. starts with o-o.debug. +// it return the empty string if it is not a debug message, otherwise it will return the +// name with o-o.debug. stripped off. +func isDebug(name string) string { + if len(name) == len(debugName) { + return "" + } + debug := strings.HasPrefix(name, debugName) + if !debug { + return "" + } + return name[len(debugName):] +} + +// servicesToTxt puts debug in TXT RRs. +func servicesToTxt(debug []msg.Service) []dns.RR { + if debug == nil { + return nil + } + + rr := make([]dns.RR, len(debug)) + for i, d := range debug { + rr[i] = d.RR() + } + return rr +} diff --git a/middleware/etcd/debug_test.go b/middleware/etcd/debug_test.go new file mode 100644 index 000000000..9e50b6930 --- /dev/null +++ b/middleware/etcd/debug_test.go @@ -0,0 +1,147 @@ +package etcd + +import ( + "sort" + "testing" + + "github.com/miekg/coredns/middleware" + "github.com/miekg/coredns/middleware/etcd/msg" + "github.com/miekg/coredns/middleware/test" + + "github.com/miekg/dns" +) + +func TestisDebug(t *testing.T) { + if ok := isDebug("o-o.debug.miek.nl."); ok != "miek.nl." { + t.Errorf("expected o-o.debug.miek.nl. to be debug") + } + if ok := isDebug("o-o.Debug.miek.nl."); ok != "miek.nl." { + t.Errorf("expected o-o.Debug.miek.nl. to be debug") + } + if ok := isDebug("i-o.Debug.miek.nl."); ok != "" { + t.Errorf("expected i-o.Debug.miek.nl. to be non-debug") + } + if ok := isDebug("i-o.Debug."); ok != "" { + t.Errorf("expected o-o.Debug. to be non-debug") + } +} + +func TestDebugLookup(t *testing.T) { + for _, serv := range servicesDebug { + set(t, etc, serv.Key, 0, serv) + defer delete(t, etc, serv.Key) + } + etc.Debug = true + defer func() { etc.Debug = false }() + for _, tc := range dnsTestCasesDebug { + m := tc.Msg() + + rec := middleware.NewResponseRecorder(&test.ResponseWriter{}) + _, err := etc.ServeDNS(ctx, rec, m) + if err != nil { + t.Errorf("expected no error, got %v\n", err) + continue + } + resp := rec.Msg() + + sort.Sort(test.RRSet(resp.Answer)) + sort.Sort(test.RRSet(resp.Ns)) + sort.Sort(test.RRSet(resp.Extra)) + + if !test.Header(t, tc, resp) { + t.Logf("%v\n", resp) + continue + } + if !test.Section(t, tc, test.Answer, resp.Answer) { + t.Logf("%v\n", resp) + } + if !test.Section(t, tc, test.Ns, resp.Ns) { + t.Logf("%v\n", resp) + } + if !test.Section(t, tc, test.Extra, resp.Extra) { + t.Logf("%v\n", resp) + } + } +} + +func TestDebugLookupFalse(t *testing.T) { + for _, serv := range servicesDebug { + set(t, etc, serv.Key, 0, serv) + defer delete(t, etc, serv.Key) + } + for _, tc := range dnsTestCasesDebugFalse { + m := tc.Msg() + + rec := middleware.NewResponseRecorder(&test.ResponseWriter{}) + _, err := etc.ServeDNS(ctx, rec, m) + if err != nil { + t.Errorf("expected no error, got %v\n", err) + continue + } + resp := rec.Msg() + + sort.Sort(test.RRSet(resp.Answer)) + sort.Sort(test.RRSet(resp.Ns)) + sort.Sort(test.RRSet(resp.Extra)) + + if !test.Header(t, tc, resp) { + t.Logf("%v\n", resp) + continue + } + if !test.Section(t, tc, test.Answer, resp.Answer) { + t.Logf("%v\n", resp) + } + if !test.Section(t, tc, test.Ns, resp.Ns) { + t.Logf("%v\n", resp) + } + if !test.Section(t, tc, test.Extra, resp.Extra) { + t.Logf("%v\n", resp) + } + } +} + +var servicesDebug = []*msg.Service{ + {Host: "127.0.0.1", Key: "a.dom.skydns.test."}, + {Host: "127.0.0.2", Key: "b.sub.dom.skydns.test."}, +} + +var dnsTestCasesDebug = []test.Case{ + { + Qname: "o-o.debug.dom.skydns.test.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("dom.skydns.test. 300 IN A 127.0.0.1"), + test.A("dom.skydns.test. 300 IN A 127.0.0.2"), + }, + Extra: []dns.RR{ + test.TXT(`skydns.test.skydns.dom.a. 300 CH TXT "127.0.0.1:0(10,0,,false)[0,]"`), + test.TXT(`skydns.test.skydns.dom.sub.b. 300 CH TXT "127.0.0.2:0(10,0,,false)[0,]"`), + }, + }, + { + Qname: "o-o.debug.dom.skydns.test.", Qtype: dns.TypeTXT, + Ns: []dns.RR{ + test.SOA("skydns.test. 300 IN SOA ns.dns.skydns.test. hostmaster.skydns.test. 1463943291 7200 1800 86400 60"), + }, + Extra: []dns.RR{ + test.TXT(`skydns.test.skydns.dom.a. 300 CH TXT "127.0.0.1:0(10,0,,false)[0,]"`), + test.TXT(`skydns.test.skydns.dom.sub.b. 300 CH TXT "127.0.0.2:0(10,0,,false)[0,]"`), + }, + }, +} + +var dnsTestCasesDebugFalse = []test.Case{ + { + Qname: "o-o.debug.dom.skydns.test.", Qtype: dns.TypeA, + Rcode: dns.RcodeNameError, + Ns: []dns.RR{ + test.SOA("skydns.test. 300 IN SOA ns.dns.skydns.test. hostmaster.skydns.test. 1463943291 7200 1800 86400 60"), + }, + }, + { + Qname: "o-o.debug.dom.skydns.test.", Qtype: dns.TypeTXT, + Rcode: dns.RcodeNameError, + Ns: []dns.RR{ + test.SOA("skydns.test. 300 IN SOA ns.dns.skydns.test. hostmaster.skydns.test. 1463943291 7200 1800 86400 60"), + }, + }, +} diff --git a/middleware/etcd/etcd.go b/middleware/etcd/etcd.go index 38eac7ab3..eb0eb5c43 100644 --- a/middleware/etcd/etcd.go +++ b/middleware/etcd/etcd.go @@ -24,6 +24,8 @@ type Etcd struct { Ctx context.Context Inflight *singleflight.Group Stubmap *map[string]proxy.Proxy // List of proxies for stub resolving. + Debug bool // Do we allow debug queries. + debug string // Should we return debugging information, if so, contains original qname. } // Records looks up records in etcd. If exact is true, it will lookup just diff --git a/middleware/etcd/handler.go b/middleware/etcd/handler.go index 38e1b51cd..ac0f701dd 100644 --- a/middleware/etcd/handler.go +++ b/middleware/etcd/handler.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/miekg/coredns/middleware" + "github.com/miekg/coredns/middleware/etcd/msg" "github.com/miekg/dns" "golang.org/x/net/context" @@ -14,11 +15,18 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i if state.QClass() != dns.ClassINET { return dns.RcodeServerFailure, fmt.Errorf("can only deal with ClassINET") } + name := state.Name() + if e.Debug { + if debug := isDebug(name); debug != "" { + e.debug = r.Question[0].Name + state.Clear() + state.Req.Question[0].Name = debug + } + } // We need to check stubzones first, because we may get a request for a zone we // are not auth. for *but* do have a stubzone forward for. If we do the stubzone // handler will handle the request. - name := state.Name() if e.Stubmap != nil && len(*e.Stubmap) > 0 { for zone, _ := range *e.Stubmap { if middleware.Name(zone).Matches(name) { @@ -36,52 +44,62 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i return e.Next.ServeDNS(ctx, w, r) } - m := new(dns.Msg) - m.SetReply(r) - m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true - var ( records, extra []dns.RR + debug []msg.Service err error ) switch state.Type() { case "A": - records, err = e.A(zone, state, nil) + records, debug, err = e.A(zone, state, nil) case "AAAA": - records, err = e.AAAA(zone, state, nil) + records, debug, err = e.AAAA(zone, state, nil) case "TXT": - records, err = e.TXT(zone, state) + records, debug, err = e.TXT(zone, state) case "CNAME": - records, err = e.CNAME(zone, state) + records, debug, err = e.CNAME(zone, state) case "MX": - records, extra, err = e.MX(zone, state) + records, extra, debug, err = e.MX(zone, state) case "SRV": - records, extra, err = e.SRV(zone, state) + records, extra, debug, err = e.SRV(zone, state) case "SOA": - records = []dns.RR{e.SOA(zone, state)} + records, debug, err = e.SOA(zone, state) case "NS": if state.Name() == zone { - records, extra, err = e.NS(zone, state) + records, extra, debug, err = e.NS(zone, state) break } fallthrough default: - // Do a fake A lookup, so we can distinguish betwen NODATA and NXDOMAIN - _, err = e.A(zone, state, nil) + // Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN + _, debug, err = e.A(zone, state, nil) } + + if e.debug != "" { + // substitute this name with the original when we return the request. + state.Clear() + state.Req.Question[0].Name = e.debug + } + if isEtcdNameError(err) { - return e.Err(zone, dns.RcodeNameError, state) + return e.Err(zone, dns.RcodeNameError, state, debug) } if err != nil { return dns.RcodeServerFailure, err } if len(records) == 0 { - return e.Err(zone, dns.RcodeSuccess, state) + return e.Err(zone, dns.RcodeSuccess, state, debug) } + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true m.Answer = append(m.Answer, records...) m.Extra = append(m.Extra, extra...) + if e.debug != "" { + m.Extra = append(m.Extra, servicesToTxt(debug)...) + } m = dedup(m) state.SizeAndDo(m) @@ -90,11 +108,12 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i return dns.RcodeSuccess, nil } -// NoData write a nodata response to the client. -func (e Etcd) Err(zone string, rcode int, state middleware.State) (int, error) { +// Err write an error response to the client. +func (e Etcd) Err(zone string, rcode int, state middleware.State, debug []msg.Service) (int, error) { m := new(dns.Msg) m.SetRcode(state.Req, rcode) - m.Ns = []dns.RR{e.SOA(zone, state)} + m.Ns, _, _ = e.SOA(zone, state) + m.Extra = servicesToTxt(debug) state.SizeAndDo(m) state.W.WriteMsg(m) return rcode, nil diff --git a/middleware/etcd/lookup.go b/middleware/etcd/lookup.go index 35d4b7226..15b6afe19 100644 --- a/middleware/etcd/lookup.go +++ b/middleware/etcd/lookup.go @@ -12,19 +12,22 @@ import ( "github.com/miekg/dns" ) -func (e Etcd) records(state middleware.State, exact bool) ([]msg.Service, error) { - services, err := e.Records(state.Name(), exact) +func (e Etcd) records(state middleware.State, exact bool) (services, debug []msg.Service, err error) { + services, err = e.Records(state.Name(), exact) if err != nil { - return nil, err + return + } + if e.debug != "" { + debug = services } services = msg.Group(services) - return services, nil + return } -func (e Etcd) A(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, err error) { - services, err := e.records(state, false) +func (e Etcd) A(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, debug []msg.Service, err error) { + services, debug, err := e.records(state, false) if err != nil { - return nil, err + return nil, debug, err } for _, serv := range services { @@ -47,13 +50,14 @@ func (e Etcd) A(zone string, state middleware.State, previousRecords []dns.RR) ( } state1 := copyState(state, serv.Host, state.QType()) - nextRecords, err := e.A(zone, state1, append(previousRecords, newRecord)) + nextRecords, nextDebug, err := e.A(zone, state1, append(previousRecords, newRecord)) if err == nil { // Not only have we found something we should add the CNAME and the IP addresses. if len(nextRecords) > 0 { records = append(records, newRecord) records = append(records, nextRecords...) + debug = append(debug, nextDebug...) } continue } @@ -77,13 +81,13 @@ func (e Etcd) A(zone string, state middleware.State, previousRecords []dns.RR) ( // nodata? } } - return records, nil + return records, debug, nil } -func (e Etcd) AAAA(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, err error) { - services, err := e.records(state, false) +func (e Etcd) AAAA(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, debug []msg.Service, err error) { + services, debug, err := e.records(state, false) if err != nil { - return nil, err + return nil, debug, err } for _, serv := range services { @@ -106,13 +110,14 @@ func (e Etcd) AAAA(zone string, state middleware.State, previousRecords []dns.RR } state1 := copyState(state, serv.Host, state.QType()) - nextRecords, err := e.AAAA(zone, state1, append(previousRecords, newRecord)) + nextRecords, nextDebug, err := e.AAAA(zone, state1, append(previousRecords, newRecord)) if err == nil { // Not only have we found something we should add the CNAME and the IP addresses. if len(nextRecords) > 0 { records = append(records, newRecord) records = append(records, nextRecords...) + debug = append(debug, nextDebug...) } continue } @@ -137,15 +142,15 @@ func (e Etcd) AAAA(zone string, state middleware.State, previousRecords []dns.RR records = append(records, serv.NewAAAA(state.QName(), ip.To16())) } } - return records, nil + return records, debug, nil } // SRV returns SRV records from etcd. // If the Target is not a name but an IP address, a name is created on the fly. -func (e Etcd) SRV(zone string, state middleware.State) (records []dns.RR, extra []dns.RR, err error) { - services, err := e.records(state, false) +func (e Etcd) SRV(zone string, state middleware.State) (records, extra []dns.RR, debug []msg.Service, err error) { + services, debug, err := e.records(state, false) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // Looping twice to get the right weight vs priority @@ -201,9 +206,10 @@ func (e Etcd) SRV(zone string, state middleware.State) (records []dns.RR, extra // Internal name, we should have some info on them, either v4 or v6 // Clients expect a complete answer, because we are a recursor in their view. state1 := copyState(state, srv.Target, dns.TypeA) - addr, e1 := e.A(zone, state1, nil) + addr, debugAddr, e1 := e.A(zone, state1, nil) if e1 == nil { extra = append(extra, addr...) + debug = append(debug, debugAddr...) } // e.AAA(zone, state1, nil) as well...? case ip.To4() != nil: @@ -220,15 +226,15 @@ func (e Etcd) SRV(zone string, state middleware.State) (records []dns.RR, extra extra = append(extra, serv.NewAAAA(srv.Target, ip.To16())) } } - return records, extra, nil + return records, extra, debug, nil } // MX returns MX records from etcd. // If the Target is not a name but an IP address, a name is created on the fly. -func (e Etcd) MX(zone string, state middleware.State) (records []dns.RR, extra []dns.RR, err error) { - services, err := e.records(state, false) +func (e Etcd) MX(zone string, state middleware.State) (records, extra []dns.RR, debug []msg.Service, err error) { + services, debug, err := e.records(state, false) if err != nil { - return nil, nil, err + return nil, nil, debug, err } lookup := make(map[string]bool) @@ -265,9 +271,10 @@ func (e Etcd) MX(zone string, state middleware.State) (records []dns.RR, extra [ } // Internal name state1 := copyState(state, mx.Mx, dns.TypeA) - addr, e1 := e.A(zone, state1, nil) + addr, debugAddr, e1 := e.A(zone, state1, nil) if e1 == nil { extra = append(extra, addr...) + debug = append(debug, debugAddr...) } // e.AAAA as well case ip.To4() != nil: @@ -280,13 +287,13 @@ func (e Etcd) MX(zone string, state middleware.State) (records []dns.RR, extra [ extra = append(extra, serv.NewAAAA(serv.Host, ip.To16())) } } - return records, extra, nil + return records, extra, debug, nil } -func (e Etcd) CNAME(zone string, state middleware.State) (records []dns.RR, err error) { - services, err := e.records(state, true) +func (e Etcd) CNAME(zone string, state middleware.State) (records []dns.RR, debug []msg.Service, err error) { + services, debug, err := e.records(state, true) if err != nil { - return nil, err + return nil, debug, err } if len(services) > 0 { @@ -295,13 +302,13 @@ func (e Etcd) CNAME(zone string, state middleware.State) (records []dns.RR, err records = append(records, serv.NewCNAME(state.QName(), serv.Host)) } } - return records, nil + return records, debug, nil } -func (e Etcd) TXT(zone string, state middleware.State) (records []dns.RR, err error) { - services, err := e.records(state, false) +func (e Etcd) TXT(zone string, state middleware.State) (records []dns.RR, debug []msg.Service, err error) { + services, debug, err := e.records(state, false) if err != nil { - return nil, err + return nil, debug, err } for _, serv := range services { @@ -310,19 +317,19 @@ func (e Etcd) TXT(zone string, state middleware.State) (records []dns.RR, err er } records = append(records, serv.NewTXT(state.QName())) } - return records, nil + return records, debug, nil } -func (e Etcd) NS(zone string, state middleware.State) (records, extra []dns.RR, err error) { +func (e Etcd) NS(zone string, state middleware.State) (records, extra []dns.RR, debug []msg.Service, err error) { // NS record for this zone live in a special place, ns.dns.. Fake our lookup. // only a tad bit fishy... old := state.QName() state.Clear() state.Req.Question[0].Name = "ns.dns." + zone - services, err := e.records(state, false) + services, debug, err := e.records(state, false) if err != nil { - return nil, nil, err + return nil, nil, debug, err } // ... and reset state.Req.Question[0].Name = old @@ -331,7 +338,7 @@ func (e Etcd) NS(zone string, state middleware.State) (records, extra []dns.RR, ip := net.ParseIP(serv.Host) switch { case ip == nil: - return nil, nil, fmt.Errorf("NS record must be an IP address: %s", serv.Host) + return nil, nil, debug, fmt.Errorf("NS record must be an IP address: %s", serv.Host) case ip.To4() != nil: serv.Host = e.Domain(serv.Key) records = append(records, serv.NewNS(state.QName())) @@ -342,13 +349,14 @@ func (e Etcd) NS(zone string, state middleware.State) (records, extra []dns.RR, extra = append(extra, serv.NewAAAA(serv.Host, ip.To16())) } } - return records, extra, nil + return records, extra, debug, nil } // SOA Record returns a SOA record. -func (e Etcd) SOA(zone string, state middleware.State) *dns.SOA { +func (e Etcd) SOA(zone string, state middleware.State) ([]dns.RR, []msg.Service, error) { header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: 300, Class: dns.ClassINET} - return &dns.SOA{Hdr: header, + + soa := &dns.SOA{Hdr: header, Mbox: "hostmaster." + zone, Ns: "ns.dns." + zone, Serial: uint32(time.Now().Unix()), @@ -357,10 +365,10 @@ func (e Etcd) SOA(zone string, state middleware.State) *dns.SOA { Expire: 86400, Minttl: 60, } + // TODO(miek): fake some msg.Service here when returning. + return []dns.RR{soa}, nil, nil } -// TODO(miek): DNSKEY and friends... intercepted by the DNSSEC middleware? - func isDuplicateCNAME(r *dns.CNAME, records []dns.RR) bool { for _, rec := range records { if v, ok := rec.(*dns.CNAME); ok { @@ -372,6 +380,7 @@ func isDuplicateCNAME(r *dns.CNAME, records []dns.RR) bool { return false } +// TODO(miek): Move to middleware? func copyState(state middleware.State, target string, typ uint16) middleware.State { state1 := middleware.State{W: state.W, Req: state.Req.Copy()} state1.Req.Question[0] = dns.Question{dns.Fqdn(target), dns.ClassINET, typ} diff --git a/middleware/etcd/msg/service.go b/middleware/etcd/msg/service.go index 588e7b33c..e851df978 100644 --- a/middleware/etcd/msg/service.go +++ b/middleware/etcd/msg/service.go @@ -1,6 +1,7 @@ package msg import ( + "fmt" "net" "strings" @@ -35,6 +36,37 @@ type Service struct { Key string `json:"-"` } +// RR returns an RR representation of s. It is in a condensed form to minimize space +// when this is returned in a DNS message. +// The RR will look like: +// skydns.local.skydns.east.production.rails.1. 300 CH TXT "service1.example.com:8080(10,0,,false)[0,]" +// etcd Key Ttl Host:Port < see below > +// between parens: (Priority, Weight, Text (only first 200 bytes!), Mail) +// between blockquotes: [TargetStrip,Group] +// If the record is synthesised by CoreDNS (i.e. no lookup in etcd happened): +// +// skydns.local.skydns.east.production.rails.1. 300 CH TXT "service1.example.com:8080(10,0,,false)[0,]" +// +func (s *Service) RR() *dns.TXT { + l := len(s.Text) + if l > 200 { + l = 200 + } + t := new(dns.TXT) + t.Hdr.Class = dns.ClassCHAOS + t.Hdr.Ttl = s.Ttl + t.Hdr.Rrtype = dns.TypeTXT + // TODO(miek): key guaranteerd to be > 1? + t.Hdr.Name = strings.Replace(s.Key[1:], "/", ".", -1) + "." // TODO(miek): slightly more like etcd.Domain() + + t.Txt = make([]string, 1) + t.Txt[0] = fmt.Sprintf("%s:%d(%d,%d,%s,%t)[%d,%s]", + s.Host, s.Port, + s.Priority, s.Weight, s.Text[:l], s.Mail, + s.TargetStrip, s.Group) + return t +} + // NewSRV returns a new SRV record based on the Service. func (s *Service) NewSRV(name string, weight uint16) *dns.SRV { host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip) diff --git a/middleware/state.go b/middleware/state.go index 1028ab2a3..4299641ab 100644 --- a/middleware/state.go +++ b/middleware/state.go @@ -190,7 +190,8 @@ func (s *State) Type() string { return dns.Type(s.Req.Question[0].Qtype).String( func (s *State) QType() uint16 { return s.Req.Question[0].Qtype } // Name returns the name of the question in the request. Note -// this name will always have a closing dot and will be lower cased. +// this name will always have a closing dot and will be lower cased. After a call Name +// the value will be cached. To clear this caching call Clear. func (s *State) Name() string { if s.name != "" { return s.name @@ -222,6 +223,7 @@ func (s *State) Clear() { } const ( + // TODO(miek): make this less awkward. doTrue = 1 doFalse = 2 )