rndc.c 28.6 KB
Newer Older
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 8 9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
10 11
 */

12
/*! \file */
13

14
#include <inttypes.h>
15
#include <stdbool.h>
16 17
#include <stdlib.h>

18
#include <isc/app.h>
19
#include <isc/atomic.h>
20
#include <isc/buffer.h>
21
#include <isc/commandline.h>
22
#include <isc/file.h>
23
#include <isc/log.h>
24
#include <isc/mem.h>
25
#include <isc/net.h>
26
#include <isc/print.h>
27
#include <isc/random.h>
28
#include <isc/refcount.h>
29
#include <isc/socket.h>
30
#include <isc/stdtime.h>
31
#include <isc/string.h>
32
#include <isc/task.h>
33
#include <isc/thread.h>
34 35
#include <isc/util.h>

36
#include <pk11/site.h>
37

38
#include <dns/name.h>
39

40 41 42 43 44 45 46 47
#include <isccc/alist.h>
#include <isccc/base64.h>
#include <isccc/cc.h>
#include <isccc/ccmsg.h>
#include <isccc/result.h>
#include <isccc/sexpr.h>
#include <isccc/types.h>
#include <isccc/util.h>
48 49 50 51 52 53

#include <isccfg/namedconf.h>

#include <bind9/getaddresses.h>

#include "util.h"
54

55
#define SERVERADDRS 10
56

57
const char *progname;
Evan Hunt's avatar
Evan Hunt committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
bool verbose;

static const char *admin_conffile;
static const char *admin_keyfile;
static const char *version = VERSION;
static const char *servername = NULL;
static isc_sockaddr_t serveraddrs[SERVERADDRS];
static isc_sockaddr_t local4, local6;
static bool local4set = false, local6set = false;
static int nserveraddrs;
static int currentaddr = 0;
static unsigned int remoteport = 0;
static isc_socketmgr_t *socketmgr = NULL;
static isc_buffer_t *databuf;
static isccc_ccmsg_t ccmsg;
static uint32_t algorithm;
static isccc_region_t secret;
static bool failed = false;
static bool c_flag = false;
static isc_mem_t *rndc_mctx;
78 79 80
static atomic_uint_fast32_t sends = ATOMIC_VAR_INIT(0);
static atomic_uint_fast32_t recvs = ATOMIC_VAR_INIT(0);
static atomic_uint_fast32_t connects = ATOMIC_VAR_INIT(0);
Evan Hunt's avatar
Evan Hunt committed
81 82 83 84 85 86 87
static char *command;
static char *args;
static char program[256];
static isc_socket_t *sock = NULL;
static uint32_t serial;
static bool quiet = false;
static bool showresult = false;
88

Ondřej Surý's avatar
Ondřej Surý committed
89 90
static void
rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);
91

Francis Dupont's avatar
Francis Dupont committed
92 93 94
ISC_PLATFORM_NORETURN_PRE static void
usage(int status) ISC_PLATFORM_NORETURN_POST;

