controlconf.c revision 222395
1/*
2 * Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2001-2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: controlconf.c,v 1.60 2008-07-23 23:27:54 marka Exp $ */
19
20/*! \file */
21
22#include <config.h>
23
24#include <isc/base64.h>
25#include <isc/buffer.h>
26#include <isc/event.h>
27#include <isc/mem.h>
28#include <isc/net.h>
29#include <isc/netaddr.h>
30#include <isc/random.h>
31#include <isc/result.h>
32#include <isc/stdtime.h>
33#include <isc/string.h>
34#include <isc/timer.h>
35#include <isc/util.h>
36
37#include <isccfg/namedconf.h>
38
39#include <bind9/check.h>
40
41#include <isccc/alist.h>
42#include <isccc/cc.h>
43#include <isccc/ccmsg.h>
44#include <isccc/events.h>
45#include <isccc/result.h>
46#include <isccc/sexpr.h>
47#include <isccc/symtab.h>
48#include <isccc/util.h>
49
50#include <dns/result.h>
51
52#include <named/config.h>
53#include <named/control.h>
54#include <named/log.h>
55#include <named/server.h>
56
57/*
58 * Note: Listeners and connections are not locked.  All event handlers are
59 * executed by the server task, and all callers of exported routines must
60 * be running under the server task.
61 */
62
63typedef struct controlkey controlkey_t;
64typedef ISC_LIST(controlkey_t) controlkeylist_t;
65
66typedef struct controlconnection controlconnection_t;
67typedef ISC_LIST(controlconnection_t) controlconnectionlist_t;
68
69typedef struct controllistener controllistener_t;
70typedef ISC_LIST(controllistener_t) controllistenerlist_t;
71
72struct controlkey {
73	char *				keyname;
74	isc_region_t			secret;
75	ISC_LINK(controlkey_t)		link;
76};
77
78struct controlconnection {
79	isc_socket_t *			sock;
80	isccc_ccmsg_t			ccmsg;
81	isc_boolean_t			ccmsg_valid;
82	isc_boolean_t			sending;
83	isc_timer_t *			timer;
84	unsigned char			buffer[2048];
85	controllistener_t *		listener;
86	isc_uint32_t			nonce;
87	ISC_LINK(controlconnection_t)	link;
88};
89
90struct controllistener {
91	ns_controls_t *			controls;
92	isc_mem_t *			mctx;
93	isc_task_t *			task;
94	isc_sockaddr_t			address;
95	isc_socket_t *			sock;
96	dns_acl_t *			acl;
97	isc_boolean_t			listening;
98	isc_boolean_t			exiting;
99	controlkeylist_t		keys;
100	controlconnectionlist_t		connections;
101	isc_sockettype_t		type;
102	isc_uint32_t			perm;
103	isc_uint32_t			owner;
104	isc_uint32_t			group;
105	ISC_LINK(controllistener_t)	link;
106};
107
108struct ns_controls {
109	ns_server_t			*server;
110	controllistenerlist_t 		listeners;
111	isc_boolean_t			shuttingdown;
112	isccc_symtab_t			*symtab;
113};
114
115static void control_newconn(isc_task_t *task, isc_event_t *event);
116static void control_recvmessage(isc_task_t *task, isc_event_t *event);
117
118#define CLOCKSKEW 300
119
120static void
121free_controlkey(controlkey_t *key, isc_mem_t *mctx) {
122	if (key->keyname != NULL)
123		isc_mem_free(mctx, key->keyname);
124	if (key->secret.base != NULL)
125		isc_mem_put(mctx, key->secret.base, key->secret.length);
126	isc_mem_put(mctx, key, sizeof(*key));
127}
128
129static void
130free_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) {
131	while (!ISC_LIST_EMPTY(*keylist)) {
132		controlkey_t *key = ISC_LIST_HEAD(*keylist);
133		ISC_LIST_UNLINK(*keylist, key, link);
134		free_controlkey(key, mctx);
135	}
136}
137
138static void
139free_listener(controllistener_t *listener) {
140	INSIST(listener->exiting);
141	INSIST(!listener->listening);
142	INSIST(ISC_LIST_EMPTY(listener->connections));
143
144	if (listener->sock != NULL)
145		isc_socket_detach(&listener->sock);
146
147	free_controlkeylist(&listener->keys, listener->mctx);
148
149	if (listener->acl != NULL)
150		dns_acl_detach(&listener->acl);
151
152	isc_mem_put(listener->mctx, listener, sizeof(*listener));
153}
154
155static void
156maybe_free_listener(controllistener_t *listener) {
157	if (listener->exiting &&
158	    !listener->listening &&
159	    ISC_LIST_EMPTY(listener->connections))
160		free_listener(listener);
161}
162
163static void
164maybe_free_connection(controlconnection_t *conn) {
165	controllistener_t *listener = conn->listener;
166
167	if (conn->timer != NULL)
168		isc_timer_detach(&conn->timer);
169
170	if (conn->ccmsg_valid) {
171		isccc_ccmsg_cancelread(&conn->ccmsg);
172		return;
173	}
174
175	if (conn->sending) {
176		isc_socket_cancel(conn->sock, listener->task,
177				  ISC_SOCKCANCEL_SEND);
178		return;
179	}
180
181	ISC_LIST_UNLINK(listener->connections, conn, link);
182	isc_mem_put(listener->mctx, conn, sizeof(*conn));
183}
184
185static void
186shutdown_listener(controllistener_t *listener) {
187	controlconnection_t *conn;
188	controlconnection_t *next;
189
190	if (!listener->exiting) {
191		char socktext[ISC_SOCKADDR_FORMATSIZE];
192
193		ISC_LIST_UNLINK(listener->controls->listeners, listener, link);
194
195		isc_sockaddr_format(&listener->address, socktext,
196				    sizeof(socktext));
197		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
198			      NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
199			      "stopping command channel on %s", socktext);
200		if (listener->type == isc_sockettype_unix)
201			isc_socket_cleanunix(&listener->address, ISC_TRUE);
202		listener->exiting = ISC_TRUE;
203	}
204
205	for (conn = ISC_LIST_HEAD(listener->connections);
206	     conn != NULL;
207	     conn = next)
208	{
209		next = ISC_LIST_NEXT(conn, link);
210		maybe_free_connection(conn);
211	}
212
213	if (listener->listening)
214		isc_socket_cancel(listener->sock, listener->task,
215				  ISC_SOCKCANCEL_ACCEPT);
216
217	maybe_free_listener(listener);
218}
219
220static isc_boolean_t
221address_ok(isc_sockaddr_t *sockaddr, dns_acl_t *acl) {
222	isc_netaddr_t netaddr;
223	isc_result_t result;
224	int match;
225
226	isc_netaddr_fromsockaddr(&netaddr, sockaddr);
227
228	result = dns_acl_match(&netaddr, NULL, acl,
229			       &ns_g_server->aclenv, &match, NULL);
230
231	if (result != ISC_R_SUCCESS || match <= 0)
232		return (ISC_FALSE);
233	else
234		return (ISC_TRUE);
235}
236
237static isc_result_t
238control_accept(controllistener_t *listener) {
239	isc_result_t result;
240	result = isc_socket_accept(listener->sock,
241				   listener->task,
242				   control_newconn, listener);
243	if (result != ISC_R_SUCCESS)
244		UNEXPECTED_ERROR(__FILE__, __LINE__,
245				 "isc_socket_accept() failed: %s",
246				 isc_result_totext(result));
247	else
248		listener->listening = ISC_TRUE;
249	return (result);
250}
251
252static isc_result_t
253control_listen(controllistener_t *listener) {
254	isc_result_t result;
255
256	result = isc_socket_listen(listener->sock, 0);
257	if (result != ISC_R_SUCCESS)
258		UNEXPECTED_ERROR(__FILE__, __LINE__,
259				 "isc_socket_listen() failed: %s",
260				 isc_result_totext(result));
261	return (result);
262}
263
264static void
265control_next(controllistener_t *listener) {
266	(void)control_accept(listener);
267}
268
269static void
270control_senddone(isc_task_t *task, isc_event_t *event) {
271	isc_socketevent_t *sevent = (isc_socketevent_t *) event;
272	controlconnection_t *conn = event->ev_arg;
273	controllistener_t *listener = conn->listener;
274	isc_socket_t *sock = (isc_socket_t *)sevent->ev_sender;
275	isc_result_t result;
276
277	REQUIRE(conn->sending);
278
279	UNUSED(task);
280
281	conn->sending = ISC_FALSE;
282
283	if (sevent->result != ISC_R_SUCCESS &&
284	    sevent->result != ISC_R_CANCELED)
285	{
286		char socktext[ISC_SOCKADDR_FORMATSIZE];
287		isc_sockaddr_t peeraddr;
288
289		(void)isc_socket_getpeername(sock, &peeraddr);
290		isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
291		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
292			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
293			      "error sending command response to %s: %s",
294			      socktext, isc_result_totext(sevent->result));
295	}
296	isc_event_free(&event);
297
298	result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task,
299					 control_recvmessage, conn);
300	if (result != ISC_R_SUCCESS) {
301		isc_socket_detach(&conn->sock);
302		maybe_free_connection(conn);
303		maybe_free_listener(listener);
304	}
305}
306
307static inline void
308log_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) {
309	char socktext[ISC_SOCKADDR_FORMATSIZE];
310	isc_sockaddr_t peeraddr;
311
312	(void)isc_socket_getpeername(ccmsg->sock, &peeraddr);
313	isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
314	isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
315		      NS_LOGMODULE_CONTROL, ISC_LOG_ERROR,
316		      "invalid command from %s: %s",
317		      socktext, isc_result_totext(result));
318}
319
320static void
321control_recvmessage(isc_task_t *task, isc_event_t *event) {
322	controlconnection_t *conn;
323	controllistener_t *listener;
324	controlkey_t *key;
325	isccc_sexpr_t *request = NULL;
326	isccc_sexpr_t *response = NULL;
327	isccc_region_t ccregion;
328	isccc_region_t secret;
329	isc_stdtime_t now;
330	isc_buffer_t b;
331	isc_region_t r;
332	isc_uint32_t len;
333	isc_buffer_t text;
334	char textarray[1024];
335	isc_result_t result;
336	isc_result_t eresult;
337	isccc_sexpr_t *_ctrl;
338	isccc_time_t sent;
339	isccc_time_t exp;
340	isc_uint32_t nonce;
341
342	REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG);
343
344	conn = event->ev_arg;
345	listener = conn->listener;
346	secret.rstart = NULL;
347
348	/* Is the server shutting down? */
349	if (listener->controls->shuttingdown)
350		goto cleanup;
351
352	if (conn->ccmsg.result != ISC_R_SUCCESS) {
353		if (conn->ccmsg.result != ISC_R_CANCELED &&
354		    conn->ccmsg.result != ISC_R_EOF)
355			log_invalid(&conn->ccmsg, conn->ccmsg.result);
356		goto cleanup;
357	}
358
359	request = NULL;
360
361	for (key = ISC_LIST_HEAD(listener->keys);
362	     key != NULL;
363	     key = ISC_LIST_NEXT(key, link))
364	{
365		ccregion.rstart = isc_buffer_base(&conn->ccmsg.buffer);
366		ccregion.rend = isc_buffer_used(&conn->ccmsg.buffer);
367		secret.rstart = isc_mem_get(listener->mctx, key->secret.length);
368		if (secret.rstart == NULL)
369			goto cleanup;
370		memcpy(secret.rstart, key->secret.base, key->secret.length);
371		secret.rend = secret.rstart + key->secret.length;
372		result = isccc_cc_fromwire(&ccregion, &request, &secret);
373		if (result == ISC_R_SUCCESS)
374			break;
375		isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
376		if (result == ISCCC_R_BADAUTH) {
377			/*
378			 * For some reason, request is non-NULL when
379			 * isccc_cc_fromwire returns ISCCC_R_BADAUTH.
380			 */
381			if (request != NULL)
382				isccc_sexpr_free(&request);
383		} else {
384			log_invalid(&conn->ccmsg, result);
385			goto cleanup;
386		}
387	}
388
389	if (key == NULL) {
390		log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
391		goto cleanup;
392	}
393
394	/* We shouldn't be getting a reply. */
395	if (isccc_cc_isreply(request)) {
396		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
397		goto cleanup_request;
398	}
399
400	isc_stdtime_get(&now);
401
402	/*
403	 * Limit exposure to replay attacks.
404	 */
405	_ctrl = isccc_alist_lookup(request, "_ctrl");
406	if (_ctrl == NULL) {
407		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
408		goto cleanup_request;
409	}
410
411	if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) {
412		if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) {
413			log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW);
414			goto cleanup_request;
415		}
416	} else {
417		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
418		goto cleanup_request;
419	}
420
421	/*
422	 * Expire messages that are too old.
423	 */
424	if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS &&
425	    now > exp) {
426		log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED);
427		goto cleanup_request;
428	}
429
430	/*
431	 * Duplicate suppression (required for UDP).
432	 */
433	isccc_cc_cleansymtab(listener->controls->symtab, now);
434	result = isccc_cc_checkdup(listener->controls->symtab, request, now);
435	if (result != ISC_R_SUCCESS) {
436		if (result == ISC_R_EXISTS)
437			result = ISCCC_R_DUPLICATE;
438		log_invalid(&conn->ccmsg, result);
439		goto cleanup_request;
440	}
441
442	if (conn->nonce != 0 &&
443	    (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS ||
444	     conn->nonce != nonce)) {
445		log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
446		goto cleanup_request;
447	}
448
449	/*
450	 * Establish nonce.
451	 */
452	while (conn->nonce == 0)
453		isc_random_get(&conn->nonce);
454
455	isc_buffer_init(&text, textarray, sizeof(textarray));
456	eresult = ns_control_docommand(request, &text);
457
458	result = isccc_cc_createresponse(request, now, now + 60, &response);
459	if (result != ISC_R_SUCCESS)
460		goto cleanup_request;
461	if (eresult != ISC_R_SUCCESS) {
462		isccc_sexpr_t *data;
463
464		data = isccc_alist_lookup(response, "_data");
465		if (data != NULL) {
466			const char *estr = isc_result_totext(eresult);
467			if (isccc_cc_definestring(data, "err", estr) == NULL)
468				goto cleanup_response;
469		}
470	}
471
472	if (isc_buffer_usedlength(&text) > 0) {
473		isccc_sexpr_t *data;
474
475		data = isccc_alist_lookup(response, "_data");
476		if (data != NULL) {
477			char *str = (char *)isc_buffer_base(&text);
478			if (isccc_cc_definestring(data, "text", str) == NULL)
479				goto cleanup_response;
480		}
481	}
482
483	_ctrl = isccc_alist_lookup(response, "_ctrl");
484	if (_ctrl == NULL ||
485	    isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL)
486		goto cleanup_response;
487
488	ccregion.rstart = conn->buffer + 4;
489	ccregion.rend = conn->buffer + sizeof(conn->buffer);
490	result = isccc_cc_towire(response, &ccregion, &secret);
491	if (result != ISC_R_SUCCESS)
492		goto cleanup_response;
493	isc_buffer_init(&b, conn->buffer, 4);
494	len = sizeof(conn->buffer) - REGION_SIZE(ccregion);
495	isc_buffer_putuint32(&b, len - 4);
496	r.base = conn->buffer;
497	r.length = len;
498
499	result = isc_socket_send(conn->sock, &r, task, control_senddone, conn);
500	if (result != ISC_R_SUCCESS)
501		goto cleanup_response;
502	conn->sending = ISC_TRUE;
503
504	isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
505	isccc_sexpr_free(&request);
506	isccc_sexpr_free(&response);
507	return;
508
509 cleanup_response:
510	isccc_sexpr_free(&response);
511
512 cleanup_request:
513	isccc_sexpr_free(&request);
514	isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
515
516 cleanup:
517	isc_socket_detach(&conn->sock);
518	isccc_ccmsg_invalidate(&conn->ccmsg);
519	conn->ccmsg_valid = ISC_FALSE;
520	maybe_free_connection(conn);
521	maybe_free_listener(listener);
522}
523
524static void
525control_timeout(isc_task_t *task, isc_event_t *event) {
526	controlconnection_t *conn = event->ev_arg;
527
528	UNUSED(task);
529
530	isc_timer_detach(&conn->timer);
531	maybe_free_connection(conn);
532
533	isc_event_free(&event);
534}
535
536static isc_result_t
537newconnection(controllistener_t *listener, isc_socket_t *sock) {
538	controlconnection_t *conn;
539	isc_interval_t interval;
540	isc_result_t result;
541
542	conn = isc_mem_get(listener->mctx, sizeof(*conn));
543	if (conn == NULL)
544		return (ISC_R_NOMEMORY);
545
546	conn->sock = sock;
547	isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg);
548	conn->ccmsg_valid = ISC_TRUE;
549	conn->sending = ISC_FALSE;
550	conn->timer = NULL;
551	isc_interval_set(&interval, 60, 0);
552	result = isc_timer_create(ns_g_timermgr, isc_timertype_once,
553				  NULL, &interval, listener->task,
554				  control_timeout, conn, &conn->timer);
555	if (result != ISC_R_SUCCESS)
556		goto cleanup;
557
558	conn->listener = listener;
559	conn->nonce = 0;
560	ISC_LINK_INIT(conn, link);
561
562	result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task,
563					 control_recvmessage, conn);
564	if (result != ISC_R_SUCCESS)
565		goto cleanup;
566	isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048);
567
568	ISC_LIST_APPEND(listener->connections, conn, link);
569	return (ISC_R_SUCCESS);
570
571 cleanup:
572	isccc_ccmsg_invalidate(&conn->ccmsg);
573	if (conn->timer != NULL)
574		isc_timer_detach(&conn->timer);
575	isc_mem_put(listener->mctx, conn, sizeof(*conn));
576	return (result);
577}
578
579static void
580control_newconn(isc_task_t *task, isc_event_t *event) {
581	isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
582	controllistener_t *listener = event->ev_arg;
583	isc_socket_t *sock;
584	isc_sockaddr_t peeraddr;
585	isc_result_t result;
586
587	UNUSED(task);
588
589	listener->listening = ISC_FALSE;
590
591	if (nevent->result != ISC_R_SUCCESS) {
592		if (nevent->result == ISC_R_CANCELED) {
593			shutdown_listener(listener);
594			goto cleanup;
595		}
596		goto restart;
597	}
598
599	sock = nevent->newsocket;
600	isc_socket_setname(sock, "control", NULL);
601	(void)isc_socket_getpeername(sock, &peeraddr);
602	if (listener->type == isc_sockettype_tcp &&
603	    !address_ok(&peeraddr, listener->acl)) {
604		char socktext[ISC_SOCKADDR_FORMATSIZE];
605		isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
606		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
607			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
608			      "rejected command channel message from %s",
609			      socktext);
610		isc_socket_detach(&sock);
611		goto restart;
612	}
613
614	result = newconnection(listener, sock);
615	if (result != ISC_R_SUCCESS) {
616		char socktext[ISC_SOCKADDR_FORMATSIZE];
617		isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
618		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
619			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
620			      "dropped command channel from %s: %s",
621			      socktext, isc_result_totext(result));
622		isc_socket_detach(&sock);
623		goto restart;
624	}
625
626 restart:
627	control_next(listener);
628 cleanup:
629	isc_event_free(&event);
630}
631
632static void
633controls_shutdown(ns_controls_t *controls) {
634	controllistener_t *listener;
635	controllistener_t *next;
636
637	for (listener = ISC_LIST_HEAD(controls->listeners);
638	     listener != NULL;
639	     listener = next)
640	{
641		/*
642		 * This is asynchronous.  As listeners shut down, they will
643		 * call their callbacks.
644		 */
645		next = ISC_LIST_NEXT(listener, link);
646		shutdown_listener(listener);
647	}
648}
649
650void
651ns_controls_shutdown(ns_controls_t *controls) {
652	controls_shutdown(controls);
653	controls->shuttingdown = ISC_TRUE;
654}
655
656static isc_result_t
657cfgkeylist_find(const cfg_obj_t *keylist, const char *keyname,
658		const cfg_obj_t **objp)
659{
660	const cfg_listelt_t *element;
661	const char *str;
662	const cfg_obj_t *obj;
663
664	for (element = cfg_list_first(keylist);
665	     element != NULL;
666	     element = cfg_list_next(element))
667	{
668		obj = cfg_listelt_value(element);
669		str = cfg_obj_asstring(cfg_map_getname(obj));
670		if (strcasecmp(str, keyname) == 0)
671			break;
672	}
673	if (element == NULL)
674		return (ISC_R_NOTFOUND);
675	obj = cfg_listelt_value(element);
676	*objp = obj;
677	return (ISC_R_SUCCESS);
678}
679
680static isc_result_t
681controlkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx,
682		       controlkeylist_t *keyids)
683{
684	const cfg_listelt_t *element;
685	char *newstr = NULL;
686	const char *str;
687	const cfg_obj_t *obj;
688	controlkey_t *key;
689
690	for (element = cfg_list_first(keylist);
691	     element != NULL;
692	     element = cfg_list_next(element))
693	{
694		obj = cfg_listelt_value(element);
695		str = cfg_obj_asstring(obj);
696		newstr = isc_mem_strdup(mctx, str);
697		if (newstr == NULL)
698			goto cleanup;
699		key = isc_mem_get(mctx, sizeof(*key));
700		if (key == NULL)
701			goto cleanup;
702		key->keyname = newstr;
703		key->secret.base = NULL;
704		key->secret.length = 0;
705		ISC_LINK_INIT(key, link);
706		ISC_LIST_APPEND(*keyids, key, link);
707		newstr = NULL;
708	}
709	return (ISC_R_SUCCESS);
710
711 cleanup:
712	if (newstr != NULL)
713		isc_mem_free(mctx, newstr);
714	free_controlkeylist(keyids, mctx);
715	return (ISC_R_NOMEMORY);
716}
717
718static void
719register_keys(const cfg_obj_t *control, const cfg_obj_t *keylist,
720	      controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext)
721{
722	controlkey_t *keyid, *next;
723	const cfg_obj_t *keydef;
724	char secret[1024];
725	isc_buffer_t b;
726	isc_result_t result;
727
728	/*
729	 * Find the keys corresponding to the keyids used by this listener.
730	 */
731	for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) {
732		next = ISC_LIST_NEXT(keyid, link);
733
734		result = cfgkeylist_find(keylist, keyid->keyname, &keydef);
735		if (result != ISC_R_SUCCESS) {
736			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
737				    "couldn't find key '%s' for use with "
738				    "command channel %s",
739				    keyid->keyname, socktext);
740			ISC_LIST_UNLINK(*keyids, keyid, link);
741			free_controlkey(keyid, mctx);
742		} else {
743			const cfg_obj_t *algobj = NULL;
744			const cfg_obj_t *secretobj = NULL;
745			const char *algstr = NULL;
746			const char *secretstr = NULL;
747
748			(void)cfg_map_get(keydef, "algorithm", &algobj);
749			(void)cfg_map_get(keydef, "secret", &secretobj);
750			INSIST(algobj != NULL && secretobj != NULL);
751
752			algstr = cfg_obj_asstring(algobj);
753			secretstr = cfg_obj_asstring(secretobj);
754
755			if (ns_config_getkeyalgorithm(algstr, NULL, NULL) !=
756			    ISC_R_SUCCESS)
757			{
758				cfg_obj_log(control, ns_g_lctx,
759					    ISC_LOG_WARNING,
760					    "unsupported algorithm '%s' in "
761					    "key '%s' for use with command "
762					    "channel %s",
763					    algstr, keyid->keyname, socktext);
764				ISC_LIST_UNLINK(*keyids, keyid, link);
765				free_controlkey(keyid, mctx);
766				continue;
767			}
768
769			isc_buffer_init(&b, secret, sizeof(secret));
770			result = isc_base64_decodestring(secretstr, &b);
771
772			if (result != ISC_R_SUCCESS) {
773				cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
774					    "secret for key '%s' on "
775					    "command channel %s: %s",
776					    keyid->keyname, socktext,
777					    isc_result_totext(result));
778				ISC_LIST_UNLINK(*keyids, keyid, link);
779				free_controlkey(keyid, mctx);
780				continue;
781			}
782
783			keyid->secret.length = isc_buffer_usedlength(&b);
784			keyid->secret.base = isc_mem_get(mctx,
785							 keyid->secret.length);
786			if (keyid->secret.base == NULL) {
787				cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
788					   "couldn't register key '%s': "
789					   "out of memory", keyid->keyname);
790				ISC_LIST_UNLINK(*keyids, keyid, link);
791				free_controlkey(keyid, mctx);
792				break;
793			}
794			memcpy(keyid->secret.base, isc_buffer_base(&b),
795			       keyid->secret.length);
796		}
797	}
798}
799
800#define CHECK(x) \
801	do { \
802		 result = (x); \
803		 if (result != ISC_R_SUCCESS) \
804			goto cleanup; \
805	} while (0)
806
807static isc_result_t
808get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) {
809	isc_result_t result;
810	cfg_parser_t *pctx = NULL;
811	cfg_obj_t *config = NULL;
812	const cfg_obj_t *key = NULL;
813	const cfg_obj_t *algobj = NULL;
814	const cfg_obj_t *secretobj = NULL;
815	const char *algstr = NULL;
816	const char *secretstr = NULL;
817	controlkey_t *keyid = NULL;
818	char secret[1024];
819	isc_buffer_t b;
820
821	CHECK(cfg_parser_create(mctx, ns_g_lctx, &pctx));
822	CHECK(cfg_parse_file(pctx, ns_g_keyfile, &cfg_type_rndckey, &config));
823	CHECK(cfg_map_get(config, "key", &key));
824
825	keyid = isc_mem_get(mctx, sizeof(*keyid));
826	if (keyid == NULL)
827		CHECK(ISC_R_NOMEMORY);
828	keyid->keyname = isc_mem_strdup(mctx,
829					cfg_obj_asstring(cfg_map_getname(key)));
830	keyid->secret.base = NULL;
831	keyid->secret.length = 0;
832	ISC_LINK_INIT(keyid, link);
833	if (keyid->keyname == NULL)
834		CHECK(ISC_R_NOMEMORY);
835
836	CHECK(bind9_check_key(key, ns_g_lctx));
837
838	(void)cfg_map_get(key, "algorithm", &algobj);
839	(void)cfg_map_get(key, "secret", &secretobj);
840	INSIST(algobj != NULL && secretobj != NULL);
841
842	algstr = cfg_obj_asstring(algobj);
843	secretstr = cfg_obj_asstring(secretobj);
844
845	if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != ISC_R_SUCCESS) {
846		cfg_obj_log(key, ns_g_lctx,
847			    ISC_LOG_WARNING,
848			    "unsupported algorithm '%s' in "
849			    "key '%s' for use with command "
850			    "channel",
851			    algstr, keyid->keyname);
852		goto cleanup;
853	}
854
855	isc_buffer_init(&b, secret, sizeof(secret));
856	result = isc_base64_decodestring(secretstr, &b);
857
858	if (result != ISC_R_SUCCESS) {
859		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
860			    "secret for key '%s' on command channel: %s",
861			    keyid->keyname, isc_result_totext(result));
862		CHECK(result);
863	}
864
865	keyid->secret.length = isc_buffer_usedlength(&b);
866	keyid->secret.base = isc_mem_get(mctx,
867					 keyid->secret.length);
868	if (keyid->secret.base == NULL) {
869		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
870			   "couldn't register key '%s': "
871			   "out of memory", keyid->keyname);
872		CHECK(ISC_R_NOMEMORY);
873	}
874	memcpy(keyid->secret.base, isc_buffer_base(&b),
875	       keyid->secret.length);
876	ISC_LIST_APPEND(*keyids, keyid, link);
877	keyid = NULL;
878	result = ISC_R_SUCCESS;
879
880  cleanup:
881	if (keyid != NULL)
882		free_controlkey(keyid, mctx);
883	if (config != NULL)
884		cfg_obj_destroy(pctx, &config);
885	if (pctx != NULL)
886		cfg_parser_destroy(&pctx);
887	return (result);
888}
889
890/*
891 * Ensures that both '*global_keylistp' and '*control_keylistp' are
892 * valid or both are NULL.
893 */
894static void
895get_key_info(const cfg_obj_t *config, const cfg_obj_t *control,
896	     const cfg_obj_t **global_keylistp,
897	     const cfg_obj_t **control_keylistp)
898{
899	isc_result_t result;
900	const cfg_obj_t *control_keylist = NULL;
901	const cfg_obj_t *global_keylist = NULL;
902
903	REQUIRE(global_keylistp != NULL && *global_keylistp == NULL);
904	REQUIRE(control_keylistp != NULL && *control_keylistp == NULL);
905
906	control_keylist = cfg_tuple_get(control, "keys");
907
908	if (!cfg_obj_isvoid(control_keylist) &&
909	    cfg_list_first(control_keylist) != NULL) {
910		result = cfg_map_get(config, "key", &global_keylist);
911
912		if (result == ISC_R_SUCCESS) {
913			*global_keylistp = global_keylist;
914			*control_keylistp = control_keylist;
915		}
916	}
917}
918
919static void
920update_listener(ns_controls_t *cp, controllistener_t **listenerp,
921		const cfg_obj_t *control, const cfg_obj_t *config,
922		isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
923		const char *socktext, isc_sockettype_t type)
924{
925	controllistener_t *listener;
926	const cfg_obj_t *allow;
927	const cfg_obj_t *global_keylist = NULL;
928	const cfg_obj_t *control_keylist = NULL;
929	dns_acl_t *new_acl = NULL;
930	controlkeylist_t keys;
931	isc_result_t result = ISC_R_SUCCESS;
932
933	for (listener = ISC_LIST_HEAD(cp->listeners);
934	     listener != NULL;
935	     listener = ISC_LIST_NEXT(listener, link))
936		if (isc_sockaddr_equal(addr, &listener->address))
937			break;
938
939	if (listener == NULL) {
940		*listenerp = NULL;
941		return;
942	}
943
944	/*
945	 * There is already a listener for this sockaddr.
946	 * Update the access list and key information.
947	 *
948	 * First try to deal with the key situation.  There are a few
949	 * possibilities:
950	 *  (a)	It had an explicit keylist and still has an explicit keylist.
951	 *  (b)	It had an automagic key and now has an explicit keylist.
952	 *  (c)	It had an explicit keylist and now needs an automagic key.
953	 *  (d) It has an automagic key and still needs the automagic key.
954	 *
955	 * (c) and (d) are the annoying ones.  The caller needs to know
956	 * that it should use the automagic configuration for key information
957	 * in place of the named.conf configuration.
958	 *
959	 * XXXDCL There is one other hazard that has not been dealt with,
960	 * the problem that if a key change is being caused by a control
961	 * channel reload, then the response will be with the new key
962	 * and not able to be decrypted by the client.
963	 */
964	if (control != NULL)
965		get_key_info(config, control, &global_keylist,
966			     &control_keylist);
967
968	if (control_keylist != NULL) {
969		INSIST(global_keylist != NULL);
970
971		ISC_LIST_INIT(keys);
972		result = controlkeylist_fromcfg(control_keylist,
973						listener->mctx, &keys);
974		if (result == ISC_R_SUCCESS) {
975			free_controlkeylist(&listener->keys, listener->mctx);
976			listener->keys = keys;
977			register_keys(control, global_keylist, &listener->keys,
978				      listener->mctx, socktext);
979		}
980	} else {
981		free_controlkeylist(&listener->keys, listener->mctx);
982		result = get_rndckey(listener->mctx, &listener->keys);
983	}
984
985	if (result != ISC_R_SUCCESS && global_keylist != NULL) {
986		/*
987		 * This message might be a little misleading since the
988		 * "new keys" might in fact be identical to the old ones,
989		 * but tracking whether they are identical just for the
990		 * sake of avoiding this message would be too much trouble.
991		 */
992		if (control != NULL)
993			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
994				    "couldn't install new keys for "
995				    "command channel %s: %s",
996				    socktext, isc_result_totext(result));
997		else
998			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
999				      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
1000				      "couldn't install new keys for "
1001				      "command channel %s: %s",
1002				      socktext, isc_result_totext(result));
1003	}
1004
1005	/*
1006	 * Now, keep the old access list unless a new one can be made.
1007	 */
1008	if (control != NULL && type == isc_sockettype_tcp) {
1009		allow = cfg_tuple_get(control, "allow");
1010		result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1011					    aclconfctx, listener->mctx, 0,
1012					    &new_acl);
1013	} else {
1014		result = dns_acl_any(listener->mctx, &new_acl);
1015	}
1016
1017	if (result == ISC_R_SUCCESS) {
1018		dns_acl_detach(&listener->acl);
1019		dns_acl_attach(new_acl, &listener->acl);
1020		dns_acl_detach(&new_acl);
1021		/* XXXDCL say the old acl is still used? */
1022	} else if (control != NULL)
1023		cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1024			    "couldn't install new acl for "
1025			    "command channel %s: %s",
1026			    socktext, isc_result_totext(result));
1027	else
1028		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1029			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
1030			      "couldn't install new acl for "
1031			      "command channel %s: %s",
1032			      socktext, isc_result_totext(result));
1033
1034	if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
1035		isc_uint32_t perm, owner, group;
1036		perm  = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
1037		owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner"));
1038		group = cfg_obj_asuint32(cfg_tuple_get(control, "group"));
1039		result = ISC_R_SUCCESS;
1040		if (listener->perm != perm || listener->owner != owner ||
1041		    listener->group != group)
1042			result = isc_socket_permunix(&listener->address, perm,
1043						     owner, group);
1044		if (result == ISC_R_SUCCESS) {
1045			listener->perm = perm;
1046			listener->owner = owner;
1047			listener->group = group;
1048		} else if (control != NULL)
1049			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1050				    "couldn't update ownership/permission for "
1051				    "command channel %s", socktext);
1052	}
1053
1054	*listenerp = listener;
1055}
1056
1057static void
1058add_listener(ns_controls_t *cp, controllistener_t **listenerp,
1059	     const cfg_obj_t *control, const cfg_obj_t *config,
1060	     isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
1061	     const char *socktext, isc_sockettype_t type)
1062{
1063	isc_mem_t *mctx = cp->server->mctx;
1064	controllistener_t *listener;
1065	const cfg_obj_t *allow;
1066	const cfg_obj_t *global_keylist = NULL;
1067	const cfg_obj_t *control_keylist = NULL;
1068	dns_acl_t *new_acl = NULL;
1069	isc_result_t result = ISC_R_SUCCESS;
1070
1071	listener = isc_mem_get(mctx, sizeof(*listener));
1072	if (listener == NULL)
1073		result = ISC_R_NOMEMORY;
1074
1075	if (result == ISC_R_SUCCESS) {
1076		listener->controls = cp;
1077		listener->mctx = mctx;
1078		listener->task = cp->server->task;
1079		listener->address = *addr;
1080		listener->sock = NULL;
1081		listener->listening = ISC_FALSE;
1082		listener->exiting = ISC_FALSE;
1083		listener->acl = NULL;
1084		listener->type = type;
1085		listener->perm = 0;
1086		listener->owner = 0;
1087		listener->group = 0;
1088		ISC_LINK_INIT(listener, link);
1089		ISC_LIST_INIT(listener->keys);
1090		ISC_LIST_INIT(listener->connections);
1091
1092		/*
1093		 * Make the acl.
1094		 */
1095		if (control != NULL && type == isc_sockettype_tcp) {
1096			allow = cfg_tuple_get(control, "allow");
1097			result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1098						    aclconfctx, mctx, 0,
1099						    &new_acl);
1100		} else {
1101			result = dns_acl_any(mctx, &new_acl);
1102		}
1103	}
1104
1105	if (result == ISC_R_SUCCESS) {
1106		dns_acl_attach(new_acl, &listener->acl);
1107		dns_acl_detach(&new_acl);
1108
1109		if (config != NULL)
1110			get_key_info(config, control, &global_keylist,
1111				     &control_keylist);
1112
1113		if (control_keylist != NULL) {
1114			result = controlkeylist_fromcfg(control_keylist,
1115							listener->mctx,
1116							&listener->keys);
1117			if (result == ISC_R_SUCCESS)
1118				register_keys(control, global_keylist,
1119					      &listener->keys,
1120					      listener->mctx, socktext);
1121		} else
1122			result = get_rndckey(mctx, &listener->keys);
1123
1124		if (result != ISC_R_SUCCESS && control != NULL)
1125			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1126				    "couldn't install keys for "
1127				    "command channel %s: %s",
1128				    socktext, isc_result_totext(result));
1129	}
1130
1131	if (result == ISC_R_SUCCESS) {
1132		int pf = isc_sockaddr_pf(&listener->address);
1133		if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
1134#ifdef ISC_PLATFORM_HAVESYSUNH
1135		    (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) ||
1136#endif
1137		    (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
1138			result = ISC_R_FAMILYNOSUPPORT;
1139	}
1140
1141	if (result == ISC_R_SUCCESS && type == isc_sockettype_unix)
1142		isc_socket_cleanunix(&listener->address, ISC_FALSE);
1143
1144	if (result == ISC_R_SUCCESS)
1145		result = isc_socket_create(ns_g_socketmgr,
1146					   isc_sockaddr_pf(&listener->address),
1147					   type, &listener->sock);
1148	if (result == ISC_R_SUCCESS)
1149		isc_socket_setname(listener->sock, "control", NULL);
1150
1151	if (result == ISC_R_SUCCESS)
1152		result = isc_socket_bind(listener->sock, &listener->address,
1153					 ISC_SOCKET_REUSEADDRESS);
1154
1155	if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
1156		listener->perm = cfg_obj_asuint32(cfg_tuple_get(control,
1157								"perm"));
1158		listener->owner = cfg_obj_asuint32(cfg_tuple_get(control,
1159								 "owner"));
1160		listener->group = cfg_obj_asuint32(cfg_tuple_get(control,
1161								 "group"));
1162		result = isc_socket_permunix(&listener->address, listener->perm,
1163					     listener->owner, listener->group);
1164	}
1165	if (result == ISC_R_SUCCESS)
1166		result = control_listen(listener);
1167
1168	if (result == ISC_R_SUCCESS)
1169		result = control_accept(listener);
1170
1171	if (result == ISC_R_SUCCESS) {
1172		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1173			      NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
1174			      "command channel listening on %s", socktext);
1175		*listenerp = listener;
1176
1177	} else {
1178		if (listener != NULL) {
1179			listener->exiting = ISC_TRUE;
1180			free_listener(listener);
1181		}
1182
1183		if (control != NULL)
1184			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1185				    "couldn't add command channel %s: %s",
1186				    socktext, isc_result_totext(result));
1187		else
1188			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1189				      NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
1190				      "couldn't add command channel %s: %s",
1191				      socktext, isc_result_totext(result));
1192
1193		*listenerp = NULL;
1194	}
1195
1196	/* XXXDCL return error results? fail hard? */
1197}
1198
1199isc_result_t
1200ns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config,
1201		      cfg_aclconfctx_t *aclconfctx)
1202{
1203	controllistener_t *listener;
1204	controllistenerlist_t new_listeners;
1205	const cfg_obj_t *controlslist = NULL;
1206	const cfg_listelt_t *element, *element2;
1207	char socktext[ISC_SOCKADDR_FORMATSIZE];
1208
1209	ISC_LIST_INIT(new_listeners);
1210
1211	/*
1212	 * Get the list of named.conf 'controls' statements.
1213	 */
1214	(void)cfg_map_get(config, "controls", &controlslist);
1215
1216	/*
1217	 * Run through the new control channel list, noting sockets that
1218	 * are already being listened on and moving them to the new list.
1219	 *
1220	 * Identifying duplicate addr/port combinations is left to either
1221	 * the underlying config code, or to the bind attempt getting an
1222	 * address-in-use error.
1223	 */
1224	if (controlslist != NULL) {
1225		for (element = cfg_list_first(controlslist);
1226		     element != NULL;
1227		     element = cfg_list_next(element)) {
1228			const cfg_obj_t *controls;
1229			const cfg_obj_t *inetcontrols = NULL;
1230
1231			controls = cfg_listelt_value(element);
1232			(void)cfg_map_get(controls, "inet", &inetcontrols);
1233			if (inetcontrols == NULL)
1234				continue;
1235
1236			for (element2 = cfg_list_first(inetcontrols);
1237			     element2 != NULL;
1238			     element2 = cfg_list_next(element2)) {
1239				const cfg_obj_t *control;
1240				const cfg_obj_t *obj;
1241				isc_sockaddr_t addr;
1242
1243				/*
1244				 * The parser handles BIND 8 configuration file
1245				 * syntax, so it allows unix phrases as well
1246				 * inet phrases with no keys{} clause.
1247				 */
1248				control = cfg_listelt_value(element2);
1249
1250				obj = cfg_tuple_get(control, "address");
1251				addr = *cfg_obj_assockaddr(obj);
1252				if (isc_sockaddr_getport(&addr) == 0)
1253					isc_sockaddr_setport(&addr,
1254							     NS_CONTROL_PORT);
1255
1256				isc_sockaddr_format(&addr, socktext,
1257						    sizeof(socktext));
1258
1259				isc_log_write(ns_g_lctx,
1260					      NS_LOGCATEGORY_GENERAL,
1261					      NS_LOGMODULE_CONTROL,
1262					      ISC_LOG_DEBUG(9),
1263					      "processing control channel %s",
1264					      socktext);
1265
1266				update_listener(cp, &listener, control, config,
1267						&addr, aclconfctx, socktext,
1268						isc_sockettype_tcp);
1269
1270				if (listener != NULL)
1271					/*
1272					 * Remove the listener from the old
1273					 * list, so it won't be shut down.
1274					 */
1275					ISC_LIST_UNLINK(cp->listeners,
1276							listener, link);
1277				else
1278					/*
1279					 * This is a new listener.
1280					 */
1281					add_listener(cp, &listener, control,
1282						     config, &addr, aclconfctx,
1283						     socktext,
1284						     isc_sockettype_tcp);
1285
1286				if (listener != NULL)
1287					ISC_LIST_APPEND(new_listeners,
1288							listener, link);
1289			}
1290		}
1291		for (element = cfg_list_first(controlslist);
1292		     element != NULL;
1293		     element = cfg_list_next(element)) {
1294			const cfg_obj_t *controls;
1295			const cfg_obj_t *unixcontrols = NULL;
1296
1297			controls = cfg_listelt_value(element);
1298			(void)cfg_map_get(controls, "unix", &unixcontrols);
1299			if (unixcontrols == NULL)
1300				continue;
1301
1302			for (element2 = cfg_list_first(unixcontrols);
1303			     element2 != NULL;
1304			     element2 = cfg_list_next(element2)) {
1305				const cfg_obj_t *control;
1306				const cfg_obj_t *path;
1307				isc_sockaddr_t addr;
1308				isc_result_t result;
1309
1310				/*
1311				 * The parser handles BIND 8 configuration file
1312				 * syntax, so it allows unix phrases as well
1313				 * inet phrases with no keys{} clause.
1314				 */
1315				control = cfg_listelt_value(element2);
1316
1317				path = cfg_tuple_get(control, "path");
1318				result = isc_sockaddr_frompath(&addr,
1319						      cfg_obj_asstring(path));
1320				if (result != ISC_R_SUCCESS) {
1321					isc_log_write(ns_g_lctx,
1322					      NS_LOGCATEGORY_GENERAL,
1323					      NS_LOGMODULE_CONTROL,
1324					      ISC_LOG_DEBUG(9),
1325					      "control channel '%s': %s",
1326					      cfg_obj_asstring(path),
1327					      isc_result_totext(result));
1328					continue;
1329				}
1330
1331				isc_log_write(ns_g_lctx,
1332					      NS_LOGCATEGORY_GENERAL,
1333					      NS_LOGMODULE_CONTROL,
1334					      ISC_LOG_DEBUG(9),
1335					      "processing control channel '%s'",
1336					      cfg_obj_asstring(path));
1337
1338				update_listener(cp, &listener, control, config,
1339						&addr, aclconfctx,
1340						cfg_obj_asstring(path),
1341						isc_sockettype_unix);
1342
1343				if (listener != NULL)
1344					/*
1345					 * Remove the listener from the old
1346					 * list, so it won't be shut down.
1347					 */
1348					ISC_LIST_UNLINK(cp->listeners,
1349							listener, link);
1350				else
1351					/*
1352					 * This is a new listener.
1353					 */
1354					add_listener(cp, &listener, control,
1355						     config, &addr, aclconfctx,
1356						     cfg_obj_asstring(path),
1357						     isc_sockettype_unix);
1358
1359				if (listener != NULL)
1360					ISC_LIST_APPEND(new_listeners,
1361							listener, link);
1362			}
1363		}
1364	} else {
1365		int i;
1366
1367		for (i = 0; i < 2; i++) {
1368			isc_sockaddr_t addr;
1369
1370			if (i == 0) {
1371				struct in_addr localhost;
1372
1373				if (isc_net_probeipv4() != ISC_R_SUCCESS)
1374					continue;
1375				localhost.s_addr = htonl(INADDR_LOOPBACK);
1376				isc_sockaddr_fromin(&addr, &localhost, 0);
1377			} else {
1378				if (isc_net_probeipv6() != ISC_R_SUCCESS)
1379					continue;
1380				isc_sockaddr_fromin6(&addr,
1381						     &in6addr_loopback, 0);
1382			}
1383			isc_sockaddr_setport(&addr, NS_CONTROL_PORT);
1384
1385			isc_sockaddr_format(&addr, socktext, sizeof(socktext));
1386
1387			update_listener(cp, &listener, NULL, NULL,
1388					&addr, NULL, socktext,
1389					isc_sockettype_tcp);
1390
1391			if (listener != NULL)
1392				/*
1393				 * Remove the listener from the old
1394				 * list, so it won't be shut down.
1395				 */
1396				ISC_LIST_UNLINK(cp->listeners,
1397						listener, link);
1398			else
1399				/*
1400				 * This is a new listener.
1401				 */
1402				add_listener(cp, &listener, NULL, NULL,
1403					     &addr, NULL, socktext,
1404					     isc_sockettype_tcp);
1405
1406			if (listener != NULL)
1407				ISC_LIST_APPEND(new_listeners,
1408						listener, link);
1409		}
1410	}
1411
1412	/*
1413	 * ns_control_shutdown() will stop whatever is on the global
1414	 * listeners list, which currently only has whatever sockaddrs
1415	 * were in the previous configuration (if any) that do not
1416	 * remain in the current configuration.
1417	 */
1418	controls_shutdown(cp);
1419
1420	/*
1421	 * Put all of the valid listeners on the listeners list.
1422	 * Anything already on listeners in the process of shutting
1423	 * down will be taken care of by listen_done().
1424	 */
1425	ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link);
1426	return (ISC_R_SUCCESS);
1427}
1428
1429isc_result_t
1430ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp) {
1431	isc_mem_t *mctx = server->mctx;
1432	isc_result_t result;
1433	ns_controls_t *controls = isc_mem_get(mctx, sizeof(*controls));
1434
1435	if (controls == NULL)
1436		return (ISC_R_NOMEMORY);
1437	controls->server = server;
1438	ISC_LIST_INIT(controls->listeners);
1439	controls->shuttingdown = ISC_FALSE;
1440	controls->symtab = NULL;
1441	result = isccc_cc_createsymtab(&controls->symtab);
1442	if (result != ISC_R_SUCCESS) {
1443		isc_mem_put(server->mctx, controls, sizeof(*controls));
1444		return (result);
1445	}
1446	*ctrlsp = controls;
1447	return (ISC_R_SUCCESS);
1448}
1449
1450void
1451ns_controls_destroy(ns_controls_t **ctrlsp) {
1452	ns_controls_t *controls = *ctrlsp;
1453
1454	REQUIRE(ISC_LIST_EMPTY(controls->listeners));
1455
1456	isccc_symtab_destroy(&controls->symtab);
1457	isc_mem_put(controls->server->mctx, controls, sizeof(*controls));
1458	*ctrlsp = NULL;
1459}
1460