controlconf.c revision 182645
1/*
2 * Copyright (C) 2004-2006, 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.40.18.10.40.3 2008/07/23 23:16:43 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		if (secret.rstart != NULL)
368			isc_mem_put(listener->mctx, secret.rstart,
369				    REGION_SIZE(secret));
370		secret.rstart = isc_mem_get(listener->mctx, key->secret.length);
371		if (secret.rstart == NULL)
372			goto cleanup;
373		memcpy(secret.rstart, key->secret.base, key->secret.length);
374		secret.rend = secret.rstart + key->secret.length;
375		result = isccc_cc_fromwire(&ccregion, &request, &secret);
376		if (result == ISC_R_SUCCESS)
377			break;
378		else if (result == ISCCC_R_BADAUTH) {
379			/*
380			 * For some reason, request is non-NULL when
381			 * isccc_cc_fromwire returns ISCCC_R_BADAUTH.
382			 */
383			if (request != NULL)
384				isccc_sexpr_free(&request);
385		} else {
386			log_invalid(&conn->ccmsg, result);
387			goto cleanup;
388		}
389	}
390
391	if (key == NULL) {
392		log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
393		goto cleanup;
394	}
395
396	/* We shouldn't be getting a reply. */
397	if (isccc_cc_isreply(request)) {
398		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
399		goto cleanup;
400	}
401
402	isc_stdtime_get(&now);
403
404	/*
405	 * Limit exposure to replay attacks.
406	 */
407	_ctrl = isccc_alist_lookup(request, "_ctrl");
408	if (_ctrl == NULL) {
409		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
410		goto cleanup;
411	}
412
413	if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) {
414		if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) {
415			log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW);
416			goto cleanup;
417		}
418	} else {
419		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
420		goto cleanup;
421	}
422
423	/*
424	 * Expire messages that are too old.
425	 */
426	if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS &&
427	    now > exp) {
428		log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED);
429		goto cleanup;
430	}
431
432	/*
433	 * Duplicate suppression (required for UDP).
434	 */
435	isccc_cc_cleansymtab(listener->controls->symtab, now);
436	result = isccc_cc_checkdup(listener->controls->symtab, request, now);
437	if (result != ISC_R_SUCCESS) {
438		if (result == ISC_R_EXISTS)
439                        result = ISCCC_R_DUPLICATE;
440		log_invalid(&conn->ccmsg, result);
441		goto cleanup;
442	}
443
444	if (conn->nonce != 0 &&
445	    (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS ||
446	     conn->nonce != nonce)) {
447		log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
448		goto cleanup;
449	}
450
451	/*
452	 * Establish nonce.
453	 */
454	while (conn->nonce == 0)
455		isc_random_get(&conn->nonce);
456
457	isc_buffer_init(&text, textarray, sizeof(textarray));
458	eresult = ns_control_docommand(request, &text);
459
460	result = isccc_cc_createresponse(request, now, now + 60, &response);
461	if (result != ISC_R_SUCCESS)
462		goto cleanup;
463	if (eresult != ISC_R_SUCCESS) {
464		isccc_sexpr_t *data;
465
466		data = isccc_alist_lookup(response, "_data");
467		if (data != NULL) {
468			const char *estr = isc_result_totext(eresult);
469			if (isccc_cc_definestring(data, "err", estr) == NULL)
470				goto cleanup;
471		}
472	}
473
474	if (isc_buffer_usedlength(&text) > 0) {
475		isccc_sexpr_t *data;
476
477		data = isccc_alist_lookup(response, "_data");
478		if (data != NULL) {
479			char *str = (char *)isc_buffer_base(&text);
480			if (isccc_cc_definestring(data, "text", str) == NULL)
481				goto cleanup;
482		}
483	}
484
485	_ctrl = isccc_alist_lookup(response, "_ctrl");
486	if (_ctrl == NULL ||
487	    isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL)
488		goto cleanup;
489
490	ccregion.rstart = conn->buffer + 4;
491	ccregion.rend = conn->buffer + sizeof(conn->buffer);
492	result = isccc_cc_towire(response, &ccregion, &secret);
493	if (result != ISC_R_SUCCESS)
494		goto cleanup;
495	isc_buffer_init(&b, conn->buffer, 4);
496	len = sizeof(conn->buffer) - REGION_SIZE(ccregion);
497	isc_buffer_putuint32(&b, len - 4);
498	r.base = conn->buffer;
499	r.length = len;
500
501	result = isc_socket_send(conn->sock, &r, task, control_senddone, conn);
502	if (result != ISC_R_SUCCESS)
503		goto cleanup;
504	conn->sending = ISC_TRUE;
505
506	if (secret.rstart != NULL)
507		isc_mem_put(listener->mctx, secret.rstart,
508			    REGION_SIZE(secret));
509	if (request != NULL)
510		isccc_sexpr_free(&request);
511	if (response != NULL)
512		isccc_sexpr_free(&response);
513	return;
514
515 cleanup:
516	if (secret.rstart != NULL)
517		isc_mem_put(listener->mctx, secret.rstart,
518			    REGION_SIZE(secret));
519	isc_socket_detach(&conn->sock);
520	isccc_ccmsg_invalidate(&conn->ccmsg);
521	conn->ccmsg_valid = ISC_FALSE;
522	maybe_free_connection(conn);
523	maybe_free_listener(listener);
524	if (request != NULL)
525		isccc_sexpr_free(&request);
526	if (response != NULL)
527		isccc_sexpr_free(&response);
528}
529
530static void
531control_timeout(isc_task_t *task, isc_event_t *event) {
532	controlconnection_t *conn = event->ev_arg;
533
534	UNUSED(task);
535
536	isc_timer_detach(&conn->timer);
537	maybe_free_connection(conn);
538
539	isc_event_free(&event);
540}
541
542static isc_result_t
543newconnection(controllistener_t *listener, isc_socket_t *sock) {
544	controlconnection_t *conn;
545	isc_interval_t interval;
546	isc_result_t result;
547
548	conn = isc_mem_get(listener->mctx, sizeof(*conn));
549	if (conn == NULL)
550		return (ISC_R_NOMEMORY);
551
552	conn->sock = sock;
553	isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg);
554	conn->ccmsg_valid = ISC_TRUE;
555	conn->sending = ISC_FALSE;
556	conn->timer = NULL;
557	isc_interval_set(&interval, 60, 0);
558	result = isc_timer_create(ns_g_timermgr, isc_timertype_once,
559				  NULL, &interval, listener->task,
560				  control_timeout, conn, &conn->timer);
561	if (result != ISC_R_SUCCESS)
562		goto cleanup;
563
564	conn->listener = listener;
565	conn->nonce = 0;
566	ISC_LINK_INIT(conn, link);
567
568	result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task,
569					 control_recvmessage, conn);
570	if (result != ISC_R_SUCCESS)
571		goto cleanup;
572	isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048);
573
574	ISC_LIST_APPEND(listener->connections, conn, link);
575	return (ISC_R_SUCCESS);
576
577 cleanup:
578	isccc_ccmsg_invalidate(&conn->ccmsg);
579	if (conn->timer != NULL)
580		isc_timer_detach(&conn->timer);
581	isc_mem_put(listener->mctx, conn, sizeof(*conn));
582	return (result);
583}
584
585static void
586control_newconn(isc_task_t *task, isc_event_t *event) {
587	isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
588	controllistener_t *listener = event->ev_arg;
589	isc_socket_t *sock;
590	isc_sockaddr_t peeraddr;
591	isc_result_t result;
592
593	UNUSED(task);
594
595	listener->listening = ISC_FALSE;
596
597	if (nevent->result != ISC_R_SUCCESS) {
598		if (nevent->result == ISC_R_CANCELED) {
599			shutdown_listener(listener);
600			goto cleanup;
601		}
602		goto restart;
603	}
604
605	sock = nevent->newsocket;
606	(void)isc_socket_getpeername(sock, &peeraddr);
607	if (listener->type == isc_sockettype_tcp &&
608	    !address_ok(&peeraddr, listener->acl)) {
609		char socktext[ISC_SOCKADDR_FORMATSIZE];
610		isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
611		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
612			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
613			      "rejected command channel message from %s",
614			      socktext);
615		isc_socket_detach(&sock);
616		goto restart;
617	}
618
619	result = newconnection(listener, sock);
620	if (result != ISC_R_SUCCESS) {
621		char socktext[ISC_SOCKADDR_FORMATSIZE];
622		isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
623		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
624			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
625			      "dropped command channel from %s: %s",
626			      socktext, isc_result_totext(result));
627		isc_socket_detach(&sock);
628		goto restart;
629	}
630
631 restart:
632	control_next(listener);
633 cleanup:
634	isc_event_free(&event);
635}
636
637static void
638controls_shutdown(ns_controls_t *controls) {
639	controllistener_t *listener;
640	controllistener_t *next;
641
642	for (listener = ISC_LIST_HEAD(controls->listeners);
643	     listener != NULL;
644	     listener = next)
645	{
646		/*
647		 * This is asynchronous.  As listeners shut down, they will
648		 * call their callbacks.
649		 */
650		next = ISC_LIST_NEXT(listener, link);
651		shutdown_listener(listener);
652	}
653}
654
655void
656ns_controls_shutdown(ns_controls_t *controls) {
657	controls_shutdown(controls);
658	controls->shuttingdown = ISC_TRUE;
659}
660
661static isc_result_t
662cfgkeylist_find(const cfg_obj_t *keylist, const char *keyname,
663	        const cfg_obj_t **objp)
664{
665	const cfg_listelt_t *element;
666	const char *str;
667	const cfg_obj_t *obj;
668
669	for (element = cfg_list_first(keylist);
670	     element != NULL;
671	     element = cfg_list_next(element))
672	{
673		obj = cfg_listelt_value(element);
674		str = cfg_obj_asstring(cfg_map_getname(obj));
675		if (strcasecmp(str, keyname) == 0)
676			break;
677	}
678	if (element == NULL)
679		return (ISC_R_NOTFOUND);
680	obj = cfg_listelt_value(element);
681	*objp = obj;
682	return (ISC_R_SUCCESS);
683}
684
685static isc_result_t
686controlkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx,
687		       controlkeylist_t *keyids)
688{
689	const cfg_listelt_t *element;
690	char *newstr = NULL;
691	const char *str;
692	const cfg_obj_t *obj;
693	controlkey_t *key;
694
695	for (element = cfg_list_first(keylist);
696	     element != NULL;
697	     element = cfg_list_next(element))
698	{
699		obj = cfg_listelt_value(element);
700		str = cfg_obj_asstring(obj);
701		newstr = isc_mem_strdup(mctx, str);
702		if (newstr == NULL)
703			goto cleanup;
704		key = isc_mem_get(mctx, sizeof(*key));
705		if (key == NULL)
706			goto cleanup;
707		key->keyname = newstr;
708		key->secret.base = NULL;
709		key->secret.length = 0;
710		ISC_LINK_INIT(key, link);
711		ISC_LIST_APPEND(*keyids, key, link);
712		newstr = NULL;
713	}
714	return (ISC_R_SUCCESS);
715
716 cleanup:
717	if (newstr != NULL)
718		isc_mem_free(mctx, newstr);
719	free_controlkeylist(keyids, mctx);
720	return (ISC_R_NOMEMORY);
721}
722
723static void
724register_keys(const cfg_obj_t *control, const cfg_obj_t *keylist,
725	      controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext)
726{
727	controlkey_t *keyid, *next;
728	const cfg_obj_t *keydef;
729	char secret[1024];
730	isc_buffer_t b;
731	isc_result_t result;
732
733	/*
734	 * Find the keys corresponding to the keyids used by this listener.
735	 */
736	for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) {
737		next = ISC_LIST_NEXT(keyid, link);
738
739		result = cfgkeylist_find(keylist, keyid->keyname, &keydef);
740		if (result != ISC_R_SUCCESS) {
741			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
742				    "couldn't find key '%s' for use with "
743				    "command channel %s",
744				    keyid->keyname, socktext);
745			ISC_LIST_UNLINK(*keyids, keyid, link);
746			free_controlkey(keyid, mctx);
747		} else {
748			const cfg_obj_t *algobj = NULL;
749			const cfg_obj_t *secretobj = NULL;
750			const char *algstr = NULL;
751			const char *secretstr = NULL;
752
753			(void)cfg_map_get(keydef, "algorithm", &algobj);
754			(void)cfg_map_get(keydef, "secret", &secretobj);
755			INSIST(algobj != NULL && secretobj != NULL);
756
757			algstr = cfg_obj_asstring(algobj);
758			secretstr = cfg_obj_asstring(secretobj);
759
760			if (ns_config_getkeyalgorithm(algstr, NULL, NULL) !=
761			    ISC_R_SUCCESS)
762			{
763				cfg_obj_log(control, ns_g_lctx,
764					    ISC_LOG_WARNING,
765					    "unsupported algorithm '%s' in "
766					    "key '%s' for use with command "
767					    "channel %s",
768					    algstr, keyid->keyname, socktext);
769				ISC_LIST_UNLINK(*keyids, keyid, link);
770				free_controlkey(keyid, mctx);
771				continue;
772			}
773
774			isc_buffer_init(&b, secret, sizeof(secret));
775			result = isc_base64_decodestring(secretstr, &b);
776
777			if (result != ISC_R_SUCCESS) {
778				cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
779					    "secret for key '%s' on "
780					    "command channel %s: %s",
781					    keyid->keyname, socktext,
782					    isc_result_totext(result));
783				ISC_LIST_UNLINK(*keyids, keyid, link);
784				free_controlkey(keyid, mctx);
785				continue;
786			}
787
788			keyid->secret.length = isc_buffer_usedlength(&b);
789			keyid->secret.base = isc_mem_get(mctx,
790							 keyid->secret.length);
791			if (keyid->secret.base == NULL) {
792				cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
793					   "couldn't register key '%s': "
794					   "out of memory", keyid->keyname);
795				ISC_LIST_UNLINK(*keyids, keyid, link);
796				free_controlkey(keyid, mctx);
797				break;
798			}
799			memcpy(keyid->secret.base, isc_buffer_base(&b),
800			       keyid->secret.length);
801		}
802	}
803}
804
805#define CHECK(x) \
806	do { \
807		 result = (x); \
808		 if (result != ISC_R_SUCCESS) \
809			goto cleanup; \
810	} while (0)
811
812static isc_result_t
813get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) {
814	isc_result_t result;
815	cfg_parser_t *pctx = NULL;
816	cfg_obj_t *config = NULL;
817	const cfg_obj_t *key = NULL;
818	const cfg_obj_t *algobj = NULL;
819	const cfg_obj_t *secretobj = NULL;
820	const char *algstr = NULL;
821	const char *secretstr = NULL;
822	controlkey_t *keyid = NULL;
823	char secret[1024];
824	isc_buffer_t b;
825
826	CHECK(cfg_parser_create(mctx, ns_g_lctx, &pctx));
827	CHECK(cfg_parse_file(pctx, ns_g_keyfile, &cfg_type_rndckey, &config));
828	CHECK(cfg_map_get(config, "key", &key));
829
830	keyid = isc_mem_get(mctx, sizeof(*keyid));
831	if (keyid == NULL)
832		CHECK(ISC_R_NOMEMORY);
833	keyid->keyname = isc_mem_strdup(mctx,
834					cfg_obj_asstring(cfg_map_getname(key)));
835	keyid->secret.base = NULL;
836	keyid->secret.length = 0;
837	ISC_LINK_INIT(keyid, link);
838	if (keyid->keyname == NULL)
839		CHECK(ISC_R_NOMEMORY);
840
841	CHECK(bind9_check_key(key, ns_g_lctx));
842
843	(void)cfg_map_get(key, "algorithm", &algobj);
844	(void)cfg_map_get(key, "secret", &secretobj);
845	INSIST(algobj != NULL && secretobj != NULL);
846
847	algstr = cfg_obj_asstring(algobj);
848	secretstr = cfg_obj_asstring(secretobj);
849
850	if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != ISC_R_SUCCESS) {
851		cfg_obj_log(key, ns_g_lctx,
852			    ISC_LOG_WARNING,
853			    "unsupported algorithm '%s' in "
854			    "key '%s' for use with command "
855			    "channel",
856			    algstr, keyid->keyname);
857		goto cleanup;
858	}
859
860	isc_buffer_init(&b, secret, sizeof(secret));
861	result = isc_base64_decodestring(secretstr, &b);
862
863	if (result != ISC_R_SUCCESS) {
864		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
865			    "secret for key '%s' on command channel: %s",
866			    keyid->keyname, isc_result_totext(result));
867		CHECK(result);
868	}
869
870	keyid->secret.length = isc_buffer_usedlength(&b);
871	keyid->secret.base = isc_mem_get(mctx,
872					 keyid->secret.length);
873	if (keyid->secret.base == NULL) {
874		cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
875			   "couldn't register key '%s': "
876			   "out of memory", keyid->keyname);
877		CHECK(ISC_R_NOMEMORY);
878	}
879	memcpy(keyid->secret.base, isc_buffer_base(&b),
880	       keyid->secret.length);
881	ISC_LIST_APPEND(*keyids, keyid, link);
882	keyid = NULL;
883	result = ISC_R_SUCCESS;
884
885  cleanup:
886	if (keyid != NULL)
887		free_controlkey(keyid, mctx);
888	if (config != NULL)
889		cfg_obj_destroy(pctx, &config);
890	if (pctx != NULL)
891		cfg_parser_destroy(&pctx);
892	return (result);
893}
894
895/*
896 * Ensures that both '*global_keylistp' and '*control_keylistp' are
897 * valid or both are NULL.
898 */
899static void
900get_key_info(const cfg_obj_t *config, const cfg_obj_t *control,
901	     const cfg_obj_t **global_keylistp,
902	     const cfg_obj_t **control_keylistp)
903{
904	isc_result_t result;
905	const cfg_obj_t *control_keylist = NULL;
906	const cfg_obj_t *global_keylist = NULL;
907
908	REQUIRE(global_keylistp != NULL && *global_keylistp == NULL);
909	REQUIRE(control_keylistp != NULL && *control_keylistp == NULL);
910
911	control_keylist = cfg_tuple_get(control, "keys");
912
913	if (!cfg_obj_isvoid(control_keylist) &&
914	    cfg_list_first(control_keylist) != NULL) {
915		result = cfg_map_get(config, "key", &global_keylist);
916
917		if (result == ISC_R_SUCCESS) {
918			*global_keylistp = global_keylist;
919			*control_keylistp = control_keylist;
920		}
921	}
922}
923
924static void
925update_listener(ns_controls_t *cp, controllistener_t **listenerp,
926		const cfg_obj_t *control, const cfg_obj_t *config,
927		isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
928	        const char *socktext, isc_sockettype_t type)
929{
930	controllistener_t *listener;
931	const cfg_obj_t *allow;
932	const cfg_obj_t *global_keylist = NULL;
933	const cfg_obj_t *control_keylist = NULL;
934	dns_acl_t *new_acl = NULL;
935	controlkeylist_t keys;
936	isc_result_t result = ISC_R_SUCCESS;
937
938	for (listener = ISC_LIST_HEAD(cp->listeners);
939	     listener != NULL;
940	     listener = ISC_LIST_NEXT(listener, link))
941		if (isc_sockaddr_equal(addr, &listener->address))
942			break;
943
944	if (listener == NULL) {
945		*listenerp = NULL;
946		return;
947	}
948
949	/*
950	 * There is already a listener for this sockaddr.
951	 * Update the access list and key information.
952	 *
953	 * First try to deal with the key situation.  There are a few
954	 * possibilities:
955	 *  (a)	It had an explicit keylist and still has an explicit keylist.
956	 *  (b)	It had an automagic key and now has an explicit keylist.
957	 *  (c)	It had an explicit keylist and now needs an automagic key.
958	 *  (d) It has an automagic key and still needs the automagic key.
959	 *
960	 * (c) and (d) are the annoying ones.  The caller needs to know
961	 * that it should use the automagic configuration for key information
962	 * in place of the named.conf configuration.
963	 *
964	 * XXXDCL There is one other hazard that has not been dealt with,
965	 * the problem that if a key change is being caused by a control
966	 * channel reload, then the response will be with the new key
967	 * and not able to be decrypted by the client.
968	 */
969	if (control != NULL)
970		get_key_info(config, control, &global_keylist,
971			     &control_keylist);
972
973	if (control_keylist != NULL) {
974		INSIST(global_keylist != NULL);
975
976		ISC_LIST_INIT(keys);
977		result = controlkeylist_fromcfg(control_keylist,
978						listener->mctx, &keys);
979		if (result == ISC_R_SUCCESS) {
980			free_controlkeylist(&listener->keys, listener->mctx);
981			listener->keys = keys;
982			register_keys(control, global_keylist, &listener->keys,
983				      listener->mctx, socktext);
984		}
985	} else {
986		free_controlkeylist(&listener->keys, listener->mctx);
987		result = get_rndckey(listener->mctx, &listener->keys);
988	}
989
990	if (result != ISC_R_SUCCESS && global_keylist != NULL) {
991		/*
992		 * This message might be a little misleading since the
993		 * "new keys" might in fact be identical to the old ones,
994		 * but tracking whether they are identical just for the
995		 * sake of avoiding this message would be too much trouble.
996		 */
997		if (control != NULL)
998			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
999				    "couldn't install new keys for "
1000				    "command channel %s: %s",
1001				    socktext, isc_result_totext(result));
1002		else
1003			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1004				      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
1005				      "couldn't install new keys for "
1006				      "command channel %s: %s",
1007				      socktext, isc_result_totext(result));
1008	}
1009
1010	/*
1011	 * Now, keep the old access list unless a new one can be made.
1012	 */
1013	if (control != NULL && type == isc_sockettype_tcp) {
1014		allow = cfg_tuple_get(control, "allow");
1015		result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1016					    aclconfctx, listener->mctx,
1017					    &new_acl);
1018	} else {
1019		result = dns_acl_any(listener->mctx, &new_acl);
1020	}
1021
1022	if (result == ISC_R_SUCCESS) {
1023		dns_acl_detach(&listener->acl);
1024		dns_acl_attach(new_acl, &listener->acl);
1025		dns_acl_detach(&new_acl);
1026		/* XXXDCL say the old acl is still used? */
1027	} else if (control != NULL)
1028		cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1029			    "couldn't install new acl for "
1030			    "command channel %s: %s",
1031			    socktext, isc_result_totext(result));
1032	else
1033		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1034			      NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
1035			      "couldn't install new acl for "
1036			      "command channel %s: %s",
1037			      socktext, isc_result_totext(result));
1038
1039	if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
1040		isc_uint32_t perm, owner, group;
1041		perm  = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
1042		owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner"));
1043		group = cfg_obj_asuint32(cfg_tuple_get(control, "group"));
1044		result = ISC_R_SUCCESS;
1045		if (listener->perm != perm || listener->owner != owner ||
1046		    listener->group != group)
1047			result = isc_socket_permunix(&listener->address, perm,
1048						     owner, group);
1049		if (result == ISC_R_SUCCESS) {
1050			listener->perm = perm;
1051			listener->owner = owner;
1052			listener->group = group;
1053		} else if (control != NULL)
1054			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1055				    "couldn't update ownership/permission for "
1056				    "command channel %s", socktext);
1057	}
1058
1059	*listenerp = listener;
1060}
1061
1062static void
1063add_listener(ns_controls_t *cp, controllistener_t **listenerp,
1064	     const cfg_obj_t *control, const cfg_obj_t *config,
1065	     isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
1066	     const char *socktext, isc_sockettype_t type)
1067{
1068	isc_mem_t *mctx = cp->server->mctx;
1069	controllistener_t *listener;
1070	const cfg_obj_t *allow;
1071	const cfg_obj_t *global_keylist = NULL;
1072	const cfg_obj_t *control_keylist = NULL;
1073	dns_acl_t *new_acl = NULL;
1074	isc_result_t result = ISC_R_SUCCESS;
1075
1076	listener = isc_mem_get(mctx, sizeof(*listener));
1077	if (listener == NULL)
1078		result = ISC_R_NOMEMORY;
1079
1080	if (result == ISC_R_SUCCESS) {
1081		listener->controls = cp;
1082		listener->mctx = mctx;
1083		listener->task = cp->server->task;
1084		listener->address = *addr;
1085		listener->sock = NULL;
1086		listener->listening = ISC_FALSE;
1087		listener->exiting = ISC_FALSE;
1088		listener->acl = NULL;
1089		listener->type = type;
1090		listener->perm = 0;
1091		listener->owner = 0;
1092		listener->group = 0;
1093		ISC_LINK_INIT(listener, link);
1094		ISC_LIST_INIT(listener->keys);
1095		ISC_LIST_INIT(listener->connections);
1096
1097		/*
1098		 * Make the acl.
1099		 */
1100		if (control != NULL && type == isc_sockettype_tcp) {
1101			allow = cfg_tuple_get(control, "allow");
1102			result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
1103						    aclconfctx, mctx, &new_acl);
1104		} else {
1105			result = dns_acl_any(mctx, &new_acl);
1106		}
1107	}
1108
1109	if (result == ISC_R_SUCCESS) {
1110		dns_acl_attach(new_acl, &listener->acl);
1111		dns_acl_detach(&new_acl);
1112
1113		if (config != NULL)
1114			get_key_info(config, control, &global_keylist,
1115				     &control_keylist);
1116
1117		if (control_keylist != NULL) {
1118			result = controlkeylist_fromcfg(control_keylist,
1119							listener->mctx,
1120							&listener->keys);
1121			if (result == ISC_R_SUCCESS)
1122				register_keys(control, global_keylist,
1123					      &listener->keys,
1124					      listener->mctx, socktext);
1125		} else
1126			result = get_rndckey(mctx, &listener->keys);
1127
1128		if (result != ISC_R_SUCCESS && control != NULL)
1129			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1130				    "couldn't install keys for "
1131				    "command channel %s: %s",
1132				    socktext, isc_result_totext(result));
1133	}
1134
1135	if (result == ISC_R_SUCCESS) {
1136		int pf = isc_sockaddr_pf(&listener->address);
1137		if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
1138#ifdef ISC_PLATFORM_HAVESYSUNH
1139		    (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) ||
1140#endif
1141		    (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
1142			result = ISC_R_FAMILYNOSUPPORT;
1143	}
1144
1145	if (result == ISC_R_SUCCESS && type == isc_sockettype_unix)
1146		isc_socket_cleanunix(&listener->address, ISC_FALSE);
1147
1148	if (result == ISC_R_SUCCESS)
1149		result = isc_socket_create(ns_g_socketmgr,
1150					   isc_sockaddr_pf(&listener->address),
1151					   type, &listener->sock);
1152
1153	if (result == ISC_R_SUCCESS)
1154		result = isc_socket_bind(listener->sock, &listener->address,
1155					 ISC_SOCKET_REUSEADDRESS);
1156
1157	if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
1158		listener->perm = cfg_obj_asuint32(cfg_tuple_get(control,
1159								"perm"));
1160		listener->owner = cfg_obj_asuint32(cfg_tuple_get(control,
1161								 "owner"));
1162		listener->group = cfg_obj_asuint32(cfg_tuple_get(control,
1163								 "group"));
1164		result = isc_socket_permunix(&listener->address, listener->perm,
1165					     listener->owner, listener->group);
1166	}
1167	if (result == ISC_R_SUCCESS)
1168		result = control_listen(listener);
1169
1170	if (result == ISC_R_SUCCESS)
1171		result = control_accept(listener);
1172
1173	if (result == ISC_R_SUCCESS) {
1174		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1175			      NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
1176			      "command channel listening on %s", socktext);
1177		*listenerp = listener;
1178
1179	} else {
1180		if (listener != NULL) {
1181			listener->exiting = ISC_TRUE;
1182			free_listener(listener);
1183		}
1184
1185		if (control != NULL)
1186			cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
1187				    "couldn't add command channel %s: %s",
1188				    socktext, isc_result_totext(result));
1189		else
1190			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
1191				      NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
1192				      "couldn't add command channel %s: %s",
1193				      socktext, isc_result_totext(result));
1194
1195		*listenerp = NULL;
1196	}
1197
1198	/* XXXDCL return error results? fail hard? */
1199}
1200
1201isc_result_t
1202ns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config,
1203		      cfg_aclconfctx_t *aclconfctx)
1204{
1205	controllistener_t *listener;
1206	controllistenerlist_t new_listeners;
1207	const cfg_obj_t *controlslist = NULL;
1208	const cfg_listelt_t *element, *element2;
1209	char socktext[ISC_SOCKADDR_FORMATSIZE];
1210
1211	ISC_LIST_INIT(new_listeners);
1212
1213	/*
1214	 * Get the list of named.conf 'controls' statements.
1215	 */
1216	(void)cfg_map_get(config, "controls", &controlslist);
1217
1218	/*
1219	 * Run through the new control channel list, noting sockets that
1220	 * are already being listened on and moving them to the new list.
1221	 *
1222	 * Identifying duplicate addr/port combinations is left to either
1223	 * the underlying config code, or to the bind attempt getting an
1224	 * address-in-use error.
1225	 */
1226	if (controlslist != NULL) {
1227		for (element = cfg_list_first(controlslist);
1228		     element != NULL;
1229		     element = cfg_list_next(element)) {
1230			const cfg_obj_t *controls;
1231			const cfg_obj_t *inetcontrols = NULL;
1232
1233			controls = cfg_listelt_value(element);
1234			(void)cfg_map_get(controls, "inet", &inetcontrols);
1235			if (inetcontrols == NULL)
1236				continue;
1237
1238			for (element2 = cfg_list_first(inetcontrols);
1239			     element2 != NULL;
1240			     element2 = cfg_list_next(element2)) {
1241				const cfg_obj_t *control;
1242				const cfg_obj_t *obj;
1243				isc_sockaddr_t addr;
1244
1245				/*
1246				 * The parser handles BIND 8 configuration file
1247				 * syntax, so it allows unix phrases as well
1248				 * inet phrases with no keys{} clause.
1249				 */
1250				control = cfg_listelt_value(element2);
1251
1252				obj = cfg_tuple_get(control, "address");
1253				addr = *cfg_obj_assockaddr(obj);
1254				if (isc_sockaddr_getport(&addr) == 0)
1255					isc_sockaddr_setport(&addr,
1256							     NS_CONTROL_PORT);
1257
1258				isc_sockaddr_format(&addr, socktext,
1259						    sizeof(socktext));
1260
1261				isc_log_write(ns_g_lctx,
1262					      NS_LOGCATEGORY_GENERAL,
1263					      NS_LOGMODULE_CONTROL,
1264					      ISC_LOG_DEBUG(9),
1265					      "processing control channel %s",
1266					      socktext);
1267
1268				update_listener(cp, &listener, control, config,
1269						&addr, aclconfctx, socktext,
1270						isc_sockettype_tcp);
1271
1272				if (listener != NULL)
1273					/*
1274					 * Remove the listener from the old
1275					 * list, so it won't be shut down.
1276					 */
1277					ISC_LIST_UNLINK(cp->listeners,
1278							listener, link);
1279				else
1280					/*
1281					 * This is a new listener.
1282					 */
1283					add_listener(cp, &listener, control,
1284						     config, &addr, aclconfctx,
1285						     socktext,
1286						     isc_sockettype_tcp);
1287
1288				if (listener != NULL)
1289					ISC_LIST_APPEND(new_listeners,
1290							listener, link);
1291			}
1292		}
1293		for (element = cfg_list_first(controlslist);
1294		     element != NULL;
1295		     element = cfg_list_next(element)) {
1296			const cfg_obj_t *controls;
1297			const cfg_obj_t *unixcontrols = NULL;
1298
1299			controls = cfg_listelt_value(element);
1300			(void)cfg_map_get(controls, "unix", &unixcontrols);
1301			if (unixcontrols == NULL)
1302				continue;
1303
1304			for (element2 = cfg_list_first(unixcontrols);
1305			     element2 != NULL;
1306			     element2 = cfg_list_next(element2)) {
1307				const cfg_obj_t *control;
1308				const cfg_obj_t *path;
1309				isc_sockaddr_t addr;
1310				isc_result_t result;
1311
1312				/*
1313				 * The parser handles BIND 8 configuration file
1314				 * syntax, so it allows unix phrases as well
1315				 * inet phrases with no keys{} clause.
1316				 */
1317				control = cfg_listelt_value(element2);
1318
1319				path = cfg_tuple_get(control, "path");
1320				result = isc_sockaddr_frompath(&addr,
1321						      cfg_obj_asstring(path));
1322				if (result != ISC_R_SUCCESS) {
1323					isc_log_write(ns_g_lctx,
1324					      NS_LOGCATEGORY_GENERAL,
1325					      NS_LOGMODULE_CONTROL,
1326					      ISC_LOG_DEBUG(9),
1327					      "control channel '%s': %s",
1328					      cfg_obj_asstring(path),
1329					      isc_result_totext(result));
1330					continue;
1331				}
1332
1333				isc_log_write(ns_g_lctx,
1334					      NS_LOGCATEGORY_GENERAL,
1335					      NS_LOGMODULE_CONTROL,
1336					      ISC_LOG_DEBUG(9),
1337					      "processing control channel '%s'",
1338					      cfg_obj_asstring(path));
1339
1340				update_listener(cp, &listener, control, config,
1341						&addr, aclconfctx,
1342					        cfg_obj_asstring(path),
1343						isc_sockettype_unix);
1344
1345				if (listener != NULL)
1346					/*
1347					 * Remove the listener from the old
1348					 * list, so it won't be shut down.
1349					 */
1350					ISC_LIST_UNLINK(cp->listeners,
1351							listener, link);
1352				else
1353					/*
1354					 * This is a new listener.
1355					 */
1356					add_listener(cp, &listener, control,
1357						     config, &addr, aclconfctx,
1358						     cfg_obj_asstring(path),
1359						     isc_sockettype_unix);
1360
1361				if (listener != NULL)
1362					ISC_LIST_APPEND(new_listeners,
1363							listener, link);
1364			}
1365		}
1366	} else {
1367		int i;
1368
1369		for (i = 0; i < 2; i++) {
1370			isc_sockaddr_t addr;
1371
1372			if (i == 0) {
1373				struct in_addr localhost;
1374
1375				if (isc_net_probeipv4() != ISC_R_SUCCESS)
1376					continue;
1377				localhost.s_addr = htonl(INADDR_LOOPBACK);
1378				isc_sockaddr_fromin(&addr, &localhost, 0);
1379			} else {
1380				if (isc_net_probeipv6() != ISC_R_SUCCESS)
1381					continue;
1382				isc_sockaddr_fromin6(&addr,
1383						     &in6addr_loopback, 0);
1384			}
1385			isc_sockaddr_setport(&addr, NS_CONTROL_PORT);
1386
1387			isc_sockaddr_format(&addr, socktext, sizeof(socktext));
1388
1389			update_listener(cp, &listener, NULL, NULL,
1390					&addr, NULL, socktext,
1391				        isc_sockettype_tcp);
1392
1393			if (listener != NULL)
1394				/*
1395				 * Remove the listener from the old
1396				 * list, so it won't be shut down.
1397				 */
1398				ISC_LIST_UNLINK(cp->listeners,
1399						listener, link);
1400			else
1401				/*
1402				 * This is a new listener.
1403				 */
1404				add_listener(cp, &listener, NULL, NULL,
1405					     &addr, NULL, socktext,
1406					     isc_sockettype_tcp);
1407
1408			if (listener != NULL)
1409				ISC_LIST_APPEND(new_listeners,
1410						listener, link);
1411		}
1412	}
1413
1414	/*
1415	 * ns_control_shutdown() will stop whatever is on the global
1416	 * listeners list, which currently only has whatever sockaddrs
1417	 * were in the previous configuration (if any) that do not
1418	 * remain in the current configuration.
1419	 */
1420	controls_shutdown(cp);
1421
1422	/*
1423	 * Put all of the valid listeners on the listeners list.
1424	 * Anything already on listeners in the process of shutting
1425	 * down will be taken care of by listen_done().
1426	 */
1427	ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link);
1428	return (ISC_R_SUCCESS);
1429}
1430
1431isc_result_t
1432ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp) {
1433	isc_mem_t *mctx = server->mctx;
1434	isc_result_t result;
1435	ns_controls_t *controls = isc_mem_get(mctx, sizeof(*controls));
1436
1437	if (controls == NULL)
1438		return (ISC_R_NOMEMORY);
1439	controls->server = server;
1440	ISC_LIST_INIT(controls->listeners);
1441	controls->shuttingdown = ISC_FALSE;
1442	controls->symtab = NULL;
1443	result = isccc_cc_createsymtab(&controls->symtab);
1444	if (result != ISC_R_SUCCESS) {
1445		isc_mem_put(server->mctx, controls, sizeof(*controls));
1446		return (result);
1447	}
1448	*ctrlsp = controls;
1449	return (ISC_R_SUCCESS);
1450}
1451
1452void
1453ns_controls_destroy(ns_controls_t **ctrlsp) {
1454	ns_controls_t *controls = *ctrlsp;
1455
1456	REQUIRE(ISC_LIST_EMPTY(controls->listeners));
1457
1458	isccc_symtab_destroy(&controls->symtab);
1459	isc_mem_put(controls->server->mctx, controls, sizeof(*controls));
1460	*ctrlsp = NULL;
1461}
1462