rndc.c 26.4 KB
Newer Older
1
/*
Tinderbox User's avatar
Tinderbox User committed
2
 * Copyright (C) 2004-2014  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Copyright (C) 2000-2003  Internet Software Consortium.
4
 *
Automatic Updater's avatar
Automatic Updater committed
5
 * Permission to use, copy, modify, and/or distribute this software for any
6
7
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
8
 *
Mark Andrews's avatar
Mark Andrews committed
9
10
11
12
13
14
15
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
16
17
 */

18
/*! \file */
19

20
/*
21
22
23
 * Principal Author: DCL
 */

David Lawrence's avatar
David Lawrence committed
24
25
#include <config.h>

26
27
#include <stdlib.h>

28
#include <isc/app.h>
29
#include <isc/buffer.h>
30
#include <isc/commandline.h>
31
#include <isc/file.h>
32
#include <isc/log.h>
33
#include <isc/net.h>
34
#include <isc/mem.h>
35
#include <isc/random.h>
36
#include <isc/socket.h>
37
#include <isc/stdtime.h>
38
#include <isc/string.h>
39
#include <isc/task.h>
40
#include <isc/thread.h>
41
42
#include <isc/util.h>

43
#include <isccfg/namedconf.h>
44

45
46
47
48
49
50
51
52
53
#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
54
55
#include <dns/name.h>

56
#include <bind9/getaddresses.h>
57

58
#include "util.h"
59

60
#define SERVERADDRS 10
61

62
const char *progname;
63
64
isc_boolean_t verbose;

65
static const char *admin_conffile;
Mark Andrews's avatar
Mark Andrews committed
66
static const char *admin_keyfile;
67
static const char *version = VERSION;
68
static const char *servername = NULL;
69
static isc_sockaddr_t serveraddrs[SERVERADDRS];
70
71
static isc_sockaddr_t local4, local6;
static isc_boolean_t local4set = ISC_FALSE, local6set = ISC_FALSE;
72
73
static int nserveraddrs;
static int currentaddr = 0;
Mark Andrews's avatar
Mark Andrews committed
74
static unsigned int remoteport = 0;
75
76
77
static isc_socketmgr_t *socketmgr = NULL;
static unsigned char databuf[2048];
static isccc_ccmsg_t ccmsg;
78
static isc_uint32_t algorithm;
79
80
static isccc_region_t secret;
static isc_boolean_t failed = ISC_FALSE;
81
static isc_boolean_t c_flag = ISC_FALSE;
82
static isc_mem_t *mctx;
83
static int sends, recvs, connects;
84
85
static char *command;
static char *args;
86
static char program[256];
87
static isc_socket_t *sock = NULL;
88
static isc_uint32_t serial;
Evan Hunt's avatar
Evan Hunt committed
89
static isc_boolean_t quiet = ISC_FALSE;
90

91
92
static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);

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

