1135446Strhodes/* 2296611Sdelphij * Copyright (C) 2004-2008, 2011-2014, 2016 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 2001-2003 Internet Software Consortium. 4135446Strhodes * 5182645Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18254897Serwin/* $Id: controlconf.c,v 1.63 2011/12/22 08:07:48 marka Exp $ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes#include <config.h> 23135446Strhodes 24135446Strhodes#include <isc/base64.h> 25135446Strhodes#include <isc/buffer.h> 26135446Strhodes#include <isc/event.h> 27135446Strhodes#include <isc/mem.h> 28135446Strhodes#include <isc/net.h> 29135446Strhodes#include <isc/netaddr.h> 30135446Strhodes#include <isc/random.h> 31135446Strhodes#include <isc/result.h> 32135446Strhodes#include <isc/stdtime.h> 33135446Strhodes#include <isc/string.h> 34135446Strhodes#include <isc/timer.h> 35135446Strhodes#include <isc/util.h> 36135446Strhodes 37135446Strhodes#include <isccfg/namedconf.h> 38135446Strhodes 39135446Strhodes#include <bind9/check.h> 40135446Strhodes 41135446Strhodes#include <isccc/alist.h> 42135446Strhodes#include <isccc/cc.h> 43135446Strhodes#include <isccc/ccmsg.h> 44135446Strhodes#include <isccc/events.h> 45135446Strhodes#include <isccc/result.h> 46135446Strhodes#include <isccc/sexpr.h> 47135446Strhodes#include <isccc/symtab.h> 48135446Strhodes#include <isccc/util.h> 49135446Strhodes 50135446Strhodes#include <dns/result.h> 51135446Strhodes 52135446Strhodes#include <named/config.h> 53135446Strhodes#include <named/control.h> 54135446Strhodes#include <named/log.h> 55135446Strhodes#include <named/server.h> 56135446Strhodes 57135446Strhodes/* 58135446Strhodes * Note: Listeners and connections are not locked. All event handlers are 59135446Strhodes * executed by the server task, and all callers of exported routines must 60135446Strhodes * be running under the server task. 61135446Strhodes */ 62135446Strhodes 63135446Strhodestypedef struct controlkey controlkey_t; 64135446Strhodestypedef ISC_LIST(controlkey_t) controlkeylist_t; 65135446Strhodes 66135446Strhodestypedef struct controlconnection controlconnection_t; 67135446Strhodestypedef ISC_LIST(controlconnection_t) controlconnectionlist_t; 68135446Strhodes 69135446Strhodestypedef struct controllistener controllistener_t; 70135446Strhodestypedef ISC_LIST(controllistener_t) controllistenerlist_t; 71135446Strhodes 72135446Strhodesstruct controlkey { 73135446Strhodes char * keyname; 74135446Strhodes isc_region_t secret; 75135446Strhodes ISC_LINK(controlkey_t) link; 76135446Strhodes}; 77135446Strhodes 78135446Strhodesstruct controlconnection { 79135446Strhodes isc_socket_t * sock; 80135446Strhodes isccc_ccmsg_t ccmsg; 81135446Strhodes isc_boolean_t ccmsg_valid; 82135446Strhodes isc_boolean_t sending; 83135446Strhodes isc_timer_t * timer; 84135446Strhodes unsigned char buffer[2048]; 85135446Strhodes controllistener_t * listener; 86135446Strhodes isc_uint32_t nonce; 87135446Strhodes ISC_LINK(controlconnection_t) link; 88135446Strhodes}; 89135446Strhodes 90135446Strhodesstruct controllistener { 91135446Strhodes ns_controls_t * controls; 92135446Strhodes isc_mem_t * mctx; 93135446Strhodes isc_task_t * task; 94135446Strhodes isc_sockaddr_t address; 95135446Strhodes isc_socket_t * sock; 96135446Strhodes dns_acl_t * acl; 97135446Strhodes isc_boolean_t listening; 98135446Strhodes isc_boolean_t exiting; 99135446Strhodes controlkeylist_t keys; 100135446Strhodes controlconnectionlist_t connections; 101170222Sdougb isc_sockettype_t type; 102170222Sdougb isc_uint32_t perm; 103170222Sdougb isc_uint32_t owner; 104170222Sdougb isc_uint32_t group; 105135446Strhodes ISC_LINK(controllistener_t) link; 106135446Strhodes}; 107135446Strhodes 108135446Strhodesstruct ns_controls { 109135446Strhodes ns_server_t *server; 110135446Strhodes controllistenerlist_t listeners; 111135446Strhodes isc_boolean_t shuttingdown; 112135446Strhodes isccc_symtab_t *symtab; 113135446Strhodes}; 114135446Strhodes 115135446Strhodesstatic void control_newconn(isc_task_t *task, isc_event_t *event); 116135446Strhodesstatic void control_recvmessage(isc_task_t *task, isc_event_t *event); 117135446Strhodes 118135446Strhodes#define CLOCKSKEW 300 119135446Strhodes 120135446Strhodesstatic void 121135446Strhodesfree_controlkey(controlkey_t *key, isc_mem_t *mctx) { 122135446Strhodes if (key->keyname != NULL) 123135446Strhodes isc_mem_free(mctx, key->keyname); 124135446Strhodes if (key->secret.base != NULL) 125135446Strhodes isc_mem_put(mctx, key->secret.base, key->secret.length); 126135446Strhodes isc_mem_put(mctx, key, sizeof(*key)); 127135446Strhodes} 128135446Strhodes 129135446Strhodesstatic void 130135446Strhodesfree_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) { 131135446Strhodes while (!ISC_LIST_EMPTY(*keylist)) { 132135446Strhodes controlkey_t *key = ISC_LIST_HEAD(*keylist); 133135446Strhodes ISC_LIST_UNLINK(*keylist, key, link); 134135446Strhodes free_controlkey(key, mctx); 135135446Strhodes } 136135446Strhodes} 137135446Strhodes 138135446Strhodesstatic void 139135446Strhodesfree_listener(controllistener_t *listener) { 140135446Strhodes INSIST(listener->exiting); 141135446Strhodes INSIST(!listener->listening); 142135446Strhodes INSIST(ISC_LIST_EMPTY(listener->connections)); 143135446Strhodes 144135446Strhodes if (listener->sock != NULL) 145135446Strhodes isc_socket_detach(&listener->sock); 146135446Strhodes 147135446Strhodes free_controlkeylist(&listener->keys, listener->mctx); 148135446Strhodes 149135446Strhodes if (listener->acl != NULL) 150135446Strhodes dns_acl_detach(&listener->acl); 151135446Strhodes 152254402Serwin isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener)); 153135446Strhodes} 154135446Strhodes 155135446Strhodesstatic void 156135446Strhodesmaybe_free_listener(controllistener_t *listener) { 157135446Strhodes if (listener->exiting && 158135446Strhodes !listener->listening && 159135446Strhodes ISC_LIST_EMPTY(listener->connections)) 160135446Strhodes free_listener(listener); 161135446Strhodes} 162135446Strhodes 163135446Strhodesstatic void 164135446Strhodesmaybe_free_connection(controlconnection_t *conn) { 165135446Strhodes controllistener_t *listener = conn->listener; 166135446Strhodes 167135446Strhodes if (conn->timer != NULL) 168135446Strhodes isc_timer_detach(&conn->timer); 169135446Strhodes 170135446Strhodes if (conn->ccmsg_valid) { 171135446Strhodes isccc_ccmsg_cancelread(&conn->ccmsg); 172135446Strhodes return; 173135446Strhodes } 174135446Strhodes 175135446Strhodes if (conn->sending) { 176135446Strhodes isc_socket_cancel(conn->sock, listener->task, 177135446Strhodes ISC_SOCKCANCEL_SEND); 178135446Strhodes return; 179135446Strhodes } 180135446Strhodes 181135446Strhodes ISC_LIST_UNLINK(listener->connections, conn, link); 182135446Strhodes isc_mem_put(listener->mctx, conn, sizeof(*conn)); 183135446Strhodes} 184135446Strhodes 185135446Strhodesstatic void 186135446Strhodesshutdown_listener(controllistener_t *listener) { 187135446Strhodes controlconnection_t *conn; 188135446Strhodes controlconnection_t *next; 189135446Strhodes 190135446Strhodes if (!listener->exiting) { 191135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 192135446Strhodes 193135446Strhodes ISC_LIST_UNLINK(listener->controls->listeners, listener, link); 194135446Strhodes 195135446Strhodes isc_sockaddr_format(&listener->address, socktext, 196135446Strhodes sizeof(socktext)); 197135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 198135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 199135446Strhodes "stopping command channel on %s", socktext); 200170222Sdougb if (listener->type == isc_sockettype_unix) 201170222Sdougb isc_socket_cleanunix(&listener->address, ISC_TRUE); 202135446Strhodes listener->exiting = ISC_TRUE; 203135446Strhodes } 204135446Strhodes 205135446Strhodes for (conn = ISC_LIST_HEAD(listener->connections); 206135446Strhodes conn != NULL; 207135446Strhodes conn = next) 208135446Strhodes { 209135446Strhodes next = ISC_LIST_NEXT(conn, link); 210135446Strhodes maybe_free_connection(conn); 211135446Strhodes } 212135446Strhodes 213135446Strhodes if (listener->listening) 214135446Strhodes isc_socket_cancel(listener->sock, listener->task, 215135446Strhodes ISC_SOCKCANCEL_ACCEPT); 216135446Strhodes 217135446Strhodes maybe_free_listener(listener); 218135446Strhodes} 219135446Strhodes 220135446Strhodesstatic isc_boolean_t 221135446Strhodesaddress_ok(isc_sockaddr_t *sockaddr, dns_acl_t *acl) { 222135446Strhodes isc_netaddr_t netaddr; 223135446Strhodes isc_result_t result; 224135446Strhodes int match; 225135446Strhodes 226135446Strhodes isc_netaddr_fromsockaddr(&netaddr, sockaddr); 227135446Strhodes 228135446Strhodes result = dns_acl_match(&netaddr, NULL, acl, 229135446Strhodes &ns_g_server->aclenv, &match, NULL); 230135446Strhodes 231135446Strhodes if (result != ISC_R_SUCCESS || match <= 0) 232135446Strhodes return (ISC_FALSE); 233135446Strhodes else 234135446Strhodes return (ISC_TRUE); 235135446Strhodes} 236135446Strhodes 237135446Strhodesstatic isc_result_t 238135446Strhodescontrol_accept(controllistener_t *listener) { 239135446Strhodes isc_result_t result; 240135446Strhodes result = isc_socket_accept(listener->sock, 241135446Strhodes listener->task, 242135446Strhodes control_newconn, listener); 243135446Strhodes if (result != ISC_R_SUCCESS) 244135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 245135446Strhodes "isc_socket_accept() failed: %s", 246135446Strhodes isc_result_totext(result)); 247135446Strhodes else 248135446Strhodes listener->listening = ISC_TRUE; 249135446Strhodes return (result); 250135446Strhodes} 251135446Strhodes 252135446Strhodesstatic isc_result_t 253135446Strhodescontrol_listen(controllistener_t *listener) { 254135446Strhodes isc_result_t result; 255135446Strhodes 256135446Strhodes result = isc_socket_listen(listener->sock, 0); 257135446Strhodes if (result != ISC_R_SUCCESS) 258135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, 259135446Strhodes "isc_socket_listen() failed: %s", 260135446Strhodes isc_result_totext(result)); 261135446Strhodes return (result); 262135446Strhodes} 263135446Strhodes 264135446Strhodesstatic void 265135446Strhodescontrol_next(controllistener_t *listener) { 266135446Strhodes (void)control_accept(listener); 267135446Strhodes} 268135446Strhodes 269135446Strhodesstatic void 270135446Strhodescontrol_senddone(isc_task_t *task, isc_event_t *event) { 271135446Strhodes isc_socketevent_t *sevent = (isc_socketevent_t *) event; 272135446Strhodes controlconnection_t *conn = event->ev_arg; 273135446Strhodes controllistener_t *listener = conn->listener; 274135446Strhodes isc_socket_t *sock = (isc_socket_t *)sevent->ev_sender; 275135446Strhodes isc_result_t result; 276135446Strhodes 277135446Strhodes REQUIRE(conn->sending); 278135446Strhodes 279135446Strhodes UNUSED(task); 280135446Strhodes 281135446Strhodes conn->sending = ISC_FALSE; 282135446Strhodes 283135446Strhodes if (sevent->result != ISC_R_SUCCESS && 284135446Strhodes sevent->result != ISC_R_CANCELED) 285135446Strhodes { 286135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 287135446Strhodes isc_sockaddr_t peeraddr; 288135446Strhodes 289135446Strhodes (void)isc_socket_getpeername(sock, &peeraddr); 290135446Strhodes isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 291135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 292135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 293135446Strhodes "error sending command response to %s: %s", 294135446Strhodes socktext, isc_result_totext(sevent->result)); 295135446Strhodes } 296135446Strhodes isc_event_free(&event); 297135446Strhodes 298135446Strhodes result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task, 299135446Strhodes control_recvmessage, conn); 300135446Strhodes if (result != ISC_R_SUCCESS) { 301135446Strhodes isc_socket_detach(&conn->sock); 302135446Strhodes maybe_free_connection(conn); 303135446Strhodes maybe_free_listener(listener); 304135446Strhodes } 305135446Strhodes} 306135446Strhodes 307135446Strhodesstatic inline void 308135446Strhodeslog_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) { 309135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 310135446Strhodes isc_sockaddr_t peeraddr; 311135446Strhodes 312135446Strhodes (void)isc_socket_getpeername(ccmsg->sock, &peeraddr); 313135446Strhodes isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 314135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 315135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_ERROR, 316135446Strhodes "invalid command from %s: %s", 317135446Strhodes socktext, isc_result_totext(result)); 318135446Strhodes} 319135446Strhodes 320135446Strhodesstatic void 321135446Strhodescontrol_recvmessage(isc_task_t *task, isc_event_t *event) { 322135446Strhodes controlconnection_t *conn; 323135446Strhodes controllistener_t *listener; 324135446Strhodes controlkey_t *key; 325135446Strhodes isccc_sexpr_t *request = NULL; 326135446Strhodes isccc_sexpr_t *response = NULL; 327135446Strhodes isccc_region_t ccregion; 328135446Strhodes isccc_region_t secret; 329135446Strhodes isc_stdtime_t now; 330135446Strhodes isc_buffer_t b; 331135446Strhodes isc_region_t r; 332135446Strhodes isc_uint32_t len; 333135446Strhodes isc_buffer_t text; 334135446Strhodes char textarray[1024]; 335135446Strhodes isc_result_t result; 336135446Strhodes isc_result_t eresult; 337135446Strhodes isccc_sexpr_t *_ctrl; 338135446Strhodes isccc_time_t sent; 339135446Strhodes isccc_time_t exp; 340135446Strhodes isc_uint32_t nonce; 341135446Strhodes 342135446Strhodes REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG); 343135446Strhodes 344135446Strhodes conn = event->ev_arg; 345135446Strhodes listener = conn->listener; 346135446Strhodes secret.rstart = NULL; 347135446Strhodes 348186462Sdougb /* Is the server shutting down? */ 349186462Sdougb if (listener->controls->shuttingdown) 350186462Sdougb goto cleanup; 351135446Strhodes 352135446Strhodes if (conn->ccmsg.result != ISC_R_SUCCESS) { 353135446Strhodes if (conn->ccmsg.result != ISC_R_CANCELED && 354135446Strhodes conn->ccmsg.result != ISC_R_EOF) 355135446Strhodes log_invalid(&conn->ccmsg, conn->ccmsg.result); 356135446Strhodes goto cleanup; 357135446Strhodes } 358135446Strhodes 359135446Strhodes request = NULL; 360135446Strhodes 361135446Strhodes for (key = ISC_LIST_HEAD(listener->keys); 362135446Strhodes key != NULL; 363135446Strhodes key = ISC_LIST_NEXT(key, link)) 364135446Strhodes { 365135446Strhodes ccregion.rstart = isc_buffer_base(&conn->ccmsg.buffer); 366135446Strhodes ccregion.rend = isc_buffer_used(&conn->ccmsg.buffer); 367135446Strhodes secret.rstart = isc_mem_get(listener->mctx, key->secret.length); 368135446Strhodes if (secret.rstart == NULL) 369135446Strhodes goto cleanup; 370262706Serwin memmove(secret.rstart, key->secret.base, key->secret.length); 371135446Strhodes secret.rend = secret.rstart + key->secret.length; 372135446Strhodes result = isccc_cc_fromwire(&ccregion, &request, &secret); 373135446Strhodes if (result == ISC_R_SUCCESS) 374135446Strhodes break; 375186462Sdougb isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret)); 376245163Serwin if (result != ISCCC_R_BADAUTH) { 377245163Serwin log_invalid(&conn->ccmsg, result); 378245163Serwin goto cleanup; 379245163Serwin } 380135446Strhodes } 381135446Strhodes 382135446Strhodes if (key == NULL) { 383135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); 384135446Strhodes goto cleanup; 385135446Strhodes } 386135446Strhodes 387135446Strhodes /* We shouldn't be getting a reply. */ 388135446Strhodes if (isccc_cc_isreply(request)) { 389135446Strhodes log_invalid(&conn->ccmsg, ISC_R_FAILURE); 390186462Sdougb goto cleanup_request; 391135446Strhodes } 392135446Strhodes 393135446Strhodes isc_stdtime_get(&now); 394135446Strhodes 395135446Strhodes /* 396135446Strhodes * Limit exposure to replay attacks. 397135446Strhodes */ 398135446Strhodes _ctrl = isccc_alist_lookup(request, "_ctrl"); 399296611Sdelphij if (!isccc_alist_alistp(_ctrl)) { 400135446Strhodes log_invalid(&conn->ccmsg, ISC_R_FAILURE); 401186462Sdougb goto cleanup_request; 402135446Strhodes } 403135446Strhodes 404135446Strhodes if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) { 405135446Strhodes if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) { 406135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW); 407186462Sdougb goto cleanup_request; 408135446Strhodes } 409135446Strhodes } else { 410135446Strhodes log_invalid(&conn->ccmsg, ISC_R_FAILURE); 411186462Sdougb goto cleanup_request; 412135446Strhodes } 413135446Strhodes 414135446Strhodes /* 415135446Strhodes * Expire messages that are too old. 416135446Strhodes */ 417135446Strhodes if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS && 418135446Strhodes now > exp) { 419135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED); 420186462Sdougb goto cleanup_request; 421135446Strhodes } 422135446Strhodes 423135446Strhodes /* 424135446Strhodes * Duplicate suppression (required for UDP). 425135446Strhodes */ 426135446Strhodes isccc_cc_cleansymtab(listener->controls->symtab, now); 427135446Strhodes result = isccc_cc_checkdup(listener->controls->symtab, request, now); 428135446Strhodes if (result != ISC_R_SUCCESS) { 429135446Strhodes if (result == ISC_R_EXISTS) 430186462Sdougb result = ISCCC_R_DUPLICATE; 431135446Strhodes log_invalid(&conn->ccmsg, result); 432186462Sdougb goto cleanup_request; 433135446Strhodes } 434135446Strhodes 435135446Strhodes if (conn->nonce != 0 && 436135446Strhodes (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS || 437135446Strhodes conn->nonce != nonce)) { 438135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); 439186462Sdougb goto cleanup_request; 440135446Strhodes } 441135446Strhodes 442135446Strhodes /* 443135446Strhodes * Establish nonce. 444135446Strhodes */ 445135446Strhodes while (conn->nonce == 0) 446135446Strhodes isc_random_get(&conn->nonce); 447135446Strhodes 448135446Strhodes isc_buffer_init(&text, textarray, sizeof(textarray)); 449135446Strhodes eresult = ns_control_docommand(request, &text); 450135446Strhodes 451135446Strhodes result = isccc_cc_createresponse(request, now, now + 60, &response); 452135446Strhodes if (result != ISC_R_SUCCESS) 453186462Sdougb goto cleanup_request; 454135446Strhodes if (eresult != ISC_R_SUCCESS) { 455135446Strhodes isccc_sexpr_t *data; 456135446Strhodes 457135446Strhodes data = isccc_alist_lookup(response, "_data"); 458135446Strhodes if (data != NULL) { 459135446Strhodes const char *estr = isc_result_totext(eresult); 460135446Strhodes if (isccc_cc_definestring(data, "err", estr) == NULL) 461186462Sdougb goto cleanup_response; 462135446Strhodes } 463135446Strhodes } 464135446Strhodes 465135446Strhodes if (isc_buffer_usedlength(&text) > 0) { 466135446Strhodes isccc_sexpr_t *data; 467135446Strhodes 468135446Strhodes data = isccc_alist_lookup(response, "_data"); 469135446Strhodes if (data != NULL) { 470135446Strhodes char *str = (char *)isc_buffer_base(&text); 471135446Strhodes if (isccc_cc_definestring(data, "text", str) == NULL) 472186462Sdougb goto cleanup_response; 473135446Strhodes } 474135446Strhodes } 475135446Strhodes 476135446Strhodes _ctrl = isccc_alist_lookup(response, "_ctrl"); 477135446Strhodes if (_ctrl == NULL || 478135446Strhodes isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL) 479186462Sdougb goto cleanup_response; 480135446Strhodes 481135446Strhodes ccregion.rstart = conn->buffer + 4; 482135446Strhodes ccregion.rend = conn->buffer + sizeof(conn->buffer); 483135446Strhodes result = isccc_cc_towire(response, &ccregion, &secret); 484135446Strhodes if (result != ISC_R_SUCCESS) 485186462Sdougb goto cleanup_response; 486135446Strhodes isc_buffer_init(&b, conn->buffer, 4); 487135446Strhodes len = sizeof(conn->buffer) - REGION_SIZE(ccregion); 488135446Strhodes isc_buffer_putuint32(&b, len - 4); 489135446Strhodes r.base = conn->buffer; 490135446Strhodes r.length = len; 491135446Strhodes 492135446Strhodes result = isc_socket_send(conn->sock, &r, task, control_senddone, conn); 493135446Strhodes if (result != ISC_R_SUCCESS) 494186462Sdougb goto cleanup_response; 495135446Strhodes conn->sending = ISC_TRUE; 496135446Strhodes 497186462Sdougb isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret)); 498186462Sdougb isccc_sexpr_free(&request); 499186462Sdougb isccc_sexpr_free(&response); 500135446Strhodes return; 501135446Strhodes 502186462Sdougb cleanup_response: 503186462Sdougb isccc_sexpr_free(&response); 504186462Sdougb 505186462Sdougb cleanup_request: 506186462Sdougb isccc_sexpr_free(&request); 507186462Sdougb isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret)); 508186462Sdougb 509135446Strhodes cleanup: 510135446Strhodes isc_socket_detach(&conn->sock); 511135446Strhodes isccc_ccmsg_invalidate(&conn->ccmsg); 512135446Strhodes conn->ccmsg_valid = ISC_FALSE; 513135446Strhodes maybe_free_connection(conn); 514135446Strhodes maybe_free_listener(listener); 515135446Strhodes} 516135446Strhodes 517135446Strhodesstatic void 518135446Strhodescontrol_timeout(isc_task_t *task, isc_event_t *event) { 519135446Strhodes controlconnection_t *conn = event->ev_arg; 520135446Strhodes 521135446Strhodes UNUSED(task); 522135446Strhodes 523135446Strhodes isc_timer_detach(&conn->timer); 524135446Strhodes maybe_free_connection(conn); 525135446Strhodes 526135446Strhodes isc_event_free(&event); 527135446Strhodes} 528135446Strhodes 529135446Strhodesstatic isc_result_t 530135446Strhodesnewconnection(controllistener_t *listener, isc_socket_t *sock) { 531135446Strhodes controlconnection_t *conn; 532135446Strhodes isc_interval_t interval; 533135446Strhodes isc_result_t result; 534135446Strhodes 535135446Strhodes conn = isc_mem_get(listener->mctx, sizeof(*conn)); 536135446Strhodes if (conn == NULL) 537135446Strhodes return (ISC_R_NOMEMORY); 538186462Sdougb 539135446Strhodes conn->sock = sock; 540135446Strhodes isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg); 541135446Strhodes conn->ccmsg_valid = ISC_TRUE; 542135446Strhodes conn->sending = ISC_FALSE; 543135446Strhodes conn->timer = NULL; 544135446Strhodes isc_interval_set(&interval, 60, 0); 545135446Strhodes result = isc_timer_create(ns_g_timermgr, isc_timertype_once, 546135446Strhodes NULL, &interval, listener->task, 547135446Strhodes control_timeout, conn, &conn->timer); 548135446Strhodes if (result != ISC_R_SUCCESS) 549135446Strhodes goto cleanup; 550135446Strhodes 551135446Strhodes conn->listener = listener; 552135446Strhodes conn->nonce = 0; 553135446Strhodes ISC_LINK_INIT(conn, link); 554135446Strhodes 555135446Strhodes result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task, 556135446Strhodes control_recvmessage, conn); 557135446Strhodes if (result != ISC_R_SUCCESS) 558135446Strhodes goto cleanup; 559135446Strhodes isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048); 560135446Strhodes 561135446Strhodes ISC_LIST_APPEND(listener->connections, conn, link); 562135446Strhodes return (ISC_R_SUCCESS); 563135446Strhodes 564135446Strhodes cleanup: 565135446Strhodes isccc_ccmsg_invalidate(&conn->ccmsg); 566135446Strhodes if (conn->timer != NULL) 567135446Strhodes isc_timer_detach(&conn->timer); 568135446Strhodes isc_mem_put(listener->mctx, conn, sizeof(*conn)); 569135446Strhodes return (result); 570135446Strhodes} 571135446Strhodes 572135446Strhodesstatic void 573135446Strhodescontrol_newconn(isc_task_t *task, isc_event_t *event) { 574135446Strhodes isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event; 575135446Strhodes controllistener_t *listener = event->ev_arg; 576135446Strhodes isc_socket_t *sock; 577135446Strhodes isc_sockaddr_t peeraddr; 578135446Strhodes isc_result_t result; 579135446Strhodes 580135446Strhodes UNUSED(task); 581135446Strhodes 582135446Strhodes listener->listening = ISC_FALSE; 583135446Strhodes 584135446Strhodes if (nevent->result != ISC_R_SUCCESS) { 585135446Strhodes if (nevent->result == ISC_R_CANCELED) { 586135446Strhodes shutdown_listener(listener); 587135446Strhodes goto cleanup; 588135446Strhodes } 589135446Strhodes goto restart; 590135446Strhodes } 591135446Strhodes 592135446Strhodes sock = nevent->newsocket; 593193149Sdougb isc_socket_setname(sock, "control", NULL); 594135446Strhodes (void)isc_socket_getpeername(sock, &peeraddr); 595170222Sdougb if (listener->type == isc_sockettype_tcp && 596170222Sdougb !address_ok(&peeraddr, listener->acl)) { 597135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 598135446Strhodes isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 599135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 600135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 601135446Strhodes "rejected command channel message from %s", 602135446Strhodes socktext); 603135446Strhodes isc_socket_detach(&sock); 604135446Strhodes goto restart; 605135446Strhodes } 606135446Strhodes 607135446Strhodes result = newconnection(listener, sock); 608135446Strhodes if (result != ISC_R_SUCCESS) { 609135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 610135446Strhodes isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 611135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 612135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 613135446Strhodes "dropped command channel from %s: %s", 614135446Strhodes socktext, isc_result_totext(result)); 615135446Strhodes isc_socket_detach(&sock); 616135446Strhodes goto restart; 617135446Strhodes } 618135446Strhodes 619135446Strhodes restart: 620135446Strhodes control_next(listener); 621135446Strhodes cleanup: 622135446Strhodes isc_event_free(&event); 623135446Strhodes} 624135446Strhodes 625135446Strhodesstatic void 626135446Strhodescontrols_shutdown(ns_controls_t *controls) { 627135446Strhodes controllistener_t *listener; 628135446Strhodes controllistener_t *next; 629135446Strhodes 630135446Strhodes for (listener = ISC_LIST_HEAD(controls->listeners); 631135446Strhodes listener != NULL; 632135446Strhodes listener = next) 633135446Strhodes { 634135446Strhodes /* 635135446Strhodes * This is asynchronous. As listeners shut down, they will 636135446Strhodes * call their callbacks. 637135446Strhodes */ 638135446Strhodes next = ISC_LIST_NEXT(listener, link); 639135446Strhodes shutdown_listener(listener); 640135446Strhodes } 641135446Strhodes} 642135446Strhodes 643135446Strhodesvoid 644135446Strhodesns_controls_shutdown(ns_controls_t *controls) { 645135446Strhodes controls_shutdown(controls); 646135446Strhodes controls->shuttingdown = ISC_TRUE; 647135446Strhodes} 648135446Strhodes 649135446Strhodesstatic isc_result_t 650165071Sdougbcfgkeylist_find(const cfg_obj_t *keylist, const char *keyname, 651186462Sdougb const cfg_obj_t **objp) 652165071Sdougb{ 653165071Sdougb const cfg_listelt_t *element; 654135446Strhodes const char *str; 655165071Sdougb const cfg_obj_t *obj; 656135446Strhodes 657135446Strhodes for (element = cfg_list_first(keylist); 658135446Strhodes element != NULL; 659135446Strhodes element = cfg_list_next(element)) 660135446Strhodes { 661135446Strhodes obj = cfg_listelt_value(element); 662135446Strhodes str = cfg_obj_asstring(cfg_map_getname(obj)); 663135446Strhodes if (strcasecmp(str, keyname) == 0) 664135446Strhodes break; 665135446Strhodes } 666135446Strhodes if (element == NULL) 667135446Strhodes return (ISC_R_NOTFOUND); 668135446Strhodes obj = cfg_listelt_value(element); 669135446Strhodes *objp = obj; 670135446Strhodes return (ISC_R_SUCCESS); 671135446Strhodes} 672135446Strhodes 673135446Strhodesstatic isc_result_t 674165071Sdougbcontrolkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx, 675135446Strhodes controlkeylist_t *keyids) 676135446Strhodes{ 677165071Sdougb const cfg_listelt_t *element; 678135446Strhodes char *newstr = NULL; 679135446Strhodes const char *str; 680165071Sdougb const cfg_obj_t *obj; 681170222Sdougb controlkey_t *key; 682135446Strhodes 683135446Strhodes for (element = cfg_list_first(keylist); 684135446Strhodes element != NULL; 685135446Strhodes element = cfg_list_next(element)) 686135446Strhodes { 687135446Strhodes obj = cfg_listelt_value(element); 688135446Strhodes str = cfg_obj_asstring(obj); 689135446Strhodes newstr = isc_mem_strdup(mctx, str); 690135446Strhodes if (newstr == NULL) 691135446Strhodes goto cleanup; 692135446Strhodes key = isc_mem_get(mctx, sizeof(*key)); 693135446Strhodes if (key == NULL) 694135446Strhodes goto cleanup; 695135446Strhodes key->keyname = newstr; 696135446Strhodes key->secret.base = NULL; 697135446Strhodes key->secret.length = 0; 698135446Strhodes ISC_LINK_INIT(key, link); 699135446Strhodes ISC_LIST_APPEND(*keyids, key, link); 700135446Strhodes newstr = NULL; 701135446Strhodes } 702135446Strhodes return (ISC_R_SUCCESS); 703135446Strhodes 704135446Strhodes cleanup: 705135446Strhodes if (newstr != NULL) 706135446Strhodes isc_mem_free(mctx, newstr); 707135446Strhodes free_controlkeylist(keyids, mctx); 708135446Strhodes return (ISC_R_NOMEMORY); 709135446Strhodes} 710135446Strhodes 711135446Strhodesstatic void 712165071Sdougbregister_keys(const cfg_obj_t *control, const cfg_obj_t *keylist, 713135446Strhodes controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext) 714135446Strhodes{ 715135446Strhodes controlkey_t *keyid, *next; 716165071Sdougb const cfg_obj_t *keydef; 717135446Strhodes char secret[1024]; 718135446Strhodes isc_buffer_t b; 719135446Strhodes isc_result_t result; 720135446Strhodes 721135446Strhodes /* 722135446Strhodes * Find the keys corresponding to the keyids used by this listener. 723135446Strhodes */ 724135446Strhodes for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) { 725135446Strhodes next = ISC_LIST_NEXT(keyid, link); 726135446Strhodes 727135446Strhodes result = cfgkeylist_find(keylist, keyid->keyname, &keydef); 728135446Strhodes if (result != ISC_R_SUCCESS) { 729135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 730135446Strhodes "couldn't find key '%s' for use with " 731135446Strhodes "command channel %s", 732135446Strhodes keyid->keyname, socktext); 733135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 734135446Strhodes free_controlkey(keyid, mctx); 735135446Strhodes } else { 736165071Sdougb const cfg_obj_t *algobj = NULL; 737165071Sdougb const cfg_obj_t *secretobj = NULL; 738165071Sdougb const char *algstr = NULL; 739165071Sdougb const char *secretstr = NULL; 740135446Strhodes 741135446Strhodes (void)cfg_map_get(keydef, "algorithm", &algobj); 742135446Strhodes (void)cfg_map_get(keydef, "secret", &secretobj); 743135446Strhodes INSIST(algobj != NULL && secretobj != NULL); 744135446Strhodes 745135446Strhodes algstr = cfg_obj_asstring(algobj); 746135446Strhodes secretstr = cfg_obj_asstring(secretobj); 747135446Strhodes 748170222Sdougb if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != 749135446Strhodes ISC_R_SUCCESS) 750135446Strhodes { 751135446Strhodes cfg_obj_log(control, ns_g_lctx, 752135446Strhodes ISC_LOG_WARNING, 753135446Strhodes "unsupported algorithm '%s' in " 754135446Strhodes "key '%s' for use with command " 755135446Strhodes "channel %s", 756135446Strhodes algstr, keyid->keyname, socktext); 757135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 758135446Strhodes free_controlkey(keyid, mctx); 759135446Strhodes continue; 760135446Strhodes } 761135446Strhodes 762135446Strhodes isc_buffer_init(&b, secret, sizeof(secret)); 763135446Strhodes result = isc_base64_decodestring(secretstr, &b); 764135446Strhodes 765135446Strhodes if (result != ISC_R_SUCCESS) { 766135446Strhodes cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING, 767135446Strhodes "secret for key '%s' on " 768135446Strhodes "command channel %s: %s", 769135446Strhodes keyid->keyname, socktext, 770135446Strhodes isc_result_totext(result)); 771135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 772135446Strhodes free_controlkey(keyid, mctx); 773135446Strhodes continue; 774135446Strhodes } 775135446Strhodes 776135446Strhodes keyid->secret.length = isc_buffer_usedlength(&b); 777135446Strhodes keyid->secret.base = isc_mem_get(mctx, 778135446Strhodes keyid->secret.length); 779135446Strhodes if (keyid->secret.base == NULL) { 780135446Strhodes cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING, 781135446Strhodes "couldn't register key '%s': " 782135446Strhodes "out of memory", keyid->keyname); 783135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 784135446Strhodes free_controlkey(keyid, mctx); 785135446Strhodes break; 786135446Strhodes } 787262706Serwin memmove(keyid->secret.base, isc_buffer_base(&b), 788262706Serwin keyid->secret.length); 789135446Strhodes } 790135446Strhodes } 791135446Strhodes} 792135446Strhodes 793135446Strhodes#define CHECK(x) \ 794135446Strhodes do { \ 795135446Strhodes result = (x); \ 796135446Strhodes if (result != ISC_R_SUCCESS) \ 797135446Strhodes goto cleanup; \ 798135446Strhodes } while (0) 799186462Sdougb 800135446Strhodesstatic isc_result_t 801135446Strhodesget_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) { 802135446Strhodes isc_result_t result; 803135446Strhodes cfg_parser_t *pctx = NULL; 804135446Strhodes cfg_obj_t *config = NULL; 805165071Sdougb const cfg_obj_t *key = NULL; 806165071Sdougb const cfg_obj_t *algobj = NULL; 807165071Sdougb const cfg_obj_t *secretobj = NULL; 808165071Sdougb const char *algstr = NULL; 809165071Sdougb const char *secretstr = NULL; 810135446Strhodes controlkey_t *keyid = NULL; 811135446Strhodes char secret[1024]; 812135446Strhodes isc_buffer_t b; 813135446Strhodes 814135446Strhodes CHECK(cfg_parser_create(mctx, ns_g_lctx, &pctx)); 815135446Strhodes CHECK(cfg_parse_file(pctx, ns_g_keyfile, &cfg_type_rndckey, &config)); 816135446Strhodes CHECK(cfg_map_get(config, "key", &key)); 817135446Strhodes 818135446Strhodes keyid = isc_mem_get(mctx, sizeof(*keyid)); 819186462Sdougb if (keyid == NULL) 820135446Strhodes CHECK(ISC_R_NOMEMORY); 821135446Strhodes keyid->keyname = isc_mem_strdup(mctx, 822135446Strhodes cfg_obj_asstring(cfg_map_getname(key))); 823135446Strhodes keyid->secret.base = NULL; 824135446Strhodes keyid->secret.length = 0; 825135446Strhodes ISC_LINK_INIT(keyid, link); 826186462Sdougb if (keyid->keyname == NULL) 827135446Strhodes CHECK(ISC_R_NOMEMORY); 828135446Strhodes 829135446Strhodes CHECK(bind9_check_key(key, ns_g_lctx)); 830135446Strhodes 831135446Strhodes (void)cfg_map_get(key, "algorithm", &algobj); 832135446Strhodes (void)cfg_map_get(key, "secret", &secretobj); 833135446Strhodes INSIST(algobj != NULL && secretobj != NULL); 834135446Strhodes 835135446Strhodes algstr = cfg_obj_asstring(algobj); 836135446Strhodes secretstr = cfg_obj_asstring(secretobj); 837135446Strhodes 838170222Sdougb if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != ISC_R_SUCCESS) { 839135446Strhodes cfg_obj_log(key, ns_g_lctx, 840135446Strhodes ISC_LOG_WARNING, 841135446Strhodes "unsupported algorithm '%s' in " 842135446Strhodes "key '%s' for use with command " 843135446Strhodes "channel", 844135446Strhodes algstr, keyid->keyname); 845135446Strhodes goto cleanup; 846135446Strhodes } 847135446Strhodes 848135446Strhodes isc_buffer_init(&b, secret, sizeof(secret)); 849135446Strhodes result = isc_base64_decodestring(secretstr, &b); 850135446Strhodes 851135446Strhodes if (result != ISC_R_SUCCESS) { 852135446Strhodes cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, 853135446Strhodes "secret for key '%s' on command channel: %s", 854135446Strhodes keyid->keyname, isc_result_totext(result)); 855225361Sdougb goto cleanup; 856135446Strhodes } 857135446Strhodes 858135446Strhodes keyid->secret.length = isc_buffer_usedlength(&b); 859135446Strhodes keyid->secret.base = isc_mem_get(mctx, 860135446Strhodes keyid->secret.length); 861135446Strhodes if (keyid->secret.base == NULL) { 862135446Strhodes cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, 863135446Strhodes "couldn't register key '%s': " 864135446Strhodes "out of memory", keyid->keyname); 865135446Strhodes CHECK(ISC_R_NOMEMORY); 866135446Strhodes } 867262706Serwin memmove(keyid->secret.base, isc_buffer_base(&b), 868262706Serwin keyid->secret.length); 869135446Strhodes ISC_LIST_APPEND(*keyids, keyid, link); 870135446Strhodes keyid = NULL; 871135446Strhodes result = ISC_R_SUCCESS; 872135446Strhodes 873135446Strhodes cleanup: 874135446Strhodes if (keyid != NULL) 875135446Strhodes free_controlkey(keyid, mctx); 876135446Strhodes if (config != NULL) 877135446Strhodes cfg_obj_destroy(pctx, &config); 878135446Strhodes if (pctx != NULL) 879135446Strhodes cfg_parser_destroy(&pctx); 880135446Strhodes return (result); 881135446Strhodes} 882186462Sdougb 883135446Strhodes/* 884135446Strhodes * Ensures that both '*global_keylistp' and '*control_keylistp' are 885135446Strhodes * valid or both are NULL. 886135446Strhodes */ 887135446Strhodesstatic void 888165071Sdougbget_key_info(const cfg_obj_t *config, const cfg_obj_t *control, 889165071Sdougb const cfg_obj_t **global_keylistp, 890165071Sdougb const cfg_obj_t **control_keylistp) 891135446Strhodes{ 892135446Strhodes isc_result_t result; 893165071Sdougb const cfg_obj_t *control_keylist = NULL; 894165071Sdougb const cfg_obj_t *global_keylist = NULL; 895135446Strhodes 896135446Strhodes REQUIRE(global_keylistp != NULL && *global_keylistp == NULL); 897135446Strhodes REQUIRE(control_keylistp != NULL && *control_keylistp == NULL); 898135446Strhodes 899135446Strhodes control_keylist = cfg_tuple_get(control, "keys"); 900135446Strhodes 901135446Strhodes if (!cfg_obj_isvoid(control_keylist) && 902135446Strhodes cfg_list_first(control_keylist) != NULL) { 903135446Strhodes result = cfg_map_get(config, "key", &global_keylist); 904135446Strhodes 905135446Strhodes if (result == ISC_R_SUCCESS) { 906135446Strhodes *global_keylistp = global_keylist; 907135446Strhodes *control_keylistp = control_keylist; 908135446Strhodes } 909135446Strhodes } 910135446Strhodes} 911135446Strhodes 912135446Strhodesstatic void 913165071Sdougbupdate_listener(ns_controls_t *cp, controllistener_t **listenerp, 914165071Sdougb const cfg_obj_t *control, const cfg_obj_t *config, 915170222Sdougb isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, 916186462Sdougb const char *socktext, isc_sockettype_t type) 917135446Strhodes{ 918135446Strhodes controllistener_t *listener; 919165071Sdougb const cfg_obj_t *allow; 920165071Sdougb const cfg_obj_t *global_keylist = NULL; 921165071Sdougb const cfg_obj_t *control_keylist = NULL; 922135446Strhodes dns_acl_t *new_acl = NULL; 923135446Strhodes controlkeylist_t keys; 924135446Strhodes isc_result_t result = ISC_R_SUCCESS; 925135446Strhodes 926135446Strhodes for (listener = ISC_LIST_HEAD(cp->listeners); 927135446Strhodes listener != NULL; 928135446Strhodes listener = ISC_LIST_NEXT(listener, link)) 929135446Strhodes if (isc_sockaddr_equal(addr, &listener->address)) 930135446Strhodes break; 931135446Strhodes 932135446Strhodes if (listener == NULL) { 933135446Strhodes *listenerp = NULL; 934135446Strhodes return; 935135446Strhodes } 936186462Sdougb 937135446Strhodes /* 938135446Strhodes * There is already a listener for this sockaddr. 939135446Strhodes * Update the access list and key information. 940135446Strhodes * 941135446Strhodes * First try to deal with the key situation. There are a few 942135446Strhodes * possibilities: 943135446Strhodes * (a) It had an explicit keylist and still has an explicit keylist. 944135446Strhodes * (b) It had an automagic key and now has an explicit keylist. 945135446Strhodes * (c) It had an explicit keylist and now needs an automagic key. 946135446Strhodes * (d) It has an automagic key and still needs the automagic key. 947135446Strhodes * 948135446Strhodes * (c) and (d) are the annoying ones. The caller needs to know 949135446Strhodes * that it should use the automagic configuration for key information 950135446Strhodes * in place of the named.conf configuration. 951135446Strhodes * 952135446Strhodes * XXXDCL There is one other hazard that has not been dealt with, 953135446Strhodes * the problem that if a key change is being caused by a control 954135446Strhodes * channel reload, then the response will be with the new key 955135446Strhodes * and not able to be decrypted by the client. 956135446Strhodes */ 957135446Strhodes if (control != NULL) 958135446Strhodes get_key_info(config, control, &global_keylist, 959135446Strhodes &control_keylist); 960135446Strhodes 961135446Strhodes if (control_keylist != NULL) { 962135446Strhodes INSIST(global_keylist != NULL); 963135446Strhodes 964135446Strhodes ISC_LIST_INIT(keys); 965135446Strhodes result = controlkeylist_fromcfg(control_keylist, 966135446Strhodes listener->mctx, &keys); 967135446Strhodes if (result == ISC_R_SUCCESS) { 968135446Strhodes free_controlkeylist(&listener->keys, listener->mctx); 969135446Strhodes listener->keys = keys; 970135446Strhodes register_keys(control, global_keylist, &listener->keys, 971135446Strhodes listener->mctx, socktext); 972135446Strhodes } 973135446Strhodes } else { 974135446Strhodes free_controlkeylist(&listener->keys, listener->mctx); 975135446Strhodes result = get_rndckey(listener->mctx, &listener->keys); 976135446Strhodes } 977135446Strhodes 978165071Sdougb if (result != ISC_R_SUCCESS && global_keylist != NULL) { 979135446Strhodes /* 980135446Strhodes * This message might be a little misleading since the 981135446Strhodes * "new keys" might in fact be identical to the old ones, 982135446Strhodes * but tracking whether they are identical just for the 983135446Strhodes * sake of avoiding this message would be too much trouble. 984135446Strhodes */ 985165071Sdougb if (control != NULL) 986165071Sdougb cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 987165071Sdougb "couldn't install new keys for " 988165071Sdougb "command channel %s: %s", 989165071Sdougb socktext, isc_result_totext(result)); 990165071Sdougb else 991165071Sdougb isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 992165071Sdougb NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 993165071Sdougb "couldn't install new keys for " 994165071Sdougb "command channel %s: %s", 995165071Sdougb socktext, isc_result_totext(result)); 996165071Sdougb } 997135446Strhodes 998135446Strhodes /* 999135446Strhodes * Now, keep the old access list unless a new one can be made. 1000135446Strhodes */ 1001170222Sdougb if (control != NULL && type == isc_sockettype_tcp) { 1002135446Strhodes allow = cfg_tuple_get(control, "allow"); 1003170222Sdougb result = cfg_acl_fromconfig(allow, config, ns_g_lctx, 1004193149Sdougb aclconfctx, listener->mctx, 0, 1005170222Sdougb &new_acl); 1006135446Strhodes } else { 1007135446Strhodes result = dns_acl_any(listener->mctx, &new_acl); 1008135446Strhodes } 1009135446Strhodes 1010135446Strhodes if (result == ISC_R_SUCCESS) { 1011135446Strhodes dns_acl_detach(&listener->acl); 1012135446Strhodes dns_acl_attach(new_acl, &listener->acl); 1013135446Strhodes dns_acl_detach(&new_acl); 1014135446Strhodes /* XXXDCL say the old acl is still used? */ 1015165071Sdougb } else if (control != NULL) 1016135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1017135446Strhodes "couldn't install new acl for " 1018135446Strhodes "command channel %s: %s", 1019135446Strhodes socktext, isc_result_totext(result)); 1020165071Sdougb else 1021165071Sdougb isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1022165071Sdougb NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 1023165071Sdougb "couldn't install new acl for " 1024165071Sdougb "command channel %s: %s", 1025165071Sdougb socktext, isc_result_totext(result)); 1026135446Strhodes 1027170222Sdougb if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) { 1028170222Sdougb isc_uint32_t perm, owner, group; 1029170222Sdougb perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm")); 1030170222Sdougb owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner")); 1031170222Sdougb group = cfg_obj_asuint32(cfg_tuple_get(control, "group")); 1032170222Sdougb result = ISC_R_SUCCESS; 1033170222Sdougb if (listener->perm != perm || listener->owner != owner || 1034170222Sdougb listener->group != group) 1035170222Sdougb result = isc_socket_permunix(&listener->address, perm, 1036170222Sdougb owner, group); 1037170222Sdougb if (result == ISC_R_SUCCESS) { 1038170222Sdougb listener->perm = perm; 1039170222Sdougb listener->owner = owner; 1040170222Sdougb listener->group = group; 1041170222Sdougb } else if (control != NULL) 1042170222Sdougb cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1043170222Sdougb "couldn't update ownership/permission for " 1044170222Sdougb "command channel %s", socktext); 1045170222Sdougb } 1046170222Sdougb 1047135446Strhodes *listenerp = listener; 1048135446Strhodes} 1049135446Strhodes 1050135446Strhodesstatic void 1051135446Strhodesadd_listener(ns_controls_t *cp, controllistener_t **listenerp, 1052165071Sdougb const cfg_obj_t *control, const cfg_obj_t *config, 1053170222Sdougb isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, 1054170222Sdougb const char *socktext, isc_sockettype_t type) 1055135446Strhodes{ 1056135446Strhodes isc_mem_t *mctx = cp->server->mctx; 1057135446Strhodes controllistener_t *listener; 1058165071Sdougb const cfg_obj_t *allow; 1059165071Sdougb const cfg_obj_t *global_keylist = NULL; 1060165071Sdougb const cfg_obj_t *control_keylist = NULL; 1061135446Strhodes dns_acl_t *new_acl = NULL; 1062135446Strhodes isc_result_t result = ISC_R_SUCCESS; 1063135446Strhodes 1064135446Strhodes listener = isc_mem_get(mctx, sizeof(*listener)); 1065135446Strhodes if (listener == NULL) 1066135446Strhodes result = ISC_R_NOMEMORY; 1067135446Strhodes 1068135446Strhodes if (result == ISC_R_SUCCESS) { 1069254402Serwin listener->mctx = NULL; 1070254402Serwin isc_mem_attach(mctx, &listener->mctx); 1071135446Strhodes listener->controls = cp; 1072135446Strhodes listener->task = cp->server->task; 1073135446Strhodes listener->address = *addr; 1074135446Strhodes listener->sock = NULL; 1075135446Strhodes listener->listening = ISC_FALSE; 1076135446Strhodes listener->exiting = ISC_FALSE; 1077135446Strhodes listener->acl = NULL; 1078170222Sdougb listener->type = type; 1079170222Sdougb listener->perm = 0; 1080170222Sdougb listener->owner = 0; 1081170222Sdougb listener->group = 0; 1082135446Strhodes ISC_LINK_INIT(listener, link); 1083135446Strhodes ISC_LIST_INIT(listener->keys); 1084135446Strhodes ISC_LIST_INIT(listener->connections); 1085135446Strhodes 1086135446Strhodes /* 1087135446Strhodes * Make the acl. 1088135446Strhodes */ 1089170222Sdougb if (control != NULL && type == isc_sockettype_tcp) { 1090135446Strhodes allow = cfg_tuple_get(control, "allow"); 1091170222Sdougb result = cfg_acl_fromconfig(allow, config, ns_g_lctx, 1092193149Sdougb aclconfctx, mctx, 0, 1093193149Sdougb &new_acl); 1094135446Strhodes } else { 1095135446Strhodes result = dns_acl_any(mctx, &new_acl); 1096135446Strhodes } 1097135446Strhodes } 1098135446Strhodes 1099135446Strhodes if (result == ISC_R_SUCCESS) { 1100135446Strhodes dns_acl_attach(new_acl, &listener->acl); 1101135446Strhodes dns_acl_detach(&new_acl); 1102135446Strhodes 1103135446Strhodes if (config != NULL) 1104135446Strhodes get_key_info(config, control, &global_keylist, 1105135446Strhodes &control_keylist); 1106135446Strhodes 1107135446Strhodes if (control_keylist != NULL) { 1108135446Strhodes result = controlkeylist_fromcfg(control_keylist, 1109135446Strhodes listener->mctx, 1110135446Strhodes &listener->keys); 1111135446Strhodes if (result == ISC_R_SUCCESS) 1112135446Strhodes register_keys(control, global_keylist, 1113135446Strhodes &listener->keys, 1114135446Strhodes listener->mctx, socktext); 1115135446Strhodes } else 1116135446Strhodes result = get_rndckey(mctx, &listener->keys); 1117135446Strhodes 1118135446Strhodes if (result != ISC_R_SUCCESS && control != NULL) 1119135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1120135446Strhodes "couldn't install keys for " 1121135446Strhodes "command channel %s: %s", 1122135446Strhodes socktext, isc_result_totext(result)); 1123135446Strhodes } 1124135446Strhodes 1125135446Strhodes if (result == ISC_R_SUCCESS) { 1126135446Strhodes int pf = isc_sockaddr_pf(&listener->address); 1127135446Strhodes if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) || 1128170222Sdougb#ifdef ISC_PLATFORM_HAVESYSUNH 1129170222Sdougb (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) || 1130170222Sdougb#endif 1131135446Strhodes (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) 1132135446Strhodes result = ISC_R_FAMILYNOSUPPORT; 1133135446Strhodes } 1134135446Strhodes 1135170222Sdougb if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) 1136170222Sdougb isc_socket_cleanunix(&listener->address, ISC_FALSE); 1137170222Sdougb 1138135446Strhodes if (result == ISC_R_SUCCESS) 1139135446Strhodes result = isc_socket_create(ns_g_socketmgr, 1140135446Strhodes isc_sockaddr_pf(&listener->address), 1141170222Sdougb type, &listener->sock); 1142193149Sdougb if (result == ISC_R_SUCCESS) 1143193149Sdougb isc_socket_setname(listener->sock, "control", NULL); 1144135446Strhodes 1145234010Sdougb#ifndef ISC_ALLOW_MAPPED 1146135446Strhodes if (result == ISC_R_SUCCESS) 1147234010Sdougb isc_socket_ipv6only(listener->sock, ISC_TRUE); 1148234010Sdougb#endif 1149234010Sdougb 1150234010Sdougb if (result == ISC_R_SUCCESS) 1151182645Sdougb result = isc_socket_bind(listener->sock, &listener->address, 1152182645Sdougb ISC_SOCKET_REUSEADDRESS); 1153135446Strhodes 1154170222Sdougb if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) { 1155170222Sdougb listener->perm = cfg_obj_asuint32(cfg_tuple_get(control, 1156170222Sdougb "perm")); 1157170222Sdougb listener->owner = cfg_obj_asuint32(cfg_tuple_get(control, 1158170222Sdougb "owner")); 1159170222Sdougb listener->group = cfg_obj_asuint32(cfg_tuple_get(control, 1160170222Sdougb "group")); 1161170222Sdougb result = isc_socket_permunix(&listener->address, listener->perm, 1162170222Sdougb listener->owner, listener->group); 1163170222Sdougb } 1164135446Strhodes if (result == ISC_R_SUCCESS) 1165135446Strhodes result = control_listen(listener); 1166135446Strhodes 1167135446Strhodes if (result == ISC_R_SUCCESS) 1168135446Strhodes result = control_accept(listener); 1169135446Strhodes 1170135446Strhodes if (result == ISC_R_SUCCESS) { 1171135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1172135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 1173135446Strhodes "command channel listening on %s", socktext); 1174135446Strhodes *listenerp = listener; 1175135446Strhodes 1176135446Strhodes } else { 1177135446Strhodes if (listener != NULL) { 1178135446Strhodes listener->exiting = ISC_TRUE; 1179135446Strhodes free_listener(listener); 1180135446Strhodes } 1181135446Strhodes 1182135446Strhodes if (control != NULL) 1183135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1184135446Strhodes "couldn't add command channel %s: %s", 1185135446Strhodes socktext, isc_result_totext(result)); 1186135446Strhodes else 1187135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1188135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 1189135446Strhodes "couldn't add command channel %s: %s", 1190135446Strhodes socktext, isc_result_totext(result)); 1191135446Strhodes 1192135446Strhodes *listenerp = NULL; 1193135446Strhodes } 1194135446Strhodes 1195135446Strhodes /* XXXDCL return error results? fail hard? */ 1196135446Strhodes} 1197135446Strhodes 1198135446Strhodesisc_result_t 1199165071Sdougbns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config, 1200170222Sdougb cfg_aclconfctx_t *aclconfctx) 1201135446Strhodes{ 1202135446Strhodes controllistener_t *listener; 1203135446Strhodes controllistenerlist_t new_listeners; 1204165071Sdougb const cfg_obj_t *controlslist = NULL; 1205165071Sdougb const cfg_listelt_t *element, *element2; 1206135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 1207135446Strhodes 1208135446Strhodes ISC_LIST_INIT(new_listeners); 1209135446Strhodes 1210135446Strhodes /* 1211135446Strhodes * Get the list of named.conf 'controls' statements. 1212135446Strhodes */ 1213135446Strhodes (void)cfg_map_get(config, "controls", &controlslist); 1214135446Strhodes 1215135446Strhodes /* 1216135446Strhodes * Run through the new control channel list, noting sockets that 1217135446Strhodes * are already being listened on and moving them to the new list. 1218135446Strhodes * 1219135446Strhodes * Identifying duplicate addr/port combinations is left to either 1220135446Strhodes * the underlying config code, or to the bind attempt getting an 1221135446Strhodes * address-in-use error. 1222135446Strhodes */ 1223135446Strhodes if (controlslist != NULL) { 1224135446Strhodes for (element = cfg_list_first(controlslist); 1225135446Strhodes element != NULL; 1226135446Strhodes element = cfg_list_next(element)) { 1227165071Sdougb const cfg_obj_t *controls; 1228165071Sdougb const cfg_obj_t *inetcontrols = NULL; 1229135446Strhodes 1230135446Strhodes controls = cfg_listelt_value(element); 1231135446Strhodes (void)cfg_map_get(controls, "inet", &inetcontrols); 1232135446Strhodes if (inetcontrols == NULL) 1233135446Strhodes continue; 1234135446Strhodes 1235135446Strhodes for (element2 = cfg_list_first(inetcontrols); 1236135446Strhodes element2 != NULL; 1237135446Strhodes element2 = cfg_list_next(element2)) { 1238165071Sdougb const cfg_obj_t *control; 1239165071Sdougb const cfg_obj_t *obj; 1240165071Sdougb isc_sockaddr_t addr; 1241135446Strhodes 1242135446Strhodes /* 1243135446Strhodes * The parser handles BIND 8 configuration file 1244135446Strhodes * syntax, so it allows unix phrases as well 1245135446Strhodes * inet phrases with no keys{} clause. 1246135446Strhodes */ 1247135446Strhodes control = cfg_listelt_value(element2); 1248135446Strhodes 1249135446Strhodes obj = cfg_tuple_get(control, "address"); 1250165071Sdougb addr = *cfg_obj_assockaddr(obj); 1251165071Sdougb if (isc_sockaddr_getport(&addr) == 0) 1252165071Sdougb isc_sockaddr_setport(&addr, 1253135446Strhodes NS_CONTROL_PORT); 1254135446Strhodes 1255165071Sdougb isc_sockaddr_format(&addr, socktext, 1256135446Strhodes sizeof(socktext)); 1257135446Strhodes 1258135446Strhodes isc_log_write(ns_g_lctx, 1259135446Strhodes NS_LOGCATEGORY_GENERAL, 1260135446Strhodes NS_LOGMODULE_CONTROL, 1261135446Strhodes ISC_LOG_DEBUG(9), 1262135446Strhodes "processing control channel %s", 1263135446Strhodes socktext); 1264135446Strhodes 1265135446Strhodes update_listener(cp, &listener, control, config, 1266170222Sdougb &addr, aclconfctx, socktext, 1267170222Sdougb isc_sockettype_tcp); 1268135446Strhodes 1269135446Strhodes if (listener != NULL) 1270135446Strhodes /* 1271135446Strhodes * Remove the listener from the old 1272135446Strhodes * list, so it won't be shut down. 1273135446Strhodes */ 1274135446Strhodes ISC_LIST_UNLINK(cp->listeners, 1275135446Strhodes listener, link); 1276135446Strhodes else 1277135446Strhodes /* 1278135446Strhodes * This is a new listener. 1279135446Strhodes */ 1280135446Strhodes add_listener(cp, &listener, control, 1281165071Sdougb config, &addr, aclconfctx, 1282170222Sdougb socktext, 1283170222Sdougb isc_sockettype_tcp); 1284135446Strhodes 1285135446Strhodes if (listener != NULL) 1286135446Strhodes ISC_LIST_APPEND(new_listeners, 1287135446Strhodes listener, link); 1288135446Strhodes } 1289135446Strhodes } 1290170222Sdougb for (element = cfg_list_first(controlslist); 1291170222Sdougb element != NULL; 1292170222Sdougb element = cfg_list_next(element)) { 1293170222Sdougb const cfg_obj_t *controls; 1294170222Sdougb const cfg_obj_t *unixcontrols = NULL; 1295170222Sdougb 1296170222Sdougb controls = cfg_listelt_value(element); 1297170222Sdougb (void)cfg_map_get(controls, "unix", &unixcontrols); 1298170222Sdougb if (unixcontrols == NULL) 1299170222Sdougb continue; 1300170222Sdougb 1301170222Sdougb for (element2 = cfg_list_first(unixcontrols); 1302170222Sdougb element2 != NULL; 1303170222Sdougb element2 = cfg_list_next(element2)) { 1304170222Sdougb const cfg_obj_t *control; 1305170222Sdougb const cfg_obj_t *path; 1306170222Sdougb isc_sockaddr_t addr; 1307170222Sdougb isc_result_t result; 1308170222Sdougb 1309170222Sdougb /* 1310170222Sdougb * The parser handles BIND 8 configuration file 1311170222Sdougb * syntax, so it allows unix phrases as well 1312170222Sdougb * inet phrases with no keys{} clause. 1313170222Sdougb */ 1314170222Sdougb control = cfg_listelt_value(element2); 1315170222Sdougb 1316170222Sdougb path = cfg_tuple_get(control, "path"); 1317170222Sdougb result = isc_sockaddr_frompath(&addr, 1318170222Sdougb cfg_obj_asstring(path)); 1319170222Sdougb if (result != ISC_R_SUCCESS) { 1320170222Sdougb isc_log_write(ns_g_lctx, 1321170222Sdougb NS_LOGCATEGORY_GENERAL, 1322170222Sdougb NS_LOGMODULE_CONTROL, 1323170222Sdougb ISC_LOG_DEBUG(9), 1324170222Sdougb "control channel '%s': %s", 1325170222Sdougb cfg_obj_asstring(path), 1326170222Sdougb isc_result_totext(result)); 1327170222Sdougb continue; 1328170222Sdougb } 1329170222Sdougb 1330170222Sdougb isc_log_write(ns_g_lctx, 1331170222Sdougb NS_LOGCATEGORY_GENERAL, 1332170222Sdougb NS_LOGMODULE_CONTROL, 1333170222Sdougb ISC_LOG_DEBUG(9), 1334170222Sdougb "processing control channel '%s'", 1335170222Sdougb cfg_obj_asstring(path)); 1336170222Sdougb 1337170222Sdougb update_listener(cp, &listener, control, config, 1338170222Sdougb &addr, aclconfctx, 1339186462Sdougb cfg_obj_asstring(path), 1340170222Sdougb isc_sockettype_unix); 1341170222Sdougb 1342170222Sdougb if (listener != NULL) 1343170222Sdougb /* 1344170222Sdougb * Remove the listener from the old 1345170222Sdougb * list, so it won't be shut down. 1346170222Sdougb */ 1347170222Sdougb ISC_LIST_UNLINK(cp->listeners, 1348170222Sdougb listener, link); 1349170222Sdougb else 1350170222Sdougb /* 1351170222Sdougb * This is a new listener. 1352170222Sdougb */ 1353170222Sdougb add_listener(cp, &listener, control, 1354170222Sdougb config, &addr, aclconfctx, 1355170222Sdougb cfg_obj_asstring(path), 1356170222Sdougb isc_sockettype_unix); 1357170222Sdougb 1358170222Sdougb if (listener != NULL) 1359170222Sdougb ISC_LIST_APPEND(new_listeners, 1360170222Sdougb listener, link); 1361170222Sdougb } 1362170222Sdougb } 1363135446Strhodes } else { 1364135446Strhodes int i; 1365135446Strhodes 1366135446Strhodes for (i = 0; i < 2; i++) { 1367135446Strhodes isc_sockaddr_t addr; 1368135446Strhodes 1369135446Strhodes if (i == 0) { 1370135446Strhodes struct in_addr localhost; 1371135446Strhodes 1372135446Strhodes if (isc_net_probeipv4() != ISC_R_SUCCESS) 1373135446Strhodes continue; 1374135446Strhodes localhost.s_addr = htonl(INADDR_LOOPBACK); 1375135446Strhodes isc_sockaddr_fromin(&addr, &localhost, 0); 1376135446Strhodes } else { 1377135446Strhodes if (isc_net_probeipv6() != ISC_R_SUCCESS) 1378135446Strhodes continue; 1379135446Strhodes isc_sockaddr_fromin6(&addr, 1380135446Strhodes &in6addr_loopback, 0); 1381135446Strhodes } 1382135446Strhodes isc_sockaddr_setport(&addr, NS_CONTROL_PORT); 1383135446Strhodes 1384135446Strhodes isc_sockaddr_format(&addr, socktext, sizeof(socktext)); 1385186462Sdougb 1386135446Strhodes update_listener(cp, &listener, NULL, NULL, 1387170222Sdougb &addr, NULL, socktext, 1388186462Sdougb isc_sockettype_tcp); 1389135446Strhodes 1390135446Strhodes if (listener != NULL) 1391135446Strhodes /* 1392135446Strhodes * Remove the listener from the old 1393135446Strhodes * list, so it won't be shut down. 1394135446Strhodes */ 1395135446Strhodes ISC_LIST_UNLINK(cp->listeners, 1396135446Strhodes listener, link); 1397135446Strhodes else 1398135446Strhodes /* 1399135446Strhodes * This is a new listener. 1400135446Strhodes */ 1401135446Strhodes add_listener(cp, &listener, NULL, NULL, 1402170222Sdougb &addr, NULL, socktext, 1403170222Sdougb isc_sockettype_tcp); 1404135446Strhodes 1405135446Strhodes if (listener != NULL) 1406135446Strhodes ISC_LIST_APPEND(new_listeners, 1407135446Strhodes listener, link); 1408135446Strhodes } 1409135446Strhodes } 1410135446Strhodes 1411135446Strhodes /* 1412135446Strhodes * ns_control_shutdown() will stop whatever is on the global 1413135446Strhodes * listeners list, which currently only has whatever sockaddrs 1414135446Strhodes * were in the previous configuration (if any) that do not 1415135446Strhodes * remain in the current configuration. 1416135446Strhodes */ 1417135446Strhodes controls_shutdown(cp); 1418135446Strhodes 1419135446Strhodes /* 1420135446Strhodes * Put all of the valid listeners on the listeners list. 1421135446Strhodes * Anything already on listeners in the process of shutting 1422135446Strhodes * down will be taken care of by listen_done(). 1423135446Strhodes */ 1424135446Strhodes ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link); 1425135446Strhodes return (ISC_R_SUCCESS); 1426135446Strhodes} 1427135446Strhodes 1428135446Strhodesisc_result_t 1429135446Strhodesns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp) { 1430135446Strhodes isc_mem_t *mctx = server->mctx; 1431135446Strhodes isc_result_t result; 1432135446Strhodes ns_controls_t *controls = isc_mem_get(mctx, sizeof(*controls)); 1433135446Strhodes 1434135446Strhodes if (controls == NULL) 1435135446Strhodes return (ISC_R_NOMEMORY); 1436135446Strhodes controls->server = server; 1437135446Strhodes ISC_LIST_INIT(controls->listeners); 1438135446Strhodes controls->shuttingdown = ISC_FALSE; 1439135446Strhodes controls->symtab = NULL; 1440135446Strhodes result = isccc_cc_createsymtab(&controls->symtab); 1441135446Strhodes if (result != ISC_R_SUCCESS) { 1442135446Strhodes isc_mem_put(server->mctx, controls, sizeof(*controls)); 1443135446Strhodes return (result); 1444135446Strhodes } 1445135446Strhodes *ctrlsp = controls; 1446135446Strhodes return (ISC_R_SUCCESS); 1447135446Strhodes} 1448135446Strhodes 1449135446Strhodesvoid 1450135446Strhodesns_controls_destroy(ns_controls_t **ctrlsp) { 1451135446Strhodes ns_controls_t *controls = *ctrlsp; 1452135446Strhodes 1453135446Strhodes REQUIRE(ISC_LIST_EMPTY(controls->listeners)); 1454135446Strhodes 1455135446Strhodes isccc_symtab_destroy(&controls->symtab); 1456135446Strhodes isc_mem_put(controls->server->mctx, controls, sizeof(*controls)); 1457135446Strhodes *ctrlsp = NULL; 1458135446Strhodes} 1459