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