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