95
static void
Evan Hunt's avatar
Evan Hunt committed
96
usage(int status) {
97
	fprintf(stderr, "\
98
Usage: %s [-b address] [-c config] [-s server] [-p port]\n\
99
	[-k key-file ] [-y key] [-r] [-V] [-4 | -6] command\n\
100
\n\
101
command is one of the following:\n\
102
\n\
103
  addzone zone [class [view]] { zone-options }\n\
104
		Add zone to given view. Requires allow-new-zones option.\n\
105
  delzone [-clean] zone [class [view]]\n\
106
		Removes zone from given view.\n\
Mark Andrews's avatar
Mark Andrews committed
107
  dnstap -reopen\n\
108
		Close, truncate and re-open the DNSTAP output file.\n\
Mark Andrews's avatar
Mark Andrews committed
109 110
  dnstap -roll count\n\
		Close, rename and re-open the DNSTAP output file(s).\n\
Evan Hunt's avatar
Evan Hunt committed
111
  dumpdb [-all|-cache|-zones|-adb|-bad|-fail] [view ...]\n\
112
		Dump cache(s) to the dump file (named_dump.db).\n\
113 114
  flush 	Flushes all of the server's caches.\n\
  flush [view]	Flushes the server's cache for a view.\n\
115
  flushname name [view]\n\
Mark Andrews's avatar
Mark Andrews committed
116
		Flush the given name from the server's cache(s)\n\
117 118
  flushtree name [view]\n\
		Flush all names under the given name from the server's cache(s)\n\
119 120 121 122 123 124 125 126
  freeze	Suspend updates to all dynamic zones.\n\
  freeze zone [class [view]]\n\
		Suspend updates to a dynamic zone.\n\
  halt		Stop the server without saving pending updates.\n\
  halt -p	Stop the server without saving pending updates reporting\n\
		process id.\n\
  loadkeys zone [class [view]]\n\
		Update keys without signing immediately.\n\
Mark Andrews's avatar
Mark Andrews committed
127 128 129 130 131 132
  managed-keys refresh [class [view]]\n\
		Check trust anchor for RFC 5011 key changes\n\
  managed-keys status [class [view]]\n\
		Display RFC 5011 managed keys information\n\
  managed-keys sync [class [view]]\n\
		Write RFC 5011 managed keys to disk\n\
133 134 135
  modzone zone [class [view]] { zone-options }\n\
		Modify a zone's configuration.\n\
		Requires allow-new-zones option.\n\
136 137 138
  notify zone [class [view]]\n\
		Resend NOTIFY messages for the zone.\n\
  notrace	Set debugging level to 0.\n\
Mark Andrews's avatar
Mark Andrews committed
139 140
  nta -dump\n\
		List all negative trust anchors.\n\
141 142 143 144
  nta [-lifetime duration] [-force] domain [view]\n\
		Set a negative trust anchor, disabling DNSSEC validation\n\
		for the given domain.\n\
		Using -lifetime specifies the duration of the NTA, up\n\
145
		to one week.\n\
146 147 148 149 150
		Using -force prevents the NTA from expiring before its\n\
		full lifetime, even if the domain can validate sooner.\n\
  nta -remove domain [view]\n\
		Remove a negative trust anchor, re-enabling validation\n\
		for the given domain.\n\
151
  querylog [ on | off ]\n\
152 153 154 155 156 157 158 159 160 161
		Enable / disable query logging.\n\
  reconfig	Reload configuration file and new zones only.\n\
  recursing	Dump the queries that are currently recursing (named.recursing)\n\
  refresh zone [class [view]]\n\
		Schedule immediate maintenance for a zone.\n\
  reload	Reload configuration file and zones.\n\
  reload zone [class [view]]\n\
		Reload a single zone.\n\
  retransfer zone [class [view]]\n\
		Retransfer a single zone without checking serial number.\n\
162
  scan		Scan available network interfaces for changes.\n\
163 164
  secroots [view ...]\n\
		Write security roots to the secroots file.\n\
165
  serve-stale [ on | off | reset | status ] [class [view]]\n\
166
		Control whether stale answers are returned\n\
167 168
  showzone zone [class [view]]\n\
		Print a zone's configuration.\n\
169 170
  sign zone [class [view]]\n\
		Update zone keys, and sign as needed.\n\
171 172 173
  signing -clear all zone [class [view]]\n\
		Remove the private records for all keys that have\n\
		finished signing the given zone.\n\
174 175 176 177 178 179
  signing -clear <keyid>/<algorithm> zone [class [view]]\n\
		Remove the private record that indicating the given key\n\
		has finished signing the given zone.\n\
  signing -list zone [class [view]]\n\
		List the private records showing the state of DNSSEC\n\
		signing in the given zone.\n\
180 181 182
  signing -nsec3param hash flags iterations salt zone [class [view]]\n\
		Add NSEC3 chain to zone if already signed.\n\
		Prime zone with NSEC3 chain if not yet signed.\n\
183 184
  signing -nsec3param none zone [class [view]]\n\
		Remove NSEC3 chains from zone.\n\
185 186
  signing -serial <value> zone [class [view]]\n\
		Set the zones's serial to <value>.\n\
187 188 189 190 191 192 193 194 195 196
  stats		Write server statistics to the statistics file.\n\
  status	Display status of the server.\n\
  stop		Save pending updates to master files and stop the server.\n\
  stop -p	Save pending updates to master files and stop the server\n\
		reporting process id.\n\
  sync [-clean]	Dump changes to all dynamic zones to disk, and optionally\n\
		remove their journal files.\n\
  sync [-clean] zone [class [view]]\n\
		Dump a single zone's changes to disk, and optionally\n\
		remove its journal file.\n\
197 198 199
  tcp-timeouts	Display the tcp-*-timeout option values\n\
  tcp-timeouts initial idle keepalive advertised\n\
		Update the tcp-*-timeout option values\n\
200 201 202 203 204 205 206 207 208
  thaw		Enable updates to all dynamic zones and reload them.\n\
  thaw zone [class [view]]\n\
		Enable updates to a frozen dynamic zone and reload it.\n\
  trace		Increment debugging level by one.\n\
  trace level	Change the debugging level.\n\
  tsig-delete keyname [view]\n\
		Delete a TKEY-negotiated TSIG key.\n\
  tsig-list	List all currently active TSIG keys, including both statically\n\
		configured and TKEY-negotiated keys.\n\
209
  validation [ on | off | status ] [view]\n\
210 211 212
		Enable / disable DNSSEC validation.\n\
  zonestatus zone [class [view]]\n\
		Display the current status of a zone.\n\
213
\n\
214 215
Version: %s\n",
		progname, version);
216 217

	exit(status);
218 219
}

