]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
move DNS64 implementation into an external module
authorEvan Hunt <each@isc.org>
Thu, 1 Nov 2018 15:48:43 +0000 (08:48 -0700)
committerEvan Hunt <each@isc.org>
Fri, 30 Aug 2019 22:13:38 +0000 (15:13 -0700)
- dns64 implementation has been moved from query.c to the dns64 module.
- the code in lib/dns/dns64.c has not yet been moved.
- the module does not yet parse dns64 options in named.conf; that's
  still done by named.
- the module does not have persistent storage; we still use the client
  object.
- made more functions globally accessible so they can be called from
  modules: ns_query_lookup(), ns_query_addsoa(), ns_query_nodata(),
  ns_query_ncache(), ns_query_setorder().

14 files changed:
bin/named/server.c
bin/plugins/Makefile.in
bin/plugins/dns64.8 [new file with mode: 0644]
bin/plugins/dns64.c [new file with mode: 0644]
bin/plugins/dns64.docbook [new file with mode: 0644]
bin/plugins/dns64.html [new file with mode: 0644]
bin/tests/system/dns64/ns1/named.conf.in
bin/tests/system/dns64/ns2/named.conf.in
bin/tests/system/dns64/ns3/named.conf.in
doc/arm/Bv9ARM-book.xml
doc/arm/man.dns64.html [new file with mode: 0644]
doc/arm/plugins.xml
lib/ns/include/ns/query.h
lib/ns/query.c

index 13cb46e1b968dd0a33abfabccf7370d10a1ee63a..9c3bd99e3cf15c17e80ec376dfae9ca947169487 100644 (file)
@@ -5300,15 +5300,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        }
 
 #ifdef HAVE_DLOPEN
