2 * Copyright (C) 2004-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-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.
23 * Principal Author: DCL
31 #include <isc/buffer.h>
32 #include <isc/commandline.h>
37 #include <isc/random.h>
38 #include <isc/socket.h>
39 #include <isc/stdtime.h>
40 #include <isc/string.h>
42 #include <isc/thread.h>
45 #include <isccfg/namedconf.h>
47 #include <isccc/alist.h>
48 #include <isccc/base64.h>
50 #include <isccc/ccmsg.h>
51 #include <isccc/result.h>
52 #include <isccc/sexpr.h>
53 #include <isccc/types.h>
54 #include <isccc/util.h>
58 #include <bind9/getaddresses.h>
62 #define SERVERADDRS 10
65 isc_boolean_t verbose;
67 static const char *admin_conffile;
68 static const char *admin_keyfile;
69 static const char *version = VERSION;
70 static const char *servername = NULL;
71 static isc_sockaddr_t serveraddrs[SERVERADDRS];
72 static isc_sockaddr_t local4, local6;
73 static isc_boolean_t local4set = ISC_FALSE, local6set = ISC_FALSE;
74 static int nserveraddrs;
75 static int currentaddr = 0;
76 static unsigned int remoteport = 0;
77 static isc_socketmgr_t *socketmgr = NULL;
78 static unsigned char databuf[2048];
79 static isccc_ccmsg_t ccmsg;
80 static isccc_region_t secret;
81 static isc_boolean_t failed = ISC_FALSE;
82 static isc_mem_t *mctx;
83 static int sends, recvs, connects;
86 static char program[256];
87 static isc_socket_t *sock = NULL;
88 static isc_uint32_t serial;
90 static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);
92 ISC_PLATFORM_NORETURN_PRE static void
93 usage(int status) ISC_PLATFORM_NORETURN_POST;
98 Usage: %s [-b address] [-c config] [-s server] [-p port]\n\
99 [-k key-file ] [-y key] [-V] command\n\
101 command is one of the following:\n\
103 reload Reload configuration file and zones.\n\
104 reload zone [class [view]]\n\
105 Reload a single zone.\n\
106 refresh zone [class [view]]\n\
107 Schedule immediate maintenance for a zone.\n\
108 retransfer zone [class [view]]\n\
109 Retransfer a single zone without checking serial number.\n\
110 freeze Suspend updates to all dynamic zones.\n\
111 freeze zone [class [view]]\n\
112 Suspend updates to a dynamic zone.\n\
113 thaw Enable updates to all dynamic zones and reload them.\n\
114 thaw zone [class [view]]\n\
115 Enable updates to a frozen dynamic zone and reload it.\n\
116 notify zone [class [view]]\n\
117 Resend NOTIFY messages for the zone.\n\
118 reconfig Reload configuration file and new zones only.\n\
119 stats Write server statistics to the statistics file.\n\
120 querylog Toggle query logging.\n\
121 dumpdb [-all|-cache|-zones] [view ...]\n\
122 Dump cache(s) to the dump file (named_dump.db).\n\
123 stop Save pending updates to master files and stop the server.\n\
124 stop -p Save pending updates to master files and stop the server\n\
125 reporting process id.\n\
126 halt Stop the server without saving pending updates.\n\
127 halt -p Stop the server without saving pending updates reporting\n\
129 trace Increment debugging level by one.\n\
130 trace level Change the debugging level.\n\
131 notrace Set debugging level to 0.\n\
132 flush Flushes all of the server's caches.\n\
133 flush [view] Flushes the server's cache for a view.\n\
134 flushname name [view]\n\
135 Flush the given name from the server's cache(s)\n\
136 status Display status of the server.\n\
137 recursing Dump the queries that are currently recursing (named.recursing)\n\
138 tsig-list List all currently active TSIG keys, including both statically\n\
139 configured and TKEY-negotiated keys.\n\
140 tsig-delete keyname [view]\n\
141 Delete a TKEY-negotiated TSIG key.\n\
142 validation newstate [view]\n\
143 Enable / disable DNSSEC validation.\n\
144 *restart Restart the server.\n\
146 * == not yet implemented\n\
154 get_addresses(const char *host, in_port_t port) {
156 int found = 0, count;
159 result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
161 if (result == ISC_R_SUCCESS)
164 count = SERVERADDRS - nserveraddrs;
165 result = bind9_getaddresses(host, port,
166 &serveraddrs[nserveraddrs],
168 nserveraddrs += found;
170 if (result != ISC_R_SUCCESS)
171 fatal("couldn't get address for '%s': %s",
172 host, isc_result_totext(result));
173 INSIST(nserveraddrs > 0);
177 rndc_senddone(isc_task_t *task, isc_event_t *event) {
178 isc_socketevent_t *sevent = (isc_socketevent_t *)event;
183 if (sevent->result != ISC_R_SUCCESS)
184 fatal("send failed: %s", isc_result_totext(sevent->result));
185 isc_event_free(&event);
186 if (sends == 0 && recvs == 0) {
187 isc_socket_detach(&sock);
188 isc_task_shutdown(task);
189 RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
194 rndc_recvdone(isc_task_t *task, isc_event_t *event) {
195 isccc_sexpr_t *response = NULL;
197 isccc_region_t source;
198 char *errormsg = NULL;
199 char *textmsg = NULL;
204 if (ccmsg.result == ISC_R_EOF)
205 fatal("connection to remote host closed\n"
206 "This may indicate that\n"
207 "* the remote server is using an older version of"
208 " the command protocol,\n"
209 "* this host is not authorized to connect,\n"
210 "* the clocks are not synchronized, or\n"
211 "* the key is invalid.");
213 if (ccmsg.result != ISC_R_SUCCESS)
214 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
216 source.rstart = isc_buffer_base(&ccmsg.buffer);
217 source.rend = isc_buffer_used(&ccmsg.buffer);
219 DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
221 data = isccc_alist_lookup(response, "_data");
223 fatal("no data section in response");
224 result = isccc_cc_lookupstring(data, "err", &errormsg);
225 if (result == ISC_R_SUCCESS) {
227 fprintf(stderr, "%s: '%s' failed: %s\n",
228 progname, command, errormsg);
230 else if (result != ISC_R_NOTFOUND)
231 fprintf(stderr, "%s: parsing response failed: %s\n",
232 progname, isc_result_totext(result));
234 result = isccc_cc_lookupstring(data, "text", &textmsg);
235 if (result == ISC_R_SUCCESS)
236 printf("%s\n", textmsg);
237 else if (result != ISC_R_NOTFOUND)
238 fprintf(stderr, "%s: parsing response failed: %s\n",
239 progname, isc_result_totext(result));
241 isc_event_free(&event);
242 isccc_sexpr_free(&response);
243 if (sends == 0 && recvs == 0) {
244 isc_socket_detach(&sock);
245 isc_task_shutdown(task);
246 RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
251 rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
252 isccc_sexpr_t *response = NULL;
253 isccc_sexpr_t *_ctrl;
254 isccc_region_t source;
257 isccc_sexpr_t *request = NULL;
261 isccc_region_t message;
267 if (ccmsg.result == ISC_R_EOF)
268 fatal("connection to remote host closed\n"
269 "This may indicate that\n"
270 "* the remote server is using an older version of"
271 " the command protocol,\n"
272 "* this host is not authorized to connect,\n"
273 "* the clocks are not synchronized, or\n"
274 "* the key is invalid.");
276 if (ccmsg.result != ISC_R_SUCCESS)
277 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
279 source.rstart = isc_buffer_base(&ccmsg.buffer);
280 source.rend = isc_buffer_used(&ccmsg.buffer);
282 DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
284 _ctrl = isccc_alist_lookup(response, "_ctrl");
286 fatal("_ctrl section missing");
288 if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
291 isc_stdtime_get(&now);
293 DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
294 now, now + 60, &request));
295 data = isccc_alist_lookup(request, "_data");
297 fatal("_data section missing");
298 if (isccc_cc_definestring(data, "type", args) == NULL)
299 fatal("out of memory");
301 _ctrl = isccc_alist_lookup(request, "_ctrl");
303 fatal("_ctrl section missing");
304 if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
305 fatal("out of memory");
307 message.rstart = databuf + 4;
308 message.rend = databuf + sizeof(databuf);
309 DO("render message", isccc_cc_towire(request, &message, &secret));
310 len = sizeof(databuf) - REGION_SIZE(message);
311 isc_buffer_init(&b, databuf, 4);
312 isc_buffer_putuint32(&b, len - 4);
316 isccc_ccmsg_cancelread(&ccmsg);
317 DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
318 rndc_recvdone, NULL));
320 DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
324 isc_event_free(&event);
325 isccc_sexpr_free(&response);
330 rndc_connected(isc_task_t *task, isc_event_t *event) {
331 char socktext[ISC_SOCKADDR_FORMATSIZE];
332 isc_socketevent_t *sevent = (isc_socketevent_t *)event;
333 isccc_sexpr_t *request = NULL;
336 isccc_region_t message;
344 if (sevent->result != ISC_R_SUCCESS) {
345 isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
347 if (sevent->result != ISC_R_CANCELED &&
348 ++currentaddr < nserveraddrs)
350 notify("connection failed: %s: %s", socktext,
351 isc_result_totext(sevent->result));
352 isc_socket_detach(&sock);
353 isc_event_free(&event);
354 rndc_startconnect(&serveraddrs[currentaddr], task);
357 fatal("connect failed: %s: %s", socktext,
358 isc_result_totext(sevent->result));
361 isc_stdtime_get(&now);
362 DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
363 now, now + 60, &request));
364 data = isccc_alist_lookup(request, "_data");
366 fatal("_data section missing");
367 if (isccc_cc_definestring(data, "type", "null") == NULL)
368 fatal("out of memory");
369 message.rstart = databuf + 4;
370 message.rend = databuf + sizeof(databuf);
371 DO("render message", isccc_cc_towire(request, &message, &secret));
372 len = sizeof(databuf) - REGION_SIZE(message);
373 isc_buffer_init(&b, databuf, 4);
374 isc_buffer_putuint32(&b, len - 4);
378 isccc_ccmsg_init(mctx, sock, &ccmsg);
379 isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024);
381 DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
382 rndc_recvnonce, NULL));
384 DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
387 isc_event_free(&event);
391 rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
394 isc_sockettype_t type;
396 char socktext[ISC_SOCKADDR_FORMATSIZE];
398 isc_sockaddr_format(addr, socktext, sizeof(socktext));
400 notify("using server %s (%s)", servername, socktext);
402 pf = isc_sockaddr_pf(addr);
403 if (pf == AF_INET || pf == AF_INET6)
404 type = isc_sockettype_tcp;
406 type = isc_sockettype_unix;
407 DO("create socket", isc_socket_create(socketmgr, pf, type, &sock));
408 switch (isc_sockaddr_pf(addr)) {
410 DO("bind socket", isc_socket_bind(sock, &local4, 0));
413 DO("bind socket", isc_socket_bind(sock, &local6, 0));
418 DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
424 rndc_start(isc_task_t *task, isc_event_t *event) {
425 isc_event_free(&event);
428 rndc_startconnect(&serveraddrs[currentaddr], task);
432 parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
433 cfg_parser_t **pctxp, cfg_obj_t **configp)
436 const char *conffile = admin_conffile;
437 const cfg_obj_t *addresses = NULL;
438 const cfg_obj_t *defkey = NULL;
439 const cfg_obj_t *options = NULL;
440 const cfg_obj_t *servers = NULL;
441 const cfg_obj_t *server = NULL;
442 const cfg_obj_t *keys = NULL;
443 const cfg_obj_t *key = NULL;
444 const cfg_obj_t *defport = NULL;
445 const cfg_obj_t *secretobj = NULL;
446 const cfg_obj_t *algorithmobj = NULL;
447 cfg_obj_t *config = NULL;
448 const cfg_obj_t *address = NULL;
449 const cfg_listelt_t *elt;
450 const char *secretstr;
451 const char *algorithm;
452 static char secretarray[1024];
453 const cfg_type_t *conftype = &cfg_type_rndcconf;
454 isc_boolean_t key_only = ISC_FALSE;
455 const cfg_listelt_t *element;
457 if (! isc_file_exists(conffile)) {
458 conffile = admin_keyfile;
459 conftype = &cfg_type_rndckey;
461 if (! isc_file_exists(conffile))
462 fatal("neither %s nor %s was found",
463 admin_conffile, admin_keyfile);
467 DO("create parser", cfg_parser_create(mctx, log, pctxp));
470 * The parser will output its own errors, so DO() is not used.
472 result = cfg_parse_file(*pctxp, conffile, conftype, &config);
473 if (result != ISC_R_SUCCESS)
474 fatal("could not load rndc configuration");
477 (void)cfg_map_get(config, "options", &options);
479 if (key_only && servername == NULL)
480 servername = "127.0.0.1";
481 else if (servername == NULL && options != NULL) {
482 const cfg_obj_t *defserverobj = NULL;
483 (void)cfg_map_get(options, "default-server", &defserverobj);
484 if (defserverobj != NULL)
485 servername = cfg_obj_asstring(defserverobj);
488 if (servername == NULL)
489 fatal("no server specified and no default");
492 (void)cfg_map_get(config, "server", &servers);
493 if (servers != NULL) {
494 for (elt = cfg_list_first(servers);
496 elt = cfg_list_next(elt))
499 server = cfg_listelt_value(elt);
500 name = cfg_obj_asstring(cfg_map_getname(server));
501 if (strcasecmp(name, servername) == 0)
509 * Look for the name of the key to use.
512 ; /* Was set on command line, do nothing. */
513 else if (server != NULL) {
514 DO("get key for server", cfg_map_get(server, "key", &defkey));
515 keyname = cfg_obj_asstring(defkey);
516 } else if (options != NULL) {
517 DO("get default key", cfg_map_get(options, "default-key",
519 keyname = cfg_obj_asstring(defkey);
520 } else if (!key_only)
521 fatal("no key for server and no default");
524 * Get the key's definition.
527 DO("get key", cfg_map_get(config, "key", &key));
529 DO("get config key list", cfg_map_get(config, "key", &keys));
530 for (elt = cfg_list_first(keys);
532 elt = cfg_list_next(elt))
534 key = cfg_listelt_value(elt);
535 if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
540 fatal("no key definition for name %s", keyname);
542 (void)cfg_map_get(key, "secret", &secretobj);
543 (void)cfg_map_get(key, "algorithm", &algorithmobj);
544 if (secretobj == NULL || algorithmobj == NULL)
545 fatal("key must have algorithm and secret");
547 secretstr = cfg_obj_asstring(secretobj);
548 algorithm = cfg_obj_asstring(algorithmobj);
550 if (strcasecmp(algorithm, "hmac-md5") != 0)
551 fatal("unsupported algorithm: %s", algorithm);
553 secret.rstart = (unsigned char *)secretarray;
554 secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
555 DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
556 secret.rend = secret.rstart;
557 secret.rstart = (unsigned char *)secretarray;
560 * Find the port to connect to.
563 ; /* Was set on command line, do nothing. */
566 (void)cfg_map_get(server, "port", &defport);
567 if (defport == NULL && options != NULL)
568 (void)cfg_map_get(options, "default-port", &defport);
570 if (defport != NULL) {
571 remoteport = cfg_obj_asuint32(defport);
572 if (remoteport > 65535 || remoteport == 0)
573 fatal("port %u out of range", remoteport);
574 } else if (remoteport == 0)
575 remoteport = NS_CONTROL_PORT;
578 result = cfg_map_get(server, "addresses", &addresses);
580 result = ISC_R_NOTFOUND;
581 if (result == ISC_R_SUCCESS) {
582 for (element = cfg_list_first(addresses);
584 element = cfg_list_next(element))
588 address = cfg_listelt_value(element);
589 if (!cfg_obj_issockaddr(address)) {
592 const cfg_obj_t *obj;
594 obj = cfg_tuple_get(address, "name");
595 name = cfg_obj_asstring(obj);
596 obj = cfg_tuple_get(address, "port");
597 if (cfg_obj_isuint32(obj)) {
598 myport = cfg_obj_asuint32(obj);
599 if (myport > ISC_UINT16_MAX ||
601 fatal("port %u out of range",
605 if (nserveraddrs < SERVERADDRS)
606 get_addresses(name, (in_port_t) myport);
608 fprintf(stderr, "too many address: "
609 "%s: dropped\n", name);
612 sa = *cfg_obj_assockaddr(address);
613 if (isc_sockaddr_getport(&sa) == 0)
614 isc_sockaddr_setport(&sa, remoteport);
615 if (nserveraddrs < SERVERADDRS)
616 serveraddrs[nserveraddrs++] = sa;
618 char socktext[ISC_SOCKADDR_FORMATSIZE];
620 isc_sockaddr_format(&sa, socktext,
623 "too many address: %s: dropped\n",
629 if (!local4set && server != NULL) {
631 cfg_map_get(server, "source-address", &address);
632 if (address != NULL) {
633 local4 = *cfg_obj_assockaddr(address);
634 local4set = ISC_TRUE;
637 if (!local4set && options != NULL) {
639 cfg_map_get(options, "default-source-address", &address);
640 if (address != NULL) {
641 local4 = *cfg_obj_assockaddr(address);
642 local4set = ISC_TRUE;
646 if (!local6set && server != NULL) {
648 cfg_map_get(server, "source-address-v6", &address);
649 if (address != NULL) {
650 local6 = *cfg_obj_assockaddr(address);
651 local6set = ISC_TRUE;
654 if (!local6set && options != NULL) {
656 cfg_map_get(options, "default-source-address-v6", &address);
657 if (address != NULL) {
658 local6 = *cfg_obj_assockaddr(address);
659 local6set = ISC_TRUE;
667 main(int argc, char **argv) {
668 isc_boolean_t show_final_mem = ISC_FALSE;
669 isc_result_t result = ISC_R_SUCCESS;
670 isc_taskmgr_t *taskmgr = NULL;
671 isc_task_t *task = NULL;
672 isc_log_t *log = NULL;
673 isc_logconfig_t *logconfig = NULL;
674 isc_logdestination_t logdest;
675 cfg_parser_t *pctx = NULL;
676 cfg_obj_t *config = NULL;
677 const char *keyname = NULL;
685 result = isc_file_progname(*argv, program, sizeof(program));
686 if (result != ISC_R_SUCCESS)
687 memcpy(program, "rndc", 5);
690 admin_conffile = RNDC_CONFFILE;
691 admin_keyfile = RNDC_KEYFILE;
693 isc_sockaddr_any(&local4);
694 isc_sockaddr_any6(&local6);
696 result = isc_app_start();
697 if (result != ISC_R_SUCCESS)
698 fatal("isc_app_start() failed: %s", isc_result_totext(result));
700 isc_commandline_errprint = ISC_FALSE;
702 while ((ch = isc_commandline_parse(argc, argv, "b:c:hk:Mmp:s:Vy:"))
706 if (inet_pton(AF_INET, isc_commandline_argument,
708 isc_sockaddr_fromin(&local4, &in, 0);
709 local4set = ISC_TRUE;
710 } else if (inet_pton(AF_INET6, isc_commandline_argument,
712 isc_sockaddr_fromin6(&local6, &in6, 0);
713 local6set = ISC_TRUE;
718 admin_conffile = isc_commandline_argument;
722 admin_keyfile = isc_commandline_argument;
726 isc_mem_debugging = ISC_MEM_DEBUGTRACE;
730 show_final_mem = ISC_TRUE;
734 remoteport = atoi(isc_commandline_argument);
735 if (remoteport > 65535 || remoteport == 0)
736 fatal("port '%s' out of range",
737 isc_commandline_argument);
741 servername = isc_commandline_argument;
749 keyname = isc_commandline_argument;
753 if (isc_commandline_option != '?') {
754 fprintf(stderr, "%s: invalid argument -%c\n",
755 program, isc_commandline_option);
762 fprintf(stderr, "%s: unhandled option -%c\n",
763 program, isc_commandline_option);
768 argc -= isc_commandline_index;
769 argv += isc_commandline_index;
774 isc_random_get(&serial);
776 DO("create memory context", isc_mem_create(0, 0, &mctx));
777 DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
778 DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
779 DO("create task", isc_task_create(taskmgr, 0, &task));
781 DO("create logging context", isc_log_create(mctx, &log, &logconfig));
782 isc_log_setcontext(log);
783 DO("setting log tag", isc_log_settag(logconfig, progname));
784 logdest.file.stream = stderr;
785 logdest.file.name = NULL;
786 logdest.file.versions = ISC_LOG_ROLLNEVER;
787 logdest.file.maximum_size = 0;
788 DO("creating log channel",
789 isc_log_createchannel(logconfig, "stderr",
790 ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
791 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
792 DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
795 parse_config(mctx, log, keyname, &pctx, &config);
797 isccc_result_register();
802 * Convert argc/argv into a space-delimited command string
803 * similar to what the user might enter in interactive mode
804 * (if that were implemented).
807 for (i = 0; i < argc; i++)
808 argslen += strlen(argv[i]) + 1;
810 args = isc_mem_get(mctx, argslen);
812 DO("isc_mem_get", ISC_R_NOMEMORY);
815 for (i = 0; i < argc; i++) {
816 size_t len = strlen(argv[i]);
817 memcpy(p, argv[i], len);
824 INSIST(p == args + argslen);
826 notify("%s", command);
828 if (strcmp(command, "restart") == 0)
829 fatal("'%s' is not implemented", command);
831 if (nserveraddrs == 0)
832 get_addresses(servername, (in_port_t) remoteport);
834 DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL));
836 result = isc_app_run();
837 if (result != ISC_R_SUCCESS)
838 fatal("isc_app_run() failed: %s", isc_result_totext(result));
840 if (connects > 0 || sends > 0 || recvs > 0)
841 isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
843 isc_task_detach(&task);
844 isc_taskmgr_destroy(&taskmgr);
845 isc_socketmgr_destroy(&socketmgr);
846 isc_log_destroy(&log);
847 isc_log_setcontext(NULL);
849 cfg_obj_destroy(pctx, &config);
850 cfg_parser_destroy(&pctx);
852 isc_mem_put(mctx, args, argslen);
853 isccc_ccmsg_invalidate(&ccmsg);
858 isc_mem_stats(mctx, stderr);
860 isc_mem_destroy(&mctx);