1 /* $NetBSD: dig.c,v 1.11 2024/09/22 00:13:56 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 <ctype.h> 19 #include <inttypes.h> 20 #include <stdbool.h> 21 #include <stdlib.h> 22 #include <time.h> 23 24 #include <isc/app.h> 25 #include <isc/attributes.h> 26 #include <isc/dir.h> 27 #include <isc/netaddr.h> 28 #include <isc/parseint.h> 29 #include <isc/print.h> 30 #include <isc/result.h> 31 #include <isc/string.h> 32 #include <isc/task.h> 33 #include <isc/time.h> 34 #include <isc/util.h> 35 36 #include <dns/byaddr.h> 37 #include <dns/dns64.h> 38 #include <dns/fixedname.h> 39 #include <dns/masterdump.h> 40 #include <dns/message.h> 41 #include <dns/name.h> 42 #include <dns/rcode.h> 43 #include <dns/rdata.h> 44 #include <dns/rdataclass.h> 45 #include <dns/rdataset.h> 46 #include <dns/rdatatype.h> 47 #include <dns/tsig.h> 48 49 #include "dighost.h" 50 51 #define ADD_STRING(b, s) \ 52 { \ 53 if (strlen(s) >= isc_buffer_availablelength(b)) { \ 54 return ((((ISC_R_NOSPACE)))); \ 55 } else { \ 56 isc_buffer_putstr(b, s); \ 57 } \ 58 } 59 60 #define DIG_MAX_ADDRESSES 20 61 62 dig_lookup_t *default_lookup = NULL; 63 64 static atomic_uintptr_t batchname = 0; 65 static FILE *batchfp = NULL; 66 static char *argv0; 67 static int addresscount = 0; 68 69 static char domainopt[DNS_NAME_MAXTEXT]; 70 static char hexcookie[81]; 71 72 static bool short_form = false, printcmd = true, plusquest = false, 73 pluscomm = false, ipv4only = false, ipv6only = false, digrc = true; 74 static uint32_t splitwidth = 0xffffffff; 75 76 /*% opcode text */ 77 static const char *const opcodetext[] = { 78 "QUERY", "IQUERY", "STATUS", "RESERVED3", 79 "NOTIFY", "UPDATE", "RESERVED6", "RESERVED7", 80 "RESERVED8", "RESERVED9", "RESERVED10", "RESERVED11", 81 "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15" 82 }; 83 84 static const char * 85 rcode_totext(dns_rcode_t rcode) { 86 static char buf[64]; 87 isc_buffer_t b; 88 isc_result_t result; 89 90 memset(buf, 0, sizeof(buf)); 91 isc_buffer_init(&b, buf + 1, sizeof(buf) - 2); 92 result = dns_rcode_totext(rcode, &b); 93 RUNTIME_CHECK(result == ISC_R_SUCCESS); 94 if (strspn(buf + 1, "0123456789") == strlen(buf + 1)) { 95 buf[0] = '?'; 96 return (buf); 97 } 98 return (buf + 1); 99 } 100 101 /*% print usage */ 102 static void 103 print_usage(FILE *fp) { 104 fprintf(fp, 105 "Usage: dig [@global-server] [domain] [q-type] [q-class] " 106 "{q-opt}\n" 107 " {global-d-opt} host [@local-server] " 108 "{local-d-opt}\n" 109 " [ host [@local-server] {local-d-opt} [...]]\n"); 110 } 111 112 #if TARGET_OS_IPHONE 113 static void 114 usage(void) { 115 fprintf(stderr, "Press <Help> for complete list of options\n"); 116 } 117 #else /* if TARGET_OS_IPHONE */ 118 noreturn static void 119 usage(void); 120 121 static void 122 usage(void) { 123 print_usage(stderr); 124 fprintf(stderr, "\nUse \"dig -h\" (or \"dig -h | more\") " 125 "for complete list of options\n"); 126 exit(EXIT_FAILURE); 127 } 128 #endif /* if TARGET_OS_IPHONE */ 129 130 /*% version */ 131 static void 132 version(void) { 133 fprintf(stderr, "DiG %s\n", PACKAGE_VERSION); 134 } 135 136 /*% help */ 137 static void 138 help(void) { 139 print_usage(stdout); 140 printf("Where: domain is in the Domain Name System\n" 141 " q-class is one of (in,hs,ch,...) [default: in]\n" 142 " q-type is one of " 143 "(a,any,mx,ns,soa,hinfo,axfr,txt,...) " 144 "[default:a]\n" 145 " (Use ixfr=version for type ixfr)\n" 146 " q-opt is one of:\n" 147 " -4 (use IPv4 query transport " 148 "only)\n" 149 " -6 (use IPv6 query transport " 150 "only)\n" 151 " -b address[#port] (bind to source " 152 "address/port)\n" 153 " -c class (specify query class)\n" 154 " -f filename (batch mode)\n" 155 " -k keyfile (specify tsig key file)\n" 156 " -m (enable memory usage " 157 "debugging)\n" 158 " -p port (specify port number)\n" 159 " -q name (specify query name)\n" 160 " -r (do not read ~/.digrc)\n" 161 " -t type (specify query type)\n" 162 " -u (display times in usec " 163 "instead of msec)\n" 164 " -x dot-notation (shortcut for reverse " 165 "lookups)\n" 166 " -y [hmac:]name:key (specify named base64 " 167 "tsig " 168 "key)\n" 169 " d-opt is of the form +keyword[=value], where " 170 "keyword " 171 "is:\n" 172 " +[no]aaflag (Set AA flag in query " 173 "(+[no]aaflag))\n" 174 " +[no]aaonly (Set AA flag in query " 175 "(+[no]aaflag))\n" 176 " +[no]additional (Control display of " 177 "additional section)\n" 178 " +[no]adflag (Set AD flag in query " 179 "(default on))\n" 180 " +[no]all (Set or clear all display " 181 "flags)\n" 182 " +[no]answer (Control display of " 183 "answer " 184 "section)\n" 185 " +[no]authority (Control display of " 186 "authority section)\n" 187 " +[no]badcookie (Retry BADCOOKIE " 188 "responses)\n" 189 " +[no]besteffort (Try to parse even " 190 "illegal " 191 "messages)\n" 192 " +bufsize[=###] (Set EDNS0 Max UDP packet " 193 "size)\n" 194 " +[no]cdflag (Set checking disabled " 195 "flag in query)\n" 196 " +[no]class (Control display of class " 197 "in records)\n" 198 " +[no]cmd (Control display of " 199 "command line -\n" 200 " global option)\n" 201 " +[no]comments (Control display of " 202 "packet " 203 "header\n" 204 " and section name " 205 "comments)\n" 206 " +[no]cookie (Add a COOKIE option to " 207 "the request)\n" 208 " +[no]crypto (Control display of " 209 "cryptographic\n" 210 " fields in records)\n" 211 " +[no]defname (Use search list " 212 "(+[no]search))\n" 213 " +[no]dns64prefix (Get the DNS64 prefixes " 214 "from ipv4only.arpa)\n" 215 " +[no]dnssec (Request DNSSEC records)\n" 216 " +domain=### (Set default domainname)\n" 217 " +[no]edns[=###] (Set EDNS version) [0]\n" 218 " +ednsflags=### (Set EDNS flag bits)\n" 219 " +[no]ednsnegotiation (Set EDNS version " 220 "negotiation)\n" 221 " +ednsopt=###[:value] (Send specified EDNS " 222 "option)\n" 223 " +noednsopt (Clear list of +ednsopt " 224 "options)\n" 225 " +[no]expandaaaa (Expand AAAA records)\n" 226 " +[no]expire (Request time to expire)\n" 227 " +[no]fail (Don't try next server on " 228 "SERVFAIL)\n" 229 " +[no]header-only (Send query without a " 230 "question section)\n" 231 " +[no]https[=###] (DNS-over-HTTPS mode) " 232 "[/]\n" 233 " +[no]https-get (Use GET instead of " 234 "default POST method while using HTTPS)\n" 235 " +[no]http-plain[=###] (DNS over plain HTTP " 236 "mode) " 237 "[/]\n" 238 " +[no]http-plain-get (Use GET instead of " 239 "default POST method while using plain HTTP)\n" 240 " +[no]identify (ID responders in short " 241 "answers)\n" 242 #ifdef HAVE_LIBIDN2 243 " +[no]idnin (Parse IDN names " 244 "[default=on on tty])\n" 245 " +[no]idnout (Convert IDN response " 246 "[default=on on tty])\n" 247 #endif /* ifdef HAVE_LIBIDN2 */ 248 " +[no]ignore (Don't revert to TCP for " 249 "TC responses.)\n" 250 " +[no]keepalive (Request EDNS TCP " 251 "keepalive)\n" 252 " +[no]keepopen (Keep the TCP socket open " 253 "between " 254 "queries)\n" 255 " +[no]multiline (Print records in an " 256 "expanded format)\n" 257 " +ndots=### (Set search NDOTS value)\n" 258 " +[no]nsid (Request Name Server ID)\n" 259 " +[no]nssearch (Search all authoritative " 260 "nameservers)\n" 261 " +[no]onesoa (AXFR prints only one soa " 262 "record)\n" 263 " +[no]opcode=### (Set the opcode of the " 264 "request)\n" 265 " +padding=### (Set padding block size " 266 "[0])\n" 267 " +qid=### (Specify the query ID to " 268 "use when sending queries)\n" 269 " +[no]qr (Print question before " 270 "sending)\n" 271 " +[no]question (Control display of " 272 "question section)\n" 273 " +[no]raflag (Set RA flag in query " 274 "(+[no]raflag))\n" 275 " +[no]rdflag (Recursive mode " 276 "(+[no]recurse))\n" 277 " +[no]recurse (Recursive mode " 278 "(+[no]rdflag))\n" 279 " +retry=### (Set number of UDP " 280 "retries) [2]\n" 281 " +[no]rrcomments (Control display of " 282 "per-record " 283 "comments)\n" 284 " +[no]search (Set whether to use " 285 "searchlist)\n" 286 " +[no]short (Display nothing except " 287 "short\n" 288 " form of answers - global " 289 "option)\n" 290 " +[no]showbadcookie (Show BADCOOKIE message)\n" 291 " +[no]showsearch (Search with intermediate " 292 "results)\n" 293 " +[no]split=## (Split hex/base64 fields " 294 "into chunks)\n" 295 " +[no]stats (Control display of " 296 "statistics)\n" 297 " +subnet=addr (Set edns-client-subnet " 298 "option)\n" 299 " +[no]tcflag (Set TC flag in query " 300 "(+[no]tcflag))\n" 301 " +[no]tcp (TCP mode (+[no]vc))\n" 302 " +timeout=### (Set query timeout) [5]\n" 303 " +[no]tls (DNS-over-TLS mode)\n" 304 " +[no]tls-ca[=file] (Enable remote server's " 305 "TLS certificate validation)\n" 306 " +[no]tls-hostname=hostname (Explicitly set " 307 "the expected TLS hostname)\n" 308 " +[no]tls-certfile=file (Load client TLS " 309 "certificate chain from file)\n" 310 " +[no]tls-keyfile=file (Load client TLS " 311 "private key from file)\n" 312 " +[no]trace (Trace delegation down " 313 "from root " 314 "[+dnssec])\n" 315 " +tries=### (Set number of UDP " 316 "attempts) [3]\n" 317 " +[no]ttlid (Control display of ttls " 318 "in records)\n" 319 " +[no]ttlunits (Display TTLs in " 320 "human-readable units)\n" 321 " +[no]unknownformat (Print RDATA in RFC 3597 " 322 "\"unknown\" " 323 "format)\n" 324 " +[no]vc (TCP mode (+[no]tcp))\n" 325 " +[no]yaml (Present the results as " 326 "YAML)\n" 327 " +[no]zflag (Set Z flag in query)\n" 328 " global d-opts and servers (before host name) affect " 329 "all " 330 "queries.\n" 331 " local d-opts and servers (after host name) affect only " 332 "that lookup.\n" 333 " -h (print help and exit)\n" 334 " -v (print version " 335 "and exit)\n"); 336 } 337 338 /*% 339 * Callback from dighost.c to print the received message. 340 */ 341 static void 342 received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { 343 uint64_t diff; 344 time_t tnow; 345 struct tm tmnow; 346 char time_str[100]; 347 char fromtext[ISC_SOCKADDR_FORMATSIZE]; 348 349 isc_sockaddr_format(from, fromtext, sizeof(fromtext)); 350 351 if (short_form || yaml) { 352 return; 353 } 354 355 if (query->lookup->stats) { 356 const char *proto; 357 diff = isc_time_microdiff(&query->time_recv, &query->time_sent); 358 if (query->lookup->use_usec) { 359 printf(";; Query time: %ld usec\n", (long)diff); 360 } else { 361 printf(";; Query time: %ld msec\n", (long)diff / 1000); 362 } 363 if (dig_lookup_is_tls(query->lookup)) { 364 proto = "TLS"; 365 } else if (query->lookup->https_mode) { 366 if (query->lookup->http_plain) { 367 proto = query->lookup->https_get ? "HTTP-GET" 368 : "HTTP"; 369 } else { 370 proto = query->lookup->https_get ? "HTTPS-GET" 371 : "HTTPS"; 372 } 373 } else if (query->lookup->tcp_mode) { 374 proto = "TCP"; 375 } else { 376 proto = "UDP"; 377 } 378 printf(";; SERVER: %s(%s) (%s)\n", fromtext, query->userarg, 379 proto); 380 time(&tnow); 381 (void)localtime_r(&tnow, &tmnow); 382 383 if (strftime(time_str, sizeof(time_str), 384 "%a %b %d %H:%M:%S %Z %Y", &tmnow) > 0U) 385 { 386 printf(";; WHEN: %s\n", time_str); 387 } 388 if (query->lookup->doing_xfr) { 389 printf(";; XFR size: %u records (messages %u, " 390 "bytes %" PRIu64 ")\n", 391 query->rr_count, query->msg_count, 392 query->byte_count); 393 } else { 394 printf(";; MSG SIZE rcvd: %u\n", bytes); 395 } 396 if (tsigkey != NULL) { 397 if (!validated) { 398 puts(";; WARNING -- Some TSIG could not " 399 "be validated"); 400 } 401 } 402 if ((tsigkey == NULL) && (keysecret[0] != 0)) { 403 puts(";; WARNING -- TSIG key was not used."); 404 } 405 puts(""); 406 } else if (query->lookup->identify) { 407 diff = isc_time_microdiff(&query->time_recv, &query->time_sent); 408 if (query->lookup->use_usec) { 409 printf(";; Received %" PRIu64 " bytes " 410 "from %s(%s) in %ld us\n\n", 411 query->lookup->doing_xfr ? query->byte_count 412 : (uint64_t)bytes, 413 fromtext, query->userarg, (long)diff); 414 } else { 415 printf(";; Received %" PRIu64 " bytes " 416 "from %s(%s) in %ld ms\n\n", 417 query->lookup->doing_xfr ? query->byte_count 418 : (uint64_t)bytes, 419 fromtext, query->userarg, (long)diff / 1000); 420 } 421 } 422 } 423 424 /* 425 * Callback from dighost.c to print that it is trying a server. 426 * Not used in dig. 427 * XXX print_trying 428 */ 429 static void 430 trying(char *frm, dig_lookup_t *lookup) { 431 UNUSED(frm); 432 UNUSED(lookup); 433 } 434 435 /*% 436 * Internal print routine used to print short form replies. 437 */ 438 static isc_result_t 439 say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) { 440 isc_result_t result; 441 uint64_t diff; 442 char store[sizeof(" in 18446744073709551616 us.")]; 443 unsigned int styleflags = 0; 444 445 if (query->lookup->trace || query->lookup->ns_search_only) { 446 result = dns_rdatatype_totext(rdata->type, buf); 447 if (result != ISC_R_SUCCESS) { 448 return (result); 449 } 450 ADD_STRING(buf, " "); 451 } 452 453 /* Turn on rrcomments if explicitly enabled */ 454 if (query->lookup->rrcomments > 0) { 455 styleflags |= DNS_STYLEFLAG_RRCOMMENT; 456 } 457 if (query->lookup->nocrypto) { 458 styleflags |= DNS_STYLEFLAG_NOCRYPTO; 459 } 460 if (query->lookup->print_unknown_format) { 461 styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; 462 } 463 if (query->lookup->expandaaaa) { 464 styleflags |= DNS_STYLEFLAG_EXPANDAAAA; 465 } 466 result = dns_rdata_tofmttext(rdata, NULL, styleflags, 0, splitwidth, 467 " ", buf); 468 if (result == ISC_R_NOSPACE) { 469 return (result); 470 } 471 check_result(result, "dns_rdata_totext"); 472 if (query->lookup->identify) { 473 diff = isc_time_microdiff(&query->time_recv, &query->time_sent); 474 ADD_STRING(buf, " from server "); 475 ADD_STRING(buf, query->servname); 476 if (query->lookup->use_usec) { 477 snprintf(store, sizeof(store), " in %" PRIu64 " us.", 478 diff); 479 } else { 480 snprintf(store, sizeof(store), " in %" PRIu64 " ms.", 481 diff / 1000); 482 } 483 ADD_STRING(buf, store); 484 } 485 ADD_STRING(buf, "\n"); 486 return (ISC_R_SUCCESS); 487 } 488 489 /*% 490 * short_form message print handler. Calls above say_message() 491 */ 492 static isc_result_t 493 dns64prefix_answer(dns_message_t *msg, isc_buffer_t *buf) { 494 dns_rdataset_t *rdataset = NULL; 495 dns_fixedname_t fixed; 496 dns_name_t *name; 497 isc_result_t result; 498 isc_netprefix_t prefix[10]; 499 size_t i, count = 10; 500 501 name = dns_fixedname_initname(&fixed); 502 result = dns_name_fromstring(name, "ipv4only.arpa", 0, NULL); 503 check_result(result, "dns_name_fromstring"); 504 505 result = dns_message_findname(msg, DNS_SECTION_ANSWER, name, 506 dns_rdatatype_aaaa, dns_rdatatype_none, 507 NULL, &rdataset); 508 if (result == DNS_R_NXDOMAIN || result == DNS_R_NXRRSET) { 509 return (ISC_R_SUCCESS); 510 } else if (result != ISC_R_SUCCESS) { 511 return (result); 512 } 513 514 result = dns_dns64_findprefix(rdataset, prefix, &count); 515 if (result == ISC_R_NOTFOUND) { 516 return (ISC_R_SUCCESS); 517 } 518 if (count > 10) { 519 count = 10; 520 } 521 for (i = 0; i < count; i++) { 522 result = isc_netaddr_totext(&prefix[i].addr, buf); 523 if (result != ISC_R_SUCCESS) { 524 return (result); 525 } 526 result = isc_buffer_printf(buf, "/%u\n", prefix[i].prefixlen); 527 if (result != ISC_R_SUCCESS) { 528 return (result); 529 } 530 } 531 532 return (ISC_R_SUCCESS); 533 } 534 535 /*% 536 * short_form message print handler. Calls above say_message() 537 */ 538 static isc_result_t 539 short_answer(dns_message_t *msg, dns_messagetextflag_t flags, isc_buffer_t *buf, 540 dig_query_t *query) { 541 dns_name_t *name; 542 dns_rdataset_t *rdataset; 543 isc_result_t result, loopresult; 544 dns_name_t empty_name; 545 dns_rdata_t rdata = DNS_RDATA_INIT; 546 547 UNUSED(flags); 548 549 dns_name_init(&empty_name, NULL); 550 result = dns_message_firstname(msg, DNS_SECTION_ANSWER); 551 if (result == ISC_R_NOMORE) { 552 return (ISC_R_SUCCESS); 553 } else if (result != ISC_R_SUCCESS) { 554 return (result); 555 } 556 557 for (;;) { 558 name = NULL; 559 dns_message_currentname(msg, DNS_SECTION_ANSWER, &name); 560 561 for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; 562 rdataset = ISC_LIST_NEXT(rdataset, link)) 563 { 564 loopresult = dns_rdataset_first(rdataset); 565 while (loopresult == ISC_R_SUCCESS) { 566 dns_rdataset_current(rdataset, &rdata); 567 result = say_message(&rdata, query, buf); 568 if (result == ISC_R_NOSPACE) { 569 return (result); 570 } 571 check_result(result, "say_message"); 572 loopresult = dns_rdataset_next(rdataset); 573 dns_rdata_reset(&rdata); 574 } 575 } 576 result = dns_message_nextname(msg, DNS_SECTION_ANSWER); 577 if (result == ISC_R_NOMORE) { 578 break; 579 } else if (result != ISC_R_SUCCESS) { 580 return (result); 581 } 582 } 583 584 return (ISC_R_SUCCESS); 585 } 586 587 static bool 588 isdotlocal(dns_message_t *msg) { 589 isc_result_t result; 590 static unsigned char local_ndata[] = { "\005local" }; 591 static unsigned char local_offsets[] = { 0, 6 }; 592 static dns_name_t local = DNS_NAME_INITABSOLUTE(local_ndata, 593 local_offsets); 594 595 for (result = dns_message_firstname(msg, DNS_SECTION_QUESTION); 596 result == ISC_R_SUCCESS; 597 result = dns_message_nextname(msg, DNS_SECTION_QUESTION)) 598 { 599 dns_name_t *name = NULL; 600 dns_message_currentname(msg, DNS_SECTION_QUESTION, &name); 601 if (dns_name_issubdomain(name, &local)) { 602 return (true); 603 } 604 } 605 return (false); 606 } 607 608 /* 609 * Callback from dighost.c to print the reply from a server 610 */ 611 static isc_result_t 612 printmessage(dig_query_t *query, const isc_buffer_t *msgbuf, dns_message_t *msg, 613 bool headers) { 614 isc_result_t result; 615 dns_messagetextflag_t flags; 616 isc_buffer_t *buf = NULL; 617 unsigned int len = OUTPUTBUF; 618 dns_master_style_t *style = NULL; 619 unsigned int styleflags = 0; 620 bool isquery = (msg == query->lookup->sendmsg); 621 bool dns64prefix = query->lookup->dns64prefix; 622 623 UNUSED(msgbuf); 624 625 dig_idnsetup(query->lookup, true); 626 627 styleflags |= DNS_STYLEFLAG_REL_OWNER; 628 if (yaml) { 629 msg->indent.string = " "; 630 msg->indent.count = 3; 631 styleflags |= DNS_STYLEFLAG_YAML; 632 } else { 633 if (query->lookup->comments) { 634 styleflags |= DNS_STYLEFLAG_COMMENT; 635 } 636 if (query->lookup->print_unknown_format) { 637 styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; 638 } 639 /* Turn on rrcomments if explicitly enabled */ 640 if (query->lookup->rrcomments > 0) { 641 styleflags |= DNS_STYLEFLAG_RRCOMMENT; 642 } 643 if (query->lookup->ttlunits) { 644 styleflags |= DNS_STYLEFLAG_TTL_UNITS; 645 } 646 if (query->lookup->nottl) { 647 styleflags |= DNS_STYLEFLAG_NO_TTL; 648 } 649 if (query->lookup->noclass) { 650 styleflags |= DNS_STYLEFLAG_NO_CLASS; 651 } 652 if (query->lookup->nocrypto) { 653 styleflags |= DNS_STYLEFLAG_NOCRYPTO; 654 } 655 if (query->lookup->expandaaaa) { 656 styleflags |= DNS_STYLEFLAG_EXPANDAAAA; 657 } 658 if (query->lookup->multiline) { 659 styleflags |= DNS_STYLEFLAG_OMIT_OWNER; 660 styleflags |= DNS_STYLEFLAG_OMIT_CLASS; 661 styleflags |= DNS_STYLEFLAG_REL_DATA; 662 styleflags |= DNS_STYLEFLAG_OMIT_TTL; 663 styleflags |= DNS_STYLEFLAG_TTL; 664 styleflags |= DNS_STYLEFLAG_MULTILINE; 665 /* Turn on rrcomments unless explicitly disabled */ 666 if (query->lookup->rrcomments >= 0) { 667 styleflags |= DNS_STYLEFLAG_RRCOMMENT; 668 } 669 } 670 } 671 if (query->lookup->multiline || 672 (query->lookup->nottl && query->lookup->noclass)) 673 { 674 result = dns_master_stylecreate(&style, styleflags, 24, 24, 24, 675 32, 80, 8, splitwidth, mctx); 676 } else if (query->lookup->nottl || query->lookup->noclass) { 677 result = dns_master_stylecreate(&style, styleflags, 24, 24, 32, 678 40, 80, 8, splitwidth, mctx); 679 } else { 680 result = dns_master_stylecreate(&style, styleflags, 24, 32, 40, 681 48, 80, 8, splitwidth, mctx); 682 } 683 check_result(result, "dns_master_stylecreate"); 684 685 if (query->lookup->cmdline[0] != 0) { 686 if (!short_form && !dns64prefix && printcmd) { 687 printf("%s", query->lookup->cmdline); 688 } 689 query->lookup->cmdline[0] = '\0'; 690 } 691 debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders", 692 query->lookup->comments ? "comments" : "nocomments", 693 short_form ? "short_form" 694 : dns64prefix ? "dns64prefix_form" 695 : "long_form"); 696 697 flags = 0; 698 if (!headers) { 699 flags |= DNS_MESSAGETEXTFLAG_NOHEADERS; 700 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; 701 } 702 if (query->lookup->onesoa && 703 query->lookup->rdtype == dns_rdatatype_axfr) 704 { 705 flags |= (query->msg_count == 0) ? DNS_MESSAGETEXTFLAG_ONESOA 706 : DNS_MESSAGETEXTFLAG_OMITSOA; 707 } 708 if (!query->lookup->comments) { 709 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; 710 } 711 712 isc_buffer_allocate(mctx, &buf, len); 713 714 if (yaml) { 715 enum { Q = 0x1, R = 0x2 }; /* Q:query; R:ecursive */ 716 unsigned int tflag = 0; 717 char sockstr[ISC_SOCKADDR_FORMATSIZE]; 718 uint16_t sport; 719 char *hash; 720 int pf; 721 722 printf("- type: MESSAGE\n"); 723 printf(" message:\n"); 724 725 if (isquery) { 726 tflag |= Q; 727 if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) { 728 tflag |= R; 729 } 730 } else if (((msg->flags & DNS_MESSAGEFLAG_RD) != 0) && 731 ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)) 732 { 733 tflag |= R; 734 } 735 736 if (tflag == (Q | R)) { 737 printf(" type: RECURSIVE_QUERY\n"); 738 } else if (tflag == Q) { 739 printf(" type: AUTH_QUERY\n"); 740 } else if (tflag == R) { 741 printf(" type: RECURSIVE_RESPONSE\n"); 742 } else { 743 printf(" type: AUTH_RESPONSE\n"); 744 } 745 746 if (!isc_time_isepoch(&query->time_sent)) { 747 char tbuf[100]; 748 if (query->lookup->use_usec) { 749 isc_time_formatISO8601us(&query->time_sent, 750 tbuf, sizeof(tbuf)); 751 } else { 752 isc_time_formatISO8601ms(&query->time_sent, 753 tbuf, sizeof(tbuf)); 754 } 755 printf(" query_time: !!timestamp %s\n", tbuf); 756 } 757 758 if (!isquery && !isc_time_isepoch(&query->time_recv)) { 759 char tbuf[100]; 760 if (query->lookup->use_usec) { 761 isc_time_formatISO8601us(&query->time_recv, 762 tbuf, sizeof(tbuf)); 763 } else { 764 isc_time_formatISO8601ms(&query->time_recv, 765 tbuf, sizeof(tbuf)); 766 } 767 printf(" response_time: !!timestamp %s\n", tbuf); 768 } 769 770 printf(" message_size: %ub\n", 771 isc_buffer_usedlength(msgbuf)); 772 773 pf = isc_sockaddr_pf(&query->sockaddr); 774 if (pf == PF_INET || pf == PF_INET6) { 775 printf(" socket_family: %s\n", 776 pf == PF_INET ? "INET" : "INET6"); 777 778 printf(" socket_protocol: %s\n", 779 query->lookup->tcp_mode ? "TCP" : "UDP"); 780 781 sport = isc_sockaddr_getport(&query->sockaddr); 782 isc_sockaddr_format(&query->sockaddr, sockstr, 783 sizeof(sockstr)); 784 hash = strchr(sockstr, '#'); 785 if (hash != NULL) { 786 *hash = '\0'; 787 } 788 if (strcmp(sockstr, "::") == 0) { 789 strlcat(sockstr, "0", sizeof(sockstr)); 790 } 791 792 printf(" response_address: \"%s\"\n", sockstr); 793 printf(" response_port: %u\n", sport); 794 } 795 796 if (query->handle != NULL) { 797 isc_sockaddr_t saddr = 798 isc_nmhandle_localaddr(query->handle); 799 sport = isc_sockaddr_getport(&saddr); 800 isc_sockaddr_format(&saddr, sockstr, sizeof(sockstr)); 801 hash = strchr(sockstr, '#'); 802 if (hash != NULL) { 803 *hash = '\0'; 804 } 805 if (strcmp(sockstr, "::") == 0) { 806 strlcat(sockstr, "0", sizeof(sockstr)); 807 } 808 809 printf(" query_address: \"%s\"\n", sockstr); 810 printf(" query_port: %u\n", sport); 811 } 812 813 printf(" %s:\n", isquery ? "query_message_data" 814 : "response_message_data"); 815 result = dns_message_headertotext(msg, style, flags, buf); 816 } else if (query->lookup->comments && !short_form && !dns64prefix) { 817 if (query->lookup->cmdline[0] != '\0' && printcmd) { 818 printf("; %s\n", query->lookup->cmdline); 819 } 820 if (msg == query->lookup->sendmsg) { 821 printf(";; Sending:\n"); 822 } else { 823 printf(";; Got answer:\n"); 824 } 825 826 if (headers) { 827 if (isdotlocal(msg)) { 828 printf(";; WARNING: .local is reserved for " 829 "Multicast DNS\n;; You are currently " 830 "testing what happens when an mDNS " 831 "query is leaked to DNS\n"); 832 } 833 printf(";; ->>HEADER<<- opcode: %s, status: %s, " 834 "id: %u\n", 835 opcodetext[msg->opcode], 836 rcode_totext(msg->rcode), msg->id); 837 printf(";; flags:"); 838 if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) { 839 printf(" qr"); 840 } 841 if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) { 842 printf(" aa"); 843 } 844 if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) { 845 printf(" tc"); 846 } 847 if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) { 848 printf(" rd"); 849 } 850 if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) { 851 printf(" ra"); 852 } 853 if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) { 854 printf(" ad"); 855 } 856 if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) { 857 printf(" cd"); 858 } 859 if ((msg->flags & 0x0040U) != 0) { 860 printf("; MBZ: 0x4"); 861 } 862 863 printf("; QUERY: %u, ANSWER: %u, " 864 "AUTHORITY: %u, ADDITIONAL: %u\n", 865 msg->counts[DNS_SECTION_QUESTION], 866 msg->counts[DNS_SECTION_ANSWER], 867 msg->counts[DNS_SECTION_AUTHORITY], 868 msg->counts[DNS_SECTION_ADDITIONAL]); 869 870 if (msg != query->lookup->sendmsg && 871 (msg->flags & DNS_MESSAGEFLAG_RD) != 0 && 872 (msg->flags & DNS_MESSAGEFLAG_RA) == 0) 873 { 874 printf(";; WARNING: recursion requested " 875 "but not available\n"); 876 } 877 } 878 if (msg != query->lookup->sendmsg && 879 query->lookup->edns != -1 && msg->opt == NULL && 880 (msg->rcode == dns_rcode_formerr || 881 msg->rcode == dns_rcode_notimp)) 882 { 883 printf("\n;; WARNING: EDNS query returned status " 884 "%s - retry with '%s+noedns'\n", 885 rcode_totext(msg->rcode), 886 query->lookup->dnssec ? "+nodnssec " : ""); 887 } 888 if (msg != query->lookup->sendmsg && extrabytes != 0U) { 889 printf(";; WARNING: Message has %u extra byte%s at " 890 "end\n", 891 extrabytes, extrabytes != 0 ? "s" : ""); 892 } 893 } 894 895 repopulate_buffer: 896 897 if (query->lookup->comments && headers && !short_form && !dns64prefix) { 898 result = dns_message_pseudosectiontotext( 899 msg, DNS_PSEUDOSECTION_OPT, style, flags, buf); 900 if (result == ISC_R_NOSPACE) { 901 buftoosmall: 902 len += OUTPUTBUF; 903 isc_buffer_free(&buf); 904 isc_buffer_allocate(mctx, &buf, len); 905 goto repopulate_buffer; 906 } 907 check_result(result, "dns_message_pseudosectiontotext"); 908 } 909 910 if (query->lookup->section_question && headers) { 911 if (!short_form && !dns64prefix) { 912 result = dns_message_sectiontotext( 913 msg, DNS_SECTION_QUESTION, style, flags, buf); 914 if (result == ISC_R_NOSPACE) { 915 goto buftoosmall; 916 } 917 check_result(result, "dns_message_sectiontotext"); 918 } 919 } 920 if (query->lookup->section_answer) { 921 if (!short_form && !dns64prefix) { 922 result = dns_message_sectiontotext( 923 msg, DNS_SECTION_ANSWER, style, flags, buf); 924 if (result == ISC_R_NOSPACE) { 925 goto buftoosmall; 926 } 927 check_result(result, "dns_message_sectiontotext"); 928 } else if (dns64prefix) { 929 result = dns64prefix_answer(msg, buf); 930 if (result == ISC_R_NOSPACE) { 931 goto buftoosmall; 932 } 933 check_result(result, "dns64prefix_answer"); 934 } else { 935 result = short_answer(msg, flags, buf, query); 936 if (result == ISC_R_NOSPACE) { 937 goto buftoosmall; 938 } 939 check_result(result, "short_answer"); 940 } 941 } 942 if (query->lookup->section_authority) { 943 if (!short_form && !dns64prefix) { 944 result = dns_message_sectiontotext( 945 msg, DNS_SECTION_AUTHORITY, style, flags, buf); 946 if (result == ISC_R_NOSPACE) { 947 goto buftoosmall; 948 } 949 check_result(result, "dns_message_sectiontotext"); 950 } 951 } 952 if (query->lookup->section_additional) { 953 if (!short_form && !dns64prefix) { 954 result = dns_message_sectiontotext( 955 msg, DNS_SECTION_ADDITIONAL, style, flags, buf); 956 if (result == ISC_R_NOSPACE) { 957 goto buftoosmall; 958 } 959 check_result(result, "dns_message_sectiontotext"); 960 /* 961 * Only print the signature on the first record. 962 */ 963 if (headers) { 964 result = dns_message_pseudosectiontotext( 965 msg, DNS_PSEUDOSECTION_TSIG, style, 966 flags, buf); 967 if (result == ISC_R_NOSPACE) { 968 goto buftoosmall; 969 } 970 check_result(result, "dns_message_" 971 "pseudosectiontotext"); 972 result = dns_message_pseudosectiontotext( 973 msg, DNS_PSEUDOSECTION_SIG0, style, 974 flags, buf); 975 if (result == ISC_R_NOSPACE) { 976 goto buftoosmall; 977 } 978 check_result(result, "dns_message_" 979 "pseudosectiontotext"); 980 } 981 } 982 } 983 984 if (headers && query->lookup->comments && !short_form && !yaml) { 985 printf("\n"); 986 } 987 988 printf("%.*s", (int)isc_buffer_usedlength(buf), 989 (char *)isc_buffer_base(buf)); 990 isc_buffer_free(&buf); 991 992 if (style != NULL) { 993 dns_master_styledestroy(&style, mctx); 994 } 995 996 dig_idnsetup(query->lookup, false); 997 998 return (result); 999 } 1000 1001 /*% 1002 * print the greeting message when the program first starts up. 1003 */ 1004 static void 1005 printgreeting(int argc, char **argv, dig_lookup_t *lookup) { 1006 int i; 1007 static bool first = true; 1008 char append[MXNAME]; 1009 1010 if (printcmd) { 1011 snprintf(lookup->cmdline, sizeof(lookup->cmdline), 1012 "%s; <<>> DiG %s <<>>", first ? "\n" : "", 1013 PACKAGE_VERSION); 1014 i = 1; 1015 while (i < argc) { 1016 snprintf(append, sizeof(append), " %s", argv[i++]); 1017 strlcat(lookup->cmdline, append, 1018 sizeof(lookup->cmdline)); 1019 } 1020 strlcat(lookup->cmdline, "\n", sizeof(lookup->cmdline)); 1021 if (first && addresscount != 0) { 1022 snprintf(append, sizeof(append), 1023 "; (%d server%s found)\n", addresscount, 1024 addresscount > 1 ? "s" : ""); 1025 strlcat(lookup->cmdline, append, 1026 sizeof(lookup->cmdline)); 1027 } 1028 if (first) { 1029 snprintf(append, sizeof(append), 1030 ";; global options:%s%s\n", 1031 short_form ? " +short" : "", 1032 printcmd ? " +cmd" : ""); 1033 first = false; 1034 strlcat(lookup->cmdline, append, 1035 sizeof(lookup->cmdline)); 1036 } 1037 } 1038 } 1039 1040 #define FULLCHECK(A) \ 1041 do { \ 1042 size_t _l = strlen(cmd); \ 1043 if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \ 1044 goto invalid_option; \ 1045 } while (0) 1046 #define FULLCHECK2(A, B) \ 1047 do { \ 1048 size_t _l = strlen(cmd); \ 1049 if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \ 1050 (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \ 1051 goto invalid_option; \ 1052 } while (0) 1053 #define FULLCHECK6(A, B, C, D, E, F) \ 1054 do { \ 1055 size_t _l = strlen(cmd); \ 1056 if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \ 1057 (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0) && \ 1058 (_l >= sizeof(C) || strncasecmp(cmd, C, _l) != 0) && \ 1059 (_l >= sizeof(D) || strncasecmp(cmd, D, _l) != 0) && \ 1060 (_l >= sizeof(E) || strncasecmp(cmd, E, _l) != 0) && \ 1061 (_l >= sizeof(F) || strncasecmp(cmd, F, _l) != 0)) \ 1062 goto invalid_option; \ 1063 } while (0) 1064 1065 static bool 1066 plus_tls_options(const char *cmd, const char *value, const bool state, 1067 dig_lookup_t *lookup) { 1068 /* 1069 * Using TLS implies "TCP-like" mode. 1070 */ 1071 if (!lookup->tcp_mode_set) { 1072 lookup->tcp_mode = state; 1073 } 1074 switch (cmd[3]) { 1075 case '-': 1076 /* 1077 * Assume that if any of the +tls-* options are set, then we 1078 * need to verify the remote certificate (compatibility with 1079 * kdig). 1080 */ 1081 if (state) { 1082 lookup->tls_ca_set = state; 1083 } 1084 switch (cmd[4]) { 1085 case 'c': 1086 switch (cmd[5]) { 1087 case 'a': 1088 FULLCHECK("tls-ca"); 1089 lookup->tls_ca_set = state; 1090 if (state && value != NULL) { 1091 lookup->tls_ca_file = 1092 isc_mem_strdup(mctx, value); 1093 } 1094 break; 1095 case 'e': 1096 FULLCHECK("tls-certfile"); 1097 lookup->tls_cert_file_set = state; 1098 if (state) { 1099 if (value != NULL && *value != '\0') { 1100 lookup->tls_cert_file = 1101 isc_mem_strdup(mctx, 1102 value); 1103 } else { 1104 fprintf(stderr, 1105 ";; TLS certificate " 1106 "file is " 1107 "not specified\n"); 1108 goto invalid_option; 1109 } 1110 } 1111 break; 1112 default: 1113 goto invalid_option; 1114 } 1115 break; 1116 case 'h': 1117 FULLCHECK("tls-hostname"); 1118 lookup->tls_hostname_set = state; 1119 if (state) { 1120 if (value != NULL && *value != '\0') { 1121 lookup->tls_hostname = 1122 isc_mem_strdup(mctx, value); 1123 } else { 1124 fprintf(stderr, ";; TLS hostname is " 1125 "not specified\n"); 1126 goto invalid_option; 1127 } 1128 } 1129 break; 1130 case 'k': 1131 FULLCHECK("tls-keyfile"); 1132 lookup->tls_key_file_set = state; 1133 if (state) { 1134 if (value != NULL && *value != '\0') { 1135 lookup->tls_key_file = 1136 isc_mem_strdup(mctx, value); 1137 } else { 1138 fprintf(stderr, 1139 ";; TLS private key file is " 1140 "not specified\n"); 1141 goto invalid_option; 1142 } 1143 } 1144 break; 1145 default: 1146 goto invalid_option; 1147 } 1148 break; 1149 case '\0': 1150 FULLCHECK("tls"); 1151 lookup->tls_mode = state; 1152 break; 1153 default: 1154 goto invalid_option; 1155 } 1156 1157 return true; 1158 invalid_option: 1159 return false; 1160 } 1161 1162 /*% 1163 * We're not using isc_commandline_parse() here since the command line 1164 * syntax of dig is quite a bit different from that which can be described 1165 * by that routine. 1166 * XXX doc options 1167 */ 1168 1169 static dig_lookup_t * 1170 plus_option(char *option, bool is_batchfile, bool *need_clone, 1171 dig_lookup_t *lookup) { 1172 isc_result_t result; 1173 char *cmd, *value, *last = NULL, *code, *extra; 1174 uint32_t num; 1175 bool state = true; 1176 size_t n; 1177 1178 INSIST(option != NULL); 1179 1180 if ((cmd = strtok_r(option, "=", &last)) == NULL) { 1181 printf(";; Invalid option %s\n", option); 1182 return (lookup); 1183 } 1184 if (strncasecmp(cmd, "no", 2) == 0) { 1185 cmd += 2; 1186 state = false; 1187 } 1188 /* parse the rest of the string */ 1189 value = strtok_r(NULL, "", &last); 1190 1191 switch (cmd[0]) { 1192 case 'a': 1193 switch (cmd[1]) { 1194 case 'a': /* aaonly / aaflag */ 1195 FULLCHECK2("aaonly", "aaflag"); 1196 lookup->aaonly = state; 1197 break; 1198 case 'd': 1199 switch (cmd[2]) { 1200 case 'd': /* additional */ 1201 FULLCHECK("additional"); 1202 lookup->section_additional = state; 1203 break; 1204 case 'f': /* adflag */ 1205 case '\0': /* +ad is a synonym for +adflag */ 1206 FULLCHECK("adflag"); 1207 lookup->adflag = state; 1208 break; 1209 default: 1210 goto invalid_option; 1211 } 1212 break; 1213 case 'l': /* all */ 1214 FULLCHECK("all"); 1215 lookup->section_question = state; 1216 lookup->section_authority = state; 1217 lookup->section_answer = state; 1218 lookup->section_additional = state; 1219 lookup->comments = state; 1220 lookup->stats = state; 1221 printcmd = state; 1222 break; 1223 case 'n': /* answer */ 1224 FULLCHECK("answer"); 1225 lookup->section_answer = state; 1226 break; 1227 case 'u': /* authority */ 1228 FULLCHECK("authority"); 1229 lookup->section_authority = state; 1230 break; 1231 default: 1232 goto invalid_option; 1233 } 1234 break; 1235 case 'b': 1236 switch (cmd[1]) { 1237 case 'a': /* badcookie */ 1238 FULLCHECK("badcookie"); 1239 lookup->badcookie = state; 1240 break; 1241 case 'e': /* besteffort */ 1242 FULLCHECK("besteffort"); 1243 lookup->besteffort = state; 1244 break; 1245 case 'u': /* bufsize */ 1246 FULLCHECK("bufsize"); 1247 if (!state) { 1248 goto invalid_option; 1249 } 1250 if (value == NULL) { 1251 lookup->udpsize = DEFAULT_EDNS_BUFSIZE; 1252 break; 1253 } 1254 result = parse_uint(&num, value, COMMSIZE, 1255 "buffer size"); 1256 if (result != ISC_R_SUCCESS) { 1257 warn("Couldn't parse buffer size"); 1258 goto exit_or_usage; 1259 } 1260 lookup->udpsize = num; 1261 break; 1262 default: 1263 goto invalid_option; 1264 } 1265 break; 1266 case 'c': 1267 switch (cmd[1]) { 1268 case 'd': /* cdflag */ 1269 switch (cmd[2]) { 1270 case 'f': /* cdflag */ 1271 case '\0': /* +cd is a synonym for +cdflag */ 1272 FULLCHECK("cdflag"); 1273 lookup->cdflag = state; 1274 break; 1275 default: 1276 goto invalid_option; 1277 } 1278 break; 1279 case 'l': /* class */ 1280 /* keep +cl for backwards compatibility */ 1281 FULLCHECK2("cl", "class"); 1282 lookup->noclass = !state; 1283 break; 1284 case 'm': /* cmd */ 1285 FULLCHECK("cmd"); 1286 printcmd = state; 1287 break; 1288 case 'o': /* comments */ 1289 switch (cmd[2]) { 1290 case 'm': 1291 FULLCHECK("comments"); 1292 lookup->comments = state; 1293 if (lookup == default_lookup) { 1294 pluscomm = state; 1295 } 1296 break; 1297 case 'o': /* cookie */ 1298 FULLCHECK("cookie"); 1299 if (state && lookup->edns == -1) { 1300 lookup->edns = DEFAULT_EDNS_VERSION; 1301 } 1302 lookup->sendcookie = state; 1303 if (value != NULL) { 1304 n = strlcpy(hexcookie, value, 1305 sizeof(hexcookie)); 1306 if (n >= sizeof(hexcookie)) { 1307 warn("COOKIE data too large"); 1308 goto exit_or_usage; 1309 } 1310 lookup->cookie = hexcookie; 1311 } else { 1312 lookup->cookie = NULL; 1313 } 1314 break; 1315 default: 1316 goto invalid_option; 1317 } 1318 break; 1319 case 'r': 1320 FULLCHECK("crypto"); 1321 lookup->nocrypto = !state; 1322 break; 1323 default: 1324 goto invalid_option; 1325 } 1326 break; 1327 case 'd': 1328 switch (cmd[1]) { 1329 case 'e': /* defname */ 1330 FULLCHECK("defname"); 1331 if (!lookup->trace) { 1332 usesearch = state; 1333 } 1334 break; 1335 case 'n': 1336 switch (cmd[2]) { 1337 case 's': 1338 switch (cmd[3]) { 1339 case '6': /* dns64prefix */ 1340 FULLCHECK("dns64prefix"); 1341 if (state) { 1342 if (*need_clone) { 1343 lookup = clone_lookup( 1344 default_lookup, 1345 true); 1346 } 1347 *need_clone = true; 1348 lookup->dns64prefix = state; 1349 strlcpy(lookup->textname, 1350 "ipv4only.arpa", 1351 sizeof(lookup->textname)); 1352 printcmd = false; 1353 lookup->section_additional = 1354 false; 1355 lookup->section_answer = true; 1356 lookup->section_authority = 1357 false; 1358 lookup->section_question = 1359 false; 1360 lookup->comments = false; 1361 lookup->stats = false; 1362 lookup->rrcomments = -1; 1363 lookup->rdtype = 1364 dns_rdatatype_aaaa; 1365 lookup->rdtypeset = true; 1366 ISC_LIST_APPEND(lookup_list, 1367 lookup, link); 1368 } 1369 break; 1370 case 's': /* dnssec */ 1371 FULLCHECK("dnssec"); 1372 dnssec: 1373 if (state && lookup->edns == -1) { 1374 lookup->edns = 1375 DEFAULT_EDNS_VERSION; 1376 } 1377 lookup->dnssec = state; 1378 break; 1379 default: 1380 goto invalid_option; 1381 } 1382 break; 1383 default: 1384 goto invalid_option; 1385 } 1386 break; 1387 case 'o': /* domain ... but treat "do" as synonym for dnssec */ 1388 if (cmd[2] == '\0') { 1389 goto dnssec; 1390 } 1391 FULLCHECK("domain"); 1392 if (value == NULL) { 1393 goto need_value; 1394 } 1395 if (!state) { 1396 goto invalid_option; 1397 } 1398 strlcpy(domainopt, value, sizeof(domainopt)); 1399 break; 1400 case 's': /* dscp */ 1401 /* obsolete */ 1402 FULLCHECK("dscp"); 1403 fprintf(stderr, ";; +dscp option is obsolete " 1404 "and has no effect"); 1405 break; 1406 default: 1407 goto invalid_option; 1408 } 1409 break; 1410 case 'e': 1411 switch (cmd[1]) { 1412 case 'd': 1413 switch (cmd[2]) { 1414 case 'n': 1415 switch (cmd[3]) { 1416 case 's': 1417 switch (cmd[4]) { 1418 case 0: 1419 FULLCHECK("edns"); 1420 if (!state) { 1421 lookup->edns = -1; 1422 break; 1423 } 1424 if (value == NULL) { 1425 lookup->edns = 1426 DEFAULT_EDNS_VERSION; 1427 break; 1428 } 1429 result = parse_uint(&num, value, 1430 255, 1431 "edns"); 1432 if (result != ISC_R_SUCCESS) { 1433 warn("Couldn't parse " 1434 "edns"); 1435 goto exit_or_usage; 1436 } 1437 lookup->edns = num; 1438 break; 1439 case 'f': 1440 FULLCHECK("ednsflags"); 1441 if (!state) { 1442 lookup->ednsflags = 0; 1443 break; 1444 } 1445 if (value == NULL) { 1446 lookup->ednsflags = 0; 1447 break; 1448 } 1449 result = parse_xint( 1450 &num, value, 0xffff, 1451 "ednsflags"); 1452 if (result != ISC_R_SUCCESS) { 1453 warn("Couldn't parse " 1454 "ednsflags"); 1455 goto exit_or_usage; 1456 } 1457 if (lookup->edns == -1) { 1458 lookup->edns = 1459 DEFAULT_EDNS_VERSION; 1460 } 1461 lookup->ednsflags = num; 1462 break; 1463 case 'n': 1464 FULLCHECK("ednsnegotiation"); 1465 lookup->ednsneg = state; 1466 break; 1467 case 'o': 1468 FULLCHECK("ednsopt"); 1469 if (!state) { 1470 lookup->ednsoptscnt = 0; 1471 break; 1472 } 1473 code = NULL; 1474 if (value != NULL) { 1475 code = strtok_r(value, 1476 ":", 1477 &last); 1478 } 1479 if (code == NULL) { 1480 warn("ednsopt no " 1481 "code point " 1482 "specified"); 1483 goto exit_or_usage; 1484 } 1485 extra = strtok_r(NULL, "\0", 1486 &last); 1487 save_opt(lookup, code, extra); 1488 break; 1489 default: 1490 goto invalid_option; 1491 } 1492 break; 1493 default: 1494 goto invalid_option; 1495 } 1496 break; 1497 default: 1498 goto invalid_option; 1499 } 1500 break; 1501 case 'x': 1502 switch (cmd[2]) { 1503 case 'p': 1504 switch (cmd[3]) { 1505 case 'a': 1506 FULLCHECK("expandaaaa"); 1507 lookup->expandaaaa = state; 1508 break; 1509 case 'i': 1510 FULLCHECK("expire"); 1511 lookup->expire = state; 1512 break; 1513 default: 1514 goto invalid_option; 1515 } 1516 break; 1517 default: 1518 goto invalid_option; 1519 } 1520 break; 1521 default: 1522 goto invalid_option; 1523 } 1524 break; 1525 case 'f': /* fail */ 1526 switch (cmd[1]) { 1527 case 'a': 1528 FULLCHECK("fail"); 1529 lookup->servfail_stops = state; 1530 break; 1531 case 'u': 1532 FULLCHECK("fuzztime"); 1533 lookup->fuzzing = state; 1534 if (lookup->fuzzing) { 1535 if (value == NULL) { 1536 lookup->fuzztime = 0x622acce1; 1537 break; 1538 } 1539 result = parse_uint(&num, value, 0xffffffff, 1540 "fuzztime"); 1541 if (result != ISC_R_SUCCESS) { 1542 warn("Couldn't parse fuzztime"); 1543 goto exit_or_usage; 1544 } 1545 lookup->fuzztime = num; 1546 } 1547 break; 1548 default: 1549 goto invalid_option; 1550 } 1551 break; 1552 case 'h': 1553 switch (cmd[1]) { 1554 case 'e': /* header-only */ 1555 FULLCHECK("header-only"); 1556 lookup->header_only = state; 1557 break; 1558 case 't': 1559 FULLCHECK6("https", "https-get", "https-post", 1560 "http-plain", "http-plain-get", 1561 "http-plain-post"); 1562 #if HAVE_LIBNGHTTP2 1563 if (lookup->https_path != NULL) { 1564 isc_mem_free(mctx, lookup->https_path); 1565 lookup->https_path = NULL; 1566 } 1567 if (!state) { 1568 lookup->https_mode = false; 1569 break; 1570 } 1571 lookup->https_mode = true; 1572 if (cmd[4] == '-') { 1573 lookup->http_plain = true; 1574 switch (cmd[10]) { 1575 case '\0': 1576 FULLCHECK("http-plain"); 1577 break; 1578 case '-': 1579 switch (cmd[11]) { 1580 case 'p': 1581 FULLCHECK("http-plain-post"); 1582 break; 1583 case 'g': 1584 FULLCHECK("http-plain-get"); 1585 lookup->https_get = true; 1586 break; 1587 } 1588 break; 1589 default: 1590 goto invalid_option; 1591 } 1592 } else { 1593 switch (cmd[5]) { 1594 case '\0': 1595 FULLCHECK("https"); 1596 break; 1597 case '-': 1598 switch (cmd[6]) { 1599 case 'p': 1600 FULLCHECK("https-post"); 1601 break; 1602 case 'g': 1603 FULLCHECK("https-get"); 1604 lookup->https_get = true; 1605 break; 1606 } 1607 break; 1608 default: 1609 goto invalid_option; 1610 } 1611 } 1612 if (!lookup->tcp_mode_set) { 1613 lookup->tcp_mode = state; 1614 } 1615 if (value == NULL) { 1616 lookup->https_path = isc_mem_strdup( 1617 mctx, ISC_NM_HTTP_DEFAULT_PATH); 1618 } else { 1619 if (!isc_nm_http_path_isvalid(value)) { 1620 fprintf(stderr, 1621 ";; The given HTTP path \"%s\" " 1622 "is not " 1623 "a valid absolute path\n", 1624 value); 1625 goto invalid_option; 1626 } 1627 lookup->https_path = isc_mem_strdup(mctx, 1628 value); 1629 } 1630 #else 1631 fprintf(stderr, ";; DoH support not enabled\n"); 1632 #endif 1633 break; 1634 default: 1635 goto invalid_option; 1636 } 1637 break; 1638 case 'i': 1639 switch (cmd[1]) { 1640 case 'd': /* identify */ 1641 switch (cmd[2]) { 1642 case 'e': 1643 FULLCHECK("identify"); 1644 lookup->identify = state; 1645 break; 1646 case 'n': 1647 switch (cmd[3]) { 1648 case 'i': 1649 FULLCHECK("idnin"); 1650 #ifndef HAVE_LIBIDN2 1651 fprintf(stderr, ";; IDN input support" 1652 " not enabled\n"); 1653 #else /* ifndef HAVE_LIBIDN2 */ 1654 lookup->idnin = state; 1655 #endif /* ifndef HAVE_LIBIDN2 */ 1656 break; 1657 case 'o': 1658 FULLCHECK("idnout"); 1659 #ifndef HAVE_LIBIDN2 1660 fprintf(stderr, ";; IDN output support" 1661 " not enabled\n"); 1662 #else /* ifndef HAVE_LIBIDN2 */ 1663 lookup->idnout = state; 1664 #endif /* ifndef HAVE_LIBIDN2 */ 1665 break; 1666 default: 1667 goto invalid_option; 1668 } 1669 break; 1670 default: 1671 goto invalid_option; 1672 } 1673 break; 1674 case 'g': /* ignore */ 1675 default: /* 1676 * Inherits default for compatibility (+[no]i*). 1677 */ 1678 FULLCHECK("ignore"); 1679 lookup->ignore = state; 1680 } 1681 break; 1682 case 'k': 1683 switch (cmd[1]) { 1684 case 'e': 1685 switch (cmd[2]) { 1686 case 'e': 1687 switch (cmd[3]) { 1688 case 'p': 1689 switch (cmd[4]) { 1690 case 'a': 1691 FULLCHECK("keepalive"); 1692 lookup->tcp_keepalive = state; 1693 break; 1694 case 'o': 1695 FULLCHECK("keepopen"); 1696 keep_open = state; 1697 break; 1698 default: 1699 goto invalid_option; 1700 } 1701 break; 1702 default: 1703 goto invalid_option; 1704 } 1705 break; 1706 default: 1707 goto invalid_option; 1708 } 1709 break; 1710 default: 1711 goto invalid_option; 1712 } 1713 break; 1714 case 'm': /* multiline */ 1715 switch (cmd[1]) { 1716 case 'a': 1717 FULLCHECK("mapped"); 1718 fprintf(stderr, ";; +mapped option is deprecated"); 1719 break; 1720 case 'u': 1721 FULLCHECK("multiline"); 1722 lookup->multiline = state; 1723 break; 1724 default: 1725 goto invalid_option; 1726 } 1727 break; 1728 case 'n': 1729 switch (cmd[1]) { 1730 case 'd': /* ndots */ 1731 FULLCHECK("ndots"); 1732 if (value == NULL) { 1733 goto need_value; 1734 } 1735 if (!state) { 1736 goto invalid_option; 1737 } 1738 result = parse_uint(&num, value, MAXNDOTS, "ndots"); 1739 if (result != ISC_R_SUCCESS) { 1740 warn("Couldn't parse ndots"); 1741 goto exit_or_usage; 1742 } 1743 ndots = num; 1744 break; 1745 case 's': 1746 switch (cmd[2]) { 1747 case 'i': /* nsid */ 1748 FULLCHECK("nsid"); 1749 if (state && lookup->edns == -1) { 1750 lookup->edns = DEFAULT_EDNS_VERSION; 1751 } 1752 lookup->nsid = state; 1753 break; 1754 case 's': /* nssearch */ 1755 FULLCHECK("nssearch"); 1756 lookup->ns_search_only = state; 1757 if (state) { 1758 lookup->trace_root = true; 1759 lookup->recurse = true; 1760 lookup->identify = true; 1761 lookup->stats = false; 1762 lookup->comments = false; 1763 lookup->section_additional = false; 1764 lookup->section_authority = false; 1765 lookup->section_question = false; 1766 lookup->rdtype = dns_rdatatype_ns; 1767 lookup->rdtypeset = true; 1768 short_form = true; 1769 lookup->rrcomments = 0; 1770 } 1771 break; 1772 default: 1773 goto invalid_option; 1774 } 1775 break; 1776 default: 1777 goto invalid_option; 1778 } 1779 break; 1780 case 'o': 1781 switch (cmd[1]) { 1782 case 'n': 1783 FULLCHECK("onesoa"); 1784 lookup->onesoa = state; 1785 break; 1786 case 'p': 1787 FULLCHECK("opcode"); 1788 if (!state) { 1789 lookup->opcode = 0; /* default - query */ 1790 break; 1791 } 1792 if (value == NULL) { 1793 goto need_value; 1794 } 1795 for (num = 0; 1796 num < sizeof(opcodetext) / sizeof(opcodetext[0]); 1797 num++) 1798 { 1799 if (strcasecmp(opcodetext[num], value) == 0) { 1800 break; 1801 } 1802 } 1803 if (num < 16) { 1804 lookup->opcode = (dns_opcode_t)num; 1805 break; 1806 } 1807 result = parse_uint(&num, value, 15, "opcode"); 1808 if (result != ISC_R_SUCCESS) { 1809 warn("Couldn't parse opcode"); 1810 goto exit_or_usage; 1811 } 1812 lookup->opcode = (dns_opcode_t)num; 1813 break; 1814 default: 1815 goto invalid_option; 1816 } 1817 break; 1818 case 'p': 1819 FULLCHECK("padding"); 1820 if (state && lookup->edns == -1) { 1821 lookup->edns = DEFAULT_EDNS_VERSION; 1822 } 1823 if (value == NULL) { 1824 goto need_value; 1825 } 1826 result = parse_uint(&num, value, 512, "padding"); 1827 if (result != ISC_R_SUCCESS) { 1828 warn("Couldn't parse padding"); 1829 goto exit_or_usage; 1830 } 1831 lookup->padding = (uint16_t)num; 1832 break; 1833 case 'q': 1834 switch (cmd[1]) { 1835 case 'i': /* qid */ 1836 FULLCHECK("qid"); 1837 if (!state) { 1838 lookup->setqid = false; 1839 lookup->qid = 0; 1840 break; 1841 } 1842 if (value == NULL) { 1843 goto need_value; 1844 } 1845 result = parse_uint(&num, value, MAXQID, "qid"); 1846 if (result != ISC_R_SUCCESS) { 1847 warn("Couldn't parse qid"); 1848 goto exit_or_usage; 1849 } 1850 lookup->setqid = true; 1851 lookup->qid = num; 1852 break; 1853 case 'r': /* qr */ 1854 FULLCHECK("qr"); 1855 lookup->qr = state; 1856 break; 1857 case 'u': /* question */ 1858 FULLCHECK("question"); 1859 lookup->section_question = state; 1860 if (lookup == default_lookup) { 1861 plusquest = state; 1862 } 1863 break; 1864 default: 1865 goto invalid_option; 1866 } 1867 break; 1868 case 'r': 1869 switch (cmd[1]) { 1870 case 'a': /* raflag */ 1871 FULLCHECK("raflag"); 1872 lookup->raflag = state; 1873 break; 1874 case 'd': /* rdflag */ 1875 FULLCHECK("rdflag"); 1876 lookup->recurse = state; 1877 break; 1878 case 'e': 1879 switch (cmd[2]) { 1880 case 'c': /* recurse */ 1881 FULLCHECK("recurse"); 1882 lookup->recurse = state; 1883 break; 1884 case 't': /* retry / retries */ 1885 FULLCHECK2("retry", "retries"); 1886 if (value == NULL) { 1887 goto need_value; 1888 } 1889 if (!state) { 1890 goto invalid_option; 1891 } 1892 result = parse_uint(&lookup->retries, value, 1893 MAXTRIES - 1, "retries"); 1894 if (result != ISC_R_SUCCESS) { 1895 warn("Couldn't parse retries"); 1896 goto exit_or_usage; 1897 } 1898 lookup->retries++; 1899 break; 1900 default: 1901 goto invalid_option; 1902 } 1903 break; 1904 case 'r': /* rrcomments */ 1905 FULLCHECK("rrcomments"); 1906 lookup->rrcomments = state ? 1 : -1; 1907 break; 1908 default: 1909 goto invalid_option; 1910 } 1911 break; 1912 case 's': 1913 switch (cmd[1]) { 1914 case 'e': /* search */ 1915 FULLCHECK("search"); 1916 if (!lookup->trace) { 1917 usesearch = state; 1918 } 1919 break; 1920 case 'h': 1921 if (cmd[2] != 'o') { 1922 goto invalid_option; 1923 } 1924 switch (cmd[3]) { 1925 case 'r': /* short */ 1926 FULLCHECK("short"); 1927 short_form = state; 1928 if (state) { 1929 printcmd = false; 1930 lookup->section_additional = false; 1931 lookup->section_answer = true; 1932 lookup->section_authority = false; 1933 lookup->section_question = false; 1934 lookup->comments = false; 1935 lookup->stats = false; 1936 lookup->rrcomments = -1; 1937 } 1938 break; 1939 case 'w': /* showsearch */ 1940 switch (cmd[4]) { 1941 case 'b': 1942 FULLCHECK("showbadcookie"); 1943 lookup->showbadcookie = state; 1944 break; 1945 case 's': 1946 FULLCHECK("showsearch"); 1947 if (!lookup->trace) { 1948 showsearch = state; 1949 usesearch = state; 1950 } 1951 break; 1952 default: 1953 goto invalid_option; 1954 } 1955 break; 1956 default: 1957 goto invalid_option; 1958 } 1959 break; 1960 case 'i': /* sigchase */ 1961 FULLCHECK("sigchase"); 1962 fprintf(stderr, ";; +sigchase option is deprecated"); 1963 break; 1964 case 'p': /* split */ 1965 FULLCHECK("split"); 1966 if (value != NULL && !state) { 1967 goto invalid_option; 1968 } 1969 if (!state) { 1970 splitwidth = 0; 1971 break; 1972 } else if (value == NULL) { 1973 break; 1974 } 1975 1976 result = parse_uint(&splitwidth, value, 1023, "split"); 1977 if ((splitwidth % 4) != 0U) { 1978 splitwidth = ((splitwidth + 3) / 4) * 4; 1979 fprintf(stderr, 1980 ";; Warning, split must be " 1981 "a multiple of 4; adjusting " 1982 "to %u\n", 1983 splitwidth); 1984 } 1985 /* 1986 * There is an adjustment done in the 1987 * totext_<rrtype>() functions which causes 1988 * splitwidth to shrink. This is okay when we're 1989 * using the default width but incorrect in this 1990 * case, so we correct for it 1991 */ 1992 if (splitwidth) { 1993 splitwidth += 3; 1994 } 1995 if (result != ISC_R_SUCCESS) { 1996 warn("Couldn't parse split"); 1997 goto exit_or_usage; 1998 } 1999 break; 2000 case 't': /* stats */ 2001 FULLCHECK("stats"); 2002 lookup->stats = state; 2003 break; 2004 case 'u': /* subnet */ 2005 FULLCHECK("subnet"); 2006 if (state && value == NULL) { 2007 goto need_value; 2008 } 2009 if (!state) { 2010 if (lookup->ecs_addr != NULL) { 2011 isc_mem_free(mctx, lookup->ecs_addr); 2012 lookup->ecs_addr = NULL; 2013 } 2014 break; 2015 } 2016 if (lookup->edns == -1) { 2017 lookup->edns = DEFAULT_EDNS_VERSION; 2018 } 2019 if (lookup->ecs_addr != NULL) { 2020 isc_mem_free(mctx, lookup->ecs_addr); 2021 lookup->ecs_addr = NULL; 2022 } 2023 result = parse_netprefix(&lookup->ecs_addr, value); 2024 if (result != ISC_R_SUCCESS) { 2025 warn("Couldn't parse client"); 2026 goto exit_or_usage; 2027 } 2028 break; 2029 default: 2030 goto invalid_option; 2031 } 2032 break; 2033 case 't': 2034 switch (cmd[1]) { 2035 case 'c': /* tcp */ 2036 switch (cmd[2]) { 2037 case 'f': 2038 FULLCHECK("tcflag"); 2039 lookup->tcflag = state; 2040 break; 2041 case 'p': 2042 FULLCHECK("tcp"); 2043 if (!is_batchfile) { 2044 lookup->tcp_mode = state; 2045 lookup->tcp_mode_set = true; 2046 } 2047 break; 2048 default: 2049 goto invalid_option; 2050 } 2051 break; 2052 case 'i': /* timeout */ 2053 FULLCHECK("timeout"); 2054 if (value == NULL) { 2055 goto need_value; 2056 } 2057 if (!state) { 2058 goto invalid_option; 2059 } 2060 result = parse_uint(&timeout, value, MAXTIMEOUT, 2061 "timeout"); 2062 if (result != ISC_R_SUCCESS) { 2063 warn("Couldn't parse timeout"); 2064 goto exit_or_usage; 2065 } 2066 if (timeout == 0) { 2067 timeout = 1; 2068 } 2069 break; 2070 case 'l': 2071 switch (cmd[2]) { 2072 case 's': 2073 if (!plus_tls_options(cmd, value, state, 2074 lookup)) 2075 { 2076 goto invalid_option; 2077 } 2078 break; 2079 default: 2080 goto invalid_option; 2081 } 2082 break; 2083 case 'o': 2084 FULLCHECK("topdown"); 2085 fprintf(stderr, ";; +topdown option is deprecated"); 2086 break; 2087 case 'r': 2088 switch (cmd[2]) { 2089 case 'a': /* trace */ 2090 FULLCHECK("trace"); 2091 lookup->trace = state; 2092 lookup->trace_root = state; 2093 if (state) { 2094 lookup->recurse = true; 2095 lookup->identify = true; 2096 lookup->comments = false; 2097 lookup->rrcomments = 0; 2098 lookup->stats = false; 2099 lookup->section_additional = false; 2100 lookup->section_authority = true; 2101 lookup->section_question = false; 2102 lookup->dnssec = true; 2103 lookup->sendcookie = true; 2104 usesearch = false; 2105 } 2106 break; 2107 case 'i': /* tries */ 2108 FULLCHECK("tries"); 2109 if (value == NULL) { 2110 goto need_value; 2111 } 2112 if (!state) { 2113 goto invalid_option; 2114 } 2115 result = parse_uint(&lookup->retries, value, 2116 MAXTRIES, "tries"); 2117 if (result != ISC_R_SUCCESS) { 2118 warn("Couldn't parse tries"); 2119 goto exit_or_usage; 2120 } 2121 if (lookup->retries == 0) { 2122 lookup->retries = 1; 2123 } 2124 break; 2125 case 'u': /* trusted-key */ 2126 FULLCHECK("trusted-key"); 2127 fprintf(stderr, ";; +trusted-key option is " 2128 "deprecated"); 2129 break; 2130 default: 2131 goto invalid_option; 2132 } 2133 break; 2134 case 't': 2135 switch (cmd[2]) { 2136 case 'l': 2137 switch (cmd[3]) { 2138 case 0: 2139 case 'i': /* ttlid */ 2140 FULLCHECK2("ttl", "ttlid"); 2141 lookup->nottl = !state; 2142 break; 2143 case 'u': /* ttlunits */ 2144 FULLCHECK("ttlunits"); 2145 lookup->nottl = false; 2146 lookup->ttlunits = state; 2147 break; 2148 default: 2149 goto invalid_option; 2150 } 2151 break; 2152 default: 2153 goto invalid_option; 2154 } 2155 break; 2156 default: 2157 goto invalid_option; 2158 } 2159 break; 2160 case 'u': 2161 switch (cmd[1]) { 2162 case 'n': 2163 switch (cmd[2]) { 2164 case 'e': 2165 FULLCHECK("unexpected"); 2166 fprintf(stderr, ";; +unexpected option " 2167 "is deprecated"); 2168 break; 2169 case 'k': 2170 FULLCHECK("unknownformat"); 2171 lookup->print_unknown_format = state; 2172 break; 2173 default: 2174 goto invalid_option; 2175 } 2176 break; 2177 default: 2178 goto invalid_option; 2179 } 2180 2181 break; 2182 case 'v': 2183 FULLCHECK("vc"); 2184 if (!is_batchfile) { 2185 lookup->tcp_mode = state; 2186 lookup->tcp_mode_set = true; 2187 } 2188 break; 2189 case 'y': /* yaml */ 2190 FULLCHECK("yaml"); 2191 yaml = state; 2192 if (state) { 2193 printcmd = false; 2194 lookup->stats = false; 2195 lookup->rrcomments = -1; 2196 } 2197 break; 2198 case 'z': /* zflag */ 2199 FULLCHECK("zflag"); 2200 lookup->zflag = state; 2201 break; 2202 default: 2203 invalid_option: 2204 need_value: 2205 #if TARGET_OS_IPHONE 2206 exit_or_usage: 2207 #endif /* if TARGET_OS_IPHONE */ 2208 fprintf(stderr, "Invalid option: +%s\n", option); 2209 usage(); 2210 } 2211 return (lookup); 2212 2213 #if !TARGET_OS_IPHONE 2214 exit_or_usage: 2215 cleanup_openssl_refs(); 2216 digexit(); 2217 #endif /* if !TARGET_OS_IPHONE */ 2218 } 2219 2220 /*% 2221 * #true returned if value was used 2222 */ 2223 static const char *single_dash_opts = "46dhimnruv"; 2224 static const char *dash_opts = "46bcdfhikmnpqrtvyx"; 2225 static bool 2226 dash_option(char *option, char *next, dig_lookup_t **lookup, 2227 bool *open_type_class, bool *need_clone, bool config_only, int argc, 2228 char **argv, bool *firstarg) { 2229 char opt, *value, *ptr, *ptr2, *ptr3, *last; 2230 isc_result_t result; 2231 bool value_from_next; 2232 isc_textregion_t tr; 2233 dns_rdatatype_t rdtype; 2234 dns_rdataclass_t rdclass; 2235 char textname[MXNAME]; 2236 struct in_addr in4; 2237 struct in6_addr in6; 2238 in_port_t srcport; 2239 char *hash, *cmd; 2240 uint32_t num; 2241 2242 while (strpbrk(option, single_dash_opts) == &option[0]) { 2243 /* 2244 * Since the -[46dhimnuv] options do not take an argument, 2245 * account for them (in any number and/or combination) 2246 * if they appear as the first character(s) of a q-opt. 2247 */ 2248 opt = option[0]; 2249 switch (opt) { 2250 case '4': 2251 if (have_ipv4) { 2252 isc_net_disableipv6(); 2253 have_ipv6 = false; 2254 } else { 2255 fatal("can't find IPv4 networking"); 2256 UNREACHABLE(); 2257 return (false); 2258 } 2259 break; 2260 case '6': 2261 if (have_ipv6) { 2262 isc_net_disableipv4(); 2263 have_ipv4 = false; 2264 } else { 2265 fatal("can't find IPv6 networking"); 2266 UNREACHABLE(); 2267 return (false); 2268 } 2269 break; 2270 case 'd': 2271 ptr = strpbrk(&option[1], dash_opts); 2272 if (ptr != &option[1]) { 2273 cmd = option; 2274 FULLCHECK("debug"); 2275 debugging = true; 2276 return (false); 2277 } else { 2278 debugging = true; 2279 } 2280 break; 2281 case 'h': 2282 help(); 2283 exit(EXIT_SUCCESS); 2284 break; 2285 case 'i': 2286 /* deprecated */ 2287 break; 2288 case 'm': /* memdebug */ 2289 /* memdebug is handled in preparse_args() */ 2290 break; 2291 case 'n': 2292 /* deprecated */ 2293 break; 2294 case 'r': 2295 debug("digrc (late)"); 2296 digrc = false; 2297 break; 2298 case 'u': 2299 (*lookup)->use_usec = true; 2300 break; 2301 case 'v': 2302 version(); 2303 exit(EXIT_SUCCESS); 2304 break; 2305 } 2306 if (strlen(option) > 1U) { 2307 option = &option[1]; 2308 } else { 2309 return (false); 2310 } 2311 } 2312 opt = option[0]; 2313 if (strlen(option) > 1U) { 2314 value_from_next = false; 2315 value = &option[1]; 2316 } else { 2317 value_from_next = true; 2318 value = next; 2319 } 2320 if (value == NULL) { 2321 goto invalid_option; 2322 } 2323 switch (opt) { 2324 case 'b': 2325 hash = strchr(value, '#'); 2326 if (hash != NULL) { 2327 result = parse_uint(&num, hash + 1, MAXPORT, 2328 "port number"); 2329 if (result != ISC_R_SUCCESS) { 2330 fatal("Couldn't parse port number"); 2331 } 2332 srcport = num; 2333 *hash = '\0'; 2334 } else { 2335 srcport = 0; 2336 } 2337 if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) { 2338 isc_sockaddr_fromin6(&localaddr, &in6, srcport); 2339 isc_net_disableipv4(); 2340 } else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) { 2341 isc_sockaddr_fromin(&localaddr, &in4, srcport); 2342 isc_net_disableipv6(); 2343 } else { 2344 if (hash != NULL) { 2345 *hash = '#'; 2346 } 2347 fatal("invalid address %s", value); 2348 } 2349 if (hash != NULL) { 2350 *hash = '#'; 2351 } 2352 specified_source = true; 2353 return (value_from_next); 2354 case 'c': 2355 if ((*lookup)->rdclassset) { 2356 fprintf(stderr, ";; Warning, extra class option\n"); 2357 } 2358 *open_type_class = false; 2359 tr.base = value; 2360 tr.length = (unsigned int)strlen(value); 2361 result = dns_rdataclass_fromtext(&rdclass, 2362 (isc_textregion_t *)&tr); 2363 if (result == ISC_R_SUCCESS) { 2364 (*lookup)->rdclass = rdclass; 2365 (*lookup)->rdclassset = true; 2366 } else { 2367 fprintf(stderr, 2368 ";; Warning, ignoring " 2369 "invalid class %s\n", 2370 value); 2371 } 2372 return (value_from_next); 2373 case 'f': 2374 atomic_store(&batchname, (uintptr_t)value); 2375 return (value_from_next); 2376 case 'k': 2377 strlcpy(keyfile, value, sizeof(keyfile)); 2378 return (value_from_next); 2379 case 'p': 2380 result = parse_uint(&num, value, MAXPORT, "port number"); 2381 if (result != ISC_R_SUCCESS) { 2382 fatal("Couldn't parse port number"); 2383 } 2384 port = num; 2385 port_set = true; 2386 return (value_from_next); 2387 case 'q': 2388 if (!config_only) { 2389 if (*need_clone) { 2390 (*lookup) = clone_lookup(default_lookup, true); 2391 } 2392 *need_clone = true; 2393 strlcpy((*lookup)->textname, value, 2394 sizeof((*lookup)->textname)); 2395 (*lookup)->trace_root = ((*lookup)->trace || 2396 (*lookup)->ns_search_only); 2397 (*lookup)->new_search = true; 2398 if (*firstarg) { 2399 printgreeting(argc, argv, *lookup); 2400 *firstarg = false; 2401 } 2402 ISC_LIST_APPEND(lookup_list, (*lookup), link); 2403 debug("looking up %s", (*lookup)->textname); 2404 } 2405 return (value_from_next); 2406 case 't': 2407 *open_type_class = false; 2408 if (strncasecmp(value, "ixfr=", 5) == 0) { 2409 rdtype = dns_rdatatype_ixfr; 2410 result = ISC_R_SUCCESS; 2411 } else { 2412 tr.base = value; 2413 tr.length = (unsigned int)strlen(value); 2414 result = dns_rdatatype_fromtext( 2415 &rdtype, (isc_textregion_t *)&tr); 2416 if (result == ISC_R_SUCCESS && 2417 rdtype == dns_rdatatype_ixfr) 2418 { 2419 result = DNS_R_UNKNOWN; 2420 } 2421 } 2422 if (result == ISC_R_SUCCESS) { 2423 if ((*lookup)->rdtypeset) { 2424 fprintf(stderr, ";; Warning, " 2425 "extra type option\n"); 2426 } 2427 if (rdtype == dns_rdatatype_ixfr) { 2428 uint32_t serial; 2429 (*lookup)->rdtype = dns_rdatatype_ixfr; 2430 (*lookup)->rdtypeset = true; 2431 result = parse_uint(&serial, &value[5], 2432 MAXSERIAL, "serial number"); 2433 if (result != ISC_R_SUCCESS) { 2434 fatal("Couldn't parse serial number"); 2435 } 2436 (*lookup)->ixfr_serial = serial; 2437 (*lookup)->section_question = plusquest; 2438 (*lookup)->comments = pluscomm; 2439 if (!(*lookup)->tcp_mode_set) { 2440 (*lookup)->tcp_mode = true; 2441 } 2442 } else { 2443 (*lookup)->rdtype = rdtype; 2444 if (!config_only) { 2445 (*lookup)->rdtypeset = true; 2446 } 2447 if (rdtype == dns_rdatatype_axfr) { 2448 (*lookup)->section_question = plusquest; 2449 (*lookup)->comments = pluscomm; 2450 } else if (rdtype == dns_rdatatype_any) { 2451 if (!(*lookup)->tcp_mode_set) { 2452 (*lookup)->tcp_mode = true; 2453 } 2454 } 2455 (*lookup)->ixfr_serial = false; 2456 } 2457 } else { 2458 fprintf(stderr, 2459 ";; Warning, ignoring " 2460 "invalid type %s\n", 2461 value); 2462 } 2463 return (value_from_next); 2464 case 'y': 2465 if ((ptr = strtok_r(value, ":", &last)) == NULL) { 2466 usage(); 2467 } 2468 if ((ptr2 = strtok_r(NULL, ":", &last)) == NULL) { /* name or 2469 * secret */ 2470 usage(); 2471 } 2472 if ((ptr3 = strtok_r(NULL, ":", &last)) != NULL) { /* secret or 2473 * NULL */ 2474 parse_hmac(ptr); 2475 ptr = ptr2; 2476 ptr2 = ptr3; 2477 } else { 2478 hmacname = DNS_TSIG_HMACMD5_NAME; 2479 digestbits = 0; 2480 } 2481 /* XXXONDREJ: FIXME */ 2482 strlcpy(keynametext, ptr, sizeof(keynametext)); 2483 strlcpy(keysecret, ptr2, sizeof(keysecret)); 2484 return (value_from_next); 2485 case 'x': 2486 if (*need_clone) { 2487 *lookup = clone_lookup(default_lookup, true); 2488 } 2489 *need_clone = true; 2490 if (get_reverse(textname, sizeof(textname), value, false) == 2491 ISC_R_SUCCESS) 2492 { 2493 strlcpy((*lookup)->textname, textname, 2494 sizeof((*lookup)->textname)); 2495 debug("looking up %s", (*lookup)->textname); 2496 (*lookup)->trace_root = ((*lookup)->trace || 2497 (*lookup)->ns_search_only); 2498 if (!(*lookup)->rdtypeset) { 2499 (*lookup)->rdtype = dns_rdatatype_ptr; 2500 } 2501 if (!(*lookup)->rdclassset) { 2502 (*lookup)->rdclass = dns_rdataclass_in; 2503 } 2504 (*lookup)->new_search = true; 2505 if (*firstarg) { 2506 printgreeting(argc, argv, *lookup); 2507 *firstarg = false; 2508 } 2509 ISC_LIST_APPEND(lookup_list, *lookup, link); 2510 } else { 2511 fprintf(stderr, "Invalid IP address %s\n", value); 2512 exit(EXIT_FAILURE); 2513 } 2514 return (value_from_next); 2515 invalid_option: 2516 default: 2517 fprintf(stderr, "Invalid option: -%s\n", option); 2518 usage(); 2519 } 2520 UNREACHABLE(); 2521 return (false); 2522 } 2523 2524 /*% 2525 * Because we may be trying to do memory allocation recording, we're going 2526 * to need to parse the arguments for the -m *before* we start the main 2527 * argument parsing routine. 2528 * 2529 * I'd prefer not to have to do this, but I am not quite sure how else to 2530 * fix the problem. Argument parsing in dig involves memory allocation 2531 * by its nature, so it can't be done in the main argument parser. 2532 */ 2533 static void 2534 preparse_args(int argc, char **argv) { 2535 int rc; 2536 char **rv; 2537 char *option; 2538 2539 rc = argc; 2540 rv = argv; 2541 for (rc--, rv++; rc > 0; rc--, rv++) { 2542 if (rv[0][0] != '-') { 2543 continue; 2544 } 2545 option = &rv[0][1]; 2546 while (strpbrk(option, single_dash_opts) == &option[0]) { 2547 switch (option[0]) { 2548 case 'd': 2549 /* For debugging early startup */ 2550 debugging = true; 2551 break; 2552 case 'm': 2553 memdebugging = true; 2554 isc_mem_debugging = ISC_MEM_DEBUGTRACE | 2555 ISC_MEM_DEBUGRECORD; 2556 break; 2557 case 'r': 2558 /* 2559 * Must be done early, because ~/.digrc 2560 * is read before command line parsing 2561 */ 2562 debug("digrc (early)"); 2563 digrc = false; 2564 break; 2565 case '4': 2566 if (ipv6only) { 2567 fatal("only one of -4 and -6 allowed"); 2568 } 2569 ipv4only = true; 2570 break; 2571 case '6': 2572 if (ipv4only) { 2573 fatal("only one of -4 and -6 allowed"); 2574 } 2575 ipv6only = true; 2576 break; 2577 } 2578 option = &option[1]; 2579 } 2580 if (strlen(option) == 0U) { 2581 continue; 2582 } 2583 /* Look for dash value option. */ 2584 if (strpbrk(option, dash_opts) != &option[0]) { 2585 goto invalid_option; 2586 } 2587 if (strlen(option) > 1U) { 2588 /* value in option. */ 2589 continue; 2590 } 2591 /* Dash value is next argument so we need to skip it. */ 2592 rc--, rv++; 2593 /* Handle missing argument */ 2594 if (rc == 0) { 2595 invalid_option: 2596 fprintf(stderr, "Invalid option: -%s\n", option); 2597 usage(); 2598 } 2599 } 2600 } 2601 2602 static int 2603 split_batchline(char *batchline, char **bargv, int len, const char *msg) { 2604 int bargc; 2605 char *last = NULL; 2606 2607 REQUIRE(batchline != NULL); 2608 2609 for (bargc = 1, bargv[bargc] = strtok_r(batchline, " \t\r\n", &last); 2610 bargc < len && bargv[bargc]; 2611 bargv[++bargc] = strtok_r(NULL, " \t\r\n", &last)) 2612 { 2613 debug("%s %d: %s", msg, bargc, bargv[bargc]); 2614 } 2615 return (bargc); 2616 } 2617 2618 static void 2619 parse_args(bool is_batchfile, bool config_only, int argc, char **argv) { 2620 isc_result_t result; 2621 isc_textregion_t tr; 2622 bool firstarg = true; 2623 dig_lookup_t *lookup = NULL; 2624 dns_rdatatype_t rdtype; 2625 dns_rdataclass_t rdclass; 2626 bool open_type_class = true; 2627 char batchline[MXNAME]; 2628 int bargc; 2629 char *bargv[64]; 2630 int rc; 2631 char **rv; 2632 #ifndef NOPOSIX 2633 char *homedir; 2634 char rcfile[PATH_MAX]; 2635 #endif /* ifndef NOPOSIX */ 2636 bool need_clone = true; 2637 2638 /* 2639 * The semantics for parsing the args is a bit complex; if 2640 * we don't have a host yet, make the arg apply globally, 2641 * otherwise make it apply to the latest host. This is 2642 * a bit different than the previous versions, but should 2643 * form a consistent user interface. 2644 * 2645 * First, create a "default lookup" which won't actually be used 2646 * anywhere, except for cloning into new lookups 2647 */ 2648 2649 debug("parse_args()"); 2650 if (!is_batchfile) { 2651 debug("making new lookup"); 2652 default_lookup = make_empty_lookup(); 2653 default_lookup->adflag = true; 2654 default_lookup->edns = DEFAULT_EDNS_VERSION; 2655 default_lookup->sendcookie = true; 2656 2657 #ifndef NOPOSIX 2658 /* 2659 * Treat ${HOME}/.digrc as a special batchfile 2660 */ 2661 INSIST(batchfp == NULL); 2662 homedir = getenv("HOME"); 2663 if (homedir != NULL && digrc) { 2664 unsigned int n; 2665 debug("digrc (open)"); 2666 n = snprintf(rcfile, sizeof(rcfile), "%s/.digrc", 2667 homedir); 2668 if (n < sizeof(rcfile)) { 2669 batchfp = fopen(rcfile, "r"); 2670 } 2671 } 2672 if (batchfp != NULL) { 2673 while (fgets(batchline, sizeof(batchline), batchfp) != 2674 0) 2675 { 2676 debug("config line %s", batchline); 2677 bargc = split_batchline(batchline, bargv, 62, 2678 ".digrc argv"); 2679 bargv[0] = argv[0]; 2680 argv0 = argv[0]; 2681 parse_args(true, true, bargc, (char **)bargv); 2682 } 2683 fclose(batchfp); 2684 } 2685 #endif /* ifndef NOPOSIX */ 2686 } 2687 2688 if (is_batchfile && !config_only) { 2689 /* Processing '-f batchfile'. */ 2690 lookup = clone_lookup(default_lookup, true); 2691 need_clone = false; 2692 } else { 2693 lookup = default_lookup; 2694 } 2695 2696 rc = argc; 2697 rv = argv; 2698 for (rc--, rv++; rc > 0; rc--, rv++) { 2699 debug("main parsing %s", rv[0]); 2700 if (strncmp(rv[0], "%", 1) == 0) { 2701 break; 2702 } 2703 if (rv[0][0] == '@') { 2704 if (is_batchfile && !config_only) { 2705 addresscount = getaddresses(lookup, &rv[0][1], 2706 &result); 2707 if (addresscount == 0) { 2708 fprintf(stderr, 2709 "couldn't get address " 2710 "for '%s': %s: skipping " 2711 "lookup\n", 2712 &rv[0][1], 2713 isc_result_totext(result)); 2714 if (ISC_LINK_LINKED(lookup, link)) { 2715 ISC_LIST_DEQUEUE(lookup_list, 2716 lookup, link); 2717 } 2718 destroy_lookup(lookup); 2719 return; 2720 } 2721 } else { 2722 addresscount = getaddresses(lookup, &rv[0][1], 2723 NULL); 2724 if (addresscount == 0) { 2725 fatal("no valid addresses for '%s'\n", 2726 &rv[0][1]); 2727 } 2728 } 2729 } else if (rv[0][0] == '+') { 2730 lookup = plus_option(&rv[0][1], is_batchfile, 2731 &need_clone, lookup); 2732 } else if (rv[0][0] == '-') { 2733 if (rc <= 1) { 2734 if (dash_option(&rv[0][1], NULL, &lookup, 2735 &open_type_class, &need_clone, 2736 config_only, argc, argv, 2737 &firstarg)) 2738 { 2739 rc--; 2740 rv++; 2741 } 2742 } else { 2743 if (dash_option(&rv[0][1], rv[1], &lookup, 2744 &open_type_class, &need_clone, 2745 config_only, argc, argv, 2746 &firstarg)) 2747 { 2748 rc--; 2749 rv++; 2750 } 2751 } 2752 } else { 2753 /* 2754 * Anything which isn't an option 2755 */ 2756 if (open_type_class) { 2757 if (strncasecmp(rv[0], "ixfr=", 5) == 0) { 2758 rdtype = dns_rdatatype_ixfr; 2759 result = ISC_R_SUCCESS; 2760 } else { 2761 tr.base = rv[0]; 2762 tr.length = (unsigned int)strlen(rv[0]); 2763 result = dns_rdatatype_fromtext( 2764 &rdtype, 2765 (isc_textregion_t *)&tr); 2766 if (result == ISC_R_SUCCESS && 2767 rdtype == dns_rdatatype_ixfr) 2768 { 2769 fprintf(stderr, ";; Warning, " 2770 "ixfr requires " 2771 "a " 2772 "serial " 2773 "number\n"); 2774 continue; 2775 } 2776 } 2777 if (result == ISC_R_SUCCESS) { 2778 if (lookup->rdtypeset) { 2779 fprintf(stderr, ";; Warning, " 2780 "extra type " 2781 "option\n"); 2782 } 2783 if (rdtype == dns_rdatatype_ixfr) { 2784 uint32_t serial; 2785 lookup->rdtype = 2786 dns_rdatatype_ixfr; 2787 lookup->rdtypeset = true; 2788 result = parse_uint(&serial, 2789 &rv[0][5], 2790 MAXSERIAL, 2791 "serial " 2792 "number"); 2793 if (result != ISC_R_SUCCESS) { 2794 fatal("Couldn't parse " 2795 "serial number"); 2796 } 2797 lookup->ixfr_serial = serial; 2798 lookup->section_question = 2799 plusquest; 2800 lookup->comments = pluscomm; 2801 if (!lookup->tcp_mode_set) { 2802 lookup->tcp_mode = true; 2803 } 2804 } else { 2805 lookup->rdtype = rdtype; 2806 lookup->rdtypeset = true; 2807 if (rdtype == 2808 dns_rdatatype_axfr) 2809 { 2810 lookup->section_question = 2811 plusquest; 2812 lookup->comments = 2813 pluscomm; 2814 } 2815 if (rdtype == 2816 dns_rdatatype_any && 2817 !lookup->tcp_mode_set) 2818 { 2819 lookup->tcp_mode = true; 2820 } 2821 lookup->ixfr_serial = false; 2822 } 2823 continue; 2824 } 2825 result = dns_rdataclass_fromtext( 2826 &rdclass, (isc_textregion_t *)&tr); 2827 if (result == ISC_R_SUCCESS) { 2828 if (lookup->rdclassset) { 2829 fprintf(stderr, ";; Warning, " 2830 "extra class " 2831 "option\n"); 2832 } 2833 lookup->rdclass = rdclass; 2834 lookup->rdclassset = true; 2835 continue; 2836 } 2837 } 2838 2839 if (!config_only) { 2840 if (need_clone) { 2841 lookup = clone_lookup(default_lookup, 2842 true); 2843 } 2844 need_clone = true; 2845 strlcpy(lookup->textname, rv[0], 2846 sizeof(lookup->textname)); 2847 lookup->trace_root = (lookup->trace || 2848 lookup->ns_search_only); 2849 lookup->new_search = true; 2850 if (firstarg) { 2851 printgreeting(argc, argv, lookup); 2852 firstarg = false; 2853 } 2854 ISC_LIST_APPEND(lookup_list, lookup, link); 2855 debug("looking up %s", lookup->textname); 2856 } 2857 /* XXX Error message */ 2858 } 2859 } 2860 2861 /* 2862 * If we have a batchfile, seed the lookup list with the 2863 * first entry, then trust the callback in dighost_shutdown 2864 * to get the rest 2865 */ 2866 char *filename = (char *)atomic_load(&batchname); 2867 if ((filename != NULL) && !(is_batchfile)) { 2868 if (strcmp(filename, "-") == 0) { 2869 batchfp = stdin; 2870 } else { 2871 batchfp = fopen(filename, "r"); 2872 } 2873 if (batchfp == NULL) { 2874 perror(filename); 2875 if (exitcode < 8) { 2876 exitcode = 8; 2877 } 2878 fatal("couldn't open specified batch file"); 2879 } 2880 /* XXX Remove code dup from shutdown code */ 2881 next_line: 2882 if (fgets(batchline, sizeof(batchline), batchfp) != 0) { 2883 debug("batch line %s", batchline); 2884 if (batchline[0] == '\r' || batchline[0] == '\n' || 2885 batchline[0] == '#' || batchline[0] == ';') 2886 { 2887 goto next_line; 2888 } 2889 bargc = split_batchline(batchline, bargv, 14, 2890 "batch argv"); 2891 bargv[0] = argv[0]; 2892 argv0 = argv[0]; 2893 parse_args(true, false, bargc, (char **)bargv); 2894 return; 2895 } 2896 return; 2897 } 2898 /* 2899 * If no lookup specified, search for root 2900 */ 2901 if ((lookup_list.head == NULL) && !config_only) { 2902 if (need_clone) { 2903 lookup = clone_lookup(default_lookup, true); 2904 } 2905 need_clone = true; 2906 lookup->trace_root = (lookup->trace || lookup->ns_search_only); 2907 lookup->new_search = true; 2908 strlcpy(lookup->textname, ".", sizeof(lookup->textname)); 2909 lookup->rdtype = dns_rdatatype_ns; 2910 lookup->rdtypeset = true; 2911 if (firstarg) { 2912 printgreeting(argc, argv, lookup); 2913 firstarg = false; 2914 } 2915 ISC_LIST_APPEND(lookup_list, lookup, link); 2916 } 2917 if (!need_clone) { 2918 destroy_lookup(lookup); 2919 } 2920 } 2921 2922 /* 2923 * Callback from dighost.c to allow program-specific shutdown code. 2924 * Here, we're possibly reading from a batch file, then shutting down 2925 * for real if there's nothing in the batch file to read. 2926 */ 2927 static void 2928 query_finished(void) { 2929 char batchline[MXNAME]; 2930 int bargc; 2931 char *bargv[16]; 2932 2933 if (atomic_load(&batchname) == 0) { 2934 isc_app_shutdown(); 2935 return; 2936 } 2937 2938 fflush(stdout); 2939 if (feof(batchfp)) { 2940 atomic_store(&batchname, 0); 2941 isc_app_shutdown(); 2942 if (batchfp != stdin) { 2943 fclose(batchfp); 2944 } 2945 return; 2946 } 2947 2948 if (fgets(batchline, sizeof(batchline), batchfp) != 0) { 2949 debug("batch line %s", batchline); 2950 bargc = split_batchline(batchline, bargv, 14, "batch argv"); 2951 bargv[0] = argv0; 2952 parse_args(true, false, bargc, (char **)bargv); 2953 start_lookup(); 2954 } else { 2955 atomic_store(&batchname, 0); 2956 if (batchfp != stdin) { 2957 fclose(batchfp); 2958 } 2959 isc_app_shutdown(); 2960 return; 2961 } 2962 } 2963 2964 static void 2965 dig_error(const char *format, ...) { 2966 va_list args; 2967 2968 if (yaml) { 2969 printf("- type: DIG_ERROR\n"); 2970 2971 /* 2972 * Print an indent before a literal block quote. 2973 * Note: this will break if used to print more than 2974 * one line of text as only the first line would be 2975 * indented. 2976 */ 2977 printf(" message: |\n"); 2978 printf(" "); 2979 } else { 2980 printf(";; "); 2981 } 2982 2983 va_start(args, format); 2984 vprintf(format, args); 2985 va_end(args); 2986 printf("\n"); /* We get the error without a newline */ 2987 } 2988 2989 static void 2990 dig_warning(const char *format, ...) { 2991 va_list args; 2992 2993 if (!yaml) { 2994 printf(";; "); 2995 2996 va_start(args, format); 2997 vprintf(format, args); 2998 va_end(args); 2999 3000 printf("\n"); 3001 } 3002 } 3003 3004 static void 3005 dig_comments(dig_lookup_t *lookup, const char *format, ...) { 3006 va_list args; 3007 3008 if (lookup->comments && !yaml) { 3009 printf(";; "); 3010 3011 va_start(args, format); 3012 vprintf(format, args); 3013 va_end(args); 3014 3015 printf("\n"); 3016 } 3017 } 3018 3019 void 3020 dig_setup(int argc, char **argv) { 3021 isc_result_t result; 3022 3023 ISC_LIST_INIT(lookup_list); 3024 ISC_LIST_INIT(server_list); 3025 ISC_LIST_INIT(search_list); 3026 3027 debug("dig_setup()"); 3028 3029 /* setup dighost callbacks */ 3030 dighost_printmessage = printmessage; 3031 dighost_received = received; 3032 dighost_trying = trying; 3033 dighost_shutdown = query_finished; 3034 dighost_error = dig_error; 3035 dighost_warning = dig_warning; 3036 dighost_comments = dig_comments; 3037 3038 progname = argv[0]; 3039 preparse_args(argc, argv); 3040 3041 result = isc_app_start(); 3042 check_result(result, "isc_app_start"); 3043 3044 setup_libs(); 3045 setup_system(ipv4only, ipv6only); 3046 } 3047 3048 void 3049 dig_query_setup(bool is_batchfile, bool config_only, int argc, char **argv) { 3050 debug("dig_query_setup"); 3051 3052 parse_args(is_batchfile, config_only, argc, argv); 3053 if (keyfile[0] != 0) { 3054 setup_file_key(); 3055 } else if (keysecret[0] != 0) { 3056 setup_text_key(); 3057 } 3058 if (domainopt[0] != '\0') { 3059 set_search_domain(domainopt); 3060 usesearch = true; 3061 } 3062 } 3063 3064 void 3065 dig_startup(void) { 3066 isc_result_t result; 3067 3068 debug("dig_startup()"); 3069 3070 result = isc_app_onrun(mctx, global_task, onrun_callback, NULL); 3071 check_result(result, "isc_app_onrun"); 3072 isc_app_run(); 3073 } 3074 3075 void 3076 dig_query_start(void) { 3077 start_lookup(); 3078 } 3079 3080 void 3081 dig_shutdown(void) { 3082 destroy_lookup(default_lookup); 3083 if (atomic_load(&batchname) != 0) { 3084 if (batchfp != NULL && batchfp != stdin) { 3085 fclose(batchfp); 3086 } 3087 atomic_store(&batchname, 0); 3088 } 3089 cancel_all(); 3090 destroy_libs(); 3091 isc_app_finish(); 3092 } 3093 3094 /*% Main processing routine for dig */ 3095 int 3096 main(int argc, char **argv) { 3097 dig_setup(argc, argv); 3098 dig_query_setup(false, false, argc, argv); 3099 dig_startup(); 3100 dig_shutdown(); 3101 3102 return (exitcode); 3103 } 3104