-       /*
-        * XXX: because dns64 is still automatically configured,
-        * we need to create a hooktable for every view regardless
-        * of whether hook_list is set or not.
- */
-#if 0
-       if (plugin_list != NULL)
-#endif
-       {
+       if (plugin_list != NULL) {
                INSIST(view->hooktable == NULL);
                CHECK(ns_hooktable_create(view->mctx,
                                  (ns_hooktable_t **) &view->hooktable));
@@ -5320,9 +5312,6 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
                CHECK(cfg_pluginlist_foreach(config, plugin_list, named_g_lctx,
                                             register_one_plugin, view));
        }
-
-       /* XXX: temporary initialization for built in dns64 hooks */
-       ns__query_inithooks(view);
 #endif
 
        /*
index 0c4f7695d5290e8e5d70ecb892467674f98af4ac..9b667c3c233dc79db1fb273bcebbba243ef9bc7d 100644 (file)
@@ -24,24 +24,32 @@ NSLIBS =    ../../lib/ns/libns.@A@
 
 LIBS =
 
-SO_TARGETS =   lib/filter-aaaa.@SO@
-SO_INSTALL =   filter-aaaa.@SO@
+SO_TARGETS =   lib/dns64.@SO@ lib/filter-aaaa.@SO@
+SO_INSTALL =   dns64.@SO@ filter-aaaa.@SO@
 TARGETS =      @SO_TARGETS@
 
-SO_OBJS =      filter-aaaa.@O@
-SO_SRCS =      filter-aaaa.c
+SO_OBJS =      dns64.@O@ filter-aaaa.@O@
+SO_SRCS =      dns64.c filter-aaaa.c
 
 CFLAGS =       @CFLAGS@ @SO_CFLAGS@
 SO_LDFLAGS =   @LDFLAGS@ @SO_LDFLAGS@
 
-MANPAGES =     filter-aaaa.8
+MANPAGES =     dns64.8 filter-aaaa.8
 
-HTMLPAGES =    filter-aaaa.html
+HTMLPAGES =    dns64.html filter-aaaa.html
 
 MANOBJS =      ${MANPAGES} ${HTMLPAGES}
 
 @BIND9_MAKE_RULES@
 
+lib/dns64.@SO@: dns64.@SO@
+       $(SHELL) ${top_srcdir}/mkinstalldirs `pwd`/lib
+       ${LIBTOOL_MODE_INSTALL} ${INSTALL} dns64.@SO@ `pwd`/lib
+
+dns64.@SO@: dns64.@O@
+       ${LIBTOOL_MODE_LINK} @SO_LD@ ${SO_LDFLAGS} -o $@ \
+               dns64.@O@ ${LIBS}
+
 lib/filter-aaaa.@SO@: filter-aaaa.@SO@
        $(SHELL) ${top_srcdir}/mkinstalldirs `pwd`/lib
        ${LIBTOOL_MODE_INSTALL} ${INSTALL} filter-aaaa.@SO@ `pwd`/lib
@@ -56,7 +64,7 @@ docclean manclean maintainer-clean::
        rm -f ${MANOBJS}
 
 clean distclean::
-       rm -f filter-aaaa.so
+       rm -f dns64.@SO@ filter-aaaa.@SO@
        rm -f ${TARGETS} ${OBJS}
 
 installdirs:
@@ -73,7 +81,10 @@ install:: @SO_TARGETS@ installdirs
                fi \
        done
        ${INSTALL_DATA} ${srcdir}/filter-aaaa.8 ${DESTDIR}${mandir}/man8
+       ${INSTALL_DATA} ${srcdir}/dns64.8 ${DESTDIR}${mandir}/man8
 
 uninstall::
+       ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${plugindir}/dns64.@SO@
        ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${plugindir}/filter-aaaa.@SO@
+       rm -f ${DESTDIR}${mandir}/man8/dns64.8
        rm -f ${DESTDIR}${mandir}/man8/filter-aaaa.8
diff --git a/bin/plugins/dns64.8 b/bin/plugins/dns64.8
new file mode 100644 (file)
index 0000000..3b54513
--- /dev/null
@@ -0,0 +1,144 @@
+.\" Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+.\" 
+.\" This Source Code Form is subject to the terms of the Mozilla Public
+.\" License, v. 2.0. If a copy of the MPL was not distributed with this
+.\" file, You can obtain one at http://mozilla.org/MPL/2.0/.
+.\"
+.hy 0
+.ad l
+'\" t
+.\"     Title: dns64.so
+.\"    Author: 
+.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
+.\"      Date: 2018-11-30
+.\"    Manual: BIND9
+.\"    Source: ISC
+.\"  Language: English
+.\"
+.TH "DNS64\&.SO" "8" "2018\-11\-30" "ISC" "BIND9"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+dns64.so \- perform DNS64 synthesis
+.SH "SYNOPSIS"
+.HP 24
+\fBplugin query "dns64\&.so"\fR [\fI{\ parameters\ }\fR];
+.SH "DESCRIPTION"
+.PP
+\fBdns64\&.so\fR
+is a query plugin module for
+\fBnamed\fR, enabling
+\fBnamed\fR
+to perform DNS64 address synthesis\&.
+.PP
+Until BIND 9\&.12, this feature was implemented natively in
+\fBnamed\fR
+and enabled with the
+\fBdns64\fR
+option\&. This option is now deprecated in
+named\&.conf, but can be passed as parameters to the
+\fBdns64\&.so\fR
+plugin, for example:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+       acl rfc1918 { 10/8; 192\&.168/16; 172\&.16/12; };
+       plugin query "/usr/local/lib/dns64\&.so" {
+               dns64 64:FF9B::/96 {
+                       clients { any; };
+                       mapped { !rfc1918; any; };
+                       exclude { 64:FF9B::/96; ::ffff:0000:0000/96; };
+                       suffix ::;
+               };
+               dns64\-server "dns64\&.example\&.net\&.";
+               dns64\-contact "hostmaster\&.example\&.net\&.";
+       };
+.fi
+.if n \{\
+.RE
+.\}
+.PP
+This plugin enables
+\fBnamed\fR
+to return mapped IPv4 addresses to AAAA queries when there are no AAAA records\&. It is intended to be used in conjunction with NAT64\&.
+.PP
+Each
+\fBdns64\fR
+option defined in the plugin parameters defines one DNS64 prefix\&. Multiple DNS64 prefixes can be defined\&.
+.PP
+Compatible IPv6 prefixes have lengths of 32, 40, 48, 56, 64 and 96 as per RFC 6052\&.
+.PP
+Additionally a reverse IP6\&.ARPA zone will be created for the prefix to provide a mapping from the IP6\&.ARPA names to the corresponding IN\-ADDR\&.ARPA names using synthesized CNAMEs\&.
+\fBdns64\-server\fR
+and
+\fBdns64\-contact\fR
+can be used to specify the name of the server and contact for the zones\&. These are not settable on a per\-prefix basis\&.
+.PP
+Each
+\fBdns64\fR
+supports an optional
+\fBclients\fR
+ACL that determines which clients are affected by this directive\&. If not defined, it defaults to
+\fBany;\fR\&.
+.PP
+Each
+\fBdns64\fR
+supports an optional
+\fBmapped\fR
+ACL that selects which IPv4 addresses are to be mapped in the corresponding A RRset\&. If not defined it defaults to
+\fBany;\fR\&.
+.PP
+Normally, DNS64 won\*(Aqt apply to a domain name that owns one or more AAAA records; these records will simply be returned\&. The optional
+\fBexclude\fR
+ACL allows specification of a list of IPv6 addresses that will be ignored if they appear in a domain name\*(Aqs AAAA records, and DNS64 will be applied to any A records the domain name owns\&. If not defined,
+\fBexclude\fR
+defaults to ::ffff:0\&.0\&.0\&.0/96\&.
+.PP
+A optional
+\fBsuffix\fR
+can also be defined to set the bits trailing the mapped IPv4 address bits\&. By default these bits are set to
+\fB::\fR\&. The bits matching the prefix and mapped IPv4 address must be zero\&.
+.PP
+If
+\fBrecursive\-only\fR
+is set to
+\fByes\fR
+the DNS64 synthesis will only happen for recursive queries\&. The default is
+\fBno\fR\&.
+.PP
+If
+\fBbreak\-dnssec\fR
+is set to
+\fByes\fR
+the DNS64 synthesis will happen even if the result, if validated, would cause a DNSSEC validation failure\&. If this option is set to
+\fBno\fR
+(the default), the DO is set on the incoming query, and there are RRSIGs on the applicable records, then synthesis will not happen\&.
+.SH "SEE ALSO"
+.PP
+BIND 9 Administrator Reference Manual\&.
+.SH "AUTHOR"
+.PP
+\fBInternet Systems Consortium, Inc\&.\fR
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2018 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/bin/plugins/dns64.c b/bin/plugins/dns64.c
new file mode 100644 (file)
index 0000000..b669c37
--- /dev/null
@@ -0,0 +1,952 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/hash.h>
+#include <isc/list.h>
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <ns/client.h>
+#include <ns/hooks.h>
+#include <ns/interfacemgr.h>
+#include <ns/log.h>
+#include <ns/query.h>
+#include <ns/types.h>
+
+#include <dns/db.h>
+#include <dns/dns64.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rdataset.h>
+#include <dns/rdatalist.h>
+#include <dns/result.h>
+#include <dns/types.h>
+#include <dns/view.h>
+
+#define CHECK(op)                                              \
+       do {                                                    \
+               result = (op);                                  \
+               if (result != ISC_R_SUCCESS) {                  \
+                       goto cleanup;                           \
+               }                                               \
+       } while (0)
+
+#define QUERY_ERROR(qctx, r) \
+do { \
+       qctx->result = r; \
+       qctx->want_restart = false; \
+       qctx->line = __LINE__; \
+} while (0)
+
+#define SAVE(a, b) do { INSIST(a == NULL); a = b; b = NULL; } while (0)
+#define RESTORE(a, b) SAVE(a, b)
+
+/*
+ * Client attribute tests.
+ */
+/*% Recursion OK? */
+#define RECURSIONOK(c)         (((c)->query.attributes & \
+                                 NS_QUERYATTR_RECURSIONOK) != 0)
+/*% Want DNSSEC? */
+#define WANTDNSSEC(c)          (((c)->attributes & \
+                                 NS_CLIENTATTR_WANTDNSSEC) != 0)
+
+#define DNS64(c)               (((c)->query.attributes & \
+                                 NS_QUERYATTR_DNS64) != 0)
+
+#define DNS64EXCLUDE(c)                (((c)->query.attributes & \
+                                 NS_QUERYATTR_DNS64EXCLUDE) != 0)
+
+static uint32_t
+dns64_ttl(dns_db_t *db, dns_dbversion_t *version);
+
+static bool
+dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset,
+            dns_rdataset_t *sigrdataset);
+
+static isc_result_t
+dns64_synth(query_ctx_t *qctx);
+
+static void
+dns64_filter(query_ctx_t *qctx);
+
+/*
+ * Hook registration structures: pointers to these structures will
+ * be added to a hook table when this module is registered.
+ */
+static ns_hookresult_t
+dns64_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp);
+static ns_hook_t dns64_init = {
+       .action = dns64_qctx_initialize,
+};
+
+static ns_hookresult_t
+dns64_respond_begin(void *arg, void *cbdata, isc_result_t *resp);
+static ns_hook_t dns64_respbegin = {
+       .action = dns64_respond_begin,
+};
+
+static ns_hookresult_t
+dns64_addanswer(void *arg, void *cbdata, isc_result_t *resp);
+static ns_hook_t dns64_addanswerbegin = {
+       .action = dns64_addanswer,
+};
+
+static ns_hookresult_t
+dns64_resume_restored(void *arg, void *cbdata, isc_result_t *resp);
+static ns_hook_t dns64_resumerest = {
+       .action = dns64_resume_restored,
+};
+
+static ns_hookresult_t
+dns64_notfound_recurse(void *arg, void *cbdata, isc_result_t *resp);
+static ns_hook_t dns64_nfrec = {
+       .action = dns64_notfound_recurse,
+};
+
+static ns_hookresult_t
+dns64_delegation_recurse(void *arg, void *cbdata, isc_result_t *resp);
+static ns_hook_t dns64_delrec = {
+       .action = dns64_delegation_recurse,
+};
+
+static ns_hookresult_t
+dns64_nodata_begin(void *arg, void *cbdata, isc_result_t *resp);
+static ns_hook_t dns64_nodata = {
+       .action = dns64_nodata_begin,
+};
+
+static ns_hookresult_t
+dns64_zerottl_recurse(void *arg, void *cbdata, isc_result_t *resp);
+static ns_hook_t dns64_zerottl = {
+       .action = dns64_zerottl_recurse,
+};
+
+static ns_hookresult_t
+dns64_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp);
+static ns_hook_t dns64_destroy = {
+       .action = dns64_qctx_destroy,
+};
+
+/**
+ ** Support for parsing of parameters and configuration of the module.
+ **/
+
+/**
+ ** Mandatory plugin API functions:
+ **
+ ** - plugin_check
+ ** - plugin_destroy
+ ** - plugin_register
+ ** - plugin_version
+ **/
+
+/*
+ * Called by ns_plugin_register() to register hook actions into
+ * a hook table.
+ */
+isc_result_t
+plugin_register(const char *parameters,
+               const void *cfg, const char *cfg_file, unsigned long cfg_line,
+               isc_mem_t *mctx, isc_log_t *lctx, void *actx,
+               ns_hooktable_t *hooktable, void **instp)
+{
+
+       UNUSED(cfg);
+       UNUSED(actx);
+       UNUSED(instp);
+
+       isc_log_write(lctx, NS_LOGCATEGORY_GENERAL,
+                     NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
+                     "loading 'dns64' "
+                     "module from %s:%lu, %s parameters",
+                     cfg_file, cfg_line, parameters != NULL ? "with" : "no");
+
+       ns_hook_add(hooktable, mctx,
+                   NS_QUERY_QCTX_INITIALIZED, &dns64_init);
+       ns_hook_add(hooktable, mctx,
+                   NS_QUERY_RESPOND_BEGIN, &dns64_respbegin);
+       ns_hook_add(hooktable, mctx,
+                   NS_QUERY_ADDANSWER_BEGIN, &dns64_addanswerbegin);
+       ns_hook_add(hooktable, mctx,
+                   NS_QUERY_RESUME_RESTORED, &dns64_resumerest);
+       ns_hook_add(hooktable, mctx,
+                   NS_QUERY_NOTFOUND_RECURSE, &dns64_nfrec);
+       ns_hook_add(hooktable, mctx,
+                   NS_QUERY_DELEGATION_RECURSE_BEGIN, &dns64_delrec);
+       ns_hook_add(hooktable, mctx,
+                   NS_QUERY_NODATA_BEGIN, &dns64_nodata);
+       ns_hook_add(hooktable, mctx,
+                   NS_QUERY_ZEROTTL_RECURSE, &dns64_zerottl);
+       ns_hook_add(hooktable, mctx,
+                   NS_QUERY_QCTX_DESTROYED, &dns64_destroy);
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+plugin_check(const char *parameters,
+            const void *cfg, const char *cfg_file, unsigned long cfg_line,
+            isc_mem_t *mctx, isc_log_t *lctx, void *actx)
+{
+       UNUSED(parameters);
+       UNUSED(cfg_file);
+       UNUSED(cfg_line);
+       UNUSED(cfg);
+       UNUSED(mctx);
+       UNUSED(lctx);
+       UNUSED(actx);
+
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Called by ns_plugins_free(); frees memory allocated by
+ * the module when it was registered.
+ */
+void
+plugin_destroy(void **instp) {
+       UNUSED(instp);
+
+       return;
+
+}
+
+/*
+ * Returns plugin API version for compatibility checks.
+ */
+int
+plugin_version(void) {
+       return (NS_PLUGIN_VERSION);
+}
+
+/**
+ ** DNS64 feature implementation begins here.
+ **/
+static uint32_t
+dns64_ttl(dns_db_t *db, dns_dbversion_t *version) {
+       dns_dbnode_t *node = NULL;
+       dns_rdata_soa_t soa;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+       uint32_t ttl = UINT32_MAX;
+
+       dns_rdataset_init(&rdataset);
+
+       result = dns_db_getoriginnode(db, &node);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+
+       result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa,
+                                    0, 0, &rdataset, NULL);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       result = dns_rdataset_first(&rdataset);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+
+       dns_rdataset_current(&rdataset, &rdata);
+       result = dns_rdata_tostruct(&rdata, &soa, NULL);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       ttl = ISC_MIN(rdataset.ttl, soa.minimum);
+
+cleanup:
+       if (dns_rdataset_isassociated(&rdataset))
+               dns_rdataset_disassociate(&rdataset);
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       return (ttl);
+}
+
+static bool
+dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset,
+            dns_rdataset_t *sigrdataset)
+{
+       isc_netaddr_t netaddr;
+       dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+       dns_dns64_t *dns64 = ISC_LIST_HEAD(client->view->dns64);
+       unsigned int flags = 0;
+       unsigned int i, count;
+       bool *aaaaok;
+
+       INSIST(client->dns64_aaaaok == NULL);
+       INSIST(client->dns64_aaaaoklen == 0);
+       INSIST(client->dns64_aaaa == NULL);
+       INSIST(client->dns64_sigaaaa == NULL);
+
+       if (dns64 == NULL)
+               return (true);
+
+       if (RECURSIONOK(client))
+               flags |= DNS_DNS64_RECURSIVE;
+
+       if (WANTDNSSEC(client) && sigrdataset != NULL &&
+           dns_rdataset_isassociated(sigrdataset))
+               flags |= DNS_DNS64_DNSSEC;
+
+       count = dns_rdataset_count(rdataset);
+       aaaaok = isc_mem_get(client->mctx, sizeof(bool) * count);
+
+       isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+       if (dns_dns64_aaaaok(dns64, &netaddr, client->signer,
+                            env, flags, rdataset, aaaaok, count))
+       {
+               for (i = 0; i < count; i++) {
+                       if (aaaaok != NULL && !aaaaok[i]) {
+                               SAVE(client->dns64_aaaaok, aaaaok);
+                               client->dns64_aaaaoklen = count;
+                               break;
+                       }
+               }
+               if (aaaaok != NULL)
+                       isc_mem_put(client->mctx, aaaaok,
+                                   sizeof(bool) * count);
+               return (true);
+       }
+       if (aaaaok != NULL)
+               isc_mem_put(client->mctx, aaaaok,
+                           sizeof(bool) * count);
+       return (NS_HOOK_CONTINUE);
+}
+
+static isc_result_t
+dns64_synth(query_ctx_t *qctx) {
+       ns_client_t *client = qctx->client;
+       dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
+       dns_name_t *name, *mname;
+       dns_rdata_t *dns64_rdata;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdatalist_t *dns64_rdatalist;
+       dns_rdataset_t *dns64_rdataset;
+       dns_rdataset_t *mrdataset;
+       isc_buffer_t *buffer;
+       isc_region_t r;
+       isc_result_t result;
+       dns_view_t *view = client->view;
+       isc_netaddr_t netaddr;
+       dns_dns64_t *dns64;
+       unsigned int flags = 0;
+       const dns_section_t section = DNS_SECTION_ANSWER;
+
+       /*%
+        * To the current response for 'qctx->client', add the answer RRset
+        * '*rdatasetp' and an optional signature set '*sigrdatasetp', with
+        * owner name '*namep', to the answer section, unless they are
+        * already there.  Also add any pertinent additional data.
+        *
+        * If 'qctx->dbuf' is not NULL, then 'qctx->fname' is the name
+        * whose data is stored 'qctx->dbuf'.  In this case,
+        * query_addrrset() guarantees that when it returns the name
+        * will either have been kept or released.
+        */
+       qctx->qtype = qctx->type = dns_rdatatype_aaaa;
+
+       name = qctx->fname;
+       mname = NULL;
+       mrdataset = NULL;
+       buffer = NULL;
+       dns64_rdata = NULL;
+       dns64_rdataset = NULL;
+       dns64_rdatalist = NULL;
+       result = dns_message_findname(client->message, section,
+                                     name, dns_rdatatype_aaaa,
+                                     qctx->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;
+                */
+               if (qctx->dbuf != NULL) {
+                       ns_client_releasename(client, &qctx->fname);
+               }
+               return (ISC_R_SUCCESS);
+       } else if (result == DNS_R_NXDOMAIN) {
+               /*
+                * The name doesn't exist.
+                */
+               if (qctx->dbuf != NULL) {
+                       ns_client_keepname(client, name, qctx->dbuf);
+               }
+               dns_message_addname(client->message, name, section);
+               qctx->fname = NULL;
+               mname = name;
+       } else {
+               RUNTIME_CHECK(result == DNS_R_NXRRSET);
+               if (qctx->dbuf != NULL) {
+                       ns_client_releasename(client, &qctx->fname);
+               }
+       }
+
+       if (qctx->rdataset->trust != dns_trust_secure) {
+               client->query.attributes &= ~NS_QUERYATTR_SECURE;
+       }
+
+       isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+       result = isc_buffer_allocate(client->mctx, &buffer,
+                                    view->dns64cnt * 16 *
+                                    dns_rdataset_count(qctx->rdataset));
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       result = dns_message_gettemprdataset(client->message,
+                                            &dns64_rdataset);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       result = dns_message_gettemprdatalist(client->message,
+                                             &dns64_rdatalist);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+
+       dns_rdatalist_init(dns64_rdatalist);
+       dns64_rdatalist->rdclass = dns_rdataclass_in;
+       dns64_rdatalist->type = dns_rdatatype_aaaa;
+       if (client->dns64_ttl != UINT32_MAX)
+               dns64_rdatalist->ttl = ISC_MIN(qctx->rdataset->ttl,
+                                              client->dns64_ttl);
+       else
+               dns64_rdatalist->ttl = ISC_MIN(qctx->rdataset->ttl, 600);
+
+       if (RECURSIONOK(client))
+               flags |= DNS_DNS64_RECURSIVE;
+
+       /*
+        * We use the signatures from the A lookup to set DNS_DNS64_DNSSEC
+        * as this provides a easy way to see if the answer was signed.
+        */
+       if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
+           dns_rdataset_isassociated(qctx->sigrdataset))
+               flags |= DNS_DNS64_DNSSEC;
+
+       for (result = dns_rdataset_first(qctx->rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(qctx->rdataset)) {
+               for (dns64 = ISC_LIST_HEAD(client->view->dns64);
+                    dns64 != NULL; dns64 = dns_dns64_next(dns64)) {
+
+                       dns_rdataset_current(qctx->rdataset, &rdata);
+                       isc_buffer_availableregion(buffer, &r);
+                       INSIST(r.length >= 16);
+                       result = dns_dns64_aaaafroma(dns64, &netaddr,
+                                                    client->signer, env, flags,
+                                                    rdata.data, r.base);
+                       if (result != ISC_R_SUCCESS) {
+                               dns_rdata_reset(&rdata);
+                               continue;
+                       }
+                       isc_buffer_add(buffer, 16);
+                       isc_buffer_remainingregion(buffer, &r);
+                       isc_buffer_forward(buffer, 16);
+                       result = dns_message_gettemprdata(client->message,
+                                                         &dns64_rdata);
+                       if (result != ISC_R_SUCCESS)
+                               goto cleanup;
+                       dns_rdata_init(dns64_rdata);
+                       dns_rdata_fromregion(dns64_rdata, dns_rdataclass_in,
+                                            dns_rdatatype_aaaa, &r);
+                       ISC_LIST_APPEND(dns64_rdatalist->rdata, dns64_rdata,
+                                       link);
+                       dns64_rdata = NULL;
+                       dns_rdata_reset(&rdata);
+               }
+       }
+       if (result != ISC_R_NOMORE)
+               goto cleanup;
+
+       if (ISC_LIST_EMPTY(dns64_rdatalist->rdata))
+               goto cleanup;
+
+       result = dns_rdatalist_tordataset(dns64_rdatalist, dns64_rdataset);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       dns_rdataset_setownercase(dns64_rdataset, mname);
+       client->query.attributes |= NS_QUERYATTR_NOADDITIONAL;
+       dns64_rdataset->trust = qctx->rdataset->trust;
+
+       /* Add rdataset to mname */
+       ISC_LIST_APPEND(mname->list, dns64_rdataset, link);
+
+       ns_query_setorder(client, mname, dns64_rdataset);
+
+       dns64_rdataset = NULL;
+       dns64_rdatalist = NULL;
+       dns_message_takebuffer(client->message, &buffer);
+
+       /*
+        * XXX: this functionality will need to be restored
+        * inc_stats(client, ns_statscounter_dns64);
+        */
+       result = ISC_R_SUCCESS;
+
+ cleanup:
+       if (buffer != NULL)
+               isc_buffer_free(&buffer);
+
+       if (dns64_rdata != NULL)
+               dns_message_puttemprdata(client->message, &dns64_rdata);
+
+       if (dns64_rdataset != NULL)
+               dns_message_puttemprdataset(client->message, &dns64_rdataset);
+
+       if (dns64_rdatalist != NULL) {
+               for (dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata);
+                    dns64_rdata != NULL;
+                    dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata))
+               {
+                       ISC_LIST_UNLINK(dns64_rdatalist->rdata,
+                                       dns64_rdata, link);
+                       dns_message_puttemprdata(client->message, &dns64_rdata);
+               }
+               dns_message_puttemprdatalist(client->message, &dns64_rdatalist);
+       }
+
+       return (result);
+}
+
+static void
+dns64_filter(query_ctx_t *qctx) {
+       ns_client_t *client = qctx->client;
+       dns_name_t *name, *mname;
+       dns_rdata_t *myrdata;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdatalist_t *myrdatalist;
+       dns_rdataset_t *myrdataset;
+       isc_buffer_t *buffer;
+       isc_region_t r;
+       isc_result_t result;
+       unsigned int i;
+       const dns_section_t section = DNS_SECTION_ANSWER;
+
+       INSIST(client->dns64_aaaaok != NULL);
+       INSIST(client->dns64_aaaaoklen ==
+              dns_rdataset_count(qctx->rdataset));
+
+       name = qctx->fname;
+       mname = NULL;
+       buffer = NULL;
+       myrdata = NULL;
+       myrdataset = NULL;
+       myrdatalist = NULL;
+       result = dns_message_findname(client->message, section,
+                                     name, dns_rdatatype_aaaa,
+                                     qctx->rdataset->covers,
+                                     &mname, &myrdataset);
+       if (result == ISC_R_SUCCESS) {
+               /*
+                * We've already got an RRset of the given name and type.
+                * There's nothing else to do;
+                */
+               if (qctx->dbuf != NULL) {
+                       ns_client_releasename(client, &qctx->fname);
+               }
+               return;
+       } else if (result == DNS_R_NXDOMAIN) {
+               mname = name;
+               qctx->fname = NULL;
+       } else {
+               RUNTIME_CHECK(result == DNS_R_NXRRSET);
+               if (qctx->dbuf != NULL) {
+                       ns_client_releasename(client, &qctx->fname);
+               }
+               qctx->dbuf = NULL;
+       }
+
+       if (qctx->rdataset->trust != dns_trust_secure) {
+               client->query.attributes &= ~NS_QUERYATTR_SECURE;
+       }
+
+       result = isc_buffer_allocate(client->mctx, &buffer,
+                                    16 * dns_rdataset_count(qctx->rdataset));
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       result = dns_message_gettemprdataset(client->message, &myrdataset);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       result = dns_message_gettemprdatalist(client->message, &myrdatalist);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+
+       dns_rdatalist_init(myrdatalist);
+       myrdatalist->rdclass = dns_rdataclass_in;
+       myrdatalist->type = dns_rdatatype_aaaa;
+       myrdatalist->ttl = qctx->rdataset->ttl;
+
+       i = 0;
+       for (result = dns_rdataset_first(qctx->rdataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(qctx->rdataset))
+       {
+               if (!client->dns64_aaaaok[i++])
+                       continue;
+               dns_rdataset_current(qctx->rdataset, &rdata);
+               INSIST(rdata.length == 16);
+               isc_buffer_putmem(buffer, rdata.data, rdata.length);
+               isc_buffer_remainingregion(buffer, &r);
+               isc_buffer_forward(buffer, rdata.length);
+               result = dns_message_gettemprdata(client->message, &myrdata);
+               if (result != ISC_R_SUCCESS)
+                       goto cleanup;
+               dns_rdata_init(myrdata);
+               dns_rdata_fromregion(myrdata, dns_rdataclass_in,
+                                    dns_rdatatype_aaaa, &r);
+               ISC_LIST_APPEND(myrdatalist->rdata, myrdata, link);
+               myrdata = NULL;
+               dns_rdata_reset(&rdata);
+       }
+       if (result != ISC_R_NOMORE)
+               goto cleanup;
+
+       result = dns_rdatalist_tordataset(myrdatalist, myrdataset);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+       dns_rdataset_setownercase(myrdataset, name);
+       client->query.attributes |= NS_QUERYATTR_NOADDITIONAL;
+       if (mname == name) {
+               if (qctx->dbuf != NULL) {
+                       ns_client_keepname(client, name, qctx->dbuf);
+               }
+               dns_message_addname(client->message, name,
+                                   section);
+               qctx->dbuf = NULL;
+       }
+       myrdataset->trust = qctx->rdataset->trust;
+
+       /* Add rdataset to mname */
+       ISC_LIST_APPEND(mname->list, myrdataset, link);
+
+       ns_query_setorder(client, mname, myrdataset);
+
+       myrdataset = NULL;
+       myrdatalist = NULL;
+       dns_message_takebuffer(client->message, &buffer);
+
+ cleanup:
+       if (buffer != NULL)
+               isc_buffer_free(&buffer);
+
+       if (myrdata != NULL)
+               dns_message_puttemprdata(client->message, &myrdata);
+
+       if (myrdataset != NULL)
+               dns_message_puttemprdataset(client->message, &myrdataset);
+
+       if (myrdatalist != NULL) {
+               for (myrdata = ISC_LIST_HEAD(myrdatalist->rdata);
+                    myrdata != NULL;
+                    myrdata = ISC_LIST_HEAD(myrdatalist->rdata))
+               {
+                       ISC_LIST_UNLINK(myrdatalist->rdata, myrdata, link);
+                       dns_message_puttemprdata(client->message, &myrdata);
+               }
+               dns_message_puttemprdatalist(client->message, &myrdatalist);
+       }
+       if (qctx->dbuf != NULL) {
+               ns_client_releasename(client, &name);
+       }
+}
+
+static ns_hookresult_t
+dns64_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) {
+       UNUSED(arg);
+       UNUSED(cbdata);
+
+       *resp = ISC_R_UNSET;
+       return (NS_HOOK_CONTINUE);
+}
+
+static ns_hookresult_t
+dns64_respond_begin(void *arg, void *cbdata, isc_result_t *resp) {
+       query_ctx_t *qctx = (query_ctx_t *) arg;
+
+       UNUSED(cbdata);
+
+       /*
+        * Check to see if the AAAA RRset has non-excluded addresses
+        * in it.  If not look for a A RRset.
+        */
+       INSIST(qctx->client->dns64_aaaaok == NULL);
+
+       if (qctx->qtype == dns_rdatatype_aaaa && !qctx->dns64_exclude &&
+           !ISC_LIST_EMPTY(qctx->view->dns64) &&
+           qctx->client->message->rdclass == dns_rdataclass_in &&
+           !dns64_aaaaok(qctx->client, qctx->rdataset, qctx->sigrdataset))
+       {
+               /*
+                * Look to see if there are A records for this name.
+                */
+               qctx->client->dns64_ttl = qctx->rdataset->ttl;
+               SAVE(qctx->client->dns64_aaaa, qctx->rdataset);
+               SAVE(qctx->client->dns64_sigaaaa, qctx->sigrdataset);
+               ns_client_releasename(qctx->client, &qctx->fname);
+               dns_db_detachnode(qctx->db, &qctx->node);
+               qctx->type = qctx->qtype = dns_rdatatype_a;
+               qctx->dns64_exclude = qctx->dns64 = true;
+
+               /*
+                * XXX: we are depending here on DNS64
+                * being reached before any other modules that
+                * might set up recursion. In particular if
+                * the filter-aaaa module runs first, there'll
+                * be an assertion failure. We need to make this
+                * order-indeendent.
+                */
+               *resp = ns_query_lookup(qctx);
+               return (NS_HOOK_RETURN);
+       }
+
+       *resp = ISC_R_UNSET;
+       return (NS_HOOK_CONTINUE);
+}
+
+static ns_hookresult_t
+dns64_addanswer(void *arg, void *cbdata, isc_result_t *resp) {
+       query_ctx_t *qctx = (query_ctx_t *) arg;
+
+       UNUSED(cbdata);
+
+       if (qctx->dns64) {
+               isc_result_t result = dns64_synth(qctx);
+               qctx->noqname = NULL;
+               dns_rdataset_disassociate(qctx->rdataset);
+               dns_message_puttemprdataset(qctx->client->message,
+                                           &qctx->rdataset);
+               if (result == ISC_R_NOMORE) {
+                       if (qctx->dns64_exclude) {
+                               if (!qctx->is_zone) {
+                                       *resp = ns_query_done(qctx);
+                                       return (NS_HOOK_RETURN);
+                               }
+                               /*
+                                * Add a fake SOA record.
+                                */
+                               (void) ns_query_addsoa(qctx, 600,
+                                                      DNS_SECTION_AUTHORITY);
+                               *resp = ns_query_done(qctx);
+                               return (NS_HOOK_RETURN);
+                       }
+                       if (qctx->is_zone) {
+                               qctx->nxresult = DNS_R_NXDOMAIN;
+                               *resp = ns_query_nodata(qctx);
+                       } else {
+                               qctx->nxresult = DNS_R_NXDOMAIN;
+                               *resp = ns_query_ncache(qctx);
+                       }
+               } else if (result != ISC_R_SUCCESS) {
+                       qctx->result = result;
+                       *resp = ns_query_done(qctx);
+               } else {
+                       *resp = ISC_R_COMPLETE;
+               }
+               return (NS_HOOK_RETURN);
+       } else if (qctx->client->dns64_aaaaok != NULL) {
+               dns64_filter(qctx);
+               ns_client_putrdataset(qctx->client, &qctx->rdataset);
+               *resp = ISC_R_COMPLETE;
+               return (NS_HOOK_RETURN);
+       }
+
+       *resp = ISC_R_UNSET;
+       return (NS_HOOK_CONTINUE);
+}
+
+static ns_hookresult_t
+dns64_resume_restored(void *arg, void *cbdata, isc_result_t *resp) {
+       query_ctx_t *qctx = (query_ctx_t *) arg;
+
+       UNUSED(cbdata);
+
+       if (DNS64(qctx->client)) {
+               qctx->client->query.attributes &= ~NS_QUERYATTR_DNS64;
+               qctx->dns64 = true;
+       }
+
+       if (DNS64EXCLUDE(qctx->client)) {
+               qctx->client->query.attributes &= ~NS_QUERYATTR_DNS64EXCLUDE;
+               qctx->dns64_exclude = true;
+       }
+
+       *resp = ISC_R_UNSET;
+       return (NS_HOOK_CONTINUE);
+}
+
+static ns_hookresult_t
+dns64_notfound_recurse(void *arg, void *cbdata, isc_result_t *resp) {
+       query_ctx_t *qctx = (query_ctx_t *) arg;
+
+       UNUSED(cbdata);
+
+       if (qctx->dns64) {
+               qctx->client->query.attributes |= NS_QUERYATTR_DNS64;
+       }
+       if (qctx->dns64_exclude) {
+               qctx->client->query.attributes |= NS_QUERYATTR_DNS64EXCLUDE;
+       }
+
+       *resp = ISC_R_UNSET;
+       return (NS_HOOK_CONTINUE);
+}
+
+static ns_hookresult_t
+dns64_delegation_recurse(void *arg, void *cbdata, isc_result_t *resp) {
+       query_ctx_t *qctx = (query_ctx_t *) arg;
+
+       UNUSED(cbdata);
+
+       /*
+        * Look up an A record so we can synthesize DNS64.
+        */
+       if (qctx->dns64) {
+               qctx->result = ns_query_recurse(qctx->client,
+                                               dns_rdatatype_a,
+                                               qctx->client->query.qname,
+                                               NULL, NULL,
+                                               qctx->resuming);
+               qctx->client->query.attributes |= NS_QUERYATTR_RECURSING;
+               if (qctx->result == ISC_R_SUCCESS) {
+                       qctx->client->query.attributes |= NS_QUERYATTR_DNS64;
+                       if (qctx->dns64_exclude) {
+                               qctx->client->query.attributes |=
+                                     NS_QUERYATTR_DNS64EXCLUDE;
+                       }
+               }
+               *resp = ISC_R_COMPLETE;
+               return (NS_HOOK_RETURN);
+       }
+
+       *resp = ISC_R_UNSET;
+       return (NS_HOOK_CONTINUE);
+}
+
+static ns_hookresult_t
+dns64_nodata_begin(void *arg, void *cbdata, isc_result_t *resp) {
+       query_ctx_t *qctx = (query_ctx_t *) arg;
+
+       UNUSED(cbdata);
+
+       if (qctx->dns64 && !qctx->dns64_exclude) {
+               isc_buffer_t b;
+
+               /*
+                * Restore the answers from the previous AAAA lookup.
+                */
+               if (qctx->rdataset != NULL) {
+                       ns_client_putrdataset(qctx->client, &qctx->rdataset);
+               }
+               if (qctx->sigrdataset != NULL) {
+                       ns_client_putrdataset(qctx->client, &qctx->sigrdataset);
+               }
+               RESTORE(qctx->rdataset, qctx->client->dns64_aaaa);
+               RESTORE(qctx->sigrdataset, qctx->client->dns64_sigaaaa);
+               if (qctx->fname == NULL) {
+                       qctx->dbuf = ns_client_getnamebuf(qctx->client);
+                       if (qctx->dbuf == NULL) {
+                               QUERY_ERROR(qctx, DNS_R_SERVFAIL);
+                               *resp = ns_query_done(qctx);
+                               return (NS_HOOK_RETURN);
+                       }
+                       qctx->fname = ns_client_newname(qctx->client,
+                                                   qctx->dbuf, &b);
+                       if (qctx->fname == NULL) {
+                               QUERY_ERROR(qctx, DNS_R_SERVFAIL);
+                               *resp = ns_query_done(qctx);
+                               return (NS_HOOK_RETURN);
+                       }
+               }
+               dns_name_copy(qctx->client->query.qname, qctx->fname, NULL);
+               qctx->dns64 = false;
+       } else if ((qctx->nxresult == DNS_R_NXRRSET ||
+                   qctx->nxresult == DNS_R_NCACHENXRRSET) &&
+                  !ISC_LIST_EMPTY(qctx->view->dns64) &&
+                  !qctx->nxrewrite &&
+                  qctx->client->message->rdclass == dns_rdataclass_in &&
+                  qctx->qtype == dns_rdatatype_aaaa)
+       {
+               /*
+                * Look to see if there are A records for this name.
+                */
+               switch (qctx->nxresult) {
+               case DNS_R_NCACHENXRRSET:
+                       /*
+                        * This is from the negative cache; if the ttl is
+                        * zero, we need to work out whether we have just
+                        * decremented to zero or there was no negative
+                        * cache ttl in the answer.
+                        */
+                       if (qctx->rdataset->ttl != 0) {
+                               qctx->client->dns64_ttl = qctx->rdataset->ttl;
+                               break;
+                       }
+                       if (dns_rdataset_first(qctx->rdataset) == ISC_R_SUCCESS)
+                               qctx->client->dns64_ttl = 0;
+                       break;
+               case DNS_R_NXRRSET:
+                       qctx->client->dns64_ttl =
+                               dns64_ttl(qctx->db, qctx->version);
+                       break;
+               default:
+                       INSIST(0);
+                       ISC_UNREACHABLE();
+               }
+
+               SAVE(qctx->client->dns64_aaaa, qctx->rdataset);
+               SAVE(qctx->client->dns64_sigaaaa, qctx->sigrdataset);
+               ns_client_releasename(qctx->client, &qctx->fname);
+               dns_db_detachnode(qctx->db, &qctx->node);
+               qctx->type = qctx->qtype = dns_rdatatype_a;
+               qctx->dns64 = true;
+               *resp = ns_query_lookup(qctx);
+               return (NS_HOOK_RETURN);
+       }
+
+       *resp = ISC_R_UNSET;
+       return (NS_HOOK_CONTINUE);
+}
+
+static ns_hookresult_t
+dns64_zerottl_recurse(void *arg, void *cbdata, isc_result_t *resp) {
+       query_ctx_t *qctx = (query_ctx_t *) arg;
+
+       UNUSED(cbdata);
+
+       if (qctx->dns64) {
+               qctx->client->query.attributes |= NS_QUERYATTR_DNS64;
+       }
+       if (qctx->dns64_exclude) {
+               qctx->client->query.attributes |= NS_QUERYATTR_DNS64EXCLUDE;
+       }
+
+       *resp = ISC_R_UNSET;
+       return (NS_HOOK_CONTINUE);
+}
+
+static ns_hookresult_t
+dns64_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) {
+       UNUSED(arg);
+       UNUSED(cbdata);
+
+       *resp = ISC_R_UNSET;
+       return (NS_HOOK_CONTINUE);
+}
diff --git a/bin/plugins/dns64.docbook b/bin/plugins/dns64.docbook
new file mode 100644 (file)
index 0000000..af27423
--- /dev/null
@@ -0,0 +1,154 @@
+<!--
+ - Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ -
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ -
+ - See the COPYRIGHT file distributed with this work for additional
+ - information regarding copyright ownership.
+-->
+
+<!-- Converted by db4-upgrade version 1.0 -->
+<refentry xmlns:db="http://docbook.org/ns/docbook" version="5.0" xml:id="man.dns64">
+  <info>
+    <date>2018-11-30</date>
+  </info>
+  <refentryinfo>
+    <corpname>ISC</corpname>
+    <corpauthor>Internet Systems Consortium, Inc.</corpauthor>
+  </refentryinfo>
+  <refmeta>
+    <refentrytitle><application>dns64.so</application></refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND9</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname><application>dns64.so</application></refname>
+    <refpurpose>perform DNS64 synthesis</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2018</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis sepchar=" ">
+      <command>plugin query "dns64.so"</command>
+      <arg choice="opt" rep="norepeat"><replaceable class="parameter">{ parameters }</replaceable></arg>;
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsection><info><title>DESCRIPTION</title></info>
+    <para>
+      <command>dns64.so</command> is a query plugin module for
+      <command>named</command>, enabling <command>named</command>
+      to perform DNS64 address synthesis.
+    </para>
+    <para>
+      Until BIND 9.12, this feature was implemented natively in
+      <command>named</command> and enabled with the
+      <command>dns64</command> option.
+      This option is now deprecated in <filename>named.conf</filename>,
+      but can be passed as parameters to the
+      <command>dns64.so</command> plugin, for example:
+    </para>
+    <programlisting>
+       acl rfc1918 { 10/8; 192.168/16; 172.16/12; };
+
+       plugin query "/usr/local/lib/dns64.so" {
+               dns64 64:FF9B::/96 {
+                       clients { any; };
+                       mapped { !rfc1918; any; };
+                       exclude { 64:FF9B::/96; ::ffff:0000:0000/96; };
+                       suffix ::;
+               };
+
+               dns64-server "dns64.example.net.";
+               dns64-contact "hostmaster.example.net.";
+       };
+</programlisting>
+    <para>
+      This plugin enables <command>named</command> to
+      return mapped IPv4 addresses to AAAA queries when
+      there are no AAAA records.  It is intended to be
+      used in conjunction with NAT64.
+    </para>
+    <para>
+      Each <command>dns64</command> option defined in the plugin
+      parameters defines one DNS64 prefix. Multiple DNS64 prefixes
+      can be defined.
+    </para>
+    <para>
+      Compatible IPv6 prefixes have lengths of 32, 40, 48, 56,
+      64 and 96 as per RFC 6052.
+    </para>
+    <para>
+      Additionally a reverse IP6.ARPA zone will be created for
+      the prefix to provide a mapping from the IP6.ARPA names
+      to the corresponding IN-ADDR.ARPA names using synthesized
+      CNAMEs.  <command>dns64-server</command> and
+      <command>dns64-contact</command> can be used to specify
+      the name of the server and contact for the zones.
+      These are not settable on a per-prefix basis.
+    </para>
+    <para>
+      Each <command>dns64</command> supports an optional
+      <command>clients</command> ACL that determines which
+      clients are affected by this directive.  If not defined,
+      it defaults to <userinput>any;</userinput>.
+    </para>
+    <para>
+      Each <command>dns64</command> supports an optional
+      <command>mapped</command> ACL that selects which
+      IPv4 addresses are to be mapped in the corresponding
+      A RRset.  If not defined it defaults to
+      <userinput>any;</userinput>.
+    </para>
+    <para>
+      Normally, DNS64 won't apply to a domain name that
+      owns one or more AAAA records; these records will
+      simply be returned.  The optional
+      <command>exclude</command> ACL allows specification
+      of a list of IPv6 addresses that will be ignored
+      if they appear in a domain name's AAAA records, and
+      DNS64 will be applied to any A records the domain
+      name owns.  If not defined, <command>exclude</command>
+      defaults to ::ffff:0.0.0.0/96.
+    </para>
+    <para>
+      A optional <command>suffix</command> can also
+      be defined to set the bits trailing the mapped
+      IPv4 address bits.  By default these bits are
+      set to <userinput>::</userinput>.  The bits
+      matching the prefix and mapped IPv4 address
+      must be zero.
+    </para>
+    <para>
+      If <command>recursive-only</command> is set to
+      <command>yes</command> the DNS64 synthesis will
+      only happen for recursive queries.  The default
+      is <command>no</command>.
+    </para>
+    <para>
+      If <command>break-dnssec</command> is set to
+      <command>yes</command> the DNS64 synthesis will
+      happen even if the result, if validated, would
+      cause a DNSSEC validation failure.  If this option
+      is set to <command>no</command> (the default), the DO
+      is set on the incoming query, and there are RRSIGs on
+      the applicable records, then synthesis will not happen.
+    </para>
+  </refsection>
+
+  <refsection><info><title>SEE ALSO</title></info>
+    <para>
+      <citetitle>BIND 9 Administrator Reference Manual</citetitle>.
+    </para>
+  </refsection>
+
+</refentry>
diff --git a/bin/plugins/dns64.html b/bin/plugins/dns64.html
new file mode 100644 (file)
index 0000000..d2ceee6
--- /dev/null
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!--
+ - Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+ - 
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+<html lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>dns64.so</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry">
+<a name="man.dns64"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><span class="application">dns64.so</span> &#8212; perform DNS64 synthesis</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">plugin query "dns64.so"</code>  [<em class="replaceable"><code>{ parameters }</code></em>];
+    </p></div>
+</div>
+<div class="refsection">
+<a name="id-1.7"></a><h2>DESCRIPTION</h2>
+<p>
+      <span class="command"><strong>dns64.so</strong></span> is a query plugin module for
+      <span class="command"><strong>named</strong></span>, enabling <span class="command"><strong>named</strong></span>
+      to perform DNS64 address synthesis.
+    </p>
+<p>
+      Until BIND 9.12, this feature was implemented natively in
+      <span class="command"><strong>named</strong></span> and enabled with the
+      <span class="command"><strong>dns64</strong></span> option.
+      This option is now deprecated in <code class="filename">named.conf</code>,
+      but can be passed as parameters to the
+      <span class="command"><strong>dns64.so</strong></span> plugin, for example:
+    </p>
+<pre class="programlisting">
+       acl rfc1918 { 10/8; 192.168/16; 172.16/12; };
+
+       plugin query "/usr/local/lib/dns64.so" {
+               dns64 64:FF9B::/96 {
+                       clients { any; };
+                       mapped { !rfc1918; any; };
+                       exclude { 64:FF9B::/96; ::ffff:0000:0000/96; };
+                       suffix ::;
+               };
+
+               dns64-server "dns64.example.net.";
+               dns64-contact "hostmaster.example.net.";
+       };
+</pre>
+<p>
+      This plugin enables <span class="command"><strong>named</strong></span> to
+      return mapped IPv4 addresses to AAAA queries when
+      there are no AAAA records.  It is intended to be
+      used in conjunction with NAT64.
+    </p>
+<p>
+      Each <span class="command"><strong>dns64</strong></span> option defined in the plugin
+      parameters defines one DNS64 prefix. Multiple DNS64 prefixes
+      can be defined.
+    </p>
+<p>
+      Compatible IPv6 prefixes have lengths of 32, 40, 48, 56,
+      64 and 96 as per RFC 6052.
+    </p>
+<p>
+      Additionally a reverse IP6.ARPA zone will be created for
+      the prefix to provide a mapping from the IP6.ARPA names
+      to the corresponding IN-ADDR.ARPA names using synthesized
+      CNAMEs.  <span class="command"><strong>dns64-server</strong></span> and
+      <span class="command"><strong>dns64-contact</strong></span> can be used to specify
+      the name of the server and contact for the zones.
+      These are not settable on a per-prefix basis.
+    </p>
+<p>
+      Each <span class="command"><strong>dns64</strong></span> supports an optional
+      <span class="command"><strong>clients</strong></span> ACL that determines which
+      clients are affected by this directive.  If not defined,
+      it defaults to <strong class="userinput"><code>any;</code></strong>.
+    </p>
+<p>
+      Each <span class="command"><strong>dns64</strong></span> supports an optional
+      <span class="command"><strong>mapped</strong></span> ACL that selects which
+      IPv4 addresses are to be mapped in the corresponding
+      A RRset.  If not defined it defaults to
+      <strong class="userinput"><code>any;</code></strong>.
+    </p>
+<p>
+      Normally, DNS64 won't apply to a domain name that
+      owns one or more AAAA records; these records will
+      simply be returned.  The optional
+      <span class="command"><strong>exclude</strong></span> ACL allows specification
+      of a list of IPv6 addresses that will be ignored
+      if they appear in a domain name's AAAA records, and
+      DNS64 will be applied to any A records the domain
+      name owns.  If not defined, <span class="command"><strong>exclude</strong></span>
+      defaults to ::ffff:0.0.0.0/96.
+    </p>
+<p>
+      A optional <span class="command"><strong>suffix</strong></span> can also
+      be defined to set the bits trailing the mapped
+      IPv4 address bits.  By default these bits are
+      set to <strong class="userinput"><code>::</code></strong>.  The bits
+      matching the prefix and mapped IPv4 address
+      must be zero.
+    </p>
+<p>
+      If <span class="command"><strong>recursive-only</strong></span> is set to
+      <span class="command"><strong>yes</strong></span> the DNS64 synthesis will
+      only happen for recursive queries.  The default
+      is <span class="command"><strong>no</strong></span>.
+    </p>
+<p>
+      If <span class="command"><strong>break-dnssec</strong></span> is set to
+      <span class="command"><strong>yes</strong></span> the DNS64 synthesis will
+      happen even if the result, if validated, would
+      cause a DNSSEC validation failure.  If this option
+      is set to <span class="command"><strong>no</strong></span> (the default), the DO
+      is set on the incoming query, and there are RRSIGs on
+      the applicable records, then synthesis will not happen.
+    </p>
+</div>
+<div class="refsection">
+<a name="id-1.8"></a><h2>SEE ALSO</h2>
+<p>
+      <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+    </p>
+</div>
+</div></body>
+</html>
index 778c0bc9a53382325620dc84fba03ddfdd53052d..664b50cf09766a132f0c5cd4e4313a4eeb736842 100644 (file)
@@ -34,6 +34,8 @@ options {
        };
 };
 
