diff options
Diffstat (limited to 'middleware/dnstap/msg')
-rw-r--r-- | middleware/dnstap/msg/msg.go | 89 | ||||
-rw-r--r-- | middleware/dnstap/msg/msg_test.go | 42 | ||||
-rw-r--r-- | middleware/dnstap/msg/wrapper.go | 25 |
3 files changed, 156 insertions, 0 deletions
diff --git a/middleware/dnstap/msg/msg.go b/middleware/dnstap/msg/msg.go new file mode 100644 index 000000000..97c7ec7cc --- /dev/null +++ b/middleware/dnstap/msg/msg.go @@ -0,0 +1,89 @@ +// Package msg helps to build a dnstap Message. +package msg + +import ( + "errors" + "net" + "time" + + "github.com/coredns/coredns/request" + + tap "github.com/dnstap/golang-dnstap" + "github.com/miekg/dns" +) + +// Data helps to build a dnstap Message. +// It can be transformed into the actual Message using this package. +type Data struct { + Type tap.Message_Type + Packed []byte + SocketProto tap.SocketProtocol + SocketFam tap.SocketFamily + Address []byte + Port uint32 + TimeSec uint64 +} + +func (d *Data) FromRequest(r request.Request) error { + switch addr := r.W.RemoteAddr().(type) { + case *net.TCPAddr: + d.Address = addr.IP + d.Port = uint32(addr.Port) + d.SocketProto = tap.SocketProtocol_TCP + case *net.UDPAddr: + d.Address = addr.IP + d.Port = uint32(addr.Port) + d.SocketProto = tap.SocketProtocol_UDP + default: + return errors.New("unknown remote address type") + } + + if a := net.IP(d.Address); a.To4() != nil { + d.SocketFam = tap.SocketFamily_INET + } else { + d.SocketFam = tap.SocketFamily_INET6 + } + + return nil +} + +func (d *Data) Pack(m *dns.Msg) error { + packed, err := m.Pack() + if err != nil { + return err + } + d.Packed = packed + return nil +} + +func (d *Data) Epoch() { + d.TimeSec = uint64(time.Now().Unix()) +} + +// Transform the data into a client response message. +func (d *Data) ToClientResponse() *tap.Message { + d.Type = tap.Message_CLIENT_RESPONSE + return &tap.Message{ + Type: &d.Type, + SocketFamily: &d.SocketFam, + SocketProtocol: &d.SocketProto, + ResponseTimeSec: &d.TimeSec, + ResponseMessage: d.Packed, + QueryAddress: d.Address, + QueryPort: &d.Port, + } +} + +// Transform the data into a client query message. +func (d *Data) ToClientQuery() *tap.Message { + d.Type = tap.Message_CLIENT_QUERY + return &tap.Message{ + Type: &d.Type, + SocketFamily: &d.SocketFam, + SocketProtocol: &d.SocketProto, + QueryTimeSec: &d.TimeSec, + QueryMessage: d.Packed, + QueryAddress: d.Address, + QueryPort: &d.Port, + } +} diff --git a/middleware/dnstap/msg/msg_test.go b/middleware/dnstap/msg/msg_test.go new file mode 100644 index 000000000..6a54a9e8c --- /dev/null +++ b/middleware/dnstap/msg/msg_test.go @@ -0,0 +1,42 @@ +package msg + +import ( + "net" + "reflect" + "testing" + + "github.com/coredns/coredns/middleware/test" + "github.com/coredns/coredns/request" + + tap "github.com/dnstap/golang-dnstap" + "github.com/miekg/dns" +) + +func testRequest(t *testing.T, expected Data, r request.Request) { + d := Data{} + if err := d.FromRequest(r); err != nil { + t.Fail() + return + } + if d.SocketProto != expected.SocketProto || + d.SocketFam != expected.SocketFam || + !reflect.DeepEqual(d.Address, expected.Address) || + d.Port != expected.Port { + t.Fatalf("expected: %v, have: %v", expected, d) + return + } +} +func TestRequest(t *testing.T) { + testRequest(t, Data{ + SocketProto: tap.SocketProtocol_UDP, + SocketFam: tap.SocketFamily_INET, + Address: net.ParseIP("10.240.0.1"), + Port: 40212, + }, testingRequest()) +} +func testingRequest() request.Request { + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + m.SetEdns0(4097, true) + return request.Request{W: &test.ResponseWriter{}, Req: m} +} diff --git a/middleware/dnstap/msg/wrapper.go b/middleware/dnstap/msg/wrapper.go new file mode 100644 index 000000000..0cb6a76c0 --- /dev/null +++ b/middleware/dnstap/msg/wrapper.go @@ -0,0 +1,25 @@ +package msg + +import ( + "fmt" + + lib "github.com/dnstap/golang-dnstap" + "github.com/golang/protobuf/proto" +) + +func wrap(m *lib.Message) *lib.Dnstap { + t := lib.Dnstap_MESSAGE + return &lib.Dnstap{ + Type: &t, + Message: m, + } +} + +func Marshal(m *lib.Message) (data []byte, err error) { + data, err = proto.Marshal(wrap(m)) + if err != nil { + err = fmt.Errorf("proto: %s", err) + return + } + return +} |