1135446Strhodes/* 2262706Serwin * Copyright (C) 2004-2014 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 2000-2003 Internet Software Consortium. 4135446Strhodes * 5182645Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18254897Serwin/* $Id$ */ 19135446Strhodes 20170222Sdougb/*! \file */ 21170222Sdougb 22135446Strhodes/* 23135446Strhodes * Principal Author: DCL 24135446Strhodes */ 25135446Strhodes 26135446Strhodes#include <config.h> 27135446Strhodes 28135446Strhodes#include <stdlib.h> 29135446Strhodes 30135446Strhodes#include <isc/app.h> 31135446Strhodes#include <isc/buffer.h> 32135446Strhodes#include <isc/commandline.h> 33135446Strhodes#include <isc/file.h> 34135446Strhodes#include <isc/log.h> 35170222Sdougb#include <isc/net.h> 36135446Strhodes#include <isc/mem.h> 37135446Strhodes#include <isc/random.h> 38135446Strhodes#include <isc/socket.h> 39135446Strhodes#include <isc/stdtime.h> 40135446Strhodes#include <isc/string.h> 41135446Strhodes#include <isc/task.h> 42135446Strhodes#include <isc/thread.h> 43135446Strhodes#include <isc/util.h> 44135446Strhodes 45135446Strhodes#include <isccfg/namedconf.h> 46135446Strhodes 47135446Strhodes#include <isccc/alist.h> 48135446Strhodes#include <isccc/base64.h> 49135446Strhodes#include <isccc/cc.h> 50135446Strhodes#include <isccc/ccmsg.h> 51135446Strhodes#include <isccc/result.h> 52135446Strhodes#include <isccc/sexpr.h> 53135446Strhodes#include <isccc/types.h> 54135446Strhodes#include <isccc/util.h> 55135446Strhodes 56170222Sdougb#include <dns/name.h> 57170222Sdougb 58135446Strhodes#include <bind9/getaddresses.h> 59135446Strhodes 60135446Strhodes#include "util.h" 61135446Strhodes 62135446Strhodes#define SERVERADDRS 10 63135446Strhodes 64186462Sdougbconst char *progname; 65135446Strhodesisc_boolean_t verbose; 66135446Strhodes 67135446Strhodesstatic const char *admin_conffile; 68135446Strhodesstatic const char *admin_keyfile; 69135446Strhodesstatic const char *version = VERSION; 70135446Strhodesstatic const char *servername = NULL; 71135446Strhodesstatic isc_sockaddr_t serveraddrs[SERVERADDRS]; 72170222Sdougbstatic isc_sockaddr_t local4, local6; 73170222Sdougbstatic isc_boolean_t local4set = ISC_FALSE, local6set = ISC_FALSE; 74135446Strhodesstatic int nserveraddrs; 75135446Strhodesstatic int currentaddr = 0; 76135446Strhodesstatic unsigned int remoteport = 0; 77135446Strhodesstatic isc_socketmgr_t *socketmgr = NULL; 78135446Strhodesstatic unsigned char databuf[2048]; 79135446Strhodesstatic isccc_ccmsg_t ccmsg; 80135446Strhodesstatic isccc_region_t secret; 81135446Strhodesstatic isc_boolean_t failed = ISC_FALSE; 82224092Sdougbstatic isc_boolean_t c_flag = ISC_FALSE; 83135446Strhodesstatic isc_mem_t *mctx; 84135446Strhodesstatic int sends, recvs, connects; 85135446Strhodesstatic char *command; 86135446Strhodesstatic char *args; 87135446Strhodesstatic char program[256]; 88135446Strhodesstatic isc_socket_t *sock = NULL; 89135446Strhodesstatic isc_uint32_t serial; 90135446Strhodes 91135446Strhodesstatic void rndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task); 92135446Strhodes 93224092SdougbISC_PLATFORM_NORETURN_PRE static void 94224092Sdougbusage(int status) ISC_PLATFORM_NORETURN_POST; 95224092Sdougb 96135446Strhodesstatic void 97135446Strhodesusage(int status) { 98135446Strhodes fprintf(stderr, "\ 99224092SdougbUsage: %s [-b address] [-c config] [-s server] [-p port]\n\ 100186462Sdougb [-k key-file ] [-y key] [-V] command\n\ 101135446Strhodes\n\ 102135446Strhodescommand is one of the following:\n\ 103135446Strhodes\n\ 104135446Strhodes reload Reload configuration file and zones.\n\ 105135446Strhodes reload zone [class [view]]\n\ 106135446Strhodes Reload a single zone.\n\ 107135446Strhodes refresh zone [class [view]]\n\ 108135446Strhodes Schedule immediate maintenance for a zone.\n\ 109135446Strhodes retransfer zone [class [view]]\n\ 110135446Strhodes Retransfer a single zone without checking serial number.\n\ 111170222Sdougb freeze Suspend updates to all dynamic zones.\n\ 112135446Strhodes freeze zone [class [view]]\n\ 113186462Sdougb Suspend updates to a dynamic zone.\n\ 114170222Sdougb thaw Enable updates to all dynamic zones and reload them.\n\ 115135601Sdes thaw zone [class [view]]\n\ 116186462Sdougb Enable updates to a frozen dynamic zone and reload it.\n\ 117254897Serwin sync [-clean] Dump changes to all dynamic zones to disk, and optionally\n\ 118254897Serwin remove their journal files.\n\ 119254897Serwin sync [-clean] zone [class [view]]\n\ 120254897Serwin Dump a single zone's changes to disk, and optionally\n\ 121254897Serwin remove its journal file.\n\ 122170222Sdougb notify zone [class [view]]\n\ 123170222Sdougb Resend NOTIFY messages for the zone.\n\ 124135446Strhodes reconfig Reload configuration file and new zones only.\n\ 125224092Sdougb sign zone [class [view]]\n\ 126224092Sdougb Update zone keys, and sign as needed.\n\ 127224092Sdougb loadkeys zone [class [view]]\n\ 128224092Sdougb Update keys without signing immediately.\n\ 129135446Strhodes stats Write server statistics to the statistics file.\n\ 130254897Serwin querylog newstate\n\ 131254897Serwin Enable / disable query logging.\n\ 132153816Sdougb dumpdb [-all|-cache|-zones] [view ...]\n\ 133153816Sdougb Dump cache(s) to the dump file (named_dump.db).\n\ 134224092Sdougb secroots [view ...]\n\ 135224092Sdougb Write security roots to the secroots file.\n\ 136135446Strhodes stop Save pending updates to master files and stop the server.\n\ 137135446Strhodes stop -p Save pending updates to master files and stop the server\n\ 138135446Strhodes reporting process id.\n\ 139135446Strhodes halt Stop the server without saving pending updates.\n\ 140135446Strhodes halt -p Stop the server without saving pending updates reporting\n\ 141135446Strhodes process id.\n\ 142135446Strhodes trace Increment debugging level by one.\n\ 143135446Strhodes trace level Change the debugging level.\n\ 144135446Strhodes notrace Set debugging level to 0.\n\ 145135446Strhodes flush Flushes all of the server's caches.\n\ 146135446Strhodes flush [view] Flushes the server's cache for a view.\n\ 147135446Strhodes flushname name [view]\n\ 148135446Strhodes Flush the given name from the server's cache(s)\n\ 149254897Serwin flushtree name [view]\n\ 150254897Serwin Flush all names under the given name from the server's cache(s)\n\ 151135446Strhodes status Display status of the server.\n\ 152135446Strhodes recursing Dump the queries that are currently recursing (named.recursing)\n\ 153234010Sdougb tsig-list List all currently active TSIG keys, including both statically\n\ 154234010Sdougb configured and TKEY-negotiated keys.\n\ 155234010Sdougb tsig-delete keyname [view] \n\ 156234010Sdougb Delete a TKEY-negotiated TSIG key.\n\ 157170222Sdougb validation newstate [view]\n\ 158170222Sdougb Enable / disable DNSSEC validation.\n\ 159224092Sdougb addzone [\"file\"] zone [class [view]] { zone-options }\n\ 160224092Sdougb Add zone to given view. Requires new-zone-file option.\n\ 161224092Sdougb delzone [\"file\"] zone [class [view]]\n\ 162224092Sdougb Removes zone from given view. Requires new-zone-file option.\n\ 163254897Serwin signing -list zone [class [view]]\n\ 164254897Serwin List the private records showing the state of DNSSEC\n\ 165254897Serwin signing in the given zone.\n\ 166254897Serwin signing -clear <keyid>/<algorithm> zone [class [view]]\n\ 167254897Serwin Remove the private record that indicating the given key\n\ 168254897Serwin has finished signing the given zone.\n\ 169254897Serwin signing -clear all zone [class [view]]\n\ 170254897Serwin Remove the private records for all keys that have\n\ 171254897Serwin finished signing the given zone.\n\ 172254897Serwin signing -nsec3param none zone [class [view]]\n\ 173254897Serwin Remove NSEC3 chains from zone.\n\ 174254897Serwin signing -nsec3param hash flags iterations salt zone [class [view]]\n\ 175254897Serwin Add NSEC3 chain to zone if already signed.\n\ 176254897Serwin Prime zone with NSEC3 chain if not yet signed.\n\ 177234010Sdougb *restart Restart the server.\n\ 178135446Strhodes\n\ 179135446Strhodes* == not yet implemented\n\ 180135446StrhodesVersion: %s\n", 181135446Strhodes progname, version); 182135446Strhodes 183135446Strhodes exit(status); 184135446Strhodes} 185135446Strhodes 186135446Strhodesstatic void 187135446Strhodesget_addresses(const char *host, in_port_t port) { 188135446Strhodes isc_result_t result; 189170222Sdougb int found = 0, count; 190135446Strhodes 191170222Sdougb if (*host == '/') { 192170222Sdougb result = isc_sockaddr_frompath(&serveraddrs[nserveraddrs], 193170222Sdougb host); 194170222Sdougb if (result == ISC_R_SUCCESS) 195186462Sdougb nserveraddrs++; 196170222Sdougb } else { 197170222Sdougb count = SERVERADDRS - nserveraddrs; 198170222Sdougb result = bind9_getaddresses(host, port, 199170222Sdougb &serveraddrs[nserveraddrs], 200170222Sdougb count, &found); 201170222Sdougb nserveraddrs += found; 202170222Sdougb } 203135446Strhodes if (result != ISC_R_SUCCESS) 204135446Strhodes fatal("couldn't get address for '%s': %s", 205135446Strhodes host, isc_result_totext(result)); 206135446Strhodes INSIST(nserveraddrs > 0); 207135446Strhodes} 208135446Strhodes 209135446Strhodesstatic void 210135446Strhodesrndc_senddone(isc_task_t *task, isc_event_t *event) { 211135446Strhodes isc_socketevent_t *sevent = (isc_socketevent_t *)event; 212135446Strhodes 213135446Strhodes UNUSED(task); 214135446Strhodes 215135446Strhodes sends--; 216135446Strhodes if (sevent->result != ISC_R_SUCCESS) 217135446Strhodes fatal("send failed: %s", isc_result_totext(sevent->result)); 218135446Strhodes isc_event_free(&event); 219165071Sdougb if (sends == 0 && recvs == 0) { 220165071Sdougb isc_socket_detach(&sock); 221165071Sdougb isc_task_shutdown(task); 222165071Sdougb RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS); 223165071Sdougb } 224135446Strhodes} 225135446Strhodes 226135446Strhodesstatic void 227135446Strhodesrndc_recvdone(isc_task_t *task, isc_event_t *event) { 228135446Strhodes isccc_sexpr_t *response = NULL; 229135446Strhodes isccc_sexpr_t *data; 230135446Strhodes isccc_region_t source; 231135446Strhodes char *errormsg = NULL; 232135446Strhodes char *textmsg = NULL; 233135446Strhodes isc_result_t result; 234135446Strhodes 235135446Strhodes recvs--; 236135446Strhodes 237135446Strhodes if (ccmsg.result == ISC_R_EOF) 238135446Strhodes fatal("connection to remote host closed\n" 239170222Sdougb "This may indicate that\n" 240170222Sdougb "* the remote server is using an older version of" 241170222Sdougb " the command protocol,\n" 242170222Sdougb "* this host is not authorized to connect,\n" 243193149Sdougb "* the clocks are not synchronized, or\n" 244170222Sdougb "* the key is invalid."); 245135446Strhodes 246135446Strhodes if (ccmsg.result != ISC_R_SUCCESS) 247135446Strhodes fatal("recv failed: %s", isc_result_totext(ccmsg.result)); 248135446Strhodes 249135446Strhodes source.rstart = isc_buffer_base(&ccmsg.buffer); 250135446Strhodes source.rend = isc_buffer_used(&ccmsg.buffer); 251135446Strhodes 252135446Strhodes DO("parse message", isccc_cc_fromwire(&source, &response, &secret)); 253135446Strhodes 254135446Strhodes data = isccc_alist_lookup(response, "_data"); 255296611Sdelphij if (!isccc_alist_alistp(data)) 256296611Sdelphij fatal("bad or missing data section in response"); 257135446Strhodes result = isccc_cc_lookupstring(data, "err", &errormsg); 258135446Strhodes if (result == ISC_R_SUCCESS) { 259135446Strhodes failed = ISC_TRUE; 260135446Strhodes fprintf(stderr, "%s: '%s' failed: %s\n", 261135446Strhodes progname, command, errormsg); 262135446Strhodes } 263135446Strhodes else if (result != ISC_R_NOTFOUND) 264135446Strhodes fprintf(stderr, "%s: parsing response failed: %s\n", 265135446Strhodes progname, isc_result_totext(result)); 266135446Strhodes 267135446Strhodes result = isccc_cc_lookupstring(data, "text", &textmsg); 268262706Serwin if (result == ISC_R_SUCCESS) { 269262706Serwin if (strlen(textmsg) != 0U) 270262706Serwin printf("%s\n", textmsg); 271262706Serwin } else if (result != ISC_R_NOTFOUND) 272135446Strhodes fprintf(stderr, "%s: parsing response failed: %s\n", 273135446Strhodes progname, isc_result_totext(result)); 274135446Strhodes 275135446Strhodes isc_event_free(&event); 276135446Strhodes isccc_sexpr_free(&response); 277165071Sdougb if (sends == 0 && recvs == 0) { 278165071Sdougb isc_socket_detach(&sock); 279165071Sdougb isc_task_shutdown(task); 280165071Sdougb RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS); 281165071Sdougb } 282135446Strhodes} 283135446Strhodes 284135446Strhodesstatic void 285135446Strhodesrndc_recvnonce(isc_task_t *task, isc_event_t *event) { 286135446Strhodes isccc_sexpr_t *response = NULL; 287135446Strhodes isccc_sexpr_t *_ctrl; 288135446Strhodes isccc_region_t source; 289135446Strhodes isc_result_t result; 290135446Strhodes isc_uint32_t nonce; 291135446Strhodes isccc_sexpr_t *request = NULL; 292135446Strhodes isccc_time_t now; 293135446Strhodes isc_region_t r; 294135446Strhodes isccc_sexpr_t *data; 295135446Strhodes isccc_region_t message; 296135446Strhodes isc_uint32_t len; 297135446Strhodes isc_buffer_t b; 298135446Strhodes 299135446Strhodes recvs--; 300135446Strhodes 301135446Strhodes if (ccmsg.result == ISC_R_EOF) 302135446Strhodes fatal("connection to remote host closed\n" 303170222Sdougb "This may indicate that\n" 304170222Sdougb "* the remote server is using an older version of" 305170222Sdougb " the command protocol,\n" 306170222Sdougb "* this host is not authorized to connect,\n" 307193149Sdougb "* the clocks are not synchronized, or\n" 308170222Sdougb "* the key is invalid."); 309135446Strhodes 310135446Strhodes if (ccmsg.result != ISC_R_SUCCESS) 311135446Strhodes fatal("recv failed: %s", isc_result_totext(ccmsg.result)); 312135446Strhodes 313135446Strhodes source.rstart = isc_buffer_base(&ccmsg.buffer); 314135446Strhodes source.rend = isc_buffer_used(&ccmsg.buffer); 315135446Strhodes 316135446Strhodes DO("parse message", isccc_cc_fromwire(&source, &response, &secret)); 317135446Strhodes 318135446Strhodes _ctrl = isccc_alist_lookup(response, "_ctrl"); 319296611Sdelphij if (!isccc_alist_alistp(_ctrl)) 320296611Sdelphij fatal("bad or missing ctrl section in response"); 321135446Strhodes nonce = 0; 322135446Strhodes if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS) 323135446Strhodes nonce = 0; 324135446Strhodes 325135446Strhodes isc_stdtime_get(&now); 326135446Strhodes 327135446Strhodes DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial, 328135446Strhodes now, now + 60, &request)); 329135446Strhodes data = isccc_alist_lookup(request, "_data"); 330135446Strhodes if (data == NULL) 331135446Strhodes fatal("_data section missing"); 332135446Strhodes if (isccc_cc_definestring(data, "type", args) == NULL) 333135446Strhodes fatal("out of memory"); 334135446Strhodes if (nonce != 0) { 335135446Strhodes _ctrl = isccc_alist_lookup(request, "_ctrl"); 336135446Strhodes if (_ctrl == NULL) 337135446Strhodes fatal("_ctrl section missing"); 338135446Strhodes if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL) 339135446Strhodes fatal("out of memory"); 340135446Strhodes } 341135446Strhodes message.rstart = databuf + 4; 342135446Strhodes message.rend = databuf + sizeof(databuf); 343135446Strhodes DO("render message", isccc_cc_towire(request, &message, &secret)); 344135446Strhodes len = sizeof(databuf) - REGION_SIZE(message); 345135446Strhodes isc_buffer_init(&b, databuf, 4); 346135446Strhodes isc_buffer_putuint32(&b, len - 4); 347135446Strhodes r.length = len; 348135446Strhodes r.base = databuf; 349135446Strhodes 350135446Strhodes isccc_ccmsg_cancelread(&ccmsg); 351135446Strhodes DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task, 352135446Strhodes rndc_recvdone, NULL)); 353135446Strhodes recvs++; 354135446Strhodes DO("send message", isc_socket_send(sock, &r, task, rndc_senddone, 355135446Strhodes NULL)); 356135446Strhodes sends++; 357135446Strhodes 358135446Strhodes isc_event_free(&event); 359135446Strhodes isccc_sexpr_free(&response); 360135446Strhodes return; 361135446Strhodes} 362135446Strhodes 363135446Strhodesstatic void 364135446Strhodesrndc_connected(isc_task_t *task, isc_event_t *event) { 365165071Sdougb char socktext[ISC_SOCKADDR_FORMATSIZE]; 366135446Strhodes isc_socketevent_t *sevent = (isc_socketevent_t *)event; 367135446Strhodes isccc_sexpr_t *request = NULL; 368135446Strhodes isccc_sexpr_t *data; 369135446Strhodes isccc_time_t now; 370135446Strhodes isccc_region_t message; 371135446Strhodes isc_region_t r; 372135446Strhodes isc_uint32_t len; 373135446Strhodes isc_buffer_t b; 374135446Strhodes isc_result_t result; 375135446Strhodes 376135446Strhodes connects--; 377135446Strhodes 378135446Strhodes if (sevent->result != ISC_R_SUCCESS) { 379165071Sdougb isc_sockaddr_format(&serveraddrs[currentaddr], socktext, 380165071Sdougb sizeof(socktext)); 381135446Strhodes if (sevent->result != ISC_R_CANCELED && 382165071Sdougb ++currentaddr < nserveraddrs) 383135446Strhodes { 384165071Sdougb notify("connection failed: %s: %s", socktext, 385135446Strhodes isc_result_totext(sevent->result)); 386135446Strhodes isc_socket_detach(&sock); 387135446Strhodes isc_event_free(&event); 388165071Sdougb rndc_startconnect(&serveraddrs[currentaddr], task); 389135446Strhodes return; 390135446Strhodes } else 391165071Sdougb fatal("connect failed: %s: %s", socktext, 392135446Strhodes isc_result_totext(sevent->result)); 393135446Strhodes } 394135446Strhodes 395135446Strhodes isc_stdtime_get(&now); 396135446Strhodes DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial, 397135446Strhodes now, now + 60, &request)); 398135446Strhodes data = isccc_alist_lookup(request, "_data"); 399135446Strhodes if (data == NULL) 400135446Strhodes fatal("_data section missing"); 401135446Strhodes if (isccc_cc_definestring(data, "type", "null") == NULL) 402135446Strhodes fatal("out of memory"); 403135446Strhodes message.rstart = databuf + 4; 404135446Strhodes message.rend = databuf + sizeof(databuf); 405135446Strhodes DO("render message", isccc_cc_towire(request, &message, &secret)); 406135446Strhodes len = sizeof(databuf) - REGION_SIZE(message); 407135446Strhodes isc_buffer_init(&b, databuf, 4); 408135446Strhodes isc_buffer_putuint32(&b, len - 4); 409135446Strhodes r.length = len; 410135446Strhodes r.base = databuf; 411135446Strhodes 412135446Strhodes isccc_ccmsg_init(mctx, sock, &ccmsg); 413193149Sdougb isccc_ccmsg_setmaxsize(&ccmsg, 1024 * 1024); 414135446Strhodes 415135446Strhodes DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task, 416135446Strhodes rndc_recvnonce, NULL)); 417135446Strhodes recvs++; 418135446Strhodes DO("send message", isc_socket_send(sock, &r, task, rndc_senddone, 419135446Strhodes NULL)); 420135446Strhodes sends++; 421135446Strhodes isc_event_free(&event); 422135446Strhodes} 423135446Strhodes 424135446Strhodesstatic void 425135446Strhodesrndc_startconnect(isc_sockaddr_t *addr, isc_task_t *task) { 426135446Strhodes isc_result_t result; 427170222Sdougb int pf; 428170222Sdougb isc_sockettype_t type; 429135446Strhodes 430135446Strhodes char socktext[ISC_SOCKADDR_FORMATSIZE]; 431135446Strhodes 432135446Strhodes isc_sockaddr_format(addr, socktext, sizeof(socktext)); 433135446Strhodes 434135446Strhodes notify("using server %s (%s)", servername, socktext); 435135446Strhodes 436170222Sdougb pf = isc_sockaddr_pf(addr); 437170222Sdougb if (pf == AF_INET || pf == AF_INET6) 438170222Sdougb type = isc_sockettype_tcp; 439170222Sdougb else 440170222Sdougb type = isc_sockettype_unix; 441170222Sdougb DO("create socket", isc_socket_create(socketmgr, pf, type, &sock)); 442170222Sdougb switch (isc_sockaddr_pf(addr)) { 443170222Sdougb case AF_INET: 444182645Sdougb DO("bind socket", isc_socket_bind(sock, &local4, 0)); 445170222Sdougb break; 446170222Sdougb case AF_INET6: 447182645Sdougb DO("bind socket", isc_socket_bind(sock, &local6, 0)); 448170222Sdougb break; 449170222Sdougb default: 450170222Sdougb break; 451170222Sdougb } 452135446Strhodes DO("connect", isc_socket_connect(sock, addr, task, rndc_connected, 453135446Strhodes NULL)); 454135446Strhodes connects++; 455135446Strhodes} 456135446Strhodes 457135446Strhodesstatic void 458135446Strhodesrndc_start(isc_task_t *task, isc_event_t *event) { 459135446Strhodes isc_event_free(&event); 460135446Strhodes 461135446Strhodes currentaddr = 0; 462165071Sdougb rndc_startconnect(&serveraddrs[currentaddr], task); 463135446Strhodes} 464135446Strhodes 465135446Strhodesstatic void 466135446Strhodesparse_config(isc_mem_t *mctx, isc_log_t *log, const char *keyname, 467135446Strhodes cfg_parser_t **pctxp, cfg_obj_t **configp) 468135446Strhodes{ 469135446Strhodes isc_result_t result; 470135446Strhodes const char *conffile = admin_conffile; 471170222Sdougb const cfg_obj_t *addresses = NULL; 472165071Sdougb const cfg_obj_t *defkey = NULL; 473165071Sdougb const cfg_obj_t *options = NULL; 474165071Sdougb const cfg_obj_t *servers = NULL; 475165071Sdougb const cfg_obj_t *server = NULL; 476165071Sdougb const cfg_obj_t *keys = NULL; 477165071Sdougb const cfg_obj_t *key = NULL; 478165071Sdougb const cfg_obj_t *defport = NULL; 479165071Sdougb const cfg_obj_t *secretobj = NULL; 480165071Sdougb const cfg_obj_t *algorithmobj = NULL; 481135446Strhodes cfg_obj_t *config = NULL; 482170222Sdougb const cfg_obj_t *address = NULL; 483165071Sdougb const cfg_listelt_t *elt; 484135446Strhodes const char *secretstr; 485135446Strhodes const char *algorithm; 486135446Strhodes static char secretarray[1024]; 487135446Strhodes const cfg_type_t *conftype = &cfg_type_rndcconf; 488135446Strhodes isc_boolean_t key_only = ISC_FALSE; 489170222Sdougb const cfg_listelt_t *element; 490135446Strhodes 491135446Strhodes if (! isc_file_exists(conffile)) { 492135446Strhodes conffile = admin_keyfile; 493135446Strhodes conftype = &cfg_type_rndckey; 494135446Strhodes 495262706Serwin if (c_flag) 496262706Serwin fatal("%s does not exist", admin_conffile); 497262706Serwin 498135446Strhodes if (! isc_file_exists(conffile)) 499135446Strhodes fatal("neither %s nor %s was found", 500135446Strhodes admin_conffile, admin_keyfile); 501135446Strhodes key_only = ISC_TRUE; 502224092Sdougb } else if (! c_flag && isc_file_exists(admin_keyfile)) { 503224092Sdougb fprintf(stderr, "WARNING: key file (%s) exists, but using " 504224092Sdougb "default configuration file (%s)\n", 505224092Sdougb admin_keyfile, admin_conffile); 506135446Strhodes } 507135446Strhodes 508135446Strhodes DO("create parser", cfg_parser_create(mctx, log, pctxp)); 509135446Strhodes 510135446Strhodes /* 511135446Strhodes * The parser will output its own errors, so DO() is not used. 512135446Strhodes */ 513135446Strhodes result = cfg_parse_file(*pctxp, conffile, conftype, &config); 514135446Strhodes if (result != ISC_R_SUCCESS) 515135446Strhodes fatal("could not load rndc configuration"); 516135446Strhodes 517135446Strhodes if (!key_only) 518135446Strhodes (void)cfg_map_get(config, "options", &options); 519135446Strhodes 520135446Strhodes if (key_only && servername == NULL) 521135446Strhodes servername = "127.0.0.1"; 522135446Strhodes else if (servername == NULL && options != NULL) { 523165071Sdougb const cfg_obj_t *defserverobj = NULL; 524135446Strhodes (void)cfg_map_get(options, "default-server", &defserverobj); 525135446Strhodes if (defserverobj != NULL) 526135446Strhodes servername = cfg_obj_asstring(defserverobj); 527135446Strhodes } 528135446Strhodes 529135446Strhodes if (servername == NULL) 530135446Strhodes fatal("no server specified and no default"); 531135446Strhodes 532135446Strhodes if (!key_only) { 533135446Strhodes (void)cfg_map_get(config, "server", &servers); 534135446Strhodes if (servers != NULL) { 535135446Strhodes for (elt = cfg_list_first(servers); 536186462Sdougb elt != NULL; 537135446Strhodes elt = cfg_list_next(elt)) 538135446Strhodes { 539135446Strhodes const char *name; 540135446Strhodes server = cfg_listelt_value(elt); 541135446Strhodes name = cfg_obj_asstring(cfg_map_getname(server)); 542135446Strhodes if (strcasecmp(name, servername) == 0) 543135446Strhodes break; 544135446Strhodes server = NULL; 545135446Strhodes } 546135446Strhodes } 547135446Strhodes } 548135446Strhodes 549135446Strhodes /* 550135446Strhodes * Look for the name of the key to use. 551135446Strhodes */ 552135446Strhodes if (keyname != NULL) 553135446Strhodes ; /* Was set on command line, do nothing. */ 554135446Strhodes else if (server != NULL) { 555135446Strhodes DO("get key for server", cfg_map_get(server, "key", &defkey)); 556135446Strhodes keyname = cfg_obj_asstring(defkey); 557135446Strhodes } else if (options != NULL) { 558135446Strhodes DO("get default key", cfg_map_get(options, "default-key", 559135446Strhodes &defkey)); 560135446Strhodes keyname = cfg_obj_asstring(defkey); 561135446Strhodes } else if (!key_only) 562135446Strhodes fatal("no key for server and no default"); 563135446Strhodes 564135446Strhodes /* 565135446Strhodes * Get the key's definition. 566135446Strhodes */ 567135446Strhodes if (key_only) 568135446Strhodes DO("get key", cfg_map_get(config, "key", &key)); 569135446Strhodes else { 570135446Strhodes DO("get config key list", cfg_map_get(config, "key", &keys)); 571135446Strhodes for (elt = cfg_list_first(keys); 572186462Sdougb elt != NULL; 573135446Strhodes elt = cfg_list_next(elt)) 574135446Strhodes { 575135446Strhodes key = cfg_listelt_value(elt); 576135446Strhodes if (strcasecmp(cfg_obj_asstring(cfg_map_getname(key)), 577135446Strhodes keyname) == 0) 578135446Strhodes break; 579135446Strhodes } 580135446Strhodes if (elt == NULL) 581135446Strhodes fatal("no key definition for name %s", keyname); 582135446Strhodes } 583135446Strhodes (void)cfg_map_get(key, "secret", &secretobj); 584135446Strhodes (void)cfg_map_get(key, "algorithm", &algorithmobj); 585135446Strhodes if (secretobj == NULL || algorithmobj == NULL) 586135446Strhodes fatal("key must have algorithm and secret"); 587135446Strhodes 588135446Strhodes secretstr = cfg_obj_asstring(secretobj); 589135446Strhodes algorithm = cfg_obj_asstring(algorithmobj); 590135446Strhodes 591135446Strhodes if (strcasecmp(algorithm, "hmac-md5") != 0) 592135446Strhodes fatal("unsupported algorithm: %s", algorithm); 593135446Strhodes 594135446Strhodes secret.rstart = (unsigned char *)secretarray; 595135446Strhodes secret.rend = (unsigned char *)secretarray + sizeof(secretarray); 596135446Strhodes DO("decode base64 secret", isccc_base64_decode(secretstr, &secret)); 597135446Strhodes secret.rend = secret.rstart; 598135446Strhodes secret.rstart = (unsigned char *)secretarray; 599135446Strhodes 600135446Strhodes /* 601135446Strhodes * Find the port to connect to. 602135446Strhodes */ 603135446Strhodes if (remoteport != 0) 604135446Strhodes ; /* Was set on command line, do nothing. */ 605135446Strhodes else { 606135446Strhodes if (server != NULL) 607135446Strhodes (void)cfg_map_get(server, "port", &defport); 608135446Strhodes if (defport == NULL && options != NULL) 609135446Strhodes (void)cfg_map_get(options, "default-port", &defport); 610135446Strhodes } 611135446Strhodes if (defport != NULL) { 612135446Strhodes remoteport = cfg_obj_asuint32(defport); 613135446Strhodes if (remoteport > 65535 || remoteport == 0) 614170222Sdougb fatal("port %u out of range", remoteport); 615135446Strhodes } else if (remoteport == 0) 616135446Strhodes remoteport = NS_CONTROL_PORT; 617135446Strhodes 618170222Sdougb if (server != NULL) 619170222Sdougb result = cfg_map_get(server, "addresses", &addresses); 620170222Sdougb else 621170222Sdougb result = ISC_R_NOTFOUND; 622170222Sdougb if (result == ISC_R_SUCCESS) { 623170222Sdougb for (element = cfg_list_first(addresses); 624170222Sdougb element != NULL; 625170222Sdougb element = cfg_list_next(element)) 626170222Sdougb { 627170222Sdougb isc_sockaddr_t sa; 628170222Sdougb 629170222Sdougb address = cfg_listelt_value(element); 630170222Sdougb if (!cfg_obj_issockaddr(address)) { 631170222Sdougb unsigned int myport; 632170222Sdougb const char *name; 633170222Sdougb const cfg_obj_t *obj; 634170222Sdougb 635170222Sdougb obj = cfg_tuple_get(address, "name"); 636170222Sdougb name = cfg_obj_asstring(obj); 637170222Sdougb obj = cfg_tuple_get(address, "port"); 638170222Sdougb if (cfg_obj_isuint32(obj)) { 639170222Sdougb myport = cfg_obj_asuint32(obj); 640170222Sdougb if (myport > ISC_UINT16_MAX || 641170222Sdougb myport == 0) 642170222Sdougb fatal("port %u out of range", 643170222Sdougb myport); 644170222Sdougb } else 645170222Sdougb myport = remoteport; 646170222Sdougb if (nserveraddrs < SERVERADDRS) 647170222Sdougb get_addresses(name, (in_port_t) myport); 648170222Sdougb else 649170222Sdougb fprintf(stderr, "too many address: " 650186462Sdougb "%s: dropped\n", name); 651170222Sdougb continue; 652170222Sdougb } 653170222Sdougb sa = *cfg_obj_assockaddr(address); 654170222Sdougb if (isc_sockaddr_getport(&sa) == 0) 655170222Sdougb isc_sockaddr_setport(&sa, remoteport); 656170222Sdougb if (nserveraddrs < SERVERADDRS) 657170222Sdougb serveraddrs[nserveraddrs++] = sa; 658170222Sdougb else { 659170222Sdougb char socktext[ISC_SOCKADDR_FORMATSIZE]; 660170222Sdougb 661170222Sdougb isc_sockaddr_format(&sa, socktext, 662170222Sdougb sizeof(socktext)); 663170222Sdougb fprintf(stderr, 664170222Sdougb "too many address: %s: dropped\n", 665170222Sdougb socktext); 666170222Sdougb } 667170222Sdougb } 668170222Sdougb } 669170222Sdougb 670170222Sdougb if (!local4set && server != NULL) { 671170222Sdougb address = NULL; 672170222Sdougb cfg_map_get(server, "source-address", &address); 673170222Sdougb if (address != NULL) { 674170222Sdougb local4 = *cfg_obj_assockaddr(address); 675170222Sdougb local4set = ISC_TRUE; 676170222Sdougb } 677170222Sdougb } 678170222Sdougb if (!local4set && options != NULL) { 679170222Sdougb address = NULL; 680170222Sdougb cfg_map_get(options, "default-source-address", &address); 681170222Sdougb if (address != NULL) { 682170222Sdougb local4 = *cfg_obj_assockaddr(address); 683170222Sdougb local4set = ISC_TRUE; 684170222Sdougb } 685170222Sdougb } 686170222Sdougb 687170222Sdougb if (!local6set && server != NULL) { 688170222Sdougb address = NULL; 689170222Sdougb cfg_map_get(server, "source-address-v6", &address); 690170222Sdougb if (address != NULL) { 691170222Sdougb local6 = *cfg_obj_assockaddr(address); 692170222Sdougb local6set = ISC_TRUE; 693170222Sdougb } 694170222Sdougb } 695170222Sdougb if (!local6set && options != NULL) { 696170222Sdougb address = NULL; 697170222Sdougb cfg_map_get(options, "default-source-address-v6", &address); 698170222Sdougb if (address != NULL) { 699170222Sdougb local6 = *cfg_obj_assockaddr(address); 700170222Sdougb local6set = ISC_TRUE; 701170222Sdougb } 702170222Sdougb } 703170222Sdougb 704135446Strhodes *configp = config; 705135446Strhodes} 706135446Strhodes 707135446Strhodesint 708135446Strhodesmain(int argc, char **argv) { 709135446Strhodes isc_boolean_t show_final_mem = ISC_FALSE; 710135446Strhodes isc_result_t result = ISC_R_SUCCESS; 711135446Strhodes isc_taskmgr_t *taskmgr = NULL; 712135446Strhodes isc_task_t *task = NULL; 713135446Strhodes isc_log_t *log = NULL; 714135446Strhodes isc_logconfig_t *logconfig = NULL; 715135446Strhodes isc_logdestination_t logdest; 716135446Strhodes cfg_parser_t *pctx = NULL; 717135446Strhodes cfg_obj_t *config = NULL; 718135446Strhodes const char *keyname = NULL; 719170222Sdougb struct in_addr in; 720170222Sdougb struct in6_addr in6; 721135446Strhodes char *p; 722135446Strhodes size_t argslen; 723135446Strhodes int ch; 724135446Strhodes int i; 725135446Strhodes 726135446Strhodes result = isc_file_progname(*argv, program, sizeof(program)); 727135446Strhodes if (result != ISC_R_SUCCESS) 728262706Serwin memmove(program, "rndc", 5); 729135446Strhodes progname = program; 730135446Strhodes 731135446Strhodes admin_conffile = RNDC_CONFFILE; 732135446Strhodes admin_keyfile = RNDC_KEYFILE; 733135446Strhodes 734170222Sdougb isc_sockaddr_any(&local4); 735170222Sdougb isc_sockaddr_any6(&local6); 736170222Sdougb 737135446Strhodes result = isc_app_start(); 738135446Strhodes if (result != ISC_R_SUCCESS) 739135446Strhodes fatal("isc_app_start() failed: %s", isc_result_totext(result)); 740135446Strhodes 741193149Sdougb isc_commandline_errprint = ISC_FALSE; 742193149Sdougb 743193149Sdougb while ((ch = isc_commandline_parse(argc, argv, "b:c:hk:Mmp:s:Vy:")) 744135446Strhodes != -1) { 745135446Strhodes switch (ch) { 746170222Sdougb case 'b': 747170222Sdougb if (inet_pton(AF_INET, isc_commandline_argument, 748170222Sdougb &in) == 1) { 749170222Sdougb isc_sockaddr_fromin(&local4, &in, 0); 750170222Sdougb local4set = ISC_TRUE; 751170222Sdougb } else if (inet_pton(AF_INET6, isc_commandline_argument, 752170222Sdougb &in6) == 1) { 753170222Sdougb isc_sockaddr_fromin6(&local6, &in6, 0); 754170222Sdougb local6set = ISC_TRUE; 755170222Sdougb } 756170222Sdougb break; 757170222Sdougb 758135446Strhodes case 'c': 759135446Strhodes admin_conffile = isc_commandline_argument; 760224092Sdougb c_flag = ISC_TRUE; 761135446Strhodes break; 762135446Strhodes 763135446Strhodes case 'k': 764135446Strhodes admin_keyfile = isc_commandline_argument; 765135446Strhodes break; 766135446Strhodes 767135446Strhodes case 'M': 768135446Strhodes isc_mem_debugging = ISC_MEM_DEBUGTRACE; 769135446Strhodes break; 770135446Strhodes 771135446Strhodes case 'm': 772135446Strhodes show_final_mem = ISC_TRUE; 773135446Strhodes break; 774135446Strhodes 775135446Strhodes case 'p': 776135446Strhodes remoteport = atoi(isc_commandline_argument); 777135446Strhodes if (remoteport > 65535 || remoteport == 0) 778135446Strhodes fatal("port '%s' out of range", 779135446Strhodes isc_commandline_argument); 780135446Strhodes break; 781135446Strhodes 782135446Strhodes case 's': 783135446Strhodes servername = isc_commandline_argument; 784135446Strhodes break; 785170222Sdougb 786135446Strhodes case 'V': 787135446Strhodes verbose = ISC_TRUE; 788135446Strhodes break; 789170222Sdougb 790135446Strhodes case 'y': 791135446Strhodes keyname = isc_commandline_argument; 792135446Strhodes break; 793186462Sdougb 794135446Strhodes case '?': 795193149Sdougb if (isc_commandline_option != '?') { 796193149Sdougb fprintf(stderr, "%s: invalid argument -%c\n", 797193149Sdougb program, isc_commandline_option); 798193149Sdougb usage(1); 799193149Sdougb } 800254402Serwin /* FALLTHROUGH */ 801193149Sdougb case 'h': 802135446Strhodes usage(0); 803135446Strhodes break; 804135446Strhodes default: 805193149Sdougb fprintf(stderr, "%s: unhandled option -%c\n", 806193149Sdougb program, isc_commandline_option); 807193149Sdougb exit(1); 808135446Strhodes } 809135446Strhodes } 810135446Strhodes 811135446Strhodes argc -= isc_commandline_index; 812135446Strhodes argv += isc_commandline_index; 813135446Strhodes 814135446Strhodes if (argc < 1) 815135446Strhodes usage(1); 816135446Strhodes 817135446Strhodes isc_random_get(&serial); 818135446Strhodes 819135446Strhodes DO("create memory context", isc_mem_create(0, 0, &mctx)); 820135446Strhodes DO("create socket manager", isc_socketmgr_create(mctx, &socketmgr)); 821135446Strhodes DO("create task manager", isc_taskmgr_create(mctx, 1, 0, &taskmgr)); 822135446Strhodes DO("create task", isc_task_create(taskmgr, 0, &task)); 823135446Strhodes 824135446Strhodes DO("create logging context", isc_log_create(mctx, &log, &logconfig)); 825135446Strhodes isc_log_setcontext(log); 826135446Strhodes DO("setting log tag", isc_log_settag(logconfig, progname)); 827135446Strhodes logdest.file.stream = stderr; 828135446Strhodes logdest.file.name = NULL; 829135446Strhodes logdest.file.versions = ISC_LOG_ROLLNEVER; 830135446Strhodes logdest.file.maximum_size = 0; 831135446Strhodes DO("creating log channel", 832135446Strhodes isc_log_createchannel(logconfig, "stderr", 833186462Sdougb ISC_LOG_TOFILEDESC, ISC_LOG_INFO, &logdest, 834135446Strhodes ISC_LOG_PRINTTAG|ISC_LOG_PRINTLEVEL)); 835135446Strhodes DO("enabling log channel", isc_log_usechannel(logconfig, "stderr", 836135446Strhodes NULL, NULL)); 837135446Strhodes 838135446Strhodes parse_config(mctx, log, keyname, &pctx, &config); 839135446Strhodes 840135446Strhodes isccc_result_register(); 841135446Strhodes 842135446Strhodes command = *argv; 843135446Strhodes 844135446Strhodes /* 845135446Strhodes * Convert argc/argv into a space-delimited command string 846135446Strhodes * similar to what the user might enter in interactive mode 847135446Strhodes * (if that were implemented). 848135446Strhodes */ 849135446Strhodes argslen = 0; 850135446Strhodes for (i = 0; i < argc; i++) 851135446Strhodes argslen += strlen(argv[i]) + 1; 852135446Strhodes 853135446Strhodes args = isc_mem_get(mctx, argslen); 854135446Strhodes if (args == NULL) 855135446Strhodes DO("isc_mem_get", ISC_R_NOMEMORY); 856135446Strhodes 857135446Strhodes p = args; 858135446Strhodes for (i = 0; i < argc; i++) { 859135446Strhodes size_t len = strlen(argv[i]); 860262706Serwin memmove(p, argv[i], len); 861135446Strhodes p += len; 862135446Strhodes *p++ = ' '; 863135446Strhodes } 864135446Strhodes 865135446Strhodes p--; 866135446Strhodes *p++ = '\0'; 867135446Strhodes INSIST(p == args + argslen); 868135446Strhodes 869135446Strhodes notify("%s", command); 870135446Strhodes 871135446Strhodes if (strcmp(command, "restart") == 0) 872135446Strhodes fatal("'%s' is not implemented", command); 873135446Strhodes 874170222Sdougb if (nserveraddrs == 0) 875170222Sdougb get_addresses(servername, (in_port_t) remoteport); 876170222Sdougb 877135446Strhodes DO("post event", isc_app_onrun(mctx, task, rndc_start, NULL)); 878135446Strhodes 879135446Strhodes result = isc_app_run(); 880135446Strhodes if (result != ISC_R_SUCCESS) 881135446Strhodes fatal("isc_app_run() failed: %s", isc_result_totext(result)); 882135446Strhodes 883135446Strhodes if (connects > 0 || sends > 0 || recvs > 0) 884135446Strhodes isc_socket_cancel(sock, task, ISC_SOCKCANCEL_ALL); 885135446Strhodes 886135446Strhodes isc_task_detach(&task); 887135446Strhodes isc_taskmgr_destroy(&taskmgr); 888135446Strhodes isc_socketmgr_destroy(&socketmgr); 889135446Strhodes isc_log_destroy(&log); 890135446Strhodes isc_log_setcontext(NULL); 891135446Strhodes 892135446Strhodes cfg_obj_destroy(pctx, &config); 893135446Strhodes cfg_parser_destroy(&pctx); 894135446Strhodes 895135446Strhodes isc_mem_put(mctx, args, argslen); 896135446Strhodes isccc_ccmsg_invalidate(&ccmsg); 897135446Strhodes 898170222Sdougb dns_name_destroy(); 899170222Sdougb 900135446Strhodes if (show_final_mem) 901135446Strhodes isc_mem_stats(mctx, stderr); 902135446Strhodes 903135446Strhodes isc_mem_destroy(&mctx); 904135446Strhodes 905135446Strhodes if (failed) 906135446Strhodes return (1); 907135446Strhodes 908135446Strhodes return (0); 909135446Strhodes} 910