1# Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
2#
3# Permission to use, copy, modify, and/or distribute this software for any
4# purpose with or without fee is hereby granted, provided that the above
5# copyright notice and this permission notice appear in all copies.
6#
7# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13# PERFORMANCE OF THIS SOFTWARE.
14
15# Id: tests.sh,v 1.12 2012/01/07 23:46:53 tbox Exp 
16
17# test response policy zones (RPZ)
18
19SYSTEMTESTTOP=..
20. $SYSTEMTESTTOP/conf.sh
21
22ns=10.53.0
23ns1=$ns.1			    # root, defining the others
24ns2=$ns.2			    # server whose answers are rewritten
25ns3=$ns.3			    # resolve that does the rewriting
26ns4=$ns.4			    # another server that is rewritten
27ns5=$ns.5			    # check performance with this server
28
29HAVE_CORE=
30
31USAGE="$0: [-x]"
32while getopts "x" c; do
33    case $c in
34	x) set -x;;
35	*) echo "$USAGE" 1>&2; exit 1;;
36    esac
37done
38shift `expr $OPTIND - 1 || true`
39if test "$#" -ne 0; then
40    echo "$USAGE" 1>&2
41    exit 1
42fi
43# really quit on control-C
44trap 'exit 1' 1 2 15
45
46
47RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p 9953 -s"
48
49digcmd () {
50    digcmd_args="+noadd +nosearch +time=1 +tries=1 -p 5300 $*"
51    expr "$digcmd_args" : '.*@' >/dev/null || \
52	digcmd_args="$digcmd_args @$ns3"
53    expr "$digcmd_args" : '.*+[no]*auth' >/dev/null || \
54	digcmd_args="+noauth $digcmd_args"
55    #echo I:dig $digcmd_args 1>&2
56    $DIG $digcmd_args
57}
58
59# set DIGNM=file name for dig output
60GROUP_NM=
61TEST_NUM=0
62make_dignm () {
63    TEST_NUM=`expr $TEST_NUM + 1`
64    DIGNM=dig.out$GROUP_NM-$TEST_NUM
65    while test -f $DIGNM; do
66	TEST_NUM="$TEST_NUM+"
67	DIGNM=dig.out$GROUP_NM-$TEST_NUM
68    done
69}
70
71setret () {
72    ret=1
73    echo "$*"
74}
75
76# (re)load the reponse policy zones with the rules in the file $TEST_FILE
77load_db () {
78    if test -n "$TEST_FILE"; then
79	if $NSUPDATE -v $TEST_FILE; then : ; else
80	    echo "I:failed to update policy zone with $TEST_FILE"
81	    exit 1
82	fi
83    fi
84}
85
86restart () {
87    # try to ensure that the server really has stopped
88    # and won't mess with ns$1/name.pid
89    if test -z "$HAVE_CORE" -a -f ns$1/named.pid; then
90	$RNDCCMD $ns$1 halt >/dev/null 2>&1
91	if test -f ns$1/named.pid; then
92	    sleep 1
93	    PID=`cat ns$1/named.pid 2>/dev/null`
94	    if test -n "$PID"; then
95		echo "I:killing ns$1 server $PID"
96		kill -9 $PID
97	    fi
98	fi
99    fi
100    rm -f ns$1/*.jnl
101    if test -f ns$1/base.db; then
102	for NM in ns$1/bl*.db; do
103	    cp -f ns$1/base.db $NM
104	done
105    fi
106    $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns$1
107    load_db
108}
109
110# $1=server and irrelevant args  $2=error message
111ckalive () {
112    CKALIVE_NS=`expr "$1" : '.*@ns\([1-9]\).*'`
113    if test -z "$CKALIVE_NS"; then
114	CKALIVE_NS=3
115    fi
116    eval CKALIVE_IP=\$ns$CKALIVE_NS
117    $RNDCCMD $CKALIVE_IP status >/dev/null 2>&1 && return 0
118    HAVE_CORE=yes
119    setret "$2"
120    # restart the server to avoid stalling waiting for it to stop
121    restart $CKALIVE_NS
122    return 1
123}
124
125# $1=message  $2=optional test file name
126start_group () {
127    ret=0
128    test -n "$1" && echo "I:checking $1"
129    TEST_FILE=$2
130    if test -n "$TEST_FILE"; then
131	GROUP_NM="-$TEST_FILE"
132	load_db
133    else
134	GROUP_NM=
135    fi
136    TEST_NUM=0
137}
138
139end_group () {
140    if test -n "$TEST_FILE"; then
141	sed -e 's/[	 ]add[	 ]/ delete /' $TEST_FILE | $NSUPDATE
142	TEST_FILE=
143    fi
144    ckalive $ns3 "I:failed; ns3 server crashed and restarted"
145    if test "$status" -eq 0; then
146	# look for complaints from rpz.c
147	EMSGS=`grep -l 'invalid rpz' */*.run`
148	if test -n "$EMSGS"; then
149	    setret "I:'invalid rpz' complaints in $EMSGS starting with:"
150	    grep 'invalid rpz' */*.run | sed -e '4,$d' -e 's/^/I:    /'
151	fi
152	# look for complaints from rpz.c and query.c
153	EMSGS=`grep -l 'rpz .*failed' */*.run`
154	if test -n "$EMSGS"; then
155	    setret "I:'rpz failed' complaints in $EMSGS starting with:"
156	    grep 'rpz .*failed' */*.run | sed -e '4,$d' -e 's/^/I:    /'
157	fi
158    fi
159    status=`expr $status + $ret`
160    GROUP_NM=
161}
162
163# $1=dig args $2=other dig output file
164ckresult () {
165    #ckalive "$1" "I:server crashed by 'dig $1'" || return 1
166    if $PERL $SYSTEMTESTTOP/digcomp.pl $DIGNM $2 >/dev/null; then
167	rm -f ${DIGNM}*
168	return 0
169    fi
170    setret "I:'dig $1' wrong; diff $DIGNM $2"
171    return 1
172}
173
174# check only that the server does not crash
175# $1=target domain  $2=optional query type
176nocrash () {
177    digcmd $* >/dev/null
178    ckalive "$*" "I:server crashed by 'dig $*'"
179}
180
181
182# check rewrite to NXDOMAIN
183# $1=target domain  $2=optional query type
184nxdomain () {
185    make_dignm
186    digcmd $*								\
187	| sed -e 's/^[a-z].*	IN	CNAME	/;xxx &/'		\
188		-e 's/^[a-z].*	IN	RRSIG	/;xxx &/'		\
189	    >$DIGNM
190    ckresult "$*" proto.nxdomain
191}
192
193# check rewrite to NODATA
194# $1=target domain  $2=optional query type
195nodata () {
196    make_dignm
197    digcmd $*								\
198	| sed -e 's/^[a-z].*	IN	CNAME	/;xxx &/' >$DIGNM
199    ckresult "$*" proto.nodata
200}
201
202# check rewrite to an address
203#   modify the output so that it is easily compared, but save the original line
204# $1=IPv4 address  $2=digcmd args  $3=optional TTL
205addr () {
206    ADDR=$1
207    make_dignm
208    digcmd $2 >$DIGNM
209    #ckalive "$2" "I:server crashed by 'dig $2'" || return 1
210    ADDR_ESC=`echo "$ADDR" | sed -e 's/\./\\\\./g'`
211    ADDR_TTL=`sed -n -e  "s/^[-.a-z0-9]\{1,\}	*\([0-9]*\)	IN	A\{1,4\}	${ADDR_ESC}\$/\1/p" $DIGNM`
212    if test -z "$ADDR_TTL"; then
213	setret "I:'dig $2' wrong; no address $ADDR record in $DIGNM"
214	return 1
215    fi
216    if test -n "$3" && test "$ADDR_TTL" -ne "$3"; then
217	setret "I:'dig $2' wrong; TTL=$ADDR_TTL instead of $3 in $DIGNM"
218	return 1
219    fi
220    rm -f ${DIGNM}*
221}
222
223# check that a response is not rewritten
224# $1=target domain  $2=optional query type
225nochange () {
226    make_dignm
227    digcmd $* >$DIGNM
228    digcmd $* @$ns2 >${DIGNM}_OK
229    ckresult "$*" ${DIGNM}_OK && rm -f ${DIGNM}_OK
230}
231
232# check against a 'here document'
233here () {
234    make_dignm
235    sed -e 's/^[	 ]*//' >${DIGNM}_OK
236    digcmd $* >$DIGNM
237    ckresult "$*" ${DIGNM}_OK
238}
239
240# make prototype files to check against rewritten results
241digcmd nonexistent @$ns2 >proto.nxdomain
242digcmd txt-only.tld2 @$ns2 >proto.nodata
243
244
245status=0
246
247start_group "QNAME rewrites" test1
248nochange .				# 1 do not crash or rewrite root
249nxdomain a0-1.tld2			# 2
250nodata a3-1.tld2			# 3
251nodata a3-2.tld2			# 4 no crash on DNAME
252nodata sub.a3-2.tld2
253nxdomain a4-2.tld2			# 6 rewrite based on CNAME target
254nxdomain a4-2-cname.tld2		# 7
255nodata a4-3-cname.tld2			# 8
256addr 12.12.12.12  a4-1.sub1.tld2	# 9 A replacement
257addr 12.12.12.12  a4-1.sub2.tld2	# 10 A replacement with wildcard
258addr 12.12.12.12  nxc1.sub1.tld2	# 11 replace NXDOMAIN with CNAME
259addr 12.12.12.12  nxc2.sub1.tld2	# 12 replace NXDOMAIN with CNAME chain
260addr 127.4.4.1	  a4-4.tld2		# 13 prefer 1st conflicting QNAME zone
261nochange a6-1.tld2			# 14
262addr 127.6.2.1	  a6-2.tld2		# 15
263addr 56.56.56.56  a3-6.tld2		# 16 wildcard CNAME
264addr 57.57.57.57  a3-7.sub1.tld2	# 17 wildcard CNAME
265addr 127.0.0.16	  a4-5-cname3.tld2	# 18 CNAME chain
266addr 127.0.0.17	  a4-6-cname3.tld2	# 19 stop short in CNAME chain
267nochange a0-1.tld2	    +norecurse	# 20 check that RD=1 is required
268nochange a3-1.tld2	    +norecurse	# 21
269nochange a3-2.tld2	    +norecurse	# 22
270nochange sub.a3-2.tld2	    +norecurse	# 23
271nxdomain c1.crash2.tld3			# 24 assert in rbtdb.c
272nxdomain a0-1.tld2	    +dnssec	# 25 simple DO=1 without signatures
273nxdomain a0-1.tld2s			# 26 simple DO=0 with signatures
274nochange a0-1.tld2s	    +dnssec	# 27 simple DO=1 with signatures
275nxdomain a0-1s-cname.tld2s  +dnssec	# 28 DNSSEC too early in CNAME chain
276nochange a0-1-scname.tld2   +dnssec	# 29 DNSSEC on target in CNAME chain
277nochange a0-1.tld2s srv +auth +dnssec	# 30 no write for +DNSSEC and no record
278nxdomain a0-1.tld2s srv			# 31
279end_group
280
281start_group "IP rewrites" test2
282nodata a3-1.tld2			# 1 NODATA
283nochange a3-2.tld2			# 2 no policy record so no change
284nochange a4-1.tld2			# 3 obsolete PASSTHRU record style
285nxdomain a4-2.tld2			# 4
286nochange a4-2.tld2 -taaaa		# 5 no A => no policy rewrite
287nochange a4-2.tld2 -ttxt		# 6 no A => no policy rewrite
288nxdomain a4-2.tld2 -tany		# 7 no A => no policy rewrite
289nodata a4-3.tld2			# 8
290nxdomain a3-1.tld2 -taaaa		# 9 IPv6 policy
291nochange a4-1-aaaa.tld2 -taaaa		# 10
292addr 127.0.0.1	 a5-1-2.tld2		# 11 prefer smallest policy address
293addr 127.0.0.1	 a5-3.tld2		# 12 prefer first conflicting IP zone
294addr 14.14.14.14 a5-4.tld2		# 13 prefer QNAME to IP
295nochange a5-4.tld2	    +norecurse	# 14 check that RD=1 is required
296nochange a4-4.tld2			# 15 PASSTHRU
297nxdomain c2.crash2.tld3			# 16 assert in rbtdb.c
298end_group
299
300# check that IP addresses for previous group were deleted from the radix tree
301start_group "radix tree deletions"
302nochange a3-1.tld2
303nochange a3-2.tld2
304nochange a4-1.tld2
305nochange a4-2.tld2
306nochange a4-2.tld2 -taaaa
307nochange a4-2.tld2 -ttxt
308nochange a4-2.tld2 -tany
309nochange a4-3.tld2
310nochange a3-1.tld2 -tAAAA
311nochange a4-1-aaaa.tld2 -tAAAA
312nochange a5-1-2.tld2
313end_group
314
315if ./rpz nsdname; then
316    start_group "NSDNAME rewrites" test3
317    nochange a3-1.tld2
318    nochange a3-1.tld2	    +dnssec	# 2 this once caused problems
319    nxdomain a3-1.sub1.tld2		# 3 NXDOMAIN *.sub1.tld2 by NSDNAME
320    nxdomain a3-1.subsub.sub1.tld2
321    nxdomain a3-1.subsub.sub1.tld2 -tany
322    addr 12.12.12.12 a4-2.subsub.sub2.tld2 # 6 walled garden for *.sub2.tld2
323    nochange a3-2.tld2.			# 7 exempt rewrite by name
324    nochange a0-1.tld2.			# 8 exempt rewrite by address block
325    addr 12.12.12.12 a4-1.tld2		# 9 prefer QNAME policy to NSDNAME
326    addr 127.0.0.1 a3-1.sub3.tld2	# 10 prefer policy for largest NSDNAME
327    addr 127.0.0.2 a3-1.subsub.sub3.tld2
328    nxdomain xxx.crash1.tld2		# 12 dns_db_detachnode() crash
329    end_group
330else
331    echo "I:NSDNAME not checked; named not configured with --enable-rpz-nsdname"
332fi
333
334if ./rpz nsip; then
335    start_group "NSIP rewrites" test4
336    nxdomain a3-1.tld2			# 1 NXDOMAIN for all of tld2 by NSIP
337    nochange a3-2.tld2.			# 2 exempt rewrite by name
338    nochange a0-1.tld2.			# 3 exempt rewrite by address block
339    nochange a3-1.tld4			# 4 different NS IP address
340    end_group
341else
342    echo "I:NSIP not checked; named not configured with --enable-rpz-nsip"
343fi
344
345# policies in ./test5 overridden by response-policy{} in ns3/named.conf
346#   and in ns5/named.conf
347start_group "policy overrides" test5
348addr 127.0.0.1 a3-1.tld2		# 1 bl-given
349nochange a3-2.tld2			# 2 bl-passthru
350nochange a3-3.tld2			# 3 bl-no-op	obsolete for passthru
351nochange a3-4.tld2			# 4 bl-disabled
352nodata a3-5.tld2			# 5 bl-nodata
353nodata a3-5.tld2    +norecurse		# 6 bl-nodata	    recursive-only no
354nodata a3-5.tld2			# 7 bl-nodata
355nodata a3-5.tld2    +norecurse	@$ns5	# 8 bl-nodata	    recursive-only no
356nodata a3-5.tld2s		@$ns5	# 9 bl-nodata
357nodata a3-5.tld2s   +dnssec	@$ns5	# 10 bl-nodata	    break-dnssec
358nxdomain a3-6.tld2			# 11 bl-nxdomain
359here a3-7.tld2 -tany <<'EOF'
360    ;; status: NOERROR, x
361    a3-7.tld2.	    x	IN	CNAME   txt-only.tld2.
362    txt-only.tld2.  x	IN	TXT     "txt-only-tld2"
363EOF
364addr 58.58.58.58 a3-8.tld2		# 13 bl_wildcname
365addr 59.59.59.59 a3-9.sub9.tld2		# 14 bl_wildcname
366addr 12.12.12.12 a3-15.tld2		# 15 bl-garden	via CNAME to a12.tld2
367addr 127.0.0.16 a3-16.tld2	    100	# 16 bl		    max-policy-ttl 100
368addr 17.17.17.17 "a3-17.tld2 @$ns5" 90	# 17 ns5 bl	    max-policy-ttl 90
369end_group
370
371# check that miscellaneous bugs are still absent
372start_group "crashes"
373for Q in RRSIG SIG ANY 'ANY +dnssec'; do
374    nocrash a3-1.tld2 -t$Q
375    nocrash a3-2.tld2 -t$Q
376    nocrash a3-5.tld2 -t$Q
377    nocrash www.redirect -t$Q
378    nocrash www.credirect -t$Q
379done
380end_group
381
382
383# superficial test for major performance bugs
384QPERF=`sh qperf.sh`
385if test -n "$QPERF"; then
386    perf () {
387	echo "I:checking performance $1"
388	# don't measure the costs of -d99
389	$RNDCCMD $ns5 notrace >/dev/null
390	$QPERF -1 -l2 -d ns5/requests -s $ns5 -p 5300 >ns5/$2.perf
391	ckalive $ns5 "I:failed; server #5 crashed"
392    }
393    trim () {
394	sed -n -e 's/.*Queries per second: *\([0-9]*\).*/\1/p' ns5/$1.perf
395    }
396
397    # Dry run to prime disk cache
398    #	Otherwise a first test of either flavor is 25% low
399    perf 'to prime disk cache' rpz
400
401    # get queries/second with rpz
402    perf 'with rpz' rpz
403
404    # turn off rpz and measure queries/second again
405    # Don't wait for a clean stop.  Clean stops of this server need seconds
406    # until the sockets are closed.  5 or 10 seconds after that, the
407    # server really stops and deletes named.pid.
408    echo "# rpz off" >ns5/rpz-switch
409    PID=`cat ns5/named.pid`
410    test -z "$PID" || kill -9 "$PID"
411    $PERL $SYSTEMTESTTOP/start.pl --noclean --restart . ns5
412    perf 'without rpz' norpz
413
414    # Don't wait for a clean stop.  Clean stops of this server need seconds
415    # until the sockets are closed.  5 or 10 seconds after that, the
416    # server really stops and deletes named.pid.
417    echo "# rpz off" >ns5/rpz-switch
418    PID=`cat ns5/named.pid`
419    test -z "$PID" || kill -9 "$PID" && rm -f ns5/named.pid
420
421    NORPZ=`trim norpz`
422    RPZ=`trim rpz`
423    echo "I:$RPZ qps with RPZ versus $NORPZ qps without"
424
425    # fail if RPZ costs more than 100%
426    NORPZ2=`expr "$NORPZ" / 2`
427    if test "$RPZ" -le "$NORPZ2"; then
428	echo "I:rpz $RPZ qps too far below non-RPZ $NORPZ qps"
429	status=`expr $status + 1`
430    fi
431else
432    echo "I:performance not checked; queryperf not available"
433fi
434
435
436# restart the main test RPZ server to see if that creates a core file
437if test -z "$HAVE_CORE"; then
438    $PERL $SYSTEMTESTTOP/stop.pl . ns3
439    restart 3
440    HAVE_CORE=`find ns* -name '*core*' -print`
441    test -z "$HAVE_CORE" || setret "I:found $HAVE_CORE; memory leak?"
442fi
443
444
445echo "I:exit status: $status"
446exit $status
447