1/*	$NetBSD: nta.c,v 1.1 2024/02/18 20:57:32 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
43struct 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 */
64static void
65nta_ref(dns_nta_t *nta) {
66	isc_refcount_increment(&nta->refcount);
67}
68
69static void
70nta_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
98static void
99free_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
106isc_result_t
107dns_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
144cleanup_task:
145	isc_task_detach(&ntatable->task);
146
147cleanup_ntatable:
148	isc_mem_put(view->mctx, ntatable, sizeof(*ntatable));
149
150	return (result);
151}
152
153void
154dns_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
163void
164dns_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
186static void
187fetch_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
244static void
245checkbogus(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
276static isc_result_t
277settimer(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
304static isc_result_t
305nta_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
335isc_result_t
336dns_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
384unlock:
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 */
397static isc_result_t
398deletenode(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
421isc_result_t
422dns_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
432bool
433dns_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
452relock:
453	RWLOCK(&ntatable->rwlock, locktype);
454again:
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
504static isc_result_t
505putstr(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
517isc_result_t
518dns_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
586cleanup:
587	dns_rbtnodechain_invalidate(&chain);
588	RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
589	return (result);
590}
591
592isc_result_t
593dns_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
617isc_result_t
618dns_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
685cleanup:
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
696void
697dns_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