1 /* $NetBSD: mdig.c,v 1.16 2026/04/08 00:16:13 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 CHECKM(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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("dns_message_firstname", result); 448 } 449 450 for (;;) { 451 if (result == ISC_R_NOMORE) { 452 break; 453 } 454 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("dns_message_buildopt", result); 567 result = dns_message_setopt(msg, rdataset); 568 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("reverse_octets", result); 1071 result = append(".", 1, p, end); 1072 CHECKM("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 CHECKM("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 CHECKM("reverse_octets", result); 1110 /* Append .in-addr.arpa. and a terminating NUL. */ 1111 result = append(".in-addr.arpa.", 15, &p, end); 1112 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 CHECKM("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 = 1; 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, *oldvalue; 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 const 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 oldvalue = value; 1732 if (hash != NULL) { 1733 result = parse_uint(&num, hash + 1, MAXPORT, 1734 "port number"); 1735 CHECKM("parse_uint(srcport)", result); 1736 srcport = num; 1737 snprintf(textname, sizeof(textname), "%.*s", 1738 (int)(hash - value), value); 1739 value = textname; 1740 } else { 1741 srcport = 0; 1742 } 1743 if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) { 1744 isc_sockaddr_fromin6(&srcaddr, &in6, srcport); 1745 isc_net_disableipv4(); 1746 } else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) { 1747 isc_sockaddr_fromin(&srcaddr, &in4, srcport); 1748 isc_net_disableipv6(); 1749 } else { 1750 fatal("invalid address %s", oldvalue); 1751 } 1752 have_src = true; 1753 return value_from_next; 1754 case 'c': 1755 tr.base = value; 1756 tr.length = strlen(value); 1757 result = dns_rdataclass_fromtext(&rdclass, 1758 (isc_textregion_t *)&tr); 1759 CHECKM("dns_rdataclass_fromtext", result); 1760 query->rdclass = rdclass; 1761 return value_from_next; 1762 case 'f': 1763 batchname = value; 1764 return value_from_next; 1765 case 'p': 1766 GLOBAL(); 1767 result = parse_uint(&num, value, MAXPORT, "port number"); 1768 CHECKM("parse_uint(port)", result); 1769 port = num; 1770 return value_from_next; 1771 case 't': 1772 tr.base = value; 1773 tr.length = strlen(value); 1774 result = dns_rdatatype_fromtext(&rdtype, 1775 (isc_textregion_t *)&tr); 1776 CHECKM("dns_rdatatype_fromtext", result); 1777 query->rdtype = rdtype; 1778 return value_from_next; 1779 case 'x': 1780 get_reverse(textname, sizeof(textname), value); 1781 strlcpy(query->textname, textname, sizeof(query->textname)); 1782 query->rdtype = dns_rdatatype_ptr; 1783 query->rdclass = dns_rdataclass_in; 1784 *setname = true; 1785 return value_from_next; 1786 global_option: 1787 fprintf(stderr, "Ignored late global option: -%s\n", option); 1788 usage(); 1789 default: 1790 invalid_option: 1791 fprintf(stderr, "Invalid option: -%s\n", option); 1792 usage(); 1793 } 1794 UNREACHABLE(); 1795 return false; 1796 } 1797 1798 static struct query * 1799 clone_default_query(void) { 1800 struct query *query; 1801 1802 query = isc_mem_allocate(mctx, sizeof(struct query)); 1803 memmove(query, &default_query, sizeof(struct query)); 1804 if (default_query.ecs_addr != NULL) { 1805 size_t len = sizeof(isc_sockaddr_t); 1806 1807 query->ecs_addr = isc_mem_allocate(mctx, len); 1808 memmove(query->ecs_addr, default_query.ecs_addr, len); 1809 } 1810 1811 if (query->timeout == 0) { 1812 query->timeout = tcp_mode ? TCPTIMEOUT : UDPTIMEOUT; 1813 } 1814 1815 return query; 1816 } 1817 1818 /*% 1819 * Because we may be trying to do memory allocation recording, we're going 1820 * to need to parse the arguments for the -m *before* we start the main 1821 * argument parsing routine. 1822 * 1823 * I'd prefer not to have to do this, but I am not quite sure how else to 1824 * fix the problem. Argument parsing in mdig involves memory allocation 1825 * by its nature, so it can't be done in the main argument parser. 1826 */ 1827 static void 1828 preparse_args(int argc, char **argv) { 1829 int rc; 1830 char **rv; 1831 char *option; 1832 bool ipv4only = false, ipv6only = false; 1833 1834 rc = argc; 1835 rv = argv; 1836 for (rc--, rv++; rc > 0; rc--, rv++) { 1837 if (rv[0][0] != '-') { 1838 continue; 1839 } 1840 option = &rv[0][1]; 1841 while (strpbrk(option, single_dash_opts) == &option[0]) { 1842 switch (option[0]) { 1843 case 'm': 1844 isc_mem_debugging = ISC_MEM_DEBUGTRACE | 1845 ISC_MEM_DEBUGRECORD; 1846 break; 1847 case '4': 1848 if (ipv6only) { 1849 fatal("only one of -4 and -6 allowed"); 1850 } 1851 ipv4only = true; 1852 break; 1853 case '6': 1854 if (ipv4only) { 1855 fatal("only one of -4 and -6 allowed"); 1856 } 1857 ipv6only = true; 1858 break; 1859 } 1860 option = &option[1]; 1861 } 1862 if (strlen(option) == 0U) { 1863 continue; 1864 } 1865 /* Look for dash value option. */ 1866 if (strpbrk(option, dash_opts) != &option[0] || 1867 strlen(option) > 1U) 1868 { 1869 /* Error or value in option. */ 1870 continue; 1871 } 1872 /* Dash value is next argument so we need to skip it. */ 1873 rc--, rv++; 1874 /* Handle missing argument */ 1875 if (rc == 0) { 1876 break; 1877 } 1878 } 1879 } 1880 1881 static void 1882 parse_args(bool is_batchfile, int argc, char **argv) { 1883 struct query *query = NULL; 1884 char batchline[MXNAME]; 1885 int bargc; 1886 char *bargv[64]; 1887 int rc; 1888 char **rv; 1889 bool global = true; 1890 char *last; 1891 1892 /* 1893 * The semantics for parsing the args is a bit complex; if 1894 * we don't have a host yet, make the arg apply globally, 1895 * otherwise make it apply to the latest host. This is 1896 * a bit different than the previous versions, but should 1897 * form a consistent user interface. 1898 * 1899 * First, create a "default query" which won't actually be used 1900 * anywhere, except for cloning into new queries 1901 */ 1902 1903 if (!is_batchfile) { 1904 default_query.textname[0] = 0; 1905 default_query.recurse = true; 1906 default_query.have_aaonly = false; 1907 default_query.have_adflag = true; /*XXX*/ 1908 default_query.have_cdflag = false; 1909 default_query.have_zflag = false; 1910 default_query.dnssec = false; 1911 default_query.expire = false; 1912 default_query.send_cookie = false; 1913 default_query.cookie = NULL; 1914 default_query.nsid = false; 1915 default_query.rdtype = dns_rdatatype_a; 1916 default_query.rdclass = dns_rdataclass_in; 1917 default_query.udpsize = 0; 1918 default_query.edns = 0; /*XXX*/ 1919 default_query.ednsopts = NULL; 1920 default_query.ednsoptscnt = 0; 1921 default_query.ednsflags = 0; 1922 default_query.ecs_addr = NULL; 1923 default_query.timeout = 0; 1924 default_query.udptimeout = 0; 1925 default_query.udpretries = 2; 1926 ISC_LINK_INIT(&default_query, link); 1927 } 1928 1929 if (is_batchfile) { 1930 /* Processing '-f batchfile'. */ 1931 query = clone_default_query(); 1932 global = false; 1933 } else { 1934 query = &default_query; 1935 } 1936 1937 rc = argc; 1938 rv = argv; 1939 for (rc--, rv++; rc > 0; rc--, rv++) { 1940 if (strncmp(rv[0], "%", 1) == 0) { 1941 break; 1942 } 1943 if (rv[0][0] == '@') { 1944 if (server != NULL) { 1945 fatal("server already set to @%s", server); 1946 } 1947 server = &rv[0][1]; 1948 } else if (rv[0][0] == '+') { 1949 plus_option(&rv[0][1], query, global); 1950 } else if (rv[0][0] == '-') { 1951 bool setname = false; 1952 1953 if (rc <= 1) { 1954 if (dash_option(&rv[0][1], NULL, query, global, 1955 &setname)) 1956 { 1957 rc--; 1958 rv++; 1959 } 1960 } else { 1961 if (dash_option(&rv[0][1], rv[1], query, global, 1962 &setname)) 1963 { 1964 rc--; 1965 rv++; 1966 } 1967 } 1968 if (setname) { 1969 if (query == &default_query) { 1970 query = clone_default_query(); 1971 } 1972 ISC_LIST_APPEND(queries, query, link); 1973 1974 default_query.textname[0] = 0; 1975 query = clone_default_query(); 1976 global = false; 1977 } 1978 } else { 1979 /* 1980 * Anything which isn't an option 1981 */ 1982 if (query == &default_query) { 1983 query = clone_default_query(); 1984 } 1985 strlcpy(query->textname, rv[0], 1986 sizeof(query->textname)); 1987 ISC_LIST_APPEND(queries, query, link); 1988 1989 query = clone_default_query(); 1990 global = false; 1991 /* XXX Error message */ 1992 } 1993 } 1994 1995 /* 1996 * If we have a batchfile, read the query list from it. 1997 */ 1998 if ((batchname != NULL) && !is_batchfile) { 1999 if (strcmp(batchname, "-") == 0) { 2000 batchfp = stdin; 2001 } else { 2002 batchfp = fopen(batchname, "r"); 2003 } 2004 if (batchfp == NULL) { 2005 perror(batchname); 2006 fatal("couldn't open batch file '%s'", batchname); 2007 } 2008 while (fgets(batchline, sizeof(batchline), batchfp) != 0) { 2009 if (batchline[0] == '\r' || batchline[0] == '\n' || 2010 batchline[0] == '#' || batchline[0] == ';') 2011 { 2012 continue; 2013 } 2014 for (bargc = 1, bargv[bargc] = strtok_r( 2015 batchline, " \t\r\n", &last); 2016 (bargc < 14) && bargv[bargc]; bargc++, 2017 bargv[bargc] = strtok_r(NULL, " \t\r\n", &last)) 2018 { 2019 /* empty body */ 2020 } 2021 2022 bargv[0] = argv[0]; 2023 parse_args(true, bargc, (char **)bargv); 2024 } 2025 if (batchfp != stdin) { 2026 fclose(batchfp); 2027 } 2028 } 2029 if (query != &default_query) { 2030 if (query->ecs_addr != NULL) { 2031 isc_mem_free(mctx, query->ecs_addr); 2032 } 2033 isc_mem_free(mctx, query); 2034 } 2035 } 2036 2037 /* 2038 * Try honoring the operating system's preferred ephemeral port range. 2039 */ 2040 static void 2041 set_source_ports(dns_dispatchmgr_t *manager) { 2042 isc_portset_t *v4portset = NULL, *v6portset = NULL; 2043 in_port_t udpport_low, udpport_high; 2044 isc_result_t result; 2045 2046 result = isc_portset_create(mctx, &v4portset); 2047 if (result != ISC_R_SUCCESS) { 2048 fatal("isc_portset_create (v4) failed"); 2049 } 2050 2051 isc_net_getportrange(AF_INET, &udpport_low, &udpport_high); 2052 2053 isc_portset_addrange(v4portset, udpport_low, udpport_high); 2054 2055 result = isc_portset_create(mctx, &v6portset); 2056 if (result != ISC_R_SUCCESS) { 2057 fatal("isc_portset_create (v6) failed"); 2058 } 2059 isc_net_getportrange(AF_INET6, &udpport_low, &udpport_high); 2060 2061 isc_portset_addrange(v6portset, udpport_low, udpport_high); 2062 2063 result = dns_dispatchmgr_setavailports(manager, v4portset, v6portset); 2064 if (result != ISC_R_SUCCESS) { 2065 fatal("dns_dispatchmgr_setavailports failed"); 2066 } 2067 2068 isc_portset_destroy(mctx, &v4portset); 2069 isc_portset_destroy(mctx, &v6portset); 2070 } 2071 2072 static void 2073 teardown(void *arg ISC_ATTR_UNUSED) { 2074 dns_view_detach(&view); 2075 dns_requestmgr_shutdown(requestmgr); 2076 dns_requestmgr_detach(&requestmgr); 2077 dns_dispatch_detach(&dispatchvx); 2078 dns_dispatchmgr_detach(&dispatchmgr); 2079 } 2080 2081 static void 2082 setup(void *arg ISC_ATTR_UNUSED) { 2083 RUNCHECK(dns_dispatchmgr_create(mctx, loopmgr, netmgr, &dispatchmgr)); 2084 2085 set_source_ports(dispatchmgr); 2086 2087 if (have_ipv4) { 2088 isc_sockaddr_any(&bind_any); 2089 } else { 2090 isc_sockaddr_any6(&bind_any); 2091 } 2092 RUNCHECK(dns_dispatch_createudp( 2093 dispatchmgr, have_src ? &srcaddr : &bind_any, &dispatchvx)); 2094 2095 RUNCHECK(dns_requestmgr_create( 2096 mctx, loopmgr, dispatchmgr, have_ipv4 ? dispatchvx : NULL, 2097 have_ipv6 ? dispatchvx : NULL, &requestmgr)); 2098 2099 RUNCHECK(dns_view_create(mctx, loopmgr, NULL, 0, "_mdig", &view)); 2100 } 2101 2102 /*% Main processing routine for mdig */ 2103 int 2104 main(int argc, char *argv[]) { 2105 struct query *query = NULL; 2106 isc_result_t result; 2107 isc_log_t *lctx = NULL; 2108 isc_logconfig_t *lcfg = NULL; 2109 unsigned int i; 2110 int ns; 2111 2112 if (isc_net_probeipv4() == ISC_R_SUCCESS) { 2113 have_ipv4 = true; 2114 } 2115 if (isc_net_probeipv6() == ISC_R_SUCCESS) { 2116 have_ipv6 = true; 2117 } 2118 if (!have_ipv4 && !have_ipv6) { 2119 fatal("could not find either IPv4 or IPv6"); 2120 } 2121 2122 preparse_args(argc, argv); 2123 2124 isc_managers_create(&mctx, 1, &loopmgr, &netmgr); 2125 isc_log_create(mctx, &lctx, &lcfg); 2126 2127 RUNCHECK(dst_lib_init(mctx, NULL)); 2128 isc_nonce_buf(cookie_secret, sizeof(cookie_secret)); 2129 2130 ISC_LIST_INIT(queries); 2131 parse_args(false, argc, argv); 2132 if (server == NULL) { 2133 fatal("a server '@xxx' is required"); 2134 } 2135 2136 ns = 0; 2137 result = isc_getaddresses(server, port, &dstaddr, 1, &ns); 2138 if (result != ISC_R_SUCCESS) { 2139 fatal("couldn't get address for '%s': %s", server, 2140 isc_result_totext(result)); 2141 } 2142 2143 if (isc_sockaddr_pf(&dstaddr) == PF_INET && have_ipv6) { 2144 isc_net_disableipv6(); 2145 have_ipv6 = false; 2146 } else if (isc_sockaddr_pf(&dstaddr) == PF_INET6 && have_ipv4) { 2147 isc_net_disableipv4(); 2148 have_ipv4 = false; 2149 } 2150 if (have_ipv4 && have_ipv6) { 2151 fatal("can't choose between IPv4 and IPv6"); 2152 } 2153 2154 query = ISC_LIST_HEAD(queries); 2155 isc_loopmgr_setup(loopmgr, setup, NULL); 2156 isc_loopmgr_setup(loopmgr, sendqueries, query); 2157 isc_loopmgr_teardown(loopmgr, teardown, NULL); 2158 2159 /* 2160 * Stall to the start of a new second. 2161 */ 2162 if (burst) { 2163 isc_time_t start, now; 2164 start = isc_time_now(); 2165 /* 2166 * Sleep to 1ms of the end of the second then run a busy loop 2167 * until the second changes. 2168 */ 2169 do { 2170 now = isc_time_now(); 2171 if (isc_time_seconds(&start) == isc_time_seconds(&now)) 2172 { 2173 unsigned int us = US_PER_SEC - 2174 (isc_time_nanoseconds(&now) / 2175 NS_PER_US); 2176 if (us > US_PER_MS) { 2177 usleep(us - US_PER_MS); 2178 } 2179 } else { 2180 break; 2181 } 2182 } while (1); 2183 } 2184 2185 isc_loopmgr_run(loopmgr); 2186 2187 dst_lib_destroy(); 2188 2189 isc_log_destroy(&lctx); 2190 2191 query = ISC_LIST_HEAD(queries); 2192 while (query != NULL) { 2193 struct query *next = ISC_LIST_NEXT(query, link); 2194 2195 if (query->ednsopts != NULL) { 2196 for (i = 0; i < EDNSOPTS; i++) { 2197 if (query->ednsopts[i].value != NULL) { 2198 isc_mem_free(mctx, 2199 query->ednsopts[i].value); 2200 } 2201 } 2202 isc_mem_free(mctx, query->ednsopts); 2203 } 2204 if (query->ecs_addr != NULL) { 2205 isc_mem_free(mctx, query->ecs_addr); 2206 query->ecs_addr = NULL; 2207 } 2208 isc_mem_free(mctx, query); 2209 query = next; 2210 } 2211 2212 if (default_query.ecs_addr != NULL) { 2213 isc_mem_free(mctx, default_query.ecs_addr); 2214 } 2215 2216 isc_managers_destroy(&mctx, &loopmgr, &netmgr); 2217 return 0; 2218 } 2219