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