1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2014  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000-2003  Internet Software Consortium.
4135446Strhodes *
5182645Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18254897Serwin/* $Id$ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes/*
23135446Strhodes * Principal Author: DCL
24135446Strhodes */
25135446Strhodes
26135446Strhodes#include <config.h>
27135446Strhodes
28135446Strhodes#include <stdlib.h>
29135446Strhodes
30135446Strhodes#include <isc/app.h>
31135446Strhodes#include <isc/buffer.h>
32135446Strhodes#include <isc/commandline.h>
33135446Strhodes#include <isc/file.h>
34135446Strhodes#include <isc/log.h>
35170222Sdougb#include <isc/net.h>
36135446Strhodes#include <isc/mem.h>
37135446Strhodes#include <isc/random.h>
38135446Strhodes#include <isc/socket.h>
39135446Strhodes#include <isc/stdtime.h>
40135446Strhodes#include <isc/string.h>
41135446Strhodes#include <isc/task.h>
42135446Strhodes#include <isc/thread.h>
43135446Strhodes#include <isc/util.h>
44135446Strhodes
45135446Strhodes#include <isccfg/namedconf.h>
46135446Strhodes
47135446Strhodes#include <isccc/alist.h>
48135446Strhodes#include <isccc/base64.h>
49135446Strhodes#include <isccc/cc.h>
50135446Strhodes#include <isccc/ccmsg.h>
51135446Strhodes#include <isccc/result.h>
52135446Strhodes#include <isccc/sexpr.h>
53135446Strhodes#include <isccc/types.h>
54135446Strhodes#include <isccc/util.h>
55135446Strhodes
56170222Sdougb#include <dns/name.h>
57170222Sdougb
58135446Strhodes#include <bind9/getaddresses.h>
59135446Strhodes
60135446Strhodes#include "util.h"
61135446Strhodes
62135446Strhodes#define SERVERADDRS 10
63135446Strhodes
64186462Sdougbconst char *progname;
65135446Strhodesisc_boolean_t verbose;
66135446Strhodes
67135446Strhodesstatic const char *admin_conffile;
68135446Strhodesstatic const char *admin_keyfile;
69135446Strhodesstatic const char *version = VERSION;
70135446Strhodesstatic const char *servername = NULL;
71135446Strhodesstatic isc_sockaddr_t serveraddrs[SERVERADDRS];
72170222Sdougbstatic isc_sockaddr_t local4, local6;
73170222Sdougbstatic isc_boolean_t local4set = ISC_FALSE, local6set = ISC_FALSE;
74135446Strhodesstatic int nserveraddrs;
75135446Strhodesstatic int currentaddr = 0;
76135446Strhodesstatic unsigned int remoteport = 0;
77135446Strhodesstatic isc_socketmgr_t *socketmgr = NULL;
78135446Strhodesstatic unsigned char databuf[2048];
79135446Strhodesstatic isccc_ccmsg_t ccmsg;
80135446Strhodesstatic isccc_region_t secret;
81135446Strhodesstatic isc_boolean_t failed = ISC_FALSE;
82224092Sdougbstatic isc_boolean_t c_flag = ISC_FALSE;
83135446Strhodesstatic isc_mem_t *mctx;
84135446Strhodesstatic int sends, recvs, connects;
85135446Strhodesstatic char *command;
86135446Strhodesstatic char *args;
87135446Strhodesstatic char program[256];
88135446Strhodesstatic isc_socket_t *sock = NULL;
89135446Strhodesstatic isc_uint32_t serial;
90135446Strhodes
91135446Strhodesstatic void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);
92135446Strhodes
93224092SdougbISC_PLATFORM_NORETURN_PRE static void
94224092Sdougbusage(int status) ISC_PLATFORM_NORETURN_POST;
95224092Sdougb
96135446Strhodesstatic void
97135446Strhodesusage(int status) {
98135446Strhodes	fprintf(stderr, "\
99224092SdougbUsage: %s [-b address] [-c config] [-s server] [-p port]\n\
100186462Sdougb	[-k key-file ] [-y key] [-V] command\n\
101135446Strhodes\n\
102135446Strhodescommand is one of the following:\n\
103135446Strhodes\n\
104135446Strhodes  reload	Reload configuration file and zones.\n\
105135446Strhodes  reload zone [class [view]]\n\
106135446Strhodes		Reload a single zone.\n\
107135446Strhodes  refresh zone [class [view]]\n\
108135446Strhodes		Schedule immediate maintenance for a zone.\n\
109135446Strhodes  retransfer zone [class [view]]\n\
110135446Strhodes		Retransfer a single zone without checking serial number.\n\
111170222Sdougb  freeze	Suspend updates to all dynamic zones.\n\
112135446Strhodes  freeze zone [class [view]]\n\
113186462Sdougb		Suspend updates to a dynamic zone.\n\
114170222Sdougb  thaw		Enable updates to all dynamic zones and reload them.\n\
115135601Sdes  thaw zone [class [view]]\n\
116186462Sdougb		Enable updates to a frozen dynamic zone and reload it.\n\
117254897Serwin  sync [-clean]	Dump changes to all dynamic zones to disk, and optionally\n\
118254897Serwin		remove their journal files.\n\
119254897Serwin  sync [-clean] zone [class [view]]\n\
120254897Serwin		Dump a single zone's changes to disk, and optionally\n\
121254897Serwin		remove its journal file.\n\
122170222Sdougb  notify zone [class [view]]\n\
123170222Sdougb		Resend NOTIFY messages for the zone.\n\
124135446Strhodes  reconfig	Reload configuration file and new zones only.\n\
125224092Sdougb  sign zone [class [view]]\n\
126224092Sdougb		Update zone keys, and sign as needed.\n\
127224092Sdougb  loadkeys zone [class [view]]\n\
128224092Sdougb		Update keys without signing immediately.\n\
129135446Strhodes  stats		Write server statistics to the statistics file.\n\
130254897Serwin  querylog newstate\n\
131254897Serwin		Enable / disable query logging.\n\
132153816Sdougb  dumpdb [-all|-cache|-zones] [view ...]\n\
133153816Sdougb		Dump cache(s) to the dump file (named_dump.db).\n\
134224092Sdougb  secroots [view ...]\n\
135224092Sdougb		Write security roots to the secroots file.\n\
136135446Strhodes  stop		Save pending updates to master files and stop the server.\n\
137135446Strhodes  stop -p	Save pending updates to master files and stop the server\n\
138135446Strhodes		reporting process id.\n\
139135446Strhodes  halt		Stop the server without saving pending updates.\n\
140135446Strhodes  halt -p	Stop the server without saving pending updates reporting\n\
141135446Strhodes		process id.\n\
142135446Strhodes  trace		Increment debugging level by one.\n\
143135446Strhodes  trace level	Change the debugging level.\n\
144135446Strhodes  notrace	Set debugging level to 0.\n\
145135446Strhodes  flush 	Flushes all of the server's caches.\n\
146135446Strhodes  flush [view]	Flushes the server's cache for a view.\n\
147135446Strhodes  flushname name [view]\n\
148135446Strhodes		Flush the given name from the server's cache(s)\n\
149254897Serwin  flushtree name [view]\n\
150254897Serwin		Flush all names under the given name from the server's cache(s)\n\
151135446Strhodes  status	Display status of the server.\n\
152135446Strhodes  recursing	Dump the queries that are currently recursing (named.recursing)\n\
153234010Sdougb  tsig-list	List all currently active TSIG keys, including both statically\n\
154234010Sdougb		configured and TKEY-negotiated keys.\n\
155234010Sdougb  tsig-delete keyname [view]	\n\
156234010Sdougb		Delete a TKEY-negotiated TSIG key.\n\
157170222Sdougb  validation newstate [view]\n\
158170222Sdougb		Enable / disable DNSSEC validation.\n\
159224092Sdougb  addzone [\"file\"] zone [class [view]] { zone-options }\n\
160224092Sdougb		Add zone to given view. Requires new-zone-file option.\n\
161224092Sdougb  delzone [\"file\"] zone [class [view]]\n\
162224092Sdougb		Removes zone from given view. Requires new-zone-file option.\n\
163254897Serwin  signing -list zone [class [view]]\n\
164254897Serwin		List the private records showing the state of DNSSEC\n\
165254897Serwin		signing in the given zone.\n\
166254897Serwin  signing -clear <keyid>/<algorithm> zone [class [view]]\n\
167254897Serwin		Remove the private record that indicating the given key\n\
168254897Serwin		has finished signing the given zone.\n\
169254897Serwin  signing -clear all zone [class [view]]\n\
170254897Serwin		Remove the private records for all keys that have\n\
171254897Serwin		finished signing the given zone.\n\
172254897Serwin  signing -nsec3param none zone [class [view]]\n\
173254897Serwin		Remove NSEC3 chains from zone.\n\
174254897Serwin  signing -nsec3param hash flags iterations salt zone [class [view]]\n\
175254897Serwin		Add NSEC3 chain to zone if already signed.\n\
176254897Serwin		Prime zone with NSEC3 chain if not yet signed.\n\
177234010Sdougb  *restart	Restart the server.\n\
178135446Strhodes\n\
179135446Strhodes* == not yet implemented\n\
180135446StrhodesVersion: %s\n",
181135446Strhodes		progname, version);
182135446Strhodes
183135446Strhodes	exit(status);
184135446Strhodes}
185135446Strhodes
186135446Strhodesstatic void
187135446Strhodesget_addresses(const char *host, in_port_t port) {
188135446Strhodes	isc_result_t result;
189170222Sdougb	int found = 0, count;
190135446Strhodes
191170222Sdougb	if (*host == '/') {
192170222Sdougb		result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
193170222Sdougb					       host);
194170222Sdougb		if (result == ISC_R_SUCCESS)
195186462Sdougb			nserveraddrs++;
196170222Sdougb	} else {
197170222Sdougb		count = SERVERADDRS - nserveraddrs;
198170222Sdougb		result = bind9_getaddresses(host, port,
199170222Sdougb					    &serveraddrs[nserveraddrs],
200170222Sdougb					    count, &found);
201170222Sdougb		nserveraddrs += found;
202170222Sdougb	}
203135446Strhodes	if (result != ISC_R_SUCCESS)
204135446Strhodes		fatal("couldn't get address for '%s': %s",
205135446Strhodes		      host, isc_result_totext(result));
206135446Strhodes	INSIST(nserveraddrs > 0);
207135446Strhodes}
208135446Strhodes
209135446Strhodesstatic void
210135446Strhodesrndc_senddone(isc_task_t *task, isc_event_t *event) {
211135446Strhodes	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
212135446Strhodes
213135446Strhodes	UNUSED(task);
214135446Strhodes
215135446Strhodes	sends--;
216135446Strhodes	if (sevent->result != ISC_R_SUCCESS)
217135446Strhodes		fatal("send failed: %s", isc_result_totext(sevent->result));
218135446Strhodes	isc_event_free(&event);
219165071Sdougb	if (sends == 0 && recvs == 0) {
220165071Sdougb		isc_socket_detach(&sock);
221165071Sdougb		isc_task_shutdown(task);
222165071Sdougb		RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
223165071Sdougb	}
224135446Strhodes}
225135446Strhodes
226135446Strhodesstatic void
227135446Strhodesrndc_recvdone(isc_task_t *task, isc_event_t *event) {
228135446Strhodes	isccc_sexpr_t *response = NULL;
229135446Strhodes	isccc_sexpr_t *data;
230135446Strhodes	isccc_region_t source;
231135446Strhodes	char *errormsg = NULL;
232135446Strhodes	char *textmsg = NULL;
233135446Strhodes	isc_result_t result;
234135446Strhodes
235135446Strhodes	recvs--;
236135446Strhodes
237135446Strhodes	if (ccmsg.result == ISC_R_EOF)
238135446Strhodes		fatal("connection to remote host closed\n"
239170222Sdougb		      "This may indicate that\n"
240170222Sdougb		      "* the remote server is using an older version of"
241170222Sdougb		      " the command protocol,\n"
242170222Sdougb		      "* this host is not authorized to connect,\n"
243193149Sdougb		      "* the clocks are not synchronized, or\n"
244170222Sdougb		      "* the key is invalid.");
245135446Strhodes
246135446Strhodes	if (ccmsg.result != ISC_R_SUCCESS)
247135446Strhodes		fatal("recv failed: %s", isc_result_totext(ccmsg.result));
248135446Strhodes
249135446Strhodes	source.rstart = isc_buffer_base(&ccmsg.buffer);
250135446Strhodes	source.rend = isc_buffer_used(&ccmsg.buffer);
251135446Strhodes
252135446Strhodes	DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
253135446Strhodes
254135446Strhodes	data = isccc_alist_lookup(response, "_data");
255296611Sdelphij	if (!isccc_alist_alistp(data))
256296611Sdelphij		fatal("bad or missing data section in response");
257135446Strhodes	result = isccc_cc_lookupstring(data, "err", &errormsg);
258135446Strhodes	if (result == ISC_R_SUCCESS) {
259135446Strhodes		failed = ISC_TRUE;
260135446Strhodes		fprintf(stderr, "%s: '%s' failed: %s\n",
261135446Strhodes			progname, command, errormsg);
262135446Strhodes	}
263135446Strhodes	else if (result != ISC_R_NOTFOUND)
264135446Strhodes		fprintf(stderr, "%s: parsing response failed: %s\n",
265135446Strhodes			progname, isc_result_totext(result));
266135446Strhodes
267135446Strhodes	result = isccc_cc_lookupstring(data, "text", &textmsg);
268262706Serwin	if (result == ISC_R_SUCCESS) {
269262706Serwin		if (strlen(textmsg) != 0U)
270262706Serwin			printf("%s\n", textmsg);
271262706Serwin	} else if (result != ISC_R_NOTFOUND)
272135446Strhodes		fprintf(stderr, "%s: parsing response failed: %s\n",
273135446Strhodes			progname, isc_result_totext(result));
274135446Strhodes
275135446Strhodes	isc_event_free(&event);
276135446Strhodes	isccc_sexpr_free(&response);
277165071Sdougb	if (sends == 0 && recvs == 0) {
278165071Sdougb		isc_socket_detach(&sock);
279165071Sdougb		isc_task_shutdown(task);
280165071Sdougb		RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
281165071Sdougb	}
282135446Strhodes}
283135446Strhodes
284135446Strhodesstatic void
285135446Strhodesrndc_recvnonce(isc_task_t *task, isc_event_t *event) {
286135446Strhodes	isccc_sexpr_t *response = NULL;
287135446Strhodes	isccc_sexpr_t *_ctrl;
288135446Strhodes	isccc_region_t source;
289135446Strhodes	isc_result_t result;
290135446Strhodes	isc_uint32_t nonce;
291135446Strhodes	isccc_sexpr_t *request = NULL;
292135446Strhodes	isccc_time_t now;
293135446Strhodes	isc_region_t r;
294135446Strhodes	isccc_sexpr_t *data;
295135446Strhodes	isccc_region_t message;
296135446Strhodes	isc_uint32_t len;
297135446Strhodes	isc_buffer_t b;
298135446Strhodes
299135446Strhodes	recvs--;
300135446Strhodes
301135446Strhodes	if (ccmsg.result == ISC_R_EOF)
302135446Strhodes		fatal("connection to remote host closed\n"
303170222Sdougb		      "This may indicate that\n"
304170222Sdougb		      "* the remote server is using an older version of"
305170222Sdougb		      " the command protocol,\n"
306170222Sdougb		      "* this host is not authorized to connect,\n"
307193149Sdougb		      "* the clocks are not synchronized, or\n"
308170222Sdougb		      "* the key is invalid.");
309135446Strhodes
310135446Strhodes	if (ccmsg.result != ISC_R_SUCCESS)
311135446Strhodes		fatal("recv failed: %s", isc_result_totext(ccmsg.result));
312135446Strhodes
313135446Strhodes	source.rstart = isc_buffer_base(&ccmsg.buffer);
314135446Strhodes	source.rend = isc_buffer_used(&ccmsg.buffer);
315135446Strhodes
316135446Strhodes	DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
317135446Strhodes
318135446Strhodes	_ctrl = isccc_alist_lookup(response, "_ctrl");
319296611Sdelphij	if (!isccc_alist_alistp(_ctrl))
320296611Sdelphij		fatal("bad or missing ctrl section in response");
321135446Strhodes	nonce = 0;
322135446Strhodes	if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
323135446Strhodes		nonce = 0;
324135446Strhodes
325135446Strhodes	isc_stdtime_get(&now);
326135446Strhodes
327135446Strhodes	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
328135446Strhodes						    now, now + 60, &request));
329135446Strhodes	data = isccc_alist_lookup(request, "_data");
330135446Strhodes	if (data == NULL)
331135446Strhodes		fatal("_data section missing");
332135446Strhodes	if (isccc_cc_definestring(data, "type", args) == NULL)
333135446Strhodes		fatal("out of memory");
334135446Strhodes	if (nonce != 0) {
335135446Strhodes		_ctrl = isccc_alist_lookup(request, "_ctrl");
336135446Strhodes		if (_ctrl == NULL)
337135446Strhodes			fatal("_ctrl section missing");
338135446Strhodes		if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
339135446Strhodes			fatal("out of memory");
340135446Strhodes	}
341135446Strhodes	message.rstart = databuf + 4;
342135446Strhodes	message.rend = databuf + sizeof(databuf);
343135446Strhodes	DO("render message", isccc_cc_towire(request, &message, &secret));
344135446Strhodes	len = sizeof(databuf) - REGION_SIZE(message);
345135446Strhodes	isc_buffer_init(&b, databuf, 4);
346135446Strhodes	isc_buffer_putuint32(&b, len - 4);
347135446Strhodes	r.length = len;
348135446Strhodes	r.base = databuf;
349135446Strhodes
350135446Strhodes	isccc_ccmsg_cancelread(&ccmsg);
351135446Strhodes	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
352135446Strhodes						    rndc_recvdone, NULL));
353135446Strhodes	recvs++;
354135446Strhodes	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
355135446Strhodes					   NULL));
356135446Strhodes	sends++;
357135446Strhodes
358135446Strhodes	isc_event_free(&event);
359135446Strhodes	isccc_sexpr_free(&response);
360135446Strhodes	return;
361135446Strhodes}
362135446Strhodes
363135446Strhodesstatic void
364135446Strhodesrndc_connected(isc_task_t *task, isc_event_t *event) {
365165071Sdougb	char socktext[ISC_SOCKADDR_FORMATSIZE];
366135446Strhodes	isc_socketevent_t *sevent = (isc_socketevent_t *)event;
367135446Strhodes	isccc_sexpr_t *request = NULL;
368135446Strhodes	isccc_sexpr_t *data;
369135446Strhodes	isccc_time_t now;
370135446Strhodes	isccc_region_t message;
371135446Strhodes	isc_region_t r;
372135446Strhodes	isc_uint32_t len;
373135446Strhodes	isc_buffer_t b;
374135446Strhodes	isc_result_t result;
375135446Strhodes
376135446Strhodes	connects--;
377135446Strhodes
378135446Strhodes	if (sevent->result != ISC_R_SUCCESS) {
379165071Sdougb		isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
380165071Sdougb				    sizeof(socktext));
381135446Strhodes		if (sevent->result != ISC_R_CANCELED &&
382165071Sdougb		    ++currentaddr < nserveraddrs)
383135446Strhodes		{
384165071Sdougb			notify("connection failed: %s: %s", socktext,
385135446Strhodes			       isc_result_totext(sevent->result));
386135446Strhodes			isc_socket_detach(&sock);
387135446Strhodes			isc_event_free(&event);
388165071Sdougb			rndc_startconnect(&serveraddrs[currentaddr], task);
389135446Strhodes			return;
390135446Strhodes		} else
391165071Sdougb			fatal("connect failed: %s: %s", socktext,
392135446Strhodes			      isc_result_totext(sevent->result));
393135446Strhodes	}
394135446Strhodes
395135446Strhodes	isc_stdtime_get(&now);
396135446Strhodes	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
397135446Strhodes						    now, now + 60, &request));
398135446Strhodes	data = isccc_alist_lookup(request, "_data");
399135446Strhodes	if (data == NULL)
400135446Strhodes		fatal("_data section missing");
401135446Strhodes	if (isccc_cc_definestring(data, "type", "null") == NULL)
402135446Strhodes		fatal("out of memory");
403135446Strhodes	message.rstart = databuf + 4;
404135446Strhodes	message.rend = databuf + sizeof(databuf);
405135446Strhodes	DO("render message", isccc_cc_towire(request, &message, &secret));
406135446Strhodes	len = sizeof(databuf) - REGION_SIZE(message);
407135446Strhodes	isc_buffer_init(&b, databuf, 4);
408135446Strhodes	isc_buffer_putuint32(&b, len - 4);
409135446Strhodes	r.length = len;
410135446Strhodes	r.base = databuf;
411135446Strhodes
412135446Strhodes	isccc_ccmsg_init(mctx, sock, &ccmsg);
413193149Sdougb	isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024);
414135446Strhodes
415135446Strhodes	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
416135446Strhodes						    rndc_recvnonce, NULL));
417135446Strhodes	recvs++;
418135446Strhodes	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
419135446Strhodes					   NULL));
420135446Strhodes	sends++;
421135446Strhodes	isc_event_free(&event);
422135446Strhodes}
423135446Strhodes
424135446Strhodesstatic void
425135446Strhodesrndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
426135446Strhodes	isc_result_t result;
427170222Sdougb	int pf;
428170222Sdougb	isc_sockettype_t type;
429135446Strhodes
430135446Strhodes	char socktext[ISC_SOCKADDR_FORMATSIZE];
431135446Strhodes
432135446Strhodes	isc_sockaddr_format(addr, socktext, sizeof(socktext));
433135446Strhodes
434135446Strhodes	notify("using server %s (%s)", servername, socktext);
435135446Strhodes
436170222Sdougb	pf = isc_sockaddr_pf(addr);
437170222Sdougb	if (pf == AF_INET || pf == AF_INET6)
438170222Sdougb		type = isc_sockettype_tcp;
439170222Sdougb	else
440170222Sdougb		type = isc_sockettype_unix;
441170222Sdougb	DO("create socket", isc_socket_create(socketmgr, pf, type, &sock));
442170222Sdougb	switch (isc_sockaddr_pf(addr)) {
443170222Sdougb	case AF_INET:
444182645Sdougb		DO("bind socket", isc_socket_bind(sock, &local4, 0));
445170222Sdougb		break;
446170222Sdougb	case AF_INET6:
447182645Sdougb		DO("bind socket", isc_socket_bind(sock, &local6, 0));
448170222Sdougb		break;
449170222Sdougb	default:
450170222Sdougb		break;
451170222Sdougb	}
452135446Strhodes	DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
453135446Strhodes					 NULL));
454135446Strhodes	connects++;
455135446Strhodes}
456135446Strhodes
457135446Strhodesstatic void
458135446Strhodesrndc_start(isc_task_t *task, isc_event_t *event) {
459135446Strhodes	isc_event_free(&event);
460135446Strhodes
461135446Strhodes	currentaddr = 0;
462165071Sdougb	rndc_startconnect(&serveraddrs[currentaddr], task);
463135446Strhodes}
464135446Strhodes
465135446Strhodesstatic void
466135446Strhodesparse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
467135446Strhodes	     cfg_parser_t **pctxp, cfg_obj_t **configp)
468135446Strhodes{
469135446Strhodes	isc_result_t result;
470135446Strhodes	const char *conffile = admin_conffile;
471170222Sdougb	const cfg_obj_t *addresses = NULL;
472165071Sdougb	const cfg_obj_t *defkey = NULL;
473165071Sdougb	const cfg_obj_t *options = NULL;
474165071Sdougb	const cfg_obj_t *servers = NULL;
475165071Sdougb	const cfg_obj_t *server = NULL;
476165071Sdougb	const cfg_obj_t *keys = NULL;
477165071Sdougb	const cfg_obj_t *key = NULL;
478165071Sdougb	const cfg_obj_t *defport = NULL;
479165071Sdougb	const cfg_obj_t *secretobj = NULL;
480165071Sdougb	const cfg_obj_t *algorithmobj = NULL;
481135446Strhodes	cfg_obj_t *config = NULL;
482170222Sdougb	const cfg_obj_t *address = NULL;
483165071Sdougb	const cfg_listelt_t *elt;
484135446Strhodes	const char *secretstr;
485135446Strhodes	const char *algorithm;
486135446Strhodes	static char secretarray[1024];
487135446Strhodes	const cfg_type_t *conftype = &cfg_type_rndcconf;
488135446Strhodes	isc_boolean_t key_only = ISC_FALSE;
489170222Sdougb	const cfg_listelt_t *element;
490135446Strhodes
491135446Strhodes	if (! isc_file_exists(conffile)) {
492135446Strhodes		conffile = admin_keyfile;
493135446Strhodes		conftype = &cfg_type_rndckey;
494135446Strhodes
495262706Serwin		if (c_flag)
496262706Serwin			fatal("%s does not exist", admin_conffile);
497262706Serwin
498135446Strhodes		if (! isc_file_exists(conffile))
499135446Strhodes			fatal("neither %s nor %s was found",
500135446Strhodes			      admin_conffile, admin_keyfile);
501135446Strhodes		key_only = ISC_TRUE;
502224092Sdougb	} else if (! c_flag && isc_file_exists(admin_keyfile)) {
503224092Sdougb		fprintf(stderr, "WARNING: key file (%s) exists, but using "
504224092Sdougb			"default configuration file (%s)\n",
505224092Sdougb			admin_keyfile, admin_conffile);
506135446Strhodes	}
507135446Strhodes
508135446Strhodes	DO("create parser", cfg_parser_create(mctx, log, pctxp));
509135446Strhodes
510135446Strhodes	/*
511135446Strhodes	 * The parser will output its own errors, so DO() is not used.
512135446Strhodes	 */
513135446Strhodes	result = cfg_parse_file(*pctxp, conffile, conftype, &config);
514135446Strhodes	if (result != ISC_R_SUCCESS)
515135446Strhodes		fatal("could not load rndc configuration");
516135446Strhodes
517135446Strhodes	if (!key_only)
518135446Strhodes		(void)cfg_map_get(config, "options", &options);
519135446Strhodes
520135446Strhodes	if (key_only && servername == NULL)
521135446Strhodes		servername = "127.0.0.1";
522135446Strhodes	else if (servername == NULL && options != NULL) {
523165071Sdougb		const cfg_obj_t *defserverobj = NULL;
524135446Strhodes		(void)cfg_map_get(options, "default-server", &defserverobj);
525135446Strhodes		if (defserverobj != NULL)
526135446Strhodes			servername = cfg_obj_asstring(defserverobj);
527135446Strhodes	}
528135446Strhodes
529135446Strhodes	if (servername == NULL)
530135446Strhodes		fatal("no server specified and no default");
531135446Strhodes
532135446Strhodes	if (!key_only) {
533135446Strhodes		(void)cfg_map_get(config, "server", &servers);
534135446Strhodes		if (servers != NULL) {
535135446Strhodes			for (elt = cfg_list_first(servers);
536186462Sdougb			     elt != NULL;
537135446Strhodes			     elt = cfg_list_next(elt))
538135446Strhodes			{
539135446Strhodes				const char *name;
540135446Strhodes				server = cfg_listelt_value(elt);
541135446Strhodes				name = cfg_obj_asstring(cfg_map_getname(server));
542135446Strhodes				if (strcasecmp(name, servername) == 0)
543135446Strhodes					break;
544135446Strhodes				server = NULL;
545135446Strhodes			}
546135446Strhodes		}
547135446Strhodes	}
548135446Strhodes
549135446Strhodes	/*
550135446Strhodes	 * Look for the name of the key to use.
551135446Strhodes	 */
552135446Strhodes	if (keyname != NULL)
553135446Strhodes		;		/* Was set on command line, do nothing. */
554135446Strhodes	else if (server != NULL) {
555135446Strhodes		DO("get key for server", cfg_map_get(server, "key", &defkey));
556135446Strhodes		keyname = cfg_obj_asstring(defkey);
557135446Strhodes	} else if (options != NULL) {
558135446Strhodes		DO("get default key", cfg_map_get(options, "default-key",
559135446Strhodes						  &defkey));
560135446Strhodes		keyname = cfg_obj_asstring(defkey);
561135446Strhodes	} else if (!key_only)
562135446Strhodes		fatal("no key for server and no default");
563135446Strhodes
564135446Strhodes	/*
565135446Strhodes	 * Get the key's definition.
566135446Strhodes	 */
567135446Strhodes	if (key_only)
568135446Strhodes		DO("get key", cfg_map_get(config, "key", &key));
569135446Strhodes	else {
570135446Strhodes		DO("get config key list", cfg_map_get(config, "key", &keys));
571135446Strhodes		for (elt = cfg_list_first(keys);
572186462Sdougb		     elt != NULL;
573135446Strhodes		     elt = cfg_list_next(elt))
574135446Strhodes		{
575135446Strhodes			key = cfg_listelt_value(elt);
576135446Strhodes			if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
577135446Strhodes				       keyname) == 0)
578135446Strhodes				break;
579135446Strhodes		}
580135446Strhodes		if (elt == NULL)
581135446Strhodes			fatal("no key definition for name %s", keyname);
582135446Strhodes	}
583135446Strhodes	(void)cfg_map_get(key, "secret", &secretobj);
584135446Strhodes	(void)cfg_map_get(key, "algorithm", &algorithmobj);
585135446Strhodes	if (secretobj == NULL || algorithmobj == NULL)
586135446Strhodes		fatal("key must have algorithm and secret");
587135446Strhodes
588135446Strhodes	secretstr = cfg_obj_asstring(secretobj);
589135446Strhodes	algorithm = cfg_obj_asstring(algorithmobj);
590135446Strhodes
591135446Strhodes	if (strcasecmp(algorithm, "hmac-md5") != 0)
592135446Strhodes		fatal("unsupported algorithm: %s", algorithm);
593135446Strhodes
594135446Strhodes	secret.rstart = (unsigned char *)secretarray;
595135446Strhodes	secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
596135446Strhodes	DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
597135446Strhodes	secret.rend = secret.rstart;
598135446Strhodes	secret.rstart = (unsigned char *)secretarray;
599135446Strhodes
600135446Strhodes	/*
601135446Strhodes	 * Find the port to connect to.
602135446Strhodes	 */
603135446Strhodes	if (remoteport != 0)
604135446Strhodes		;		/* Was set on command line, do nothing. */
605135446Strhodes	else {
606135446Strhodes		if (server != NULL)
607135446Strhodes			(void)cfg_map_get(server, "port", &defport);
608135446Strhodes		if (defport == NULL && options != NULL)
609135446Strhodes			(void)cfg_map_get(options, "default-port", &defport);
610135446Strhodes	}
611135446Strhodes	if (defport != NULL) {
612135446Strhodes		remoteport = cfg_obj_asuint32(defport);
613135446Strhodes		if (remoteport > 65535 || remoteport == 0)
614170222Sdougb			fatal("port %u out of range", remoteport);
615135446Strhodes	} else if (remoteport == 0)
616135446Strhodes		remoteport = NS_CONTROL_PORT;
617135446Strhodes
618170222Sdougb	if (server != NULL)
619170222Sdougb		result = cfg_map_get(server, "addresses", &addresses);
620170222Sdougb	else
621170222Sdougb		result = ISC_R_NOTFOUND;
622170222Sdougb	if (result == ISC_R_SUCCESS) {
623170222Sdougb		for (element = cfg_list_first(addresses);
624170222Sdougb		     element != NULL;
625170222Sdougb		     element = cfg_list_next(element))
626170222Sdougb		{
627170222Sdougb			isc_sockaddr_t sa;
628170222Sdougb
629170222Sdougb			address = cfg_listelt_value(element);
630170222Sdougb			if (!cfg_obj_issockaddr(address)) {
631170222Sdougb				unsigned int myport;
632170222Sdougb				const char *name;
633170222Sdougb				const cfg_obj_t *obj;
634170222Sdougb
635170222Sdougb				obj = cfg_tuple_get(address, "name");
636170222Sdougb				name = cfg_obj_asstring(obj);
637170222Sdougb				obj = cfg_tuple_get(address, "port");
638170222Sdougb				if (cfg_obj_isuint32(obj)) {
639170222Sdougb					myport = cfg_obj_asuint32(obj);
640170222Sdougb					if (myport > ISC_UINT16_MAX ||
641170222Sdougb					    myport == 0)
642170222Sdougb						fatal("port %u out of range",
643170222Sdougb						      myport);
644170222Sdougb				} else
645170222Sdougb					myport = remoteport;
646170222Sdougb				if (nserveraddrs < SERVERADDRS)
647170222Sdougb					get_addresses(name, (in_port_t) myport);
648170222Sdougb				else
649170222Sdougb					fprintf(stderr, "too many address: "
650186462Sdougb						"%s: dropped\n", name);
651170222Sdougb				continue;
652170222Sdougb			}
653170222Sdougb			sa = *cfg_obj_assockaddr(address);
654170222Sdougb			if (isc_sockaddr_getport(&sa) == 0)
655170222Sdougb				isc_sockaddr_setport(&sa, remoteport);
656170222Sdougb			if (nserveraddrs < SERVERADDRS)
657170222Sdougb				serveraddrs[nserveraddrs++] = sa;
658170222Sdougb			else {
659170222Sdougb				char socktext[ISC_SOCKADDR_FORMATSIZE];
660170222Sdougb
661170222Sdougb				isc_sockaddr_format(&sa, socktext,
662170222Sdougb						    sizeof(socktext));
663170222Sdougb				fprintf(stderr,
664170222Sdougb					"too many address: %s: dropped\n",
665170222Sdougb					socktext);
666170222Sdougb			}
667170222Sdougb		}
668170222Sdougb	}
669170222Sdougb
670170222Sdougb	if (!local4set && server != NULL) {
671170222Sdougb		address = NULL;
672170222Sdougb		cfg_map_get(server, "source-address", &address);
673170222Sdougb		if (address != NULL) {
674170222Sdougb			local4 = *cfg_obj_assockaddr(address);
675170222Sdougb			local4set = ISC_TRUE;
676170222Sdougb		}
677170222Sdougb	}
678170222Sdougb	if (!local4set && options != NULL) {
679170222Sdougb		address = NULL;
680170222Sdougb		cfg_map_get(options, "default-source-address", &address);
681170222Sdougb		if (address != NULL) {
682170222Sdougb			local4 = *cfg_obj_assockaddr(address);
683170222Sdougb			local4set = ISC_TRUE;
684170222Sdougb		}
685170222Sdougb	}
686170222Sdougb
687170222Sdougb	if (!local6set && server != NULL) {
688170222Sdougb		address = NULL;
689170222Sdougb		cfg_map_get(server, "source-address-v6", &address);
690170222Sdougb		if (address != NULL) {
691170222Sdougb			local6 = *cfg_obj_assockaddr(address);
692170222Sdougb			local6set = ISC_TRUE;
693170222Sdougb		}
694170222Sdougb	}
695170222Sdougb	if (!local6set && options != NULL) {
696170222Sdougb		address = NULL;
697170222Sdougb		cfg_map_get(options, "default-source-address-v6", &address);
698170222Sdougb		if (address != NULL) {
699170222Sdougb			local6 = *cfg_obj_assockaddr(address);
700170222Sdougb			local6set = ISC_TRUE;
701170222Sdougb		}
702170222Sdougb	}
703170222Sdougb
704135446Strhodes	*configp = config;
705135446Strhodes}
706135446Strhodes
707135446Strhodesint
708135446Strhodesmain(int argc, char **argv) {
709135446Strhodes	isc_boolean_t show_final_mem = ISC_FALSE;
710135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
711135446Strhodes	isc_taskmgr_t *taskmgr = NULL;
712135446Strhodes	isc_task_t *task = NULL;
713135446Strhodes	isc_log_t *log = NULL;
714135446Strhodes	isc_logconfig_t *logconfig = NULL;
715135446Strhodes	isc_logdestination_t logdest;
716135446Strhodes	cfg_parser_t *pctx = NULL;
717135446Strhodes	cfg_obj_t *config = NULL;
718135446Strhodes	const char *keyname = NULL;
719170222Sdougb	struct in_addr in;
720170222Sdougb	struct in6_addr in6;
721135446Strhodes	char *p;
722135446Strhodes	size_t argslen;
723135446Strhodes	int ch;
724135446Strhodes	int i;
725135446Strhodes
726135446Strhodes	result = isc_file_progname(*argv, program, sizeof(program));
727135446Strhodes	if (result != ISC_R_SUCCESS)
728262706Serwin		memmove(program, "rndc", 5);
729135446Strhodes	progname = program;
730135446Strhodes
731135446Strhodes	admin_conffile = RNDC_CONFFILE;
732135446Strhodes	admin_keyfile = RNDC_KEYFILE;
733135446Strhodes
734170222Sdougb	isc_sockaddr_any(&local4);
735170222Sdougb	isc_sockaddr_any6(&local6);
736170222Sdougb
737135446Strhodes	result = isc_app_start();
738135446Strhodes	if (result != ISC_R_SUCCESS)
739135446Strhodes		fatal("isc_app_start() failed: %s", isc_result_totext(result));
740135446Strhodes
741193149Sdougb	isc_commandline_errprint = ISC_FALSE;
742193149Sdougb
743193149Sdougb	while ((ch = isc_commandline_parse(argc, argv, "b:c:hk:Mmp:s:Vy:"))
744135446Strhodes	       != -1) {
745135446Strhodes		switch (ch) {
746170222Sdougb		case 'b':
747170222Sdougb			if (inet_pton(AF_INET, isc_commandline_argument,
748170222Sdougb				      &in) == 1) {
749170222Sdougb				isc_sockaddr_fromin(&local4, &in, 0);
750170222Sdougb				local4set = ISC_TRUE;
751170222Sdougb			} else if (inet_pton(AF_INET6, isc_commandline_argument,
752170222Sdougb					     &in6) == 1) {
753170222Sdougb				isc_sockaddr_fromin6(&local6, &in6, 0);
754170222Sdougb				local6set = ISC_TRUE;
755170222Sdougb			}
756170222Sdougb			break;
757170222Sdougb
758135446Strhodes		case 'c':
759135446Strhodes			admin_conffile = isc_commandline_argument;
760224092Sdougb			c_flag = ISC_TRUE;
761135446Strhodes			break;
762135446Strhodes
763135446Strhodes		case 'k':
764135446Strhodes			admin_keyfile = isc_commandline_argument;
765135446Strhodes			break;
766135446Strhodes
767135446Strhodes		case 'M':
768135446Strhodes			isc_mem_debugging = ISC_MEM_DEBUGTRACE;
769135446Strhodes			break;
770135446Strhodes
771135446Strhodes		case 'm':
772135446Strhodes			show_final_mem = ISC_TRUE;
773135446Strhodes			break;
774135446Strhodes
775135446Strhodes		case 'p':
776135446Strhodes			remoteport = atoi(isc_commandline_argument);
777135446Strhodes			if (remoteport > 65535 || remoteport == 0)
778135446Strhodes				fatal("port '%s' out of range",
779135446Strhodes				      isc_commandline_argument);
780135446Strhodes			break;
781135446Strhodes
782135446Strhodes		case 's':
783135446Strhodes			servername = isc_commandline_argument;
784135446Strhodes			break;
785170222Sdougb
786135446Strhodes		case 'V':
787135446Strhodes			verbose = ISC_TRUE;
788135446Strhodes			break;
789170222Sdougb
790135446Strhodes		case 'y':
791135446Strhodes			keyname = isc_commandline_argument;
792135446Strhodes			break;
793186462Sdougb
794135446Strhodes		case '?':
795193149Sdougb			if (isc_commandline_option != '?') {
796193149Sdougb				fprintf(stderr, "%s: invalid argument -%c\n",
797193149Sdougb					program, isc_commandline_option);
798193149Sdougb				usage(1);
799193149Sdougb			}
800254402Serwin			/* FALLTHROUGH */
801193149Sdougb		case 'h':
802135446Strhodes			usage(0);
803135446Strhodes			break;
804135446Strhodes		default:
805193149Sdougb			fprintf(stderr, "%s: unhandled option -%c\n",
806193149Sdougb				program, isc_commandline_option);
807193149Sdougb			exit(1);
808135446Strhodes		}
809135446Strhodes	}
810135446Strhodes
811135446Strhodes	argc -= isc_commandline_index;
812135446Strhodes	argv += isc_commandline_index;
813135446Strhodes
814135446Strhodes	if (argc < 1)
815135446Strhodes		usage(1);
816135446Strhodes
817135446Strhodes	isc_random_get(&serial);
818135446Strhodes
819135446Strhodes	DO("create memory context", isc_mem_create(0, 0, &mctx));
820135446Strhodes	DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
821135446Strhodes	DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
822135446Strhodes	DO("create task", isc_task_create(taskmgr, 0, &task));
823135446Strhodes
824135446Strhodes	DO("create logging context", isc_log_create(mctx, &log, &logconfig));
825135446Strhodes	isc_log_setcontext(log);
826135446Strhodes	DO("setting log tag", isc_log_settag(logconfig, progname));
827135446Strhodes	logdest.file.stream = stderr;
828135446Strhodes	logdest.file.name = NULL;
829135446Strhodes	logdest.file.versions = ISC_LOG_ROLLNEVER;
830135446Strhodes	logdest.file.maximum_size = 0;
831135446Strhodes	DO("creating log channel",
832135446Strhodes	   isc_log_createchannel(logconfig, "stderr",
833186462Sdougb				 ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
834135446Strhodes				 ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
835135446Strhodes	DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
836135446Strhodes						      NULL, NULL));
837135446Strhodes
838135446Strhodes	parse_config(mctx, log, keyname, &pctx, &config);
839135446Strhodes
840135446Strhodes	isccc_result_register();
841135446Strhodes
842135446Strhodes	command = *argv;
843135446Strhodes
844135446Strhodes	/*
845135446Strhodes	 * Convert argc/argv into a space-delimited command string
846135446Strhodes	 * similar to what the user might enter in interactive mode
847135446Strhodes	 * (if that were implemented).
848135446Strhodes	 */
849135446Strhodes	argslen = 0;
850135446Strhodes	for (i = 0; i < argc; i++)
851135446Strhodes		argslen += strlen(argv[i]) + 1;
852135446Strhodes
853135446Strhodes	args = isc_mem_get(mctx, argslen);
854135446Strhodes	if (args == NULL)
855135446Strhodes		DO("isc_mem_get", ISC_R_NOMEMORY);
856135446Strhodes
857135446Strhodes	p = args;
858135446Strhodes	for (i = 0; i < argc; i++) {
859135446Strhodes		size_t len = strlen(argv[i]);
860262706Serwin		memmove(p, argv[i], len);
861135446Strhodes		p += len;
862135446Strhodes		*p++ = ' ';
863135446Strhodes	}
864135446Strhodes
865135446Strhodes	p--;
866135446Strhodes	*p++ = '\0';
867135446Strhodes	INSIST(p == args + argslen);
868135446Strhodes
869135446Strhodes	notify("%s", command);
870135446Strhodes
871135446Strhodes	if (strcmp(command, "restart") == 0)
872135446Strhodes		fatal("'%s' is not implemented", command);
873135446Strhodes
874170222Sdougb	if (nserveraddrs == 0)
875170222Sdougb		get_addresses(servername, (in_port_t) remoteport);
876170222Sdougb
877135446Strhodes	DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL));
878135446Strhodes
879135446Strhodes	result = isc_app_run();
880135446Strhodes	if (result != ISC_R_SUCCESS)
881135446Strhodes		fatal("isc_app_run() failed: %s", isc_result_totext(result));
882135446Strhodes
883135446Strhodes	if (connects > 0 || sends > 0 || recvs > 0)
884135446Strhodes		isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
885135446Strhodes
886135446Strhodes	isc_task_detach(&task);
887135446Strhodes	isc_taskmgr_destroy(&taskmgr);
888135446Strhodes	isc_socketmgr_destroy(&socketmgr);
889135446Strhodes	isc_log_destroy(&log);
890135446Strhodes	isc_log_setcontext(NULL);
891135446Strhodes
892135446Strhodes	cfg_obj_destroy(pctx, &config);
893135446Strhodes	cfg_parser_destroy(&pctx);
894135446Strhodes
895135446Strhodes	isc_mem_put(mctx, args, argslen);
896135446Strhodes	isccc_ccmsg_invalidate(&ccmsg);
897135446Strhodes
898170222Sdougb	dns_name_destroy();
899170222Sdougb
900135446Strhodes	if (show_final_mem)
901135446Strhodes		isc_mem_stats(mctx, stderr);
902135446Strhodes
903135446Strhodes	isc_mem_destroy(&mctx);
904135446Strhodes
905135446Strhodes	if (failed)
906135446Strhodes		return (1);
907135446Strhodes
908135446Strhodes	return (0);
909135446Strhodes}
910