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