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