aboutsummaryrefslogtreecommitdiff
path: root/plugin/dnstap/msg
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/dnstap/msg')
-rw-r--r--plugin/dnstap/msg/msg.go168
-rw-r--r--plugin/dnstap/msg/msg_test.go42
-rw-r--r--plugin/dnstap/msg/wrapper.go26
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
+}