Import bind 9.5.2 vendor sources.
[dragonfly.git] / contrib / bind-9.5.2 / bin / rndc / rndc.c
1 /*
2  * Copyright (C) 2004-2009  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
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.
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.118.128.6 2009/01/19 23:47:01 tbox Exp $ */
19
20 /*! \file */
21
22 /*
23  * Principal Author: DCL
24  */
25
26 #include <config.h>
27
28 #include <stdlib.h>
29
30 #include <isc/app.h>
31 #include <isc/buffer.h>
32 #include <isc/commandline.h>
33 #include <isc/file.h>
34 #include <isc/log.h>
35 #include <isc/net.h>
36 #include <isc/mem.h>
37 #include <isc/random.h>
38 #include <isc/socket.h>
39 #include <isc/stdtime.h>
40 #include <isc/string.h>
41 #include <isc/task.h>
42 #include <isc/thread.h>
43 #include <isc/util.h>
44
45 #include <isccfg/namedconf.h>
46
47 #include <isccc/alist.h>
48 #include <isccc/base64.h>
49 #include <isccc/cc.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>
55
56 #include <dns/name.h>
57
58 #include <bind9/getaddresses.h>
59
60 #include "util.h"
61
62 #define SERVERADDRS 10
63
64 const char *progname;
65 isc_boolean_t verbose;
66
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;
84 static char *command;
85 static char *args;
86 static char program[256];
87 static isc_socket_t *sock = NULL;
88 static isc_uint32_t serial;
89
90 static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);
91
92 static void
93 usage(int status) {
94         fprintf(stderr, "\
95 Usage: %s [-c config] [-s server] [-p port]\n\
96         [-k key-file ] [-y key] [-V] command\n\
97 \n\
98 command is one of the following:\n\
99 \n\
100   reload        Reload configuration file and zones.\n\
101   reload zone [class [view]]\n\
102                 Reload a single zone.\n\
103   refresh zone [class [view]]\n\
104                 Schedule immediate maintenance for a zone.\n\
105   retransfer zone [class [view]]\n\
106                 Retransfer a single zone without checking serial number.\n\
107   freeze        Suspend updates to all dynamic zones.\n\
108   freeze zone [class [view]]\n\
109                 Suspend updates to a dynamic zone.\n\
110   thaw          Enable updates to all dynamic zones and reload them.\n\
111   thaw zone [class [view]]\n\
112                 Enable updates to a frozen dynamic zone and reload it.\n\
113   notify zone [class [view]]\n\
114                 Resend NOTIFY messages for the zone.\n\
115   reconfig      Reload configuration file and new zones only.\n\
116   stats         Write server statistics to the statistics file.\n\
117   querylog      Toggle query logging.\n\
118   dumpdb [-all|-cache|-zones] [view ...]\n\
119                 Dump cache(s) to the dump file (named_dump.db).\n\
120   stop          Save pending updates to master files and stop the server.\n\
121   stop -p       Save pending updates to master files and stop the server\n\
122                 reporting process id.\n\
123   halt          Stop the server without saving pending updates.\n\
124   halt -p       Stop the server without saving pending updates reporting\n\
125                 process id.\n\
126   trace         Increment debugging level by one.\n\
127   trace level   Change the debugging level.\n\
128   notrace       Set debugging level to 0.\n\
129   flush         Flushes all of the server's caches.\n\
130   flush [view]  Flushes the server's cache for a view.\n\
131   flushname name [view]\n\
132                 Flush the given name from the server's cache(s)\n\
133   status        Display status of the server.\n\
134   recursing     Dump the queries that are currently recursing (named.recursing)\n\
135   validation newstate [view]\n\
136                 Enable / disable DNSSEC validation.\n\
137   *restart      Restart the server.\n\
138 \n\
139 * == not yet implemented\n\
140 Version: %s\n",
141                 progname, version);
142
143         exit(status);
144 }
145
146 static void
147 get_addresses(const char *host, in_port_t port) {
148         isc_result_t result;
149         int found = 0, count;
150
151         if (*host == '/') {
152                 result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
153                                                host);
154                 if (result == ISC_R_SUCCESS)
155                         nserveraddrs++;
156         } else {
157                 count = SERVERADDRS - nserveraddrs;
158                 result = bind9_getaddresses(host, port,
159                                             &serveraddrs[nserveraddrs],
160                                             count, &found);
161                 nserveraddrs += found;
162         }
163         if (result != ISC_R_SUCCESS)
164                 fatal("couldn't get address for '%s': %s",
165                       host, isc_result_totext(result));
166         INSIST(nserveraddrs > 0);
167 }
168
169 static void
170 rndc_senddone(isc_task_t *task, isc_event_t *event) {
171         isc_socketevent_t *sevent = (isc_socketevent_t *)event;
172
173         UNUSED(task);
174
175         sends--;
176         if (sevent->result != ISC_R_SUCCESS)
177                 fatal("send failed: %s", isc_result_totext(sevent->result));
178         isc_event_free(&event);
179         if (sends == 0 && recvs == 0) {
180                 isc_socket_detach(&sock);
181                 isc_task_shutdown(task);
182                 RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
183         }
184 }
185
186 static void
187 rndc_recvdone(isc_task_t *task, isc_event_t *event) {
188         isccc_sexpr_t *response = NULL;
189         isccc_sexpr_t *data;
190         isccc_region_t source;
191         char *errormsg = NULL;
192         char *textmsg = NULL;
193         isc_result_t result;
194
195         recvs--;
196
197         if (ccmsg.result == ISC_R_EOF)
198                 fatal("connection to remote host closed\n"
199                       "This may indicate that\n"
200                       "* the remote server is using an older version of"
201                       " the command protocol,\n"
202                       "* this host is not authorized to connect,\n"
203                       "* the clocks are not synchronized, or\n"
204                       "* the key is invalid.");
205
206         if (ccmsg.result != ISC_R_SUCCESS)
207                 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
208
209         source.rstart = isc_buffer_base(&ccmsg.buffer);
210         source.rend = isc_buffer_used(&ccmsg.buffer);
211
212         DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
213
214         data = isccc_alist_lookup(response, "_data");
215         if (data == NULL)
216                 fatal("no data section in response");
217         result = isccc_cc_lookupstring(data, "err", &errormsg);
218         if (result == ISC_R_SUCCESS) {
219                 failed = ISC_TRUE;
220                 fprintf(stderr, "%s: '%s' failed: %s\n",
221                         progname, command, errormsg);
222         }
223         else if (result != ISC_R_NOTFOUND)
224                 fprintf(stderr, "%s: parsing response failed: %s\n",
225                         progname, isc_result_totext(result));
226
227         result = isccc_cc_lookupstring(data, "text", &textmsg);
228         if (result == ISC_R_SUCCESS)
229                 printf("%s\n", textmsg);
230         else if (result != ISC_R_NOTFOUND)
231                 fprintf(stderr, "%s: parsing response failed: %s\n",
232                         progname, isc_result_totext(result));
233
234         isc_event_free(&event);
235         isccc_sexpr_free(&response);
236         if (sends == 0 && recvs == 0) {
237                 isc_socket_detach(&sock);
238                 isc_task_shutdown(task);
239                 RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
240         }
241 }
242
243 static void
244 rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
245         isccc_sexpr_t *response = NULL;
246         isccc_sexpr_t *_ctrl;
247         isccc_region_t source;
248         isc_result_t result;
249         isc_uint32_t nonce;
250         isccc_sexpr_t *request = NULL;
251         isccc_time_t now;
252         isc_region_t r;
253         isccc_sexpr_t *data;
254         isccc_region_t message;
255         isc_uint32_t len;
256         isc_buffer_t b;
257
258         recvs--;
259
260         if (ccmsg.result == ISC_R_EOF)
261                 fatal("connection to remote host closed\n"
262                       "This may indicate that\n"
263                       "* the remote server is using an older version of"
264                       " the command protocol,\n"
265                       "* this host is not authorized to connect,\n"
266                       "* the clocks are not synchronized, or\n"
267                       "* the key is invalid.");
268
269         if (ccmsg.result != ISC_R_SUCCESS)
270                 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
271
272         source.rstart = isc_buffer_base(&ccmsg.buffer);
273         source.rend = isc_buffer_used(&ccmsg.buffer);
274
275         DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
276
277         _ctrl = isccc_alist_lookup(response, "_ctrl");
278         if (_ctrl == NULL)
279                 fatal("_ctrl section missing");
280         nonce = 0;
281         if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
282                 nonce = 0;
283
284         isc_stdtime_get(&now);
285
286         DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
287                                                     now, now + 60, &request));
288         data = isccc_alist_lookup(request, "_data");
289         if (data == NULL)
290                 fatal("_data section missing");
291         if (isccc_cc_definestring(data, "type", args) == NULL)
292                 fatal("out of memory");
293         if (nonce != 0) {
294                 _ctrl = isccc_alist_lookup(request, "_ctrl");
295                 if (_ctrl == NULL)
296                         fatal("_ctrl section missing");
297                 if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
298                         fatal("out of memory");
299         }
300         message.rstart = databuf + 4;
301         message.rend = databuf + sizeof(databuf);
302         DO("render message", isccc_cc_towire(request, &message, &secret));
303         len = sizeof(databuf) - REGION_SIZE(message);
304         isc_buffer_init(&b, databuf, 4);
305         isc_buffer_putuint32(&b, len - 4);
306         r.length = len;
307         r.base = databuf;
308
309         isccc_ccmsg_cancelread(&ccmsg);
310         DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
311                                                     rndc_recvdone, NULL));
312         recvs++;
313         DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
314                                            NULL));
315         sends++;
316
317         isc_event_free(&event);
318         isccc_sexpr_free(&response);
319         return;
320 }
321
322 static void
323 rndc_connected(isc_task_t *task, isc_event_t *event) {
324         char socktext[ISC_SOCKADDR_FORMATSIZE];
325         isc_socketevent_t *sevent = (isc_socketevent_t *)event;
326         isccc_sexpr_t *request = NULL;
327         isccc_sexpr_t *data;
328         isccc_time_t now;
329         isccc_region_t message;
330         isc_region_t r;
331         isc_uint32_t len;
332         isc_buffer_t b;
333         isc_result_t result;
334
335         connects--;
336
337         if (sevent->result != ISC_R_SUCCESS) {
338                 isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
339                                     sizeof(socktext));
340                 if (sevent->result != ISC_R_CANCELED &&
341                     ++currentaddr < nserveraddrs)
342                 {
343                         notify("connection failed: %s: %s", socktext,
344                                isc_result_totext(sevent->result));
345                         isc_socket_detach(&sock);
346                         isc_event_free(&event);
347                         rndc_startconnect(&serveraddrs[currentaddr], task);
348                         return;
349                 } else
350                         fatal("connect failed: %s: %s", socktext,
351                               isc_result_totext(sevent->result));
352         }
353
354         isc_stdtime_get(&now);
355         DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
356                                                     now, now + 60, &request));
357         data = isccc_alist_lookup(request, "_data");
358         if (data == NULL)
359                 fatal("_data section missing");
360         if (isccc_cc_definestring(data, "type", "null") == NULL)
361                 fatal("out of memory");
362         message.rstart = databuf + 4;
363         message.rend = databuf + sizeof(databuf);
364         DO("render message", isccc_cc_towire(request, &message, &secret));
365         len = sizeof(databuf) - REGION_SIZE(message);
366         isc_buffer_init(&b, databuf, 4);
367         isc_buffer_putuint32(&b, len - 4);
368         r.length = len;
369         r.base = databuf;
370
371         isccc_ccmsg_init(mctx, sock, &ccmsg);
372         isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024);
373
374         DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
375                                                     rndc_recvnonce, NULL));
376         recvs++;
377         DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
378                                            NULL));
379         sends++;
380         isc_event_free(&event);
381 }
382
383 static void
384 rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
385         isc_result_t result;
386         int pf;
387         isc_sockettype_t type;
388
389         char socktext[ISC_SOCKADDR_FORMATSIZE];
390
391         isc_sockaddr_format(addr, socktext, sizeof(socktext));
392
393         notify("using server %s (%s)", servername, socktext);
394
395         pf = isc_sockaddr_pf(addr);
396         if (pf == AF_INET || pf == AF_INET6)
397                 type = isc_sockettype_tcp;
398         else
399                 type = isc_sockettype_unix;
400         DO("create socket", isc_socket_create(socketmgr, pf, type, &sock));
401         switch (isc_sockaddr_pf(addr)) {
402         case AF_INET:
403                 DO("bind socket", isc_socket_bind(sock, &local4, 0));
404                 break;
405         case AF_INET6:
406                 DO("bind socket", isc_socket_bind(sock, &local6, 0));
407                 break;
408         default:
409                 break;
410         }
411         DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
412                                          NULL));
413         connects++;
414 }
415
416 static void
417 rndc_start(isc_task_t *task, isc_event_t *event) {
418         isc_event_free(&event);
419
420         currentaddr = 0;
421         rndc_startconnect(&serveraddrs[currentaddr], task);
422 }
423
424 static void
425 parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
426              cfg_parser_t **pctxp, cfg_obj_t **configp)
427 {
428         isc_result_t result;
429         const char *conffile = admin_conffile;
430         const cfg_obj_t *addresses = NULL;
431         const cfg_obj_t *defkey = NULL;
432         const cfg_obj_t *options = NULL;
433         const cfg_obj_t *servers = NULL;
434         const cfg_obj_t *server = NULL;
435         const cfg_obj_t *keys = NULL;
436         const cfg_obj_t *key = NULL;
437         const cfg_obj_t *defport = NULL;
438         const cfg_obj_t *secretobj = NULL;
439         const cfg_obj_t *algorithmobj = NULL;
440         cfg_obj_t *config = NULL;
441         const cfg_obj_t *address = NULL;
442         const cfg_listelt_t *elt;
443         const char *secretstr;
444         const char *algorithm;
445         static char secretarray[1024];
446         const cfg_type_t *conftype = &cfg_type_rndcconf;
447         isc_boolean_t key_only = ISC_FALSE;
448         const cfg_listelt_t *element;
449
450         if (! isc_file_exists(conffile)) {
451                 conffile = admin_keyfile;
452                 conftype = &cfg_type_rndckey;
453
454                 if (! isc_file_exists(conffile))
455                         fatal("neither %s nor %s was found",
456                               admin_conffile, admin_keyfile);
457                 key_only = ISC_TRUE;
458         }
459
460         DO("create parser", cfg_parser_create(mctx, log, pctxp));
461
462         /*
463          * The parser will output its own errors, so DO() is not used.
464          */
465         result = cfg_parse_file(*pctxp, conffile, conftype, &config);
466         if (result != ISC_R_SUCCESS)
467                 fatal("could not load rndc configuration");
468
469         if (!key_only)
470                 (void)cfg_map_get(config, "options", &options);
471
472         if (key_only && servername == NULL)
473                 servername = "127.0.0.1";
474         else if (servername == NULL && options != NULL) {
475                 const cfg_obj_t *defserverobj = NULL;
476                 (void)cfg_map_get(options, "default-server", &defserverobj);
477                 if (defserverobj != NULL)
478                         servername = cfg_obj_asstring(defserverobj);
479         }
480
481         if (servername == NULL)
482                 fatal("no server specified and no default");
483
484         if (!key_only) {
485                 (void)cfg_map_get(config, "server", &servers);
486                 if (servers != NULL) {
487                         for (elt = cfg_list_first(servers);
488                              elt != NULL;
489                              elt = cfg_list_next(elt))
490                         {
491                                 const char *name;
492                                 server = cfg_listelt_value(elt);
493                                 name = cfg_obj_asstring(cfg_map_getname(server));
494                                 if (strcasecmp(name, servername) == 0)
495                                         break;
496                                 server = NULL;
497                         }
498                 }
499         }
500
501         /*
502          * Look for the name of the key to use.
503          */
504         if (keyname != NULL)
505                 ;               /* Was set on command line, do nothing. */
506         else if (server != NULL) {
507                 DO("get key for server", cfg_map_get(server, "key", &defkey));
508                 keyname = cfg_obj_asstring(defkey);
509         } else if (options != NULL) {
510                 DO("get default key", cfg_map_get(options, "default-key",
511                                                   &defkey));
512                 keyname = cfg_obj_asstring(defkey);
513         } else if (!key_only)
514                 fatal("no key for server and no default");
515
516         /*
517          * Get the key's definition.
518          */
519         if (key_only)
520                 DO("get key", cfg_map_get(config, "key", &key));
521         else {
522                 DO("get config key list", cfg_map_get(config, "key", &keys));
523                 for (elt = cfg_list_first(keys);
524                      elt != NULL;
525                      elt = cfg_list_next(elt))
526                 {
527                         key = cfg_listelt_value(elt);
528                         if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
529                                        keyname) == 0)
530                                 break;
531                 }
532                 if (elt == NULL)
533                         fatal("no key definition for name %s", keyname);
534         }
535         (void)cfg_map_get(key, "secret", &secretobj);
536         (void)cfg_map_get(key, "algorithm", &algorithmobj);
537         if (secretobj == NULL || algorithmobj == NULL)
538                 fatal("key must have algorithm and secret");
539
540         secretstr = cfg_obj_asstring(secretobj);
541         algorithm = cfg_obj_asstring(algorithmobj);
542
543         if (strcasecmp(algorithm, "hmac-md5") != 0)
544                 fatal("unsupported algorithm: %s", algorithm);
545
546         secret.rstart = (unsigned char *)secretarray;
547         secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
548         DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
549         secret.rend = secret.rstart;
550         secret.rstart = (unsigned char *)secretarray;
551
552         /*
553          * Find the port to connect to.
554          */
555         if (remoteport != 0)
556                 ;               /* Was set on command line, do nothing. */
557         else {
558                 if (server != NULL)
559                         (void)cfg_map_get(server, "port", &defport);
560                 if (defport == NULL && options != NULL)
561                         (void)cfg_map_get(options, "default-port", &defport);
562         }
563         if (defport != NULL) {
564                 remoteport = cfg_obj_asuint32(defport);
565                 if (remoteport > 65535 || remoteport == 0)
566                         fatal("port %u out of range", remoteport);
567         } else if (remoteport == 0)
568                 remoteport = NS_CONTROL_PORT;
569
570         if (server != NULL)
571                 result = cfg_map_get(server, "addresses", &addresses);
572         else
573                 result = ISC_R_NOTFOUND;
574         if (result == ISC_R_SUCCESS) {
575                 for (element = cfg_list_first(addresses);
576                      element != NULL;
577                      element = cfg_list_next(element))
578                 {
579                         isc_sockaddr_t sa;
580
581                         address = cfg_listelt_value(element);
582                         if (!cfg_obj_issockaddr(address)) {
583                                 unsigned int myport;
584                                 const char *name;
585                                 const cfg_obj_t *obj;
586
587                                 obj = cfg_tuple_get(address, "name");
588                                 name = cfg_obj_asstring(obj);
589                                 obj = cfg_tuple_get(address, "port");
590                                 if (cfg_obj_isuint32(obj)) {
591                                         myport = cfg_obj_asuint32(obj);
592                                         if (myport > ISC_UINT16_MAX ||
593                                             myport == 0)
594                                                 fatal("port %u out of range",
595                                                       myport);
596                                 } else
597                                         myport = remoteport;
598                                 if (nserveraddrs < SERVERADDRS)
599                                         get_addresses(name, (in_port_t) myport);
600                                 else
601                                         fprintf(stderr, "too many address: "
602                                                 "%s: dropped\n", name);
603                                 continue;
604                         }
605                         sa = *cfg_obj_assockaddr(address);
606                         if (isc_sockaddr_getport(&sa) == 0)
607                                 isc_sockaddr_setport(&sa, remoteport);
608                         if (nserveraddrs < SERVERADDRS)
609                                 serveraddrs[nserveraddrs++] = sa;
610                         else {
611                                 char socktext[ISC_SOCKADDR_FORMATSIZE];
612
613                                 isc_sockaddr_format(&sa, socktext,
614                                                     sizeof(socktext));
615                                 fprintf(stderr,
616                                         "too many address: %s: dropped\n",
617                                         socktext);
618                         }
619                 }
620         }
621
622         if (!local4set && server != NULL) {
623                 address = NULL;
624                 cfg_map_get(server, "source-address", &address);
625                 if (address != NULL) {
626                         local4 = *cfg_obj_assockaddr(address);
627                         local4set = ISC_TRUE;
628                 }
629         }
630         if (!local4set && options != NULL) {
631                 address = NULL;
632                 cfg_map_get(options, "default-source-address", &address);
633                 if (address != NULL) {
634                         local4 = *cfg_obj_assockaddr(address);
635                         local4set = ISC_TRUE;
636                 }
637         }
638
639         if (!local6set && server != NULL) {
640                 address = NULL;
641                 cfg_map_get(server, "source-address-v6", &address);
642                 if (address != NULL) {
643                         local6 = *cfg_obj_assockaddr(address);
644                         local6set = ISC_TRUE;
645                 }
646         }
647         if (!local6set && options != NULL) {
648                 address = NULL;
649                 cfg_map_get(options, "default-source-address-v6", &address);
650                 if (address != NULL) {
651                         local6 = *cfg_obj_assockaddr(address);
652                         local6set = ISC_TRUE;
653                 }
654         }
655
656         *configp = config;
657 }
658
659 int
660 main(int argc, char **argv) {
661         isc_boolean_t show_final_mem = ISC_FALSE;
662         isc_result_t result = ISC_R_SUCCESS;
663         isc_taskmgr_t *taskmgr = NULL;
664         isc_task_t *task = NULL;
665         isc_log_t *log = NULL;
666         isc_logconfig_t *logconfig = NULL;
667         isc_logdestination_t logdest;
668         cfg_parser_t *pctx = NULL;
669         cfg_obj_t *config = NULL;
670         const char *keyname = NULL;
671         struct in_addr in;
672         struct in6_addr in6;
673         char *p;
674         size_t argslen;
675         int ch;
676         int i;
677
678         result = isc_file_progname(*argv, program, sizeof(program));
679         if (result != ISC_R_SUCCESS)
680                 memcpy(program, "rndc", 5);
681         progname = program;
682
683         admin_conffile = RNDC_CONFFILE;
684         admin_keyfile = RNDC_KEYFILE;
685
686         isc_sockaddr_any(&local4);
687         isc_sockaddr_any6(&local6);
688
689         result = isc_app_start();
690         if (result != ISC_R_SUCCESS)
691                 fatal("isc_app_start() failed: %s", isc_result_totext(result));
692
693         isc_commandline_errprint = ISC_FALSE;
694
695         while ((ch = isc_commandline_parse(argc, argv, "b:c:hk:Mmp:s:Vy:"))
696                != -1) {
697                 switch (ch) {
698                 case 'b':
699                         if (inet_pton(AF_INET, isc_commandline_argument,
700                                       &in) == 1) {
701                                 isc_sockaddr_fromin(&local4, &in, 0);
702                                 local4set = ISC_TRUE;
703                         } else if (inet_pton(AF_INET6, isc_commandline_argument,
704                                              &in6) == 1) {
705                                 isc_sockaddr_fromin6(&local6, &in6, 0);
706                                 local6set = ISC_TRUE;
707                         }
708                         break;
709
710                 case 'c':
711                         admin_conffile = isc_commandline_argument;
712                         break;
713
714                 case 'k':
715                         admin_keyfile = isc_commandline_argument;
716                         break;
717
718                 case 'M':
719                         isc_mem_debugging = ISC_MEM_DEBUGTRACE;
720                         break;
721
722                 case 'm':
723                         show_final_mem = ISC_TRUE;
724                         break;
725
726                 case 'p':
727                         remoteport = atoi(isc_commandline_argument);
728                         if (remoteport > 65535 || remoteport == 0)
729                                 fatal("port '%s' out of range",
730                                       isc_commandline_argument);
731                         break;
732
733                 case 's':
734                         servername = isc_commandline_argument;
735                         break;
736
737                 case 'V':
738                         verbose = ISC_TRUE;
739                         break;
740
741                 case 'y':
742                         keyname = isc_commandline_argument;
743                         break;
744
745                 case '?':
746                         if (isc_commandline_option != '?') {
747                                 fprintf(stderr, "%s: invalid argument -%c\n",
748                                         program, isc_commandline_option);
749                                 usage(1);
750                         }
751                 case 'h':
752                         usage(0);
753                         break;
754                 default:
755                         fprintf(stderr, "%s: unhandled option -%c\n",
756                                 program, isc_commandline_option);
757                         exit(1);
758                 }
759         }
760
761         argc -= isc_commandline_index;
762         argv += isc_commandline_index;
763
764         if (argc < 1)
765                 usage(1);
766
767         isc_random_get(&serial);
768
769         DO("create memory context", isc_mem_create(0, 0, &mctx));
770         DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
771         DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
772         DO("create task", isc_task_create(taskmgr, 0, &task));
773
774         DO("create logging context", isc_log_create(mctx, &log, &logconfig));
775         isc_log_setcontext(log);
776         DO("setting log tag", isc_log_settag(logconfig, progname));
777         logdest.file.stream = stderr;
778         logdest.file.name = NULL;
779         logdest.file.versions = ISC_LOG_ROLLNEVER;
780         logdest.file.maximum_size = 0;
781         DO("creating log channel",
782            isc_log_createchannel(logconfig, "stderr",
783                                  ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
784                                  ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
785         DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
786                                                       NULL, NULL));
787
788         parse_config(mctx, log, keyname, &pctx, &config);
789
790         isccc_result_register();
791
792         command = *argv;
793
794         /*
795          * Convert argc/argv into a space-delimited command string
796          * similar to what the user might enter in interactive mode
797          * (if that were implemented).
798          */
799         argslen = 0;
800         for (i = 0; i < argc; i++)
801                 argslen += strlen(argv[i]) + 1;
802
803         args = isc_mem_get(mctx, argslen);
804         if (args == NULL)
805                 DO("isc_mem_get", ISC_R_NOMEMORY);
806
807         p = args;
808         for (i = 0; i < argc; i++) {
809                 size_t len = strlen(argv[i]);
810                 memcpy(p, argv[i], len);
811                 p += len;
812                 *p++ = ' ';
813         }
814
815         p--;
816         *p++ = '\0';
817         INSIST(p == args + argslen);
818
819         notify("%s", command);
820
821         if (strcmp(command, "restart") == 0)
822                 fatal("'%s' is not implemented", command);
823
824         if (nserveraddrs == 0)
825                 get_addresses(servername, (in_port_t) remoteport);
826
827         DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL));
828
829         result = isc_app_run();
830         if (result != ISC_R_SUCCESS)
831                 fatal("isc_app_run() failed: %s", isc_result_totext(result));
832
833         if (connects > 0 || sends > 0 || recvs > 0)
834                 isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
835
836         isc_task_detach(&task);
837         isc_taskmgr_destroy(&taskmgr);
838         isc_socketmgr_destroy(&socketmgr);
839         isc_log_destroy(&log);
840         isc_log_setcontext(NULL);
841
842         cfg_obj_destroy(pctx, &config);
843         cfg_parser_destroy(&pctx);
844
845         isc_mem_put(mctx, args, argslen);
846         isccc_ccmsg_invalidate(&ccmsg);
847
848         dns_name_destroy();
849
850         if (show_final_mem)
851                 isc_mem_stats(mctx, stderr);
852
853         isc_mem_destroy(&mctx);
854
855         if (failed)
856                 return (1);
857
858         return (0);
859 }