xref: /netbsd-src/external/mpl/bind/dist/lib/dns/client.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: client.c,v 1.15 2025/01/26 16:25:22 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 #include <stdbool.h>
17 #include <stddef.h>
18 
19 #include <isc/async.h>
20 #include <isc/buffer.h>
21 #include <isc/loop.h>
22 #include <isc/md.h>
23 #include <isc/mem.h>
24 #include <isc/mutex.h>
25 #include <isc/netmgr.h>
26 #include <isc/portset.h>
27 #include <isc/refcount.h>
28 #include <isc/result.h>
29 #include <isc/safe.h>
30 #include <isc/sockaddr.h>
31 #include <isc/util.h>
32 
33 #include <dns/adb.h>
34 #include <dns/client.h>
35 #include <dns/db.h>
36 #include <dns/dispatch.h>
37 #include <dns/forward.h>
38 #include <dns/keytable.h>
39 #include <dns/message.h>
40 #include <dns/name.h>
41 #include <dns/rdata.h>
42 #include <dns/rdatalist.h>
43 #include <dns/rdataset.h>
44 #include <dns/rdatasetiter.h>
45 #include <dns/rdatastruct.h>
46 #include <dns/rdatatype.h>
47 #include <dns/request.h>
48 #include <dns/resolver.h>
49 #include <dns/tsig.h>
50 #include <dns/view.h>
51 
52 #include <dst/dst.h>
53 
54 #define DNS_CLIENT_MAGIC    ISC_MAGIC('D', 'N', 'S', 'c')
55 #define DNS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, DNS_CLIENT_MAGIC)
56 
57 #define RCTX_MAGIC    ISC_MAGIC('R', 'c', 't', 'x')
58 #define RCTX_VALID(c) ISC_MAGIC_VALID(c, RCTX_MAGIC)
59 
60 #define UCTX_MAGIC    ISC_MAGIC('U', 'c', 't', 'x')
61 #define UCTX_VALID(c) ISC_MAGIC_VALID(c, UCTX_MAGIC)
62 
63 #define CHECK(r)                             \
64 	do {                                 \
65 		result = (r);                \
66 		if (result != ISC_R_SUCCESS) \
67 			goto cleanup;        \
68 	} while (0)
69 
70 /*%
71  * DNS client object
72  */
73 struct dns_client {
74 	unsigned int magic;
75 	unsigned int attributes;
76 	isc_mem_t *mctx;
77 	isc_loop_t *loop;
78 	isc_nm_t *nm;
79 	dns_dispatchmgr_t *dispatchmgr;
80 	dns_dispatch_t *dispatchv4;
81 	dns_dispatch_t *dispatchv6;
82 
83 	unsigned int find_timeout;
84 	unsigned int find_udpretries;
85 	uint8_t max_restarts;
86 
87 	isc_refcount_t references;
88 
89 	dns_view_t *view;
90 	ISC_LIST(struct resctx) resctxs;
91 };
92 
93 #define DEF_FIND_TIMEOUT    5
94 #define DEF_FIND_UDPRETRIES 3
95 #define DEF_MAX_RESTARTS    11
96 
97 /*%
98  * Internal state for a single name resolution procedure
99  */
100 typedef struct resctx {
101 	unsigned int magic;
102 	dns_client_t *client;
103 	bool want_dnssec;
104 	bool want_validation;
105 	bool want_cdflag;
106 	bool want_tcp;
107 
108 	ISC_LINK(struct resctx) link;
109 	dns_view_t *view;
110 	unsigned int restarts;
111 	dns_fixedname_t name;
112 	dns_rdatatype_t type;
113 	dns_fetch_t *fetch;
114 	dns_namelist_t namelist;
115 	isc_result_t result;
116 	dns_clientresume_t *rev;
117 	dns_rdataset_t *rdataset;
118 	dns_rdataset_t *sigrdataset;
119 } resctx_t;
120 
121 /*%
122  * Argument of an internal event for synchronous name resolution.
123  */
124 typedef struct resarg {
125 	isc_mem_t *mctx;
126 	dns_client_t *client;
127 	const dns_name_t *name;
128 
129 	isc_result_t result;
130 	isc_result_t vresult;
131 	dns_namelist_t *namelist;
132 	dns_clientrestrans_t *trans;
133 	dns_client_resolve_cb resolve_cb;
134 } resarg_t;
135 
136 static void
137 client_resfind(resctx_t *rctx, dns_fetchresponse_t *event);
138 static void
139 destroyrestrans(dns_clientrestrans_t **transp);
140 
141 /*
142  * Try honoring the operating system's preferred ephemeral port range.
143  */
144 static isc_result_t
145 setsourceports(isc_mem_t *mctx, dns_dispatchmgr_t *manager) {
146 	isc_portset_t *v4portset = NULL, *v6portset = NULL;
147 	in_port_t udpport_low, udpport_high;
148 	isc_result_t result;
149 
150 	result = isc_portset_create(mctx, &v4portset);
151 	if (result != ISC_R_SUCCESS) {
152 		goto cleanup;
153 	}
154 	result = isc_net_getudpportrange(AF_INET, &udpport_low, &udpport_high);
155 	if (result != ISC_R_SUCCESS) {
156 		goto cleanup;
157 	}
158 	isc_portset_addrange(v4portset, udpport_low, udpport_high);
159 
160 	result = isc_portset_create(mctx, &v6portset);
161 	if (result != ISC_R_SUCCESS) {
162 		goto cleanup;
163 	}
164 	result = isc_net_getudpportrange(AF_INET6, &udpport_low, &udpport_high);
165 	if (result != ISC_R_SUCCESS) {
166 		goto cleanup;
167 	}
168 	isc_portset_addrange(v6portset, udpport_low, udpport_high);
169 
170 	result = dns_dispatchmgr_setavailports(manager, v4portset, v6portset);
171 
172 cleanup:
173 	if (v4portset != NULL) {
174 		isc_portset_destroy(mctx, &v4portset);
175 	}
176 	if (v6portset != NULL) {
177 		isc_portset_destroy(mctx, &v6portset);
178 	}
179 
180 	return result;
181 }
182 
183 static isc_result_t
184 getudpdispatch(int family, dns_dispatchmgr_t *dispatchmgr,
185 	       dns_dispatch_t **dispp, const isc_sockaddr_t *localaddr) {
186 	dns_dispatch_t *disp = NULL;
187 	isc_result_t result;
188 	isc_sockaddr_t anyaddr;
189 
190 	if (localaddr == NULL) {
191 		isc_sockaddr_anyofpf(&anyaddr, family);
192 		localaddr = &anyaddr;
193 	}
194 
195 	result = dns_dispatch_createudp(dispatchmgr, localaddr, &disp);
196 	if (result == ISC_R_SUCCESS) {
197 		*dispp = disp;
198 	}
199 
200 	return result;
201 }
202 
203 static isc_result_t
204 createview(isc_mem_t *mctx, dns_rdataclass_t rdclass, isc_nm_t *nm,
205 	   isc_tlsctx_cache_t *tlsctx_client_cache, isc_loopmgr_t *loopmgr,
206 	   dns_dispatchmgr_t *dispatchmgr, dns_dispatch_t *dispatchv4,
207 	   dns_dispatch_t *dispatchv6, dns_view_t **viewp) {
208 	isc_result_t result;
209 	dns_view_t *view = NULL;
210 
211 	result = dns_view_create(mctx, loopmgr, dispatchmgr, rdclass,
212 				 DNS_CLIENTVIEW_NAME, &view);
213 	if (result != ISC_R_SUCCESS) {
214 		return result;
215 	}
216 
217 	/* Initialize view security roots */
218 	dns_view_initsecroots(view);
219 
220 	CHECK(dns_view_createresolver(view, nm, 0, tlsctx_client_cache,
221 				      dispatchv4, dispatchv6));
222 	CHECK(dns_db_create(mctx, CACHEDB_DEFAULT, dns_rootname,
223 			    dns_dbtype_cache, rdclass, 0, NULL,
224 			    &view->cachedb));
225 
226 	*viewp = view;
227 	return ISC_R_SUCCESS;
228 
229 cleanup:
230 	dns_view_detach(&view);
231 	return result;
232 }
233 
234 isc_result_t
235 dns_client_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr, isc_nm_t *nm,
236 		  unsigned int options, isc_tlsctx_cache_t *tlsctx_client_cache,
237 		  dns_client_t **clientp, const isc_sockaddr_t *localaddr4,
238 		  const isc_sockaddr_t *localaddr6) {
239 	isc_result_t result;
240 	dns_client_t *client = NULL;
241 	dns_dispatch_t *dispatchv4 = NULL;
242 	dns_dispatch_t *dispatchv6 = NULL;
243 	dns_view_t *view = NULL;
244 
245 	REQUIRE(mctx != NULL);
246 	REQUIRE(nm != NULL);
247 	REQUIRE(tlsctx_client_cache != NULL);
248 	REQUIRE(clientp != NULL && *clientp == NULL);
249 
250 	UNUSED(options);
251 
252 	client = isc_mem_get(mctx, sizeof(*client));
253 	*client = (dns_client_t){
254 		.loop = isc_loop_get(loopmgr, 0),
255 		.nm = nm,
256 		.max_restarts = DEF_MAX_RESTARTS,
257 	};
258 
259 	result = dns_dispatchmgr_create(mctx, loopmgr, nm,
260 					&client->dispatchmgr);
261 	if (result != ISC_R_SUCCESS) {
262 		goto cleanup_client;
263 	}
264 	(void)setsourceports(mctx, client->dispatchmgr);
265 
266 	/*
267 	 * If only one address family is specified, use it.
268 	 * If neither family is specified, or if both are, use both.
269 	 */
270 	client->dispatchv4 = NULL;
271 	if (localaddr4 != NULL || localaddr6 == NULL) {
272 		result = getudpdispatch(AF_INET, client->dispatchmgr,
273 					&dispatchv4, localaddr4);
274 		if (result == ISC_R_SUCCESS) {
275 			client->dispatchv4 = dispatchv4;
276 		}
277 	}
278 
279 	client->dispatchv6 = NULL;
280 	if (localaddr6 != NULL || localaddr4 == NULL) {
281 		result = getudpdispatch(AF_INET6, client->dispatchmgr,
282 					&dispatchv6, localaddr6);
283 		if (result == ISC_R_SUCCESS) {
284 			client->dispatchv6 = dispatchv6;
285 		}
286 	}
287 
288 	/* We need at least one of the dispatchers */
289 	if (dispatchv4 == NULL && dispatchv6 == NULL) {
290 		INSIST(result != ISC_R_SUCCESS);
291 		goto cleanup_dispatchmgr;
292 	}
293 
294 	isc_refcount_init(&client->references, 1);
295 
296 	/* Create the default view for class IN */
297 	result = createview(mctx, dns_rdataclass_in, nm, tlsctx_client_cache,
298 			    isc_loop_getloopmgr(client->loop),
299 			    client->dispatchmgr, dispatchv4, dispatchv6, &view);
300 	if (result != ISC_R_SUCCESS) {
301 		goto cleanup_references;
302 	}
303 
304 	client->view = view;
305 
306 	dns_view_freeze(view); /* too early? */
307 
308 	ISC_LIST_INIT(client->resctxs);
309 
310 	isc_mem_attach(mctx, &client->mctx);
311 
312 	client->find_timeout = DEF_FIND_TIMEOUT;
313 	client->find_udpretries = DEF_FIND_UDPRETRIES;
314 
315 	client->magic = DNS_CLIENT_MAGIC;
316 
317 	*clientp = client;
318 
319 	return ISC_R_SUCCESS;
320 
321 cleanup_references:
322 	isc_refcount_decrementz(&client->references);
323 	isc_refcount_destroy(&client->references);
324 cleanup_dispatchmgr:
325 	if (dispatchv4 != NULL) {
326 		dns_dispatch_detach(&dispatchv4);
327 	}
328 	if (dispatchv6 != NULL) {
329 		dns_dispatch_detach(&dispatchv6);
330 	}
331 	dns_dispatchmgr_detach(&client->dispatchmgr);
332 cleanup_client:
333 	isc_mem_put(mctx, client, sizeof(*client));
334 
335 	return result;
336 }
337 
338 static void
339 destroyclient(dns_client_t *client) {
340 	isc_refcount_destroy(&client->references);
341 
342 	dns_view_detach(&client->view);
343 
344 	if (client->dispatchv4 != NULL) {
345 		dns_dispatch_detach(&client->dispatchv4);
346 	}
347 	if (client->dispatchv6 != NULL) {
348 		dns_dispatch_detach(&client->dispatchv6);
349 	}
350 
351 	dns_dispatchmgr_detach(&client->dispatchmgr);
352 
353 	client->magic = 0;
354 
355 	isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
356 }
357 
358 void
359 dns_client_detach(dns_client_t **clientp) {
360 	dns_client_t *client = NULL;
361 
362 	REQUIRE(clientp != NULL);
363 	REQUIRE(DNS_CLIENT_VALID(*clientp));
364 
365 	client = *clientp;
366 	*clientp = NULL;
367 
368 	if (isc_refcount_decrement(&client->references) == 1) {
369 		destroyclient(client);
370 	}
371 }
372 
373 isc_result_t
374 dns_client_setservers(dns_client_t *client, dns_rdataclass_t rdclass,
375 		      const dns_name_t *name_space, isc_sockaddrlist_t *addrs) {
376 	isc_result_t result;
377 
378 	REQUIRE(DNS_CLIENT_VALID(client));
379 	REQUIRE(addrs != NULL);
380 	REQUIRE(rdclass == dns_rdataclass_in);
381 
382 	if (name_space == NULL) {
383 		name_space = dns_rootname;
384 	}
385 
386 	result = dns_fwdtable_add(client->view->fwdtable, name_space, addrs,
387 				  dns_fwdpolicy_only);
388 
389 	return result;
390 }
391 
392 void
393 dns_client_setmaxrestarts(dns_client_t *client, uint8_t max_restarts) {
394 	REQUIRE(DNS_CLIENT_VALID(client));
395 	REQUIRE(max_restarts > 0);
396 
397 	client->max_restarts = max_restarts;
398 }
399 
400 static isc_result_t
401 getrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) {
402 	dns_rdataset_t *rdataset;
403 
404 	REQUIRE(mctx != NULL);
405 	REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
406 
407 	rdataset = isc_mem_get(mctx, sizeof(*rdataset));
408 
409 	dns_rdataset_init(rdataset);
410 
411 	*rdatasetp = rdataset;
412 
413 	return ISC_R_SUCCESS;
414 }
415 
416 static void
417 putrdataset(isc_mem_t *mctx, dns_rdataset_t **rdatasetp) {
418 	dns_rdataset_t *rdataset;
419 
420 	REQUIRE(rdatasetp != NULL);
421 	rdataset = *rdatasetp;
422 	*rdatasetp = NULL;
423 	REQUIRE(rdataset != NULL);
424 
425 	if (dns_rdataset_isassociated(rdataset)) {
426 		dns_rdataset_disassociate(rdataset);
427 	}
428 
429 	isc_mem_put(mctx, rdataset, sizeof(*rdataset));
430 }
431 
432 static void
433 fetch_done(void *arg) {
434 	dns_fetchresponse_t *resp = (dns_fetchresponse_t *)arg;
435 	resctx_t *rctx = resp->arg;
436 
437 	REQUIRE(RCTX_VALID(rctx));
438 
439 	client_resfind(rctx, resp);
440 }
441 
442 static isc_result_t
443 start_fetch(resctx_t *rctx) {
444 	isc_result_t result;
445 	int fopts = 0;
446 
447 	REQUIRE(rctx->fetch == NULL);
448 
449 	if (!rctx->want_cdflag) {
450 		fopts |= DNS_FETCHOPT_NOCDFLAG;
451 	}
452 	if (!rctx->want_validation) {
453 		fopts |= DNS_FETCHOPT_NOVALIDATE;
454 	}
455 	if (rctx->want_tcp) {
456 		fopts |= DNS_FETCHOPT_TCP;
457 	}
458 
459 	result = dns_resolver_createfetch(
460 		rctx->view->resolver, dns_fixedname_name(&rctx->name),
461 		rctx->type, NULL, NULL, NULL, NULL, 0, fopts, 0, NULL,
462 		rctx->client->loop, fetch_done, rctx, rctx->rdataset,
463 		rctx->sigrdataset, &rctx->fetch);
464 
465 	return result;
466 }
467 
468 static isc_result_t
469 view_find(resctx_t *rctx, dns_db_t **dbp, dns_dbnode_t **nodep,
470 	  dns_name_t *foundname) {
471 	isc_result_t result;
472 	dns_name_t *name = dns_fixedname_name(&rctx->name);
473 	dns_rdatatype_t type;
474 
475 	if (rctx->type == dns_rdatatype_rrsig) {
476 		type = dns_rdatatype_any;
477 	} else {
478 		type = rctx->type;
479 	}
480 
481 	result = dns_view_find(rctx->view, name, type, 0, 0, false, false, dbp,
482 			       nodep, foundname, rctx->rdataset,
483 			       rctx->sigrdataset);
484 
485 	return result;
486 }
487 
488 static void
489 client_resfind(resctx_t *rctx, dns_fetchresponse_t *resp) {
490 	isc_mem_t *mctx = NULL;
491 	isc_result_t tresult, result = ISC_R_SUCCESS;
492 	isc_result_t vresult = ISC_R_SUCCESS;
493 	bool want_restart;
494 	bool send_event = false;
495 	dns_name_t *name = NULL, *prefix = NULL;
496 	dns_fixedname_t foundname, fixed;
497 	dns_rdataset_t *trdataset = NULL;
498 	dns_rdata_t rdata = DNS_RDATA_INIT;
499 	unsigned int nlabels;
500 	int order;
501 	dns_namereln_t namereln;
502 	dns_rdata_cname_t cname;
503 	dns_rdata_dname_t dname;
504 
505 	REQUIRE(RCTX_VALID(rctx));
506 
507 	mctx = rctx->view->mctx;
508 
509 	name = dns_fixedname_name(&rctx->name);
510 
511 	do {
512 		dns_name_t *fname = NULL;
513 		dns_name_t *ansname = NULL;
514 		dns_db_t *db = NULL;
515 		dns_dbnode_t *node = NULL;
516 
517 		rctx->restarts++;
518 		want_restart = false;
519 
520 		if (resp == NULL) {
521 			fname = dns_fixedname_initname(&foundname);
522 			INSIST(!dns_rdataset_isassociated(rctx->rdataset));
523 			INSIST(rctx->sigrdataset == NULL ||
524 			       !dns_rdataset_isassociated(rctx->sigrdataset));
525 			result = view_find(rctx, &db, &node, fname);
526 			if (result == ISC_R_NOTFOUND) {
527 				/*
528 				 * We don't know anything about the name.
529 				 * Launch a fetch.
530 				 */
531 				if (node != NULL) {
532 					INSIST(db != NULL);
533 					dns_db_detachnode(db, &node);
534 				}
535 				if (db != NULL) {
536 					dns_db_detach(&db);
537 				}
538 				result = start_fetch(rctx);
539 				if (result != ISC_R_SUCCESS) {
540 					putrdataset(mctx, &rctx->rdataset);
541 					if (rctx->sigrdataset != NULL) {
542 						putrdataset(mctx,
543 							    &rctx->sigrdataset);
544 					}
545 					send_event = true;
546 				}
547 				goto done;
548 			}
549 		} else {
550 			INSIST(resp != NULL);
551 			INSIST(resp->fetch == rctx->fetch);
552 			dns_resolver_destroyfetch(&rctx->fetch);
553 			db = resp->db;
554 			node = resp->node;
555 			result = resp->result;
556 			vresult = resp->vresult;
557 			fname = resp->foundname;
558 			INSIST(resp->rdataset == rctx->rdataset);
559 			INSIST(resp->sigrdataset == rctx->sigrdataset);
560 			isc_mem_putanddetach(&resp->mctx, resp, sizeof(*resp));
561 		}
562 
563 		/*
564 		 * Get some resource for copying the
565 		 * result.
566 		 */
567 		dns_name_t *aname = dns_fixedname_name(&rctx->name);
568 
569 		ansname = isc_mem_get(mctx, sizeof(*ansname));
570 		dns_name_init(ansname, NULL);
571 
572 		dns_name_dup(aname, mctx, ansname);
573 
574 		switch (result) {
575 		case ISC_R_SUCCESS:
576 			send_event = true;
577 			/*
578 			 * This case is handled in the main line below.
579 			 */
580 			break;
581 		case DNS_R_CNAME:
582 			/*
583 			 * Add the CNAME to the answer list.
584 			 */
585 			trdataset = rctx->rdataset;
586 			ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
587 			rctx->rdataset = NULL;
588 			if (rctx->sigrdataset != NULL) {
589 				ISC_LIST_APPEND(ansname->list,
590 						rctx->sigrdataset, link);
591 				rctx->sigrdataset = NULL;
592 			}
593 			ISC_LIST_APPEND(rctx->namelist, ansname, link);
594 			ansname = NULL;
595 
596 			/*
597 			 * Copy the CNAME's target into the lookup's
598 			 * query name and start over.
599 			 */
600 			tresult = dns_rdataset_first(trdataset);
601 			if (tresult != ISC_R_SUCCESS) {
602 				goto done;
603 			}
604 			dns_rdataset_current(trdataset, &rdata);
605 			tresult = dns_rdata_tostruct(&rdata, &cname, NULL);
606 			dns_rdata_reset(&rdata);
607 			if (tresult != ISC_R_SUCCESS) {
608 				goto done;
609 			}
610 			dns_name_copy(&cname.cname, name);
611 			dns_rdata_freestruct(&cname);
612 			want_restart = true;
613 			goto done;
614 		case DNS_R_DNAME:
615 			/*
616 			 * Add the DNAME to the answer list.
617 			 */
618 			trdataset = rctx->rdataset;
619 			ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
620 			rctx->rdataset = NULL;
621 			if (rctx->sigrdataset != NULL) {
622 				ISC_LIST_APPEND(ansname->list,
623 						rctx->sigrdataset, link);
624 				rctx->sigrdataset = NULL;
625 			}
626 			ISC_LIST_APPEND(rctx->namelist, ansname, link);
627 			ansname = NULL;
628 
629 			namereln = dns_name_fullcompare(name, fname, &order,
630 							&nlabels);
631 			INSIST(namereln == dns_namereln_subdomain);
632 			/*
633 			 * Get the target name of the DNAME.
634 			 */
635 			tresult = dns_rdataset_first(trdataset);
636 			if (tresult != ISC_R_SUCCESS) {
637 				result = tresult;
638 				goto done;
639 			}
640 			dns_rdataset_current(trdataset, &rdata);
641 			tresult = dns_rdata_tostruct(&rdata, &dname, NULL);
642 			dns_rdata_reset(&rdata);
643 			if (tresult != ISC_R_SUCCESS) {
644 				result = tresult;
645 				goto done;
646 			}
647 			/*
648 			 * Construct the new query name and start over.
649 			 */
650 			prefix = dns_fixedname_initname(&fixed);
651 			dns_name_split(name, nlabels, prefix, NULL);
652 			tresult = dns_name_concatenate(prefix, &dname.dname,
653 						       name, NULL);
654 			dns_rdata_freestruct(&dname);
655 			if (tresult == ISC_R_SUCCESS) {
656 				want_restart = true;
657 			} else {
658 				result = tresult;
659 			}
660 			goto done;
661 		case DNS_R_NCACHENXDOMAIN:
662 		case DNS_R_NCACHENXRRSET:
663 			ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
664 			ISC_LIST_APPEND(rctx->namelist, ansname, link);
665 			ansname = NULL;
666 			rctx->rdataset = NULL;
667 			/* What about sigrdataset? */
668 			if (rctx->sigrdataset != NULL) {
669 				putrdataset(mctx, &rctx->sigrdataset);
670 			}
671 			send_event = true;
672 			goto done;
673 		default:
674 			if (rctx->rdataset != NULL) {
675 				putrdataset(mctx, &rctx->rdataset);
676 			}
677 			if (rctx->sigrdataset != NULL) {
678 				putrdataset(mctx, &rctx->sigrdataset);
679 			}
680 			send_event = true;
681 			goto done;
682 		}
683 
684 		if (rctx->type == dns_rdatatype_any) {
685 			int n = 0;
686 			dns_rdatasetiter_t *rdsiter = NULL;
687 
688 			tresult = dns_db_allrdatasets(db, node, NULL, 0, 0,
689 						      &rdsiter);
690 			if (tresult != ISC_R_SUCCESS) {
691 				result = tresult;
692 				goto done;
693 			}
694 
695 			tresult = dns_rdatasetiter_first(rdsiter);
696 			while (tresult == ISC_R_SUCCESS) {
697 				dns_rdatasetiter_current(rdsiter,
698 							 rctx->rdataset);
699 				if (rctx->rdataset->type != 0) {
700 					ISC_LIST_APPEND(ansname->list,
701 							rctx->rdataset, link);
702 					n++;
703 					rctx->rdataset = NULL;
704 				} else {
705 					/*
706 					 * We're not interested in this
707 					 * rdataset.
708 					 */
709 					dns_rdataset_disassociate(
710 						rctx->rdataset);
711 				}
712 				tresult = dns_rdatasetiter_next(rdsiter);
713 
714 				if (tresult == ISC_R_SUCCESS &&
715 				    rctx->rdataset == NULL)
716 				{
717 					tresult = getrdataset(mctx,
718 							      &rctx->rdataset);
719 					if (tresult != ISC_R_SUCCESS) {
720 						result = tresult;
721 						POST(result);
722 						break;
723 					}
724 				}
725 			}
726 			if (rctx->rdataset != NULL) {
727 				putrdataset(mctx, &rctx->rdataset);
728 			}
729 			if (rctx->sigrdataset != NULL) {
730 				putrdataset(mctx, &rctx->sigrdataset);
731 			}
732 			if (n == 0) {
733 				/*
734 				 * We didn't match any rdatasets (which means
735 				 * something went wrong in this
736 				 * implementation).
737 				 */
738 				result = DNS_R_SERVFAIL; /* better code? */
739 				POST(result);
740 			} else {
741 				ISC_LIST_APPEND(rctx->namelist, ansname, link);
742 				ansname = NULL;
743 			}
744 			dns_rdatasetiter_destroy(&rdsiter);
745 			if (tresult != ISC_R_NOMORE) {
746 				result = DNS_R_SERVFAIL; /* ditto */
747 			} else {
748 				result = ISC_R_SUCCESS;
749 			}
750 			goto done;
751 		} else {
752 			/*
753 			 * This is the "normal" case -- an ordinary question
754 			 * to which we've got the answer.
755 			 */
756 			ISC_LIST_APPEND(ansname->list, rctx->rdataset, link);
757 			rctx->rdataset = NULL;
758 			if (rctx->sigrdataset != NULL) {
759 				ISC_LIST_APPEND(ansname->list,
760 						rctx->sigrdataset, link);
761 				rctx->sigrdataset = NULL;
762 			}
763 			ISC_LIST_APPEND(rctx->namelist, ansname, link);
764 			ansname = NULL;
765 		}
766 
767 	done:
768 		/*
769 		 * Free temporary resources
770 		 */
771 		if (ansname != NULL) {
772 			dns_rdataset_t *rdataset;
773 
774 			while ((rdataset = ISC_LIST_HEAD(ansname->list)) !=
775 			       NULL)
776 			{
777 				ISC_LIST_UNLINK(ansname->list, rdataset, link);
778 				putrdataset(mctx, &rdataset);
779 			}
780 			dns_name_free(ansname, mctx);
781 			isc_mem_put(mctx, ansname, sizeof(*ansname));
782 		}
783 
784 		if (node != NULL) {
785 			dns_db_detachnode(db, &node);
786 		}
787 		if (db != NULL) {
788 			dns_db_detach(&db);
789 		}
790 
791 		/*
792 		 * Limit the number of restarts.
793 		 */
794 		if (want_restart &&
795 		    rctx->restarts == rctx->client->max_restarts)
796 		{
797 			want_restart = false;
798 			result = ISC_R_QUOTA;
799 			send_event = true;
800 		}
801 
802 		/*
803 		 * Prepare further find with new resources
804 		 */
805 		if (want_restart) {
806 			INSIST(rctx->rdataset == NULL &&
807 			       rctx->sigrdataset == NULL);
808 
809 			result = getrdataset(mctx, &rctx->rdataset);
810 			if (result == ISC_R_SUCCESS && rctx->want_dnssec) {
811 				result = getrdataset(mctx, &rctx->sigrdataset);
812 				if (result != ISC_R_SUCCESS) {
813 					putrdataset(mctx, &rctx->rdataset);
814 				}
815 			}
816 
817 			if (result != ISC_R_SUCCESS) {
818 				want_restart = false;
819 				send_event = true;
820 			}
821 		}
822 	} while (want_restart);
823 
824 	if (send_event) {
825 		while ((name = ISC_LIST_HEAD(rctx->namelist)) != NULL) {
826 			ISC_LIST_UNLINK(rctx->namelist, name, link);
827 			ISC_LIST_APPEND(rctx->rev->answerlist, name, link);
828 		}
829 
830 		rctx->rev->result = result;
831 		rctx->rev->vresult = vresult;
832 		isc_async_run(rctx->client->loop, rctx->rev->cb, rctx->rev);
833 	}
834 }
835 
836 static void
837 resolve_done(void *arg) {
838 	dns_clientresume_t *rev = (dns_clientresume_t *)arg;
839 	resarg_t *resarg = rev->arg;
840 	dns_name_t *name = NULL;
841 	isc_result_t result;
842 
843 	resarg->result = rev->result;
844 	resarg->vresult = rev->vresult;
845 	while ((name = ISC_LIST_HEAD(rev->answerlist)) != NULL) {
846 		ISC_LIST_UNLINK(rev->answerlist, name, link);
847 		ISC_LIST_APPEND(*resarg->namelist, name, link);
848 	}
849 
850 	isc_mem_put(resarg->mctx, rev, sizeof(*rev));
851 	destroyrestrans(&resarg->trans);
852 
853 	result = resarg->result;
854 
855 	if (result != ISC_R_SUCCESS && resarg->vresult != ISC_R_SUCCESS) {
856 		/*
857 		 * If this lookup failed due to some error in DNSSEC
858 		 * validation, return the validation error code.
859 		 * XXX: or should we pass the validation result separately?
860 		 */
861 		result = resarg->vresult;
862 	}
863 
864 	resarg->resolve_cb(resarg->client, resarg->name, resarg->namelist,
865 			   result);
866 
867 	dns_client_detach(&resarg->client);
868 
869 	isc_mem_putanddetach(&resarg->mctx, resarg, sizeof(*resarg));
870 }
871 
872 static isc_result_t
873 startresolve(dns_client_t *client, const dns_name_t *name,
874 	     dns_rdataclass_t rdclass, dns_rdatatype_t type,
875 	     unsigned int options, isc_job_cb cb, void *arg,
876 	     dns_clientrestrans_t **transp) {
877 	dns_clientresume_t *rev = NULL;
878 	resctx_t *rctx = NULL;
879 	isc_mem_t *mctx = NULL;
880 	isc_result_t result;
881 	dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
882 	bool want_dnssec, want_validation, want_cdflag, want_tcp;
883 
884 	REQUIRE(DNS_CLIENT_VALID(client));
885 	REQUIRE(transp != NULL && *transp == NULL);
886 	REQUIRE(rdclass == dns_rdataclass_in);
887 
888 	mctx = client->mctx;
889 	want_dnssec = ((options & DNS_CLIENTRESOPT_NODNSSEC) == 0);
890 	want_validation = ((options & DNS_CLIENTRESOPT_NOVALIDATE) == 0);
891 	want_cdflag = ((options & DNS_CLIENTRESOPT_NOCDFLAG) == 0);
892 	want_tcp = ((options & DNS_CLIENTRESOPT_TCP) != 0);
893 
894 	/*
895 	 * Prepare some intermediate resources
896 	 */
897 	rev = isc_mem_get(mctx, sizeof(*rev));
898 	*rev = (dns_clientresume_t){
899 		.result = DNS_R_SERVFAIL,
900 		.answerlist = ISC_LIST_INITIALIZER,
901 		.cb = cb,
902 		.arg = arg,
903 	};
904 
905 	rctx = isc_mem_get(mctx, sizeof(*rctx));
906 	*rctx = (resctx_t){
907 		.client = client,
908 		.rev = rev,
909 		.type = type,
910 		.want_dnssec = want_dnssec,
911 		.want_validation = want_validation,
912 		.want_cdflag = want_cdflag,
913 		.want_tcp = want_tcp,
914 		.namelist = ISC_LIST_INITIALIZER,
915 		.link = ISC_LINK_INITIALIZER,
916 	};
917 
918 	result = getrdataset(mctx, &rdataset);
919 	if (result != ISC_R_SUCCESS) {
920 		goto cleanup;
921 	}
922 	rctx->rdataset = rdataset;
923 
924 	if (want_dnssec) {
925 		result = getrdataset(mctx, &sigrdataset);
926 		if (result != ISC_R_SUCCESS) {
927 			goto cleanup;
928 		}
929 	}
930 	rctx->sigrdataset = sigrdataset;
931 
932 	dns_fixedname_init(&rctx->name);
933 	dns_name_copy(name, dns_fixedname_name(&rctx->name));
934 
935 	dns_view_attach(client->view, &rctx->view);
936 
937 	rctx->magic = RCTX_MAGIC;
938 	isc_refcount_increment(&client->references);
939 
940 	ISC_LIST_APPEND(client->resctxs, rctx, link);
941 
942 	*transp = (dns_clientrestrans_t *)rctx;
943 	client_resfind(rctx, NULL);
944 
945 	return ISC_R_SUCCESS;
946 
947 cleanup:
948 	if (rdataset != NULL) {
949 		putrdataset(client->mctx, &rdataset);
950 	}
951 	if (sigrdataset != NULL) {
952 		putrdataset(client->mctx, &sigrdataset);
953 	}
954 	isc_mem_put(mctx, rctx, sizeof(*rctx));
955 	isc_mem_put(mctx, rev, sizeof(*rev));
956 
957 	return result;
958 }
959 
960 isc_result_t
961 dns_client_resolve(dns_client_t *client, const dns_name_t *name,
962 		   dns_rdataclass_t rdclass, dns_rdatatype_t type,
963 		   unsigned int options, dns_namelist_t *namelist,
964 		   dns_client_resolve_cb resolve_cb) {
965 	isc_result_t result;
966 	resarg_t *resarg = NULL;
967 
968 	REQUIRE(DNS_CLIENT_VALID(client));
969 	REQUIRE(namelist != NULL && ISC_LIST_EMPTY(*namelist));
970 	REQUIRE(rdclass == dns_rdataclass_in);
971 
972 	resarg = isc_mem_get(client->mctx, sizeof(*resarg));
973 
974 	*resarg = (resarg_t){
975 		.client = client,
976 		.name = name,
977 		.result = DNS_R_SERVFAIL,
978 		.namelist = namelist,
979 		.resolve_cb = resolve_cb,
980 	};
981 
982 	isc_mem_attach(client->mctx, &resarg->mctx);
983 
984 	result = startresolve(client, name, rdclass, type, options,
985 			      resolve_done, resarg, &resarg->trans);
986 	if (result != ISC_R_SUCCESS) {
987 		isc_mem_put(client->mctx, resarg, sizeof(*resarg));
988 		return result;
989 	}
990 
991 	return result;
992 }
993 
994 void
995 dns_client_freeresanswer(dns_client_t *client, dns_namelist_t *namelist) {
996 	dns_name_t *name;
997 	dns_rdataset_t *rdataset;
998 
999 	REQUIRE(DNS_CLIENT_VALID(client));
1000 	REQUIRE(namelist != NULL);
1001 
1002 	while ((name = ISC_LIST_HEAD(*namelist)) != NULL) {
1003 		ISC_LIST_UNLINK(*namelist, name, link);
1004 		while ((rdataset = ISC_LIST_HEAD(name->list)) != NULL) {
1005 			ISC_LIST_UNLINK(name->list, rdataset, link);
1006 			putrdataset(client->mctx, &rdataset);
1007 		}
1008 		dns_name_free(name, client->mctx);
1009 		isc_mem_put(client->mctx, name, sizeof(*name));
1010 	}
1011 }
1012 
1013 /*%
1014  * Destroy name resolution transaction state identified by '*transp'.
1015  *
1016  * The caller must have received the CLIENTRESDONE event (either because the
1017  * resolution completed or because cancelresolve() was called).
1018  */
1019 static void
1020 destroyrestrans(dns_clientrestrans_t **transp) {
1021 	resctx_t *rctx = NULL;
1022 	isc_mem_t *mctx = NULL;
1023 	dns_client_t *client = NULL;
1024 
1025 	REQUIRE(transp != NULL);
1026 
1027 	rctx = (resctx_t *)*transp;
1028 	*transp = NULL;
1029 
1030 	REQUIRE(RCTX_VALID(rctx));
1031 	REQUIRE(rctx->fetch == NULL);
1032 
1033 	client = rctx->client;
1034 
1035 	REQUIRE(DNS_CLIENT_VALID(client));
1036 
1037 	mctx = client->mctx;
1038 	dns_view_detach(&rctx->view);
1039 
1040 	INSIST(ISC_LINK_LINKED(rctx, link));
1041 	ISC_LIST_UNLINK(client->resctxs, rctx, link);
1042 
1043 	INSIST(ISC_LIST_EMPTY(rctx->namelist));
1044 
1045 	rctx->magic = 0;
1046 
1047 	isc_mem_put(mctx, rctx, sizeof(*rctx));
1048 }
1049 
1050 isc_result_t
1051 dns_client_addtrustedkey(dns_client_t *client, dns_rdataclass_t rdclass,
1052 			 dns_rdatatype_t rdtype, const dns_name_t *keyname,
1053 			 isc_buffer_t *databuf) {
1054 	REQUIRE(DNS_CLIENT_VALID(client));
1055 	REQUIRE(rdclass == dns_rdataclass_in);
1056 
1057 	return dns_view_addtrustedkey(client->view, rdtype, keyname, databuf);
1058 }
1059