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