+plugin query "../../../../plugins/lib/dns64.so";
+
 zone "." {
        type master;
        file "root.db";
index 386a9d740b939d16e1206651d9af5d44aff9c44a..4c45890106403d60b93988cf60ec51f9b63d32fc 100644 (file)
@@ -58,6 +58,8 @@ options {
        response-policy { zone "rpz"; };
 };
 
+plugin query "../../../../plugins/lib/dns64.so";
+
 zone "." {
        type hint;
        file "../../common/root.hint";
index 01abb12a841688e1aa8816e8a9da31d1f0fed7c7..6d29fc6b418310a5af592d4a651914e78dd28664 100644 (file)
@@ -41,6 +41,8 @@ options {
        response-policy { zone "rpz"; };
 };
 
+plugin query "../../../../plugins/lib/dns64.so";
+
 zone "." {
        type hint;
        file "../../common/root.hint";
index 86d4ccd187676d86f40b624930cdfdad8f5270c7..ceb84c0498384151c9e393f5a8d3f4b1900d5ff7 100644 (file)
@@ -18106,6 +18106,7 @@ allow-query { !{ !10/8; any; }; key example; };
       <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="../../bin/dnssec/dnssec-verify.docbook"/>
       <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="../../bin/tools/dnstap-read.docbook"/>
       <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="../../bin/plugins/filter-aaaa.docbook"/>
+      <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="../../bin/plugins/dns64.docbook"/>
       <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="../../bin/dig/host.docbook"/>
       <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="../../bin/tools/mdig.docbook"/>
       <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="../../bin/check/named-checkconf.docbook"/>
diff --git a/doc/arm/man.dns64.html b/doc/arm/man.dns64.html
new file mode 100644 (file)
index 0000000..0ca519e
--- /dev/null
@@ -0,0 +1,173 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!--
+ - Copyright (C) 2000-2018 Internet Systems Consortium, Inc. ("ISC")
+ - 
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+<html lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>dns64.so</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
+<link rel="home" href="Bv9ARM.html" title="BIND 9 Administrator Reference Manual">
+<link rel="up" href="Bv9ARM.ch12.html" title="Manual pages">
+<link rel="prev" href="man.filter-aaaa.html" title="filter-aaaa.so">
+<link rel="next" href="man.host.html" title="host">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
+<div class="navheader">
+<table width="100%" summary="Navigation header">
+<tr><th colspan="3" align="center"><span class="application">dns64.so</span></th></tr>
+<tr>
+<td width="20%" align="left">
+<a accesskey="p" href="man.filter-aaaa.html">Prev</a> </td>
+<th width="60%" align="center">Manual pages</th>
+<td width="20%" align="right"> <a accesskey="n" href="man.host.html">Next</a>
+</td>
+</tr>
+</table>
+<hr>
+</div>
+<div class="refentry">
+<a name="man.dns64"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><span class="application">dns64.so</span> &#8212; perform DNS64 synthesis</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">hook query "dns64.so"</code>  [<em class="replaceable"><code>{ parameters }</code></em>];
+    </p></div>
+</div>
+<div class="refsection">
+<a name="id-1.13.20.7"></a><h2>DESCRIPTION</h2>
+<p>
+      <span class="command"><strong>dns64.so</strong></span> is a query hook module for
+      <span class="command"><strong>named</strong></span>, enabling <span class="command"><strong>named</strong></span>
+      to perform DNS64 address synthesis.
+    </p>
+<p>
+      Until BIND 9.12, this feature was implemented natively in
+      <span class="command"><strong>named</strong></span> and enabled with the
+      <span class="command"><strong>dns64</strong></span> option.
+      This option is now deprecated in <code class="filename">named.conf</code>,
+      but can be passed as parameters to the
+      <span class="command"><strong>dns64.so</strong></span> module, for example:
+    </p>
+<pre class="programlisting">
+       acl rfc1918 { 10/8; 192.168/16; 172.16/12; };
+
+       plugin query "/usr/local/lib/dns64.so" {
+               dns64 64:FF9B::/96 {
+                       clients { any; };
+                       mapped { !rfc1918; any; };
+                       exclude { 64:FF9B::/96; ::ffff:0000:0000/96; };
+                       suffix ::;
+               };
+
+               dns64-server "dns64.example.net.";
+               dns64-contact "hostmaster.example.net.";
+       };
+</pre>
+<p>
+      This plugin enables <span class="command"><strong>named</strong></span> to
+      return mapped IPv4 addresses to AAAA queries when
+      there are no AAAA records.  It is intended to be
+      used in conjunction with NAT64.
+    </p>
+<p>
+      Each <span class="command"><strong>dns64</strong></span> option defined in the plugin
+      parameters defines one DNS64 prefix. Multiple DNS64 prefixes
+      can be defined.
+    </p>
+<p>
+      Compatible IPv6 prefixes have lengths of 32, 40, 48, 56,
+      64 and 96 as per RFC 6052.
+    </p>
+<p>
+      Additionally a reverse IP6.ARPA zone will be created for
+      the prefix to provide a mapping from the IP6.ARPA names
+      to the corresponding IN-ADDR.ARPA names using synthesized
+      CNAMEs.  <span class="command"><strong>dns64-server</strong></span> and
+      <span class="command"><strong>dns64-contact</strong></span> can be used to specify
+      the name of the server and contact for the zones.
+      These are not settable on a per-prefix basis.
+    </p>
+<p>
+      Each <span class="command"><strong>dns64</strong></span> supports an optional
+      <span class="command"><strong>clients</strong></span> ACL that determines which
+      clients are affected by this directive.  If not defined,
+      it defaults to <strong class="userinput"><code>any;</code></strong>.
+    </p>
+<p>
+      Each <span class="command"><strong>dns64</strong></span> supports an optional
+      <span class="command"><strong>mapped</strong></span> ACL that selects which
+      IPv4 addresses are to be mapped in the corresponding
+      A RRset.  If not defined it defaults to
+      <strong class="userinput"><code>any;</code></strong>.
+    </p>
+<p>
+      Normally, DNS64 won't apply to a domain name that
+      owns one or more AAAA records; these records will
+      simply be returned.  The optional
+      <span class="command"><strong>exclude</strong></span> ACL allows specification
+      of a list of IPv6 addresses that will be ignored
+      if they appear in a domain name's AAAA records, and
+      DNS64 will be applied to any A records the domain
+      name owns.  If not defined, <span class="command"><strong>exclude</strong></span>
+      defaults to ::ffff:0.0.0.0/96.
+    </p>
+<p>
+      A optional <span class="command"><strong>suffix</strong></span> can also
+      be defined to set the bits trailing the mapped
+      IPv4 address bits.  By default these bits are
+      set to <strong class="userinput"><code>::</code></strong>.  The bits
+      matching the prefix and mapped IPv4 address
+      must be zero.
+    </p>
+<p>
+      If <span class="command"><strong>recursive-only</strong></span> is set to
+      <span class="command"><strong>yes</strong></span> the DNS64 synthesis will
+      only happen for recursive queries.  The default
+      is <span class="command"><strong>no</strong></span>.
+    </p>
+<p>
+      If <span class="command"><strong>break-dnssec</strong></span> is set to
+      <span class="command"><strong>yes</strong></span> the DNS64 synthesis will
+      happen even if the result, if validated, would
+      cause a DNSSEC validation failure.  If this option
+      is set to <span class="command"><strong>no</strong></span> (the default), the DO
+      is set on the incoming query, and there are RRSIGs on
+      the applicable records, then synthesis will not happen.
+    </p>
+</div>
+<div class="refsection">
+<a name="id-1.13.20.8"></a><h2>SEE ALSO</h2>
+<p>
+      <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+    </p>
+</div>
+</div>
+<div class="navfooter">
+<hr>
+<table width="100%" summary="Navigation footer">
+<tr>
+<td width="40%" align="left">
+<a accesskey="p" href="man.filter-aaaa.html">Prev</a> </td>
+<td width="20%" align="center"><a accesskey="u" href="Bv9ARM.ch12.html">Up</a></td>
+<td width="40%" align="right"> <a accesskey="n" href="man.host.html">Next</a>
+</td>
+</tr>
+<tr>
+<td width="40%" align="left" valign="top">
+<span class="application">filter-aaaa.so</span> </td>
+<td width="20%" align="center"><a accesskey="h" href="Bv9ARM.html">Home</a></td>
+<td width="40%" align="right" valign="top"> host</td>
+</tr>
+</table>
+</div>
+<p xmlns:db="http://docbook.org/ns/docbook" style="text-align: center;">BIND 9.13.4 (Development Release)</p>
+</body>
+</html>
index 507970b85d84167ba3e528cc64070fe2afc5bd84..2f840edf3b136819b80480cf155a2fca7b409f82 100644 (file)
     in the future.
   </para>
   <para>
-    The only plugin currently included in BIND is
-    <filename>filter-aaaa.so</filename>, which replaces the
-    <command>filter-aaaa</command> feature that previously existed natively
+    The only plugins currently included in BIND are
+    <filename>filter-aaaa.so</filename> and <filename>dns64.so</filename>,
+    which replace the <command>filter-aaaa</command> and
+    <command>dns64</command> features that previously existed natively
     as part of <command>named</command>.
-    The code for this feature has been removed from <command>named</command>,
-    and can no longer be configured using standard
+    The code for these features has been removed from <command>named</command>,
+    and they can no longer be configured using standard
     <filename>named.conf</filename> syntax, but linking in the
-    <filename>filter-aaaa.so</filename> plugin provides identical
-    functionality.
+    <filename>filter-aaaa.so</filename> or <filename>dns64.so</filename>
+    plugins provides identical functionality.
   </para>
 
   <section><info><title>Configuring Plugins</title></info>
index 8a946f0fb3d6256610cd6669d1b60571a28d4337..6a5cd20d0a39595b04c53dbcc878519371dfdb3e 100644 (file)
@@ -239,6 +239,43 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
  * recursion completes.
  */
 
+isc_result_t
+ns_query_lookup(query_ctx_t *qctx);
+/*%<
+ * Perform a local database lookup, in either an authoritative or
+ * cache database. If unable to answer, call ns_query_done(); otherwise
+ * hand off processing to query_gotanswer().
+ */
+
+isc_result_t
+ns_query_addsoa(query_ctx_t *qctx, dns_ttl_t ttl, dns_section_t section);
+/*%<
+ * Add SOA to the authority section when sending negative responses
+ * (or to the additional section if sending negative responses triggered
+ * by RPZ rewriting.)
+ */
+
+isc_result_t
+ns_query_nodata(query_ctx_t *qctx);
+/*%<
+ * Handle authoritative NOERROR/NODATA responses.
+ */
+
+isc_result_t
+ns_query_ncache(query_ctx_t *qctx);
+/*%<
+ * Handle negative cache responses, DNS_R_NCACHENXRRSET or
+ * DNS_R_NCACHENXDOMAIN. (Note: may be called with other
+ * result codes as a result of hook actions; for example,
+ * DNS64 may call with DNS_R_NXOMAIN.)
+ */
+
+void
+ns_query_setorder(ns_client_t *client, dns_name_t *name,
+                 dns_rdataset_t *rdataset);
+/*%<
+ * Set the ordering for 'rdataset'.
+ */
 
 isc_result_t
 ns__query_sfcache(query_ctx_t *qctx);
@@ -252,12 +289,4 @@ ns__query_start(query_ctx_t *qctx);
  * (Must not be used outside this module and its associated unit tests.)
  */
 
-/*
- * XXX:
- * Temporary function used to initialize the dns64 hooks,
- * which are currently hard-coded rather than loaded as a module.
- */
-void
-ns__query_inithooks(dns_view_t *view);
-
 #endif /* NS_QUERY_H */
index 0e9a3f54597d71ff9bcb883d444fbd4867ac5476..aef079735e24fcf7cee67afc3199a40a25afa82e 100644 (file)
@@ -123,13 +123,6 @@ do { \
 /*% Secure? */
 #define SECURE(c)              (((c)->query.attributes & \
                                  NS_QUERYATTR_SECURE) != 0)
-/*% DNS64 A lookup? */
-#define DNS64(c)               (((c)->query.attributes & \
-                                 NS_QUERYATTR_DNS64) != 0)
-
-#define DNS64EXCLUDE(c)                (((c)->query.attributes & \
-                                 NS_QUERYATTR_DNS64EXCLUDE) != 0)
-
 #define REDIRECT(c)            (((c)->query.attributes & \
                                  NS_QUERYATTR_REDIRECT) != 0)
 
