controlconf.c revision 170222
1135446Strhodes/* 2170222Sdougb * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 2001-2003 Internet Software Consortium. 4135446Strhodes * 5135446Strhodes * Permission to use, copy, modify, and 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 18170222Sdougb/* $Id: controlconf.c,v 1.40.18.10 2006/12/07 04:53:02 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 152135446Strhodes isc_mem_put(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 348135446Strhodes /* Is the server shutting down? */ 349135446Strhodes if (listener->controls->shuttingdown) 350135446Strhodes 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); 367165071Sdougb if (secret.rstart != NULL) 368165071Sdougb isc_mem_put(listener->mctx, secret.rstart, 369165071Sdougb REGION_SIZE(secret)); 370135446Strhodes secret.rstart = isc_mem_get(listener->mctx, key->secret.length); 371135446Strhodes if (secret.rstart == NULL) 372135446Strhodes goto cleanup; 373135446Strhodes memcpy(secret.rstart, key->secret.base, key->secret.length); 374135446Strhodes secret.rend = secret.rstart + key->secret.length; 375135446Strhodes result = isccc_cc_fromwire(&ccregion, &request, &secret); 376135446Strhodes if (result == ISC_R_SUCCESS) 377135446Strhodes break; 378135446Strhodes else if (result == ISCCC_R_BADAUTH) { 379135446Strhodes /* 380135446Strhodes * For some reason, request is non-NULL when 381135446Strhodes * isccc_cc_fromwire returns ISCCC_R_BADAUTH. 382135446Strhodes */ 383135446Strhodes if (request != NULL) 384135446Strhodes isccc_sexpr_free(&request); 385135446Strhodes } else { 386135446Strhodes log_invalid(&conn->ccmsg, result); 387135446Strhodes goto cleanup; 388135446Strhodes } 389135446Strhodes } 390135446Strhodes 391135446Strhodes if (key == NULL) { 392135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); 393135446Strhodes goto cleanup; 394135446Strhodes } 395135446Strhodes 396135446Strhodes /* We shouldn't be getting a reply. */ 397135446Strhodes if (isccc_cc_isreply(request)) { 398135446Strhodes log_invalid(&conn->ccmsg, ISC_R_FAILURE); 399135446Strhodes goto cleanup; 400135446Strhodes } 401135446Strhodes 402135446Strhodes isc_stdtime_get(&now); 403135446Strhodes 404135446Strhodes /* 405135446Strhodes * Limit exposure to replay attacks. 406135446Strhodes */ 407135446Strhodes _ctrl = isccc_alist_lookup(request, "_ctrl"); 408135446Strhodes if (_ctrl == NULL) { 409135446Strhodes log_invalid(&conn->ccmsg, ISC_R_FAILURE); 410135446Strhodes goto cleanup; 411135446Strhodes } 412135446Strhodes 413135446Strhodes if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) { 414135446Strhodes if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) { 415135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW); 416135446Strhodes goto cleanup; 417135446Strhodes } 418135446Strhodes } else { 419135446Strhodes log_invalid(&conn->ccmsg, ISC_R_FAILURE); 420135446Strhodes goto cleanup; 421135446Strhodes } 422135446Strhodes 423135446Strhodes /* 424135446Strhodes * Expire messages that are too old. 425135446Strhodes */ 426135446Strhodes if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS && 427135446Strhodes now > exp) { 428135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED); 429135446Strhodes goto cleanup; 430135446Strhodes } 431135446Strhodes 432135446Strhodes /* 433135446Strhodes * Duplicate suppression (required for UDP). 434135446Strhodes */ 435135446Strhodes isccc_cc_cleansymtab(listener->controls->symtab, now); 436135446Strhodes result = isccc_cc_checkdup(listener->controls->symtab, request, now); 437135446Strhodes if (result != ISC_R_SUCCESS) { 438135446Strhodes if (result == ISC_R_EXISTS) 439135446Strhodes result = ISCCC_R_DUPLICATE; 440135446Strhodes log_invalid(&conn->ccmsg, result); 441135446Strhodes goto cleanup; 442135446Strhodes } 443135446Strhodes 444135446Strhodes if (conn->nonce != 0 && 445135446Strhodes (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS || 446135446Strhodes conn->nonce != nonce)) { 447135446Strhodes log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH); 448135446Strhodes goto cleanup; 449135446Strhodes } 450135446Strhodes 451135446Strhodes /* 452135446Strhodes * Establish nonce. 453135446Strhodes */ 454135446Strhodes while (conn->nonce == 0) 455135446Strhodes isc_random_get(&conn->nonce); 456135446Strhodes 457135446Strhodes isc_buffer_init(&text, textarray, sizeof(textarray)); 458135446Strhodes eresult = ns_control_docommand(request, &text); 459135446Strhodes 460135446Strhodes result = isccc_cc_createresponse(request, now, now + 60, &response); 461135446Strhodes if (result != ISC_R_SUCCESS) 462135446Strhodes goto cleanup; 463135446Strhodes if (eresult != ISC_R_SUCCESS) { 464135446Strhodes isccc_sexpr_t *data; 465135446Strhodes 466135446Strhodes data = isccc_alist_lookup(response, "_data"); 467135446Strhodes if (data != NULL) { 468135446Strhodes const char *estr = isc_result_totext(eresult); 469135446Strhodes if (isccc_cc_definestring(data, "err", estr) == NULL) 470135446Strhodes goto cleanup; 471135446Strhodes } 472135446Strhodes } 473135446Strhodes 474135446Strhodes if (isc_buffer_usedlength(&text) > 0) { 475135446Strhodes isccc_sexpr_t *data; 476135446Strhodes 477135446Strhodes data = isccc_alist_lookup(response, "_data"); 478135446Strhodes if (data != NULL) { 479135446Strhodes char *str = (char *)isc_buffer_base(&text); 480135446Strhodes if (isccc_cc_definestring(data, "text", str) == NULL) 481135446Strhodes goto cleanup; 482135446Strhodes } 483135446Strhodes } 484135446Strhodes 485135446Strhodes _ctrl = isccc_alist_lookup(response, "_ctrl"); 486135446Strhodes if (_ctrl == NULL || 487135446Strhodes isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL) 488135446Strhodes goto cleanup; 489135446Strhodes 490135446Strhodes ccregion.rstart = conn->buffer + 4; 491135446Strhodes ccregion.rend = conn->buffer + sizeof(conn->buffer); 492135446Strhodes result = isccc_cc_towire(response, &ccregion, &secret); 493135446Strhodes if (result != ISC_R_SUCCESS) 494135446Strhodes goto cleanup; 495135446Strhodes isc_buffer_init(&b, conn->buffer, 4); 496135446Strhodes len = sizeof(conn->buffer) - REGION_SIZE(ccregion); 497135446Strhodes isc_buffer_putuint32(&b, len - 4); 498135446Strhodes r.base = conn->buffer; 499135446Strhodes r.length = len; 500135446Strhodes 501135446Strhodes result = isc_socket_send(conn->sock, &r, task, control_senddone, conn); 502135446Strhodes if (result != ISC_R_SUCCESS) 503135446Strhodes goto cleanup; 504135446Strhodes conn->sending = ISC_TRUE; 505135446Strhodes 506135446Strhodes if (secret.rstart != NULL) 507135446Strhodes isc_mem_put(listener->mctx, secret.rstart, 508135446Strhodes REGION_SIZE(secret)); 509135446Strhodes if (request != NULL) 510135446Strhodes isccc_sexpr_free(&request); 511135446Strhodes if (response != NULL) 512135446Strhodes isccc_sexpr_free(&response); 513135446Strhodes return; 514135446Strhodes 515135446Strhodes cleanup: 516135446Strhodes if (secret.rstart != NULL) 517135446Strhodes isc_mem_put(listener->mctx, secret.rstart, 518135446Strhodes REGION_SIZE(secret)); 519135446Strhodes isc_socket_detach(&conn->sock); 520135446Strhodes isccc_ccmsg_invalidate(&conn->ccmsg); 521135446Strhodes conn->ccmsg_valid = ISC_FALSE; 522135446Strhodes maybe_free_connection(conn); 523135446Strhodes maybe_free_listener(listener); 524135446Strhodes if (request != NULL) 525135446Strhodes isccc_sexpr_free(&request); 526135446Strhodes if (response != NULL) 527135446Strhodes isccc_sexpr_free(&response); 528135446Strhodes} 529135446Strhodes 530135446Strhodesstatic void 531135446Strhodescontrol_timeout(isc_task_t *task, isc_event_t *event) { 532135446Strhodes controlconnection_t *conn = event->ev_arg; 533135446Strhodes 534135446Strhodes UNUSED(task); 535135446Strhodes 536135446Strhodes isc_timer_detach(&conn->timer); 537135446Strhodes maybe_free_connection(conn); 538135446Strhodes 539135446Strhodes isc_event_free(&event); 540135446Strhodes} 541135446Strhodes 542135446Strhodesstatic isc_result_t 543135446Strhodesnewconnection(controllistener_t *listener, isc_socket_t *sock) { 544135446Strhodes controlconnection_t *conn; 545135446Strhodes isc_interval_t interval; 546135446Strhodes isc_result_t result; 547135446Strhodes 548135446Strhodes conn = isc_mem_get(listener->mctx, sizeof(*conn)); 549135446Strhodes if (conn == NULL) 550135446Strhodes return (ISC_R_NOMEMORY); 551135446Strhodes 552135446Strhodes conn->sock = sock; 553135446Strhodes isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg); 554135446Strhodes conn->ccmsg_valid = ISC_TRUE; 555135446Strhodes conn->sending = ISC_FALSE; 556135446Strhodes conn->timer = NULL; 557135446Strhodes isc_interval_set(&interval, 60, 0); 558135446Strhodes result = isc_timer_create(ns_g_timermgr, isc_timertype_once, 559135446Strhodes NULL, &interval, listener->task, 560135446Strhodes control_timeout, conn, &conn->timer); 561135446Strhodes if (result != ISC_R_SUCCESS) 562135446Strhodes goto cleanup; 563135446Strhodes 564135446Strhodes conn->listener = listener; 565135446Strhodes conn->nonce = 0; 566135446Strhodes ISC_LINK_INIT(conn, link); 567135446Strhodes 568135446Strhodes result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task, 569135446Strhodes control_recvmessage, conn); 570135446Strhodes if (result != ISC_R_SUCCESS) 571135446Strhodes goto cleanup; 572135446Strhodes isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048); 573135446Strhodes 574135446Strhodes ISC_LIST_APPEND(listener->connections, conn, link); 575135446Strhodes return (ISC_R_SUCCESS); 576135446Strhodes 577135446Strhodes cleanup: 578135446Strhodes isccc_ccmsg_invalidate(&conn->ccmsg); 579135446Strhodes if (conn->timer != NULL) 580135446Strhodes isc_timer_detach(&conn->timer); 581135446Strhodes isc_mem_put(listener->mctx, conn, sizeof(*conn)); 582135446Strhodes return (result); 583135446Strhodes} 584135446Strhodes 585135446Strhodesstatic void 586135446Strhodescontrol_newconn(isc_task_t *task, isc_event_t *event) { 587135446Strhodes isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event; 588135446Strhodes controllistener_t *listener = event->ev_arg; 589135446Strhodes isc_socket_t *sock; 590135446Strhodes isc_sockaddr_t peeraddr; 591135446Strhodes isc_result_t result; 592135446Strhodes 593135446Strhodes UNUSED(task); 594135446Strhodes 595135446Strhodes listener->listening = ISC_FALSE; 596135446Strhodes 597135446Strhodes if (nevent->result != ISC_R_SUCCESS) { 598135446Strhodes if (nevent->result == ISC_R_CANCELED) { 599135446Strhodes shutdown_listener(listener); 600135446Strhodes goto cleanup; 601135446Strhodes } 602135446Strhodes goto restart; 603135446Strhodes } 604135446Strhodes 605135446Strhodes sock = nevent->newsocket; 606135446Strhodes (void)isc_socket_getpeername(sock, &peeraddr); 607170222Sdougb if (listener->type == isc_sockettype_tcp && 608170222Sdougb !address_ok(&peeraddr, listener->acl)) { 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 "rejected command channel message from %s", 614135446Strhodes socktext); 615135446Strhodes isc_socket_detach(&sock); 616135446Strhodes goto restart; 617135446Strhodes } 618135446Strhodes 619135446Strhodes result = newconnection(listener, sock); 620135446Strhodes if (result != ISC_R_SUCCESS) { 621135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 622135446Strhodes isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext)); 623135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 624135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 625135446Strhodes "dropped command channel from %s: %s", 626135446Strhodes socktext, isc_result_totext(result)); 627135446Strhodes isc_socket_detach(&sock); 628135446Strhodes goto restart; 629135446Strhodes } 630135446Strhodes 631135446Strhodes restart: 632135446Strhodes control_next(listener); 633135446Strhodes cleanup: 634135446Strhodes isc_event_free(&event); 635135446Strhodes} 636135446Strhodes 637135446Strhodesstatic void 638135446Strhodescontrols_shutdown(ns_controls_t *controls) { 639135446Strhodes controllistener_t *listener; 640135446Strhodes controllistener_t *next; 641135446Strhodes 642135446Strhodes for (listener = ISC_LIST_HEAD(controls->listeners); 643135446Strhodes listener != NULL; 644135446Strhodes listener = next) 645135446Strhodes { 646135446Strhodes /* 647135446Strhodes * This is asynchronous. As listeners shut down, they will 648135446Strhodes * call their callbacks. 649135446Strhodes */ 650135446Strhodes next = ISC_LIST_NEXT(listener, link); 651135446Strhodes shutdown_listener(listener); 652135446Strhodes } 653135446Strhodes} 654135446Strhodes 655135446Strhodesvoid 656135446Strhodesns_controls_shutdown(ns_controls_t *controls) { 657135446Strhodes controls_shutdown(controls); 658135446Strhodes controls->shuttingdown = ISC_TRUE; 659135446Strhodes} 660135446Strhodes 661135446Strhodesstatic isc_result_t 662165071Sdougbcfgkeylist_find(const cfg_obj_t *keylist, const char *keyname, 663165071Sdougb const cfg_obj_t **objp) 664165071Sdougb{ 665165071Sdougb const cfg_listelt_t *element; 666135446Strhodes const char *str; 667165071Sdougb const cfg_obj_t *obj; 668135446Strhodes 669135446Strhodes for (element = cfg_list_first(keylist); 670135446Strhodes element != NULL; 671135446Strhodes element = cfg_list_next(element)) 672135446Strhodes { 673135446Strhodes obj = cfg_listelt_value(element); 674135446Strhodes str = cfg_obj_asstring(cfg_map_getname(obj)); 675135446Strhodes if (strcasecmp(str, keyname) == 0) 676135446Strhodes break; 677135446Strhodes } 678135446Strhodes if (element == NULL) 679135446Strhodes return (ISC_R_NOTFOUND); 680135446Strhodes obj = cfg_listelt_value(element); 681135446Strhodes *objp = obj; 682135446Strhodes return (ISC_R_SUCCESS); 683135446Strhodes} 684135446Strhodes 685135446Strhodesstatic isc_result_t 686165071Sdougbcontrolkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx, 687135446Strhodes controlkeylist_t *keyids) 688135446Strhodes{ 689165071Sdougb const cfg_listelt_t *element; 690135446Strhodes char *newstr = NULL; 691135446Strhodes const char *str; 692165071Sdougb const cfg_obj_t *obj; 693170222Sdougb controlkey_t *key; 694135446Strhodes 695135446Strhodes for (element = cfg_list_first(keylist); 696135446Strhodes element != NULL; 697135446Strhodes element = cfg_list_next(element)) 698135446Strhodes { 699135446Strhodes obj = cfg_listelt_value(element); 700135446Strhodes str = cfg_obj_asstring(obj); 701135446Strhodes newstr = isc_mem_strdup(mctx, str); 702135446Strhodes if (newstr == NULL) 703135446Strhodes goto cleanup; 704135446Strhodes key = isc_mem_get(mctx, sizeof(*key)); 705135446Strhodes if (key == NULL) 706135446Strhodes goto cleanup; 707135446Strhodes key->keyname = newstr; 708135446Strhodes key->secret.base = NULL; 709135446Strhodes key->secret.length = 0; 710135446Strhodes ISC_LINK_INIT(key, link); 711135446Strhodes ISC_LIST_APPEND(*keyids, key, link); 712135446Strhodes newstr = NULL; 713135446Strhodes } 714135446Strhodes return (ISC_R_SUCCESS); 715135446Strhodes 716135446Strhodes cleanup: 717135446Strhodes if (newstr != NULL) 718135446Strhodes isc_mem_free(mctx, newstr); 719135446Strhodes free_controlkeylist(keyids, mctx); 720135446Strhodes return (ISC_R_NOMEMORY); 721135446Strhodes} 722135446Strhodes 723135446Strhodesstatic void 724165071Sdougbregister_keys(const cfg_obj_t *control, const cfg_obj_t *keylist, 725135446Strhodes controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext) 726135446Strhodes{ 727135446Strhodes controlkey_t *keyid, *next; 728165071Sdougb const cfg_obj_t *keydef; 729135446Strhodes char secret[1024]; 730135446Strhodes isc_buffer_t b; 731135446Strhodes isc_result_t result; 732135446Strhodes 733135446Strhodes /* 734135446Strhodes * Find the keys corresponding to the keyids used by this listener. 735135446Strhodes */ 736135446Strhodes for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) { 737135446Strhodes next = ISC_LIST_NEXT(keyid, link); 738135446Strhodes 739135446Strhodes result = cfgkeylist_find(keylist, keyid->keyname, &keydef); 740135446Strhodes if (result != ISC_R_SUCCESS) { 741135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 742135446Strhodes "couldn't find key '%s' for use with " 743135446Strhodes "command channel %s", 744135446Strhodes keyid->keyname, socktext); 745135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 746135446Strhodes free_controlkey(keyid, mctx); 747135446Strhodes } else { 748165071Sdougb const cfg_obj_t *algobj = NULL; 749165071Sdougb const cfg_obj_t *secretobj = NULL; 750165071Sdougb const char *algstr = NULL; 751165071Sdougb const char *secretstr = NULL; 752135446Strhodes 753135446Strhodes (void)cfg_map_get(keydef, "algorithm", &algobj); 754135446Strhodes (void)cfg_map_get(keydef, "secret", &secretobj); 755135446Strhodes INSIST(algobj != NULL && secretobj != NULL); 756135446Strhodes 757135446Strhodes algstr = cfg_obj_asstring(algobj); 758135446Strhodes secretstr = cfg_obj_asstring(secretobj); 759135446Strhodes 760170222Sdougb if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != 761135446Strhodes ISC_R_SUCCESS) 762135446Strhodes { 763135446Strhodes cfg_obj_log(control, ns_g_lctx, 764135446Strhodes ISC_LOG_WARNING, 765135446Strhodes "unsupported algorithm '%s' in " 766135446Strhodes "key '%s' for use with command " 767135446Strhodes "channel %s", 768135446Strhodes algstr, keyid->keyname, socktext); 769135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 770135446Strhodes free_controlkey(keyid, mctx); 771135446Strhodes continue; 772135446Strhodes } 773135446Strhodes 774135446Strhodes isc_buffer_init(&b, secret, sizeof(secret)); 775135446Strhodes result = isc_base64_decodestring(secretstr, &b); 776135446Strhodes 777135446Strhodes if (result != ISC_R_SUCCESS) { 778135446Strhodes cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING, 779135446Strhodes "secret for key '%s' on " 780135446Strhodes "command channel %s: %s", 781135446Strhodes keyid->keyname, socktext, 782135446Strhodes isc_result_totext(result)); 783135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 784135446Strhodes free_controlkey(keyid, mctx); 785135446Strhodes continue; 786135446Strhodes } 787135446Strhodes 788135446Strhodes keyid->secret.length = isc_buffer_usedlength(&b); 789135446Strhodes keyid->secret.base = isc_mem_get(mctx, 790135446Strhodes keyid->secret.length); 791135446Strhodes if (keyid->secret.base == NULL) { 792135446Strhodes cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING, 793135446Strhodes "couldn't register key '%s': " 794135446Strhodes "out of memory", keyid->keyname); 795135446Strhodes ISC_LIST_UNLINK(*keyids, keyid, link); 796135446Strhodes free_controlkey(keyid, mctx); 797135446Strhodes break; 798135446Strhodes } 799135446Strhodes memcpy(keyid->secret.base, isc_buffer_base(&b), 800135446Strhodes keyid->secret.length); 801135446Strhodes } 802135446Strhodes } 803135446Strhodes} 804135446Strhodes 805135446Strhodes#define CHECK(x) \ 806135446Strhodes do { \ 807135446Strhodes result = (x); \ 808135446Strhodes if (result != ISC_R_SUCCESS) \ 809135446Strhodes goto cleanup; \ 810135446Strhodes } while (0) 811135446Strhodes 812135446Strhodesstatic isc_result_t 813135446Strhodesget_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) { 814135446Strhodes isc_result_t result; 815135446Strhodes cfg_parser_t *pctx = NULL; 816135446Strhodes cfg_obj_t *config = NULL; 817165071Sdougb const cfg_obj_t *key = NULL; 818165071Sdougb const cfg_obj_t *algobj = NULL; 819165071Sdougb const cfg_obj_t *secretobj = NULL; 820165071Sdougb const char *algstr = NULL; 821165071Sdougb const char *secretstr = NULL; 822135446Strhodes controlkey_t *keyid = NULL; 823135446Strhodes char secret[1024]; 824135446Strhodes isc_buffer_t b; 825135446Strhodes 826135446Strhodes CHECK(cfg_parser_create(mctx, ns_g_lctx, &pctx)); 827135446Strhodes CHECK(cfg_parse_file(pctx, ns_g_keyfile, &cfg_type_rndckey, &config)); 828135446Strhodes CHECK(cfg_map_get(config, "key", &key)); 829135446Strhodes 830135446Strhodes keyid = isc_mem_get(mctx, sizeof(*keyid)); 831135446Strhodes if (keyid == NULL) 832135446Strhodes CHECK(ISC_R_NOMEMORY); 833135446Strhodes keyid->keyname = isc_mem_strdup(mctx, 834135446Strhodes cfg_obj_asstring(cfg_map_getname(key))); 835135446Strhodes keyid->secret.base = NULL; 836135446Strhodes keyid->secret.length = 0; 837135446Strhodes ISC_LINK_INIT(keyid, link); 838135446Strhodes if (keyid->keyname == NULL) 839135446Strhodes CHECK(ISC_R_NOMEMORY); 840135446Strhodes 841135446Strhodes CHECK(bind9_check_key(key, ns_g_lctx)); 842135446Strhodes 843135446Strhodes (void)cfg_map_get(key, "algorithm", &algobj); 844135446Strhodes (void)cfg_map_get(key, "secret", &secretobj); 845135446Strhodes INSIST(algobj != NULL && secretobj != NULL); 846135446Strhodes 847135446Strhodes algstr = cfg_obj_asstring(algobj); 848135446Strhodes secretstr = cfg_obj_asstring(secretobj); 849135446Strhodes 850170222Sdougb if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != ISC_R_SUCCESS) { 851135446Strhodes cfg_obj_log(key, ns_g_lctx, 852135446Strhodes ISC_LOG_WARNING, 853135446Strhodes "unsupported algorithm '%s' in " 854135446Strhodes "key '%s' for use with command " 855135446Strhodes "channel", 856135446Strhodes algstr, keyid->keyname); 857135446Strhodes goto cleanup; 858135446Strhodes } 859135446Strhodes 860135446Strhodes isc_buffer_init(&b, secret, sizeof(secret)); 861135446Strhodes result = isc_base64_decodestring(secretstr, &b); 862135446Strhodes 863135446Strhodes if (result != ISC_R_SUCCESS) { 864135446Strhodes cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, 865135446Strhodes "secret for key '%s' on command channel: %s", 866135446Strhodes keyid->keyname, isc_result_totext(result)); 867135446Strhodes CHECK(result); 868135446Strhodes } 869135446Strhodes 870135446Strhodes keyid->secret.length = isc_buffer_usedlength(&b); 871135446Strhodes keyid->secret.base = isc_mem_get(mctx, 872135446Strhodes keyid->secret.length); 873135446Strhodes if (keyid->secret.base == NULL) { 874135446Strhodes cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING, 875135446Strhodes "couldn't register key '%s': " 876135446Strhodes "out of memory", keyid->keyname); 877135446Strhodes CHECK(ISC_R_NOMEMORY); 878135446Strhodes } 879135446Strhodes memcpy(keyid->secret.base, isc_buffer_base(&b), 880135446Strhodes keyid->secret.length); 881135446Strhodes ISC_LIST_APPEND(*keyids, keyid, link); 882135446Strhodes keyid = NULL; 883135446Strhodes result = ISC_R_SUCCESS; 884135446Strhodes 885135446Strhodes cleanup: 886135446Strhodes if (keyid != NULL) 887135446Strhodes free_controlkey(keyid, mctx); 888135446Strhodes if (config != NULL) 889135446Strhodes cfg_obj_destroy(pctx, &config); 890135446Strhodes if (pctx != NULL) 891135446Strhodes cfg_parser_destroy(&pctx); 892135446Strhodes return (result); 893135446Strhodes} 894135446Strhodes 895135446Strhodes/* 896135446Strhodes * Ensures that both '*global_keylistp' and '*control_keylistp' are 897135446Strhodes * valid or both are NULL. 898135446Strhodes */ 899135446Strhodesstatic void 900165071Sdougbget_key_info(const cfg_obj_t *config, const cfg_obj_t *control, 901165071Sdougb const cfg_obj_t **global_keylistp, 902165071Sdougb const cfg_obj_t **control_keylistp) 903135446Strhodes{ 904135446Strhodes isc_result_t result; 905165071Sdougb const cfg_obj_t *control_keylist = NULL; 906165071Sdougb const cfg_obj_t *global_keylist = NULL; 907135446Strhodes 908135446Strhodes REQUIRE(global_keylistp != NULL && *global_keylistp == NULL); 909135446Strhodes REQUIRE(control_keylistp != NULL && *control_keylistp == NULL); 910135446Strhodes 911135446Strhodes control_keylist = cfg_tuple_get(control, "keys"); 912135446Strhodes 913135446Strhodes if (!cfg_obj_isvoid(control_keylist) && 914135446Strhodes cfg_list_first(control_keylist) != NULL) { 915135446Strhodes result = cfg_map_get(config, "key", &global_keylist); 916135446Strhodes 917135446Strhodes if (result == ISC_R_SUCCESS) { 918135446Strhodes *global_keylistp = global_keylist; 919135446Strhodes *control_keylistp = control_keylist; 920135446Strhodes } 921135446Strhodes } 922135446Strhodes} 923135446Strhodes 924135446Strhodesstatic void 925165071Sdougbupdate_listener(ns_controls_t *cp, controllistener_t **listenerp, 926165071Sdougb const cfg_obj_t *control, const cfg_obj_t *config, 927170222Sdougb isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, 928170222Sdougb const char *socktext, isc_sockettype_t type) 929135446Strhodes{ 930135446Strhodes controllistener_t *listener; 931165071Sdougb const cfg_obj_t *allow; 932165071Sdougb const cfg_obj_t *global_keylist = NULL; 933165071Sdougb const cfg_obj_t *control_keylist = NULL; 934135446Strhodes dns_acl_t *new_acl = NULL; 935135446Strhodes controlkeylist_t keys; 936135446Strhodes isc_result_t result = ISC_R_SUCCESS; 937135446Strhodes 938135446Strhodes for (listener = ISC_LIST_HEAD(cp->listeners); 939135446Strhodes listener != NULL; 940135446Strhodes listener = ISC_LIST_NEXT(listener, link)) 941135446Strhodes if (isc_sockaddr_equal(addr, &listener->address)) 942135446Strhodes break; 943135446Strhodes 944135446Strhodes if (listener == NULL) { 945135446Strhodes *listenerp = NULL; 946135446Strhodes return; 947135446Strhodes } 948135446Strhodes 949135446Strhodes /* 950135446Strhodes * There is already a listener for this sockaddr. 951135446Strhodes * Update the access list and key information. 952135446Strhodes * 953135446Strhodes * First try to deal with the key situation. There are a few 954135446Strhodes * possibilities: 955135446Strhodes * (a) It had an explicit keylist and still has an explicit keylist. 956135446Strhodes * (b) It had an automagic key and now has an explicit keylist. 957135446Strhodes * (c) It had an explicit keylist and now needs an automagic key. 958135446Strhodes * (d) It has an automagic key and still needs the automagic key. 959135446Strhodes * 960135446Strhodes * (c) and (d) are the annoying ones. The caller needs to know 961135446Strhodes * that it should use the automagic configuration for key information 962135446Strhodes * in place of the named.conf configuration. 963135446Strhodes * 964135446Strhodes * XXXDCL There is one other hazard that has not been dealt with, 965135446Strhodes * the problem that if a key change is being caused by a control 966135446Strhodes * channel reload, then the response will be with the new key 967135446Strhodes * and not able to be decrypted by the client. 968135446Strhodes */ 969135446Strhodes if (control != NULL) 970135446Strhodes get_key_info(config, control, &global_keylist, 971135446Strhodes &control_keylist); 972135446Strhodes 973135446Strhodes if (control_keylist != NULL) { 974135446Strhodes INSIST(global_keylist != NULL); 975135446Strhodes 976135446Strhodes ISC_LIST_INIT(keys); 977135446Strhodes result = controlkeylist_fromcfg(control_keylist, 978135446Strhodes listener->mctx, &keys); 979135446Strhodes if (result == ISC_R_SUCCESS) { 980135446Strhodes free_controlkeylist(&listener->keys, listener->mctx); 981135446Strhodes listener->keys = keys; 982135446Strhodes register_keys(control, global_keylist, &listener->keys, 983135446Strhodes listener->mctx, socktext); 984135446Strhodes } 985135446Strhodes } else { 986135446Strhodes free_controlkeylist(&listener->keys, listener->mctx); 987135446Strhodes result = get_rndckey(listener->mctx, &listener->keys); 988135446Strhodes } 989135446Strhodes 990165071Sdougb if (result != ISC_R_SUCCESS && global_keylist != NULL) { 991135446Strhodes /* 992135446Strhodes * This message might be a little misleading since the 993135446Strhodes * "new keys" might in fact be identical to the old ones, 994135446Strhodes * but tracking whether they are identical just for the 995135446Strhodes * sake of avoiding this message would be too much trouble. 996135446Strhodes */ 997165071Sdougb if (control != NULL) 998165071Sdougb cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 999165071Sdougb "couldn't install new keys for " 1000165071Sdougb "command channel %s: %s", 1001165071Sdougb socktext, isc_result_totext(result)); 1002165071Sdougb else 1003165071Sdougb isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1004165071Sdougb NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 1005165071Sdougb "couldn't install new keys for " 1006165071Sdougb "command channel %s: %s", 1007165071Sdougb socktext, isc_result_totext(result)); 1008165071Sdougb } 1009135446Strhodes 1010135446Strhodes /* 1011135446Strhodes * Now, keep the old access list unless a new one can be made. 1012135446Strhodes */ 1013170222Sdougb if (control != NULL && type == isc_sockettype_tcp) { 1014135446Strhodes allow = cfg_tuple_get(control, "allow"); 1015170222Sdougb result = cfg_acl_fromconfig(allow, config, ns_g_lctx, 1016170222Sdougb aclconfctx, listener->mctx, 1017170222Sdougb &new_acl); 1018135446Strhodes } else { 1019135446Strhodes result = dns_acl_any(listener->mctx, &new_acl); 1020135446Strhodes } 1021135446Strhodes 1022135446Strhodes if (result == ISC_R_SUCCESS) { 1023135446Strhodes dns_acl_detach(&listener->acl); 1024135446Strhodes dns_acl_attach(new_acl, &listener->acl); 1025135446Strhodes dns_acl_detach(&new_acl); 1026135446Strhodes /* XXXDCL say the old acl is still used? */ 1027165071Sdougb } else if (control != NULL) 1028135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1029135446Strhodes "couldn't install new acl for " 1030135446Strhodes "command channel %s: %s", 1031135446Strhodes socktext, isc_result_totext(result)); 1032165071Sdougb else 1033165071Sdougb isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1034165071Sdougb NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, 1035165071Sdougb "couldn't install new acl for " 1036165071Sdougb "command channel %s: %s", 1037165071Sdougb socktext, isc_result_totext(result)); 1038135446Strhodes 1039170222Sdougb if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) { 1040170222Sdougb isc_uint32_t perm, owner, group; 1041170222Sdougb perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm")); 1042170222Sdougb owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner")); 1043170222Sdougb group = cfg_obj_asuint32(cfg_tuple_get(control, "group")); 1044170222Sdougb result = ISC_R_SUCCESS; 1045170222Sdougb if (listener->perm != perm || listener->owner != owner || 1046170222Sdougb listener->group != group) 1047170222Sdougb result = isc_socket_permunix(&listener->address, perm, 1048170222Sdougb owner, group); 1049170222Sdougb if (result == ISC_R_SUCCESS) { 1050170222Sdougb listener->perm = perm; 1051170222Sdougb listener->owner = owner; 1052170222Sdougb listener->group = group; 1053170222Sdougb } else if (control != NULL) 1054170222Sdougb cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1055170222Sdougb "couldn't update ownership/permission for " 1056170222Sdougb "command channel %s", socktext); 1057170222Sdougb } 1058170222Sdougb 1059135446Strhodes *listenerp = listener; 1060135446Strhodes} 1061135446Strhodes 1062135446Strhodesstatic void 1063135446Strhodesadd_listener(ns_controls_t *cp, controllistener_t **listenerp, 1064165071Sdougb const cfg_obj_t *control, const cfg_obj_t *config, 1065170222Sdougb isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx, 1066170222Sdougb const char *socktext, isc_sockettype_t type) 1067135446Strhodes{ 1068135446Strhodes isc_mem_t *mctx = cp->server->mctx; 1069135446Strhodes controllistener_t *listener; 1070165071Sdougb const cfg_obj_t *allow; 1071165071Sdougb const cfg_obj_t *global_keylist = NULL; 1072165071Sdougb const cfg_obj_t *control_keylist = NULL; 1073135446Strhodes dns_acl_t *new_acl = NULL; 1074135446Strhodes isc_result_t result = ISC_R_SUCCESS; 1075135446Strhodes 1076135446Strhodes listener = isc_mem_get(mctx, sizeof(*listener)); 1077135446Strhodes if (listener == NULL) 1078135446Strhodes result = ISC_R_NOMEMORY; 1079135446Strhodes 1080135446Strhodes if (result == ISC_R_SUCCESS) { 1081135446Strhodes listener->controls = cp; 1082135446Strhodes listener->mctx = mctx; 1083135446Strhodes listener->task = cp->server->task; 1084135446Strhodes listener->address = *addr; 1085135446Strhodes listener->sock = NULL; 1086135446Strhodes listener->listening = ISC_FALSE; 1087135446Strhodes listener->exiting = ISC_FALSE; 1088135446Strhodes listener->acl = NULL; 1089170222Sdougb listener->type = type; 1090170222Sdougb listener->perm = 0; 1091170222Sdougb listener->owner = 0; 1092170222Sdougb listener->group = 0; 1093135446Strhodes ISC_LINK_INIT(listener, link); 1094135446Strhodes ISC_LIST_INIT(listener->keys); 1095135446Strhodes ISC_LIST_INIT(listener->connections); 1096135446Strhodes 1097135446Strhodes /* 1098135446Strhodes * Make the acl. 1099135446Strhodes */ 1100170222Sdougb if (control != NULL && type == isc_sockettype_tcp) { 1101135446Strhodes allow = cfg_tuple_get(control, "allow"); 1102170222Sdougb result = cfg_acl_fromconfig(allow, config, ns_g_lctx, 1103170222Sdougb aclconfctx, mctx, &new_acl); 1104135446Strhodes } else { 1105135446Strhodes result = dns_acl_any(mctx, &new_acl); 1106135446Strhodes } 1107135446Strhodes } 1108135446Strhodes 1109135446Strhodes if (result == ISC_R_SUCCESS) { 1110135446Strhodes dns_acl_attach(new_acl, &listener->acl); 1111135446Strhodes dns_acl_detach(&new_acl); 1112135446Strhodes 1113135446Strhodes if (config != NULL) 1114135446Strhodes get_key_info(config, control, &global_keylist, 1115135446Strhodes &control_keylist); 1116135446Strhodes 1117135446Strhodes if (control_keylist != NULL) { 1118135446Strhodes result = controlkeylist_fromcfg(control_keylist, 1119135446Strhodes listener->mctx, 1120135446Strhodes &listener->keys); 1121135446Strhodes if (result == ISC_R_SUCCESS) 1122135446Strhodes register_keys(control, global_keylist, 1123135446Strhodes &listener->keys, 1124135446Strhodes listener->mctx, socktext); 1125135446Strhodes } else 1126135446Strhodes result = get_rndckey(mctx, &listener->keys); 1127135446Strhodes 1128135446Strhodes if (result != ISC_R_SUCCESS && control != NULL) 1129135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1130135446Strhodes "couldn't install keys for " 1131135446Strhodes "command channel %s: %s", 1132135446Strhodes socktext, isc_result_totext(result)); 1133135446Strhodes } 1134135446Strhodes 1135135446Strhodes if (result == ISC_R_SUCCESS) { 1136135446Strhodes int pf = isc_sockaddr_pf(&listener->address); 1137135446Strhodes if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) || 1138170222Sdougb#ifdef ISC_PLATFORM_HAVESYSUNH 1139170222Sdougb (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) || 1140170222Sdougb#endif 1141135446Strhodes (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) 1142135446Strhodes result = ISC_R_FAMILYNOSUPPORT; 1143135446Strhodes } 1144135446Strhodes 1145170222Sdougb if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) 1146170222Sdougb isc_socket_cleanunix(&listener->address, ISC_FALSE); 1147170222Sdougb 1148135446Strhodes if (result == ISC_R_SUCCESS) 1149135446Strhodes result = isc_socket_create(ns_g_socketmgr, 1150135446Strhodes isc_sockaddr_pf(&listener->address), 1151170222Sdougb type, &listener->sock); 1152135446Strhodes 1153135446Strhodes if (result == ISC_R_SUCCESS) 1154135446Strhodes result = isc_socket_bind(listener->sock, 1155135446Strhodes &listener->address); 1156135446Strhodes 1157170222Sdougb if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) { 1158170222Sdougb listener->perm = cfg_obj_asuint32(cfg_tuple_get(control, 1159170222Sdougb "perm")); 1160170222Sdougb listener->owner = cfg_obj_asuint32(cfg_tuple_get(control, 1161170222Sdougb "owner")); 1162170222Sdougb listener->group = cfg_obj_asuint32(cfg_tuple_get(control, 1163170222Sdougb "group")); 1164170222Sdougb result = isc_socket_permunix(&listener->address, listener->perm, 1165170222Sdougb listener->owner, listener->group); 1166170222Sdougb } 1167135446Strhodes if (result == ISC_R_SUCCESS) 1168135446Strhodes result = control_listen(listener); 1169135446Strhodes 1170135446Strhodes if (result == ISC_R_SUCCESS) 1171135446Strhodes result = control_accept(listener); 1172135446Strhodes 1173135446Strhodes if (result == ISC_R_SUCCESS) { 1174135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1175135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 1176135446Strhodes "command channel listening on %s", socktext); 1177135446Strhodes *listenerp = listener; 1178135446Strhodes 1179135446Strhodes } else { 1180135446Strhodes if (listener != NULL) { 1181135446Strhodes listener->exiting = ISC_TRUE; 1182135446Strhodes free_listener(listener); 1183135446Strhodes } 1184135446Strhodes 1185135446Strhodes if (control != NULL) 1186135446Strhodes cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING, 1187135446Strhodes "couldn't add command channel %s: %s", 1188135446Strhodes socktext, isc_result_totext(result)); 1189135446Strhodes else 1190135446Strhodes isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, 1191135446Strhodes NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE, 1192135446Strhodes "couldn't add command channel %s: %s", 1193135446Strhodes socktext, isc_result_totext(result)); 1194135446Strhodes 1195135446Strhodes *listenerp = NULL; 1196135446Strhodes } 1197135446Strhodes 1198135446Strhodes /* XXXDCL return error results? fail hard? */ 1199135446Strhodes} 1200135446Strhodes 1201135446Strhodesisc_result_t 1202165071Sdougbns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config, 1203170222Sdougb cfg_aclconfctx_t *aclconfctx) 1204135446Strhodes{ 1205135446Strhodes controllistener_t *listener; 1206135446Strhodes controllistenerlist_t new_listeners; 1207165071Sdougb const cfg_obj_t *controlslist = NULL; 1208165071Sdougb const cfg_listelt_t *element, *element2; 1209135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 1210135446Strhodes 1211135446Strhodes ISC_LIST_INIT(new_listeners); 1212135446Strhodes 1213135446Strhodes /* 1214135446Strhodes * Get the list of named.conf 'controls' statements. 1215135446Strhodes */ 1216135446Strhodes (void)cfg_map_get(config, "controls", &controlslist); 1217135446Strhodes 1218135446Strhodes /* 1219135446Strhodes * Run through the new control channel list, noting sockets that 1220135446Strhodes * are already being listened on and moving them to the new list. 1221135446Strhodes * 1222135446Strhodes * Identifying duplicate addr/port combinations is left to either 1223135446Strhodes * the underlying config code, or to the bind attempt getting an 1224135446Strhodes * address-in-use error. 1225135446Strhodes */ 1226135446Strhodes if (controlslist != NULL) { 1227135446Strhodes for (element = cfg_list_first(controlslist); 1228135446Strhodes element != NULL; 1229135446Strhodes element = cfg_list_next(element)) { 1230165071Sdougb const cfg_obj_t *controls; 1231165071Sdougb const cfg_obj_t *inetcontrols = NULL; 1232135446Strhodes 1233135446Strhodes controls = cfg_listelt_value(element); 1234135446Strhodes (void)cfg_map_get(controls, "inet", &inetcontrols); 1235135446Strhodes if (inetcontrols == NULL) 1236135446Strhodes continue; 1237135446Strhodes 1238135446Strhodes for (element2 = cfg_list_first(inetcontrols); 1239135446Strhodes element2 != NULL; 1240135446Strhodes element2 = cfg_list_next(element2)) { 1241165071Sdougb const cfg_obj_t *control; 1242165071Sdougb const cfg_obj_t *obj; 1243165071Sdougb isc_sockaddr_t addr; 1244135446Strhodes 1245135446Strhodes /* 1246135446Strhodes * The parser handles BIND 8 configuration file 1247135446Strhodes * syntax, so it allows unix phrases as well 1248135446Strhodes * inet phrases with no keys{} clause. 1249135446Strhodes */ 1250135446Strhodes control = cfg_listelt_value(element2); 1251135446Strhodes 1252135446Strhodes obj = cfg_tuple_get(control, "address"); 1253165071Sdougb addr = *cfg_obj_assockaddr(obj); 1254165071Sdougb if (isc_sockaddr_getport(&addr) == 0) 1255165071Sdougb isc_sockaddr_setport(&addr, 1256135446Strhodes NS_CONTROL_PORT); 1257135446Strhodes 1258165071Sdougb isc_sockaddr_format(&addr, socktext, 1259135446Strhodes sizeof(socktext)); 1260135446Strhodes 1261135446Strhodes isc_log_write(ns_g_lctx, 1262135446Strhodes NS_LOGCATEGORY_GENERAL, 1263135446Strhodes NS_LOGMODULE_CONTROL, 1264135446Strhodes ISC_LOG_DEBUG(9), 1265135446Strhodes "processing control channel %s", 1266135446Strhodes socktext); 1267135446Strhodes 1268135446Strhodes update_listener(cp, &listener, control, config, 1269170222Sdougb &addr, aclconfctx, socktext, 1270170222Sdougb isc_sockettype_tcp); 1271135446Strhodes 1272135446Strhodes if (listener != NULL) 1273135446Strhodes /* 1274135446Strhodes * Remove the listener from the old 1275135446Strhodes * list, so it won't be shut down. 1276135446Strhodes */ 1277135446Strhodes ISC_LIST_UNLINK(cp->listeners, 1278135446Strhodes listener, link); 1279135446Strhodes else 1280135446Strhodes /* 1281135446Strhodes * This is a new listener. 1282135446Strhodes */ 1283135446Strhodes add_listener(cp, &listener, control, 1284165071Sdougb config, &addr, aclconfctx, 1285170222Sdougb socktext, 1286170222Sdougb isc_sockettype_tcp); 1287135446Strhodes 1288135446Strhodes if (listener != NULL) 1289135446Strhodes ISC_LIST_APPEND(new_listeners, 1290135446Strhodes listener, link); 1291135446Strhodes } 1292135446Strhodes } 1293170222Sdougb for (element = cfg_list_first(controlslist); 1294170222Sdougb element != NULL; 1295170222Sdougb element = cfg_list_next(element)) { 1296170222Sdougb const cfg_obj_t *controls; 1297170222Sdougb const cfg_obj_t *unixcontrols = NULL; 1298170222Sdougb 1299170222Sdougb controls = cfg_listelt_value(element); 1300170222Sdougb (void)cfg_map_get(controls, "unix", &unixcontrols); 1301170222Sdougb if (unixcontrols == NULL) 1302170222Sdougb continue; 1303170222Sdougb 1304170222Sdougb for (element2 = cfg_list_first(unixcontrols); 1305170222Sdougb element2 != NULL; 1306170222Sdougb element2 = cfg_list_next(element2)) { 1307170222Sdougb const cfg_obj_t *control; 1308170222Sdougb const cfg_obj_t *path; 1309170222Sdougb isc_sockaddr_t addr; 1310170222Sdougb isc_result_t result; 1311170222Sdougb 1312170222Sdougb /* 1313170222Sdougb * The parser handles BIND 8 configuration file 1314170222Sdougb * syntax, so it allows unix phrases as well 1315170222Sdougb * inet phrases with no keys{} clause. 1316170222Sdougb */ 1317170222Sdougb control = cfg_listelt_value(element2); 1318170222Sdougb 1319170222Sdougb path = cfg_tuple_get(control, "path"); 1320170222Sdougb result = isc_sockaddr_frompath(&addr, 1321170222Sdougb cfg_obj_asstring(path)); 1322170222Sdougb if (result != ISC_R_SUCCESS) { 1323170222Sdougb isc_log_write(ns_g_lctx, 1324170222Sdougb NS_LOGCATEGORY_GENERAL, 1325170222Sdougb NS_LOGMODULE_CONTROL, 1326170222Sdougb ISC_LOG_DEBUG(9), 1327170222Sdougb "control channel '%s': %s", 1328170222Sdougb cfg_obj_asstring(path), 1329170222Sdougb isc_result_totext(result)); 1330170222Sdougb continue; 1331170222Sdougb } 1332170222Sdougb 1333170222Sdougb isc_log_write(ns_g_lctx, 1334170222Sdougb NS_LOGCATEGORY_GENERAL, 1335170222Sdougb NS_LOGMODULE_CONTROL, 1336170222Sdougb ISC_LOG_DEBUG(9), 1337170222Sdougb "processing control channel '%s'", 1338170222Sdougb cfg_obj_asstring(path)); 1339170222Sdougb 1340170222Sdougb update_listener(cp, &listener, control, config, 1341170222Sdougb &addr, aclconfctx, 1342170222Sdougb cfg_obj_asstring(path), 1343170222Sdougb isc_sockettype_unix); 1344170222Sdougb 1345170222Sdougb if (listener != NULL) 1346170222Sdougb /* 1347170222Sdougb * Remove the listener from the old 1348170222Sdougb * list, so it won't be shut down. 1349170222Sdougb */ 1350170222Sdougb ISC_LIST_UNLINK(cp->listeners, 1351170222Sdougb listener, link); 1352170222Sdougb else 1353170222Sdougb /* 1354170222Sdougb * This is a new listener. 1355170222Sdougb */ 1356170222Sdougb add_listener(cp, &listener, control, 1357170222Sdougb config, &addr, aclconfctx, 1358170222Sdougb cfg_obj_asstring(path), 1359170222Sdougb isc_sockettype_unix); 1360170222Sdougb 1361170222Sdougb if (listener != NULL) 1362170222Sdougb ISC_LIST_APPEND(new_listeners, 1363170222Sdougb listener, link); 1364170222Sdougb } 1365170222Sdougb } 1366135446Strhodes } else { 1367135446Strhodes int i; 1368135446Strhodes 1369135446Strhodes for (i = 0; i < 2; i++) { 1370135446Strhodes isc_sockaddr_t addr; 1371135446Strhodes 1372135446Strhodes if (i == 0) { 1373135446Strhodes struct in_addr localhost; 1374135446Strhodes 1375135446Strhodes if (isc_net_probeipv4() != ISC_R_SUCCESS) 1376135446Strhodes continue; 1377135446Strhodes localhost.s_addr = htonl(INADDR_LOOPBACK); 1378135446Strhodes isc_sockaddr_fromin(&addr, &localhost, 0); 1379135446Strhodes } else { 1380135446Strhodes if (isc_net_probeipv6() != ISC_R_SUCCESS) 1381135446Strhodes continue; 1382135446Strhodes isc_sockaddr_fromin6(&addr, 1383135446Strhodes &in6addr_loopback, 0); 1384135446Strhodes } 1385135446Strhodes isc_sockaddr_setport(&addr, NS_CONTROL_PORT); 1386135446Strhodes 1387135446Strhodes isc_sockaddr_format(&addr, socktext, sizeof(socktext)); 1388135446Strhodes 1389135446Strhodes update_listener(cp, &listener, NULL, NULL, 1390170222Sdougb &addr, NULL, socktext, 1391170222Sdougb isc_sockettype_tcp); 1392135446Strhodes 1393135446Strhodes if (listener != NULL) 1394135446Strhodes /* 1395135446Strhodes * Remove the listener from the old 1396135446Strhodes * list, so it won't be shut down. 1397135446Strhodes */ 1398135446Strhodes ISC_LIST_UNLINK(cp->listeners, 1399135446Strhodes listener, link); 1400135446Strhodes else 1401135446Strhodes /* 1402135446Strhodes * This is a new listener. 1403135446Strhodes */ 1404135446Strhodes add_listener(cp, &listener, NULL, NULL, 1405170222Sdougb &addr, NULL, socktext, 1406170222Sdougb isc_sockettype_tcp); 1407135446Strhodes 1408135446Strhodes if (listener != NULL) 1409135446Strhodes ISC_LIST_APPEND(new_listeners, 1410135446Strhodes listener, link); 1411135446Strhodes } 1412135446Strhodes } 1413135446Strhodes 1414135446Strhodes /* 1415135446Strhodes * ns_control_shutdown() will stop whatever is on the global 1416135446Strhodes * listeners list, which currently only has whatever sockaddrs 1417135446Strhodes * were in the previous configuration (if any) that do not 1418135446Strhodes * remain in the current configuration. 1419135446Strhodes */ 1420135446Strhodes controls_shutdown(cp); 1421135446Strhodes 1422135446Strhodes /* 1423135446Strhodes * Put all of the valid listeners on the listeners list. 1424135446Strhodes * Anything already on listeners in the process of shutting 1425135446Strhodes * down will be taken care of by listen_done(). 1426135446Strhodes */ 1427135446Strhodes ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link); 1428135446Strhodes return (ISC_R_SUCCESS); 1429135446Strhodes} 1430135446Strhodes 1431135446Strhodesisc_result_t 1432135446Strhodesns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp) { 1433135446Strhodes isc_mem_t *mctx = server->mctx; 1434135446Strhodes isc_result_t result; 1435135446Strhodes ns_controls_t *controls = isc_mem_get(mctx, sizeof(*controls)); 1436135446Strhodes 1437135446Strhodes if (controls == NULL) 1438135446Strhodes return (ISC_R_NOMEMORY); 1439135446Strhodes controls->server = server; 1440135446Strhodes ISC_LIST_INIT(controls->listeners); 1441135446Strhodes controls->shuttingdown = ISC_FALSE; 1442135446Strhodes controls->symtab = NULL; 1443135446Strhodes result = isccc_cc_createsymtab(&controls->symtab); 1444135446Strhodes if (result != ISC_R_SUCCESS) { 1445135446Strhodes isc_mem_put(server->mctx, controls, sizeof(*controls)); 1446135446Strhodes return (result); 1447135446Strhodes } 1448135446Strhodes *ctrlsp = controls; 1449135446Strhodes return (ISC_R_SUCCESS); 1450135446Strhodes} 1451135446Strhodes 1452135446Strhodesvoid 1453135446Strhodesns_controls_destroy(ns_controls_t **ctrlsp) { 1454135446Strhodes ns_controls_t *controls = *ctrlsp; 1455135446Strhodes 1456135446Strhodes REQUIRE(ISC_LIST_EMPTY(controls->listeners)); 1457135446Strhodes 1458135446Strhodes isccc_symtab_destroy(&controls->symtab); 1459135446Strhodes isc_mem_put(controls->server->mctx, controls, sizeof(*controls)); 1460135446Strhodes *ctrlsp = NULL; 1461135446Strhodes} 1462