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