From 403d42ce200f3c2074c521e4287728f2b8d2bf0d Mon Sep 17 00:00:00 2001 From: peiranliushop <33286233+peiranliushop@users.noreply.github.com> Date: Sat, 23 Mar 2019 05:45:21 -0400 Subject: [PATCH] Support logging to dnstap in forward plugin (#2703) --- plugin/dnstap/test/helpers.go | 7 ---- plugin/forward/dnstap.go | 61 +++++++++++++++++++++++++++++++++ plugin/forward/dnstap_test.go | 63 +++++++++++++++++++++++++++++++++++ plugin/forward/forward.go | 7 ++-- 4 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 plugin/forward/dnstap.go create mode 100644 plugin/forward/dnstap_test.go diff --git a/plugin/dnstap/test/helpers.go b/plugin/dnstap/test/helpers.go index 4f71a5a83..5f498d59f 100644 --- a/plugin/dnstap/test/helpers.go +++ b/plugin/dnstap/test/helpers.go @@ -1,7 +1,6 @@ package test import ( - "context" "net" "reflect" @@ -10,12 +9,6 @@ import ( tap "github.com/dnstap/golang-dnstap" ) -// Context is a message trap. -type Context struct { - context.Context - TrapTapper -} - // TestingData returns the Data matching coredns/test.ResponseWriter. func TestingData() (d *msg.Builder) { d = &msg.Builder{ diff --git a/plugin/forward/dnstap.go b/plugin/forward/dnstap.go new file mode 100644 index 000000000..7866aa39b --- /dev/null +++ b/plugin/forward/dnstap.go @@ -0,0 +1,61 @@ +package forward + +import ( + "context" + "time" + + "github.com/coredns/coredns/plugin/dnstap" + "github.com/coredns/coredns/plugin/dnstap/msg" + "github.com/coredns/coredns/request" + + tap "github.com/dnstap/golang-dnstap" + "github.com/miekg/dns" +) + +func toDnstap(ctx context.Context, host string, f *Forward, state request.Request, reply *dns.Msg, start time.Time) error { + tapper := dnstap.TapperFromContext(ctx) + if tapper == nil { + return nil + } + // Query + b := msg.New().Time(start).HostPort(host) + opts := f.opts + t := "" + switch { + case opts.forceTCP: // TCP flag has precedence over UDP flag + t = "tcp" + case opts.preferUDP: + t = "udp" + default: + t = state.Proto() + } + + if t == "tcp" { + b.SocketProto = tap.SocketProtocol_TCP + } else { + b.SocketProto = tap.SocketProtocol_UDP + } + + if tapper.Pack() { + b.Msg(state.Req) + } + m, err := b.ToOutsideQuery(tap.Message_FORWARDER_QUERY) + if err != nil { + return err + } + tapper.TapMessage(m) + + // Response + if reply != nil { + if tapper.Pack() { + b.Msg(reply) + } + m, err := b.Time(time.Now()).ToOutsideResponse(tap.Message_FORWARDER_RESPONSE) + if err != nil { + return err + } + tapper.TapMessage(m) + } + + return nil +} diff --git a/plugin/forward/dnstap_test.go b/plugin/forward/dnstap_test.go new file mode 100644 index 000000000..c86ee8c75 --- /dev/null +++ b/plugin/forward/dnstap_test.go @@ -0,0 +1,63 @@ +package forward + +import ( + "context" + "testing" + "time" + + "github.com/coredns/coredns/plugin/dnstap" + "github.com/coredns/coredns/plugin/dnstap/msg" + "github.com/coredns/coredns/plugin/dnstap/test" + mwtest "github.com/coredns/coredns/plugin/test" + "github.com/coredns/coredns/request" + + tap "github.com/dnstap/golang-dnstap" + "github.com/miekg/dns" +) + +func testCase(t *testing.T, f *Forward, q, r *dns.Msg, datq, datr *msg.Builder) { + tapq, _ := datq.ToOutsideQuery(tap.Message_FORWARDER_QUERY) + tapr, _ := datr.ToOutsideResponse(tap.Message_FORWARDER_RESPONSE) + tapper := test.TrapTapper{} + ctx := dnstap.ContextWithTapper(context.TODO(), &tapper) + err := toDnstap(ctx, "10.240.0.1:40212", f, + request.Request{W: &mwtest.ResponseWriter{}, Req: q}, r, time.Now()) + if err != nil { + t.Fatal(err) + } + if len(tapper.Trap) != 2 { + t.Fatalf("Messages: %d", len(tapper.Trap)) + } + if !test.MsgEqual(tapper.Trap[0], tapq) { + t.Errorf("Want: %v\nhave: %v", tapq, tapper.Trap[0]) + } + if !test.MsgEqual(tapper.Trap[1], tapr) { + t.Errorf("Want: %v\nhave: %v", tapr, tapper.Trap[1]) + } +} + +func TestDnstap(t *testing.T) { + q := mwtest.Case{Qname: "example.org", Qtype: dns.TypeA}.Msg() + r := mwtest.Case{ + Qname: "example.org.", Qtype: dns.TypeA, + Answer: []dns.RR{ + mwtest.A("example.org. 3600 IN A 10.0.0.1"), + }, + }.Msg() + tapq, tapr := test.TestingData(), test.TestingData() + fu := New() + fu.opts.preferUDP = true + testCase(t, fu, q, r, tapq, tapr) + tapq.SocketProto = tap.SocketProtocol_TCP + tapr.SocketProto = tap.SocketProtocol_TCP + ft := New() + ft.opts.forceTCP = true + testCase(t, ft, q, r, tapq, tapr) +} + +func TestNoDnstap(t *testing.T) { + err := toDnstap(context.TODO(), "", nil, request.Request{}, nil, time.Now()) + if err != nil { + t.Fatal(err) + } +} diff --git a/plugin/forward/forward.go b/plugin/forward/forward.go index 0b043e070..c7f7a83c4 100644 --- a/plugin/forward/forward.go +++ b/plugin/forward/forward.go @@ -74,7 +74,7 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg i := 0 list := f.List() deadline := time.Now().Add(defaultTimeout) - + start := time.Now() for time.Now().Before(deadline) { if i >= len(list) { // reached the end of list, reset to begin @@ -126,6 +126,7 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg if child != nil { child.Finish() } + taperr := toDnstap(ctx, proxy.addr, f, state, ret, start) upstreamErr = err @@ -147,11 +148,11 @@ func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg formerr := state.ErrorMessage(dns.RcodeFormatError) w.WriteMsg(formerr) - return 0, nil + return 0, taperr } w.WriteMsg(ret) - return 0, nil + return 0, taperr } if upstreamErr != nil {