2 * Copyright (C) 2004, 2005 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.15 2005/03/17 03:58:27 marka Exp $ */
21 * Principal Author: DCL
29 #include <isc/buffer.h>
30 #include <isc/commandline.h>
34 #include <isc/random.h>
35 #include <isc/socket.h>
36 #include <isc/stdtime.h>
37 #include <isc/string.h>
39 #include <isc/thread.h>
42 #include <isccfg/namedconf.h>
44 #include <isccc/alist.h>
45 #include <isccc/base64.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>
57 #define SERVERADDRS 10
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;
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);
88 Usage: %s [-c config] [-s server] [-p port]\n\
89 [-k key-file ] [-y key] [-V] command\n\
91 command is one of the following:\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\
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\
134 get_addresses(const char *host, in_port_t port) {
138 result = bind9_getaddresses(servername, port,
139 serveraddrs, SERVERADDRS, &nserveraddrs);
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);
148 rndc_senddone(isc_task_t *task, isc_event_t *event) {
149 isc_socketevent_t *sevent = (isc_socketevent_t *)event;
154 if (sevent->result != ISC_R_SUCCESS)
155 fatal("send failed: %s", isc_result_totext(sevent->result));
156 isc_event_free(&event);
160 rndc_recvdone(isc_task_t *task, isc_event_t *event) {
161 isccc_sexpr_t *response = NULL;
163 isccc_region_t source;
164 char *errormsg = NULL;
165 char *textmsg = NULL;
170 if (ccmsg.result == ISC_R_EOF)
171 fatal("connection to remote host closed\n"
172 "This may indicate that the remote server is using "
173 "an older version of \n"
174 "the command protocol, this host is not authorized "
175 "to connect,\nor the key is invalid.");
177 if (ccmsg.result != ISC_R_SUCCESS)
178 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
180 source.rstart = isc_buffer_base(&ccmsg.buffer);
181 source.rend = isc_buffer_used(&ccmsg.buffer);
183 DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
185 data = isccc_alist_lookup(response, "_data");
187 fatal("no data section in response");
188 result = isccc_cc_lookupstring(data, "err", &errormsg);
189 if (result == ISC_R_SUCCESS) {
191 fprintf(stderr, "%s: '%s' failed: %s\n",
192 progname, command, errormsg);
194 else if (result != ISC_R_NOTFOUND)
195 fprintf(stderr, "%s: parsing response failed: %s\n",
196 progname, isc_result_totext(result));
198 result = isccc_cc_lookupstring(data, "text", &textmsg);
199 if (result == ISC_R_SUCCESS)
200 printf("%s\n", textmsg);
201 else if (result != ISC_R_NOTFOUND)
202 fprintf(stderr, "%s: parsing response failed: %s\n",
203 progname, isc_result_totext(result));
205 isc_event_free(&event);
206 isccc_sexpr_free(&response);
207 isc_socket_detach(&sock);
208 isc_task_shutdown(task);
209 RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
213 rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
214 isccc_sexpr_t *response = NULL;
215 isccc_sexpr_t *_ctrl;
216 isccc_region_t source;
219 isccc_sexpr_t *request = NULL;
223 isccc_region_t message;
229 if (ccmsg.result == ISC_R_EOF)
230 fatal("connection to remote host closed\n"
231 "This may indicate that the remote server is using "
232 "an older version of \n"
233 "the command protocol, this host is not authorized "
234 "to connect,\nor the key is invalid.");
236 if (ccmsg.result != ISC_R_SUCCESS)
237 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
239 source.rstart = isc_buffer_base(&ccmsg.buffer);
240 source.rend = isc_buffer_used(&ccmsg.buffer);
242 DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
244 _ctrl = isccc_alist_lookup(response, "_ctrl");
246 fatal("_ctrl section missing");
248 if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
251 isc_stdtime_get(&now);
253 DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
254 now, now + 60, &request));
255 data = isccc_alist_lookup(request, "_data");
257 fatal("_data section missing");
258 if (isccc_cc_definestring(data, "type", args) == NULL)
259 fatal("out of memory");
261 _ctrl = isccc_alist_lookup(request, "_ctrl");
263 fatal("_ctrl section missing");
264 if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
265 fatal("out of memory");
267 message.rstart = databuf + 4;
268 message.rend = databuf + sizeof(databuf);
269 DO("render message", isccc_cc_towire(request, &message, &secret));
270 len = sizeof(databuf) - REGION_SIZE(message);
271 isc_buffer_init(&b, databuf, 4);
272 isc_buffer_putuint32(&b, len - 4);
276 isccc_ccmsg_cancelread(&ccmsg);
277 DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
278 rndc_recvdone, NULL));
280 DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
284 isc_event_free(&event);
285 isccc_sexpr_free(&response);
290 rndc_connected(isc_task_t *task, isc_event_t *event) {
291 isc_socketevent_t *sevent = (isc_socketevent_t *)event;
292 isccc_sexpr_t *request = NULL;
295 isccc_region_t message;
303 if (sevent->result != ISC_R_SUCCESS) {
304 if (sevent->result != ISC_R_CANCELED &&
305 currentaddr < nserveraddrs)
307 notify("connection failed: %s",
308 isc_result_totext(sevent->result));
309 isc_socket_detach(&sock);
310 isc_event_free(&event);
311 rndc_startconnect(&serveraddrs[currentaddr++], task);
314 fatal("connect failed: %s",
315 isc_result_totext(sevent->result));
318 isc_stdtime_get(&now);
319 DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
320 now, now + 60, &request));
321 data = isccc_alist_lookup(request, "_data");
323 fatal("_data section missing");
324 if (isccc_cc_definestring(data, "type", "null") == NULL)
325 fatal("out of memory");
326 message.rstart = databuf + 4;
327 message.rend = databuf + sizeof(databuf);
328 DO("render message", isccc_cc_towire(request, &message, &secret));
329 len = sizeof(databuf) - REGION_SIZE(message);
330 isc_buffer_init(&b, databuf, 4);
331 isc_buffer_putuint32(&b, len - 4);
335 isccc_ccmsg_init(mctx, sock, &ccmsg);
336 isccc_ccmsg_setmaxsize(&ccmsg, 1024);
338 DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
339 rndc_recvnonce, NULL));
341 DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
344 isc_event_free(&event);
348 rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
351 char socktext[ISC_SOCKADDR_FORMATSIZE];
353 isc_sockaddr_format(addr, socktext, sizeof(socktext));
355 notify("using server %s (%s)", servername, socktext);
357 DO("create socket", isc_socket_create(socketmgr,
358 isc_sockaddr_pf(addr),
359 isc_sockettype_tcp, &sock));
360 DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
366 rndc_start(isc_task_t *task, isc_event_t *event) {
367 isc_event_free(&event);
369 get_addresses(servername, (in_port_t) remoteport);
372 rndc_startconnect(&serveraddrs[currentaddr++], task);
376 parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
377 cfg_parser_t **pctxp, cfg_obj_t **configp)
380 const char *conffile = admin_conffile;
381 cfg_obj_t *defkey = NULL;
382 cfg_obj_t *options = NULL;
383 cfg_obj_t *servers = NULL;
384 cfg_obj_t *server = NULL;
385 cfg_obj_t *keys = NULL;
386 cfg_obj_t *key = NULL;
387 cfg_obj_t *defport = NULL;
388 cfg_obj_t *secretobj = NULL;
389 cfg_obj_t *algorithmobj = NULL;
390 cfg_obj_t *config = NULL;
392 const char *secretstr;
393 const char *algorithm;
394 static char secretarray[1024];
395 const cfg_type_t *conftype = &cfg_type_rndcconf;
396 isc_boolean_t key_only = ISC_FALSE;
398 if (! isc_file_exists(conffile)) {
399 conffile = admin_keyfile;
400 conftype = &cfg_type_rndckey;
402 if (! isc_file_exists(conffile))
403 fatal("neither %s nor %s was found",
404 admin_conffile, admin_keyfile);
408 DO("create parser", cfg_parser_create(mctx, log, pctxp));
411 * The parser will output its own errors, so DO() is not used.
413 result = cfg_parse_file(*pctxp, conffile, conftype, &config);
414 if (result != ISC_R_SUCCESS)
415 fatal("could not load rndc configuration");
418 (void)cfg_map_get(config, "options", &options);
420 if (key_only && servername == NULL)
421 servername = "127.0.0.1";
422 else if (servername == NULL && options != NULL) {
423 cfg_obj_t *defserverobj = NULL;
424 (void)cfg_map_get(options, "default-server", &defserverobj);
425 if (defserverobj != NULL)
426 servername = cfg_obj_asstring(defserverobj);
429 if (servername == NULL)
430 fatal("no server specified and no default");
433 (void)cfg_map_get(config, "server", &servers);
434 if (servers != NULL) {
435 for (elt = cfg_list_first(servers);
437 elt = cfg_list_next(elt))
440 server = cfg_listelt_value(elt);
441 name = cfg_obj_asstring(cfg_map_getname(server));
442 if (strcasecmp(name, servername) == 0)
450 * Look for the name of the key to use.
453 ; /* Was set on command line, do nothing. */
454 else if (server != NULL) {
455 DO("get key for server", cfg_map_get(server, "key", &defkey));
456 keyname = cfg_obj_asstring(defkey);
457 } else if (options != NULL) {
458 DO("get default key", cfg_map_get(options, "default-key",
460 keyname = cfg_obj_asstring(defkey);
461 } else if (!key_only)
462 fatal("no key for server and no default");
465 * Get the key's definition.
468 DO("get key", cfg_map_get(config, "key", &key));
470 DO("get config key list", cfg_map_get(config, "key", &keys));
471 for (elt = cfg_list_first(keys);
473 elt = cfg_list_next(elt))
475 key = cfg_listelt_value(elt);
476 if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
481 fatal("no key definition for name %s", keyname);
483 (void)cfg_map_get(key, "secret", &secretobj);
484 (void)cfg_map_get(key, "algorithm", &algorithmobj);
485 if (secretobj == NULL || algorithmobj == NULL)
486 fatal("key must have algorithm and secret");
488 secretstr = cfg_obj_asstring(secretobj);
489 algorithm = cfg_obj_asstring(algorithmobj);
491 if (strcasecmp(algorithm, "hmac-md5") != 0)
492 fatal("unsupported algorithm: %s", algorithm);
494 secret.rstart = (unsigned char *)secretarray;
495 secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
496 DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
497 secret.rend = secret.rstart;
498 secret.rstart = (unsigned char *)secretarray;
501 * Find the port to connect to.
504 ; /* Was set on command line, do nothing. */
507 (void)cfg_map_get(server, "port", &defport);
508 if (defport == NULL && options != NULL)
509 (void)cfg_map_get(options, "default-port", &defport);
511 if (defport != NULL) {
512 remoteport = cfg_obj_asuint32(defport);
513 if (remoteport > 65535 || remoteport == 0)
514 fatal("port %d out of range", remoteport);
515 } else if (remoteport == 0)
516 remoteport = NS_CONTROL_PORT;
522 main(int argc, char **argv) {
523 isc_boolean_t show_final_mem = ISC_FALSE;
524 isc_result_t result = ISC_R_SUCCESS;
525 isc_taskmgr_t *taskmgr = NULL;
526 isc_task_t *task = NULL;
527 isc_log_t *log = NULL;
528 isc_logconfig_t *logconfig = NULL;
529 isc_logdestination_t logdest;
530 cfg_parser_t *pctx = NULL;
531 cfg_obj_t *config = NULL;
532 const char *keyname = NULL;
538 result = isc_file_progname(*argv, program, sizeof(program));
539 if (result != ISC_R_SUCCESS)
540 memcpy(program, "rndc", 5);
543 admin_conffile = RNDC_CONFFILE;
544 admin_keyfile = RNDC_KEYFILE;
546 result = isc_app_start();
547 if (result != ISC_R_SUCCESS)
548 fatal("isc_app_start() failed: %s", isc_result_totext(result));
550 while ((ch = isc_commandline_parse(argc, argv, "c:k:Mmp:s:Vy:"))
554 admin_conffile = isc_commandline_argument;
558 admin_keyfile = isc_commandline_argument;
562 isc_mem_debugging = ISC_MEM_DEBUGTRACE;
566 show_final_mem = ISC_TRUE;
570 remoteport = atoi(isc_commandline_argument);
571 if (remoteport > 65535 || remoteport == 0)
572 fatal("port '%s' out of range",
573 isc_commandline_argument);
577 servername = isc_commandline_argument;
583 keyname = isc_commandline_argument;
589 fatal("unexpected error parsing command arguments: "
595 argc -= isc_commandline_index;
596 argv += isc_commandline_index;
601 isc_random_get(&serial);
603 DO("create memory context", isc_mem_create(0, 0, &mctx));
604 DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
605 DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
606 DO("create task", isc_task_create(taskmgr, 0, &task));
608 DO("create logging context", isc_log_create(mctx, &log, &logconfig));
609 isc_log_setcontext(log);
610 DO("setting log tag", isc_log_settag(logconfig, progname));
611 logdest.file.stream = stderr;
612 logdest.file.name = NULL;
613 logdest.file.versions = ISC_LOG_ROLLNEVER;
614 logdest.file.maximum_size = 0;
615 DO("creating log channel",
616 isc_log_createchannel(logconfig, "stderr",
617 ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
618 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
619 DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
622 parse_config(mctx, log, keyname, &pctx, &config);
624 isccc_result_register();
629 * Convert argc/argv into a space-delimited command string
630 * similar to what the user might enter in interactive mode
631 * (if that were implemented).
634 for (i = 0; i < argc; i++)
635 argslen += strlen(argv[i]) + 1;
637 args = isc_mem_get(mctx, argslen);
639 DO("isc_mem_get", ISC_R_NOMEMORY);
642 for (i = 0; i < argc; i++) {
643 size_t len = strlen(argv[i]);
644 memcpy(p, argv[i], len);
651 INSIST(p == args + argslen);
653 notify("%s", command);
655 if (strcmp(command, "restart") == 0)
656 fatal("'%s' is not implemented", command);
658 DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL));
660 result = isc_app_run();
661 if (result != ISC_R_SUCCESS)
662 fatal("isc_app_run() failed: %s", isc_result_totext(result));
664 if (connects > 0 || sends > 0 || recvs > 0)
665 isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
667 isc_task_detach(&task);
668 isc_taskmgr_destroy(&taskmgr);
669 isc_socketmgr_destroy(&socketmgr);
670 isc_log_destroy(&log);
671 isc_log_setcontext(NULL);
673 cfg_obj_destroy(pctx, &config);
674 cfg_parser_destroy(&pctx);
676 isc_mem_put(mctx, args, argslen);
677 isccc_ccmsg_invalidate(&ccmsg);
680 isc_mem_stats(mctx, stderr);
682 isc_mem_destroy(&mctx);