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# shellcheck source=conf.sh
17. ../conf.sh
18
19status=0
20n=1
21
22israw() {
23  # shellcheck disable=SC2016
24  $PERL -e 'binmode STDIN;
25             read(STDIN, $input, 8);
26             ($style, $version) = unpack("NN", $input);
27             exit 1 if ($style != 2 || $version > 1);' <"$1" || return $?
28}
29
30isfull() {
31  # there should be no whitespace at the beginning of a line
32  if grep '^[ 	][ 	]*' "$1" >/dev/null 2>&1; then
33    return 1
34  else
35    return 0
36  fi
37}
38
39rawversion() {
40  # shellcheck disable=SC2016
41  $PERL -e 'binmode STDIN;
42             read(STDIN, $input, 8);
43             if (length($input) < 8) { print "not raw\n"; exit 0; };
44             ($style, $version) = unpack("NN", $input);
45             print ($style == 2 || $style == 3 ? "$version\n" :
46		"not raw\n");' <"$1"
47}
48
49sourceserial() {
50  # shellcheck disable=SC2016
51  $PERL -e 'binmode STDIN;
52             read(STDIN, $input, 20);
53             if (length($input) < 20) { print "UNSET\n"; exit; };
54             ($format, $version, $dumptime, $flags, $sourceserial) =
55                     unpack("NNNNN", $input);
56             if ($format != 2 || $version <  1) { print "UNSET\n"; exit; };
57             if ($flags & 02) {
58                     print $sourceserial . "\n";
59             } else {
60                     print "UNSET\n";
61             }' <"$1"
62}
63
64stomp() {
65  # shellcheck disable=SC2016
66  $PERL -e 'open(my $file, "+<", $ARGV[0]);
67              binmode $file;
68              seek($file, $ARGV[1], 0);
69              for (my $i = 0; $i < $ARGV[2]; $i++) {
70                      print $file pack("C", $ARGV[3]);
71              }
72              close($file);' "$@"
73}
74
75restart() {
76  sleep 1
77  start_server --noclean --restart --port "${PORT}" ns3
78}
79
80dig_with_opts() {
81  "$DIG" +tcp +noauth +noadd +nosea +nostat +noquest +nocomm +nocmd -p "${PORT}" "$@"
82}
83
84rndccmd() {
85  "$RNDC" -c ../_common/rndc.conf -p "${CONTROLPORT}" -s "$@"
86}
87
88status=0
89
90echo_i "checking that files in raw format loaded ($n)"
91ret=0
92set -- 1 2 3
93for zone in example example-explicit example-compat; do
94  for server in "$@"; do
95    for qname in ns mx a aaaa cname dname txt rrsig nsec \
96      dnskey ds cdnskey cds; do
97      qtype="$qname"
98      dig_with_opts @10.53.0.${server} -q ${qname}.${zone}. -t ${qtype}
99      echo
100    done >dig.out.${zone}.${server}.test${n}
101    for qname in private-dnskey private-cdnskey; do
102      qtype=$(expr "$qname" : '.*-\(.*\)')
103      dig_with_opts @10.53.0.${server} -q ${qname}.${zone}. -t ${qtype}
104    done >>dig.out.${zone}.${server}.test${n}
105  done
106  digcomp dig.out.${zone}.1.test${n} dig.out.${zone}.2.test${n} || ret=1
107  if [ "$zone" = "example" ]; then
108    set -- 1 2
109    digcomp dig.out.${zone}.1.test${n} dig.out.${zone}.3.test${n} || ret=1
110  fi
111done
112n=$((n + 1))
113[ $ret -eq 0 ] || echo_i "failed"
114status=$((status + ret))
115
116echo_i "checking raw format versions ($n)"
117ret=0
118israw ns1/example.db.raw || ret=1
119israw ns1/example.db.raw1 || ret=1
120israw ns1/example.db.compat || ret=1
121[ "$(rawversion ns1/example.db.raw)" -eq 1 ] || ret=1
122[ "$(rawversion ns1/example.db.raw1)" -eq 1 ] || ret=1
123[ "$(rawversion ns1/example.db.compat)" -eq 0 ] || ret=1
124n=$((n + 1))
125[ $ret -eq 0 ] || echo_i "failed"
126status=$((status + ret))
127
128echo_i "checking source serial numbers ($n)"
129ret=0
130[ "$(sourceserial ns1/example.db.raw)" = "UNSET" ] || ret=1
131[ "$(sourceserial ns1/example.db.serial.raw)" = "3333" ] || ret=1
132n=$((n + 1))
133[ $ret -eq 0 ] || echo_i "failed"
134status=$((status + ret))
135
136echo_i "waiting for transfers to complete"
137for i in 0 1 2 3 4 5 6 7 8 9; do
138  test -f ns2/transfer.db.raw -a -f ns2/transfer.db.txt && break
139  sleep 1
140done
141
142echo_i "checking that secondary was saved in raw format by default ($n)"
143ret=0
144israw ns2/transfer.db.raw || ret=1
145n=$((n + 1))
146[ $ret -eq 0 ] || echo_i "failed"
147status=$((status + ret))
148
149echo_i "checking that secondary was saved in text format when configured ($n)"
150ret=0
151israw ns2/transfer.db.txt && ret=1
152isfull ns2/transfer.db.txt && ret=1
153n=$((n + 1))
154[ $ret -eq 0 ] || echo_i "failed"
155status=$((status + ret))
156
157echo_i "checking that secondary was saved in 'full' style when configured ($n)"
158ret=0
159isfull ns2/transfer.db.full >/dev/null 2>&1 || ret=1
160n=$((n + 1))
161[ $ret -eq 0 ] || echo_i "failed"
162status=$((status + ret))
163
164echo_i "checking that secondary formerly in text format is now raw ($n)"
165for i in 0 1 2 3 4 5 6 7 8 9; do
166  ret=0
167  israw ns2/formerly-text.db >/dev/null 2>&1 || ret=1
168  [ "$(rawversion ns2/formerly-text.db)" -eq 1 ] || ret=1
169  [ $ret -eq 0 ] && break
170  sleep 1
171done
172n=$((n + 1))
173[ $ret -eq 0 ] || echo_i "failed"
174status=$((status + ret))
175
176echo_i "checking that large rdatasets loaded ($n)"
177for i in 0 1 2 3 4 5 6 7 8 9; do
178  ret=0
179  for a in a b c; do
180    $DIG +tcp txt "${a}.large" @10.53.0.2 -p "${PORT}" >"dig.out.ns2.test$n"
181    grep "status: NOERROR" "dig.out.ns2.test$n" >/dev/null || ret=1
182  done
183  [ $ret -eq 0 ] && break
184  sleep 1
185done
186n=$((n + 1))
187[ $ret -eq 0 ] || echo_i "failed"
188status=$((status + ret))
189
190echo_i "checking format transitions: text->raw->text ($n)"
191ret=0
192$CHECKZONE -D -f text -F text -o baseline.txt example.nil ns1/example.db >/dev/null
193$CHECKZONE -D -f text -F raw -o raw.1 example.nil baseline.txt >/dev/null
194$CHECKZONE -D -f raw -F text -o text.1 example.nil raw.1 >/dev/null
195cmp -s baseline.txt text.1 || ret=0
196n=$((n + 1))
197[ $ret -eq 0 ] || echo_i "failed"
198status=$((status + ret))
199
200echo_i "checking raw format loading with journal file rollforward ($n)"
201ret=0
202$NSUPDATE <<END >/dev/null || status=1
203server 10.53.0.3 ${PORT}
204ttl 600
205update add newtext.dynamic IN TXT "added text"
206update delete aaaa.dynamic
207send
208END
209dig_with_opts @10.53.0.3 newtext.dynamic txt >"dig.out.dynamic1.ns3.test$n"
210grep "added text" "dig.out.dynamic1.ns3.test$n" >/dev/null 2>&1 || ret=1
211dig_with_opts +comm @10.53.0.3 added.dynamic txt >"dig.out.dynamic2.ns3.test$n"
212grep "NXDOMAIN" "dig.out.dynamic2.ns3.test$n" >/dev/null 2>&1 || ret=1
213# using "rndc halt" ensures that we don't dump the zone file
214stop_server --use-rndc --halt --port ${CONTROLPORT} ns3
215restart
216check_added_text() {
217  dig_with_opts @10.53.0.3 newtext.dynamic txt >"dig.out.dynamic3.ns3.test$n" || return 1
218  grep "added text" "dig.out.dynamic3.ns3.test$n" >/dev/null || return 1
219  return 0
220}
221retry_quiet 10 check_added_text || ret=1
222dig_with_opts +comm @10.53.0.3 added.dynamic txt >"dig.out.dynamic4.ns3.test$n"
223grep "NXDOMAIN" "dig.out.dynamic4.ns3.test$n" >/dev/null 2>&1 || ret=1
224n=$((n + 1))
225[ $ret -eq 0 ] || echo_i "failed"
226status=$((status + ret))
227
228echo_i "checking raw format file dumps correctly ($n)"
229ret=0
230$NSUPDATE <<END >/dev/null || status=1
231server 10.53.0.3 ${PORT}
232ttl 600
233update add moretext.dynamic IN TXT "more text"
234send
235END
236dig_with_opts @10.53.0.3 moretext.dynamic txt >"dig.out.dynamic1.ns3.test$n"
237grep "more text" "dig.out.dynamic1.ns3.test$n" >/dev/null 2>&1 || ret=1
238# using "rndc stop" will cause the zone file to flush before shutdown
239stop_server --use-rndc --port ${CONTROLPORT} ns3
240rm ns3/*.jnl
241restart
242#shellcheck disable=SC2034
243for i in 0 1 2 3 4 5 6 7 8 9; do
244  lret=0
245  dig_with_opts +comm @10.53.0.3 moretext.dynamic txt >"dig.out.dynamic2.ns3.test$n"
246  grep "more text" "dig.out.dynamic2.ns3.test$n" >/dev/null 2>&1 || lret=1
247  [ $lret -eq 0 ] && break
248done
249[ $lret -eq 1 ] && ret=1
250n=$((n + 1))
251[ $ret -eq 0 ] || echo_i "failed"
252status=$((status + ret))
253
254echo_i "checking raw format zone is scheduled for resigning (compilezone) ($n)"
255ret=0
256rndccmd 10.53.0.1 zonestatus signed >rndc.out 2>&1 || ret=1
257grep 'next resign' rndc.out >/dev/null 2>&1 || ret=1
258n=$((n + 1))
259[ $ret -eq 0 ] || echo_i "failed"
260status=$((status + ret))
261
262echo_i "checking raw format zone is scheduled for resigning (signzone) ($n)"
263ret=0
264rndccmd 10.53.0.1 freeze signed >rndc.out 2>&1 || ret=1
265(
266  cd ns1 || exit 1
267  $SIGNER -S -O raw -f signed.db.raw -o signed signed.db >/dev/null
268)
269rndc_reload ns1 10.53.0.1 signed
270rndccmd 10.53.0.1 zonestatus signed >rndc.out 2>&1 || ret=1
271grep 'next resign' rndc.out >/dev/null 2>&1 || ret=1
272n=$((n + 1))
273[ $ret -eq 0 ] || echo_i "failed"
274status=$((status + ret))
275
276echo_i "exit status: $status"
277[ $status -eq 0 ] || exit 1
278