rndc.c 27 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

David Lawrence's avatar
David Lawrence committed
14 15
#include <config.h>

16
#include <inttypes.h>
17
#include <stdbool.h>
18 19
#include <stdlib.h>

20
#include <isc/app.h>
21
#include <isc/buffer.h>
22
#include <isc/commandline.h>
23
#include <isc/file.h>
24
#include <isc/log.h>
25
#include <isc/net.h>
26
#include <isc/mem.h>
27
#include <isc/print.h>
28
#include <isc/random.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 37
#include <pk11/site.h>

38
#include <isccfg/namedconf.h>
39

40 41 42 43 44 45 46 47 48
#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>

Mark Andrews's avatar
Mark Andrews committed
49 50
#include <dns/name.h>

51
#include <bind9/getaddresses.h>
52

53
#include "util.h"
54

55
#define SERVERADDRS 10
56

57
const char *progname;
58
bool verbose;
59

60
static const char *admin_conffile;
Mark Andrews's avatar
Mark Andrews committed
61
static const char *admin_keyfile;
62
static const char *version = VERSION;
63
static const char *servername = NULL;
64
static isc_sockaddr_t serveraddrs[SERVERADDRS];
65
static isc_sockaddr_t local4, local6;
66
static bool local4set = false, local6set = false;
67 68
static int nserveraddrs;
static int currentaddr = 0;
Mark Andrews's avatar
Mark Andrews committed
69
static unsigned int remoteport = 0;
70
static isc_socketmgr_t *socketmgr = NULL;
71
static isc_buffer_t *databuf;
72
static isccc_ccmsg_t ccmsg;
73
static uint32_t algorithm;
74
static isccc_region_t secret;
75 76
static bool failed = false;
static bool c_flag = false;
77
static isc_mem_t *rndc_mctx;
78
static int sends, recvs, connects;
79 80
static char *command;
static char *args;
81
static char program[256];
82
static isc_socket_t *sock = NULL;
83
static uint32_t serial;
84 85
static bool quiet = false;
static bool showresult = false;
86

87 88
static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);

Francis Dupont's avatar
Francis Dupont committed
89 90 91
ISC_PLATFORM_NORETURN_PRE static void
usage(int status) ISC_PLATFORM_NORETURN_POST;

92
static void
93
usage(int status) {
94
	fprintf(stderr, "\
95
Usage: %s [-b address] [-c config] [-s server] [-p port]\n\
96
	[-k key-file ] [-y key] [-r] [-V] command\n\
97
\n\
98
command is one of the following:\n\
99
\n\
100
  addzone zone [class [view]] { zone-options }\n\
101
		Add zone to given view. Requires allow-new-zones option.\n\
102
  delzone [-clean] zone [class [view]]\n\
103
		Removes zone from given view.\n\
Mark Andrews's avatar
Mark Andrews committed
104
  dnstap -reopen\n\
105
		Close, truncate and re-open the DNSTAP output file.\n\
Mark Andrews's avatar
Mark Andrews committed
106 107
  dnstap -roll count\n\
		Close, rename and re-open the DNSTAP output file(s).\n\
Evan Hunt's avatar
Evan Hunt committed
108
  dumpdb [-all|-cache|-zones|-adb|-bad|-fail] [view ...]\n\
109
		Dump cache(s) to the dump file (named_dump.db).\n\
110 111
  flush 	Flushes all of the server's caches.\n\
  flush [view]	Flushes the server's cache for a view.\n\
112
  flushname name [view]\n\
Mark Andrews's avatar
Mark Andrews committed
113
		Flush the given name from the server's cache(s)\n\
114 115
  flushtree name [view]\n\
		Flush all names under the given name from the server's cache(s)\n\
116 117 118 119 120 121 122 123
  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
124 125 126 127 128 129
  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\
130 131 132
  modzone zone [class [view]] { zone-options }\n\
		Modify a zone's configuration.\n\
		Requires allow-new-zones option.\n\
133 134 135
  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
136 137
  nta -dump\n\
		List all negative trust anchors.\n\
138 139 140 141
  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\
142
		to one week.\n\
143 144 145 146 147
		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\
148
  querylog [ on | off ]\n\
149 150 151 152 153 154 155 156 157 158
		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\
159
  scan		Scan available network interfaces for changes.\n\
160 161
  secroots [view ...]\n\
		Write security roots to the secroots file.\n\
162 163
  showzone zone [class [view]]\n\
		Print a zone's configuration.\n\
164 165
  sign zone [class [view]]\n\
		Update zone keys, and sign as needed.\n\
166 167 168
  signing -clear all zone [class [view]]\n\
		Remove the private records for all keys that have\n\
		finished signing the given zone.\n\
169 170 171 172 173 174
  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\
175 176 177
  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\
178 179
  signing -nsec3param none zone [class [view]]\n\
		Remove NSEC3 chains from zone.\n\
180 181
  signing -serial <value> zone [class [view]]\n\
		Set the zones's serial to <value>.\n\
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
  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\
  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\
201
  validation [ yes | no | status ] [view]\n\
202 203 204
		Enable / disable DNSSEC validation.\n\
  zonestatus zone [class [view]]\n\
		Display the current status of a zone.\n\
205
\n\
206 207
Version: %s\n",
		progname, version);
208 209

	exit(status);
210 211
}

