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