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