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