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