]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add constant-time DNS name predicates
authorEvan Hunt <each@isc.org>
Thu, 30 Apr 2026 07:33:28 +0000 (00:33 -0700)
committerOndřej Surý <ondrej@isc.org>
Fri, 14 Aug 2026 07:32:29 +0000 (09:32 +0200)
Determining whether a DNS name is the root or contains a non-root label
does not require walking the whole name. Add constant-time predicates
and a semantic patch for equivalent label-count and equality checks.

cocci/dns_name_isroot.spatch [new file with mode: 0644]
lib/dns/include/dns/name.h
tests/dns/name_test.c

diff --git a/cocci/dns_name_isroot.spatch b/cocci/dns_name_isroot.spatch
new file mode 100644 (file)
index 0000000..1cf712a
--- /dev/null
@@ -0,0 +1,47 @@
+@@
+expression E;
+identifier ROOT =~ "^dns_rootname$";
+@@
+
+- dns_name_equal(E, ROOT)
++ dns_name_isroot(E)
+
+@@
+expression E;
+identifier ROOT =~ "^dns_rootname$";
+@@
+
+- dns_name_equal(ROOT, E)
++ dns_name_isroot(E)
+
+@@
+expression E;
+identifier ROOT =~ "^dns_rootname$";
+@@
+
+- dns_name_compare(E, ROOT) == 0
++ dns_name_isroot(E)
+
+@@
+expression E;
+identifier ROOT =~ "^dns_rootname$";
+@@
+
+- dns_name_compare(ROOT, E) == 0
++ dns_name_isroot(E)
+
+@@
+constant ONE =~ "^1[uUlL]*$";
+expression E;
+@@
+
+- dns_name_countlabels(E) <= ONE
++ !dns_name_belowroot(E)
+
+@@
+constant ONE =~ "^1[uUlL]*$";
+expression E;
+@@
+
+- dns_name_countlabels(E) > ONE
++ dns_name_belowroot(E)
index 60506f73e4e86893b7b03fd16d916ec8542bcb0e..57df242f071b6f9a2d17b2942298d01baa51d40e 100644 (file)
@@ -1378,3 +1378,30 @@ dns_name_israd(const dns_name_t *name, const dns_name_t *rad);
  * Requires:
  * \li 'name' to be valid.
  */
+
+static inline bool
+dns_name_isroot(const dns_name_t *name) {
+       REQUIRE(DNS_NAME_VALID(name));
+
+       return name->length == 1 && name->ndata[0] == 0;
+}
+/*%<
+ * Return whether 'name' is the root name.
+ *
+ * Requires:
+ * \li 'name' to be valid.
+ */
+
+static inline bool
+dns_name_belowroot(const dns_name_t *name) {
+       REQUIRE(DNS_NAME_VALID(name));
+
+       return name->length != 0 && name->ndata[0] != 0;
+}
+/*%<
+ * Return whether 'name' is below root.  It checks whether there's at least one
+ * non-root label.
+ *
+ * Requires:
+ * \li 'name' to be valid.
+ */
index 151e4c4c3268092d5dff3bf2861ac45e50043623..7d6e5d0f4a57500419aa124520ac13d3cf5d449d 100644 (file)
@@ -688,6 +688,64 @@ ISC_RUN_TEST_IMPL(isabsolute) {
        }
 }
 
+/* dns_name_isroot */
+ISC_RUN_TEST_IMPL(isroot) {
+       struct {
+               const char *namestr;
+               bool expect;
+       } testcases[] = {
+               { ".", true },
+               { "x", false },
+               { "x.", false },
+               { "x.y.", false },
+       };
+       dns_name_t empty;
+
+       UNUSED(state);
+
+       dns_name_init(&empty);
+       assert_false(dns_name_isroot(&empty));
+
+       for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+               dns_fixedname_t fixed;
+               dns_name_t *name = dns_fixedname_initname(&fixed);
+               isc_result_t result = dns_name_fromstring(
+                       name, testcases[i].namestr, NULL, 0, NULL);
+
+               assert_int_equal(result, ISC_R_SUCCESS);
+               assert_int_equal(dns_name_isroot(name), testcases[i].expect);
+       }
+}
+
+/* dns_name_belowroot */
+ISC_RUN_TEST_IMPL(belowroot) {
+       struct {
+               const char *namestr;
+               bool expect;
+       } testcases[] = {
+               { ".", false },
+               { "x", true },
+               { "x.", true },
+               { "x.y.", true },
+       };
+       dns_name_t empty;
+
+       UNUSED(state);
+
+       dns_name_init(&empty);
+       assert_false(dns_name_belowroot(&empty));
+
+       for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) {
+               dns_fixedname_t fixed;
+               dns_name_t *name = dns_fixedname_initname(&fixed);
+               isc_result_t result = dns_name_fromstring(
+                       name, testcases[i].namestr, NULL, 0, NULL);
+
+               assert_int_equal(result, ISC_R_SUCCESS);
+               assert_int_equal(dns_name_belowroot(name), testcases[i].expect);
+       }
+}
+
 /* dns_name_hash */
 ISC_RUN_TEST_IMPL(hash) {
        struct {
@@ -1179,6 +1237,7 @@ ISC_RUN_TEST_IMPL(benchmark) {
 #endif /* DNS_BENCHMARK_TESTS */
 
 ISC_TEST_LIST_START
+ISC_TEST_ENTRY(belowroot)
 ISC_TEST_ENTRY(buffer)
 ISC_TEST_ENTRY(collision)
 ISC_TEST_ENTRY(compression)
@@ -1192,6 +1251,7 @@ ISC_TEST_ENTRY(hash)
 ISC_TEST_ENTRY(init)
 ISC_TEST_ENTRY(invalidate)
 ISC_TEST_ENTRY(isabsolute)
+ISC_TEST_ENTRY(isroot)
 ISC_TEST_ENTRY(issubdomain)
 ISC_TEST_ENTRY(istat)
 ISC_TEST_ENTRY(maxlabels)