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