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