220 221 222
#define CMDLINE_FLAGS "46b:c:hk:Mmp:qrs:Vy:"

static void
Evan Hunt's avatar
Evan Hunt committed
223
preparse_args(int argc, char **argv) {
224
	bool ipv4only = false, ipv6only = false;
Evan Hunt's avatar
Evan Hunt committed
225
	int ch;
226 227 228 229 230 231 232

	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
		switch (ch) {
		case '4':
			if (ipv6only) {
				fatal("only one of -4 and -6 allowed");
			}
233
			ipv4only = true;
234 235 236 237 238
			break;
		case '6':
			if (ipv4only) {
				fatal("only one of -4 and -6 allowed");
			}
239
			ipv6only = true;
240 241 242 243 244 245
			break;
		default:
			break;
		}
	}

246
	isc_commandline_reset = true;
247 248 249
	isc_commandline_index = 1;
}

250
static void
Evan Hunt's avatar
Evan Hunt committed
251
get_addresses(const char *host, in_port_t port) {
252
	isc_result_t result;
Evan Hunt's avatar
Evan Hunt committed
253
	int found = 0, count;
254

255 256
	REQUIRE(host != NULL);

257
	if (*host == '/') {
Evan Hunt's avatar
Evan Hunt committed
258 259
		result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
					       host);
260
		if (result == ISC_R_SUCCESS) {
Automatic Updater's avatar
Automatic Updater committed
261
			nserveraddrs++;
262
		}
263 264
	} else {
		count = SERVERADDRS - nserveraddrs;
265 266
		result = bind9_getaddresses(
			host, port, &serveraddrs[nserveraddrs], count, &found);
267 268
		nserveraddrs += found;
	}
269
	if (result != ISC_R_SUCCESS) {
270 271
		fatal("couldn't get address for '%s': %s", host,
		      isc_result_totext(result));
272
	}
273
	INSIST(nserveraddrs > 0);
274 275 276
}

static void
Evan Hunt's avatar
Evan Hunt committed
277
rndc_senddone(isc_task_t *task, isc_event_t *event) {
278 279 280 281
	isc_socketevent_t *sevent = (isc_socketevent_t *)event;

	UNUSED(task);

282
	if (sevent->result != ISC_R_SUCCESS) {
283
		fatal("send failed: %s", isc_result_totext(sevent->result));
284
	}
285
	isc_event_free(&event);
286
	if (atomic_fetch_sub_release(&sends, 1) == 1 &&
Evan Hunt's avatar
Evan Hunt committed
287 288
	    atomic_load_acquire(&recvs) == 0)
	{
289 290
		isc_socket_detach(&sock);
		isc_task_shutdown(task);
291
		isc_app_shutdown();
292
	}
293 294 295
}

