2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000, 2001, 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.6 2004/03/09 06:09:27 marka Exp $ */
21 * Principal Author: DCL
29 #include <isc/buffer.h>
30 #include <isc/commandline.h>
34 #include <isc/netdb.h>
35 #include <isc/random.h>
36 #include <isc/socket.h>
37 #include <isc/stdtime.h>
38 #include <isc/string.h>
40 #include <isc/thread.h>
43 #include <isccfg/cfg.h>
45 #include <isccc/alist.h>
46 #include <isccc/base64.h>
48 #include <isccc/ccmsg.h>
49 #include <isccc/result.h>
50 #include <isccc/sexpr.h>
51 #include <isccc/types.h>
52 #include <isccc/util.h>
57 #ifdef HAVE_GETADDRINFO
58 #ifdef HAVE_GAISTRERROR
59 #define USE_GETADDRINFO
64 #ifndef USE_GETADDRINFO
65 #ifndef ISC_PLATFORM_NONSTDHERRNO
71 isc_boolean_t verbose;
73 static const char *admin_conffile;
74 static const char *admin_keyfile;
75 static const char *version = VERSION;
76 static const char *servername = NULL;
77 static unsigned int remoteport = 0;
78 static isc_socketmgr_t *socketmgr = NULL;
79 static unsigned char databuf[2048];
80 static isccc_ccmsg_t ccmsg;
81 static isccc_region_t secret;
82 static isc_boolean_t failed = ISC_FALSE;
83 static isc_mem_t *mctx;
84 static int sends, recvs, connects;
87 static char program[256];
88 static isc_socket_t *sock = NULL;
89 static isc_uint32_t serial;
94 Usage: %s [-c config] [-s server] [-p port]\n\
95 [-k key-file ] [-y key] [-V] command\n\
97 command is one of the following:\n\
99 reload Reload configuration file and zones.\n\
100 reload zone [class [view]]\n\
101 Reload a single zone.\n\
102 refresh zone [class [view]]\n\
103 Schedule immediate maintenance for a zone.\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 Dump cache(s) to the dump file (named_dump.db).\n\
108 stop Save pending updates to master files and stop the server.\n\
109 halt Stop the server without saving pending updates.\n\
110 trace Increment debugging level by one.\n\
111 trace level Change the debugging level.\n\
112 notrace Set debugging level to 0.\n\
113 flush Flushes all of the server's caches.\n\
114 flush [view] Flushes the server's cache for a view.\n\
115 status Display status of the server.\n\
116 *restart Restart the server.\n\
118 * == not yet implemented\n\
126 get_address(const char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
129 isc_boolean_t have_ipv6;
130 #ifdef USE_GETADDRINFO
131 struct addrinfo *res = NULL, hints;
137 have_ipv6 = ISC_TF(isc_net_probeipv6() == ISC_R_SUCCESS);
140 * Assume we have v4 if we don't have v6, since setup_libs
141 * fatal()'s out if we don't have either.
143 if (have_ipv6 && inet_pton(AF_INET6, host, &in6) == 1)
144 isc_sockaddr_fromin6(sockaddr, &in6, port);
145 else if (inet_pton(AF_INET, host, &in4) == 1)
146 isc_sockaddr_fromin(sockaddr, &in4, port);
148 #ifdef USE_GETADDRINFO
149 memset(&hints, 0, sizeof(hints));
151 hints.ai_family = PF_INET;
152 else if (isc_net_probeipv4() != ISC_R_SUCCESS)
153 hints.ai_family = PF_INET6;
155 hints.ai_family = PF_UNSPEC;
157 hints.ai_flags = AI_ADDRCONFIG;
160 hints.ai_socktype = SOCK_STREAM;
165 result = getaddrinfo(host, NULL, &hints, &res);
167 if (result == EAI_BADFLAGS &&
168 (hints.ai_flags & AI_ADDRCONFIG) != 0) {
169 hints.ai_flags &= ~AI_ADDRCONFIG;
175 fatal("Couldn't find server '%s': %s",
176 host, gai_strerror(result));
177 memcpy(&sockaddr->type.sa, res->ai_addr, res->ai_addrlen);
178 sockaddr->length = res->ai_addrlen;
179 isc_sockaddr_setport(sockaddr, port);
183 he = gethostbyname(host);
186 fatal("Couldn't find server '%s' (h_errno=%d)",
188 INSIST(he->h_addrtype == AF_INET);
189 isc_sockaddr_fromin(sockaddr,
190 (struct in_addr *)(he->h_addr_list[0]),
197 rndc_senddone(isc_task_t *task, isc_event_t *event) {
198 isc_socketevent_t *sevent = (isc_socketevent_t *)event;
203 if (sevent->result != ISC_R_SUCCESS)
204 fatal("send failed: %s", isc_result_totext(sevent->result));
205 isc_event_free(&event);
209 rndc_recvdone(isc_task_t *task, isc_event_t *event) {
210 isccc_sexpr_t *response = NULL;
212 isccc_region_t source;
213 char *errormsg = NULL;
214 char *textmsg = NULL;
219 if (ccmsg.result == ISC_R_EOF)
220 fatal("connection to remote host closed\n"
221 "This may indicate that the remote server is using "
222 "an older version of \n"
223 "the command protocol, this host is not authorized "
224 "to connect,\nor the key is invalid.");
226 if (ccmsg.result != ISC_R_SUCCESS)
227 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
229 source.rstart = isc_buffer_base(&ccmsg.buffer);
230 source.rend = isc_buffer_used(&ccmsg.buffer);
232 DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
234 data = isccc_alist_lookup(response, "_data");
236 fatal("no data section in response");
237 result = isccc_cc_lookupstring(data, "err", &errormsg);
238 if (result == ISC_R_SUCCESS) {
240 fprintf(stderr, "%s: '%s' failed: %s\n",
241 progname, command, errormsg);
243 else if (result != ISC_R_NOTFOUND)
244 fprintf(stderr, "%s: parsing response failed: %s\n",
245 progname, isc_result_totext(result));
247 result = isccc_cc_lookupstring(data, "text", &textmsg);
248 if (result == ISC_R_SUCCESS)
249 printf("%s\n", textmsg);
250 else if (result != ISC_R_NOTFOUND)
251 fprintf(stderr, "%s: parsing response failed: %s\n",
252 progname, isc_result_totext(result));
254 isc_event_free(&event);
255 isccc_sexpr_free(&response);
256 isc_socket_detach(&sock);
257 isc_task_shutdown(task);
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;
268 isccc_sexpr_t *request = NULL;
272 isccc_region_t message;
278 if (ccmsg.result == ISC_R_EOF)
279 fatal("connection to remote host closed\n"
280 "This may indicate that the remote server is using "
281 "an older version of \n"
282 "the command protocol, this host is not authorized "
283 "to connect,\nor the key is invalid.");
285 if (ccmsg.result != ISC_R_SUCCESS)
286 fatal("recv failed: %s", isc_result_totext(ccmsg.result));
288 source.rstart = isc_buffer_base(&ccmsg.buffer);
289 source.rend = isc_buffer_used(&ccmsg.buffer);
291 DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
293 _ctrl = isccc_alist_lookup(response, "_ctrl");
295 fatal("_ctrl section missing");
297 if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
300 isc_stdtime_get(&now);
302 DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
303 now, now + 60, &request));
304 data = isccc_alist_lookup(request, "_data");
306 fatal("_data section missing");
307 if (isccc_cc_definestring(data, "type", args) == NULL)
308 fatal("out of memory");
310 _ctrl = isccc_alist_lookup(request, "_ctrl");
312 fatal("_ctrl section missing");
313 if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
314 fatal("out of memory");
316 message.rstart = databuf + 4;
317 message.rend = databuf + sizeof(databuf);
318 DO("render message", isccc_cc_towire(request, &message, &secret));
319 len = sizeof(databuf) - REGION_SIZE(message);
320 isc_buffer_init(&b, databuf, 4);
321 isc_buffer_putuint32(&b, len - 4);
325 isccc_ccmsg_cancelread(&ccmsg);
326 DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
327 rndc_recvdone, NULL));
329 DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
333 isc_event_free(&event);
334 isccc_sexpr_free(&response);
339 rndc_connected(isc_task_t *task, isc_event_t *event) {
340 isc_socketevent_t *sevent = (isc_socketevent_t *)event;
341 isccc_sexpr_t *request = NULL;
344 isccc_region_t message;
352 if (sevent->result != ISC_R_SUCCESS)
353 fatal("connect failed: %s", isc_result_totext(sevent->result));
355 isc_stdtime_get(&now);
356 DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
357 now, now + 60, &request));
358 data = isccc_alist_lookup(request, "_data");
360 fatal("_data section missing");
361 if (isccc_cc_definestring(data, "type", "null") == NULL)
362 fatal("out of memory");
363 message.rstart = databuf + 4;
364 message.rend = databuf + sizeof(databuf);
365 DO("render message", isccc_cc_towire(request, &message, &secret));
366 len = sizeof(databuf) - REGION_SIZE(message);
367 isc_buffer_init(&b, databuf, 4);
368 isc_buffer_putuint32(&b, len - 4);
372 isccc_ccmsg_init(mctx, sock, &ccmsg);
373 isccc_ccmsg_setmaxsize(&ccmsg, 1024);
375 DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
376 rndc_recvnonce, NULL));
378 DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
381 isc_event_free(&event);
385 rndc_start(isc_task_t *task, isc_event_t *event) {
388 char socktext[ISC_SOCKADDR_FORMATSIZE];
390 isc_event_free(&event);
392 get_address(servername, (in_port_t) remoteport, &addr);
394 isc_sockaddr_format(&addr, socktext, sizeof(socktext));
396 notify("using server %s (%s)", servername, socktext);
398 DO("create socket", isc_socket_create(socketmgr,
399 isc_sockaddr_pf(&addr),
400 isc_sockettype_tcp, &sock));
401 DO("connect", isc_socket_connect(sock, &addr, task, rndc_connected,
407 parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
408 cfg_parser_t **pctxp, cfg_obj_t **configp)
411 const char *conffile = admin_conffile;
412 cfg_obj_t *defkey = NULL;
413 cfg_obj_t *options = NULL;
414 cfg_obj_t *servers = NULL;
415 cfg_obj_t *server = NULL;
416 cfg_obj_t *keys = NULL;
417 cfg_obj_t *key = NULL;
418 cfg_obj_t *defport = NULL;
419 cfg_obj_t *secretobj = NULL;
420 cfg_obj_t *algorithmobj = NULL;
421 cfg_obj_t *config = NULL;
423 const char *secretstr;
424 const char *algorithm;
425 static char secretarray[1024];
426 const cfg_type_t *conftype = &cfg_type_rndcconf;
427 isc_boolean_t key_only = ISC_FALSE;
429 if (! isc_file_exists(conffile)) {
430 conffile = admin_keyfile;
431 conftype = &cfg_type_rndckey;
433 if (! isc_file_exists(conffile))
434 fatal("neither %s nor %s was found",
435 admin_conffile, admin_keyfile);
439 DO("create parser", cfg_parser_create(mctx, log, pctxp));
442 * The parser will output its own errors, so DO() is not used.
444 result = cfg_parse_file(*pctxp, conffile, conftype, &config);
445 if (result != ISC_R_SUCCESS)
446 fatal("could not load rndc configuration");
449 (void)cfg_map_get(config, "options", &options);
451 if (key_only && servername == NULL)
452 servername = "127.0.0.1";
453 else if (servername == NULL && options != NULL) {
454 cfg_obj_t *defserverobj = NULL;
455 (void)cfg_map_get(options, "default-server", &defserverobj);
456 if (defserverobj != NULL)
457 servername = cfg_obj_asstring(defserverobj);
460 if (servername == NULL)
461 fatal("no server specified and no default");
464 cfg_map_get(config, "server", &servers);
465 if (servers != NULL) {
466 for (elt = cfg_list_first(servers);
468 elt = cfg_list_next(elt))
471 server = cfg_listelt_value(elt);
472 name = cfg_obj_asstring(cfg_map_getname(server));
473 if (strcasecmp(name, servername) == 0)
481 * Look for the name of the key to use.
484 ; /* Was set on command line, do nothing. */
485 else if (server != NULL) {
486 DO("get key for server", cfg_map_get(server, "key", &defkey));
487 keyname = cfg_obj_asstring(defkey);
488 } else if (options != NULL) {
489 DO("get default key", cfg_map_get(options, "default-key",
491 keyname = cfg_obj_asstring(defkey);
492 } else if (!key_only)
493 fatal("no key for server and no default");
496 * Get the key's definition.
499 DO("get key", cfg_map_get(config, "key", &key));
501 DO("get config key list", cfg_map_get(config, "key", &keys));
502 for (elt = cfg_list_first(keys);
504 elt = cfg_list_next(elt))
506 key = cfg_listelt_value(elt);
507 if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
512 fatal("no key definition for name %s", keyname);
514 (void)cfg_map_get(key, "secret", &secretobj);
515 (void)cfg_map_get(key, "algorithm", &algorithmobj);
516 if (secretobj == NULL || algorithmobj == NULL)
517 fatal("key must have algorithm and secret");
519 secretstr = cfg_obj_asstring(secretobj);
520 algorithm = cfg_obj_asstring(algorithmobj);
522 if (strcasecmp(algorithm, "hmac-md5") != 0)
523 fatal("unsupported algorithm: %s", algorithm);
525 secret.rstart = (unsigned char *)secretarray;
526 secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
527 DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
528 secret.rend = secret.rstart;
529 secret.rstart = (unsigned char *)secretarray;
532 * Find the port to connect to.
535 ; /* Was set on command line, do nothing. */
538 (void)cfg_map_get(server, "port", &defport);
539 if (defport == NULL && options != NULL)
540 cfg_map_get(options, "default-port", &defport);
542 if (defport != NULL) {
543 remoteport = cfg_obj_asuint32(defport);
544 if (remoteport > 65535 || remoteport == 0)
545 fatal("port %d out of range", remoteport);
546 } else if (remoteport == 0)
547 remoteport = NS_CONTROL_PORT;
553 main(int argc, char **argv) {
554 isc_boolean_t show_final_mem = ISC_FALSE;
555 isc_result_t result = ISC_R_SUCCESS;
556 isc_taskmgr_t *taskmgr = NULL;
557 isc_task_t *task = NULL;
558 isc_log_t *log = NULL;
559 isc_logconfig_t *logconfig = NULL;
560 isc_logdestination_t logdest;
561 cfg_parser_t *pctx = NULL;
562 cfg_obj_t *config = NULL;
563 const char *keyname = NULL;
569 result = isc_file_progname(*argv, program, sizeof(program));
570 if (result != ISC_R_SUCCESS)
571 memcpy(program, "rndc", 5);
574 admin_conffile = RNDC_CONFFILE;
575 admin_keyfile = RNDC_KEYFILE;
579 while ((ch = isc_commandline_parse(argc, argv, "c:k:Mmp:s:Vy:"))
583 admin_conffile = isc_commandline_argument;
587 admin_keyfile = isc_commandline_argument;
591 isc_mem_debugging = 1;
595 show_final_mem = ISC_TRUE;
599 remoteport = atoi(isc_commandline_argument);
600 if (remoteport > 65535 || remoteport == 0)
601 fatal("port '%s' out of range",
602 isc_commandline_argument);
606 servername = isc_commandline_argument;
612 keyname = isc_commandline_argument;
618 fatal("unexpected error parsing command arguments: "
624 argc -= isc_commandline_index;
625 argv += isc_commandline_index;
630 isc_random_get(&serial);
632 DO("create memory context", isc_mem_create(0, 0, &mctx));
633 DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
634 DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
635 DO("create task", isc_task_create(taskmgr, 0, &task));
637 DO("create logging context", isc_log_create(mctx, &log, &logconfig));
638 isc_log_setcontext(log);
639 DO("setting log tag", isc_log_settag(logconfig, progname));
640 logdest.file.stream = stderr;
641 logdest.file.name = NULL;
642 logdest.file.versions = ISC_LOG_ROLLNEVER;
643 logdest.file.maximum_size = 0;
644 DO("creating log channel",
645 isc_log_createchannel(logconfig, "stderr",
646 ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
647 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
648 DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
651 parse_config(mctx, log, keyname, &pctx, &config);
653 isccc_result_register();
658 * Convert argc/argv into a space-delimited command string
659 * similar to what the user might enter in interactive mode
660 * (if that were implemented).
663 for (i = 0; i < argc; i++)
664 argslen += strlen(argv[i]) + 1;
666 args = isc_mem_get(mctx, argslen);
668 DO("isc_mem_get", ISC_R_NOMEMORY);
671 for (i = 0; i < argc; i++) {
672 size_t len = strlen(argv[i]);
673 memcpy(p, argv[i], len);
680 INSIST(p == args + argslen);
682 notify("%s", command);
684 if (strcmp(command, "restart") == 0)
685 fatal("'%s' is not implemented", command);
687 DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL));
691 if (connects > 0 || sends > 0 || recvs > 0)
692 isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
694 isc_task_detach(&task);
695 isc_taskmgr_destroy(&taskmgr);
696 isc_socketmgr_destroy(&socketmgr);
697 isc_log_destroy(&log);
698 isc_log_setcontext(NULL);
700 cfg_obj_destroy(pctx, &config);
701 cfg_parser_destroy(&pctx);
703 isc_mem_put(mctx, args, argslen);
704 isccc_ccmsg_invalidate(&ccmsg);
707 isc_mem_stats(mctx, stderr);
709 isc_mem_destroy(&mctx);