--- /dev/null
+#!/usr/bin/python3
+############################################################################
+# Copyright (C) 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 https://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+############################################################################
+
+"""
+Example property-based test for wildcard synthesis.
+Verifies that otherwise-empty zone with single wildcard record * A 192.0.2.1
+produces synthesized answers for <random_label>.test. A, and returns NODATA for
+<random_label>.test. when rdtype is not A.
+
+Limitations - untested properties:
+ - expansion works with multiple labels
+ - asterisk in qname does not cause expansion
+ - empty non-terminals prevent expansion
+ - or more generally any existing node prevents expansion
+ - DNSSEC record inclusion
+ - possibly others, see RFC 4592 and company
+ - content of authority & additional sections
+ - flags beyond RCODE
+ - special behavior of rdtypes like CNAME
+"""
+import pytest
+
+pytest.importorskip("dns")
+import dns.message
+import dns.name
+import dns.query
+import dns.rcode
+import dns.rdatatype
+
+pytest.importorskip("hypothesis")
+from hypothesis import given
+from hypothesis.strategies import binary, integers
+
+
+# labels of a zone with * A 192.0.2.1 wildcard
+WILDCARD_ZONE = ('allwild', 'test', '')
+WILDCARD_RDTYPE = dns.rdatatype.A
+WILDCARD_RDATA = '192.0.2.1'
+IPADDR = '10.53.0.1'
+TIMEOUT = 5 # seconds, just a sanity check
+
+
+# Helpers
+def is_nonexpanding_rdtype(rdtype):
+ """skip meta types to avoid weird rcodes caused by AXFR etc.; RFC 6895"""
+ return not(rdtype == WILDCARD_RDTYPE
+ or dns.rdatatype.is_metatype(rdtype) # known metatypes: OPT ...
+ or 128 <= rdtype <= 255) # unknown meta types
+
+
+def tcp_query(where, port, qname, qtype):
+ querymsg = dns.message.make_query(qname, qtype)
+ assert len(querymsg.question) == 1
+ return querymsg, dns.query.tcp(querymsg, where, port=port, timeout=TIMEOUT)
+
+
+def query(where, port, label, rdtype):
+ labels = (label, ) + WILDCARD_ZONE
+ qname = dns.name.Name(labels)
+ return tcp_query(where, port, qname, rdtype)
+
+
+# Tests
+@given(label=binary(min_size=1, max_size=63),
+ rdtype=integers(min_value=0, max_value=65535).filter(
+ is_nonexpanding_rdtype))
+def test_wildcard_rdtype_mismatch(label, rdtype, named_port):
+ """any label non-matching rdtype must result in to NODATA"""
+ check_answer_nodata(*query(IPADDR, named_port, label, rdtype))
+
+
+def check_answer_nodata(querymsg, answer):
+ assert querymsg.is_response(answer), str(answer)
+ assert answer.rcode() == dns.rcode.NOERROR, str(answer)
+ assert answer.answer == [], str(answer)
+
+
+@given(label=binary(min_size=1, max_size=63))
+def test_wildcard_match(label, named_port):
+ """any label with maching rdtype must result in wildcard data in answer"""
+ check_answer_noerror(*query(IPADDR, named_port, label, WILDCARD_RDTYPE))
+
+
+def check_answer_noerror(querymsg, answer):
+ assert querymsg.is_response(answer), str(answer)
+ assert answer.rcode() == dns.rcode.NOERROR, str(answer)
+ assert len(querymsg.question) == 1, str(answer)
+ expected_answer = [dns.rrset.from_text(
+ querymsg.question[0].name,
+ 300, # TTL, ignored by dnspython comparison
+ dns.rdataclass.IN,
+ WILDCARD_RDTYPE,
+ WILDCARD_RDATA)]
+ assert answer.answer == expected_answer, str(answer)
./bin/tests/system/views/setup.sh SH 2000,2001,2004,2007,2012,2014,2016,2017,2018,2019,2020,2021
./bin/tests/system/views/tests.sh SH 2000,2001,2004,2007,2012,2013,2014,2016,2018,2019,2020,2021
./bin/tests/system/wildcard/clean.sh SH 2012,2013,2014,2016,2018,2019,2020,2021
+./bin/tests/system/wildcard/conftest.py PYTHON 2021
./bin/tests/system/wildcard/ns1/sign.sh SH 2012,2013,2014,2016,2018,2019,2020,2021
./bin/tests/system/wildcard/setup.sh SH 2012,2014,2016,2017,2018,2019,2020,2021
+./bin/tests/system/wildcard/tests-wildcard.py PYTHON-BIN 2021
./bin/tests/system/wildcard/tests.sh SH 2012,2013,2016,2018,2019,2020,2021
./bin/tests/system/xfer/ans5/badkeydata X 2011,2018,2019,2020,2021
./bin/tests/system/xfer/ans5/badmessageid X 2020,2021