static void
Evan Hunt's avatar
Evan Hunt committed
296
rndc_recvdone(isc_task_t *task, isc_event_t *event) {
297 298 299
	isccc_sexpr_t *response = NULL;
	isccc_sexpr_t *data;
	isccc_region_t source;
Evan Hunt's avatar
Evan Hunt committed
300 301 302
	char *errormsg = NULL;
	char *textmsg = NULL;
	isc_result_t result;
303

304
	atomic_fetch_sub_release(&recvs, 1);
305

306
	if (ccmsg.result == ISC_R_EOF) {
307
		fatal("connection to remote host closed\n"
308 309 310 311
		      "This may indicate that\n"
		      "* the remote server is using an older version of"
		      " the command protocol,\n"
		      "* this host is not authorized to connect,\n"
Francis Dupont's avatar
Francis Dupont committed
312
		      "* the clocks are not synchronized, or\n"
313
		      "* the key is invalid.");
314
	}
315

316
	if (ccmsg.result != ISC_R_SUCCESS) {
317
		fatal("recv failed: %s", isc_result_totext(ccmsg.result));
318
	}
319 320 321

	source.rstart = isc_buffer_base(&ccmsg.buffer);
	source.rend = isc_buffer_used(&ccmsg.buffer);
322

323 324
	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
325

326
	data = isccc_alist_lookup(response, "_data");
327
	if (!isccc_alist_alistp(data)) {
328
		fatal("bad or missing data section in response");
329
	}
330 331
	result = isccc_cc_lookupstring(data, "err", &errormsg);
	if (result == ISC_R_SUCCESS) {
332
		failed = true;
333 334
		fprintf(stderr, "%s: '%s' failed: %s\n", progname, command,
			errormsg);
335
	} else if (result != ISC_R_NOTFOUND) {
336 337
		fprintf(stderr, "%s: parsing response failed: %s\n", progname,
			isc_result_totext(result));
338
	}
339

340
	result = isccc_cc_lookupstring(data, "text", &textmsg);
341
	if (result == ISC_R_SUCCESS) {
342
		if ((!quiet || failed) && strlen(textmsg) != 0U) {
Evan Hunt's avatar
Evan Hunt committed
343
			fprintf(failed ? stderr : stdout, "%s\n", textmsg);
344 345
		}
	} else if (result != ISC_R_NOTFOUND) {
346 347
		fprintf(stderr, "%s: parsing response failed: %s\n", progname,
			isc_result_totext(result));
348
	}
349

350 351 352 353
	if (showresult) {
		isc_result_t eresult;

		result = isccc_cc_lookupuint32(data, "result", &eresult);
354
		if (result == ISC_R_SUCCESS) {
355
			printf("%s %u\n", isc_result_toid(eresult), eresult);
356
		} else {
357
			printf("NONE -1\n");
358
		}
359 360
	}

361 362
	isc_event_free(&event);
	isccc_sexpr_free(&response);
363 364
	if (atomic_load_acquire(&sends) == 0 &&
	    atomic_load_acquire(&recvs) == 0) {
365 366
		isc_socket_detach(&sock);
		isc_task_shutdown(task);
367
		isc_app_shutdown();
368
	}
369 370
}

371
static void
Evan Hunt's avatar
Evan Hunt committed
372
rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
373 374 375
	isccc_sexpr_t *response = NULL;
	isccc_sexpr_t *_ctrl;
	isccc_region_t source;
Evan Hunt's avatar
Evan Hunt committed
376 377
	isc_result_t result;
	uint32_t nonce;
378
	isccc_sexpr_t *request = NULL;
Evan Hunt's avatar
Evan Hunt committed
379 380
	isccc_time_t now;
	isc_region_t r;
381
	isccc_sexpr_t *data;
Evan Hunt's avatar
Evan Hunt committed
382
	isc_buffer_t b;
383

384
	atomic_fetch_sub_release(&recvs, 1);
385

386
	if (ccmsg.result == ISC_R_EOF) {
387
		fatal("connection to remote host closed\n"
388 389 390 391
		      "This may indicate that\n"
		      "* the remote server is using an older version of"
		      " the command protocol,\n"
		      "* this host is not authorized to connect,\n"
392
		      "* the clocks are not synchronized,\n"
Evan Hunt's avatar
Evan Hunt committed
393
		      "* the key signing algorithm is incorrect, or\n"
394
		      "* the key is invalid.");
395
	}
396

397
	if (ccmsg.result != ISC_R_SUCCESS) {
398
		fatal("recv failed: %s", isc_result_totext(ccmsg.result));
399
	}
400 401 402 403

	source.rstart = isc_buffer_base(&ccmsg.buffer);
	source.rend = isc_buffer_used(&ccmsg.buffer);

404 405
	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
406 407

	_ctrl = isccc_alist_lookup(response, "_ctrl");
408
	if (!isccc_alist_alistp(_ctrl)) {
409
		fatal("bad or missing ctrl section in response");
410
	}
411
	nonce = 0;
