rndc.c 14.4 KB
Newer Older
1
/*
Brian Wellington's avatar
Brian Wellington committed
2
 * Copyright (C) 2000, 2001  Internet Software Consortium.
3
 *
4 5 6
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
7
 *
8 9 10 11 12 13 14 15
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM 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
/* $Id: rndc.c,v 1.44 2001/03/27 00:44:42 bwelling Exp $ */
19

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

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

26
#include <stdlib.h>
27
#include <netdb.h>
28

29
#include <isc/app.h>
30
#include <isc/buffer.h>
31
#include <isc/commandline.h>
32
#include <isc/log.h>
33
#include <isc/mem.h>
34
#include <isc/socket.h>
35
#include <isc/stdtime.h>
36
#include <isc/string.h>
37
#include <isc/task.h>
38 39
#include <isc/util.h>

40
#include <isccfg/cfg.h>
41

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#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>

#include <named/control.h>

#ifdef HAVE_ADDRINFO
#ifdef HAVE_GETADDRINFO
#ifdef HAVE_GAISTRERROR
#define USE_GETADDRINFO
#endif
#endif
#endif

#ifndef USE_GETADDRINFO
extern int h_errno;
#endif
64

65
static const char *progname;
66 67
static const char *conffile = RNDC_SYSCONFDIR "/rndc.conf";
static const char *version = VERSION;
68 69 70 71 72 73 74 75
static unsigned int remoteport = NS_CONTROL_PORT;
static const char *servername = NULL;
static isc_socketmgr_t *socketmgr = NULL;
static unsigned char databuf[2048];
static isccc_ccmsg_t ccmsg;
static char *args;
static isc_boolean_t have_ipv4, have_ipv6;
static isccc_region_t secret;
76
static isc_boolean_t verbose;
77
static isc_boolean_t failed = ISC_FALSE;
78
static isc_mem_t *mctx;
79
char *command;
80

81 82 83 84 85 86 87 88 89 90 91 92
static void
notify(const char *fmt, ...) {
	va_list ap;

	if (verbose) {
		va_start(ap, fmt);
		vfprintf(stderr, fmt, ap);
		va_end(ap);
		fputs("\n", stderr);
	}
}

93 94 95
static void
usage(void) {
	fprintf(stderr, "\
96 97
Usage: %s [-c config] [-s server] [-p port] [-y key] [-z zone] [-v view]\n\
	command [command ...]\n\
98
\n\
99
command is one of the following:\n\
100
\n\
101 102 103 104 105 106 107
  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\
  stats		Write server statistics to the statistics file.\n\
  querylog	Toggle query logging.\n\
Andreas Gustafsson's avatar
Andreas Gustafsson committed
108
  dumpdb	Dump cache(s) to the dump file (named_dump.db).\n\
109 110
  stop		Save pending updates to master files and stop the server.\n\
  halt		Stop the server without saving pending updates.\n\
111
  trace		Increment debugging level by one.\n\
112
  trace level	Change the debugging level.\n\
113
  notrace	Set debugging level to 0.\n\
114 115 116
  *status	Display ps(1) status of named.\n\
  *restart	Restart the server.\n\
\n\
117 118 119
* == not yet implemented\n\
Version: %s\n",
		progname, version);
120 121
}

122 123 124 125 126 127 128 129 130 131 132 133 134
static void            
fatal(const char *format, ...) {
	va_list args;

	fprintf(stderr, "%s: ", progname);
	va_start(args, format);
	vfprintf(stderr, format, args);
	va_end(args);
	fprintf(stderr, "\n");
	exit(1);
}               


135 136 137
#undef DO
#define DO(name, function) \
	do { \
138 139 140 141 142 143 144
		result = function; \
		if (result != ISC_R_SUCCESS) { \
			fprintf(stderr, "%s: %s: %s\n", progname, \
				name, isc_result_totext(result)); \
			exit(1); \
		} else \
			notify(name); \
145 146
	} while (0)

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316

static void
get_address(const char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
	struct in_addr in4;
	struct in6_addr in6;
#ifdef USE_GETADDRINFO
	struct addrinfo *res = NULL, hints;
	int result;
#else
	struct hostent *he;
#endif

	/*
	 * Assume we have v4 if we don't have v6, since setup_libs
	 * fatal()'s out if we don't have either.
	 */
	if (have_ipv6 && inet_pton(AF_INET6, host, &in6) == 1)
		isc_sockaddr_fromin6(sockaddr, &in6, port);
	else if (inet_pton(AF_INET, host, &in4) == 1)
		isc_sockaddr_fromin(sockaddr, &in4, port);
	else {
#ifdef USE_GETADDRINFO
		memset(&hints, 0, sizeof(hints));
		if (!have_ipv6)
			hints.ai_family = PF_INET;
		else if (!have_ipv4)
			hints.ai_family = PF_INET6;
		else
			hints.ai_family = PF_UNSPEC;
		isc_app_block();
		result = getaddrinfo(host, NULL, &hints, &res);
		isc_app_unblock();
		if (result != 0)
			fatal("Couldn't find server '%s': %s",
			      host, gai_strerror(result));
		memcpy(&sockaddr->type.sa,res->ai_addr, res->ai_addrlen);
		sockaddr->length = res->ai_addrlen;
		isc_sockaddr_setport(sockaddr, port);
		freeaddrinfo(res);
#else
		isc_app_block();
		he = gethostbyname(host);
		isc_app_unblock();
		if (he == NULL)
			fatal("Couldn't find server '%s' (h_errno=%d)",
			      host, h_errno);
		INSIST(he->h_addrtype == AF_INET);
		isc_sockaddr_fromin(sockaddr,
				    (struct in_addr *)(he->h_addr_list[0]),
				    port);
#endif
	}
}

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

	UNUSED(task);

	if (sevent->result != ISC_R_SUCCESS)
		fatal("send failed: %s", isc_result_totext(sevent->result));
	isc_event_free(&event);
}

