xref: /netbsd/external/mpl/bind/dist/lib/dns/nta.c (revision a706c3b7)
1 /*	$NetBSD: nta.c,v 1.9 2023/06/26 22:03:00 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/buffer.h>
22 #include <isc/log.h>
23 #include <isc/mem.h>
24 #include <isc/print.h>
25 #include <isc/rwlock.h>
26 #include <isc/string.h>
27 #include <isc/task.h>
28 #include <isc/time.h>
29 #include <isc/timer.h>
30 #include <isc/util.h>
31 
32 #include <dns/db.h>
33 #include <dns/fixedname.h>
34 #include <dns/log.h>
35 #include <dns/name.h>
36 #include <dns/nta.h>
37 #include <dns/rbt.h>
38 #include <dns/rdataset.h>
39 #include <dns/resolver.h>
40 #include <dns/result.h>
41 #include <dns/time.h>
42 
43 struct dns_nta {
44 	unsigned int magic;
45 	isc_refcount_t refcount;
46 	dns_ntatable_t *ntatable;
47 	bool forced;
48 	isc_timer_t *timer;
49 	dns_fetch_t *fetch;
50 	dns_rdataset_t rdataset;
51 	dns_rdataset_t sigrdataset;
52 	dns_fixedname_t fn;
53 	dns_name_t *name;
54 	isc_stdtime_t expiry;
55 };
56 
57 #define NTA_MAGIC     ISC_MAGIC('N', 'T', 'A', 'n')
58 #define VALID_NTA(nn) ISC_MAGIC_VALID(nn, NTA_MAGIC)
59 
60 /*
61  * Obtain a reference to the nta object.  Released by
62  * nta_detach.
63  */
64 static void
nta_ref(dns_nta_t * nta)65 nta_ref(dns_nta_t *nta) {
66 	isc_refcount_increment(&nta->refcount);
67 }
68 
69 static void
nta_detach(isc_mem_t * mctx,dns_nta_t ** ntap)70 nta_detach(isc_mem_t *mctx, dns_nta_t **ntap) {
71 	REQUIRE(ntap != NULL && VALID_NTA(*ntap));
72 	dns_nta_t *nta = *ntap;
73 	*ntap = NULL;
74 
75 	if (isc_refcount_decrement(&nta->refcount) == 1) {
76 		isc_refcount_destroy(&nta->refcount);
77 		nta->magic = 0;
78 		if (nta->timer != NULL) {
79 			(void)isc_timer_reset(nta->timer,
80 					      isc_timertype_inactive, NULL,
81 					      NULL, true);
82 			isc_timer_destroy(&nta->timer);
83 		}
84 		if (dns_rdataset_isassociated(&nta->rdataset)) {
85 			dns_rdataset_disassociate(&nta->rdataset);
86 		}
87 		if (dns_rdataset_isassociated(&nta->sigrdataset)) {
88 			dns_rdataset_disassociate(&nta->sigrdataset);
89 		}
90 		if (nta->fetch != NULL) {
91 			dns_resolver_cancelfetch(nta->fetch);
92 			dns_resolver_destroyfetch(&nta->fetch);
93 		}
94 		isc_mem_put(mctx, nta, sizeof(dns_nta_t));
95 	}
96 }
97 
98 static void
free_nta(void * data,void * arg)99 free_nta(void *data, void *arg) {
100 	dns_nta_t *nta = (dns_nta_t *)data;
101 	isc_mem_t *mctx = (isc_mem_t *)arg;
102 
103 	nta_detach(mctx, &nta);
104 }
105 
106 isc_result_t
dns_ntatable_create(dns_view_t * view,isc_taskmgr_t * taskmgr,isc_timermgr_t * timermgr,dns_ntatable_t ** ntatablep)107 dns_ntatable_create(dns_view_t *view, isc_taskmgr_t *taskmgr,
108 		    isc_timermgr_t *timermgr, dns_ntatable_t **ntatablep) {
109 	dns_ntatable_t *ntatable;
110 	isc_result_t result;
111 
112 	REQUIRE(ntatablep != NULL && *ntatablep == NULL);
113 
114 	ntatable = isc_mem_get(view->mctx, sizeof(*ntatable));
115 
116 	ntatable->task = NULL;
117 	result = isc_task_create(taskmgr, 0, &ntatable->task);
118 	if (result != ISC_R_SUCCESS) {
119 		goto cleanup_ntatable;
120 	}
121 	isc_task_setname(ntatable->task, "ntatable", ntatable);
122 
123 	ntatable->table = NULL;
124 	result = dns_rbt_create(view->mctx, free_nta, view->mctx,
125 				&ntatable->table);
126 	if (result != ISC_R_SUCCESS) {
127 		goto cleanup_task;
128 	}
129 
130 	isc_rwlock_init(&ntatable->rwlock, 0, 0);
131 
132 	ntatable->shuttingdown = false;
133 	ntatable->timermgr = timermgr;
134 	ntatable->taskmgr = taskmgr;
135 
136 	ntatable->view = view;
137 	isc_refcount_init(&ntatable->references, 1);
138 
139 	ntatable->magic = NTATABLE_MAGIC;
140 	*ntatablep = ntatable;
141 
142 	return (ISC_R_SUCCESS);
143 
144 cleanup_task:
145 	isc_task_detach(&ntatable->task);
146 
147 cleanup_ntatable:
148 	isc_mem_put(view->mctx, ntatable, sizeof(*ntatable));
149 
150 	return (result);
151 }
152 
153 void
dns_ntatable_attach(dns_ntatable_t * source,dns_ntatable_t ** targetp)154 dns_ntatable_attach(dns_ntatable_t *source, dns_ntatable_t **targetp) {
155 	REQUIRE(VALID_NTATABLE(source));
156 	REQUIRE(targetp != NULL && *targetp == NULL);
157 
158 	isc_refcount_increment(&source->references);
159 
160 	*targetp = source;
161 }
162 
163 void
dns_ntatable_detach(dns_ntatable_t ** ntatablep)164 dns_ntatable_detach(dns_ntatable_t **ntatablep) {
165 	dns_ntatable_t *ntatable;
166 
167 	REQUIRE(ntatablep != NULL && VALID_NTATABLE(*ntatablep));
168 
169 	ntatable = *ntatablep;
170 	*ntatablep = NULL;
171 
172 	if (isc_refcount_decrement(&ntatable->references) == 1) {
173 		dns_rbt_destroy(&ntatable->table);
174 		isc_rwlock_destroy(&ntatable->rwlock);
175 		isc_refcount_destroy(&ntatable->references);
176 		if (ntatable->task != NULL) {
177 			isc_task_detach(&ntatable->task);
178 		}
179 		ntatable->timermgr = NULL;
180 		ntatable->taskmgr = NULL;
181 		ntatable->magic = 0;
182 		isc_mem_put(ntatable->view->mctx, ntatable, sizeof(*ntatable));
183 	}
184 }
185 
186 static void
fetch_done(isc_task_t * task,isc_event_t * event)187 fetch_done(isc_task_t *task, isc_event_t *event) {
188 	dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
189 	dns_nta_t *nta = devent->ev_arg;
190 	isc_result_t eresult = devent->result;
191 	dns_ntatable_t *ntatable = nta->ntatable;
192 	dns_view_t *view = ntatable->view;
193 	isc_stdtime_t now;
194 
195 	UNUSED(task);
196 
197 	if (dns_rdataset_isassociated(&nta->rdataset)) {
198 		dns_rdataset_disassociate(&nta->rdataset);
199 	}
200 	if (dns_rdataset_isassociated(&nta->sigrdataset)) {
201 		dns_rdataset_disassociate(&nta->sigrdataset);
202 	}
203 	if (nta->fetch == devent->fetch) {
204 		nta->fetch = NULL;
205 	}
206 	dns_resolver_destroyfetch(&devent->fetch);
207 
208 	if (devent->node != NULL) {
209 		dns_db_detachnode(devent->db, &devent->node);
210 	}
211 	if (devent->db != NULL) {
212 		dns_db_detach(&devent->db);
213 	}
214 
215 	isc_event_free(&event);
216 	isc_stdtime_get(&now);
217 
218 	switch (eresult) {
219 	case ISC_R_SUCCESS:
220 	case DNS_R_NCACHENXDOMAIN:
221 	case DNS_R_NXDOMAIN:
222 	case DNS_R_NCACHENXRRSET:
223 	case DNS_R_NXRRSET:
224 		if (nta->expiry > now) {
225 			nta->expiry = now;
226 		}
227 		break;
228 	default:
229 		break;
230 	}
231 
232 	/*
233 	 * If we're expiring before the next recheck, we might
234 	 * as well stop the timer now.
235 	 */
236 	if (nta->timer != NULL && nta->expiry - now < view->nta_recheck) {
237 		(void)isc_timer_reset(nta->timer, isc_timertype_inactive, NULL,
238 				      NULL, true);
239 	}
240 	nta_detach(view->mctx, &nta);
241 	dns_view_weakdetach(&view);
242 }
243 
244 static void
checkbogus(isc_task_t * task,isc_event_t * event)245 checkbogus(isc_task_t *task, isc_event_t *event) {
246 	dns_nta_t *nta = event->ev_arg;
247 	dns_ntatable_t *ntatable = nta->ntatable;
248 	dns_view_t *view = NULL;
249 	isc_result_t result;
250 
251 	if (nta->fetch != NULL) {
252 		dns_resolver_cancelfetch(nta->fetch);
253 		nta->fetch = NULL;
254 	}
255 	if (dns_rdataset_isassociated(&nta->rdataset)) {
256 		dns_rdataset_disassociate(&nta->rdataset);
257 	}
258 	if (dns_rdataset_isassociated(&nta->sigrdataset)) {
259 		dns_rdataset_disassociate(&nta->sigrdataset);
260 	}
261 
262 	isc_event_free(&event);
263 
264 	nta_ref(nta);
265 	dns_view_weakattach(ntatable->view, &view);
266 	result = dns_resolver_createfetch(
267 		view->resolver, nta->name, dns_rdatatype_nsec, NULL, NULL, NULL,
268 		NULL, 0, DNS_FETCHOPT_NONTA, 0, NULL, task, fetch_done, nta,
269 		&nta->rdataset, &nta->sigrdataset, &nta->fetch);
270 	if (result != ISC_R_SUCCESS) {
271 		nta_detach(view->mctx, &nta);
272 		dns_view_weakdetach(&view);
273 	}
274 }
275 
276 static isc_result_t
settimer(dns_ntatable_t * ntatable,dns_nta_t * nta,uint32_t lifetime)277 settimer(dns_ntatable_t *ntatable, dns_nta_t *nta, uint32_t lifetime) {
278 	isc_result_t result;
279 	isc_interval_t interval;
280 	dns_view_t *view;
281 
282 	REQUIRE(VALID_NTATABLE(ntatable));
283 	REQUIRE(VALID_NTA(nta));
284 
285 	if (ntatable->timermgr == NULL) {
286 		return (ISC_R_SUCCESS);
287 	}
288 
289 	view = ntatable->view;
290 	if (view->nta_recheck == 0 || lifetime <= view->nta_recheck) {
291 		return (ISC_R_SUCCESS);
292 	}
293 
294 	isc_interval_set(&interval, view->nta_recheck, 0);
295 	result = isc_timer_create(ntatable->timermgr, isc_timertype_ticker,
296 				  NULL, &interval, ntatable->task, checkbogus,
297 				  nta, &nta->timer);
298 	if (result != ISC_R_SUCCESS) {
299 		isc_timer_destroy(&nta->timer);
300 	}
301 	return (result);
302 }
303 
304 static isc_result_t
nta_create(dns_ntatable_t * ntatable,const dns_name_t * name,dns_nta_t ** target)305 nta_create(dns_ntatable_t *ntatable, const dns_name_t *name,
306 	   dns_nta_t **target) {
307 	dns_nta_t *nta = NULL;
308 	dns_view_t *view;
309 
310 	REQUIRE(VALID_NTATABLE(ntatable));
311 	REQUIRE(target != NULL && *target == NULL);
312 
313 	view = ntatable->view;
314 
315 	nta = isc_mem_get(view->mctx, sizeof(dns_nta_t));
316 
317 	nta->ntatable = ntatable;
318 	nta->expiry = 0;
319 	nta->timer = NULL;
320 	nta->fetch = NULL;
321 	dns_rdataset_init(&nta->rdataset);
322 	dns_rdataset_init(&nta->sigrdataset);
323 
324 	isc_refcount_init(&nta->refcount, 1);
325 
326 	nta->name = dns_fixedname_initname(&nta->fn);
327 	dns_name_copynf(name, nta->name);
328 
329 	nta->magic = NTA_MAGIC;
330 
331 	*target = nta;
332 	return (ISC_R_SUCCESS);
333 }
334 
335 isc_result_t
dns_ntatable_add(dns_ntatable_t * ntatable,const dns_name_t * name,bool force,isc_stdtime_t now,uint32_t lifetime)336 dns_ntatable_add(dns_ntatable_t *ntatable, const dns_name_t *name, bool force,
337 		 isc_stdtime_t now, uint32_t lifetime) {
338 	isc_result_t result = ISC_R_SUCCESS;
339 	dns_nta_t *nta = NULL;
340 	dns_rbtnode_t *node;
341 	dns_view_t *view;
342 
343 	REQUIRE(VALID_NTATABLE(ntatable));
344 
345 	view = ntatable->view;
346 
347 	RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
348 
349 	if (ntatable->shuttingdown) {
350 		goto unlock;
351 	}
352 
353 	result = nta_create(ntatable, name, &nta);
354 	if (result != ISC_R_SUCCESS) {
355 		goto unlock;
356 	}
357 
358 	nta->expiry = now + lifetime;
359 	nta->forced = force;
360 
361 	node = NULL;
362 	result = dns_rbt_addnode(ntatable->table, name, &node);
363 	if (result == ISC_R_SUCCESS) {
364 		if (!force) {
365 			(void)settimer(ntatable, nta, lifetime);
366 		}
367 		node->data = nta;
368 		nta = NULL;
369 	} else if (result == ISC_R_EXISTS) {
370 		dns_nta_t *n = node->data;
371 		if (n == NULL) {
372 			if (!force) {
373 				(void)settimer(ntatable, nta, lifetime);
374 			}
375 			node->data = nta;
376 			nta = NULL;
377 		} else {
378 			n->expiry = nta->expiry;
379 			nta_detach(view->mctx, &nta);
380 		}
381 		result = ISC_R_SUCCESS;
382 	}
383 
384 unlock:
385 	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
386 
387 	if (nta != NULL) {
388 		nta_detach(view->mctx, &nta);
389 	}
390 
391 	return (result);
392 }
393 
394 /*
395  * Caller must hold a write lock on rwlock.
396  */
397 static isc_result_t
deletenode(dns_ntatable_t * ntatable,const dns_name_t * name)398 deletenode(dns_ntatable_t *ntatable, const dns_name_t *name) {
399 	isc_result_t result;
400 	dns_rbtnode_t *node = NULL;
401 
402 	REQUIRE(VALID_NTATABLE(ntatable));
403 	REQUIRE(name != NULL);
404 
405 	result = dns_rbt_findnode(ntatable->table, name, NULL, &node, NULL,
406 				  DNS_RBTFIND_NOOPTIONS, NULL, NULL);
407 	if (result == ISC_R_SUCCESS) {
408 		if (node->data != NULL) {
409 			result = dns_rbt_deletenode(ntatable->table, node,
410 						    false);
411 		} else {
412 			result = ISC_R_NOTFOUND;
413 		}
414 	} else if (result == DNS_R_PARTIALMATCH) {
415 		result = ISC_R_NOTFOUND;
416 	}
417 
418 	return (result);
419 }
420 
421 isc_result_t
dns_ntatable_delete(dns_ntatable_t * ntatable,const dns_name_t * name)422 dns_ntatable_delete(dns_ntatable_t *ntatable, const dns_name_t *name) {
423 	isc_result_t result;
424 
425 	RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
426 	result = deletenode(ntatable, name);
427 	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
428 
429 	return (result);
430 }
431 
432 bool
dns_ntatable_covered(dns_ntatable_t * ntatable,isc_stdtime_t now,const dns_name_t * name,const dns_name_t * anchor)433 dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
434 		     const dns_name_t *name, const dns_name_t *anchor) {
435 	isc_result_t result;
436 	dns_fixedname_t fn;
437 	dns_rbtnode_t *node;
438 	dns_name_t *foundname;
439 	dns_nta_t *nta = NULL;
440 	bool answer = false;
441 	isc_rwlocktype_t locktype = isc_rwlocktype_read;
442 
443 	REQUIRE(ntatable == NULL || VALID_NTATABLE(ntatable));
444 	REQUIRE(dns_name_isabsolute(name));
445 
446 	if (ntatable == NULL) {
447 		return (false);
448 	}
449 
450 	foundname = dns_fixedname_initname(&fn);
451 
452 relock:
453 	RWLOCK(&ntatable->rwlock, locktype);
454 again:
455 	node = NULL;
456 	result = dns_rbt_findnode(ntatable->table, name, foundname, &node, NULL,
457 				  DNS_RBTFIND_NOOPTIONS, NULL, NULL);
458 	if (result == DNS_R_PARTIALMATCH) {
459 		if (dns_name_issubdomain(foundname, anchor)) {
460 			result = ISC_R_SUCCESS;
461 		}
462 	}
463 	if (result == ISC_R_SUCCESS) {
464 		nta = (dns_nta_t *)node->data;
465 		answer = (nta->expiry > now);
466 	}
467 
468 	/* Deal with expired NTA */
469 	if (result == ISC_R_SUCCESS && !answer) {
470 		char nb[DNS_NAME_FORMATSIZE];
471 
472 		if (locktype == isc_rwlocktype_read) {
473 			RWUNLOCK(&ntatable->rwlock, locktype);
474 			locktype = isc_rwlocktype_write;
475 			goto relock;
476 		}
477 
478 		dns_name_format(foundname, nb, sizeof(nb));
479 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
480 			      DNS_LOGMODULE_NTA, ISC_LOG_INFO,
481 			      "deleting expired NTA at %s", nb);
482 
483 		if (nta->timer != NULL) {
484 			(void)isc_timer_reset(nta->timer,
485 					      isc_timertype_inactive, NULL,
486 					      NULL, true);
487 			isc_timer_destroy(&nta->timer);
488 		}
489 
490 		result = deletenode(ntatable, foundname);
491 		if (result != ISC_R_SUCCESS) {
492 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
493 				      DNS_LOGMODULE_NTA, ISC_LOG_INFO,
494 				      "deleting NTA failed: %s",
495 				      isc_result_totext(result));
496 		}
497 		goto again;
498 	}
499 	RWUNLOCK(&ntatable->rwlock, locktype);
500 
501 	return (answer);
502 }
503 
504 static isc_result_t
putstr(isc_buffer_t ** b,const char * str)505 putstr(isc_buffer_t **b, const char *str) {
506 	isc_result_t result;
507 
508 	result = isc_buffer_reserve(b, strlen(str));
509 	if (result != ISC_R_SUCCESS) {
510 		return (result);
511 	}
512 
513 	isc_buffer_putstr(*b, str);
514 	return (ISC_R_SUCCESS);
515 }
516 
517 isc_result_t
dns_ntatable_totext(dns_ntatable_t * ntatable,const char * view,isc_buffer_t ** buf)518 dns_ntatable_totext(dns_ntatable_t *ntatable, const char *view,
519 		    isc_buffer_t **buf) {
520 	isc_result_t result;
521 	dns_rbtnode_t *node;
522 	dns_rbtnodechain_t chain;
523 	bool first = true;
524 	isc_stdtime_t now;
525 
526 	REQUIRE(VALID_NTATABLE(ntatable));
527 
528 	isc_stdtime_get(&now);
529 
530 	RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
531 	dns_rbtnodechain_init(&chain);
532 	result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
533 	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
534 		if (result == ISC_R_NOTFOUND) {
535 			result = ISC_R_SUCCESS;
536 		}
537 		goto cleanup;
538 	}
539 	for (;;) {
540 		dns_rbtnodechain_current(&chain, NULL, NULL, &node);
541 		if (node->data != NULL) {
542 			dns_nta_t *n = (dns_nta_t *)node->data;
543 			char nbuf[DNS_NAME_FORMATSIZE];
544 			char tbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
545 			char obuf[DNS_NAME_FORMATSIZE +
546 				  ISC_FORMATHTTPTIMESTAMP_SIZE +
547 				  sizeof("expired:  \n")];
548 			dns_fixedname_t fn;
549 			dns_name_t *name;
550 			isc_time_t t;
551 
552 			/*
553 			 * Skip "validate-except" entries.
554 			 */
555 			if (n->expiry != 0xffffffffU) {
556 				name = dns_fixedname_initname(&fn);
557 				dns_rbt_fullnamefromnode(node, name);
558 				dns_name_format(name, nbuf, sizeof(nbuf));
559 				isc_time_set(&t, n->expiry, 0);
560 				isc_time_formattimestamp(&t, tbuf,
561 							 sizeof(tbuf));
562 
563 				snprintf(obuf, sizeof(obuf), "%s%s%s%s: %s %s",
564 					 first ? "" : "\n", nbuf,
565 					 view != NULL ? "/" : "",
566 					 view != NULL ? view : "",
567 					 n->expiry <= now ? "expired"
568 							  : "expiry",
569 					 tbuf);
570 				first = false;
571 				result = putstr(buf, obuf);
572 				if (result != ISC_R_SUCCESS) {
573 					goto cleanup;
574 				}
575 			}
576 		}
577 		result = dns_rbtnodechain_next(&chain, NULL, NULL);
578 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
579 			if (result == ISC_R_NOMORE) {
580 				result = ISC_R_SUCCESS;
581 			}
582 			break;
583 		}
584 	}
585 
586 cleanup:
587 	dns_rbtnodechain_invalidate(&chain);
588 	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
589 	return (result);
590 }
591 
592 isc_result_t
dns_ntatable_dump(dns_ntatable_t * ntatable,FILE * fp)593 dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp) {
594 	isc_result_t result;
595 	isc_buffer_t *text = NULL;
596 	int len = 4096;
597 
598 	isc_buffer_allocate(ntatable->view->mctx, &text, len);
599 
600 	result = dns_ntatable_totext(ntatable, NULL, &text);
601 
602 	if (isc_buffer_usedlength(text) != 0) {
603 		(void)putstr(&text, "\n");
604 	} else if (result == ISC_R_SUCCESS) {
605 		(void)putstr(&text, "none");
606 	} else {
607 		(void)putstr(&text, "could not dump NTA table: ");
608 		(void)putstr(&text, isc_result_totext(result));
609 	}
610 
611 	fprintf(fp, "%.*s", (int)isc_buffer_usedlength(text),
612 		(char *)isc_buffer_base(text));
613 	isc_buffer_free(&text);
614 	return (result);
615 }
616 
617 isc_result_t
dns_ntatable_save(dns_ntatable_t * ntatable,FILE * fp)618 dns_ntatable_save(dns_ntatable_t *ntatable, FILE *fp) {
619 	isc_result_t result;
620 	dns_rbtnode_t *node;
621 	dns_rbtnodechain_t chain;
622 	isc_stdtime_t now;
623 	bool written = false;
624 
625 	REQUIRE(VALID_NTATABLE(ntatable));
626 
627 	isc_stdtime_get(&now);
628 
629 	RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
630 	dns_rbtnodechain_init(&chain);
631 	result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
632 	if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
633 		goto cleanup;
634 	}
635 
636 	for (;;) {
637 		dns_rbtnodechain_current(&chain, NULL, NULL, &node);
638 		if (node->data != NULL) {
639 			isc_buffer_t b;
640 			char nbuf[DNS_NAME_FORMATSIZE + 1], tbuf[80];
641 			dns_fixedname_t fn;
642 			dns_name_t *name;
643 			dns_nta_t *n = (dns_nta_t *)node->data;
644 
645 			/*
646 			 * Skip this node if the expiry is already in the
647 			 * past, or if this is a "validate-except" entry.
648 			 */
649 			if (n->expiry <= now || n->expiry == 0xffffffffU) {
650 				goto skip;
651 			}
652 
653 			name = dns_fixedname_initname(&fn);
654 			dns_rbt_fullnamefromnode(node, name);
655 
656 			isc_buffer_init(&b, nbuf, sizeof(nbuf));
657 			result = dns_name_totext(name, false, &b);
658 			if (result != ISC_R_SUCCESS) {
659 				goto skip;
660 			}
661 
662 			/* Zero terminate. */
663 			isc_buffer_putuint8(&b, 0);
664 
665 			isc_buffer_init(&b, tbuf, sizeof(tbuf));
666 			dns_time32_totext(n->expiry, &b);
667 
668 			/* Zero terminate. */
669 			isc_buffer_putuint8(&b, 0);
670 
671 			fprintf(fp, "%s %s %s\n", nbuf,
672 				n->forced ? "forced" : "regular", tbuf);
673 			written = true;
674 		}
675 	skip:
676 		result = dns_rbtnodechain_next(&chain, NULL, NULL);
677 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
678 			if (result == ISC_R_NOMORE) {
679 				result = ISC_R_SUCCESS;
680 			}
681 			break;
682 		}
683 	}
684 
685 cleanup:
686 	dns_rbtnodechain_invalidate(&chain);
687 	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
688 
689 	if (result != ISC_R_SUCCESS) {
690 		return (result);
691 	} else {
692 		return (written ? ISC_R_SUCCESS : ISC_R_NOTFOUND);
693 	}
694 }
695 
696 void
dns_ntatable_shutdown(dns_ntatable_t * ntatable)697 dns_ntatable_shutdown(dns_ntatable_t *ntatable) {
698 	isc_result_t result;
699 	dns_rbtnode_t *node;
700 	dns_rbtnodechain_t chain;
701 
702 	REQUIRE(VALID_NTATABLE(ntatable));
703 
704 	RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
705 	ntatable->shuttingdown = true;
706 
707 	dns_rbtnodechain_init(&chain);
708 	result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
709 	while (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN) {
710 		dns_rbtnodechain_current(&chain, NULL, NULL, &node);
711 		if (node->data != NULL) {
712 			dns_nta_t *nta = (dns_nta_t *)node->data;
713 			if (nta->timer != NULL) {
714 				(void)isc_timer_reset(nta->timer,
715 						      isc_timertype_inactive,
716 						      NULL, NULL, true);
717 			}
718 		}
719 		result = dns_rbtnodechain_next(&chain, NULL, NULL);
720 	}
721 
722 	dns_rbtnodechain_invalidate(&chain);
723 	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
724 }
725