212
static void
213
get_addresses(const char *host, in_port_t port) {
214
	isc_result_t result;
215
	int found = 0, count;
216

217 218
	REQUIRE(host != NULL);

219 220 221 222
	if (*host == '/') {
		result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
					       host);
		if (result == ISC_R_SUCCESS)
Automatic Updater's avatar
Automatic Updater committed
223
			nserveraddrs++;
224 225 226 227 228 229 230
	} else {
		count = SERVERADDRS - nserveraddrs;
		result = bind9_getaddresses(host, port,
					    &serveraddrs[nserveraddrs],
					    count, &found);
		nserveraddrs += found;
	}
231 232 233
	if (result != ISC_R_SUCCESS)
		fatal("couldn't get address for '%s': %s",
		      host, isc_result_totext(result));
234
	INSIST(nserveraddrs > 0);
235 236 237 238 239 240 241 242
}

static void
rndc_senddone(isc_task_t *task, isc_event_t *event) {
	isc_socketevent_t *sevent = (isc_socketevent_t *)event;

	UNUSED(task);

243
	sends--;
244 245 246
	if (sevent->result != ISC_R_SUCCESS)
		fatal("send failed: %s", isc_result_totext(sevent->result));
	isc_event_free(&event);
247 248 249 250 251
	if (sends == 0 && recvs == 0) {
		isc_socket_detach(&sock);
		isc_task_shutdown(task);
		RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
	}
252 253 254 255 256 257 258 259
}

static void
rndc_recvdone(isc_task_t *task, isc_event_t *event) {
	isccc_sexpr_t *response = NULL;
	isccc_sexpr_t *data;
	isccc_region_t source;
	char *errormsg = NULL;
260
	char *textmsg = NULL;
261 262
	isc_result_t result;

263 264
	recvs--;

265
	if (ccmsg.result == ISC_R_EOF)
266
		fatal("connection to remote host closed\n"
267 268 269 270
		      "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
271
		      "* the clocks are not synchronized, or\n"
272
		      "* the key is invalid.");
273 274 275 276 277 278

	if (ccmsg.result != ISC_R_SUCCESS)
		fatal("recv failed: %s", isc_result_totext(ccmsg.result));

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

280 281
	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
282

283
	data = isccc_alist_lookup(response, "_data");
284 285
	if (!isccc_alist_alistp(data))
		fatal("bad or missing data section in response");
286 287
	result = isccc_cc_lookupstring(data, "err", &errormsg);
	if (result == ISC_R_SUCCESS) {
288
		failed = true;
289 290 291 292 293 294 295
		fprintf(stderr, "%s: '%s' failed: %s\n",
			progname, command, errormsg);
	}
	else if (result != ISC_R_NOTFOUND)
		fprintf(stderr, "%s: parsing response failed: %s\n",
			progname, isc_result_totext(result));

296
	result = isccc_cc_lookupstring(data, "text", &textmsg);
297
	if (result == ISC_R_SUCCESS) {
Evan Hunt's avatar
Evan Hunt committed
298 299
		if ((!quiet || failed) && strlen(textmsg) != 0U)
			fprintf(failed ? stderr : stdout, "%s\n", textmsg);
300
	} else if (result != ISC_R_NOTFOUND)
301 302 303
		fprintf(stderr, "%s: parsing response failed: %s\n",
			progname, isc_result_totext(result));

304 305 306 307 308 309 310 311 312 313
	if (showresult) {
		isc_result_t eresult;

		result = isccc_cc_lookupuint32(data, "result", &eresult);
		if (result == ISC_R_SUCCESS)
			printf("%s %u\n", isc_result_toid(eresult), eresult);
		else
			printf("NONE -1\n");
	}

314 315
	isc_event_free(&event);
	isccc_sexpr_free(&response);
316 317 318 319 320
	if (sends == 0 && recvs == 0) {
		isc_socket_detach(&sock);
		isc_task_shutdown(task);
		RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
	}
321 322
}

