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