Allow debug queries to etcd middleware (#150)
With this you can retreive the raw data that the etcd middleware used to create the reply. The debug data is put in TXT records that are stuffed in the CH classs. This is only enabled if you specify `debug` in the etcd stanza. You can retrieve it by prefixing your query with 'o-o.debug.' For instance: ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost -p 1053 SRV o-o.debug.production.*.skydns.local ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47798 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;o-o.debug.production.*.skydns.local. IN SRV ;; ANSWER SECTION: production.*.skydns.local. 154 IN SRV 10 50 8080 service1.example.com. production.*.skydns.local. 154 IN SRV 10 50 8080 service2.example.com. ;; ADDITIONAL SECTION: skydns.local.skydns.east.production.rails.1. 154 CH TXT "service1.example.com:8080(10,0,,false)[0,]" skydns.local.skydns.west.production.rails.2. 154 CH TXT "service2.example.com:8080(10,0,,false)[0,]"
This commit is contained in:
parent
d35394a8df
commit
c30671f4c0
9 changed files with 323 additions and 62 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
38
middleware/etcd/debug.go
Normal file
38
middleware/etcd/debug.go
Normal file
|
@ -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
|
||||
}
|
147
middleware/etcd/debug_test.go
Normal file
147
middleware/etcd/debug_test.go
Normal file
|
@ -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"),
|
||||
},
|
||||
},
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue