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