From: Evan Hunt Date: Thu, 30 Apr 2026 07:33:28 +0000 (-0700) Subject: Add constant-time DNS name predicates X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=38120cd79e8cadb7b0bb717c97ead732cfa0f9db;p=thirdparty%2Fbind9.git Add constant-time DNS name predicates 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. --- diff --git a/cocci/dns_name_isroot.spatch b/cocci/dns_name_isroot.spatch new file mode 100644 index 00000000000..1cf712a2d63 --- /dev/null +++ b/cocci/dns_name_isroot.spatch @@ -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) diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h index 60506f73e4e..57df242f071 100644 --- a/lib/dns/include/dns/name.h +++ b/lib/dns/include/dns/name.h @@ -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. + */ diff --git a/tests/dns/name_test.c b/tests/dns/name_test.c index 151e4c4c326..7d6e5d0f4a5 100644 --- a/tests/dns/name_test.c +++ b/tests/dns/name_test.c @@ -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)