rndc.c 28 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/buffer.h>
20
#include <isc/commandline.h>
21
#include <isc/file.h>
22
#include <isc/log.h>
23
#include <isc/net.h>
24
#include <isc/mem.h>
25
#include <isc/print.h>
26
#include <isc/random.h>
27
#include <isc/socket.h>
28
#include <isc/stdtime.h>
29
#include <isc/string.h>
30
#include <isc/task.h>
31
#include <isc/thread.h>
32 33
#include <isc/util.h>

34 35
#include <pk11/site.h>

36
#include <isccfg/namedconf.h>
37

38 39 40 41 42 43 44 45 46
#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
47 48
#include <dns/name.h>

49
#include <bind9/getaddresses.h>
50

51
#include "util.h"
52

53
#define SERVERADDRS 10
54

55
const char *progname;
56
bool verbose;
57

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

85 86
static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);

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

90
static void
91
usage(int status) {
92
	fprintf(stderr, "\
93
Usage: %s [-b address] [-c config] [-s server] [-p port]\n\
94
	[-k key-file ] [-y key] [-r] [-V] [-4 | -6] command\n\
95
\n\
96
command is one of the following:\n\
97
\n\
98
  addzone zone [class [view]] { zone-options }\n\
99
		Add zone to given view. Requires allow-new-zones option.\n\
100
  delzone [-clean] zone [class [view]]\n\
101
		Removes zone from given view.\n\
Mark Andrews's avatar
Mark Andrews committed
102
  dnstap -reopen\n\
103
		Close, truncate and re-open the DNSTAP output file.\n\
Mark Andrews's avatar
Mark Andrews committed
104 105
  dnstap -roll count\n\
		Close, rename and re-open the DNSTAP output file(s).\n\
Evan Hunt's avatar
Evan Hunt committed
106
  dumpdb [-all|-cache|-zones|-adb|-bad|-fail] [view ...]\n\
107
		Dump cache(s) to the dump file (named_dump.db).\n\
108 109
  flush 	Flushes all of the server's caches.\n\
  flush [view]	Flushes the server's cache for a view.\n\
110
  flushname name [view]\n\
Mark Andrews's avatar
Mark Andrews committed
111
		Flush the given name from the server's cache(s)\n\
112 113
  flushtree name [view]\n\
		Flush all names under the given name from the server's cache(s)\n\
114 115 116 117 118 119 120 121
  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
122 123 124 125 126 127
  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\
128 129 130
  modzone zone [class [view]] { zone-options }\n\
		Modify a zone's configuration.\n\
		Requires allow-new-zones option.\n\
131 132 133
  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
134 135
  nta -dump\n\
		List all negative trust anchors.\n\
136 137 138 139
  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\
140
		to one week.\n\
141 142 143 144 145
		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\
146
  querylog [ on | off ]\n\
147 148 149 150 151 152 153 154 155 156
		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\
157
  scan		Scan available network interfaces for changes.\n\
158 159
  secroots [view ...]\n\
		Write security roots to the secroots file.\n\
160
  serve-stale [ yes | no | reset | status ] [class [view]]\n\
161
		Control whether stale answers are returned\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
  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\
192 193 194
  tcp-timeouts	Display the tcp-*-timeout option values\n\
  tcp-timeouts initial idle keepalive advertised\n\
		Update the tcp-*-timeout option values\n\
195 196 197 198 199 200 201 202 203
  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\
204
  validation [ yes | no | status ] [view]\n\
205 206 207
		Enable / disable DNSSEC validation.\n\
  zonestatus zone [class [view]]\n\
		Display the current status of a zone.\n\
208
\n\
209 210
Version: %s\n",
		progname, version);
211 212

	exit(status);
213 214
}

215 216 217 218
#define CMDLINE_FLAGS "46b:c:hk:Mmp:qrs:Vy:"

static void
preparse_args(int argc, char **argv) {
219
	bool ipv4only = false, ipv6only = false;
220 221 222 223 224 225 226 227
	int ch;

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

241
	isc_commandline_reset = true;
242 243 244
	isc_commandline_index = 1;
}

245
static void
246
get_addresses(const char *host, in_port_t port) {
247
	isc_result_t result;
248
	int found = 0, count;
249

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

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

	UNUSED(task);

274
	sends--;
275 276 277
	if (sevent->result != ISC_R_SUCCESS)
		fatal("send failed: %s", isc_result_totext(sevent->result));
	isc_event_free(&event);
278 279 280
	if (sends == 0 && recvs == 0) {
		isc_socket_detach(&sock);
		isc_task_shutdown(task);
281
		isc_app_shutdown();
282
	}
283 284 285 286 287 288 289 290
}

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;
291
	char *textmsg = NULL;
292 293
	isc_result_t result;

294 295
	recvs--;

296
	if (ccmsg.result == ISC_R_EOF)
297
		fatal("connection to remote host closed\n"
298 299 300 301
		      "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
302
		      "* the clocks are not synchronized, or\n"
303
		      "* the key is invalid.");
304 305 306 307 308 309

	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);
310

311 312
	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
313

314
	data = isccc_alist_lookup(response, "_data");
315 316
	if (!isccc_alist_alistp(data))
		fatal("bad or missing data section in response");
317 318
	result = isccc_cc_lookupstring(data, "err", &errormsg);
	if (result == ISC_R_SUCCESS) {
319
		failed = true;
320 321 322 323 324 325 326
		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));

327
	result = isccc_cc_lookupstring(data, "text", &textmsg);
328
	if (result == ISC_R_SUCCESS) {
Evan Hunt's avatar
Evan Hunt committed
329 330
		if ((!quiet || failed) && strlen(textmsg) != 0U)
			fprintf(failed ? stderr : stdout, "%s\n", textmsg);
331
	} else if (result != ISC_R_NOTFOUND)
332 333 334
		fprintf(stderr, "%s: parsing response failed: %s\n",
			progname, isc_result_totext(result));

335 336 337 338 339 340 341 342 343 344
	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");
	}

345 346
	isc_event_free(&event);
	isccc_sexpr_free(&response);
347 348 349
	if (sends == 0 && recvs == 0) {
		isc_socket_detach(&sock);
		isc_task_shutdown(task);
350
		isc_app_shutdown();
351
	}
352 353
}

354 355 356 357 358 359
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;
360
	uint32_t nonce;
361 362 363 364 365 366 367 368 369 370
	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"
371 372 373 374
		      "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"
375
		      "* the clocks are not synchronized,\n"
Evan Hunt's avatar
Evan Hunt committed
376
		      "* the key signing algorithm is incorrect, or\n"
377
		      "* the key is invalid.");
378 379 380 381 382 383 384

	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);

385 386
	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
387 388

	_ctrl = isccc_alist_lookup(response, "_ctrl");
389 390
	if (!isccc_alist_alistp(_ctrl))
		fatal("bad or missing ctrl section in response");
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
	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");
	}
411 412 413 414 415

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

416
	DO("render message",
417 418 419 420 421 422 423
	   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;
424 425 426 427 428 429 430 431 432 433 434

	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);
435
	isccc_sexpr_free(&request);
436 437 438
	return;
}

439 440
static void
rndc_connected(isc_task_t *task, isc_event_t *event) {
441
	char socktext[ISC_SOCKADDR_FORMATSIZE];
442 443 444 445 446 447 448 449
	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;

450 451
	connects--;

452
	if (sevent->result != ISC_R_SUCCESS) {
453 454
		isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
				    sizeof(socktext));
455
		if (sevent->result != ISC_R_CANCELED &&
456
		    ++currentaddr < nserveraddrs)
457
		{
458
			notify("connection failed: %s: %s", socktext,
459
			       isc_result_totext(sevent->result));
460
			isc_socket_detach(&sock);
461
			isc_event_free(&event);
462
			rndc_startconnect(&serveraddrs[currentaddr], task);
463 464
			return;
		} else
465
			fatal("connect failed: %s: %s", socktext,
466 467
			      isc_result_totext(sevent->result));
	}
468 469

	isc_stdtime_get(&now);
470
	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
471 472 473 474
						    now, now + 60, &request));
	data = isccc_alist_lookup(request, "_data");
	if (data == NULL)
		fatal("_data section missing");
475
	if (isccc_cc_definestring(data, "type", "null") == NULL)
476
		fatal("out of memory");
477 478 479 480 481

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

482
	DO("render message",
483 484 485 486 487 488 489
	   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;
490

491
	isccc_ccmsg_init(rndc_mctx, sock, &ccmsg);
492
	isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024);
493 494

	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
495
						    rndc_recvnonce, NULL));
496
	recvs++;
497 498
	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
					   NULL));
499
	sends++;
500
	isc_event_free(&event);
501
	isccc_sexpr_free(&request);
502 503 504
}

static void
505
rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
506
	isc_result_t result;
507 508
	int pf;
	isc_sockettype_t type;
509

510
	char socktext[ISC_SOCKADDR_FORMATSIZE];
511

512
	isc_sockaddr_format(addr, socktext, sizeof(socktext));
513 514 515

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

516 517 518 519 520 521
	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));
522 523
	switch (isc_sockaddr_pf(addr)) {
	case AF_INET:
524
		DO("bind socket", isc_socket_bind(sock, &local4, 0));
525 526
		break;
	case AF_INET6:
527
		DO("bind socket", isc_socket_bind(sock, &local6, 0));
528 529 530 531
		break;
	default:
		break;
	}
532
	DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
533
					 NULL));
534
	connects++;
535 536
}

537 538 539 540 541
static void
rndc_start(isc_task_t *task, isc_event_t *event) {
	isc_event_free(&event);

	currentaddr = 0;
542
	rndc_startconnect(&serveraddrs[currentaddr], task);
543 544
}

545
static void
Mark Andrews's avatar
Mark Andrews committed
546 547
parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
	     cfg_parser_t **pctxp, cfg_obj_t **configp)
548 549 550
{
	isc_result_t result;
	const char *conffile = admin_conffile;
551 552 553 554 555 556 557 558 559 560
	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
561
	cfg_obj_t *config = NULL;
562 563
	const cfg_obj_t *address = NULL;
	const cfg_listelt_t *elt;
Mark Andrews's avatar
Mark Andrews committed
564
	const char *secretstr;
565
	const char *algorithmstr;
Mark Andrews's avatar
Mark Andrews committed
566 567
	static char secretarray[1024];
	const cfg_type_t *conftype = &cfg_type_rndcconf;
568
	bool key_only = false;
569
	const cfg_listelt_t *element;
570

571
	if (! isc_file_exists(conffile)) {
Mark Andrews's avatar
Mark Andrews committed
572 573
		conffile = admin_keyfile;
		conftype = &cfg_type_rndckey;
574

575 576 577
		if (c_flag)
			fatal("%s does not exist", admin_conffile);

578
		if (! isc_file_exists(conffile))
579
			fatal("neither %s nor %s was found",
Mark Andrews's avatar
Mark Andrews committed
580
			      admin_conffile, admin_keyfile);
581
		key_only = true;
582 583 584 585
	} 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);
586 587 588 589 590 591 592
	}

	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
593
	result = cfg_parse_file(*pctxp, conffile, conftype, &config);
594 595
	if (result != ISC_R_SUCCESS)
		fatal("could not load rndc configuration");
596

Mark Andrews's avatar
Mark Andrews committed
597 598 599 600
	if (!key_only)
		(void)cfg_map_get(config, "options", &options);

	if (key_only && servername == NULL)
601
		servername = "127.0.0.1";
Mark Andrews's avatar
Mark Andrews committed
602
	else if (servername == NULL && options != NULL) {
603
		const cfg_obj_t *defserverobj = NULL;
Mark Andrews's avatar
Mark Andrews committed
604 605 606 607 608 609 610 611 612
		(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) {
613
		(void)cfg_map_get(config, "server", &servers);
Mark Andrews's avatar
Mark Andrews committed
614 615
		if (servers != NULL) {
			for (elt = cfg_list_first(servers);
Automatic Updater's avatar
Automatic Updater committed
616
			     elt != NULL;
Mark Andrews's avatar
Mark Andrews committed
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
			     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
652
		     elt != NULL;
Mark Andrews's avatar
Mark Andrews committed
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
		     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);
669 670
	algorithmstr = cfg_obj_asstring(algorithmobj);

Ondřej Surý's avatar
Ondřej Surý committed
671
	if (strcasecmp(algorithmstr, "hmac-md5") == 0) {
672
		algorithm = ISCCC_ALG_HMACMD5;
Ondřej Surý's avatar
Ondřej Surý committed
673
	} else if (strcasecmp(algorithmstr, "hmac-sha1") == 0) {
674
		algorithm = ISCCC_ALG_HMACSHA1;
Ondřej Surý's avatar
Ondřej Surý committed
675
	} else if (strcasecmp(algorithmstr, "hmac-sha224") == 0) {
676
		algorithm = ISCCC_ALG_HMACSHA224;
Ondřej Surý's avatar
Ondřej Surý committed
677
	} else if (strcasecmp(algorithmstr, "hmac-sha256") == 0) {
678
		algorithm = ISCCC_ALG_HMACSHA256;
Ondřej Surý's avatar
Ondřej Surý committed
679
	} else if (strcasecmp(algorithmstr, "hmac-sha384") == 0) {
680
		algorithm = ISCCC_ALG_HMACSHA384;
Ondřej Surý's avatar
Ondřej Surý committed
681
	} else if (strcasecmp(algorithmstr, "hmac-sha512") == 0) {
682
		algorithm = ISCCC_ALG_HMACSHA512;
Ondřej Surý's avatar
Ondřej Surý committed
683
	} else {
684
		fatal("unsupported algorithm: %s", algorithmstr);
Ondřej Surý's avatar
Ondřej Surý committed
685
	}
Mark Andrews's avatar
Mark Andrews committed
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701

	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)
702
			(void)cfg_map_get(options, "default-port", &defport);
Mark Andrews's avatar
Mark Andrews committed
703 704 705 706
	}
	if (defport != NULL) {
		remoteport = cfg_obj_asuint32(defport);
		if (remoteport > 65535 || remoteport == 0)
707
			fatal("port %u out of range", remoteport);
Mark Andrews's avatar
Mark Andrews committed
708 709 710
	} else if (remoteport == 0)
		remoteport = NS_CONTROL_PORT;

711 712 713 714 715 716 717 718 719 720 721
	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;

722
			address = cfg_listelt_value(element);
723 724 725
			if (!cfg_obj_issockaddr(address)) {
				unsigned int myport;
				const char *name;
726
				const cfg_obj_t *obj;
727 728 729 730 731 732

				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);
733
					if (myport > UINT16_MAX ||
734 735 736 737 738 739 740 741 742
					    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
743
						"%s: dropped\n", name);
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
				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);
			}
		}
	}
762 763 764 765 766 767

	if (!local4set && server != NULL) {
		address = NULL;
		cfg_map_get(server, "source-address", &address);
		if (address != NULL) {
			local4 = *cfg_obj_assockaddr(address);
768
			local4set = true;
769 770 771 772 773 774 775
		}
	}
	if (!local4set && options != NULL) {
		address = NULL;
		cfg_map_get(options, "default-source-address", &address);
		if (address != NULL) {
			local4 = *cfg_obj_assockaddr(address);
776
			local4set = true;
777 778 779 780 781 782 783 784
		}
	}

	if (!local6set && server != NULL) {
		address = NULL;
		cfg_map_get(server, "source-address-v6", &address);
		if (address != NULL) {
			local6 = *cfg_obj_assockaddr(address);
785
			local6set = true;
786 787 788 789 790 791 792
		}
	}
	if (!local6set && options != NULL) {
		address = NULL;
		cfg_map_get(options, "default-source-address-v6", &address);
		if (address != NULL) {
			local6 = *cfg_obj_assockaddr(address);
793
			local6set = true;
794 795 796
		}
	}

Mark Andrews's avatar
Mark Andrews committed
797
	*configp = config;
798 799
}

800 801
int
main(int argc, char **argv) {
802
	isc_result_t result = ISC_R_SUCCESS;
803
	bool show_final_mem = false;
804
	isc_taskmgr_t *taskmgr = NULL;
805
	isc_task_t *task = NULL;
806 807 808 809 810
	isc_log_t *log = NULL;
	isc_logconfig_t *logconfig = NULL;
	isc_logdestination_t logdest;
	cfg_parser_t *pctx = NULL;
	cfg_obj_t *config = NULL;
811
	const char *keyname = NULL;
812 813
	struct in_addr in;
	struct in6_addr in6;
814
	char *p;
815
	size_t argslen;
816
	int ch;
817
	int i;
818

819
	result = isc_file_progname(*argv, program, sizeof(program));
820
	if (result != ISC_R_SUCCESS)
821
		memmove(program, "rndc", 5);
822
	progname = program;
823

Mark Andrews's avatar
Mark Andrews committed
824 825 826
	admin_conffile = RNDC_CONFFILE;
	admin_keyfile = RNDC_KEYFILE;

827 828 829
	isc_sockaddr_any(&local4);
	isc_sockaddr_any6(&local6);

830 831 832
	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
833

834
	isc_commandline_errprint = false;
835

836 837 838
	preparse_args(argc, argv);

	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
839
		switch (ch) {
840 841 842 843 844 845 846 847 848 849 850 851
		case '4':
			if (isc_net_probeipv4() != ISC_R_SUCCESS) {
				fatal("can't find IPv4 networking");
			}
			isc_net_disableipv6();
			break;
		case '6':
			if (isc_net_probeipv6() != ISC_R_SUCCESS) {
				fatal("can't find IPv6 networking");
			}
			isc_net_disableipv4();
			break;
852 853 854 855
		case 'b':
			if (inet_pton(AF_INET, isc_commandline_argument,
				      &in) == 1) {
				isc_sockaddr_fromin(&local4, &in, 0);
856
				local4set = true;
857 858 859
			} else if (inet_pton(AF_INET6, isc_commandline_argument,
					     &in6) == 1) {
				isc_sockaddr_fromin6(&local6, &in6, 0);
860
				local6set = true;
861 862 863
			}
			break;

864
		case 'c':
865
			admin_conffile = isc_commandline_argument;
866
			c_flag = true;
867 868
			break;

Mark Andrews's avatar
Mark Andrews committed
869 870 871 872
		case 'k':
			admin_keyfile = isc_commandline_argument;
			break;

873
		case 'M':
874
			isc_mem_debugging = ISC_MEM_DEBUGTRACE;
875 876
			break;

877
		case 'm':
878
			show_final_mem = true;
879 880 881
			break;

		case 'p':
882
			remoteport = atoi(isc_commandline_argument);
Mark Andrews's avatar
Mark Andrews committed
883
			if (remoteport > 65535 || remoteport == 0)
884 885
				fatal("port '%s' out of range",
				      isc_commandline_argument);
886 887
			break;

Evan Hunt's avatar
Evan Hunt committed
888
		case 'q':
889
			quiet = true;
Evan Hunt's avatar
Evan Hunt committed
890 891
			break;

892
		case 'r':
893
			showresult = true;
894 895
			break;

896 897 898
		case 's':
			servername = isc_commandline_argument;
			break;
899

900
		case 'V':