412
	if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS) {
413
		nonce = 0;
414
	}
415 416 417 418 419 420

	isc_stdtime_get(&now);

	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
						    now, now + 60, &request));
	data = isccc_alist_lookup(request, "_data");
421
	if (data == NULL) {
422
		fatal("_data section missing");
423 424
	}
	if (isccc_cc_definestring(data, "type", args) == NULL) {
425
		fatal("out of memory");
426
	}
427 428
	if (nonce != 0) {
		_ctrl = isccc_alist_lookup(request, "_ctrl");
429
		if (_ctrl == NULL) {
430
			fatal("_ctrl section missing");
431 432
		}
		if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL) {
433
			fatal("out of memory");
434
		}
435
	}
436 437 438 439 440

	isc_buffer_clear(databuf);
	/* Skip the length field (4 bytes) */
	isc_buffer_add(databuf, 4);

441
	DO("render message",
442 443 444 445 446 447 448
	   isccc_cc_towire(request, &databuf, algorithm, &secret));

	isc_buffer_init(&b, databuf->base, 4);
	isc_buffer_putuint32(&b, databuf->used - 4);

	r.base = databuf->base;
	r.length = databuf->used;
449 450

	isccc_ccmsg_cancelread(&ccmsg);
451 452
	DO("schedule recv",
	   isccc_ccmsg_readmessage(&ccmsg, task, rndc_recvdone, NULL));
453
	atomic_fetch_add_relaxed(&recvs, 1);
454 455
	DO("send message",
	   isc_socket_send(sock, &r, task, rndc_senddone, NULL));
456
	atomic_fetch_add_relaxed(&sends, 1);
457 458 459

	isc_event_free(&event);
	isccc_sexpr_free(&response);
460
	isccc_sexpr_free(&request);
461 462 463
	return;
}

464
static void
Evan Hunt's avatar
Evan Hunt committed
465 466
rndc_connected(isc_task_t *task, isc_event_t *event) {
	char socktext[ISC_SOCKADDR_FORMATSIZE];
467
	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
Evan Hunt's avatar
Evan Hunt committed
468 469 470 471 472 473
	isccc_sexpr_t *request = NULL;
	isccc_sexpr_t *data;
	isccc_time_t now;
	isc_region_t r;
	isc_buffer_t b;
	isc_result_t result;
474

475
	atomic_fetch_sub_release(&connects, 1);
476

477
	if (sevent->result != ISC_R_SUCCESS) {
478 479
		isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
				    sizeof(socktext));
480
		if (sevent->result != ISC_R_CANCELED &&
481
		    ++currentaddr < nserveraddrs) {
482
			notify("connection failed: %s: %s", socktext,
483
			       isc_result_totext(sevent->result));
484
			isc_socket_detach(&sock);
485
			isc_event_free(&event);
486
			rndc_startconnect(&serveraddrs[currentaddr], task);
487
			return;
488
		} else {
489
			fatal("connect failed: %s: %s", socktext,
490
			      isc_result_totext(sevent->result));
491
		}
492
	}
493 494

	isc_stdtime_get(&now);
495
	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
496 497
						    now, now + 60, &request));
	data = isccc_alist_lookup(request, "_data");
498
	if (data == NULL) {
499
		fatal("_data section missing");
500 501
	}
	if (isccc_cc_definestring(data, "type", "null") == NULL) {
502
		fatal("out of memory");
503
	}
504 505 506 507 508

	isc_buffer_clear(databuf);
	/* Skip the length field (4 bytes) */
	isc_buffer_add(databuf, 4);

509
	DO("render message",
510 511 512 513 514 515 516
	   isccc_cc_towire(request, &databuf, algorithm, &secret));

	isc_buffer_init(&b, databuf->base, 4);
	isc_buffer_putuint32(&b, databuf->used - 4);

	r.base = databuf->base;
	r.length = databuf->used;
517

518
	isccc_ccmsg_init(rndc_mctx, sock, &ccmsg);
519
	isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024);
520

521 522
	DO("schedule recv",
	   isccc_ccmsg_readmessage(&ccmsg, task, rndc_recvnonce, NULL));
523
	atomic_fetch_add_relaxed(&recvs, 1);
524 525
	DO("send message",
	   isc_socket_send(sock, &r, task, rndc_senddone, NULL));
526
	atomic_fetch_add_relaxed(&sends, 1);
