"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.17.5/bin/tools/mdig.c" (4 Sep 2020, 55762 Bytes) of package /linux/misc/dns/bind9/9.17.5/bind-9.17.5.tar.xz:


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