WIP: middleware/dnstap (#711)

middleware/dnstap add
This commit is contained in:
varyoo
2017-07-24 23:12:50 +02:00
committed by Miek Gieben
parent f33b02689c
commit 1b7492be6e
41 changed files with 3537 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
// Package taprw takes a query and intercepts the response.
// It will log both after the response is written.
package taprw
import (
"fmt"
"github.com/coredns/coredns/middleware/dnstap/msg"
"github.com/coredns/coredns/request"
tap "github.com/dnstap/golang-dnstap"
"github.com/miekg/dns"
)
type Taper interface {
TapMessage(m *tap.Message) error
}
// Single request use.
type ResponseWriter struct {
queryData msg.Data
Query *dns.Msg
dns.ResponseWriter
Taper
Pack bool
err error
}
// Check if a dnstap error occured.
// Set during ResponseWriter.Write.
func (w ResponseWriter) DnstapError() error {
return w.err
}
// To be called as soon as possible.
func (w *ResponseWriter) QueryEpoch() {
w.queryData.Epoch()
}
// Write back the response to the client and THEN work on logging the request
// and response to dnstap.
// Dnstap errors to be checked by DnstapError.
func (w *ResponseWriter) WriteMsg(resp *dns.Msg) error {
writeErr := w.ResponseWriter.WriteMsg(resp)
if err := tapQuery(w); err != nil {
w.err = fmt.Errorf("client query: %s", err)
// don't forget to call DnstapError later
}
if writeErr == nil {
if err := tapResponse(w, resp); err != nil {
w.err = fmt.Errorf("client response: %s", err)
}
}
return writeErr
}
func tapQuery(w *ResponseWriter) error {
req := request.Request{W: w.ResponseWriter, Req: w.Query}
if err := w.queryData.FromRequest(req); err != nil {
return err
}
if w.Pack {
if err := w.queryData.Pack(w.Query); err != nil {
return fmt.Errorf("pack: %s", err)
}
}
return w.Taper.TapMessage(w.queryData.ToClientQuery())
}
func tapResponse(w *ResponseWriter, resp *dns.Msg) error {
d := &msg.Data{}
d.Epoch()
req := request.Request{W: w, Req: resp}
if err := d.FromRequest(req); err != nil {
return err
}
if w.Pack {
if err := d.Pack(resp); err != nil {
return fmt.Errorf("pack: %s", err)
}
}
return w.Taper.TapMessage(d.ToClientResponse())
}

View File

@@ -0,0 +1,96 @@
package taprw
import (
"errors"
"testing"
"github.com/coredns/coredns/middleware/dnstap/test"
mwtest "github.com/coredns/coredns/middleware/test"
tap "github.com/dnstap/golang-dnstap"
"github.com/miekg/dns"
)
type TapFailer struct {
}
func (TapFailer) TapMessage(*tap.Message) error {
return errors.New("failed")
}
func TestDnstapError(t *testing.T) {
rw := ResponseWriter{
Query: new(dns.Msg),
ResponseWriter: &mwtest.ResponseWriter{},
Taper: TapFailer{},
}
if err := rw.WriteMsg(new(dns.Msg)); err != nil {
t.Errorf("dnstap error during Write: %s", err)
}
if rw.DnstapError() == nil {
t.Fatal("no dnstap error")
}
}
func testingMsg() (m *dns.Msg) {
m = new(dns.Msg)
m.SetQuestion("example.com.", dns.TypeA)
m.SetEdns0(4097, true)
return
}
func TestClientResponse(t *testing.T) {
trapper := test.TrapTaper{}
rw := ResponseWriter{
Pack: true,
Taper: &trapper,
ResponseWriter: &mwtest.ResponseWriter{},
}
d := test.TestingData()
m := testingMsg()
// will the wire-format msg be reported?
bin, err := m.Pack()
if err != nil {
t.Fatal(err)
return
}
d.Packed = bin
if err := tapResponse(&rw, m); err != nil {
t.Fatal(err)
return
}
want := d.ToClientResponse()
if l := len(trapper.Trap); l != 1 {
t.Fatalf("%d msg trapped", l)
return
}
have := trapper.Trap[0]
if !test.MsgEqual(want, have) {
t.Fatalf("want: %v\nhave: %v", want, have)
}
}
func TestClientQuery(t *testing.T) {
trapper := test.TrapTaper{}
rw := ResponseWriter{
Pack: false, // no binary this time
Taper: &trapper,
ResponseWriter: &mwtest.ResponseWriter{},
Query: testingMsg(),
}
if err := tapQuery(&rw); err != nil {
t.Fatal(err)
return
}
want := test.TestingData().ToClientQuery()
if l := len(trapper.Trap); l != 1 {
t.Fatalf("%d msg trapped", l)
return
}
have := trapper.Trap[0]
if !test.MsgEqual(want, have) {
t.Fatalf("want: %v\nhave: %v", want, have)
}
}