diff options
Diffstat (limited to 'plugin/dnstap/msg')
-rw-r--r-- | plugin/dnstap/msg/msg.go | 168 | ||||
-rw-r--r-- | plugin/dnstap/msg/msg_test.go | 42 | ||||
-rw-r--r-- | plugin/dnstap/msg/wrapper.go | 26 |
3 files changed, 236 insertions, 0 deletions
diff --git a/plugin/dnstap/msg/msg.go b/plugin/dnstap/msg/msg.go new file mode 100644 index 000000000..07930929e --- /dev/null +++ b/plugin/dnstap/msg/msg.go @@ -0,0 +1,168 @@ +// Package msg helps to build a dnstap Message. +package msg + +import ( + "errors" + "net" + "strconv" + "time" + + tap "github.com/dnstap/golang-dnstap" + "github.com/miekg/dns" +) + +// Builder helps to build Data by being aware of the dnstap plugin configuration. +type Builder struct { + Full bool + Data +} + +// AddrMsg parses the info of net.Addr and dns.Msg. +func (b *Builder) AddrMsg(a net.Addr, m *dns.Msg) (err error) { + err = b.RemoteAddr(a) + if err != nil { + return + } + return b.Msg(m) +} + +// Msg parses the info of dns.Msg. +func (b *Builder) Msg(m *dns.Msg) (err error) { + if b.Full { + err = b.Pack(m) + } + return +} + +// Data helps to build a dnstap Message. +// It can be transformed into the actual Message using this package. +type Data struct { + Packed []byte + SocketProto tap.SocketProtocol + SocketFam tap.SocketFamily + Address []byte + Port uint32 + TimeSec uint64 +} + +// HostPort decodes into Data any string returned by dnsutil.ParseHostPortOrFile. +func (d *Data) HostPort(addr string) error { + ip, port, err := net.SplitHostPort(addr) + if err != nil { + return err + } + p, err := strconv.ParseUint(port, 10, 32) + if err != nil { + return err + } + d.Port = uint32(p) + + if ip := net.ParseIP(ip); ip != nil { + d.Address = []byte(ip) + if ip := ip.To4(); ip != nil { + d.SocketFam = tap.SocketFamily_INET + } else { + d.SocketFam = tap.SocketFamily_INET6 + } + return nil + } + return errors.New("not an ip address") +} + +// RemoteAddr parses the information about the remote address into Data. +func (d *Data) RemoteAddr(remote net.Addr) error { + switch addr := remote.(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 +} + +// Pack encodes the DNS message into Data. +func (d *Data) Pack(m *dns.Msg) error { + packed, err := m.Pack() + if err != nil { + return err + } + d.Packed = packed + return nil +} + +// Epoch returns the epoch time in seconds. +func Epoch() uint64 { + return uint64(time.Now().Unix()) +} + +// Epoch sets the dnstap message epoch. +func (d *Data) Epoch() { + d.TimeSec = Epoch() +} + +// ToClientResponse transforms Data into a client response message. +func (d *Data) ToClientResponse() *tap.Message { + t := tap.Message_CLIENT_RESPONSE + return &tap.Message{ + Type: &t, + SocketFamily: &d.SocketFam, + SocketProtocol: &d.SocketProto, + ResponseTimeSec: &d.TimeSec, + ResponseMessage: d.Packed, + QueryAddress: d.Address, + QueryPort: &d.Port, + } +} + +// ToClientQuery transforms Data into a client query message. +func (d *Data) ToClientQuery() *tap.Message { + t := tap.Message_CLIENT_QUERY + return &tap.Message{ + Type: &t, + SocketFamily: &d.SocketFam, + SocketProtocol: &d.SocketProto, + QueryTimeSec: &d.TimeSec, + QueryMessage: d.Packed, + QueryAddress: d.Address, + QueryPort: &d.Port, + } +} + +// ToOutsideQuery transforms the data into a forwarder or resolver query message. +func (d *Data) ToOutsideQuery(t tap.Message_Type) *tap.Message { + return &tap.Message{ + Type: &t, + SocketFamily: &d.SocketFam, + SocketProtocol: &d.SocketProto, + QueryTimeSec: &d.TimeSec, + QueryMessage: d.Packed, + ResponseAddress: d.Address, + ResponsePort: &d.Port, + } +} + +// ToOutsideResponse transforms the data into a forwarder or resolver response message. +func (d *Data) ToOutsideResponse(t tap.Message_Type) *tap.Message { + return &tap.Message{ + Type: &t, + SocketFamily: &d.SocketFam, + SocketProtocol: &d.SocketProto, + ResponseTimeSec: &d.TimeSec, + ResponseMessage: d.Packed, + ResponseAddress: d.Address, + ResponsePort: &d.Port, + } +} diff --git a/plugin/dnstap/msg/msg_test.go b/plugin/dnstap/msg/msg_test.go new file mode 100644 index 000000000..649659a80 --- /dev/null +++ b/plugin/dnstap/msg/msg_test.go @@ -0,0 +1,42 @@ +package msg + +import ( + "net" + "reflect" + "testing" + + "github.com/coredns/coredns/plugin/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.RemoteAddr(r.W.RemoteAddr()); 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/plugin/dnstap/msg/wrapper.go b/plugin/dnstap/msg/wrapper.go new file mode 100644 index 000000000..a74c604d8 --- /dev/null +++ b/plugin/dnstap/msg/wrapper.go @@ -0,0 +1,26 @@ +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, + } +} + +// Marshal encodes the message to a binary dnstap payload. +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 +} |