323 324 325 326 327 328
static void
rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
	isccc_sexpr_t *response = NULL;
	isccc_sexpr_t *_ctrl;
	isccc_region_t source;
	isc_result_t result;
329
	uint32_t nonce;
330 331 332 333 334 335 336 337 338 339
	isccc_sexpr_t *request = NULL;
	isccc_time_t now;
	isc_region_t r;
	isccc_sexpr_t *data;
	isc_buffer_t b;

	recvs--;

	if (ccmsg.result == ISC_R_EOF)
		fatal("connection to remote host closed\n"
340 341 342 343
		      "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"
344
		      "* the clocks are not synchronized,\n"
Evan Hunt's avatar
Evan Hunt committed
345
		      "* the key signing algorithm is incorrect, or\n"
346
		      "* the key is invalid.");
347 348 349 350 351 352 353

	if (ccmsg.result != ISC_R_SUCCESS)
		fatal("recv failed: %s", isc_result_totext(ccmsg.result));

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

354 355
	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
356 357

	_ctrl = isccc_alist_lookup(response, "_ctrl");
358 359
	if (!isccc_alist_alistp(_ctrl))
		fatal("bad or missing ctrl section in response");
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
	nonce = 0;
	if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
		nonce = 0;

	isc_stdtime_get(&now);

	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
						    now, now + 60, &request));
	data = isccc_alist_lookup(request, "_data");
	if (data == NULL)
		fatal("_data section missing");
	if (isccc_cc_definestring(data, "type", args) == NULL)
		fatal("out of memory");
	if (nonce != 0) {
		_ctrl = isccc_alist_lookup(request, "_ctrl");
		if (_ctrl == NULL)
			fatal("_ctrl section missing");
		if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
			fatal("out of memory");
	}
380 381 382 383 384

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

385
	DO("render message",
386 387 388 389 390 391 392
	   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;
393 394 395 396 397 398 399 400 401 402 403

	isccc_ccmsg_cancelread(&ccmsg);
	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
						    rndc_recvdone, NULL));
	recvs++;
	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
					   NULL));
	sends++;

	isc_event_free(&event);
	isccc_sexpr_free(&response);
404
	isccc_sexpr_free(&request);
405 406 407
	return;
}

408 409
static void
rndc_connected(isc_task_t *task, isc_event_t *event) {
410
	char socktext[ISC_SOCKADDR_FORMATSIZE];
411 412 413 414 415 416 417 418
	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
	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;

419 420
	connects--;

421
	if (sevent->result != ISC_R_SUCCESS) {
422 423
		isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
				    sizeof(socktext));
424
		if (sevent->result != ISC_R_CANCELED &&
425
		    ++currentaddr < nserveraddrs)
426
		{
427
			notify("connection failed: %s: %s", socktext,
428
			       isc_result_totext(sevent->result));
429
			isc_socket_detach(&sock);
430
			isc_event_free(&event);
431
			rndc_startconnect(&serveraddrs[currentaddr], task);
432 433
			return;
		} else
434
			fatal("connect failed: %s: %s", socktext,
435 436
			      isc_result_totext(sevent->result));
	}
437 438

	isc_stdtime_get(&now);
439
	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
440 441 442 443
						    now, now + 60, &request));
	data = isccc_alist_lookup(request, "_data");
	if (data == NULL)
		fatal("_data section missing");
444
	if (isccc_cc_definestring(data, "type", "null") == NULL)
445
		fatal("out of memory");
446 447 448 449 450

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

451
	DO("render message",
452 453 454 455 456 457 458
	   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;
459

460
	isccc_ccmsg_init(rndc_mctx, sock, &ccmsg);
461
	isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024);
