summaryrefslogtreecommitdiffstats
path: root/bin/named
diff options
context:
space:
mode:
Diffstat (limited to 'bin/named')
-rw-r--r--bin/named/Makefile.in145
-rw-r--r--bin/named/builtin.c307
-rw-r--r--bin/named/client.c2643
-rw-r--r--bin/named/config.c796
-rw-r--r--bin/named/control.c186
-rw-r--r--bin/named/controlconf.c1461
-rw-r--r--bin/named/include/named/builtin.h31
-rw-r--r--bin/named/include/named/client.h361
-rw-r--r--bin/named/include/named/config.h79
-rw-r--r--bin/named/include/named/control.h93
-rw-r--r--bin/named/include/named/globals.h120
-rw-r--r--bin/named/include/named/interfacemgr.h176
-rw-r--r--bin/named/include/named/listenlist.h105
-rw-r--r--bin/named/include/named/log.h98
-rw-r--r--bin/named/include/named/logconf.h34
-rw-r--r--bin/named/include/named/lwaddr.h36
-rw-r--r--bin/named/include/named/lwdclient.h234
-rw-r--r--bin/named/include/named/lwresd.h121
-rw-r--r--bin/named/include/named/lwsearch.h112
-rw-r--r--bin/named/include/named/main.h34
-rw-r--r--bin/named/include/named/notify.h55
-rw-r--r--bin/named/include/named/ns_smf_globals.h44
-rw-r--r--bin/named/include/named/query.h87
-rw-r--r--bin/named/include/named/server.h230
-rw-r--r--bin/named/include/named/sortlist.h87
-rw-r--r--bin/named/include/named/tkeyconf.h53
-rw-r--r--bin/named/include/named/tsigconf.h49
-rw-r--r--bin/named/include/named/types.h43
-rw-r--r--bin/named/include/named/update.h50
-rw-r--r--bin/named/include/named/xfrout.h39
-rw-r--r--bin/named/include/named/zoneconf.h63
-rw-r--r--bin/named/interfacemgr.c978
-rw-r--r--bin/named/listenlist.c138
-rw-r--r--bin/named/log.c235
-rw-r--r--bin/named/logconf.c299
-rw-r--r--bin/named/lwaddr.c94
-rw-r--r--bin/named/lwdclient.c467
-rw-r--r--bin/named/lwderror.c80
-rw-r--r--bin/named/lwdgabn.c657
-rw-r--r--bin/named/lwdgnba.c272
-rw-r--r--bin/named/lwdgrbn.c513
-rw-r--r--bin/named/lwdnoop.c88
-rw-r--r--bin/named/lwresd.8223
-rw-r--r--bin/named/lwresd.c869
-rw-r--r--bin/named/lwresd.docbook372
-rw-r--r--bin/named/lwresd.html225
-rw-r--r--bin/named/lwsearch.c206
-rw-r--r--bin/named/main.c926
-rw-r--r--bin/named/named.8236
-rw-r--r--bin/named/named.conf.5520
-rw-r--r--bin/named/named.conf.docbook597
-rw-r--r--bin/named/named.conf.html554
-rw-r--r--bin/named/named.docbook406
-rw-r--r--bin/named/named.html255
-rw-r--r--bin/named/notify.c163
-rw-r--r--bin/named/query.c4603
-rw-r--r--bin/named/server.c4835
-rw-r--r--bin/named/sortlist.c166
-rw-r--r--bin/named/tkeyconf.c120
-rw-r--r--bin/named/tsigconf.c181
-rw-r--r--bin/named/unix/Makefile.in36
-rw-r--r--bin/named/unix/include/named/os.h69
-rw-r--r--bin/named/unix/os.c691
-rw-r--r--bin/named/update.c3026
-rw-r--r--bin/named/xfrout.c1810
-rw-r--r--bin/named/zoneconf.c913
66 files changed, 33795 insertions, 0 deletions
diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in
new file mode 100644
index 0000000..a809e59c
--- /dev/null
+++ b/bin/named/Makefile.in
@@ -0,0 +1,145 @@
+# Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 1998-2002 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: Makefile.in,v 1.80.18.7 2005/09/05 00:18:10 marka Exp $
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+@BIND9_VERSION@
+
+@BIND9_MAKE_INCLUDES@
+
+#
+# Add database drivers here.
+#
+DBDRIVER_OBJS =
+DBDRIVER_SRCS =
+DBDRIVER_INCLUDES =
+DBDRIVER_LIBS =
+
+DLZ_DRIVER_DIR = ${top_srcdir}/contrib/dlz/drivers
+
+DLZDRIVER_OBJS = @DLZ_DRIVER_OBJS@
+DLZDRIVER_SRCS = @DLZ_DRIVER_SRCS@
+DLZDRIVER_INCLUDES = @DLZ_DRIVER_INCLUDES@
+DLZDRIVER_LIBS = @DLZ_DRIVER_LIBS@
+
+CINCLUDES = -I${srcdir}/include -I${srcdir}/unix/include \
+ ${LWRES_INCLUDES} ${DNS_INCLUDES} ${BIND9_INCLUDES} \
+ ${ISCCFG_INCLUDES} ${ISCCC_INCLUDES} ${ISC_INCLUDES} \
+ ${DLZDRIVER_INCLUDES} ${DBDRIVER_INCLUDES}
+
+CDEFINES = @USE_DLZ@
+
+CWARNINGS =
+
+DNSLIBS = ../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@
+ISCCFGLIBS = ../../lib/isccfg/libisccfg.@A@
+ISCCCLIBS = ../../lib/isccc/libisccc.@A@
+ISCLIBS = ../../lib/isc/libisc.@A@
+LWRESLIBS = ../../lib/lwres/liblwres.@A@
+BIND9LIBS = ../../lib/bind9/libbind9.@A@
+
+DNSDEPLIBS = ../../lib/dns/libdns.@A@
+ISCCFGDEPLIBS = ../../lib/isccfg/libisccfg.@A@
+ISCCCDEPLIBS = ../../lib/isccc/libisccc.@A@
+ISCDEPLIBS = ../../lib/isc/libisc.@A@
+LWRESDEPLIBS = ../../lib/lwres/liblwres.@A@
+BIND9DEPLIBS = ../../lib/bind9/libbind9.@A@
+
+DEPLIBS = ${LWRESDEPLIBS} ${DNSDEPLIBS} ${BIND9DEPLIBS} \
+ ${ISCCFGDEPLIBS} ${ISCCCDEPLIBS} ${ISCDEPLIBS}
+
+LIBS = ${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} \
+ ${ISCCFGLIBS} ${ISCCCLIBS} ${ISCLIBS} \
+ ${DLZDRIVER_LIBS} ${DBDRIVER_LIBS} @LIBS@
+
+SUBDIRS = unix
+
+TARGETS = named@EXEEXT@ lwresd@EXEEXT@
+
+OBJS = builtin.@O@ client.@O@ config.@O@ control.@O@ \
+ controlconf.@O@ interfacemgr.@O@ \
+ listenlist.@O@ log.@O@ logconf.@O@ main.@O@ notify.@O@ \
+ query.@O@ server.@O@ sortlist.@O@ \
+ tkeyconf.@O@ tsigconf.@O@ update.@O@ xfrout.@O@ \
+ zoneconf.@O@ \
+ lwaddr.@O@ lwresd.@O@ lwdclient.@O@ lwderror.@O@ lwdgabn.@O@ \
+ lwdgnba.@O@ lwdgrbn.@O@ lwdnoop.@O@ lwsearch.@O@ \
+ ${DLZDRIVER_OBJS} ${DBDRIVER_OBJS}
+
+UOBJS = unix/os.@O@
+
+SRCS = builtin.c client.c config.c control.c \
+ controlconf.c interfacemgr.c \
+ listenlist.c log.c logconf.c main.c notify.c \
+ query.c server.c sortlist.c \
+ tkeyconf.c tsigconf.c update.c xfrout.c \
+ zoneconf.c \
+ lwaddr.c lwresd.c lwdclient.c lwderror.c lwdgabn.c \
+ lwdgnba.c lwdgrbn.c lwdnoop.c lwsearch.c \
+ ${DLZDRIVER_SRCS} ${DBDRIVER_SRCS}
+
+MANPAGES = named.8 lwresd.8 named.conf.5
+
+HTMLPAGES = named.html lwresd.html named.conf.html
+
+MANOBJS = ${MANPAGES} ${HTMLPAGES}
+
+@BIND9_MAKE_RULES@
+
+main.@O@: main.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \
+ -DVERSION=\"${VERSION}\" \
+ -DNS_LOCALSTATEDIR=\"${localstatedir}\" \
+ -DNS_SYSCONFDIR=\"${sysconfdir}\" -c ${srcdir}/main.c
+
+config.@O@: config.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \
+ -DVERSION=\"${VERSION}\" \
+ -DNS_LOCALSTATEDIR=\"${localstatedir}\" \
+ -c ${srcdir}/config.c
+
+named@EXEEXT@: ${OBJS} ${UOBJS} ${DEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ ${OBJS} ${UOBJS} ${LIBS}
+
+lwresd@EXEEXT@: named@EXEEXT@
+ rm -f lwresd@EXEEXT@
+ @LN@ named@EXEEXT@ lwresd@EXEEXT@
+
+doc man:: ${MANOBJS}
+
+docclean manclean maintainer-clean::
+ rm -f ${MANOBJS}
+
+clean distclean maintainer-clean::
+ rm -f ${TARGETS} ${OBJS}
+
+installdirs:
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${sbindir}
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man5
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man8
+
+install:: named@EXEEXT@ lwresd@EXEEXT@ installdirs
+ ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} named@EXEEXT@ ${DESTDIR}${sbindir}
+ (cd ${DESTDIR}${sbindir}; rm -f lwresd@EXEEXT@; @LN@ named@EXEEXT@ lwresd@EXEEXT@)
+ ${INSTALL_DATA} ${srcdir}/named.8 ${DESTDIR}${mandir}/man8
+ ${INSTALL_DATA} ${srcdir}/lwresd.8 ${DESTDIR}${mandir}/man8
+ ${INSTALL_DATA} ${srcdir}/named.conf.5 ${DESTDIR}${mandir}/man5
+
+@DLZ_DRIVER_RULES@
diff --git a/bin/named/builtin.c b/bin/named/builtin.c
new file mode 100644
index 0000000..06cbd4a
--- /dev/null
+++ b/bin/named/builtin.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2004, 2005 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: builtin.c,v 1.5.18.5 2005/08/23 04:12:38 marka Exp $ */
+
+/*! \file
+ * \brief
+ * The built-in "version", "hostname", "id", "authors" and "empty" databases.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+#include <dns/sdb.h>
+
+#include <named/builtin.h>
+#include <named/globals.h>
+#include <named/server.h>
+#include <named/os.h>
+
+typedef struct builtin builtin_t;
+
+static isc_result_t do_version_lookup(dns_sdblookup_t *lookup);
+static isc_result_t do_hostname_lookup(dns_sdblookup_t *lookup);
+static isc_result_t do_authors_lookup(dns_sdblookup_t *lookup);
+static isc_result_t do_id_lookup(dns_sdblookup_t *lookup);
+static isc_result_t do_empty_lookup(dns_sdblookup_t *lookup);
+
+/*
+ * We can't use function pointers as the db_data directly
+ * because ANSI C does not guarantee that function pointers
+ * can safely be cast to void pointers and back.
+ */
+
+struct builtin {
+ isc_result_t (*do_lookup)(dns_sdblookup_t *lookup);
+ char *server;
+ char *contact;
+};
+
+static builtin_t version_builtin = { do_version_lookup, NULL, NULL };
+static builtin_t hostname_builtin = { do_hostname_lookup, NULL, NULL };
+static builtin_t authors_builtin = { do_authors_lookup, NULL, NULL };
+static builtin_t id_builtin = { do_id_lookup, NULL, NULL };
+static builtin_t empty_builtin = { do_empty_lookup, NULL, NULL };
+
+static dns_sdbimplementation_t *builtin_impl;
+
+static isc_result_t
+builtin_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup)
+{
+ builtin_t *b = (builtin_t *) dbdata;
+
+ UNUSED(zone);
+
+ if (strcmp(name, "@") == 0)
+ return (b->do_lookup(lookup));
+ else
+ return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+put_txt(dns_sdblookup_t *lookup, const char *text) {
+ unsigned char buf[256];
+ unsigned int len = strlen(text);
+ if (len > 255)
+ len = 255; /* Silently truncate */
+ buf[0] = len;
+ memcpy(&buf[1], text, len);
+ return (dns_sdb_putrdata(lookup, dns_rdatatype_txt, 0, buf, len + 1));
+}
+
+static isc_result_t
+do_version_lookup(dns_sdblookup_t *lookup) {
+ if (ns_g_server->version_set) {
+ if (ns_g_server->version == NULL)
+ return (ISC_R_SUCCESS);
+ else
+ return (put_txt(lookup, ns_g_server->version));
+ } else {
+ return (put_txt(lookup, ns_g_version));
+ }
+}
+
+static isc_result_t
+do_hostname_lookup(dns_sdblookup_t *lookup) {
+ if (ns_g_server->hostname_set) {
+ if (ns_g_server->hostname == NULL)
+ return (ISC_R_SUCCESS);
+ else
+ return (put_txt(lookup, ns_g_server->hostname));
+ } else {
+ char buf[256];
+ isc_result_t result = ns_os_gethostname(buf, sizeof(buf));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (put_txt(lookup, buf));
+ }
+}
+
+static isc_result_t
+do_authors_lookup(dns_sdblookup_t *lookup) {
+ isc_result_t result;
+ const char **p;
+ static const char *authors[] = {
+ "Mark Andrews",
+ "James Brister",
+ "Ben Cottrell",
+ "Michael Graff",
+ "Andreas Gustafsson",
+ "Bob Halley",
+ "David Lawrence",
+ "Danny Mayer",
+ "Damien Neil",
+ "Matt Nelson",
+ "Michael Sawyer",
+ "Brian Wellington",
+ NULL
+ };
+
+ /*
+ * If a version string is specified, disable the authors.bind zone.
+ */
+ if (ns_g_server->version_set)
+ return (ISC_R_SUCCESS);
+
+ for (p = authors; *p != NULL; p++) {
+ result = put_txt(lookup, *p);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+do_id_lookup(dns_sdblookup_t *lookup) {
+
+ if (ns_g_server->server_usehostname) {
+ char buf[256];
+ isc_result_t result = ns_os_gethostname(buf, sizeof(buf));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (put_txt(lookup, buf));
+ }
+
+ if (ns_g_server->server_id == NULL)
+ return (ISC_R_SUCCESS);
+ else
+ return (put_txt(lookup, ns_g_server->server_id));
+}
+
+static isc_result_t
+do_empty_lookup(dns_sdblookup_t *lookup) {
+
+ UNUSED(lookup);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+builtin_authority(const char *zone, void *dbdata, dns_sdblookup_t *lookup) {
+ isc_result_t result;
+ const char *contact = "hostmaster";
+ const char *server = "@";
+ builtin_t *b = (builtin_t *) dbdata;
+
+ UNUSED(zone);
+ UNUSED(dbdata);
+
+ if (b == &empty_builtin) {
+ server = ".";
+ contact = ".";
+ } else {
+ if (b->server != NULL)
+ server = b->server;
+ if (b->contact != NULL)
+ contact = b->contact;
+ }
+
+ result = dns_sdb_putsoa(lookup, server, contact, 0);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+
+ result = dns_sdb_putrr(lookup, "ns", 0, server);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+builtin_create(const char *zone, int argc, char **argv,
+ void *driverdata, void **dbdata)
+{
+ REQUIRE(argc >= 1);
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ if (strcmp(argv[0], "empty") == 0) {
+ if (argc != 3)
+ return (DNS_R_SYNTAX);
+ } else if (argc != 1)
+ return (DNS_R_SYNTAX);
+
+ if (strcmp(argv[0], "version") == 0)
+ *dbdata = &version_builtin;
+ else if (strcmp(argv[0], "hostname") == 0)
+ *dbdata = &hostname_builtin;
+ else if (strcmp(argv[0], "authors") == 0)
+ *dbdata = &authors_builtin;
+ else if (strcmp(argv[0], "id") == 0)
+ *dbdata = &id_builtin;
+ else if (strcmp(argv[0], "empty") == 0) {
+ builtin_t *empty;
+ char *server;
+ char *contact;
+ /*
+ * We don't want built-in zones to fail. Fallback to
+ * the static configuration if memory allocation fails.
+ */
+ empty = isc_mem_get(ns_g_mctx, sizeof(*empty));
+ server = isc_mem_strdup(ns_g_mctx, argv[1]);
+ contact = isc_mem_strdup(ns_g_mctx, argv[2]);
+ if (empty == NULL || server == NULL || contact == NULL) {
+ *dbdata = &empty_builtin;
+ if (server != NULL)
+ isc_mem_free(ns_g_mctx, server);
+ if (contact != NULL)
+ isc_mem_free(ns_g_mctx, contact);
+ if (empty != NULL)
+ isc_mem_put(ns_g_mctx, empty, sizeof (*empty));
+ } else {
+ memcpy(empty, &empty_builtin, sizeof (empty_builtin));
+ empty->server = server;
+ empty->contact = contact;
+ *dbdata = empty;
+ }
+ } else
+ return (ISC_R_NOTIMPLEMENTED);
+ return (ISC_R_SUCCESS);
+}
+
+static void
+builtin_destroy(const char *zone, void *driverdata, void **dbdata) {
+ builtin_t *b = (builtin_t *) *dbdata;
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ /*
+ * Don't free the static versions.
+ */
+ if (*dbdata == &version_builtin || *dbdata == &hostname_builtin ||
+ *dbdata == &authors_builtin || *dbdata == &id_builtin ||
+ *dbdata == &empty_builtin)
+ return;
+
+ isc_mem_free(ns_g_mctx, b->server);
+ isc_mem_free(ns_g_mctx, b->contact);
+ isc_mem_put(ns_g_mctx, b, sizeof (*b));
+}
+
+static dns_sdbmethods_t builtin_methods = {
+ builtin_lookup,
+ builtin_authority,
+ NULL, /* allnodes */
+ builtin_create,
+ builtin_destroy
+};
+
+isc_result_t
+ns_builtin_init(void) {
+ RUNTIME_CHECK(dns_sdb_register("_builtin", &builtin_methods, NULL,
+ DNS_SDBFLAG_RELATIVEOWNER |
+ DNS_SDBFLAG_RELATIVERDATA,
+ ns_g_mctx, &builtin_impl)
+ == ISC_R_SUCCESS);
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_builtin_deinit(void) {
+ dns_sdb_unregister(&builtin_impl);
+}
diff --git a/bin/named/client.c b/bin/named/client.c
new file mode 100644
index 0000000..b0e9cdd
--- /dev/null
+++ b/bin/named/client.c
@@ -0,0 +1,2643 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: client.c,v 1.219.18.28 2007/08/28 07:20:00 tbox Exp $ */
+
+#include <config.h>
+
+#include <isc/formatcheck.h>
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#include <dns/events.h>
+#include <dns/message.h>
+#include <dns/peer.h>
+#include <dns/rcode.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/resolver.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include <named/interfacemgr.h>
+#include <named/log.h>
+#include <named/notify.h>
+#include <named/server.h>
+#include <named/update.h>
+
+/***
+ *** Client
+ ***/
+
+/*! \file
+ * Client Routines
+ *
+ * Important note!
+ *
+ * All client state changes, other than that from idle to listening, occur
+ * as a result of events. This guarantees serialization and avoids the
+ * need for locking.
+ *
+ * If a routine is ever created that allows someone other than the client's
+ * task to change the client, then the client will have to be locked.
+ */
+
+#define NS_CLIENT_TRACE
+#ifdef NS_CLIENT_TRACE
+#define CTRACE(m) ns_client_log(client, \
+ NS_LOGCATEGORY_CLIENT, \
+ NS_LOGMODULE_CLIENT, \
+ ISC_LOG_DEBUG(3), \
+ "%s", (m))
+#define MTRACE(m) isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_GENERAL, \
+ NS_LOGMODULE_CLIENT, \
+ ISC_LOG_DEBUG(3), \
+ "clientmgr @%p: %s", manager, (m))
+#else
+#define CTRACE(m) ((void)(m))
+#define MTRACE(m) ((void)(m))
+#endif
+
+#define TCP_CLIENT(c) (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
+
+#define TCP_BUFFER_SIZE (65535 + 2)
+#define SEND_BUFFER_SIZE 4096
+#define RECV_BUFFER_SIZE 4096
+
+#ifdef ISC_PLATFORM_USETHREADS
+#define NMCTXS 100
+/*%<
+ * Number of 'mctx pools' for clients. (Should this be configurable?)
+ * When enabling threads, we use a pool of memory contexts shared by
+ * client objects, since concurrent access to a shared context would cause
+ * heavy contentions. The above constant is expected to be enough for
+ * completely avoiding contentions among threads for an authoritative-only
+ * server.
+ */
+#else
+#define NMCTXS 0
+/*%<
+ * If named with built without thread, simply share manager's context. Using
+ * a separate context in this case would simply waste memory.
+ */
+#endif
+
+/*% nameserver client manager structure */
+struct ns_clientmgr {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t * mctx;
+ isc_taskmgr_t * taskmgr;
+ isc_timermgr_t * timermgr;
+ isc_mutex_t lock;
+ /* Locked by lock. */
+ isc_boolean_t exiting;
+ client_list_t active; /*%< Active clients */
+ client_list_t recursing; /*%< Recursing clients */
+ client_list_t inactive; /*%< To be recycled */
+#if NMCTXS > 0
+ /*%< mctx pool for clients. */
+ unsigned int nextmctx;
+ isc_mem_t * mctxpool[NMCTXS];
+#endif
+};
+
+#define MANAGER_MAGIC ISC_MAGIC('N', 'S', 'C', 'm')
+#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, MANAGER_MAGIC)
+
+/*!
+ * Client object states. Ordering is significant: higher-numbered
+ * states are generally "more active", meaning that the client can
+ * have more dynamically allocated data, outstanding events, etc.
+ * In the list below, any such properties listed for state N
+ * also apply to any state > N.
+ *
+ * To force the client into a less active state, set client->newstate
+ * to that state and call exit_check(). This will cause any
+ * activities defined for higher-numbered states to be aborted.
+ */
+
+#define NS_CLIENTSTATE_FREED 0
+/*%<
+ * The client object no longer exists.
+ */
+
+#define NS_CLIENTSTATE_INACTIVE 1
+/*%<
+ * The client object exists and has a task and timer.
+ * Its "query" struct and sendbuf are initialized.
+ * It is on the client manager's list of inactive clients.
+ * It has a message and OPT, both in the reset state.
+ */
+
+#define NS_CLIENTSTATE_READY 2
+/*%<
+ * The client object is either a TCP or a UDP one, and
+ * it is associated with a network interface. It is on the
+ * client manager's list of active clients.
+ *
+ * If it is a TCP client object, it has a TCP listener socket
+ * and an outstanding TCP listen request.
+ *
+ * If it is a UDP client object, it has a UDP listener socket
+ * and an outstanding UDP receive request.
+ */
+
+#define NS_CLIENTSTATE_READING 3
+/*%<
+ * The client object is a TCP client object that has received
+ * a connection. It has a tcpsocket, tcpmsg, TCP quota, and an
+ * outstanding TCP read request. This state is not used for
+ * UDP client objects.
+ */
+
+#define NS_CLIENTSTATE_WORKING 4
+/*%<
+ * The client object has received a request and is working
+ * on it. It has a view, and it may have any of a non-reset OPT,
+ * recursion quota, and an outstanding write request.
+ */
+
+#define NS_CLIENTSTATE_MAX 9
+/*%<
+ * Sentinel value used to indicate "no state". When client->newstate
+ * has this value, we are not attempting to exit the current state.
+ * Must be greater than any valid state.
+ */
+
+/*
+ * Enable ns_client_dropport() by default.
+ */
+#ifndef NS_CLIENT_DROPPORT
+#define NS_CLIENT_DROPPORT 1
+#endif
+
+unsigned int ns_client_requests;
+
+static void client_read(ns_client_t *client);
+static void client_accept(ns_client_t *client);
+static void client_udprecv(ns_client_t *client);
+static void clientmgr_destroy(ns_clientmgr_t *manager);
+static isc_boolean_t exit_check(ns_client_t *client);
+static void ns_client_endrequest(ns_client_t *client);
+static void ns_client_checkactive(ns_client_t *client);
+static void client_start(isc_task_t *task, isc_event_t *event);
+static void client_request(isc_task_t *task, isc_event_t *event);
+static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
+
+void
+ns_client_recursing(ns_client_t *client) {
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ LOCK(&client->manager->lock);
+ ISC_LIST_UNLINK(*client->list, client, link);
+ ISC_LIST_APPEND(client->manager->recursing, client, link);
+ client->list = &client->manager->recursing;
+ UNLOCK(&client->manager->lock);
+}
+
+void
+ns_client_killoldestquery(ns_client_t *client) {
+ ns_client_t *oldest;
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ LOCK(&client->manager->lock);
+ oldest = ISC_LIST_HEAD(client->manager->recursing);
+ if (oldest != NULL) {
+ ns_query_cancel(oldest);
+ ISC_LIST_UNLINK(*oldest->list, oldest, link);
+ ISC_LIST_APPEND(client->manager->active, oldest, link);
+ oldest->list = &client->manager->active;
+ }
+ UNLOCK(&client->manager->lock);
+}
+
+void
+ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
+ isc_result_t result;
+ isc_interval_t interval;
+
+ isc_interval_set(&interval, seconds, 0);
+ result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
+ &interval, ISC_FALSE);
+ client->timerset = ISC_TRUE;
+ if (result != ISC_R_SUCCESS) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
+ "setting timeout: %s",
+ isc_result_totext(result));
+ /* Continue anyway. */
+ }
+}
+
+/*%
+ * Check for a deactivation or shutdown request and take appropriate
+ * action. Returns ISC_TRUE if either is in progress; in this case
+ * the caller must no longer use the client object as it may have been
+ * freed.
+ */
+static isc_boolean_t
+exit_check(ns_client_t *client) {
+ ns_clientmgr_t *locked_manager = NULL;
+ ns_clientmgr_t *destroy_manager = NULL;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ if (client->state <= client->newstate)
+ return (ISC_FALSE); /* Business as usual. */
+
+ INSIST(client->newstate < NS_CLIENTSTATE_WORKING);
+
+ /*
+ * We need to detach from the view early when shutting down
+ * the server to break the following vicious circle:
+ *
+ * - The resolver will not shut down until the view refcount is zero
+ * - The view refcount does not go to zero until all clients detach
+ * - The client does not detach from the view until references is zero
+ * - references does not go to zero until the resolver has shut down
+ *
+ * Keep the view attached until any outstanding updates complete.
+ */
+ if (client->nupdates == 0 &&
+ client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
+ dns_view_detach(&client->view);
+
+ if (client->state == NS_CLIENTSTATE_WORKING) {
+ INSIST(client->newstate <= NS_CLIENTSTATE_READING);
+ /*
+ * Let the update processing complete.
+ */
+ if (client->nupdates > 0)
+ return (ISC_TRUE);
+ /*
+ * We are trying to abort request processing.
+ */
+ if (client->nsends > 0) {
+ isc_socket_t *socket;
+ if (TCP_CLIENT(client))
+ socket = client->tcpsocket;
+ else
+ socket = client->udpsocket;
+ isc_socket_cancel(socket, client->task,
+ ISC_SOCKCANCEL_SEND);
+ }
+
+ if (! (client->nsends == 0 && client->nrecvs == 0 &&
+ client->references == 0))
+ {
+ /*
+ * Still waiting for I/O cancel completion.
+ * or lingering references.
+ */
+ return (ISC_TRUE);
+ }
+ /*
+ * I/O cancel is complete. Burn down all state
+ * related to the current request. Ensure that
+ * the client is on the active list and not the
+ * recursing list.
+ */
+ LOCK(&client->manager->lock);
+ if (client->list == &client->manager->recursing) {
+ ISC_LIST_UNLINK(*client->list, client, link);
+ ISC_LIST_APPEND(client->manager->active, client, link);
+ client->list = &client->manager->active;
+ }
+ UNLOCK(&client->manager->lock);
+ ns_client_endrequest(client);
+
+ client->state = NS_CLIENTSTATE_READING;
+ INSIST(client->recursionquota == NULL);
+ if (NS_CLIENTSTATE_READING == client->newstate) {
+ client_read(client);
+ client->newstate = NS_CLIENTSTATE_MAX;
+ return (ISC_TRUE); /* We're done. */
+ }
+ }
+
+ if (client->state == NS_CLIENTSTATE_READING) {
+ /*
+ * We are trying to abort the current TCP connection,
+ * if any.
+ */
+ INSIST(client->recursionquota == NULL);
+ INSIST(client->newstate <= NS_CLIENTSTATE_READY);
+ if (client->nreads > 0)
+ dns_tcpmsg_cancelread(&client->tcpmsg);
+ if (! client->nreads == 0) {
+ /* Still waiting for read cancel completion. */
+ return (ISC_TRUE);
+ }
+
+ if (client->tcpmsg_valid) {
+ dns_tcpmsg_invalidate(&client->tcpmsg);
+ client->tcpmsg_valid = ISC_FALSE;
+ }
+ if (client->tcpsocket != NULL) {
+ CTRACE("closetcp");
+ isc_socket_detach(&client->tcpsocket);
+ }
+
+ if (client->tcpquota != NULL)
+ isc_quota_detach(&client->tcpquota);
+
+ if (client->timerset) {
+ (void)isc_timer_reset(client->timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE);
+ client->timerset = ISC_FALSE;
+ }
+
+ client->peeraddr_valid = ISC_FALSE;
+
+ client->state = NS_CLIENTSTATE_READY;
+ INSIST(client->recursionquota == NULL);
+
+ /*
+ * Now the client is ready to accept a new TCP connection
+ * or UDP request, but we may have enough clients doing
+ * that already. Check whether this client needs to remain
+ * active and force it to go inactive if not.
+ */
+ ns_client_checkactive(client);
+
+ if (NS_CLIENTSTATE_READY == client->newstate) {
+ if (TCP_CLIENT(client)) {
+ client_accept(client);
+ } else
+ client_udprecv(client);
+ client->newstate = NS_CLIENTSTATE_MAX;
+ return (ISC_TRUE);
+ }
+ }
+
+ if (client->state == NS_CLIENTSTATE_READY) {
+ INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
+ /*
+ * We are trying to enter the inactive state.
+ */
+ if (client->naccepts > 0)
+ isc_socket_cancel(client->tcplistener, client->task,
+ ISC_SOCKCANCEL_ACCEPT);
+
+ if (! (client->naccepts == 0)) {
+ /* Still waiting for accept cancel completion. */
+ return (ISC_TRUE);
+ }
+ /* Accept cancel is complete. */
+
+ if (client->nrecvs > 0)
+ isc_socket_cancel(client->udpsocket, client->task,
+ ISC_SOCKCANCEL_RECV);
+ if (! (client->nrecvs == 0)) {
+ /* Still waiting for recv cancel completion. */
+ return (ISC_TRUE);
+ }
+ /* Recv cancel is complete. */
+
+ if (client->nctls > 0) {
+ /* Still waiting for control event to be delivered */
+ return (ISC_TRUE);
+ }
+
+ /* Deactivate the client. */
+ if (client->interface)
+ ns_interface_detach(&client->interface);
+
+ INSIST(client->naccepts == 0);
+ INSIST(client->recursionquota == NULL);
+ if (client->tcplistener != NULL)
+ isc_socket_detach(&client->tcplistener);
+
+ if (client->udpsocket != NULL)
+ isc_socket_detach(&client->udpsocket);
+
+ if (client->dispatch != NULL)
+ dns_dispatch_detach(&client->dispatch);
+
+ client->attributes = 0;
+ client->mortal = ISC_FALSE;
+
+ LOCK(&client->manager->lock);
+ /*
+ * Put the client on the inactive list. If we are aiming for
+ * the "freed" state, it will be removed from the inactive
+ * list shortly, and we need to keep the manager locked until
+ * that has been done, lest the manager decide to reactivate
+ * the dying client inbetween.
+ */
+ locked_manager = client->manager;
+ ISC_LIST_UNLINK(*client->list, client, link);
+ ISC_LIST_APPEND(client->manager->inactive, client, link);
+ client->list = &client->manager->inactive;
+ client->state = NS_CLIENTSTATE_INACTIVE;
+ INSIST(client->recursionquota == NULL);
+
+ if (client->state == client->newstate) {
+ client->newstate = NS_CLIENTSTATE_MAX;
+ goto unlock;
+ }
+ }
+
+ if (client->state == NS_CLIENTSTATE_INACTIVE) {
+ INSIST(client->newstate == NS_CLIENTSTATE_FREED);
+ /*
+ * We are trying to free the client.
+ *
+ * When "shuttingdown" is true, either the task has received
+ * its shutdown event or no shutdown event has ever been
+ * set up. Thus, we have no outstanding shutdown
+ * event at this point.
+ */
+ REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);
+
+ INSIST(client->recursionquota == NULL);
+
+ ns_query_free(client);
+ isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
+ isc_event_free((isc_event_t **)&client->sendevent);
+ isc_event_free((isc_event_t **)&client->recvevent);
+ isc_timer_detach(&client->timer);
+
+ if (client->tcpbuf != NULL)
+ isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ if (client->opt != NULL) {
+ INSIST(dns_rdataset_isassociated(client->opt));
+ dns_rdataset_disassociate(client->opt);
+ dns_message_puttemprdataset(client->message, &client->opt);
+ }
+ dns_message_destroy(&client->message);
+ if (client->manager != NULL) {
+ ns_clientmgr_t *manager = client->manager;
+ if (locked_manager == NULL) {
+ LOCK(&manager->lock);
+ locked_manager = manager;
+ }
+ ISC_LIST_UNLINK(*client->list, client, link);
+ client->list = NULL;
+ if (manager->exiting &&
+ ISC_LIST_EMPTY(manager->active) &&
+ ISC_LIST_EMPTY(manager->inactive) &&
+ ISC_LIST_EMPTY(manager->recursing))
+ destroy_manager = manager;
+ }
+ /*
+ * Detaching the task must be done after unlinking from
+ * the manager's lists because the manager accesses
+ * client->task.
+ */
+ if (client->task != NULL)
+ isc_task_detach(&client->task);
+
+ CTRACE("free");
+ client->magic = 0;
+ isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
+
+ goto unlock;
+ }
+
+ unlock:
+ if (locked_manager != NULL) {
+ UNLOCK(&locked_manager->lock);
+ locked_manager = NULL;
+ }
+
+ /*
+ * Only now is it safe to destroy the client manager (if needed),
+ * because we have accessed its lock for the last time.
+ */
+ if (destroy_manager != NULL)
+ clientmgr_destroy(destroy_manager);
+
+ return (ISC_TRUE);
+}
+
+/*%
+ * The client's task has received the client's control event
+ * as part of the startup process.
+ */
+static void
+client_start(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client = (ns_client_t *) event->ev_arg;
+
+ INSIST(task == client->task);
+
+ UNUSED(task);
+
+ INSIST(client->nctls == 1);
+ client->nctls--;
+
+ if (exit_check(client))
+ return;
+
+ if (TCP_CLIENT(client)) {
+ client_accept(client);
+ } else {
+ client_udprecv(client);
+ }
+}
+
+
+/*%
+ * The client's task has received a shutdown event.
+ */
+static void
+client_shutdown(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client;
+
+ REQUIRE(event != NULL);
+ REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
+ client = event->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+
+ UNUSED(task);
+
+ CTRACE("shutdown");
+
+ isc_event_free(&event);
+
+ if (client->shutdown != NULL) {
+ (client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
+ client->shutdown = NULL;
+ client->shutdown_arg = NULL;
+ }
+
+ client->newstate = NS_CLIENTSTATE_FREED;
+ (void)exit_check(client);
+}
+
+static void
+ns_client_endrequest(ns_client_t *client) {
+ INSIST(client->naccepts == 0);
+ INSIST(client->nreads == 0);
+ INSIST(client->nsends == 0);
+ INSIST(client->nrecvs == 0);
+ INSIST(client->nupdates == 0);
+ INSIST(client->state == NS_CLIENTSTATE_WORKING);
+
+ CTRACE("endrequest");
+
+ if (client->next != NULL) {
+ (client->next)(client);
+ client->next = NULL;
+ }
+
+ if (client->view != NULL)
+ dns_view_detach(&client->view);
+ if (client->opt != NULL) {
+ INSIST(dns_rdataset_isassociated(client->opt));
+ dns_rdataset_disassociate(client->opt);
+ dns_message_puttemprdataset(client->message, &client->opt);
+ }
+
+ client->udpsize = 512;
+ client->extflags = 0;
+ client->ednsversion = -1;
+ dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
+
+ if (client->recursionquota != NULL)
+ isc_quota_detach(&client->recursionquota);
+
+ /*
+ * Clear all client attributes that are specific to
+ * the request; that's all except the TCP flag.
+ */
+ client->attributes &= NS_CLIENTATTR_TCP;
+}
+
+static void
+ns_client_checkactive(ns_client_t *client) {
+ if (client->mortal) {
+ /*
+ * This client object should normally go inactive
+ * at this point, but if we have fewer active client
+ * objects than desired due to earlier quota exhaustion,
+ * keep it active to make up for the shortage.
+ */
+ isc_boolean_t need_another_client = ISC_FALSE;
+ if (TCP_CLIENT(client)) {
+ LOCK(&client->interface->lock);
+ if (client->interface->ntcpcurrent <
+ client->interface->ntcptarget)
+ need_another_client = ISC_TRUE;
+ UNLOCK(&client->interface->lock);
+ } else {
+ /*
+ * The UDP client quota is enforced by making
+ * requests fail rather than by not listening
+ * for new ones. Therefore, there is always a
+ * full set of UDP clients listening.
+ */
+ }
+ if (! need_another_client) {
+ /*
+ * We don't need this client object. Recycle it.
+ */
+ if (client->newstate >= NS_CLIENTSTATE_INACTIVE)
+ client->newstate = NS_CLIENTSTATE_INACTIVE;
+ }
+ }
+}
+
+void
+ns_client_next(ns_client_t *client, isc_result_t result) {
+ int newstate;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
+ client->state == NS_CLIENTSTATE_READING);
+
+ CTRACE("next");
+
+ if (result != ISC_R_SUCCESS)
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "request failed: %s", isc_result_totext(result));
+
+ /*
+ * An error processing a TCP request may have left
+ * the connection out of sync. To be safe, we always
+ * sever the connection when result != ISC_R_SUCCESS.
+ */
+ if (result == ISC_R_SUCCESS && TCP_CLIENT(client))
+ newstate = NS_CLIENTSTATE_READING;
+ else
+ newstate = NS_CLIENTSTATE_READY;
+
+ if (client->newstate > newstate)
+ client->newstate = newstate;
+ (void)exit_check(client);
+}
+
+
+static void
+client_senddone(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client;
+ isc_socketevent_t *sevent = (isc_socketevent_t *) event;
+
+ REQUIRE(sevent != NULL);
+ REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
+ client = sevent->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+ REQUIRE(sevent == client->sendevent);
+
+ UNUSED(task);
+
+ CTRACE("senddone");
+
+ if (sevent->result != ISC_R_SUCCESS)
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
+ "error sending response: %s",
+ isc_result_totext(sevent->result));
+
+ INSIST(client->nsends > 0);
+ client->nsends--;
+
+ if (client->tcpbuf != NULL) {
+ INSIST(TCP_CLIENT(client));
+ isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ client->tcpbuf = NULL;
+ }
+
+ if (exit_check(client))
+ return;
+
+ ns_client_next(client, ISC_R_SUCCESS);
+}
+
+/*%
+ * We only want to fail with ISC_R_NOSPACE when called from
+ * ns_client_sendraw() and not when called from ns_client_send(),
+ * tcpbuffer is NULL when called from ns_client_sendraw() and
+ * length != 0. tcpbuffer != NULL when called from ns_client_send()
+ * and length == 0.
+ */
+
+static isc_result_t
+client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
+ isc_buffer_t *tcpbuffer, isc_uint32_t length,
+ unsigned char *sendbuf, unsigned char **datap)
+{
+ unsigned char *data;
+ isc_uint32_t bufsize;
+ isc_result_t result;
+
+ INSIST(datap != NULL);
+ INSIST((tcpbuffer == NULL && length != 0) ||
+ (tcpbuffer != NULL && length == 0));
+
+ if (TCP_CLIENT(client)) {
+ INSIST(client->tcpbuf == NULL);
+ if (length + 2 > TCP_BUFFER_SIZE) {
+ result = ISC_R_NOSPACE;
+ goto done;
+ }
+ client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE);
+ if (client->tcpbuf == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto done;
+ }
+ data = client->tcpbuf;
+ if (tcpbuffer != NULL) {
+ isc_buffer_init(tcpbuffer, data, TCP_BUFFER_SIZE);
+ isc_buffer_init(buffer, data + 2, TCP_BUFFER_SIZE - 2);
+ } else {
+ isc_buffer_init(buffer, data, TCP_BUFFER_SIZE);
+ INSIST(length <= 0xffff);
+ isc_buffer_putuint16(buffer, (isc_uint16_t)length);
+ }
+ } else {
+ data = sendbuf;
+ if (client->udpsize < SEND_BUFFER_SIZE)
+ bufsize = client->udpsize;
+ else
+ bufsize = SEND_BUFFER_SIZE;
+ if (length > bufsize) {
+ result = ISC_R_NOSPACE;
+ goto done;
+ }
+ isc_buffer_init(buffer, data, bufsize);
+ }
+ *datap = data;
+ result = ISC_R_SUCCESS;
+
+ done:
+ return (result);
+}
+
+static isc_result_t
+client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) {
+ struct in6_pktinfo *pktinfo;
+ isc_result_t result;
+ isc_region_t r;
+ isc_sockaddr_t *address;
+ isc_socket_t *socket;
+ isc_netaddr_t netaddr;
+ int match;
+ unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
+
+ if (TCP_CLIENT(client)) {
+ socket = client->tcpsocket;
+ address = NULL;
+ } else {
+ socket = client->udpsocket;
+ address = &client->peeraddr;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ if (ns_g_server->blackholeacl != NULL &&
+ dns_acl_match(&netaddr, NULL,
+ ns_g_server->blackholeacl,
+ &ns_g_server->aclenv,
+ &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ return (DNS_R_BLACKHOLED);
+ sockflags |= ISC_SOCKFLAG_NORETRY;
+ }
+
+ if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
+ (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
+ pktinfo = &client->pktinfo;
+ else
+ pktinfo = NULL;
+
+ isc_buffer_usedregion(buffer, &r);
+
+ CTRACE("sendto");
+
+ result = isc_socket_sendto2(socket, &r, client->task,
+ address, pktinfo,
+ client->sendevent, sockflags);
+ if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
+ client->nsends++;
+ if (result == ISC_R_SUCCESS)
+ client_senddone(client->task,
+ (isc_event_t *)client->sendevent);
+ result = ISC_R_SUCCESS;
+ }
+ return (result);
+}
+
+void
+ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
+ isc_result_t result;
+ unsigned char *data;
+ isc_buffer_t buffer;
+ isc_region_t r;
+ isc_region_t *mr;
+ unsigned char sendbuf[SEND_BUFFER_SIZE];
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ CTRACE("sendraw");
+
+ mr = dns_message_getrawmessage(message);
+ if (mr == NULL) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+
+ result = client_allocsendbuf(client, &buffer, NULL, mr->length,
+ sendbuf, &data);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ /*
+ * Copy message to buffer and fixup id.
+ */
+ isc_buffer_availableregion(&buffer, &r);
+ result = isc_buffer_copyregion(&buffer, mr);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ r.base[0] = (client->message->id >> 8) & 0xff;
+ r.base[1] = client->message->id & 0xff;
+
+ result = client_sendpkg(client, &buffer);
+ if (result == ISC_R_SUCCESS)
+ return;
+
+ done:
+ if (client->tcpbuf != NULL) {
+ isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ client->tcpbuf = NULL;
+ }
+ ns_client_next(client, result);
+}
+
+void
+ns_client_send(ns_client_t *client) {
+ isc_result_t result;
+ unsigned char *data;
+ isc_buffer_t buffer;
+ isc_buffer_t tcpbuffer;
+ isc_region_t r;
+ dns_compress_t cctx;
+ isc_boolean_t cleanup_cctx = ISC_FALSE;
+ unsigned char sendbuf[SEND_BUFFER_SIZE];
+ unsigned int dnssec_opts;
+ unsigned int preferred_glue;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ CTRACE("send");
+
+ if ((client->attributes & NS_CLIENTATTR_RA) != 0)
+ client->message->flags |= DNS_MESSAGEFLAG_RA;
+
+ if ((client->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
+ dnssec_opts = 0;
+ else
+ dnssec_opts = DNS_MESSAGERENDER_OMITDNSSEC;
+
+ preferred_glue = 0;
+ if (client->view != NULL) {
+ if (client->view->preferred_glue == dns_rdatatype_a)
+ preferred_glue = DNS_MESSAGERENDER_PREFER_A;
+ else if (client->view->preferred_glue == dns_rdatatype_aaaa)
+ preferred_glue = DNS_MESSAGERENDER_PREFER_AAAA;
+ }
+
+ /*
+ * XXXRTH The following doesn't deal with TCP buffer resizing.
+ */
+ result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0,
+ sendbuf, &data);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ result = dns_compress_init(&cctx, -1, client->mctx);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ cleanup_cctx = ISC_TRUE;
+
+ result = dns_message_renderbegin(client->message, &cctx, &buffer);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ if (client->opt != NULL) {
+ result = dns_message_setopt(client->message, client->opt);
+ /*
+ * XXXRTH dns_message_setopt() should probably do this...
+ */
+ client->opt = NULL;
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ }
+ result = dns_message_rendersection(client->message,
+ DNS_SECTION_QUESTION, 0);
+ if (result == ISC_R_NOSPACE) {
+ client->message->flags |= DNS_MESSAGEFLAG_TC;
+ goto renderend;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ result = dns_message_rendersection(client->message,
+ DNS_SECTION_ANSWER,
+ DNS_MESSAGERENDER_PARTIAL |
+ dnssec_opts);
+ if (result == ISC_R_NOSPACE) {
+ client->message->flags |= DNS_MESSAGEFLAG_TC;
+ goto renderend;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ result = dns_message_rendersection(client->message,
+ DNS_SECTION_AUTHORITY,
+ DNS_MESSAGERENDER_PARTIAL |
+ dnssec_opts);
+ if (result == ISC_R_NOSPACE) {
+ client->message->flags |= DNS_MESSAGEFLAG_TC;
+ goto renderend;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ result = dns_message_rendersection(client->message,
+ DNS_SECTION_ADDITIONAL,
+ preferred_glue | dnssec_opts);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
+ goto done;
+ renderend:
+ result = dns_message_renderend(client->message);
+
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ if (cleanup_cctx) {
+ dns_compress_invalidate(&cctx);
+ cleanup_cctx = ISC_FALSE;
+ }
+
+ if (TCP_CLIENT(client)) {
+ isc_buffer_usedregion(&buffer, &r);
+ isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t) r.length);
+ isc_buffer_add(&tcpbuffer, r.length);
+ result = client_sendpkg(client, &tcpbuffer);
+ } else
+ result = client_sendpkg(client, &buffer);
+ if (result == ISC_R_SUCCESS)
+ return;
+
+ done:
+ if (client->tcpbuf != NULL) {
+ isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ client->tcpbuf = NULL;
+ }
+
+ if (cleanup_cctx)
+ dns_compress_invalidate(&cctx);
+
+ ns_client_next(client, result);
+}
+
+#if NS_CLIENT_DROPPORT
+#define DROPPORT_NO 0
+#define DROPPORT_REQUEST 1
+#define DROPPORT_RESPONSE 2
+/*%
+ * ns_client_dropport determines if certain requests / responses
+ * should be dropped based on the port number.
+ *
+ * Returns:
+ * \li 0: Don't drop.
+ * \li 1: Drop request.
+ * \li 2: Drop (error) response.
+ */
+static int
+ns_client_dropport(in_port_t port) {
+ switch (port) {
+ case 7: /* echo */
+ case 13: /* daytime */
+ case 19: /* chargen */
+ case 37: /* time */
+ return (DROPPORT_REQUEST);
+ case 464: /* kpasswd */
+ return (DROPPORT_RESPONSE);
+ }
+ return (DROPPORT_NO);
+}
+#endif
+
+void
+ns_client_error(ns_client_t *client, isc_result_t result) {
+ dns_rcode_t rcode;
+ dns_message_t *message;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ CTRACE("error");
+
+ message = client->message;
+ rcode = dns_result_torcode(result);
+
+#if NS_CLIENT_DROPPORT
+ /*
+ * Don't send FORMERR to ports on the drop port list.
+ */
+ if (rcode == dns_rcode_formerr &&
+ ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) !=
+ DROPPORT_NO) {
+ char buf[64];
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, buf, sizeof(buf) - 1);
+ if (dns_rcode_totext(rcode, &b) != ISC_R_SUCCESS)
+ isc_buffer_putstr(&b, "UNKNOWN RCODE");
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "dropped error (%.*s) response: suspicious port",
+ (int)isc_buffer_usedlength(&b), buf);
+ ns_client_next(client, ISC_R_SUCCESS);
+ return;
+ }
+#endif
+
+ /*
+ * Message may be an in-progress reply that we had trouble
+ * with, in which case QR will be set. We need to clear QR before
+ * calling dns_message_reply() to avoid triggering an assertion.
+ */
+ message->flags &= ~DNS_MESSAGEFLAG_QR;
+ /*
+ * AA and AD shouldn't be set.
+ */
+ message->flags &= ~(DNS_MESSAGEFLAG_AA | DNS_MESSAGEFLAG_AD);
+ result = dns_message_reply(message, ISC_TRUE);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * It could be that we've got a query with a good header,
+ * but a bad question section, so we try again with
+ * want_question_section set to ISC_FALSE.
+ */
+ result = dns_message_reply(message, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_next(client, result);
+ return;
+ }
+ }
+ message->rcode = rcode;
+
+ /*
+ * FORMERR loop avoidance: If we sent a FORMERR message
+ * with the same ID to the same client less than two
+ * seconds ago, assume that we are in an infinite error
+ * packet dialog with a server for some protocol whose
+ * error responses look enough like DNS queries to
+ * elicit a FORMERR response. Drop a packet to break
+ * the loop.
+ */
+ if (rcode == dns_rcode_formerr) {
+ if (isc_sockaddr_equal(&client->peeraddr,
+ &client->formerrcache.addr) &&
+ message->id == client->formerrcache.id &&
+ client->requesttime - client->formerrcache.time < 2) {
+ /* Drop packet. */
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+ "possible error packet loop, "
+ "FORMERR dropped");
+ ns_client_next(client, result);
+ return;
+ }
+ client->formerrcache.addr = client->peeraddr;
+ client->formerrcache.time = client->requesttime;
+ client->formerrcache.id = message->id;
+ }
+ ns_client_send(client);
+}
+
+static inline isc_result_t
+client_addopt(ns_client_t *client) {
+ dns_rdataset_t *rdataset;
+ dns_rdatalist_t *rdatalist;
+ dns_rdata_t *rdata;
+ isc_result_t result;
+ dns_view_t *view;
+ dns_resolver_t *resolver;
+ isc_uint16_t udpsize;
+
+ REQUIRE(client->opt == NULL); /* XXXRTH free old. */
+
+ rdatalist = NULL;
+ result = dns_message_gettemprdatalist(client->message, &rdatalist);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdata = NULL;
+ result = dns_message_gettemprdata(client->message, &rdata);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdataset = NULL;
+ result = dns_message_gettemprdataset(client->message, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_init(rdataset);
+
+ rdatalist->type = dns_rdatatype_opt;
+ rdatalist->covers = 0;
+
+ /*
+ * Set the maximum UDP buffer size.
+ */
+ view = client->view;
+ resolver = (view != NULL) ? view->resolver : NULL;
+ if (resolver != NULL)
+ udpsize = dns_resolver_getudpsize(resolver);
+ else
+ udpsize = ns_g_udpsize;
+ rdatalist->rdclass = udpsize;
+
+ /*
+ * Set EXTENDED-RCODE, VERSION and Z to 0.
+ */
+ rdatalist->ttl = (client->extflags & DNS_MESSAGEEXTFLAG_REPLYPRESERVE);
+
+ /*
+ * No EDNS options in the default case.
+ */
+ rdata->data = NULL;
+ rdata->length = 0;
+ rdata->rdclass = rdatalist->rdclass;
+ rdata->type = rdatalist->type;
+ rdata->flags = 0;
+
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset)
+ == ISC_R_SUCCESS);
+
+ client->opt = rdataset;
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_boolean_t
+allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl) {
+ int match;
+ isc_result_t result;
+
+ if (acl == NULL)
+ return (ISC_TRUE);
+ result = dns_acl_match(addr, signer, acl, &ns_g_server->aclenv,
+ &match, NULL);
+ if (result == ISC_R_SUCCESS && match > 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+/*
+ * Callback to see if a non-recursive query coming from 'srcaddr' to
+ * 'destaddr', with optional key 'mykey' for class 'rdclass' would be
+ * delivered to 'myview'.
+ *
+ * We run this unlocked as both the view list and the interface list
+ * are updated when the approprite task has exclusivity.
+ */
+isc_boolean_t
+ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *dstaddr,
+ dns_rdataclass_t rdclass, void *arg)
+{
+ dns_view_t *view;
+ dns_tsigkey_t *key = NULL;
+ dns_name_t *tsig = NULL;
+ isc_netaddr_t netsrc;
+ isc_netaddr_t netdst;
+
+ UNUSED(arg);
+
+ if (!ns_interfacemgr_listeningon(ns_g_server->interfacemgr, dstaddr))
+ return (ISC_FALSE);
+
+ isc_netaddr_fromsockaddr(&netsrc, srcaddr);
+ isc_netaddr_fromsockaddr(&netdst, dstaddr);
+
+ for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+
+ if (view->matchrecursiveonly)
+ continue;
+
+ if (rdclass != view->rdclass)
+ continue;
+
+ if (mykey != NULL) {
+ isc_boolean_t match;
+ isc_result_t result;
+
+ tsig = &mykey->name;
+ result = dns_view_gettsig(view, tsig, &key);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ match = dst_key_compare(mykey->key, key->key);
+ dns_tsigkey_detach(&key);
+ if (!match)
+ continue;
+ }
+
+ if (allowed(&netsrc, tsig, view->matchclients) &&
+ allowed(&netdst, tsig, view->matchdestinations))
+ break;
+ }
+ return (ISC_TF(view == myview));
+}
+
+/*
+ * Handle an incoming request event from the socket (UDP case)
+ * or tcpmsg (TCP case).
+ */
+static void
+client_request(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client;
+ isc_socketevent_t *sevent;
+ isc_result_t result;
+ isc_result_t sigresult = ISC_R_SUCCESS;
+ isc_buffer_t *buffer;
+ isc_buffer_t tbuffer;
+ dns_view_t *view;
+ dns_rdataset_t *opt;
+ isc_boolean_t ra; /* Recursion available. */
+ isc_netaddr_t netaddr;
+ isc_netaddr_t destaddr;
+ int match;
+ dns_messageid_t id;
+ unsigned int flags;
+ isc_boolean_t notimp;
+
+ REQUIRE(event != NULL);
+ client = event->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+
+ INSIST(client->recursionquota == NULL);
+
+ INSIST(client->state ==
+ TCP_CLIENT(client) ?
+ NS_CLIENTSTATE_READING :
+ NS_CLIENTSTATE_READY);
+
+ ns_client_requests++;
+
+ if (event->ev_type == ISC_SOCKEVENT_RECVDONE) {
+ INSIST(!TCP_CLIENT(client));
+ sevent = (isc_socketevent_t *)event;
+ REQUIRE(sevent == client->recvevent);
+ isc_buffer_init(&tbuffer, sevent->region.base, sevent->n);
+ isc_buffer_add(&tbuffer, sevent->n);
+ buffer = &tbuffer;
+ result = sevent->result;
+ if (result == ISC_R_SUCCESS) {
+ client->peeraddr = sevent->address;
+ client->peeraddr_valid = ISC_TRUE;
+ }
+ if ((sevent->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
+ client->attributes |= NS_CLIENTATTR_PKTINFO;
+ client->pktinfo = sevent->pktinfo;
+ }
+ if ((sevent->attributes & ISC_SOCKEVENTATTR_MULTICAST) != 0)
+ client->attributes |= NS_CLIENTATTR_MULTICAST;
+ client->nrecvs--;
+ } else {
+ INSIST(TCP_CLIENT(client));
+ REQUIRE(event->ev_type == DNS_EVENT_TCPMSG);
+ REQUIRE(event->ev_sender == &client->tcpmsg);
+ buffer = &client->tcpmsg.buffer;
+ result = client->tcpmsg.result;
+ INSIST(client->nreads == 1);
+ /*
+ * client->peeraddr was set when the connection was accepted.
+ */
+ client->nreads--;
+ }
+
+ if (exit_check(client))
+ goto cleanup;
+ client->state = client->newstate = NS_CLIENTSTATE_WORKING;
+
+ isc_task_getcurrenttime(task, &client->requesttime);
+ client->now = client->requesttime;
+
+ if (result != ISC_R_SUCCESS) {
+ if (TCP_CLIENT(client)) {
+ ns_client_next(client, result);
+ } else {
+ if (result != ISC_R_CANCELED)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT,
+ ISC_LOG_ERROR,
+ "UDP client handler shutting "
+ "down due to fatal receive "
+ "error: %s",
+ isc_result_totext(result));
+ isc_task_shutdown(client->task);
+ }
+ goto cleanup;
+ }
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+#if NS_CLIENT_DROPPORT
+ if (ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) ==
+ DROPPORT_REQUEST) {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "dropped request: suspicious port");
+ ns_client_next(client, ISC_R_SUCCESS);
+ goto cleanup;
+ }
+#endif
+
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "%s request",
+ TCP_CLIENT(client) ? "TCP" : "UDP");
+
+ /*
+ * Check the blackhole ACL for UDP only, since TCP is done in
+ * client_newconn.
+ */
+ if (!TCP_CLIENT(client)) {
+
+ if (ns_g_server->blackholeacl != NULL &&
+ dns_acl_match(&netaddr, NULL, ns_g_server->blackholeacl,
+ &ns_g_server->aclenv,
+ &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "blackholed UDP datagram");
+ ns_client_next(client, ISC_R_SUCCESS);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Silently drop multicast requests for the present.
+ * XXXMPA look at when/if mDNS spec stabilizes.
+ */
+ if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+ "dropping multicast request");
+ ns_client_next(client, DNS_R_REFUSED);
+ goto cleanup;
+ }
+
+ result = dns_message_peekheader(buffer, &id, &flags);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * There isn't enough header to determine whether
+ * this was a request or a response. Drop it.
+ */
+ ns_client_next(client, result);
+ goto cleanup;
+ }
+
+ /*
+ * The client object handles requests, not responses.
+ * If this is a UDP response, forward it to the dispatcher.
+ * If it's a TCP response, discard it here.
+ */
+ if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
+ if (TCP_CLIENT(client)) {
+ CTRACE("unexpected response");
+ ns_client_next(client, DNS_R_FORMERR);
+ goto cleanup;
+ } else {
+ dns_dispatch_importrecv(client->dispatch, event);
+ ns_client_next(client, ISC_R_SUCCESS);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Hash the incoming request here as it is after
+ * dns_dispatch_importrecv().
+ */
+ dns_dispatch_hash(&client->now, sizeof(client->now));
+ dns_dispatch_hash(isc_buffer_base(buffer),
+ isc_buffer_usedlength(buffer));
+
+ /*
+ * It's a request. Parse it.
+ */
+ result = dns_message_parse(client->message, buffer, 0);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Parsing the request failed. Send a response
+ * (typically FORMERR or SERVFAIL).
+ */
+ ns_client_error(client, result);
+ goto cleanup;
+ }
+
+ switch (client->message->opcode) {
+ case dns_opcode_query:
+ case dns_opcode_update:
+ case dns_opcode_notify:
+ notimp = ISC_FALSE;
+ break;
+ case dns_opcode_iquery:
+ default:
+ notimp = ISC_TRUE;
+ break;
+ }
+
+ client->message->rcode = dns_rcode_noerror;
+
+ /* RFC1123 section 6.1.3.2 */
+ if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0)
+ client->message->flags &= ~DNS_MESSAGEFLAG_RD;
+
+ /*
+ * Deal with EDNS.
+ */
+ opt = dns_message_getopt(client->message);
+ if (opt != NULL) {
+ /*
+ * Set the client's UDP buffer size.
+ */
+ client->udpsize = opt->rdclass;
+
+ /*
+ * If the requested UDP buffer size is less than 512,
+ * ignore it and use 512.
+ */
+ if (client->udpsize < 512)
+ client->udpsize = 512;
+
+ /*
+ * Get the flags out of the OPT record.
+ */
+ client->extflags = (isc_uint16_t)(opt->ttl & 0xFFFF);
+
+ /*
+ * Do we understand this version of EDNS?
+ *
+ * XXXRTH need library support for this!
+ */
+ client->ednsversion = (opt->ttl & 0x00FF0000) >> 16;
+ if (client->ednsversion > 0) {
+ result = client_addopt(client);
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_BADVERS;
+ ns_client_error(client, result);
+ goto cleanup;
+ }
+ /*
+ * Create an OPT for our reply.
+ */
+ result = client_addopt(client);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_error(client, result);
+ goto cleanup;
+ }
+ }
+
+ if (client->message->rdclass == 0) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+ "message class could not be determined");
+ ns_client_dumpmessage(client,
+ "message class could not be determined");
+ ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
+ goto cleanup;
+ }
+
+ /*
+ * Determine the destination address. If the receiving interface is
+ * bound to a specific address, we simply use it regardless of the
+ * address family. All IPv4 queries should fall into this case.
+ * Otherwise, if this is a TCP query, get the address from the
+ * receiving socket (this needs a system call and can be heavy).
+ * For IPv6 UDP queries, we get this from the pktinfo structure (if
+ * supported).
+ * If all the attempts fail (this can happen due to memory shortage,
+ * etc), we regard this as an error for safety.
+ */
+ if ((client->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
+ isc_netaddr_fromsockaddr(&destaddr, &client->interface->addr);
+ else {
+ result = ISC_R_FAILURE;
+
+ if (TCP_CLIENT(client)) {
+ isc_sockaddr_t destsockaddr;
+
+ result = isc_socket_getsockname(client->tcpsocket,
+ &destsockaddr);
+ if (result == ISC_R_SUCCESS)
+ isc_netaddr_fromsockaddr(&destaddr,
+ &destsockaddr);
+ }
+ if (result != ISC_R_SUCCESS &&
+ client->interface->addr.type.sa.sa_family == AF_INET6 &&
+ (client->attributes & NS_CLIENTATTR_PKTINFO) != 0) {
+ isc_uint32_t zone = 0;
+
+ /*
+ * XXXJT technically, we should convert the receiving
+ * interface ID to a proper scope zone ID. However,
+ * due to the fact there is no standard API for this,
+ * we only handle link-local addresses and use the
+ * interface index as link ID. Despite the assumption,
+ * it should cover most typical cases.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&client->pktinfo.ipi6_addr))
+ zone = (isc_uint32_t)client->pktinfo.ipi6_ifindex;
+
+ isc_netaddr_fromin6(&destaddr,
+ &client->pktinfo.ipi6_addr);
+ isc_netaddr_setzone(&destaddr, zone);
+ result = ISC_R_SUCCESS;
+ }
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "failed to get request's "
+ "destination: %s",
+ isc_result_totext(result));
+ ns_client_next(client, ISC_R_SUCCESS);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Find a view that matches the client's source address.
+ */
+ for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (client->message->rdclass == view->rdclass ||
+ client->message->rdclass == dns_rdataclass_any)
+ {
+ dns_name_t *tsig = NULL;
+ sigresult = dns_message_rechecksig(client->message,
+ view);
+ if (sigresult == ISC_R_SUCCESS)
+ tsig = client->message->tsigname;
+
+ if (allowed(&netaddr, tsig, view->matchclients) &&
+ allowed(&destaddr, tsig, view->matchdestinations) &&
+ !((client->message->flags & DNS_MESSAGEFLAG_RD)
+ == 0 && view->matchrecursiveonly))
+ {
+ dns_view_attach(view, &client->view);
+ break;
+ }
+ }
+ }
+
+ if (view == NULL) {
+ char classname[DNS_RDATACLASS_FORMATSIZE];
+
+ /*
+ * Do a dummy TSIG verification attempt so that the
+ * response will have a TSIG if the query did, as
+ * required by RFC2845.
+ */
+ isc_buffer_t b;
+ isc_region_t *r;
+
+ dns_message_resetsig(client->message);
+
+ r = dns_message_getrawmessage(client->message);
+ isc_buffer_init(&b, r->base, r->length);
+ isc_buffer_add(&b, r->length);
+ (void)dns_tsig_verify(&b, client->message, NULL, NULL);
+
+ dns_rdataclass_format(client->message->rdclass, classname,
+ sizeof(classname));
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+ "no matching view in class '%s'", classname);
+ ns_client_dumpmessage(client, "no matching view in class");
+ ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
+ goto cleanup;
+ }
+
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
+ "using view '%s'", view->name);
+
+ /*
+ * Check for a signature. We log bad signatures regardless of
+ * whether they ultimately cause the request to be rejected or
+ * not. We do not log the lack of a signature unless we are
+ * debugging.
+ */
+ client->signer = NULL;
+ dns_name_init(&client->signername, NULL);
+ result = dns_message_signer(client->message, &client->signername);
+ if (result == ISC_R_SUCCESS) {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "request has valid signature");
+ client->signer = &client->signername;
+ } else if (result == ISC_R_NOTFOUND) {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "request is not signed");
+ } else if (result == DNS_R_NOIDENTITY) {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "request is signed by a nonauthoritative key");
+ } else {
+ char tsigrcode[64];
+ isc_buffer_t b;
+ dns_name_t *name = NULL;
+ dns_rcode_t status;
+ isc_result_t tresult;
+
+ /* There is a signature, but it is bad. */
+ if (dns_message_gettsig(client->message, &name) != NULL) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ status = client->message->tsigstatus;
+ isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
+ tresult = dns_tsigrcode_totext(status, &b);
+ INSIST(tresult == ISC_R_SUCCESS);
+ tsigrcode[isc_buffer_usedlength(&b)] = '\0';
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
+ "request has invalid signature: "
+ "TSIG %s: %s (%s)", namebuf,
+ isc_result_totext(result), tsigrcode);
+ } else {
+ status = client->message->sig0status;
+ isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
+ tresult = dns_tsigrcode_totext(status, &b);
+ INSIST(tresult == ISC_R_SUCCESS);
+ tsigrcode[isc_buffer_usedlength(&b)] = '\0';
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
+ "request has invalid signature: %s (%s)",
+ isc_result_totext(result), tsigrcode);
+ }
+ /*
+ * Accept update messages signed by unknown keys so that
+ * update forwarding works transparently through slaves
+ * that don't have all the same keys as the master.
+ */
+ if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
+ client->message->opcode == dns_opcode_update)) {
+ ns_client_error(client, sigresult);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Decide whether recursive service is available to this client.
+ * We do this here rather than in the query code so that we can
+ * set the RA bit correctly on all kinds of responses, not just
+ * responses to ordinary queries. Note if you can't query the
+ * cache there is no point in setting RA.
+ */
+ ra = ISC_FALSE;
+ if (client->view->resolver != NULL &&
+ client->view->recursion == ISC_TRUE &&
+ ns_client_checkaclsilent(client, client->view->recursionacl,
+ ISC_TRUE) == ISC_R_SUCCESS &&
+ ns_client_checkaclsilent(client, client->view->queryacl,
+ ISC_TRUE) == ISC_R_SUCCESS)
+ ra = ISC_TRUE;
+
+ if (ra == ISC_TRUE)
+ client->attributes |= NS_CLIENTATTR_RA;
+
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_CLIENT,
+ ISC_LOG_DEBUG(3), ra ? "recursion available" :
+ "recursion not available");
+
+ /*
+ * Adjust maximum UDP response size for this client.
+ */
+ if (client->udpsize > 512) {
+ dns_peer_t *peer = NULL;
+ isc_uint16_t udpsize = view->maxudp;
+ (void) dns_peerlist_peerbyaddr(view->peers, &netaddr, &peer);
+ if (peer != NULL)
+ dns_peer_getmaxudp(peer, &udpsize);
+ if (client->udpsize > udpsize)
+ client->udpsize = udpsize;
+ }
+
+ /*
+ * Dispatch the request.
+ */
+ switch (client->message->opcode) {
+ case dns_opcode_query:
+ CTRACE("query");
+ ns_query_start(client);
+ break;
+ case dns_opcode_update:
+ CTRACE("update");
+ ns_client_settimeout(client, 60);
+ ns_update_start(client, sigresult);
+ break;
+ case dns_opcode_notify:
+ CTRACE("notify");
+ ns_client_settimeout(client, 60);
+ ns_notify_start(client);
+ break;
+ case dns_opcode_iquery:
+ CTRACE("iquery");
+ ns_client_error(client, DNS_R_NOTIMP);
+ break;
+ default:
+ CTRACE("unknown opcode");
+ ns_client_error(client, DNS_R_NOTIMP);
+ }
+
+ cleanup:
+ return;
+}
+
+static void
+client_timeout(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client;
+
+ REQUIRE(event != NULL);
+ REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
+ event->ev_type == ISC_TIMEREVENT_IDLE);
+ client = event->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+ REQUIRE(client->timer != NULL);
+
+ UNUSED(task);
+
+ CTRACE("timeout");
+
+ isc_event_free(&event);
+
+ if (client->shutdown != NULL) {
+ (client->shutdown)(client->shutdown_arg, ISC_R_TIMEDOUT);
+ client->shutdown = NULL;
+ client->shutdown_arg = NULL;
+ }
+
+ if (client->newstate > NS_CLIENTSTATE_READY)
+ client->newstate = NS_CLIENTSTATE_READY;
+ (void)exit_check(client);
+}
+
+static isc_result_t
+get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) {
+ isc_mem_t *clientmctx;
+#if NMCTXS > 0
+ isc_result_t result;
+#endif
+
+ /*
+ * Caller must be holding the manager lock.
+ */
+#if NMCTXS > 0
+ INSIST(manager->nextmctx < NMCTXS);
+ clientmctx = manager->mctxpool[manager->nextmctx];
+ if (clientmctx == NULL) {
+ result = isc_mem_create(0, 0, &clientmctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ manager->mctxpool[manager->nextmctx] = clientmctx;
+ manager->nextmctx++;
+ if (manager->nextmctx == NMCTXS)
+ manager->nextmctx = 0;
+ }
+#else
+ clientmctx = manager->mctx;
+#endif
+
+ isc_mem_attach(clientmctx, mctxp);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
+ ns_client_t *client;
+ isc_result_t result;
+ isc_mem_t *mctx = NULL;
+
+ /*
+ * Caller must be holding the manager lock.
+ *
+ * Note: creating a client does not add the client to the
+ * manager's client list or set the client's manager pointer.
+ * The caller is responsible for that.
+ */
+
+ REQUIRE(clientp != NULL && *clientp == NULL);
+
+ result = get_clientmctx(manager, &mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ client = isc_mem_get(mctx, sizeof(*client));
+ if (client == NULL) {
+ isc_mem_detach(&mctx);
+ return (ISC_R_NOMEMORY);
+ }
+ client->mctx = mctx;
+
+ client->task = NULL;
+ result = isc_task_create(manager->taskmgr, 0, &client->task);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_client;
+ isc_task_setname(client->task, "client", client);
+
+ client->timer = NULL;
+ result = isc_timer_create(manager->timermgr, isc_timertype_inactive,
+ NULL, NULL, client->task, client_timeout,
+ client, &client->timer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_task;
+ client->timerset = ISC_FALSE;
+
+ client->message = NULL;
+ result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
+ &client->message);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_timer;
+
+ /* XXXRTH Hardwired constants */
+
+ client->sendevent = (isc_socketevent_t *)
+ isc_event_allocate(client->mctx, client,
+ ISC_SOCKEVENT_SENDDONE,
+ client_senddone, client,
+ sizeof(isc_socketevent_t));
+ if (client->sendevent == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_message;
+ }
+
+ client->recvbuf = isc_mem_get(client->mctx, RECV_BUFFER_SIZE);
+ if (client->recvbuf == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_sendevent;
+ }
+
+ client->recvevent = (isc_socketevent_t *)
+ isc_event_allocate(client->mctx, client,
+ ISC_SOCKEVENT_RECVDONE,
+ client_request, client,
+ sizeof(isc_socketevent_t));
+ if (client->recvevent == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_recvbuf;
+ }
+
+ client->magic = NS_CLIENT_MAGIC;
+ client->manager = NULL;
+ client->state = NS_CLIENTSTATE_INACTIVE;
+ client->newstate = NS_CLIENTSTATE_MAX;
+ client->naccepts = 0;
+ client->nreads = 0;
+ client->nsends = 0;
+ client->nrecvs = 0;
+ client->nupdates = 0;
+ client->nctls = 0;
+ client->references = 0;
+ client->attributes = 0;
+ client->view = NULL;
+ client->dispatch = NULL;
+ client->udpsocket = NULL;
+ client->tcplistener = NULL;
+ client->tcpsocket = NULL;
+ client->tcpmsg_valid = ISC_FALSE;
+ client->tcpbuf = NULL;
+ client->opt = NULL;
+ client->udpsize = 512;
+ client->extflags = 0;
+ client->ednsversion = -1;
+ client->next = NULL;
+ client->shutdown = NULL;
+ client->shutdown_arg = NULL;
+ dns_name_init(&client->signername, NULL);
+ client->mortal = ISC_FALSE;
+ client->tcpquota = NULL;
+ client->recursionquota = NULL;
+ client->interface = NULL;
+ client->peeraddr_valid = ISC_FALSE;
+ ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL,
+ NS_EVENT_CLIENTCONTROL, client_start, client, client,
+ NULL, NULL);
+ /*
+ * Initialize FORMERR cache to sentinel value that will not match
+ * any actual FORMERR response.
+ */
+ isc_sockaddr_any(&client->formerrcache.addr);
+ client->formerrcache.time = 0;
+ client->formerrcache.id = 0;
+ ISC_LINK_INIT(client, link);
+ client->list = NULL;
+
+ /*
+ * We call the init routines for the various kinds of client here,
+ * after we have created an otherwise valid client, because some
+ * of them call routines that REQUIRE(NS_CLIENT_VALID(client)).
+ */
+ result = ns_query_init(client);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_recvevent;
+
+ result = isc_task_onshutdown(client->task, client_shutdown, client);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_query;
+
+ CTRACE("create");
+
+ *clientp = client;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_query:
+ ns_query_free(client);
+
+ cleanup_recvevent:
+ isc_event_free((isc_event_t **)&client->recvevent);
+
+ cleanup_recvbuf:
+ isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
+
+ cleanup_sendevent:
+ isc_event_free((isc_event_t **)&client->sendevent);
+
+ client->magic = 0;
+
+ cleanup_message:
+ dns_message_destroy(&client->message);
+
+ cleanup_timer:
+ isc_timer_detach(&client->timer);
+
+ cleanup_task:
+ isc_task_detach(&client->task);
+
+ cleanup_client:
+ isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
+
+ return (result);
+}
+
+static void
+client_read(ns_client_t *client) {
+ isc_result_t result;
+
+ CTRACE("read");
+
+ result = dns_tcpmsg_readmessage(&client->tcpmsg, client->task,
+ client_request, client);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ /*
+ * Set a timeout to limit the amount of time we will wait
+ * for a request on this TCP connection.
+ */
+ ns_client_settimeout(client, 30);
+
+ client->state = client->newstate = NS_CLIENTSTATE_READING;
+ INSIST(client->nreads == 0);
+ INSIST(client->recursionquota == NULL);
+ client->nreads++;
+
+ return;
+ fail:
+ ns_client_next(client, result);
+}
+
+static void
+client_newconn(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client = event->ev_arg;
+ isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
+ isc_result_t result;
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(client->task == task);
+
+ UNUSED(task);
+
+ INSIST(client->state == NS_CLIENTSTATE_READY);
+
+ INSIST(client->naccepts == 1);
+ client->naccepts--;
+
+ LOCK(&client->interface->lock);
+ INSIST(client->interface->ntcpcurrent > 0);
+ client->interface->ntcpcurrent--;
+ UNLOCK(&client->interface->lock);
+
+ /*
+ * We must take ownership of the new socket before the exit
+ * check to make sure it gets destroyed if we decide to exit.
+ */
+ if (nevent->result == ISC_R_SUCCESS) {
+ client->tcpsocket = nevent->newsocket;
+ client->state = NS_CLIENTSTATE_READING;
+ INSIST(client->recursionquota == NULL);
+
+ (void)isc_socket_getpeername(client->tcpsocket,
+ &client->peeraddr);
+ client->peeraddr_valid = ISC_TRUE;
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "new TCP connection");
+ } else {
+ /*
+ * XXXRTH What should we do? We're trying to accept but
+ * it didn't work. If we just give up, then TCP
+ * service may eventually stop.
+ *
+ * For now, we just go idle.
+ *
+ * Going idle is probably the right thing if the
+ * I/O was canceled.
+ */
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "accept failed: %s",
+ isc_result_totext(nevent->result));
+ }
+
+ if (exit_check(client))
+ goto freeevent;
+
+ if (nevent->result == ISC_R_SUCCESS) {
+ int match;
+ isc_netaddr_t netaddr;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+ if (ns_g_server->blackholeacl != NULL &&
+ dns_acl_match(&netaddr, NULL,
+ ns_g_server->blackholeacl,
+ &ns_g_server->aclenv,
+ &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "blackholed connection attempt");
+ client->newstate = NS_CLIENTSTATE_READY;
+ (void)exit_check(client);
+ goto freeevent;
+ }
+
+ INSIST(client->tcpmsg_valid == ISC_FALSE);
+ dns_tcpmsg_init(client->mctx, client->tcpsocket,
+ &client->tcpmsg);
+ client->tcpmsg_valid = ISC_TRUE;
+
+ /*
+ * Let a new client take our place immediately, before
+ * we wait for a request packet. If we don't,
+ * telnetting to port 53 (once per CPU) will
+ * deny service to legititmate TCP clients.
+ */
+ result = isc_quota_attach(&ns_g_server->tcpquota,
+ &client->tcpquota);
+ if (result == ISC_R_SUCCESS)
+ result = ns_client_replace(client);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
+ "no more TCP clients: %s",
+ isc_result_totext(result));
+ }
+
+ client_read(client);
+ }
+
+ freeevent:
+ isc_event_free(&event);
+}
+
+static void
+client_accept(ns_client_t *client) {
+ isc_result_t result;
+
+ CTRACE("accept");
+
+ result = isc_socket_accept(client->tcplistener, client->task,
+ client_newconn, client);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socket_accept() failed: %s",
+ isc_result_totext(result));
+ /*
+ * XXXRTH What should we do? We're trying to accept but
+ * it didn't work. If we just give up, then TCP
+ * service may eventually stop.
+ *
+ * For now, we just go idle.
+ */
+ return;
+ }
+ INSIST(client->naccepts == 0);
+ client->naccepts++;
+ LOCK(&client->interface->lock);
+ client->interface->ntcpcurrent++;
+ UNLOCK(&client->interface->lock);
+}
+
+static void
+client_udprecv(ns_client_t *client) {
+ isc_result_t result;
+ isc_region_t r;
+
+ CTRACE("udprecv");
+
+ r.base = client->recvbuf;
+ r.length = RECV_BUFFER_SIZE;
+ result = isc_socket_recv2(client->udpsocket, &r, 1,
+ client->task, client->recvevent, 0);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socket_recv2() failed: %s",
+ isc_result_totext(result));
+ /*
+ * This cannot happen in the current implementation, since
+ * isc_socket_recv2() cannot fail if flags == 0.
+ *
+ * If this does fail, we just go idle.
+ */
+ return;
+ }
+ INSIST(client->nrecvs == 0);
+ client->nrecvs++;
+}
+
+void
+ns_client_attach(ns_client_t *source, ns_client_t **targetp) {
+ REQUIRE(NS_CLIENT_VALID(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ source->references++;
+ ns_client_log(source, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "ns_client_attach: ref = %d", source->references);
+ *targetp = source;
+}
+
+void
+ns_client_detach(ns_client_t **clientp) {
+ ns_client_t *client = *clientp;
+
+ client->references--;
+ INSIST(client->references >= 0);
+ *clientp = NULL;
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "ns_client_detach: ref = %d", client->references);
+ (void)exit_check(client);
+}
+
+isc_boolean_t
+ns_client_shuttingdown(ns_client_t *client) {
+ return (ISC_TF(client->newstate == NS_CLIENTSTATE_FREED));
+}
+
+isc_result_t
+ns_client_replace(ns_client_t *client) {
+ isc_result_t result;
+
+ CTRACE("replace");
+
+ result = ns_clientmgr_createclients(client->manager,
+ 1, client->interface,
+ (TCP_CLIENT(client) ?
+ ISC_TRUE : ISC_FALSE));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * The responsibility for listening for new requests is hereby
+ * transferred to the new client. Therefore, the old client
+ * should refrain from listening for any more requests.
+ */
+ client->mortal = ISC_TRUE;
+
+ return (ISC_R_SUCCESS);
+}
+
+/***
+ *** Client Manager
+ ***/
+
+static void
+clientmgr_destroy(ns_clientmgr_t *manager) {
+#if NMCTXS > 0
+ int i;
+#endif
+
+ REQUIRE(ISC_LIST_EMPTY(manager->active));
+ REQUIRE(ISC_LIST_EMPTY(manager->inactive));
+ REQUIRE(ISC_LIST_EMPTY(manager->recursing));
+
+ MTRACE("clientmgr_destroy");
+
+#if NMCTXS > 0
+ for (i = 0; i < NMCTXS; i++) {
+ if (manager->mctxpool[i] != NULL)
+ isc_mem_detach(&manager->mctxpool[i]);
+ }
+#endif
+
+ DESTROYLOCK(&manager->lock);
+ manager->magic = 0;
+ isc_mem_put(manager->mctx, manager, sizeof(*manager));
+}
+
+isc_result_t
+ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, ns_clientmgr_t **managerp)
+{
+ ns_clientmgr_t *manager;
+ isc_result_t result;
+#if NMCTXS > 0
+ int i;
+#endif
+
+ manager = isc_mem_get(mctx, sizeof(*manager));
+ if (manager == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&manager->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_manager;
+
+ manager->mctx = mctx;
+ manager->taskmgr = taskmgr;
+ manager->timermgr = timermgr;
+ manager->exiting = ISC_FALSE;
+ ISC_LIST_INIT(manager->active);
+ ISC_LIST_INIT(manager->inactive);
+ ISC_LIST_INIT(manager->recursing);
+#if NMCTXS > 0
+ manager->nextmctx = 0;
+ for (i = 0; i < NMCTXS; i++)
+ manager->mctxpool[i] = NULL; /* will be created on-demand */
+#endif
+ manager->magic = MANAGER_MAGIC;
+
+ MTRACE("create");
+
+ *managerp = manager;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_manager:
+ isc_mem_put(manager->mctx, manager, sizeof(*manager));
+
+ return (result);
+}
+
+void
+ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
+ ns_clientmgr_t *manager;
+ ns_client_t *client;
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(managerp != NULL);
+ manager = *managerp;
+ REQUIRE(VALID_MANAGER(manager));
+
+ MTRACE("destroy");
+
+ LOCK(&manager->lock);
+
+ manager->exiting = ISC_TRUE;
+
+ for (client = ISC_LIST_HEAD(manager->recursing);
+ client != NULL;
+ client = ISC_LIST_NEXT(client, link))
+ isc_task_shutdown(client->task);
+
+ for (client = ISC_LIST_HEAD(manager->active);
+ client != NULL;
+ client = ISC_LIST_NEXT(client, link))
+ isc_task_shutdown(client->task);
+
+ for (client = ISC_LIST_HEAD(manager->inactive);
+ client != NULL;
+ client = ISC_LIST_NEXT(client, link))
+ isc_task_shutdown(client->task);
+
+ if (ISC_LIST_EMPTY(manager->active) &&
+ ISC_LIST_EMPTY(manager->inactive) &&
+ ISC_LIST_EMPTY(manager->recursing))
+ need_destroy = ISC_TRUE;
+
+ UNLOCK(&manager->lock);
+
+ if (need_destroy)
+ clientmgr_destroy(manager);
+
+ *managerp = NULL;
+}
+
+isc_result_t
+ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
+ ns_interface_t *ifp, isc_boolean_t tcp)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned int i;
+ ns_client_t *client;
+
+ REQUIRE(VALID_MANAGER(manager));
+ REQUIRE(n > 0);
+
+ MTRACE("createclients");
+
+ /*
+ * We MUST lock the manager lock for the entire client creation
+ * process. If we didn't do this, then a client could get a
+ * shutdown event and disappear out from under us.
+ */
+
+ LOCK(&manager->lock);
+
+ for (i = 0; i < n; i++) {
+ isc_event_t *ev;
+ /*
+ * Allocate a client. First try to get a recycled one;
+ * if that fails, make a new one.
+ */
+ client = ISC_LIST_HEAD(manager->inactive);
+ if (client != NULL) {
+ MTRACE("recycle");
+ ISC_LIST_UNLINK(manager->inactive, client, link);
+ client->list = NULL;
+ } else {
+ MTRACE("create new");
+ result = client_create(manager, &client);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+
+ ns_interface_attach(ifp, &client->interface);
+ client->state = NS_CLIENTSTATE_READY;
+ INSIST(client->recursionquota == NULL);
+
+ if (tcp) {
+ client->attributes |= NS_CLIENTATTR_TCP;
+ isc_socket_attach(ifp->tcpsocket,
+ &client->tcplistener);
+ } else {
+ isc_socket_t *sock;
+
+ dns_dispatch_attach(ifp->udpdispatch,
+ &client->dispatch);
+ sock = dns_dispatch_getsocket(client->dispatch);
+ isc_socket_attach(sock, &client->udpsocket);
+ }
+ client->manager = manager;
+ ISC_LIST_APPEND(manager->active, client, link);
+ client->list = &manager->active;
+
+ INSIST(client->nctls == 0);
+ client->nctls++;
+ ev = &client->ctlevent;
+ isc_task_send(client->task, &ev);
+ }
+ if (i != 0) {
+ /*
+ * We managed to create at least one client, so we
+ * declare victory.
+ */
+ result = ISC_R_SUCCESS;
+ }
+
+ UNLOCK(&manager->lock);
+
+ return (result);
+}
+
+isc_sockaddr_t *
+ns_client_getsockaddr(ns_client_t *client) {
+ return (&client->peeraddr);
+}
+
+isc_result_t
+ns_client_checkaclsilent(ns_client_t *client, dns_acl_t *acl,
+ isc_boolean_t default_allow)
+{
+ isc_result_t result;
+ int match;
+ isc_netaddr_t netaddr;
+
+ if (acl == NULL) {
+ if (default_allow)
+ goto allow;
+ else
+ goto deny;
+ }
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+ result = dns_acl_match(&netaddr, client->signer, acl,
+ &ns_g_server->aclenv,
+ &match, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto deny; /* Internal error, already logged. */
+ if (match > 0)
+ goto allow;
+ goto deny; /* Negative match or no match. */
+
+ allow:
+ return (ISC_R_SUCCESS);
+
+ deny:
+ return (DNS_R_REFUSED);
+}
+
+isc_result_t
+ns_client_checkacl(ns_client_t *client,
+ const char *opname, dns_acl_t *acl,
+ isc_boolean_t default_allow, int log_level)
+{
+ isc_result_t result =
+ ns_client_checkaclsilent(client, acl, default_allow);
+
+ if (result == ISC_R_SUCCESS)
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "%s approved", opname);
+ else
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT,
+ log_level, "%s denied", opname);
+ return (result);
+}
+
+static void
+ns_client_name(ns_client_t *client, char *peerbuf, size_t len) {
+ if (client->peeraddr_valid)
+ isc_sockaddr_format(&client->peeraddr, peerbuf, len);
+ else
+ snprintf(peerbuf, len, "@%p", client);
+}
+
+void
+ns_client_logv(ns_client_t *client, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *fmt, va_list ap)
+{
+ char msgbuf[2048];
+ char peerbuf[ISC_SOCKADDR_FORMATSIZE];
+ const char *name = "";
+ const char *sep = "";
+
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ ns_client_name(client, peerbuf, sizeof(peerbuf));
+ if (client->view != NULL && strcmp(client->view->name, "_bind") != 0 &&
+ strcmp(client->view->name, "_default") != 0) {
+ name = client->view->name;
+ sep = ": view ";
+ }
+
+ isc_log_write(ns_g_lctx, category, module, level,
+ "client %s%s%s: %s", peerbuf, sep, name, msgbuf);
+}
+
+void
+ns_client_log(ns_client_t *client, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (! isc_log_wouldlog(ns_g_lctx, level))
+ return;
+
+ va_start(ap, fmt);
+ ns_client_logv(client, category, module, level, fmt, ap);
+ va_end(ap);
+}
+
+void
+ns_client_aclmsg(const char *msg, dns_name_t *name, dns_rdatatype_t type,
+ dns_rdataclass_t rdclass, char *buf, size_t len)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(type, typebuf, sizeof(typebuf));
+ dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
+ (void)snprintf(buf, len, "%s '%s/%s/%s'", msg, namebuf, typebuf,
+ classbuf);
+}
+
+static void
+ns_client_dumpmessage(ns_client_t *client, const char *reason) {
+ isc_buffer_t buffer;
+ char *buf = NULL;
+ int len = 1024;
+ isc_result_t result;
+
+ /*
+ * Note that these are multiline debug messages. We want a newline
+ * to appear in the log after each message.
+ */
+
+ do {
+ buf = isc_mem_get(client->mctx, len);
+ if (buf == NULL)
+ break;
+ isc_buffer_init(&buffer, buf, len);
+ result = dns_message_totext(client->message,
+ &dns_master_style_debug,
+ 0, &buffer);
+ if (result == ISC_R_NOSPACE) {
+ isc_mem_put(client->mctx, buf, len);
+ len += 1024;
+ } else if (result == ISC_R_SUCCESS)
+ ns_client_log(client, NS_LOGCATEGORY_UNMATCHED,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+ "%s\n%.*s", reason,
+ (int)isc_buffer_usedlength(&buffer),
+ buf);
+ } while (result == ISC_R_NOSPACE);
+
+ if (buf != NULL)
+ isc_mem_put(client->mctx, buf, len);
+}
+
+void
+ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager) {
+ ns_client_t *client;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char peerbuf[ISC_SOCKADDR_FORMATSIZE];
+ const char *name;
+ const char *sep;
+
+ REQUIRE(VALID_MANAGER(manager));
+
+ LOCK(&manager->lock);
+ client = ISC_LIST_HEAD(manager->recursing);
+ while (client != NULL) {
+ ns_client_name(client, peerbuf, sizeof(peerbuf));
+ if (client->view != NULL &&
+ strcmp(client->view->name, "_bind") != 0 &&
+ strcmp(client->view->name, "_default") != 0) {
+ name = client->view->name;
+ sep = ": view ";
+ } else {
+ name = "";
+ sep = "";
+ }
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+ fprintf(f, "; client %s%s%s: '%s' requesttime %d\n",
+ peerbuf, sep, name, namebuf, client->requesttime);
+ client = ISC_LIST_NEXT(client, link);
+ }
+ UNLOCK(&manager->lock);
+}
+
+void
+ns_client_qnamereplace(ns_client_t *client, dns_name_t *name) {
+
+ if (client->manager != NULL)
+ LOCK(&client->manager->lock);
+ if (client->query.restarts > 0) {
+ /*
+ * client->query.qname was dynamically allocated.
+ */
+ dns_message_puttempname(client->message,
+ &client->query.qname);
+ }
+ client->query.qname = name;
+ if (client->manager != NULL)
+ UNLOCK(&client->manager->lock);
+}
diff --git a/bin/named/config.c b/bin/named/config.c
new file mode 100644
index 0000000..e2dc833
--- /dev/null
+++ b/bin/named/config.c
@@ -0,0 +1,796 @@
+/*
+ * 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: config.c,v 1.47.18.32 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/parseint.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <isccfg/namedconf.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/tsig.h>
+#include <dns/zone.h>
+
+#include <named/config.h>
+#include <named/globals.h>
+
+/*% default configuration */
+static char defaultconf[] = "\
+options {\n\
+# blackhole {none;};\n"
+#ifndef WIN32
+" coresize default;\n\
+ datasize default;\n\
+ files default;\n\
+ stacksize default;\n"
+#endif
+" deallocate-on-exit true;\n\
+# directory <none>\n\
+ dump-file \"named_dump.db\";\n\
+ fake-iquery no;\n\
+ has-old-clients false;\n\
+ heartbeat-interval 60;\n\
+ host-statistics no;\n\
+ interface-interval 60;\n\
+ listen-on {any;};\n\
+ listen-on-v6 {none;};\n\
+ match-mapped-addresses no;\n\
+ memstatistics-file \"named.memstats\";\n\
+ multiple-cnames no;\n\
+# named-xfer <obsolete>;\n\
+# pid-file \"" NS_LOCALSTATEDIR "/named.pid\"; /* or /lwresd.pid */\n\
+ port 53;\n\
+ recursing-file \"named.recursing\";\n\
+"
+#ifdef PATH_RANDOMDEV
+"\
+ random-device \"" PATH_RANDOMDEV "\";\n\
+"
+#endif
+"\
+ recursive-clients 1000;\n\
+ rrset-order {type NS order random; order cyclic; };\n\
+ serial-queries 20;\n\
+ serial-query-rate 20;\n\
+ server-id none;\n\
+ statistics-file \"named.stats\";\n\
+ statistics-interval 60;\n\
+ tcp-clients 100;\n\
+ tcp-listen-queue 3;\n\
+# tkey-dhkey <none>\n\
+# tkey-gssapi-credential <none>\n\
+# tkey-domain <none>\n\
+ transfers-per-ns 2;\n\
+ transfers-in 10;\n\
+ transfers-out 10;\n\
+ treat-cr-as-space true;\n\
+ use-id-pool true;\n\
+ use-ixfr true;\n\
+ edns-udp-size 4096;\n\
+ max-udp-size 4096;\n\
+\n\
+ /* view */\n\
+ allow-notify {none;};\n\
+ allow-update-forwarding {none;};\n\
+ allow-query-cache { localnets; localhost; };\n\
+ allow-recursion { localnets; localhost; };\n\
+# allow-v6-synthesis <obsolete>;\n\
+# sortlist <none>\n\
+# topology <none>\n\
+ auth-nxdomain false;\n\
+ minimal-responses false;\n\
+ recursion true;\n\
+ provide-ixfr true;\n\
+ request-ixfr true;\n\
+ fetch-glue no;\n\
+ rfc2308-type1 no;\n\
+ additional-from-auth true;\n\
+ additional-from-cache true;\n\
+ query-source address *;\n\
+ query-source-v6 address *;\n\
+ notify-source *;\n\
+ notify-source-v6 *;\n\
+ cleaning-interval 60;\n\
+ min-roots 2;\n\
+ lame-ttl 600;\n\
+ max-ncache-ttl 10800; /* 3 hours */\n\
+ max-cache-ttl 604800; /* 1 week */\n\
+ transfer-format many-answers;\n\
+ max-cache-size 0;\n\
+ check-names master fail;\n\
+ check-names slave warn;\n\
+ check-names response ignore;\n\
+ check-mx warn;\n\
+ acache-enable no;\n\
+ acache-cleaning-interval 60;\n\
+ max-acache-size 0;\n\
+ dnssec-enable yes;\n\
+ dnssec-validation no; /* Make yes for 9.5. */ \n\
+ dnssec-accept-expired no;\n\
+ clients-per-query 10;\n\
+ max-clients-per-query 100;\n\
+ zero-no-soa-ttl-cache no;\n\
+"
+
+" /* zone */\n\
+ allow-query {any;};\n\
+ allow-transfer {any;};\n\
+ notify yes;\n\
+# also-notify <none>\n\
+ notify-delay 5;\n\
+ dialup no;\n\
+# forward <none>\n\
+# forwarders <none>\n\
+ maintain-ixfr-base no;\n\
+# max-ixfr-log-size <obsolete>\n\
+ transfer-source *;\n\
+ transfer-source-v6 *;\n\
+ alt-transfer-source *;\n\
+ alt-transfer-source-v6 *;\n\
+ max-transfer-time-in 120;\n\
+ max-transfer-time-out 120;\n\
+ max-transfer-idle-in 60;\n\
+ max-transfer-idle-out 60;\n\
+ max-retry-time 1209600; /* 2 weeks */\n\
+ min-retry-time 500;\n\
+ max-refresh-time 2419200; /* 4 weeks */\n\
+ min-refresh-time 300;\n\
+ multi-master no;\n\
+ sig-validity-interval 30; /* days */\n\
+ zone-statistics false;\n\
+ max-journal-size unlimited;\n\
+ ixfr-from-differences false;\n\
+ check-wildcard yes;\n\
+ check-sibling yes;\n\
+ check-integrity yes;\n\
+ check-mx-cname warn;\n\
+ check-srv-cname warn;\n\
+ zero-no-soa-ttl yes;\n\
+ update-check-ksk yes;\n\
+};\n\
+"
+
+"#\n\
+# Zones in the \"_bind\" view are NOT counted in the count of zones.\n\
+#\n\
+view \"_bind\" chaos {\n\
+ recursion no;\n\
+ notify no;\n\
+\n\
+ zone \"version.bind\" chaos {\n\
+ type master;\n\
+ database \"_builtin version\";\n\
+ };\n\
+\n\
+ zone \"hostname.bind\" chaos {\n\
+ type master;\n\
+ database \"_builtin hostname\";\n\
+ };\n\
+\n\
+ zone \"authors.bind\" chaos {\n\
+ type master;\n\
+ database \"_builtin authors\";\n\
+ };\n\
+ zone \"id.server\" chaos {\n\
+ type master;\n\
+ database \"_builtin id\";\n\
+ };\n\
+};\n\
+";
+
+isc_result_t
+ns_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf) {
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, defaultconf, sizeof(defaultconf) - 1);
+ isc_buffer_add(&b, sizeof(defaultconf) - 1);
+ return (cfg_parse_buffer(parser, &b, &cfg_type_namedconf, conf));
+}
+
+isc_result_t
+ns_config_get(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) {
+ int i;
+
+ for (i = 0;; i++) {
+ if (maps[i] == NULL)
+ return (ISC_R_NOTFOUND);
+ if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+ }
+}
+
+isc_result_t
+ns_checknames_get(const cfg_obj_t **maps, const char *which,
+ const cfg_obj_t **obj)
+{
+ const cfg_listelt_t *element;
+ const cfg_obj_t *checknames;
+ const cfg_obj_t *type;
+ const cfg_obj_t *value;
+ int i;
+
+ for (i = 0;; i++) {
+ if (maps[i] == NULL)
+ return (ISC_R_NOTFOUND);
+ checknames = NULL;
+ if (cfg_map_get(maps[i], "check-names", &checknames) == ISC_R_SUCCESS) {
+ /*
+ * Zone map entry is not a list.
+ */
+ if (checknames != NULL && !cfg_obj_islist(checknames)) {
+ *obj = checknames;
+ return (ISC_R_SUCCESS);
+ }
+ for (element = cfg_list_first(checknames);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ value = cfg_listelt_value(element);
+ type = cfg_tuple_get(value, "type");
+ if (strcasecmp(cfg_obj_asstring(type), which) == 0) {
+ *obj = cfg_tuple_get(value, "mode");
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ }
+ }
+}
+
+int
+ns_config_listcount(const cfg_obj_t *list) {
+ const cfg_listelt_t *e;
+ int i = 0;
+
+ for (e = cfg_list_first(list); e != NULL; e = cfg_list_next(e))
+ i++;
+
+ return (i);
+}
+
+isc_result_t
+ns_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass,
+ dns_rdataclass_t *classp) {
+ isc_textregion_t r;
+ isc_result_t result;
+
+ if (!cfg_obj_isstring(classobj)) {
+ *classp = defclass;
+ return (ISC_R_SUCCESS);
+ }
+ DE_CONST(cfg_obj_asstring(classobj), r.base);
+ r.length = strlen(r.base);
+ result = dns_rdataclass_fromtext(classp, &r);
+ if (result != ISC_R_SUCCESS)
+ cfg_obj_log(classobj, ns_g_lctx, ISC_LOG_ERROR,
+ "unknown class '%s'", r.base);
+ return (result);
+}
+
+isc_result_t
+ns_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype,
+ dns_rdatatype_t *typep) {
+ isc_textregion_t r;
+ isc_result_t result;
+
+ if (!cfg_obj_isstring(typeobj)) {
+ *typep = deftype;
+ return (ISC_R_SUCCESS);
+ }
+ DE_CONST(cfg_obj_asstring(typeobj), r.base);
+ r.length = strlen(r.base);
+ result = dns_rdatatype_fromtext(typep, &r);
+ if (result != ISC_R_SUCCESS)
+ cfg_obj_log(typeobj, ns_g_lctx, ISC_LOG_ERROR,
+ "unknown type '%s'", r.base);
+ return (result);
+}
+
+dns_zonetype_t
+ns_config_getzonetype(const cfg_obj_t *zonetypeobj) {
+ dns_zonetype_t ztype = dns_zone_none;
+ const char *str;
+
+ str = cfg_obj_asstring(zonetypeobj);
+ if (strcasecmp(str, "master") == 0)
+ ztype = dns_zone_master;
+ else if (strcasecmp(str, "slave") == 0)
+ ztype = dns_zone_slave;
+ else if (strcasecmp(str, "stub") == 0)
+ ztype = dns_zone_stub;
+ else
+ INSIST(0);
+ return (ztype);
+}
+
+isc_result_t
+ns_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list,
+ in_port_t defport, isc_mem_t *mctx,
+ isc_sockaddr_t **addrsp, isc_uint32_t *countp)
+{
+ int count, i = 0;
+ const cfg_obj_t *addrlist;
+ const cfg_obj_t *portobj;
+ const cfg_listelt_t *element;
+ isc_sockaddr_t *addrs;
+ in_port_t port;
+ isc_result_t result;
+
+ INSIST(addrsp != NULL && *addrsp == NULL);
+ INSIST(countp != NULL);
+
+ addrlist = cfg_tuple_get(list, "addresses");
+ count = ns_config_listcount(addrlist);
+
+ portobj = cfg_tuple_get(list, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t) val;
+ } else if (defport != 0)
+ port = defport;
+ else {
+ result = ns_config_getport(config, &port);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ addrs = isc_mem_get(mctx, count * sizeof(isc_sockaddr_t));
+ if (addrs == NULL)
+ return (ISC_R_NOMEMORY);
+
+ for (element = cfg_list_first(addrlist);
+ element != NULL;
+ element = cfg_list_next(element), i++)
+ {
+ INSIST(i < count);
+ addrs[i] = *cfg_obj_assockaddr(cfg_listelt_value(element));
+ if (isc_sockaddr_getport(&addrs[i]) == 0)
+ isc_sockaddr_setport(&addrs[i], port);
+ }
+ INSIST(i == count);
+
+ *addrsp = addrs;
+ *countp = count;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ isc_uint32_t count)
+{
+ INSIST(addrsp != NULL && *addrsp != NULL);
+
+ isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t));
+ *addrsp = NULL;
+}
+
+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);
+}
+
+isc_result_t
+ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
+ isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ dns_name_t ***keysp, isc_uint32_t *countp)
+{
+ isc_uint32_t addrcount = 0, keycount = 0, i = 0;
+ isc_uint32_t listcount = 0, l = 0, j;
+ isc_uint32_t stackcount = 0, pushed = 0;
+ isc_result_t result;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *addrlist;
+ const cfg_obj_t *portobj;
+ in_port_t port;
+ dns_fixedname_t fname;
+ isc_sockaddr_t *addrs = NULL;
+ dns_name_t **keys = NULL;
+ struct { const char *name; } *lists = NULL;
+ struct {
+ const cfg_listelt_t *element;
+ in_port_t port;
+ } *stack = NULL;
+
+ REQUIRE(addrsp != NULL && *addrsp == NULL);
+ REQUIRE(keysp != NULL && *keysp == NULL);
+ REQUIRE(countp != NULL);
+
+ newlist:
+ addrlist = cfg_tuple_get(list, "addresses");
+ portobj = cfg_tuple_get(list, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ port = (in_port_t) val;
+ } else {
+ result = ns_config_getport(config, &port);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ result = ISC_R_NOMEMORY;
+
+ element = cfg_list_first(addrlist);
+ resume:
+ for ( ;
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *addr;
+ const cfg_obj_t *key;
+ const char *keystr;
+ isc_buffer_t b;
+
+ addr = cfg_tuple_get(cfg_listelt_value(element),
+ "masterselement");
+ key = cfg_tuple_get(cfg_listelt_value(element), "key");
+
+ if (!cfg_obj_issockaddr(addr)) {
+ const char *listname = cfg_obj_asstring(addr);
+ isc_result_t tresult;
+
+ /* Grow lists? */
+ if (listcount == l) {
+ void * new;
+ isc_uint32_t newlen = listcount + 16;
+ size_t newsize, oldsize;
+
+ newsize = newlen * sizeof(*lists);
+ oldsize = listcount * sizeof(*lists);
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ if (listcount != 0) {
+ memcpy(new, lists, oldsize);
+ isc_mem_put(mctx, lists, oldsize);
+ }
+ lists = new;
+ listcount = newlen;
+ }
+ /* Seen? */
+ for (j = 0; j < l; j++)
+ if (strcasecmp(lists[j].name, listname) == 0)
+ break;
+ if (j < l)
+ continue;
+ tresult = get_masters_def(config, listname, &list);
+ if (tresult == ISC_R_NOTFOUND) {
+ cfg_obj_log(addr, ns_g_lctx, ISC_LOG_ERROR,
+ "masters \"%s\" not found", listname);
+
+ result = tresult;
+ goto cleanup;
+ }
+ if (tresult != ISC_R_SUCCESS)
+ goto cleanup;
+ lists[l++].name = listname;
+ /* 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;
+ }
+ /*
+ * We want to resume processing this list on the
+ * next element.
+ */
+ stack[pushed].element = cfg_list_next(element);
+ stack[pushed].port = port;
+ pushed++;
+ goto newlist;
+ }
+
+ if (i == addrcount) {
+ void * new;
+ isc_uint32_t newlen = addrcount + 16;
+ size_t newsize, oldsize;
+
+ newsize = newlen * sizeof(isc_sockaddr_t);
+ oldsize = addrcount * sizeof(isc_sockaddr_t);
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ if (addrcount != 0) {
+ memcpy(new, addrs, oldsize);
+ isc_mem_put(mctx, addrs, oldsize);
+ }
+ addrs = new;
+ addrcount = newlen;
+
+ newsize = newlen * sizeof(dns_name_t *);
+ oldsize = keycount * sizeof(dns_name_t *);
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ if (keycount != 0) {
+ memcpy(new, keys, oldsize);
+ isc_mem_put(mctx, keys, oldsize);
+ }
+ keys = new;
+ keycount = newlen;
+ }
+
+ addrs[i] = *cfg_obj_assockaddr(addr);
+ if (isc_sockaddr_getport(&addrs[i]) == 0)
+ isc_sockaddr_setport(&addrs[i], port);
+ keys[i] = NULL;
+ if (!cfg_obj_isstring(key)) {
+ i++;
+ continue;
+ }
+ keys[i] = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (keys[i] == NULL)
+ goto cleanup;
+ dns_name_init(keys[i], NULL);
+
+ keystr = cfg_obj_asstring(key);
+ isc_buffer_init(&b, keystr, strlen(keystr));
+ isc_buffer_add(&b, strlen(keystr));
+ dns_fixedname_init(&fname);
+ result = dns_name_fromtext(dns_fixedname_name(&fname), &b,
+ dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_name_dup(dns_fixedname_name(&fname), mctx,
+ keys[i]);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ i++;
+ }
+ if (pushed != 0) {
+ pushed--;
+ element = stack[pushed].element;
+ port = stack[pushed].port;
+ goto resume;
+ }
+ if (i < addrcount) {
+ void * new;
+ size_t newsize, oldsize;
+
+ newsize = i * sizeof(isc_sockaddr_t);
+ oldsize = addrcount * sizeof(isc_sockaddr_t);
+ if (i != 0) {
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ memcpy(new, addrs, newsize);
+ } else
+ new = NULL;
+ isc_mem_put(mctx, addrs, oldsize);
+ addrs = new;
+ addrcount = i;
+
+ newsize = i * sizeof(dns_name_t *);
+ oldsize = keycount * sizeof(dns_name_t *);
+ if (i != 0) {
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ memcpy(new, keys, newsize);
+ } else
+ new = NULL;
+ isc_mem_put(mctx, keys, oldsize);
+ keys = new;
+ keycount = i;
+ }
+
+ if (lists != NULL)
+ isc_mem_put(mctx, lists, listcount * sizeof(*lists));
+ if (stack != NULL)
+ isc_mem_put(mctx, stack, stackcount * sizeof(*stack));
+
+ INSIST(keycount == addrcount);
+
+ *addrsp = addrs;
+ *keysp = keys;
+ *countp = addrcount;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (addrs != NULL)
+ isc_mem_put(mctx, addrs, addrcount * sizeof(isc_sockaddr_t));
+ if (keys != NULL) {
+ for (j = 0; j <= i; j++) {
+ if (keys[j] == NULL)
+ continue;
+ if (dns_name_dynamic(keys[j]))
+ dns_name_free(keys[j], mctx);
+ isc_mem_put(mctx, keys[j], sizeof(dns_name_t));
+ }
+ isc_mem_put(mctx, keys, keycount * sizeof(dns_name_t *));
+ }
+ if (lists != NULL)
+ isc_mem_put(mctx, lists, listcount * sizeof(*lists));
+ if (stack != NULL)
+ isc_mem_put(mctx, stack, stackcount * sizeof(*stack));
+ return (result);
+}
+
+void
+ns_config_putipandkeylist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ dns_name_t ***keysp, isc_uint32_t count)
+{
+ unsigned int i;
+ dns_name_t **keys = *keysp;
+
+ INSIST(addrsp != NULL && *addrsp != NULL);
+
+ isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t));
+ for (i = 0; i < count; i++) {
+ if (keys[i] == NULL)
+ continue;
+ if (dns_name_dynamic(keys[i]))
+ dns_name_free(keys[i], mctx);
+ isc_mem_put(mctx, keys[i], sizeof(dns_name_t));
+ }
+ isc_mem_put(mctx, *keysp, count * sizeof(dns_name_t *));
+ *addrsp = NULL;
+ *keysp = NULL;
+}
+
+isc_result_t
+ns_config_getport(const cfg_obj_t *config, in_port_t *portp) {
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *portobj = NULL;
+ isc_result_t result;
+ int i;
+
+ (void)cfg_map_get(config, "options", &options);
+ i = 0;
+ if (options != NULL)
+ maps[i++] = options;
+ maps[i++] = ns_g_defaults;
+ maps[i] = NULL;
+
+ result = ns_config_get(maps, "port", &portobj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range",
+ cfg_obj_asuint32(portobj));
+ return (ISC_R_RANGE);
+ }
+ *portp = (in_port_t)cfg_obj_asuint32(portobj);
+ return (ISC_R_SUCCESS);
+}
+
+struct keyalgorithms {
+ const char *str;
+ enum { hmacnone, hmacmd5, hmacsha1, hmacsha224,
+ hmacsha256, hmacsha384, hmacsha512 } hmac;
+ isc_uint16_t size;
+} algorithms[] = {
+ { "hmac-md5", hmacmd5, 128 },
+ { "hmac-md5.sig-alg.reg.int", hmacmd5, 0 },
+ { "hmac-md5.sig-alg.reg.int.", hmacmd5, 0 },
+ { "hmac-sha1", hmacsha1, 160 },
+ { "hmac-sha224", hmacsha224, 224 },
+ { "hmac-sha256", hmacsha256, 256 },
+ { "hmac-sha384", hmacsha384, 384 },
+ { "hmac-sha512", hmacsha512, 512 },
+ { NULL, hmacnone, 0 }
+};
+
+isc_result_t
+ns_config_getkeyalgorithm(const char *str, dns_name_t **name,
+ isc_uint16_t *digestbits)
+{
+ int i;
+ size_t len = 0;
+ isc_uint16_t bits;
+ isc_result_t result;
+
+ for (i = 0; algorithms[i].str != NULL; i++) {
+ len = strlen(algorithms[i].str);
+ if (strncasecmp(algorithms[i].str, str, len) == 0 &&
+ (str[len] == '\0' ||
+ (algorithms[i].size != 0 && str[len] == '-')))
+ break;
+ }
+ if (algorithms[i].str == NULL)
+ return (ISC_R_NOTFOUND);
+ if (str[len] == '-') {
+ result = isc_parse_uint16(&bits, str + len + 1, 10);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (bits > algorithms[i].size)
+ return (ISC_R_RANGE);
+ } else if (algorithms[i].size == 0)
+ bits = 128;
+ else
+ bits = algorithms[i].size;
+
+ if (name != NULL) {
+ switch (algorithms[i].hmac) {
+ case hmacmd5: *name = dns_tsig_hmacmd5_name; break;
+ case hmacsha1: *name = dns_tsig_hmacsha1_name; break;
+ case hmacsha224: *name = dns_tsig_hmacsha224_name; break;
+ case hmacsha256: *name = dns_tsig_hmacsha256_name; break;
+ case hmacsha384: *name = dns_tsig_hmacsha384_name; break;
+ case hmacsha512: *name = dns_tsig_hmacsha512_name; break;
+ default:
+ INSIST(0);
+ }
+ }
+ if (digestbits != NULL)
+ *digestbits = bits;
+ return (ISC_R_SUCCESS);
+}
diff --git a/bin/named/control.c b/bin/named/control.c
new file mode 100644
index 0000000..3f2d52e
--- /dev/null
+++ b/bin/named/control.c
@@ -0,0 +1,186 @@
+/*
+ * 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: control.c,v 1.20.10.10 2007/09/13 23:46:26 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+
+#include <isc/app.h>
+#include <isc/event.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+
+#include <isccc/alist.h>
+#include <isccc/cc.h>
+#include <isccc/result.h>
+
+#include <named/control.h>
+#include <named/log.h>
+#include <named/os.h>
+#include <named/server.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#endif
+
+static isc_boolean_t
+command_compare(const char *text, const char *command) {
+ unsigned int commandlen = strlen(command);
+ if (strncasecmp(text, command, commandlen) == 0 &&
+ (text[commandlen] == '\0' ||
+ text[commandlen] == ' ' ||
+ text[commandlen] == '\t'))
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+/*%
+ * This function is called to process the incoming command
+ * when a control channel message is received.
+ */
+isc_result_t
+ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) {
+ isccc_sexpr_t *data;
+ char *command;
+ isc_result_t result;
+#ifdef HAVE_LIBSCF
+ ns_smf_want_disable = 0;
+#endif
+
+ data = isccc_alist_lookup(message, "_data");
+ if (data == NULL) {
+ /*
+ * No data section.
+ */
+ return (ISC_R_FAILURE);
+ }
+
+ result = isccc_cc_lookupstring(data, "type", &command);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * We have no idea what this is.
+ */
+ return (result);
+ }
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_DEBUG(1),
+ "received control channel command '%s'",
+ command);
+
+ /*
+ * Compare the 'command' parameter against all known control commands.
+ */
+ if (command_compare(command, NS_COMMAND_RELOAD)) {
+ result = ns_server_reloadcommand(ns_g_server, command, text);
+ } else if (command_compare(command, NS_COMMAND_RECONFIG)) {
+ result = ns_server_reconfigcommand(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_REFRESH)) {
+ result = ns_server_refreshcommand(ns_g_server, command, text);
+ } else if (command_compare(command, NS_COMMAND_RETRANSFER)) {
+ result = ns_server_retransfercommand(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_HALT)) {
+#ifdef HAVE_LIBSCF
+ /*
+ * If we are managed by smf(5), AND in chroot, then
+ * we cannot connect to the smf repository, so just
+ * return with an appropriate message back to rndc.
+ */
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 1) {
+ result = ns_smf_add_message(text);
+ return (result);
+ }
+ /*
+ * If we are managed by smf(5) but not in chroot,
+ * try to disable ourselves the smf way.
+ */
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 0)
+ ns_smf_want_disable = 1;
+ /*
+ * If ns_smf_got_instance = 0, ns_smf_chroot
+ * is not relevant and we fall through to
+ * isc_app_shutdown below.
+ */
+#endif
+ ns_server_flushonshutdown(ns_g_server, ISC_FALSE);
+ ns_os_shutdownmsg(command, text);
+ isc_app_shutdown();
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_STOP)) {
+#ifdef HAVE_LIBSCF
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 1) {
+ result = ns_smf_add_message(text);
+ return (result);
+ }
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 0)
+ ns_smf_want_disable = 1;
+#endif
+ ns_server_flushonshutdown(ns_g_server, ISC_TRUE);
+ ns_os_shutdownmsg(command, text);
+ isc_app_shutdown();
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_DUMPSTATS)) {
+ result = ns_server_dumpstats(ns_g_server);
+ } else if (command_compare(command, NS_COMMAND_QUERYLOG)) {
+ result = ns_server_togglequerylog(ns_g_server);
+ } else if (command_compare(command, NS_COMMAND_DUMPDB)) {
+ ns_server_dumpdb(ns_g_server, command);
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_TRACE)) {
+ result = ns_server_setdebuglevel(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_NOTRACE)) {
+ ns_g_debuglevel = 0;
+ isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_FLUSH)) {
+ result = ns_server_flushcache(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_FLUSHNAME)) {
+ result = ns_server_flushname(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_STATUS)) {
+ result = ns_server_status(ns_g_server, text);
+ } else if (command_compare(command, NS_COMMAND_FREEZE)) {
+ result = ns_server_freeze(ns_g_server, ISC_TRUE, command);
+ } else if (command_compare(command, NS_COMMAND_UNFREEZE) ||
+ command_compare(command, NS_COMMAND_THAW)) {
+ result = ns_server_freeze(ns_g_server, ISC_FALSE, command);
+ } else if (command_compare(command, NS_COMMAND_RECURSING)) {
+ result = ns_server_dumprecursing(ns_g_server);
+ } else if (command_compare(command, NS_COMMAND_TIMERPOKE)) {
+ result = ISC_R_SUCCESS;
+ isc_timermgr_poke(ns_g_timermgr);
+ } else if (command_compare(command, NS_COMMAND_NULL)) {
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_NOTIFY)) {
+ result = ns_server_notifycommand(ns_g_server, command, text);
+ } else if (command_compare(command, NS_COMMAND_VALIDATION)) {
+ result = ns_server_validation(ns_g_server, command);
+ } else {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "unknown control channel command '%s'",
+ command);
+ result = DNS_R_UNKNOWNCOMMAND;
+ }
+
+ return (result);
+}
diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c
new file mode 100644
index 0000000..3e36446
--- /dev/null
+++ b/bin/named/controlconf.c
@@ -0,0 +1,1461 @@
+/*
+ * 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: controlconf.c,v 1.40.18.10 2006/12/07 04:53:02 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/event.h>
+#include <isc/mem.h>
+#include <isc/net.h>
+#include <isc/netaddr.h>
+#include <isc/random.h>
+#include <isc/result.h>
+#include <isc/stdtime.h>
+#include <isc/string.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <isccfg/namedconf.h>
+
+#include <bind9/check.h>
+
+#include <isccc/alist.h>
+#include <isccc/cc.h>
+#include <isccc/ccmsg.h>
+#include <isccc/events.h>
+#include <isccc/result.h>
+#include <isccc/sexpr.h>
+#include <isccc/symtab.h>
+#include <isccc/util.h>
+
+#include <dns/result.h>
+
+#include <named/config.h>
+#include <named/control.h>
+#include <named/log.h>
+#include <named/server.h>
+
+/*
+ * Note: Listeners and connections are not locked. All event handlers are
+ * executed by the server task, and all callers of exported routines must
+ * be running under the server task.
+ */
+
+typedef struct controlkey controlkey_t;
+typedef ISC_LIST(controlkey_t) controlkeylist_t;
+
+typedef struct controlconnection controlconnection_t;
+typedef ISC_LIST(controlconnection_t) controlconnectionlist_t;
+
+typedef struct controllistener controllistener_t;
+typedef ISC_LIST(controllistener_t) controllistenerlist_t;
+
+struct controlkey {
+ char * keyname;
+ isc_region_t secret;
+ ISC_LINK(controlkey_t) link;
+};
+
+struct controlconnection {
+ isc_socket_t * sock;
+ isccc_ccmsg_t ccmsg;
+ isc_boolean_t ccmsg_valid;
+ isc_boolean_t sending;
+ isc_timer_t * timer;
+ unsigned char buffer[2048];
+ controllistener_t * listener;
+ isc_uint32_t nonce;
+ ISC_LINK(controlconnection_t) link;
+};
+
+struct controllistener {
+ ns_controls_t * controls;
+ isc_mem_t * mctx;
+ isc_task_t * task;
+ isc_sockaddr_t address;
+ isc_socket_t * sock;
+ dns_acl_t * acl;
+ isc_boolean_t listening;
+ isc_boolean_t exiting;
+ controlkeylist_t keys;
+ controlconnectionlist_t connections;
+ isc_sockettype_t type;
+ isc_uint32_t perm;
+ isc_uint32_t owner;
+ isc_uint32_t group;
+ ISC_LINK(controllistener_t) link;
+};
+
+struct ns_controls {
+ ns_server_t *server;
+ controllistenerlist_t listeners;
+ isc_boolean_t shuttingdown;
+ isccc_symtab_t *symtab;
+};
+
+static void control_newconn(isc_task_t *task, isc_event_t *event);
+static void control_recvmessage(isc_task_t *task, isc_event_t *event);
+
+#define CLOCKSKEW 300
+
+static void
+free_controlkey(controlkey_t *key, isc_mem_t *mctx) {
+ if (key->keyname != NULL)
+ isc_mem_free(mctx, key->keyname);
+ if (key->secret.base != NULL)
+ isc_mem_put(mctx, key->secret.base, key->secret.length);
+ isc_mem_put(mctx, key, sizeof(*key));
+}
+
+static void
+free_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) {
+ while (!ISC_LIST_EMPTY(*keylist)) {
+ controlkey_t *key = ISC_LIST_HEAD(*keylist);
+ ISC_LIST_UNLINK(*keylist, key, link);
+ free_controlkey(key, mctx);
+ }
+}
+
+static void
+free_listener(controllistener_t *listener) {
+ INSIST(listener->exiting);
+ INSIST(!listener->listening);
+ INSIST(ISC_LIST_EMPTY(listener->connections));
+
+ if (listener->sock != NULL)
+ isc_socket_detach(&listener->sock);
+
+ free_controlkeylist(&listener->keys, listener->mctx);
+
+ if (listener->acl != NULL)
+ dns_acl_detach(&listener->acl);
+
+ isc_mem_put(listener->mctx, listener, sizeof(*listener));
+}
+
+static void
+maybe_free_listener(controllistener_t *listener) {
+ if (listener->exiting &&
+ !listener->listening &&
+ ISC_LIST_EMPTY(listener->connections))
+ free_listener(listener);
+}
+
+static void
+maybe_free_connection(controlconnection_t *conn) {
+ controllistener_t *listener = conn->listener;
+
+ if (conn->timer != NULL)
+ isc_timer_detach(&conn->timer);
+
+ if (conn->ccmsg_valid) {
+ isccc_ccmsg_cancelread(&conn->ccmsg);
+ return;
+ }
+
+ if (conn->sending) {
+ isc_socket_cancel(conn->sock, listener->task,
+ ISC_SOCKCANCEL_SEND);
+ return;
+ }
+
+ ISC_LIST_UNLINK(listener->connections, conn, link);
+ isc_mem_put(listener->mctx, conn, sizeof(*conn));
+}
+
+static void
+shutdown_listener(controllistener_t *listener) {
+ controlconnection_t *conn;
+ controlconnection_t *next;
+
+ if (!listener->exiting) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+
+ ISC_LIST_UNLINK(listener->controls->listeners, listener, link);
+
+ isc_sockaddr_format(&listener->address, socktext,
+ sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
+ "stopping command channel on %s", socktext);
+ if (listener->type == isc_sockettype_unix)
+ isc_socket_cleanunix(&listener->address, ISC_TRUE);
+ listener->exiting = ISC_TRUE;
+ }
+
+ for (conn = ISC_LIST_HEAD(listener->connections);
+ conn != NULL;
+ conn = next)
+ {
+ next = ISC_LIST_NEXT(conn, link);
+ maybe_free_connection(conn);
+ }
+
+ if (listener->listening)
+ isc_socket_cancel(listener->sock, listener->task,
+ ISC_SOCKCANCEL_ACCEPT);
+
+ maybe_free_listener(listener);
+}
+
+static isc_boolean_t
+address_ok(isc_sockaddr_t *sockaddr, dns_acl_t *acl) {
+ isc_netaddr_t netaddr;
+ isc_result_t result;
+ int match;
+
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+
+ result = dns_acl_match(&netaddr, NULL, acl,
+ &ns_g_server->aclenv, &match, NULL);
+
+ if (result != ISC_R_SUCCESS || match <= 0)
+ return (ISC_FALSE);
+ else
+ return (ISC_TRUE);
+}
+
+static isc_result_t
+control_accept(controllistener_t *listener) {
+ isc_result_t result;
+ result = isc_socket_accept(listener->sock,
+ listener->task,
+ control_newconn, listener);
+ if (result != ISC_R_SUCCESS)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socket_accept() failed: %s",
+ isc_result_totext(result));
+ else
+ listener->listening = ISC_TRUE;
+ return (result);
+}
+
+static isc_result_t
+control_listen(controllistener_t *listener) {
+ isc_result_t result;
+
+ result = isc_socket_listen(listener->sock, 0);
+ if (result != ISC_R_SUCCESS)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socket_listen() failed: %s",
+ isc_result_totext(result));
+ return (result);
+}
+
+static void
+control_next(controllistener_t *listener) {
+ (void)control_accept(listener);
+}
+
+static void
+control_senddone(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sevent = (isc_socketevent_t *) event;
+ controlconnection_t *conn = event->ev_arg;
+ controllistener_t *listener = conn->listener;
+ isc_socket_t *sock = (isc_socket_t *)sevent->ev_sender;
+ isc_result_t result;
+
+ REQUIRE(conn->sending);
+
+ UNUSED(task);
+
+ conn->sending = ISC_FALSE;
+
+ if (sevent->result != ISC_R_SUCCESS &&
+ sevent->result != ISC_R_CANCELED)
+ {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_t peeraddr;
+
+ (void)isc_socket_getpeername(sock, &peeraddr);
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "error sending command response to %s: %s",
+ socktext, isc_result_totext(sevent->result));
+ }
+ isc_event_free(&event);
+
+ result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task,
+ control_recvmessage, conn);
+ if (result != ISC_R_SUCCESS) {
+ isc_socket_detach(&conn->sock);
+ maybe_free_connection(conn);
+ maybe_free_listener(listener);
+ }
+}
+
+static inline void
+log_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_t peeraddr;
+
+ (void)isc_socket_getpeername(ccmsg->sock, &peeraddr);
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_ERROR,
+ "invalid command from %s: %s",
+ socktext, isc_result_totext(result));
+}
+
+static void
+control_recvmessage(isc_task_t *task, isc_event_t *event) {
+ controlconnection_t *conn;
+ controllistener_t *listener;
+ controlkey_t *key;
+ isccc_sexpr_t *request = NULL;
+ isccc_sexpr_t *response = NULL;
+ isccc_region_t ccregion;
+ isccc_region_t secret;
+ isc_stdtime_t now;
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_uint32_t len;
+ isc_buffer_t text;
+ char textarray[1024];
+ isc_result_t result;
+ isc_result_t eresult;
+ isccc_sexpr_t *_ctrl;
+ isccc_time_t sent;
+ isccc_time_t exp;
+ isc_uint32_t nonce;
+
+ REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG);
+
+ conn = event->ev_arg;
+ listener = conn->listener;
+ secret.rstart = NULL;
+
+ /* Is the server shutting down? */
+ if (listener->controls->shuttingdown)
+ goto cleanup;
+
+ if (conn->ccmsg.result != ISC_R_SUCCESS) {
+ if (conn->ccmsg.result != ISC_R_CANCELED &&
+ conn->ccmsg.result != ISC_R_EOF)
+ log_invalid(&conn->ccmsg, conn->ccmsg.result);
+ goto cleanup;
+ }
+
+ request = NULL;
+
+ for (key = ISC_LIST_HEAD(listener->keys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link))
+ {
+ ccregion.rstart = isc_buffer_base(&conn->ccmsg.buffer);
+ ccregion.rend = isc_buffer_used(&conn->ccmsg.buffer);
+ if (secret.rstart != NULL)
+ isc_mem_put(listener->mctx, secret.rstart,
+ REGION_SIZE(secret));
+ secret.rstart = isc_mem_get(listener->mctx, key->secret.length);
+ if (secret.rstart == NULL)
+ goto cleanup;
+ memcpy(secret.rstart, key->secret.base, key->secret.length);
+ secret.rend = secret.rstart + key->secret.length;
+ result = isccc_cc_fromwire(&ccregion, &request, &secret);
+ if (result == ISC_R_SUCCESS)
+ break;
+ else if (result == ISCCC_R_BADAUTH) {
+ /*
+ * For some reason, request is non-NULL when
+ * isccc_cc_fromwire returns ISCCC_R_BADAUTH.
+ */
+ if (request != NULL)
+ isccc_sexpr_free(&request);
+ } else {
+ log_invalid(&conn->ccmsg, result);
+ goto cleanup;
+ }
+ }
+
+ if (key == NULL) {
+ log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
+ goto cleanup;
+ }
+
+ /* We shouldn't be getting a reply. */
+ if (isccc_cc_isreply(request)) {
+ log_invalid(&conn->ccmsg, ISC_R_FAILURE);
+ goto cleanup;
+ }
+
+ isc_stdtime_get(&now);
+
+ /*
+ * Limit exposure to replay attacks.
+ */
+ _ctrl = isccc_alist_lookup(request, "_ctrl");
+ if (_ctrl == NULL) {
+ log_invalid(&conn->ccmsg, ISC_R_FAILURE);
+ goto cleanup;
+ }
+
+ if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) {
+ if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) {
+ log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW);
+ goto cleanup;
+ }
+ } else {
+ log_invalid(&conn->ccmsg, ISC_R_FAILURE);
+ goto cleanup;
+ }
+
+ /*
+ * Expire messages that are too old.
+ */
+ if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS &&
+ now > exp) {
+ log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED);
+ goto cleanup;
+ }
+
+ /*
+ * Duplicate suppression (required for UDP).
+ */
+ isccc_cc_cleansymtab(listener->controls->symtab, now);
+ result = isccc_cc_checkdup(listener->controls->symtab, request, now);
+ if (result != ISC_R_SUCCESS) {
+ if (result == ISC_R_EXISTS)
+ result = ISCCC_R_DUPLICATE;
+ log_invalid(&conn->ccmsg, result);
+ goto cleanup;
+ }
+
+ if (conn->nonce != 0 &&
+ (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS ||
+ conn->nonce != nonce)) {
+ log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
+ goto cleanup;
+ }
+
+ /*
+ * Establish nonce.
+ */
+ while (conn->nonce == 0)
+ isc_random_get(&conn->nonce);
+
+ isc_buffer_init(&text, textarray, sizeof(textarray));
+ eresult = ns_control_docommand(request, &text);
+
+ result = isccc_cc_createresponse(request, now, now + 60, &response);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (eresult != ISC_R_SUCCESS) {
+ isccc_sexpr_t *data;
+
+ data = isccc_alist_lookup(response, "_data");
+ if (data != NULL) {
+ const char *estr = isc_result_totext(eresult);
+ if (isccc_cc_definestring(data, "err", estr) == NULL)
+ goto cleanup;
+ }
+ }
+
+ if (isc_buffer_usedlength(&text) > 0) {
+ isccc_sexpr_t *data;
+
+ data = isccc_alist_lookup(response, "_data");
+ if (data != NULL) {
+ char *str = (char *)isc_buffer_base(&text);
+ if (isccc_cc_definestring(data, "text", str) == NULL)
+ goto cleanup;
+ }
+ }
+
+ _ctrl = isccc_alist_lookup(response, "_ctrl");
+ if (_ctrl == NULL ||
+ isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL)
+ goto cleanup;
+
+ ccregion.rstart = conn->buffer + 4;
+ ccregion.rend = conn->buffer + sizeof(conn->buffer);
+ result = isccc_cc_towire(response, &ccregion, &secret);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_buffer_init(&b, conn->buffer, 4);
+ len = sizeof(conn->buffer) - REGION_SIZE(ccregion);
+ isc_buffer_putuint32(&b, len - 4);
+ r.base = conn->buffer;
+ r.length = len;
+
+ result = isc_socket_send(conn->sock, &r, task, control_senddone, conn);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ conn->sending = ISC_TRUE;
+
+ if (secret.rstart != NULL)
+ isc_mem_put(listener->mctx, secret.rstart,
+ REGION_SIZE(secret));
+ if (request != NULL)
+ isccc_sexpr_free(&request);
+ if (response != NULL)
+ isccc_sexpr_free(&response);
+ return;
+
+ cleanup:
+ if (secret.rstart != NULL)
+ isc_mem_put(listener->mctx, secret.rstart,
+ REGION_SIZE(secret));
+ isc_socket_detach(&conn->sock);
+ isccc_ccmsg_invalidate(&conn->ccmsg);
+ conn->ccmsg_valid = ISC_FALSE;
+ maybe_free_connection(conn);
+ maybe_free_listener(listener);
+ if (request != NULL)
+ isccc_sexpr_free(&request);
+ if (response != NULL)
+ isccc_sexpr_free(&response);
+}
+
+static void
+control_timeout(isc_task_t *task, isc_event_t *event) {
+ controlconnection_t *conn = event->ev_arg;
+
+ UNUSED(task);
+
+ isc_timer_detach(&conn->timer);
+ maybe_free_connection(conn);
+
+ isc_event_free(&event);
+}
+
+static isc_result_t
+newconnection(controllistener_t *listener, isc_socket_t *sock) {
+ controlconnection_t *conn;
+ isc_interval_t interval;
+ isc_result_t result;
+
+ conn = isc_mem_get(listener->mctx, sizeof(*conn));
+ if (conn == NULL)
+ return (ISC_R_NOMEMORY);
+
+ conn->sock = sock;
+ isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg);
+ conn->ccmsg_valid = ISC_TRUE;
+ conn->sending = ISC_FALSE;
+ conn->timer = NULL;
+ isc_interval_set(&interval, 60, 0);
+ result = isc_timer_create(ns_g_timermgr, isc_timertype_once,
+ NULL, &interval, listener->task,
+ control_timeout, conn, &conn->timer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ conn->listener = listener;
+ conn->nonce = 0;
+ ISC_LINK_INIT(conn, link);
+
+ result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task,
+ control_recvmessage, conn);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048);
+
+ ISC_LIST_APPEND(listener->connections, conn, link);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ isccc_ccmsg_invalidate(&conn->ccmsg);
+ if (conn->timer != NULL)
+ isc_timer_detach(&conn->timer);
+ isc_mem_put(listener->mctx, conn, sizeof(*conn));
+ return (result);
+}
+
+static void
+control_newconn(isc_task_t *task, isc_event_t *event) {
+ isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
+ controllistener_t *listener = event->ev_arg;
+ isc_socket_t *sock;
+ isc_sockaddr_t peeraddr;
+ isc_result_t result;
+
+ UNUSED(task);
+
+ listener->listening = ISC_FALSE;
+
+ if (nevent->result != ISC_R_SUCCESS) {
+ if (nevent->result == ISC_R_CANCELED) {
+ shutdown_listener(listener);
+ goto cleanup;
+ }
+ goto restart;
+ }
+
+ sock = nevent->newsocket;
+ (void)isc_socket_getpeername(sock, &peeraddr);
+ if (listener->type == isc_sockettype_tcp &&
+ !address_ok(&peeraddr, listener->acl)) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "rejected command channel message from %s",
+ socktext);
+ isc_socket_detach(&sock);
+ goto restart;
+ }
+
+ result = newconnection(listener, sock);
+ if (result != ISC_R_SUCCESS) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "dropped command channel from %s: %s",
+ socktext, isc_result_totext(result));
+ isc_socket_detach(&sock);
+ goto restart;
+ }
+
+ restart:
+ control_next(listener);
+ cleanup:
+ isc_event_free(&event);
+}
+
+static void
+controls_shutdown(ns_controls_t *controls) {
+ controllistener_t *listener;
+ controllistener_t *next;
+
+ for (listener = ISC_LIST_HEAD(controls->listeners);
+ listener != NULL;
+ listener = next)
+ {
+ /*
+ * This is asynchronous. As listeners shut down, they will
+ * call their callbacks.
+ */
+ next = ISC_LIST_NEXT(listener, link);
+ shutdown_listener(listener);
+ }
+}
+
+void
+ns_controls_shutdown(ns_controls_t *controls) {
+ controls_shutdown(controls);
+ controls->shuttingdown = ISC_TRUE;
+}
+
+static isc_result_t
+cfgkeylist_find(const cfg_obj_t *keylist, const char *keyname,
+ const cfg_obj_t **objp)
+{
+ const cfg_listelt_t *element;
+ const char *str;
+ const cfg_obj_t *obj;
+
+ 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)
+ break;
+ }
+ if (element == NULL)
+ return (ISC_R_NOTFOUND);
+ obj = cfg_listelt_value(element);
+ *objp = obj;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+controlkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx,
+ controlkeylist_t *keyids)
+{
+ const cfg_listelt_t *element;
+ char *newstr = NULL;
+ const char *str;
+ const cfg_obj_t *obj;
+ controlkey_t *key;
+
+ for (element = cfg_list_first(keylist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(obj);
+ newstr = isc_mem_strdup(mctx, str);
+ if (newstr == NULL)
+ goto cleanup;
+ key = isc_mem_get(mctx, sizeof(*key));
+ if (key == NULL)
+ goto cleanup;
+ key->keyname = newstr;
+ key->secret.base = NULL;
+ key->secret.length = 0;
+ ISC_LINK_INIT(key, link);
+ ISC_LIST_APPEND(*keyids, key, link);
+ newstr = NULL;
+ }
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (newstr != NULL)
+ isc_mem_free(mctx, newstr);
+ free_controlkeylist(keyids, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static void
+register_keys(const cfg_obj_t *control, const cfg_obj_t *keylist,
+ controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext)
+{
+ controlkey_t *keyid, *next;
+ const cfg_obj_t *keydef;
+ char secret[1024];
+ isc_buffer_t b;
+ isc_result_t result;
+
+ /*
+ * Find the keys corresponding to the keyids used by this listener.
+ */
+ for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) {
+ next = ISC_LIST_NEXT(keyid, link);
+
+ result = cfgkeylist_find(keylist, keyid->keyname, &keydef);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't find key '%s' for use with "
+ "command channel %s",
+ keyid->keyname, socktext);
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ } else {
+ const cfg_obj_t *algobj = NULL;
+ const cfg_obj_t *secretobj = NULL;
+ const char *algstr = NULL;
+ const char *secretstr = NULL;
+
+ (void)cfg_map_get(keydef, "algorithm", &algobj);
+ (void)cfg_map_get(keydef, "secret", &secretobj);
+ INSIST(algobj != NULL && secretobj != NULL);
+
+ algstr = cfg_obj_asstring(algobj);
+ secretstr = cfg_obj_asstring(secretobj);
+
+ if (ns_config_getkeyalgorithm(algstr, NULL, NULL) !=
+ ISC_R_SUCCESS)
+ {
+ cfg_obj_log(control, ns_g_lctx,
+ ISC_LOG_WARNING,
+ "unsupported algorithm '%s' in "
+ "key '%s' for use with command "
+ "channel %s",
+ algstr, keyid->keyname, socktext);
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ continue;
+ }
+
+ isc_buffer_init(&b, secret, sizeof(secret));
+ result = isc_base64_decodestring(secretstr, &b);
+
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
+ "secret for key '%s' on "
+ "command channel %s: %s",
+ keyid->keyname, socktext,
+ isc_result_totext(result));
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ continue;
+ }
+
+ keyid->secret.length = isc_buffer_usedlength(&b);
+ keyid->secret.base = isc_mem_get(mctx,
+ keyid->secret.length);
+ if (keyid->secret.base == NULL) {
+ cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't register key '%s': "
+ "out of memory", keyid->keyname);
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ break;
+ }
+ memcpy(keyid->secret.base, isc_buffer_base(&b),
+ keyid->secret.length);
+ }
+ }
+}
+
+#define CHECK(x) \
+ do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+static isc_result_t
+get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) {
+ isc_result_t result;
+ cfg_parser_t *pctx = NULL;
+ cfg_obj_t *config = NULL;
+ const cfg_obj_t *key = NULL;
+ const cfg_obj_t *algobj = NULL;
+ const cfg_obj_t *secretobj = NULL;
+ const char *algstr = NULL;
+ const char *secretstr = NULL;
+ controlkey_t *keyid = NULL;
+ char secret[1024];
+ isc_buffer_t b;
+
+ CHECK(cfg_parser_create(mctx, ns_g_lctx, &pctx));
+ CHECK(cfg_parse_file(pctx, ns_g_keyfile, &cfg_type_rndckey, &config));
+ CHECK(cfg_map_get(config, "key", &key));
+
+ keyid = isc_mem_get(mctx, sizeof(*keyid));
+ if (keyid == NULL)
+ CHECK(ISC_R_NOMEMORY);
+ keyid->keyname = isc_mem_strdup(mctx,
+ cfg_obj_asstring(cfg_map_getname(key)));
+ keyid->secret.base = NULL;
+ keyid->secret.length = 0;
+ ISC_LINK_INIT(keyid, link);
+ if (keyid->keyname == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ CHECK(bind9_check_key(key, ns_g_lctx));
+
+ (void)cfg_map_get(key, "algorithm", &algobj);
+ (void)cfg_map_get(key, "secret", &secretobj);
+ INSIST(algobj != NULL && secretobj != NULL);
+
+ algstr = cfg_obj_asstring(algobj);
+ secretstr = cfg_obj_asstring(secretobj);
+
+ if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != ISC_R_SUCCESS) {
+ cfg_obj_log(key, ns_g_lctx,
+ ISC_LOG_WARNING,
+ "unsupported algorithm '%s' in "
+ "key '%s' for use with command "
+ "channel",
+ algstr, keyid->keyname);
+ goto cleanup;
+ }
+
+ isc_buffer_init(&b, secret, sizeof(secret));
+ result = isc_base64_decodestring(secretstr, &b);
+
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
+ "secret for key '%s' on command channel: %s",
+ keyid->keyname, isc_result_totext(result));
+ CHECK(result);
+ }
+
+ keyid->secret.length = isc_buffer_usedlength(&b);
+ keyid->secret.base = isc_mem_get(mctx,
+ keyid->secret.length);
+ if (keyid->secret.base == NULL) {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't register key '%s': "
+ "out of memory", keyid->keyname);
+ CHECK(ISC_R_NOMEMORY);
+ }
+ memcpy(keyid->secret.base, isc_buffer_base(&b),
+ keyid->secret.length);
+ ISC_LIST_APPEND(*keyids, keyid, link);
+ keyid = NULL;
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (keyid != NULL)
+ free_controlkey(keyid, mctx);
+ if (config != NULL)
+ cfg_obj_destroy(pctx, &config);
+ if (pctx != NULL)
+ cfg_parser_destroy(&pctx);
+ return (result);
+}
+
+/*
+ * Ensures that both '*global_keylistp' and '*control_keylistp' are
+ * valid or both are NULL.
+ */
+static void
+get_key_info(const cfg_obj_t *config, const cfg_obj_t *control,
+ const cfg_obj_t **global_keylistp,
+ const cfg_obj_t **control_keylistp)
+{
+ isc_result_t result;
+ const cfg_obj_t *control_keylist = NULL;
+ const cfg_obj_t *global_keylist = NULL;
+
+ REQUIRE(global_keylistp != NULL && *global_keylistp == NULL);
+ REQUIRE(control_keylistp != NULL && *control_keylistp == NULL);
+
+ control_keylist = cfg_tuple_get(control, "keys");
+
+ if (!cfg_obj_isvoid(control_keylist) &&
+ cfg_list_first(control_keylist) != NULL) {
+ result = cfg_map_get(config, "key", &global_keylist);
+
+ if (result == ISC_R_SUCCESS) {
+ *global_keylistp = global_keylist;
+ *control_keylistp = control_keylist;
+ }
+ }
+}
+
+static void
+update_listener(ns_controls_t *cp, controllistener_t **listenerp,
+ const cfg_obj_t *control, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext, isc_sockettype_t type)
+{
+ controllistener_t *listener;
+ const cfg_obj_t *allow;
+ const cfg_obj_t *global_keylist = NULL;
+ const cfg_obj_t *control_keylist = NULL;
+ dns_acl_t *new_acl = NULL;
+ controlkeylist_t keys;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (listener = ISC_LIST_HEAD(cp->listeners);
+ listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ if (isc_sockaddr_equal(addr, &listener->address))
+ break;
+
+ if (listener == NULL) {
+ *listenerp = NULL;
+ return;
+ }
+
+ /*
+ * There is already a listener for this sockaddr.
+ * Update the access list and key information.
+ *
+ * First try to deal with the key situation. There are a few
+ * possibilities:
+ * (a) It had an explicit keylist and still has an explicit keylist.
+ * (b) It had an automagic key and now has an explicit keylist.
+ * (c) It had an explicit keylist and now needs an automagic key.
+ * (d) It has an automagic key and still needs the automagic key.
+ *
+ * (c) and (d) are the annoying ones. The caller needs to know
+ * that it should use the automagic configuration for key information
+ * in place of the named.conf configuration.
+ *
+ * XXXDCL There is one other hazard that has not been dealt with,
+ * the problem that if a key change is being caused by a control
+ * channel reload, then the response will be with the new key
+ * and not able to be decrypted by the client.
+ */
+ if (control != NULL)
+ get_key_info(config, control, &global_keylist,
+ &control_keylist);
+
+ if (control_keylist != NULL) {
+ INSIST(global_keylist != NULL);
+
+ ISC_LIST_INIT(keys);
+ result = controlkeylist_fromcfg(control_keylist,
+ listener->mctx, &keys);
+ if (result == ISC_R_SUCCESS) {
+ free_controlkeylist(&listener->keys, listener->mctx);
+ listener->keys = keys;
+ register_keys(control, global_keylist, &listener->keys,
+ listener->mctx, socktext);
+ }
+ } else {
+ free_controlkeylist(&listener->keys, listener->mctx);
+ result = get_rndckey(listener->mctx, &listener->keys);
+ }
+
+ if (result != ISC_R_SUCCESS && global_keylist != NULL) {
+ /*
+ * This message might be a little misleading since the
+ * "new keys" might in fact be identical to the old ones,
+ * but tracking whether they are identical just for the
+ * sake of avoiding this message would be too much trouble.
+ */
+ if (control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't install new keys for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "couldn't install new keys for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ }
+
+ /*
+ * Now, keep the old access list unless a new one can be made.
+ */
+ if (control != NULL && type == isc_sockettype_tcp) {
+ allow = cfg_tuple_get(control, "allow");
+ result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
+ aclconfctx, listener->mctx,
+ &new_acl);
+ } else {
+ result = dns_acl_any(listener->mctx, &new_acl);
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ dns_acl_detach(&listener->acl);
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+ /* XXXDCL say the old acl is still used? */
+ } else if (control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't install new acl for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "couldn't install new acl for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+
+ if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
+ isc_uint32_t perm, owner, group;
+ perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
+ owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner"));
+ group = cfg_obj_asuint32(cfg_tuple_get(control, "group"));
+ result = ISC_R_SUCCESS;
+ if (listener->perm != perm || listener->owner != owner ||
+ listener->group != group)
+ result = isc_socket_permunix(&listener->address, perm,
+ owner, group);
+ if (result == ISC_R_SUCCESS) {
+ listener->perm = perm;
+ listener->owner = owner;
+ listener->group = group;
+ } else if (control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't update ownership/permission for "
+ "command channel %s", socktext);
+ }
+
+ *listenerp = listener;
+}
+
+static void
+add_listener(ns_controls_t *cp, controllistener_t **listenerp,
+ const cfg_obj_t *control, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext, isc_sockettype_t type)
+{
+ isc_mem_t *mctx = cp->server->mctx;
+ controllistener_t *listener;
+ const cfg_obj_t *allow;
+ const cfg_obj_t *global_keylist = NULL;
+ const cfg_obj_t *control_keylist = NULL;
+ dns_acl_t *new_acl = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ listener = isc_mem_get(mctx, sizeof(*listener));
+ if (listener == NULL)
+ result = ISC_R_NOMEMORY;
+
+ if (result == ISC_R_SUCCESS) {
+ listener->controls = cp;
+ listener->mctx = mctx;
+ listener->task = cp->server->task;
+ listener->address = *addr;
+ listener->sock = NULL;
+ listener->listening = ISC_FALSE;
+ listener->exiting = ISC_FALSE;
+ listener->acl = NULL;
+ listener->type = type;
+ listener->perm = 0;
+ listener->owner = 0;
+ listener->group = 0;
+ ISC_LINK_INIT(listener, link);
+ ISC_LIST_INIT(listener->keys);
+ ISC_LIST_INIT(listener->connections);
+
+ /*
+ * Make the acl.
+ */
+ if (control != NULL && type == isc_sockettype_tcp) {
+ allow = cfg_tuple_get(control, "allow");
+ result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
+ aclconfctx, mctx, &new_acl);
+ } else {
+ result = dns_acl_any(mctx, &new_acl);
+ }
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+
+ if (config != NULL)
+ get_key_info(config, control, &global_keylist,
+ &control_keylist);
+
+ if (control_keylist != NULL) {
+ result = controlkeylist_fromcfg(control_keylist,
+ listener->mctx,
+ &listener->keys);
+ if (result == ISC_R_SUCCESS)
+ register_keys(control, global_keylist,
+ &listener->keys,
+ listener->mctx, socktext);
+ } else
+ result = get_rndckey(mctx, &listener->keys);
+
+ if (result != ISC_R_SUCCESS && control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't install keys for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ int pf = isc_sockaddr_pf(&listener->address);
+ if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
+#ifdef ISC_PLATFORM_HAVESYSUNH
+ (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) ||
+#endif
+ (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
+ result = ISC_R_FAMILYNOSUPPORT;
+ }
+
+ if (result == ISC_R_SUCCESS && type == isc_sockettype_unix)
+ isc_socket_cleanunix(&listener->address, ISC_FALSE);
+
+ if (result == ISC_R_SUCCESS)
+ result = isc_socket_create(ns_g_socketmgr,
+ isc_sockaddr_pf(&listener->address),
+ type, &listener->sock);
+
+ if (result == ISC_R_SUCCESS)
+ result = isc_socket_bind(listener->sock,
+ &listener->address);
+
+ if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
+ listener->perm = cfg_obj_asuint32(cfg_tuple_get(control,
+ "perm"));
+ listener->owner = cfg_obj_asuint32(cfg_tuple_get(control,
+ "owner"));
+ listener->group = cfg_obj_asuint32(cfg_tuple_get(control,
+ "group"));
+ result = isc_socket_permunix(&listener->address, listener->perm,
+ listener->owner, listener->group);
+ }
+ if (result == ISC_R_SUCCESS)
+ result = control_listen(listener);
+
+ if (result == ISC_R_SUCCESS)
+ result = control_accept(listener);
+
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
+ "command channel listening on %s", socktext);
+ *listenerp = listener;
+
+ } else {
+ if (listener != NULL) {
+ listener->exiting = ISC_TRUE;
+ free_listener(listener);
+ }
+
+ if (control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't add command channel %s: %s",
+ socktext, isc_result_totext(result));
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
+ "couldn't add command channel %s: %s",
+ socktext, isc_result_totext(result));
+
+ *listenerp = NULL;
+ }
+
+ /* XXXDCL return error results? fail hard? */
+}
+
+isc_result_t
+ns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx)
+{
+ controllistener_t *listener;
+ controllistenerlist_t new_listeners;
+ const cfg_obj_t *controlslist = NULL;
+ const cfg_listelt_t *element, *element2;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+
+ ISC_LIST_INIT(new_listeners);
+
+ /*
+ * Get the list of named.conf 'controls' statements.
+ */
+ (void)cfg_map_get(config, "controls", &controlslist);
+
+ /*
+ * Run through the new control channel list, noting sockets that
+ * are already being listened on and moving them to the new list.
+ *
+ * Identifying duplicate addr/port combinations is left to either
+ * the underlying config code, or to the bind attempt getting an
+ * address-in-use error.
+ */
+ if (controlslist != NULL) {
+ for (element = cfg_list_first(controlslist);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ const cfg_obj_t *controls;
+ const cfg_obj_t *inetcontrols = NULL;
+
+ controls = cfg_listelt_value(element);
+ (void)cfg_map_get(controls, "inet", &inetcontrols);
+ if (inetcontrols == NULL)
+ continue;
+
+ for (element2 = cfg_list_first(inetcontrols);
+ element2 != NULL;
+ element2 = cfg_list_next(element2)) {
+ const cfg_obj_t *control;
+ const cfg_obj_t *obj;
+ isc_sockaddr_t addr;
+
+ /*
+ * The parser handles BIND 8 configuration file
+ * syntax, so it allows unix phrases as well
+ * inet phrases with no keys{} clause.
+ */
+ control = cfg_listelt_value(element2);
+
+ obj = cfg_tuple_get(control, "address");
+ addr = *cfg_obj_assockaddr(obj);
+ if (isc_sockaddr_getport(&addr) == 0)
+ isc_sockaddr_setport(&addr,
+ NS_CONTROL_PORT);
+
+ isc_sockaddr_format(&addr, socktext,
+ sizeof(socktext));
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL,
+ ISC_LOG_DEBUG(9),
+ "processing control channel %s",
+ socktext);
+
+ update_listener(cp, &listener, control, config,
+ &addr, aclconfctx, socktext,
+ isc_sockettype_tcp);
+
+ if (listener != NULL)
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(cp->listeners,
+ listener, link);
+ else
+ /*
+ * This is a new listener.
+ */
+ add_listener(cp, &listener, control,
+ config, &addr, aclconfctx,
+ socktext,
+ isc_sockettype_tcp);
+
+ if (listener != NULL)
+ ISC_LIST_APPEND(new_listeners,
+ listener, link);
+ }
+ }
+ for (element = cfg_list_first(controlslist);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ const cfg_obj_t *controls;
+ const cfg_obj_t *unixcontrols = NULL;
+
+ controls = cfg_listelt_value(element);
+ (void)cfg_map_get(controls, "unix", &unixcontrols);
+ if (unixcontrols == NULL)
+ continue;
+
+ for (element2 = cfg_list_first(unixcontrols);
+ element2 != NULL;
+ element2 = cfg_list_next(element2)) {
+ const cfg_obj_t *control;
+ const cfg_obj_t *path;
+ isc_sockaddr_t addr;
+ isc_result_t result;
+
+ /*
+ * The parser handles BIND 8 configuration file
+ * syntax, so it allows unix phrases as well
+ * inet phrases with no keys{} clause.
+ */
+ control = cfg_listelt_value(element2);
+
+ path = cfg_tuple_get(control, "path");
+ result = isc_sockaddr_frompath(&addr,
+ cfg_obj_asstring(path));
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL,
+ ISC_LOG_DEBUG(9),
+ "control channel '%s': %s",
+ cfg_obj_asstring(path),
+ isc_result_totext(result));
+ continue;
+ }
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL,
+ ISC_LOG_DEBUG(9),
+ "processing control channel '%s'",
+ cfg_obj_asstring(path));
+
+ update_listener(cp, &listener, control, config,
+ &addr, aclconfctx,
+ cfg_obj_asstring(path),
+ isc_sockettype_unix);
+
+ if (listener != NULL)
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(cp->listeners,
+ listener, link);
+ else
+ /*
+ * This is a new listener.
+ */
+ add_listener(cp, &listener, control,
+ config, &addr, aclconfctx,
+ cfg_obj_asstring(path),
+ isc_sockettype_unix);
+
+ if (listener != NULL)
+ ISC_LIST_APPEND(new_listeners,
+ listener, link);
+ }
+ }
+ } else {
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ isc_sockaddr_t addr;
+
+ if (i == 0) {
+ struct in_addr localhost;
+
+ if (isc_net_probeipv4() != ISC_R_SUCCESS)
+ continue;
+ localhost.s_addr = htonl(INADDR_LOOPBACK);
+ isc_sockaddr_fromin(&addr, &localhost, 0);
+ } else {
+ if (isc_net_probeipv6() != ISC_R_SUCCESS)
+ continue;
+ isc_sockaddr_fromin6(&addr,
+ &in6addr_loopback, 0);
+ }
+ isc_sockaddr_setport(&addr, NS_CONTROL_PORT);
+
+ isc_sockaddr_format(&addr, socktext, sizeof(socktext));
+
+ update_listener(cp, &listener, NULL, NULL,
+ &addr, NULL, socktext,
+ isc_sockettype_tcp);
+
+ if (listener != NULL)
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(cp->listeners,
+ listener, link);
+ else
+ /*
+ * This is a new listener.
+ */
+ add_listener(cp, &listener, NULL, NULL,
+ &addr, NULL, socktext,
+ isc_sockettype_tcp);
+
+ if (listener != NULL)
+ ISC_LIST_APPEND(new_listeners,
+ listener, link);
+ }
+ }
+
+ /*
+ * ns_control_shutdown() will stop whatever is on the global
+ * listeners list, which currently only has whatever sockaddrs
+ * were in the previous configuration (if any) that do not
+ * remain in the current configuration.
+ */
+ controls_shutdown(cp);
+
+ /*
+ * Put all of the valid listeners on the listeners list.
+ * Anything already on listeners in the process of shutting
+ * down will be taken care of by listen_done().
+ */
+ ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp) {
+ isc_mem_t *mctx = server->mctx;
+ isc_result_t result;
+ ns_controls_t *controls = isc_mem_get(mctx, sizeof(*controls));
+
+ if (controls == NULL)
+ return (ISC_R_NOMEMORY);
+ controls->server = server;
+ ISC_LIST_INIT(controls->listeners);
+ controls->shuttingdown = ISC_FALSE;
+ controls->symtab = NULL;
+ result = isccc_cc_createsymtab(&controls->symtab);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(server->mctx, controls, sizeof(*controls));
+ return (result);
+ }
+ *ctrlsp = controls;
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_controls_destroy(ns_controls_t **ctrlsp) {
+ ns_controls_t *controls = *ctrlsp;
+
+ REQUIRE(ISC_LIST_EMPTY(controls->listeners));
+
+ isccc_symtab_destroy(&controls->symtab);
+ isc_mem_put(controls->server->mctx, controls, sizeof(*controls));
+ *ctrlsp = NULL;
+}
diff --git a/bin/named/include/named/builtin.h b/bin/named/include/named/builtin.h
new file mode 100644
index 0000000..37a3e76
--- /dev/null
+++ b/bin/named/include/named/builtin.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001 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: builtin.h,v 1.2.18.2 2005/04/29 00:15:34 marka Exp $ */
+
+#ifndef NAMED_BUILTIN_H
+#define NAMED_BUILTIN_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+
+isc_result_t ns_builtin_init(void);
+
+void ns_builtin_deinit(void);
+
+#endif /* NAMED_BUILTIN_H */
diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h
new file mode 100644
index 0000000..0cf7985
--- /dev/null
+++ b/bin/named/include/named/client.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: client.h,v 1.69.18.9 2006/06/06 00:11:41 marka Exp $ */
+
+#ifndef NAMED_CLIENT_H
+#define NAMED_CLIENT_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * This module defines two objects, ns_client_t and ns_clientmgr_t.
+ *
+ * An ns_client_t object handles incoming DNS requests from clients
+ * on a given network interface.
+ *
+ * Each ns_client_t object can handle only one TCP connection or UDP
+ * request at a time. Therefore, several ns_client_t objects are
+ * typically created to serve each network interface, e.g., one
+ * for handling TCP requests and a few (one per CPU) for handling
+ * UDP requests.
+ *
+ * Incoming requests are classified as queries, zone transfer
+ * requests, update requests, notify requests, etc, and handed off
+ * to the appropriate request handler. When the request has been
+ * fully handled (which can be much later), the ns_client_t must be
+ * notified of this by calling one of the following functions
+ * exactly once in the context of its task:
+ * \code
+ * ns_client_send() (sending a non-error response)
+ * ns_client_sendraw() (sending a raw response)
+ * ns_client_error() (sending an error response)
+ * ns_client_next() (sending no response)
+ *\endcode
+ * This will release any resources used by the request and
+ * and allow the ns_client_t to listen for the next request.
+ *
+ * A ns_clientmgr_t manages a number of ns_client_t objects.
+ * New ns_client_t objects are created by calling
+ * ns_clientmgr_createclients(). They are destroyed by
+ * destroying their manager.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/stdtime.h>
+#include <isc/quota.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/tcpmsg.h>
+#include <dns/types.h>
+
+#include <named/types.h>
+#include <named/query.h>
+
+/***
+ *** Types
+ ***/
+
+typedef ISC_LIST(ns_client_t) client_list_t;
+
+/*% nameserver client structure */
+struct ns_client {
+ unsigned int magic;
+ isc_mem_t * mctx;
+ ns_clientmgr_t * manager;
+ int state;
+ int newstate;
+ int naccepts;
+ int nreads;
+ int nsends;
+ int nrecvs;
+ int nupdates;
+ int nctls;
+ int references;
+ unsigned int attributes;
+ isc_task_t * task;
+ dns_view_t * view;
+ dns_dispatch_t * dispatch;
+ isc_socket_t * udpsocket;
+ isc_socket_t * tcplistener;
+ isc_socket_t * tcpsocket;
+ unsigned char * tcpbuf;
+ dns_tcpmsg_t tcpmsg;
+ isc_boolean_t tcpmsg_valid;
+ isc_timer_t * timer;
+ isc_boolean_t timerset;
+ dns_message_t * message;
+ isc_socketevent_t * sendevent;
+ isc_socketevent_t * recvevent;
+ unsigned char * recvbuf;
+ dns_rdataset_t * opt;
+ isc_uint16_t udpsize;
+ isc_uint16_t extflags;
+ isc_int16_t ednsversion; /* -1 noedns */
+ void (*next)(ns_client_t *);
+ void (*shutdown)(void *arg, isc_result_t result);
+ void *shutdown_arg;
+ ns_query_t query;
+ isc_stdtime_t requesttime;
+ isc_stdtime_t now;
+ dns_name_t signername; /*%< [T]SIG key name */
+ dns_name_t * signer; /*%< NULL if not valid sig */
+ isc_boolean_t mortal; /*%< Die after handling request */
+ isc_quota_t *tcpquota;
+ isc_quota_t *recursionquota;
+ ns_interface_t *interface;
+ isc_sockaddr_t peeraddr;
+ isc_boolean_t peeraddr_valid;
+ struct in6_pktinfo pktinfo;
+ isc_event_t ctlevent;
+ /*%
+ * Information about recent FORMERR response(s), for
+ * FORMERR loop avoidance. This is separate for each
+ * client object rather than global only to avoid
+ * the need for locking.
+ */
+ struct {
+ isc_sockaddr_t addr;
+ isc_stdtime_t time;
+ dns_messageid_t id;
+ } formerrcache;
+ ISC_LINK(ns_client_t) link;
+ /*%
+ * The list 'link' is part of, or NULL if not on any list.
+ */
+ client_list_t *list;
+};
+
+#define NS_CLIENT_MAGIC ISC_MAGIC('N','S','C','c')
+#define NS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, NS_CLIENT_MAGIC)
+
+#define NS_CLIENTATTR_TCP 0x01
+#define NS_CLIENTATTR_RA 0x02 /*%< Client gets recusive service */
+#define NS_CLIENTATTR_PKTINFO 0x04 /*%< pktinfo is valid */
+#define NS_CLIENTATTR_MULTICAST 0x08 /*%< recv'd from multicast */
+#define NS_CLIENTATTR_WANTDNSSEC 0x10 /*%< include dnssec records */
+
+extern unsigned int ns_client_requests;
+
+/***
+ *** Functions
+ ***/
+
+/*%
+ * Note! These ns_client_ routines MUST be called ONLY from the client's
+ * task in order to ensure synchronization.
+ */
+
+void
+ns_client_send(ns_client_t *client);
+/*%
+ * Finish processing the current client request and
+ * send client->message as a response.
+ * \brief
+ * Note! These ns_client_ routines MUST be called ONLY from the client's
+ * task in order to ensure synchronization.
+ */
+
+void
+ns_client_sendraw(ns_client_t *client, dns_message_t *msg);
+/*%
+ * Finish processing the current client request and
+ * send msg as a response using client->message->id for the id.
+ */
+
+void
+ns_client_error(ns_client_t *client, isc_result_t result);
+/*%
+ * Finish processing the current client request and return
+ * an error response to the client. The error response
+ * will have an RCODE determined by 'result'.
+ */
+
+void
+ns_client_next(ns_client_t *client, isc_result_t result);
+/*%
+ * Finish processing the current client request,
+ * return no response to the client.
+ */
+
+isc_boolean_t
+ns_client_shuttingdown(ns_client_t *client);
+/*%
+ * Return ISC_TRUE iff the client is currently shutting down.
+ */
+
+void
+ns_client_attach(ns_client_t *source, ns_client_t **target);
+/*%
+ * Attach '*targetp' to 'source'.
+ */
+
+void
+ns_client_detach(ns_client_t **clientp);
+/*%
+ * Detach '*clientp' from its client.
+ */
+
+isc_result_t
+ns_client_replace(ns_client_t *client);
+/*%
+ * Try to replace the current client with a new one, so that the
+ * current one can go off and do some lengthy work without
+ * leaving the dispatch/socket without service.
+ */
+
+void
+ns_client_settimeout(ns_client_t *client, unsigned int seconds);
+/*%
+ * Set a timer in the client to go off in the specified amount of time.
+ */
+
+isc_result_t
+ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, ns_clientmgr_t **managerp);
+/*%
+ * Create a client manager.
+ */
+
+void
+ns_clientmgr_destroy(ns_clientmgr_t **managerp);
+/*%
+ * Destroy a client manager and all ns_client_t objects
+ * managed by it.
+ */
+
+isc_result_t
+ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
+ ns_interface_t *ifp, isc_boolean_t tcp);
+/*%
+ * Create up to 'n' clients listening on interface 'ifp'.
+ * If 'tcp' is ISC_TRUE, the clients will listen for TCP connections,
+ * otherwise for UDP requests.
+ */
+
+isc_sockaddr_t *
+ns_client_getsockaddr(ns_client_t *client);
+/*%
+ * Get the socket address of the client whose request is
+ * currently being processed.
+ */
+
+isc_result_t
+ns_client_checkaclsilent(ns_client_t *client,dns_acl_t *acl,
+ isc_boolean_t default_allow);
+
+/*%
+ * Convenience function for client request ACL checking.
+ *
+ * Check the current client request against 'acl'. If 'acl'
+ * is NULL, allow the request iff 'default_allow' is ISC_TRUE.
+ *
+ * Notes:
+ *\li This is appropriate for checking allow-update,
+ * allow-query, allow-transfer, etc. It is not appropriate
+ * for checking the blackhole list because we treat positive
+ * matches as "allow" and negative matches as "deny"; in
+ * the case of the blackhole list this would be backwards.
+ *
+ * Requires:
+ *\li 'client' points to a valid client.
+ *\li 'acl' points to a valid ACL, or is NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS if the request should be allowed
+ * \li ISC_R_REFUSED if the request should be denied
+ *\li No other return values are possible.
+ */
+
+isc_result_t
+ns_client_checkacl(ns_client_t *client,
+ const char *opname, dns_acl_t *acl,
+ isc_boolean_t default_allow,
+ int log_level);
+/*%
+ * Like ns_client_checkacl, but also logs the outcome of the
+ * check at log level 'log_level' if denied, and at debug 3
+ * if approved. Log messages will refer to the request as
+ * an 'opname' request.
+ *
+ * Requires:
+ *\li Those of ns_client_checkaclsilent(), and:
+ *
+ *\li 'opname' points to a null-terminated string.
+ */
+
+void
+ns_client_log(ns_client_t *client, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level,
+ const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6);
+
+void
+ns_client_logv(ns_client_t *client, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *fmt, va_list ap) ISC_FORMAT_PRINTF(5, 0);
+
+void
+ns_client_aclmsg(const char *msg, dns_name_t *name, dns_rdatatype_t type,
+ dns_rdataclass_t rdclass, char *buf, size_t len);
+
+#define NS_CLIENT_ACLMSGSIZE(x) \
+ (DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE + \
+ DNS_RDATACLASS_FORMATSIZE + sizeof(x) + sizeof("'/'"))
+
+void
+ns_client_recursing(ns_client_t *client);
+/*%
+ * Add client to end of th recursing list.
+ */
+
+void
+ns_client_killoldestquery(ns_client_t *client);
+/*%
+ * Kill the oldest recursive query (recursing list head).
+ */
+
+void
+ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager);
+/*%
+ * Dump the outstanding recursive queries to 'f'.
+ */
+
+void
+ns_client_qnamereplace(ns_client_t *client, dns_name_t *name);
+/*%
+ * Replace the qname.
+ */
+
+isc_boolean_t
+ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ dns_rdataclass_t rdclass, void *arg);
+/*%
+ * Isself callback.
+ */
+
+#endif /* NAMED_CLIENT_H */
diff --git a/bin/named/include/named/config.h b/bin/named/include/named/config.h
new file mode 100644
index 0000000..e8e6038
--- /dev/null
+++ b/bin/named/include/named/config.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001, 2002 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: config.h,v 1.6.18.6 2006/02/28 03:10:47 marka Exp $ */
+
+#ifndef NAMED_CONFIG_H
+#define NAMED_CONFIG_H 1
+
+/*! \file */
+
+#include <isccfg/cfg.h>
+
+#include <dns/types.h>
+#include <dns/zone.h>
+
+isc_result_t
+ns_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf);
+
+isc_result_t
+ns_config_get(const cfg_obj_t **maps, const char* name, const cfg_obj_t **obj);
+
+isc_result_t
+ns_checknames_get(const cfg_obj_t **maps, const char* name,
+ const cfg_obj_t **obj);
+
+int
+ns_config_listcount(const cfg_obj_t *list);
+
+isc_result_t
+ns_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass,
+ dns_rdataclass_t *classp);
+
+isc_result_t
+ns_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype,
+ dns_rdatatype_t *typep);
+
+dns_zonetype_t
+ns_config_getzonetype(const cfg_obj_t *zonetypeobj);
+
+isc_result_t
+ns_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list,
+ in_port_t defport, isc_mem_t *mctx,
+ isc_sockaddr_t **addrsp, isc_uint32_t *countp);
+
+void
+ns_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ isc_uint32_t count);
+
+isc_result_t
+ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
+ isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ dns_name_t ***keys, isc_uint32_t *countp);
+
+void
+ns_config_putipandkeylist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ dns_name_t ***keys, isc_uint32_t count);
+
+isc_result_t
+ns_config_getport(const cfg_obj_t *config, in_port_t *portp);
+
+isc_result_t
+ns_config_getkeyalgorithm(const char *str, dns_name_t **name,
+ isc_uint16_t *digestbits);
+
+#endif /* NAMED_CONFIG_H */
diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h
new file mode 100644
index 0000000..5b7e5f4
--- /dev/null
+++ b/bin/named/include/named/control.h
@@ -0,0 +1,93 @@
+/*
+ * 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: control.h,v 1.14.18.8 2006/03/09 23:46:20 marka Exp $ */
+
+#ifndef NAMED_CONTROL_H
+#define NAMED_CONTROL_H 1
+
+/*! \file
+ * \brief
+ * The name server command channel.
+ */
+
+#include <isccc/types.h>
+
+#include <isccfg/aclconf.h>
+
+#include <named/types.h>
+
+#define NS_CONTROL_PORT 953
+
+#define NS_COMMAND_STOP "stop"
+#define NS_COMMAND_HALT "halt"
+#define NS_COMMAND_RELOAD "reload"
+#define NS_COMMAND_RECONFIG "reconfig"
+#define NS_COMMAND_REFRESH "refresh"
+#define NS_COMMAND_RETRANSFER "retransfer"
+#define NS_COMMAND_DUMPSTATS "stats"
+#define NS_COMMAND_QUERYLOG "querylog"
+#define NS_COMMAND_DUMPDB "dumpdb"
+#define NS_COMMAND_TRACE "trace"
+#define NS_COMMAND_NOTRACE "notrace"
+#define NS_COMMAND_FLUSH "flush"
+#define NS_COMMAND_FLUSHNAME "flushname"
+#define NS_COMMAND_STATUS "status"
+#define NS_COMMAND_FREEZE "freeze"
+#define NS_COMMAND_UNFREEZE "unfreeze"
+#define NS_COMMAND_THAW "thaw"
+#define NS_COMMAND_TIMERPOKE "timerpoke"
+#define NS_COMMAND_RECURSING "recursing"
+#define NS_COMMAND_NULL "null"
+#define NS_COMMAND_NOTIFY "notify"
+#define NS_COMMAND_VALIDATION "validation"
+
+isc_result_t
+ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp);
+/*%<
+ * Create an initial, empty set of command channels for 'server'.
+ */
+
+void
+ns_controls_destroy(ns_controls_t **ctrlsp);
+/*%<
+ * Destroy a set of command channels.
+ *
+ * Requires:
+ * Shutdown of the channels has completed.
+ */
+
+isc_result_t
+ns_controls_configure(ns_controls_t *controls, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx);
+/*%<
+ * Configure zero or more command channels into 'controls'
+ * as defined in the configuration parse tree 'config'.
+ * The channels will evaluate ACLs in the context of
+ * 'aclconfctx'.
+ */
+
+void
+ns_controls_shutdown(ns_controls_t *controls);
+/*%<
+ * Initiate shutdown of all the command channels in 'controls'.
+ */
+
+isc_result_t
+ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text);
+
+#endif /* NAMED_CONTROL_H */
diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h
new file mode 100644
index 0000000..11f3989
--- /dev/null
+++ b/bin/named/include/named/globals.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: globals.h,v 1.64.18.4 2006/03/02 00:37:21 marka Exp $ */
+
+#ifndef NAMED_GLOBALS_H
+#define NAMED_GLOBALS_H 1
+
+/*! \file */
+
+#include <isc/rwlock.h>
+#include <isc/log.h>
+#include <isc/net.h>
+
+#include <isccfg/cfg.h>
+
+#include <dns/zone.h>
+
+#include <named/types.h>
+
+#undef EXTERN
+#undef INIT
+#ifdef NS_MAIN
+#define EXTERN
+#define INIT(v) = (v)
+#else
+#define EXTERN extern
+#define INIT(v)
+#endif
+
+EXTERN isc_mem_t * ns_g_mctx INIT(NULL);
+EXTERN unsigned int ns_g_cpus INIT(0);
+EXTERN isc_taskmgr_t * ns_g_taskmgr INIT(NULL);
+EXTERN dns_dispatchmgr_t * ns_g_dispatchmgr INIT(NULL);
+EXTERN isc_entropy_t * ns_g_entropy INIT(NULL);
+EXTERN isc_entropy_t * ns_g_fallbackentropy INIT(NULL);
+
+/*
+ * XXXRTH We're going to want multiple timer managers eventually. One
+ * for really short timers, another for client timers, and one
+ * for zone timers.
+ */
+EXTERN isc_timermgr_t * ns_g_timermgr INIT(NULL);
+EXTERN isc_socketmgr_t * ns_g_socketmgr INIT(NULL);
+EXTERN cfg_parser_t * ns_g_parser INIT(NULL);
+EXTERN const char * ns_g_version INIT(VERSION);
+EXTERN in_port_t ns_g_port INIT(0);
+EXTERN in_port_t lwresd_g_listenport INIT(0);
+
+EXTERN ns_server_t * ns_g_server INIT(NULL);
+
+EXTERN isc_boolean_t ns_g_lwresdonly INIT(ISC_FALSE);
+
+/*
+ * Logging.
+ */
+EXTERN isc_log_t * ns_g_lctx INIT(NULL);
+EXTERN isc_logcategory_t * ns_g_categories INIT(NULL);
+EXTERN isc_logmodule_t * ns_g_modules INIT(NULL);
+EXTERN unsigned int ns_g_debuglevel INIT(0);
+
+/*
+ * Current configuration information.
+ */
+EXTERN cfg_obj_t * ns_g_config INIT(NULL);
+EXTERN const cfg_obj_t * ns_g_defaults INIT(NULL);
+EXTERN const char * ns_g_conffile INIT(NS_SYSCONFDIR
+ "/named.conf");
+EXTERN const char * ns_g_keyfile INIT(NS_SYSCONFDIR
+ "/rndc.key");
+EXTERN const char * lwresd_g_conffile INIT(NS_SYSCONFDIR
+ "/lwresd.conf");
+EXTERN const char * lwresd_g_resolvconffile INIT("/etc"
+ "/resolv.conf");
+EXTERN isc_boolean_t ns_g_conffileset INIT(ISC_FALSE);
+EXTERN isc_boolean_t lwresd_g_useresolvconf INIT(ISC_FALSE);
+EXTERN isc_uint16_t ns_g_udpsize INIT(4096);
+
+/*
+ * Initial resource limits.
+ */
+EXTERN isc_resourcevalue_t ns_g_initstacksize INIT(0);
+EXTERN isc_resourcevalue_t ns_g_initdatasize INIT(0);
+EXTERN isc_resourcevalue_t ns_g_initcoresize INIT(0);
+EXTERN isc_resourcevalue_t ns_g_initopenfiles INIT(0);
+
+/*
+ * Misc.
+ */
+EXTERN isc_boolean_t ns_g_coreok INIT(ISC_TRUE);
+EXTERN const char * ns_g_chrootdir INIT(NULL);
+EXTERN isc_boolean_t ns_g_foreground INIT(ISC_FALSE);
+EXTERN isc_boolean_t ns_g_logstderr INIT(ISC_FALSE);
+
+EXTERN const char * ns_g_defaultpidfile INIT(NS_LOCALSTATEDIR
+ "/run/named.pid");
+EXTERN const char * lwresd_g_defaultpidfile INIT(NS_LOCALSTATEDIR
+ "/run/lwresd.pid");
+EXTERN const char * ns_g_username INIT(NULL);
+
+EXTERN int ns_g_listen INIT(3);
+
+#undef EXTERN
+#undef INIT
+
+#endif /* NAMED_GLOBALS_H */
diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h
new file mode 100644
index 0000000..42279ff
--- /dev/null
+++ b/bin/named/include/named/interfacemgr.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 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: interfacemgr.h,v 1.26.18.4 2005/04/27 05:00:35 sra Exp $ */
+
+#ifndef NAMED_INTERFACEMGR_H
+#define NAMED_INTERFACEMGR_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * The interface manager monitors the operating system's list
+ * of network interfaces, creating and destroying listeners
+ * as needed.
+ *
+ * Reliability:
+ *\li No impact expected.
+ *
+ * Resources:
+ *
+ * Security:
+ * \li The server will only be able to bind to the DNS port on
+ * newly discovered interfaces if it is running as root.
+ *
+ * Standards:
+ *\li The API for scanning varies greatly among operating systems.
+ * This module attempts to hide the differences.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/socket.h>
+
+#include <dns/result.h>
+
+#include <named/listenlist.h>
+#include <named/types.h>
+
+/***
+ *** Types
+ ***/
+
+#define IFACE_MAGIC ISC_MAGIC('I',':','-',')')
+#define NS_INTERFACE_VALID(t) ISC_MAGIC_VALID(t, IFACE_MAGIC)
+
+#define NS_INTERFACEFLAG_ANYADDR 0x01U /*%< bound to "any" address */
+
+/*% The nameserver interface structure */
+struct ns_interface {
+ unsigned int magic; /*%< Magic number. */
+ ns_interfacemgr_t * mgr; /*%< Interface manager. */
+ isc_mutex_t lock;
+ int references; /*%< Locked */
+ unsigned int generation; /*%< Generation number. */
+ isc_sockaddr_t addr; /*%< Address and port. */
+ unsigned int flags; /*%< Interface characteristics */
+ char name[32]; /*%< Null terminated. */
+ dns_dispatch_t * udpdispatch; /*%< UDP dispatcher. */
+ isc_socket_t * tcpsocket; /*%< TCP socket. */
+ int ntcptarget; /*%< Desired number of concurrent
+ TCP accepts */
+ int ntcpcurrent; /*%< Current ditto, locked */
+ ns_clientmgr_t * clientmgr; /*%< Client manager. */
+ ISC_LINK(ns_interface_t) link;
+};
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+ns_interfacemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_socketmgr_t *socketmgr,
+ dns_dispatchmgr_t *dispatchmgr,
+ ns_interfacemgr_t **mgrp);
+/*%
+ * Create a new interface manager.
+ *
+ * Initially, the new manager will not listen on any interfaces.
+ * Call ns_interfacemgr_setlistenon() and/or ns_interfacemgr_setlistenon6()
+ * to set nonempty listen-on lists.
+ */
+
+void
+ns_interfacemgr_attach(ns_interfacemgr_t *source, ns_interfacemgr_t **target);
+
+void
+ns_interfacemgr_detach(ns_interfacemgr_t **targetp);
+
+void
+ns_interfacemgr_shutdown(ns_interfacemgr_t *mgr);
+
+void
+ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose);
+/*%
+ * Scan the operatings system's list of network interfaces
+ * and create listeners when new interfaces are discovered.
+ * Shut down the sockets for interfaces that go away.
+ *
+ * This should be called once on server startup and then
+ * periodically according to the 'interface-interval' option
+ * in named.conf.
+ */
+
+void
+ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list,
+ isc_boolean_t verbose);
+/*%
+ * Similar to ns_interfacemgr_scan(), but this function also tries to see the
+ * need for an explicit listen-on when a list element in 'list' is going to
+ * override an already-listening a wildcard interface.
+ *
+ * This function does not update localhost and localnets ACLs.
+ *
+ * This should be called once on server startup, after configuring views and
+ * zones.
+ */
+
+void
+ns_interfacemgr_setlistenon4(ns_interfacemgr_t *mgr, ns_listenlist_t *value);
+/*%
+ * Set the IPv4 "listen-on" list of 'mgr' to 'value'.
+ * The previous IPv4 listen-on list is freed.
+ */
+
+void
+ns_interfacemgr_setlistenon6(ns_interfacemgr_t *mgr, ns_listenlist_t *value);
+/*%
+ * Set the IPv6 "listen-on" list of 'mgr' to 'value'.
+ * The previous IPv6 listen-on list is freed.
+ */
+
+dns_aclenv_t *
+ns_interfacemgr_getaclenv(ns_interfacemgr_t *mgr);
+
+void
+ns_interface_attach(ns_interface_t *source, ns_interface_t **target);
+
+void
+ns_interface_detach(ns_interface_t **targetp);
+
+void
+ns_interface_shutdown(ns_interface_t *ifp);
+/*%
+ * Stop listening for queries on interface 'ifp'.
+ * May safely be called multiple times.
+ */
+
+void
+ns_interfacemgr_dumprecursing(FILE *f, ns_interfacemgr_t *mgr);
+
+isc_boolean_t
+ns_interfacemgr_listeningon(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr);
+
+#endif /* NAMED_INTERFACEMGR_H */
diff --git a/bin/named/include/named/listenlist.h b/bin/named/include/named/listenlist.h
new file mode 100644
index 0000000..cdca026
--- /dev/null
+++ b/bin/named/include/named/listenlist.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: listenlist.h,v 1.11.18.2 2005/04/29 00:15:34 marka Exp $ */
+
+#ifndef NAMED_LISTENLIST_H
+#define NAMED_LISTENLIST_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * "Listen lists", as in the "listen-on" configuration statement.
+ */
+
+/***
+ *** Imports
+ ***/
+#include <isc/net.h>
+
+#include <dns/types.h>
+
+/***
+ *** Types
+ ***/
+
+typedef struct ns_listenelt ns_listenelt_t;
+typedef struct ns_listenlist ns_listenlist_t;
+
+struct ns_listenelt {
+ isc_mem_t * mctx;
+ in_port_t port;
+ dns_acl_t * acl;
+ ISC_LINK(ns_listenelt_t) link;
+};
+
+struct ns_listenlist {
+ isc_mem_t * mctx;
+ int refcount;
+ ISC_LIST(ns_listenelt_t) elts;
+};
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+ns_listenelt_create(isc_mem_t *mctx, in_port_t port,
+ dns_acl_t *acl, ns_listenelt_t **target);
+/*%
+ * Create a listen-on list element.
+ */
+
+void
+ns_listenelt_destroy(ns_listenelt_t *elt);
+/*%
+ * Destroy a listen-on list element.
+ */
+
+isc_result_t
+ns_listenlist_create(isc_mem_t *mctx, ns_listenlist_t **target);
+/*%
+ * Create a new, empty listen-on list.
+ */
+
+void
+ns_listenlist_attach(ns_listenlist_t *source, ns_listenlist_t **target);
+/*%
+ * Attach '*target' to '*source'.
+ */
+
+void
+ns_listenlist_detach(ns_listenlist_t **listp);
+/*%
+ * Detach 'listp'.
+ */
+
+isc_result_t
+ns_listenlist_default(isc_mem_t *mctx, in_port_t port,
+ isc_boolean_t enabled, ns_listenlist_t **target);
+/*%
+ * Create a listen-on list with default contents, matching
+ * all addresses with port 'port' (if 'enabled' is ISC_TRUE),
+ * or no addresses (if 'enabled' is ISC_FALSE).
+ */
+
+#endif /* NAMED_LISTENLIST_H */
+
+
diff --git a/bin/named/include/named/log.h b/bin/named/include/named/log.h
new file mode 100644
index 0000000..6d6e648
--- /dev/null
+++ b/bin/named/include/named/log.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 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: log.h,v 1.21.18.2 2005/04/29 00:15:35 marka Exp $ */
+
+#ifndef NAMED_LOG_H
+#define NAMED_LOG_H 1
+
+/*! \file */
+
+#include <isc/log.h>
+#include <isc/types.h>
+
+#include <dns/log.h>
+
+#include <named/globals.h> /* Required for ns_g_(categories|modules). */
+
+/* Unused slot 0. */
+#define NS_LOGCATEGORY_CLIENT (&ns_g_categories[1])
+#define NS_LOGCATEGORY_NETWORK (&ns_g_categories[2])
+#define NS_LOGCATEGORY_UPDATE (&ns_g_categories[3])
+#define NS_LOGCATEGORY_QUERIES (&ns_g_categories[4])
+#define NS_LOGCATEGORY_UNMATCHED (&ns_g_categories[5])
+#define NS_LOGCATEGORY_UPDATE_SECURITY (&ns_g_categories[6])
+
+/*
+ * Backwards compatibility.
+ */
+#define NS_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL
+
+#define NS_LOGMODULE_MAIN (&ns_g_modules[0])
+#define NS_LOGMODULE_CLIENT (&ns_g_modules[1])
+#define NS_LOGMODULE_SERVER (&ns_g_modules[2])
+#define NS_LOGMODULE_QUERY (&ns_g_modules[3])
+#define NS_LOGMODULE_INTERFACEMGR (&ns_g_modules[4])
+#define NS_LOGMODULE_UPDATE (&ns_g_modules[5])
+#define NS_LOGMODULE_XFER_IN (&ns_g_modules[6])
+#define NS_LOGMODULE_XFER_OUT (&ns_g_modules[7])
+#define NS_LOGMODULE_NOTIFY (&ns_g_modules[8])
+#define NS_LOGMODULE_CONTROL (&ns_g_modules[9])
+#define NS_LOGMODULE_LWRESD (&ns_g_modules[10])
+
+isc_result_t
+ns_log_init(isc_boolean_t safe);
+/*%
+ * Initialize the logging system and set up an initial default
+ * logging default configuration that will be used until the
+ * config file has been read.
+ *
+ * If 'safe' is true, use a default configuration that refrains
+ * from opening files. This is to avoid creating log files
+ * as root.
+ */
+
+isc_result_t
+ns_log_setdefaultchannels(isc_logconfig_t *lcfg);
+/*%
+ * Set up logging channels according to the named defaults, which
+ * may differ from the logging library defaults. Currently,
+ * this just means setting up default_debug.
+ */
+
+isc_result_t
+ns_log_setsafechannels(isc_logconfig_t *lcfg);
+/*%
+ * Like ns_log_setdefaultchannels(), but omits any logging to files.
+ */
+
+isc_result_t
+ns_log_setdefaultcategory(isc_logconfig_t *lcfg);
+/*%
+ * Set up "category default" to go to the right places.
+ */
+
+isc_result_t
+ns_log_setunmatchedcategory(isc_logconfig_t *lcfg);
+/*%
+ * Set up "category unmatched" to go to the right places.
+ */
+
+void
+ns_log_shutdown(void);
+
+#endif /* NAMED_LOG_H */
diff --git a/bin/named/include/named/logconf.h b/bin/named/include/named/logconf.h
new file mode 100644
index 0000000..79df5c6
--- /dev/null
+++ b/bin/named/include/named/logconf.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: logconf.h,v 1.11.18.4 2006/03/02 00:37:21 marka Exp $ */
+
+#ifndef NAMED_LOGCONF_H
+#define NAMED_LOGCONF_H 1
+
+/*! \file */
+
+#include <isc/log.h>
+
+isc_result_t
+ns_log_configure(isc_logconfig_t *logconf, const cfg_obj_t *logstmt);
+/*%<
+ * Set up the logging configuration in '*logconf' according to
+ * the named.conf data in 'logstmt'.
+ */
+
+#endif /* NAMED_LOGCONF_H */
diff --git a/bin/named/include/named/lwaddr.h b/bin/named/include/named/lwaddr.h
new file mode 100644
index 0000000..552d1d4
--- /dev/null
+++ b/bin/named/include/named/lwaddr.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwaddr.h,v 1.4.18.2 2005/04/29 00:15:35 marka Exp $ */
+
+/*! \file */
+
+#include <lwres/lwres.h>
+#include <lwres/net.h>
+
+isc_result_t
+lwaddr_netaddr_fromlwresaddr(isc_netaddr_t *na, lwres_addr_t *la);
+
+isc_result_t
+lwaddr_sockaddr_fromlwresaddr(isc_sockaddr_t *sa, lwres_addr_t *la,
+ in_port_t port);
+
+isc_result_t
+lwaddr_lwresaddr_fromnetaddr(lwres_addr_t *la, isc_netaddr_t *na);
+
+isc_result_t
+lwaddr_lwresaddr_fromsockaddr(lwres_addr_t *la, isc_sockaddr_t *sa);
diff --git a/bin/named/include/named/lwdclient.h b/bin/named/include/named/lwdclient.h
new file mode 100644
index 0000000..591b86c
--- /dev/null
+++ b/bin/named/include/named/lwdclient.h
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwdclient.h,v 1.14.18.2 2005/04/29 00:15:36 marka Exp $ */
+
+#ifndef NAMED_LWDCLIENT_H
+#define NAMED_LWDCLIENT_H 1
+
+/*! \file */
+
+#include <isc/event.h>
+#include <isc/eventclass.h>
+#include <isc/netaddr.h>
+#include <isc/sockaddr.h>
+#include <isc/types.h>
+
+#include <dns/fixedname.h>
+#include <dns/types.h>
+
+#include <lwres/lwres.h>
+
+#include <named/lwsearch.h>
+
+#define LWRD_EVENTCLASS ISC_EVENTCLASS(4242)
+
+#define LWRD_SHUTDOWN (LWRD_EVENTCLASS + 0x0001)
+
+/*% Lighweight Resolver Daemon Client */
+struct ns_lwdclient {
+ isc_sockaddr_t address; /*%< where to reply */
+ struct in6_pktinfo pktinfo;
+ isc_boolean_t pktinfo_valid;
+ ns_lwdclientmgr_t *clientmgr; /*%< our parent */
+ ISC_LINK(ns_lwdclient_t) link;
+ unsigned int state;
+ void *arg; /*%< packet processing state */
+
+ /*
+ * Received data info.
+ */
+ unsigned char buffer[LWRES_RECVLENGTH]; /*%< receive buffer */
+ isc_uint32_t recvlength; /*%< length recv'd */
+ lwres_lwpacket_t pkt;
+
+ /*%
+ * Send data state. If sendbuf != buffer (that is, the send buffer
+ * isn't our receive buffer) it will be freed to the lwres_context_t.
+ */
+ unsigned char *sendbuf;
+ isc_uint32_t sendlength;
+ isc_buffer_t recv_buffer;
+
+ /*%
+ * gabn (get address by name) state info.
+ */
+ dns_adbfind_t *find;
+ dns_adbfind_t *v4find;
+ dns_adbfind_t *v6find;
+ unsigned int find_wanted; /*%< Addresses we want */
+ dns_fixedname_t query_name;
+ dns_fixedname_t target_name;
+ ns_lwsearchctx_t searchctx;
+ lwres_gabnresponse_t gabn;
+
+ /*%
+ * gnba (get name by address) state info.
+ */
+ lwres_gnbaresponse_t gnba;
+ dns_byaddr_t *byaddr;
+ unsigned int options;
+ isc_netaddr_t na;
+
+ /*%
+ * grbn (get rrset by name) state info.
+ *
+ * Note: this also uses target_name and searchctx.
+ */
+ lwres_grbnresponse_t grbn;
+ dns_lookup_t *lookup;
+ dns_rdatatype_t rdtype;
+
+ /*%
+ * Alias and address info. This is copied up to the gabn/gnba
+ * structures eventually.
+ *
+ * XXXMLG We can keep all of this in a client since we only service
+ * three packet types right now. If we started handling more,
+ * we'd need to use "arg" above and allocate/destroy things.
+ */
+ char *aliases[LWRES_MAX_ALIASES];
+ isc_uint16_t aliaslen[LWRES_MAX_ALIASES];
+ lwres_addr_t addrs[LWRES_MAX_ADDRS];
+};
+
+/*%
+ * Client states.
+ *
+ * _IDLE The client is not doing anything at all.
+ *
+ * _RECV The client is waiting for data after issuing a socket recv().
+ *
+ * _RECVDONE Data has been received, and is being processed.
+ *
+ * _FINDWAIT An adb (or other) request was made that cannot be satisfied
+ * immediately. An event will wake the client up.
+ *
+ * _SEND All data for a response has completed, and a reply was
+ * sent via a socket send() call.
+ *
+ * Badly formatted state table:
+ *
+ * IDLE -> RECV when client has a recv() queued.
+ *
+ * RECV -> RECVDONE when recvdone event received.
+ *
+ * RECVDONE -> SEND if the data for a reply is at hand.
+ * RECVDONE -> FINDWAIT if more searching is needed, and events will
+ * eventually wake us up again.
+ *
+ * FINDWAIT -> SEND when enough data was received to reply.
+ *
+ * SEND -> IDLE when a senddone event was received.
+ *
+ * At any time -> IDLE on error. Sometimes this will be -> SEND
+ * instead, if enough data is on hand to reply with a meaningful
+ * error.
+ *
+ * Packets which are badly formatted may or may not get error returns.
+ */
+#define NS_LWDCLIENT_STATEIDLE 1
+#define NS_LWDCLIENT_STATERECV 2
+#define NS_LWDCLIENT_STATERECVDONE 3
+#define NS_LWDCLIENT_STATEFINDWAIT 4
+#define NS_LWDCLIENT_STATESEND 5
+#define NS_LWDCLIENT_STATESENDDONE 6
+
+#define NS_LWDCLIENT_ISIDLE(c) \
+ ((c)->state == NS_LWDCLIENT_STATEIDLE)
+#define NS_LWDCLIENT_ISRECV(c) \
+ ((c)->state == NS_LWDCLIENT_STATERECV)
+#define NS_LWDCLIENT_ISRECVDONE(c) \
+ ((c)->state == NS_LWDCLIENT_STATERECVDONE)
+#define NS_LWDCLIENT_ISFINDWAIT(c) \
+ ((c)->state == NS_LWDCLIENT_STATEFINDWAIT)
+#define NS_LWDCLIENT_ISSEND(c) \
+ ((c)->state == NS_LWDCLIENT_STATESEND)
+
+/*%
+ * Overall magic test that means we're not idle.
+ */
+#define NS_LWDCLIENT_ISRUNNING(c) (!NS_LWDCLIENT_ISIDLE(c))
+
+#define NS_LWDCLIENT_SETIDLE(c) \
+ ((c)->state = NS_LWDCLIENT_STATEIDLE)
+#define NS_LWDCLIENT_SETRECV(c) \
+ ((c)->state = NS_LWDCLIENT_STATERECV)
+#define NS_LWDCLIENT_SETRECVDONE(c) \
+ ((c)->state = NS_LWDCLIENT_STATERECVDONE)
+#define NS_LWDCLIENT_SETFINDWAIT(c) \
+ ((c)->state = NS_LWDCLIENT_STATEFINDWAIT)
+#define NS_LWDCLIENT_SETSEND(c) \
+ ((c)->state = NS_LWDCLIENT_STATESEND)
+#define NS_LWDCLIENT_SETSENDDONE(c) \
+ ((c)->state = NS_LWDCLIENT_STATESENDDONE)
+
+/*% lightweight daemon client manager */
+struct ns_lwdclientmgr {
+ ns_lwreslistener_t *listener;
+ isc_mem_t *mctx;
+ isc_socket_t *sock; /*%< socket to use */
+ dns_view_t *view;
+ lwres_context_t *lwctx; /*%< lightweight proto context */
+ isc_task_t *task; /*%< owning task */
+ unsigned int flags;
+ ISC_LINK(ns_lwdclientmgr_t) link;
+ ISC_LIST(ns_lwdclient_t) idle; /*%< idle client slots */
+ ISC_LIST(ns_lwdclient_t) running; /*%< running clients */
+};
+
+#define NS_LWDCLIENTMGR_FLAGRECVPENDING 0x00000001
+#define NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN 0x00000002
+
+isc_result_t
+ns_lwdclientmgr_create(ns_lwreslistener_t *, unsigned int, isc_taskmgr_t *);
+
+void
+ns_lwdclient_initialize(ns_lwdclient_t *, ns_lwdclientmgr_t *);
+
+isc_result_t
+ns_lwdclient_startrecv(ns_lwdclientmgr_t *);
+
+void
+ns_lwdclient_stateidle(ns_lwdclient_t *);
+
+void
+ns_lwdclient_recv(isc_task_t *, isc_event_t *);
+
+void
+ns_lwdclient_shutdown(isc_task_t *, isc_event_t *);
+
+void
+ns_lwdclient_send(isc_task_t *, isc_event_t *);
+
+isc_result_t
+ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r);
+
+/*
+ * Processing functions of various types.
+ */
+void ns_lwdclient_processgabn(ns_lwdclient_t *, lwres_buffer_t *);
+void ns_lwdclient_processgnba(ns_lwdclient_t *, lwres_buffer_t *);
+void ns_lwdclient_processgrbn(ns_lwdclient_t *, lwres_buffer_t *);
+void ns_lwdclient_processnoop(ns_lwdclient_t *, lwres_buffer_t *);
+
+void ns_lwdclient_errorpktsend(ns_lwdclient_t *, isc_uint32_t);
+
+void ns_lwdclient_log(int level, const char *format, ...)
+ ISC_FORMAT_PRINTF(2, 3);
+
+#endif /* NAMED_LWDCLIENT_H */
diff --git a/bin/named/include/named/lwresd.h b/bin/named/include/named/lwresd.h
new file mode 100644
index 0000000..ef93fcd
--- /dev/null
+++ b/bin/named/include/named/lwresd.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwresd.h,v 1.13.18.4 2006/03/02 00:37:21 marka Exp $ */
+
+#ifndef NAMED_LWRESD_H
+#define NAMED_LWRESD_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+#include <isc/sockaddr.h>
+
+#include <isccfg/cfg.h>
+
+#include <dns/types.h>
+
+struct ns_lwresd {
+ unsigned int magic;
+
+ isc_mutex_t lock;
+ dns_view_t *view;
+ ns_lwsearchlist_t *search;
+ unsigned int ndots;
+ isc_mem_t *mctx;
+ isc_boolean_t shutting_down;
+ unsigned int refs;
+};
+
+struct ns_lwreslistener {
+ unsigned int magic;
+
+ isc_mutex_t lock;
+ isc_mem_t *mctx;
+ isc_sockaddr_t address;
+ ns_lwresd_t *manager;
+ isc_socket_t *sock;
+ unsigned int refs;
+ ISC_LIST(ns_lwdclientmgr_t) cmgrs;
+ ISC_LINK(ns_lwreslistener_t) link;
+};
+
+/*%
+ * Configure lwresd.
+ */
+isc_result_t
+ns_lwresd_configure(isc_mem_t *mctx, const cfg_obj_t *config);
+
+isc_result_t
+ns_lwresd_parseeresolvconf(isc_mem_t *mctx, cfg_parser_t *pctx,
+ cfg_obj_t **configp);
+
+/*%
+ * Trigger shutdown.
+ */
+void
+ns_lwresd_shutdown(void);
+
+/*
+ * Manager functions
+ */
+/*% create manager */
+isc_result_t
+ns_lwdmanager_create(isc_mem_t *mctx, const cfg_obj_t *lwres,
+ ns_lwresd_t **lwresdp);
+
+/*% attach to manager */
+void
+ns_lwdmanager_attach(ns_lwresd_t *source, ns_lwresd_t **targetp);
+
+/*% detach from manager */
+void
+ns_lwdmanager_detach(ns_lwresd_t **lwresdp);
+
+/*
+ * Listener functions
+ */
+/*% attach to listener */
+void
+ns_lwreslistener_attach(ns_lwreslistener_t *source,
+ ns_lwreslistener_t **targetp);
+
+/*% detach from lister */
+void
+ns_lwreslistener_detach(ns_lwreslistener_t **listenerp);
+
+/*% link client manager */
+void
+ns_lwreslistener_unlinkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm);
+
+/*% unlink client manager */
+void
+ns_lwreslistener_linkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm);
+
+
+
+
+/*
+ * INTERNAL FUNCTIONS.
+ */
+void *
+ns__lwresd_memalloc(void *arg, size_t size);
+
+void
+ns__lwresd_memfree(void *arg, void *mem, size_t size);
+
+#endif /* NAMED_LWRESD_H */
diff --git a/bin/named/include/named/lwsearch.h b/bin/named/include/named/lwsearch.h
new file mode 100644
index 0000000..b85e401
--- /dev/null
+++ b/bin/named/include/named/lwsearch.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwsearch.h,v 1.5.18.2 2005/04/29 00:15:36 marka Exp $ */
+
+#ifndef NAMED_LWSEARCH_H
+#define NAMED_LWSEARCH_H 1
+
+#include <isc/mutex.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+#include <named/types.h>
+
+/*! \file
+ * \brief
+ * Lightweight resolver search list types and routines.
+ *
+ * An ns_lwsearchlist_t holds a list of search path elements.
+ *
+ * An ns_lwsearchctx stores the state of search list during a lookup
+ * operation.
+ */
+
+/*% An ns_lwsearchlist_t holds a list of search path elements. */
+struct ns_lwsearchlist {
+ unsigned int magic;
+
+ isc_mutex_t lock;
+ isc_mem_t *mctx;
+ unsigned int refs;
+ dns_namelist_t names;
+};
+/*% An ns_lwsearchctx stores the state of search list during a lookup operation. */
+struct ns_lwsearchctx {
+ dns_name_t *relname;
+ dns_name_t *searchname;
+ unsigned int ndots;
+ ns_lwsearchlist_t *list;
+ isc_boolean_t doneexact;
+ isc_boolean_t exactfirst;
+};
+
+isc_result_t
+ns_lwsearchlist_create(isc_mem_t *mctx, ns_lwsearchlist_t **listp);
+/*%<
+ * Create an empty search list object.
+ */
+
+void
+ns_lwsearchlist_attach(ns_lwsearchlist_t *source, ns_lwsearchlist_t **target);
+/*%<
+ * Attach to a search list object.
+ */
+
+void
+ns_lwsearchlist_detach(ns_lwsearchlist_t **listp);
+/*%<
+ * Detach from a search list object.
+ */
+
+isc_result_t
+ns_lwsearchlist_append(ns_lwsearchlist_t *list, dns_name_t *name);
+/*%<
+ * Append an element to a search list. This creates a copy of the name.
+ */
+
+void
+ns_lwsearchctx_init(ns_lwsearchctx_t *sctx, ns_lwsearchlist_t *list,
+ dns_name_t *name, unsigned int ndots);
+/*%<
+ * Creates a search list context structure.
+ */
+
+void
+ns_lwsearchctx_first(ns_lwsearchctx_t *sctx);
+/*%<
+ * Moves the search list context iterator to the first element, which
+ * is usually the exact name.
+ */
+
+isc_result_t
+ns_lwsearchctx_next(ns_lwsearchctx_t *sctx);
+/*%<
+ * Moves the search list context iterator to the next element.
+ */
+
+isc_result_t
+ns_lwsearchctx_current(ns_lwsearchctx_t *sctx, dns_name_t *absname);
+/*%<
+ * Obtains the current name to be looked up. This involves either
+ * concatenating the name with a search path element, making an
+ * exact name absolute, or doing nothing.
+ */
+
+#endif /* NAMED_LWSEARCH_H */
diff --git a/bin/named/include/named/main.h b/bin/named/include/named/main.h
new file mode 100644
index 0000000..dd4fe8c
--- /dev/null
+++ b/bin/named/include/named/main.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 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: main.h,v 1.11.18.2 2005/04/29 00:15:37 marka Exp $ */
+
+#ifndef NAMED_MAIN_H
+#define NAMED_MAIN_H 1
+
+/*! \file */
+
+void
+ns_main_earlyfatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
+
+void
+ns_main_earlywarning(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
+
+void
+ns_main_setmemstats(const char *);
+
+#endif /* NAMED_MAIN_H */
diff --git a/bin/named/include/named/notify.h b/bin/named/include/named/notify.h
new file mode 100644
index 0000000..106d70c
--- /dev/null
+++ b/bin/named/include/named/notify.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: notify.h,v 1.10.18.2 2005/04/29 00:15:37 marka Exp $ */
+
+#ifndef NAMED_NOTIFY_H
+#define NAMED_NOTIFY_H 1
+
+#include <named/types.h>
+#include <named/client.h>
+
+/***
+ *** Module Info
+ ***/
+
+/*! \file
+ * \brief
+ * RFC1996
+ * A Mechanism for Prompt Notification of Zone Changes (DNS NOTIFY)
+ */
+
+/***
+ *** Functions.
+ ***/
+
+void
+ns_notify_start(ns_client_t *client);
+
+/*%<
+ * Examines the incoming message to determine apporiate zone.
+ * Returns FORMERR if there is not exactly one question.
+ * Returns REFUSED if we do not serve the listed zone.
+ * Pass the message to the zone module for processing
+ * and returns the return status.
+ *
+ * Requires
+ *\li client to be valid.
+ */
+
+#endif /* NAMED_NOTIFY_H */
+
diff --git a/bin/named/include/named/ns_smf_globals.h b/bin/named/include/named/ns_smf_globals.h
new file mode 100644
index 0000000..06df2ba
--- /dev/null
+++ b/bin/named/include/named/ns_smf_globals.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2005 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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: ns_smf_globals.h,v 1.2.2.4 2005/05/13 01:32:46 marka Exp $ */
+
+#ifndef NS_SMF_GLOBALS_H
+#define NS_SMF_GLOBALS_H 1
+
+#include <libscf.h>
+
+#undef EXTERN
+#undef INIT
+#ifdef NS_MAIN
+#define EXTERN
+#define INIT(v) = (v)
+#else
+#define EXTERN extern
+#define INIT(v)
+#endif
+
+EXTERN unsigned int ns_smf_got_instance INIT(0);
+EXTERN unsigned int ns_smf_chroot INIT(0);
+EXTERN unsigned int ns_smf_want_disable INIT(0);
+
+isc_result_t ns_smf_add_message(isc_buffer_t *text);
+isc_result_t ns_smf_get_instance(char **name, int debug, isc_mem_t *mctx);
+
+#undef EXTERN
+#undef INIT
+
+#endif /* NS_SMF_GLOBALS_H */
diff --git a/bin/named/include/named/query.h b/bin/named/include/named/query.h
new file mode 100644
index 0000000..741212f
--- /dev/null
+++ b/bin/named/include/named/query.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 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: query.h,v 1.36.18.2 2005/04/29 00:15:37 marka Exp $ */
+
+#ifndef NAMED_QUERY_H
+#define NAMED_QUERY_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+#include <isc/buffer.h>
+#include <isc/netaddr.h>
+
+#include <dns/types.h>
+
+#include <named/types.h>
+
+/*% nameserver database version structure */
+typedef struct ns_dbversion {
+ dns_db_t *db;
+ dns_dbversion_t *version;
+ isc_boolean_t queryok;
+ ISC_LINK(struct ns_dbversion) link;
+} ns_dbversion_t;
+
+/*% nameserver query structure */
+struct ns_query {
+ unsigned int attributes;
+ unsigned int restarts;
+ isc_boolean_t timerset;
+ dns_name_t * qname;
+ dns_name_t * origqname;
+ unsigned int dboptions;
+ unsigned int fetchoptions;
+ dns_db_t * gluedb;
+ dns_db_t * authdb;
+ dns_zone_t * authzone;
+ isc_boolean_t authdbset;
+ isc_boolean_t isreferral;
+ isc_mutex_t fetchlock;
+ dns_fetch_t * fetch;
+ isc_bufferlist_t namebufs;
+ ISC_LIST(ns_dbversion_t) activeversions;
+ ISC_LIST(ns_dbversion_t) freeversions;
+};
+
+#define NS_QUERYATTR_RECURSIONOK 0x0001
+#define NS_QUERYATTR_CACHEOK 0x0002
+#define NS_QUERYATTR_PARTIALANSWER 0x0004
+#define NS_QUERYATTR_NAMEBUFUSED 0x0008
+#define NS_QUERYATTR_RECURSING 0x0010
+#define NS_QUERYATTR_CACHEGLUEOK 0x0020
+#define NS_QUERYATTR_QUERYOKVALID 0x0040
+#define NS_QUERYATTR_QUERYOK 0x0080
+#define NS_QUERYATTR_WANTRECURSION 0x0100
+#define NS_QUERYATTR_SECURE 0x0200
+#define NS_QUERYATTR_NOAUTHORITY 0x0400
+#define NS_QUERYATTR_NOADDITIONAL 0x0800
+
+isc_result_t
+ns_query_init(ns_client_t *client);
+
+void
+ns_query_free(ns_client_t *client);
+
+void
+ns_query_start(ns_client_t *client);
+
+void
+ns_query_cancel(ns_client_t *client);
+
+#endif /* NAMED_QUERY_H */
diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h
new file mode 100644
index 0000000..54d1dae
--- /dev/null
+++ b/bin/named/include/named/server.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: server.h,v 1.73.18.8 2006/03/09 23:46:20 marka Exp $ */
+
+#ifndef NAMED_SERVER_H
+#define NAMED_SERVER_H 1
+
+/*! \file */
+
+#include <isc/log.h>
+#include <isc/sockaddr.h>
+#include <isc/magic.h>
+#include <isc/types.h>
+#include <isc/quota.h>
+
+#include <dns/types.h>
+#include <dns/acl.h>
+
+#include <named/types.h>
+
+#define NS_EVENTCLASS ISC_EVENTCLASS(0x4E43)
+#define NS_EVENT_RELOAD (NS_EVENTCLASS + 0)
+#define NS_EVENT_CLIENTCONTROL (NS_EVENTCLASS + 1)
+
+/*%
+ * Name server state. Better here than in lots of separate global variables.
+ */
+struct ns_server {
+ unsigned int magic;
+ isc_mem_t * mctx;
+
+ isc_task_t * task;
+
+ /* Configurable data. */
+ isc_quota_t xfroutquota;
+ isc_quota_t tcpquota;
+ isc_quota_t recursionquota;
+ dns_acl_t *blackholeacl;
+ char * statsfile; /*%< Statistics file name */
+ char * dumpfile; /*%< Dump file name */
+ char * recfile; /*%< Recursive file name */
+ isc_boolean_t version_set; /*%< User has set version */
+ char * version; /*%< User-specified version */
+ isc_boolean_t hostname_set; /*%< User has set hostname */
+ char * hostname; /*%< User-specified hostname */
+ /*% Use hostname for server id */
+ isc_boolean_t server_usehostname;
+ char * server_id; /*%< User-specified server id */
+
+ /*%
+ * Current ACL environment. This defines the
+ * current values of the localhost and localnets
+ * ACLs.
+ */
+ dns_aclenv_t aclenv;
+
+ /* Server data structures. */
+ dns_loadmgr_t * loadmgr;
+ dns_zonemgr_t * zonemgr;
+ dns_viewlist_t viewlist;
+ ns_interfacemgr_t * interfacemgr;
+ dns_db_t * in_roothints;
+ dns_tkeyctx_t * tkeyctx;
+
+ isc_timer_t * interface_timer;
+ isc_timer_t * heartbeat_timer;
+ isc_timer_t * pps_timer;
+
+ isc_uint32_t interface_interval;
+ isc_uint32_t heartbeat_interval;
+
+ isc_mutex_t reload_event_lock;
+ isc_event_t * reload_event;
+
+ isc_boolean_t flushonshutdown;
+ isc_boolean_t log_queries; /*%< For BIND 8 compatibility */
+
+ isc_uint64_t * querystats; /*%< Query statistics counters */
+
+ ns_controls_t * controls; /*%< Control channels */
+ unsigned int dispatchgen;
+ ns_dispatchlist_t dispatches;
+
+ dns_acache_t *acache;
+};
+
+#define NS_SERVER_MAGIC ISC_MAGIC('S','V','E','R')
+#define NS_SERVER_VALID(s) ISC_MAGIC_VALID(s, NS_SERVER_MAGIC)
+
+void
+ns_server_create(isc_mem_t *mctx, ns_server_t **serverp);
+/*%<
+ * Create a server object with default settings.
+ * This function either succeeds or causes the program to exit
+ * with a fatal error.
+ */
+
+void
+ns_server_destroy(ns_server_t **serverp);
+/*%<
+ * Destroy a server object, freeing its memory.
+ */
+
+void
+ns_server_reloadwanted(ns_server_t *server);
+/*%<
+ * Inform a server that a reload is wanted. This function
+ * may be called asynchronously, from outside the server's task.
+ * If a reload is already scheduled or in progress, the call
+ * is ignored.
+ */
+
+void
+ns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush);
+/*%<
+ * Inform the server that the zones should be flushed to disk on shutdown.
+ */
+
+isc_result_t
+ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text);
+/*%<
+ * Act on a "reload" command from the command channel.
+ */
+
+isc_result_t
+ns_server_reconfigcommand(ns_server_t *server, char *args);
+/*%<
+ * Act on a "reconfig" command from the command channel.
+ */
+
+isc_result_t
+ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text);
+/*%<
+ * Act on a "notify" command from the command channel.
+ */
+
+isc_result_t
+ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text);
+/*%<
+ * Act on a "refresh" command from the command channel.
+ */
+
+isc_result_t
+ns_server_retransfercommand(ns_server_t *server, char *args);
+/*%<
+ * Act on a "retransfer" command from the command channel.
+ */
+
+isc_result_t
+ns_server_togglequerylog(ns_server_t *server);
+/*%<
+ * Toggle logging of queries, as in BIND 8.
+ */
+
+/*%
+ * Dump the current statistics to the statistics file.
+ */
+isc_result_t
+ns_server_dumpstats(ns_server_t *server);
+
+/*%
+ * Dump the current cache to the dump file.
+ */
+isc_result_t
+ns_server_dumpdb(ns_server_t *server, char *args);
+
+/*%
+ * Change or increment the server debug level.
+ */
+isc_result_t
+ns_server_setdebuglevel(ns_server_t *server, char *args);
+
+/*%
+ * Flush the server's cache(s)
+ */
+isc_result_t
+ns_server_flushcache(ns_server_t *server, char *args);
+
+/*%
+ * Flush a particular name from the server's cache(s)
+ */
+isc_result_t
+ns_server_flushname(ns_server_t *server, char *args);
+
+/*%
+ * Report the server's status.
+ */
+isc_result_t
+ns_server_status(ns_server_t *server, isc_buffer_t *text);
+
+/*%
+ * Enable or disable updates for a zone.
+ */
+isc_result_t
+ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args);
+
+/*%
+ * Dump the current recursive queries.
+ */
+isc_result_t
+ns_server_dumprecursing(ns_server_t *server);
+
+/*%
+ * Maintain a list of dispatches that require reserved ports.
+ */
+void
+ns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr);
+
+/*%
+ * Enable or disable dnssec validation.
+ */
+isc_result_t
+ns_server_validation(ns_server_t *server, char *args);
+
+#endif /* NAMED_SERVER_H */
diff --git a/bin/named/include/named/sortlist.h b/bin/named/include/named/sortlist.h
new file mode 100644
index 0000000..f849be2
--- /dev/null
+++ b/bin/named/include/named/sortlist.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: sortlist.h,v 1.5.18.4 2006/03/02 00:37:21 marka Exp $ */
+
+#ifndef NAMED_SORTLIST_H
+#define NAMED_SORTLIST_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+/*%
+ * Type for callback functions that rank addresses.
+ */
+typedef int
+(*dns_addressorderfunc_t)(const isc_netaddr_t *address, const void *arg);
+
+/*%
+ * Return value type for setup_sortlist.
+ */
+typedef enum {
+ NS_SORTLISTTYPE_NONE,
+ NS_SORTLISTTYPE_1ELEMENT,
+ NS_SORTLISTTYPE_2ELEMENT
+} ns_sortlisttype_t;
+
+ns_sortlisttype_t
+ns_sortlist_setup(dns_acl_t *acl, isc_netaddr_t *clientaddr,
+ const void **argp);
+/*%<
+ * Find the sortlist statement in 'acl' that applies to 'clientaddr', if any.
+ *
+ * If a 1-element sortlist item applies, return NS_SORTLISTTYPE_1ELEMENT and
+ * make '*argp' point to the matching subelement.
+ *
+ * If a 2-element sortlist item applies, return NS_SORTLISTTYPE_2ELEMENT and
+ * make '*argp' point to ACL that forms the second element.
+ *
+ * If no sortlist item applies, return NS_SORTLISTTYPE_NONE and set '*argp'
+ * to NULL.
+ */
+
+int
+ns_sortlist_addrorder1(const isc_netaddr_t *addr, const void *arg);
+/*%<
+ * Find the sort order of 'addr' in 'arg', the matching element
+ * of a 1-element top-level sortlist statement.
+ */
+
+int
+ns_sortlist_addrorder2(const isc_netaddr_t *addr, const void *arg);
+/*%<
+ * Find the sort order of 'addr' in 'arg', a topology-like
+ * ACL forming the second element in a 2-element top-level
+ * sortlist statement.
+ */
+
+void
+ns_sortlist_byaddrsetup(dns_acl_t *sortlist_acl, isc_netaddr_t *client_addr,
+ dns_addressorderfunc_t *orderp,
+ const void **argp);
+/*%<
+ * Find the sortlist statement in 'acl' that applies to 'clientaddr', if any.
+ * If a sortlist statement applies, return in '*orderp' a pointer to a function
+ * for ranking network addresses based on that sortlist statement, and in
+ * '*argp' an argument to pass to said function. If no sortlist statement
+ * applies, set '*orderp' and '*argp' to NULL.
+ */
+
+#endif /* NAMED_SORTLIST_H */
diff --git a/bin/named/include/named/tkeyconf.h b/bin/named/include/named/tkeyconf.h
new file mode 100644
index 0000000..946944d
--- /dev/null
+++ b/bin/named/include/named/tkeyconf.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: tkeyconf.h,v 1.10.18.4 2006/03/02 00:37:21 marka Exp $ */
+
+#ifndef NS_TKEYCONF_H
+#define NS_TKEYCONF_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+#include <isc/lang.h>
+
+#include <isccfg/cfg.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+ns_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx,
+ isc_entropy_t *ectx, dns_tkeyctx_t **tctxp);
+/*%<
+ * Create a TKEY context and configure it, including the default DH key
+ * and default domain, according to 'options'.
+ *
+ * Requires:
+ *\li 'cfg' is a valid configuration options object.
+ *\li 'mctx' is not NULL
+ *\li 'ectx' is not NULL
+ *\li 'tctx' is not NULL
+ *\li '*tctx' is NULL
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMEMORY
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* NS_TKEYCONF_H */
diff --git a/bin/named/include/named/tsigconf.h b/bin/named/include/named/tsigconf.h
new file mode 100644
index 0000000..a18eede
--- /dev/null
+++ b/bin/named/include/named/tsigconf.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: tsigconf.h,v 1.10.18.4 2006/03/02 00:37:21 marka Exp $ */
+
+#ifndef NS_TSIGCONF_H
+#define NS_TSIGCONF_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+ns_tsigkeyring_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_tsig_keyring_t **ringp);
+/*%<
+ * Create a TSIG key ring and configure it according to the 'key'
+ * statements in the global and view configuration objects.
+ *
+ * Requires:
+ * \li 'config' is not NULL.
+ * \li 'mctx' is not NULL
+ * \li 'ring' is not NULL, and '*ring' is NULL
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOMEMORY
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* NS_TSIGCONF_H */
diff --git a/bin/named/include/named/types.h b/bin/named/include/named/types.h
new file mode 100644
index 0000000..abc25d5
--- /dev/null
+++ b/bin/named/include/named/types.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: types.h,v 1.21.18.2 2005/04/29 00:15:38 marka Exp $ */
+
+#ifndef NAMED_TYPES_H
+#define NAMED_TYPES_H 1
+
+/*! \file */
+
+#include <dns/types.h>
+
+typedef struct ns_client ns_client_t;
+typedef struct ns_clientmgr ns_clientmgr_t;
+typedef struct ns_query ns_query_t;
+typedef struct ns_server ns_server_t;
+typedef struct ns_interface ns_interface_t;
+typedef struct ns_interfacemgr ns_interfacemgr_t;
+typedef struct ns_lwresd ns_lwresd_t;
+typedef struct ns_lwreslistener ns_lwreslistener_t;
+typedef struct ns_lwdclient ns_lwdclient_t;
+typedef struct ns_lwdclientmgr ns_lwdclientmgr_t;
+typedef struct ns_lwsearchlist ns_lwsearchlist_t;
+typedef struct ns_lwsearchctx ns_lwsearchctx_t;
+typedef struct ns_controls ns_controls_t;
+typedef struct ns_dispatch ns_dispatch_t;
+typedef ISC_LIST(ns_dispatch_t) ns_dispatchlist_t;
+
+#endif /* NAMED_TYPES_H */
diff --git a/bin/named/include/named/update.h b/bin/named/include/named/update.h
new file mode 100644
index 0000000..37daa95
--- /dev/null
+++ b/bin/named/include/named/update.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: update.h,v 1.9.18.2 2005/04/29 00:15:39 marka Exp $ */
+
+#ifndef NAMED_UPDATE_H
+#define NAMED_UPDATE_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * RFC2136 Dynamic Update
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <dns/types.h>
+#include <dns/result.h>
+
+/***
+ *** Types.
+ ***/
+
+/***
+ *** Functions
+ ***/
+
+void
+ns_update_start(ns_client_t *client, isc_result_t sigresult);
+
+#endif /* NAMED_UPDATE_H */
diff --git a/bin/named/include/named/xfrout.h b/bin/named/include/named/xfrout.h
new file mode 100644
index 0000000..82e0e66
--- /dev/null
+++ b/bin/named/include/named/xfrout.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: xfrout.h,v 1.8.18.2 2005/04/29 00:15:39 marka Exp $ */
+
+#ifndef NAMED_XFROUT_H
+#define NAMED_XFROUT_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * Outgoing zone transfers (AXFR + IXFR).
+ */
+
+/***
+ *** Functions
+ ***/
+
+void
+ns_xfr_start(ns_client_t *client, dns_rdatatype_t xfrtype);
+
+#endif /* NAMED_XFROUT_H */
diff --git a/bin/named/include/named/zoneconf.h b/bin/named/include/named/zoneconf.h
new file mode 100644
index 0000000..61737a2
--- /dev/null
+++ b/bin/named/include/named/zoneconf.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 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: zoneconf.h,v 1.19.18.5 2006/03/02 00:37:21 marka Exp $ */
+
+#ifndef NS_ZONECONF_H
+#define NS_ZONECONF_H 1
+
+/*! \file */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <isccfg/aclconf.h>
+#include <isccfg/cfg.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac,
+ dns_zone_t *zone);
+/*%<
+ * Configure or reconfigure a zone according to the named.conf
+ * data in 'cctx' and 'czone'.
+ *
+ * The zone origin is not configured, it is assumed to have been set
+ * at zone creation time.
+ *
+ * Require:
+ * \li 'lctx' to be initialized or NULL.
+ * \li 'cctx' to be initialized or NULL.
+ * \li 'ac' to point to an initialized ns_aclconfctx_t.
+ * \li 'czone' to be initialized.
+ * \li 'zone' to be initialized.
+ */
+
+isc_boolean_t
+ns_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig);
+/*%<
+ * If 'zone' can be safely reconfigured according to the configuration
+ * data in 'zconfig', return ISC_TRUE. If the configuration data is so
+ * different from the current zone state that the zone needs to be destroyed
+ * and recreated, return ISC_FALSE.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* NS_ZONECONF_H */
diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c
new file mode 100644
index 0000000..db41031
--- /dev/null
+++ b/bin/named/interfacemgr.c
@@ -0,0 +1,978 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 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: interfacemgr.c,v 1.76.18.8 2006/07/20 01:10:30 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/interfaceiter.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/dispatch.h>
+
+#include <named/client.h>
+#include <named/log.h>
+#include <named/interfacemgr.h>
+
+#define IFMGR_MAGIC ISC_MAGIC('I', 'F', 'M', 'G')
+#define NS_INTERFACEMGR_VALID(t) ISC_MAGIC_VALID(t, IFMGR_MAGIC)
+
+#define IFMGR_COMMON_LOGARGS \
+ ns_g_lctx, NS_LOGCATEGORY_NETWORK, NS_LOGMODULE_INTERFACEMGR
+
+/*% nameserver interface manager structure */
+struct ns_interfacemgr {
+ unsigned int magic; /*%< Magic number. */
+ int references;
+ isc_mutex_t lock;
+ isc_mem_t * mctx; /*%< Memory context. */
+ isc_taskmgr_t * taskmgr; /*%< Task manager. */
+ isc_socketmgr_t * socketmgr; /*%< Socket manager. */
+ dns_dispatchmgr_t * dispatchmgr;
+ unsigned int generation; /*%< Current generation no. */
+ ns_listenlist_t * listenon4;
+ ns_listenlist_t * listenon6;
+ dns_aclenv_t aclenv; /*%< Localhost/localnets ACLs */
+ ISC_LIST(ns_interface_t) interfaces; /*%< List of interfaces. */
+ ISC_LIST(isc_sockaddr_t) listenon;
+};
+
+static void
+purge_old_interfaces(ns_interfacemgr_t *mgr);
+
+static void
+clearlistenon(ns_interfacemgr_t *mgr);
+
+isc_result_t
+ns_interfacemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_socketmgr_t *socketmgr,
+ dns_dispatchmgr_t *dispatchmgr,
+ ns_interfacemgr_t **mgrp)
+{
+ isc_result_t result;
+ ns_interfacemgr_t *mgr;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(mgrp != NULL);
+ REQUIRE(*mgrp == NULL);
+
+ mgr = isc_mem_get(mctx, sizeof(*mgr));
+ if (mgr == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&mgr->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mem;
+
+ mgr->mctx = mctx;
+ mgr->taskmgr = taskmgr;
+ mgr->socketmgr = socketmgr;
+ mgr->dispatchmgr = dispatchmgr;
+ mgr->generation = 1;
+ mgr->listenon4 = NULL;
+ mgr->listenon6 = NULL;
+
+ ISC_LIST_INIT(mgr->interfaces);
+ ISC_LIST_INIT(mgr->listenon);
+
+ /*
+ * The listen-on lists are initially empty.
+ */
+ result = ns_listenlist_create(mctx, &mgr->listenon4);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mem;
+ ns_listenlist_attach(mgr->listenon4, &mgr->listenon6);
+
+ result = dns_aclenv_init(mctx, &mgr->aclenv);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_listenon;
+
+ mgr->references = 1;
+ mgr->magic = IFMGR_MAGIC;
+ *mgrp = mgr;
+ return (ISC_R_SUCCESS);
+
+ cleanup_listenon:
+ ns_listenlist_detach(&mgr->listenon4);
+ ns_listenlist_detach(&mgr->listenon6);
+ cleanup_mem:
+ isc_mem_put(mctx, mgr, sizeof(*mgr));
+ return (result);
+}
+
+static void
+ns_interfacemgr_destroy(ns_interfacemgr_t *mgr) {
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+ dns_aclenv_destroy(&mgr->aclenv);
+ ns_listenlist_detach(&mgr->listenon4);
+ ns_listenlist_detach(&mgr->listenon6);
+ clearlistenon(mgr);
+ DESTROYLOCK(&mgr->lock);
+ mgr->magic = 0;
+ isc_mem_put(mgr->mctx, mgr, sizeof(*mgr));
+}
+
+dns_aclenv_t *
+ns_interfacemgr_getaclenv(ns_interfacemgr_t *mgr) {
+ return (&mgr->aclenv);
+}
+
+void
+ns_interfacemgr_attach(ns_interfacemgr_t *source, ns_interfacemgr_t **target) {
+ REQUIRE(NS_INTERFACEMGR_VALID(source));
+ LOCK(&source->lock);
+ INSIST(source->references > 0);
+ source->references++;
+ UNLOCK(&source->lock);
+ *target = source;
+}
+
+void
+ns_interfacemgr_detach(ns_interfacemgr_t **targetp) {
+ isc_result_t need_destroy = ISC_FALSE;
+ ns_interfacemgr_t *target = *targetp;
+ REQUIRE(target != NULL);
+ REQUIRE(NS_INTERFACEMGR_VALID(target));
+ LOCK(&target->lock);
+ REQUIRE(target->references > 0);
+ target->references--;
+ if (target->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&target->lock);
+ if (need_destroy)
+ ns_interfacemgr_destroy(target);
+ *targetp = NULL;
+}
+
+void
+ns_interfacemgr_shutdown(ns_interfacemgr_t *mgr) {
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+
+ /*%
+ * Shut down and detach all interfaces.
+ * By incrementing the generation count, we make purge_old_interfaces()
+ * consider all interfaces "old".
+ */
+ mgr->generation++;
+ purge_old_interfaces(mgr);
+}
+
+
+static isc_result_t
+ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
+ const char *name, ns_interface_t **ifpret)
+{
+ ns_interface_t *ifp;
+ isc_result_t result;
+
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+ ifp = isc_mem_get(mgr->mctx, sizeof(*ifp));
+ if (ifp == NULL)
+ return (ISC_R_NOMEMORY);
+ ifp->mgr = NULL;
+ ifp->generation = mgr->generation;
+ ifp->addr = *addr;
+ ifp->flags = 0;
+ strncpy(ifp->name, name, sizeof(ifp->name));
+ ifp->name[sizeof(ifp->name)-1] = '\0';
+ ifp->clientmgr = NULL;
+
+ result = isc_mutex_init(&ifp->lock);
+ if (result != ISC_R_SUCCESS)
+ goto lock_create_failure;
+
+ result = ns_clientmgr_create(mgr->mctx, mgr->taskmgr,
+ ns_g_timermgr,
+ &ifp->clientmgr);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "ns_clientmgr_create() failed: %s",
+ isc_result_totext(result));
+ goto clientmgr_create_failure;
+ }
+
+ ifp->udpdispatch = NULL;
+
+ ifp->tcpsocket = NULL;
+ /*
+ * Create a single TCP client object. It will replace itself
+ * with a new one as soon as it gets a connection, so the actual
+ * connections will be handled in parallel even though there is
+ * only one client initially.
+ */
+ ifp->ntcptarget = 1;
+ ifp->ntcpcurrent = 0;
+
+ ISC_LINK_INIT(ifp, link);
+
+ ns_interfacemgr_attach(mgr, &ifp->mgr);
+ ISC_LIST_APPEND(mgr->interfaces, ifp, link);
+
+ ifp->references = 1;
+ ifp->magic = IFACE_MAGIC;
+ *ifpret = ifp;
+
+ return (ISC_R_SUCCESS);
+
+ clientmgr_create_failure:
+ DESTROYLOCK(&ifp->lock);
+ lock_create_failure:
+ ifp->magic = 0;
+ isc_mem_put(mgr->mctx, ifp, sizeof(*ifp));
+
+ return (ISC_R_UNEXPECTED);
+}
+
+static isc_result_t
+ns_interface_listenudp(ns_interface_t *ifp) {
+ isc_result_t result;
+ unsigned int attrs;
+ unsigned int attrmask;
+
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_UDP;
+ if (isc_sockaddr_pf(&ifp->addr) == AF_INET)
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ else
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ attrs |= DNS_DISPATCHATTR_NOLISTEN;
+ attrmask = 0;
+ attrmask |= DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP;
+ attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
+ result = dns_dispatch_getudp(ifp->mgr->dispatchmgr, ns_g_socketmgr,
+ ns_g_taskmgr, &ifp->addr,
+ 4096, 1000, 32768, 8219, 8237,
+ attrs, attrmask, &ifp->udpdispatch);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "could not listen on UDP socket: %s",
+ isc_result_totext(result));
+ goto udp_dispatch_failure;
+ }
+
+ result = ns_clientmgr_createclients(ifp->clientmgr, ns_g_cpus,
+ ifp, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "UDP ns_clientmgr_createclients(): %s",
+ isc_result_totext(result));
+ goto addtodispatch_failure;
+ }
+ return (ISC_R_SUCCESS);
+
+ addtodispatch_failure:
+ dns_dispatch_changeattributes(ifp->udpdispatch, 0,
+ DNS_DISPATCHATTR_NOLISTEN);
+ dns_dispatch_detach(&ifp->udpdispatch);
+ udp_dispatch_failure:
+ return (result);
+}
+
+static isc_result_t
+ns_interface_accepttcp(ns_interface_t *ifp) {
+ isc_result_t result;
+
+ /*
+ * Open a TCP socket.
+ */
+ result = isc_socket_create(ifp->mgr->socketmgr,
+ isc_sockaddr_pf(&ifp->addr),
+ isc_sockettype_tcp,
+ &ifp->tcpsocket);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "creating TCP socket: %s",
+ isc_result_totext(result));
+ goto tcp_socket_failure;
+ }
+#ifndef ISC_ALLOW_MAPPED
+ isc_socket_ipv6only(ifp->tcpsocket, ISC_TRUE);
+#endif
+ result = isc_socket_bind(ifp->tcpsocket, &ifp->addr);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "binding TCP socket: %s",
+ isc_result_totext(result));
+ goto tcp_bind_failure;
+ }
+ result = isc_socket_listen(ifp->tcpsocket, ns_g_listen);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "listening on TCP socket: %s",
+ isc_result_totext(result));
+ goto tcp_listen_failure;
+ }
+
+ /*
+ * If/when there a multiple filters listen to the
+ * result.
+ */
+ (void)isc_socket_filter(ifp->tcpsocket, "dataready");
+
+ result = ns_clientmgr_createclients(ifp->clientmgr,
+ ifp->ntcptarget, ifp,
+ ISC_TRUE);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "TCP ns_clientmgr_createclients(): %s",
+ isc_result_totext(result));
+ goto accepttcp_failure;
+ }
+ return (ISC_R_SUCCESS);
+
+ accepttcp_failure:
+ tcp_listen_failure:
+ tcp_bind_failure:
+ isc_socket_detach(&ifp->tcpsocket);
+ tcp_socket_failure:
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
+ const char *name, ns_interface_t **ifpret,
+ isc_boolean_t accept_tcp)
+{
+ isc_result_t result;
+ ns_interface_t *ifp = NULL;
+ REQUIRE(ifpret != NULL && *ifpret == NULL);
+
+ result = ns_interface_create(mgr, addr, name, &ifp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = ns_interface_listenudp(ifp);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_interface;
+
+ if (accept_tcp == ISC_TRUE) {
+ result = ns_interface_accepttcp(ifp);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * XXXRTH We don't currently have a way to easily stop
+ * dispatch service, so we currently return
+ * ISC_R_SUCCESS (the UDP stuff will work even if TCP
+ * creation failed). This will be fixed later.
+ */
+ result = ISC_R_SUCCESS;
+ }
+ }
+ *ifpret = ifp;
+ return (ISC_R_SUCCESS);
+
+ cleanup_interface:
+ ISC_LIST_UNLINK(ifp->mgr->interfaces, ifp, link);
+ ns_interface_detach(&ifp);
+ return (result);
+}
+
+void
+ns_interface_shutdown(ns_interface_t *ifp) {
+ if (ifp->clientmgr != NULL)
+ ns_clientmgr_destroy(&ifp->clientmgr);
+}
+
+static void
+ns_interface_destroy(ns_interface_t *ifp) {
+ isc_mem_t *mctx = ifp->mgr->mctx;
+ REQUIRE(NS_INTERFACE_VALID(ifp));
+
+ ns_interface_shutdown(ifp);
+
+ if (ifp->udpdispatch != NULL) {
+ dns_dispatch_changeattributes(ifp->udpdispatch, 0,
+ DNS_DISPATCHATTR_NOLISTEN);
+ dns_dispatch_detach(&ifp->udpdispatch);
+ }
+ if (ifp->tcpsocket != NULL)
+ isc_socket_detach(&ifp->tcpsocket);
+
+ DESTROYLOCK(&ifp->lock);
+
+ ns_interfacemgr_detach(&ifp->mgr);
+
+ ifp->magic = 0;
+ isc_mem_put(mctx, ifp, sizeof(*ifp));
+}
+
+void
+ns_interface_attach(ns_interface_t *source, ns_interface_t **target) {
+ REQUIRE(NS_INTERFACE_VALID(source));
+ LOCK(&source->lock);
+ INSIST(source->references > 0);
+ source->references++;
+ UNLOCK(&source->lock);
+ *target = source;
+}
+
+void
+ns_interface_detach(ns_interface_t **targetp) {
+ isc_result_t need_destroy = ISC_FALSE;
+ ns_interface_t *target = *targetp;
+ REQUIRE(target != NULL);
+ REQUIRE(NS_INTERFACE_VALID(target));
+ LOCK(&target->lock);
+ REQUIRE(target->references > 0);
+ target->references--;
+ if (target->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&target->lock);
+ if (need_destroy)
+ ns_interface_destroy(target);
+ *targetp = NULL;
+}
+
+/*%
+ * Search the interface list for an interface whose address and port
+ * both match those of 'addr'. Return a pointer to it, or NULL if not found.
+ */
+static ns_interface_t *
+find_matching_interface(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr) {
+ ns_interface_t *ifp;
+ for (ifp = ISC_LIST_HEAD(mgr->interfaces); ifp != NULL;
+ ifp = ISC_LIST_NEXT(ifp, link)) {
+ if (isc_sockaddr_equal(&ifp->addr, addr))
+ break;
+ }
+ return (ifp);
+}
+
+/*%
+ * Remove any interfaces whose generation number is not the current one.
+ */
+static void
+purge_old_interfaces(ns_interfacemgr_t *mgr) {
+ ns_interface_t *ifp, *next;
+ for (ifp = ISC_LIST_HEAD(mgr->interfaces); ifp != NULL; ifp = next) {
+ INSIST(NS_INTERFACE_VALID(ifp));
+ next = ISC_LIST_NEXT(ifp, link);
+ if (ifp->generation != mgr->generation) {
+ char sabuf[256];
+ ISC_LIST_UNLINK(ifp->mgr->interfaces, ifp, link);
+ isc_sockaddr_format(&ifp->addr, sabuf, sizeof(sabuf));
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_INFO,
+ "no longer listening on %s", sabuf);
+ ns_interface_shutdown(ifp);
+ ns_interface_detach(&ifp);
+ }
+ }
+}
+
+static isc_result_t
+clearacl(isc_mem_t *mctx, dns_acl_t **aclp) {
+ dns_acl_t *newacl = NULL;
+ isc_result_t result;
+ result = dns_acl_create(mctx, 10, &newacl);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_acl_detach(aclp);
+ dns_acl_attach(newacl, aclp);
+ dns_acl_detach(&newacl);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+listenon_is_ip6_any(ns_listenelt_t *elt) {
+ if (elt->acl->length != 1)
+ return (ISC_FALSE);
+ if (elt->acl->elements[0].negative == ISC_FALSE &&
+ elt->acl->elements[0].type == dns_aclelementtype_any)
+ return (ISC_TRUE); /* listen-on-v6 { any; } */
+ return (ISC_FALSE); /* All others */
+}
+
+static isc_result_t
+setup_locals(ns_interfacemgr_t *mgr, isc_interface_t *interface) {
+ isc_result_t result;
+ dns_aclelement_t elt;
+ unsigned int family;
+ unsigned int prefixlen;
+
+ family = interface->address.family;
+
+ elt.type = dns_aclelementtype_ipprefix;
+ elt.negative = ISC_FALSE;
+ elt.u.ip_prefix.address = interface->address;
+ elt.u.ip_prefix.prefixlen = (family == AF_INET) ? 32 : 128;
+ result = dns_acl_appendelement(mgr->aclenv.localhost, &elt);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = isc_netaddr_masktoprefixlen(&interface->netmask,
+ &prefixlen);
+
+ /* Non contigious netmasks not allowed by IPv6 arch. */
+ if (result != ISC_R_SUCCESS && family == AF_INET6)
+ return (result);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_WARNING,
+ "omitting IPv4 interface %s from "
+ "localnets ACL: %s",
+ interface->name,
+ isc_result_totext(result));
+ } else {
+ elt.u.ip_prefix.prefixlen = prefixlen;
+ if (dns_acl_elementmatch(mgr->aclenv.localnets, &elt,
+ NULL) == ISC_R_NOTFOUND) {
+ result = dns_acl_appendelement(mgr->aclenv.localnets,
+ &elt);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+setup_listenon(ns_interfacemgr_t *mgr, isc_interface_t *interface,
+ in_port_t port)
+{
+ isc_sockaddr_t *addr;
+ isc_sockaddr_t *old;
+
+ addr = isc_mem_get(mgr->mctx, sizeof(*addr));
+ if (addr == NULL)
+ return;
+
+ isc_sockaddr_fromnetaddr(addr, &interface->address, port);
+
+ for (old = ISC_LIST_HEAD(mgr->listenon);
+ old != NULL;
+ old = ISC_LIST_NEXT(old, link))
+ if (isc_sockaddr_equal(addr, old))
+ break;
+
+ if (old != NULL)
+ isc_mem_put(mgr->mctx, addr, sizeof(*addr));
+ else
+ ISC_LIST_APPEND(mgr->listenon, addr, link);
+}
+
+static void
+clearlistenon(ns_interfacemgr_t *mgr) {
+ isc_sockaddr_t *old;
+
+ old = ISC_LIST_HEAD(mgr->listenon);
+ while (old != NULL) {
+ ISC_LIST_UNLINK(mgr->listenon, old, link);
+ isc_mem_put(mgr->mctx, old, sizeof(*old));
+ old = ISC_LIST_HEAD(mgr->listenon);
+ }
+}
+
+static isc_result_t
+do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
+ isc_boolean_t verbose)
+{
+ isc_interfaceiter_t *iter = NULL;
+ isc_boolean_t scan_ipv4 = ISC_FALSE;
+ isc_boolean_t scan_ipv6 = ISC_FALSE;
+ isc_boolean_t adjusting = ISC_FALSE;
+ isc_boolean_t ipv6only = ISC_TRUE;
+ isc_boolean_t ipv6pktinfo = ISC_TRUE;
+ isc_result_t result;
+ isc_netaddr_t zero_address, zero_address6;
+ ns_listenelt_t *le;
+ isc_sockaddr_t listen_addr;
+ ns_interface_t *ifp;
+ isc_boolean_t log_explicit = ISC_FALSE;
+ isc_boolean_t dolistenon;
+
+ if (ext_listen != NULL)
+ adjusting = ISC_TRUE;
+
+ if (isc_net_probeipv6() == ISC_R_SUCCESS)
+ scan_ipv6 = ISC_TRUE;
+#ifdef WANT_IPV6
+ else
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ verbose ? ISC_LOG_INFO : ISC_LOG_DEBUG(1),
+ "no IPv6 interfaces found");
+#endif
+
+ if (isc_net_probeipv4() == ISC_R_SUCCESS)
+ scan_ipv4 = ISC_TRUE;
+ else
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ verbose ? ISC_LOG_INFO : ISC_LOG_DEBUG(1),
+ "no IPv4 interfaces found");
+
+ /*
+ * A special, but typical case; listen-on-v6 { any; }.
+ * When we can make the socket IPv6-only, open a single wildcard
+ * socket for IPv6 communication. Otherwise, make separate socket
+ * for each IPv6 address in order to avoid accepting IPv4 packets
+ * as the form of mapped addresses unintentionally unless explicitly
+ * allowed.
+ */
+#ifndef ISC_ALLOW_MAPPED
+ if (scan_ipv6 == ISC_TRUE &&
+ isc_net_probe_ipv6only() != ISC_R_SUCCESS) {
+ ipv6only = ISC_FALSE;
+ log_explicit = ISC_TRUE;
+ }
+#endif
+ if (scan_ipv6 == ISC_TRUE &&
+ isc_net_probe_ipv6pktinfo() != ISC_R_SUCCESS) {
+ ipv6pktinfo = ISC_FALSE;
+ log_explicit = ISC_TRUE;
+ }
+ if (scan_ipv6 == ISC_TRUE && ipv6only && ipv6pktinfo) {
+ for (le = ISC_LIST_HEAD(mgr->listenon6->elts);
+ le != NULL;
+ le = ISC_LIST_NEXT(le, link)) {
+ struct in6_addr in6a;
+
+ if (!listenon_is_ip6_any(le))
+ continue;
+
+ in6a = in6addr_any;
+ isc_sockaddr_fromin6(&listen_addr, &in6a, le->port);
+
+ ifp = find_matching_interface(mgr, &listen_addr);
+ if (ifp != NULL) {
+ ifp->generation = mgr->generation;
+ } else {
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_INFO,
+ "listening on IPv6 "
+ "interfaces, port %u",
+ le->port);
+ result = ns_interface_setup(mgr, &listen_addr,
+ "<any>", &ifp,
+ ISC_TRUE);
+ if (result == ISC_R_SUCCESS)
+ ifp->flags |= NS_INTERFACEFLAG_ANYADDR;
+ else
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_ERROR,
+ "listening on all IPv6 "
+ "interfaces failed");
+ /* Continue. */
+ }
+ }
+ }
+
+ isc_netaddr_any(&zero_address);
+ isc_netaddr_any6(&zero_address6);
+
+ result = isc_interfaceiter_create(mgr->mctx, &iter);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (adjusting == ISC_FALSE) {
+ result = clearacl(mgr->mctx, &mgr->aclenv.localhost);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iter;
+ result = clearacl(mgr->mctx, &mgr->aclenv.localnets);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iter;
+ clearlistenon(mgr);
+ }
+
+ for (result = isc_interfaceiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = isc_interfaceiter_next(iter))
+ {
+ isc_interface_t interface;
+ ns_listenlist_t *ll;
+ unsigned int family;
+
+ result = isc_interfaceiter_current(iter, &interface);
+ if (result != ISC_R_SUCCESS)
+ break;
+
+ family = interface.address.family;
+ if (family != AF_INET && family != AF_INET6)
+ continue;
+ if (scan_ipv4 == ISC_FALSE && family == AF_INET)
+ continue;
+ if (scan_ipv6 == ISC_FALSE && family == AF_INET6)
+ continue;
+
+ /*
+ * Test for the address being nonzero rather than testing
+ * INTERFACE_F_UP, because on some systems the latter
+ * follows the media state and we could end up ignoring
+ * the interface for an entire rescan interval due to
+ * a temporary media glitch at rescan time.
+ */
+ if (family == AF_INET &&
+ isc_netaddr_equal(&interface.address, &zero_address)) {
+ continue;
+ }
+ if (family == AF_INET6 &&
+ isc_netaddr_equal(&interface.address, &zero_address6)) {
+ continue;
+ }
+
+ if (adjusting == ISC_FALSE) {
+ result = setup_locals(mgr, &interface);
+ if (result != ISC_R_SUCCESS)
+ goto ignore_interface;
+ }
+
+ ll = (family == AF_INET) ? mgr->listenon4 : mgr->listenon6;
+ dolistenon = ISC_TRUE;
+ for (le = ISC_LIST_HEAD(ll->elts);
+ le != NULL;
+ le = ISC_LIST_NEXT(le, link))
+ {
+ int match;
+ isc_boolean_t ipv6_wildcard = ISC_FALSE;
+ isc_netaddr_t listen_netaddr;
+ isc_sockaddr_t listen_sockaddr;
+
+ /*
+ * Construct a socket address for this IP/port
+ * combination.
+ */
+ if (family == AF_INET) {
+ isc_netaddr_fromin(&listen_netaddr,
+ &interface.address.type.in);
+ } else {
+ isc_netaddr_fromin6(&listen_netaddr,
+ &interface.address.type.in6);
+ isc_netaddr_setzone(&listen_netaddr,
+ interface.address.zone);
+ }
+ isc_sockaddr_fromnetaddr(&listen_sockaddr,
+ &listen_netaddr,
+ le->port);
+
+ /*
+ * See if the address matches the listen-on statement;
+ * if not, ignore the interface.
+ */
+ (void)dns_acl_match(&listen_netaddr, NULL, le->acl,
+ &mgr->aclenv, &match, NULL);
+ if (match <= 0)
+ continue;
+
+ if (adjusting == ISC_FALSE && dolistenon == ISC_TRUE) {
+ setup_listenon(mgr, &interface, le->port);
+ dolistenon = ISC_FALSE;
+ }
+
+ /*
+ * The case of "any" IPv6 address will require
+ * special considerations later, so remember it.
+ */
+ if (family == AF_INET6 && ipv6only && ipv6pktinfo &&
+ listenon_is_ip6_any(le))
+ ipv6_wildcard = ISC_TRUE;
+
+ /*
+ * When adjusting interfaces with extra a listening
+ * list, see if the address matches the extra list.
+ * If it does, and is also covered by a wildcard
+ * interface, we need to listen on the address
+ * explicitly.
+ */
+ if (adjusting == ISC_TRUE) {
+ ns_listenelt_t *ele;
+
+ match = 0;
+ for (ele = ISC_LIST_HEAD(ext_listen->elts);
+ ele != NULL;
+ ele = ISC_LIST_NEXT(ele, link)) {
+ (void)dns_acl_match(&listen_netaddr,
+ NULL, ele->acl,
+ NULL, &match, NULL);
+ if (match > 0 && ele->port == le->port)
+ break;
+ else
+ match = 0;
+ }
+ if (ipv6_wildcard == ISC_TRUE && match == 0)
+ continue;
+ }
+
+ ifp = find_matching_interface(mgr, &listen_sockaddr);
+ if (ifp != NULL) {
+ ifp->generation = mgr->generation;
+ } else {
+ char sabuf[ISC_SOCKADDR_FORMATSIZE];
+
+ if (adjusting == ISC_FALSE &&
+ ipv6_wildcard == ISC_TRUE)
+ continue;
+
+ if (log_explicit && family == AF_INET6 &&
+ !adjusting && listenon_is_ip6_any(le)) {
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ verbose ? ISC_LOG_INFO :
+ ISC_LOG_DEBUG(1),
+ "IPv6 socket API is "
+ "incomplete; explicitly "
+ "binding to each IPv6 "
+ "address separately");
+ log_explicit = ISC_FALSE;
+ }
+ isc_sockaddr_format(&listen_sockaddr,
+ sabuf, sizeof(sabuf));
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_INFO,
+ "%s"
+ "listening on %s interface "
+ "%s, %s",
+ (adjusting == ISC_TRUE) ?
+ "additionally " : "",
+ (family == AF_INET) ?
+ "IPv4" : "IPv6",
+ interface.name, sabuf);
+
+ result = ns_interface_setup(mgr,
+ &listen_sockaddr,
+ interface.name,
+ &ifp,
+ (adjusting == ISC_TRUE) ?
+ ISC_FALSE :
+ ISC_TRUE);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_ERROR,
+ "creating %s interface "
+ "%s failed; interface "
+ "ignored",
+ (family == AF_INET) ?
+ "IPv4" : "IPv6",
+ interface.name);
+ }
+ /* Continue. */
+ }
+
+ }
+ continue;
+
+ ignore_interface:
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_ERROR,
+ "ignoring %s interface %s: %s",
+ (family == AF_INET) ? "IPv4" : "IPv6",
+ interface.name, isc_result_totext(result));
+ continue;
+ }
+ if (result != ISC_R_NOMORE)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "interface iteration failed: %s",
+ isc_result_totext(result));
+ else
+ result = ISC_R_SUCCESS;
+ cleanup_iter:
+ isc_interfaceiter_destroy(&iter);
+ return (result);
+}
+
+static void
+ns_interfacemgr_scan0(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
+ isc_boolean_t verbose)
+{
+ isc_boolean_t purge = ISC_TRUE;
+
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+
+ mgr->generation++; /* Increment the generation count. */
+
+ if (do_scan(mgr, ext_listen, verbose) != ISC_R_SUCCESS)
+ purge = ISC_FALSE;
+
+ /*
+ * Now go through the interface list and delete anything that
+ * does not have the current generation number. This is
+ * how we catch interfaces that go away or change their
+ * addresses.
+ */
+ if (purge)
+ purge_old_interfaces(mgr);
+
+ /*
+ * Warn if we are not listening on any interface, unless
+ * we're in lwresd-only mode, in which case that is to
+ * be expected.
+ */
+ if (ext_listen == NULL &&
+ ISC_LIST_EMPTY(mgr->interfaces) && ! ns_g_lwresdonly) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_WARNING,
+ "not listening on any interfaces");
+ }
+}
+
+void
+ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) {
+ ns_interfacemgr_scan0(mgr, NULL, verbose);
+}
+
+void
+ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list,
+ isc_boolean_t verbose)
+{
+ ns_interfacemgr_scan0(mgr, list, verbose);
+}
+
+void
+ns_interfacemgr_setlistenon4(ns_interfacemgr_t *mgr, ns_listenlist_t *value) {
+ LOCK(&mgr->lock);
+ ns_listenlist_detach(&mgr->listenon4);
+ ns_listenlist_attach(value, &mgr->listenon4);
+ UNLOCK(&mgr->lock);
+}
+
+void
+ns_interfacemgr_setlistenon6(ns_interfacemgr_t *mgr, ns_listenlist_t *value) {
+ LOCK(&mgr->lock);
+ ns_listenlist_detach(&mgr->listenon6);
+ ns_listenlist_attach(value, &mgr->listenon6);
+ UNLOCK(&mgr->lock);
+}
+
+void
+ns_interfacemgr_dumprecursing(FILE *f, ns_interfacemgr_t *mgr) {
+ ns_interface_t *interface;
+
+ LOCK(&mgr->lock);
+ interface = ISC_LIST_HEAD(mgr->interfaces);
+ while (interface != NULL) {
+ if (interface->clientmgr != NULL)
+ ns_client_dumprecursing(f, interface->clientmgr);
+ interface = ISC_LIST_NEXT(interface, link);
+ }
+ UNLOCK(&mgr->lock);
+}
+
+isc_boolean_t
+ns_interfacemgr_listeningon(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr) {
+ isc_sockaddr_t *old;
+
+ old = ISC_LIST_HEAD(mgr->listenon);
+ for (old = ISC_LIST_HEAD(mgr->listenon);
+ old != NULL;
+ old = ISC_LIST_NEXT(old, link))
+ if (isc_sockaddr_equal(old, addr))
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
diff --git a/bin/named/listenlist.c b/bin/named/listenlist.c
new file mode 100644
index 0000000..7e70ac9
--- /dev/null
+++ b/bin/named/listenlist.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: listenlist.c,v 1.10.18.2 2005/04/29 00:15:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+
+#include <named/listenlist.h>
+
+static void
+destroy(ns_listenlist_t *list);
+
+isc_result_t
+ns_listenelt_create(isc_mem_t *mctx, in_port_t port,
+ dns_acl_t *acl, ns_listenelt_t **target)
+{
+ ns_listenelt_t *elt = NULL;
+ REQUIRE(target != NULL && *target == NULL);
+ elt = isc_mem_get(mctx, sizeof(*elt));
+ if (elt == NULL)
+ return (ISC_R_NOMEMORY);
+ elt->mctx = mctx;
+ ISC_LINK_INIT(elt, link);
+ elt->port = port;
+ elt->acl = acl;
+ *target = elt;
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_listenelt_destroy(ns_listenelt_t *elt) {
+ if (elt->acl != NULL)
+ dns_acl_detach(&elt->acl);
+ isc_mem_put(elt->mctx, elt, sizeof(*elt));
+}
+
+isc_result_t
+ns_listenlist_create(isc_mem_t *mctx, ns_listenlist_t **target) {
+ ns_listenlist_t *list = NULL;
+ REQUIRE(target != NULL && *target == NULL);
+ list = isc_mem_get(mctx, sizeof(*list));
+ if (list == NULL)
+ return (ISC_R_NOMEMORY);
+ list->mctx = mctx;
+ list->refcount = 1;
+ ISC_LIST_INIT(list->elts);
+ *target = list;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+destroy(ns_listenlist_t *list) {
+ ns_listenelt_t *elt, *next;
+ for (elt = ISC_LIST_HEAD(list->elts);
+ elt != NULL;
+ elt = next)
+ {
+ next = ISC_LIST_NEXT(elt, link);
+ ns_listenelt_destroy(elt);
+ }
+ isc_mem_put(list->mctx, list, sizeof(*list));
+}
+
+void
+ns_listenlist_attach(ns_listenlist_t *source, ns_listenlist_t **target) {
+ INSIST(source->refcount > 0);
+ source->refcount++;
+ *target = source;
+}
+
+void
+ns_listenlist_detach(ns_listenlist_t **listp) {
+ ns_listenlist_t *list = *listp;
+ INSIST(list->refcount > 0);
+ list->refcount--;
+ if (list->refcount == 0)
+ destroy(list);
+ *listp = NULL;
+}
+
+isc_result_t
+ns_listenlist_default(isc_mem_t *mctx, in_port_t port,
+ isc_boolean_t enabled, ns_listenlist_t **target)
+{
+ isc_result_t result;
+ dns_acl_t *acl = NULL;
+ ns_listenelt_t *elt = NULL;
+ ns_listenlist_t *list = NULL;
+
+ REQUIRE(target != NULL && *target == NULL);
+ if (enabled)
+ result = dns_acl_any(mctx, &acl);
+ else
+ result = dns_acl_none(mctx, &acl);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = ns_listenelt_create(mctx, port, acl, &elt);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_acl;
+
+ result = ns_listenlist_create(mctx, &list);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_listenelt;
+
+ ISC_LIST_APPEND(list->elts, elt, link);
+
+ *target = list;
+ return (ISC_R_SUCCESS);
+
+ cleanup_listenelt:
+ ns_listenelt_destroy(elt);
+ cleanup_acl:
+ dns_acl_detach(&acl);
+ cleanup:
+ return (result);
+}
diff --git a/bin/named/log.c b/bin/named/log.c
new file mode 100644
index 0000000..af75bab
--- /dev/null
+++ b/bin/named/log.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 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: log.c,v 1.37.18.6 2006/06/09 00:54:08 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/result.h>
+
+#include <isccfg/log.h>
+
+#include <named/log.h>
+
+#ifndef ISC_FACILITY
+#define ISC_FACILITY LOG_DAEMON
+#endif
+
+/*%
+ * When adding a new category, be sure to add the appropriate
+ * #define to <named/log.h> and to update the list in
+ * bin/check/check-tool.c.
+ */
+static isc_logcategory_t categories[] = {
+ { "", 0 },
+ { "client", 0 },
+ { "network", 0 },
+ { "update", 0 },
+ { "queries", 0 },
+ { "unmatched", 0 },
+ { "update-security", 0 },
+ { NULL, 0 }
+};
+
+/*%
+ * When adding a new module, be sure to add the appropriate
+ * #define to <dns/log.h>.
+ */
+static isc_logmodule_t modules[] = {
+ { "main", 0 },
+ { "client", 0 },
+ { "server", 0 },
+ { "query", 0 },
+ { "interfacemgr", 0 },
+ { "update", 0 },
+ { "xfer-in", 0 },
+ { "xfer-out", 0 },
+ { "notify", 0 },
+ { "control", 0 },
+ { "lwresd", 0 },
+ { NULL, 0 }
+};
+
+isc_result_t
+ns_log_init(isc_boolean_t safe) {
+ isc_result_t result;
+ isc_logconfig_t *lcfg = NULL;
+
+ ns_g_categories = categories;
+ ns_g_modules = modules;
+
+ /*
+ * Setup a logging context.
+ */
+ result = isc_log_create(ns_g_mctx, &ns_g_lctx, &lcfg);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * named-checktool.c:setup_logging() needs to be kept in sync.
+ */
+ isc_log_registercategories(ns_g_lctx, ns_g_categories);
+ isc_log_registermodules(ns_g_lctx, ns_g_modules);
+ isc_log_setcontext(ns_g_lctx);
+ dns_log_init(ns_g_lctx);
+ dns_log_setcontext(ns_g_lctx);
+ cfg_log_init(ns_g_lctx);
+
+ if (safe)
+ result = ns_log_setsafechannels(lcfg);
+ else
+ result = ns_log_setdefaultchannels(lcfg);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = ns_log_setdefaultcategory(lcfg);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ isc_log_destroy(&ns_g_lctx);
+ isc_log_setcontext(NULL);
+ dns_log_setcontext(NULL);
+
+ return (result);
+}
+
+isc_result_t
+ns_log_setdefaultchannels(isc_logconfig_t *lcfg) {
+ isc_result_t result;
+ isc_logdestination_t destination;
+
+ /*
+ * By default, the logging library makes "default_debug" log to
+ * stderr. In BIND, we want to override this and log to named.run
+ * instead, unless the the -g option was given.
+ */
+ if (! ns_g_logstderr) {
+ destination.file.stream = NULL;
+ destination.file.name = "named.run";
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.maximum_size = 0;
+ result = isc_log_createchannel(lcfg, "default_debug",
+ ISC_LOG_TOFILE,
+ ISC_LOG_DYNAMIC,
+ &destination,
+ ISC_LOG_PRINTTIME|
+ ISC_LOG_DEBUGONLY);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+#if ISC_FACILITY != LOG_DAEMON
+ destination.facility = ISC_FACILITY;
+ result = isc_log_createchannel(lcfg, "default_syslog",
+ ISC_LOG_TOSYSLOG, ISC_LOG_INFO,
+ &destination, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#endif
+
+ /*
+ * Set the initial debug level.
+ */
+ isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+ns_log_setsafechannels(isc_logconfig_t *lcfg) {
+ isc_result_t result;
+#if ISC_FACILITY != LOG_DAEMON
+ isc_logdestination_t destination;
+#endif
+
+ if (! ns_g_logstderr) {
+ result = isc_log_createchannel(lcfg, "default_debug",
+ ISC_LOG_TONULL,
+ ISC_LOG_DYNAMIC,
+ NULL, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Setting the debug level to zero should get the output
+ * discarded a bit faster.
+ */
+ isc_log_setdebuglevel(ns_g_lctx, 0);
+ } else {
+ isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
+ }
+
+#if ISC_FACILITY != LOG_DAEMON
+ destination.facility = ISC_FACILITY;
+ result = isc_log_createchannel(lcfg, "default_syslog",
+ ISC_LOG_TOSYSLOG, ISC_LOG_INFO,
+ &destination, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#endif
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+ns_log_setdefaultcategory(isc_logconfig_t *lcfg) {
+ isc_result_t result;
+
+ if (! ns_g_logstderr) {
+ result = isc_log_usechannel(lcfg, "default_syslog",
+ ISC_LOGCATEGORY_DEFAULT, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ result = isc_log_usechannel(lcfg, "default_debug",
+ ISC_LOGCATEGORY_DEFAULT, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+ns_log_setunmatchedcategory(isc_logconfig_t *lcfg) {
+ isc_result_t result;
+
+ result = isc_log_usechannel(lcfg, "null",
+ NS_LOGCATEGORY_UNMATCHED, NULL);
+ return (result);
+}
+
+void
+ns_log_shutdown(void) {
+ isc_log_destroy(&ns_g_lctx);
+ isc_log_setcontext(NULL);
+ dns_log_setcontext(NULL);
+}
diff --git a/bin/named/logconf.c b/bin/named/logconf.c
new file mode 100644
index 0000000..ce815f4
--- /dev/null
+++ b/bin/named/logconf.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: logconf.c,v 1.35.18.5 2006/03/02 00:37:21 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/offset.h>
+#include <isc/result.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/syslog.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/log.h>
+
+#include <named/log.h>
+#include <named/logconf.h>
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+/*%
+ * Set up a logging category according to the named.conf data
+ * in 'ccat' and add it to 'lctx'.
+ */
+static isc_result_t
+category_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *lctx) {
+ isc_result_t result;
+ const char *catname;
+ isc_logcategory_t *category;
+ isc_logmodule_t *module;
+ const cfg_obj_t *destinations = NULL;
+ const cfg_listelt_t *element = NULL;
+
+ catname = cfg_obj_asstring(cfg_tuple_get(ccat, "name"));
+ category = isc_log_categorybyname(ns_g_lctx, catname);
+ if (category == NULL) {
+ cfg_obj_log(ccat, ns_g_lctx, ISC_LOG_ERROR,
+ "unknown logging category '%s' ignored",
+ catname);
+ /*
+ * Allow further processing by returning success.
+ */
+ return (ISC_R_SUCCESS);
+ }
+
+ module = NULL;
+
+ destinations = cfg_tuple_get(ccat, "destinations");
+ for (element = cfg_list_first(destinations);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *channel = cfg_listelt_value(element);
+ const char *channelname = cfg_obj_asstring(channel);
+
+ result = isc_log_usechannel(lctx, channelname, category,
+ module);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "logging channel '%s': %s", channelname,
+ isc_result_totext(result));
+ return (result);
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Set up a logging channel according to the named.conf data
+ * in 'cchan' and add it to 'lctx'.
+ */
+static isc_result_t
+channel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *lctx) {
+ isc_result_t result;
+ isc_logdestination_t dest;
+ unsigned int type;
+ unsigned int flags = 0;
+ int level;
+ const char *channelname;
+ 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 *severity = NULL;
+ int i;
+
+ channelname = cfg_obj_asstring(cfg_map_getname(channel));
+
+ (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, ns_g_lctx, ISC_LOG_ERROR,
+ "channel '%s': exactly one of file, syslog, "
+ "null, and stderr must be present", channelname);
+ return (ISC_R_FAILURE);
+ }
+
+ type = ISC_LOG_TONULL;
+
+ if (fileobj != NULL) {
+ const cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file");
+ const cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size");
+ const cfg_obj_t *versionsobj =
+ cfg_tuple_get(fileobj, "versions");
+ isc_int32_t versions = ISC_LOG_ROLLNEVER;
+ isc_offset_t size = 0;
+
+ type = ISC_LOG_TOFILE;
+
+ if (versionsobj != NULL && cfg_obj_isuint32(versionsobj))
+ versions = cfg_obj_asuint32(versionsobj);
+ if (versionsobj != NULL && cfg_obj_isstring(versionsobj) &&
+ strcasecmp(cfg_obj_asstring(versionsobj), "unlimited") == 0)
+ versions = ISC_LOG_ROLLINFINITE;
+ if (sizeobj != NULL &&
+ cfg_obj_isuint64(sizeobj) &&
+ cfg_obj_asuint64(sizeobj) < ISC_OFFSET_MAXIMUM)
+ size = (isc_offset_t)cfg_obj_asuint64(sizeobj);
+ dest.file.stream = NULL;
+ dest.file.name = cfg_obj_asstring(pathobj);
+ dest.file.versions = versions;
+ dest.file.maximum_size = size;
+ } else if (syslogobj != NULL) {
+ int facility = LOG_DAEMON;
+
+ type = ISC_LOG_TOSYSLOG;
+
+ if (cfg_obj_isstring(syslogobj)) {
+ const char *facilitystr = cfg_obj_asstring(syslogobj);
+ (void)isc_syslog_facilityfromstring(facilitystr,
+ &facility);
+ }
+ dest.facility = facility;
+ } else if (stderrobj != NULL) {
+ type = ISC_LOG_TOFILEDESC;
+ dest.file.stream = stderr;
+ dest.file.name = NULL;
+ dest.file.versions = ISC_LOG_ROLLNEVER;
+ dest.file.maximum_size = 0;
+ }
+
+ /*
+ * Munge flags.
+ */
+ {
+ const cfg_obj_t *printcat = NULL;
+ const cfg_obj_t *printsev = NULL;
+ const cfg_obj_t *printtime = NULL;
+
+ (void)cfg_map_get(channel, "print-category", &printcat);
+ (void)cfg_map_get(channel, "print-severity", &printsev);
+ (void)cfg_map_get(channel, "print-time", &printtime);
+
+ if (printcat != NULL && cfg_obj_asboolean(printcat))
+ flags |= ISC_LOG_PRINTCATEGORY;
+ if (printtime != NULL && cfg_obj_asboolean(printtime))
+ flags |= ISC_LOG_PRINTTIME;
+ if (printsev != NULL && cfg_obj_asboolean(printsev))
+ flags |= ISC_LOG_PRINTLEVEL;
+ }
+
+ level = ISC_LOG_INFO;
+ if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) {
+ if (cfg_obj_isstring(severity)) {
+ const char *str = cfg_obj_asstring(severity);
+ if (strcasecmp(str, "critical") == 0)
+ level = ISC_LOG_CRITICAL;
+ else if (strcasecmp(str, "error") == 0)
+ level = ISC_LOG_ERROR;
+ else if (strcasecmp(str, "warning") == 0)
+ level = ISC_LOG_WARNING;
+ else if (strcasecmp(str, "notice") == 0)
+ level = ISC_LOG_NOTICE;
+ else if (strcasecmp(str, "info") == 0)
+ level = ISC_LOG_INFO;
+ else if (strcasecmp(str, "dynamic") == 0)
+ level = ISC_LOG_DYNAMIC;
+ } else
+ /* debug */
+ level = cfg_obj_asuint32(severity);
+ }
+
+ result = isc_log_createchannel(lctx, channelname,
+ type, level, &dest, flags);
+
+ if (result == ISC_R_SUCCESS && type == ISC_LOG_TOFILE) {
+ FILE *fp;
+
+ /*
+ * Test that the file can be opened, since isc_log_open()
+ * can't effectively report failures when called in
+ * isc_log_doit().
+ */
+ result = isc_stdio_open(dest.file.name, "a", &fp);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "logging channel '%s' file '%s': %s",
+ channelname, dest.file.name,
+ isc_result_totext(result));
+ else
+ (void)isc_stdio_close(fp);
+
+ /*
+ * Allow named to continue by returning success.
+ */
+ result = ISC_R_SUCCESS;
+ }
+
+ return (result);
+}
+
+isc_result_t
+ns_log_configure(isc_logconfig_t *logconf, const cfg_obj_t *logstmt) {
+ isc_result_t result;
+ const cfg_obj_t *channels = NULL;
+ const cfg_obj_t *categories = NULL;
+ const cfg_listelt_t *element;
+ isc_boolean_t default_set = ISC_FALSE;
+ isc_boolean_t unmatched_set = ISC_FALSE;
+ const cfg_obj_t *catname;
+
+ CHECK(ns_log_setdefaultchannels(logconf));
+
+ (void)cfg_map_get(logstmt, "channel", &channels);
+ for (element = cfg_list_first(channels);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *channel = cfg_listelt_value(element);
+ CHECK(channel_fromconf(channel, logconf));
+ }
+
+ (void)cfg_map_get(logstmt, "category", &categories);
+ for (element = cfg_list_first(categories);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *category = cfg_listelt_value(element);
+ CHECK(category_fromconf(category, logconf));
+ if (!default_set) {
+ catname = cfg_tuple_get(category, "name");
+ if (strcmp(cfg_obj_asstring(catname), "default") == 0)
+ default_set = ISC_TRUE;
+ }
+ if (!unmatched_set) {
+ catname = cfg_tuple_get(category, "name");
+ if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0)
+ unmatched_set = ISC_TRUE;
+ }
+ }
+
+ if (!default_set)
+ CHECK(ns_log_setdefaultcategory(logconf));
+
+ if (!unmatched_set)
+ CHECK(ns_log_setunmatchedcategory(logconf));
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (logconf != NULL)
+ isc_logconfig_destroy(&logconf);
+ return (result);
+}
diff --git a/bin/named/lwaddr.c b/bin/named/lwaddr.c
new file mode 100644
index 0000000..78c2b0b
--- /dev/null
+++ b/bin/named/lwaddr.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwaddr.c,v 1.4.18.2 2005/04/29 00:15:23 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <isc/result.h>
+#include <isc/netaddr.h>
+#include <isc/sockaddr.h>
+
+#include <lwres/lwres.h>
+
+#include <named/lwaddr.h>
+
+/*%
+ * Convert addresses from lwres to isc format.
+ */
+isc_result_t
+lwaddr_netaddr_fromlwresaddr(isc_netaddr_t *na, lwres_addr_t *la) {
+ if (la->family != LWRES_ADDRTYPE_V4 && la->family != LWRES_ADDRTYPE_V6)
+ return (ISC_R_FAMILYNOSUPPORT);
+
+ if (la->family == LWRES_ADDRTYPE_V4) {
+ struct in_addr ina;
+ memcpy(&ina.s_addr, la->address, 4);
+ isc_netaddr_fromin(na, &ina);
+ } else {
+ struct in6_addr ina6;
+ memcpy(&ina6.s6_addr, la->address, 16);
+ isc_netaddr_fromin6(na, &ina6);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+lwaddr_sockaddr_fromlwresaddr(isc_sockaddr_t *sa, lwres_addr_t *la,
+ in_port_t port)
+{
+ isc_netaddr_t na;
+ isc_result_t result;
+
+ result = lwaddr_netaddr_fromlwresaddr(&na, la);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_sockaddr_fromnetaddr(sa, &na, port);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Convert addresses from isc to lwres format.
+ */
+
+isc_result_t
+lwaddr_lwresaddr_fromnetaddr(lwres_addr_t *la, isc_netaddr_t *na) {
+ if (na->family != AF_INET && na->family != AF_INET6)
+ return (ISC_R_FAMILYNOSUPPORT);
+
+ if (na->family == AF_INET) {
+ la->family = LWRES_ADDRTYPE_V4;
+ la->length = 4;
+ memcpy(la->address, &na->type.in, 4);
+ } else {
+ la->family = LWRES_ADDRTYPE_V6;
+ la->length = 16;
+ memcpy(la->address, &na->type.in, 16);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+lwaddr_lwresaddr_fromsockaddr(lwres_addr_t *la, isc_sockaddr_t *sa) {
+ isc_netaddr_t na;
+ isc_netaddr_fromsockaddr(&na, sa);
+ return (lwaddr_lwresaddr_fromnetaddr(la, &na));
+}
diff --git a/bin/named/lwdclient.c b/bin/named/lwdclient.c
new file mode 100644
index 0000000..68069ed
--- /dev/null
+++ b/bin/named/lwdclient.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwdclient.c,v 1.17.18.2 2005/04/29 00:15:23 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/socket.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/view.h>
+#include <dns/log.h>
+
+#include <named/types.h>
+#include <named/log.h>
+#include <named/lwresd.h>
+#include <named/lwdclient.h>
+
+#define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
+
+static void
+lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev);
+
+void
+ns_lwdclient_log(int level, const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ isc_log_vwrite(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
+ ISC_LOG_DEBUG(level), format, args);
+ va_end(args);
+}
+
+isc_result_t
+ns_lwdclientmgr_create(ns_lwreslistener_t *listener, unsigned int nclients,
+ isc_taskmgr_t *taskmgr)
+{
+ ns_lwresd_t *lwresd = listener->manager;
+ ns_lwdclientmgr_t *cm;
+ ns_lwdclient_t *client;
+ unsigned int i;
+ isc_result_t result = ISC_R_FAILURE;
+
+ cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
+ if (cm == NULL)
+ return (ISC_R_NOMEMORY);
+
+ cm->listener = NULL;
+ ns_lwreslistener_attach(listener, &cm->listener);
+ cm->mctx = lwresd->mctx;
+ cm->sock = NULL;
+ isc_socket_attach(listener->sock, &cm->sock);
+ cm->view = lwresd->view;
+ cm->lwctx = NULL;
+ cm->task = NULL;
+ cm->flags = 0;
+ ISC_LINK_INIT(cm, link);
+ ISC_LIST_INIT(cm->idle);
+ ISC_LIST_INIT(cm->running);
+
+ if (lwres_context_create(&cm->lwctx, cm->mctx,
+ ns__lwresd_memalloc, ns__lwresd_memfree,
+ LWRES_CONTEXT_SERVERMODE)
+ != ISC_R_SUCCESS)
+ goto errout;
+
+ for (i = 0; i < nclients; i++) {
+ client = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclient_t));
+ if (client != NULL) {
+ ns_lwdclient_log(50, "created client %p, manager %p",
+ client, cm);
+ ns_lwdclient_initialize(client, cm);
+ }
+ }
+
+ /*
+ * If we could create no clients, clean up and return.
+ */
+ if (ISC_LIST_EMPTY(cm->idle))
+ goto errout;
+
+ result = isc_task_create(taskmgr, 0, &cm->task);
+ if (result != ISC_R_SUCCESS)
+ goto errout;
+
+ /*
+ * This MUST be last, since there is no way to cancel an onshutdown...
+ */
+ result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
+ cm);
+ if (result != ISC_R_SUCCESS)
+ goto errout;
+
+ ns_lwreslistener_linkcm(listener, cm);
+
+ return (ISC_R_SUCCESS);
+
+ errout:
+ client = ISC_LIST_HEAD(cm->idle);
+ while (client != NULL) {
+ ISC_LIST_UNLINK(cm->idle, client, link);
+ isc_mem_put(lwresd->mctx, client, sizeof(*client));
+ client = ISC_LIST_HEAD(cm->idle);
+ }
+
+ if (cm->task != NULL)
+ isc_task_detach(&cm->task);
+
+ if (cm->lwctx != NULL)
+ lwres_context_destroy(&cm->lwctx);
+
+ isc_mem_put(lwresd->mctx, cm, sizeof(*cm));
+ return (result);
+}
+
+static void
+lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
+ ns_lwdclient_t *client;
+ ns_lwreslistener_t *listener;
+
+ if (!SHUTTINGDOWN(cm))
+ return;
+
+ /*
+ * run through the idle list and free the clients there. Idle
+ * clients do not have a recv running nor do they have any finds
+ * or similar running.
+ */
+ client = ISC_LIST_HEAD(cm->idle);
+ while (client != NULL) {
+ ns_lwdclient_log(50, "destroying client %p, manager %p",
+ client, cm);
+ ISC_LIST_UNLINK(cm->idle, client, link);
+ isc_mem_put(cm->mctx, client, sizeof(*client));
+ client = ISC_LIST_HEAD(cm->idle);
+ }
+
+ if (!ISC_LIST_EMPTY(cm->running))
+ return;
+
+ lwres_context_destroy(&cm->lwctx);
+ cm->view = NULL;
+ isc_socket_detach(&cm->sock);
+ isc_task_detach(&cm->task);
+
+ listener = cm->listener;
+ ns_lwreslistener_unlinkcm(listener, cm);
+ ns_lwdclient_log(50, "destroying manager %p", cm);
+ isc_mem_put(cm->mctx, cm, sizeof(*cm));
+ ns_lwreslistener_detach(&listener);
+}
+
+static void
+process_request(ns_lwdclient_t *client) {
+ lwres_buffer_t b;
+ isc_result_t result;
+
+ lwres_buffer_init(&b, client->buffer, client->recvlength);
+ lwres_buffer_add(&b, client->recvlength);
+
+ result = lwres_lwpacket_parseheader(&b, &client->pkt);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_log(50, "invalid packet header received");
+ goto restart;
+ }
+
+ ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
+
+ switch (client->pkt.opcode) {
+ case LWRES_OPCODE_GETADDRSBYNAME:
+ ns_lwdclient_processgabn(client, &b);
+ return;
+ case LWRES_OPCODE_GETNAMEBYADDR:
+ ns_lwdclient_processgnba(client, &b);
+ return;
+ case LWRES_OPCODE_GETRDATABYNAME:
+ ns_lwdclient_processgrbn(client, &b);
+ return;
+ case LWRES_OPCODE_NOOP:
+ ns_lwdclient_processnoop(client, &b);
+ return;
+ default:
+ ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode);
+ goto restart;
+ }
+
+ /*
+ * Drop the packet.
+ */
+ restart:
+ ns_lwdclient_log(50, "restarting client %p...", client);
+ ns_lwdclient_stateidle(client);
+}
+
+void
+ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) {
+ isc_result_t result;
+ ns_lwdclient_t *client = ev->ev_arg;
+ ns_lwdclientmgr_t *cm = client->clientmgr;
+ isc_socketevent_t *dev = (isc_socketevent_t *)ev;
+
+ INSIST(dev->region.base == client->buffer);
+ INSIST(NS_LWDCLIENT_ISRECV(client));
+
+ NS_LWDCLIENT_SETRECVDONE(client);
+
+ INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
+ cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
+
+ ns_lwdclient_log(50,
+ "event received: task %p, length %u, result %u (%s)",
+ task, dev->n, dev->result,
+ isc_result_totext(dev->result));
+
+ if (dev->result != ISC_R_SUCCESS) {
+ isc_event_free(&ev);
+ dev = NULL;
+
+ /*
+ * Go idle.
+ */
+ ns_lwdclient_stateidle(client);
+
+ return;
+ }
+
+ client->recvlength = dev->n;
+ client->address = dev->address;
+ if ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
+ client->pktinfo = dev->pktinfo;
+ client->pktinfo_valid = ISC_TRUE;
+ } else
+ client->pktinfo_valid = ISC_FALSE;
+ isc_event_free(&ev);
+ dev = NULL;
+
+ result = ns_lwdclient_startrecv(cm);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
+ "could not start lwres "
+ "client handler: %s",
+ isc_result_totext(result));
+
+ process_request(client);
+}
+
+/*
+ * This function will start a new recv() on a socket for this client manager.
+ */
+isc_result_t
+ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) {
+ ns_lwdclient_t *client;
+ isc_result_t result;
+ isc_region_t r;
+
+ if (SHUTTINGDOWN(cm)) {
+ lwdclientmgr_destroy(cm);
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * If a recv is already running, don't bother.
+ */
+ if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * If we have no idle slots, just return success.
+ */
+ client = ISC_LIST_HEAD(cm->idle);
+ if (client == NULL)
+ return (ISC_R_SUCCESS);
+ INSIST(NS_LWDCLIENT_ISIDLE(client));
+
+ /*
+ * Issue the recv. If it fails, return that it did.
+ */
+ r.base = client->buffer;
+ r.length = LWRES_RECVLENGTH;
+ result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv,
+ client);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Set the flag to say we've issued a recv() call.
+ */
+ cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
+
+ /*
+ * Remove the client from the idle list, and put it on the running
+ * list.
+ */
+ NS_LWDCLIENT_SETRECV(client);
+ ISC_LIST_UNLINK(cm->idle, client, link);
+ ISC_LIST_APPEND(cm->running, client, link);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev) {
+ ns_lwdclientmgr_t *cm = ev->ev_arg;
+ ns_lwdclient_t *client;
+
+ REQUIRE(!SHUTTINGDOWN(cm));
+
+ ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
+ task, cm);
+
+ /*
+ * run through the idle list and free the clients there. Idle
+ * clients do not have a recv running nor do they have any finds
+ * or similar running.
+ */
+ client = ISC_LIST_HEAD(cm->idle);
+ while (client != NULL) {
+ ns_lwdclient_log(50, "destroying client %p, manager %p",
+ client, cm);
+ ISC_LIST_UNLINK(cm->idle, client, link);
+ isc_mem_put(cm->mctx, client, sizeof(*client));
+ client = ISC_LIST_HEAD(cm->idle);
+ }
+
+ /*
+ * Cancel any pending I/O.
+ */
+ isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
+
+ /*
+ * Run through the running client list and kill off any finds
+ * in progress.
+ */
+ client = ISC_LIST_HEAD(cm->running);
+ while (client != NULL) {
+ if (client->find != client->v4find
+ && client->find != client->v6find)
+ dns_adb_cancelfind(client->find);
+ if (client->v4find != NULL)
+ dns_adb_cancelfind(client->v4find);
+ if (client->v6find != NULL)
+ dns_adb_cancelfind(client->v6find);
+ client = ISC_LIST_NEXT(client, link);
+ }
+
+ cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
+
+ isc_event_free(&ev);
+}
+
+/*
+ * Do all the crap needed to move a client from the run queue to the idle
+ * queue.
+ */
+void
+ns_lwdclient_stateidle(ns_lwdclient_t *client) {
+ ns_lwdclientmgr_t *cm;
+ isc_result_t result;
+
+ cm = client->clientmgr;
+
+ INSIST(client->sendbuf == NULL);
+ INSIST(client->sendlength == 0);
+ INSIST(client->arg == NULL);
+ INSIST(client->v4find == NULL);
+ INSIST(client->v6find == NULL);
+
+ ISC_LIST_UNLINK(cm->running, client, link);
+ ISC_LIST_PREPEND(cm->idle, client, link);
+
+ NS_LWDCLIENT_SETIDLE(client);
+
+ result = ns_lwdclient_startrecv(cm);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
+ "could not start lwres "
+ "client handler: %s",
+ isc_result_totext(result));
+}
+
+void
+ns_lwdclient_send(isc_task_t *task, isc_event_t *ev) {
+ ns_lwdclient_t *client = ev->ev_arg;
+ ns_lwdclientmgr_t *cm = client->clientmgr;
+ isc_socketevent_t *dev = (isc_socketevent_t *)ev;
+
+ UNUSED(task);
+ UNUSED(dev);
+
+ INSIST(NS_LWDCLIENT_ISSEND(client));
+ INSIST(client->sendbuf == dev->region.base);
+
+ ns_lwdclient_log(50, "task %p for client %p got send-done event",
+ task, client);
+
+ if (client->sendbuf != client->buffer)
+ lwres_context_freemem(cm->lwctx, client->sendbuf,
+ client->sendlength);
+ client->sendbuf = NULL;
+ client->sendlength = 0;
+
+ ns_lwdclient_stateidle(client);
+
+ isc_event_free(&ev);
+}
+
+isc_result_t
+ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) {
+ struct in6_pktinfo *pktinfo;
+ ns_lwdclientmgr_t *cm = client->clientmgr;
+
+ if (client->pktinfo_valid)
+ pktinfo = &client->pktinfo;
+ else
+ pktinfo = NULL;
+ return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send,
+ client, &client->address, pktinfo));
+}
+
+void
+ns_lwdclient_initialize(ns_lwdclient_t *client, ns_lwdclientmgr_t *cmgr) {
+ client->clientmgr = cmgr;
+ ISC_LINK_INIT(client, link);
+ NS_LWDCLIENT_SETIDLE(client);
+ client->arg = NULL;
+
+ client->recvlength = 0;
+
+ client->sendbuf = NULL;
+ client->sendlength = 0;
+
+ client->find = NULL;
+ client->v4find = NULL;
+ client->v6find = NULL;
+ client->find_wanted = 0;
+
+ client->options = 0;
+ client->byaddr = NULL;
+
+ client->lookup = NULL;
+
+ client->pktinfo_valid = ISC_FALSE;
+
+ ISC_LIST_APPEND(cmgr->idle, client, link);
+}
diff --git a/bin/named/lwderror.c b/bin/named/lwderror.c
new file mode 100644
index 0000000..db25824
--- /dev/null
+++ b/bin/named/lwderror.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwderror.c,v 1.8.18.2 2005/04/29 00:15:24 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/socket.h>
+#include <isc/util.h>
+
+#include <named/types.h>
+#include <named/lwdclient.h>
+
+/*%
+ * Generate an error packet for the client, schedule a send, and put us in
+ * the SEND state.
+ *
+ * The client->pkt structure will be modified to form an error return.
+ * The receiver needs to verify that it is in fact an error, and do the
+ * right thing with it. The opcode will be unchanged. The result needs
+ * to be set before calling this function.
+ *
+ * The only change this code makes is to set the receive buffer size to the
+ * size we use, set the reply bit, and recompute any security information.
+ */
+void
+ns_lwdclient_errorpktsend(ns_lwdclient_t *client, isc_uint32_t _result) {
+ isc_result_t result;
+ int lwres;
+ isc_region_t r;
+ lwres_buffer_t b;
+
+ REQUIRE(NS_LWDCLIENT_ISRUNNING(client));
+
+ /*
+ * Since we are only sending the packet header, we can safely toss
+ * the receive buffer. This means we won't need to allocate space
+ * for sending an error reply. This is a Good Thing.
+ */
+ client->pkt.length = LWRES_LWPACKET_LENGTH;
+ client->pkt.pktflags |= LWRES_LWPACKETFLAG_RESPONSE;
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+ client->pkt.result = _result;
+
+ lwres_buffer_init(&b, client->buffer, LWRES_RECVLENGTH);
+ lwres = lwres_lwpacket_renderheader(&b, &client->pkt);
+ if (lwres != LWRES_R_SUCCESS) {
+ ns_lwdclient_stateidle(client);
+ return;
+ }
+
+ r.base = client->buffer;
+ r.length = b.used;
+ client->sendbuf = client->buffer;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_stateidle(client);
+ return;
+ }
+
+ NS_LWDCLIENT_SETSEND(client);
+}
diff --git a/bin/named/lwdgabn.c b/bin/named/lwdgabn.c
new file mode 100644
index 0000000..454d4df
--- /dev/null
+++ b/bin/named/lwdgabn.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwdgabn.c,v 1.15.18.5 2006/03/02 00:37:21 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/netaddr.h>
+#include <isc/sockaddr.h>
+#include <isc/socket.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/events.h>
+#include <dns/result.h>
+
+#include <named/types.h>
+#include <named/lwaddr.h>
+#include <named/lwdclient.h>
+#include <named/lwresd.h>
+#include <named/lwsearch.h>
+#include <named/sortlist.h>
+
+#define NEED_V4(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V4) != 0) \
+ && ((c)->v4find == NULL))
+#define NEED_V6(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V6) != 0) \
+ && ((c)->v6find == NULL))
+
+static isc_result_t start_find(ns_lwdclient_t *);
+static void restart_find(ns_lwdclient_t *);
+static void init_gabn(ns_lwdclient_t *);
+
+/*%
+ * Destroy any finds. This can be used to "start over from scratch" and
+ * should only be called when events are _not_ being generated by the finds.
+ */
+static void
+cleanup_gabn(ns_lwdclient_t *client) {
+ ns_lwdclient_log(50, "cleaning up client %p", client);
+
+ if (client->v6find != NULL) {
+ if (client->v6find == client->v4find)
+ client->v6find = NULL;
+ else
+ dns_adb_destroyfind(&client->v6find);
+ }
+ if (client->v4find != NULL)
+ dns_adb_destroyfind(&client->v4find);
+}
+
+static void
+setup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) {
+ dns_adbaddrinfo_t *ai;
+ lwres_addr_t *addr;
+ int af;
+ const struct sockaddr *sa;
+ isc_result_t result;
+
+ if (at == DNS_ADBFIND_INET)
+ af = AF_INET;
+ else
+ af = AF_INET6;
+
+ ai = ISC_LIST_HEAD(find->list);
+ while (ai != NULL && client->gabn.naddrs < LWRES_MAX_ADDRS) {
+ sa = &ai->sockaddr.type.sa;
+ if (sa->sa_family != af)
+ goto next;
+
+ addr = &client->addrs[client->gabn.naddrs];
+
+ result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
+ if (result != ISC_R_SUCCESS)
+ goto next;
+
+ ns_lwdclient_log(50, "adding address %p, family %d, length %d",
+ addr->address, addr->family, addr->length);
+
+ client->gabn.naddrs++;
+ REQUIRE(!LWRES_LINK_LINKED(addr, link));
+ LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
+
+ next:
+ ai = ISC_LIST_NEXT(ai, publink);
+ }
+}
+
+typedef struct {
+ isc_netaddr_t address;
+ int rank;
+} rankedaddress;
+
+static int
+addr_compare(const void *av, const void *bv) {
+ const rankedaddress *a = (const rankedaddress *) av;
+ const rankedaddress *b = (const rankedaddress *) bv;
+ return (a->rank - b->rank);
+}
+
+static void
+sort_addresses(ns_lwdclient_t *client) {
+ unsigned int naddrs;
+ rankedaddress *addrs;
+ isc_netaddr_t remote;
+ dns_addressorderfunc_t order;
+ const void *arg;
+ ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
+ unsigned int i;
+ isc_result_t result;
+
+ naddrs = client->gabn.naddrs;
+
+ if (naddrs <= 1 || lwresd->view->sortlist == NULL)
+ return;
+
+ addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
+ if (addrs == NULL)
+ return;
+
+ isc_netaddr_fromsockaddr(&remote, &client->address);
+ ns_sortlist_byaddrsetup(lwresd->view->sortlist,
+ &remote, &order, &arg);
+ if (order == NULL) {
+ isc_mem_put(lwresd->mctx, addrs,
+ sizeof(rankedaddress) * naddrs);
+ return;
+ }
+ for (i = 0; i < naddrs; i++) {
+ result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address,
+ &client->addrs[i]);
+ INSIST(result == ISC_R_SUCCESS);
+ addrs[i].rank = (*order)(&addrs[i].address, arg);
+ }
+ qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare);
+ for (i = 0; i < naddrs; i++) {
+ result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i],
+ &addrs[i].address);
+ INSIST(result == ISC_R_SUCCESS);
+ }
+
+ isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
+}
+
+static void
+generate_reply(ns_lwdclient_t *client) {
+ isc_result_t result;
+ int lwres;
+ isc_region_t r;
+ lwres_buffer_t lwb;
+ ns_lwdclientmgr_t *cm;
+
+ cm = client->clientmgr;
+ lwb.base = NULL;
+
+ ns_lwdclient_log(50, "generating gabn reply for client %p", client);
+
+ /*
+ * We must make certain the client->find is not still active.
+ * If it is either the v4 or v6 answer, just set it to NULL and
+ * let the cleanup code destroy it. Otherwise, destroy it now.
+ */
+ if (client->find == client->v4find || client->find == client->v6find)
+ client->find = NULL;
+ else
+ if (client->find != NULL)
+ dns_adb_destroyfind(&client->find);
+
+ /*
+ * perhaps there are some here?
+ */
+ if (NEED_V6(client) && client->v4find != NULL)
+ client->v6find = client->v4find;
+
+ /*
+ * Run through the finds we have and wire them up to the gabn
+ * structure.
+ */
+ LWRES_LIST_INIT(client->gabn.addrs);
+ if (client->v4find != NULL)
+ setup_addresses(client, client->v4find, DNS_ADBFIND_INET);
+ if (client->v6find != NULL)
+ setup_addresses(client, client->v6find, DNS_ADBFIND_INET6);
+
+ /*
+ * If there are no addresses, try the next element in the search
+ * path, if there are any more. Otherwise, fall through into
+ * the error handling code below.
+ */
+ if (client->gabn.naddrs == 0) {
+ do {
+ result = ns_lwsearchctx_next(&client->searchctx);
+ if (result == ISC_R_SUCCESS) {
+ cleanup_gabn(client);
+ result = start_find(client);
+ if (result == ISC_R_SUCCESS)
+ return;
+ }
+ } while (result == ISC_R_SUCCESS);
+ }
+
+ /*
+ * Render the packet.
+ */
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+
+ /*
+ * If there are no addresses, return failure.
+ */
+ if (client->gabn.naddrs != 0)
+ client->pkt.result = LWRES_R_SUCCESS;
+ else
+ client->pkt.result = LWRES_R_NOTFOUND;
+
+ sort_addresses(client);
+
+ lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
+ &client->pkt, &lwb);
+ if (lwres != LWRES_R_SUCCESS)
+ goto out;
+
+ r.base = lwb.base;
+ r.length = lwb.used;
+ client->sendbuf = r.base;
+ client->sendlength = r.length;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ NS_LWDCLIENT_SETSEND(client);
+
+ /*
+ * All done!
+ */
+ cleanup_gabn(client);
+
+ return;
+
+ out:
+ cleanup_gabn(client);
+
+ if (lwb.base != NULL)
+ lwres_context_freemem(client->clientmgr->lwctx,
+ lwb.base, lwb.length);
+
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
+
+/*
+ * Take the current real name, move it to an alias slot (if any are
+ * open) then put this new name in as the real name for the target.
+ *
+ * Return success if it can be rendered, otherwise failure. Note that
+ * not having enough alias slots open is NOT a failure.
+ */
+static isc_result_t
+add_alias(ns_lwdclient_t *client) {
+ isc_buffer_t b;
+ isc_result_t result;
+ isc_uint16_t naliases;
+
+ b = client->recv_buffer;
+
+ /*
+ * Render the new name to the buffer.
+ */
+ result = dns_name_totext(dns_fixedname_name(&client->target_name),
+ ISC_TRUE, &client->recv_buffer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Are there any open slots?
+ */
+ naliases = client->gabn.naliases;
+ if (naliases < LWRES_MAX_ALIASES) {
+ client->gabn.aliases[naliases] = client->gabn.realname;
+ client->gabn.aliaslen[naliases] = client->gabn.realnamelen;
+ client->gabn.naliases++;
+ }
+
+ /*
+ * Save this name away as the current real name.
+ */
+ client->gabn.realname = (char *)(b.base) + b.used;
+ client->gabn.realnamelen = client->recv_buffer.used - b.used;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+store_realname(ns_lwdclient_t *client) {
+ isc_buffer_t b;
+ isc_result_t result;
+ dns_name_t *tname;
+
+ b = client->recv_buffer;
+
+ tname = dns_fixedname_name(&client->target_name);
+ result = ns_lwsearchctx_current(&client->searchctx, tname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Render the new name to the buffer.
+ */
+ result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Save this name away as the current real name.
+ */
+ client->gabn.realname = (char *) b.base + b.used;
+ client->gabn.realnamelen = client->recv_buffer.used - b.used;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+process_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
+ ns_lwdclient_t *client = ev->ev_arg;
+ isc_eventtype_t evtype;
+ isc_boolean_t claimed;
+
+ ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
+
+ evtype = ev->ev_type;
+ isc_event_free(&ev);
+
+ /*
+ * No more info to be had? If so, we have all the good stuff
+ * right now, so we can render things.
+ */
+ claimed = ISC_FALSE;
+ if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
+ if (NEED_V4(client)) {
+ client->v4find = client->find;
+ claimed = ISC_TRUE;
+ }
+ if (NEED_V6(client)) {
+ client->v6find = client->find;
+ claimed = ISC_TRUE;
+ }
+ if (client->find != NULL) {
+ if (claimed)
+ client->find = NULL;
+ else
+ dns_adb_destroyfind(&client->find);
+
+ }
+ generate_reply(client);
+ return;
+ }
+
+ /*
+ * We probably don't need this find anymore. We're either going to
+ * reissue it, or an error occurred. Either way, we're done with
+ * it.
+ */
+ if ((client->find != client->v4find)
+ && (client->find != client->v6find)) {
+ dns_adb_destroyfind(&client->find);
+ } else {
+ client->find = NULL;
+ }
+
+ /*
+ * We have some new information we can gather. Run off and fetch
+ * it.
+ */
+ if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
+ restart_find(client);
+ return;
+ }
+
+ /*
+ * An error or other strangeness happened. Drop this query.
+ */
+ cleanup_gabn(client);
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
+
+static void
+restart_find(ns_lwdclient_t *client) {
+ unsigned int options;
+ isc_result_t result;
+ isc_boolean_t claimed;
+
+ ns_lwdclient_log(50, "starting find for client %p", client);
+
+ /*
+ * Issue a find for the name contained in the request. We won't
+ * set the bit that says "anything is good enough" -- we want it
+ * all.
+ */
+ options = 0;
+ options |= DNS_ADBFIND_WANTEVENT;
+ options |= DNS_ADBFIND_RETURNLAME;
+
+ /*
+ * Set the bits up here to mark that we want this address family
+ * and that we do not currently have a find pending. We will
+ * set that bit again below if it turns out we will get an event.
+ */
+ if (NEED_V4(client))
+ options |= DNS_ADBFIND_INET;
+ if (NEED_V6(client))
+ options |= DNS_ADBFIND_INET6;
+
+ find_again:
+ INSIST(client->find == NULL);
+ result = dns_adb_createfind(client->clientmgr->view->adb,
+ client->clientmgr->task,
+ process_gabn_finddone, client,
+ dns_fixedname_name(&client->target_name),
+ dns_rootname, 0, options, 0,
+ dns_fixedname_name(&client->target_name),
+ client->clientmgr->view->dstport,
+ &client->find);
+
+ /*
+ * Did we get an alias? If so, save it and re-issue the query.
+ */
+ if (result == DNS_R_ALIAS) {
+ ns_lwdclient_log(50, "found alias, restarting query");
+ dns_adb_destroyfind(&client->find);
+ cleanup_gabn(client);
+ result = add_alias(client);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_log(50,
+ "out of buffer space adding alias");
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+ return;
+ }
+ goto find_again;
+ }
+
+ ns_lwdclient_log(50, "find returned %d (%s)", result,
+ isc_result_totext(result));
+
+ /*
+ * Did we get an error?
+ */
+ if (result != ISC_R_SUCCESS) {
+ if (client->find != NULL)
+ dns_adb_destroyfind(&client->find);
+ cleanup_gabn(client);
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+ return;
+ }
+
+ claimed = ISC_FALSE;
+
+ /*
+ * Did we get our answer to V4 addresses?
+ */
+ if (NEED_V4(client)
+ && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
+ ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
+ client, client->find);
+ claimed = ISC_TRUE;
+ client->v4find = client->find;
+ }
+
+ /*
+ * Did we get our answer to V6 addresses?
+ */
+ if (NEED_V6(client)
+ && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
+ ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
+ client, client->find);
+ claimed = ISC_TRUE;
+ client->v6find = client->find;
+ }
+
+ /*
+ * If we're going to get an event, set our internal pending flag
+ * and return. When we get an event back we'll do the right
+ * thing, basically by calling this function again, perhaps with a
+ * new target name.
+ *
+ * If we have both v4 and v6, and we are still getting an event,
+ * we have a programming error, so die hard.
+ */
+ if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
+ ns_lwdclient_log(50, "event will be sent");
+ INSIST(client->v4find == NULL || client->v6find == NULL);
+ return;
+ }
+ ns_lwdclient_log(50, "no event will be sent");
+ if (claimed)
+ client->find = NULL;
+ else
+ dns_adb_destroyfind(&client->find);
+
+ /*
+ * We seem to have everything we asked for, or at least we are
+ * able to respond with things we've learned.
+ */
+
+ generate_reply(client);
+}
+
+static isc_result_t
+start_find(ns_lwdclient_t *client) {
+ isc_result_t result;
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ init_gabn(client);
+
+ result = store_realname(client);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ restart_find(client);
+ return (ISC_R_SUCCESS);
+
+}
+
+static void
+init_gabn(ns_lwdclient_t *client) {
+ int i;
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ for (i = 0; i < LWRES_MAX_ALIASES; i++) {
+ client->aliases[i] = NULL;
+ client->aliaslen[i] = 0;
+ }
+ for (i = 0; i < LWRES_MAX_ADDRS; i++) {
+ client->addrs[i].family = 0;
+ client->addrs[i].length = 0;
+ memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
+ LWRES_LINK_INIT(&client->addrs[i], link);
+ }
+
+ client->gabn.naliases = 0;
+ client->gabn.naddrs = 0;
+ client->gabn.realname = NULL;
+ client->gabn.aliases = client->aliases;
+ client->gabn.realnamelen = 0;
+ client->gabn.aliaslen = client->aliaslen;
+ LWRES_LIST_INIT(client->gabn.addrs);
+ client->gabn.base = NULL;
+ client->gabn.baselen = 0;
+
+ /*
+ * Set up the internal buffer to point to the receive region.
+ */
+ isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
+}
+
+/*
+ * When we are called, we can be assured that:
+ *
+ * client->sockaddr contains the address we need to reply to,
+ *
+ * client->pkt contains the packet header data,
+ *
+ * the packet "checks out" overall -- any MD5 hashes or crypto
+ * bits have been verified,
+ *
+ * "b" points to the remaining data after the packet header
+ * was parsed off.
+ *
+ * We are in a the RECVDONE state.
+ *
+ * From this state we will enter the SEND state if we happen to have
+ * everything we need or we need to return an error packet, or to the
+ * FINDWAIT state if we need to look things up.
+ */
+void
+ns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) {
+ isc_result_t result;
+ lwres_gabnrequest_t *req;
+ ns_lwdclientmgr_t *cm;
+ isc_buffer_t namebuf;
+
+ REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
+
+ cm = client->clientmgr;
+ req = NULL;
+
+ result = lwres_gabnrequest_parse(client->clientmgr->lwctx,
+ b, &client->pkt, &req);
+ if (result != LWRES_R_SUCCESS)
+ goto out;
+ if (req->name == NULL)
+ goto out;
+
+ isc_buffer_init(&namebuf, req->name, req->namelen);
+ isc_buffer_add(&namebuf, req->namelen);
+
+ dns_fixedname_init(&client->target_name);
+ dns_fixedname_init(&client->query_name);
+ result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
+ &namebuf, NULL, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ ns_lwsearchctx_init(&client->searchctx,
+ cm->listener->manager->search,
+ dns_fixedname_name(&client->query_name),
+ cm->listener->manager->ndots);
+ ns_lwsearchctx_first(&client->searchctx);
+
+ client->find_wanted = req->addrtypes;
+ ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
+ client, client->find_wanted);
+
+ /*
+ * We no longer need to keep this around.
+ */
+ lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
+
+ /*
+ * Start the find.
+ */
+ result = start_find(client);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ return;
+
+ /*
+ * We're screwed. Return an error packet to our caller.
+ */
+ out:
+ if (req != NULL)
+ lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
+
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
diff --git a/bin/named/lwdgnba.c b/bin/named/lwdgnba.c
new file mode 100644
index 0000000..a500d27
--- /dev/null
+++ b/bin/named/lwdgnba.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2002 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: lwdgnba.c,v 1.16.18.2 2005/04/29 00:15:24 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/socket.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/byaddr.h>
+#include <dns/result.h>
+
+#include <named/types.h>
+#include <named/lwdclient.h>
+
+static void start_byaddr(ns_lwdclient_t *);
+
+static void
+byaddr_done(isc_task_t *task, isc_event_t *event) {
+ ns_lwdclient_t *client;
+ ns_lwdclientmgr_t *cm;
+ dns_byaddrevent_t *bevent;
+ int lwres;
+ lwres_buffer_t lwb;
+ dns_name_t *name;
+ isc_result_t result;
+ lwres_result_t lwresult;
+ isc_region_t r;
+ isc_buffer_t b;
+ lwres_gnbaresponse_t *gnba;
+ isc_uint16_t naliases;
+
+ UNUSED(task);
+
+ lwb.base = NULL;
+ client = event->ev_arg;
+ cm = client->clientmgr;
+ INSIST(client->byaddr == (dns_byaddr_t *)event->ev_sender);
+
+ bevent = (dns_byaddrevent_t *)event;
+ gnba = &client->gnba;
+
+ ns_lwdclient_log(50, "byaddr event result = %s",
+ isc_result_totext(bevent->result));
+
+ result = bevent->result;
+ if (result != ISC_R_SUCCESS) {
+ dns_byaddr_destroy(&client->byaddr);
+ isc_event_free(&event);
+ bevent = NULL;
+
+ if (client->na.family != AF_INET6 ||
+ (client->options & DNS_BYADDROPT_IPV6INT) != 0) {
+ if (result == DNS_R_NCACHENXDOMAIN ||
+ result == DNS_R_NCACHENXRRSET ||
+ result == DNS_R_NXDOMAIN ||
+ result == DNS_R_NXRRSET)
+ lwresult = LWRES_R_NOTFOUND;
+ else
+ lwresult = LWRES_R_FAILURE;
+ ns_lwdclient_errorpktsend(client, lwresult);
+ return;
+ }
+
+ /*
+ * Fall back to ip6.int reverse if the default ip6.arpa
+ * fails.
+ */
+ client->options |= DNS_BYADDROPT_IPV6INT;
+
+ start_byaddr(client);
+ return;
+ }
+
+ for (name = ISC_LIST_HEAD(bevent->names);
+ name != NULL;
+ name = ISC_LIST_NEXT(name, link))
+ {
+ b = client->recv_buffer;
+
+ result = dns_name_totext(name, ISC_TRUE, &client->recv_buffer);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ ns_lwdclient_log(50, "found name '%.*s'",
+ (int)(client->recv_buffer.used - b.used),
+ (char *)(b.base) + b.used);
+ if (gnba->realname == NULL) {
+ gnba->realname = (char *)(b.base) + b.used;
+ gnba->realnamelen = client->recv_buffer.used - b.used;
+ } else {
+ naliases = gnba->naliases;
+ if (naliases >= LWRES_MAX_ALIASES)
+ break;
+ gnba->aliases[naliases] = (char *)(b.base) + b.used;
+ gnba->aliaslen[naliases] =
+ client->recv_buffer.used - b.used;
+ gnba->naliases++;
+ }
+ }
+
+ dns_byaddr_destroy(&client->byaddr);
+ isc_event_free(&event);
+
+ /*
+ * Render the packet.
+ */
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+ client->pkt.result = LWRES_R_SUCCESS;
+
+ lwres = lwres_gnbaresponse_render(cm->lwctx,
+ gnba, &client->pkt, &lwb);
+ if (lwres != LWRES_R_SUCCESS)
+ goto out;
+
+ r.base = lwb.base;
+ r.length = lwb.used;
+ client->sendbuf = r.base;
+ client->sendlength = r.length;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ NS_LWDCLIENT_SETSEND(client);
+
+ return;
+
+ out:
+ if (client->byaddr != NULL)
+ dns_byaddr_destroy(&client->byaddr);
+ if (lwb.base != NULL)
+ lwres_context_freemem(cm->lwctx,
+ lwb.base, lwb.length);
+
+ if (event != NULL)
+ isc_event_free(&event);
+}
+
+static void
+start_byaddr(ns_lwdclient_t *client) {
+ isc_result_t result;
+ ns_lwdclientmgr_t *cm;
+
+ cm = client->clientmgr;
+
+ INSIST(client->byaddr == NULL);
+
+ result = dns_byaddr_create(cm->mctx, &client->na, cm->view,
+ client->options, cm->task, byaddr_done,
+ client, &client->byaddr);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+ return;
+ }
+}
+
+static void
+init_gnba(ns_lwdclient_t *client) {
+ int i;
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ for (i = 0; i < LWRES_MAX_ALIASES; i++) {
+ client->aliases[i] = NULL;
+ client->aliaslen[i] = 0;
+ }
+ for (i = 0; i < LWRES_MAX_ADDRS; i++) {
+ client->addrs[i].family = 0;
+ client->addrs[i].length = 0;
+ memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
+ LWRES_LINK_INIT(&client->addrs[i], link);
+ }
+
+ client->gnba.naliases = 0;
+ client->gnba.realname = NULL;
+ client->gnba.aliases = client->aliases;
+ client->gnba.realnamelen = 0;
+ client->gnba.aliaslen = client->aliaslen;
+ client->gnba.base = NULL;
+ client->gnba.baselen = 0;
+ isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
+}
+
+void
+ns_lwdclient_processgnba(ns_lwdclient_t *client, lwres_buffer_t *b) {
+ lwres_gnbarequest_t *req;
+ isc_result_t result;
+ isc_sockaddr_t sa;
+ ns_lwdclientmgr_t *cm;
+
+ REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
+ INSIST(client->byaddr == NULL);
+
+ cm = client->clientmgr;
+ req = NULL;
+
+ result = lwres_gnbarequest_parse(cm->lwctx,
+ b, &client->pkt, &req);
+ if (result != LWRES_R_SUCCESS)
+ goto out;
+ if (req->addr.address == NULL)
+ goto out;
+
+ client->options = 0;
+ if (req->addr.family == LWRES_ADDRTYPE_V4) {
+ client->na.family = AF_INET;
+ if (req->addr.length != 4)
+ goto out;
+ memcpy(&client->na.type.in, req->addr.address, 4);
+ } else if (req->addr.family == LWRES_ADDRTYPE_V6) {
+ client->na.family = AF_INET6;
+ if (req->addr.length != 16)
+ goto out;
+ memcpy(&client->na.type.in6, req->addr.address, 16);
+ } else {
+ goto out;
+ }
+ isc_sockaddr_fromnetaddr(&sa, &client->na, 53);
+
+ ns_lwdclient_log(50, "client %p looking for addrtype %08x",
+ client, req->addr.family);
+
+ /*
+ * We no longer need to keep this around.
+ */
+ lwres_gnbarequest_free(cm->lwctx, &req);
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ init_gnba(client);
+ client->options = 0;
+
+ /*
+ * Start the find.
+ */
+ start_byaddr(client);
+
+ return;
+
+ /*
+ * We're screwed. Return an error packet to our caller.
+ */
+ out:
+ if (req != NULL)
+ lwres_gnbarequest_free(cm->lwctx, &req);
+
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
diff --git a/bin/named/lwdgrbn.c b/bin/named/lwdgrbn.c
new file mode 100644
index 0000000..c1b2b1e
--- /dev/null
+++ b/bin/named/lwdgrbn.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 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: lwdgrbn.c,v 1.13.18.5 2006/12/07 23:57:58 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/socket.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/lookup.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+#include <named/types.h>
+#include <named/lwdclient.h>
+#include <named/lwresd.h>
+#include <named/lwsearch.h>
+
+static void start_lookup(ns_lwdclient_t *);
+
+static isc_result_t
+fill_array(int *pos, dns_rdataset_t *rdataset,
+ int size, unsigned char **rdatas, lwres_uint16_t *rdatalen)
+{
+ dns_rdata_t rdata;
+ isc_result_t result;
+ isc_region_t r;
+
+ UNUSED(size);
+
+ dns_rdata_init(&rdata);
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ INSIST(*pos < size);
+ dns_rdataset_current(rdataset, &rdata);
+ dns_rdata_toregion(&rdata, &r);
+ rdatas[*pos] = r.base;
+ rdatalen[*pos] = r.length;
+ dns_rdata_reset(&rdata);
+ (*pos)++;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+static isc_result_t
+iterate_node(lwres_grbnresponse_t *grbn, dns_db_t *db, dns_dbnode_t *node,
+ isc_mem_t *mctx)
+{
+ int used = 0, count;
+ int size = 8, oldsize = 0;
+ unsigned char **rdatas = NULL, **oldrdatas = NULL, **newrdatas = NULL;
+ lwres_uint16_t *lens = NULL, *oldlens = NULL, *newlens = NULL;
+ dns_rdatasetiter_t *iter = NULL;
+ dns_rdataset_t set;
+ dns_ttl_t ttl = ISC_INT32_MAX;
+ lwres_uint32_t flags = LWRDATA_VALIDATED;
+ isc_result_t result = ISC_R_NOMEMORY;
+
+ result = dns_db_allrdatasets(db, node, NULL, 0, &iter);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ rdatas = isc_mem_get(mctx, size * sizeof(*rdatas));
+ if (rdatas == NULL)
+ goto out;
+ lens = isc_mem_get(mctx, size * sizeof(*lens));
+ if (lens == NULL)
+ goto out;
+
+ for (result = dns_rdatasetiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iter))
+ {
+ result = ISC_R_NOMEMORY;
+ dns_rdataset_init(&set);
+ dns_rdatasetiter_current(iter, &set);
+
+ if (set.type != dns_rdatatype_rrsig) {
+ dns_rdataset_disassociate(&set);
+ continue;
+ }
+
+ count = dns_rdataset_count(&set);
+ if (used + count > size) {
+ /* copy & reallocate */
+ oldsize = size;
+ oldrdatas = rdatas;
+ oldlens = lens;
+ rdatas = NULL;
+ lens = NULL;
+
+ size *= 2;
+
+ rdatas = isc_mem_get(mctx, size * sizeof(*rdatas));
+ if (rdatas == NULL)
+ goto out;
+ lens = isc_mem_get(mctx, size * sizeof(*lens));
+ if (lens == NULL)
+ goto out;
+ memcpy(rdatas, oldrdatas, used * sizeof(*rdatas));
+ memcpy(lens, oldlens, used * sizeof(*lens));
+ isc_mem_put(mctx, oldrdatas,
+ oldsize * sizeof(*oldrdatas));
+ isc_mem_put(mctx, oldlens, oldsize * sizeof(*oldlens));
+ oldrdatas = NULL;
+ oldlens = NULL;
+ }
+ if (set.ttl < ttl)
+ ttl = set.ttl;
+ if (set.trust != dns_trust_secure)
+ flags &= (~LWRDATA_VALIDATED);
+ result = fill_array(&used, &set, size, rdatas, lens);
+ dns_rdataset_disassociate(&set);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ dns_rdatasetiter_destroy(&iter);
+
+ /*
+ * If necessary, shrink and copy the arrays.
+ */
+ if (size != used) {
+ result = ISC_R_NOMEMORY;
+ newrdatas = isc_mem_get(mctx, used * sizeof(*rdatas));
+ if (newrdatas == NULL)
+ goto out;
+ newlens = isc_mem_get(mctx, used * sizeof(*lens));
+ if (newlens == NULL)
+ goto out;
+ memcpy(newrdatas, rdatas, used * sizeof(*rdatas));
+ memcpy(newlens, lens, used * sizeof(*lens));
+ isc_mem_put(mctx, rdatas, size * sizeof(*rdatas));
+ isc_mem_put(mctx, lens, size * sizeof(*lens));
+ grbn->rdatas = newrdatas;
+ grbn->rdatalen = newlens;
+ } else {
+ grbn->rdatas = rdatas;
+ grbn->rdatalen = lens;
+ }
+ grbn->nrdatas = used;
+ grbn->ttl = ttl;
+ grbn->flags = flags;
+ return (ISC_R_SUCCESS);
+
+ out:
+ dns_rdatasetiter_destroy(&iter);
+ if (rdatas != NULL)
+ isc_mem_put(mctx, rdatas, size * sizeof(*rdatas));
+ if (lens != NULL)
+ isc_mem_put(mctx, lens, size * sizeof(*lens));
+ if (oldrdatas != NULL)
+ isc_mem_put(mctx, oldrdatas, oldsize * sizeof(*oldrdatas));
+ if (oldlens != NULL)
+ isc_mem_put(mctx, oldlens, oldsize * sizeof(*oldlens));
+ if (newrdatas != NULL)
+ isc_mem_put(mctx, newrdatas, used * sizeof(*oldrdatas));
+ return (result);
+}
+
+static void
+lookup_done(isc_task_t *task, isc_event_t *event) {
+ ns_lwdclient_t *client;
+ ns_lwdclientmgr_t *cm;
+ dns_lookupevent_t *levent;
+ lwres_buffer_t lwb;
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+ dns_rdataset_t *sigrdataset;
+ isc_result_t result;
+ lwres_result_t lwresult;
+ isc_region_t r;
+ isc_buffer_t b;
+ lwres_grbnresponse_t *grbn;
+ int i;
+
+ UNUSED(task);
+
+ lwb.base = NULL;
+ client = event->ev_arg;
+ cm = client->clientmgr;
+ INSIST(client->lookup == (dns_lookup_t *)event->ev_sender);
+
+ levent = (dns_lookupevent_t *)event;
+ grbn = &client->grbn;
+
+ ns_lwdclient_log(50, "lookup event result = %s",
+ isc_result_totext(levent->result));
+
+ result = levent->result;
+ if (result != ISC_R_SUCCESS) {
+ dns_lookup_destroy(&client->lookup);
+ isc_event_free(&event);
+ levent = NULL;
+
+ switch (result) {
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NCACHENXDOMAIN:
+ result = ns_lwsearchctx_next(&client->searchctx);
+ if (result != ISC_R_SUCCESS)
+ lwresult = LWRES_R_NOTFOUND;
+ else {
+ start_lookup(client);
+ return;
+ }
+ break;
+ case DNS_R_NXRRSET:
+ case DNS_R_NCACHENXRRSET:
+ lwresult = LWRES_R_TYPENOTFOUND;
+ break;
+ default:
+ lwresult = LWRES_R_FAILURE;
+ }
+ ns_lwdclient_errorpktsend(client, lwresult);
+ return;
+ }
+
+ name = levent->name;
+ b = client->recv_buffer;
+
+ grbn->flags = 0;
+
+ grbn->nrdatas = 0;
+ grbn->rdatas = NULL;
+ grbn->rdatalen = NULL;
+
+ grbn->nsigs = 0;
+ grbn->sigs = NULL;
+ grbn->siglen = NULL;
+
+ result = dns_name_totext(name, ISC_TRUE, &client->recv_buffer);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ grbn->realname = (char *)isc_buffer_used(&b);
+ grbn->realnamelen = isc_buffer_usedlength(&client->recv_buffer) -
+ isc_buffer_usedlength(&b);
+ ns_lwdclient_log(50, "found name '%.*s'", grbn->realnamelen,
+ grbn->realname);
+
+ grbn->rdclass = cm->view->rdclass;
+ grbn->rdtype = client->rdtype;
+
+ rdataset = levent->rdataset;
+ if (rdataset != NULL) {
+ /* The normal case */
+ grbn->nrdatas = dns_rdataset_count(rdataset);
+ grbn->rdatas = isc_mem_get(cm->mctx, grbn->nrdatas *
+ sizeof(unsigned char *));
+ if (grbn->rdatas == NULL)
+ goto out;
+ grbn->rdatalen = isc_mem_get(cm->mctx, grbn->nrdatas *
+ sizeof(lwres_uint16_t));
+ if (grbn->rdatalen == NULL)
+ goto out;
+
+ i = 0;
+ result = fill_array(&i, rdataset, grbn->nrdatas, grbn->rdatas,
+ grbn->rdatalen);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ INSIST(i == grbn->nrdatas);
+ grbn->ttl = rdataset->ttl;
+ if (rdataset->trust == dns_trust_secure)
+ grbn->flags |= LWRDATA_VALIDATED;
+ } else {
+ /* The SIG query case */
+ result = iterate_node(grbn, levent->db, levent->node,
+ cm->mctx);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ }
+ ns_lwdclient_log(50, "filled in %d rdata%s", grbn->nrdatas,
+ (grbn->nrdatas == 1) ? "" : "s");
+
+ sigrdataset = levent->sigrdataset;
+ if (sigrdataset != NULL) {
+ grbn->nsigs = dns_rdataset_count(sigrdataset);
+ grbn->sigs = isc_mem_get(cm->mctx, grbn->nsigs *
+ sizeof(unsigned char *));
+ if (grbn->sigs == NULL)
+ goto out;
+ grbn->siglen = isc_mem_get(cm->mctx, grbn->nsigs *
+ sizeof(lwres_uint16_t));
+ if (grbn->siglen == NULL)
+ goto out;
+
+ i = 0;
+ result = fill_array(&i, sigrdataset, grbn->nsigs, grbn->sigs,
+ grbn->siglen);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ INSIST(i == grbn->nsigs);
+ ns_lwdclient_log(50, "filled in %d signature%s", grbn->nsigs,
+ (grbn->nsigs == 1) ? "" : "s");
+ }
+
+ dns_lookup_destroy(&client->lookup);
+ isc_event_free(&event);
+
+ /*
+ * Render the packet.
+ */
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+ client->pkt.result = LWRES_R_SUCCESS;
+
+ lwresult = lwres_grbnresponse_render(cm->lwctx,
+ grbn, &client->pkt, &lwb);
+ if (lwresult != LWRES_R_SUCCESS)
+ goto out;
+
+ isc_mem_put(cm->mctx, grbn->rdatas,
+ grbn->nrdatas * sizeof(unsigned char *));
+ isc_mem_put(cm->mctx, grbn->rdatalen,
+ grbn->nrdatas * sizeof(lwres_uint16_t));
+
+ if (grbn->sigs != NULL)
+ isc_mem_put(cm->mctx, grbn->sigs,
+ grbn->nsigs * sizeof(unsigned char *));
+ if (grbn->siglen != NULL)
+ isc_mem_put(cm->mctx, grbn->siglen,
+ grbn->nsigs * sizeof(lwres_uint16_t));
+
+ r.base = lwb.base;
+ r.length = lwb.used;
+ client->sendbuf = r.base;
+ client->sendlength = r.length;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS)
+ goto out2;
+
+ NS_LWDCLIENT_SETSEND(client);
+
+ return;
+
+ out:
+ if (grbn->rdatas != NULL)
+ isc_mem_put(cm->mctx, grbn->rdatas,
+ grbn->nrdatas * sizeof(unsigned char *));
+ if (grbn->rdatalen != NULL)
+ isc_mem_put(cm->mctx, grbn->rdatalen,
+ grbn->nrdatas * sizeof(lwres_uint16_t));
+
+ if (grbn->sigs != NULL)
+ isc_mem_put(cm->mctx, grbn->sigs,
+ grbn->nsigs * sizeof(unsigned char *));
+ if (grbn->siglen != NULL)
+ isc_mem_put(cm->mctx, grbn->siglen,
+ grbn->nsigs * sizeof(lwres_uint16_t));
+ out2:
+ if (client->lookup != NULL)
+ dns_lookup_destroy(&client->lookup);
+ if (lwb.base != NULL)
+ lwres_context_freemem(cm->lwctx, lwb.base, lwb.length);
+
+ if (event != NULL)
+ isc_event_free(&event);
+
+ ns_lwdclient_log(50, "error constructing getrrsetbyname response");
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
+
+static void
+start_lookup(ns_lwdclient_t *client) {
+ isc_result_t result;
+ ns_lwdclientmgr_t *cm;
+ dns_fixedname_t absname;
+
+ cm = client->clientmgr;
+
+ INSIST(client->lookup == NULL);
+
+ dns_fixedname_init(&absname);
+ result = ns_lwsearchctx_current(&client->searchctx,
+ dns_fixedname_name(&absname));
+ /*
+ * This will return failure if relative name + suffix is too long.
+ * In this case, just go on to the next entry in the search path.
+ */
+ if (result != ISC_R_SUCCESS)
+ start_lookup(client);
+
+ result = dns_lookup_create(cm->mctx,
+ dns_fixedname_name(&absname),
+ client->rdtype, cm->view,
+ client->options, cm->task, lookup_done,
+ client, &client->lookup);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+ return;
+ }
+}
+
+static void
+init_grbn(ns_lwdclient_t *client) {
+ client->grbn.rdclass = 0;
+ client->grbn.rdtype = 0;
+ client->grbn.ttl = 0;
+ client->grbn.nrdatas = 0;
+ client->grbn.realname = NULL;
+ client->grbn.realnamelen = 0;
+ client->grbn.rdatas = 0;
+ client->grbn.rdatalen = 0;
+ client->grbn.base = NULL;
+ client->grbn.baselen = 0;
+ isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
+}
+
+void
+ns_lwdclient_processgrbn(ns_lwdclient_t *client, lwres_buffer_t *b) {
+ lwres_grbnrequest_t *req;
+ isc_result_t result;
+ ns_lwdclientmgr_t *cm;
+ isc_buffer_t namebuf;
+
+ REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
+ INSIST(client->byaddr == NULL);
+
+ cm = client->clientmgr;
+ req = NULL;
+
+ result = lwres_grbnrequest_parse(cm->lwctx,
+ b, &client->pkt, &req);
+ if (result != LWRES_R_SUCCESS)
+ goto out;
+ if (req->name == NULL)
+ goto out;
+
+ client->options = 0;
+ if (req->rdclass != cm->view->rdclass)
+ goto out;
+
+ if (req->rdclass == dns_rdataclass_any ||
+ req->rdtype == dns_rdatatype_any)
+ goto out;
+
+ client->rdtype = req->rdtype;
+
+ isc_buffer_init(&namebuf, req->name, req->namelen);
+ isc_buffer_add(&namebuf, req->namelen);
+
+ dns_fixedname_init(&client->query_name);
+ result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
+ &namebuf, NULL, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ ns_lwsearchctx_init(&client->searchctx,
+ cm->listener->manager->search,
+ dns_fixedname_name(&client->query_name),
+ cm->listener->manager->ndots);
+ ns_lwsearchctx_first(&client->searchctx);
+
+ ns_lwdclient_log(50, "client %p looking for type %d",
+ client, client->rdtype);
+
+ /*
+ * We no longer need to keep this around.
+ */
+ lwres_grbnrequest_free(cm->lwctx, &req);
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ init_grbn(client);
+
+ /*
+ * Start the find.
+ */
+ start_lookup(client);
+
+ return;
+
+ /*
+ * We're screwed. Return an error packet to our caller.
+ */
+ out:
+ if (req != NULL)
+ lwres_grbnrequest_free(cm->lwctx, &req);
+
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
diff --git a/bin/named/lwdnoop.c b/bin/named/lwdnoop.c
new file mode 100644
index 0000000..fa591b4
--- /dev/null
+++ b/bin/named/lwdnoop.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwdnoop.c,v 1.7.18.2 2005/04/29 00:15:25 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/socket.h>
+#include <isc/util.h>
+
+#include <named/types.h>
+#include <named/lwdclient.h>
+
+void
+ns_lwdclient_processnoop(ns_lwdclient_t *client, lwres_buffer_t *b) {
+ lwres_nooprequest_t *req;
+ lwres_noopresponse_t resp;
+ isc_result_t result;
+ lwres_result_t lwres;
+ isc_region_t r;
+ lwres_buffer_t lwb;
+
+ REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
+ INSIST(client->byaddr == NULL);
+
+ req = NULL;
+
+ result = lwres_nooprequest_parse(client->clientmgr->lwctx,
+ b, &client->pkt, &req);
+ if (result != LWRES_R_SUCCESS)
+ goto out;
+
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+ client->pkt.result = LWRES_R_SUCCESS;
+
+ resp.datalength = req->datalength;
+ resp.data = req->data;
+
+ lwres = lwres_noopresponse_render(client->clientmgr->lwctx, &resp,
+ &client->pkt, &lwb);
+ if (lwres != LWRES_R_SUCCESS)
+ goto out;
+
+ r.base = lwb.base;
+ r.length = lwb.used;
+ client->sendbuf = r.base;
+ client->sendlength = r.length;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ /*
+ * We can now destroy request.
+ */
+ lwres_nooprequest_free(client->clientmgr->lwctx, &req);
+
+ NS_LWDCLIENT_SETSEND(client);
+
+ return;
+
+ out:
+ if (req != NULL)
+ lwres_nooprequest_free(client->clientmgr->lwctx, &req);
+
+ if (lwb.base != NULL)
+ lwres_context_freemem(client->clientmgr->lwctx,
+ lwb.base, lwb.length);
+
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
diff --git a/bin/named/lwresd.8 b/bin/named/lwresd.8
new file mode 100644
index 0000000..825645a
--- /dev/null
+++ b/bin/named/lwresd.8
@@ -0,0 +1,223 @@
+.\" Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (C) 2000, 2001 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: lwresd.8,v 1.15.18.12 2007/05/16 06:11:27 marka Exp $
+.\"
+.hy 0
+.ad l
+.\" Title: lwresd
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.71.1 <http://docbook.sf.net/>
+.\" Date: June 30, 2000
+.\" Manual: BIND9
+.\" Source: BIND9
+.\"
+.TH "LWRESD" "8" "June 30, 2000" "BIND9" "BIND9"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+lwresd \- lightweight resolver daemon
+.SH "SYNOPSIS"
+.HP 7
+\fBlwresd\fR [\fB\-c\ \fR\fB\fIconfig\-file\fR\fR] [\fB\-C\ \fR\fB\fIconfig\-file\fR\fR] [\fB\-d\ \fR\fB\fIdebug\-level\fR\fR] [\fB\-f\fR] [\fB\-g\fR] [\fB\-i\ \fR\fB\fIpid\-file\fR\fR] [\fB\-m\ \fR\fB\fIflag\fR\fR] [\fB\-n\ \fR\fB\fI#cpus\fR\fR] [\fB\-P\ \fR\fB\fIport\fR\fR] [\fB\-p\ \fR\fB\fIport\fR\fR] [\fB\-s\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-4\fR] [\fB\-6\fR]
+.SH "DESCRIPTION"
+.PP
+\fBlwresd\fR
+is the daemon providing name lookup services to clients that use the BIND 9 lightweight resolver library. It is essentially a stripped\-down, caching\-only name server that answers queries using the BIND 9 lightweight resolver protocol rather than the DNS protocol.
+.PP
+\fBlwresd\fR
+listens for resolver queries on a UDP port on the IPv4 loopback interface, 127.0.0.1. This means that
+\fBlwresd\fR
+can only be used by processes running on the local machine. By default UDP port number 921 is used for lightweight resolver requests and responses.
+.PP
+Incoming lightweight resolver requests are decoded by the server which then resolves them using the DNS protocol. When the DNS lookup completes,
+\fBlwresd\fR
+encodes the answers in the lightweight resolver format and returns them to the client that made the request.
+.PP
+If
+\fI/etc/resolv.conf\fR
+contains any
+\fBnameserver\fR
+entries,
+\fBlwresd\fR
+sends recursive DNS queries to those servers. This is similar to the use of forwarders in a caching name server. If no
+\fBnameserver\fR
+entries are present, or if forwarding fails,
+\fBlwresd\fR
+resolves the queries autonomously starting at the root name servers, using a built\-in list of root server hints.
+.SH "OPTIONS"
+.PP
+\-4
+.RS 4
+Use IPv4 only even if the host machine is capable of IPv6.
+\fB\-4\fR
+and
+\fB\-6\fR
+are mutually exclusive.
+.RE
+.PP
+\-6
+.RS 4
+Use IPv6 only even if the host machine is capable of IPv4.
+\fB\-4\fR
+and
+\fB\-6\fR
+are mutually exclusive.
+.RE
+.PP
+\-c \fIconfig\-file\fR
+.RS 4
+Use
+\fIconfig\-file\fR
+as the configuration file instead of the default,
+\fI/etc/lwresd.conf\fR.
+<term>\-c</term>
+can not be used with
+<term>\-C</term>.
+.RE
+.PP
+\-C \fIconfig\-file\fR
+.RS 4
+Use
+\fIconfig\-file\fR
+as the configuration file instead of the default,
+\fI/etc/resolv.conf\fR.
+<term>\-C</term>
+can not be used with
+<term>\-c</term>.
+.RE
+.PP
+\-d \fIdebug\-level\fR
+.RS 4
+Set the daemon's debug level to
+\fIdebug\-level\fR. Debugging traces from
+\fBlwresd\fR
+become more verbose as the debug level increases.
+.RE
+.PP
+\-f
+.RS 4
+Run the server in the foreground (i.e. do not daemonize).
+.RE
+.PP
+\-g
+.RS 4
+Run the server in the foreground and force all logging to
+\fIstderr\fR.
+.RE
+.PP
+\-i \fIpid\-file\fR
+.RS 4
+Use
+\fIpid\-file\fR
+as the PID file instead of the default,
+\fI/var/run/lwresd.pid\fR.
+.RE
+.PP
+\-m \fIflag\fR
+.RS 4
+Turn on memory usage debugging flags. Possible flags are
+\fIusage\fR,
+\fItrace\fR,
+\fIrecord\fR,
+\fIsize\fR, and
+\fImctx\fR. These correspond to the ISC_MEM_DEBUGXXXX flags described in
+\fI<isc/mem.h>\fR.
+.RE
+.PP
+\-n \fI#cpus\fR
+.RS 4
+Create
+\fI#cpus\fR
+worker threads to take advantage of multiple CPUs. If not specified,
+\fBlwresd\fR
+will try to determine the number of CPUs present and create one thread per CPU. If it is unable to determine the number of CPUs, a single worker thread will be created.
+.RE
+.PP
+\-P \fIport\fR
+.RS 4
+Listen for lightweight resolver queries on port
+\fIport\fR. If not specified, the default is port 921.
+.RE
+.PP
+\-p \fIport\fR
+.RS 4
+Send DNS lookups to port
+\fIport\fR. If not specified, the default is port 53. This provides a way of testing the lightweight resolver daemon with a name server that listens for queries on a non\-standard port number.
+.RE
+.PP
+\-s
+.RS 4
+Write memory usage statistics to
+\fIstdout\fR
+on exit.
+.RS
+.B "Note:"
+This option is mainly of interest to BIND 9 developers and may be removed or changed in a future release.
+.RE
+.RE
+.PP
+\-t \fIdirectory\fR
+.RS 4
+Chroot to
+\fIdirectory\fR
+after processing the command line arguments, but before reading the configuration file.
+.RS
+.B "Warning:"
+This option should be used in conjunction with the
+\fB\-u\fR
+option, as chrooting a process running as root doesn't enhance security on most systems; the way
+\fBchroot(2)\fR
+is defined allows a process with root privileges to escape a chroot jail.
+.RE
+.RE
+.PP
+\-u \fIuser\fR
+.RS 4
+Setuid to
+\fIuser\fR
+after completing privileged operations, such as creating sockets that listen on privileged ports.
+.RE
+.PP
+\-v
+.RS 4
+Report the version number and exit.
+.RE
+.SH "FILES"
+.PP
+\fI/etc/resolv.conf\fR
+.RS 4
+The default configuration file.
+.RE
+.PP
+\fI/var/run/lwresd.pid\fR
+.RS 4
+The default process\-id file.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBnamed\fR(8),
+\fBlwres\fR(3),
+\fBresolver\fR(5).
+.SH "AUTHOR"
+.PP
+Internet Systems Consortium
+.SH "COPYRIGHT"
+Copyright \(co 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+.br
+Copyright \(co 2000, 2001 Internet Software Consortium.
+.br
diff --git a/bin/named/lwresd.c b/bin/named/lwresd.c
new file mode 100644
index 0000000..a1073fa
--- /dev/null
+++ b/bin/named/lwresd.c
@@ -0,0 +1,869 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-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: lwresd.c,v 1.46.18.7 2006/03/02 00:37:21 marka Exp $ */
+
+/*! \file
+ * \brief
+ * Main program for the Lightweight Resolver Daemon.
+ *
+ * To paraphrase the old saying about X11, "It's not a lightweight deamon
+ * for resolvers, it's a deamon for lightweight resolvers".
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/list.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/socket.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <isccfg/namedconf.h>
+
+#include <dns/log.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+#include <named/config.h>
+#include <named/globals.h>
+#include <named/log.h>
+#include <named/lwaddr.h>
+#include <named/lwresd.h>
+#include <named/lwdclient.h>
+#include <named/lwsearch.h>
+#include <named/server.h>
+
+#define LWRESD_MAGIC ISC_MAGIC('L', 'W', 'R', 'D')
+#define VALID_LWRESD(l) ISC_MAGIC_VALID(l, LWRESD_MAGIC)
+
+#define LWRESLISTENER_MAGIC ISC_MAGIC('L', 'W', 'R', 'L')
+#define VALID_LWRESLISTENER(l) ISC_MAGIC_VALID(l, LWRESLISTENER_MAGIC)
+
+/*!
+ * The total number of clients we can handle will be NTASKS * NRECVS.
+ */
+#define NTASKS 2 /*%< tasks to create to handle lwres queries */
+#define NRECVS 2 /*%< max clients per task */
+
+typedef ISC_LIST(ns_lwreslistener_t) ns_lwreslistenerlist_t;
+
+static ns_lwreslistenerlist_t listeners;
+static isc_mutex_t listeners_lock;
+static isc_once_t once = ISC_ONCE_INIT;
+
+
+static void
+initialize_mutex(void) {
+ RUNTIME_CHECK(isc_mutex_init(&listeners_lock) == ISC_R_SUCCESS);
+}
+
+
+/*%
+ * Wrappers around our memory management stuff, for the lwres functions.
+ */
+void *
+ns__lwresd_memalloc(void *arg, size_t size) {
+ return (isc_mem_get(arg, size));
+}
+
+void
+ns__lwresd_memfree(void *arg, void *mem, size_t size) {
+ isc_mem_put(arg, mem, size);
+}
+
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+static isc_result_t
+buffer_putstr(isc_buffer_t *b, const char *s) {
+ unsigned int len = strlen(s);
+ if (isc_buffer_availablelength(b) <= len)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putmem(b, (const unsigned char *)s, len);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Convert a resolv.conf file into a config structure.
+ */
+isc_result_t
+ns_lwresd_parseeresolvconf(isc_mem_t *mctx, cfg_parser_t *pctx,
+ cfg_obj_t **configp)
+{
+ char text[4096];
+ char str[16];
+ isc_buffer_t b;
+ lwres_context_t *lwctx = NULL;
+ lwres_conf_t *lwc = NULL;
+ isc_sockaddr_t sa;
+ isc_netaddr_t na;
+ int i;
+ isc_result_t result;
+ lwres_result_t lwresult;
+
+ lwctx = NULL;
+ lwresult = lwres_context_create(&lwctx, mctx, ns__lwresd_memalloc,
+ ns__lwresd_memfree,
+ LWRES_CONTEXT_SERVERMODE);
+ if (lwresult != LWRES_R_SUCCESS) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ lwresult = lwres_conf_parse(lwctx, lwresd_g_resolvconffile);
+ if (lwresult != LWRES_R_SUCCESS) {
+ result = DNS_R_SYNTAX;
+ goto cleanup;
+ }
+
+ lwc = lwres_conf_get(lwctx);
+ INSIST(lwc != NULL);
+
+ isc_buffer_init(&b, text, sizeof(text));
+
+ CHECK(buffer_putstr(&b, "options {\n"));
+
+ /*
+ * Build the list of forwarders.
+ */
+ if (lwc->nsnext > 0) {
+ CHECK(buffer_putstr(&b, "\tforwarders {\n"));
+
+ for (i = 0; i < lwc->nsnext; i++) {
+ CHECK(lwaddr_sockaddr_fromlwresaddr(
+ &sa,
+ &lwc->nameservers[i],
+ ns_g_port));
+ isc_netaddr_fromsockaddr(&na, &sa);
+ CHECK(buffer_putstr(&b, "\t\t"));
+ CHECK(isc_netaddr_totext(&na, &b));
+ CHECK(buffer_putstr(&b, ";\n"));
+ }
+ CHECK(buffer_putstr(&b, "\t};\n"));
+ }
+
+ /*
+ * Build the sortlist
+ */
+ if (lwc->sortlistnxt > 0) {
+ CHECK(buffer_putstr(&b, "\tsortlist {\n"));
+ CHECK(buffer_putstr(&b, "\t\t{\n"));
+ CHECK(buffer_putstr(&b, "\t\t\tany;\n"));
+ CHECK(buffer_putstr(&b, "\t\t\t{\n"));
+ for (i = 0; i < lwc->sortlistnxt; i++) {
+ lwres_addr_t *lwaddr = &lwc->sortlist[i].addr;
+ lwres_addr_t *lwmask = &lwc->sortlist[i].mask;
+ unsigned int mask;
+
+ CHECK(lwaddr_sockaddr_fromlwresaddr(&sa, lwmask, 0));
+ isc_netaddr_fromsockaddr(&na, &sa);
+ result = isc_netaddr_masktoprefixlen(&na, &mask);
+ if (result != ISC_R_SUCCESS) {
+ char addrtext[ISC_NETADDR_FORMATSIZE];
+ isc_netaddr_format(&na, addrtext,
+ sizeof(addrtext));
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD,
+ ISC_LOG_ERROR,
+ "processing sortlist: '%s' is "
+ "not a valid netmask",
+ addrtext);
+ goto cleanup;
+ }
+
+ CHECK(lwaddr_sockaddr_fromlwresaddr(&sa, lwaddr, 0));
+ isc_netaddr_fromsockaddr(&na, &sa);
+
+ CHECK(buffer_putstr(&b, "\t\t\t\t"));
+ CHECK(isc_netaddr_totext(&na, &b));
+ snprintf(str, sizeof(str), "%u", mask);
+ CHECK(buffer_putstr(&b, "/"));
+ CHECK(buffer_putstr(&b, str));
+ CHECK(buffer_putstr(&b, ";\n"));
+ }
+ CHECK(buffer_putstr(&b, "\t\t\t};\n"));
+ CHECK(buffer_putstr(&b, "\t\t};\n"));
+ CHECK(buffer_putstr(&b, "\t};\n"));
+ }
+
+ CHECK(buffer_putstr(&b, "};\n\n"));
+
+ CHECK(buffer_putstr(&b, "lwres {\n"));
+
+ /*
+ * Build the search path
+ */
+ if (lwc->searchnxt > 0) {
+ if (lwc->searchnxt > 0) {
+ CHECK(buffer_putstr(&b, "\tsearch {\n"));
+ for (i = 0; i < lwc->searchnxt; i++) {
+ CHECK(buffer_putstr(&b, "\t\t\""));
+ CHECK(buffer_putstr(&b, lwc->search[i]));
+ CHECK(buffer_putstr(&b, "\";\n"));
+ }
+ CHECK(buffer_putstr(&b, "\t};\n"));
+ }
+ }
+
+ /*
+ * Build the ndots line
+ */
+ if (lwc->ndots != 1) {
+ CHECK(buffer_putstr(&b, "\tndots "));
+ snprintf(str, sizeof(str), "%u", lwc->ndots);
+ CHECK(buffer_putstr(&b, str));
+ CHECK(buffer_putstr(&b, ";\n"));
+ }
+
+ /*
+ * Build the listen-on line
+ */
+ if (lwc->lwnext > 0) {
+ CHECK(buffer_putstr(&b, "\tlisten-on {\n"));
+
+ for (i = 0; i < lwc->lwnext; i++) {
+ CHECK(lwaddr_sockaddr_fromlwresaddr(&sa,
+ &lwc->lwservers[i],
+ 0));
+ isc_netaddr_fromsockaddr(&na, &sa);
+ CHECK(buffer_putstr(&b, "\t\t"));
+ CHECK(isc_netaddr_totext(&na, &b));
+ CHECK(buffer_putstr(&b, ";\n"));
+ }
+ CHECK(buffer_putstr(&b, "\t};\n"));
+ }
+
+ CHECK(buffer_putstr(&b, "};\n"));
+
+#if 0
+ printf("%.*s\n",
+ (int)isc_buffer_usedlength(&b),
+ (char *)isc_buffer_base(&b));
+#endif
+
+ lwres_conf_clear(lwctx);
+ lwres_context_destroy(&lwctx);
+
+ return (cfg_parse_buffer(pctx, &b, &cfg_type_namedconf, configp));
+
+ cleanup:
+
+ if (lwctx != NULL) {
+ lwres_conf_clear(lwctx);
+ lwres_context_destroy(&lwctx);
+ }
+
+ return (result);
+}
+
+
+/*
+ * Handle lwresd manager objects
+ */
+isc_result_t
+ns_lwdmanager_create(isc_mem_t *mctx, const cfg_obj_t *lwres,
+ ns_lwresd_t **lwresdp)
+{
+ ns_lwresd_t *lwresd;
+ const char *vname;
+ dns_rdataclass_t vclass;
+ const cfg_obj_t *obj, *viewobj, *searchobj;
+ const cfg_listelt_t *element;
+ isc_result_t result;
+
+ INSIST(lwresdp != NULL && *lwresdp == NULL);
+
+ lwresd = isc_mem_get(mctx, sizeof(ns_lwresd_t));
+ if (lwresd == NULL)
+ return (ISC_R_NOMEMORY);
+
+ lwresd->mctx = NULL;
+ isc_mem_attach(mctx, &lwresd->mctx);
+ lwresd->view = NULL;
+ lwresd->search = NULL;
+ lwresd->refs = 1;
+
+ obj = NULL;
+ (void)cfg_map_get(lwres, "ndots", &obj);
+ if (obj != NULL)
+ lwresd->ndots = cfg_obj_asuint32(obj);
+ else
+ lwresd->ndots = 1;
+
+ RUNTIME_CHECK(isc_mutex_init(&lwresd->lock) == ISC_R_SUCCESS);
+
+ lwresd->shutting_down = ISC_FALSE;
+
+ viewobj = NULL;
+ (void)cfg_map_get(lwres, "view", &viewobj);
+ if (viewobj != NULL) {
+ vname = cfg_obj_asstring(cfg_tuple_get(viewobj, "name"));
+ obj = cfg_tuple_get(viewobj, "class");
+ result = ns_config_getclass(obj, dns_rdataclass_in, &vclass);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ } else {
+ vname = "_default";
+ vclass = dns_rdataclass_in;
+ }
+
+ result = dns_viewlist_find(&ns_g_server->viewlist, vname, vclass,
+ &lwresd->view);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "couldn't find view %s", vname);
+ goto fail;
+ }
+
+ searchobj = NULL;
+ (void)cfg_map_get(lwres, "search", &searchobj);
+ if (searchobj != NULL) {
+ lwresd->search = NULL;
+ result = ns_lwsearchlist_create(lwresd->mctx,
+ &lwresd->search);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "couldn't create searchlist");
+ goto fail;
+ }
+ for (element = cfg_list_first(searchobj);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *search;
+ const char *searchstr;
+ isc_buffer_t namebuf;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ search = cfg_listelt_value(element);
+ searchstr = cfg_obj_asstring(search);
+
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ isc_buffer_init(&namebuf, searchstr,
+ strlen(searchstr));
+ isc_buffer_add(&namebuf, strlen(searchstr));
+ result = dns_name_fromtext(name, &namebuf,
+ dns_rootname, ISC_FALSE,
+ NULL);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD,
+ ISC_LOG_WARNING,
+ "invalid name %s in searchlist",
+ searchstr);
+ continue;
+ }
+
+ result = ns_lwsearchlist_append(lwresd->search, name);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD,
+ ISC_LOG_WARNING,
+ "couldn't update searchlist");
+ goto fail;
+ }
+ }
+ }
+
+ lwresd->magic = LWRESD_MAGIC;
+
+ *lwresdp = lwresd;
+ return (ISC_R_SUCCESS);
+
+ fail:
+ if (lwresd->view != NULL)
+ dns_view_detach(&lwresd->view);
+ if (lwresd->search != NULL)
+ ns_lwsearchlist_detach(&lwresd->search);
+ if (lwresd->mctx != NULL)
+ isc_mem_detach(&lwresd->mctx);
+ isc_mem_put(mctx, lwresd, sizeof(ns_lwresd_t));
+ return (result);
+}
+
+void
+ns_lwdmanager_attach(ns_lwresd_t *source, ns_lwresd_t **targetp) {
+ INSIST(VALID_LWRESD(source));
+ INSIST(targetp != NULL && *targetp == NULL);
+
+ LOCK(&source->lock);
+ source->refs++;
+ UNLOCK(&source->lock);
+
+ *targetp = source;
+}
+
+void
+ns_lwdmanager_detach(ns_lwresd_t **lwresdp) {
+ ns_lwresd_t *lwresd;
+ isc_mem_t *mctx;
+ isc_boolean_t done = ISC_FALSE;
+
+ INSIST(lwresdp != NULL && *lwresdp != NULL);
+ INSIST(VALID_LWRESD(*lwresdp));
+
+ lwresd = *lwresdp;
+ *lwresdp = NULL;
+
+ LOCK(&lwresd->lock);
+ INSIST(lwresd->refs > 0);
+ lwresd->refs--;
+ if (lwresd->refs == 0)
+ done = ISC_TRUE;
+ UNLOCK(&lwresd->lock);
+
+ if (!done)
+ return;
+
+ dns_view_detach(&lwresd->view);
+ if (lwresd->search != NULL)
+ ns_lwsearchlist_detach(&lwresd->search);
+ mctx = lwresd->mctx;
+ lwresd->magic = 0;
+ isc_mem_put(mctx, lwresd, sizeof(*lwresd));
+ isc_mem_detach(&mctx);
+}
+
+
+/*
+ * Handle listener objects
+ */
+void
+ns_lwreslistener_attach(ns_lwreslistener_t *source,
+ ns_lwreslistener_t **targetp)
+{
+ INSIST(VALID_LWRESLISTENER(source));
+ INSIST(targetp != NULL && *targetp == NULL);
+
+ LOCK(&source->lock);
+ source->refs++;
+ UNLOCK(&source->lock);
+
+ *targetp = source;
+}
+
+void
+ns_lwreslistener_detach(ns_lwreslistener_t **listenerp) {
+ ns_lwreslistener_t *listener;
+ isc_mem_t *mctx;
+ isc_boolean_t done = ISC_FALSE;
+
+ INSIST(listenerp != NULL && *listenerp != NULL);
+ INSIST(VALID_LWRESLISTENER(*listenerp));
+
+ listener = *listenerp;
+
+ LOCK(&listener->lock);
+ INSIST(listener->refs > 0);
+ listener->refs--;
+ if (listener->refs == 0)
+ done = ISC_TRUE;
+ UNLOCK(&listener->lock);
+
+ if (!done)
+ return;
+
+ if (listener->manager != NULL)
+ ns_lwdmanager_detach(&listener->manager);
+
+ if (listener->sock != NULL)
+ isc_socket_detach(&listener->sock);
+
+ listener->magic = 0;
+ mctx = listener->mctx;
+ isc_mem_put(mctx, listener, sizeof(*listener));
+ isc_mem_detach(&mctx);
+ listenerp = NULL;
+}
+
+static isc_result_t
+listener_create(isc_mem_t *mctx, ns_lwresd_t *lwresd,
+ ns_lwreslistener_t **listenerp)
+{
+ ns_lwreslistener_t *listener;
+ isc_result_t result;
+
+ REQUIRE(listenerp != NULL && *listenerp == NULL);
+
+ listener = isc_mem_get(mctx, sizeof(ns_lwreslistener_t));
+ if (listener == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&listener->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, listener, sizeof(ns_lwreslistener_t));
+ return (result);
+ }
+
+ listener->magic = LWRESLISTENER_MAGIC;
+ listener->refs = 1;
+
+ listener->sock = NULL;
+
+ listener->manager = NULL;
+ ns_lwdmanager_attach(lwresd, &listener->manager);
+
+ listener->mctx = NULL;
+ isc_mem_attach(mctx, &listener->mctx);
+
+ ISC_LINK_INIT(listener, link);
+ ISC_LIST_INIT(listener->cmgrs);
+
+ *listenerp = listener;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+listener_bind(ns_lwreslistener_t *listener, isc_sockaddr_t *address) {
+ isc_socket_t *sock = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ int pf;
+
+ pf = isc_sockaddr_pf(address);
+ if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
+ (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
+ return (ISC_R_FAMILYNOSUPPORT);
+
+ listener->address = *address;
+
+ if (isc_sockaddr_getport(&listener->address) == 0) {
+ in_port_t port;
+ port = lwresd_g_listenport;
+ if (port == 0)
+ port = LWRES_UDP_PORT;
+ isc_sockaddr_setport(&listener->address, port);
+ }
+
+ sock = NULL;
+ result = isc_socket_create(ns_g_socketmgr, pf,
+ isc_sockettype_udp, &sock);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "failed to create lwres socket: %s",
+ isc_result_totext(result));
+ return (result);
+ }
+
+ result = isc_socket_bind(sock, &listener->address);
+ if (result != ISC_R_SUCCESS) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&listener->address, socktext,
+ sizeof(socktext));
+ isc_socket_detach(&sock);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "failed to add lwres socket: %s: %s",
+ socktext, isc_result_totext(result));
+ return (result);
+ }
+ listener->sock = sock;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+listener_copysock(ns_lwreslistener_t *oldlistener,
+ ns_lwreslistener_t *newlistener)
+{
+ newlistener->address = oldlistener->address;
+ isc_socket_attach(oldlistener->sock, &newlistener->sock);
+}
+
+static isc_result_t
+listener_startclients(ns_lwreslistener_t *listener) {
+ ns_lwdclientmgr_t *cm;
+ unsigned int i;
+ isc_result_t result;
+
+ /*
+ * Create the client managers.
+ */
+ result = ISC_R_SUCCESS;
+ for (i = 0; i < NTASKS && result == ISC_R_SUCCESS; i++)
+ result = ns_lwdclientmgr_create(listener, NRECVS,
+ ns_g_taskmgr);
+
+ /*
+ * Ensure that we have created at least one.
+ */
+ if (ISC_LIST_EMPTY(listener->cmgrs))
+ return (result);
+
+ /*
+ * Walk the list of clients and start each one up.
+ */
+ LOCK(&listener->lock);
+ cm = ISC_LIST_HEAD(listener->cmgrs);
+ while (cm != NULL) {
+ result = ns_lwdclient_startrecv(cm);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
+ "could not start lwres "
+ "client handler: %s",
+ isc_result_totext(result));
+ cm = ISC_LIST_NEXT(cm, link);
+ }
+ UNLOCK(&listener->lock);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+listener_shutdown(ns_lwreslistener_t *listener) {
+ ns_lwdclientmgr_t *cm;
+
+ cm = ISC_LIST_HEAD(listener->cmgrs);
+ while (cm != NULL) {
+ isc_task_shutdown(cm->task);
+ cm = ISC_LIST_NEXT(cm, link);
+ }
+}
+
+static isc_result_t
+find_listener(isc_sockaddr_t *address, ns_lwreslistener_t **listenerp) {
+ ns_lwreslistener_t *listener;
+
+ INSIST(listenerp != NULL && *listenerp == NULL);
+
+ for (listener = ISC_LIST_HEAD(listeners);
+ listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ {
+ if (!isc_sockaddr_equal(address, &listener->address))
+ continue;
+ *listenerp = listener;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+void
+ns_lwreslistener_unlinkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm)
+{
+ REQUIRE(VALID_LWRESLISTENER(listener));
+
+ LOCK(&listener->lock);
+ ISC_LIST_UNLINK(listener->cmgrs, cm, link);
+ UNLOCK(&listener->lock);
+}
+
+void
+ns_lwreslistener_linkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm) {
+ REQUIRE(VALID_LWRESLISTENER(listener));
+
+ /*
+ * This does no locking, since it's called early enough that locking
+ * isn't needed.
+ */
+ ISC_LIST_APPEND(listener->cmgrs, cm, link);
+}
+
+static isc_result_t
+configure_listener(isc_sockaddr_t *address, ns_lwresd_t *lwresd,
+ isc_mem_t *mctx, ns_lwreslistenerlist_t *newlisteners)
+{
+ ns_lwreslistener_t *listener, *oldlistener = NULL;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_result_t result;
+
+ (void)find_listener(address, &oldlistener);
+ listener = NULL;
+ result = listener_create(mctx, lwresd, &listener);
+ if (result != ISC_R_SUCCESS) {
+ isc_sockaddr_format(address, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "lwres failed to configure %s: %s",
+ socktext, isc_result_totext(result));
+ return (result);
+ }
+
+ /*
+ * If there's already a listener, don't rebind the socket.
+ */
+ if (oldlistener == NULL) {
+ result = listener_bind(listener, address);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwreslistener_detach(&listener);
+ return (ISC_R_SUCCESS);
+ }
+ } else
+ listener_copysock(oldlistener, listener);
+
+ result = listener_startclients(listener);
+ if (result != ISC_R_SUCCESS) {
+ isc_sockaddr_format(address, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "lwres: failed to start %s: %s", socktext,
+ isc_result_totext(result));
+ ns_lwreslistener_detach(&listener);
+ return (ISC_R_SUCCESS);
+ }
+
+ if (oldlistener != NULL) {
+ /*
+ * Remove the old listener from the old list and shut it down.
+ */
+ ISC_LIST_UNLINK(listeners, oldlistener, link);
+ listener_shutdown(oldlistener);
+ ns_lwreslistener_detach(&oldlistener);
+ } else {
+ isc_sockaddr_format(address, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_NOTICE,
+ "lwres listening on %s", socktext);
+ }
+
+ ISC_LIST_APPEND(*newlisteners, listener, link);
+ return (result);
+}
+
+isc_result_t
+ns_lwresd_configure(isc_mem_t *mctx, const cfg_obj_t *config) {
+ const cfg_obj_t *lwreslist = NULL;
+ const cfg_obj_t *lwres = NULL;
+ const cfg_obj_t *listenerslist = NULL;
+ const cfg_listelt_t *element = NULL;
+ ns_lwreslistener_t *listener;
+ ns_lwreslistenerlist_t newlisteners;
+ isc_result_t result;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_t *addrs = NULL;
+ ns_lwresd_t *lwresd = NULL;
+ isc_uint32_t count = 0;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(config != NULL);
+
+ RUNTIME_CHECK(isc_once_do(&once, initialize_mutex) == ISC_R_SUCCESS);
+
+ ISC_LIST_INIT(newlisteners);
+
+ result = cfg_map_get(config, "lwres", &lwreslist);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ LOCK(&listeners_lock);
+ /*
+ * Run through the new lwres address list, noting sockets that
+ * are already being listened on and moving them to the new list.
+ *
+ * Identifying duplicates addr/port combinations is left to either
+ * the underlying config code, or to the bind attempt getting an
+ * address-in-use error.
+ */
+ for (element = cfg_list_first(lwreslist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ in_port_t port;
+
+ lwres = cfg_listelt_value(element);
+ CHECK(ns_lwdmanager_create(mctx, lwres, &lwresd));
+
+ port = lwresd_g_listenport;
+ if (port == 0)
+ port = LWRES_UDP_PORT;
+
+ listenerslist = NULL;
+ (void)cfg_map_get(lwres, "listen-on", &listenerslist);
+ if (listenerslist == NULL) {
+ struct in_addr localhost;
+ isc_sockaddr_t address;
+
+ localhost.s_addr = htonl(INADDR_LOOPBACK);
+ isc_sockaddr_fromin(&address, &localhost, port);
+ CHECK(configure_listener(&address, lwresd, mctx,
+ &newlisteners));
+ } else {
+ isc_uint32_t i;
+
+ CHECK(ns_config_getiplist(config, listenerslist,
+ port, mctx, &addrs, &count));
+ for (i = 0; i < count; i++)
+ CHECK(configure_listener(&addrs[i], lwresd,
+ mctx, &newlisteners));
+ ns_config_putiplist(mctx, &addrs, count);
+ }
+ ns_lwdmanager_detach(&lwresd);
+ }
+
+ /*
+ * Shutdown everything on the listeners list, and remove them from
+ * the list. Then put all of the new listeners on it.
+ */
+
+ while (!ISC_LIST_EMPTY(listeners)) {
+ listener = ISC_LIST_HEAD(listeners);
+ ISC_LIST_UNLINK(listeners, listener, link);
+
+ isc_sockaddr_format(&listener->address,
+ socktext, sizeof(socktext));
+
+ listener_shutdown(listener);
+ ns_lwreslistener_detach(&listener);
+
+ isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_NOTICE,
+ "lwres no longer listening on %s", socktext);
+ }
+
+ cleanup:
+ ISC_LIST_APPENDLIST(listeners, newlisteners, link);
+
+ if (addrs != NULL)
+ ns_config_putiplist(mctx, &addrs, count);
+
+ if (lwresd != NULL)
+ ns_lwdmanager_detach(&lwresd);
+
+ UNLOCK(&listeners_lock);
+
+ return (result);
+}
+
+void
+ns_lwresd_shutdown(void) {
+ ns_lwreslistener_t *listener;
+
+ RUNTIME_CHECK(isc_once_do(&once, initialize_mutex) == ISC_R_SUCCESS);
+
+ while (!ISC_LIST_EMPTY(listeners)) {
+ listener = ISC_LIST_HEAD(listeners);
+ ISC_LIST_UNLINK(listeners, listener, link);
+ ns_lwreslistener_detach(&listener);
+ }
+}
diff --git a/bin/named/lwresd.docbook b/bin/named/lwresd.docbook
new file mode 100644
index 0000000..5b3143e
--- /dev/null
+++ b/bin/named/lwresd.docbook
@@ -0,0 +1,372 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2000, 2001 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: lwresd.docbook,v 1.7.18.8 2007/08/28 07:20:01 tbox Exp $ -->
+<refentry>
+ <refentryinfo>
+ <date>June 30, 2000</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle><application>lwresd</application></refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo>BIND9</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname><application>lwresd</application></refname>
+ <refpurpose>lightweight resolver daemon</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2007</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ <copyright>
+ <year>2000</year>
+ <year>2001</year>
+ <holder>Internet Software Consortium.</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>lwresd</command>
+ <arg><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
+ <arg><option>-C <replaceable class="parameter">config-file</replaceable></option></arg>
+ <arg><option>-d <replaceable class="parameter">debug-level</replaceable></option></arg>
+ <arg><option>-f</option></arg>
+ <arg><option>-g</option></arg>
+ <arg><option>-i <replaceable class="parameter">pid-file</replaceable></option></arg>
+ <arg><option>-m <replaceable class="parameter">flag</replaceable></option></arg>
+ <arg><option>-n <replaceable class="parameter">#cpus</replaceable></option></arg>
+ <arg><option>-P <replaceable class="parameter">port</replaceable></option></arg>
+ <arg><option>-p <replaceable class="parameter">port</replaceable></option></arg>
+ <arg><option>-s</option></arg>
+ <arg><option>-t <replaceable class="parameter">directory</replaceable></option></arg>
+ <arg><option>-u <replaceable class="parameter">user</replaceable></option></arg>
+ <arg><option>-v</option></arg>
+ <arg><option>-4</option></arg>
+ <arg><option>-6</option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para><command>lwresd</command>
+ is the daemon providing name lookup
+ services to clients that use the BIND 9 lightweight resolver
+ library. It is essentially a stripped-down, caching-only name
+ server that answers queries using the BIND 9 lightweight
+ resolver protocol rather than the DNS protocol.
+ </para>
+
+ <para><command>lwresd</command>
+ listens for resolver queries on a
+ UDP port on the IPv4 loopback interface, 127.0.0.1. This
+ means that <command>lwresd</command> can only be used by
+ processes running on the local machine. By default UDP port
+ number 921 is used for lightweight resolver requests and
+ responses.
+ </para>
+ <para>
+ Incoming lightweight resolver requests are decoded by the
+ server which then resolves them using the DNS protocol. When
+ the DNS lookup completes, <command>lwresd</command> encodes
+ the answers in the lightweight resolver format and returns
+ them to the client that made the request.
+ </para>
+ <para>
+ If <filename>/etc/resolv.conf</filename> contains any
+ <option>nameserver</option> entries, <command>lwresd</command>
+ sends recursive DNS queries to those servers. This is similar
+ to the use of forwarders in a caching name server. If no
+ <option>nameserver</option> entries are present, or if
+ forwarding fails, <command>lwresd</command> resolves the
+ queries autonomously starting at the root name servers, using
+ a built-in list of root server hints.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>-4</term>
+ <listitem>
+ <para>
+ Use IPv4 only even if the host machine is capable of IPv6.
+ <option>-4</option> and <option>-6</option> are mutually
+ exclusive.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-6</term>
+ <listitem>
+ <para>
+ Use IPv6 only even if the host machine is capable of IPv4.
+ <option>-4</option> and <option>-6</option> are mutually
+ exclusive.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <!-- this is in source but not mentioned? does this matter? -->
+ <varlistentry>
+ <term>-c <replaceable class="parameter">config-file</replaceable></term>
+ <listitem>
+ <para>
+ Use <replaceable class="parameter">config-file</replaceable> as the
+ configuration file instead of the default,
+ <filename>/etc/lwresd.conf</filename>.
+ <!-- Should this be an absolute path name? -->
+ <term>-c</term> can not be used with <term>-C</term>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-C <replaceable class="parameter">config-file</replaceable></term>
+ <listitem>
+ <para>
+ Use <replaceable class="parameter">config-file</replaceable> as the
+ configuration file instead of the default,
+ <filename>/etc/resolv.conf</filename>.
+ <term>-C</term> can not be used with <term>-c</term>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-d <replaceable class="parameter">debug-level</replaceable></term>
+ <listitem>
+ <para>
+ Set the daemon's debug level to <replaceable class="parameter">debug-level</replaceable>.
+ Debugging traces from <command>lwresd</command> become
+ more verbose as the debug level increases.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-f</term>
+ <listitem>
+ <para>
+ Run the server in the foreground (i.e. do not daemonize).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-g</term>
+ <listitem>
+ <para>
+ Run the server in the foreground and force all logging
+ to <filename>stderr</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-i <replaceable class="parameter">pid-file</replaceable></term>
+ <listitem>
+ <para>
+ Use <replaceable class="parameter">pid-file</replaceable> as the
+ PID file instead of the default,
+ <filename>/var/run/lwresd.pid</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-m <replaceable class="parameter">flag</replaceable></term>
+ <listitem>
+ <para>
+ Turn on memory usage debugging flags. Possible flags are
+ <replaceable class="parameter">usage</replaceable>,
+ <replaceable class="parameter">trace</replaceable>,
+ <replaceable class="parameter">record</replaceable>,
+ <replaceable class="parameter">size</replaceable>, and
+ <replaceable class="parameter">mctx</replaceable>.
+ These correspond to the ISC_MEM_DEBUGXXXX flags described in
+ <filename>&lt;isc/mem.h&gt;</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-n <replaceable class="parameter">#cpus</replaceable></term>
+ <listitem>
+ <para>
+ Create <replaceable class="parameter">#cpus</replaceable> worker threads
+ to take advantage of multiple CPUs. If not specified,
+ <command>lwresd</command> will try to determine the
+ number of CPUs present and create one thread per CPU.
+ If it is unable to determine the number of CPUs, a
+ single worker thread will be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-P <replaceable class="parameter">port</replaceable></term>
+ <listitem>
+ <para>
+ Listen for lightweight resolver queries on port
+ <replaceable class="parameter">port</replaceable>. If
+ not specified, the default is port 921.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-p <replaceable class="parameter">port</replaceable></term>
+ <listitem>
+ <para>
+ Send DNS lookups to port <replaceable class="parameter">port</replaceable>. If not
+ specified, the default is port 53. This provides a
+ way of testing the lightweight resolver daemon with a
+ name server that listens for queries on a non-standard
+ port number.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s</term>
+ <listitem>
+ <para>
+ Write memory usage statistics to <filename>stdout</filename>
+ on exit.
+ </para>
+ <note>
+ <para>
+ This option is mainly of interest to BIND 9 developers
+ and may be removed or changed in a future release.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-t <replaceable class="parameter">directory</replaceable></term>
+ <listitem>
+ <para>Chroot
+ to <replaceable class="parameter">directory</replaceable> after
+ processing the command line arguments, but before
+ reading the configuration file.
+ </para>
+ <warning>
+ <para>
+ This option should be used in conjunction with the
+ <option>-u</option> option, as chrooting a process
+ running as root doesn't enhance security on most
+ systems; the way <function>chroot(2)</function> is
+ defined allows a process with root privileges to
+ escape a chroot jail.
+ </para>
+ </warning>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-u <replaceable class="parameter">user</replaceable></term>
+ <listitem>
+ <para>Setuid
+ to <replaceable class="parameter">user</replaceable> after completing
+ privileged operations, such as creating sockets that
+ listen on privileged ports.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-v</term>
+ <listitem>
+ <para>
+ Report the version number and exit.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>FILES</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><filename>/etc/resolv.conf</filename></term>
+ <listitem>
+ <para>
+ The default configuration file.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>/var/run/lwresd.pid</filename></term>
+ <listitem>
+ <para>
+ The default process-id file.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para><citerefentry>
+ <refentrytitle>named</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>lwres</refentrytitle><manvolnum>3</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>resolver</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>AUTHOR</title>
+ <para><corpauthor>Internet Systems Consortium</corpauthor>
+ </para>
+ </refsect1>
+
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/bin/named/lwresd.html b/bin/named/lwresd.html
new file mode 100644
index 0000000..b59a7cc
--- /dev/null
+++ b/bin/named/lwresd.html
@@ -0,0 +1,225 @@
+<!--
+ - Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2000, 2001 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: lwresd.html,v 1.5.18.18 2007/05/16 06:11:27 marka Exp $ -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>lwresd</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.71.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry" lang="en">
+<a name="id2476275"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><span class="application">lwresd</span> &#8212; lightweight resolver daemon</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">lwresd</code> [<code class="option">-c <em class="replaceable"><code>config-file</code></em></code>] [<code class="option">-C <em class="replaceable"><code>config-file</code></em></code>] [<code class="option">-d <em class="replaceable"><code>debug-level</code></em></code>] [<code class="option">-f</code>] [<code class="option">-g</code>] [<code class="option">-i <em class="replaceable"><code>pid-file</code></em></code>] [<code class="option">-m <em class="replaceable"><code>flag</code></em></code>] [<code class="option">-n <em class="replaceable"><code>#cpus</code></em></code>] [<code class="option">-P <em class="replaceable"><code>port</code></em></code>] [<code class="option">-p <em class="replaceable"><code>port</code></em></code>] [<code class="option">-s</code>] [<code class="option">-t <em class="replaceable"><code>directory</code></em></code>] [<code class="option">-u <em class="replaceable"><code>user</code></em></code>] [<code class="option">-v</code>] [<code class="option">-4</code>] [<code class="option">-6</code>]</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543461"></a><h2>DESCRIPTION</h2>
+<p><span><strong class="command">lwresd</strong></span>
+ is the daemon providing name lookup
+ services to clients that use the BIND 9 lightweight resolver
+ library. It is essentially a stripped-down, caching-only name
+ server that answers queries using the BIND 9 lightweight
+ resolver protocol rather than the DNS protocol.
+ </p>
+<p><span><strong class="command">lwresd</strong></span>
+ listens for resolver queries on a
+ UDP port on the IPv4 loopback interface, 127.0.0.1. This
+ means that <span><strong class="command">lwresd</strong></span> can only be used by
+ processes running on the local machine. By default UDP port
+ number 921 is used for lightweight resolver requests and
+ responses.
+ </p>
+<p>
+ Incoming lightweight resolver requests are decoded by the
+ server which then resolves them using the DNS protocol. When
+ the DNS lookup completes, <span><strong class="command">lwresd</strong></span> encodes
+ the answers in the lightweight resolver format and returns
+ them to the client that made the request.
+ </p>
+<p>
+ If <code class="filename">/etc/resolv.conf</code> contains any
+ <code class="option">nameserver</code> entries, <span><strong class="command">lwresd</strong></span>
+ sends recursive DNS queries to those servers. This is similar
+ to the use of forwarders in a caching name server. If no
+ <code class="option">nameserver</code> entries are present, or if
+ forwarding fails, <span><strong class="command">lwresd</strong></span> resolves the
+ queries autonomously starting at the root name servers, using
+ a built-in list of root server hints.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543508"></a><h2>OPTIONS</h2>
+<div class="variablelist"><dl>
+<dt><span class="term">-4</span></dt>
+<dd><p>
+ Use IPv4 only even if the host machine is capable of IPv6.
+ <code class="option">-4</code> and <code class="option">-6</code> are mutually
+ exclusive.
+ </p></dd>
+<dt><span class="term">-6</span></dt>
+<dd><p>
+ Use IPv6 only even if the host machine is capable of IPv4.
+ <code class="option">-4</code> and <code class="option">-6</code> are mutually
+ exclusive.
+ </p></dd>
+<dt><span class="term">-c <em class="replaceable"><code>config-file</code></em></span></dt>
+<dd><p>
+ Use <em class="replaceable"><code>config-file</code></em> as the
+ configuration file instead of the default,
+ <code class="filename">/etc/lwresd.conf</code>.
+
+ <font color="red">&lt;term&gt;-c&lt;/term&gt;</font> can not be used with <font color="red">&lt;term&gt;-C&lt;/term&gt;</font>.
+ </p></dd>
+<dt><span class="term">-C <em class="replaceable"><code>config-file</code></em></span></dt>
+<dd><p>
+ Use <em class="replaceable"><code>config-file</code></em> as the
+ configuration file instead of the default,
+ <code class="filename">/etc/resolv.conf</code>.
+ <font color="red">&lt;term&gt;-C&lt;/term&gt;</font> can not be used with <font color="red">&lt;term&gt;-c&lt;/term&gt;</font>.
+ </p></dd>
+<dt><span class="term">-d <em class="replaceable"><code>debug-level</code></em></span></dt>
+<dd><p>
+ Set the daemon's debug level to <em class="replaceable"><code>debug-level</code></em>.
+ Debugging traces from <span><strong class="command">lwresd</strong></span> become
+ more verbose as the debug level increases.
+ </p></dd>
+<dt><span class="term">-f</span></dt>
+<dd><p>
+ Run the server in the foreground (i.e. do not daemonize).
+ </p></dd>
+<dt><span class="term">-g</span></dt>
+<dd><p>
+ Run the server in the foreground and force all logging
+ to <code class="filename">stderr</code>.
+ </p></dd>
+<dt><span class="term">-i <em class="replaceable"><code>pid-file</code></em></span></dt>
+<dd><p>
+ Use <em class="replaceable"><code>pid-file</code></em> as the
+ PID file instead of the default,
+ <code class="filename">/var/run/lwresd.pid</code>.
+ </p></dd>
+<dt><span class="term">-m <em class="replaceable"><code>flag</code></em></span></dt>
+<dd><p>
+ Turn on memory usage debugging flags. Possible flags are
+ <em class="replaceable"><code>usage</code></em>,
+ <em class="replaceable"><code>trace</code></em>,
+ <em class="replaceable"><code>record</code></em>,
+ <em class="replaceable"><code>size</code></em>, and
+ <em class="replaceable"><code>mctx</code></em>.
+ These correspond to the ISC_MEM_DEBUGXXXX flags described in
+ <code class="filename">&lt;isc/mem.h&gt;</code>.
+ </p></dd>
+<dt><span class="term">-n <em class="replaceable"><code>#cpus</code></em></span></dt>
+<dd><p>
+ Create <em class="replaceable"><code>#cpus</code></em> worker threads
+ to take advantage of multiple CPUs. If not specified,
+ <span><strong class="command">lwresd</strong></span> will try to determine the
+ number of CPUs present and create one thread per CPU.
+ If it is unable to determine the number of CPUs, a
+ single worker thread will be created.
+ </p></dd>
+<dt><span class="term">-P <em class="replaceable"><code>port</code></em></span></dt>
+<dd><p>
+ Listen for lightweight resolver queries on port
+ <em class="replaceable"><code>port</code></em>. If
+ not specified, the default is port 921.
+ </p></dd>
+<dt><span class="term">-p <em class="replaceable"><code>port</code></em></span></dt>
+<dd><p>
+ Send DNS lookups to port <em class="replaceable"><code>port</code></em>. If not
+ specified, the default is port 53. This provides a
+ way of testing the lightweight resolver daemon with a
+ name server that listens for queries on a non-standard
+ port number.
+ </p></dd>
+<dt><span class="term">-s</span></dt>
+<dd>
+<p>
+ Write memory usage statistics to <code class="filename">stdout</code>
+ on exit.
+ </p>
+<div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Note</h3>
+<p>
+ This option is mainly of interest to BIND 9 developers
+ and may be removed or changed in a future release.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-t <em class="replaceable"><code>directory</code></em></span></dt>
+<dd>
+<p>Chroot
+ to <em class="replaceable"><code>directory</code></em> after
+ processing the command line arguments, but before
+ reading the configuration file.
+ </p>
+<div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Warning</h3>
+<p>
+ This option should be used in conjunction with the
+ <code class="option">-u</code> option, as chrooting a process
+ running as root doesn't enhance security on most
+ systems; the way <code class="function">chroot(2)</code> is
+ defined allows a process with root privileges to
+ escape a chroot jail.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-u <em class="replaceable"><code>user</code></em></span></dt>
+<dd><p>Setuid
+ to <em class="replaceable"><code>user</code></em> after completing
+ privileged operations, such as creating sockets that
+ listen on privileged ports.
+ </p></dd>
+<dt><span class="term">-v</span></dt>
+<dd><p>
+ Report the version number and exit.
+ </p></dd>
+</dl></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543925"></a><h2>FILES</h2>
+<div class="variablelist"><dl>
+<dt><span class="term"><code class="filename">/etc/resolv.conf</code></span></dt>
+<dd><p>
+ The default configuration file.
+ </p></dd>
+<dt><span class="term"><code class="filename">/var/run/lwresd.pid</code></span></dt>
+<dd><p>
+ The default process-id file.
+ </p></dd>
+</dl></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543964"></a><h2>SEE ALSO</h2>
+<p><span class="citerefentry"><span class="refentrytitle">named</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">lwres</span>(3)</span>,
+ <span class="citerefentry"><span class="refentrytitle">resolver</span>(5)</span>.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543998"></a><h2>AUTHOR</h2>
+<p><span class="corpauthor">Internet Systems Consortium</span>
+ </p>
+</div>
+</div></body>
+</html>
diff --git a/bin/named/lwsearch.c b/bin/named/lwsearch.c
new file mode 100644
index 0000000..4a61f96
--- /dev/null
+++ b/bin/named/lwsearch.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: lwsearch.c,v 1.8.18.3 2005/07/12 01:22:17 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/result.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/name.h>
+#include <dns/types.h>
+
+#include <named/lwsearch.h>
+#include <named/types.h>
+
+#define LWSEARCHLIST_MAGIC ISC_MAGIC('L', 'W', 'S', 'L')
+#define VALID_LWSEARCHLIST(l) ISC_MAGIC_VALID(l, LWSEARCHLIST_MAGIC)
+
+isc_result_t
+ns_lwsearchlist_create(isc_mem_t *mctx, ns_lwsearchlist_t **listp) {
+ ns_lwsearchlist_t *list;
+ isc_result_t result;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(listp != NULL && *listp == NULL);
+
+ list = isc_mem_get(mctx, sizeof(ns_lwsearchlist_t));
+ if (list == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&list->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, list, sizeof(ns_lwsearchlist_t));
+ return (result);
+ }
+ list->mctx = NULL;
+ isc_mem_attach(mctx, &list->mctx);
+ list->refs = 1;
+ ISC_LIST_INIT(list->names);
+ list->magic = LWSEARCHLIST_MAGIC;
+
+ *listp = list;
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_lwsearchlist_attach(ns_lwsearchlist_t *source, ns_lwsearchlist_t **target) {
+ REQUIRE(VALID_LWSEARCHLIST(source));
+ REQUIRE(target != NULL && *target == NULL);
+
+ LOCK(&source->lock);
+ INSIST(source->refs > 0);
+ source->refs++;
+ INSIST(source->refs != 0);
+ UNLOCK(&source->lock);
+
+ *target = source;
+}
+
+void
+ns_lwsearchlist_detach(ns_lwsearchlist_t **listp) {
+ ns_lwsearchlist_t *list;
+ isc_mem_t *mctx;
+
+ REQUIRE(listp != NULL);
+ list = *listp;
+ REQUIRE(VALID_LWSEARCHLIST(list));
+
+ LOCK(&list->lock);
+ INSIST(list->refs > 0);
+ list->refs--;
+ UNLOCK(&list->lock);
+
+ *listp = NULL;
+ if (list->refs != 0)
+ return;
+
+ mctx = list->mctx;
+ while (!ISC_LIST_EMPTY(list->names)) {
+ dns_name_t *name = ISC_LIST_HEAD(list->names);
+ ISC_LIST_UNLINK(list->names, name, link);
+ dns_name_free(name, list->mctx);
+ isc_mem_put(list->mctx, name, sizeof(dns_name_t));
+ }
+ list->magic = 0;
+ isc_mem_put(mctx, list, sizeof(ns_lwsearchlist_t));
+ isc_mem_detach(&mctx);
+}
+
+isc_result_t
+ns_lwsearchlist_append(ns_lwsearchlist_t *list, dns_name_t *name) {
+ dns_name_t *newname;
+ isc_result_t result;
+
+ REQUIRE(VALID_LWSEARCHLIST(list));
+ REQUIRE(name != NULL);
+
+ newname = isc_mem_get(list->mctx, sizeof(dns_name_t));
+ if (newname == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_name_init(newname, NULL);
+ result = dns_name_dup(name, list->mctx, newname);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(list->mctx, newname, sizeof(dns_name_t));
+ return (result);
+ }
+ ISC_LINK_INIT(newname, link);
+ ISC_LIST_APPEND(list->names, newname, link);
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_lwsearchctx_init(ns_lwsearchctx_t *sctx, ns_lwsearchlist_t *list,
+ dns_name_t *name, unsigned int ndots)
+{
+ INSIST(sctx != NULL);
+ sctx->relname = name;
+ sctx->searchname = NULL;
+ sctx->doneexact = ISC_FALSE;
+ sctx->exactfirst = ISC_FALSE;
+ sctx->ndots = ndots;
+ if (dns_name_isabsolute(name) || list == NULL) {
+ sctx->list = NULL;
+ return;
+ }
+ sctx->list = list;
+ sctx->searchname = ISC_LIST_HEAD(sctx->list->names);
+ if (dns_name_countlabels(name) > ndots)
+ sctx->exactfirst = ISC_TRUE;
+}
+
+void
+ns_lwsearchctx_first(ns_lwsearchctx_t *sctx) {
+ REQUIRE(sctx != NULL);
+ UNUSED(sctx);
+}
+
+isc_result_t
+ns_lwsearchctx_next(ns_lwsearchctx_t *sctx) {
+ REQUIRE(sctx != NULL);
+
+ if (sctx->list == NULL)
+ return (ISC_R_NOMORE);
+
+ if (sctx->searchname == NULL) {
+ INSIST (!sctx->exactfirst || sctx->doneexact);
+ if (sctx->exactfirst || sctx->doneexact)
+ return (ISC_R_NOMORE);
+ sctx->doneexact = ISC_TRUE;
+ } else {
+ if (sctx->exactfirst && !sctx->doneexact)
+ sctx->doneexact = ISC_TRUE;
+ else {
+ sctx->searchname = ISC_LIST_NEXT(sctx->searchname,
+ link);
+ if (sctx->searchname == NULL && sctx->doneexact)
+ return (ISC_R_NOMORE);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_lwsearchctx_current(ns_lwsearchctx_t *sctx, dns_name_t *absname) {
+ dns_name_t *tname;
+ isc_boolean_t useexact = ISC_FALSE;
+
+ REQUIRE(sctx != NULL);
+
+ if (sctx->list == NULL ||
+ sctx->searchname == NULL ||
+ (sctx->exactfirst && !sctx->doneexact))
+ useexact = ISC_TRUE;
+
+ if (useexact) {
+ if (dns_name_isabsolute(sctx->relname))
+ tname = NULL;
+ else
+ tname = dns_rootname;
+ } else
+ tname = sctx->searchname;
+
+ return (dns_name_concatenate(sctx->relname, tname, absname, NULL));
+}
diff --git a/bin/named/main.c b/bin/named/main.c
new file mode 100644
index 0000000..6b9b67e
--- /dev/null
+++ b/bin/named/main.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: main.c,v 1.136.18.17 2006/11/10 18:51:14 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/app.h>
+#include <isc/commandline.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/os.h>
+#include <isc/platform.h>
+#include <isc/resource.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <isccc/result.h>
+
+#include <dns/dispatch.h>
+#include <dns/name.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+#include <dst/result.h>
+
+/*
+ * Defining NS_MAIN provides storage declarations (rather than extern)
+ * for variables in named/globals.h.
+ */
+#define NS_MAIN 1
+
+#include <named/builtin.h>
+#include <named/control.h>
+#include <named/globals.h> /* Explicit, though named/log.h includes it. */
+#include <named/interfacemgr.h>
+#include <named/log.h>
+#include <named/os.h>
+#include <named/server.h>
+#include <named/lwresd.h>
+#include <named/main.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#endif
+
+/*
+ * Include header files for database drivers here.
+ */
+/* #include "xxdb.h" */
+
+/*
+ * Include DLZ drivers if appropriate.
+ */
+#ifdef DLZ
+#include <dlz/dlz_drivers.h>
+#endif
+
+static isc_boolean_t want_stats = ISC_FALSE;
+static char program_name[ISC_DIR_NAMEMAX] = "named";
+static char absolute_conffile[ISC_DIR_PATHMAX];
+static char saved_command_line[512];
+static char version[512];
+
+void
+ns_main_earlywarning(const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ if (ns_g_lctx != NULL) {
+ isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_WARNING,
+ format, args);
+ } else {
+ fprintf(stderr, "%s: ", program_name);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+ va_end(args);
+}
+
+void
+ns_main_earlyfatal(const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ if (ns_g_lctx != NULL) {
+ isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ format, args);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "exiting (due to early fatal error)");
+ } else {
+ fprintf(stderr, "%s: ", program_name);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+ va_end(args);
+
+ exit(1);
+}
+
+static void
+assertion_failed(const char *file, int line, isc_assertiontype_t type,
+ const char *cond)
+{
+ /*
+ * Handle assertion failures.
+ */
+
+ if (ns_g_lctx != NULL) {
+ /*
+ * Reset the assetion callback in case it is the log
+ * routines causing the assertion.
+ */
+ isc_assertion_setcallback(NULL);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "%s:%d: %s(%s) failed", file, line,
+ isc_assertion_typetotext(type), cond);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "exiting (due to assertion failure)");
+ } else {
+ fprintf(stderr, "%s:%d: %s(%s) failed\n",
+ file, line, isc_assertion_typetotext(type), cond);
+ fflush(stderr);
+ }
+
+ if (ns_g_coreok)
+ abort();
+ exit(1);
+}
+
+static void
+library_fatal_error(const char *file, int line, const char *format,
+ va_list args) ISC_FORMAT_PRINTF(3, 0);
+
+static void
+library_fatal_error(const char *file, int line, const char *format,
+ va_list args)
+{
+ /*
+ * Handle isc_error_fatal() calls from our libraries.
+ */
+
+ if (ns_g_lctx != NULL) {
+ /*
+ * Reset the error callback in case it is the log
+ * routines causing the assertion.
+ */
+ isc_error_setfatal(NULL);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "%s:%d: fatal error:", file, line);
+ isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ format, args);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "exiting (due to fatal error in library)");
+ } else {
+ fprintf(stderr, "%s:%d: fatal error: ", file, line);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+
+ if (ns_g_coreok)
+ abort();
+ exit(1);
+}
+
+static void
+library_unexpected_error(const char *file, int line, const char *format,
+ va_list args) ISC_FORMAT_PRINTF(3, 0);
+
+static void
+library_unexpected_error(const char *file, int line, const char *format,
+ va_list args)
+{
+ /*
+ * Handle isc_error_unexpected() calls from our libraries.
+ */
+
+ if (ns_g_lctx != NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_ERROR,
+ "%s:%d: unexpected error:", file, line);
+ isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_ERROR,
+ format, args);
+ } else {
+ fprintf(stderr, "%s:%d: fatal error: ", file, line);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+}
+
+static void
+lwresd_usage(void) {
+ fprintf(stderr,
+ "usage: lwresd [-4|-6] [-c conffile | -C resolvconffile] "
+ "[-d debuglevel]\n"
+ " [-f|-g] [-n number_of_cpus] [-p port] "
+ "[-P listen-port] [-s]\n"
+ " [-t chrootdir] [-u username] [-i pidfile]\n"
+ " [-m {usage|trace|record|size|mctx}]\n");
+}
+
+static void
+usage(void) {
+ if (ns_g_lwresdonly) {
+ lwresd_usage();
+ return;
+ }
+ fprintf(stderr,
+ "usage: named [-4|-6] [-c conffile] [-d debuglevel] "
+ "[-f|-g] [-n number_of_cpus]\n"
+ " [-p port] [-s] [-t chrootdir] [-u username]\n"
+ " [-m {usage|trace|record|size|mctx}]\n");
+}
+
+static void
+save_command_line(int argc, char *argv[]) {
+ int i;
+ char *src;
+ char *dst;
+ char *eob;
+ const char truncated[] = "...";
+ isc_boolean_t quoted = ISC_FALSE;
+
+ dst = saved_command_line;
+ eob = saved_command_line + sizeof(saved_command_line);
+
+ for (i = 1; i < argc && dst < eob; i++) {
+ *dst++ = ' ';
+
+ src = argv[i];
+ while (*src != '\0' && dst < eob) {
+ /*
+ * This won't perfectly produce a shell-independent
+ * pastable command line in all circumstances, but
+ * comes close, and for practical purposes will
+ * nearly always be fine.
+ */
+ if (quoted || isalnum(*src & 0xff) ||
+ *src == '-' || *src == '_' ||
+ *src == '.' || *src == '/') {
+ *dst++ = *src++;
+ quoted = ISC_FALSE;
+ } else {
+ *dst++ = '\\';
+ quoted = ISC_TRUE;
+ }
+ }
+ }
+
+ INSIST(sizeof(saved_command_line) >= sizeof(truncated));
+
+ if (dst == eob)
+ strcpy(eob - sizeof(truncated), truncated);
+ else
+ *dst = '\0';
+}
+
+static int
+parse_int(char *arg, const char *desc) {
+ char *endp;
+ int tmp;
+ long int ltmp;
+
+ ltmp = strtol(arg, &endp, 10);
+ tmp = (int) ltmp;
+ if (*endp != '\0')
+ ns_main_earlyfatal("%s '%s' must be numeric", desc, arg);
+ if (tmp < 0 || tmp != ltmp)
+ ns_main_earlyfatal("%s '%s' out of range", desc, arg);
+ return (tmp);
+}
+
+static struct flag_def {
+ const char *name;
+ unsigned int value;
+} mem_debug_flags[] = {
+ { "trace", ISC_MEM_DEBUGTRACE },
+ { "record", ISC_MEM_DEBUGRECORD },
+ { "usage", ISC_MEM_DEBUGUSAGE },
+ { "size", ISC_MEM_DEBUGSIZE },
+ { "mctx", ISC_MEM_DEBUGCTX },
+ { NULL, 0 }
+};
+
+static void
+set_flags(const char *arg, struct flag_def *defs, unsigned int *ret) {
+ for (;;) {
+ const struct flag_def *def;
+ const char *end = strchr(arg, ',');
+ int arglen;
+ if (end == NULL)
+ end = arg + strlen(arg);
+ arglen = end - arg;
+ for (def = defs; def->name != NULL; def++) {
+ if (arglen == (int)strlen(def->name) &&
+ memcmp(arg, def->name, arglen) == 0) {
+ *ret |= def->value;
+ goto found;
+ }
+ }
+ ns_main_earlyfatal("unrecognized flag '%.*s'", arglen, arg);
+ found:
+ if (*end == '\0')
+ break;
+ arg = end + 1;
+ }
+}
+
+static void
+parse_command_line(int argc, char *argv[]) {
+ int ch;
+ int port;
+ isc_boolean_t disable6 = ISC_FALSE;
+ isc_boolean_t disable4 = ISC_FALSE;
+
+ save_command_line(argc, argv);
+
+ isc_commandline_errprint = ISC_FALSE;
+ while ((ch = isc_commandline_parse(argc, argv,
+ "46c:C:d:fgi:lm:n:N:p:P:st:u:vx:")) != -1) {
+ switch (ch) {
+ case '4':
+ if (disable4)
+ ns_main_earlyfatal("cannot specify -4 and -6");
+ if (isc_net_probeipv4() != ISC_R_SUCCESS)
+ ns_main_earlyfatal("IPv4 not supported by OS");
+ isc_net_disableipv6();
+ disable6 = ISC_TRUE;
+ break;
+ case '6':
+ if (disable6)
+ ns_main_earlyfatal("cannot specify -4 and -6");
+ if (isc_net_probeipv6() != ISC_R_SUCCESS)
+ ns_main_earlyfatal("IPv6 not supported by OS");
+ isc_net_disableipv4();
+ disable4 = ISC_TRUE;
+ break;
+ case 'c':
+ ns_g_conffile = isc_commandline_argument;
+ lwresd_g_conffile = isc_commandline_argument;
+ if (lwresd_g_useresolvconf)
+ ns_main_earlyfatal("cannot specify -c and -C");
+ ns_g_conffileset = ISC_TRUE;
+ break;
+ case 'C':
+ lwresd_g_resolvconffile = isc_commandline_argument;
+ if (ns_g_conffileset)
+ ns_main_earlyfatal("cannot specify -c and -C");
+ lwresd_g_useresolvconf = ISC_TRUE;
+ break;
+ case 'd':
+ ns_g_debuglevel = parse_int(isc_commandline_argument,
+ "debug level");
+ break;
+ case 'f':
+ ns_g_foreground = ISC_TRUE;
+ break;
+ case 'g':
+ ns_g_foreground = ISC_TRUE;
+ ns_g_logstderr = ISC_TRUE;
+ break;
+ /* XXXBEW -i should be removed */
+ case 'i':
+ lwresd_g_defaultpidfile = isc_commandline_argument;
+ break;
+ case 'l':
+ ns_g_lwresdonly = ISC_TRUE;
+ break;
+ case 'm':
+ set_flags(isc_commandline_argument, mem_debug_flags,
+ &isc_mem_debugging);
+ break;
+ case 'N': /* Deprecated. */
+ case 'n':
+ ns_g_cpus = parse_int(isc_commandline_argument,
+ "number of cpus");
+ if (ns_g_cpus == 0)
+ ns_g_cpus = 1;
+ break;
+ case 'p':
+ port = parse_int(isc_commandline_argument, "port");
+ if (port < 1 || port > 65535)
+ ns_main_earlyfatal("port '%s' out of range",
+ isc_commandline_argument);
+ ns_g_port = port;
+ break;
+ /* XXXBEW Should -P be removed? */
+ case 'P':
+ port = parse_int(isc_commandline_argument, "port");
+ if (port < 1 || port > 65535)
+ ns_main_earlyfatal("port '%s' out of range",
+ isc_commandline_argument);
+ lwresd_g_listenport = port;
+ break;
+ case 's':
+ /* XXXRTH temporary syntax */
+ want_stats = ISC_TRUE;
+ break;
+ case 't':
+ /* XXXJAB should we make a copy? */
+ ns_g_chrootdir = isc_commandline_argument;
+ break;
+ case 'u':
+ ns_g_username = isc_commandline_argument;
+ break;
+ case 'v':
+ printf("BIND %s\n", ns_g_version);
+ exit(0);
+ case '?':
+ usage();
+ ns_main_earlyfatal("unknown option '-%c'",
+ isc_commandline_option);
+ default:
+ ns_main_earlyfatal("parsing options returned %d", ch);
+ }
+ }
+
+ argc -= isc_commandline_index;
+ argv += isc_commandline_index;
+
+ if (argc > 0) {
+ usage();
+ ns_main_earlyfatal("extra command line arguments");
+ }
+}
+
+static isc_result_t
+create_managers(void) {
+ isc_result_t result;
+#ifdef ISC_PLATFORM_USETHREADS
+ unsigned int cpus_detected;
+#endif
+
+#ifdef ISC_PLATFORM_USETHREADS
+ cpus_detected = isc_os_ncpus();
+ if (ns_g_cpus == 0)
+ ns_g_cpus = cpus_detected;
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "found %u CPU%s, using %u worker thread%s",
+ cpus_detected, cpus_detected == 1 ? "" : "s",
+ ns_g_cpus, ns_g_cpus == 1 ? "" : "s");
+#else
+ ns_g_cpus = 1;
+#endif
+ result = isc_taskmgr_create(ns_g_mctx, ns_g_cpus, 0, &ns_g_taskmgr);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_taskmgr_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = isc_timermgr_create(ns_g_mctx, &ns_g_timermgr);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_timermgr_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = isc_socketmgr_create(ns_g_mctx, &ns_g_socketmgr);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socketmgr_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = isc_entropy_create(ns_g_mctx, &ns_g_entropy);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_entropy_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = isc_hash_create(ns_g_mctx, ns_g_entropy, DNS_NAME_MAXWIRE);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_hash_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+destroy_managers(void) {
+ ns_lwresd_shutdown();
+
+ isc_entropy_detach(&ns_g_entropy);
+ if (ns_g_fallbackentropy != NULL)
+ isc_entropy_detach(&ns_g_fallbackentropy);
+
+ /*
+ * isc_taskmgr_destroy() will block until all tasks have exited,
+ */
+ isc_taskmgr_destroy(&ns_g_taskmgr);
+ isc_timermgr_destroy(&ns_g_timermgr);
+ isc_socketmgr_destroy(&ns_g_socketmgr);
+
+ /*
+ * isc_hash_destroy() cannot be called as long as a resolver may be
+ * running. Calling this after isc_taskmgr_destroy() ensures the
+ * call is safe.
+ */
+ isc_hash_destroy();
+}
+
+static void
+setup(void) {
+ isc_result_t result;
+#ifdef HAVE_LIBSCF
+ char *instance = NULL;
+#endif
+
+ /*
+ * Get the user and group information before changing the root
+ * directory, so the administrator does not need to keep a copy
+ * of the user and group databases in the chroot'ed environment.
+ */
+ ns_os_inituserinfo(ns_g_username);
+
+ /*
+ * Initialize time conversion information
+ */
+ ns_os_tzset();
+
+ ns_os_opendevnull();
+
+#ifdef HAVE_LIBSCF
+ /* Check if named is under smf control, before chroot. */
+ result = ns_smf_get_instance(&instance, 0, ns_g_mctx);
+ /* We don't care about instance, just check if we got one. */
+ if (result == ISC_R_SUCCESS)
+ ns_smf_got_instance = 1;
+ else
+ ns_smf_got_instance = 0;
+ if (instance != NULL)
+ isc_mem_free(ns_g_mctx, instance);
+#endif /* HAVE_LIBSCF */
+
+#ifdef PATH_RANDOMDEV
+ /*
+ * Initialize system's random device as fallback entropy source
+ * if running chroot'ed.
+ */
+ if (ns_g_chrootdir != NULL) {
+ result = isc_entropy_create(ns_g_mctx, &ns_g_fallbackentropy);
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("isc_entropy_create() failed: %s",
+ isc_result_totext(result));
+
+ result = isc_entropy_createfilesource(ns_g_fallbackentropy,
+ PATH_RANDOMDEV);
+ if (result != ISC_R_SUCCESS) {
+ ns_main_earlywarning("could not open pre-chroot "
+ "entropy source %s: %s",
+ PATH_RANDOMDEV,
+ isc_result_totext(result));
+ isc_entropy_detach(&ns_g_fallbackentropy);
+ }
+ }
+#endif
+
+ ns_os_chroot(ns_g_chrootdir);
+
+ /*
+ * For operating systems which have a capability mechanism, now
+ * is the time to switch to minimal privs and change our user id.
+ * On traditional UNIX systems, this call will be a no-op, and we
+ * will change the user ID after reading the config file the first
+ * time. (We need to read the config file to know which possibly
+ * privileged ports to bind() to.)
+ */
+ ns_os_minprivs();
+
+ result = ns_log_init(ISC_TF(ns_g_username != NULL));
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("ns_log_init() failed: %s",
+ isc_result_totext(result));
+
+ /*
+ * Now is the time to daemonize (if we're not running in the
+ * foreground). We waited until now because we wanted to get
+ * a valid logging context setup. We cannot daemonize any later,
+ * because calling create_managers() will create threads, which
+ * would be lost after fork().
+ */
+ if (!ns_g_foreground)
+ ns_os_daemonize();
+
+ /*
+ * We call isc_app_start() here as some versions of FreeBSD's fork()
+ * destroys all the signal handling it sets up.
+ */
+ result = isc_app_start();
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("isc_app_start() failed: %s",
+ isc_result_totext(result));
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE, "starting BIND %s%s", ns_g_version,
+ saved_command_line);
+
+ /*
+ * Get the initial resource limits.
+ */
+ (void)isc_resource_getlimit(isc_resource_stacksize,
+ &ns_g_initstacksize);
+ (void)isc_resource_getlimit(isc_resource_datasize,
+ &ns_g_initdatasize);
+ (void)isc_resource_getlimit(isc_resource_coresize,
+ &ns_g_initcoresize);
+ (void)isc_resource_getlimit(isc_resource_openfiles,
+ &ns_g_initopenfiles);
+
+ /*
+ * If the named configuration filename is relative, prepend the current
+ * directory's name before possibly changing to another directory.
+ */
+ if (! isc_file_isabsolute(ns_g_conffile)) {
+ result = isc_file_absolutepath(ns_g_conffile,
+ absolute_conffile,
+ sizeof(absolute_conffile));
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("could not construct absolute path of "
+ "configuration file: %s",
+ isc_result_totext(result));
+ ns_g_conffile = absolute_conffile;
+ }
+
+ result = create_managers();
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("create_managers() failed: %s",
+ isc_result_totext(result));
+
+ ns_builtin_init();
+
+ /*
+ * Add calls to register sdb drivers here.
+ */
+ /* xxdb_init(); */
+
+#ifdef DLZ
+ /*
+ * Registyer any DLZ drivers.
+ */
+ result = dlz_drivers_init();
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("dlz_drivers_init() failed: %s",
+ isc_result_totext(result));
+#endif
+
+ ns_server_create(ns_g_mctx, &ns_g_server);
+}
+
+static void
+cleanup(void) {
+ destroy_managers();
+
+ ns_server_destroy(&ns_g_server);
+
+ ns_builtin_deinit();
+
+ /*
+ * Add calls to unregister sdb drivers here.
+ */
+ /* xxdb_clear(); */
+
+#ifdef DLZ
+ /*
+ * Unregister any DLZ drivers.
+ */
+ dlz_drivers_clear();
+#endif
+
+ dns_name_destroy();
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE, "exiting");
+ ns_log_shutdown();
+}
+
+static char *memstats = NULL;
+
+void
+ns_main_setmemstats(const char *filename) {
+ /*
+ * Caller has to ensure locking.
+ */
+
+ if (memstats != NULL) {
+ free(memstats);
+ memstats = NULL;
+ }
+ if (filename == NULL)
+ return;
+ memstats = malloc(strlen(filename) + 1);
+ if (memstats)
+ strcpy(memstats, filename);
+}
+
+#ifdef HAVE_LIBSCF
+/*
+ * Get FMRI for the named process.
+ */
+isc_result_t
+ns_smf_get_instance(char **ins_name, int debug, isc_mem_t *mctx) {
+ scf_handle_t *h = NULL;
+ int namelen;
+ char *instance;
+
+ REQUIRE(ins_name != NULL && *ins_name == NULL);
+
+ if ((h = scf_handle_create(SCF_VERSION)) == NULL) {
+ if (debug)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "scf_handle_create() failed: %s",
+ scf_strerror(scf_error()));
+ return (ISC_R_FAILURE);
+ }
+
+ if (scf_handle_bind(h) == -1) {
+ if (debug)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "scf_handle_bind() failed: %s",
+ scf_strerror(scf_error()));
+ scf_handle_destroy(h);
+ return (ISC_R_FAILURE);
+ }
+
+ if ((namelen = scf_myname(h, NULL, 0)) == -1) {
+ if (debug)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "scf_myname() failed: %s",
+ scf_strerror(scf_error()));
+ scf_handle_destroy(h);
+ return (ISC_R_FAILURE);
+ }
+
+ if ((instance = isc_mem_allocate(mctx, namelen + 1)) == NULL) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "ns_smf_get_instance memory "
+ "allocation failed: %s",
+ isc_result_totext(ISC_R_NOMEMORY));
+ scf_handle_destroy(h);
+ return (ISC_R_FAILURE);
+ }
+
+ if (scf_myname(h, instance, namelen + 1) == -1) {
+ if (debug)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "scf_myname() failed: %s",
+ scf_strerror(scf_error()));
+ scf_handle_destroy(h);
+ isc_mem_free(mctx, instance);
+ return (ISC_R_FAILURE);
+ }
+
+ scf_handle_destroy(h);
+ *ins_name = instance;
+ return (ISC_R_SUCCESS);
+}
+#endif /* HAVE_LIBSCF */
+
+int
+main(int argc, char *argv[]) {
+ isc_result_t result;
+#ifdef HAVE_LIBSCF
+ char *instance = NULL;
+#endif
+
+ /*
+ * Record version in core image.
+ * strings named.core | grep "named version:"
+ */
+ strlcat(version,
+#ifdef __DATE__
+ "named version: BIND " VERSION " (" __DATE__ ")",
+#else
+ "named version: BIND " VERSION,
+#endif
+ sizeof(version));
+ result = isc_file_progname(*argv, program_name, sizeof(program_name));
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("program name too long");
+
+ if (strcmp(program_name, "lwresd") == 0)
+ ns_g_lwresdonly = ISC_TRUE;
+
+ isc_assertion_setcallback(assertion_failed);
+ isc_error_setfatal(library_fatal_error);
+ isc_error_setunexpected(library_unexpected_error);
+
+ ns_os_init(program_name);
+
+ dns_result_register();
+ dst_result_register();
+ isccc_result_register();
+
+ parse_command_line(argc, argv);
+
+ /*
+ * Warn about common configuration error.
+ */
+ if (ns_g_chrootdir != NULL) {
+ int len = strlen(ns_g_chrootdir);
+ if (strncmp(ns_g_chrootdir, ns_g_conffile, len) == 0 &&
+ (ns_g_conffile[len] == '/' || ns_g_conffile[len] == '\\'))
+ ns_main_earlywarning("config filename (-c %s) contains "
+ "chroot path (-t %s)",
+ ns_g_conffile, ns_g_chrootdir);
+ }
+
+ result = isc_mem_create(0, 0, &ns_g_mctx);
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("isc_mem_create() failed: %s",
+ isc_result_totext(result));
+
+ setup();
+
+ /*
+ * Start things running and then wait for a shutdown request
+ * or reload.
+ */
+ do {
+ result = isc_app_run();
+
+ if (result == ISC_R_RELOAD) {
+ ns_server_reloadwanted(ns_g_server);
+ } else if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_app_run(): %s",
+ isc_result_totext(result));
+ /*
+ * Force exit.
+ */
+ result = ISC_R_SUCCESS;
+ }
+ } while (result != ISC_R_SUCCESS);
+
+#ifdef HAVE_LIBSCF
+ if (ns_smf_want_disable == 1) {
+ result = ns_smf_get_instance(&instance, 1, ns_g_mctx);
+ if (result == ISC_R_SUCCESS && instance != NULL) {
+ if (smf_disable_instance(instance, 0) != 0)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "smf_disable_instance() "
+ "failed for %s : %s",
+ instance,
+ scf_strerror(scf_error()));
+ }
+ if (instance != NULL)
+ isc_mem_free(ns_g_mctx, instance);
+ }
+#endif /* HAVE_LIBSCF */
+
+ cleanup();
+
+ if (want_stats) {
+ isc_mem_stats(ns_g_mctx, stdout);
+ isc_mutex_stats(stdout);
+ }
+ if (memstats != NULL) {
+ FILE *fp = NULL;
+ result = isc_stdio_open(memstats, "w", &fp);
+ if (result == ISC_R_SUCCESS) {
+ isc_mem_stats(ns_g_mctx, fp);
+ isc_mutex_stats(fp);
+ isc_stdio_close(fp);
+ }
+ }
+ isc_mem_destroy(&ns_g_mctx);
+ isc_mem_checkdestroyed(stderr);
+
+ ns_main_setmemstats(NULL);
+
+ isc_app_finish();
+
+ ns_os_closedevnull();
+
+ ns_os_shutdown();
+
+ return (0);
+}
diff --git a/bin/named/named.8 b/bin/named/named.8
new file mode 100644
index 0000000..f5e8230
--- /dev/null
+++ b/bin/named/named.8
@@ -0,0 +1,236 @@
+.\" Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (C) 2000, 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: named.8,v 1.20.18.15 2007/06/20 02:26:58 marka Exp $
+.\"
+.hy 0
+.ad l
+.\" Title: named
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.71.1 <http://docbook.sf.net/>
+.\" Date: June 30, 2000
+.\" Manual: BIND9
+.\" Source: BIND9
+.\"
+.TH "NAMED" "8" "June 30, 2000" "BIND9" "BIND9"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+named \- Internet domain name server
+.SH "SYNOPSIS"
+.HP 6
+\fBnamed\fR [\fB\-4\fR] [\fB\-6\fR] [\fB\-c\ \fR\fB\fIconfig\-file\fR\fR] [\fB\-d\ \fR\fB\fIdebug\-level\fR\fR] [\fB\-f\fR] [\fB\-g\fR] [\fB\-m\ \fR\fB\fIflag\fR\fR] [\fB\-n\ \fR\fB\fI#cpus\fR\fR] [\fB\-p\ \fR\fB\fIport\fR\fR] [\fB\-s\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-x\ \fR\fB\fIcache\-file\fR\fR]
+.SH "DESCRIPTION"
+.PP
+\fBnamed\fR
+is a Domain Name System (DNS) server, part of the BIND 9 distribution from ISC. For more information on the DNS, see RFCs 1033, 1034, and 1035.
+.PP
+When invoked without arguments,
+\fBnamed\fR
+will read the default configuration file
+\fI/etc/named.conf\fR, read any initial data, and listen for queries.
+.SH "OPTIONS"
+.PP
+\-4
+.RS 4
+Use IPv4 only even if the host machine is capable of IPv6.
+\fB\-4\fR
+and
+\fB\-6\fR
+are mutually exclusive.
+.RE
+.PP
+\-6
+.RS 4
+Use IPv6 only even if the host machine is capable of IPv4.
+\fB\-4\fR
+and
+\fB\-6\fR
+are mutually exclusive.
+.RE
+.PP
+\-c \fIconfig\-file\fR
+.RS 4
+Use
+\fIconfig\-file\fR
+as the configuration file instead of the default,
+\fI/etc/named.conf\fR. To ensure that reloading the configuration file continues to work after the server has changed its working directory due to to a possible
+\fBdirectory\fR
+option in the configuration file,
+\fIconfig\-file\fR
+should be an absolute pathname.
+.RE
+.PP
+\-d \fIdebug\-level\fR
+.RS 4
+Set the daemon's debug level to
+\fIdebug\-level\fR. Debugging traces from
+\fBnamed\fR
+become more verbose as the debug level increases.
+.RE
+.PP
+\-f
+.RS 4
+Run the server in the foreground (i.e. do not daemonize).
+.RE
+.PP
+\-g
+.RS 4
+Run the server in the foreground and force all logging to
+\fIstderr\fR.
+.RE
+.PP
+\-m \fIflag\fR
+.RS 4
+Turn on memory usage debugging flags. Possible flags are
+\fIusage\fR,
+\fItrace\fR,
+\fIrecord\fR,
+\fIsize\fR, and
+\fImctx\fR. These correspond to the ISC_MEM_DEBUGXXXX flags described in
+\fI<isc/mem.h>\fR.
+.RE
+.PP
+\-n \fI#cpus\fR
+.RS 4
+Create
+\fI#cpus\fR
+worker threads to take advantage of multiple CPUs. If not specified,
+\fBnamed\fR
+will try to determine the number of CPUs present and create one thread per CPU. If it is unable to determine the number of CPUs, a single worker thread will be created.
+.RE
+.PP
+\-p \fIport\fR
+.RS 4
+Listen for queries on port
+\fIport\fR. If not specified, the default is port 53.
+.RE
+.PP
+\-s
+.RS 4
+Write memory usage statistics to
+\fIstdout\fR
+on exit.
+.RS
+.B "Note:"
+This option is mainly of interest to BIND 9 developers and may be removed or changed in a future release.
+.RE
+.RE
+.PP
+\-t \fIdirectory\fR
+.RS 4
+Chroot to
+\fIdirectory\fR
+after processing the command line arguments, but before reading the configuration file.
+.RS
+.B "Warning:"
+This option should be used in conjunction with the
+\fB\-u\fR
+option, as chrooting a process running as root doesn't enhance security on most systems; the way
+\fBchroot(2)\fR
+is defined allows a process with root privileges to escape a chroot jail.
+.RE
+.RE
+.PP
+\-u \fIuser\fR
+.RS 4
+Setuid to
+\fIuser\fR
+after completing privileged operations, such as creating sockets that listen on privileged ports.
+.RS
+.B "Note:"
+On Linux,
+\fBnamed\fR
+uses the kernel's capability mechanism to drop all root privileges except the ability to
+\fBbind(2)\fR
+to a privileged port and set process resource limits. Unfortunately, this means that the
+\fB\-u\fR
+option only works when
+\fBnamed\fR
+is run on kernel 2.2.18 or later, or kernel 2.3.99\-pre3 or later, since previous kernels did not allow privileges to be retained after
+\fBsetuid(2)\fR.
+.RE
+.RE
+.PP
+\-v
+.RS 4
+Report the version number and exit.
+.RE
+.PP
+\-x \fIcache\-file\fR
+.RS 4
+Load data from
+\fIcache\-file\fR
+into the cache of the default view.
+.RS
+.B "Warning:"
+This option must not be used. It is only of interest to BIND 9 developers and may be removed or changed in a future release.
+.RE
+.RE
+.SH "SIGNALS"
+.PP
+In routine operation, signals should not be used to control the nameserver;
+\fBrndc\fR
+should be used instead.
+.PP
+SIGHUP
+.RS 4
+Force a reload of the server.
+.RE
+.PP
+SIGINT, SIGTERM
+.RS 4
+Shut down the server.
+.RE
+.PP
+The result of sending any other signals to the server is undefined.
+.SH "CONFIGURATION"
+.PP
+The
+\fBnamed\fR
+configuration file is too complex to describe in detail here. A complete description is provided in the
+BIND 9 Administrator Reference Manual.
+.SH "FILES"
+.PP
+\fI/etc/named.conf\fR
+.RS 4
+The default configuration file.
+.RE
+.PP
+\fI/var/run/named.pid\fR
+.RS 4
+The default process\-id file.
+.RE
+.SH "SEE ALSO"
+.PP
+RFC 1033,
+RFC 1034,
+RFC 1035,
+\fBnamed\-checkconf\fR(8),
+\fBnamed\-checkzone\fR(8),
+\fBrndc\fR(8),
+\fBlwresd\fR(8),
+\fBnamed.conf\fR(5),
+BIND 9 Administrator Reference Manual.
+.SH "AUTHOR"
+.PP
+Internet Systems Consortium
+.SH "COPYRIGHT"
+Copyright \(co 2004\-2007 Internet Systems Consortium, Inc. ("ISC")
+.br
+Copyright \(co 2000, 2001, 2003 Internet Software Consortium.
+.br
diff --git a/bin/named/named.conf.5 b/bin/named/named.conf.5
new file mode 100644
index 0000000..00c92a6
--- /dev/null
+++ b/bin/named/named.conf.5
@@ -0,0 +1,520 @@
+.\" Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+.\"
+.\" 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: named.conf.5,v 1.1.2.26 2007/08/19 23:26:13 marka Exp $
+.\"
+.hy 0
+.ad l
+.\" Title: \fInamed.conf\fR
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.71.1 <http://docbook.sf.net/>
+.\" Date: Aug 13, 2004
+.\" Manual: BIND9
+.\" Source: BIND9
+.\"
+.TH "\fINAMED.CONF\fR" "5" "Aug 13, 2004" "BIND9" "BIND9"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+named.conf \- configuration file for named
+.SH "SYNOPSIS"
+.HP 11
+\fBnamed.conf\fR
+.SH "DESCRIPTION"
+.PP
+\fInamed.conf\fR
+is the configuration file for
+\fBnamed\fR. Statements are enclosed in braces and terminated with a semi\-colon. Clauses in the statements are also semi\-colon terminated. The usual comment styles are supported:
+.PP
+C style: /* */
+.PP
+C++ style: // to end of line
+.PP
+Unix style: # to end of line
+.SH "ACL"
+.sp
+.RS 4
+.nf
+acl \fIstring\fR { \fIaddress_match_element\fR; ... };
+.fi
+.RE
+.SH "KEY"
+.sp
+.RS 4
+.nf
+key \fIdomain_name\fR {
+ algorithm \fIstring\fR;
+ secret \fIstring\fR;
+};
+.fi
+.RE
+.SH "MASTERS"
+.sp
+.RS 4
+.nf
+masters \fIstring\fR [ port \fIinteger\fR ] {
+ ( \fImasters\fR | \fIipv4_address\fR [port \fIinteger\fR] |
+ \fIipv6_address\fR [port \fIinteger\fR] ) [ key \fIstring\fR ]; ...
+};
+.fi
+.RE
+.SH "SERVER"
+.sp
+.RS 4
+.nf
+server ( \fIipv4_address\fR\fI[/prefixlen]\fR | \fIipv6_address\fR\fI[/prefixlen]\fR ) {
+ bogus \fIboolean\fR;
+ edns \fIboolean\fR;
+ edns\-udp\-size \fIinteger\fR;
+ max\-udp\-size \fIinteger\fR;
+ provide\-ixfr \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ keys \fIserver_key\fR;
+ transfers \fIinteger\fR;
+ transfer\-format ( many\-answers | one\-answer );
+ transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ support\-ixfr \fIboolean\fR; // obsolete
+};
+.fi
+.RE
+.SH "TRUSTED\-KEYS"
+.sp
+.RS 4
+.nf
+trusted\-keys {
+ \fIdomain_name\fR \fIflags\fR \fIprotocol\fR \fIalgorithm\fR \fIkey\fR; ...
+};
+.fi
+.RE
+.SH "CONTROLS"
+.sp
+.RS 4
+.nf
+controls {
+ inet ( \fIipv4_address\fR | \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ]
+ allow { \fIaddress_match_element\fR; ... }
+ [ keys { \fIstring\fR; ... } ];
+ unix \fIunsupported\fR; // not implemented
+};
+.fi
+.RE
+.SH "LOGGING"
+.sp
+.RS 4
+.nf
+logging {
+ channel \fIstring\fR {
+ file \fIlog_file\fR;
+ syslog \fIoptional_facility\fR;
+ null;
+ stderr;
+ severity \fIlog_severity\fR;
+ print\-time \fIboolean\fR;
+ print\-severity \fIboolean\fR;
+ print\-category \fIboolean\fR;
+ };
+ category \fIstring\fR { \fIstring\fR; ... };
+};
+.fi
+.RE
+.SH "LWRES"
+.sp
+.RS 4
+.nf
+lwres {
+ listen\-on [ port \fIinteger\fR ] {
+ ( \fIipv4_address\fR | \fIipv6_address\fR ) [ port \fIinteger\fR ]; ...
+ };
+ view \fIstring\fR \fIoptional_class\fR;
+ search { \fIstring\fR; ... };
+ ndots \fIinteger\fR;
+};
+.fi
+.RE
+.SH "OPTIONS"
+.sp
+.RS 4
+.nf
+options {
+ avoid\-v4\-udp\-ports { \fIport\fR; ... };
+ avoid\-v6\-udp\-ports { \fIport\fR; ... };
+ blackhole { \fIaddress_match_element\fR; ... };
+ coresize \fIsize\fR;
+ datasize \fIsize\fR;
+ directory \fIquoted_string\fR;
+ dump\-file \fIquoted_string\fR;
+ files \fIsize\fR;
+ heartbeat\-interval \fIinteger\fR;
+ host\-statistics \fIboolean\fR; // not implemented
+ host\-statistics\-max \fInumber\fR; // not implemented
+ hostname ( \fIquoted_string\fR | none );
+ interface\-interval \fIinteger\fR;
+ listen\-on [ port \fIinteger\fR ] { \fIaddress_match_element\fR; ... };
+ listen\-on\-v6 [ port \fIinteger\fR ] { \fIaddress_match_element\fR; ... };
+ match\-mapped\-addresses \fIboolean\fR;
+ memstatistics\-file \fIquoted_string\fR;
+ pid\-file ( \fIquoted_string\fR | none );
+ port \fIinteger\fR;
+ querylog \fIboolean\fR;
+ recursing\-file \fIquoted_string\fR;
+ random\-device \fIquoted_string\fR;
+ recursive\-clients \fIinteger\fR;
+ serial\-query\-rate \fIinteger\fR;
+ server\-id ( \fIquoted_string\fR | none |;
+ stacksize \fIsize\fR;
+ statistics\-file \fIquoted_string\fR;
+ statistics\-interval \fIinteger\fR; // not yet implemented
+ tcp\-clients \fIinteger\fR;
+ tcp\-listen\-queue \fIinteger\fR;
+ tkey\-dhkey \fIquoted_string\fR \fIinteger\fR;
+ tkey\-gssapi\-credential \fIquoted_string\fR;
+ tkey\-domain \fIquoted_string\fR;
+ transfers\-per\-ns \fIinteger\fR;
+ transfers\-in \fIinteger\fR;
+ transfers\-out \fIinteger\fR;
+ use\-ixfr \fIboolean\fR;
+ version ( \fIquoted_string\fR | none );
+ allow\-recursion { \fIaddress_match_element\fR; ... };
+ sortlist { \fIaddress_match_element\fR; ... };
+ topology { \fIaddress_match_element\fR; ... }; // not implemented
+ auth\-nxdomain \fIboolean\fR; // default changed
+ minimal\-responses \fIboolean\fR;
+ recursion \fIboolean\fR;
+ rrset\-order {
+ [ class \fIstring\fR ] [ type \fIstring\fR ]
+ [ name \fIquoted_string\fR ] \fIstring\fR \fIstring\fR; ...
+ };
+ provide\-ixfr \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ rfc2308\-type1 \fIboolean\fR; // not yet implemented
+ additional\-from\-auth \fIboolean\fR;
+ additional\-from\-cache \fIboolean\fR;
+ query\-source ( ( \fIipv4_address\fR | * ) | [ address ( \fIipv4_address\fR | * ) ] ) [ port ( \fIinteger\fR | * ) ];
+ query\-source\-v6 ( ( \fIipv6_address\fR | * ) | [ address ( \fIipv6_address\fR | * ) ] ) [ port ( \fIinteger\fR | * ) ];
+ cleaning\-interval \fIinteger\fR;
+ min\-roots \fIinteger\fR; // not implemented
+ lame\-ttl \fIinteger\fR;
+ max\-ncache\-ttl \fIinteger\fR;
+ max\-cache\-ttl \fIinteger\fR;
+ transfer\-format ( many\-answers | one\-answer );
+ max\-cache\-size \fIsize_no_default\fR;
+ max\-acache\-size \fIsize_no_default\fR;
+ clients\-per\-query \fInumber\fR;
+ max\-clients\-per\-query \fInumber\fR;
+ check\-names ( master | slave | response )
+ ( fail | warn | ignore );
+ check\-mx ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ cache\-file \fIquoted_string\fR; // test option
+ suppress\-initial\-notify \fIboolean\fR; // not yet implemented
+ preferred\-glue \fIstring\fR;
+ dual\-stack\-servers [ port \fIinteger\fR ] {
+ ( \fIquoted_string\fR [port \fIinteger\fR] |
+ \fIipv4_address\fR [port \fIinteger\fR] |
+ \fIipv6_address\fR [port \fIinteger\fR] ); ...
+ };
+ edns\-udp\-size \fIinteger\fR;
+ max\-udp\-size \fIinteger\fR;
+ root\-delegation\-only [ exclude { \fIquoted_string\fR; ... } ];
+ disable\-algorithms \fIstring\fR { \fIstring\fR; ... };
+ dnssec\-enable \fIboolean\fR;
+ dnssec\-validation \fIboolean\fR;
+ dnssec\-lookaside \fIstring\fR trust\-anchor \fIstring\fR;
+ dnssec\-must\-be\-secure \fIstring\fR \fIboolean\fR;
+ dnssec\-accept\-expired \fIboolean\fR;
+ empty\-server \fIstring\fR;
+ empty\-contact \fIstring\fR;
+ empty\-zones\-enable \fIboolean\fR;
+ disable\-empty\-zone \fIstring\fR;
+ dialup \fIdialuptype\fR;
+ ixfr\-from\-differences \fIixfrdiff\fR;
+ allow\-query { \fIaddress_match_element\fR; ... };
+ allow\-query\-cache { \fIaddress_match_element\fR; ... };
+ allow\-transfer { \fIaddress_match_element\fR; ... };
+ allow\-update { \fIaddress_match_element\fR; ... };
+ allow\-update\-forwarding { \fIaddress_match_element\fR; ... };
+ update\-check\-ksk \fIboolean\fR;
+ masterfile\-format ( text | raw );
+ notify \fInotifytype\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-delay \fIseconds\fR;
+ also\-notify [ port \fIinteger\fR ] { ( \fIipv4_address\fR | \fIipv6_address\fR )
+ [ port \fIinteger\fR ]; ... };
+ allow\-notify { \fIaddress_match_element\fR; ... };
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] {
+ ( \fIipv4_address\fR | \fIipv6_address\fR ) [ port \fIinteger\fR ]; ...
+ };
+ max\-journal\-size \fIsize_no_default\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ min\-refresh\-time \fIinteger\fR;
+ multi\-master \fIboolean\fR;
+ sig\-validity\-interval \fIinteger\fR;
+ transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ use\-alt\-transfer\-source \fIboolean\fR;
+ zone\-statistics \fIboolean\fR;
+ key\-directory \fIquoted_string\fR;
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ zero\-no\-soa\-ttl\-cache \fIboolean\fR;
+ allow\-v6\-synthesis { \fIaddress_match_element\fR; ... }; // obsolete
+ deallocate\-on\-exit \fIboolean\fR; // obsolete
+ fake\-iquery \fIboolean\fR; // obsolete
+ fetch\-glue \fIboolean\fR; // obsolete
+ has\-old\-clients \fIboolean\fR; // obsolete
+ maintain\-ixfr\-base \fIboolean\fR; // obsolete
+ max\-ixfr\-log\-size \fIsize\fR; // obsolete
+ multiple\-cnames \fIboolean\fR; // obsolete
+ named\-xfer \fIquoted_string\fR; // obsolete
+ serial\-queries \fIinteger\fR; // obsolete
+ treat\-cr\-as\-space \fIboolean\fR; // obsolete
+ use\-id\-pool \fIboolean\fR; // obsolete
+};
+.fi
+.RE
+.SH "VIEW"
+.sp
+.RS 4
+.nf
+view \fIstring\fR \fIoptional_class\fR {
+ match\-clients { \fIaddress_match_element\fR; ... };
+ match\-destinations { \fIaddress_match_element\fR; ... };
+ match\-recursive\-only \fIboolean\fR;
+ key \fIstring\fR {
+ algorithm \fIstring\fR;
+ secret \fIstring\fR;
+ };
+ zone \fIstring\fR \fIoptional_class\fR {
+ ...
+ };
+ server ( \fIipv4_address\fR\fI[/prefixlen]\fR | \fIipv6_address\fR\fI[/prefixlen]\fR ) {
+ ...
+ };
+ trusted\-keys {
+ \fIstring\fR \fIinteger\fR \fIinteger\fR \fIinteger\fR \fIquoted_string\fR; ...
+ };
+ allow\-recursion { \fIaddress_match_element\fR; ... };
+ sortlist { \fIaddress_match_element\fR; ... };
+ topology { \fIaddress_match_element\fR; ... }; // not implemented
+ auth\-nxdomain \fIboolean\fR; // default changed
+ minimal\-responses \fIboolean\fR;
+ recursion \fIboolean\fR;
+ rrset\-order {
+ [ class \fIstring\fR ] [ type \fIstring\fR ]
+ [ name \fIquoted_string\fR ] \fIstring\fR \fIstring\fR; ...
+ };
+ provide\-ixfr \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ rfc2308\-type1 \fIboolean\fR; // not yet implemented
+ additional\-from\-auth \fIboolean\fR;
+ additional\-from\-cache \fIboolean\fR;
+ query\-source ( ( \fIipv4_address\fR | * ) | [ address ( \fIipv4_address\fR | * ) ] ) [ port ( \fIinteger\fR | * ) ];
+ query\-source\-v6 ( ( \fIipv6_address\fR | * ) | [ address ( \fIipv6_address\fR | * ) ] ) [ port ( \fIinteger\fR | * ) ];
+ cleaning\-interval \fIinteger\fR;
+ min\-roots \fIinteger\fR; // not implemented
+ lame\-ttl \fIinteger\fR;
+ max\-ncache\-ttl \fIinteger\fR;
+ max\-cache\-ttl \fIinteger\fR;
+ transfer\-format ( many\-answers | one\-answer );
+ max\-cache\-size \fIsize_no_default\fR;
+ max\-acache\-size \fIsize_no_default\fR;
+ clients\-per\-query \fInumber\fR;
+ max\-clients\-per\-query \fInumber\fR;
+ check\-names ( master | slave | response )
+ ( fail | warn | ignore );
+ check\-mx ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ cache\-file \fIquoted_string\fR; // test option
+ suppress\-initial\-notify \fIboolean\fR; // not yet implemented
+ preferred\-glue \fIstring\fR;
+ dual\-stack\-servers [ port \fIinteger\fR ] {
+ ( \fIquoted_string\fR [port \fIinteger\fR] |
+ \fIipv4_address\fR [port \fIinteger\fR] |
+ \fIipv6_address\fR [port \fIinteger\fR] ); ...
+ };
+ edns\-udp\-size \fIinteger\fR;
+ max\-udp\-size \fIinteger\fR;
+ root\-delegation\-only [ exclude { \fIquoted_string\fR; ... } ];
+ disable\-algorithms \fIstring\fR { \fIstring\fR; ... };
+ dnssec\-enable \fIboolean\fR;
+ dnssec\-validation \fIboolean\fR;
+ dnssec\-lookaside \fIstring\fR trust\-anchor \fIstring\fR;
+ dnssec\-must\-be\-secure \fIstring\fR \fIboolean\fR;
+ dnssec\-accept\-expired \fIboolean\fR;
+ empty\-server \fIstring\fR;
+ empty\-contact \fIstring\fR;
+ empty\-zones\-enable \fIboolean\fR;
+ disable\-empty\-zone \fIstring\fR;
+ dialup \fIdialuptype\fR;
+ ixfr\-from\-differences \fIixfrdiff\fR;
+ allow\-query { \fIaddress_match_element\fR; ... };
+ allow\-query\-cache { \fIaddress_match_element\fR; ... };
+ allow\-transfer { \fIaddress_match_element\fR; ... };
+ allow\-update { \fIaddress_match_element\fR; ... };
+ allow\-update\-forwarding { \fIaddress_match_element\fR; ... };
+ update\-check\-ksk \fIboolean\fR;
+ masterfile\-format ( text | raw );
+ notify \fInotifytype\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-delay \fIseconds\fR;
+ also\-notify [ port \fIinteger\fR ] { ( \fIipv4_address\fR | \fIipv6_address\fR )
+ [ port \fIinteger\fR ]; ... };
+ allow\-notify { \fIaddress_match_element\fR; ... };
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] {
+ ( \fIipv4_address\fR | \fIipv6_address\fR ) [ port \fIinteger\fR ]; ...
+ };
+ max\-journal\-size \fIsize_no_default\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ min\-refresh\-time \fIinteger\fR;
+ multi\-master \fIboolean\fR;
+ sig\-validity\-interval \fIinteger\fR;
+ transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ use\-alt\-transfer\-source \fIboolean\fR;
+ zone\-statistics \fIboolean\fR;
+ key\-directory \fIquoted_string\fR;
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ zero\-no\-soa\-ttl\-cache \fIboolean\fR;
+ allow\-v6\-synthesis { \fIaddress_match_element\fR; ... }; // obsolete
+ fetch\-glue \fIboolean\fR; // obsolete
+ maintain\-ixfr\-base \fIboolean\fR; // obsolete
+ max\-ixfr\-log\-size \fIsize\fR; // obsolete
+};
+.fi
+.RE
+.SH "ZONE"
+.sp
+.RS 4
+.nf
+zone \fIstring\fR \fIoptional_class\fR {
+ type ( master | slave | stub | hint |
+ forward | delegation\-only );
+ file \fIquoted_string\fR;
+ masters [ port \fIinteger\fR ] {
+ ( \fImasters\fR |
+ \fIipv4_address\fR [port \fIinteger\fR] |
+ \fIipv6_address\fR [ port \fIinteger\fR ] ) [ key \fIstring\fR ]; ...
+ };
+ database \fIstring\fR;
+ delegation\-only \fIboolean\fR;
+ check\-names ( fail | warn | ignore );
+ check\-mx ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ dialup \fIdialuptype\fR;
+ ixfr\-from\-differences \fIboolean\fR;
+ journal \fIquoted_string\fR;
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ allow\-query { \fIaddress_match_element\fR; ... };
+ allow\-transfer { \fIaddress_match_element\fR; ... };
+ allow\-update { \fIaddress_match_element\fR; ... };
+ allow\-update\-forwarding { \fIaddress_match_element\fR; ... };
+ update\-policy {
+ ( grant | deny ) \fIstring\fR
+ ( name | subdomain | wildcard | self ) \fIstring\fR
+ \fIrrtypelist\fR; ...
+ };
+ update\-check\-ksk \fIboolean\fR;
+ masterfile\-format ( text | raw );
+ notify \fInotifytype\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-delay \fIseconds\fR;
+ also\-notify [ port \fIinteger\fR ] { ( \fIipv4_address\fR | \fIipv6_address\fR )
+ [ port \fIinteger\fR ]; ... };
+ allow\-notify { \fIaddress_match_element\fR; ... };
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] {
+ ( \fIipv4_address\fR | \fIipv6_address\fR ) [ port \fIinteger\fR ]; ...
+ };
+ max\-journal\-size \fIsize_no_default\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ min\-refresh\-time \fIinteger\fR;
+ multi\-master \fIboolean\fR;
+ sig\-validity\-interval \fIinteger\fR;
+ transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ use\-alt\-transfer\-source \fIboolean\fR;
+ zone\-statistics \fIboolean\fR;
+ key\-directory \fIquoted_string\fR;
+ ixfr\-base \fIquoted_string\fR; // obsolete
+ ixfr\-tmp\-file \fIquoted_string\fR; // obsolete
+ maintain\-ixfr\-base \fIboolean\fR; // obsolete
+ max\-ixfr\-log\-size \fIsize\fR; // obsolete
+ pubkey \fIinteger\fR \fIinteger\fR \fIinteger\fR \fIquoted_string\fR; // obsolete
+};
+.fi
+.RE
+.SH "FILES"
+.PP
+\fI/etc/named.conf\fR
+.SH "SEE ALSO"
+.PP
+\fBnamed\fR(8),
+\fBnamed\-checkconf\fR(8),
+\fBrndc\fR(8),
+BIND 9 Administrator Reference Manual.
+.SH "COPYRIGHT"
+Copyright \(co 2004\-2007 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook
new file mode 100644
index 0000000..e4cfcc7
--- /dev/null
+++ b/bin/named/named.conf.docbook
@@ -0,0 +1,597 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - 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: named.conf.docbook,v 1.1.2.29 2007/08/28 07:20:01 tbox Exp $ -->
+<refentry>
+ <refentryinfo>
+ <date>Aug 13, 2004</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle><filename>named.conf</filename></refentrytitle>
+ <manvolnum>5</manvolnum>
+ <refmiscinfo>BIND9</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname><filename>named.conf</filename></refname>
+ <refpurpose>configuration file for named</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2006</year>
+ <year>2007</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>named.conf</command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+ <para><filename>named.conf</filename> is the configuration file
+ for
+ <command>named</command>. Statements are enclosed
+ in braces and terminated with a semi-colon. Clauses in
+ the statements are also semi-colon terminated. The usual
+ comment styles are supported:
+ </para>
+ <para>
+ C style: /* */
+ </para>
+ <para>
+ C++ style: // to end of line
+ </para>
+ <para>
+ Unix style: # to end of line
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>ACL</title>
+ <literallayout>
+acl <replaceable>string</replaceable> { <replaceable>address_match_element</replaceable>; ... };
+
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>KEY</title>
+ <literallayout>
+key <replaceable>domain_name</replaceable> {
+ algorithm <replaceable>string</replaceable>;
+ secret <replaceable>string</replaceable>;
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>MASTERS</title>
+ <literallayout>
+masters <replaceable>string</replaceable> <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv6_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> ) <optional> key <replaceable>string</replaceable> </optional>; ...
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>SERVER</title>
+ <literallayout>
+server ( <replaceable>ipv4_address<optional>/prefixlen</optional></replaceable> | <replaceable>ipv6_address<optional>/prefixlen</optional></replaceable> ) {
+ bogus <replaceable>boolean</replaceable>;
+ edns <replaceable>boolean</replaceable>;
+ edns-udp-size <replaceable>integer</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ provide-ixfr <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ keys <replaceable>server_key</replaceable>;
+ transfers <replaceable>integer</replaceable>;
+ transfer-format ( many-answers | one-answer );
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+
+ support-ixfr <replaceable>boolean</replaceable>; // obsolete
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>TRUSTED-KEYS</title>
+ <literallayout>
+trusted-keys {
+ <replaceable>domain_name</replaceable> <replaceable>flags</replaceable> <replaceable>protocol</replaceable> <replaceable>algorithm</replaceable> <replaceable>key</replaceable>; ...
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>CONTROLS</title>
+ <literallayout>
+controls {
+ inet ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>
+ allow { <replaceable>address_match_element</replaceable>; ... }
+ <optional> keys { <replaceable>string</replaceable>; ... } </optional>;
+ unix <replaceable>unsupported</replaceable>; // not implemented
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>LOGGING</title>
+ <literallayout>
+logging {
+ channel <replaceable>string</replaceable> {
+ file <replaceable>log_file</replaceable>;
+ syslog <replaceable>optional_facility</replaceable>;
+ null;
+ stderr;
+ severity <replaceable>log_severity</replaceable>;
+ print-time <replaceable>boolean</replaceable>;
+ print-severity <replaceable>boolean</replaceable>;
+ print-category <replaceable>boolean</replaceable>;
+ };
+ category <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>LWRES</title>
+ <literallayout>
+lwres {
+ listen-on <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) <optional> port <replaceable>integer</replaceable> </optional>; ...
+ };
+ view <replaceable>string</replaceable> <replaceable>optional_class</replaceable>;
+ search { <replaceable>string</replaceable>; ... };
+ ndots <replaceable>integer</replaceable>;
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+ <literallayout>
+options {
+ avoid-v4-udp-ports { <replaceable>port</replaceable>; ... };
+ avoid-v6-udp-ports { <replaceable>port</replaceable>; ... };
+ blackhole { <replaceable>address_match_element</replaceable>; ... };
+ coresize <replaceable>size</replaceable>;
+ datasize <replaceable>size</replaceable>;
+ directory <replaceable>quoted_string</replaceable>;
+ dump-file <replaceable>quoted_string</replaceable>;
+ files <replaceable>size</replaceable>;
+ heartbeat-interval <replaceable>integer</replaceable>;
+ host-statistics <replaceable>boolean</replaceable>; // not implemented
+ host-statistics-max <replaceable>number</replaceable>; // not implemented
+ hostname ( <replaceable>quoted_string</replaceable> | none );
+ interface-interval <replaceable>integer</replaceable>;
+ listen-on <optional> port <replaceable>integer</replaceable> </optional> { <replaceable>address_match_element</replaceable>; ... };
+ listen-on-v6 <optional> port <replaceable>integer</replaceable> </optional> { <replaceable>address_match_element</replaceable>; ... };
+ match-mapped-addresses <replaceable>boolean</replaceable>;
+ memstatistics-file <replaceable>quoted_string</replaceable>;
+ pid-file ( <replaceable>quoted_string</replaceable> | none );
+ port <replaceable>integer</replaceable>;
+ querylog <replaceable>boolean</replaceable>;
+ recursing-file <replaceable>quoted_string</replaceable>;
+ random-device <replaceable>quoted_string</replaceable>;
+ recursive-clients <replaceable>integer</replaceable>;
+ serial-query-rate <replaceable>integer</replaceable>;
+ server-id ( <replaceable>quoted_string</replaceable> | none |;
+ stacksize <replaceable>size</replaceable>;
+ statistics-file <replaceable>quoted_string</replaceable>;
+ statistics-interval <replaceable>integer</replaceable>; // not yet implemented
+ tcp-clients <replaceable>integer</replaceable>;
+ tcp-listen-queue <replaceable>integer</replaceable>;
+ tkey-dhkey <replaceable>quoted_string</replaceable> <replaceable>integer</replaceable>;
+ tkey-gssapi-credential <replaceable>quoted_string</replaceable>;
+ tkey-domain <replaceable>quoted_string</replaceable>;
+ transfers-per-ns <replaceable>integer</replaceable>;
+ transfers-in <replaceable>integer</replaceable>;
+ transfers-out <replaceable>integer</replaceable>;
+ use-ixfr <replaceable>boolean</replaceable>;
+ version ( <replaceable>quoted_string</replaceable> | none );
+ allow-recursion { <replaceable>address_match_element</replaceable>; ... };
+ sortlist { <replaceable>address_match_element</replaceable>; ... };
+ topology { <replaceable>address_match_element</replaceable>; ... }; // not implemented
+ auth-nxdomain <replaceable>boolean</replaceable>; // default changed
+ minimal-responses <replaceable>boolean</replaceable>;
+ recursion <replaceable>boolean</replaceable>;
+ rrset-order {
+ <optional> class <replaceable>string</replaceable> </optional> <optional> type <replaceable>string</replaceable> </optional>
+ <optional> name <replaceable>quoted_string</replaceable> </optional> <replaceable>string</replaceable> <replaceable>string</replaceable>; ...
+ };
+ provide-ixfr <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ rfc2308-type1 <replaceable>boolean</replaceable>; // not yet implemented
+ additional-from-auth <replaceable>boolean</replaceable>;
+ additional-from-cache <replaceable>boolean</replaceable>;
+ query-source ( ( <replaceable>ipv4_address</replaceable> | * ) | <optional> address ( <replaceable>ipv4_address</replaceable> | * ) </optional> ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ query-source-v6 ( ( <replaceable>ipv6_address</replaceable> | * ) | <optional> address ( <replaceable>ipv6_address</replaceable> | * ) </optional> ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ cleaning-interval <replaceable>integer</replaceable>;
+ min-roots <replaceable>integer</replaceable>; // not implemented
+ lame-ttl <replaceable>integer</replaceable>;
+ max-ncache-ttl <replaceable>integer</replaceable>;
+ max-cache-ttl <replaceable>integer</replaceable>;
+ transfer-format ( many-answers | one-answer );
+ max-cache-size <replaceable>size_no_default</replaceable>;
+ max-acache-size <replaceable>size_no_default</replaceable>;
+ clients-per-query <replaceable>number</replaceable>;
+ max-clients-per-query <replaceable>number</replaceable>;
+ check-names ( master | slave | response )
+ ( fail | warn | ignore );
+ check-mx ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx-cname ( fail | warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ cache-file <replaceable>quoted_string</replaceable>; // test option
+ suppress-initial-notify <replaceable>boolean</replaceable>; // not yet implemented
+ preferred-glue <replaceable>string</replaceable>;
+ dual-stack-servers <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>quoted_string</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv4_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv6_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> ); ...
+ };
+ edns-udp-size <replaceable>integer</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ root-delegation-only <optional> exclude { <replaceable>quoted_string</replaceable>; ... } </optional>;
+ disable-algorithms <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
+ dnssec-enable <replaceable>boolean</replaceable>;
+ dnssec-validation <replaceable>boolean</replaceable>;
+ dnssec-lookaside <replaceable>string</replaceable> trust-anchor <replaceable>string</replaceable>;
+ dnssec-must-be-secure <replaceable>string</replaceable> <replaceable>boolean</replaceable>;
+ dnssec-accept-expired <replaceable>boolean</replaceable>;
+
+ empty-server <replaceable>string</replaceable>;
+ empty-contact <replaceable>string</replaceable>;
+ empty-zones-enable <replaceable>boolean</replaceable>;
+ disable-empty-zone <replaceable>string</replaceable>;
+
+ dialup <replaceable>dialuptype</replaceable>;
+ ixfr-from-differences <replaceable>ixfrdiff</replaceable>;
+
+ allow-query { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache { <replaceable>address_match_element</replaceable>; ... };
+ allow-transfer { <replaceable>address_match_element</replaceable>; ... };
+ allow-update { <replaceable>address_match_element</replaceable>; ... };
+ allow-update-forwarding { <replaceable>address_match_element</replaceable>; ... };
+ update-check-ksk <replaceable>boolean</replaceable>;
+
+ masterfile-format ( text | raw );
+ notify <replaceable>notifytype</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-delay <replaceable>seconds</replaceable>;
+ also-notify <optional> port <replaceable>integer</replaceable> </optional> { ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> )
+ <optional> port <replaceable>integer</replaceable> </optional>; ... };
+ allow-notify { <replaceable>address_match_element</replaceable>; ... };
+
+ forward ( first | only );
+ forwarders <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) <optional> port <replaceable>integer</replaceable> </optional>; ...
+ };
+
+ max-journal-size <replaceable>size_no_default</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ min-refresh-time <replaceable>integer</replaceable>;
+ multi-master <replaceable>boolean</replaceable>;
+ sig-validity-interval <replaceable>integer</replaceable>;
+
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+
+ zone-statistics <replaceable>boolean</replaceable>;
+ key-directory <replaceable>quoted_string</replaceable>;
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+ zero-no-soa-ttl-cache <replaceable>boolean</replaceable>;
+
+ allow-v6-synthesis { <replaceable>address_match_element</replaceable>; ... }; // obsolete
+ deallocate-on-exit <replaceable>boolean</replaceable>; // obsolete
+ fake-iquery <replaceable>boolean</replaceable>; // obsolete
+ fetch-glue <replaceable>boolean</replaceable>; // obsolete
+ has-old-clients <replaceable>boolean</replaceable>; // obsolete
+ maintain-ixfr-base <replaceable>boolean</replaceable>; // obsolete
+ max-ixfr-log-size <replaceable>size</replaceable>; // obsolete
+ multiple-cnames <replaceable>boolean</replaceable>; // obsolete
+ named-xfer <replaceable>quoted_string</replaceable>; // obsolete
+ serial-queries <replaceable>integer</replaceable>; // obsolete
+ treat-cr-as-space <replaceable>boolean</replaceable>; // obsolete
+ use-id-pool <replaceable>boolean</replaceable>; // obsolete
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>VIEW</title>
+ <literallayout>
+view <replaceable>string</replaceable> <replaceable>optional_class</replaceable> {
+ match-clients { <replaceable>address_match_element</replaceable>; ... };
+ match-destinations { <replaceable>address_match_element</replaceable>; ... };
+ match-recursive-only <replaceable>boolean</replaceable>;
+
+ key <replaceable>string</replaceable> {
+ algorithm <replaceable>string</replaceable>;
+ secret <replaceable>string</replaceable>;
+ };
+
+ zone <replaceable>string</replaceable> <replaceable>optional_class</replaceable> {
+ ...
+ };
+
+ server ( <replaceable>ipv4_address<optional>/prefixlen</optional></replaceable> | <replaceable>ipv6_address<optional>/prefixlen</optional></replaceable> ) {
+ ...
+ };
+
+ trusted-keys {
+ <replaceable>string</replaceable> <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>quoted_string</replaceable>; ...
+ };
+
+ allow-recursion { <replaceable>address_match_element</replaceable>; ... };
+ sortlist { <replaceable>address_match_element</replaceable>; ... };
+ topology { <replaceable>address_match_element</replaceable>; ... }; // not implemented
+ auth-nxdomain <replaceable>boolean</replaceable>; // default changed
+ minimal-responses <replaceable>boolean</replaceable>;
+ recursion <replaceable>boolean</replaceable>;
+ rrset-order {
+ <optional> class <replaceable>string</replaceable> </optional> <optional> type <replaceable>string</replaceable> </optional>
+ <optional> name <replaceable>quoted_string</replaceable> </optional> <replaceable>string</replaceable> <replaceable>string</replaceable>; ...
+ };
+ provide-ixfr <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ rfc2308-type1 <replaceable>boolean</replaceable>; // not yet implemented
+ additional-from-auth <replaceable>boolean</replaceable>;
+ additional-from-cache <replaceable>boolean</replaceable>;
+ query-source ( ( <replaceable>ipv4_address</replaceable> | * ) | <optional> address ( <replaceable>ipv4_address</replaceable> | * ) </optional> ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ query-source-v6 ( ( <replaceable>ipv6_address</replaceable> | * ) | <optional> address ( <replaceable>ipv6_address</replaceable> | * ) </optional> ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ cleaning-interval <replaceable>integer</replaceable>;
+ min-roots <replaceable>integer</replaceable>; // not implemented
+ lame-ttl <replaceable>integer</replaceable>;
+ max-ncache-ttl <replaceable>integer</replaceable>;
+ max-cache-ttl <replaceable>integer</replaceable>;
+ transfer-format ( many-answers | one-answer );
+ max-cache-size <replaceable>size_no_default</replaceable>;
+ max-acache-size <replaceable>size_no_default</replaceable>;
+ clients-per-query <replaceable>number</replaceable>;
+ max-clients-per-query <replaceable>number</replaceable>;
+ check-names ( master | slave | response )
+ ( fail | warn | ignore );
+ check-mx ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx-cname ( fail | warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ cache-file <replaceable>quoted_string</replaceable>; // test option
+ suppress-initial-notify <replaceable>boolean</replaceable>; // not yet implemented
+ preferred-glue <replaceable>string</replaceable>;
+ dual-stack-servers <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>quoted_string</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv4_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv6_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> ); ...
+ };
+ edns-udp-size <replaceable>integer</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ root-delegation-only <optional> exclude { <replaceable>quoted_string</replaceable>; ... } </optional>;
+ disable-algorithms <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
+ dnssec-enable <replaceable>boolean</replaceable>;
+ dnssec-validation <replaceable>boolean</replaceable>;
+ dnssec-lookaside <replaceable>string</replaceable> trust-anchor <replaceable>string</replaceable>;
+ dnssec-must-be-secure <replaceable>string</replaceable> <replaceable>boolean</replaceable>;
+ dnssec-accept-expired <replaceable>boolean</replaceable>;
+
+ empty-server <replaceable>string</replaceable>;
+ empty-contact <replaceable>string</replaceable>;
+ empty-zones-enable <replaceable>boolean</replaceable>;
+ disable-empty-zone <replaceable>string</replaceable>;
+
+ dialup <replaceable>dialuptype</replaceable>;
+ ixfr-from-differences <replaceable>ixfrdiff</replaceable>;
+
+ allow-query { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache { <replaceable>address_match_element</replaceable>; ... };
+ allow-transfer { <replaceable>address_match_element</replaceable>; ... };
+ allow-update { <replaceable>address_match_element</replaceable>; ... };
+ allow-update-forwarding { <replaceable>address_match_element</replaceable>; ... };
+ update-check-ksk <replaceable>boolean</replaceable>;
+
+ masterfile-format ( text | raw );
+ notify <replaceable>notifytype</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-delay <replaceable>seconds</replaceable>;
+ also-notify <optional> port <replaceable>integer</replaceable> </optional> { ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> )
+ <optional> port <replaceable>integer</replaceable> </optional>; ... };
+ allow-notify { <replaceable>address_match_element</replaceable>; ... };
+
+ forward ( first | only );
+ forwarders <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) <optional> port <replaceable>integer</replaceable> </optional>; ...
+ };
+
+ max-journal-size <replaceable>size_no_default</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ min-refresh-time <replaceable>integer</replaceable>;
+ multi-master <replaceable>boolean</replaceable>;
+ sig-validity-interval <replaceable>integer</replaceable>;
+
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+
+ zone-statistics <replaceable>boolean</replaceable>;
+ key-directory <replaceable>quoted_string</replaceable>;
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+ zero-no-soa-ttl-cache <replaceable>boolean</replaceable>;
+
+ allow-v6-synthesis { <replaceable>address_match_element</replaceable>; ... }; // obsolete
+ fetch-glue <replaceable>boolean</replaceable>; // obsolete
+ maintain-ixfr-base <replaceable>boolean</replaceable>; // obsolete
+ max-ixfr-log-size <replaceable>size</replaceable>; // obsolete
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>ZONE</title>
+ <literallayout>
+zone <replaceable>string</replaceable> <replaceable>optional_class</replaceable> {
+ type ( master | slave | stub | hint |
+ forward | delegation-only );
+ file <replaceable>quoted_string</replaceable>;
+
+ masters <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>masters</replaceable> |
+ <replaceable>ipv4_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv6_address</replaceable> <optional> port <replaceable>integer</replaceable> </optional> ) <optional> key <replaceable>string</replaceable> </optional>; ...
+ };
+
+ database <replaceable>string</replaceable>;
+ delegation-only <replaceable>boolean</replaceable>;
+ check-names ( fail | warn | ignore );
+ check-mx ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx-cname ( fail | warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ dialup <replaceable>dialuptype</replaceable>;
+ ixfr-from-differences <replaceable>boolean</replaceable>;
+ journal <replaceable>quoted_string</replaceable>;
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+
+ allow-query { <replaceable>address_match_element</replaceable>; ... };
+ allow-transfer { <replaceable>address_match_element</replaceable>; ... };
+ allow-update { <replaceable>address_match_element</replaceable>; ... };
+ allow-update-forwarding { <replaceable>address_match_element</replaceable>; ... };
+ update-policy {
+ ( grant | deny ) <replaceable>string</replaceable>
+ ( name | subdomain | wildcard | self ) <replaceable>string</replaceable>
+ <replaceable>rrtypelist</replaceable>; ...
+ };
+ update-check-ksk <replaceable>boolean</replaceable>;
+
+ masterfile-format ( text | raw );
+ notify <replaceable>notifytype</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-delay <replaceable>seconds</replaceable>;
+ also-notify <optional> port <replaceable>integer</replaceable> </optional> { ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> )
+ <optional> port <replaceable>integer</replaceable> </optional>; ... };
+ allow-notify { <replaceable>address_match_element</replaceable>; ... };
+
+ forward ( first | only );
+ forwarders <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) <optional> port <replaceable>integer</replaceable> </optional>; ...
+ };
+
+ max-journal-size <replaceable>size_no_default</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ min-refresh-time <replaceable>integer</replaceable>;
+ multi-master <replaceable>boolean</replaceable>;
+ sig-validity-interval <replaceable>integer</replaceable>;
+
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+
+ zone-statistics <replaceable>boolean</replaceable>;
+ key-directory <replaceable>quoted_string</replaceable>;
+
+ ixfr-base <replaceable>quoted_string</replaceable>; // obsolete
+ ixfr-tmp-file <replaceable>quoted_string</replaceable>; // obsolete
+ maintain-ixfr-base <replaceable>boolean</replaceable>; // obsolete
+ max-ixfr-log-size <replaceable>size</replaceable>; // obsolete
+ pubkey <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>quoted_string</replaceable>; // obsolete
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>FILES</title>
+ <para><filename>/etc/named.conf</filename>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para><citerefentry>
+ <refentrytitle>named</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>named-checkconf</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>rndc</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citetitle>BIND 9 Administrator Reference Manual</citetitle>.
+ </para>
+ </refsect1>
+
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/bin/named/named.conf.html b/bin/named/named.conf.html
new file mode 100644
index 0000000..09e71a3
--- /dev/null
+++ b/bin/named/named.conf.html
@@ -0,0 +1,554 @@
+<!--
+ - Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - 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: named.conf.html,v 1.1.2.35 2007/08/19 23:26:13 marka Exp $ -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>named.conf</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.71.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry" lang="en">
+<a name="id2476275"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><code class="filename">named.conf</code> &#8212; configuration file for named</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">named.conf</code> </p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2542042"></a><h2>DESCRIPTION</h2>
+<p><code class="filename">named.conf</code> is the configuration file
+ for
+ <span><strong class="command">named</strong></span>. Statements are enclosed
+ in braces and terminated with a semi-colon. Clauses in
+ the statements are also semi-colon terminated. The usual
+ comment styles are supported:
+ </p>
+<p>
+ C style: /* */
+ </p>
+<p>
+ C++ style: // to end of line
+ </p>
+<p>
+ Unix style: # to end of line
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543367"></a><h2>ACL</h2>
+<div class="literallayout"><p><br>
+acl <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543383"></a><h2>KEY</h2>
+<div class="literallayout"><p><br>
+key <em class="replaceable"><code>domain_name</code></em> {<br>
+ algorithm <em class="replaceable"><code>string</code></em>;<br>
+ secret <em class="replaceable"><code>string</code></em>;<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543402"></a><h2>MASTERS</h2>
+<div class="literallayout"><p><br>
+masters <em class="replaceable"><code>string</code></em> [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>masters</code></em> | <em class="replaceable"><code>ipv4_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv6_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] ) [<span class="optional"> key <em class="replaceable"><code>string</code></em> </span>]; ...<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543448"></a><h2>SERVER</h2>
+<div class="literallayout"><p><br>
+server ( <em class="replaceable"><code>ipv4_address[<span class="optional">/prefixlen</span>]</code></em> | <em class="replaceable"><code>ipv6_address[<span class="optional">/prefixlen</span>]</code></em> ) {<br>
+ bogus <em class="replaceable"><code>boolean</code></em>;<br>
+ edns <em class="replaceable"><code>boolean</code></em>;<br>
+ edns-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ keys <em class="replaceable"><code>server_key</code></em>;<br>
+ transfers <em class="replaceable"><code>integer</code></em>;<br>
+ transfer-format ( many-answers | one-answer );<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+<br>
+ support-ixfr <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543516"></a><h2>TRUSTED-KEYS</h2>
+<div class="literallayout"><p><br>
+trusted-keys {<br>
+ <em class="replaceable"><code>domain_name</code></em> <em class="replaceable"><code>flags</code></em> <em class="replaceable"><code>protocol</code></em> <em class="replaceable"><code>algorithm</code></em> <em class="replaceable"><code>key</code></em>; ... <br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543542"></a><h2>CONTROLS</h2>
+<div class="literallayout"><p><br>
+controls {<br>
+ inet ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>]<br>
+ allow { <em class="replaceable"><code>address_match_element</code></em>; ... }<br>
+ [<span class="optional"> keys { <em class="replaceable"><code>string</code></em>; ... } </span>];<br>
+ unix <em class="replaceable"><code>unsupported</code></em>; // not implemented<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543577"></a><h2>LOGGING</h2>
+<div class="literallayout"><p><br>
+logging {<br>
+ channel <em class="replaceable"><code>string</code></em> {<br>
+ file <em class="replaceable"><code>log_file</code></em>;<br>
+ syslog <em class="replaceable"><code>optional_facility</code></em>;<br>
+ null;<br>
+ stderr;<br>
+ severity <em class="replaceable"><code>log_severity</code></em>;<br>
+ print-time <em class="replaceable"><code>boolean</code></em>;<br>
+ print-severity <em class="replaceable"><code>boolean</code></em>;<br>
+ print-category <em class="replaceable"><code>boolean</code></em>;<br>
+ };<br>
+ category <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>; ... };<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543616"></a><h2>LWRES</h2>
+<div class="literallayout"><p><br>
+lwres {<br>
+ listen-on [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ...<br>
+ };<br>
+ view <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>optional_class</code></em>;<br>
+ search { <em class="replaceable"><code>string</code></em>; ... };<br>
+ ndots <em class="replaceable"><code>integer</code></em>;<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543657"></a><h2>OPTIONS</h2>
+<div class="literallayout"><p><br>
+options {<br>
+ avoid-v4-udp-ports { <em class="replaceable"><code>port</code></em>; ... };<br>
+ avoid-v6-udp-ports { <em class="replaceable"><code>port</code></em>; ... };<br>
+ blackhole { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ coresize <em class="replaceable"><code>size</code></em>;<br>
+ datasize <em class="replaceable"><code>size</code></em>;<br>
+ directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ dump-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ files <em class="replaceable"><code>size</code></em>;<br>
+ heartbeat-interval <em class="replaceable"><code>integer</code></em>;<br>
+ host-statistics <em class="replaceable"><code>boolean</code></em>; // not implemented<br>
+ host-statistics-max <em class="replaceable"><code>number</code></em>; // not implemented<br>
+ hostname ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ interface-interval <em class="replaceable"><code>integer</code></em>;<br>
+ listen-on [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ listen-on-v6 [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ match-mapped-addresses <em class="replaceable"><code>boolean</code></em>;<br>
+ memstatistics-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ pid-file ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ port <em class="replaceable"><code>integer</code></em>;<br>
+ querylog <em class="replaceable"><code>boolean</code></em>;<br>
+ recursing-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ random-device <em class="replaceable"><code>quoted_string</code></em>;<br>
+ recursive-clients <em class="replaceable"><code>integer</code></em>;<br>
+ serial-query-rate <em class="replaceable"><code>integer</code></em>;<br>
+ server-id ( <em class="replaceable"><code>quoted_string</code></em> | none |;<br>
+ stacksize <em class="replaceable"><code>size</code></em>;<br>
+ statistics-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ statistics-interval <em class="replaceable"><code>integer</code></em>; // not yet implemented<br>
+ tcp-clients <em class="replaceable"><code>integer</code></em>;<br>
+ tcp-listen-queue <em class="replaceable"><code>integer</code></em>;<br>
+ tkey-dhkey <em class="replaceable"><code>quoted_string</code></em> <em class="replaceable"><code>integer</code></em>;<br>
+ tkey-gssapi-credential <em class="replaceable"><code>quoted_string</code></em>;<br>
+ tkey-domain <em class="replaceable"><code>quoted_string</code></em>;<br>
+ transfers-per-ns <em class="replaceable"><code>integer</code></em>;<br>
+ transfers-in <em class="replaceable"><code>integer</code></em>;<br>
+ transfers-out <em class="replaceable"><code>integer</code></em>;<br>
+ use-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ version ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ allow-recursion { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ sortlist { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ topology { <em class="replaceable"><code>address_match_element</code></em>; ... }; // not implemented<br>
+ auth-nxdomain <em class="replaceable"><code>boolean</code></em>; // default changed<br>
+ minimal-responses <em class="replaceable"><code>boolean</code></em>;<br>
+ recursion <em class="replaceable"><code>boolean</code></em>;<br>
+ rrset-order {<br>
+ [<span class="optional"> class <em class="replaceable"><code>string</code></em> </span>] [<span class="optional"> type <em class="replaceable"><code>string</code></em> </span>]<br>
+ [<span class="optional"> name <em class="replaceable"><code>quoted_string</code></em> </span>] <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>string</code></em>; ...<br>
+ };<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ rfc2308-type1 <em class="replaceable"><code>boolean</code></em>; // not yet implemented<br>
+ additional-from-auth <em class="replaceable"><code>boolean</code></em>;<br>
+ additional-from-cache <em class="replaceable"><code>boolean</code></em>;<br>
+ query-source ( ( <em class="replaceable"><code>ipv4_address</code></em> | * ) | [<span class="optional"> address ( <em class="replaceable"><code>ipv4_address</code></em> | * ) </span>] ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ query-source-v6 ( ( <em class="replaceable"><code>ipv6_address</code></em> | * ) | [<span class="optional"> address ( <em class="replaceable"><code>ipv6_address</code></em> | * ) </span>] ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ cleaning-interval <em class="replaceable"><code>integer</code></em>;<br>
+ min-roots <em class="replaceable"><code>integer</code></em>; // not implemented<br>
+ lame-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-ncache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-cache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ transfer-format ( many-answers | one-answer );<br>
+ max-cache-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ max-acache-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ clients-per-query <em class="replaceable"><code>number</code></em>;<br>
+ max-clients-per-query <em class="replaceable"><code>number</code></em>;<br>
+ check-names ( master | slave | response )<br>
+ ( fail | warn | ignore );<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ cache-file <em class="replaceable"><code>quoted_string</code></em>; // test option<br>
+ suppress-initial-notify <em class="replaceable"><code>boolean</code></em>; // not yet implemented<br>
+ preferred-glue <em class="replaceable"><code>string</code></em>;<br>
+ dual-stack-servers [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>quoted_string</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv4_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv6_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] ); ...<br>
+ };<br>
+ edns-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ root-delegation-only [<span class="optional"> exclude { <em class="replaceable"><code>quoted_string</code></em>; ... } </span>];<br>
+ disable-algorithms <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>; ... };<br>
+ dnssec-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-validation <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-lookaside <em class="replaceable"><code>string</code></em> trust-anchor <em class="replaceable"><code>string</code></em>;<br>
+ dnssec-must-be-secure <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-accept-expired <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ empty-server <em class="replaceable"><code>string</code></em>;<br>
+ empty-contact <em class="replaceable"><code>string</code></em>;<br>
+ empty-zones-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ disable-empty-zone <em class="replaceable"><code>string</code></em>;<br>
+<br>
+ dialup <em class="replaceable"><code>dialuptype</code></em>;<br>
+ ixfr-from-differences <em class="replaceable"><code>ixfrdiff</code></em>;<br>
+<br>
+ allow-query { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query-cache { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-transfer { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update-forwarding { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ masterfile-format ( text | raw );<br>
+ notify <em class="replaceable"><code>notifytype</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-delay <em class="replaceable"><code>seconds</code></em>;<br>
+ also-notify [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> )<br>
+ [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ... };<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+<br>
+ forward ( first | only );<br>
+ forwarders [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ...<br>
+ };<br>
+<br>
+ max-journal-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ max-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em>;<br>
+<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ zone-statistics <em class="replaceable"><code>boolean</code></em>;<br>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ zero-no-soa-ttl <em class="replaceable"><code>boolean</code></em>;<br>
+ zero-no-soa-ttl-cache <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ allow-v6-synthesis { <em class="replaceable"><code>address_match_element</code></em>; ... }; // obsolete<br>
+ deallocate-on-exit <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ fake-iquery <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ fetch-glue <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ has-old-clients <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ maintain-ixfr-base <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ max-ixfr-log-size <em class="replaceable"><code>size</code></em>; // obsolete<br>
+ multiple-cnames <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ named-xfer <em class="replaceable"><code>quoted_string</code></em>; // obsolete<br>
+ serial-queries <em class="replaceable"><code>integer</code></em>; // obsolete<br>
+ treat-cr-as-space <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ use-id-pool <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2544401"></a><h2>VIEW</h2>
+<div class="literallayout"><p><br>
+view <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>optional_class</code></em> {<br>
+ match-clients { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ match-destinations { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ match-recursive-only <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ key <em class="replaceable"><code>string</code></em> {<br>
+ algorithm <em class="replaceable"><code>string</code></em>;<br>
+ secret <em class="replaceable"><code>string</code></em>;<br>
+ };<br>
+<br>
+ zone <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>optional_class</code></em> {<br>
+ ...<br>
+ };<br>
+<br>
+ server ( <em class="replaceable"><code>ipv4_address[<span class="optional">/prefixlen</span>]</code></em> | <em class="replaceable"><code>ipv6_address[<span class="optional">/prefixlen</span>]</code></em> ) {<br>
+ ...<br>
+ };<br>
+<br>
+ trusted-keys {<br>
+ <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>quoted_string</code></em>; ...<br>
+ };<br>
+<br>
+ allow-recursion { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ sortlist { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ topology { <em class="replaceable"><code>address_match_element</code></em>; ... }; // not implemented<br>
+ auth-nxdomain <em class="replaceable"><code>boolean</code></em>; // default changed<br>
+ minimal-responses <em class="replaceable"><code>boolean</code></em>;<br>
+ recursion <em class="replaceable"><code>boolean</code></em>;<br>
+ rrset-order {<br>
+ [<span class="optional"> class <em class="replaceable"><code>string</code></em> </span>] [<span class="optional"> type <em class="replaceable"><code>string</code></em> </span>]<br>
+ [<span class="optional"> name <em class="replaceable"><code>quoted_string</code></em> </span>] <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>string</code></em>; ...<br>
+ };<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ rfc2308-type1 <em class="replaceable"><code>boolean</code></em>; // not yet implemented<br>
+ additional-from-auth <em class="replaceable"><code>boolean</code></em>;<br>
+ additional-from-cache <em class="replaceable"><code>boolean</code></em>;<br>
+ query-source ( ( <em class="replaceable"><code>ipv4_address</code></em> | * ) | [<span class="optional"> address ( <em class="replaceable"><code>ipv4_address</code></em> | * ) </span>] ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ query-source-v6 ( ( <em class="replaceable"><code>ipv6_address</code></em> | * ) | [<span class="optional"> address ( <em class="replaceable"><code>ipv6_address</code></em> | * ) </span>] ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ cleaning-interval <em class="replaceable"><code>integer</code></em>;<br>
+ min-roots <em class="replaceable"><code>integer</code></em>; // not implemented<br>
+ lame-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-ncache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-cache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ transfer-format ( many-answers | one-answer );<br>
+ max-cache-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ max-acache-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ clients-per-query <em class="replaceable"><code>number</code></em>;<br>
+ max-clients-per-query <em class="replaceable"><code>number</code></em>;<br>
+ check-names ( master | slave | response )<br>
+ ( fail | warn | ignore );<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ cache-file <em class="replaceable"><code>quoted_string</code></em>; // test option<br>
+ suppress-initial-notify <em class="replaceable"><code>boolean</code></em>; // not yet implemented<br>
+ preferred-glue <em class="replaceable"><code>string</code></em>;<br>
+ dual-stack-servers [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>quoted_string</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv4_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv6_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] ); ...<br>
+ };<br>
+ edns-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ root-delegation-only [<span class="optional"> exclude { <em class="replaceable"><code>quoted_string</code></em>; ... } </span>];<br>
+ disable-algorithms <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>; ... };<br>
+ dnssec-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-validation <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-lookaside <em class="replaceable"><code>string</code></em> trust-anchor <em class="replaceable"><code>string</code></em>;<br>
+ dnssec-must-be-secure <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-accept-expired <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ empty-server <em class="replaceable"><code>string</code></em>;<br>
+ empty-contact <em class="replaceable"><code>string</code></em>;<br>
+ empty-zones-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ disable-empty-zone <em class="replaceable"><code>string</code></em>;<br>
+<br>
+ dialup <em class="replaceable"><code>dialuptype</code></em>;<br>
+ ixfr-from-differences <em class="replaceable"><code>ixfrdiff</code></em>;<br>
+<br>
+ allow-query { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query-cache { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-transfer { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update-forwarding { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ masterfile-format ( text | raw );<br>
+ notify <em class="replaceable"><code>notifytype</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-delay <em class="replaceable"><code>seconds</code></em>;<br>
+ also-notify [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> )<br>
+ [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ... };<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+<br>
+ forward ( first | only );<br>
+ forwarders [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ...<br>
+ };<br>
+<br>
+ max-journal-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ max-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em>;<br>
+<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ zone-statistics <em class="replaceable"><code>boolean</code></em>;<br>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ zero-no-soa-ttl <em class="replaceable"><code>boolean</code></em>;<br>
+ zero-no-soa-ttl-cache <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ allow-v6-synthesis { <em class="replaceable"><code>address_match_element</code></em>; ... }; // obsolete<br>
+ fetch-glue <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ maintain-ixfr-base <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ max-ixfr-log-size <em class="replaceable"><code>size</code></em>; // obsolete<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2544966"></a><h2>ZONE</h2>
+<div class="literallayout"><p><br>
+zone <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>optional_class</code></em> {<br>
+ type ( master | slave | stub | hint |<br>
+ forward | delegation-only );<br>
+ file <em class="replaceable"><code>quoted_string</code></em>;<br>
+<br>
+ masters [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>masters</code></em> |<br>
+ <em class="replaceable"><code>ipv4_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv6_address</code></em> [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] ) [<span class="optional"> key <em class="replaceable"><code>string</code></em> </span>]; ...<br>
+ };<br>
+<br>
+ database <em class="replaceable"><code>string</code></em>;<br>
+ delegation-only <em class="replaceable"><code>boolean</code></em>;<br>
+ check-names ( fail | warn | ignore );<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ dialup <em class="replaceable"><code>dialuptype</code></em>;<br>
+ ixfr-from-differences <em class="replaceable"><code>boolean</code></em>;<br>
+ journal <em class="replaceable"><code>quoted_string</code></em>;<br>
+ zero-no-soa-ttl <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ allow-query { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-transfer { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update-forwarding { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ update-policy {<br>
+ ( grant | deny ) <em class="replaceable"><code>string</code></em><br>
+ ( name | subdomain | wildcard | self ) <em class="replaceable"><code>string</code></em><br>
+ <em class="replaceable"><code>rrtypelist</code></em>; ...<br>
+ };<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ masterfile-format ( text | raw );<br>
+ notify <em class="replaceable"><code>notifytype</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-delay <em class="replaceable"><code>seconds</code></em>;<br>
+ also-notify [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> )<br>
+ [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ... };<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+<br>
+ forward ( first | only );<br>
+ forwarders [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ...<br>
+ };<br>
+<br>
+ max-journal-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ max-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em>;<br>
+<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ zone-statistics <em class="replaceable"><code>boolean</code></em>;<br>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+<br>
+ ixfr-base <em class="replaceable"><code>quoted_string</code></em>; // obsolete<br>
+ ixfr-tmp-file <em class="replaceable"><code>quoted_string</code></em>; // obsolete<br>
+ maintain-ixfr-base <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ max-ixfr-log-size <em class="replaceable"><code>size</code></em>; // obsolete<br>
+ pubkey <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>quoted_string</code></em>; // obsolete<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2545319"></a><h2>FILES</h2>
+<p><code class="filename">/etc/named.conf</code>
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2545331"></a><h2>SEE ALSO</h2>
+<p><span class="citerefentry"><span class="refentrytitle">named</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">named-checkconf</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">rndc</span>(8)</span>,
+ <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+ </p>
+</div>
+</div></body>
+</html>
diff --git a/bin/named/named.docbook b/bin/named/named.docbook
new file mode 100644
index 0000000..74b41f5
--- /dev/null
+++ b/bin/named/named.docbook
@@ -0,0 +1,406 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2000, 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: named.docbook,v 1.7.18.12 2007/08/28 07:20:01 tbox Exp $ -->
+<refentry id="man.named">
+ <refentryinfo>
+ <date>June 30, 2000</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle><application>named</application></refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo>BIND9</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname><application>named</application></refname>
+ <refpurpose>Internet domain name server</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2006</year>
+ <year>2007</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ <copyright>
+ <year>2000</year>
+ <year>2001</year>
+ <year>2003</year>
+ <holder>Internet Software Consortium.</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>named</command>
+ <arg><option>-4</option></arg>
+ <arg><option>-6</option></arg>
+ <arg><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
+ <arg><option>-d <replaceable class="parameter">debug-level</replaceable></option></arg>
+ <arg><option>-f</option></arg>
+ <arg><option>-g</option></arg>
+ <arg><option>-m <replaceable class="parameter">flag</replaceable></option></arg>
+ <arg><option>-n <replaceable class="parameter">#cpus</replaceable></option></arg>
+ <arg><option>-p <replaceable class="parameter">port</replaceable></option></arg>
+ <arg><option>-s</option></arg>
+ <arg><option>-t <replaceable class="parameter">directory</replaceable></option></arg>
+ <arg><option>-u <replaceable class="parameter">user</replaceable></option></arg>
+ <arg><option>-v</option></arg>
+ <arg><option>-x <replaceable class="parameter">cache-file</replaceable></option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+ <para><command>named</command>
+ is a Domain Name System (DNS) server,
+ part of the BIND 9 distribution from ISC. For more
+ information on the DNS, see RFCs 1033, 1034, and 1035.
+ </para>
+ <para>
+ When invoked without arguments, <command>named</command>
+ will
+ read the default configuration file
+ <filename>/etc/named.conf</filename>, read any initial
+ data, and listen for queries.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-4</term>
+ <listitem>
+ <para>
+ Use IPv4 only even if the host machine is capable of IPv6.
+ <option>-4</option> and <option>-6</option> are mutually
+ exclusive.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-6</term>
+ <listitem>
+ <para>
+ Use IPv6 only even if the host machine is capable of IPv4.
+ <option>-4</option> and <option>-6</option> are mutually
+ exclusive.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-c <replaceable class="parameter">config-file</replaceable></term>
+ <listitem>
+ <para>
+ Use <replaceable class="parameter">config-file</replaceable> as the
+ configuration file instead of the default,
+ <filename>/etc/named.conf</filename>. To
+ ensure that reloading the configuration file continues
+ to work after the server has changed its working
+ directory due to to a possible
+ <option>directory</option> option in the configuration
+ file, <replaceable class="parameter">config-file</replaceable> should be
+ an absolute pathname.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-d <replaceable class="parameter">debug-level</replaceable></term>
+ <listitem>
+ <para>
+ Set the daemon's debug level to <replaceable class="parameter">debug-level</replaceable>.
+ Debugging traces from <command>named</command> become
+ more verbose as the debug level increases.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-f</term>
+ <listitem>
+ <para>
+ Run the server in the foreground (i.e. do not daemonize).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-g</term>
+ <listitem>
+ <para>
+ Run the server in the foreground and force all logging
+ to <filename>stderr</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-m <replaceable class="parameter">flag</replaceable></term>
+ <listitem>
+ <para>
+ Turn on memory usage debugging flags. Possible flags are
+ <replaceable class="parameter">usage</replaceable>,
+ <replaceable class="parameter">trace</replaceable>,
+ <replaceable class="parameter">record</replaceable>,
+ <replaceable class="parameter">size</replaceable>, and
+ <replaceable class="parameter">mctx</replaceable>.
+ These correspond to the ISC_MEM_DEBUGXXXX flags described in
+ <filename>&lt;isc/mem.h&gt;</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-n <replaceable class="parameter">#cpus</replaceable></term>
+ <listitem>
+ <para>
+ Create <replaceable class="parameter">#cpus</replaceable> worker threads
+ to take advantage of multiple CPUs. If not specified,
+ <command>named</command> will try to determine the
+ number of CPUs present and create one thread per CPU.
+ If it is unable to determine the number of CPUs, a
+ single worker thread will be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-p <replaceable class="parameter">port</replaceable></term>
+ <listitem>
+ <para>
+ Listen for queries on port <replaceable class="parameter">port</replaceable>. If not
+ specified, the default is port 53.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s</term>
+ <listitem>
+ <para>
+ Write memory usage statistics to <filename>stdout</filename> on exit.
+ </para>
+ <note>
+ <para>
+ This option is mainly of interest to BIND 9 developers
+ and may be removed or changed in a future release.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-t <replaceable class="parameter">directory</replaceable></term>
+ <listitem>
+ <para>Chroot
+ to <replaceable class="parameter">directory</replaceable> after
+ processing the command line arguments, but before
+ reading the configuration file.
+ </para>
+ <warning>
+ <para>
+ This option should be used in conjunction with the
+ <option>-u</option> option, as chrooting a process
+ running as root doesn't enhance security on most
+ systems; the way <function>chroot(2)</function> is
+ defined allows a process with root privileges to
+ escape a chroot jail.
+ </para>
+ </warning>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-u <replaceable class="parameter">user</replaceable></term>
+ <listitem>
+ <para>Setuid
+ to <replaceable class="parameter">user</replaceable> after completing
+ privileged operations, such as creating sockets that
+ listen on privileged ports.
+ </para>
+ <note>
+ <para>
+ On Linux, <command>named</command> uses the kernel's
+ capability mechanism to drop all root privileges
+ except the ability to <function>bind(2)</function> to
+ a
+ privileged port and set process resource limits.
+ Unfortunately, this means that the <option>-u</option>
+ option only works when <command>named</command> is
+ run
+ on kernel 2.2.18 or later, or kernel 2.3.99-pre3 or
+ later, since previous kernels did not allow privileges
+ to be retained after <function>setuid(2)</function>.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-v</term>
+ <listitem>
+ <para>
+ Report the version number and exit.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-x <replaceable class="parameter">cache-file</replaceable></term>
+ <listitem>
+ <para>
+ Load data from <replaceable class="parameter">cache-file</replaceable> into the
+ cache of the default view.
+ </para>
+ <warning>
+ <para>
+ This option must not be used. It is only of interest
+ to BIND 9 developers and may be removed or changed in a
+ future release.
+ </para>
+ </warning>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>SIGNALS</title>
+ <para>
+ In routine operation, signals should not be used to control
+ the nameserver; <command>rndc</command> should be used
+ instead.
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>SIGHUP</term>
+ <listitem>
+ <para>
+ Force a reload of the server.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>SIGINT, SIGTERM</term>
+ <listitem>
+ <para>
+ Shut down the server.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+ The result of sending any other signals to the server is undefined.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>CONFIGURATION</title>
+ <para>
+ The <command>named</command> configuration file is too complex
+ to describe in detail here. A complete description is provided
+ in the
+ <citetitle>BIND 9 Administrator Reference Manual</citetitle>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>FILES</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><filename>/etc/named.conf</filename></term>
+ <listitem>
+ <para>
+ The default configuration file.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>/var/run/named.pid</filename></term>
+ <listitem>
+ <para>
+ The default process-id file.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para><citetitle>RFC 1033</citetitle>,
+ <citetitle>RFC 1034</citetitle>,
+ <citetitle>RFC 1035</citetitle>,
+ <citerefentry>
+ <refentrytitle>named-checkconf</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>named-checkzone</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>rndc</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>lwresd</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>named.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citetitle>BIND 9 Administrator Reference Manual</citetitle>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>AUTHOR</title>
+ <para><corpauthor>Internet Systems Consortium</corpauthor>
+ </para>
+ </refsect1>
+
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/bin/named/named.html b/bin/named/named.html
new file mode 100644
index 0000000..294ecce
--- /dev/null
+++ b/bin/named/named.html
@@ -0,0 +1,255 @@
+<!--
+ - Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2000, 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: named.html,v 1.6.18.21 2007/06/20 02:26:58 marka Exp $ -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>named</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.71.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry" lang="en">
+<a name="man.named"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><span class="application">named</span> &#8212; Internet domain name server</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">named</code> [<code class="option">-4</code>] [<code class="option">-6</code>] [<code class="option">-c <em class="replaceable"><code>config-file</code></em></code>] [<code class="option">-d <em class="replaceable"><code>debug-level</code></em></code>] [<code class="option">-f</code>] [<code class="option">-g</code>] [<code class="option">-m <em class="replaceable"><code>flag</code></em></code>] [<code class="option">-n <em class="replaceable"><code>#cpus</code></em></code>] [<code class="option">-p <em class="replaceable"><code>port</code></em></code>] [<code class="option">-s</code>] [<code class="option">-t <em class="replaceable"><code>directory</code></em></code>] [<code class="option">-u <em class="replaceable"><code>user</code></em></code>] [<code class="option">-v</code>] [<code class="option">-x <em class="replaceable"><code>cache-file</code></em></code>]</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543452"></a><h2>DESCRIPTION</h2>
+<p><span><strong class="command">named</strong></span>
+ is a Domain Name System (DNS) server,
+ part of the BIND 9 distribution from ISC. For more
+ information on the DNS, see RFCs 1033, 1034, and 1035.
+ </p>
+<p>
+ When invoked without arguments, <span><strong class="command">named</strong></span>
+ will
+ read the default configuration file
+ <code class="filename">/etc/named.conf</code>, read any initial
+ data, and listen for queries.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543477"></a><h2>OPTIONS</h2>
+<div class="variablelist"><dl>
+<dt><span class="term">-4</span></dt>
+<dd><p>
+ Use IPv4 only even if the host machine is capable of IPv6.
+ <code class="option">-4</code> and <code class="option">-6</code> are mutually
+ exclusive.
+ </p></dd>
+<dt><span class="term">-6</span></dt>
+<dd><p>
+ Use IPv6 only even if the host machine is capable of IPv4.
+ <code class="option">-4</code> and <code class="option">-6</code> are mutually
+ exclusive.
+ </p></dd>
+<dt><span class="term">-c <em class="replaceable"><code>config-file</code></em></span></dt>
+<dd><p>
+ Use <em class="replaceable"><code>config-file</code></em> as the
+ configuration file instead of the default,
+ <code class="filename">/etc/named.conf</code>. To
+ ensure that reloading the configuration file continues
+ to work after the server has changed its working
+ directory due to to a possible
+ <code class="option">directory</code> option in the configuration
+ file, <em class="replaceable"><code>config-file</code></em> should be
+ an absolute pathname.
+ </p></dd>
+<dt><span class="term">-d <em class="replaceable"><code>debug-level</code></em></span></dt>
+<dd><p>
+ Set the daemon's debug level to <em class="replaceable"><code>debug-level</code></em>.
+ Debugging traces from <span><strong class="command">named</strong></span> become
+ more verbose as the debug level increases.
+ </p></dd>
+<dt><span class="term">-f</span></dt>
+<dd><p>
+ Run the server in the foreground (i.e. do not daemonize).
+ </p></dd>
+<dt><span class="term">-g</span></dt>
+<dd><p>
+ Run the server in the foreground and force all logging
+ to <code class="filename">stderr</code>.
+ </p></dd>
+<dt><span class="term">-m <em class="replaceable"><code>flag</code></em></span></dt>
+<dd><p>
+ Turn on memory usage debugging flags. Possible flags are
+ <em class="replaceable"><code>usage</code></em>,
+ <em class="replaceable"><code>trace</code></em>,
+ <em class="replaceable"><code>record</code></em>,
+ <em class="replaceable"><code>size</code></em>, and
+ <em class="replaceable"><code>mctx</code></em>.
+ These correspond to the ISC_MEM_DEBUGXXXX flags described in
+ <code class="filename">&lt;isc/mem.h&gt;</code>.
+ </p></dd>
+<dt><span class="term">-n <em class="replaceable"><code>#cpus</code></em></span></dt>
+<dd><p>
+ Create <em class="replaceable"><code>#cpus</code></em> worker threads
+ to take advantage of multiple CPUs. If not specified,
+ <span><strong class="command">named</strong></span> will try to determine the
+ number of CPUs present and create one thread per CPU.
+ If it is unable to determine the number of CPUs, a
+ single worker thread will be created.
+ </p></dd>
+<dt><span class="term">-p <em class="replaceable"><code>port</code></em></span></dt>
+<dd><p>
+ Listen for queries on port <em class="replaceable"><code>port</code></em>. If not
+ specified, the default is port 53.
+ </p></dd>
+<dt><span class="term">-s</span></dt>
+<dd>
+<p>
+ Write memory usage statistics to <code class="filename">stdout</code> on exit.
+ </p>
+<div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Note</h3>
+<p>
+ This option is mainly of interest to BIND 9 developers
+ and may be removed or changed in a future release.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-t <em class="replaceable"><code>directory</code></em></span></dt>
+<dd>
+<p>Chroot
+ to <em class="replaceable"><code>directory</code></em> after
+ processing the command line arguments, but before
+ reading the configuration file.
+ </p>
+<div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Warning</h3>
+<p>
+ This option should be used in conjunction with the
+ <code class="option">-u</code> option, as chrooting a process
+ running as root doesn't enhance security on most
+ systems; the way <code class="function">chroot(2)</code> is
+ defined allows a process with root privileges to
+ escape a chroot jail.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-u <em class="replaceable"><code>user</code></em></span></dt>
+<dd>
+<p>Setuid
+ to <em class="replaceable"><code>user</code></em> after completing
+ privileged operations, such as creating sockets that
+ listen on privileged ports.
+ </p>
+<div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Note</h3>
+<p>
+ On Linux, <span><strong class="command">named</strong></span> uses the kernel's
+ capability mechanism to drop all root privileges
+ except the ability to <code class="function">bind(2)</code> to
+ a
+ privileged port and set process resource limits.
+ Unfortunately, this means that the <code class="option">-u</code>
+ option only works when <span><strong class="command">named</strong></span> is
+ run
+ on kernel 2.2.18 or later, or kernel 2.3.99-pre3 or
+ later, since previous kernels did not allow privileges
+ to be retained after <code class="function">setuid(2)</code>.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-v</span></dt>
+<dd><p>
+ Report the version number and exit.
+ </p></dd>
+<dt><span class="term">-x <em class="replaceable"><code>cache-file</code></em></span></dt>
+<dd>
+<p>
+ Load data from <em class="replaceable"><code>cache-file</code></em> into the
+ cache of the default view.
+ </p>
+<div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Warning</h3>
+<p>
+ This option must not be used. It is only of interest
+ to BIND 9 developers and may be removed or changed in a
+ future release.
+ </p>
+</div>
+</dd>
+</dl></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543864"></a><h2>SIGNALS</h2>
+<p>
+ In routine operation, signals should not be used to control
+ the nameserver; <span><strong class="command">rndc</strong></span> should be used
+ instead.
+ </p>
+<div class="variablelist"><dl>
+<dt><span class="term">SIGHUP</span></dt>
+<dd><p>
+ Force a reload of the server.
+ </p></dd>
+<dt><span class="term">SIGINT, SIGTERM</span></dt>
+<dd><p>
+ Shut down the server.
+ </p></dd>
+</dl></div>
+<p>
+ The result of sending any other signals to the server is undefined.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543912"></a><h2>CONFIGURATION</h2>
+<p>
+ The <span><strong class="command">named</strong></span> configuration file is too complex
+ to describe in detail here. A complete description is provided
+ in the
+ <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543929"></a><h2>FILES</h2>
+<div class="variablelist"><dl>
+<dt><span class="term"><code class="filename">/etc/named.conf</code></span></dt>
+<dd><p>
+ The default configuration file.
+ </p></dd>
+<dt><span class="term"><code class="filename">/var/run/named.pid</code></span></dt>
+<dd><p>
+ The default process-id file.
+ </p></dd>
+</dl></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543969"></a><h2>SEE ALSO</h2>
+<p><em class="citetitle">RFC 1033</em>,
+ <em class="citetitle">RFC 1034</em>,
+ <em class="citetitle">RFC 1035</em>,
+ <span class="citerefentry"><span class="refentrytitle">named-checkconf</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">named-checkzone</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">rndc</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">lwresd</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">named.conf</span>(5)</span>,
+ <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2544039"></a><h2>AUTHOR</h2>
+<p><span class="corpauthor">Internet Systems Consortium</span>
+ </p>
+</div>
+</div></body>
+</html>
diff --git a/bin/named/notify.c b/bin/named/notify.c
new file mode 100644
index 0000000..db2be71
--- /dev/null
+++ b/bin/named/notify.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: notify.c,v 1.30.18.3 2005/04/29 00:15:26 marka Exp $ */
+
+#include <config.h>
+
+#include <isc/log.h>
+#include <isc/print.h>
+
+#include <dns/message.h>
+#include <dns/rdataset.h>
+#include <dns/result.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <named/log.h>
+#include <named/notify.h>
+
+/*! \file
+ * \brief
+ * This module implements notify as in RFC1996.
+ */
+
+static void
+notify_log(ns_client_t *client, int level, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ ns_client_logv(client, DNS_LOGCATEGORY_NOTIFY, NS_LOGMODULE_NOTIFY,
+ level, fmt, ap);
+ va_end(ap);
+}
+
+static void
+respond(ns_client_t *client, isc_result_t result) {
+ dns_rcode_t rcode;
+ dns_message_t *message;
+ isc_result_t msg_result;
+
+ message = client->message;
+ rcode = dns_result_torcode(result);
+
+ msg_result = dns_message_reply(message, ISC_TRUE);
+ if (msg_result != ISC_R_SUCCESS)
+ msg_result = dns_message_reply(message, ISC_FALSE);
+ if (msg_result != ISC_R_SUCCESS) {
+ ns_client_next(client, msg_result);
+ return;
+ }
+ message->rcode = rcode;
+ if (rcode == dns_rcode_noerror)
+ message->flags |= DNS_MESSAGEFLAG_AA;
+ else
+ message->flags &= ~DNS_MESSAGEFLAG_AA;
+ ns_client_send(client);
+}
+
+void
+ns_notify_start(ns_client_t *client) {
+ dns_message_t *request = client->message;
+ isc_result_t result;
+ dns_name_t *zonename;
+ dns_rdataset_t *zone_rdataset;
+ dns_zone_t *zone = NULL;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char tsigbuf[DNS_NAME_FORMATSIZE + sizeof(": TSIG ''")];
+ dns_name_t *tsigname;
+
+ /*
+ * Interpret the question section.
+ */
+ result = dns_message_firstname(request, DNS_SECTION_QUESTION);
+ if (result != ISC_R_SUCCESS) {
+ notify_log(client, ISC_LOG_NOTICE,
+ "notify question section empty");
+ goto formerr;
+ }
+
+ /*
+ * The question section must contain exactly one question.
+ */
+ zonename = NULL;
+ dns_message_currentname(request, DNS_SECTION_QUESTION, &zonename);
+ zone_rdataset = ISC_LIST_HEAD(zonename->list);
+ if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) {
+ notify_log(client, ISC_LOG_NOTICE,
+ "notify question section contains multiple RRs");
+ goto formerr;
+ }
+
+ /* The zone section must have exactly one name. */
+ result = dns_message_nextname(request, DNS_SECTION_ZONE);
+ if (result != ISC_R_NOMORE) {
+ notify_log(client, ISC_LOG_NOTICE,
+ "notify question section contains multiple RRs");
+ goto formerr;
+ }
+
+ /* The one rdataset must be an SOA. */
+ if (zone_rdataset->type != dns_rdatatype_soa) {
+ notify_log(client, ISC_LOG_NOTICE,
+ "notify question section contains no SOA");
+ goto formerr;
+ }
+
+ tsigname = NULL;
+ if (dns_message_gettsig(request, &tsigname) != NULL) {
+ dns_name_format(tsigname, namebuf, sizeof(namebuf));
+ snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s'", namebuf);
+ } else
+ tsigbuf[0] = '\0';
+ dns_name_format(zonename, namebuf, sizeof(namebuf));
+ result = dns_zt_find(client->view->zonetable, zonename, 0, NULL,
+ &zone);
+ if (result != ISC_R_SUCCESS)
+ goto notauth;
+
+ switch (dns_zone_gettype(zone)) {
+ case dns_zone_master:
+ case dns_zone_slave:
+ case dns_zone_stub: /* Allow dialup passive to work. */
+ notify_log(client, ISC_LOG_INFO,
+ "received notify for zone '%s'%s", namebuf, tsigbuf);
+ respond(client, dns_zone_notifyreceive(zone,
+ ns_client_getsockaddr(client), request));
+ break;
+ default:
+ goto notauth;
+ }
+ dns_zone_detach(&zone);
+ return;
+
+ notauth:
+ notify_log(client, ISC_LOG_NOTICE,
+ "received notify for zone '%s'%s: not authoritative",
+ namebuf, tsigbuf);
+ result = DNS_R_NOTAUTH;
+ goto failure;
+
+ formerr:
+ result = DNS_R_FORMERR;
+
+ failure:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ respond(client, result);
+}
diff --git a/bin/named/query.c b/bin/named/query.c
new file mode 100644
index 0000000..38eb9a1
--- /dev/null
+++ b/bin/named/query.c
@@ -0,0 +1,4603 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: query.c,v 1.257.18.40 2007/09/26 03:08:14 each Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/byaddr.h>
+#include <dns/db.h>
+#ifdef DLZ
+#include <dns/dlz.h>
+#endif
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/message.h>
+#include <dns/ncache.h>
+#include <dns/order.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/stats.h>
+#include <dns/tkey.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <named/client.h>
+#include <named/log.h>
+#include <named/server.h>
+#include <named/sortlist.h>
+#include <named/xfrout.h>
+
+/*% Partial answer? */
+#define PARTIALANSWER(c) (((c)->query.attributes & \
+ NS_QUERYATTR_PARTIALANSWER) != 0)
+/*% Use Cache? */
+#define USECACHE(c) (((c)->query.attributes & \
+ NS_QUERYATTR_CACHEOK) != 0)
+/*% Recursion OK? */
+#define RECURSIONOK(c) (((c)->query.attributes & \
+ NS_QUERYATTR_RECURSIONOK) != 0)
+/*% Recursing? */
+#define RECURSING(c) (((c)->query.attributes & \
+ NS_QUERYATTR_RECURSING) != 0)
+/*% Cache glue ok? */
+#define CACHEGLUEOK(c) (((c)->query.attributes & \
+ NS_QUERYATTR_CACHEGLUEOK) != 0)
+/*% Want Recursion? */
+#define WANTRECURSION(c) (((c)->query.attributes & \
+ NS_QUERYATTR_WANTRECURSION) != 0)
+/*% Want DNSSEC? */
+#define WANTDNSSEC(c) (((c)->attributes & \
+ NS_CLIENTATTR_WANTDNSSEC) != 0)
+/*% No authority? */
+#define NOAUTHORITY(c) (((c)->query.attributes & \
+ NS_QUERYATTR_NOAUTHORITY) != 0)
+/*% No additional? */
+#define NOADDITIONAL(c) (((c)->query.attributes & \
+ NS_QUERYATTR_NOADDITIONAL) != 0)
+/*% Secure? */
+#define SECURE(c) (((c)->query.attributes & \
+ NS_QUERYATTR_SECURE) != 0)
+
+#if 0
+#define CTRACE(m) isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_CLIENT, \
+ NS_LOGMODULE_QUERY, \
+ ISC_LOG_DEBUG(3), \
+ "client %p: %s", client, (m))
+#define QTRACE(m) isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_GENERAL, \
+ NS_LOGMODULE_QUERY, \
+ ISC_LOG_DEBUG(3), \
+ "query %p: %s", query, (m))
+#else
+#define CTRACE(m) ((void)m)
+#define QTRACE(m) ((void)m)
+#endif
+
+#define DNS_GETDB_NOEXACT 0x01U
+#define DNS_GETDB_NOLOG 0x02U
+#define DNS_GETDB_PARTIAL 0x04U
+
+typedef struct client_additionalctx {
+ ns_client_t *client;
+ dns_rdataset_t *rdataset;
+} client_additionalctx_t;
+
+static void
+query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
+
+static isc_boolean_t
+validate(ns_client_t *client, dns_db_t *db, dns_name_t *name,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+
+/*%
+ * Increment query statistics counters.
+ */
+static inline void
+inc_stats(ns_client_t *client, dns_statscounter_t counter) {
+ dns_zone_t *zone = client->query.authzone;
+
+ REQUIRE(counter < DNS_STATS_NCOUNTERS);
+
+ ns_g_server->querystats[counter]++;
+
+ if (zone != NULL) {
+ isc_uint64_t *zonestats = dns_zone_getstatscounters(zone);
+ if (zonestats != NULL)
+ zonestats[counter]++;
+ }
+}
+
+static void
+query_send(ns_client_t *client) {
+ dns_statscounter_t counter;
+ if (client->message->rcode == dns_rcode_noerror) {
+ if (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER])) {
+ if (client->query.isreferral) {
+ counter = dns_statscounter_referral;
+ } else {
+ counter = dns_statscounter_nxrrset;
+ }
+ } else {
+ counter = dns_statscounter_success;
+ }
+ } else if (client->message->rcode == dns_rcode_nxdomain) {
+ counter = dns_statscounter_nxdomain;
+ } else {
+ /* We end up here in case of YXDOMAIN, and maybe others */
+ counter = dns_statscounter_failure;
+ }
+ inc_stats(client, counter);
+ ns_client_send(client);
+}
+
+static void
+query_error(ns_client_t *client, isc_result_t result) {
+ inc_stats(client, dns_statscounter_failure);
+ ns_client_error(client, result);
+}
+
+static void
+query_next(ns_client_t *client, isc_result_t result) {
+ if (result == DNS_R_DUPLICATE)
+ inc_stats(client, dns_statscounter_duplicate);
+ else if (result == DNS_R_DROP)
+ inc_stats(client, dns_statscounter_dropped);
+ else
+ inc_stats(client, dns_statscounter_failure);
+ ns_client_next(client, result);
+}
+
+static inline void
+query_freefreeversions(ns_client_t *client, isc_boolean_t everything) {
+ ns_dbversion_t *dbversion, *dbversion_next;
+ unsigned int i;
+
+ for (dbversion = ISC_LIST_HEAD(client->query.freeversions), i = 0;
+ dbversion != NULL;
+ dbversion = dbversion_next, i++)
+ {
+ dbversion_next = ISC_LIST_NEXT(dbversion, link);
+ /*
+ * If we're not freeing everything, we keep the first three
+ * dbversions structures around.
+ */
+ if (i > 3 || everything) {
+ ISC_LIST_UNLINK(client->query.freeversions, dbversion,
+ link);
+ isc_mem_put(client->mctx, dbversion,
+ sizeof(*dbversion));
+ }
+ }
+}
+
+void
+ns_query_cancel(ns_client_t *client) {
+ LOCK(&client->query.fetchlock);
+ if (client->query.fetch != NULL) {
+ dns_resolver_cancelfetch(client->query.fetch);
+
+ client->query.fetch = NULL;
+ }
+ UNLOCK(&client->query.fetchlock);
+}
+
+static inline void
+query_reset(ns_client_t *client, isc_boolean_t everything) {
+ isc_buffer_t *dbuf, *dbuf_next;
+ ns_dbversion_t *dbversion, *dbversion_next;
+
+ /*%
+ * Reset the query state of a client to its default state.
+ */
+
+ /*
+ * Cancel the fetch if it's running.
+ */
+ ns_query_cancel(client);
+
+ /*
+ * Cleanup any active versions.
+ */
+ for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
+ dbversion != NULL;
+ dbversion = dbversion_next) {
+ dbversion_next = ISC_LIST_NEXT(dbversion, link);
+ dns_db_closeversion(dbversion->db, &dbversion->version,
+ ISC_FALSE);
+ dns_db_detach(&dbversion->db);
+ ISC_LIST_INITANDAPPEND(client->query.freeversions,
+ dbversion, link);
+ }
+ ISC_LIST_INIT(client->query.activeversions);
+
+ if (client->query.authdb != NULL)
+ dns_db_detach(&client->query.authdb);
+ if (client->query.authzone != NULL)
+ dns_zone_detach(&client->query.authzone);
+
+ query_freefreeversions(client, everything);
+
+ for (dbuf = ISC_LIST_HEAD(client->query.namebufs);
+ dbuf != NULL;
+ dbuf = dbuf_next) {
+ dbuf_next = ISC_LIST_NEXT(dbuf, link);
+ if (dbuf_next != NULL || everything) {
+ ISC_LIST_UNLINK(client->query.namebufs, dbuf, link);
+ isc_buffer_free(&dbuf);
+ }
+ }
+
+ if (client->query.restarts > 0) {
+ /*
+ * client->query.qname was dynamically allocated.
+ */
+ dns_message_puttempname(client->message,
+ &client->query.qname);
+ }
+ client->query.qname = NULL;
+ client->query.attributes = (NS_QUERYATTR_RECURSIONOK |
+ NS_QUERYATTR_CACHEOK |
+ NS_QUERYATTR_SECURE);
+ client->query.restarts = 0;
+ client->query.timerset = ISC_FALSE;
+ client->query.origqname = NULL;
+ client->query.qname = NULL;
+ client->query.dboptions = 0;
+ client->query.fetchoptions = 0;
+ client->query.gluedb = NULL;
+ client->query.authdbset = ISC_FALSE;
+ client->query.isreferral = ISC_FALSE;
+}
+
+static void
+query_next_callback(ns_client_t *client) {
+ query_reset(client, ISC_FALSE);
+}
+
+void
+ns_query_free(ns_client_t *client) {
+ query_reset(client, ISC_TRUE);
+}
+
+static inline isc_result_t
+query_newnamebuf(ns_client_t *client) {
+ isc_buffer_t *dbuf;
+ isc_result_t result;
+
+ CTRACE("query_newnamebuf");
+ /*%
+ * Allocate a name buffer.
+ */
+
+ dbuf = NULL;
+ result = isc_buffer_allocate(client->mctx, &dbuf, 1024);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_newnamebuf: isc_buffer_allocate failed: done");
+ return (result);
+ }
+ ISC_LIST_APPEND(client->query.namebufs, dbuf, link);
+
+ CTRACE("query_newnamebuf: done");
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_buffer_t *
+query_getnamebuf(ns_client_t *client) {
+ isc_buffer_t *dbuf;
+ isc_result_t result;
+ isc_region_t r;
+
+ CTRACE("query_getnamebuf");
+ /*%
+ * Return a name buffer with space for a maximal name, allocating
+ * a new one if necessary.
+ */
+
+ if (ISC_LIST_EMPTY(client->query.namebufs)) {
+ result = query_newnamebuf(client);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_getnamebuf: query_newnamebuf failed: done");
+ return (NULL);
+ }
+ }
+
+ dbuf = ISC_LIST_TAIL(client->query.namebufs);
+ INSIST(dbuf != NULL);
+ isc_buffer_availableregion(dbuf, &r);
+ if (r.length < 255) {
+ result = query_newnamebuf(client);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_getnamebuf: query_newnamebuf failed: done");
+ return (NULL);
+
+ }
+ dbuf = ISC_LIST_TAIL(client->query.namebufs);
+ isc_buffer_availableregion(dbuf, &r);
+ INSIST(r.length >= 255);
+ }
+ CTRACE("query_getnamebuf: done");
+ return (dbuf);
+}
+
+static inline void
+query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
+ isc_region_t r;
+
+ CTRACE("query_keepname");
+ /*%
+ * 'name' is using space in 'dbuf', but 'dbuf' has not yet been
+ * adjusted to take account of that. We do the adjustment.
+ */
+
+ REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0);
+
+ dns_name_toregion(name, &r);
+ isc_buffer_add(dbuf, r.length);
+ dns_name_setbuffer(name, NULL);
+ client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
+}
+
+static inline void
+query_releasename(ns_client_t *client, dns_name_t **namep) {
+ dns_name_t *name = *namep;
+
+ /*%
+ * 'name' is no longer needed. Return it to our pool of temporary
+ * names. If it is using a name buffer, relinquish its exclusive
+ * rights on the buffer.
+ */
+
+ CTRACE("query_releasename");
+ if (dns_name_hasbuffer(name)) {
+ INSIST((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED)
+ != 0);
+ client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
+ }
+ dns_message_puttempname(client->message, namep);
+ CTRACE("query_releasename: done");
+}
+
+static inline dns_name_t *
+query_newname(ns_client_t *client, isc_buffer_t *dbuf,
+ isc_buffer_t *nbuf)
+{
+ dns_name_t *name;
+ isc_region_t r;
+ isc_result_t result;
+
+ REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0);
+
+ CTRACE("query_newname");
+ name = NULL;
+ result = dns_message_gettempname(client->message, &name);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_newname: dns_message_gettempname failed: done");
+ return (NULL);
+ }
+ isc_buffer_availableregion(dbuf, &r);
+ isc_buffer_init(nbuf, r.base, r.length);
+ dns_name_init(name, NULL);
+ dns_name_setbuffer(name, nbuf);
+ client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;
+
+ CTRACE("query_newname: done");
+ return (name);
+}
+
+static inline dns_rdataset_t *
+query_newrdataset(ns_client_t *client) {
+ dns_rdataset_t *rdataset;
+ isc_result_t result;
+
+ CTRACE("query_newrdataset");
+ rdataset = NULL;
+ result = dns_message_gettemprdataset(client->message, &rdataset);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_newrdataset: "
+ "dns_message_gettemprdataset failed: done");
+ return (NULL);
+ }
+ dns_rdataset_init(rdataset);
+
+ CTRACE("query_newrdataset: done");
+ return (rdataset);
+}
+
+static inline void
+query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
+ dns_rdataset_t *rdataset = *rdatasetp;
+
+ CTRACE("query_putrdataset");
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, rdatasetp);
+ }
+ CTRACE("query_putrdataset: done");
+}
+
+
+static inline isc_result_t
+query_newdbversion(ns_client_t *client, unsigned int n) {
+ unsigned int i;
+ ns_dbversion_t *dbversion;
+
+ for (i = 0; i < n; i++) {
+ dbversion = isc_mem_get(client->mctx, sizeof(*dbversion));
+ if (dbversion != NULL) {
+ dbversion->db = NULL;
+ dbversion->version = NULL;
+ ISC_LIST_INITANDAPPEND(client->query.freeversions,
+ dbversion, link);
+ } else {
+ /*
+ * We only return ISC_R_NOMEMORY if we couldn't
+ * allocate anything.
+ */
+ if (i == 0)
+ return (ISC_R_NOMEMORY);
+ else
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline ns_dbversion_t *
+query_getdbversion(ns_client_t *client) {
+ isc_result_t result;
+ ns_dbversion_t *dbversion;
+
+ if (ISC_LIST_EMPTY(client->query.freeversions)) {
+ result = query_newdbversion(client, 1);
+ if (result != ISC_R_SUCCESS)
+ return (NULL);
+ }
+ dbversion = ISC_LIST_HEAD(client->query.freeversions);
+ INSIST(dbversion != NULL);
+ ISC_LIST_UNLINK(client->query.freeversions, dbversion, link);
+
+ return (dbversion);
+}
+
+isc_result_t
+ns_query_init(ns_client_t *client) {
+ isc_result_t result;
+
+ ISC_LIST_INIT(client->query.namebufs);
+ ISC_LIST_INIT(client->query.activeversions);
+ ISC_LIST_INIT(client->query.freeversions);
+ client->query.restarts = 0;
+ client->query.timerset = ISC_FALSE;
+ client->query.qname = NULL;
+ result = isc_mutex_init(&client->query.fetchlock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ client->query.fetch = NULL;
+ client->query.authdb = NULL;
+ client->query.authzone = NULL;
+ client->query.authdbset = ISC_FALSE;
+ client->query.isreferral = ISC_FALSE;
+ query_reset(client, ISC_FALSE);
+ result = query_newdbversion(client, 3);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&client->query.fetchlock);
+ return (result);
+ }
+ result = query_newnamebuf(client);
+ if (result != ISC_R_SUCCESS)
+ query_freefreeversions(client, ISC_TRUE);
+
+ return (result);
+}
+
+static inline ns_dbversion_t *
+query_findversion(ns_client_t *client, dns_db_t *db,
+ isc_boolean_t *newzonep)
+{
+ ns_dbversion_t *dbversion;
+
+ /*%
+ * We may already have done a query related to this
+ * database. If so, we must be sure to make subsequent
+ * queries from the same version.
+ */
+ for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
+ dbversion != NULL;
+ dbversion = ISC_LIST_NEXT(dbversion, link)) {
+ if (dbversion->db == db)
+ break;
+ }
+
+ if (dbversion == NULL) {
+ /*
+ * This is a new zone for this query. Add it to
+ * the active list.
+ */
+ dbversion = query_getdbversion(client);
+ if (dbversion == NULL)
+ return (NULL);
+ dns_db_attach(db, &dbversion->db);
+ dns_db_currentversion(db, &dbversion->version);
+ dbversion->queryok = ISC_FALSE;
+ ISC_LIST_APPEND(client->query.activeversions,
+ dbversion, link);
+ *newzonep = ISC_TRUE;
+ } else
+ *newzonep = ISC_FALSE;
+
+ return (dbversion);
+}
+
+static inline isc_result_t
+query_validatezonedb(ns_client_t *client, dns_name_t *name,
+ dns_rdatatype_t qtype, unsigned int options,
+ dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t **versionp)
+{
+ isc_result_t result;
+ isc_boolean_t check_acl, new_zone;
+ dns_acl_t *queryacl;
+ ns_dbversion_t *dbversion;
+
+ REQUIRE(zone != NULL);
+ REQUIRE(db != NULL);
+
+ /*
+ * This limits our searching to the zone where the first name
+ * (the query target) was looked for. This prevents following
+ * CNAMES or DNAMES into other zones and prevents returning
+ * additional data from other zones.
+ */
+ if (!client->view->additionalfromauth &&
+ client->query.authdbset &&
+ db != client->query.authdb)
+ goto refuse;
+
+ /*
+ * If the zone has an ACL, we'll check it, otherwise
+ * we use the view's "allow-query" ACL. Each ACL is only checked
+ * once per query.
+ *
+ * Also, get the database version to use.
+ */
+
+ check_acl = ISC_TRUE; /* Keep compiler happy. */
+ queryacl = NULL;
+
+ /*
+ * Get the current version of this database.
+ */
+ dbversion = query_findversion(client, db, &new_zone);
+ if (dbversion == NULL) {
+ result = DNS_R_SERVFAIL;
+ goto fail;
+ }
+ if (new_zone) {
+ check_acl = ISC_TRUE;
+ } else if (!dbversion->queryok) {
+ goto refuse;
+ } else {
+ check_acl = ISC_FALSE;
+ }
+
+ queryacl = dns_zone_getqueryacl(zone);
+ if (queryacl == NULL) {
+ queryacl = client->view->queryacl;
+ if ((client->query.attributes &
+ NS_QUERYATTR_QUERYOKVALID) != 0) {
+ /*
+ * We've evaluated the view's queryacl already. If
+ * NS_QUERYATTR_QUERYOK is set, then the client is
+ * allowed to make queries, otherwise the query should
+ * be refused.
+ */
+ check_acl = ISC_FALSE;
+ if ((client->query.attributes &
+ NS_QUERYATTR_QUERYOK) == 0)
+ goto refuse;
+ } else {
+ /*
+ * We haven't evaluated the view's queryacl yet.
+ */
+ check_acl = ISC_TRUE;
+ }
+ }
+
+ if (check_acl) {
+ isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
+
+ result = ns_client_checkaclsilent(client, queryacl, ISC_TRUE);
+ if (log) {
+ char msg[NS_CLIENT_ACLMSGSIZE("query")];
+ if (result == ISC_R_SUCCESS) {
+ if (isc_log_wouldlog(ns_g_lctx,
+ ISC_LOG_DEBUG(3)))
+ {
+ ns_client_aclmsg("query", name, qtype,
+ client->view->rdclass,
+ msg, sizeof(msg));
+ ns_client_log(client,
+ DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_DEBUG(3),
+ "%s approved", msg);
+ }
+ } else {
+ ns_client_aclmsg("query", name, qtype,
+ client->view->rdclass,
+ msg, sizeof(msg));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "%s denied", msg);
+ }
+ }
+
+ if (queryacl == client->view->queryacl) {
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We were allowed by the default
+ * "allow-query" ACL. Remember this so we
+ * don't have to check again.
+ */
+ client->query.attributes |=
+ NS_QUERYATTR_QUERYOK;
+ }
+ /*
+ * We've now evaluated the view's query ACL, and
+ * the NS_QUERYATTR_QUERYOK attribute is now valid.
+ */
+ client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
+ }
+
+ if (result != ISC_R_SUCCESS)
+ goto refuse;
+ }
+
+ /* Approved. */
+
+ /*
+ * Remember the result of the ACL check so we
+ * don't have to check again.
+ */
+ dbversion->queryok = ISC_TRUE;
+
+ /* Transfer ownership, if necessary. */
+ if (versionp != NULL)
+ *versionp = dbversion->version;
+
+ return (ISC_R_SUCCESS);
+
+ refuse:
+ return (DNS_R_REFUSED);
+
+ fail:
+ return (result);
+}
+
+static inline isc_result_t
+query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
+ unsigned int options, dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbversion_t **versionp)
+{
+ isc_result_t result;
+ unsigned int ztoptions;
+ dns_zone_t *zone = NULL;
+ dns_db_t *db = NULL;
+ isc_boolean_t partial = ISC_FALSE;
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ /*%
+ * Find a zone database to answer the query.
+ */
+ ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ?
+ DNS_ZTFIND_NOEXACT : 0;
+
+ result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,
+ &zone);
+ if (result == DNS_R_PARTIALMATCH)
+ partial = ISC_TRUE;
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ result = dns_zone_getdb(zone, &db);
+
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ result = query_validatezonedb(client, name, qtype, options, zone, db,
+ versionp);
+
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ /* Transfer ownership. */
+ *zonep = zone;
+ *dbp = db;
+
+ if (partial && (options & DNS_GETDB_PARTIAL) != 0)
+ return (DNS_R_PARTIALMATCH);
+ return (ISC_R_SUCCESS);
+
+ fail:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ return (result);
+}
+
+static inline isc_result_t
+query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
+ dns_db_t **dbp, unsigned int options)
+{
+ isc_result_t result;
+ isc_boolean_t check_acl;
+ dns_db_t *db = NULL;
+
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ /*%
+ * Find a cache database to answer the query.
+ * This may fail with DNS_R_REFUSED if the client
+ * is not allowed to use the cache.
+ */
+
+ if (!USECACHE(client))
+ return (DNS_R_REFUSED);
+ dns_db_attach(client->view->cachedb, &db);
+
+ if ((client->query.attributes &
+ NS_QUERYATTR_QUERYOKVALID) != 0) {
+ /*
+ * We've evaluated the view's queryacl already. If
+ * NS_QUERYATTR_QUERYOK is set, then the client is
+ * allowed to make queries, otherwise the query should
+ * be refused.
+ */
+ check_acl = ISC_FALSE;
+ if ((client->query.attributes &
+ NS_QUERYATTR_QUERYOK) == 0)
+ goto refuse;
+ } else {
+ /*
+ * We haven't evaluated the view's queryacl yet.
+ */
+ check_acl = ISC_TRUE;
+ }
+
+ if (check_acl) {
+ isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
+ char msg[NS_CLIENT_ACLMSGSIZE("query (cache)")];
+
+ result = ns_client_checkaclsilent(client,
+ client->view->queryacl,
+ ISC_TRUE);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We were allowed by the default
+ * "allow-query" ACL. Remember this so we
+ * don't have to check again.
+ */
+ client->query.attributes |=
+ NS_QUERYATTR_QUERYOK;
+ if (log && isc_log_wouldlog(ns_g_lctx,
+ ISC_LOG_DEBUG(3)))
+ {
+ ns_client_aclmsg("query (cache)", name, qtype,
+ client->view->rdclass,
+ msg, sizeof(msg));
+ ns_client_log(client,
+ DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_DEBUG(3),
+ "%s approved", msg);
+ }
+ } else if (log) {
+ ns_client_aclmsg("query (cache)", name, qtype,
+ client->view->rdclass, msg,
+ sizeof(msg));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "%s denied", msg);
+ }
+ /*
+ * We've now evaluated the view's query ACL, and
+ * the NS_QUERYATTR_QUERYOK attribute is now valid.
+ */
+ client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
+
+ if (result != ISC_R_SUCCESS)
+ goto refuse;
+ }
+
+ /* Approved. */
+
+ /* Transfer ownership. */
+ *dbp = db;
+
+ return (ISC_R_SUCCESS);
+
+ refuse:
+ result = DNS_R_REFUSED;
+
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ return (result);
+}
+
+
+static inline isc_result_t
+query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
+ unsigned int options, dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbversion_t **versionp, isc_boolean_t *is_zonep)
+{
+ isc_result_t result;
+
+#ifdef DLZ
+ isc_result_t tresult;
+ unsigned int namelabels;
+ unsigned int zonelabels;
+ dns_zone_t *zone = NULL;
+ dns_db_t *tdbp;
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ tdbp = NULL;
+
+ /* Calculate how many labels are in name. */
+ namelabels = dns_name_countlabels(name);
+ zonelabels = 0;
+
+ /* Try to find name in bind's standard database. */
+ result = query_getzonedb(client, name, qtype, options, &zone,
+ dbp, versionp);
+
+ /* See how many labels are in the zone's name. */
+ if (result == ISC_R_SUCCESS && zone != NULL)
+ zonelabels = dns_name_countlabels(dns_zone_getorigin(zone));
+ /*
+ * If # zone labels < # name labels, try to find an even better match
+ * Only try if a DLZ driver is loaded for this view
+ */
+ if (zonelabels < namelabels && client->view->dlzdatabase != NULL) {
+ tresult = dns_dlzfindzone(client->view, name,
+ zonelabels, &tdbp);
+ /* If we successful, we found a better match. */
+ if (tresult == ISC_R_SUCCESS) {
+ /*
+ * If the previous search returned a zone, detach it.
+ */
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ /*
+ * If the previous search returned a database,
+ * detach it.
+ */
+ if (*dbp != NULL)
+ dns_db_detach(dbp);
+
+ /*
+ * If the previous search returned a version, clear it.
+ */
+ *versionp = NULL;
+
+ /*
+ * Get our database version.
+ */
+ dns_db_currentversion(tdbp, versionp);
+
+ /*
+ * Be sure to return our database.
+ */
+ *dbp = tdbp;
+
+ /*
+ * We return a null zone, No stats for DLZ zones.
+ */
+ zone = NULL;
+ result = tresult;
+ }
+ }
+#else
+ result = query_getzonedb(client, name, qtype, options,
+ zonep, dbp, versionp);
+#endif
+
+ /* If successfull, Transfer ownership of zone. */
+ if (result == ISC_R_SUCCESS) {
+#ifdef DLZ
+ *zonep = zone;
+#endif
+ /*
+ * If neither attempt above succeeded, return the cache instead
+ */
+ *is_zonep = ISC_TRUE;
+ } else if (result == ISC_R_NOTFOUND) {
+ result = query_getcachedb(client, name, qtype, dbp, options);
+ *is_zonep = ISC_FALSE;
+ }
+ return (result);
+}
+
+static inline isc_boolean_t
+query_isduplicate(ns_client_t *client, dns_name_t *name,
+ dns_rdatatype_t type, dns_name_t **mnamep)
+{
+ dns_section_t section;
+ dns_name_t *mname = NULL;
+ isc_result_t result;
+
+ CTRACE("query_isduplicate");
+
+ for (section = DNS_SECTION_ANSWER;
+ section <= DNS_SECTION_ADDITIONAL;
+ section++) {
+ result = dns_message_findname(client->message, section,
+ name, type, 0, &mname, NULL);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We've already got this RRset in the response.
+ */
+ CTRACE("query_isduplicate: true: done");
+ return (ISC_TRUE);
+ } else if (result == DNS_R_NXRRSET) {
+ /*
+ * The name exists, but the rdataset does not.
+ */
+ if (section == DNS_SECTION_ADDITIONAL)
+ break;
+ } else
+ RUNTIME_CHECK(result == DNS_R_NXDOMAIN);
+ mname = NULL;
+ }
+
+ /*
+ * If the dns_name_t we're looking up is already in the message,
+ * we don't want to trigger the caller's name replacement logic.
+ */
+ if (name == mname)
+ mname = NULL;
+
+ *mnamep = mname;
+
+ CTRACE("query_isduplicate: false: done");
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
+ ns_client_t *client = arg;
+ isc_result_t result, eresult;
+ dns_dbnode_t *node;
+ dns_db_t *db;
+ dns_name_t *fname, *mname;
+ dns_rdataset_t *rdataset, *sigrdataset, *trdataset;
+ isc_buffer_t *dbuf;
+ isc_buffer_t b;
+ dns_dbversion_t *version;
+ isc_boolean_t added_something, need_addname;
+ dns_zone_t *zone;
+ dns_rdatatype_t type;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(qtype != dns_rdatatype_any);
+
+ if (!WANTDNSSEC(client) && dns_rdatatype_isdnssec(qtype))
+ return (ISC_R_SUCCESS);
+
+ CTRACE("query_addadditional");
+
+ /*
+ * Initialization.
+ */
+ eresult = ISC_R_SUCCESS;
+ fname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ trdataset = NULL;
+ db = NULL;
+ version = NULL;
+ node = NULL;
+ added_something = ISC_FALSE;
+ need_addname = ISC_FALSE;
+ zone = NULL;
+
+ /*
+ * We treat type A additional section processing as if it
+ * were "any address type" additional section processing.
+ * To avoid multiple lookups, we do an 'any' database
+ * lookup and iterate over the node.
+ */
+ if (qtype == dns_rdatatype_a)
+ type = dns_rdatatype_any;
+ else
+ type = qtype;
+
+ /*
+ * Get some resources.
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ rdataset = query_newrdataset(client);
+ if (fname == NULL || rdataset == NULL)
+ goto cleanup;
+ if (WANTDNSSEC(client)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+ }
+
+ /*
+ * Look for a zone database that might contain authoritative
+ * additional data.
+ */
+ result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG,
+ &zone, &db, &version);
+ if (result != ISC_R_SUCCESS)
+ goto try_cache;
+
+ CTRACE("query_addadditional: db_find");
+
+ /*
+ * Since we are looking for authoritative data, we do not set
+ * the GLUEOK flag. Glue will be looked for later, but not
+ * necessarily in the same database.
+ */
+ node = NULL;
+ result = dns_db_find(db, name, version, type, client->query.dboptions,
+ client->now, &node, fname, rdataset,
+ sigrdataset);
+ if (result == ISC_R_SUCCESS)
+ goto found;
+
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ version = NULL;
+ dns_db_detach(&db);
+
+ /*
+ * No authoritative data was found. The cache is our next best bet.
+ */
+
+ try_cache:
+ result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG);
+ if (result != ISC_R_SUCCESS)
+ /*
+ * Most likely the client isn't allowed to query the cache.
+ */
+ goto try_glue;
+ /*
+ * Attempt to validate glue.
+ */
+ if (sigrdataset == NULL) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+ }
+ result = dns_db_find(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, rdataset,
+ sigrdataset);
+ if (result == DNS_R_GLUE &&
+ validate(client, db, fname, rdataset, sigrdataset))
+ result = ISC_R_SUCCESS;
+ if (!WANTDNSSEC(client))
+ query_putrdataset(client, &sigrdataset);
+ if (result == ISC_R_SUCCESS)
+ goto found;
+
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+
+ try_glue:
+ /*
+ * No cached data was found. Glue is our last chance.
+ * RFC1035 sayeth:
+ *
+ * NS records cause both the usual additional section
+ * processing to locate a type A record, and, when used
+ * in a referral, a special search of the zone in which
+ * they reside for glue information.
+ *
+ * This is the "special search". Note that we must search
+ * the zone where the NS record resides, not the zone it
+ * points to, and that we only do the search in the delegation
+ * case (identified by client->query.gluedb being set).
+ */
+
+ if (client->query.gluedb == NULL)
+ goto cleanup;
+
+ /*
+ * Don't poision caches using the bailiwick protection model.
+ */
+ if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb)))
+ goto cleanup;
+
+ dns_db_attach(client->query.gluedb, &db);
+ result = dns_db_find(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, rdataset,
+ sigrdataset);
+ if (!(result == ISC_R_SUCCESS ||
+ result == DNS_R_ZONECUT ||
+ result == DNS_R_GLUE))
+ goto cleanup;
+
+ found:
+ /*
+ * We have found a potential additional data rdataset, or
+ * at least a node to iterate over.
+ */
+ query_keepname(client, fname, dbuf);
+
+ /*
+ * If we have an rdataset, add it to the additional data
+ * section.
+ */
+ mname = NULL;
+ if (dns_rdataset_isassociated(rdataset) &&
+ !query_isduplicate(client, fname, type, &mname)) {
+ if (mname != NULL) {
+ query_releasename(client, &fname);
+ fname = mname;
+ } else
+ need_addname = ISC_TRUE;
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ trdataset = rdataset;
+ rdataset = NULL;
+ added_something = ISC_TRUE;
+ /*
+ * Note: we only add SIGs if we've added the type they cover,
+ * so we do not need to check if the SIG rdataset is already
+ * in the response.
+ */
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ {
+ ISC_LIST_APPEND(fname->list, sigrdataset, link);
+ sigrdataset = NULL;
+ }
+ }
+
+ if (qtype == dns_rdatatype_a) {
+ /*
+ * We now go looking for A and AAAA records, along with
+ * their signatures.
+ *
+ * XXXRTH This code could be more efficient.
+ */
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ } else {
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ goto addname;
+ }
+ if (sigrdataset != NULL) {
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ } else if (WANTDNSSEC(client)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto addname;
+ }
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_a, 0,
+ client->now, rdataset,
+ sigrdataset);
+ if (result == DNS_R_NCACHENXDOMAIN)
+ goto addname;
+ if (result == DNS_R_NCACHENXRRSET) {
+ dns_rdataset_disassociate(rdataset);
+ /*
+ * Negative cache entries don't have sigrdatasets.
+ */
+ INSIST(sigrdataset == NULL ||
+ ! dns_rdataset_isassociated(sigrdataset));
+ }
+ if (result == ISC_R_SUCCESS) {
+ mname = NULL;
+ if (!query_isduplicate(client, fname,
+ dns_rdatatype_a, &mname)) {
+ if (mname != NULL) {
+ query_releasename(client, &fname);
+ fname = mname;
+ } else
+ need_addname = ISC_TRUE;
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ added_something = ISC_TRUE;
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ {
+ ISC_LIST_APPEND(fname->list,
+ sigrdataset, link);
+ sigrdataset =
+ query_newrdataset(client);
+ }
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ goto addname;
+ if (WANTDNSSEC(client) && sigrdataset == NULL)
+ goto addname;
+ } else {
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ }
+ }
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_aaaa, 0,
+ client->now, rdataset,
+ sigrdataset);
+ if (result == DNS_R_NCACHENXDOMAIN)
+ goto addname;
+ if (result == DNS_R_NCACHENXRRSET) {
+ dns_rdataset_disassociate(rdataset);
+ INSIST(sigrdataset == NULL ||
+ ! dns_rdataset_isassociated(sigrdataset));
+ }
+ if (result == ISC_R_SUCCESS) {
+ mname = NULL;
+ if (!query_isduplicate(client, fname,
+ dns_rdatatype_aaaa, &mname)) {
+ if (mname != NULL) {
+ query_releasename(client, &fname);
+ fname = mname;
+ } else
+ need_addname = ISC_TRUE;
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ added_something = ISC_TRUE;
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ {
+ ISC_LIST_APPEND(fname->list,
+ sigrdataset, link);
+ sigrdataset = NULL;
+ }
+ rdataset = NULL;
+ }
+ }
+ }
+
+ addname:
+ CTRACE("query_addadditional: addname");
+ /*
+ * If we haven't added anything, then we're done.
+ */
+ if (!added_something)
+ goto cleanup;
+
+ /*
+ * We may have added our rdatasets to an existing name, if so, then
+ * need_addname will be ISC_FALSE. Whether we used an existing name
+ * or a new one, we must set fname to NULL to prevent cleanup.
+ */
+ if (need_addname)
+ dns_message_addname(client->message, fname,
+ DNS_SECTION_ADDITIONAL);
+ fname = NULL;
+
+ /*
+ * In a few cases, we want to add additional data for additional
+ * data. It's simpler to just deal with special cases here than
+ * to try to create a general purpose mechanism and allow the
+ * rdata implementations to do it themselves.
+ *
+ * This involves recursion, but the depth is limited. The
+ * most complex case is adding a SRV rdataset, which involves
+ * recursing to add address records, which in turn can cause
+ * recursion to add KEYs.
+ */
+ if (type == dns_rdatatype_srv && trdataset != NULL) {
+ /*
+ * If we're adding SRV records to the additional data
+ * section, it's helpful if we add the SRV additional data
+ * as well.
+ */
+ eresult = dns_rdataset_additionaldata(trdataset,
+ query_addadditional,
+ client);
+ }
+
+ cleanup:
+ CTRACE("query_addadditional: cleanup");
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ CTRACE("query_addadditional: done");
+ return (eresult);
+}
+
+static inline void
+query_discardcache(ns_client_t *client, dns_rdataset_t *rdataset_base,
+ dns_rdatasetadditional_t additionaltype,
+ dns_rdatatype_t type, dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbversion_t **versionp, dns_dbnode_t **nodep,
+ dns_name_t *fname)
+{
+ dns_rdataset_t *rdataset;
+
+ while ((rdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
+ ISC_LIST_UNLINK(fname->list, rdataset, link);
+ query_putrdataset(client, &rdataset);
+ }
+ if (*versionp != NULL)
+ dns_db_closeversion(*dbp, versionp, ISC_FALSE);
+ if (*nodep != NULL)
+ dns_db_detachnode(*dbp, nodep);
+ if (*dbp != NULL)
+ dns_db_detach(dbp);
+ if (*zonep != NULL)
+ dns_zone_detach(zonep);
+ (void)dns_rdataset_putadditional(client->view->acache, rdataset_base,
+ additionaltype, type);
+}
+
+static inline isc_result_t
+query_iscachevalid(dns_zone_t *zone, dns_db_t *db, dns_db_t *db0,
+ dns_dbversion_t *version)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_dbversion_t *version_current = NULL;
+ dns_db_t *db_current = db0;
+
+ if (db_current == NULL) {
+ result = dns_zone_getdb(zone, &db_current);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ dns_db_currentversion(db_current, &version_current);
+ if (db_current != db || version_current != version) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ cleanup:
+ dns_db_closeversion(db_current, &version_current, ISC_FALSE);
+ if (db0 == NULL && db_current != NULL)
+ dns_db_detach(&db_current);
+
+ return (result);
+}
+
+static isc_result_t
+query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
+ client_additionalctx_t *additionalctx = arg;
+ dns_rdataset_t *rdataset_base;
+ ns_client_t *client;
+ isc_result_t result, eresult;
+ dns_dbnode_t *node, *cnode;
+ dns_db_t *db, *cdb;
+ dns_name_t *fname, *mname0, cfname;
+ dns_rdataset_t *rdataset, *sigrdataset;
+ dns_rdataset_t *crdataset, *crdataset_next;
+ isc_buffer_t *dbuf;
+ isc_buffer_t b;
+ dns_dbversion_t *version, *cversion;
+ isc_boolean_t added_something, need_addname, needadditionalcache;
+ isc_boolean_t need_sigrrset;
+ dns_zone_t *zone;
+ dns_rdatatype_t type;
+ dns_rdatasetadditional_t additionaltype;
+
+ if (qtype != dns_rdatatype_a) {
+ /*
+ * This function is optimized for "address" types. For other
+ * types, use a generic routine.
+ * XXX: ideally, this function should be generic enough.
+ */
+ return (query_addadditional(additionalctx->client,
+ name, qtype));
+ }
+
+ /*
+ * Initialization.
+ */
+ rdataset_base = additionalctx->rdataset;
+ client = additionalctx->client;
+ REQUIRE(NS_CLIENT_VALID(client));
+ eresult = ISC_R_SUCCESS;
+ fname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ db = NULL;
+ cdb = NULL;
+ version = NULL;
+ cversion = NULL;
+ node = NULL;
+ cnode = NULL;
+ added_something = ISC_FALSE;
+ need_addname = ISC_FALSE;
+ zone = NULL;
+ needadditionalcache = ISC_FALSE;
+ additionaltype = dns_rdatasetadditional_fromauth;
+ dns_name_init(&cfname, NULL);
+
+ CTRACE("query_addadditional2");
+
+ /*
+ * We treat type A additional section processing as if it
+ * were "any address type" additional section processing.
+ * To avoid multiple lookups, we do an 'any' database
+ * lookup and iterate over the node.
+ * XXXJT: this approach can cause a suboptimal result when the cache
+ * DB only has partial address types and the glue DB has remaining
+ * ones.
+ */
+ type = dns_rdatatype_any;
+
+ /*
+ * Get some resources.
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL)
+ goto cleanup;
+ dns_name_setbuffer(&cfname, &b); /* share the buffer */
+
+ /* Check additional cache */
+ result = dns_rdataset_getadditional(rdataset_base, additionaltype,
+ type, client->view->acache, &zone,
+ &cdb, &cversion, &cnode, &cfname,
+ client->message, client->now);
+ if (result != ISC_R_SUCCESS)
+ goto findauthdb;
+ if (zone == NULL) {
+ CTRACE("query_addadditional2: auth zone not found");
+ goto try_cache;
+ }
+
+ /* Is the cached DB up-to-date? */
+ result = query_iscachevalid(zone, cdb, NULL, cversion);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_addadditional2: old auth additional cache");
+ query_discardcache(client, rdataset_base, additionaltype,
+ type, &zone, &cdb, &cversion, &cnode,
+ &cfname);
+ goto findauthdb;
+ }
+
+ if (cnode == NULL) {
+ /*
+ * We have a negative cache. We don't have to check the zone
+ * ACL, since the result (not using this zone) would be same
+ * regardless of the result.
+ */
+ CTRACE("query_addadditional2: negative auth additional cache");
+ dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+ dns_db_detach(&cdb);
+ dns_zone_detach(&zone);
+ goto try_cache;
+ }
+
+ result = query_validatezonedb(client, name, qtype, DNS_GETDB_NOLOG,
+ zone, cdb, NULL);
+ if (result != ISC_R_SUCCESS) {
+ query_discardcache(client, rdataset_base, additionaltype,
+ type, &zone, &cdb, &cversion, &cnode,
+ &cfname);
+ goto try_cache;
+ }
+
+ /* We've got an active cache. */
+ CTRACE("query_addadditional2: auth additional cache");
+ dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+ db = cdb;
+ node = cnode;
+ dns_name_clone(&cfname, fname);
+ query_keepname(client, fname, dbuf);
+ goto foundcache;
+
+ /*
+ * Look for a zone database that might contain authoritative
+ * additional data.
+ */
+ findauthdb:
+ result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG,
+ &zone, &db, &version);
+ if (result != ISC_R_SUCCESS) {
+ /* Cache the negative result */
+ (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+ type, client->view->acache,
+ NULL, NULL, NULL, NULL,
+ NULL);
+ goto try_cache;
+ }
+
+ CTRACE("query_addadditional2: db_find");
+
+ /*
+ * Since we are looking for authoritative data, we do not set
+ * the GLUEOK flag. Glue will be looked for later, but not
+ * necessarily in the same database.
+ */
+ node = NULL;
+ result = dns_db_find(db, name, version, type, client->query.dboptions,
+ client->now, &node, fname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ goto found;
+
+ /* Cache the negative result */
+ (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+ type, client->view->acache, zone, db,
+ version, NULL, fname);
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ version = NULL;
+ dns_db_detach(&db);
+
+ /*
+ * No authoritative data was found. The cache is our next best bet.
+ */
+
+ try_cache:
+ additionaltype = dns_rdatasetadditional_fromcache;
+ result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG);
+ if (result != ISC_R_SUCCESS)
+ /*
+ * Most likely the client isn't allowed to query the cache.
+ */
+ goto try_glue;
+
+ result = dns_db_find(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ goto found;
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+
+ try_glue:
+ /*
+ * No cached data was found. Glue is our last chance.
+ * RFC1035 sayeth:
+ *
+ * NS records cause both the usual additional section
+ * processing to locate a type A record, and, when used
+ * in a referral, a special search of the zone in which
+ * they reside for glue information.
+ *
+ * This is the "special search". Note that we must search
+ * the zone where the NS record resides, not the zone it
+ * points to, and that we only do the search in the delegation
+ * case (identified by client->query.gluedb being set).
+ */
+ if (client->query.gluedb == NULL)
+ goto cleanup;
+
+ /*
+ * Don't poision caches using the bailiwick protection model.
+ */
+ if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb)))
+ goto cleanup;
+
+ /* Check additional cache */
+ additionaltype = dns_rdatasetadditional_fromglue;
+ result = dns_rdataset_getadditional(rdataset_base, additionaltype,
+ type, client->view->acache, NULL,
+ &cdb, &cversion, &cnode, &cfname,
+ client->message, client->now);
+ if (result != ISC_R_SUCCESS)
+ goto findglue;
+
+ result = query_iscachevalid(zone, cdb, client->query.gluedb, cversion);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_addadditional2: old glue additional cache");
+ query_discardcache(client, rdataset_base, additionaltype,
+ type, &zone, &cdb, &cversion, &cnode,
+ &cfname);
+ goto findglue;
+ }
+
+ if (cnode == NULL) {
+ /* We have a negative cache. */
+ CTRACE("query_addadditional2: negative glue additional cache");
+ dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+ dns_db_detach(&cdb);
+ goto cleanup;
+ }
+
+ /* Cache hit. */
+ CTRACE("query_addadditional2: glue additional cache");
+ dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+ db = cdb;
+ node = cnode;
+ dns_name_clone(&cfname, fname);
+ query_keepname(client, fname, dbuf);
+ goto foundcache;
+
+ findglue:
+ dns_db_attach(client->query.gluedb, &db);
+ result = dns_db_find(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, NULL, NULL);
+ if (!(result == ISC_R_SUCCESS ||
+ result == DNS_R_ZONECUT ||
+ result == DNS_R_GLUE)) {
+ /* cache the negative result */
+ (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+ type, client->view->acache,
+ NULL, db, version, NULL,
+ fname);
+ goto cleanup;
+ }
+
+ found:
+ /*
+ * We have found a DB node to iterate over from a DB.
+ * We are going to look for address RRsets (i.e., A and AAAA) in the DB
+ * node we've just found. We'll then store the complete information
+ * in the additional data cache.
+ */
+ dns_name_clone(fname, &cfname);
+ query_keepname(client, fname, dbuf);
+ needadditionalcache = ISC_TRUE;
+
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ goto cleanup;
+
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+
+ /*
+ * Find A RRset with sig RRset. Even if we don't find a sig RRset
+ * for a client using DNSSEC, we'll continue the process to make a
+ * complete list to be cached. However, we need to cancel the
+ * caching when something unexpected happens, in order to avoid
+ * caching incomplete information.
+ */
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_a, 0,
+ client->now, rdataset, sigrdataset);
+ /*
+ * If we can't promote glue/pending from the cache to secure
+ * then drop it.
+ */
+ if (result == ISC_R_SUCCESS &&
+ additionaltype == dns_rdatasetadditional_fromcache &&
+ (rdataset->trust == dns_trust_pending ||
+ rdataset->trust == dns_trust_glue) &&
+ !validate(client, db, fname, rdataset, sigrdataset)) {
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ result = ISC_R_NOTFOUND;
+ }
+ if (result == DNS_R_NCACHENXDOMAIN)
+ goto setcache;
+ if (result == DNS_R_NCACHENXRRSET) {
+ dns_rdataset_disassociate(rdataset);
+ /*
+ * Negative cache entries don't have sigrdatasets.
+ */
+ INSIST(! dns_rdataset_isassociated(sigrdataset));
+ }
+ if (result == ISC_R_SUCCESS) {
+ /* Remember the result as a cache */
+ ISC_LIST_APPEND(cfname.list, rdataset, link);
+ if (dns_rdataset_isassociated(sigrdataset)) {
+ ISC_LIST_APPEND(cfname.list, sigrdataset, link);
+ sigrdataset = query_newrdataset(client);
+ }
+ rdataset = query_newrdataset(client);
+ if (sigrdataset == NULL || rdataset == NULL) {
+ /* do not cache incomplete information */
+ goto foundcache;
+ }
+ }
+
+ /* Find AAAA RRset with sig RRset */
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_aaaa,
+ 0, client->now, rdataset, sigrdataset);
+ /*
+ * If we can't promote glue/pending from the cache to secure
+ * then drop it.
+ */
+ if (result == ISC_R_SUCCESS &&
+ additionaltype == dns_rdatasetadditional_fromcache &&
+ (rdataset->trust == dns_trust_pending ||
+ rdataset->trust == dns_trust_glue) &&
+ !validate(client, db, fname, rdataset, sigrdataset)) {
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ result = ISC_R_NOTFOUND;
+ }
+ if (result == ISC_R_SUCCESS) {
+ ISC_LIST_APPEND(cfname.list, rdataset, link);
+ rdataset = NULL;
+ if (dns_rdataset_isassociated(sigrdataset)) {
+ ISC_LIST_APPEND(cfname.list, sigrdataset, link);
+ sigrdataset = NULL;
+ }
+ }
+
+ setcache:
+ /*
+ * Set the new result in the cache if required. We do not support
+ * caching additional data from a cache DB.
+ */
+ if (needadditionalcache == ISC_TRUE &&
+ (additionaltype == dns_rdatasetadditional_fromauth ||
+ additionaltype == dns_rdatasetadditional_fromglue)) {
+ (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+ type, client->view->acache,
+ zone, db, version, node,
+ &cfname);
+ }
+
+ foundcache:
+ need_sigrrset = ISC_FALSE;
+ mname0 = NULL;
+ for (crdataset = ISC_LIST_HEAD(cfname.list);
+ crdataset != NULL;
+ crdataset = crdataset_next) {
+ dns_name_t *mname;
+
+ crdataset_next = ISC_LIST_NEXT(crdataset, link);
+
+ mname = NULL;
+ if (crdataset->type == dns_rdatatype_a ||
+ crdataset->type == dns_rdatatype_aaaa) {
+ if (!query_isduplicate(client, fname, crdataset->type,
+ &mname)) {
+ if (mname != NULL) {
+ /*
+ * A different type of this name is
+ * already stored in the additional
+ * section. We'll reuse the name.
+ * Note that this should happen at most
+ * once. Otherwise, fname->link could
+ * leak below.
+ */
+ INSIST(mname0 == NULL);
+
+ query_releasename(client, &fname);
+ fname = mname;
+ mname0 = mname;
+ } else
+ need_addname = ISC_TRUE;
+ ISC_LIST_UNLINK(cfname.list, crdataset, link);
+ ISC_LIST_APPEND(fname->list, crdataset, link);
+ added_something = ISC_TRUE;
+ need_sigrrset = ISC_TRUE;
+ } else
+ need_sigrrset = ISC_FALSE;
+ } else if (crdataset->type == dns_rdatatype_rrsig &&
+ need_sigrrset && WANTDNSSEC(client)) {
+ ISC_LIST_UNLINK(cfname.list, crdataset, link);
+ ISC_LIST_APPEND(fname->list, crdataset, link);
+ added_something = ISC_TRUE; /* just in case */
+ need_sigrrset = ISC_FALSE;
+ }
+ }
+
+ CTRACE("query_addadditional2: addname");
+
+ /*
+ * If we haven't added anything, then we're done.
+ */
+ if (!added_something)
+ goto cleanup;
+
+ /*
+ * We may have added our rdatasets to an existing name, if so, then
+ * need_addname will be ISC_FALSE. Whether we used an existing name
+ * or a new one, we must set fname to NULL to prevent cleanup.
+ */
+ if (need_addname)
+ dns_message_addname(client->message, fname,
+ DNS_SECTION_ADDITIONAL);
+ fname = NULL;
+
+ cleanup:
+ CTRACE("query_addadditional2: cleanup");
+
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ while ((crdataset = ISC_LIST_HEAD(cfname.list)) != NULL) {
+ ISC_LIST_UNLINK(cfname.list, crdataset, link);
+ query_putrdataset(client, &crdataset);
+ }
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ CTRACE("query_addadditional2: done");
+ return (eresult);
+}
+
+static inline void
+query_addrdataset(ns_client_t *client, dns_name_t *fname,
+ dns_rdataset_t *rdataset)
+{
+ client_additionalctx_t additionalctx;
+
+ /*
+ * Add 'rdataset' and any pertinent additional data to
+ * 'fname', a name in the response message for 'client'.
+ */
+
+ CTRACE("query_addrdataset");
+
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+
+ if (client->view->order != NULL)
+ rdataset->attributes |= dns_order_find(client->view->order,
+ fname, rdataset->type,
+ rdataset->rdclass);
+ rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
+
+ if (NOADDITIONAL(client))
+ return;
+
+ /*
+ * Add additional data.
+ *
+ * We don't care if dns_rdataset_additionaldata() fails.
+ */
+ additionalctx.client = client;
+ additionalctx.rdataset = rdataset;
+ (void)dns_rdataset_additionaldata(rdataset, query_addadditional2,
+ &additionalctx);
+ CTRACE("query_addrdataset: done");
+}
+
+static void
+query_addrrset(ns_client_t *client, dns_name_t **namep,
+ dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp,
+ isc_buffer_t *dbuf, dns_section_t section)
+{
+ dns_name_t *name, *mname;
+ dns_rdataset_t *rdataset, *mrdataset, *sigrdataset;
+ isc_result_t result;
+
+ /*%
+ * To the current response for 'client', add the answer RRset
+ * '*rdatasetp' and an optional signature set '*sigrdatasetp', with
+ * owner name '*namep', to section 'section', unless they are
+ * already there. Also add any pertinent additional data.
+ *
+ * If 'dbuf' is not NULL, then '*namep' is the name whose data is
+ * stored in 'dbuf'. In this case, query_addrrset() guarantees that
+ * when it returns the name will either have been kept or released.
+ */
+ CTRACE("query_addrrset");
+ name = *namep;
+ rdataset = *rdatasetp;
+ if (sigrdatasetp != NULL)
+ sigrdataset = *sigrdatasetp;
+ else
+ sigrdataset = NULL;
+ mname = NULL;
+ mrdataset = NULL;
+ result = dns_message_findname(client->message, section,
+ name, rdataset->type, rdataset->covers,
+ &mname, &mrdataset);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We've already got an RRset of the given name and type.
+ * There's nothing else to do;
+ */
+ CTRACE("query_addrrset: dns_message_findname succeeded: done");
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ return;
+ } else if (result == DNS_R_NXDOMAIN) {
+ /*
+ * The name doesn't exist.
+ */
+ if (dbuf != NULL)
+ query_keepname(client, name, dbuf);
+ dns_message_addname(client->message, name, section);
+ *namep = NULL;
+ mname = name;
+ } else {
+ RUNTIME_CHECK(result == DNS_R_NXRRSET);
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ }
+
+ if (rdataset->trust != dns_trust_secure &&
+ (section == DNS_SECTION_ANSWER ||
+ section == DNS_SECTION_AUTHORITY))
+ client->query.attributes &= ~NS_QUERYATTR_SECURE;
+ /*
+ * Note: we only add SIGs if we've added the type they cover, so
+ * we do not need to check if the SIG rdataset is already in the
+ * response.
+ */
+ query_addrdataset(client, mname, rdataset);
+ *rdatasetp = NULL;
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) {
+ /*
+ * We have a signature. Add it to the response.
+ */
+ ISC_LIST_APPEND(mname->list, sigrdataset, link);
+ *sigrdatasetp = NULL;
+ }
+ CTRACE("query_addrrset: done");
+}
+
+static inline isc_result_t
+query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version,
+ isc_boolean_t zero_ttl)
+{
+ dns_name_t *name;
+ dns_dbnode_t *node;
+ isc_result_t result, eresult;
+ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+ dns_rdataset_t **sigrdatasetp = NULL;
+
+ CTRACE("query_addsoa");
+ /*
+ * Initialization.
+ */
+ eresult = ISC_R_SUCCESS;
+ name = NULL;
+ rdataset = NULL;
+ node = NULL;
+
+ /*
+ * Get resources and make 'name' be the database origin.
+ */
+ result = dns_message_gettempname(client->message, &name);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_name_init(name, NULL);
+ dns_name_clone(dns_db_origin(db), name);
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL) {
+ eresult = DNS_R_SERVFAIL;
+ goto cleanup;
+ }
+ if (WANTDNSSEC(client)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ eresult = DNS_R_SERVFAIL;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Find the SOA.
+ */
+ result = dns_db_getoriginnode(db, &node);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_soa,
+ 0, client->now, rdataset,
+ sigrdataset);
+ } else {
+ dns_fixedname_t foundname;
+ dns_name_t *fname;
+
+ dns_fixedname_init(&foundname);
+ fname = dns_fixedname_name(&foundname);
+
+ result = dns_db_find(db, name, version, dns_rdatatype_soa,
+ client->query.dboptions, 0, &node,
+ fname, rdataset, sigrdataset);
+ }
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * This is bad. We tried to get the SOA RR at the zone top
+ * and it didn't work!
+ */
+ eresult = DNS_R_SERVFAIL;
+ } else {
+ /*
+ * Extract the SOA MINIMUM.
+ */
+ dns_rdata_soa_t soa;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ result = dns_rdataset_first(rdataset);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (zero_ttl) {
+ rdataset->ttl = 0;
+ if (sigrdataset != NULL)
+ sigrdataset->ttl = 0;
+ }
+
+ /*
+ * Add the SOA and its SIG to the response, with the
+ * TTLs adjusted per RFC2308 section 3.
+ */
+ if (rdataset->ttl > soa.minimum)
+ rdataset->ttl = soa.minimum;
+ if (sigrdataset != NULL && sigrdataset->ttl > soa.minimum)
+ sigrdataset->ttl = soa.minimum;
+
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL,
+ DNS_SECTION_AUTHORITY);
+ }
+
+ cleanup:
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (name != NULL)
+ query_releasename(client, &name);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ return (eresult);
+}
+
+static inline isc_result_t
+query_addns(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version) {
+ dns_name_t *name, *fname;
+ dns_dbnode_t *node;
+ isc_result_t result, eresult;
+ dns_fixedname_t foundname;
+ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+ dns_rdataset_t **sigrdatasetp = NULL;
+
+ CTRACE("query_addns");
+ /*
+ * Initialization.
+ */
+ eresult = ISC_R_SUCCESS;
+ name = NULL;
+ rdataset = NULL;
+ node = NULL;
+ dns_fixedname_init(&foundname);
+ fname = dns_fixedname_name(&foundname);
+
+ /*
+ * Get resources and make 'name' be the database origin.
+ */
+ result = dns_message_gettempname(client->message, &name);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_addns: dns_message_gettempname failed: done");
+ return (result);
+ }
+ dns_name_init(name, NULL);
+ dns_name_clone(dns_db_origin(db), name);
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL) {
+ CTRACE("query_addns: query_newrdataset failed");
+ eresult = DNS_R_SERVFAIL;
+ goto cleanup;
+ }
+ if (WANTDNSSEC(client)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ CTRACE("query_addns: query_newrdataset failed");
+ eresult = DNS_R_SERVFAIL;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Find the NS rdataset.
+ */
+ result = dns_db_getoriginnode(db, &node);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_ns,
+ 0, client->now, rdataset,
+ sigrdataset);
+ } else {
+ CTRACE("query_addns: calling dns_db_find");
+ result = dns_db_find(db, name, NULL, dns_rdatatype_ns,
+ client->query.dboptions, 0, &node,
+ fname, rdataset, sigrdataset);
+ CTRACE("query_addns: dns_db_find complete");
+ }
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_addns: "
+ "dns_db_findrdataset or dns_db_find failed");
+ /*
+ * This is bad. We tried to get the NS rdataset at the zone
+ * top and it didn't work!
+ */
+ eresult = DNS_R_SERVFAIL;
+ } else {
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL,
+ DNS_SECTION_AUTHORITY);
+ }
+
+ cleanup:
+ CTRACE("query_addns: cleanup");
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (name != NULL)
+ query_releasename(client, &name);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ CTRACE("query_addns: done");
+ return (eresult);
+}
+
+static inline isc_result_t
+query_addcnamelike(ns_client_t *client, dns_name_t *qname, dns_name_t *tname,
+ dns_trust_t trust, dns_name_t **anamep, dns_rdatatype_t type)
+{
+ dns_rdataset_t *rdataset;
+ dns_rdatalist_t *rdatalist;
+ dns_rdata_t *rdata;
+ isc_result_t result;
+ isc_region_t r;
+
+ /*
+ * We assume the name data referred to by tname won't go away.
+ */
+
+ REQUIRE(anamep != NULL);
+
+ rdatalist = NULL;
+ result = dns_message_gettemprdatalist(client->message, &rdatalist);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdata = NULL;
+ result = dns_message_gettemprdata(client->message, &rdata);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdataset = NULL;
+ result = dns_message_gettemprdataset(client->message, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_init(rdataset);
+ result = dns_name_dup(qname, client->mctx, *anamep);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttemprdataset(client->message, &rdataset);
+ return (result);
+ }
+
+ rdatalist->type = type;
+ rdatalist->covers = 0;
+ rdatalist->rdclass = client->message->rdclass;
+ rdatalist->ttl = 0;
+
+ dns_name_toregion(tname, &r);
+ rdata->data = r.base;
+ rdata->length = r.length;
+ rdata->rdclass = client->message->rdclass;
+ rdata->type = type;
+
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset)
+ == ISC_R_SUCCESS);
+ rdataset->trust = trust;
+
+ query_addrrset(client, anamep, &rdataset, NULL, NULL,
+ DNS_SECTION_ANSWER);
+
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, &rdataset);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Mark the RRsets as secure. Update the cache (db) to reflect the
+ * change in trust level.
+ */
+static void
+mark_secure(ns_client_t *client, dns_db_t *db, dns_name_t *name,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+
+ rdataset->trust = dns_trust_secure;
+ sigrdataset->trust = dns_trust_secure;
+
+ /*
+ * Save the updated secure state. Ignore failures.
+ */
+ result = dns_db_findnode(db, name, ISC_TRUE, &node);
+ if (result != ISC_R_SUCCESS)
+ return;
+ (void)dns_db_addrdataset(db, node, NULL, client->now, rdataset,
+ 0, NULL);
+ (void)dns_db_addrdataset(db, node, NULL, client->now, sigrdataset,
+ 0, NULL);
+ dns_db_detachnode(db, &node);
+}
+
+/*
+ * Find the secure key that corresponds to rrsig.
+ * Note: 'keyrdataset' maintains state between sucessive calls,
+ * there may be multiple keys with the same keyid.
+ * Return ISC_FALSE if we have exhausted all the possible keys.
+ */
+static isc_boolean_t
+get_key(ns_client_t *client, dns_db_t *db, dns_rdata_rrsig_t *rrsig,
+ dns_rdataset_t *keyrdataset, dst_key_t **keyp)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ isc_boolean_t secure = ISC_FALSE;
+
+ if (!dns_rdataset_isassociated(keyrdataset)) {
+ result = dns_db_findnode(db, &rrsig->signer, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+
+ result = dns_db_findrdataset(db, node, NULL,
+ dns_rdatatype_dnskey, 0,
+ client->now, keyrdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+
+ if (keyrdataset->trust != dns_trust_secure)
+ return (ISC_FALSE);
+
+ result = dns_rdataset_first(keyrdataset);
+ } else
+ result = dns_rdataset_next(keyrdataset);
+
+ for ( ; result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(keyrdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t b;
+
+ dns_rdataset_current(keyrdataset, &rdata);
+ isc_buffer_init(&b, rdata.data, rdata.length);
+ isc_buffer_add(&b, rdata.length);
+ result = dst_key_fromdns(&rrsig->signer, rdata.rdclass, &b,
+ client->mctx, keyp);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if (rrsig->algorithm == (dns_secalg_t)dst_key_alg(*keyp) &&
+ rrsig->keyid == (dns_keytag_t)dst_key_id(*keyp) &&
+ dst_key_iszonekey(*keyp)) {
+ secure = ISC_TRUE;
+ break;
+ }
+ dst_key_free(keyp);
+ }
+ return (secure);
+}
+
+static isc_boolean_t
+verify(dst_key_t *key, dns_name_t *name, dns_rdataset_t *rdataset,
+ dns_rdata_t *rdata, isc_mem_t *mctx, isc_boolean_t acceptexpired)
+{
+ isc_result_t result;
+ dns_fixedname_t fixed;
+ isc_boolean_t ignore = ISC_FALSE;
+
+ dns_fixedname_init(&fixed);
+
+again:
+ result = dns_dnssec_verify2(name, rdataset, key, ignore, mctx,
+ rdata, NULL);
+ if (result == DNS_R_SIGEXPIRED && acceptexpired) {
+ ignore = ISC_TRUE;
+ goto again;
+ }
+ if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+/*
+ * Validate the rdataset if possible with available records.
+ */
+static isc_boolean_t
+validate(ns_client_t *client, dns_db_t *db, dns_name_t *name,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t rrsig;
+ dst_key_t *key = NULL;
+ dns_rdataset_t keyrdataset;
+
+ if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset))
+ return (ISC_FALSE);
+
+ for (result = dns_rdataset_first(sigrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(sigrdataset)) {
+
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(sigrdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+ if (!dns_resolver_algorithm_supported(client->view->resolver,
+ name, rrsig.algorithm))
+ continue;
+ if (!dns_name_issubdomain(name, &rrsig.signer))
+ continue;
+ dns_rdataset_init(&keyrdataset);
+ do {
+ if (!get_key(client, db, &rrsig, &keyrdataset, &key))
+ break;
+ if (verify(key, name, rdataset, &rdata, client->mctx,
+ client->view->acceptexpired)) {
+ dst_key_free(&key);
+ dns_rdataset_disassociate(&keyrdataset);
+ mark_secure(client, db, name, rdataset,
+ sigrdataset);
+ return (ISC_TRUE);
+ }
+ dst_key_free(&key);
+ } while (1);
+ if (dns_rdataset_isassociated(&keyrdataset))
+ dns_rdataset_disassociate(&keyrdataset);
+ }
+ return (ISC_FALSE);
+}
+
+static void
+query_addbestns(ns_client_t *client) {
+ dns_db_t *db, *zdb;
+ dns_dbnode_t *node;
+ dns_name_t *fname, *zfname;
+ dns_rdataset_t *rdataset, *sigrdataset, *zrdataset, *zsigrdataset;
+ isc_boolean_t is_zone, use_zone;
+ isc_buffer_t *dbuf;
+ isc_result_t result;
+ dns_dbversion_t *version;
+ dns_zone_t *zone;
+ isc_buffer_t b;
+
+ CTRACE("query_addbestns");
+ fname = NULL;
+ zfname = NULL;
+ rdataset = NULL;
+ zrdataset = NULL;
+ sigrdataset = NULL;
+ zsigrdataset = NULL;
+ node = NULL;
+ db = NULL;
+ zdb = NULL;
+ version = NULL;
+ zone = NULL;
+ is_zone = ISC_FALSE;
+ use_zone = ISC_FALSE;
+
+ /*
+ * Find the right database.
+ */
+ result = query_getdb(client, client->query.qname, dns_rdatatype_ns, 0,
+ &zone, &db, &version, &is_zone);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ db_find:
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ rdataset = query_newrdataset(client);
+ if (fname == NULL || rdataset == NULL)
+ goto cleanup;
+ /*
+ * Get the RRSIGs if the client requested them or if we may
+ * need to validate answers from the cache.
+ */
+ if (WANTDNSSEC(client) || !is_zone) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+ }
+
+ /*
+ * Now look for the zonecut.
+ */
+ if (is_zone) {
+ result = dns_db_find(db, client->query.qname, version,
+ dns_rdatatype_ns, client->query.dboptions,
+ client->now, &node, fname,
+ rdataset, sigrdataset);
+ if (result != DNS_R_DELEGATION)
+ goto cleanup;
+ if (USECACHE(client)) {
+ query_keepname(client, fname, dbuf);
+ zdb = db;
+ zfname = fname;
+ fname = NULL;
+ zrdataset = rdataset;
+ rdataset = NULL;
+ zsigrdataset = sigrdataset;
+ sigrdataset = NULL;
+ dns_db_detachnode(db, &node);
+ version = NULL;
+ db = NULL;
+ dns_db_attach(client->view->cachedb, &db);
+ is_zone = ISC_FALSE;
+ goto db_find;
+ }
+ } else {
+ result = dns_db_findzonecut(db, client->query.qname,
+ client->query.dboptions,
+ client->now, &node, fname,
+ rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ if (zfname != NULL &&
+ !dns_name_issubdomain(fname, zfname)) {
+ /*
+ * We found a zonecut in the cache, but our
+ * zone delegation is better.
+ */
+ use_zone = ISC_TRUE;
+ }
+ } else if (result == ISC_R_NOTFOUND && zfname != NULL) {
+ /*
+ * We didn't find anything in the cache, but we
+ * have a zone delegation, so use it.
+ */
+ use_zone = ISC_TRUE;
+ } else
+ goto cleanup;
+ }
+
+ if (use_zone) {
+ query_releasename(client, &fname);
+ fname = zfname;
+ zfname = NULL;
+ /*
+ * We've already done query_keepname() on
+ * zfname, so we must set dbuf to NULL to
+ * prevent query_addrrset() from trying to
+ * call query_keepname() again.
+ */
+ dbuf = NULL;
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ rdataset = zrdataset;
+ zrdataset = NULL;
+ sigrdataset = zsigrdataset;
+ zsigrdataset = NULL;
+ }
+
+ /*
+ * Attempt to validate RRsets that are pending or that are glue.
+ */
+ if ((rdataset->trust == dns_trust_pending ||
+ (sigrdataset != NULL && sigrdataset->trust == dns_trust_pending))
+ && !validate(client, db, fname, rdataset, sigrdataset) &&
+ (client->query.dboptions & DNS_DBFIND_PENDINGOK) == 0)
+ goto cleanup;
+
+ if ((rdataset->trust == dns_trust_glue ||
+ (sigrdataset != NULL && sigrdataset->trust == dns_trust_glue)) &&
+ !validate(client, db, fname, rdataset, sigrdataset) &&
+ SECURE(client) && WANTDNSSEC(client))
+ goto cleanup;
+
+ /*
+ * If the client doesn't want DNSSEC we can discard the sigrdataset
+ * now.
+ */
+ if (!WANTDNSSEC(client))
+ query_putrdataset(client, &sigrdataset);
+ query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf,
+ DNS_SECTION_AUTHORITY);
+
+ cleanup:
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (zdb != NULL) {
+ query_putrdataset(client, &zrdataset);
+ if (zsigrdataset != NULL)
+ query_putrdataset(client, &zsigrdataset);
+ if (zfname != NULL)
+ query_releasename(client, &zfname);
+ dns_db_detach(&zdb);
+ }
+}
+
+static void
+query_addds(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version)
+{
+ dns_name_t *rname;
+ dns_rdataset_t *rdataset, *sigrdataset;
+ isc_result_t result;
+
+ CTRACE("query_addds");
+ rname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+
+ /*
+ * We'll need some resources...
+ */
+ rdataset = query_newrdataset(client);
+ sigrdataset = query_newrdataset(client);
+ if (rdataset == NULL || sigrdataset == NULL)
+ goto cleanup;
+
+ /*
+ * Look for the DS record, which may or may not be present.
+ */
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_ds, 0,
+ client->now, rdataset, sigrdataset);
+ /*
+ * If we didn't find it, look for an NSEC. */
+ if (result == ISC_R_NOTFOUND)
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec, 0, client->now,
+ rdataset, sigrdataset);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto cleanup;
+ if (!dns_rdataset_isassociated(rdataset) ||
+ !dns_rdataset_isassociated(sigrdataset))
+ goto cleanup;
+
+ /*
+ * We've already added the NS record, so if the name's not there,
+ * we have other problems. Use this name rather than calling
+ * query_addrrset().
+ */
+ result = dns_message_firstname(client->message, DNS_SECTION_AUTHORITY);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ rname = NULL;
+ dns_message_currentname(client->message, DNS_SECTION_AUTHORITY,
+ &rname);
+ result = dns_message_findtype(rname, dns_rdatatype_ns, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ ISC_LIST_APPEND(rname->list, rdataset, link);
+ ISC_LIST_APPEND(rname->list, sigrdataset, link);
+ rdataset = NULL;
+ sigrdataset = NULL;
+
+ cleanup:
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+}
+
+static void
+query_addwildcardproof(ns_client_t *client, dns_db_t *db,
+ dns_dbversion_t *version, dns_name_t *name,
+ isc_boolean_t ispositive)
+{
+ isc_buffer_t *dbuf, b;
+ dns_name_t *fname;
+ dns_rdataset_t *rdataset, *sigrdataset;
+ dns_fixedname_t wfixed;
+ dns_name_t *wname;
+ dns_dbnode_t *node;
+ unsigned int options;
+ unsigned int olabels, nlabels;
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec_t nsec;
+ isc_boolean_t have_wname;
+ int order;
+
+ CTRACE("query_addwildcardproof");
+ fname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ node = NULL;
+
+ /*
+ * Get the NOQNAME proof then if !ispositve
+ * get the NOWILDCARD proof.
+ *
+ * DNS_DBFIND_NOWILD finds the NSEC records that covers the
+ * name ignoring any wildcard. From the owner and next names
+ * of this record you can compute which wildcard (if it exists)
+ * will match by finding the longest common suffix of the
+ * owner name and next names with the qname and prefixing that
+ * with the wildcard label.
+ *
+ * e.g.
+ * Given:
+ * example SOA
+ * example NSEC b.example
+ * b.example A
+ * b.example NSEC a.d.example
+ * a.d.example A
+ * a.d.example NSEC g.f.example
+ * g.f.example A
+ * g.f.example NSEC z.i.example
+ * z.i.example A
+ * z.i.example NSEC example
+ *
+ * QNAME:
+ * a.example -> example NSEC b.example
+ * owner common example
+ * next common example
+ * wild *.example
+ * d.b.example -> b.example NSEC a.d.example
+ * owner common b.example
+ * next common example
+ * wild *.b.example
+ * a.f.example -> a.d.example NSEC g.f.example
+ * owner common example
+ * next common f.example
+ * wild *.f.example
+ * j.example -> z.i.example NSEC example
+ * owner common example
+ * next common example
+ * wild *.f.example
+ */
+ options = client->query.dboptions | DNS_DBFIND_NOWILD;
+ dns_fixedname_init(&wfixed);
+ wname = dns_fixedname_name(&wfixed);
+ again:
+ have_wname = ISC_FALSE;
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ rdataset = query_newrdataset(client);
+ sigrdataset = query_newrdataset(client);
+ if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
+ goto cleanup;
+
+ result = dns_db_find(db, name, version, dns_rdatatype_nsec, options,
+ 0, &node, fname, rdataset, sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (result == DNS_R_NXDOMAIN) {
+ if (!ispositive)
+ result = dns_rdataset_first(rdataset);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &nsec, NULL);
+ }
+ if (result == ISC_R_SUCCESS) {
+ (void)dns_name_fullcompare(name, fname, &order,
+ &olabels);
+ (void)dns_name_fullcompare(name, &nsec.next, &order,
+ &nlabels);
+ if (olabels > nlabels)
+ dns_name_split(name, olabels, NULL, wname);
+ else
+ dns_name_split(name, nlabels, NULL, wname);
+ result = dns_name_concatenate(dns_wildcardname,
+ wname, wname, NULL);
+ if (result == ISC_R_SUCCESS)
+ have_wname = ISC_TRUE;
+ dns_rdata_freestruct(&nsec);
+ }
+ query_addrrset(client, &fname, &rdataset, &sigrdataset,
+ dbuf, DNS_SECTION_AUTHORITY);
+ }
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (have_wname) {
+ ispositive = ISC_TRUE; /* prevent loop */
+ if (!dns_name_equal(name, wname)) {
+ name = wname;
+ goto again;
+ }
+ }
+ cleanup:
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+}
+
+static void
+query_addnxrrsetnsec(ns_client_t *client, dns_db_t *db,
+ dns_dbversion_t *version, dns_name_t **namep,
+ dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp)
+{
+ dns_name_t *name;
+ dns_rdataset_t *sigrdataset;
+ dns_rdata_t sigrdata;
+ dns_rdata_rrsig_t sig;
+ unsigned int labels;
+ isc_buffer_t *dbuf, b;
+ dns_name_t *fname;
+ isc_result_t result;
+
+ name = *namep;
+ if ((name->attributes & DNS_NAMEATTR_WILDCARD) == 0) {
+ query_addrrset(client, namep, rdatasetp, sigrdatasetp,
+ NULL, DNS_SECTION_AUTHORITY);
+ return;
+ }
+
+ if (sigrdatasetp == NULL)
+ return;
+ sigrdataset = *sigrdatasetp;
+ if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset))
+ return;
+ result = dns_rdataset_first(sigrdataset);
+ if (result != ISC_R_SUCCESS)
+ return;
+ dns_rdata_init(&sigrdata);
+ dns_rdataset_current(sigrdataset, &sigrdata);
+ result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ labels = dns_name_countlabels(name);
+ if ((unsigned int)sig.labels + 1 >= labels)
+ return;
+
+ /* XXX */
+ query_addwildcardproof(client, db, version, client->query.qname,
+ ISC_TRUE);
+
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ return;
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL)
+ return;
+ dns_name_split(name, sig.labels + 1, NULL, fname);
+ /* This will succeed, since we've stripped labels. */
+ RUNTIME_CHECK(dns_name_concatenate(dns_wildcardname, fname, fname,
+ NULL) == ISC_R_SUCCESS);
+ query_addrrset(client, &fname, rdatasetp, sigrdatasetp,
+ dbuf, DNS_SECTION_AUTHORITY);
+}
+
+static void
+query_resume(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
+ ns_client_t *client;
+ isc_boolean_t fetch_cancelled, client_shuttingdown;
+
+ /*
+ * Resume a query after recursion.
+ */
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+ client = devent->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+ REQUIRE(RECURSING(client));
+
+ LOCK(&client->query.fetchlock);
+ if (client->query.fetch != NULL) {
+ /*
+ * This is the fetch we've been waiting for.
+ */
+ INSIST(devent->fetch == client->query.fetch);
+ client->query.fetch = NULL;
+ fetch_cancelled = ISC_FALSE;
+ /*
+ * Update client->now.
+ */
+ isc_stdtime_get(&client->now);
+ } else {
+ /*
+ * This is a fetch completion event for a cancelled fetch.
+ * Clean up and don't resume the find.
+ */
+ fetch_cancelled = ISC_TRUE;
+ }
+ UNLOCK(&client->query.fetchlock);
+ INSIST(client->query.fetch == NULL);
+
+ client->query.attributes &= ~NS_QUERYATTR_RECURSING;
+ dns_resolver_destroyfetch(&devent->fetch);
+
+ /*
+ * If this client is shutting down, or this transaction
+ * has timed out, do not resume the find.
+ */
+ client_shuttingdown = ns_client_shuttingdown(client);
+ if (fetch_cancelled || client_shuttingdown) {
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ query_putrdataset(client, &devent->rdataset);
+ if (devent->sigrdataset != NULL)
+ query_putrdataset(client, &devent->sigrdataset);
+ isc_event_free(&event);
+ if (fetch_cancelled)
+ query_error(client, DNS_R_SERVFAIL);
+ else
+ query_next(client, ISC_R_CANCELED);
+ /*
+ * This may destroy the client.
+ */
+ ns_client_detach(&client);
+ } else {
+ query_find(client, devent, 0);
+ }
+}
+
+static isc_result_t
+query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
+ dns_rdataset_t *nameservers)
+{
+ isc_result_t result;
+ dns_rdataset_t *rdataset, *sigrdataset;
+ isc_sockaddr_t *peeraddr;
+
+ inc_stats(client, dns_statscounter_recursion);
+
+ /*
+ * We are about to recurse, which means that this client will
+ * be unavailable for serving new requests for an indeterminate
+ * amount of time. If this client is currently responsible
+ * for handling incoming queries, set up a new client
+ * object to handle them while we are waiting for a
+ * response. There is no need to replace TCP clients
+ * because those have already been replaced when the
+ * connection was accepted (if allowed by the TCP quota).
+ */
+ if (client->recursionquota == NULL) {
+ result = isc_quota_attach(&ns_g_server->recursionquota,
+ &client->recursionquota);
+ if (result == ISC_R_SOFTQUOTA) {
+ static isc_stdtime_t last = 0;
+ isc_stdtime_t now;
+ isc_stdtime_get(&now);
+ if (now != last) {
+ last = now;
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_WARNING,
+ "recursive-clients soft limit "
+ "exceeded, aborting oldest query");
+ }
+ ns_client_killoldestquery(client);
+ result = ISC_R_SUCCESS;
+ } else if (result == ISC_R_QUOTA) {
+ static isc_stdtime_t last = 0;
+ isc_stdtime_t now;
+ isc_stdtime_get(&now);
+ if (now != last) {
+ last = now;
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_WARNING,
+ "no more recursive clients: %s",
+ isc_result_totext(result));
+ }
+ ns_client_killoldestquery(client);
+ }
+ if (result == ISC_R_SUCCESS && !client->mortal &&
+ (client->attributes & NS_CLIENTATTR_TCP) == 0) {
+ result = ns_client_replace(client);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_WARNING,
+ "ns_client_replace() failed: %s",
+ isc_result_totext(result));
+ isc_quota_detach(&client->recursionquota);
+ }
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ ns_client_recursing(client);
+ }
+
+ /*
+ * Invoke the resolver.
+ */
+ REQUIRE(nameservers == NULL || nameservers->type == dns_rdatatype_ns);
+ REQUIRE(client->query.fetch == NULL);
+
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ return (ISC_R_NOMEMORY);
+ if (WANTDNSSEC(client)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ query_putrdataset(client, &rdataset);
+ return (ISC_R_NOMEMORY);
+ }
+ } else
+ sigrdataset = NULL;
+
+ if (client->query.timerset == ISC_FALSE)
+ ns_client_settimeout(client, 60);
+ if ((client->attributes & NS_CLIENTATTR_TCP) == 0)
+ peeraddr = &client->peeraddr;
+ else
+ peeraddr = NULL;
+ result = dns_resolver_createfetch2(client->view->resolver,
+ client->query.qname,
+ qtype, qdomain, nameservers,
+ NULL, peeraddr, client->message->id,
+ client->query.fetchoptions,
+ client->task,
+ query_resume, client,
+ rdataset, sigrdataset,
+ &client->query.fetch);
+
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * Record that we're waiting for an event. A client which
+ * is shutting down will not be destroyed until all the
+ * events have been received.
+ */
+ } else {
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ }
+
+ return (result);
+}
+
+#define MAX_RESTARTS 16
+
+#define QUERY_ERROR(r) \
+do { \
+ eresult = r; \
+ want_restart = ISC_FALSE; \
+} while (0)
+
+/*
+ * Extract a network address from the RDATA of an A or AAAA
+ * record.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_NOTIMPLEMENTED The rdata is not a known address type.
+ */
+static isc_result_t
+rdata_tonetaddr(const dns_rdata_t *rdata, isc_netaddr_t *netaddr) {
+ struct in_addr ina;
+ struct in6_addr in6a;
+
+ switch (rdata->type) {
+ case dns_rdatatype_a:
+ INSIST(rdata->length == 4);
+ memcpy(&ina.s_addr, rdata->data, 4);
+ isc_netaddr_fromin(netaddr, &ina);
+ return (ISC_R_SUCCESS);
+ case dns_rdatatype_aaaa:
+ INSIST(rdata->length == 16);
+ memcpy(in6a.s6_addr, rdata->data, 16);
+ isc_netaddr_fromin6(netaddr, &in6a);
+ return (ISC_R_SUCCESS);
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+}
+
+/*
+ * Find the sort order of 'rdata' in the topology-like
+ * ACL forming the second element in a 2-element top-level
+ * sortlist statement.
+ */
+static int
+query_sortlist_order_2element(const dns_rdata_t *rdata, const void *arg) {
+ isc_netaddr_t netaddr;
+
+ if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS)
+ return (INT_MAX);
+ return (ns_sortlist_addrorder2(&netaddr, arg));
+}
+
+/*
+ * Find the sort order of 'rdata' in the matching element
+ * of a 1-element top-level sortlist statement.
+ */
+static int
+query_sortlist_order_1element(const dns_rdata_t *rdata, const void *arg) {
+ isc_netaddr_t netaddr;
+
+ if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS)
+ return (INT_MAX);
+ return (ns_sortlist_addrorder1(&netaddr, arg));
+}
+
+/*
+ * Find the sortlist statement that applies to 'client' and set up
+ * the sortlist info in in client->message appropriately.
+ */
+static void
+setup_query_sortlist(ns_client_t *client) {
+ isc_netaddr_t netaddr;
+ dns_rdatasetorderfunc_t order = NULL;
+ const void *order_arg = NULL;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ switch (ns_sortlist_setup(client->view->sortlist,
+ &netaddr, &order_arg)) {
+ case NS_SORTLISTTYPE_1ELEMENT:
+ order = query_sortlist_order_1element;
+ break;
+ case NS_SORTLISTTYPE_2ELEMENT:
+ order = query_sortlist_order_2element;
+ break;
+ case NS_SORTLISTTYPE_NONE:
+ order = NULL;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ dns_message_setsortorder(client->message, order, order_arg);
+}
+
+static void
+query_addnoqnameproof(ns_client_t *client, dns_rdataset_t *rdataset) {
+ isc_buffer_t *dbuf, b;
+ dns_name_t *fname;
+ dns_rdataset_t *nsec, *nsecsig;
+ isc_result_t result = ISC_R_NOMEMORY;
+
+ CTRACE("query_addnoqnameproof");
+
+ fname = NULL;
+ nsec = NULL;
+ nsecsig = NULL;
+
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ nsec = query_newrdataset(client);
+ nsecsig = query_newrdataset(client);
+ if (fname == NULL || nsec == NULL || nsecsig == NULL)
+ goto cleanup;
+
+ result = dns_rdataset_getnoqname(rdataset, fname, nsec, nsecsig);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ query_addrrset(client, &fname, &nsec, &nsecsig, dbuf,
+ DNS_SECTION_AUTHORITY);
+
+ cleanup:
+ if (nsec != NULL)
+ query_putrdataset(client, &nsec);
+ if (nsecsig != NULL)
+ query_putrdataset(client, &nsecsig);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+}
+
+static inline void
+answer_in_glue(ns_client_t *client, dns_rdatatype_t qtype) {
+ dns_name_t *name;
+ dns_message_t *msg;
+ dns_section_t section = DNS_SECTION_ADDITIONAL;
+ dns_rdataset_t *rdataset = NULL;
+
+ msg = client->message;
+ for (name = ISC_LIST_HEAD(msg->sections[section]);
+ name != NULL;
+ name = ISC_LIST_NEXT(name, link))
+ if (dns_name_equal(name, client->query.qname)) {
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ if (rdataset->type == qtype)
+ break;
+ break;
+ }
+ if (rdataset != NULL) {
+ ISC_LIST_UNLINK(msg->sections[section], name, link);
+ ISC_LIST_PREPEND(msg->sections[section], name, link);
+ ISC_LIST_UNLINK(name->list, rdataset, link);
+ ISC_LIST_PREPEND(name->list, rdataset, link);
+ rdataset->attributes |= DNS_RDATASETATTR_REQUIREDGLUE;
+ }
+}
+
+#define NS_NAME_INIT(A,B) \
+ { \
+ DNS_NAME_MAGIC, \
+ A, sizeof(A), sizeof(B), \
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, \
+ B, NULL, { (void *)-1, (void *)-1}, \
+ {NULL, NULL} \
+ }
+
+static unsigned char inaddr10_offsets[] = { 0, 3, 11, 16 };
+static unsigned char inaddr172_offsets[] = { 0, 3, 7, 15, 20 };
+static unsigned char inaddr192_offsets[] = { 0, 4, 8, 16, 21 };
+
+static unsigned char inaddr10[] = "\00210\007IN-ADDR\004ARPA";
+
+static unsigned char inaddr16172[] = "\00216\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr17172[] = "\00217\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr18172[] = "\00218\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr19172[] = "\00219\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr20172[] = "\00220\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr21172[] = "\00221\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr22172[] = "\00222\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr23172[] = "\00223\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr24172[] = "\00224\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr25172[] = "\00225\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr26172[] = "\00226\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr27172[] = "\00227\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr28172[] = "\00228\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr29172[] = "\00229\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr30172[] = "\00230\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr31172[] = "\00231\003172\007IN-ADDR\004ARPA";
+
+static unsigned char inaddr168192[] = "\003168\003192\007IN-ADDR\004ARPA";
+
+static dns_name_t rfc1918names[] = {
+ NS_NAME_INIT(inaddr10, inaddr10_offsets),
+ NS_NAME_INIT(inaddr16172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr17172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr18172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr19172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr20172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr21172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr22172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr23172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr24172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr25172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr26172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr27172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr28172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr29172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr30172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr31172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr168192, inaddr192_offsets)
+};
+
+
+static unsigned char prisoner_data[] = "\010prisoner\004iana\003org";
+static unsigned char hostmaster_data[] = "\012hostmaster\014root-servers\003org";
+
+static unsigned char prisoner_offsets[] = { 0, 9, 14, 18 };
+static unsigned char hostmaster_offsets[] = { 0, 11, 24, 28 };
+
+static dns_name_t prisoner = NS_NAME_INIT(prisoner_data, prisoner_offsets);
+static dns_name_t hostmaster = NS_NAME_INIT(hostmaster_data, hostmaster_offsets);
+
+static void
+warn_rfc1918(ns_client_t *client, dns_name_t *fname, dns_rdataset_t *rdataset) {
+ unsigned int i;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_soa_t soa;
+ dns_rdataset_t found;
+ isc_result_t result;
+
+ for (i = 0; i < (sizeof(rfc1918names)/sizeof(*rfc1918names)); i++) {
+ if (dns_name_issubdomain(fname, &rfc1918names[i])) {
+ dns_rdataset_init(&found);
+ result = dns_ncache_getrdataset(rdataset,
+ &rfc1918names[i],
+ dns_rdatatype_soa,
+ &found);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ result = dns_rdataset_first(&found);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(&found, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ if (result != ISC_R_SUCCESS)
+ return;
+ if (dns_name_equal(&soa.origin, &prisoner) &&
+ dns_name_equal(&soa.contact, &hostmaster)) {
+ char buf[DNS_NAME_FORMATSIZE];
+ dns_name_format(fname, buf, sizeof(buf));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_WARNING,
+ "RFC 1918 response from "
+ "Internet for %s", buf);
+ }
+ dns_rdataset_disassociate(&found);
+ return;
+ }
+ }
+}
+
+/*
+ * Do the bulk of query processing for the current query of 'client'.
+ * If 'event' is non-NULL, we are returning from recursion and 'qtype'
+ * is ignored. Otherwise, 'qtype' is the query type.
+ */
+static void
+query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
+{
+ dns_db_t *db, *zdb;
+ dns_dbnode_t *node;
+ dns_rdatatype_t type;
+ dns_name_t *fname, *zfname, *tname, *prefix;
+ dns_rdataset_t *rdataset, *trdataset;
+ dns_rdataset_t *sigrdataset, *zrdataset, *zsigrdataset;
+ dns_rdataset_t **sigrdatasetp;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatasetiter_t *rdsiter;
+ isc_boolean_t want_restart, authoritative, is_zone, need_wildcardproof;
+ unsigned int n, nlabels;
+ dns_namereln_t namereln;
+ int order;
+ isc_buffer_t *dbuf;
+ isc_buffer_t b;
+ isc_result_t result, eresult;
+ dns_fixedname_t fixed;
+ dns_fixedname_t wildcardname;
+ dns_dbversion_t *version;
+ dns_zone_t *zone;
+ dns_rdata_cname_t cname;
+ dns_rdata_dname_t dname;
+ unsigned int options;
+ isc_boolean_t empty_wild;
+ dns_rdataset_t *noqname;
+
+ CTRACE("query_find");
+
+ /*
+ * One-time initialization.
+ *
+ * It's especially important to initialize anything that the cleanup
+ * code might cleanup.
+ */
+
+ eresult = ISC_R_SUCCESS;
+ fname = NULL;
+ zfname = NULL;
+ rdataset = NULL;
+ zrdataset = NULL;
+ sigrdataset = NULL;
+ zsigrdataset = NULL;
+ node = NULL;
+ db = NULL;
+ zdb = NULL;
+ version = NULL;
+ zone = NULL;
+ need_wildcardproof = ISC_FALSE;
+ empty_wild = ISC_FALSE;
+ options = 0;
+
+ if (event != NULL) {
+ /*
+ * We're returning from recursion. Restore the query context
+ * and resume.
+ */
+
+ want_restart = ISC_FALSE;
+ authoritative = ISC_FALSE;
+ is_zone = ISC_FALSE;
+
+ qtype = event->qtype;
+ if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig)
+ type = dns_rdatatype_any;
+ else
+ type = qtype;
+ db = event->db;
+ node = event->node;
+ rdataset = event->rdataset;
+ sigrdataset = event->sigrdataset;
+
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ tname = dns_fixedname_name(&event->foundname);
+ result = dns_name_copy(tname, fname, NULL);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+
+ result = event->result;
+
+ goto resume;
+ }
+
+ /*
+ * Not returning from recursion.
+ */
+
+ /*
+ * If it's a SIG query, we'll iterate the node.
+ */
+ if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig)
+ type = dns_rdatatype_any;
+ else
+ type = qtype;
+
+ restart:
+ CTRACE("query_find: restart");
+ want_restart = ISC_FALSE;
+ authoritative = ISC_FALSE;
+ version = NULL;
+ need_wildcardproof = ISC_FALSE;
+
+ if (client->view->checknames &&
+ !dns_rdata_checkowner(client->query.qname,
+ client->message->rdclass,
+ qtype, ISC_FALSE)) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typename[DNS_RDATATYPE_FORMATSIZE];
+ char classname[DNS_RDATACLASS_FORMATSIZE];
+
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(qtype, typename, sizeof(typename));
+ dns_rdataclass_format(client->message->rdclass, classname,
+ sizeof(classname));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY, ISC_LOG_ERROR,
+ "check-names failure %s/%s/%s", namebuf,
+ typename, classname);
+ QUERY_ERROR(DNS_R_REFUSED);
+ goto cleanup;
+ }
+
+ /*
+ * First we must find the right database.
+ */
+ options &= DNS_GETDB_NOLOG; /* Preserve DNS_GETDB_NOLOG. */
+ if (dns_rdatatype_atparent(qtype) &&
+ !dns_name_equal(client->query.qname, dns_rootname))
+ options |= DNS_GETDB_NOEXACT;
+ result = query_getdb(client, client->query.qname, qtype, options,
+ &zone, &db, &version, &is_zone);
+ if ((result != ISC_R_SUCCESS || !is_zone) && !RECURSIONOK(client) &&
+ (options & DNS_GETDB_NOEXACT) != 0 && qtype == dns_rdatatype_ds) {
+ /*
+ * Look to see if we are authoritative for the
+ * child zone if the query type is DS.
+ */
+ dns_db_t *tdb = NULL;
+ dns_zone_t *tzone = NULL;
+ dns_dbversion_t *tversion = NULL;
+ isc_result_t tresult;
+
+ tresult = query_getzonedb(client, client->query.qname, qtype,
+ DNS_GETDB_PARTIAL, &tzone, &tdb,
+ &tversion);
+ if (tresult == ISC_R_SUCCESS) {
+ options &= ~DNS_GETDB_NOEXACT;
+ query_putrdataset(client, &rdataset);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ version = tversion;
+ db = tdb;
+ zone = tzone;
+ is_zone = ISC_TRUE;
+ result = ISC_R_SUCCESS;
+ } else {
+ if (tdb != NULL)
+ dns_db_detach(&tdb);
+ if (tzone != NULL)
+ dns_zone_detach(&tzone);
+ }
+ }
+ if (result != ISC_R_SUCCESS) {
+ if (result == DNS_R_REFUSED) {
+ if (!PARTIALANSWER(client))
+ QUERY_ERROR(DNS_R_REFUSED);
+ } else
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+
+ if (is_zone)
+ authoritative = ISC_TRUE;
+
+ if (event == NULL && client->query.restarts == 0) {
+ if (is_zone) {
+#ifdef DLZ
+ if (zone != NULL) {
+ /*
+ * if is_zone = true, zone = NULL then this is
+ * a DLZ zone. Don't attempt to attach zone.
+ */
+#endif
+ dns_zone_attach(zone, &client->query.authzone);
+#ifdef DLZ
+ }
+#endif
+ dns_db_attach(db, &client->query.authdb);
+ }
+ client->query.authdbset = ISC_TRUE;
+ }
+
+ db_find:
+ CTRACE("query_find: db_find");
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ rdataset = query_newrdataset(client);
+ if (fname == NULL || rdataset == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ if (WANTDNSSEC(client)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Now look for an answer in the database.
+ */
+ result = dns_db_find(db, client->query.qname, version, type,
+ client->query.dboptions, client->now,
+ &node, fname, rdataset, sigrdataset);
+
+ resume:
+ CTRACE("query_find: resume");
+ switch (result) {
+ case ISC_R_SUCCESS:
+ /*
+ * This case is handled in the main line below.
+ */
+ break;
+ case DNS_R_GLUE:
+ case DNS_R_ZONECUT:
+ /*
+ * These cases are handled in the main line below.
+ */
+ INSIST(is_zone);
+ authoritative = ISC_FALSE;
+ break;
+ case ISC_R_NOTFOUND:
+ /*
+ * The cache doesn't even have the root NS. Get them from
+ * the hints DB.
+ */
+ INSIST(!is_zone);
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ if (client->view->hints == NULL) {
+ /* We have no hints. */
+ result = ISC_R_FAILURE;
+ } else {
+ dns_db_attach(client->view->hints, &db);
+ result = dns_db_find(db, dns_rootname,
+ NULL, dns_rdatatype_ns,
+ 0, client->now, &node, fname,
+ rdataset, sigrdataset);
+ }
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Nonsensical root hints may require cleanup.
+ */
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ /*
+ * We don't have any root server hints, but
+ * we may have working forwarders, so try to
+ * recurse anyway.
+ */
+ if (RECURSIONOK(client)) {
+ result = query_recurse(client, qtype,
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ else if (result == DNS_R_DUPLICATE ||
+ result == DNS_R_DROP) {
+ /* Duplicate query. */
+ QUERY_ERROR(result);
+ } else {
+ /* Unable to recurse. */
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ }
+ goto cleanup;
+ } else {
+ /* Unable to give root server referral. */
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+ /*
+ * XXXRTH We should trigger root server priming here.
+ */
+ /* FALLTHROUGH */
+ case DNS_R_DELEGATION:
+ authoritative = ISC_FALSE;
+ if (is_zone) {
+ /*
+ * Look to see if we are authoritative for the
+ * child zone if the query type is DS.
+ */
+ if (!RECURSIONOK(client) &&
+ (options & DNS_GETDB_NOEXACT) != 0 &&
+ qtype == dns_rdatatype_ds) {
+ dns_db_t *tdb = NULL;
+ dns_zone_t *tzone = NULL;
+ dns_dbversion_t *tversion = NULL;
+ result = query_getzonedb(client,
+ client->query.qname,
+ qtype,
+ DNS_GETDB_PARTIAL,
+ &tzone, &tdb,
+ &tversion);
+ if (result == ISC_R_SUCCESS) {
+ options &= ~DNS_GETDB_NOEXACT;
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client,
+ &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client,
+ &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ version = tversion;
+ db = tdb;
+ zone = tzone;
+ authoritative = ISC_TRUE;
+ goto db_find;
+ }
+ if (tdb != NULL)
+ dns_db_detach(&tdb);
+ if (tzone != NULL)
+ dns_zone_detach(&tzone);
+ }
+ /*
+ * We're authoritative for an ancestor of QNAME.
+ */
+ if (!USECACHE(client) || !RECURSIONOK(client)) {
+ /*
+ * If we don't have a cache, this is the best
+ * answer.
+ *
+ * If the client is making a nonrecursive
+ * query we always give out the authoritative
+ * delegation. This way even if we get
+ * junk in our cache, we won't fail in our
+ * role as the delegating authority if another
+ * nameserver asks us about a delegated
+ * subzone.
+ *
+ * We enable the retrieval of glue for this
+ * database by setting client->query.gluedb.
+ */
+ client->query.gluedb = db;
+ client->query.isreferral = ISC_TRUE;
+ /*
+ * We must ensure NOADDITIONAL is off,
+ * because the generation of
+ * additional data is required in
+ * delegations.
+ */
+ client->query.attributes &=
+ ~NS_QUERYATTR_NOADDITIONAL;
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ query_addrrset(client, &fname,
+ &rdataset, sigrdatasetp,
+ dbuf, DNS_SECTION_AUTHORITY);
+ client->query.gluedb = NULL;
+ if (WANTDNSSEC(client) && dns_db_issecure(db))
+ query_addds(client, db, node, version);
+ } else {
+ /*
+ * We might have a better answer or delegation
+ * in the cache. We'll remember the current
+ * values of fname, rdataset, and sigrdataset.
+ * We'll then go looking for QNAME in the
+ * cache. If we find something better, we'll
+ * use it instead.
+ */
+ query_keepname(client, fname, dbuf);
+ zdb = db;
+ zfname = fname;
+ fname = NULL;
+ zrdataset = rdataset;
+ rdataset = NULL;
+ zsigrdataset = sigrdataset;
+ sigrdataset = NULL;
+ dns_db_detachnode(db, &node);
+ version = NULL;
+ db = NULL;
+ dns_db_attach(client->view->cachedb, &db);
+ is_zone = ISC_FALSE;
+ goto db_find;
+ }
+ } else {
+ if (zfname != NULL &&
+ !dns_name_issubdomain(fname, zfname)) {
+ /*
+ * We've already got a delegation from
+ * authoritative data, and it is better
+ * than what we found in the cache. Use
+ * it instead of the cache delegation.
+ */
+ query_releasename(client, &fname);
+ fname = zfname;
+ zfname = NULL;
+ /*
+ * We've already done query_keepname() on
+ * zfname, so we must set dbuf to NULL to
+ * prevent query_addrrset() from trying to
+ * call query_keepname() again.
+ */
+ dbuf = NULL;
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client,
+ &sigrdataset);
+ rdataset = zrdataset;
+ zrdataset = NULL;
+ sigrdataset = zsigrdataset;
+ zsigrdataset = NULL;
+ /*
+ * We don't clean up zdb here because we
+ * may still need it. It will get cleaned
+ * up by the main cleanup code.
+ */
+ }
+
+ if (RECURSIONOK(client)) {
+ /*
+ * Recurse!
+ */
+ if (dns_rdatatype_atparent(type))
+ result = query_recurse(client, qtype,
+ NULL, NULL);
+ else
+ result = query_recurse(client, qtype,
+ fname, rdataset);
+ if (result == ISC_R_SUCCESS)
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ else if (result == DNS_R_DUPLICATE ||
+ result == DNS_R_DROP)
+ QUERY_ERROR(result);
+ else
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ } else {
+ /*
+ * This is the best answer.
+ */
+ client->query.attributes |=
+ NS_QUERYATTR_CACHEGLUEOK;
+ client->query.gluedb = zdb;
+ client->query.isreferral = ISC_TRUE;
+ /*
+ * We must ensure NOADDITIONAL is off,
+ * because the generation of
+ * additional data is required in
+ * delegations.
+ */
+ client->query.attributes &=
+ ~NS_QUERYATTR_NOADDITIONAL;
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ query_addrrset(client, &fname,
+ &rdataset, sigrdatasetp,
+ dbuf, DNS_SECTION_AUTHORITY);
+ client->query.gluedb = NULL;
+ client->query.attributes &=
+ ~NS_QUERYATTR_CACHEGLUEOK;
+ if (WANTDNSSEC(client))
+ query_addds(client, db, node, version);
+ }
+ }
+ goto cleanup;
+ case DNS_R_EMPTYNAME:
+ result = DNS_R_NXRRSET;
+ /* FALLTHROUGH */
+ case DNS_R_NXRRSET:
+ INSIST(is_zone);
+ if (dns_rdataset_isassociated(rdataset)) {
+ /*
+ * If we've got a NSEC record, we need to save the
+ * name now because we're going call query_addsoa()
+ * below, and it needs to use the name buffer.
+ */
+ query_keepname(client, fname, dbuf);
+ } else {
+ /*
+ * We're not going to use fname, and need to release
+ * our hold on the name buffer so query_addsoa()
+ * may use it.
+ */
+ query_releasename(client, &fname);
+ }
+ /*
+ * Add SOA.
+ */
+ result = query_addsoa(client, db, version, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(result);
+ goto cleanup;
+ }
+ /*
+ * Add NSEC record if we found one.
+ */
+ if (WANTDNSSEC(client)) {
+ if (dns_rdataset_isassociated(rdataset))
+ query_addnxrrsetnsec(client, db, version,
+ &fname, &rdataset,
+ &sigrdataset);
+ }
+ goto cleanup;
+ case DNS_R_EMPTYWILD:
+ empty_wild = ISC_TRUE;
+ /* FALLTHROUGH */
+ case DNS_R_NXDOMAIN:
+ INSIST(is_zone);
+ if (dns_rdataset_isassociated(rdataset)) {
+ /*
+ * If we've got a NSEC record, we need to save the
+ * name now because we're going call query_addsoa()
+ * below, and it needs to use the name buffer.
+ */
+ query_keepname(client, fname, dbuf);
+ } else {
+ /*
+ * We're not going to use fname, and need to release
+ * our hold on the name buffer so query_addsoa()
+ * may use it.
+ */
+ query_releasename(client, &fname);
+ }
+ /*
+ * Add SOA. If the query was for a SOA record force the
+ * ttl to zero so that it is possible for clients to find
+ * the containing zone of an arbitrary name with a stub
+ * resolver and not have it cached.
+ */
+ if (qtype == dns_rdatatype_soa &&
+#ifdef DLZ
+ zone != NULL &&
+#endif
+ dns_zone_getzeronosoattl(zone))
+ result = query_addsoa(client, db, version, ISC_TRUE);
+ else
+ result = query_addsoa(client, db, version, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(result);
+ goto cleanup;
+ }
+ /*
+ * Add NSEC record if we found one.
+ */
+ if (dns_rdataset_isassociated(rdataset)) {
+ if (WANTDNSSEC(client)) {
+ query_addrrset(client, &fname, &rdataset,
+ &sigrdataset,
+ NULL, DNS_SECTION_AUTHORITY);
+ query_addwildcardproof(client, db, version,
+ client->query.qname,
+ ISC_FALSE);
+ }
+ }
+ /*
+ * Set message rcode.
+ */
+ if (empty_wild)
+ client->message->rcode = dns_rcode_noerror;
+ else
+ client->message->rcode = dns_rcode_nxdomain;
+ goto cleanup;
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ INSIST(!is_zone);
+ authoritative = ISC_FALSE;
+ /*
+ * Set message rcode, if required.
+ */
+ if (result == DNS_R_NCACHENXDOMAIN)
+ client->message->rcode = dns_rcode_nxdomain;
+ /*
+ * Look for RFC 1918 leakage from Internet.
+ */
+ if (result == DNS_R_NCACHENXDOMAIN &&
+ qtype == dns_rdatatype_ptr &&
+ client->message->rdclass == dns_rdataclass_in &&
+ dns_name_countlabels(fname) == 7)
+ warn_rfc1918(client, fname, rdataset);
+ /*
+ * We don't call query_addrrset() because we don't need any
+ * of its extra features (and things would probably break!).
+ */
+ query_keepname(client, fname, dbuf);
+ dns_message_addname(client->message, fname,
+ DNS_SECTION_AUTHORITY);
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ fname = NULL;
+ rdataset = NULL;
+ goto cleanup;
+ case DNS_R_CNAME:
+ /*
+ * Keep a copy of the rdataset. We have to do this because
+ * query_addrrset may clear 'rdataset' (to prevent the
+ * cleanup code from cleaning it up).
+ */
+ trdataset = rdataset;
+ /*
+ * Add the CNAME to the answer section.
+ */
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ if (WANTDNSSEC(client) &&
+ (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0)
+ {
+ dns_fixedname_init(&wildcardname);
+ dns_name_copy(fname, dns_fixedname_name(&wildcardname),
+ NULL);
+ need_wildcardproof = ISC_TRUE;
+ }
+ if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0 &&
+ WANTDNSSEC(client))
+ noqname = rdataset;
+ else
+ noqname = NULL;
+ query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf,
+ DNS_SECTION_ANSWER);
+ if (noqname != NULL)
+ query_addnoqnameproof(client, noqname);
+ /*
+ * We set the PARTIALANSWER attribute so that if anything goes
+ * wrong later on, we'll return what we've got so far.
+ */
+ client->query.attributes |= NS_QUERYATTR_PARTIALANSWER;
+ /*
+ * Reset qname to be the target name of the CNAME and restart
+ * the query.
+ */
+ tname = NULL;
+ result = dns_message_gettempname(client->message, &tname);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_rdataset_first(trdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ dns_rdataset_current(trdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &cname, NULL);
+ dns_rdata_reset(&rdata);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ dns_name_init(tname, NULL);
+ result = dns_name_dup(&cname.cname, client->mctx, tname);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ dns_rdata_freestruct(&cname);
+ goto cleanup;
+ }
+ dns_rdata_freestruct(&cname);
+ ns_client_qnamereplace(client, tname);
+ want_restart = ISC_TRUE;
+ if (!WANTRECURSION(client))
+ options |= DNS_GETDB_NOLOG;
+ goto addauth;
+ case DNS_R_DNAME:
+ /*
+ * Compare the current qname to the found name. We need
+ * to know how many labels and bits are in common because
+ * we're going to have to split qname later on.
+ */
+ namereln = dns_name_fullcompare(client->query.qname, fname,
+ &order, &nlabels);
+ INSIST(namereln == dns_namereln_subdomain);
+ /*
+ * Keep a copy of the rdataset. We have to do this because
+ * query_addrrset may clear 'rdataset' (to prevent the
+ * cleanup code from cleaning it up).
+ */
+ trdataset = rdataset;
+ /*
+ * Add the DNAME to the answer section.
+ */
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ if (WANTDNSSEC(client) &&
+ (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0)
+ {
+ dns_fixedname_init(&wildcardname);
+ dns_name_copy(fname, dns_fixedname_name(&wildcardname),
+ NULL);
+ need_wildcardproof = ISC_TRUE;
+ }
+ query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf,
+ DNS_SECTION_ANSWER);
+ /*
+ * We set the PARTIALANSWER attribute so that if anything goes
+ * wrong later on, we'll return what we've got so far.
+ */
+ client->query.attributes |= NS_QUERYATTR_PARTIALANSWER;
+ /*
+ * Get the target name of the DNAME.
+ */
+ tname = NULL;
+ result = dns_message_gettempname(client->message, &tname);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_rdataset_first(trdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ dns_rdataset_current(trdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &dname, NULL);
+ dns_rdata_reset(&rdata);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ dns_name_init(tname, NULL);
+ dns_name_clone(&dname.dname, tname);
+ dns_rdata_freestruct(&dname);
+ /*
+ * Construct the new qname.
+ */
+ dns_fixedname_init(&fixed);
+ prefix = dns_fixedname_name(&fixed);
+ dns_name_split(client->query.qname, nlabels, prefix, NULL);
+ INSIST(fname == NULL);
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ result = dns_name_concatenate(prefix, tname, fname, NULL);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ if (result == ISC_R_NOSPACE) {
+ /*
+ * RFC2672, section 4.1, subsection 3c says
+ * we should return YXDOMAIN if the constructed
+ * name would be too long.
+ */
+ client->message->rcode = dns_rcode_yxdomain;
+ }
+ goto cleanup;
+ }
+ query_keepname(client, fname, dbuf);
+ /*
+ * Synthesize a CNAME for this DNAME.
+ *
+ * We want to synthesize a CNAME since if we don't
+ * then older software that doesn't understand DNAME
+ * will not chain like it should.
+ *
+ * We do not try to synthesize a signature because we hope
+ * that security aware servers will understand DNAME. Also,
+ * even if we had an online key, making a signature
+ * on-the-fly is costly, and not really legitimate anyway
+ * since the synthesized CNAME is NOT in the zone.
+ */
+ dns_name_init(tname, NULL);
+ (void)query_addcnamelike(client, client->query.qname, fname,
+ trdataset->trust, &tname,
+ dns_rdatatype_cname);
+ if (tname != NULL)
+ dns_message_puttempname(client->message, &tname);
+ /*
+ * Switch to the new qname and restart.
+ */
+ ns_client_qnamereplace(client, fname);
+ fname = NULL;
+ want_restart = ISC_TRUE;
+ if (!WANTRECURSION(client))
+ options |= DNS_GETDB_NOLOG;
+ goto addauth;
+ default:
+ /*
+ * Something has gone wrong.
+ */
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+
+ if (WANTDNSSEC(client) &&
+ (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0)
+ {
+ dns_fixedname_init(&wildcardname);
+ dns_name_copy(fname, dns_fixedname_name(&wildcardname), NULL);
+ need_wildcardproof = ISC_TRUE;
+ }
+
+ if (type == dns_rdatatype_any) {
+ /*
+ * XXXRTH Need to handle zonecuts with special case
+ * code.
+ */
+ n = 0;
+ rdsiter = NULL;
+ result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ /*
+ * Calling query_addrrset() with a non-NULL dbuf is going
+ * to either keep or release the name. We don't want it to
+ * release fname, since we may have to call query_addrrset()
+ * more than once. That means we have to call query_keepname()
+ * now, and pass a NULL dbuf to query_addrrset().
+ *
+ * If we do a query_addrrset() below, we must set fname to
+ * NULL before leaving this block, otherwise we might try to
+ * cleanup fname even though we're using it!
+ */
+ query_keepname(client, fname, dbuf);
+ tname = fname;
+ result = dns_rdatasetiter_first(rdsiter);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(rdsiter, rdataset);
+ if ((qtype == dns_rdatatype_any ||
+ rdataset->type == qtype) && rdataset->type != 0) {
+ query_addrrset(client,
+ fname != NULL ? &fname : &tname,
+ &rdataset, NULL,
+ NULL, DNS_SECTION_ANSWER);
+ n++;
+ INSIST(tname != NULL);
+ /*
+ * rdataset is non-NULL only in certain pathological
+ * cases involving DNAMEs.
+ */
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ break;
+ } else {
+ /*
+ * We're not interested in this rdataset.
+ */
+ dns_rdataset_disassociate(rdataset);
+ }
+ result = dns_rdatasetiter_next(rdsiter);
+ }
+
+ if (fname != NULL)
+ dns_message_puttempname(client->message, &fname);
+
+ if (n == 0) {
+ /*
+ * We didn't match any rdatasets.
+ */
+ if (qtype == dns_rdatatype_rrsig &&
+ result == ISC_R_NOMORE) {
+ /*
+ * XXXRTH If this is a secure zone and we
+ * didn't find any SIGs, we should generate
+ * an error unless we were searching for
+ * glue. Ugh.
+ */
+ if (!is_zone) {
+ authoritative = ISC_FALSE;
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (RECURSIONOK(client)) {
+ result = query_recurse(client,
+ qtype,
+ NULL,
+ NULL);
+ if (result == ISC_R_SUCCESS)
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ else
+ QUERY_ERROR(DNS_R_SERVFAIL); }
+ goto addauth;
+ }
+ /*
+ * We were searching for SIG records in
+ * a nonsecure zone. Send a "no error,
+ * no data" response.
+ */
+ /*
+ * Add SOA.
+ */
+ result = query_addsoa(client, db, version,
+ ISC_FALSE);
+ if (result == ISC_R_SUCCESS)
+ result = ISC_R_NOMORE;
+ } else {
+ /*
+ * Something went wrong.
+ */
+ result = DNS_R_SERVFAIL;
+ }
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_NOMORE) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ } else {
+ /*
+ * This is the "normal" case -- an ordinary question to which
+ * we know the answer.
+ */
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ if ((rdataset->attributes & DNS_RDATASETATTR_NOQNAME) != 0 &&
+ WANTDNSSEC(client))
+ noqname = rdataset;
+ else
+ noqname = NULL;
+ /*
+ * BIND 8 priming queries need the additional section.
+ */
+ if (is_zone && qtype == dns_rdatatype_ns &&
+ dns_name_equal(client->query.qname, dns_rootname))
+ client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL;
+
+ query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf,
+ DNS_SECTION_ANSWER);
+ if (noqname != NULL)
+ query_addnoqnameproof(client, noqname);
+ /*
+ * We shouldn't ever fail to add 'rdataset'
+ * because it's already in the answer.
+ */
+ INSIST(rdataset == NULL);
+ }
+
+ addauth:
+ CTRACE("query_find: addauth");
+ /*
+ * Add NS records to the authority section (if we haven't already
+ * added them to the answer section).
+ */
+ if (!want_restart && !NOAUTHORITY(client)) {
+ if (is_zone) {
+ if (!((qtype == dns_rdatatype_ns ||
+ qtype == dns_rdatatype_any) &&
+ dns_name_equal(client->query.qname,
+ dns_db_origin(db))))
+ (void)query_addns(client, db, version);
+ } else if (qtype != dns_rdatatype_ns) {
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ query_addbestns(client);
+ }
+ }
+
+ /*
+ * Add NSEC records to the authority section if they're needed for
+ * DNSSEC wildcard proofs.
+ */
+ if (need_wildcardproof && dns_db_issecure(db))
+ query_addwildcardproof(client, db, version,
+ dns_fixedname_name(&wildcardname),
+ ISC_TRUE);
+ cleanup:
+ CTRACE("query_find: cleanup");
+ /*
+ * General cleanup.
+ */
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (zdb != NULL) {
+ query_putrdataset(client, &zrdataset);
+ if (zsigrdataset != NULL)
+ query_putrdataset(client, &zsigrdataset);
+ if (zfname != NULL)
+ query_releasename(client, &zfname);
+ dns_db_detach(&zdb);
+ }
+ if (event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&event));
+
+ /*
+ * AA bit.
+ */
+ if (client->query.restarts == 0 && !authoritative) {
+ /*
+ * We're not authoritative, so we must ensure the AA bit
+ * isn't set.
+ */
+ client->message->flags &= ~DNS_MESSAGEFLAG_AA;
+ }
+
+ /*
+ * Restart the query?
+ */
+ if (want_restart && client->query.restarts < MAX_RESTARTS) {
+ client->query.restarts++;
+ goto restart;
+ }
+
+ if (eresult != ISC_R_SUCCESS &&
+ (!PARTIALANSWER(client) || WANTRECURSION(client))) {
+ if (eresult == DNS_R_DUPLICATE || eresult == DNS_R_DROP) {
+ /*
+ * This was a duplicate query that we are
+ * recursing on. Don't send a response now.
+ * The original query will still cause a response.
+ */
+ query_next(client, eresult);
+ } else {
+ /*
+ * If we don't have any answer to give the client,
+ * or if the client requested recursion and thus wanted
+ * the complete answer, send an error response.
+ */
+ query_error(client, eresult);
+ }
+ ns_client_detach(&client);
+ } else if (!RECURSING(client)) {
+ /*
+ * We are done. Set up sortlist data for the message
+ * rendering code, make a final tweak to the AA bit if the
+ * auth-nxdomain config option says so, then render and
+ * send the response.
+ */
+ setup_query_sortlist(client);
+
+ /*
+ * If this is a referral and the answer to the question
+ * is in the glue sort it to the start of the additional
+ * section.
+ */
+ if (client->message->counts[DNS_SECTION_ANSWER] == 0 &&
+ client->message->rcode == dns_rcode_noerror &&
+ (qtype == dns_rdatatype_a || qtype == dns_rdatatype_aaaa))
+ answer_in_glue(client, qtype);
+
+ if (client->message->rcode == dns_rcode_nxdomain &&
+ client->view->auth_nxdomain == ISC_TRUE)
+ client->message->flags |= DNS_MESSAGEFLAG_AA;
+
+ query_send(client);
+ ns_client_detach(&client);
+ }
+ CTRACE("query_find: done");
+}
+
+static inline void
+log_query(ns_client_t *client) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typename[DNS_RDATATYPE_FORMATSIZE];
+ char classname[DNS_RDATACLASS_FORMATSIZE];
+ dns_rdataset_t *rdataset;
+ int level = ISC_LOG_INFO;
+
+ if (! isc_log_wouldlog(ns_g_lctx, level))
+ return;
+
+ rdataset = ISC_LIST_HEAD(client->query.qname->list);
+ INSIST(rdataset != NULL);
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(rdataset->rdclass, classname, sizeof(classname));
+ dns_rdatatype_format(rdataset->type, typename, sizeof(typename));
+
+ ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY,
+ level, "query: %s %s %s %s%s%s", namebuf, classname,
+ typename, WANTRECURSION(client) ? "+" : "-",
+ (client->signer != NULL) ? "S": "",
+ (client->opt != NULL) ? "E" : "");
+}
+
+void
+ns_query_start(ns_client_t *client) {
+ isc_result_t result;
+ dns_message_t *message = client->message;
+ dns_rdataset_t *rdataset;
+ ns_client_t *qclient;
+ dns_rdatatype_t qtype;
+
+ CTRACE("ns_query_start");
+
+ /*
+ * Ensure that appropriate cleanups occur.
+ */
+ client->next = query_next_callback;
+
+ /*
+ * Behave as if we don't support DNSSEC if not enabled.
+ */
+ if (!client->view->enablednssec) {
+ message->flags &= ~DNS_MESSAGEFLAG_CD;
+ client->extflags &= ~DNS_MESSAGEEXTFLAG_DO;
+ if (client->opt != NULL)
+ client->opt->ttl &= ~DNS_MESSAGEEXTFLAG_DO;
+ }
+
+ if ((message->flags & DNS_MESSAGEFLAG_RD) != 0)
+ client->query.attributes |= NS_QUERYATTR_WANTRECURSION;
+
+ if ((client->extflags & DNS_MESSAGEEXTFLAG_DO) != 0)
+ client->attributes |= NS_CLIENTATTR_WANTDNSSEC;
+
+ if (client->view->minimalresponses)
+ client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
+ NS_QUERYATTR_NOADDITIONAL);
+
+ if ((client->view->cachedb == NULL)
+ || (!client->view->additionalfromcache)) {
+ /*
+ * We don't have a cache. Turn off cache support and
+ * recursion.
+ */
+ client->query.attributes &=
+ ~(NS_QUERYATTR_RECURSIONOK|NS_QUERYATTR_CACHEOK);
+ } else if ((client->attributes & NS_CLIENTATTR_RA) == 0 ||
+ (message->flags & DNS_MESSAGEFLAG_RD) == 0) {
+ /*
+ * If the client isn't allowed to recurse (due to
+ * "recursion no", the allow-recursion ACL, or the
+ * lack of a resolver in this view), or if it
+ * doesn't want recursion, turn recursion off.
+ */
+ client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK;
+ }
+
+ /*
+ * Get the question name.
+ */
+ result = dns_message_firstname(message, DNS_SECTION_QUESTION);
+ if (result != ISC_R_SUCCESS) {
+ query_error(client, result);
+ return;
+ }
+ dns_message_currentname(message, DNS_SECTION_QUESTION,
+ &client->query.qname);
+ client->query.origqname = client->query.qname;
+ result = dns_message_nextname(message, DNS_SECTION_QUESTION);
+ if (result != ISC_R_NOMORE) {
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * There's more than one QNAME in the question
+ * section.
+ */
+ query_error(client, DNS_R_FORMERR);
+ } else
+ query_error(client, result);
+ return;
+ }
+
+ if (ns_g_server->log_queries)
+ log_query(client);
+
+ /*
+ * Check for multiple question queries, since edns1 is dead.
+ */
+ if (message->counts[DNS_SECTION_QUESTION] > 1) {
+ query_error(client, DNS_R_FORMERR);
+ return;
+ }
+
+ /*
+ * Check for meta-queries like IXFR and AXFR.
+ */
+ rdataset = ISC_LIST_HEAD(client->query.qname->list);
+ INSIST(rdataset != NULL);
+ qtype = rdataset->type;
+ if (dns_rdatatype_ismeta(qtype)) {
+ switch (qtype) {
+ case dns_rdatatype_any:
+ break; /* Let query_find handle it. */
+ case dns_rdatatype_ixfr:
+ case dns_rdatatype_axfr:
+ ns_xfr_start(client, rdataset->type);
+ return;
+ case dns_rdatatype_maila:
+ case dns_rdatatype_mailb:
+ query_error(client, DNS_R_NOTIMP);
+ return;
+ case dns_rdatatype_tkey:
+ result = dns_tkey_processquery(client->message,
+ ns_g_server->tkeyctx,
+ client->view->dynamickeys);
+ if (result == ISC_R_SUCCESS)
+ query_send(client);
+ else
+ query_error(client, result);
+ return;
+ default: /* TSIG, etc. */
+ query_error(client, DNS_R_FORMERR);
+ return;
+ }
+ }
+
+ /*
+ * If the client has requested that DNSSEC checking be disabled,
+ * allow lookups to return pending data and instruct the resolver
+ * to return data before validation has completed.
+ *
+ * We don't need to set DNS_DBFIND_PENDINGOK when validation is
+ * disabled as there will be no pending data.
+ */
+ if (message->flags & DNS_MESSAGEFLAG_CD ||
+ qtype == dns_rdatatype_rrsig)
+ {
+ client->query.dboptions |= DNS_DBFIND_PENDINGOK;
+ client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
+ } else if (!client->view->enablevalidation)
+ client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
+
+ /*
+ * Allow glue NS records to be added to the authority section
+ * if the answer is secure.
+ */
+ if (message->flags & DNS_MESSAGEFLAG_CD)
+ client->query.attributes &= ~NS_QUERYATTR_SECURE;
+
+ /*
+ * This is an ordinary query.
+ */
+ result = dns_message_reply(message, ISC_TRUE);
+ if (result != ISC_R_SUCCESS) {
+ query_next(client, result);
+ return;
+ }
+
+ /*
+ * Assume authoritative response until it is known to be
+ * otherwise.
+ */
+ message->flags |= DNS_MESSAGEFLAG_AA;
+
+ /*
+ * Set AD. We must clear it if we add non-validated data to a
+ * response.
+ */
+ if (WANTDNSSEC(client))
+ message->flags |= DNS_MESSAGEFLAG_AD;
+
+ qclient = NULL;
+ ns_client_attach(client, &qclient);
+ query_find(qclient, NULL, qtype);
+}
diff --git a/bin/named/server.c b/bin/named/server.c
new file mode 100644
index 0000000..cd8bff1
--- /dev/null
+++ b/bin/named/server.c
@@ -0,0 +1,4835 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: server.c,v 1.419.18.57 2007/08/28 07:20:01 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/app.h>
+#include <isc/base64.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/lex.h>
+#include <isc/parseint.h>
+#include <isc/print.h>
+#include <isc/resource.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <isccfg/namedconf.h>
+
+#include <bind9/check.h>
+
+#include <dns/acache.h>
+#include <dns/adb.h>
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#ifdef DLZ
+#include <dns/dlz.h>
+#endif
+#include <dns/forward.h>
+#include <dns/journal.h>
+#include <dns/keytable.h>
+#include <dns/lib.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/order.h>
+#include <dns/peer.h>
+#include <dns/portlist.h>
+#include <dns/rdataclass.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/resolver.h>
+#include <dns/rootns.h>
+#include <dns/secalg.h>
+#include <dns/stats.h>
+#include <dns/tkey.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <dst/dst.h>
+#include <dst/result.h>
+
+#include <named/client.h>
+#include <named/config.h>
+#include <named/control.h>
+#include <named/interfacemgr.h>
+#include <named/log.h>
+#include <named/logconf.h>
+#include <named/lwresd.h>
+#include <named/main.h>
+#include <named/os.h>
+#include <named/server.h>
+#include <named/tkeyconf.h>
+#include <named/tsigconf.h>
+#include <named/zoneconf.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#include <stdlib.h>
+#endif
+
+/*%
+ * Check an operation for failure. Assumes that the function
+ * using it has a 'result' variable and a 'cleanup' label.
+ */
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+#define CHECKM(op, msg) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) { \
+ isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_GENERAL, \
+ NS_LOGMODULE_SERVER, \
+ ISC_LOG_ERROR, \
+ "%s: %s", msg, \
+ isc_result_totext(result)); \
+ goto cleanup; \
+ } \
+ } while (0) \
+
+#define CHECKMF(op, msg, file) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) { \
+ isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_GENERAL, \
+ NS_LOGMODULE_SERVER, \
+ ISC_LOG_ERROR, \
+ "%s '%s': %s", msg, file, \
+ isc_result_totext(result)); \
+ goto cleanup; \
+ } \
+ } while (0) \
+
+#define CHECKFATAL(op, msg) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) \
+ fatal(msg, result); \
+ } while (0) \
+
+struct ns_dispatch {
+ isc_sockaddr_t addr;
+ unsigned int dispatchgen;
+ dns_dispatch_t *dispatch;
+ ISC_LINK(struct ns_dispatch) link;
+};
+
+struct dumpcontext {
+ isc_mem_t *mctx;
+ isc_boolean_t dumpcache;
+ isc_boolean_t dumpzones;
+ FILE *fp;
+ ISC_LIST(struct viewlistentry) viewlist;
+ struct viewlistentry *view;
+ struct zonelistentry *zone;
+ dns_dumpctx_t *mdctx;
+ dns_db_t *db;
+ dns_db_t *cache;
+ isc_task_t *task;
+ dns_dbversion_t *version;
+};
+
+struct viewlistentry {
+ dns_view_t *view;
+ ISC_LINK(struct viewlistentry) link;
+ ISC_LIST(struct zonelistentry) zonelist;
+};
+
+struct zonelistentry {
+ dns_zone_t *zone;
+ ISC_LINK(struct zonelistentry) link;
+};
+
+/*
+ * These zones should not leak onto the Internet.
+ */
+static const struct {
+ const char *zone;
+ isc_boolean_t rfc1918;
+} empty_zones[] = {
+#ifdef notyet
+ /* RFC 1918 */
+ { "10.IN-ADDR.ARPA", ISC_TRUE },
+ { "16.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "17.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "18.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "19.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "20.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "21.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "22.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "23.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "24.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "25.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "26.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "27.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "28.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "29.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "30.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "31.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "168.192.IN-ADDR.ARPA", ISC_TRUE },
+#endif
+
+ /* RFC 3330 */
+ { "127.IN-ADDR.ARPA", ISC_FALSE }, /* LOOPBACK */
+ { "254.169.IN-ADDR.ARPA", ISC_FALSE }, /* LINK LOCAL */
+ { "2.0.192.IN-ADDR.ARPA", ISC_FALSE }, /* TEST NET */
+ { "255.255.255.255.IN-ADDR.ARPA", ISC_FALSE }, /* BROADCAST */
+
+ /* Local IPv6 Unicast Addresses */
+ { "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE },
+ { "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE },
+ /* LOCALLY ASSIGNED LOCAL ADDRES S SCOPE */
+ { "D.F.IP6.ARPA", ISC_FALSE },
+ { "8.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */
+ { "9.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */
+ { "A.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */
+ { "B.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */
+
+ { NULL, ISC_FALSE }
+};
+
+static void
+fatal(const char *msg, isc_result_t result);
+
+static void
+ns_server_reload(isc_task_t *task, isc_event_t *event);
+
+static isc_result_t
+ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, ns_listenelt_t **target);
+static isc_result_t
+ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, ns_listenlist_t **target);
+
+static isc_result_t
+configure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
+ const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype);
+
+static isc_result_t
+configure_alternates(const cfg_obj_t *config, dns_view_t *view,
+ const cfg_obj_t *alternates);
+
+static isc_result_t
+configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
+ const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *aclconf);
+
+static void
+end_reserved_dispatches(ns_server_t *server, isc_boolean_t all);
+
+/*%
+ * Configure a single view ACL at '*aclp'. Get its configuration by
+ * calling 'getvcacl' (for per-view configuration) and maybe 'getscacl'
+ * (for a global default).
+ */
+static isc_result_t
+configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ const char *aclname, cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, dns_acl_t **aclp)
+{
+ isc_result_t result;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *aclobj = NULL;
+ int i = 0;
+
+ if (*aclp != NULL)
+ dns_acl_detach(aclp);
+ if (vconfig != NULL)
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ if (config != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL)
+ maps[i++] = options;
+ }
+ maps[i] = NULL;
+
+ (void)ns_config_get(maps, aclname, &aclobj);
+ if (aclobj == NULL)
+ /*
+ * No value available. *aclp == NULL.
+ */
+ return (ISC_R_SUCCESS);
+
+ result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
+ actx, mctx, aclp);
+
+ return (result);
+}
+
+static isc_result_t
+configure_view_dnsseckey(const cfg_obj_t *vconfig, const cfg_obj_t *key,
+ dns_keytable_t *keytable, isc_mem_t *mctx)
+{
+ dns_rdataclass_t viewclass;
+ dns_rdata_dnskey_t keystruct;
+ isc_uint32_t flags, proto, alg;
+ const char *keystr, *keynamestr;
+ unsigned char keydata[4096];
+ isc_buffer_t keydatabuf;
+ unsigned char rrdata[4096];
+ isc_buffer_t rrdatabuf;
+ isc_region_t r;
+ dns_fixedname_t fkeyname;
+ dns_name_t *keyname;
+ isc_buffer_t namebuf;
+ isc_result_t result;
+ dst_key_t *dstkey = NULL;
+
+ flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags"));
+ proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol"));
+ alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm"));
+ keyname = dns_fixedname_name(&fkeyname);
+ keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));
+
+ if (vconfig == NULL)
+ viewclass = dns_rdataclass_in;
+ else {
+ const cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class");
+ CHECK(ns_config_getclass(classobj, dns_rdataclass_in,
+ &viewclass));
+ }
+ keystruct.common.rdclass = viewclass;
+ keystruct.common.rdtype = dns_rdatatype_dnskey;
+ /*
+ * The key data in keystruct is not dynamically allocated.
+ */
+ keystruct.mctx = NULL;
+
+ ISC_LINK_INIT(&keystruct.common, link);
+
+ if (flags > 0xffff)
+ CHECKM(ISC_R_RANGE, "key flags");
+ if (proto > 0xff)
+ CHECKM(ISC_R_RANGE, "key protocol");
+ if (alg > 0xff)
+ CHECKM(ISC_R_RANGE, "key algorithm");
+ keystruct.flags = (isc_uint16_t)flags;
+ keystruct.protocol = (isc_uint8_t)proto;
+ keystruct.algorithm = (isc_uint8_t)alg;
+
+ isc_buffer_init(&keydatabuf, keydata, sizeof(keydata));
+ isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
+
+ keystr = cfg_obj_asstring(cfg_tuple_get(key, "key"));
+ CHECK(isc_base64_decodestring(keystr, &keydatabuf));
+ isc_buffer_usedregion(&keydatabuf, &r);
+ keystruct.datalen = r.length;
+ keystruct.data = r.base;
+
+ if ((keystruct.algorithm == DST_ALG_RSASHA1 ||
+ keystruct.algorithm == DST_ALG_RSAMD5) &&
+ r.length > 1 && r.base[0] == 1 && r.base[1] == 3)
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
+ "trusted key '%s' has a weak exponent",
+ keynamestr);
+
+ CHECK(dns_rdata_fromstruct(NULL,
+ keystruct.common.rdclass,
+ keystruct.common.rdtype,
+ &keystruct, &rrdatabuf));
+ dns_fixedname_init(&fkeyname);
+ isc_buffer_init(&namebuf, keynamestr, strlen(keynamestr));
+ isc_buffer_add(&namebuf, strlen(keynamestr));
+ CHECK(dns_name_fromtext(keyname, &namebuf,
+ dns_rootname, ISC_FALSE,
+ NULL));
+ CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf,
+ mctx, &dstkey));
+
+ CHECK(dns_keytable_add(keytable, &dstkey));
+ INSIST(dstkey == NULL);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (result == DST_R_NOCRYPTO) {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
+ "ignoring trusted key for '%s': no crypto support",
+ keynamestr);
+ result = ISC_R_SUCCESS;
+ } else {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
+ "configuring trusted key for '%s': %s",
+ keynamestr, isc_result_totext(result));
+ result = ISC_R_FAILURE;
+ }
+
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+
+ return (result);
+}
+
+/*%
+ * Configure DNSSEC keys for a view. Currently used only for
+ * the security roots.
+ *
+ * The per-view configuration values and the server-global defaults are read
+ * from 'vconfig' and 'config'. The variable to be configured is '*target'.
+ */
+static isc_result_t
+configure_view_dnsseckeys(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ isc_mem_t *mctx, dns_keytable_t **target)
+{
+ isc_result_t result;
+ const cfg_obj_t *keys = NULL;
+ const cfg_obj_t *voptions = NULL;
+ const cfg_listelt_t *element, *element2;
+ const cfg_obj_t *keylist;
+ const cfg_obj_t *key;
+ dns_keytable_t *keytable = NULL;
+
+ CHECK(dns_keytable_create(mctx, &keytable));
+
+ if (vconfig != NULL)
+ voptions = cfg_tuple_get(vconfig, "options");
+
+ keys = NULL;
+ if (voptions != NULL)
+ (void)cfg_map_get(voptions, "trusted-keys", &keys);
+ if (keys == NULL)
+ (void)cfg_map_get(config, "trusted-keys", &keys);
+
+ for (element = cfg_list_first(keys);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ keylist = cfg_listelt_value(element);
+ for (element2 = cfg_list_first(keylist);
+ element2 != NULL;
+ element2 = cfg_list_next(element2))
+ {
+ key = cfg_listelt_value(element2);
+ CHECK(configure_view_dnsseckey(vconfig, key,
+ keytable, mctx));
+ }
+ }
+
+ dns_keytable_detach(target);
+ *target = keytable; /* Transfer ownership. */
+ keytable = NULL;
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+static isc_result_t
+mustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver)
+{
+ const cfg_listelt_t *element;
+ const cfg_obj_t *obj;
+ const char *str;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_boolean_t value;
+ isc_result_t result;
+ isc_buffer_t b;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ for (element = cfg_list_first(mbs);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname,
+ ISC_FALSE, NULL));
+ value = cfg_obj_asboolean(cfg_tuple_get(obj, "value"));
+ CHECK(dns_resolver_setmustbesecure(resolver, name, value));
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+/*%
+ * Get a dispatch appropriate for the resolver of a given view.
+ */
+static isc_result_t
+get_view_querysource_dispatch(const cfg_obj_t **maps,
+ int af, dns_dispatch_t **dispatchp)
+{
+ isc_result_t result;
+ dns_dispatch_t *disp;
+ isc_sockaddr_t sa;
+ unsigned int attrs, attrmask;
+ const cfg_obj_t *obj = NULL;
+
+ /*
+ * Make compiler happy.
+ */
+ result = ISC_R_FAILURE;
+
+ switch (af) {
+ case AF_INET:
+ result = ns_config_get(maps, "query-source", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ break;
+ case AF_INET6:
+ result = ns_config_get(maps, "query-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ sa = *(cfg_obj_assockaddr(obj));
+ INSIST(isc_sockaddr_pf(&sa) == af);
+
+ /*
+ * If we don't support this address family, we're done!
+ */
+ switch (af) {
+ case AF_INET:
+ result = isc_net_probeipv4();
+ break;
+ case AF_INET6:
+ result = isc_net_probeipv6();
+ break;
+ default:
+ INSIST(0);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Try to find a dispatcher that we can share.
+ */
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_UDP;
+ switch (af) {
+ case AF_INET:
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ break;
+ case AF_INET6:
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ break;
+ }
+ attrmask = 0;
+ attrmask |= DNS_DISPATCHATTR_UDP;
+ attrmask |= DNS_DISPATCHATTR_TCP;
+ attrmask |= DNS_DISPATCHATTR_IPV4;
+ attrmask |= DNS_DISPATCHATTR_IPV6;
+
+ disp = NULL;
+ result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
+ ns_g_taskmgr, &sa, 4096,
+ 1000, 32768, 16411, 16433,
+ attrs, attrmask, &disp);
+ if (result != ISC_R_SUCCESS) {
+ isc_sockaddr_t any;
+ char buf[ISC_SOCKADDR_FORMATSIZE];
+
+ switch (af) {
+ case AF_INET:
+ isc_sockaddr_any(&any);
+ break;
+ case AF_INET6:
+ isc_sockaddr_any6(&any);
+ break;
+ }
+ if (isc_sockaddr_equal(&sa, &any))
+ return (ISC_R_SUCCESS);
+ isc_sockaddr_format(&sa, buf, sizeof(buf));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "could not get query source dispatcher (%s)",
+ buf);
+ return (result);
+ }
+
+ *dispatchp = disp;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+configure_order(dns_order_t *order, const cfg_obj_t *ent) {
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t rdtype;
+ const cfg_obj_t *obj;
+ dns_fixedname_t fixed;
+ unsigned int mode = 0;
+ const char *str;
+ isc_buffer_t b;
+ isc_result_t result;
+ isc_boolean_t addroot;
+
+ result = ns_config_getclass(cfg_tuple_get(ent, "class"),
+ dns_rdataclass_any, &rdclass);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = ns_config_gettype(cfg_tuple_get(ent, "type"),
+ dns_rdatatype_any, &rdtype);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = cfg_tuple_get(ent, "name");
+ if (cfg_obj_isstring(obj))
+ str = cfg_obj_asstring(obj);
+ else
+ str = "*";
+ addroot = ISC_TF(strcmp(str, "*") == 0);
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ dns_fixedname_init(&fixed);
+ result = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
+ dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = cfg_tuple_get(ent, "ordering");
+ INSIST(cfg_obj_isstring(obj));
+ str = cfg_obj_asstring(obj);
+ if (!strcasecmp(str, "fixed"))
+ mode = DNS_RDATASETATTR_FIXEDORDER;
+ else if (!strcasecmp(str, "random"))
+ mode = DNS_RDATASETATTR_RANDOMIZE;
+ else if (!strcasecmp(str, "cyclic"))
+ mode = 0;
+ else
+ INSIST(0);
+
+ /*
+ * "*" should match everything including the root (BIND 8 compat).
+ * As dns_name_matcheswildcard(".", "*.") returns FALSE add a
+ * explicit entry for "." when the name is "*".
+ */
+ if (addroot) {
+ result = dns_order_add(order, dns_rootname,
+ rdtype, rdclass, mode);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ return (dns_order_add(order, dns_fixedname_name(&fixed),
+ rdtype, rdclass, mode));
+}
+
+static isc_result_t
+configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
+ isc_netaddr_t na;
+ dns_peer_t *peer;
+ const cfg_obj_t *obj;
+ const char *str;
+ isc_result_t result;
+ unsigned int prefixlen;
+
+ cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen);
+
+ peer = NULL;
+ result = dns_peer_new(mctx, &na, &peer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "bogus", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "provide-ixfr", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "request-ixfr", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "edns", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "edns-udp-size", &obj);
+ if (obj != NULL) {
+ isc_uint32_t udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ CHECK(dns_peer_setudpsize(peer, (isc_uint16_t)udpsize));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "max-udp-size", &obj);
+ if (obj != NULL) {
+ isc_uint32_t udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ CHECK(dns_peer_setmaxudp(peer, (isc_uint16_t)udpsize));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "transfers", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "transfer-format", &obj);
+ if (obj != NULL) {
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "many-answers") == 0)
+ CHECK(dns_peer_settransferformat(peer,
+ dns_many_answers));
+ else if (strcasecmp(str, "one-answer") == 0)
+ CHECK(dns_peer_settransferformat(peer,
+ dns_one_answer));
+ else
+ INSIST(0);
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "keys", &obj);
+ if (obj != NULL) {
+ result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ obj = NULL;
+ if (na.family == AF_INET)
+ (void)cfg_map_get(cpeer, "transfer-source", &obj);
+ else
+ (void)cfg_map_get(cpeer, "transfer-source-v6", &obj);
+ if (obj != NULL) {
+ result = dns_peer_settransfersource(peer,
+ cfg_obj_assockaddr(obj));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+ }
+
+ obj = NULL;
+ if (na.family == AF_INET)
+ (void)cfg_map_get(cpeer, "notify-source", &obj);
+ else
+ (void)cfg_map_get(cpeer, "notify-source-v6", &obj);
+ if (obj != NULL) {
+ result = dns_peer_setnotifysource(peer,
+ cfg_obj_assockaddr(obj));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+ }
+
+ obj = NULL;
+ if (na.family == AF_INET)
+ (void)cfg_map_get(cpeer, "query-source", &obj);
+ else
+ (void)cfg_map_get(cpeer, "query-source-v6", &obj);
+ if (obj != NULL) {
+ result = dns_peer_setquerysource(peer,
+ cfg_obj_assockaddr(obj));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+ }
+
+ *peerp = peer;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ dns_peer_detach(&peer);
+ return (result);
+}
+
+static isc_result_t
+disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
+ isc_result_t result;
+ const cfg_obj_t *algorithms;
+ const cfg_listelt_t *element;
+ const char *str;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL));
+
+ algorithms = cfg_tuple_get(disabled, "algorithms");
+ for (element = cfg_list_first(algorithms);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ isc_textregion_t r;
+ dns_secalg_t alg;
+
+ DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
+ r.length = strlen(r.base);
+
+ result = dns_secalg_fromtext(&alg, &r);
+ if (result != ISC_R_SUCCESS) {
+ isc_uint8_t ui;
+ result = isc_parse_uint8(&ui, r.base, 10);
+ alg = ui;
+ }
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(cfg_listelt_value(element),
+ ns_g_lctx, ISC_LOG_ERROR,
+ "invalid algorithm");
+ CHECK(result);
+ }
+ CHECK(dns_resolver_disable_algorithm(resolver, name, alg));
+ }
+ cleanup:
+ return (result);
+}
+
+static isc_boolean_t
+on_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) {
+ const cfg_listelt_t *element;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_result_t result;
+ const cfg_obj_t *value;
+ const char *str;
+ isc_buffer_t b;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+
+ for (element = cfg_list_first(disablelist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ value = cfg_listelt_value(element);
+ str = cfg_obj_asstring(value);
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(name, &b, dns_rootname,
+ ISC_TRUE, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (dns_name_equal(name, zonename))
+ return (ISC_TRUE);
+ }
+ return (ISC_FALSE);
+}
+
+static void
+check_dbtype(dns_zone_t **zonep, unsigned int dbtypec, const char **dbargv,
+ isc_mem_t *mctx)
+{
+ char **argv = NULL;
+ unsigned int i;
+ isc_result_t result;
+
+ result = dns_zone_getdbtype(*zonep, &argv, mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_detach(zonep);
+ return;
+ }
+
+ /*
+ * Check that all the arguments match.
+ */
+ for (i = 0; i < dbtypec; i++)
+ if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) {
+ dns_zone_detach(zonep);
+ break;
+ }
+
+ /*
+ * Check that there are not extra arguments.
+ */
+ if (i == dbtypec && argv[i] != NULL)
+ dns_zone_detach(zonep);
+ isc_mem_free(mctx, argv);
+}
+
+
+/*
+ * Configure 'view' according to 'vconfig', taking defaults from 'config'
+ * where values are missing in 'vconfig'.
+ *
+ * When configuring the default view, 'vconfig' will be NULL and the
+ * global defaults in 'config' used exclusively.
+ */
+static isc_result_t
+configure_view(dns_view_t *view, const cfg_obj_t *config,
+ const cfg_obj_t *vconfig, isc_mem_t *mctx,
+ cfg_aclconfctx_t *actx, isc_boolean_t need_hints)
+{
+ const cfg_obj_t *maps[4];
+ const cfg_obj_t *cfgmaps[3];
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *voptions = NULL;
+ const cfg_obj_t *forwardtype;
+ const cfg_obj_t *forwarders;
+ const cfg_obj_t *alternates;
+ const cfg_obj_t *zonelist;
+#ifdef DLZ
+ const cfg_obj_t *dlz;
+ unsigned int dlzargc;
+ char **dlzargv;
+#endif
+ const cfg_obj_t *disabled;
+ const cfg_obj_t *obj;
+ const cfg_listelt_t *element;
+ in_port_t port;
+ dns_cache_t *cache = NULL;
+ isc_result_t result;
+ isc_uint32_t max_adb_size;
+ isc_uint32_t max_cache_size;
+ isc_uint32_t max_acache_size;
+ isc_uint32_t lame_ttl;
+ dns_tsig_keyring_t *ring;
+ dns_view_t *pview = NULL; /* Production view */
+ isc_mem_t *cmctx;
+ dns_dispatch_t *dispatch4 = NULL;
+ dns_dispatch_t *dispatch6 = NULL;
+ isc_boolean_t reused_cache = ISC_FALSE;
+ int i;
+ const char *str;
+ dns_order_t *order = NULL;
+ isc_uint32_t udpsize;
+ unsigned int check = 0;
+ dns_zone_t *zone = NULL;
+ isc_uint32_t max_clients_per_query;
+ const char *sep = ": view ";
+ const char *viewname = view->name;
+ const char *forview = " for view ";
+ isc_boolean_t rfc1918;
+ isc_boolean_t empty_zones_enable;
+ const cfg_obj_t *disablelist = NULL;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ cmctx = NULL;
+
+ if (config != NULL)
+ (void)cfg_map_get(config, "options", &options);
+
+ i = 0;
+ if (vconfig != NULL) {
+ voptions = cfg_tuple_get(vconfig, "options");
+ maps[i++] = voptions;
+ }
+ if (options != NULL)
+ maps[i++] = options;
+ maps[i++] = ns_g_defaults;
+ maps[i] = NULL;
+
+ i = 0;
+ if (voptions != NULL)
+ cfgmaps[i++] = voptions;
+ if (config != NULL)
+ cfgmaps[i++] = config;
+ cfgmaps[i] = NULL;
+
+ if (!strcmp(viewname, "_default")) {
+ sep = "";
+ viewname = "";
+ forview = "";
+ }
+
+ /*
+ * Set the view's port number for outgoing queries.
+ */
+ CHECKM(ns_config_getport(config, &port), "port");
+ dns_view_setdstport(view, port);
+
+ /*
+ * Create additional cache for this view and zones under the view
+ * if explicitly enabled.
+ * XXX950 default to on.
+ */
+ obj = NULL;
+ (void)ns_config_get(maps, "acache-enable", &obj);
+ if (obj != NULL && cfg_obj_asboolean(obj)) {
+ cmctx = NULL;
+ CHECK(isc_mem_create(0, 0, &cmctx));
+ CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr,
+ ns_g_timermgr));
+ isc_mem_detach(&cmctx);
+ }
+ if (view->acache != NULL) {
+ obj = NULL;
+ result = ns_config_get(maps, "acache-cleaning-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_acache_setcleaninginterval(view->acache,
+ cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-acache-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isstring(obj)) {
+ str = cfg_obj_asstring(obj);
+ INSIST(strcasecmp(str, "unlimited") == 0);
+ max_acache_size = ISC_UINT32_MAX;
+ } else {
+ isc_resourcevalue_t value;
+
+ value = cfg_obj_asuint64(obj);
+ if (value > ISC_UINT32_MAX) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "'max-acache-size "
+ "%" ISC_PRINT_QUADFORMAT
+ "d' is too large",
+ value);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ max_acache_size = (isc_uint32_t)value;
+ }
+ dns_acache_setcachesize(view->acache, max_acache_size);
+ }
+
+ /*
+ * Configure the zones.
+ */
+ zonelist = NULL;
+ if (voptions != NULL)
+ (void)cfg_map_get(voptions, "zone", &zonelist);
+ else
+ (void)cfg_map_get(config, "zone", &zonelist);
+ for (element = cfg_list_first(zonelist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *zconfig = cfg_listelt_value(element);
+ CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
+ actx));
+ }
+
+#ifdef DLZ
+ /*
+ * Create Dynamically Loadable Zone driver.
+ */
+ dlz = NULL;
+ if (voptions != NULL)
+ (void)cfg_map_get(voptions, "dlz", &dlz);
+ else
+ (void)cfg_map_get(config, "dlz", &dlz);
+
+ obj = NULL;
+ if (dlz != NULL) {
+ (void)cfg_map_get(cfg_tuple_get(dlz, "options"),
+ "database", &obj);
+ if (obj != NULL) {
+ char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
+ if (s == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_free(mctx, s);
+ goto cleanup;
+ }
+
+ obj = cfg_tuple_get(dlz, "name");
+ result = dns_dlzcreate(mctx, cfg_obj_asstring(obj),
+ dlzargv[0], dlzargc, dlzargv,
+ &view->dlzdatabase);
+ isc_mem_free(mctx, s);
+ isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ }
+#endif
+
+ /*
+ * Configure the view's cache. Try to reuse an existing
+ * cache if possible, otherwise create a new cache.
+ * Note that the ADB is not preserved in either case.
+ *
+ * XXX Determining when it is safe to reuse a cache is
+ * tricky. When the view's configuration changes, the cached
+ * data may become invalid because it reflects our old
+ * view of the world. As more view attributes become
+ * configurable, we will have to add code here to check
+ * whether they have changed in ways that could
+ * invalidate the cache.
+ */
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (pview != NULL) {
+ INSIST(pview->cache != NULL);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3),
+ "reusing existing cache");
+ reused_cache = ISC_TRUE;
+ dns_cache_attach(pview->cache, &cache);
+ dns_view_detach(&pview);
+ } else {
+ CHECK(isc_mem_create(0, 0, &cmctx));
+ CHECK(dns_cache_create(cmctx, ns_g_taskmgr, ns_g_timermgr,
+ view->rdclass, "rbt", 0, NULL, &cache));
+ }
+ dns_view_setcache(view, cache);
+
+ /*
+ * cache-file cannot be inherited if views are present, but this
+ * should be caught by the configuration checking stage.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "cache-file", &obj);
+ if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
+ CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
+ if (!reused_cache)
+ CHECK(dns_cache_load(cache));
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "cleaning-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_cache_setcleaninginterval(cache, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-cache-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isstring(obj)) {
+ str = cfg_obj_asstring(obj);
+ INSIST(strcasecmp(str, "unlimited") == 0);
+ max_cache_size = ISC_UINT32_MAX;
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > ISC_UINT32_MAX) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "'max-cache-size "
+ "%" ISC_PRINT_QUADFORMAT "d' is too large",
+ value);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ max_cache_size = (isc_uint32_t)value;
+ }
+ dns_cache_setcachesize(cache, max_cache_size);
+
+ dns_cache_detach(&cache);
+
+ /*
+ * Check-names.
+ */
+ obj = NULL;
+ result = ns_checknames_get(maps, "response", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "fail") == 0) {
+ check = DNS_RESOLVER_CHECKNAMES |
+ DNS_RESOLVER_CHECKNAMESFAIL;
+ view->checknames = ISC_TRUE;
+ } else if (strcasecmp(str, "warn") == 0) {
+ check = DNS_RESOLVER_CHECKNAMES;
+ view->checknames = ISC_FALSE;
+ } else if (strcasecmp(str, "ignore") == 0) {
+ check = 0;
+ view->checknames = ISC_FALSE;
+ } else
+ INSIST(0);
+
+ /*
+ * Resolver.
+ *
+ * XXXRTH Hardwired number of tasks.
+ */
+ CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4));
+ CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6));
+ if (dispatch4 == NULL && dispatch6 == NULL) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "unable to obtain neither an IPv4 nor"
+ " an IPv6 dispatch");
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+ CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
+ ns_g_socketmgr, ns_g_timermgr,
+ check, ns_g_dispatchmgr,
+ dispatch4, dispatch6));
+
+ /*
+ * Set the ADB cache size to 1/8th of the max-cache-size.
+ */
+ max_adb_size = 0;
+ if (max_cache_size != 0) {
+ max_adb_size = max_cache_size / 8;
+ if (max_adb_size == 0)
+ max_adb_size = 1; /* Force minimum. */
+ }
+ dns_adb_setadbsize(view->adb, max_adb_size);
+
+ /*
+ * Set resolver's lame-ttl.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "lame-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ lame_ttl = cfg_obj_asuint32(obj);
+ if (lame_ttl > 1800)
+ lame_ttl = 1800;
+ dns_resolver_setlamettl(view->resolver, lame_ttl);
+
+ obj = NULL;
+ result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setzeronosoattl(view->resolver, cfg_obj_asboolean(obj));
+
+ /*
+ * Set the resolver's EDNS UDP size.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "edns-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ dns_resolver_setudpsize(view->resolver, (isc_uint16_t)udpsize);
+
+ /*
+ * Set the maximum UDP response size.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "max-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ view->maxudp = udpsize;
+
+ /*
+ * Set supported DNSSEC algorithms.
+ */
+ dns_resolver_reset_algorithms(view->resolver);
+ disabled = NULL;
+ (void)ns_config_get(maps, "disable-algorithms", &disabled);
+ if (disabled != NULL) {
+ for (element = cfg_list_first(disabled);
+ element != NULL;
+ element = cfg_list_next(element))
+ CHECK(disable_algorithms(cfg_listelt_value(element),
+ view->resolver));
+ }
+
+ /*
+ * A global or view "forwarders" option, if present,
+ * creates an entry for "." in the forwarding table.
+ */
+ forwardtype = NULL;
+ forwarders = NULL;
+ (void)ns_config_get(maps, "forward", &forwardtype);
+ (void)ns_config_get(maps, "forwarders", &forwarders);
+ if (forwarders != NULL)
+ CHECK(configure_forward(config, view, dns_rootname,
+ forwarders, forwardtype));
+
+ /*
+ * Dual Stack Servers.
+ */
+ alternates = NULL;
+ (void)ns_config_get(maps, "dual-stack-servers", &alternates);
+ if (alternates != NULL)
+ CHECK(configure_alternates(config, view, alternates));
+
+ /*
+ * We have default hints for class IN if we need them.
+ */
+ if (view->rdclass == dns_rdataclass_in && view->hints == NULL)
+ dns_view_sethints(view, ns_g_server->in_roothints);
+
+ /*
+ * If we still have no hints, this is a non-IN view with no
+ * "hints zone" configured. Issue a warning, except if this
+ * is a root server. Root servers never need to consult
+ * their hints, so it's no point requiring users to configure
+ * them.
+ */
+ if (view->hints == NULL) {
+ dns_zone_t *rootzone = NULL;
+ (void)dns_view_findzone(view, dns_rootname, &rootzone);
+ if (rootzone != NULL) {
+ dns_zone_detach(&rootzone);
+ need_hints = ISC_FALSE;
+ }
+ if (need_hints)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "no root hints for view '%s'",
+ view->name);
+ }
+
+ /*
+ * Configure the view's TSIG keys.
+ */
+ ring = NULL;
+ CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring));
+ dns_view_setkeyring(view, ring);
+
+ /*
+ * Configure the view's peer list.
+ */
+ {
+ const cfg_obj_t *peers = NULL;
+ const cfg_listelt_t *element;
+ dns_peerlist_t *newpeers = NULL;
+
+ (void)ns_config_get(cfgmaps, "server", &peers);
+ CHECK(dns_peerlist_new(mctx, &newpeers));
+ for (element = cfg_list_first(peers);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *cpeer = cfg_listelt_value(element);
+ dns_peer_t *peer;
+
+ CHECK(configure_peer(cpeer, mctx, &peer));
+ dns_peerlist_addpeer(newpeers, peer);
+ dns_peer_detach(&peer);
+ }
+ dns_peerlist_detach(&view->peers);
+ view->peers = newpeers; /* Transfer ownership. */
+ }
+
+ /*
+ * Configure the views rrset-order.
+ */
+ {
+ const cfg_obj_t *rrsetorder = NULL;
+ const cfg_listelt_t *element;
+
+ (void)ns_config_get(maps, "rrset-order", &rrsetorder);
+ CHECK(dns_order_create(mctx, &order));
+ for (element = cfg_list_first(rrsetorder);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *ent = cfg_listelt_value(element);
+
+ CHECK(configure_order(order, ent));
+ }
+ if (view->order != NULL)
+ dns_order_detach(&view->order);
+ dns_order_attach(order, &view->order);
+ dns_order_detach(&order);
+ }
+ /*
+ * Copy the aclenv object.
+ */
+ dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv);
+
+ /*
+ * Configure the "match-clients" and "match-destinations" ACL.
+ */
+ CHECK(configure_view_acl(vconfig, config, "match-clients", actx,
+ ns_g_mctx, &view->matchclients));
+ CHECK(configure_view_acl(vconfig, config, "match-destinations", actx,
+ ns_g_mctx, &view->matchdestinations));
+
+ /*
+ * Configure the "match-recursive-only" option.
+ */
+ obj = NULL;
+ (void)ns_config_get(maps, "match-recursive-only", &obj);
+ if (obj != NULL && cfg_obj_asboolean(obj))
+ view->matchrecursiveonly = ISC_TRUE;
+ else
+ view->matchrecursiveonly = ISC_FALSE;
+
+ /*
+ * Configure other configurable data.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "recursion", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->recursion = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "auth-nxdomain", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->auth_nxdomain = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "minimal-responses", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->minimalresponses = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "transfer-format", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "many-answers") == 0)
+ view->transfer_format = dns_many_answers;
+ else if (strcasecmp(str, "one-answer") == 0)
+ view->transfer_format = dns_one_answer;
+ else
+ INSIST(0);
+
+ /*
+ * Set sources where additional data and CNAME/DNAME
+ * targets for authoritative answers may be found.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "additional-from-auth", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->additionalfromauth = cfg_obj_asboolean(obj);
+ if (view->recursion && ! view->additionalfromauth) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
+ "'additional-from-auth no' is only supported "
+ "with 'recursion no'");
+ view->additionalfromauth = ISC_TRUE;
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "additional-from-cache", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->additionalfromcache = cfg_obj_asboolean(obj);
+ if (view->recursion && ! view->additionalfromcache) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
+ "'additional-from-cache no' is only supported "
+ "with 'recursion no'");
+ view->additionalfromcache = ISC_TRUE;
+ }
+
+ /*
+ * Set "allow-query-cache" and "allow-recursion" acls if
+ * configured in named.conf.
+ */
+ CHECK(configure_view_acl(vconfig, config, "allow-query-cache",
+ actx, ns_g_mctx, &view->queryacl));
+
+ if (strcmp(view->name, "_bind") != 0)
+ CHECK(configure_view_acl(vconfig, config, "allow-recursion",
+ actx, ns_g_mctx, &view->recursionacl));
+
+ /*
+ * Warning if both "recursion no;" and allow-recursion are active
+ * except for "allow-recursion { none; };".
+ */
+ if (!view->recursion && view->recursionacl != NULL &&
+ (view->recursionacl->length != 1 ||
+ view->recursionacl->elements[0].type != dns_aclelementtype_any ||
+ view->recursionacl->elements[0].negative != ISC_TRUE))
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "both \"recursion no;\" and \"allow-recursion\" "
+ "active%s%s", forview, viewname);
+
+ /*
+ * "allow-query-cache" inherits from "allow-recursion" if set,
+ * otherwise from "allow-query" if set.
+ * "allow-recursion" inherits from "allow-query-cache" if set,
+ * otherwise from "allow-query" if set.
+ */
+ if (view->queryacl == NULL && view->recursionacl != NULL)
+ dns_acl_attach(view->recursionacl, &view->queryacl);
+ if (view->queryacl == NULL)
+ CHECK(configure_view_acl(vconfig, config, "allow-query",
+ actx, ns_g_mctx, &view->queryacl));
+ if (view->recursionacl == NULL && view->queryacl != NULL)
+ dns_acl_attach(view->queryacl, &view->recursionacl);
+
+ /*
+ * Set default "allow-recursion" and "allow-query-cache" acls.
+ */
+ if (view->recursionacl == NULL && view->recursion)
+ CHECK(configure_view_acl(NULL, ns_g_config, "allow-recursion",
+ actx, ns_g_mctx, &view->recursionacl));
+ if (view->queryacl == NULL)
+ CHECK(configure_view_acl(NULL, ns_g_config,
+ "allow-query-cache", actx,
+ ns_g_mctx, &view->queryacl));
+
+ CHECK(configure_view_acl(vconfig, config, "sortlist",
+ actx, ns_g_mctx, &view->sortlist));
+
+ obj = NULL;
+ result = ns_config_get(maps, "request-ixfr", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->requestixfr = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "provide-ixfr", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->provideixfr = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-clients-per-query", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ max_clients_per_query = cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "clients-per-query", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setclientsperquery(view->resolver,
+ cfg_obj_asuint32(obj),
+ max_clients_per_query);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-enable", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->enablednssec = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-accept-expired", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->acceptexpired = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-validation", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->enablevalidation = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-lookaside", &obj);
+ if (result == ISC_R_SUCCESS) {
+ for (element = cfg_list_first(obj);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const char *str;
+ isc_buffer_t b;
+ dns_name_t *dlv;
+
+ obj = cfg_listelt_value(element);
+#if 0
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ /*
+ * When we support multiple dnssec-lookaside
+ * entries this is how to find the domain to be
+ * checked. XXXMPA
+ */
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ str = cfg_obj_asstring(cfg_tuple_get(obj,
+ "domain"));
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname,
+ ISC_TRUE, NULL));
+#endif
+ str = cfg_obj_asstring(cfg_tuple_get(obj,
+ "trust-anchor"));
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ dlv = dns_fixedname_name(&view->dlv_fixed);
+ CHECK(dns_name_fromtext(dlv, &b, dns_rootname,
+ ISC_TRUE, NULL));
+ view->dlv = dns_fixedname_name(&view->dlv_fixed);
+ }
+ } else
+ view->dlv = NULL;
+
+ /*
+ * For now, there is only one kind of trusted keys, the
+ * "security roots".
+ */
+ CHECK(configure_view_dnsseckeys(vconfig, config, mctx,
+ &view->secroots));
+ dns_resolver_resetmustbesecure(view->resolver);
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-must-be-secure", &obj);
+ if (result == ISC_R_SUCCESS)
+ CHECK(mustbesecure(obj, view->resolver));
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-cache-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->maxcachettl = cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-ncache-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->maxncachettl = cfg_obj_asuint32(obj);
+ if (view->maxncachettl > 7 * 24 * 3600)
+ view->maxncachettl = 7 * 24 * 3600;
+
+ obj = NULL;
+ result = ns_config_get(maps, "preferred-glue", &obj);
+ if (result == ISC_R_SUCCESS) {
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "a") == 0)
+ view->preferred_glue = dns_rdatatype_a;
+ else if (strcasecmp(str, "aaaa") == 0)
+ view->preferred_glue = dns_rdatatype_aaaa;
+ else
+ view->preferred_glue = 0;
+ } else
+ view->preferred_glue = 0;
+
+ obj = NULL;
+ result = ns_config_get(maps, "root-delegation-only", &obj);
+ if (result == ISC_R_SUCCESS) {
+ dns_view_setrootdelonly(view, ISC_TRUE);
+ if (!cfg_obj_isvoid(obj)) {
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+ const char *str;
+ const cfg_obj_t *exclude;
+
+ 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));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname,
+ ISC_FALSE, NULL));
+ CHECK(dns_view_excludedelegationonly(view,
+ name));
+ }
+ }
+ } else
+ dns_view_setrootdelonly(view, ISC_FALSE);
+
+ /*
+ * Setup automatic empty zones. If recursion is off then
+ * they are disabled by default.
+ */
+ obj = NULL;
+ (void)ns_config_get(maps, "empty-zones-enable", &obj);
+ (void)ns_config_get(maps, "disable-empty-zone", &disablelist);
+ if (obj == NULL && disablelist == NULL &&
+ view->rdclass == dns_rdataclass_in) {
+ rfc1918 = ISC_FALSE;
+ empty_zones_enable = view->recursion;
+ } else if (view->rdclass == dns_rdataclass_in) {
+ rfc1918 = ISC_TRUE;
+ if (obj != NULL)
+ empty_zones_enable = cfg_obj_asboolean(obj);
+ else
+ empty_zones_enable = view->recursion;
+ } else {
+ rfc1918 = ISC_FALSE;
+ empty_zones_enable = ISC_FALSE;
+ }
+ if (empty_zones_enable) {
+ const char *empty;
+ int empty_zone = 0;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t buffer;
+ const char *str;
+ char server[DNS_NAME_FORMATSIZE + 1];
+ char contact[DNS_NAME_FORMATSIZE + 1];
+ isc_boolean_t logit;
+ const char *empty_dbtype[4] =
+ { "_builtin", "empty", NULL, NULL };
+ int empty_dbtypec = 4;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+
+ obj = NULL;
+ result = ns_config_get(maps, "empty-server", &obj);
+ if (result == ISC_R_SUCCESS) {
+ str = cfg_obj_asstring(obj);
+ isc_buffer_init(&buffer, str, strlen(str));
+ isc_buffer_add(&buffer, strlen(str));
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
+ ISC_FALSE, NULL));
+ isc_buffer_init(&buffer, server, sizeof(server) - 1);
+ CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
+ server[isc_buffer_usedlength(&buffer)] = 0;
+ empty_dbtype[2] = server;
+ } else
+ empty_dbtype[2] = "@";
+
+ obj = NULL;
+ result = ns_config_get(maps, "empty-contact", &obj);
+ if (result == ISC_R_SUCCESS) {
+ str = cfg_obj_asstring(obj);
+ isc_buffer_init(&buffer, str, strlen(str));
+ isc_buffer_add(&buffer, strlen(str));
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
+ ISC_FALSE, NULL));
+ isc_buffer_init(&buffer, contact, sizeof(contact) - 1);
+ CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
+ contact[isc_buffer_usedlength(&buffer)] = 0;
+ empty_dbtype[3] = contact;
+ } else
+ empty_dbtype[3] = ".";
+
+ logit = ISC_TRUE;
+ for (empty = empty_zones[empty_zone].zone;
+ empty != NULL;
+ empty = empty_zones[++empty_zone].zone)
+ {
+ dns_forwarders_t *forwarders = NULL;
+ dns_view_t *pview = NULL;
+
+ isc_buffer_init(&buffer, empty, strlen(empty));
+ isc_buffer_add(&buffer, strlen(empty));
+ /*
+ * Look for zone on drop list.
+ */
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
+ ISC_FALSE, NULL));
+ if (disablelist != NULL &&
+ on_disable_list(disablelist, name))
+ continue;
+
+ /*
+ * This zone already exists.
+ */
+ (void)dns_view_findzone(view, name, &zone);
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ continue;
+ }
+
+ /*
+ * If we would forward this name don't add a
+ * empty zone for it.
+ */
+ result = dns_fwdtable_find(view->fwdtable, name,
+ &forwarders);
+ if (result == ISC_R_SUCCESS &&
+ forwarders->fwdpolicy == dns_fwdpolicy_only)
+ continue;
+
+ if (!rfc1918 && empty_zones[empty_zone].rfc1918) {
+ if (logit) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING,
+ "Warning%s%s: "
+ "'empty-zones-enable/"
+ "disable-empty-zone' "
+ "not set: disabling "
+ "RFC 1918 empty zones",
+ sep, viewname);
+ logit = ISC_FALSE;
+ }
+ continue;
+ }
+
+ /*
+ * See if we can re-use a existing zone.
+ */
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND &&
+ result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (pview != NULL) {
+ (void)dns_view_findzone(pview, name, &zone);
+ dns_view_detach(&pview);
+ if (zone != NULL)
+ check_dbtype(&zone, empty_dbtypec,
+ empty_dbtype, mctx);
+ if (zone != NULL) {
+ dns_zone_setview(zone, view);
+ CHECK(dns_view_addzone(view, zone));
+ dns_zone_detach(&zone);
+ continue;
+ }
+ }
+
+ CHECK(dns_zone_create(&zone, mctx));
+ CHECK(dns_zone_setorigin(zone, name));
+ dns_zone_setview(zone, view);
+ CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
+ dns_zone_setclass(zone, view->rdclass);
+ dns_zone_settype(zone, dns_zone_master);
+ CHECK(dns_zone_setdbtype(zone, empty_dbtypec,
+ empty_dbtype));
+ if (view->queryacl != NULL)
+ dns_zone_setqueryacl(zone, view->queryacl);
+ dns_zone_setdialup(zone, dns_dialuptype_no);
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS,
+ ISC_TRUE);
+ CHECK(dns_view_addzone(view, zone));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "automatic empty zone%s%s: %s",
+ sep, viewname, empty);
+ dns_zone_detach(&zone);
+ }
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (dispatch4 != NULL)
+ dns_dispatch_detach(&dispatch4);
+ if (dispatch6 != NULL)
+ dns_dispatch_detach(&dispatch6);
+ if (order != NULL)
+ dns_order_detach(&order);
+ if (cmctx != NULL)
+ isc_mem_detach(&cmctx);
+
+ if (cache != NULL)
+ dns_cache_detach(&cache);
+
+ return (result);
+}
+
+static isc_result_t
+configure_hints(dns_view_t *view, const char *filename) {
+ isc_result_t result;
+ dns_db_t *db;
+
+ db = NULL;
+ result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
+ if (result == ISC_R_SUCCESS) {
+ dns_view_sethints(view, db);
+ dns_db_detach(&db);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+configure_alternates(const cfg_obj_t *config, dns_view_t *view,
+ const cfg_obj_t *alternates)
+{
+ const cfg_obj_t *portobj;
+ const cfg_obj_t *addresses;
+ const cfg_listelt_t *element;
+ isc_result_t result = ISC_R_SUCCESS;
+ in_port_t port;
+
+ /*
+ * Determine which port to send requests to.
+ */
+ if (ns_g_lwresdonly && ns_g_port != 0)
+ port = ns_g_port;
+ else
+ CHECKM(ns_config_getport(config, &port), "port");
+
+ if (alternates != NULL) {
+ portobj = cfg_tuple_get(alternates, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t) val;
+ }
+ }
+
+ addresses = NULL;
+ if (alternates != NULL)
+ addresses = cfg_tuple_get(alternates, "addresses");
+
+ for (element = cfg_list_first(addresses);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *alternate = cfg_listelt_value(element);
+ isc_sockaddr_t sa;
+
+ if (!cfg_obj_issockaddr(alternate)) {
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ const char *str = cfg_obj_asstring(cfg_tuple_get(
+ alternate, "name"));
+ isc_buffer_t buffer;
+ in_port_t myport = port;
+
+ isc_buffer_init(&buffer, str, strlen(str));
+ isc_buffer_add(&buffer, strlen(str));
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
+ ISC_FALSE, NULL));
+
+ portobj = cfg_tuple_get(alternate, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx,
+ ISC_LOG_ERROR,
+ "port '%u' out of range",
+ val);
+ return (ISC_R_RANGE);
+ }
+ myport = (in_port_t) val;
+ }
+ CHECK(dns_resolver_addalternate(view->resolver, NULL,
+ name, myport));
+ continue;
+ }
+
+ sa = *cfg_obj_assockaddr(alternate);
+ if (isc_sockaddr_getport(&sa) == 0)
+ isc_sockaddr_setport(&sa, port);
+ CHECK(dns_resolver_addalternate(view->resolver, &sa,
+ NULL, 0));
+ }
+
+ cleanup:
+ return (result);
+}
+
+static isc_result_t
+configure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
+ const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype)
+{
+ const cfg_obj_t *portobj;
+ const cfg_obj_t *faddresses;
+ const cfg_listelt_t *element;
+ dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none;
+ isc_sockaddrlist_t addresses;
+ isc_sockaddr_t *sa;
+ isc_result_t result;
+ in_port_t port;
+
+ /*
+ * Determine which port to send forwarded requests to.
+ */
+ if (ns_g_lwresdonly && ns_g_port != 0)
+ port = ns_g_port;
+ else
+ CHECKM(ns_config_getport(config, &port), "port");
+
+ if (forwarders != NULL) {
+ portobj = cfg_tuple_get(forwarders, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t) val;
+ }
+ }
+
+ faddresses = NULL;
+ if (forwarders != NULL)
+ faddresses = cfg_tuple_get(forwarders, "addresses");
+
+ ISC_LIST_INIT(addresses);
+
+ for (element = cfg_list_first(faddresses);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *forwarder = cfg_listelt_value(element);
+ sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t));
+ if (sa == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ *sa = *cfg_obj_assockaddr(forwarder);
+ if (isc_sockaddr_getport(sa) == 0)
+ isc_sockaddr_setport(sa, port);
+ ISC_LINK_INIT(sa, link);
+ ISC_LIST_APPEND(addresses, sa, link);
+ }
+
+ if (ISC_LIST_EMPTY(addresses)) {
+ if (forwardtype != NULL)
+ cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
+ "no forwarders seen; disabling "
+ "forwarding");
+ fwdpolicy = dns_fwdpolicy_none;
+ } else {
+ if (forwardtype == NULL)
+ fwdpolicy = dns_fwdpolicy_first;
+ else {
+ const char *forwardstr = cfg_obj_asstring(forwardtype);
+ if (strcasecmp(forwardstr, "first") == 0)
+ fwdpolicy = dns_fwdpolicy_first;
+ else if (strcasecmp(forwardstr, "only") == 0)
+ fwdpolicy = dns_fwdpolicy_only;
+ else
+ INSIST(0);
+ }
+ }
+
+ result = dns_fwdtable_add(view->fwdtable, origin, &addresses,
+ fwdpolicy);
+ if (result != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(origin, namebuf, sizeof(namebuf));
+ cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
+ "could not set up forwarding for domain '%s': %s",
+ namebuf, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+
+ while (!ISC_LIST_EMPTY(addresses)) {
+ sa = ISC_LIST_HEAD(addresses);
+ ISC_LIST_UNLINK(addresses, sa, link);
+ isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t));
+ }
+
+ return (result);
+}
+
+/*
+ * Create a new view and add it to the list.
+ *
+ * If 'vconfig' is NULL, create the default view.
+ *
+ * The view created is attached to '*viewp'.
+ */
+static isc_result_t
+create_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
+ dns_view_t **viewp)
+{
+ isc_result_t result;
+ const char *viewname;
+ dns_rdataclass_t viewclass;
+ dns_view_t *view = NULL;
+
+ if (vconfig != NULL) {
+ const cfg_obj_t *classobj = NULL;
+
+ viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
+ classobj = cfg_tuple_get(vconfig, "class");
+ result = ns_config_getclass(classobj, dns_rdataclass_in,
+ &viewclass);
+ } else {
+ viewname = "_default";
+ viewclass = dns_rdataclass_in;
+ }
+ result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_R_EXISTS);
+ if (result != ISC_R_NOTFOUND)
+ return (result);
+ INSIST(view == NULL);
+
+ result = dns_view_create(ns_g_mctx, viewclass, viewname, &view);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ ISC_LIST_APPEND(*viewlist, view, link);
+ dns_view_attach(view, viewp);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Configure or reconfigure a zone.
+ */
+static isc_result_t
+configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
+ const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *aclconf)
+{
+ dns_view_t *pview = NULL; /* Production view */
+ dns_zone_t *zone = NULL; /* New or reused zone */
+ dns_zone_t *dupzone = NULL;
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *typeobj = NULL;
+ const cfg_obj_t *forwarders = NULL;
+ const cfg_obj_t *forwardtype = NULL;
+ const cfg_obj_t *only = NULL;
+ isc_result_t result;
+ isc_result_t tresult;
+ isc_buffer_t buffer;
+ dns_fixedname_t fixorigin;
+ dns_name_t *origin;
+ const char *zname;
+ dns_rdataclass_t zclass;
+ const char *ztypestr;
+
+ options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+
+ /*
+ * Get the zone origin as a dns_name_t.
+ */
+ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+ isc_buffer_init(&buffer, zname, strlen(zname));
+ isc_buffer_add(&buffer, strlen(zname));
+ dns_fixedname_init(&fixorigin);
+ CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin),
+ &buffer, dns_rootname, ISC_FALSE, NULL));
+ origin = dns_fixedname_name(&fixorigin);
+
+ CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
+ view->rdclass, &zclass));
+ if (zclass != view->rdclass) {
+ const char *vname = NULL;
+ if (vconfig != NULL)
+ vname = cfg_obj_asstring(cfg_tuple_get(vconfig,
+ "name"));
+ else
+ vname = "<default view>";
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': wrong class for view '%s'",
+ zname, vname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ (void)cfg_map_get(zoptions, "type", &typeobj);
+ if (typeobj == NULL) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "zone '%s' 'type' not specified", zname);
+ return (ISC_R_FAILURE);
+ }
+ ztypestr = cfg_obj_asstring(typeobj);
+
+ /*
+ * "hints zones" aren't zones. If we've got one,
+ * configure it and return.
+ */
+ if (strcasecmp(ztypestr, "hint") == 0) {
+ const cfg_obj_t *fileobj = NULL;
+ if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': 'file' not specified",
+ zname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ if (dns_name_equal(origin, dns_rootname)) {
+ const char *hintsfile = cfg_obj_asstring(fileobj);
+
+ result = configure_hints(view, hintsfile);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "could not configure root hints "
+ "from '%s': %s", hintsfile,
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ /*
+ * Hint zones may also refer to delegation only points.
+ */
+ only = NULL;
+ tresult = cfg_map_get(zoptions, "delegation-only",
+ &only);
+ if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only))
+ CHECK(dns_view_adddelegationonly(view, origin));
+ } else {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "ignoring non-root hint zone '%s'",
+ zname);
+ result = ISC_R_SUCCESS;
+ }
+ /* Skip ordinary zone processing. */
+ goto cleanup;
+ }
+
+ /*
+ * "forward zones" aren't zones either. Translate this syntax into
+ * the appropriate selective forwarding configuration and return.
+ */
+ if (strcasecmp(ztypestr, "forward") == 0) {
+ forwardtype = NULL;
+ forwarders = NULL;
+
+ (void)cfg_map_get(zoptions, "forward", &forwardtype);
+ (void)cfg_map_get(zoptions, "forwarders", &forwarders);
+ result = configure_forward(config, view, origin, forwarders,
+ forwardtype);
+ goto cleanup;
+ }
+
+ /*
+ * "delegation-only zones" aren't zones either.
+ */
+ if (strcasecmp(ztypestr, "delegation-only") == 0) {
+ result = dns_view_adddelegationonly(view, origin);
+ goto cleanup;
+ }
+
+ /*
+ * Check for duplicates in the new zone table.
+ */
+ result = dns_view_findzone(view, origin, &dupzone);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We already have this zone!
+ */
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "zone '%s' already exists", zname);
+ dns_zone_detach(&dupzone);
+ result = ISC_R_EXISTS;
+ goto cleanup;
+ }
+ INSIST(dupzone == NULL);
+
+ /*
+ * See if we can reuse an existing zone. This is
+ * only possible if all of these are true:
+ * - The zone's view exists
+ * - A zone with the right name exists in the view
+ * - The zone is compatible with the config
+ * options (e.g., an existing master zone cannot
+ * be reused if the options specify a slave zone)
+ */
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (pview != NULL)
+ result = dns_view_findzone(pview, origin, &zone);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (zone != NULL && !ns_zone_reusable(zone, zconfig))
+ dns_zone_detach(&zone);
+
+ if (zone != NULL) {
+ /*
+ * We found a reusable zone. Make it use the
+ * new view.
+ */
+ dns_zone_setview(zone, view);
+ if (view->acache != NULL)
+ dns_zone_setacache(zone, view->acache);
+ } else {
+ /*
+ * We cannot reuse an existing zone, we have
+ * to create a new one.
+ */
+ CHECK(dns_zone_create(&zone, mctx));
+ CHECK(dns_zone_setorigin(zone, origin));
+ dns_zone_setview(zone, view);
+ if (view->acache != NULL)
+ dns_zone_setacache(zone, view->acache);
+ CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
+ }
+
+ /*
+ * If the zone contains a 'forwarders' statement, configure
+ * selective forwarding.
+ */
+ forwarders = NULL;
+ if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS)
+ {
+ forwardtype = NULL;
+ (void)cfg_map_get(zoptions, "forward", &forwardtype);
+ CHECK(configure_forward(config, view, origin, forwarders,
+ forwardtype));
+ }
+
+ /*
+ * Stub and forward zones may also refer to delegation only points.
+ */
+ only = NULL;
+ if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS)
+ {
+ if (cfg_obj_asboolean(only))
+ CHECK(dns_view_adddelegationonly(view, origin));
+ }
+
+ /*
+ * Configure the zone.
+ */
+ CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone));
+
+ /*
+ * Add the zone to its view in the new view list.
+ */
+ CHECK(dns_view_addzone(view, zone));
+
+ cleanup:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (pview != NULL)
+ dns_view_detach(&pview);
+
+ return (result);
+}
+
+/*
+ * Configure a single server quota.
+ */
+static void
+configure_server_quota(const cfg_obj_t **maps, const char *name,
+ isc_quota_t *quota)
+{
+ const cfg_obj_t *obj = NULL;
+ isc_result_t result;
+
+ result = ns_config_get(maps, name, &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ isc_quota_max(quota, cfg_obj_asuint32(obj));
+}
+
+/*
+ * This function is called as soon as the 'directory' statement has been
+ * parsed. This can be extended to support other options if necessary.
+ */
+static isc_result_t
+directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
+ isc_result_t result;
+ const char *directory;
+
+ REQUIRE(strcasecmp("directory", clausename) == 0);
+
+ UNUSED(arg);
+ UNUSED(clausename);
+
+ /*
+ * Change directory.
+ */
+ directory = cfg_obj_asstring(obj);
+
+ if (! isc_file_ischdiridempotent(directory))
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
+ "option 'directory' contains relative path '%s'",
+ directory);
+
+ result = isc_dir_chdir(directory);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "change directory to '%s' failed: %s",
+ directory, isc_result_totext(result));
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+scan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
+ isc_boolean_t match_mapped = server->aclenv.match_mapped;
+
+ ns_interfacemgr_scan(server->interfacemgr, verbose);
+ /*
+ * Update the "localhost" and "localnets" ACLs to match the
+ * current set of network interfaces.
+ */
+ dns_aclenv_copy(&server->aclenv,
+ ns_interfacemgr_getaclenv(server->interfacemgr));
+
+ server->aclenv.match_mapped = match_mapped;
+}
+
+static isc_result_t
+add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr) {
+ ns_listenelt_t *lelt = NULL;
+ dns_acl_t *src_acl = NULL;
+ dns_aclelement_t aelt;
+ isc_result_t result;
+ isc_sockaddr_t any_sa6;
+
+ REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
+
+ isc_sockaddr_any6(&any_sa6);
+ if (!isc_sockaddr_equal(&any_sa6, addr)) {
+ aelt.type = dns_aclelementtype_ipprefix;
+ aelt.negative = ISC_FALSE;
+ aelt.u.ip_prefix.prefixlen = 128;
+ isc_netaddr_fromin6(&aelt.u.ip_prefix.address,
+ &addr->type.sin6.sin6_addr);
+
+ result = dns_acl_create(mctx, 1, &src_acl);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_acl_appendelement(src_acl, &aelt);
+ if (result != ISC_R_SUCCESS)
+ goto clean;
+
+ result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr),
+ src_acl, &lelt);
+ if (result != ISC_R_SUCCESS)
+ goto clean;
+ ISC_LIST_APPEND(list->elts, lelt, link);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ clean:
+ INSIST(lelt == NULL);
+ dns_acl_detach(&src_acl);
+
+ return (result);
+}
+
+/*
+ * Make a list of xxx-source addresses and call ns_interfacemgr_adjust()
+ * to update the listening interfaces accordingly.
+ * We currently only consider IPv6, because this only affects IPv6 wildcard
+ * sockets.
+ */
+static void
+adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) {
+ isc_result_t result;
+ ns_listenlist_t *list = NULL;
+ dns_view_t *view;
+ dns_zone_t *zone, *next;
+ isc_sockaddr_t addr, *addrp;
+
+ result = ns_listenlist_create(mctx, &list);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ dns_dispatch_t *dispatch6;
+
+ dispatch6 = dns_resolver_dispatchv6(view->resolver);
+ if (dispatch6 == NULL)
+ continue;
+ result = dns_dispatch_getlocaladdress(dispatch6, &addr);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ result = add_listenelt(mctx, list, &addr);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+
+ zone = NULL;
+ for (result = dns_zone_first(server->zonemgr, &zone);
+ result == ISC_R_SUCCESS;
+ next = NULL, result = dns_zone_next(zone, &next), zone = next) {
+ dns_view_t *zoneview;
+
+ /*
+ * At this point the zone list may contain a stale zone
+ * just removed from the configuration. To see the validity,
+ * check if the corresponding view is in our current view list.
+ * There may also be old zones that are still in the process
+ * of shutting down and have detached from their old view
+ * (zoneview == NULL).
+ */
+ zoneview = dns_zone_getview(zone);
+ if (zoneview == NULL)
+ continue;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL && view != zoneview;
+ view = ISC_LIST_NEXT(view, link))
+ ;
+ if (view == NULL)
+ continue;
+
+ addrp = dns_zone_getnotifysrc6(zone);
+ result = add_listenelt(mctx, list, addrp);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ addrp = dns_zone_getxfrsource6(zone);
+ result = add_listenelt(mctx, list, addrp);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+
+ ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE);
+
+ clean:
+ ns_listenlist_detach(&list);
+ return;
+
+ fail:
+ /*
+ * Even when we failed the procedure, most of other interfaces
+ * should work correctly. We therefore just warn it.
+ */
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "could not adjust the listen-on list; "
+ "some interfaces may not work");
+ goto clean;
+}
+
+/*
+ * This event callback is invoked to do periodic network
+ * interface scanning.
+ */
+static void
+interface_timer_tick(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ ns_server_t *server = (ns_server_t *) event->ev_arg;
+ INSIST(task == server->task);
+ UNUSED(task);
+ isc_event_free(&event);
+ /*
+ * XXX should scan interfaces unlocked and get exclusive access
+ * only to replace ACLs.
+ */
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ scan_interfaces(server, ISC_FALSE);
+ isc_task_endexclusive(server->task);
+}
+
+static void
+heartbeat_timer_tick(isc_task_t *task, isc_event_t *event) {
+ ns_server_t *server = (ns_server_t *) event->ev_arg;
+ dns_view_t *view;
+
+ UNUSED(task);
+ isc_event_free(&event);
+ view = ISC_LIST_HEAD(server->viewlist);
+ while (view != NULL) {
+ dns_view_dialup(view);
+ view = ISC_LIST_NEXT(view, link);
+ }
+}
+
+static void
+pps_timer_tick(isc_task_t *task, isc_event_t *event) {
+ static unsigned int oldrequests = 0;
+ unsigned int requests = ns_client_requests;
+
+ UNUSED(task);
+ isc_event_free(&event);
+
+ /*
+ * Don't worry about wrapping as the overflow result will be right.
+ */
+ dns_pps = (requests - oldrequests) / 1200;
+ oldrequests = requests;
+}
+
+/*
+ * Replace the current value of '*field', a dynamically allocated
+ * string or NULL, with a dynamically allocated copy of the
+ * null-terminated string pointed to by 'value', or NULL.
+ */
+static isc_result_t
+setstring(ns_server_t *server, char **field, const char *value) {
+ char *copy;
+
+ if (value != NULL) {
+ copy = isc_mem_strdup(server->mctx, value);
+ if (copy == NULL)
+ return (ISC_R_NOMEMORY);
+ } else {
+ copy = NULL;
+ }
+
+ if (*field != NULL)
+ isc_mem_free(server->mctx, *field);
+
+ *field = copy;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Replace the current value of '*field', a dynamically allocated
+ * string or NULL, with another dynamically allocated string
+ * or NULL if whether 'obj' is a string or void value, respectively.
+ */
+static isc_result_t
+setoptstring(ns_server_t *server, char **field, const cfg_obj_t *obj) {
+ if (cfg_obj_isvoid(obj))
+ return (setstring(server, field, NULL));
+ else
+ return (setstring(server, field, cfg_obj_asstring(obj)));
+}
+
+static void
+set_limit(const cfg_obj_t **maps, const char *configname,
+ const char *description, isc_resource_t resourceid,
+ isc_resourcevalue_t defaultvalue)
+{
+ const cfg_obj_t *obj = NULL;
+ const char *resource;
+ isc_resourcevalue_t value;
+ isc_result_t result;
+
+ if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS)
+ return;
+
+ if (cfg_obj_isstring(obj)) {
+ resource = cfg_obj_asstring(obj);
+ if (strcasecmp(resource, "unlimited") == 0)
+ value = ISC_RESOURCE_UNLIMITED;
+ else {
+ INSIST(strcasecmp(resource, "default") == 0);
+ value = defaultvalue;
+ }
+ } else
+ value = cfg_obj_asuint64(obj);
+
+ result = isc_resource_setlimit(resourceid, value);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ result == ISC_R_SUCCESS ?
+ ISC_LOG_DEBUG(3) : ISC_LOG_WARNING,
+ "set maximum %s to %" ISC_PRINT_QUADFORMAT "d: %s",
+ description, value, isc_result_totext(result));
+}
+
+#define SETLIMIT(cfgvar, resource, description) \
+ set_limit(maps, cfgvar, description, isc_resource_ ## resource, \
+ ns_g_init ## resource)
+
+static void
+set_limits(const cfg_obj_t **maps) {
+ SETLIMIT("stacksize", stacksize, "stack size");
+ SETLIMIT("datasize", datasize, "data size");
+ SETLIMIT("coresize", coresize, "core size");
+ SETLIMIT("files", openfiles, "open files");
+}
+
+static isc_result_t
+portlist_fromconf(dns_portlist_t *portlist, unsigned int family,
+ const cfg_obj_t *ports)
+{
+ const cfg_listelt_t *element;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (element = cfg_list_first(ports);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ const cfg_obj_t *obj = cfg_listelt_value(element);
+ in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
+
+ result = dns_portlist_add(portlist, family, port);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ return (result);
+}
+
+static isc_result_t
+removed(dns_zone_t *zone, void *uap) {
+ const char *type;
+
+ if (dns_zone_getview(zone) != uap)
+ return (ISC_R_SUCCESS);
+
+ switch (dns_zone_gettype(zone)) {
+ case dns_zone_master:
+ type = "master";
+ break;
+ case dns_zone_slave:
+ type = "slave";
+ break;
+ case dns_zone_stub:
+ type = "stub";
+ break;
+ default:
+ type = "other";
+ break;
+ }
+ dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_configuration(const char *filename, ns_server_t *server,
+ isc_boolean_t first_time)
+{
+ isc_result_t result;
+ isc_interval_t interval;
+ cfg_parser_t *parser = NULL;
+ cfg_obj_t *config;
+ const cfg_obj_t *options;
+ const cfg_obj_t *views;
+ const cfg_obj_t *obj;
+ const cfg_obj_t *v4ports, *v6ports;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *builtin_views;
+ const cfg_listelt_t *element;
+ dns_view_t *view = NULL;
+ dns_view_t *view_next;
+ dns_viewlist_t viewlist;
+ dns_viewlist_t tmpviewlist;
+ cfg_aclconfctx_t aclconfctx;
+ isc_uint32_t interface_interval;
+ isc_uint32_t heartbeat_interval;
+ isc_uint32_t udpsize;
+ in_port_t listen_port;
+ int i;
+
+ cfg_aclconfctx_init(&aclconfctx);
+ ISC_LIST_INIT(viewlist);
+
+ /* Ensure exclusive access to configuration data. */
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * Parse the global default pseudo-config file.
+ */
+ if (first_time) {
+ CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config));
+ RUNTIME_CHECK(cfg_map_get(ns_g_config, "options",
+ &ns_g_defaults) ==
+ ISC_R_SUCCESS);
+ }
+
+ /*
+ * Parse the configuration file using the new config code.
+ */
+ result = ISC_R_FAILURE;
+ config = NULL;
+
+ /*
+ * Unless this is lwresd with the -C option, parse the config file.
+ */
+ if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "loading configuration from '%s'",
+ filename);
+ CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &parser));
+ cfg_parser_setcallback(parser, directory_callback, NULL);
+ result = cfg_parse_file(parser, filename, &cfg_type_namedconf,
+ &config);
+ }
+
+ /*
+ * If this is lwresd with the -C option, or lwresd with no -C or -c
+ * option where the above parsing failed, parse resolv.conf.
+ */
+ if (ns_g_lwresdonly &&
+ (lwresd_g_useresolvconf ||
+ (!ns_g_conffileset && result == ISC_R_FILENOTFOUND)))
+ {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "loading configuration from '%s'",
+ lwresd_g_resolvconffile);
+ if (parser != NULL)
+ cfg_parser_destroy(&parser);
+ CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &parser));
+ result = ns_lwresd_parseeresolvconf(ns_g_mctx, parser,
+ &config);
+ }
+ CHECK(result);
+
+ /*
+ * Check the validity of the configuration.
+ */
+ CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx));
+
+ /*
+ * Fill in the maps array, used for resolving defaults.
+ */
+ i = 0;
+ options = NULL;
+ result = cfg_map_get(config, "options", &options);
+ if (result == ISC_R_SUCCESS)
+ maps[i++] = options;
+ maps[i++] = ns_g_defaults;
+ maps[i++] = NULL;
+
+ /*
+ * Set process limits, which (usually) needs to be done as root.
+ */
+ set_limits(maps);
+
+ /*
+ * Configure various server options.
+ */
+ configure_server_quota(maps, "transfers-out", &server->xfroutquota);
+ configure_server_quota(maps, "tcp-clients", &server->tcpquota);
+ configure_server_quota(maps, "recursive-clients",
+ &server->recursionquota);
+ if (server->recursionquota.max > 1000)
+ isc_quota_soft(&server->recursionquota,
+ server->recursionquota.max - 100);
+ else
+ isc_quota_soft(&server->recursionquota, 0);
+
+ CHECK(configure_view_acl(NULL, config, "blackhole", &aclconfctx,
+ ns_g_mctx, &server->blackholeacl));
+ if (server->blackholeacl != NULL)
+ dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
+ server->blackholeacl);
+
+ obj = NULL;
+ result = ns_config_get(maps, "match-mapped-addresses", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ server->aclenv.match_mapped = cfg_obj_asboolean(obj);
+
+ v4ports = NULL;
+ v6ports = NULL;
+ (void)ns_config_get(maps, "avoid-v4-udp-ports", &v4ports);
+ (void)ns_config_get(maps, "avoid-v6-udp-ports", &v6ports);
+ if (v4ports != NULL || v6ports != NULL) {
+ dns_portlist_t *portlist = NULL;
+ result = dns_portlist_create(ns_g_mctx, &portlist);
+ if (result == ISC_R_SUCCESS && v4ports != NULL)
+ result = portlist_fromconf(portlist, AF_INET, v4ports);
+ if (result == ISC_R_SUCCESS && v6ports != NULL)
+ portlist_fromconf(portlist, AF_INET6, v6ports);
+ if (result == ISC_R_SUCCESS)
+ dns_dispatchmgr_setblackportlist(ns_g_dispatchmgr, portlist);
+ if (portlist != NULL)
+ dns_portlist_detach(&portlist);
+ CHECK(result);
+ } else
+ dns_dispatchmgr_setblackportlist(ns_g_dispatchmgr, NULL);
+
+ /*
+ * Set the EDNS UDP size when we don't match a view.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "edns-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ ns_g_udpsize = (isc_uint16_t)udpsize;
+
+ /*
+ * Configure the zone manager.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "transfers-in", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "transfers-per-ns", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "serial-query-rate", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj));
+
+ /*
+ * Determine which port to use for listening for incoming connections.
+ */
+ if (ns_g_port != 0)
+ listen_port = ns_g_port;
+ else
+ CHECKM(ns_config_getport(config, &listen_port), "port");
+
+ /*
+ * Find the listen queue depth.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "tcp-listen-queue", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ ns_g_listen = cfg_obj_asuint32(obj);
+ if (ns_g_listen < 3)
+ ns_g_listen = 3;
+
+ /*
+ * Configure the interface manager according to the "listen-on"
+ * statement.
+ */
+ {
+ const cfg_obj_t *clistenon = NULL;
+ ns_listenlist_t *listenon = NULL;
+
+ clistenon = NULL;
+ /*
+ * Even though listen-on is present in the default
+ * configuration, we can't use it here, since it isn't
+ * used if we're in lwresd mode. This way is easier.
+ */
+ if (options != NULL)
+ (void)cfg_map_get(options, "listen-on", &clistenon);
+ if (clistenon != NULL) {
+ result = ns_listenlist_fromconfig(clistenon,
+ config,
+ &aclconfctx,
+ ns_g_mctx,
+ &listenon);
+ } else if (!ns_g_lwresdonly) {
+ /*
+ * Not specified, use default.
+ */
+ CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
+ ISC_TRUE, &listenon));
+ }
+ if (listenon != NULL) {
+ ns_interfacemgr_setlistenon4(server->interfacemgr,
+ listenon);
+ ns_listenlist_detach(&listenon);
+ }
+ }
+ /*
+ * Ditto for IPv6.
+ */
+ {
+ const cfg_obj_t *clistenon = NULL;
+ ns_listenlist_t *listenon = NULL;
+
+ if (options != NULL)
+ (void)cfg_map_get(options, "listen-on-v6", &clistenon);
+ if (clistenon != NULL) {
+ result = ns_listenlist_fromconfig(clistenon,
+ config,
+ &aclconfctx,
+ ns_g_mctx,
+ &listenon);
+ } else if (!ns_g_lwresdonly) {
+ /*
+ * Not specified, use default.
+ */
+ CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
+ ISC_FALSE, &listenon));
+ }
+ if (listenon != NULL) {
+ ns_interfacemgr_setlistenon6(server->interfacemgr,
+ listenon);
+ ns_listenlist_detach(&listenon);
+ }
+ }
+
+ /*
+ * Rescan the interface list to pick up changes in the
+ * listen-on option. It's important that we do this before we try
+ * to configure the query source, since the dispatcher we use might
+ * be shared with an interface.
+ */
+ scan_interfaces(server, ISC_TRUE);
+
+ /*
+ * Arrange for further interface scanning to occur periodically
+ * as specified by the "interface-interval" option.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "interface-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ interface_interval = cfg_obj_asuint32(obj) * 60;
+ if (interface_interval == 0) {
+ CHECK(isc_timer_reset(server->interface_timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE));
+ } else if (server->interface_interval != interface_interval) {
+ isc_interval_set(&interval, interface_interval, 0);
+ CHECK(isc_timer_reset(server->interface_timer,
+ isc_timertype_ticker,
+ NULL, &interval, ISC_FALSE));
+ }
+ server->interface_interval = interface_interval;
+
+ /*
+ * Configure the dialup heartbeat timer.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "heartbeat-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ heartbeat_interval = cfg_obj_asuint32(obj) * 60;
+ if (heartbeat_interval == 0) {
+ CHECK(isc_timer_reset(server->heartbeat_timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE));
+ } else if (server->heartbeat_interval != heartbeat_interval) {
+ isc_interval_set(&interval, heartbeat_interval, 0);
+ CHECK(isc_timer_reset(server->heartbeat_timer,
+ isc_timertype_ticker,
+ NULL, &interval, ISC_FALSE));
+ }
+ server->heartbeat_interval = heartbeat_interval;
+
+ isc_interval_set(&interval, 1200, 0);
+ CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL,
+ &interval, ISC_FALSE));
+
+ /*
+ * Configure and freeze all explicit views. Explicit
+ * views that have zones were already created at parsing
+ * time, but views with no zones must be created here.
+ */
+ views = NULL;
+ (void)cfg_map_get(config, "view", &views);
+ for (element = cfg_list_first(views);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *vconfig = cfg_listelt_value(element);
+ view = NULL;
+
+ CHECK(create_view(vconfig, &viewlist, &view));
+ INSIST(view != NULL);
+ CHECK(configure_view(view, config, vconfig,
+ ns_g_mctx, &aclconfctx, ISC_TRUE));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Make sure we have a default view if and only if there
+ * were no explicit views.
+ */
+ if (views == NULL) {
+ /*
+ * No explicit views; there ought to be a default view.
+ * There may already be one created as a side effect
+ * of zone statements, or we may have to create one.
+ * In either case, we need to configure and freeze it.
+ */
+ CHECK(create_view(NULL, &viewlist, &view));
+ CHECK(configure_view(view, config, NULL, ns_g_mctx,
+ &aclconfctx, ISC_TRUE));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Create (or recreate) the built-in views. Currently
+ * there is only one, the _bind view.
+ */
+ builtin_views = NULL;
+ RUNTIME_CHECK(cfg_map_get(ns_g_config, "view",
+ &builtin_views) == ISC_R_SUCCESS);
+ for (element = cfg_list_first(builtin_views);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *vconfig = cfg_listelt_value(element);
+ CHECK(create_view(vconfig, &viewlist, &view));
+ CHECK(configure_view(view, config, vconfig, ns_g_mctx,
+ &aclconfctx, ISC_FALSE));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ view = NULL;
+ }
+
+ /*
+ * Swap our new view list with the production one.
+ */
+ tmpviewlist = server->viewlist;
+ server->viewlist = viewlist;
+ viewlist = tmpviewlist;
+
+ /*
+ * Load the TKEY information from the configuration.
+ */
+ if (options != NULL) {
+ dns_tkeyctx_t *t = NULL;
+ CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy,
+ &t),
+ "configuring TKEY");
+ if (server->tkeyctx != NULL)
+ dns_tkeyctx_destroy(&server->tkeyctx);
+ server->tkeyctx = t;
+ }
+
+ /*
+ * Bind the control port(s).
+ */
+ CHECKM(ns_controls_configure(ns_g_server->controls, config,
+ &aclconfctx),
+ "binding control channel(s)");
+
+ /*
+ * Bind the lwresd port(s).
+ */
+ CHECKM(ns_lwresd_configure(ns_g_mctx, config),
+ "binding lightweight resolver ports");
+
+ /*
+ * Open the source of entropy.
+ */
+ if (first_time) {
+ obj = NULL;
+ result = ns_config_get(maps, "random-device", &obj);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "no source of entropy found");
+ } else {
+ const char *randomdev = cfg_obj_asstring(obj);
+ result = isc_entropy_createfilesource(ns_g_entropy,
+ randomdev);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO,
+ "could not open entropy source "
+ "%s: %s",
+ randomdev,
+ isc_result_totext(result));
+#ifdef PATH_RANDOMDEV
+ if (ns_g_fallbackentropy != NULL) {
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO,
+ "using pre-chroot entropy source "
+ "%s",
+ PATH_RANDOMDEV);
+ isc_entropy_detach(&ns_g_entropy);
+ isc_entropy_attach(ns_g_fallbackentropy,
+ &ns_g_entropy);
+ }
+ isc_entropy_detach(&ns_g_fallbackentropy);
+ }
+#endif
+ }
+ }
+
+ /*
+ * Relinquish root privileges.
+ */
+ if (first_time)
+ ns_os_changeuser();
+
+ /*
+ * Configure the logging system.
+ *
+ * Do this after changing UID to make sure that any log
+ * files specified in named.conf get created by the
+ * unprivileged user, not root.
+ */
+ if (ns_g_logstderr) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "ignoring config file logging "
+ "statement due to -g option");
+ } else {
+ const cfg_obj_t *logobj = NULL;
+ isc_logconfig_t *logc = NULL;
+
+ CHECKM(isc_logconfig_create(ns_g_lctx, &logc),
+ "creating new logging configuration");
+
+ logobj = NULL;
+ (void)cfg_map_get(config, "logging", &logobj);
+ if (logobj != NULL) {
+ CHECKM(ns_log_configure(logc, logobj),
+ "configuring logging");
+ } else {
+ CHECKM(ns_log_setdefaultchannels(logc),
+ "setting up default logging channels");
+ CHECKM(ns_log_setunmatchedcategory(logc),
+ "setting up default 'category unmatched'");
+ CHECKM(ns_log_setdefaultcategory(logc),
+ "setting up default 'category default'");
+ }
+
+ result = isc_logconfig_use(ns_g_lctx, logc);
+ if (result != ISC_R_SUCCESS) {
+ isc_logconfig_destroy(&logc);
+ CHECKM(result, "installing logging configuration");
+ }
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "now using logging configuration from "
+ "config file");
+ }
+
+ /*
+ * Set the default value of the query logging flag depending
+ * whether a "queries" category has been defined. This is
+ * a disgusting hack, but we need to do this for BIND 8
+ * compatibility.
+ */
+ if (first_time) {
+ const cfg_obj_t *logobj = NULL;
+ const cfg_obj_t *categories = NULL;
+
+ obj = NULL;
+ if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) {
+ server->log_queries = cfg_obj_asboolean(obj);
+ } else {
+
+ (void)cfg_map_get(config, "logging", &logobj);
+ if (logobj != NULL)
+ (void)cfg_map_get(logobj, "category",
+ &categories);
+ if (categories != NULL) {
+ const cfg_listelt_t *element;
+ for (element = cfg_list_first(categories);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *catobj;
+ const char *str;
+
+ obj = cfg_listelt_value(element);
+ catobj = cfg_tuple_get(obj, "name");
+ str = cfg_obj_asstring(catobj);
+ if (strcasecmp(str, "queries") == 0)
+ server->log_queries = ISC_TRUE;
+ }
+ }
+ }
+ }
+
+ obj = NULL;
+ if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS)
+ if (cfg_obj_isvoid(obj))
+ ns_os_writepidfile(NULL, first_time);
+ else
+ ns_os_writepidfile(cfg_obj_asstring(obj), first_time);
+ else if (ns_g_lwresdonly)
+ ns_os_writepidfile(lwresd_g_defaultpidfile, first_time);
+ else
+ ns_os_writepidfile(ns_g_defaultpidfile, first_time);
+
+ obj = NULL;
+ if (options != NULL &&
+ cfg_map_get(options, "memstatistics-file", &obj) == ISC_R_SUCCESS)
+ ns_main_setmemstats(cfg_obj_asstring(obj));
+ else
+ ns_main_setmemstats(NULL);
+
+ obj = NULL;
+ result = ns_config_get(maps, "statistics-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = ns_config_get(maps, "dump-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = ns_config_get(maps, "recursing-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = ns_config_get(maps, "version", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECKM(setoptstring(server, &server->version, obj), "strdup");
+ server->version_set = ISC_TRUE;
+ } else {
+ server->version_set = ISC_FALSE;
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "hostname", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECKM(setoptstring(server, &server->hostname, obj), "strdup");
+ server->hostname_set = ISC_TRUE;
+ } else {
+ server->hostname_set = ISC_FALSE;
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "server-id", &obj);
+ server->server_usehostname = ISC_FALSE;
+ if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
+ server->server_usehostname = ISC_TRUE;
+ } else if (result == ISC_R_SUCCESS) {
+ CHECKM(setoptstring(server, &server->server_id, obj), "strdup");
+ } else {
+ result = setstring(server, &server->server_id, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "flush-zones-on-shutdown", &obj);
+ if (result == ISC_R_SUCCESS) {
+ server->flushonshutdown = cfg_obj_asboolean(obj);
+ } else {
+ server->flushonshutdown = ISC_FALSE;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ cfg_aclconfctx_destroy(&aclconfctx);
+
+ if (parser != NULL) {
+ if (config != NULL)
+ cfg_obj_destroy(parser, &config);
+ cfg_parser_destroy(&parser);
+ }
+
+ if (view != NULL)
+ dns_view_detach(&view);
+
+ /*
+ * This cleans up either the old production view list
+ * or our temporary list depending on whether they
+ * were swapped above or not.
+ */
+ for (view = ISC_LIST_HEAD(viewlist);
+ view != NULL;
+ view = view_next) {
+ view_next = ISC_LIST_NEXT(view, link);
+ ISC_LIST_UNLINK(viewlist, view, link);
+ if (result == ISC_R_SUCCESS &&
+ strcmp(view->name, "_bind") != 0)
+ (void)dns_zt_apply(view->zonetable, ISC_FALSE,
+ removed, view);
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Adjust the listening interfaces in accordance with the source
+ * addresses specified in views and zones.
+ */
+ if (isc_net_probeipv6() == ISC_R_SUCCESS)
+ adjust_interfaces(server, ns_g_mctx);
+
+ /* Relinquish exclusive access to configuration data. */
+ isc_task_endexclusive(server->task);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_DEBUG(1), "load_configuration: %s",
+ isc_result_totext(result));
+
+ return (result);
+}
+
+static isc_result_t
+load_zones(ns_server_t *server, isc_boolean_t stop) {
+ isc_result_t result;
+ dns_view_t *view;
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * Load zone data from disk.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ CHECK(dns_view_load(view, stop));
+ }
+
+ /*
+ * Force zone maintenance. Do this after loading
+ * so that we know when we need to force AXFR of
+ * slave zones whose master files are missing.
+ */
+ CHECK(dns_zonemgr_forcemaint(server->zonemgr));
+ cleanup:
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+static isc_result_t
+load_new_zones(ns_server_t *server, isc_boolean_t stop) {
+ isc_result_t result;
+ dns_view_t *view;
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * Load zone data from disk.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ CHECK(dns_view_loadnew(view, stop));
+ }
+ /*
+ * Force zone maintenance. Do this after loading
+ * so that we know when we need to force AXFR of
+ * slave zones whose master files are missing.
+ */
+ dns_zonemgr_resumexfrs(server->zonemgr);
+ cleanup:
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+static void
+run_server(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ ns_server_t *server = (ns_server_t *)event->ev_arg;
+
+ INSIST(task == server->task);
+
+ isc_event_free(&event);
+
+ CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy,
+ &ns_g_dispatchmgr),
+ "creating dispatch manager");
+
+ CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr,
+ ns_g_socketmgr, ns_g_dispatchmgr,
+ &server->interfacemgr),
+ "creating interface manager");
+
+ CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task,
+ interface_timer_tick,
+ server, &server->interface_timer),
+ "creating interface timer");
+
+ CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task,
+ heartbeat_timer_tick,
+ server, &server->heartbeat_timer),
+ "creating heartbeat timer");
+
+ CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task, pps_timer_tick,
+ server, &server->pps_timer),
+ "creating pps timer");
+
+ CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser),
+ "creating default configuration parser");
+
+ if (ns_g_lwresdonly)
+ CHECKFATAL(load_configuration(lwresd_g_conffile, server,
+ ISC_TRUE),
+ "loading configuration");
+ else
+ CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE),
+ "loading configuration");
+
+ isc_hash_init();
+
+ CHECKFATAL(load_zones(server, ISC_FALSE), "loading zones");
+
+ ns_os_started();
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_NOTICE, "running");
+}
+
+void
+ns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) {
+
+ REQUIRE(NS_SERVER_VALID(server));
+
+ server->flushonshutdown = flush;
+}
+
+static void
+shutdown_server(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ dns_view_t *view, *view_next;
+ ns_server_t *server = (ns_server_t *)event->ev_arg;
+ isc_boolean_t flush = server->flushonshutdown;
+
+ UNUSED(task);
+ INSIST(task == server->task);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "shutting down%s",
+ flush ? ": flushing changes" : "");
+
+ ns_controls_shutdown(server->controls);
+ end_reserved_dispatches(server, ISC_TRUE);
+
+ cfg_obj_destroy(ns_g_parser, &ns_g_config);
+ cfg_parser_destroy(&ns_g_parser);
+
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = view_next) {
+ view_next = ISC_LIST_NEXT(view, link);
+ ISC_LIST_UNLINK(server->viewlist, view, link);
+ if (flush)
+ dns_view_flushanddetach(&view);
+ else
+ dns_view_detach(&view);
+ }
+
+ isc_timer_detach(&server->interface_timer);
+ isc_timer_detach(&server->heartbeat_timer);
+ isc_timer_detach(&server->pps_timer);
+
+ ns_interfacemgr_shutdown(server->interfacemgr);
+ ns_interfacemgr_detach(&server->interfacemgr);
+
+ dns_dispatchmgr_destroy(&ns_g_dispatchmgr);
+
+ dns_zonemgr_shutdown(server->zonemgr);
+
+ if (server->blackholeacl != NULL)
+ dns_acl_detach(&server->blackholeacl);
+
+ dns_db_detach(&server->in_roothints);
+
+ isc_task_endexclusive(server->task);
+
+ isc_task_detach(&server->task);
+
+ isc_event_free(&event);
+}
+
+void
+ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
+ isc_result_t result;
+
+ ns_server_t *server = isc_mem_get(mctx, sizeof(*server));
+ if (server == NULL)
+ fatal("allocating server object", ISC_R_NOMEMORY);
+
+ server->mctx = mctx;
+ server->task = NULL;
+
+ /* Initialize configuration data with default values. */
+
+ result = isc_quota_init(&server->xfroutquota, 10);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = isc_quota_init(&server->tcpquota, 10);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = isc_quota_init(&server->recursionquota, 100);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ result = dns_aclenv_init(mctx, &server->aclenv);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /* Initialize server data structures. */
+ server->zonemgr = NULL;
+ server->interfacemgr = NULL;
+ ISC_LIST_INIT(server->viewlist);
+ server->in_roothints = NULL;
+ server->blackholeacl = NULL;
+
+ CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
+ &server->in_roothints),
+ "setting up root hints");
+
+ CHECKFATAL(isc_mutex_init(&server->reload_event_lock),
+ "initializing reload event lock");
+ server->reload_event =
+ isc_event_allocate(ns_g_mctx, server,
+ NS_EVENT_RELOAD,
+ ns_server_reload,
+ server,
+ sizeof(isc_event_t));
+ CHECKFATAL(server->reload_event == NULL ?
+ ISC_R_NOMEMORY : ISC_R_SUCCESS,
+ "allocating reload event");
+
+ CHECKFATAL(dst_lib_init(ns_g_mctx, ns_g_entropy, ISC_ENTROPY_GOODONLY),
+ "initializing DST");
+
+ server->tkeyctx = NULL;
+ CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy,
+ &server->tkeyctx),
+ "creating TKEY context");
+
+ /*
+ * Setup the server task, which is responsible for coordinating
+ * startup and shutdown of the server.
+ */
+ CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task),
+ "creating server task");
+ isc_task_setname(server->task, "server", server);
+ CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server),
+ "isc_task_onshutdown");
+ CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),
+ "isc_app_onrun");
+
+ server->interface_timer = NULL;
+ server->heartbeat_timer = NULL;
+ server->pps_timer = NULL;
+
+ server->interface_interval = 0;
+ server->heartbeat_interval = 0;
+
+ CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr,
+ ns_g_socketmgr, &server->zonemgr),
+ "dns_zonemgr_create");
+
+ server->statsfile = isc_mem_strdup(server->mctx, "named.stats");
+ CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
+ "isc_mem_strdup");
+ server->querystats = NULL;
+
+ server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db");
+ CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
+ "isc_mem_strdup");
+
+ server->recfile = isc_mem_strdup(server->mctx, "named.recursing");
+ CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
+ "isc_mem_strdup");
+
+ server->hostname_set = ISC_FALSE;
+ server->hostname = NULL;
+ server->version_set = ISC_FALSE;
+ server->version = NULL;
+ server->server_usehostname = ISC_FALSE;
+ server->server_id = NULL;
+
+ CHECKFATAL(dns_stats_alloccounters(ns_g_mctx, &server->querystats),
+ "dns_stats_alloccounters");
+
+ server->flushonshutdown = ISC_FALSE;
+ server->log_queries = ISC_FALSE;
+
+ server->controls = NULL;
+ CHECKFATAL(ns_controls_create(server, &server->controls),
+ "ns_controls_create");
+ server->dispatchgen = 0;
+ ISC_LIST_INIT(server->dispatches);
+
+ server->magic = NS_SERVER_MAGIC;
+ *serverp = server;
+}
+
+void
+ns_server_destroy(ns_server_t **serverp) {
+ ns_server_t *server = *serverp;
+ REQUIRE(NS_SERVER_VALID(server));
+
+ ns_controls_destroy(&server->controls);
+
+ dns_stats_freecounters(server->mctx, &server->querystats);
+
+ isc_mem_free(server->mctx, server->statsfile);
+ isc_mem_free(server->mctx, server->dumpfile);
+ isc_mem_free(server->mctx, server->recfile);
+
+ if (server->version != NULL)
+ isc_mem_free(server->mctx, server->version);
+ if (server->hostname != NULL)
+ isc_mem_free(server->mctx, server->hostname);
+ if (server->server_id != NULL)
+ isc_mem_free(server->mctx, server->server_id);
+
+ dns_zonemgr_detach(&server->zonemgr);
+
+ if (server->tkeyctx != NULL)
+ dns_tkeyctx_destroy(&server->tkeyctx);
+
+ dst_lib_destroy();
+
+ isc_event_free(&server->reload_event);
+
+ INSIST(ISC_LIST_EMPTY(server->viewlist));
+
+ dns_aclenv_destroy(&server->aclenv);
+
+ isc_quota_destroy(&server->recursionquota);
+ isc_quota_destroy(&server->tcpquota);
+ isc_quota_destroy(&server->xfroutquota);
+
+ server->magic = 0;
+ isc_mem_put(server->mctx, server, sizeof(*server));
+ *serverp = NULL;
+}
+
+static void
+fatal(const char *msg, isc_result_t result) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_CRITICAL, "%s: %s", msg,
+ isc_result_totext(result));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_CRITICAL, "exiting (due to fatal error)");
+ exit(1);
+}
+
+static void
+start_reserved_dispatches(ns_server_t *server) {
+
+ REQUIRE(NS_SERVER_VALID(server));
+
+ server->dispatchgen++;
+}
+
+static void
+end_reserved_dispatches(ns_server_t *server, isc_boolean_t all) {
+ ns_dispatch_t *dispatch, *nextdispatch;
+
+ REQUIRE(NS_SERVER_VALID(server));
+
+ for (dispatch = ISC_LIST_HEAD(server->dispatches);
+ dispatch != NULL;
+ dispatch = nextdispatch) {
+ nextdispatch = ISC_LIST_NEXT(dispatch, link);
+ if (!all && server->dispatchgen == dispatch-> dispatchgen)
+ continue;
+ ISC_LIST_UNLINK(server->dispatches, dispatch, link);
+ dns_dispatch_detach(&dispatch->dispatch);
+ isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
+ }
+}
+
+void
+ns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr) {
+ ns_dispatch_t *dispatch;
+ in_port_t port;
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+ isc_result_t result;
+ unsigned int attrs, attrmask;
+
+ REQUIRE(NS_SERVER_VALID(server));
+
+ port = isc_sockaddr_getport(addr);
+ if (port == 0 || port >= 1024)
+ return;
+
+ for (dispatch = ISC_LIST_HEAD(server->dispatches);
+ dispatch != NULL;
+ dispatch = ISC_LIST_NEXT(dispatch, link)) {
+ if (isc_sockaddr_equal(&dispatch->addr, addr))
+ break;
+ }
+ if (dispatch != NULL) {
+ dispatch->dispatchgen = server->dispatchgen;
+ return;
+ }
+
+ dispatch = isc_mem_get(server->mctx, sizeof(*dispatch));
+ if (dispatch == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ dispatch->addr = *addr;
+ dispatch->dispatchgen = server->dispatchgen;
+ dispatch->dispatch = NULL;
+
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_UDP;
+ switch (isc_sockaddr_pf(addr)) {
+ case AF_INET:
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ break;
+ case AF_INET6:
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup;
+ }
+ attrmask = 0;
+ attrmask |= DNS_DISPATCHATTR_UDP;
+ attrmask |= DNS_DISPATCHATTR_TCP;
+ attrmask |= DNS_DISPATCHATTR_IPV4;
+ attrmask |= DNS_DISPATCHATTR_IPV6;
+
+ result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
+ ns_g_taskmgr, &dispatch->addr, 4096,
+ 1000, 32768, 16411, 16433,
+ attrs, attrmask, &dispatch->dispatch);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link);
+
+ return;
+
+ cleanup:
+ if (dispatch != NULL)
+ isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
+ isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "unable to create dispatch for reserved port %s: %s",
+ addrbuf, isc_result_totext(result));
+}
+
+
+static isc_result_t
+loadconfig(ns_server_t *server) {
+ isc_result_t result;
+ start_reserved_dispatches(server);
+ result = load_configuration(ns_g_lwresdonly ?
+ lwresd_g_conffile : ns_g_conffile,
+ server, ISC_FALSE);
+ if (result == ISC_R_SUCCESS)
+ end_reserved_dispatches(server, ISC_FALSE);
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "reloading configuration failed: %s",
+ isc_result_totext(result));
+ return (result);
+}
+
+static isc_result_t
+reload(ns_server_t *server) {
+ isc_result_t result;
+ CHECK(loadconfig(server));
+
+ result = load_zones(server, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "reloading zones failed: %s",
+ isc_result_totext(result));
+ }
+ cleanup:
+ return (result);
+}
+
+static void
+reconfig(ns_server_t *server) {
+ isc_result_t result;
+ CHECK(loadconfig(server));
+
+ result = load_new_zones(server, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "loading new zones failed: %s",
+ isc_result_totext(result));
+ }
+ cleanup: ;
+}
+
+/*
+ * Handle a reload event (from SIGHUP).
+ */
+static void
+ns_server_reload(isc_task_t *task, isc_event_t *event) {
+ ns_server_t *server = (ns_server_t *)event->ev_arg;
+
+ INSIST(task = server->task);
+ UNUSED(task);
+
+ (void)reload(server);
+
+ LOCK(&server->reload_event_lock);
+ INSIST(server->reload_event == NULL);
+ server->reload_event = event;
+ UNLOCK(&server->reload_event_lock);
+}
+
+void
+ns_server_reloadwanted(ns_server_t *server) {
+ LOCK(&server->reload_event_lock);
+ if (server->reload_event != NULL)
+ isc_task_send(server->task, &server->reload_event);
+ UNLOCK(&server->reload_event_lock);
+}
+
+static char *
+next_token(char **stringp, const char *delim) {
+ char *res;
+
+ do {
+ res = strsep(stringp, delim);
+ if (res == NULL)
+ break;
+ } while (*res == '\0');
+ return (res);
+}
+
+/*
+ * Find the zone specified in the control channel command 'args',
+ * if any. If a zone is specified, point '*zonep' at it, otherwise
+ * set '*zonep' to NULL.
+ */
+static isc_result_t
+zone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep) {
+ char *input, *ptr;
+ const char *zonetxt;
+ char *classtxt;
+ const char *viewtxt = NULL;
+ dns_fixedname_t name;
+ isc_result_t result;
+ isc_buffer_t buf;
+ dns_view_t *view = NULL;
+ dns_rdataclass_t rdclass;
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ input = args;
+
+ /* Skip the command name. */
+ ptr = next_token(&input, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Look for the zone name. */
+ zonetxt = next_token(&input, " \t");
+ if (zonetxt == NULL)
+ return (ISC_R_SUCCESS);
+
+ /* Look for the optional class name. */
+ classtxt = next_token(&input, " \t");
+ if (classtxt != NULL) {
+ /* Look for the optional view name. */
+ viewtxt = next_token(&input, " \t");
+ }
+
+ isc_buffer_init(&buf, zonetxt, strlen(zonetxt));
+ isc_buffer_add(&buf, strlen(zonetxt));
+ dns_fixedname_init(&name);
+ result = dns_name_fromtext(dns_fixedname_name(&name),
+ &buf, dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto fail1;
+
+ if (classtxt != NULL) {
+ isc_textregion_t r;
+ r.base = classtxt;
+ r.length = strlen(classtxt);
+ result = dns_rdataclass_fromtext(&rdclass, &r);
+ if (result != ISC_R_SUCCESS)
+ goto fail1;
+ } else {
+ rdclass = dns_rdataclass_in;
+ }
+
+ if (viewtxt == NULL)
+ viewtxt = "_default";
+ result = dns_viewlist_find(&server->viewlist, viewtxt,
+ rdclass, &view);
+ if (result != ISC_R_SUCCESS)
+ goto fail1;
+
+ result = dns_zt_find(view->zonetable, dns_fixedname_name(&name),
+ 0, NULL, zonep);
+ /* Partial match? */
+ if (result != ISC_R_SUCCESS && *zonep != NULL)
+ dns_zone_detach(zonep);
+ dns_view_detach(&view);
+ fail1:
+ return (result);
+}
+
+/*
+ * Act on a "retransfer" command from the command channel.
+ */
+isc_result_t
+ns_server_retransfercommand(ns_server_t *server, char *args) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_zonetype_t type;
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_slave || type == dns_zone_stub)
+ dns_zone_forcereload(zone);
+ else
+ result = ISC_R_NOTFOUND;
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+/*
+ * Act on a "reload" command from the command channel.
+ */
+isc_result_t
+ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_zonetype_t type;
+ const char *msg = NULL;
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL) {
+ result = reload(server);
+ if (result == ISC_R_SUCCESS)
+ msg = "server reload successful";
+ } else {
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_slave || type == dns_zone_stub) {
+ dns_zone_refresh(zone);
+ dns_zone_detach(&zone);
+ msg = "zone refresh queued";
+ } else {
+ result = dns_zone_load(zone);
+ dns_zone_detach(&zone);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ msg = "zone reload successful";
+ break;
+ case DNS_R_CONTINUE:
+ msg = "zone reload queued";
+ result = ISC_R_SUCCESS;
+ break;
+ case DNS_R_UPTODATE:
+ msg = "zone reload up-to-date";
+ result = ISC_R_SUCCESS;
+ break;
+ default:
+ /* failure message will be generated by rndc */
+ break;
+ }
+ }
+ }
+ if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
+ isc_buffer_putmem(text, (const unsigned char *)msg,
+ strlen(msg) + 1);
+ return (result);
+}
+
+/*
+ * Act on a "reconfig" command from the command channel.
+ */
+isc_result_t
+ns_server_reconfigcommand(ns_server_t *server, char *args) {
+ UNUSED(args);
+
+ reconfig(server);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Act on a "notify" command from the command channel.
+ */
+isc_result_t
+ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ const unsigned char msg[] = "zone notify queued";
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ dns_zone_notify(zone);
+ dns_zone_detach(&zone);
+ if (sizeof(msg) <= isc_buffer_availablelength(text))
+ isc_buffer_putmem(text, msg, sizeof(msg));
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Act on a "refresh" command from the command channel.
+ */
+isc_result_t
+ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ const unsigned char msg1[] = "zone refresh queued";
+ const unsigned char msg2[] = "not a slave or stub zone";
+ dns_zonetype_t type;
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_slave || type == dns_zone_stub) {
+ dns_zone_refresh(zone);
+ dns_zone_detach(&zone);
+ if (sizeof(msg1) <= isc_buffer_availablelength(text))
+ isc_buffer_putmem(text, msg1, sizeof(msg1));
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_zone_detach(&zone);
+ if (sizeof(msg2) <= isc_buffer_availablelength(text))
+ isc_buffer_putmem(text, msg2, sizeof(msg2));
+ return (ISC_R_FAILURE);
+}
+
+isc_result_t
+ns_server_togglequerylog(ns_server_t *server) {
+ server->log_queries = server->log_queries ? ISC_FALSE : ISC_TRUE;
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "query logging is now %s",
+ server->log_queries ? "on" : "off");
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, ns_listenlist_t **target)
+{
+ isc_result_t result;
+ const cfg_listelt_t *element;
+ ns_listenlist_t *dlist = NULL;
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ result = ns_listenlist_create(mctx, &dlist);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ for (element = cfg_list_first(listenlist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ ns_listenelt_t *delt = NULL;
+ const cfg_obj_t *listener = cfg_listelt_value(element);
+ result = ns_listenelt_fromconfig(listener, config, actx,
+ mctx, &delt);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ISC_LIST_APPEND(dlist->elts, delt, link);
+ }
+ *target = dlist;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ ns_listenlist_detach(&dlist);
+ return (result);
+}
+
+/*
+ * Create a listen list from the corresponding configuration
+ * data structure.
+ */
+static isc_result_t
+ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, ns_listenelt_t **target)
+{
+ isc_result_t result;
+ const cfg_obj_t *portobj;
+ in_port_t port;
+ ns_listenelt_t *delt = NULL;
+ REQUIRE(target != NULL && *target == NULL);
+
+ portobj = cfg_tuple_get(listener, "port");
+ if (!cfg_obj_isuint32(portobj)) {
+ if (ns_g_port != 0) {
+ port = ns_g_port;
+ } else {
+ result = ns_config_getport(config, &port);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ } else {
+ if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port value '%u' is out of range",
+ cfg_obj_asuint32(portobj));
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t)cfg_obj_asuint32(portobj);
+ }
+
+ result = ns_listenelt_create(mctx, port, NULL, &delt);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"),
+ config, ns_g_lctx, actx, mctx, &delt->acl);
+ if (result != ISC_R_SUCCESS) {
+ ns_listenelt_destroy(delt);
+ return (result);
+ }
+ *target = delt;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_server_dumpstats(ns_server_t *server) {
+ isc_result_t result;
+ dns_zone_t *zone, *next;
+ isc_stdtime_t now;
+ FILE *fp = NULL;
+ int i;
+ int ncounters;
+
+ isc_stdtime_get(&now);
+
+ CHECKMF(isc_stdio_open(server->statsfile, "a", &fp),
+ "could not open statistics dump file", server->statsfile);
+
+ ncounters = DNS_STATS_NCOUNTERS;
+ fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now);
+
+ for (i = 0; i < ncounters; i++)
+ fprintf(fp, "%s %" ISC_PRINT_QUADFORMAT "u\n",
+ dns_statscounter_names[i],
+ server->querystats[i]);
+
+ zone = NULL;
+ for (result = dns_zone_first(server->zonemgr, &zone);
+ result == ISC_R_SUCCESS;
+ next = NULL, result = dns_zone_next(zone, &next), zone = next)
+ {
+ isc_uint64_t *zonestats = dns_zone_getstatscounters(zone);
+ if (zonestats != NULL) {
+ char zonename[DNS_NAME_FORMATSIZE];
+ dns_view_t *view;
+ char *viewname;
+
+ dns_name_format(dns_zone_getorigin(zone),
+ zonename, sizeof(zonename));
+ view = dns_zone_getview(zone);
+ viewname = view->name;
+ for (i = 0; i < ncounters; i++) {
+ fprintf(fp, "%s %" ISC_PRINT_QUADFORMAT
+ "u %s",
+ dns_statscounter_names[i],
+ zonestats[i],
+ zonename);
+ if (strcmp(viewname, "_default") != 0)
+ fprintf(fp, " %s", viewname);
+ fprintf(fp, "\n");
+ }
+ }
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ CHECK(result);
+
+ fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now);
+
+ cleanup:
+ if (fp != NULL)
+ (void)isc_stdio_close(fp);
+ return (result);
+}
+
+static isc_result_t
+add_zone_tolist(dns_zone_t *zone, void *uap) {
+ struct dumpcontext *dctx = uap;
+ struct zonelistentry *zle;
+
+ zle = isc_mem_get(dctx->mctx, sizeof *zle);
+ if (zle == NULL)
+ return (ISC_R_NOMEMORY);
+ zle->zone = NULL;
+ dns_zone_attach(zone, &zle->zone);
+ ISC_LINK_INIT(zle, link);
+ ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+add_view_tolist(struct dumpcontext *dctx, dns_view_t *view) {
+ struct viewlistentry *vle;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ /*
+ * Prevent duplicate views.
+ */
+ for (vle = ISC_LIST_HEAD(dctx->viewlist);
+ vle != NULL;
+ vle = ISC_LIST_NEXT(vle, link))
+ if (vle->view == view)
+ return (ISC_R_SUCCESS);
+
+ vle = isc_mem_get(dctx->mctx, sizeof *vle);
+ if (vle == NULL)
+ return (ISC_R_NOMEMORY);
+ vle->view = NULL;
+ dns_view_attach(view, &vle->view);
+ ISC_LINK_INIT(vle, link);
+ ISC_LIST_INIT(vle->zonelist);
+ ISC_LIST_APPEND(dctx->viewlist, vle, link);
+ if (dctx->dumpzones)
+ result = dns_zt_apply(view->zonetable, ISC_TRUE,
+ add_zone_tolist, dctx);
+ return (result);
+}
+
+static void
+dumpcontext_destroy(struct dumpcontext *dctx) {
+ struct viewlistentry *vle;
+ struct zonelistentry *zle;
+
+ vle = ISC_LIST_HEAD(dctx->viewlist);
+ while (vle != NULL) {
+ ISC_LIST_UNLINK(dctx->viewlist, vle, link);
+ zle = ISC_LIST_HEAD(vle->zonelist);
+ while (zle != NULL) {
+ ISC_LIST_UNLINK(vle->zonelist, zle, link);
+ dns_zone_detach(&zle->zone);
+ isc_mem_put(dctx->mctx, zle, sizeof *zle);
+ zle = ISC_LIST_HEAD(vle->zonelist);
+ }
+ dns_view_detach(&vle->view);
+ isc_mem_put(dctx->mctx, vle, sizeof *vle);
+ vle = ISC_LIST_HEAD(dctx->viewlist);
+ }
+ if (dctx->version != NULL)
+ dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
+ if (dctx->db != NULL)
+ dns_db_detach(&dctx->db);
+ if (dctx->cache != NULL)
+ dns_db_detach(&dctx->cache);
+ if (dctx->task != NULL)
+ isc_task_detach(&dctx->task);
+ if (dctx->fp != NULL)
+ (void)isc_stdio_close(dctx->fp);
+ if (dctx->mdctx != NULL)
+ dns_dumpctx_detach(&dctx->mdctx);
+ isc_mem_put(dctx->mctx, dctx, sizeof *dctx);
+}
+
+static void
+dumpdone(void *arg, isc_result_t result) {
+ struct dumpcontext *dctx = arg;
+ char buf[1024+32];
+ const dns_master_style_t *style;
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (dctx->mdctx != NULL)
+ dns_dumpctx_detach(&dctx->mdctx);
+ if (dctx->view == NULL) {
+ dctx->view = ISC_LIST_HEAD(dctx->viewlist);
+ if (dctx->view == NULL)
+ goto done;
+ INSIST(dctx->zone == NULL);
+ } else
+ goto resume;
+ nextview:
+ fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name);
+ resume:
+ if (dctx->zone == NULL && dctx->cache == NULL && dctx->dumpcache) {
+ style = &dns_master_style_cache;
+ /* start cache dump */
+ if (dctx->view->view->cachedb != NULL)
+ dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
+ if (dctx->cache != NULL) {
+
+ fprintf(dctx->fp, ";\n; Cache dump of view '%s'\n;\n",
+ dctx->view->view->name);
+ result = dns_master_dumptostreaminc(dctx->mctx,
+ dctx->cache, NULL,
+ style, dctx->fp,
+ dctx->task,
+ dumpdone, dctx,
+ &dctx->mdctx);
+ if (result == DNS_R_CONTINUE)
+ return;
+ if (result == ISC_R_NOTIMPLEMENTED)
+ fprintf(dctx->fp, "; %s\n",
+ dns_result_totext(result));
+ else if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ }
+ if (dctx->cache != NULL) {
+ dns_adb_dump(dctx->view->view->adb, dctx->fp);
+ dns_db_detach(&dctx->cache);
+ }
+ if (dctx->dumpzones) {
+ style = &dns_master_style_full;
+ nextzone:
+ if (dctx->version != NULL)
+ dns_db_closeversion(dctx->db, &dctx->version,
+ ISC_FALSE);
+ if (dctx->db != NULL)
+ dns_db_detach(&dctx->db);
+ if (dctx->zone == NULL)
+ dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist);
+ else
+ dctx->zone = ISC_LIST_NEXT(dctx->zone, link);
+ if (dctx->zone != NULL) {
+ /* start zone dump */
+ dns_zone_name(dctx->zone->zone, buf, sizeof(buf));
+ fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf);
+ result = dns_zone_getdb(dctx->zone->zone, &dctx->db);
+ if (result != ISC_R_SUCCESS) {
+ fprintf(dctx->fp, "; %s\n",
+ dns_result_totext(result));
+ goto nextzone;
+ }
+ dns_db_currentversion(dctx->db, &dctx->version);
+ result = dns_master_dumptostreaminc(dctx->mctx,
+ dctx->db,
+ dctx->version,
+ style, dctx->fp,
+ dctx->task,
+ dumpdone, dctx,
+ &dctx->mdctx);
+ if (result == DNS_R_CONTINUE)
+ return;
+ if (result == ISC_R_NOTIMPLEMENTED) {
+ fprintf(dctx->fp, "; %s\n",
+ dns_result_totext(result));
+ result = ISC_R_SUCCESS;
+ goto nextzone;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ }
+ if (dctx->view != NULL)
+ dctx->view = ISC_LIST_NEXT(dctx->view, link);
+ if (dctx->view != NULL)
+ goto nextview;
+ done:
+ fprintf(dctx->fp, "; Dump complete\n");
+ result = isc_stdio_flush(dctx->fp);
+ if (result == ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpdb complete");
+ cleanup:
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpdb failed: %s", dns_result_totext(result));
+ dumpcontext_destroy(dctx);
+}
+
+isc_result_t
+ns_server_dumpdb(ns_server_t *server, char *args) {
+ struct dumpcontext *dctx = NULL;
+ dns_view_t *view;
+ isc_result_t result;
+ char *ptr;
+ const char *sep;
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ dctx = isc_mem_get(server->mctx, sizeof(*dctx));
+ if (dctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dctx->mctx = server->mctx;
+ dctx->dumpcache = ISC_TRUE;
+ dctx->dumpzones = ISC_FALSE;
+ dctx->fp = NULL;
+ ISC_LIST_INIT(dctx->viewlist);
+ dctx->view = NULL;
+ dctx->zone = NULL;
+ dctx->cache = NULL;
+ dctx->mdctx = NULL;
+ dctx->db = NULL;
+ dctx->cache = NULL;
+ dctx->task = NULL;
+ dctx->version = NULL;
+ isc_task_attach(server->task, &dctx->task);
+
+ CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp),
+ "could not open dump file", server->dumpfile);
+
+ sep = (args == NULL) ? "" : ": ";
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpdb started%s%s", sep, (args != NULL) ? args : "");
+
+ ptr = next_token(&args, " \t");
+ if (ptr != NULL && strcmp(ptr, "-all") == 0) {
+ dctx->dumpzones = ISC_TRUE;
+ dctx->dumpcache = ISC_TRUE;
+ ptr = next_token(&args, " \t");
+ } else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
+ dctx->dumpzones = ISC_FALSE;
+ dctx->dumpcache = ISC_TRUE;
+ ptr = next_token(&args, " \t");
+ } else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
+ dctx->dumpzones = ISC_TRUE;
+ dctx->dumpcache = ISC_FALSE;
+ ptr = next_token(&args, " \t");
+ }
+
+ nextview:
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (ptr != NULL && strcmp(view->name, ptr) != 0)
+ continue;
+ CHECK(add_view_tolist(dctx, view));
+ }
+ if (ptr != NULL) {
+ ptr = next_token(&args, " \t");
+ if (ptr != NULL)
+ goto nextview;
+ }
+ dumpdone(dctx, ISC_R_SUCCESS);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (dctx != NULL)
+ dumpcontext_destroy(dctx);
+ return (result);
+}
+
+isc_result_t
+ns_server_dumprecursing(ns_server_t *server) {
+ FILE *fp = NULL;
+ isc_result_t result;
+
+ CHECKMF(isc_stdio_open(server->recfile, "w", &fp),
+ "could not open dump file", server->recfile);
+ fprintf(fp,";\n; Recursing Queries\n;\n");
+ ns_interfacemgr_dumprecursing(fp, server->interfacemgr);
+ fprintf(fp, "; Dump complete\n");
+
+ cleanup:
+ if (fp != NULL)
+ result = isc_stdio_close(fp);
+ return (result);
+}
+
+isc_result_t
+ns_server_setdebuglevel(ns_server_t *server, char *args) {
+ char *ptr;
+ char *levelstr;
+ char *endp;
+ long newlevel;
+
+ UNUSED(server);
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Look for the new level name. */
+ levelstr = next_token(&args, " \t");
+ if (levelstr == NULL) {
+ if (ns_g_debuglevel < 99)
+ ns_g_debuglevel++;
+ } else {
+ newlevel = strtol(levelstr, &endp, 10);
+ if (*endp != '\0' || newlevel < 0 || newlevel > 99)
+ return (ISC_R_RANGE);
+ ns_g_debuglevel = (unsigned int)newlevel;
+ }
+ isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_server_validation(ns_server_t *server, char *args) {
+ char *ptr, *viewname;
+ dns_view_t *view;
+ isc_boolean_t changed = ISC_FALSE;
+ isc_result_t result;
+ isc_boolean_t enable;
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Find out what we are to do. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
+ !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
+ enable = ISC_TRUE;
+ else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
+ !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
+ enable = ISC_FALSE;
+ else
+ return (DNS_R_SYNTAX);
+
+ /* Look for the view name. */
+ viewname = next_token(&args, " \t");
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
+ continue;
+ result = dns_view_flushcache(view);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ view->enablevalidation = enable;
+ changed = ISC_TRUE;
+ }
+ if (changed)
+ result = ISC_R_SUCCESS;
+ else
+ result = ISC_R_FAILURE;
+ out:
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+ns_server_flushcache(ns_server_t *server, char *args) {
+ char *ptr, *viewname;
+ dns_view_t *view;
+ isc_boolean_t flushed;
+ isc_boolean_t found;
+ isc_result_t result;
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Look for the view name. */
+ viewname = next_token(&args, " \t");
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ flushed = ISC_TRUE;
+ found = ISC_FALSE;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
+ continue;
+ found = ISC_TRUE;
+ result = dns_view_flushcache(view);
+ if (result != ISC_R_SUCCESS)
+ flushed = ISC_FALSE;
+ }
+ if (flushed && found) {
+ result = ISC_R_SUCCESS;
+ } else {
+ if (!found)
+ result = ISC_R_NOTFOUND;
+ else
+ result = ISC_R_FAILURE;
+ }
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+ns_server_flushname(ns_server_t *server, char *args) {
+ char *ptr, *target, *viewname;
+ dns_view_t *view;
+ isc_boolean_t flushed;
+ isc_boolean_t found;
+ isc_result_t result;
+ isc_buffer_t b;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Find the domain name to flush. */
+ target = next_token(&args, " \t");
+ if (target == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_init(&b, target, strlen(target));
+ isc_buffer_add(&b, strlen(target));
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* Look for the view name. */
+ viewname = next_token(&args, " \t");
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ flushed = ISC_TRUE;
+ found = ISC_FALSE;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
+ continue;
+ found = ISC_TRUE;
+ result = dns_view_flushname(view, name);
+ if (result != ISC_R_SUCCESS)
+ flushed = ISC_FALSE;
+ }
+ if (flushed && found)
+ result = ISC_R_SUCCESS;
+ else if (!found)
+ result = ISC_R_NOTFOUND;
+ else
+ result = ISC_R_FAILURE;
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+ns_server_status(ns_server_t *server, isc_buffer_t *text) {
+ int zonecount, xferrunning, xferdeferred, soaqueries;
+ unsigned int n;
+
+ zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY);
+ xferrunning = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_XFERRUNNING);
+ xferdeferred = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_XFERDEFERRED);
+ soaqueries = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_SOAQUERY);
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "number of zones: %u\n"
+ "debug level: %d\n"
+ "xfers running: %u\n"
+ "xfers deferred: %u\n"
+ "soa queries in progress: %u\n"
+ "query logging is %s\n"
+ "recursive clients: %d/%d/%d\n"
+ "tcp clients: %d/%d\n"
+ "server is up and running",
+ zonecount, ns_g_debuglevel, xferrunning, xferdeferred,
+ soaqueries, server->log_queries ? "ON" : "OFF",
+ server->recursionquota.used, server->recursionquota.soft,
+ server->recursionquota.max,
+ server->tcpquota.used, server->tcpquota.max);
+ if (n >= isc_buffer_availablelength(text))
+ return (ISC_R_NOSPACE);
+ isc_buffer_add(text, n);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Act on a "freeze" or "thaw" command from the command channel.
+ */
+isc_result_t
+ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args) {
+ isc_result_t result, tresult;
+ dns_zone_t *zone = NULL;
+ dns_zonetype_t type;
+ char classstr[DNS_RDATACLASS_FORMATSIZE];
+ char zonename[DNS_NAME_FORMATSIZE];
+ dns_view_t *view;
+ char *journal;
+ const char *vname, *sep;
+ isc_boolean_t frozen;
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL) {
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ tresult = ISC_R_SUCCESS;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ result = dns_view_freezezones(view, freeze);
+ if (result != ISC_R_SUCCESS &&
+ tresult == ISC_R_SUCCESS)
+ tresult = result;
+ }
+ isc_task_endexclusive(server->task);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s all zones: %s",
+ freeze ? "freezing" : "thawing",
+ isc_result_totext(tresult));
+ return (tresult);
+ }
+ type = dns_zone_gettype(zone);
+ if (type != dns_zone_master) {
+ dns_zone_detach(&zone);
+ return (ISC_R_NOTFOUND);
+ }
+
+ frozen = dns_zone_getupdatedisabled(zone);
+ if (freeze) {
+ if (frozen)
+ result = DNS_R_FROZEN;
+ if (result == ISC_R_SUCCESS)
+ result = dns_zone_flush(zone);
+ if (result == ISC_R_SUCCESS) {
+ journal = dns_zone_getjournal(zone);
+ if (journal != NULL)
+ (void)isc_file_remove(journal);
+ }
+ } else {
+ if (frozen) {
+ result = dns_zone_load(zone);
+ if (result == DNS_R_CONTINUE ||
+ result == DNS_R_UPTODATE)
+ result = ISC_R_SUCCESS;
+ }
+ }
+ if (result == ISC_R_SUCCESS)
+ dns_zone_setupdatedisabled(zone, freeze);
+
+ view = dns_zone_getview(zone);
+ if (strcmp(view->name, "_bind") == 0 ||
+ strcmp(view->name, "_default") == 0)
+ {
+ vname = "";
+ sep = "";
+ } else {
+ vname = view->name;
+ sep = " ";
+ }
+ dns_rdataclass_format(dns_zone_getclass(zone), classstr,
+ sizeof(classstr));
+ dns_name_format(dns_zone_getorigin(zone),
+ zonename, sizeof(zonename));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s zone '%s/%s'%s%s: %s",
+ freeze ? "freezing" : "thawing",
+ zonename, classstr, sep, vname,
+ isc_result_totext(result));
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+#ifdef HAVE_LIBSCF
+/*
+ * This function adds a message for rndc to echo if named
+ * is managed by smf and is also running chroot.
+ */
+isc_result_t
+ns_smf_add_message(isc_buffer_t *text) {
+ unsigned int n;
+
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "use svcadm(1M) to manage named");
+ if (n >= isc_buffer_availablelength(text))
+ return (ISC_R_NOSPACE);
+ isc_buffer_add(text, n);
+ return (ISC_R_SUCCESS);
+}
+#endif /* HAVE_LIBSCF */
diff --git a/bin/named/sortlist.c b/bin/named/sortlist.c
new file mode 100644
index 0000000..28f0360
--- /dev/null
+++ b/bin/named/sortlist.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 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: sortlist.c,v 1.9.18.4 2006/03/02 00:37:21 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/result.h>
+
+#include <named/globals.h>
+#include <named/server.h>
+#include <named/sortlist.h>
+
+ns_sortlisttype_t
+ns_sortlist_setup(dns_acl_t *acl, isc_netaddr_t *clientaddr,
+ const void **argp)
+{
+ unsigned int i;
+
+ if (acl == NULL)
+ goto dont_sort;
+
+ for (i = 0; i < acl->length; i++) {
+ /*
+ * 'e' refers to the current 'top level statement'
+ * in the sortlist (see ARM).
+ */
+ dns_aclelement_t *e = &acl->elements[i];
+ dns_aclelement_t *try_elt;
+ dns_aclelement_t *order_elt = NULL;
+ const dns_aclelement_t *matched_elt = NULL;
+
+ if (e->type == dns_aclelementtype_nestedacl) {
+ dns_acl_t *inner = e->u.nestedacl;
+
+ if (inner->length < 1 || inner->length > 2)
+ goto dont_sort;
+ if (inner->elements[0].negative)
+ goto dont_sort;
+ try_elt = &inner->elements[0];
+ if (inner->length == 2)
+ order_elt = &inner->elements[1];
+ } else {
+ /*
+ * BIND 8 allows bare elements at the top level
+ * as an undocumented feature.
+ */
+ try_elt = e;
+ }
+
+ if (dns_aclelement_match(clientaddr, NULL, try_elt,
+ &ns_g_server->aclenv,
+ &matched_elt)) {
+ if (order_elt != NULL) {
+ if (order_elt->type ==
+ dns_aclelementtype_nestedacl) {
+ *argp = order_elt->u.nestedacl;
+ return (NS_SORTLISTTYPE_2ELEMENT);
+ } else if (order_elt->type ==
+ dns_aclelementtype_localhost &&
+ ns_g_server->aclenv.localhost != NULL) {
+ *argp = ns_g_server->aclenv.localhost;
+ return (NS_SORTLISTTYPE_2ELEMENT);
+ } else if (order_elt->type ==
+ dns_aclelementtype_localnets &&
+ ns_g_server->aclenv.localnets != NULL) {
+ *argp = ns_g_server->aclenv.localnets;
+ return (NS_SORTLISTTYPE_2ELEMENT);
+ } else {
+ /*
+ * BIND 8 allows a bare IP prefix as
+ * the 2nd element of a 2-element
+ * sortlist statement.
+ */
+ *argp = order_elt;
+ return (NS_SORTLISTTYPE_1ELEMENT);
+ }
+ } else {
+ INSIST(matched_elt != NULL);
+ *argp = matched_elt;
+ return (NS_SORTLISTTYPE_1ELEMENT);
+ }
+ }
+ }
+
+ /* No match; don't sort. */
+ dont_sort:
+ *argp = NULL;
+ return (NS_SORTLISTTYPE_NONE);
+}
+
+int
+ns_sortlist_addrorder2(const isc_netaddr_t *addr, const void *arg) {
+ const dns_acl_t *sortacl = (const dns_acl_t *) arg;
+ int match;
+
+ (void)dns_acl_match(addr, NULL, sortacl,
+ &ns_g_server->aclenv,
+ &match, NULL);
+ if (match > 0)
+ return (match);
+ else if (match < 0)
+ return (INT_MAX - (-match));
+ else
+ return (INT_MAX / 2);
+}
+
+int
+ns_sortlist_addrorder1(const isc_netaddr_t *addr, const void *arg) {
+ const dns_aclelement_t *matchelt = (const dns_aclelement_t *) arg;
+ if (dns_aclelement_match(addr, NULL, matchelt,
+ &ns_g_server->aclenv,
+ NULL)) {
+ return (0);
+ } else {
+ return (INT_MAX);
+ }
+}
+
+void
+ns_sortlist_byaddrsetup(dns_acl_t *sortlist_acl, isc_netaddr_t *client_addr,
+ dns_addressorderfunc_t *orderp,
+ const void **argp)
+{
+ ns_sortlisttype_t sortlisttype;
+
+ sortlisttype = ns_sortlist_setup(sortlist_acl, client_addr, argp);
+
+ switch (sortlisttype) {
+ case NS_SORTLISTTYPE_1ELEMENT:
+ *orderp = ns_sortlist_addrorder1;
+ break;
+ case NS_SORTLISTTYPE_2ELEMENT:
+ *orderp = ns_sortlist_addrorder2;
+ break;
+ case NS_SORTLISTTYPE_NONE:
+ *orderp = NULL;
+ break;
+ default:
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "unexpected return from ns_sortlist_setup(): "
+ "%d", sortlisttype);
+ break;
+ }
+}
+
diff --git a/bin/named/tkeyconf.c b/bin/named/tkeyconf.c
new file mode 100644
index 0000000..3c843ac
--- /dev/null
+++ b/bin/named/tkeyconf.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: tkeyconf.c,v 1.20.18.6 2006/03/02 00:37:21 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/mem.h>
+
+#include <isccfg/cfg.h>
+
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/name.h>
+#include <dns/tkey.h>
+
+#include <dst/gssapi.h>
+
+#include <named/tkeyconf.h>
+
+#define RETERR(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto failure; \
+ } while (0)
+
+
+isc_result_t
+ns_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx,
+ isc_entropy_t *ectx, dns_tkeyctx_t **tctxp)
+{
+ isc_result_t result;
+ dns_tkeyctx_t *tctx = NULL;
+ const char *s;
+ isc_uint32_t n;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_buffer_t b;
+ const cfg_obj_t *obj;
+ int type;
+
+ result = dns_tkeyctx_create(mctx, ectx, &tctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-dhkey", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
+ n = cfg_obj_asuint32(cfg_tuple_get(obj, "keyid"));
+ isc_buffer_init(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname,
+ ISC_FALSE, NULL));
+ type = DST_TYPE_PUBLIC|DST_TYPE_PRIVATE|DST_TYPE_KEY;
+ RETERR(dst_key_fromfile(name, (dns_keytag_t) n, DNS_KEYALG_DH,
+ type, NULL, mctx, &tctx->dhkey));
+ }
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-domain", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(obj);
+ isc_buffer_init(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE,
+ NULL));
+ tctx->domain = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (tctx->domain == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ dns_name_init(tctx->domain, NULL);
+ RETERR(dns_name_dup(name, mctx, tctx->domain));
+ }
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-gssapi-credential", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(obj);
+ isc_buffer_init(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE,
+ NULL));
+ RETERR(dst_gssapi_acquirecred(name, ISC_FALSE,
+ &tctx->gsscred));
+ }
+
+ *tctxp = tctx;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ dns_tkeyctx_destroy(&tctx);
+ return (result);
+}
+
diff --git a/bin/named/tsigconf.c b/bin/named/tsigconf.c
new file mode 100644
index 0000000..7fa7fe5
--- /dev/null
+++ b/bin/named/tsigconf.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 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: tsigconf.c,v 1.22.18.6 2006/02/28 03:10:47 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+
+#include <isccfg/cfg.h>
+
+#include <dns/tsig.h>
+#include <dns/result.h>
+
+#include <named/log.h>
+
+#include <named/config.h>
+#include <named/tsigconf.h>
+
+static isc_result_t
+add_initial_keys(const cfg_obj_t *list, dns_tsig_keyring_t *ring,
+ isc_mem_t *mctx)
+{
+ dns_tsigkey_t *tsigkey = NULL;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *key = NULL;
+ const char *keyid = NULL;
+ unsigned char *secret = NULL;
+ int secretalloc = 0;
+ int secretlen = 0;
+ isc_result_t ret;
+ isc_stdtime_t now;
+ isc_uint16_t bits;
+
+ for (element = cfg_list_first(list);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *algobj = NULL;
+ const cfg_obj_t *secretobj = NULL;
+ dns_name_t keyname;
+ dns_name_t *alg;
+ const char *algstr;
+ char keynamedata[1024];
+ isc_buffer_t keynamesrc, keynamebuf;
+ const char *secretstr;
+ isc_buffer_t secretbuf;
+
+ key = cfg_listelt_value(element);
+ keyid = cfg_obj_asstring(cfg_map_getname(key));
+
+ algobj = NULL;
+ secretobj = NULL;
+ (void)cfg_map_get(key, "algorithm", &algobj);
+ (void)cfg_map_get(key, "secret", &secretobj);
+ INSIST(algobj != NULL && secretobj != NULL);
+
+ /*
+ * Create the key name.
+ */
+ dns_name_init(&keyname, NULL);
+ isc_buffer_init(&keynamesrc, keyid, strlen(keyid));
+ isc_buffer_add(&keynamesrc, strlen(keyid));
+ isc_buffer_init(&keynamebuf, keynamedata, sizeof(keynamedata));
+ ret = dns_name_fromtext(&keyname, &keynamesrc, dns_rootname,
+ ISC_TRUE, &keynamebuf);
+ if (ret != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Create the algorithm.
+ */
+ algstr = cfg_obj_asstring(algobj);
+ if (ns_config_getkeyalgorithm(algstr, &alg, &bits)
+ != ISC_R_SUCCESS) {
+ cfg_obj_log(algobj, ns_g_lctx, ISC_LOG_ERROR,
+ "key '%s': has a unsupported algorithm '%s'",
+ keyid, algstr);
+ ret = DNS_R_BADALG;
+ goto failure;
+ }
+
+ secretstr = cfg_obj_asstring(secretobj);
+ secretalloc = secretlen = strlen(secretstr) * 3 / 4;
+ secret = isc_mem_get(mctx, secretlen);
+ if (secret == NULL) {
+ ret = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ isc_buffer_init(&secretbuf, secret, secretlen);
+ ret = isc_base64_decodestring(secretstr, &secretbuf);
+ if (ret != ISC_R_SUCCESS)
+ goto failure;
+ secretlen = isc_buffer_usedlength(&secretbuf);
+
+ isc_stdtime_get(&now);
+ ret = dns_tsigkey_create(&keyname, alg, secret, secretlen,
+ ISC_FALSE, NULL, now, now,
+ mctx, ring, &tsigkey);
+ isc_mem_put(mctx, secret, secretalloc);
+ secret = NULL;
+ if (ret != ISC_R_SUCCESS)
+ goto failure;
+ /*
+ * Set digest bits.
+ */
+ dst_key_setbits(tsigkey->key, bits);
+ dns_tsigkey_detach(&tsigkey);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
+ "configuring key '%s': %s", keyid,
+ isc_result_totext(ret));
+
+ if (secret != NULL)
+ isc_mem_put(mctx, secret, secretalloc);
+ return (ret);
+}
+
+isc_result_t
+ns_tsigkeyring_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_tsig_keyring_t **ringp)
+{
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *keylist;
+ dns_tsig_keyring_t *ring = NULL;
+ isc_result_t result;
+ int i;
+
+ i = 0;
+ if (config != NULL)
+ maps[i++] = config;
+ if (vconfig != NULL)
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ maps[i] = NULL;
+
+ result = dns_tsigkeyring_create(mctx, &ring);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ for (i = 0; ; i++) {
+ if (maps[i] == NULL)
+ break;
+ keylist = NULL;
+ result = cfg_map_get(maps[i], "key", &keylist);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ result = add_initial_keys(keylist, ring, mctx);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+
+ *ringp = ring;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ dns_tsigkeyring_destroy(&ring);
+ return (result);
+}
diff --git a/bin/named/unix/Makefile.in b/bin/named/unix/Makefile.in
new file mode 100644
index 0000000..a18351a
--- /dev/null
+++ b/bin/named/unix/Makefile.in
@@ -0,0 +1,36 @@
+# Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 1999-2001 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: Makefile.in,v 1.8 2004/03/05 04:58:01 marka Exp $
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+@BIND9_MAKE_INCLUDES@
+
+CINCLUDES = -I${srcdir}/include -I${srcdir}/../include \
+ ${DNS_INCLUDES} ${ISC_INCLUDES}
+
+CDEFINES =
+CWARNINGS =
+
+OBJS = os.@O@
+
+SRCS = os.c
+
+TARGETS = ${OBJS}
+
+@BIND9_MAKE_RULES@
diff --git a/bin/named/unix/include/named/os.h b/bin/named/unix/include/named/os.h
new file mode 100644
index 0000000..24afdcb
--- /dev/null
+++ b/bin/named/unix/include/named/os.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2004, 2005 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 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: os.h,v 1.22.18.3 2005/04/29 00:15:39 marka Exp $ */
+
+#ifndef NS_OS_H
+#define NS_OS_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+
+void
+ns_os_init(const char *progname);
+
+void
+ns_os_daemonize(void);
+
+void
+ns_os_opendevnull(void);
+
+void
+ns_os_closedevnull(void);
+
+void
+ns_os_chroot(const char *root);
+
+void
+ns_os_inituserinfo(const char *username);
+
+void
+ns_os_changeuser(void);
+
+void
+ns_os_minprivs(void);
+
+void
+ns_os_writepidfile(const char *filename, isc_boolean_t first_time);
+
+void
+ns_os_shutdown(void);
+
+isc_result_t
+ns_os_gethostname(char *buf, size_t len);
+
+void
+ns_os_shutdownmsg(char *command, isc_buffer_t *text);
+
+void
+ns_os_tzset(void);
+
+void
+ns_os_started(void);
+
+#endif /* NS_OS_H */
diff --git a/bin/named/unix/os.c b/bin/named/unix/os.c
new file mode 100644
index 0000000..3864612
--- /dev/null
+++ b/bin/named/unix/os.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 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: os.c,v 1.66.18.11 2006/02/03 23:51:38 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+#include <stdarg.h>
+
+#include <sys/types.h> /* dev_t FreeBSD 2.1 */
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h> /* Required for initgroups() on IRIX. */
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <syslog.h>
+#ifdef HAVE_TZSET
+#include <time.h>
+#endif
+#include <unistd.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/strerror.h>
+#include <isc/string.h>
+
+#include <named/main.h>
+#include <named/os.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#endif
+
+static char *pidfile = NULL;
+static int devnullfd = -1;
+
+#ifndef ISC_FACILITY
+#define ISC_FACILITY LOG_DAEMON
+#endif
+
+/*
+ * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
+ */
+#ifndef HAVE_LINUX_CAPABILITY_H
+#undef HAVE_SYS_PRCTL_H
+#endif
+
+/*
+ * Linux defines:
+ * (T) HAVE_LINUXTHREADS
+ * (C) HAVE_LINUX_CAPABILITY_H
+ * (P) HAVE_SYS_PRCTL_H
+ * The possible cases are:
+ * none: setuid() normally
+ * T: no setuid()
+ * C: setuid() normally, drop caps (keep CAP_SETUID)
+ * T+C: no setuid(), drop caps (don't keep CAP_SETUID)
+ * T+C+P: setuid() early, drop caps (keep CAP_SETUID)
+ * C+P: setuid() normally, drop caps (keep CAP_SETUID)
+ * P: not possible
+ * T+P: not possible
+ *
+ * if (C)
+ * caps = BIND_SERVICE + CHROOT + SETGID
+ * if ((T && C && P) || !T)
+ * caps += SETUID
+ * endif
+ * capset(caps)
+ * endif
+ * if (T && C && P && -u)
+ * setuid()
+ * else if (T && -u)
+ * fail
+ * --> start threads
+ * if (!T && -u)
+ * setuid()
+ * if (C && (P || !-u))
+ * caps = BIND_SERVICE
+ * capset(caps)
+ * endif
+ *
+ * It will be nice when Linux threads work properly with setuid().
+ */
+
+#ifdef HAVE_LINUXTHREADS
+static pid_t mainpid = 0;
+#endif
+
+static struct passwd *runas_pw = NULL;
+static isc_boolean_t done_setuid = ISC_FALSE;
+static int dfd[2] = { -1, -1 };
+
+#ifdef HAVE_LINUX_CAPABILITY_H
+
+static isc_boolean_t non_root = ISC_FALSE;
+static isc_boolean_t non_root_caps = ISC_FALSE;
+
+/*%
+ * We define _LINUX_FS_H to prevent it from being included. We don't need
+ * anything from it, and the files it includes cause warnings with 2.2
+ * kernels, and compilation failures (due to conflicts between <linux/string.h>
+ * and <string.h>) on 2.3 kernels.
+ */
+#define _LINUX_FS_H
+
+#include <sys/syscall.h> /* Required for syscall(). */
+#include <linux/capability.h> /* Required for _LINUX_CAPABILITY_VERSION. */
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h> /* Required for prctl(). */
+
+/*
+ * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
+ * here. This allows setuid() to work on systems running a new enough
+ * kernel but with /usr/include/linux pointing to "standard" kernel
+ * headers.
+ */
+#ifndef PR_SET_KEEPCAPS
+#define PR_SET_KEEPCAPS 8
+#endif
+
+#endif /* HAVE_SYS_PRCTL_H */
+
+#ifndef SYS_capset
+#ifndef __NR_capset
+#include <asm/unistd.h> /* Slackware 4.0 needs this. */
+#endif
+#define SYS_capset __NR_capset
+#endif
+
+static void
+linux_setcaps(unsigned int caps) {
+ struct __user_cap_header_struct caphead;
+ struct __user_cap_data_struct cap;
+ char strbuf[ISC_STRERRORSIZE];
+
+ if ((getuid() != 0 && !non_root_caps) || non_root)
+ return;
+
+ memset(&caphead, 0, sizeof(caphead));
+ caphead.version = _LINUX_CAPABILITY_VERSION;
+ caphead.pid = 0;
+ memset(&cap, 0, sizeof(cap));
+ cap.effective = caps;
+ cap.permitted = caps;
+ cap.inheritable = 0;
+ if (syscall(SYS_capset, &caphead, &cap) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("capset failed: %s:"
+ " please ensure that the capset kernel"
+ " module is loaded. see insmod(8)",
+ strbuf);
+ }
+}
+
+static void
+linux_initialprivs(void) {
+ unsigned int caps;
+
+ /*%
+ * We don't need most privileges, so we drop them right away.
+ * Later on linux_minprivs() will be called, which will drop our
+ * capabilities to the minimum needed to run the server.
+ */
+
+ caps = 0;
+
+ /*
+ * We need to be able to bind() to privileged ports, notably port 53!
+ */
+ caps |= (1 << CAP_NET_BIND_SERVICE);
+
+ /*
+ * We need chroot() initially too.
+ */
+ caps |= (1 << CAP_SYS_CHROOT);
+
+#if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
+ /*
+ * We can setuid() only if either the kernel supports keeping
+ * capabilities after setuid() (which we don't know until we've
+ * tried) or we're not using threads. If either of these is
+ * true, we want the setuid capability.
+ */
+ caps |= (1 << CAP_SETUID);
+#endif
+
+ /*
+ * Since we call initgroups, we need this.
+ */
+ caps |= (1 << CAP_SETGID);
+
+ /*
+ * Without this, we run into problems reading a configuration file
+ * owned by a non-root user and non-world-readable on startup.
+ */
+ caps |= (1 << CAP_DAC_READ_SEARCH);
+
+ /*
+ * XXX We might want to add CAP_SYS_RESOURCE, though it's not
+ * clear it would work right given the way linuxthreads work.
+ * XXXDCL But since we need to be able to set the maximum number
+ * of files, the stack size, data size, and core dump size to
+ * support named.conf options, this is now being added to test.
+ */
+ caps |= (1 << CAP_SYS_RESOURCE);
+
+ linux_setcaps(caps);
+}
+
+static void
+linux_minprivs(void) {
+ unsigned int caps;
+
+ /*%
+ * Drop all privileges except the ability to bind() to privileged
+ * ports.
+ *
+ * It's important that we drop CAP_SYS_CHROOT. If we didn't, it
+ * chroot() could be used to escape from the chrooted area.
+ */
+
+ caps = 0;
+ caps |= (1 << CAP_NET_BIND_SERVICE);
+
+ /*
+ * XXX We might want to add CAP_SYS_RESOURCE, though it's not
+ * clear it would work right given the way linuxthreads work.
+ * XXXDCL But since we need to be able to set the maximum number
+ * of files, the stack size, data size, and core dump size to
+ * support named.conf options, this is now being added to test.
+ */
+ caps |= (1 << CAP_SYS_RESOURCE);
+
+ linux_setcaps(caps);
+}
+
+#ifdef HAVE_SYS_PRCTL_H
+static void
+linux_keepcaps(void) {
+ char strbuf[ISC_STRERRORSIZE];
+ /*%
+ * Ask the kernel to allow us to keep our capabilities after we
+ * setuid().
+ */
+
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
+ if (errno != EINVAL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("prctl() failed: %s", strbuf);
+ }
+ } else {
+ non_root_caps = ISC_TRUE;
+ if (getuid() != 0)
+ non_root = ISC_TRUE;
+ }
+}
+#endif
+
+#endif /* HAVE_LINUX_CAPABILITY_H */
+
+
+static void
+setup_syslog(const char *progname) {
+ int options;
+
+ options = LOG_PID;
+#ifdef LOG_NDELAY
+ options |= LOG_NDELAY;
+#endif
+ openlog(isc_file_basename(progname), options, ISC_FACILITY);
+}
+
+void
+ns_os_init(const char *progname) {
+ setup_syslog(progname);
+#ifdef HAVE_LINUX_CAPABILITY_H
+ linux_initialprivs();
+#endif
+#ifdef HAVE_LINUXTHREADS
+ mainpid = getpid();
+#endif
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, SIG_IGN);
+#endif
+}
+
+void
+ns_os_daemonize(void) {
+ pid_t pid;
+ char strbuf[ISC_STRERRORSIZE];
+
+ if (pipe(dfd) == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("pipe(): %s", strbuf);
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("fork(): %s", strbuf);
+ }
+ if (pid != 0) {
+ int n;
+ /*
+ * Wait for the child to finish loading for the first time.
+ * This would be so much simpler if fork() worked once we
+ * were multi-threaded.
+ */
+ (void)close(dfd[1]);
+ do {
+ char buf;
+ n = read(dfd[0], &buf, 1);
+ if (n == 1)
+ _exit(0);
+ } while (n == -1 && errno == EINTR);
+ _exit(1);
+ }
+ (void)close(dfd[0]);
+
+ /*
+ * We're the child.
+ */
+
+#ifdef HAVE_LINUXTHREADS
+ mainpid = getpid();
+#endif
+
+ if (setsid() == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("setsid(): %s", strbuf);
+ }
+
+ /*
+ * Try to set stdin, stdout, and stderr to /dev/null, but press
+ * on even if it fails.
+ *
+ * XXXMLG The close() calls here are unneeded on all but NetBSD, but
+ * are harmless to include everywhere. dup2() is supposed to close
+ * the FD if it is in use, but unproven-pthreads-0.16 is broken
+ * and will end up closing the wrong FD. This will be fixed eventually,
+ * and these calls will be removed.
+ */
+ if (devnullfd != -1) {
+ if (devnullfd != STDIN_FILENO) {
+ (void)close(STDIN_FILENO);
+ (void)dup2(devnullfd, STDIN_FILENO);
+ }
+ if (devnullfd != STDOUT_FILENO) {
+ (void)close(STDOUT_FILENO);
+ (void)dup2(devnullfd, STDOUT_FILENO);
+ }
+ if (devnullfd != STDERR_FILENO) {
+ (void)close(STDERR_FILENO);
+ (void)dup2(devnullfd, STDERR_FILENO);
+ }
+ }
+}
+
+void
+ns_os_started(void) {
+ char buf = 0;
+
+ /*
+ * Signal to the parent that we stated successfully.
+ */
+ if (dfd[0] != -1 && dfd[1] != -1) {
+ write(dfd[1], &buf, 1);
+ close(dfd[1]);
+ dfd[0] = dfd[1] = -1;
+ }
+}
+
+void
+ns_os_opendevnull(void) {
+ devnullfd = open("/dev/null", O_RDWR, 0);
+}
+
+void
+ns_os_closedevnull(void) {
+ if (devnullfd != STDIN_FILENO &&
+ devnullfd != STDOUT_FILENO &&
+ devnullfd != STDERR_FILENO) {
+ close(devnullfd);
+ devnullfd = -1;
+ }
+}
+
+static isc_boolean_t
+all_digits(const char *s) {
+ if (*s == '\0')
+ return (ISC_FALSE);
+ while (*s != '\0') {
+ if (!isdigit((*s)&0xff))
+ return (ISC_FALSE);
+ s++;
+ }
+ return (ISC_TRUE);
+}
+
+void
+ns_os_chroot(const char *root) {
+ char strbuf[ISC_STRERRORSIZE];
+#ifdef HAVE_LIBSCF
+ ns_smf_chroot = 0;
+#endif
+ if (root != NULL) {
+ if (chroot(root) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("chroot(): %s", strbuf);
+ }
+ if (chdir("/") < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("chdir(/): %s", strbuf);
+ }
+#ifdef HAVE_LIBSCF
+ /* Set ns_smf_chroot flag on successful chroot. */
+ ns_smf_chroot = 1;
+#endif
+ }
+}
+
+void
+ns_os_inituserinfo(const char *username) {
+ char strbuf[ISC_STRERRORSIZE];
+ if (username == NULL)
+ return;
+
+ if (all_digits(username))
+ runas_pw = getpwuid((uid_t)atoi(username));
+ else
+ runas_pw = getpwnam(username);
+ endpwent();
+
+ if (runas_pw == NULL)
+ ns_main_earlyfatal("user '%s' unknown", username);
+
+ if (getuid() == 0) {
+ if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("initgroups(): %s", strbuf);
+ }
+ }
+
+}
+
+void
+ns_os_changeuser(void) {
+ char strbuf[ISC_STRERRORSIZE];
+ if (runas_pw == NULL || done_setuid)
+ return;
+
+ done_setuid = ISC_TRUE;
+
+#ifdef HAVE_LINUXTHREADS
+#ifdef HAVE_LINUX_CAPABILITY_H
+ if (!non_root_caps)
+ ns_main_earlyfatal("-u with Linux threads not supported: "
+ "requires kernel support for "
+ "prctl(PR_SET_KEEPCAPS)");
+#else
+ ns_main_earlyfatal("-u with Linux threads not supported: "
+ "no capabilities support or capabilities "
+ "disabled at build time");
+#endif
+#endif
+
+ if (setgid(runas_pw->pw_gid) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("setgid(): %s", strbuf);
+ }
+
+ if (setuid(runas_pw->pw_uid) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("setuid(): %s", strbuf);
+ }
+
+#if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
+ linux_minprivs();
+#endif
+#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
+ /*
+ * Restore the ability of named to drop core after the setuid()
+ * call has disabled it.
+ */
+ prctl(PR_SET_DUMPABLE,1,0,0,0);
+#endif
+}
+
+void
+ns_os_minprivs(void) {
+#ifdef HAVE_SYS_PRCTL_H
+ linux_keepcaps();
+#endif
+
+#ifdef HAVE_LINUXTHREADS
+ ns_os_changeuser(); /* Call setuid() before threads are started */
+#endif
+
+#if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
+ linux_minprivs();
+#endif
+}
+
+static int
+safe_open(const char *filename, isc_boolean_t append) {
+ int fd;
+ struct stat sb;
+
+ if (stat(filename, &sb) == -1) {
+ if (errno != ENOENT)
+ return (-1);
+ } else if ((sb.st_mode & S_IFREG) == 0) {
+ errno = EOPNOTSUPP;
+ return (-1);
+ }
+
+ if (append)
+ fd = open(filename, O_WRONLY|O_CREAT|O_APPEND,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ else {
+ (void)unlink(filename);
+ fd = open(filename, O_WRONLY|O_CREAT|O_EXCL,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ }
+ return (fd);
+}
+
+static void
+cleanup_pidfile(void) {
+ if (pidfile != NULL) {
+ (void)unlink(pidfile);
+ free(pidfile);
+ }
+ pidfile = NULL;
+}
+
+void
+ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
+ int fd;
+ FILE *lockfile;
+ size_t len;
+ pid_t pid;
+ char strbuf[ISC_STRERRORSIZE];
+ void (*report)(const char *, ...);
+
+ /*
+ * The caller must ensure any required synchronization.
+ */
+
+ report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
+
+ cleanup_pidfile();
+
+ if (filename == NULL)
+ return;
+
+ len = strlen(filename);
+ pidfile = malloc(len + 1);
+ if (pidfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't malloc '%s': %s", filename, strbuf);
+ return;
+ }
+ /* This is safe. */
+ strcpy(pidfile, filename);
+
+ fd = safe_open(filename, ISC_FALSE);
+ if (fd < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't open pid file '%s': %s", filename, strbuf);
+ free(pidfile);
+ pidfile = NULL;
+ return;
+ }
+ lockfile = fdopen(fd, "w");
+ if (lockfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("could not fdopen() pid file '%s': %s",
+ filename, strbuf);
+ (void)close(fd);
+ cleanup_pidfile();
+ return;
+ }
+#ifdef HAVE_LINUXTHREADS
+ pid = mainpid;
+#else
+ pid = getpid();
+#endif
+ if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
+ (*report)("fprintf() to pid file '%s' failed", filename);
+ (void)fclose(lockfile);
+ cleanup_pidfile();
+ return;
+ }
+ if (fflush(lockfile) == EOF) {
+ (*report)("fflush() to pid file '%s' failed", filename);
+ (void)fclose(lockfile);
+ cleanup_pidfile();
+ return;
+ }
+ (void)fclose(lockfile);
+}
+
+void
+ns_os_shutdown(void) {
+ closelog();
+ cleanup_pidfile();
+}
+
+isc_result_t
+ns_os_gethostname(char *buf, size_t len) {
+ int n;
+
+ n = gethostname(buf, len);
+ return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
+}
+
+static char *
+next_token(char **stringp, const char *delim) {
+ char *res;
+
+ do {
+ res = strsep(stringp, delim);
+ if (res == NULL)
+ break;
+ } while (*res == '\0');
+ return (res);
+}
+
+void
+ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
+ char *input, *ptr;
+ unsigned int n;
+ pid_t pid;
+
+ input = command;
+
+ /* Skip the command name. */
+ ptr = next_token(&input, " \t");
+ if (ptr == NULL)
+ return;
+
+ ptr = next_token(&input, " \t");
+ if (ptr == NULL)
+ return;
+
+ if (strcmp(ptr, "-p") != 0)
+ return;
+
+#ifdef HAVE_LINUXTHREADS
+ pid = mainpid;
+#else
+ pid = getpid();
+#endif
+
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "pid: %ld", (long)pid);
+ /* Only send a message if it is complete. */
+ if (n < isc_buffer_availablelength(text))
+ isc_buffer_add(text, n);
+}
+
+void
+ns_os_tzset(void) {
+#ifdef HAVE_TZSET
+ tzset();
+#endif
+}
diff --git a/bin/named/update.c b/bin/named/update.c
new file mode 100644
index 0000000..98054f8
--- /dev/null
+++ b/bin/named/update.c
@@ -0,0 +1,3026 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: update.c,v 1.109.18.23 2007/08/28 07:20:01 tbox Exp $ */
+
+#include <config.h>
+
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/taskpool.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/diff.h>
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/fixedname.h>
+#include <dns/journal.h>
+#include <dns/keyvalues.h>
+#include <dns/message.h>
+#include <dns/nsec.h>
+#include <dns/rdataclass.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/soa.h>
+#include <dns/ssu.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <named/client.h>
+#include <named/log.h>
+#include <named/update.h>
+
+/*! \file
+ * \brief
+ * This module implements dynamic update as in RFC2136.
+ */
+
+/*
+ XXX TODO:
+ - document strict minimality
+*/
+
+/**************************************************************************/
+
+/*%
+ * Log level for tracing dynamic update protocol requests.
+ */
+#define LOGLEVEL_PROTOCOL ISC_LOG_INFO
+
+/*%
+ * Log level for low-level debug tracing.
+ */
+#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8)
+
+/*%
+ * Check an operation for failure. These macros all assume that
+ * the function using them has a 'result' variable and a 'failure'
+ * label.
+ */
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/*%
+ * Fail unconditionally with result 'code', which must not
+ * be ISC_R_SUCCESS. The reason for failure presumably has
+ * been logged already.
+ *
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+
+#define FAIL(code) \
+ do { \
+ result = (code); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/*%
+ * Fail unconditionally and log as a client error.
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAILC(code, msg) \
+ do { \
+ const char *_what = "failed"; \
+ result = (code); \
+ switch (result) { \
+ case DNS_R_NXDOMAIN: \
+ case DNS_R_YXDOMAIN: \
+ case DNS_R_YXRRSET: \
+ case DNS_R_NXRRSET: \
+ _what = "unsuccessful"; \
+ } \
+ update_log(client, zone, LOGLEVEL_PROTOCOL, \
+ "update %s: %s (%s)", _what, \
+ msg, isc_result_totext(result)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define FAILN(code, name, msg) \
+ do { \
+ const char *_what = "failed"; \
+ result = (code); \
+ switch (result) { \
+ case DNS_R_NXDOMAIN: \
+ case DNS_R_YXDOMAIN: \
+ case DNS_R_YXRRSET: \
+ case DNS_R_NXRRSET: \
+ _what = "unsuccessful"; \
+ } \
+ if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \
+ char _nbuf[DNS_NAME_FORMATSIZE]; \
+ dns_name_format(name, _nbuf, sizeof(_nbuf)); \
+ update_log(client, zone, LOGLEVEL_PROTOCOL, \
+ "update %s: %s: %s (%s)", _what, _nbuf, \
+ msg, isc_result_totext(result)); \
+ } \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define FAILNT(code, name, type, msg) \
+ do { \
+ const char *_what = "failed"; \
+ result = (code); \
+ switch (result) { \
+ case DNS_R_NXDOMAIN: \
+ case DNS_R_YXDOMAIN: \
+ case DNS_R_YXRRSET: \
+ case DNS_R_NXRRSET: \
+ _what = "unsuccessful"; \
+ } \
+ if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \
+ char _nbuf[DNS_NAME_FORMATSIZE]; \
+ char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \
+ dns_name_format(name, _nbuf, sizeof(_nbuf)); \
+ dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \
+ update_log(client, zone, LOGLEVEL_PROTOCOL, \
+ "update %s: %s/%s: %s (%s)", \
+ _what, _nbuf, _tbuf, msg, \
+ isc_result_totext(result)); \
+ } \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+/*%
+ * Fail unconditionally and log as a server error.
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAILS(code, msg) \
+ do { \
+ result = (code); \
+ update_log(client, zone, LOGLEVEL_PROTOCOL, \
+ "error: %s: %s", \
+ msg, isc_result_totext(result)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/**************************************************************************/
+
+typedef struct rr rr_t;
+
+struct rr {
+ /* dns_name_t name; */
+ isc_uint32_t ttl;
+ dns_rdata_t rdata;
+};
+
+typedef struct update_event update_event_t;
+
+struct update_event {
+ ISC_EVENT_COMMON(update_event_t);
+ dns_zone_t *zone;
+ isc_result_t result;
+ dns_message_t *answer;
+};
+
+/**************************************************************************/
+/*
+ * Forward declarations.
+ */
+
+static void update_action(isc_task_t *task, isc_event_t *event);
+static void updatedone_action(isc_task_t *task, isc_event_t *event);
+static isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone);
+static void forward_done(isc_task_t *task, isc_event_t *event);
+
+/**************************************************************************/
+
+static void
+update_log(ns_client_t *client, dns_zone_t *zone,
+ int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);
+
+static void
+update_log(ns_client_t *client, dns_zone_t *zone,
+ int level, const char *fmt, ...)
+{
+ va_list ap;
+ char message[4096];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+
+ if (client == NULL || zone == NULL)
+ return;
+
+ if (isc_log_wouldlog(ns_g_lctx, level) == ISC_FALSE)
+ return;
+
+ dns_name_format(dns_zone_getorigin(zone), namebuf,
+ sizeof(namebuf));
+ dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
+ sizeof(classbuf));
+
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+
+ ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
+ level, "updating zone '%s/%s': %s",
+ namebuf, classbuf, message);
+}
+
+static isc_result_t
+checkupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message,
+ dns_name_t *zonename, isc_boolean_t slave)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+ int level = ISC_LOG_ERROR;
+ const char *msg = "denied";
+ isc_result_t result;
+
+ if (slave && acl == NULL) {
+ result = DNS_R_NOTIMP;
+ level = ISC_LOG_DEBUG(3);
+ msg = "disabled";
+ } else
+ result = ns_client_checkaclsilent(client, acl, ISC_FALSE);
+
+ if (result == ISC_R_SUCCESS) {
+ level = ISC_LOG_DEBUG(3);
+ msg = "approved";
+ }
+
+ dns_name_format(zonename, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(client->view->rdclass, classbuf,
+ sizeof(classbuf));
+
+ ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
+ NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s",
+ message, namebuf, classbuf, msg);
+ return (result);
+}
+
+/*%
+ * Update a single RR in version 'ver' of 'db' and log the
+ * update in 'diff'.
+ *
+ * Ensures:
+ * \li '*tuple' == NULL. Either the tuple is freed, or its
+ * ownership has been transferred to the diff.
+ */
+static isc_result_t
+do_one_tuple(dns_difftuple_t **tuple,
+ dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ dns_diff_t temp_diff;
+ isc_result_t result;
+
+ /*
+ * Create a singleton diff.
+ */
+ dns_diff_init(diff->mctx, &temp_diff);
+ ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
+
+ /*
+ * Apply it to the database.
+ */
+ result = dns_diff_apply(&temp_diff, db, ver);
+ ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
+ if (result != ISC_R_SUCCESS) {
+ dns_difftuple_free(tuple);
+ return (result);
+ }
+
+ /*
+ * Merge it into the current pending journal entry.
+ */
+ dns_diff_appendminimal(diff, tuple);
+
+ /*
+ * Do not clear temp_diff.
+ */
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Perform the updates in 'updates' in version 'ver' of 'db' and log the
+ * update in 'diff'.
+ *
+ * Ensures:
+ * \li 'updates' is empty.
+ */
+static isc_result_t
+do_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ while (! ISC_LIST_EMPTY(updates->tuples)) {
+ dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples);
+ ISC_LIST_UNLINK(updates->tuples, t, link);
+ CHECK(do_one_tuple(&t, db, ver, diff));
+ }
+ return (ISC_R_SUCCESS);
+
+ failure:
+ dns_diff_clear(diff);
+ return (result);
+}
+
+static isc_result_t
+update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
+ dns_diffop_t op, dns_name_t *name,
+ dns_ttl_t ttl, dns_rdata_t *rdata)
+{
+ dns_difftuple_t *tuple = NULL;
+ isc_result_t result;
+ result = dns_difftuple_create(diff->mctx, op,
+ name, ttl, rdata, &tuple);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (do_one_tuple(&tuple, db, ver, diff));
+}
+
+/**************************************************************************/
+/*
+ * Callback-style iteration over rdatasets and rdatas.
+ *
+ * foreach_rrset() can be used to iterate over the RRsets
+ * of a name and call a callback function with each
+ * one. Similarly, foreach_rr() can be used to iterate
+ * over the individual RRs at name, optionally restricted
+ * to RRs of a given type.
+ *
+ * The callback functions are called "actions" and take
+ * two arguments: a void pointer for passing arbitrary
+ * context information, and a pointer to the current RRset
+ * or RR. By convention, their names end in "_action".
+ */
+
+/*
+ * XXXRTH We might want to make this public somewhere in libdns.
+ */
+
+/*%
+ * Function type for foreach_rrset() iterator actions.
+ */
+typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset);
+
+/*%
+ * Function type for foreach_rr() iterator actions.
+ */
+typedef isc_result_t rr_func(void *data, rr_t *rr);
+
+/*%
+ * Internal context struct for foreach_node_rr().
+ */
+typedef struct {
+ rr_func * rr_action;
+ void * rr_action_data;
+} foreach_node_rr_ctx_t;
+
+/*%
+ * Internal helper function for foreach_node_rr().
+ */
+static isc_result_t
+foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) {
+ isc_result_t result;
+ foreach_node_rr_ctx_t *ctx = data;
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ rr_t rr = { 0, DNS_RDATA_INIT };
+
+ dns_rdataset_current(rdataset, &rr.rdata);
+ rr.ttl = rdataset->ttl;
+ result = (*ctx->rr_action)(ctx->rr_action_data, &rr);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ if (result != ISC_R_NOMORE)
+ return (result);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * For each rdataset of 'name' in 'ver' of 'db', call 'action'
+ * with the rdataset and 'action_data' as arguments. If the name
+ * does not exist, do nothing.
+ *
+ * If 'action' returns an error, abort iteration and return the error.
+ */
+static isc_result_t
+foreach_rrset(dns_db_t *db,
+ dns_dbversion_t *ver,
+ dns_name_t *name,
+ rrset_func *action,
+ void *action_data)
+{
+ isc_result_t result;
+ dns_dbnode_t *node;
+ dns_rdatasetiter_t *iter;
+
+ node = NULL;
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ iter = NULL;
+ result = dns_db_allrdatasets(db, node, ver,
+ (isc_stdtime_t) 0, &iter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node;
+
+ for (result = dns_rdatasetiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iter))
+ {
+ dns_rdataset_t rdataset;
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatasetiter_current(iter, &rdataset);
+
+ result = (*action)(action_data, &rdataset);
+
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iterator;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ cleanup_iterator:
+ dns_rdatasetiter_destroy(&iter);
+
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/*%
+ * For each RR of 'name' in 'ver' of 'db', call 'action'
+ * with the RR and 'action_data' as arguments. If the name
+ * does not exist, do nothing.
+ *
+ * If 'action' returns an error, abort iteration
+ * and return the error.
+ */
+static isc_result_t
+foreach_node_rr(dns_db_t *db,
+ dns_dbversion_t *ver,
+ dns_name_t *name,
+ rr_func *rr_action,
+ void *rr_action_data)
+{
+ foreach_node_rr_ctx_t ctx;
+ ctx.rr_action = rr_action;
+ ctx.rr_action_data = rr_action_data;
+ return (foreach_rrset(db, ver, name,
+ foreach_node_rr_action, &ctx));
+}
+
+
+/*%
+ * For each of the RRs specified by 'db', 'ver', 'name', 'type',
+ * (which can be dns_rdatatype_any to match any type), and 'covers', call
+ * 'action' with the RR and 'action_data' as arguments. If the name
+ * does not exist, or if no RRset of the given type exists at the name,
+ * do nothing.
+ *
+ * If 'action' returns an error, abort iteration and return the error.
+ */
+static isc_result_t
+foreach_rr(dns_db_t *db,
+ dns_dbversion_t *ver,
+ dns_name_t *name,
+ dns_rdatatype_t type,
+ dns_rdatatype_t covers,
+ rr_func *rr_action,
+ void *rr_action_data)
+{
+
+ isc_result_t result;
+ dns_dbnode_t *node;
+ dns_rdataset_t rdataset;
+
+ if (type == dns_rdatatype_any)
+ return (foreach_node_rr(db, ver, name,
+ rr_action, rr_action_data));
+
+ node = NULL;
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, ver, type, covers,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ goto cleanup_node;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ rr_t rr = { 0, DNS_RDATA_INIT };
+ dns_rdataset_current(&rdataset, &rr.rdata);
+ rr.ttl = rdataset.ttl;
+ result = (*rr_action)(rr_action_data, &rr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_rdataset;
+ }
+ if (result != ISC_R_NOMORE)
+ goto cleanup_rdataset;
+ result = ISC_R_SUCCESS;
+
+ cleanup_rdataset:
+ dns_rdataset_disassociate(&rdataset);
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/**************************************************************************/
+/*
+ * Various tests on the database contents (for prerequisites, etc).
+ */
+
+/*%
+ * Function type for predicate functions that compare a database RR 'db_rr'
+ * against an update RR 'update_rr'.
+ */
+typedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr);
+
+/*%
+ * Helper function for rrset_exists().
+ */
+static isc_result_t
+rrset_exists_action(void *data, rr_t *rr) {
+ UNUSED(data);
+ UNUSED(rr);
+ return (ISC_R_EXISTS);
+}
+
+/*%
+ * Utility macro for RR existence checking functions.
+ *
+ * If the variable 'result' has the value ISC_R_EXISTS or
+ * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE,
+ * respectively, and return success.
+ *
+ * If 'result' has any other value, there was a failure.
+ * Return the failure result code and do not set *exists.
+ *
+ * This would be more readable as "do { if ... } while(0)",
+ * but that form generates tons of warnings on Solaris 2.6.
+ */
+#define RETURN_EXISTENCE_FLAG \
+ return ((result == ISC_R_EXISTS) ? \
+ (*exists = ISC_TRUE, ISC_R_SUCCESS) : \
+ ((result == ISC_R_SUCCESS) ? \
+ (*exists = ISC_FALSE, ISC_R_SUCCESS) : \
+ result))
+
+/*%
+ * Set '*exists' to true iff an rrset of the given type exists,
+ * to false otherwise.
+ */
+static isc_result_t
+rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
+ dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_boolean_t *exists)
+{
+ isc_result_t result;
+ result = foreach_rr(db, ver, name, type, covers,
+ rrset_exists_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*%
+ * Helper function for cname_incompatible_rrset_exists.
+ */
+static isc_result_t
+cname_compatibility_action(void *data, dns_rdataset_t *rrset) {
+ UNUSED(data);
+ if (rrset->type != dns_rdatatype_cname &&
+ ! dns_rdatatype_isdnssec(rrset->type))
+ return (ISC_R_EXISTS);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Check whether there is an rrset incompatible with adding a CNAME RR,
+ * i.e., anything but another CNAME (which can be replaced) or a
+ * DNSSEC RR (which can coexist).
+ *
+ * If such an incompatible rrset exists, set '*exists' to ISC_TRUE.
+ * Otherwise, set it to ISC_FALSE.
+ */
+static isc_result_t
+cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
+ dns_name_t *name, isc_boolean_t *exists) {
+ isc_result_t result;
+ result = foreach_rrset(db, ver, name,
+ cname_compatibility_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*%
+ * Helper function for rr_count().
+ */
+static isc_result_t
+count_rr_action(void *data, rr_t *rr) {
+ int *countp = data;
+ UNUSED(rr);
+ (*countp)++;
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'.
+ */
+static isc_result_t
+rr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, dns_rdatatype_t covers, int *countp)
+{
+ *countp = 0;
+ return (foreach_rr(db, ver, name, type, covers,
+ count_rr_action, countp));
+}
+
+/*%
+ * Context struct and helper function for name_exists().
+ */
+
+static isc_result_t
+name_exists_action(void *data, dns_rdataset_t *rrset) {
+ UNUSED(data);
+ UNUSED(rrset);
+ return (ISC_R_EXISTS);
+}
+
+/*%
+ * Set '*exists' to true iff the given name exists, to false otherwise.
+ */
+static isc_result_t
+name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ isc_boolean_t *exists)
+{
+ isc_result_t result;
+ result = foreach_rrset(db, ver, name,
+ name_exists_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+typedef struct {
+ dns_name_t *name, *signer;
+ dns_ssutable_t *table;
+} ssu_check_t;
+
+static isc_result_t
+ssu_checkrule(void *data, dns_rdataset_t *rrset) {
+ ssu_check_t *ssuinfo = data;
+ isc_boolean_t result;
+
+ /*
+ * If we're deleting all records, it's ok to delete RRSIG and NSEC even
+ * if we're normally not allowed to.
+ */
+ if (rrset->type == dns_rdatatype_rrsig ||
+ rrset->type == dns_rdatatype_nsec)
+ return (ISC_R_SUCCESS);
+ result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer,
+ ssuinfo->name, rrset->type);
+ return (result == ISC_TRUE ? ISC_R_SUCCESS : ISC_R_FAILURE);
+}
+
+static isc_boolean_t
+ssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_ssutable_t *ssutable, dns_name_t *signer)
+{
+ isc_result_t result;
+ ssu_check_t ssuinfo;
+
+ ssuinfo.name = name;
+ ssuinfo.table = ssutable;
+ ssuinfo.signer = signer;
+ result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo);
+ return (ISC_TF(result == ISC_R_SUCCESS));
+}
+
+/**************************************************************************/
+/*
+ * Checking of "RRset exists (value dependent)" prerequisites.
+ *
+ * In the RFC2136 section 3.2.5, this is the pseudocode involving
+ * a variable called "temp", a mapping of <name, type> tuples to rrsets.
+ *
+ * Here, we represent the "temp" data structure as (non-minimial) "dns_diff_t"
+ * where each typle has op==DNS_DIFFOP_EXISTS.
+ */
+
+
+/*%
+ * Append a tuple asserting the existence of the RR with
+ * 'name' and 'rdata' to 'diff'.
+ */
+static isc_result_t
+temp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) {
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+
+ REQUIRE(DNS_DIFF_VALID(diff));
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS,
+ name, 0, rdata, &tuple));
+ ISC_LIST_APPEND(diff->tuples, tuple, link);
+ failure:
+ return (result);
+}
+
+/*%
+ * Compare two rdatasets represented as sorted lists of tuples.
+ * All list elements must have the same owner name and type.
+ * Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset)
+ * if not.
+ */
+static isc_result_t
+temp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) {
+ for (;;) {
+ if (a == NULL || b == NULL)
+ break;
+ INSIST(a->op == DNS_DIFFOP_EXISTS &&
+ b->op == DNS_DIFFOP_EXISTS);
+ INSIST(a->rdata.type == b->rdata.type);
+ INSIST(dns_name_equal(&a->name, &b->name));
+ if (dns_rdata_compare(&a->rdata, &b->rdata) != 0)
+ return (DNS_R_NXRRSET);
+ a = ISC_LIST_NEXT(a, link);
+ b = ISC_LIST_NEXT(b, link);
+ }
+ if (a != NULL || b != NULL)
+ return (DNS_R_NXRRSET);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * A comparison function defining the sorting order for the entries
+ * in the "temp" data structure. The major sort key is the owner name,
+ * followed by the type and rdata.
+ */
+static int
+temp_order(const void *av, const void *bv) {
+ dns_difftuple_t const * const *ap = av;
+ dns_difftuple_t const * const *bp = bv;
+ dns_difftuple_t const *a = *ap;
+ dns_difftuple_t const *b = *bp;
+ int r;
+ r = dns_name_compare(&a->name, &b->name);
+ if (r != 0)
+ return (r);
+ r = (b->rdata.type - a->rdata.type);
+ if (r != 0)
+ return (r);
+ r = dns_rdata_compare(&a->rdata, &b->rdata);
+ return (r);
+}
+
+/*%
+ * Check the "RRset exists (value dependent)" prerequisite information
+ * in 'temp' against the contents of the database 'db'.
+ *
+ * Return ISC_R_SUCCESS if the prerequisites are satisfied,
+ * rcode(dns_rcode_nxrrset) if not.
+ *
+ * 'temp' must be pre-sorted.
+ */
+
+static isc_result_t
+temp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *tmpname, dns_rdatatype_t *typep)
+{
+ isc_result_t result;
+ dns_name_t *name;
+ dns_dbnode_t *node;
+ dns_difftuple_t *t;
+ dns_diff_t trash;
+
+ dns_diff_init(mctx, &trash);
+
+ /*
+ * For each name and type in the prerequisites,
+ * construct a sorted rdata list of the corresponding
+ * database contents, and compare the lists.
+ */
+ t = ISC_LIST_HEAD(temp->tuples);
+ while (t != NULL) {
+ name = &t->name;
+ (void)dns_name_copy(name, tmpname, NULL);
+ *typep = t->rdata.type;
+
+ /* A new unique name begins here. */
+ node = NULL;
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (DNS_R_NXRRSET);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* A new unique type begins here. */
+ while (t != NULL && dns_name_equal(&t->name, name)) {
+ dns_rdatatype_t type, covers;
+ dns_rdataset_t rdataset;
+ dns_diff_t d_rrs; /* Database RRs with
+ this name and type */
+ dns_diff_t u_rrs; /* Update RRs with
+ this name and type */
+
+ *typep = type = t->rdata.type;
+ if (type == dns_rdatatype_rrsig ||
+ type == dns_rdatatype_sig)
+ covers = dns_rdata_covers(&t->rdata);
+ else
+ covers = 0;
+
+ /*
+ * Collect all database RRs for this name and type
+ * onto d_rrs and sort them.
+ */
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, ver, type,
+ covers, (isc_stdtime_t) 0,
+ &rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_detachnode(db, &node);
+ return (DNS_R_NXRRSET);
+ }
+
+ dns_diff_init(mctx, &d_rrs);
+ dns_diff_init(mctx, &u_rrs);
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &rdata);
+ result = temp_append(&d_rrs, name, &rdata);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ result = dns_diff_sort(&d_rrs, temp_order);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Collect all update RRs for this name and type
+ * onto u_rrs. No need to sort them here -
+ * they are already sorted.
+ */
+ while (t != NULL &&
+ dns_name_equal(&t->name, name) &&
+ t->rdata.type == type)
+ {
+ dns_difftuple_t *next =
+ ISC_LIST_NEXT(t, link);
+ ISC_LIST_UNLINK(temp->tuples, t, link);
+ ISC_LIST_APPEND(u_rrs.tuples, t, link);
+ t = next;
+ }
+
+ /* Compare the two sorted lists. */
+ result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples),
+ ISC_LIST_HEAD(d_rrs.tuples));
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * We are done with the tuples, but we can't free
+ * them yet because "name" still points into one
+ * of them. Move them on a temporary list.
+ */
+ ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link);
+ ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link);
+ dns_rdataset_disassociate(&rdataset);
+
+ continue;
+
+ failure:
+ dns_diff_clear(&d_rrs);
+ dns_diff_clear(&u_rrs);
+ dns_diff_clear(&trash);
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (result);
+ }
+
+ dns_db_detachnode(db, &node);
+ }
+
+ dns_diff_clear(&trash);
+ return (ISC_R_SUCCESS);
+}
+
+/**************************************************************************/
+/*
+ * Conditional deletion of RRs.
+ */
+
+/*%
+ * Context structure for delete_if().
+ */
+
+typedef struct {
+ rr_predicate *predicate;
+ dns_db_t *db;
+ dns_dbversion_t *ver;
+ dns_diff_t *diff;
+ dns_name_t *name;
+ dns_rdata_t *update_rr;
+} conditional_delete_ctx_t;
+
+/*%
+ * Predicate functions for delete_if().
+ */
+
+/*%
+ * Return true iff 'db_rr' is neither a SOA nor an NS RR nor
+ * an RRSIG nor a NSEC.
+ */
+static isc_boolean_t
+type_not_soa_nor_ns_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ return ((db_rr->type != dns_rdatatype_soa &&
+ db_rr->type != dns_rdatatype_ns &&
+ db_rr->type != dns_rdatatype_rrsig &&
+ db_rr->type != dns_rdatatype_nsec) ?
+ ISC_TRUE : ISC_FALSE);
+}
+
+/*%
+ * Return true iff 'db_rr' is neither a RRSIG nor a NSEC.
+ */
+static isc_boolean_t
+type_not_dnssec(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ return ((db_rr->type != dns_rdatatype_rrsig &&
+ db_rr->type != dns_rdatatype_nsec) ?
+ ISC_TRUE : ISC_FALSE);
+}
+
+/*%
+ * Return true always.
+ */
+static isc_boolean_t
+true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ UNUSED(db_rr);
+ return (ISC_TRUE);
+}
+
+/*%
+ * Return true iff the two RRs have identical rdata.
+ */
+static isc_boolean_t
+rr_equal_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ /*
+ * XXXRTH This is not a problem, but we should consider creating
+ * dns_rdata_equal() (that used dns_name_equal()), since it
+ * would be faster. Not a priority.
+ */
+ return (dns_rdata_compare(update_rr, db_rr) == 0 ?
+ ISC_TRUE : ISC_FALSE);
+}
+
+/*%
+ * Return true iff 'update_rr' should replace 'db_rr' according
+ * to the special RFC2136 rules for CNAME, SOA, and WKS records.
+ *
+ * RFC2136 does not mention NSEC or DNAME, but multiple NSECs or DNAMEs
+ * make little sense, so we replace those, too.
+ */
+static isc_boolean_t
+replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ if (db_rr->type != update_rr->type)
+ return (ISC_FALSE);
+ if (db_rr->type == dns_rdatatype_cname)
+ return (ISC_TRUE);
+ if (db_rr->type == dns_rdatatype_dname)
+ return (ISC_TRUE);
+ if (db_rr->type == dns_rdatatype_soa)
+ return (ISC_TRUE);
+ if (db_rr->type == dns_rdatatype_nsec)
+ return (ISC_TRUE);
+ if (db_rr->type == dns_rdatatype_wks) {
+ /*
+ * Compare the address and protocol fields only. These
+ * form the first five bytes of the RR data. Do a
+ * raw binary comparison; unpacking the WKS RRs using
+ * dns_rdata_tostruct() might be cleaner in some ways,
+ * but it would require us to pass around an mctx.
+ */
+ INSIST(db_rr->length >= 5 && update_rr->length >= 5);
+ return (memcmp(db_rr->data, update_rr->data, 5) == 0 ?
+ ISC_TRUE : ISC_FALSE);
+ }
+ return (ISC_FALSE);
+}
+
+/*%
+ * Internal helper function for delete_if().
+ */
+static isc_result_t
+delete_if_action(void *data, rr_t *rr) {
+ conditional_delete_ctx_t *ctx = data;
+ if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) {
+ isc_result_t result;
+ result = update_one_rr(ctx->db, ctx->ver, ctx->diff,
+ DNS_DIFFOP_DEL, ctx->name,
+ rr->ttl, &rr->rdata);
+ return (result);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+}
+
+/*%
+ * Conditionally delete RRs. Apply 'predicate' to the RRs
+ * specified by 'db', 'ver', 'name', and 'type' (which can
+ * be dns_rdatatype_any to match any type). Delete those
+ * RRs for which the predicate returns true, and log the
+ * deletions in 'diff'.
+ */
+static isc_result_t
+delete_if(rr_predicate *predicate,
+ dns_db_t *db,
+ dns_dbversion_t *ver,
+ dns_name_t *name,
+ dns_rdatatype_t type,
+ dns_rdatatype_t covers,
+ dns_rdata_t *update_rr,
+ dns_diff_t *diff)
+{
+ conditional_delete_ctx_t ctx;
+ ctx.predicate = predicate;
+ ctx.db = db;
+ ctx.ver = ver;
+ ctx.diff = diff;
+ ctx.name = name;
+ ctx.update_rr = update_rr;
+ return (foreach_rr(db, ver, name, type, covers,
+ delete_if_action, &ctx));
+}
+
+/**************************************************************************/
+/*%
+ * Prepare an RR for the addition of the new RR 'ctx->update_rr',
+ * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting
+ * the RRs if it is replaced by the new RR or has a conflicting TTL.
+ * The necessary changes are appended to ctx->del_diff and ctx->add_diff;
+ * we need to do all deletions before any additions so that we don't run
+ * into transient states with conflicting TTLs.
+ */
+
+typedef struct {
+ dns_db_t *db;
+ dns_dbversion_t *ver;
+ dns_diff_t *diff;
+ dns_name_t *name;
+ dns_rdata_t *update_rr;
+ dns_ttl_t update_rr_ttl;
+ isc_boolean_t ignore_add;
+ dns_diff_t del_diff;
+ dns_diff_t add_diff;
+} add_rr_prepare_ctx_t;
+
+static isc_result_t
+add_rr_prepare_action(void *data, rr_t *rr) {
+ isc_result_t result = ISC_R_SUCCESS;
+ add_rr_prepare_ctx_t *ctx = data;
+ dns_difftuple_t *tuple = NULL;
+ isc_boolean_t equal;
+
+ /*
+ * If the update RR is a "duplicate" of the update RR,
+ * the update should be silently ignored.
+ */
+ equal = ISC_TF(dns_rdata_compare(&rr->rdata, ctx->update_rr) == 0);
+ if (equal && rr->ttl == ctx->update_rr_ttl) {
+ ctx->ignore_add = ISC_TRUE;
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * If this RR is "equal" to the update RR, it should
+ * be deleted before the update RR is added.
+ */
+ if (replaces_p(ctx->update_rr, &rr->rdata)) {
+ CHECK(dns_difftuple_create(ctx->del_diff.mctx,
+ DNS_DIFFOP_DEL, ctx->name,
+ rr->ttl,
+ &rr->rdata,
+ &tuple));
+ dns_diff_append(&ctx->del_diff, &tuple);
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * If this RR differs in TTL from the update RR,
+ * its TTL must be adjusted.
+ */
+ if (rr->ttl != ctx->update_rr_ttl) {
+ CHECK(dns_difftuple_create(ctx->del_diff.mctx,
+ DNS_DIFFOP_DEL, ctx->name,
+ rr->ttl,
+ &rr->rdata,
+ &tuple));
+ dns_diff_append(&ctx->del_diff, &tuple);
+ if (!equal) {
+ CHECK(dns_difftuple_create(ctx->add_diff.mctx,
+ DNS_DIFFOP_ADD, ctx->name,
+ ctx->update_rr_ttl,
+ &rr->rdata,
+ &tuple));
+ dns_diff_append(&ctx->add_diff, &tuple);
+ }
+ }
+ failure:
+ return (result);
+}
+
+/**************************************************************************/
+/*
+ * Miscellaneous subroutines.
+ */
+
+/*%
+ * Extract a single update RR from 'section' of dynamic update message
+ * 'msg', with consistency checking.
+ *
+ * Stores the owner name, rdata, and TTL of the update RR at 'name',
+ * 'rdata', and 'ttl', respectively.
+ */
+static void
+get_current_rr(dns_message_t *msg, dns_section_t section,
+ dns_rdataclass_t zoneclass,
+ dns_name_t **name, dns_rdata_t *rdata, dns_rdatatype_t *covers,
+ dns_ttl_t *ttl,
+ dns_rdataclass_t *update_class)
+{
+ dns_rdataset_t *rdataset;
+ isc_result_t result;
+ dns_message_currentname(msg, section, name);
+ rdataset = ISC_LIST_HEAD((*name)->list);
+ INSIST(rdataset != NULL);
+ INSIST(ISC_LIST_NEXT(rdataset, link) == NULL);
+ *covers = rdataset->covers;
+ *ttl = rdataset->ttl;
+ result = dns_rdataset_first(rdataset);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, rdata);
+ INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
+ *update_class = rdata->rdclass;
+ rdata->rdclass = zoneclass;
+}
+
+/*%
+ * Increment the SOA serial number of database 'db', version 'ver'.
+ * Replace the SOA record in the database, and log the
+ * change in 'diff'.
+ */
+
+ /*
+ * XXXRTH Failures in this routine will be worth logging, when
+ * we have a logging system. Failure to find the zonename
+ * or the SOA rdataset warrant at least an UNEXPECTED_ERROR().
+ */
+
+static isc_result_t
+increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff, isc_mem_t *mctx)
+{
+ dns_difftuple_t *deltuple = NULL;
+ dns_difftuple_t *addtuple = NULL;
+ isc_uint32_t serial;
+ isc_result_t result;
+
+ CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
+ CHECK(dns_difftuple_copy(deltuple, &addtuple));
+ addtuple->op = DNS_DIFFOP_ADD;
+
+ serial = dns_soa_getserial(&addtuple->rdata);
+
+ /* RFC1982 */
+ serial = (serial + 1) & 0xFFFFFFFF;
+ if (serial == 0)
+ serial = 1;
+
+ dns_soa_setserial(serial, &addtuple->rdata);
+ CHECK(do_one_tuple(&deltuple, db, ver, diff));
+ CHECK(do_one_tuple(&addtuple, db, ver, diff));
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (addtuple != NULL)
+ dns_difftuple_free(&addtuple);
+ if (deltuple != NULL)
+ dns_difftuple_free(&deltuple);
+ return (result);
+}
+
+/*%
+ * Check that the new SOA record at 'update_rdata' does not
+ * illegally cause the SOA serial number to decrease or stay
+ * unchanged relative to the existing SOA in 'db'.
+ *
+ * Sets '*ok' to ISC_TRUE if the update is legal, ISC_FALSE if not.
+ *
+ * William King points out that RFC2136 is inconsistent about
+ * the case where the serial number stays unchanged:
+ *
+ * section 3.4.2.2 requires a server to ignore a SOA update request
+ * if the serial number on the update SOA is less_than_or_equal to
+ * the zone SOA serial.
+ *
+ * section 3.6 requires a server to ignore a SOA update request if
+ * the serial is less_than the zone SOA serial.
+ *
+ * Paul says 3.4.2.2 is correct.
+ *
+ */
+static isc_result_t
+check_soa_increment(dns_db_t *db, dns_dbversion_t *ver,
+ dns_rdata_t *update_rdata,
+ isc_boolean_t *ok)
+{
+ isc_uint32_t db_serial;
+ isc_uint32_t update_serial;
+ isc_result_t result;
+
+ update_serial = dns_soa_getserial(update_rdata);
+
+ result = dns_db_getsoaserial(db, ver, &db_serial);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (DNS_SERIAL_GE(db_serial, update_serial)) {
+ *ok = ISC_FALSE;
+ } else {
+ *ok = ISC_TRUE;
+ }
+
+ return (ISC_R_SUCCESS);
+
+}
+
+/**************************************************************************/
+/*
+ * Incremental updating of NSECs and RRSIGs.
+ */
+
+#define MAXZONEKEYS 32 /*%< Maximum number of zone keys supported. */
+
+/*%
+ * We abuse the dns_diff_t type to represent a set of domain names
+ * affected by the update.
+ */
+static isc_result_t
+namelist_append_name(dns_diff_t *list, dns_name_t *name) {
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+ static dns_rdata_t dummy_rdata = DNS_RDATA_INIT;
+
+ CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0,
+ &dummy_rdata, &tuple));
+ dns_diff_append(list, &tuple);
+ failure:
+ return (result);
+}
+
+static isc_result_t
+namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected)
+{
+ isc_result_t result;
+ dns_fixedname_t fixedname;
+ dns_name_t *child;
+ dns_dbiterator_t *dbit = NULL;
+
+ dns_fixedname_init(&fixedname);
+ child = dns_fixedname_name(&fixedname);
+
+ CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
+
+ for (result = dns_dbiterator_seek(dbit, name);
+ result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(dbit))
+ {
+ dns_dbnode_t *node = NULL;
+ CHECK(dns_dbiterator_current(dbit, &node, child));
+ dns_db_detachnode(db, &node);
+ if (! dns_name_issubdomain(child, name))
+ break;
+ CHECK(namelist_append_name(affected, child));
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ failure:
+ if (dbit != NULL)
+ dns_dbiterator_destroy(&dbit);
+ return (result);
+}
+
+
+
+/*%
+ * Helper function for non_nsec_rrset_exists().
+ */
+static isc_result_t
+is_non_nsec_action(void *data, dns_rdataset_t *rrset) {
+ UNUSED(data);
+ if (!(rrset->type == dns_rdatatype_nsec ||
+ (rrset->type == dns_rdatatype_rrsig &&
+ rrset->covers == dns_rdatatype_nsec)))
+ return (ISC_R_EXISTS);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Check whether there is an rrset other than a NSEC or RRSIG NSEC,
+ * i.e., anything that justifies the continued existence of a name
+ * after a secure update.
+ *
+ * If such an rrset exists, set '*exists' to ISC_TRUE.
+ * Otherwise, set it to ISC_FALSE.
+ */
+static isc_result_t
+non_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
+ dns_name_t *name, isc_boolean_t *exists)
+{
+ isc_result_t result;
+ result = foreach_rrset(db, ver, name,
+ is_non_nsec_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*%
+ * A comparison function for sorting dns_diff_t:s by name.
+ */
+static int
+name_order(const void *av, const void *bv) {
+ dns_difftuple_t const * const *ap = av;
+ dns_difftuple_t const * const *bp = bv;
+ dns_difftuple_t const *a = *ap;
+ dns_difftuple_t const *b = *bp;
+ return (dns_name_compare(&a->name, &b->name));
+}
+
+static isc_result_t
+uniqify_name_list(dns_diff_t *list) {
+ isc_result_t result;
+ dns_difftuple_t *p, *q;
+
+ CHECK(dns_diff_sort(list, name_order));
+
+ p = ISC_LIST_HEAD(list->tuples);
+ while (p != NULL) {
+ do {
+ q = ISC_LIST_NEXT(p, link);
+ if (q == NULL || ! dns_name_equal(&p->name, &q->name))
+ break;
+ ISC_LIST_UNLINK(list->tuples, q, link);
+ dns_difftuple_free(&q);
+ } while (1);
+ p = ISC_LIST_NEXT(p, link);
+ }
+ failure:
+ return (result);
+}
+
+
+static isc_result_t
+is_glue(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ isc_boolean_t *flag)
+{
+ isc_result_t result;
+ dns_fixedname_t foundname;
+ dns_fixedname_init(&foundname);
+ result = dns_db_find(db, name, ver, dns_rdatatype_any,
+ DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD,
+ (isc_stdtime_t) 0, NULL,
+ dns_fixedname_name(&foundname),
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS) {
+ *flag = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+ } else if (result == DNS_R_ZONECUT) {
+ /*
+ * We are at the zonecut. The name will have an NSEC, but
+ * non-delegation will be omitted from the type bit map.
+ */
+ *flag = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+ } else if (result == DNS_R_GLUE || result == DNS_R_DNAME) {
+ *flag = ISC_TRUE;
+ return (ISC_R_SUCCESS);
+ } else {
+ return (result);
+ }
+}
+
+/*%
+ * Find the next/previous name that has a NSEC record.
+ * In other words, skip empty database nodes and names that
+ * have had their NSECs removed because they are obscured by
+ * a zone cut.
+ */
+static isc_result_t
+next_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname,
+ isc_boolean_t forward)
+{
+ isc_result_t result;
+ dns_dbiterator_t *dbit = NULL;
+ isc_boolean_t has_nsec;
+ unsigned int wraps = 0;
+
+ CHECK(dns_db_createiterator(db, ISC_FALSE, &dbit));
+
+ CHECK(dns_dbiterator_seek(dbit, oldname));
+ do {
+ dns_dbnode_t *node = NULL;
+
+ if (forward)
+ result = dns_dbiterator_next(dbit);
+ else
+ result = dns_dbiterator_prev(dbit);
+ if (result == ISC_R_NOMORE) {
+ /*
+ * Wrap around.
+ */
+ if (forward)
+ CHECK(dns_dbiterator_first(dbit));
+ else
+ CHECK(dns_dbiterator_last(dbit));
+ wraps++;
+ if (wraps == 2) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "secure zone with no NSECs");
+ result = DNS_R_BADZONE;
+ goto failure;
+ }
+ }
+ CHECK(dns_dbiterator_current(dbit, &node, newname));
+ dns_db_detachnode(db, &node);
+
+ /*
+ * The iterator may hold the tree lock, and
+ * rrset_exists() calls dns_db_findnode() which
+ * may try to reacquire it. To avoid deadlock
+ * we must pause the iterator first.
+ */
+ CHECK(dns_dbiterator_pause(dbit));
+ CHECK(rrset_exists(db, ver, newname,
+ dns_rdatatype_nsec, 0, &has_nsec));
+
+ } while (! has_nsec);
+ failure:
+ if (dbit != NULL)
+ dns_dbiterator_destroy(&dbit);
+
+ return (result);
+}
+
+/*%
+ * Add a NSEC record for "name", recording the change in "diff".
+ * The existing NSEC is removed.
+ */
+static isc_result_t
+add_nsec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ unsigned char buffer[DNS_NSEC_BUFFERSIZE];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_difftuple_t *tuple = NULL;
+ dns_fixedname_t fixedname;
+ dns_name_t *target;
+
+ dns_fixedname_init(&fixedname);
+ target = dns_fixedname_name(&fixedname);
+
+ /*
+ * Find the successor name, aka NSEC target.
+ */
+ CHECK(next_active(client, zone, db, ver, name, target, ISC_TRUE));
+
+ /*
+ * Create the NSEC RDATA.
+ */
+ CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+ dns_rdata_init(&rdata);
+ CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata));
+ dns_db_detachnode(db, &node);
+
+ /*
+ * Delete the old NSEC and record the change.
+ */
+ CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0,
+ NULL, diff));
+ /*
+ * Add the new NSEC and record the change.
+ */
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
+ nsecttl, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ INSIST(tuple == NULL);
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*%
+ * Add a placeholder NSEC record for "name", recording the change in "diff".
+ */
+static isc_result_t
+add_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_diff_t *diff) {
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+ isc_region_t r;
+ unsigned char data[1] = { 0 }; /* The root domain, no bits. */
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ r.base = data;
+ r.length = sizeof(data);
+ dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r);
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0,
+ &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ failure:
+ return (result);
+}
+
+static isc_result_t
+find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ isc_mem_t *mctx, unsigned int maxkeys,
+ dst_key_t **keys, unsigned int *nkeys)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ const char *directory = dns_zone_getkeydirectory(zone);
+ CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
+ CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db),
+ directory, mctx, maxkeys, keys, nkeys));
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_boolean_t
+ksk_sanity(dns_db_t *db, dns_dbversion_t *ver) {
+ isc_boolean_t ret = ISC_FALSE;
+ isc_boolean_t have_ksk = ISC_FALSE, have_nonksk = ISC_FALSE;
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_dnskey_t dnskey;
+
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
+ CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0,
+ &rdataset, NULL));
+ CHECK(dns_rdataset_first(&rdataset));
+ while (result == ISC_R_SUCCESS && (!have_ksk || !have_nonksk)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL));
+ if ((dnskey.flags & (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH))
+ == DNS_KEYOWNER_ZONE) {
+ if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0)
+ have_ksk = ISC_TRUE;
+ else
+ have_nonksk = ISC_TRUE;
+ }
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ if (have_ksk && have_nonksk)
+ ret = ISC_TRUE;
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (ret);
+}
+
+/*%
+ * Add RRSIG records for an RRset, recording the change in "diff".
+ */
+static isc_result_t
+add_sigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, dns_diff_t *diff, dst_key_t **keys,
+ unsigned int nkeys, isc_mem_t *mctx, isc_stdtime_t inception,
+ isc_stdtime_t expire, isc_boolean_t check_ksk)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t sig_rdata = DNS_RDATA_INIT;
+ isc_buffer_t buffer;
+ unsigned char data[1024]; /* XXX */
+ unsigned int i;
+
+ dns_rdataset_init(&rdataset);
+ isc_buffer_init(&buffer, data, sizeof(data));
+
+ /* Get the rdataset to sign. */
+ CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+ CHECK(dns_db_findrdataset(db, node, ver, type, 0,
+ (isc_stdtime_t) 0,
+ &rdataset, NULL));
+ dns_db_detachnode(db, &node);
+
+ for (i = 0; i < nkeys; i++) {
+
+ if (check_ksk && type != dns_rdatatype_dnskey &&
+ (dst_key_flags(keys[i]) & DNS_KEYFLAG_KSK) != 0)
+ continue;
+
+ if (!dst_key_isprivate(keys[i]))
+ continue;
+
+ /* Calculate the signature, creating a RRSIG RDATA. */
+ CHECK(dns_dnssec_sign(name, &rdataset, keys[i],
+ &inception, &expire,
+ mctx, &buffer, &sig_rdata));
+
+ /* Update the database and journal with the RRSIG. */
+ /* XXX inefficient - will cause dataset merging */
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADD, name,
+ rdataset.ttl, &sig_rdata));
+ dns_rdata_reset(&sig_rdata);
+ }
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*%
+ * Update RRSIG and NSEC records affected by an update. The original
+ * update, including the SOA serial update but exluding the RRSIG & NSEC
+ * changes, is in "diff" and has already been applied to "newver" of "db".
+ * The database version prior to the update is "oldver".
+ *
+ * The necessary RRSIG and NSEC changes will be applied to "newver"
+ * and added (as a minimal diff) to "diff".
+ *
+ * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds.
+ */
+static isc_result_t
+update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *oldver, dns_dbversion_t *newver,
+ dns_diff_t *diff, isc_uint32_t sigvalidityinterval)
+{
+ isc_result_t result;
+ dns_difftuple_t *t;
+ dns_diff_t diffnames;
+ dns_diff_t affected;
+ dns_diff_t sig_diff;
+ dns_diff_t nsec_diff;
+ dns_diff_t nsec_mindiff;
+ isc_boolean_t flag;
+ dst_key_t *zone_keys[MAXZONEKEYS];
+ unsigned int nkeys = 0;
+ unsigned int i;
+ isc_stdtime_t now, inception, expire;
+ dns_ttl_t nsecttl;
+ dns_rdata_soa_t soa;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ dns_dbnode_t *node = NULL;
+ isc_boolean_t check_ksk;
+
+ dns_diff_init(client->mctx, &diffnames);
+ dns_diff_init(client->mctx, &affected);
+
+ dns_diff_init(client->mctx, &sig_diff);
+ dns_diff_init(client->mctx, &nsec_diff);
+ dns_diff_init(client->mctx, &nsec_mindiff);
+
+ result = find_zone_keys(zone, db, newver, client->mctx,
+ MAXZONEKEYS, zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "could not get zone keys for secure dynamic update");
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+ inception = now - 3600; /* Allow for some clock skew. */
+ expire = now + sigvalidityinterval;
+
+ /*
+ * Do we look at the KSK flag on the DNSKEY to determining which
+ * keys sign which RRsets? First check the zone option then
+ * check the keys flags to make sure atleast one has a ksk set
+ * and one doesn't.
+ */
+ check_ksk = ISC_TF((dns_zone_getoptions(zone) &
+ DNS_ZONEOPT_UPDATECHECKKSK) != 0);
+ if (check_ksk)
+ check_ksk = ksk_sanity(db, newver);
+
+ /*
+ * Get the NSEC's TTL from the SOA MINIMUM field.
+ */
+ CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL));
+ CHECK(dns_rdataset_first(&rdataset));
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &soa, NULL));
+ nsecttl = soa.minimum;
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+
+ /*
+ * Find all RRsets directly affected by the update, and
+ * update their RRSIGs. Also build a list of names affected
+ * by the update in "diffnames".
+ */
+ CHECK(dns_diff_sort(diff, temp_order));
+
+ t = ISC_LIST_HEAD(diff->tuples);
+ while (t != NULL) {
+ dns_name_t *name = &t->name;
+ /* Now "name" is a new, unique name affected by the update. */
+
+ CHECK(namelist_append_name(&diffnames, name));
+
+ while (t != NULL && dns_name_equal(&t->name, name)) {
+ dns_rdatatype_t type;
+ type = t->rdata.type;
+
+ /*
+ * Now "name" and "type" denote a new unique RRset
+ * affected by the update.
+ */
+
+ /* Don't sign RRSIGs. */
+ if (type == dns_rdatatype_rrsig)
+ goto skip;
+
+ /*
+ * Delete all old RRSIGs covering this type, since they
+ * are all invalid when the signed RRset has changed.
+ * We may not be able to recreate all of them - tough.
+ */
+ CHECK(delete_if(true_p, db, newver, name,
+ dns_rdatatype_rrsig, type,
+ NULL, &sig_diff));
+
+ /*
+ * If this RRset still exists after the update,
+ * add a new signature for it.
+ */
+ CHECK(rrset_exists(db, newver, name, type, 0, &flag));
+ if (flag) {
+ CHECK(add_sigs(db, newver, name, type,
+ &sig_diff, zone_keys, nkeys,
+ client->mctx, inception,
+ expire, check_ksk));
+ }
+ skip:
+ /* Skip any other updates to the same RRset. */
+ while (t != NULL &&
+ dns_name_equal(&t->name, name) &&
+ t->rdata.type == type)
+ {
+ t = ISC_LIST_NEXT(t, link);
+ }
+ }
+ }
+
+ /* Remove orphaned NSECs and RRSIG NSECs. */
+ for (t = ISC_LIST_HEAD(diffnames.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ CHECK(non_nsec_rrset_exists(db, newver, &t->name, &flag));
+ if (! flag) {
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_any, 0,
+ NULL, &sig_diff));
+ }
+ }
+
+ /*
+ * When a name is created or deleted, its predecessor needs to
+ * have its NSEC updated.
+ */
+ for (t = ISC_LIST_HEAD(diffnames.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ isc_boolean_t existed, exists;
+ dns_fixedname_t fixedname;
+ dns_name_t *prevname;
+
+ dns_fixedname_init(&fixedname);
+ prevname = dns_fixedname_name(&fixedname);
+
+ CHECK(name_exists(db, oldver, &t->name, &existed));
+ CHECK(name_exists(db, newver, &t->name, &exists));
+ if (exists == existed)
+ continue;
+
+ /*
+ * Find the predecessor.
+ * When names become obscured or unobscured in this update
+ * transaction, we may find the wrong predecessor because
+ * the NSECs have not yet been updated to reflect the delegation
+ * change. This should not matter because in this case,
+ * the correct predecessor is either the delegation node or
+ * a newly unobscured node, and those nodes are on the
+ * "affected" list in any case.
+ */
+ CHECK(next_active(client, zone, db, newver,
+ &t->name, prevname, ISC_FALSE));
+ CHECK(namelist_append_name(&affected, prevname));
+ }
+
+ /*
+ * Find names potentially affected by delegation changes
+ * (obscured by adding an NS or DNAME, or unobscured by
+ * removing one).
+ */
+ for (t = ISC_LIST_HEAD(diffnames.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ isc_boolean_t ns_existed, dname_existed;
+ isc_boolean_t ns_exists, dname_exists;
+
+ CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_ns, 0,
+ &ns_existed));
+ CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_dname, 0,
+ &dname_existed));
+ CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0,
+ &ns_exists));
+ CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0,
+ &dname_exists));
+ if ((ns_exists || dname_exists) == (ns_existed || dname_existed))
+ continue;
+ /*
+ * There was a delegation change. Mark all subdomains
+ * of t->name as potentially needing a NSEC update.
+ */
+ CHECK(namelist_append_subdomain(db, &t->name, &affected));
+ }
+
+ ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link);
+ INSIST(ISC_LIST_EMPTY(diffnames.tuples));
+
+ CHECK(uniqify_name_list(&affected));
+
+ /*
+ * Determine which names should have NSECs, and delete/create
+ * NSECs to make it so. We don't know the final NSEC targets yet,
+ * so we just create placeholder NSECs with arbitrary contents
+ * to indicate that their respective owner names should be part of
+ * the NSEC chain.
+ */
+ for (t = ISC_LIST_HEAD(affected.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ isc_boolean_t exists;
+ CHECK(name_exists(db, newver, &t->name, &exists));
+ if (! exists)
+ continue;
+ CHECK(is_glue(db, newver, &t->name, &flag));
+ if (flag) {
+ /*
+ * This name is obscured. Delete any
+ * existing NSEC record.
+ */
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_nsec, 0,
+ NULL, &nsec_diff));
+ } else {
+ /*
+ * This name is not obscured. It should have a NSEC.
+ */
+ CHECK(rrset_exists(db, newver, &t->name,
+ dns_rdatatype_nsec, 0, &flag));
+ if (! flag)
+ CHECK(add_placeholder_nsec(db, newver, &t->name,
+ diff));
+ }
+ }
+
+ /*
+ * Now we know which names are part of the NSEC chain.
+ * Make them all point at their correct targets.
+ */
+ for (t = ISC_LIST_HEAD(affected.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ CHECK(rrset_exists(db, newver, &t->name,
+ dns_rdatatype_nsec, 0, &flag));
+ if (flag) {
+ /*
+ * There is a NSEC, but we don't know if it is correct.
+ * Delete it and create a correct one to be sure.
+ * If the update was unnecessary, the diff minimization
+ * will take care of eliminating it from the journal,
+ * IXFRs, etc.
+ *
+ * The RRSIG bit should always be set in the NSECs
+ * we generate, because they will all get RRSIG NSECs.
+ * (XXX what if the zone keys are missing?).
+ * Because the RRSIG NSECs have not necessarily been
+ * created yet, the correctness of the bit mask relies
+ * on the assumption that NSECs are only created if
+ * there is other data, and if there is other data,
+ * there are other RRSIGs.
+ */
+ CHECK(add_nsec(client, zone, db, newver, &t->name,
+ nsecttl, &nsec_diff));
+ }
+ }
+
+ /*
+ * Minimize the set of NSEC updates so that we don't
+ * have to regenerate the RRSIG NSECs for NSECs that were
+ * replaced with identical ones.
+ */
+ while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(nsec_diff.tuples, t, link);
+ dns_diff_appendminimal(&nsec_mindiff, &t);
+ }
+
+ /* Update RRSIG NSECs. */
+ for (t = ISC_LIST_HEAD(nsec_mindiff.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ if (t->op == DNS_DIFFOP_DEL) {
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_rrsig, dns_rdatatype_nsec,
+ NULL, &sig_diff));
+ } else if (t->op == DNS_DIFFOP_ADD) {
+ CHECK(add_sigs(db, newver, &t->name, dns_rdatatype_nsec,
+ &sig_diff, zone_keys, nkeys,
+ client->mctx, inception, expire,
+ check_ksk));
+ } else {
+ INSIST(0);
+ }
+ }
+
+ /* Record our changes for the journal. */
+ while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(sig_diff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+ while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+
+ INSIST(ISC_LIST_EMPTY(sig_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(nsec_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples));
+
+ failure:
+ dns_diff_clear(&sig_diff);
+ dns_diff_clear(&nsec_diff);
+ dns_diff_clear(&nsec_mindiff);
+
+ dns_diff_clear(&affected);
+ dns_diff_clear(&diffnames);
+
+ for (i = 0; i < nkeys; i++)
+ dst_key_free(&zone_keys[i]);
+
+ return (result);
+}
+
+
+/**************************************************************************/
+/*%
+ * The actual update code in all its glory. We try to follow
+ * the RFC2136 pseudocode as closely as possible.
+ */
+
+static isc_result_t
+send_update_event(ns_client_t *client, dns_zone_t *zone) {
+ isc_result_t result = ISC_R_SUCCESS;
+ update_event_t *event = NULL;
+ isc_task_t *zonetask = NULL;
+ ns_client_t *evclient;
+
+ event = (update_event_t *)
+ isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
+ update_action, NULL, sizeof(*event));
+ if (event == NULL)
+ FAIL(ISC_R_NOMEMORY);
+ event->zone = zone;
+ event->result = ISC_R_SUCCESS;
+
+ evclient = NULL;
+ ns_client_attach(client, &evclient);
+ INSIST(client->nupdates == 0);
+ client->nupdates++;
+ event->ev_arg = evclient;
+
+ dns_zone_gettask(zone, &zonetask);
+ isc_task_send(zonetask, ISC_EVENT_PTR(&event));
+
+ failure:
+ if (event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&event));
+ return (result);
+}
+
+static void
+respond(ns_client_t *client, isc_result_t result) {
+ isc_result_t msg_result;
+
+ msg_result = dns_message_reply(client->message, ISC_TRUE);
+ if (msg_result != ISC_R_SUCCESS)
+ goto msg_failure;
+ client->message->rcode = dns_result_torcode(result);
+
+ ns_client_send(client);
+ return;
+
+ msg_failure:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
+ ISC_LOG_ERROR,
+ "could not create update response message: %s",
+ isc_result_totext(msg_result));
+ ns_client_next(client, msg_result);
+}
+
+void
+ns_update_start(ns_client_t *client, isc_result_t sigresult) {
+ dns_message_t *request = client->message;
+ isc_result_t result;
+ dns_name_t *zonename;
+ dns_rdataset_t *zone_rdataset;
+ dns_zone_t *zone = NULL;
+
+ /*
+ * Interpret the zone section.
+ */
+ result = dns_message_firstname(request, DNS_SECTION_ZONE);
+ if (result != ISC_R_SUCCESS)
+ FAILC(DNS_R_FORMERR,
+ "update zone section empty");
+
+ /*
+ * The zone section must contain exactly one "question", and
+ * it must be of type SOA.
+ */
+ zonename = NULL;
+ dns_message_currentname(request, DNS_SECTION_ZONE, &zonename);
+ zone_rdataset = ISC_LIST_HEAD(zonename->list);
+ if (zone_rdataset->type != dns_rdatatype_soa)
+ FAILC(DNS_R_FORMERR,
+ "update zone section contains non-SOA");
+ if (ISC_LIST_NEXT(zone_rdataset, link) != NULL)
+ FAILC(DNS_R_FORMERR,
+ "update zone section contains multiple RRs");
+
+ /* The zone section must have exactly one name. */
+ result = dns_message_nextname(request, DNS_SECTION_ZONE);
+ if (result != ISC_R_NOMORE)
+ FAILC(DNS_R_FORMERR,
+ "update zone section contains multiple RRs");
+
+ result = dns_zt_find(client->view->zonetable, zonename, 0, NULL,
+ &zone);
+ if (result != ISC_R_SUCCESS)
+ FAILC(DNS_R_NOTAUTH,
+ "not authoritative for update zone");
+
+ switch(dns_zone_gettype(zone)) {
+ case dns_zone_master:
+ /*
+ * We can now fail due to a bad signature as we now know
+ * that we are the master.
+ */
+ if (sigresult != ISC_R_SUCCESS)
+ FAIL(sigresult);
+ CHECK(send_update_event(client, zone));
+ break;
+ case dns_zone_slave:
+ CHECK(checkupdateacl(client, dns_zone_getforwardacl(zone),
+ "update forwarding", zonename, ISC_TRUE));
+ CHECK(send_forward_event(client, zone));
+ break;
+ default:
+ FAILC(DNS_R_NOTAUTH,
+ "not authoritative for update zone");
+ }
+ return;
+
+ failure:
+ /*
+ * We failed without having sent an update event to the zone.
+ * We are still in the client task context, so we can
+ * simply give an error response without switching tasks.
+ */
+ respond(client, result);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+}
+
+/*%
+ * DS records are not allowed to exist without corresponding NS records,
+ * draft-ietf-dnsext-delegation-signer-11.txt, 2.2 Protocol Change,
+ * "DS RRsets MUST NOT appear at non-delegation points or at a zone's apex".
+ */
+
+static isc_result_t
+remove_orphaned_ds(dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) {
+ isc_result_t result;
+ isc_boolean_t ns_exists, ds_exists;
+ dns_difftuple_t *t;
+
+ for (t = ISC_LIST_HEAD(diff->tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link)) {
+ if (t->op != DNS_DIFFOP_ADD ||
+ t->rdata.type != dns_rdatatype_ns)
+ continue;
+ CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0,
+ &ns_exists));
+ if (ns_exists)
+ continue;
+ CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ds, 0,
+ &ds_exists));
+ if (!ds_exists)
+ continue;
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_ds, 0, NULL, diff));
+ }
+ return (ISC_R_SUCCESS);
+
+ failure:
+ return (result);
+}
+
+/*
+ * This implements the post load integrity checks for mx records.
+ */
+static isc_result_t
+check_mx(ns_client_t *client, dns_zone_t *zone,
+ dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff)
+{
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")];
+ char ownerbuf[DNS_NAME_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char altbuf[DNS_NAME_FORMATSIZE];
+ dns_difftuple_t *t;
+ dns_fixedname_t fixed;
+ dns_name_t *foundname;
+ dns_rdata_mx_t mx;
+ dns_rdata_t rdata;
+ isc_boolean_t ok = ISC_TRUE;
+ isc_boolean_t isaddress;
+ isc_result_t result;
+ struct in6_addr addr6;
+ struct in_addr addr;
+ unsigned int options;
+
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
+ dns_rdata_init(&rdata);
+ options = dns_zone_getoptions(zone);
+
+ for (t = ISC_LIST_HEAD(diff->tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link)) {
+ if (t->op != DNS_DIFFOP_ADD ||
+ t->rdata.type != dns_rdatatype_mx)
+ continue;
+
+ result = dns_rdata_tostruct(&t->rdata, &mx, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ /*
+ * Check if we will error out if we attempt to reload the
+ * zone.
+ */
+ dns_name_format(&mx.mx, namebuf, sizeof(namebuf));
+ dns_name_format(&t->name, ownerbuf, sizeof(ownerbuf));
+ isaddress = ISC_FALSE;
+ if ((options & DNS_RDATA_CHECKMX) != 0 &&
+ strlcpy(tmp, namebuf, sizeof(tmp)) < sizeof(tmp)) {
+ if (tmp[strlen(tmp) - 1] == '.')
+ tmp[strlen(tmp) - 1] = '\0';
+ if (inet_aton(tmp, &addr) == 1 ||
+ inet_pton(AF_INET6, tmp, &addr6) == 1)
+ isaddress = ISC_TRUE;
+ }
+
+ if (isaddress && (options & DNS_RDATA_CHECKMXFAIL) != 0) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX: '%s': %s",
+ ownerbuf, namebuf,
+ dns_result_totext(DNS_R_MXISADDRESS));
+ ok = ISC_FALSE;
+ } else if (isaddress) {
+ update_log(client, zone, ISC_LOG_WARNING,
+ "%s/MX: warning: '%s': %s",
+ ownerbuf, namebuf,
+ dns_result_totext(DNS_R_MXISADDRESS));
+ }
+
+ /*
+ * Check zone integrity checks.
+ */
+ if ((options & DNS_ZONEOPT_CHECKINTEGRITY) == 0)
+ continue;
+ result = dns_db_find(db, &mx.mx, newver, dns_rdatatype_a,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ continue;
+
+ if (result == DNS_R_NXRRSET) {
+ result = dns_db_find(db, &mx.mx, newver,
+ dns_rdatatype_aaaa,
+ 0, 0, NULL, foundname,
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ continue;
+ }
+
+ if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX '%s' has no address records "
+ "(A or AAAA)", ownerbuf, namebuf);
+ ok = ISC_FALSE;
+ } else if (result == DNS_R_CNAME) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX '%s' is a CNAME (illegal)",
+ ownerbuf, namebuf);
+ ok = ISC_FALSE;
+ } else if (result == DNS_R_DNAME) {
+ dns_name_format(foundname, altbuf, sizeof altbuf);
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX '%s' is below a DNAME '%s' (illegal)",
+ ownerbuf, namebuf, altbuf);
+ ok = ISC_FALSE;
+ }
+ }
+ return (ok ? ISC_R_SUCCESS : DNS_R_REFUSED);
+}
+
+static void
+update_action(isc_task_t *task, isc_event_t *event) {
+ update_event_t *uev = (update_event_t *) event;
+ dns_zone_t *zone = uev->zone;
+ ns_client_t *client = (ns_client_t *)event->ev_arg;
+
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *oldver = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff; /* Pending updates. */
+ dns_diff_t temp; /* Pending RR existence assertions. */
+ isc_boolean_t soa_serial_changed = ISC_FALSE;
+ isc_mem_t *mctx = client->mctx;
+ dns_rdatatype_t covers;
+ dns_message_t *request = client->message;
+ dns_rdataclass_t zoneclass;
+ dns_name_t *zonename;
+ dns_ssutable_t *ssutable = NULL;
+ dns_fixedname_t tmpnamefixed;
+ dns_name_t *tmpname = NULL;
+ unsigned int options;
+
+ INSIST(event->ev_type == DNS_EVENT_UPDATE);
+
+ dns_diff_init(mctx, &diff);
+ dns_diff_init(mctx, &temp);
+
+ CHECK(dns_zone_getdb(zone, &db));
+ zonename = dns_db_origin(db);
+ zoneclass = dns_db_class(db);
+ dns_zone_getssutable(zone, &ssutable);
+ dns_db_currentversion(db, &oldver);
+ CHECK(dns_db_newversion(db, &ver));
+
+ /*
+ * Check prerequisites.
+ */
+
+ for (result = dns_message_firstname(request, DNS_SECTION_PREREQUISITE);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(request, DNS_SECTION_PREREQUISITE))
+ {
+ dns_name_t *name = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_ttl_t ttl;
+ dns_rdataclass_t update_class;
+ isc_boolean_t flag;
+
+ get_current_rr(request, DNS_SECTION_PREREQUISITE, zoneclass,
+ &name, &rdata, &covers, &ttl, &update_class);
+
+ if (ttl != 0)
+ FAILC(DNS_R_FORMERR, "prerequisite TTL is not zero");
+
+ if (! dns_name_issubdomain(name, zonename))
+ FAILN(DNS_R_NOTZONE, name,
+ "prerequisite name is out of zone");
+
+ if (update_class == dns_rdataclass_any) {
+ if (rdata.length != 0)
+ FAILC(DNS_R_FORMERR,
+ "class ANY prerequisite "
+ "RDATA is not empty");
+ if (rdata.type == dns_rdatatype_any) {
+ CHECK(name_exists(db, ver, name, &flag));
+ if (! flag) {
+ FAILN(DNS_R_NXDOMAIN, name,
+ "'name in use' prerequisite "
+ "not satisfied");
+ }
+ } else {
+ CHECK(rrset_exists(db, ver, name,
+ rdata.type, covers, &flag));
+ if (! flag) {
+ /* RRset does not exist. */
+ FAILNT(DNS_R_NXRRSET, name, rdata.type,
+ "'rrset exists (value independent)' "
+ "prerequisite not satisfied");
+ }
+ }
+ } else if (update_class == dns_rdataclass_none) {
+ if (rdata.length != 0)
+ FAILC(DNS_R_FORMERR,
+ "class NONE prerequisite "
+ "RDATA is not empty");
+ if (rdata.type == dns_rdatatype_any) {
+ CHECK(name_exists(db, ver, name, &flag));
+ if (flag) {
+ FAILN(DNS_R_YXDOMAIN, name,
+ "'name not in use' prerequisite "
+ "not satisfied");
+ }
+ } else {
+ CHECK(rrset_exists(db, ver, name,
+ rdata.type, covers, &flag));
+ if (flag) {
+ /* RRset exists. */
+ FAILNT(DNS_R_YXRRSET, name, rdata.type,
+ "'rrset does not exist' "
+ "prerequisite not satisfied");
+ }
+ }
+ } else if (update_class == zoneclass) {
+ /* "temp<rr.name, rr.type> += rr;" */
+ result = temp_append(&temp, name, &rdata);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "temp entry creation failed: %s",
+ dns_result_totext(result));
+ FAIL(ISC_R_UNEXPECTED);
+ }
+ } else {
+ FAILC(DNS_R_FORMERR, "malformed prerequisite");
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ FAIL(result);
+
+
+ /*
+ * Perform the final check of the "rrset exists (value dependent)"
+ * prerequisites.
+ */
+ if (ISC_LIST_HEAD(temp.tuples) != NULL) {
+ dns_rdatatype_t type;
+
+ /*
+ * Sort the prerequisite records by owner name,
+ * type, and rdata.
+ */
+ result = dns_diff_sort(&temp, temp_order);
+ if (result != ISC_R_SUCCESS)
+ FAILC(result, "'RRset exists (value dependent)' "
+ "prerequisite not satisfied");
+
+ dns_fixedname_init(&tmpnamefixed);
+ tmpname = dns_fixedname_name(&tmpnamefixed);
+ result = temp_check(mctx, &temp, db, ver, tmpname, &type);
+ if (result != ISC_R_SUCCESS)
+ FAILNT(result, tmpname, type,
+ "'RRset exists (value dependent)' "
+ "prerequisite not satisfied");
+ }
+
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "prerequisites are OK");
+
+ /*
+ * Check Requestor's Permissions. It seems a bit silly to do this
+ * only after prerequisite testing, but that is what RFC2136 says.
+ */
+ result = ISC_R_SUCCESS;
+ if (ssutable == NULL)
+ CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone),
+ "update", zonename, ISC_FALSE));
+ else if (client->signer == NULL)
+ CHECK(checkupdateacl(client, NULL, "update", zonename,
+ ISC_FALSE));
+
+ if (dns_zone_getupdatedisabled(zone))
+ FAILC(DNS_R_REFUSED, "dynamic update temporarily disabled");
+
+ /*
+ * Perform the Update Section Prescan.
+ */
+
+ for (result = dns_message_firstname(request, DNS_SECTION_UPDATE);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(request, DNS_SECTION_UPDATE))
+ {
+ dns_name_t *name = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_ttl_t ttl;
+ dns_rdataclass_t update_class;
+ get_current_rr(request, DNS_SECTION_UPDATE, zoneclass,
+ &name, &rdata, &covers, &ttl, &update_class);
+
+ if (! dns_name_issubdomain(name, zonename))
+ FAILC(DNS_R_NOTZONE,
+ "update RR is outside zone");
+ if (update_class == zoneclass) {
+ /*
+ * Check for meta-RRs. The RFC2136 pseudocode says
+ * check for ANY|AXFR|MAILA|MAILB, but the text adds
+ * "or any other QUERY metatype"
+ */
+ if (dns_rdatatype_ismeta(rdata.type)) {
+ FAILC(DNS_R_FORMERR,
+ "meta-RR in update");
+ }
+ result = dns_zone_checknames(zone, name, &rdata);
+ if (result != ISC_R_SUCCESS)
+ FAIL(DNS_R_REFUSED);
+ } else if (update_class == dns_rdataclass_any) {
+ if (ttl != 0 || rdata.length != 0 ||
+ (dns_rdatatype_ismeta(rdata.type) &&
+ rdata.type != dns_rdatatype_any))
+ FAILC(DNS_R_FORMERR,
+ "meta-RR in update");
+ } else if (update_class == dns_rdataclass_none) {
+ if (ttl != 0 ||
+ dns_rdatatype_ismeta(rdata.type))
+ FAILC(DNS_R_FORMERR,
+ "meta-RR in update");
+ } else {
+ update_log(client, zone, ISC_LOG_WARNING,
+ "update RR has incorrect class %d",
+ update_class);
+ FAIL(DNS_R_FORMERR);
+ }
+ /*
+ * draft-ietf-dnsind-simple-secure-update-01 says
+ * "Unlike traditional dynamic update, the client
+ * is forbidden from updating NSEC records."
+ */
+ if (dns_db_issecure(db)) {
+ if (rdata.type == dns_rdatatype_nsec) {
+ FAILC(DNS_R_REFUSED,
+ "explicit NSEC updates are not allowed "
+ "in secure zones");
+ }
+ else if (rdata.type == dns_rdatatype_rrsig) {
+ FAILC(DNS_R_REFUSED,
+ "explicit RRSIG updates are currently not "
+ "supported in secure zones");
+ }
+ }
+
+ if (ssutable != NULL && client->signer != NULL) {
+ if (rdata.type != dns_rdatatype_any) {
+ if (!dns_ssutable_checkrules(ssutable,
+ client->signer,
+ name, rdata.type))
+ FAILC(DNS_R_REFUSED,
+ "rejected by secure update");
+ }
+ else {
+ if (!ssu_checkall(db, ver, name, ssutable,
+ client->signer))
+ FAILC(DNS_R_REFUSED,
+ "rejected by secure update");
+ }
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ FAIL(result);
+
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "update section prescan OK");
+
+ /*
+ * Process the Update Section.
+ */
+
+ options = dns_zone_getoptions(zone);
+ for (result = dns_message_firstname(request, DNS_SECTION_UPDATE);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(request, DNS_SECTION_UPDATE))
+ {
+ dns_name_t *name = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_ttl_t ttl;
+ dns_rdataclass_t update_class;
+ isc_boolean_t flag;
+
+ get_current_rr(request, DNS_SECTION_UPDATE, zoneclass,
+ &name, &rdata, &covers, &ttl, &update_class);
+
+ if (update_class == zoneclass) {
+
+ /*
+ * RFC1123 doesn't allow MF and MD in master zones. */
+ if (rdata.type == dns_rdatatype_md ||
+ rdata.type == dns_rdatatype_mf) {
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_rdatatype_format(rdata.type, typebuf,
+ sizeof(typebuf));
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "attempt to add %s ignored",
+ typebuf);
+ continue;
+ }
+ if (rdata.type == dns_rdatatype_ns &&
+ dns_name_iswildcard(name)) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to add wildcard NS record"
+ "ignored");
+ continue;
+ }
+ if (rdata.type == dns_rdatatype_cname) {
+ CHECK(cname_incompatible_rrset_exists(db, ver,
+ name,
+ &flag));
+ if (flag) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to add CNAME "
+ "alongside non-CNAME "
+ "ignored");
+ continue;
+ }
+ } else {
+ CHECK(rrset_exists(db, ver, name,
+ dns_rdatatype_cname, 0,
+ &flag));
+ if (flag &&
+ ! dns_rdatatype_isdnssec(rdata.type))
+ {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to add non-CNAME "
+ "alongside CNAME ignored");
+ continue;
+ }
+ }
+ if (rdata.type == dns_rdatatype_soa) {
+ isc_boolean_t ok;
+ CHECK(rrset_exists(db, ver, name,
+ dns_rdatatype_soa, 0,
+ &flag));
+ if (! flag) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to create 2nd "
+ "SOA ignored");
+ continue;
+ }
+ CHECK(check_soa_increment(db, ver, &rdata,
+ &ok));
+ if (! ok) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "SOA update failed to "
+ "increment serial, "
+ "ignoring it");
+ continue;
+ }
+ soa_serial_changed = ISC_TRUE;
+ }
+ if ((options & DNS_ZONEOPT_CHECKWILDCARD) != 0 &&
+ dns_name_internalwildcard(name)) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namestr,
+ sizeof(namestr));
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "warning: ownername '%s' contains "
+ "a non-terminal wildcard", namestr);
+ }
+
+ if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char typestr[DNS_RDATATYPE_FORMATSIZE];
+ dns_name_format(name, namestr,
+ sizeof(namestr));
+ dns_rdatatype_format(rdata.type, typestr,
+ sizeof(typestr));
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "adding an RR at '%s' %s",
+ namestr, typestr);
+ }
+
+ /* Prepare the affected RRset for the addition. */
+ {
+ add_rr_prepare_ctx_t ctx;
+ ctx.db = db;
+ ctx.ver = ver;
+ ctx.diff = &diff;
+ ctx.name = name;
+ ctx.update_rr = &rdata;
+ ctx.update_rr_ttl = ttl;
+ ctx.ignore_add = ISC_FALSE;
+ dns_diff_init(mctx, &ctx.del_diff);
+ dns_diff_init(mctx, &ctx.add_diff);
+ CHECK(foreach_rr(db, ver, name, rdata.type,
+ covers, add_rr_prepare_action,
+ &ctx));
+
+ if (ctx.ignore_add) {
+ dns_diff_clear(&ctx.del_diff);
+ dns_diff_clear(&ctx.add_diff);
+ } else {
+ CHECK(do_diff(&ctx.del_diff, db, ver, &diff));
+ CHECK(do_diff(&ctx.add_diff, db, ver, &diff));
+ CHECK(update_one_rr(db, ver, &diff,
+ DNS_DIFFOP_ADD,
+ name, ttl, &rdata));
+ }
+ }
+ } else if (update_class == dns_rdataclass_any) {
+ if (rdata.type == dns_rdatatype_any) {
+ if (isc_log_wouldlog(ns_g_lctx,
+ LOGLEVEL_PROTOCOL))
+ {
+ char namestr[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namestr,
+ sizeof(namestr));
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "delete all rrsets from "
+ "name '%s'", namestr);
+ }
+ if (dns_name_equal(name, zonename)) {
+ CHECK(delete_if(type_not_soa_nor_ns_p,
+ db, ver, name,
+ dns_rdatatype_any, 0,
+ &rdata, &diff));
+ } else {
+ CHECK(delete_if(type_not_dnssec,
+ db, ver, name,
+ dns_rdatatype_any, 0,
+ &rdata, &diff));
+ }
+ } else if (dns_name_equal(name, zonename) &&
+ (rdata.type == dns_rdatatype_soa ||
+ rdata.type == dns_rdatatype_ns)) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to delete all SOA "
+ "or NS records ignored");
+ continue;
+ } else {
+ if (isc_log_wouldlog(ns_g_lctx,
+ LOGLEVEL_PROTOCOL))
+ {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char typestr[DNS_RDATATYPE_FORMATSIZE];
+ dns_name_format(name, namestr,
+ sizeof(namestr));
+ dns_rdatatype_format(rdata.type,
+ typestr,
+ sizeof(typestr));
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "deleting rrset at '%s' %s",
+ namestr, typestr);
+ }
+ CHECK(delete_if(true_p, db, ver, name,
+ rdata.type, covers, &rdata,
+ &diff));
+ }
+ } else if (update_class == dns_rdataclass_none) {
+ /*
+ * The (name == zonename) condition appears in
+ * RFC2136 3.4.2.4 but is missing from the pseudocode.
+ */
+ if (dns_name_equal(name, zonename)) {
+ if (rdata.type == dns_rdatatype_soa) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to delete SOA "
+ "ignored");
+ continue;
+ }
+ if (rdata.type == dns_rdatatype_ns) {
+ int count;
+ CHECK(rr_count(db, ver, name,
+ dns_rdatatype_ns,
+ 0, &count));
+ if (count == 1) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to "
+ "delete last "
+ "NS ignored");
+ continue;
+ }
+ }
+ }
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "deleting an RR");
+ CHECK(delete_if(rr_equal_p, db, ver, name,
+ rdata.type, covers, &rdata, &diff));
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ FAIL(result);
+
+ /*
+ * If any changes were made, increment the SOA serial number,
+ * update RRSIGs and NSECs (if zone is secure), and write the update
+ * to the journal.
+ */
+ if (! ISC_LIST_EMPTY(diff.tuples)) {
+ char *journalfile;
+ dns_journal_t *journal;
+
+ /*
+ * Increment the SOA serial, but only if it was not
+ * changed as a result of an update operation.
+ */
+ if (! soa_serial_changed) {
+ CHECK(increment_soa_serial(db, ver, &diff, mctx));
+ }
+
+ CHECK(check_mx(client, zone, db, ver, &diff));
+
+ CHECK(remove_orphaned_ds(db, ver, &diff));
+
+ if (dns_db_issecure(db)) {
+ result = update_signatures(client, zone, db, oldver,
+ ver, &diff,
+ dns_zone_getsigvalidityinterval(zone));
+ if (result != ISC_R_SUCCESS) {
+ update_log(client, zone,
+ ISC_LOG_ERROR,
+ "RRSIG/NSEC update failed: %s",
+ isc_result_totext(result));
+ goto failure;
+ }
+ }
+
+ journalfile = dns_zone_getjournal(zone);
+ if (journalfile != NULL) {
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "writing journal %s", journalfile);
+
+ journal = NULL;
+ result = dns_journal_open(mctx, journalfile,
+ ISC_TRUE, &journal);
+ if (result != ISC_R_SUCCESS)
+ FAILS(result, "journal open failed");
+
+ result = dns_journal_write_transaction(journal, &diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_journal_destroy(&journal);
+ FAILS(result, "journal write failed");
+ }
+
+ dns_journal_destroy(&journal);
+ }
+
+ /*
+ * XXXRTH Just a note that this committing code will have
+ * to change to handle databases that need two-phase
+ * commit, but this isn't a priority.
+ */
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "committing update transaction");
+ dns_db_closeversion(db, &ver, ISC_TRUE);
+
+ /*
+ * Mark the zone as dirty so that it will be written to disk.
+ */
+ dns_zone_markdirty(zone);
+
+ /*
+ * Notify slaves of the change we just made.
+ */
+ dns_zone_notify(zone);
+ } else {
+ update_log(client, zone, LOGLEVEL_DEBUG, "redundant request");
+ dns_db_closeversion(db, &ver, ISC_TRUE);
+ }
+ result = ISC_R_SUCCESS;
+ goto common;
+
+ failure:
+ /*
+ * The reason for failure should have been logged at this point.
+ */
+ if (ver != NULL) {
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "rolling back");
+ dns_db_closeversion(db, &ver, ISC_FALSE);
+ }
+
+ common:
+ dns_diff_clear(&temp);
+ dns_diff_clear(&diff);
+
+ if (oldver != NULL)
+ dns_db_closeversion(db, &oldver, ISC_FALSE);
+
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ if (ssutable != NULL)
+ dns_ssutable_detach(&ssutable);
+
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ isc_task_detach(&task);
+ uev->result = result;
+ uev->ev_type = DNS_EVENT_UPDATEDONE;
+ uev->ev_action = updatedone_action;
+ isc_task_send(client->task, &event);
+ INSIST(event == NULL);
+}
+
+static void
+updatedone_action(isc_task_t *task, isc_event_t *event) {
+ update_event_t *uev = (update_event_t *) event;
+ ns_client_t *client = (ns_client_t *) event->ev_arg;
+
+ UNUSED(task);
+
+ INSIST(event->ev_type == DNS_EVENT_UPDATEDONE);
+ INSIST(task == client->task);
+
+ INSIST(client->nupdates > 0);
+ client->nupdates--;
+ respond(client, uev->result);
+ isc_event_free(&event);
+ ns_client_detach(&client);
+}
+
+/*%
+ * Update forwarding support.
+ */
+
+static void
+forward_fail(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client = (ns_client_t *)event->ev_arg;
+
+ UNUSED(task);
+
+ INSIST(client->nupdates > 0);
+ client->nupdates--;
+ respond(client, DNS_R_SERVFAIL);
+ isc_event_free(&event);
+ ns_client_detach(&client);
+}
+
+
+static void
+forward_callback(void *arg, isc_result_t result, dns_message_t *answer) {
+ update_event_t *uev = arg;
+ ns_client_t *client = uev->ev_arg;
+
+ if (result != ISC_R_SUCCESS) {
+ INSIST(answer == NULL);
+ uev->ev_type = DNS_EVENT_UPDATEDONE;
+ uev->ev_action = forward_fail;
+ } else {
+ uev->ev_type = DNS_EVENT_UPDATEDONE;
+ uev->ev_action = forward_done;
+ uev->answer = answer;
+ }
+ isc_task_send(client->task, ISC_EVENT_PTR(&uev));
+}
+
+static void
+forward_done(isc_task_t *task, isc_event_t *event) {
+ update_event_t *uev = (update_event_t *) event;
+ ns_client_t *client = (ns_client_t *)event->ev_arg;
+
+ UNUSED(task);
+
+ INSIST(client->nupdates > 0);
+ client->nupdates--;
+ ns_client_sendraw(client, uev->answer);
+ dns_message_destroy(&uev->answer);
+ isc_event_free(&event);
+ ns_client_detach(&client);
+}
+
+static void
+forward_action(isc_task_t *task, isc_event_t *event) {
+ update_event_t *uev = (update_event_t *) event;
+ dns_zone_t *zone = uev->zone;
+ ns_client_t *client = (ns_client_t *)event->ev_arg;
+ isc_result_t result;
+
+ result = dns_zone_forwardupdate(zone, client->message,
+ forward_callback, event);
+ if (result != ISC_R_SUCCESS) {
+ uev->ev_type = DNS_EVENT_UPDATEDONE;
+ uev->ev_action = forward_fail;
+ isc_task_send(client->task, &event);
+ }
+ dns_zone_detach(&zone);
+ isc_task_detach(&task);
+}
+
+static isc_result_t
+send_forward_event(ns_client_t *client, dns_zone_t *zone) {
+ isc_result_t result = ISC_R_SUCCESS;
+ update_event_t *event = NULL;
+ isc_task_t *zonetask = NULL;
+ ns_client_t *evclient;
+
+ event = (update_event_t *)
+ isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
+ forward_action, NULL, sizeof(*event));
+ if (event == NULL)
+ FAIL(ISC_R_NOMEMORY);
+ event->zone = zone;
+ event->result = ISC_R_SUCCESS;
+
+ evclient = NULL;
+ ns_client_attach(client, &evclient);
+ INSIST(client->nupdates == 0);
+ client->nupdates++;
+ event->ev_arg = evclient;
+
+ dns_zone_gettask(zone, &zonetask);
+ isc_task_send(zonetask, ISC_EVENT_PTR(&event));
+
+ failure:
+ if (event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&event));
+ return (result);
+}
diff --git a/bin/named/xfrout.c b/bin/named/xfrout.c
new file mode 100644
index 0000000..9fe90a2
--- /dev/null
+++ b/bin/named/xfrout.c
@@ -0,0 +1,1810 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: xfrout.c,v 1.115.18.8 2006/03/05 23:58:51 marka Exp $ */
+
+#include <config.h>
+
+#include <isc/formatcheck.h>
+#include <isc/mem.h>
+#include <isc/timer.h>
+#include <isc/print.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#ifdef DLZ
+#include <dns/dlz.h>
+#endif
+#include <dns/fixedname.h>
+#include <dns/journal.h>
+#include <dns/message.h>
+#include <dns/peer.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+#include <dns/soa.h>
+#include <dns/timer.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <named/client.h>
+#include <named/log.h>
+#include <named/server.h>
+#include <named/xfrout.h>
+
+/*! \file
+ * \brief
+ * Outgoing AXFR and IXFR.
+ */
+
+/*
+ * TODO:
+ * - IXFR over UDP
+ */
+
+#define XFROUT_COMMON_LOGARGS \
+ ns_g_lctx, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT
+
+#define XFROUT_PROTOCOL_LOGARGS \
+ XFROUT_COMMON_LOGARGS, ISC_LOG_INFO
+
+#define XFROUT_DEBUG_LOGARGS(n) \
+ XFROUT_COMMON_LOGARGS, ISC_LOG_DEBUG(n)
+
+#define XFROUT_RR_LOGARGS \
+ XFROUT_COMMON_LOGARGS, XFROUT_RR_LOGLEVEL
+
+#define XFROUT_RR_LOGLEVEL ISC_LOG_DEBUG(8)
+
+/*%
+ * Fail unconditionally and log as a client error.
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAILC(code, msg) \
+ do { \
+ result = (code); \
+ ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
+ NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
+ "bad zone transfer request: %s (%s)", \
+ msg, isc_result_totext(code)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define FAILQ(code, msg, question, rdclass) \
+ do { \
+ char _buf1[DNS_NAME_FORMATSIZE]; \
+ char _buf2[DNS_RDATACLASS_FORMATSIZE]; \
+ result = (code); \
+ dns_name_format(question, _buf1, sizeof(_buf1)); \
+ dns_rdataclass_format(rdclass, _buf2, sizeof(_buf2)); \
+ ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
+ NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
+ "bad zone transfer request: '%s/%s': %s (%s)", \
+ _buf1, _buf2, msg, isc_result_totext(code)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/**************************************************************************/
+/*%
+ * A db_rr_iterator_t is an iterator that iterates over an entire database,
+ * returning one RR at a time, in some arbitrary order.
+ */
+
+typedef struct db_rr_iterator db_rr_iterator_t;
+
+/*% db_rr_iterator structure */
+struct db_rr_iterator {
+ isc_result_t result;
+ dns_db_t *db;
+ dns_dbiterator_t *dbit;
+ dns_dbversion_t *ver;
+ isc_stdtime_t now;
+ dns_dbnode_t *node;
+ dns_fixedname_t fixedname;
+ dns_rdatasetiter_t *rdatasetit;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata;
+};
+
+static isc_result_t
+db_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
+ isc_stdtime_t now);
+
+static isc_result_t
+db_rr_iterator_first(db_rr_iterator_t *it);
+
+static isc_result_t
+db_rr_iterator_next(db_rr_iterator_t *it);
+
+static void
+db_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name,
+ isc_uint32_t *ttl, dns_rdata_t **rdata);
+
+static void
+db_rr_iterator_destroy(db_rr_iterator_t *it);
+
+static isc_result_t
+db_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
+ isc_stdtime_t now)
+{
+ isc_result_t result;
+ it->db = db;
+ it->dbit = NULL;
+ it->ver = ver;
+ it->now = now;
+ it->node = NULL;
+ result = dns_db_createiterator(it->db, ISC_FALSE, &it->dbit);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ it->rdatasetit = NULL;
+ dns_rdata_init(&it->rdata);
+ dns_rdataset_init(&it->rdataset);
+ dns_fixedname_init(&it->fixedname);
+ INSIST(! dns_rdataset_isassociated(&it->rdataset));
+ it->result = ISC_R_SUCCESS;
+ return (it->result);
+}
+
+static isc_result_t
+db_rr_iterator_first(db_rr_iterator_t *it) {
+ it->result = dns_dbiterator_first(it->dbit);
+ /*
+ * The top node may be empty when out of zone glue exists.
+ * Walk the tree to find the first node with data.
+ */
+ while (it->result == ISC_R_SUCCESS) {
+ it->result = dns_dbiterator_current(it->dbit, &it->node,
+ dns_fixedname_name(&it->fixedname));
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+
+ it->result = dns_db_allrdatasets(it->db, it->node,
+ it->ver, it->now,
+ &it->rdatasetit);
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+
+ it->result = dns_rdatasetiter_first(it->rdatasetit);
+ if (it->result != ISC_R_SUCCESS) {
+ /*
+ * This node is empty. Try next node.
+ */
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ dns_db_detachnode(it->db, &it->node);
+ it->result = dns_dbiterator_next(it->dbit);
+ continue;
+ }
+ dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
+ it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
+ it->result = dns_rdataset_first(&it->rdataset);
+ return (it->result);
+ }
+ return (it->result);
+}
+
+
+static isc_result_t
+db_rr_iterator_next(db_rr_iterator_t *it) {
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+
+ INSIST(it->dbit != NULL);
+ INSIST(it->node != NULL);
+ INSIST(it->rdatasetit != NULL);
+
+ it->result = dns_rdataset_next(&it->rdataset);
+ if (it->result == ISC_R_NOMORE) {
+ dns_rdataset_disassociate(&it->rdataset);
+ it->result = dns_rdatasetiter_next(it->rdatasetit);
+ /*
+ * The while loop body is executed more than once
+ * only when an empty dbnode needs to be skipped.
+ */
+ while (it->result == ISC_R_NOMORE) {
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ dns_db_detachnode(it->db, &it->node);
+ it->result = dns_dbiterator_next(it->dbit);
+ if (it->result == ISC_R_NOMORE) {
+ /* We are at the end of the entire database. */
+ return (it->result);
+ }
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ it->result = dns_dbiterator_current(it->dbit,
+ &it->node,
+ dns_fixedname_name(&it->fixedname));
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ it->result = dns_db_allrdatasets(it->db, it->node,
+ it->ver, it->now,
+ &it->rdatasetit);
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ it->result = dns_rdatasetiter_first(it->rdatasetit);
+ }
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
+ it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
+ it->result = dns_rdataset_first(&it->rdataset);
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ }
+ return (it->result);
+}
+
+static void
+db_rr_iterator_pause(db_rr_iterator_t *it) {
+ RUNTIME_CHECK(dns_dbiterator_pause(it->dbit) == ISC_R_SUCCESS);
+}
+
+static void
+db_rr_iterator_destroy(db_rr_iterator_t *it) {
+ if (dns_rdataset_isassociated(&it->rdataset))
+ dns_rdataset_disassociate(&it->rdataset);
+ if (it->rdatasetit != NULL)
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ if (it->node != NULL)
+ dns_db_detachnode(it->db, &it->node);
+ dns_dbiterator_destroy(&it->dbit);
+}
+
+static void
+db_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name,
+ isc_uint32_t *ttl, dns_rdata_t **rdata)
+{
+ REQUIRE(name != NULL && *name == NULL);
+ REQUIRE(it->result == ISC_R_SUCCESS);
+ *name = dns_fixedname_name(&it->fixedname);
+ *ttl = it->rdataset.ttl;
+ dns_rdata_reset(&it->rdata);
+ dns_rdataset_current(&it->rdataset, &it->rdata);
+ *rdata = &it->rdata;
+}
+
+/**************************************************************************/
+
+/*% Log an RR (for debugging) */
+
+static void
+log_rr(dns_name_t *name, dns_rdata_t *rdata, isc_uint32_t ttl) {
+ isc_result_t result;
+ isc_buffer_t buf;
+ char mem[2000];
+ dns_rdatalist_t rdl;
+ dns_rdataset_t rds;
+ dns_rdata_t rd = DNS_RDATA_INIT;
+
+ rdl.type = rdata->type;
+ rdl.rdclass = rdata->rdclass;
+ rdl.ttl = ttl;
+ ISC_LIST_INIT(rdl.rdata);
+ ISC_LINK_INIT(&rdl, link);
+ dns_rdataset_init(&rds);
+ dns_rdata_init(&rd);
+ dns_rdata_clone(rdata, &rd);
+ ISC_LIST_APPEND(rdl.rdata, &rd, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(&rdl, &rds) == ISC_R_SUCCESS);
+
+ isc_buffer_init(&buf, mem, sizeof(mem));
+ result = dns_rdataset_totext(&rds, name,
+ ISC_FALSE, ISC_FALSE, &buf);
+
+ /*
+ * We could use xfrout_log(), but that would produce
+ * very long lines with a repetitive prefix.
+ */
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * Get rid of final newline.
+ */
+ INSIST(buf.used >= 1 &&
+ ((char *) buf.base)[buf.used - 1] == '\n');
+ buf.used--;
+
+ isc_log_write(XFROUT_RR_LOGARGS, "%.*s",
+ (int)isc_buffer_usedlength(&buf),
+ (char *)isc_buffer_base(&buf));
+ } else {
+ isc_log_write(XFROUT_RR_LOGARGS, "<RR too large to print>");
+ }
+}
+
+/**************************************************************************/
+/*
+ * An 'rrstream_t' is a polymorphic iterator that returns
+ * a stream of resource records. There are multiple implementations,
+ * e.g. for generating AXFR and IXFR records streams.
+ */
+
+typedef struct rrstream_methods rrstream_methods_t;
+
+typedef struct rrstream {
+ isc_mem_t *mctx;
+ rrstream_methods_t *methods;
+} rrstream_t;
+
+struct rrstream_methods {
+ isc_result_t (*first)(rrstream_t *);
+ isc_result_t (*next)(rrstream_t *);
+ void (*current)(rrstream_t *,
+ dns_name_t **,
+ isc_uint32_t *,
+ dns_rdata_t **);
+ void (*pause)(rrstream_t *);
+ void (*destroy)(rrstream_t **);
+};
+
+static void
+rrstream_noop_pause(rrstream_t *rs) {
+ UNUSED(rs);
+}
+
+/**************************************************************************/
+/*
+ * An 'ixfr_rrstream_t' is an 'rrstream_t' that returns
+ * an IXFR-like RR stream from a journal file.
+ *
+ * The SOA at the beginning of each sequence of additions
+ * or deletions are included in the stream, but the extra
+ * SOAs at the beginning and end of the entire transfer are
+ * not included.
+ */
+
+typedef struct ixfr_rrstream {
+ rrstream_t common;
+ dns_journal_t *journal;
+} ixfr_rrstream_t;
+
+/* Forward declarations. */
+static void
+ixfr_rrstream_destroy(rrstream_t **sp);
+
+static rrstream_methods_t ixfr_rrstream_methods;
+
+/*
+ * Returns: anything dns_journal_open() or dns_journal_iter_init()
+ * may return.
+ */
+
+static isc_result_t
+ixfr_rrstream_create(isc_mem_t *mctx,
+ const char *journal_filename,
+ isc_uint32_t begin_serial,
+ isc_uint32_t end_serial,
+ rrstream_t **sp)
+{
+ ixfr_rrstream_t *s;
+ isc_result_t result;
+
+ INSIST(sp != NULL && *sp == NULL);
+
+ s = isc_mem_get(mctx, sizeof(*s));
+ if (s == NULL)
+ return (ISC_R_NOMEMORY);
+ s->common.mctx = mctx;
+ s->common.methods = &ixfr_rrstream_methods;
+ s->journal = NULL;
+
+ CHECK(dns_journal_open(mctx, journal_filename,
+ ISC_FALSE, &s->journal));
+ CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial));
+
+ *sp = (rrstream_t *) s;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ ixfr_rrstream_destroy((rrstream_t **) (void *)&s);
+ return (result);
+}
+
+static isc_result_t
+ixfr_rrstream_first(rrstream_t *rs) {
+ ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
+ return (dns_journal_first_rr(s->journal));
+}
+
+static isc_result_t
+ixfr_rrstream_next(rrstream_t *rs) {
+ ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
+ return (dns_journal_next_rr(s->journal));
+}
+
+static void
+ixfr_rrstream_current(rrstream_t *rs,
+ dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
+ dns_journal_current_rr(s->journal, name, ttl, rdata);
+}
+
+static void
+ixfr_rrstream_destroy(rrstream_t **rsp) {
+ ixfr_rrstream_t *s = (ixfr_rrstream_t *) *rsp;
+ if (s->journal != 0)
+ dns_journal_destroy(&s->journal);
+ isc_mem_put(s->common.mctx, s, sizeof(*s));
+}
+
+static rrstream_methods_t ixfr_rrstream_methods = {
+ ixfr_rrstream_first,
+ ixfr_rrstream_next,
+ ixfr_rrstream_current,
+ rrstream_noop_pause,
+ ixfr_rrstream_destroy
+};
+
+/**************************************************************************/
+/*
+ * An 'axfr_rrstream_t' is an 'rrstream_t' that returns
+ * an AXFR-like RR stream from a database.
+ *
+ * The SOAs at the beginning and end of the transfer are
+ * not included in the stream.
+ */
+
+typedef struct axfr_rrstream {
+ rrstream_t common;
+ db_rr_iterator_t it;
+ isc_boolean_t it_valid;
+} axfr_rrstream_t;
+
+/*
+ * Forward declarations.
+ */
+static void
+axfr_rrstream_destroy(rrstream_t **rsp);
+
+static rrstream_methods_t axfr_rrstream_methods;
+
+static isc_result_t
+axfr_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
+ rrstream_t **sp)
+{
+ axfr_rrstream_t *s;
+ isc_result_t result;
+
+ INSIST(sp != NULL && *sp == NULL);
+
+ s = isc_mem_get(mctx, sizeof(*s));
+ if (s == NULL)
+ return (ISC_R_NOMEMORY);
+ s->common.mctx = mctx;
+ s->common.methods = &axfr_rrstream_methods;
+ s->it_valid = ISC_FALSE;
+
+ CHECK(db_rr_iterator_init(&s->it, db, ver, 0));
+ s->it_valid = ISC_TRUE;
+
+ *sp = (rrstream_t *) s;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ axfr_rrstream_destroy((rrstream_t **) (void *)&s);
+ return (result);
+}
+
+static isc_result_t
+axfr_rrstream_first(rrstream_t *rs) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ isc_result_t result;
+ result = db_rr_iterator_first(&s->it);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ /* Skip SOA records. */
+ for (;;) {
+ dns_name_t *name_dummy = NULL;
+ isc_uint32_t ttl_dummy;
+ dns_rdata_t *rdata = NULL;
+ db_rr_iterator_current(&s->it, &name_dummy,
+ &ttl_dummy, &rdata);
+ if (rdata->type != dns_rdatatype_soa)
+ break;
+ result = db_rr_iterator_next(&s->it);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ return (result);
+}
+
+static isc_result_t
+axfr_rrstream_next(rrstream_t *rs) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ isc_result_t result;
+
+ /* Skip SOA records. */
+ for (;;) {
+ dns_name_t *name_dummy = NULL;
+ isc_uint32_t ttl_dummy;
+ dns_rdata_t *rdata = NULL;
+ result = db_rr_iterator_next(&s->it);
+ if (result != ISC_R_SUCCESS)
+ break;
+ db_rr_iterator_current(&s->it, &name_dummy,
+ &ttl_dummy, &rdata);
+ if (rdata->type != dns_rdatatype_soa)
+ break;
+ }
+ return (result);
+}
+
+static void
+axfr_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ db_rr_iterator_current(&s->it, name, ttl, rdata);
+}
+
+static void
+axfr_rrstream_pause(rrstream_t *rs) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ db_rr_iterator_pause(&s->it);
+}
+
+static void
+axfr_rrstream_destroy(rrstream_t **rsp) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) *rsp;
+ if (s->it_valid)
+ db_rr_iterator_destroy(&s->it);
+ isc_mem_put(s->common.mctx, s, sizeof(*s));
+}
+
+static rrstream_methods_t axfr_rrstream_methods = {
+ axfr_rrstream_first,
+ axfr_rrstream_next,
+ axfr_rrstream_current,
+ axfr_rrstream_pause,
+ axfr_rrstream_destroy
+};
+
+/**************************************************************************/
+/*
+ * An 'soa_rrstream_t' is a degenerate 'rrstream_t' that returns
+ * a single SOA record.
+ */
+
+typedef struct soa_rrstream {
+ rrstream_t common;
+ dns_difftuple_t *soa_tuple;
+} soa_rrstream_t;
+
+/*
+ * Forward declarations.
+ */
+static void
+soa_rrstream_destroy(rrstream_t **rsp);
+
+static rrstream_methods_t soa_rrstream_methods;
+
+static isc_result_t
+soa_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
+ rrstream_t **sp)
+{
+ soa_rrstream_t *s;
+ isc_result_t result;
+
+ INSIST(sp != NULL && *sp == NULL);
+
+ s = isc_mem_get(mctx, sizeof(*s));
+ if (s == NULL)
+ return (ISC_R_NOMEMORY);
+ s->common.mctx = mctx;
+ s->common.methods = &soa_rrstream_methods;
+ s->soa_tuple = NULL;
+
+ CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
+ &s->soa_tuple));
+
+ *sp = (rrstream_t *) s;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ soa_rrstream_destroy((rrstream_t **) (void *)&s);
+ return (result);
+}
+
+static isc_result_t
+soa_rrstream_first(rrstream_t *rs) {
+ UNUSED(rs);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+soa_rrstream_next(rrstream_t *rs) {
+ UNUSED(rs);
+ return (ISC_R_NOMORE);
+}
+
+static void
+soa_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ soa_rrstream_t *s = (soa_rrstream_t *) rs;
+ *name = &s->soa_tuple->name;
+ *ttl = s->soa_tuple->ttl;
+ *rdata = &s->soa_tuple->rdata;
+}
+
+static void
+soa_rrstream_destroy(rrstream_t **rsp) {
+ soa_rrstream_t *s = (soa_rrstream_t *) *rsp;
+ if (s->soa_tuple != NULL)
+ dns_difftuple_free(&s->soa_tuple);
+ isc_mem_put(s->common.mctx, s, sizeof(*s));
+}
+
+static rrstream_methods_t soa_rrstream_methods = {
+ soa_rrstream_first,
+ soa_rrstream_next,
+ soa_rrstream_current,
+ rrstream_noop_pause,
+ soa_rrstream_destroy
+};
+
+/**************************************************************************/
+/*
+ * A 'compound_rrstream_t' objects owns a soa_rrstream
+ * and another rrstream, the "data stream". It returns
+ * a concatenated stream consisting of the soa_rrstream, then
+ * the data stream, then the soa_rrstream again.
+ *
+ * The component streams are owned by the compound_rrstream_t
+ * and are destroyed with it.
+ */
+
+typedef struct compound_rrstream {
+ rrstream_t common;
+ rrstream_t *components[3];
+ int state;
+ isc_result_t result;
+} compound_rrstream_t;
+
+/*
+ * Forward declarations.
+ */
+static void
+compound_rrstream_destroy(rrstream_t **rsp);
+
+static isc_result_t
+compound_rrstream_next(rrstream_t *rs);
+
+static rrstream_methods_t compound_rrstream_methods;
+
+/*
+ * Requires:
+ * soa_stream != NULL && *soa_stream != NULL
+ * data_stream != NULL && *data_stream != NULL
+ * sp != NULL && *sp == NULL
+ *
+ * Ensures:
+ * *soa_stream == NULL
+ * *data_stream == NULL
+ * *sp points to a valid compound_rrstream_t
+ * The soa and data streams will be destroyed
+ * when the compound_rrstream_t is destroyed.
+ */
+static isc_result_t
+compound_rrstream_create(isc_mem_t *mctx, rrstream_t **soa_stream,
+ rrstream_t **data_stream, rrstream_t **sp)
+{
+ compound_rrstream_t *s;
+
+ INSIST(sp != NULL && *sp == NULL);
+
+ s = isc_mem_get(mctx, sizeof(*s));
+ if (s == NULL)
+ return (ISC_R_NOMEMORY);
+ s->common.mctx = mctx;
+ s->common.methods = &compound_rrstream_methods;
+ s->components[0] = *soa_stream;
+ s->components[1] = *data_stream;
+ s->components[2] = *soa_stream;
+ s->state = -1;
+ s->result = ISC_R_FAILURE;
+
+ *soa_stream = NULL;
+ *data_stream = NULL;
+ *sp = (rrstream_t *) s;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+compound_rrstream_first(rrstream_t *rs) {
+ compound_rrstream_t *s = (compound_rrstream_t *) rs;
+ s->state = 0;
+ do {
+ rrstream_t *curstream = s->components[s->state];
+ s->result = curstream->methods->first(curstream);
+ } while (s->result == ISC_R_NOMORE && s->state < 2);
+ return (s->result);
+}
+
+static isc_result_t
+compound_rrstream_next(rrstream_t *rs) {
+ compound_rrstream_t *s = (compound_rrstream_t *) rs;
+ rrstream_t *curstream = s->components[s->state];
+ s->result = curstream->methods->next(curstream);
+ while (s->result == ISC_R_NOMORE) {
+ /*
+ * Make sure locks held by the current stream
+ * are released before we switch streams.
+ */
+ curstream->methods->pause(curstream);
+ if (s->state == 2)
+ return (ISC_R_NOMORE);
+ s->state++;
+ curstream = s->components[s->state];
+ s->result = curstream->methods->first(curstream);
+ }
+ return (s->result);
+}
+
+static void
+compound_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ compound_rrstream_t *s = (compound_rrstream_t *) rs;
+ rrstream_t *curstream;
+ INSIST(0 <= s->state && s->state < 3);
+ INSIST(s->result == ISC_R_SUCCESS);
+ curstream = s->components[s->state];
+ curstream->methods->current(curstream, name, ttl, rdata);
+}
+
+static void
+compound_rrstream_pause(rrstream_t *rs)
+{
+ compound_rrstream_t *s = (compound_rrstream_t *) rs;
+ rrstream_t *curstream;
+ INSIST(0 <= s->state && s->state < 3);
+ curstream = s->components[s->state];
+ curstream->methods->pause(curstream);
+}
+
+static void
+compound_rrstream_destroy(rrstream_t **rsp) {
+ compound_rrstream_t *s = (compound_rrstream_t *) *rsp;
+ s->components[0]->methods->destroy(&s->components[0]);
+ s->components[1]->methods->destroy(&s->components[1]);
+ s->components[2] = NULL; /* Copy of components[0]. */
+ isc_mem_put(s->common.mctx, s, sizeof(*s));
+}
+
+static rrstream_methods_t compound_rrstream_methods = {
+ compound_rrstream_first,
+ compound_rrstream_next,
+ compound_rrstream_current,
+ compound_rrstream_pause,
+ compound_rrstream_destroy
+};
+
+/**************************************************************************/
+/*
+ * An 'xfrout_ctx_t' contains the state of an outgoing AXFR or IXFR
+ * in progress.
+ */
+
+typedef struct {
+ isc_mem_t *mctx;
+ ns_client_t *client;
+ unsigned int id; /* ID of request */
+ dns_name_t *qname; /* Question name of request */
+ dns_rdatatype_t qtype; /* dns_rdatatype_{a,i}xfr */
+ dns_rdataclass_t qclass;
+ dns_db_t *db;
+ dns_dbversion_t *ver;
+ isc_quota_t *quota;
+ rrstream_t *stream; /* The XFR RR stream */
+ isc_boolean_t end_of_stream; /* EOS has been reached */
+ isc_buffer_t buf; /* Buffer for message owner
+ names and rdatas */
+ isc_buffer_t txlenbuf; /* Transmit length buffer */
+ isc_buffer_t txbuf; /* Transmit message buffer */
+ void *txmem;
+ unsigned int txmemlen;
+ unsigned int nmsg; /* Number of messages sent */
+ dns_tsigkey_t *tsigkey; /* Key used to create TSIG */
+ isc_buffer_t *lasttsig; /* the last TSIG */
+ isc_boolean_t many_answers;
+ int sends; /* Send in progress */
+ isc_boolean_t shuttingdown;
+ const char *mnemonic; /* Style of transfer */
+} xfrout_ctx_t;
+
+static isc_result_t
+xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client,
+ unsigned int id, dns_name_t *qname, dns_rdatatype_t qtype,
+ dns_rdataclass_t qclass,
+ dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
+ rrstream_t *stream, dns_tsigkey_t *tsigkey,
+ isc_buffer_t *lasttsig,
+ unsigned int maxtime,
+ unsigned int idletime,
+ isc_boolean_t many_answers,
+ xfrout_ctx_t **xfrp);
+
+static void
+sendstream(xfrout_ctx_t *xfr);
+
+static void
+xfrout_senddone(isc_task_t *task, isc_event_t *event);
+
+static void
+xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg);
+
+static void
+xfrout_maybe_destroy(xfrout_ctx_t *xfr);
+
+static void
+xfrout_ctx_destroy(xfrout_ctx_t **xfrp);
+
+static void
+xfrout_client_shutdown(void *arg, isc_result_t result);
+
+static void
+xfrout_log1(ns_client_t *client, dns_name_t *zonename,
+ dns_rdataclass_t rdclass, int level,
+ const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6);
+
+static void
+xfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+
+/**************************************************************************/
+
+void
+ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
+ isc_result_t result;
+ dns_name_t *question_name;
+ dns_rdataset_t *question_rdataset;
+ dns_zone_t *zone = NULL;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_rdataclass_t question_class;
+ rrstream_t *soa_stream = NULL;
+ rrstream_t *data_stream = NULL;
+ rrstream_t *stream = NULL;
+ dns_difftuple_t *current_soa_tuple = NULL;
+ dns_name_t *soa_name;
+ dns_rdataset_t *soa_rdataset;
+ dns_rdata_t soa_rdata = DNS_RDATA_INIT;
+ isc_boolean_t have_soa = ISC_FALSE;
+ const char *mnemonic = NULL;
+ isc_mem_t *mctx = client->mctx;
+ dns_message_t *request = client->message;
+ xfrout_ctx_t *xfr = NULL;
+ isc_quota_t *quota = NULL;
+ dns_transfer_format_t format = client->view->transfer_format;
+ isc_netaddr_t na;
+ dns_peer_t *peer = NULL;
+ isc_buffer_t *tsigbuf = NULL;
+ char *journalfile;
+ char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")];
+ char keyname[DNS_NAME_FORMATSIZE];
+ isc_boolean_t is_poll = ISC_FALSE;
+#ifdef DLZ
+ isc_boolean_t is_dlz = ISC_FALSE;
+#endif
+
+ switch (reqtype) {
+ case dns_rdatatype_axfr:
+ mnemonic = "AXFR";
+ break;
+ case dns_rdatatype_ixfr:
+ mnemonic = "IXFR";
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+
+ ns_client_log(client,
+ DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT,
+ ISC_LOG_DEBUG(6), "%s request", mnemonic);
+ /*
+ * Apply quota.
+ */
+ result = isc_quota_attach(&ns_g_server->xfroutquota, &quota);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING,
+ "%s request denied: %s", mnemonic,
+ isc_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Interpret the question section.
+ */
+ result = dns_message_firstname(request, DNS_SECTION_QUESTION);
+ INSIST(result == ISC_R_SUCCESS);
+
+ /*
+ * The question section must contain exactly one question, and
+ * it must be for AXFR/IXFR as appropriate.
+ */
+ question_name = NULL;
+ dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name);
+ question_rdataset = ISC_LIST_HEAD(question_name->list);
+ question_class = question_rdataset->rdclass;
+ INSIST(question_rdataset->type == reqtype);
+ if (ISC_LIST_NEXT(question_rdataset, link) != NULL)
+ FAILC(DNS_R_FORMERR, "multiple questions");
+ result = dns_message_nextname(request, DNS_SECTION_QUESTION);
+ if (result != ISC_R_NOMORE)
+ FAILC(DNS_R_FORMERR, "multiple questions");
+
+ result = dns_zt_find(client->view->zonetable, question_name, 0, NULL,
+ &zone);
+
+ if (result != ISC_R_SUCCESS)
+#ifdef DLZ
+ {
+ /*
+ * Normal zone table does not have a match. Try the DLZ database
+ */
+ if (client->view->dlzdatabase != NULL) {
+ result = dns_dlzallowzonexfr(client->view,
+ question_name, &client->peeraddr,
+ &db);
+
+ if (result == ISC_R_NOPERM) {
+ char _buf1[DNS_NAME_FORMATSIZE];
+ char _buf2[DNS_RDATACLASS_FORMATSIZE];
+
+ result = DNS_R_REFUSED;
+ dns_name_format(question_name, _buf1,
+ sizeof(_buf1));
+ dns_rdataclass_format(question_class,
+ _buf2, sizeof(_buf2));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_XFER_OUT,
+ ISC_LOG_ERROR,
+ "zone transfer '%s/%s' denied",
+ _buf1, _buf2);
+ goto failure;
+ }
+ if (result != ISC_R_SUCCESS)
+#endif
+ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
+ question_name, question_class);
+#ifdef DLZ
+ is_dlz = ISC_TRUE;
+ /*
+ * DLZ only support full zone transfer, not incremental
+ */
+ if (reqtype != dns_rdatatype_axfr) {
+ mnemonic = "AXFR-style IXFR";
+ reqtype = dns_rdatatype_axfr;
+ }
+
+ } else {
+ /*
+ * not DLZ and not in normal zone table, we are
+ * not authoritative
+ */
+ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
+ question_name, question_class);
+ }
+ } else {
+ /* zone table has a match */
+#endif
+ switch(dns_zone_gettype(zone)) {
+ case dns_zone_master:
+ case dns_zone_slave:
+ break; /* Master and slave zones are OK for transfer. */
+ default:
+ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class);
+ }
+ CHECK(dns_zone_getdb(zone, &db));
+ dns_db_currentversion(db, &ver);
+#ifdef DLZ
+ }
+#endif
+
+ xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
+ "%s question section OK", mnemonic);
+
+ /*
+ * Check the authority section. Look for a SOA record with
+ * the same name and class as the question.
+ */
+ for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(request, DNS_SECTION_AUTHORITY))
+ {
+ soa_name = NULL;
+ dns_message_currentname(request, DNS_SECTION_AUTHORITY,
+ &soa_name);
+
+ /*
+ * Ignore data whose owner name is not the zone apex.
+ */
+ if (! dns_name_equal(soa_name, question_name))
+ continue;
+
+ for (soa_rdataset = ISC_LIST_HEAD(soa_name->list);
+ soa_rdataset != NULL;
+ soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link))
+ {
+ /*
+ * Ignore non-SOA data.
+ */
+ if (soa_rdataset->type != dns_rdatatype_soa)
+ continue;
+ if (soa_rdataset->rdclass != question_class)
+ continue;
+
+ CHECK(dns_rdataset_first(soa_rdataset));
+ dns_rdataset_current(soa_rdataset, &soa_rdata);
+ result = dns_rdataset_next(soa_rdataset);
+ if (result == ISC_R_SUCCESS)
+ FAILC(DNS_R_FORMERR,
+ "IXFR authority section "
+ "has multiple SOAs");
+ have_soa = ISC_TRUE;
+ goto got_soa;
+ }
+ }
+ got_soa:
+ if (result != ISC_R_NOMORE)
+ CHECK(result);
+
+ xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
+ "%s authority section OK", mnemonic);
+
+ /*
+ * Decide whether to allow this transfer.
+ */
+#ifdef DLZ
+ /*
+ * if not a DLZ zone decide whether to allow this transfer.
+ */
+ if (!is_dlz) {
+#endif
+ ns_client_aclmsg("zone transfer", question_name, reqtype,
+ client->view->rdclass, msg, sizeof(msg));
+ CHECK(ns_client_checkacl(client, msg,
+ dns_zone_getxfracl(zone), ISC_TRUE,
+ ISC_LOG_ERROR));
+#ifdef DLZ
+ }
+#endif
+
+ /*
+ * AXFR over UDP is not possible.
+ */
+ if (reqtype == dns_rdatatype_axfr &&
+ (client->attributes & NS_CLIENTATTR_TCP) == 0)
+ FAILC(DNS_R_FORMERR, "attempted AXFR over UDP");
+
+ /*
+ * Look up the requesting server in the peer table.
+ */
+ isc_netaddr_fromsockaddr(&na, &client->peeraddr);
+ (void)dns_peerlist_peerbyaddr(client->view->peers, &na, &peer);
+
+ /*
+ * Decide on the transfer format (one-answer or many-answers).
+ */
+ if (peer != NULL)
+ (void)dns_peer_gettransferformat(peer, &format);
+
+ /*
+ * Get a dynamically allocated copy of the current SOA.
+ */
+#ifdef DLZ
+ if (is_dlz)
+ dns_db_currentversion(db, &ver);
+#endif
+ CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
+ &current_soa_tuple));
+
+ if (reqtype == dns_rdatatype_ixfr) {
+ isc_uint32_t begin_serial, current_serial;
+ isc_boolean_t provide_ixfr;
+
+ /*
+ * Outgoing IXFR may have been disabled for this peer
+ * or globally.
+ */
+ provide_ixfr = client->view->provideixfr;
+ if (peer != NULL)
+ (void) dns_peer_getprovideixfr(peer, &provide_ixfr);
+ if (provide_ixfr == ISC_FALSE)
+ goto axfr_fallback;
+
+ if (! have_soa)
+ FAILC(DNS_R_FORMERR,
+ "IXFR request missing SOA");
+
+ begin_serial = dns_soa_getserial(&soa_rdata);
+ current_serial = dns_soa_getserial(&current_soa_tuple->rdata);
+
+ /*
+ * RFC1995 says "If an IXFR query with the same or
+ * newer version number than that of the server
+ * is received, it is replied to with a single SOA
+ * record of the server's current version, just as
+ * in AXFR". The claim about AXFR is incorrect,
+ * but other than that, we do as the RFC says.
+ *
+ * Sending a single SOA record is also how we refuse
+ * IXFR over UDP (currently, we always do).
+ */
+ if (DNS_SERIAL_GE(begin_serial, current_serial) ||
+ (client->attributes & NS_CLIENTATTR_TCP) == 0)
+ {
+ CHECK(soa_rrstream_create(mctx, db, ver, &stream));
+ is_poll = ISC_TRUE;
+ goto have_stream;
+ }
+ journalfile = dns_zone_getjournal(zone);
+ if (journalfile != NULL)
+ result = ixfr_rrstream_create(mctx,
+ journalfile,
+ begin_serial,
+ current_serial,
+ &data_stream);
+ else
+ result = ISC_R_NOTFOUND;
+ if (result == ISC_R_NOTFOUND ||
+ result == ISC_R_RANGE) {
+ xfrout_log1(client, question_name, question_class,
+ ISC_LOG_DEBUG(4),
+ "IXFR version not in journal, "
+ "falling back to AXFR");
+ mnemonic = "AXFR-style IXFR";
+ goto axfr_fallback;
+ }
+ CHECK(result);
+ } else {
+ axfr_fallback:
+ CHECK(axfr_rrstream_create(mctx, db, ver,
+ &data_stream));
+ }
+
+ /*
+ * Bracket the the data stream with SOAs.
+ */
+ CHECK(soa_rrstream_create(mctx, db, ver, &soa_stream));
+ CHECK(compound_rrstream_create(mctx, &soa_stream, &data_stream,
+ &stream));
+ soa_stream = NULL;
+ data_stream = NULL;
+
+ have_stream:
+ CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf));
+ /*
+ * Create the xfrout context object. This transfers the ownership
+ * of "stream", "db", "ver", and "quota" to the xfrout context object.
+ */
+
+
+
+#ifdef DLZ
+ if (is_dlz)
+ CHECK(xfrout_ctx_create(mctx, client, request->id, question_name,
+ reqtype, question_class, db, ver, quota,
+ stream, dns_message_gettsigkey(request),
+ tsigbuf,
+ 3600,
+ 3600,
+ (format == dns_many_answers) ?
+ ISC_TRUE : ISC_FALSE,
+ &xfr));
+ else
+#endif
+ CHECK(xfrout_ctx_create(mctx, client, request->id, question_name,
+ reqtype, question_class, db, ver, quota,
+ stream, dns_message_gettsigkey(request),
+ tsigbuf,
+ dns_zone_getmaxxfrout(zone),
+ dns_zone_getidleout(zone),
+ (format == dns_many_answers) ?
+ ISC_TRUE : ISC_FALSE,
+ &xfr));
+
+ xfr->mnemonic = mnemonic;
+ stream = NULL;
+ quota = NULL;
+
+ CHECK(xfr->stream->methods->first(xfr->stream));
+
+ if (xfr->tsigkey != NULL) {
+ dns_name_format(&xfr->tsigkey->name, keyname, sizeof(keyname));
+ } else
+ keyname[0] = '\0';
+ if (is_poll)
+ xfrout_log1(client, question_name, question_class,
+ ISC_LOG_DEBUG(1), "IXFR poll up to date%s%s",
+ (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname);
+ else
+ xfrout_log1(client, question_name, question_class,
+ ISC_LOG_INFO, "%s started%s%s", mnemonic,
+ (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname);
+
+ /*
+ * Hand the context over to sendstream(). Set xfr to NULL;
+ * sendstream() is responsible for either passing the
+ * context on to a later event handler or destroying it.
+ */
+ sendstream(xfr);
+ xfr = NULL;
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (quota != NULL)
+ isc_quota_detach(&quota);
+ if (current_soa_tuple != NULL)
+ dns_difftuple_free(&current_soa_tuple);
+ if (stream != NULL)
+ stream->methods->destroy(&stream);
+ if (soa_stream != NULL)
+ soa_stream->methods->destroy(&soa_stream);
+ if (data_stream != NULL)
+ data_stream->methods->destroy(&data_stream);
+ if (ver != NULL)
+ dns_db_closeversion(db, &ver, ISC_FALSE);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ /* XXX kludge */
+ if (xfr != NULL) {
+ xfrout_fail(xfr, result, "setting up zone transfer");
+ } else if (result != ISC_R_SUCCESS) {
+ ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT,
+ NS_LOGMODULE_XFER_OUT,
+ ISC_LOG_DEBUG(3), "zone transfer setup failed");
+ ns_client_error(client, result);
+ }
+}
+
+static isc_result_t
+xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
+ dns_name_t *qname, dns_rdatatype_t qtype,
+ dns_rdataclass_t qclass,
+ dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
+ rrstream_t *stream, dns_tsigkey_t *tsigkey,
+ isc_buffer_t *lasttsig, unsigned int maxtime,
+ unsigned int idletime, isc_boolean_t many_answers,
+ xfrout_ctx_t **xfrp)
+{
+ xfrout_ctx_t *xfr;
+ isc_result_t result;
+ unsigned int len;
+ void *mem;
+
+ INSIST(xfrp != NULL && *xfrp == NULL);
+ xfr = isc_mem_get(mctx, sizeof(*xfr));
+ if (xfr == NULL)
+ return (ISC_R_NOMEMORY);
+ xfr->mctx = mctx;
+ xfr->client = NULL;
+ ns_client_attach(client, &xfr->client);
+ xfr->id = id;
+ xfr->qname = qname;
+ xfr->qtype = qtype;
+ xfr->qclass = qclass;
+ xfr->db = NULL;
+ xfr->ver = NULL;
+ dns_db_attach(db, &xfr->db);
+ dns_db_attachversion(db, ver, &xfr->ver);
+ xfr->end_of_stream = ISC_FALSE;
+ xfr->tsigkey = tsigkey;
+ xfr->lasttsig = lasttsig;
+ xfr->txmem = NULL;
+ xfr->txmemlen = 0;
+ xfr->nmsg = 0;
+ xfr->many_answers = many_answers,
+ xfr->sends = 0;
+ xfr->shuttingdown = ISC_FALSE;
+ xfr->mnemonic = NULL;
+ xfr->buf.base = NULL;
+ xfr->buf.length = 0;
+ xfr->txmem = NULL;
+ xfr->txmemlen = 0;
+ xfr->stream = NULL;
+ xfr->quota = NULL;
+
+ /*
+ * Allocate a temporary buffer for the uncompressed response
+ * message data. The size should be no more than 65535 bytes
+ * so that the compressed data will fit in a TCP message,
+ * and no less than 65535 bytes so that an almost maximum-sized
+ * RR will fit. Note that although 65535-byte RRs are allowed
+ * in principle, they cannot be zone-transferred (at least not
+ * if uncompressible), because the message and RR headers would
+ * push the size of the TCP message over the 65536 byte limit.
+ */
+ len = 65535;
+ mem = isc_mem_get(mctx, len);
+ if (mem == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ isc_buffer_init(&xfr->buf, mem, len);
+
+ /*
+ * Allocate another temporary buffer for the compressed
+ * response message and its TCP length prefix.
+ */
+ len = 2 + 65535;
+ mem = isc_mem_get(mctx, len);
+ if (mem == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ isc_buffer_init(&xfr->txlenbuf, mem, 2);
+ isc_buffer_init(&xfr->txbuf, (char *) mem + 2, len - 2);
+ xfr->txmem = mem;
+ xfr->txmemlen = len;
+
+ CHECK(dns_timer_setidle(xfr->client->timer,
+ maxtime, idletime, ISC_FALSE));
+
+ /*
+ * Register a shutdown callback with the client, so that we
+ * can stop the transfer immediately when the client task
+ * gets a shutdown event.
+ */
+ xfr->client->shutdown = xfrout_client_shutdown;
+ xfr->client->shutdown_arg = xfr;
+ /*
+ * These MUST be after the last "goto failure;" / CHECK to
+ * prevent a double free by the caller.
+ */
+ xfr->quota = quota;
+ xfr->stream = stream;
+
+ *xfrp = xfr;
+ return (ISC_R_SUCCESS);
+
+failure:
+ xfrout_ctx_destroy(&xfr);
+ return (result);
+}
+
+
+/*
+ * Arrange to send as much as we can of "stream" without blocking.
+ *
+ * Requires:
+ * The stream iterator is initialized and points at an RR,
+ * or possiby at the end of the stream (that is, the
+ * _first method of the iterator has been called).
+ */
+static void
+sendstream(xfrout_ctx_t *xfr) {
+ dns_message_t *tcpmsg = NULL;
+ dns_message_t *msg = NULL; /* Client message if UDP, tcpmsg if TCP */
+ isc_result_t result;
+ isc_region_t used;
+ isc_region_t region;
+ dns_rdataset_t *qrdataset;
+ dns_name_t *msgname = NULL;
+ dns_rdata_t *msgrdata = NULL;
+ dns_rdatalist_t *msgrdl = NULL;
+ dns_rdataset_t *msgrds = NULL;
+ dns_compress_t cctx;
+ isc_boolean_t cleanup_cctx = ISC_FALSE;
+
+ int n_rrs;
+
+ isc_buffer_clear(&xfr->buf);
+ isc_buffer_clear(&xfr->txlenbuf);
+ isc_buffer_clear(&xfr->txbuf);
+
+ if ((xfr->client->attributes & NS_CLIENTATTR_TCP) == 0) {
+ /*
+ * In the UDP case, we put the response data directly into
+ * the client message.
+ */
+ msg = xfr->client->message;
+ CHECK(dns_message_reply(msg, ISC_TRUE));
+ } else {
+ /*
+ * TCP. Build a response dns_message_t, temporarily storing
+ * the raw, uncompressed owner names and RR data contiguously
+ * in xfr->buf. We know that if the uncompressed data fits
+ * in xfr->buf, the compressed data will surely fit in a TCP
+ * message.
+ */
+
+ CHECK(dns_message_create(xfr->mctx,
+ DNS_MESSAGE_INTENTRENDER, &tcpmsg));
+ msg = tcpmsg;
+
+ msg->id = xfr->id;
+ msg->rcode = dns_rcode_noerror;
+ msg->flags = DNS_MESSAGEFLAG_QR | DNS_MESSAGEFLAG_AA;
+ if ((xfr->client->attributes & NS_CLIENTATTR_RA) != 0)
+ msg->flags |= DNS_MESSAGEFLAG_RA;
+ CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
+ CHECK(dns_message_setquerytsig(msg, xfr->lasttsig));
+ if (xfr->lasttsig != NULL)
+ isc_buffer_free(&xfr->lasttsig);
+
+ /*
+ * Include a question section in the first message only.
+ * BIND 8.2.1 will not recognize an IXFR if it does not
+ * have a question section.
+ */
+ if (xfr->nmsg == 0) {
+ dns_name_t *qname = NULL;
+ isc_region_t r;
+
+ /*
+ * Reserve space for the 12-byte message header
+ * and 4 bytes of question.
+ */
+ isc_buffer_add(&xfr->buf, 12 + 4);
+
+ qrdataset = NULL;
+ result = dns_message_gettemprdataset(msg, &qrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ dns_rdataset_init(qrdataset);
+ dns_rdataset_makequestion(qrdataset,
+ xfr->client->message->rdclass,
+ xfr->qtype);
+
+ result = dns_message_gettempname(msg, &qname);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ dns_name_init(qname, NULL);
+ isc_buffer_availableregion(&xfr->buf, &r);
+ INSIST(r.length >= xfr->qname->length);
+ r.length = xfr->qname->length;
+ isc_buffer_putmem(&xfr->buf, xfr->qname->ndata,
+ xfr->qname->length);
+ dns_name_fromregion(qname, &r);
+ ISC_LIST_INIT(qname->list);
+ ISC_LIST_APPEND(qname->list, qrdataset, link);
+
+ dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
+ }
+ else
+ msg->tcp_continuation = 1;
+ }
+
+ /*
+ * Try to fit in as many RRs as possible, unless "one-answer"
+ * format has been requested.
+ */
+ for (n_rrs = 0; ; n_rrs++) {
+ dns_name_t *name = NULL;
+ isc_uint32_t ttl;
+ dns_rdata_t *rdata = NULL;
+
+ unsigned int size;
+ isc_region_t r;
+
+ msgname = NULL;
+ msgrdata = NULL;
+ msgrdl = NULL;
+ msgrds = NULL;
+
+ xfr->stream->methods->current(xfr->stream,
+ &name, &ttl, &rdata);
+ size = name->length + 10 + rdata->length;
+ isc_buffer_availableregion(&xfr->buf, &r);
+ if (size >= r.length) {
+ /*
+ * RR would not fit. If there are other RRs in the
+ * buffer, send them now and leave this RR to the
+ * next message. If this RR overflows the buffer
+ * all by itself, fail.
+ *
+ * In theory some RRs might fit in a TCP message
+ * when compressed even if they do not fit when
+ * uncompressed, but surely we don't want
+ * to send such monstrosities to an unsuspecting
+ * slave.
+ */
+ if (n_rrs == 0) {
+ xfrout_log(xfr, ISC_LOG_WARNING,
+ "RR too large for zone transfer "
+ "(%d bytes)", size);
+ /* XXX DNS_R_RRTOOLARGE? */
+ result = ISC_R_NOSPACE;
+ goto failure;
+ }
+ break;
+ }
+
+ if (isc_log_wouldlog(ns_g_lctx, XFROUT_RR_LOGLEVEL))
+ log_rr(name, rdata, ttl); /* XXX */
+
+ result = dns_message_gettempname(msg, &msgname);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ dns_name_init(msgname, NULL);
+ isc_buffer_availableregion(&xfr->buf, &r);
+ INSIST(r.length >= name->length);
+ r.length = name->length;
+ isc_buffer_putmem(&xfr->buf, name->ndata, name->length);
+ dns_name_fromregion(msgname, &r);
+
+ /* Reserve space for RR header. */
+ isc_buffer_add(&xfr->buf, 10);
+
+ result = dns_message_gettemprdata(msg, &msgrdata);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ isc_buffer_availableregion(&xfr->buf, &r);
+ r.length = rdata->length;
+ isc_buffer_putmem(&xfr->buf, rdata->data, rdata->length);
+ dns_rdata_init(msgrdata);
+ dns_rdata_fromregion(msgrdata,
+ rdata->rdclass, rdata->type, &r);
+
+ result = dns_message_gettemprdatalist(msg, &msgrdl);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ msgrdl->type = rdata->type;
+ msgrdl->rdclass = rdata->rdclass;
+ msgrdl->ttl = ttl;
+ ISC_LINK_INIT(msgrdl, link);
+ ISC_LIST_INIT(msgrdl->rdata);
+ ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link);
+
+ result = dns_message_gettemprdataset(msg, &msgrds);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ dns_rdataset_init(msgrds);
+ result = dns_rdatalist_tordataset(msgrdl, msgrds);
+ INSIST(result == ISC_R_SUCCESS);
+
+ ISC_LIST_APPEND(msgname->list, msgrds, link);
+
+ dns_message_addname(msg, msgname, DNS_SECTION_ANSWER);
+ msgname = NULL;
+
+ result = xfr->stream->methods->next(xfr->stream);
+ if (result == ISC_R_NOMORE) {
+ xfr->end_of_stream = ISC_TRUE;
+ break;
+ }
+ CHECK(result);
+
+ if (! xfr->many_answers)
+ break;
+ }
+
+ if ((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0) {
+ CHECK(dns_compress_init(&cctx, -1, xfr->mctx));
+ dns_compress_setsensitive(&cctx, ISC_TRUE);
+ cleanup_cctx = ISC_TRUE;
+ CHECK(dns_message_renderbegin(msg, &cctx, &xfr->txbuf));
+ CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0));
+ CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0));
+ CHECK(dns_message_renderend(msg));
+ dns_compress_invalidate(&cctx);
+ cleanup_cctx = ISC_FALSE;
+
+ isc_buffer_usedregion(&xfr->txbuf, &used);
+ isc_buffer_putuint16(&xfr->txlenbuf,
+ (isc_uint16_t)used.length);
+ region.base = xfr->txlenbuf.base;
+ region.length = 2 + used.length;
+ xfrout_log(xfr, ISC_LOG_DEBUG(8),
+ "sending TCP message of %d bytes",
+ used.length);
+ CHECK(isc_socket_send(xfr->client->tcpsocket, /* XXX */
+ &region, xfr->client->task,
+ xfrout_senddone,
+ xfr));
+ xfr->sends++;
+ } else {
+ xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending IXFR UDP response");
+ ns_client_send(xfr->client);
+ xfr->stream->methods->pause(xfr->stream);
+ xfrout_ctx_destroy(&xfr);
+ return;
+ }
+
+ /* Advance lasttsig to be the last TSIG generated */
+ CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig));
+
+ xfr->nmsg++;
+
+ failure:
+ if (msgname != NULL) {
+ if (msgrds != NULL) {
+ if (dns_rdataset_isassociated(msgrds))
+ dns_rdataset_disassociate(msgrds);
+ dns_message_puttemprdataset(msg, &msgrds);
+ }
+ if (msgrdl != NULL) {
+ ISC_LIST_UNLINK(msgrdl->rdata, msgrdata, link);
+ dns_message_puttemprdatalist(msg, &msgrdl);
+ }
+ if (msgrdata != NULL)
+ dns_message_puttemprdata(msg, &msgrdata);
+ dns_message_puttempname(msg, &msgname);
+ }
+
+ if (tcpmsg != NULL)
+ dns_message_destroy(&tcpmsg);
+
+ if (cleanup_cctx)
+ dns_compress_invalidate(&cctx);
+ /*
+ * Make sure to release any locks held by database
+ * iterators before returning from the event handler.
+ */
+ xfr->stream->methods->pause(xfr->stream);
+
+ if (result == ISC_R_SUCCESS)
+ return;
+
+ xfrout_fail(xfr, result, "sending zone data");
+}
+
+static void
+xfrout_ctx_destroy(xfrout_ctx_t **xfrp) {
+ xfrout_ctx_t *xfr = *xfrp;
+
+ INSIST(xfr->sends == 0);
+
+ xfr->client->shutdown = NULL;
+ xfr->client->shutdown_arg = NULL;
+
+ if (xfr->stream != NULL)
+ xfr->stream->methods->destroy(&xfr->stream);
+ if (xfr->buf.base != NULL)
+ isc_mem_put(xfr->mctx, xfr->buf.base, xfr->buf.length);
+ if (xfr->txmem != NULL)
+ isc_mem_put(xfr->mctx, xfr->txmem, xfr->txmemlen);
+ if (xfr->lasttsig != NULL)
+ isc_buffer_free(&xfr->lasttsig);
+ if (xfr->quota != NULL)
+ isc_quota_detach(&xfr->quota);
+ if (xfr->ver != NULL)
+ dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
+ if (xfr->db != NULL)
+ dns_db_detach(&xfr->db);
+
+ ns_client_detach(&xfr->client);
+
+ isc_mem_put(xfr->mctx, xfr, sizeof(*xfr));
+
+ *xfrp = NULL;
+}
+
+static void
+xfrout_senddone(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sev = (isc_socketevent_t *)event;
+ xfrout_ctx_t *xfr = (xfrout_ctx_t *)event->ev_arg;
+ isc_result_t evresult = sev->result;
+
+ UNUSED(task);
+
+ INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
+
+ isc_event_free(&event);
+ xfr->sends--;
+ INSIST(xfr->sends == 0);
+
+ (void)isc_timer_touch(xfr->client->timer);
+ if (xfr->shuttingdown == ISC_TRUE) {
+ xfrout_maybe_destroy(xfr);
+ } else if (evresult != ISC_R_SUCCESS) {
+ xfrout_fail(xfr, evresult, "send");
+ } else if (xfr->end_of_stream == ISC_FALSE) {
+ sendstream(xfr);
+ } else {
+ /* End of zone transfer stream. */
+ xfrout_log(xfr, ISC_LOG_INFO, "%s ended", xfr->mnemonic);
+ ns_client_next(xfr->client, ISC_R_SUCCESS);
+ xfrout_ctx_destroy(&xfr);
+ }
+}
+
+static void
+xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg) {
+ xfr->shuttingdown = ISC_TRUE;
+ xfrout_log(xfr, ISC_LOG_ERROR, "%s: %s",
+ msg, isc_result_totext(result));
+ xfrout_maybe_destroy(xfr);
+}
+
+static void
+xfrout_maybe_destroy(xfrout_ctx_t *xfr) {
+ INSIST(xfr->shuttingdown == ISC_TRUE);
+ if (xfr->sends > 0) {
+ /*
+ * If we are currently sending, cancel it and wait for
+ * cancel event before destroying the context.
+ */
+ isc_socket_cancel(xfr->client->tcpsocket, xfr->client->task,
+ ISC_SOCKCANCEL_SEND);
+ } else {
+ ns_client_next(xfr->client, ISC_R_CANCELED);
+ xfrout_ctx_destroy(&xfr);
+ }
+}
+
+static void
+xfrout_client_shutdown(void *arg, isc_result_t result) {
+ xfrout_ctx_t *xfr = (xfrout_ctx_t *) arg;
+ xfrout_fail(xfr, result, "aborted");
+}
+
+/*
+ * Log outgoing zone transfer messages in a format like
+ * <client>: transfer of <zone>: <message>
+ */
+
+static void
+xfrout_logv(ns_client_t *client, dns_name_t *zonename,
+ dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap)
+ ISC_FORMAT_PRINTF(5, 0);
+
+static void
+xfrout_logv(ns_client_t *client, dns_name_t *zonename,
+ dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap)
+{
+ char msgbuf[2048];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+
+ dns_name_format(zonename, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT,
+ NS_LOGMODULE_XFER_OUT, level,
+ "transfer of '%s/%s': %s", namebuf, classbuf, msgbuf);
+}
+
+/*
+ * Logging function for use when a xfrout_ctx_t has not yet been created.
+ */
+static void
+xfrout_log1(ns_client_t *client, dns_name_t *zonename,
+ dns_rdataclass_t rdclass, int level, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ xfrout_logv(client, zonename, rdclass, level, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Logging function for use when there is a xfrout_ctx_t.
+ */
+static void
+xfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ xfrout_logv(xfr->client, xfr->qname, xfr->qclass, level, fmt, ap);
+ va_end(ap);
+}
diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c
new file mode 100644
index 0000000..a0c1bab
--- /dev/null
+++ b/bin/named/zoneconf.c
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-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: zoneconf.c,v 1.110.18.23 2006/05/16 03:39:57 marka Exp $ */
+
+/*% */
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/rdatatype.h>
+#include <dns/ssu.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include <named/client.h>
+#include <named/config.h>
+#include <named/globals.h>
+#include <named/log.h>
+#include <named/server.h>
+#include <named/zoneconf.h>
+
+/*%
+ * These are BIND9 server defaults, not necessarily identical to the
+ * library defaults defined in zone.c.
+ */
+#define RETERR(x) do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return (_r); \
+ } while (0)
+
+/*%
+ * Convenience function for configuring a single zone ACL.
+ */
+static isc_result_t
+configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
+ const cfg_obj_t *config, const char *aclname,
+ cfg_aclconfctx_t *actx, dns_zone_t *zone,
+ void (*setzacl)(dns_zone_t *, dns_acl_t *),
+ void (*clearzacl)(dns_zone_t *))
+{
+ isc_result_t result;
+ const cfg_obj_t *maps[5];
+ const cfg_obj_t *aclobj = NULL;
+ int i = 0;
+ dns_acl_t *dacl = NULL;
+
+ if (zconfig != NULL)
+ maps[i++] = cfg_tuple_get(zconfig, "options");
+ if (vconfig != NULL)
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ if (config != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL)
+ maps[i++] = options;
+ }
+ maps[i++] = ns_g_defaults;
+ maps[i] = NULL;
+
+ result = ns_config_get(maps, aclname, &aclobj);
+ if (aclobj == NULL) {
+ (*clearzacl)(zone);
+ return (ISC_R_SUCCESS);
+ }
+
+ result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx, actx,
+ dns_zone_getmctx(zone), &dacl);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ (*setzacl)(zone, dacl);
+ dns_acl_detach(&dacl);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Parse the zone update-policy statement.
+ */
+static isc_result_t
+configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone) {
+ const cfg_obj_t *updatepolicy = NULL;
+ const cfg_listelt_t *element, *element2;
+ dns_ssutable_t *table = NULL;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ isc_result_t result;
+
+ (void)cfg_map_get(zconfig, "update-policy", &updatepolicy);
+ if (updatepolicy == NULL) {
+ dns_zone_setssutable(zone, NULL);
+ return (ISC_R_SUCCESS);
+ }
+
+ result = dns_ssutable_create(mctx, &table);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ for (element = cfg_list_first(updatepolicy);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *stmt = cfg_listelt_value(element);
+ const cfg_obj_t *mode = cfg_tuple_get(stmt, "mode");
+ 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");
+ const char *str;
+ isc_boolean_t grant = ISC_FALSE;
+ unsigned int mtype = DNS_SSUMATCHTYPE_NAME;
+ dns_fixedname_t fname, fident;
+ isc_buffer_t b;
+ dns_rdatatype_t *types;
+ unsigned int i, n;
+
+ str = cfg_obj_asstring(mode);
+ if (strcasecmp(str, "grant") == 0)
+ grant = ISC_TRUE;
+ else if (strcasecmp(str, "deny") == 0)
+ grant = ISC_FALSE;
+ else
+ INSIST(0);
+
+ str = cfg_obj_asstring(matchtype);
+ if (strcasecmp(str, "name") == 0)
+ mtype = DNS_SSUMATCHTYPE_NAME;
+ else if (strcasecmp(str, "subdomain") == 0)
+ mtype = DNS_SSUMATCHTYPE_SUBDOMAIN;
+ else if (strcasecmp(str, "wildcard") == 0)
+ mtype = DNS_SSUMATCHTYPE_WILDCARD;
+ else if (strcasecmp(str, "self") == 0)
+ mtype = DNS_SSUMATCHTYPE_SELF;
+ else if (strcasecmp(str, "selfsub") == 0)
+ mtype = DNS_SSUMATCHTYPE_SELFSUB;
+ else if (strcasecmp(str, "selfwild") == 0)
+ mtype = DNS_SSUMATCHTYPE_SELFWILD;
+ else
+ INSIST(0);
+
+ dns_fixedname_init(&fident);
+ str = cfg_obj_asstring(identity);
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
+ dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
+ "'%s' is not a valid name", str);
+ goto cleanup;
+ }
+
+ dns_fixedname_init(&fname);
+ str = cfg_obj_asstring(dname);
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(dns_fixedname_name(&fname), &b,
+ dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
+ "'%s' is not a valid name", str);
+ goto cleanup;
+ }
+
+ n = ns_config_listcount(typelist);
+ if (n == 0)
+ types = NULL;
+ else {
+ types = isc_mem_get(mctx, n * sizeof(dns_rdatatype_t));
+ if (types == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ }
+
+ i = 0;
+ for (element2 = cfg_list_first(typelist);
+ element2 != NULL;
+ element2 = cfg_list_next(element2))
+ {
+ const cfg_obj_t *typeobj;
+ isc_textregion_t r;
+
+ INSIST(i < n);
+
+ typeobj = cfg_listelt_value(element2);
+ str = cfg_obj_asstring(typeobj);
+ DE_CONST(str, r.base);
+ r.length = strlen(str);
+
+ result = dns_rdatatype_fromtext(&types[i++], &r);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
+ "'%s' is not a valid type", str);
+ isc_mem_put(mctx, types,
+ n * sizeof(dns_rdatatype_t));
+ goto cleanup;
+ }
+ }
+ INSIST(i == n);
+
+ result = dns_ssutable_addrule(table, grant,
+ dns_fixedname_name(&fident),
+ mtype,
+ dns_fixedname_name(&fname),
+ n, types);
+ if (types != NULL)
+ isc_mem_put(mctx, types, n * sizeof(dns_rdatatype_t));
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ }
+
+ result = ISC_R_SUCCESS;
+ dns_zone_setssutable(zone, table);
+
+ cleanup:
+ dns_ssutable_detach(&table);
+ return (result);
+}
+
+/*%
+ * Convert a config file zone type into a server zone type.
+ */
+static inline dns_zonetype_t
+zonetype_fromconfig(const cfg_obj_t *map) {
+ const cfg_obj_t *obj = NULL;
+ isc_result_t result;
+
+ result = cfg_map_get(map, "type", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ return (ns_config_getzonetype(obj));
+}
+
+/*%
+ * Helper function for strtoargv(). Pardon the gratuitous recursion.
+ */
+static isc_result_t
+strtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
+ char ***argvp, unsigned int n)
+{
+ isc_result_t result;
+
+ /* Discard leading whitespace. */
+ while (*s == ' ' || *s == '\t')
+ s++;
+
+ if (*s == '\0') {
+ /* We have reached the end of the string. */
+ *argcp = n;
+ *argvp = isc_mem_get(mctx, n * sizeof(char *));
+ if (*argvp == NULL)
+ return (ISC_R_NOMEMORY);
+ } else {
+ char *p = s;
+ while (*p != ' ' && *p != '\t' && *p != '\0')
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+
+ result = strtoargvsub(mctx, p, argcp, argvp, n + 1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ (*argvp)[n] = s;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Tokenize the string "s" into whitespace-separated words,
+ * return the number of words in '*argcp' and an array
+ * of pointers to the words in '*argvp'. The caller
+ * must free the array using isc_mem_put(). The string
+ * is modified in-place.
+ */
+static isc_result_t
+strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) {
+ return (strtoargvsub(mctx, s, argcp, argvp, 0));
+}
+
+static void
+checknames(dns_zonetype_t ztype, const cfg_obj_t **maps,
+ const cfg_obj_t **objp)
+{
+ const char *zone = NULL;
+ isc_result_t result;
+
+ switch (ztype) {
+ case dns_zone_slave: zone = "slave"; break;
+ case dns_zone_master: zone = "master"; break;
+ default:
+ INSIST(0);
+ }
+ result = ns_checknames_get(maps, zone, objp);
+ INSIST(result == ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac,
+ dns_zone_t *zone)
+{
+ isc_result_t result;
+ const char *zname;
+ dns_rdataclass_t zclass;
+ dns_rdataclass_t vclass;
+ const cfg_obj_t *maps[5];
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *obj;
+ const char *filename = NULL;
+ dns_notifytype_t notifytype = dns_notifytype_yes;
+ isc_sockaddr_t *addrs;
+ dns_name_t **keynames;
+ isc_uint32_t count;
+ char *cpval;
+ unsigned int dbargc;
+ char **dbargv;
+ static char default_dbtype[] = "rbt";
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ dns_dialuptype_t dialup = dns_dialuptype_no;
+ dns_zonetype_t ztype;
+ int i;
+ isc_int32_t journal_size;
+ isc_boolean_t multi;
+ isc_boolean_t alt;
+ dns_view_t *view;
+ isc_boolean_t check = ISC_FALSE, fail = ISC_FALSE;
+ isc_boolean_t warn = ISC_FALSE, ignore = ISC_FALSE;
+ isc_boolean_t ixfrdiff;
+ dns_masterformat_t masterformat;
+
+ i = 0;
+ if (zconfig != NULL) {
+ zoptions = cfg_tuple_get(zconfig, "options");
+ maps[i++] = zoptions;
+ }
+ if (vconfig != NULL)
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ if (config != NULL) {
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL)
+ maps[i++] = options;
+ }
+ maps[i++] = ns_g_defaults;
+ maps[i++] = NULL;
+
+ if (vconfig != NULL)
+ RETERR(ns_config_getclass(cfg_tuple_get(vconfig, "class"),
+ dns_rdataclass_in, &vclass));
+ else
+ vclass = dns_rdataclass_in;
+
+ /*
+ * Configure values common to all zone types.
+ */
+
+ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+
+ RETERR(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
+ vclass, &zclass));
+ dns_zone_setclass(zone, zclass);
+
+ ztype = zonetype_fromconfig(zoptions);
+ dns_zone_settype(zone, ztype);
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "database", &obj);
+ if (result == ISC_R_SUCCESS)
+ cpval = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
+ else
+ cpval = default_dbtype;
+
+ if (cpval == NULL)
+ return(ISC_R_NOMEMORY);
+
+ result = strtoargv(mctx, cpval, &dbargc, &dbargv);
+ if (result != ISC_R_SUCCESS && cpval != default_dbtype) {
+ isc_mem_free(mctx, cpval);
+ return (result);
+ }
+
+ /*
+ * ANSI C is strange here. There is no logical reason why (char **)
+ * cannot be promoted automatically to (const char * const *) by the
+ * compiler w/o generating a warning.
+ */
+ result = dns_zone_setdbtype(zone, dbargc, (const char * const *)dbargv);
+ isc_mem_put(mctx, dbargv, dbargc * sizeof(*dbargv));
+ if (cpval != default_dbtype)
+ isc_mem_free(mctx, cpval);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "file", &obj);
+ if (result == ISC_R_SUCCESS)
+ filename = cfg_obj_asstring(obj);
+
+ masterformat = dns_masterformat_text;
+ obj = NULL;
+ result= ns_config_get(maps, "masterfile-format", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *masterformatstr = cfg_obj_asstring(obj);
+
+ if (strcasecmp(masterformatstr, "text") == 0)
+ masterformat = dns_masterformat_text;
+ else if (strcasecmp(masterformatstr, "raw") == 0)
+ masterformat = dns_masterformat_raw;
+ else
+ INSIST(0);
+ }
+ RETERR(dns_zone_setfile2(zone, filename, masterformat));
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "journal", &obj);
+ if (result == ISC_R_SUCCESS)
+ RETERR(dns_zone_setjournal(zone, cfg_obj_asstring(obj)));
+
+ if (ztype == dns_zone_slave)
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ "allow-notify", ac, zone,
+ dns_zone_setnotifyacl,
+ dns_zone_clearnotifyacl));
+ /*
+ * XXXAG This probably does not make sense for stubs.
+ */
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ "allow-query", ac, zone,
+ dns_zone_setqueryacl,
+ dns_zone_clearqueryacl));
+
+ obj = NULL;
+ result = ns_config_get(maps, "dialup", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj))
+ dialup = dns_dialuptype_yes;
+ else
+ dialup = dns_dialuptype_no;
+ } else {
+ const char *dialupstr = cfg_obj_asstring(obj);
+ if (strcasecmp(dialupstr, "notify") == 0)
+ dialup = dns_dialuptype_notify;
+ else if (strcasecmp(dialupstr, "notify-passive") == 0)
+ dialup = dns_dialuptype_notifypassive;
+ else if (strcasecmp(dialupstr, "refresh") == 0)
+ dialup = dns_dialuptype_refresh;
+ else if (strcasecmp(dialupstr, "passive") == 0)
+ dialup = dns_dialuptype_passive;
+ else
+ INSIST(0);
+ }
+ dns_zone_setdialup(zone, dialup);
+
+ obj = NULL;
+ result = ns_config_get(maps, "zone-statistics", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setstatistics(zone, cfg_obj_asboolean(obj)));
+
+ /*
+ * Configure master functionality. This applies
+ * to primary masters (type "master") and slaves
+ * acting as masters (type "slave"), but not to stubs.
+ */
+ if (ztype != dns_zone_stub) {
+ obj = NULL;
+ result = ns_config_get(maps, "notify", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj))
+ notifytype = dns_notifytype_yes;
+ else
+ notifytype = dns_notifytype_no;
+ } else {
+ const char *notifystr = cfg_obj_asstring(obj);
+ if (strcasecmp(notifystr, "explicit") == 0)
+ notifytype = dns_notifytype_explicit;
+ else if (strcasecmp(notifystr, "master-only") == 0)
+ notifytype = dns_notifytype_masteronly;
+ else
+ INSIST(0);
+ }
+ dns_zone_setnotifytype(zone, notifytype);
+
+ obj = NULL;
+ result = ns_config_get(maps, "also-notify", &obj);
+ if (result == ISC_R_SUCCESS) {
+ isc_sockaddr_t *addrs = NULL;
+ isc_uint32_t addrcount;
+ result = ns_config_getiplist(config, obj, 0, mctx,
+ &addrs, &addrcount);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_zone_setalsonotify(zone, addrs,
+ addrcount);
+ ns_config_putiplist(mctx, &addrs, addrcount);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else
+ RETERR(dns_zone_setalsonotify(zone, NULL, 0));
+
+ obj = NULL;
+ result = ns_config_get(maps, "notify-source", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setnotifysrc4(zone, cfg_obj_assockaddr(obj)));
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "notify-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setnotifysrc6(zone, cfg_obj_assockaddr(obj)));
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+
+ dns_zone_setisself(zone, ns_client_isself, NULL);
+
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ "allow-transfer", ac, zone,
+ dns_zone_setxfracl,
+ dns_zone_clearxfracl));
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-time-out", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setmaxxfrout(zone, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-idle-out", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setidleout(zone, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-journal-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setjournalsize(zone, -1);
+ if (cfg_obj_isstring(obj)) {
+ const char *str = cfg_obj_asstring(obj);
+ INSIST(strcasecmp(str, "unlimited") == 0);
+ journal_size = ISC_UINT32_MAX / 2;
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > ISC_UINT32_MAX / 2) {
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_ERROR,
+ "'max-journal-size "
+ "%" ISC_PRINT_QUADFORMAT "d' "
+ "is too large",
+ value);
+ RETERR(ISC_R_RANGE);
+ }
+ journal_size = (isc_uint32_t)value;
+ }
+ dns_zone_setjournalsize(zone, journal_size);
+
+ obj = NULL;
+ result = ns_config_get(maps, "ixfr-from-differences", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj))
+ ixfrdiff = cfg_obj_asboolean(obj);
+ else if (strcasecmp(cfg_obj_asstring(obj), "master") &&
+ ztype == dns_zone_master)
+ ixfrdiff = ISC_TRUE;
+ else if (strcasecmp(cfg_obj_asstring(obj), "slave") &&
+ ztype == dns_zone_slave)
+ ixfrdiff = ISC_TRUE;
+ else
+ ixfrdiff = ISC_FALSE;
+ dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS, ixfrdiff);
+
+ checknames(ztype, maps, &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ fail = ISC_FALSE;
+ check = ISC_TRUE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ fail = check = ISC_TRUE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ fail = check = ISC_FALSE;
+ } else
+ INSIST(0);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, check);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL, fail);
+
+ obj = NULL;
+ result = ns_config_get(maps, "notify-delay", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setnotifydelay(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-sibling", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKSIBLING,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "zero-no-soa-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setzeronosoattl(zone, cfg_obj_asboolean(obj));
+ }
+
+ /*
+ * Configure update-related options. These apply to
+ * primary masters only.
+ */
+ if (ztype == dns_zone_master) {
+ dns_acl_t *updateacl;
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ "allow-update", ac, zone,
+ dns_zone_setupdateacl,
+ dns_zone_clearupdateacl));
+
+ updateacl = dns_zone_getupdateacl(zone);
+ if (updateacl != NULL && dns_acl_isinsecure(updateacl))
+ isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "zone '%s' allows updates by IP "
+ "address, which is insecure",
+ zname);
+
+ RETERR(configure_zone_ssutable(zoptions, zone));
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-validity-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setsigvalidityinterval(zone,
+ cfg_obj_asuint32(obj) * 86400);
+
+ obj = NULL;
+ result = ns_config_get(maps, "key-directory", &obj);
+ if (result == ISC_R_SUCCESS) {
+ filename = cfg_obj_asstring(obj);
+ if (!isc_file_isabsolute(filename)) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "key-directory '%s' "
+ "is not absolute", filename);
+ return (ISC_R_FAILURE);
+ }
+ RETERR(dns_zone_setkeydirectory(zone, filename));
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-wildcard", &obj);
+ if (result == ISC_R_SUCCESS)
+ check = cfg_obj_asboolean(obj);
+ else
+ check = ISC_FALSE;
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKWILDCARD, check);
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-mx", &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ fail = ISC_FALSE;
+ check = ISC_TRUE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ fail = check = ISC_TRUE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ fail = check = ISC_FALSE;
+ } else
+ INSIST(0);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKMX, check);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKMXFAIL, fail);
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-integrity", &obj);
+ INSIST(obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKINTEGRITY,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-mx-cname", &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ warn = ISC_TRUE;
+ ignore = ISC_FALSE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ warn = ignore = ISC_FALSE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ warn = ignore = ISC_TRUE;
+ } else
+ INSIST(0);
+ dns_zone_setoption(zone, DNS_ZONEOPT_WARNMXCNAME, warn);
+ dns_zone_setoption(zone, DNS_ZONEOPT_IGNOREMXCNAME, ignore);
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-srv-cname", &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ warn = ISC_TRUE;
+ ignore = ISC_FALSE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ warn = ignore = ISC_FALSE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ warn = ignore = ISC_TRUE;
+ } else
+ INSIST(0);
+ dns_zone_setoption(zone, DNS_ZONEOPT_WARNSRVCNAME, warn);
+ dns_zone_setoption(zone, DNS_ZONEOPT_IGNORESRVCNAME, ignore);
+
+ obj = NULL;
+ result = ns_config_get(maps, "update-check-ksk", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setoption(zone, DNS_ZONEOPT_UPDATECHECKKSK,
+ cfg_obj_asboolean(obj));
+ }
+
+ /*
+ * Configure update-related options. These apply to
+ * primary masters only.
+ */
+ if (ztype == dns_zone_master) {
+ dns_acl_t *updateacl;
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ "allow-update", ac, zone,
+ dns_zone_setupdateacl,
+ dns_zone_clearupdateacl));
+
+ updateacl = dns_zone_getupdateacl(zone);
+ if (updateacl != NULL && dns_acl_isinsecure(updateacl))
+ isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "zone '%s' allows updates by IP "
+ "address, which is insecure",
+ zname);
+
+ RETERR(configure_zone_ssutable(zoptions, zone));
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-validity-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setsigvalidityinterval(zone,
+ cfg_obj_asuint32(obj) * 86400);
+
+ obj = NULL;
+ result = ns_config_get(maps, "key-directory", &obj);
+ if (result == ISC_R_SUCCESS) {
+ filename = cfg_obj_asstring(obj);
+ if (!isc_file_isabsolute(filename)) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "key-directory '%s' "
+ "is not absolute", filename);
+ return (ISC_R_FAILURE);
+ }
+ RETERR(dns_zone_setkeydirectory(zone, filename));
+ }
+
+ } else if (ztype == dns_zone_slave) {
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ "allow-update-forwarding", ac, zone,
+ dns_zone_setforwardacl,
+ dns_zone_clearforwardacl));
+ }
+
+ /*
+ * Configure slave functionality.
+ */
+ switch (ztype) {
+ case dns_zone_slave:
+ case dns_zone_stub:
+ count = 0;
+ obj = NULL;
+ result = cfg_map_get(zoptions, "masters", &obj);
+ if (obj != NULL) {
+ addrs = NULL;
+ keynames = NULL;
+ RETERR(ns_config_getipandkeylist(config, obj, mctx,
+ &addrs, &keynames,
+ &count));
+ result = dns_zone_setmasterswithkeys(zone, addrs,
+ keynames, count);
+ ns_config_putipandkeylist(mctx, &addrs, &keynames,
+ count);
+ } else
+ result = dns_zone_setmasters(zone, NULL, 0);
+ RETERR(result);
+
+ multi = ISC_FALSE;
+ if (count > 1) {
+ obj = NULL;
+ result = ns_config_get(maps, "multi-master", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ multi = cfg_obj_asboolean(obj);
+ }
+ dns_zone_setoption(zone, DNS_ZONEOPT_MULTIMASTER, multi);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-time-in", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setmaxxfrin(zone, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-idle-in", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setidlein(zone, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-refresh-time", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setmaxrefreshtime(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "min-refresh-time", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setminrefreshtime(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-retry-time", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setmaxretrytime(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "min-retry-time", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setminretrytime(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "transfer-source", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setxfrsource4(zone, cfg_obj_assockaddr(obj)));
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "transfer-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setxfrsource6(zone, cfg_obj_assockaddr(obj)));
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "alt-transfer-source", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setaltxfrsource4(zone, cfg_obj_assockaddr(obj)));
+
+ obj = NULL;
+ result = ns_config_get(maps, "alt-transfer-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setaltxfrsource6(zone, cfg_obj_assockaddr(obj)));
+
+ obj = NULL;
+ (void)ns_config_get(maps, "use-alt-transfer-source", &obj);
+ if (obj == NULL) {
+ /*
+ * Default off when views are in use otherwise
+ * on for BIND 8 compatibility.
+ */
+ view = dns_zone_getview(zone);
+ if (view != NULL && strcmp(view->name, "_default") == 0)
+ alt = ISC_TRUE;
+ else
+ alt = ISC_FALSE;
+ } else
+ alt = cfg_obj_asboolean(obj);
+ dns_zone_setoption(zone, DNS_ZONEOPT_USEALTXFRSRC, alt);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_boolean_t
+ns_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig) {
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *obj = NULL;
+ const char *cfilename;
+ const char *zfilename;
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+
+ if (zonetype_fromconfig(zoptions) != dns_zone_gettype(zone))
+ return (ISC_FALSE);
+
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "file", &obj);
+ if (obj != NULL)
+ cfilename = cfg_obj_asstring(obj);
+ else
+ cfilename = NULL;
+ zfilename = dns_zone_getfile(zone);
+ if (!((cfilename == NULL && zfilename == NULL) ||
+ (cfilename != NULL && zfilename != NULL &&
+ strcmp(cfilename, zfilename) == 0)))
+ return (ISC_FALSE);
+
+ return (ISC_TRUE);
+}
OpenPOWER on IntegriCloud