1 /* $NetBSD: mdig.c,v 1.11 2023/01/25 21:43:28 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 #include <inttypes.h> 17 #include <stdbool.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <unistd.h> 21 22 #include <isc/app.h> 23 #include <isc/base64.h> 24 #include <isc/hash.h> 25 #include <isc/hex.h> 26 #include <isc/log.h> 27 #include <isc/managers.h> 28 #include <isc/mem.h> 29 #include <isc/net.h> 30 #include <isc/nonce.h> 31 #include <isc/parseint.h> 32 #include <isc/portset.h> 33 #include <isc/print.h> 34 #include <isc/random.h> 35 #include <isc/sockaddr.h> 36 #include <isc/socket.h> 37 #include <isc/string.h> 38 #include <isc/task.h> 39 #include <isc/timer.h> 40 #include <isc/util.h> 41 42 #include <dns/byaddr.h> 43 #include <dns/dispatch.h> 44 #include <dns/events.h> 45 #include <dns/fixedname.h> 46 #include <dns/message.h> 47 #include <dns/name.h> 48 #include <dns/rdata.h> 49 #include <dns/rdataclass.h> 50 #include <dns/rdataset.h> 51 #include <dns/rdatatype.h> 52 #include <dns/request.h> 53 #include <dns/resolver.h> 54 #include <dns/result.h> 55 #include <dns/types.h> 56 #include <dns/view.h> 57 58 #include <dst/result.h> 59 60 #include <bind9/getaddresses.h> 61 62 #define CHECK(str, x) \ 63 { \ 64 if ((x) != ISC_R_SUCCESS) { \ 65 fprintf(stderr, "mdig: %s failed with %s\n", (str), \ 66 isc_result_totext(x)); \ 67 exit(-1); \ 68 } \ 69 } 70 71 #define RUNCHECK(x) RUNTIME_CHECK((x) == ISC_R_SUCCESS) 72 73 #define ADD_STRING(b, s) \ 74 { \ 75 if (strlen(s) >= isc_buffer_availablelength(b)) \ 76 return ((ISC_R_NOSPACE)); \ 77 else \ 78 isc_buffer_putstr(b, s); \ 79 } 80 81 #define MXNAME (DNS_NAME_MAXTEXT + 1) 82 #define COMMSIZE 0xffff 83 #define OUTPUTBUF 32767 84 #define MAXPORT 0xffff 85 #define PORT 53 86 #define MAXTIMEOUT 0xffff 87 #define TCPTIMEOUT 10 88 #define UDPTIMEOUT 5 89 #define MAXTRIES 0xffffffff 90 91 #define NS_PER_US 1000 /*%< Nanoseconds per microsecond. */ 92 #define US_PER_SEC 1000000 /*%< Microseconds per second. */ 93 #define US_PER_MS 1000 /*%< Microseconds per millisecond. */ 94 95 static isc_mem_t *mctx = NULL; 96 static dns_requestmgr_t *requestmgr = NULL; 97 static const char *batchname = NULL; 98 static FILE *batchfp = NULL; 99 static bool burst = false; 100 static bool have_ipv4 = false; 101 static bool have_ipv6 = false; 102 static bool have_src = false; 103 static bool tcp_mode = false; 104 static bool besteffort = true; 105 static bool display_short_form = false; 106 static bool display_headers = true; 107 static bool display_comments = true; 108 static int display_rrcomments = 0; 109 static bool display_ttlunits = true; 110 static bool display_ttl = true; 111 static bool display_class = true; 112 static bool display_crypto = true; 113 static bool display_multiline = false; 114 static bool display_question = true; 115 static bool display_answer = true; 116 static bool display_authority = true; 117 static bool display_additional = true; 118 static bool display_unknown_format = false; 119 static bool yaml = false; 120 static bool continue_on_error = false; 121 static uint32_t display_splitwidth = 0xffffffff; 122 static isc_sockaddr_t srcaddr; 123 static char *server = NULL; 124 static isc_sockaddr_t dstaddr; 125 static in_port_t port = 53; 126 static isc_dscp_t dscp = -1; 127 static unsigned char cookie_secret[33]; 128 static int onfly = 0; 129 static char hexcookie[81]; 130 131 struct query { 132 char textname[MXNAME]; /*% Name we're going to be 133 * looking up */ 134 bool recurse; 135 bool have_aaonly; 136 bool have_adflag; 137 bool have_cdflag; 138 bool have_zflag; 139 bool dnssec; 140 bool expire; 141 bool send_cookie; 142 char *cookie; 143 bool nsid; 144 dns_rdatatype_t rdtype; 145 dns_rdataclass_t rdclass; 146 uint16_t udpsize; 147 int16_t edns; 148 dns_ednsopt_t *ednsopts; 149 unsigned int ednsoptscnt; 150 unsigned int ednsflags; 151 isc_sockaddr_t *ecs_addr; 152 unsigned int timeout; 153 unsigned int udptimeout; 154 unsigned int udpretries; 155 ISC_LINK(struct query) link; 156 }; 157 static struct query default_query; 158 static ISC_LIST(struct query) queries; 159 160 #define EDNSOPTS 100U 161 /*% opcode text */ 162 static const char *const opcodetext[] = { 163 "QUERY", "IQUERY", "STATUS", "RESERVED3", 164 "NOTIFY", "UPDATE", "RESERVED6", "RESERVED7", 165 "RESERVED8", "RESERVED9", "RESERVED10", "RESERVED11", 166 "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15" 167 }; 168 169 /*% return code text */ 170 static const char *const rcodetext[] = { 171 "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", 172 "REFUSED", "YXDOMAIN", "YXRRSET", "NXRRSET", "NOTAUTH", 173 "NOTZONE", "RESERVED11", "RESERVED12", "RESERVED13", "RESERVED14", 174 "RESERVED15", "BADVERS" 175 }; 176 177 /*% safe rcodetext[] */ 178 static char * 179 rcode_totext(dns_rcode_t rcode) { 180 static char buf[sizeof("?65535")]; 181 union { 182 const char *consttext; 183 char *deconsttext; 184 } totext; 185 186 if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) { 187 snprintf(buf, sizeof(buf), "?%u", rcode); 188 totext.deconsttext = buf; 189 } else { 190 totext.consttext = rcodetext[rcode]; 191 } 192 return (totext.deconsttext); 193 } 194 195 /* receive response event handler */ 196 static void 197 recvresponse(isc_task_t *task, isc_event_t *event) { 198 dns_requestevent_t *reqev = (dns_requestevent_t *)event; 199 isc_result_t result; 200 dns_message_t *query = NULL, *response = NULL; 201 unsigned int parseflags = 0; 202 isc_buffer_t *msgbuf = NULL, *buf = NULL; 203 unsigned int len = OUTPUTBUF; 204 dns_master_style_t *style = NULL; 205 unsigned int styleflags = 0; 206 dns_messagetextflag_t flags; 207 208 UNUSED(task); 209 210 REQUIRE(reqev != NULL); 211 query = reqev->ev_arg; 212 213 if (reqev->result != ISC_R_SUCCESS) { 214 fprintf(stderr, "response failed with %s\n", 215 isc_result_totext(reqev->result)); 216 if (continue_on_error) { 217 goto cleanup; 218 } else { 219 exit(-1); 220 } 221 } 222 223 dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &response); 224 225 parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER; 226 if (besteffort) { 227 parseflags |= DNS_MESSAGEPARSE_BESTEFFORT; 228 parseflags |= DNS_MESSAGEPARSE_IGNORETRUNCATION; 229 } 230 231 msgbuf = dns_request_getanswer(reqev->request); 232 result = dns_request_getresponse(reqev->request, response, parseflags); 233 CHECK("dns_request_getresponse", result); 234 235 styleflags |= DNS_STYLEFLAG_REL_OWNER; 236 if (yaml) { 237 styleflags |= DNS_STYLEFLAG_YAML; 238 response->indent.string = " "; 239 response->indent.count = 3; 240 } else { 241 if (display_comments) { 242 styleflags |= DNS_STYLEFLAG_COMMENT; 243 } 244 if (display_unknown_format) { 245 styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; 246 } 247 if (display_rrcomments > 0) { 248 styleflags |= DNS_STYLEFLAG_RRCOMMENT; 249 } 250 if (display_ttlunits) { 251 styleflags |= DNS_STYLEFLAG_TTL_UNITS; 252 } 253 if (!display_ttl) { 254 styleflags |= DNS_STYLEFLAG_NO_TTL; 255 } 256 if (!display_class) { 257 styleflags |= DNS_STYLEFLAG_NO_CLASS; 258 } 259 if (!display_crypto) { 260 styleflags |= DNS_STYLEFLAG_NOCRYPTO; 261 } 262 if (display_multiline) { 263 styleflags |= DNS_STYLEFLAG_OMIT_OWNER; 264 styleflags |= DNS_STYLEFLAG_OMIT_CLASS; 265 styleflags |= DNS_STYLEFLAG_REL_DATA; 266 styleflags |= DNS_STYLEFLAG_OMIT_TTL; 267 styleflags |= DNS_STYLEFLAG_TTL; 268 styleflags |= DNS_STYLEFLAG_MULTILINE; 269 styleflags |= DNS_STYLEFLAG_COMMENT; 270 /* Turn on rrcomments unless explicitly disabled */ 271 if (display_rrcomments >= 0) { 272 styleflags |= DNS_STYLEFLAG_RRCOMMENT; 273 } 274 } 275 } 276 if (display_multiline || (!display_ttl && !display_class)) { 277 result = dns_master_stylecreate(&style, styleflags, 24, 24, 24, 278 32, 80, 8, display_splitwidth, 279 mctx); 280 } else if (!display_ttl || !display_class) { 281 result = dns_master_stylecreate(&style, styleflags, 24, 24, 32, 282 40, 80, 8, display_splitwidth, 283 mctx); 284 } else { 285 result = dns_master_stylecreate(&style, styleflags, 24, 32, 40, 286 48, 80, 8, display_splitwidth, 287 mctx); 288 } 289 CHECK("dns_master_stylecreate2", result); 290 291 flags = 0; 292 if (!display_headers) { 293 flags |= DNS_MESSAGETEXTFLAG_NOHEADERS; 294 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; 295 } 296 if (!display_comments) { 297 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; 298 } 299 300 isc_buffer_allocate(mctx, &buf, len); 301 302 if (yaml) { 303 char sockstr[ISC_SOCKADDR_FORMATSIZE]; 304 uint16_t sport; 305 char *hash; 306 int pf; 307 308 printf("-\n"); 309 printf(" type: MESSAGE\n"); 310 printf(" message:\n"); 311 312 if (((response->flags & DNS_MESSAGEFLAG_RD) != 0) && 313 ((response->flags & DNS_MESSAGEFLAG_RA) != 0)) 314 { 315 printf(" type: RECURSIVE_RESPONSE\n"); 316 } else { 317 printf(" type: AUTH_RESPONSE\n"); 318 } 319 320 printf(" message_size: %ub\n", 321 isc_buffer_usedlength(msgbuf)); 322 323 pf = isc_sockaddr_pf(&dstaddr); 324 if (pf == PF_INET || pf == PF_INET6) { 325 printf(" socket_family: %s\n", 326 pf == PF_INET ? "INET" : "INET6"); 327 328 printf(" socket_protocol: %s\n", 329 tcp_mode ? "TCP" : "UDP"); 330 331 sport = isc_sockaddr_getport(&dstaddr); 332 isc_sockaddr_format(&dstaddr, sockstr, sizeof(sockstr)); 333 hash = strchr(sockstr, '#'); 334 if (hash != NULL) { 335 *hash = '\0'; 336 } 337 printf(" response_address: \"%s\"\n", sockstr); 338 printf(" response_port: %u\n", sport); 339 } 340 341 if (have_src) { 342 sport = isc_sockaddr_getport(&srcaddr); 343 isc_sockaddr_format(&srcaddr, sockstr, sizeof(sockstr)); 344 hash = strchr(sockstr, '#'); 345 if (hash != NULL) { 346 *hash = '\0'; 347 } 348 printf(" query_address: \"%s\"\n", sockstr); 349 printf(" query_port: %u\n", sport); 350 } 351 352 printf(" %s:\n", "response_message_data"); 353 result = dns_message_headertotext(response, style, flags, buf); 354 CHECK("dns_message_headertotext", result); 355 } else if (display_comments && !display_short_form) { 356 printf(";; Got answer:\n"); 357 358 if (display_headers) { 359 printf(";; ->>HEADER<<- opcode: %s, status: %s, " 360 "id: %u\n", 361 opcodetext[response->opcode], 362 rcode_totext(response->rcode), response->id); 363 printf(";; flags:"); 364 if ((response->flags & DNS_MESSAGEFLAG_QR) != 0) { 365 printf(" qr"); 366 } 367 if ((response->flags & DNS_MESSAGEFLAG_AA) != 0) { 368 printf(" aa"); 369 } 370 if ((response->flags & DNS_MESSAGEFLAG_TC) != 0) { 371 printf(" tc"); 372 } 373 if ((response->flags & DNS_MESSAGEFLAG_RD) != 0) { 374 printf(" rd"); 375 } 376 if ((response->flags & DNS_MESSAGEFLAG_RA) != 0) { 377 printf(" ra"); 378 } 379 if ((response->flags & DNS_MESSAGEFLAG_AD) != 0) { 380 printf(" ad"); 381 } 382 if ((response->flags & DNS_MESSAGEFLAG_CD) != 0) { 383 printf(" cd"); 384 } 385 if ((response->flags & 0x0040U) != 0) { 386 printf("; MBZ: 0x4"); 387 } 388 389 printf("; QUERY: %u, ANSWER: %u, " 390 "AUTHORITY: %u, ADDITIONAL: %u\n", 391 response->counts[DNS_SECTION_QUESTION], 392 response->counts[DNS_SECTION_ANSWER], 393 response->counts[DNS_SECTION_AUTHORITY], 394 response->counts[DNS_SECTION_ADDITIONAL]); 395 396 if ((response->flags & DNS_MESSAGEFLAG_RD) != 0 && 397 (response->flags & DNS_MESSAGEFLAG_RA) == 0) 398 { 399 printf(";; WARNING: recursion requested " 400 "but not available\n"); 401 } 402 } 403 } 404 405 repopulate_buffer: 406 407 if (display_comments && display_headers && !display_short_form) { 408 result = dns_message_pseudosectiontotext( 409 response, DNS_PSEUDOSECTION_OPT, style, flags, buf); 410 if (result == ISC_R_NOSPACE) { 411 buftoosmall: 412 len += OUTPUTBUF; 413 isc_buffer_free(&buf); 414 isc_buffer_allocate(mctx, &buf, len); 415 goto repopulate_buffer; 416 } 417 CHECK("dns_message_pseudosectiontotext", result); 418 } 419 420 if (display_question && display_headers && !display_short_form) { 421 result = dns_message_sectiontotext( 422 response, DNS_SECTION_QUESTION, style, flags, buf); 423 if (result == ISC_R_NOSPACE) { 424 goto buftoosmall; 425 } 426 CHECK("dns_message_sectiontotext", result); 427 } 428 429 if (display_answer && !display_short_form) { 430 result = dns_message_sectiontotext(response, DNS_SECTION_ANSWER, 431 style, flags, buf); 432 if (result == ISC_R_NOSPACE) { 433 goto buftoosmall; 434 } 435 CHECK("dns_message_sectiontotext", result); 436 } else if (display_answer) { 437 dns_name_t *name; 438 dns_rdataset_t *rdataset; 439 isc_result_t loopresult; 440 dns_name_t empty_name; 441 dns_rdata_t rdata = DNS_RDATA_INIT; 442 unsigned int answerstyleflags = 0; 443 444 if (!display_crypto) { 445 answerstyleflags |= DNS_STYLEFLAG_NOCRYPTO; 446 } 447 if (display_unknown_format) { 448 answerstyleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; 449 } 450 451 dns_name_init(&empty_name, NULL); 452 result = dns_message_firstname(response, DNS_SECTION_ANSWER); 453 if (result != ISC_R_NOMORE) { 454 CHECK("dns_message_firstname", result); 455 } 456 457 for (;;) { 458 if (result == ISC_R_NOMORE) { 459 break; 460 } 461 CHECK("dns_message_nextname", result); 462 name = NULL; 463 dns_message_currentname(response, DNS_SECTION_ANSWER, 464 &name); 465 466 for (rdataset = ISC_LIST_HEAD(name->list); 467 rdataset != NULL; 468 rdataset = ISC_LIST_NEXT(rdataset, link)) 469 { 470 loopresult = dns_rdataset_first(rdataset); 471 while (loopresult == ISC_R_SUCCESS) { 472 dns_rdataset_current(rdataset, &rdata); 473 result = dns_rdata_tofmttext( 474 &rdata, NULL, answerstyleflags, 475 0, 60, " ", buf); 476 if (result == ISC_R_NOSPACE) { 477 goto buftoosmall; 478 } 479 CHECK("dns_rdata_tofmttext", result); 480 loopresult = 481 dns_rdataset_next(rdataset); 482 dns_rdata_reset(&rdata); 483 if (strlen("\n") >= 484 isc_buffer_availablelength(buf)) 485 { 486 goto buftoosmall; 487 } 488 isc_buffer_putstr(buf, "\n"); 489 } 490 } 491 result = dns_message_nextname(response, 492 DNS_SECTION_ANSWER); 493 } 494 } 495 496 if (display_authority && !display_short_form) { 497 result = dns_message_sectiontotext( 498 response, DNS_SECTION_AUTHORITY, style, flags, buf); 499 if (result == ISC_R_NOSPACE) { 500 goto buftoosmall; 501 } 502 CHECK("dns_message_sectiontotext", result); 503 } 504 505 if (display_additional && !display_short_form) { 506 result = dns_message_sectiontotext( 507 response, DNS_SECTION_ADDITIONAL, style, flags, buf); 508 if (result == ISC_R_NOSPACE) { 509 goto buftoosmall; 510 } 511 CHECK("dns_message_sectiontotext", result); 512 } 513 514 if (display_additional && !display_short_form && display_headers) { 515 /* 516 * Only print the signature on the first record. 517 */ 518 result = dns_message_pseudosectiontotext( 519 response, DNS_PSEUDOSECTION_TSIG, style, flags, buf); 520 if (result == ISC_R_NOSPACE) { 521 goto buftoosmall; 522 } 523 CHECK("dns_message_pseudosectiontotext", result); 524 result = dns_message_pseudosectiontotext( 525 response, DNS_PSEUDOSECTION_SIG0, style, flags, buf); 526 if (result == ISC_R_NOSPACE) { 527 goto buftoosmall; 528 } 529 CHECK("dns_message_pseudosectiontotext", result); 530 } 531 532 if (display_headers && display_comments && !display_short_form && !yaml) 533 { 534 printf("\n"); 535 } 536 537 printf("%.*s", (int)isc_buffer_usedlength(buf), 538 (char *)isc_buffer_base(buf)); 539 isc_buffer_free(&buf); 540 541 cleanup: 542 fflush(stdout); 543 if (style != NULL) { 544 dns_master_styledestroy(&style, mctx); 545 } 546 if (query != NULL) { 547 dns_message_detach(&query); 548 } 549 if (response != NULL) { 550 dns_message_detach(&response); 551 } 552 dns_request_destroy(&reqev->request); 553 isc_event_free(&event); 554 555 if (--onfly == 0) { 556 isc_app_shutdown(); 557 } 558 return; 559 } 560 561 /*% 562 * Add EDNS0 option record to a message. Currently, the only supported 563 * options are UDP buffer size, the DO bit, and EDNS options 564 * (e.g., NSID, COOKIE, client-subnet) 565 */ 566 static void 567 add_opt(dns_message_t *msg, uint16_t udpsize, uint16_t edns, unsigned int flags, 568 dns_ednsopt_t *opts, size_t count) { 569 dns_rdataset_t *rdataset = NULL; 570 isc_result_t result; 571 572 result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags, 573 opts, count); 574 CHECK("dns_message_buildopt", result); 575 result = dns_message_setopt(msg, rdataset); 576 CHECK("dns_message_setopt", result); 577 } 578 579 static void 580 compute_cookie(unsigned char *cookie, size_t len) { 581 /* XXXMPA need to fix, should be per server. */ 582 INSIST(len >= 8U); 583 memmove(cookie, cookie_secret, 8); 584 } 585 586 static isc_result_t 587 sendquery(struct query *query, isc_task_t *task) { 588 dns_request_t *request = NULL; 589 dns_message_t *message = NULL; 590 dns_name_t *qname = NULL; 591 dns_rdataset_t *qrdataset = NULL; 592 isc_result_t result; 593 dns_fixedname_t queryname; 594 isc_buffer_t buf; 595 unsigned int options; 596 597 onfly++; 598 599 dns_fixedname_init(&queryname); 600 isc_buffer_init(&buf, query->textname, strlen(query->textname)); 601 isc_buffer_add(&buf, strlen(query->textname)); 602 result = dns_name_fromtext(dns_fixedname_name(&queryname), &buf, 603 dns_rootname, 0, NULL); 604 CHECK("dns_name_fromtext", result); 605 606 dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &message); 607 608 message->opcode = dns_opcode_query; 609 if (query->recurse) { 610 message->flags |= DNS_MESSAGEFLAG_RD; 611 } 612 if (query->have_aaonly) { 613 message->flags |= DNS_MESSAGEFLAG_AA; 614 } 615 if (query->have_adflag) { 616 message->flags |= DNS_MESSAGEFLAG_AD; 617 } 618 if (query->have_cdflag) { 619 message->flags |= DNS_MESSAGEFLAG_CD; 620 } 621 if (query->have_zflag) { 622 message->flags |= 0x0040U; 623 } 624 message->rdclass = query->rdclass; 625 message->id = (unsigned short)(random() & 0xFFFF); 626 627 result = dns_message_gettempname(message, &qname); 628 CHECK("dns_message_gettempname", result); 629 630 result = dns_message_gettemprdataset(message, &qrdataset); 631 CHECK("dns_message_gettemprdataset", result); 632 633 dns_name_clone(dns_fixedname_name(&queryname), qname); 634 dns_rdataset_makequestion(qrdataset, query->rdclass, query->rdtype); 635 ISC_LIST_APPEND(qname->list, qrdataset, link); 636 dns_message_addname(message, qname, DNS_SECTION_QUESTION); 637 638 if (query->udpsize > 0 || query->dnssec || query->edns > -1 || 639 query->ecs_addr != NULL) 640 { 641 dns_ednsopt_t opts[EDNSOPTS + DNS_EDNSOPTIONS]; 642 unsigned int flags; 643 int i = 0; 644 char ecsbuf[20]; 645 unsigned char cookie[40]; 646 647 if (query->udpsize == 0) { 648 query->udpsize = 4096; 649 } 650 if (query->edns < 0) { 651 query->edns = 0; 652 } 653 654 if (query->nsid) { 655 INSIST(i < DNS_EDNSOPTIONS); 656 opts[i].code = DNS_OPT_NSID; 657 opts[i].length = 0; 658 opts[i].value = NULL; 659 i++; 660 } 661 662 if (query->ecs_addr != NULL) { 663 uint8_t addr[16], family; 664 uint32_t plen; 665 struct sockaddr *sa; 666 struct sockaddr_in *sin; 667 struct sockaddr_in6 *sin6; 668 size_t addrl; 669 isc_buffer_t b; 670 671 sa = &query->ecs_addr->type.sa; 672 plen = query->ecs_addr->length; 673 674 /* Round up prefix len to a multiple of 8 */ 675 addrl = (plen + 7) / 8; 676 677 INSIST(i < DNS_EDNSOPTIONS); 678 opts[i].code = DNS_OPT_CLIENT_SUBNET; 679 opts[i].length = (uint16_t)addrl + 4; 680 CHECK("isc_buffer_allocate", result); 681 isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf)); 682 if (sa->sa_family == AF_INET) { 683 family = 1; 684 sin = (struct sockaddr_in *)sa; 685 memmove(addr, &sin->sin_addr, 4); 686 if ((plen % 8) != 0) { 687 addr[addrl - 1] &= ~0U 688 << (8 - (plen % 8)); 689 } 690 } else { 691 family = 2; 692 sin6 = (struct sockaddr_in6 *)sa; 693 memmove(addr, &sin6->sin6_addr, 16); 694 } 695 696 /* Mask off last address byte */ 697 if (addrl > 0 && (plen % 8) != 0) { 698 addr[addrl - 1] &= ~0U << (8 - (plen % 8)); 699 } 700 701 /* family */ 702 isc_buffer_putuint16(&b, family); 703 /* source prefix-length */ 704 isc_buffer_putuint8(&b, plen); 705 /* scope prefix-length */ 706 isc_buffer_putuint8(&b, 0); 707 /* address */ 708 if (addrl > 0) { 709 isc_buffer_putmem(&b, addr, (unsigned)addrl); 710 } 711 712 opts[i].value = (uint8_t *)ecsbuf; 713 i++; 714 } 715 716 if (query->send_cookie) { 717 INSIST(i < DNS_EDNSOPTIONS); 718 opts[i].code = DNS_OPT_COOKIE; 719 if (query->cookie != NULL) { 720 isc_buffer_t b; 721 722 isc_buffer_init(&b, cookie, sizeof(cookie)); 723 result = isc_hex_decodestring(query->cookie, 724 &b); 725 CHECK("isc_hex_decodestring", result); 726 opts[i].value = isc_buffer_base(&b); 727 opts[i].length = isc_buffer_usedlength(&b); 728 } else { 729 compute_cookie(cookie, 8); 730 opts[i].length = 8; 731 opts[i].value = cookie; 732 } 733 i++; 734 } 735 736 if (query->expire) { 737 INSIST(i < DNS_EDNSOPTIONS); 738 opts[i].code = DNS_OPT_EXPIRE; 739 opts[i].length = 0; 740 opts[i].value = NULL; 741 i++; 742 } 743 744 if (query->ednsoptscnt != 0) { 745 memmove(&opts[i], query->ednsopts, 746 sizeof(dns_ednsopt_t) * query->ednsoptscnt); 747 i += query->ednsoptscnt; 748 } 749 750 flags = query->ednsflags; 751 flags &= ~DNS_MESSAGEEXTFLAG_DO; 752 if (query->dnssec) { 753 flags |= DNS_MESSAGEEXTFLAG_DO; 754 } 755 add_opt(message, query->udpsize, query->edns, flags, opts, i); 756 } 757 758 options = 0; 759 if (tcp_mode) { 760 options |= DNS_REQUESTOPT_TCP | DNS_REQUESTOPT_SHARE; 761 } 762 request = NULL; 763 result = dns_request_createvia( 764 requestmgr, message, have_src ? &srcaddr : NULL, &dstaddr, dscp, 765 options, NULL, query->timeout, query->udptimeout, 766 query->udpretries, task, recvresponse, message, &request); 767 CHECK("dns_request_createvia4", result); 768 769 return (ISC_R_SUCCESS); 770 } 771 772 static void 773 sendqueries(isc_task_t *task, isc_event_t *event) { 774 struct query *query = (struct query *)event->ev_arg; 775 776 isc_event_free(&event); 777 778 while (query != NULL) { 779 struct query *next = ISC_LIST_NEXT(query, link); 780 781 sendquery(query, task); 782 query = next; 783 } 784 785 if (onfly == 0) { 786 isc_app_shutdown(); 787 } 788 return; 789 } 790 791 ISC_PLATFORM_NORETURN_PRE static void 792 usage(void) ISC_PLATFORM_NORETURN_POST; 793 794 static void 795 usage(void) { 796 fputs("Usage: mdig @server {global-opt} host\n" 797 " {local-opt} [ host {local-opt} [...]]\n", 798 stderr); 799 fputs("\nUse \"mdig -h\" (or \"mdig -h | more\") " 800 "for complete list of options\n", 801 stderr); 802 exit(1); 803 } 804 805 /*% help */ 806 static void 807 help(void) { 808 fputs("Usage: mdig @server {global-opt} host\n" 809 " {local-opt} [ host {local-opt} [...]]\n", 810 stdout); 811 fputs("Where:\n" 812 " anywhere opt is one of:\n" 813 " -f filename (batch mode)\n" 814 " -h (print help and exit)\n" 815 " -v (print version and exit)\n" 816 " global opt is one of:\n" 817 " -4 (use IPv4 query transport " 818 "only)\n" 819 " -6 (use IPv6 query transport " 820 "only)\n" 821 " -b address[#port] (bind to source " 822 "address/port)\n" 823 " -p port (specify port number)\n" 824 " -m (enable memory usage " 825 "debugging)\n" 826 " +[no]dscp[=###] (Set the DSCP value to ### " 827 "[0..63])\n" 828 " +[no]vc (TCP mode)\n" 829 " +[no]tcp (TCP mode, alternate " 830 "syntax)\n" 831 " +[no]besteffort (Try to parse even illegal " 832 "messages)\n" 833 " +[no]cl (Control display of class " 834 "in records)\n" 835 " +[no]comments (Control display of " 836 "comment lines)\n" 837 " +[no]rrcomments (Control display of " 838 "per-record " 839 "comments)\n" 840 " +[no]crypto (Control display of " 841 "cryptographic " 842 "fields in records)\n" 843 " +[no]question (Control display of " 844 "question)\n" 845 " +[no]answer (Control display of " 846 "answer)\n" 847 " +[no]authority (Control display of " 848 "authority)\n" 849 " +[no]additional (Control display of " 850 "additional)\n" 851 " +[no]short (Disable everything except " 852 "short\n" 853 " form of answer)\n" 854 " +[no]ttlid (Control display of ttls " 855 "in records)\n" 856 " +[no]ttlunits (Display TTLs in " 857 "human-readable units)\n" 858 " +[no]unknownformat (Print RDATA in RFC 3597 " 859 "\"unknown\" format)\n" 860 " +[no]all (Set or clear all display " 861 "flags)\n" 862 " +[no]multiline (Print records in an " 863 "expanded format)\n" 864 " +[no]split=## (Split hex/base64 fields " 865 "into chunks)\n" 866 " local opt is one of:\n" 867 " -c class (specify query class)\n" 868 " -t type (specify query type)\n" 869 " -x dot-notation (shortcut for reverse " 870 "lookups)\n" 871 " +timeout=### (Set query timeout) " 872 "[UDP=5,TCP=10]\n" 873 " +udptimeout=### (Set timeout before UDP " 874 "retry)\n" 875 " +tries=### (Set number of UDP " 876 "attempts) [3]\n" 877 " +retry=### (Set number of UDP " 878 "retries) [2]\n" 879 " +bufsize=### (Set EDNS0 Max UDP packet " 880 "size)\n" 881 " +subnet=addr (Set edns-client-subnet " 882 "option)\n" 883 " +[no]edns[=###] (Set EDNS version) [0]\n" 884 " +ednsflags=### (Set EDNS flag bits)\n" 885 " +ednsopt=###[:value] (Send specified EDNS " 886 "option)\n" 887 " +noednsopt (Clear list of +ednsopt " 888 "options)\n" 889 " +[no]recurse (Recursive mode)\n" 890 " +[no]aaonly (Set AA flag in query " 891 "(+[no]aaflag))\n" 892 " +[no]adflag (Set AD flag in query)\n" 893 " +[no]cdflag (Set CD flag in query)\n" 894 " +[no]zflag (Set Z flag in query)\n" 895 " +[no]dnssec (Request DNSSEC records)\n" 896 " +[no]expire (Request time to expire)\n" 897 " +[no]cookie[=###] (Send a COOKIE option)\n" 898 " +[no]nsid (Request Name Server ID)\n", 899 stdout); 900 } 901 902 ISC_PLATFORM_NORETURN_PRE static void 903 fatal(const char *format, ...) 904 ISC_FORMAT_PRINTF(1, 2) ISC_PLATFORM_NORETURN_POST; 905 906 static void 907 fatal(const char *format, ...) { 908 va_list args; 909 910 fflush(stdout); 911 fprintf(stderr, "mdig: "); 912 va_start(args, format); 913 vfprintf(stderr, format, args); 914 va_end(args); 915 fprintf(stderr, "\n"); 916 exit(-2); 917 } 918 919 static isc_result_t 920 parse_uint_helper(uint32_t *uip, const char *value, uint32_t max, 921 const char *desc, int base) { 922 uint32_t n; 923 isc_result_t result = isc_parse_uint32(&n, value, base); 924 if (result == ISC_R_SUCCESS && n > max) { 925 result = ISC_R_RANGE; 926 } 927 if (result != ISC_R_SUCCESS) { 928 printf("invalid %s '%s': %s\n", desc, value, 929 isc_result_totext(result)); 930 return (result); 931 } 932 *uip = n; 933 return (ISC_R_SUCCESS); 934 } 935 936 static isc_result_t 937 parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc) { 938 return (parse_uint_helper(uip, value, max, desc, 10)); 939 } 940 941 static isc_result_t 942 parse_xint(uint32_t *uip, const char *value, uint32_t max, const char *desc) { 943 return (parse_uint_helper(uip, value, max, desc, 0)); 944 } 945 946 static void 947 newopts(struct query *query) { 948 size_t len = sizeof(query->ednsopts[0]) * EDNSOPTS; 949 size_t i; 950 951 query->ednsopts = isc_mem_allocate(mctx, len); 952 953 for (i = 0; i < EDNSOPTS; i++) { 954 query->ednsopts[i].code = 0; 955 query->ednsopts[i].length = 0; 956 query->ednsopts[i].value = NULL; 957 } 958 } 959 960 static void 961 save_opt(struct query *query, char *code, char *value) { 962 uint32_t num; 963 isc_buffer_t b; 964 isc_result_t result; 965 966 if (query->ednsopts == NULL) { 967 newopts(query); 968 } 969 970 if (query->ednsoptscnt == EDNSOPTS) { 971 fatal("too many ednsopts"); 972 } 973 974 result = parse_uint(&num, code, 65535, "ednsopt"); 975 if (result != ISC_R_SUCCESS) { 976 fatal("bad edns code point: %s", code); 977 } 978 979 query->ednsopts[query->ednsoptscnt].code = num; 980 query->ednsopts[query->ednsoptscnt].length = 0; 981 query->ednsopts[query->ednsoptscnt].value = NULL; 982 983 if (value != NULL) { 984 char *buf; 985 buf = isc_mem_allocate(mctx, strlen(value) / 2 + 1); 986 isc_buffer_init(&b, buf, strlen(value) / 2 + 1); 987 result = isc_hex_decodestring(value, &b); 988 CHECK("isc_hex_decodestring", result); 989 query->ednsopts[query->ednsoptscnt].value = isc_buffer_base(&b); 990 query->ednsopts[query->ednsoptscnt].length = 991 isc_buffer_usedlength(&b); 992 } 993 994 query->ednsoptscnt++; 995 } 996 997 static isc_result_t 998 parse_netprefix(isc_sockaddr_t **sap, const char *value) { 999 isc_sockaddr_t *sa = NULL; 1000 struct in_addr in4; 1001 struct in6_addr in6; 1002 uint32_t netmask = 0xffffffff; 1003 char *slash = NULL; 1004 bool parsed = false; 1005 char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX/128")]; 1006 1007 if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) { 1008 fatal("invalid prefix '%s'\n", value); 1009 } 1010 1011 slash = strchr(buf, '/'); 1012 if (slash != NULL) { 1013 isc_result_t result; 1014 *slash = '\0'; 1015 result = isc_parse_uint32(&netmask, slash + 1, 10); 1016 if (result != ISC_R_SUCCESS) { 1017 fatal("invalid prefix length in '%s': %s\n", value, 1018 isc_result_totext(result)); 1019 } 1020 } else if (strcmp(value, "0") == 0) { 1021 netmask = 0; 1022 } 1023 1024 sa = isc_mem_allocate(mctx, sizeof(*sa)); 1025 if (inet_pton(AF_INET6, buf, &in6) == 1) { 1026 parsed = true; 1027 isc_sockaddr_fromin6(sa, &in6, 0); 1028 if (netmask > 128) { 1029 netmask = 128; 1030 } 1031 } else if (inet_pton(AF_INET, buf, &in4) == 1) { 1032 parsed = true; 1033 isc_sockaddr_fromin(sa, &in4, 0); 1034 if (netmask > 32) { 1035 netmask = 32; 1036 } 1037 } else if (netmask != 0xffffffff) { 1038 int i; 1039 1040 for (i = 0; i < 3 && strlen(buf) < sizeof(buf) - 2; i++) { 1041 strlcat(buf, ".0", sizeof(buf)); 1042 if (inet_pton(AF_INET, buf, &in4) == 1) { 1043 parsed = true; 1044 isc_sockaddr_fromin(sa, &in4, 0); 1045 break; 1046 } 1047 } 1048 1049 if (netmask > 32) { 1050 netmask = 32; 1051 } 1052 } 1053 1054 if (!parsed) { 1055 fatal("invalid address '%s'", value); 1056 } 1057 1058 sa->length = netmask; 1059 *sap = sa; 1060 1061 return (ISC_R_SUCCESS); 1062 } 1063 1064 /*% 1065 * Append 'len' bytes of 'text' at '*p', failing with 1066 * ISC_R_NOSPACE if that would advance p past 'end'. 1067 */ 1068 static isc_result_t 1069 append(const char *text, int len, char **p, char *end) { 1070 if (len > end - *p) { 1071 return (ISC_R_NOSPACE); 1072 } 1073 memmove(*p, text, len); 1074 *p += len; 1075 return (ISC_R_SUCCESS); 1076 } 1077 1078 static isc_result_t 1079 reverse_octets(const char *in, char **p, char *end) { 1080 const char *dot = strchr(in, '.'); 1081 int len; 1082 if (dot != NULL) { 1083 isc_result_t result; 1084 result = reverse_octets(dot + 1, p, end); 1085 CHECK("reverse_octets", result); 1086 result = append(".", 1, p, end); 1087 CHECK("append", result); 1088 len = (int)(dot - in); 1089 } else { 1090 len = strlen(in); 1091 } 1092 return (append(in, len, p, end)); 1093 } 1094 1095 static void 1096 get_reverse(char *reverse, size_t len, const char *value) { 1097 int r; 1098 isc_result_t result; 1099 isc_netaddr_t addr; 1100 1101 addr.family = AF_INET6; 1102 r = inet_pton(AF_INET6, value, &addr.type.in6); 1103 if (r > 0) { 1104 /* This is a valid IPv6 address. */ 1105 dns_fixedname_t fname; 1106 dns_name_t *name; 1107 unsigned int options = 0; 1108 1109 name = dns_fixedname_initname(&fname); 1110 result = dns_byaddr_createptrname(&addr, options, name); 1111 CHECK("dns_byaddr_createptrname2", result); 1112 dns_name_format(name, reverse, (unsigned int)len); 1113 return; 1114 } else { 1115 /* 1116 * Not a valid IPv6 address. Assume IPv4. 1117 * Construct the in-addr.arpa name by blindly 1118 * reversing octets whether or not they look like 1119 * integers, so that this can be used for RFC2317 1120 * names and such. 1121 */ 1122 char *p = reverse; 1123 char *end = reverse + len; 1124 result = reverse_octets(value, &p, end); 1125 CHECK("reverse_octets", result); 1126 /* Append .in-addr.arpa. and a terminating NUL. */ 1127 result = append(".in-addr.arpa.", 15, &p, end); 1128 CHECK("append", result); 1129 return; 1130 } 1131 } 1132 1133 /*% 1134 * We're not using isc_commandline_parse() here since the command line 1135 * syntax of mdig is quite a bit different from that which can be described 1136 * by that routine. 1137 * XXX doc options 1138 */ 1139 1140 static void 1141 plus_option(char *option, struct query *query, bool global) { 1142 isc_result_t result; 1143 char *cmd, *value, *last = NULL, *code; 1144 uint32_t num; 1145 bool state = true; 1146 size_t n; 1147 1148 INSIST(option != NULL); 1149 1150 if ((cmd = strtok_r(option, "=", &last)) == NULL) { 1151 printf(";; Invalid option %s\n", option); 1152 return; 1153 } 1154 if (strncasecmp(cmd, "no", 2) == 0) { 1155 cmd += 2; 1156 state = false; 1157 } 1158 /* parse the rest of the string */ 1159 value = strtok_r(NULL, "", &last); 1160 1161 #define FULLCHECK(A) \ 1162 do { \ 1163 size_t _l = strlen(cmd); \ 1164 if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \ 1165 goto invalid_option; \ 1166 } while (0) 1167 #define FULLCHECK2(A, B) \ 1168 do { \ 1169 size_t _l = strlen(cmd); \ 1170 if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \ 1171 (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \ 1172 goto invalid_option; \ 1173 } while (0) 1174 #define GLOBAL() \ 1175 do { \ 1176 if (!global) \ 1177 goto global_option; \ 1178 } while (0) 1179 1180 switch (cmd[0]) { 1181 case 'a': 1182 switch (cmd[1]) { 1183 case 'a': /* aaonly / aaflag */ 1184 FULLCHECK2("aaonly", "aaflag"); 1185 query->have_aaonly = state; 1186 break; 1187 case 'd': 1188 switch (cmd[2]) { 1189 case 'd': /* additional */ 1190 FULLCHECK("additional"); 1191 display_additional = state; 1192 break; 1193 case 'f': /* adflag */ 1194 case '\0': /* +ad is a synonym for +adflag */ 1195 FULLCHECK("adflag"); 1196 query->have_adflag = state; 1197 break; 1198 default: 1199 goto invalid_option; 1200 } 1201 break; 1202 case 'l': /* all */ 1203 FULLCHECK("all"); 1204 GLOBAL(); 1205 display_question = state; 1206 display_answer = state; 1207 display_authority = state; 1208 display_additional = state; 1209 display_comments = state; 1210 display_rrcomments = state ? 1 : -1; 1211 break; 1212 case 'n': /* answer */ 1213 FULLCHECK("answer"); 1214 GLOBAL(); 1215 display_answer = state; 1216 break; 1217 case 'u': /* authority */ 1218 FULLCHECK("authority"); 1219 GLOBAL(); 1220 display_authority = state; 1221 break; 1222 default: 1223 goto invalid_option; 1224 } 1225 break; 1226 case 'b': 1227 switch (cmd[1]) { 1228 case 'e': /* besteffort */ 1229 FULLCHECK("besteffort"); 1230 GLOBAL(); 1231 besteffort = state; 1232 break; 1233 case 'u': 1234 switch (cmd[2]) { 1235 case 'f': /* bufsize */ 1236 FULLCHECK("bufsize"); 1237 if (value == NULL) { 1238 goto need_value; 1239 } 1240 if (!state) { 1241 goto invalid_option; 1242 } 1243 result = parse_uint(&num, value, COMMSIZE, 1244 "buffer size"); 1245 CHECK("parse_uint(buffer size)", result); 1246 query->udpsize = num; 1247 break; 1248 case 'r': /* burst */ 1249 FULLCHECK("burst"); 1250 GLOBAL(); 1251 burst = state; 1252 break; 1253 default: 1254 goto invalid_option; 1255 } 1256 break; 1257 default: 1258 goto invalid_option; 1259 } 1260 break; 1261 case 'c': 1262 switch (cmd[1]) { 1263 case 'd': /* cdflag */ 1264 switch (cmd[2]) { 1265 case 'f': /* cdflag */ 1266 case '\0': /* +cd is a synonym for +cdflag */ 1267 FULLCHECK("cdflag"); 1268 query->have_cdflag = state; 1269 break; 1270 default: 1271 goto invalid_option; 1272 } 1273 break; 1274 case 'l': /* cl */ 1275 FULLCHECK("cl"); 1276 GLOBAL(); 1277 display_class = state; 1278 break; 1279 case 'o': /* comments */ 1280 switch (cmd[2]) { 1281 case 'm': 1282 FULLCHECK("comments"); 1283 GLOBAL(); 1284 display_comments = state; 1285 break; 1286 case 'n': 1287 FULLCHECK("continue"); 1288 GLOBAL(); 1289 continue_on_error = state; 1290 break; 1291 case 'o': 1292 FULLCHECK("cookie"); 1293 if (state && query->edns == -1) { 1294 query->edns = 0; 1295 } 1296 query->send_cookie = state; 1297 if (value != NULL) { 1298 n = strlcpy(hexcookie, value, 1299 sizeof(hexcookie)); 1300 if (n >= sizeof(hexcookie)) { 1301 fatal("COOKIE data too large"); 1302 } 1303 query->cookie = hexcookie; 1304 } else { 1305 query->cookie = NULL; 1306 } 1307 break; 1308 default: 1309 goto invalid_option; 1310 } 1311 break; 1312 case 'r': 1313 FULLCHECK("crypto"); 1314 GLOBAL(); 1315 display_crypto = state; 1316 break; 1317 default: 1318 goto invalid_option; 1319 } 1320 break; 1321 case 'd': 1322 switch (cmd[1]) { 1323 case 'n': /* dnssec */ 1324 FULLCHECK("dnssec"); 1325 if (state && query->edns == -1) { 1326 query->edns = 0; 1327 } 1328 query->dnssec = state; 1329 break; 1330 case 's': /* dscp */ 1331 FULLCHECK("dscp"); 1332 GLOBAL(); 1333 if (!state) { 1334 dscp = -1; 1335 break; 1336 } 1337 if (value == NULL) { 1338 goto need_value; 1339 } 1340 result = parse_uint(&num, value, 0x3f, "DSCP"); 1341 CHECK("parse_uint(DSCP)", result); 1342 dscp = num; 1343 break; 1344 default: 1345 goto invalid_option; 1346 } 1347 break; 1348 case 'e': 1349 switch (cmd[1]) { 1350 case 'd': 1351 switch (cmd[2]) { 1352 case 'n': 1353 switch (cmd[3]) { 1354 case 's': 1355 switch (cmd[4]) { 1356 case 0: 1357 FULLCHECK("edns"); 1358 if (!state) { 1359 query->edns = -1; 1360 break; 1361 } 1362 if (value == NULL) { 1363 query->edns = 0; 1364 break; 1365 } 1366 result = parse_uint(&num, value, 1367 255, 1368 "edns"); 1369 CHECK("parse_uint(edns)", 1370 result); 1371 query->edns = num; 1372 break; 1373 case 'f': 1374 FULLCHECK("ednsflags"); 1375 if (!state) { 1376 query->ednsflags = 0; 1377 break; 1378 } 1379 if (value == NULL) { 1380 query->ednsflags = 0; 1381 break; 1382 } 1383 result = parse_xint( 1384 &num, value, 0xffff, 1385 "ednsflags"); 1386 CHECK("parse_xint(ednsflags)", 1387 result); 1388 query->ednsflags = num; 1389 break; 1390 case 'o': 1391 FULLCHECK("ednsopt"); 1392 if (!state) { 1393 query->ednsoptscnt = 0; 1394 break; 1395 } 1396 code = NULL; 1397 if (value != NULL) { 1398 code = strtok_r(value, 1399 ":", 1400 &last); 1401 } 1402 if (code == NULL) { 1403 fatal("ednsopt no " 1404 "code point " 1405 "specified"); 1406 } 1407 value = strtok_r(NULL, "\0", 1408 &last); 1409 save_opt(query, code, value); 1410 break; 1411 default: 1412 goto invalid_option; 1413 } 1414 break; 1415 default: 1416 goto invalid_option; 1417 } 1418 break; 1419 default: 1420 goto invalid_option; 1421 } 1422 break; 1423 case 'x': 1424 FULLCHECK("expire"); 1425 query->expire = state; 1426 break; 1427 default: 1428 goto invalid_option; 1429 } 1430 break; 1431 case 'm': /* multiline */ 1432 FULLCHECK("multiline"); 1433 GLOBAL(); 1434 display_multiline = state; 1435 break; 1436 case 'n': 1437 FULLCHECK("nsid"); 1438 if (state && query->edns == -1) { 1439 query->edns = 0; 1440 } 1441 query->nsid = state; 1442 break; 1443 case 'q': 1444 FULLCHECK("question"); 1445 GLOBAL(); 1446 display_question = state; 1447 break; 1448 case 'r': 1449 switch (cmd[1]) { 1450 case 'e': 1451 switch (cmd[2]) { 1452 case 'c': /* recurse */ 1453 FULLCHECK("recurse"); 1454 query->recurse = state; 1455 break; 1456 case 't': /* retry / retries */ 1457 FULLCHECK2("retry", "retries"); 1458 if (value == NULL) { 1459 goto need_value; 1460 } 1461 if (!state) { 1462 goto invalid_option; 1463 } 1464 result = parse_uint(&query->udpretries, value, 1465 MAXTRIES - 1, "udpretries"); 1466 CHECK("parse_uint(udpretries)", result); 1467 query->udpretries++; 1468 break; 1469 default: 1470 goto invalid_option; 1471 } 1472 break; 1473 case 'r': 1474 FULLCHECK("rrcomments"); 1475 GLOBAL(); 1476 display_rrcomments = state ? 1 : -1; 1477 break; 1478 default: 1479 goto invalid_option; 1480 } 1481 break; 1482 case 's': 1483 switch (cmd[1]) { 1484 case 'h': 1485 FULLCHECK("short"); 1486 GLOBAL(); 1487 display_short_form = state; 1488 if (state) { 1489 display_question = false; 1490 display_answer = true; 1491 display_authority = false; 1492 display_additional = false; 1493 display_comments = false; 1494 display_rrcomments = -1; 1495 } 1496 break; 1497 case 'p': /* split */ 1498 FULLCHECK("split"); 1499 GLOBAL(); 1500 if (value != NULL && !state) { 1501 goto invalid_option; 1502 } 1503 if (!state) { 1504 display_splitwidth = 0; 1505 break; 1506 } else if (value == NULL) { 1507 break; 1508 } 1509 1510 result = parse_uint(&display_splitwidth, value, 1023, 1511 "split"); 1512 if ((display_splitwidth % 4) != 0) { 1513 display_splitwidth = 1514 ((display_splitwidth + 3) / 4) * 4; 1515 fprintf(stderr, 1516 ";; Warning, split must be " 1517 "a multiple of 4; adjusting " 1518 "to %u\n", 1519 display_splitwidth); 1520 } 1521 /* 1522 * There is an adjustment done in the 1523 * totext_<rrtype>() functions which causes 1524 * splitwidth to shrink. This is okay when we're 1525 * using the default width but incorrect in this 1526 * case, so we correct for it 1527 */ 1528 if (display_splitwidth) { 1529 display_splitwidth += 3; 1530 } 1531 CHECK("parse_uint(split)", result); 1532 break; 1533 case 'u': /* subnet */ 1534 FULLCHECK("subnet"); 1535 if (state && value == NULL) { 1536 goto need_value; 1537 } 1538 if (!state) { 1539 if (query->ecs_addr != NULL) { 1540 isc_mem_free(mctx, query->ecs_addr); 1541 query->ecs_addr = NULL; 1542 } 1543 break; 1544 } 1545 if (query->edns == -1) { 1546 query->edns = 0; 1547 } 1548 result = parse_netprefix(&query->ecs_addr, value); 1549 CHECK("parse_netprefix", result); 1550 break; 1551 default: 1552 goto invalid_option; 1553 } 1554 break; 1555 case 't': 1556 switch (cmd[1]) { 1557 case 'c': /* tcp */ 1558 FULLCHECK("tcp"); 1559 GLOBAL(); 1560 tcp_mode = state; 1561 break; 1562 case 'i': /* timeout */ 1563 FULLCHECK("timeout"); 1564 if (value == NULL) { 1565 goto need_value; 1566 } 1567 if (!state) { 1568 goto invalid_option; 1569 } 1570 result = parse_uint(&query->timeout, value, MAXTIMEOUT, 1571 "timeout"); 1572 CHECK("parse_uint(timeout)", result); 1573 if (query->timeout == 0) { 1574 query->timeout = 1; 1575 } 1576 break; 1577 case 'r': 1578 FULLCHECK("tries"); 1579 if (value == NULL) { 1580 goto need_value; 1581 } 1582 if (!state) { 1583 goto invalid_option; 1584 } 1585 result = parse_uint(&query->udpretries, value, MAXTRIES, 1586 "udpretries"); 1587 CHECK("parse_uint(udpretries)", result); 1588 if (query->udpretries == 0) { 1589 query->udpretries = 1; 1590 } 1591 break; 1592 case 't': 1593 switch (cmd[2]) { 1594 case 'l': 1595 switch (cmd[3]) { 1596 case 0: 1597 case 'i': /* ttlid */ 1598 FULLCHECK2("ttl", "ttlid"); 1599 GLOBAL(); 1600 display_ttl = state; 1601 break; 1602 case 'u': /* ttlunits */ 1603 FULLCHECK("ttlunits"); 1604 GLOBAL(); 1605 display_ttl = true; 1606 display_ttlunits = state; 1607 break; 1608 default: 1609 goto invalid_option; 1610 } 1611 break; 1612 default: 1613 goto invalid_option; 1614 } 1615 break; 1616 default: 1617 goto invalid_option; 1618 } 1619 break; 1620 case 'u': 1621 switch (cmd[1]) { 1622 case 'd': 1623 FULLCHECK("udptimeout"); 1624 if (value == NULL) { 1625 goto need_value; 1626 } 1627 if (!state) { 1628 goto invalid_option; 1629 } 1630 result = parse_uint(&query->udptimeout, value, 1631 MAXTIMEOUT, "udptimeout"); 1632 CHECK("parse_uint(udptimeout)", result); 1633 break; 1634 case 'n': 1635 FULLCHECK("unknownformat"); 1636 display_unknown_format = state; 1637 break; 1638 default: 1639 goto invalid_option; 1640 } 1641 break; 1642 case 'v': 1643 FULLCHECK("vc"); 1644 GLOBAL(); 1645 tcp_mode = state; 1646 break; 1647 case 'y': /* yaml */ 1648 FULLCHECK("yaml"); 1649 yaml = state; 1650 if (state) { 1651 display_rrcomments = state; 1652 } 1653 break; 1654 case 'z': /* zflag */ 1655 FULLCHECK("zflag"); 1656 query->have_zflag = state; 1657 break; 1658 global_option: 1659 fprintf(stderr, "Ignored late global option: +%s\n", option); 1660 break; 1661 default: 1662 invalid_option: 1663 need_value: 1664 fprintf(stderr, "Invalid option: +%s\n", option); 1665 usage(); 1666 } 1667 return; 1668 } 1669 1670 /*% 1671 * #true returned if value was used 1672 */ 1673 static const char *single_dash_opts = "46himv"; 1674 static const char *dash_opts = "46bcfhiptvx"; 1675 static bool 1676 dash_option(const char *option, char *next, struct query *query, bool global, 1677 bool *setname) { 1678 char opt; 1679 const char *value; 1680 isc_result_t result; 1681 bool value_from_next; 1682 isc_consttextregion_t tr; 1683 dns_rdatatype_t rdtype; 1684 dns_rdataclass_t rdclass; 1685 char textname[MXNAME]; 1686 struct in_addr in4; 1687 struct in6_addr in6; 1688 in_port_t srcport; 1689 char *hash; 1690 uint32_t num; 1691 1692 while (strpbrk(option, single_dash_opts) == &option[0]) { 1693 /* 1694 * Since the -[46hiv] options do not take an argument, 1695 * account for them (in any number and/or combination) 1696 * if they appear as the first character(s) of an opt. 1697 */ 1698 opt = option[0]; 1699 switch (opt) { 1700 case '4': 1701 GLOBAL(); 1702 if (have_ipv4) { 1703 isc_net_disableipv6(); 1704 have_ipv6 = false; 1705 } else { 1706 fatal("can't find IPv4 networking"); 1707 UNREACHABLE(); 1708 return (false); 1709 } 1710 break; 1711 case '6': 1712 GLOBAL(); 1713 if (have_ipv6) { 1714 isc_net_disableipv4(); 1715 have_ipv4 = false; 1716 } else { 1717 fatal("can't find IPv6 networking"); 1718 UNREACHABLE(); 1719 return (false); 1720 } 1721 break; 1722 case 'h': 1723 help(); 1724 exit(0); 1725 break; 1726 case 'i': 1727 /* deprecated */ 1728 break; 1729 case 'm': 1730 /* 1731 * handled by preparse_args() 1732 */ 1733 break; 1734 case 'v': 1735 fputs("mDiG " VERSION "\n", stderr); 1736 exit(0); 1737 break; 1738 } 1739 if (strlen(option) > 1U) { 1740 option = &option[1]; 1741 } else { 1742 return (false); 1743 } 1744 } 1745 opt = option[0]; 1746 if (strlen(option) > 1U) { 1747 value_from_next = false; 1748 value = &option[1]; 1749 } else { 1750 value_from_next = true; 1751 value = next; 1752 } 1753 if (value == NULL) { 1754 goto invalid_option; 1755 } 1756 switch (opt) { 1757 case 'b': 1758 GLOBAL(); 1759 hash = strchr(value, '#'); 1760 if (hash != NULL) { 1761 result = parse_uint(&num, hash + 1, MAXPORT, 1762 "port number"); 1763 CHECK("parse_uint(srcport)", result); 1764 srcport = num; 1765 *hash = '\0'; 1766 } else { 1767 srcport = 0; 1768 } 1769 if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) { 1770 isc_sockaddr_fromin6(&srcaddr, &in6, srcport); 1771 isc_net_disableipv4(); 1772 } else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) { 1773 isc_sockaddr_fromin(&srcaddr, &in4, srcport); 1774 isc_net_disableipv6(); 1775 } else { 1776 if (hash != NULL) { 1777 *hash = '#'; 1778 } 1779 fatal("invalid address %s", value); 1780 } 1781 if (hash != NULL) { 1782 *hash = '#'; 1783 } 1784 have_src = true; 1785 return (value_from_next); 1786 case 'c': 1787 tr.base = value; 1788 tr.length = strlen(value); 1789 result = dns_rdataclass_fromtext(&rdclass, 1790 (isc_textregion_t *)&tr); 1791 CHECK("dns_rdataclass_fromtext", result); 1792 query->rdclass = rdclass; 1793 return (value_from_next); 1794 case 'f': 1795 batchname = value; 1796 return (value_from_next); 1797 case 'p': 1798 GLOBAL(); 1799 result = parse_uint(&num, value, MAXPORT, "port number"); 1800 CHECK("parse_uint(port)", result); 1801 port = num; 1802 return (value_from_next); 1803 case 't': 1804 tr.base = value; 1805 tr.length = strlen(value); 1806 result = dns_rdatatype_fromtext(&rdtype, 1807 (isc_textregion_t *)&tr); 1808 CHECK("dns_rdatatype_fromtext", result); 1809 query->rdtype = rdtype; 1810 return (value_from_next); 1811 case 'x': 1812 get_reverse(textname, sizeof(textname), value); 1813 strlcpy(query->textname, textname, sizeof(query->textname)); 1814 query->rdtype = dns_rdatatype_ptr; 1815 query->rdclass = dns_rdataclass_in; 1816 *setname = true; 1817 return (value_from_next); 1818 global_option: 1819 fprintf(stderr, "Ignored late global option: -%s\n", option); 1820 usage(); 1821 default: 1822 invalid_option: 1823 fprintf(stderr, "Invalid option: -%s\n", option); 1824 usage(); 1825 } 1826 UNREACHABLE(); 1827 return (false); 1828 } 1829 1830 static struct query * 1831 clone_default_query() { 1832 struct query *query; 1833 1834 query = isc_mem_allocate(mctx, sizeof(struct query)); 1835 memmove(query, &default_query, sizeof(struct query)); 1836 if (default_query.ecs_addr != NULL) { 1837 size_t len = sizeof(isc_sockaddr_t); 1838 1839 query->ecs_addr = isc_mem_allocate(mctx, len); 1840 memmove(query->ecs_addr, default_query.ecs_addr, len); 1841 } 1842 1843 if (query->timeout == 0) { 1844 query->timeout = tcp_mode ? TCPTIMEOUT : UDPTIMEOUT; 1845 } 1846 1847 return (query); 1848 } 1849 1850 /*% 1851 * Because we may be trying to do memory allocation recording, we're going 1852 * to need to parse the arguments for the -m *before* we start the main 1853 * argument parsing routine. 1854 * 1855 * I'd prefer not to have to do this, but I am not quite sure how else to 1856 * fix the problem. Argument parsing in mdig involves memory allocation 1857 * by its nature, so it can't be done in the main argument parser. 1858 */ 1859 static void 1860 preparse_args(int argc, char **argv) { 1861 int rc; 1862 char **rv; 1863 char *option; 1864 bool ipv4only = false, ipv6only = false; 1865 1866 rc = argc; 1867 rv = argv; 1868 for (rc--, rv++; rc > 0; rc--, rv++) { 1869 if (rv[0][0] != '-') { 1870 continue; 1871 } 1872 option = &rv[0][1]; 1873 while (strpbrk(option, single_dash_opts) == &option[0]) { 1874 switch (option[0]) { 1875 case 'm': 1876 isc_mem_debugging = ISC_MEM_DEBUGTRACE | 1877 ISC_MEM_DEBUGRECORD; 1878 break; 1879 case '4': 1880 if (ipv6only) { 1881 fatal("only one of -4 and -6 allowed"); 1882 } 1883 ipv4only = true; 1884 break; 1885 case '6': 1886 if (ipv4only) { 1887 fatal("only one of -4 and -6 allowed"); 1888 } 1889 ipv6only = true; 1890 break; 1891 } 1892 option = &option[1]; 1893 } 1894 if (strlen(option) == 0U) { 1895 continue; 1896 } 1897 /* Look for dash value option. */ 1898 if (strpbrk(option, dash_opts) != &option[0] || 1899 strlen(option) > 1U) 1900 { 1901 /* Error or value in option. */ 1902 continue; 1903 } 1904 /* Dash value is next argument so we need to skip it. */ 1905 rc--, rv++; 1906 /* Handle missing argument */ 1907 if (rc == 0) { 1908 break; 1909 } 1910 } 1911 } 1912 1913 static void 1914 parse_args(bool is_batchfile, int argc, char **argv) { 1915 struct query *query = NULL; 1916 char batchline[MXNAME]; 1917 int bargc; 1918 char *bargv[64]; 1919 int rc; 1920 char **rv; 1921 bool global = true; 1922 char *last; 1923 1924 /* 1925 * The semantics for parsing the args is a bit complex; if 1926 * we don't have a host yet, make the arg apply globally, 1927 * otherwise make it apply to the latest host. This is 1928 * a bit different than the previous versions, but should 1929 * form a consistent user interface. 1930 * 1931 * First, create a "default query" which won't actually be used 1932 * anywhere, except for cloning into new queries 1933 */ 1934 1935 if (!is_batchfile) { 1936 default_query.textname[0] = 0; 1937 default_query.recurse = true; 1938 default_query.have_aaonly = false; 1939 default_query.have_adflag = true; /*XXX*/ 1940 default_query.have_cdflag = false; 1941 default_query.have_zflag = false; 1942 default_query.dnssec = false; 1943 default_query.expire = false; 1944 default_query.send_cookie = false; 1945 default_query.cookie = NULL; 1946 default_query.nsid = false; 1947 default_query.rdtype = dns_rdatatype_a; 1948 default_query.rdclass = dns_rdataclass_in; 1949 default_query.udpsize = 0; 1950 default_query.edns = 0; /*XXX*/ 1951 default_query.ednsopts = NULL; 1952 default_query.ednsoptscnt = 0; 1953 default_query.ednsflags = 0; 1954 default_query.ecs_addr = NULL; 1955 default_query.timeout = 0; 1956 default_query.udptimeout = 0; 1957 default_query.udpretries = 3; 1958 ISC_LINK_INIT(&default_query, link); 1959 } 1960 1961 if (is_batchfile) { 1962 /* Processing '-f batchfile'. */ 1963 query = clone_default_query(); 1964 global = false; 1965 } else { 1966 query = &default_query; 1967 } 1968 1969 rc = argc; 1970 rv = argv; 1971 for (rc--, rv++; rc > 0; rc--, rv++) { 1972 if (strncmp(rv[0], "%", 1) == 0) { 1973 break; 1974 } 1975 if (rv[0][0] == '@') { 1976 if (server != NULL) { 1977 fatal("server already set to @%s", server); 1978 } 1979 server = &rv[0][1]; 1980 } else if (rv[0][0] == '+') { 1981 plus_option(&rv[0][1], query, global); 1982 } else if (rv[0][0] == '-') { 1983 bool setname = false; 1984 1985 if (rc <= 1) { 1986 if (dash_option(&rv[0][1], NULL, query, global, 1987 &setname)) 1988 { 1989 rc--; 1990 rv++; 1991 } 1992 } else { 1993 if (dash_option(&rv[0][1], rv[1], query, global, 1994 &setname)) 1995 { 1996 rc--; 1997 rv++; 1998 } 1999 } 2000 if (setname) { 2001 if (query == &default_query) { 2002 query = clone_default_query(); 2003 } 2004 ISC_LIST_APPEND(queries, query, link); 2005 2006 default_query.textname[0] = 0; 2007 query = clone_default_query(); 2008 global = false; 2009 } 2010 } else { 2011 /* 2012 * Anything which isn't an option 2013 */ 2014 if (query == &default_query) { 2015 query = clone_default_query(); 2016 } 2017 strlcpy(query->textname, rv[0], 2018 sizeof(query->textname)); 2019 ISC_LIST_APPEND(queries, query, link); 2020 2021 query = clone_default_query(); 2022 global = false; 2023 /* XXX Error message */ 2024 } 2025 } 2026 2027 /* 2028 * If we have a batchfile, read the query list from it. 2029 */ 2030 if ((batchname != NULL) && !is_batchfile) { 2031 if (strcmp(batchname, "-") == 0) { 2032 batchfp = stdin; 2033 } else { 2034 batchfp = fopen(batchname, "r"); 2035 } 2036 if (batchfp == NULL) { 2037 perror(batchname); 2038 fatal("couldn't open batch file '%s'", batchname); 2039 } 2040 while (fgets(batchline, sizeof(batchline), batchfp) != 0) { 2041 if (batchline[0] == '\r' || batchline[0] == '\n' || 2042 batchline[0] == '#' || batchline[0] == ';') 2043 { 2044 continue; 2045 } 2046 for (bargc = 1, bargv[bargc] = strtok_r( 2047 batchline, " \t\r\n", &last); 2048 (bargc < 14) && bargv[bargc]; bargc++, 2049 bargv[bargc] = strtok_r(NULL, " \t\r\n", &last)) 2050 { 2051 /* empty body */ 2052 } 2053 2054 bargv[0] = argv[0]; 2055 parse_args(true, bargc, (char **)bargv); 2056 } 2057 if (batchfp != stdin) { 2058 fclose(batchfp); 2059 } 2060 } 2061 if (query != &default_query) { 2062 if (query->ecs_addr != NULL) { 2063 isc_mem_free(mctx, query->ecs_addr); 2064 } 2065 isc_mem_free(mctx, query); 2066 } 2067 } 2068 2069 /* 2070 * Try honoring the operating system's preferred ephemeral port range. 2071 */ 2072 static void 2073 set_source_ports(dns_dispatchmgr_t *manager) { 2074 isc_portset_t *v4portset = NULL, *v6portset = NULL; 2075 in_port_t udpport_low, udpport_high; 2076 isc_result_t result; 2077 2078 result = isc_portset_create(mctx, &v4portset); 2079 if (result != ISC_R_SUCCESS) { 2080 fatal("isc_portset_create (v4) failed"); 2081 } 2082 2083 result = isc_net_getudpportrange(AF_INET, &udpport_low, &udpport_high); 2084 if (result != ISC_R_SUCCESS) { 2085 fatal("isc_net_getudpportrange (v4) failed"); 2086 } 2087 2088 isc_portset_addrange(v4portset, udpport_low, udpport_high); 2089 2090 result = isc_portset_create(mctx, &v6portset); 2091 if (result != ISC_R_SUCCESS) { 2092 fatal("isc_portset_create (v6) failed"); 2093 } 2094 result = isc_net_getudpportrange(AF_INET6, &udpport_low, &udpport_high); 2095 if (result != ISC_R_SUCCESS) { 2096 fatal("isc_net_getudpportrange (v6) failed"); 2097 } 2098 2099 isc_portset_addrange(v6portset, udpport_low, udpport_high); 2100 2101 result = dns_dispatchmgr_setavailports(manager, v4portset, v6portset); 2102 if (result != ISC_R_SUCCESS) { 2103 fatal("dns_dispatchmgr_setavailports failed"); 2104 } 2105 2106 isc_portset_destroy(mctx, &v4portset); 2107 isc_portset_destroy(mctx, &v6portset); 2108 } 2109 2110 /*% Main processing routine for mdig */ 2111 int 2112 main(int argc, char *argv[]) { 2113 struct query *query = NULL; 2114 isc_result_t result; 2115 isc_sockaddr_t bind_any; 2116 isc_log_t *lctx = NULL; 2117 isc_logconfig_t *lcfg = NULL; 2118 isc_nm_t *netmgr = NULL; 2119 isc_taskmgr_t *taskmgr = NULL; 2120 isc_task_t *task = NULL; 2121 isc_timermgr_t *timermgr = NULL; 2122 isc_socketmgr_t *socketmgr = NULL; 2123 dns_dispatchmgr_t *dispatchmgr = NULL; 2124 unsigned int attrs, attrmask; 2125 dns_dispatch_t *dispatchvx = NULL; 2126 dns_view_t *view = NULL; 2127 int ns; 2128 unsigned int i; 2129 2130 RUNCHECK(isc_app_start()); 2131 2132 dns_result_register(); 2133 2134 if (isc_net_probeipv4() == ISC_R_SUCCESS) { 2135 have_ipv4 = true; 2136 } 2137 if (isc_net_probeipv6() == ISC_R_SUCCESS) { 2138 have_ipv6 = true; 2139 } 2140 if (!have_ipv4 && !have_ipv6) { 2141 fatal("could not find either IPv4 or IPv6"); 2142 } 2143 2144 preparse_args(argc, argv); 2145 2146 isc_mem_create(&mctx); 2147 2148 isc_log_create(mctx, &lctx, &lcfg); 2149 2150 RUNCHECK(dst_lib_init(mctx, NULL)); 2151 isc_nonce_buf(cookie_secret, sizeof(cookie_secret)); 2152 2153 ISC_LIST_INIT(queries); 2154 parse_args(false, argc, argv); 2155 if (server == NULL) { 2156 fatal("a server '@xxx' is required"); 2157 } 2158 2159 ns = 0; 2160 result = bind9_getaddresses(server, port, &dstaddr, 1, &ns); 2161 if (result != ISC_R_SUCCESS) { 2162 fatal("couldn't get address for '%s': %s", server, 2163 isc_result_totext(result)); 2164 } 2165 2166 if (isc_sockaddr_pf(&dstaddr) == PF_INET && have_ipv6) { 2167 isc_net_disableipv6(); 2168 have_ipv6 = false; 2169 } else if (isc_sockaddr_pf(&dstaddr) == PF_INET6 && have_ipv4) { 2170 isc_net_disableipv4(); 2171 have_ipv4 = false; 2172 } 2173 if (have_ipv4 && have_ipv6) { 2174 fatal("can't choose between IPv4 and IPv6"); 2175 } 2176 2177 RUNCHECK(isc_managers_create(mctx, 1, 0, &netmgr, &taskmgr)); 2178 RUNCHECK(isc_task_create(taskmgr, 0, &task)); 2179 RUNCHECK(isc_timermgr_create(mctx, &timermgr)); 2180 RUNCHECK(isc_socketmgr_create(mctx, &socketmgr)); 2181 RUNCHECK(dns_dispatchmgr_create(mctx, &dispatchmgr)); 2182 2183 set_source_ports(dispatchmgr); 2184 2185 attrs = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_MAKEQUERY; 2186 2187 if (have_ipv4) { 2188 isc_sockaddr_any(&bind_any); 2189 attrs |= DNS_DISPATCHATTR_IPV4; 2190 } else { 2191 isc_sockaddr_any6(&bind_any); 2192 attrs |= DNS_DISPATCHATTR_IPV6; 2193 } 2194 attrmask = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP | 2195 DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6; 2196 RUNCHECK(dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr, 2197 have_src ? &srcaddr : &bind_any, 4096, 100, 2198 100, 17, 19, attrs, attrmask, 2199 &dispatchvx)); 2200 RUNCHECK(dns_requestmgr_create( 2201 mctx, timermgr, socketmgr, taskmgr, dispatchmgr, 2202 have_ipv4 ? dispatchvx : NULL, have_ipv6 ? dispatchvx : NULL, 2203 &requestmgr)); 2204 2205 RUNCHECK(dns_view_create(mctx, 0, "_test", &view)); 2206 2207 query = ISC_LIST_HEAD(queries); 2208 RUNCHECK(isc_app_onrun(mctx, task, sendqueries, query)); 2209 2210 /* 2211 * Stall to the start of a new second. 2212 */ 2213 if (burst) { 2214 isc_time_t start, now; 2215 RUNCHECK(isc_time_now(&start)); 2216 /* 2217 * Sleep to 1ms of the end of the second then run a busy loop 2218 * until the second changes. 2219 */ 2220 do { 2221 RUNCHECK(isc_time_now(&now)); 2222 if (isc_time_seconds(&start) == isc_time_seconds(&now)) 2223 { 2224 int us = US_PER_SEC - 2225 (isc_time_nanoseconds(&now) / 2226 NS_PER_US); 2227 if (us > US_PER_MS) { 2228 usleep(us - US_PER_MS); 2229 } 2230 } else { 2231 break; 2232 } 2233 } while (1); 2234 } 2235 2236 (void)isc_app_run(); 2237 2238 dns_view_detach(&view); 2239 2240 dns_requestmgr_shutdown(requestmgr); 2241 dns_requestmgr_detach(&requestmgr); 2242 2243 dns_dispatch_detach(&dispatchvx); 2244 dns_dispatchmgr_destroy(&dispatchmgr); 2245 2246 isc_socketmgr_destroy(&socketmgr); 2247 isc_timermgr_destroy(&timermgr); 2248 2249 isc_task_shutdown(task); 2250 isc_task_detach(&task); 2251 isc_managers_destroy(&netmgr, &taskmgr); 2252 2253 dst_lib_destroy(); 2254 2255 isc_log_destroy(&lctx); 2256 2257 query = ISC_LIST_HEAD(queries); 2258 while (query != NULL) { 2259 struct query *next = ISC_LIST_NEXT(query, link); 2260 2261 if (query->ednsopts != NULL) { 2262 for (i = 0; i < EDNSOPTS; i++) { 2263 if (query->ednsopts[i].value != NULL) { 2264 isc_mem_free(mctx, 2265 query->ednsopts[i].value); 2266 } 2267 } 2268 isc_mem_free(mctx, query->ednsopts); 2269 } 2270 if (query->ecs_addr != NULL) { 2271 isc_mem_free(mctx, query->ecs_addr); 2272 query->ecs_addr = NULL; 2273 } 2274 isc_mem_free(mctx, query); 2275 query = next; 2276 } 2277 2278 if (default_query.ecs_addr != NULL) { 2279 isc_mem_free(mctx, default_query.ecs_addr); 2280 } 2281 2282 isc_mem_destroy(&mctx); 2283 2284 isc_app_finish(); 2285 2286 return (0); 2287 } 2288