1#!/bin/sh
2
3# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
4#
5# SPDX-License-Identifier: MPL-2.0
6#
7# This Source Code Form is subject to the terms of the Mozilla Public
8# License, v. 2.0.  If a copy of the MPL was not distributed with this
9# file, you can obtain one at https://mozilla.org/MPL/2.0/.
10#
11# See the COPYRIGHT file distributed with this work for additional
12# information regarding copyright ownership.
13
14set -e
15
16. ../conf.sh
17
18status=0
19n=0
20
21RNDCOPTS="-c ../_common/rndc.conf -s 10.53.0.2 -p ${CONTROLPORT}"
22DIGOPTS="+nosea +nocomm +nocmd +noquest +noadd +noauth +nocomm \
23         +nostat @10.53.0.2 -p ${PORT}"
24
25# fill the cache with nodes from flushtest.example zone
26load_cache() {
27  # empty all existing cache data
28  $RNDC $RNDCOPTS flush
29
30  # load the positive cache entries
31  $DIG $DIGOPTS -f - <<EOF >/dev/null 2>&1
32txt top1.flushtest.example
33txt second1.top1.flushtest.example
34txt third1.second1.top1.flushtest.example
35txt third2.second1.top1.flushtest.example
36txt second2.top1.flushtest.example
37txt second3.top1.flushtest.example
38txt second1.top2.flushtest.example
39txt second2.top2.flushtest.example
40txt second3.top2.flushtest.example
41txt top3.flushtest.example
42txt second1.top3.flushtest.example
43txt third1.second1.top3.flushtest.example
44txt third2.second1.top3.flushtest.example
45txt third1.second2.top3.flushtest.example
46txt third2.second2.top3.flushtest.example
47txt second3.top3.flushtest.example
48EOF
49
50  # load the negative cache entries
51  # nxrrset:
52  $DIG $DIGOPTS a third1.second1.top1.flushtest.example >/dev/null
53  # nxdomain:
54  $DIG $DIGOPTS txt top4.flushtest.example >/dev/null
55  # empty nonterminal:
56  $DIG $DIGOPTS txt second2.top3.flushtest.example >/dev/null
57
58  # sleep 2 seconds ensure the TTLs will be lower on cached data
59  sleep 2
60}
61
62dump_cache() {
63  rndc_dumpdb ns2 -cache _default
64}
65
66clear_cache() {
67  $RNDC $RNDCOPTS flush
68}
69
70in_cache() {
71  ttl=$($DIG $DIGOPTS "$@" | awk '{print $2}')
72  [ -z "$ttl" ] && {
73    ttl=$($DIG $DIGOPTS +noanswer +auth "$@" | awk '{print $2}')
74    [ "$ttl" -ge 3599 ] && return 1
75    return 0
76  }
77  [ "$ttl" -ge 3599 ] && return 1
78  return 0
79}
80
81# Extract records at and below name "$1" from the cache dump in file "$2".
82filter_tree() {
83  tree="$1"
84  file="$2"
85  perl -n -e '
86		next if /^;/;
87		if (/'"$tree"'/ || (/^\t/ && $print)) {
88			$print = 1;
89		} else {
90			$print = 0;
91		}
92		print if $print;
93	' "$file"
94}
95
96n=$((n + 1))
97echo_i "check correctness of routine cache cleaning ($n)"
98$DIG $DIGOPTS +tcp +keepopen -b 10.53.0.7 -f dig.batch >dig.out.ns2 || status=1
99
100digcomp --lc dig.out.ns2 knowngood.dig.out || status=1
101
102n=$((n + 1))
103echo_i "only one tcp socket was used ($n)"
104tcpclients=$(awk '$3 == "client" && $5 ~ /10.53.0.7#[0-9]*:/ {print $5}' ns2/named.run | sort | uniq -c | wc -l)
105
106test $tcpclients -eq 1 || {
107  status=1
108  echo_i "failed"
109}
110
111n=$((n + 1))
112echo_i "reset and check that records are correctly cached initially ($n)"
113ret=0
114load_cache
115dump_cache
116nrecords=$(filter_tree flushtest.example ns2/named_dump.db.test$n | grep -E '(TXT|ANY)' | wc -l)
117[ $nrecords -eq 18 ] || {
118  ret=1
119  echo_i "found $nrecords records expected 18"
120}
121if [ $ret != 0 ]; then echo_i "failed"; fi
122status=$((status + ret))
123
124n=$((n + 1))
125echo_i "check flushing of the full cache ($n)"
126ret=0
127clear_cache
128dump_cache
129nrecords=$(filter_tree flushtest.example ns2/named_dump.db.test$n | wc -l)
130[ $nrecords -eq 0 ] || ret=1
131if [ $ret != 0 ]; then echo_i "failed"; fi
132status=$((status + ret))
133
134n=$((n + 1))
135echo_i "check flushing of individual nodes (interior node) ($n)"
136ret=0
137clear_cache
138load_cache
139# interior node
140in_cache txt top1.flushtest.example || ret=1
141$RNDC $RNDCOPTS flushname top1.flushtest.example
142in_cache txt top1.flushtest.example && ret=1
143if [ $ret != 0 ]; then echo_i "failed"; fi
144status=$((status + ret))
145
146n=$((n + 1))
147echo_i "check flushing of individual nodes (leaf node, under the interior node) ($n)"
148ret=0
149# leaf node, under the interior node (should still exist)
150in_cache txt third2.second1.top1.flushtest.example || ret=1
151$RNDC $RNDCOPTS flushname third2.second1.top1.flushtest.example
152in_cache txt third2.second1.top1.flushtest.example && ret=1
153if [ $ret != 0 ]; then echo_i "failed"; fi
154status=$((status + ret))
155
156n=$((n + 1))
157echo_i "check flushing of individual nodes (another leaf node, with both positive and negative cache entries) ($n)"
158ret=0
159# another leaf node, with both positive and negative cache entries
160in_cache a third1.second1.top1.flushtest.example || ret=1
161in_cache txt third1.second1.top1.flushtest.example || ret=1
162$RNDC $RNDCOPTS flushname third1.second1.top1.flushtest.example
163in_cache a third1.second1.top1.flushtest.example && ret=1
164in_cache txt third1.second1.top1.flushtest.example && ret=1
165if [ $ret != 0 ]; then echo_i "failed"; fi
166status=$((status + ret))
167
168n=$((n + 1))
169echo_i "check flushing a nonexistent name ($n)"
170ret=0
171$RNDC $RNDCOPTS flushname fake.flushtest.example || ret=1
172if [ $ret != 0 ]; then echo_i "failed"; fi
173status=$((status + ret))
174
175n=$((n + 1))
176echo_i "check flushing of namespaces ($n)"
177ret=0
178clear_cache
179load_cache
180# flushing leaf node should leave the interior node:
181in_cache txt third1.second1.top1.flushtest.example || ret=1
182in_cache txt top1.flushtest.example || ret=1
183$RNDC $RNDCOPTS flushtree third1.second1.top1.flushtest.example
184in_cache txt third1.second1.top1.flushtest.example && ret=1
185in_cache txt top1.flushtest.example || ret=1
186in_cache txt second1.top1.flushtest.example || ret=1
187in_cache txt third2.second1.top1.flushtest.example || ret=1
188$RNDC $RNDCOPTS flushtree second1.top1.flushtest.example
189in_cache txt top1.flushtest.example || ret=1
190in_cache txt second1.top1.flushtest.example && ret=1
191in_cache txt third2.second1.top1.flushtest.example && ret=1
192
193# flushing from an empty node should still remove all its children
194in_cache txt second1.top2.flushtest.example || ret=1
195$RNDC $RNDCOPTS flushtree top2.flushtest.example
196in_cache txt second1.top2.flushtest.example && ret=1
197in_cache txt second2.top2.flushtest.example && ret=1
198in_cache txt second3.top2.flushtest.example && ret=1
199if [ $ret != 0 ]; then echo_i "failed"; fi
200status=$((status + ret))
201
202n=$((n + 1))
203echo_i "check flushing a nonexistent namespace ($n)"
204ret=0
205$RNDC $RNDCOPTS flushtree fake.flushtest.example || ret=1
206if [ $ret != 0 ]; then echo_i "failed"; fi
207status=$((status + ret))
208
209n=$((n + 1))
210echo_i "check the number of cached records remaining ($n)"
211ret=0
212dump_cache
213nrecords=$(filter_tree flushtest.example ns2/named_dump.db.test$n | grep -v '^;' | grep -E '(TXT|ANY)' | wc -l)
214[ $nrecords -eq 17 ] || {
215  ret=1
216  echo_i "found $nrecords records expected 17"
217}
218if [ $ret != 0 ]; then echo_i "failed"; fi
219status=$((status + ret))
220
221n=$((n + 1))
222echo_i "check the check that flushname of a partial match works ($n)"
223ret=0
224in_cache txt second2.top1.flushtest.example || ret=1
225$RNDC $RNDCOPTS flushtree example
226in_cache txt second2.top1.flushtest.example && ret=1
227if [ $ret != 0 ]; then echo_i "failed"; fi
228status=$((status + ret))
229
230n=$((n + 1))
231echo_i "check the number of cached records remaining ($n)"
232ret=0
233dump_cache
234nrecords=$(filter_tree flushtest.example ns2/named_dump.db.test$n | grep -E '(TXT|ANY)' | wc -l)
235[ $nrecords -eq 1 ] || {
236  ret=1
237  echo_i "found $nrecords records expected 1"
238}
239if [ $ret != 0 ]; then echo_i "failed"; fi
240status=$((status + ret))
241
242n=$((n + 1))
243echo_i "check flushtree clears adb correctly ($n)"
244ret=0
245load_cache
246dump_cache
247mv ns2/named_dump.db.test$n ns2/named_dump.db.test$n.a
248sed -n '/plain success\/timeout/,/Unassociated entries/p' \
249  ns2/named_dump.db.test$n.a >sed.out.$n.a
250grep 'plain success/timeout' sed.out.$n.a >/dev/null 2>&1 || ret=1
251grep 'Unassociated entries' sed.out.$n.a >/dev/null 2>&1 || ret=1
252grep 'ns.flushtest.example' sed.out.$n.a >/dev/null 2>&1 || ret=1
253$RNDC $RNDCOPTS flushtree flushtest.example || ret=1
254dump_cache
255mv ns2/named_dump.db.test$n ns2/named_dump.db.test$n.b
256sed -n '/plain success\/timeout/,/Unassociated entries/p' \
257  ns2/named_dump.db.test$n.b >sed.out.$n.b
258grep 'plain success/timeout' sed.out.$n.b >/dev/null 2>&1 || ret=1
259grep 'Unassociated entries' sed.out.$n.b >/dev/null 2>&1 || ret=1
260grep 'ns.flushtest.example' sed.out.$n.b >/dev/null 2>&1 && ret=1
261if [ $ret != 0 ]; then echo_i "failed"; fi
262status=$((status + ret))
263
264n=$((n + 1))
265echo_i "check expire option returned from primary zone ($n)"
266ret=0
267$DIG @10.53.0.1 -p ${PORT} +expire soa expire-test >dig.out.expire || ret=1
268grep EXPIRE: dig.out.expire >/dev/null || ret=1
269if [ $ret != 0 ]; then echo_i "failed"; fi
270status=$((status + ret))
271
272n=$((n + 1))
273echo_i "check expire option returned from secondary zone ($n)"
274ret=0
275$DIG @10.53.0.2 -p ${PORT} +expire soa expire-test >dig.out.expire || ret=1
276grep EXPIRE: dig.out.expire >/dev/null || ret=1
277if [ $ret != 0 ]; then echo_i "failed"; fi
278status=$((status + ret))
279
280echo_i "exit status: $status"
281[ $status -eq 0 ] || exit 1
282