527
	isc_event_free(&event);
528
	isccc_sexpr_free(&request);
529 530 531
}

static void
Evan Hunt's avatar
Evan Hunt committed
532 533 534
rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
	isc_result_t result;
	int pf;
535
	isc_sockettype_t type;
536

537
	char socktext[ISC_SOCKADDR_FORMATSIZE];
538

539
	isc_sockaddr_format(addr, socktext, sizeof(socktext));
540 541 542

	notify("using server %s (%s)", servername, socktext);

543
	pf = isc_sockaddr_pf(addr);
544
	if (pf == AF_INET || pf == AF_INET6) {
545
		type = isc_sockettype_tcp;
546
	} else {
547
		type = isc_sockettype_unix;
548
	}
549
	DO("create socket", isc_socket_create(socketmgr, pf, type, &sock));
550 551
	switch (isc_sockaddr_pf(addr)) {
	case AF_INET:
552
		DO("bind socket", isc_socket_bind(sock, &local4, 0));
553 554
		break;
	case AF_INET6:
555
		DO("bind socket", isc_socket_bind(sock, &local6, 0));
556 557 558 559
		break;
	default:
		break;
	}
560 561
	DO("connect",
	   isc_socket_connect(sock, addr, task, rndc_connected, NULL));
562
	atomic_fetch_add_relaxed(&connects, 1);
563 564
}

565
static void
Evan Hunt's avatar
Evan Hunt committed
566
rndc_start(isc_task_t *task, isc_event_t *event) {
567 568 569
	isc_event_free(&event);

	currentaddr = 0;
570
	rndc_startconnect(&serveraddrs[currentaddr], task);
571 572
}

