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