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