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