]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Fix invalid dns message state in resolver's logic
authorDiego Fronza <diego@isc.org>
Mon, 21 Sep 2020 20:32:39 +0000 (17:32 -0300)
committerOndřej Surý <ondrej@sury.org>
Tue, 29 Sep 2020 06:22:08 +0000 (08:22 +0200)
The assertion failure REQUIRE(msg->state == DNS_SECTION_ANY),
caused by calling dns_message_setclass within function resquery_response()
in resolver.c, was happening due to wrong management of dns message_t
objects used to process responses to the queries issued by the resolver.

Before the fix, a resolver's fetch context (fetchctx_t) would hold
a pointer to the message, this same reference would then be used over all
the attempts to resolve the query, trying next server, etc... for this to work
the message object would have it's state reset between each iteration, marking
it as ready for a new processing.

The problem arose in a scenario with many different forwarders configured,
managing the state of the dns_message_t object was lacking better
synchronization, which have led it to a invalid dns_message_t state in
resquery_response().

Instead of adding unnecessarily complex code to synchronize the object,
the dns_message_t object was moved from fetchctx_t structure to the
query structure, where it better belongs to, since each query will produce
a response, this way whenever a new query is created an associated
dns_messate_t is also created.

This commit deals mainly with moving the dns_message_t object from fetchctx_t
to the query structure.

lib/dns/resolver.c

index 5f5cafce7bc72ae8a5027ebf916bda769c6e8c7f..973c7d3bb3cc657678278a7799ab7c2a6b3d770e 100644 (file)
@@ -21,6 +21,7 @@
 #include <isc/platform.h>
 #include <isc/print.h>
 #include <isc/random.h>
+#include <isc/refcount.h>
 #include <isc/siphash.h>
 #include <isc/socket.h>
 #include <isc/stats.h>
