aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/setup/etcd.go4
-rw-r--r--middleware/etcd/README.md8
-rw-r--r--middleware/etcd/debug.go38
-rw-r--r--middleware/etcd/debug_test.go147
-rw-r--r--middleware/etcd/etcd.go2
-rw-r--r--middleware/etcd/handler.go59
-rw-r--r--middleware/etcd/lookup.go91
-rw-r--r--middleware/etcd/msg/service.go32
-rw-r--r--middleware/state.go4
9 files changed, 323 insertions, 62 deletions
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.<zone>. 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
)