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