2 * Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2001-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
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.
18 /* $Id: controlconf.c,v 1.40.18.10.40.3 2008/07/23 23:16:43 marka Exp $ */
24 #include <isc/base64.h>
25 #include <isc/buffer.h>
26 #include <isc/event.h>
29 #include <isc/netaddr.h>
30 #include <isc/random.h>
31 #include <isc/result.h>
32 #include <isc/stdtime.h>
33 #include <isc/string.h>
34 #include <isc/timer.h>
37 #include <isccfg/namedconf.h>
39 #include <bind9/check.h>
41 #include <isccc/alist.h>
43 #include <isccc/ccmsg.h>
44 #include <isccc/events.h>
45 #include <isccc/result.h>
46 #include <isccc/sexpr.h>
47 #include <isccc/symtab.h>
48 #include <isccc/util.h>
50 #include <dns/result.h>
52 #include <named/config.h>
53 #include <named/control.h>
54 #include <named/log.h>
55 #include <named/server.h>
58 * Note: Listeners and connections are not locked. All event handlers are
59 * executed by the server task, and all callers of exported routines must
60 * be running under the server task.
63 typedef struct controlkey controlkey_t
;
64 typedef ISC_LIST(controlkey_t
) controlkeylist_t
;
66 typedef struct controlconnection controlconnection_t
;
67 typedef ISC_LIST(controlconnection_t
) controlconnectionlist_t
;
69 typedef struct controllistener controllistener_t
;
70 typedef ISC_LIST(controllistener_t
) controllistenerlist_t
;
75 ISC_LINK(controlkey_t
) link
;
78 struct controlconnection
{
81 isc_boolean_t ccmsg_valid
;
82 isc_boolean_t sending
;
84 unsigned char buffer
[2048];
85 controllistener_t
* listener
;
87 ISC_LINK(controlconnection_t
) link
;
90 struct controllistener
{
91 ns_controls_t
* controls
;
94 isc_sockaddr_t address
;
97 isc_boolean_t listening
;
98 isc_boolean_t exiting
;
99 controlkeylist_t keys
;
100 controlconnectionlist_t connections
;
101 isc_sockettype_t type
;
105 ISC_LINK(controllistener_t
) link
;
110 controllistenerlist_t listeners
;
111 isc_boolean_t shuttingdown
;
112 isccc_symtab_t
*symtab
;
115 static void control_newconn(isc_task_t
*task
, isc_event_t
*event
);
116 static void control_recvmessage(isc_task_t
*task
, isc_event_t
*event
);
118 #define CLOCKSKEW 300
121 free_controlkey(controlkey_t
*key
, isc_mem_t
*mctx
) {
122 if (key
->keyname
!= NULL
)
123 isc_mem_free(mctx
, key
->keyname
);
124 if (key
->secret
.base
!= NULL
)
125 isc_mem_put(mctx
, key
->secret
.base
, key
->secret
.length
);
126 isc_mem_put(mctx
, key
, sizeof(*key
));
130 free_controlkeylist(controlkeylist_t
*keylist
, isc_mem_t
*mctx
) {
131 while (!ISC_LIST_EMPTY(*keylist
)) {
132 controlkey_t
*key
= ISC_LIST_HEAD(*keylist
);
133 ISC_LIST_UNLINK(*keylist
, key
, link
);
134 free_controlkey(key
, mctx
);
139 free_listener(controllistener_t
*listener
) {
140 INSIST(listener
->exiting
);
141 INSIST(!listener
->listening
);
142 INSIST(ISC_LIST_EMPTY(listener
->connections
));
144 if (listener
->sock
!= NULL
)
145 isc_socket_detach(&listener
->sock
);
147 free_controlkeylist(&listener
->keys
, listener
->mctx
);
149 if (listener
->acl
!= NULL
)
150 dns_acl_detach(&listener
->acl
);
152 isc_mem_put(listener
->mctx
, listener
, sizeof(*listener
));
156 maybe_free_listener(controllistener_t
*listener
) {
157 if (listener
->exiting
&&
158 !listener
->listening
&&
159 ISC_LIST_EMPTY(listener
->connections
))
160 free_listener(listener
);
164 maybe_free_connection(controlconnection_t
*conn
) {
165 controllistener_t
*listener
= conn
->listener
;
167 if (conn
->timer
!= NULL
)
168 isc_timer_detach(&conn
->timer
);
170 if (conn
->ccmsg_valid
) {
171 isccc_ccmsg_cancelread(&conn
->ccmsg
);
176 isc_socket_cancel(conn
->sock
, listener
->task
,
177 ISC_SOCKCANCEL_SEND
);
181 ISC_LIST_UNLINK(listener
->connections
, conn
, link
);
182 isc_mem_put(listener
->mctx
, conn
, sizeof(*conn
));
186 shutdown_listener(controllistener_t
*listener
) {
187 controlconnection_t
*conn
;
188 controlconnection_t
*next
;
190 if (!listener
->exiting
) {
191 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
193 ISC_LIST_UNLINK(listener
->controls
->listeners
, listener
, link
);
195 isc_sockaddr_format(&listener
->address
, socktext
,
197 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
198 NS_LOGMODULE_CONTROL
, ISC_LOG_NOTICE
,
199 "stopping command channel on %s", socktext
);
200 if (listener
->type
== isc_sockettype_unix
)
201 isc_socket_cleanunix(&listener
->address
, ISC_TRUE
);
202 listener
->exiting
= ISC_TRUE
;
205 for (conn
= ISC_LIST_HEAD(listener
->connections
);
209 next
= ISC_LIST_NEXT(conn
, link
);
210 maybe_free_connection(conn
);
213 if (listener
->listening
)
214 isc_socket_cancel(listener
->sock
, listener
->task
,
215 ISC_SOCKCANCEL_ACCEPT
);
217 maybe_free_listener(listener
);
221 address_ok(isc_sockaddr_t
*sockaddr
, dns_acl_t
*acl
) {
222 isc_netaddr_t netaddr
;
226 isc_netaddr_fromsockaddr(&netaddr
, sockaddr
);
228 result
= dns_acl_match(&netaddr
, NULL
, acl
,
229 &ns_g_server
->aclenv
, &match
, NULL
);
231 if (result
!= ISC_R_SUCCESS
|| match
<= 0)
238 control_accept(controllistener_t
*listener
) {
240 result
= isc_socket_accept(listener
->sock
,
242 control_newconn
, listener
);
243 if (result
!= ISC_R_SUCCESS
)
244 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
245 "isc_socket_accept() failed: %s",
246 isc_result_totext(result
));
248 listener
->listening
= ISC_TRUE
;
253 control_listen(controllistener_t
*listener
) {
256 result
= isc_socket_listen(listener
->sock
, 0);
257 if (result
!= ISC_R_SUCCESS
)
258 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
259 "isc_socket_listen() failed: %s",
260 isc_result_totext(result
));
265 control_next(controllistener_t
*listener
) {
266 (void)control_accept(listener
);
270 control_senddone(isc_task_t
*task
, isc_event_t
*event
) {
271 isc_socketevent_t
*sevent
= (isc_socketevent_t
*) event
;
272 controlconnection_t
*conn
= event
->ev_arg
;
273 controllistener_t
*listener
= conn
->listener
;
274 isc_socket_t
*sock
= (isc_socket_t
*)sevent
->ev_sender
;
277 REQUIRE(conn
->sending
);
281 conn
->sending
= ISC_FALSE
;
283 if (sevent
->result
!= ISC_R_SUCCESS
&&
284 sevent
->result
!= ISC_R_CANCELED
)
286 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
287 isc_sockaddr_t peeraddr
;
289 (void)isc_socket_getpeername(sock
, &peeraddr
);
290 isc_sockaddr_format(&peeraddr
, socktext
, sizeof(socktext
));
291 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
292 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
293 "error sending command response to %s: %s",
294 socktext
, isc_result_totext(sevent
->result
));
296 isc_event_free(&event
);
298 result
= isccc_ccmsg_readmessage(&conn
->ccmsg
, listener
->task
,
299 control_recvmessage
, conn
);
300 if (result
!= ISC_R_SUCCESS
) {
301 isc_socket_detach(&conn
->sock
);
302 maybe_free_connection(conn
);
303 maybe_free_listener(listener
);
308 log_invalid(isccc_ccmsg_t
*ccmsg
, isc_result_t result
) {
309 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
310 isc_sockaddr_t peeraddr
;
312 (void)isc_socket_getpeername(ccmsg
->sock
, &peeraddr
);
313 isc_sockaddr_format(&peeraddr
, socktext
, sizeof(socktext
));
314 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
315 NS_LOGMODULE_CONTROL
, ISC_LOG_ERROR
,
316 "invalid command from %s: %s",
317 socktext
, isc_result_totext(result
));
321 control_recvmessage(isc_task_t
*task
, isc_event_t
*event
) {
322 controlconnection_t
*conn
;
323 controllistener_t
*listener
;
325 isccc_sexpr_t
*request
= NULL
;
326 isccc_sexpr_t
*response
= NULL
;
327 isccc_region_t ccregion
;
328 isccc_region_t secret
;
334 char textarray
[1024];
336 isc_result_t eresult
;
337 isccc_sexpr_t
*_ctrl
;
342 REQUIRE(event
->ev_type
== ISCCC_EVENT_CCMSG
);
344 conn
= event
->ev_arg
;
345 listener
= conn
->listener
;
346 secret
.rstart
= NULL
;
348 /* Is the server shutting down? */
349 if (listener
->controls
->shuttingdown
)
352 if (conn
->ccmsg
.result
!= ISC_R_SUCCESS
) {
353 if (conn
->ccmsg
.result
!= ISC_R_CANCELED
&&
354 conn
->ccmsg
.result
!= ISC_R_EOF
)
355 log_invalid(&conn
->ccmsg
, conn
->ccmsg
.result
);
361 for (key
= ISC_LIST_HEAD(listener
->keys
);
363 key
= ISC_LIST_NEXT(key
, link
))
365 ccregion
.rstart
= isc_buffer_base(&conn
->ccmsg
.buffer
);
366 ccregion
.rend
= isc_buffer_used(&conn
->ccmsg
.buffer
);
367 if (secret
.rstart
!= NULL
)
368 isc_mem_put(listener
->mctx
, secret
.rstart
,
369 REGION_SIZE(secret
));
370 secret
.rstart
= isc_mem_get(listener
->mctx
, key
->secret
.length
);
371 if (secret
.rstart
== NULL
)
373 memcpy(secret
.rstart
, key
->secret
.base
, key
->secret
.length
);
374 secret
.rend
= secret
.rstart
+ key
->secret
.length
;
375 result
= isccc_cc_fromwire(&ccregion
, &request
, &secret
);
376 if (result
== ISC_R_SUCCESS
)
378 else if (result
== ISCCC_R_BADAUTH
) {
380 * For some reason, request is non-NULL when
381 * isccc_cc_fromwire returns ISCCC_R_BADAUTH.
384 isccc_sexpr_free(&request
);
386 log_invalid(&conn
->ccmsg
, result
);
392 log_invalid(&conn
->ccmsg
, ISCCC_R_BADAUTH
);
396 /* We shouldn't be getting a reply. */
397 if (isccc_cc_isreply(request
)) {
398 log_invalid(&conn
->ccmsg
, ISC_R_FAILURE
);
402 isc_stdtime_get(&now
);
405 * Limit exposure to replay attacks.
407 _ctrl
= isccc_alist_lookup(request
, "_ctrl");
409 log_invalid(&conn
->ccmsg
, ISC_R_FAILURE
);
413 if (isccc_cc_lookupuint32(_ctrl
, "_tim", &sent
) == ISC_R_SUCCESS
) {
414 if ((sent
+ CLOCKSKEW
) < now
|| (sent
- CLOCKSKEW
) > now
) {
415 log_invalid(&conn
->ccmsg
, ISCCC_R_CLOCKSKEW
);
419 log_invalid(&conn
->ccmsg
, ISC_R_FAILURE
);
424 * Expire messages that are too old.
426 if (isccc_cc_lookupuint32(_ctrl
, "_exp", &exp
) == ISC_R_SUCCESS
&&
428 log_invalid(&conn
->ccmsg
, ISCCC_R_EXPIRED
);
433 * Duplicate suppression (required for UDP).
435 isccc_cc_cleansymtab(listener
->controls
->symtab
, now
);
436 result
= isccc_cc_checkdup(listener
->controls
->symtab
, request
, now
);
437 if (result
!= ISC_R_SUCCESS
) {
438 if (result
== ISC_R_EXISTS
)
439 result
= ISCCC_R_DUPLICATE
;
440 log_invalid(&conn
->ccmsg
, result
);
444 if (conn
->nonce
!= 0 &&
445 (isccc_cc_lookupuint32(_ctrl
, "_nonce", &nonce
) != ISC_R_SUCCESS
||
446 conn
->nonce
!= nonce
)) {
447 log_invalid(&conn
->ccmsg
, ISCCC_R_BADAUTH
);
454 while (conn
->nonce
== 0)
455 isc_random_get(&conn
->nonce
);
457 isc_buffer_init(&text
, textarray
, sizeof(textarray
));
458 eresult
= ns_control_docommand(request
, &text
);
460 result
= isccc_cc_createresponse(request
, now
, now
+ 60, &response
);
461 if (result
!= ISC_R_SUCCESS
)
463 if (eresult
!= ISC_R_SUCCESS
) {
466 data
= isccc_alist_lookup(response
, "_data");
468 const char *estr
= isc_result_totext(eresult
);
469 if (isccc_cc_definestring(data
, "err", estr
) == NULL
)
474 if (isc_buffer_usedlength(&text
) > 0) {
477 data
= isccc_alist_lookup(response
, "_data");
479 char *str
= (char *)isc_buffer_base(&text
);
480 if (isccc_cc_definestring(data
, "text", str
) == NULL
)
485 _ctrl
= isccc_alist_lookup(response
, "_ctrl");
487 isccc_cc_defineuint32(_ctrl
, "_nonce", conn
->nonce
) == NULL
)
490 ccregion
.rstart
= conn
->buffer
+ 4;
491 ccregion
.rend
= conn
->buffer
+ sizeof(conn
->buffer
);
492 result
= isccc_cc_towire(response
, &ccregion
, &secret
);
493 if (result
!= ISC_R_SUCCESS
)
495 isc_buffer_init(&b
, conn
->buffer
, 4);
496 len
= sizeof(conn
->buffer
) - REGION_SIZE(ccregion
);
497 isc_buffer_putuint32(&b
, len
- 4);
498 r
.base
= conn
->buffer
;
501 result
= isc_socket_send(conn
->sock
, &r
, task
, control_senddone
, conn
);
502 if (result
!= ISC_R_SUCCESS
)
504 conn
->sending
= ISC_TRUE
;
506 if (secret
.rstart
!= NULL
)
507 isc_mem_put(listener
->mctx
, secret
.rstart
,
508 REGION_SIZE(secret
));
510 isccc_sexpr_free(&request
);
511 if (response
!= NULL
)
512 isccc_sexpr_free(&response
);
516 if (secret
.rstart
!= NULL
)
517 isc_mem_put(listener
->mctx
, secret
.rstart
,
518 REGION_SIZE(secret
));
519 isc_socket_detach(&conn
->sock
);
520 isccc_ccmsg_invalidate(&conn
->ccmsg
);
521 conn
->ccmsg_valid
= ISC_FALSE
;
522 maybe_free_connection(conn
);
523 maybe_free_listener(listener
);
525 isccc_sexpr_free(&request
);
526 if (response
!= NULL
)
527 isccc_sexpr_free(&response
);
531 control_timeout(isc_task_t
*task
, isc_event_t
*event
) {
532 controlconnection_t
*conn
= event
->ev_arg
;
536 isc_timer_detach(&conn
->timer
);
537 maybe_free_connection(conn
);
539 isc_event_free(&event
);
543 newconnection(controllistener_t
*listener
, isc_socket_t
*sock
) {
544 controlconnection_t
*conn
;
545 isc_interval_t interval
;
548 conn
= isc_mem_get(listener
->mctx
, sizeof(*conn
));
550 return (ISC_R_NOMEMORY
);
553 isccc_ccmsg_init(listener
->mctx
, sock
, &conn
->ccmsg
);
554 conn
->ccmsg_valid
= ISC_TRUE
;
555 conn
->sending
= ISC_FALSE
;
557 isc_interval_set(&interval
, 60, 0);
558 result
= isc_timer_create(ns_g_timermgr
, isc_timertype_once
,
559 NULL
, &interval
, listener
->task
,
560 control_timeout
, conn
, &conn
->timer
);
561 if (result
!= ISC_R_SUCCESS
)
564 conn
->listener
= listener
;
566 ISC_LINK_INIT(conn
, link
);
568 result
= isccc_ccmsg_readmessage(&conn
->ccmsg
, listener
->task
,
569 control_recvmessage
, conn
);
570 if (result
!= ISC_R_SUCCESS
)
572 isccc_ccmsg_setmaxsize(&conn
->ccmsg
, 2048);
574 ISC_LIST_APPEND(listener
->connections
, conn
, link
);
575 return (ISC_R_SUCCESS
);
578 isccc_ccmsg_invalidate(&conn
->ccmsg
);
579 if (conn
->timer
!= NULL
)
580 isc_timer_detach(&conn
->timer
);
581 isc_mem_put(listener
->mctx
, conn
, sizeof(*conn
));
586 control_newconn(isc_task_t
*task
, isc_event_t
*event
) {
587 isc_socket_newconnev_t
*nevent
= (isc_socket_newconnev_t
*)event
;
588 controllistener_t
*listener
= event
->ev_arg
;
590 isc_sockaddr_t peeraddr
;
595 listener
->listening
= ISC_FALSE
;
597 if (nevent
->result
!= ISC_R_SUCCESS
) {
598 if (nevent
->result
== ISC_R_CANCELED
) {
599 shutdown_listener(listener
);
605 sock
= nevent
->newsocket
;
606 (void)isc_socket_getpeername(sock
, &peeraddr
);
607 if (listener
->type
== isc_sockettype_tcp
&&
608 !address_ok(&peeraddr
, listener
->acl
)) {
609 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
610 isc_sockaddr_format(&peeraddr
, socktext
, sizeof(socktext
));
611 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
612 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
613 "rejected command channel message from %s",
615 isc_socket_detach(&sock
);
619 result
= newconnection(listener
, sock
);
620 if (result
!= ISC_R_SUCCESS
) {
621 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
622 isc_sockaddr_format(&peeraddr
, socktext
, sizeof(socktext
));
623 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
624 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
625 "dropped command channel from %s: %s",
626 socktext
, isc_result_totext(result
));
627 isc_socket_detach(&sock
);
632 control_next(listener
);
634 isc_event_free(&event
);
638 controls_shutdown(ns_controls_t
*controls
) {
639 controllistener_t
*listener
;
640 controllistener_t
*next
;
642 for (listener
= ISC_LIST_HEAD(controls
->listeners
);
647 * This is asynchronous. As listeners shut down, they will
648 * call their callbacks.
650 next
= ISC_LIST_NEXT(listener
, link
);
651 shutdown_listener(listener
);
656 ns_controls_shutdown(ns_controls_t
*controls
) {
657 controls_shutdown(controls
);
658 controls
->shuttingdown
= ISC_TRUE
;
662 cfgkeylist_find(const cfg_obj_t
*keylist
, const char *keyname
,
663 const cfg_obj_t
**objp
)
665 const cfg_listelt_t
*element
;
667 const cfg_obj_t
*obj
;
669 for (element
= cfg_list_first(keylist
);
671 element
= cfg_list_next(element
))
673 obj
= cfg_listelt_value(element
);
674 str
= cfg_obj_asstring(cfg_map_getname(obj
));
675 if (strcasecmp(str
, keyname
) == 0)
679 return (ISC_R_NOTFOUND
);
680 obj
= cfg_listelt_value(element
);
682 return (ISC_R_SUCCESS
);
686 controlkeylist_fromcfg(const cfg_obj_t
*keylist
, isc_mem_t
*mctx
,
687 controlkeylist_t
*keyids
)
689 const cfg_listelt_t
*element
;
692 const cfg_obj_t
*obj
;
695 for (element
= cfg_list_first(keylist
);
697 element
= cfg_list_next(element
))
699 obj
= cfg_listelt_value(element
);
700 str
= cfg_obj_asstring(obj
);
701 newstr
= isc_mem_strdup(mctx
, str
);
704 key
= isc_mem_get(mctx
, sizeof(*key
));
707 key
->keyname
= newstr
;
708 key
->secret
.base
= NULL
;
709 key
->secret
.length
= 0;
710 ISC_LINK_INIT(key
, link
);
711 ISC_LIST_APPEND(*keyids
, key
, link
);
714 return (ISC_R_SUCCESS
);
718 isc_mem_free(mctx
, newstr
);
719 free_controlkeylist(keyids
, mctx
);
720 return (ISC_R_NOMEMORY
);
724 register_keys(const cfg_obj_t
*control
, const cfg_obj_t
*keylist
,
725 controlkeylist_t
*keyids
, isc_mem_t
*mctx
, const char *socktext
)
727 controlkey_t
*keyid
, *next
;
728 const cfg_obj_t
*keydef
;
734 * Find the keys corresponding to the keyids used by this listener.
736 for (keyid
= ISC_LIST_HEAD(*keyids
); keyid
!= NULL
; keyid
= next
) {
737 next
= ISC_LIST_NEXT(keyid
, link
);
739 result
= cfgkeylist_find(keylist
, keyid
->keyname
, &keydef
);
740 if (result
!= ISC_R_SUCCESS
) {
741 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
742 "couldn't find key '%s' for use with "
743 "command channel %s",
744 keyid
->keyname
, socktext
);
745 ISC_LIST_UNLINK(*keyids
, keyid
, link
);
746 free_controlkey(keyid
, mctx
);
748 const cfg_obj_t
*algobj
= NULL
;
749 const cfg_obj_t
*secretobj
= NULL
;
750 const char *algstr
= NULL
;
751 const char *secretstr
= NULL
;
753 (void)cfg_map_get(keydef
, "algorithm", &algobj
);
754 (void)cfg_map_get(keydef
, "secret", &secretobj
);
755 INSIST(algobj
!= NULL
&& secretobj
!= NULL
);
757 algstr
= cfg_obj_asstring(algobj
);
758 secretstr
= cfg_obj_asstring(secretobj
);
760 if (ns_config_getkeyalgorithm(algstr
, NULL
, NULL
) !=
763 cfg_obj_log(control
, ns_g_lctx
,
765 "unsupported algorithm '%s' in "
766 "key '%s' for use with command "
768 algstr
, keyid
->keyname
, socktext
);
769 ISC_LIST_UNLINK(*keyids
, keyid
, link
);
770 free_controlkey(keyid
, mctx
);
774 isc_buffer_init(&b
, secret
, sizeof(secret
));
775 result
= isc_base64_decodestring(secretstr
, &b
);
777 if (result
!= ISC_R_SUCCESS
) {
778 cfg_obj_log(keydef
, ns_g_lctx
, ISC_LOG_WARNING
,
779 "secret for key '%s' on "
780 "command channel %s: %s",
781 keyid
->keyname
, socktext
,
782 isc_result_totext(result
));
783 ISC_LIST_UNLINK(*keyids
, keyid
, link
);
784 free_controlkey(keyid
, mctx
);
788 keyid
->secret
.length
= isc_buffer_usedlength(&b
);
789 keyid
->secret
.base
= isc_mem_get(mctx
,
790 keyid
->secret
.length
);
791 if (keyid
->secret
.base
== NULL
) {
792 cfg_obj_log(keydef
, ns_g_lctx
, ISC_LOG_WARNING
,
793 "couldn't register key '%s': "
794 "out of memory", keyid
->keyname
);
795 ISC_LIST_UNLINK(*keyids
, keyid
, link
);
796 free_controlkey(keyid
, mctx
);
799 memcpy(keyid
->secret
.base
, isc_buffer_base(&b
),
800 keyid
->secret
.length
);
808 if (result != ISC_R_SUCCESS) \
813 get_rndckey(isc_mem_t
*mctx
, controlkeylist_t
*keyids
) {
815 cfg_parser_t
*pctx
= NULL
;
816 cfg_obj_t
*config
= NULL
;
817 const cfg_obj_t
*key
= NULL
;
818 const cfg_obj_t
*algobj
= NULL
;
819 const cfg_obj_t
*secretobj
= NULL
;
820 const char *algstr
= NULL
;
821 const char *secretstr
= NULL
;
822 controlkey_t
*keyid
= NULL
;
826 CHECK(cfg_parser_create(mctx
, ns_g_lctx
, &pctx
));
827 CHECK(cfg_parse_file(pctx
, ns_g_keyfile
, &cfg_type_rndckey
, &config
));
828 CHECK(cfg_map_get(config
, "key", &key
));
830 keyid
= isc_mem_get(mctx
, sizeof(*keyid
));
832 CHECK(ISC_R_NOMEMORY
);
833 keyid
->keyname
= isc_mem_strdup(mctx
,
834 cfg_obj_asstring(cfg_map_getname(key
)));
835 keyid
->secret
.base
= NULL
;
836 keyid
->secret
.length
= 0;
837 ISC_LINK_INIT(keyid
, link
);
838 if (keyid
->keyname
== NULL
)
839 CHECK(ISC_R_NOMEMORY
);
841 CHECK(bind9_check_key(key
, ns_g_lctx
));
843 (void)cfg_map_get(key
, "algorithm", &algobj
);
844 (void)cfg_map_get(key
, "secret", &secretobj
);
845 INSIST(algobj
!= NULL
&& secretobj
!= NULL
);
847 algstr
= cfg_obj_asstring(algobj
);
848 secretstr
= cfg_obj_asstring(secretobj
);
850 if (ns_config_getkeyalgorithm(algstr
, NULL
, NULL
) != ISC_R_SUCCESS
) {
851 cfg_obj_log(key
, ns_g_lctx
,
853 "unsupported algorithm '%s' in "
854 "key '%s' for use with command "
856 algstr
, keyid
->keyname
);
860 isc_buffer_init(&b
, secret
, sizeof(secret
));
861 result
= isc_base64_decodestring(secretstr
, &b
);
863 if (result
!= ISC_R_SUCCESS
) {
864 cfg_obj_log(key
, ns_g_lctx
, ISC_LOG_WARNING
,
865 "secret for key '%s' on command channel: %s",
866 keyid
->keyname
, isc_result_totext(result
));
870 keyid
->secret
.length
= isc_buffer_usedlength(&b
);
871 keyid
->secret
.base
= isc_mem_get(mctx
,
872 keyid
->secret
.length
);
873 if (keyid
->secret
.base
== NULL
) {
874 cfg_obj_log(key
, ns_g_lctx
, ISC_LOG_WARNING
,
875 "couldn't register key '%s': "
876 "out of memory", keyid
->keyname
);
877 CHECK(ISC_R_NOMEMORY
);
879 memcpy(keyid
->secret
.base
, isc_buffer_base(&b
),
880 keyid
->secret
.length
);
881 ISC_LIST_APPEND(*keyids
, keyid
, link
);
883 result
= ISC_R_SUCCESS
;
887 free_controlkey(keyid
, mctx
);
889 cfg_obj_destroy(pctx
, &config
);
891 cfg_parser_destroy(&pctx
);
896 * Ensures that both '*global_keylistp' and '*control_keylistp' are
897 * valid or both are NULL.
900 get_key_info(const cfg_obj_t
*config
, const cfg_obj_t
*control
,
901 const cfg_obj_t
**global_keylistp
,
902 const cfg_obj_t
**control_keylistp
)
905 const cfg_obj_t
*control_keylist
= NULL
;
906 const cfg_obj_t
*global_keylist
= NULL
;
908 REQUIRE(global_keylistp
!= NULL
&& *global_keylistp
== NULL
);
909 REQUIRE(control_keylistp
!= NULL
&& *control_keylistp
== NULL
);
911 control_keylist
= cfg_tuple_get(control
, "keys");
913 if (!cfg_obj_isvoid(control_keylist
) &&
914 cfg_list_first(control_keylist
) != NULL
) {
915 result
= cfg_map_get(config
, "key", &global_keylist
);
917 if (result
== ISC_R_SUCCESS
) {
918 *global_keylistp
= global_keylist
;
919 *control_keylistp
= control_keylist
;
925 update_listener(ns_controls_t
*cp
, controllistener_t
**listenerp
,
926 const cfg_obj_t
*control
, const cfg_obj_t
*config
,
927 isc_sockaddr_t
*addr
, cfg_aclconfctx_t
*aclconfctx
,
928 const char *socktext
, isc_sockettype_t type
)
930 controllistener_t
*listener
;
931 const cfg_obj_t
*allow
;
932 const cfg_obj_t
*global_keylist
= NULL
;
933 const cfg_obj_t
*control_keylist
= NULL
;
934 dns_acl_t
*new_acl
= NULL
;
935 controlkeylist_t keys
;
936 isc_result_t result
= ISC_R_SUCCESS
;
938 for (listener
= ISC_LIST_HEAD(cp
->listeners
);
940 listener
= ISC_LIST_NEXT(listener
, link
))
941 if (isc_sockaddr_equal(addr
, &listener
->address
))
944 if (listener
== NULL
) {
950 * There is already a listener for this sockaddr.
951 * Update the access list and key information.
953 * First try to deal with the key situation. There are a few
955 * (a) It had an explicit keylist and still has an explicit keylist.
956 * (b) It had an automagic key and now has an explicit keylist.
957 * (c) It had an explicit keylist and now needs an automagic key.
958 * (d) It has an automagic key and still needs the automagic key.
960 * (c) and (d) are the annoying ones. The caller needs to know
961 * that it should use the automagic configuration for key information
962 * in place of the named.conf configuration.
964 * XXXDCL There is one other hazard that has not been dealt with,
965 * the problem that if a key change is being caused by a control
966 * channel reload, then the response will be with the new key
967 * and not able to be decrypted by the client.
970 get_key_info(config
, control
, &global_keylist
,
973 if (control_keylist
!= NULL
) {
974 INSIST(global_keylist
!= NULL
);
977 result
= controlkeylist_fromcfg(control_keylist
,
978 listener
->mctx
, &keys
);
979 if (result
== ISC_R_SUCCESS
) {
980 free_controlkeylist(&listener
->keys
, listener
->mctx
);
981 listener
->keys
= keys
;
982 register_keys(control
, global_keylist
, &listener
->keys
,
983 listener
->mctx
, socktext
);
986 free_controlkeylist(&listener
->keys
, listener
->mctx
);
987 result
= get_rndckey(listener
->mctx
, &listener
->keys
);
990 if (result
!= ISC_R_SUCCESS
&& global_keylist
!= NULL
) {
992 * This message might be a little misleading since the
993 * "new keys" might in fact be identical to the old ones,
994 * but tracking whether they are identical just for the
995 * sake of avoiding this message would be too much trouble.
998 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
999 "couldn't install new keys for "
1000 "command channel %s: %s",
1001 socktext
, isc_result_totext(result
));
1003 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
1004 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
1005 "couldn't install new keys for "
1006 "command channel %s: %s",
1007 socktext
, isc_result_totext(result
));
1011 * Now, keep the old access list unless a new one can be made.
1013 if (control
!= NULL
&& type
== isc_sockettype_tcp
) {
1014 allow
= cfg_tuple_get(control
, "allow");
1015 result
= cfg_acl_fromconfig(allow
, config
, ns_g_lctx
,
1016 aclconfctx
, listener
->mctx
,
1019 result
= dns_acl_any(listener
->mctx
, &new_acl
);
1022 if (result
== ISC_R_SUCCESS
) {
1023 dns_acl_detach(&listener
->acl
);
1024 dns_acl_attach(new_acl
, &listener
->acl
);
1025 dns_acl_detach(&new_acl
);
1026 /* XXXDCL say the old acl is still used? */
1027 } else if (control
!= NULL
)
1028 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
1029 "couldn't install new acl for "
1030 "command channel %s: %s",
1031 socktext
, isc_result_totext(result
));
1033 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
1034 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
1035 "couldn't install new acl for "
1036 "command channel %s: %s",
1037 socktext
, isc_result_totext(result
));
1039 if (result
== ISC_R_SUCCESS
&& type
== isc_sockettype_unix
) {
1040 isc_uint32_t perm
, owner
, group
;
1041 perm
= cfg_obj_asuint32(cfg_tuple_get(control
, "perm"));
1042 owner
= cfg_obj_asuint32(cfg_tuple_get(control
, "owner"));
1043 group
= cfg_obj_asuint32(cfg_tuple_get(control
, "group"));
1044 result
= ISC_R_SUCCESS
;
1045 if (listener
->perm
!= perm
|| listener
->owner
!= owner
||
1046 listener
->group
!= group
)
1047 result
= isc_socket_permunix(&listener
->address
, perm
,
1049 if (result
== ISC_R_SUCCESS
) {
1050 listener
->perm
= perm
;
1051 listener
->owner
= owner
;
1052 listener
->group
= group
;
1053 } else if (control
!= NULL
)
1054 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
1055 "couldn't update ownership/permission for "
1056 "command channel %s", socktext
);
1059 *listenerp
= listener
;
1063 add_listener(ns_controls_t
*cp
, controllistener_t
**listenerp
,
1064 const cfg_obj_t
*control
, const cfg_obj_t
*config
,
1065 isc_sockaddr_t
*addr
, cfg_aclconfctx_t
*aclconfctx
,
1066 const char *socktext
, isc_sockettype_t type
)
1068 isc_mem_t
*mctx
= cp
->server
->mctx
;
1069 controllistener_t
*listener
;
1070 const cfg_obj_t
*allow
;
1071 const cfg_obj_t
*global_keylist
= NULL
;
1072 const cfg_obj_t
*control_keylist
= NULL
;
1073 dns_acl_t
*new_acl
= NULL
;
1074 isc_result_t result
= ISC_R_SUCCESS
;
1076 listener
= isc_mem_get(mctx
, sizeof(*listener
));
1077 if (listener
== NULL
)
1078 result
= ISC_R_NOMEMORY
;
1080 if (result
== ISC_R_SUCCESS
) {
1081 listener
->controls
= cp
;
1082 listener
->mctx
= mctx
;
1083 listener
->task
= cp
->server
->task
;
1084 listener
->address
= *addr
;
1085 listener
->sock
= NULL
;
1086 listener
->listening
= ISC_FALSE
;
1087 listener
->exiting
= ISC_FALSE
;
1088 listener
->acl
= NULL
;
1089 listener
->type
= type
;
1091 listener
->owner
= 0;
1092 listener
->group
= 0;
1093 ISC_LINK_INIT(listener
, link
);
1094 ISC_LIST_INIT(listener
->keys
);
1095 ISC_LIST_INIT(listener
->connections
);
1100 if (control
!= NULL
&& type
== isc_sockettype_tcp
) {
1101 allow
= cfg_tuple_get(control
, "allow");
1102 result
= cfg_acl_fromconfig(allow
, config
, ns_g_lctx
,
1103 aclconfctx
, mctx
, &new_acl
);
1105 result
= dns_acl_any(mctx
, &new_acl
);
1109 if (result
== ISC_R_SUCCESS
) {
1110 dns_acl_attach(new_acl
, &listener
->acl
);
1111 dns_acl_detach(&new_acl
);
1114 get_key_info(config
, control
, &global_keylist
,
1117 if (control_keylist
!= NULL
) {
1118 result
= controlkeylist_fromcfg(control_keylist
,
1121 if (result
== ISC_R_SUCCESS
)
1122 register_keys(control
, global_keylist
,
1124 listener
->mctx
, socktext
);
1126 result
= get_rndckey(mctx
, &listener
->keys
);
1128 if (result
!= ISC_R_SUCCESS
&& control
!= NULL
)
1129 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
1130 "couldn't install keys for "
1131 "command channel %s: %s",
1132 socktext
, isc_result_totext(result
));
1135 if (result
== ISC_R_SUCCESS
) {
1136 int pf
= isc_sockaddr_pf(&listener
->address
);
1137 if ((pf
== AF_INET
&& isc_net_probeipv4() != ISC_R_SUCCESS
) ||
1138 #ifdef ISC_PLATFORM_HAVESYSUNH
1139 (pf
== AF_UNIX
&& isc_net_probeunix() != ISC_R_SUCCESS
) ||
1141 (pf
== AF_INET6
&& isc_net_probeipv6() != ISC_R_SUCCESS
))
1142 result
= ISC_R_FAMILYNOSUPPORT
;
1145 if (result
== ISC_R_SUCCESS
&& type
== isc_sockettype_unix
)
1146 isc_socket_cleanunix(&listener
->address
, ISC_FALSE
);
1148 if (result
== ISC_R_SUCCESS
)
1149 result
= isc_socket_create(ns_g_socketmgr
,
1150 isc_sockaddr_pf(&listener
->address
),
1151 type
, &listener
->sock
);
1153 if (result
== ISC_R_SUCCESS
)
1154 result
= isc_socket_bind(listener
->sock
, &listener
->address
,
1155 ISC_SOCKET_REUSEADDRESS
);
1157 if (result
== ISC_R_SUCCESS
&& type
== isc_sockettype_unix
) {
1158 listener
->perm
= cfg_obj_asuint32(cfg_tuple_get(control
,
1160 listener
->owner
= cfg_obj_asuint32(cfg_tuple_get(control
,
1162 listener
->group
= cfg_obj_asuint32(cfg_tuple_get(control
,
1164 result
= isc_socket_permunix(&listener
->address
, listener
->perm
,
1165 listener
->owner
, listener
->group
);
1167 if (result
== ISC_R_SUCCESS
)
1168 result
= control_listen(listener
);
1170 if (result
== ISC_R_SUCCESS
)
1171 result
= control_accept(listener
);
1173 if (result
== ISC_R_SUCCESS
) {
1174 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
1175 NS_LOGMODULE_CONTROL
, ISC_LOG_NOTICE
,
1176 "command channel listening on %s", socktext
);
1177 *listenerp
= listener
;
1180 if (listener
!= NULL
) {
1181 listener
->exiting
= ISC_TRUE
;
1182 free_listener(listener
);
1185 if (control
!= NULL
)
1186 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
1187 "couldn't add command channel %s: %s",
1188 socktext
, isc_result_totext(result
));
1190 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
1191 NS_LOGMODULE_CONTROL
, ISC_LOG_NOTICE
,
1192 "couldn't add command channel %s: %s",
1193 socktext
, isc_result_totext(result
));
1198 /* XXXDCL return error results? fail hard? */
1202 ns_controls_configure(ns_controls_t
*cp
, const cfg_obj_t
*config
,
1203 cfg_aclconfctx_t
*aclconfctx
)
1205 controllistener_t
*listener
;
1206 controllistenerlist_t new_listeners
;
1207 const cfg_obj_t
*controlslist
= NULL
;
1208 const cfg_listelt_t
*element
, *element2
;
1209 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
1211 ISC_LIST_INIT(new_listeners
);
1214 * Get the list of named.conf 'controls' statements.
1216 (void)cfg_map_get(config
, "controls", &controlslist
);
1219 * Run through the new control channel list, noting sockets that
1220 * are already being listened on and moving them to the new list.
1222 * Identifying duplicate addr/port combinations is left to either
1223 * the underlying config code, or to the bind attempt getting an
1224 * address-in-use error.
1226 if (controlslist
!= NULL
) {
1227 for (element
= cfg_list_first(controlslist
);
1229 element
= cfg_list_next(element
)) {
1230 const cfg_obj_t
*controls
;
1231 const cfg_obj_t
*inetcontrols
= NULL
;
1233 controls
= cfg_listelt_value(element
);
1234 (void)cfg_map_get(controls
, "inet", &inetcontrols
);
1235 if (inetcontrols
== NULL
)
1238 for (element2
= cfg_list_first(inetcontrols
);
1240 element2
= cfg_list_next(element2
)) {
1241 const cfg_obj_t
*control
;
1242 const cfg_obj_t
*obj
;
1243 isc_sockaddr_t addr
;
1246 * The parser handles BIND 8 configuration file
1247 * syntax, so it allows unix phrases as well
1248 * inet phrases with no keys{} clause.
1250 control
= cfg_listelt_value(element2
);
1252 obj
= cfg_tuple_get(control
, "address");
1253 addr
= *cfg_obj_assockaddr(obj
);
1254 if (isc_sockaddr_getport(&addr
) == 0)
1255 isc_sockaddr_setport(&addr
,
1258 isc_sockaddr_format(&addr
, socktext
,
1261 isc_log_write(ns_g_lctx
,
1262 NS_LOGCATEGORY_GENERAL
,
1263 NS_LOGMODULE_CONTROL
,
1265 "processing control channel %s",
1268 update_listener(cp
, &listener
, control
, config
,
1269 &addr
, aclconfctx
, socktext
,
1270 isc_sockettype_tcp
);
1272 if (listener
!= NULL
)
1274 * Remove the listener from the old
1275 * list, so it won't be shut down.
1277 ISC_LIST_UNLINK(cp
->listeners
,
1281 * This is a new listener.
1283 add_listener(cp
, &listener
, control
,
1284 config
, &addr
, aclconfctx
,
1286 isc_sockettype_tcp
);
1288 if (listener
!= NULL
)
1289 ISC_LIST_APPEND(new_listeners
,
1293 for (element
= cfg_list_first(controlslist
);
1295 element
= cfg_list_next(element
)) {
1296 const cfg_obj_t
*controls
;
1297 const cfg_obj_t
*unixcontrols
= NULL
;
1299 controls
= cfg_listelt_value(element
);
1300 (void)cfg_map_get(controls
, "unix", &unixcontrols
);
1301 if (unixcontrols
== NULL
)
1304 for (element2
= cfg_list_first(unixcontrols
);
1306 element2
= cfg_list_next(element2
)) {
1307 const cfg_obj_t
*control
;
1308 const cfg_obj_t
*path
;
1309 isc_sockaddr_t addr
;
1310 isc_result_t result
;
1313 * The parser handles BIND 8 configuration file
1314 * syntax, so it allows unix phrases as well
1315 * inet phrases with no keys{} clause.
1317 control
= cfg_listelt_value(element2
);
1319 path
= cfg_tuple_get(control
, "path");
1320 result
= isc_sockaddr_frompath(&addr
,
1321 cfg_obj_asstring(path
));
1322 if (result
!= ISC_R_SUCCESS
) {
1323 isc_log_write(ns_g_lctx
,
1324 NS_LOGCATEGORY_GENERAL
,
1325 NS_LOGMODULE_CONTROL
,
1327 "control channel '%s': %s",
1328 cfg_obj_asstring(path
),
1329 isc_result_totext(result
));
1333 isc_log_write(ns_g_lctx
,
1334 NS_LOGCATEGORY_GENERAL
,
1335 NS_LOGMODULE_CONTROL
,
1337 "processing control channel '%s'",
1338 cfg_obj_asstring(path
));
1340 update_listener(cp
, &listener
, control
, config
,
1342 cfg_obj_asstring(path
),
1343 isc_sockettype_unix
);
1345 if (listener
!= NULL
)
1347 * Remove the listener from the old
1348 * list, so it won't be shut down.
1350 ISC_LIST_UNLINK(cp
->listeners
,
1354 * This is a new listener.
1356 add_listener(cp
, &listener
, control
,
1357 config
, &addr
, aclconfctx
,
1358 cfg_obj_asstring(path
),
1359 isc_sockettype_unix
);
1361 if (listener
!= NULL
)
1362 ISC_LIST_APPEND(new_listeners
,
1369 for (i
= 0; i
< 2; i
++) {
1370 isc_sockaddr_t addr
;
1373 struct in_addr localhost
;
1375 if (isc_net_probeipv4() != ISC_R_SUCCESS
)
1377 localhost
.s_addr
= htonl(INADDR_LOOPBACK
);
1378 isc_sockaddr_fromin(&addr
, &localhost
, 0);
1380 if (isc_net_probeipv6() != ISC_R_SUCCESS
)
1382 isc_sockaddr_fromin6(&addr
,
1383 &in6addr_loopback
, 0);
1385 isc_sockaddr_setport(&addr
, NS_CONTROL_PORT
);
1387 isc_sockaddr_format(&addr
, socktext
, sizeof(socktext
));
1389 update_listener(cp
, &listener
, NULL
, NULL
,
1390 &addr
, NULL
, socktext
,
1391 isc_sockettype_tcp
);
1393 if (listener
!= NULL
)
1395 * Remove the listener from the old
1396 * list, so it won't be shut down.
1398 ISC_LIST_UNLINK(cp
->listeners
,
1402 * This is a new listener.
1404 add_listener(cp
, &listener
, NULL
, NULL
,
1405 &addr
, NULL
, socktext
,
1406 isc_sockettype_tcp
);
1408 if (listener
!= NULL
)
1409 ISC_LIST_APPEND(new_listeners
,
1415 * ns_control_shutdown() will stop whatever is on the global
1416 * listeners list, which currently only has whatever sockaddrs
1417 * were in the previous configuration (if any) that do not
1418 * remain in the current configuration.
1420 controls_shutdown(cp
);
1423 * Put all of the valid listeners on the listeners list.
1424 * Anything already on listeners in the process of shutting
1425 * down will be taken care of by listen_done().
1427 ISC_LIST_APPENDLIST(cp
->listeners
, new_listeners
, link
);
1428 return (ISC_R_SUCCESS
);
1432 ns_controls_create(ns_server_t
*server
, ns_controls_t
**ctrlsp
) {
1433 isc_mem_t
*mctx
= server
->mctx
;
1434 isc_result_t result
;
1435 ns_controls_t
*controls
= isc_mem_get(mctx
, sizeof(*controls
));
1437 if (controls
== NULL
)
1438 return (ISC_R_NOMEMORY
);
1439 controls
->server
= server
;
1440 ISC_LIST_INIT(controls
->listeners
);
1441 controls
->shuttingdown
= ISC_FALSE
;
1442 controls
->symtab
= NULL
;
1443 result
= isccc_cc_createsymtab(&controls
->symtab
);
1444 if (result
!= ISC_R_SUCCESS
) {
1445 isc_mem_put(server
->mctx
, controls
, sizeof(*controls
));
1449 return (ISC_R_SUCCESS
);
1453 ns_controls_destroy(ns_controls_t
**ctrlsp
) {
1454 ns_controls_t
*controls
= *ctrlsp
;
1456 REQUIRE(ISC_LIST_EMPTY(controls
->listeners
));
1458 isccc_symtab_destroy(&controls
->symtab
);
1459 isc_mem_put(controls
->server
->mctx
, controls
, sizeof(*controls
));