static void
rndc_recvdone(isc_task_t *task, isc_event_t *event) {
	isc_socket_t *sock = ccmsg.sock;
	isccc_sexpr_t *response = NULL;
	isccc_sexpr_t *data;
	isccc_region_t source;
	char *errormsg = NULL;
	isc_result_t result;

	if (ccmsg.result == ISC_R_EOF) {
		fprintf(stderr, "%s: connection to remote host closed\n",
			progname);
		fprintf(stderr,
			"This may indicate that the remote server is using "
			"an older version of the\n"
			"command protocol or this host is not authorized "
			"to connect.\n");
		exit(1);
	}

	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);
	DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
	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));

	isc_event_free(&event);
	isccc_sexpr_free(&response);
	isc_socket_detach(&sock);
	isc_task_shutdown(task);
	isc_app_shutdown();
}

static void
rndc_connected(isc_task_t *task, isc_event_t *event) {
	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
	isc_socket_t *sock = event->ev_sender;
	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;

	if (sevent->result != ISC_R_SUCCESS)
		fatal("connect failed: %s", isc_result_totext(sevent->result));

	isc_stdtime_get(&now);
	DO("create message", isccc_cc_createmessage(1, NULL, NULL, random(),
						    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");
	message.rstart = databuf + 4;
	message.rend = databuf + sizeof(databuf);
	DO("render message", isccc_cc_towire(request, &message, &secret));
	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);

	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
						    rndc_recvdone, NULL));
	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
					   NULL));
	isc_event_free(&event);
	
}

static void
rndc_start(isc_task_t *task, isc_event_t *event) {
	isc_sockaddr_t addr;
	isc_socket_t *sock = NULL;
	isc_result_t result;

	isc_event_free(&event);

	get_address(servername, remoteport, &addr);
	DO("create socket", isc_socket_create(socketmgr,
					      isc_sockaddr_pf(&addr),
					      isc_sockettype_tcp, &sock));
	DO("connect", isc_socket_connect(sock, &addr, task, rndc_connected,
					 NULL));
}

317 318
int
main(int argc, char **argv) {
319 320
	isc_boolean_t show_final_mem = ISC_FALSE;
	isc_result_t result = ISC_R_SUCCESS;
321
	isc_taskmgr_t *taskmgr = NULL;
322
	isc_task_t *task = NULL;
323 324 325 326 327 328 329 330 331 332 333 334 335 336
	isc_log_t *log = NULL;
	isc_logconfig_t *logconfig = NULL;
	isc_logdestination_t logdest;
	cfg_parser_t *pctx = NULL;
	cfg_obj_t *config = NULL;
	cfg_obj_t *options = NULL;
	cfg_obj_t *servers = NULL;
	cfg_obj_t *server = NULL;
	cfg_obj_t *defkey = NULL;
	cfg_obj_t *keys = NULL;
	cfg_obj_t *key = NULL;
	cfg_obj_t *secretobj = NULL;
	cfg_obj_t *algorithmobj = NULL;
	cfg_listelt_t *elt;
337
	const char *keyname = NULL;
338
	const char *secretstr;
339 340
	const char *algorithm;
	char secretarray[1024];
341
	char *p;
342
	size_t argslen;
343
	int ch;
344
	int i;
345

346 347
	isc_app_start();

348 349 350 351 352 353
	progname = strrchr(*argv, '/');
	if (progname != NULL)
		progname++;
	else
		progname = *argv;

354
	while ((ch = isc_commandline_parse(argc, argv, "c:Mmp:s:Vy:"))
355
	       != -1) {
356
		switch (ch) {
357 358 359 360
		case 'c':
			conffile = isc_commandline_argument;
			break;

361
		case 'M':
362
			isc_mem_debugging = 1;
363 364
			break;

365 366 367 368 369
		case 'm':
			show_final_mem = ISC_TRUE;
			break;

		case 'p':
370 371
			remoteport = atoi(isc_commandline_argument);
			if (remoteport > 65535) {
372 373 374 375 376 377
				fprintf(stderr, "%s: port out of range\n",
					progname);
				exit(1);
			}
			break;

378 379 380
		case 's':
			servername = isc_commandline_argument;
			break;
381
		case 'V':
382 383
			verbose = ISC_TRUE;
			break;
384 385 386
		case 'y':
			keyname = isc_commandline_argument;
			break;
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
		case '?':
			usage();
			exit(1);
			break;
		default:
			fprintf(stderr, "%s: unexpected error parsing "
				"command arguments: got %c\n", progname, ch);
			exit(1);
			break;
		}
	}

	argc -= isc_commandline_index;
	argv += isc_commandline_index;

402
	if (argc < 1) {
403 404 405 406 407
		usage();
		exit(1);
	}

	DO("create memory context", isc_mem_create(0, 0, &mctx));
408 409
	DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
	DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
410
	DO("create task", isc_task_create(taskmgr, 0, &task));
411

412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
	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",
		   		 ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
				 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
	DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
						      NULL, NULL));
	DO("create parser", cfg_parser_create(mctx, log, &pctx));
	result = cfg_parse_file(pctx, conffile, &cfg_type_rndcconf, &config);
	if (result != ISC_R_SUCCESS)
		exit(1);
