4 * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2001-2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: controlconf.c,v 1.60 2008/07/23 23:27:54 marka Exp */
26 #include <isc/base64.h>
27 #include <isc/buffer.h>
28 #include <isc/event.h>
31 #include <isc/netaddr.h>
32 #include <isc/random.h>
33 #include <isc/result.h>
34 #include <isc/stdtime.h>
35 #include <isc/string.h>
36 #include <isc/timer.h>
39 #include <isccfg/namedconf.h>
41 #include <bind9/check.h>
43 #include <isccc/alist.h>
45 #include <isccc/ccmsg.h>
46 #include <isccc/events.h>
47 #include <isccc/result.h>
48 #include <isccc/sexpr.h>
49 #include <isccc/symtab.h>
50 #include <isccc/util.h>
52 #include <dns/result.h>
54 #include <named/config.h>
55 #include <named/control.h>
56 #include <named/log.h>
57 #include <named/server.h>
60 * Note: Listeners and connections are not locked. All event handlers are
61 * executed by the server task, and all callers of exported routines must
62 * be running under the server task.
65 typedef struct controlkey controlkey_t
;
66 typedef ISC_LIST(controlkey_t
) controlkeylist_t
;
68 typedef struct controlconnection controlconnection_t
;
69 typedef ISC_LIST(controlconnection_t
) controlconnectionlist_t
;
71 typedef struct controllistener controllistener_t
;
72 typedef ISC_LIST(controllistener_t
) controllistenerlist_t
;
77 ISC_LINK(controlkey_t
) link
;
80 struct controlconnection
{
83 isc_boolean_t ccmsg_valid
;
84 isc_boolean_t sending
;
86 unsigned char buffer
[2048];
87 controllistener_t
* listener
;
89 ISC_LINK(controlconnection_t
) link
;
92 struct controllistener
{
93 ns_controls_t
* controls
;
96 isc_sockaddr_t address
;
99 isc_boolean_t listening
;
100 isc_boolean_t exiting
;
101 controlkeylist_t keys
;
102 controlconnectionlist_t connections
;
103 isc_sockettype_t type
;
107 ISC_LINK(controllistener_t
) link
;
112 controllistenerlist_t listeners
;
113 isc_boolean_t shuttingdown
;
114 isccc_symtab_t
*symtab
;
117 static void control_newconn(isc_task_t
*task
, isc_event_t
*event
);
118 static void control_recvmessage(isc_task_t
*task
, isc_event_t
*event
);
120 #define CLOCKSKEW 300
123 free_controlkey(controlkey_t
*key
, isc_mem_t
*mctx
) {
124 if (key
->keyname
!= NULL
)
125 isc_mem_free(mctx
, key
->keyname
);
126 if (key
->secret
.base
!= NULL
)
127 isc_mem_put(mctx
, key
->secret
.base
, key
->secret
.length
);
128 isc_mem_put(mctx
, key
, sizeof(*key
));
132 free_controlkeylist(controlkeylist_t
*keylist
, isc_mem_t
*mctx
) {
133 while (!ISC_LIST_EMPTY(*keylist
)) {
134 controlkey_t
*key
= ISC_LIST_HEAD(*keylist
);
135 ISC_LIST_UNLINK(*keylist
, key
, link
);
136 free_controlkey(key
, mctx
);
141 free_listener(controllistener_t
*listener
) {
142 INSIST(listener
->exiting
);
143 INSIST(!listener
->listening
);
144 INSIST(ISC_LIST_EMPTY(listener
->connections
));
146 if (listener
->sock
!= NULL
)
147 isc_socket_detach(&listener
->sock
);
149 free_controlkeylist(&listener
->keys
, listener
->mctx
);
151 if (listener
->acl
!= NULL
)
152 dns_acl_detach(&listener
->acl
);
154 isc_mem_put(listener
->mctx
, listener
, sizeof(*listener
));
158 maybe_free_listener(controllistener_t
*listener
) {
159 if (listener
->exiting
&&
160 !listener
->listening
&&
161 ISC_LIST_EMPTY(listener
->connections
))
162 free_listener(listener
);
166 maybe_free_connection(controlconnection_t
*conn
) {
167 controllistener_t
*listener
= conn
->listener
;
169 if (conn
->timer
!= NULL
)
170 isc_timer_detach(&conn
->timer
);
172 if (conn
->ccmsg_valid
) {
173 isccc_ccmsg_cancelread(&conn
->ccmsg
);
178 isc_socket_cancel(conn
->sock
, listener
->task
,
179 ISC_SOCKCANCEL_SEND
);
183 ISC_LIST_UNLINK(listener
->connections
, conn
, link
);
184 isc_mem_put(listener
->mctx
, conn
, sizeof(*conn
));
188 shutdown_listener(controllistener_t
*listener
) {
189 controlconnection_t
*conn
;
190 controlconnection_t
*next
;
192 if (!listener
->exiting
) {
193 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
195 ISC_LIST_UNLINK(listener
->controls
->listeners
, listener
, link
);
197 isc_sockaddr_format(&listener
->address
, socktext
,
199 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
200 NS_LOGMODULE_CONTROL
, ISC_LOG_NOTICE
,
201 "stopping command channel on %s", socktext
);
202 if (listener
->type
== isc_sockettype_unix
)
203 isc_socket_cleanunix(&listener
->address
, ISC_TRUE
);
204 listener
->exiting
= ISC_TRUE
;
207 for (conn
= ISC_LIST_HEAD(listener
->connections
);
211 next
= ISC_LIST_NEXT(conn
, link
);
212 maybe_free_connection(conn
);
215 if (listener
->listening
)
216 isc_socket_cancel(listener
->sock
, listener
->task
,
217 ISC_SOCKCANCEL_ACCEPT
);
219 maybe_free_listener(listener
);
223 address_ok(isc_sockaddr_t
*sockaddr
, dns_acl_t
*acl
) {
224 isc_netaddr_t netaddr
;
228 isc_netaddr_fromsockaddr(&netaddr
, sockaddr
);
230 result
= dns_acl_match(&netaddr
, NULL
, acl
,
231 &ns_g_server
->aclenv
, &match
, NULL
);
233 if (result
!= ISC_R_SUCCESS
|| match
<= 0)
240 control_accept(controllistener_t
*listener
) {
242 result
= isc_socket_accept(listener
->sock
,
244 control_newconn
, listener
);
245 if (result
!= ISC_R_SUCCESS
)
246 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
247 "isc_socket_accept() failed: %s",
248 isc_result_totext(result
));
250 listener
->listening
= ISC_TRUE
;
255 control_listen(controllistener_t
*listener
) {
258 result
= isc_socket_listen(listener
->sock
, 0);
259 if (result
!= ISC_R_SUCCESS
)
260 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
261 "isc_socket_listen() failed: %s",
262 isc_result_totext(result
));
267 control_next(controllistener_t
*listener
) {
268 (void)control_accept(listener
);
272 control_senddone(isc_task_t
*task
, isc_event_t
*event
) {
273 isc_socketevent_t
*sevent
= (isc_socketevent_t
*) event
;
274 controlconnection_t
*conn
= event
->ev_arg
;
275 controllistener_t
*listener
= conn
->listener
;
276 isc_socket_t
*sock
= (isc_socket_t
*)sevent
->ev_sender
;
279 REQUIRE(conn
->sending
);
283 conn
->sending
= ISC_FALSE
;
285 if (sevent
->result
!= ISC_R_SUCCESS
&&
286 sevent
->result
!= ISC_R_CANCELED
)
288 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
289 isc_sockaddr_t peeraddr
;
291 (void)isc_socket_getpeername(sock
, &peeraddr
);
292 isc_sockaddr_format(&peeraddr
, socktext
, sizeof(socktext
));
293 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
294 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
295 "error sending command response to %s: %s",
296 socktext
, isc_result_totext(sevent
->result
));
298 isc_event_free(&event
);
300 result
= isccc_ccmsg_readmessage(&conn
->ccmsg
, listener
->task
,
301 control_recvmessage
, conn
);
302 if (result
!= ISC_R_SUCCESS
) {
303 isc_socket_detach(&conn
->sock
);
304 maybe_free_connection(conn
);
305 maybe_free_listener(listener
);
310 log_invalid(isccc_ccmsg_t
*ccmsg
, isc_result_t result
) {
311 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
312 isc_sockaddr_t peeraddr
;
314 (void)isc_socket_getpeername(ccmsg
->sock
, &peeraddr
);
315 isc_sockaddr_format(&peeraddr
, socktext
, sizeof(socktext
));
316 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
317 NS_LOGMODULE_CONTROL
, ISC_LOG_ERROR
,
318 "invalid command from %s: %s",
319 socktext
, isc_result_totext(result
));
323 control_recvmessage(isc_task_t
*task
, isc_event_t
*event
) {
324 controlconnection_t
*conn
;
325 controllistener_t
*listener
;
327 isccc_sexpr_t
*request
= NULL
;
328 isccc_sexpr_t
*response
= NULL
;
329 isccc_region_t ccregion
;
330 isccc_region_t secret
;
336 char textarray
[1024];
338 isc_result_t eresult
;
339 isccc_sexpr_t
*_ctrl
;
344 REQUIRE(event
->ev_type
== ISCCC_EVENT_CCMSG
);
346 conn
= event
->ev_arg
;
347 listener
= conn
->listener
;
348 secret
.rstart
= NULL
;
350 /* Is the server shutting down? */
351 if (listener
->controls
->shuttingdown
)
354 if (conn
->ccmsg
.result
!= ISC_R_SUCCESS
) {
355 if (conn
->ccmsg
.result
!= ISC_R_CANCELED
&&
356 conn
->ccmsg
.result
!= ISC_R_EOF
)
357 log_invalid(&conn
->ccmsg
, conn
->ccmsg
.result
);
363 for (key
= ISC_LIST_HEAD(listener
->keys
);
365 key
= ISC_LIST_NEXT(key
, link
))
367 ccregion
.rstart
= isc_buffer_base(&conn
->ccmsg
.buffer
);
368 ccregion
.rend
= isc_buffer_used(&conn
->ccmsg
.buffer
);
369 secret
.rstart
= isc_mem_get(listener
->mctx
, key
->secret
.length
);
370 if (secret
.rstart
== NULL
)
372 memcpy(secret
.rstart
, key
->secret
.base
, key
->secret
.length
);
373 secret
.rend
= secret
.rstart
+ key
->secret
.length
;
374 result
= isccc_cc_fromwire(&ccregion
, &request
, &secret
);
375 if (result
== ISC_R_SUCCESS
)
377 isc_mem_put(listener
->mctx
, secret
.rstart
, REGION_SIZE(secret
));
378 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
);
399 goto cleanup_request
;
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
);
410 goto cleanup_request
;
413 if (isccc_cc_lookupuint32(_ctrl
, "_tim", &sent
) == ISC_R_SUCCESS
) {
414 if ((sent
+ CLOCKSKEW
) < now
|| (sent
- CLOCKSKEW
) > now
) {
415 log_invalid(&conn
->ccmsg
, ISCCC_R_CLOCKSKEW
);
416 goto cleanup_request
;
419 log_invalid(&conn
->ccmsg
, ISC_R_FAILURE
);
420 goto cleanup_request
;
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
);
429 goto cleanup_request
;
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
);
441 goto cleanup_request
;
444 if (conn
->nonce
!= 0 &&
445 (isccc_cc_lookupuint32(_ctrl
, "_nonce", &nonce
) != ISC_R_SUCCESS
||
446 conn
->nonce
!= nonce
)) {
447 log_invalid(&conn
->ccmsg
, ISCCC_R_BADAUTH
);
448 goto cleanup_request
;
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
)
462 goto cleanup_request
;
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
)
470 goto cleanup_response
;
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
)
481 goto cleanup_response
;
485 _ctrl
= isccc_alist_lookup(response
, "_ctrl");
487 isccc_cc_defineuint32(_ctrl
, "_nonce", conn
->nonce
) == NULL
)
488 goto cleanup_response
;
490 ccregion
.rstart
= conn
->buffer
+ 4;
491 ccregion
.rend
= conn
->buffer
+ sizeof(conn
->buffer
);
492 result
= isccc_cc_towire(response
, &ccregion
, &secret
);
493 if (result
!= ISC_R_SUCCESS
)
494 goto cleanup_response
;
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
)
503 goto cleanup_response
;
504 conn
->sending
= ISC_TRUE
;
506 isc_mem_put(listener
->mctx
, secret
.rstart
, REGION_SIZE(secret
));
507 isccc_sexpr_free(&request
);
508 isccc_sexpr_free(&response
);
512 isccc_sexpr_free(&response
);
515 isccc_sexpr_free(&request
);
516 isc_mem_put(listener
->mctx
, secret
.rstart
, 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
);
527 control_timeout(isc_task_t
*task
, isc_event_t
*event
) {
528 controlconnection_t
*conn
= event
->ev_arg
;
532 isc_timer_detach(&conn
->timer
);
533 maybe_free_connection(conn
);
535 isc_event_free(&event
);
539 newconnection(controllistener_t
*listener
, isc_socket_t
*sock
) {
540 controlconnection_t
*conn
;
541 isc_interval_t interval
;
544 conn
= isc_mem_get(listener
->mctx
, sizeof(*conn
));
546 return (ISC_R_NOMEMORY
);
549 isccc_ccmsg_init(listener
->mctx
, sock
, &conn
->ccmsg
);
550 conn
->ccmsg_valid
= ISC_TRUE
;
551 conn
->sending
= ISC_FALSE
;
553 isc_interval_set(&interval
, 60, 0);
554 result
= isc_timer_create(ns_g_timermgr
, isc_timertype_once
,
555 NULL
, &interval
, listener
->task
,
556 control_timeout
, conn
, &conn
->timer
);
557 if (result
!= ISC_R_SUCCESS
)
560 conn
->listener
= listener
;
562 ISC_LINK_INIT(conn
, link
);
564 result
= isccc_ccmsg_readmessage(&conn
->ccmsg
, listener
->task
,
565 control_recvmessage
, conn
);
566 if (result
!= ISC_R_SUCCESS
)
568 isccc_ccmsg_setmaxsize(&conn
->ccmsg
, 2048);
570 ISC_LIST_APPEND(listener
->connections
, conn
, link
);
571 return (ISC_R_SUCCESS
);
574 isccc_ccmsg_invalidate(&conn
->ccmsg
);
575 if (conn
->timer
!= NULL
)
576 isc_timer_detach(&conn
->timer
);
577 isc_mem_put(listener
->mctx
, conn
, sizeof(*conn
));
582 control_newconn(isc_task_t
*task
, isc_event_t
*event
) {
583 isc_socket_newconnev_t
*nevent
= (isc_socket_newconnev_t
*)event
;
584 controllistener_t
*listener
= event
->ev_arg
;
586 isc_sockaddr_t peeraddr
;
591 listener
->listening
= ISC_FALSE
;
593 if (nevent
->result
!= ISC_R_SUCCESS
) {
594 if (nevent
->result
== ISC_R_CANCELED
) {
595 shutdown_listener(listener
);
601 sock
= nevent
->newsocket
;
602 isc_socket_setname(sock
, "control", NULL
);
603 (void)isc_socket_getpeername(sock
, &peeraddr
);
604 if (listener
->type
== isc_sockettype_tcp
&&
605 !address_ok(&peeraddr
, listener
->acl
)) {
606 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
607 isc_sockaddr_format(&peeraddr
, socktext
, sizeof(socktext
));
608 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
609 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
610 "rejected command channel message from %s",
612 isc_socket_detach(&sock
);
616 result
= newconnection(listener
, sock
);
617 if (result
!= ISC_R_SUCCESS
) {
618 char socktext
[ISC_SOCKADDR_FORMATSIZE
];
619 isc_sockaddr_format(&peeraddr
, socktext
, sizeof(socktext
));
620 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
621 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
622 "dropped command channel from %s: %s",
623 socktext
, isc_result_totext(result
));
624 isc_socket_detach(&sock
);
629 control_next(listener
);
631 isc_event_free(&event
);
635 controls_shutdown(ns_controls_t
*controls
) {
636 controllistener_t
*listener
;
637 controllistener_t
*next
;
639 for (listener
= ISC_LIST_HEAD(controls
->listeners
);
644 * This is asynchronous. As listeners shut down, they will
645 * call their callbacks.
647 next
= ISC_LIST_NEXT(listener
, link
);
648 shutdown_listener(listener
);
653 ns_controls_shutdown(ns_controls_t
*controls
) {
654 controls_shutdown(controls
);
655 controls
->shuttingdown
= ISC_TRUE
;
659 cfgkeylist_find(const cfg_obj_t
*keylist
, const char *keyname
,
660 const cfg_obj_t
**objp
)
662 const cfg_listelt_t
*element
;
664 const cfg_obj_t
*obj
;
666 for (element
= cfg_list_first(keylist
);
668 element
= cfg_list_next(element
))
670 obj
= cfg_listelt_value(element
);
671 str
= cfg_obj_asstring(cfg_map_getname(obj
));
672 if (strcasecmp(str
, keyname
) == 0)
676 return (ISC_R_NOTFOUND
);
677 obj
= cfg_listelt_value(element
);
679 return (ISC_R_SUCCESS
);
683 controlkeylist_fromcfg(const cfg_obj_t
*keylist
, isc_mem_t
*mctx
,
684 controlkeylist_t
*keyids
)
686 const cfg_listelt_t
*element
;
689 const cfg_obj_t
*obj
;
692 for (element
= cfg_list_first(keylist
);
694 element
= cfg_list_next(element
))
696 obj
= cfg_listelt_value(element
);
697 str
= cfg_obj_asstring(obj
);
698 newstr
= isc_mem_strdup(mctx
, str
);
701 key
= isc_mem_get(mctx
, sizeof(*key
));
704 key
->keyname
= newstr
;
705 key
->secret
.base
= NULL
;
706 key
->secret
.length
= 0;
707 ISC_LINK_INIT(key
, link
);
708 ISC_LIST_APPEND(*keyids
, key
, link
);
711 return (ISC_R_SUCCESS
);
715 isc_mem_free(mctx
, newstr
);
716 free_controlkeylist(keyids
, mctx
);
717 return (ISC_R_NOMEMORY
);
721 register_keys(const cfg_obj_t
*control
, const cfg_obj_t
*keylist
,
722 controlkeylist_t
*keyids
, isc_mem_t
*mctx
, const char *socktext
)
724 controlkey_t
*keyid
, *next
;
725 const cfg_obj_t
*keydef
;
731 * Find the keys corresponding to the keyids used by this listener.
733 for (keyid
= ISC_LIST_HEAD(*keyids
); keyid
!= NULL
; keyid
= next
) {
734 next
= ISC_LIST_NEXT(keyid
, link
);
736 result
= cfgkeylist_find(keylist
, keyid
->keyname
, &keydef
);
737 if (result
!= ISC_R_SUCCESS
) {
738 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
739 "couldn't find key '%s' for use with "
740 "command channel %s",
741 keyid
->keyname
, socktext
);
742 ISC_LIST_UNLINK(*keyids
, keyid
, link
);
743 free_controlkey(keyid
, mctx
);
745 const cfg_obj_t
*algobj
= NULL
;
746 const cfg_obj_t
*secretobj
= NULL
;
747 const char *algstr
= NULL
;
748 const char *secretstr
= NULL
;
750 (void)cfg_map_get(keydef
, "algorithm", &algobj
);
751 (void)cfg_map_get(keydef
, "secret", &secretobj
);
752 INSIST(algobj
!= NULL
&& secretobj
!= NULL
);
754 algstr
= cfg_obj_asstring(algobj
);
755 secretstr
= cfg_obj_asstring(secretobj
);
757 if (ns_config_getkeyalgorithm(algstr
, NULL
, NULL
) !=
760 cfg_obj_log(control
, ns_g_lctx
,
762 "unsupported algorithm '%s' in "
763 "key '%s' for use with command "
765 algstr
, keyid
->keyname
, socktext
);
766 ISC_LIST_UNLINK(*keyids
, keyid
, link
);
767 free_controlkey(keyid
, mctx
);
771 isc_buffer_init(&b
, secret
, sizeof(secret
));
772 result
= isc_base64_decodestring(secretstr
, &b
);
774 if (result
!= ISC_R_SUCCESS
) {
775 cfg_obj_log(keydef
, ns_g_lctx
, ISC_LOG_WARNING
,
776 "secret for key '%s' on "
777 "command channel %s: %s",
778 keyid
->keyname
, socktext
,
779 isc_result_totext(result
));
780 ISC_LIST_UNLINK(*keyids
, keyid
, link
);
781 free_controlkey(keyid
, mctx
);
785 keyid
->secret
.length
= isc_buffer_usedlength(&b
);
786 keyid
->secret
.base
= isc_mem_get(mctx
,
787 keyid
->secret
.length
);
788 if (keyid
->secret
.base
== NULL
) {
789 cfg_obj_log(keydef
, ns_g_lctx
, ISC_LOG_WARNING
,
790 "couldn't register key '%s': "
791 "out of memory", keyid
->keyname
);
792 ISC_LIST_UNLINK(*keyids
, keyid
, link
);
793 free_controlkey(keyid
, mctx
);
796 memcpy(keyid
->secret
.base
, isc_buffer_base(&b
),
797 keyid
->secret
.length
);
805 if (result != ISC_R_SUCCESS) \
810 get_rndckey(isc_mem_t
*mctx
, controlkeylist_t
*keyids
) {
812 cfg_parser_t
*pctx
= NULL
;
813 cfg_obj_t
*config
= NULL
;
814 const cfg_obj_t
*key
= NULL
;
815 const cfg_obj_t
*algobj
= NULL
;
816 const cfg_obj_t
*secretobj
= NULL
;
817 const char *algstr
= NULL
;
818 const char *secretstr
= NULL
;
819 controlkey_t
*keyid
= NULL
;
823 CHECK(cfg_parser_create(mctx
, ns_g_lctx
, &pctx
));
824 CHECK(cfg_parse_file(pctx
, ns_g_keyfile
, &cfg_type_rndckey
, &config
));
825 CHECK(cfg_map_get(config
, "key", &key
));
827 keyid
= isc_mem_get(mctx
, sizeof(*keyid
));
829 CHECK(ISC_R_NOMEMORY
);
830 keyid
->keyname
= isc_mem_strdup(mctx
,
831 cfg_obj_asstring(cfg_map_getname(key
)));
832 keyid
->secret
.base
= NULL
;
833 keyid
->secret
.length
= 0;
834 ISC_LINK_INIT(keyid
, link
);
835 if (keyid
->keyname
== NULL
)
836 CHECK(ISC_R_NOMEMORY
);
838 CHECK(bind9_check_key(key
, ns_g_lctx
));
840 (void)cfg_map_get(key
, "algorithm", &algobj
);
841 (void)cfg_map_get(key
, "secret", &secretobj
);
842 INSIST(algobj
!= NULL
&& secretobj
!= NULL
);
844 algstr
= cfg_obj_asstring(algobj
);
845 secretstr
= cfg_obj_asstring(secretobj
);
847 if (ns_config_getkeyalgorithm(algstr
, NULL
, NULL
) != ISC_R_SUCCESS
) {
848 cfg_obj_log(key
, ns_g_lctx
,
850 "unsupported algorithm '%s' in "
851 "key '%s' for use with command "
853 algstr
, keyid
->keyname
);
857 isc_buffer_init(&b
, secret
, sizeof(secret
));
858 result
= isc_base64_decodestring(secretstr
, &b
);
860 if (result
!= ISC_R_SUCCESS
) {
861 cfg_obj_log(key
, ns_g_lctx
, ISC_LOG_WARNING
,
862 "secret for key '%s' on command channel: %s",
863 keyid
->keyname
, isc_result_totext(result
));
867 keyid
->secret
.length
= isc_buffer_usedlength(&b
);
868 keyid
->secret
.base
= isc_mem_get(mctx
,
869 keyid
->secret
.length
);
870 if (keyid
->secret
.base
== NULL
) {
871 cfg_obj_log(key
, ns_g_lctx
, ISC_LOG_WARNING
,
872 "couldn't register key '%s': "
873 "out of memory", keyid
->keyname
);
874 CHECK(ISC_R_NOMEMORY
);
876 memcpy(keyid
->secret
.base
, isc_buffer_base(&b
),
877 keyid
->secret
.length
);
878 ISC_LIST_APPEND(*keyids
, keyid
, link
);
880 result
= ISC_R_SUCCESS
;
884 free_controlkey(keyid
, mctx
);
886 cfg_obj_destroy(pctx
, &config
);
888 cfg_parser_destroy(&pctx
);
893 * Ensures that both '*global_keylistp' and '*control_keylistp' are
894 * valid or both are NULL.
897 get_key_info(const cfg_obj_t
*config
, const cfg_obj_t
*control
,
898 const cfg_obj_t
**global_keylistp
,
899 const cfg_obj_t
**control_keylistp
)
902 const cfg_obj_t
*control_keylist
= NULL
;
903 const cfg_obj_t
*global_keylist
= NULL
;
905 REQUIRE(global_keylistp
!= NULL
&& *global_keylistp
== NULL
);
906 REQUIRE(control_keylistp
!= NULL
&& *control_keylistp
== NULL
);
908 control_keylist
= cfg_tuple_get(control
, "keys");
910 if (!cfg_obj_isvoid(control_keylist
) &&
911 cfg_list_first(control_keylist
) != NULL
) {
912 result
= cfg_map_get(config
, "key", &global_keylist
);
914 if (result
== ISC_R_SUCCESS
) {
915 *global_keylistp
= global_keylist
;
916 *control_keylistp
= control_keylist
;
922 update_listener(ns_controls_t
*cp
, controllistener_t
**listenerp
,
923 const cfg_obj_t
*control
, const cfg_obj_t
*config
,
924 isc_sockaddr_t
*addr
, cfg_aclconfctx_t
*aclconfctx
,
925 const char *socktext
, isc_sockettype_t type
)
927 controllistener_t
*listener
;
928 const cfg_obj_t
*allow
;
929 const cfg_obj_t
*global_keylist
= NULL
;
930 const cfg_obj_t
*control_keylist
= NULL
;
931 dns_acl_t
*new_acl
= NULL
;
932 controlkeylist_t keys
;
933 isc_result_t result
= ISC_R_SUCCESS
;
935 for (listener
= ISC_LIST_HEAD(cp
->listeners
);
937 listener
= ISC_LIST_NEXT(listener
, link
))
938 if (isc_sockaddr_equal(addr
, &listener
->address
))
941 if (listener
== NULL
) {
947 * There is already a listener for this sockaddr.
948 * Update the access list and key information.
950 * First try to deal with the key situation. There are a few
952 * (a) It had an explicit keylist and still has an explicit keylist.
953 * (b) It had an automagic key and now has an explicit keylist.
954 * (c) It had an explicit keylist and now needs an automagic key.
955 * (d) It has an automagic key and still needs the automagic key.
957 * (c) and (d) are the annoying ones. The caller needs to know
958 * that it should use the automagic configuration for key information
959 * in place of the named.conf configuration.
961 * XXXDCL There is one other hazard that has not been dealt with,
962 * the problem that if a key change is being caused by a control
963 * channel reload, then the response will be with the new key
964 * and not able to be decrypted by the client.
967 get_key_info(config
, control
, &global_keylist
,
970 if (control_keylist
!= NULL
) {
971 INSIST(global_keylist
!= NULL
);
974 result
= controlkeylist_fromcfg(control_keylist
,
975 listener
->mctx
, &keys
);
976 if (result
== ISC_R_SUCCESS
) {
977 free_controlkeylist(&listener
->keys
, listener
->mctx
);
978 listener
->keys
= keys
;
979 register_keys(control
, global_keylist
, &listener
->keys
,
980 listener
->mctx
, socktext
);
983 free_controlkeylist(&listener
->keys
, listener
->mctx
);
984 result
= get_rndckey(listener
->mctx
, &listener
->keys
);
987 if (result
!= ISC_R_SUCCESS
&& global_keylist
!= NULL
) {
989 * This message might be a little misleading since the
990 * "new keys" might in fact be identical to the old ones,
991 * but tracking whether they are identical just for the
992 * sake of avoiding this message would be too much trouble.
995 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
996 "couldn't install new keys for "
997 "command channel %s: %s",
998 socktext
, isc_result_totext(result
));
1000 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
1001 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
1002 "couldn't install new keys for "
1003 "command channel %s: %s",
1004 socktext
, isc_result_totext(result
));
1008 * Now, keep the old access list unless a new one can be made.
1010 if (control
!= NULL
&& type
== isc_sockettype_tcp
) {
1011 allow
= cfg_tuple_get(control
, "allow");
1012 result
= cfg_acl_fromconfig(allow
, config
, ns_g_lctx
,
1013 aclconfctx
, listener
->mctx
, 0,
1016 result
= dns_acl_any(listener
->mctx
, &new_acl
);
1019 if (result
== ISC_R_SUCCESS
) {
1020 dns_acl_detach(&listener
->acl
);
1021 dns_acl_attach(new_acl
, &listener
->acl
);
1022 dns_acl_detach(&new_acl
);
1023 /* XXXDCL say the old acl is still used? */
1024 } else if (control
!= NULL
)
1025 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
1026 "couldn't install new acl for "
1027 "command channel %s: %s",
1028 socktext
, isc_result_totext(result
));
1030 isc_log_write(ns_g_lctx
, NS_LOGCATEGORY_GENERAL
,
1031 NS_LOGMODULE_CONTROL
, ISC_LOG_WARNING
,
1032 "couldn't install new acl for "
1033 "command channel %s: %s",
1034 socktext
, isc_result_totext(result
));
1036 if (result
== ISC_R_SUCCESS
&& type
== isc_sockettype_unix
) {
1037 isc_uint32_t perm
, owner
, group
;
1038 perm
= cfg_obj_asuint32(cfg_tuple_get(control
, "perm"));
1039 owner
= cfg_obj_asuint32(cfg_tuple_get(control
, "owner"));
1040 group
= cfg_obj_asuint32(cfg_tuple_get(control
, "group"));
1041 result
= ISC_R_SUCCESS
;
1042 if (listener
->perm
!= perm
|| listener
->owner
!= owner
||
1043 listener
->group
!= group
)
1044 result
= isc_socket_permunix(&listener
->address
, perm
,
1046 if (result
== ISC_R_SUCCESS
) {
1047 listener
->perm
= perm
;
1048 listener
->owner
= owner
;
1049 listener
->group
= group
;
1050 } else if (control
!= NULL
)
1051 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
1052 "couldn't update ownership/permission for "
1053 "command channel %s", socktext
);
1056 *listenerp
= listener
;
1060 add_listener(ns_controls_t
*cp
, controllistener_t
**listenerp
,
1061 const cfg_obj_t
*control
, const cfg_obj_t
*config
,
1062 isc_sockaddr_t
*addr
, cfg_aclconfctx_t
*aclconfctx
,
1063 const char *socktext
, isc_sockettype_t type
)
1065 isc_mem_t
*mctx
= cp
->server
->mctx
;
1066 controllistener_t
*listener
;
1067 const cfg_obj_t
*allow
;
1068 const cfg_obj_t
*global_keylist
= NULL
;
1069 const cfg_obj_t
*control_keylist
= NULL
;
1070 dns_acl_t
*new_acl
= NULL
;
1071 isc_result_t result
= ISC_R_SUCCESS
;
1073 listener
= isc_mem_get(mctx
, sizeof(*listener
));
1074 if (listener
== NULL
)
1075 result
= ISC_R_NOMEMORY
;
1077 if (result
== ISC_R_SUCCESS
) {
1078 listener
->controls
= cp
;
1079 listener
->mctx
= mctx
;
1080 listener
->task
= cp
->server
->task
;
1081 listener
->address
= *addr
;
1082 listener
->sock
= NULL
;
1083 listener
->listening
= ISC_FALSE
;
1084 listener
->exiting
= ISC_FALSE
;
1085 listener
->acl
= NULL
;
1086 listener
->type
= type
;
1088 listener
->owner
= 0;
1089 listener
->group
= 0;
1090 ISC_LINK_INIT(listener
, link
);
1091 ISC_LIST_INIT(listener
->keys
);
1092 ISC_LIST_INIT(listener
->connections
);
1097 if (control
!= NULL
&& type
== isc_sockettype_tcp
) {
1098 allow
= cfg_tuple_get(control
, "allow");
1099 result
= cfg_acl_fromconfig(allow
, config
, ns_g_lctx
,
1100 aclconfctx
, mctx
, 0,
1103 result
= dns_acl_any(mctx
, &new_acl
);
1107 if (result
== ISC_R_SUCCESS
) {
1108 dns_acl_attach(new_acl
, &listener
->acl
);
1109 dns_acl_detach(&new_acl
);
1112 get_key_info(config
, control
, &global_keylist
,
1115 if (control_keylist
!= NULL
) {
1116 result
= controlkeylist_fromcfg(control_keylist
,
1119 if (result
== ISC_R_SUCCESS
)
1120 register_keys(control
, global_keylist
,
1122 listener
->mctx
, socktext
);
1124 result
= get_rndckey(mctx
, &listener
->keys
);
1126 if (result
!= ISC_R_SUCCESS
&& control
!= NULL
)
1127 cfg_obj_log(control
, ns_g_lctx
, ISC_LOG_WARNING
,
1128 "couldn't install keys for "
1129 "command channel %s: %s",
1130 socktext
, isc_result_totext(result
));
1133 if (result
== ISC_R_SUCCESS
) {
1134 int pf
= isc_sockaddr_pf(&listener
->address
);
1135 if ((pf
== AF_INET
&& isc_net_probeipv4() != ISC_R_SUCCESS
) ||
1136 #ifdef ISC_PLATFORM_HAVESYSUNH
1137 (pf
== AF_UNIX
&& isc_net_probeunix() != ISC_R_SUCCESS
) ||
1139 (pf
== AF_INET6
&& isc_net_probeipv6() != ISC_R_SUCCESS
))
1140 result
= ISC_R_FAMILYNOSUPPORT
;
1143 if (result
== ISC_R_SUCCESS
&& type
== isc_sockettype_unix
)
1144 isc_socket_cleanunix(&listener
->address
, ISC_FALSE
);
1146 if (result
== ISC_R_SUCCESS
)
1147 result
= isc_socket_create(ns_g_socketmgr
,
1148 isc_sockaddr_pf(&listener
->address
),
1149 type
, &listener
->sock
);
1150 if (result
== ISC_R_SUCCESS
)
1151 isc_socket_setname(listener
->sock
, "control", NULL
);
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
));