middleware/httpproxy: add debug queries (#446)
* middleware/httproxy: implement debug queries Not too useful at the moment, but o-o.debug queries are supported and return the Comment from dns.google.com. Note that this is not always set. * improve documentation * Testing cleanups
This commit is contained in:
parent
a1b9f96d87
commit
4cfd19c7c9
12 changed files with 108 additions and 55 deletions
|
@ -37,7 +37,7 @@ etcd [ZONES...] {
|
||||||
* **ENDPOINT** the etcd endpoints. Defaults to "http://localhost:2397".
|
* **ENDPOINT** the etcd endpoints. Defaults to "http://localhost:2397".
|
||||||
* `upstream` upstream resolvers to be used resolve external names found in etcd (think CNAMEs)
|
* `upstream` upstream resolvers to be used resolve external names found in etcd (think CNAMEs)
|
||||||
pointing to external names. If you want CoreDNS to act as a proxy for clients, you'll need to add
|
pointing to external names. If you want CoreDNS to act as a proxy for clients, you'll need to add
|
||||||
the proxy middleware. **ADDRESS* can be an IP address, and IP:port or a string pointing to a file
|
the proxy middleware. **ADDRESS** can be an IP address, and IP:port or a string pointing to a file
|
||||||
that is structured as /etc/resolv.conf.
|
that is structured as /etc/resolv.conf.
|
||||||
* `tls` followed the cert, key and the CA's cert filenames.
|
* `tls` followed the cert, key and the CA's cert filenames.
|
||||||
* `debug` allows for debug queries. Prefix the name with `o-o.debug.` to retrieve extra information in the
|
* `debug` allows for debug queries. Prefix the name with `o-o.debug.` to retrieve extra information in the
|
||||||
|
@ -127,7 +127,7 @@ Or with *debug* queries enabled:
|
||||||
When debug queries are enabled CoreDNS will return errors and etcd records encountered during the resolution
|
When debug queries are enabled CoreDNS will return errors and etcd records encountered during the resolution
|
||||||
process in the response. The general form looks like this:
|
process in the response. The general form looks like this:
|
||||||
|
|
||||||
skydns.test.skydns.dom.a. 300 CH TXT "127.0.0.1:0(10,0,,false)[0,]"
|
skydns.test.skydns.dom.a. 0 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:
|
This shows the complete key as the owername, the rdata of the TXT record has:
|
||||||
`host:port(priority,weight,txt content,mail)[targetstrip,group]`.
|
`host:port(priority,weight,txt content,mail)[targetstrip,group]`.
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
package etcd
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
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. Must be called with name lowercased.
|
|
||||||
func isDebug(name string) string {
|
|
||||||
if len(name) == len(debugName) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
name = strings.ToLower(name)
|
|
||||||
debug := strings.HasPrefix(name, debugName)
|
|
||||||
if !debug {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return name[len(debugName):]
|
|
||||||
}
|
|
|
@ -4,7 +4,6 @@ package etcd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/miekg/coredns/middleware/etcd/msg"
|
"github.com/miekg/coredns/middleware/etcd/msg"
|
||||||
|
@ -14,21 +13,6 @@ import (
|
||||||
"github.com/miekg/dns"
|
"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(strings.ToLower("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(strings.ToLower("i-o.Debug.")); ok != "" {
|
|
||||||
t.Errorf("expected o-o.Debug. to be non-debug")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDebugLookup(t *testing.T) {
|
func TestDebugLookup(t *testing.T) {
|
||||||
etc := newEtcdMiddleware()
|
etc := newEtcdMiddleware()
|
||||||
etc.Debugging = true
|
etc.Debugging = true
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/miekg/coredns/middleware"
|
"github.com/miekg/coredns/middleware"
|
||||||
"github.com/miekg/coredns/middleware/etcd/msg"
|
"github.com/miekg/coredns/middleware/etcd/msg"
|
||||||
|
"github.com/miekg/coredns/middleware/pkg/debug"
|
||||||
"github.com/miekg/coredns/middleware/pkg/dnsutil"
|
"github.com/miekg/coredns/middleware/pkg/dnsutil"
|
||||||
"github.com/miekg/coredns/request"
|
"github.com/miekg/coredns/request"
|
||||||
|
|
||||||
|
@ -21,10 +22,10 @@ func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
|
||||||
}
|
}
|
||||||
name := state.Name()
|
name := state.Name()
|
||||||
if e.Debugging {
|
if e.Debugging {
|
||||||
if debug := isDebug(name); debug != "" {
|
if bug := debug.IsDebug(name); bug != "" {
|
||||||
opt.Debug = r.Question[0].Name
|
opt.Debug = r.Question[0].Name
|
||||||
state.Clear()
|
state.Clear()
|
||||||
state.Req.Question[0].Name = debug
|
state.Req.Question[0].Name = bug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,3 +48,24 @@ proxy . dns.google.com {
|
||||||
upstream /etc/resolv.conf
|
upstream /etc/resolv.conf
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
## Debug queries
|
||||||
|
|
||||||
|
Debug queries are enabled by default and currently there is no way to turn them off. When CoreDNS
|
||||||
|
receives a debug queries (i.e. the name is prefixed with `o-o.debug.` a TXT record with Comment from
|
||||||
|
`dns.google.com` is added. Note this is not always set, but sometimes you'll see:
|
||||||
|
|
||||||
|
`dig @localhost -p 1053 mx o-o.debug.example.org`:
|
||||||
|
|
||||||
|
~~~ txt
|
||||||
|
;; OPT PSEUDOSECTION:
|
||||||
|
; EDNS: version: 0, flags:; udp: 4096
|
||||||
|
;; QUESTION SECTION:
|
||||||
|
;o-o.debug.example.org. IN MX
|
||||||
|
|
||||||
|
;; AUTHORITY SECTION:
|
||||||
|
example.org. 1799 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016110711 7200 3600 1209600 3600
|
||||||
|
|
||||||
|
;; ADDITIONAL SECTION:
|
||||||
|
. 0 CH TXT "Response from 199.43.133.53"
|
||||||
|
~~~
|
||||||
|
|
|
@ -12,7 +12,9 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/miekg/coredns/middleware/pkg/debug"
|
||||||
"github.com/miekg/coredns/middleware/proxy"
|
"github.com/miekg/coredns/middleware/proxy"
|
||||||
|
"github.com/miekg/coredns/request"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
@ -30,11 +32,17 @@ type google struct {
|
||||||
|
|
||||||
func newGoogle() *google { return &google{client: newClient(ghost), quit: make(chan bool)} }
|
func newGoogle() *google { return &google{client: newClient(ghost), quit: make(chan bool)} }
|
||||||
|
|
||||||
func (g *google) Exchange(req *dns.Msg) (*dns.Msg, error) {
|
func (g *google) Exchange(state request.Request) (*dns.Msg, error) {
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
|
|
||||||
v.Set("name", req.Question[0].Name)
|
v.Set("name", state.Name())
|
||||||
v.Set("type", fmt.Sprintf("%d", req.Question[0].Qtype))
|
v.Set("type", fmt.Sprintf("%d", state.QType()))
|
||||||
|
|
||||||
|
optDebug := false
|
||||||
|
if bug := debug.IsDebug(state.Name()); bug != "" {
|
||||||
|
optDebug = true
|
||||||
|
v.Set("name", bug)
|
||||||
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
@ -60,12 +68,20 @@ func (g *google) Exchange(req *dns.Msg) (*dns.Msg, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := toMsg(gm)
|
m, debug, err := toMsg(gm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Id = req.Id
|
if optDebug {
|
||||||
|
// reset question
|
||||||
|
m.Question[0].Name = state.QName()
|
||||||
|
// prepend debug RR to the additional section
|
||||||
|
m.Extra = append([]dns.RR{debug}, m.Extra...)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Id = state.Req.Id
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,8 +239,11 @@ func (g *google) do(addr, json string) ([]byte, error) {
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toMsg(g *googleMsg) (*dns.Msg, error) {
|
// toMsg converts a googleMsg into the dns message. The returned RR is the comment disquised as a TXT
|
||||||
|
// record.
|
||||||
|
func toMsg(g *googleMsg) (*dns.Msg, dns.RR, error) {
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
|
m.Response = true
|
||||||
m.Rcode = g.Status
|
m.Rcode = g.Status
|
||||||
m.Truncated = g.TC
|
m.Truncated = g.TC
|
||||||
m.RecursionDesired = g.RD
|
m.RecursionDesired = g.RD
|
||||||
|
@ -243,23 +262,24 @@ func toMsg(g *googleMsg) (*dns.Msg, error) {
|
||||||
for i := 0; i < len(m.Answer); i++ {
|
for i := 0; i < len(m.Answer); i++ {
|
||||||
m.Answer[i], err = toRR(g.Answer[i])
|
m.Answer[i], err = toRR(g.Answer[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < len(m.Ns); i++ {
|
for i := 0; i < len(m.Ns); i++ {
|
||||||
m.Ns[i], err = toRR(g.Authority[i])
|
m.Ns[i], err = toRR(g.Authority[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < len(m.Extra); i++ {
|
for i := 0; i < len(m.Extra); i++ {
|
||||||
m.Extra[i], err = toRR(g.Additional[i])
|
m.Extra[i], err = toRR(g.Additional[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, nil
|
txt, _ := dns.NewRR(". 0 CH TXT " + g.Comment)
|
||||||
|
return m, txt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toRR(g googleRR) (dns.RR, error) {
|
func toRR(g googleRR) (dns.RR, error) {
|
||||||
|
|
|
@ -27,9 +27,9 @@ func (p *Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
state := request.Request{W: w, Req: r}
|
state := request.Request{W: w, Req: r}
|
||||||
|
|
||||||
reply, backendErr := p.e.Exchange(r)
|
reply, backendErr := p.e.Exchange(state)
|
||||||
|
|
||||||
if backendErr == nil {
|
if backendErr == nil && reply != nil {
|
||||||
state.SizeAndDo(reply)
|
state.SizeAndDo(reply)
|
||||||
|
|
||||||
w.WriteMsg(reply)
|
w.WriteMsg(reply)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package httpproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -9,7 +10,9 @@ import (
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSetupChaos(t *testing.T) {
|
func TestSetupHttpproxy(t *testing.T) {
|
||||||
|
log.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
shouldErr bool
|
shouldErr bool
|
||||||
|
@ -55,7 +58,6 @@ func TestSetupChaos(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Logf("%q", err)
|
|
||||||
if !test.shouldErr {
|
if !test.shouldErr {
|
||||||
t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
|
t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,14 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/miekg/coredns/request"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Exchanger is an interface that specifies a type implementing a DNS resolver that
|
// Exchanger is an interface that specifies a type implementing a DNS resolver that
|
||||||
// uses a HTTPS server.
|
// uses a HTTPS server.
|
||||||
type Exchanger interface {
|
type Exchanger interface {
|
||||||
Exchange(*dns.Msg) (*dns.Msg, error)
|
Exchange(request.Request) (*dns.Msg, error)
|
||||||
|
|
||||||
SetUpstream(*simpleUpstream) error
|
SetUpstream(*simpleUpstream) error
|
||||||
OnStartup() error
|
OnStartup() error
|
||||||
|
|
20
middleware/pkg/debug/debug.go
Normal file
20
middleware/pkg/debug/debug.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package debug
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
const Name = "o-o.debug."
|
||||||
|
|
||||||
|
// IsDebug checks if name is a debugging name, i.e. starts with o-o.debug.
|
||||||
|
// it returns the empty string if it is not a debug message, otherwise it will return the
|
||||||
|
// name with o-o.debug. stripped off. Must be called with name lowercased.
|
||||||
|
func IsDebug(name string) string {
|
||||||
|
if len(name) == len(Name) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
debug := strings.HasPrefix(name, Name)
|
||||||
|
if !debug {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return name[len(Name):]
|
||||||
|
}
|
21
middleware/pkg/debug/debug_test.go
Normal file
21
middleware/pkg/debug/debug_test.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package debug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
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(strings.ToLower("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(strings.ToLower("i-o.Debug.")); ok != "" {
|
||||||
|
t.Errorf("expected o-o.Debug. to be non-debug")
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package root
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -14,6 +15,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRoot(t *testing.T) {
|
func TestRoot(t *testing.T) {
|
||||||
|
log.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
// Predefined error substrings
|
// Predefined error substrings
|
||||||
parseErrContent := "Parse error:"
|
parseErrContent := "Parse error:"
|
||||||
unableToAccessErrContent := "Unable to access root path"
|
unableToAccessErrContent := "Unable to access root path"
|
||||||
|
|
Loading…
Add table
Reference in a new issue