]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
refactor filter-aaaa implementation
authorEvan Hunt <each@isc.org>
Tue, 7 Aug 2018 02:34:20 +0000 (19:34 -0700)
committerEvan Hunt <each@isc.org>
Thu, 6 Dec 2018 18:29:10 +0000 (10:29 -0800)
 - the goal of this change is for AAAA filtering to be fully contained
   in the query logic, and implemented at discrete points that can be
   replaced with hook callouts later on.
 - the new code may be slightly less efficient than the old filter-aaaa
   implementation, but maximum efficiency was never a priority for AAAA
   filtering anyway.
 - we now use the rdataset RENDERED attribute to indicate that an AAAA
   rdataset should not be included when rendering the message. (this
   flag was originally meant to indicate that an rdataset has already
   been rendered and should not be repeated, but it can also be used to
   prevent rendering in the first place.)
 - the DNS_MESSAGERENDER_FILTER_AAAA, NS_CLIENTATTR_FILTER_AAAA,
   and DNS_RDATASETGLUE_FILTERAAAA flags are all now unnecessary and
   have been removed.

13 files changed:
bin/tests/system/filter-aaaa/tests.sh
lib/dns/include/dns/db.h
lib/dns/include/dns/message.h
lib/dns/include/dns/rdataset.h
lib/dns/message.c
lib/dns/rbtdb.c
lib/dns/rdataset.c
lib/ns/client.c
lib/ns/include/ns/client.h
lib/ns/include/ns/hooks.h
lib/ns/include/ns/query.h
lib/ns/query.c
lib/ns/server.c

index 16a9332518c9aac6497e34f3c1de1024d70e89a8..f3e06b4fe472ff9b482c5b5d93213d0295c9e079 100644 (file)
@@ -117,7 +117,6 @@ echo_i "checking that A and not AAAA is returned when both AAAA and A records ex
 ret=0
 $DIG $DIGOPTS any dual.signed -b 10.53.0.1 @10.53.0.1 > dig.out.ns1.test$n || ret=1
 grep "status: NOERROR" dig.out.ns1.test$n > /dev/null || ret=1
-grep "AUTHORITY: 0," dig.out.ns1.test$n > /dev/null || ret=1
 grep "1.0.0.3" dig.out.ns1.test$n > /dev/null || ret=1
 grep "::3" dig.out.ns1.test$n > /dev/null && ret=1
 if [ $ret != 0 ]; then echo_i "failed"; fi
@@ -128,7 +127,6 @@ echo_i "checking that A and not AAAA is returned when both AAAA and A records ex
 ret=0
 $DIG $DIGOPTS any dual.unsigned -b 10.53.0.1 @10.53.0.1 > dig.out.ns1.test$n || ret=1
 grep "status: NOERROR" dig.out.ns1.test$n > /dev/null || ret=1
-grep "AUTHORITY: 0," dig.out.ns1.test$n > /dev/null || ret=1
 grep "1.0.0.6" dig.out.ns1.test$n > /dev/null || ret=1
 grep "::6" dig.out.ns1.test$n > /dev/null && ret=1
 if [ $ret != 0 ]; then echo_i "failed"; fi
@@ -150,7 +148,6 @@ echo_i "checking that A and not AAAA is returned when both AAAA and A records ex
 ret=0
 $DIG $DIGOPTS any dual.unsigned +dnssec -b 10.53.0.1 @10.53.0.1 > dig.out.ns1.test$n || ret=1
 grep "status: NOERROR" dig.out.ns1.test$n > /dev/null || ret=1
-grep "AUTHORITY: 0," dig.out.ns1.test$n > /dev/null || ret=1
 grep "1.0.0.6" dig.out.ns1.test$n > /dev/null || ret=1
 grep "::6" dig.out.ns1.test$n > /dev/null && ret=1
 if [ $ret != 0 ]; then echo_i "failed"; fi
index b7b28c4ed75880d3000e6a08216c7a97ee1525f2..4f77471e79ac413dff0ebbbf6d75105750f918d5 100644 (file)
@@ -1142,7 +1142,7 @@ dns_db_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
  *
  * \li 'sigrdataset' is a valid, disassociated rdataset, or it is NULL.
  *
- * \li If 'covers' != 0, 'type' must be SIG.
+ * \li If 'covers' != 0, 'type' must be RRSIG.
  *
  * \li 'type' is not a meta-RR type such as 'ANY' or 'OPT'.
  *
index 28290e0be596752d4755340a28c6e5016a481616..6f389aa2df2f77bc12a33267b1ea56eaa841a290 100644 (file)
@@ -181,7 +181,7 @@ typedef int dns_messagetextflag_t;
                                                      additional section. */
 #define DNS_MESSAGERENDER_PREFER_AAAA  0x0010  /*%< prefer AAAA records in
                                                  additional section. */
-#define DNS_MESSAGERENDER_FILTER_AAAA  0x0020  /*%< filter AAAA records */
+/* Obsolete: DNS_MESSAGERENDER_FILTER_AAAA     0x0020  */
 
 typedef struct dns_msgblock dns_msgblock_t;
 
index f48e32d55c775d6eec85f1cfcf7ad50455c6766c..6a07d214c9358ecbf1757ae54844856b6c229ec7 100644 (file)
@@ -92,7 +92,6 @@ typedef struct dns_rdatasetmethods {
                                                dns_name_t *name);
        isc_result_t            (*addglue)(dns_rdataset_t *rdataset,
                                           dns_dbversion_t *version,
-                                          unsigned int options,
                                           dns_message_t *msg);
 } dns_rdatasetmethods_t;
 
