Merge from vendor branch LESS:
[dragonfly.git] / contrib / bind-9.3 / bin / rndc / rndc.c
1 /*
2  * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: rndc.c,v 1.77.2.5.2.15 2005/03/17 03:58:27 marka Exp $ */
19
20 /*
21  * Principal Author: DCL
22  */
23
24 #include <config.h>
25
26 #include <stdlib.h>
27
28 #include <isc/app.h>
29 #include <isc/buffer.h>
30 #include <isc/commandline.h>
31 #include <isc/file.h>
32 #include <isc/log.h>
33 #include <isc/mem.h>
34 #include <isc/random.h>
35 #include <isc/socket.h>
36 #include <isc/stdtime.h>
37 #include <isc/string.h>
38 #include <isc/task.h>
39 #include <isc/thread.h>
40 #include <isc/util.h>
41
42 #include <isccfg/namedconf.h>
43
44 #include <isccc/alist.h>
45 #include <isccc/base64.h>
46 #include <isccc/cc.h>
47 #include <isccc/ccmsg.h>
48 #include <isccc/result.h>
49 #include <isccc/sexpr.h>
50 #include <isccc/types.h>
51 #include <isccc/util.h>
52
53 #include <bind9/getaddresses.h>
54
55 #include "util.h"
56
57 #define SERVERADDRS 10
58
59 char *progname;
60 isc_boolean_t verbose;
61
62 static const char *admin_conffile;
63 static const char *admin_keyfile;
64 static const char *version = VERSION;
65 static const char *servername = NULL;
66 static isc_sockaddr_t serveraddrs[SERVERADDRS];
67 static int nserveraddrs;
68 static int currentaddr = 0;
69 static unsigned int remoteport = 0;
70 static isc_socketmgr_t *socketmgr = NULL;
71 static unsigned char databuf[2048];
72 static isccc_ccmsg_t ccmsg;
73 static isccc_region_t secret;
74 static isc_boolean_t failed = ISC_FALSE;
75 static isc_mem_t *mctx;
76 static int sends, recvs, connects;
77 static char *command;
78 static char *args;
79 static char program[256];
80 static isc_socket_t *sock = NULL;
81 static isc_uint32_t serial;
82
83 static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);
84
85 static void
86 usage(int status) {
87         fprintf(stderr, "\
88 Usage: %s [-c config] [-s server] [-p port]\n\
89         [-k key-file ] [-y key] [-V] command\n\
90 \n\
91 command is one of the following:\n\
92 \n\
93   reload        Reload configuration file and zones.\n\
94   reload zone [class [view]]\n\
95                 Reload a single zone.\n\
96   refresh zone [class [view]]\n\
97                 Schedule immediate maintenance for a zone.\n\
98   retransfer zone [class [view]]\n\
99                 Retransfer a single zone without checking serial number.\n\
100   freeze zone [class [view]]\n\
101                 Suspend updates to a dynamic zone.\n\
102   thaw zone [class [view]]\n\
103                 Enable updates to a frozen dynamic zone and reload it.\n\
104   reconfig      Reload configuration file and new zones only.\n\
105   stats         Write server statistics to the statistics file.\n\
106   querylog      Toggle query logging.\n\
107   dumpdb [-all|-cache|-zones] [view ...]\n\
108                 Dump cache(s) to the dump file (named_dump.db).\n\
109   stop          Save pending updates to master files and stop the server.\n\
110   stop -p       Save pending updates to master files and stop the server\n\
111                 reporting process id.\n\
112   halt          Stop the server without saving pending updates.\n\
113   halt -p       Stop the server without saving pending updates reporting\n\
114                 process id.\n\
115   trace         Increment debugging level by one.\n\
116   trace level   Change the debugging level.\n\
117   notrace       Set debugging level to 0.\n\
118   flush         Flushes all of the server's caches.\n\
119   flush [view]  Flushes the server's cache for a view.\n\
120   flushname name [view]\n\
121                 Flush the given name from the server's cache(s)\n\
122   status        Display status of the server.\n\
123   recursing     Dump the queries that are currently recursing (named.recursing)\n\
124   *restart      Restart the server.\n\
125 \n\
126 * == not yet implemented\n\
127 Version: %s\n",
128                 progname, version);
129
130         exit(status);
131 }
132
133 static void
134 get_addresses(const char *host, in_port_t port) {
135         isc_result_t result;
136
137         isc_app_block();
138         result = bind9_getaddresses(servername, port,
139                                     serveraddrs, SERVERADDRS, &nserveraddrs);
140         isc_app_unblock();
141         if (result != ISC_R_SUCCESS)
142                 fatal("couldn't get address for '%s': %s",
143                       host, isc_result_totext(result));
144         INSIST(nserveraddrs > 0);
145 }
146
147 static void
148 rndc_senddone(isc_task_t *task, isc_event_t *event) {
149         isc_socketevent_t *sevent = (isc_socketevent_t *)event;
150
151         UNUSED(task);
152
153         sends--;
154         if (sevent->result != ISC_R_SUCCESS)
155                 fatal("send failed: %s", isc_result_totext(sevent->result));
156         isc_event_free(&event);
157 }
158
159 static void
160 rndc_recvdone(isc_task_t *task, isc_event_t *event) {
161         isccc_sexpr_t *response = NULL;
162         isccc_sexpr_t *data;
163         isccc_region_t source;
164         char *errormsg = NULL;
165         char *textmsg = NULL;
166         isc_result_t result;
167
168         recvs--;
169
170         if (ccmsg.result == ISC_R_EOF)
171                 fatal("connection to remote host closed\n"
172                       "This may indicate that the remote server is using "
173                       "an older version of \n"
174                       "the command protocol, this host is not authorized "
175                       "to connect,\nor the key is invalid.");
176
177         if (ccmsg.result != ISC_R_SUCCESS)
178                 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
179
180         source.rstart = isc_buffer_base(&ccmsg.buffer);
181         source.rend = isc_buffer_used(&ccmsg.buffer);
182
183         DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
184
185         data = isccc_alist_lookup(response, "_data");
186         if (data == NULL)
187                 fatal("no data section in response");
188         result = isccc_cc_lookupstring(data, "err", &errormsg);
189         if (result == ISC_R_SUCCESS) {
190                 failed = ISC_TRUE;
191                 fprintf(stderr, "%s: '%s' failed: %s\n",
192                         progname, command, errormsg);
193         }
194         else if (result != ISC_R_NOTFOUND)
195                 fprintf(stderr, "%s: parsing response failed: %s\n",
196                         progname, isc_result_totext(result));
197
198         result = isccc_cc_lookupstring(data, "text", &textmsg);
199         if (result == ISC_R_SUCCESS)
200                 printf("%s\n", textmsg);
201         else if (result != ISC_R_NOTFOUND)
202                 fprintf(stderr, "%s: parsing response failed: %s\n",
203                         progname, isc_result_totext(result));
204
205         isc_event_free(&event);
206         isccc_sexpr_free(&response);
207         isc_socket_detach(&sock);
208         isc_task_shutdown(task);
209         RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
210 }
211
212 static void
213 rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
214         isccc_sexpr_t *response = NULL;
215         isccc_sexpr_t *_ctrl;
216         isccc_region_t source;
217         isc_result_t result;
218         isc_uint32_t nonce;
219         isccc_sexpr_t *request = NULL;
220         isccc_time_t now;
221         isc_region_t r;
222         isccc_sexpr_t *data;
223         isccc_region_t message;
224         isc_uint32_t len;
225         isc_buffer_t b;
226
227         recvs--;
228
229         if (ccmsg.result == ISC_R_EOF)
230                 fatal("connection to remote host closed\n"
231                       "This may indicate that the remote server is using "
232                       "an older version of \n"
233                       "the command protocol, this host is not authorized "
234                       "to connect,\nor the key is invalid.");
235
236         if (ccmsg.result != ISC_R_SUCCESS)
237                 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
238
239         source.rstart = isc_buffer_base(&ccmsg.buffer);
240         source.rend = isc_buffer_used(&ccmsg.buffer);
241
242         DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
243
244         _ctrl = isccc_alist_lookup(response, "_ctrl");
245         if (_ctrl == NULL)
246                 fatal("_ctrl section missing");
247         nonce = 0;
248         if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
249                 nonce = 0;
250
251         isc_stdtime_get(&now);
252
253         DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
254                                                     now, now + 60, &request));
255         data = isccc_alist_lookup(request, "_data");
256         if (data == NULL)
257                 fatal("_data section missing");
258         if (isccc_cc_definestring(data, "type", args) == NULL)
259                 fatal("out of memory");
260         if (nonce != 0) {
261                 _ctrl = isccc_alist_lookup(request, "_ctrl");
262                 if (_ctrl == NULL)
263                         fatal("_ctrl section missing");
264                 if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
265                         fatal("out of memory");
266         }
267         message.rstart = databuf + 4;
268         message.rend = databuf + sizeof(databuf);
269         DO("render message", isccc_cc_towire(request, &message, &secret));
270         len = sizeof(databuf) - REGION_SIZE(message);
271         isc_buffer_init(&b, databuf, 4);
272         isc_buffer_putuint32(&b, len - 4);
273         r.length = len;
274         r.base = databuf;
275
276         isccc_ccmsg_cancelread(&ccmsg);
277         DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
278                                                     rndc_recvdone, NULL));
279         recvs++;
280         DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
281                                            NULL));
282         sends++;
283
284         isc_event_free(&event);
285         isccc_sexpr_free(&response);
286         return;
287 }
288
289 static void
290 rndc_connected(isc_task_t *task, isc_event_t *event) {
291         isc_socketevent_t *sevent = (isc_socketevent_t *)event;
292         isccc_sexpr_t *request = NULL;
293         isccc_sexpr_t *data;
294         isccc_time_t now;
295         isccc_region_t message;
296         isc_region_t r;
297         isc_uint32_t len;
298         isc_buffer_t b;
299         isc_result_t result;
300
301         connects--;
302
303         if (sevent->result != ISC_R_SUCCESS) {
304                 if (sevent->result != ISC_R_CANCELED &&
305                     currentaddr < nserveraddrs)
306                 {
307                         notify("connection failed: %s",
308                                isc_result_totext(sevent->result));
309                         isc_socket_detach(&sock);
310                         isc_event_free(&event);
311                         rndc_startconnect(&serveraddrs[currentaddr++], task);
312                         return;
313                 } else
314                         fatal("connect failed: %s",
315                               isc_result_totext(sevent->result));
316         }
317
318         isc_stdtime_get(&now);
319         DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
320                                                     now, now + 60, &request));
321         data = isccc_alist_lookup(request, "_data");
322         if (data == NULL)
323                 fatal("_data section missing");
324         if (isccc_cc_definestring(data, "type", "null") == NULL)
325                 fatal("out of memory");
326         message.rstart = databuf + 4;
327         message.rend = databuf + sizeof(databuf);
328         DO("render message", isccc_cc_towire(request, &message, &secret));
329         len = sizeof(databuf) - REGION_SIZE(message);
330         isc_buffer_init(&b, databuf, 4);
331         isc_buffer_putuint32(&b, len - 4);
332         r.length = len;
333         r.base = databuf;
334
335         isccc_ccmsg_init(mctx, sock, &ccmsg);
336         isccc_ccmsg_setmaxsize(&ccmsg, 1024);
337
338         DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
339                                                     rndc_recvnonce, NULL));
340         recvs++;
341         DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
342                                            NULL));
343         sends++;
344         isc_event_free(&event);
345 }
346
347 static void
348 rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
349         isc_result_t result;
350
351         char socktext[ISC_SOCKADDR_FORMATSIZE];
352
353         isc_sockaddr_format(addr, socktext, sizeof(socktext));
354
355         notify("using server %s (%s)", servername, socktext);
356
357         DO("create socket", isc_socket_create(socketmgr,
358                                               isc_sockaddr_pf(addr),
359                                               isc_sockettype_tcp, &sock));
360         DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
361                                          NULL));
362         connects++;
363 }
364
365 static void
366 rndc_start(isc_task_t *task, isc_event_t *event) {
367         isc_event_free(&event);
368
369         get_addresses(servername, (in_port_t) remoteport);
370
371         currentaddr = 0;
372         rndc_startconnect(&serveraddrs[currentaddr++], task);
373 }
374
375 static void
376 parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
377              cfg_parser_t **pctxp, cfg_obj_t **configp)
378 {
379         isc_result_t result;
380         const char *conffile = admin_conffile;
381         cfg_obj_t *defkey = NULL;
382         cfg_obj_t *options = NULL;
383         cfg_obj_t *servers = NULL;
384         cfg_obj_t *server = NULL;
385         cfg_obj_t *keys = NULL;
386         cfg_obj_t *key = NULL;
387         cfg_obj_t *defport = NULL;
388         cfg_obj_t *secretobj = NULL;
389         cfg_obj_t *algorithmobj = NULL;
390         cfg_obj_t *config = NULL;
391         cfg_listelt_t *elt;
392         const char *secretstr;
393         const char *algorithm;
394         static char secretarray[1024];
395         const cfg_type_t *conftype = &cfg_type_rndcconf;
396         isc_boolean_t key_only = ISC_FALSE;
397
398         if (! isc_file_exists(conffile)) {
399                 conffile = admin_keyfile;
400                 conftype = &cfg_type_rndckey;
401
402                 if (! isc_file_exists(conffile))
403                         fatal("neither %s nor %s was found",
404                               admin_conffile, admin_keyfile);
405                 key_only = ISC_TRUE;
406         }
407
408         DO("create parser", cfg_parser_create(mctx, log, pctxp));
409
410         /*
411          * The parser will output its own errors, so DO() is not used.
412          */
413         result = cfg_parse_file(*pctxp, conffile, conftype, &config);
414         if (result != ISC_R_SUCCESS)
415                 fatal("could not load rndc configuration");
416
417         if (!key_only)
418                 (void)cfg_map_get(config, "options", &options);
419
420         if (key_only && servername == NULL)
421                 servername = "127.0.0.1";
422         else if (servername == NULL && options != NULL) {
423                 cfg_obj_t *defserverobj = NULL;
424                 (void)cfg_map_get(options, "default-server", &defserverobj);
425                 if (defserverobj != NULL)
426                         servername = cfg_obj_asstring(defserverobj);
427         }
428
429         if (servername == NULL)
430                 fatal("no server specified and no default");
431
432         if (!key_only) {
433                 (void)cfg_map_get(config, "server", &servers);
434                 if (servers != NULL) {
435                         for (elt = cfg_list_first(servers);
436                              elt != NULL; 
437                              elt = cfg_list_next(elt))
438                         {
439                                 const char *name;
440                                 server = cfg_listelt_value(elt);
441                                 name = cfg_obj_asstring(cfg_map_getname(server));
442                                 if (strcasecmp(name, servername) == 0)
443                                         break;
444                                 server = NULL;
445                         }
446                 }
447         }
448
449         /*
450          * Look for the name of the key to use.
451          */
452         if (keyname != NULL)
453                 ;               /* Was set on command line, do nothing. */
454         else if (server != NULL) {
455                 DO("get key for server", cfg_map_get(server, "key", &defkey));
456                 keyname = cfg_obj_asstring(defkey);
457         } else if (options != NULL) {
458                 DO("get default key", cfg_map_get(options, "default-key",
459                                                   &defkey));
460                 keyname = cfg_obj_asstring(defkey);
461         } else if (!key_only)
462                 fatal("no key for server and no default");
463
464         /*
465          * Get the key's definition.
466          */
467         if (key_only)
468                 DO("get key", cfg_map_get(config, "key", &key));
469         else {
470                 DO("get config key list", cfg_map_get(config, "key", &keys));
471                 for (elt = cfg_list_first(keys);
472                      elt != NULL; 
473                      elt = cfg_list_next(elt))
474                 {
475                         key = cfg_listelt_value(elt);
476                         if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
477                                        keyname) == 0)
478                                 break;
479                 }
480                 if (elt == NULL)
481                         fatal("no key definition for name %s", keyname);
482         }
483         (void)cfg_map_get(key, "secret", &secretobj);
484         (void)cfg_map_get(key, "algorithm", &algorithmobj);
485         if (secretobj == NULL || algorithmobj == NULL)
486                 fatal("key must have algorithm and secret");
487
488         secretstr = cfg_obj_asstring(secretobj);
489         algorithm = cfg_obj_asstring(algorithmobj);
490
491         if (strcasecmp(algorithm, "hmac-md5") != 0)
492                 fatal("unsupported algorithm: %s", algorithm);
493
494         secret.rstart = (unsigned char *)secretarray;
495         secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
496         DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
497         secret.rend = secret.rstart;
498         secret.rstart = (unsigned char *)secretarray;
499
500         /*
501          * Find the port to connect to.
502          */
503         if (remoteport != 0)
504                 ;               /* Was set on command line, do nothing. */
505         else {
506                 if (server != NULL)
507                         (void)cfg_map_get(server, "port", &defport);
508                 if (defport == NULL && options != NULL)
509                         (void)cfg_map_get(options, "default-port", &defport);
510         }
511         if (defport != NULL) {
512                 remoteport = cfg_obj_asuint32(defport);
513                 if (remoteport > 65535 || remoteport == 0)
514                         fatal("port %d out of range", remoteport);
515         } else if (remoteport == 0)
516                 remoteport = NS_CONTROL_PORT;
517
518         *configp = config;
519 }
520
521 int
522 main(int argc, char **argv) {
523         isc_boolean_t show_final_mem = ISC_FALSE;
524         isc_result_t result = ISC_R_SUCCESS;
525         isc_taskmgr_t *taskmgr = NULL;
526         isc_task_t *task = NULL;
527         isc_log_t *log = NULL;
528         isc_logconfig_t *logconfig = NULL;
529         isc_logdestination_t logdest;
530         cfg_parser_t *pctx = NULL;
531         cfg_obj_t *config = NULL;
532         const char *keyname = NULL;
533         char *p;
534         size_t argslen;
535         int ch;
536         int i;
537
538         result = isc_file_progname(*argv, program, sizeof(program));
539         if (result != ISC_R_SUCCESS)
540                 memcpy(program, "rndc", 5);
541         progname = program;
542
543         admin_conffile = RNDC_CONFFILE;
544         admin_keyfile = RNDC_KEYFILE;
545
546         result = isc_app_start();
547         if (result != ISC_R_SUCCESS)
548                 fatal("isc_app_start() failed: %s", isc_result_totext(result));
549
550         while ((ch = isc_commandline_parse(argc, argv, "c:k:Mmp:s:Vy:"))
551                != -1) {
552                 switch (ch) {
553                 case 'c':
554                         admin_conffile = isc_commandline_argument;
555                         break;
556
557                 case 'k':
558                         admin_keyfile = isc_commandline_argument;
559                         break;
560
561                 case 'M':
562                         isc_mem_debugging = ISC_MEM_DEBUGTRACE;
563                         break;
564
565                 case 'm':
566                         show_final_mem = ISC_TRUE;
567                         break;
568
569                 case 'p':
570                         remoteport = atoi(isc_commandline_argument);
571                         if (remoteport > 65535 || remoteport == 0)
572                                 fatal("port '%s' out of range",
573                                       isc_commandline_argument);
574                         break;
575
576                 case 's':
577                         servername = isc_commandline_argument;
578                         break;
579                 case 'V':
580                         verbose = ISC_TRUE;
581                         break;
582                 case 'y':
583                         keyname = isc_commandline_argument;
584                         break;
585                 case '?':
586                         usage(0);
587                         break;
588                 default:
589                         fatal("unexpected error parsing command arguments: "
590                               "got %c\n", ch);
591                         break;
592                 }
593         }
594
595         argc -= isc_commandline_index;
596         argv += isc_commandline_index;
597
598         if (argc < 1)
599                 usage(1);
600
601         isc_random_get(&serial);
602
603         DO("create memory context", isc_mem_create(0, 0, &mctx));
604         DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
605         DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
606         DO("create task", isc_task_create(taskmgr, 0, &task));
607
608         DO("create logging context", isc_log_create(mctx, &log, &logconfig));
609         isc_log_setcontext(log);
610         DO("setting log tag", isc_log_settag(logconfig, progname));
611         logdest.file.stream = stderr;
612         logdest.file.name = NULL;
613         logdest.file.versions = ISC_LOG_ROLLNEVER;
614         logdest.file.maximum_size = 0;
615         DO("creating log channel",
616            isc_log_createchannel(logconfig, "stderr",
617                                  ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
618                                  ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
619         DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
620                                                       NULL, NULL));
621
622         parse_config(mctx, log, keyname, &pctx, &config);
623
624         isccc_result_register();
625
626         command = *argv;
627
628         /*
629          * Convert argc/argv into a space-delimited command string
630          * similar to what the user might enter in interactive mode
631          * (if that were implemented).
632          */
633         argslen = 0;
634         for (i = 0; i < argc; i++)
635                 argslen += strlen(argv[i]) + 1;
636
637         args = isc_mem_get(mctx, argslen);
638         if (args == NULL)
639                 DO("isc_mem_get", ISC_R_NOMEMORY);
640
641         p = args;
642         for (i = 0; i < argc; i++) {
643                 size_t len = strlen(argv[i]);
644                 memcpy(p, argv[i], len);
645                 p += len;
646                 *p++ = ' ';
647         }
648
649         p--;
650         *p++ = '\0';
651         INSIST(p == args + argslen);
652
653         notify("%s", command);
654
655         if (strcmp(command, "restart") == 0)
656                 fatal("'%s' is not implemented", command);
657
658         DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL));
659
660         result = isc_app_run();
661         if (result != ISC_R_SUCCESS)
662                 fatal("isc_app_run() failed: %s", isc_result_totext(result));
663
664         if (connects > 0 || sends > 0 || recvs > 0)
665                 isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
666
667         isc_task_detach(&task);
668         isc_taskmgr_destroy(&taskmgr);
669         isc_socketmgr_destroy(&socketmgr);
670         isc_log_destroy(&log);
671         isc_log_setcontext(NULL);
672
673         cfg_obj_destroy(pctx, &config);
674         cfg_parser_destroy(&pctx);
675
676         isc_mem_put(mctx, args, argslen);
677         isccc_ccmsg_invalidate(&ccmsg);
678
679         if (show_final_mem)
680                 isc_mem_stats(mctx, stderr);
681
682         isc_mem_destroy(&mctx);
683
684         if (failed)
685                 return (1);
686
687         return (0);
688 }