462 463

	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
464
						    rndc_recvnonce, NULL));
465
	recvs++;
466 467
	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
					   NULL));
468
	sends++;
469
	isc_event_free(&event);
470
	isccc_sexpr_free(&request);
471 472 473
}

static void
474
rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
475
	isc_result_t result;
476 477
	int pf;
	isc_sockettype_t type;
478

479
	char socktext[ISC_SOCKADDR_FORMATSIZE];
480

481
	isc_sockaddr_format(addr, socktext, sizeof(socktext));
482 483 484

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

485 486 487 488 489 490
	pf = isc_sockaddr_pf(addr);
	if (pf == AF_INET || pf == AF_INET6)
		type = isc_sockettype_tcp;
	else
		type = isc_sockettype_unix;
	DO("create socket", isc_socket_create(socketmgr, pf, type, &sock));
491 492
	switch (isc_sockaddr_pf(addr)) {
	case AF_INET:
493
		DO("bind socket", isc_socket_bind(sock, &local4, 0));
494 495
		break;
	case AF_INET6:
496
		DO("bind socket", isc_socket_bind(sock, &local6, 0));
497 498 499 500
		break;
	default:
		break;
	}
501
	DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
502
					 NULL));
503
	connects++;
504 505
}

506 507 508 509 510
static void
rndc_start(isc_task_t *task, isc_event_t *event) {
	isc_event_free(&event);

	currentaddr = 0;
511
	rndc_startconnect(&serveraddrs[currentaddr], task);
512 513
}

514
static void
Mark Andrews's avatar
Mark Andrews committed
515 516
parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
	     cfg_parser_t **pctxp, cfg_obj_t **configp)
517 518 519
{
	isc_result_t result;
	const char *conffile = admin_conffile;
520 521 522 523 524 525 526 527 528 529
	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;
Mark Andrews's avatar
Mark Andrews committed
530
	cfg_obj_t *config = NULL;
531 532
	const cfg_obj_t *address = NULL;
	const cfg_listelt_t *elt;
Mark Andrews's avatar
Mark Andrews committed
533
	const char *secretstr;
534
	const char *algorithmstr;
Mark Andrews's avatar
Mark Andrews committed
535 536
	static char secretarray[1024];
	const cfg_type_t *conftype = &cfg_type_rndcconf;
537
	bool key_only = false;
538
	const cfg_listelt_t *element;
539

540
	if (! isc_file_exists(conffile)) {
Mark Andrews's avatar
Mark Andrews committed
541 542
		conffile = admin_keyfile;
		conftype = &cfg_type_rndckey;
543

544 545 546
		if (c_flag)
			fatal("%s does not exist", admin_conffile);

547
		if (! isc_file_exists(conffile))
548
			fatal("neither %s nor %s was found",
Mark Andrews's avatar
Mark Andrews committed
549
			      admin_conffile, admin_keyfile);
550
		key_only = true;
551 552 553 554
	} else if (! c_flag && isc_file_exists(admin_keyfile)) {
		fprintf(stderr, "WARNING: key file (%s) exists, but using "
			"default configuration file (%s)\n",
			admin_keyfile, admin_conffile);
555 556 557 558 559 560 561
	}

	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
562
	result = cfg_parse_file(*pctxp, conffile, conftype, &config);
563 564
	if (result != ISC_R_SUCCESS)
		fatal("could not load rndc configuration");
565

Mark Andrews's avatar
Mark Andrews committed
566 567 568 569
	if (!key_only)
		(void)cfg_map_get(config, "options", &options);

	if (key_only && servername == NULL)
570
		servername = "127.0.0.1";
Mark Andrews's avatar
Mark Andrews committed
571
	else if (servername == NULL && options != NULL) {
572
		const cfg_obj_t *defserverobj = NULL;
Mark Andrews's avatar
Mark Andrews committed
573 574 575 576 577 578 579 580 581
		(void)cfg_map_get(options, "default-server", &defserverobj);
		if (defserverobj != NULL)
			servername = cfg_obj_asstring(defserverobj);
	}

	if (servername == NULL)
		fatal("no server specified and no default");

	if (!key_only) {
582
		(void)cfg_map_get(config, "server", &servers);
Mark Andrews's avatar
Mark Andrews committed
583 584
		if (servers != NULL) {
			for (elt = cfg_list_first(servers);
Automatic Updater's avatar
Automatic Updater committed
585
			     elt != NULL;
Mark Andrews's avatar
Mark Andrews committed
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
			     elt = cfg_list_next(elt))
			{
				const char *name;
				server = cfg_listelt_value(elt);
				name = cfg_obj_asstring(cfg_map_getname(server));
				if (strcasecmp(name, servername) == 0)
					break;
				server = NULL;
			}
		}
	}

	/*
	 * Look for the name of the key to use.
	 */
	if (keyname != NULL)
		;		/* Was set on command line, do nothing. */
	else if (server != NULL) {
		DO("get key for server", cfg_map_get(server, "key", &defkey));
		keyname = cfg_obj_asstring(defkey);
	} else if (options != NULL) {
		DO("get default key", cfg_map_get(options, "default-key",
						  &defkey));
		keyname = cfg_obj_asstring(defkey);
	} else if (!key_only)
		fatal("no key for server and no default");

	/*
	 * Get the key's definition.
	 */
	if (key_only)
		DO("get key", cfg_map_get(config, "key", &key));
	else {
		DO("get config key list", cfg_map_get(config, "key", &keys));
		for (elt = cfg_list_first(keys);
Automatic Updater's avatar
Automatic Updater committed
621
		     elt != NULL;
Mark Andrews's avatar
Mark Andrews committed
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
		     elt = cfg_list_next(elt))
		{
			key = cfg_listelt_value(elt);
			if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
				       keyname) == 0)
				break;
		}
		if (elt == NULL)
			fatal("no key definition for name %s", keyname);
	}
	(void)cfg_map_get(key, "secret", &secretobj);
	(void)cfg_map_get(key, "algorithm", &algorithmobj);
	if (secretobj == NULL || algorithmobj == NULL)
		fatal("key must have algorithm and secret");

	secretstr = cfg_obj_asstring(secretobj);
