diff --git a/bin/tests/system/filter-aaaa/tests.sh b/bin/tests/system/filter-aaaa/tests.sh index 16a9332518..f3e06b4fe4 100644 --- a/bin/tests/system/filter-aaaa/tests.sh +++ b/bin/tests/system/filter-aaaa/tests.sh @@ -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 diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h index b7b28c4ed7..4f77471e79 100644 --- a/lib/dns/include/dns/db.h +++ b/lib/dns/include/dns/db.h @@ -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'. * diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index 28290e0be5..6f389aa2df 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -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; diff --git a/lib/dns/include/dns/rdataset.h b/lib/dns/include/dns/rdataset.h index f48e32d55c..6a07d214c9 100644 --- a/lib/dns/include/dns/rdataset.h +++ b/lib/dns/include/dns/rdataset.h @@ -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: diff --git a/lib/dns/message.c b/lib/dns/message.c index 4a811d28db..f584700628 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -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; diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 2f5db4fa7b..22dd60519e 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -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); diff --git a/lib/dns/rdataset.c b/lib/dns/rdataset.c index d6dcc91b48..576e9475b5 100644 --- a/lib/dns/rdataset.c +++ b/lib/dns/rdataset.c @@ -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)); } diff --git a/lib/ns/client.c b/lib/ns/client.c index 3f15e5f76d..1100db7ffc 100644 --- a/lib/ns/client.c +++ b/lib/ns/client.c @@ -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, diff --git a/lib/ns/include/ns/client.h b/lib/ns/include/ns/client.h index 10e47fe53a..d855cb7a18 100644 --- a/lib/ns/include/ns/client.h +++ b/lib/ns/include/ns/client.h @@ -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 */ diff --git a/lib/ns/include/ns/hooks.h b/lib/ns/include/ns/hooks.h index 0475227750..aa6d8d1548 100644 --- a/lib/ns/include/ns/hooks.h +++ b/lib/ns/include/ns/hooks.h @@ -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; diff --git a/lib/ns/include/ns/query.h b/lib/ns/include/ns/query.h index ec07d2821a..2c67e7182b 100644 --- a/lib/ns/include/ns/query.h +++ b/lib/ns/include/ns/query.h @@ -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 */ diff --git a/lib/ns/query.c b/lib/ns/query.c index d0b079b75f..64e2c6483c 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -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 (qctx->fname != NULL) - dns_message_puttempname(qctx->client->message, &qctx->fname); + 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 (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); +} diff --git a/lib/ns/server.c b/lib/ns/server.c index 828ce344e8..fde2c459d5 100644 --- a/lib/ns/server.c +++ b/lib/ns/server.c @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -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;