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