Commit 6d7693c4 authored by Evan Hunt's avatar Evan Hunt
Browse files

[master] add chain ordering tests

4626.	[test]		Added more tests for handling of different record
			ordering in CNAME and DNAME responses. [QA #430]
parent 243cfadf
4626. [test] Added more tests for handling of different record
ordering in CNAME and DNAME responses. [QA #430]
4625. [bug] Running "rndc addzone" and "rndc delzone" at close
to the same time could trigger a deadlock if using
LMDB. [RT #45209]
......
Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
See COPYRIGHT in the source root or http://isc.org/copyright.html for terms.
ns1 is the root server.
ns2 and ns5 are both authoritative servers.
ans3 is a mock authoritative server that can return various broken
responses.
ans4 is a mock authoritative server that can return CNAME or DNAME
responses of arbitrary size in arbitrary order.
ns7 is the resolver under test.
Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
See COPYRIGHT in the source root or http://isc.org/copyright.html for terms.
REQUIREMENTS
ans.py requires at least dnspython 1.12.0.
"ans.py" is a fairly simple Python script that will respond as an
authoritative server to DNS queries. It opens a UDP socket on 10.53.0.4
and fd92:7065:b8e:ffff::8, port 5300 (these are for DNS queries) and a TCP
socket addresses on 10.53.0.4 at port 5301 (this is the control channel).
Please note that all functionality and formatting are subject to change as
we determine what features the tool will need.
"ans.py" will respond to queries as follows: TBD
#!/usr/bin/python
############################################################################
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
############################################################################
############################################################################
# ans.py: See README.anspy for details.
############################################################################
from __future__ import print_function
import os
import sys
import signal
import socket
import select
from datetime import datetime, timedelta
import dns, dns.message, dns.query
from dns.rdatatype import *
from dns.rdataclass import *
from dns.rcode import *
from dns.name import *
############################################################################
# set up the RRs to be returned in the next answer
#
# the message contains up to two pipe-separated ('|') fields.
#
# the first field of the message is a comma-separated list
# of actions indicating what to put into the answer set
# (e.g., a dname, a cname, another cname, etc)
#
# supported actions:
# - cname (cname from the current name to a new one in the same domain)
# - dname (dname to a new domain, plus a synthesized cname)
# - xname ("external" cname, to a new name in a new domain)
#
# example: xname, dname, cname represents a CNAME to an external
# domain which is then answered by a DNAME and synthesized
# CNAME pointing to yet another domain, which is then answered
# by a CNAME within the same domain, and finally an answer
# to the query. each RR in the answer set has a corresponding
# RRSIG. these signatures are not valid, but will exercise the
# response parser.
#
# the second field is a comma-separated list of which RRs in the
# answer set to include in the answer, in which order. if prepended
# with 's', the number indicates which signature to include.
#
# examples: for the answer set "cname, cname, cname", an rr set
# '1, s1, 2, s2, 3, s3, 4, s4' indicates that all four RRs should
# be included in the answer, with siagntures, in the origninal
# order, while 4, s4, 3, s3, 2, s2, 1, s1' indicates the order
# should be reversed, 's3, s3, s3, s3' indicates that the third
# RRSIG should be repeated four times and everything else should
# be omitted, and so on.
#
# if there is no second field (i.e., no pipe symbol appears in
# the line) , the default is to send all answers and signatures.
# if a pipe symbol exists but the second field is empty, then
# nothing is sent at all.
############################################################################
actions = []
rrs = []
def ctl_channel(msg):
global actions, rrs
msg = msg.splitlines().pop(0)
print ('received control message: %s' % msg)
msg = msg.split('|')
if len(msg) == 0:
return
actions = [x.strip() for x in msg[0].split(',')]
n = reduce(lambda n,act: (n + (2 if act == 'dname' else 1)), [0] + actions)
if len(msg) == 1:
rrs = []
for i in range(n):
for b in [False, True]:
rrs.append((i, b))
return
rlist = [x.strip() for x in msg[1].split(',')]
rrs = []
for item in rlist:
if item[0] == 's':
i = int(item[1:].strip()) - 1
if i > n:
print ('invalid index %d' + (i + 1))
continue
rrs.append((int(item[1:]) - 1, True))
else:
i = int(item) - 1
if i > n:
print ('invalid index %d' % (i + 1))
continue
rrs.append((i, False))
############################################################################
# Respond to a DNS query.
############################################################################
def create_response(msg):
m = dns.message.from_wire(msg)
qname = m.question[0].name.to_text()
labels = qname.lower().split('.')
wantsigs = True if m.ednsflags & dns.flags.DO else False
# get qtype
rrtype = m.question[0].rdtype
typename = dns.rdatatype.to_text(rrtype)
# for 'www.example.com.'...
# - name is 'www'
# - domain is 'example.com.'
# - sld is 'example'
# - tld is 'com.'
name = labels.pop(0)
domain = '.'.join(labels)
sld = labels.pop(0)
tld = '.'.join(labels)
print ('query: ' + qname + '/' + typename)
print ('domain: ' + domain)
# default answers, depending on QTYPE.
# currently only A, AAAA, TXT and NS are supported.
ttl = 86400
additionalA = '10.53.0.4'
additionalAAAA = 'fd92:7065:b8e:ffff::4'
if typename == 'A':
final = '10.53.0.4'
elif typename == 'AAAA':
final = 'fd92:7065:b8e:ffff::4'
elif typename == 'TXT':
final = 'Some\ text\ here'
elif typename == 'NS':
domain = qname
final = ('ns1.%s' % domain)
else:
final = None
# RRSIG rdata - won't validate but will exercise response parsing
t = datetime.now()
delta = timedelta(30)
t1 = t - delta
t2 = t + delta
inception=t1.strftime('%Y%m%d000000')
expiry=t2.strftime('%Y%m%d000000')
sigdata='OCXH2De0yE4NMTl9UykvOsJ4IBGs/ZIpff2rpaVJrVG7jQfmj50otBAp A0Zo7dpBU4ofv0N/F2Ar6LznCncIojkWptEJIAKA5tHegf/jY39arEpO cevbGp6DKxFhlkLXNcw7k9o7DSw14OaRmgAjXdTFbrl4AiAa0zAttFko Tso='
# construct answer set.
answers = []
sigs = []
curdom = domain
curname = name
i = 0
for action in actions:
if name <> 'test':
continue
if action == 'xname':
owner = curname + '.' + curdom
newname = 'cname%d' % i
i += 1
newdom = 'domain%d.%s' % (i, tld)
i += 1
target = newname + '.' + newdom
print ('add external CNAME %s to %s' % (owner, target))
answers.append(dns.rrset.from_text(owner, ttl, IN, CNAME, target))
rrsig = 'CNAME 5 3 %d %s %s 12345 %s %s' % \
(ttl, expiry, inception, domain, sigdata)
print ('add external RRISG(CNAME) %s to %s' % (owner, target))
sigs.append(dns.rrset.from_text(owner, ttl, IN, RRSIG, rrsig))
curname = newname
curdom = newdom
continue
if action == 'cname':
owner = curname + '.' + curdom
newname = 'cname%d' % i
target = newname + '.' + curdom
i += 1
print ('add CNAME %s to %s' % (owner, target))
answers.append(dns.rrset.from_text(owner, ttl, IN, CNAME, target))
rrsig = 'CNAME 5 3 %d %s %s 12345 %s %s' % \
(ttl, expiry, inception, domain, sigdata)
print ('add RRSIG(CNAME) %s to %s' % (owner, target))
sigs.append(dns.rrset.from_text(owner, ttl, IN, RRSIG, rrsig))
curname = newname
continue
if action == 'dname':
owner = curdom
newdom = 'domain%d.%s' % (i, tld)
i += 1
print ('add DNAME %s to %s' % (owner, newdom))
answers.append(dns.rrset.from_text(owner, ttl, IN, DNAME, newdom))
rrsig = 'DNAME 5 3 %d %s %s 12345 %s %s' % \
(ttl, expiry, inception, domain, sigdata)
print ('add RRSIG(DNAME) %s to %s' % (owner, newdom))
sigs.append(dns.rrset.from_text(owner, ttl, IN, RRSIG, rrsig))
owner = curname + '.' + curdom
target = curname + '.' + newdom
print ('add synthesized CNAME %s to %s' % (owner, target))
answers.append(dns.rrset.from_text(owner, ttl, IN, CNAME, target))
rrsig = 'CNAME 5 3 %d %s %s 12345 %s %s' % \
(ttl, expiry, inception, domain, sigdata)
print ('add synthesized RRSIG(CNAME) %s to %s' % (owner, target))
sigs.append(dns.rrset.from_text(owner, ttl, IN, RRSIG, rrsig))
curdom = newdom
continue
# now add the final answer
owner = curname + '.' + curdom
answers.append(dns.rrset.from_text(owner, ttl, IN, rrtype, final))
rrsig = '%s 5 3 %d %s %s 12345 %s %s' % \
(typename, ttl, expiry, inception, domain, sigdata)
sigs.append(dns.rrset.from_text(owner, ttl, IN, RRSIG, rrsig))
# prepare the response and convert to wire format
r = dns.message.make_response(m)
if name <> 'test':
r.answer.append(answers[-1])
if wantsigs:
r.answer.append(sigs[-1])
else:
for (i, sig) in rrs:
if sig and not wantsigs:
continue
elif sig:
r.answer.append(sigs[i])
else:
r.answer.append(answers[i])
if typename != 'NS':
r.authority.append(dns.rrset.from_text(domain, ttl, IN, "NS",
("ns1.%s" % domain)))
r.additional.append(dns.rrset.from_text(('ns1.%s' % domain), 86400,
IN, A, additionalA))
r.additional.append(dns.rrset.from_text(('ns1.%s' % domain), 86400,
IN, AAAA, additionalAAAA))
r.flags |= dns.flags.AA
r.use_edns()
return r.to_wire()
def sigterm(signum, frame):
print ("Shutting down now...")
os.remove('ans.pid')
running = 0
############################################################################
# Main
#
# Set up responder and control channel, open the pid file, and start
# the main loop, listening for queries on the query channel or commands
# on the control channel and acting on them.
############################################################################
ip4 = "10.53.0.4"
ip6 = "fd92:7065:b8e:ffff::4"
sock = 5300
query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
query4_socket.bind((ip4, sock))
query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
query6_socket.bind((ip6, sock))
ctrl_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ctrl_socket.bind((ip4, sock + 1))
ctrl_socket.listen(5)
signal.signal(signal.SIGTERM, sigterm)
# Unbuffered ouput ensures we can always check current status in ans.run.
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
f = open('ans.pid', 'w')
pid = os.getpid()
print (pid, file=f)
f.close()
running = 1
print ("Listening on %s port %d" % (ip4, sock))
print ("Listening on %s port %d" % (ip6, sock))
print ("Control channel on %s port %d" % (ip4, sock + 1))
print ("Ctrl-c to quit")
input = [query4_socket, query6_socket, ctrl_socket]
while running:
try:
inputready, outputready, exceptready = select.select(input, [], [])
except select.error as e:
break
except socket.error as e:
break
except KeyboardInterrupt:
break
for s in inputready:
if s == ctrl_socket:
# Handle control channel input
conn, addr = s.accept()
print ("Control channel connected")
while True:
msg = conn.recv(65535)
if not msg:
break
ctl_channel(msg)
conn.close()
if s == query4_socket or s == query6_socket:
print ("Query received on %s" %
(ip4 if s == query4_socket else ip6))
# Handle incoming queries
msg = s.recvfrom(65535)
rsp = create_response(msg[0])
if rsp:
s.sendto(rsp, msg[1])
if not running:
break
#!/bin/sh
#
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
rm -f dig.out.* named*.pid
rm -f */named.memstats */named.recursing */named.lock */named.run */ans.run
rm -f ns2/K* ns2/dsset-* ns2/example.db.signed
/*
* Copyright (C) 2011, 2016 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* $Id: named.conf,v 1.2 2011/03/18 21:14:19 fdupont Exp $ */
// NS1
controls { /* empty */ };
options {
......@@ -22,9 +18,7 @@ options {
listen-on-v6 { none; };
recursion no;
notify yes;
dnssec-enable yes;
};
zone "." {
type master;
file "root.db";
};
zone "." { type master; file "root.db"; };
; Copyright (C) 2011, 2016, 2017 Internet Systems Consortium, Inc. ("ISC")
; Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
$TTL 300
. IN SOA gson.nominum.com. a.root.servers.nil. (
2000042100 ; serial
. IN SOA root.domain.nil a.root.servers.nil. (
2016012800 ; serial
600 ; refresh
600 ; retry
1200 ; expire
......@@ -20,3 +20,23 @@ ns2.example. A 10.53.0.2
example.broken. NS ns3.example.broken.
ns3.example.broken. A 10.53.0.3
domain0.nil. NS ns2.domain0.nil
domain1.nil. NS ns2.domain0.nil
domain2.nil. NS ns2.domain0.nil
domain3.nil. NS ns2.domain0.nil
domain4.nil. NS ns2.domain0.nil
domain5.nil. NS ns2.domain0.nil
domain6.nil. NS ns2.domain0.nil
domain7.nil. NS ns2.domain0.nil
domain8.nil. NS ns2.domain0.nil
domain9.nil. NS ns2.domain0.nil
ns2.domain0.nil. A 10.53.0.2
ns2.domain0.nil. AAAA fd92:7065:b8e:ffff::2
domain.nil. NS ns4.domain.nil
ns4.domain.nil. A 10.53.0.4
ns4.domain.nil. AAAA fd92:7065:b8e:ffff::4
domain. NS ns4.domain.
ns4.domain. A 10.53.0.4
; Copyright (C) 2011, 2016, 2017 Internet Systems Consortium, Inc. ("ISC")
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
$TTL 300 ; 5 minutes
@ IN SOA mname1. . (
2000042407 ; serial
20 ; refresh (20 seconds)
20 ; retry (20 seconds)
1814400 ; expire (3 weeks)
3600 ; minimum (1 hour)
)
NS ns2
ns2 A 10.53.0.2
a.short A 10.0.0.1
short-dname DNAME short
a.longlonglonglonglonglonglonglonglonglonglonglonglong A 10.0.0.2
long-dname DNAME longlonglonglonglonglonglonglonglonglonglonglonglong
toolong-dname DNAME longlonglonglonglonglonglonglonglonglonglonglonglong
cname CNAME a.cnamedname
cnamedname DNAME target
a.target A 10.0.0.3
; CNAME to delegation
; (unsigned delegations, external and internal)
sub5 NS ns5.sub5
ns5.sub5 A 10.53.0.5
a CNAME a.sub5
sub2 NS ns2.sub2
ns2.sub2 A 10.53.0.2
b CNAME b.sub2
; (signed delegation, external and internal)
; note: these DS records are fake and will not validate; we're only
; testing that the resolver handles their presence in a reply correctly
signed-sub5 NS ns5.sub5
signed-sub5 DS 44137 8 2 1CB4F54E0B4F4F85109143113A3C679716A2377D86EB0907846A03FB 0C0A3927
c CNAME c.signed-sub5
signed-sub2 NS ns2.sub2
signed-sub2 DS 44137 8 2 1CB4F54E0B4F4F85109143113A3C679716A2377D86EB0907846A03FB 0C0A3927
d CNAME d.signed-sub2
; long CNAME loop
loop CNAME goop
goop CNAME boop
boop CNAME soup
soup CNAME gump
gump CNAME bump
bump CNAME lump
lump CNAME rump
rump CNAME romp
romp CNAME bomp
bomp CNAME stomp
stomp CNAME clomp
clomp CNAME clump
clump CNAME hunk
hunk CNAME hank
hank CNAME bank
bank CNAME wank
wank CNAME woop
woop CNAME loop
; Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
;
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
@ 86400 SOA ns2.nil. hostmaster.ns2.nil. 0 1 1 1 1
@ 86400 NS ns2.nil.
ns2 86400 A 10.53.0.2
@ 86400 A 1.2.3.4
@ 86400 AAAA 1:2:3::4
* 86400 A 1.2.3.4
* 86400 AAAA 1:2:3::4
/*
* Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// NS2
controls { /* empty */ };
options {
query-source address 10.53.0.2;
notify-source 10.53.0.2;
transfer-source 10.53.0.2;
port 5300;
pid-file "named.pid";
listen-on { 10.53.0.2; };
listen-on-v6 { none; };
recursion no;
notify yes;
};
zone "example" {
type master;
file "example.db.signed";
allow-update { any; };
};
zone "sub2.example" {
type master;
file "sub.db";
};
zone "signed-sub2.example" {
type master;
file "sub.db";
};
zone "domain0.nil" { type master; file "generic.db"; };
zone "domain1.nil" { type master; file "generic.db"; };
zone "domain2.nil" { type master; file "generic.db"; };
zone "domain3.nil" { type master; file "generic.db"; };
zone "domain4.nil" { type master; file "generic.db"; };
zone "domain5.nil" { type master; file "generic.db"; };
zone "domain6.nil" { type master; file "generic.db"; };
zone "domain7.nil" { type master; file "generic.db"; };
zone "domain8.nil" { type master; file "generic.db"; };
zone "domain9.nil" { type master; file "generic.db"; };
#!/bin/sh -e
#
# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SYSTEMTESTTOP=../..
. $SYSTEMTESTTOP/conf.sh
zone=example.
zonefile=example.db
ksk=`$KEYGEN -q -a RSASHA256 -b 2048 -fk -r $RANDFILE $zone`
zsk=`$KEYGEN -q -a RSASHA256 -b 1024 -r $RANDFILE $zone`
$SIGNER -S -r $RANDFILE -o $zone example.db > /dev/null 2>&1
; Copyright (C) 2011, 2016, 2017 Internet Systems Consortium, Inc. ("ISC")
; Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
;