429

430
	(void)cfg_map_get(config, "options", &options);
431

432 433 434 435 436 437
	if (servername == NULL && options != NULL) {
		cfg_obj_t *defserverobj = NULL;
		(void)cfg_map_get(options, "default-server", &defserverobj);
		if (defserverobj != NULL)
			servername = cfg_obj_asstring(defserverobj);
	}
438

439
	if (servername == NULL) {
440 441
		fprintf(stderr, "%s: no server specified and no default\n",
			progname);
442
		exit(1);
443 444
	}

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
	cfg_map_get(config, "server", &servers);
	if (servers != NULL) {
		for (elt = cfg_list_first(servers);
		     elt != NULL; 
		     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;
		}
	}

460 461 462 463 464
	/*
	 * Look for the name of the key to use.
	 */
	if (keyname != NULL)
		;		/* Was set on command line, do nothing. */
465
	else if (server != NULL) {
466
		DO("get key for server", cfg_map_get(server, "key", &defkey));
467 468
		keyname = cfg_obj_asstring(defkey);
	} else if (options != NULL) {
469 470
		DO("get default key", cfg_map_get(options, "default-key",
						  &defkey));
471
		keyname = cfg_obj_asstring(defkey);
472
	} else {
473 474 475 476 477
		fprintf(stderr, "%s: no key for server and no default\n",
			progname);
		exit(1);
	}

478 479 480
	/*
	 * Get the key's definition.
	 */
481 482 483 484 485 486 487 488 489 490 491
	DO("get config key list", cfg_map_get(config, "key", &keys));
	for (elt = cfg_list_first(keys);
	     elt != NULL; 
	     elt = cfg_list_next(elt))
	{
		key = cfg_listelt_value(elt);
		if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
			       keyname) == 0)
			break;
		key = NULL;
	}
492

493 494 495 496 497 498 499
	(void)cfg_map_get(key, "secret", &secretobj);
	(void)cfg_map_get(key, "algorithm", &algorithmobj);
	if (secretobj == NULL || algorithmobj == NULL) {
		fprintf(stderr, "%s: key must have algorithm and secret\n",
			progname);
		exit(1);
	}
500
	secretstr = cfg_obj_asstring(secretobj);
501
	algorithm = cfg_obj_asstring(algorithmobj);
502

503
	if (strcasecmp(algorithm, "hmac-md5") != 0) {
504
		fprintf(stderr, "%s: unsupported algorithm: %s\n",
505
			progname, algorithm);
506 507 508
		exit(1);
	}

509 510 511 512 513
	secret.rstart = secretarray;
	secret.rend = secretarray + sizeof(secretarray);
	DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
	secret.rend = secret.rstart;
	secret.rstart = secretarray;
514

515
	isccc_result_register();
516

517 518
	have_ipv4 = (isc_net_probeipv4() == ISC_R_SUCCESS);
	have_ipv6 = (isc_net_probeipv6() == ISC_R_SUCCESS);
519

520
	command = *argv;
521

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
	/*
	 * 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]);
		memcpy(p, argv[i], len);
		p += len;
		*p++ = ' ';
	}
542

543 544 545
	p--;
	*p++ = '\0';
	INSIST(p == args + argslen);
546

547
	notify(command);
548

549 550
	if (strcmp(command, "restart") == 0 || strcmp(command, "status") == 0)
		fatal("%s: '%s' is not implemented", progname, command);
551

552
	DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL));
553

554
	isc_app_run();
555

556
	isc_mem_put(mctx, args, argslen);
557

558 559
	cfg_obj_destroy(pctx, &config);
	cfg_parser_destroy(&pctx);
560

561
	isccc_ccmsg_invalidate(&ccmsg);
562 563

	isc_socketmgr_destroy(&socketmgr);
564
	isc_task_detach(&task);
565
	isc_taskmgr_destroy(&taskmgr);
566 567
	isc_log_destroy(&log);
	isc_log_setcontext(NULL);
568

569 570
	if (show_final_mem)
		isc_mem_stats(mctx, stderr);
571

572
	isc_mem_destroy(&mctx);
573

574 575
	if (failed)
		return (1);
576 577 578

	return (0);
}