Commit 24197ff742 for bind
commit 24197ff74290f66fa1eeedc3fc6a65e354393017
Author: Alessio Podda <alessio@isc.org>
Date: Wed Oct 28 14:22:42 2026 +0100
Linear time zone options check
Make the check of zone options validity linear in the number of
zone options + zone template options present.
diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c
index 6beb56bda9..d13e8d92e1 100644
--- a/lib/isccfg/check.c
+++ b/lib/isccfg/check.c
@@ -3354,6 +3354,37 @@ get_zoneopt(const cfg_obj_t *opts1, const cfg_obj_t *opts2,
return result;
}
+typedef struct {
+ unsigned int ztype;
+ const char *typestr;
+ const char *znamestr;
+ const char *tmplname;
+} zone_options_check_t;
+
+static bool
+check_zone_option(char *key ISC_ATTR_UNUSED, unsigned int type,
+ isc_symvalue_t value, void *arg) {
+ const zone_options_check_t *check = arg;
+
+ if (cfg_clause_validforzone(type, check->ztype)) {
+ return false;
+ }
+
+ if (check->tmplname == NULL) {
+ cfg_obj_log(value.as_pointer, ISC_LOG_WARNING,
+ "option '%s' is not allowed in '%s' zone '%s'",
+ cfg_clause_as_string[type], check->typestr,
+ check->znamestr);
+ } else {
+ cfg_obj_log(value.as_pointer, ISC_LOG_WARNING,
+ "option '%s' is not allowed in '%s' zone '%s' "
+ "(referencing template '%s')",
+ cfg_clause_as_string[type], check->typestr,
+ check->znamestr, check->tmplname);
+ }
+ return false;
+}
+
isc_result_t
isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
const cfg_obj_t *config, isc_symtab_t *symtab,
@@ -3383,8 +3414,6 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
bool kasp_inlinesigning = false;
bool inline_signing = false;
bool check_keys = (flags & BIND_CHECK_KEYS) != 0;
- const void *clauses = NULL;
- const cfg_clausedef_t *option = NULL;
const char *kaspname = NULL;
const char *dir = NULL;
static const enum cfg_clause acls[] = {
@@ -3711,28 +3740,21 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
/*
* Check validity of the zone options.
*/
- option = cfg_map_firstclause(&cfg_type_zoneopts, &clauses, &i);
- while (option != NULL) {
- obj = NULL;
- bool topt = false;
- (void)cfg_map_get(zoptions, option->name, &obj);
- if (obj == NULL && toptions != NULL) {
- (void)cfg_map_get(toptions, option->name, &obj);
- topt = true;
- }
- if (obj != NULL &&
- !cfg_clause_validforzone(option->name, ztype))
- {
- cfg_obj_log(obj, ISC_LOG_WARNING,
- "option '%s' is not allowed "
- "in '%s' zone '%s'%s%s%s",
- cfg_clause_as_string[option->name], typestr,
- znamestr,
- topt ? " (referencing template '" : "",
- topt ? tmplname : "", topt ? "')" : "");
- result = ISC_R_FAILURE;
+ if (!cfg_zoneopts_validforzone(zoptions, toptions, ztype)) {
+ zone_options_check_t check = {
+ .ztype = ztype,
+ .typestr = typestr,
+ .znamestr = znamestr,
+ };
+
+ isc_symtab_foreach(zoptions->value.map->symtab,
+ check_zone_option, &check);
+ if (toptions != NULL) {
+ check.tmplname = tmplname;
+ isc_symtab_foreach(toptions->value.map->symtab,
+ check_zone_option, &check);
}
- option = cfg_map_nextclause(&cfg_type_zoneopts, &clauses, &i);
+ result = ISC_R_FAILURE;
}
/*
diff --git a/lib/isccfg/include/isccfg/grammar.h b/lib/isccfg/include/isccfg/grammar.h
index 1ce2a4d598..24639c778e 100644
--- a/lib/isccfg/include/isccfg/grammar.h
+++ b/lib/isccfg/include/isccfg/grammar.h
@@ -630,6 +630,11 @@ cfg_clause_validforzone(enum cfg_clause name, unsigned int ztype);
* Check whether an option is legal for the specified zone type.
*/
+bool
+cfg_zoneopts_validforzone(const cfg_obj_t *zoptions, const cfg_obj_t *toptions,
+ unsigned int ztype);
+/*%< Check whether all zone and template options allow the zone type. */
+
void
cfg_print_zonegrammar(const unsigned int zonetype, unsigned int flags,
void (*f)(void *closure, const char *text, int textlen),
diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c
index 37e6974225..4559c70c13 100644
--- a/lib/isccfg/namedconf.c
+++ b/lib/isccfg/namedconf.c
@@ -21,8 +21,10 @@
#include <isc/lex.h>
#include <isc/log.h>
#include <isc/mem.h>
+#include <isc/once.h>
#include <isc/result.h>
#include <isc/string.h>
+#include <isc/symtab.h>
#include <isc/util.h>
#include <dns/ttl.h>
@@ -4320,36 +4322,62 @@ cmp_clause(const void *ap, const void *bp) {
cfg_clause_as_string[b->name]);
}
-bool
-cfg_clause_validforzone(enum cfg_clause name, unsigned int ztype) {
- const cfg_clausedef_t *clause;
- bool valid = false;
+static unsigned int zone_flags[CFG_CLAUSE__COUNT];
+static isc_once_t zone_flags_once = ISC_ONCE_INITIALIZER;
- for (clause = zone_clauses; clause->name != CFG_CLAUSE__NONE; clause++)
- {
- if ((clause->flags & ztype) == 0 || clause->name != name) {
- continue;
- }
- valid = true;
- }
- for (clause = zone_only_clauses; clause->name != CFG_CLAUSE__NONE;
- clause++)
+static void
+initialize_zone_flags(void) {
+ const void *clauses = NULL;
+ unsigned int i = 0;
+
+ for (const cfg_clausedef_t *clause =
+ cfg_map_firstclause(&cfg_type_zoneopts, &clauses, &i);
+ clause != NULL;
+ clause = cfg_map_nextclause(&cfg_type_zoneopts, &clauses, &i))
{
- if ((clause->flags & ztype) == 0 || clause->name != name) {
- continue;
- }
- valid = true;
+ INSIST(zone_flags[clause->name] == 0);
+ INSIST(clause->flags != 0);
+ zone_flags[clause->name] = clause->flags;
}
- for (clause = non_template_clauses; clause->name != CFG_CLAUSE__NONE;
- clause++)
- {
- if ((clause->flags & ztype) == 0 || clause->name != name) {
+}
+
+bool
+cfg_clause_validforzone(enum cfg_clause name, unsigned int ztype) {
+ REQUIRE(name > CFG_CLAUSE__NONE && name < CFG_CLAUSE__COUNT);
+
+ isc_once_do(&zone_flags_once, initialize_zone_flags);
+ return (zone_flags[name] & ztype) != 0;
+}
+
+static bool
+intersect_zone_flags(char *key ISC_ATTR_UNUSED, unsigned int type,
+ isc_symvalue_t value ISC_ATTR_UNUSED, void *arg) {
+ unsigned int *allowed = arg;
+
+ *allowed &= zone_flags[type];
+ return false;
+}
+
+bool
+cfg_zoneopts_validforzone(const cfg_obj_t *zoptions, const cfg_obj_t *toptions,
+ unsigned int ztype) {
+ const cfg_obj_t *maps[] = { zoptions, toptions };
+ unsigned int allowed = ~0U;
+
+ REQUIRE(zoptions != NULL);
+
+ isc_once_do(&zone_flags_once, initialize_zone_flags);
+ for (size_t i = 0; i < ARRAY_SIZE(maps); i++) {
+ if (maps[i] == NULL) {
continue;
}
- valid = true;
+ REQUIRE(cfg_obj_ismap(maps[i]));
+
+ isc_symtab_foreach(maps[i]->value.map->symtab,
+ intersect_zone_flags, &allowed);
}
- return valid;
+ return (allowed & ztype) != 0;
}
void
diff --git a/tests/isccfg/grammar_test.c b/tests/isccfg/grammar_test.c
index 13520248a8..03a7fd415d 100644
--- a/tests/isccfg/grammar_test.c
+++ b/tests/isccfg/grammar_test.c
@@ -153,8 +153,62 @@ ISC_RUN_TEST_IMPL(query_source_print_addr) {
"options { query-source-v6 ::1; }; ");
}
+ISC_RUN_TEST_IMPL(zone_options_validity) {
+ const struct {
+ const char *zone;
+ const char *template;
+ unsigned int ztype;
+ bool valid;
+ } cases[] = {
+ { "{}", NULL, CFG_ZONE_PRIMARY, true },
+ { "{ type primary; file \"example.db\"; }", NULL,
+ CFG_ZONE_PRIMARY, true },
+ { "{ type primary; allow-update { none; }; }", NULL,
+ CFG_ZONE_SECONDARY, false },
+ { "{}", "{ allow-update { none; }; }", CFG_ZONE_SECONDARY,
+ false },
+ { "{ notify yes; }", "{ notify no; }", CFG_ZONE_PRIMARY, true },
+ { "{ allow-update { none; }; }", "{ allow-update { any; }; }",
+ CFG_ZONE_SECONDARY, false },
+ { "{ in-view \"other\"; }", NULL, CFG_ZONE_INVIEW, true },
+ { "{ in-view \"other\"; notify yes; }", NULL, CFG_ZONE_INVIEW,
+ false },
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(cases); i++) {
+ const char *texts[] = { cases[i].zone, cases[i].template };
+ cfg_obj_t *maps[] = { NULL, NULL };
+
+ for (size_t j = 0; j < ARRAY_SIZE(texts); j++) {
+ isc_buffer_t buffer;
+ if (texts[j] == NULL) {
+ continue;
+ }
+ isc_buffer_constinit(&buffer, texts[j],
+ strlen(texts[j]));
+ isc_buffer_add(&buffer, strlen(texts[j]));
+ assert_int_equal(cfg_parse_buffer(&buffer,
+ "zone-options", 0,
+ &cfg_type_zoneopts, 0,
+ &maps[j]),
+ ISC_R_SUCCESS);
+ }
+
+ assert_int_equal(cfg_zoneopts_validforzone(maps[0], maps[1],
+ cases[i].ztype),
+ cases[i].valid);
+
+ for (size_t j = 0; j < ARRAY_SIZE(maps); j++) {
+ if (maps[j] != NULL) {
+ cfg_obj_detach(&maps[j]);
+ }
+ }
+ }
+}
+
ISC_TEST_LIST_START
+ISC_TEST_ENTRY(zone_options_validity)
ISC_TEST_ENTRY(query_source)
ISC_TEST_ENTRY(query_source_v6)
ISC_TEST_ENTRY(server_query_source)