diff options
Diffstat (limited to 'bin/tests/system/qmin')
-rwxr-xr-x | bin/tests/system/qmin/ans2/ans.py | 401 | ||||
-rwxr-xr-x | bin/tests/system/qmin/ans3/ans.py | 274 | ||||
-rwxr-xr-x | bin/tests/system/qmin/ans4/ans.py | 320 | ||||
-rw-r--r-- | bin/tests/system/qmin/clean.sh | 20 | ||||
-rw-r--r-- | bin/tests/system/qmin/ns1/named.conf.in | 32 | ||||
-rw-r--r-- | bin/tests/system/qmin/ns1/root.db | 41 | ||||
-rw-r--r-- | bin/tests/system/qmin/ns5/named.conf.in | 43 | ||||
-rw-r--r-- | bin/tests/system/qmin/ns6/named.conf.in | 43 | ||||
-rw-r--r-- | bin/tests/system/qmin/ns7/named.conf.in | 51 | ||||
-rw-r--r-- | bin/tests/system/qmin/setup.sh | 19 | ||||
-rwxr-xr-x | bin/tests/system/qmin/tests.sh | 537 | ||||
-rw-r--r-- | bin/tests/system/qmin/tests_sh_qmin.py | 18 |
12 files changed, 1799 insertions, 0 deletions
diff --git a/bin/tests/system/qmin/ans2/ans.py b/bin/tests/system/qmin/ans2/ans.py new file mode 100755 index 0000000..1994ff3 --- /dev/null +++ b/bin/tests/system/qmin/ans2/ans.py @@ -0,0 +1,401 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# 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 https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +from __future__ import print_function +import os +import sys +import signal +import socket +import select +from datetime import datetime, timedelta +import time +import functools + +import dns, dns.message, dns.query, dns.flags +from dns.rdatatype import * +from dns.rdataclass import * +from dns.rcode import * +from dns.name import * + + +# Log query to file +def logquery(type, qname): + with open("qlog", "a") as f: + f.write("%s %s\n", type, qname) + + +def endswith(domain, labels): + return domain.endswith("." + labels) or domain == labels + + +############################################################################ +# Respond to a DNS query. +# For good. it serves: +# ns2.good. IN A 10.53.0.2 +# zoop.boing.good. NS ns3.good. +# ns3.good. IN A 10.53.0.3 +# too.many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.good. A 192.0.2.2 +# it responds properly (with NODATA empty response) to non-empty terminals +# +# For slow. it works the same as for good., but each response is delayed by 400 milliseconds +# +# For bad. it works the same as for good., but returns NXDOMAIN to non-empty terminals +# +# For ugly. it works the same as for good., but returns garbage to non-empty terminals +# +# For 1.0.0.2.ip6.arpa it serves +# 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. IN PTR nee.com. +# 8.2.6.0.1.0.0.2.ip6.arpa IN NS ns3.good +# 1.0.0.2.ip6.arpa. IN NS ns2.good +# ip6.arpa. IN NS ns2.good +# +# For stale. it serves: +# a.b. NS ns.a.b.stale. +# ns.a.b.stale. IN A 10.53.0.3 +# b. NS ns.b.stale. +# ns.b.stale. IN A 10.53.0.4 +############################################################################ +def create_response(msg): + m = dns.message.from_wire(msg) + qname = m.question[0].name.to_text() + lqname = qname.lower() + labels = lqname.split(".") + + # get qtype + rrtype = m.question[0].rdtype + typename = dns.rdatatype.to_text(rrtype) + if typename == "A" or typename == "AAAA": + typename = "ADDR" + bad = False + ugly = False + slow = False + + # log this query + with open("query.log", "a") as f: + f.write("%s %s\n" % (typename, lqname)) + print("%s %s" % (typename, lqname), end=" ") + + r = dns.message.make_response(m) + r.set_rcode(NOERROR) + + if endswith(lqname, "1.0.0.2.ip6.arpa."): + # Direct query - give direct answer + if endswith(lqname, "8.2.6.0.1.0.0.2.ip6.arpa."): + # Delegate to ns3 + r.authority.append( + dns.rrset.from_text( + "8.2.6.0.1.0.0.2.ip6.arpa.", 60, IN, NS, "ns3.good." + ) + ) + r.additional.append( + dns.rrset.from_text("ns3.good.", 60, IN, A, "10.53.0.3") + ) + elif ( + lqname + == "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa." + and rrtype == PTR + ): + # Direct query - give direct answer + r.answer.append( + dns.rrset.from_text( + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.", + 1, + IN, + PTR, + "nee.com.", + ) + ) + r.flags |= dns.flags.AA + elif lqname == "1.0.0.2.ip6.arpa." and rrtype == NS: + # NS query at the apex + r.answer.append( + dns.rrset.from_text("1.0.0.2.ip6.arpa.", 30, IN, NS, "ns2.good.") + ) + r.flags |= dns.flags.AA + elif endswith( + "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.", + lqname, + ): + # NODATA answer + r.authority.append( + dns.rrset.from_text( + "1.0.0.2.ip6.arpa.", + 30, + IN, + SOA, + "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1", + ) + ) + else: + # NXDOMAIN + r.authority.append( + dns.rrset.from_text( + "1.0.0.2.ip6.arpa.", + 30, + IN, + SOA, + "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1", + ) + ) + r.set_rcode(NXDOMAIN) + return r + elif endswith(lqname, "ip6.arpa."): + if lqname == "ip6.arpa." and rrtype == NS: + # NS query at the apex + r.answer.append(dns.rrset.from_text("ip6.arpa.", 30, IN, NS, "ns2.good.")) + r.flags |= dns.flags.AA + elif endswith("1.0.0.2.ip6.arpa.", lqname): + # NODATA answer + r.authority.append( + dns.rrset.from_text( + "ip6.arpa.", + 30, + IN, + SOA, + "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1", + ) + ) + else: + # NXDOMAIN + r.authority.append( + dns.rrset.from_text( + "ip6.arpa.", + 30, + IN, + SOA, + "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1", + ) + ) + r.set_rcode(NXDOMAIN) + return r + elif endswith(lqname, "stale."): + if endswith(lqname, "a.b.stale."): + # Delegate to ns.a.b.stale. + r.authority.append( + dns.rrset.from_text("a.b.stale.", 2, IN, NS, "ns.a.b.stale.") + ) + r.additional.append( + dns.rrset.from_text("ns.a.b.stale.", 2, IN, A, "10.53.0.3") + ) + elif endswith(lqname, "b.stale."): + # Delegate to ns.b.stale. + r.authority.append( + dns.rrset.from_text("b.stale.", 2, IN, NS, "ns.b.stale.") + ) + r.additional.append( + dns.rrset.from_text("ns.b.stale.", 2, IN, A, "10.53.0.4") + ) + elif lqname == "stale." and rrtype == NS: + # NS query at the apex. + r.answer.append(dns.rrset.from_text("stale.", 2, IN, NS, "ns2.stale.")) + r.flags |= dns.flags.AA + elif lqname == "stale." and rrtype == SOA: + # SOA query at the apex. + r.answer.append( + dns.rrset.from_text( + "stale.", 2, IN, SOA, "ns2.stale. hostmaster.stale. 1 2 3 4 5" + ) + ) + r.flags |= dns.flags.AA + elif lqname == "stale.": + # NODATA answer + r.authority.append( + dns.rrset.from_text( + "stale.", 2, IN, SOA, "ns2.stale. hostmaster.arpa. 1 2 3 4 5" + ) + ) + else: + # NXDOMAIN + r.authority.append( + dns.rrset.from_text( + "stale.", 2, IN, SOA, "ns2.stale. hostmaster.arpa. 1 2 3 4 5" + ) + ) + r.set_rcode(NXDOMAIN) + return r + elif endswith(lqname, "bad."): + bad = True + suffix = "bad." + lqname = lqname[:-4] + elif endswith(lqname, "ugly."): + ugly = True + suffix = "ugly." + lqname = lqname[:-5] + elif endswith(lqname, "good."): + suffix = "good." + lqname = lqname[:-5] + elif endswith(lqname, "slow."): + slow = True + suffix = "slow." + lqname = lqname[:-5] + elif endswith(lqname, "fwd."): + suffix = "fwd." + lqname = lqname[:-4] + else: + r.set_rcode(REFUSED) + return r + + # Good/bad/ugly differs only in how we treat non-empty terminals + if endswith(lqname, "zoop.boing."): + r.authority.append( + dns.rrset.from_text("zoop.boing." + suffix, 1, IN, NS, "ns3." + suffix) + ) + elif ( + lqname == "many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z." + and rrtype == A + ): + r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, A, "192.0.2.2")) + r.flags |= dns.flags.AA + elif lqname == "" and rrtype == NS: + r.answer.append(dns.rrset.from_text(suffix, 30, IN, NS, "ns2." + suffix)) + r.flags |= dns.flags.AA + elif lqname == "ns2." and rrtype == A: + r.answer.append(dns.rrset.from_text("ns2." + suffix, 30, IN, A, "10.53.0.2")) + r.flags |= dns.flags.AA + elif lqname == "ns2." and rrtype == AAAA: + r.answer.append( + dns.rrset.from_text("ns2." + suffix, 30, IN, AAAA, "fd92:7065:b8e:ffff::2") + ) + r.flags |= dns.flags.AA + elif lqname == "ns3." and rrtype == A: + r.answer.append(dns.rrset.from_text("ns3." + suffix, 30, IN, A, "10.53.0.3")) + r.flags |= dns.flags.AA + elif lqname == "ns3." and rrtype == AAAA: + r.answer.append( + dns.rrset.from_text("ns3." + suffix, 30, IN, AAAA, "fd92:7065:b8e:ffff::3") + ) + r.flags |= dns.flags.AA + elif lqname == "ns4." and rrtype == A: + r.answer.append(dns.rrset.from_text("ns4." + suffix, 30, IN, A, "10.53.0.4")) + r.flags |= dns.flags.AA + elif lqname == "ns4." and rrtype == AAAA: + r.answer.append( + dns.rrset.from_text("ns4." + suffix, 30, IN, AAAA, "fd92:7065:b8e:ffff::4") + ) + r.flags |= dns.flags.AA + elif lqname == "a.bit.longer.ns.name." and rrtype == A: + r.answer.append( + dns.rrset.from_text("a.bit.longer.ns.name." + suffix, 1, IN, A, "10.53.0.4") + ) + r.flags |= dns.flags.AA + elif lqname == "a.bit.longer.ns.name." and rrtype == AAAA: + r.answer.append( + dns.rrset.from_text( + "a.bit.longer.ns.name." + suffix, 1, IN, AAAA, "fd92:7065:b8e:ffff::4" + ) + ) + r.flags |= dns.flags.AA + else: + r.authority.append( + dns.rrset.from_text( + suffix, + 1, + IN, + SOA, + "ns2." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1", + ) + ) + if bad or not ( + endswith("icky.icky.icky.ptang.zoop.boing.", lqname) + or endswith( + "many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.", + lqname, + ) + or endswith("a.bit.longer.ns.name.", lqname) + ): + r.set_rcode(NXDOMAIN) + if ugly: + r.set_rcode(FORMERR) + if slow: + time.sleep(0.2) + return r + + +def sigterm(signum, frame): + print("Shutting down now...") + os.remove("ans.pid") + running = False + sys.exit(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.2" +ip6 = "fd92:7065:b8e:ffff::2" + +try: + port = int(os.environ["PORT"]) +except: + port = 5300 + +query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +query4_socket.bind((ip4, port)) + +havev6 = True +try: + query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + try: + query6_socket.bind((ip6, port)) + except: + query6_socket.close() + havev6 = False +except: + havev6 = False + +signal.signal(signal.SIGTERM, sigterm) + +f = open("ans.pid", "w") +pid = os.getpid() +print(pid, file=f) +f.close() + +running = True + +print("Listening on %s port %d" % (ip4, port)) +if havev6: + print("Listening on %s port %d" % (ip6, port)) +print("Ctrl-c to quit") + +if havev6: + input = [query4_socket, query6_socket] +else: + input = [query4_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 == query4_socket or s == query6_socket: + print( + "Query received on %s" % (ip4 if s == query4_socket else ip6), end=" " + ) + # Handle incoming queries + msg = s.recvfrom(65535) + rsp = create_response(msg[0]) + if rsp: + print(dns.rcode.to_text(rsp.rcode())) + s.sendto(rsp.to_wire(), msg[1]) + else: + print("NO RESPONSE") + if not running: + break diff --git a/bin/tests/system/qmin/ans3/ans.py b/bin/tests/system/qmin/ans3/ans.py new file mode 100755 index 0000000..079c3d2 --- /dev/null +++ b/bin/tests/system/qmin/ans3/ans.py @@ -0,0 +1,274 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# 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 https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +from __future__ import print_function +import os +import sys +import signal +import socket +import select +from datetime import datetime, timedelta +import time +import functools + +import dns, dns.message, dns.query, dns.flags +from dns.rdatatype import * +from dns.rdataclass import * +from dns.rcode import * +from dns.name import * + + +# Log query to file +def logquery(type, qname): + with open("qlog", "a") as f: + f.write("%s %s\n", type, qname) + + +def endswith(domain, labels): + return domain.endswith("." + labels) or domain == labels + + +############################################################################ +# Respond to a DNS query. +# For good. it serves: +# zoop.boing.good. NS ns3.good. +# icky.ptang.zoop.boing.good. NS a.bit.longer.ns.name.good. +# it responds properly (with NODATA empty response) to non-empty terminals +# +# For slow. it works the same as for good., but each response is delayed by 400 milliseconds +# +# For bad. it works the same as for good., but returns NXDOMAIN to non-empty terminals +# +# For ugly. it works the same as for good., but returns garbage to non-empty terminals +# +# For stale. it serves: +# a.b.stale. IN TXT peekaboo (resolver did not do qname minimization) +############################################################################ +def create_response(msg): + m = dns.message.from_wire(msg) + qname = m.question[0].name.to_text() + lqname = qname.lower() + labels = lqname.split(".") + suffix = "" + + # get qtype + rrtype = m.question[0].rdtype + typename = dns.rdatatype.to_text(rrtype) + if typename == "A" or typename == "AAAA": + typename = "ADDR" + bad = False + ugly = False + slow = False + + # log this query + with open("query.log", "a") as f: + f.write("%s %s\n" % (typename, lqname)) + print("%s %s" % (typename, lqname), end=" ") + + r = dns.message.make_response(m) + r.set_rcode(NOERROR) + + ip6req = False + + if endswith(lqname, "bad."): + bad = True + suffix = "bad." + lqname = lqname[:-4] + elif endswith(lqname, "ugly."): + ugly = True + suffix = "ugly." + lqname = lqname[:-5] + elif endswith(lqname, "good."): + suffix = "good." + lqname = lqname[:-5] + elif endswith(lqname, "slow."): + slow = True + suffix = "slow." + lqname = lqname[:-5] + elif endswith(lqname, "8.2.6.0.1.0.0.2.ip6.arpa."): + ip6req = True + elif endswith(lqname, "a.b.stale."): + if lqname == "a.b.stale.": + if rrtype == TXT: + # Direct query. + r.answer.append(dns.rrset.from_text(lqname, 1, IN, TXT, "peekaboo")) + r.flags |= dns.flags.AA + elif rrtype == NS: + # NS a.b. + r.answer.append(dns.rrset.from_text(lqname, 1, IN, NS, "ns.a.b.stale.")) + r.additional.append( + dns.rrset.from_text("ns.a.b.stale.", 1, IN, A, "10.53.0.3") + ) + r.flags |= dns.flags.AA + elif rrtype == SOA: + # SOA a.b. + r.answer.append( + dns.rrset.from_text( + lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5" + ) + ) + r.flags |= dns.flags.AA + else: + # NODATA. + r.authority.append( + dns.rrset.from_text( + lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5" + ) + ) + else: + r.authority.append( + dns.rrset.from_text( + lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5" + ) + ) + r.set_rcode(NXDOMAIN) + # NXDOMAIN. + return r + else: + r.set_rcode(REFUSED) + return r + + # Good/bad differs only in how we treat non-empty terminals + if lqname == "zoop.boing." and rrtype == NS: + r.answer.append( + dns.rrset.from_text(lqname + suffix, 1, IN, NS, "ns3." + suffix) + ) + r.flags |= dns.flags.AA + elif endswith(lqname, "icky.ptang.zoop.boing."): + r.authority.append( + dns.rrset.from_text( + "icky.ptang.zoop.boing." + suffix, + 1, + IN, + NS, + "a.bit.longer.ns.name." + suffix, + ) + ) + elif endswith("icky.ptang.zoop.boing.", lqname): + r.authority.append( + dns.rrset.from_text( + "zoop.boing." + suffix, + 1, + IN, + SOA, + "ns3." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1", + ) + ) + if bad: + r.set_rcode(NXDOMAIN) + if ugly: + r.set_rcode(FORMERR) + elif endswith(lqname, "zoop.boing."): + r.authority.append( + dns.rrset.from_text( + "zoop.boing." + suffix, + 1, + IN, + SOA, + "ns3." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1", + ) + ) + r.set_rcode(NXDOMAIN) + elif ip6req: + r.authority.append( + dns.rrset.from_text( + "1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", 60, IN, NS, "ns4.good." + ) + ) + r.additional.append(dns.rrset.from_text("ns4.good.", 60, IN, A, "10.53.0.4")) + else: + r.set_rcode(REFUSED) + + if slow: + time.sleep(0.4) + return r + + +def sigterm(signum, frame): + print("Shutting down now...") + os.remove("ans.pid") + running = False + sys.exit(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.3" +ip6 = "fd92:7065:b8e:ffff::3" + +try: + port = int(os.environ["PORT"]) +except: + port = 5300 + +query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +query4_socket.bind((ip4, port)) + +havev6 = True +try: + query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + try: + query6_socket.bind((ip6, port)) + except: + query6_socket.close() + havev6 = False +except: + havev6 = False + +signal.signal(signal.SIGTERM, sigterm) + +f = open("ans.pid", "w") +pid = os.getpid() +print(pid, file=f) +f.close() + +running = True + +print("Listening on %s port %d" % (ip4, port)) +if havev6: + print("Listening on %s port %d" % (ip6, port)) +print("Ctrl-c to quit") + +if havev6: + input = [query4_socket, query6_socket] +else: + input = [query4_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 == query4_socket or s == query6_socket: + print( + "Query received on %s" % (ip4 if s == query4_socket else ip6), end=" " + ) + # Handle incoming queries + msg = s.recvfrom(65535) + rsp = create_response(msg[0]) + if rsp: + print(dns.rcode.to_text(rsp.rcode())) + s.sendto(rsp.to_wire(), msg[1]) + else: + print("NO RESPONSE") + if not running: + break diff --git a/bin/tests/system/qmin/ans4/ans.py b/bin/tests/system/qmin/ans4/ans.py new file mode 100755 index 0000000..f3d00c3 --- /dev/null +++ b/bin/tests/system/qmin/ans4/ans.py @@ -0,0 +1,320 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# 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 https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +from __future__ import print_function +import os +import sys +import signal +import socket +import select +from datetime import datetime, timedelta +import time +import functools + +import dns, dns.message, dns.query, dns.flags +from dns.rdatatype import * +from dns.rdataclass import * +from dns.rcode import * +from dns.name import * + + +# Log query to file +def logquery(type, qname): + with open("qlog", "a") as f: + f.write("%s %s\n", type, qname) + + +def endswith(domain, labels): + return domain.endswith("." + labels) or domain == labels + + +############################################################################ +# Respond to a DNS query. +# For good. it serves: +# icky.ptang.zoop.boing.good. NS a.bit.longer.ns.name. +# icky.icky.icky.ptang.zoop.boing.good. A 192.0.2.1 +# more.icky.icky.icky.ptang.zoop.boing.good. A 192.0.2.2 +# it responds properly (with NODATA empty response) to non-empty terminals +# +# For slow. it works the same as for good., but each response is delayed by 400 milliseconds +# +# For bad. it works the same as for good., but returns NXDOMAIN to non-empty terminals +# +# For ugly. it works the same as for good., but returns garbage to non-empty terminals +# +# For stale. it serves: +# a.b.stale. IN TXT hooray (resolver did do qname minimization) +############################################################################ +def create_response(msg): + m = dns.message.from_wire(msg) + qname = m.question[0].name.to_text() + lqname = qname.lower() + labels = lqname.split(".") + suffix = "" + + # get qtype + rrtype = m.question[0].rdtype + typename = dns.rdatatype.to_text(rrtype) + if typename == "A" or typename == "AAAA": + typename = "ADDR" + bad = False + slow = False + ugly = False + + # log this query + with open("query.log", "a") as f: + f.write("%s %s\n" % (typename, lqname)) + print("%s %s" % (typename, lqname), end=" ") + + r = dns.message.make_response(m) + r.set_rcode(NOERROR) + + ip6req = False + + if endswith(lqname, "bad."): + bad = True + suffix = "bad." + lqname = lqname[:-4] + elif endswith(lqname, "ugly."): + ugly = True + suffix = "ugly." + lqname = lqname[:-5] + elif endswith(lqname, "good."): + suffix = "good." + lqname = lqname[:-5] + elif endswith(lqname, "slow."): + slow = True + suffix = "slow." + lqname = lqname[:-5] + elif endswith(lqname, "1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa."): + ip6req = True + elif endswith(lqname, "b.stale."): + if lqname == "a.b.stale.": + if rrtype == TXT: + # Direct query. + r.answer.append(dns.rrset.from_text(lqname, 1, IN, TXT, "hooray")) + r.flags |= dns.flags.AA + elif rrtype == NS: + # NS a.b. + r.answer.append(dns.rrset.from_text(lqname, 1, IN, NS, "ns.a.b.stale.")) + r.additional.append( + dns.rrset.from_text("ns.a.b.stale.", 1, IN, A, "10.53.0.3") + ) + r.flags |= dns.flags.AA + elif rrtype == SOA: + # SOA a.b. + r.answer.append( + dns.rrset.from_text( + lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5" + ) + ) + r.flags |= dns.flags.AA + else: + # NODATA. + r.authority.append( + dns.rrset.from_text( + lqname, 1, IN, SOA, "a.b.stale. hostmaster.a.b.stale. 1 2 3 4 5" + ) + ) + elif lqname == "b.stale.": + if rrtype == NS: + # NS b. + r.answer.append(dns.rrset.from_text(lqname, 1, IN, NS, "ns.b.stale.")) + r.additional.append( + dns.rrset.from_text("ns.b.stale.", 1, IN, A, "10.53.0.4") + ) + r.flags |= dns.flags.AA + elif rrtype == SOA: + # SOA b. + r.answer.append( + dns.rrset.from_text( + lqname, 1, IN, SOA, "b.stale. hostmaster.b.stale. 1 2 3 4 5" + ) + ) + r.flags |= dns.flags.AA + else: + # NODATA. + r.authority.append( + dns.rrset.from_text( + lqname, 1, IN, SOA, "b.stale. hostmaster.b.stale. 1 2 3 4 5" + ) + ) + else: + r.authority.append( + dns.rrset.from_text( + lqname, 1, IN, SOA, "b.stale. hostmaster.b.stale. 1 2 3 4 5" + ) + ) + r.set_rcode(NXDOMAIN) + # NXDOMAIN. + return r + else: + r.set_rcode(REFUSED) + return r + + # Good/bad differs only in how we treat non-empty terminals + if lqname == "icky.icky.icky.ptang.zoop.boing." and rrtype == A: + r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, A, "192.0.2.1")) + r.flags |= dns.flags.AA + elif lqname == "more.icky.icky.icky.ptang.zoop.boing." and rrtype == A: + r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, A, "192.0.2.2")) + r.flags |= dns.flags.AA + elif lqname == "icky.ptang.zoop.boing." and rrtype == NS: + r.answer.append( + dns.rrset.from_text( + lqname + suffix, 1, IN, NS, "a.bit.longer.ns.name." + suffix + ) + ) + r.flags |= dns.flags.AA + elif endswith(lqname, "icky.ptang.zoop.boing."): + r.authority.append( + dns.rrset.from_text( + "icky.ptang.zoop.boing." + suffix, + 1, + IN, + SOA, + "ns2." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1", + ) + ) + if bad or not endswith("more.icky.icky.icky.ptang.zoop.boing.", lqname): + r.set_rcode(NXDOMAIN) + if ugly: + r.set_rcode(FORMERR) + elif ip6req: + r.flags |= dns.flags.AA + if ( + lqname + == "test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa." + and rrtype == TXT + ): + r.answer.append( + dns.rrset.from_text( + "test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", + 1, + IN, + TXT, + "long_ip6_name", + ) + ) + elif endswith( + "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", + lqname, + ): + # NODATA answer + r.authority.append( + dns.rrset.from_text( + "1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", + 60, + IN, + SOA, + "ns4.good. hostmaster.arpa. 2018050100 120 30 320 16", + ) + ) + else: + # NXDOMAIN + r.authority.append( + dns.rrset.from_text( + "1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa.", + 60, + IN, + SOA, + "ns4.good. hostmaster.arpa. 2018050100 120 30 320 16", + ) + ) + r.set_rcode(NXDOMAIN) + else: + r.set_rcode(REFUSED) + + if slow: + time.sleep(0.4) + return r + + +def sigterm(signum, frame): + print("Shutting down now...") + os.remove("ans.pid") + running = False + sys.exit(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" + +try: + port = int(os.environ["PORT"]) +except: + port = 5300 + +query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +query4_socket.bind((ip4, port)) + +havev6 = True +try: + query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + try: + query6_socket.bind((ip6, port)) + except: + query6_socket.close() + havev6 = False +except: + havev6 = False + +signal.signal(signal.SIGTERM, sigterm) + +f = open("ans.pid", "w") +pid = os.getpid() +print(pid, file=f) +f.close() + +running = True + +print("Listening on %s port %d" % (ip4, port)) +if havev6: + print("Listening on %s port %d" % (ip6, port)) +print("Ctrl-c to quit") + +if havev6: + input = [query4_socket, query6_socket] +else: + input = [query4_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 == query4_socket or s == query6_socket: + print( + "Query received on %s" % (ip4 if s == query4_socket else ip6), end=" " + ) + # Handle incoming queries + msg = s.recvfrom(65535) + rsp = create_response(msg[0]) + if rsp: + print(dns.rcode.to_text(rsp.rcode())) + s.sendto(rsp.to_wire(), msg[1]) + else: + print("NO RESPONSE") + if not running: + break diff --git a/bin/tests/system/qmin/clean.sh b/bin/tests/system/qmin/clean.sh new file mode 100644 index 0000000..52c38e6 --- /dev/null +++ b/bin/tests/system/qmin/clean.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# 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 https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +rm -f ns*/named.conf +rm -f */named.memstats +rm -f */named.run */named.run.prev +rm -f dig.out.* +rm -f ns*/named.lock +rm -f ans*/query.log* +rm -f query*.log diff --git a/bin/tests/system/qmin/ns1/named.conf.in b/bin/tests/system/qmin/ns1/named.conf.in new file mode 100644 index 0000000..e366cca --- /dev/null +++ b/bin/tests/system/qmin/ns1/named.conf.in @@ -0,0 +1,32 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS1 + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify yes; + dnssec-validation no; +}; + +zone "." { + type primary; + file "root.db"; +}; diff --git a/bin/tests/system/qmin/ns1/root.db b/bin/tests/system/qmin/ns1/root.db new file mode 100644 index 0000000..325f607 --- /dev/null +++ b/bin/tests/system/qmin/ns1/root.db @@ -0,0 +1,41 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; 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 https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 20 +. IN SOA wpk.isc.org. a.root.servers.nil. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 2 ; minimum + ) +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 +ip6.arpa. NS ns2.good. + +good. NS ns2.good. +ns2.good. A 10.53.0.2 + +bad. NS ns2.bad. +ns2.bad. A 10.53.0.2 + +slow NS ns2.slow. +ns2.slow. A 10.53.0.2 + +ugly. NS ns2.ugly. +ns2.ugly. A 10.53.0.2 + +fwd. NS ns2.fwd. +ns2.fwd. A 10.53.0.2 + +$TTL 2 +stale. NS ns2.stale. +ns2.stale. A 10.53.0.2 diff --git a/bin/tests/system/qmin/ns5/named.conf.in b/bin/tests/system/qmin/ns5/named.conf.in new file mode 100644 index 0000000..d84d818 --- /dev/null +++ b/bin/tests/system/qmin/ns5/named.conf.in @@ -0,0 +1,43 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS5 + +options { + query-source address 10.53.0.5; + notify-source 10.53.0.5; + transfer-source 10.53.0.5; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.5; }; + listen-on-v6 { none; }; + recursion yes; + qname-minimization disabled; + querylog yes; + resolver-query-timeout 30000; # 30 seconds + dnssec-validation no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.5 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; diff --git a/bin/tests/system/qmin/ns6/named.conf.in b/bin/tests/system/qmin/ns6/named.conf.in new file mode 100644 index 0000000..828973c --- /dev/null +++ b/bin/tests/system/qmin/ns6/named.conf.in @@ -0,0 +1,43 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS6 + +options { + query-source address 10.53.0.6; + notify-source 10.53.0.6; + transfer-source 10.53.0.6; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.6; }; + listen-on-v6 { none; }; + recursion yes; + qname-minimization strict; + querylog yes; + resolver-query-timeout 30000; # 30 seconds + dnssec-validation no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; diff --git a/bin/tests/system/qmin/ns7/named.conf.in b/bin/tests/system/qmin/ns7/named.conf.in new file mode 100644 index 0000000..8ca3275 --- /dev/null +++ b/bin/tests/system/qmin/ns7/named.conf.in @@ -0,0 +1,51 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS7 + +options { + query-source address 10.53.0.7; + notify-source 10.53.0.7; + transfer-source 10.53.0.7; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.7; }; + listen-on-v6 { none; }; + recursion yes; + qname-minimization relaxed; + querylog yes; + resolver-query-timeout 30000; # 30 seconds + dnssec-validation no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.7 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "fwd." IN { + type forward; + forwarders { + 10.53.0.2; + }; + forward only; +}; diff --git a/bin/tests/system/qmin/setup.sh b/bin/tests/system/qmin/setup.sh new file mode 100644 index 0000000..5825b3d --- /dev/null +++ b/bin/tests/system/qmin/setup.sh @@ -0,0 +1,19 @@ +#!/bin/sh -e + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# 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 https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +. ../conf.sh + +copy_setports ns1/named.conf.in ns1/named.conf +copy_setports ns5/named.conf.in ns5/named.conf +copy_setports ns6/named.conf.in ns6/named.conf +copy_setports ns7/named.conf.in ns7/named.conf diff --git a/bin/tests/system/qmin/tests.sh b/bin/tests/system/qmin/tests.sh new file mode 100755 index 0000000..b8d3128 --- /dev/null +++ b/bin/tests/system/qmin/tests.sh @@ -0,0 +1,537 @@ +#!/bin/sh + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# 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 https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +set -e + +. ../conf.sh + +DIGOPTS="-p ${PORT}" +RNDCCMD="$RNDC -c ../common/rndc.conf -p ${CONTROLPORT} -s" +CLEANQL="rm -f ans*/query.log" +status=0 +n=0 + +n=$((n+1)) +echo_i "query for .good is not minimized when qname-minimization is off ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.5 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.5 > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "icky.icky.icky.ptang.zoop.boing.good. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 +sleep 1 +cat << __EOF | diff ans2/query.log - > /dev/null || ret=1 +ADDR icky.icky.icky.ptang.zoop.boing.good. +ADDR ns3.good. +ADDR ns3.good. +ADDR a.bit.longer.ns.name.good. +ADDR a.bit.longer.ns.name.good. +__EOF +echo "ADDR icky.icky.icky.ptang.zoop.boing.good." | diff ans3/query.log - > /dev/null || ret=1 +echo "ADDR icky.icky.icky.ptang.zoop.boing.good." | diff ans4/query.log - > /dev/null || ret=1 +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .bad is not minimized when qname-minimization is off ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.5 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.5 > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "icky.icky.icky.ptang.zoop.boing.bad. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 +sleep 1 +cat << __EOF | diff ans2/query.log - > /dev/null || ret=1 +ADDR icky.icky.icky.ptang.zoop.boing.bad. +ADDR ns3.bad. +ADDR ns3.bad. +ADDR a.bit.longer.ns.name.bad. +ADDR a.bit.longer.ns.name.bad. +__EOF +echo "ADDR icky.icky.icky.ptang.zoop.boing.bad." | diff ans3/query.log - > /dev/null || ret=1 +echo "ADDR icky.icky.icky.ptang.zoop.boing.bad." | diff ans4/query.log - > /dev/null || ret=1 +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .slow is not minimized when qname-minimization is off ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.5 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.slow. @10.53.0.5 > dig.out.test$n +sleep 5 +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "icky.icky.icky.ptang.zoop.boing.slow. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 +sleep 1 +cat << __EOF | diff ans2/query.log - > /dev/null || ret=1 +ADDR icky.icky.icky.ptang.zoop.boing.slow. +ADDR ns3.slow. +ADDR ns3.slow. +ADDR a.bit.longer.ns.name.slow. +ADDR a.bit.longer.ns.name.slow. +__EOF +echo "ADDR icky.icky.icky.ptang.zoop.boing.slow." | diff ans3/query.log - > /dev/null || ret=1 +echo "ADDR icky.icky.icky.ptang.zoop.boing.slow." | diff ans4/query.log - > /dev/null || ret=1 +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .ugly is not minimized when qname-minimization is off ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.5 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.ugly. @10.53.0.5 > dig.out.test$n +sleep 5 +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "icky.icky.icky.ptang.zoop.boing.ugly. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 +sleep 1 +cat << __EOF | diff ans2/query.log - > /dev/null || ret=1 +ADDR icky.icky.icky.ptang.zoop.boing.ugly. +ADDR ns3.ugly. +ADDR ns3.ugly. +ADDR a.bit.longer.ns.name.ugly. +ADDR a.bit.longer.ns.name.ugly. +__EOF +echo "ADDR icky.icky.icky.ptang.zoop.boing.ugly." | diff ans3/query.log - > /dev/null || ret=1 +echo "ADDR icky.icky.icky.ptang.zoop.boing.ugly." | diff ans4/query.log - > /dev/null || ret=1 +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .good is properly minimized when qname-minimization is in strict mode ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.6 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.6 > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "icky.icky.icky.ptang.zoop.boing.good. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR a.bit.longer.ns.name.good. +ADDR a.bit.longer.ns.name.good. +ADDR ns2.good. +ADDR ns3.good. +ADDR ns3.good. +NS boing.good. +NS good. +NS zoop.boing.good. +__EOF +cat << __EOF | diff ans3/query.log - > /dev/null || ret=1 +NS zoop.boing.good. +NS ptang.zoop.boing.good. +NS icky.ptang.zoop.boing.good. +__EOF +cat << __EOF | diff ans4/query.log - > /dev/null || ret=1 +NS icky.ptang.zoop.boing.good. +NS icky.icky.ptang.zoop.boing.good. +ADDR icky.icky.icky.ptang.zoop.boing.good. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .good is properly minimized when qname-minimization is in relaxed mode ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.7 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.7 > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "icky.icky.icky.ptang.zoop.boing.good. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR a.bit.longer.ns.name.good. +ADDR a.bit.longer.ns.name.good. +ADDR ns2.good. +ADDR ns3.good. +ADDR ns3.good. +NS boing.good. +NS zoop.boing.good. +__EOF +cat << __EOF | diff ans3/query.log - > /dev/null || ret=1 +NS ptang.zoop.boing.good. +NS icky.ptang.zoop.boing.good. +__EOF +cat << __EOF | diff ans4/query.log - > /dev/null || ret=1 +NS icky.icky.ptang.zoop.boing.good. +ADDR icky.icky.icky.ptang.zoop.boing.good. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .bad fails when qname-minimization is in strict mode ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.6 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.6 > dig.out.test$n +grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR ns2.bad. +NS bad. +NS boing.bad. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .bad succeeds when qname-minimization is in relaxed mode ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.7 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.7 > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "icky.icky.icky.ptang.zoop.boing.bad. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR a.bit.longer.ns.name.bad. +ADDR a.bit.longer.ns.name.bad. +ADDR icky.icky.icky.ptang.zoop.boing.bad. +ADDR ns2.bad. +ADDR ns3.bad. +ADDR ns3.bad. +NS boing.bad. +__EOF +cat << __EOF | diff ans3/query.log - > /dev/null || ret=1 +ADDR icky.icky.icky.ptang.zoop.boing.bad. +__EOF +cat << __EOF | diff ans4/query.log - > /dev/null || ret=1 +ADDR icky.icky.icky.ptang.zoop.boing.bad. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .ugly fails when qname-minimization is in strict mode ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.6 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.ugly. @10.53.0.6 > dig.out.test$n +grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR ns2.ugly. +NS boing.ugly. +NS ugly. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) +$RNDCCMD 10.53.0.6 flush + +n=$((n+1)) +echo_i "query for .ugly succeeds when qname-minimization is in relaxed mode ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.7 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.ugly. @10.53.0.7 > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "icky.icky.icky.ptang.zoop.boing.ugly. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR a.bit.longer.ns.name.ugly. +ADDR a.bit.longer.ns.name.ugly. +ADDR icky.icky.icky.ptang.zoop.boing.ugly. +ADDR ns2.ugly. +ADDR ns3.ugly. +ADDR ns3.ugly. +NS boing.ugly. +__EOF +echo "ADDR icky.icky.icky.ptang.zoop.boing.ugly." | diff ans3/query.log - > /dev/null || ret=1 +echo "ADDR icky.icky.icky.ptang.zoop.boing.ugly." | diff ans4/query.log - > /dev/null || ret=1 +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) +$RNDCCMD 10.53.0.7 flush + +n=$((n+1)) +echo_i "information that minimization was unsuccessful for .ugly is logged in relaxed mode ($n)" +ret=0 +wait_for_log 5 "success resolving 'icky.icky.icky.ptang.zoop.boing.ugly/A' after disabling qname minimization" ns7/named.run > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .slow is properly minimized when qname-minimization is on ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.6 flush +$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.slow. @10.53.0.6 > dig.out.test$n +sleep 5 +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "icky.icky.icky.ptang.zoop.boing.slow. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR a.bit.longer.ns.name.slow. +ADDR a.bit.longer.ns.name.slow. +ADDR ns2.slow. +ADDR ns3.slow. +ADDR ns3.slow. +NS boing.slow. +NS slow. +NS zoop.boing.slow. +__EOF +cat << __EOF | diff ans3/query.log - > /dev/null || ret=1 +NS zoop.boing.slow. +NS ptang.zoop.boing.slow. +NS icky.ptang.zoop.boing.slow. +__EOF +cat << __EOF | diff ans4/query.log - > /dev/null || ret=1 +NS icky.ptang.zoop.boing.slow. +NS icky.icky.ptang.zoop.boing.slow. +ADDR icky.icky.icky.ptang.zoop.boing.slow. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .ip6.arpa succeeds and skips on proper boundaries when qname-minimization is on ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.6 flush +$DIG $DIGOPTS -x 2001:4f8::1 @10.53.0.6 > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. 1 IN PTR nee.com." dig.out.test$n > /dev/null || ret=1 +sleep 1 +grep -v ADDR ans2/query.log > ans2/query.log.trimmed +cat << __EOF | diff ans2/query.log.trimmed - > /dev/null || ret=1 +NS 1.0.0.2.ip6.arpa. +NS 8.f.4.0.1.0.0.2.ip6.arpa. +NS 0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. +NS 0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. +NS 0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. +PTR 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for multiple label name skips after 7th label ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.6 flush +$DIG $DIGOPTS more.icky.icky.icky.ptang.zoop.boing.good. @10.53.0.6 > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "more.icky.icky.icky.ptang.zoop.boing.good. 1 IN A 192.0.2.2" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR a.bit.longer.ns.name.good. +ADDR a.bit.longer.ns.name.good. +ADDR ns2.good. +ADDR ns3.good. +ADDR ns3.good. +NS boing.good. +NS good. +NS zoop.boing.good. +__EOF +cat << __EOF | diff ans3/query.log - > /dev/null || ret=1 +NS zoop.boing.good. +NS ptang.zoop.boing.good. +NS icky.ptang.zoop.boing.good. +__EOF +# There's no NS icky.icky.icky.ptang.zoop.boing.good. query - we skipped it. +cat << __EOF | diff ans4/query.log - > /dev/null || ret=1 +NS icky.ptang.zoop.boing.good. +NS icky.icky.ptang.zoop.boing.good. +ADDR more.icky.icky.icky.ptang.zoop.boing.good. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "qname minimization is disabled when forwarding ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.7 flush +$DIG $DIGOPTS a.bit.longer.ns.name.fwd. @10.53.0.7 > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "a.bit.longer.ns.name.fwd. 1 IN A 10.53.0.4" dig.out.test$n >/dev/null || ret=1 +sleep 1 +cat << __EOF | diff ans2/query.log - > /dev/null || ret=1 +ADDR a.bit.longer.ns.name.fwd. +__EOF +for ans in ans2; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "qname minimization resolves unusual ip6.arpa. names ($n)" +ret=0 +$CLEANQL +$DIG $DIGOPTS test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. txt @10.53.0.7 > dig.out.test$n 2>&1 +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +# Expected output in dig.out.test$n: +# ;; ANSWER SECTION: +# test1.test2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.0.9.4.1.1.1.1.8.2.6.0.1.0.0.2.ip6.arpa. 1 IN TXT "long_ip6_name" +grep 'ip6\.arpa.*TXT.*long_ip6_name' dig.out.test$n > /dev/null || ret=1 +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +# Below are test cases for GL #2665: The QNAME minimization (if enabled) should +# also occur on the second query, after the RRsets have expired from cache. +# BIND will still have the entries in cache, but marked stale. These stale +# entries should not prevent the resolver from minimizing the QNAME. +# We query for the test domain a.b.stale. in all cases (QNAME minimization off, +# strict mode, and relaxed mode) and expect it to behave the same the second +# time when we have a stale delegation structure in cache. +n=$((n+1)) +echo_i "query for .stale is not minimized when qname-minimization is off ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.5 flush +$DIG $DIGOPTS @10.53.0.5 txt a.b.stale. > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "a\.b\.stale\..*1.*IN.*TXT.*peekaboo" dig.out.test$n > /dev/null || ret=1 +sleep 1 +echo "TXT a.b.stale." | diff ans2/query.log - > /dev/null || ret=1 +echo "TXT a.b.stale." | diff ans3/query.log - > /dev/null || ret=1 +test -f ans4/query.log && ret=1 +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .stale is properly minimized when qname-minimization is in strict mode ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.6 flush +$DIG $DIGOPTS @10.53.0.6 txt a.b.stale. > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR ns.b.stale. +ADDR ns2.stale. +NS b.stale. +NS stale. +__EOF +test -f ans3/query.log && ret=1 +sort ans4/query.log > ans4/query.log.sorted +cat << __EOF | diff ans4/query.log.sorted - > /dev/null || ret=1 +ADDR ns.b.stale. +NS b.stale. +TXT a.b.stale. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .stale is properly minimized when qname-minimization is in relaxed mode ($n)" +ret=0 +$CLEANQL +$RNDCCMD 10.53.0.7 flush +$DIG $DIGOPTS @10.53.0.7 txt a.b.stale. > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +ADDR ns.b.stale. +ADDR ns2.stale. +NS b.stale. +__EOF +test -f ans3/query.log && ret=1 +sort ans4/query.log > ans4/query.log.sorted +cat << __EOF | diff ans4/query.log.sorted - > /dev/null || ret=1 +ADDR ns.b.stale. +TXT a.b.stale. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +echo_i "sleep 2, allow entries in cache to go stale" +sleep 2 + +n=$((n+1)) +echo_i "query for .stale is not minimized when qname-minimization is off (stale cache) ($n)" +ret=0 +$CLEANQL +$DIG $DIGOPTS @10.53.0.5 txt a.b.stale. > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "a\.b\.stale\..*1.*IN.*TXT.*peekaboo" dig.out.test$n > /dev/null || ret=1 +sleep 1 +echo "TXT a.b.stale." | diff ans2/query.log - > /dev/null || ret=1 +echo "TXT a.b.stale." | diff ans3/query.log - > /dev/null || ret=1 +test -f ans4/query.log && ret=1 +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .stale is properly minimized when qname-minimization is in strict mode (stale cache) ($n)" +ret=0 +$CLEANQL +$DIG $DIGOPTS @10.53.0.6 txt a.b.stale. > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +NS b.stale. +NS stale. +__EOF +test -f ans3/query.log && ret=1 +sort ans4/query.log > ans4/query.log.sorted +cat << __EOF | diff ans4/query.log.sorted - > /dev/null || ret=1 +NS b.stale. +TXT a.b.stale. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "query for .stale is properly minimized when qname-minimization is in relaxed mode (stale cache) ($n)" +ret=0 +$CLEANQL +$DIG $DIGOPTS @10.53.0.7 txt a.b.stale. > dig.out.test$n +grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 +grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" dig.out.test$n > /dev/null || ret=1 +sleep 1 +sort ans2/query.log > ans2/query.log.sorted +cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 +NS b.stale. +__EOF +test -f ans3/query.log && ret=1 +sort ans4/query.log > ans4/query.log.sorted +cat << __EOF | diff ans4/query.log.sorted - > /dev/null || ret=1 +TXT a.b.stale. +__EOF +for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +echo_i "exit status: $status" +[ $status -eq 0 ] || exit 1 diff --git a/bin/tests/system/qmin/tests_sh_qmin.py b/bin/tests/system/qmin/tests_sh_qmin.py new file mode 100644 index 0000000..6077322 --- /dev/null +++ b/bin/tests/system/qmin/tests_sh_qmin.py @@ -0,0 +1,18 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# 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 https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +import pytest_custom_markers + + +# The qmin test is inherently unstable, see GL #904 for details. +@pytest_custom_markers.flaky(max_runs=3) +def test_qmin(run_tests_sh): + run_tests_sh() |