/* $NetBSD: rndc.c,v 1.3 2019/01/09 16:55:00 christos Exp $ */
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file */
#include <config.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <isc/app.h>
#include <isc/buffer.h>
#include <isc/commandline.h>
#include <isc/file.h>
#include <isc/log.h>
#include <isc/net.h>
#include <isc/mem.h>
#include <isc/print.h>
#include <isc/random.h>
#include <isc/socket.h>
#include <isc/stdtime.h>
#include <isc/string.h>
#include <isc/task.h>
#include <isc/thread.h>
#include <isc/util.h>
#include <pk11/site.h>
#include <isccfg/namedconf.h>
#include <isccc/alist.h>
#include <isccc/base64.h>
#include <isccc/cc.h>
#include <isccc/ccmsg.h>
#include <isccc/result.h>
#include <isccc/sexpr.h>
#include <isccc/types.h>
#include <isccc/util.h>
#include <dns/name.h>
#include <bind9/getaddresses.h>
#include "util.h"
#define SERVERADDRS 10
const char *progname;
bool verbose;
static const char *admin_conffile;
static const char *admin_keyfile;
static const char *version = VERSION;
static const char *servername = NULL;
static isc_sockaddr_t serveraddrs[SERVERADDRS];
static isc_sockaddr_t local4, local6;
static bool local4set = false, local6set = false;
static int nserveraddrs;
static int currentaddr = 0;
static unsigned int remoteport = 0;
static isc_socketmgr_t *socketmgr = NULL;
static isc_buffer_t *databuf;
static isccc_ccmsg_t ccmsg;
static uint32_t algorithm;
static isccc_region_t secret;
static bool failed = false;
static bool c_flag = false;
static isc_mem_t *rndc_mctx;
static int sends, recvs, connects;
static char *command;
static char *args;
static char program[256];
static isc_socket_t *sock = NULL;
static uint32_t serial;
static bool quiet = false;
static bool showresult = false;
static void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);
ISC_PLATFORM_NORETURN_PRE static void
usage(int status) ISC_PLATFORM_NORETURN_POST;
static void
usage(int status) {
fprintf(stderr, "\
Usage: %s [-b address] [-c config] [-s server] [-p port]\n\
[-k key-file ] [-y key] [-r] [-V] [-4 | -6] command\n\
\n\
command is one of the following:\n\
\n\
addzone zone [class [view]] { zone-options }\n\
Add zone to given view. Requires allow-new-zones option.\n\
delzone [-clean] zone [class [view]]\n\
Removes zone from given view.\n\
dnstap -reopen\n\
Close, truncate and re-open the DNSTAP output file.\n\
dnstap -roll count\n\
Close, rename and re-open the DNSTAP output file(s).\n\
dumpdb [-all|-cache|-zones|-adb|-bad|-fail] [view ...]\n\
Dump cache(s) to the dump file (named_dump.db).\n\
flush Flushes all of the server's caches.\n\
flush [view] Flushes the server's cache for a view.\n\
flushname name [view]\n\
Flush the given name from the server's cache(s)\n\
flushtree name [view]\n\
Flush all names under the given name from the server's cache(s)\n\
freeze Suspend updates to all dynamic zones.\n\
freeze zone [class [view]]\n\
Suspend updates to a dynamic zone.\n\
halt Stop the server without saving pending updates.\n\
halt -p Stop the server without saving pending updates reporting\n\
process id.\n\
loadkeys zone [class [view]]\n\
Update keys without signing immediately.\n\
managed-keys refresh [class [view]]\n\
Check trust anchor for RFC 5011 key changes\n\
managed-keys status [class [view]]\n\
Display RFC 5011 managed keys information\n\
managed-keys sync [class [view]]\n\
Write RFC 5011 managed keys to disk\n\
modzone zone [class [view]] { zone-options }\n\
Modify a zone's configuration.\n\
Requires allow-new-zones option.\n\
notify zone [class [view]]\n\
Resend NOTIFY messages for the zone.\n\
notrace Set debugging level to 0.\n\
nta -dump\n\
List all negative trust anchors.\n\
nta [-lifetime duration] [-force] domain [view]\n\
Set a negative trust anchor, disabling DNSSEC validation\n\
for the given domain.\n\
Using -lifetime specifies the duration of the NTA, up\n\
to one week.\n\
Using -force prevents the NTA from expiring before its\n\
full lifetime, even if the domain can validate sooner.\n\
nta -remove domain [view]\n\
Remove a negative trust anchor, re-enabling validation\n\
for the given domain.\n\
querylog [ on | off ]\n\
Enable / disable query logging.\n\
reconfig Reload configuration file and new zones only.\n\
recursing Dump the queries that are currently recursing (named.recursing)\n\
refresh zone [class [view]]\n\
Schedule immediate maintenance for a zone.\n\
reload Reload configuration file and zones.\n\
reload zone [class [view]]\n\
Reload a single zone.\n\
retransfer zone [class [view]]\n\
Retransfer a single zone without checking serial number.\n\
scan Scan available network interfaces for changes.\n\
secroots [view ...]\n\
Write security roots to the secroots file.\n\
serve-stale [ yes | no | reset | status ] [class [view]]\n\
Control whether stale answers are returned\n\
showzone zone [class [view]]\n\
Print a zone's configuration.\n\
sign zone [class [view]]\n\
Update zone keys, and sign as needed.\n\
signing -clear all zone [class [view]]\n\
Remove the private records for all keys that have\n\
finished signing the given zone.\n\
signing -clear <keyid>/<algorithm> zone [class [view]]\n\
Remove the private record that indicating the given key\n\
has finished signing the given zone.\n\
signing -list zone [class [view]]\n\
List the private records showing the state of DNSSEC\n\
signing in the given zone.\n\
signing -nsec3param hash flags iterations salt zone [class [view]]\n\
Add NSEC3 chain to zone if already signed.\n\
Prime zone with NSEC3 chain if not yet signed.\n\
signing -nsec3param none zone [class [view]]\n\
Remove NSEC3 chains from zone.\n\
signing -serial <value> zone [class [view]]\n\
Set the zones's serial to <value>.\n\
stats Write server statistics to the statistics file.\n\
status Display status of the server.\n\
stop Save pending updates to master files and stop the server.\n\
stop -p Save pending updates to master files and stop the server\n\
reporting process id.\n\
sync [-clean] Dump changes to all dynamic zones to disk, and optionally\n\
remove their journal files.\n\
sync [-clean] zone [class [view]]\n\
Dump a single zone's changes to disk, and optionally\n\
remove its journal file.\n\
tcp-timeouts Display the tcp-*-timeout option values\n\
tcp-timeouts initial idle keepalive advertised\n\
Update the tcp-*-timeout option values\n\
thaw Enable updates to all dynamic zones and reload them.\n\
thaw zone [class [view]]\n\
Enable updates to a frozen dynamic zone and reload it.\n\
trace Increment debugging level by one.\n\
trace level Change the debugging level.\n\
tsig-delete keyname [view]\n\
Delete a TKEY-negotiated TSIG key.\n\
tsig-list List all currently active TSIG keys, including both statically\n\
configured and TKEY-negotiated keys.\n\
validation [ yes | no | status ] [view]\n\
Enable / disable DNSSEC validation.\n\
zonestatus zone [class [view]]\n\
Display the current status of a zone.\n\
\n\
Version: %s\n",
progname, version);
exit(status);
}
#define CMDLINE_FLAGS "46b:c:hk:Mmp:qrs:Vy:"
static void
preparse_args(int argc, char **argv) {
bool ipv4only = false, ipv6only = false;
int ch;
while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
switch (ch) {
case '4':
if (ipv6only) {
fatal("only one of -4 and -6 allowed");
}
ipv4only = true;
break;
case '6':
if (ipv4only) {
fatal("only one of -4 and -6 allowed");
}
ipv6only = true;
break;
default:
break;
}
}
isc_commandline_reset = true;
isc_commandline_index = 1;
}
static void
get_addresses(const char *host, in_port_t port) {
isc_result_t result;
int found = 0, count;
if (*host == '/') {
result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
host);
if (result == ISC_R_SUCCESS)
nserveraddrs++;
} else {
count = SERVERADDRS - nserveraddrs;
result = bind9_getaddresses(host, port,
&serveraddrs[nserveraddrs],
count, &found);
nserveraddrs += found;
}
if (result != ISC_R_SUCCESS)
fatal("couldn't get address for '%s': %s",
host, isc_result_totext(result));
INSIST(nserveraddrs > 0);
}
static void
rndc_senddone(isc_task_t *task, isc_event_t *event) {
isc_socketevent_t *sevent = (isc_socketevent_t *)event;
UNUSED(task);
sends--;
if (sevent->result != ISC_R_SUCCESS)
fatal("send failed: %s", isc_result_totext(sevent->result));
isc_event_free(&event);
if (sends == 0 && recvs == 0) {
isc_socket_detach(&sock);
isc_task_shutdown(task);
RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
}
}
static void
rndc_recvdone(isc_task_t *task, isc_event_t *event) {
isccc_sexpr_t *response = NULL;
isccc_sexpr_t *data;
isccc_region_t source;
char *errormsg = NULL;
char *textmsg = NULL;
isc_result_t result;
recvs--;
if (ccmsg.result == ISC_R_EOF)
fatal("connection to remote host closed\n"
"This may indicate that\n"
"* the remote server is using an older version of"
" the command protocol,\n"
"* this host is not authorized to connect,\n"
"* the clocks are not synchronized, or\n"
"* the key is invalid.");
if (ccmsg.result != ISC_R_SUCCESS)
fatal("recv failed: %s", isc_result_totext(ccmsg.result));
source.rstart = isc_buffer_base(&ccmsg.buffer);
source.rend = isc_buffer_used(&ccmsg.buffer);
DO("parse message",
isccc_cc_fromwire(&source, &response, algorithm, &secret));
data = isccc_alist_lookup(response, "_data");
if (!isccc_alist_alistp(data))
fatal("bad or missing data section in response");
result = isccc_cc_lookupstring(data, "err", &errormsg);
if (result == ISC_R_SUCCESS) {
failed = true;
fprintf(stderr, "%s: '%s' failed: %s\n",
progname, command, errormsg);
}
else if (result != ISC_R_NOTFOUND)
fprintf(stderr, "%s: parsing response failed: %s\n",
progname, isc_result_totext(result));
result = isccc_cc_lookupstring(data, "text", &textmsg);
if (result == ISC_R_SUCCESS) {
if ((!quiet || failed) && strlen(textmsg) != 0U)
fprintf(failed ? stderr : stdout, "%s\n", textmsg);
} else if (result != ISC_R_NOTFOUND)
fprintf(stderr, "%s: parsing response failed: %s\n",
progname, isc_result_totext(result));
if (showresult) {
isc_result_t eresult;
result = isccc_cc_lookupuint32(data, "result", &eresult);
if (result == ISC_R_SUCCESS)
printf("%s %u\n", isc_result_toid(eresult), eresult);
else
printf("NONE -1\n");
}
isc_event_free(&event);
isccc_sexpr_free(&response);
if (sends == 0 && recvs == 0) {
isc_socket_detach(&sock);
isc_task_shutdown(task);
RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
}
}
static void
rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
isccc_sexpr_t *response = NULL;
isccc_sexpr_t *_ctrl;
isccc_region_t source;
isc_result_t result;
uint32_t nonce;
isccc_sexpr_t *request = NULL;
isccc_time_t now;
isc_region_t r;
isccc_sexpr_t *data;
isc_buffer_t b;
recvs--;
if (ccmsg.result == ISC_R_EOF)
fatal("connection to remote host closed\n"
"This may indicate that\n"
"* the remote server is using an older version of"
" the command protocol,\n"
"* this host is not authorized to connect,\n"
"* the clocks are not synchronized,\n"
"* the key signing algorithm is incorrect, or\n"
"* the key is invalid.");
if (ccmsg.result != ISC_R_SUCCESS)
fatal("recv failed: %s", isc_result_totext(ccmsg.result));
source.rstart = isc_buffer_base(&ccmsg.buffer);
source.rend = isc_buffer_used(&ccmsg.buffer);
DO("parse message",
isccc_cc_fromwire(&source, &response, algorithm, &secret));
_ctrl = isccc_alist_lookup(response, "_ctrl");
if (!isccc_alist_alistp(_ctrl))
fatal("bad or missing ctrl section in response");
nonce = 0;
if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
nonce = 0;
isc_stdtime_get(&now);
DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
now, now + 60, &request));
data = isccc_alist_lookup(request, "_data");
if (data == NULL)
fatal("_data section missing");
if (isccc_cc_definestring(data, "type", args) == NULL)
fatal("out of memory");
if (nonce != 0) {
_ctrl = isccc_alist_lookup(request, "_ctrl");
if (_ctrl == NULL)
fatal("_ctrl section missing");
if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
fatal("out of memory");
}
isc_buffer_clear(databuf);
/* Skip the length field (4 bytes) */
isc_buffer_add(databuf, 4);
DO("render message",
isccc_cc_towire(request, &databuf, algorithm, &secret));
isc_buffer_init(&b, databuf->base, 4);
isc_buffer_putuint32(&b, databuf->used - 4);
r.base = databuf->base;
r.length = databuf->used;
isccc_ccmsg_cancelread(&ccmsg);
DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
rndc_recvdone, NULL));
recvs++;
DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
NULL));
sends++;
isc_event_free(&event);
isccc_sexpr_free(&response);
isccc_sexpr_free(&request);
return;
}
static void
rndc_connected(isc_task_t *task, isc_event_t *event) {
char socktext[ISC_SOCKADDR_FORMATSIZE];
isc_socketevent_t *sevent = (isc_socketevent_t *)event;
isccc_sexpr_t *request = NULL;
isccc_sexpr_t *data;
isccc_time_t now;
isc_region_t r;
isc_buffer_t b;
isc_result_t result;
connects--;
if (sevent->result != ISC_R_SUCCESS) {
isc_sockaddr_format(&serveraddrs[currentaddr], socktext,
sizeof(socktext));
if (sevent->result != ISC_R_CANCELED &&
++currentaddr < nserveraddrs)
{
notify("connection failed: %s: %s", socktext,
isc_result_totext(sevent->result));
isc_socket_detach(&sock);
isc_event_free(&event);
rndc_startconnect(&serveraddrs[currentaddr], task);
return;
} else
fatal("connect failed: %s: %s", socktext,
isc_result_totext(sevent->result));
}
isc_stdtime_get(&now);
DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
now, now + 60, &request));
data = isccc_alist_lookup(request, "_data");
if (data == NULL)
fatal("_data section missing");
if (isccc_cc_definestring(data, "type", "null") == NULL)
fatal("out of memory");
isc_buffer_clear(databuf);
/* Skip the length field (4 bytes) */
isc_buffer_add(databuf, 4);
DO("render message",
isccc_cc_towire(request, &databuf, algorithm, &secret));
isc_buffer_init(&b, databuf->base, 4);
isc_buffer_putuint32(&b, databuf->used - 4);
r.base = databuf->base;
r.length = databuf->used;
isccc_ccmsg_init(rndc_mctx, sock, &ccmsg);
isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024);
DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
rndc_recvnonce, NULL));
recvs++;
DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
NULL));
sends++;
isc_event_free(&event);
isccc_sexpr_free(&request);
}
static void
rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) {
isc_result_t result;
int pf;
isc_sockettype_t type;
char socktext[ISC_SOCKADDR_FORMATSIZE];
isc_sockaddr_format(addr, socktext, sizeof(socktext));
notify("using server %s (%s)", servername, socktext);
pf = isc_sockaddr_pf(addr);
if (pf == AF_INET || pf == AF_INET6)
type = isc_sockettype_tcp;
else
type = isc_sockettype_unix;
DO("create socket", isc_socket_create(socketmgr, pf, type, &sock));
switch (isc_sockaddr_pf(addr)) {
case AF_INET:
DO("bind socket", isc_socket_bind(sock, &local4, 0));
break;
case AF_INET6:
DO("bind socket", isc_socket_bind(sock, &local6, 0));
break;
default:
break;
}
DO("connect", isc_socket_connect(sock, addr, task, rndc_connected,
NULL));
connects++;
}
static void
rndc_start(isc_task_t *task, isc_event_t *event) {
isc_event_free(&event);
currentaddr = 0;
rndc_startconnect(&serveraddrs[currentaddr], task);
}
static void
parse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname,
cfg_parser_t **pctxp, cfg_obj_t **configp)
{
isc_result_t result;
const char *conffile = admin_conffile;
const cfg_obj_t *addresses = NULL;
const cfg_obj_t *defkey = NULL;
const cfg_obj_t *options = NULL;
const cfg_obj_t *servers = NULL;
const cfg_obj_t *server = NULL;
const cfg_obj_t *keys = NULL;
const cfg_obj_t *key = NULL;
const cfg_obj_t *defport = NULL;
const cfg_obj_t *secretobj = NULL;
const cfg_obj_t *algorithmobj = NULL;
cfg_obj_t *config = NULL;
const cfg_obj_t *address = NULL;
const cfg_listelt_t *elt;
const char *secretstr;
const char *algorithmstr;
static char secretarray[1024];
const cfg_type_t *conftype = &cfg_type_rndcconf;
bool key_only = false;
const cfg_listelt_t *element;
if (! isc_file_exists(conffile)) {
conffile = admin_keyfile;
conftype = &cfg_type_rndckey;
if (c_flag)
fatal("%s does not exist", admin_conffile);
if (! isc_file_exists(conffile))
fatal("neither %s nor %s was found",
admin_conffile, admin_keyfile);
key_only = true;
} else if (! c_flag && isc_file_exists(admin_keyfile)) {
fprintf(stderr, "WARNING: key file (%s) exists, but using "
"default configuration file (%s)\n",
admin_keyfile, admin_conffile);
}
DO("create parser", cfg_parser_create(mctx, log, pctxp));
/*
* The parser will output its own errors, so DO() is not used.
*/
result = cfg_parse_file(*pctxp, conffile, conftype, &config);
if (result != ISC_R_SUCCESS)
fatal("could not load rndc configuration");
if (!key_only)
(void)cfg_map_get(config, "options", &options);
if (key_only && servername == NULL)
servername = "127.0.0.1";
else if (servername == NULL && options != NULL) {
const cfg_obj_t *defserverobj = NULL;
(void)cfg_map_get(options, "default-server", &defserverobj);
if (defserverobj != NULL)
servername = cfg_obj_asstring(defserverobj);
}
if (servername == NULL)
fatal("no server specified and no default");
if (!key_only) {
(void)cfg_map_get(config, "server", &servers);
if (servers != NULL) {
for (elt = cfg_list_first(servers);
elt != NULL;
elt = cfg_list_next(elt))
{
const char *name;
server = cfg_listelt_value(elt);
name = cfg_obj_asstring(cfg_map_getname(server));
if (strcasecmp(name, servername) == 0)
break;
server = NULL;
}
}
}
/*
* Look for the name of the key to use.
*/
if (keyname != NULL)
; /* Was set on command line, do nothing. */
else if (server != NULL) {
DO("get key for server", cfg_map_get(server, "key", &defkey));
keyname = cfg_obj_asstring(defkey);
} else if (options != NULL) {
DO("get default key", cfg_map_get(options, "default-key",
&defkey));
keyname = cfg_obj_asstring(defkey);
} else if (!key_only)
fatal("no key for server and no default");
/*
* Get the key's definition.
*/
if (key_only)
DO("get key", cfg_map_get(config, "key", &key));
else {
DO("get config key list", cfg_map_get(config, "key", &keys));
for (elt = cfg_list_first(keys);
elt != NULL;
elt = cfg_list_next(elt))
{
key = cfg_listelt_value(elt);
if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)),
keyname) == 0)
break;
}
if (elt == NULL)
fatal("no key definition for name %s", keyname);
}
(void)cfg_map_get(key, "secret", &secretobj);
(void)cfg_map_get(key, "algorithm", &algorithmobj);
if (secretobj == NULL || algorithmobj == NULL)
fatal("key must have algorithm and secret");
secretstr = cfg_obj_asstring(secretobj);
algorithmstr = cfg_obj_asstring(algorithmobj);
if (strcasecmp(algorithmstr, "hmac-md5") == 0) {
algorithm = ISCCC_ALG_HMACMD5;
} else if (strcasecmp(algorithmstr, "hmac-sha1") == 0) {
algorithm = ISCCC_ALG_HMACSHA1;
} else if (strcasecmp(algorithmstr, "hmac-sha224") == 0) {
algorithm = ISCCC_ALG_HMACSHA224;
} else if (strcasecmp(algorithmstr, "hmac-sha256") == 0) {
algorithm = ISCCC_ALG_HMACSHA256;
} else if (strcasecmp(algorithmstr, "hmac-sha384") == 0) {
algorithm = ISCCC_ALG_HMACSHA384;
} else if (strcasecmp(algorithmstr, "hmac-sha512") == 0) {
algorithm = ISCCC_ALG_HMACSHA512;
} else {
fatal("unsupported algorithm: %s", algorithmstr);
}
secret.rstart = (unsigned char *)secretarray;
secret.rend = (unsigned char *)secretarray + sizeof(secretarray);
DO("decode base64 secret", isccc_base64_decode(secretstr, &secret));
secret.rend = secret.rstart;
secret.rstart = (unsigned char *)secretarray;
/*
* Find the port to connect to.
*/
if (remoteport != 0)
; /* Was set on command line, do nothing. */
else {
if (server != NULL)
(void)cfg_map_get(server, "port", &defport);
if (defport == NULL && options != NULL)
(void)cfg_map_get(options, "default-port", &defport);
}
if (defport != NULL) {
remoteport = cfg_obj_asuint32(defport);
if (remoteport > 65535 || remoteport == 0)
fatal("port %u out of range", remoteport);
} else if (remoteport == 0)
remoteport = NS_CONTROL_PORT;
if (server != NULL)
result = cfg_map_get(server, "addresses", &addresses);
else
result = ISC_R_NOTFOUND;
if (result == ISC_R_SUCCESS) {
for (element = cfg_list_first(addresses);
element != NULL;
element = cfg_list_next(element))
{
isc_sockaddr_t sa;
address = cfg_listelt_value(element);
if (!cfg_obj_issockaddr(address)) {
unsigned int myport;
const char *name;
const cfg_obj_t *obj;
obj = cfg_tuple_get(address, "name");
name = cfg_obj_asstring(obj);
obj = cfg_tuple_get(address, "port");
if (cfg_obj_isuint32(obj)) {
myport = cfg_obj_asuint32(obj);
if (myport > UINT16_MAX ||
myport == 0)
fatal("port %u out of range",
myport);
} else
myport = remoteport;
if (nserveraddrs < SERVERADDRS)
get_addresses(name, (in_port_t) myport);
else
fprintf(stderr, "too many address: "
"%s: dropped\n", name);
continue;
}
sa = *cfg_obj_assockaddr(address);
if (isc_sockaddr_getport(&sa) == 0)
isc_sockaddr_setport(&sa, remoteport);
if (nserveraddrs < SERVERADDRS)
serveraddrs[nserveraddrs++] = sa;
else {
char socktext[ISC_SOCKADDR_FORMATSIZE];
isc_sockaddr_format(&sa, socktext,
sizeof(socktext));
fprintf(stderr,
"too many address: %s: dropped\n",
socktext);
}
}
}
if (!local4set && server != NULL) {
address = NULL;
cfg_map_get(server, "source-address", &address);
if (address != NULL) {
local4 = *cfg_obj_assockaddr(address);
local4set = true;
}
}
if (!local4set && options != NULL) {
address = NULL;
cfg_map_get(options, "default-source-address", &address);
if (address != NULL) {
local4 = *cfg_obj_assockaddr(address);
local4set = true;
}
}
if (!local6set && server != NULL) {
address = NULL;
cfg_map_get(server, "source-address-v6", &address);
if (address != NULL) {
local6 = *cfg_obj_assockaddr(address);
local6set = true;
}
}
if (!local6set && options != NULL) {
address = NULL;
cfg_map_get(options, "default-source-address-v6", &address);
if (address != NULL) {
local6 = *cfg_obj_assockaddr(address);
local6set = true;
}
}
*configp = config;
}
int
main(int argc, char **argv) {
isc_result_t result = ISC_R_SUCCESS;
bool show_final_mem = false;
isc_taskmgr_t *taskmgr = NULL;
isc_task_t *task = NULL;
isc_log_t *log = NULL;
isc_logconfig_t *logconfig = NULL;
isc_logdestination_t logdest;
cfg_parser_t *pctx = NULL;
cfg_obj_t *config = NULL;
const char *keyname = NULL;
struct in_addr in;
struct in6_addr in6;
char *p;
size_t argslen;
int ch;
int i;
result = isc_file_progname(*argv, program, sizeof(program));
if (result != ISC_R_SUCCESS)
memmove(program, "rndc", 5);
progname = program;
admin_conffile = RNDC_CONFFILE;
admin_keyfile = RNDC_KEYFILE;
isc_sockaddr_any(&local4);
isc_sockaddr_any6(&local6);
result = isc_app_start();
if (result != ISC_R_SUCCESS)
fatal("isc_app_start() failed: %s", isc_result_totext(result));
isc_commandline_errprint = false;
preparse_args(argc, argv);
while ((ch = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
switch (ch) {
case '4':
if (isc_net_probeipv4() != ISC_R_SUCCESS) {
fatal("can't find IPv4 networking");
}
isc_net_disableipv6();
break;
case '6':
if (isc_net_probeipv6() != ISC_R_SUCCESS) {
fatal("can't find IPv6 networking");
}
isc_net_disableipv4();
break;
case 'b':
if (inet_pton(AF_INET, isc_commandline_argument,
&in) == 1) {
isc_sockaddr_fromin(&local4, &in, 0);
local4set = true;
} else if (inet_pton(AF_INET6, isc_commandline_argument,
&in6) == 1) {
isc_sockaddr_fromin6(&local6, &in6, 0);
local6set = true;
}
break;
case 'c':
admin_conffile = isc_commandline_argument;
c_flag = true;
break;
case 'k':
admin_keyfile = isc_commandline_argument;
break;
case 'M':
isc_mem_debugging = ISC_MEM_DEBUGTRACE;
break;
case 'm':
show_final_mem = true;
break;
case 'p':
remoteport = atoi(isc_commandline_argument);
if (remoteport > 65535 || remoteport == 0)
fatal("port '%s' out of range",
isc_commandline_argument);
break;
case 'q':
quiet = true;
break;
case 'r':
showresult = true;
break;
case 's':
servername = isc_commandline_argument;
break;
case 'V':
verbose = true;
break;
case 'y':
keyname = isc_commandline_argument;
break;
case '?':
if (isc_commandline_option != '?') {
fprintf(stderr, "%s: invalid argument -%c\n",
program, isc_commandline_option);
usage(1);
}
/* FALLTHROUGH */
case 'h':
usage(0);
break;
default:
fprintf(stderr, "%s: unhandled option -%c\n",
program, isc_commandline_option);
exit(1);
}
}
argc -= isc_commandline_index;
argv += isc_commandline_index;
if (argc < 1)
usage(1);
serial = isc_random32();
DO("create memory context", isc_mem_create(0, 0, &rndc_mctx));
DO("create socket manager", isc_socketmgr_create(rndc_mctx, &socketmgr));
DO("create task manager", isc_taskmgr_create(rndc_mctx, 1, 0, &taskmgr));
DO("create task", isc_task_create(taskmgr, 0, &task));
DO("create logging context", isc_log_create(rndc_mctx, &log, &logconfig));
isc_log_setcontext(log);
DO("setting log tag", isc_log_settag(logconfig, progname));
logdest.file.stream = stderr;
logdest.file.name = NULL;
logdest.file.versions = ISC_LOG_ROLLNEVER;
logdest.file.maximum_size = 0;
DO("creating log channel",
isc_log_createchannel(logconfig, "stderr",
ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest,
ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL));
DO("enabling log channel", isc_log_usechannel(logconfig, "stderr",
NULL, NULL));
parse_config(rndc_mctx, log, keyname, &pctx, &config);
isccc_result_register();
command = *argv;
DO("allocate data buffer",
isc_buffer_allocate(rndc_mctx, &databuf, 2048));
/*
* Convert argc/argv into a space-delimited command string
* similar to what the user might enter in interactive mode
* (if that were implemented).
*/
argslen = 0;
for (i = 0; i < argc; i++)
argslen += strlen(argv[i]) + 1;
args = isc_mem_get(rndc_mctx, argslen);
if (args == NULL)
DO("isc_mem_get", ISC_R_NOMEMORY);
p = args;
for (i = 0; i < argc; i++) {
size_t len = strlen(argv[i]);
memmove(p, argv[i], len);
p += len;
*p++ = ' ';
}
p--;
*p++ = '\0';
INSIST(p == args + argslen);
notify("%s", command);
if (strcmp(command, "restart") == 0)
fatal("'%s' is not implemented", command);
if (nserveraddrs == 0)
get_addresses(servername, (in_port_t) remoteport);
DO("post event", isc_app_onrun(rndc_mctx, task, rndc_start, NULL));
result = isc_app_run();
if (result != ISC_R_SUCCESS)
fatal("isc_app_run() failed: %s", isc_result_totext(result));
if (connects > 0 || sends > 0 || recvs > 0)
isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL);
isc_task_detach(&task);
isc_taskmgr_destroy(&taskmgr);
isc_socketmgr_destroy(&socketmgr);
isc_log_destroy(&log);
isc_log_setcontext(NULL);
cfg_obj_destroy(pctx, &config);
cfg_parser_destroy(&pctx);
isc_mem_put(rndc_mctx, args, argslen);
isccc_ccmsg_invalidate(&ccmsg);
dns_name_destroy();
isc_buffer_free(&databuf);
if (show_final_mem)
isc_mem_stats(rndc_mctx, stderr);
isc_mem_destroy(&rndc_mctx);
if (failed)
return (1);
return (0);
}