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