638 639
	algorithmstr = cfg_obj_asstring(algorithmobj);

640
#ifndef PK11_MD5_DISABLE
641 642
	if (strcasecmp(algorithmstr, "hmac-md5") == 0)
		algorithm = ISCCC_ALG_HMACMD5;
643 644 645
	else
#endif
	if (strcasecmp(algorithmstr, "hmac-sha1") == 0)
646 647 648 649 650 651 652 653 654 655 656
		algorithm = ISCCC_ALG_HMACSHA1;
	else if (strcasecmp(algorithmstr, "hmac-sha224") == 0)
		algorithm = ISCCC_ALG_HMACSHA224;
	else if (strcasecmp(algorithmstr, "hmac-sha256") == 0)
		algorithm = ISCCC_ALG_HMACSHA256;
	else if (strcasecmp(algorithmstr, "hmac-sha384") == 0)
		algorithm = ISCCC_ALG_HMACSHA384;
	else if (strcasecmp(algorithmstr, "hmac-sha512") == 0)
		algorithm = ISCCC_ALG_HMACSHA512;
	else
		fatal("unsupported algorithm: %s", algorithmstr);
Mark Andrews's avatar
Mark Andrews committed
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672

	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.
	 */
	if (remoteport != 0)
		;		/* Was set on command line, do nothing. */
	else {
		if (server != NULL)
			(void)cfg_map_get(server, "port", &defport);
		if (defport == NULL && options != NULL)
673
			(void)cfg_map_get(options, "default-port", &defport);
Mark Andrews's avatar
Mark Andrews committed
674 675 676 677
	}
	if (defport != NULL) {
		remoteport = cfg_obj_asuint32(defport);
		if (remoteport > 65535 || remoteport == 0)
678
			fatal("port %u out of range", remoteport);
Mark Andrews's avatar
Mark Andrews committed
679 680 681
	} else if (remoteport == 0)
		remoteport = NS_CONTROL_PORT;

