summaryrefslogtreecommitdiffstats
path: root/tests/dnstap
diff options
context:
space:
mode:
Diffstat (limited to 'tests/dnstap')
-rw-r--r--tests/dnstap/meson.build11
-rw-r--r--tests/dnstap/src/dnstap-test/config14
-rw-r--r--tests/dnstap/src/dnstap-test/main.go247
-rwxr-xr-xtests/dnstap/src/dnstap-test/run.sh31
-rw-r--r--tests/dnstap/src/dnstap-test/vendor/manifest55
5 files changed, 358 insertions, 0 deletions
diff --git a/tests/dnstap/meson.build b/tests/dnstap/meson.build
new file mode 100644
index 0000000..1dfd87e
--- /dev/null
+++ b/tests/dnstap/meson.build
@@ -0,0 +1,11 @@
+
+# note: it will be skipped if 'go' is missing (and marked so)
+test('dnstap',
+ find_program('./src/dnstap-test/run.sh'),
+ args: [ sbin_dir / 'kresd' ],
+ suite: [ 'postinstall', 'dnstap' ],
+ timeout: 120, # it may need to fetch go packages, etc.
+ # it takes relatively long time
+ kwargs: meson.version().version_compare('<0.52') ? {} : { 'priority': 5 },
+)
+
diff --git a/tests/dnstap/src/dnstap-test/config b/tests/dnstap/src/dnstap-test/config
new file mode 100644
index 0000000..5f15308
--- /dev/null
+++ b/tests/dnstap/src/dnstap-test/config
@@ -0,0 +1,14 @@
+-- SPDX-License-Identifier: GPL-3.0-or-later
+modules = {
+ 'hints',
+ dnstap = {
+ socket_path = "dnstap.sock",
+ client = {
+ log_queries = true,
+ log_responses = true,
+ }
+ }
+}
+hints['fake1.localdomain'] = '1.2.3.4'
+hints['fake2.localdomain'] = '1.2.3.5'
+hints['fake3.localdomain'] = '1.2.3.6'
diff --git a/tests/dnstap/src/dnstap-test/main.go b/tests/dnstap/src/dnstap-test/main.go
new file mode 100644
index 0000000..c04b4c1
--- /dev/null
+++ b/tests/dnstap/src/dnstap-test/main.go
@@ -0,0 +1,247 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "github.com/cloudflare/dns"
+ dnstap "github.com/dnstap/golang-dnstap"
+ "github.com/golang/protobuf/proto"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "os"
+ "os/exec"
+ "strings"
+ "time"
+)
+
+var (
+ kresdArgs = []string{
+ "-n",
+ "-q",
+ }
+)
+
+func qnameFromFrame(b []byte) (string, error) {
+ dt := &dnstap.Dnstap{}
+ var name string
+ if err := proto.Unmarshal(b, dt); err != nil {
+ return name, err
+ }
+
+ var msg_raw []byte
+ m := dt.Message
+ if *m.Type == dnstap.Message_CLIENT_QUERY {
+ msg_raw = m.QueryMessage
+ } else if *m.Type == dnstap.Message_CLIENT_RESPONSE {
+ msg_raw = m.ResponseMessage
+ } else {
+ return name, fmt.Errorf("incorrect message type: %v", *m.Type)
+ }
+
+ if msg_raw == nil {
+ return name, fmt.Errorf("no message payload")
+ }
+ if err := dns.IsMsg(msg_raw); err != nil {
+ return name, err
+ }
+ var msg dns.Msg
+ if err := msg.Unpack(msg_raw); err != nil {
+ return name, err
+ }
+ if len(msg.Question) < 1 {
+ return name, fmt.Errorf("question empty")
+ }
+ return msg.Question[0].Name, nil
+}
+
+func listenOn() (net.Addr, *os.File, error) {
+ udpConn, err := net.ListenUDP("udp", &net.UDPAddr{
+ IP: net.ParseIP("127.0.0.1"),
+ Port: 0,
+ })
+ if err != nil {
+ return nil, nil, err
+ }
+
+ file, err := udpConn.File()
+ if err != nil {
+ return nil, nil, err
+ }
+ return udpConn.LocalAddr(), file, nil
+}
+
+func runKresd(ctx context.Context, path, configFile string, grace time.Duration) (chan bool, error) {
+ ch := make(chan bool)
+ kresdArgs = append(kresdArgs, "-c"+configFile)
+ // we have 1 object in ExtraFiles with index 0
+ // child fd will be 3 + i = 3
+ kresdArgs = append(kresdArgs, "-S3")
+
+ file := ctx.Value("file").(*os.File)
+ debug := ctx.Value("debug").(bool)
+
+ cmd := exec.CommandContext(ctx, path, kresdArgs...)
+ cmd.ExtraFiles = []*os.File{file}
+
+ var stdout, stderr io.ReadCloser
+ var err error
+ if debug {
+ stdout, err = cmd.StdoutPipe()
+ if err != nil {
+ log.Printf("stdoutpipe: %v\n", err)
+ return ch, err
+ }
+
+ stderr, err = cmd.StderrPipe()
+ if err != nil {
+ log.Printf("stderrpipe: %v\n", err)
+ return ch, err
+ }
+ }
+
+ go func() {
+ status := false
+ defer func() {
+ ch <- status // kresd done
+ }()
+ if err := cmd.Start(); err != nil {
+ log.Printf("start: %v\n", err)
+ return
+ }
+ time.Sleep(grace)
+ ch <- true // Started kresd
+
+ if debug {
+ s, err := ioutil.ReadAll(stdout)
+ if err != nil {
+ log.Printf("readall: %v\n", err)
+ return
+ }
+ if len(s) > 0 {
+ fmt.Printf("stdout:\n%s\n", s)
+ }
+
+ s, err = ioutil.ReadAll(stderr)
+ if err != nil {
+ log.Printf("readall: %v\n", err)
+ return
+ }
+ if len(s) > 0 {
+ fmt.Printf("stderr:\n%s\n", s)
+ }
+ }
+
+ if err := cmd.Wait(); err != nil && err.Error() != "signal: killed" {
+ log.Printf("wait: %v\n", err)
+ return
+ }
+ status = true
+ }()
+ return ch, nil
+}
+
+func main() {
+ var (
+ unixSocket = flag.String("u", "dnstap.sock", "dnstap socket")
+ kresdPath = flag.String("cmd", "kresd", "kresd path")
+ configFile = flag.String("c", "config", "config file")
+ qnames = flag.String("q", ".", "list of comma separated zones")
+ grace = flag.String("g", "1s", "Time to wait for daemon start")
+ timeout = flag.String("t", "60s", "Test Timeout")
+ debug = flag.Bool("d", false, "Debug")
+ )
+
+ flag.Parse()
+
+ kresdStartGracePeriod, err := time.ParseDuration(*grace)
+ if err != nil {
+ panic(err)
+ }
+
+ testTimeout, err := time.ParseDuration(*timeout)
+ if err != nil {
+ panic(err)
+ }
+
+ input, err := dnstap.NewFrameStreamSockInputFromPath(*unixSocket)
+ if err != nil {
+ panic(err)
+ }
+
+ output := make(chan []byte)
+ go input.ReadInto(output)
+
+ ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+
+ // Create a UDP listening socket on random port
+ // FD will be passed on to kresd
+ addr, file, err := listenOn()
+ if err != nil {
+ panic(err)
+ }
+ if *debug {
+ log.Printf("listen addr:%v", addr)
+ }
+ ctx = context.WithValue(ctx, "file", file)
+ ctx = context.WithValue(ctx, "debug", *debug)
+
+ ch, err := runKresd(ctx, *kresdPath, *configFile, kresdStartGracePeriod)
+ if err != nil {
+ panic(err)
+ }
+
+ log.Printf("Waiting for kresd to start\n")
+ status := <-ch
+ if !status {
+ os.Exit(1) // error starting
+ }
+
+ go func() {
+ parts := strings.Split(*qnames, ",")
+
+ if len(parts) == 0 {
+ log.Printf("qname count is 0")
+ }
+ for _, name := range parts {
+ m := new(dns.Msg)
+ fqdn := dns.Fqdn(name)
+ m.SetQuestion(fqdn, dns.TypeA)
+ c := new(dns.Client)
+ resp, _, err := c.Exchange(m, fmt.Sprintf("%v", addr))
+ if err != nil {
+ log.Printf("%v\n", err)
+ os.Exit(1) // Test Failed
+ }
+ if *debug {
+ log.Printf("Response: %v", resp)
+ }
+
+ for range "QR" { // Checking Query and Response is the same ATM
+ o := <-output
+ if *debug {
+ log.Printf("raw dnstap:%v", o)
+ }
+ dtName, err := qnameFromFrame(o)
+ if err != nil {
+ log.Printf("%v\n", err)
+ os.Exit(1)
+ }
+ if fqdn != dtName {
+ log.Printf("expected %v got %v", fqdn, dtName)
+ os.Exit(1) // Test failed
+ }
+ log.Printf("matched qname: %v", dtName)
+ }
+ }
+ cancel() // Send signal to close daemon
+ }()
+
+ status = <-ch
+ if !status {
+ os.Exit(1) // error in wait
+ }
+ log.Printf("Tested OK\n")
+}
diff --git a/tests/dnstap/src/dnstap-test/run.sh b/tests/dnstap/src/dnstap-test/run.sh
new file mode 100755
index 0000000..2f32ea1
--- /dev/null
+++ b/tests/dnstap/src/dnstap-test/run.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+set -e
+KRESD_CMD=$1
+MESON_BUILD_ROOT=$(pwd)
+mkdir -p tests/dnstap
+export GOPATH=$MESON_BUILD_ROOT/tests/dnstap
+cd "$(dirname $0)"
+DNSTAP_TEST=dnstap-test
+
+if [ -z "$GITLAB_CI" ]; then
+ type -P go >/dev/null || exit 77
+ echo "Building the dnstap test and its dependencies..."
+ # some packages may be missing on the system right now
+ go get github.com/{FiloSottile/gvt,cloudflare/dns,dnstap/golang-dnstap,golang/protobuf/proto}
+else
+ # In CI we've prebuilt dependencies into the default GOPATH.
+ # We're in a scratch container, so we just add the dnstap test inside.
+ export GOPATH=/root/go
+fi
+DTAP=$GOPATH/src/$DNSTAP_TEST
+rm -f $DTAP && ln -s $(realpath ..)/$DNSTAP_TEST $DTAP
+go install $DNSTAP_TEST
+
+
+CONFIG=$(realpath ./config)
+ZONES="fake1.localdomain,fake2.localdomain,fake3.localdomain"
+TIMEOUT=60s
+GRACE=5s
+cd $MESON_BUILD_ROOT/tests/dnstap # don't leave stuff like *.mdb in ./.
+$GOPATH/bin/$DNSTAP_TEST -c $CONFIG -cmd $KRESD_CMD -q $ZONES -t $TIMEOUT -g $GRACE -d
+
diff --git a/tests/dnstap/src/dnstap-test/vendor/manifest b/tests/dnstap/src/dnstap-test/vendor/manifest
new file mode 100644
index 0000000..27c1dec
--- /dev/null
+++ b/tests/dnstap/src/dnstap-test/vendor/manifest
@@ -0,0 +1,55 @@
+{
+ "version": 0,
+ "dependencies": [
+ {
+ "importpath": "github.com/cloudflare/dns",
+ "repository": "https://github.com/cloudflare/dns",
+ "vcs": "git",
+ "revision": "e20ffa3da443071c7b3d164dec5b1f80dfb2ecf3",
+ "branch": "master",
+ "notests": true
+ },
+ {
+ "importpath": "github.com/dnstap/golang-dnstap",
+ "repository": "https://github.com/dnstap/golang-dnstap",
+ "vcs": "git",
+ "revision": "0145fd8482619f9c04788c7ba4e96cdeef64a041",
+ "branch": "master",
+ "notests": true
+ },
+ {
+ "importpath": "github.com/farsightsec/golang-framestream",
+ "repository": "https://github.com/farsightsec/golang-framestream",
+ "vcs": "git",
+ "revision": "b600ccf606747139c84b6d69b5c3988164db4d42",
+ "branch": "master",
+ "notests": true
+ },
+ {
+ "importpath": "github.com/golang/protobuf/proto",
+ "repository": "https://github.com/golang/protobuf",
+ "vcs": "git",
+ "revision": "8ee79997227bf9b34611aee7946ae64735e6fd93",
+ "branch": "master",
+ "path": "/proto",
+ "notests": true
+ },
+ {
+ "importpath": "github.com/golang/protobuf/ptypes/any",
+ "repository": "https://github.com/golang/protobuf",
+ "vcs": "git",
+ "revision": "8ee79997227bf9b34611aee7946ae64735e6fd93",
+ "branch": "master",
+ "path": "ptypes/any",
+ "notests": true
+ },
+ {
+ "importpath": "github.com/miekg/dns",
+ "repository": "https://github.com/miekg/dns",
+ "vcs": "git",
+ "revision": "f4d2b086946a624202dc59e6a43f72e8f3f02bc1",
+ "branch": "master",
+ "notests": true
+ }
+ ]
+} \ No newline at end of file