@@ -311,10 +304,10 @@ get_hooktab(query_ctx_t *qctx) {
  * 2. Start the search (ns__query_start())
  *
  * 3. Identify authoritative data sources which may have an answer;
- *    search them (query_lookup()). If an answer is found, go to 7.
+ *    search them (ns_query_lookup()). If an answer is found, go to 7.
  *
  * 4. If recursion or cache access are allowed, search the cache
- *    (query_lookup() again, using the cache database) to find a better
+ *    (ns_query_lookup() again, using the cache database) to find a better
  *    answer. If an answer is found, go to 7.
  *
  * 5. If recursion is allowed, begin recursion (ns_query_recurse()).
@@ -340,15 +333,15 @@ get_hooktab(query_ctx_t *qctx) {
  *    otherwise go to 15 to clean up and return the delegation to the client.
  *
  * 10. No such domain (query_nxdomain()). Attempt redirection; if
- *     unsuccessful, add authority section records (query_addsoa(),
+ *     unsuccessful, add authority section records (ns_query_addsoa(),
  *     query_addauth()), then go to 15 to return NXDOMAIN to client.
  *
- * 11. Empty answer (query_nodata()). Add authority section records
- *     (query_addsoa(), query_addauth()) and signatures if authoritative
+ * 11. Empty answer (ns_query_nodata()). Add authority section records
+ *     (ns_query_addsoa(), query_addauth()) and signatures if authoritative
  *     (query_sign_nodata()) then go to 15 and return
  *     NOERROR/ANCOUNT=0 to client.
  *
- * 12. No such domain or empty answer returned from cache (query_ncache()).
+ * 12. No such domain or empty answer returned from cache (ns_query_ncache()).
  *     Set response code appropriately, go to 11.
  *
  * 13. Prepare a response (query_prepresponse()) and then fill it
@@ -376,9 +369,6 @@ qctx_init(ns_client_t *client, dns_fetchevent_t *event,
 static isc_result_t
 query_setup(ns_client_t *client, dns_rdatatype_t qtype);
 
-static isc_result_t
-query_lookup(query_ctx_t *qctx);
-
 static void
 fetch_callback(isc_task_t *task, isc_event_t *event);
 
@@ -410,12 +400,6 @@ query_respond_any(query_ctx_t *qctx);
 static isc_result_t
 query_respond(query_ctx_t *qctx);
 
-static isc_result_t
-query_dns64(query_ctx_t *qctx);
-
-static void
-query_filter64(query_ctx_t *qctx);
-
 static isc_result_t
 query_notfound(query_ctx_t *qctx);
 
@@ -431,9 +415,6 @@ query_delegation_recurse(query_ctx_t *qctx);
 static void
 query_addds(query_ctx_t *qctx);
 
-static isc_result_t
-query_nodata(query_ctx_t *qctx);
-
 static isc_result_t
 query_sign_nodata(query_ctx_t *qctx);
 
@@ -446,9 +427,6 @@ query_nxdomain(query_ctx_t *qctx, bool empty_wild);
 static isc_result_t
 query_redirect(query_ctx_t *qctx);
 
-static isc_result_t
-query_ncache(query_ctx_t *qctx);
-
 static isc_result_t
 query_coveringnsec(query_ctx_t *qctx);
 
@@ -467,10 +445,6 @@ query_addcname(query_ctx_t *qctx, dns_trust_t trust, dns_ttl_t ttl);
 static isc_result_t
 query_prepresponse(query_ctx_t *qctx);
 
-static isc_result_t
-query_addsoa(query_ctx_t *qctx, unsigned int override_ttl,
-            dns_section_t section);
-
 static isc_result_t
 query_addns(query_ctx_t *qctx);
 
@@ -483,113 +457,6 @@ query_addwildcardproof(query_ctx_t *qctx, bool ispositive, bool nodata);
 static void
 query_addauth(query_ctx_t *qctx);
 
-/*
- * XXX: will be moved to a module later.
- */
-static uint32_t
-dns64_ttl(dns_db_t *db, dns_dbversion_t *version);
-
-static bool
-dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset,
-            dns_rdataset_t *sigrdataset);
-
-static isc_result_t
-query_dns64(query_ctx_t *qctx);
-
-static void
-query_filter64(query_ctx_t *qctx);
-
-/*
- * XXX:
- * This is a temporary hooks table, pre-populated with pointers to
- * the functions implementing dns64. Later, this will be
- * set up at initialization time when the dns64 module is loaded.
- * To activate this hooks table at runtime, call ns__query_inithooks().
- */
-
-static bool
-dns64_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp);
-static ns_hook_t dns64_init = {
-       .action = dns64_qctx_initialize,
-};
-
-static bool
-dns64_respond_begin(void *arg, void *cbdata, isc_result_t *resp);
-static ns_hook_t dns64_respbegin = {
-       .action = dns64_respond_begin,
-};
-
-static bool
-dns64_respond_add_answer(void *arg, void *cbdata, isc_result_t *resp);
-static ns_hook_t dns64_respaddanswer = {
-       .action = dns64_respond_add_answer,
-};
-
-static bool
-dns64_resume_restored(void *arg, void *cbdata, isc_result_t *resp);
-static ns_hook_t dns64_resumerest = {
-       .action = dns64_resume_restored,
-};
-
-static bool
-dns64_notfound_recurse(void *arg, void *cbdata, isc_result_t *resp);
-static ns_hook_t dns64_nfrec = {
-       .action = dns64_notfound_recurse,
-};
-
-static bool
-dns64_delegation_recurse(void *arg, void *cbdata, isc_result_t *resp);
-static ns_hook_t dns64_delrec = {
-       .action = dns64_delegation_recurse,
-};
-
-static bool
-dns64_nodata_begin(void *arg, void *cbdata, isc_result_t *resp);
-static ns_hook_t dns64_nodata = {
-       .action = dns64_nodata_begin,
-};
-
-static bool
-dns64_zerottl_recurse(void *arg, void *cbdata, isc_result_t *resp);
-static ns_hook_t dns64_zerottl = {
-       .action = dns64_zerottl_recurse,
-};
-
-static bool
-dns64_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp);
-static ns_hook_t dns64_destroy = {
-       .action = dns64_qctx_destroy,
-};
-
-/*
- * XXX:
- * This function is temporary.  Later, this will be done from the
- * registration function in the dns64 module.
- */
-void
-ns__query_inithooks(dns_view_t *view) {
-       REQUIRE(view != NULL);
-
-       ns_hook_add(view->hooktable, view->mctx,
-                   NS_QUERY_QCTX_INITIALIZED, &dns64_init);
-       ns_hook_add(view->hooktable, view->mctx,
-                   NS_QUERY_RESPOND_BEGIN, &dns64_respbegin);
-       ns_hook_add(view->hooktable, view->mctx,
-                   NS_QUERY_ADDANSWER_BEGIN, &dns64_respaddanswer);
-       ns_hook_add(view->hooktable, view->mctx,
-                   NS_QUERY_RESUME_RESTORED, &dns64_resumerest);
-       ns_hook_add(view->hooktable, view->mctx,
-                   NS_QUERY_NOTFOUND_RECURSE, &dns64_nfrec);
-       ns_hook_add(view->hooktable, view->mctx,
-                   NS_QUERY_DELEGATION_RECURSE_BEGIN, &dns64_delrec);
-       ns_hook_add(view->hooktable, view->mctx,
-                   NS_QUERY_NODATA_BEGIN, &dns64_nodata);
-       ns_hook_add(view->hooktable, view->mctx,
-                   NS_QUERY_ZEROTTL_RECURSE, &dns64_zerottl);
-       ns_hook_add(view->hooktable, view->mctx,
-                   NS_QUERY_QCTX_DESTROYED, &dns64_destroy);
-}
-
 /*%
  * Increment query statistics counters.
  */
@@ -2167,16 +2034,12 @@ query_addtoname(dns_name_t *name, dns_rdataset_t *rdataset) {
        ISC_LIST_APPEND(name->list, rdataset, link);
 }
 
-/*
- * Set the ordering for 'rdataset'.
- */
-static void
-query_setorder(query_ctx_t *qctx, dns_name_t *name, dns_rdataset_t *rdataset) {
-       ns_client_t *client = qctx->client;
+void
+ns_query_setorder(ns_client_t *client, dns_name_t *name,
+                 dns_rdataset_t *rdataset)
+{
        dns_order_t *order = client->view->order;
 
-       CTRACE(ISC_LOG_DEBUG(3), "query_setorder");
-
        UNUSED(client);
 
        if (order != NULL) {
@@ -2184,6 +2047,7 @@ query_setorder(query_ctx_t *qctx, dns_name_t *name, dns_rdataset_t *rdataset) {
                                                       rdataset->type,
                                                       rdataset->rdclass);
        }
+
        rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
 };
 
@@ -2306,7 +2170,7 @@ query_addrrset(query_ctx_t *qctx, dns_name_t **namep,
         * section processing if needed.
         */
        query_addtoname(mname, rdataset);
-       query_setorder(qctx, mname, rdataset);
+       ns_query_setorder(client, mname, rdataset);
        query_additional(qctx, rdataset);
 
        /*
@@ -4705,93 +4569,6 @@ query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
        return;
 }
 
-static uint32_t
-dns64_ttl(dns_db_t *db, dns_dbversion_t *version) {
-       dns_dbnode_t *node = NULL;
-       dns_rdata_soa_t soa;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
-       dns_rdataset_t rdataset;
-       isc_result_t result;
-       uint32_t ttl = UINT32_MAX;
-
-       dns_rdataset_init(&rdataset);
-
-       result = dns_db_getoriginnode(db, &node);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-
-       result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa,
-                                    0, 0, &rdataset, NULL);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-       result = dns_rdataset_first(&rdataset);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-
-       dns_rdataset_current(&rdataset, &rdata);
-       result = dns_rdata_tostruct(&rdata, &soa, NULL);
-       RUNTIME_CHECK(result == ISC_R_SUCCESS);
-       ttl = ISC_MIN(rdataset.ttl, soa.minimum);
-
-cleanup:
-       if (dns_rdataset_isassociated(&rdataset))
-               dns_rdataset_disassociate(&rdataset);
-       if (node != NULL)
-               dns_db_detachnode(db, &node);
-       return (ttl);
-}
-
-static bool
-dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset,
-            dns_rdataset_t *sigrdataset)
-{
-       isc_netaddr_t netaddr;
-       dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
-       dns_dns64_t *dns64 = ISC_LIST_HEAD(client->view->dns64);
-       unsigned int flags = 0;
-       unsigned int i, count;
-       bool *aaaaok;
-
-       INSIST(client->dns64_aaaaok == NULL);
-       INSIST(client->dns64_aaaaoklen == 0);
-       INSIST(client->dns64_aaaa == NULL);
-       INSIST(client->dns64_sigaaaa == NULL);
-
-       if (dns64 == NULL)
-               return (true);
-
-       if (RECURSIONOK(client))
-               flags |= DNS_DNS64_RECURSIVE;
-
-       if (WANTDNSSEC(client) && sigrdataset != NULL &&
-           dns_rdataset_isassociated(sigrdataset))
-               flags |= DNS_DNS64_DNSSEC;
-
-       count = dns_rdataset_count(rdataset);
-       aaaaok = isc_mem_get(client->mctx, sizeof(bool) * count);
-
-       isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
-       if (dns_dns64_aaaaok(dns64, &netaddr, client->signer,
-                            env, flags, rdataset, aaaaok, count))
-       {
-               for (i = 0; i < count; i++) {
-                       if (aaaaok != NULL && !aaaaok[i]) {
-                               SAVE(client->dns64_aaaaok, aaaaok);
-                               client->dns64_aaaaoklen = count;
-                               break;
-                       }
-               }
-               if (aaaaok != NULL)
-                       isc_mem_put(client->mctx, aaaaok,
-                                   sizeof(bool) * count);
-               return (true);
-       }
-       if (aaaaok != NULL)
-               isc_mem_put(client->mctx, aaaaok,
-                           sizeof(bool) * count);
-       return (false);
-}
-
 /*
  * Look for the name and type in the redirection zone.  If found update
  * the arguments as appropriate.  Return true if a update was
@@ -5329,7 +5106,7 @@ root_key_sentinel_detect(query_ctx_t *qctx) {
  *
  * Called first by query_setup(), and then again as often as needed to
  * follow a CNAME chain.  Determines which authoritative database to
- * search, then hands off processing to query_lookup().
+ * search, then hands off processing to ns_query_lookup().
  */
 isc_result_t
 ns__query_start(query_ctx_t *qctx) {
@@ -5534,19 +5311,14 @@ ns__query_start(query_ctx_t *qctx) {
                }
        }
 
-       return (query_lookup(qctx));
+       return (ns_query_lookup(qctx));
 
  cleanup:
        return (result);
 }
 
-/*%
- * Perform a local database lookup, in either an authoritative or
- * cache database. If unable to answer, call ns_query_done(); otherwise
- * hand off processing to query_gotanswer().
- */
-static isc_result_t
-query_lookup(query_ctx_t *qctx) {
+isc_result_t
+ns_query_lookup(query_ctx_t *qctx) {
        isc_buffer_t b;
        isc_result_t result;
        dns_clientinfomethods_t cm;
@@ -5554,7 +5326,7 @@ query_lookup(query_ctx_t *qctx) {
        dns_name_t *rpzqname = NULL;
        unsigned int dboptions;
 
-       CCTRACE(ISC_LOG_DEBUG(3), "query_lookup");
+       CCTRACE(ISC_LOG_DEBUG(3), "ns_query_lookup");
 
        CALL_HOOK(NS_QUERY_LOOKUP_BEGIN, qctx);
 
@@ -5567,7 +5339,7 @@ query_lookup(query_ctx_t *qctx) {
        qctx->dbuf = ns_client_getnamebuf(qctx->client);
        if (ISC_UNLIKELY(qctx->dbuf == NULL)) {
                CCTRACE(ISC_LOG_ERROR,
-                      "query_lookup: ns_client_getnamebuf failed (2)");
+                      "ns_query_lookup: ns_client_getnamebuf failed (2)");
                QUERY_ERROR(qctx, ISC_R_NOMEMORY);
                return (ns_query_done(qctx));
        }
@@ -5577,7 +5349,7 @@ query_lookup(query_ctx_t *qctx) {
 
        if (ISC_UNLIKELY(qctx->fname == NULL || qctx->rdataset == NULL)) {
                CCTRACE(ISC_LOG_ERROR,
-                      "query_lookup: ns_client_newname failed (2)");
+                      "ns_query_lookup: ns_client_newname failed (2)");
                QUERY_ERROR(qctx, ISC_R_NOMEMORY);
                return (ns_query_done(qctx));
        }
@@ -5588,7 +5360,8 @@ query_lookup(query_ctx_t *qctx) {
                qctx->sigrdataset = ns_client_newrdataset(qctx->client);
                if (qctx->sigrdataset == NULL) {
                        CCTRACE(ISC_LOG_ERROR,
-                             "query_lookup: ns_client_newrdataset failed (2)");
+                             "ns_query_lookup: "
+                             "ns_client_newrdataset failed (2)");
                        QUERY_ERROR(qctx, ISC_R_NOMEMORY);
                        return (ns_query_done(qctx));
                }
@@ -6496,8 +6269,8 @@ query_checkrpz(query_ctx_t *qctx, isc_result_t result) {
                if (qctx->rpz_st->m.rpz->addsoa) {
                        bool override_ttl =
                                 dns_rdataset_isassociated(qctx->rdataset);
-                       rresult = query_addsoa(qctx, override_ttl,
-                                              DNS_SECTION_ADDITIONAL);
+                       rresult = ns_query_addsoa(qctx, override_ttl,
+                                                 DNS_SECTION_ADDITIONAL);
                        if (rresult != ISC_R_SUCCESS) {
                                QUERY_ERROR(qctx, result);
                                return (ISC_R_COMPLETE);
@@ -6868,11 +6641,11 @@ query_gotanswer(query_ctx_t *qctx, isc_result_t res) {
 
        case DNS_R_EMPTYNAME:
                qctx->nxresult = DNS_R_EMPTYNAME;
-               return (query_nodata(qctx));
+               return (ns_query_nodata(qctx));
 
        case DNS_R_NXRRSET:
                qctx->nxresult = DNS_R_NXRRSET;
-               return (query_nodata(qctx));
+               return (ns_query_nodata(qctx));
 
        case DNS_R_EMPTYWILD:
                return (query_nxdomain(qctx, true));
@@ -6889,11 +6662,11 @@ query_gotanswer(query_ctx_t *qctx, isc_result_t res) {
                        return (result);
                }
                qctx->nxresult = DNS_R_NCACHENXDOMAIN;
-               return (query_ncache(qctx));
+               return (ns_query_ncache(qctx));
 
        case DNS_R_NCACHENXRRSET:
                qctx->nxresult = DNS_R_NCACHENXRRSET;
-               return (query_ncache(qctx));
+               return (ns_query_ncache(qctx));
 
        case DNS_R_CNAME:
                return (query_cname(qctx));
@@ -6914,7 +6687,7 @@ query_gotanswer(query_ctx_t *qctx, isc_result_t res) {
                         * If serve-stale is enabled, query_usestale() already
                         * set up 'qctx' for looking up a stale response.
                         */
-                       return (query_lookup(qctx));
+                       return (ns_query_lookup(qctx));
                }
 
                /*
@@ -7388,345 +7161,6 @@ query_respond(query_ctx_t *qctx) {
        return (result);
 }
 
-static isc_result_t
-query_dns64(query_ctx_t *qctx) {
-       ns_client_t *client = qctx->client;
-       dns_aclenv_t *env = ns_interfacemgr_getaclenv(client->interface->mgr);
-       dns_name_t *name, *mname;
-       dns_rdata_t *dns64_rdata;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
-       dns_rdatalist_t *dns64_rdatalist;
-       dns_rdataset_t *dns64_rdataset;
-       dns_rdataset_t *mrdataset;
-       isc_buffer_t *buffer;
-       isc_region_t r;
-       isc_result_t result;
-       dns_view_t *view = client->view;
-       isc_netaddr_t netaddr;
-       dns_dns64_t *dns64;
-       unsigned int flags = 0;
-       const dns_section_t section = DNS_SECTION_ANSWER;
-
-       /*%
-        * To the current response for 'qctx->client', add the answer RRset
-        * '*rdatasetp' and an optional signature set '*sigrdatasetp', with
-        * owner name '*namep', to the answer section, unless they are
-        * already there.  Also add any pertinent additional data.
-        *
-        * If 'qctx->dbuf' is not NULL, then 'qctx->fname' is the name
-        * whose data is stored 'qctx->dbuf'.  In this case,
-        * query_addrrset() guarantees that when it returns the name
-        * will either have been kept or released.
-        */
-       CTRACE(ISC_LOG_DEBUG(3), "query_dns64");
-
-       qctx->qtype = qctx->type = dns_rdatatype_aaaa;
-
-       name = qctx->fname;
-       mname = NULL;
-       mrdataset = NULL;
-       buffer = NULL;
-       dns64_rdata = NULL;
-       dns64_rdataset = NULL;
-       dns64_rdatalist = NULL;
-       result = dns_message_findname(client->message, section,
-                                     name, dns_rdatatype_aaaa,
-                                     qctx->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(ISC_LOG_DEBUG(3),
-                      "query_dns64: dns_message_findname succeeded: done");
-               if (qctx->dbuf != NULL) {
-                       ns_client_releasename(client, &qctx->fname);
-               }
-               return (ISC_R_SUCCESS);
-       } else if (result == DNS_R_NXDOMAIN) {
-               /*
-                * The name doesn't exist.
-                */
-               if (qctx->dbuf != NULL) {
-                       ns_client_keepname(client, name, qctx->dbuf);
-               }
-               dns_message_addname(client->message, name, section);
-               qctx->fname = NULL;
-               mname = name;
-       } else {
-               RUNTIME_CHECK(result == DNS_R_NXRRSET);
-               if (qctx->dbuf != NULL) {
-                       ns_client_releasename(client, &qctx->fname);
-               }
-       }
-
-       if (qctx->rdataset->trust != dns_trust_secure) {
-               client->query.attributes &= ~NS_QUERYATTR_SECURE;
-       }
-
-       isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
-
-       result = isc_buffer_allocate(client->mctx, &buffer,
-                                    view->dns64cnt * 16 *
-                                    dns_rdataset_count(qctx->rdataset));
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-       result = dns_message_gettemprdataset(client->message,
-                                            &dns64_rdataset);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-       result = dns_message_gettemprdatalist(client->message,
-                                             &dns64_rdatalist);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-
-       dns_rdatalist_init(dns64_rdatalist);
-       dns64_rdatalist->rdclass = dns_rdataclass_in;
-       dns64_rdatalist->type = dns_rdatatype_aaaa;
-       if (client->dns64_ttl != UINT32_MAX)
-               dns64_rdatalist->ttl = ISC_MIN(qctx->rdataset->ttl,
-                                              client->dns64_ttl);
-       else
-               dns64_rdatalist->ttl = ISC_MIN(qctx->rdataset->ttl, 600);
-
-       if (RECURSIONOK(client))
-               flags |= DNS_DNS64_RECURSIVE;
-
-       /*
-        * We use the signatures from the A lookup to set DNS_DNS64_DNSSEC
-        * as this provides a easy way to see if the answer was signed.
-        */
-       if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
-           dns_rdataset_isassociated(qctx->sigrdataset))
-               flags |= DNS_DNS64_DNSSEC;
-
-       for (result = dns_rdataset_first(qctx->rdataset);
-            result == ISC_R_SUCCESS;
-            result = dns_rdataset_next(qctx->rdataset)) {
-               for (dns64 = ISC_LIST_HEAD(client->view->dns64);
-                    dns64 != NULL; dns64 = dns_dns64_next(dns64)) {
-
-                       dns_rdataset_current(qctx->rdataset, &rdata);
-                       isc_buffer_availableregion(buffer, &r);
-                       INSIST(r.length >= 16);
-                       result = dns_dns64_aaaafroma(dns64, &netaddr,
-                                                    client->signer, env, flags,
-                                                    rdata.data, r.base);
-                       if (result != ISC_R_SUCCESS) {
-                               dns_rdata_reset(&rdata);
-                               continue;
-                       }
-                       isc_buffer_add(buffer, 16);
-                       isc_buffer_remainingregion(buffer, &r);
-                       isc_buffer_forward(buffer, 16);
-                       result = dns_message_gettemprdata(client->message,
-                                                         &dns64_rdata);
-                       if (result != ISC_R_SUCCESS)
-                               goto cleanup;
-                       dns_rdata_init(dns64_rdata);
-                       dns_rdata_fromregion(dns64_rdata, dns_rdataclass_in,
-                                            dns_rdatatype_aaaa, &r);
-                       ISC_LIST_APPEND(dns64_rdatalist->rdata, dns64_rdata,
-                                       link);
-                       dns64_rdata = NULL;
-                       dns_rdata_reset(&rdata);
-               }
-       }
-       if (result != ISC_R_NOMORE)
-               goto cleanup;
-
-       if (ISC_LIST_EMPTY(dns64_rdatalist->rdata))
-               goto cleanup;
-
-       result = dns_rdatalist_tordataset(dns64_rdatalist, dns64_rdataset);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-       dns_rdataset_setownercase(dns64_rdataset, mname);
-       client->query.attributes |= NS_QUERYATTR_NOADDITIONAL;
-       dns64_rdataset->trust = qctx->rdataset->trust;
-
-       query_addtoname(mname, dns64_rdataset);
-       query_setorder(qctx, mname, dns64_rdataset);
-
-       dns64_rdataset = NULL;
-       dns64_rdatalist = NULL;
-       dns_message_takebuffer(client->message, &buffer);
-       inc_stats(client, ns_statscounter_dns64);
-       result = ISC_R_SUCCESS;
-
- cleanup:
-       if (buffer != NULL)
-               isc_buffer_free(&buffer);
-
-       if (dns64_rdata != NULL)
-               dns_message_puttemprdata(client->message, &dns64_rdata);
-
-       if (dns64_rdataset != NULL)
-               dns_message_puttemprdataset(client->message, &dns64_rdataset);
-
-       if (dns64_rdatalist != NULL) {
-               for (dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata);
-                    dns64_rdata != NULL;
-                    dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata))
-               {
-                       ISC_LIST_UNLINK(dns64_rdatalist->rdata,
-                                       dns64_rdata, link);
-                       dns_message_puttemprdata(client->message, &dns64_rdata);
-               }
-               dns_message_puttemprdatalist(client->message, &dns64_rdatalist);
-       }
-
-       CTRACE(ISC_LOG_DEBUG(3), "query_dns64: done");
-       return (result);
-}
-
-static void
-query_filter64(query_ctx_t *qctx) {
-       ns_client_t *client = qctx->client;
-       dns_name_t *name, *mname;
-       dns_rdata_t *myrdata;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
-       dns_rdatalist_t *myrdatalist;
-       dns_rdataset_t *myrdataset;
-       isc_buffer_t *buffer;
-       isc_region_t r;
-       isc_result_t result;
-       unsigned int i;
-       const dns_section_t section = DNS_SECTION_ANSWER;
-
-       CTRACE(ISC_LOG_DEBUG(3), "query_filter64");
-
-       INSIST(client->dns64_aaaaok != NULL);
-       INSIST(client->dns64_aaaaoklen ==
-              dns_rdataset_count(qctx->rdataset));
-
-       name = qctx->fname;
-       mname = NULL;
-       buffer = NULL;
-       myrdata = NULL;
-       myrdataset = NULL;
-       myrdatalist = NULL;
-       result = dns_message_findname(client->message, section,
-                                     name, dns_rdatatype_aaaa,
-                                     qctx->rdataset->covers,
-                                     &mname, &myrdataset);
-       if (result == ISC_R_SUCCESS) {
-               /*
-                * We've already got an RRset of the given name and type.
-                * There's nothing else to do;
-                */
-               CTRACE(ISC_LOG_DEBUG(3),
-                      "query_filter64: dns_message_findname succeeded: done");
-               if (qctx->dbuf != NULL) {
-                       ns_client_releasename(client, &qctx->fname);
-               }
-               return;
-       } else if (result == DNS_R_NXDOMAIN) {
-               mname = name;
-               qctx->fname = NULL;
-       } else {
-               RUNTIME_CHECK(result == DNS_R_NXRRSET);
-               if (qctx->dbuf != NULL) {
-                       ns_client_releasename(client, &qctx->fname);
-               }
-               qctx->dbuf = NULL;
-       }
-
-       if (qctx->rdataset->trust != dns_trust_secure) {
-               client->query.attributes &= ~NS_QUERYATTR_SECURE;
-       }
-
-       result = isc_buffer_allocate(client->mctx, &buffer,
-                                    16 * dns_rdataset_count(qctx->rdataset));
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-       result = dns_message_gettemprdataset(client->message, &myrdataset);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-       result = dns_message_gettemprdatalist(client->message, &myrdatalist);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-
-       dns_rdatalist_init(myrdatalist);
-       myrdatalist->rdclass = dns_rdataclass_in;
-       myrdatalist->type = dns_rdatatype_aaaa;
-       myrdatalist->ttl = qctx->rdataset->ttl;
-
-       i = 0;
-       for (result = dns_rdataset_first(qctx->rdataset);
-            result == ISC_R_SUCCESS;
-            result = dns_rdataset_next(qctx->rdataset))
-       {
-               if (!client->dns64_aaaaok[i++])
-                       continue;
-               dns_rdataset_current(qctx->rdataset, &rdata);
-               INSIST(rdata.length == 16);
-               isc_buffer_putmem(buffer, rdata.data, rdata.length);
-               isc_buffer_remainingregion(buffer, &r);
-               isc_buffer_forward(buffer, rdata.length);
-               result = dns_message_gettemprdata(client->message, &myrdata);
-               if (result != ISC_R_SUCCESS)
-                       goto cleanup;
-               dns_rdata_init(myrdata);
-               dns_rdata_fromregion(myrdata, dns_rdataclass_in,
-                                    dns_rdatatype_aaaa, &r);
-               ISC_LIST_APPEND(myrdatalist->rdata, myrdata, link);
-               myrdata = NULL;
-               dns_rdata_reset(&rdata);
-       }
-       if (result != ISC_R_NOMORE)
-               goto cleanup;
-
-       result = dns_rdatalist_tordataset(myrdatalist, myrdataset);
-       if (result != ISC_R_SUCCESS)
-               goto cleanup;
-       dns_rdataset_setownercase(myrdataset, name);
-       client->query.attributes |= NS_QUERYATTR_NOADDITIONAL;
-       if (mname == name) {
-               if (qctx->dbuf != NULL) {
-                       ns_client_keepname(client, name, qctx->dbuf);
-               }
-               dns_message_addname(client->message, name,
-                                   section);
-               qctx->dbuf = NULL;
-       }
-       myrdataset->trust = qctx->rdataset->trust;
-
-       query_addtoname(mname, myrdataset);
-       query_setorder(qctx, mname, myrdataset);
-
-       myrdataset = NULL;
-       myrdatalist = NULL;
-       dns_message_takebuffer(client->message, &buffer);
-
- cleanup:
-       if (buffer != NULL)
-               isc_buffer_free(&buffer);
-
-       if (myrdata != NULL)
-               dns_message_puttemprdata(client->message, &myrdata);
-
-       if (myrdataset != NULL)
-               dns_message_puttemprdataset(client->message, &myrdataset);
-
-       if (myrdatalist != NULL) {
-               for (myrdata = ISC_LIST_HEAD(myrdatalist->rdata);
-                    myrdata != NULL;
-                    myrdata = ISC_LIST_HEAD(myrdatalist->rdata))
-               {
-                       ISC_LIST_UNLINK(myrdatalist->rdata, myrdata, link);
-                       dns_message_puttemprdata(client->message, &myrdata);
-               }
-               dns_message_puttemprdatalist(client->message, &myrdatalist);
-       }
-       if (qctx->dbuf != NULL) {
-               ns_client_releasename(client, &name);
-       }
-
-       CTRACE(ISC_LOG_DEBUG(3), "query_filter64: done");
-}
-
 /*%
  * Handle the case of a name not being found in a database lookup.
  * Called from query_gotanswer(). Passes off processing to
@@ -7916,7 +7350,7 @@ query_zone_delegation(query_ctx_t *qctx) {
                        RESTORE(qctx->zone, tzone);
                        qctx->authoritative = true;
 
-                       return (query_lookup(qctx));
+                       return (ns_query_lookup(qctx));
                }
        }
 
@@ -7930,7 +7364,7 @@ query_zone_delegation(query_ctx_t *qctx) {
                 * 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. If not, then query_lookup() calls
+                * use it instead. If not, then ns_query_lookup() calls
                 * query_notfound() which calls query_delegation(), and
                 * we'll restore these values there.
                 */
@@ -7944,7 +7378,7 @@ query_zone_delegation(query_ctx_t *qctx) {
                dns_db_attach(qctx->view->cachedb, &qctx->db);
                qctx->is_zone = false;
 
-               return (query_lookup(qctx));
+               return (ns_query_lookup(qctx));
        }
 
        return (query_prepare_delegation_response(qctx));
@@ -8216,11 +7650,8 @@ query_addds(query_ctx_t *qctx) {
        }
 }
 
-/*%
- * Handle authoritative NOERROR/NODATA responses.
- */
-static isc_result_t
-query_nodata(query_ctx_t *qctx) {
+isc_result_t
+ns_query_nodata(query_ctx_t *qctx) {
        isc_result_t result = qctx->nxresult;
 
        CALL_HOOK(NS_QUERY_NODATA_BEGIN, qctx);
@@ -8345,14 +7776,14 @@ query_sign_nodata(query_ctx_t *qctx) {
        if (dns_rdataset_isassociated(qctx->rdataset)) {
                /*
                 * If we've got a NSEC record, we need to save the
-                * name now because we're going call query_addsoa()
+                * name now because we're going call ns_query_addsoa()
                 * below, and it needs to use the name buffer.
                 */
                ns_client_keepname(qctx->client, qctx->fname, qctx->dbuf);
        } else if (qctx->fname != NULL) {
                /*
                 * We're not going to use fname, and need to release
-                * our hold on the name buffer so query_addsoa()
+                * our hold on the name buffer so ns_query_addsoa()
                 * may use it.
                 */
                ns_client_releasename(qctx->client, &qctx->fname);
@@ -8363,8 +7794,8 @@ query_sign_nodata(query_ctx_t *qctx) {
         * if this was an RPZ rewrite, but if it wasn't, add it now.
         */
        if (!qctx->nxrewrite) {
-               result = query_addsoa(qctx, UINT32_MAX,
-                                     DNS_SECTION_AUTHORITY);
+               result = ns_query_addsoa(qctx, UINT32_MAX,
+                                        DNS_SECTION_AUTHORITY);
                if (result != ISC_R_SUCCESS) {
                        QUERY_ERROR(qctx, result);
                        return (ns_query_done(qctx));
@@ -8467,14 +7898,14 @@ query_nxdomain(query_ctx_t *qctx, bool empty_wild) {
        if (dns_rdataset_isassociated(qctx->rdataset)) {
                /*
                 * If we've got a NSEC record, we need to save the
-                * name now because we're going call query_addsoa()
+                * name now because we're going call ns_query_addsoa()
                 * below, and it needs to use the name buffer.
                 */
                ns_client_keepname(qctx->client, qctx->fname, qctx->dbuf);
        } else if (qctx->fname != NULL) {
                /*
                 * We're not going to use fname, and need to release
-                * our hold on the name buffer so query_addsoa()
+                * our hold on the name buffer so ns_query_addsoa()
                 * may use it.
                 */
                ns_client_releasename(qctx->client, &qctx->fname);
@@ -8498,7 +7929,7 @@ query_nxdomain(query_ctx_t *qctx, bool empty_wild) {
                ttl = 0;
        }
        if (!qctx->nxrewrite || qctx->rpz_st->m.rpz->addsoa) {
-               result = query_addsoa(qctx, ttl, section);
+               result = ns_query_addsoa(qctx, ttl, section);
                if (result != ISC_R_SUCCESS) {
                        QUERY_ERROR(qctx, result);
                        return (ns_query_done(qctx));
@@ -8557,12 +7988,12 @@ query_redirect(query_ctx_t *qctx)  {
                qctx->redirected = true;
                qctx->is_zone = true;
                qctx->nxresult = DNS_R_NXRRSET;
-               return (query_nodata(qctx));
+               return (ns_query_nodata(qctx));
        case DNS_R_NCACHENXRRSET:
                qctx->redirected = true;
                qctx->is_zone = false;
                qctx->nxresult = DNS_R_NCACHENXRRSET;
-               return (query_ncache(qctx));
+               return (ns_query_ncache(qctx));
        default:
                break;
        }
@@ -8597,12 +8028,12 @@ query_redirect(query_ctx_t *qctx)  {
                qctx->redirected = true;
                qctx->is_zone = true;
                qctx->nxresult = DNS_R_NXRRSET;
-               return (query_nodata(qctx));
+               return (ns_query_nodata(qctx));
        case DNS_R_NCACHENXRRSET:
                qctx->redirected = true;
                qctx->is_zone = false;
                qctx->nxresult = DNS_R_NCACHENXRRSET;
-               return (query_ncache(qctx));
+               return (ns_query_ncache(qctx));
        default:
                break;
        }
@@ -9303,20 +8734,14 @@ query_coveringnsec(query_ctx_t *qctx) {
                if (qctx->sigrdataset != NULL) {
                        ns_client_putrdataset(qctx->client, &qctx->sigrdataset);
                }
-               return (query_lookup(qctx));
+               return (ns_query_lookup(qctx));
        }
 
        return (ns_query_done(qctx));
 }
 
-/*%
- * Handle negative cache responses, DNS_R_NCACHENXRRSET or
- * DNS_R_NCACHENXDOMAIN. (Note: may be called with other
- * result codes as a result of hook actions; for example,
- * DNS64 may call with DNS_R_NXOMAIN.)
- */
-static isc_result_t
-query_ncache(query_ctx_t *qctx) {
+isc_result_t
+ns_query_ncache(query_ctx_t *qctx) {
        INSIST(!qctx->is_zone);
 
        qctx->authoritative = false;
@@ -9336,7 +8761,7 @@ query_ncache(query_ctx_t *qctx) {
                }
        }
 
-       return (query_nodata(qctx));
+       return (ns_query_nodata(qctx));
 }
 
 /*
@@ -9734,32 +9159,19 @@ query_prepresponse(query_ctx_t *qctx) {
        return (result);
 }
 
-/*%
- * Add SOA to the authority section when sending negative responses
- * (or to the additional section if sending negative responses triggered
- * by RPZ rewriting.)
- */
-static isc_result_t
-query_addsoa(query_ctx_t *qctx, unsigned int override_ttl,
-            dns_section_t section)
-{
+isc_result_t
+ns_query_addsoa(query_ctx_t *qctx, dns_ttl_t override_ttl,
+               dns_section_t section) {
        ns_client_t *client = qctx->client;
-       dns_name_t *name;
-       dns_dbnode_t *node;
-       isc_result_t result, eresult;
+       dns_name_t *name = NULL;
+       dns_dbnode_t *node = NULL;
+       isc_result_t result, eresult = ISC_R_SUCCESS;
        dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
        dns_rdataset_t **sigrdatasetp = NULL;
        dns_clientinfomethods_t cm;
        dns_clientinfo_t ci;
 
-       CTRACE(ISC_LOG_DEBUG(3), "query_addsoa");
-       /*
-        * Initialization.
-        */
-       eresult = ISC_R_SUCCESS;
-       name = NULL;
-       rdataset = NULL;
-       node = NULL;
+       CTRACE(ISC_LOG_DEBUG(3), "ns_query_addsoa");
 
        dns_clientinfomethods_init(&cm, ns_client_sourceip);
        dns_clientinfo_init(&ci, client, NULL);
@@ -9840,8 +9252,9 @@ query_addsoa(query_ctx_t *qctx, unsigned int override_ttl,
                    override_ttl < rdataset->ttl)
                {
                        rdataset->ttl = override_ttl;
-                       if (sigrdataset != NULL)
+                       if (sigrdataset != NULL) {
                                sigrdataset->ttl = override_ttl;
+                       }
                }
 
                /*
@@ -11106,292 +10519,3 @@ ns_query_start(ns_client_t *client) {
        ns_client_attach(client, &qclient);
        (void)query_setup(qclient, qtype);
 }
-
-static bool
-dns64_qctx_initialize(void *arg, void *cbdata, isc_result_t *resp) {
-       UNUSED(arg);
-       UNUSED(cbdata);
-
-       *resp = ISC_R_UNSET;
-       return (false);
-}
-
-static bool
-dns64_respond_begin(void *arg, void *cbdata, isc_result_t *resp) {
-       query_ctx_t *qctx = (query_ctx_t *) arg;
-
-       UNUSED(cbdata);
-
-       /*
-        * Check to see if the AAAA RRset has non-excluded addresses
-        * in it.  If not look for a A RRset.
-        */
-       INSIST(qctx->client->dns64_aaaaok == NULL);
-
-       if (qctx->qtype == dns_rdatatype_aaaa && !qctx->dns64_exclude &&
-           !ISC_LIST_EMPTY(qctx->view->dns64) &&
-           qctx->client->message->rdclass == dns_rdataclass_in &&
-           !dns64_aaaaok(qctx->client, qctx->rdataset, qctx->sigrdataset))
-       {
-               /*
-                * Look to see if there are A records for this name.
-                */
-               qctx->client->dns64_ttl = qctx->rdataset->ttl;
-               SAVE(qctx->client->dns64_aaaa, qctx->rdataset);
-               SAVE(qctx->client->dns64_sigaaaa, qctx->sigrdataset);
-               ns_client_releasename(qctx->client, &qctx->fname);
-               dns_db_detachnode(qctx->db, &qctx->node);
-               qctx->type = qctx->qtype = dns_rdatatype_a;
-               qctx->dns64_exclude = qctx->dns64 = true;
-
-               /*
-                * XXX: we are depending here on DNS64
-                * being reached before any other modules that
-                * might set up recursion. In particular if
-                * the filter-aaaa module runs first, there'll
-                * be an assertion failure. We need to make this
-                * order-indeendent.
-                */
-               *resp = query_lookup(qctx);
-               return (true);
-       }
-
-       *resp = ISC_R_UNSET;
-       return (false);
-}
-
-static bool
-dns64_respond_add_answer(void *arg, void *cbdata, isc_result_t *resp) {
-       query_ctx_t *qctx = (query_ctx_t *) arg;
-
-       UNUSED(cbdata);
-
-       if (qctx->dns64) {
-               isc_result_t result = query_dns64(qctx);
-               qctx->noqname = NULL;
-               dns_rdataset_disassociate(qctx->rdataset);
-               dns_message_puttemprdataset(qctx->client->message,
-                                           &qctx->rdataset);
-               if (result == ISC_R_NOMORE) {
-                       if (qctx->dns64_exclude) {
-                               if (!qctx->is_zone) {
-                                       *resp = ns_query_done(qctx);
-                                       return (true);
-                               }
-                               /*
-                                * Add a fake SOA record.
-                                */
-                               (void) query_addsoa(qctx, 600,
-                                                   DNS_SECTION_AUTHORITY);
-                               *resp = ns_query_done(qctx);
-                               return (true);
-                       }
-                       if (qctx->is_zone) {
-                               qctx->nxresult = DNS_R_NXDOMAIN;
-                               *resp = query_nodata(qctx);
-                       } else {
-                               qctx->nxresult = DNS_R_NXDOMAIN;
-                               *resp = query_ncache(qctx);
-                       }
-               } else if (result != ISC_R_SUCCESS) {
-                       qctx->result = result;
-                       *resp = ns_query_done(qctx);
-               } else {
-                       *resp = ISC_R_COMPLETE;
-               }
-               return (true);
-       } else if (qctx->client->dns64_aaaaok != NULL) {
-               query_filter64(qctx);
-               ns_client_putrdataset(qctx->client, &qctx->rdataset);
-               *resp = ISC_R_COMPLETE;
-               return (true);
-       }
-
-       *resp = ISC_R_UNSET;
-       return (false);
-}
-
-static bool
-dns64_resume_restored(void *arg, void *cbdata, isc_result_t *resp) {
-       query_ctx_t *qctx = (query_ctx_t *) arg;
-
-       UNUSED(cbdata);
-
-       if (DNS64(qctx->client)) {
-               qctx->client->query.attributes &= ~NS_QUERYATTR_DNS64;
-               qctx->dns64 = true;
-       }
-
-       if (DNS64EXCLUDE(qctx->client)) {
-               qctx->client->query.attributes &= ~NS_QUERYATTR_DNS64EXCLUDE;
-               qctx->dns64_exclude = true;
-       }
-
-       *resp = ISC_R_UNSET;
-       return (false);
-}
-
-static bool
-dns64_notfound_recurse(void *arg, void *cbdata, isc_result_t *resp) {
-       query_ctx_t *qctx = (query_ctx_t *) arg;
-
-       UNUSED(cbdata);
-
-       if (qctx->dns64) {
-               qctx->client->query.attributes |= NS_QUERYATTR_DNS64;
-       }
-       if (qctx->dns64_exclude) {
-               qctx->client->query.attributes |= NS_QUERYATTR_DNS64EXCLUDE;
-       }
-
-       *resp = ISC_R_UNSET;
-       return (false);
-}
-
-static bool
-dns64_delegation_recurse(void *arg, void *cbdata, isc_result_t *resp) {
-       query_ctx_t *qctx = (query_ctx_t *) arg;
-
-       UNUSED(cbdata);
-
-       /*
-        * Look up an A record so we can synthesize DNS64.
-        */
-       if (qctx->dns64) {
-               qctx->result = ns_query_recurse(qctx->client,
-                                               dns_rdatatype_a,
-                                               qctx->client->query.qname,
-                                               NULL, NULL, qctx->resuming);
-               qctx->client->query.attributes |= NS_QUERYATTR_RECURSING;
-
-               if (qctx->result == ISC_R_SUCCESS) {
-                       qctx->client->query.attributes |= NS_QUERYATTR_DNS64;
-                       if (qctx->dns64_exclude) {
-                               qctx->client->query.attributes |=
-                                     NS_QUERYATTR_DNS64EXCLUDE;
-                       }
-               }
-
-               *resp = ISC_R_COMPLETE;
-               return (true);
-       }
-
-       *resp = ISC_R_UNSET;
-       return (false);
-}
-
-static bool
-dns64_nodata_begin(void *arg, void *cbdata, isc_result_t *resp) {
-       query_ctx_t *qctx = (query_ctx_t *) arg;
-
-       UNUSED(cbdata);
-
-       if (qctx->dns64 && !qctx->dns64_exclude) {
-               isc_buffer_t b;
-
-               /*
-                * Restore the answers from the previous AAAA lookup.
-                */
-               if (qctx->rdataset != NULL) {
-                       ns_client_putrdataset(qctx->client, &qctx->rdataset);
-               }
-               if (qctx->sigrdataset != NULL) {
-                       ns_client_putrdataset(qctx->client, &qctx->sigrdataset);
-               }
-               RESTORE(qctx->rdataset, qctx->client->dns64_aaaa);
-               RESTORE(qctx->sigrdataset, qctx->client->dns64_sigaaaa);
-               if (qctx->fname == NULL) {
-                       qctx->dbuf = ns_client_getnamebuf(qctx->client);
-                       if (qctx->dbuf == NULL) {
-                               CCTRACE(ISC_LOG_ERROR,
-                                      "query_nodata: "
-                                      "ns_client_getnamebuf failed (3)");
-                               QUERY_ERROR(qctx, DNS_R_SERVFAIL);
-                               *resp = ns_query_done(qctx);
-                               return (true);
-                       }
-                       qctx->fname = ns_client_newname(qctx->client,
-                                                   qctx->dbuf, &b);
-                       if (qctx->fname == NULL) {
-                               CCTRACE(ISC_LOG_ERROR,
-                                      "query_nodata: "
-                                      "ns_client_newname failed (3)");
-                               QUERY_ERROR(qctx, DNS_R_SERVFAIL);
-                               *resp = ns_query_done(qctx);
-                               return (true);
-                       }
-               }
-               dns_name_copy(qctx->client->query.qname, qctx->fname, NULL);
-               qctx->dns64 = false;
-       } else if ((qctx->nxresult == DNS_R_NXRRSET ||
-                   qctx->nxresult == DNS_R_NCACHENXRRSET) &&
-                  !ISC_LIST_EMPTY(qctx->view->dns64) &&
-                  !qctx->nxrewrite &&
-                  qctx->client->message->rdclass == dns_rdataclass_in &&
-                  qctx->qtype == dns_rdatatype_aaaa)
-       {
-               /*
-                * Look to see if there are A records for this name.
-                */
-               switch (qctx->nxresult) {
-               case DNS_R_NCACHENXRRSET:
-                       /*
-                        * This is from the negative cache; if the ttl is
-                        * zero, we need to work out whether we have just
-                        * decremented to zero or there was no negative
-                        * cache ttl in the answer.
-                        */
-                       if (qctx->rdataset->ttl != 0) {
-                               qctx->client->dns64_ttl = qctx->rdataset->ttl;
-                               break;
-                       }
-                       if (dns_rdataset_first(qctx->rdataset) == ISC_R_SUCCESS)
-                               qctx->client->dns64_ttl = 0;
-                       break;
-               case DNS_R_NXRRSET:
-                       qctx->client->dns64_ttl =
-                               dns64_ttl(qctx->db, qctx->version);
-                       break;
-               default:
-                       INSIST(0);
-               }
-
-               SAVE(qctx->client->dns64_aaaa, qctx->rdataset);
-               SAVE(qctx->client->dns64_sigaaaa, qctx->sigrdataset);
-               ns_client_releasename(qctx->client, &qctx->fname);
-               dns_db_detachnode(qctx->db, &qctx->node);
-               qctx->type = qctx->qtype = dns_rdatatype_a;
-               qctx->dns64 = true;
-               *resp = query_lookup(qctx);
-               return (true);
-       }
-
-       *resp = ISC_R_UNSET;
-       return (false);
-}
-
-static bool
-dns64_zerottl_recurse(void *arg, void *cbdata, isc_result_t *resp) {
-       query_ctx_t *qctx = (query_ctx_t *) arg;
-
-       UNUSED(cbdata);
-
-       if (qctx->dns64) {
-               qctx->client->query.attributes |= NS_QUERYATTR_DNS64;
-       }
-       if (qctx->dns64_exclude) {
-               qctx->client->query.attributes |= NS_QUERYATTR_DNS64EXCLUDE;
-       }
-
-       *resp = ISC_R_UNSET;
-       return (false);
-}
-
-static bool
-dns64_qctx_destroy(void *arg, void *cbdata, isc_result_t *resp) {
-       UNUSED(arg);
-       UNUSED(cbdata);
-
-       *resp = ISC_R_UNSET;
-       return (false);
-}