@@ -218,6 +219,7 @@ typedef struct query {
        /* Locked by task event serialization. */
        unsigned int magic;
        fetchctx_t *fctx;
+       dns_message_t *rmessage;
        isc_mem_t *mctx;
        dns_dispatchmgr_t *dispatchmgr;
        dns_dispatch_t *dispatch;
@@ -303,7 +305,6 @@ struct fetchctx {
        isc_time_t expires;
        isc_interval_t interval;
        dns_message_t *qmessage;
-       dns_message_t *rmessage;
        ISC_LIST(resquery_t) queries;
        dns_adbfindlist_t finds;
        dns_adbfind_t *find;
@@ -438,6 +439,7 @@ struct fetchctx {
 typedef struct {
        dns_adbaddrinfo_t *addrinfo;
        fetchctx_t *fctx;
+       dns_message_t *message;
 } dns_valarg_t;
 
 struct dns_fetch {
@@ -628,11 +630,11 @@ validated(isc_task_t *task, isc_event_t *event);
 static bool
 maybe_destroy(fetchctx_t *fctx, bool locked);
 static void
-add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason,
-       badnstype_t badtype);
+add_bad(fetchctx_t *fctx, dns_message_t *rmessage, dns_adbaddrinfo_t *addrinfo,
+       isc_result_t reason, badnstype_t badtype);
 static inline isc_result_t
-findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
-           dns_name_t **noqname);
+findnoqname(fetchctx_t *fctx, dns_message_t *message, dns_name_t *name,
+           dns_rdatatype_t type, dns_name_t **noqname);
 static void
 fctx_increference(fetchctx_t *fctx);
 static bool
@@ -880,8 +882,8 @@ dec_stats(dns_resolver_t *res, isc_statscounter_t counter) {
 }
 
 static isc_result_t
-valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
-         dns_rdatatype_t type, dns_rdataset_t *rdataset,
+valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
+         dns_name_t *name, dns_rdatatype_t type, dns_rdataset_t *rdataset,
          dns_rdataset_t *sigrdataset, unsigned int valoptions,
          isc_task_t *task) {
        dns_validator_t *validator = NULL;
@@ -892,6 +894,7 @@ valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
 
        valarg->fctx = fctx;
        valarg->addrinfo = addrinfo;
+       valarg->message = message;
 
        if (!ISC_LIST_EMPTY(fctx->validators)) {
                valoptions |= DNS_VALIDATOR_DEFER;
@@ -900,8 +903,8 @@ valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
        }
 
        result = dns_validator_create(fctx->res->view, name, type, rdataset,
-                                     sigrdataset, fctx->rmessage, valoptions,
-                                     task, validated, valarg, &validator);
+                                     sigrdataset, message, valoptions, task,
+                                     validated, valarg, &validator);
        if (result == ISC_R_SUCCESS) {
                inc_stats(fctx->res, dns_resstatscounter_val);
                if ((valoptions & DNS_VALIDATOR_DEFER) == 0) {
@@ -1184,6 +1187,10 @@ resquery_destroy(resquery_t **queryp) {
        empty = fctx_decreference(query->fctx);
        UNLOCK(&res->buckets[bucket].lock);
 
+       if (query->rmessage != NULL) {
+               dns_message_detach(&query->rmessage);
+       }
+
        query->magic = 0;
        isc_mem_put(query->mctx, query, sizeof(*query));
 
@@ -1278,8 +1285,8 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
                         */
                        if (fctx->fwdpolicy == dns_fwdpolicy_first &&
                            ISFORWARDER(query->addrinfo)) {
-                               add_bad(fctx, query->addrinfo, ISC_R_TIMEDOUT,
-                                       badns_forwarder);
+                               add_bad(fctx, query->rmessage, query->addrinfo,
+                                       ISC_R_TIMEDOUT, badns_forwarder);
                        }
 
                        /*
@@ -1820,8 +1827,8 @@ process_sendevent(resquery_t *query, isc_event_t *event) {
                        /*
                         * No route to remote.
                         */
-                       add_bad(fctx, query->addrinfo, sevent->result,
-                               badns_unreachable);
+                       add_bad(fctx, query->rmessage, query->addrinfo,
+                               sevent->result, badns_unreachable);
                        fctx_cancelquery(&query, NULL, NULL, true, false);
                        retry = true;
                        break;
@@ -2008,9 +2015,10 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 
        INSIST(ISC_LIST_EMPTY(fctx->validators));
 
-       dns_message_reset(fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
-
        query = isc_mem_get(fctx->mctx, sizeof(*query));
+       query->rmessage = NULL;
+       result = dns_message_create(fctx->mctx, DNS_MESSAGE_INTENTPARSE,
+                                   &query->rmessage);
        query->mctx = fctx->mctx;
        query->options = options;
        query->attributes = 0;
@@ -2235,6 +2243,7 @@ cleanup_dispatch:
 cleanup_query:
        if (query->connects == 0) {
                query->magic = 0;
+               dns_message_detach(&query->rmessage);
                isc_mem_put(fctx->mctx, query, sizeof(*query));
        }
 
@@ -3097,8 +3106,8 @@ resquery_connected(isc_task_t *task, isc_event_t *event) {
                         * exceeds 512 bytes from broken servers.
                         */
                        if ((query->options & DNS_FETCHOPT_EDNS512) != 0) {
-                               add_bad(fctx, query->addrinfo, sevent->result,
-                                       badns_unreachable);
+                               add_bad(fctx, query->rmessage, query->addrinfo,
+                                       sevent->result, badns_unreachable);
                        }
                        fctx_cancelquery(&query, NULL, NULL, true, false);
                        retry = true;
@@ -3296,8 +3305,8 @@ mark_bad(fetchctx_t *fctx) {
 }
 
 static void
-add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason,
-       badnstype_t badtype) {
+add_bad(fetchctx_t *fctx, dns_message_t *rmessage, dns_adbaddrinfo_t *addrinfo,
+       isc_result_t reason, badnstype_t badtype) {
        char namebuf[DNS_NAME_FORMATSIZE];
        char addrbuf[ISC_SOCKADDR_FORMATSIZE];
        char classbuf[64];
@@ -3353,20 +3362,19 @@ add_bad(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, isc_result_t reason,
        }
 
        if (reason == DNS_R_UNEXPECTEDRCODE &&
-           fctx->rmessage->rcode == dns_rcode_servfail &&
-           ISFORWARDER(addrinfo))
+           rmessage->rcode == dns_rcode_servfail && ISFORWARDER(addrinfo))
        {
                return;
        }
 
        if (reason == DNS_R_UNEXPECTEDRCODE) {
                isc_buffer_init(&b, code, sizeof(code) - 1);
-               dns_rcode_totext(fctx->rmessage->rcode, &b);
+               dns_rcode_totext(rmessage->rcode, &b);
                code[isc_buffer_usedlength(&b)] = '\0';
                spc = " ";
        } else if (reason == DNS_R_UNEXPECTEDOPCODE) {
                isc_buffer_init(&b, code, sizeof(code) - 1);
-               dns_opcode_totext((dns_opcode_t)fctx->rmessage->opcode, &b);
+               dns_opcode_totext((dns_opcode_t)rmessage->opcode, &b);
                code[isc_buffer_usedlength(&b)] = '\0';
                spc = " ";
        } else {
@@ -4561,7 +4569,6 @@ fctx_destroy(fetchctx_t *fctx) {
        isc_counter_detach(&fctx->qc);
        fcount_decr(fctx);
        isc_timer_detach(&fctx->timer);
-       dns_message_detach(&fctx->rmessage);
        dns_message_detach(&fctx->qmessage);
        if (dns_name_countlabels(&fctx->domain) > 0) {
                dns_name_free(&fctx->domain, fctx->mctx);
@@ -5127,14 +5134,6 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
                goto cleanup_fcount;
        }
 
-       fctx->rmessage = NULL;
-       result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE,
-                                   &fctx->rmessage);
-
-       if (result != ISC_R_SUCCESS) {
-               goto cleanup_qmessage;
-       }
-
        /*
         * Compute an expiration time for the entire fetch.
         */
@@ -5146,7 +5145,7 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
                                 "isc_time_nowplusinterval: %s",
                                 isc_result_totext(iresult));
                result = ISC_R_UNEXPECTED;
-               goto cleanup_rmessage;
+               goto cleanup_qmessage;
        }
 
        /*
@@ -5168,7 +5167,7 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
                UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_timer_create: %s",
                                 isc_result_totext(iresult));
                result = ISC_R_UNEXPECTED;
-               goto cleanup_rmessage;
+               goto cleanup_qmessage;
        }
 
        /*
@@ -5216,9 +5215,6 @@ cleanup_mctx:
        dns_db_detach(&fctx->cache);
        isc_timer_detach(&fctx->timer);
 
-cleanup_rmessage:
-       dns_message_detach(&fctx->rmessage);
-
 cleanup_qmessage:
        dns_message_detach(&fctx->qmessage);
 
@@ -5252,8 +5248,7 @@ cleanup_fetch:
  * Handle Responses
  */
 static inline bool
-is_lame(fetchctx_t *fctx) {
-       dns_message_t *message = fctx->rmessage;
+is_lame(fetchctx_t *fctx, dns_message_t *message) {
        dns_name_t *name;
        dns_rdataset_t *rdataset;
        isc_result_t result;
@@ -5338,9 +5333,8 @@ log_formerr(fetchctx_t *fctx, const char *format, ...) {
 }
 
 static isc_result_t
-same_question(fetchctx_t *fctx) {
+same_question(fetchctx_t *fctx, dns_message_t *message) {
        isc_result_t result;
-       dns_message_t *message = fctx->rmessage;
        dns_name_t *name;
        dns_rdataset_t *rdataset;
 
@@ -5544,12 +5538,15 @@ validated(isc_task_t *task, isc_event_t *event) {
        uint32_t bucketnum;
        dns_fixedname_t fwild;
        dns_name_t *wild = NULL;
+       dns_message_t *message;
 
        UNUSED(task); /* for now */
 
        REQUIRE(event->ev_type == DNS_EVENT_VALIDATORDONE);
        valarg = event->ev_arg;
+       message = valarg->message;
        fctx = valarg->fctx;
+
        REQUIRE(VALID_FCTX(fctx));
        res = fctx->res;
        addrinfo = valarg->addrinfo;
@@ -5693,7 +5690,7 @@ validated(isc_task_t *task, isc_event_t *event) {
                        }
                }
                result = fctx->vresult;
-               add_bad(fctx, addrinfo, result, badns_validation);
+               add_bad(fctx, message, addrinfo, result, badns_validation);
                isc_event_free(&event);
                UNLOCK(&res->buckets[bucketnum].lock);
                INSIST(fctx->validator == NULL);
@@ -5721,6 +5718,7 @@ validated(isc_task_t *task, isc_event_t *event) {
                } else {
                        fctx_try(fctx, true, true); /* Locks bucket. */
                }
+
                return;
        }
 
@@ -5733,9 +5731,8 @@ validated(isc_task_t *task, isc_event_t *event) {
                /*
                 * Cache DS NXDOMAIN separately to other types.
                 */
-               if (fctx->rmessage->rcode == dns_rcode_nxdomain &&
-                   fctx->type != dns_rdatatype_ds)
-               {
+               if (message->rcode == dns_rcode_nxdomain &&
+                   fctx->type != dns_rdatatype_ds) {
                        covers = dns_rdatatype_any;
                } else {
                        covers = fctx->type;
@@ -5759,10 +5756,10 @@ validated(isc_task_t *task, isc_event_t *event) {
                        ttl = 0;
                }
 
-               result = ncache_adderesult(
-                       fctx->rmessage, fctx->cache, node, covers, now,
-                       fctx->res->view->minncachettl, ttl, vevent->optout,
-                       vevent->secure, ardataset, &eresult);
+               result = ncache_adderesult(message, fctx->cache, node, covers,
+                                          now, fctx->res->view->minncachettl,
+                                          ttl, vevent->optout, vevent->secure,
+                                          ardataset, &eresult);
                if (result != ISC_R_SUCCESS) {
                        goto noanswer_response;
                }
@@ -5791,7 +5788,7 @@ validated(isc_task_t *task, isc_event_t *event) {
        {
                isc_result_t tresult;
                dns_name_t *noqname = NULL;
-               tresult = findnoqname(fctx, vevent->name,
+               tresult = findnoqname(fctx, message, vevent->name,
                                      vevent->rdataset->type, &noqname);
                if (tresult == ISC_R_SUCCESS && noqname != NULL) {
                        tresult = dns_rdataset_addnoqname(vevent->rdataset,
@@ -5872,11 +5869,10 @@ answer_response:
        /*
         * Cache any SOA/NS/NSEC records that happened to be validated.
         */
-       result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY);
+       result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
        while (result == ISC_R_SUCCESS) {
                name = NULL;
-               dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY,
-                                       &name);
+               dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
                for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
                     rdataset = ISC_LIST_NEXT(rdataset, link))
                {
@@ -5920,8 +5916,7 @@ answer_response:
                                continue;
                        }
                }
-               result = dns_message_nextname(fctx->rmessage,
-                                             DNS_SECTION_AUTHORITY);
+               result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
        }
 
        /*
@@ -5989,6 +5984,7 @@ noanswer_response:
 
 cleanup_event:
        INSIST(node == NULL);
+       dns_message_detach(&message);
        isc_event_free(&event);
 }
 
@@ -6008,8 +6004,8 @@ fctx_log(void *arg, int level, const char *fmt, ...) {
 }
 
 static inline isc_result_t
-findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
-           dns_name_t **noqnamep) {
+findnoqname(fetchctx_t *fctx, dns_message_t *message, dns_name_t *name,
+           dns_rdatatype_t type, dns_name_t **noqnamep) {
        dns_rdataset_t *nrdataset, *next, *sigrdataset;
        dns_rdata_rrsig_t rrsig;
        isc_result_t result;
@@ -6074,12 +6070,12 @@ findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
 #define NXND(x) ((x) == ISC_R_SUCCESS)
 
        section = DNS_SECTION_AUTHORITY;
-       for (result = dns_message_firstname(fctx->rmessage, section);
+       for (result = dns_message_firstname(message, section);
             result == ISC_R_SUCCESS;
-            result = dns_message_nextname(fctx->rmessage, section))
+            result = dns_message_nextname(message, section))
        {
                dns_name_t *nsec = NULL;
-               dns_message_currentname(fctx->rmessage, section, &nsec);
+               dns_message_currentname(message, section, &nsec);
                for (nrdataset = ISC_LIST_HEAD(nsec->list); nrdataset != NULL;
                     nrdataset = next) {
                        bool data = false, exists = false;
@@ -6140,8 +6136,8 @@ findnoqname(fetchctx_t *fctx, dns_name_t *name, dns_rdatatype_t type,
 }
 
 static inline isc_result_t
-cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
-          isc_stdtime_t now) {
+cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
+          dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now) {
        dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
        dns_rdataset_t *addedrdataset = NULL;
        dns_rdataset_t *ardataset = NULL, *asigrdataset = NULL;
@@ -6370,9 +6366,9 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
                                    rdataset->type != dns_rdatatype_rrsig) {
                                        isc_result_t tresult;
                                        dns_name_t *noqname = NULL;
-                                       tresult = findnoqname(fctx, name,
-                                                             rdataset->type,
-                                                             &noqname);
+                                       tresult = findnoqname(
+                                               fctx, message, name,
+                                               rdataset->type, &noqname);
                                        if (tresult == ISC_R_SUCCESS &&
                                            noqname != NULL) {
                                                (void)dns_rdataset_addnoqname(
@@ -6466,7 +6462,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
                                         * rdatasets needed validation.
                                         */
                                        result = valcreate(
-                                               fctx, addrinfo, name,
+                                               fctx, message, addrinfo, name,
                                                rdataset->type, rdataset,
                                                sigrdataset, valoptions, task);
                                }
@@ -6523,7 +6519,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
                            rdataset->type != dns_rdatatype_rrsig) {
                                isc_result_t tresult;
                                dns_name_t *noqname = NULL;
-                               tresult = findnoqname(fctx, name,
+                               tresult = findnoqname(fctx, message, name,
                                                      rdataset->type, &noqname);
                                if (tresult == ISC_R_SUCCESS && noqname != NULL)
                                {
@@ -6570,8 +6566,10 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
                                vtype = dns_rdatatype_dname;
                        }
                }
-               result = valcreate(fctx, addrinfo, name, vtype, valrdataset,
-                                  valsigrdataset, valoptions, task);
+
+               result = valcreate(fctx, message, addrinfo, name, vtype,
+                                  valrdataset, valsigrdataset, valoptions,
+                                  task);
        }
 
        if (result == ISC_R_SUCCESS && have_answer) {
@@ -6606,8 +6604,8 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
 }
 
 static inline isc_result_t
-cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
-             isc_stdtime_t now) {
+cache_message(fetchctx_t *fctx, dns_message_t *message,
+             dns_adbaddrinfo_t *addrinfo, isc_stdtime_t now) {
        isc_result_t result;
        dns_section_t section;
        dns_name_t *name;
@@ -6620,17 +6618,18 @@ cache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 
        for (section = DNS_SECTION_ANSWER; section <= DNS_SECTION_ADDITIONAL;
             section++) {
-               result = dns_message_firstname(fctx->rmessage, section);
+               result = dns_message_firstname(message, section);
                while (result == ISC_R_SUCCESS) {
                        name = NULL;
-                       dns_message_currentname(fctx->rmessage, section, &name);
+                       dns_message_currentname(message, section, &name);
                        if ((name->attributes & DNS_NAMEATTR_CACHE) != 0) {
-                               result = cache_name(fctx, name, addrinfo, now);
+                               result = cache_name(fctx, name, message,
+                                                   addrinfo, now);
                                if (result != ISC_R_SUCCESS) {
                                        break;
                                }
                        }
-                       result = dns_message_nextname(fctx->rmessage, section);
+                       result = dns_message_nextname(message, section);
                }
                if (result != ISC_R_NOMORE) {
                        break;
@@ -6705,8 +6704,9 @@ ncache_adderesult(dns_message_t *message, dns_db_t *cache, dns_dbnode_t *node,
 }
 
 static inline isc_result_t
-ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
-              dns_rdatatype_t covers, isc_stdtime_t now) {
+ncache_message(fetchctx_t *fctx, dns_message_t *message,
+              dns_adbaddrinfo_t *addrinfo, dns_rdatatype_t covers,
+              isc_stdtime_t now) {
        isc_result_t result, eresult;
        dns_name_t *name;
        dns_resolver_t *res;
@@ -6736,7 +6736,7 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
         * XXXMPA remove when we follow cnames and adjust the setting
         * of FCTX_ATTR_WANTNCACHE in rctx_answer_none().
         */
-       INSIST(fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0);
+       INSIST(message->counts[DNS_SECTION_ANSWER] == 0);
 
        /*
         * Is DNSSEC validation required for this name?
@@ -6771,19 +6771,18 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
                dns_rdataset_t *trdataset;
                dns_name_t *tname;
 
-               result = dns_message_firstname(fctx->rmessage,
-                                              DNS_SECTION_AUTHORITY);
+               result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
                while (result == ISC_R_SUCCESS) {
                        tname = NULL;
-                       dns_message_currentname(fctx->rmessage,
-                                               DNS_SECTION_AUTHORITY, &tname);
+                       dns_message_currentname(message, DNS_SECTION_AUTHORITY,
+                                               &tname);
                        for (trdataset = ISC_LIST_HEAD(tname->list);
                             trdataset != NULL;
                             trdataset = ISC_LIST_NEXT(trdataset, link))
                        {
                                trdataset->trust = dns_trust_pending_answer;
                        }
-                       result = dns_message_nextname(fctx->rmessage,
+                       result = dns_message_nextname(message,
                                                      DNS_SECTION_AUTHORITY);
                }
                if (result != ISC_R_NOMORE) {
@@ -6795,8 +6794,8 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
                /*
                 * Do negative response validation.
                 */
-               result = valcreate(fctx, addrinfo, name, fctx->type, NULL, NULL,
-                                  valoptions,
+               result = valcreate(fctx, message, addrinfo, name, fctx->type,
+                                  NULL, NULL, valoptions,
                                   res->buckets[fctx->bucketnum].task);
                /*
                 * If validation is necessary, return now.  Otherwise continue
@@ -6842,9 +6841,9 @@ ncache_message(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
                ttl = 0;
        }
 
-       result = ncache_adderesult(fctx->rmessage, fctx->cache, node, covers,
-                                  now, fctx->res->view->minncachettl, ttl,
-                                  false, false, ardataset, &eresult);
+       result = ncache_adderesult(message, fctx->cache, node, covers, now,
+                                  fctx->res->view->minncachettl, ttl, false,
+                                  false, ardataset, &eresult);
        if (result != ISC_R_SUCCESS) {
                goto unlock;
        }
@@ -6907,7 +6906,8 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset, bool external,
 static isc_result_t
 check_section(void *arg, const dns_name_t *addname, dns_rdatatype_t type,
              dns_section_t section) {
-       fetchctx_t *fctx = arg;
+       respctx_t *rctx = arg;
+       fetchctx_t *fctx = rctx->fctx;
        isc_result_t result;
        dns_name_t *name = NULL;
        dns_rdataset_t *rdataset = NULL;
@@ -6926,7 +6926,7 @@ check_section(void *arg, const dns_name_t *addname, dns_rdatatype_t type,
        gluing = (GLUING(fctx) || (fctx->type == dns_rdatatype_ns &&
                                   dns_name_equal(&fctx->name, dns_rootname)));
 
-       result = dns_message_findname(fctx->rmessage, section, addname,
+       result = dns_message_findname(rctx->query->rmessage, section, addname,
                                      dns_rdatatype_any, 0, &name, NULL);
        if (result == ISC_R_SUCCESS) {
                external = !dns_name_issubdomain(name, &fctx->domain);
@@ -7500,30 +7500,31 @@ log_nsid(isc_buffer_t *opt, size_t nsid_len, resquery_t *query, int level,
        isc_mem_put(mctx, buf, buflen);
 }
 
-static bool
-iscname(fetchctx_t *fctx) {
+static inline bool
+iscname(dns_message_t *message, dns_name_t *name) {
        isc_result_t result;
 
-       result = dns_message_findname(fctx->rmessage, DNS_SECTION_ANSWER,
-                                     &fctx->name, dns_rdatatype_cname, 0, NULL,
-                                     NULL);
+       result = dns_message_findname(message, DNS_SECTION_ANSWER, name,
+                                     dns_rdatatype_cname, 0, NULL, NULL);
        return (result == ISC_R_SUCCESS ? true : false);
 }
 
 static bool
-betterreferral(fetchctx_t *fctx) {
+betterreferral(respctx_t *rctx) {
        isc_result_t result;
        dns_name_t *name;
        dns_rdataset_t *rdataset;
-       dns_message_t *message = fctx->rmessage;
 
-       for (result = dns_message_firstname(message, DNS_SECTION_AUTHORITY);
+       for (result = dns_message_firstname(rctx->query->rmessage,
+                                           DNS_SECTION_AUTHORITY);
             result == ISC_R_SUCCESS;
-            result = dns_message_nextname(message, DNS_SECTION_AUTHORITY))
+            result = dns_message_nextname(rctx->query->rmessage,
+                                          DNS_SECTION_AUTHORITY))
        {
                name = NULL;
-               dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name);
-               if (!isstrictsubdomain(name, &fctx->domain)) {
+               dns_message_currentname(rctx->query->rmessage,
+                                       DNS_SECTION_AUTHORITY, &name);
+               if (!isstrictsubdomain(name, &rctx->fctx->domain)) {
                        continue;
                }
                for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
@@ -7587,7 +7588,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        }
 
        if (query->tsig != NULL) {
-               result = dns_message_setquerytsig(fctx->rmessage, query->tsig);
+               result = dns_message_setquerytsig(query->rmessage, query->tsig);
                if (result != ISC_R_SUCCESS) {
                        FCTXTRACE3("unable to set query tsig", result);
                        rctx_done(&rctx, result);
@@ -7596,7 +7597,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        }
 
        if (query->tsigkey) {
-               result = dns_message_settsigkey(fctx->rmessage, query->tsigkey);
+               result = dns_message_settsigkey(query->rmessage,
+                                               query->tsigkey);
                if (result != ISC_R_SUCCESS) {
                        FCTXTRACE3("unable to set tsig key", result);
                        rctx_done(&rctx, result);
@@ -7604,7 +7606,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
                }
        }
 
-       dns_message_setclass(fctx->rmessage, fctx->res->rdclass);
+       dns_message_setclass(query->rmessage, fctx->res->rdclass);
 
        if ((rctx.retryopts & DNS_FETCHOPT_TCP) == 0) {
                if ((rctx.retryopts & DNS_FETCHOPT_NOEDNS0) == 0) {
@@ -7629,7 +7631,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
         */
        rctx_logpacket(&rctx);
 
-       if (fctx->rmessage->rdclass != fctx->res->rdclass) {
+       if (query->rmessage->rdclass != fctx->res->rdclass) {
                rctx.resend = true;
                FCTXTRACE("bad class");
                rctx_done(&rctx, result);
@@ -7639,12 +7641,12 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        /*
         * Process receive opt record.
         */
-       rctx.opt = dns_message_getopt(fctx->rmessage);
+       rctx.opt = dns_message_getopt(query->rmessage);
        if (rctx.opt != NULL) {
                rctx_opt(&rctx);
        }
 
-       if (fctx->rmessage->cc_bad && (rctx.retryopts & DNS_FETCHOPT_TCP) == 0)
+       if (query->rmessage->cc_bad && (rctx.retryopts & DNS_FETCHOPT_TCP) == 0)
        {
                /*
                 * If the COOKIE is bad, assume it is an attack and
@@ -7669,10 +7671,10 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
         * the same question.
         * FORMERR/NOTIMP if they have a question section then it must match.
         */
-       switch (fctx->rmessage->rcode) {
+       switch (query->rmessage->rcode) {
        case dns_rcode_notimp:
        case dns_rcode_formerr:
-               if (fctx->rmessage->counts[DNS_SECTION_QUESTION] == 0) {
+               if (query->rmessage->counts[DNS_SECTION_QUESTION] == 0) {
                        break;
                }
        /* FALLTHROUGH */
@@ -7684,7 +7686,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        case dns_rcode_refused:
        case dns_rcode_servfail:
        default:
-               result = same_question(fctx);
+               result = same_question(fctx, query->rmessage);
                if (result != ISC_R_SUCCESS) {
                        FCTXTRACE3("response did not match question", result);
                        rctx.nextitem = true;
@@ -7698,7 +7700,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
         * If the message is signed, check the signature.  If not, this
         * returns success anyway.
         */
-       result = dns_message_checksig(fctx->rmessage, fctx->res->view);
+       result = dns_message_checksig(query->rmessage, fctx->res->view);
        if (result != ISC_R_SUCCESS) {
                FCTXTRACE3("signature check failed", result);
                rctx_done(&rctx, result);
@@ -7708,7 +7710,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        /*
         * The dispatcher should ensure we only get responses with QR set.
         */
-       INSIST((fctx->rmessage->flags & DNS_MESSAGEFLAG_QR) != 0);
+       INSIST((query->rmessage->flags & DNS_MESSAGEFLAG_QR) != 0);
        /*
         * INSIST() that the message comes from the place we sent it to,
         * since the dispatch code should ensure this.
@@ -7722,7 +7724,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        /*
         * Deal with truncated responses by retrying using TCP.
         */
-       if ((fctx->rmessage->flags & DNS_MESSAGEFLAG_TC) != 0) {
+       if ((query->rmessage->flags & DNS_MESSAGEFLAG_TC) != 0) {
                rctx.truncated = true;
        }
 
@@ -7743,7 +7745,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        /*
         * Is it a query response?
         */
-       if (fctx->rmessage->opcode != dns_opcode_query) {
+       if (query->rmessage->opcode != dns_opcode_query) {
                rctx.broken_server = DNS_R_UNEXPECTEDOPCODE;
                rctx.next_server = true;
                FCTXTRACE("invalid message opcode");
@@ -7754,7 +7756,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        /*
         * Update statistics about erroneous responses.
         */
-       switch (fctx->rmessage->rcode) {
+       switch (query->rmessage->rcode) {
        case dns_rcode_noerror:
                /* no error */
                break;
@@ -7807,7 +7809,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
         * to validate the names in the response message.
         */
        if ((fctx->res->options & DNS_RESOLVER_CHECKNAMES) != 0) {
-               checknames(fctx->rmessage);
+               checknames(query->rmessage);
        }
 
        /*
@@ -7818,18 +7820,18 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
        /*
         * Did we get any answers?
         */
-       if (fctx->rmessage->counts[DNS_SECTION_ANSWER] > 0 &&
-           (fctx->rmessage->rcode == dns_rcode_noerror ||
-            fctx->rmessage->rcode == dns_rcode_yxdomain ||
-            fctx->rmessage->rcode == dns_rcode_nxdomain))
+       if (query->rmessage->counts[DNS_SECTION_ANSWER] > 0 &&
+           (query->rmessage->rcode == dns_rcode_noerror ||
+            query->rmessage->rcode == dns_rcode_yxdomain ||
+            query->rmessage->rcode == dns_rcode_nxdomain))
        {
                result = rctx_answer(&rctx);
                if (result == ISC_R_COMPLETE) {
                        return;
                }
-       } else if (fctx->rmessage->counts[DNS_SECTION_AUTHORITY] > 0 ||
-                  fctx->rmessage->rcode == dns_rcode_noerror ||
-                  fctx->rmessage->rcode == dns_rcode_nxdomain)
+       } else if (query->rmessage->counts[DNS_SECTION_AUTHORITY] > 0 ||
+                  query->rmessage->rcode == dns_rcode_noerror ||
+                  query->rmessage->rcode == dns_rcode_nxdomain)
        {
                /*
                 * This might be an NXDOMAIN, NXRRSET, or referral.
@@ -7880,7 +7882,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
         */
        if (WANTCACHE(fctx)) {
                isc_result_t tresult;
-               tresult = cache_message(fctx, query->addrinfo, rctx.now);
+               tresult = cache_message(fctx, query->rmessage, query->addrinfo,
+                                       rctx.now);
                if (tresult != ISC_R_SUCCESS) {
                        FCTXTRACE3("cache_message complete", tresult);
                        rctx_done(&rctx, tresult);
@@ -7934,7 +7937,7 @@ static void
 rctx_answer_init(respctx_t *rctx) {
        fetchctx_t *fctx = rctx->fctx;
 
-       rctx->aa = ((fctx->rmessage->flags & DNS_MESSAGEFLAG_AA) != 0);
+       rctx->aa = ((rctx->query->rmessage->flags & DNS_MESSAGEFLAG_AA) != 0);
        if (rctx->aa) {
                rctx->trust = dns_trust_authanswer;
        } else {
@@ -8040,7 +8043,7 @@ rctx_parse(respctx_t *rctx) {
        fetchctx_t *fctx = rctx->fctx;
        resquery_t *query = rctx->query;
 
-       result = dns_message_parse(fctx->rmessage, &rctx->devent->buffer, 0);
+       result = dns_message_parse(query->rmessage, &rctx->devent->buffer, 0);
        if (result == ISC_R_SUCCESS) {
                return (ISC_R_SUCCESS);
        }
@@ -8049,8 +8052,8 @@ rctx_parse(respctx_t *rctx) {
 
        switch (result) {
        case ISC_R_UNEXPECTEDEND:
-               if (fctx->rmessage->question_ok &&
-                   (fctx->rmessage->flags & DNS_MESSAGEFLAG_TC) != 0 &&
+               if (query->rmessage->question_ok &&
+                   (query->rmessage->flags & DNS_MESSAGEFLAG_TC) != 0 &&
                    (rctx->retryopts & DNS_FETCHOPT_TCP) == 0)
                {
                        /*
@@ -8166,18 +8169,18 @@ rctx_opt(respctx_t *rctx) {
                                }
                                optvalue = isc_buffer_current(&optbuf);
                                compute_cc(query, cookie, sizeof(cookie));
-                               INSIST(fctx->rmessage->cc_bad == 0 &&
-                                      fctx->rmessage->cc_ok == 0);
+                               INSIST(query->rmessage->cc_bad == 0 &&
+                                      query->rmessage->cc_ok == 0);
                                if (optlen >= 8U &&
                                    memcmp(cookie, optvalue, 8) == 0) {
-                                       fctx->rmessage->cc_ok = 1;
+                                       query->rmessage->cc_ok = 1;
                                        inc_stats(fctx->res,
                                                  dns_resstatscounter_cookieok);
                                        addrinfo = query->addrinfo;
                                        dns_adb_setcookie(fctx->adb, addrinfo,
                                                          optvalue, optlen);
                                } else {
-                                       fctx->rmessage->cc_bad = 1;
+                                       query->rmessage->cc_bad = 1;
                                }
                                isc_buffer_forward(&optbuf, optlen);
                                inc_stats(fctx->res,
@@ -8210,24 +8213,24 @@ rctx_edns(respctx_t *rctx) {
         * EDNS support.
         */
        if (rctx->opt == NULL && !EDNSOK(query->addrinfo) &&
-           (fctx->rmessage->rcode == dns_rcode_noerror ||
-            fctx->rmessage->rcode == dns_rcode_nxdomain ||
-            fctx->rmessage->rcode == dns_rcode_refused ||
-            fctx->rmessage->rcode == dns_rcode_yxdomain) &&
+           (query->rmessage->rcode == dns_rcode_noerror ||
+            query->rmessage->rcode == dns_rcode_nxdomain ||
+            query->rmessage->rcode == dns_rcode_refused ||
+            query->rmessage->rcode == dns_rcode_yxdomain) &&
            bad_edns(fctx, &query->addrinfo->sockaddr))
        {
                dns_message_logpacket(
-                       fctx->rmessage, "received packet (bad edns) from",
+                       query->rmessage, "received packet (bad edns) from",
                        &query->addrinfo->sockaddr, DNS_LOGCATEGORY_RESOLVER,
                        DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
                        fctx->res->mctx);
                dns_adb_changeflags(fctx->adb, query->addrinfo,
                                    DNS_FETCHOPT_NOEDNS0, DNS_FETCHOPT_NOEDNS0);
        } else if (rctx->opt == NULL &&
-                  (fctx->rmessage->flags & DNS_MESSAGEFLAG_TC) == 0 &&
+                  (query->rmessage->flags & DNS_MESSAGEFLAG_TC) == 0 &&
                   !EDNSOK(query->addrinfo) &&
-                  (fctx->rmessage->rcode == dns_rcode_noerror ||
-                   fctx->rmessage->rcode == dns_rcode_nxdomain) &&
+                  (query->rmessage->rcode == dns_rcode_noerror ||
+                   query->rmessage->rcode == dns_rcode_nxdomain) &&
                   (rctx->retryopts & DNS_FETCHOPT_NOEDNS0) == 0)
        {
                /*
@@ -8242,7 +8245,7 @@ rctx_edns(respctx_t *rctx) {
                 * and NXDOMAIN.
                 */
                dns_message_logpacket(
-                       fctx->rmessage, "received packet (no opt) from",
+                       query->rmessage, "received packet (no opt) from",
                        &query->addrinfo->sockaddr, DNS_LOGCATEGORY_RESOLVER,
                        DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(3),
                        fctx->res->mctx);
@@ -8256,10 +8259,10 @@ rctx_edns(respctx_t *rctx) {
         */
        if (rctx->opt != NULL && !EDNSOK(query->addrinfo) &&
            (rctx->retryopts & DNS_FETCHOPT_NOEDNS0) == 0 &&
-           (fctx->rmessage->rcode == dns_rcode_noerror ||
-            fctx->rmessage->rcode == dns_rcode_nxdomain ||
-            fctx->rmessage->rcode == dns_rcode_refused ||
-            fctx->rmessage->rcode == dns_rcode_yxdomain))
+           (query->rmessage->rcode == dns_rcode_noerror ||
+            query->rmessage->rcode == dns_rcode_nxdomain ||
+            query->rmessage->rcode == dns_rcode_refused ||
+            query->rmessage->rcode == dns_rcode_yxdomain))
        {
                dns_adb_changeflags(fctx->adb, query->addrinfo,
                                    FCTX_ADDRINFO_EDNSOK, FCTX_ADDRINFO_EDNSOK);
@@ -8278,14 +8281,15 @@ rctx_answer(respctx_t *rctx) {
        fetchctx_t *fctx = rctx->fctx;
        resquery_t *query = rctx->query;
 
-       if ((fctx->rmessage->flags & DNS_MESSAGEFLAG_AA) != 0 ||
+       if ((query->rmessage->flags & DNS_MESSAGEFLAG_AA) != 0 ||
            ISFORWARDER(query->addrinfo))
        {
                result = rctx_answer_positive(rctx);
                if (result != ISC_R_SUCCESS) {
                        FCTXTRACE3("rctx_answer_positive (AA/fwd)", result);
                }
-       } else if (iscname(fctx) && fctx->type != dns_rdatatype_any &&
+       } else if (iscname(query->rmessage, &fctx->name) &&
+                  fctx->type != dns_rdatatype_any &&
                   fctx->type != dns_rdatatype_cname)
        {
                /*
@@ -8298,7 +8302,7 @@ rctx_answer(respctx_t *rctx) {
                        FCTXTRACE3("rctx_answer_positive (!ANY/!CNAME)",
                                   result);
                }
-       } else if (fctx->type != dns_rdatatype_ns && !betterreferral(fctx)) {
+       } else if (fctx->type != dns_rdatatype_ns && !betterreferral(rctx)) {
                result = rctx_answer_positive(rctx);
                if (result != ISC_R_SUCCESS) {
                        FCTXTRACE3("rctx_answer_positive (!NS)", result);
@@ -8433,7 +8437,7 @@ rctx_answer_positive(respctx_t *rctx) {
         * We didn't end with an incomplete chain, so the rcode should be
         * "no error".
         */
-       if (fctx->rmessage->rcode != dns_rcode_noerror) {
+       if (rctx->query->rmessage->rcode != dns_rcode_noerror) {
                log_formerr(fctx, "CNAME/DNAME chain complete, but RCODE "
                                  "indicates error");
                return (DNS_R_FORMERR);
@@ -8471,17 +8475,19 @@ rctx_answer_scan(respctx_t *rctx) {
        fetchctx_t *fctx = rctx->fctx;
        dns_rdataset_t *rdataset = NULL;
 
-       for (result = dns_message_firstname(fctx->rmessage, DNS_SECTION_ANSWER);
+       for (result = dns_message_firstname(rctx->query->rmessage,
+                                           DNS_SECTION_ANSWER);
             result == ISC_R_SUCCESS;
-            result = dns_message_nextname(fctx->rmessage, DNS_SECTION_ANSWER))
+            result = dns_message_nextname(rctx->query->rmessage,
+                                          DNS_SECTION_ANSWER))
        {
                int order;
                unsigned int nlabels;
                dns_namereln_t namereln;
                dns_name_t *name = NULL;
 
-               dns_message_currentname(fctx->rmessage, DNS_SECTION_ANSWER,
-                                       &name);
+               dns_message_currentname(rctx->query->rmessage,
+                                       DNS_SECTION_ANSWER, &name);
                namereln = dns_name_fullcompare(&fctx->name, name, &order,
                                                &nlabels);
                switch (namereln) {
@@ -8608,7 +8614,7 @@ rctx_answer_any(respctx_t *rctx) {
                rdataset->trust = rctx->trust;
 
                (void)dns_rdataset_additionaldata(rdataset, check_related,
-                                                 fctx);
+                                                 rctx);
        }
 
        return (ISC_R_SUCCESS);
@@ -8655,7 +8661,7 @@ rctx_answer_match(respctx_t *rctx) {
        rctx->ardataset->attributes |= DNS_RDATASETATTR_ANSWER;
        rctx->ardataset->attributes |= DNS_RDATASETATTR_CACHE;
        rctx->ardataset->trust = rctx->trust;
-       (void)dns_rdataset_additionaldata(rctx->ardataset, check_related, fctx);
+       (void)dns_rdataset_additionaldata(rctx->ardataset, check_related, rctx);
 
        for (sigrdataset = ISC_LIST_HEAD(rctx->aname->list);
             sigrdataset != NULL;
@@ -8816,13 +8822,14 @@ rctx_authority_positive(respctx_t *rctx) {
        bool done = false;
        isc_result_t result;
 
-       result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY);
+       result = dns_message_firstname(rctx->query->rmessage,
+                                      DNS_SECTION_AUTHORITY);
        while (!done && result == ISC_R_SUCCESS) {
                dns_name_t *name = NULL;
                bool external;
 
-               dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY,
-                                       &name);
+               dns_message_currentname(rctx->query->rmessage,
+                                       DNS_SECTION_AUTHORITY, &name);
                external = !dns_name_issubdomain(name, &fctx->domain);
 
                if (!external) {
@@ -8862,13 +8869,13 @@ rctx_authority_positive(respctx_t *rctx) {
                                         * to this rdataset.
                                         */
                                        (void)dns_rdataset_additionaldata(
-                                               rdataset, check_related, fctx);
+                                               rdataset, check_related, rctx);
                                        done = true;
                                }
                        }
                }
 
-               result = dns_message_nextname(fctx->rmessage,
+               result = dns_message_nextname(rctx->query->rmessage,
                                              DNS_SECTION_AUTHORITY);
        }
 }
@@ -8893,9 +8900,9 @@ rctx_answer_none(respctx_t *rctx) {
         * Sometimes we can tell if its a negative response by looking at
         * the message header.
         */
-       if (fctx->rmessage->rcode == dns_rcode_nxdomain ||
-           (fctx->rmessage->counts[DNS_SECTION_ANSWER] == 0 &&
-            fctx->rmessage->counts[DNS_SECTION_AUTHORITY] == 0))
+       if (rctx->query->rmessage->rcode == dns_rcode_nxdomain ||
+           (rctx->query->rmessage->counts[DNS_SECTION_ANSWER] == 0 &&
+            rctx->query->rmessage->counts[DNS_SECTION_AUTHORITY] == 0))
        {
                rctx->negative = true;
        }
@@ -8941,7 +8948,8 @@ rctx_answer_none(respctx_t *rctx) {
        /*
         * Trigger lookups for DNS nameservers.
         */
-       if (rctx->negative && fctx->rmessage->rcode == dns_rcode_noerror &&
+       if (rctx->negative &&
+           rctx->query->rmessage->rcode == dns_rcode_noerror &&
            fctx->type == dns_rdatatype_ds && rctx->soa_name != NULL &&
            dns_name_equal(rctx->soa_name, &fctx->name) &&
            !dns_name_equal(&fctx->name, dns_rootname))
@@ -9035,7 +9043,7 @@ rctx_authority_negative(respctx_t *rctx) {
                section = DNS_SECTION_AUTHORITY;
        }
 
-       result = dns_message_firstname(fctx->rmessage, section);
+       result = dns_message_firstname(rctx->query->rmessage, section);
        if (result != ISC_R_SUCCESS) {
                return (ISC_R_SUCCESS);
        }
@@ -9043,8 +9051,8 @@ rctx_authority_negative(respctx_t *rctx) {
        while (!finished) {
                dns_name_t *name = NULL;
 
-               dns_message_currentname(fctx->rmessage, section, &name);
-               result = dns_message_nextname(fctx->rmessage, section);
+               dns_message_currentname(rctx->query->rmessage, section, &name);
+               result = dns_message_nextname(rctx->query->rmessage, section);
                if (result != ISC_R_SUCCESS) {
                        finished = true;
                }
@@ -9160,7 +9168,7 @@ rctx_ncache(respctx_t *rctx) {
        /*
         * Cache DS NXDOMAIN separately to other types.
         */
-       if (fctx->rmessage->rcode == dns_rcode_nxdomain &&
+       if (rctx->query->rmessage->rcode == dns_rcode_nxdomain &&
            fctx->type != dns_rdatatype_ds)
        {
                covers = dns_rdatatype_any;
@@ -9171,7 +9179,8 @@ rctx_ncache(respctx_t *rctx) {
        /*
         * Cache any negative cache entries in the message.
         */
-       result = ncache_message(fctx, rctx->query->addrinfo, covers, rctx->now);
+       result = ncache_message(fctx, rctx->query->rmessage,
+                               rctx->query->addrinfo, covers, rctx->now);
        if (result != ISC_R_SUCCESS) {
                FCTXTRACE3("ncache_message complete", result);
        }
@@ -9192,7 +9201,8 @@ rctx_authority_dnssec(respctx_t *rctx) {
 
        REQUIRE(!rctx->ns_in_answer && !rctx->glue_in_answer);
 
-       result = dns_message_firstname(fctx->rmessage, DNS_SECTION_AUTHORITY);
+       result = dns_message_firstname(rctx->query->rmessage,
+                                      DNS_SECTION_AUTHORITY);
        if (result != ISC_R_SUCCESS) {
                return (ISC_R_SUCCESS);
        }
@@ -9200,9 +9210,9 @@ rctx_authority_dnssec(respctx_t *rctx) {
        while (!finished) {
                dns_name_t *name = NULL;
 
-               dns_message_currentname(fctx->rmessage, DNS_SECTION_AUTHORITY,
-                                       &name);
-               result = dns_message_nextname(fctx->rmessage,
+               dns_message_currentname(rctx->query->rmessage,
+                                       DNS_SECTION_AUTHORITY, &name);
+               result = dns_message_nextname(rctx->query->rmessage,
                                              DNS_SECTION_AUTHORITY);
                if (result != ISC_R_SUCCESS) {
                        finished = true;
@@ -9361,7 +9371,7 @@ rctx_referral(respctx_t *rctx) {
        INSIST(rctx->ns_rdataset != NULL);
        FCTX_ATTR_SET(fctx, FCTX_ATTR_GLUING);
        (void)dns_rdataset_additionaldata(rctx->ns_rdataset, check_related,
-                                         fctx);
+                                         rctx);
 #if CHECK_FOR_GLUE_IN_ANSWER
        /*
         * Look in the answer section for "glue" that is incorrectly
@@ -9459,7 +9469,6 @@ rctx_referral(respctx_t *rctx) {
  */
 static void
 rctx_additional(respctx_t *rctx) {
-       fetchctx_t *fctx = rctx->fctx;
        bool rescan;
        dns_section_t section = DNS_SECTION_ADDITIONAL;
        isc_result_t result;
@@ -9467,14 +9476,14 @@ rctx_additional(respctx_t *rctx) {
 again:
        rescan = false;
 
-       for (result = dns_message_firstname(fctx->rmessage, section);
+       for (result = dns_message_firstname(rctx->query->rmessage, section);
             result == ISC_R_SUCCESS;
-            result = dns_message_nextname(fctx->rmessage, section))
+            result = dns_message_nextname(rctx->query->rmessage, section))
        {
                dns_name_t *name = NULL;
                dns_rdataset_t *rdataset;
-               dns_message_currentname(fctx->rmessage, DNS_SECTION_ADDITIONAL,
-                                       &name);
+               dns_message_currentname(rctx->query->rmessage,
+                                       DNS_SECTION_ADDITIONAL, &name);
                if ((name->attributes & DNS_NAMEATTR_CHASE) == 0) {
                        continue;
                }
@@ -9485,7 +9494,7 @@ again:
                        if (CHASE(rdataset)) {
                                rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
                                (void)dns_rdataset_additionaldata(
-                                       rdataset, check_related, fctx);
+                                       rdataset, check_related, rctx);
                                rescan = true;
                        }
                }
@@ -9513,7 +9522,8 @@ rctx_nextserver(respctx_t *rctx, dns_adbaddrinfo_t *addrinfo,
                 * Add this server to the list of bad servers for
                 * this fctx.
                 */
-               add_bad(fctx, addrinfo, rctx->broken_server, rctx->broken_type);
+               add_bad(fctx, rctx->query->rmessage, addrinfo,
+                       rctx->broken_server, rctx->broken_type);
        }
 
        if (rctx->get_nameservers) {
@@ -9629,7 +9639,6 @@ rctx_next(respctx_t *rctx) {
        FCTXTRACE("nextitem");
        inc_stats(rctx->fctx->res, dns_resstatscounter_nextitem);
        INSIST(rctx->query->dispentry != NULL);
-       dns_message_reset(rctx->fctx->rmessage, DNS_MESSAGE_INTENTPARSE);
        result = dns_dispatch_getnext(rctx->query->dispentry, &rctx->devent);
        if (result != ISC_R_SUCCESS) {
                fctx_done(rctx->fctx, result, __LINE__);
@@ -9646,7 +9655,8 @@ rctx_chaseds(respctx_t *rctx, dns_adbaddrinfo_t *addrinfo,
        fetchctx_t *fctx = rctx->fctx;
        unsigned int n;
 
-       add_bad(fctx, addrinfo, result, rctx->broken_type);
+       add_bad(fctx, rctx->query->rmessage, addrinfo, result,
+               rctx->broken_type);
        fctx_cancelqueries(fctx, true, false);
        fctx_cleanupfinds(fctx);
        fctx_cleanupforwaddrs(fctx);
@@ -9763,7 +9773,7 @@ rctx_logpacket(respctx_t *rctx) {
 #endif /* HAVE_DNSTAP */
 
        dns_message_logfmtpacket(
-               rctx->fctx->rmessage, "received packet from",
+               rctx->query->rmessage, "received packet from",
                &rctx->query->addrinfo->sockaddr, DNS_LOGCATEGORY_RESOLVER,
                DNS_LOGMODULE_PACKETS, &dns_master_style_comment,
                ISC_LOG_DEBUG(10), rctx->fctx->res->mctx);
@@ -9816,15 +9826,15 @@ rctx_badserver(respctx_t *rctx, isc_result_t result) {
        resquery_t *query = rctx->query;
        isc_buffer_t b;
        char code[64];
+       dns_rcode_t rcode = rctx->query->rmessage->rcode;
 
-       if (fctx->rmessage->rcode == dns_rcode_noerror ||
-           fctx->rmessage->rcode == dns_rcode_yxdomain ||
-           fctx->rmessage->rcode == dns_rcode_nxdomain)
+       if (rcode == dns_rcode_noerror || rcode == dns_rcode_yxdomain ||
+           rcode == dns_rcode_nxdomain)
        {
                return (ISC_R_SUCCESS);
        }
 
-       if ((fctx->rmessage->rcode == dns_rcode_formerr) &&
+       if ((rcode == dns_rcode_formerr) &&
            (rctx->retryopts & DNS_FETCHOPT_NOEDNS0) == 0)
        {
                /*
@@ -9845,7 +9855,7 @@ rctx_badserver(respctx_t *rctx, isc_result_t result) {
                 */
                add_bad_edns(fctx, &query->addrinfo->sockaddr);
                inc_stats(fctx->res, dns_resstatscounter_edns0fail);
-       } else if (fctx->rmessage->rcode == dns_rcode_formerr) {
+       } else if (rcode == dns_rcode_formerr) {
                if (ISFORWARDER(query->addrinfo)) {
                        /*
                         * This forwarder doesn't understand us,
@@ -9865,7 +9875,7 @@ rctx_badserver(respctx_t *rctx, isc_result_t result) {
                        log_formerr(fctx, "server sent FORMERR");
                        result = DNS_R_FORMERR;
                }
-       } else if (fctx->rmessage->rcode == dns_rcode_badvers) {
+       } else if (rcode == dns_rcode_badvers) {
                unsigned int version;
 #if DNS_EDNS_VERSION > 0
                unsigned int flags, mask;
@@ -9911,8 +9921,7 @@ rctx_badserver(respctx_t *rctx, isc_result_t result) {
                rctx->broken_server = DNS_R_BADVERS;
                rctx->next_server = true;
 #endif /* if DNS_EDNS_VERSION > 0 */
-       } else if (fctx->rmessage->rcode == dns_rcode_badcookie &&
-                  fctx->rmessage->cc_ok)
+       } else if (rcode == dns_rcode_badcookie && rctx->query->rmessage->cc_ok)
        {
                /*
                 * We have recorded the new cookie.
@@ -9928,7 +9937,7 @@ rctx_badserver(respctx_t *rctx, isc_result_t result) {
        }
 
        isc_buffer_init(&b, code, sizeof(code) - 1);
-       dns_rcode_totext(fctx->rmessage->rcode, &b);
+       dns_rcode_totext(rcode, &b);
        code[isc_buffer_usedlength(&b)] = '\0';
        FCTXTRACE2("remote server broken: returned ", code);
        rctx_done(rctx, result);
@@ -9947,7 +9956,8 @@ rctx_lameserver(respctx_t *rctx) {
        resquery_t *query = rctx->query;
 
        if (fctx->res->lame_ttl == 0 || ISFORWARDER(query->addrinfo) ||
-           !is_lame(fctx)) {
+           !is_lame(fctx, query->rmessage))
+       {
                return (ISC_R_SUCCESS);
        }
 
@@ -9985,7 +9995,7 @@ rctx_delonly_zone(respctx_t *rctx) {
        if (ISFORWARDER(rctx->query->addrinfo) ||
            !dns_view_isdelegationonly(fctx->res->view, &fctx->domain) ||
            dns_name_equal(&fctx->domain, &fctx->name) ||
-           !fix_mustbedelegationornxdomain(fctx->rmessage, fctx))
+           !fix_mustbedelegationornxdomain(rctx->query->rmessage, fctx))
        {
                return;
        }