96
static void
97
usage(int status) {
98
	fprintf(stderr, "\
99
Usage: %s [-b address] [-c config] [-s server] [-p port]\n\
Automatic Updater's avatar
Automatic Updater committed
100
	[-k key-file ] [-y key] [-V] command\n\
101
\n\
102
command is one of the following:\n\
103
\n\
104
105
106
107
108
  reload	Reload configuration file and zones.\n\
  reload zone [class [view]]\n\
		Reload a single zone.\n\
  refresh zone [class [view]]\n\
		Schedule immediate maintenance for a zone.\n\
109
110
  retransfer zone [class [view]]\n\
		Retransfer a single zone without checking serial number.\n\
111
  freeze	Suspend updates to all dynamic zones.\n\
112
  freeze zone [class [view]]\n\
Automatic Updater's avatar
Automatic Updater committed
113
		Suspend updates to a dynamic zone.\n\
114
  thaw		Enable updates to all dynamic zones and reload them.\n\
115
  thaw zone [class [view]]\n\
Automatic Updater's avatar
Automatic Updater committed
116
		Enable updates to a frozen dynamic zone and reload it.\n\
117
  sync [-clean]	Dump changes to all dynamic zones to disk, and optionally\n\
118
		remove their journal files.\n\
119
  sync [-clean] zone [class [view]]\n\
120
121
		Dump a single zone's changes to disk, and optionally\n\
		remove its journal file.\n\
122
123
  notify zone [class [view]]\n\
		Resend NOTIFY messages for the zone.\n\
124
  reconfig	Reload configuration file and new zones only.\n\
125
126
  sign zone [class [view]]\n\
		Update zone keys, and sign as needed.\n\
127
128
  loadkeys zone [class [view]]\n\
		Update keys without signing immediately.\n\
129
130
  zonestatus zone [class [view]]\n\
		Display the current status of a zone.\n\
131
  stats		Write server statistics to the statistics file.\n\
132
133
  querylog newstate\n\
		Enable / disable query logging.\n\
134
135
  dumpdb [-all|-cache|-zones] [view ...]\n\
		Dump cache(s) to the dump file (named_dump.db).\n\
136
137
  secroots [view ...]\n\
		Write security roots to the secroots file.\n\
138
  stop		Save pending updates to master files and stop the server.\n\
139
140
  stop -p	Save pending updates to master files and stop the server\n\
		reporting process id.\n\
141
  halt		Stop the server without saving pending updates.\n\
142
143
  halt -p	Stop the server without saving pending updates reporting\n\
		process id.\n\
144
  trace		Increment debugging level by one.\n\
145
  trace level	Change the debugging level.\n\
146
  notrace	Set debugging level to 0.\n\
147
148
  flush 	Flushes all of the server's caches.\n\
  flush [view]	Flushes the server's cache for a view.\n\
149
  flushname name [view]\n\
Mark Andrews's avatar
Mark Andrews committed
150
		Flush the given name from the server's cache(s)\n\
151
152
  flushtree name [view]\n\
		Flush all names under the given name from the server's cache(s)\n\
153
  status	Display status of the server.\n\
154
  recursing	Dump the queries that are currently recursing (named.recursing)\n\
155
156
157
158
  tsig-list	List all currently active TSIG keys, including both statically\n\
		configured and TKEY-negotiated keys.\n\
  tsig-delete keyname [view]	\n\
		Delete a TKEY-negotiated TSIG key.\n\
159
160
  validation newstate [view]\n\
		Enable / disable DNSSEC validation.\n\
161
162
163
164
165
166
167
168
169
170
171
172
  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\
		to one day.\n\
		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\
  nta -dump\n\
		List all negative trust anchors.\n\
173
  addzone zone [class [view]] { zone-options }\n\
174
		Add zone to given view. Requires new-zone-file option.\n\
175
  delzone [-clean] zone [class [view]]\n\
176
		Removes zone from given view. Requires new-zone-file option.\n\
177
  scan		Scan available network interfaces for changes.\n\
178
179
180
181
182
183
184
185
186
  signing -list zone [class [view]]\n\
		List the private records showing the state of DNSSEC\n\
		signing in the given zone.\n\
  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 -clear all zone [class [view]]\n\
		Remove the private records for all keys that have\n\
		finished signing the given zone.\n\
187
188
189
190
191
  signing -nsec3param none zone [class [view]]\n\
		Remove NSEC3 chains from zone.\n\
  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\
192
  *restart	Restart the server.\n\
193
\n\
194
195
196
* == not yet implemented\n\
Version: %s\n",
		progname, version);
197
198

	exit(status);
199
200
}

201
static void
202
get_addresses(const char *host, in_port_t port) {
203
	isc_result_t result;
204
	int found = 0, count;
205

206
207
208
209
	if (*host == '/') {
		result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
					       host);
		if (result == ISC_R_SUCCESS)
Automatic Updater's avatar
Automatic Updater committed
210
			nserveraddrs++;
211
212
213
214
215
216
217
	} else {
		count = SERVERADDRS - nserveraddrs;
		result = bind9_getaddresses(host, port,
					    &serveraddrs[nserveraddrs],
					    count, &found);
		nserveraddrs += found;
	}
218
219
220
	if (result != ISC_R_SUCCESS)
		fatal("couldn't get address for '%s': %s",
		      host, isc_result_totext(result));
221
	INSIST(nserveraddrs > 0);
222
223
224
225
226
227
228
229
}

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

	UNUSED(task);

230
	sends--;
231
232
233
	if (sevent->result != ISC_R_SUCCESS)
		fatal("send failed: %s", isc_result_totext(sevent->result));
	isc_event_free(&event);
234
235
236
237
238
	if (sends == 0 && recvs == 0) {
		isc_socket_detach(&sock);
		isc_task_shutdown(task);
		RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
	}
239
240
241
242
243
244
245
246
}

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;
247
	char *textmsg = NULL;
248
249
	isc_result_t result;

250
251
	recvs--;

252
	if (ccmsg.result == ISC_R_EOF)
253
		fatal("connection to remote host closed\n"
254
255
256
257
		      "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
258
		      "* the clocks are not synchronized, or\n"
259
		      "* the key is invalid.");
260
261
262
263
264
265

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

267
268
	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
269

270
271
272
273
274
275
276
277
278
279
280
281
282
	data = isccc_alist_lookup(response, "_data");
	if (data == NULL)
		fatal("no data section in response");
	result = isccc_cc_lookupstring(data, "err", &errormsg);
	if (result == ISC_R_SUCCESS) {
		failed = ISC_TRUE;
		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));

283
	result = isccc_cc_lookupstring(data, "text", &textmsg);
284
	if (result == ISC_R_SUCCESS) {
Evan Hunt's avatar
Evan Hunt committed
285
286
		if ((!quiet || failed) && strlen(textmsg) != 0U)
			fprintf(failed ? stderr : stdout, "%s\n", textmsg);
287
	} else if (result != ISC_R_NOTFOUND)
288
289
290
		fprintf(stderr, "%s: parsing response failed: %s\n",
			progname, isc_result_totext(result));

291
292
	isc_event_free(&event);
	isccc_sexpr_free(&response);
293
294
295
296
297
	if (sends == 0 && recvs == 0) {
		isc_socket_detach(&sock);
		isc_task_shutdown(task);
		RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
	}
298
299
}

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
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;
	isc_uint32_t nonce;
	isccc_sexpr_t *request = NULL;
	isccc_time_t now;
	isc_region_t r;
	isccc_sexpr_t *data;
	isccc_region_t message;
	isc_uint32_t len;
	isc_buffer_t b;

	recvs--;

	if (ccmsg.result == ISC_R_EOF)
		fatal("connection to remote host closed\n"
319
320
321
322
		      "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"
323
324
		      "* the clocks are not synchronized,\n"
		      "* the the key signing algorithm is incorrect, or\n"
325
		      "* the key is invalid.");
326
327
328
329
330
331
332

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

333
334
	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360

	_ctrl = isccc_alist_lookup(response, "_ctrl");
	if (_ctrl == NULL)
		fatal("_ctrl section missing");
	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");
	}
	message.rstart = databuf + 4;
	message.rend = databuf + sizeof(databuf);
361
362
	DO("render message",
	   isccc_cc_towire(request, &message, algorithm, &secret));
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
	len = sizeof(databuf) - REGION_SIZE(message);
	isc_buffer_init(&b, databuf, 4);
	isc_buffer_putuint32(&b, len - 4);
	r.length = len;
	r.base = databuf;

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

382
383
static void
rndc_connected(isc_task_t *task, isc_event_t *event) {
384
	char socktext[ISC_SOCKADDR_FORMATSIZE];
385
386
387
388
389
390
391
392
393
394
	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
	isccc_sexpr_t *request = NULL;
	isccc_sexpr_t *data;
	isccc_time_t now;
	isccc_region_t message;
	isc_region_t r;
	isc_uint32_t len;
	isc_buffer_t b;
	isc_result_t result;

395
396
	connects--;

397
	if (sevent->result != ISC_R_SUCCESS) {
398
399
		isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
				    sizeof(socktext));
400
		if (sevent->result != ISC_R_CANCELED &&
401
		    ++currentaddr < nserveraddrs)
402
		{
403
			notify("connection failed: %s: %s", socktext,
404
			       isc_result_totext(sevent->result));
405
			isc_socket_detach(&sock);
406
			isc_event_free(&event);
407
			rndc_startconnect(&serveraddrs[currentaddr], task);
408
409
			return;
		} else
410
			fatal("connect failed: %s: %s", socktext,
411
412
			      isc_result_totext(sevent->result));
	}
413
414

	isc_stdtime_get(&now);
415
	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
416
417
418
419
						    now, now + 60, &request));
	data = isccc_alist_lookup(request, "_data");
	if (data == NULL)
		fatal("_data section missing");
420
	if (isccc_cc_definestring(data, "type", "null") == NULL)
421
422
423
		fatal("out of memory");
	message.rstart = databuf + 4;
	message.rend = databuf + sizeof(databuf);
424
425
	DO("render message",
	   isccc_cc_towire(request, &message, algorithm, &secret));
