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