Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: request.c,v 1.15 2026/05/20 16:53:45 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <inttypes.h>
     19 #include <stdbool.h>
     20 
     21 #include <isc/async.h>
     22 #include <isc/loop.h>
     23 #include <isc/magic.h>
     24 #include <isc/mem.h>
     25 #include <isc/netmgr.h>
     26 #include <isc/result.h>
     27 #include <isc/thread.h>
     28 #include <isc/tls.h>
     29 #include <isc/util.h>
     30 
     31 #include <dns/acl.h>
     32 #include <dns/compress.h>
     33 #include <dns/dispatch.h>
     34 #include <dns/log.h>
     35 #include <dns/message.h>
     36 #include <dns/rdata.h>
     37 #include <dns/rdatastruct.h>
     38 #include <dns/request.h>
     39 #include <dns/transport.h>
     40 #include <dns/tsig.h>
     41 
     42 #define REQUESTMGR_MAGIC      ISC_MAGIC('R', 'q', 'u', 'M')
     43 #define VALID_REQUESTMGR(mgr) ISC_MAGIC_VALID(mgr, REQUESTMGR_MAGIC)
     44 
     45 #define REQUEST_MAGIC	       ISC_MAGIC('R', 'q', 'u', '!')
     46 #define VALID_REQUEST(request) ISC_MAGIC_VALID(request, REQUEST_MAGIC)
     47 
     48 typedef ISC_LIST(dns_request_t) dns_requestlist_t;
     49 
     50 struct dns_requestmgr {
     51 	unsigned int magic;
     52 	isc_mem_t *mctx;
     53 	isc_refcount_t references;
     54 	isc_loopmgr_t *loopmgr;
     55 
     56 	atomic_bool shuttingdown;
     57 
     58 	dns_dispatchmgr_t *dispatchmgr;
     59 	dns_dispatchset_t *dispatches4;
     60 	dns_dispatchset_t *dispatches6;
     61 	dns_requestlist_t *requests;
     62 };
     63 
     64 struct dns_request {
     65 	unsigned int magic;
     66 	isc_refcount_t references;
     67 
     68 	isc_mem_t *mctx;
     69 	int32_t flags;
     70 
     71 	isc_loop_t *loop;
     72 	unsigned int tid;
     73 
     74 	isc_result_t result;
     75 	isc_job_cb cb;
     76 	void *arg;
     77 	ISC_LINK(dns_request_t) link;
     78 	isc_buffer_t *query;
     79 	isc_buffer_t *answer;
     80 	dns_dispatch_t *dispatch;
     81 	dns_dispentry_t *dispentry;
     82 	dns_requestmgr_t *requestmgr;
     83 	isc_buffer_t *tsig;
     84 	dns_tsigkey_t *tsigkey;
     85 	isc_sockaddr_t destaddr;
     86 	unsigned int timeout;
     87 	unsigned int udpcount;
     88 };
     89 
     90 #define DNS_REQUEST_F_CONNECTING (1 << 0)
     91 #define DNS_REQUEST_F_SENDING	 (1 << 1)
     92 #define DNS_REQUEST_F_COMPLETE	 (1 << 2)
     93 #define DNS_REQUEST_F_TCP	 (1 << 3)
     94 
     95 #define DNS_REQUEST_CONNECTING(r) (((r)->flags & DNS_REQUEST_F_CONNECTING) != 0)
     96 #define DNS_REQUEST_SENDING(r)	  (((r)->flags & DNS_REQUEST_F_SENDING) != 0)
     97 #define DNS_REQUEST_COMPLETE(r)	  (((r)->flags & DNS_REQUEST_F_COMPLETE) != 0)
     98 
     99 /***
    100  *** Forward
    101  ***/
    102 
    103 static isc_result_t
    104 req_render(dns_message_t *message, isc_buffer_t **buffer, unsigned int options,
    105 	   isc_mem_t *mctx);
    106 static void
    107 req_response(isc_result_t result, isc_region_t *region, void *arg);
    108 static void
    109 req_senddone(isc_result_t eresult, isc_region_t *region, void *arg);
    110 static void
    111 req_cleanup(dns_request_t *request);
    112 static void
    113 req_sendevent(dns_request_t *request, isc_result_t result);
    114 static void
    115 req_connected(isc_result_t eresult, isc_region_t *region, void *arg);
    116 static void
    117 req_destroy(dns_request_t *request);
    118 static void
    119 req_log(int level, const char *fmt, ...) ISC_FORMAT_PRINTF(2, 3);
    120 
    121 /***
    122  *** Public
    123  ***/
    124 
    125 isc_result_t
    126 dns_requestmgr_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
    127 		      dns_dispatchmgr_t *dispatchmgr,
    128 		      dns_dispatch_t *dispatchv4, dns_dispatch_t *dispatchv6,
    129 		      dns_requestmgr_t **requestmgrp) {
    130 	REQUIRE(requestmgrp != NULL && *requestmgrp == NULL);
    131 	REQUIRE(dispatchmgr != NULL);
    132 
    133 	req_log(ISC_LOG_DEBUG(3), "%s", __func__);
    134 
    135 	dns_requestmgr_t *requestmgr = isc_mem_get(mctx, sizeof(*requestmgr));
    136 	*requestmgr = (dns_requestmgr_t){
    137 		.magic = REQUESTMGR_MAGIC,
    138 		.loopmgr = loopmgr,
    139 	};
    140 	isc_mem_attach(mctx, &requestmgr->mctx);
    141 
    142 	uint32_t nloops = isc_loopmgr_nloops(requestmgr->loopmgr);
    143 	requestmgr->requests = isc_mem_cget(requestmgr->mctx, nloops,
    144 					    sizeof(requestmgr->requests[0]));
    145 	for (size_t i = 0; i < nloops; i++) {
    146 		ISC_LIST_INIT(requestmgr->requests[i]);
    147 
    148 		/* unreferenced in requests_cancel() */
    149 		isc_loop_ref(isc_loop_get(requestmgr->loopmgr, i));
    150 	}
    151 
    152 	dns_dispatchmgr_attach(dispatchmgr, &requestmgr->dispatchmgr);
    153 
    154 	if (dispatchv4 != NULL) {
    155 		dns_dispatchset_create(requestmgr->mctx, dispatchv4,
    156 				       &requestmgr->dispatches4,
    157 				       isc_loopmgr_nloops(requestmgr->loopmgr));
    158 	}
    159 	if (dispatchv6 != NULL) {
    160 		dns_dispatchset_create(requestmgr->mctx, dispatchv6,
    161 				       &requestmgr->dispatches6,
    162 				       isc_loopmgr_nloops(requestmgr->loopmgr));
    163 	}
    164 
    165 	isc_refcount_init(&requestmgr->references, 1);
    166 
    167 	req_log(ISC_LOG_DEBUG(3), "%s: %p", __func__, requestmgr);
    168 
    169 	*requestmgrp = requestmgr;
    170 	return ISC_R_SUCCESS;
    171 }
    172 
    173 static void
    174 requests_cancel(void *arg) {
    175 	dns_requestmgr_t *requestmgr = arg;
    176 	dns_request_t *request = NULL, *next = NULL;
    177 	uint32_t tid = isc_tid();
    178 
    179 	ISC_LIST_FOREACH_SAFE(requestmgr->requests[tid], request, link, next) {
    180 		req_log(ISC_LOG_DEBUG(3), "%s(%" PRIu32 ": request %p",
    181 			__func__, tid, request);
    182 		if (DNS_REQUEST_COMPLETE(request)) {
    183 			/* The callback has been already scheduled */
    184 			continue;
    185 		}
    186 		req_sendevent(request, ISC_R_CANCELED);
    187 	}
    188 
    189 	isc_loop_unref(isc_loop_get(requestmgr->loopmgr, tid));
    190 	dns_requestmgr_detach(&requestmgr);
    191 }
    192 
    193 void
    194 dns_requestmgr_shutdown(dns_requestmgr_t *requestmgr) {
    195 	bool first;
    196 	REQUIRE(VALID_REQUESTMGR(requestmgr));
    197 
    198 	req_log(ISC_LOG_DEBUG(3), "%s: %p", __func__, requestmgr);
    199 
    200 	rcu_read_lock();
    201 	first = atomic_compare_exchange_strong(&requestmgr->shuttingdown,
    202 					       &(bool){ false }, true);
    203 	rcu_read_unlock();
    204 
    205 	if (!first) {
    206 		return;
    207 	}
    208 
    209 	/*
    210 	 * Wait until all dns_request_create{raw}() are finished, so
    211 	 * there will be no new requests added to the lists.
    212 	 */
    213 	synchronize_rcu();
    214 
    215 	uint32_t tid = isc_tid();
    216 	uint32_t nloops = isc_loopmgr_nloops(requestmgr->loopmgr);
    217 	for (size_t i = 0; i < nloops; i++) {
    218 		dns_requestmgr_ref(requestmgr);
    219 
    220 		if (i == tid) {
    221 			/* Run the current loop synchronously */
    222 			requests_cancel(requestmgr);
    223 			continue;
    224 		}
    225 
    226 		isc_loop_t *loop = isc_loop_get(requestmgr->loopmgr, i);
    227 		isc_async_run(loop, requests_cancel, requestmgr);
    228 	}
    229 }
    230 
    231 static void
    232 requestmgr_destroy(dns_requestmgr_t *requestmgr) {
    233 	req_log(ISC_LOG_DEBUG(3), "%s", __func__);
    234 
    235 	INSIST(atomic_load(&requestmgr->shuttingdown));
    236 
    237 	size_t nloops = isc_loopmgr_nloops(requestmgr->loopmgr);
    238 	for (size_t i = 0; i < nloops; i++) {
    239 		INSIST(ISC_LIST_EMPTY(requestmgr->requests[i]));
    240 	}
    241 	isc_mem_cput(requestmgr->mctx, requestmgr->requests, nloops,
    242 		     sizeof(requestmgr->requests[0]));
    243 
    244 	if (requestmgr->dispatches4 != NULL) {
    245 		dns_dispatchset_destroy(&requestmgr->dispatches4);
    246 	}
    247 	if (requestmgr->dispatches6 != NULL) {
    248 		dns_dispatchset_destroy(&requestmgr->dispatches6);
    249 	}
    250 	if (requestmgr->dispatchmgr != NULL) {
    251 		dns_dispatchmgr_detach(&requestmgr->dispatchmgr);
    252 	}
    253 	requestmgr->magic = 0;
    254 	isc_mem_putanddetach(&requestmgr->mctx, requestmgr,
    255 			     sizeof(*requestmgr));
    256 }
    257 
    258 #if DNS_REQUEST_TRACE
    259 ISC_REFCOUNT_TRACE_IMPL(dns_requestmgr, requestmgr_destroy);
    260 #else
    261 ISC_REFCOUNT_IMPL(dns_requestmgr, requestmgr_destroy);
    262 #endif
    263 
    264 static void
    265 req_send(dns_request_t *request) {
    266 	isc_region_t r;
    267 
    268 	req_log(ISC_LOG_DEBUG(3), "%s: request %p", __func__, request);
    269 
    270 	REQUIRE(VALID_REQUEST(request));
    271 
    272 	isc_buffer_usedregion(request->query, &r);
    273 
    274 	request->flags |= DNS_REQUEST_F_SENDING;
    275 
    276 	/* detached in req_senddone() */
    277 	dns_request_ref(request);
    278 	dns_dispatch_send(request->dispentry, &r);
    279 }
    280 
    281 static dns_request_t *
    282 new_request(isc_mem_t *mctx, isc_loop_t *loop, isc_job_cb cb, void *arg,
    283 	    bool tcp, unsigned int timeout, unsigned int udptimeout,
    284 	    unsigned int udpretries) {
    285 	dns_request_t *request = isc_mem_get(mctx, sizeof(*request));
    286 	*request = (dns_request_t){
    287 		.magic = REQUEST_MAGIC,
    288 		.loop = loop,
    289 		.tid = isc_tid(),
    290 		.cb = cb,
    291 		.arg = arg,
    292 		.link = ISC_LINK_INITIALIZER,
    293 		.result = ISC_R_FAILURE,
    294 		.udpcount = udpretries + 1,
    295 	};
    296 
    297 	isc_refcount_init(&request->references, 1);
    298 	isc_mem_attach(mctx, &request->mctx);
    299 
    300 	if (tcp) {
    301 		request->timeout = timeout * 1000;
    302 	} else {
    303 		if (udptimeout == 0) {
    304 			udptimeout = timeout / request->udpcount;
    305 		}
    306 		if (udptimeout == 0) {
    307 			udptimeout = 1;
    308 		}
    309 		request->timeout = udptimeout * 1000;
    310 	}
    311 
    312 	return request;
    313 }
    314 
    315 static bool
    316 isblackholed(dns_dispatchmgr_t *dispatchmgr, const isc_sockaddr_t *destaddr) {
    317 	dns_acl_t *blackhole;
    318 	isc_netaddr_t netaddr;
    319 	char netaddrstr[ISC_NETADDR_FORMATSIZE];
    320 	int match;
    321 	isc_result_t result;
    322 
    323 	blackhole = dns_dispatchmgr_getblackhole(dispatchmgr);
    324 	if (blackhole == NULL) {
    325 		return false;
    326 	}
    327 
    328 	isc_netaddr_fromsockaddr(&netaddr, destaddr);
    329 	result = dns_acl_match(&netaddr, NULL, blackhole, NULL, &match, NULL);
    330 	if (result != ISC_R_SUCCESS || match <= 0) {
    331 		return false;
    332 	}
    333 
    334 	isc_netaddr_format(&netaddr, netaddrstr, sizeof(netaddrstr));
    335 	req_log(ISC_LOG_DEBUG(10), "blackholed address %s", netaddrstr);
    336 
    337 	return true;
    338 }
    339 
    340 static isc_result_t
    341 tcp_dispatch(dns_requestmgr_t *requestmgr, const isc_sockaddr_t *srcaddr,
    342 	     const isc_sockaddr_t *destaddr, dns_transport_t *transport,
    343 	     unsigned int dispopt, dns_dispatch_t **dispatchp) {
    344 	return dns_dispatch_createtcp(
    345 		requestmgr->dispatchmgr, srcaddr, destaddr, transport,
    346 		DNS_DISPATCHTYPE_REQUEST, dispopt, dispatchp);
    347 }
    348 
    349 static isc_result_t
    350 udp_dispatch(dns_requestmgr_t *requestmgr, const isc_sockaddr_t *srcaddr,
    351 	     const isc_sockaddr_t *destaddr, dns_dispatch_t **dispatchp) {
    352 	dns_dispatch_t *disp = NULL;
    353 
    354 	if (srcaddr == NULL) {
    355 		switch (isc_sockaddr_pf(destaddr)) {
    356 		case PF_INET:
    357 			disp = dns_dispatchset_get(requestmgr->dispatches4);
    358 			break;
    359 
    360 		case PF_INET6:
    361 			disp = dns_dispatchset_get(requestmgr->dispatches6);
    362 			break;
    363 
    364 		default:
    365 			return ISC_R_NOTIMPLEMENTED;
    366 		}
    367 		if (disp == NULL) {
    368 			return ISC_R_FAMILYNOSUPPORT;
    369 		}
    370 		dns_dispatch_attach(disp, dispatchp);
    371 		return ISC_R_SUCCESS;
    372 	}
    373 
    374 	return dns_dispatch_createudp(requestmgr->dispatchmgr, srcaddr,
    375 				      dispatchp);
    376 }
    377 
    378 static isc_result_t
    379 get_dispatch(bool tcp, dns_requestmgr_t *requestmgr,
    380 	     const isc_sockaddr_t *srcaddr, const isc_sockaddr_t *destaddr,
    381 	     dns_transport_t *transport, unsigned int dispopt,
    382 	     dns_dispatch_t **dispatchp) {
    383 	isc_result_t result;
    384 
    385 	if (tcp) {
    386 		result = tcp_dispatch(requestmgr, srcaddr, destaddr, transport,
    387 				      dispopt, dispatchp);
    388 	} else {
    389 		result = udp_dispatch(requestmgr, srcaddr, destaddr, dispatchp);
    390 	}
    391 	return result;
    392 }
    393 
    394 isc_result_t
    395 dns_request_createraw(dns_requestmgr_t *requestmgr, isc_buffer_t *msgbuf,
    396 		      const isc_sockaddr_t *srcaddr,
    397 		      const isc_sockaddr_t *destaddr,
    398 		      dns_transport_t *transport,
    399 		      isc_tlsctx_cache_t *tlsctx_cache, unsigned int options,
    400 		      unsigned int timeout, unsigned int udptimeout,
    401 		      unsigned int udpretries, isc_loop_t *loop, isc_job_cb cb,
    402 		      void *arg, dns_request_t **requestp) {
    403 	dns_request_t *request = NULL;
    404 	isc_result_t result;
    405 	isc_mem_t *mctx = NULL;
    406 	dns_messageid_t id;
    407 	bool tcp = false;
    408 	isc_region_t r;
    409 	unsigned int dispopt = 0;
    410 
    411 	REQUIRE(VALID_REQUESTMGR(requestmgr));
    412 	REQUIRE(msgbuf != NULL);
    413 	REQUIRE(destaddr != NULL);
    414 	REQUIRE(loop != NULL);
    415 	REQUIRE(cb != NULL);
    416 	REQUIRE(requestp != NULL && *requestp == NULL);
    417 	REQUIRE(timeout > 0);
    418 	REQUIRE(udpretries != UINT_MAX);
    419 
    420 	if (srcaddr != NULL) {
    421 		REQUIRE(isc_sockaddr_pf(srcaddr) == isc_sockaddr_pf(destaddr));
    422 	}
    423 
    424 	mctx = requestmgr->mctx;
    425 
    426 	req_log(ISC_LOG_DEBUG(3), "%s", __func__);
    427 
    428 	rcu_read_lock();
    429 
    430 	if (atomic_load_acquire(&requestmgr->shuttingdown)) {
    431 		result = ISC_R_SHUTTINGDOWN;
    432 		goto done;
    433 	}
    434 
    435 	if (isblackholed(requestmgr->dispatchmgr, destaddr)) {
    436 		result = DNS_R_BLACKHOLED;
    437 		goto done;
    438 	}
    439 
    440 	isc_buffer_usedregion(msgbuf, &r);
    441 	if (r.length < DNS_MESSAGE_HEADERLEN || r.length > 65535) {
    442 		result = DNS_R_FORMERR;
    443 		goto done;
    444 	}
    445 
    446 	if ((options & DNS_REQUESTOPT_TCP) != 0 || r.length > 512) {
    447 		tcp = true;
    448 	}
    449 
    450 	request = new_request(mctx, loop, cb, arg, tcp, timeout, udptimeout,
    451 			      udpretries);
    452 
    453 	isc_buffer_allocate(mctx, &request->query, r.length + (tcp ? 2 : 0));
    454 	result = isc_buffer_copyregion(request->query, &r);
    455 	if (result != ISC_R_SUCCESS) {
    456 		goto cleanup;
    457 	}
    458 
    459 	if ((options & DNS_REQUESTOPT_FIXEDID) != 0) {
    460 		id = (r.base[0] << 8) | r.base[1];
    461 		dispopt |= DNS_DISPATCHOPT_FIXEDID;
    462 	}
    463 
    464 	result = get_dispatch(tcp, requestmgr, srcaddr, destaddr, transport,
    465 			      dispopt, &request->dispatch);
    466 	if (result != ISC_R_SUCCESS) {
    467 		goto cleanup;
    468 	}
    469 
    470 	result = dns_dispatch_add(
    471 		request->dispatch, loop, dispopt, request->timeout, destaddr,
    472 		transport, tlsctx_cache, req_connected, req_senddone,
    473 		req_response, request, &id, &request->dispentry);
    474 	if (result != ISC_R_SUCCESS) {
    475 		goto cleanup;
    476 	}
    477 
    478 	/* Add message ID. */
    479 	isc_buffer_usedregion(request->query, &r);
    480 	r.base[0] = (id >> 8) & 0xff;
    481 	r.base[1] = id & 0xff;
    482 
    483 	request->destaddr = *destaddr;
    484 	request->flags |= DNS_REQUEST_F_CONNECTING;
    485 	if (tcp) {
    486 		request->flags |= DNS_REQUEST_F_TCP;
    487 	}
    488 
    489 	dns_requestmgr_attach(requestmgr, &request->requestmgr);
    490 	ISC_LIST_APPEND(requestmgr->requests[request->tid], request, link);
    491 
    492 	dns_request_ref(request); /* detached in req_connected() */
    493 	result = dns_dispatch_connect(request->dispentry);
    494 	if (result != ISC_R_SUCCESS) {
    495 		dns_request_unref(request);
    496 		goto cleanup;
    497 	}
    498 
    499 	req_log(ISC_LOG_DEBUG(3), "%s: request %p", __func__, request);
    500 	*requestp = request;
    501 
    502 cleanup:
    503 	if (result != ISC_R_SUCCESS) {
    504 		req_cleanup(request);
    505 		dns_request_detach(&request);
    506 		req_log(ISC_LOG_DEBUG(3), "%s: failed %s", __func__,
    507 			isc_result_totext(result));
    508 	}
    509 
    510 done:
    511 	rcu_read_unlock();
    512 	return result;
    513 }
    514 
    515 isc_result_t
    516 dns_request_create(dns_requestmgr_t *requestmgr, dns_message_t *message,
    517 		   const isc_sockaddr_t *srcaddr,
    518 		   const isc_sockaddr_t *destaddr, dns_transport_t *transport,
    519 		   isc_tlsctx_cache_t *tlsctx_cache, unsigned int options,
    520 		   dns_tsigkey_t *key, unsigned int timeout,
    521 		   unsigned int udptimeout, unsigned int udpretries,
    522 		   isc_loop_t *loop, isc_job_cb cb, void *arg,
    523 		   dns_request_t **requestp) {
    524 	dns_request_t *request = NULL;
    525 	isc_result_t result;
    526 	isc_mem_t *mctx = NULL;
    527 	dns_messageid_t id;
    528 	bool tcp = false;
    529 
    530 	REQUIRE(VALID_REQUESTMGR(requestmgr));
    531 	REQUIRE(message != NULL);
    532 	REQUIRE(destaddr != NULL);
    533 	REQUIRE(loop != NULL);
    534 	REQUIRE(cb != NULL);
    535 	REQUIRE(requestp != NULL && *requestp == NULL);
    536 	REQUIRE(timeout > 0);
    537 	REQUIRE(udpretries != UINT_MAX);
    538 
    539 	if (srcaddr != NULL &&
    540 	    isc_sockaddr_pf(srcaddr) != isc_sockaddr_pf(destaddr))
    541 	{
    542 		return ISC_R_FAMILYMISMATCH;
    543 	}
    544 
    545 	mctx = requestmgr->mctx;
    546 
    547 	req_log(ISC_LOG_DEBUG(3), "%s", __func__);
    548 
    549 	rcu_read_lock();
    550 
    551 	if (atomic_load_acquire(&requestmgr->shuttingdown)) {
    552 		result = ISC_R_SHUTTINGDOWN;
    553 		goto done;
    554 	}
    555 
    556 	if (isblackholed(requestmgr->dispatchmgr, destaddr)) {
    557 		result = DNS_R_BLACKHOLED;
    558 		goto done;
    559 	}
    560 
    561 	if ((options & DNS_REQUESTOPT_TCP) != 0) {
    562 		tcp = true;
    563 	}
    564 
    565 	request = new_request(mctx, loop, cb, arg, tcp, timeout, udptimeout,
    566 			      udpretries);
    567 
    568 	if (key != NULL) {
    569 		dns_tsigkey_attach(key, &request->tsigkey);
    570 	}
    571 
    572 	result = dns_message_settsigkey(message, request->tsigkey);
    573 	if (result != ISC_R_SUCCESS) {
    574 		goto cleanup;
    575 	}
    576 
    577 again:
    578 	result = get_dispatch(tcp, requestmgr, srcaddr, destaddr, transport, 0,
    579 			      &request->dispatch);
    580 	if (result != ISC_R_SUCCESS) {
    581 		goto cleanup;
    582 	}
    583 
    584 	result = dns_dispatch_add(request->dispatch, loop, 0, request->timeout,
    585 				  destaddr, transport, tlsctx_cache,
    586 				  req_connected, req_senddone, req_response,
    587 				  request, &id, &request->dispentry);
    588 	if (result != ISC_R_SUCCESS) {
    589 		goto cleanup;
    590 	}
    591 
    592 	message->id = id;
    593 	result = req_render(message, &request->query, options, mctx);
    594 	if (result == DNS_R_USETCP && !tcp) {
    595 		/* Try again using TCP. */
    596 		dns_message_renderreset(message);
    597 		dns_dispatch_done(&request->dispentry);
    598 		dns_dispatch_detach(&request->dispatch);
    599 		options |= DNS_REQUESTOPT_TCP;
    600 		tcp = true;
    601 		goto again;
    602 	} else if (result != ISC_R_SUCCESS) {
    603 		goto cleanup;
    604 	}
    605 
    606 	result = dns_message_getquerytsig(message, mctx, &request->tsig);
    607 	if (result != ISC_R_SUCCESS) {
    608 		goto cleanup;
    609 	}
    610 
    611 	request->destaddr = *destaddr;
    612 	request->flags |= DNS_REQUEST_F_CONNECTING;
    613 	if (tcp) {
    614 		request->flags |= DNS_REQUEST_F_TCP;
    615 	}
    616 
    617 	dns_requestmgr_attach(requestmgr, &request->requestmgr);
    618 	ISC_LIST_APPEND(requestmgr->requests[request->tid], request, link);
    619 
    620 	dns_request_ref(request); /* detached in req_connected() */
    621 	result = dns_dispatch_connect(request->dispentry);
    622 	if (result != ISC_R_SUCCESS) {
    623 		dns_request_unref(request);
    624 		goto cleanup;
    625 	}
    626 
    627 	req_log(ISC_LOG_DEBUG(3), "%s: request %p", __func__, request);
    628 	*requestp = request;
    629 
    630 cleanup:
    631 	if (result != ISC_R_SUCCESS) {
    632 		dns_message_settsigkey(message, NULL);
    633 		req_cleanup(request);
    634 		dns_request_detach(&request);
    635 		req_log(ISC_LOG_DEBUG(3), "%s: failed %s", __func__,
    636 			isc_result_totext(result));
    637 	}
    638 done:
    639 	rcu_read_unlock();
    640 
    641 	return result;
    642 }
    643 
    644 static isc_result_t
    645 req_render(dns_message_t *message, isc_buffer_t **bufferp, unsigned int options,
    646 	   isc_mem_t *mctx) {
    647 	isc_buffer_t *buf1 = NULL;
    648 	isc_buffer_t *buf2 = NULL;
    649 	isc_result_t result;
    650 	isc_region_t r;
    651 	dns_compress_t cctx;
    652 	unsigned int compflags;
    653 
    654 	REQUIRE(bufferp != NULL && *bufferp == NULL);
    655 
    656 	req_log(ISC_LOG_DEBUG(3), "%s", __func__);
    657 
    658 	/*
    659 	 * Create buffer able to hold largest possible message.
    660 	 */
    661 	isc_buffer_allocate(mctx, &buf1, 65535);
    662 
    663 	compflags = 0;
    664 	if ((options & DNS_REQUESTOPT_LARGE) != 0) {
    665 		compflags |= DNS_COMPRESS_LARGE;
    666 	}
    667 	if ((options & DNS_REQUESTOPT_CASE) != 0) {
    668 		compflags |= DNS_COMPRESS_CASE;
    669 	}
    670 	dns_compress_init(&cctx, mctx, compflags);
    671 
    672 	/*
    673 	 * Render message.
    674 	 */
    675 	result = dns_message_renderbegin(message, &cctx, buf1);
    676 	if (result != ISC_R_SUCCESS) {
    677 		goto cleanup;
    678 	}
    679 	result = dns_message_rendersection(message, DNS_SECTION_QUESTION, 0);
    680 	if (result != ISC_R_SUCCESS) {
    681 		goto cleanup;
    682 	}
    683 	result = dns_message_rendersection(message, DNS_SECTION_ANSWER, 0);
    684 	if (result != ISC_R_SUCCESS) {
    685 		goto cleanup;
    686 	}
    687 	result = dns_message_rendersection(message, DNS_SECTION_AUTHORITY, 0);
    688 	if (result != ISC_R_SUCCESS) {
    689 		goto cleanup;
    690 	}
    691 	result = dns_message_rendersection(message, DNS_SECTION_ADDITIONAL, 0);
    692 	if (result != ISC_R_SUCCESS) {
    693 		goto cleanup;
    694 	}
    695 	result = dns_message_renderend(message);
    696 	if (result != ISC_R_SUCCESS) {
    697 		goto cleanup;
    698 	}
    699 
    700 	/*
    701 	 * Copy rendered message to exact sized buffer.
    702 	 */
    703 	isc_buffer_usedregion(buf1, &r);
    704 	if ((options & DNS_REQUESTOPT_TCP) == 0 && r.length > 512) {
    705 		result = DNS_R_USETCP;
    706 		goto cleanup;
    707 	}
    708 	isc_buffer_allocate(mctx, &buf2, r.length);
    709 	result = isc_buffer_copyregion(buf2, &r);
    710 	if (result != ISC_R_SUCCESS) {
    711 		goto cleanup;
    712 	}
    713 
    714 	/*
    715 	 * Cleanup and return.
    716 	 */
    717 	dns_compress_invalidate(&cctx);
    718 	isc_buffer_free(&buf1);
    719 	*bufferp = buf2;
    720 	return ISC_R_SUCCESS;
    721 
    722 cleanup:
    723 	dns_message_renderreset(message);
    724 	dns_compress_invalidate(&cctx);
    725 	if (buf1 != NULL) {
    726 		isc_buffer_free(&buf1);
    727 	}
    728 	if (buf2 != NULL) {
    729 		isc_buffer_free(&buf2);
    730 	}
    731 	return result;
    732 }
    733 
    734 static void
    735 request_cancel(dns_request_t *request) {
    736 	REQUIRE(VALID_REQUEST(request));
    737 	REQUIRE(request->tid == isc_tid());
    738 
    739 	if (DNS_REQUEST_COMPLETE(request)) {
    740 		/* The request callback was already called */
    741 		return;
    742 	}
    743 
    744 	req_log(ISC_LOG_DEBUG(3), "%s: request %p", __func__, request);
    745 	req_sendevent(request, ISC_R_CANCELED); /* call asynchronously */
    746 }
    747 
    748 static void
    749 req_cancel_cb(void *arg) {
    750 	dns_request_t *request = arg;
    751 
    752 	request_cancel(request);
    753 	dns_request_unref(request);
    754 }
    755 
    756 void
    757 dns_request_cancel(dns_request_t *request) {
    758 	REQUIRE(VALID_REQUEST(request));
    759 
    760 	if (request->tid == isc_tid()) {
    761 		request_cancel(request);
    762 	} else {
    763 		dns_request_ref(request);
    764 		isc_async_run(request->loop, req_cancel_cb, request);
    765 	}
    766 }
    767 
    768 isc_result_t
    769 dns_request_getresponse(dns_request_t *request, dns_message_t *message,
    770 			unsigned int options) {
    771 	isc_result_t result;
    772 
    773 	REQUIRE(VALID_REQUEST(request));
    774 	REQUIRE(request->tid == isc_tid());
    775 	REQUIRE(request->answer != NULL);
    776 
    777 	req_log(ISC_LOG_DEBUG(3), "%s: request %p", __func__, request);
    778 
    779 	dns_message_setquerytsig(message, request->tsig);
    780 	result = dns_message_settsigkey(message, request->tsigkey);
    781 	if (result != ISC_R_SUCCESS) {
    782 		return result;
    783 	}
    784 	result = dns_message_parse(message, request->answer, options);
    785 	if (result != ISC_R_SUCCESS) {
    786 		return result;
    787 	}
    788 	if (request->tsigkey != NULL) {
    789 		result = dns_tsig_verify(request->answer, message, NULL, NULL);
    790 	}
    791 	return result;
    792 }
    793 
    794 isc_buffer_t *
    795 dns_request_getanswer(dns_request_t *request) {
    796 	REQUIRE(VALID_REQUEST(request));
    797 	REQUIRE(request->tid == isc_tid());
    798 
    799 	return request->answer;
    800 }
    801 
    802 bool
    803 dns_request_usedtcp(dns_request_t *request) {
    804 	REQUIRE(VALID_REQUEST(request));
    805 	REQUIRE(request->tid == isc_tid());
    806 
    807 	return (request->flags & DNS_REQUEST_F_TCP) != 0;
    808 }
    809 
    810 void
    811 dns_request_destroy(dns_request_t **requestp) {
    812 	REQUIRE(requestp != NULL && VALID_REQUEST(*requestp));
    813 
    814 	dns_request_t *request = *requestp;
    815 	*requestp = NULL;
    816 
    817 	req_log(ISC_LOG_DEBUG(3), "%s: request %p", __func__, request);
    818 
    819 	if (DNS_REQUEST_COMPLETE(request)) {
    820 		dns_request_cancel(request);
    821 	}
    822 
    823 	/* final detach to shut down request */
    824 	dns_request_detach(&request);
    825 }
    826 
    827 static void
    828 req_connected(isc_result_t eresult, isc_region_t *region ISC_ATTR_UNUSED,
    829 	      void *arg) {
    830 	dns_request_t *request = (dns_request_t *)arg;
    831 
    832 	REQUIRE(VALID_REQUEST(request));
    833 	REQUIRE(request->tid == isc_tid());
    834 	REQUIRE(DNS_REQUEST_CONNECTING(request));
    835 
    836 	req_log(ISC_LOG_DEBUG(3), "%s: request %p: %s", __func__, request,
    837 		isc_result_totext(eresult));
    838 
    839 	request->flags &= ~DNS_REQUEST_F_CONNECTING;
    840 
    841 	if (DNS_REQUEST_COMPLETE(request)) {
    842 		/* The request callback was already called */
    843 		goto detach;
    844 	}
    845 
    846 	if (eresult == ISC_R_SUCCESS) {
    847 		req_send(request);
    848 	} else {
    849 		req_sendevent(request, eresult);
    850 	}
    851 
    852 detach:
    853 	/* attached in dns_request_create/_createraw() */
    854 	dns_request_unref(request);
    855 }
    856 
    857 static void
    858 req_senddone(isc_result_t eresult, isc_region_t *region ISC_ATTR_UNUSED,
    859 	     void *arg) {
    860 	dns_request_t *request = (dns_request_t *)arg;
    861 
    862 	REQUIRE(VALID_REQUEST(request));
    863 	REQUIRE(request->tid == isc_tid());
    864 	REQUIRE(DNS_REQUEST_SENDING(request));
    865 
    866 	req_log(ISC_LOG_DEBUG(3), "%s: request %p", __func__, request);
    867 
    868 	request->flags &= ~DNS_REQUEST_F_SENDING;
    869 
    870 	if (DNS_REQUEST_COMPLETE(request)) {
    871 		/* The request callback was already called */
    872 		goto detach;
    873 	}
    874 
    875 	if (eresult != ISC_R_SUCCESS) {
    876 		req_sendevent(request, eresult);
    877 	}
    878 
    879 detach:
    880 	/* attached in req_send() */
    881 	dns_request_unref(request);
    882 }
    883 
    884 static void
    885 req_response(isc_result_t eresult, isc_region_t *region, void *arg) {
    886 	dns_request_t *request = (dns_request_t *)arg;
    887 
    888 	if (eresult == ISC_R_CANCELED) {
    889 		return;
    890 	}
    891 
    892 	REQUIRE(VALID_REQUEST(request));
    893 	REQUIRE(request->tid == isc_tid());
    894 
    895 	req_log(ISC_LOG_DEBUG(3), "%s: request %p: %s", __func__, request,
    896 		isc_result_totext(eresult));
    897 
    898 	if (DNS_REQUEST_COMPLETE(request)) {
    899 		/* The request callback was already called */
    900 		return;
    901 	}
    902 
    903 	switch (eresult) {
    904 	case ISC_R_TIMEDOUT:
    905 		if (request->udpcount > 1 && !dns_request_usedtcp(request)) {
    906 			request->udpcount -= 1;
    907 			dns_dispatch_resume(request->dispentry,
    908 					    request->timeout);
    909 			if (!DNS_REQUEST_SENDING(request)) {
    910 				req_send(request);
    911 			}
    912 			return;
    913 		}
    914 		break;
    915 	case ISC_R_SUCCESS:
    916 		/* Copy region to request. */
    917 		isc_buffer_allocate(request->mctx, &request->answer,
    918 				    region->length);
    919 		eresult = isc_buffer_copyregion(request->answer, region);
    920 		if (eresult != ISC_R_SUCCESS) {
    921 			isc_buffer_free(&request->answer);
    922 		}
    923 		break;
    924 	default:
    925 		break;
    926 	}
    927 
    928 	req_sendevent(request, eresult);
    929 }
    930 
    931 static void
    932 req_sendevent_cb(void *arg) {
    933 	dns_request_t *request = arg;
    934 
    935 	request->cb(request);
    936 	dns_request_unref(request);
    937 }
    938 
    939 static void
    940 req_cleanup(dns_request_t *request) {
    941 	if (ISC_LINK_LINKED(request, link)) {
    942 		ISC_LIST_UNLINK(request->requestmgr->requests[request->tid],
    943 				request, link);
    944 	}
    945 	if (request->dispentry != NULL) {
    946 		dns_dispatch_done(&request->dispentry);
    947 	}
    948 	if (request->dispatch != NULL) {
    949 		dns_dispatch_detach(&request->dispatch);
    950 	}
    951 }
    952 
    953 static void
    954 req_sendevent(dns_request_t *request, isc_result_t result) {
    955 	REQUIRE(VALID_REQUEST(request));
    956 	REQUIRE(request->tid == isc_tid());
    957 	REQUIRE(!DNS_REQUEST_COMPLETE(request));
    958 
    959 	request->flags |= DNS_REQUEST_F_COMPLETE;
    960 
    961 	req_cleanup(request);
    962 
    963 	req_log(ISC_LOG_DEBUG(3), "%s: request %p: %s", __func__, request,
    964 		isc_result_totext(result));
    965 
    966 	request->result = result;
    967 
    968 	/*
    969 	 * Do not call request->cb directly as it introduces a dead lock
    970 	 * between dns_zonemgr_shutdown and sendtoprimary in lib/dns/zone.c
    971 	 * zone->lock.
    972 	 */
    973 	dns_request_ref(request);
    974 	isc_async_run(request->loop, req_sendevent_cb, request);
    975 }
    976 
    977 static void
    978 req_destroy(dns_request_t *request) {
    979 	REQUIRE(VALID_REQUEST(request));
    980 	REQUIRE(request->tid == isc_tid());
    981 	REQUIRE(!ISC_LINK_LINKED(request, link));
    982 
    983 	req_log(ISC_LOG_DEBUG(3), "%s: request %p", __func__, request);
    984 
    985 	/*
    986 	 * These should have been cleaned up before the
    987 	 * completion event was sent.
    988 	 */
    989 	INSIST(!ISC_LINK_LINKED(request, link));
    990 	INSIST(request->dispentry == NULL);
    991 	INSIST(request->dispatch == NULL);
    992 
    993 	request->magic = 0;
    994 	if (request->query != NULL) {
    995 		isc_buffer_free(&request->query);
    996 	}
    997 	if (request->answer != NULL) {
    998 		isc_buffer_free(&request->answer);
    999 	}
   1000 	if (request->tsig != NULL) {
   1001 		isc_buffer_free(&request->tsig);
   1002 	}
   1003 	if (request->tsigkey != NULL) {
   1004 		dns_tsigkey_detach(&request->tsigkey);
   1005 	}
   1006 	if (request->requestmgr != NULL) {
   1007 		dns_requestmgr_detach(&request->requestmgr);
   1008 	}
   1009 	isc_mem_putanddetach(&request->mctx, request, sizeof(*request));
   1010 }
   1011 
   1012 void *
   1013 dns_request_getarg(dns_request_t *request) {
   1014 	REQUIRE(VALID_REQUEST(request));
   1015 	REQUIRE(request->tid == isc_tid());
   1016 
   1017 	return request->arg;
   1018 }
   1019 
   1020 isc_result_t
   1021 dns_request_getresult(dns_request_t *request) {
   1022 	REQUIRE(VALID_REQUEST(request));
   1023 	REQUIRE(request->tid == isc_tid());
   1024 
   1025 	return request->result;
   1026 }
   1027 
   1028 #if DNS_REQUEST_TRACE
   1029 ISC_REFCOUNT_TRACE_IMPL(dns_request, req_destroy);
   1030 #else
   1031 ISC_REFCOUNT_IMPL(dns_request, req_destroy);
   1032 #endif
   1033 
   1034 static void
   1035 req_log(int level, const char *fmt, ...) {
   1036 	va_list ap;
   1037 
   1038 	va_start(ap, fmt);
   1039 	isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_REQUEST,
   1040 		       level, fmt, ap);
   1041 	va_end(ap);
   1042 }
   1043