426
427
428
429
430
431
432
	len = sizeof(databuf) - REGION_SIZE(message);
	isc_buffer_init(&b, databuf, 4);
	isc_buffer_putuint32(&b, len - 4);
	r.length = len;
	r.base = databuf;

	isccc_ccmsg_init(mctx, sock, &ccmsg);
433
	isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024);
434
435

	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
436
						    rndc_recvnonce, NULL));
437
	recvs++;
438
439
	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
					   NULL));
440
	sends++;
441
442
443
444
	isc_event_free(&event);
}

static void
445
rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
446
	isc_result_t result;
447
448
	int pf;
	isc_sockettype_t type;
449

450
	char socktext[ISC_SOCKADDR_FORMATSIZE];
451

452
	isc_sockaddr_format(addr, socktext, sizeof(socktext));
453
454
455

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

456
457
458
459
460
461
	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));
462
463
	switch (isc_sockaddr_pf(addr)) {
	case AF_INET:
464
		DO("bind socket", isc_socket_bind(sock, &local4, 0));
465
466
		break;
	case AF_INET6:
467
		DO("bind socket", isc_socket_bind(sock, &local6, 0));
468
469
470
471
		break;
	default:
		break;
	}
472
	DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
473
					 NULL));
474
	connects++;
475
476
}

477
478
479
480
481
static void
rndc_start(isc_task_t *task, isc_event_t *event) {
	isc_event_free(&event);

	currentaddr = 0;
482
	rndc_startconnect(&serveraddrs[currentaddr], task);
483
484
}

485
static void
Mark Andrews's avatar
Mark Andrews committed
486
487
parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
	     cfg_parser_t **pctxp, cfg_obj_t **configp)
488
489
490
{
	isc_result_t result;
	const char *conffile = admin_conffile;
491
492
493
494
495
496
497
498
499
500
	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
501
	cfg_obj_t *config = NULL;
502
503
	const cfg_obj_t *address = NULL;
	const cfg_listelt_t *elt;
Mark Andrews's avatar
Mark Andrews committed
504
	const char *secretstr;
505
	const char *algorithmstr;
Mark Andrews's avatar
Mark Andrews committed
506
507
508
	static char secretarray[1024];
	const cfg_type_t *conftype = &cfg_type_rndcconf;
	isc_boolean_t key_only = ISC_FALSE;
509
	const cfg_listelt_t *element;
510

511
	if (! isc_file_exists(conffile)) {
Mark Andrews's avatar
Mark Andrews committed
512
513
		conffile = admin_keyfile;
		conftype = &cfg_type_rndckey;
514

515
516
517
		if (c_flag)
			fatal("%s does not exist", admin_conffile);

518
		if (! isc_file_exists(conffile))
519
			fatal("neither %s nor %s was found",
Mark Andrews's avatar
Mark Andrews committed
520
521
			      admin_conffile, admin_keyfile);
		key_only = ISC_TRUE;
522
523
524
525
	} 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);
526
527
528
529
530
531
532
	}

	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
533
	result = cfg_parse_file(*pctxp, conffile, conftype, &config);
534
535
	if (result != ISC_R_SUCCESS)
		fatal("could not load rndc configuration");
536

Mark Andrews's avatar
Mark Andrews committed
537
538
539
540
	if (!key_only)
		(void)cfg_map_get(config, "options", &options);

	if (key_only && servername == NULL)
541
		servername = "127.0.0.1";
