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