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