summaryrefslogtreecommitdiffstats
path: root/bin/tests/system/upforwd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
commitea648e70a989cca190cd7403fe892fd2dcc290b4 (patch)
treee2b6b1c647da68b0d4d66082835e256eb30970e8 /bin/tests/system/upforwd
parentInitial commit. (diff)
downloadbind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.tar.xz
bind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.zip
Adding upstream version 1:9.11.5.P4+dfsg.upstream/1%9.11.5.P4+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--bin/tests/system/upforwd/ans4/ans.pl361
-rw-r--r--bin/tests/system/upforwd/clean.sh26
-rw-r--r--bin/tests/system/upforwd/knowngood.after110
-rw-r--r--bin/tests/system/upforwd/knowngood.after211
-rw-r--r--bin/tests/system/upforwd/knowngood.before8
-rw-r--r--bin/tests/system/upforwd/knowngood.ns2.before6
-rw-r--r--bin/tests/system/upforwd/ns1/example1.db16
-rw-r--r--bin/tests/system/upforwd/ns1/named.conf.in40
-rw-r--r--bin/tests/system/upforwd/ns2/named.conf.in35
-rw-r--r--bin/tests/system/upforwd/ns3/named.conf.in45
-rw-r--r--bin/tests/system/upforwd/ns3/nomaster.db12
-rw-r--r--bin/tests/system/upforwd/prereq.sh21
-rw-r--r--bin/tests/system/upforwd/setup.sh36
-rw-r--r--bin/tests/system/upforwd/tests.sh198
14 files changed, 825 insertions, 0 deletions
diff --git a/bin/tests/system/upforwd/ans4/ans.pl b/bin/tests/system/upforwd/ans4/ans.pl
new file mode 100644
index 0000000..e98c2cf
--- /dev/null
+++ b/bin/tests/system/upforwd/ans4/ans.pl
@@ -0,0 +1,361 @@
+#!/usr/bin/perl
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+#
+# This is the name server from hell. It provides canned
+# responses based on pattern matching the queries, and
+# can be reprogrammed on-the-fly over a TCP connection.
+#
+# The server listens for control connections on port 5301.
+# A control connection is a TCP stream of lines like
+#
+# /pattern/
+# name ttl type rdata
+# name ttl type rdata
+# ...
+# /pattern/
+# name ttl type rdata
+# name ttl type rdata
+# ...
+#
+# There can be any number of patterns, each associated
+# with any number of response RRs. Each pattern is a
+# Perl regular expression.
+#
+# Each incoming query is converted into a string of the form
+# "qname qtype" (the printable query domain name, space,
+# printable query type) and matched against each pattern.
+#
+# The first pattern matching the query is selected, and
+# the RR following the pattern line are sent in the
+# answer section of the response.
+#
+# Each new control connection causes the current set of
+# patterns and responses to be cleared before adding new
+# ones.
+#
+# The server handles UDP and TCP queries. Zone transfer
+# responses work, but must fit in a single 64 k message.
+#
+# Now you can add TSIG, just specify key/key data with:
+#
+# /pattern <key> <key_data>/
+# name ttl type rdata
+# name ttl type rdata
+#
+# Note that this data will still be sent with any request for
+# pattern, only this data will be signed. Currently, this is only
+# done for TCP.
+
+
+use IO::File;
+use IO::Socket;
+use Data::Dumper;
+use Net::DNS;
+use Net::DNS::Packet;
+use strict;
+
+# Ignore SIGPIPE so we won't fail if peer closes a TCP socket early
+local $SIG{PIPE} = 'IGNORE';
+
+# Flush logged output after every line
+local $| = 1;
+
+my $server_addr = "10.53.0.4";
+
+my $localport = int($ENV{'PORT'});
+if (!$localport) { $localport = 5300; }
+
+my $udpsock = IO::Socket::INET->new(LocalAddr => "$server_addr",
+ LocalPort => $localport, Proto => "udp", Reuse => 1) or die "$!";
+
+my $tcpsock = IO::Socket::INET->new(LocalAddr => "$server_addr",
+ LocalPort => $localport, Proto => "tcp", Listen => 5, Reuse => 1) or die "$!";
+
+print "listening on $server_addr:$localport.\n";
+
+my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!";
+print $pidf "$$\n" or die "cannot write pid file: $!";
+$pidf->close or die "cannot close pid file: $!";;
+sub rmpid { unlink "ans.pid"; exit 1; };
+
+$SIG{INT} = \&rmpid;
+$SIG{TERM} = \&rmpid;
+
+#my @answers = ();
+my @rules;
+sub handleUDP {
+ my ($buf) = @_;
+ my $packet;
+
+ if ($Net::DNS::VERSION > 0.68) {
+ $packet = new Net::DNS::Packet(\$buf, 0);
+ $@ and die $@;
+ } else {
+ my $err;
+ ($packet, $err) = new Net::DNS::Packet(\$buf, 0);
+ $err and die $err;
+ }
+
+ $packet->header->qr(1);
+ $packet->header->aa(1);
+
+ my @questions = $packet->question;
+ my $qname = $questions[0]->qname;
+ my $qtype = $questions[0]->qtype;
+
+ # get the existing signature if any, and clear the additional section
+ my $prev_tsig;
+ while (my $rr = $packet->pop("additional")) {
+ if ($rr->type eq "TSIG") {
+ $prev_tsig = $rr;
+ }
+ }
+
+ my $r;
+ foreach $r (@rules) {
+ my $pattern = $r->{pattern};
+ my($dbtype, $key_name, $key_data) = split(/ /,$pattern);
+ print "[handleUDP] $dbtype, $key_name, $key_data \n";
+ if ("$qname $qtype" =~ /$dbtype/) {
+ my $a;
+ foreach $a (@{$r->{answer}}) {
+ $packet->push("answer", $a);
+ }
+ if(defined($key_name) && defined($key_data)) {
+ # Sign the packet
+ print " Signing the response with " .
+ "$key_name/$key_data\n";
+ my $tsig = Net::DNS::RR->
+ new("$key_name TSIG $key_data");
+
+ # These kluges are necessary because Net::DNS
+ # doesn't know how to sign responses. We
+ # clear compnames so that the TSIG key and
+ # algorithm name won't be compressed, and
+ # add one to arcount because the signing
+ # function will attempt to decrement it,
+ # which is incorrect in a response. Finally
+ # we set request_mac to the previous digest.
+ $packet->{"compnames"} = {};
+ $packet->{"header"}{"arcount"} += 1;
+ if (defined($prev_tsig)) {
+ my $rmac = pack('n H*',
+ $prev_tsig->mac_size,
+ $prev_tsig->mac);
+ $tsig->{"request_mac"} =
+ unpack("H*", $rmac);
+ }
+
+ $packet->sign_tsig($tsig);
+ }
+ last;
+ }
+ }
+ #$packet->print;
+
+ return $packet->data;
+}
+
+# namelen:
+# given a stream of data, reads a DNS-formatted name and returns its
+# total length, thus making it possible to skip past it.
+sub namelen {
+ my ($data) = @_;
+ my $len = 0;
+ my $label_len = 0;
+ do {
+ $label_len = unpack("c", $data);
+ $data = substr($data, $label_len + 1);
+ $len += $label_len + 1;
+ } while ($label_len != 0);
+ return ($len);
+}
+
+# packetlen:
+# given a stream of data, reads a DNS wire-format packet and returns
+# its total length, making it possible to skip past it.
+sub packetlen {
+ my ($data) = @_;
+ my $q;
+ my $rr;
+
+ my ($header, $offset) = Net::DNS::Header->parse(\$data);
+ for (1 .. $header->qdcount) {
+ ($q, $offset) = Net::DNS::Question->parse(\$data, $offset);
+ }
+ for (1 .. $header->ancount) {
+ ($rr, $offset) = Net::DNS::RR->parse(\$data, $offset);
+ }
+ for (1 .. $header->nscount) {
+ ($rr, $offset) = Net::DNS::RR->parse(\$data, $offset);
+ }
+ for (1 .. $header->arcount) {
+ ($rr, $offset) = Net::DNS::RR->parse(\$data, $offset);
+ }
+ return $offset;
+}
+
+# sign_tcp_continuation:
+# This is a hack to correct the problem that Net::DNS has no idea how
+# to sign multiple-message TCP responses. Several data that are included
+# in the digest when signing a query or the first message of a response are
+# omitted when signing subsequent messages in a TCP stream.
+#
+# Net::DNS::Packet->sign_tsig() has the ability to use a custom signing
+# function (specified by calling Packet->sign_func()). We use this
+# function as the signing function for TCP continuations, and it removes
+# the unwanted data from the digest before calling the default sign_hmac
+# function.
+sub sign_tcp_continuation {
+ my ($key, $data) = @_;
+
+ # copy out first two bytes: size of the previous MAC
+ my $rmacsize = unpack("n", $data);
+ $data = substr($data, 2);
+
+ # copy out previous MAC
+ my $rmac = substr($data, 0, $rmacsize);
+ $data = substr($data, $rmacsize);
+
+ # try parsing out the packet information
+ my $plen = packetlen($data);
+ my $pdata = substr($data, 0, $plen);
+ $data = substr($data, $plen);
+
+ # remove the keyname, ttl, class, and algorithm name
+ $data = substr($data, namelen($data));
+ $data = substr($data, 6);
+ $data = substr($data, namelen($data));
+
+ # preserve the TSIG data
+ my $tdata = substr($data, 0, 8);
+
+ # prepare a new digest and sign with it
+ $data = pack("n", $rmacsize) . $rmac . $pdata . $tdata;
+ return Net::DNS::RR::TSIG::sign_hmac($key, $data);
+}
+
+sub handleTCP {
+ my ($buf) = @_;
+ my $packet;
+
+ if ($Net::DNS::VERSION > 0.68) {
+ $packet = new Net::DNS::Packet(\$buf, 0);
+ $@ and die $@;
+ } else {
+ my $err;
+ ($packet, $err) = new Net::DNS::Packet(\$buf, 0);
+ $err and die $err;
+ }
+
+ $packet->header->qr(1);
+ $packet->header->aa(1);
+
+ my @questions = $packet->question;
+ my $qname = $questions[0]->qname;
+ my $qtype = $questions[0]->qtype;
+
+ # get the existing signature if any, and clear the additional section
+ my $prev_tsig;
+ my $signer;
+ while (my $rr = $packet->pop("additional")) {
+ if ($rr->type eq "TSIG") {
+ $prev_tsig = $rr;
+ }
+ }
+
+ my @results = ();
+ my $count_these = 0;
+
+ my $r;
+ foreach $r (@rules) {
+ my $pattern = $r->{pattern};
+ my($dbtype, $key_name, $key_data) = split(/ /,$pattern);
+ print "[handleTCP] $dbtype, $key_name, $key_data \n";
+ if ("$qname $qtype" =~ /$dbtype/) {
+ $count_these++;
+ my $a;
+ foreach $a (@{$r->{answer}}) {
+ $packet->push("answer", $a);
+ }
+ if(defined($key_name) && defined($key_data)) {
+ # sign the packet
+ print " Signing the data with " .
+ "$key_name/$key_data\n";
+
+ my $tsig = Net::DNS::RR->
+ new("$key_name TSIG $key_data");
+
+ # These kluges are necessary because Net::DNS
+ # doesn't know how to sign responses. We
+ # clear compnames so that the TSIG key and
+ # algorithm name won't be compressed, and
+ # add one to arcount because the signing
+ # function will attempt to decrement it,
+ # which is incorrect in a response. Finally
+ # we set request_mac to the previous digest.
+ $packet->{"compnames"} = {};
+ $packet->{"header"}{"arcount"} += 1;
+ if (defined($prev_tsig)) {
+ my $rmac = pack('n H*',
+ $prev_tsig->mac_size,
+ $prev_tsig->mac);
+ $tsig->{"request_mac"} =
+ unpack("H*", $rmac);
+ }
+
+ $tsig->sign_func($signer) if defined($signer);
+ $packet->sign_tsig($tsig);
+ $signer = \&sign_tcp_continuation;
+
+ my $copy =
+ Net::DNS::Packet->new(\($packet->data));
+ $prev_tsig = $copy->pop("additional");
+ }
+ #$packet->print;
+ push(@results,$packet->data);
+ $packet = new Net::DNS::Packet(\$buf, 0);
+ $packet->header->qr(1);
+ $packet->header->aa(1);
+ }
+ }
+ print " A total of $count_these patterns matched\n";
+ return \@results;
+}
+
+# Main
+my $rin;
+my $rout;
+for (;;) {
+ $rin = '';
+ vec($rin, fileno($tcpsock), 1) = 1;
+ vec($rin, fileno($udpsock), 1) = 1;
+
+ select($rout = $rin, undef, undef, undef);
+
+ if (vec($rout, fileno($udpsock), 1)) {
+ printf "UDP request\n";
+ my $buf;
+ $udpsock->recv($buf, 512);
+ } elsif (vec($rout, fileno($tcpsock), 1)) {
+ my $conn = $tcpsock->accept;
+ my $buf;
+ for (;;) {
+ my $lenbuf;
+ my $n = $conn->sysread($lenbuf, 2);
+ last unless $n == 2;
+ my $len = unpack("n", $lenbuf);
+ $n = $conn->sysread($buf, $len);
+ }
+ sleep(1);
+ }
+}
diff --git a/bin/tests/system/upforwd/clean.sh b/bin/tests/system/upforwd/clean.sh
new file mode 100644
index 0000000..d622204
--- /dev/null
+++ b/bin/tests/system/upforwd/clean.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+#
+# Clean up after zone transfer tests.
+#
+
+rm -f dig.out.ns1* dig.out.ns2 dig.out.ns1 dig.out.ns3 dig.out.ns1.after
+rm -f ns1/*.jnl ns2/*.jnl ns3/*.jnl ns1/example.db ns2/*.bk ns3/*.bk
+rm -f ns3/nomaster1.db
+rm -f */named.memstats
+rm -f */named.run
+rm -f */named.conf
+rm -f */ans.run
+rm -f Ksig0.example2.*
+rm -f keyname
+rm -f ns*/named.lock
+rm -f ns1/example2.db
diff --git a/bin/tests/system/upforwd/knowngood.after1 b/bin/tests/system/upforwd/knowngood.after1
new file mode 100644
index 0000000..7fc424c
--- /dev/null
+++ b/bin/tests/system/upforwd/knowngood.after1
@@ -0,0 +1,10 @@
+example. 3600 IN SOA n1.example. hostmaster.ns1.example. 2 3600 1200 604800 7200
+example. 3600 IN NS ns2.example.
+example. 3600 IN NS ns3.example.
+ns1.example. 3600 IN A 10.53.0.1
+ns2.example. 3600 IN A 10.53.0.2
+ns3.example. 3600 IN A 10.53.0.3
+updated.example. 600 IN TXT "Foo"
+updated.example. 600 IN A 10.10.10.1
+example. 3600 IN SOA n1.example. hostmaster.ns1.example. 2 3600 1200 604800 7200
+
diff --git a/bin/tests/system/upforwd/knowngood.after2 b/bin/tests/system/upforwd/knowngood.after2
new file mode 100644
index 0000000..eab7a2c
--- /dev/null
+++ b/bin/tests/system/upforwd/knowngood.after2
@@ -0,0 +1,11 @@
+example. 3600 IN SOA n1.example. hostmaster.ns1.example. 3 3600 1200 604800 7200
+example. 3600 IN NS ns2.example.
+example. 3600 IN NS ns3.example.
+ns1.example. 3600 IN A 10.53.0.1
+ns2.example. 3600 IN A 10.53.0.2
+ns3.example. 3600 IN A 10.53.0.3
+unsigned.example. 600 IN TXT "Foo"
+unsigned.example. 600 IN A 10.10.10.1
+updated.example. 600 IN TXT "Foo"
+updated.example. 600 IN A 10.10.10.1
+example. 3600 IN SOA n1.example. hostmaster.ns1.example. 3 3600 1200 604800 7200
diff --git a/bin/tests/system/upforwd/knowngood.before b/bin/tests/system/upforwd/knowngood.before
new file mode 100644
index 0000000..4bde819
--- /dev/null
+++ b/bin/tests/system/upforwd/knowngood.before
@@ -0,0 +1,8 @@
+example. 3600 IN SOA n1.example. hostmaster.ns1.example. 1 3600 1200 604800 7200
+example. 3600 IN NS ns2.example.
+example. 3600 IN NS ns3.example.
+ns1.example. 3600 IN A 10.53.0.1
+ns2.example. 3600 IN A 10.53.0.2
+ns3.example. 3600 IN A 10.53.0.3
+example. 3600 IN SOA n1.example. hostmaster.ns1.example. 1 3600 1200 604800 7200
+
diff --git a/bin/tests/system/upforwd/knowngood.ns2.before b/bin/tests/system/upforwd/knowngood.ns2.before
new file mode 100644
index 0000000..bb3c355
--- /dev/null
+++ b/bin/tests/system/upforwd/knowngood.ns2.before
@@ -0,0 +1,6 @@
+example. 3600 IN SOA n1.example. hostmaster.ns1.example. 1 3600 1200 604800 7200
+example. 3600 IN NS ns2.example.
+ns1.example. 3600 IN A 10.53.0.1
+ns2.example. 3600 IN A 10.53.0.2
+example. 3600 IN SOA n1.example. hostmaster.ns1.example. 1 3600 1200 604800 7200
+
diff --git a/bin/tests/system/upforwd/ns1/example1.db b/bin/tests/system/upforwd/ns1/example1.db
new file mode 100644
index 0000000..a703d02
--- /dev/null
+++ b/bin/tests/system/upforwd/ns1/example1.db
@@ -0,0 +1,16 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+@ 3600 SOA n1.example. hostmaster.ns1.example. (
+ 1 3600 1200 604800 7200 )
+ NS ns2.example.
+ NS ns3.example.
+ns1 A 10.53.0.1
+ns2 A 10.53.0.2
+ns3 A 10.53.0.3
diff --git a/bin/tests/system/upforwd/ns1/named.conf.in b/bin/tests/system/upforwd/ns1/named.conf.in
new file mode 100644
index 0000000..e0a30cd
--- /dev/null
+++ b/bin/tests/system/upforwd/ns1/named.conf.in
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+key "update.example." {
+ algorithm "hmac-md5";
+ secret "c3Ryb25nIGVub3VnaCBmb3IgYSBtYW4gYnV0IG1hZGUgZm9yIGEgd29tYW4K";
+};
+
+options {
+ query-source address 10.53.0.1;
+ notify-source 10.53.0.1;
+ transfer-source 10.53.0.1;
+ port @PORT@;
+ pid-file "named.pid";
+ listen-on { 10.53.0.1; };
+ listen-on-v6 { none; };
+ recursion yes;
+ acache-enable yes;
+ notify yes;
+};
+
+zone "example" {
+ type master;
+ file "example.db";
+ allow-update { key update.example.; 10.53.0.3; };
+};
+
+zone "example2" {
+ type master;
+ file "example2.db";
+ allow-update { key sig0.example2.; };
+};
diff --git a/bin/tests/system/upforwd/ns2/named.conf.in b/bin/tests/system/upforwd/ns2/named.conf.in
new file mode 100644
index 0000000..c650781
--- /dev/null
+++ b/bin/tests/system/upforwd/ns2/named.conf.in
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+options {
+ query-source address 10.53.0.2;
+ notify-source 10.53.0.2;
+ transfer-source 10.53.0.2;
+ port @PORT@;
+ pid-file "named.pid";
+ listen-on { 10.53.0.2; };
+ listen-on-v6 { none; };
+ recursion yes;
+ acache-enable yes;
+ notify yes;
+};
+
+zone "example" {
+ type slave;
+ file "example.bk";
+ masters { 10.53.0.1; };
+};
+
+zone "example2" {
+ type slave;
+ file "example2.bk";
+ masters { 10.53.0.1; };
+};
diff --git a/bin/tests/system/upforwd/ns3/named.conf.in b/bin/tests/system/upforwd/ns3/named.conf.in
new file mode 100644
index 0000000..d6fc26c
--- /dev/null
+++ b/bin/tests/system/upforwd/ns3/named.conf.in
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+options {
+ query-source address 10.53.0.3;
+ notify-source 10.53.0.3;
+ transfer-source 10.53.0.3;
+ port @PORT@;
+ pid-file "named.pid";
+ listen-on { 10.53.0.3; };
+ listen-on-v6 { none; };
+ recursion yes;
+ acache-enable yes;
+ notify yes;
+};
+
+zone "example" {
+ type slave;
+ file "example.bk";
+ allow-update-forwarding { any; };
+ masters { 10.53.0.1; };
+};
+
+zone "example2" {
+ type slave;
+ file "example2.bk";
+ allow-update-forwarding { any; };
+ masters { 10.53.0.1; };
+};
+
+zone "nomaster" {
+ type slave;
+ file "nomaster1.db";
+ allow-update-forwarding { any; };
+ masterfile-format text;
+ masters { 10.53.0.4; };
+};
diff --git a/bin/tests/system/upforwd/ns3/nomaster.db b/bin/tests/system/upforwd/ns3/nomaster.db
new file mode 100644
index 0000000..443bedb
--- /dev/null
+++ b/bin/tests/system/upforwd/ns3/nomaster.db
@@ -0,0 +1,12 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+@ 0 SOA . . 141235 3600 1200 86400 1200
+@ 0 NS ns4
+ns4 0 A 10.53.0.4
diff --git a/bin/tests/system/upforwd/prereq.sh b/bin/tests/system/upforwd/prereq.sh
new file mode 100644
index 0000000..0e299f4
--- /dev/null
+++ b/bin/tests/system/upforwd/prereq.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+if $PERL -e 'use Net::DNS;' 2>/dev/null
+then
+ :
+else
+ echo_i "This test requires the Net::DNS library." >&2
+ exit 1
+fi
diff --git a/bin/tests/system/upforwd/setup.sh b/bin/tests/system/upforwd/setup.sh
new file mode 100644
index 0000000..c32aab4
--- /dev/null
+++ b/bin/tests/system/upforwd/setup.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+$SHELL clean.sh
+
+cp -f ns1/example1.db ns1/example.db
+cp -f ns3/nomaster.db ns3/nomaster1.db
+
+copy_setports ns1/named.conf.in ns1/named.conf
+copy_setports ns2/named.conf.in ns2/named.conf
+copy_setports ns3/named.conf.in ns3/named.conf
+
+#
+# SIG(0) required cryptographic support which may not be configured.
+#
+test -r $RANDFILE || $GENRANDOM 400 $RANDFILE
+keyname=`$KEYGEN -q -r $RANDFILE -n HOST -a RSASHA1 -b 1024 -T KEY sig0.example2 2>/dev/null | $D2U`
+if test -n "$keyname"
+then
+ cat ns1/example1.db $keyname.key > ns1/example2.db
+ echo $keyname > keyname
+else
+ cat ns1/example1.db > ns1/example2.db
+ rm -f keyname
+fi
diff --git a/bin/tests/system/upforwd/tests.sh b/bin/tests/system/upforwd/tests.sh
new file mode 100644
index 0000000..b0694bb
--- /dev/null
+++ b/bin/tests/system/upforwd/tests.sh
@@ -0,0 +1,198 @@
+#!/bin/sh
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+# ns1 = stealth master
+# ns2 = slave with update forwarding disabled; not currently used
+# ns3 = slave with update forwarding enabled
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+DIGOPTS="+tcp +noadd +nosea +nostat +noquest +nocomm +nocmd -p ${PORT}"
+
+status=0
+n=1
+
+sleep 5
+
+echo_i "waiting for servers to be ready for testing ($n)"
+for i in 1 2 3 4 5 6 7 8 9 10
+do
+ ret=0
+ $DIG +tcp -p ${PORT} example. @10.53.0.1 soa > dig.out.ns1 || ret=1
+ grep "status: NOERROR" dig.out.ns1 > /dev/null || ret=1
+ $DIG +tcp -p ${PORT} example. @10.53.0.2 soa > dig.out.ns2 || ret=1
+ grep "status: NOERROR" dig.out.ns2 > /dev/null || ret=1
+ $DIG +tcp -p ${PORT} example. @10.53.0.3 soa > dig.out.ns3 || ret=1
+ grep "status: NOERROR" dig.out.ns3 > /dev/null || ret=1
+ test $ret = 0 && break
+ sleep 1
+done
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "fetching master copy of zone before update ($n)"
+ret=0
+$DIG $DIGOPTS example.\
+ @10.53.0.1 axfr > dig.out.ns1 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "fetching slave 1 copy of zone before update ($n)"
+$DIG $DIGOPTS example.\
+ @10.53.0.2 axfr > dig.out.ns2 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "fetching slave 2 copy of zone before update ($n)"
+ret=0
+$DIG $DIGOPTS example.\
+ @10.53.0.3 axfr > dig.out.ns3 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "comparing pre-update copies to known good data ($n)"
+ret=0
+digcomp knowngood.before dig.out.ns1 || ret=1
+digcomp knowngood.before dig.out.ns2 || ret=1
+digcomp knowngood.before dig.out.ns3 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+
+echo_i "updating zone (signed) ($n)"
+ret=0
+$NSUPDATE -y update.example:c3Ryb25nIGVub3VnaCBmb3IgYSBtYW4gYnV0IG1hZGUgZm9yIGEgd29tYW4K -- - <<EOF || ret=1
+server 10.53.0.3 ${PORT}
+update add updated.example. 600 A 10.10.10.1
+update add updated.example. 600 TXT Foo
+send
+EOF
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "sleeping 15 seconds for server to incorporate changes"
+sleep 15
+
+echo_i "fetching master copy of zone after update ($n)"
+ret=0
+$DIG $DIGOPTS example.\
+ @10.53.0.1 axfr > dig.out.ns1 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "fetching slave 1 copy of zone after update ($n)"
+ret=0
+$DIG $DIGOPTS example.\
+ @10.53.0.2 axfr > dig.out.ns2 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+
+echo_i "fetching slave 2 copy of zone after update ($n)"
+ret=0
+$DIG $DIGOPTS example.\
+ @10.53.0.3 axfr > dig.out.ns3 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "comparing post-update copies to known good data ($n)"
+ret=0
+digcomp knowngood.after1 dig.out.ns1 || ret=1
+digcomp knowngood.after1 dig.out.ns2 || ret=1
+digcomp knowngood.after1 dig.out.ns3 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+
+echo_i "checking 'forwarding update for zone' is logged ($n)"
+ret=0
+grep "forwarding update for zone 'example/IN'" ns3/named.run > /dev/null || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "updating zone (unsigned) ($n)"
+ret=0
+$NSUPDATE -- - <<EOF || ret=1
+server 10.53.0.3 ${PORT}
+update add unsigned.example. 600 A 10.10.10.1
+update add unsigned.example. 600 TXT Foo
+send
+EOF
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "sleeping 15 seconds for server to incorporate changes"
+sleep 15
+
+echo_i "fetching master copy of zone after update ($n)"
+ret=0
+$DIG $DIGOPTS example.\
+ @10.53.0.1 axfr > dig.out.ns1 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+
+echo_i "fetching slave 1 copy of zone after update ($n)"
+ret=0
+$DIG $DIGOPTS example.\
+ @10.53.0.2 axfr > dig.out.ns2 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "fetching slave 2 copy of zone after update ($n)"
+ret=0
+$DIG $DIGOPTS example.\
+ @10.53.0.3 axfr > dig.out.ns3 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+
+echo_i "comparing post-update copies to known good data ($n)"
+ret=0
+digcomp knowngood.after2 dig.out.ns1 || ret=1
+digcomp knowngood.after2 dig.out.ns2 || ret=1
+digcomp knowngood.after2 dig.out.ns3 || ret=1
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+echo_i "checking update forwarding to dead master ($n)"
+count=0
+ret=0
+while [ $count -lt 5 -a $ret -eq 0 ]
+do
+(
+$NSUPDATE -- - <<EOF
+server 10.53.0.3 ${PORT}
+zone nomaster
+update add unsigned.nomaster. 600 A 10.10.10.1
+update add unsigned.nomaster. 600 TXT Foo
+send
+EOF
+) > /dev/null 2>&1 &
+ $DIG -p ${PORT} +noadd +notcp +noauth nomaster. @10.53.0.3 soa > dig.out.ns3 || ret=1
+ grep "status: NOERROR" dig.out.ns3 > /dev/null || ret=1
+ count=`expr $count + 1`
+done
+if [ $ret != 0 ] ; then echo_i "failed"; status=`expr $status + $ret`; fi
+n=`expr $n + 1`
+
+if test -f keyname
+then
+ echo_i "checking update forwarding to with sig0 ($n)"
+ ret=0
+ keyname=`cat keyname`
+ $NSUPDATE -k $keyname.private -- - <<EOF
+ server 10.53.0.3 ${PORT}
+ zone example2
+ update add unsigned.example2. 600 A 10.10.10.1
+ update add unsigned.example2. 600 TXT Foo
+ send
+EOF
+ $DIG -p ${PORT} unsigned.example2 A @10.53.0.1 > dig.out.ns1.test$n
+ grep "status: NOERROR" dig.out.ns1.test$n > /dev/null || ret=1
+ if [ $ret != 0 ] ; then echo_i "failed"; fi
+ status=`expr $status + $ret`
+ n=`expr $n + 1`
+fi
+
+echo_i "exit status: $status"
+[ $status -eq 0 ] || exit 1