rndc.c revision 1.13 1 /* $NetBSD: rndc.c,v 1.13 2025/01/26 16:24:35 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16 /*! \file */
17
18 #include <inttypes.h>
19 #include <stdbool.h>
20 #include <stdlib.h>
21
22 #include <isc/attributes.h>
23 #include <isc/buffer.h>
24 #include <isc/commandline.h>
25 #include <isc/file.h>
26 #include <isc/getaddresses.h>
27 #include <isc/log.h>
28 #include <isc/loop.h>
29 #include <isc/managers.h>
30 #include <isc/mem.h>
31 #include <isc/net.h>
32 #include <isc/netmgr.h>
33 #include <isc/random.h>
34 #include <isc/refcount.h>
35 #include <isc/result.h>
36 #include <isc/stdtime.h>
37 #include <isc/string.h>
38 #include <isc/thread.h>
39 #include <isc/util.h>
40
41 #include <dns/name.h>
42
43 #include <isccc/alist.h>
44 #include <isccc/base64.h>
45 #include <isccc/cc.h>
46 #include <isccc/ccmsg.h>
47 #include <isccc/sexpr.h>
48 #include <isccc/types.h>
49 #include <isccc/util.h>
50
51 #include <isccfg/namedconf.h>
52
53 #include "util.h"
54
55 #define SERVERADDRS 10
56 #define RNDC_TIMEOUT 60 * 1000
57
58 const char *progname = NULL;
59 bool verbose;
60
61 static isc_nm_t *netmgr = NULL;
62 static isc_loopmgr_t *loopmgr = NULL;
63
64 static const char *admin_conffile = NULL;
65 static const char *admin_keyfile = NULL;
66 static const char *version = PACKAGE_VERSION;
67 static const char *servername = NULL;
68 static isc_sockaddr_t serveraddrs[SERVERADDRS];
69 static isc_sockaddr_t local4, local6;
70 static bool local4set = false, local6set = false;
71 static int nserveraddrs;
72 static int currentaddr = 0;
73 static unsigned int remoteport = 0;
74 static isc_buffer_t *databuf = NULL;
75 static isccc_ccmsg_t rndc_ccmsg;
76 static uint32_t algorithm;
77 static isccc_region_t secret;
78 static bool failed = false;
79 static bool c_flag = false;
80 static isc_mem_t *rndc_mctx = NULL;
81 static char *command = NULL;
82 static char *args = NULL;
83 static char program[256];
84 static uint32_t serial;
85 static bool quiet = false;
86 static bool showresult = false;
87 static int32_t timeout = RNDC_TIMEOUT;
88
89 static void
90 rndc_startconnect(isc_sockaddr_t *addr);
91
92 noreturn static void
93 usage(int status);
94
95 static void
96 usage(int status) {
97 fprintf(stderr, "\
98 Usage: %s [-b address] [-c config] [-s server] [-p port]\n\
99 [-k key-file ] [-y key] [-r] [-V] [-4 | -6] command\n\
100 \n\
101 command is one of the following:\n\
102 \n\
103 addzone zone [class [view]] { zone-options }\n\
104 Add zone to given view. Requires allow-new-zones option.\n\
105 delzone [-clean] zone [class [view]]\n\
106 Removes zone from given view.\n\
107 dnssec -checkds [-key id [-alg algorithm]] [-when time] (published|withdrawn) zone [class [view]]\n\
108 Mark the DS record for the KSK of the given zone as seen\n\
109 in the parent. If the zone has multiple KSKs, select a\n\
110 specific key by providing the keytag with -key id and\n\
111 optionally the key's algorithm with -alg algorithm.\n\
112 Requires the zone to have a dnssec-policy.\n\
113 dnssec -rollover -key id [-alg algorithm] [-when time] zone [class [view]]\n\
114 Rollover key with id of the given zone. Requires the zone\n\
115 to have a dnssec-policy.\n\
116 dnssec -status zone [class [view]]\n\
117 Show the DNSSEC signing state for the specified zone.\n\
118 Requires the zone to have a dnssec-policy.\n\
119 dnstap -reopen\n\
120 Close, truncate and re-open the DNSTAP output file.\n\
121 dnstap -roll [count]\n\
122 Close, rename and re-open the DNSTAP output file(s).\n\
123 dumpdb [-all|-cache|-zones|-adb|-bad|-expired|-fail] [view ...]\n\
124 Dump cache(s) to the dump file (named_dump.db).\n\
125 flush Flushes all of the server's caches.\n\
126 flush [view] Flushes the server's cache for a view.\n\
127 flushname name [view]\n\
128 Flush the given name from the server's cache(s)\n\
129 flushtree name [view]\n\
130 Flush all names under the given name from the server's cache(s)\n\
131 freeze Suspend updates to all dynamic zones.\n\
132 freeze zone [class [view]]\n\
133 Suspend updates to a dynamic zone.\n\
134 halt Stop the server without saving pending updates.\n\
135 halt -p Stop the server without saving pending updates reporting\n\
136 process id.\n\
137 skr -import file zone [class [view]]\n\
138 Import a SKR file for the specified zone, for offline KSK\n\
139 signing.\n\
140 loadkeys zone [class [view]]\n\
141 Update keys without signing immediately.\n\
142 managed-keys refresh [class [view]]\n\
143 Check trust anchor for RFC 5011 key changes\n\
144 managed-keys status [class [view]]\n\
145 Display RFC 5011 managed keys information\n\
146 managed-keys sync [class [view]]\n\
147 Write RFC 5011 managed keys to disk\n\
148 modzone zone [class [view]] { zone-options }\n\
149 Modify a zone's configuration.\n\
150 Requires allow-new-zones option.\n\
151 notify zone [class [view]]\n\
152 Resend NOTIFY messages for the zone.\n\
153 notrace Set debugging level to 0.\n\
154 nta -dump\n\
155 List all negative trust anchors.\n\
156 nta [-lifetime duration] [-force] domain [view]\n\
157 Set a negative trust anchor, disabling DNSSEC validation\n\
158 for the given domain.\n\
159 Using -lifetime specifies the duration of the NTA, up\n\
160 to one week.\n\
161 Using -force prevents the NTA from expiring before its\n\
162 full lifetime, even if the domain can validate sooner.\n\
163 nta -remove domain [view]\n\
164 Remove a negative trust anchor, re-enabling validation\n\
165 for the given domain.\n\
166 querylog [ on | off ]\n\
167 Enable / disable query logging.\n\
168 reconfig Reload configuration file and new zones only.\n\
169 recursing Dump the queries that are currently recursing (named.recursing)\n\
170 refresh zone [class [view]]\n\
171 Schedule immediate maintenance for a zone.\n\
172 reload Reload configuration file and zones.\n\
173 reload zone [class [view]]\n\
174 Reload a single zone.\n\
175 retransfer zone [class [view]]\n\
176 Retransfer a single zone without checking serial number.\n\
177 scan Scan available network interfaces for changes.\n\
178 secroots [view ...]\n\
179 Write security roots to the secroots file.\n\
180 serve-stale [ on | off | reset | status ] [class [view]]\n\
181 Control whether stale answers are returned\n\
182 showzone zone [class [view]]\n\
183 Print a zone's configuration.\n\
184 sign zone [class [view]]\n\
185 Update zone keys, and sign as needed.\n\
186 signing -clear all zone [class [view]]\n\
187 Remove the private records for all keys that have\n\
188 finished signing the given zone.\n\
189 signing -clear <keyid>/<algorithm> zone [class [view]]\n\
190 Remove the private record that indicating the given key\n\
191 has finished signing the given zone.\n\
192 signing -list zone [class [view]]\n\
193 List the private records showing the state of DNSSEC\n\
194 signing in the given zone.\n\
195 signing -nsec3param hash flags iterations salt zone [class [view]]\n\
196 Add NSEC3 chain to zone if already signed.\n\
197 Prime zone with NSEC3 chain if not yet signed.\n\
198 signing -nsec3param none zone [class [view]]\n\
199 Remove NSEC3 chains from zone.\n\
200 signing -serial <value> zone [class [view]]\n\
201 Set the zones's serial to <value>.\n\
202 stats Write server statistics to the statistics file.\n\
203 status Display status of the server.\n\
204 stop Save pending updates to master files and stop the server.\n\
205 stop -p Save pending updates to master files and stop the server\n\
206 reporting process id.\n\
207 sync [-clean] Dump changes to all dynamic zones to disk, and optionally\n\
208 remove their journal files.\n\
209 sync [-clean] zone [class [view]]\n\
210 Dump a single zone's changes to disk, and optionally\n\
211 remove its journal file.\n\
212 tcp-timeouts Display the tcp-*-timeout option values\n\
213 tcp-timeouts initial idle keepalive advertised\n\
214 Update the tcp-*-timeout option values\n\
215 thaw Enable updates to all dynamic zones and reload them.\n\
216 thaw zone [class [view]]\n\
217 Enable updates to a frozen dynamic zone and reload it.\n\
218 trace Increment debugging level by one.\n\
219 trace level Change the debugging level.\n\
220 validation [ on | off | status ] [view]\n\
221 Enable / disable DNSSEC validation.\n\
222 zonestatus zone [class [view]]\n\
223 Display the current status of a zone.\n\
224 \n\
225 Version: %s\n",
226 progname, version);
227
228 exit(status);
229 }
230
231 #define CMDLINE_FLAGS "46b:c:hk:Mmp:qrs:t:Vy:"
232
233 static void
234 preparse_args(int argc, char **argv) {
235 bool ipv4only = false, ipv6only = false;
236 int ch;
237
238 while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
239 switch (ch) {
240 case '4':
241 if (ipv6only) {
242 fatal("only one of -4 and -6 allowed");
243 }
244 ipv4only = true;
245 break;
246 case '6':
247 if (ipv4only) {
248 fatal("only one of -4 and -6 allowed");
249 }
250 ipv6only = true;
251 break;
252 default:
253 break;
254 }
255 }
256
257 isc_commandline_reset = true;
258 isc_commandline_index = 1;
259 }
260
261 static void
262 get_addresses(const char *host, in_port_t port) {
263 isc_result_t result;
264 int found = 0, count;
265
266 REQUIRE(host != NULL);
267
268 count = SERVERADDRS - nserveraddrs;
269 result = isc_getaddresses(host, port, &serveraddrs[nserveraddrs], count,
270 &found);
271 nserveraddrs += found;
272
273 if (result != ISC_R_SUCCESS) {
274 fatal("couldn't get address for '%s': %s", host,
275 isc_result_totext(result));
276 }
277 INSIST(nserveraddrs > 0);
278 }
279
280 static void
281 rndc_senddone(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result,
282 void *arg ISC_ATTR_UNUSED) {
283 if (result != ISC_R_SUCCESS) {
284 fatal("send failed: %s", isc_result_totext(result));
285 }
286 }
287
288 static void
289 rndc_recvdone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
290 isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg;
291 isccc_sexpr_t *response = NULL;
292 isccc_sexpr_t *data = NULL;
293 isccc_region_t source;
294 char *errormsg = NULL;
295 char *textmsg = NULL;
296
297 REQUIRE(handle != NULL);
298 REQUIRE(ccmsg != NULL);
299
300 if (result == ISC_R_EOF) {
301 fatal("connection to remote host closed.\n"
302 "* This may indicate that the\n"
303 "* remote server is using an older\n"
304 "* version of the command protocol,\n"
305 "* this host is not authorized to connect,\n"
306 "* the clocks are not synchronized,\n"
307 "* the key signing algorithm is incorrect,\n"
308 "* or the key is invalid.");
309 } else if (result != ISC_R_SUCCESS) {
310 fatal("recv failed: %s", isc_result_totext(result));
311 }
312
313 isccc_ccmsg_toregion(ccmsg, &source);
314
315 DO("parse message",
316 isccc_cc_fromwire(&source, &response, algorithm, &secret));
317
318 data = isccc_alist_lookup(response, "_data");
319 if (!isccc_alist_alistp(data)) {
320 fatal("bad or missing data section in response");
321 }
322 result = isccc_cc_lookupstring(data, "err", &errormsg);
323 if (result == ISC_R_SUCCESS) {
324 failed = true;
325 fprintf(stderr, "%s: '%s' failed: %s\n", progname, command,
326 errormsg);
327 } else if (result != ISC_R_NOTFOUND) {
328 fprintf(stderr, "%s: parsing response failed: %s\n", progname,
329 isc_result_totext(result));
330 }
331
332 result = isccc_cc_lookupstring(data, "text", &textmsg);
333 if (result == ISC_R_SUCCESS) {
334 if ((!quiet || failed) && strlen(textmsg) != 0U) {
335 fprintf(failed ? stderr : stdout, "%s\n", textmsg);
336 }
337 } else if (result != ISC_R_NOTFOUND) {
338 fprintf(stderr, "%s: parsing response failed: %s\n", progname,
339 isc_result_totext(result));
340 }
341
342 if (showresult) {
343 isc_result_t eresult;
344
345 result = isccc_cc_lookupuint32(data, "result", &eresult);
346 if (result == ISC_R_SUCCESS) {
347 printf("%s %u\n", isc_result_toid(eresult), eresult);
348 } else {
349 printf("NONE -1\n");
350 }
351 }
352
353 isccc_sexpr_free(&response);
354
355 isccc_ccmsg_disconnect(ccmsg);
356 isc_loopmgr_shutdown(loopmgr);
357 }
358
359 static void
360 rndc_recvnonce(isc_nmhandle_t *handle ISC_ATTR_UNUSED, isc_result_t result,
361 void *arg) {
362 isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg;
363 isccc_sexpr_t *response = NULL;
364 isccc_sexpr_t *_ctrl = NULL;
365 isccc_region_t source;
366 uint32_t nonce;
367 isccc_sexpr_t *request = NULL;
368 isccc_time_t now = isc_stdtime_now();
369 isc_region_t r;
370 isccc_sexpr_t *data = NULL;
371 isc_buffer_t b;
372
373 REQUIRE(ccmsg != NULL);
374
375 if (result == ISC_R_EOF) {
376 fatal("connection to remote host closed.\n"
377 "* This may indicate that the\n"
378 "* remote server is using an older\n"
379 "* version of the command protocol,\n"
380 "* this host is not authorized to connect,\n"
381 "* the clocks are not synchronized,\n"
382 "* the key signing algorithm is incorrect\n"
383 "* or the key is invalid.");
384 } else if (result != ISC_R_SUCCESS) {
385 fatal("recv failed: %s", isc_result_totext(result));
386 }
387
388 isccc_ccmsg_toregion(ccmsg, &source);
389
390 DO("parse message",
391 isccc_cc_fromwire(&source, &response, algorithm, &secret));
392
393 _ctrl = isccc_alist_lookup(response, "_ctrl");
394 if (!isccc_alist_alistp(_ctrl)) {
395 fatal("bad or missing ctrl section in response");
396 }
397 nonce = 0;
398 if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS) {
399 nonce = 0;
400 }
401
402 DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
403 now, now + 60, &request));
404 data = isccc_alist_lookup(request, "_data");
405 if (data == NULL) {
406 fatal("_data section missing");
407 }
408 if (isccc_cc_definestring(data, "type", args) == NULL) {
409 fatal("out of memory");
410 }
411 if (nonce != 0) {
412 _ctrl = isccc_alist_lookup(request, "_ctrl");
413 if (_ctrl == NULL) {
414 fatal("_ctrl section missing");
415 }
416 if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL) {
417 fatal("out of memory");
418 }
419 }
420
421 isc_buffer_clear(databuf);
422 /* Skip the length field (4 bytes) */
423 isc_buffer_add(databuf, 4);
424
425 DO("render message",
426 isccc_cc_towire(request, &databuf, algorithm, &secret));
427
428 isc_buffer_init(&b, databuf->base, 4);
429 isc_buffer_putuint32(&b, databuf->used - 4);
430
431 r.base = databuf->base;
432 r.length = databuf->used;
433
434 isccc_ccmsg_readmessage(ccmsg, rndc_recvdone, ccmsg);
435 isccc_ccmsg_sendmessage(ccmsg, &r, rndc_senddone, NULL);
436
437 isccc_sexpr_free(&response);
438 isccc_sexpr_free(&request);
439 return;
440 }
441
442 static void
443 rndc_connected(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
444 isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg;
445 char socktext[ISC_SOCKADDR_FORMATSIZE];
446 isccc_sexpr_t *request = NULL;
447 isccc_sexpr_t *data = NULL;
448 isccc_time_t now = isc_stdtime_now();
449 isc_region_t r;
450 isc_buffer_t b;
451
452 REQUIRE(ccmsg != NULL);
453
454 if (result != ISC_R_SUCCESS) {
455 isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
456 sizeof(socktext));
457 if (++currentaddr < nserveraddrs) {
458 notify("connection failed: %s: %s", socktext,
459 isc_result_totext(result));
460 rndc_startconnect(&serveraddrs[currentaddr]);
461 return;
462 }
463
464 fatal("connect failed: %s: %s", socktext,
465 isc_result_totext(result));
466 }
467
468 DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
469 now, now + 60, &request));
470 data = isccc_alist_lookup(request, "_data");
471 if (data == NULL) {
472 fatal("_data section missing");
473 }
474 if (isccc_cc_definestring(data, "type", "null") == NULL) {
475 fatal("out of memory");
476 }
477
478 isc_buffer_clear(databuf);
479 /* Skip the length field (4 bytes) */
480 isc_buffer_add(databuf, 4);
481
482 DO("render message",
483 isccc_cc_towire(request, &databuf, algorithm, &secret));
484
485 isc_buffer_init(&b, databuf->base, 4);
486 isc_buffer_putuint32(&b, databuf->used - 4);
487
488 r.base = databuf->base;
489 r.length = databuf->used;
490
491 /* isccc_ccmsg_init() attaches to the handle */
492 isccc_ccmsg_init(rndc_mctx, handle, ccmsg);
493 isccc_ccmsg_setmaxsize(ccmsg, 1024 * 1024);
494
495 isccc_ccmsg_readmessage(ccmsg, rndc_recvnonce, ccmsg);
496 isccc_ccmsg_sendmessage(ccmsg, &r, rndc_senddone, NULL);
497
498 isccc_sexpr_free(&request);
499 }
500
501 static void
502 rndc_startconnect(isc_sockaddr_t *addr) {
503 char socktext[ISC_SOCKADDR_FORMATSIZE];
504 isc_sockaddr_t *local = NULL;
505
506 isc_sockaddr_format(addr, socktext, sizeof(socktext));
507
508 notify("using server %s (%s)", servername, socktext);
509
510 switch (isc_sockaddr_pf(addr)) {
511 case AF_INET:
512 local = &local4;
513 break;
514 case AF_INET6:
515 local = &local6;
516 break;
517 default:
518 UNREACHABLE();
519 }
520
521 isc_nm_tcpconnect(netmgr, local, addr, rndc_connected, &rndc_ccmsg,
522 timeout);
523 }
524
525 static void
526 rndc_start(void *arg) {
527 UNUSED(arg);
528
529 currentaddr = 0;
530 rndc_startconnect(&serveraddrs[currentaddr]);
531 }
532
533 static void
534 parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
535 cfg_parser_t **pctxp, cfg_obj_t **configp) {
536 isc_result_t result;
537 const char *conffile = admin_conffile;
538 const cfg_obj_t *addresses = NULL;
539 const cfg_obj_t *defkey = NULL;
540 const cfg_obj_t *options = NULL;
541 const cfg_obj_t *servers = NULL;
542 const cfg_obj_t *server = NULL;
543 const cfg_obj_t *keys = NULL;
544 const cfg_obj_t *key = NULL;
545 const cfg_obj_t *defport = NULL;
546 const cfg_obj_t *secretobj = NULL;
547 const cfg_obj_t *algorithmobj = NULL;
548 cfg_obj_t *config = NULL;
549 const cfg_obj_t *address = NULL;
550 const cfg_listelt_t *elt;
551 const char *secretstr;
552 const char *algorithmstr;
553 static char secretarray[1024];
554 const cfg_type_t *conftype = &cfg_type_rndcconf;
555 bool key_only = false;
556 const cfg_listelt_t *element;
557
558 if (!isc_file_exists(conffile)) {
559 conffile = admin_keyfile;
560 conftype = &cfg_type_rndckey;
561
562 if (c_flag) {
563 fatal("%s does not exist", admin_conffile);
564 }
565
566 if (!isc_file_exists(conffile)) {
567 fatal("neither %s nor %s was found", admin_conffile,
568 admin_keyfile);
569 }
570 key_only = true;
571 } else if (!c_flag && isc_file_exists(admin_keyfile)) {
572 fprintf(stderr,
573 "WARNING: key file (%s) exists, but using "
574 "default configuration file (%s)\n",
575 admin_keyfile, admin_conffile);
576 }
577
578 DO("create parser", cfg_parser_create(mctx, log, pctxp));
579
580 /*
581 * The parser will output its own errors, so DO() is not used.
582 */
583 result = cfg_parse_file(*pctxp, conffile, conftype, &config);
584 if (result != ISC_R_SUCCESS) {
585 fatal("could not load rndc configuration");
586 }
587
588 if (!key_only) {
589 (void)cfg_map_get(config, "options", &options);
590 }
591
592 if (key_only && servername == NULL) {
593 servername = "127.0.0.1";
594 } else if (servername == NULL && options != NULL) {
595 const cfg_obj_t *defserverobj = NULL;
596 (void)cfg_map_get(options, "default-server", &defserverobj);
597 if (defserverobj != NULL) {
598 servername = cfg_obj_asstring(defserverobj);
599 }
600 }
601
602 if (servername == NULL) {
603 fatal("no server specified and no default");
604 }
605
606 if (!key_only) {
607 (void)cfg_map_get(config, "server", &servers);
608 if (servers != NULL) {
609 for (elt = cfg_list_first(servers); elt != NULL;
610 elt = cfg_list_next(elt))
611 {
612 const char *name = NULL;
613 server = cfg_listelt_value(elt);
614 name = cfg_obj_asstring(
615 cfg_map_getname(server));
616 if (strcasecmp(name, servername) == 0) {
617 break;
618 }
619 server = NULL;
620 }
621 }
622 }
623
624 /*
625 * Look for the name of the key to use.
626 */
627 if (keyname != NULL) {
628 /* Was set on command line, do nothing. */
629 } else if (server != NULL) {
630 DO("get key for server", cfg_map_get(server, "key", &defkey));
631 keyname = cfg_obj_asstring(defkey);
632 } else if (options != NULL) {
633 DO("get default key",
634 cfg_map_get(options, "default-key", &defkey));
635 keyname = cfg_obj_asstring(defkey);
636 } else if (!key_only) {
637 fatal("no key for server and no default");
638 }
639
640 /*
641 * Get the key's definition.
642 */
643 if (key_only) {
644 DO("get key", cfg_map_get(config, "key", &key));
645 } else {
646 DO("get config key list", cfg_map_get(config, "key", &keys));
647 for (elt = cfg_list_first(keys); elt != NULL;
648 elt = cfg_list_next(elt))
649 {
650 const char *name = NULL;
651
652 key = cfg_listelt_value(elt);
653 name = cfg_obj_asstring(cfg_map_getname(key));
654 if (strcasecmp(name, keyname) == 0) {
655 break;
656 }
657 }
658 if (elt == NULL) {
659 fatal("no key definition for name %s", keyname);
660 }
661 }
662 (void)cfg_map_get(key, "secret", &secretobj);
663 (void)cfg_map_get(key, "algorithm", &algorithmobj);
664 if (secretobj == NULL || algorithmobj == NULL) {
665 fatal("key must have algorithm and secret");
666 }
667
668 secretstr = cfg_obj_asstring(secretobj);
669 algorithmstr = cfg_obj_asstring(algorithmobj);
670
671 if (strcasecmp(algorithmstr, "hmac-md5") == 0) {
672 algorithm = ISCCC_ALG_HMACMD5;
673 } else if (strcasecmp(algorithmstr, "hmac-sha1") == 0) {
674 algorithm = ISCCC_ALG_HMACSHA1;
675 } else if (strcasecmp(algorithmstr, "hmac-sha224") == 0) {
676 algorithm = ISCCC_ALG_HMACSHA224;
677 } else if (strcasecmp(algorithmstr, "hmac-sha256") == 0) {
678 algorithm = ISCCC_ALG_HMACSHA256;
679 } else if (strcasecmp(algorithmstr, "hmac-sha384") == 0) {
680 algorithm = ISCCC_ALG_HMACSHA384;
681 } else if (strcasecmp(algorithmstr, "hmac-sha512") == 0) {
682 algorithm = ISCCC_ALG_HMACSHA512;
683 } else {
684 fatal("unsupported algorithm: %s", algorithmstr);
685 }
686
687 secret.rstart = (unsigned char *)secretarray;
688 secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
689 DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
690 secret.rend = secret.rstart;
691 secret.rstart = (unsigned char *)secretarray;
692
693 /*
694 * Find the port to connect to.
695 */
696 if (remoteport != 0) {
697 /* Was set on command line, do nothing. */
698 } else {
699 if (server != NULL) {
700 (void)cfg_map_get(server, "port", &defport);
701 }
702 if (defport == NULL && options != NULL) {
703 (void)cfg_map_get(options, "default-port", &defport);
704 }
705 }
706 if (defport != NULL) {
707 remoteport = cfg_obj_asuint32(defport);
708 if (remoteport > 65535 || remoteport == 0) {
709 fatal("port %u out of range", remoteport);
710 }
711 } else if (remoteport == 0) {
712 remoteport = NS_CONTROL_PORT;
713 }
714
715 if (server != NULL) {
716 result = cfg_map_get(server, "addresses", &addresses);
717 } else {
718 result = ISC_R_NOTFOUND;
719 }
720 if (result == ISC_R_SUCCESS) {
721 for (element = cfg_list_first(addresses); element != NULL;
722 element = cfg_list_next(element))
723 {
724 isc_sockaddr_t sa;
725
726 address = cfg_listelt_value(element);
727 if (!cfg_obj_issockaddr(address)) {
728 unsigned int myport;
729 const char *name;
730 const cfg_obj_t *obj;
731
732 obj = cfg_tuple_get(address, "name");
733 name = cfg_obj_asstring(obj);
734 obj = cfg_tuple_get(address, "port");
735 if (cfg_obj_isuint32(obj)) {
736 myport = cfg_obj_asuint32(obj);
737 if (myport > UINT16_MAX || myport == 0)
738 {
739 fatal("port %u out of range",
740 myport);
741 }
742 } else {
743 myport = remoteport;
744 }
745 if (nserveraddrs < SERVERADDRS) {
746 get_addresses(name, (in_port_t)myport);
747 } else {
748 fprintf(stderr,
749 "too many address: "
750 "%s: dropped\n",
751 name);
752 }
753 continue;
754 }
755 sa = *cfg_obj_assockaddr(address);
756 if (isc_sockaddr_getport(&sa) == 0) {
757 isc_sockaddr_setport(&sa, remoteport);
758 }
759 if (nserveraddrs < SERVERADDRS) {
760 serveraddrs[nserveraddrs++] = sa;
761 } else {
762 char socktext[ISC_SOCKADDR_FORMATSIZE];
763
764 isc_sockaddr_format(&sa, socktext,
765 sizeof(socktext));
766 fprintf(stderr,
767 "too many address: %s: dropped\n",
768 socktext);
769 }
770 }
771 }
772
773 if (!local4set && server != NULL) {
774 address = NULL;
775 cfg_map_get(server, "source-address", &address);
776 if (address != NULL) {
777 local4 = *cfg_obj_assockaddr(address);
778 local4set = true;
779 }
780 }
781 if (!local4set && options != NULL) {
782 address = NULL;
783 cfg_map_get(options, "default-source-address", &address);
784 if (address != NULL) {
785 local4 = *cfg_obj_assockaddr(address);
786 local4set = true;
787 }
788 }
789
790 if (!local6set && server != NULL) {
791 address = NULL;
792 cfg_map_get(server, "source-address-v6", &address);
793 if (address != NULL) {
794 local6 = *cfg_obj_assockaddr(address);
795 local6set = true;
796 }
797 }
798 if (!local6set && options != NULL) {
799 address = NULL;
800 cfg_map_get(options, "default-source-address-v6", &address);
801 if (address != NULL) {
802 local6 = *cfg_obj_assockaddr(address);
803 local6set = true;
804 }
805 }
806
807 *configp = config;
808 }
809
810 int
811 main(int argc, char **argv) {
812 isc_result_t result = ISC_R_SUCCESS;
813 bool show_final_mem = false;
814 isc_log_t *log = NULL;
815 isc_logconfig_t *logconfig = NULL;
816 isc_logdestination_t logdest;
817 cfg_parser_t *pctx = NULL;
818 cfg_obj_t *config = NULL;
819 const char *keyname = NULL;
820 struct in_addr in;
821 struct in6_addr in6;
822 char *p = NULL;
823 size_t argslen;
824 int ch;
825 int i;
826
827 result = isc_file_progname(*argv, program, sizeof(program));
828 if (result != ISC_R_SUCCESS) {
829 memmove(program, "rndc", 5);
830 }
831 progname = program;
832
833 admin_conffile = RNDC_CONFFILE;
834 admin_keyfile = RNDC_KEYFILE;
835
836 isc_sockaddr_any(&local4);
837 isc_sockaddr_any6(&local6);
838
839 isc_commandline_errprint = false;
840
841 preparse_args(argc, argv);
842
843 while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
844 switch (ch) {
845 case '4':
846 if (isc_net_probeipv4() != ISC_R_SUCCESS) {
847 fatal("can't find IPv4 networking");
848 }
849 isc_net_disableipv6();
850 break;
851 case '6':
852 if (isc_net_probeipv6() != ISC_R_SUCCESS) {
853 fatal("can't find IPv6 networking");
854 }
855 isc_net_disableipv4();
856 break;
857 case 'b':
858 if (inet_pton(AF_INET, isc_commandline_argument, &in) ==
859 1)
860 {
861 isc_sockaddr_fromin(&local4, &in, 0);
862 local4set = true;
863 } else if (inet_pton(AF_INET6, isc_commandline_argument,
864 &in6) == 1)
865 {
866 isc_sockaddr_fromin6(&local6, &in6, 0);
867 local6set = true;
868 }
869 break;
870
871 case 'c':
872 admin_conffile = isc_commandline_argument;
873 c_flag = true;
874 break;
875
876 case 'k':
877 admin_keyfile = isc_commandline_argument;
878 break;
879
880 case 'M':
881 isc_mem_debugging = ISC_MEM_DEBUGTRACE;
882 break;
883
884 case 'm':
885 show_final_mem = true;
886 break;
887
888 case 'p':
889 remoteport = atoi(isc_commandline_argument);
890 if (remoteport > 65535 || remoteport == 0) {
891 fatal("port '%s' out of range",
892 isc_commandline_argument);
893 }
894 break;
895
896 case 'q':
897 quiet = true;
898 break;
899
900 case 'r':
901 showresult = true;
902 break;
903
904 case 's':
905 servername = isc_commandline_argument;
906 break;
907
908 case 't':
909 timeout = strtol(isc_commandline_argument, &p, 10);
910 if (*p != '\0' || timeout < 0 || timeout > 86400) {
911 fatal("invalid timeout '%s'",
912 isc_commandline_argument);
913 }
914 timeout *= 1000;
915 break;
916
917 case 'V':
918 verbose = true;
919 break;
920
921 case 'y':
922 keyname = isc_commandline_argument;
923 break;
924
925 case '?':
926 if (isc_commandline_option != '?') {
927 fprintf(stderr, "%s: invalid argument -%c\n",
928 program, isc_commandline_option);
929 usage(1);
930 }
931 FALLTHROUGH;
932 case 'h':
933 usage(0);
934 break;
935 default:
936 fprintf(stderr, "%s: unhandled option -%c\n", program,
937 isc_commandline_option);
938 exit(EXIT_FAILURE);
939 }
940 }
941
942 argc -= isc_commandline_index;
943 argv += isc_commandline_index;
944
945 if (argv[0] == NULL) {
946 usage(1);
947 } else {
948 command = argv[0];
949 if (strcmp(command, "restart") == 0) {
950 fatal("'%s' is not implemented", command);
951 }
952 notify("%s", command);
953 }
954
955 serial = isc_random32();
956
957 isc_managers_create(&rndc_mctx, 1, &loopmgr, &netmgr);
958 isc_loopmgr_setup(loopmgr, rndc_start, NULL);
959
960 isc_nm_settimeouts(netmgr, timeout, timeout, timeout, 0);
961
962 isc_log_create(rndc_mctx, &log, &logconfig);
963 isc_log_setcontext(log);
964 isc_log_settag(logconfig, progname);
965 logdest.file.stream = stderr;
966 logdest.file.name = NULL;
967 logdest.file.versions = ISC_LOG_ROLLNEVER;
968 logdest.file.maximum_size = 0;
969 isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC,
970 ISC_LOG_INFO, &logdest,
971 ISC_LOG_PRINTTAG | ISC_LOG_PRINTLEVEL);
972 DO("enabling log channel",
973 isc_log_usechannel(logconfig, "stderr", NULL, NULL));
974
975 parse_config(rndc_mctx, log, keyname, &pctx, &config);
976
977 isc_buffer_allocate(rndc_mctx, &databuf, 2048);
978
979 /*
980 * Convert argc/argv into a space-delimited command string
981 * similar to what the user might enter in interactive mode
982 * (if that were implemented).
983 */
984 argslen = 0;
985 for (i = 0; i < argc; i++) {
986 argslen += strlen(argv[i]) + 1;
987 }
988
989 args = isc_mem_get(rndc_mctx, argslen);
990
991 p = args;
992 for (i = 0; i < argc; i++) {
993 size_t len = strlen(argv[i]);
994 memmove(p, argv[i], len);
995 p += len;
996 *p++ = ' ';
997 }
998
999 p--;
1000 *p++ = '\0';
1001 INSIST(p == args + argslen);
1002
1003 if (nserveraddrs == 0 && servername != NULL) {
1004 get_addresses(servername, (in_port_t)remoteport);
1005 }
1006
1007 isc_loopmgr_run(loopmgr);
1008
1009 isccc_ccmsg_invalidate(&rndc_ccmsg);
1010
1011 isc_log_destroy(&log);
1012 isc_log_setcontext(NULL);
1013
1014 cfg_obj_destroy(pctx, &config);
1015 cfg_parser_destroy(&pctx);
1016
1017 isc_mem_put(rndc_mctx, args, argslen);
1018
1019 isc_buffer_free(&databuf);
1020
1021 if (show_final_mem) {
1022 isc_mem_stats(rndc_mctx, stderr);
1023 }
1024
1025 isc_managers_destroy(&rndc_mctx, &loopmgr, &netmgr);
1026
1027 if (failed) {
1028 return 1;
1029 }
1030
1031 return 0;
1032 }
1033