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