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