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