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