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