aboutsummaryrefslogtreecommitdiff
path: root/contrib/unbound/testdata
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/unbound/testdata')
-rw-r--r--contrib/unbound/testdata/00-lint.tdir/00-lint.pre14
-rw-r--r--contrib/unbound/testdata/09-unbound-control.tdir/conf.bad_credentials5
-rw-r--r--contrib/unbound/testdata/09-unbound-control.tdir/conf.spoofed_credentials5
-rw-r--r--contrib/unbound/testdata/cachedb_cached_ede.crpl91
-rw-r--r--contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.conf29
-rw-r--r--contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc16
-rw-r--r--contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.post20
-rw-r--r--contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.pre36
-rw-r--r--contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns8
-rw-r--r--contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.test132
-rw-r--r--contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.testns9
-rw-r--r--contrib/unbound/testdata/cachedb_servfail_cname.crpl181
-rw-r--r--contrib/unbound/testdata/disable_edns_do.rpl164
-rw-r--r--contrib/unbound/testdata/edns_downstream_cookies.rpl235
-rw-r--r--contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.conf28
-rw-r--r--contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.dsc16
-rw-r--r--contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.post13
-rw-r--r--contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.pre24
-rw-r--r--contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.test165
-rw-r--r--contrib/unbound/testdata/ip_ratelimit.tdir/unbound_control.key39
-rw-r--r--contrib/unbound/testdata/ip_ratelimit.tdir/unbound_control.pem22
-rw-r--r--contrib/unbound/testdata/ip_ratelimit.tdir/unbound_server.key39
-rw-r--r--contrib/unbound/testdata/ip_ratelimit.tdir/unbound_server.pem22
-rw-r--r--contrib/unbound/testdata/iter_cname_minimise_nx.rpl245
-rw-r--r--contrib/unbound/testdata/iter_dname_ttl.rpl310
-rw-r--r--contrib/unbound/testdata/iter_failreply.rpl132
-rw-r--r--contrib/unbound/testdata/iter_ignore_empty.rpl248
-rw-r--r--contrib/unbound/testdata/iter_nat64.rpl117
-rw-r--r--contrib/unbound/testdata/iter_nat64_prefix.rpl119
-rw-r--r--contrib/unbound/testdata/iter_nat64_prefix48.rpl118
-rw-r--r--contrib/unbound/testdata/iter_scrub_rr_length.rpl298
-rw-r--r--contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.conf34
-rw-r--r--contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.dsc16
-rw-r--r--contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.post14
-rw-r--r--contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.pre50
-rw-r--r--contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.test63
-rw-r--r--contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.testns9
-rw-r--r--contrib/unbound/testdata/rpz_cached_cname.rpl122
-rw-r--r--contrib/unbound/testdata/rrset_use_cached.rpl151
-rw-r--r--contrib/unbound/testdata/serve_expired_0ttl_nodata.rpl154
-rw-r--r--contrib/unbound/testdata/serve_expired_0ttl_nxdomain.rpl154
-rw-r--r--contrib/unbound/testdata/serve_expired_0ttl_servfail.rpl129
-rw-r--r--contrib/unbound/testdata/serve_expired_cached_servfail.rpl130
-rw-r--r--contrib/unbound/testdata/serve_expired_cached_servfail_refresh.rpl145
-rw-r--r--contrib/unbound/testdata/stat_values.tdir/stat_values_cachedb.conf36
-rw-r--r--contrib/unbound/testdata/stat_values.tdir/stat_values_downstream_cookies.conf32
-rw-r--r--contrib/unbound/testdata/subnet_cached_ede.crpl114
-rw-r--r--contrib/unbound/testdata/subnet_cached_servfail.crpl167
-rw-r--r--contrib/unbound/testdata/subnet_global_prefetch.crpl236
-rw-r--r--contrib/unbound/testdata/subnet_global_prefetch_always_forward.crpl167
-rw-r--r--contrib/unbound/testdata/subnet_global_prefetch_expired.crpl241
-rw-r--r--contrib/unbound/testdata/subnet_prezero.crpl155
-rw-r--r--contrib/unbound/testdata/subnet_scopezero_noedns.crpl441
-rw-r--r--contrib/unbound/testdata/val_any_negcache.rpl243
-rw-r--r--contrib/unbound/testdata/val_scrub_rr_length.rpl164
55 files changed, 6067 insertions, 0 deletions
diff --git a/contrib/unbound/testdata/00-lint.tdir/00-lint.pre b/contrib/unbound/testdata/00-lint.tdir/00-lint.pre
new file mode 100644
index 000000000000..507f5e1e9454
--- /dev/null
+++ b/contrib/unbound/testdata/00-lint.tdir/00-lint.pre
@@ -0,0 +1,14 @@
+# #-- 00-lint.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+. ../common.sh
+PRE="../.."
+
+if test -f $PRE/unbound_test_00-lint ; then
+ echo test enabled
+else
+ skip_test "test skipped; clang linter preferred over splint"
+fi
diff --git a/contrib/unbound/testdata/09-unbound-control.tdir/conf.bad_credentials b/contrib/unbound/testdata/09-unbound-control.tdir/conf.bad_credentials
new file mode 100644
index 000000000000..11a131130000
--- /dev/null
+++ b/contrib/unbound/testdata/09-unbound-control.tdir/conf.bad_credentials
@@ -0,0 +1,5 @@
+remote-control:
+ server-key-file: bad_server.key
+ server-cert-file: bad_server.pem
+ control-key-file: bad_control.key
+ control-cert-file: bad_control.pem
diff --git a/contrib/unbound/testdata/09-unbound-control.tdir/conf.spoofed_credentials b/contrib/unbound/testdata/09-unbound-control.tdir/conf.spoofed_credentials
new file mode 100644
index 000000000000..25cb830dca4e
--- /dev/null
+++ b/contrib/unbound/testdata/09-unbound-control.tdir/conf.spoofed_credentials
@@ -0,0 +1,5 @@
+remote-control:
+ server-key-file: unbound_server.key
+ server-cert-file: unbound_server.pem
+ control-key-file: bad_control.key
+ control-cert-file: bad_control.pem
diff --git a/contrib/unbound/testdata/cachedb_cached_ede.crpl b/contrib/unbound/testdata/cachedb_cached_ede.crpl
new file mode 100644
index 000000000000..5eade545105f
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_cached_ede.crpl
@@ -0,0 +1,91 @@
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: no
+ minimal-responses: no
+ module-config: "cachedb validator iterator"
+ trust-anchor-signaling: no
+ verbosity: 4
+ ede: yes
+ val-log-level: 2
+ trust-anchor: "example.nl. DS 50602 8 2 FA8EE175C47325F4BD46D8A4083C3EBEB11C977D689069F2B41F1A29B22446B1"
+
+
+cachedb:
+ backend: "testframe"
+ secret-seed: "testvalue"
+
+stub-zone:
+ name: "example.nl"
+ stub-addr: 193.0.14.129
+CONFIG_END
+
+SCENARIO_BEGIN Test cachedb support for caching EDEs.
+
+RANGE_BEGIN 0 10
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+example.nl. IN DNSKEY
+SECTION ANSWER
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+example.nl. IN A
+SECTION ANSWER
+example.nl. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; get the entry in cache.
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+example.nl. IN A
+SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ FF FE ; option code = 65534 (LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST)
+ 00 00 ; option length
+ HEX_EDNSDATA_END
+ENTRY_END
+
+; get the answer for it
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ede=9
+REPLY QR RD RA DO SERVFAIL
+SECTION QUESTION
+example.nl. IN A
+ENTRY_END
+
+; query again for the cached entry
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+example.nl. IN A
+SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ FF FE ; option code = 65534 (LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST)
+ 00 00 ; option length
+ HEX_EDNSDATA_END
+ENTRY_END
+
+; this must be a cached answer since stub is not answering in this range
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ede=9
+REPLY QR RD RA DO SERVFAIL
+SECTION QUESTION
+example.nl. IN A
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.conf b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.conf
new file mode 100644
index 000000000000..ff76cc37970c
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.conf
@@ -0,0 +1,29 @@
+server:
+ verbosity: 4
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: ""
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ module-config: "cachedb iterator"
+ do-not-query-localhost: no
+ qname-minimisation: no
+
+forward-zone:
+ name: "."
+ forward-addr: 127.0.0.1@@TOPORT@
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 127.0.0.1@@TOPORT@
+
+remote-control:
+ control-enable: yes
+ control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@
+ control-use-cert: no
+
+cachedb:
+ backend: "testframe"
+ secret-seed: "testvalue"
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc
new file mode 100644
index 000000000000..9d267436edf6
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc
@@ -0,0 +1,16 @@
+BaseName: cachedb_no_store
+Version: 1.0
+Description: cachedb test the cachedb-no-store option
+CreationDate: Wed 11 Oct 11:00:00 CEST 2023
+Maintainer: dr. W.C.A. Wijngaards
+Category:
+Component:
+CmdDepends:
+Depends:
+Help:
+Pre: cachedb_no_store.pre
+Post: cachedb_no_store.post
+Test: cachedb_no_store.test
+AuxFiles:
+Passed:
+Failure:
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.post b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.post
new file mode 100644
index 000000000000..320dcc3e3e3b
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.post
@@ -0,0 +1,20 @@
+# #-- cachedb_no_store.post --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# source the test var file when it's there
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+#
+# do your teardown here
+PRE="../.."
+. ../common.sh
+
+echo "> cat logfiles"
+cat fwd.log
+if test -f fwd2.log; then cat fwd2.log; else echo "no fwd2.log"; fi
+if test -f fwd3.log; then cat fwd3.log; else echo "no fwd3.log"; fi
+if test -f fwd4.log; then cat fwd4.log; else echo "no fwd4.log"; fi
+cat unbound.log
+if test -f unbound2.log; then cat unbound2.log; else echo "no unbound2.log"; fi
+kill_pid $FWD_PID
+kill_from_pidfile "unbound.pid"
+rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.pre b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.pre
new file mode 100644
index 000000000000..e59d3b8da759
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.pre
@@ -0,0 +1,36 @@
+# #-- cachedb_no_store.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+if grep "define USE_CACHEDB 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi
+
+get_random_port 2
+UNBOUND_PORT=$RND_PORT
+FWD_PORT=$(($RND_PORT + 1))
+echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
+echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test
+
+# start forwarder
+get_ldns_testns
+$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.testns >fwd.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+
+# make config file
+CONTROL_PATH=/tmp
+CONTROL_PID=$$
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < cachedb_no_store.conf > ub.conf
+# start unbound in the background
+$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test
+echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test
+
+cat .tpkg.var.test
+wait_ldns_testns_up fwd.log
+wait_unbound_up unbound.log
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns
new file mode 100644
index 000000000000..b41abb0ff629
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns
@@ -0,0 +1,8 @@
+ENTRY_BEGIN
+MATCH opcode
+ADJUST copy_id copy_query
+REPLY QR AA SERVFAIL
+SECTION QUESTION
+txt1.example.com. IN TXT
+SECTION ANSWER
+ENTRY_END
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.test b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.test
new file mode 100644
index 000000000000..47a89656c6c2
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.test
@@ -0,0 +1,132 @@
+# #-- cachedb_no_store.test --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+
+# do the test
+get_ldns_testns
+
+# query for a text record that is stored by unbound's cache and cachedb
+# in the testframe cache.
+echo "> dig txt1.example.com."
+dig @127.0.0.1 -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# stop the forwarder with servfail, to check the answer came from the cache
+echo "> stop ldns-testns"
+kill_pid $FWD_PID
+echo "> start ldns-testns with servfails"
+$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.servfail.testns >fwd2.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+wait_ldns_testns_up fwd2.log
+
+echo "> dig txt1.example.com. from unbound cache"
+dig @127.0.0.1 -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# clear the cache of unbound, but not cachedb testframe cache
+echo "> unbound-control flush"
+$PRE/unbound-control -c ub.conf flush_type txt1.example.com. TXT
+if test $? -ne 0; then
+ echo "wrong exit value."
+ exit 1
+else
+ echo "exit value: OK"
+fi
+
+echo "> dig txt1.example.com. from cachedb"
+dig @127.0.0.1 -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# start the forwarder again.
+echo "> stop ldns-testns"
+kill_pid $FWD_PID
+echo "> start ldns-testns"
+$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.testns >fwd3.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+wait_ldns_testns_up fwd3.log
+
+# stop unbound to flush the cachedb cache
+echo "> stop unbound"
+kill_from_pidfile "unbound.pid"
+
+echo ""
+echo "> config unbound with cachedb-no-store: yes"
+echo "cachedb: cachedb-no-store: yes" >> ub.conf
+
+# start unbound again.
+echo "> start unbound"
+$PRE/unbound -d -c ub.conf >unbound2.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+wait_unbound_up unbound2.log
+
+echo ""
+echo "> dig txt1.example.com."
+dig @127.0.0.1 -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# stop the forwarder with servfail, to check the answer came from the cache
+echo "> stop ldns-testns"
+kill_pid $FWD_PID
+echo "> start ldns-testns with servfails"
+$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.servfail.testns >fwd4.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+wait_ldns_testns_up fwd4.log
+
+echo "> dig txt1.example.com. from unbound cache"
+dig @127.0.0.1 -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# clear the cache of unbound, but not cachedb testframe cache
+echo "> unbound-control flush"
+$PRE/unbound-control -c ub.conf flush_type txt1.example.com. TXT
+if test $? -ne 0; then
+ echo "wrong exit value."
+ exit 1
+else
+ echo "exit value: OK"
+fi
+
+echo "> dig txt1.example.com. from cachedb, but that has no message stored"
+dig @127.0.0.1 -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "SERVFAIL" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.testns b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.testns
new file mode 100644
index 000000000000..282b224f82bd
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.testns
@@ -0,0 +1,9 @@
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+txt1.example.com. IN TXT
+SECTION ANSWER
+txt1.example.com. IN TXT "example text message"
+ENTRY_END
diff --git a/contrib/unbound/testdata/cachedb_servfail_cname.crpl b/contrib/unbound/testdata/cachedb_servfail_cname.crpl
new file mode 100644
index 000000000000..221f00d4df54
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_servfail_cname.crpl
@@ -0,0 +1,181 @@
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: no
+ minimal-responses: no
+ ;serve-expired: yes
+ module-config: "cachedb iterator"
+
+cachedb:
+ backend: "testframe"
+ secret-seed: "testvalue"
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129
+CONFIG_END
+
+SCENARIO_BEGIN Test cachedb store and servfail reply from cname.
+; the servfail reply should not overwrite the cache contents.
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION AUTHORITY
+example.com. IN NS ns2.example.com.
+SECTION ADDITIONAL
+ns2.example.com. IN A 1.2.3.5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+foo.com. IN NS
+SECTION AUTHORITY
+foo.com. IN NS ns.example.com.
+ENTRY_END
+RANGE_END
+
+; ns2.example.com.
+RANGE_BEGIN 0 20
+ ADDRESS 1.2.3.5
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns2.example.com., now failing
+RANGE_BEGIN 20 100
+ ADDRESS 1.2.3.5
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN CNAME foo.example.com.
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA SERVFAIL
+SECTION QUESTION
+foo.example.com. IN A
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA SERVFAIL
+SECTION QUESTION
+ns2.example.com. IN A
+SECTION ANSWER
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+REPLY QR AA SERVFAIL
+SECTION QUESTION
+ns2.example.com. IN AAAA
+SECTION ANSWER
+ENTRY_END
+RANGE_END
+
+; get and entry in cache, to make it expired.
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; get the answer for it
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN A 1.2.3.4
+ENTRY_END
+
+; it is now expired
+STEP 20 TIME_PASSES ELAPSE 20
+
+; get a servfail in cache for the destination
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+foo.example.com. IN A
+ENTRY_END
+
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+foo.example.com. IN A
+ENTRY_END
+
+; the query is now a CNAME to servfail.
+; there is a valid, but expired, entry in cache.
+STEP 50 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+STEP 60 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 10 IN CNAME foo.example.com.
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/disable_edns_do.rpl b/contrib/unbound/testdata/disable_edns_do.rpl
new file mode 100644
index 000000000000..82a16da062f1
--- /dev/null
+++ b/contrib/unbound/testdata/disable_edns_do.rpl
@@ -0,0 +1,164 @@
+; config options
+; The island of trust is at example.com
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ trust-anchor-signaling: no
+ minimal-responses: no
+ disable-edns-do: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test lookup with disable-edns-do
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to query of interest, when sent with EDNS DO
+ENTRY_BEGIN
+MATCH opcode qtype qname DO
+ADJUST copy_id
+REPLY QR AA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ENTRY_END
+
+; response to query of interest, when sent without DO
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/edns_downstream_cookies.rpl b/contrib/unbound/testdata/edns_downstream_cookies.rpl
new file mode 100644
index 000000000000..820bc5a7ca70
--- /dev/null
+++ b/contrib/unbound/testdata/edns_downstream_cookies.rpl
@@ -0,0 +1,235 @@
+; config options
+server:
+ answer-cookie: yes
+ cookie-secret: "000102030405060708090a0b0c0d0e0f"
+ access-control: 127.0.0.1 allow_cookie
+ access-control: 1.2.3.4 allow
+ local-data: "test. TXT test"
+
+CONFIG_END
+
+SCENARIO_BEGIN Test downstream DNS Cookies
+
+; Note: When a valid hash was required, it was generated by running this test
+; with an invalid one and checking the output for the valid one.
+; Actual hash generation is tested with unit tests.
+
+; Query without a client cookie ...
+STEP 0 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test. IN TXT
+ENTRY_END
+; ... get TC and refused
+STEP 1 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA TC REFUSED
+SECTION QUESTION
+test. IN TXT
+ENTRY_END
+
+; Query without a client cookie on TCP ...
+STEP 10 QUERY
+ENTRY_BEGIN
+REPLY RD
+MATCH TCP
+SECTION QUESTION
+test. IN TXT
+ENTRY_END
+; ... get an answer
+STEP 11 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+test. IN TXT
+SECTION ANSWER
+test. IN TXT "test"
+ENTRY_END
+
+; Query with only a client cookie ...
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test. IN TXT
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 0a ; Opcode 10
+ 00 08 ; Length 8
+ 31 32 33 34 35 36 37 38 ; Random bits
+HEX_EDNSDATA_END
+ENTRY_END
+; ... get BADCOOKIE and a new cookie
+STEP 21 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all server_cookie
+REPLY QR RD RA DO YXRRSET ; BADCOOKIE is an extended rcode
+SECTION QUESTION
+test. IN TXT
+ENTRY_END
+
+; Query with an invalid cookie ...
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test. IN TXT
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 0a ; Opcode 10
+ 00 18 ; Length 24
+ 31 32 33 34 35 36 37 38 ; Random bits
+ 02 00 00 00 ; wrong version
+ 00 00 00 00 ; Timestamp
+ 31 32 33 34 35 36 37 38 ; wrong hash
+HEX_EDNSDATA_END
+ENTRY_END
+; ... get BADCOOKIE and a new cookie
+STEP 31 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all server_cookie
+REPLY QR RD RA DO YXRRSET ; BADCOOKIE is an extended rcode
+SECTION QUESTION
+test. IN TXT
+ENTRY_END
+
+; Query with an invalid cookie from a non-cookie protected address ...
+STEP 40 QUERY ADDRESS 1.2.3.4
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test. IN TXT
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 0a ; Opcode 10
+ 00 18 ; Length 24
+ 31 32 33 34 35 36 37 38 ; Random bits
+ 02 00 00 00 ; wrong version
+ 00 00 00 00 ; Timestamp
+ 31 32 33 34 35 36 37 38 ; wrong hash
+HEX_EDNSDATA_END
+ENTRY_END
+; ... get answer and a cookie
+STEP 41 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all server_cookie
+REPLY QR RD RA AA DO NOERROR
+SECTION QUESTION
+test. IN TXT
+SECTION ANSWER
+test. IN TXT "test"
+ENTRY_END
+
+; Query with a valid cookie ...
+STEP 50 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test. IN TXT
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 0a ; Opcode 10
+ 00 18 ; Length 24
+ 31 32 33 34 35 36 37 38 ; Random bits
+ 01 00 00 00 ; Version/Reserved
+ 00 00 00 00 ; Timestamp
+ 38 52 7b a8 c6 a4 ea 96 ; Hash
+HEX_EDNSDATA_END
+ENTRY_END
+; ... get answer and the cookie
+STEP 51 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all server_cookie
+REPLY QR RD RA AA DO NOERROR
+SECTION QUESTION
+test. IN TXT
+SECTION ANSWER
+test. IN TXT "test"
+ENTRY_END
+
+; Query with a valid >30 minutes old cookie ...
+STEP 59 TIME_PASSES ELAPSE 1801
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test. IN TXT
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 0a ; Opcode 10
+ 00 18 ; Length 24
+ 31 32 33 34 35 36 37 38 ; Random bits
+ 01 00 00 00 ; Version/Reserved
+ 00 00 00 00 ; Timestamp
+ 38 52 7b a8 c6 a4 ea 96 ; Hash
+HEX_EDNSDATA_END
+ENTRY_END
+; ... Get answer and a refreshed cookie
+; (we don't check the re-freshness here; it has its own unit test)
+STEP 61 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all server_cookie
+REPLY QR RD RA AA DO NOERROR
+SECTION QUESTION
+test. IN TXT
+SECTION ANSWER
+test. IN TXT "test"
+ENTRY_END
+
+; Query with a hash-valid >60 minutes old cookie ...
+STEP 69 TIME_PASSES ELAPSE 3601
+STEP 70 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test. IN TXT
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 0a ; Opcode 10
+ 00 18 ; Length 24
+ 31 32 33 34 35 36 37 38 ; Random bits
+ 01 00 00 00 ; Version/Reserved
+ 00 00 07 09 ; Timestamp (1801)
+ 77 81 38 e3 8f aa 72 86 ; Hash
+HEX_EDNSDATA_END
+ENTRY_END
+; ... get BADCOOKIE and a new cookie
+STEP 71 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all server_cookie
+REPLY QR RD RA DO YXRRSET ; BADCOOKIE is an extended rcode
+SECTION QUESTION
+test. IN TXT
+ENTRY_END
+
+; Query with a valid future (<5 minutes) cookie ...
+STEP 80 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test. IN TXT
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 0a ; Opcode 10
+ 00 18 ; Length 24
+ 31 32 33 34 35 36 37 38 ; Random bits
+ 01 00 00 00 ; Version/Reserved
+ 00 00 16 45 ; Timestamp (1801 + 3601 + 299)
+ 4a f5 0f df f0 e8 c7 09 ; Hash
+HEX_EDNSDATA_END
+ENTRY_END
+; ... get an answer
+STEP 81 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all server_cookie
+REPLY QR RD RA AA DO NOERROR
+SECTION QUESTION
+test. IN TXT
+SECTION ANSWER
+test. IN TXT "test"
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.conf b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.conf
new file mode 100644
index 000000000000..ae7d0cda0d9d
--- /dev/null
+++ b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.conf
@@ -0,0 +1,28 @@
+server:
+ verbosity: 5
+ # num-threads: 1
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: .
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ local-data: "test. IN TXT localdata"
+
+ ip-ratelimit: 1
+ ip-ratelimit-cookie: 0
+ ip-ratelimit-factor: 0
+ ip-ratelimit-backoff: yes
+ answer-cookie: yes
+ access-control: 127.0.0.0/8 allow_cookie
+
+remote-control:
+ control-enable: yes
+ control-interface: 127.0.0.1
+ # control-interface: ::1
+ control-port: @CONTROL_PORT@
+ server-key-file: "unbound_server.key"
+ server-cert-file: "unbound_server.pem"
+ control-key-file: "unbound_control.key"
+ control-cert-file: "unbound_control.pem"
diff --git a/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.dsc b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.dsc
new file mode 100644
index 000000000000..a6f6192360cd
--- /dev/null
+++ b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.dsc
@@ -0,0 +1,16 @@
+BaseName: ip_ratelimit
+Version: 1.0
+Description: Test IP source ratelimit.
+CreationDate: Tue Aug 8 00:00:00 CET 2023
+Maintainer: Yorgos Thessalonikefs
+Category:
+Component:
+CmdDepends:
+Depends:
+Help:
+Pre: ip_ratelimit.pre
+Post: ip_ratelimit.post
+Test: ip_ratelimit.test
+AuxFiles:
+Passed:
+Failure:
diff --git a/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.post b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.post
new file mode 100644
index 000000000000..1f86d008587d
--- /dev/null
+++ b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.post
@@ -0,0 +1,13 @@
+# #-- ip_ratelimit.post --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# source the test var file when it's there
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+#
+# do your teardown here
+. ../common.sh
+kill_pid $UNBOUND_PID
+if test -f unbound.log; then
+ echo ">>> unbound log"
+ cat unbound.log
+fi
diff --git a/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.pre b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.pre
new file mode 100644
index 000000000000..c4589a0ea4fe
--- /dev/null
+++ b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.pre
@@ -0,0 +1,24 @@
+# #-- ip_ratelimit.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+get_random_port 2
+UNBOUND_PORT=$RND_PORT
+CONTROL_PORT=$(($RND_PORT + 1))
+echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
+echo "CONTROL_PORT=$CONTROL_PORT" >> .tpkg.var.test
+
+# make config file
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@CONTROL_PORT\@/'$CONTROL_PORT'/' < ip_ratelimit.conf > ub.conf
+# start unbound in the background
+$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+
+wait_unbound_up unbound.log
+
+cat .tpkg.var.test
diff --git a/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.test b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.test
new file mode 100644
index 000000000000..f58b7edcbe2a
--- /dev/null
+++ b/contrib/unbound/testdata/ip_ratelimit.tdir/ip_ratelimit.test
@@ -0,0 +1,165 @@
+# #-- ip_ratelimit.test --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+
+get_make
+(cd $PRE; $MAKE streamtcp)
+
+# These tests rely on second time precision. To combat false negatives the
+# tests run multiple times and we allow 1/3 of the runs to fail.
+total_runs=6
+success_threshold=4 # 2/3*total_runs
+
+if dig -h 2>&1 | grep "cookie" >/dev/null; then
+ nocookie="+nocookie"
+else
+ nocookie=""
+fi
+
+echo "> First get a valid cookie"
+dig @127.0.0.1 -p $UNBOUND_PORT +ednsopt=10:0102030405060708 $nocookie +tcp +retry=0 +time=1 test. TXT >outfile 2>&1
+if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+if test `grep "COOKIE: " outfile | wc -l` -ne 1; then
+ echo "Could not get cookie"
+ echo "> cat logfiles"
+ cat outfile
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+fi
+cookie=`grep "COOKIE: " outfile | cut -d ' ' -f 3`
+
+successes=0
+echo "> Three parallel queries with backoff and cookie"
+# For this test we send three parallel queries. The ratelimit should be reached
+# for that second. We send a query to verify that there is no reply.
+# Then for the next second we again send three parallel queries and we expect
+# none of them to be allowed through because of the backoff logic that keeps
+# rolling the RATE_WINDOW based on demand.
+# Again we send another query but with a valid cookie and we expect to receive
+# an answer.
+for i in $(seq 1 $total_runs); do
+ # Try to hit limit
+ $PRE/streamtcp -nu -f 127.0.0.1@$UNBOUND_PORT test. TXT IN test. TXT IN test. TXT IN >outfile 2>&1
+ if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+ fi
+ # Expect no answer because of limit
+ dig @127.0.0.1 -p $UNBOUND_PORT $nocookie +retry=0 +time=1 test. TXT >outfile 2>&1
+ if test "$?" -eq 0; then
+ continue
+ fi
+ # Try to keep limit
+ $PRE/streamtcp -nu -f 127.0.0.1@$UNBOUND_PORT test. TXT IN test. TXT IN test. TXT IN >outfile 2>&1
+ if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+ fi
+ # Expect answer because of DNS cookie
+ dig @127.0.0.1 -p $UNBOUND_PORT +ednsopt=10:$cookie $nocookie +retry=0 +time=1 test. TXT >outfile 2>&1
+ if test "$?" -ne 0; then
+ continue
+ fi
+ ((successes++))
+ # We don't have to wait for all the runs to complete if we know
+ # we passed the threshold.
+ if test $successes -ge $success_threshold; then
+ break
+ fi
+done
+
+if test $successes -ge $success_threshold; then
+ echo "Three parallel queries with backoff and cookie OK"
+else
+ echo "Three parallel queries with backoff and cookie NOT OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat unbound.log
+ echo "Three parallel queries with backoff and cookie NOT OK"
+ exit 1
+fi
+
+echo "> Activating ip-ratelimit-cookie"
+echo "$PRE/unbound-control -c ub.conf set_option ip-ratelimit-cookie: 1"
+$PRE/unbound-control -c ub.conf set_option ip-ratelimit-cookie: 1
+if test $? -ne 0; then
+ echo "wrong exit value after success"
+ exit 1
+fi
+
+successes=0
+echo "> Three parallel queries with backoff and cookie with ip-ratelimit-cookie"
+# This is the exact same test as above with the exception that we don't expect
+# an answer on the last query because ip-ratelimit-cookie is now enabled.
+for i in $(seq 1 $total_runs); do
+ # Try to hit limit
+ $PRE/streamtcp -nu -f 127.0.0.1@$UNBOUND_PORT test. TXT IN test. TXT IN test. TXT IN >outfile 2>&1
+ if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+ fi
+ # Expect no answer because of limit
+ dig @127.0.0.1 -p $UNBOUND_PORT $nocookie +retry=0 +time=1 test. TXT >outfile 2>&1
+ if test "$?" -eq 0; then
+ continue
+ fi
+ # Try to keep limit
+ $PRE/streamtcp -nu -f 127.0.0.1@$UNBOUND_PORT test. TXT IN test. TXT IN test. TXT IN >outfile 2>&1
+ if test "$?" -ne 0; then
+ echo "exit status not OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat unbound.log
+ echo "Not OK"
+ exit 1
+ fi
+ # Expect no answer because of ip-ratelimit-cookie
+ dig @127.0.0.1 -p $UNBOUND_PORT +ednsopt=10:$cookie $nocookie +retry=0 +time=1 test. TXT >outfile 2>&1
+ if test "$?" -eq 0; then
+ continue
+ fi
+ ((successes++))
+ # We don't have to wait for all the runs to complete if we know
+ # we passed the threshold.
+ if test $successes -ge $success_threshold; then
+ break
+ fi
+done
+
+if test $successes -ge $success_threshold; then
+ echo "Three parallel queries with backoff and cookie with ip-ratelimit-cookie OK"
+else
+ echo "Three parallel queries with backoff and cookie with ip-ratelimit-cookie NOT OK"
+ echo "> cat logfiles"
+ cat outfile
+ cat unbound.log
+ echo "Three parallel queries with backoff and cookie with ip-ratelimit-cookie NOT OK"
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_control.key b/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_control.key
new file mode 100644
index 000000000000..753a4ef6162e
--- /dev/null
+++ b/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_control.key
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIG4gIBAAKCAYEAstEp+Pyh8XGrtZ77A4FhYjvbeB3dMa7Q2rGWxobzlA9przhA
+1aChAvUtCOAuM+rB6NTNB8YWfZJbQHawyMNpmC77cg6vXLYCGUQHZyAqidN049RJ
+F5T7j4N8Vniv17LiRdr0S6swy4PRvEnIPPV43EQHZqC5jVvHsKkhIfmBF/Dj5TXR
+ypeawWV/m5jeU6/4HRYMfytBZdO1mPXuWLh0lgbQ4SCbgrOUVD3rniMk1yZIbQOm
+vlDHYqekjDb/vOW2KxUQLG04aZMJ1mWfdbwG0CKQkSjISEDZ1l76vhM6mTM0fwXb
+IvyFZ9yPPCle1mF5aSlxS2cmGuGVSRQaw8XF9fe3a9ACJJTr33HdSpyaZkKRAUzL
+cKqLCl323daKv3NwwAT03Tj4iQM416ASMoiyfFa/2GWTKQVjddu8Crar7tGaf5xr
+lig4DBmrBvdYA3njy72/RD71hLwmlRoCGU7dRuDr9O6KASUm1Ri91ONZ/qdjMvov
+15l2vj4GV+KXR00dAgMBAAECggGAHepIL1N0dEQkCdpy+/8lH54L9WhpnOo2HqAf
+LU9eaKK7d4jdr9+TkD8cLaPzltPrZNxVALvu/0sA4SP6J1wpyj/x6P7z73qzly5+
+Xo5PD4fEwmi9YaiW/UduAblnEZrnp/AddptJKoL/D5T4XtpiQddPtael4zQ7kB57
+YIexRSQTvEDovA/o3/nvA0TrzOxfgd4ycQP3iOWGN/TMzyLsvjydrUwbOB567iz9
+whL3Etdgvnwh5Sz2blbFfH+nAR8ctvFFz+osPvuIVR21VMEI6wm7kTpSNnQ6sh/c
+lrLb/bTADn4g7z/LpIZJ+MrLvyEcoqValrLYeFBhM9CV8woPxvkO2P3pU47HVGax
+tC7GV6a/kt5RoKFd/TNdiA3OC7NGZtaeXv9VkPf4fVwBtSO9d5ZZXTGEynDD/rUQ
+U4KFJe6OD23APjse08HiiKqTPhsOneOONU67iqoaTdIkT2R4EdlkVEDpXVtWb+G9
+Q+IqYzVljlzuyHrhWXLJw/FMa2aBAoHBAOnZbi4gGpH+P6886WDWVgIlTccuXoyc
+Mg9QQYk9UDeXxL0AizR5bZy49Sduegz9vkHpAiZARQsUnizHjZ8YlRcrmn4t6tx3
+ahTIKAjdprnxJfYINM580j8CGbXvX5LhIlm3O267D0Op+co3+7Ujy+cjsIuFQrP+
+1MqMgXSeBjzC1APivmps7HeFE+4w0k2PfN5wSMDNCzLo99PZuUG5XZ93OVOS5dpN
+b+WskdcD8NOoJy/X/5A08veEI/jYO/DyqQKBwQDDwUQCOWf41ecvJLtBHKmEnHDz
+ftzHino9DRKG8a9XaN4rmetnoWEaM2vHGX3pf3mwH+dAe8vJdAQueDhBKYeEpm6C
+TYNOpou1+Zs5s99BilCTNYo8fkMOAyqwRwmz9zgHS6QxXuPwsghKefLJGt6o6RFF
+tfWVTfLlYJ+I3GQe3ySsk3wjVz4oUTKiyiq5+KzD+HhEkS7u+RQ7Z0ZI2xd2cF8Y
+aN2hjKDpcOiFf3CDoqka5D1qMNLgIHO52AHww1UCgcA1h7o7AMpURRka6hyaODY0
+A4oMYEbwdQjYjIyT998W+rzkbu1us6UtzQEBZ760npkgyU/epbOoV63lnkCC/MOU
+LD0PST+L/CHiY/cWIHb79YG1EifUZKpUFg0Aoq0EGFkepF0MefGCkbRGYA5UZr9U
+R80wAu9D+L+JJiS0J0BSRF74DL196zUuHt5zFeXuLzxsRtPAnq9DliS08BACRYZy
+7H3I7cWD9Vn5/0jbKWHFcaaWwyETR6uekTcSzZzbCRECgcBeoE3/xUA9SSk34Mmj
+7/cB4522Ft0imA3+9RK/qJTZ7Bd5fC4PKjOGNtUiqW/0L2rjeIiQ40bfWvWqgPKw
+jSK1PL6uvkl6+4cNsFsYyZpiVDoe7wKju2UuoNlB3RUTqa2r2STFuNj2wRjA57I1
+BIgdnox65jqQsd14g/yaa+75/WP9CE45xzKEyrtvdcqxm0Pod3OrsYK+gikFjiar
+kT0GQ8u0QPzh2tjt/2ZnIfOBrl+QYERP0MofDZDjhUdq2wECgcB0Lu841+yP5cdR
+qbJhXO4zJNh7oWNcJlOuQp3ZMNFrA1oHpe9pmLukiROOy01k9WxIMQDzU5GSqRv3
+VLkYOIcbhJ3kClKAcM3j95SkKbU2H5/RENb3Ck52xtl4pNU1x/3PnVFZfDVuuHO9
+MZ9YBcIeK98MyP2jr5JtFKnOyPE7xKq0IHIhXadpbc2wjje5FtZ1cUtMyEECCXNa
+C1TpXebHGyXGpY9WdWXhjdE/1jPvfS+uO5WyuDpYPr339gsdq1g=
+-----END RSA PRIVATE KEY-----
diff --git a/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_control.pem b/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_control.pem
new file mode 100644
index 000000000000..a1edf7017f1d
--- /dev/null
+++ b/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_control.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDszCCAhsCFGD5193whHQ2bVdzbaQfdf1gc4SkMA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB3VuYm91bmQwHhcNMjAwNzA4MTMzMjMwWhcNNDAwMzI1MTMzMjMw
+WjAaMRgwFgYDVQQDDA91bmJvdW5kLWNvbnRyb2wwggGiMA0GCSqGSIb3DQEBAQUA
+A4IBjwAwggGKAoIBgQCy0Sn4/KHxcau1nvsDgWFiO9t4Hd0xrtDasZbGhvOUD2mv
+OEDVoKEC9S0I4C4z6sHo1M0HxhZ9kltAdrDIw2mYLvtyDq9ctgIZRAdnICqJ03Tj
+1EkXlPuPg3xWeK/XsuJF2vRLqzDLg9G8Scg89XjcRAdmoLmNW8ewqSEh+YEX8OPl
+NdHKl5rBZX+bmN5Tr/gdFgx/K0Fl07WY9e5YuHSWBtDhIJuCs5RUPeueIyTXJkht
+A6a+UMdip6SMNv+85bYrFRAsbThpkwnWZZ91vAbQIpCRKMhIQNnWXvq+EzqZMzR/
+Bdsi/IVn3I88KV7WYXlpKXFLZyYa4ZVJFBrDxcX197dr0AIklOvfcd1KnJpmQpEB
+TMtwqosKXfbd1oq/c3DABPTdOPiJAzjXoBIyiLJ8Vr/YZZMpBWN127wKtqvu0Zp/
+nGuWKDgMGasG91gDeePLvb9EPvWEvCaVGgIZTt1G4Ov07ooBJSbVGL3U41n+p2My
++i/XmXa+PgZX4pdHTR0CAwEAATANBgkqhkiG9w0BAQsFAAOCAYEAd++Wen6l8Ifj
+4h3p/y16PhSsWJWuJ4wdNYy3/GM84S26wGjzlEEwiW76HpH6VJzPOiBAeWnFKE83
+hFyetEIxgJeIPbcs9ZP/Uoh8GZH9tRISBSN9Hgk2Slr9llo4t1H0g/XTgA5HqMQU
+9YydlBh43G7Vw3FVwh09OM6poNOGQKNc/tq2/QdKeUMtyBbLWpRmjH5XcCT35fbn
+ZiVOUldqSHD4kKrFO4nJYXZyipRbcXybsLiX9GP0GLemc3IgIvOXyJ2RPp06o/SJ
+pzlMlkcAfLJaSuEW57xRakhuNK7m051TKKzJzIEX+NFYOVdafFHS8VwGrYsdrFvD
+72tMfu+Fu55y3awdWWGc6YlaGogZiuMnJkvQphwgn+5qE/7CGEckoKEsH601rqIZ
+muaIc85+nEcHJeijd/ZlBN9zeltjFoMuqTUENgmv8+tUAdVm/UMY9Vjme6b43ydP
+uv6DS02+k9z8toxXworLiPr94BGaiGV1NxgwZKLZigYJt/Fi2Qte
+-----END CERTIFICATE-----
diff --git a/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_server.key b/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_server.key
new file mode 100644
index 000000000000..370a7bbb2f22
--- /dev/null
+++ b/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_server.key
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIG5AIBAAKCAYEAvjSVSN2QMXudpzukdLCqgg/IOhCX8KYkD0FFFfWcQjgKq5wI
+0x41iG32a6wbGanre4IX7VxaSPu9kkHfnGgynCk5nwDRedE/FLFhAU78PoT0+Nqq
+GRS7XVQ24vLmIz9Hqc2Ozx1um1BXBTmIT0UfN2e22I0LWQ6a3seZlEDRj45gnk7Z
+uh9MDgotaBdm+v1JAbupSf6Zis4VEH3JNdvVGE3O1DHEIeuuz/3BDhpf6WBDH+8K
+WaBe1ca4TZHr9ThL2gEMEfAQl0wXDwRWRoi3NjNMH+mw0L1rjwThI5GXqNIee7o5
+FzUReSXZuTdFMyGe3Owcx+XoYnwi6cplSNoGsDBu4B9bKKglR9YleJVw4L4Xi8xP
+q6O9UPj4+nypHk/DOoC7DIM3ufN0yxPBsFo5TVowxfhdjZXJbbftd2TZv7AH8+XL
+A5UoZgRzXgzECelXSCTBFlMTnT48LfA9pMLydyjAz2UdPHs5Iv+TK5nnI+aJoeaP
+7kFZSngxdy1+A/bNAgMBAAECggGBALpTOIqQwVg4CFBylL/a8K1IWJTI/I65sklf
+XxYL7G7SB2HlEJ//z+E+F0+S4Vlao1vyLQ5QkgE82pAUB8FoMWvY1qF0Y8A5wtm6
+iZSGk4OLK488ZbT8Ii9i+AGKgPe2XbVxsJwj8N4k7Zooqec9hz73Up8ATEWJkRz7
+2u7oMGG4z91E0PULA64dOi3l/vOQe5w/Aa+CwVbAWtI05o7kMvQEBMDJn6C7CByo
+MB5op9wueJMnz7PM7hns+U7Dy6oE4ljuolJUy51bDzFWwoM54cRoQqLFNHd8JVQj
+WxldCkbfF43iyprlsEcUrTyUjtdA+ZeiG39vg/mtdmgNpGmdupHJZQvSuG8IcVlz
+O+eMSeQS1QXPD6Ik8UK4SU0h+zOl8xIWtRrsxQuh4fnTN40udm/YUWl/6gOebsBI
+IrVLlKGqJSfB3tMjpCRqdTzJ0dA9keVpkqm2ugZkxEf1+/efq/rFIQ2pUBLCqNTN
+qpNqruK8y8FphP30I2uI4Ej2UIB8AQKBwQDd2Yptj2FyDyaXCycsyde0wYkNyzGU
+dRnzdibfHnMZwjgTjwAwgIUBVIS8H0/z7ZJQKN7osJfddMrtjJtYYUk9g/dCpHXs
+bNh2QSoWah3FdzNGuWd0iRf9+LFxhjAAMo/FS8zFJAJKrFsBdCGTfFUMdsLC0bjr
+YjiWBuvV72uKf8XIZX5KIZruKdWBBcWukcb21R1UDyFYyXRBsly5XHaIYKZql3km
+7pV7MKWO0IYgHbHIqGUqPQlzZ/lkunS1jKECgcEA23wHffD6Ou9/x3okPx2AWpTr
+gh8rgqbyo6hQkBW5Y90Wz824cqaYebZDaBR/xlVx/YwjKkohv8Bde2lpH/ZxRZ1Z
+5Sk2s6GJ/vU0L9RsJZgCgj4L6Coal1NMxuZtCXAlnOpiCdxSZgfqbshbTVz30KsG
+ZJG361Cua1ScdAHxlZBxT52/1Sm0zRC2hnxL7h4qo7Idmtzs40LAJvYOKekR0pPN
+oWeJfra7vgx/jVNvMFWoOoSLpidVO4g+ot4ery6tAoHAdW3rCic1C2zdnmH28Iw+
+s50l8Lk3mz+I5wgJd1zkzCO0DxZIoWPGA3g7cmCYr6N3KRsZMs4W9NAXgjpFGDkW
+zYsG3K21BdpvkdjYcFjnPVjlOXB2RIc0vehf9Jl02wXoeCSxVUDEPcaRvWk9RJYx
+ZpGOchUU7vNkxHURbIJ4yCzuAi9G8/Jp0dsu+kaV5tufF5SjG5WOrzKjaQsCbdN1
+oqaWMCHRrTvov/Z2C+xwsptFOdN5CSyZzg6hQiI4GMlBAoHAXyb6KINcOEi0YMp3
+BFXJ23tMTnEs78tozcKeipigcsbaqORK3omS+NEnj+uzKUzJyl4CsMbKstK2tFYS
+mSTCHqgE3PBtIpsZtEqhgUraR8IK9GPpzZDTTl9ynZgwFTNlWw3RyuyVXF56J+T8
+kCGJ3hEHCHqT/ZRQyX85BKIDFhA0z4tYKxWVqIFiYBNq56R0X9tMMmMs36mEnF93
+7Ht6mowxTZQRa7nU0qOgeKh/P7ki4Zus3y+WJ+T9IqahLtlRAoHBAIhqMrcxSAB8
+RpB9jukJlAnidw2jCMPgrFE8tP0khhVvGrXMldxAUsMKntDIo8dGCnG1KTcWDI0O
+jepvSPHSsxVLFugL79h0eVIS5z4huW48i9xgU8VlHdgAcgEPIAOFcOw2BCu/s0Vp
+O+MM/EyUOdo3NsibB3qc/GJI6iNBYS7AljYEVo6rXo5V/MZvZUF4vClen6Obzsre
+MTTb+4sJjfqleWuvr1XNMeu2mBfXBQkWGZP1byBK0MvD/aQ2PWq92A==
+-----END RSA PRIVATE KEY-----
diff --git a/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_server.pem b/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_server.pem
new file mode 100644
index 000000000000..986807310f2b
--- /dev/null
+++ b/contrib/unbound/testdata/ip_ratelimit.tdir/unbound_server.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDqzCCAhMCFBHWXeQ6ZIa9QcQbXLFfC6tj+KA+MA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB3VuYm91bmQwHhcNMjAwNzA4MTMzMjI5WhcNNDAwMzI1MTMzMjI5
+WjASMRAwDgYDVQQDDAd1bmJvdW5kMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
+igKCAYEAvjSVSN2QMXudpzukdLCqgg/IOhCX8KYkD0FFFfWcQjgKq5wI0x41iG32
+a6wbGanre4IX7VxaSPu9kkHfnGgynCk5nwDRedE/FLFhAU78PoT0+NqqGRS7XVQ2
+4vLmIz9Hqc2Ozx1um1BXBTmIT0UfN2e22I0LWQ6a3seZlEDRj45gnk7Zuh9MDgot
+aBdm+v1JAbupSf6Zis4VEH3JNdvVGE3O1DHEIeuuz/3BDhpf6WBDH+8KWaBe1ca4
+TZHr9ThL2gEMEfAQl0wXDwRWRoi3NjNMH+mw0L1rjwThI5GXqNIee7o5FzUReSXZ
+uTdFMyGe3Owcx+XoYnwi6cplSNoGsDBu4B9bKKglR9YleJVw4L4Xi8xPq6O9UPj4
++nypHk/DOoC7DIM3ufN0yxPBsFo5TVowxfhdjZXJbbftd2TZv7AH8+XLA5UoZgRz
+XgzECelXSCTBFlMTnT48LfA9pMLydyjAz2UdPHs5Iv+TK5nnI+aJoeaP7kFZSngx
+dy1+A/bNAgMBAAEwDQYJKoZIhvcNAQELBQADggGBABunf93MKaCUHiZgnoOTinsW
+84/EgInrgtKzAyH+BhnKkJOhhR0kkIAx5d9BpDlaSiRTACFon9moWCgDIIsK/Ar7
+JE0Kln9cV//wiiNoFU0O4mnzyGUIMvlaEX6QHMJJQYvL05+w/3AAcf5XmMJtR5ca
+fJ8FqvGC34b2WxX9lTQoyT52sRt+1KnQikiMEnEyAdKktMG+MwKsFDdOwDXyZhZg
+XZhRrfX3/NVJolqB6EahjWIGXDeKuSSKZVtCyib6LskyeMzN5lcRfvubKDdlqFVF
+qlD7rHBsKhQUWK/IO64mGf7y/de+CgHtED5vDvr/p2uj/9sABATfbrOQR3W/Of25
+sLBj4OEfrJ7lX8hQgFaxkMI3x6VFT3W8dTCp7xnQgb6bgROWB5fNEZ9jk/gjSRmD
+yIU+r0UbKe5kBk/CmZVFXL2TyJ92V5NYEQh8V4DGy19qZ6u/XKYyNJL4ocs35GGe
+CA8SBuyrmdhx38h1RHErR2Skzadi1S7MwGf1y431fQ==
+-----END CERTIFICATE-----
diff --git a/contrib/unbound/testdata/iter_cname_minimise_nx.rpl b/contrib/unbound/testdata/iter_cname_minimise_nx.rpl
new file mode 100644
index 000000000000..a04eb8b24791
--- /dev/null
+++ b/contrib/unbound/testdata/iter_cname_minimise_nx.rpl
@@ -0,0 +1,245 @@
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: yes
+ module-config: "validator iterator"
+ trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
+ val-override-date: "20070916134226"
+ fake-sha1: yes
+ trust-anchor-signaling: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test cname chain resolution of nxdomain with qname minimisation.
+; the qtype CNAME lookup has NXDOMAIN.
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.44
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.44
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.44
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. AAZrcta3WCyz0iq2p78gmcPpXbmXPP9nQXM/czH1R9ilCaEoV8E27UU=
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.com. IN A
+SECTION ANSWER
+ns.example.com. IN A 1.2.3.44
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. AAZrcta3WCyz0iq2p78gmcPpXbmXPP9nQXM/czH1R9ilCaEoV8E27UU=
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.44
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. AAZrcta3WCyz0iq2p78gmcPpXbmXPP9nQXM/czH1R9ilCaEoV8E27UU=
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+ns.example.com. IN AAAA
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.44
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. AAZrcta3WCyz0iq2p78gmcPpXbmXPP9nQXM/czH1R9ilCaEoV8E27UU=
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NXDOMAIN
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+example.com. 300 IN SOA a. b. 1 2 3 4 300
+example.com. 300 IN RRSIG SOA 3 2 300 20070926134150 20070829134150 2854 example.com. AFPx1ZhcHixnxfB90ha4zgp7A+EdM8L63tUnVdlI5B14NiRIXONPDB4=
+v.example.com. IN NSEC x.example.com. A AAAA RRSIG NSEC
+v.example.com. 3600 IN RRSIG NSEC 3 3 3600 20070926134150 20070829134150 2854 example.com. AFT0Ao01lUN8Ppa9QPayQIN9ZtNIj4TzyhUQV31+FhNRK5uSQhiVwMc=
+example.com. 3600 IN NSEC abc.example.com. NS SOA RRSIG NSEC DNSKEY
+example.com. 3600 IN RRSIG NSEC 3 2 3600 20070926134150 20070829134150 2854 example.com. ABEOu6iietfjKY1MS0TutZZxUtRYA6XKsC1rMTrenwBF2darY3/Emco=
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NXDOMAIN
+SECTION QUESTION
+c.example.com. IN A
+SECTION ANSWER
+c.example.com. 10 IN CNAME www.example.com.
+c.example.com. 10 IN RRSIG CNAME 3 3 10 20070926134150 20070829134150 2854 example.com. ABT7twnK5qkCBKnaOHxFthUOK+3rBge1wEMItoFPdf16OoVdfccYU2U=
+SECTION AUTHORITY
+example.com. 300 IN SOA a. b. 1 2 3 4 300
+example.com. 300 IN RRSIG SOA 3 2 300 20070926134150 20070829134150 2854 example.com. AFPx1ZhcHixnxfB90ha4zgp7A+EdM8L63tUnVdlI5B14NiRIXONPDB4=
+v.example.com. IN NSEC x.example.com. A AAAA RRSIG NSEC
+v.example.com. 3600 IN RRSIG NSEC 3 3 3600 20070926134150 20070829134150 2854 example.com. AFT0Ao01lUN8Ppa9QPayQIN9ZtNIj4TzyhUQV31+FhNRK5uSQhiVwMc=
+example.com. 3600 IN NSEC abc.example.com. NS SOA RRSIG NSEC DNSKEY
+example.com. 3600 IN RRSIG NSEC 3 2 3600 20070926134150 20070829134150 2854 example.com. ABEOu6iietfjKY1MS0TutZZxUtRYA6XKsC1rMTrenwBF2darY3/Emco=
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+c.example.com. IN CNAME
+SECTION ANSWER
+c.example.com. 10 IN CNAME www.example.com.
+c.example.com. 10 IN RRSIG CNAME 3 3 10 20070926134150 20070829134150 2854 example.com. ABT7twnK5qkCBKnaOHxFthUOK+3rBge1wEMItoFPdf16OoVdfccYU2U=
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+c.example.com. IN CNAME
+ENTRY_END
+
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+c.example.com. IN CNAME
+SECTION ANSWER
+c.example.com. 10 IN CNAME www.example.com.
+c.example.com. 10 IN RRSIG CNAME 3 3 10 20070926134150 20070829134150 2854 example.com. ABT7twnK5qkCBKnaOHxFthUOK+3rBge1wEMItoFPdf16OoVdfccYU2U=
+ENTRY_END
+
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+c.example.com. IN CNAME
+ENTRY_END
+
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+c.example.com. IN CNAME
+SECTION ANSWER
+c.example.com. 10 IN CNAME www.example.com.
+c.example.com. 10 IN RRSIG CNAME 3 3 10 20070926134150 20070829134150 2854 example.com. ABT7twnK5qkCBKnaOHxFthUOK+3rBge1wEMItoFPdf16OoVdfccYU2U=
+ENTRY_END
+
+STEP 50 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+c.example.com. IN A
+ENTRY_END
+
+STEP 60 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD DO NXDOMAIN
+SECTION QUESTION
+c.example.com. IN A
+SECTION ANSWER
+c.example.com. 10 IN CNAME www.example.com.
+c.example.com. 10 IN RRSIG CNAME 3 3 10 20070926134150 20070829134150 2854 example.com. ABT7twnK5qkCBKnaOHxFthUOK+3rBge1wEMItoFPdf16OoVdfccYU2U=
+SECTION AUTHORITY
+example.com. 300 IN SOA a. b. 1 2 3 4 300
+example.com. 300 IN RRSIG SOA 3 2 300 20070926134150 20070829134150 2854 example.com. AFPx1ZhcHixnxfB90ha4zgp7A+EdM8L63tUnVdlI5B14NiRIXONPDB4=
+v.example.com. IN NSEC x.example.com. A AAAA RRSIG NSEC
+v.example.com. 3600 IN RRSIG NSEC 3 3 3600 20070926134150 20070829134150 2854 example.com. AFT0Ao01lUN8Ppa9QPayQIN9ZtNIj4TzyhUQV31+FhNRK5uSQhiVwMc=
+example.com. 3600 IN NSEC abc.example.com. NS SOA RRSIG NSEC DNSKEY
+example.com. 3600 IN RRSIG NSEC 3 2 3600 20070926134150 20070829134150 2854 example.com. ABEOu6iietfjKY1MS0TutZZxUtRYA6XKsC1rMTrenwBF2darY3/Emco=
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/iter_dname_ttl.rpl b/contrib/unbound/testdata/iter_dname_ttl.rpl
new file mode 100644
index 000000000000..115947af3ab3
--- /dev/null
+++ b/contrib/unbound/testdata/iter_dname_ttl.rpl
@@ -0,0 +1,310 @@
+; config options
+; The island of trust is at example.com
+; validation is enabled because the pickup of DNAME from cache wants
+; a DNSSEC signed DNAME.
+server:
+ trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
+ trust-anchor: "example.net. 3600 IN DNSKEY 256 3 5 AQPQ41chR9DEHt/aIzIFAqanbDlRflJoRs5yz1jFsoRIT7dWf0r+PeDuewdxkszNH6wnU4QL8pfKFRh5PIYVBLK3 ;{id = 30899 (zsk), size = 512b}"
+ val-override-date: "20070916134226"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-sha1: yes
+ trust-anchor-signaling: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test iterator for TTL of synthesized CNAME of a DNAME from cache.
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+net. IN A
+SECTION AUTHORITY
+net. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+net. IN NS
+SECTION ANSWER
+net. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.net. IN A
+SECTION AUTHORITY
+example.net. IN NS ns.example.net.
+SECTION ADDITIONAL
+ns.example.net. IN A 1.2.3.5
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com. 3600 IN RRSIG DNSKEY DSA 2 3600 20070926134150 20070829134150 2854 example.com. MCwCFBQRtlR4BEv9ohi+PGFjp+AHsJuHAhRCvz0shggvnvI88DFnBDCczHUcVA== ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN CNAME
+SECTION ANSWER
+www.example.com. IN CNAME www.example.net.
+www.example.com. 3600 IN RRSIG CNAME DSA 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFGcJxnNxpWCBzXejiSdl4p1BKRMnAhUApoJrugVBRwFgAoYAhhqlZFac7fE= ;{id = 2854}
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www2.example.com. IN A
+SECTION ANSWER
+www2.example.com. 3600 IN CNAME www.example.net.
+www2.example.com. 3600 IN RRSIG CNAME 3 3 3600 20070926135752 20070829135752 2854 example.com. AGgh6pDCL7VF0uJablClW7cgvsPuNzpHZ+M7nZIwi61+0RPhFZLHcN4=
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+foo.test-dname.example.com. IN A
+SECTION ANSWER
+test-dname.example.com. 3600 IN DNAME example.net.
+test-dname.example.com. 3600 IN RRSIG DNAME 3 3 3600 20070926135752 20070829135752 2854 example.com. ACp31Evt1c6tKzmTh/smAuGFydZ1OO26Qkej/BW4Bw5RFBQiKaY22Z0=
+foo.test-dname.example.com. 3600 IN CNAME foo.example.net.
+ENTRY_END
+RANGE_END
+
+; ns.example.net.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.5
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.net. IN NS
+SECTION ANSWER
+example.net. IN NS ns.example.net.
+example.net. 3600 IN RRSIG NS RSASHA1 2 3600 20070926134150 20070829134150 30899 example.net. E8JX0l4B+cSR5bkHQwOJy1pBmlLMTYCJ8EwfNMU/eCv0YhKwo26rHhn52FGisgv+Nwp7/NbhHqQ+kJgoZC94XA== ;{id = 30899}
+SECTION ADDITIONAL
+ns.example.net. IN A 1.2.3.5
+ns.example.net. 3600 IN RRSIG A RSASHA1 3 3600 20070926134150 20070829134150 30899 example.net. x+tQMC9FhzT7Fcy1pM5NrOC7E8nLd7THPI3C6ie4EwL8PrxllqlR3q/DKB0d/m0qCOPcgN6HFOYURV1s4uAcsw== ;{id = 30899}
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.net. IN DNSKEY
+SECTION ANSWER
+example.net. 3600 IN DNSKEY 256 3 5 AQPQ41chR9DEHt/aIzIFAqanbDlRflJoRs5yz1jFsoRIT7dWf0r+PeDuewdxkszNH6wnU4QL8pfKFRh5PIYVBLK3 ;{id = 30899 (zsk), size = 512b}
+example.net. 3600 IN RRSIG DNSKEY RSASHA1 2 3600 20070926134150 20070829134150 30899 example.net. hiFzlQ8VoYgCuvIsfVuxC3mfJDqsTh0yc6abs5xMx5uEcIjb0dndFQx7INOM+imlzveEN73Hqp4OLFpFhsWLlw== ;{id = 30899}
+SECTION AUTHORITY
+example.net. IN NS ns.example.net.
+example.net. 3600 IN RRSIG NS RSASHA1 2 3600 20070926134150 20070829134150 30899 example.net. E8JX0l4B+cSR5bkHQwOJy1pBmlLMTYCJ8EwfNMU/eCv0YhKwo26rHhn52FGisgv+Nwp7/NbhHqQ+kJgoZC94XA== ;{id = 30899}
+SECTION ADDITIONAL
+ns.example.net. IN A 1.2.3.5
+ns.example.net. 3600 IN RRSIG A RSASHA1 3 3600 20070926134150 20070829134150 30899 example.net. x+tQMC9FhzT7Fcy1pM5NrOC7E8nLd7THPI3C6ie4EwL8PrxllqlR3q/DKB0d/m0qCOPcgN6HFOYURV1s4uAcsw== ;{id = 30899}
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.net. IN A
+SECTION ANSWER
+www.example.net. IN A 11.12.13.14
+www.example.net. 3600 IN RRSIG A 5 3 3600 20070926134150 20070829134150 30899 example.net. CPxF5hK9Kg5eT7W6LgZwr0ePYEm9HMcSY4vvqCS6gDWB4X9jvXLCfBkCLhsNybPBpGWlsLi5wM6MTdJXuPpsRA== ;{id = 30899}
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+foo.example.net. IN A
+SECTION ANSWER
+foo.example.net. IN A 11.12.13.15
+foo.example.net. 3600 IN RRSIG A 5 3 3600 20070926134150 20070829134150 30899 example.net. X6T6SE9UzxAD/4zKpwGOxEDyE4g7lfYYw3lvw533uwRN8mWTcBvSva0/jjyhrogJcuLO32jPHK6zGb93w2xnuA==
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+foo2.example.net. IN A
+SECTION ANSWER
+foo2.example.net. IN A 11.12.13.16
+foo2.example.net. 3600 IN RRSIG A 5 3 3600 20070926134150 20070829134150 30899 example.net. BZm+GljD8m9N+pNJN8D+LlSyHqM+InNUe0+heKILR9be+Goqv6SEb7LKtX6+kj3239Y5by7u+/Cuk8kkWistEQ==
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+RANGE_END
+
+STEP 1 TIME_PASSES ELAPSE 10
+; Get DNAME in cache and then pick it up again from cache.
+STEP 10 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+foo.test-dname.example.com. IN A
+ENTRY_END
+
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+foo.test-dname.example.com. IN A
+SECTION ANSWER
+test-dname.example.com. 3600 IN DNAME example.net.
+test-dname.example.com. 3600 IN RRSIG DNAME 3 3 3600 20070926135752 20070829135752 2854 example.com. ACp31Evt1c6tKzmTh/smAuGFydZ1OO26Qkej/BW4Bw5RFBQiKaY22Z0=
+foo.test-dname.example.com. 3600 IN CNAME foo.example.net.
+foo.example.net. IN A 11.12.13.15
+foo.example.net. 3600 IN RRSIG A 5 3 3600 20070926134150 20070829134150 30899 example.net. X6T6SE9UzxAD/4zKpwGOxEDyE4g7lfYYw3lvw533uwRN8mWTcBvSva0/jjyhrogJcuLO32jPHK6zGb93w2xnuA==
+ENTRY_END
+
+STEP 30 TIME_PASSES ELAPSE 10
+
+; Use DNAME from cache
+STEP 40 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+foo2.test-dname.example.com. IN A
+ENTRY_END
+
+; Test the TTL on the synthesized CNAME for the DNAME record from cache.
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+foo2.test-dname.example.com. IN A
+SECTION ANSWER
+test-dname.example.com. 3590 IN DNAME example.net.
+test-dname.example.com. 3590 IN RRSIG DNAME 3 3 3600 20070926135752 20070829135752 2854 example.com. ACp31Evt1c6tKzmTh/smAuGFydZ1OO26Qkej/BW4Bw5RFBQiKaY22Z0=
+foo2.test-dname.example.com. 3590 IN CNAME foo2.example.net.
+foo2.example.net. 3600 IN A 11.12.13.16
+foo2.example.net. 3600 IN RRSIG A 5 3 3600 20070926134150 20070829134150 30899 example.net. BZm+GljD8m9N+pNJN8D+LlSyHqM+InNUe0+heKILR9be+Goqv6SEb7LKtX6+kj3239Y5by7u+/Cuk8kkWistEQ==
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/iter_failreply.rpl b/contrib/unbound/testdata/iter_failreply.rpl
new file mode 100644
index 000000000000..393714196d89
--- /dev/null
+++ b/contrib/unbound/testdata/iter_failreply.rpl
@@ -0,0 +1,132 @@
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ minimal-responses: no
+ log-servfail: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test iterator fail_reply report
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. IN NS ns2.example.net.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. IN AAAA ::1
+ns2.example.net. IN AAAA ::1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns2.example.net. IN A
+SECTION ANSWER
+ns2.example.net. IN A 1.2.3.5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns2.example.net. IN AAAA
+SECTION ANSWER
+ns2.example.net. IN AAAA ::1
+ENTRY_END
+
+RANGE_END
+
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR SERVFAIL
+SECTION QUESTION
+ns.example.com. IN A
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR SERVFAIL
+SECTION QUESTION
+ns.example.com. IN AAAA
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+STEP 20 CHECK_OUT_QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+STEP 21 TIMEOUT
+STEP 22 TIMEOUT
+STEP 23 TIMEOUT
+STEP 24 TIMEOUT
+STEP 25 TIMEOUT
+
+STEP 31 TIMEOUT
+STEP 32 TIMEOUT
+STEP 33 TIMEOUT
+STEP 34 TIMEOUT
+
+; recursion happens here.
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/iter_ignore_empty.rpl b/contrib/unbound/testdata/iter_ignore_empty.rpl
new file mode 100644
index 000000000000..4b2f695b8501
--- /dev/null
+++ b/contrib/unbound/testdata/iter_ignore_empty.rpl
@@ -0,0 +1,248 @@
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ minimal-responses: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test ignore of an empty response.
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. IN NS ns2.example2.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example2.com. IN NS
+SECTION AUTHORITY
+example2.com. IN NS ns2.example2.com.
+SECTION ADDITIONAL
+ns2.example2.com. IN A 1.2.3.5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+foo.com. IN NS
+SECTION AUTHORITY
+foo.com. IN NS ns.foo.com.
+SECTION ADDITIONAL
+ns.foo.com. IN A 1.2.3.5
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. IN NS ns2.example.net.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.example.com. IN A
+SECTION ANSWER
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.example.com. IN AAAA
+SECTION AUTHORITY
+example.com. IN SOA ns root 4 14400 3600 604800 3600
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+RANGE_END
+
+; ns2.example2.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.5
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+example2.com. IN NS
+SECTION ANSWER
+example2.com. IN NS ns2.example2.com.
+SECTION ADDITIONAL
+ns2.example2.com. IN A 1.2.3.5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns2.example2.com. IN A
+SECTION ANSWER
+ns2.example2.com. IN A 1.2.3.5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns2.example2.com. IN AAAA
+SECTION AUTHORITY
+example2.com. IN SOA ns2 root 4 14400 3600 604800 3600
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+ENTRY_END
+
+; foo.com
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.foo.com. IN A
+SECTION ANSWER
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.foo.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+;foo.com. IN SOA ns2.foo.com root.foo.com 4 14400 3600 604800 3600
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+ENTRY_END
+
+; wait for pending nameserver lookups.
+STEP 20 TRAFFIC
+
+; Test that a nodata stays a nodata.
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.foo.com. IN A
+ENTRY_END
+
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.foo.com. IN A
+SECTION ANSWER
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/iter_nat64.rpl b/contrib/unbound/testdata/iter_nat64.rpl
new file mode 100644
index 000000000000..dde0a25596c1
--- /dev/null
+++ b/contrib/unbound/testdata/iter_nat64.rpl
@@ -0,0 +1,117 @@
+; config options
+server:
+ do-nat64: yes
+ target-fetch-policy: "0 0 0 0 0"
+
+stub-zone:
+ name: "."
+ stub-addr: 2001:db8::1
+CONFIG_END
+
+SCENARIO_BEGIN Test NAT64 transport for a v4-only server.
+
+RANGE_BEGIN 0 100
+ ADDRESS 2001:db8::1
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS FAKE.ROOT.
+SECTION ADDITIONAL
+FAKE.ROOT. IN AAAA 2001:db8::1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+v4only. IN NS
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+RANGE_END
+
+; replies from NS over "NAT64"
+
+RANGE_BEGIN 0 100
+ ADDRESS 64:ff9b::c000:0201
+
+; A over NAT64
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+ns.v4only. IN A
+SECTION ANSWER
+ns.v4only. IN A 192.0.2.1
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+ENTRY_END
+
+; no AAAA
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+ns.v4only. IN AAAA
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+v4only. IN NS
+SECTION ANSWER
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+test.v4only. IN A
+SECTION ANSWER
+test.v4only. IN A 192.0.2.2
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test.v4only. IN A
+ENTRY_END
+
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+test.v4only. IN A
+SECTION ANSWER
+test.v4only. IN A 192.0.2.2
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/iter_nat64_prefix.rpl b/contrib/unbound/testdata/iter_nat64_prefix.rpl
new file mode 100644
index 000000000000..ecb6508dcf55
--- /dev/null
+++ b/contrib/unbound/testdata/iter_nat64_prefix.rpl
@@ -0,0 +1,119 @@
+; config options
+server:
+ do-nat64: yes
+ nat64-prefix: 2001:db8:1234::/96
+ target-fetch-policy: "0 0 0 0 0"
+ do-ip4: no
+
+stub-zone:
+ name: "."
+ stub-addr: 2001:db8::1
+CONFIG_END
+
+SCENARIO_BEGIN Test NAT64 transport for a v4-only server, custom NAT64 prefix.
+
+RANGE_BEGIN 0 100
+ ADDRESS 2001:db8::1
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS FAKE.ROOT.
+SECTION ADDITIONAL
+FAKE.ROOT. IN AAAA 2001:db8::1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+v4only. IN NS
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+RANGE_END
+
+; replies from NS over "NAT64"
+
+RANGE_BEGIN 0 100
+ ADDRESS 2001:db8:1234::c000:0201
+
+; A over NAT64
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+ns.v4only. IN A
+SECTION ANSWER
+ns.v4only. IN A 192.0.2.1
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+ENTRY_END
+
+; no AAAA
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+ns.v4only. IN AAAA
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+v4only. IN NS
+SECTION ANSWER
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+test.v4only. IN A
+SECTION ANSWER
+test.v4only. IN A 192.0.2.2
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test.v4only. IN A
+ENTRY_END
+
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+test.v4only. IN A
+SECTION ANSWER
+test.v4only. IN A 192.0.2.2
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/iter_nat64_prefix48.rpl b/contrib/unbound/testdata/iter_nat64_prefix48.rpl
new file mode 100644
index 000000000000..e7c32e8ffc6a
--- /dev/null
+++ b/contrib/unbound/testdata/iter_nat64_prefix48.rpl
@@ -0,0 +1,118 @@
+; config options
+server:
+ do-nat64: yes
+ nat64-prefix: 2001:db8:2345::/48
+ target-fetch-policy: "0 0 0 0 0"
+
+stub-zone:
+ name: "."
+ stub-addr: 2001:db8::1
+CONFIG_END
+
+SCENARIO_BEGIN Test NAT64 transport, this time with /48 NAT64 prefix.
+
+RANGE_BEGIN 0 100
+ ADDRESS 2001:db8::1
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS FAKE.ROOT.
+SECTION ADDITIONAL
+FAKE.ROOT. IN AAAA 2001:db8::1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+v4only. IN NS
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+RANGE_END
+
+; replies from NS over "NAT64"
+
+RANGE_BEGIN 0 100
+ ADDRESS 2001:db8:2345:c000:0002:0100::
+
+; A over NAT64
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+ns.v4only. IN A
+SECTION ANSWER
+ns.v4only. IN A 192.0.2.1
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+ENTRY_END
+
+; no AAAA
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+ns.v4only. IN AAAA
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+v4only. IN NS
+SECTION ANSWER
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY AA QR NOERROR
+SECTION QUESTION
+test.v4only. IN A
+SECTION ANSWER
+test.v4only. IN A 192.0.2.2
+SECTION AUTHORITY
+v4only. IN NS ns.v4only.
+SECTION ADDITIONAL
+ns.v4only. IN A 192.0.2.1
+ENTRY_END
+
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+test.v4only. IN A
+ENTRY_END
+
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+test.v4only. IN A
+SECTION ANSWER
+test.v4only. IN A 192.0.2.2
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/iter_scrub_rr_length.rpl b/contrib/unbound/testdata/iter_scrub_rr_length.rpl
new file mode 100644
index 000000000000..2ef73c2fe152
--- /dev/null
+++ b/contrib/unbound/testdata/iter_scrub_rr_length.rpl
@@ -0,0 +1,298 @@
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ minimal-responses: no
+ rrset-roundrobin: no
+ ede: yes
+ log-servfail: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test scrub of RRs of inappropriate length
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 200
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 200
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 200
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+www.example.com. IN A \# 3 030405
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN AAAA
+SECTION ANSWER
+www.example.com. IN AAAA 2001:db8::1234
+www.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+broken1.example.com. IN A
+SECTION ANSWER
+broken1.example.com. IN A \# 3 030405
+broken1.example.com. IN A \# 3 030406
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+broken1.example.com. IN AAAA
+SECTION ANSWER
+broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F
+broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E30
+broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E31
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+broken2.example.com. IN A
+SECTION ANSWER
+broken2.example.com. IN A 1.2.3.4
+broken2.example.com. IN A \# 3 030405
+broken2.example.com. IN A 1.2.3.5
+broken2.example.com. IN A \# 3 030406
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A \# 3 030407
+ns.example.com. IN A 1.2.3.6
+ns.example.com. IN A \# 3 030408
+ns.example.com. IN A \# 3 030409
+ns.example.com. IN A 1.2.3.7
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN AAAA
+ENTRY_END
+
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN AAAA
+SECTION ANSWER
+www.example.com. IN AAAA 2001:db8::1234
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+STEP 40 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+broken1.example.com. IN A
+ENTRY_END
+
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+broken1.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+broken1.example.com. IN AAAA
+ENTRY_END
+
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+broken1.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+STEP 80 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+broken2.example.com. IN A
+ENTRY_END
+
+STEP 90 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+broken2.example.com. IN A
+SECTION ANSWER
+broken2.example.com. IN A 1.2.3.4
+broken2.example.com. IN A 1.2.3.5
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.6
+ns.example.com. IN A 1.2.3.7
+ENTRY_END
+
+STEP 100 QUERY
+ENTRY_BEGIN
+REPLY RD CD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+STEP 110 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ede=0
+REPLY QR RD CD RA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.6
+ns.example.com. IN A 1.2.3.7
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.conf b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.conf
new file mode 100644
index 000000000000..befb4fbe97b3
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.conf
@@ -0,0 +1,34 @@
+server:
+ verbosity: 7
+ # num-threads: 1
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: ""
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ do-not-query-localhost: no
+ # for the test, so that DNSSEC verification works.
+ #val-override-date: 20230929090000
+ trust-anchor: ". DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"
+
+remote-control:
+ control-enable: yes
+ control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@
+ control-use-cert: no
+
+# for the test, an upstream server in the test setup.
+stub-zone:
+ name: "."
+ stub-addr: 127.0.0.1@@TOPORT@
+
+# hyperlocal root zone
+auth-zone:
+ name: "."
+ fallback-enabled: yes
+ for-downstream: no
+ for-upstream: yes
+ zonefile: "root.zone"
+ zonemd-check: yes
+ zonemd-reject-absence: yes
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.dsc b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.dsc
new file mode 100644
index 000000000000..8015ac2d13ad
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.dsc
@@ -0,0 +1,16 @@
+BaseName: root_zonemd
+Version: 1.0
+Description: ZONEMD check for root zone
+CreationDate: Fri 29 Sep 09:00:00 CEST 2023
+Maintainer: dr. W.C.A. Wijngaards
+Category:
+Component:
+CmdDepends:
+Depends:
+Help:
+Pre: root_zonemd.pre
+Post: root_zonemd.post
+Test: root_zonemd.test
+AuxFiles:
+Passed:
+Failure:
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.post b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.post
new file mode 100644
index 000000000000..a28599fafe7a
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.post
@@ -0,0 +1,14 @@
+# #-- root_zonemd.post --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# source the test var file when it's there
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+#
+# do your teardown here
+. ../common.sh
+echo "> cat logfiles"
+cat fwd.log
+cat unbound.log
+kill_pid $FWD_PID
+kill_pid $UNBOUND_PID
+rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.pre b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.pre
new file mode 100644
index 000000000000..fe369bb20bbb
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.pre
@@ -0,0 +1,50 @@
+# #-- root_zonemd.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+. ../common.sh
+
+# attempt to download the root zone
+from=k.root-servers.net
+dig @$from . AXFR > root.txt
+if test $? -ne 0; then
+ echo "could not fetch root zone"
+ skip_test "could not fetch root zone"
+fi
+grep " SOA " root.txt | head -1 > root.soa
+cat root.soa >> root.zone
+grep -v " SOA " root.txt >> root.zone
+echo "fetched root.zone"
+ls -l root.zone
+cat root.soa
+
+get_random_port 2
+UNBOUND_PORT=$RND_PORT
+FWD_PORT=$(($RND_PORT + 1))
+echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
+echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test
+
+# start forwarder
+get_ldns_testns
+$LDNS_TESTNS -p $FWD_PORT root_zonemd.testns >fwd.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+
+# make config file
+CONTROL_PATH=/tmp
+CONTROL_PID=$$
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < root_zonemd.conf > ub.conf
+# start unbound in the background
+PRE="../.."
+$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test
+echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test
+
+cat .tpkg.var.test
+wait_ldns_testns_up fwd.log
+wait_unbound_up unbound.log
+
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.test b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.test
new file mode 100644
index 000000000000..2745b5009e8f
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.test
@@ -0,0 +1,63 @@
+# #-- root_zonemd.test --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+# do the test
+echo "> dig . SOA"
+dig @127.0.0.1 -p $UNBOUND_PORT . SOA | tee outfile
+echo "> check answer"
+if grep root-servers outfile | grep "nstld.verisign-grs.com"; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+echo "> unbound-control status"
+$PRE/unbound-control -c ub.conf status
+if test $? -ne 0; then
+ echo "wrong exit value."
+ exit 1
+else
+ echo "exit value: OK"
+fi
+
+# This is the output when an unsupported algorithm is used.
+if grep "auth zone . zonemd DNSSEC verification of SOA and ZONEMD RRsets secure" unbound.log; then
+ echo "OK"
+else
+ echo "ZONEMD verification not OK"
+ exit 1
+fi
+if grep "auth-zone . ZONEMD hash is correct" unbound.log; then
+ echo "OK"
+else
+ echo "ZONEMD verification not OK"
+ exit 1
+fi
+if grep "auth zone . ZONEMD verification successful" unbound.log; then
+ echo "OK"
+else
+ echo "ZONEMD verification not OK"
+ exit 1
+fi
+
+echo "> unbound-control auth_zone_reload ."
+$PRE/unbound-control -c ub.conf auth_zone_reload . 2>&1 | tee outfile
+if test $? -ne 0; then
+ echo "wrong exit value."
+ exit 1
+fi
+# The output of the reload can be checked.
+echo "> check unbound-control output"
+if grep ".: ZONEMD verification successful" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.testns b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.testns
new file mode 100644
index 000000000000..d538f2215ecf
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.testns
@@ -0,0 +1,9 @@
+# reply to everything
+ENTRY_BEGIN
+MATCH opcode
+ADJUST copy_id copy_query
+REPLY QR SERVFAIL
+SECTION QUESTION
+example.com. IN SOA
+SECTION ANSWER
+ENTRY_END
diff --git a/contrib/unbound/testdata/rpz_cached_cname.rpl b/contrib/unbound/testdata/rpz_cached_cname.rpl
new file mode 100644
index 000000000000..198b946310bf
--- /dev/null
+++ b/contrib/unbound/testdata/rpz_cached_cname.rpl
@@ -0,0 +1,122 @@
+; config options
+server:
+ module-config: "respip validator iterator"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: no
+ rrset-roundrobin: no
+ access-control: 192.0.0.0/8 allow
+
+rpz:
+ name: "rpz.example.com"
+ rpz-log: yes
+ rpz-log-name: "rpz.example.com"
+ zonefile:
+TEMPFILE_NAME rpz.example.com
+TEMPFILE_CONTENTS rpz.example.com
+rpz.example.com. 3600 IN SOA ns.rpz.example.com. hostmaster.rpz.example.com. 1 3600 900 86400 3600
+rpz.example.com. 3600 IN NS ns.rpz.example.net.
+a.foo.rpz.example.com. 120 IN A 10.99.99.99
+TEMPFILE_END
+
+stub-zone:
+ name: "."
+ stub-addr: 10.20.30.40
+
+CONFIG_END
+
+SCENARIO_BEGIN Test RPZ with cached CNAME to A record
+
+RANGE_BEGIN 0 100
+ ADDRESS 10.20.30.40
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR NOERROR AA
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS ns.
+SECTION ADDITIONAL
+ns. IN NS 10.20.30.40
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR NOERROR AA
+SECTION QUESTION
+b.foo. IN A
+SECTION ANSWER
+b.foo. 30 CNAME a.foo.
+a.foo. 30 A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR NOERROR AA
+SECTION QUESTION
+a.foo. IN A
+SECTION ANSWER
+a.foo. A 1.2.3.4
+ENTRY_END
+
+RANGE_END
+
+STEP 10 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+a.foo. IN A
+ENTRY_END
+
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+a.foo. IN A
+SECTION ANSWER
+a.foo. 120 A 10.99.99.99
+ENTRY_END
+
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+b.foo. IN A
+ENTRY_END
+
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+b.foo. IN A
+SECTION ANSWER
+b.foo. 30 CNAME a.foo.
+a.foo. 120 A 10.99.99.99
+ENTRY_END
+
+STEP 50 TIME_PASSES ELAPSE 3
+
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+b.foo. IN A
+ENTRY_END
+
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+b.foo. IN A
+SECTION ANSWER
+b.foo. 30 CNAME a.foo.
+a.foo. 120 A 10.99.99.99
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/rrset_use_cached.rpl b/contrib/unbound/testdata/rrset_use_cached.rpl
new file mode 100644
index 000000000000..8420ae02afe6
--- /dev/null
+++ b/contrib/unbound/testdata/rrset_use_cached.rpl
@@ -0,0 +1,151 @@
+server:
+ minimal-responses: no
+ serve-expired: yes
+ # The value does not matter, we will not simulate delay.
+ # We do not want only serve-expired because fetches from that
+ # apply a generous PREFETCH_LEEWAY.
+ serve-expired-client-timeout: 1000
+ # So that we can only have to give one SERVFAIL answer.
+ outbound-msg-retry: 0
+
+forward-zone: name: "." forward-addr: 216.0.0.1
+CONFIG_END
+
+SCENARIO_BEGIN RRset from cache updates the message TTL.
+
+STEP 1 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ www.example.com. IN A
+ENTRY_END
+; the query is sent to the forwarder - no cache yet.
+STEP 2 CHECK_OUT_QUERY
+ENTRY_BEGIN
+ MATCH qname qtype opcode
+ SECTION QUESTION
+ www.example.com. IN A
+ENTRY_END
+STEP 3 REPLY
+ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ ; authoritative answer
+ REPLY QR AA RD RA NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 5 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 10.20.30.50
+ENTRY_END
+STEP 4 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 5 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 10.20.30.50
+ENTRY_END
+
+; Wait for the A RRSET to expire.
+STEP 5 TIME_PASSES ELAPSE 6
+
+STEP 6 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ www.example.com. IN A
+ENTRY_END
+; expired answer will not be served due to serve-expired-client-timeout.
+STEP 7 CHECK_OUT_QUERY
+ENTRY_BEGIN
+ MATCH qname qtype opcode
+ SECTION QUESTION
+ www.example.com. IN A
+ENTRY_END
+STEP 8 REPLY
+ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ ; authoritative answer
+ REPLY QR AA RD RA NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 5 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 10.20.30.50
+ENTRY_END
+; The cached NS related RRSETs will not be overwritten by the fresh answer.
+; The message should have a TTL of 4 instead of 5 from above.
+STEP 9 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 5 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. 4 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 4 IN A 10.20.30.50
+ENTRY_END
+
+; Wait for the NS RRSETs to expire.
+STEP 10 TIME_PASSES ELAPSE 5
+
+STEP 11 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ www.example.com. IN A
+ENTRY_END
+; The message should be expired, again no expired answer at this point due to
+; serve-expired-client-timeout.
+STEP 12 CHECK_OUT_QUERY
+ENTRY_BEGIN
+ MATCH qname qtype opcode
+ SECTION QUESTION
+ www.example.com. IN A
+ENTRY_END
+STEP 13 REPLY
+ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR RD RA SERVFAIL
+ SECTION QUESTION
+ www.example.com. IN A
+ENTRY_END
+; The SERVFAIL will trigger the serve-expired-client-timeout logic to try and
+; replace the SERVFAIL with a possible cached (expired) answer.
+; The A RRSET would be at 0TTL left (not expired) but the message should have
+; been updated to use a TTL of 4 so expired by now.
+; If the message TTL was not updated (bug), this message would be treated as
+; non-expired and the now expired NS related RRSETs would fail sanity checks
+; for non-expired messages. The result would be SERVFAIL here.
+STEP 14 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 0 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. 30 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 30 IN A 10.20.30.50
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/serve_expired_0ttl_nodata.rpl b/contrib/unbound/testdata/serve_expired_0ttl_nodata.rpl
new file mode 100644
index 000000000000..7f1b5a565853
--- /dev/null
+++ b/contrib/unbound/testdata/serve_expired_0ttl_nodata.rpl
@@ -0,0 +1,154 @@
+; config options
+server:
+ module-config: "validator iterator"
+ qname-minimisation: "no"
+ minimal-responses: no
+ serve-expired: yes
+ log-servfail: yes
+ ede: yes
+ ede-serve-expired: yes
+
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test serve-expired with NXDOMAIN followed by 0 TTL
+; Scenario overview:
+; - query for 0ttl.example.com. IN A
+; - answer from upstream is NODATA; will be cached for the SOA negative TTL.
+; - check that the client gets the NODATA; also cached
+; - query again right after the TTL expired
+; - this time the server answers with a 0 TTL RRset
+; - check that we get the correct answer
+
+; ns.example.com.
+RANGE_BEGIN 0 20
+ ADDRESS 1.2.3.4
+ ; response to A query
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR AA NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION AUTHORITY
+ example.com IN SOA ns.example.com dns.example.com 1 7200 3600 2419200 10
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 30 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION ANSWER
+ 0ttl.example.com. 0 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; Query with RD flag
+STEP 0 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we get the NODATA (will be cached)
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION AUTHORITY
+ example.com IN SOA ns.example.com dns.example.com 1 7200 3600 2419200 10
+ENTRY_END
+
+; Query again
+STEP 20 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we get the cached NODATA
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION AUTHORITY
+ example.com IN SOA ns.example.com dns.example.com 1 7200 3600 2419200 10
+ENTRY_END
+
+; Wait for the NXDOMAIN to expire
+STEP 31 TIME_PASSES ELAPSE 32
+
+; Query again
+STEP 40 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we get the cached NODATA
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION AUTHORITY
+ example.com IN SOA ns.example.com dns.example.com 1 7200 3600 2419200 10
+ENTRY_END
+
+; Query again
+STEP 60 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we got the correct answer
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION ANSWER
+ 0ttl.example.com. 0 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/serve_expired_0ttl_nxdomain.rpl b/contrib/unbound/testdata/serve_expired_0ttl_nxdomain.rpl
new file mode 100644
index 000000000000..4adb4b839a69
--- /dev/null
+++ b/contrib/unbound/testdata/serve_expired_0ttl_nxdomain.rpl
@@ -0,0 +1,154 @@
+; config options
+server:
+ module-config: "validator iterator"
+ qname-minimisation: "no"
+ minimal-responses: no
+ serve-expired: yes
+ log-servfail: yes
+ ede: yes
+ ede-serve-expired: yes
+
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test serve-expired with NXDOMAIN followed by 0 TTL
+; Scenario overview:
+; - query for 0ttl.example.com. IN A
+; - answer from upstream is NXDOMAIN; will be cached for the SOA negative TTL.
+; - check that the client gets the NXDOMAIN; also cached
+; - query again right after the TTL expired
+; - this time the server answers with a 0 TTL RRset
+; - check that we get the correct answer
+
+; ns.example.com.
+RANGE_BEGIN 0 20
+ ADDRESS 1.2.3.4
+ ; response to A query
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR AA NXDOMAIN
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION AUTHORITY
+ example.com IN SOA ns.example.com dns.example.com 1 7200 3600 2419200 10
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 30 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION ANSWER
+ 0ttl.example.com. 0 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; Query with RD flag
+STEP 0 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we get the SERVFAIL (will be cached)
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA NXDOMAIN
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION AUTHORITY
+ example.com IN SOA ns.example.com dns.example.com 1 7200 3600 2419200 10
+ENTRY_END
+
+; Query again
+STEP 20 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we get the cached NXDOMAIN
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA NXDOMAIN
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION AUTHORITY
+ example.com IN SOA ns.example.com dns.example.com 1 7200 3600 2419200 10
+ENTRY_END
+
+; Wait for the NXDOMAIN to expire
+STEP 31 TIME_PASSES ELAPSE 32
+
+; Query again
+STEP 40 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we get the cached NXDOMAIN
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA NXDOMAIN
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION AUTHORITY
+ example.com IN SOA ns.example.com dns.example.com 1 7200 3600 2419200 10
+ENTRY_END
+
+; Query again
+STEP 60 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we got the correct answer
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION ANSWER
+ 0ttl.example.com. 0 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/serve_expired_0ttl_servfail.rpl b/contrib/unbound/testdata/serve_expired_0ttl_servfail.rpl
new file mode 100644
index 000000000000..6833af17b827
--- /dev/null
+++ b/contrib/unbound/testdata/serve_expired_0ttl_servfail.rpl
@@ -0,0 +1,129 @@
+; config options
+server:
+ module-config: "validator iterator"
+ qname-minimisation: "no"
+ minimal-responses: no
+ serve-expired: yes
+ log-servfail: yes
+ ede: yes
+ ede-serve-expired: yes
+
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test serve-expired with SERVFAIL followed by 0 TTL
+; Scenario overview:
+; - query for 0ttl.example.com. IN A
+; - answer from upstream is SERVFAIL; will be cached for NORR_TTL(5)
+; - check that the client gets the SERVFAIL; also cached
+; - query again right after the TTL expired
+; - this time the server answers with a 0 TTL RRset
+; - check that we get the correct answer
+
+; ns.example.com.
+RANGE_BEGIN 0 20
+ ADDRESS 1.2.3.4
+ ; response to A query
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR AA SERVFAIL
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 30 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION ANSWER
+ 0ttl.example.com. 0 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; Query with RD flag
+STEP 0 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we get the SERVFAIL (will be cached)
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA SERVFAIL
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Query again
+STEP 20 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we get the cached SERVFAIL
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA SERVFAIL
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Wait for the SERVFAIL to expire
+STEP 31 TIME_PASSES ELAPSE 32
+
+; Query again
+STEP 40 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ENTRY_END
+
+; Check that we got the correct answer
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA NOERROR
+ SECTION QUESTION
+ 0ttl.example.com. IN A
+ SECTION ANSWER
+ 0ttl.example.com. 0 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/serve_expired_cached_servfail.rpl b/contrib/unbound/testdata/serve_expired_cached_servfail.rpl
new file mode 100644
index 000000000000..f5f4c7030198
--- /dev/null
+++ b/contrib/unbound/testdata/serve_expired_cached_servfail.rpl
@@ -0,0 +1,130 @@
+; config options
+server:
+ module-config: "validator iterator"
+ qname-minimisation: "no"
+ minimal-responses: no
+ serve-expired: yes
+ serve-expired-reply-ttl: 123
+ log-servfail: yes
+ ede: yes
+ ede-serve-expired: yes
+
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test serve-expired with client-timeout and a SERVFAIL upstream reply
+; Scenario overview:
+; - query for example.com. IN A
+; - answer from upstream is SERVFAIL; will be cached for NORR_TTL(5)
+; - check that the client gets the SERVFAIL; also cached
+; - query again right after the TTL expired
+; - cached SERVFAIL should be ignored and upstream queried
+; - check that we get the correct answer
+
+; ns.example.com.
+RANGE_BEGIN 0 20
+ ADDRESS 1.2.3.4
+ ; response to A query
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR AA SERVFAIL
+ SECTION QUESTION
+ example.com. IN A
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 30 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION ANSWER
+ example.com. 10 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; Query with RD flag
+STEP 0 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Check that we get the SERVFAIL (will be cached)
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA SERVFAIL
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Query again
+STEP 20 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Check that we get the cached SERVFAIL
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA SERVFAIL
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Wait for the SERVFAIL to expire
+STEP 31 TIME_PASSES ELAPSE 6
+
+; Query again
+STEP 40 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Check that we got the correct answer
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all ttl
+ REPLY QR RD RA NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION ANSWER
+ example.com. 10 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/serve_expired_cached_servfail_refresh.rpl b/contrib/unbound/testdata/serve_expired_cached_servfail_refresh.rpl
new file mode 100644
index 000000000000..9b7c1fda16c1
--- /dev/null
+++ b/contrib/unbound/testdata/serve_expired_cached_servfail_refresh.rpl
@@ -0,0 +1,145 @@
+; config options
+server:
+ module-config: "validator iterator"
+ qname-minimisation: "no"
+ minimal-responses: no
+ serve-expired: yes
+ serve-expired-reply-ttl: 123
+ log-servfail: yes
+ ede: yes
+ ede-serve-expired: yes
+
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test serve-expired with client-timeout and a SERVFAIL upstream reply
+; Scenario overview:
+; - query for example.com. IN A
+; - answer from upstream is SERVFAIL; will be cached for NORR_TTL(5)
+; - check that the client gets the SERVFAIL; also cached
+; - query again right after the TTL expired
+; - cached SERVFAIL should be ignored and upstream queried
+; - answer from upstream is still SERVFAIL; the cached error response will be
+; refreshed for another NORR_TTL(5)
+; - check that the client gets the SERVFAIL
+; - query again; the upstream now has the answer available
+; - check that we get the refreshed cached response instead
+
+; ns.example.com.
+RANGE_BEGIN 0 50
+ ADDRESS 1.2.3.4
+ ; response to A query
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR AA SERVFAIL
+ SECTION QUESTION
+ example.com. IN A
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 60 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN A
+ SECTION ANSWER
+ example.com. 10 IN A 5.6.7.8
+ SECTION AUTHORITY
+ example.com. 10 IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. 10 IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; Query with RD flag
+STEP 0 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Check that we get the SERVFAIL (will be cached)
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA SERVFAIL
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Query again
+STEP 20 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Check that we get the cached SERVFAIL
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA SERVFAIL
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Wait for the SERVFAIL to expire
+STEP 31 TIME_PASSES ELAPSE 6
+
+; Query again
+STEP 40 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Check that we get the SERVFAIL (will be refreshed)
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA SERVFAIL
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Query again, upstream has the real answer available
+STEP 60 QUERY
+ENTRY_BEGIN
+ REPLY RD
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+; Check that we get the refreshed cached SERVFAIL
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+ MATCH all
+ REPLY QR RD RA SERVFAIL
+ SECTION QUESTION
+ example.com. IN A
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/stat_values.tdir/stat_values_cachedb.conf b/contrib/unbound/testdata/stat_values.tdir/stat_values_cachedb.conf
new file mode 100644
index 000000000000..b5e9b0e02932
--- /dev/null
+++ b/contrib/unbound/testdata/stat_values.tdir/stat_values_cachedb.conf
@@ -0,0 +1,36 @@
+server:
+ verbosity: 5
+ module-config: "cachedb iterator"
+ serve-expired: yes
+ num-threads: 1
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: ""
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ do-not-query-localhost: no
+ extended-statistics: yes
+ identity: "stat_values"
+ outbound-msg-retry: 0
+ root-key-sentinel: no
+ trust-anchor-signaling: no
+
+ local-zone: local.zone static
+ local-data: "www.local.zone A 192.0.2.1"
+remote-control:
+ control-enable: yes
+ control-interface: 127.0.0.1
+ # control-interface: ::1
+ control-port: @CONTROL_PORT@
+ server-key-file: "unbound_server.key"
+ server-cert-file: "unbound_server.pem"
+ control-key-file: "unbound_control.key"
+ control-cert-file: "unbound_control.pem"
+forward-zone:
+ name: "."
+ forward-addr: "127.0.0.1@@TOPORT@"
+forward-zone:
+ name: "expired."
+ forward-addr: "127.0.0.1@@EXPIREDPORT@"
diff --git a/contrib/unbound/testdata/stat_values.tdir/stat_values_downstream_cookies.conf b/contrib/unbound/testdata/stat_values.tdir/stat_values_downstream_cookies.conf
new file mode 100644
index 000000000000..21e78829fc8e
--- /dev/null
+++ b/contrib/unbound/testdata/stat_values.tdir/stat_values_downstream_cookies.conf
@@ -0,0 +1,32 @@
+server:
+ verbosity: 5
+ module-config: "iterator"
+ num-threads: 1
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: ""
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ extended-statistics: yes
+ identity: "stat_values"
+ outbound-msg-retry: 0
+ root-key-sentinel: no
+ trust-anchor-signaling: no
+
+ local-zone: local.zone static
+ local-data: "www.local.zone A 192.0.2.1"
+
+ answer-cookie: yes
+ access-control: 127.0.0.1 allow_cookie
+
+remote-control:
+ control-enable: yes
+ control-interface: 127.0.0.1
+ # control-interface: ::1
+ control-port: @CONTROL_PORT@
+ server-key-file: "unbound_server.key"
+ server-cert-file: "unbound_server.pem"
+ control-key-file: "unbound_control.key"
+ control-cert-file: "unbound_control.pem"
diff --git a/contrib/unbound/testdata/subnet_cached_ede.crpl b/contrib/unbound/testdata/subnet_cached_ede.crpl
new file mode 100644
index 000000000000..36bb28fcc180
--- /dev/null
+++ b/contrib/unbound/testdata/subnet_cached_ede.crpl
@@ -0,0 +1,114 @@
+; Ask the same question twice. Check to see second is answered
+; from cache
+
+server:
+ trust-anchor-signaling: no
+ target-fetch-policy: "0 0 0 0 0"
+ send-client-subnet: 1.2.3.4
+ max-client-subnet-ipv4: 17
+ module-config: "subnetcache validator iterator"
+ verbosity: 3
+ qname-minimisation: no
+ minimal-responses: no
+ ede: yes
+ val-log-level: 2
+ trust-anchor: "example.nl. DS 50602 8 2 FA8EE175C47325F4BD46D8A4083C3EBEB11C977D689069F2B41F1A29B22446B1"
+
+stub-zone:
+ name: "example.nl"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test subnetcache support for caching EDEs.
+
+; ns.example.com.
+RANGE_BEGIN 0 10
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+example.nl. IN DNSKEY
+SECTION ANSWER
+SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 11 00 ; source mask, scopemask
+ 7f 00 00 ; address
+ HEX_EDNSDATA_END
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+example.nl. IN A
+SECTION ANSWER
+example.nl. IN A 1.2.3.4
+SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 11 00 ; source mask, scopemask
+ 7f 00 00 ; address
+ HEX_EDNSDATA_END
+ENTRY_END
+RANGE_END
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; get the entry in cache.
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+example.nl. IN A
+SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ 00 08 00 07 ; OPC, optlen
+ 00 01 11 00 ; ip4, scope 17, source 0
+ 7f 00 00 ; 127.0.0.0/17
+ HEX_EDNSDATA_END
+ENTRY_END
+
+; get the answer for it
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ede=9
+REPLY QR RD RA DO SERVFAIL
+SECTION QUESTION
+example.nl. IN A
+ENTRY_END
+
+; query again for the cached entry
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+example.nl. IN A
+SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ 00 08 00 07 ; OPC, optlen
+ 00 01 11 00 ; ip4, scope 17, source 0
+ 7f 00 00 ; 127.0.0.0/17
+ HEX_EDNSDATA_END
+ENTRY_END
+
+; this must be a cached answer since stub is not answering in this range
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ede=9
+REPLY QR RD RA DO SERVFAIL
+SECTION QUESTION
+example.nl. IN A
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/subnet_cached_servfail.crpl b/contrib/unbound/testdata/subnet_cached_servfail.crpl
new file mode 100644
index 000000000000..9c746d579124
--- /dev/null
+++ b/contrib/unbound/testdata/subnet_cached_servfail.crpl
@@ -0,0 +1,167 @@
+; Check if an expired SERVFAIL answer stored in the global cache does not block
+; ECS queries to reach the ECS cache.
+
+server:
+ trust-anchor-signaling: no
+ target-fetch-policy: "0 0 0 0 0"
+ send-client-subnet: 1.2.3.4
+ max-client-subnet-ipv4: 21
+ module-config: "subnetcache iterator"
+ verbosity: 3
+ access-control: 127.0.0.1 allow_snoop
+ qname-minimisation: no
+ minimal-responses: no
+ serve-expired: yes
+ prefetch: yes
+
+stub-zone:
+ name: "example.com."
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test that expired SERVFAIL in global cache does not block clients to reach the ECS cache
+
+; ns.example.com.
+RANGE_BEGIN 0 10
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR SERVFAIL
+ SECTION QUESTION
+ www.example.com. IN A
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 11 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname ednsdata
+ ADJUST copy_id copy_ednsdata_assume_clientsubnet
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 10 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 05 ; option length
+ 00 01 ; Family
+ 08 00 ; source mask, scopemask
+ 7f ; address
+ HEX_EDNSDATA_END
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; This answer should be in the global cache
+STEP 2 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; Bring the cached SERVFAIL to prefetch time
+STEP 10 TIME_PASSES ELAPSE 5
+
+STEP 11 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 05 ; OPC, optlen
+ 00 01 08 00 ; ip4, source 8, scope 0
+ 7f ; 127.0.0.0/8
+HEX_EDNSDATA_END
+ENTRY_END
+
+; This answer was cached but a prefetch was triggerred
+STEP 12 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH opcode qtype qname
+REPLY QR RD RA SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; Wait for the SERVFAIL to expire
+STEP 13 TIME_PASSES ELAPSE 2
+
+; Query again to verify that the record was prefetched and stored in the ECS
+; cache (because the server replied with ECS this time)
+STEP 14 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 05 ; OPC, optlen
+ 00 01 08 00 ; ip4, source 8, scope 0
+ 7f ; 127.0.0.0/8
+HEX_EDNSDATA_END
+ENTRY_END
+
+; This record came from the ECS cache
+STEP 15 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 8 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3598 IN NS ns.example.com.
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 05 ; OPC, optlen
+ 00 01 08 08 ; ip4, source 8, scope 0
+ 7f ; 127.0.0.0/8
+HEX_EDNSDATA_END
+ns.example.com. 3598 IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/subnet_global_prefetch.crpl b/contrib/unbound/testdata/subnet_global_prefetch.crpl
new file mode 100644
index 000000000000..2f005d43b905
--- /dev/null
+++ b/contrib/unbound/testdata/subnet_global_prefetch.crpl
@@ -0,0 +1,236 @@
+; Check if the prefetch option works properly for messages stored in the global
+; cache for non-ECS clients. The prefetch query needs to result in an ECS
+; outgoing query based on the client's IP.
+
+server:
+ trust-anchor-signaling: no
+ target-fetch-policy: "0 0 0 0 0"
+ send-client-subnet: 1.2.3.4
+ max-client-subnet-ipv4: 21
+ module-config: "subnetcache iterator"
+ verbosity: 3
+ access-control: 127.0.0.1 allow_snoop
+ qname-minimisation: no
+ minimal-responses: no
+ prefetch: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test prefetch option for global cache with ECS enabled
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ ENTRY_BEGIN
+ MATCH opcode qtype qname ednsdata
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ . IN NS
+ SECTION ANSWER
+ . IN NS K.ROOT-SERVERS.NET.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ;; we expect to receive empty
+ HEX_EDNSDATA_END
+ K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ ENTRY_BEGIN
+ MATCH opcode qtype qname ednsdata
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ com. IN NS
+ SECTION ANSWER
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ;; we expect to receive empty
+ HEX_EDNSDATA_END
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 10
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ;; we expect to receive empty
+ HEX_EDNSDATA_END
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 10 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 11 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ;; we expect to receive empty
+ HEX_EDNSDATA_END
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname ednsdata
+ ADJUST copy_id copy_ednsdata_assume_clientsubnet
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 10 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 15 00 ; source mask, scopemask
+ 7f 00 00 ; address
+ HEX_EDNSDATA_END
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; This answer should be in the global cache (because no ECS from upstream)
+STEP 2 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+; Try to trigger a prefetch
+STEP 3 TIME_PASSES ELAPSE 9
+
+STEP 11 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; This record came from the global cache and a prefetch was triggered.
+STEP 12 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 1 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3591 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 3591 IN A 1.2.3.4
+ENTRY_END
+
+; Allow time to pass so that the global cache record is expired.
+STEP 13 TIME_PASSES ELAPSE 2
+
+; Query again to verify that the record was prefetched and stored in the ECS
+; cache.
+STEP 15 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; This record came from the ECS cache.
+STEP 16 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 8 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3598 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 3598 IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/subnet_global_prefetch_always_forward.crpl b/contrib/unbound/testdata/subnet_global_prefetch_always_forward.crpl
new file mode 100644
index 000000000000..ccfe5dfd6ea1
--- /dev/null
+++ b/contrib/unbound/testdata/subnet_global_prefetch_always_forward.crpl
@@ -0,0 +1,167 @@
+; Check if the prefetch option works properly when serve-expired is combined
+; with client-subnet-always-forward for non-ECS clients. The prefetch query
+; needs to result in an outgoing query without ECS.
+
+server:
+ trust-anchor-signaling: no
+ target-fetch-policy: "0 0 0 0 0"
+ serve-expired: yes
+ client-subnet-always-forward: yes
+ module-config: "subnetcache iterator"
+ verbosity: 3
+ access-control: 127.0.0.1 allow_snoop
+ qname-minimisation: no
+ minimal-responses: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test serve-expired and client-subnet-always-forward without ECS in the request
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ ENTRY_BEGIN
+ MATCH opcode qtype qname ednsdata
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ . IN NS
+ SECTION ANSWER
+ . IN NS K.ROOT-SERVERS.NET.
+ SECTION ADDITIONAL
+ K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ ENTRY_BEGIN
+ MATCH opcode qtype qname ednsdata
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ com. IN NS
+ SECTION ANSWER
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 10 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; This answer should be in the global cache
+STEP 2 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+; Wait for the TTL to expire
+STEP 3 TIME_PASSES ELAPSE 20
+
+STEP 11 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; This record came from the global cache and a prefetch was triggered
+STEP 12 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 30 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3580 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 3580 IN A 1.2.3.4
+ENTRY_END
+
+STEP 13 CHECK_OUT_QUERY
+ENTRY_BEGIN
+ MATCH all
+ REPLY NOERROR DO
+ SECTION QUESTION
+ www.example.com. IN A
+ENTRY_END
+
+STEP 14 TRAFFIC
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/subnet_global_prefetch_expired.crpl b/contrib/unbound/testdata/subnet_global_prefetch_expired.crpl
new file mode 100644
index 000000000000..de1b780553a9
--- /dev/null
+++ b/contrib/unbound/testdata/subnet_global_prefetch_expired.crpl
@@ -0,0 +1,241 @@
+; Check if the prefetch option works properly for messages stored in the global
+; cache for non-ECS clients. The prefetch query needs to result in an ECS
+; outgoing query based on the client's IP.
+; Prefetch initiated via serve-expired.
+
+server:
+ trust-anchor-signaling: no
+ target-fetch-policy: "0 0 0 0 0"
+ send-client-subnet: 1.2.3.4
+ max-client-subnet-ipv4: 21
+ module-config: "subnetcache iterator"
+ verbosity: 3
+ access-control: 127.0.0.1 allow_snoop
+ qname-minimisation: no
+ minimal-responses: no
+ serve-expired: yes
+ serve-expired-ttl: 1
+ prefetch: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test prefetch option for global cache with ECS enabled (initiated via serve-expired)
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ ENTRY_BEGIN
+ MATCH opcode qtype qname ednsdata
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ . IN NS
+ SECTION ANSWER
+ . IN NS K.ROOT-SERVERS.NET.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ;; we expect to receive empty
+ HEX_EDNSDATA_END
+ K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ ENTRY_BEGIN
+ MATCH opcode qtype qname ednsdata
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ com. IN NS
+ SECTION ANSWER
+ com. IN NS a.gtld-servers.net.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ;; we expect to receive empty
+ HEX_EDNSDATA_END
+ a.gtld-servers.net. IN A 192.5.6.30
+ ENTRY_END
+
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 10
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ;; we expect to receive empty
+ HEX_EDNSDATA_END
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 10 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 11 100
+ ADDRESS 1.2.3.4
+ ENTRY_BEGIN
+ MATCH opcode qtype qname
+ ADJUST copy_id
+ REPLY QR NOERROR
+ SECTION QUESTION
+ example.com. IN NS
+ SECTION ANSWER
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ;; we expect to receive empty
+ HEX_EDNSDATA_END
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+
+ ; response to query of interest
+ ENTRY_BEGIN
+ MATCH opcode qtype qname ednsdata
+ ADJUST copy_id copy_ednsdata_assume_clientsubnet
+ REPLY QR NOERROR
+ SECTION QUESTION
+ www.example.com. IN A
+ SECTION ANSWER
+ www.example.com. 10 IN A 10.20.30.40
+ SECTION AUTHORITY
+ example.com. IN NS ns.example.com.
+ SECTION ADDITIONAL
+ HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 15 00 ; source mask, scopemask
+ 7f 00 00 ; address
+ HEX_EDNSDATA_END
+ ns.example.com. IN A 1.2.3.4
+ ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; This answer should be in the global cache (because no ECS from upstream)
+STEP 2 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+; Try to trigger a prefetch with expired data
+STEP 3 TIME_PASSES ELAPSE 11
+
+STEP 11 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; This expired record came from the global cache and a prefetch is triggered.
+STEP 12 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 30 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3589 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 3589 IN A 1.2.3.4
+ENTRY_END
+
+;STEP 13 TRAFFIC
+; Allow enough time to pass so that the expired record from the global cache
+; cannot be used anymore.
+STEP 14 TIME_PASSES ELAPSE 1
+
+; Query again to verify that the record was prefetched and stored in the ECS
+; cache.
+STEP 15 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; This record came from the ECS cache.
+STEP 16 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ttl
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. 9 IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. 3599 IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. 3599 IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/subnet_prezero.crpl b/contrib/unbound/testdata/subnet_prezero.crpl
new file mode 100644
index 000000000000..22cdfffb03b3
--- /dev/null
+++ b/contrib/unbound/testdata/subnet_prezero.crpl
@@ -0,0 +1,155 @@
+; subnet unit test
+server:
+ trust-anchor-signaling: no
+ send-client-subnet: 1.2.3.4
+ send-client-subnet: 1.2.3.5
+ target-fetch-policy: "0 0 0 0 0"
+ module-config: "subnetcache validator iterator"
+ qname-minimisation: no
+ minimal-responses: no
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test subnetcache source prefix zero from client.
+; In RFC7871 section-7.1.2 (para. 2).
+; It says that the recursor must send no EDNS subnet or its own address
+; in the EDNS subnet to the upstream server. And use that answer for the
+; source prefix length zero query. That type of query is for privacy.
+; The authority server is then going to use the resolver's IP, if any, to
+; tailor the answer to the query source address.
+
+; ns.example.com
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+
+; reply with 0.0.0.0/0 in reply
+; For the test the answers for 0.0.0.0/0 queries are SERVFAIL, the normal
+; answers are NOERROR.
+ENTRY_BEGIN
+MATCH opcode qtype qname ednsdata
+ADJUST copy_id
+REPLY QR AA DO SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN CNAME star.c10r.example.com.
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+
+; reply without subnet
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN CNAME star.c10r.example.com.
+ENTRY_END
+
+; delegation answer for c10r.example.com, with subnet /0
+ENTRY_BEGIN
+MATCH opcode subdomain ednsdata
+ADJUST copy_id copy_query
+REPLY QR DO SERVFAIL
+SECTION QUESTION
+c10r.example.com. IN NS
+SECTION AUTHORITY
+c10r.example.com. IN NS ns.c10r.example.com.
+SECTION ADDITIONAL
+ns.c10r.example.com. IN A 1.2.3.5
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+
+; delegation answer for c10r.example.com, without subnet
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR DO NOERROR
+SECTION QUESTION
+c10r.example.com. IN NS
+SECTION AUTHORITY
+c10r.example.com. IN NS ns.c10r.example.com.
+SECTION ADDITIONAL
+ns.c10r.example.com. IN A 1.2.3.5
+ENTRY_END
+RANGE_END
+
+; ns.c10r.example.com
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.5
+
+; reply with 0.0.0.0/0 in reply
+ENTRY_BEGIN
+MATCH opcode qtype qname ednsdata
+ADJUST copy_id
+REPLY QR AA DO SERVFAIL
+SECTION QUESTION
+star.c10r.example.com. IN A
+SECTION ANSWER
+star.c10r.example.com. IN A 1.2.3.6
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+
+; reply without subnet
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA DO NOERROR
+SECTION QUESTION
+star.c10r.example.com. IN A
+SECTION ANSWER
+star.c10r.example.com. IN A 1.2.3.6
+ENTRY_END
+RANGE_END
+
+; ask for www.example.com
+; server answers with CNAME to a delegation, that then
+; returns a /24 answer.
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN CNAME star.c10r.example.com.
+star.c10r.example.com. IN A 1.2.3.6
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+SCENARIO_END
diff --git a/contrib/unbound/testdata/subnet_scopezero_noedns.crpl b/contrib/unbound/testdata/subnet_scopezero_noedns.crpl
new file mode 100644
index 000000000000..25df0dd71cf2
--- /dev/null
+++ b/contrib/unbound/testdata/subnet_scopezero_noedns.crpl
@@ -0,0 +1,441 @@
+; scope of 0, if the query also had scope of 0, do not answer this
+; to everyone, but only for scope 0 queries. Otherwise can answer cached.
+
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ send-client-subnet: 1.2.3.4
+ module-config: "subnetcache validator iterator"
+ verbosity: 4
+ qname-minimisation: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test subnet cache with scope zero response without EDNS.
+
+; the upstream server.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+
+ENTRY_BEGIN
+MATCH opcode qtype qname ednsdata
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ;; we expect to receive empty
+HEX_EDNSDATA_END
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 0 11
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+;copy_ednsdata_assume_clientsubnet
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 18 11 ; source mask, scopemask
+ 7f 00 00 ; address
+HEX_EDNSDATA_END
+ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 20 31
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+;copy_ednsdata_assume_clientsubnet
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.41
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 18 11 ; source mask, scopemask
+ 7f 01 00 ; address
+HEX_EDNSDATA_END
+ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 40 51
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+;copy_ednsdata_assume_clientsubnet
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.42
+SECTION AUTHORITY
+SECTION ADDITIONAL
+;no EDNS in this answer. Tests if the back_parsed callback
+;is called to process the lack of edns contents.
+;HEX_EDNSDATA_BEGIN
+ ;00 08 ; OPC
+ ;00 04 ; option length
+ ;00 01 ; Family
+ ;00 00 ; source mask, scopemask
+ ; ; address 0.0.0.0/0 scope 0
+;HEX_EDNSDATA_END
+ENTRY_END
+RANGE_END
+
+RANGE_BEGIN 120 131
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+;copy_ednsdata_assume_clientsubnet
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.43
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 18 00 ; source mask, scopemask
+ 7f 02 00 ; address 127.2.0.0/24 scope 0
+HEX_EDNSDATA_END
+ENTRY_END
+RANGE_END
+
+; query for 127.0.0.0/24
+STEP 1 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+ 00 00 01 00 00 01 00 00 ;ID 0
+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO)
+ 07 65 78 61 6d 70 6c 65
+ 03 63 6f 6d 00 00 01 00
+ 01 00 00 29 10 00 00 00
+ 80 00 00 0b
+
+ 00 08 00 07 ; OPC, optlen
+ 00 01 18 00 ; ip4, scope 24, source 0
+ 7f 00 00 ;127.0.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer is 10.20.30.40 for 127.0.0.0/24 scope 17
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 18 11 ; source mask, scopemask
+ 7f 00 00 ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 127.1.0.0/24
+STEP 20 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+ 00 00 01 00 00 01 00 00 ;ID 0
+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO)
+ 07 65 78 61 6d 70 6c 65
+ 03 63 6f 6d 00 00 01 00
+ 01 00 00 29 10 00 00 00
+ 80 00 00 0b
+
+ 00 08 00 07 ; OPC, optlen
+ 00 01 18 00 ; ip4, scope 24, source 0
+ 7f 01 00 ;127.1.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer is 10.20.30.41 for 127.1.0.0/24 scope 17
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.41
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ; client is 127.1.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 18 11 ; source mask, scopemask
+ 7f 01 00 ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 0.0.0.0/0
+STEP 40 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+ 00 00 01 00 00 01 00 00 ;ID 0
+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO)
+ 07 65 78 61 6d 70 6c 65
+ 03 63 6f 6d 00 00 01 00
+ 01 00 00 29 10 00 00 00
+ 80 00 00 08
+
+ 00 08 00 04 ; OPC, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ;0.0.0.0/0
+HEX_ANSWER_END
+ENTRY_END
+
+; answer is 10.20.30.42 for 0.0.0.0/0 scope 0
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.42
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 ; OPC
+ 00 04 ; option length
+ 00 01 ; Family
+ 00 00 ; source mask, scopemask
+ ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 127.0.0.0/24, again, it should be in cache.
+; and not from the scope 0 answer.
+STEP 60 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+ 00 00 01 00 00 01 00 00 ;ID 0
+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO)
+ 07 65 78 61 6d 70 6c 65
+ 03 63 6f 6d 00 00 01 00
+ 01 00 00 29 10 00 00 00
+ 80 00 00 0b
+
+ 00 08 00 07 ; OPC, optlen
+ 00 01 18 00 ; ip4, scope 24, source 0
+ 7f 00 00 ;127.0.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer should be 10.20.30.40 for 127.0.0.0/24 scope 17
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 18 11 ; source mask, scopemask
+ 7f 00 00 ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 127.1.0.0/24, again, it should be in cache.
+STEP 80 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+ 00 00 01 00 00 01 00 00 ;ID 0
+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO)
+ 07 65 78 61 6d 70 6c 65
+ 03 63 6f 6d 00 00 01 00
+ 01 00 00 29 10 00 00 00
+ 80 00 00 0b
+
+ 00 08 00 07 ; OPC, optlen
+ 00 01 18 00 ; ip4, scope 24, source 0
+ 7f 01 00 ;127.1.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer should be 10.20.30.41 for 127.1.0.0/24 scope 17
+STEP 90 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.41
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ; client is 127.1.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 18 11 ; source mask, scopemask
+ 7f 01 00 ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; query for 0.0.0.0/0, again.
+STEP 100 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+ 00 00 01 00 00 01 00 00 ;ID 0
+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO)
+ 07 65 78 61 6d 70 6c 65
+ 03 63 6f 6d 00 00 01 00
+ 01 00 00 29 10 00 00 00
+ 80 00 00 08
+
+ 00 08 00 04 ; OPC, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ;0.0.0.0/0
+HEX_ANSWER_END
+ENTRY_END
+
+; answer should be 10.20.30.42 for 0.0.0.0/0 scope 0
+STEP 110 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.42
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 ; OPC
+ 00 04 ; option length
+ 00 01 ; Family
+ 00 00 ; source mask, scopemask
+ ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; now a query for a /24 that gets an answer for a /0.
+STEP 120 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+ 00 00 01 00 00 01 00 00 ;ID 0
+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO)
+ 07 65 78 61 6d 70 6c 65
+ 03 63 6f 6d 00 00 01 00
+ 01 00 00 29 10 00 00 00
+ 80 00 00 0b
+
+ 00 08 00 07 ; OPC, optlen
+ 00 01 18 00 ; ip4, scope 24, source 0
+ 7f 02 00 ;127.2.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+; answer should be 10.20.30.43 for 127.2.0.0/24 scope 0
+STEP 130 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.43
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ; client is 127.2.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 18 00 ; source mask, scopemask
+ 7f 02 00 ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+; the scope 0 answer is now used to answer queries from
+; query for 127.0.0.0/24
+STEP 140 QUERY
+ENTRY_BEGIN
+HEX_ANSWER_BEGIN
+ 00 00 01 00 00 01 00 00 ;ID 0
+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO)
+ 07 65 78 61 6d 70 6c 65
+ 03 63 6f 6d 00 00 01 00
+ 01 00 00 29 10 00 00 00
+ 80 00 00 0b
+
+ 00 08 00 07 ; OPC, optlen
+ 00 01 18 00 ; ip4, scope 24, source 0
+ 7f 00 00 ;127.0.0.0/24
+HEX_ANSWER_END
+ENTRY_END
+
+STEP 150 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.43
+SECTION AUTHORITY
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ ; client is 127.0.0.1
+ 00 08 ; OPC
+ 00 07 ; option length
+ 00 01 ; Family
+ 18 00 ; source mask, scopemask
+ 7f 00 00 ; address
+HEX_EDNSDATA_END
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/val_any_negcache.rpl b/contrib/unbound/testdata/val_any_negcache.rpl
new file mode 100644
index 000000000000..8800a2140219
--- /dev/null
+++ b/contrib/unbound/testdata/val_any_negcache.rpl
@@ -0,0 +1,243 @@
+; config options
+; The island of trust is at example.com
+server:
+ trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
+ val-override-date: "20070916134226"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-sha1: yes
+ trust-anchor-signaling: no
+ rrset-roundrobin: no
+ aggressive-nsec: yes
+ harden-unknown-additional: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test validator with response to qtype ANY and negative cache.
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response with NODATA
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN LOC
+SECTION AUTHORITY
+example.com. 86400 IN SOA open.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000
+example.com. 86400 IN RRSIG SOA 3 2 86400 20070926134150 20070829134150 2854 example.com. MC0CFQCSs8KJepwaIp5vu++/0hk04lkXvgIUdphJSAE/MYob30WcRei9/nL49tE= ;{id = 2854}
+example.com. 18000 IN NSEC _sip._udp.example.com. A NS SOA MX TXT AAAA NAPTR RRSIG NSEC DNSKEY
+example.com. 18000 IN RRSIG NSEC 3 2 18000 20070926134150 20070829134150 2854 example.com. MCwCFBzOGtpgq4uJ2jeuLPYl2HowIRzDAhQVXNz1haQ1mI7z9lt5gcvWW+lFhA== ;{id = 2854}
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN ANY
+SECTION ANSWER
+example.com. 86400 IN SOA open.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000
+example.com. 86400 IN RRSIG SOA 3 2 86400 20070926134150 20070829134150 2854 example.com. MC0CFQCSs8KJepwaIp5vu++/0hk04lkXvgIUdphJSAE/MYob30WcRei9/nL49tE= ;{id = 2854}
+example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJIIs70j+sDS/UT2QRp61SE7S3EEXopNXoFE73JLRmvpi/UrOO/Vz4Se6wXv/CYCKjGw06U4WRgRYXcpEhJROyNapmdIKSxhOzfLVE1gqA0PweZR8dtY3aNQSRn3sPpwJr6Mi/PqQKAMMrZ9ckJpf1+bQMOOvxgzz2U1GS18b3yZKcgTMEaJzd/GZYzi/BN2DzQ0MsrSwYXfsNLFOBbs8PJMW4LYIxeeOe6rUgkWOF7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134150 20070829134150 2854 example.com. MCwCFHq7BNVAeLW+Uw/rkjVS08lrMDk/AhR+bvChHfiE4jLb6uoyE54/irCuqA== ;{id = 2854}
+example.com. 600 IN NAPTR 20 0 "s" "SIP+D2U" "" _sip._udp.example.com.
+example.com. 600 IN RRSIG NAPTR 3 2 600 20070926134150 20070829134150 2854 example.com. MC0CFE8qs66bzuOyKmTIacamrmqabMRzAhUAn0MujX1LB0UpTHuLMgdgMgJJlq4= ;{id = 2854}
+example.com. 86400 IN AAAA 2001:7b8:206:1::1
+example.com. 86400 IN RRSIG AAAA 3 2 86400 20070926134150 20070829134150 2854 example.com. MC0CFEqS4WHyqhUkv7t42TsBZJk/Q9paAhUAtTZ8GaXGpot0PmsM0oGzQU+2iw4= ;{id = 2854}
+example.com. 86400 IN TXT "Stichting NLnet Labs"
+example.com. 86400 IN RRSIG TXT 3 2 86400 20070926134150 20070829134150 2854 example.com. MCwCFH3otn2u8zXczBS8L0VKpyAYZGSkAhQLGaQclkzMAzlB5j73opFjdkh8TA== ;{id = 2854}
+example.com. 86400 IN MX 100 v.net.example.
+example.com. 86400 IN MX 50 open.example.com.
+example.com. 86400 IN RRSIG MX 3 2 86400 20070926134150 20070829134150 2854 example.com. MCwCFEKh3jeqh69zcOqWWv3GNKlMECPyAhR9HJkcPLqlyVWUccWDFJfGGcQfdg== ;{id = 2854}
+example.com. 86400 IN NS v.net.example.
+example.com. 86400 IN NS open.example.com.
+example.com. 86400 IN NS ns7.domain-registry.example.
+example.com. 86400 IN RRSIG NS 3 2 86400 20070926134150 20070829134150 2854 example.com. MC0CFQCaRn30X4neKW7KYoTa2kcsoOLgfgIURvKEyDczLypWlx99KpxzMxRYhEc= ;{id = 2854}
+example.com. 86400 IN A 213.154.224.1
+example.com. 86400 IN RRSIG A 3 2 86400 20070926134150 20070829134150 2854 example.com. MCwCFH8kSLxmRTwzlGDxvF1e4y/gM+5dAhQkzyQ2a6Gf+CMaHzVScaUvTt9HhQ== ;{id = 2854}
+example.com. 18000 IN NSEC _sip._udp.example.com. A NS SOA MX TXT AAAA NAPTR RRSIG NSEC DNSKEY
+example.com. 18000 IN RRSIG NSEC 3 2 18000 20070926134150 20070829134150 2854 example.com. MCwCFBzOGtpgq4uJ2jeuLPYl2HowIRzDAhQVXNz1haQ1mI7z9lt5gcvWW+lFhA== ;{id = 2854}
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ns7.domain-registry.example. 80173 IN A 62.4.86.230
+open.example.com. 600 IN A 213.154.224.1
+open.example.com. 600 IN AAAA 2001:7b8:206:1::53
+open.example.com. 600 IN AAAA 2001:7b8:206:1::1
+v.net.example. 28800 IN A 213.154.224.17
+v.net.example. 28800 IN AAAA 2001:7b8:206:1:200:39ff:fe59:b187
+johnny.example.com. 600 IN A 213.154.224.44
+open.example.com. 600 IN RRSIG A 3 3 600 20070926134150 20070829134150 2854 example.com. MC0CFQCh8bja923UJmg1+sYXMK8WIE4dpgIUQe9sZa0GOcUYSgb2rXoogF8af+Y= ;{id = 2854}
+open.example.com. 600 IN RRSIG AAAA 3 3 600 20070926134150 20070829134150 2854 example.com. MC0CFQCRGJgIS6kEVG7aJfovuG/q3cgOWwIUYEIFCnfRQlMIYWF7BKMQoMbdkE0= ;{id = 2854}
+johnny.example.com. 600 IN RRSIG A 3 3 600 20070926134150 20070829134150 2854 example.com. MCwCFAh0/zSpCd/9eMNz7AyfnuGQFD1ZAhQEpNFNw4XByNEcbi/vsVeii9kp7g== ;{id = 2854}
+_sip._udp.example.com. 600 IN RRSIG SRV 3 4 600 20070926134150 20070829134150 2854 example.com. MCwCFFSRVgOcq1ihVuO6MhCuzWs6SxpVAhRPHHCKy0JxymVkYeFOxTkbVSWMMw== ;{id = 2854}
+_sip._udp.example.com. 600 IN SRV 0 0 5060 johnny.example.com.
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+MATCH TCP
+REPLY RD DO
+SECTION QUESTION
+example.com. IN LOC
+ENTRY_END
+
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+example.com. IN LOC
+SECTION ANSWER
+SECTION AUTHORITY
+example.com. 86400 IN SOA open.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000
+example.com. 86400 IN RRSIG SOA 3 2 86400 20070926134150 20070829134150 2854 example.com. MC0CFQCSs8KJepwaIp5vu++/0hk04lkXvgIUdphJSAE/MYob30WcRei9/nL49tE= ;{id = 2854}
+example.com. 18000 IN NSEC _sip._udp.example.com. A NS SOA MX TXT AAAA NAPTR RRSIG NSEC DNSKEY
+example.com. 18000 IN RRSIG NSEC 3 2 18000 20070926134150 20070829134150 2854 example.com. MCwCFBzOGtpgq4uJ2jeuLPYl2HowIRzDAhQVXNz1haQ1mI7z9lt5gcvWW+lFhA== ;{id = 2854}
+ENTRY_END
+
+STEP 20 QUERY
+ENTRY_BEGIN
+MATCH TCP
+REPLY RD DO
+SECTION QUESTION
+example.com. IN ANY
+ENTRY_END
+
+; Allow validation resuming for the RRSIGs
+STEP 21 TIME_PASSES ELAPSE 0.05
+
+; recursion happens here.
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+example.com. IN ANY
+SECTION ANSWER
+example.com. 86400 IN SOA open.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000
+example.com. 86400 IN RRSIG SOA 3 2 86400 20070926134150 20070829134150 2854 example.com. MC0CFQCSs8KJepwaIp5vu++/0hk04lkXvgIUdphJSAE/MYob30WcRei9/nL49tE= ;{id = 2854}
+example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJIIs70j+sDS/UT2QRp61SE7S3EEXopNXoFE73JLRmvpi/UrOO/Vz4Se6wXv/CYCKjGw06U4WRgRYXcpEhJROyNapmdIKSxhOzfLVE1gqA0PweZR8dtY3aNQSRn3sPpwJr6Mi/PqQKAMMrZ9ckJpf1+bQMOOvxgzz2U1GS18b3yZKcgTMEaJzd/GZYzi/BN2DzQ0MsrSwYXfsNLFOBbs8PJMW4LYIxeeOe6rUgkWOF7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134150 20070829134150 2854 example.com. MCwCFHq7BNVAeLW+Uw/rkjVS08lrMDk/AhR+bvChHfiE4jLb6uoyE54/irCuqA== ;{id = 2854}
+example.com. 600 IN NAPTR 20 0 "s" "SIP+D2U" "" _sip._udp.example.com.
+example.com. 600 IN RRSIG NAPTR 3 2 600 20070926134150 20070829134150 2854 example.com. MC0CFE8qs66bzuOyKmTIacamrmqabMRzAhUAn0MujX1LB0UpTHuLMgdgMgJJlq4= ;{id = 2854}
+example.com. 86400 IN AAAA 2001:7b8:206:1::1
+example.com. 86400 IN RRSIG AAAA 3 2 86400 20070926134150 20070829134150 2854 example.com. MC0CFEqS4WHyqhUkv7t42TsBZJk/Q9paAhUAtTZ8GaXGpot0PmsM0oGzQU+2iw4= ;{id = 2854}
+example.com. 86400 IN TXT "Stichting NLnet Labs"
+example.com. 86400 IN RRSIG TXT 3 2 86400 20070926134150 20070829134150 2854 example.com. MCwCFH3otn2u8zXczBS8L0VKpyAYZGSkAhQLGaQclkzMAzlB5j73opFjdkh8TA== ;{id = 2854}
+example.com. 86400 IN MX 100 v.net.example.
+example.com. 86400 IN MX 50 open.example.com.
+example.com. 86400 IN RRSIG MX 3 2 86400 20070926134150 20070829134150 2854 example.com. MCwCFEKh3jeqh69zcOqWWv3GNKlMECPyAhR9HJkcPLqlyVWUccWDFJfGGcQfdg== ;{id = 2854}
+example.com. 86400 IN NS v.net.example.
+example.com. 86400 IN NS open.example.com.
+example.com. 86400 IN NS ns7.domain-registry.example.
+example.com. 86400 IN RRSIG NS 3 2 86400 20070926134150 20070829134150 2854 example.com. MC0CFQCaRn30X4neKW7KYoTa2kcsoOLgfgIURvKEyDczLypWlx99KpxzMxRYhEc= ;{id = 2854}
+example.com. 86400 IN A 213.154.224.1
+example.com. 86400 IN RRSIG A 3 2 86400 20070926134150 20070829134150 2854 example.com. MCwCFH8kSLxmRTwzlGDxvF1e4y/gM+5dAhQkzyQ2a6Gf+CMaHzVScaUvTt9HhQ== ;{id = 2854}
+example.com. 18000 IN NSEC _sip._udp.example.com. A NS SOA MX TXT AAAA NAPTR RRSIG NSEC DNSKEY
+example.com. 18000 IN RRSIG NSEC 3 2 18000 20070926134150 20070829134150 2854 example.com. MCwCFBzOGtpgq4uJ2jeuLPYl2HowIRzDAhQVXNz1haQ1mI7z9lt5gcvWW+lFhA== ;{id = 2854}
+SECTION AUTHORITY
+SECTION ADDITIONAL
+open.example.com. 600 IN A 213.154.224.1
+open.example.com. 600 IN AAAA 2001:7b8:206:1::53
+open.example.com. 600 IN AAAA 2001:7b8:206:1::1
+open.example.com. 600 IN RRSIG A 3 3 600 20070926134150 20070829134150 2854 example.com. MC0CFQCh8bja923UJmg1+sYXMK8WIE4dpgIUQe9sZa0GOcUYSgb2rXoogF8af+Y= ;{id = 2854}
+open.example.com. 600 IN RRSIG AAAA 3 3 600 20070926134150 20070829134150 2854 example.com. MC0CFQCRGJgIS6kEVG7aJfovuG/q3cgOWwIUYEIFCnfRQlMIYWF7BKMQoMbdkE0= ;{id = 2854}
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/val_scrub_rr_length.rpl b/contrib/unbound/testdata/val_scrub_rr_length.rpl
new file mode 100644
index 000000000000..0219b918e421
--- /dev/null
+++ b/contrib/unbound/testdata/val_scrub_rr_length.rpl
@@ -0,0 +1,164 @@
+; config options
+; The island of trust is at example.com
+server:
+ trust-anchor: "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af"
+ val-override-date: "20070916134226"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ trust-anchor-signaling: no
+ minimal-responses: no
+ rrset-roundrobin: no
+ ede: yes
+ log-servfail: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test validator with scrub of RR for inappropriate length
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8=
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as=
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com. IN DNSKEY 256 3 8 AwEAAdug/L739i0mgN2nuK/bhxu3wFn5Ud9nK2+XUmZQlPUEZUC5YZvm1rfMmEWTGBn87fFxEu/kjFZHJ55JLzqsbbpVHLbmKCTT2gYR2FV2WDKROGKuYbVkJIXdKAjJ0ONuK507NinYvlWXIoxHn22KAWOd9wKgSTNHBlmGkX+ts3hh ;{id = 55566 (zsk), size = 1024b}
+example.com. 3600 IN RRSIG DNSKEY 8 2 3600 20070926134150 20070829134150 55566 example.com. Ni7Q17l2dzKcAnHdU3Mycpdwo0I6qgGxRvBhBNI43xIUFHJpgKpbeMFxKvVTkbwHyMPMIuHmOaC82IBhOpGD10SExVh4erQhWS3Hvl+m4Cwl3WI9N+AW6CTB9yj+d4xzX3bHjjBt6MSk4bU8ABR7qIoAjgjY7zdtUDWQlaM+d18=
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8=
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as=
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.example.com. IN AAAA
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8=
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as=
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+www.example.com. IN A \# 5 0102030405
+; RRSIG includes the malformed record.
+www.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. W4WFu9B81uRvp3Dj8uLIscypznKWuLuKrZqVg1on5/45/3/xyjHvj3TjTL3gruWFXPiQpldvOstXLZ5eN3OpqILdkVey0eqVATujpHwIruY6GWztVx5WptmFfK6E6zzshZ3RmAARqq/czQ+IZli2A9xixdY2H0o1dSU6gohEjjE=
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8=
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as=
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ede=0
+REPLY QR RD RA DO SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+ENTRY_END
+
+SCENARIO_END