@@ -197,13 +196,6 @@ struct dns_rdataset {
  */
 #define DNS_RDATASETTOWIRE_OMITDNSSEC  0x0001
 
-/*%
- * _FILTERAAAA
- *     If A records are present, omit AAAA records when adding
- *     glue
- */
-#define DNS_RDATASETADDGLUE_FILTERAAAA 0x0001
-
 void
 dns_rdataset_init(dns_rdataset_t *rdataset);
 /*%<
@@ -580,14 +572,11 @@ dns_rdataset_getownercase(const dns_rdataset_t *rdataset, dns_name_t *name);
  */
 
 isc_result_t
-dns_rdataset_addglue(dns_rdataset_t *rdataset,
-                    dns_dbversion_t *version,
-                    unsigned int options,
+dns_rdataset_addglue(dns_rdataset_t *rdataset, dns_dbversion_t *version,
                     dns_message_t *msg);
 /*%<
  * Add glue records for rdataset to the additional section of message in
- * 'msg'. 'rdataset' must be of type NS. If DNS_RDATASETADDGLUE_FILTERAAAA
- * is set in 'options' there is type A glue, type AAAA glue is not added.
+ * 'msg'. 'rdataset' must be of type NS.
  *
  * In case a successful result is not returned, the caller should try to
  * add glue directly to the message by iterating for additional data.
@@ -595,7 +584,6 @@ dns_rdataset_addglue(dns_rdataset_t *rdataset,
  * Requires:
  * \li 'rdataset' is a valid NS rdataset.
  * \li 'version' is the DB version.
- * \li  'options' is options; currently only _FILTERAAAA is defined.
  * \li 'msg' is the DNS message to which the glue should be added.
  *
  * Returns:
index 4a811d28dbc9b62933984c420a3f216be64a0bab..f5847006281e21fdb7ddd39954b996f0fbc3ea29 100644 (file)
@@ -1903,48 +1903,6 @@ wrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) {
        return (true);
 }
 
-/*
- * Decide whether to not answer with an AAAA record and its RRSIG
- */
-static inline bool
-norender_rdataset(const dns_rdataset_t *rdataset, unsigned int options,
-                 dns_section_t sectionid)
-{
-       if (sectionid == DNS_SECTION_QUESTION)
-               return (false);
-
-       switch (rdataset->type) {
-       case dns_rdatatype_ns:
-               if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0 ||
-                   sectionid != DNS_SECTION_AUTHORITY)
-                       return (false);
-               break;
-
-       case dns_rdatatype_aaaa:
-               if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0)
-                       return (false);
-               break;
-
-       case dns_rdatatype_rrsig:
-               if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0 ||
-                   (rdataset->covers != dns_rdatatype_ns &&
-                    rdataset->covers != dns_rdatatype_aaaa))
-                       return (false);
-               if ((rdataset->covers == dns_rdatatype_ns) &&
-                   (sectionid != DNS_SECTION_AUTHORITY))
-                       return (false);
-               break;
-
-       default:
-               return (false);
-       }
-
-       if (rdataset->rdclass != dns_rdataclass_in)
-               return (false);
-
-       return (true);
-}
-
 static isc_result_t
 renderset(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
          dns_compress_t *cctx, isc_buffer_t *target,
@@ -2104,22 +2062,6 @@ dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
                                                      preferred_glue))
                                        goto next;
 
-                               /*
-                                * Suppress AAAAs if asked and we are
-                                * not doing DNSSEC or are breaking DNSSEC.
-                                * Say so in the AD bit if we break DNSSEC.
-                                */
-                               if (norender_rdataset(rdataset, options,
-                                                     sectionid))
-                               {
-                                       if (sectionid == DNS_SECTION_ANSWER ||
-                                           sectionid == DNS_SECTION_AUTHORITY)
-                                           msg->flags &= ~DNS_MESSAGEFLAG_AD;
-                                       if (OPTOUT(rdataset))
-                                           msg->flags &= ~DNS_MESSAGEFLAG_AD;
-                                       goto next;
-                               }
-
                                st = *(msg->buffer);
 
                                count = 0;
index 2f5db4fa7b63ccc56b4510c8d25148767d4ea82e..22dd60519e71375754e6e63b8dcd937fff89173c 100644 (file)
@@ -561,7 +561,6 @@ static void rdataset_getownercase(const dns_rdataset_t *rdataset,
                                  dns_name_t *name);
 static isc_result_t rdataset_addglue(dns_rdataset_t *rdataset,
                                     dns_dbversion_t *version,
-                                    unsigned int options,
                                     dns_message_t *msg);
 static void free_gluetable(rbtdb_version_t *version);
 
@@ -9807,9 +9806,7 @@ out:
 }
 
 static isc_result_t
-rdataset_addglue(dns_rdataset_t *rdataset,
-                dns_dbversion_t *version,
-                unsigned int options,
+rdataset_addglue(dns_rdataset_t *rdataset, dns_dbversion_t *version,
                 dns_message_t *msg)
 {
        dns_rbtdb_t *rbtdb = rdataset->private1;
@@ -9926,42 +9923,39 @@ restart:
                        }
                }
 
