summaryrefslogtreecommitdiff
path: root/regress
diff options
context:
space:
mode:
authoranton <anton@cvs.openbsd.org>2018-05-09 19:34:54 +0000
committeranton <anton@cvs.openbsd.org>2018-05-09 19:34:54 +0000
commit7e77aebf8e945918f4cfd50b938bb25c2ba3367c (patch)
tree012266f5825ce0573581bf468c8beb8b6ade5e66 /regress
parent6e371a04bcb2d032354db049986cf7f4f4811983 (diff)
Add rebound regress tests, including a simple DNS server used as the upstream
resolver with support for stubbing responses. Input and ok bluhm@
Diffstat (limited to 'regress')
-rw-r--r--regress/usr.sbin/rebound/Makefile53
-rw-r--r--regress/usr.sbin/rebound/cache.sh32
-rw-r--r--regress/usr.sbin/rebound/localhost.sh30
-rw-r--r--regress/usr.sbin/rebound/rebound-ns.c270
-rw-r--r--regress/usr.sbin/rebound/record.sh75
-rw-r--r--regress/usr.sbin/rebound/reload.sh54
-rw-r--r--regress/usr.sbin/rebound/run.sh90
7 files changed, 604 insertions, 0 deletions
diff --git a/regress/usr.sbin/rebound/Makefile b/regress/usr.sbin/rebound/Makefile
new file mode 100644
index 00000000000..2c738c2c972
--- /dev/null
+++ b/regress/usr.sbin/rebound/Makefile
@@ -0,0 +1,53 @@
+# $OpenBSD: Makefile,v 1.1 2018/05/09 19:34:53 anton Exp $
+
+REBOUND= /usr/sbin/rebound
+
+PROG= rebound-ns
+CFLAGS+= -Wall -Wextra
+
+# IP address prefix.
+PREFIX= 172.16.0
+# IP address suffixes.
+IFS= 1 2 3
+
+.if ! (make(clean) || make(cleandir) || make(obj) || make(rebound-ns))
+PREREQ1!= for v in ${IFS}; do \
+ ifconfig lo$$v >/dev/null 2>&1 && echo lo$$v; \
+ done; \
+ exit 0
+PREREQ2!= pgrep rebound || exit 0
+
+. if ! empty(PREREQ1)
+regress:
+ @echo "${PREREQ1}: interface(s) already exists"
+ @echo "SKIPPED"
+. elif ! empty(PREREQ2)
+regress:
+ @echo "rebound: already running"
+ @echo "SKIPPED"
+. else
+.BEGIN:
+. for i in ${IFS}
+ -${SUDO} ifconfig lo${i} create
+ -${SUDO} ifconfig lo${i} inet ${PREFIX}.${i} netmask 255.255.255.0
+. endfor
+
+.END:
+. for i in ${IFS}
+ -${SUDO} ifconfig lo${i} destroy
+. endfor
+. endif
+.endif
+
+REGRESS_TARGETS+= cache localhost record reload
+
+.SUFFIXES: .sh
+
+.sh: rebound-ns
+ @echo '\n======== ${@} ========'
+ ${SUDO} sh ${.CURDIR}/run.sh \
+ -n ${.OBJDIR}/rebound-ns -r ${REBOUND} \
+ ${IFS:C/^/-a ${PREFIX}./} \
+ -- ${.CURDIR}/${@}.sh
+
+.include <bsd.regress.mk>
diff --git a/regress/usr.sbin/rebound/cache.sh b/regress/usr.sbin/rebound/cache.sh
new file mode 100644
index 00000000000..9315abb45cf
--- /dev/null
+++ b/regress/usr.sbin/rebound/cache.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# $OpenBSD: cache.sh,v 1.1 2018/05/09 19:34:53 anton Exp $
+#
+# Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+echo nameserver $ADDR1 >$CONF
+
+$ENV $NS -l $ADDR1 A t1.example.com. 1.1.1.1
+
+$ENV $REBOUND -c $CONF -l $ADDR2
+
+R=$(resolve -t A t1.example.com $ADDR2)
+asserteq "t1.example.com has address 1.1.1.1" "$R" \
+ "must be resolved using rebound-ns"
+
+pkillw "^${NS}"
+
+R=$(resolve -t A t1.example.com $ADDR2)
+asserteq "t1.example.com has address 1.1.1.1" "$R" "must be cached by rebound"
diff --git a/regress/usr.sbin/rebound/localhost.sh b/regress/usr.sbin/rebound/localhost.sh
new file mode 100644
index 00000000000..6bc2483c348
--- /dev/null
+++ b/regress/usr.sbin/rebound/localhost.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# $OpenBSD: localhost.sh,v 1.1 2018/05/09 19:34:53 anton Exp $
+#
+# Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+cat <<! >$CONF
+nameserver 127.0.0.1
+nameserver ${ADDR1}
+!
+
+$ENV $NS -l $ADDR1 A t1.example.com. 1.1.1.1
+
+$ENV $REBOUND -c $CONF -l $ADDR2
+
+R=$(resolve -t A t1.example.com $ADDR2)
+asserteq "t1.example.com has address 1.1.1.1" "$R" \
+ "must be resolved using rebound-ns since localhost is skipped"
diff --git a/regress/usr.sbin/rebound/rebound-ns.c b/regress/usr.sbin/rebound/rebound-ns.c
new file mode 100644
index 00000000000..4cca4dbb970
--- /dev/null
+++ b/regress/usr.sbin/rebound/rebound-ns.c
@@ -0,0 +1,270 @@
+/* $OpenBSD: rebound-ns.c,v 1.1 2018/05/09 19:34:53 anton Exp $ */
+/*
+ * Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct dnsheader {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+};
+
+union dnsmsg {
+ struct dnsheader *hdr;
+ uint8_t *u8;
+ uint16_t *u16;
+ uint32_t *u32;
+};
+
+struct dnsrecord {
+ unsigned char req[256];
+ size_t reqlen;
+ unsigned char resp[256];
+ size_t resplen;
+};
+
+static const struct dnsrecord *lookup(struct dnsrecord *, size_t, union dnsmsg,
+ size_t);
+static const unsigned char *lowercase(union dnsmsg, size_t *);
+static void newdnsrecord(struct dnsrecord *, const char *, const char *,
+ const char *);
+static __dead void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+#define NRECORDS 32
+ static struct dnsrecord records[NRECORDS];
+ unsigned char recvbuf[65536];
+ union {
+ struct sockaddr a;
+ struct sockaddr_in i;
+ } bindaddr, fromaddr;
+ const char *bindname = "127.0.0.1";
+ const struct dnsrecord *resp;
+ union dnsmsg req;
+ socklen_t fromlen;
+ ssize_t n;
+ size_t nrecords = 0;
+ int c, fd;
+
+ while ((c = getopt(argc, argv, "l:")) != -1)
+ switch (c) {
+ case 'l':
+ bindname = optarg;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0 || argc % 3 > 0)
+ usage();
+
+ for (; argc > 0; argc -= 3, argv += 3) {
+ if (nrecords == NRECORDS)
+ errx(1, "too many arguments");
+ newdnsrecord(records + nrecords, argv[0], argv[1], argv[2]);
+ nrecords++;
+ }
+
+ memset(&bindaddr, 0, sizeof(bindaddr));
+ bindaddr.i.sin_len = sizeof(bindaddr.i);
+ bindaddr.i.sin_family = AF_INET;
+ bindaddr.i.sin_port = htons(53);
+ inet_aton(bindname, &bindaddr.i.sin_addr);
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd == -1)
+ err(1, "socket");
+ if (bind(fd, &bindaddr.a, bindaddr.a.sa_len) == -1)
+ err(1, "bind");
+
+ if (daemon(0, 1) == -1)
+ err(1, "daemon");
+
+ req.u8 = recvbuf;
+ for (;;) {
+ fromlen = sizeof(fromaddr.a);
+ n = recvfrom(fd, req.u8, sizeof(recvbuf), 0, &fromaddr.a,
+ &fromlen);
+ if (n == -1) {
+ warn("recvfrom");
+ break;
+ }
+ resp = lookup(records, nrecords, req, n);
+ sendto(fd, resp->resp, resp->resplen, 0, &fromaddr.a, fromlen);
+ }
+ close(fd);
+
+ return 0;
+}
+
+static __dead void
+usage(void)
+{
+ fprintf(stderr, "usage: ns [-l addr] type name rddata ...\n");
+ exit(1);
+}
+
+static const struct dnsrecord *
+lookup(struct dnsrecord *records, size_t nrecords, union dnsmsg req,
+ size_t reqlen)
+{
+ static struct dnsrecord resp;
+ union dnsmsg dns;
+ const unsigned char *qname;
+ size_t i, qnamelen;
+ uint16_t id;
+
+ qname = lowercase(req, &qnamelen);
+ id = req.hdr->id;
+ req.hdr->id = 0;
+
+ for (i = 0; i < nrecords; i++) {
+ if (records[i].reqlen != reqlen)
+ continue;
+ dns.u8 = records[i].req;
+ if (memcmp(dns.hdr, req.hdr, reqlen) == 0) {
+ memcpy(resp.resp, records[i].resp, records[i].resplen);
+ resp.resplen = records[i].resplen;
+ dns.u8 = resp.resp;
+ dns.hdr->id = id;
+ memcpy(dns.hdr + 1, qname, qnamelen);
+ return &resp;
+ }
+ }
+
+ resp.resplen = sizeof(struct dnsheader);
+ memset(resp.resp, 0, sizeof(resp.resp));
+ dns.u8 = resp.resp;
+ dns.hdr->id = id;
+ dns.hdr->flags = htons(0x8000 | 0x3); /* QR | NXDOMAIN */
+ return &resp;
+}
+
+static const unsigned char *
+lowercase(union dnsmsg dns, size_t *retlen)
+{
+ static unsigned char buf[256];
+ size_t i;
+ size_t len = 0;
+
+ dns.hdr++; /* move to qname */
+ for (;;) {
+ if (*dns.u8 == '\0')
+ break;
+ i = buf[len++] = *dns.u8++; /* label length */
+ for (; i > 0; i--) {
+ buf[len++] = *dns.u8;
+ *dns.u8 = tolower(*dns.u8);
+ dns.u8++;
+ }
+ }
+ *retlen = len;
+ return buf;
+}
+
+static void
+newdnsrecord(struct dnsrecord *record, const char *type, const char *qname,
+ const char *rddata)
+{
+ union dnsmsg beg, dns;
+ const char *label;
+ int qtype;
+
+ if (strcmp(type, "A") == 0)
+ qtype = 1;
+ else
+ errx(1, "%s: unknown type", type);
+
+ /* question header */
+ dns.u8 = beg.u8 = record->req;
+ dns.hdr->flags = htons(0x100); /* RD */
+ dns.hdr->qdcount = htons(1);
+
+ /* question message */
+ dns.hdr++;
+
+ /* question name */
+ for (;;) {
+ label = strchr(qname, '.');
+ if (label == NULL)
+ break;
+ *dns.u8++ = label - qname;
+ for (; qname < label; qname++)
+ *dns.u8++ = *qname;
+ qname = label + 1;
+ }
+ *dns.u8++ = '\0';
+
+ /* question type */
+ *dns.u16++ = htons(qtype);
+
+ /* question class */
+ *dns.u16++ = htons(1);
+
+ record->reqlen = dns.u8 - beg.u8;
+
+ /* response header */
+ memcpy(record->resp, record->req, record->reqlen);
+ dns.u8 = beg.u8 = record->resp;
+ dns.hdr->flags = htons(0x8000 | 0x100); /* QR | RD */
+ dns.hdr->ancount = htons(1);
+
+ /* response message */
+ dns.u8 += record->reqlen;
+
+ /* response name with compression */
+ *dns.u16++ = htons(0xc000 | sizeof(struct dnsheader));
+
+ /* response type */
+ *dns.u16++ = htons(qtype);
+
+ /* response class */
+ *dns.u16++ = htons(1);
+
+ /* response ttl */
+ *dns.u32++ = htonl(3600);
+
+ /* response rdlength */
+ *dns.u16++ = htons(4);
+
+ /* response rddata */
+ for (;;) {
+ for (; isdigit(*rddata); rddata++)
+ *dns.u8 = *dns.u8 * 10 + (*rddata - '0');
+ dns.u8++;
+ if (*rddata++ == '\0')
+ break;
+ }
+
+ record->resplen = dns.u8 - beg.u8;
+}
diff --git a/regress/usr.sbin/rebound/record.sh b/regress/usr.sbin/rebound/record.sh
new file mode 100644
index 00000000000..8d2dc7a2b52
--- /dev/null
+++ b/regress/usr.sbin/rebound/record.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+# $OpenBSD: record.sh,v 1.1 2018/05/09 19:34:53 anton Exp $
+#
+# Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+cat <<! >$CONF
+nameserver ${ADDR1}
+# Unknown type.
+record MX t1.example.com. 1.1.1.1
+# Missing trailing dot.
+record A t1.example.com 1.1.1.1
+# Name too short.
+record A a 1.1.1.1
+!
+
+$ENV $REBOUND -c $CONF -l $ADDR2
+
+R=$(resolve -W 1 -t MX t1.example.com $ADDR2)
+asserteq ";; connection timed out; no servers could be reached" "$R" \
+ "must not be resolved by rebound"
+
+R=$(resolve -W 1 -t A t1.example.com $ADDR2)
+asserteq ";; connection timed out; no servers could be reached" "$R" \
+ "must not be resolved by rebound"
+
+R=$(resolve -W 1 -t A a $ADDR2)
+asserteq ";; connection timed out; no servers could be reached" "$R" \
+ "must not be resolved by rebound"
+
+pkillw "^${REBOUND}"
+
+cat <<! >$CONF
+nameserver ${ADDR1}
+record A t1.example.com. 1.1.1.1
+record A t2.example.com. 2.2.2.2
+!
+
+$ENV $NS -l $ADDR1 A t3.example.com. 3.3.3.3
+
+$ENV $REBOUND -c $CONF -l $ADDR2
+
+R=$(resolve -t A t1.example.com $ADDR2)
+asserteq "t1.example.com has address 1.1.1.1" "$R" "must be resolved by rebound"
+
+R=$(resolve -t PTR 1.1.1.1 $ADDR2)
+asserteq "1.1.1.1.in-addr.arpa domain name pointer t1.example.com." "$R" \
+ "must be resolved by rebound"
+
+R=$(resolve -t A t2.example.com $ADDR2)
+asserteq "t2.example.com has address 2.2.2.2" "$R" "must be resolved by rebound"
+
+R=$(resolve -t PTR 2.2.2.2 $ADDR2)
+asserteq "2.2.2.2.in-addr.arpa domain name pointer t2.example.com." "$R" \
+ "must be resolved by rebound"
+
+R=$(resolve -t A t3.example.com $ADDR2)
+asserteq "t3.example.com has address 3.3.3.3" "$R" \
+ "must be resolved by rebound-ns"
+
+R=$(resolve -t A t4.example.com $ADDR2)
+asserteq "Host t4.example.com not found: 3(NXDOMAIN)" "$R" \
+ "must not be resolved by rebound nor rebound-ns"
diff --git a/regress/usr.sbin/rebound/reload.sh b/regress/usr.sbin/rebound/reload.sh
new file mode 100644
index 00000000000..0073e5d7437
--- /dev/null
+++ b/regress/usr.sbin/rebound/reload.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# $OpenBSD: reload.sh,v 1.1 2018/05/09 19:34:53 anton Exp $
+#
+# Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+echo nameserver $ADDR1 >$CONF
+
+$ENV $NS -l $ADDR1 A t1.example.com. 1.1.1.1
+
+$ENV $REBOUND -c $CONF -l $ADDR2
+
+R=$(resolve -t A t1.example.com $ADDR2)
+asserteq "t1.example.com has address 1.1.1.1" "$R" \
+ "must be resolved by rebound-ns"
+
+# Kill and restart rebound-ns in order to bind to a different address.
+pkillw "^${NS}"
+echo nameserver $ADDR3 >$CONF
+$ENV $NS -l $ADDR3 A t2.example.com. 2.2.2.2 A t3.example.com. 3.3.3.3
+
+R=$(resolve -t A t2.example.com $ADDR2)
+asserteq "t2.example.com has address 2.2.2.2" "$R" \
+ "must be resolved by rebound-ns"
+
+# It should survive a SIGHUP delivery.
+pkill -HUP -f "^${REBOUND}"
+
+R=$(resolve -t A t3.example.com $ADDR2)
+asserteq "t3.example.com has address 3.3.3.3" "$R" \
+ "must be resolved by rebound-ns"
+
+# Clear resolv.conf, rebound should exit since all nameservers are gone.
+echo >$CONF
+NTRIES=0
+while pgrep -f "^${REBOUND}" >/dev/null 2>&1; do
+ sleep .1
+ NTRIES=$((NTRIES + 1))
+ [ $NTRIES -eq 100 ] && break
+done
+R=$(pgrep -fl "^${REBOUND}" | xargs)
+asserteq "" "$R" "rebound not dead after $((NTRIES / 10)) seconds"
diff --git a/regress/usr.sbin/rebound/run.sh b/regress/usr.sbin/rebound/run.sh
new file mode 100644
index 00000000000..1ff6c7cd489
--- /dev/null
+++ b/regress/usr.sbin/rebound/run.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+#
+# $OpenBSD: run.sh,v 1.1 2018/05/09 19:34:53 anton Exp $
+#
+# Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+set -eu
+
+usage() {
+ echo "usage: sh run.sh -a addr -n rebound-ns -r rebound file" 1>&2
+ exit 1
+}
+
+# asserteq WANT GOT [MESSAGE]
+asserteq() {
+ [ "$1" = "$2" ] && return 0
+
+ printf 'FAIL:\n\tWANT:\t"%s"\n\tGOT:\t"%s"\n' "$1" "$2"
+ [ $# -eq 3 ] && printf '\tREASON:\t"%s"\n' "$3"
+ FAIL=1
+}
+
+atexit() {
+ local _err=$?
+
+ # Kill daemons.
+ pkillw "^${REBOUND}" "^${NS}"
+
+ # Cleanup temporary files.
+ rm -f $@
+
+ if [ $_err -ne 0 ] || [ $FAIL -ne 0 ]; then
+ exit 1
+ else
+ exit 0
+ fi
+}
+
+pkillw() {
+ local _pat _sig
+
+ for _pat; do
+ _sig=TERM
+ while pgrep -f "$_pat" >/dev/null 2>&1; do
+ pkill "-${_sig}" -f "$_pat"
+ sleep .5
+ _sig=KILL
+ done
+ done
+}
+
+resolve() {
+ host $@ | sed -n '$p'
+}
+
+ENV='env MALLOC_OPTIONS=S'
+FAIL=0
+NADDR=0
+NS=
+REBOUND=
+
+while getopts "a:n:r:" opt; do
+ case "$opt" in
+ a) NADDR=$((NADDR + 1))
+ eval "ADDR${NADDR}=${OPTARG}"
+ ;;
+ n) NS=$OPTARG;;
+ r) REBOUND=$OPTARG;;
+ *) usage;;
+ esac
+done
+shift $((OPTIND - 1))
+([ $# -ne 1 ] || [ $NADDR -eq 0 ] || [ -z "$NS" ] || [ -z "$REBOUND" ]) && usage
+
+CONF=$(mktemp -t rebound.XXXXXX)
+trap 'atexit $CONF' EXIT
+
+. $1