From ba8f85b49c38af7bc2a9acdef5dcde2de008d25e Mon Sep 17 00:00:00 2001
From: peter <peter@FreeBSD.org>
Date: Sat, 12 Jul 2008 05:00:28 +0000
Subject: Flatten bind9 vendor work area

---
 lib/bind9/check.c | 2043 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 2043 insertions(+)
 create mode 100644 lib/bind9/check.c

(limited to 'lib/bind9/check.c')

diff --git a/lib/bind9/check.c b/lib/bind9/check.c
new file mode 100644
index 0000000..6cfdc93
--- /dev/null
+++ b/lib/bind9/check.c
@@ -0,0 +1,2043 @@
+/*
+ * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001-2003  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or 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.44.18.35 2007/09/13 05:04:01 each Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.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/string.h>
+#include <isc/symtab.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/fixedname.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/secalg.h>
+
+#include <isccfg/aclconf.h>
+#include <isccfg/cfg.h>
+
+#include <bind9/check.h>
+
+#ifndef DNS_RDATASET_FIXED
+#define DNS_RDATASET_FIXED 1
+#endif
+
+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) {
+#if !DNS_RDATASET_FIXED
+		cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
+			    "rrset-order: order 'fixed' not fully implemented");
+#endif
+	} else if (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 '%s'",
+				    r.base);
+			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);
+}
+
+static isc_result_t
+checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig,
+	 const cfg_obj_t *voptions, const cfg_obj_t *config,
+	 isc_log_t *logctx, isc_mem_t *mctx)
+{
+	isc_result_t result;
+	const cfg_obj_t *aclobj = NULL;
+	const cfg_obj_t *options;
+	dns_acl_t *acl = NULL;
+
+	if (zconfig != NULL) {
+		options = cfg_tuple_get(zconfig, "options");
+		cfg_map_get(options, aclname, &aclobj);
+	}
+	if (voptions != NULL && aclobj == NULL)
+		cfg_map_get(voptions, aclname, &aclobj);
+	if (config != NULL && aclobj == NULL) {
+		options = NULL;
+		cfg_map_get(config, "options", &options);
+		if (options != NULL)
+			cfg_map_get(options, aclname, &aclobj);
+	}
+	if (aclobj == NULL)
+		return (ISC_R_SUCCESS);
+	result = cfg_acl_fromconfig(aclobj, config, logctx, actx, mctx, &acl);
+	if (acl != NULL)
+		dns_acl_detach(&acl);
+	return (result);
+}
+
+static isc_result_t
+check_viewacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
+	       const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx)
+{
+	isc_result_t result = ISC_R_SUCCESS, tresult;
+	int i = 0;
+	
+	static const char *acls[] = { "allow-query", "allow-query-cache",
+		"allow-recursion", "blackhole", "match-clients",
+		"match-destinations", "sortlist", NULL };
+
+	while (acls[i] != NULL) {
+		tresult = checkacl(acls[i++], actx, NULL, voptions, config,
+				   logctx, mctx);
+		if (tresult != ISC_R_SUCCESS)
+			result = tresult;  
+	}
+	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;
+	dns_fixedname_t fixed;
+	const char *str;
+	dns_name_t *name;
+	isc_buffer_t b;
+
+	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;
+		}
+	}
+
+	dns_fixedname_init(&fixed);
+	name = dns_fixedname_name(&fixed);
+
+	/*
+	 * 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))
+		{
+			const char *dlv;
+
+			obj = cfg_listelt_value(element);
+
+			dlv = cfg_obj_asstring(cfg_tuple_get(obj, "domain"));
+			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"));
+			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);
+	}
+
+	/*
+	 * Check empty zone configuration.
+	 */
+	obj = NULL;
+	(void)cfg_map_get(options, "empty-server", &obj);
+	if (obj != NULL) {
+		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,
+				    "empty-server: invalid name '%s'", str);
+			result = ISC_R_FAILURE;
+		}
+	}
+
+	obj = NULL;
+	(void)cfg_map_get(options, "empty-contact", &obj);
+	if (obj != NULL) {
+		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,
+				    "empty-contact: invalid name '%s'", str);
+			result = ISC_R_FAILURE;
+		}
+	}
+
+	obj = NULL;
+	(void)cfg_map_get(options, "disable-empty-zone", &obj);
+	for (element = cfg_list_first(obj);
+	     element != NULL;
+	     element = cfg_list_next(element))
+	{
+		obj = cfg_listelt_value(element);
+		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,
+				    "disable-empty-zone: invalid name '%s'",
+				    str);
+			result = ISC_R_FAILURE;
+		}
+	}
+
+	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);
+}
+
+static isc_result_t
+check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) {
+	isc_result_t result = ISC_R_SUCCESS;
+	isc_result_t tresult;
+	const cfg_listelt_t *element;
+	const cfg_listelt_t *element2;
+	dns_fixedname_t fixed;
+	const char *str;
+	isc_buffer_t b;
+
+	for (element = cfg_list_first(policy);
+	     element != NULL;
+	     element = cfg_list_next(element))
+	{
+		const cfg_obj_t *stmt = cfg_listelt_value(element);
+		const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
+		const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
+		const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
+		const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
+
+		dns_fixedname_init(&fixed);
+		str = cfg_obj_asstring(identity);
+		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(identity, logctx, ISC_LOG_ERROR,
+				    "'%s' is not a valid name", str);
+			result = tresult;
+		}
+
+		dns_fixedname_init(&fixed);
+		str = cfg_obj_asstring(dname);
+		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(dname, logctx, ISC_LOG_ERROR,
+				    "'%s' is not a valid name", str);
+			result = tresult;
+		}
+		if (tresult == ISC_R_SUCCESS &&
+		    strcasecmp(cfg_obj_asstring(matchtype), "wildcard") == 0 &&
+		    !dns_name_iswildcard(dns_fixedname_name(&fixed))) {
+			cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
+				    "'%s' is not a wildcard", str);
+			result = ISC_R_FAILURE;
+		}
+
+		for (element2 = cfg_list_first(typelist);
+		     element2 != NULL;
+		     element2 = cfg_list_next(element2))
+		{
+			const cfg_obj_t *typeobj;
+			isc_textregion_t r;
+			dns_rdatatype_t type;
+			
+			typeobj = cfg_listelt_value(element2);
+			DE_CONST(cfg_obj_asstring(typeobj), r.base);
+			r.length = strlen(r.base);
+
+			tresult = dns_rdatatype_fromtext(&type, &r);
+			if (tresult != ISC_R_SUCCESS) {
+				cfg_obj_log(typeobj, logctx, ISC_LOG_ERROR,
+                                            "'%s' is not a valid type", r.base);
+				result = tresult;
+			}
+		}
+	}
+	return (result);
+}
+
+#define MASTERZONE	1
+#define SLAVEZONE	2
+#define STUBZONE	4
+#define HINTZONE	8
+#define FORWARDZONE	16
+#define DELEGATIONZONE	32
+#define CHECKACL	64
+
+typedef struct {
+	const char *name;
+	int allowed;
+} optionstable;
+
+static isc_result_t
+check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
+	       const cfg_obj_t *config, isc_symtab_t *symtab,
+	       dns_rdataclass_t defclass, cfg_aclconfctx_t *actx,
+	       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 | CHECKACL },
+	{ "allow-notify", SLAVEZONE | CHECKACL },
+	{ "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL },
+	{ "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 | CHECKACL },
+	{ "allow-update-forwarding", SLAVEZONE | CHECKACL },
+	{ "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
+	{ "journal", MASTERZONE | SLAVEZONE },
+	{ "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 },
+	{ "check-wildcard", MASTERZONE },
+	{ "check-mx", MASTERZONE },
+	{ "integrity-check", MASTERZONE },
+	{ "check-mx-cname", MASTERZONE },
+	{ "check-srv-cname", MASTERZONE },
+	{ "masterfile-format", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
+	{ "update-check-ksk", 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.
+	 * Check that ACLs expand correctly.
+	 */
+	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);
+		}
+		obj = NULL;
+		if ((options[i].allowed & ztype) != 0 &&
+		    (options[i].allowed & CHECKACL) != 0) {
+
+			tresult = checkacl(options[i].name, actx, zconfig,
+				           voptions, config, logctx, mctx);
+			if (tresult != ISC_R_SUCCESS)
+				result = tresult;
+		}
+
+	}
+
+	/*
+	 * 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;
+		} else if (res2 == ISC_R_SUCCESS &&
+			   check_update_policy(obj, logctx) != ISC_R_SUCCESS)
+			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);
+}
+
+
+typedef struct keyalgorithms {
+	const char *name;
+	isc_uint16_t size;
+} algorithmtable;
+
+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));
+	const char *algorithm;
+	int i;
+	size_t len = 0;
+	static const algorithmtable algorithms[] = {
+		{ "hmac-md5", 128 },
+		{ "hmac-md5.sig-alg.reg.int", 0 },
+		{ "hmac-md5.sig-alg.reg.int.", 0 },
+		{ "hmac-sha1", 160 },
+		{ "hmac-sha224", 224 },
+		{ "hmac-sha256", 256 },
+		{ "hmac-sha384", 384 },
+		{ "hmac-sha512", 512 },
+		{  NULL, 0 }
+	};
+	
+	(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);
+	}
+
+	algorithm = cfg_obj_asstring(algobj);
+	for (i = 0; algorithms[i].name != NULL; i++) {
+		len = strlen(algorithms[i].name);
+		if (strncasecmp(algorithms[i].name, algorithm, len) == 0 &&
+		    (algorithm[len] == '\0' ||
+		     (algorithms[i].size != 0 && algorithm[len] == '-')))
+			break;
+	}
+	if (algorithms[i].name == NULL) {
+		cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
+			    "unknown algorithm '%s'", algorithm);
+		return (ISC_R_NOTFOUND);
+	}
+	if (algorithm[len] == '-') {
+		isc_uint16_t digestbits;
+		isc_result_t result;
+		result = isc_parse_uint16(&digestbits, algorithm + len + 1, 10);
+		if (result == ISC_R_SUCCESS || result == ISC_R_RANGE) {
+			if (result == ISC_R_RANGE ||
+			    digestbits > algorithms[i].size) {
+				cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
+					    "key '%s' digest-bits too large "
+					    "[%u..%u]", keyname,
+					    algorithms[i].size / 2,
+					    algorithms[i].size);
+				return (ISC_R_RANGE);
+			}
+			if ((digestbits % 8) != 0) {
+				cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
+					    "key '%s' digest-bits not multiple"
+					    " of 8", keyname);
+				return (ISC_R_RANGE);
+			}
+			/*
+			 * Recommended minima for hmac algorithms.
+			 */
+			if ((digestbits < (algorithms[i].size / 2U) ||
+			     (digestbits < 80U)))
+				cfg_obj_log(algobj, logctx, ISC_LOG_WARNING,
+					    "key '%s' digest-bits too small "
+					    "[<%u]", keyname, 
+					    algorithms[i].size/2);
+		} else {
+			cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
+				    "key '%s': unable to parse digest-bits",
+				    keyname);
+			return (result);
+		}
+	}
+	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;
+
+		tresult = bind9_check_key(key, logctx);
+		if (tresult != ISC_R_SUCCESS)
+			return (tresult);
+
+		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);
+	}
+	return (result);
+}
+
+static struct {
+	const char *v4;
+	const char *v6;
+} sources[] = {
+	{ "transfer-source", "transfer-source-v6" },
+	{ "notify-source", "notify-source-v6" },
+	{ "query-source", "query-source-v6" },
+	{ NULL, NULL }
+};
+
+static isc_result_t
+check_servers(const cfg_obj_t *servers, isc_log_t *logctx) {
+	isc_result_t result = ISC_R_SUCCESS;
+	isc_result_t tresult;
+	const cfg_listelt_t *e1, *e2;
+	const cfg_obj_t *v1, *v2;
+	isc_netaddr_t n1, n2;
+	unsigned int p1, p2;
+	const cfg_obj_t *obj;
+	char buf[ISC_NETADDR_FORMATSIZE];
+	const char *xfr;
+	int source;
+
+	for (e1 = cfg_list_first(servers); e1 != NULL; e1 = cfg_list_next(e1)) {
+		v1 = cfg_listelt_value(e1);
+		cfg_obj_asnetprefix(cfg_map_getname(v1), &n1, &p1);
+		/*
+		 * Check that unused bits are zero.
+		 */
+		tresult = isc_netaddr_prefixok(&n1, p1);
+		if (tresult != ISC_R_SUCCESS) {
+			INSIST(tresult == ISC_R_FAILURE);
+			isc_netaddr_format(&n1, buf, sizeof(buf));
+			cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
+				    "server '%s/%u': invalid prefix "
+				    "(extra bits specified)", buf, p1);
+			result = tresult;
+		}
+		source = 0;
+		do {
+			obj = NULL;
+			if (n1.family == AF_INET)
+				xfr = sources[source].v6;
+			else
+				xfr = sources[source].v4;
+			(void)cfg_map_get(v1, xfr, &obj);
+			if (obj != NULL) {
+				isc_netaddr_format(&n1, buf, sizeof(buf));
+				cfg_obj_log(v1, logctx, ISC_LOG_ERROR,
+					    "server '%s': %s not legal",
+					    buf, xfr);
+				result = ISC_R_FAILURE;
+			}
+		} while (sources[++source].v4 != NULL);
+		e2 = e1;
+		while ((e2 = cfg_list_next(e2)) != NULL) {
+			v2 = cfg_listelt_value(e2);
+			cfg_obj_asnetprefix(cfg_map_getname(v2), &n2, &p2);
+			if (p1 == p2 && isc_netaddr_equal(&n1, &n2)) {
+				const char *file = cfg_obj_file(v1);
+				unsigned int line = cfg_obj_line(v1);
+
+				if (file == NULL)
+					file = "<unknown file>";
+
+				isc_netaddr_format(&n2, buf, sizeof(buf));
+				cfg_obj_log(v2, logctx, ISC_LOG_ERROR,
+					    "server '%s/%u': already exists "
+					    "previous definition: %s:%u",
+					    buf, p2, file, line);
+				result = ISC_R_FAILURE;
+			}
+		}
+	}
+	return (result);
+}
+		
+static isc_result_t
+check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions,
+	       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;
+	cfg_aclconfctx_t actx;
+	const cfg_obj_t *obj;
+	isc_boolean_t enablednssec, enablevalidation;
+
+	/*
+	 * 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);
+
+	cfg_aclconfctx_init(&actx);
+
+	if (voptions != NULL)
+		(void)cfg_map_get(voptions, "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, voptions, config, symtab,
+					 vclass, &actx, 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 (voptions != NULL) {
+		keys = NULL;
+		(void)cfg_map_get(voptions, "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 (voptions == 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(voptions, logctx) != ISC_R_SUCCESS)
+			result = ISC_R_FAILURE;
+	}
+	/*
+	 * Check that dual-stack-servers is reasonable.
+	 */
+	if (voptions == 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(voptions, logctx) != ISC_R_SUCCESS)
+			result = ISC_R_FAILURE;
+	}
+
+	/*
+	 * Check that rrset-order is reasonable.
+	 */
+	if (voptions != NULL) {
+		if (check_order(voptions, logctx) != ISC_R_SUCCESS)
+			result = ISC_R_FAILURE;
+	}
+
+	if (voptions != NULL) {
+		(void)cfg_map_get(voptions, "server", &servers);
+		if (servers != NULL &&
+		    check_servers(servers, logctx) != ISC_R_SUCCESS)
+			result = ISC_R_FAILURE;
+	}
+
+	/*
+	 * Check that dnssec-enable/dnssec-validation are sensible.
+	 */
+	obj = NULL;
+	if (voptions != NULL)
+		(void)cfg_map_get(voptions, "dnssec-enable", &obj);
+	if (obj == NULL)
+		(void)cfg_map_get(config, "dnssec-enable", &obj);
+	if (obj == NULL)
+		enablednssec = ISC_TRUE;
+	else
+		enablednssec = cfg_obj_asboolean(obj);
+
+	obj = NULL;
+	if (voptions != NULL)
+		(void)cfg_map_get(voptions, "dnssec-validation", &obj);
+	if (obj == NULL)
+		(void)cfg_map_get(config, "dnssec-validation", &obj);
+	if (obj == NULL)
+		enablevalidation = ISC_FALSE;	/* XXXMPA Change for 9.5. */
+	else
+		enablevalidation = cfg_obj_asboolean(obj);
+
+	if (enablevalidation && !enablednssec)
+		cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
+			    "'dnssec-validation yes;' and 'dnssec-enable no;'");
+
+	if (voptions != NULL)
+		tresult = check_options(voptions, logctx, mctx);
+	else
+		tresult = check_options(config, logctx, mctx);
+	if (tresult != ISC_R_SUCCESS)
+		result = tresult;
+
+	tresult = check_viewacls(&actx, voptions, config, logctx, mctx);
+	if (tresult != ISC_R_SUCCESS)
+		result = tresult;
+
+	cfg_aclconfctx_destroy(&actx);
+
+	return (result);
+}
+
+static const char *
+default_channels[] = {
+	"default_syslog",
+	"default_stderr",
+	"default_debug",
+	"null",
+	NULL
+};
+
+static isc_result_t
+bind9_check_logging(const cfg_obj_t *config, isc_log_t *logctx,
+		    isc_mem_t *mctx)
+{
+	const cfg_obj_t *categories = NULL;
+	const cfg_obj_t *category;
+	const cfg_obj_t *channels = NULL;
+	const cfg_obj_t *channel;
+	const cfg_listelt_t *element;
+	const cfg_listelt_t *delement;
+	const char *channelname;
+	const char *catname;
+	const cfg_obj_t *fileobj = NULL;
+        const cfg_obj_t *syslogobj = NULL;
+        const cfg_obj_t *nullobj = NULL;
+        const cfg_obj_t *stderrobj = NULL;
+        const cfg_obj_t *logobj = NULL;
+	isc_result_t result = ISC_R_SUCCESS;
+	isc_result_t tresult;
+	isc_symtab_t *symtab = NULL;
+	isc_symvalue_t symvalue;
+	int i;
+
+	(void)cfg_map_get(config, "logging", &logobj);
+	if (logobj == NULL)
+		return (ISC_R_SUCCESS);
+
+	result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
+	if (result != ISC_R_SUCCESS)
+		return (result);
+
+	symvalue.as_cpointer = NULL;
+	for (i = 0; default_channels[i] != NULL; i++) {
+		tresult = isc_symtab_define(symtab, default_channels[i], 1,
+					    symvalue, isc_symexists_replace);
+		if (tresult != ISC_R_SUCCESS)
+			result = tresult;
+	}
+
+	cfg_map_get(logobj, "channel", &channels);
+
+	for (element = cfg_list_first(channels);
+	     element != NULL;
+	     element = cfg_list_next(element))
+	{
+		channel = cfg_listelt_value(element);
+		channelname = cfg_obj_asstring(cfg_map_getname(channel));
+		fileobj = syslogobj = nullobj = stderrobj = NULL;
+		(void)cfg_map_get(channel, "file", &fileobj);
+		(void)cfg_map_get(channel, "syslog", &syslogobj);
+		(void)cfg_map_get(channel, "null", &nullobj);
+		(void)cfg_map_get(channel, "stderr", &stderrobj);
+		i = 0;
+		if (fileobj != NULL)
+			i++;
+		if (syslogobj != NULL)
+			i++;
+		if (nullobj != NULL)
+			i++;
+		if (stderrobj != NULL)
+			i++;
+		if (i != 1) {
+			cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
+				    "channel '%s': exactly one of file, syslog, "
+				    "null, and stderr must be present",
+				     channelname);
+			result = ISC_R_FAILURE;
+		}
+		tresult = isc_symtab_define(symtab, channelname, 1,
+					    symvalue, isc_symexists_replace);
+		if (tresult != ISC_R_SUCCESS)
+			result = tresult;
+	}
+
+	cfg_map_get(logobj, "category", &categories);
+
+	for (element = cfg_list_first(categories);
+             element != NULL;
+             element = cfg_list_next(element))
+        {
+		category = cfg_listelt_value(element);
+		catname = cfg_obj_asstring(cfg_tuple_get(category, "name"));
+		if (isc_log_categorybyname(logctx, catname) == NULL) {
+			cfg_obj_log(category, logctx, ISC_LOG_ERROR,
+				    "undefined category: '%s'", catname);
+			result = ISC_R_FAILURE;
+		}
+		channels = cfg_tuple_get(category, "destinations");
+		for (delement = cfg_list_first(channels);
+		     delement != NULL;
+		     delement = cfg_list_next(delement))
+		{
+			channel = cfg_listelt_value(delement);
+			channelname = cfg_obj_asstring(channel);
+			tresult = isc_symtab_lookup(symtab, channelname, 1,
+                                          	    &symvalue);
+			if (tresult != ISC_R_SUCCESS) {
+				cfg_obj_log(channel, logctx, ISC_LOG_ERROR,
+					    "undefined channel: '%s'",
+					    channelname);
+				result = tresult;
+			}
+		}
+	}
+	isc_symtab_destroy(&symtab);
+	return (result);
+}
+
+static isc_result_t
+key_exists(const cfg_obj_t *keylist, const char *keyname) {
+	const cfg_listelt_t *element;
+	const char *str;
+	const cfg_obj_t *obj;
+	
+	if (keylist == NULL)
+		return (ISC_R_NOTFOUND);
+	for (element = cfg_list_first(keylist);
+	     element != NULL;   
+	     element = cfg_list_next(element)) 
+	{
+		obj = cfg_listelt_value(element);
+		str = cfg_obj_asstring(cfg_map_getname(obj));
+		if (strcasecmp(str, keyname) == 0)
+			return (ISC_R_SUCCESS);
+	}
+	return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+bind9_check_controlskeys(const cfg_obj_t *control, const cfg_obj_t *keylist,
+			 isc_log_t *logctx)
+{
+	isc_result_t result = ISC_R_SUCCESS, tresult;
+	const cfg_obj_t *control_keylist;
+	const cfg_listelt_t *element;
+	const cfg_obj_t *key;
+	
+	control_keylist = cfg_tuple_get(control, "keys");
+	if (cfg_obj_isvoid(control_keylist))
+		return (ISC_R_SUCCESS);
+
+	for (element = cfg_list_first(control_keylist);
+	     element != NULL;
+	     element = cfg_list_next(element))
+	{
+		key = cfg_listelt_value(element);
+		tresult = key_exists(keylist, cfg_obj_asstring(key));
+		if (tresult != ISC_R_SUCCESS) {
+			cfg_obj_log(key, logctx, ISC_LOG_ERROR,
+				    "unknown key '%s'", cfg_obj_asstring(key));
+			result = tresult;
+		}
+	}
+	return (result);
+}
+
+static isc_result_t
+bind9_check_controls(const cfg_obj_t *config, isc_log_t *logctx,
+		     isc_mem_t *mctx)
+{
+	isc_result_t result = ISC_R_SUCCESS, tresult;
+	cfg_aclconfctx_t actx;
+	const cfg_listelt_t *element, *element2;
+	const cfg_obj_t *allow;
+	const cfg_obj_t *control;
+	const cfg_obj_t *controls;
+	const cfg_obj_t *controlslist = NULL;
+	const cfg_obj_t *inetcontrols;
+	const cfg_obj_t *unixcontrols;
+	const cfg_obj_t *keylist = NULL;
+	const char *path;
+	isc_uint32_t perm, mask;
+	dns_acl_t *acl = NULL;
+	isc_sockaddr_t addr;
+	int i;
+
+	(void)cfg_map_get(config, "controls", &controlslist);
+	if (controlslist == NULL)
+		return (ISC_R_SUCCESS);
+
+	(void)cfg_map_get(config, "key", &keylist);
+
+	cfg_aclconfctx_init(&actx);
+
+	/*
+	 * INET: Check allow clause.
+	 * UNIX: Check "perm" for sanity, check path length.
+	 */
+	for (element = cfg_list_first(controlslist);
+	     element != NULL;
+	     element = cfg_list_next(element)) {
+		controls = cfg_listelt_value(element);
+		unixcontrols = NULL;
+		inetcontrols = NULL;
+		(void)cfg_map_get(controls, "unix", &unixcontrols);
+		(void)cfg_map_get(controls, "inet", &inetcontrols);
+		for (element2 = cfg_list_first(inetcontrols);
+		     element2 != NULL;
+		     element2 = cfg_list_next(element2)) {
+			control = cfg_listelt_value(element2);
+			allow = cfg_tuple_get(control, "allow");
+			tresult = cfg_acl_fromconfig(allow, config, logctx,
+						     &actx, mctx, &acl);
+			if (acl != NULL)
+				dns_acl_detach(&acl);
+			if (tresult != ISC_R_SUCCESS)
+				result = tresult;
+			tresult = bind9_check_controlskeys(control, keylist,
+							   logctx);
+			if (tresult != ISC_R_SUCCESS)
+				result = tresult;
+		}
+		for (element2 = cfg_list_first(unixcontrols);
+		     element2 != NULL;
+		     element2 = cfg_list_next(element2)) {
+			control = cfg_listelt_value(element2);
+			path = cfg_obj_asstring(cfg_tuple_get(control, "path"));
+			tresult = isc_sockaddr_frompath(&addr, path);
+			if (tresult == ISC_R_NOSPACE) {
+				cfg_obj_log(control, logctx, ISC_LOG_ERROR,
+					    "unix control '%s': path too long",
+					    path);
+				result = ISC_R_NOSPACE;
+			}
+			perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
+			for (i = 0; i < 3; i++) {
+#ifdef NEED_SECURE_DIRECTORY
+				mask = (0x1 << (i*3));	/* SEARCH */
+#else
+				mask = (0x6 << (i*3)); 	/* READ + WRITE */
+#endif
+				if ((perm & mask) == mask)
+					break;
+			}
+			if (i == 0) {
+				cfg_obj_log(control, logctx, ISC_LOG_WARNING,
+					    "unix control '%s' allows access "
+					    "to everyone", path);
+			} else if (i == 3) {
+				cfg_obj_log(control, logctx, ISC_LOG_WARNING,
+					    "unix control '%s' allows access "
+					    "to nobody", path);
+			}
+			tresult = bind9_check_controlskeys(control, keylist,
+							   logctx);
+			if (tresult != ISC_R_SUCCESS)
+				result = tresult;
+		}
+	}
+	cfg_aclconfctx_destroy(&actx);
+	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 (bind9_check_logging(config, logctx, mctx) != ISC_R_SUCCESS)
+		result = ISC_R_FAILURE;
+
+	if (bind9_check_controls(config, logctx, mctx) != 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);
+}
-- 
cgit v1.1