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