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