1/*	$NetBSD: rndc.c,v 1.11 2024/02/21 22:51:12 christos Exp $	*/
2
3/*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16/*! \file */
17
18#include <inttypes.h>
19#include <stdbool.h>
20#include <stdlib.h>
21
22#include <isc/app.h>
23#include <isc/atomic.h>
24#include <isc/attributes.h>
25#include <isc/buffer.h>
26#include <isc/commandline.h>
27#include <isc/file.h>
28#include <isc/log.h>
29#include <isc/managers.h>
30#include <isc/mem.h>
31#include <isc/net.h>
32#include <isc/netmgr.h>
33#include <isc/print.h>
34#include <isc/random.h>
35#include <isc/refcount.h>
36#include <isc/result.h>
37#include <isc/stdtime.h>
38#include <isc/string.h>
39#include <isc/task.h>
40#include <isc/thread.h>
41#include <isc/util.h>
42
43#include <dns/name.h>
44
45#include <isccc/alist.h>
46#include <isccc/base64.h>
47#include <isccc/cc.h>
48#include <isccc/ccmsg.h>
49#include <isccc/sexpr.h>
50#include <isccc/types.h>
51#include <isccc/util.h>
52
53#include <isccfg/namedconf.h>
54
55#include <bind9/getaddresses.h>
56
57#include "util.h"
58
59#define SERVERADDRS  10
60#define RNDC_TIMEOUT 60 * 1000
61
62const char *progname = NULL;
63bool verbose;
64
65static isc_nm_t *netmgr = NULL;
66static isc_taskmgr_t *taskmgr = NULL;
67static isc_task_t *rndc_task = NULL;
68
69static const char *admin_conffile = NULL;
70static const char *admin_keyfile = NULL;
71static const char *version = PACKAGE_VERSION;
72static const char *servername = NULL;
73static isc_sockaddr_t serveraddrs[SERVERADDRS];
74static isc_sockaddr_t local4, local6;
75static bool local4set = false, local6set = false;
76static int nserveraddrs;
77static int currentaddr = 0;
78static unsigned int remoteport = 0;
79static isc_buffer_t *databuf = NULL;
80static isccc_ccmsg_t rndc_ccmsg;
81static uint32_t algorithm;
82static isccc_region_t secret;
83static bool failed = false;
84static bool c_flag = false;
85static isc_mem_t *rndc_mctx = NULL;
86static atomic_uint_fast32_t sends = 0;
87static atomic_uint_fast32_t recvs = 0;
88static atomic_uint_fast32_t connects = 0;
89static char *command = NULL;
90static char *args = NULL;
91static char program[256];
92static uint32_t serial;
93static bool quiet = false;
94static bool showresult = false;
95static bool shuttingdown = false;
96static isc_nmhandle_t *recvdone_handle = NULL;
97static isc_nmhandle_t *recvnonce_handle = NULL;
98
99static void
100rndc_startconnect(isc_sockaddr_t *addr);
101
102noreturn static void
103usage(int status);
104
105static void
106usage(int status) {
107	fprintf(stderr, "\
108Usage: %s [-b address] [-c config] [-s server] [-p port]\n\
109	[-k key-file ] [-y key] [-r] [-V] [-4 | -6] command\n\
110\n\
111command is one of the following:\n\
112\n\
113  addzone zone [class [view]] { zone-options }\n\
114		Add zone to given view. Requires allow-new-zones option.\n\
115  delzone [-clean] zone [class [view]]\n\
116		Removes zone from given view.\n\
117  dnssec -checkds [-key id [-alg algorithm]] [-when time] (published|withdrawn) zone [class [view]]\n\
118		Mark the DS record for the KSK of the given zone as seen\n\
119		in the parent.  If the zone has multiple KSKs, select a\n\
120		specific key by providing the keytag with -key id and\n\
121		optionally the key's algorithm with -alg algorithm.\n\
122		Requires the zone to have a dnssec-policy.\n\
123  dnssec -rollover -key id [-alg algorithm] [-when time] zone [class [view]]\n\
124		Rollover key with id of the given zone. Requires the zone\n\
125		to have a dnssec-policy.\n\
126  dnssec -status zone [class [view]]\n\
127		Show the DNSSEC signing state for the specified zone.\n\
128		Requires the zone to have a dnssec-policy.\n\
129  dnstap -reopen\n\
130		Close, truncate and re-open the DNSTAP output file.\n\
131  dnstap -roll [count]\n\
132		Close, rename and re-open the DNSTAP output file(s).\n\
133  dumpdb [-all|-cache|-zones|-adb|-bad|-expired|-fail] [view ...]\n\
134		Dump cache(s) to the dump file (named_dump.db).\n\
135  flush         Flushes all of the server's caches.\n\
136  flush [view]	Flushes the server's cache for a view.\n\
137  flushname name [view]\n\
138		Flush the given name from the server's cache(s)\n\
139  flushtree name [view]\n\
140		Flush all names under the given name from the server's cache(s)\n\
141  freeze	Suspend updates to all dynamic zones.\n\
142  freeze zone [class [view]]\n\
143		Suspend updates to a dynamic zone.\n\
144  halt		Stop the server without saving pending updates.\n\
145  halt -p	Stop the server without saving pending updates reporting\n\
146		process id.\n\
147  loadkeys zone [class [view]]\n\
148		Update keys without signing immediately.\n\
149  managed-keys refresh [class [view]]\n\
150		Check trust anchor for RFC 5011 key changes\n\
151  managed-keys status [class [view]]\n\
152		Display RFC 5011 managed keys information\n\
153  managed-keys sync [class [view]]\n\
154		Write RFC 5011 managed keys to disk\n\
155  modzone zone [class [view]] { zone-options }\n\
156		Modify a zone's configuration.\n\
157		Requires allow-new-zones option.\n\
158  notify zone [class [view]]\n\
159		Resend NOTIFY messages for the zone.\n\
160  notrace	Set debugging level to 0.\n\
161  nta -dump\n\
162		List all negative trust anchors.\n\
163  nta [-lifetime duration] [-force] domain [view]\n\
164		Set a negative trust anchor, disabling DNSSEC validation\n\
165		for the given domain.\n\
166		Using -lifetime specifies the duration of the NTA, up\n\
167		to one week.\n\
168		Using -force prevents the NTA from expiring before its\n\
169		full lifetime, even if the domain can validate sooner.\n\
170  nta -remove domain [view]\n\
171		Remove a negative trust anchor, re-enabling validation\n\
172		for the given domain.\n\
173  querylog [ on | off ]\n\
174		Enable / disable query logging.\n\
175  reconfig	Reload configuration file and new zones only.\n\
176  recursing	Dump the queries that are currently recursing (named.recursing)\n\
177  refresh zone [class [view]]\n\
178		Schedule immediate maintenance for a zone.\n\
179  reload	Reload configuration file and zones.\n\
180  reload zone [class [view]]\n\
181		Reload a single zone.\n\
182  retransfer zone [class [view]]\n\
183		Retransfer a single zone without checking serial number.\n\
184  scan		Scan available network interfaces for changes.\n\
185  secroots [view ...]\n\
186		Write security roots to the secroots file.\n\
187  serve-stale [ on | off | reset | status ] [class [view]]\n\
188		Control whether stale answers are returned\n\
189  showzone zone [class [view]]\n\
190		Print a zone's configuration.\n\
191  sign zone [class [view]]\n\
192		Update zone keys, and sign as needed.\n\
193  signing -clear all zone [class [view]]\n\
194		Remove the private records for all keys that have\n\
195		finished signing the given zone.\n\
196  signing -clear <keyid>/<algorithm> zone [class [view]]\n\
197		Remove the private record that indicating the given key\n\
198		has finished signing the given zone.\n\
199  signing -list zone [class [view]]\n\
200		List the private records showing the state of DNSSEC\n\
201		signing in the given zone.\n\
202  signing -nsec3param hash flags iterations salt zone [class [view]]\n\
203		Add NSEC3 chain to zone if already signed.\n\
204		Prime zone with NSEC3 chain if not yet signed.\n\
205  signing -nsec3param none zone [class [view]]\n\
206		Remove NSEC3 chains from zone.\n\
207  signing -serial <value> zone [class [view]]\n\
208		Set the zones's serial to <value>.\n\
209  stats		Write server statistics to the statistics file.\n\
210  status	Display status of the server.\n\
211  stop		Save pending updates to master files and stop the server.\n\
212  stop -p	Save pending updates to master files and stop the server\n\
213		reporting process id.\n\
214  sync [-clean]	Dump changes to all dynamic zones to disk, and optionally\n\
215		remove their journal files.\n\
216  sync [-clean] zone [class [view]]\n\
217		Dump a single zone's changes to disk, and optionally\n\
218		remove its journal file.\n\
219  tcp-timeouts	Display the tcp-*-timeout option values\n\
220  tcp-timeouts initial idle keepalive advertised\n\
221		Update the tcp-*-timeout option values\n\
222  thaw		Enable updates to all dynamic zones and reload them.\n\
223  thaw zone [class [view]]\n\
224		Enable updates to a frozen dynamic zone and reload it.\n\
225  trace		Increment debugging level by one.\n\
226  trace level	Change the debugging level.\n\
227  tsig-delete keyname [view]\n\
228		Delete a TKEY-negotiated TSIG key.\n\
229  tsig-list	List all currently active TSIG keys, including both statically\n\
230		configured and TKEY-negotiated keys.\n\
231  validation [ on | off | status ] [view]\n\
232		Enable / disable DNSSEC validation.\n\
233  zonestatus zone [class [view]]\n\
234		Display the current status of a zone.\n\
235\n\
236Version: %s\n",
237		progname, version);
238
239	exit(status);
240}
241
242#define CMDLINE_FLAGS "46b:c:hk:Mmp:qrs:Vy:"
243
244static void
245preparse_args(int argc, char **argv) {
246	bool ipv4only = false, ipv6only = false;
247	int ch;
248
249	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
250		switch (ch) {
251		case '4':
252			if (ipv6only) {
253				fatal("only one of -4 and -6 allowed");
254			}
255			ipv4only = true;
256			break;
257		case '6':
258			if (ipv4only) {
259				fatal("only one of -4 and -6 allowed");
260			}
261			ipv6only = true;
262			break;
263		default:
264			break;
265		}
266	}
267
268	isc_commandline_reset = true;
269	isc_commandline_index = 1;
270}
271
272static void
273get_addresses(const char *host, in_port_t port) {
274	isc_result_t result;
275	int found = 0, count;
276
277	REQUIRE(host != NULL);
278
279	if (*host == '/') {
280		result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
281					       host);
282		if (result == ISC_R_SUCCESS) {
283			nserveraddrs++;
284		}
285	} else {
286		count = SERVERADDRS - nserveraddrs;
287		result = bind9_getaddresses(
288			host, port, &serveraddrs[nserveraddrs], count, &found);
289		nserveraddrs += found;
290	}
291	if (result != ISC_R_SUCCESS) {
292		fatal("couldn't get address for '%s': %s", host,
293		      isc_result_totext(result));
294	}
295	INSIST(nserveraddrs > 0);
296}
297
298static void
299rndc_senddone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
300	isc_nmhandle_t *sendhandle = (isc_nmhandle_t *)arg;
301
302	if (result != ISC_R_SUCCESS) {
303		fatal("send failed: %s", isc_result_totext(result));
304	}
305
306	REQUIRE(sendhandle == handle);
307	isc_nmhandle_detach(&sendhandle);
308
309	if (atomic_fetch_sub_release(&sends, 1) == 1 &&
310	    atomic_load_acquire(&recvs) == 0)
311	{
312		shuttingdown = true;
313		isc_task_detach(&rndc_task);
314		isc_app_shutdown();
315	}
316}
317
318static void
319rndc_recvdone(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
320	isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg;
321	isccc_sexpr_t *response = NULL;
322	isccc_sexpr_t *data = NULL;
323	isccc_region_t source;
324	char *errormsg = NULL;
325	char *textmsg = NULL;
326
327	REQUIRE(ccmsg != NULL);
328
329	if (shuttingdown && (result == ISC_R_EOF || result == ISC_R_CANCELED)) {
330		atomic_fetch_sub_release(&recvs, 1);
331		if (handle != NULL) {
332			REQUIRE(recvdone_handle == handle);
333			isc_nmhandle_detach(&recvdone_handle);
334		}
335		return;
336	} else if (result == ISC_R_EOF) {
337		fatal("connection to remote host closed.\n"
338		      "* This may indicate that the\n"
339		      "* remote server is using an older\n"
340		      "* version of the command protocol,\n"
341		      "* this host is not authorized to connect,\n"
342		      "* the clocks are not synchronized,\n"
343		      "* the key signing algorithm is incorrect,\n"
344		      "* or the key is invalid.");
345	} else if (result != ISC_R_SUCCESS) {
346		fatal("recv failed: %s", isc_result_totext(result));
347	}
348
349	source.rstart = isc_buffer_base(ccmsg->buffer);
350	source.rend = isc_buffer_used(ccmsg->buffer);
351
352	DO("parse message",
353	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
354
355	data = isccc_alist_lookup(response, "_data");
356	if (!isccc_alist_alistp(data)) {
357		fatal("bad or missing data section in response");
358	}
359	result = isccc_cc_lookupstring(data, "err", &errormsg);
360	if (result == ISC_R_SUCCESS) {
361		failed = true;
362		fprintf(stderr, "%s: '%s' failed: %s\n", progname, command,
363			errormsg);
364	} else if (result != ISC_R_NOTFOUND) {
365		fprintf(stderr, "%s: parsing response failed: %s\n", progname,
366			isc_result_totext(result));
367	}
368
369	result = isccc_cc_lookupstring(data, "text", &textmsg);
370	if (result == ISC_R_SUCCESS) {
371		if ((!quiet || failed) && strlen(textmsg) != 0U) {
372			fprintf(failed ? stderr : stdout, "%s\n", textmsg);
373		}
374	} else if (result != ISC_R_NOTFOUND) {
375		fprintf(stderr, "%s: parsing response failed: %s\n", progname,
376			isc_result_totext(result));
377	}
378
379	if (showresult) {
380		isc_result_t eresult;
381
382		result = isccc_cc_lookupuint32(data, "result", &eresult);
383		if (result == ISC_R_SUCCESS) {
384			printf("%s %u\n", isc_result_toid(eresult), eresult);
385		} else {
386			printf("NONE -1\n");
387		}
388	}
389
390	isccc_sexpr_free(&response);
391
392	REQUIRE(recvdone_handle == handle);
393	isc_nmhandle_detach(&recvdone_handle);
394
395	if (atomic_fetch_sub_release(&recvs, 1) == 1 &&
396	    atomic_load_acquire(&sends) == 0)
397	{
398		shuttingdown = true;
399		isc_task_detach(&rndc_task);
400		isc_app_shutdown();
401	}
402}
403
404static void
405rndc_recvnonce(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
406	isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg;
407	isccc_sexpr_t *response = NULL;
408	isc_nmhandle_t *sendhandle = NULL;
409	isccc_sexpr_t *_ctrl = NULL;
410	isccc_region_t source;
411	uint32_t nonce;
412	isccc_sexpr_t *request = NULL;
413	isccc_time_t now;
414	isc_region_t r;
415	isccc_sexpr_t *data = NULL;
416	isc_buffer_t b;
417
418	REQUIRE(ccmsg != NULL);
419
420	if (shuttingdown && (result == ISC_R_EOF || result == ISC_R_CANCELED)) {
421		atomic_fetch_sub_release(&recvs, 1);
422		if (handle != NULL) {
423			REQUIRE(recvnonce_handle == handle);
424			isc_nmhandle_detach(&recvnonce_handle);
425		}
426		return;
427	} else if (result == ISC_R_EOF) {
428		fatal("connection to remote host closed.\n"
429		      "* This may indicate that the\n"
430		      "* remote server is using an older\n"
431		      "* version of the command protocol,\n"
432		      "* this host is not authorized to connect,\n"
433		      "* the clocks are not synchronized,\n"
434		      "* the key signing algorithm is incorrect\n"
435		      "* or the key is invalid.");
436	} else if (result != ISC_R_SUCCESS) {
437		fatal("recv failed: %s", isc_result_totext(result));
438	}
439
440	source.rstart = isc_buffer_base(ccmsg->buffer);
441	source.rend = isc_buffer_used(ccmsg->buffer);
442
443	DO("parse message",
444	   isccc_cc_fromwire(&source, &response, algorithm, &secret));
445
446	_ctrl = isccc_alist_lookup(response, "_ctrl");
447	if (!isccc_alist_alistp(_ctrl)) {
448		fatal("bad or missing ctrl section in response");
449	}
450	nonce = 0;
451	if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS) {
452		nonce = 0;
453	}
454
455	isc_stdtime_get(&now);
456
457	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
458						    now, now + 60, &request));
459	data = isccc_alist_lookup(request, "_data");
460	if (data == NULL) {
461		fatal("_data section missing");
462	}
463	if (isccc_cc_definestring(data, "type", args) == NULL) {
464		fatal("out of memory");
465	}
466	if (nonce != 0) {
467		_ctrl = isccc_alist_lookup(request, "_ctrl");
468		if (_ctrl == NULL) {
469			fatal("_ctrl section missing");
470		}
471		if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL) {
472			fatal("out of memory");
473		}
474	}
475
476	isc_buffer_clear(databuf);
477	/* Skip the length field (4 bytes) */
478	isc_buffer_add(databuf, 4);
479
480	DO("render message",
481	   isccc_cc_towire(request, &databuf, algorithm, &secret));
482
483	isc_buffer_init(&b, databuf->base, 4);
484	isc_buffer_putuint32(&b, databuf->used - 4);
485
486	r.base = databuf->base;
487	r.length = databuf->used;
488
489	isc_nmhandle_attach(handle, &recvdone_handle);
490	atomic_fetch_add_relaxed(&recvs, 1);
491	isccc_ccmsg_readmessage(ccmsg, rndc_recvdone, ccmsg);
492
493	isc_nmhandle_attach(handle, &sendhandle);
494	atomic_fetch_add_relaxed(&sends, 1);
495	isc_nm_send(handle, &r, rndc_senddone, sendhandle);
496
497	REQUIRE(recvnonce_handle == handle);
498	isc_nmhandle_detach(&recvnonce_handle);
499	atomic_fetch_sub_release(&recvs, 1);
500
501	isccc_sexpr_free(&response);
502	isccc_sexpr_free(&request);
503	return;
504}
505
506static void
507rndc_connected(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
508	isccc_ccmsg_t *ccmsg = (isccc_ccmsg_t *)arg;
509	char socktext[ISC_SOCKADDR_FORMATSIZE];
510	isccc_sexpr_t *request = NULL;
511	isccc_sexpr_t *data = NULL;
512	isccc_time_t now;
513	isc_region_t r;
514	isc_buffer_t b;
515	isc_nmhandle_t *connhandle = NULL;
516	isc_nmhandle_t *sendhandle = NULL;
517
518	REQUIRE(ccmsg != NULL);
519
520	if (result != ISC_R_SUCCESS) {
521		atomic_fetch_sub_release(&connects, 1);
522		isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
523				    sizeof(socktext));
524		if (++currentaddr < nserveraddrs) {
525			notify("connection failed: %s: %s", socktext,
526			       isc_result_totext(result));
527			rndc_startconnect(&serveraddrs[currentaddr]);
528			return;
529		}
530
531		fatal("connect failed: %s: %s", socktext,
532		      isc_result_totext(result));
533	}
534
535	isc_nmhandle_attach(handle, &connhandle);
536
537	isc_stdtime_get(&now);
538	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
539						    now, now + 60, &request));
540	data = isccc_alist_lookup(request, "_data");
541	if (data == NULL) {
542		fatal("_data section missing");
543	}
544	if (isccc_cc_definestring(data, "type", "null") == NULL) {
545		fatal("out of memory");
546	}
547
548	isc_buffer_clear(databuf);
549	/* Skip the length field (4 bytes) */
550	isc_buffer_add(databuf, 4);
551
552	DO("render message",
553	   isccc_cc_towire(request, &databuf, algorithm, &secret));
554
555	isc_buffer_init(&b, databuf->base, 4);
556	isc_buffer_putuint32(&b, databuf->used - 4);
557
558	r.base = databuf->base;
559	r.length = databuf->used;
560
561	isccc_ccmsg_init(rndc_mctx, handle, ccmsg);
562	isccc_ccmsg_setmaxsize(ccmsg, 1024 * 1024);
563
564	isc_nmhandle_attach(handle, &recvnonce_handle);
565	atomic_fetch_add_relaxed(&recvs, 1);
566	isccc_ccmsg_readmessage(ccmsg, rndc_recvnonce, ccmsg);
567
568	isc_nmhandle_attach(handle, &sendhandle);
569	atomic_fetch_add_relaxed(&sends, 1);
570	isc_nm_send(handle, &r, rndc_senddone, sendhandle);
571
572	isc_nmhandle_detach(&connhandle);
573	atomic_fetch_sub_release(&connects, 1);
574
575	isccc_sexpr_free(&request);
576}
577
578static void
579rndc_startconnect(isc_sockaddr_t *addr) {
580	char socktext[ISC_SOCKADDR_FORMATSIZE];
581	isc_sockaddr_t *local = NULL;
582
583	isc_sockaddr_format(addr, socktext, sizeof(socktext));
584
585	notify("using server %s (%s)", servername, socktext);
586
587	switch (isc_sockaddr_pf(addr)) {
588	case AF_INET:
589		local = &local4;
590		break;
591	case AF_INET6:
592		local = &local6;
593		break;
594	case AF_UNIX:
595		/*
596		 * TODO: support UNIX domain sockets in netgmr.
597		 */
598		fatal("UNIX domain sockets not currently supported");
599	default:
600		UNREACHABLE();
601	}
602
603	atomic_fetch_add_relaxed(&connects, 1);
604	isc_nm_tcpconnect(netmgr, local, addr, rndc_connected, &rndc_ccmsg,
605			  RNDC_TIMEOUT, 0);
606}
607
608static void
609rndc_start(isc_task_t *task, isc_event_t *event) {
610	isc_event_free(&event);
611
612	UNUSED(task);
613
614	currentaddr = 0;
615	rndc_startconnect(&serveraddrs[currentaddr]);
616}
617
618static void
619parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
620	     cfg_parser_t **pctxp, cfg_obj_t **configp) {
621	isc_result_t result;
622	const char *conffile = admin_conffile;
623	const cfg_obj_t *addresses = NULL;
624	const cfg_obj_t *defkey = NULL;
625	const cfg_obj_t *options = NULL;
626	const cfg_obj_t *servers = NULL;
627	const cfg_obj_t *server = NULL;
628	const cfg_obj_t *keys = NULL;
629	const cfg_obj_t *key = NULL;
630	const cfg_obj_t *defport = NULL;
631	const cfg_obj_t *secretobj = NULL;
632	const cfg_obj_t *algorithmobj = NULL;
633	cfg_obj_t *config = NULL;
634	const cfg_obj_t *address = NULL;
635	const cfg_listelt_t *elt;
636	const char *secretstr;
637	const char *algorithmstr;
638	static char secretarray[1024];
639	const cfg_type_t *conftype = &cfg_type_rndcconf;
640	bool key_only = false;
641	const cfg_listelt_t *element;
642
643	if (!isc_file_exists(conffile)) {
644		conffile = admin_keyfile;
645		conftype = &cfg_type_rndckey;
646
647		if (c_flag) {
648			fatal("%s does not exist", admin_conffile);
649		}
650
651		if (!isc_file_exists(conffile)) {
652			fatal("neither %s nor %s was found", admin_conffile,
653			      admin_keyfile);
654		}
655		key_only = true;
656	} else if (!c_flag && isc_file_exists(admin_keyfile)) {
657		fprintf(stderr,
658			"WARNING: key file (%s) exists, but using "
659			"default configuration file (%s)\n",
660			admin_keyfile, admin_conffile);
661	}
662
663	DO("create parser", cfg_parser_create(mctx, log, pctxp));
664
665	/*
666	 * The parser will output its own errors, so DO() is not used.
667	 */
668	result = cfg_parse_file(*pctxp, conffile, conftype, &config);
669	if (result != ISC_R_SUCCESS) {
670		fatal("could not load rndc configuration");
671	}
672
673	if (!key_only) {
674		(void)cfg_map_get(config, "options", &options);
675	}
676
677	if (key_only && servername == NULL) {
678		servername = "127.0.0.1";
679	} else if (servername == NULL && options != NULL) {
680		const cfg_obj_t *defserverobj = NULL;
681		(void)cfg_map_get(options, "default-server", &defserverobj);
682		if (defserverobj != NULL) {
683			servername = cfg_obj_asstring(defserverobj);
684		}
685	}
686
687	if (servername == NULL) {
688		fatal("no server specified and no default");
689	}
690
691	if (!key_only) {
692		(void)cfg_map_get(config, "server", &servers);
693		if (servers != NULL) {
694			for (elt = cfg_list_first(servers); elt != NULL;
695			     elt = cfg_list_next(elt))
696			{
697				const char *name = NULL;
698				server = cfg_listelt_value(elt);
699				name = cfg_obj_asstring(
700					cfg_map_getname(server));
701				if (strcasecmp(name, servername) == 0) {
702					break;
703				}
704				server = NULL;
705			}
706		}
707	}
708
709	/*
710	 * Look for the name of the key to use.
711	 */
712	if (keyname != NULL) {
713		/* Was set on command line, do nothing. */
714	} else if (server != NULL) {
715		DO("get key for server", cfg_map_get(server, "key", &defkey));
716		keyname = cfg_obj_asstring(defkey);
717	} else if (options != NULL) {
718		DO("get default key",
719		   cfg_map_get(options, "default-key", &defkey));
720		keyname = cfg_obj_asstring(defkey);
721	} else if (!key_only) {
722		fatal("no key for server and no default");
723	}
724
725	/*
726	 * Get the key's definition.
727	 */
728	if (key_only) {
729		DO("get key", cfg_map_get(config, "key", &key));
730	} else {
731		DO("get config key list", cfg_map_get(config, "key", &keys));
732		for (elt = cfg_list_first(keys); elt != NULL;
733		     elt = cfg_list_next(elt))
734		{
735			const char *name = NULL;
736
737			key = cfg_listelt_value(elt);
738			name = cfg_obj_asstring(cfg_map_getname(key));
739			if (strcasecmp(name, keyname) == 0) {
740				break;
741			}
742		}
743		if (elt == NULL) {
744			fatal("no key definition for name %s", keyname);
745		}
746	}
747	(void)cfg_map_get(key, "secret", &secretobj);
748	(void)cfg_map_get(key, "algorithm", &algorithmobj);
749	if (secretobj == NULL || algorithmobj == NULL) {
750		fatal("key must have algorithm and secret");
751	}
752
753	secretstr = cfg_obj_asstring(secretobj);
754	algorithmstr = cfg_obj_asstring(algorithmobj);
755
756	if (strcasecmp(algorithmstr, "hmac-md5") == 0) {
757		algorithm = ISCCC_ALG_HMACMD5;
758	} else if (strcasecmp(algorithmstr, "hmac-sha1") == 0) {
759		algorithm = ISCCC_ALG_HMACSHA1;
760	} else if (strcasecmp(algorithmstr, "hmac-sha224") == 0) {
761		algorithm = ISCCC_ALG_HMACSHA224;
762	} else if (strcasecmp(algorithmstr, "hmac-sha256") == 0) {
763		algorithm = ISCCC_ALG_HMACSHA256;
764	} else if (strcasecmp(algorithmstr, "hmac-sha384") == 0) {
765		algorithm = ISCCC_ALG_HMACSHA384;
766	} else if (strcasecmp(algorithmstr, "hmac-sha512") == 0) {
767		algorithm = ISCCC_ALG_HMACSHA512;
768	} else {
769		fatal("unsupported algorithm: %s", algorithmstr);
770	}
771
772	secret.rstart = (unsigned char *)secretarray;
773	secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
774	DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
775	secret.rend = secret.rstart;
776	secret.rstart = (unsigned char *)secretarray;
777
778	/*
779	 * Find the port to connect to.
780	 */
781	if (remoteport != 0) {
782		/* Was set on command line, do nothing. */
783	} else {
784		if (server != NULL) {
785			(void)cfg_map_get(server, "port", &defport);
786		}
787		if (defport == NULL && options != NULL) {
788			(void)cfg_map_get(options, "default-port", &defport);
789		}
790	}
791	if (defport != NULL) {
792		remoteport = cfg_obj_asuint32(defport);
793		if (remoteport > 65535 || remoteport == 0) {
794			fatal("port %u out of range", remoteport);
795		}
796	} else if (remoteport == 0) {
797		remoteport = NS_CONTROL_PORT;
798	}
799
800	if (server != NULL) {
801		result = cfg_map_get(server, "addresses", &addresses);
802	} else {
803		result = ISC_R_NOTFOUND;
804	}
805	if (result == ISC_R_SUCCESS) {
806		for (element = cfg_list_first(addresses); element != NULL;
807		     element = cfg_list_next(element))
808		{
809			isc_sockaddr_t sa;
810
811			address = cfg_listelt_value(element);
812			if (!cfg_obj_issockaddr(address)) {
813				unsigned int myport;
814				const char *name;
815				const cfg_obj_t *obj;
816
817				obj = cfg_tuple_get(address, "name");
818				name = cfg_obj_asstring(obj);
819				obj = cfg_tuple_get(address, "port");
820				if (cfg_obj_isuint32(obj)) {
821					myport = cfg_obj_asuint32(obj);
822					if (myport > UINT16_MAX || myport == 0)
823					{
824						fatal("port %u out of range",
825						      myport);
826					}
827				} else {
828					myport = remoteport;
829				}
830				if (nserveraddrs < SERVERADDRS) {
831					get_addresses(name, (in_port_t)myport);
832				} else {
833					fprintf(stderr,
834						"too many address: "
835						"%s: dropped\n",
836						name);
837				}
838				continue;
839			}
840			sa = *cfg_obj_assockaddr(address);
841			if (isc_sockaddr_getport(&sa) == 0) {
842				isc_sockaddr_setport(&sa, remoteport);
843			}
844			if (nserveraddrs < SERVERADDRS) {
845				serveraddrs[nserveraddrs++] = sa;
846			} else {
847				char socktext[ISC_SOCKADDR_FORMATSIZE];
848
849				isc_sockaddr_format(&sa, socktext,
850						    sizeof(socktext));
851				fprintf(stderr,
852					"too many address: %s: dropped\n",
853					socktext);
854			}
855		}
856	}
857
858	if (!local4set && server != NULL) {
859		address = NULL;
860		cfg_map_get(server, "source-address", &address);
861		if (address != NULL) {
862			local4 = *cfg_obj_assockaddr(address);
863			local4set = true;
864		}
865	}
866	if (!local4set && options != NULL) {
867		address = NULL;
868		cfg_map_get(options, "default-source-address", &address);
869		if (address != NULL) {
870			local4 = *cfg_obj_assockaddr(address);
871			local4set = true;
872		}
873	}
874
875	if (!local6set && server != NULL) {
876		address = NULL;
877		cfg_map_get(server, "source-address-v6", &address);
878		if (address != NULL) {
879			local6 = *cfg_obj_assockaddr(address);
880			local6set = true;
881		}
882	}
883	if (!local6set && options != NULL) {
884		address = NULL;
885		cfg_map_get(options, "default-source-address-v6", &address);
886		if (address != NULL) {
887			local6 = *cfg_obj_assockaddr(address);
888			local6set = true;
889		}
890	}
891
892	*configp = config;
893}
894
895int
896main(int argc, char **argv) {
897	isc_result_t result = ISC_R_SUCCESS;
898	bool show_final_mem = false;
899	isc_log_t *log = NULL;
900	isc_logconfig_t *logconfig = NULL;
901	isc_logdestination_t logdest;
902	cfg_parser_t *pctx = NULL;
903	cfg_obj_t *config = NULL;
904	const char *keyname = NULL;
905	struct in_addr in;
906	struct in6_addr in6;
907	char *p;
908	size_t argslen;
909	int ch;
910	int i;
911
912	result = isc_file_progname(*argv, program, sizeof(program));
913	if (result != ISC_R_SUCCESS) {
914		memmove(program, "rndc", 5);
915	}
916	progname = program;
917
918	admin_conffile = RNDC_CONFFILE;
919	admin_keyfile = RNDC_KEYFILE;
920
921	isc_sockaddr_any(&local4);
922	isc_sockaddr_any6(&local6);
923
924	result = isc_app_start();
925	if (result != ISC_R_SUCCESS) {
926		fatal("isc_app_start() failed: %s", isc_result_totext(result));
927	}
928
929	isc_commandline_errprint = false;
930
931	preparse_args(argc, argv);
932
933	while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
934		switch (ch) {
935		case '4':
936			if (isc_net_probeipv4() != ISC_R_SUCCESS) {
937				fatal("can't find IPv4 networking");
938			}
939			isc_net_disableipv6();
940			break;
941		case '6':
942			if (isc_net_probeipv6() != ISC_R_SUCCESS) {
943				fatal("can't find IPv6 networking");
944			}
945			isc_net_disableipv4();
946			break;
947		case 'b':
948			if (inet_pton(AF_INET, isc_commandline_argument, &in) ==
949			    1)
950			{
951				isc_sockaddr_fromin(&local4, &in, 0);
952				local4set = true;
953			} else if (inet_pton(AF_INET6, isc_commandline_argument,
954					     &in6) == 1)
955			{
956				isc_sockaddr_fromin6(&local6, &in6, 0);
957				local6set = true;
958			}
959			break;
960
961		case 'c':
962			admin_conffile = isc_commandline_argument;
963			c_flag = true;
964			break;
965
966		case 'k':
967			admin_keyfile = isc_commandline_argument;
968			break;
969
970		case 'M':
971			isc_mem_debugging = ISC_MEM_DEBUGTRACE;
972			break;
973
974		case 'm':
975			show_final_mem = true;
976			break;
977
978		case 'p':
979			remoteport = atoi(isc_commandline_argument);
980			if (remoteport > 65535 || remoteport == 0) {
981				fatal("port '%s' out of range",
982				      isc_commandline_argument);
983			}
984			break;
985
986		case 'q':
987			quiet = true;
988			break;
989
990		case 'r':
991			showresult = true;
992			break;
993
994		case 's':
995			servername = isc_commandline_argument;
996			break;
997
998		case 'V':
999			verbose = true;
1000			break;
1001
1002		case 'y':
1003			keyname = isc_commandline_argument;
1004			break;
1005
1006		case '?':
1007			if (isc_commandline_option != '?') {
1008				fprintf(stderr, "%s: invalid argument -%c\n",
1009					program, isc_commandline_option);
1010				usage(1);
1011			}
1012			FALLTHROUGH;
1013		case 'h':
1014			usage(0);
1015			break;
1016		default:
1017			fprintf(stderr, "%s: unhandled option -%c\n", program,
1018				isc_commandline_option);
1019			exit(1);
1020		}
1021	}
1022
1023	argc -= isc_commandline_index;
1024	argv += isc_commandline_index;
1025
1026	if (argv[0] == NULL) {
1027		usage(1);
1028	} else {
1029		command = argv[0];
1030		if (strcmp(command, "restart") == 0) {
1031			fatal("'%s' is not implemented", command);
1032		}
1033		notify("%s", command);
1034	}
1035
1036	serial = isc_random32();
1037
1038	isc_mem_create(&rndc_mctx);
1039	isc_managers_create(rndc_mctx, 1, 0, &netmgr, &taskmgr, NULL);
1040	DO("create task", isc_task_create(taskmgr, 0, &rndc_task));
1041
1042	isc_nm_settimeouts(netmgr, RNDC_TIMEOUT, RNDC_TIMEOUT, RNDC_TIMEOUT, 0);
1043
1044	isc_log_create(rndc_mctx, &log, &logconfig);
1045	isc_log_setcontext(log);
1046	isc_log_settag(logconfig, progname);
1047	logdest.file.stream = stderr;
1048	logdest.file.name = NULL;
1049	logdest.file.versions = ISC_LOG_ROLLNEVER;
1050	logdest.file.maximum_size = 0;
1051	isc_log_createchannel(logconfig, "stderr", ISC_LOG_TOFILEDESC,
1052			      ISC_LOG_INFO, &logdest,
1053			      ISC_LOG_PRINTTAG | ISC_LOG_PRINTLEVEL);
1054	DO("enabling log channel",
1055	   isc_log_usechannel(logconfig, "stderr", NULL, NULL));
1056
1057	parse_config(rndc_mctx, log, keyname, &pctx, &config);
1058
1059	isc_buffer_allocate(rndc_mctx, &databuf, 2048);
1060
1061	/*
1062	 * Convert argc/argv into a space-delimited command string
1063	 * similar to what the user might enter in interactive mode
1064	 * (if that were implemented).
1065	 */
1066	argslen = 0;
1067	for (i = 0; i < argc; i++) {
1068		argslen += strlen(argv[i]) + 1;
1069	}
1070
1071	args = isc_mem_get(rndc_mctx, argslen);
1072
1073	p = args;
1074	for (i = 0; i < argc; i++) {
1075		size_t len = strlen(argv[i]);
1076		memmove(p, argv[i], len);
1077		p += len;
1078		*p++ = ' ';
1079	}
1080
1081	p--;
1082	*p++ = '\0';
1083	INSIST(p == args + argslen);
1084
1085	if (nserveraddrs == 0 && servername != NULL) {
1086		get_addresses(servername, (in_port_t)remoteport);
1087	}
1088
1089	DO("post event", isc_app_onrun(rndc_mctx, rndc_task, rndc_start, NULL));
1090
1091	result = isc_app_run();
1092	if (result != ISC_R_SUCCESS) {
1093		fatal("isc_app_run() failed: %s", isc_result_totext(result));
1094	}
1095
1096	isc_managers_destroy(&netmgr, &taskmgr, NULL);
1097
1098	/*
1099	 * Note: when TCP connections are shut down, there will be a final
1100	 * call to the isccc callback routine with &rndc_ccmsg as its
1101	 * argument. We therefore need to delay invalidating it until
1102	 * after the netmgr is closed down.
1103	 */
1104	isccc_ccmsg_invalidate(&rndc_ccmsg);
1105
1106	isc_log_destroy(&log);
1107	isc_log_setcontext(NULL);
1108
1109	cfg_obj_destroy(pctx, &config);
1110	cfg_parser_destroy(&pctx);
1111
1112	isc_mem_put(rndc_mctx, args, argslen);
1113
1114	isc_buffer_free(&databuf);
1115
1116	if (show_final_mem) {
1117		isc_mem_stats(rndc_mctx, stderr);
1118	}
1119
1120	isc_mem_destroy(&rndc_mctx);
1121
1122	if (failed) {
1123		return (1);
1124	}
1125
1126	return (0);
1127}
1128