682 683 684 685 686 687 688 689 690 691 692
	if (server != NULL)
		result = cfg_map_get(server, "addresses", &addresses);
	else
		result = ISC_R_NOTFOUND;
	if (result == ISC_R_SUCCESS) {
		for (element = cfg_list_first(addresses);
		     element != NULL;
		     element = cfg_list_next(element))
		{
			isc_sockaddr_t sa;

693
			address = cfg_listelt_value(element);
694 695 696
			if (!cfg_obj_issockaddr(address)) {
				unsigned int myport;
				const char *name;
697
				const cfg_obj_t *obj;
698 699 700 701 702 703

				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);
704
					if (myport > UINT16_MAX ||
705 706 707 708 709 710 711 712 713
					    myport == 0)
						fatal("port %u out of range",
						      myport);
				} else
					myport = remoteport;
				if (nserveraddrs < SERVERADDRS)
					get_addresses(name, (in_port_t) myport);
				else
					fprintf(stderr, "too many address: "
Automatic Updater's avatar
Automatic Updater committed
714
						"%s: dropped\n", name);
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
				continue;
			}
			sa = *cfg_obj_assockaddr(address);
			if (isc_sockaddr_getport(&sa) == 0)
				isc_sockaddr_setport(&sa, remoteport);
			if (nserveraddrs < SERVERADDRS)
				serveraddrs[nserveraddrs++] = sa;
			else {
				char socktext[ISC_SOCKADDR_FORMATSIZE];

				isc_sockaddr_format(&sa, socktext,
						    sizeof(socktext));
				fprintf(stderr,
					"too many address: %s: dropped\n",
					socktext);
			}
		}
	}
733 734 735 736 737 738

	if (!local4set && server != NULL) {
		address = NULL;
		cfg_map_get(server, "source-address", &address);
		if (address != NULL) {
			local4 = *cfg_obj_assockaddr(address);
739
			local4set = true;
740 741 742 743 744 745 746
		}
	}
	if (!local4set && options != NULL) {
		address = NULL;
		cfg_map_get(options, "default-source-address", &address);
		if (address != NULL) {
			local4 = *cfg_obj_assockaddr(address);
747
			local4set = true;
748 749 750 751 752 753 754 755
		}
	}

	if (!local6set && server != NULL) {
		address = NULL;
		cfg_map_get(server, "source-address-v6", &address);
		if (address != NULL) {
			local6 = *cfg_obj_assockaddr(address);
756
			local6set = true;
757 758 759 760 761 762 763
		}
	}
	if (!local6set && options != NULL) {
		address = NULL;
		cfg_map_get(options, "default-source-address-v6", &address);
		if (address != NULL) {
			local6 = *cfg_obj_assockaddr(address);
764
			local6set = true;
765 766 767
		}
	}

Mark Andrews's avatar
Mark Andrews committed
768
	*configp = config;
769 770
}

