"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.16.7/bin/rndc/rndc.c" (4 Sep 2020, 29861 Bytes) of package /linux/misc/dns/bind9/9.16.7/bind-9.16.7.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "rndc.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 9.16.6_vs_9.16.7.

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