-               if (ISC_LIKELY((options & DNS_RDATASETADDGLUE_FILTERAAAA) == 0))
-               {
-                       if (dns_rdataset_isassociated(&ge->rdataset_aaaa)) {
-                               result = dns_message_gettemprdataset(msg,
-                                                           &rdataset_aaaa);
-                               if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
-                                       dns_message_puttempname(msg, &name);
-                                       if (rdataset_a != NULL) {
-                                               dns_message_puttemprdataset(msg,
-                                                           &rdataset_a);
-                                       }
-                                       if (sigrdataset_a != NULL) {
-                                               dns_message_puttemprdataset(msg,
-                                                           &sigrdataset_a);
-                                       }
-                                       goto no_glue;
+               if (dns_rdataset_isassociated(&ge->rdataset_aaaa)) {
+                       result = dns_message_gettemprdataset(msg,
+                                                   &rdataset_aaaa);
+                       if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
+                               dns_message_puttempname(msg, &name);
+                               if (rdataset_a != NULL) {
+                                       dns_message_puttemprdataset(msg,
+                                                   &rdataset_a);
+                               }
+                               if (sigrdataset_a != NULL) {
+                                       dns_message_puttemprdataset(msg,
+                                                   &sigrdataset_a);
                                }
+                               goto no_glue;
                        }
+               }
 
-                       if (dns_rdataset_isassociated(&ge->sigrdataset_aaaa)) {
-                               result = dns_message_gettemprdataset(msg,
-                                                           &sigrdataset_aaaa);
-                               if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
-                                       dns_message_puttempname(msg, &name);
-                                       if (rdataset_a != NULL) {
-                                               dns_message_puttemprdataset(msg,
-                                                           &rdataset_a);
-                                       }
-                                       if (sigrdataset_a != NULL)
-                                               dns_message_puttemprdataset(msg,
-                                                           &sigrdataset_a);
-                                       if (rdataset_aaaa != NULL)
-                                               dns_message_puttemprdataset(msg,
-                                                           &rdataset_aaaa);
-                                       goto no_glue;
+               if (dns_rdataset_isassociated(&ge->sigrdataset_aaaa)) {
+                       result = dns_message_gettemprdataset(msg,
+                                                   &sigrdataset_aaaa);
+                       if (ISC_UNLIKELY(result != ISC_R_SUCCESS)) {
+                               dns_message_puttempname(msg, &name);
+                               if (rdataset_a != NULL) {
+                                       dns_message_puttemprdataset(msg,
+                                                   &rdataset_a);
                                }
+                               if (sigrdataset_a != NULL)
+                                       dns_message_puttemprdataset(msg,
+                                                   &sigrdataset_a);
+                               if (rdataset_aaaa != NULL)
+                                       dns_message_puttemprdataset(msg,
+                                                   &rdataset_aaaa);
+                               goto no_glue;
                        }
                }
 
@@ -9975,20 +9969,17 @@ restart:
                        ISC_LIST_APPEND(name->list, sigrdataset_a, link);
                }
 
-               if (ISC_LIKELY((options & DNS_RDATASETADDGLUE_FILTERAAAA) == 0))
-               {
-                       if (rdataset_aaaa != NULL) {
-                               dns_rdataset_clone(&ge->rdataset_aaaa,
-                                                  rdataset_aaaa);
-                               ISC_LIST_APPEND(name->list, rdataset_aaaa,
-                                               link);
-                       }
-                       if (sigrdataset_aaaa != NULL) {
-                               dns_rdataset_clone(&ge->sigrdataset_aaaa,
-                                                  sigrdataset_aaaa);
-                               ISC_LIST_APPEND(name->list, sigrdataset_aaaa,
-                                               link);
-                       }
+               if (rdataset_aaaa != NULL) {
+                       dns_rdataset_clone(&ge->rdataset_aaaa,
+                                          rdataset_aaaa);
+                       ISC_LIST_APPEND(name->list, rdataset_aaaa,
+                                       link);
+               }
+               if (sigrdataset_aaaa != NULL) {
+                       dns_rdataset_clone(&ge->sigrdataset_aaaa,
+                                          sigrdataset_aaaa);
+                       ISC_LIST_APPEND(name->list, sigrdataset_aaaa,
+                                       link);
                }
 
                dns_message_addname(msg, name, DNS_SECTION_ADDITIONAL);
index d6dcc91b4897aec5aa79ee0888fcbe9afafdf2d6..576e9475b5a279a01d14f4b14d356354a194723b 100644 (file)
@@ -751,9 +751,7 @@ dns_rdataset_trimttl(dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
 }
 
 isc_result_t
-dns_rdataset_addglue(dns_rdataset_t *rdataset,
-                    dns_dbversion_t *version,
-                    unsigned int options,
+dns_rdataset_addglue(dns_rdataset_t *rdataset, dns_dbversion_t *version,
                     dns_message_t *msg)
 {
        REQUIRE(DNS_RDATASET_VALID(rdataset));
@@ -763,6 +761,5 @@ dns_rdataset_addglue(dns_rdataset_t *rdataset,
        if (rdataset->methods->addglue == NULL)
                return (ISC_R_NOTIMPLEMENTED);
 
-       return ((rdataset->methods->addglue)(rdataset, version,
-                                            options, msg));
+       return ((rdataset->methods->addglue)(rdataset, version, msg));
 }
index 3f15e5f76d9c32e5bf9e0f76f2cfe5efc6b80b81..1100db7ffc3766d5e7248c2956fdfb651cd911d2 100644 (file)
@@ -1097,23 +1097,6 @@ client_send(ns_client_t *client) {
                        preferred_glue = DNS_MESSAGERENDER_PREFER_AAAA;
        }
 
-       /*
-        * filter-aaaa-on-v4 yes or break-dnssec option to suppress
-        * AAAA records.
-        *
-        * We already know that request came via IPv4,
-        * that we have both AAAA and A records,
-        * and that we either have no signatures that the client wants
-        * or we are supposed to break DNSSEC.
-        *
-        * Override preferred glue if necessary.
-        */
-       if ((client->attributes & NS_CLIENTATTR_FILTER_AAAA) != 0) {
-               render_opts |= DNS_MESSAGERENDER_FILTER_AAAA;
-               if (preferred_glue == DNS_MESSAGERENDER_PREFER_AAAA)
-                       preferred_glue = DNS_MESSAGERENDER_PREFER_A;
-       }
-
        /*
         * Create an OPT for our reply.
         */
@@ -3064,6 +3047,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
        ISC_QLINK_INIT(client, ilink);
        client->keytag = NULL;
        client->keytag_len = 0;
+       client->hookflags = 0;
 
        /*
         * We call the init routines for the various kinds of client here,
index 10e47fe53af06b5fd626b0d56af08cc6676aa9d5..d855cb7a18b234162435d2b42f78b7259f233c35 100644 (file)
@@ -170,6 +170,12 @@ struct ns_client {
        uint32_t                expire;
        unsigned char           *keytag;
        uint16_t                keytag_len;
+
+       /*%
+        * Allows a hook module to set flags
+        * that persist across recursion.
+        */
+       uint32_t                hookflags;
 };
 
 typedef ISC_QUEUE(ns_client_t) client_queue_t;
@@ -184,8 +190,8 @@ typedef ISC_LIST(ns_client_t) client_list_t;
 #define NS_CLIENTATTR_MULTICAST                0x00008 /*%< recv'd from multicast */
 #define NS_CLIENTATTR_WANTDNSSEC       0x00010 /*%< include dnssec records */
 #define NS_CLIENTATTR_WANTNSID         0x00020 /*%< include nameserver ID */
-#define NS_CLIENTATTR_FILTER_AAAA      0x00040 /*%< suppress AAAAs */
-#define NS_CLIENTATTR_FILTER_AAAA_RC   0x00080 /*%< recursing for A against AAAA */
+/* Obsolete: NS_CLIENTATTR_FILTER_AAAA 0x00040 */
+/* Obsolete: define NS_CLIENTATTR_FILTER_AAAA_RC 0x00080 */
 #define NS_CLIENTATTR_WANTAD           0x00100 /*%< want AD in response if possible */
 #define NS_CLIENTATTR_WANTCOOKIE       0x00200 /*%< return a COOKIE */
 #define NS_CLIENTATTR_HAVECOOKIE       0x00400 /*%< has a valid COOKIE */
index 0475227750909e64c3f3e1ba1e65635ac98e1a49..aa6d8d1548bb853a5d6aceec9d4639db3abd5193 100644 (file)
@@ -162,13 +162,11 @@ typedef enum {
        NS_QUERY_START_BEGIN,
        NS_QUERY_LOOKUP_BEGIN,
        NS_QUERY_RESUME_BEGIN,
-       NS_QUERY_PREP_RESPONSE_BEGIN,
+       NS_QUERY_GOT_ANSWER_BEGIN,
        NS_QUERY_RESPOND_ANY_BEGIN,
-       NS_QUERY_RESPOND_ANY_POST_LOOKUP,
        NS_QUERY_RESPOND_ANY_FOUND,
        NS_QUERY_RESPOND_ANY_NOT_FOUND,
        NS_QUERY_RESPOND_BEGIN,
-       NS_QUERY_GOT_ANSWER_BEGIN,
        NS_QUERY_NOTFOUND_BEGIN,
        NS_QUERY_PREP_DELEGATION_BEGIN,
        NS_QUERY_ZONE_DELEGATION_BEGIN,
@@ -177,9 +175,10 @@ typedef enum {
        NS_QUERY_NXDOMAIN_BEGIN,
        NS_QUERY_CNAME_BEGIN,
        NS_QUERY_DNAME_BEGIN,
-       NS_QUERY_ADDITIONAL_BEGIN,
+       NS_QUERY_PREP_RESPONSE_BEGIN,
        NS_QUERY_DONE_BEGIN,
        NS_QUERY_DONE_SEND,
+
        NS_QUERY_HOOKS_COUNT    /* MUST BE LAST */
 } ns_hookpoint_t;
 
index ec07d2821a4cd3930e129f45c075826f673f3646..2c67e7182b48c1fcf8833246e8f5300dfa13b8e8 100644 (file)
@@ -121,6 +121,9 @@ struct ns_query {
 typedef struct query_ctx {
        isc_buffer_t *dbuf;                     /* name buffer */
        dns_name_t *fname;                      /* found name from DB lookup */
+       dns_name_t *tname;                      /* temporary name, used
+                                                * when processing ANY
+                                                * queries */
        dns_rdataset_t *rdataset;               /* found rdataset */
        dns_rdataset_t *sigrdataset;            /* found sigrdataset */
        dns_rdataset_t *noqname;                /* rdataset needing
@@ -192,4 +195,12 @@ ns__query_sfcache(query_ctx_t *qctx);
 isc_result_t
 ns__query_start(query_ctx_t *qctx);
 
+/*
+ * XXX:
+ * Temporary function used to initialize the filter-aaaa hooks,
+ * which are currently hard-coded rather than loaded as a module.
+ */
+void
+ns__query_inithooks(void);
+
 #endif /* NS_QUERY_H */
index d0b079b75fb40c53998d35bd4f63f72b96b73b47..64e2c6483cf4fe2faa0b59c00055295ae760ee7c 100644 (file)
@@ -191,7 +191,6 @@ client_trace(ns_client_t *client, int level, const char *message) {
 #define CCTRACE(l,m) ((void)m)
 #endif /* WANT_QUERYTRACE */
 
-
 #define DNS_GETDB_NOEXACT 0x01U
 #define DNS_GETDB_NOLOG 0x02U
 #define DNS_GETDB_PARTIAL 0x04U
@@ -429,6 +428,64 @@ query_addauth(query_ctx_t *qctx);
 static isc_result_t
 query_done(query_ctx_t *qctx);
 
+/*
+ * XXX:
+ * Functions implementing filter-aaaa. Later, these will be moved
+ * out to a loadable module.
+ */
+static isc_result_t
+query_filter_aaaa_check(query_ctx_t *qctx);
+
+static isc_result_t
+query_filter_aaaa(query_ctx_t *qctx);
+
+static isc_result_t
+query_filter_aaaa_any(query_ctx_t *qctx);
+
+static isc_result_t
+query_filter_aaaa_additional(query_ctx_t *qctx);
+
+/*
+ * XXX:
+ * This is a temporary hooks table, pre-populated with pointers to
+ * the functions implementing filter-aaaa. Later, this will be
+ * redesigned to be set up at initialization time when the
+ * filter-aaaa module is loaded. To activate this hooks table
+ * at runtime, call ns__query_inithooks().
+ */
+static bool
+filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp);
+
+static bool
+filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp);
+
+static bool
+filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp);
+
+static bool
+filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp);
+
+ns_hook_t filter_respbegin = {
+       .callback = filter_respond_begin,
+       .callback_data = NULL,
+       .link = { (void *) -1, (void *) -1 },
+};
+ns_hook_t filter_respanyfound = {
+       .callback = filter_respond_any_found,
+       .callback_data = NULL,
+       .link = { (void *) -1, (void *) -1 },
+};
+ns_hook_t filter_prepresp = {
+       .callback = filter_prep_response_begin,
+       .callback_data = NULL,
+       .link = { (void *) -1, (void *) -1 },
+};
+ns_hook_t filter_donesend = {
+       .callback = filter_query_done_send,
+       .callback_data = NULL,
+       .link = { (void *) -1, (void *) -1 },
+};
+
 /*%
  * Increment query statistics counters.
  */
@@ -1876,8 +1933,6 @@ query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) {
        }
 
        if (qtype == dns_rdatatype_a) {
-               bool have_a = false;
-
                /*
                 * We now go looking for A and AAAA records, along with
                 * their signatures.
@@ -1923,8 +1978,6 @@ query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) {
                } else if (result == ISC_R_SUCCESS) {
                        bool invalid = false;
                        mname = NULL;
-
-                       have_a = true;
                        if (additionaltype ==
                            dns_rdatasetadditional_fromcache &&
                            (DNS_TRUST_PENDING(rdataset->trust) ||
@@ -1999,17 +2052,6 @@ query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) {
                } else if (result == ISC_R_SUCCESS) {
                        bool invalid = false;
                        mname = NULL;
-                       /*
-                        * There's an A; check whether we're filtering AAAA
-                        */
-                       if (have_a &&
-                           (qctx->filter_aaaa == dns_aaaa_break_dnssec ||
-                           (qctx->filter_aaaa == dns_aaaa_filter &&
-                            (!WANTDNSSEC(client) || sigrdataset == NULL ||
-                             !dns_rdataset_isassociated(sigrdataset)))))
-                       {
-                               goto addname;
-                       }
 
                        if (additionaltype ==
                            dns_rdatasetadditional_fromcache &&
@@ -2165,21 +2207,14 @@ query_additional(query_ctx_t *qctx, dns_rdataset_t *rdataset) {
        {
                isc_result_t result;
                ns_dbversion_t *dbversion;
-               unsigned int options = 0;
 
                dbversion = query_findversion(client, client->query.gluedb);
                if (dbversion == NULL) {
                        goto regular;
                }
 
-               if (qctx->filter_aaaa == dns_aaaa_filter ||
-                   qctx->filter_aaaa == dns_aaaa_break_dnssec)
-               {
-                       options |= DNS_RDATASETADDGLUE_FILTERAAAA;
-               }
-
                result = dns_rdataset_addglue(rdataset, dbversion->version,
-                                             options, client->message);
+                                             client->message);
                if (result == ISC_R_SUCCESS) {
                        return;
                }
@@ -4518,7 +4553,6 @@ static dns_name_t rfc1918names[] = {
        DNS_NAME_INITABSOLUTE(inaddr168192, inaddr192_offsets)
 };
 
-
 static unsigned char prisoner_data[] = "\010prisoner\004iana\003org";
 static unsigned char hostmaster_data[] = "\012hostmaster\014root-servers\003org";
 
@@ -6946,21 +6980,10 @@ query_addnoqnameproof(query_ctx_t *qctx) {
  */
 static isc_result_t
 query_respond_any(query_ctx_t *qctx) {
-       dns_name_t *tname;
-       int rdatasets_found = 0;
+       bool found = false;
        dns_rdatasetiter_t *rdsiter = NULL;
        isc_result_t result;
        dns_rdatatype_t onetype = 0;    /* type to use for minimal-any */
-       bool have_aaaa, have_a, have_sig;
-
-       /*
-        * If we are not authoritative, assume there is an A record
-        * even in if it is not in our cache.  This assumption could
-        * be wrong but it is a good bet.
-        */
-       have_aaaa = false;
-       have_a = !qctx->authoritative;
-       have_sig = false;
 
        PROCESS_HOOK(NS_QUERY_RESPOND_ANY_BEGIN, qctx);
 
@@ -6985,21 +7008,11 @@ query_respond_any(query_ctx_t *qctx) {
         * cleanup qctx->fname even though we're using it!
         */
        query_keepname(qctx->client, qctx->fname, qctx->dbuf);
-       tname = qctx->fname;
+       qctx->tname = qctx->fname;
 
        result = dns_rdatasetiter_first(rdsiter);
        while (result == ISC_R_SUCCESS) {
                dns_rdatasetiter_current(rdsiter, qctx->rdataset);
-               /*
-                * Notice the presence of A and AAAAs so
-                * that AAAAs can be hidden from IPv4 clients.
-                */
-               if (qctx->filter_aaaa != dns_aaaa_ok) {
-                       if (qctx->rdataset->type == dns_rdatatype_aaaa)
-                               have_aaaa = true;
-                       else if (qctx->rdataset->type == dns_rdatatype_a)
-                               have_a = true;
-               }
 
                /*
                 * We found an NS RRset; no need to add one later.
@@ -7047,9 +7060,6 @@ query_respond_any(query_ctx_t *qctx) {
                            qctx->rdataset->type == qctx->qtype) &&
                           qctx->rdataset->type != 0)
                {
-                       if (dns_rdatatype_isdnssec(qctx->rdataset->type))
-                               have_sig = true;
-
                        if (NOQNAME(qctx->rdataset) && WANTDNSSEC(qctx->client))
                        {
                                qctx->noqname = qctx->rdataset;
@@ -7067,7 +7077,7 @@ query_respond_any(query_ctx_t *qctx) {
                                dns_name_t *name;
                                name = (qctx->fname != NULL)
                                        ? qctx->fname
-                                       : tname;
+                                       : qctx->tname;
                                query_prefetch(qctx->client, name,
                                               qctx->rdataset);
                        }
@@ -7078,29 +7088,32 @@ query_respond_any(query_ctx_t *qctx) {
                         */
                        if (qctx->rdataset->type == dns_rdatatype_sig ||
                            qctx->rdataset->type == dns_rdatatype_rrsig)
+                       {
                                onetype = qctx->rdataset->covers;
-                       else
+                       } else {
                                onetype = qctx->rdataset->type;
+                       }
 
                        query_addrrset(qctx,
                                       (qctx->fname != NULL)
                                        ? &qctx->fname
-                                       : &tname,
+                                       : &qctx->tname,
                                       &qctx->rdataset, NULL,
                                       NULL, DNS_SECTION_ANSWER);
 
                        query_addnoqnameproof(qctx);
 
-                       rdatasets_found++;
-                       INSIST(tname != NULL);
+                       found = true;
+                       INSIST(qctx->tname != NULL);
 
                        /*
                         * rdataset is non-NULL only in certain
                         * pathological cases involving DNAMEs.
                         */
-                       if (qctx->rdataset != NULL)
+                       if (qctx->rdataset != NULL) {
                                query_putrdataset(qctx->client,
                                                  &qctx->rdataset);
+                       }
 
                        qctx->rdataset = query_newrdataset(qctx->client);
                        if (qctx->rdataset == NULL)
@@ -7115,36 +7128,40 @@ query_respond_any(query_ctx_t *qctx) {
                result = dns_rdatasetiter_next(rdsiter);
        }
 
-       PROCESS_HOOK(NS_QUERY_RESPOND_ANY_POST_LOOKUP, qctx);
+       dns_rdatasetiter_destroy(&rdsiter);
 
-       /*
-        * Filter AAAAs if there is an A and there is no signature
-        * or we are supposed to break DNSSEC.
-        */
-       if (qctx->filter_aaaa == dns_aaaa_break_dnssec)
-               qctx->client->attributes |= NS_CLIENTATTR_FILTER_AAAA;
-       else if (qctx->filter_aaaa != dns_aaaa_ok &&
-                have_aaaa && have_a &&
-                (!have_sig || !WANTDNSSEC(qctx->client)))
-                 qctx->client->attributes |= NS_CLIENTATTR_FILTER_AAAA;
+       if (result != ISC_R_NOMORE) {
+               CCTRACE(ISC_LOG_ERROR,
+                      "query_respond_any: rdataset iterator failed");
+               QUERY_ERROR(qctx, DNS_R_SERVFAIL);
+       }
+
+       if (found) {
+               PROCESS_HOOK(NS_QUERY_RESPOND_ANY_FOUND, qctx);
+
+               if (qctx->fname != NULL) {
+                       dns_message_puttempname(qctx->client->message,
+                                               &qctx->fname);
+               }
+       } else {
+               PROCESS_HOOK(NS_QUERY_RESPOND_ANY_NOT_FOUND, qctx);
 
-       if (qctx->fname != NULL)
-               dns_message_puttempname(qctx->client->message, &qctx->fname);
+               if (qctx->fname != NULL) {
+                       dns_message_puttempname(qctx->client->message,
+                                               &qctx->fname);
+               }
 
-       if (rdatasets_found == 0) {
                /*
                 * No matching rdatasets found in cache. If we were
                 * searching for RRSIG/SIG, that's probably okay;
                 * otherwise this is an error condition.
                 */
-               if ((qctx->qtype == dns_rdatatype_rrsig ||
-                    qctx->qtype == dns_rdatatype_sig) &&
-                   result == ISC_R_NOMORE)
+               if (qctx->qtype == dns_rdatatype_rrsig ||
+                    qctx->qtype == dns_rdatatype_sig)
                {
                        isc_buffer_t b;
                        if (!qctx->is_zone) {
                                qctx->authoritative = false;
-                               dns_rdatasetiter_destroy(&rdsiter);
                                qctx->client->attributes &= ~NS_CLIENTATTR_RA;
                                query_addauth(qctx);
                                return (query_done(qctx));
@@ -7164,7 +7181,6 @@ query_respond_any(query_ctx_t *qctx) {
                                              namebuf);
                        }
 
-                       dns_rdatasetiter_destroy(&rdsiter);
                        qctx->fname = query_newname(qctx->client,
                                                    qctx->dbuf, &b);
                        return (query_sign_nodata(qctx));
@@ -7176,14 +7192,7 @@ query_respond_any(query_ctx_t *qctx) {
                }
        }
 
-       dns_rdatasetiter_destroy(&rdsiter);
-       if (result != ISC_R_NOMORE) {
-               CCTRACE(ISC_LOG_ERROR,
-                      "query_respond_any: dns_rdatasetiter_destroy failed");
-               QUERY_ERROR(qctx, result);
-       } else {
-               query_addauth(qctx);
-       }
+       query_addauth(qctx);
 
        return (query_done(qctx));
 }
@@ -7242,101 +7251,6 @@ query_getexpire(query_ctx_t *qctx) {
        }
 }
 
-/*
- * Optionally hide AAAAs from IPv4 clients if there is an A.
- *
- * We add the AAAAs now, but might refuse to render them later
- * after DNSSEC is figured out.
- *
- * This could be more efficient, but the whole idea is
- * so fundamentally wrong, unavoidably inaccurate, and
- * unneeded that it is best to keep it as short as possible.
- */
-static isc_result_t
-query_filter_aaaa(query_ctx_t *qctx) {
-       isc_result_t result;
-
-       if (qctx->filter_aaaa != dns_aaaa_break_dnssec &&
-           (qctx->filter_aaaa != dns_aaaa_filter ||
-            (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
-             dns_rdataset_isassociated(qctx->sigrdataset))))
-       {
-               return (ISC_R_COMPLETE);
-       }
-
-       if (qctx->qtype == dns_rdatatype_aaaa) {
-               dns_rdataset_t *trdataset;
-               trdataset = query_newrdataset(qctx->client);
-               result = dns_db_findrdataset(qctx->db, qctx->node,
-                                            qctx->version,
-                                            dns_rdatatype_a, 0,
-                                            qctx->client->now,
-                                            trdataset, NULL);
-               if (dns_rdataset_isassociated(trdataset)) {
-                       dns_rdataset_disassociate(trdataset);
-               }
-               query_putrdataset(qctx->client, &trdataset);
-
-               /*
-                * We have an AAAA but the A is not in our cache.
-                * Assume any result other than DNS_R_DELEGATION
-                * or ISC_R_NOTFOUND means there is no A and
-                * so AAAAs are ok.
-                *
-                * Assume there is no A if we can't recurse
-                * for this client, although that could be
-                * the wrong answer. What else can we do?
-                * Besides, that we have the AAAA and are using
-                * this mechanism suggests that we care more
-                * about As than AAAAs and would have cached
-                * the A if it existed.
-                */
-               if (result == ISC_R_SUCCESS) {
-                       qctx->client->attributes |=
-                                   NS_CLIENTATTR_FILTER_AAAA;
-
-               } else if (qctx->authoritative ||
-                          !RECURSIONOK(qctx->client) ||
-                          (result != DNS_R_DELEGATION &&
-                           result != ISC_R_NOTFOUND))
-               {
-                       qctx->client->attributes &=
-                               ~NS_CLIENTATTR_FILTER_AAAA;
-               } else {
-                       /*
-                        * This is an ugly kludge to recurse
-                        * for the A and discard the result.
-                        *
-                        * Continue to add the AAAA now.
-                        * We'll make a note to not render it
-                        * if the recursion for the A succeeds.
-                        */
-                       INSIST(!REDIRECT(qctx->client));
-                       result = query_recurse(qctx->client,
-                                       dns_rdatatype_a,
-                                       qctx->client->query.qname,
-                                       NULL, NULL, qctx->resuming);
-                       if (result == ISC_R_SUCCESS) {
-                               qctx->client->attributes |=
-                                       NS_CLIENTATTR_FILTER_AAAA_RC;
-                               qctx->client->query.attributes |=
-                                       NS_QUERYATTR_RECURSING;
-                       }
-               }
-       } else if (qctx->qtype == dns_rdatatype_a &&
-                  (qctx->client->attributes &
-                   NS_CLIENTATTR_FILTER_AAAA_RC) != 0)
-       {
-               qctx->client->attributes &= ~NS_CLIENTATTR_FILTER_AAAA_RC;
-               qctx->client->attributes |= NS_CLIENTATTR_FILTER_AAAA;
-               qctx_clean(qctx);
-
-               return (query_done(qctx));
-       }
-
-       return (ISC_R_COMPLETE);
-}
-
 /*%
  * Build a repsonse for a "normal" query, for a type other than ANY,
  * for which we have an answer (either positive or negative).
@@ -7379,12 +7293,6 @@ query_respond(query_ctx_t *qctx) {
        /*
         * Check to see if the AAAA RRset has non-excluded addresses
         * in it.  If not look for a A RRset.
-        *
-        * Note: the order of dns64_aaaaok() and query_filter_aaaa() is
-        * important.  query_filter_aaaa() calls query_recurse() but
-        * continues so that the AAAA records are added.  If the
-        * order is reversed client->query.fetch will be non-NULL
-        * when query_lookup() is called leading to a assertion.
         */
        INSIST(qctx->client->query.dns64_aaaaok == NULL);
 
@@ -7407,10 +7315,6 @@ query_respond(query_ctx_t *qctx) {
                return (query_lookup(qctx));
        }
 
-       result = query_filter_aaaa(qctx);
-       if (result != ISC_R_COMPLETE)
-               return (result);
-
        if (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL) {
                sigrdatasetp = &qctx->sigrdataset;
        }
@@ -9268,12 +9172,6 @@ query_coveringnsec(query_ctx_t *qctx) {
                if (qctx->type == dns_rdatatype_any) {  /* XXX not yet */
                        goto cleanup;
                }
-               if (qctx->filter_aaaa != dns_aaaa_ok &&
-                   (qctx->type == dns_rdatatype_a ||
-                    qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
-               {
-                       goto cleanup;
-               }
                if (!ISC_LIST_EMPTY(qctx->client->view->dns64) &&
                    (qctx->type == dns_rdatatype_a ||
                     qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
@@ -9338,12 +9236,6 @@ query_coveringnsec(query_ctx_t *qctx) {
                if (qctx->type == dns_rdatatype_any) {  /* XXX not yet */
                        goto cleanup;
                }
-               if (qctx->filter_aaaa != dns_aaaa_ok &&
-                   (qctx->type == dns_rdatatype_a ||
-                    qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
-               {
-                       goto cleanup;
-               }
                if (!ISC_LIST_EMPTY(qctx->client->view->dns64) &&
                    (qctx->type == dns_rdatatype_a ||
                     qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
@@ -9865,8 +9757,8 @@ query_addcname(query_ctx_t *qctx, dns_trust_t trust, dns_ttl_t ttl) {
 
 /*%
  * Prepare to respond: determine whether a wildcard proof is needed,
- * check whether to filter AAAA answers, then hand off to query_respond()
- * or (for type ANY queries) query_respond_any().
+ * then hand off to query_respond() or (for type ANY queries)
+ * query_respond_any().
  */
 static isc_result_t
 query_prepresponse(query_ctx_t *qctx) {
@@ -9881,33 +9773,6 @@ query_prepresponse(query_ctx_t *qctx) {
                qctx->need_wildcardproof = true;
        }
 
-       /*
-        * The filter-aaaa-on-v4 option should suppress AAAAs for IPv4
-        * clients if there is an A; filter-aaaa-on-v6 option does the same
-        * for IPv6 clients.
-        */
-       qctx->filter_aaaa = dns_aaaa_ok;
-       if (qctx->client->view->v4_aaaa != dns_aaaa_ok ||
-           qctx->client->view->v6_aaaa != dns_aaaa_ok)
-       {
-               isc_result_t result;
-               result = ns_client_checkaclsilent(qctx->client, NULL,
-                                                 qctx->client->view->aaaa_acl,
-                                                 true);
-               if (result == ISC_R_SUCCESS &&
-                   qctx->client->view->v4_aaaa != dns_aaaa_ok &&
-                   is_v4_client(qctx->client))
-               {
-                       qctx->filter_aaaa = qctx->client->view->v4_aaaa;
-               } else if (result == ISC_R_SUCCESS &&
-                          qctx->client->view->v6_aaaa != dns_aaaa_ok &&
-                          is_v6_client(qctx->client))
-               {
-                       qctx->filter_aaaa = qctx->client->view->v6_aaaa;
-               }
-       }
-
-
        if (qctx->type == dns_rdatatype_any) {
                return (query_respond_any(qctx));
        } else {
@@ -10783,6 +10648,7 @@ query_glueanswer(query_ctx_t *qctx) {
 static isc_result_t
 query_done(query_ctx_t *qctx) {
        const dns_namelist_t *secs = qctx->client->message->sections;
+
        CCTRACE(ISC_LOG_DEBUG(3), "query_done");
 
        PROCESS_HOOK(NS_QUERY_DONE_BEGIN, qctx);
@@ -11284,3 +11150,371 @@ ns_query_start(ns_client_t *client) {
        ns_client_attach(client, &qclient);
        (void)query_setup(qclient, qtype);
 }
+
+/*
+ * Per-client flags set by this module
+ */
+#define FILTER_AAAA_RECURSING  0x0001  /* Recursing for A */
+#define FILTER_AAAA_FILTERED   0x0002  /* AAAA was removed from answer */
+
+/*
+ * The filter-aaaa-on-v4 option suppresses AAAAs for IPv4
+ * clients if there is an A; filter-aaaa-on-v6 option does
+ * the same for IPv6 clients.
+ */
+static isc_result_t
+query_filter_aaaa_check(query_ctx_t *qctx) {
+       qctx->filter_aaaa = dns_aaaa_ok;
+       if (qctx->client->view->v4_aaaa != dns_aaaa_ok ||
+           qctx->client->view->v6_aaaa != dns_aaaa_ok)
+       {
+               isc_result_t result;
+               result = ns_client_checkaclsilent(qctx->client, NULL,
+                                                 qctx->client->view->aaaa_acl,
+                                                 true);
+               if (result == ISC_R_SUCCESS &&
+                   qctx->client->view->v4_aaaa != dns_aaaa_ok &&
+                   is_v4_client(qctx->client))
+               {
+                       qctx->filter_aaaa = qctx->client->view->v4_aaaa;
+               } else if (result == ISC_R_SUCCESS &&
+                          qctx->client->view->v6_aaaa != dns_aaaa_ok &&
+                          is_v6_client(qctx->client))
+               {
+                       qctx->filter_aaaa = qctx->client->view->v6_aaaa;
+               }
+       }
+
+       return (ISC_R_COMPLETE);
+}
+
+/*
+ * Optionally hide AAAA rrsets if there is a matching A.
+ * (This version is for processing answers to explicit AAAA
+ * queries; ANY queries are handled in query_filter_aaaa_any().)
+ */
+static isc_result_t
+query_filter_aaaa(query_ctx_t *qctx) {
+       isc_result_t result;
+
+       if (qctx->filter_aaaa != dns_aaaa_break_dnssec &&
+           (qctx->filter_aaaa != dns_aaaa_filter ||
+            (WANTDNSSEC(qctx->client) && qctx->sigrdataset != NULL &&
+             dns_rdataset_isassociated(qctx->sigrdataset))))
+       {
+               return (ISC_R_COMPLETE);
+       }
+
+       if (qctx->qtype == dns_rdatatype_aaaa) {
+               dns_rdataset_t *trdataset;
+               trdataset = query_newrdataset(qctx->client);
+               result = dns_db_findrdataset(qctx->db, qctx->node,
+                                            qctx->version,
+                                            dns_rdatatype_a, 0,
+                                            qctx->client->now,
+                                            trdataset, NULL);
+               if (dns_rdataset_isassociated(trdataset)) {
+                       dns_rdataset_disassociate(trdataset);
+               }
+               query_putrdataset(qctx->client, &trdataset);
+
+               /*
+                * We found an AAAA. If we also found an A, then the AAAA
+                * must not be rendered.
+                *
+                * If the A is not in our cache, then any result other than
+                * DNS_R_DELEGATION or ISC_R_NOTFOUND means there is no A,
+                * and so AAAAs are okay.
+                *
+                * We assume there is no A if we can't recurse for this
+                * client. That might be the wrong answer, but what else
+                * can we do?  Besides, the fact that we have the AAAA and
+                * are using this mechanism in the first place suggests
+                * that we care more about As than AAAAs, and would have
+                * cached an A if it existed.
+                */
+               if (result == ISC_R_SUCCESS) {
+                       qctx->rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
+                       if (qctx->sigrdataset != NULL &&
+                           dns_rdataset_isassociated(qctx->sigrdataset))
+                       {
+                               qctx->sigrdataset->attributes |=
+                                       DNS_RDATASETATTR_RENDERED;
+                       }
+                       qctx->client->hookflags |= FILTER_AAAA_FILTERED;
+               } else if (!qctx->authoritative &&
+                          RECURSIONOK(qctx->client) &&
+                          (result == DNS_R_DELEGATION ||
+                           result == ISC_R_NOTFOUND))
+               {
+                       /*
+                        * This is an ugly kludge to recurse
+                        * for the A and discard the result.
+                        *
+                        * Continue to add the AAAA now.
+                        * We'll make a note to not render it
+                        * if the recursion for the A succeeds.
+                        */
+                       INSIST(!REDIRECT(qctx->client));
+                       result = query_recurse(qctx->client,
+                                              dns_rdatatype_a,
+                                              qctx->client->query.qname,
+                                              NULL, NULL, qctx->resuming);
+                       if (result == ISC_R_SUCCESS) {
+                               qctx->client->hookflags |=
+                                       FILTER_AAAA_RECURSING;
+                               qctx->client->query.attributes |=
+                                       NS_QUERYATTR_RECURSING;
+                       }
+               }
+       } else if (qctx->qtype == dns_rdatatype_a &&
+                  ((qctx->client->hookflags & FILTER_AAAA_RECURSING) != 0))
+       {
+
+               dns_rdataset_t *mrdataset = NULL;
+               dns_rdataset_t *sigrdataset = NULL;
+
+               result = dns_message_findname(qctx->client->message,
+                                             DNS_SECTION_ANSWER, qctx->fname,
+                                             dns_rdatatype_aaaa, 0,
+                                             NULL, &mrdataset);
+               if (result == ISC_R_SUCCESS) {
+                       mrdataset->attributes |= DNS_RDATASETATTR_RENDERED;
+               }
+
+               result = dns_message_findname(qctx->client->message,
+                                             DNS_SECTION_ANSWER, qctx->fname,
+                                             dns_rdatatype_rrsig,
+                                             dns_rdatatype_aaaa,
+                                             NULL, &sigrdataset);
+               if (result == ISC_R_SUCCESS) {
+                       sigrdataset->attributes |= DNS_RDATASETATTR_RENDERED;
+               }
+
+               qctx->client->hookflags &= ~FILTER_AAAA_RECURSING;
+
+               return (query_done(qctx));
+       }
+
+       return (ISC_R_COMPLETE);
+}
+
+/*
+ * Optionally hide AAAA rrsets if there is a matching A.
+ * (This version is for processing answers to ANY queries;
+ * explicit AAAA queries are handled in query_filter_aaaa().)
+ */
+static isc_result_t
+query_filter_aaaa_any(query_ctx_t *qctx) {
+       dns_name_t *name = NULL;
+       dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL;
+       dns_rdataset_t *a = NULL;
+       bool have_a = true;
+
+       if (qctx->filter_aaaa == dns_aaaa_ok) {
+               return (ISC_R_COMPLETE);
+       }
+
+       dns_message_findname(qctx->client->message, DNS_SECTION_ANSWER,
+                            (qctx->fname != NULL)
+                             ? qctx->fname
+                             : qctx->tname,
+                            dns_rdatatype_any, 0, &name, NULL);
+
+       /*
+        * If we're not authoritative, just assume there's an
+        * A even if it wasn't in the cache and therefore isn't
+        * in the message.  But if we're authoritative, then
+        * if there was an A, it should be here.
+        */
+       if (qctx->authoritative && name != NULL) {
+               dns_message_findtype(name, dns_rdatatype_a, 0, &a);
+               if (a == NULL) {
+                       have_a = false;
+               }
+       }
+
+       if (name != NULL) {
+               dns_message_findtype(name, dns_rdatatype_aaaa, 0, &aaaa);
+               dns_message_findtype(name, dns_rdatatype_rrsig,
+                                    dns_rdatatype_aaaa, &aaaa_sig);
+       }
+
+       if (have_a && aaaa != NULL &&
+           (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
+            qctx->filter_aaaa == dns_aaaa_break_dnssec))
+       {
+               aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
+               if (aaaa_sig != NULL) {
+                       aaaa_sig->attributes |= DNS_RDATASETATTR_RENDERED;
+               }
+       }
+
+       return (ISC_R_COMPLETE);
+}
+
+/*
+ * Hide AAAA rrsets in the additional section if there is a matching A,
+ * and hide NS in the additional section if AAAA was filtered in the answer
+ * section.
+ */
+static isc_result_t
+query_filter_aaaa_additional(query_ctx_t *qctx) {
+       isc_result_t result;
+
+       if (qctx->filter_aaaa == dns_aaaa_ok) {
+               return (ISC_R_COMPLETE);
+       }
+
+       result = dns_message_firstname(qctx->client->message,
+                                      DNS_SECTION_ADDITIONAL);
+       while (result == ISC_R_SUCCESS) {
+               dns_name_t *name = NULL;
+               dns_rdataset_t *aaaa = NULL, *aaaa_sig = NULL;
+               dns_rdataset_t *a = NULL;
+
+               dns_message_currentname(qctx->client->message,
+                                       DNS_SECTION_ADDITIONAL,
+                                       &name);
+
+               result = dns_message_nextname(qctx->client->message,
+                                             DNS_SECTION_ADDITIONAL);
+
+               dns_message_findtype(name, dns_rdatatype_a, 0, &a);
+               if (a == NULL) {
+                       continue;
+               }
+
+               dns_message_findtype(name, dns_rdatatype_aaaa, 0,
+                                    &aaaa);
+               if (aaaa == NULL) {
+                       continue;
+               }
+
+               dns_message_findtype(name, dns_rdatatype_rrsig,
+                                    dns_rdatatype_aaaa, &aaaa_sig);
+
+               if (aaaa_sig == NULL || !WANTDNSSEC(qctx->client) ||
+                    qctx->filter_aaaa == dns_aaaa_break_dnssec)
+               {
+                       aaaa->attributes |= DNS_RDATASETATTR_RENDERED;
+                       if (aaaa_sig != NULL) {
+                               aaaa_sig->attributes |=
+                                       DNS_RDATASETATTR_RENDERED;
+                       }
+               }
+       }
+
+       if ((qctx->client->hookflags & FILTER_AAAA_FILTERED) != 0) {
+               result = dns_message_firstname(qctx->client->message,
+                                              DNS_SECTION_AUTHORITY);
+               while (result == ISC_R_SUCCESS) {
+                       dns_name_t *name = NULL;
+                       dns_rdataset_t *ns = NULL, *ns_sig = NULL;
+
+                       dns_message_currentname(qctx->client->message,
+                                               DNS_SECTION_AUTHORITY,
+                                               &name);
+
+                       result = dns_message_findtype(name, dns_rdatatype_ns,
+                                                     0, &ns);
+                       if (result == ISC_R_SUCCESS) {
+                               ns->attributes |= DNS_RDATASETATTR_RENDERED;
+                       }
+
+                       result = dns_message_findtype(name, dns_rdatatype_rrsig,
+                                                     dns_rdatatype_ns,
+                                                     &ns_sig);
+                       if (result == ISC_R_SUCCESS) {
+                               ns_sig->attributes |= DNS_RDATASETATTR_RENDERED;
+                       }
+
+                       result = dns_message_nextname(qctx->client->message,
+                                                     DNS_SECTION_AUTHORITY);
+               }
+       }
+
+       return (ISC_R_COMPLETE);
+}
+
+static bool
+filter_respond_begin(void *hookdata, void *cbdata, isc_result_t *resp) {
+       isc_result_t result;
+
+       UNUSED(cbdata);
+
+       result = query_filter_aaaa((query_ctx_t *) hookdata);
+       if (result != ISC_R_COMPLETE) {
+               *resp = result;
+               return (true);
+       }
+
+       *resp = ISC_R_SUCCESS;
+       return (false);
+}
+
+static bool
+filter_respond_any_found(void *hookdata, void *cbdata, isc_result_t *resp) {
+       isc_result_t result;
+
+       UNUSED(cbdata);
+
+       result = query_filter_aaaa_any((query_ctx_t *) hookdata);
+       if (result != ISC_R_COMPLETE) {
+               *resp = result;
+               return (true);
+       }
+
+       *resp = ISC_R_SUCCESS;
+       return (false);
+}
+
+static bool
+filter_prep_response_begin(void *hookdata, void *cbdata, isc_result_t *resp) {
+       isc_result_t result;
+
+       UNUSED(cbdata);
+
+       result = query_filter_aaaa_check((query_ctx_t *) hookdata);
+       if (result != ISC_R_COMPLETE) {
+               *resp = result;
+               return (true);
+       }
+
+       *resp = ISC_R_SUCCESS;
+       return (false);
+}
+
+static bool
+filter_query_done_send(void *hookdata, void *cbdata, isc_result_t *resp) {
+       isc_result_t result;
+
+       UNUSED(cbdata);
+
+       result = query_filter_aaaa_additional((query_ctx_t *) hookdata);
+       if (result != ISC_R_COMPLETE) {
+               *resp = result;
+               return (true);
+       }
+
+       *resp = ISC_R_SUCCESS;
+       return (false);
+}
+
+void
+ns__query_inithooks() {
+       /*
+        * XXX: This function is temporary.  Later, the hook table
+        * will be set up when initializing hook modules after
+        * configuring the server.
+        *
+        * For now, however, we just call this once when initializing named
+        * and it will set up all the filter-aaaa hooks.
+        */
+
+       ns_hooktable_init(NULL);
+       ns_hook_add(NULL, NS_QUERY_RESPOND_BEGIN, &filter_respbegin);
+       ns_hook_add(NULL, NS_QUERY_RESPOND_ANY_FOUND, &filter_respanyfound);
+       ns_hook_add(NULL, NS_QUERY_PREP_RESPONSE_BEGIN, &filter_prepresp);
+       ns_hook_add(NULL, NS_QUERY_DONE_SEND, &filter_donesend);
+}
index 828ce344e8ab0aabd05b322818f35246623eac1e..fde2c459d5a2b948f56e21170d3da6c597248e45 100644 (file)
@@ -22,6 +22,7 @@
 #include <dns/tkey.h>
 #include <dns/stats.h>
 
+#include <ns/query.h>
 #include <ns/server.h>
 #include <ns/stats.h>
 
@@ -107,6 +108,11 @@ ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview,
 
        ISC_LIST_INIT(sctx->altsecrets);
 
+       /*
+        * XXX: temporary.
+        */
+       ns__query_inithooks();
+
        sctx->magic = SCTX_MAGIC;
        *sctxp = sctx;