771 772
int
main(int argc, char **argv) {
773
	isc_result_t result = ISC_R_SUCCESS;
774
	bool show_final_mem = false;
775
	isc_taskmgr_t *taskmgr = NULL;
776
	isc_task_t *task = NULL;
777 778 779 780 781
	isc_log_t *log = NULL;
	isc_logconfig_t *logconfig = NULL;
	isc_logdestination_t logdest;
	cfg_parser_t *pctx = NULL;
	cfg_obj_t *config = NULL;
782
	const char *keyname = NULL;
783 784
	struct in_addr in;
	struct in6_addr in6;
785
	char *p;
786
	size_t argslen;
787
	int ch;
788
	int i;
789

790
	result = isc_file_progname(*argv, program, sizeof(program));
791
	if (result != ISC_R_SUCCESS)
792
		memmove(program, "rndc", 5);
793
	progname = program;
794

Mark Andrews's avatar
Mark Andrews committed
795 796 797
	admin_conffile = RNDC_CONFFILE;
	admin_keyfile = RNDC_KEYFILE;

798 799 800
	isc_sockaddr_any(&local4);
	isc_sockaddr_any6(&local6);

801 802 803
	result = isc_app_start();
	if (result != ISC_R_SUCCESS)
		fatal("isc_app_start() failed: %s", isc_result_totext(result));
Mark Andrews's avatar
Mark Andrews committed
804

805
	isc_commandline_errprint = false;
806

807
	while ((ch = isc_commandline_parse(argc, argv, "b:c:hk:Mmp:qrs:Vy:"))
808
	       != -1) {
809
		switch (ch) {
810 811 812 813
		case 'b':
			if (inet_pton(AF_INET, isc_commandline_argument,
				      &in) == 1) {
				isc_sockaddr_fromin(&local4, &in, 0);
814
				local4set = true;
815 816 817
			} else if (inet_pton(AF_INET6, isc_commandline_argument,
					     &in6) == 1) {
				isc_sockaddr_fromin6(&local6, &in6, 0);
818
				local6set = true;
819 820 821
			}
			break;

822
		case 'c':
823
			admin_conffile = isc_commandline_argument;
824
			c_flag = true;
825 826
			break;

Mark Andrews's avatar
Mark Andrews committed
827 828 829 830
		case 'k':
			admin_keyfile = isc_commandline_argument;
			break;

831
		case 'M':
832
			isc_mem_debugging = ISC_MEM_DEBUGTRACE;
833 834
			break;

835
		case 'm':
836
			show_final_mem = true;
837 838 839
			break;

		case 'p':
840
			remoteport = atoi(isc_commandline_argument);
Mark Andrews's avatar
Mark Andrews committed
841
			if (remoteport > 65535 || remoteport == 0)
842 843
				fatal("port '%s' out of range",
				      isc_commandline_argument);
844 845
			break;

Evan Hunt's avatar
Evan Hunt committed
846
		case 'q':
847
			quiet = true;
Evan Hunt's avatar
Evan Hunt committed
848 849
			break;

850
		case 'r':
851
			showresult = true;
852 853
			break;

854 855 856
		case 's':
			servername = isc_commandline_argument;
			break;
857

858
		case 'V':
859
			verbose = true;
860
			break;
861

862 863 864
		case 'y':
			keyname = isc_commandline_argument;
			break;
Automatic Updater's avatar
Automatic Updater committed
865

866
		case '?':
867 868 869 870 871
			if (isc_commandline_option != '?') {
				fprintf(stderr, "%s: invalid argument -%c\n",
					program, isc_commandline_option);
				usage(1);
			}
Evan Hunt's avatar
Evan Hunt committed
872
			/* FALLTHROUGH */
873
		case 'h':
874
			usage(0);
875 876
			break;
		default:
877 878
			fprintf(stderr, "%s: unhandled option -%c\n",
				program, isc_commandline_option);
Automatic Updater's avatar
Automatic Updater committed
879
			exit(1);
880 881 882 883 884 885
		}
	}

	argc -= isc_commandline_index;
	argv += isc_commandline_index;

886 887
	if (argc < 1)
		usage(1);
888

889 890
	isc_random_get(&serial);

891 892 893
	DO("create memory context", isc_mem_create(0, 0, &rndc_mctx));
	DO("create socket manager", isc_socketmgr_create(rndc_mctx, &socketmgr));
	DO("create task manager", isc_taskmgr_create(rndc_mctx, 1, 0, &taskmgr));
894
	DO("create task", isc_task_create(taskmgr, 0, &task));
895

896
	DO("create logging context", isc_log_create(rndc_mctx, &log, &logconfig));
897 898 899 900 901 902 903 904
	isc_log_setcontext(log);
	DO("setting log tag", isc_log_settag(logconfig, progname));
	logdest.file.stream = stderr;
	logdest.file.name = NULL;
	logdest.file.versions = ISC_LOG_ROLLNEVER;
	logdest.file.maximum_size = 0;
	DO("creating log channel",
	   isc_log_createchannel(logconfig, "stderr",
Automatic Updater's avatar
Automatic Updater committed
905
				 ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
906 907 908
				 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
	DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
						      NULL, NULL));
909

910
	parse_config(rndc_mctx, log, keyname, &pctx, &config);
911

912
	isccc_result_register();
913

914
	command = *argv;
915

916
	DO("allocate data buffer",
917
	   isc_buffer_allocate(rndc_mctx, &databuf, 2048));
918

919 920 921 922 923 924 925 926 927
	/*
	 * Convert argc/argv into a space-delimited command string
	 * similar to what the user might enter in interactive mode
	 * (if that were implemented).
	 */
	argslen = 0;
	for (i = 0; i < argc; i++)
		argslen += strlen(argv[i]) + 1;

928
	args = isc_mem_get(rndc_mctx, argslen);
929 930 931 932 933 934
	if (args == NULL)
		DO("isc_mem_get", ISC_R_NOMEMORY);

	p = args;
	for (i = 0; i < argc; i++) {
		size_t len = strlen(argv[i]);
935
		memmove(p, argv[i], len);
936 937 938
		p += len;
		*p++ = ' ';
	}
939