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