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