Mark Andrews's avatar
Mark Andrews committed
542
	else if (servername == NULL && options != NULL) {
543
		const cfg_obj_t *defserverobj = NULL;
Mark Andrews's avatar
Mark Andrews committed
544
545
546
547
548
549
550
551
552
		(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) {
553
		(void)cfg_map_get(config, "server", &servers);
Mark Andrews's avatar
Mark Andrews committed
554
555
		if (servers != NULL) {
			for (elt = cfg_list_first(servers);
Automatic Updater's avatar
Automatic Updater committed
556
			     elt != NULL;
Mark Andrews's avatar
Mark Andrews committed
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
			     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
592
		     elt != NULL;
Mark Andrews's avatar
Mark Andrews committed
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
		     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);
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
	algorithmstr = cfg_obj_asstring(algorithmobj);

	if (strcasecmp(algorithmstr, "hmac-md5") == 0)
		algorithm = ISCCC_ALG_HMACMD5;
	else if (strcasecmp(algorithmstr, "hmac-sha1") == 0)
		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
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640

	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)
641
			(void)cfg_map_get(options, "default-port", &defport);
Mark Andrews's avatar
Mark Andrews committed
642
643
644
645
	}
	if (defport != NULL) {
		remoteport = cfg_obj_asuint32(defport);
		if (remoteport > 65535 || remoteport == 0)
646
			fatal("port %u out of range", remoteport);
Mark Andrews's avatar
Mark Andrews committed
647
648
649
	} else if (remoteport == 0)
		remoteport = NS_CONTROL_PORT;

650
651
652
653
654
655
656
657
658
659
660
	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;

661
			address = cfg_listelt_value(element);
662
663
664
			if (!cfg_obj_issockaddr(address)) {
				unsigned int myport;
				const char *name;
665
				const cfg_obj_t *obj;
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681

				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);
					if (myport > ISC_UINT16_MAX ||
					    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
682
						"%s: dropped\n", name);
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
				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);
			}
		}
	}
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735

	if (!local4set && server != NULL) {
		address = NULL;
		cfg_map_get(server, "source-address", &address);
		if (address != NULL) {
			local4 = *cfg_obj_assockaddr(address);
			local4set = ISC_TRUE;
		}
	}
	if (!local4set && options != NULL) {
		address = NULL;
		cfg_map_get(options, "default-source-address", &address);
		if (address != NULL) {
			local4 = *cfg_obj_assockaddr(address);
			local4set = ISC_TRUE;
		}
	}

	if (!local6set && server != NULL) {
		address = NULL;
		cfg_map_get(server, "source-address-v6", &address);
		if (address != NULL) {
			local6 = *cfg_obj_assockaddr(address);
			local6set = ISC_TRUE;
		}
	}
	if (!local6set && options != NULL) {
		address = NULL;
		cfg_map_get(options, "default-source-address-v6", &address);
		if (address != NULL) {
			local6 = *cfg_obj_assockaddr(address);
			local6set = ISC_TRUE;
		}
	}

Mark Andrews's avatar
Mark Andrews committed
736
	*configp = config;
737
738
}

739
740
int
main(int argc, char **argv) {
741
	isc_result_t result = ISC_R_SUCCESS;
Evan Hunt's avatar
Evan Hunt committed
742
	isc_boolean_t show_final_mem = ISC_FALSE;
743
	isc_taskmgr_t *taskmgr = NULL;
744
	isc_task_t *task = NULL;
745
746
747
748
749
	isc_log_t *log = NULL;
	isc_logconfig_t *logconfig = NULL;
	isc_logdestination_t logdest;
	cfg_parser_t *pctx = NULL;
	cfg_obj_t *config = NULL;
750
	const char *keyname = NULL;
751
752
	struct in_addr in;
	struct in6_addr in6;
753
	char *p;
754
	size_t argslen;
755
	int ch;
756
	int i;
757

758
	result = isc_file_progname(*argv, program, sizeof(program));
759
	if (result != ISC_R_SUCCESS)
760
		memmove(program, "rndc", 5);
761
	progname = program;
762

Mark Andrews's avatar
Mark Andrews committed
763
764
765
	admin_conffile = RNDC_CONFFILE;
	admin_keyfile = RNDC_KEYFILE;

766
767
768
	isc_sockaddr_any(&local4);
	isc_sockaddr_any6(&local6);

769
770
771
	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
772

773
774
	isc_commandline_errprint = ISC_FALSE;

Evan Hunt's avatar
Evan Hunt committed
775
	while ((ch = isc_commandline_parse(argc, argv, "b:c:hk:Mmp:qs:Vy:"))
776
	       != -1) {
777
		switch (ch) {
778
779
780
781
782
783
784
785
786
787
788
789
		case 'b':
			if (inet_pton(AF_INET, isc_commandline_argument,
				      &in) == 1) {
				isc_sockaddr_fromin(&local4, &in, 0);
				local4set = ISC_TRUE;
			} else if (inet_pton(AF_INET6, isc_commandline_argument,
					     &in6) == 1) {
				isc_sockaddr_fromin6(&local6, &in6, 0);
				local6set = ISC_TRUE;
			}
			break;

790
		case 'c':
791
			admin_conffile = isc_commandline_argument;
792
			c_flag = ISC_TRUE;
793
794
			break;

Mark Andrews's avatar
Mark Andrews committed
795
796
797
798
		case 'k':
			admin_keyfile = isc_commandline_argument;
			break;

799
		case 'M':
800
			isc_mem_debugging = ISC_MEM_DEBUGTRACE;
801
802
			break;

803
804
805
806
807
		case 'm':
			show_final_mem = ISC_TRUE;
			break;

		case 'p':
808
			remoteport = atoi(isc_commandline_argument);
Mark Andrews's avatar
Mark Andrews committed
809
			if (remoteport > 65535 || remoteport == 0)
810
811
				fatal("port '%s' out of range",
				      isc_commandline_argument);
812
813
			break;

Evan Hunt's avatar
Evan Hunt committed
814
815
816
817
		case 'q':
			quiet = ISC_TRUE;
			break;

818
819
820
		case 's':
			servername = isc_commandline_argument;
			break;
821

822
		case 'V':
823
824
			verbose = ISC_TRUE;
			break;
825

826
827
828
		case 'y':
			keyname = isc_commandline_argument;
			break;
Automatic Updater's avatar
Automatic Updater committed
829

830
		case '?':
831
832
833
834
835
			if (isc_commandline_option != '?') {
				fprintf(stderr, "%s: invalid argument -%c\n",
					program, isc_commandline_option);
				usage(1);
			}
Evan Hunt's avatar
Evan Hunt committed
836
			/* FALLTHROUGH */
837
		case 'h':
838
			usage(0);
839
840
			break;
		default:
841
842
			fprintf(stderr, "%s: unhandled option -%c\n",
				program, isc_commandline_option);
Automatic Updater's avatar
Automatic Updater committed
843
			exit(1);
844
845
846
847
848
849
		}
	}

	argc -= isc_commandline_index;
	argv += isc_commandline_index;

850
851
	if (argc < 1)
		usage(1);
852

853
854
	isc_random_get(&serial);

855
	DO("create memory context", isc_mem_create(0, 0, &mctx));
856
857
	DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
	DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
858
	DO("create task", isc_task_create(taskmgr, 0, &task));
859

860
861
862
863
864
865
866
867
868
	DO("create logging context", isc_log_create(mctx, &log, &logconfig));
	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
869
				 ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
870
871
872
				 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
	DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
						      NULL, NULL));
873

Mark Andrews's avatar
Mark Andrews committed
874
	parse_config(mctx, log, keyname, &pctx, &config);
875

876
	isccc_result_register();
877

878
	command = *argv;
879

880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
	/*
	 * 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;

	args = isc_mem_get(mctx, argslen);
	if (args == NULL)
		DO("isc_mem_get", ISC_R_NOMEMORY);

	p = args;
	for (i = 0; i < argc; i++) {
		size_t len = strlen(argv[i]);
896
		memmove(p, argv[i], len);
897
898
899
		p += len;
		*p++ = ' ';
	}
900

901
902
903
	p--;
	*p++ = '\0';
	INSIST(p == args + argslen);
904

Mark Andrews's avatar
Mark Andrews committed
905
	notify("%s", command);
906

907
	if (strcmp(command, "restart") == 0)
908
		fatal("'%s' is not implemented", command);
909

910
911
912
	if (nserveraddrs == 0)
		get_addresses(servername, (in_port_t) remoteport);

913
	DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL));
914

915
916
917
	result = isc_app_run();
	if (result != ISC_R_SUCCESS)
		fatal("isc_app_run() failed: %s", isc_result_totext(result));
918

919
	if (connects > 0 || sends > 0 || recvs > 0)
920
		isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
921

922
	isc_task_detach(&task);
923
	isc_taskmgr_destroy(&taskmgr);
924
	isc_socketmgr_destroy(&socketmgr);
925
926
	isc_log_destroy(&log);
	isc_log_setcontext(NULL);
927

928
929
930
931
932
933
	cfg_obj_destroy(pctx, &config);
	cfg_parser_destroy(&pctx);

	isc_mem_put(mctx, args, argslen);
	isccc_ccmsg_invalidate(&ccmsg);

Mark Andrews's avatar
Mark Andrews committed
934
935
	dns_name_destroy();

936
937
	if (show_final_mem)
		isc_mem_stats(mctx, stderr);
938

939
	isc_mem_destroy(&mctx);
940

941
942
	if (failed)
		return (1);
943
944
945

	return (0);
}