xref: /minix3/external/bsd/bind/dist/bin/rndc/rndc.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1 /*	$NetBSD: rndc.c,v 1.12 2015/07/08 17:28:55 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004-2015  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 2000-2003  Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*! \file */
21 
22 /*
23  * Principal Author: DCL
24  */
25 
26 #include <config.h>
27 
28 #include <stdlib.h>
29 
30 #include <isc/app.h>
31 #include <isc/buffer.h>
32 #include <isc/commandline.h>
33 #include <isc/file.h>
34 #include <isc/log.h>
35 #include <isc/net.h>
36 #include <isc/mem.h>
37 #include <isc/random.h>
38 #include <isc/socket.h>
39 #include <isc/stdtime.h>
40 #include <isc/string.h>
41 #include <isc/task.h>
42 #include <isc/thread.h>
43 #include <isc/util.h>
44 
45 #include <isccfg/namedconf.h>
46 
47 #include <isccc/alist.h>
48 #include <isccc/base64.h>
49 #include <isccc/cc.h>
50 #include <isccc/ccmsg.h>
51 #include <isccc/result.h>
52 #include <isccc/sexpr.h>
53 #include <isccc/types.h>
54 #include <isccc/util.h>
55 
56 #include <dns/name.h>
57 
58 #include <bind9/getaddresses.h>
59 
60 #include "util.h"
61 
62 #define SERVERADDRS 10
63 
64 const char *progname;
65 isc_boolean_t verbose;
66 
67 static const char *admin_conffile;
68 static const char *admin_keyfile;
69 static const char *version = VERSION;
70 static const char *servername = NULL;
71 static isc_sockaddr_t serveraddrs[SERVERADDRS];
72 static isc_sockaddr_t local4, local6;
73 static isc_boolean_t local4set = ISC_FALSE, local6set = ISC_FALSE;
74 static int nserveraddrs;
75 static int currentaddr = 0;
76 static unsigned int remoteport = 0;
77 static isc_socketmgr_t *socketmgr = NULL;
78 static unsigned char databuf[2048];
79 static isccc_ccmsg_t ccmsg;
80 static isc_uint32_t algorithm;
81 static isccc_region_t secret;
82 static isc_boolean_t failed = ISC_FALSE;
83 static isc_boolean_t c_flag = ISC_FALSE;
84 static isc_mem_t *rndc_mctx;
85 static int sends, recvs, connects;
86 static char *command;
87 static char *args;
88 static char program[256];
89 static isc_socket_t *sock = NULL;
90 static isc_uint32_t serial;
91 static isc_boolean_t quiet = ISC_FALSE;
92 
93 static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);
94 
95 ISC_PLATFORM_NORETURN_PRE static void
96 usage(int status) ISC_PLATFORM_NORETURN_POST;
97 
98 static void
usage(int status)99 usage(int status) {
100 	fprintf(stderr, "\
101 Usage: %s [-b address] [-c config] [-s server] [-p port]\n\
102 	[-k key-file ] [-y key] [-V] command\n\
103 \n\
104 command is one of the following:\n\
105 \n\
106   addzone zone [class [view]] { zone-options }\n\
107 		Add zone to given view. Requires new-zone-file option.\n\
108   delzone [-clean] zone [class [view]]\n\
109 		Removes zone from given view. Requires new-zone-file option.\n\
110   dumpdb [-all|-cache|-zones] [view ...]\n\
111 		Dump cache(s) to the dump file (named_dump.db).\n\
112   flush 	Flushes all of the server's caches.\n\
113   flush [view]	Flushes the server's cache for a view.\n\
114   flushname name [view]\n\
115 		Flush the given name from the server's cache(s)\n\
116   flushtree name [view]\n\
117 		Flush all names under the given name from the server's cache(s)\n\
118   freeze	Suspend updates to all dynamic zones.\n\
119   freeze zone [class [view]]\n\
120 		Suspend updates to a dynamic zone.\n\
121   halt		Stop the server without saving pending updates.\n\
122   halt -p	Stop the server without saving pending updates reporting\n\
123 		process id.\n\
124   loadkeys zone [class [view]]\n\
125 		Update keys without signing immediately.\n\
126   notify zone [class [view]]\n\
127 		Resend NOTIFY messages for the zone.\n\
128   notrace	Set debugging level to 0.\n\
129   querylog newstate\n\
130 		Enable / disable query logging.\n\
131   reconfig	Reload configuration file and new zones only.\n\
132   recursing	Dump the queries that are currently recursing (named.recursing)\n\
133   refresh zone [class [view]]\n\
134 		Schedule immediate maintenance for a zone.\n\
135   reload	Reload configuration file and zones.\n\
136   reload zone [class [view]]\n\
137 		Reload a single zone.\n\
138   retransfer zone [class [view]]\n\
139 		Retransfer a single zone without checking serial number.\n\
140   scan		Scan available network interfaces for changes.\n\
141   secroots [view ...]\n\
142 		Write security roots to the secroots file.\n\
143   sign zone [class [view]]\n\
144 		Update zone keys, and sign as needed.\n\
145   signing -clear all zone [class [view]]\n\
146 		Remove the private records for all keys that have\n\
147 		finished signing the given zone.\n\
148   signing -clear <keyid>/<algorithm> zone [class [view]]\n\
149 		Remove the private record that indicating the given key\n\
150 		has finished signing the given zone.\n\
151   signing -list zone [class [view]]\n\
152 		List the private records showing the state of DNSSEC\n\
153 		signing in the given zone.\n\
154   signing -nsec3param hash flags iterations salt zone [class [view]]\n\
155 		Add NSEC3 chain to zone if already signed.\n\
156 		Prime zone with NSEC3 chain if not yet signed.\n\
157   signing -nsec3param none zone [class [view]]\n\
158 		Remove NSEC3 chains from zone.\n\
159   stats		Write server statistics to the statistics file.\n\
160   status	Display status of the server.\n\
161   stop		Save pending updates to master files and stop the server.\n\
162   stop -p	Save pending updates to master files and stop the server\n\
163 		reporting process id.\n\
164   sync [-clean]	Dump changes to all dynamic zones to disk, and optionally\n\
165 		remove their journal files.\n\
166   sync [-clean] zone [class [view]]\n\
167 		Dump a single zone's changes to disk, and optionally\n\
168 		remove its journal file.\n\
169   thaw		Enable updates to all dynamic zones and reload them.\n\
170   thaw zone [class [view]]\n\
171 		Enable updates to a frozen dynamic zone and reload it.\n\
172   trace		Increment debugging level by one.\n\
173   trace level	Change the debugging level.\n\
174   tsig-delete keyname [view]\n\
175 		Delete a TKEY-negotiated TSIG key.\n\
176   tsig-list	List all currently active TSIG keys, including both statically\n\
177 		configured and TKEY-negotiated keys.\n\
178   validation newstate [view]\n\
179 		Enable / disable DNSSEC validation.\n\
180   zonestatus zone [class [view]]\n\
181 		Display the current status of a zone.\n\
182 \n\
183 Version: %s\n",
184 		progname, version);
185 
186 	exit(status);
187 }
188 
189 static void
get_addresses(const char * host,in_port_t port)190 get_addresses(const char *host, in_port_t port) {
191 	isc_result_t result;
192 	int found = 0, count;
193 
194 	if (*host == '/') {
195 		result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
196 					       host);
197 		if (result == ISC_R_SUCCESS)
198 			nserveraddrs++;
199 	} else {
200 		count = SERVERADDRS - nserveraddrs;
201 		result = bind9_getaddresses(host, port,
202 					    &serveraddrs[nserveraddrs],
203 					    count, &found);
204 		nserveraddrs += found;
205 	}
206 	if (result != ISC_R_SUCCESS)
207 		fatal("couldn't get address for '%s': %s",
208 		      host, isc_result_totext(result));
209 	INSIST(nserveraddrs > 0);
210 }
211 
212 static void
rndc_senddone(isc_task_t * task,isc_event_t * event)213 rndc_senddone(isc_task_t *task, isc_event_t *event) {
214 	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
215 
216 	UNUSED(task);
217 
218 	sends--;
219 	if (sevent->result != ISC_R_SUCCESS)
220 		fatal("send failed: %s", isc_result_totext(sevent->result));
221 	isc_event_free(&event);
222 	if (sends == 0 && recvs == 0) {
223 		isc_socket_detach(&sock);
224 		isc_task_shutdown(task);
225 		RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
226 	}
227 }
228 
229 static void
rndc_recvdone(isc_task_t * task,isc_event_t * event)230 rndc_recvdone(isc_task_t *task, isc_event_t *event) {
231 	isccc_sexpr_t *response = NULL;
232 	isccc_sexpr_t *data;
233 	isccc_region_t source;
234 	char *errormsg = NULL;
235 	char *textmsg = NULL;
236 	isc_result_t result;
237 
238 	recvs--;
239 
240 	if (ccmsg.result == ISC_R_EOF)
241 		fatal("connection to remote host closed\n"
242 		      "This may indicate that\n"
243 		      "* the remote server is using an older version of"
244 		      " the command protocol,\n"
245 		      "* this host is not authorized to connect,\n"
246 		      "* the clocks are not synchronized, or\n"
247 		      "* the key is invalid.");
248 
249 	if (ccmsg.result != ISC_R_SUCCESS)
250 		fatal("recv failed: %s", isc_result_totext(ccmsg.result));
251 
252 	source.rstart = isc_buffer_base(&ccmsg.buffer);
253 	source.rend = isc_buffer_used(&ccmsg.buffer);
254 
255 	DO("parse message",
256 	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
257 
258 	data = isccc_alist_lookup(response, "_data");
259 	if (data == NULL)
260 		fatal("no data section in response");
261 	result = isccc_cc_lookupstring(data, "err", &errormsg);
262 	if (result == ISC_R_SUCCESS) {
263 		failed = ISC_TRUE;
264 		fprintf(stderr, "%s: '%s' failed: %s\n",
265 			progname, command, errormsg);
266 	}
267 	else if (result != ISC_R_NOTFOUND)
268 		fprintf(stderr, "%s: parsing response failed: %s\n",
269 			progname, isc_result_totext(result));
270 
271 	result = isccc_cc_lookupstring(data, "text", &textmsg);
272 	if (result == ISC_R_SUCCESS) {
273 		if ((!quiet || failed) && strlen(textmsg) != 0U)
274 			fprintf(failed ? stderr : stdout, "%s\n", textmsg);
275 	} else if (result != ISC_R_NOTFOUND)
276 		fprintf(stderr, "%s: parsing response failed: %s\n",
277 			progname, isc_result_totext(result));
278 
279 	isc_event_free(&event);
280 	isccc_sexpr_free(&response);
281 	if (sends == 0 && recvs == 0) {
282 		isc_socket_detach(&sock);
283 		isc_task_shutdown(task);
284 		RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
285 	}
286 }
287 
288 static void
rndc_recvnonce(isc_task_t * task,isc_event_t * event)289 rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
290 	isccc_sexpr_t *response = NULL;
291 	isccc_sexpr_t *_ctrl;
292 	isccc_region_t source;
293 	isc_result_t result;
294 	isc_uint32_t nonce;
295 	isccc_sexpr_t *request = NULL;
296 	isccc_time_t now;
297 	isc_region_t r;
298 	isccc_sexpr_t *data;
299 	isccc_region_t message;
300 	isc_uint32_t len;
301 	isc_buffer_t b;
302 
303 	recvs--;
304 
305 	if (ccmsg.result == ISC_R_EOF)
306 		fatal("connection to remote host closed\n"
307 		      "This may indicate that\n"
308 		      "* the remote server is using an older version of"
309 		      " the command protocol,\n"
310 		      "* this host is not authorized to connect,\n"
311 		      "* the clocks are not synchronized,\n"
312 		      "* the the key signing algorithm is incorrect, or\n"
313 		      "* the key is invalid.");
314 
315 	if (ccmsg.result != ISC_R_SUCCESS)
316 		fatal("recv failed: %s", isc_result_totext(ccmsg.result));
317 
318 	source.rstart = isc_buffer_base(&ccmsg.buffer);
319 	source.rend = isc_buffer_used(&ccmsg.buffer);
320 
321 	DO("parse message",
322 	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
323 
324 	_ctrl = isccc_alist_lookup(response, "_ctrl");
325 	if (_ctrl == NULL)
326 		fatal("_ctrl section missing");
327 	nonce = 0;
328 	if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
329 		nonce = 0;
330 
331 	isc_stdtime_get(&now);
332 
333 	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
334 						    now, now + 60, &request));
335 	data = isccc_alist_lookup(request, "_data");
336 	if (data == NULL)
337 		fatal("_data section missing");
338 	if (isccc_cc_definestring(data, "type", args) == NULL)
339 		fatal("out of memory");
340 	if (nonce != 0) {
341 		_ctrl = isccc_alist_lookup(request, "_ctrl");
342 		if (_ctrl == NULL)
343 			fatal("_ctrl section missing");
344 		if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
345 			fatal("out of memory");
346 	}
347 	message.rstart = databuf + 4;
348 	message.rend = databuf + sizeof(databuf);
349 	DO("render message",
350 	   isccc_cc_towire(request, &message, algorithm, &secret));
351 	len = sizeof(databuf) - REGION_SIZE(message);
352 	isc_buffer_init(&b, databuf, 4);
353 	isc_buffer_putuint32(&b, len - 4);
354 	r.length = len;
355 	r.base = databuf;
356 
357 	isccc_ccmsg_cancelread(&ccmsg);
358 	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
359 						    rndc_recvdone, NULL));
360 	recvs++;
361 	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
362 					   NULL));
363 	sends++;
364 
365 	isc_event_free(&event);
366 	isccc_sexpr_free(&response);
367 	return;
368 }
369 
370 static void
rndc_connected(isc_task_t * task,isc_event_t * event)371 rndc_connected(isc_task_t *task, isc_event_t *event) {
372 	char socktext[ISC_SOCKADDR_FORMATSIZE];
373 	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
374 	isccc_sexpr_t *request = NULL;
375 	isccc_sexpr_t *data;
376 	isccc_time_t now;
377 	isccc_region_t message;
378 	isc_region_t r;
379 	isc_uint32_t len;
380 	isc_buffer_t b;
381 	isc_result_t result;
382 
383 	connects--;
384 
385 	if (sevent->result != ISC_R_SUCCESS) {
386 		isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
387 				    sizeof(socktext));
388 		if (sevent->result != ISC_R_CANCELED &&
389 		    ++currentaddr < nserveraddrs)
390 		{
391 			notify("connection failed: %s: %s", socktext,
392 			       isc_result_totext(sevent->result));
393 			isc_socket_detach(&sock);
394 			isc_event_free(&event);
395 			rndc_startconnect(&serveraddrs[currentaddr], task);
396 			return;
397 		} else
398 			fatal("connect failed: %s: %s", socktext,
399 			      isc_result_totext(sevent->result));
400 	}
401 
402 	isc_stdtime_get(&now);
403 	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
404 						    now, now + 60, &request));
405 	data = isccc_alist_lookup(request, "_data");
406 	if (data == NULL)
407 		fatal("_data section missing");
408 	if (isccc_cc_definestring(data, "type", "null") == NULL)
409 		fatal("out of memory");
410 	message.rstart = databuf + 4;
411 	message.rend = databuf + sizeof(databuf);
412 	DO("render message",
413 	   isccc_cc_towire(request, &message, algorithm, &secret));
414 	len = sizeof(databuf) - REGION_SIZE(message);
415 	isc_buffer_init(&b, databuf, 4);
416 	isc_buffer_putuint32(&b, len - 4);
417 	r.length = len;
418 	r.base = databuf;
419 
420 	isccc_ccmsg_init(rndc_mctx, sock, &ccmsg);
421 	isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024);
422 
423 	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
424 						    rndc_recvnonce, NULL));
425 	recvs++;
426 	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
427 					   NULL));
428 	sends++;
429 	isc_event_free(&event);
430 }
431 
432 static void
rndc_startconnect(isc_sockaddr_t * addr,isc_task_t * task)433 rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
434 	isc_result_t result;
435 	int pf;
436 	isc_sockettype_t type;
437 
438 	char socktext[ISC_SOCKADDR_FORMATSIZE];
439 
440 	isc_sockaddr_format(addr, socktext, sizeof(socktext));
441 
442 	notify("using server %s (%s)", servername, socktext);
443 
444 	pf = isc_sockaddr_pf(addr);
445 	if (pf == AF_INET || pf == AF_INET6)
446 		type = isc_sockettype_tcp;
447 	else
448 		type = isc_sockettype_unix;
449 	DO("create socket", isc_socket_create(socketmgr, pf, type, &sock));
450 	switch (isc_sockaddr_pf(addr)) {
451 	case AF_INET:
452 		DO("bind socket", isc_socket_bind(sock, &local4, 0));
453 		break;
454 	case AF_INET6:
455 		DO("bind socket", isc_socket_bind(sock, &local6, 0));
456 		break;
457 	default:
458 		break;
459 	}
460 	DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
461 					 NULL));
462 	connects++;
463 }
464 
465 static void
rndc_start(isc_task_t * task,isc_event_t * event)466 rndc_start(isc_task_t *task, isc_event_t *event) {
467 	isc_event_free(&event);
468 
469 	currentaddr = 0;
470 	rndc_startconnect(&serveraddrs[currentaddr], task);
471 }
472 
473 static void
parse_config(isc_mem_t * mctx,isc_log_t * log,const char * keyname,cfg_parser_t ** pctxp,cfg_obj_t ** configp)474 parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
475 	     cfg_parser_t **pctxp, cfg_obj_t **configp)
476 {
477 	isc_result_t result;
478 	const char *conffile = admin_conffile;
479 	const cfg_obj_t *addresses = NULL;
480 	const cfg_obj_t *defkey = NULL;
481 	const cfg_obj_t *options = NULL;
482 	const cfg_obj_t *servers = NULL;
483 	const cfg_obj_t *server = NULL;
484 	const cfg_obj_t *keys = NULL;
485 	const cfg_obj_t *key = NULL;
486 	const cfg_obj_t *defport = NULL;
487 	const cfg_obj_t *secretobj = NULL;
488 	const cfg_obj_t *algorithmobj = NULL;
489 	cfg_obj_t *config = NULL;
490 	const cfg_obj_t *address = NULL;
491 	const cfg_listelt_t *elt;
492 	const char *secretstr;
493 	const char *algorithmstr;
494 	static char secretarray[1024];
495 	const cfg_type_t *conftype = &cfg_type_rndcconf;
496 	isc_boolean_t key_only = ISC_FALSE;
497 	const cfg_listelt_t *element;
498 
499 	if (! isc_file_exists(conffile)) {
500 		conffile = admin_keyfile;
501 		conftype = &cfg_type_rndckey;
502 
503 		if (c_flag)
504 			fatal("%s does not exist", admin_conffile);
505 
506 		if (! isc_file_exists(conffile))
507 			fatal("neither %s nor %s was found",
508 			      admin_conffile, admin_keyfile);
509 		key_only = ISC_TRUE;
510 	} else if (! c_flag && isc_file_exists(admin_keyfile)) {
511 		fprintf(stderr, "WARNING: key file (%s) exists, but using "
512 			"default configuration file (%s)\n",
513 			admin_keyfile, admin_conffile);
514 	}
515 
516 	DO("create parser", cfg_parser_create(mctx, log, pctxp));
517 
518 	/*
519 	 * The parser will output its own errors, so DO() is not used.
520 	 */
521 	result = cfg_parse_file(*pctxp, conffile, conftype, &config);
522 	if (result != ISC_R_SUCCESS)
523 		fatal("could not load rndc configuration");
524 
525 	if (!key_only)
526 		(void)cfg_map_get(config, "options", &options);
527 
528 	if (key_only && servername == NULL)
529 		servername = "127.0.0.1";
530 	else if (servername == NULL && options != NULL) {
531 		const cfg_obj_t *defserverobj = NULL;
532 		(void)cfg_map_get(options, "default-server", &defserverobj);
533 		if (defserverobj != NULL)
534 			servername = cfg_obj_asstring(defserverobj);
535 	}
536 
537 	if (servername == NULL)
538 		fatal("no server specified and no default");
539 
540 	if (!key_only) {
541 		(void)cfg_map_get(config, "server", &servers);
542 		if (servers != NULL) {
543 			for (elt = cfg_list_first(servers);
544 			     elt != NULL;
545 			     elt = cfg_list_next(elt))
546 			{
547 				const char *name;
548 				server = cfg_listelt_value(elt);
549 				name = cfg_obj_asstring(cfg_map_getname(server));
550 				if (strcasecmp(name, servername) == 0)
551 					break;
552 				server = NULL;
553 			}
554 		}
555 	}
556 
557 	/*
558 	 * Look for the name of the key to use.
559 	 */
560 	if (keyname != NULL)
561 		;		/* Was set on command line, do nothing. */
562 	else if (server != NULL) {
563 		DO("get key for server", cfg_map_get(server, "key", &defkey));
564 		keyname = cfg_obj_asstring(defkey);
565 	} else if (options != NULL) {
566 		DO("get default key", cfg_map_get(options, "default-key",
567 						  &defkey));
568 		keyname = cfg_obj_asstring(defkey);
569 	} else if (!key_only)
570 		fatal("no key for server and no default");
571 
572 	/*
573 	 * Get the key's definition.
574 	 */
575 	if (key_only)
576 		DO("get key", cfg_map_get(config, "key", &key));
577 	else {
578 		DO("get config key list", cfg_map_get(config, "key", &keys));
579 		for (elt = cfg_list_first(keys);
580 		     elt != NULL;
581 		     elt = cfg_list_next(elt))
582 		{
583 			key = cfg_listelt_value(elt);
584 			if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
585 				       keyname) == 0)
586 				break;
587 		}
588 		if (elt == NULL)
589 			fatal("no key definition for name %s", keyname);
590 	}
591 	(void)cfg_map_get(key, "secret", &secretobj);
592 	(void)cfg_map_get(key, "algorithm", &algorithmobj);
593 	if (secretobj == NULL || algorithmobj == NULL)
594 		fatal("key must have algorithm and secret");
595 
596 	secretstr = cfg_obj_asstring(secretobj);
597 	algorithmstr = cfg_obj_asstring(algorithmobj);
598 
599 	if (strcasecmp(algorithmstr, "hmac-md5") == 0)
600 		algorithm = ISCCC_ALG_HMACMD5;
601 	else if (strcasecmp(algorithmstr, "hmac-sha1") == 0)
602 		algorithm = ISCCC_ALG_HMACSHA1;
603 	else if (strcasecmp(algorithmstr, "hmac-sha224") == 0)
604 		algorithm = ISCCC_ALG_HMACSHA224;
605 	else if (strcasecmp(algorithmstr, "hmac-sha256") == 0)
606 		algorithm = ISCCC_ALG_HMACSHA256;
607 	else if (strcasecmp(algorithmstr, "hmac-sha384") == 0)
608 		algorithm = ISCCC_ALG_HMACSHA384;
609 	else if (strcasecmp(algorithmstr, "hmac-sha512") == 0)
610 		algorithm = ISCCC_ALG_HMACSHA512;
611 	else
612 		fatal("unsupported algorithm: %s", algorithmstr);
613 
614 	secret.rstart = (unsigned char *)secretarray;
615 	secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
616 	DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
617 	secret.rend = secret.rstart;
618 	secret.rstart = (unsigned char *)secretarray;
619 
620 	/*
621 	 * Find the port to connect to.
622 	 */
623 	if (remoteport != 0)
624 		;		/* Was set on command line, do nothing. */
625 	else {
626 		if (server != NULL)
627 			(void)cfg_map_get(server, "port", &defport);
628 		if (defport == NULL && options != NULL)
629 			(void)cfg_map_get(options, "default-port", &defport);
630 	}
631 	if (defport != NULL) {
632 		remoteport = cfg_obj_asuint32(defport);
633 		if (remoteport > 65535 || remoteport == 0)
634 			fatal("port %u out of range", remoteport);
635 	} else if (remoteport == 0)
636 		remoteport = NS_CONTROL_PORT;
637 
638 	if (server != NULL)
639 		result = cfg_map_get(server, "addresses", &addresses);
640 	else
641 		result = ISC_R_NOTFOUND;
642 	if (result == ISC_R_SUCCESS) {
643 		for (element = cfg_list_first(addresses);
644 		     element != NULL;
645 		     element = cfg_list_next(element))
646 		{
647 			isc_sockaddr_t sa;
648 
649 			address = cfg_listelt_value(element);
650 			if (!cfg_obj_issockaddr(address)) {
651 				unsigned int myport;
652 				const char *name;
653 				const cfg_obj_t *obj;
654 
655 				obj = cfg_tuple_get(address, "name");
656 				name = cfg_obj_asstring(obj);
657 				obj = cfg_tuple_get(address, "port");
658 				if (cfg_obj_isuint32(obj)) {
659 					myport = cfg_obj_asuint32(obj);
660 					if (myport > ISC_UINT16_MAX ||
661 					    myport == 0)
662 						fatal("port %u out of range",
663 						      myport);
664 				} else
665 					myport = remoteport;
666 				if (nserveraddrs < SERVERADDRS)
667 					get_addresses(name, (in_port_t) myport);
668 				else
669 					fprintf(stderr, "too many address: "
670 						"%s: dropped\n", name);
671 				continue;
672 			}
673 			sa = *cfg_obj_assockaddr(address);
674 			if (isc_sockaddr_getport(&sa) == 0)
675 				isc_sockaddr_setport(&sa, remoteport);
676 			if (nserveraddrs < SERVERADDRS)
677 				serveraddrs[nserveraddrs++] = sa;
678 			else {
679 				char socktext[ISC_SOCKADDR_FORMATSIZE];
680 
681 				isc_sockaddr_format(&sa, socktext,
682 						    sizeof(socktext));
683 				fprintf(stderr,
684 					"too many address: %s: dropped\n",
685 					socktext);
686 			}
687 		}
688 	}
689 
690 	if (!local4set && server != NULL) {
691 		address = NULL;
692 		cfg_map_get(server, "source-address", &address);
693 		if (address != NULL) {
694 			local4 = *cfg_obj_assockaddr(address);
695 			local4set = ISC_TRUE;
696 		}
697 	}
698 	if (!local4set && options != NULL) {
699 		address = NULL;
700 		cfg_map_get(options, "default-source-address", &address);
701 		if (address != NULL) {
702 			local4 = *cfg_obj_assockaddr(address);
703 			local4set = ISC_TRUE;
704 		}
705 	}
706 
707 	if (!local6set && server != NULL) {
708 		address = NULL;
709 		cfg_map_get(server, "source-address-v6", &address);
710 		if (address != NULL) {
711 			local6 = *cfg_obj_assockaddr(address);
712 			local6set = ISC_TRUE;
713 		}
714 	}
715 	if (!local6set && options != NULL) {
716 		address = NULL;
717 		cfg_map_get(options, "default-source-address-v6", &address);
718 		if (address != NULL) {
719 			local6 = *cfg_obj_assockaddr(address);
720 			local6set = ISC_TRUE;
721 		}
722 	}
723 
724 	*configp = config;
725 }
726 
727 int
main(int argc,char ** argv)728 main(int argc, char **argv) {
729 	isc_result_t result = ISC_R_SUCCESS;
730 	isc_boolean_t show_final_mem = ISC_FALSE;
731 	isc_taskmgr_t *taskmgr = NULL;
732 	isc_task_t *task = NULL;
733 	isc_log_t *log = NULL;
734 	isc_logconfig_t *logconfig = NULL;
735 	isc_logdestination_t logdest;
736 	cfg_parser_t *pctx = NULL;
737 	cfg_obj_t *config = NULL;
738 	const char *keyname = NULL;
739 	struct in_addr in;
740 	struct in6_addr in6;
741 	char *p;
742 	size_t argslen;
743 	int ch;
744 	int i;
745 
746 	result = isc_file_progname(*argv, program, sizeof(program));
747 	if (result != ISC_R_SUCCESS)
748 		memmove(program, "rndc", 5);
749 	progname = program;
750 
751 	admin_conffile = RNDC_CONFFILE;
752 	admin_keyfile = RNDC_KEYFILE;
753 
754 	isc_sockaddr_any(&local4);
755 	isc_sockaddr_any6(&local6);
756 
757 	result = isc_app_start();
758 	if (result != ISC_R_SUCCESS)
759 		fatal("isc_app_start() failed: %s", isc_result_totext(result));
760 
761 	isc_commandline_errprint = ISC_FALSE;
762 
763 	while ((ch = isc_commandline_parse(argc, argv, "b:c:hk:Mmp:qs:Vy:"))
764 	       != -1) {
765 		switch (ch) {
766 		case 'b':
767 			if (inet_pton(AF_INET, isc_commandline_argument,
768 				      &in) == 1) {
769 				isc_sockaddr_fromin(&local4, &in, 0);
770 				local4set = ISC_TRUE;
771 			} else if (inet_pton(AF_INET6, isc_commandline_argument,
772 					     &in6) == 1) {
773 				isc_sockaddr_fromin6(&local6, &in6, 0);
774 				local6set = ISC_TRUE;
775 			}
776 			break;
777 
778 		case 'c':
779 			admin_conffile = isc_commandline_argument;
780 			c_flag = ISC_TRUE;
781 			break;
782 
783 		case 'k':
784 			admin_keyfile = isc_commandline_argument;
785 			break;
786 
787 		case 'M':
788 			isc_mem_debugging = ISC_MEM_DEBUGTRACE;
789 			break;
790 
791 		case 'm':
792 			show_final_mem = ISC_TRUE;
793 			break;
794 
795 		case 'p':
796 			remoteport = atoi(isc_commandline_argument);
797 			if (remoteport > 65535 || remoteport == 0)
798 				fatal("port '%s' out of range",
799 				      isc_commandline_argument);
800 			break;
801 
802 		case 'q':
803 			quiet = ISC_TRUE;
804 			break;
805 
806 		case 's':
807 			servername = isc_commandline_argument;
808 			break;
809 
810 		case 'V':
811 			verbose = ISC_TRUE;
812 			break;
813 
814 		case 'y':
815 			keyname = isc_commandline_argument;
816 			break;
817 
818 		case '?':
819 			if (isc_commandline_option != '?') {
820 				fprintf(stderr, "%s: invalid argument -%c\n",
821 					program, isc_commandline_option);
822 				usage(1);
823 			}
824 			/* FALLTHROUGH */
825 		case 'h':
826 			usage(0);
827 			break;
828 		default:
829 			fprintf(stderr, "%s: unhandled option -%c\n",
830 				program, isc_commandline_option);
831 			exit(1);
832 		}
833 	}
834 
835 	argc -= isc_commandline_index;
836 	argv += isc_commandline_index;
837 
838 	if (argc < 1)
839 		usage(1);
840 
841 	isc_random_get(&serial);
842 
843 	DO("create memory context", isc_mem_create(0, 0, &rndc_mctx));
844 	DO("create socket manager", isc_socketmgr_create(rndc_mctx, &socketmgr));
845 	DO("create task manager", isc_taskmgr_create(rndc_mctx, 1, 0, &taskmgr));
846 	DO("create task", isc_task_create(taskmgr, 0, &task));
847 
848 	DO("create logging context", isc_log_create(rndc_mctx, &log, &logconfig));
849 	isc_log_setcontext(log);
850 	DO("setting log tag", isc_log_settag(logconfig, progname));
851 	logdest.file.stream = stderr;
852 	logdest.file.name = NULL;
853 	logdest.file.versions = ISC_LOG_ROLLNEVER;
854 	logdest.file.maximum_size = 0;
855 	DO("creating log channel",
856 	   isc_log_createchannel(logconfig, "stderr",
857 				 ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
858 				 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
859 	DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
860 						      NULL, NULL));
861 
862 	parse_config(rndc_mctx, log, keyname, &pctx, &config);
863 
864 	isccc_result_register();
865 
866 	command = *argv;
867 
868 	/*
869 	 * Convert argc/argv into a space-delimited command string
870 	 * similar to what the user might enter in interactive mode
871 	 * (if that were implemented).
872 	 */
873 	argslen = 0;
874 	for (i = 0; i < argc; i++)
875 		argslen += strlen(argv[i]) + 1;
876 
877 	args = isc_mem_get(rndc_mctx, argslen);
878 	if (args == NULL)
879 		DO("isc_mem_get", ISC_R_NOMEMORY);
880 
881 	p = args;
882 	for (i = 0; i < argc; i++) {
883 		size_t len = strlen(argv[i]);
884 		memmove(p, argv[i], len);
885 		p += len;
886 		*p++ = ' ';
887 	}
888 
889 	p--;
890 	*p++ = '\0';
891 	INSIST(p == args + argslen);
892 
893 	notify("%s", command);
894 
895 	if (strcmp(command, "restart") == 0)
896 		fatal("'%s' is not implemented", command);
897 
898 	if (nserveraddrs == 0)
899 		get_addresses(servername, (in_port_t) remoteport);
900 
901 	DO("post event", isc_app_onrun(rndc_mctx, task, rndc_start, NULL));
902 
903 	result = isc_app_run();
904 	if (result != ISC_R_SUCCESS)
905 		fatal("isc_app_run() failed: %s", isc_result_totext(result));
906 
907 	if (connects > 0 || sends > 0 || recvs > 0)
908 		isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
909 
910 	isc_task_detach(&task);
911 	isc_taskmgr_destroy(&taskmgr);
912 	isc_socketmgr_destroy(&socketmgr);
913 	isc_log_destroy(&log);
914 	isc_log_setcontext(NULL);
915 
916 	cfg_obj_destroy(pctx, &config);
917 	cfg_parser_destroy(&pctx);
918 
919 	isc_mem_put(rndc_mctx, args, argslen);
920 	isccc_ccmsg_invalidate(&ccmsg);
921 
922 	dns_name_destroy();
923 
924 	if (show_final_mem)
925 		isc_mem_stats(rndc_mctx, stderr);
926 
927 	isc_mem_destroy(&rndc_mctx);
928 
929 	if (failed)
930 		return (1);
931 
932 	return (0);
933 }
934