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