rndc.c revision debd489a44363870f96f75818e89ec27d3cab736
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt/*
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
f3df966b81ba3d3db8412d8787af60ca05d3fce5Tinderbox User * Copyright (C) 2000-2003 Internet Software Consortium.
bf8267aa453e5d2a735ed732a043b77a0b355b20Mark Andrews *
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * Permission to use, copy, modify, and/or distribute this software for any
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * purpose with or without fee is hereby granted, provided that the above
0c27b3fe77ac1d5094ba3521e8142d9e7973133fMark Andrews * copyright notice and this permission notice appear in all copies.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt *
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt * PERFORMANCE OF THIS SOFTWARE.
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt */
079c9e6939ef8972bf0d13441738e6ef64505647Mark Andrews
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt/* $Id: rndc.c,v 1.126 2009/09/29 15:06:06 fdupont Exp $ */
095810f8cb710e0211c7db084191c2cad8e3c4c9Evan Hunt
095810f8cb710e0211c7db084191c2cad8e3c4c9Evan Hunt/*! \file */
095810f8cb710e0211c7db084191c2cad8e3c4c9Evan Hunt
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt/*
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt * Principal Author: DCL
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt */
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt#include <config.h>
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt#include <stdlib.h>
b47c020d5c635b662ac57e5485d266fd62c796c0Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/app.h>
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki#include <isc/buffer.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/commandline.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/file.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/log.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/net.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/mem.h>
6a4d6e3379f891a532d16c1c1d822676de26905cEvan Hunt#include <isc/random.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/socket.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isc/stdtime.h>
9a859983d7059a6eb9c877c1d2ac6a3a5b7170f7Evan Hunt#include <isc/string.h>
9a859983d7059a6eb9c877c1d2ac6a3a5b7170f7Evan Hunt#include <isc/task.h>
6a4d6e3379f891a532d16c1c1d822676de26905cEvan Hunt#include <isc/thread.h>
6a4d6e3379f891a532d16c1c1d822676de26905cEvan Hunt#include <isc/util.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isccfg/namedconf.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
6a4d6e3379f891a532d16c1c1d822676de26905cEvan Hunt#include <isccc/alist.h>
6a4d6e3379f891a532d16c1c1d822676de26905cEvan Hunt#include <isccc/base64.h>
6a4d6e3379f891a532d16c1c1d822676de26905cEvan Hunt#include <isccc/cc.h>
9a859983d7059a6eb9c877c1d2ac6a3a5b7170f7Evan Hunt#include <isccc/ccmsg.h>
6a4d6e3379f891a532d16c1c1d822676de26905cEvan Hunt#include <isccc/result.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isccc/sexpr.h>
6a4d6e3379f891a532d16c1c1d822676de26905cEvan Hunt#include <isccc/types.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <isccc/util.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <dns/name.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include <bind9/getaddresses.h>
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#include "util.h"
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt#define SERVERADDRS 10
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntconst char *progname;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntisc_boolean_t verbose;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic const char *admin_conffile;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic const char *admin_keyfile;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic const char *version = VERSION;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic const char *servername = NULL;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isc_sockaddr_t serveraddrs[SERVERADDRS];
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isc_sockaddr_t local4, local6;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isc_boolean_t local4set = ISC_FALSE, local6set = ISC_FALSE;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic int nserveraddrs;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic int currentaddr = 0;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic unsigned int remoteport = 0;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isc_socketmgr_t *socketmgr = NULL;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic unsigned char databuf[2048];
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isccc_ccmsg_t ccmsg;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isccc_region_t secret;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isc_boolean_t failed = ISC_FALSE;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isc_boolean_t c_flag = ISC_FALSE;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isc_mem_t *mctx;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic int sends, recvs, connects;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic char *command;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic char *args;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic char program[256];
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isc_socket_t *sock = NULL;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic isc_uint32_t serial;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task);
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan HuntISC_PLATFORM_NORETURN_PRE static void
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntusage(int status) ISC_PLATFORM_NORETURN_POST;
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntstatic void
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntusage(int status) {
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt fprintf(stderr, "\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan HuntUsage: %s [-c config] [-s server] [-p port]\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt [-k key-file ] [-y key] [-V] command\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Huntcommand is one of the following:\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt reload Reload configuration file and zones.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt reload zone [class [view]]\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt Reload a single zone.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt refresh zone [class [view]]\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt Schedule immediate maintenance for a zone.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt retransfer zone [class [view]]\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt Retransfer a single zone without checking serial number.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt freeze Suspend updates to all dynamic zones.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt freeze zone [class [view]]\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt Suspend updates to a dynamic zone.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt thaw Enable updates to all dynamic zones and reload them.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt thaw zone [class [view]]\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt Enable updates to a frozen dynamic zone and reload it.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt notify zone [class [view]]\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt Resend NOTIFY messages for the zone.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt reconfig Reload configuration file and new zones only.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt stats Write server statistics to the statistics file.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt querylog Toggle query logging.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt dumpdb [-all|-cache|-zones] [view ...]\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt Dump cache(s) to the dump file (named_dump.db).\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt stop Save pending updates to master files and stop the server.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt stop -p Save pending updates to master files and stop the server\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt reporting process id.\n\
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt halt Stop the server without saving pending updates.\n\
c514f38c801755da4dbe405139d8512873b332b0ckb halt -p Stop the server without saving pending updates reporting\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt process id.\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt trace Increment debugging level by one.\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt trace level Change the debugging level.\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt notrace Set debugging level to 0.\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt flush Flushes all of the server's caches.\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt flush [view] Flushes the server's cache for a view.\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt flushname name [view]\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt Flush the given name from the server's cache(s)\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt status Display status of the server.\n\
10a759cee69dcc3ce3a4d65e6e263c66e7f60ee8Evan Hunt recursing Dump the queries that are currently recursing (named.recursing)\n\
c514f38c801755da4dbe405139d8512873b332b0ckb validation newstate [view]\n\
079c9e6939ef8972bf0d13441738e6ef64505647Mark Andrews Enable / disable DNSSEC validation.\n\
079c9e6939ef8972bf0d13441738e6ef64505647Mark Andrews *restart Restart the server.\n\
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki\n\
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki* == not yet implemented\n\
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold KrecickiVersion: %s\n",
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki progname, version);
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki
079c9e6939ef8972bf0d13441738e6ef64505647Mark Andrews exit(status);
079c9e6939ef8972bf0d13441738e6ef64505647Mark Andrews}
079c9e6939ef8972bf0d13441738e6ef64505647Mark Andrews
079c9e6939ef8972bf0d13441738e6ef64505647Mark Andrewsstatic void
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecickiget_addresses(const char *host, in_port_t port) {
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki isc_result_t result;
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki int found = 0, count;
cc51cd2d2076e33117c60c9effcb8caccde4983bWitold Krecicki
c514f38c801755da4dbe405139d8512873b332b0ckb if (*host == '/') {
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs],
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn host);
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn if (result == ISC_R_SUCCESS)
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn nserveraddrs++;
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn } else {
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn count = SERVERADDRS - nserveraddrs;
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn result = bind9_getaddresses(host, port,
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn &serveraddrs[nserveraddrs],
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn count, &found);
0aadc6dd7b719539445e7a0a058b15dd9d982a9bMichał Kępień nserveraddrs += found;
0aadc6dd7b719539445e7a0a058b15dd9d982a9bMichał Kępień }
0aadc6dd7b719539445e7a0a058b15dd9d982a9bMichał Kępień if (result != ISC_R_SUCCESS)
0aadc6dd7b719539445e7a0a058b15dd9d982a9bMichał Kępień fatal("couldn't get address for '%s': %s",
0aadc6dd7b719539445e7a0a058b15dd9d982a9bMichał Kępień host, isc_result_totext(result));
0aadc6dd7b719539445e7a0a058b15dd9d982a9bMichał Kępień INSIST(nserveraddrs > 0);
0aadc6dd7b719539445e7a0a058b15dd9d982a9bMichał Kępień}
0aadc6dd7b719539445e7a0a058b15dd9d982a9bMichał Kępień
0aadc6dd7b719539445e7a0a058b15dd9d982a9bMichał Kępieństatic void
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburnrndc_senddone(isc_task_t *task, isc_event_t *event) {
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn isc_socketevent_t *sevent = (isc_socketevent_t *)event;
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn UNUSED(task);
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn sends--;
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn if (sevent->result != ISC_R_SUCCESS)
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn fatal("send failed: %s", isc_result_totext(sevent->result));
c8803902d6e740d1ed85e099835813466fa51391Curtis Blackburn isc_event_free(&event);
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt if (sends == 0 && recvs == 0) {
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt isc_socket_detach(&sock);
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt isc_task_shutdown(task);
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt }
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt}
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt
a165a17a81ff3285f4f4d79785fafb465e626183Evan Huntstatic void
a165a17a81ff3285f4f4d79785fafb465e626183Evan Huntrndc_recvdone(isc_task_t *task, isc_event_t *event) {
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt isccc_sexpr_t *response = NULL;
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt isccc_sexpr_t *data;
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt isccc_region_t source;
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt char *errormsg = NULL;
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt char *textmsg = NULL;
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt isc_result_t result;
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt
a165a17a81ff3285f4f4d79785fafb465e626183Evan Hunt recvs--;
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień if (ccmsg.result == ISC_R_EOF)
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień fatal("connection to remote host closed\n"
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień "This may indicate that\n"
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień "* the remote server is using an older version of"
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień " the command protocol,\n"
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień "* this host is not authorized to connect,\n"
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień "* the clocks are not synchronized, or\n"
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień "* the key is invalid.");
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień
f20ff8b74d21fa3e3f071544f6fd060d015cf27eMichał Kępień if (ccmsg.result != ISC_R_SUCCESS)
75b8de87879ad017c9cd2ffc328e5d2391d16e99Evan Hunt fatal("recv failed: %s", isc_result_totext(ccmsg.result));
3635d8f9104e70e141a8f191a0e6c1502ceed2f3Mark Andrews
source.rstart = isc_buffer_base(&ccmsg.buffer);
source.rend = isc_buffer_used(&ccmsg.buffer);
DO("parse message", isccc_cc_fromwire(&source, &response, &secret));
data = isccc_alist_lookup(response, "_data");
if (data == NULL)
fatal("no data section in response");
result = isccc_cc_lookupstring(data, "err", &errormsg);
if (result == ISC_R_SUCCESS) {
failed = ISC_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)
printf("%s\n", textmsg);
else if (result != ISC_R_NOTFOUND)
fprintf(stderr, "%s: parsing response failed: %s\n",
progname, isc_result_totext(result));
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;
isc_uint32_t nonce;
isccc_sexpr_t *request = NULL;
isccc_time_t now;
isc_region_t r;
isccc_sexpr_t *data;
isccc_region_t message;
isc_uint32_t len;
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, 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, &secret));
_ctrl = isccc_alist_lookup(response, "_ctrl");
if (_ctrl == NULL)
fatal("_ctrl section missing");
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");
}
message.rstart = databuf + 4;
message.rend = databuf + sizeof(databuf);
DO("render message", isccc_cc_towire(request, &message, &secret));
len = sizeof(databuf) - REGION_SIZE(message);
isc_buffer_init(&b, databuf, 4);
isc_buffer_putuint32(&b, len - 4);
r.length = len;
r.base = databuf;
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);
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;
isccc_region_t message;
isc_region_t r;
isc_uint32_t len;
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");
message.rstart = databuf + 4;
message.rend = databuf + sizeof(databuf);
DO("render message", isccc_cc_towire(request, &message, &secret));
len = sizeof(databuf) - REGION_SIZE(message);
isc_buffer_init(&b, databuf, 4);
isc_buffer_putuint32(&b, len - 4);
r.length = len;
r.base = databuf;
isccc_ccmsg_init(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);
}
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 *algorithm;
static char secretarray[1024];
const cfg_type_t *conftype = &cfg_type_rndcconf;
isc_boolean_t key_only = ISC_FALSE;
const cfg_listelt_t *element;
if (! isc_file_exists(conffile)) {
conffile = admin_keyfile;
conftype = &cfg_type_rndckey;
if (! isc_file_exists(conffile))
fatal("neither %s nor %s was found",
admin_conffile, admin_keyfile);
key_only = ISC_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);
algorithm = cfg_obj_asstring(algorithmobj);
if (strcasecmp(algorithm, "hmac-md5") != 0)
fatal("unsupported algorithm: %s", algorithm);
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 > ISC_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 = ISC_TRUE;
}
}
if (!local4set && options != NULL) {
address = NULL;
cfg_map_get(options, "default-source-address", &address);
if (address != NULL) {
local4 = *cfg_obj_assockaddr(address);
local4set = ISC_TRUE;
}
}
if (!local6set && server != NULL) {
address = NULL;
cfg_map_get(server, "source-address-v6", &address);
if (address != NULL) {
local6 = *cfg_obj_assockaddr(address);
local6set = ISC_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 = ISC_TRUE;
}
}
*configp = config;
}
int
main(int argc, char **argv) {
isc_boolean_t show_final_mem = ISC_FALSE;
isc_result_t result = ISC_R_SUCCESS;
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)
memcpy(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 = ISC_FALSE;
while ((ch = isc_commandline_parse(argc, argv, "b:c:hk:Mmp:s:Vy:"))
!= -1) {
switch (ch) {
case 'b':
if (inet_pton(AF_INET, isc_commandline_argument,
&in) == 1) {
isc_sockaddr_fromin(&local4, &in, 0);
local4set = ISC_TRUE;
} else if (inet_pton(AF_INET6, isc_commandline_argument,
&in6) == 1) {
isc_sockaddr_fromin6(&local6, &in6, 0);
local6set = ISC_TRUE;
}
break;
case 'c':
admin_conffile = isc_commandline_argument;
c_flag = ISC_TRUE;
break;
case 'k':
admin_keyfile = isc_commandline_argument;
break;
case 'M':
isc_mem_debugging = ISC_MEM_DEBUGTRACE;
break;
case 'm':
show_final_mem = ISC_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 's':
servername = isc_commandline_argument;
break;
case 'V':
verbose = ISC_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);
}
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);
isc_random_get(&serial);
DO("create memory context", isc_mem_create(0, 0, &mctx));
DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr));
DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr));
DO("create task", isc_task_create(taskmgr, 0, &task));
DO("create logging context", isc_log_create(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(mctx, log, keyname, &pctx, &config);
isccc_result_register();
command = *argv;
/*
* 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(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]);
memcpy(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(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(mctx, args, argslen);
isccc_ccmsg_invalidate(&ccmsg);
dns_name_destroy();
if (show_final_mem)
isc_mem_stats(mctx, stderr);
isc_mem_destroy(&mctx);
if (failed)
return (1);
return (0);
}