1 /* $NetBSD: transport.c,v 1.3 2025/01/26 16:25:25 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 #include <inttypes.h> 17 18 #include <isc/hashmap.h> 19 #include <isc/list.h> 20 #include <isc/mem.h> 21 #include <isc/netaddr.h> 22 #include <isc/refcount.h> 23 #include <isc/result.h> 24 #include <isc/rwlock.h> 25 #include <isc/sockaddr.h> 26 #include <isc/util.h> 27 28 #include <dns/fixedname.h> 29 #include <dns/name.h> 30 #include <dns/transport.h> 31 32 #define TRANSPORT_MAGIC ISC_MAGIC('T', 'r', 'n', 's') 33 #define VALID_TRANSPORT(ptr) ISC_MAGIC_VALID(ptr, TRANSPORT_MAGIC) 34 35 #define TRANSPORT_LIST_MAGIC ISC_MAGIC('T', 'r', 'L', 's') 36 #define VALID_TRANSPORT_LIST(ptr) ISC_MAGIC_VALID(ptr, TRANSPORT_LIST_MAGIC) 37 38 struct dns_transport_list { 39 unsigned int magic; 40 isc_refcount_t references; 41 isc_mem_t *mctx; 42 isc_rwlock_t lock; 43 isc_hashmap_t *transports[DNS_TRANSPORT_COUNT]; 44 }; 45 46 typedef enum ternary { ter_none = 0, ter_true = 1, ter_false = 2 } ternary_t; 47 48 struct dns_transport { 49 unsigned int magic; 50 isc_refcount_t references; 51 isc_mem_t *mctx; 52 dns_transport_type_t type; 53 dns_fixedname_t fn; 54 dns_name_t *name; 55 struct { 56 char *tlsname; 57 char *certfile; 58 char *keyfile; 59 char *cafile; 60 char *remote_hostname; 61 char *ciphers; 62 char *cipher_suites; 63 uint32_t protocol_versions; 64 ternary_t prefer_server_ciphers; 65 bool always_verify_remote; 66 } tls; 67 struct { 68 char *endpoint; 69 dns_http_mode_t mode; 70 } doh; 71 }; 72 73 static bool 74 transport_match(void *node, const void *key) { 75 dns_transport_t *transport = node; 76 77 return dns_name_equal(transport->name, key); 78 } 79 80 static isc_result_t 81 list_add(dns_transport_list_t *list, const dns_name_t *name, 82 const dns_transport_type_t type, dns_transport_t *transport) { 83 isc_result_t result; 84 isc_hashmap_t *hm = NULL; 85 86 RWLOCK(&list->lock, isc_rwlocktype_write); 87 hm = list->transports[type]; 88 INSIST(hm != NULL); 89 90 transport->name = dns_fixedname_initname(&transport->fn); 91 dns_name_copy(name, transport->name); 92 result = isc_hashmap_add(hm, dns_name_hash(name), transport_match, name, 93 transport, NULL); 94 RWUNLOCK(&list->lock, isc_rwlocktype_write); 95 96 return result; 97 } 98 99 dns_transport_type_t 100 dns_transport_get_type(const dns_transport_t *transport) { 101 REQUIRE(VALID_TRANSPORT(transport)); 102 103 return transport->type; 104 } 105 106 char * 107 dns_transport_get_certfile(const dns_transport_t *transport) { 108 REQUIRE(VALID_TRANSPORT(transport)); 109 110 return transport->tls.certfile; 111 } 112 113 char * 114 dns_transport_get_keyfile(const dns_transport_t *transport) { 115 REQUIRE(VALID_TRANSPORT(transport)); 116 117 return transport->tls.keyfile; 118 } 119 120 char * 121 dns_transport_get_cafile(const dns_transport_t *transport) { 122 REQUIRE(VALID_TRANSPORT(transport)); 123 124 return transport->tls.cafile; 125 } 126 127 char * 128 dns_transport_get_remote_hostname(const dns_transport_t *transport) { 129 REQUIRE(VALID_TRANSPORT(transport)); 130 131 return transport->tls.remote_hostname; 132 } 133 134 char * 135 dns_transport_get_endpoint(const dns_transport_t *transport) { 136 REQUIRE(VALID_TRANSPORT(transport)); 137 138 return transport->doh.endpoint; 139 } 140 141 dns_http_mode_t 142 dns_transport_get_mode(const dns_transport_t *transport) { 143 REQUIRE(VALID_TRANSPORT(transport)); 144 145 return transport->doh.mode; 146 } 147 148 dns_transport_t * 149 dns_transport_new(const dns_name_t *name, dns_transport_type_t type, 150 dns_transport_list_t *list) { 151 dns_transport_t *transport = isc_mem_get(list->mctx, 152 sizeof(*transport)); 153 *transport = (dns_transport_t){ .type = type }; 154 isc_refcount_init(&transport->references, 1); 155 isc_mem_attach(list->mctx, &transport->mctx); 156 transport->magic = TRANSPORT_MAGIC; 157 158 list_add(list, name, type, transport); 159 160 return transport; 161 } 162 163 void 164 dns_transport_set_certfile(dns_transport_t *transport, const char *certfile) { 165 REQUIRE(VALID_TRANSPORT(transport)); 166 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 167 transport->type == DNS_TRANSPORT_HTTP); 168 169 if (transport->tls.certfile != NULL) { 170 isc_mem_free(transport->mctx, transport->tls.certfile); 171 } 172 173 if (certfile != NULL) { 174 transport->tls.certfile = isc_mem_strdup(transport->mctx, 175 certfile); 176 } 177 } 178 179 void 180 dns_transport_set_keyfile(dns_transport_t *transport, const char *keyfile) { 181 REQUIRE(VALID_TRANSPORT(transport)); 182 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 183 transport->type == DNS_TRANSPORT_HTTP); 184 185 if (transport->tls.keyfile != NULL) { 186 isc_mem_free(transport->mctx, transport->tls.keyfile); 187 } 188 189 if (keyfile != NULL) { 190 transport->tls.keyfile = isc_mem_strdup(transport->mctx, 191 keyfile); 192 } 193 } 194 195 void 196 dns_transport_set_cafile(dns_transport_t *transport, const char *cafile) { 197 REQUIRE(VALID_TRANSPORT(transport)); 198 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 199 transport->type == DNS_TRANSPORT_HTTP); 200 201 if (transport->tls.cafile != NULL) { 202 isc_mem_free(transport->mctx, transport->tls.cafile); 203 } 204 205 if (cafile != NULL) { 206 transport->tls.cafile = isc_mem_strdup(transport->mctx, cafile); 207 } 208 } 209 210 void 211 dns_transport_set_remote_hostname(dns_transport_t *transport, 212 const char *hostname) { 213 REQUIRE(VALID_TRANSPORT(transport)); 214 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 215 transport->type == DNS_TRANSPORT_HTTP); 216 217 if (transport->tls.remote_hostname != NULL) { 218 isc_mem_free(transport->mctx, transport->tls.remote_hostname); 219 } 220 221 if (hostname != NULL) { 222 transport->tls.remote_hostname = isc_mem_strdup(transport->mctx, 223 hostname); 224 } 225 } 226 227 void 228 dns_transport_set_endpoint(dns_transport_t *transport, const char *endpoint) { 229 REQUIRE(VALID_TRANSPORT(transport)); 230 REQUIRE(transport->type == DNS_TRANSPORT_HTTP); 231 232 if (transport->doh.endpoint != NULL) { 233 isc_mem_free(transport->mctx, transport->doh.endpoint); 234 } 235 236 if (endpoint != NULL) { 237 transport->doh.endpoint = isc_mem_strdup(transport->mctx, 238 endpoint); 239 } 240 } 241 242 void 243 dns_transport_set_mode(dns_transport_t *transport, dns_http_mode_t mode) { 244 REQUIRE(VALID_TRANSPORT(transport)); 245 REQUIRE(transport->type == DNS_TRANSPORT_HTTP); 246 247 transport->doh.mode = mode; 248 } 249 250 void 251 dns_transport_set_tls_versions(dns_transport_t *transport, 252 const uint32_t tls_versions) { 253 REQUIRE(VALID_TRANSPORT(transport)); 254 REQUIRE(transport->type == DNS_TRANSPORT_HTTP || 255 transport->type == DNS_TRANSPORT_TLS); 256 257 transport->tls.protocol_versions = tls_versions; 258 } 259 260 uint32_t 261 dns_transport_get_tls_versions(const dns_transport_t *transport) { 262 REQUIRE(VALID_TRANSPORT(transport)); 263 264 return transport->tls.protocol_versions; 265 } 266 267 void 268 dns_transport_set_ciphers(dns_transport_t *transport, const char *ciphers) { 269 REQUIRE(VALID_TRANSPORT(transport)); 270 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 271 transport->type == DNS_TRANSPORT_HTTP); 272 273 if (transport->tls.ciphers != NULL) { 274 isc_mem_free(transport->mctx, transport->tls.ciphers); 275 } 276 277 if (ciphers != NULL) { 278 transport->tls.ciphers = isc_mem_strdup(transport->mctx, 279 ciphers); 280 } 281 } 282 283 void 284 dns_transport_set_tlsname(dns_transport_t *transport, const char *tlsname) { 285 REQUIRE(VALID_TRANSPORT(transport)); 286 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 287 transport->type == DNS_TRANSPORT_HTTP); 288 289 if (transport->tls.tlsname != NULL) { 290 isc_mem_free(transport->mctx, transport->tls.tlsname); 291 } 292 293 if (tlsname != NULL) { 294 transport->tls.tlsname = isc_mem_strdup(transport->mctx, 295 tlsname); 296 } 297 } 298 299 char * 300 dns_transport_get_ciphers(const dns_transport_t *transport) { 301 REQUIRE(VALID_TRANSPORT(transport)); 302 303 return transport->tls.ciphers; 304 } 305 306 void 307 dns_transport_set_cipher_suites(dns_transport_t *transport, 308 const char *cipher_suites) { 309 REQUIRE(VALID_TRANSPORT(transport)); 310 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 311 transport->type == DNS_TRANSPORT_HTTP); 312 313 if (transport->tls.cipher_suites != NULL) { 314 isc_mem_free(transport->mctx, transport->tls.cipher_suites); 315 } 316 317 if (cipher_suites != NULL) { 318 transport->tls.cipher_suites = isc_mem_strdup(transport->mctx, 319 cipher_suites); 320 } 321 } 322 323 char * 324 dns_transport_get_cipher_suites(const dns_transport_t *transport) { 325 REQUIRE(VALID_TRANSPORT(transport)); 326 327 return transport->tls.cipher_suites; 328 } 329 330 char * 331 dns_transport_get_tlsname(const dns_transport_t *transport) { 332 REQUIRE(VALID_TRANSPORT(transport)); 333 334 return transport->tls.tlsname; 335 } 336 337 void 338 dns_transport_set_prefer_server_ciphers(dns_transport_t *transport, 339 const bool prefer) { 340 REQUIRE(VALID_TRANSPORT(transport)); 341 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 342 transport->type == DNS_TRANSPORT_HTTP); 343 344 transport->tls.prefer_server_ciphers = prefer ? ter_true : ter_false; 345 } 346 347 bool 348 dns_transport_get_prefer_server_ciphers(const dns_transport_t *transport, 349 bool *preferp) { 350 REQUIRE(VALID_TRANSPORT(transport)); 351 REQUIRE(preferp != NULL); 352 if (transport->tls.prefer_server_ciphers == ter_none) { 353 return false; 354 } else if (transport->tls.prefer_server_ciphers == ter_true) { 355 *preferp = true; 356 return true; 357 } else if (transport->tls.prefer_server_ciphers == ter_false) { 358 *preferp = false; 359 return true; 360 } 361 362 UNREACHABLE(); 363 return false; 364 } 365 366 void 367 dns_transport_set_always_verify_remote(dns_transport_t *transport, 368 const bool always_verify_remote) { 369 REQUIRE(VALID_TRANSPORT(transport)); 370 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 371 transport->type == DNS_TRANSPORT_HTTP); 372 373 transport->tls.always_verify_remote = always_verify_remote; 374 } 375 376 bool 377 dns_transport_get_always_verify_remote(dns_transport_t *transport) { 378 REQUIRE(VALID_TRANSPORT(transport)); 379 REQUIRE(transport->type == DNS_TRANSPORT_TLS || 380 transport->type == DNS_TRANSPORT_HTTP); 381 382 return transport->tls.always_verify_remote; 383 } 384 385 isc_result_t 386 dns_transport_get_tlsctx(dns_transport_t *transport, const isc_sockaddr_t *peer, 387 isc_tlsctx_cache_t *tlsctx_cache, isc_mem_t *mctx, 388 isc_tlsctx_t **pctx, 389 isc_tlsctx_client_session_cache_t **psess_cache) { 390 isc_result_t result = ISC_R_FAILURE; 391 isc_tlsctx_t *tlsctx = NULL, *found = NULL; 392 isc_tls_cert_store_t *store = NULL, *found_store = NULL; 393 isc_tlsctx_client_session_cache_t *sess_cache = NULL; 394 isc_tlsctx_client_session_cache_t *found_sess_cache = NULL; 395 uint32_t tls_versions; 396 const char *ciphers = NULL; 397 const char *cipher_suites = NULL; 398 bool prefer_server_ciphers; 399 uint16_t family; 400 const char *tlsname = NULL; 401 402 REQUIRE(VALID_TRANSPORT(transport)); 403 REQUIRE(transport->type == DNS_TRANSPORT_TLS); 404 REQUIRE(peer != NULL); 405 REQUIRE(tlsctx_cache != NULL); 406 REQUIRE(mctx != NULL); 407 REQUIRE(pctx != NULL && *pctx == NULL); 408 REQUIRE(psess_cache != NULL && *psess_cache == NULL); 409 410 family = (isc_sockaddr_pf(peer) == PF_INET6) ? AF_INET6 : AF_INET; 411 412 tlsname = dns_transport_get_tlsname(transport); 413 INSIST(tlsname != NULL && *tlsname != '\0'); 414 415 /* 416 * Let's try to re-use the already created context. This way 417 * we have a chance to resume the TLS session, bypassing the 418 * full TLS handshake procedure, making establishing 419 * subsequent TLS connections faster. 420 */ 421 result = isc_tlsctx_cache_find(tlsctx_cache, tlsname, 422 isc_tlsctx_cache_tls, family, &found, 423 &found_store, &found_sess_cache); 424 if (result != ISC_R_SUCCESS) { 425 const char *hostname = 426 dns_transport_get_remote_hostname(transport); 427 const char *ca_file = dns_transport_get_cafile(transport); 428 const char *cert_file = dns_transport_get_certfile(transport); 429 const char *key_file = dns_transport_get_keyfile(transport); 430 const bool always_verify_remote = 431 dns_transport_get_always_verify_remote(transport); 432 char peer_addr_str[INET6_ADDRSTRLEN] = { 0 }; 433 isc_netaddr_t peer_netaddr = { 0 }; 434 bool hostname_ignore_subject; 435 436 /* 437 * So, no context exists. Let's create one using the 438 * parameters from the configuration file and try to 439 * store it for further reuse. 440 */ 441 result = isc_tlsctx_createclient(&tlsctx); 442 if (result != ISC_R_SUCCESS) { 443 goto failure; 444 } 445 tls_versions = dns_transport_get_tls_versions(transport); 446 if (tls_versions != 0) { 447 isc_tlsctx_set_protocols(tlsctx, tls_versions); 448 } 449 ciphers = dns_transport_get_ciphers(transport); 450 if (ciphers != NULL) { 451 isc_tlsctx_set_cipherlist(tlsctx, ciphers); 452 } 453 cipher_suites = dns_transport_get_cipher_suites(transport); 454 if (cipher_suites != NULL) { 455 isc_tlsctx_set_cipher_suites(tlsctx, cipher_suites); 456 } 457 458 if (dns_transport_get_prefer_server_ciphers( 459 transport, &prefer_server_ciphers)) 460 { 461 isc_tlsctx_prefer_server_ciphers(tlsctx, 462 prefer_server_ciphers); 463 } 464 465 if (always_verify_remote || hostname != NULL || ca_file != NULL) 466 { 467 /* 468 * The situation when 'found_store != NULL' while 469 * 'found == NULL' may occur as there is a one-to-many 470 * relation between cert stores and per-transport TLS 471 * contexts. That is, there could be one store 472 * shared between multiple contexts. 473 */ 474 if (found_store == NULL) { 475 /* 476 * 'ca_file' can equal 'NULL' here, in 477 * which case the store with system-wide 478 * CA certificates will be created. 479 */ 480 result = isc_tls_cert_store_create(ca_file, 481 &store); 482 483 if (result != ISC_R_SUCCESS) { 484 goto failure; 485 } 486 } else { 487 store = found_store; 488 } 489 490 INSIST(store != NULL); 491 if (hostname == NULL) { 492 /* 493 * If hostname is not specified, then use the 494 * peer IP address for validation. 495 */ 496 isc_netaddr_fromsockaddr(&peer_netaddr, peer); 497 isc_netaddr_format(&peer_netaddr, peer_addr_str, 498 sizeof(peer_addr_str)); 499 hostname = peer_addr_str; 500 } 501 502 /* 503 * According to RFC 8310, Subject field MUST NOT 504 * be inspected when verifying hostname for DoT. 505 * Only SubjectAltName must be checked. 506 */ 507 hostname_ignore_subject = true; 508 result = isc_tlsctx_enable_peer_verification( 509 tlsctx, false, store, hostname, 510 hostname_ignore_subject); 511 if (result != ISC_R_SUCCESS) { 512 goto failure; 513 } 514 515 /* 516 * Let's load client certificate and enable 517 * Mutual TLS. We do that only in the case when 518 * Strict TLS is enabled, because Mutual TLS is 519 * an extension of it. 520 */ 521 if (cert_file != NULL) { 522 INSIST(key_file != NULL); 523 524 result = isc_tlsctx_load_certificate( 525 tlsctx, key_file, cert_file); 526 if (result != ISC_R_SUCCESS) { 527 goto failure; 528 } 529 } 530 } 531 532 isc_tlsctx_enable_dot_client_alpn(tlsctx); 533 534 isc_tlsctx_client_session_cache_create( 535 mctx, tlsctx, 536 ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE, 537 &sess_cache); 538 539 found_store = NULL; 540 result = isc_tlsctx_cache_add(tlsctx_cache, tlsname, 541 isc_tlsctx_cache_tls, family, 542 tlsctx, store, sess_cache, &found, 543 &found_store, &found_sess_cache); 544 if (result == ISC_R_EXISTS) { 545 /* 546 * It seems the entry has just been created from 547 * within another thread while we were initialising 548 * ours. Although this is unlikely, it could happen 549 * after startup/re-initialisation. In such a case, 550 * discard the new context and associated data and use 551 * the already established one from now on. 552 * 553 * Such situation will not occur after the 554 * initial 'warm-up', so it is not critical 555 * performance-wise. 556 */ 557 INSIST(found != NULL); 558 isc_tlsctx_free(&tlsctx); 559 /* 560 * The 'store' variable can be 'NULL' when remote server 561 * verification is not enabled (that is, when Strict or 562 * Mutual TLS are not used). 563 * 564 * The 'found_store' might be equal to 'store' as there 565 * is one-to-many relation between a store and 566 * per-transport TLS contexts. In that case, the call to 567 * 'isc_tlsctx_cache_find()' above could have returned a 568 * store via the 'found_store' variable, whose value we 569 * can assign to 'store' later. In that case, 570 * 'isc_tlsctx_cache_add()' will return the same value. 571 * When that happens, we should not free the store 572 * object, as it is managed by the TLS context cache. 573 */ 574 if (store != NULL && store != found_store) { 575 isc_tls_cert_store_free(&store); 576 } 577 isc_tlsctx_client_session_cache_detach(&sess_cache); 578 /* Let's return the data from the cache. */ 579 *psess_cache = found_sess_cache; 580 *pctx = found; 581 } else { 582 /* 583 * Adding the fresh values into the cache has been 584 * successful, let's return them 585 */ 586 INSIST(result == ISC_R_SUCCESS); 587 *psess_cache = sess_cache; 588 *pctx = tlsctx; 589 } 590 } else { 591 /* 592 * The cache lookup has been successful, let's return the 593 * results. 594 */ 595 INSIST(result == ISC_R_SUCCESS); 596 *psess_cache = found_sess_cache; 597 *pctx = found; 598 } 599 600 return ISC_R_SUCCESS; 601 602 failure: 603 if (tlsctx != NULL) { 604 isc_tlsctx_free(&tlsctx); 605 } 606 607 /* 608 * The 'found_store' is being managed by the TLS context 609 * cache. Thus, we should keep it as it is, as it will get 610 * destroyed alongside the cache. As there is one store per 611 * multiple TLS contexts, we need to handle store deletion in a 612 * special way. 613 */ 614 if (store != NULL && store != found_store) { 615 isc_tls_cert_store_free(&store); 616 } 617 618 return result; 619 } 620 621 static void 622 transport_destroy(dns_transport_t *transport) { 623 isc_refcount_destroy(&transport->references); 624 transport->magic = 0; 625 626 if (transport->doh.endpoint != NULL) { 627 isc_mem_free(transport->mctx, transport->doh.endpoint); 628 } 629 if (transport->tls.remote_hostname != NULL) { 630 isc_mem_free(transport->mctx, transport->tls.remote_hostname); 631 } 632 if (transport->tls.cafile != NULL) { 633 isc_mem_free(transport->mctx, transport->tls.cafile); 634 } 635 if (transport->tls.keyfile != NULL) { 636 isc_mem_free(transport->mctx, transport->tls.keyfile); 637 } 638 if (transport->tls.certfile != NULL) { 639 isc_mem_free(transport->mctx, transport->tls.certfile); 640 } 641 if (transport->tls.ciphers != NULL) { 642 isc_mem_free(transport->mctx, transport->tls.ciphers); 643 } 644 if (transport->tls.cipher_suites != NULL) { 645 isc_mem_free(transport->mctx, transport->tls.cipher_suites); 646 } 647 648 if (transport->tls.tlsname != NULL) { 649 isc_mem_free(transport->mctx, transport->tls.tlsname); 650 } 651 652 isc_mem_putanddetach(&transport->mctx, transport, sizeof(*transport)); 653 } 654 655 void 656 dns_transport_attach(dns_transport_t *source, dns_transport_t **targetp) { 657 REQUIRE(source != NULL); 658 REQUIRE(targetp != NULL && *targetp == NULL); 659 660 isc_refcount_increment(&source->references); 661 662 *targetp = source; 663 } 664 665 void 666 dns_transport_detach(dns_transport_t **transportp) { 667 dns_transport_t *transport = NULL; 668 669 REQUIRE(transportp != NULL); 670 REQUIRE(VALID_TRANSPORT(*transportp)); 671 672 transport = *transportp; 673 *transportp = NULL; 674 675 if (isc_refcount_decrement(&transport->references) == 1) { 676 transport_destroy(transport); 677 } 678 } 679 680 dns_transport_t * 681 dns_transport_find(const dns_transport_type_t type, const dns_name_t *name, 682 dns_transport_list_t *list) { 683 isc_result_t result; 684 dns_transport_t *transport = NULL; 685 isc_hashmap_t *hm = NULL; 686 687 REQUIRE(VALID_TRANSPORT_LIST(list)); 688 REQUIRE(list->transports[type] != NULL); 689 690 hm = list->transports[type]; 691 692 RWLOCK(&list->lock, isc_rwlocktype_read); 693 result = isc_hashmap_find(hm, dns_name_hash(name), transport_match, 694 name, (void **)&transport); 695 if (result == ISC_R_SUCCESS) { 696 isc_refcount_increment(&transport->references); 697 } 698 RWUNLOCK(&list->lock, isc_rwlocktype_read); 699 700 return transport; 701 } 702 703 dns_transport_list_t * 704 dns_transport_list_new(isc_mem_t *mctx) { 705 dns_transport_list_t *list = isc_mem_get(mctx, sizeof(*list)); 706 707 *list = (dns_transport_list_t){ 0 }; 708 709 isc_rwlock_init(&list->lock); 710 711 isc_mem_attach(mctx, &list->mctx); 712 isc_refcount_init(&list->references, 1); 713 714 list->magic = TRANSPORT_LIST_MAGIC; 715 716 for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) { 717 isc_hashmap_create(list->mctx, 10, &list->transports[type]); 718 } 719 720 return list; 721 } 722 723 void 724 dns_transport_list_attach(dns_transport_list_t *source, 725 dns_transport_list_t **targetp) { 726 REQUIRE(VALID_TRANSPORT_LIST(source)); 727 REQUIRE(targetp != NULL && *targetp == NULL); 728 729 isc_refcount_increment(&source->references); 730 731 *targetp = source; 732 } 733 734 static void 735 transport_list_destroy(dns_transport_list_t *list) { 736 isc_refcount_destroy(&list->references); 737 list->magic = 0; 738 739 for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) { 740 isc_result_t result; 741 isc_hashmap_iter_t *it = NULL; 742 743 if (list->transports[type] == NULL) { 744 continue; 745 } 746 747 isc_hashmap_iter_create(list->transports[type], &it); 748 for (result = isc_hashmap_iter_first(it); 749 result == ISC_R_SUCCESS; 750 result = isc_hashmap_iter_delcurrent_next(it)) 751 { 752 dns_transport_t *transport = NULL; 753 isc_hashmap_iter_current(it, (void **)&transport); 754 dns_transport_detach(&transport); 755 } 756 isc_hashmap_iter_destroy(&it); 757 isc_hashmap_destroy(&list->transports[type]); 758 } 759 isc_rwlock_destroy(&list->lock); 760 isc_mem_putanddetach(&list->mctx, list, sizeof(*list)); 761 } 762 763 void 764 dns_transport_list_detach(dns_transport_list_t **listp) { 765 dns_transport_list_t *list = NULL; 766 767 REQUIRE(listp != NULL); 768 REQUIRE(VALID_TRANSPORT_LIST(*listp)); 769 770 list = *listp; 771 *listp = NULL; 772 773 if (isc_refcount_decrement(&list->references) == 1) { 774 transport_list_destroy(list); 775 } 776 } 777