573
static void
Mark Andrews's avatar
Mark Andrews committed
574
parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
Evan Hunt's avatar
Evan Hunt committed
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
	     cfg_parser_t **pctxp, cfg_obj_t **configp) {
	isc_result_t result;
	const char *conffile = admin_conffile;
	const cfg_obj_t *addresses = NULL;
	const cfg_obj_t *defkey = NULL;
	const cfg_obj_t *options = NULL;
	const cfg_obj_t *servers = NULL;
	const cfg_obj_t *server = NULL;
	const cfg_obj_t *keys = NULL;
	const cfg_obj_t *key = NULL;
	const cfg_obj_t *defport = NULL;
	const cfg_obj_t *secretobj = NULL;
	const cfg_obj_t *algorithmobj = NULL;
	cfg_obj_t *config = NULL;
	const cfg_obj_t *address = NULL;
590
	const cfg_listelt_t *elt;
Evan Hunt's avatar
Evan Hunt committed
591 592 593 594 595
	const char *secretstr;
	const char *algorithmstr;
	static char secretarray[1024];
	const cfg_type_t *conftype = &cfg_type_rndcconf;
	bool key_only = false;
596
	const cfg_listelt_t *element;
597

598
	if (!isc_file_exists(conffile)) {
Mark Andrews's avatar
Mark Andrews committed
599 600
		conffile = admin_keyfile;
		conftype = &cfg_type_rndckey;
601

602
		if (c_flag) {
603
			fatal("%s does not exist", admin_conffile);
604
		}
605

606
		if (!isc_file_exists(conffile)) {
607 608
			fatal("neither %s nor %s was found", admin_conffile,
			      admin_keyfile);
609
		}
610
		key_only = true;
611 612 613
	} else if (!c_flag && isc_file_exists(admin_keyfile)) {
		fprintf(stderr,
			"WARNING: key file (%s) exists, but using "
614 615
			"default configuration file (%s)\n",
			admin_keyfile, admin_conffile);
616 617 618 619 620 621 622
	}

	DO("create parser", cfg_parser_create(mctx, log, pctxp));

	/*
	 * The parser will output its own errors, so DO() is not used.
	 */
Mark Andrews's avatar
Mark Andrews committed
623
	result = cfg_parse_file(*pctxp, conffile, conftype, &config);
624
	if (result != ISC_R_SUCCESS) {
625
		fatal("could not load rndc configuration");
626
	}
627

628
	if (!key_only) {
Mark Andrews's avatar
Mark Andrews committed
629
		(void)cfg_map_get(config, "options", &options);
630
	}
Mark Andrews's avatar
Mark Andrews committed
631

632
	if (key_only && servername == NULL) {
633
		servername = "127.0.0.1";
634
	} else if (servername == NULL && options != NULL) {
635
		const cfg_obj_t *defserverobj = NULL;
Mark Andrews's avatar
Mark Andrews committed
636
		(void)cfg_map_get(options, "default-server", &defserverobj);
637
		if (defserverobj != NULL) {
Mark Andrews's avatar
Mark Andrews committed
638
			servername = cfg_obj_asstring(defserverobj);
639
		}
Mark Andrews's avatar
Mark Andrews committed
640 641
	}

642
	if (servername == NULL) {
Mark Andrews's avatar
Mark Andrews committed
643
		fatal("no server specified and no default");
644
	}
Mark Andrews's avatar
Mark Andrews committed
645 646

	if (!key_only) {
647
		(void)cfg_map_get(config, "server", &servers);
Mark Andrews's avatar
Mark Andrews committed
648
		if (servers != NULL) {
649 650
			for (elt = cfg_list_first(servers); elt != NULL;
			     elt = cfg_list_next(elt)) {
Mark Andrews's avatar
Mark Andrews committed
651 652
				const char *name;
				server = cfg_listelt_value(elt);
653 654
				name = cfg_obj_asstring(
					cfg_map_getname(server));
655
				if (strcasecmp(name, servername) == 0) {
Mark Andrews's avatar
Mark Andrews committed
656
					break;
657
				}
Mark Andrews's avatar
Mark Andrews committed
658 659 660 661 662 663 664 665
				server = NULL;
			}
		}
	}

	/*
	 * Look for the name of the key to use.
	 */
666 667 668
	if (keyname != NULL) {
		/* Was set on command line, do nothing. */
	} else if (server != NULL) {
Mark Andrews's avatar
Mark Andrews committed
669 670 671
		DO("get key for server", cfg_map_get(server, "key", &defkey));
		keyname = cfg_obj_asstring(defkey);
	} else if (options != NULL) {
672 673
		DO("get default key",
		   cfg_map_get(options, "default-key", &defkey));
Mark Andrews's avatar
Mark Andrews committed
674
		keyname = cfg_obj_asstring(defkey);
675
	} else if (!key_only) {
Mark Andrews's avatar
Mark Andrews committed
676
		fatal("no key for server and no default");
677
	}
Mark Andrews's avatar
Mark Andrews committed
678 679 680 681

	/*
	 * Get the key's definition.
	 */
682
	if (key_only) {
Mark Andrews's avatar
Mark Andrews committed
683
		DO("get key", cfg_map_get(config, "key", &key));
684
	} else {
Mark Andrews's avatar
Mark Andrews committed
685
		DO("get config key list", cfg_map_get(config, "key", &keys));
686 687
		for (elt = cfg_list_first(keys); elt != NULL;
		     elt = cfg_list_next(elt)) {
Mark Andrews's avatar
Mark Andrews committed
688 689
			key = cfg_listelt_value(elt);
			if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
690
				       keyname) == 0) {
Mark Andrews's avatar
Mark Andrews committed
691
				break;
692
			}
Mark Andrews's avatar
Mark Andrews committed
693
		}
694
		if (elt == NULL) {
Mark Andrews's avatar
Mark Andrews committed
695
			fatal("no key definition for name %s", keyname);
696
		}
Mark Andrews's avatar
Mark Andrews committed
697 698 699
	}
	(void)cfg_map_get(key, "secret", &secretobj);
	(void)cfg_map_get(key, "algorithm", &algorithmobj);
700
	if (secretobj == NULL || algorithmobj == NULL) {
Mark Andrews's avatar
Mark Andrews committed
701
		fatal("key must have algorithm and secret");
702
	}
Mark Andrews's avatar
Mark Andrews committed
703 704

	secretstr = cfg_obj_asstring(secretobj);
705 706
	algorithmstr = cfg_obj_asstring(algorithmobj);

