diff options
Diffstat (limited to 'lib/bind9/check.c')
-rw-r--r-- | lib/bind9/check.c | 1442 |
1 files changed, 1442 insertions, 0 deletions
diff --git a/lib/bind9/check.c b/lib/bind9/check.c new file mode 100644 index 000000000000..2079a8477ac6 --- /dev/null +++ b/lib/bind9/check.c @@ -0,0 +1,1442 @@ +/* + * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2001-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: check.c,v 1.37.6.34 2006/03/02 00:37:20 marka Exp $ */ + +#include <config.h> + +#include <stdlib.h> +#include <string.h> + +#include <isc/buffer.h> +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/netaddr.h> +#include <isc/parseint.h> +#include <isc/region.h> +#include <isc/result.h> +#include <isc/sockaddr.h> +#include <isc/symtab.h> +#include <isc/util.h> + +#include <dns/fixedname.h> +#include <dns/rdataclass.h> +#include <dns/rdatatype.h> +#include <dns/secalg.h> + +#include <isccfg/cfg.h> + +#include <bind9/check.h> + +static void +freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) { + UNUSED(type); + UNUSED(value); + isc_mem_free(userarg, key); +} + +static isc_result_t +check_orderent(const cfg_obj_t *ent, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + isc_textregion_t r; + dns_fixedname_t fixed; + const cfg_obj_t *obj; + dns_rdataclass_t rdclass; + dns_rdatatype_t rdtype; + isc_buffer_t b; + const char *str; + + dns_fixedname_init(&fixed); + obj = cfg_tuple_get(ent, "class"); + if (cfg_obj_isstring(obj)) { + + DE_CONST(cfg_obj_asstring(obj), r.base); + r.length = strlen(r.base); + tresult = dns_rdataclass_fromtext(&rdclass, &r); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "rrset-order: invalid class '%s'", + r.base); + result = ISC_R_FAILURE; + } + } + + obj = cfg_tuple_get(ent, "type"); + if (cfg_obj_isstring(obj)) { + + DE_CONST(cfg_obj_asstring(obj), r.base); + r.length = strlen(r.base); + tresult = dns_rdatatype_fromtext(&rdtype, &r); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "rrset-order: invalid type '%s'", + r.base); + result = ISC_R_FAILURE; + } + } + + obj = cfg_tuple_get(ent, "name"); + if (cfg_obj_isstring(obj)) { + str = cfg_obj_asstring(obj); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b, + dns_rootname, ISC_FALSE, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "rrset-order: invalid name '%s'", str); + result = ISC_R_FAILURE; + } + } + + obj = cfg_tuple_get(ent, "order"); + if (!cfg_obj_isstring(obj) || + strcasecmp("order", cfg_obj_asstring(obj)) != 0) { + cfg_obj_log(ent, logctx, ISC_LOG_ERROR, + "rrset-order: keyword 'order' missing"); + result = ISC_R_FAILURE; + } + + obj = cfg_tuple_get(ent, "ordering"); + if (!cfg_obj_isstring(obj)) { + cfg_obj_log(ent, logctx, ISC_LOG_ERROR, + "rrset-order: missing ordering"); + result = ISC_R_FAILURE; + } else if (strcasecmp(cfg_obj_asstring(obj), "fixed") == 0) { + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "rrset-order: order 'fixed' not fully implemented"); + } else if (/* strcasecmp(cfg_obj_asstring(obj), "fixed") != 0 && */ + strcasecmp(cfg_obj_asstring(obj), "random") != 0 && + strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "rrset-order: invalid order '%s'", + cfg_obj_asstring(obj)); + result = ISC_R_FAILURE; + } + return (result); +} + +static isc_result_t +check_order(const cfg_obj_t *options, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + const cfg_listelt_t *element; + const cfg_obj_t *obj = NULL; + + if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS) + return (result); + + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) + { + tresult = check_orderent(cfg_listelt_value(element), logctx); + if (tresult != ISC_R_SUCCESS) + result = tresult; + } + return (result); +} + +static isc_result_t +check_dual_stack(const cfg_obj_t *options, isc_log_t *logctx) { + const cfg_listelt_t *element; + const cfg_obj_t *alternates = NULL; + const cfg_obj_t *value; + const cfg_obj_t *obj; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t buffer; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + + (void)cfg_map_get(options, "dual-stack-servers", &alternates); + + if (alternates == NULL) + return (ISC_R_SUCCESS); + + obj = cfg_tuple_get(alternates, "port"); + if (cfg_obj_isuint32(obj)) { + isc_uint32_t val = cfg_obj_asuint32(obj); + if (val > ISC_UINT16_MAX) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "port '%u' out of range", val); + result = ISC_R_FAILURE; + } + } + obj = cfg_tuple_get(alternates, "addresses"); + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) { + value = cfg_listelt_value(element); + if (cfg_obj_issockaddr(value)) + continue; + obj = cfg_tuple_get(value, "name"); + str = cfg_obj_asstring(obj); + isc_buffer_init(&buffer, str, strlen(str)); + isc_buffer_add(&buffer, strlen(str)); + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + tresult = dns_name_fromtext(name, &buffer, dns_rootname, + ISC_FALSE, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "bad name '%s'", str); + result = ISC_R_FAILURE; + } + obj = cfg_tuple_get(value, "port"); + if (cfg_obj_isuint32(obj)) { + isc_uint32_t val = cfg_obj_asuint32(obj); + if (val > ISC_UINT16_MAX) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "port '%u' out of range", val); + result = ISC_R_FAILURE; + } + } + } + return (result); +} + +static isc_result_t +check_forward(const cfg_obj_t *options, isc_log_t *logctx) { + const cfg_obj_t *forward = NULL; + const cfg_obj_t *forwarders = NULL; + + (void)cfg_map_get(options, "forward", &forward); + (void)cfg_map_get(options, "forwarders", &forwarders); + + if (forward != NULL && forwarders == NULL) { + cfg_obj_log(forward, logctx, ISC_LOG_ERROR, + "no matching 'forwarders' statement"); + return (ISC_R_FAILURE); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +disabled_algorithms(const cfg_obj_t *disabled, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + const cfg_listelt_t *element; + const char *str; + isc_buffer_t b; + dns_fixedname_t fixed; + dns_name_t *name; + const cfg_obj_t *obj; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + obj = cfg_tuple_get(disabled, "name"); + str = cfg_obj_asstring(obj); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + tresult = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "bad domain name '%s'", str); + result = tresult; + } + + obj = cfg_tuple_get(disabled, "algorithms"); + + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) + { + isc_textregion_t r; + dns_secalg_t alg; + isc_result_t tresult; + + DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base); + r.length = strlen(r.base); + + tresult = dns_secalg_fromtext(&alg, &r); + if (tresult != ISC_R_SUCCESS) { + isc_uint8_t ui; + result = isc_parse_uint8(&ui, r.base, 10); + } + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(cfg_listelt_value(element), logctx, + ISC_LOG_ERROR, "invalid algorithm"); + result = tresult; + } + } + return (result); +} + +static isc_result_t +nameexist(const cfg_obj_t *obj, const char *name, int value, + isc_symtab_t *symtab, const char *fmt, isc_log_t *logctx, + isc_mem_t *mctx) +{ + char *key; + const char *file; + unsigned int line; + isc_result_t result; + isc_symvalue_t symvalue; + + key = isc_mem_strdup(mctx, name); + if (key == NULL) + return (ISC_R_NOMEMORY); + symvalue.as_cpointer = obj; + result = isc_symtab_define(symtab, key, value, symvalue, + isc_symexists_reject); + if (result == ISC_R_EXISTS) { + RUNTIME_CHECK(isc_symtab_lookup(symtab, key, value, + &symvalue) == ISC_R_SUCCESS); + file = cfg_obj_file(symvalue.as_cpointer); + line = cfg_obj_line(symvalue.as_cpointer); + + if (file == NULL) + file = "<unknown file>"; + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, fmt, key, file, line); + isc_mem_free(mctx, key); + result = ISC_R_EXISTS; + } else if (result != ISC_R_SUCCESS) { + isc_mem_free(mctx, key); + } + return (result); +} + +static isc_result_t +mustbesecure(const cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx, + isc_mem_t *mctx) +{ + const cfg_obj_t *obj; + char namebuf[DNS_NAME_FORMATSIZE]; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t b; + isc_result_t result = ISC_R_SUCCESS; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + obj = cfg_tuple_get(secure, "name"); + str = cfg_obj_asstring(obj); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "bad domain name '%s'", str); + } else { + dns_name_format(name, namebuf, sizeof(namebuf)); + result = nameexist(secure, namebuf, 1, symtab, + "dnssec-must-be-secure '%s': already " + "exists previous definition: %s:%u", + logctx, mctx); + } + return (result); +} + +typedef struct { + const char *name; + unsigned int scale; + unsigned int max; +} intervaltable; + +static isc_result_t +check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + unsigned int i; + const cfg_obj_t *obj = NULL; + const cfg_listelt_t *element; + isc_symtab_t *symtab = NULL; + + static intervaltable intervals[] = { + { "cleaning-interval", 60, 28 * 24 * 60 }, /* 28 days */ + { "heartbeat-interval", 60, 28 * 24 * 60 }, /* 28 days */ + { "interface-interval", 60, 28 * 24 * 60 }, /* 28 days */ + { "max-transfer-idle-in", 60, 28 * 24 * 60 }, /* 28 days */ + { "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */ + { "max-transfer-time-in", 60, 28 * 24 * 60 }, /* 28 days */ + { "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */ + { "sig-validity-interval", 86400, 10 * 366 }, /* 10 years */ + { "statistics-interval", 60, 28 * 24 * 60 }, /* 28 days */ + }; + + /* + * Check that fields specified in units of time other than seconds + * have reasonable values. + */ + for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) { + isc_uint32_t val; + obj = NULL; + (void)cfg_map_get(options, intervals[i].name, &obj); + if (obj == NULL) + continue; + val = cfg_obj_asuint32(obj); + if (val > intervals[i].max) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%u' is out of range (0..%u)", + intervals[i].name, val, + intervals[i].max); + result = ISC_R_RANGE; + } else if (val > (ISC_UINT32_MAX / intervals[i].scale)) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "%s '%d' is out of range", + intervals[i].name, val); + result = ISC_R_RANGE; + } + } + obj = NULL; + (void)cfg_map_get(options, "preferred-glue", &obj); + if (obj != NULL) { + const char *str; + str = cfg_obj_asstring(obj); + if (strcasecmp(str, "a") != 0 && + strcasecmp(str, "aaaa") != 0 && + strcasecmp(str, "none") != 0) + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "preferred-glue unexpected value '%s'", + str); + } + obj = NULL; + (void)cfg_map_get(options, "root-delegation-only", &obj); + if (obj != NULL) { + if (!cfg_obj_isvoid(obj)) { + const cfg_listelt_t *element; + const cfg_obj_t *exclude; + const char *str; + dns_fixedname_t fixed; + dns_name_t *name; + isc_buffer_t b; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) { + exclude = cfg_listelt_value(element); + str = cfg_obj_asstring(exclude); + isc_buffer_init(&b, str, strlen(str)); + isc_buffer_add(&b, strlen(str)); + tresult = dns_name_fromtext(name, &b, + dns_rootname, + ISC_FALSE, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "bad domain name '%s'", + str); + result = tresult; + } + } + } + } + + /* + * Set supported DNSSEC algorithms. + */ + obj = NULL; + (void)cfg_map_get(options, "disable-algorithms", &obj); + if (obj != NULL) { + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + tresult = disabled_algorithms(obj, logctx); + if (tresult != ISC_R_SUCCESS) + result = tresult; + } + } + + /* + * Check the DLV zone name. + */ + obj = NULL; + (void)cfg_map_get(options, "dnssec-lookaside", &obj); + if (obj != NULL) { + tresult = isc_symtab_create(mctx, 100, freekey, mctx, + ISC_TRUE, &symtab); + if (tresult != ISC_R_SUCCESS) + result = tresult; + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) + { + dns_fixedname_t fixedname; + dns_name_t *name; + const char *dlv; + isc_buffer_t b; + + obj = cfg_listelt_value(element); + + dlv = cfg_obj_asstring(cfg_tuple_get(obj, "domain")); + dns_fixedname_init(&fixedname); + name = dns_fixedname_name(&fixedname); + isc_buffer_init(&b, dlv, strlen(dlv)); + isc_buffer_add(&b, strlen(dlv)); + tresult = dns_name_fromtext(name, &b, dns_rootname, + ISC_TRUE, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "bad domain name '%s'", dlv); + result = tresult; + } + if (symtab != NULL) { + tresult = nameexist(obj, dlv, 1, symtab, + "dnssec-lookaside '%s': " + "already exists previous " + "definition: %s:%u", + logctx, mctx); + if (tresult != ISC_R_SUCCESS && + result == ISC_R_SUCCESS) + result = tresult; + } + /* + * XXXMPA to be removed when multiple lookaside + * namespaces are supported. + */ + if (!dns_name_equal(dns_rootname, name)) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "dnssec-lookaside '%s': " + "non-root not yet supported", dlv); + if (result == ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } + dlv = cfg_obj_asstring(cfg_tuple_get(obj, + "trust-anchor")); + dns_fixedname_init(&fixedname); + isc_buffer_init(&b, dlv, strlen(dlv)); + isc_buffer_add(&b, strlen(dlv)); + tresult = dns_name_fromtext(name, &b, dns_rootname, + ISC_TRUE, NULL); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "bad domain name '%s'", dlv); + if (result == ISC_R_SUCCESS) + result = tresult; + } + } + if (symtab != NULL) + isc_symtab_destroy(&symtab); + } + + /* + * Check dnssec-must-be-secure. + */ + obj = NULL; + (void)cfg_map_get(options, "dnssec-must-be-secure", &obj); + if (obj != NULL) { + isc_symtab_t *symtab = NULL; + tresult = isc_symtab_create(mctx, 100, freekey, mctx, + ISC_FALSE, &symtab); + if (tresult != ISC_R_SUCCESS) + result = tresult; + for (element = cfg_list_first(obj); + element != NULL; + element = cfg_list_next(element)) + { + obj = cfg_listelt_value(element); + tresult = mustbesecure(obj, symtab, logctx, mctx); + if (tresult != ISC_R_SUCCESS) + result = tresult; + } + if (symtab != NULL) + isc_symtab_destroy(&symtab); + } + + return (result); +} + +static isc_result_t +get_masters_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) { + isc_result_t result; + const cfg_obj_t *masters = NULL; + const cfg_listelt_t *elt; + + result = cfg_map_get(cctx, "masters", &masters); + if (result != ISC_R_SUCCESS) + return (result); + for (elt = cfg_list_first(masters); + elt != NULL; + elt = cfg_list_next(elt)) { + const cfg_obj_t *list; + const char *listname; + + list = cfg_listelt_value(elt); + listname = cfg_obj_asstring(cfg_tuple_get(list, "name")); + + if (strcasecmp(listname, name) == 0) { + *ret = list; + return (ISC_R_SUCCESS); + } + } + return (ISC_R_NOTFOUND); +} + +static isc_result_t +validate_masters(const cfg_obj_t *obj, const cfg_obj_t *config, + isc_uint32_t *countp, isc_log_t *logctx, isc_mem_t *mctx) +{ + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + isc_uint32_t count = 0; + isc_symtab_t *symtab = NULL; + isc_symvalue_t symvalue; + const cfg_listelt_t *element; + const cfg_listelt_t **stack = NULL; + isc_uint32_t stackcount = 0, pushed = 0; + const cfg_obj_t *list; + + REQUIRE(countp != NULL); + result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab); + if (result != ISC_R_SUCCESS) { + *countp = count; + return (result); + } + + newlist: + list = cfg_tuple_get(obj, "addresses"); + element = cfg_list_first(list); + resume: + for ( ; + element != NULL; + element = cfg_list_next(element)) + { + const char *listname; + const cfg_obj_t *addr; + const cfg_obj_t *key; + + addr = cfg_tuple_get(cfg_listelt_value(element), + "masterselement"); + key = cfg_tuple_get(cfg_listelt_value(element), "key"); + + if (cfg_obj_issockaddr(addr)) { + count++; + continue; + } + if (!cfg_obj_isvoid(key)) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "unexpected token '%s'", + cfg_obj_asstring(key)); + if (result == ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } + listname = cfg_obj_asstring(addr); + symvalue.as_cpointer = addr; + tresult = isc_symtab_define(symtab, listname, 1, symvalue, + isc_symexists_reject); + if (tresult == ISC_R_EXISTS) + continue; + tresult = get_masters_def(config, listname, &obj); + if (tresult != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) + result = tresult; + cfg_obj_log(addr, logctx, ISC_LOG_ERROR, + "unable to find masters list '%s'", + listname); + continue; + } + /* Grow stack? */ + if (stackcount == pushed) { + void * new; + isc_uint32_t newlen = stackcount + 16; + size_t newsize, oldsize; + + newsize = newlen * sizeof(*stack); + oldsize = stackcount * sizeof(*stack); + new = isc_mem_get(mctx, newsize); + if (new == NULL) + goto cleanup; + if (stackcount != 0) { + memcpy(new, stack, oldsize); + isc_mem_put(mctx, stack, oldsize); + } + stack = new; + stackcount = newlen; + } + stack[pushed++] = cfg_list_next(element); + goto newlist; + } + if (pushed != 0) { + element = stack[--pushed]; + goto resume; + } + cleanup: + if (stack != NULL) + isc_mem_put(mctx, stack, stackcount * sizeof(*stack)); + isc_symtab_destroy(&symtab); + *countp = count; + return (result); +} + +#define MASTERZONE 1 +#define SLAVEZONE 2 +#define STUBZONE 4 +#define HINTZONE 8 +#define FORWARDZONE 16 +#define DELEGATIONZONE 32 + +typedef struct { + const char *name; + int allowed; +} optionstable; + +static isc_result_t +check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *config, + isc_symtab_t *symtab, dns_rdataclass_t defclass, + isc_log_t *logctx, isc_mem_t *mctx) +{ + const char *zname; + const char *typestr; + unsigned int ztype; + const cfg_obj_t *zoptions; + const cfg_obj_t *obj = NULL; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + unsigned int i; + dns_rdataclass_t zclass; + dns_fixedname_t fixedname; + isc_buffer_t b; + + static optionstable options[] = { + { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE }, + { "allow-notify", SLAVEZONE }, + { "allow-transfer", MASTERZONE | SLAVEZONE }, + { "notify", MASTERZONE | SLAVEZONE }, + { "also-notify", MASTERZONE | SLAVEZONE }, + { "dialup", MASTERZONE | SLAVEZONE | STUBZONE }, + { "delegation-only", HINTZONE | STUBZONE }, + { "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE}, + { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE}, + { "maintain-ixfr-base", MASTERZONE | SLAVEZONE }, + { "max-ixfr-log-size", MASTERZONE | SLAVEZONE }, + { "notify-source", MASTERZONE | SLAVEZONE }, + { "notify-source-v6", MASTERZONE | SLAVEZONE }, + { "transfer-source", SLAVEZONE | STUBZONE }, + { "transfer-source-v6", SLAVEZONE | STUBZONE }, + { "max-transfer-time-in", SLAVEZONE | STUBZONE }, + { "max-transfer-time-out", MASTERZONE | SLAVEZONE }, + { "max-transfer-idle-in", SLAVEZONE | STUBZONE }, + { "max-transfer-idle-out", MASTERZONE | SLAVEZONE }, + { "max-retry-time", SLAVEZONE | STUBZONE }, + { "min-retry-time", SLAVEZONE | STUBZONE }, + { "max-refresh-time", SLAVEZONE | STUBZONE }, + { "min-refresh-time", SLAVEZONE | STUBZONE }, + { "sig-validity-interval", MASTERZONE }, + { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE }, + { "allow-update", MASTERZONE }, + { "allow-update-forwarding", SLAVEZONE }, + { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE }, + { "ixfr-base", MASTERZONE | SLAVEZONE }, + { "ixfr-tmp-file", MASTERZONE | SLAVEZONE }, + { "masters", SLAVEZONE | STUBZONE }, + { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE }, + { "update-policy", MASTERZONE }, + { "database", MASTERZONE | SLAVEZONE | STUBZONE }, + { "key-directory", MASTERZONE }, + }; + + static optionstable dialups[] = { + { "notify", MASTERZONE | SLAVEZONE }, + { "notify-passive", SLAVEZONE }, + { "refresh", SLAVEZONE | STUBZONE }, + { "passive", SLAVEZONE | STUBZONE }, + }; + + zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); + + zoptions = cfg_tuple_get(zconfig, "options"); + + obj = NULL; + (void)cfg_map_get(zoptions, "type", &obj); + if (obj == NULL) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': type not present", zname); + return (ISC_R_FAILURE); + } + + typestr = cfg_obj_asstring(obj); + if (strcasecmp(typestr, "master") == 0) + ztype = MASTERZONE; + else if (strcasecmp(typestr, "slave") == 0) + ztype = SLAVEZONE; + else if (strcasecmp(typestr, "stub") == 0) + ztype = STUBZONE; + else if (strcasecmp(typestr, "forward") == 0) + ztype = FORWARDZONE; + else if (strcasecmp(typestr, "hint") == 0) + ztype = HINTZONE; + else if (strcasecmp(typestr, "delegation-only") == 0) + ztype = DELEGATIONZONE; + else { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "zone '%s': invalid type %s", + zname, typestr); + return (ISC_R_FAILURE); + } + + obj = cfg_tuple_get(zconfig, "class"); + if (cfg_obj_isstring(obj)) { + isc_textregion_t r; + + DE_CONST(cfg_obj_asstring(obj), r.base); + r.length = strlen(r.base); + result = dns_rdataclass_fromtext(&zclass, &r); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "zone '%s': invalid class %s", + zname, r.base); + return (ISC_R_FAILURE); + } + if (zclass != defclass) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "zone '%s': class '%s' does not " + "match view/default class", + zname, r.base); + return (ISC_R_FAILURE); + } + } + + /* + * Look for an already existing zone. + * We need to make this cannonical as isc_symtab_define() + * deals with strings. + */ + dns_fixedname_init(&fixedname); + isc_buffer_init(&b, zname, strlen(zname)); + isc_buffer_add(&b, strlen(zname)); + tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b, + dns_rootname, ISC_TRUE, NULL); + if (result != ISC_R_SUCCESS) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': is not a valid name", zname); + tresult = ISC_R_FAILURE; + } else { + char namebuf[DNS_NAME_FORMATSIZE]; + + dns_name_format(dns_fixedname_name(&fixedname), + namebuf, sizeof(namebuf)); + tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2, + symtab, "zone '%s': already exists " + "previous definition: %s:%u", logctx, mctx); + if (tresult != ISC_R_SUCCESS) + result = tresult; + } + + /* + * Look for inappropriate options for the given zone type. + */ + for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) { + obj = NULL; + if ((options[i].allowed & ztype) == 0 && + cfg_map_get(zoptions, options[i].name, &obj) == + ISC_R_SUCCESS) + { + if (strcmp(options[i].name, "allow-update") != 0 || + ztype != SLAVEZONE) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "option '%s' is not allowed " + "in '%s' zone '%s'", + options[i].name, typestr, zname); + result = ISC_R_FAILURE; + } else + cfg_obj_log(obj, logctx, ISC_LOG_WARNING, + "option '%s' is not allowed " + "in '%s' zone '%s'", + options[i].name, typestr, zname); + } + } + + /* + * Slave & stub zones must have a "masters" field. + */ + if (ztype == SLAVEZONE || ztype == STUBZONE) { + obj = NULL; + if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) { + cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, + "zone '%s': missing 'masters' entry", + zname); + result = ISC_R_FAILURE; + } else { + isc_uint32_t count; + tresult = validate_masters(obj, config, &count, + logctx, mctx); + if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) + result = tresult; + if (tresult == ISC_R_SUCCESS && count == 0) { + cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR, + "zone '%s': empty 'masters' entry", + zname); + result = ISC_R_FAILURE; + } + } + } + + /* + * Master zones can't have both "allow-update" and "update-policy". + */ + if (ztype == MASTERZONE) { + isc_result_t res1, res2; + obj = NULL; + res1 = cfg_map_get(zoptions, "allow-update", &obj); + obj = NULL; + res2 = cfg_map_get(zoptions, "update-policy", &obj); + if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "zone '%s': 'allow-update' is ignored " + "when 'update-policy' is present", + zname); + result = ISC_R_FAILURE; + } + } + + /* + * Check the excessively complicated "dialup" option. + */ + if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) { + const cfg_obj_t *dialup = NULL; + (void)cfg_map_get(zoptions, "dialup", &dialup); + if (dialup != NULL && cfg_obj_isstring(dialup)) { + const char *str = cfg_obj_asstring(dialup); + for (i = 0; + i < sizeof(dialups) / sizeof(dialups[0]); + i++) + { + if (strcasecmp(dialups[i].name, str) != 0) + continue; + if ((dialups[i].allowed & ztype) == 0) { + cfg_obj_log(obj, logctx, + ISC_LOG_ERROR, + "dialup type '%s' is not " + "allowed in '%s' " + "zone '%s'", + str, typestr, zname); + result = ISC_R_FAILURE; + } + break; + } + if (i == sizeof(dialups) / sizeof(dialups[0])) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "invalid dialup type '%s' in zone " + "'%s'", str, zname); + result = ISC_R_FAILURE; + } + } + } + + /* + * Check that forwarding is reasonable. + */ + if (check_forward(zoptions, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + + /* + * Check various options. + */ + tresult = check_options(zoptions, logctx, mctx); + if (tresult != ISC_R_SUCCESS) + result = tresult; + + /* + * If the zone type is rbt/rbt64 then master/hint zones + * require file clauses. + */ + obj = NULL; + tresult = cfg_map_get(zoptions, "database", &obj); + if (tresult == ISC_R_NOTFOUND || + (tresult == ISC_R_SUCCESS && + (strcmp("rbt", cfg_obj_asstring(obj)) == 0 || + strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) { + obj = NULL; + tresult = cfg_map_get(zoptions, "file", &obj); + if (tresult != ISC_R_SUCCESS && + (ztype == MASTERZONE || ztype == HINTZONE)) { + cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR, + "zone '%s': missing 'file' entry", + zname); + result = tresult; + } + } + + return (result); +} + +isc_result_t +bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) { + const cfg_obj_t *algobj = NULL; + const cfg_obj_t *secretobj = NULL; + const char *keyname = cfg_obj_asstring(cfg_map_getname(key)); + + (void)cfg_map_get(key, "algorithm", &algobj); + (void)cfg_map_get(key, "secret", &secretobj); + if (secretobj == NULL || algobj == NULL) { + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "key '%s' must have both 'secret' and " + "'algorithm' defined", + keyname); + return (ISC_R_FAILURE); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +check_keylist(const cfg_obj_t *keys, isc_symtab_t *symtab, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + const cfg_listelt_t *element; + + for (element = cfg_list_first(keys); + element != NULL; + element = cfg_list_next(element)) + { + const cfg_obj_t *key = cfg_listelt_value(element); + const char *keyname = cfg_obj_asstring(cfg_map_getname(key)); + isc_symvalue_t symvalue; + + symvalue.as_cpointer = key; + tresult = isc_symtab_define(symtab, keyname, 1, + symvalue, isc_symexists_reject); + if (tresult == ISC_R_EXISTS) { + const char *file; + unsigned int line; + + RUNTIME_CHECK(isc_symtab_lookup(symtab, keyname, + 1, &symvalue) == ISC_R_SUCCESS); + file = cfg_obj_file(symvalue.as_cpointer); + line = cfg_obj_line(symvalue.as_cpointer); + + if (file == NULL) + file = "<unknown file>"; + cfg_obj_log(key, logctx, ISC_LOG_ERROR, + "key '%s': already exists " + "previous definition: %s:%u", + keyname, file, line); + result = tresult; + } else if (tresult != ISC_R_SUCCESS) + return (tresult); + + tresult = bind9_check_key(key, logctx); + if (tresult != ISC_R_SUCCESS) + return (tresult); + } + return (result); +} + +static isc_result_t +check_servers(const cfg_obj_t *servers, isc_log_t *logctx) { + isc_result_t result = ISC_R_SUCCESS; + const cfg_listelt_t *e1; + const cfg_listelt_t *e2; + const cfg_obj_t *v1; + const cfg_obj_t *v2; + const isc_sockaddr_t *s1; + const isc_sockaddr_t *s2; + isc_netaddr_t na; + const cfg_obj_t *ts; + char buf[128]; + const char *xfr; + isc_buffer_t target; + + for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) { + v1 = cfg_listelt_value(e1); + s1 = cfg_obj_assockaddr(cfg_map_getname(v1)); + ts = NULL; + if (isc_sockaddr_pf(s1) == AF_INET) + xfr = "transfer-source-v6"; + else + xfr = "transfer-source"; + (void)cfg_map_get(v1, xfr, &ts); + if (ts != NULL) { + isc_netaddr_fromsockaddr(&na, s1); + isc_buffer_init(&target, buf, sizeof(buf) - 1); + RUNTIME_CHECK(isc_netaddr_totext(&na, &target) + == ISC_R_SUCCESS); + buf[isc_buffer_usedlength(&target)] = '\0'; + cfg_obj_log(v1, logctx, ISC_LOG_ERROR, + "server '%s': %s not valid", buf, xfr); + result = ISC_R_FAILURE; + } + e2 = e1; + while ((e2 = cfg_list_next(e2)) != NULL) { + v2 = cfg_listelt_value(e2); + s2 = cfg_obj_assockaddr(cfg_map_getname(v2)); + if (isc_sockaddr_eqaddr(s1, s2)) { + const char *file = cfg_obj_file(v1); + unsigned int line = cfg_obj_line(v1); + + if (file == NULL) + file = "<unknown file>"; + + isc_netaddr_fromsockaddr(&na, s2); + isc_buffer_init(&target, buf, sizeof(buf) - 1); + RUNTIME_CHECK(isc_netaddr_totext(&na, &target) + == ISC_R_SUCCESS); + buf[isc_buffer_usedlength(&target)] = '\0'; + + cfg_obj_log(v2, logctx, ISC_LOG_ERROR, + "server '%s': already exists " + "previous definition: %s:%u", + buf, file, line); + result = ISC_R_FAILURE; + } + } + } + return (result); +} + +static isc_result_t +check_viewconf(const cfg_obj_t *config, const cfg_obj_t *vconfig, + dns_rdataclass_t vclass, isc_log_t *logctx, isc_mem_t *mctx) +{ + const cfg_obj_t *servers = NULL; + const cfg_obj_t *zones = NULL; + const cfg_obj_t *keys = NULL; + const cfg_listelt_t *element; + isc_symtab_t *symtab = NULL; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult = ISC_R_SUCCESS; + + /* + * Check that all zone statements are syntactically correct and + * there are no duplicate zones. + */ + tresult = isc_symtab_create(mctx, 100, freekey, mctx, + ISC_FALSE, &symtab); + if (tresult != ISC_R_SUCCESS) + return (ISC_R_NOMEMORY); + + if (vconfig != NULL) + (void)cfg_map_get(vconfig, "zone", &zones); + else + (void)cfg_map_get(config, "zone", &zones); + + for (element = cfg_list_first(zones); + element != NULL; + element = cfg_list_next(element)) + { + isc_result_t tresult; + const cfg_obj_t *zone = cfg_listelt_value(element); + + tresult = check_zoneconf(zone, config, symtab, vclass, + logctx, mctx); + if (tresult != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } + + isc_symtab_destroy(&symtab); + + /* + * Check that all key statements are syntactically correct and + * there are no duplicate keys. + */ + tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab); + if (tresult != ISC_R_SUCCESS) + return (ISC_R_NOMEMORY); + + (void)cfg_map_get(config, "key", &keys); + tresult = check_keylist(keys, symtab, logctx); + if (tresult == ISC_R_EXISTS) + result = ISC_R_FAILURE; + else if (tresult != ISC_R_SUCCESS) { + isc_symtab_destroy(&symtab); + return (tresult); + } + + if (vconfig != NULL) { + keys = NULL; + (void)cfg_map_get(vconfig, "key", &keys); + tresult = check_keylist(keys, symtab, logctx); + if (tresult == ISC_R_EXISTS) + result = ISC_R_FAILURE; + else if (tresult != ISC_R_SUCCESS) { + isc_symtab_destroy(&symtab); + return (tresult); + } + } + + isc_symtab_destroy(&symtab); + + /* + * Check that forwarding is reasonable. + */ + if (vconfig == NULL) { + const cfg_obj_t *options = NULL; + (void)cfg_map_get(config, "options", &options); + if (options != NULL) + if (check_forward(options, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } else { + if (check_forward(vconfig, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } + /* + * Check that dual-stack-servers is reasonable. + */ + if (vconfig == NULL) { + const cfg_obj_t *options = NULL; + (void)cfg_map_get(config, "options", &options); + if (options != NULL) + if (check_dual_stack(options, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } else { + if (check_dual_stack(vconfig, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } + + /* + * Check that rrset-order is reasonable. + */ + if (vconfig != NULL) { + if (check_order(vconfig, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } + + if (vconfig != NULL) { + (void)cfg_map_get(vconfig, "server", &servers); + if (servers != NULL && + check_servers(servers, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } + + if (vconfig != NULL) + tresult = check_options(vconfig, logctx, mctx); + else + tresult = check_options(config, logctx, mctx); + if (tresult != ISC_R_SUCCESS) + result = tresult; + + return (result); +} + + +isc_result_t +bind9_check_namedconf(const cfg_obj_t *config, isc_log_t *logctx, + isc_mem_t *mctx) +{ + const cfg_obj_t *options = NULL; + const cfg_obj_t *servers = NULL; + const cfg_obj_t *views = NULL; + const cfg_obj_t *acls = NULL; + const cfg_obj_t *kals = NULL; + const cfg_obj_t *obj; + const cfg_listelt_t *velement; + isc_result_t result = ISC_R_SUCCESS; + isc_result_t tresult; + isc_symtab_t *symtab = NULL; + + static const char *builtin[] = { "localhost", "localnets", + "any", "none"}; + + (void)cfg_map_get(config, "options", &options); + + if (options != NULL && + check_options(options, logctx, mctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + + (void)cfg_map_get(config, "server", &servers); + if (servers != NULL && + check_servers(servers, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + + if (options != NULL && + check_order(options, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + + (void)cfg_map_get(config, "view", &views); + + if (views != NULL && options != NULL) + if (check_dual_stack(options, logctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + + if (views == NULL) { + if (check_viewconf(config, NULL, dns_rdataclass_in, + logctx, mctx) != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } else { + const cfg_obj_t *zones = NULL; + + (void)cfg_map_get(config, "zone", &zones); + if (zones != NULL) { + cfg_obj_log(zones, logctx, ISC_LOG_ERROR, + "when using 'view' statements, " + "all zones must be in views"); + result = ISC_R_FAILURE; + } + } + + tresult = isc_symtab_create(mctx, 100, NULL, NULL, ISC_TRUE, &symtab); + if (tresult != ISC_R_SUCCESS) + result = tresult; + for (velement = cfg_list_first(views); + velement != NULL; + velement = cfg_list_next(velement)) + { + const cfg_obj_t *view = cfg_listelt_value(velement); + const cfg_obj_t *vname = cfg_tuple_get(view, "name"); + const cfg_obj_t *voptions = cfg_tuple_get(view, "options"); + const cfg_obj_t *vclassobj = cfg_tuple_get(view, "class"); + dns_rdataclass_t vclass = dns_rdataclass_in; + isc_result_t tresult = ISC_R_SUCCESS; + const char *key = cfg_obj_asstring(vname); + isc_symvalue_t symvalue; + + if (cfg_obj_isstring(vclassobj)) { + isc_textregion_t r; + + DE_CONST(cfg_obj_asstring(vclassobj), r.base); + r.length = strlen(r.base); + tresult = dns_rdataclass_fromtext(&vclass, &r); + if (tresult != ISC_R_SUCCESS) + cfg_obj_log(vclassobj, logctx, ISC_LOG_ERROR, + "view '%s': invalid class %s", + cfg_obj_asstring(vname), r.base); + } + if (tresult == ISC_R_SUCCESS && symtab != NULL) { + symvalue.as_cpointer = view; + tresult = isc_symtab_define(symtab, key, vclass, + symvalue, + isc_symexists_reject); + if (tresult == ISC_R_EXISTS) { + const char *file; + unsigned int line; + RUNTIME_CHECK(isc_symtab_lookup(symtab, key, + vclass, &symvalue) == ISC_R_SUCCESS); + file = cfg_obj_file(symvalue.as_cpointer); + line = cfg_obj_line(symvalue.as_cpointer); + cfg_obj_log(view, logctx, ISC_LOG_ERROR, + "view '%s': already exists " + "previous definition: %s:%u", + key, file, line); + result = tresult; + } else if (result != ISC_R_SUCCESS) { + result = tresult; + } else if ((strcasecmp(key, "_bind") == 0 && + vclass == dns_rdataclass_ch) || + (strcasecmp(key, "_default") == 0 && + vclass == dns_rdataclass_in)) { + cfg_obj_log(view, logctx, ISC_LOG_ERROR, + "attempt to redefine builtin view " + "'%s'", key); + result = ISC_R_EXISTS; + } + } + if (tresult == ISC_R_SUCCESS) + tresult = check_viewconf(config, voptions, + vclass, logctx, mctx); + if (tresult != ISC_R_SUCCESS) + result = ISC_R_FAILURE; + } + if (symtab != NULL) + isc_symtab_destroy(&symtab); + + if (views != NULL && options != NULL) { + obj = NULL; + tresult = cfg_map_get(options, "cache-file", &obj); + if (tresult == ISC_R_SUCCESS) { + cfg_obj_log(obj, logctx, ISC_LOG_ERROR, + "'cache-file' cannot be a global " + "option if views are present"); + result = ISC_R_FAILURE; + } + } + + tresult = cfg_map_get(config, "acl", &acls); + if (tresult == ISC_R_SUCCESS) { + const cfg_listelt_t *elt; + const cfg_listelt_t *elt2; + const char *aclname; + + for (elt = cfg_list_first(acls); + elt != NULL; + elt = cfg_list_next(elt)) { + const cfg_obj_t *acl = cfg_listelt_value(elt); + unsigned int i; + + aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name")); + for (i = 0; + i < sizeof(builtin) / sizeof(builtin[0]); + i++) + if (strcasecmp(aclname, builtin[i]) == 0) { + cfg_obj_log(acl, logctx, ISC_LOG_ERROR, + "attempt to redefine " + "builtin acl '%s'", + aclname); + result = ISC_R_FAILURE; + break; + } + + for (elt2 = cfg_list_next(elt); + elt2 != NULL; + elt2 = cfg_list_next(elt2)) { + const cfg_obj_t *acl2 = cfg_listelt_value(elt2); + const char *name; + name = cfg_obj_asstring(cfg_tuple_get(acl2, + "name")); + if (strcasecmp(aclname, name) == 0) { + const char *file = cfg_obj_file(acl); + unsigned int line = cfg_obj_line(acl); + + if (file == NULL) + file = "<unknown file>"; + + cfg_obj_log(acl2, logctx, ISC_LOG_ERROR, + "attempt to redefine " + "acl '%s' previous " + "definition: %s:%u", + name, file, line); + result = ISC_R_FAILURE; + } + } + } + } + + tresult = cfg_map_get(config, "kal", &kals); + if (tresult == ISC_R_SUCCESS) { + const cfg_listelt_t *elt; + const cfg_listelt_t *elt2; + const char *aclname; + + for (elt = cfg_list_first(kals); + elt != NULL; + elt = cfg_list_next(elt)) { + const cfg_obj_t *acl = cfg_listelt_value(elt); + + aclname = cfg_obj_asstring(cfg_tuple_get(acl, "name")); + + for (elt2 = cfg_list_next(elt); + elt2 != NULL; + elt2 = cfg_list_next(elt2)) { + const cfg_obj_t *acl2 = cfg_listelt_value(elt2); + const char *name; + name = cfg_obj_asstring(cfg_tuple_get(acl2, + "name")); + if (strcasecmp(aclname, name) == 0) { + const char *file = cfg_obj_file(acl); + unsigned int line = cfg_obj_line(acl); + + if (file == NULL) + file = "<unknown file>"; + + cfg_obj_log(acl2, logctx, ISC_LOG_ERROR, + "attempt to redefine " + "kal '%s' previous " + "definition: %s:%u", + name, file, line); + result = ISC_R_FAILURE; + } + } + } + } + + return (result); +} |