"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.11.23/bin/tools/mdig.c" (7 Sep 2020, 52992 Bytes) of package /linux/misc/dns/bind9/9.11.23/bind-9.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mdig.c" see the Fossies "Dox" file reference documentation.

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