1 /* $NetBSD: controlconf.c,v 1.13 2025/01/26 16:24:33 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/async.h> 22 #include <isc/base64.h> 23 #include <isc/buffer.h> 24 #include <isc/file.h> 25 #include <isc/mem.h> 26 #include <isc/mutex.h> 27 #include <isc/net.h> 28 #include <isc/netaddr.h> 29 #include <isc/netmgr.h> 30 #include <isc/nonce.h> 31 #include <isc/random.h> 32 #include <isc/refcount.h> 33 #include <isc/result.h> 34 #include <isc/stdtime.h> 35 #include <isc/string.h> 36 #include <isc/util.h> 37 38 #include <isccc/alist.h> 39 #include <isccc/cc.h> 40 #include <isccc/ccmsg.h> 41 #include <isccc/sexpr.h> 42 #include <isccc/symtab.h> 43 #include <isccc/util.h> 44 45 #include <isccfg/check.h> 46 #include <isccfg/namedconf.h> 47 48 #include <named/config.h> 49 #include <named/control.h> 50 #include <named/log.h> 51 #include <named/main.h> 52 #include <named/server.h> 53 54 /* Add -DNAMED_CONTROLCONF_TRACE=1 to CFLAGS for detailed reference tracing */ 55 56 typedef struct controlkey controlkey_t; 57 typedef ISC_LIST(controlkey_t) controlkeylist_t; 58 59 typedef struct controlconnection controlconnection_t; 60 typedef ISC_LIST(controlconnection_t) controlconnectionlist_t; 61 62 typedef struct controllistener controllistener_t; 63 typedef ISC_LIST(controllistener_t) controllistenerlist_t; 64 65 struct controlkey { 66 char *keyname; 67 uint32_t algorithm; 68 isc_region_t secret; 69 ISC_LINK(controlkey_t) link; 70 }; 71 72 struct controlconnection { 73 isc_refcount_t references; 74 isccc_ccmsg_t ccmsg; 75 controllistener_t *listener; 76 isccc_sexpr_t *ctrl; 77 isc_buffer_t *buffer; 78 isc_buffer_t *text; 79 isccc_sexpr_t *request; 80 isccc_sexpr_t *response; 81 uint32_t alg; 82 isccc_region_t secret; 83 uint32_t nonce; 84 isc_stdtime_t now; 85 isc_result_t result; 86 ISC_LINK(controlconnection_t) link; 87 bool shuttingdown; 88 }; 89 90 struct controllistener { 91 named_controls_t *controls; 92 isc_mem_t *mctx; 93 isc_sockaddr_t address; 94 isc_nmsocket_t *sock; 95 dns_acl_t *acl; 96 bool shuttingdown; 97 isc_refcount_t references; 98 controlkeylist_t keys; 99 controlconnectionlist_t connections; 100 isc_socktype_t type; 101 uint32_t perm; 102 uint32_t owner; 103 uint32_t group; 104 bool readonly; 105 ISC_LINK(controllistener_t) link; 106 }; 107 108 struct named_controls { 109 named_server_t *server; 110 controllistenerlist_t listeners; 111 bool shuttingdown; 112 isc_mutex_t symtab_lock; 113 isccc_symtab_t *symtab; 114 }; 115 116 static isc_result_t 117 control_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg); 118 static void 119 control_recvmessage(isc_nmhandle_t *handle, isc_result_t result, void *arg); 120 static void 121 conn_cleanup(controlconnection_t *conn); 122 static void 123 conn_free(controlconnection_t *conn); 124 static void 125 conn_shutdown(controlconnection_t *conn); 126 127 #if NAMED_CONTROLCONF_TRACE 128 #define controllistener_ref(ptr) \ 129 controllistener__ref(ptr, __func__, __FILE__, __LINE__) 130 #define controllistener_unref(ptr) \ 131 controllistener__unref(ptr, __func__, __FILE__, __LINE__) 132 #define controllistener_attach(ptr, ptrp) \ 133 controllistener__attach(ptr, ptrp, __func__, __FILE__, __LINE__) 134 #define controllistener_detach(ptrp) \ 135 controllistener__detach(ptrp, __func__, __FILE__, __LINE__) 136 ISC_REFCOUNT_TRACE_DECL(controllistener); 137 138 #define controlconnection_ref(ptr) \ 139 controlconnection__ref(ptr, __func__, __FILE__, __LINE__) 140 #define controlconnection_unref(ptr) \ 141 controlconnection__unref(ptr, __func__, __FILE__, __LINE__) 142 #define controlconnection_attach(ptr, ptrp) \ 143 controlconnection__attach(ptr, ptrp, __func__, __FILE__, __LINE__) 144 #define controlconnection_detach(ptrp) \ 145 controlconnection__detach(ptrp, __func__, __FILE__, __LINE__) 146 ISC_REFCOUNT_TRACE_DECL(controlconnection); 147 #else 148 ISC_REFCOUNT_DECL(controllistener); 149 ISC_REFCOUNT_DECL(controlconnection); 150 #endif 151 152 #define CLOCKSKEW 300 153 154 #define CHECK(x) \ 155 { \ 156 result = (x); \ 157 if (result != ISC_R_SUCCESS) { \ 158 goto cleanup; \ 159 } \ 160 } 161 162 static void 163 free_controlkey(controlkey_t *key, isc_mem_t *mctx) { 164 if (key->keyname != NULL) { 165 isc_mem_free(mctx, key->keyname); 166 } 167 if (key->secret.base != NULL) { 168 isc_mem_put(mctx, key->secret.base, key->secret.length); 169 } 170 isc_mem_put(mctx, key, sizeof(*key)); 171 } 172 173 static void 174 free_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) { 175 while (!ISC_LIST_EMPTY(*keylist)) { 176 controlkey_t *key = ISC_LIST_HEAD(*keylist); 177 ISC_LIST_UNLINK(*keylist, key, link); 178 free_controlkey(key, mctx); 179 } 180 } 181 182 static void 183 free_listener(controllistener_t *listener) { 184 REQUIRE(listener->shuttingdown); 185 REQUIRE(ISC_LIST_EMPTY(listener->connections)); 186 REQUIRE(listener->sock == NULL); 187 188 free_controlkeylist(&listener->keys, listener->mctx); 189 190 if (listener->acl != NULL) { 191 dns_acl_detach(&listener->acl); 192 } 193 194 isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener)); 195 } 196 197 #if NAMED_CONTROLCONF_TRACE 198 ISC_REFCOUNT_TRACE_IMPL(controllistener, free_listener); 199 ISC_REFCOUNT_TRACE_IMPL(controlconnection, conn_free); 200 #else 201 ISC_REFCOUNT_IMPL(controllistener, free_listener); 202 ISC_REFCOUNT_IMPL(controlconnection, conn_free); 203 #endif 204 205 static void 206 shutdown_listener(controllistener_t *listener) { 207 controlconnection_t *conn = NULL; 208 controlconnection_t *next = NULL; 209 210 /* Don't shutdown the same listener twice */ 211 if (listener->shuttingdown) { 212 return; 213 } 214 listener->shuttingdown = true; 215 216 for (conn = ISC_LIST_HEAD(listener->connections); conn != NULL; 217 conn = next) 218 { 219 /* 220 * 'conn' is likely to be freed by the conn_shutdown() call. 221 */ 222 next = ISC_LIST_NEXT(conn, link); 223 conn_shutdown(conn); 224 } 225 226 ISC_LIST_UNLINK(listener->controls->listeners, listener, link); 227 228 char socktext[ISC_SOCKADDR_FORMATSIZE]; 229 isc_sockaddr_format(&listener->address, socktext, sizeof(socktext)); 230 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 231 NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 232 "stopping command channel on %s", socktext); 233 234 isc_nm_stoplistening(listener->sock); 235 isc_nmsocket_close(&listener->sock); 236 controllistener_detach(&listener); 237 } 238 239 static bool 240 address_ok(isc_sockaddr_t *sockaddr, controllistener_t *listener) { 241 dns_aclenv_t *env = 242 ns_interfacemgr_getaclenv(named_g_server->interfacemgr); 243 isc_netaddr_t netaddr; 244 isc_result_t result; 245 int match; 246 247 isc_netaddr_fromsockaddr(&netaddr, sockaddr); 248 249 result = dns_acl_match(&netaddr, NULL, listener->acl, env, &match, 250 NULL); 251 return result == ISC_R_SUCCESS && match > 0; 252 } 253 254 static void 255 control_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) { 256 controlconnection_t *conn = (controlconnection_t *)arg; 257 258 if (conn->shuttingdown) { 259 /* The connection is shuttingdown */ 260 result = ISC_R_SHUTTINGDOWN; 261 } 262 263 if (result == ISC_R_SUCCESS) { 264 /* Everything is peachy, continue reading from the socket */ 265 isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, 266 conn); 267 /* Detach the sending reference */ 268 controlconnection_detach(&conn); 269 return; 270 } 271 272 if (result != ISC_R_SHUTTINGDOWN) { 273 char socktext[ISC_SOCKADDR_FORMATSIZE]; 274 isc_sockaddr_t peeraddr = isc_nmhandle_peeraddr(handle); 275 276 isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 277 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 278 NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, 279 "error sending command response to %s: %s", 280 socktext, isc_result_totext(result)); 281 } 282 283 /* Shutdown the reading */ 284 conn_shutdown(conn); 285 286 /* Detach the sending reference */ 287 controlconnection_detach(&conn); 288 } 289 290 static void 291 log_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) { 292 char socktext[ISC_SOCKADDR_FORMATSIZE]; 293 isc_sockaddr_t peeraddr = isc_nmhandle_peeraddr(ccmsg->handle); 294 295 isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 296 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 297 NAMED_LOGMODULE_CONTROL, ISC_LOG_ERROR, 298 "invalid command from %s: %s", socktext, 299 isc_result_totext(result)); 300 } 301 302 static void 303 conn_cleanup(controlconnection_t *conn) { 304 controllistener_t *listener = conn->listener; 305 306 if (conn->response != NULL) { 307 isccc_sexpr_free(&conn->response); 308 } 309 if (conn->request != NULL) { 310 isccc_sexpr_free(&conn->request); 311 } 312 if (conn->secret.rstart != NULL) { 313 isc_mem_put(listener->mctx, conn->secret.rstart, 314 REGION_SIZE(conn->secret)); 315 } 316 if (conn->text != NULL) { 317 isc_buffer_free(&conn->text); 318 } 319 } 320 321 static void 322 control_respond(controlconnection_t *conn) { 323 controllistener_t *listener = conn->listener; 324 isccc_sexpr_t *data = NULL; 325 isc_buffer_t b; 326 isc_region_t r; 327 isc_result_t result; 328 329 result = isccc_cc_createresponse(conn->request, conn->now, 330 conn->now + 60, &conn->response); 331 if (result != ISC_R_SUCCESS) { 332 goto cleanup; 333 } 334 335 if (conn->result == ISC_R_SHUTTINGDOWN) { 336 result = ISC_R_SUCCESS; 337 } else { 338 result = conn->result; 339 } 340 341 data = isccc_alist_lookup(conn->response, "_data"); 342 if (data != NULL) { 343 if (isccc_cc_defineuint32(data, "result", result) == NULL) { 344 goto cleanup; 345 } 346 } 347 348 if (result != ISC_R_SUCCESS) { 349 if (data != NULL) { 350 const char *estr = isc_result_totext(result); 351 if (isccc_cc_definestring(data, "err", estr) == NULL) { 352 goto cleanup; 353 } 354 } 355 } 356 357 if (isc_buffer_usedlength(conn->text) > 0) { 358 if (data != NULL) { 359 char *str = (char *)isc_buffer_base(conn->text); 360 if (isccc_cc_definestring(data, "text", str) == NULL) { 361 goto cleanup; 362 } 363 } 364 } 365 366 conn->ctrl = isccc_alist_lookup(conn->response, "_ctrl"); 367 if (conn->ctrl == NULL || 368 isccc_cc_defineuint32(conn->ctrl, "_nonce", conn->nonce) == NULL) 369 { 370 goto cleanup; 371 } 372 373 if (conn->buffer == NULL) { 374 isc_buffer_allocate(listener->mctx, &conn->buffer, 2 * 2048); 375 } 376 377 isc_buffer_clear(conn->buffer); 378 /* Skip the length field (4 bytes) */ 379 isc_buffer_add(conn->buffer, 4); 380 381 result = isccc_cc_towire(conn->response, &conn->buffer, conn->alg, 382 &conn->secret); 383 if (result != ISC_R_SUCCESS) { 384 return; 385 } 386 387 isc_buffer_init(&b, conn->buffer->base, 4); 388 isc_buffer_putuint32(&b, conn->buffer->used - 4); 389 390 r.base = conn->buffer->base; 391 r.length = conn->buffer->used; 392 393 /* Attach the sending reference */ 394 controlconnection_ref(conn); 395 isccc_ccmsg_sendmessage(&conn->ccmsg, &r, control_senddone, conn); 396 397 cleanup: 398 conn_cleanup(conn); 399 } 400 401 static void 402 control_command(void *arg) { 403 controlconnection_t *conn = (controlconnection_t *)arg; 404 405 /* Don't run the command if we already started the shutdown */ 406 if (!conn->shuttingdown) { 407 conn->result = named_control_docommand( 408 conn->request, conn->listener->readonly, &conn->text); 409 control_respond(conn); 410 } 411 412 /* Detach the control command reference */ 413 controlconnection_detach(&conn); 414 } 415 416 static void 417 conn_shutdown(controlconnection_t *conn) { 418 /* Don't shutdown the same controlconnection twice */ 419 if (conn->shuttingdown) { 420 return; 421 } 422 conn->shuttingdown = true; 423 424 /* 425 * Close the TCP connection to make sure that no read callback will be 426 * called for it ever again. 427 */ 428 isccc_ccmsg_disconnect(&conn->ccmsg); 429 430 /* Detach the reading reference */ 431 controlconnection_detach(&conn); 432 } 433 434 static void 435 control_recvmessage(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result, 436 void *arg) { 437 controlconnection_t *conn = (controlconnection_t *)arg; 438 controllistener_t *listener = conn->listener; 439 controlkey_t *key = NULL; 440 isccc_time_t sent; 441 isccc_time_t exp; 442 uint32_t nonce; 443 444 if (result != ISC_R_SUCCESS) { 445 goto cleanup; 446 } 447 448 for (key = ISC_LIST_HEAD(listener->keys); key != NULL; 449 key = ISC_LIST_NEXT(key, link)) 450 { 451 isccc_region_t ccregion; 452 453 isccc_ccmsg_toregion(&conn->ccmsg, &ccregion); 454 conn->secret.rstart = isc_mem_get(listener->mctx, 455 key->secret.length); 456 memmove(conn->secret.rstart, key->secret.base, 457 key->secret.length); 458 conn->secret.rend = conn->secret.rstart + key->secret.length; 459 conn->alg = key->algorithm; 460 result = isccc_cc_fromwire(&ccregion, &conn->request, conn->alg, 461 &conn->secret); 462 if (result == ISC_R_SUCCESS) { 463 break; 464 } 465 isc_mem_put(listener->mctx, conn->secret.rstart, 466 REGION_SIZE(conn->secret)); 467 } 468 469 if (key == NULL) { 470 result = ISCCC_R_BADAUTH; 471 goto cleanup; 472 } 473 474 /* We shouldn't be getting a reply. */ 475 if (isccc_cc_isreply(conn->request)) { 476 result = ISC_R_FAILURE; 477 goto cleanup; 478 } 479 480 conn->now = isc_stdtime_now(); 481 482 /* 483 * Limit exposure to replay attacks. 484 */ 485 conn->ctrl = isccc_alist_lookup(conn->request, "_ctrl"); 486 if (!isccc_alist_alistp(conn->ctrl)) { 487 result = ISC_R_FAILURE; 488 goto cleanup; 489 } 490 491 if (isccc_cc_lookupuint32(conn->ctrl, "_tim", &sent) == ISC_R_SUCCESS) { 492 if ((sent + CLOCKSKEW) < conn->now || 493 (sent - CLOCKSKEW) > conn->now) 494 { 495 result = ISCCC_R_CLOCKSKEW; 496 goto cleanup; 497 } 498 } else { 499 result = ISC_R_FAILURE; 500 goto cleanup; 501 } 502 503 /* 504 * Expire messages that are too old. 505 */ 506 if (isccc_cc_lookupuint32(conn->ctrl, "_exp", &exp) == ISC_R_SUCCESS && 507 conn->now > exp) 508 { 509 result = ISCCC_R_EXPIRED; 510 goto cleanup; 511 } 512 513 /* 514 * Duplicate suppression (required for UDP). 515 */ 516 LOCK(&listener->controls->symtab_lock); 517 isccc_cc_cleansymtab(listener->controls->symtab, conn->now); 518 result = isccc_cc_checkdup(listener->controls->symtab, conn->request, 519 conn->now); 520 UNLOCK(&listener->controls->symtab_lock); 521 if (result != ISC_R_SUCCESS) { 522 if (result == ISC_R_EXISTS) { 523 result = ISCCC_R_DUPLICATE; 524 } 525 goto cleanup; 526 } 527 528 if (conn->nonce != 0 && 529 (isccc_cc_lookupuint32(conn->ctrl, "_nonce", &nonce) != 530 ISC_R_SUCCESS || 531 conn->nonce != nonce)) 532 { 533 result = ISCCC_R_BADAUTH; 534 goto cleanup; 535 } 536 537 isc_buffer_allocate(listener->mctx, &conn->text, 2 * 2048); 538 539 if (conn->nonce == 0) { 540 /* 541 * Establish nonce. 542 */ 543 while (conn->nonce == 0) { 544 isc_nonce_buf(&conn->nonce, sizeof(conn->nonce)); 545 } 546 conn->result = ISC_R_SUCCESS; 547 control_respond(conn); 548 return; 549 } 550 551 /* Attach the command reference */ 552 controlconnection_ref(conn); 553 554 /* Trigger the command asynchronously. */ 555 isc_async_run(named_g_mainloop, control_command, conn); 556 557 return; 558 559 cleanup: 560 switch (result) { 561 case ISC_R_SHUTTINGDOWN: 562 case ISC_R_EOF: 563 break; 564 default: 565 log_invalid(&conn->ccmsg, result); 566 } 567 568 conn_shutdown(conn); 569 } 570 571 static void 572 conn_free(controlconnection_t *conn) { 573 /* Make sure that the connection was shutdown first */ 574 REQUIRE(conn->shuttingdown); 575 576 controllistener_t *listener = conn->listener; 577 578 isccc_ccmsg_invalidate(&conn->ccmsg); 579 580 conn_cleanup(conn); 581 582 if (conn->buffer != NULL) { 583 isc_buffer_free(&conn->buffer); 584 } 585 586 ISC_LIST_UNLINK(listener->connections, conn, link); 587 #ifdef ENABLE_AFL 588 if (named_g_fuzz_type == isc_fuzz_rndc) { 589 named_fuzz_notify(); 590 } 591 #endif /* ifdef ENABLE_AFL */ 592 593 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 594 NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3), 595 "freeing control connection"); 596 597 isc_mem_put(listener->mctx, conn, sizeof(*conn)); 598 599 controllistener_detach(&listener); 600 } 601 602 static void 603 newconnection(controllistener_t *listener, isc_nmhandle_t *handle) { 604 /* Don't create new connection if we are shutting down */ 605 if (listener->shuttingdown) { 606 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 607 NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3), 608 "rejected new control connection: %s", 609 isc_result_totext(ISC_R_SHUTTINGDOWN)); 610 return; 611 } 612 613 controlconnection_t *conn = isc_mem_get(listener->mctx, sizeof(*conn)); 614 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 615 NAMED_LOGMODULE_CONTROL, ISC_LOG_DEBUG(3), 616 "allocate new control connection"); 617 618 *conn = (controlconnection_t){ 619 .alg = DST_ALG_UNKNOWN, 620 .references = ISC_REFCOUNT_INITIALIZER(1), 621 .listener = controllistener_ref(listener), 622 .link = ISC_LINK_INITIALIZER, 623 }; 624 625 /* isccc_ccmsg_init() attaches to the handle */ 626 isccc_ccmsg_init(listener->mctx, handle, &conn->ccmsg); 627 628 /* Set a 32 KiB upper limit on incoming message. */ 629 isccc_ccmsg_setmaxsize(&conn->ccmsg, 32768); 630 631 ISC_LIST_APPEND(listener->connections, conn, link); 632 633 /* The reading reference has been initialized in the initializer */ 634 isccc_ccmsg_readmessage(&conn->ccmsg, control_recvmessage, conn); 635 } 636 637 static isc_result_t 638 control_newconn(isc_nmhandle_t *handle, isc_result_t result, void *arg) { 639 controllistener_t *listener = arg; 640 isc_sockaddr_t peeraddr; 641 642 if (result != ISC_R_SUCCESS) { 643 if (result == ISC_R_SHUTTINGDOWN) { 644 shutdown_listener(listener); 645 } 646 return result; 647 } 648 649 peeraddr = isc_nmhandle_peeraddr(handle); 650 if (!address_ok(&peeraddr, listener)) { 651 char socktext[ISC_SOCKADDR_FORMATSIZE]; 652 isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 653 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 654 NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, 655 "rejected command channel message from %s", 656 socktext); 657 return ISC_R_FAILURE; 658 } 659 660 newconnection(listener, handle); 661 return ISC_R_SUCCESS; 662 } 663 664 static void 665 controls_shutdown(named_controls_t *controls) { 666 controllistener_t *listener = NULL; 667 controllistener_t *next = NULL; 668 669 for (listener = ISC_LIST_HEAD(controls->listeners); listener != NULL; 670 listener = next) 671 { 672 /* 673 * As listeners shut down, they will call their callbacks. 674 */ 675 next = ISC_LIST_NEXT(listener, link); 676 shutdown_listener(listener); 677 } 678 } 679 680 void 681 named_controls_shutdown(named_controls_t *controls) { 682 /* 683 * Don't ever shutdown the controls twice. 684 * 685 * NOTE: This functions is called when the server is shutting down, but 686 * controls_shutdown() can and will be called multiple times - on each 687 * reconfiguration, the listeners will be torn down and recreated again, 688 * see named_controls_configure() for details. 689 */ 690 if (controls->shuttingdown) { 691 return; 692 } 693 controls->shuttingdown = true; 694 695 controls_shutdown(controls); 696 } 697 698 static isc_result_t 699 cfgkeylist_find(const cfg_obj_t *keylist, const char *keyname, 700 const cfg_obj_t **objp) { 701 const cfg_listelt_t *element = NULL; 702 const char *str = NULL; 703 const cfg_obj_t *obj = NULL; 704 705 for (element = cfg_list_first(keylist); element != NULL; 706 element = cfg_list_next(element)) 707 { 708 obj = cfg_listelt_value(element); 709 str = cfg_obj_asstring(cfg_map_getname(obj)); 710 if (strcasecmp(str, keyname) == 0) { 711 break; 712 } 713 } 714 if (element == NULL) { 715 return ISC_R_NOTFOUND; 716 } 717 obj = cfg_listelt_value(element); 718 *objp = obj; 719 return ISC_R_SUCCESS; 720 } 721 722 static void 723 controlkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx, 724 controlkeylist_t *keyids) { 725 const cfg_listelt_t *element = NULL; 726 char *newstr = NULL; 727 const char *str = NULL; 728 const cfg_obj_t *obj = NULL; 729 controlkey_t *key = NULL; 730 731 for (element = cfg_list_first(keylist); element != NULL; 732 element = cfg_list_next(element)) 733 { 734 obj = cfg_listelt_value(element); 735 str = cfg_obj_asstring(obj); 736 newstr = isc_mem_strdup(mctx, str); 737 key = isc_mem_get(mctx, sizeof(*key)); 738 key->keyname = newstr; 739 key->algorithm = DST_ALG_UNKNOWN; 740 key->secret.base = NULL; 741 key->secret.length = 0; 742 ISC_LINK_INIT(key, link); 743 ISC_LIST_APPEND(*keyids, key, link); 744 newstr = NULL; 745 } 746 } 747 748 static void 749 register_keys(const cfg_obj_t *control, const cfg_obj_t *keylist, 750 controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext) { 751 controlkey_t *keyid = NULL, *next = NULL; 752 const cfg_obj_t *keydef = NULL; 753 char secret[1024]; 754 isc_buffer_t b; 755 isc_result_t result; 756 757 /* 758 * Find the keys corresponding to the keyids used by this listener. 759 */ 760 for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) { 761 next = ISC_LIST_NEXT(keyid, link); 762 763 result = cfgkeylist_find(keylist, keyid->keyname, &keydef); 764 if (result != ISC_R_SUCCESS) { 765 cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, 766 "couldn't find key '%s' for use with " 767 "command channel %s", 768 keyid->keyname, socktext); 769 ISC_LIST_UNLINK(*keyids, keyid, link); 770 free_controlkey(keyid, mctx); 771 } else { 772 const cfg_obj_t *algobj = NULL; 773 const cfg_obj_t *secretobj = NULL; 774 const char *algstr = NULL; 775 const char *secretstr = NULL; 776 unsigned int algtype; 777 778 (void)cfg_map_get(keydef, "algorithm", &algobj); 779 (void)cfg_map_get(keydef, "secret", &secretobj); 780 INSIST(algobj != NULL && secretobj != NULL); 781 782 algstr = cfg_obj_asstring(algobj); 783 secretstr = cfg_obj_asstring(secretobj); 784 785 result = named_config_getkeyalgorithm(algstr, &algtype, 786 NULL); 787 if (result != ISC_R_SUCCESS) { 788 cfg_obj_log(control, named_g_lctx, 789 ISC_LOG_WARNING, 790 "unsupported algorithm '%s' in " 791 "key '%s' for use with command " 792 "channel %s", 793 algstr, keyid->keyname, socktext); 794 ISC_LIST_UNLINK(*keyids, keyid, link); 795 free_controlkey(keyid, mctx); 796 continue; 797 } 798 799 keyid->algorithm = algtype; 800 isc_buffer_init(&b, secret, sizeof(secret)); 801 result = isc_base64_decodestring(secretstr, &b); 802 803 if (result != ISC_R_SUCCESS) { 804 cfg_obj_log(keydef, named_g_lctx, 805 ISC_LOG_WARNING, 806 "secret for key '%s' on " 807 "command channel %s: %s", 808 keyid->keyname, socktext, 809 isc_result_totext(result)); 810 ISC_LIST_UNLINK(*keyids, keyid, link); 811 free_controlkey(keyid, mctx); 812 continue; 813 } 814 815 keyid->secret.length = isc_buffer_usedlength(&b); 816 keyid->secret.base = isc_mem_get(mctx, 817 keyid->secret.length); 818 memmove(keyid->secret.base, isc_buffer_base(&b), 819 keyid->secret.length); 820 } 821 } 822 } 823 824 static isc_result_t 825 get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) { 826 isc_result_t result; 827 cfg_parser_t *pctx = NULL; 828 cfg_obj_t *config = NULL; 829 const cfg_obj_t *key = NULL; 830 const cfg_obj_t *algobj = NULL; 831 const cfg_obj_t *secretobj = NULL; 832 const char *algstr = NULL; 833 const char *secretstr = NULL; 834 controlkey_t *keyid = NULL; 835 char secret[1024]; 836 unsigned int algtype; 837 isc_buffer_t b; 838 839 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 840 NAMED_LOGMODULE_CONTROL, ISC_LOG_INFO, 841 "configuring command channel from '%s'", named_g_keyfile); 842 if (!isc_file_exists(named_g_keyfile)) { 843 return ISC_R_FILENOTFOUND; 844 } 845 846 CHECK(cfg_parser_create(mctx, named_g_lctx, &pctx)); 847 CHECK(cfg_parse_file(pctx, named_g_keyfile, &cfg_type_rndckey, 848 &config)); 849 CHECK(cfg_map_get(config, "key", &key)); 850 851 keyid = isc_mem_get(mctx, sizeof(*keyid)); 852 keyid->keyname = isc_mem_strdup(mctx, 853 cfg_obj_asstring(cfg_map_getname(key))); 854 keyid->secret.base = NULL; 855 keyid->secret.length = 0; 856 keyid->algorithm = DST_ALG_UNKNOWN; 857 ISC_LINK_INIT(keyid, link); 858 if (keyid->keyname == NULL) { 859 CHECK(ISC_R_NOMEMORY); 860 } 861 862 CHECK(isccfg_check_key(key, named_g_lctx)); 863 864 (void)cfg_map_get(key, "algorithm", &algobj); 865 (void)cfg_map_get(key, "secret", &secretobj); 866 INSIST(algobj != NULL && secretobj != NULL); 867 868 algstr = cfg_obj_asstring(algobj); 869 secretstr = cfg_obj_asstring(secretobj); 870 871 result = named_config_getkeyalgorithm(algstr, &algtype, NULL); 872 if (result != ISC_R_SUCCESS) { 873 cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING, 874 "unsupported algorithm '%s' in " 875 "key '%s' for use with command " 876 "channel", 877 algstr, keyid->keyname); 878 goto cleanup; 879 } 880 881 keyid->algorithm = algtype; 882 isc_buffer_init(&b, secret, sizeof(secret)); 883 result = isc_base64_decodestring(secretstr, &b); 884 885 if (result != ISC_R_SUCCESS) { 886 cfg_obj_log(key, named_g_lctx, ISC_LOG_WARNING, 887 "secret for key '%s' on command channel: %s", 888 keyid->keyname, isc_result_totext(result)); 889 goto cleanup; 890 } 891 892 keyid->secret.length = isc_buffer_usedlength(&b); 893 keyid->secret.base = isc_mem_get(mctx, keyid->secret.length); 894 memmove(keyid->secret.base, isc_buffer_base(&b), keyid->secret.length); 895 ISC_LIST_APPEND(*keyids, keyid, link); 896 keyid = NULL; 897 result = ISC_R_SUCCESS; 898 899 cleanup: 900 if (keyid != NULL) { 901 free_controlkey(keyid, mctx); 902 } 903 if (config != NULL) { 904 cfg_obj_destroy(pctx, &config); 905 } 906 if (pctx != NULL) { 907 cfg_parser_destroy(&pctx); 908 } 909 return result; 910 } 911 912 /* 913 * Ensures that both '*global_keylistp' and '*control_keylistp' are 914 * valid or both are NULL. 915 */ 916 static void 917 get_key_info(const cfg_obj_t *config, const cfg_obj_t *control, 918 const cfg_obj_t **global_keylistp, 919 const cfg_obj_t **control_keylistp) { 920 isc_result_t result; 921 const cfg_obj_t *control_keylist = NULL; 922 const cfg_obj_t *global_keylist = NULL; 923 924 REQUIRE(global_keylistp != NULL && *global_keylistp == NULL); 925 REQUIRE(control_keylistp != NULL && *control_keylistp == NULL); 926 927 control_keylist = cfg_tuple_get(control, "keys"); 928 929 if (!cfg_obj_isvoid(control_keylist) && 930 cfg_list_first(control_keylist) != NULL) 931 { 932 result = cfg_map_get(config, "key", &global_keylist); 933 934 if (result == ISC_R_SUCCESS) { 935 *global_keylistp = global_keylist; 936 *control_keylistp = control_keylist; 937 } 938 } 939 } 940 941 static void 942 update_listener(named_controls_t *cp, controllistener_t **listenerp, 943 const cfg_obj_t *control, const cfg_obj_t *config, 944 isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, 945 const char *socktext, isc_socktype_t type) { 946 controllistener_t *listener = NULL; 947 const cfg_obj_t *allow = NULL; 948 const cfg_obj_t *global_keylist = NULL; 949 const cfg_obj_t *control_keylist = NULL; 950 dns_acl_t *new_acl = NULL; 951 controlkeylist_t keys; 952 isc_result_t result = ISC_R_SUCCESS; 953 954 for (listener = ISC_LIST_HEAD(cp->listeners); listener != NULL; 955 listener = ISC_LIST_NEXT(listener, link)) 956 { 957 if (isc_sockaddr_equal(addr, &listener->address)) { 958 break; 959 } 960 } 961 962 if (listener == NULL) { 963 *listenerp = NULL; 964 return; 965 } 966 967 /* 968 * There is already a listener for this sockaddr. 969 * Update the access list and key information. 970 * 971 * First try to deal with the key situation. There are a few 972 * possibilities: 973 * (a) It had an explicit keylist and still has an explicit keylist. 974 * (b) It had an automagic key and now has an explicit keylist. 975 * (c) It had an explicit keylist and now needs an automagic key. 976 * (d) It has an automagic key and still needs the automagic key. 977 * 978 * (c) and (d) are the annoying ones. The caller needs to know 979 * that it should use the automagic configuration for key information 980 * in place of the named.conf configuration. 981 * 982 * XXXDCL There is one other hazard that has not been dealt with, 983 * the problem that if a key change is being caused by a control 984 * channel reload, then the response will be with the new key 985 * and not able to be decrypted by the client. 986 */ 987 if (control != NULL) { 988 get_key_info(config, control, &global_keylist, 989 &control_keylist); 990 } 991 992 if (control_keylist != NULL) { 993 INSIST(global_keylist != NULL); 994 995 ISC_LIST_INIT(keys); 996 controlkeylist_fromcfg(control_keylist, listener->mctx, &keys); 997 free_controlkeylist(&listener->keys, listener->mctx); 998 listener->keys = keys; 999 register_keys(control, global_keylist, &listener->keys, 1000 listener->mctx, socktext); 1001 } else { 1002 free_controlkeylist(&listener->keys, listener->mctx); 1003 result = get_rndckey(listener->mctx, &listener->keys); 1004 } 1005 1006 if (result != ISC_R_SUCCESS && global_keylist != NULL) { 1007 /* 1008 * This message might be a little misleading since the 1009 * "new keys" might in fact be identical to the old ones, 1010 * but tracking whether they are identical just for the 1011 * sake of avoiding this message would be too much trouble. 1012 */ 1013 if (control != NULL) { 1014 cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, 1015 "couldn't install new keys for " 1016 "command channel %s: %s", 1017 socktext, isc_result_totext(result)); 1018 } else { 1019 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 1020 NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, 1021 "couldn't install new keys for " 1022 "command channel %s: %s", 1023 socktext, isc_result_totext(result)); 1024 } 1025 } 1026 1027 /* 1028 * Now, keep the old access list unless a new one can be made. 1029 */ 1030 if (control != NULL && type == isc_socktype_tcp) { 1031 allow = cfg_tuple_get(control, "allow"); 1032 result = cfg_acl_fromconfig(allow, config, named_g_lctx, 1033 aclconfctx, listener->mctx, 0, 1034 &new_acl); 1035 } else { 1036 result = dns_acl_any(listener->mctx, &new_acl); 1037 } 1038 1039 if (control != NULL) { 1040 const cfg_obj_t *readonly = NULL; 1041 1042 readonly = cfg_tuple_get(control, "read-only"); 1043 if (!cfg_obj_isvoid(readonly)) { 1044 listener->readonly = cfg_obj_asboolean(readonly); 1045 } 1046 } 1047 1048 if (result == ISC_R_SUCCESS) { 1049 dns_acl_detach(&listener->acl); 1050 dns_acl_attach(new_acl, &listener->acl); 1051 dns_acl_detach(&new_acl); 1052 /* XXXDCL say the old acl is still used? */ 1053 } else if (control != NULL) { 1054 cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, 1055 "couldn't install new acl for " 1056 "command channel %s: %s", 1057 socktext, isc_result_totext(result)); 1058 } else { 1059 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 1060 NAMED_LOGMODULE_CONTROL, ISC_LOG_WARNING, 1061 "couldn't install new acl for " 1062 "command channel %s: %s", 1063 socktext, isc_result_totext(result)); 1064 } 1065 1066 *listenerp = listener; 1067 } 1068 1069 static void 1070 add_listener(named_controls_t *cp, controllistener_t **listenerp, 1071 const cfg_obj_t *control, const cfg_obj_t *config, 1072 isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, 1073 const char *socktext, isc_socktype_t type) { 1074 isc_mem_t *mctx = cp->server->mctx; 1075 controllistener_t *listener = NULL; 1076 const cfg_obj_t *allow = NULL; 1077 const cfg_obj_t *global_keylist = NULL; 1078 const cfg_obj_t *control_keylist = NULL; 1079 dns_acl_t *new_acl = NULL; 1080 isc_result_t result = ISC_R_SUCCESS; 1081 int pf; 1082 1083 /* Don't create new listener if we are shutting down */ 1084 if (cp->shuttingdown) { 1085 result = ISC_R_SHUTTINGDOWN; 1086 goto shuttingdown; 1087 } 1088 1089 listener = isc_mem_get(mctx, sizeof(*listener)); 1090 *listener = (controllistener_t){ .controls = cp, 1091 .address = *addr, 1092 .type = type }; 1093 isc_mem_attach(mctx, &listener->mctx); 1094 ISC_LINK_INIT(listener, link); 1095 ISC_LIST_INIT(listener->keys); 1096 ISC_LIST_INIT(listener->connections); 1097 isc_refcount_init(&listener->references, 1); 1098 1099 /* 1100 * Make the ACL. 1101 */ 1102 if (control != NULL && type == isc_socktype_tcp) { 1103 const cfg_obj_t *readonly = NULL; 1104 1105 allow = cfg_tuple_get(control, "allow"); 1106 CHECK(cfg_acl_fromconfig(allow, config, named_g_lctx, 1107 aclconfctx, mctx, 0, &new_acl)); 1108 1109 readonly = cfg_tuple_get(control, "read-only"); 1110 if (!cfg_obj_isvoid(readonly)) { 1111 listener->readonly = cfg_obj_asboolean(readonly); 1112 } 1113 } else { 1114 CHECK(dns_acl_any(mctx, &new_acl)); 1115 } 1116 1117 dns_acl_attach(new_acl, &listener->acl); 1118 dns_acl_detach(&new_acl); 1119 1120 if (config != NULL) { 1121 get_key_info(config, control, &global_keylist, 1122 &control_keylist); 1123 } 1124 1125 if (control_keylist != NULL) { 1126 controlkeylist_fromcfg(control_keylist, listener->mctx, 1127 &listener->keys); 1128 register_keys(control, global_keylist, &listener->keys, 1129 listener->mctx, socktext); 1130 } else { 1131 result = get_rndckey(mctx, &listener->keys); 1132 if (result != ISC_R_SUCCESS && control != NULL) { 1133 cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, 1134 "couldn't install keys for " 1135 "command channel %s: %s", 1136 socktext, isc_result_totext(result)); 1137 } 1138 } 1139 1140 pf = isc_sockaddr_pf(&listener->address); 1141 if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) || 1142 (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) 1143 { 1144 CHECK(ISC_R_FAMILYNOSUPPORT); 1145 } 1146 1147 CHECK(isc_nm_listentcp(named_g_netmgr, ISC_NM_LISTEN_ONE, 1148 &listener->address, control_newconn, listener, 5, 1149 NULL, &listener->sock)); 1150 1151 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 1152 NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 1153 "command channel listening on %s", socktext); 1154 *listenerp = listener; 1155 return; 1156 1157 cleanup: 1158 isc_refcount_decrement(&listener->references); 1159 listener->shuttingdown = true; 1160 free_listener(listener); 1161 1162 shuttingdown: 1163 if (control != NULL) { 1164 cfg_obj_log(control, named_g_lctx, ISC_LOG_WARNING, 1165 "couldn't add command channel %s: %s", socktext, 1166 isc_result_totext(result)); 1167 } else { 1168 isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL, 1169 NAMED_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 1170 "couldn't add command channel %s: %s", socktext, 1171 isc_result_totext(result)); 1172 } 1173 1174 *listenerp = NULL; 1175 } 1176 1177 isc_result_t 1178 named_controls_configure(named_controls_t *cp, const cfg_obj_t *config, 1179 cfg_aclconfctx_t *aclconfctx) { 1180 controllistener_t *listener = NULL; 1181 controllistenerlist_t new_listeners; 1182 const cfg_obj_t *controlslist = NULL; 1183 const cfg_listelt_t *element, *element2; 1184 char socktext[ISC_SOCKADDR_FORMATSIZE]; 1185 1186 ISC_LIST_INIT(new_listeners); 1187 1188 /* 1189 * Get the list of named.conf 'controls' statements. 1190 */ 1191 (void)cfg_map_get(config, "controls", &controlslist); 1192 1193 /* 1194 * Run through the new control channel list, noting sockets that 1195 * are already being listened on and moving them to the new list. 1196 * 1197 * Identifying duplicate addr/port combinations is left to either 1198 * the underlying config code, or to the bind attempt getting an 1199 * address-in-use error. 1200 */ 1201 if (controlslist != NULL) { 1202 for (element = cfg_list_first(controlslist); element != NULL; 1203 element = cfg_list_next(element)) 1204 { 1205 const cfg_obj_t *controls = NULL; 1206 const cfg_obj_t *inetcontrols = NULL; 1207 const cfg_obj_t *unixcontrols = NULL; 1208 1209 controls = cfg_listelt_value(element); 1210 1211 (void)cfg_map_get(controls, "unix", &unixcontrols); 1212 if (unixcontrols != NULL) { 1213 cfg_obj_log(controls, named_g_lctx, 1214 ISC_LOG_ERROR, 1215 "UNIX domain sockets are not " 1216 "supported"); 1217 return ISC_R_FAILURE; 1218 } 1219 1220 (void)cfg_map_get(controls, "inet", &inetcontrols); 1221 if (inetcontrols == NULL) { 1222 continue; 1223 } 1224 1225 for (element2 = cfg_list_first(inetcontrols); 1226 element2 != NULL; 1227 element2 = cfg_list_next(element2)) 1228 { 1229 const cfg_obj_t *control = NULL; 1230 const cfg_obj_t *obj = NULL; 1231 isc_sockaddr_t addr; 1232 1233 /* 1234 * The parser handles BIND 8 configuration file 1235 * syntax, so it allows inet phrases with no 1236 * keys{} clause. 1237 */ 1238 control = cfg_listelt_value(element2); 1239 1240 obj = cfg_tuple_get(control, "address"); 1241 addr = *cfg_obj_assockaddr(obj); 1242 if (isc_sockaddr_getport(&addr) == 0) { 1243 isc_sockaddr_setport( 1244 &addr, NAMED_CONTROL_PORT); 1245 } 1246 1247 isc_sockaddr_format(&addr, socktext, 1248 sizeof(socktext)); 1249 1250 isc_log_write(named_g_lctx, 1251 NAMED_LOGCATEGORY_GENERAL, 1252 NAMED_LOGMODULE_CONTROL, 1253 ISC_LOG_DEBUG(9), 1254 "processing control channel %s", 1255 socktext); 1256 1257 update_listener(cp, &listener, control, config, 1258 &addr, aclconfctx, socktext, 1259 isc_socktype_tcp); 1260 1261 if (listener != NULL) { 1262 /* 1263 * Remove the listener from the old 1264 * list, so it won't be shut down. 1265 */ 1266 ISC_LIST_UNLINK(cp->listeners, listener, 1267 link); 1268 } else { 1269 /* 1270 * This is a new listener. 1271 */ 1272 add_listener(cp, &listener, control, 1273 config, &addr, aclconfctx, 1274 socktext, 1275 isc_socktype_tcp); 1276 } 1277 1278 if (listener != NULL) { 1279 ISC_LIST_APPEND(new_listeners, listener, 1280 link); 1281 } 1282 } 1283 } 1284 } else { 1285 int i; 1286 1287 for (i = 0; i < 2; i++) { 1288 isc_sockaddr_t addr; 1289 1290 if (i == 0) { 1291 struct in_addr localhost; 1292 1293 if (isc_net_probeipv4() != ISC_R_SUCCESS) { 1294 continue; 1295 } 1296 localhost.s_addr = htonl(INADDR_LOOPBACK); 1297 isc_sockaddr_fromin(&addr, &localhost, 0); 1298 } else { 1299 if (isc_net_probeipv6() != ISC_R_SUCCESS) { 1300 continue; 1301 } 1302 isc_sockaddr_fromin6(&addr, &in6addr_loopback, 1303 0); 1304 } 1305 isc_sockaddr_setport(&addr, NAMED_CONTROL_PORT); 1306 1307 isc_sockaddr_format(&addr, socktext, sizeof(socktext)); 1308 1309 update_listener(cp, &listener, NULL, NULL, &addr, NULL, 1310 socktext, isc_socktype_tcp); 1311 1312 if (listener != NULL) { 1313 /* 1314 * Remove the listener from the old 1315 * list, so it won't be shut down. 1316 */ 1317 ISC_LIST_UNLINK(cp->listeners, listener, link); 1318 } else { 1319 /* 1320 * This is a new listener. 1321 */ 1322 add_listener(cp, &listener, NULL, NULL, &addr, 1323 NULL, socktext, isc_socktype_tcp); 1324 } 1325 1326 if (listener != NULL) { 1327 ISC_LIST_APPEND(new_listeners, listener, link); 1328 } 1329 } 1330 } 1331 1332 /* 1333 * named_control_shutdown() will stop whatever is on the global 1334 * listeners list, which currently only has whatever sockaddrs 1335 * were in the previous configuration (if any) that do not 1336 * remain in the current configuration. 1337 */ 1338 controls_shutdown(cp); 1339 1340 /* 1341 * Put all of the valid listeners on the listeners list. 1342 * Anything already on listeners in the process of shutting 1343 * down will be taken care of by listen_done(). 1344 */ 1345 ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link); 1346 return ISC_R_SUCCESS; 1347 } 1348 1349 isc_result_t 1350 named_controls_create(named_server_t *server, named_controls_t **ctrlsp) { 1351 isc_mem_t *mctx = server->mctx; 1352 isc_result_t result; 1353 named_controls_t *controls = isc_mem_get(mctx, sizeof(*controls)); 1354 1355 *controls = (named_controls_t){ 1356 .server = server, 1357 }; 1358 1359 ISC_LIST_INIT(controls->listeners); 1360 1361 isc_mutex_init(&controls->symtab_lock); 1362 LOCK(&controls->symtab_lock); 1363 result = isccc_cc_createsymtab(&controls->symtab); 1364 UNLOCK(&controls->symtab_lock); 1365 1366 if (result != ISC_R_SUCCESS) { 1367 isc_mutex_destroy(&controls->symtab_lock); 1368 isc_mem_put(server->mctx, controls, sizeof(*controls)); 1369 return result; 1370 } 1371 *ctrlsp = controls; 1372 return ISC_R_SUCCESS; 1373 } 1374 1375 void 1376 named_controls_destroy(named_controls_t **ctrlsp) { 1377 named_controls_t *controls = *ctrlsp; 1378 *ctrlsp = NULL; 1379 1380 REQUIRE(controls->shuttingdown); 1381 REQUIRE(ISC_LIST_EMPTY(controls->listeners)); 1382 1383 LOCK(&controls->symtab_lock); 1384 isccc_symtab_destroy(&controls->symtab); 1385 UNLOCK(&controls->symtab_lock); 1386 isc_mutex_destroy(&controls->symtab_lock); 1387 isc_mem_put(controls->server->mctx, controls, sizeof(*controls)); 1388 } 1389