--- /dev/null
+#!/usr/bin/env python3
+
+'''Test of zone transfers over QUIC.'''
+
+from dnstest.test import Test
+from dnstest.utils import *
+import random
+
+t = Test(quic=True, tsig=True) # TSIG needed to skip weaker ACL rules
+
+master = t.server("knot")
+slave = t.server("knot")
+rnd_zones = t.zone_rnd(1, records=50) + \
+ t.zone_rnd(1, records=500) + \
+ t.zone_rnd(1, records=1000)
+zones = t.zone(".") + rnd_zones
+
+t.link(zones, master, slave)
+
+for z in rnd_zones:
+ master.dnssec(z).enable = True
+
+MSG_DENIED_NOTIFY = "ACL, denied, action notify"
+MSG_DENIED_TRANSFER = "ACL, denied, action transfer"
+MSG_RMT_NOTAUTH = "server responded with error 'NOTAUTH'"
+MSG_RMT_BADCERT = "failed (unknown certificate key)"
+MSG_TSIG_ERROR = "failed (failed to verify TSIG)"
+
+def check_error(server, msg):
+ for i in range(10):
+ if server.log_search(msg):
+ return
+ t.sleep(1)
+ detail_log("Failed log expected")
+ set_err("MISSING ERROR LOG")
+
+def upd_check_zones(master, slave, zones, prev_serials):
+ for z in rnd_zones:
+ master.random_ddns(z, allow_empty=False)
+ serials = slave.zones_wait(zones, prev_serials)
+ t.xfr_diff(master, slave, zones, prev_serials)
+ return serials
+
+try:
+ t.start()
+except Failed as e:
+ stderr = t.out_dir + "/" + str(e).split("'")[1] + "/stderr"
+ with open(stderr) as fstderr:
+ if "QUIC" in fstderr.readline():
+ raise Skip("QUIC support not compiled in")
+ raise e
+
+# Check initial AXFR without cert-key-based authentication
+serials = master.zones_wait(zones)
+slave.zones_wait(zones, serials, equal=True, greater=False)
+if slave.log_search(MSG_TSIG_ERROR):
+ set_err("INCOMPLETE TRANSFER")
+t.xfr_diff(master, slave, zones)
+
+# Check master not authenticated due to bad cert-key
+master.cert_key = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY="
+slave.gen_confile()
+slave.reload()
+master.ctl("zone-notify")
+check_error(master, MSG_RMT_NOTAUTH)
+check_error(slave, MSG_DENIED_NOTIFY)
+slave.ctl("zone-retransfer")
+check_error(slave, MSG_RMT_BADCERT)
+
+# Check IXFR with cert-key-based authenticated master
+master.fill_cert_key()
+slave.gen_confile()
+slave.reload()
+serials = upd_check_zones(master, slave, rnd_zones, serials)
+
+# Check slave not authenticated due to bad cert-key
+slave.cert_key = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY="
+master.gen_confile()
+master.reload()
+master.ctl("zone-notify")
+check_error(master, MSG_RMT_BADCERT)
+slave.ctl("zone-retransfer")
+check_error(slave, MSG_RMT_NOTAUTH)
+check_error(master, MSG_DENIED_TRANSFER)
+
+# Check IXFR with cert-key-based authenticated slave
+slave.fill_cert_key()
+master.gen_confile()
+master.reload()
+serials = upd_check_zones(master, slave, rnd_zones, serials)
+
+t.end()
self.addr = None
self.addr_extra = list()
self.port = 53 # Needed for keymgr when port not yet generated
+ self.quic_port = None
+ self.cert_key = str()
self.udp_workers = None
self.bg_workers = None
self.fixed_port = False
f.write(self.get_config())
f.close()
+ def fill_cert_key(self):
+ try:
+ out = check_output([self.control_bin] + self.ctl_params + ["status", "cert-key"],
+ stderr=open(self.dir + "/call.err", mode="a"))
+ key = out.rstrip().decode('ascii')
+ if key != "-":
+ self.cert_key = key
+ except CalledProcessError as e:
+ raise Failed("Can't get certificate key, server='%s', ret='%i'" %
+ (self.name, e.returncode))
+
def dig(self, rname, rtype, rclass="IN", udp=None, serial=None, timeout=None,
tries=3, flags="", bufsize=None, edns=None, nsid=False, dnssec=False,
log_no_sep=False, tsig=None, addr=None, source=None):
s.item_str("listen", "%s" % self.addr)
else:
s.item_str("listen", "%s@%s" % (self.addr, self.port))
+ if self.quic_port:
+ s.item_str("listen-quic", "%s@%s" % (self.addr, self.quic_port))
if self.udp_workers:
s.item_str("udp-workers", self.udp_workers)
if self.bg_workers:
s.begin("remote")
have_remote = True
s.id_item("id", master.name)
- if master.addr.startswith("/"):
- s.item_str("address", "%s" % master.addr)
+ if master.quic_port:
+ s.item_str("address", "%s@%s" % (master.addr, master.quic_port))
+ s.item_str("quic", "on")
+ if master.cert_key:
+ s.item_str("cert-key", master.cert_key)
else:
- s.item_str("address", "%s@%s" % (master.addr, master.port))
+ if master.addr.startswith("/"):
+ s.item_str("address", "%s" % master.addr)
+ else:
+ s.item_str("address", "%s@%s" % (master.addr, master.port))
if self.tsig:
s.item_str("key", self.tsig.name)
if master.no_xfr_edns:
s.begin("remote")
have_remote = True
s.id_item("id", slave.name)
- if slave.addr.startswith("/"):
- s.item_str("address", "%s" % slave.addr)
+ if slave.quic_port:
+ s.item_str("address", "%s@%s" % (slave.addr, slave.quic_port))
+ s.item_str("quic", "on")
+ if slave.cert_key:
+ s.item_str("cert-key", slave.cert_key)
else:
- s.item_str("address", "%s@%s" % (slave.addr, slave.port))
+ if slave.addr.startswith("/"):
+ s.item_str("address", "%s" % slave.addr)
+ else:
+ s.item_str("address", "%s@%s" % (slave.addr, slave.port))
if self.tsig:
s.item_str("key", self.tsig.name)
servers.add(slave.name)
s.item_str("address", master.addr)
if master.tsig:
s.item_str("key", master.tsig.name)
+ if master.cert_key:
+ s.item_str("cert-key", master.cert_key)
s.item("action", "notify")
servers.add(master.name)
for slave in z.slaves:
s.item_str("address", slave.addr)
if slave.tsig:
s.item_str("key", slave.tsig.name)
+ if slave.cert_key:
+ s.item_str("cert-key", slave.cert_key)
s.item("action", "[transfer, update]")
servers.add(slave.name)
s.end()
rel_time = time.time()
start_time = 0
- def __init__(self, address=None, tsig=None, stress=True):
+ def __init__(self, address=None, tsig=None, stress=True, quic=False):
if not os.path.exists(Context().out_dir):
raise Exception("Output directory doesn't exist")
self.out_dir = Context().out_dir
self.data_dir = Context().test_dir + "/data/"
self.zones_dir = self.out_dir + "/zones/"
+ self.quic = quic
if address == 4 or address == 6:
self.addr = Test.LOCAL_ADDR[address]
server.port = self._gen_port()
server.ctlport = self._gen_port()
+ server.quic_port = self._gen_port() if self.quic else None
for server in self.servers:
server.gen_confile()