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