controlconf.c revision 234010
1135446Strhodes/*
2234010Sdougb * Copyright (C) 2004-2008, 2011, 2012  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
18234010Sdougb/* $Id: controlconf.c,v 1.60.544.3 2011/12/22 08:10:09 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
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;
370135446Strhodes		memcpy(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));
376234010Sdougb		log_invalid(&conn->ccmsg, result);
377234010Sdougb		goto cleanup;
378135446Strhodes	}
379135446Strhodes
380135446Strhodes	if (key == NULL) {
381135446Strhodes		log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
382135446Strhodes		goto cleanup;
383135446Strhodes	}
384135446Strhodes
385135446Strhodes	/* We shouldn't be getting a reply. */
386135446Strhodes	if (isccc_cc_isreply(request)) {
387135446Strhodes		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
388186462Sdougb		goto cleanup_request;
389135446Strhodes	}
390135446Strhodes
391135446Strhodes	isc_stdtime_get(&now);
392135446Strhodes
393135446Strhodes	/*
394135446Strhodes	 * Limit exposure to replay attacks.
395135446Strhodes	 */
396135446Strhodes	_ctrl = isccc_alist_lookup(request, "_ctrl");
397135446Strhodes	if (_ctrl == NULL) {
398135446Strhodes		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
399186462Sdougb		goto cleanup_request;
400135446Strhodes	}
401135446Strhodes
402135446Strhodes	if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) {
403135446Strhodes		if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) {
404135446Strhodes			log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW);
405186462Sdougb			goto cleanup_request;
406135446Strhodes		}
407135446Strhodes	} else {
408135446Strhodes		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
409186462Sdougb		goto cleanup_request;
410135446Strhodes	}
411135446Strhodes
412135446Strhodes	/*
413135446Strhodes	 * Expire messages that are too old.
414135446Strhodes	 */
415135446Strhodes	if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS &&
416135446Strhodes	    now > exp) {
417135446Strhodes		log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED);
418186462Sdougb		goto cleanup_request;
419135446Strhodes	}
420135446Strhodes
421135446Strhodes	/*
422135446Strhodes	 * Duplicate suppression (required for UDP).
423135446Strhodes	 */
424135446Strhodes	isccc_cc_cleansymtab(listener->controls->symtab, now);
425135446Strhodes	result = isccc_cc_checkdup(listener->controls->symtab, request, now);
426135446Strhodes	if (result != ISC_R_SUCCESS) {
427135446Strhodes		if (result == ISC_R_EXISTS)
428186462Sdougb			result = ISCCC_R_DUPLICATE;
429135446Strhodes		log_invalid(&conn->ccmsg, result);
430186462Sdougb		goto cleanup_request;
431135446Strhodes	}
432135446Strhodes
433135446Strhodes	if (conn->nonce != 0 &&
434135446Strhodes	    (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS ||
435135446Strhodes	     conn->nonce != nonce)) {
436135446Strhodes		log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
437186462Sdougb		goto cleanup_request;
438135446Strhodes	}
439135446Strhodes
440135446Strhodes	/*
441135446Strhodes	 * Establish nonce.
442135446Strhodes	 */
443135446Strhodes	while (conn->nonce == 0)
444135446Strhodes		isc_random_get(&conn->nonce);
445135446Strhodes
446135446Strhodes	isc_buffer_init(&text, textarray, sizeof(textarray));
447135446Strhodes	eresult = ns_control_docommand(request, &text);
448135446Strhodes
449135446Strhodes	result = isccc_cc_createresponse(request, now, now + 60, &response);
450135446Strhodes	if (result != ISC_R_SUCCESS)
451186462Sdougb		goto cleanup_request;
452135446Strhodes	if (eresult != ISC_R_SUCCESS) {
453135446Strhodes		isccc_sexpr_t *data;
454135446Strhodes
455135446Strhodes		data = isccc_alist_lookup(response, "_data");
456135446Strhodes		if (data != NULL) {
457135446Strhodes			const char *estr = isc_result_totext(eresult);
458135446Strhodes			if (isccc_cc_definestring(data, "err", estr) == NULL)
459186462Sdougb				goto cleanup_response;
460135446Strhodes		}
461135446Strhodes	}
462135446Strhodes
463135446Strhodes	if (isc_buffer_usedlength(&text) > 0) {
464135446Strhodes		isccc_sexpr_t *data;
465135446Strhodes
466135446Strhodes		data = isccc_alist_lookup(response, "_data");
467135446Strhodes		if (data != NULL) {
468135446Strhodes			char *str = (char *)isc_buffer_base(&text);
469135446Strhodes			if (isccc_cc_definestring(data, "text", str) == NULL)
470186462Sdougb				goto cleanup_response;
471135446Strhodes		}
472135446Strhodes	}
473135446Strhodes
474135446Strhodes	_ctrl = isccc_alist_lookup(response, "_ctrl");
475135446Strhodes	if (_ctrl == NULL ||
476135446Strhodes	    isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL)
477186462Sdougb		goto cleanup_response;
478135446Strhodes
479135446Strhodes	ccregion.rstart = conn->buffer + 4;
480135446Strhodes	ccregion.rend = conn->buffer + sizeof(conn->buffer);
481135446Strhodes	result = isccc_cc_towire(response, &ccregion, &secret);
482135446Strhodes	if (result != ISC_R_SUCCESS)
483186462Sdougb		goto cleanup_response;
484135446Strhodes	isc_buffer_init(&b, conn->buffer, 4);
485135446Strhodes	len = sizeof(conn->buffer) - REGION_SIZE(ccregion);
486135446Strhodes	isc_buffer_putuint32(&b, len - 4);
487135446Strhodes	r.base = conn->buffer;
488135446Strhodes	r.length = len;
489135446Strhodes
490135446Strhodes	result = isc_socket_send(conn->sock, &r, task, control_senddone, conn);
491135446Strhodes	if (result != ISC_R_SUCCESS)
492186462Sdougb		goto cleanup_response;
493135446Strhodes	conn->sending = ISC_TRUE;
494135446Strhodes
495186462Sdougb	isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
496186462Sdougb	isccc_sexpr_free(&request);
497186462Sdougb	isccc_sexpr_free(&response);
498135446Strhodes	return;
499135446Strhodes
500186462Sdougb cleanup_response:
501186462Sdougb	isccc_sexpr_free(&response);
502186462Sdougb
503186462Sdougb cleanup_request:
504186462Sdougb	isccc_sexpr_free(&request);
505186462Sdougb	isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
506186462Sdougb
507135446Strhodes cleanup:
508135446Strhodes	isc_socket_detach(&conn->sock);
509135446Strhodes	isccc_ccmsg_invalidate(&conn->ccmsg);
510135446Strhodes	conn->ccmsg_valid = ISC_FALSE;
511135446Strhodes	maybe_free_connection(conn);
512135446Strhodes	maybe_free_listener(listener);
513135446Strhodes}
514135446Strhodes
515135446Strhodesstatic void
516135446Strhodescontrol_timeout(isc_task_t *task, isc_event_t *event) {
517135446Strhodes	controlconnection_t *conn = event->ev_arg;
518135446Strhodes
519135446Strhodes	UNUSED(task);
520135446Strhodes
521135446Strhodes	isc_timer_detach(&conn->timer);
522135446Strhodes	maybe_free_connection(conn);
523135446Strhodes
524135446Strhodes	isc_event_free(&event);
525135446Strhodes}
526135446Strhodes
527135446Strhodesstatic isc_result_t
528135446Strhodesnewconnection(controllistener_t *listener, isc_socket_t *sock) {
529135446Strhodes	controlconnection_t *conn;
530135446Strhodes	isc_interval_t interval;
531135446Strhodes	isc_result_t result;
532135446Strhodes
533135446Strhodes	conn = isc_mem_get(listener->mctx, sizeof(*conn));
534135446Strhodes	if (conn == NULL)
535135446Strhodes		return (ISC_R_NOMEMORY);
536186462Sdougb
537135446Strhodes	conn->sock = sock;
538135446Strhodes	isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg);
539135446Strhodes	conn->ccmsg_valid = ISC_TRUE;
540135446Strhodes	conn->sending = ISC_FALSE;
541135446Strhodes	conn->timer = NULL;
542135446Strhodes	isc_interval_set(&interval, 60, 0);
543135446Strhodes	result = isc_timer_create(ns_g_timermgr, isc_timertype_once,
544135446Strhodes				  NULL, &interval, listener->task,
545135446Strhodes				  control_timeout, conn, &conn->timer);
546135446Strhodes	if (result != ISC_R_SUCCESS)
547135446Strhodes		goto cleanup;
548135446Strhodes
549135446Strhodes	conn->listener = listener;
550135446Strhodes	conn->nonce = 0;
551135446Strhodes	ISC_LINK_INIT(conn, link);
552135446Strhodes
553135446Strhodes	result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task,
554135446Strhodes					 control_recvmessage, conn);
555135446Strhodes	if (result != ISC_R_SUCCESS)
556135446Strhodes		goto cleanup;
557135446Strhodes	isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048);
558135446Strhodes
559135446Strhodes	ISC_LIST_APPEND(listener->connections, conn, link);
560135446Strhodes	return (ISC_R_SUCCESS);
561135446Strhodes
562135446Strhodes cleanup:
563135446Strhodes	isccc_ccmsg_invalidate(&conn->ccmsg);
564135446Strhodes	if (conn->timer != NULL)
565135446Strhodes		isc_timer_detach(&conn->timer);
566135446Strhodes	isc_mem_put(listener->mctx, conn, sizeof(*conn));
567135446Strhodes	return (result);
568135446Strhodes}
569135446Strhodes
570135446Strhodesstatic void
571135446Strhodescontrol_newconn(isc_task_t *task, isc_event_t *event) {
572135446Strhodes	isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
573135446Strhodes	controllistener_t *listener = event->ev_arg;
574135446Strhodes	isc_socket_t *sock;
575135446Strhodes	isc_sockaddr_t peeraddr;
576135446Strhodes	isc_result_t result;
577135446Strhodes
578135446Strhodes	UNUSED(task);
579135446Strhodes
580135446Strhodes	listener->listening = ISC_FALSE;
581135446Strhodes
582135446Strhodes	if (nevent->result != ISC_R_SUCCESS) {
583135446Strhodes		if (nevent->result == ISC_R_CANCELED) {
584135446Strhodes			shutdown_listener(listener);
585135446Strhodes			goto cleanup;
586135446Strhodes		}
587135446Strhodes		goto restart;
588135446Strhodes	}
589135446Strhodes
590135446Strhodes	sock = nevent->newsocket;
591193149Sdougb	isc_socket_setname(sock, "control", NULL);
592135446Strhodes	(void)isc_socket_getpeername(sock, &peeraddr);
593170222Sdougb	if (listener->type == isc_sockettype_tcp &&
594170222Sdougb	    !address_ok(&peeraddr, listener->acl)) {
595135446Strhodes		char socktext[ISC_SOCKADDR_FORMATSIZE];
596135446Strhodes		isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
597135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
598135446Strhodes			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
599135446Strhodes			      "rejected command channel message from %s",
600135446Strhodes			      socktext);
601135446Strhodes		isc_socket_detach(&sock);
602135446Strhodes		goto restart;
603135446Strhodes	}
604135446Strhodes
605135446Strhodes	result = newconnection(listener, sock);
606135446Strhodes	if (result != ISC_R_SUCCESS) {
607135446Strhodes		char socktext[ISC_SOCKADDR_FORMATSIZE];
608135446Strhodes		isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
609135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
610135446Strhodes			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
611135446Strhodes			      "dropped command channel from %s: %s",
612135446Strhodes			      socktext, isc_result_totext(result));
613135446Strhodes		isc_socket_detach(&sock);
614135446Strhodes		goto restart;
615135446Strhodes	}
616135446Strhodes
617135446Strhodes restart:
618135446Strhodes	control_next(listener);
619135446Strhodes cleanup:
620135446Strhodes	isc_event_free(&event);
621135446Strhodes}
622135446Strhodes
623135446Strhodesstatic void
624135446Strhodescontrols_shutdown(ns_controls_t *controls) {
625135446Strhodes	controllistener_t *listener;
626135446Strhodes	controllistener_t *next;
627135446Strhodes
628135446Strhodes	for (listener = ISC_LIST_HEAD(controls->listeners);
629135446Strhodes	     listener != NULL;
630135446Strhodes	     listener = next)
631135446Strhodes	{
632135446Strhodes		/*
633135446Strhodes		 * This is asynchronous.  As listeners shut down, they will
634135446Strhodes		 * call their callbacks.
635135446Strhodes		 */
636135446Strhodes		next = ISC_LIST_NEXT(listener, link);
637135446Strhodes		shutdown_listener(listener);
638135446Strhodes	}
639135446Strhodes}
640135446Strhodes
641135446Strhodesvoid
642135446Strhodesns_controls_shutdown(ns_controls_t *controls) {
643135446Strhodes	controls_shutdown(controls);
644135446Strhodes	controls->shuttingdown = ISC_TRUE;
645135446Strhodes}
646135446Strhodes
647135446Strhodesstatic isc_result_t
648165071Sdougbcfgkeylist_find(const cfg_obj_t *keylist, const char *keyname,
649186462Sdougb		const cfg_obj_t **objp)
650165071Sdougb{
651165071Sdougb	const cfg_listelt_t *element;
652135446Strhodes	const char *str;
653165071Sdougb	const cfg_obj_t *obj;
654135446Strhodes
655135446Strhodes	for (element = cfg_list_first(keylist);
656135446Strhodes	     element != NULL;
657135446Strhodes	     element = cfg_list_next(element))
658135446Strhodes	{
659135446Strhodes		obj = cfg_listelt_value(element);
660135446Strhodes		str = cfg_obj_asstring(cfg_map_getname(obj));
661135446Strhodes		if (strcasecmp(str, keyname) == 0)
662135446Strhodes			break;
663135446Strhodes	}
664135446Strhodes	if (element == NULL)
665135446Strhodes		return (ISC_R_NOTFOUND);
666135446Strhodes	obj = cfg_listelt_value(element);
667135446Strhodes	*objp = obj;
668135446Strhodes	return (ISC_R_SUCCESS);
669135446Strhodes}
670135446Strhodes
671135446Strhodesstatic isc_result_t
672165071Sdougbcontrolkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx,
673135446Strhodes		       controlkeylist_t *keyids)
674135446Strhodes{
675165071Sdougb	const cfg_listelt_t *element;
676135446Strhodes	char *newstr = NULL;
677135446Strhodes	const char *str;
678165071Sdougb	const cfg_obj_t *obj;
679170222Sdougb	controlkey_t *key;
680135446Strhodes
681135446Strhodes	for (element = cfg_list_first(keylist);
682135446Strhodes	     element != NULL;
683135446Strhodes	     element = cfg_list_next(element))
684135446Strhodes	{
685135446Strhodes		obj = cfg_listelt_value(element);
686135446Strhodes		str = cfg_obj_asstring(obj);
687135446Strhodes		newstr = isc_mem_strdup(mctx, str);
688135446Strhodes		if (newstr == NULL)
689135446Strhodes			goto cleanup;
690135446Strhodes		key = isc_mem_get(mctx, sizeof(*key));
691135446Strhodes		if (key == NULL)
692135446Strhodes			goto cleanup;
693135446Strhodes		key->keyname = newstr;
694135446Strhodes		key->secret.base = NULL;
695135446Strhodes		key->secret.length = 0;
696135446Strhodes		ISC_LINK_INIT(key, link);
697135446Strhodes		ISC_LIST_APPEND(*keyids, key, link);
698135446Strhodes		newstr = NULL;
699135446Strhodes	}
700135446Strhodes	return (ISC_R_SUCCESS);
701135446Strhodes
702135446Strhodes cleanup:
703135446Strhodes	if (newstr != NULL)
704135446Strhodes		isc_mem_free(mctx, newstr);
705135446Strhodes	free_controlkeylist(keyids, mctx);
706135446Strhodes	return (ISC_R_NOMEMORY);
707135446Strhodes}
708135446Strhodes
709135446Strhodesstatic void
710165071Sdougbregister_keys(const cfg_obj_t *control, const cfg_obj_t *keylist,
711135446Strhodes	      controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext)
712135446Strhodes{
713135446Strhodes	controlkey_t *keyid, *next;
714165071Sdougb	const cfg_obj_t *keydef;
715135446Strhodes	char secret[1024];
716135446Strhodes	isc_buffer_t b;
717135446Strhodes	isc_result_t result;
718135446Strhodes
719135446Strhodes	/*
720135446Strhodes	 * Find the keys corresponding to the keyids used by this listener.
721135446Strhodes	 */
722135446Strhodes	for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) {
723135446Strhodes		next = ISC_LIST_NEXT(keyid, link);
724135446Strhodes
725135446Strhodes		result = cfgkeylist_find(keylist, keyid->keyname, &keydef);
726135446Strhodes		if (result != ISC_R_SUCCESS) {
727135446Strhodes			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
728135446Strhodes				    "couldn't find key '%s' for use with "
729135446Strhodes				    "command channel %s",
730135446Strhodes				    keyid->keyname, socktext);
731135446Strhodes			ISC_LIST_UNLINK(*keyids, keyid, link);
732135446Strhodes			free_controlkey(keyid, mctx);
733135446Strhodes		} else {
734165071Sdougb			const cfg_obj_t *algobj = NULL;
735165071Sdougb			const cfg_obj_t *secretobj = NULL;
736165071Sdougb			const char *algstr = NULL;
737165071Sdougb			const char *secretstr = NULL;
738135446Strhodes
739135446Strhodes			(void)cfg_map_get(keydef, "algorithm", &algobj);
740135446Strhodes			(void)cfg_map_get(keydef, "secret", &secretobj);
741135446Strhodes			INSIST(algobj != NULL && secretobj != NULL);
742135446Strhodes
743135446Strhodes			algstr = cfg_obj_asstring(algobj);
744135446Strhodes			secretstr = cfg_obj_asstring(secretobj);
745135446Strhodes
746170222Sdougb			if (ns_config_getkeyalgorithm(algstr, NULL, NULL) !=
747135446Strhodes			    ISC_R_SUCCESS)
748135446Strhodes			{
749135446Strhodes				cfg_obj_log(control, ns_g_lctx,
750135446Strhodes					    ISC_LOG_WARNING,
751135446Strhodes					    "unsupported algorithm '%s' in "
752135446Strhodes					    "key '%s' for use with command "
753135446Strhodes					    "channel %s",
754135446Strhodes					    algstr, keyid->keyname, socktext);
755135446Strhodes				ISC_LIST_UNLINK(*keyids, keyid, link);
756135446Strhodes				free_controlkey(keyid, mctx);
757135446Strhodes				continue;
758135446Strhodes			}
759135446Strhodes
760135446Strhodes			isc_buffer_init(&b, secret, sizeof(secret));
761135446Strhodes			result = isc_base64_decodestring(secretstr, &b);
762135446Strhodes
763135446Strhodes			if (result != ISC_R_SUCCESS) {
764135446Strhodes				cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
765135446Strhodes					    "secret for key '%s' on "
766135446Strhodes					    "command channel %s: %s",
767135446Strhodes					    keyid->keyname, socktext,
768135446Strhodes					    isc_result_totext(result));
769135446Strhodes				ISC_LIST_UNLINK(*keyids, keyid, link);
770135446Strhodes				free_controlkey(keyid, mctx);
771135446Strhodes				continue;
772135446Strhodes			}
773135446Strhodes
774135446Strhodes			keyid->secret.length = isc_buffer_usedlength(&b);
775135446Strhodes			keyid->secret.base = isc_mem_get(mctx,
776135446Strhodes							 keyid->secret.length);
777135446Strhodes			if (keyid->secret.base == NULL) {
778135446Strhodes				cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
779135446Strhodes					   "couldn't register key '%s': "
780135446Strhodes					   "out of memory", keyid->keyname);
781135446Strhodes				ISC_LIST_UNLINK(*keyids, keyid, link);
782135446Strhodes				free_controlkey(keyid, mctx);
783135446Strhodes				break;
784135446Strhodes			}
785135446Strhodes			memcpy(keyid->secret.base, isc_buffer_base(&b),
786135446Strhodes			       keyid->secret.length);
787135446Strhodes		}
788135446Strhodes	}
789135446Strhodes}
790135446Strhodes
791135446Strhodes#define CHECK(x) \
792135446Strhodes	do { \
793135446Strhodes		 result = (x); \
794135446Strhodes		 if (result != ISC_R_SUCCESS) \
795135446Strhodes			goto cleanup; \
796135446Strhodes	} while (0)
797186462Sdougb
798135446Strhodesstatic isc_result_t
799135446Strhodesget_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) {
800135446Strhodes	isc_result_t result;
801135446Strhodes	cfg_parser_t *pctx = NULL;
802135446Strhodes	cfg_obj_t *config = NULL;
803165071Sdougb	const cfg_obj_t *key = NULL;
804165071Sdougb	const cfg_obj_t *algobj = NULL;
805165071Sdougb	const cfg_obj_t *secretobj = NULL;
806165071Sdougb	const char *algstr = NULL;
807165071Sdougb	const char *secretstr = NULL;
808135446Strhodes	controlkey_t *keyid = NULL;
809135446Strhodes	char secret[1024];
810135446Strhodes	isc_buffer_t b;
811135446Strhodes
812135446Strhodes	CHECK(cfg_parser_create(mctx, ns_g_lctx, &pctx));
813135446Strhodes	CHECK(cfg_parse_file(pctx, ns_g_keyfile, &cfg_type_rndckey, &config));
814135446Strhodes	CHECK(cfg_map_get(config, "key", &key));
815135446Strhodes
816135446Strhodes	keyid = isc_mem_get(mctx, sizeof(*keyid));
817186462Sdougb	if (keyid == NULL)
818135446Strhodes		CHECK(ISC_R_NOMEMORY);
819135446Strhodes	keyid->keyname = isc_mem_strdup(mctx,
820135446Strhodes					cfg_obj_asstring(cfg_map_getname(key)));
821135446Strhodes	keyid->secret.base = NULL;
822135446Strhodes	keyid->secret.length = 0;
823135446Strhodes	ISC_LINK_INIT(keyid, link);
824186462Sdougb	if (keyid->keyname == NULL)
825135446Strhodes		CHECK(ISC_R_NOMEMORY);
826135446Strhodes
827135446Strhodes	CHECK(bind9_check_key(key, ns_g_lctx));
828135446Strhodes
829135446Strhodes	(void)cfg_map_get(key, "algorithm", &algobj);
830135446Strhodes	(void)cfg_map_get(key, "secret", &secretobj);
831135446Strhodes	INSIST(algobj != NULL && secretobj != NULL);
832135446Strhodes
833135446Strhodes	algstr = cfg_obj_asstring(algobj);
834135446Strhodes	secretstr = cfg_obj_asstring(secretobj);
835135446Strhodes
836170222Sdougb	if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != ISC_R_SUCCESS) {
837135446Strhodes		cfg_obj_log(key, ns_g_lctx,
838135446Strhodes			    ISC_LOG_WARNING,
839135446Strhodes			    "unsupported algorithm '%s' in "
840135446Strhodes			    "key '%s' for use with command "
841135446Strhodes			    "channel",
842135446Strhodes			    algstr, keyid->keyname);
843135446Strhodes		goto cleanup;
844135446Strhodes	}
845135446Strhodes
846135446Strhodes	isc_buffer_init(&b, secret, sizeof(secret));
847135446Strhodes	result = isc_base64_decodestring(secretstr, &b);
848135446Strhodes
849135446Strhodes	if (result != ISC_R_SUCCESS) {
850135446Strhodes		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
851135446Strhodes			    "secret for key '%s' on command channel: %s",
852135446Strhodes			    keyid->keyname, isc_result_totext(result));
853225361Sdougb		goto cleanup;
854135446Strhodes	}
855135446Strhodes
856135446Strhodes	keyid->secret.length = isc_buffer_usedlength(&b);
857135446Strhodes	keyid->secret.base = isc_mem_get(mctx,
858135446Strhodes					 keyid->secret.length);
859135446Strhodes	if (keyid->secret.base == NULL) {
860135446Strhodes		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
861135446Strhodes			   "couldn't register key '%s': "
862135446Strhodes			   "out of memory", keyid->keyname);
863135446Strhodes		CHECK(ISC_R_NOMEMORY);
864135446Strhodes	}
865135446Strhodes	memcpy(keyid->secret.base, isc_buffer_base(&b),
866135446Strhodes	       keyid->secret.length);
867135446Strhodes	ISC_LIST_APPEND(*keyids, keyid, link);
868135446Strhodes	keyid = NULL;
869135446Strhodes	result = ISC_R_SUCCESS;
870135446Strhodes
871135446Strhodes  cleanup:
872135446Strhodes	if (keyid != NULL)
873135446Strhodes		free_controlkey(keyid, mctx);
874135446Strhodes	if (config != NULL)
875135446Strhodes		cfg_obj_destroy(pctx, &config);
876135446Strhodes	if (pctx != NULL)
877135446Strhodes		cfg_parser_destroy(&pctx);
878135446Strhodes	return (result);
879135446Strhodes}
880186462Sdougb
881135446Strhodes/*
882135446Strhodes * Ensures that both '*global_keylistp' and '*control_keylistp' are
883135446Strhodes * valid or both are NULL.
884135446Strhodes */
885135446Strhodesstatic void
886165071Sdougbget_key_info(const cfg_obj_t *config, const cfg_obj_t *control,
887165071Sdougb	     const cfg_obj_t **global_keylistp,
888165071Sdougb	     const cfg_obj_t **control_keylistp)
889135446Strhodes{
890135446Strhodes	isc_result_t result;
891165071Sdougb	const cfg_obj_t *control_keylist = NULL;
892165071Sdougb	const cfg_obj_t *global_keylist = NULL;
893135446Strhodes
894135446Strhodes	REQUIRE(global_keylistp != NULL && *global_keylistp == NULL);
895135446Strhodes	REQUIRE(control_keylistp != NULL && *control_keylistp == NULL);
896135446Strhodes
897135446Strhodes	control_keylist = cfg_tuple_get(control, "keys");
898135446Strhodes
899135446Strhodes	if (!cfg_obj_isvoid(control_keylist) &&
900135446Strhodes	    cfg_list_first(control_keylist) != NULL) {
901135446Strhodes		result = cfg_map_get(config, "key", &global_keylist);
902135446Strhodes
903135446Strhodes		if (result == ISC_R_SUCCESS) {
904135446Strhodes			*global_keylistp = global_keylist;
905135446Strhodes			*control_keylistp = control_keylist;
906135446Strhodes		}
907135446Strhodes	}
908135446Strhodes}
909135446Strhodes
910135446Strhodesstatic void
911165071Sdougbupdate_listener(ns_controls_t *cp, controllistener_t **listenerp,
912165071Sdougb		const cfg_obj_t *control, const cfg_obj_t *config,
913170222Sdougb		isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
914186462Sdougb		const char *socktext, isc_sockettype_t type)
915135446Strhodes{
916135446Strhodes	controllistener_t *listener;
917165071Sdougb	const cfg_obj_t *allow;
918165071Sdougb	const cfg_obj_t *global_keylist = NULL;
919165071Sdougb	const cfg_obj_t *control_keylist = NULL;
920135446Strhodes	dns_acl_t *new_acl = NULL;
921135446Strhodes	controlkeylist_t keys;
922135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
923135446Strhodes
924135446Strhodes	for (listener = ISC_LIST_HEAD(cp->listeners);
925135446Strhodes	     listener != NULL;
926135446Strhodes	     listener = ISC_LIST_NEXT(listener, link))
927135446Strhodes		if (isc_sockaddr_equal(addr, &listener->address))
928135446Strhodes			break;
929135446Strhodes
930135446Strhodes	if (listener == NULL) {
931135446Strhodes		*listenerp = NULL;
932135446Strhodes		return;
933135446Strhodes	}
934186462Sdougb
935135446Strhodes	/*
936135446Strhodes	 * There is already a listener for this sockaddr.
937135446Strhodes	 * Update the access list and key information.
938135446Strhodes	 *
939135446Strhodes	 * First try to deal with the key situation.  There are a few
940135446Strhodes	 * possibilities:
941135446Strhodes	 *  (a)	It had an explicit keylist and still has an explicit keylist.
942135446Strhodes	 *  (b)	It had an automagic key and now has an explicit keylist.
943135446Strhodes	 *  (c)	It had an explicit keylist and now needs an automagic key.
944135446Strhodes	 *  (d) It has an automagic key and still needs the automagic key.
945135446Strhodes	 *
946135446Strhodes	 * (c) and (d) are the annoying ones.  The caller needs to know
947135446Strhodes	 * that it should use the automagic configuration for key information
948135446Strhodes	 * in place of the named.conf configuration.
949135446Strhodes	 *
950135446Strhodes	 * XXXDCL There is one other hazard that has not been dealt with,
951135446Strhodes	 * the problem that if a key change is being caused by a control
952135446Strhodes	 * channel reload, then the response will be with the new key
953135446Strhodes	 * and not able to be decrypted by the client.
954135446Strhodes	 */
955135446Strhodes	if (control != NULL)
956135446Strhodes		get_key_info(config, control, &global_keylist,
957135446Strhodes			     &control_keylist);
958135446Strhodes
959135446Strhodes	if (control_keylist != NULL) {
960135446Strhodes		INSIST(global_keylist != NULL);
961135446Strhodes
962135446Strhodes		ISC_LIST_INIT(keys);
963135446Strhodes		result = controlkeylist_fromcfg(control_keylist,
964135446Strhodes						listener->mctx, &keys);
965135446Strhodes		if (result == ISC_R_SUCCESS) {
966135446Strhodes			free_controlkeylist(&listener->keys, listener->mctx);
967135446Strhodes			listener->keys = keys;
968135446Strhodes			register_keys(control, global_keylist, &listener->keys,
969135446Strhodes				      listener->mctx, socktext);
970135446Strhodes		}
971135446Strhodes	} else {
972135446Strhodes		free_controlkeylist(&listener->keys, listener->mctx);
973135446Strhodes		result = get_rndckey(listener->mctx, &listener->keys);
974135446Strhodes	}
975135446Strhodes
976165071Sdougb	if (result != ISC_R_SUCCESS && global_keylist != NULL) {
977135446Strhodes		/*
978135446Strhodes		 * This message might be a little misleading since the
979135446Strhodes		 * "new keys" might in fact be identical to the old ones,
980135446Strhodes		 * but tracking whether they are identical just for the
981135446Strhodes		 * sake of avoiding this message would be too much trouble.
982135446Strhodes		 */
983165071Sdougb		if (control != NULL)
984165071Sdougb			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
985165071Sdougb				    "couldn't install new keys for "
986165071Sdougb				    "command channel %s: %s",
987165071Sdougb				    socktext, isc_result_totext(result));
988165071Sdougb		else
989165071Sdougb			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
990165071Sdougb				      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
991165071Sdougb				      "couldn't install new keys for "
992165071Sdougb				      "command channel %s: %s",
993165071Sdougb				      socktext, isc_result_totext(result));
994165071Sdougb	}
995135446Strhodes
996135446Strhodes	/*
997135446Strhodes	 * Now, keep the old access list unless a new one can be made.
998135446Strhodes	 */
999170222Sdougb	if (control != NULL && type == isc_sockettype_tcp) {
1000135446Strhodes		allow = cfg_tuple_get(control, "allow");
1001170222Sdougb		result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1002193149Sdougb					    aclconfctx, listener->mctx, 0,
1003170222Sdougb					    &new_acl);
1004135446Strhodes	} else {
1005135446Strhodes		result = dns_acl_any(listener->mctx, &new_acl);
1006135446Strhodes	}
1007135446Strhodes
1008135446Strhodes	if (result == ISC_R_SUCCESS) {
1009135446Strhodes		dns_acl_detach(&listener->acl);
1010135446Strhodes		dns_acl_attach(new_acl, &listener->acl);
1011135446Strhodes		dns_acl_detach(&new_acl);
1012135446Strhodes		/* XXXDCL say the old acl is still used? */
1013165071Sdougb	} else if (control != NULL)
1014135446Strhodes		cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1015135446Strhodes			    "couldn't install new acl for "
1016135446Strhodes			    "command channel %s: %s",
1017135446Strhodes			    socktext, isc_result_totext(result));
1018165071Sdougb	else
1019165071Sdougb		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1020165071Sdougb			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
1021165071Sdougb			      "couldn't install new acl for "
1022165071Sdougb			      "command channel %s: %s",
1023165071Sdougb			      socktext, isc_result_totext(result));
1024135446Strhodes
1025170222Sdougb	if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
1026170222Sdougb		isc_uint32_t perm, owner, group;
1027170222Sdougb		perm  = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
1028170222Sdougb		owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner"));
1029170222Sdougb		group = cfg_obj_asuint32(cfg_tuple_get(control, "group"));
1030170222Sdougb		result = ISC_R_SUCCESS;
1031170222Sdougb		if (listener->perm != perm || listener->owner != owner ||
1032170222Sdougb		    listener->group != group)
1033170222Sdougb			result = isc_socket_permunix(&listener->address, perm,
1034170222Sdougb						     owner, group);
1035170222Sdougb		if (result == ISC_R_SUCCESS) {
1036170222Sdougb			listener->perm = perm;
1037170222Sdougb			listener->owner = owner;
1038170222Sdougb			listener->group = group;
1039170222Sdougb		} else if (control != NULL)
1040170222Sdougb			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1041170222Sdougb				    "couldn't update ownership/permission for "
1042170222Sdougb				    "command channel %s", socktext);
1043170222Sdougb	}
1044170222Sdougb
1045135446Strhodes	*listenerp = listener;
1046135446Strhodes}
1047135446Strhodes
1048135446Strhodesstatic void
1049135446Strhodesadd_listener(ns_controls_t *cp, controllistener_t **listenerp,
1050165071Sdougb	     const cfg_obj_t *control, const cfg_obj_t *config,
1051170222Sdougb	     isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
1052170222Sdougb	     const char *socktext, isc_sockettype_t type)
1053135446Strhodes{
1054135446Strhodes	isc_mem_t *mctx = cp->server->mctx;
1055135446Strhodes	controllistener_t *listener;
1056165071Sdougb	const cfg_obj_t *allow;
1057165071Sdougb	const cfg_obj_t *global_keylist = NULL;
1058165071Sdougb	const cfg_obj_t *control_keylist = NULL;
1059135446Strhodes	dns_acl_t *new_acl = NULL;
1060135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
1061135446Strhodes
1062135446Strhodes	listener = isc_mem_get(mctx, sizeof(*listener));
1063135446Strhodes	if (listener == NULL)
1064135446Strhodes		result = ISC_R_NOMEMORY;
1065135446Strhodes
1066135446Strhodes	if (result == ISC_R_SUCCESS) {
1067135446Strhodes		listener->controls = cp;
1068135446Strhodes		listener->mctx = mctx;
1069135446Strhodes		listener->task = cp->server->task;
1070135446Strhodes		listener->address = *addr;
1071135446Strhodes		listener->sock = NULL;
1072135446Strhodes		listener->listening = ISC_FALSE;
1073135446Strhodes		listener->exiting = ISC_FALSE;
1074135446Strhodes		listener->acl = NULL;
1075170222Sdougb		listener->type = type;
1076170222Sdougb		listener->perm = 0;
1077170222Sdougb		listener->owner = 0;
1078170222Sdougb		listener->group = 0;
1079135446Strhodes		ISC_LINK_INIT(listener, link);
1080135446Strhodes		ISC_LIST_INIT(listener->keys);
1081135446Strhodes		ISC_LIST_INIT(listener->connections);
1082135446Strhodes
1083135446Strhodes		/*
1084135446Strhodes		 * Make the acl.
1085135446Strhodes		 */
1086170222Sdougb		if (control != NULL && type == isc_sockettype_tcp) {
1087135446Strhodes			allow = cfg_tuple_get(control, "allow");
1088170222Sdougb			result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1089193149Sdougb						    aclconfctx, mctx, 0,
1090193149Sdougb						    &new_acl);
1091135446Strhodes		} else {
1092135446Strhodes			result = dns_acl_any(mctx, &new_acl);
1093135446Strhodes		}
1094135446Strhodes	}
1095135446Strhodes
1096135446Strhodes	if (result == ISC_R_SUCCESS) {
1097135446Strhodes		dns_acl_attach(new_acl, &listener->acl);
1098135446Strhodes		dns_acl_detach(&new_acl);
1099135446Strhodes
1100135446Strhodes		if (config != NULL)
1101135446Strhodes			get_key_info(config, control, &global_keylist,
1102135446Strhodes				     &control_keylist);
1103135446Strhodes
1104135446Strhodes		if (control_keylist != NULL) {
1105135446Strhodes			result = controlkeylist_fromcfg(control_keylist,
1106135446Strhodes							listener->mctx,
1107135446Strhodes							&listener->keys);
1108135446Strhodes			if (result == ISC_R_SUCCESS)
1109135446Strhodes				register_keys(control, global_keylist,
1110135446Strhodes					      &listener->keys,
1111135446Strhodes					      listener->mctx, socktext);
1112135446Strhodes		} else
1113135446Strhodes			result = get_rndckey(mctx, &listener->keys);
1114135446Strhodes
1115135446Strhodes		if (result != ISC_R_SUCCESS && control != NULL)
1116135446Strhodes			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1117135446Strhodes				    "couldn't install keys for "
1118135446Strhodes				    "command channel %s: %s",
1119135446Strhodes				    socktext, isc_result_totext(result));
1120135446Strhodes	}
1121135446Strhodes
1122135446Strhodes	if (result == ISC_R_SUCCESS) {
1123135446Strhodes		int pf = isc_sockaddr_pf(&listener->address);
1124135446Strhodes		if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
1125170222Sdougb#ifdef ISC_PLATFORM_HAVESYSUNH
1126170222Sdougb		    (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) ||
1127170222Sdougb#endif
1128135446Strhodes		    (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
1129135446Strhodes			result = ISC_R_FAMILYNOSUPPORT;
1130135446Strhodes	}
1131135446Strhodes
1132170222Sdougb	if (result == ISC_R_SUCCESS && type == isc_sockettype_unix)
1133170222Sdougb		isc_socket_cleanunix(&listener->address, ISC_FALSE);
1134170222Sdougb
1135135446Strhodes	if (result == ISC_R_SUCCESS)
1136135446Strhodes		result = isc_socket_create(ns_g_socketmgr,
1137135446Strhodes					   isc_sockaddr_pf(&listener->address),
1138170222Sdougb					   type, &listener->sock);
1139193149Sdougb	if (result == ISC_R_SUCCESS)
1140193149Sdougb		isc_socket_setname(listener->sock, "control", NULL);
1141135446Strhodes
1142234010Sdougb#ifndef ISC_ALLOW_MAPPED
1143135446Strhodes	if (result == ISC_R_SUCCESS)
1144234010Sdougb		isc_socket_ipv6only(listener->sock, ISC_TRUE);
1145234010Sdougb#endif
1146234010Sdougb
1147234010Sdougb	if (result == ISC_R_SUCCESS)
1148182645Sdougb		result = isc_socket_bind(listener->sock, &listener->address,
1149182645Sdougb					 ISC_SOCKET_REUSEADDRESS);
1150135446Strhodes
1151170222Sdougb	if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
1152170222Sdougb		listener->perm = cfg_obj_asuint32(cfg_tuple_get(control,
1153170222Sdougb								"perm"));
1154170222Sdougb		listener->owner = cfg_obj_asuint32(cfg_tuple_get(control,
1155170222Sdougb								 "owner"));
1156170222Sdougb		listener->group = cfg_obj_asuint32(cfg_tuple_get(control,
1157170222Sdougb								 "group"));
1158170222Sdougb		result = isc_socket_permunix(&listener->address, listener->perm,
1159170222Sdougb					     listener->owner, listener->group);
1160170222Sdougb	}
1161135446Strhodes	if (result == ISC_R_SUCCESS)
1162135446Strhodes		result = control_listen(listener);
1163135446Strhodes
1164135446Strhodes	if (result == ISC_R_SUCCESS)
1165135446Strhodes		result = control_accept(listener);
1166135446Strhodes
1167135446Strhodes	if (result == ISC_R_SUCCESS) {
1168135446Strhodes		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1169135446Strhodes			      NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
1170135446Strhodes			      "command channel listening on %s", socktext);
1171135446Strhodes		*listenerp = listener;
1172135446Strhodes
1173135446Strhodes	} else {
1174135446Strhodes		if (listener != NULL) {
1175135446Strhodes			listener->exiting = ISC_TRUE;
1176135446Strhodes			free_listener(listener);
1177135446Strhodes		}
1178135446Strhodes
1179135446Strhodes		if (control != NULL)
1180135446Strhodes			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1181135446Strhodes				    "couldn't add command channel %s: %s",
1182135446Strhodes				    socktext, isc_result_totext(result));
1183135446Strhodes		else
1184135446Strhodes			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1185135446Strhodes				      NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
1186135446Strhodes				      "couldn't add command channel %s: %s",
1187135446Strhodes				      socktext, isc_result_totext(result));
1188135446Strhodes
1189135446Strhodes		*listenerp = NULL;
1190135446Strhodes	}
1191135446Strhodes
1192135446Strhodes	/* XXXDCL return error results? fail hard? */
1193135446Strhodes}
1194135446Strhodes
1195135446Strhodesisc_result_t
1196165071Sdougbns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config,
1197170222Sdougb		      cfg_aclconfctx_t *aclconfctx)
1198135446Strhodes{
1199135446Strhodes	controllistener_t *listener;
1200135446Strhodes	controllistenerlist_t new_listeners;
1201165071Sdougb	const cfg_obj_t *controlslist = NULL;
1202165071Sdougb	const cfg_listelt_t *element, *element2;
1203135446Strhodes	char socktext[ISC_SOCKADDR_FORMATSIZE];
1204135446Strhodes
1205135446Strhodes	ISC_LIST_INIT(new_listeners);
1206135446Strhodes
1207135446Strhodes	/*
1208135446Strhodes	 * Get the list of named.conf 'controls' statements.
1209135446Strhodes	 */
1210135446Strhodes	(void)cfg_map_get(config, "controls", &controlslist);
1211135446Strhodes
1212135446Strhodes	/*
1213135446Strhodes	 * Run through the new control channel list, noting sockets that
1214135446Strhodes	 * are already being listened on and moving them to the new list.
1215135446Strhodes	 *
1216135446Strhodes	 * Identifying duplicate addr/port combinations is left to either
1217135446Strhodes	 * the underlying config code, or to the bind attempt getting an
1218135446Strhodes	 * address-in-use error.
1219135446Strhodes	 */
1220135446Strhodes	if (controlslist != NULL) {
1221135446Strhodes		for (element = cfg_list_first(controlslist);
1222135446Strhodes		     element != NULL;
1223135446Strhodes		     element = cfg_list_next(element)) {
1224165071Sdougb			const cfg_obj_t *controls;
1225165071Sdougb			const cfg_obj_t *inetcontrols = NULL;
1226135446Strhodes
1227135446Strhodes			controls = cfg_listelt_value(element);
1228135446Strhodes			(void)cfg_map_get(controls, "inet", &inetcontrols);
1229135446Strhodes			if (inetcontrols == NULL)
1230135446Strhodes				continue;
1231135446Strhodes
1232135446Strhodes			for (element2 = cfg_list_first(inetcontrols);
1233135446Strhodes			     element2 != NULL;
1234135446Strhodes			     element2 = cfg_list_next(element2)) {
1235165071Sdougb				const cfg_obj_t *control;
1236165071Sdougb				const cfg_obj_t *obj;
1237165071Sdougb				isc_sockaddr_t addr;
1238135446Strhodes
1239135446Strhodes				/*
1240135446Strhodes				 * The parser handles BIND 8 configuration file
1241135446Strhodes				 * syntax, so it allows unix phrases as well
1242135446Strhodes				 * inet phrases with no keys{} clause.
1243135446Strhodes				 */
1244135446Strhodes				control = cfg_listelt_value(element2);
1245135446Strhodes
1246135446Strhodes				obj = cfg_tuple_get(control, "address");
1247165071Sdougb				addr = *cfg_obj_assockaddr(obj);
1248165071Sdougb				if (isc_sockaddr_getport(&addr) == 0)
1249165071Sdougb					isc_sockaddr_setport(&addr,
1250135446Strhodes							     NS_CONTROL_PORT);
1251135446Strhodes
1252165071Sdougb				isc_sockaddr_format(&addr, socktext,
1253135446Strhodes						    sizeof(socktext));
1254135446Strhodes
1255135446Strhodes				isc_log_write(ns_g_lctx,
1256135446Strhodes					      NS_LOGCATEGORY_GENERAL,
1257135446Strhodes					      NS_LOGMODULE_CONTROL,
1258135446Strhodes					      ISC_LOG_DEBUG(9),
1259135446Strhodes					      "processing control channel %s",
1260135446Strhodes					      socktext);
1261135446Strhodes
1262135446Strhodes				update_listener(cp, &listener, control, config,
1263170222Sdougb						&addr, aclconfctx, socktext,
1264170222Sdougb						isc_sockettype_tcp);
1265135446Strhodes
1266135446Strhodes				if (listener != NULL)
1267135446Strhodes					/*
1268135446Strhodes					 * Remove the listener from the old
1269135446Strhodes					 * list, so it won't be shut down.
1270135446Strhodes					 */
1271135446Strhodes					ISC_LIST_UNLINK(cp->listeners,
1272135446Strhodes							listener, link);
1273135446Strhodes				else
1274135446Strhodes					/*
1275135446Strhodes					 * This is a new listener.
1276135446Strhodes					 */
1277135446Strhodes					add_listener(cp, &listener, control,
1278165071Sdougb						     config, &addr, aclconfctx,
1279170222Sdougb						     socktext,
1280170222Sdougb						     isc_sockettype_tcp);
1281135446Strhodes
1282135446Strhodes				if (listener != NULL)
1283135446Strhodes					ISC_LIST_APPEND(new_listeners,
1284135446Strhodes							listener, link);
1285135446Strhodes			}
1286135446Strhodes		}
1287170222Sdougb		for (element = cfg_list_first(controlslist);
1288170222Sdougb		     element != NULL;
1289170222Sdougb		     element = cfg_list_next(element)) {
1290170222Sdougb			const cfg_obj_t *controls;
1291170222Sdougb			const cfg_obj_t *unixcontrols = NULL;
1292170222Sdougb
1293170222Sdougb			controls = cfg_listelt_value(element);
1294170222Sdougb			(void)cfg_map_get(controls, "unix", &unixcontrols);
1295170222Sdougb			if (unixcontrols == NULL)
1296170222Sdougb				continue;
1297170222Sdougb
1298170222Sdougb			for (element2 = cfg_list_first(unixcontrols);
1299170222Sdougb			     element2 != NULL;
1300170222Sdougb			     element2 = cfg_list_next(element2)) {
1301170222Sdougb				const cfg_obj_t *control;
1302170222Sdougb				const cfg_obj_t *path;
1303170222Sdougb				isc_sockaddr_t addr;
1304170222Sdougb				isc_result_t result;
1305170222Sdougb
1306170222Sdougb				/*
1307170222Sdougb				 * The parser handles BIND 8 configuration file
1308170222Sdougb				 * syntax, so it allows unix phrases as well
1309170222Sdougb				 * inet phrases with no keys{} clause.
1310170222Sdougb				 */
1311170222Sdougb				control = cfg_listelt_value(element2);
1312170222Sdougb
1313170222Sdougb				path = cfg_tuple_get(control, "path");
1314170222Sdougb				result = isc_sockaddr_frompath(&addr,
1315170222Sdougb						      cfg_obj_asstring(path));
1316170222Sdougb				if (result != ISC_R_SUCCESS) {
1317170222Sdougb					isc_log_write(ns_g_lctx,
1318170222Sdougb					      NS_LOGCATEGORY_GENERAL,
1319170222Sdougb					      NS_LOGMODULE_CONTROL,
1320170222Sdougb					      ISC_LOG_DEBUG(9),
1321170222Sdougb					      "control channel '%s': %s",
1322170222Sdougb					      cfg_obj_asstring(path),
1323170222Sdougb					      isc_result_totext(result));
1324170222Sdougb					continue;
1325170222Sdougb				}
1326170222Sdougb
1327170222Sdougb				isc_log_write(ns_g_lctx,
1328170222Sdougb					      NS_LOGCATEGORY_GENERAL,
1329170222Sdougb					      NS_LOGMODULE_CONTROL,
1330170222Sdougb					      ISC_LOG_DEBUG(9),
1331170222Sdougb					      "processing control channel '%s'",
1332170222Sdougb					      cfg_obj_asstring(path));
1333170222Sdougb
1334170222Sdougb				update_listener(cp, &listener, control, config,
1335170222Sdougb						&addr, aclconfctx,
1336186462Sdougb						cfg_obj_asstring(path),
1337170222Sdougb						isc_sockettype_unix);
1338170222Sdougb
1339170222Sdougb				if (listener != NULL)
1340170222Sdougb					/*
1341170222Sdougb					 * Remove the listener from the old
1342170222Sdougb					 * list, so it won't be shut down.
1343170222Sdougb					 */
1344170222Sdougb					ISC_LIST_UNLINK(cp->listeners,
1345170222Sdougb							listener, link);
1346170222Sdougb				else
1347170222Sdougb					/*
1348170222Sdougb					 * This is a new listener.
1349170222Sdougb					 */
1350170222Sdougb					add_listener(cp, &listener, control,
1351170222Sdougb						     config, &addr, aclconfctx,
1352170222Sdougb						     cfg_obj_asstring(path),
1353170222Sdougb						     isc_sockettype_unix);
1354170222Sdougb
1355170222Sdougb				if (listener != NULL)
1356170222Sdougb					ISC_LIST_APPEND(new_listeners,
1357170222Sdougb							listener, link);
1358170222Sdougb			}
1359170222Sdougb		}
1360135446Strhodes	} else {
1361135446Strhodes		int i;
1362135446Strhodes
1363135446Strhodes		for (i = 0; i < 2; i++) {
1364135446Strhodes			isc_sockaddr_t addr;
1365135446Strhodes
1366135446Strhodes			if (i == 0) {
1367135446Strhodes				struct in_addr localhost;
1368135446Strhodes
1369135446Strhodes				if (isc_net_probeipv4() != ISC_R_SUCCESS)
1370135446Strhodes					continue;
1371135446Strhodes				localhost.s_addr = htonl(INADDR_LOOPBACK);
1372135446Strhodes				isc_sockaddr_fromin(&addr, &localhost, 0);
1373135446Strhodes			} else {
1374135446Strhodes				if (isc_net_probeipv6() != ISC_R_SUCCESS)
1375135446Strhodes					continue;
1376135446Strhodes				isc_sockaddr_fromin6(&addr,
1377135446Strhodes						     &in6addr_loopback, 0);
1378135446Strhodes			}
1379135446Strhodes			isc_sockaddr_setport(&addr, NS_CONTROL_PORT);
1380135446Strhodes
1381135446Strhodes			isc_sockaddr_format(&addr, socktext, sizeof(socktext));
1382186462Sdougb
1383135446Strhodes			update_listener(cp, &listener, NULL, NULL,
1384170222Sdougb					&addr, NULL, socktext,
1385186462Sdougb					isc_sockettype_tcp);
1386135446Strhodes
1387135446Strhodes			if (listener != NULL)
1388135446Strhodes				/*
1389135446Strhodes				 * Remove the listener from the old
1390135446Strhodes				 * list, so it won't be shut down.
1391135446Strhodes				 */
1392135446Strhodes				ISC_LIST_UNLINK(cp->listeners,
1393135446Strhodes						listener, link);
1394135446Strhodes			else
1395135446Strhodes				/*
1396135446Strhodes				 * This is a new listener.
1397135446Strhodes				 */
1398135446Strhodes				add_listener(cp, &listener, NULL, NULL,
1399170222Sdougb					     &addr, NULL, socktext,
1400170222Sdougb					     isc_sockettype_tcp);
1401135446Strhodes
1402135446Strhodes			if (listener != NULL)
1403135446Strhodes				ISC_LIST_APPEND(new_listeners,
1404135446Strhodes						listener, link);
1405135446Strhodes		}
1406135446Strhodes	}
1407135446Strhodes
1408135446Strhodes	/*
1409135446Strhodes	 * ns_control_shutdown() will stop whatever is on the global
1410135446Strhodes	 * listeners list, which currently only has whatever sockaddrs
1411135446Strhodes	 * were in the previous configuration (if any) that do not
1412135446Strhodes	 * remain in the current configuration.
1413135446Strhodes	 */
1414135446Strhodes	controls_shutdown(cp);
1415135446Strhodes
1416135446Strhodes	/*
1417135446Strhodes	 * Put all of the valid listeners on the listeners list.
1418135446Strhodes	 * Anything already on listeners in the process of shutting
1419135446Strhodes	 * down will be taken care of by listen_done().
1420135446Strhodes	 */
1421135446Strhodes	ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link);
1422135446Strhodes	return (ISC_R_SUCCESS);
1423135446Strhodes}
1424135446Strhodes
1425135446Strhodesisc_result_t
1426135446Strhodesns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp) {
1427135446Strhodes	isc_mem_t *mctx = server->mctx;
1428135446Strhodes	isc_result_t result;
1429135446Strhodes	ns_controls_t *controls = isc_mem_get(mctx, sizeof(*controls));
1430135446Strhodes
1431135446Strhodes	if (controls == NULL)
1432135446Strhodes		return (ISC_R_NOMEMORY);
1433135446Strhodes	controls->server = server;
1434135446Strhodes	ISC_LIST_INIT(controls->listeners);
1435135446Strhodes	controls->shuttingdown = ISC_FALSE;
1436135446Strhodes	controls->symtab = NULL;
1437135446Strhodes	result = isccc_cc_createsymtab(&controls->symtab);
1438135446Strhodes	if (result != ISC_R_SUCCESS) {
1439135446Strhodes		isc_mem_put(server->mctx, controls, sizeof(*controls));
1440135446Strhodes		return (result);
1441135446Strhodes	}
1442135446Strhodes	*ctrlsp = controls;
1443135446Strhodes	return (ISC_R_SUCCESS);
1444135446Strhodes}
1445135446Strhodes
1446135446Strhodesvoid
1447135446Strhodesns_controls_destroy(ns_controls_t **ctrlsp) {
1448135446Strhodes	ns_controls_t *controls = *ctrlsp;
1449135446Strhodes
1450135446Strhodes	REQUIRE(ISC_LIST_EMPTY(controls->listeners));
1451135446Strhodes
1452135446Strhodes	isccc_symtab_destroy(&controls->symtab);
1453135446Strhodes	isc_mem_put(controls->server->mctx, controls, sizeof(*controls));
1454135446Strhodes	*ctrlsp = NULL;
1455135446Strhodes}
1456