Commit 4cfd19c7 authored by Miek Gieben's avatar Miek Gieben Committed by GitHub

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
parent a1b9f96d
...@@ -37,7 +37,7 @@ etcd [ZONES...] { ...@@ -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: ...@@ -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]`.
......
...@@ -4,7 +4,6 @@ package etcd ...@@ -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 ( ...@@ -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 ( ...@@ -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) ( ...@@ -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 { ...@@ -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 ( ...@@ -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 { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) ...@@ -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 ...@@ -2,6 +2,7 @@ package httpproxy
import ( import (
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"strings" "strings"
"testing" "testing"
...@@ -9,7 +10,9 @@ import ( ...@@ -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) { ...@@ -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 ( ...@@ -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
......
package etcd package debug
import "strings" import "strings"
const debugName = "o-o.debug." const Name = "o-o.debug."
// isDebug checks if name is a debugging name, i.e. starts with 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 // 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. // name with o-o.debug. stripped off. Must be called with name lowercased.
func isDebug(name string) string { func IsDebug(name string) string {
if len(name) == len(debugName) { if len(name) == len(Name) {
return "" return ""
} }
name = strings.ToLower(name) name = strings.ToLower(name)
debug := strings.HasPrefix(name, debugName) debug := strings.HasPrefix(name, Name)
if !debug { if !debug {
return "" return ""
} }
return name[len(debugName):] return name[len(Name):]
} }
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 ...@@ -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 ( ...@@ -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"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment