aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Miek Gieben <miek@miek.nl> 2016-09-17 17:09:05 +0100
committerGravatar GitHub <noreply@github.com> 2016-09-17 17:09:05 +0100
commit30fd224504d7e6fcfa7da027d62d2105ecb6f0c5 (patch)
treed63f32750f56265e13d5a8c98ef840f1c343395f
parented907d33278003072443029fef666232f16e1985 (diff)
downloadcoredns-30fd224504d7e6fcfa7da027d62d2105ecb6f0c5.tar.gz
coredns-30fd224504d7e6fcfa7da027d62d2105ecb6f0c5.tar.zst
coredns-30fd224504d7e6fcfa7da027d62d2105ecb6f0c5.zip
middleware/whoami: add (#264)
Add a new middleware that tells you who you are; IP, port and transport is echoed back. Also some various cleanup and documentation improvements while at it: * ResponseWriter: improve the documentation of these helper functions. * And add an NextHandler for use in tests. Make chaos_test.go and * whoam_test.go use it.
-rw-r--r--core/coredns.go1
-rw-r--r--middleware/chaos/chaos_test.go12
-rw-r--r--middleware/middleware.go4
-rw-r--r--middleware/test/helpers.go10
-rw-r--r--middleware/test/responsewriter.go5
-rw-r--r--middleware/whoami/README.md38
-rw-r--r--middleware/whoami/setup.go28
-rw-r--r--middleware/whoami/setup_test.go19
-rw-r--r--middleware/whoami/whoami.go50
-rw-r--r--middleware/whoami/whoami_test.go62
10 files changed, 219 insertions, 10 deletions
diff --git a/core/coredns.go b/core/coredns.go
index 186c59612..369230492 100644
--- a/core/coredns.go
+++ b/core/coredns.go
@@ -21,6 +21,7 @@ import (
_ "github.com/miekg/coredns/middleware/proxy"
_ "github.com/miekg/coredns/middleware/rewrite"
_ "github.com/miekg/coredns/middleware/secondary"
+ _ "github.com/miekg/coredns/middleware/whoami"
)
// Quiet mode will not show any informative output on initialization.
diff --git a/middleware/chaos/chaos_test.go b/middleware/chaos/chaos_test.go
index 6a3261754..333083187 100644
--- a/middleware/chaos/chaos_test.go
+++ b/middleware/chaos/chaos_test.go
@@ -26,21 +26,21 @@ func TestChaos(t *testing.T) {
expectedErr error
}{
{
- next: genHandler(dns.RcodeSuccess, nil),
+ next: test.NextHandler(dns.RcodeSuccess, nil),
qname: "version.bind",
expectedCode: dns.RcodeSuccess,
expectedReply: version,
expectedErr: nil,
},
{
- next: genHandler(dns.RcodeSuccess, nil),
+ next: test.NextHandler(dns.RcodeSuccess, nil),
qname: "authors.bind",
expectedCode: dns.RcodeSuccess,
expectedReply: "Miek Gieben",
expectedErr: nil,
},
{
- next: genHandler(dns.RcodeSuccess, nil),
+ next: test.NextHandler(dns.RcodeSuccess, nil),
qname: "authors.bind",
qtype: dns.TypeSRV,
expectedCode: dns.RcodeSuccess,
@@ -77,10 +77,4 @@ func TestChaos(t *testing.T) {
}
}
-func genHandler(rcode int, err error) middleware.Handler {
- return middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
- return rcode, err
- })
-}
-
const version = "CoreDNS-001"
diff --git a/middleware/middleware.go b/middleware/middleware.go
index 33990c1be..804209fa9 100644
--- a/middleware/middleware.go
+++ b/middleware/middleware.go
@@ -19,9 +19,13 @@ type (
//
// If ServeDNS writes to the response body, it should return a status
// code. If the status code is not one of the following:
+ //
// * SERVFAIL (dns.RcodeServerFailure)
+ //
// * REFUSED (dns.RecodeRefused)
+ //
// * FORMERR (dns.RcodeFormatError)
+ //
// * NOTIMP (dns.RcodeNotImplemented)
//
// CoreDNS assumes *no* reply has yet been written. All other response
diff --git a/middleware/test/helpers.go b/middleware/test/helpers.go
index 01a6f156b..8ed27261f 100644
--- a/middleware/test/helpers.go
+++ b/middleware/test/helpers.go
@@ -224,6 +224,7 @@ func Section(t *testing.T, tc Case, sect Sect, rr []dns.RR) bool {
return true
}
+// ErrorHanlder returns a Handler that returns ServerFailure error when called.
func ErrorHandler() Handler {
return HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
m := new(dns.Msg)
@@ -233,7 +234,14 @@ func ErrorHandler() Handler {
})
}
-// Copied here to prevent an import cycle.
+// NextHandler returns a Handler that returns rcode and err.
+func NextHandler(rcode int, err error) Handler {
+ return HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
+ return rcode, err
+ })
+}
+
+// Copied here to prevent an import cycle, so that we can define to above handlers.
type (
// HandlerFunc is a convenience type like dns.HandlerFunc, except
// ServeDNS returns an rcode and an error.
diff --git a/middleware/test/responsewriter.go b/middleware/test/responsewriter.go
index fb70d7e8d..925dc7b35 100644
--- a/middleware/test/responsewriter.go
+++ b/middleware/test/responsewriter.go
@@ -6,14 +6,19 @@ import (
"github.com/miekg/dns"
)
+// ResponseWriter is useful for writing tests. It uses some fixed values for the client. The
+// remote will always be 10.240.0.1 and port 40212. The local address is always 127.0.0.1 and
+// port 53.
type ResponseWriter struct{}
+// LocalAddr returns the local address, always 127.0.0.1:53 (UDP).
func (t *ResponseWriter) LocalAddr() net.Addr {
ip := net.ParseIP("127.0.0.1")
port := 53
return &net.UDPAddr{IP: ip, Port: port, Zone: ""}
}
+// RemoteAddr returns the remote address, always 10.240.0.1:40212 (UDP).
func (t *ResponseWriter) RemoteAddr() net.Addr {
ip := net.ParseIP("10.240.0.1")
port := 40212
diff --git a/middleware/whoami/README.md b/middleware/whoami/README.md
new file mode 100644
index 000000000..4ebdac47f
--- /dev/null
+++ b/middleware/whoami/README.md
@@ -0,0 +1,38 @@
+# whoami
+
+whoami returns your local IP address, port and transport used. Your local IP address is returned in
+the additional section as either an A or AAAA record.
+
+The port and transport are included in the additional section as a SRV record, transport can be
+"tcp" or "udp".
+
+~~~ txt
+._<transport>.qname. 0 IN SRV 0 0 <port> .
+~~~
+
+The *whoami* middleware will respond to every A or AAAA query, regardless of the query name.
+
+## Syntax
+
+~~~ txt
+whoami
+~~~
+
+## Examples
+
+~~~ txt
+.:53 {
+ whoami
+}
+~~~
+
+When queried for "example.org A", CoreDNS will respond with:
+
+~~~ txt
+;; QUESTION SECTION:
+;example.org. IN A
+
+;; ADDITIONAL SECTION:
+example.org. 0 IN A 10.240.0.1
+_udp.example.org. 0 IN SRV 0 0 40212
+~~~
diff --git a/middleware/whoami/setup.go b/middleware/whoami/setup.go
new file mode 100644
index 000000000..ad9995085
--- /dev/null
+++ b/middleware/whoami/setup.go
@@ -0,0 +1,28 @@
+package whoami
+
+import (
+ "github.com/miekg/coredns/core/dnsserver"
+ "github.com/miekg/coredns/middleware"
+
+ "github.com/mholt/caddy"
+)
+
+func init() {
+ caddy.RegisterPlugin("whoami", caddy.Plugin{
+ ServerType: "dns",
+ Action: setupWhoami,
+ })
+}
+
+func setupWhoami(c *caddy.Controller) error {
+ c.Next() // 'whoami'
+ if c.NextArg() {
+ return middleware.Error("whoami", c.ArgErr())
+ }
+
+ dnsserver.GetConfig(c).AddMiddleware(func(next dnsserver.Handler) dnsserver.Handler {
+ return Whoami{Next: next}
+ })
+
+ return nil
+}
diff --git a/middleware/whoami/setup_test.go b/middleware/whoami/setup_test.go
new file mode 100644
index 000000000..73db67d88
--- /dev/null
+++ b/middleware/whoami/setup_test.go
@@ -0,0 +1,19 @@
+package whoami
+
+import (
+ "testing"
+
+ "github.com/mholt/caddy"
+)
+
+func TestSetupWhoami(t *testing.T) {
+ c := caddy.NewTestController("dns", `whoami`)
+ if err := setupWhoami(c); err != nil {
+ t.Fatalf("Expected no errors, but got: %v", err)
+ }
+
+ c = caddy.NewTestController("dns", `whoami example.org`)
+ if err := setupWhoami(c); err == nil {
+ t.Fatalf("Expected errors, but got: %v", err)
+ }
+}
diff --git a/middleware/whoami/whoami.go b/middleware/whoami/whoami.go
new file mode 100644
index 000000000..4117db35e
--- /dev/null
+++ b/middleware/whoami/whoami.go
@@ -0,0 +1,50 @@
+package whoami
+
+import (
+ "net"
+ "strconv"
+
+ "github.com/miekg/coredns/middleware"
+ "github.com/miekg/coredns/request"
+
+ "github.com/miekg/dns"
+ "golang.org/x/net/context"
+)
+
+type Whoami struct {
+ Next middleware.Handler
+}
+
+func (wh Whoami) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
+ state := request.Request{W: w, Req: r}
+
+ a := new(dns.Msg)
+ a.SetReply(r)
+ a.Compress = true
+ a.Authoritative = true
+
+ ip := state.IP()
+ var rr dns.RR
+
+ switch state.Family() {
+ case 1:
+ rr = new(dns.A)
+ rr.(*dns.A).Hdr = dns.RR_Header{Name: state.QName(), Rrtype: state.QType(), Class: state.QClass()}
+ rr.(*dns.A).A = net.ParseIP(ip).To4()
+ case 2:
+ rr = new(dns.AAAA)
+ rr.(*dns.AAAA).Hdr = dns.RR_Header{Name: state.QName(), Rrtype: state.QType(), Class: state.QClass()}
+ rr.(*dns.AAAA).AAAA = net.ParseIP(ip)
+ }
+
+ srv := new(dns.SRV)
+ srv.Hdr = dns.RR_Header{Name: "_" + state.Proto() + "." + state.QName(), Rrtype: dns.TypeSRV, Class: state.QClass()}
+ port, _ := strconv.Atoi(state.Port())
+ srv.Port = uint16(port)
+
+ a.Extra = append(a.Extra, rr)
+ a.Extra = append(a.Extra, srv)
+
+ w.WriteMsg(a)
+ return 0, nil
+}
diff --git a/middleware/whoami/whoami_test.go b/middleware/whoami/whoami_test.go
new file mode 100644
index 000000000..a143185ef
--- /dev/null
+++ b/middleware/whoami/whoami_test.go
@@ -0,0 +1,62 @@
+package whoami
+
+import (
+ "testing"
+
+ "github.com/miekg/coredns/middleware"
+ "github.com/miekg/coredns/middleware/pkg/dnsrecorder"
+ "github.com/miekg/coredns/middleware/test"
+
+ "github.com/miekg/dns"
+ "golang.org/x/net/context"
+)
+
+func TestWhoami(t *testing.T) {
+ wh := Whoami{}
+
+ tests := []struct {
+ next middleware.Handler
+ qname string
+ qtype uint16
+ expectedCode int
+ expectedReply []string // ownernames for the records in the additional section.
+ expectedErr error
+ }{
+ {
+ next: test.NextHandler(dns.RcodeSuccess, nil),
+ qname: "example.org",
+ qtype: dns.TypeA,
+ expectedCode: dns.RcodeSuccess,
+ expectedReply: []string{"example.org.", "_udp.example.org."},
+ expectedErr: nil,
+ },
+ }
+
+ ctx := context.TODO()
+
+ for i, tc := range tests {
+ wh.Next = tc.next
+ req := new(dns.Msg)
+ req.SetQuestion(dns.Fqdn(tc.qname), tc.qtype)
+
+ rec := dnsrecorder.New(&test.ResponseWriter{})
+ code, err := wh.ServeDNS(ctx, rec, req)
+
+ t.Logf("%s\n", rec.Msg)
+
+ if err != tc.expectedErr {
+ t.Errorf("Test %d: Expected error %v, but got %v", i, tc.expectedErr, err)
+ }
+ if code != int(tc.expectedCode) {
+ t.Errorf("Test %d: Expected status code %d, but got %d", i, tc.expectedCode, code)
+ }
+ if len(tc.expectedReply) != 0 {
+ for i, expected := range tc.expectedReply {
+ actual := rec.Msg.Extra[i].Header().Name
+ if actual != expected {
+ t.Errorf("Test %d: Expected answer %s, but got %s", i, expected, actual)
+ }
+ }
+ }
+ }
+}