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