Ondřej Surý's avatar
Ondřej Surý committed
707
	if (strcasecmp(algorithmstr, "hmac-md5") == 0) {
708
		algorithm = ISCCC_ALG_HMACMD5;
Ondřej Surý's avatar
Ondřej Surý committed
709
	} else if (strcasecmp(algorithmstr, "hmac-sha1") == 0) {
710
		algorithm = ISCCC_ALG_HMACSHA1;
Ondřej Surý's avatar
Ondřej Surý committed
711
	} else if (strcasecmp(algorithmstr, "hmac-sha224") == 0) {
712
		algorithm = ISCCC_ALG_HMACSHA224;
Ondřej Surý's avatar
Ondřej Surý committed
713
	} else if (strcasecmp(algorithmstr, "hmac-sha256") == 0) {
714
		algorithm = ISCCC_ALG_HMACSHA256;
Ondřej Surý's avatar
Ondřej Surý committed
715
	} else if (strcasecmp(algorithmstr, "hmac-sha384") == 0) {
716
		algorithm = ISCCC_ALG_HMACSHA384;
Ondřej Surý's avatar
Ondřej Surý committed
717
	} else if (strcasecmp(algorithmstr, "hmac-sha512") == 0) {
718
		algorithm = ISCCC_ALG_HMACSHA512;
Ondřej Surý's avatar
Ondřej Surý committed
719
	} else {
720
		fatal("unsupported algorithm: %s", algorithmstr);
Ondřej Surý's avatar
Ondřej Surý committed
721
	}
Mark Andrews's avatar
Mark Andrews committed
722 723 724 725 726 727 728 729 730 731

	secret.rstart = (unsigned char *)secretarray;
	secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
	DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
	secret.rend = secret.rstart;
	secret.rstart = (unsigned char *)secretarray;

	/*
	 * Find the port to connect to.
	 */
732 733 734 735
	if (remoteport != 0) {
		/* Was set on command line, do nothing. */
	} else {
		if (server != NULL) {
Mark Andrews's avatar
Mark Andrews committed
736
			(void)cfg_map_get(server, "port", &defport);
737 738
		}
		if (defport == NULL && options != NULL) {
739
			(void)cfg_map_get(options, "default-port", &defport);
740
		}
Mark Andrews's avatar
Mark Andrews committed
741 742 743
	}
	if (defport != NULL) {
		remoteport = cfg_obj_asuint32(defport);
744
		if (remoteport > 65535 || remoteport == 0) {
745
			fatal("port %u out of range", remoteport);
746 747
		}
	} else if (remoteport == 0) {
Mark Andrews's avatar
Mark Andrews committed
748
		remoteport = NS_CONTROL_PORT;
749
	}
Mark Andrews's avatar
Mark Andrews committed
750

751
	if (server != NULL) {
752
		result = cfg_map_get(server, "addresses", &addresses);
753
	} else {
754
		result = ISC_R_NOTFOUND;
755
	}
756
	if (result == ISC_R_SUCCESS) {
757
		for (element = cfg_list_first(addresses); element != NULL;
Evan Hunt's avatar
Evan Hunt committed
758 759
		     element = cfg_list_next(element))
		{
760 761
			isc_sockaddr_t sa;

762
			address = cfg_listelt_value(element);
763
			if (!cfg_obj_issockaddr(address)) {
Evan Hunt's avatar
Evan Hunt committed
764 765
				unsigned int myport;
				const char *name;
766
				const cfg_obj_t *obj;
767 768 769 770 771 772

				obj = cfg_tuple_get(address, "name");
				name = cfg_obj_asstring(obj);
				obj = cfg_tuple_get(address, "port");
				if (cfg_obj_isuint32(obj)) {
					myport = cfg_obj_asuint32(obj);
Evan Hunt's avatar
Evan Hunt committed
773 774
					if (myport > UINT16_MAX || myport == 0)
					{
775 776
						fatal("port %u out of range",
						      myport);
777 778
					}
				} else {
779
					myport = remoteport;
780 781
				}
				if (nserveraddrs < SERVERADDRS) {
782
					get_addresses(name, (in_port_t)myport);
783
				} else {
784 785 786 787
					fprintf(stderr,
						"too many address: "
						"%s: dropped\n",
						name);
788
				}
789 790 791
				continue;
			}
			sa = *cfg_obj_assockaddr(address);
792
			if (isc_sockaddr_getport(&sa) == 0) {
793
				isc_sockaddr_setport(&sa, remoteport);
794 795
			}
			if (nserveraddrs < SERVERADDRS) {
796
				serveraddrs[nserveraddrs++] = sa;
797
			} else {
798 799 800 801 802 803 804 805 806 807
				char socktext[ISC_SOCKADDR_FORMATSIZE];

				isc_sockaddr_format(&sa, socktext,
						    sizeof(socktext));
				fprintf(stderr,
					"too many address: %s: drop