diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
commit | 45d6379135504814ab723b57f0eb8be23393a51d (patch) | |
tree | d4f2ec4acca824a8446387a758b0ce4238a4dffa /bin/dig | |
parent | Initial commit. (diff) | |
download | bind9-upstream/1%9.16.44.tar.xz bind9-upstream/1%9.16.44.zip |
Adding upstream version 1:9.16.44.upstream/1%9.16.44upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bin/dig')
-rw-r--r-- | bin/dig/Makefile.in | 98 | ||||
-rw-r--r-- | bin/dig/dig.c | 2728 | ||||
-rw-r--r-- | bin/dig/dig.rst | 645 | ||||
-rw-r--r-- | bin/dig/dighost.c | 4604 | ||||
-rw-r--r-- | bin/dig/host.c | 927 | ||||
-rw-r--r-- | bin/dig/host.rst | 171 | ||||
l--------- | bin/dig/include/.clang-format | 1 | ||||
-rw-r--r-- | bin/dig/include/dig/dig.h | 425 | ||||
-rw-r--r-- | bin/dig/nslookup.c | 1047 | ||||
-rw-r--r-- | bin/dig/nslookup.rst | 206 | ||||
-rw-r--r-- | bin/dig/win32/dig.vcxproj.filters.in | 27 | ||||
-rw-r--r-- | bin/dig/win32/dig.vcxproj.in | 122 | ||||
-rw-r--r-- | bin/dig/win32/dig.vcxproj.user | 3 | ||||
-rw-r--r-- | bin/dig/win32/dighost.vcxproj.filters.in | 18 | ||||
-rw-r--r-- | bin/dig/win32/dighost.vcxproj.in | 115 | ||||
-rw-r--r-- | bin/dig/win32/dighost.vcxproj.user | 3 | ||||
-rw-r--r-- | bin/dig/win32/host.vcxproj.filters.in | 18 | ||||
-rw-r--r-- | bin/dig/win32/host.vcxproj.in | 119 | ||||
-rw-r--r-- | bin/dig/win32/host.vcxproj.user | 3 | ||||
-rw-r--r-- | bin/dig/win32/nslookup.vcxproj.filters.in | 21 | ||||
-rw-r--r-- | bin/dig/win32/nslookup.vcxproj.in | 120 | ||||
-rw-r--r-- | bin/dig/win32/nslookup.vcxproj.user | 3 |
22 files changed, 11424 insertions, 0 deletions
diff --git a/bin/dig/Makefile.in b/bin/dig/Makefile.in new file mode 100644 index 0000000..c329502 --- /dev/null +++ b/bin/dig/Makefile.in @@ -0,0 +1,98 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +VERSION=@BIND9_VERSION@ + +@BIND9_MAKE_INCLUDES@ + +READLINE_LIB = @READLINE_LIB@ + +CINCLUDES = -I${srcdir}/include ${DNS_INCLUDES} \ + ${BIND9_INCLUDES} ${ISC_INCLUDES} \ + ${IRS_INCLUDES} ${ISCCFG_INCLUDES} @LIBIDN2_CFLAGS@ \ + ${OPENSSL_CFLAGS} + +CDEFINES = -DVERSION=\"${VERSION}\" +CWARNINGS = + +ISCCFGLIBS = ../../lib/isccfg/libisccfg.@A@ +DNSLIBS = ../../lib/dns/libdns.@A@ @NO_LIBTOOL_DNSLIBS@ +BIND9LIBS = ../../lib/bind9/libbind9.@A@ +ISCLIBS = ../../lib/isc/libisc.@A@ @NO_LIBTOOL_ISCLIBS@ +ISCNOSYMLIBS = ../../lib/isc/libisc-nosymtbl.@A@ @NO_LIBTOOL_ISCLIBS@ +IRSLIBS = ../../lib/irs/libirs.@A@ + +ISCCFGDEPLIBS = ../../lib/isccfg/libisccfg.@A@ +DNSDEPLIBS = ../../lib/dns/libdns.@A@ +BIND9DEPLIBS = ../../lib/bind9/libbind9.@A@ +ISCDEPLIBS = ../../lib/isc/libisc.@A@ +IRSDEPLIBS = ../../lib/irs/libirs.@A@ + +DEPLIBS = ${DNSDEPLIBS} ${IRSDEPLIBS} ${BIND9DEPLIBS} \ + ${ISCDEPLIBS} ${ISCCFGDEPLIBS} + +LIBS = ${DNSLIBS} ${IRSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \ + ${ISCLIBS} @LIBIDN2_LIBS@ @LIBS@ + +NOSYMLIBS = ${DNSLIBS} ${IRSLIBS} ${BIND9LIBS} ${ISCCFGLIBS} \ + ${ISCNOSYMLIBS} @LIBIDN2_LIBS@ @LIBS@ + +SUBDIRS = + +TARGETS = dig@EXEEXT@ host@EXEEXT@ nslookup@EXEEXT@ + +OBJS = dig.@O@ dighost.@O@ host.@O@ nslookup.@O@ + +UOBJS = + +SRCS = dig.c dighost.c host.c nslookup.c + +@BIND9_MAKE_RULES@ + +LDFLAGS = @LDFLAGS@ @LIBIDN2_LDFLAGS@ + +dig@EXEEXT@: dig.@O@ dighost.@O@ ${UOBJS} ${DEPLIBS} + export BASEOBJS="dig.@O@ dighost.@O@ ${UOBJS}"; \ + export LIBS0="${DNSLIBS} ${IRSLIBS}"; \ + ${FINALBUILDCMD} + +host@EXEEXT@: host.@O@ dighost.@O@ ${UOBJS} ${DEPLIBS} + export BASEOBJS="host.@O@ dighost.@O@ ${UOBJS}"; \ + export LIBS0="${DNSLIBS} ${IRSLIBS}"; \ + ${FINALBUILDCMD} + +nslookup@EXEEXT@: nslookup.@O@ dighost.@O@ ${UOBJS} ${DEPLIBS} + export BASEOBJS="nslookup.@O@ dighost.@O@ ${READLINE_LIB} ${UOBJS}"; \ + export LIBS0="${DNSLIBS} ${IRSLIBS}"; \ + ${FINALBUILDCMD} + +clean distclean maintainer-clean:: + rm -f ${TARGETS} + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${bindir} + +install:: dig@EXEEXT@ host@EXEEXT@ nslookup@EXEEXT@ installdirs + ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} \ + dig@EXEEXT@ ${DESTDIR}${bindir} + ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} \ + host@EXEEXT@ ${DESTDIR}${bindir} + ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} \ + nslookup@EXEEXT@ ${DESTDIR}${bindir} + +uninstall:: + ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${bindir}/nslookup@EXEEXT@ + ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${bindir}/host@EXEEXT@ + ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${bindir}/dig@EXEEXT@ diff --git a/bin/dig/dig.c b/bin/dig/dig.c new file mode 100644 index 0000000..423bf7d --- /dev/null +++ b/bin/dig/dig.c @@ -0,0 +1,2728 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <ctype.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <time.h> + +#include <isc/app.h> +#include <isc/netaddr.h> +#include <isc/parseint.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/util.h> + +#include <pk11/site.h> + +#include <dns/byaddr.h> +#include <dns/fixedname.h> +#include <dns/masterdump.h> +#include <dns/message.h> +#include <dns/name.h> +#include <dns/rcode.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdataset.h> +#include <dns/rdatatype.h> +#include <dns/result.h> +#include <dns/tsig.h> + +#include <dig/dig.h> + +#define ADD_STRING(b, s) \ + { \ + if (strlen(s) >= isc_buffer_availablelength(b)) { \ + return (ISC_R_NOSPACE); \ + } else { \ + isc_buffer_putstr(b, s); \ + } \ + } + +#define DIG_MAX_ADDRESSES 20 + +dig_lookup_t *default_lookup = NULL; + +static atomic_uintptr_t batchname = 0; +static FILE *batchfp = NULL; +static char *argv0; +static int addresscount = 0; + +static char domainopt[DNS_NAME_MAXTEXT]; +static char hexcookie[81]; + +static bool short_form = false, printcmd = true, plusquest = false, + pluscomm = false, ipv4only = false, ipv6only = false, digrc = true; +static uint32_t splitwidth = 0xffffffff; + +/*% opcode text */ +static const char *const opcodetext[] = { + "QUERY", "IQUERY", "STATUS", "RESERVED3", + "NOTIFY", "UPDATE", "RESERVED6", "RESERVED7", + "RESERVED8", "RESERVED9", "RESERVED10", "RESERVED11", + "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15" +}; + +static const char * +rcode_totext(dns_rcode_t rcode) { + static char buf[64]; + isc_buffer_t b; + isc_result_t result; + + memset(buf, 0, sizeof(buf)); + isc_buffer_init(&b, buf + 1, sizeof(buf) - 2); + result = dns_rcode_totext(rcode, &b); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (strspn(buf + 1, "0123456789") == strlen(buf + 1)) { + buf[0] = '?'; + return (buf); + } + return (buf + 1); +} + +/*% print usage */ +static void +print_usage(FILE *fp) { + fputs("Usage: dig [@global-server] [domain] [q-type] [q-class] " + "{q-opt}\n" + " {global-d-opt} host [@local-server] {local-d-opt}\n" + " [ host [@local-server] {local-d-opt} [...]]\n", + fp); +} + +#if TARGET_OS_IPHONE +static void +usage(void) { + fprintf(stderr, "Press <Help> for complete list of options\n"); +} +#else /* if TARGET_OS_IPHONE */ +ISC_PLATFORM_NORETURN_PRE static void +usage(void) ISC_PLATFORM_NORETURN_POST; + +static void +usage(void) { + print_usage(stderr); + fputs("\nUse \"dig -h\" (or \"dig -h | more\") " + "for complete list of options\n", + stderr); + exit(1); +} +#endif /* if TARGET_OS_IPHONE */ + +/*% version */ +static void +version(void) { + fputs("DiG " VERSION "\n", stderr); +} + +/*% help */ +static void +help(void) { + print_usage(stdout); + fputs("Where: domain is in the Domain Name System\n" + " q-class is one of (in,hs,ch,...) [default: in]\n" + " q-type is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) " + "[default:a]\n" + " (Use ixfr=version for type ixfr)\n" + " q-opt is one of:\n" + " -4 (use IPv4 query transport " + "only)\n" + " -6 (use IPv6 query transport " + "only)\n" + " -b address[#port] (bind to source " + "address/port)\n" + " -c class (specify query class)\n" + " -f filename (batch mode)\n" + " -k keyfile (specify tsig key file)\n" + " -m (enable memory usage " + "debugging)\n" + " -p port (specify port number)\n" + " -q name (specify query name)\n" + " -r (do not read ~/.digrc)\n" + " -t type (specify query type)\n" + " -u (display times in usec " + "instead of msec)\n" + " -x dot-notation (shortcut for reverse " + "lookups)\n" + " -y [hmac:]name:key (specify named base64 tsig " + "key)\n" + " d-opt is of the form +keyword[=value], where keyword " + "is:\n" + " +[no]aaflag (Set AA flag in query " + "(+[no]aaflag))\n" + " +[no]aaonly (Set AA flag in query " + "(+[no]aaflag))\n" + " +[no]additional (Control display of " + "additional section)\n" + " +[no]adflag (Set AD flag in query " + "(default on))\n" + " +[no]all (Set or clear all display " + "flags)\n" + " +[no]answer (Control display of answer " + "section)\n" + " +[no]authority (Control display of " + "authority section)\n" + " +[no]badcookie (Retry BADCOOKIE " + "responses)\n" + " +[no]besteffort (Try to parse even illegal " + "messages)\n" + " +bufsize[=###] (Set EDNS0 Max UDP packet " + "size)\n" + " +[no]cdflag (Set checking disabled " + "flag in query)\n" + " +[no]class (Control display of class " + "in records)\n" + " +[no]cmd (Control display of " + "command line -\n" + " global option)\n" + " +[no]comments (Control display of packet " + "header\n" + " and section name " + "comments)\n" + " +[no]cookie (Add a COOKIE option to " + "the request)\n" + " +[no]crypto (Control display of " + "cryptographic\n" + " fields in records)\n" + " +[no]defname (Use search list " + "(+[no]search))\n" + " +[no]dnssec (Request DNSSEC records)\n" + " +domain=### (Set default domainname)\n" + " +[no]dscp[=###] (Set the DSCP value to ### " + "[0..63])\n" + " +[no]edns[=###] (Set EDNS version) [0]\n" + " +ednsflags=### (Set EDNS flag bits)\n" + " +[no]ednsnegotiation (Set EDNS version " + "negotiation)\n" + " +ednsopt=###[:value] (Send specified EDNS " + "option)\n" + " +noednsopt (Clear list of +ednsopt " + "options)\n" + " +[no]expandaaaa (Expand AAAA records)\n" + " +[no]expire (Request time to expire)\n" + " +[no]fail (Don't try next server on " + "SERVFAIL)\n" + " +[no]header-only (Send query without a " + "question section)\n" + " +[no]identify (ID responders in short " + "answers)\n" +#ifdef HAVE_LIBIDN2 + " +[no]idnin (Parse IDN names " + "[default=on on tty])\n" + " +[no]idnout (Convert IDN response " + "[default=on on tty])\n" +#endif /* ifdef HAVE_LIBIDN2 */ + " +[no]ignore (Don't revert to TCP for " + "TC responses.)\n" + " +[no]keepalive (Request EDNS TCP " + "keepalive)\n" + " +[no]keepopen (Keep the TCP socket open " + "between " + "queries)\n" + " +[no]mapped (Allow mapped IPv4 over " + "IPv6)\n" + " +[no]multiline (Print records in an " + "expanded format)\n" + " +ndots=### (Set search NDOTS value)\n" + " +[no]nsid (Request Name Server ID)\n" + " +[no]nssearch (Search all authoritative " + "nameservers)\n" + " +[no]onesoa (AXFR prints only one soa " + "record)\n" + " +[no]opcode=### (Set the opcode of the " + "request)\n" + " +padding=### (Set padding block size " + "[0])\n" + " +[no]qr (Print question before " + "sending)\n" + " +[no]question (Control display of " + "question section)\n" + " +[no]raflag (Set RA flag in query " + "(+[no]raflag))\n" + " +[no]rdflag (Recursive mode " + "(+[no]recurse))\n" + " +[no]recurse (Recursive mode " + "(+[no]rdflag))\n" + " +retry=### (Set number of UDP " + "retries) [2]\n" + " +[no]rrcomments (Control display of " + "per-record " + "comments)\n" + " +[no]search (Set whether to use " + "searchlist)\n" + " +[no]short (Display nothing except " + "short\n" + " form of answers - global " + "option)\n" + " +[no]showsearch (Search with intermediate " + "results)\n" + " +[no]split=## (Split hex/base64 fields " + "into chunks)\n" + " +[no]stats (Control display of " + "statistics)\n" + " +subnet=addr (Set edns-client-subnet " + "option)\n" + " +[no]tcflag (Set TC flag in query " + "(+[no]tcflag))\n" + " +[no]tcp (TCP mode (+[no]vc))\n" + " +timeout=### (Set query timeout) [5]\n" + " +[no]trace (Trace delegation down " + "from root " + "[+dnssec])\n" + " +tries=### (Set number of UDP " + "attempts) [3]\n" + " +[no]ttlid (Control display of ttls " + "in records)\n" + " +[no]ttlunits (Display TTLs in " + "human-readable units)\n" + " +[no]unexpected (Print replies from " + "unexpected sources\n" + " default=off)\n" + " +[no]unknownformat (Print RDATA in RFC 3597 " + "\"unknown\" " + "format)\n" + " +[no]vc (TCP mode (+[no]tcp))\n" + " +[no]yaml (Present the results as " + "YAML)\n" + " +[no]zflag (Set Z flag in query)\n" + " global d-opts and servers (before host name) affect all " + "queries.\n" + " local d-opts and servers (after host name) affect only " + "that lookup.\n" + " -h (print help and exit)\n" + " -v (print version and exit)\n", + stdout); +} + +/*% + * Callback from dighost.c to print the received message. + */ +static void +received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { + uint64_t diff; + time_t tnow; + struct tm tmnow; +#ifdef WIN32 + wchar_t time_str[100]; +#else /* ifdef WIN32 */ + char time_str[100]; +#endif /* ifdef WIN32 */ + char fromtext[ISC_SOCKADDR_FORMATSIZE]; + + isc_sockaddr_format(from, fromtext, sizeof(fromtext)); + + if (short_form || yaml) { + return; + } + + if (query->lookup->stats) { + diff = isc_time_microdiff(&query->time_recv, &query->time_sent); + if (query->lookup->use_usec) { + printf(";; Query time: %ld usec\n", (long)diff); + } else { + printf(";; Query time: %ld msec\n", (long)diff / 1000); + } + printf(";; SERVER: %s(%s)\n", fromtext, query->servname); + time(&tnow); + (void)localtime_r(&tnow, &tmnow); + +#ifdef WIN32 + /* + * On Windows, time zone name ("%Z") may be a localized + * wide-character string, which strftime() handles incorrectly. + */ + if (wcsftime(time_str, sizeof(time_str) / sizeof(time_str[0]), + L"%a %b %d %H:%M:%S %Z %Y", &tmnow) > 0U) + { + printf(";; WHEN: %ls\n", time_str); + } +#else /* ifdef WIN32 */ + if (strftime(time_str, sizeof(time_str), + "%a %b %d %H:%M:%S %Z %Y", &tmnow) > 0U) + { + printf(";; WHEN: %s\n", time_str); + } +#endif /* ifdef WIN32 */ + if (query->lookup->doing_xfr) { + printf(";; XFR size: %u records (messages %u, " + "bytes %" PRIu64 ")\n", + query->rr_count, query->msg_count, + query->byte_count); + } else { + printf(";; MSG SIZE rcvd: %u\n", bytes); + } + if (tsigkey != NULL) { + if (!validated) { + puts(";; WARNING -- Some TSIG could not " + "be validated"); + } + } + if ((tsigkey == NULL) && (keysecret[0] != 0)) { + puts(";; WARNING -- TSIG key was not used."); + } + puts(""); + } else if (query->lookup->identify) { + diff = isc_time_microdiff(&query->time_recv, &query->time_sent); + if (query->lookup->use_usec) { + printf(";; Received %" PRIu64 " bytes " + "from %s(%s) in %ld us\n\n", + query->lookup->doing_xfr ? query->byte_count + : (uint64_t)bytes, + fromtext, query->userarg, (long)diff); + } else { + printf(";; Received %" PRIu64 " bytes " + "from %s(%s) in %ld ms\n\n", + query->lookup->doing_xfr ? query->byte_count + : (uint64_t)bytes, + fromtext, query->userarg, (long)diff / 1000); + } + } +} + +/* + * Callback from dighost.c to print that it is trying a server. + * Not used in dig. + * XXX print_trying + */ +static void +trying(char *frm, dig_lookup_t *lookup) { + UNUSED(frm); + UNUSED(lookup); +} + +/*% + * Internal print routine used to print short form replies. + */ +static isc_result_t +say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) { + isc_result_t result; + uint64_t diff; + char store[sizeof(" in 18446744073709551616 us.")]; + unsigned int styleflags = 0; + + if (query->lookup->trace || query->lookup->ns_search_only) { + result = dns_rdatatype_totext(rdata->type, buf); + if (result != ISC_R_SUCCESS) { + return (result); + } + ADD_STRING(buf, " "); + } + + /* Turn on rrcomments if explicitly enabled */ + if (query->lookup->rrcomments > 0) { + styleflags |= DNS_STYLEFLAG_RRCOMMENT; + } + if (query->lookup->nocrypto) { + styleflags |= DNS_STYLEFLAG_NOCRYPTO; + } + if (query->lookup->print_unknown_format) { + styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; + } + if (query->lookup->expandaaaa) { + styleflags |= DNS_STYLEFLAG_EXPANDAAAA; + } + result = dns_rdata_tofmttext(rdata, NULL, styleflags, 0, splitwidth, + " ", buf); + if (result == ISC_R_NOSPACE) { + return (result); + } + check_result(result, "dns_rdata_totext"); + if (query->lookup->identify) { + diff = isc_time_microdiff(&query->time_recv, &query->time_sent); + ADD_STRING(buf, " from server "); + ADD_STRING(buf, query->servname); + if (query->lookup->use_usec) { + snprintf(store, sizeof(store), " in %" PRIu64 " us.", + diff); + } else { + snprintf(store, sizeof(store), " in %" PRIu64 " ms.", + diff / 1000); + } + ADD_STRING(buf, store); + } + ADD_STRING(buf, "\n"); + return (ISC_R_SUCCESS); +} + +/*% + * short_form message print handler. Calls above say_message() + */ +static isc_result_t +short_answer(dns_message_t *msg, dns_messagetextflag_t flags, isc_buffer_t *buf, + dig_query_t *query) { + dns_name_t *name; + dns_rdataset_t *rdataset; + isc_result_t result, loopresult; + dns_name_t empty_name; + dns_rdata_t rdata = DNS_RDATA_INIT; + + UNUSED(flags); + + dns_name_init(&empty_name, NULL); + result = dns_message_firstname(msg, DNS_SECTION_ANSWER); + if (result == ISC_R_NOMORE) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + return (result); + } + + for (;;) { + name = NULL; + dns_message_currentname(msg, DNS_SECTION_ANSWER, &name); + + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + loopresult = dns_rdataset_first(rdataset); + while (loopresult == ISC_R_SUCCESS) { + dns_rdataset_current(rdataset, &rdata); + result = say_message(&rdata, query, buf); + if (result == ISC_R_NOSPACE) { + return (result); + } + check_result(result, "say_message"); + loopresult = dns_rdataset_next(rdataset); + dns_rdata_reset(&rdata); + } + } + result = dns_message_nextname(msg, DNS_SECTION_ANSWER); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + return (result); + } + } + + return (ISC_R_SUCCESS); +} + +static bool +isdotlocal(dns_message_t *msg) { + isc_result_t result; + static unsigned char local_ndata[] = { "\005local" }; + static unsigned char local_offsets[] = { 0, 6 }; + static dns_name_t local = DNS_NAME_INITABSOLUTE(local_ndata, + local_offsets); + + for (result = dns_message_firstname(msg, DNS_SECTION_QUESTION); + result == ISC_R_SUCCESS; + result = dns_message_nextname(msg, DNS_SECTION_QUESTION)) + { + dns_name_t *name = NULL; + dns_message_currentname(msg, DNS_SECTION_QUESTION, &name); + if (dns_name_issubdomain(name, &local)) { + return (true); + } + } + return (false); +} + +/* + * Callback from dighost.c to print the reply from a server + */ +static isc_result_t +printmessage(dig_query_t *query, const isc_buffer_t *msgbuf, dns_message_t *msg, + bool headers) { + isc_result_t result; + dns_messagetextflag_t flags; + isc_buffer_t *buf = NULL; + unsigned int len = OUTPUTBUF; + dns_master_style_t *style = NULL; + unsigned int styleflags = 0; + bool isquery = (msg == query->lookup->sendmsg); + + UNUSED(msgbuf); + + styleflags |= DNS_STYLEFLAG_REL_OWNER; + if (yaml) { + msg->indent.string = " "; + msg->indent.count = 3; + styleflags |= DNS_STYLEFLAG_YAML; + } else { + if (query->lookup->comments) { + styleflags |= DNS_STYLEFLAG_COMMENT; + } + if (query->lookup->print_unknown_format) { + styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT; + } + /* Turn on rrcomments if explicitly enabled */ + if (query->lookup->rrcomments > 0) { + styleflags |= DNS_STYLEFLAG_RRCOMMENT; + } + if (query->lookup->ttlunits) { + styleflags |= DNS_STYLEFLAG_TTL_UNITS; + } + if (query->lookup->nottl) { + styleflags |= DNS_STYLEFLAG_NO_TTL; + } + if (query->lookup->noclass) { + styleflags |= DNS_STYLEFLAG_NO_CLASS; + } + if (query->lookup->nocrypto) { + styleflags |= DNS_STYLEFLAG_NOCRYPTO; + } + if (query->lookup->expandaaaa) { + styleflags |= DNS_STYLEFLAG_EXPANDAAAA; + } + if (query->lookup->multiline) { + styleflags |= DNS_STYLEFLAG_OMIT_OWNER; + styleflags |= DNS_STYLEFLAG_OMIT_CLASS; + styleflags |= DNS_STYLEFLAG_REL_DATA; + styleflags |= DNS_STYLEFLAG_OMIT_TTL; + styleflags |= DNS_STYLEFLAG_TTL; + styleflags |= DNS_STYLEFLAG_MULTILINE; + /* Turn on rrcomments unless explicitly disabled */ + if (query->lookup->rrcomments >= 0) { + styleflags |= DNS_STYLEFLAG_RRCOMMENT; + } + } + } + if (query->lookup->multiline || + (query->lookup->nottl && query->lookup->noclass)) + { + result = dns_master_stylecreate(&style, styleflags, 24, 24, 24, + 32, 80, 8, splitwidth, mctx); + } else if (query->lookup->nottl || query->lookup->noclass) { + result = dns_master_stylecreate(&style, styleflags, 24, 24, 32, + 40, 80, 8, splitwidth, mctx); + } else { + result = dns_master_stylecreate(&style, styleflags, 24, 32, 40, + 48, 80, 8, splitwidth, mctx); + } + check_result(result, "dns_master_stylecreate"); + + if (query->lookup->cmdline[0] != 0) { + if (!short_form && printcmd) { + fputs(query->lookup->cmdline, stdout); + } + query->lookup->cmdline[0] = '\0'; + } + debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders", + query->lookup->comments ? "comments" : "nocomments", + short_form ? "short_form" : "long_form"); + + flags = 0; + if (!headers) { + flags |= DNS_MESSAGETEXTFLAG_NOHEADERS; + flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; + } + if (query->lookup->onesoa && + query->lookup->rdtype == dns_rdatatype_axfr) + { + flags |= (query->msg_count == 0) ? DNS_MESSAGETEXTFLAG_ONESOA + : DNS_MESSAGETEXTFLAG_OMITSOA; + } + if (!query->lookup->comments) { + flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS; + } + + isc_buffer_allocate(mctx, &buf, len); + + if (yaml) { + enum { Q = 0x1, R = 0x2 }; /* Q:query; R:ecursive */ + unsigned int tflag = 0; + isc_sockaddr_t saddr; + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + uint16_t sport; + char *hash; + int pf; + + printf("-\n"); + printf(" type: MESSAGE\n"); + printf(" message:\n"); + + if (isquery) { + tflag |= Q; + if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) { + tflag |= R; + } + } else if (((msg->flags & DNS_MESSAGEFLAG_RD) != 0) && + ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)) + { + tflag |= R; + } + + if (tflag == (Q | R)) { + printf(" type: RECURSIVE_QUERY\n"); + } else if (tflag == Q) { + printf(" type: AUTH_QUERY\n"); + } else if (tflag == R) { + printf(" type: RECURSIVE_RESPONSE\n"); + } else { + printf(" type: AUTH_RESPONSE\n"); + } + + if (!isc_time_isepoch(&query->time_sent)) { + char tbuf[100]; + if (query->lookup->use_usec) { + isc_time_formatISO8601us(&query->time_sent, + tbuf, sizeof(tbuf)); + } else { + isc_time_formatISO8601ms(&query->time_sent, + tbuf, sizeof(tbuf)); + } + printf(" query_time: !!timestamp %s\n", tbuf); + } + + if (!isquery && !isc_time_isepoch(&query->time_recv)) { + char tbuf[100]; + if (query->lookup->use_usec) { + isc_time_formatISO8601us(&query->time_recv, + tbuf, sizeof(tbuf)); + } else { + isc_time_formatISO8601ms(&query->time_recv, + tbuf, sizeof(tbuf)); + } + printf(" response_time: !!timestamp %s\n", tbuf); + } + + printf(" message_size: %ub\n", + isc_buffer_usedlength(msgbuf)); + + pf = isc_sockaddr_pf(&query->sockaddr); + if (pf == PF_INET || pf == PF_INET6) { + printf(" socket_family: %s\n", + pf == PF_INET ? "INET" : "INET6"); + + printf(" socket_protocol: %s\n", + query->lookup->tcp_mode ? "TCP" : "UDP"); + + sport = isc_sockaddr_getport(&query->sockaddr); + isc_sockaddr_format(&query->sockaddr, sockstr, + sizeof(sockstr)); + hash = strchr(sockstr, '#'); + if (hash != NULL) { + *hash = '\0'; + } + if (strcmp(sockstr, "::") == 0) { + strlcat(sockstr, "0", sizeof(sockstr)); + } + + printf(" response_address: \"%s\"\n", sockstr); + printf(" response_port: %u\n", sport); + } + + if (query->sock != NULL && + isc_socket_getsockname(query->sock, &saddr) == + ISC_R_SUCCESS) + { + sport = isc_sockaddr_getport(&saddr); + isc_sockaddr_format(&saddr, sockstr, sizeof(sockstr)); + hash = strchr(sockstr, '#'); + if (hash != NULL) { + *hash = '\0'; + } + if (strcmp(sockstr, "::") == 0) { + strlcat(sockstr, "0", sizeof(sockstr)); + } + + printf(" query_address: \"%s\"\n", sockstr); + printf(" query_port: %u\n", sport); + } + + printf(" %s:\n", isquery ? "query_message_data" + : "response_message_data"); + result = dns_message_headertotext(msg, style, flags, buf); + } else if (query->lookup->comments && !short_form) { + if (query->lookup->cmdline[0] != '\0' && printcmd) { + printf("; %s\n", query->lookup->cmdline); + } + if (msg == query->lookup->sendmsg) { + printf(";; Sending:\n"); + } else { + printf(";; Got answer:\n"); + } + + if (headers) { + if (isdotlocal(msg)) { + printf(";; WARNING: .local is reserved for " + "Multicast DNS\n;; You are currently " + "testing what happens when an mDNS " + "query is leaked to DNS\n"); + } + printf(";; ->>HEADER<<- opcode: %s, status: %s, " + "id: %u\n", + opcodetext[msg->opcode], + rcode_totext(msg->rcode), msg->id); + printf(";; flags:"); + if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) { + printf(" qr"); + } + if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) { + printf(" aa"); + } + if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) { + printf(" tc"); + } + if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) { + printf(" rd"); + } + if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) { + printf(" ra"); + } + if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) { + printf(" ad"); + } + if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) { + printf(" cd"); + } + if ((msg->flags & 0x0040U) != 0) { + printf("; MBZ: 0x4"); + } + + printf("; QUERY: %u, ANSWER: %u, " + "AUTHORITY: %u, ADDITIONAL: %u\n", + msg->counts[DNS_SECTION_QUESTION], + msg->counts[DNS_SECTION_ANSWER], + msg->counts[DNS_SECTION_AUTHORITY], + msg->counts[DNS_SECTION_ADDITIONAL]); + + if (msg != query->lookup->sendmsg && + (msg->flags & DNS_MESSAGEFLAG_RD) != 0 && + (msg->flags & DNS_MESSAGEFLAG_RA) == 0) + { + printf(";; WARNING: recursion requested " + "but not available\n"); + } + } + if (msg != query->lookup->sendmsg && + query->lookup->edns != -1 && msg->opt == NULL && + (msg->rcode == dns_rcode_formerr || + msg->rcode == dns_rcode_notimp)) + { + printf("\n;; WARNING: EDNS query returned status " + "%s - retry with '%s+noedns'\n", + rcode_totext(msg->rcode), + query->lookup->dnssec ? "+nodnssec " : ""); + } + if (msg != query->lookup->sendmsg && extrabytes != 0U) { + printf(";; WARNING: Message has %u extra byte%s at " + "end\n", + extrabytes, extrabytes != 0 ? "s" : ""); + } + } + +repopulate_buffer: + + if (query->lookup->comments && headers && !short_form) { + result = dns_message_pseudosectiontotext( + msg, DNS_PSEUDOSECTION_OPT, style, flags, buf); + if (result == ISC_R_NOSPACE) { + buftoosmall: + len += OUTPUTBUF; + isc_buffer_free(&buf); + isc_buffer_allocate(mctx, &buf, len); + goto repopulate_buffer; + } + check_result(result, "dns_message_pseudosectiontotext"); + } + + if (query->lookup->section_question && headers) { + if (!short_form) { + result = dns_message_sectiontotext( + msg, DNS_SECTION_QUESTION, style, flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_sectiontotext"); + } + } + if (query->lookup->section_answer) { + if (!short_form) { + result = dns_message_sectiontotext( + msg, DNS_SECTION_ANSWER, style, flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_sectiontotext"); + } else { + result = short_answer(msg, flags, buf, query); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "short_answer"); + } + } + if (query->lookup->section_authority) { + if (!short_form) { + result = dns_message_sectiontotext( + msg, DNS_SECTION_AUTHORITY, style, flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_sectiontotext"); + } + } + if (query->lookup->section_additional) { + if (!short_form) { + result = dns_message_sectiontotext( + msg, DNS_SECTION_ADDITIONAL, style, flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_sectiontotext"); + /* + * Only print the signature on the first record. + */ + if (headers) { + result = dns_message_pseudosectiontotext( + msg, DNS_PSEUDOSECTION_TSIG, style, + flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_" + "pseudosectiontotext"); + result = dns_message_pseudosectiontotext( + msg, DNS_PSEUDOSECTION_SIG0, style, + flags, buf); + if (result == ISC_R_NOSPACE) { + goto buftoosmall; + } + check_result(result, "dns_message_" + "pseudosectiontotext"); + } + } + } + + if (headers && query->lookup->comments && !short_form && !yaml) { + printf("\n"); + } + + printf("%.*s", (int)isc_buffer_usedlength(buf), + (char *)isc_buffer_base(buf)); + isc_buffer_free(&buf); + + if (style != NULL) { + dns_master_styledestroy(&style, mctx); + } + return (result); +} + +/*% + * print the greeting message when the program first starts up. + */ +static void +printgreeting(int argc, char **argv, dig_lookup_t *lookup) { + int i; + static bool first = true; + char append[MXNAME]; + + if (printcmd) { + snprintf(lookup->cmdline, sizeof(lookup->cmdline), + "%s; <<>> DiG " VERSION " <<>>", first ? "\n" : ""); + i = 1; + while (i < argc) { + snprintf(append, sizeof(append), " %s", argv[i++]); + strlcat(lookup->cmdline, append, + sizeof(lookup->cmdline)); + } + strlcat(lookup->cmdline, "\n", sizeof(lookup->cmdline)); + if (first && addresscount != 0) { + snprintf(append, sizeof(append), + "; (%d server%s found)\n", addresscount, + addresscount > 1 ? "s" : ""); + strlcat(lookup->cmdline, append, + sizeof(lookup->cmdline)); + } + if (first) { + snprintf(append, sizeof(append), + ";; global options:%s%s\n", + short_form ? " +short" : "", + printcmd ? " +cmd" : ""); + first = false; + strlcat(lookup->cmdline, append, + sizeof(lookup->cmdline)); + } + } +} + +/*% + * We're not using isc_commandline_parse() here since the command line + * syntax of dig is quite a bit different from that which can be described + * by that routine. + * XXX doc options + */ + +static void +plus_option(char *option, bool is_batchfile, dig_lookup_t *lookup) { + isc_result_t result; + char *cmd, *value, *last = NULL, *code, *extra; + uint32_t num; + bool state = true; + size_t n; + + INSIST(option != NULL); + + if ((cmd = strtok_r(option, "=", &last)) == NULL) { + printf(";; Invalid option %s\n", option); + return; + } + if (strncasecmp(cmd, "no", 2) == 0) { + cmd += 2; + state = false; + } + /* parse the rest of the string */ + value = strtok_r(NULL, "", &last); + +#define FULLCHECK(A) \ + do { \ + size_t _l = strlen(cmd); \ + if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \ + goto invalid_option; \ + } while (0) +#define FULLCHECK2(A, B) \ + do { \ + size_t _l = strlen(cmd); \ + if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \ + (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \ + goto invalid_option; \ + } while (0) + + switch (cmd[0]) { + case 'a': + switch (cmd[1]) { + case 'a': /* aaonly / aaflag */ + FULLCHECK2("aaonly", "aaflag"); + lookup->aaonly = state; + break; + case 'd': + switch (cmd[2]) { + case 'd': /* additional */ + FULLCHECK("additional"); + lookup->section_additional = state; + break; + case 'f': /* adflag */ + case '\0': /* +ad is a synonym for +adflag */ + FULLCHECK("adflag"); + lookup->adflag = state; + break; + default: + goto invalid_option; + } + break; + case 'l': /* all */ + FULLCHECK("all"); + lookup->section_question = state; + lookup->section_authority = state; + lookup->section_answer = state; + lookup->section_additional = state; + lookup->comments = state; + lookup->stats = state; + printcmd = state; + break; + case 'n': /* answer */ + FULLCHECK("answer"); + lookup->section_answer = state; + break; + case 'u': /* authority */ + FULLCHECK("authority"); + lookup->section_authority = state; + break; + default: + goto invalid_option; + } + break; + case 'b': + switch (cmd[1]) { + case 'a': /* badcookie */ + FULLCHECK("badcookie"); + lookup->badcookie = state; + break; + case 'e': /* besteffort */ + FULLCHECK("besteffort"); + lookup->besteffort = state; + break; + case 'u': /* bufsize */ + FULLCHECK("bufsize"); + if (!state) { + goto invalid_option; + } + if (value == NULL) { + lookup->udpsize = DEFAULT_EDNS_BUFSIZE; + break; + } + result = parse_uint(&num, value, COMMSIZE, + "buffer size"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse buffer size"); + goto exit_or_usage; + } + lookup->udpsize = num; + if (lookup->udpsize == 0) { + lookup->edns = -1; + } + break; + default: + goto invalid_option; + } + break; + case 'c': + switch (cmd[1]) { + case 'd': /* cdflag */ + switch (cmd[2]) { + case 'f': /* cdflag */ + case '\0': /* +cd is a synonym for +cdflag */ + FULLCHECK("cdflag"); + lookup->cdflag = state; + break; + default: + goto invalid_option; + } + break; + case 'l': /* class */ + /* keep +cl for backwards compatibility */ + FULLCHECK2("cl", "class"); + lookup->noclass = !state; + break; + case 'm': /* cmd */ + FULLCHECK("cmd"); + printcmd = state; + break; + case 'o': /* comments */ + switch (cmd[2]) { + case 'm': + FULLCHECK("comments"); + lookup->comments = state; + if (lookup == default_lookup) { + pluscomm = state; + } + break; + case 'o': /* cookie */ + FULLCHECK("cookie"); + if (state && lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + lookup->sendcookie = state; + if (value != NULL) { + n = strlcpy(hexcookie, value, + sizeof(hexcookie)); + if (n >= sizeof(hexcookie)) { + warn("COOKIE data too large"); + goto exit_or_usage; + } + lookup->cookie = hexcookie; + } else { + lookup->cookie = NULL; + } + break; + default: + goto invalid_option; + } + break; + case 'r': + FULLCHECK("crypto"); + lookup->nocrypto = !state; + break; + default: + goto invalid_option; + } + break; + case 'd': + switch (cmd[1]) { + case 'e': /* defname */ + FULLCHECK("defname"); + if (!lookup->trace) { + usesearch = state; + } + break; + case 'n': /* dnssec */ + FULLCHECK("dnssec"); + dnssec: + if (state && lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + lookup->dnssec = state; + break; + case 'o': /* domain ... but treat "do" as synonym for dnssec */ + if (cmd[2] == '\0') { + goto dnssec; + } + FULLCHECK("domain"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + strlcpy(domainopt, value, sizeof(domainopt)); + break; + case 's': /* dscp */ + FULLCHECK("dscp"); + if (!state) { + lookup->dscp = -1; + break; + } + if (value == NULL) { + goto need_value; + } + result = parse_uint(&num, value, 0x3f, "DSCP"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse DSCP value"); + goto exit_or_usage; + } + lookup->dscp = num; + break; + default: + goto invalid_option; + } + break; + case 'e': + switch (cmd[1]) { + case 'd': + switch (cmd[2]) { + case 'n': + switch (cmd[3]) { + case 's': + switch (cmd[4]) { + case 0: + FULLCHECK("edns"); + if (!state) { + lookup->edns = -1; + break; + } + if (value == NULL) { + lookup->edns = + DEFAULT_EDNS_VERSION; + break; + } + result = parse_uint(&num, value, + 255, + "edns"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse " + "edns"); + goto exit_or_usage; + } + lookup->edns = num; + break; + case 'f': + FULLCHECK("ednsflags"); + if (!state) { + lookup->ednsflags = 0; + break; + } + if (value == NULL) { + lookup->ednsflags = 0; + break; + } + result = parse_xint( + &num, value, 0xffff, + "ednsflags"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse " + "ednsflags"); + goto exit_or_usage; + } + lookup->ednsflags = num; + break; + case 'n': + FULLCHECK("ednsnegotiation"); + lookup->ednsneg = state; + break; + case 'o': + FULLCHECK("ednsopt"); + if (!state) { + lookup->ednsoptscnt = 0; + break; + } + code = NULL; + if (value != NULL) { + code = strtok_r(value, + ":", + &last); + } + if (code == NULL) { + warn("ednsopt no " + "code point " + "specified"); + goto exit_or_usage; + } + extra = strtok_r(NULL, "\0", + &last); + save_opt(lookup, code, extra); + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'x': + switch (cmd[2]) { + case 'p': + switch (cmd[3]) { + case 'a': + FULLCHECK("expandaaaa"); + lookup->expandaaaa = state; + break; + case 'i': + FULLCHECK("expire"); + lookup->expire = state; + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'f': /* fail */ + FULLCHECK("fail"); + lookup->servfail_stops = state; + break; + case 'h': + FULLCHECK("header-only"); + lookup->header_only = state; + break; + case 'i': + switch (cmd[1]) { + case 'd': /* identify */ + switch (cmd[2]) { + case 'e': + FULLCHECK("identify"); + lookup->identify = state; + break; + case 'n': + switch (cmd[3]) { + case 'i': + FULLCHECK("idnin"); +#ifndef HAVE_LIBIDN2 + fprintf(stderr, ";; IDN input support" + " not enabled\n"); +#else /* ifndef HAVE_LIBIDN2 */ + lookup->idnin = state; +#endif /* ifndef HAVE_LIBIDN2 */ + break; + case 'o': + FULLCHECK("idnout"); +#ifndef HAVE_LIBIDN2 + fprintf(stderr, ";; IDN output support" + " not enabled\n"); +#else /* ifndef HAVE_LIBIDN2 */ + lookup->idnout = state; +#endif /* ifndef HAVE_LIBIDN2 */ + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'g': /* ignore */ + default: /* + * Inherits default for compatibility (+[no]i*). + */ + FULLCHECK("ignore"); + lookup->ignore = state; + } + break; + case 'k': + switch (cmd[1]) { + case 'e': + switch (cmd[2]) { + case 'e': + switch (cmd[3]) { + case 'p': + switch (cmd[4]) { + case 'a': + FULLCHECK("keepalive"); + lookup->tcp_keepalive = state; + break; + case 'o': + FULLCHECK("keepopen"); + keep_open = state; + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'm': /* multiline */ + switch (cmd[1]) { + case 'a': + FULLCHECK("mapped"); + lookup->mapped = state; + break; + case 'u': + FULLCHECK("multiline"); + lookup->multiline = state; + break; + default: + goto invalid_option; + } + break; + case 'n': + switch (cmd[1]) { + case 'd': /* ndots */ + FULLCHECK("ndots"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + result = parse_uint(&num, value, MAXNDOTS, "ndots"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse ndots"); + goto exit_or_usage; + } + ndots = num; + break; + case 's': + switch (cmd[2]) { + case 'i': /* nsid */ + FULLCHECK("nsid"); + if (state && lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + lookup->nsid = state; + break; + case 's': /* nssearch */ + FULLCHECK("nssearch"); + lookup->ns_search_only = state; + if (state) { + lookup->trace_root = true; + lookup->recurse = true; + lookup->identify = true; + lookup->stats = false; + lookup->comments = false; + lookup->section_additional = false; + lookup->section_authority = false; + lookup->section_question = false; + lookup->rdtype = dns_rdatatype_ns; + lookup->rdtypeset = true; + short_form = true; + lookup->rrcomments = 0; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'o': + switch (cmd[1]) { + case 'n': + FULLCHECK("onesoa"); + lookup->onesoa = state; + break; + case 'p': + FULLCHECK("opcode"); + if (!state) { + lookup->opcode = 0; /* default - query */ + break; + } + if (value == NULL) { + goto need_value; + } + for (num = 0; + num < sizeof(opcodetext) / sizeof(opcodetext[0]); + num++) + { + if (strcasecmp(opcodetext[num], value) == 0) { + break; + } + } + if (num < 16) { + lookup->opcode = (dns_opcode_t)num; + break; + } + result = parse_uint(&num, value, 15, "opcode"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse opcode"); + goto exit_or_usage; + } + lookup->opcode = (dns_opcode_t)num; + break; + default: + goto invalid_option; + } + break; + case 'p': + FULLCHECK("padding"); + if (state && lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + if (value == NULL) { + goto need_value; + } + result = parse_uint(&num, value, 512, "padding"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse padding"); + goto exit_or_usage; + } + lookup->padding = (uint16_t)num; + break; + case 'q': + switch (cmd[1]) { + case 'r': /* qr */ + FULLCHECK("qr"); + lookup->qr = state; + break; + case 'u': /* question */ + FULLCHECK("question"); + lookup->section_question = state; + if (lookup == default_lookup) { + plusquest = state; + } + break; + default: + goto invalid_option; + } + break; + case 'r': + switch (cmd[1]) { + case 'a': /* raflag */ + FULLCHECK("raflag"); + lookup->raflag = state; + break; + case 'd': /* rdflag */ + FULLCHECK("rdflag"); + lookup->recurse = state; + break; + case 'e': + switch (cmd[2]) { + case 'c': /* recurse */ + FULLCHECK("recurse"); + lookup->recurse = state; + break; + case 't': /* retry / retries */ + FULLCHECK2("retry", "retries"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + result = parse_uint(&lookup->retries, value, + MAXTRIES - 1, "retries"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse retries"); + goto exit_or_usage; + } + lookup->retries++; + break; + default: + goto invalid_option; + } + break; + case 'r': /* rrcomments */ + FULLCHECK("rrcomments"); + lookup->rrcomments = state ? 1 : -1; + break; + default: + goto invalid_option; + } + break; + case 's': + switch (cmd[1]) { + case 'e': /* search */ + FULLCHECK("search"); + if (!lookup->trace) { + usesearch = state; + } + break; + case 'h': + if (cmd[2] != 'o') { + goto invalid_option; + } + switch (cmd[3]) { + case 'r': /* short */ + FULLCHECK("short"); + short_form = state; + if (state) { + printcmd = false; + lookup->section_additional = false; + lookup->section_answer = true; + lookup->section_authority = false; + lookup->section_question = false; + lookup->comments = false; + lookup->stats = false; + lookup->rrcomments = -1; + } + break; + case 'w': /* showsearch */ + FULLCHECK("showsearch"); + if (!lookup->trace) { + showsearch = state; + usesearch = state; + } + break; + default: + goto invalid_option; + } + break; + case 'i': /* sigchase */ + FULLCHECK("sigchase"); + fprintf(stderr, ";; +sigchase option is deprecated"); + break; + case 'p': /* split */ + FULLCHECK("split"); + if (value != NULL && !state) { + goto invalid_option; + } + if (!state) { + splitwidth = 0; + break; + } else if (value == NULL) { + break; + } + + result = parse_uint(&splitwidth, value, 1023, "split"); + if ((splitwidth % 4) != 0U) { + splitwidth = ((splitwidth + 3) / 4) * 4; + fprintf(stderr, + ";; Warning, split must be " + "a multiple of 4; adjusting " + "to %u\n", + splitwidth); + } + /* + * There is an adjustment done in the + * totext_<rrtype>() functions which causes + * splitwidth to shrink. This is okay when we're + * using the default width but incorrect in this + * case, so we correct for it + */ + if (splitwidth) { + splitwidth += 3; + } + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse split"); + goto exit_or_usage; + } + break; + case 't': /* stats */ + FULLCHECK("stats"); + lookup->stats = state; + break; + case 'u': /* subnet */ + FULLCHECK("subnet"); + if (state && value == NULL) { + goto need_value; + } + if (!state) { + if (lookup->ecs_addr != NULL) { + isc_mem_free(mctx, lookup->ecs_addr); + lookup->ecs_addr = NULL; + } + break; + } + if (lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + if (lookup->ecs_addr != NULL) { + isc_mem_free(mctx, lookup->ecs_addr); + lookup->ecs_addr = NULL; + } + result = parse_netprefix(&lookup->ecs_addr, value); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse client"); + goto exit_or_usage; + } + break; + default: + goto invalid_option; + } + break; + case 't': + switch (cmd[1]) { + case 'c': /* tcp */ + switch (cmd[2]) { + case 'f': + FULLCHECK("tcflag"); + lookup->tcflag = state; + break; + case 'p': + FULLCHECK("tcp"); + if (!is_batchfile) { + lookup->tcp_mode = state; + lookup->tcp_mode_set = true; + } + break; + default: + goto invalid_option; + } + break; + case 'i': /* timeout */ + FULLCHECK("timeout"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + result = parse_uint(&timeout, value, MAXTIMEOUT, + "timeout"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse timeout"); + goto exit_or_usage; + } + if (timeout == 0) { + timeout = 1; + } + break; + case 'o': + FULLCHECK("topdown"); + fprintf(stderr, ";; +topdown option is deprecated"); + break; + case 'r': + switch (cmd[2]) { + case 'a': /* trace */ + FULLCHECK("trace"); + lookup->trace = state; + lookup->trace_root = state; + if (state) { + lookup->recurse = true; + lookup->identify = true; + lookup->comments = false; + lookup->rrcomments = 0; + lookup->stats = false; + lookup->section_additional = false; + lookup->section_authority = true; + lookup->section_question = false; + lookup->dnssec = true; + lookup->sendcookie = true; + usesearch = false; + } + break; + case 'i': /* tries */ + FULLCHECK("tries"); + if (value == NULL) { + goto need_value; + } + if (!state) { + goto invalid_option; + } + result = parse_uint(&lookup->retries, value, + MAXTRIES, "tries"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse tries"); + goto exit_or_usage; + } + if (lookup->retries == 0) { + lookup->retries = 1; + } + break; + case 'u': /* trusted-key */ + FULLCHECK("trusted-key"); + fprintf(stderr, ";; +trusted-key option is " + "deprecated"); + break; + default: + goto invalid_option; + } + break; + case 't': + switch (cmd[2]) { + case 'l': + switch (cmd[3]) { + case 0: + case 'i': /* ttlid */ + FULLCHECK2("ttl", "ttlid"); + lookup->nottl = !state; + break; + case 'u': /* ttlunits */ + FULLCHECK("ttlunits"); + lookup->nottl = false; + lookup->ttlunits = state; + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + break; + case 'u': + switch (cmd[1]) { + case 'n': + switch (cmd[2]) { + case 'e': + FULLCHECK("unexpected"); + lookup->accept_reply_unexpected_src = state; + break; + case 'k': + FULLCHECK("unknownformat"); + lookup->print_unknown_format = state; + break; + default: + goto invalid_option; + } + break; + default: + goto invalid_option; + } + + break; + case 'v': + FULLCHECK("vc"); + if (!is_batchfile) { + lookup->tcp_mode = state; + lookup->tcp_mode_set = true; + } + break; + case 'y': /* yaml */ + FULLCHECK("yaml"); + yaml = state; + if (state) { + printcmd = false; + lookup->stats = false; + lookup->rrcomments = -1; + } + break; + case 'z': /* zflag */ + FULLCHECK("zflag"); + lookup->zflag = state; + break; + default: + invalid_option: + need_value: +#if TARGET_OS_IPHONE + exit_or_usage: +#endif /* if TARGET_OS_IPHONE */ + fprintf(stderr, "Invalid option: +%s\n", option); + usage(); + } + return; + +#if !TARGET_OS_IPHONE +exit_or_usage: + digexit(); +#endif /* if !TARGET_OS_IPHONE */ +} + +/*% + * #true returned if value was used + */ +static const char *single_dash_opts = "46dhimnruv"; +static const char *dash_opts = "46bcdfhikmnpqrtvyx"; +static bool +dash_option(char *option, char *next, dig_lookup_t **lookup, + bool *open_type_class, bool *need_clone, bool config_only, int argc, + char **argv, bool *firstarg) { + char opt, *value, *ptr, *ptr2, *ptr3, *last; + isc_result_t result; + bool value_from_next; + isc_textregion_t tr; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + char textname[MXNAME]; + struct in_addr in4; + struct in6_addr in6; + in_port_t srcport; + char *hash, *cmd; + uint32_t num; + + while (strpbrk(option, single_dash_opts) == &option[0]) { + /* + * Since the -[46dhimnuv] options do not take an argument, + * account for them (in any number and/or combination) + * if they appear as the first character(s) of a q-opt. + */ + opt = option[0]; + switch (opt) { + case '4': + if (have_ipv4) { + isc_net_disableipv6(); + have_ipv6 = false; + } else { + fatal("can't find IPv4 networking"); + UNREACHABLE(); + return (false); + } + break; + case '6': + if (have_ipv6) { + isc_net_disableipv4(); + have_ipv4 = false; + } else { + fatal("can't find IPv6 networking"); + UNREACHABLE(); + return (false); + } + break; + case 'd': + ptr = strpbrk(&option[1], dash_opts); + if (ptr != &option[1]) { + cmd = option; + FULLCHECK("debug"); + debugging = true; + return (false); + } else { + debugging = true; + } + break; + case 'h': + help(); + exit(0); + break; + case 'i': + /* deprecated */ + break; + case 'm': /* memdebug */ + /* memdebug is handled in preparse_args() */ + break; + case 'n': + /* deprecated */ + break; + case 'r': + debug("digrc (late)"); + digrc = false; + break; + case 'u': + (*lookup)->use_usec = true; + break; + case 'v': + version(); + exit(0); + break; + } + if (strlen(option) > 1U) { + option = &option[1]; + } else { + return (false); + } + } + opt = option[0]; + if (strlen(option) > 1U) { + value_from_next = false; + value = &option[1]; + } else { + value_from_next = true; + value = next; + } + if (value == NULL) { + goto invalid_option; + } + switch (opt) { + case 'b': + hash = strchr(value, '#'); + if (hash != NULL) { + result = parse_uint(&num, hash + 1, MAXPORT, + "port number"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse port number"); + } + srcport = num; + *hash = '\0'; + } else { + srcport = 0; + } + if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) { + isc_sockaddr_fromin6(&bind_address, &in6, srcport); + isc_net_disableipv4(); + } else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) { + isc_sockaddr_fromin(&bind_address, &in4, srcport); + isc_net_disableipv6(); + } else { + if (hash != NULL) { + *hash = '#'; + } + fatal("invalid address %s", value); + } + if (hash != NULL) { + *hash = '#'; + } + specified_source = true; + return (value_from_next); + case 'c': + if ((*lookup)->rdclassset) { + fprintf(stderr, ";; Warning, extra class option\n"); + } + *open_type_class = false; + tr.base = value; + tr.length = (unsigned int)strlen(value); + result = dns_rdataclass_fromtext(&rdclass, + (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS) { + (*lookup)->rdclass = rdclass; + (*lookup)->rdclassset = true; + } else { + fprintf(stderr, + ";; Warning, ignoring " + "invalid class %s\n", + value); + } + return (value_from_next); + case 'f': + atomic_store(&batchname, (uintptr_t)value); + return (value_from_next); + case 'k': + strlcpy(keyfile, value, sizeof(keyfile)); + return (value_from_next); + case 'p': + result = parse_uint(&num, value, MAXPORT, "port number"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse port number"); + } + port = num; + return (value_from_next); + case 'q': + if (!config_only) { + if (*need_clone) { + (*lookup) = clone_lookup(default_lookup, true); + } + *need_clone = true; + strlcpy((*lookup)->textname, value, + sizeof((*lookup)->textname)); + (*lookup)->trace_root = ((*lookup)->trace || + (*lookup)->ns_search_only); + (*lookup)->new_search = true; + if (*firstarg) { + printgreeting(argc, argv, *lookup); + *firstarg = false; + } + ISC_LIST_APPEND(lookup_list, (*lookup), link); + debug("looking up %s", (*lookup)->textname); + } + return (value_from_next); + case 't': + *open_type_class = false; + if (strncasecmp(value, "ixfr=", 5) == 0) { + rdtype = dns_rdatatype_ixfr; + result = ISC_R_SUCCESS; + } else { + tr.base = value; + tr.length = (unsigned int)strlen(value); + result = dns_rdatatype_fromtext( + &rdtype, (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS && + rdtype == dns_rdatatype_ixfr) + { + result = DNS_R_UNKNOWN; + } + } + if (result == ISC_R_SUCCESS) { + if ((*lookup)->rdtypeset) { + fprintf(stderr, ";; Warning, " + "extra type option\n"); + } + if (rdtype == dns_rdatatype_ixfr) { + uint32_t serial; + (*lookup)->rdtype = dns_rdatatype_ixfr; + (*lookup)->rdtypeset = true; + result = parse_uint(&serial, &value[5], + MAXSERIAL, "serial number"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse serial number"); + } + (*lookup)->ixfr_serial = serial; + (*lookup)->section_question = plusquest; + (*lookup)->comments = pluscomm; + if (!(*lookup)->tcp_mode_set) { + (*lookup)->tcp_mode = true; + } + } else { + (*lookup)->rdtype = rdtype; + if (!config_only) { + (*lookup)->rdtypeset = true; + } + if (rdtype == dns_rdatatype_axfr) { + (*lookup)->section_question = plusquest; + (*lookup)->comments = pluscomm; + } else if (rdtype == dns_rdatatype_any) { + if (!(*lookup)->tcp_mode_set) { + (*lookup)->tcp_mode = true; + } + } + (*lookup)->ixfr_serial = false; + } + } else { + fprintf(stderr, + ";; Warning, ignoring " + "invalid type %s\n", + value); + } + return (value_from_next); + case 'y': + if ((ptr = strtok_r(value, ":", &last)) == NULL) { + usage(); + } + if ((ptr2 = strtok_r(NULL, ":", &last)) == NULL) { /* name or + * secret */ + usage(); + } + if ((ptr3 = strtok_r(NULL, ":", &last)) != NULL) { /* secret or + * NULL */ + parse_hmac(ptr); + ptr = ptr2; + ptr2 = ptr3; + } else { + hmacname = DNS_TSIG_HMACMD5_NAME; + digestbits = 0; + } + /* XXXONDREJ: FIXME */ + strlcpy(keynametext, ptr, sizeof(keynametext)); + strlcpy(keysecret, ptr2, sizeof(keysecret)); + return (value_from_next); + case 'x': + if (*need_clone) { + *lookup = clone_lookup(default_lookup, true); + } + *need_clone = true; + if (get_reverse(textname, sizeof(textname), value, false) == + ISC_R_SUCCESS) + { + strlcpy((*lookup)->textname, textname, + sizeof((*lookup)->textname)); + debug("looking up %s", (*lookup)->textname); + (*lookup)->trace_root = ((*lookup)->trace || + (*lookup)->ns_search_only); + if (!(*lookup)->rdtypeset) { + (*lookup)->rdtype = dns_rdatatype_ptr; + } + if (!(*lookup)->rdclassset) { + (*lookup)->rdclass = dns_rdataclass_in; + } + (*lookup)->new_search = true; + if (*firstarg) { + printgreeting(argc, argv, *lookup); + *firstarg = false; + } + ISC_LIST_APPEND(lookup_list, *lookup, link); + } else { + fprintf(stderr, "Invalid IP address %s\n", value); + exit(1); + } + return (value_from_next); + invalid_option: + default: + fprintf(stderr, "Invalid option: -%s\n", option); + usage(); + } + UNREACHABLE(); + return (false); +} + +/*% + * Because we may be trying to do memory allocation recording, we're going + * to need to parse the arguments for the -m *before* we start the main + * argument parsing routine. + * + * I'd prefer not to have to do this, but I am not quite sure how else to + * fix the problem. Argument parsing in dig involves memory allocation + * by its nature, so it can't be done in the main argument parser. + */ +static void +preparse_args(int argc, char **argv) { + int rc; + char **rv; + char *option; + + rc = argc; + rv = argv; + for (rc--, rv++; rc > 0; rc--, rv++) { + if (rv[0][0] != '-') { + continue; + } + option = &rv[0][1]; + while (strpbrk(option, single_dash_opts) == &option[0]) { + switch (option[0]) { + case 'd': + /* For debugging early startup */ + debugging = true; + break; + case 'm': + memdebugging = true; + isc_mem_debugging = ISC_MEM_DEBUGTRACE | + ISC_MEM_DEBUGRECORD; + break; + case 'r': + /* + * Must be done early, because ~/.digrc + * is read before command line parsing + */ + debug("digrc (early)"); + digrc = false; + break; + case '4': + if (ipv6only) { + fatal("only one of -4 and -6 allowed"); + } + ipv4only = true; + break; + case '6': + if (ipv4only) { + fatal("only one of -4 and -6 allowed"); + } + ipv6only = true; + break; + } + option = &option[1]; + } + if (strlen(option) == 0U) { + continue; + } + /* Look for dash value option. */ + if (strpbrk(option, dash_opts) != &option[0]) { + goto invalid_option; + } + if (strlen(option) > 1U) { + /* value in option. */ + continue; + } + /* Dash value is next argument so we need to skip it. */ + rc--, rv++; + /* Handle missing argument */ + if (rc == 0) { + invalid_option: + fprintf(stderr, "Invalid option: -%s\n", option); + usage(); + } + } +} + +static int +split_batchline(char *batchline, char **bargv, int len, const char *msg) { + int bargc; + char *last = NULL; + + REQUIRE(batchline != NULL); + + for (bargc = 1, bargv[bargc] = strtok_r(batchline, " \t\r\n", &last); + bargc < len && bargv[bargc]; + bargv[++bargc] = strtok_r(NULL, " \t\r\n", &last)) + { + debug("%s %d: %s", msg, bargc, bargv[bargc]); + } + return (bargc); +} + +static void +parse_args(bool is_batchfile, bool config_only, int argc, char **argv) { + isc_result_t result; + isc_textregion_t tr; + bool firstarg = true; + dig_lookup_t *lookup = NULL; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + bool open_type_class = true; + char batchline[MXNAME]; + int bargc; + char *bargv[64]; + int rc; + char **rv; +#ifndef NOPOSIX + char *homedir; + char rcfile[PATH_MAX]; +#endif /* ifndef NOPOSIX */ + bool need_clone = true; + + /* + * The semantics for parsing the args is a bit complex; if + * we don't have a host yet, make the arg apply globally, + * otherwise make it apply to the latest host. This is + * a bit different than the previous versions, but should + * form a consistent user interface. + * + * First, create a "default lookup" which won't actually be used + * anywhere, except for cloning into new lookups + */ + + debug("parse_args()"); + if (!is_batchfile) { + debug("making new lookup"); + default_lookup = make_empty_lookup(); + default_lookup->adflag = true; + default_lookup->edns = DEFAULT_EDNS_VERSION; + default_lookup->sendcookie = true; + +#ifndef NOPOSIX + /* + * Treat ${HOME}/.digrc as a special batchfile + */ + INSIST(batchfp == NULL); + homedir = getenv("HOME"); + if (homedir != NULL && digrc) { + unsigned int n; + debug("digrc (open)"); + n = snprintf(rcfile, sizeof(rcfile), "%s/.digrc", + homedir); + if (n < sizeof(rcfile)) { + batchfp = fopen(rcfile, "r"); + } + } + if (batchfp != NULL) { + while (fgets(batchline, sizeof(batchline), batchfp) != + 0) + { + debug("config line %s", batchline); + bargc = split_batchline(batchline, bargv, 62, + ".digrc argv"); + bargv[0] = argv[0]; + argv0 = argv[0]; + parse_args(true, true, bargc, (char **)bargv); + } + fclose(batchfp); + } +#endif /* ifndef NOPOSIX */ + } + + if (is_batchfile && !config_only) { + /* Processing '-f batchfile'. */ + lookup = clone_lookup(default_lookup, true); + need_clone = false; + } else { + lookup = default_lookup; + } + + rc = argc; + rv = argv; + for (rc--, rv++; rc > 0; rc--, rv++) { + debug("main parsing %s", rv[0]); + if (strncmp(rv[0], "%", 1) == 0) { + break; + } + if (rv[0][0] == '@') { + if (is_batchfile && !config_only) { + addresscount = getaddresses(lookup, &rv[0][1], + &result); + if (addresscount == 0) { + fprintf(stderr, + "couldn't get address " + "for '%s': %s: skipping " + "lookup\n", + &rv[0][1], + isc_result_totext(result)); + if (ISC_LINK_LINKED(lookup, link)) { + ISC_LIST_DEQUEUE(lookup_list, + lookup, link); + } + destroy_lookup(lookup); + return; + } + } else { + addresscount = getaddresses(lookup, &rv[0][1], + NULL); + if (addresscount == 0) { + fatal("no valid addresses for '%s'\n", + &rv[0][1]); + } + } + } else if (rv[0][0] == '+') { + plus_option(&rv[0][1], is_batchfile, lookup); + } else if (rv[0][0] == '-') { + if (rc <= 1) { + if (dash_option(&rv[0][1], NULL, &lookup, + &open_type_class, &need_clone, + config_only, argc, argv, + &firstarg)) + { + rc--; + rv++; + } + } else { + if (dash_option(&rv[0][1], rv[1], &lookup, + &open_type_class, &need_clone, + config_only, argc, argv, + &firstarg)) + { + rc--; + rv++; + } + } + } else { + /* + * Anything which isn't an option + */ + if (open_type_class) { + if (strncasecmp(rv[0], "ixfr=", 5) == 0) { + rdtype = dns_rdatatype_ixfr; + result = ISC_R_SUCCESS; + } else { + tr.base = rv[0]; + tr.length = (unsigned int)strlen(rv[0]); + result = dns_rdatatype_fromtext( + &rdtype, + (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS && + rdtype == dns_rdatatype_ixfr) + { + fprintf(stderr, ";; Warning, " + "ixfr requires " + "a " + "serial " + "number\n"); + continue; + } + } + if (result == ISC_R_SUCCESS) { + if (lookup->rdtypeset) { + fprintf(stderr, ";; Warning, " + "extra type " + "option\n"); + } + if (rdtype == dns_rdatatype_ixfr) { + uint32_t serial; + lookup->rdtype = + dns_rdatatype_ixfr; + lookup->rdtypeset = true; + result = parse_uint(&serial, + &rv[0][5], + MAXSERIAL, + "serial " + "number"); + if (result != ISC_R_SUCCESS) { + fatal("Couldn't parse " + "serial number"); + } + lookup->ixfr_serial = serial; + lookup->section_question = + plusquest; + lookup->comments = pluscomm; + if (!lookup->tcp_mode_set) { + lookup->tcp_mode = true; + } + } else { + lookup->rdtype = rdtype; + lookup->rdtypeset = true; + if (rdtype == + dns_rdatatype_axfr) + { + lookup->section_question = + plusquest; + lookup->comments = + pluscomm; + } + if (rdtype == + dns_rdatatype_any && + !lookup->tcp_mode_set) + { + lookup->tcp_mode = true; + } + lookup->ixfr_serial = false; + } + continue; + } + result = dns_rdataclass_fromtext( + &rdclass, (isc_textregion_t *)&tr); + if (result == ISC_R_SUCCESS) { + if (lookup->rdclassset) { + fprintf(stderr, ";; Warning, " + "extra class " + "option\n"); + } + lookup->rdclass = rdclass; + lookup->rdclassset = true; + continue; + } + } + + if (!config_only) { + if (need_clone) { + lookup = clone_lookup(default_lookup, + true); + } + need_clone = true; + strlcpy(lookup->textname, rv[0], + sizeof(lookup->textname)); + lookup->trace_root = (lookup->trace || + lookup->ns_search_only); + lookup->new_search = true; + if (firstarg) { + printgreeting(argc, argv, lookup); + firstarg = false; + } + ISC_LIST_APPEND(lookup_list, lookup, link); + debug("looking up %s", lookup->textname); + } + /* XXX Error message */ + } + } + + /* + * If we have a batchfile, seed the lookup list with the + * first entry, then trust the callback in dighost_shutdown + * to get the rest + */ + char *filename = (char *)atomic_load(&batchname); + if ((filename != NULL) && !(is_batchfile)) { + if (strcmp(filename, "-") == 0) { + batchfp = stdin; + } else { + batchfp = fopen(filename, "r"); + } + if (batchfp == NULL) { + perror(filename); + if (exitcode < 8) { + exitcode = 8; + } + fatal("couldn't open specified batch file"); + } + /* XXX Remove code dup from shutdown code */ + next_line: + if (fgets(batchline, sizeof(batchline), batchfp) != 0) { + debug("batch line %s", batchline); + if (batchline[0] == '\r' || batchline[0] == '\n' || + batchline[0] == '#' || batchline[0] == ';') + { + goto next_line; + } + bargc = split_batchline(batchline, bargv, 14, + "batch argv"); + bargv[0] = argv[0]; + argv0 = argv[0]; + parse_args(true, false, bargc, (char **)bargv); + return; + } + return; + } + /* + * If no lookup specified, search for root + */ + if ((lookup_list.head == NULL) && !config_only) { + if (need_clone) { + lookup = clone_lookup(default_lookup, true); + } + need_clone = true; + lookup->trace_root = (lookup->trace || lookup->ns_search_only); + lookup->new_search = true; + strlcpy(lookup->textname, ".", sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_ns; + lookup->rdtypeset = true; + if (firstarg) { + printgreeting(argc, argv, lookup); + firstarg = false; + } + ISC_LIST_APPEND(lookup_list, lookup, link); + } + if (!need_clone) { + destroy_lookup(lookup); + } +} + +/* + * Callback from dighost.c to allow program-specific shutdown code. + * Here, we're possibly reading from a batch file, then shutting down + * for real if there's nothing in the batch file to read. + */ +static void +query_finished(void) { + char batchline[MXNAME]; + int bargc; + char *bargv[16]; + + if (atomic_load(&batchname) == 0) { + isc_app_shutdown(); + return; + } + + fflush(stdout); + if (feof(batchfp)) { + atomic_store(&batchname, 0); + isc_app_shutdown(); + if (batchfp != stdin) { + fclose(batchfp); + } + return; + } + + if (fgets(batchline, sizeof(batchline), batchfp) != 0) { + debug("batch line %s", batchline); + bargc = split_batchline(batchline, bargv, 14, "batch argv"); + bargv[0] = argv0; + parse_args(true, false, bargc, (char **)bargv); + start_lookup(); + } else { + atomic_store(&batchname, 0); + if (batchfp != stdin) { + fclose(batchfp); + } + isc_app_shutdown(); + return; + } +} + +static void +dig_error(const char *format, ...) { + va_list args; + + if (yaml) { + printf("-\n"); + printf(" type: DIG_ERROR\n"); + + /* + * Print an indent before a literal block quote. + * Note: this will break if used to print more than + * one line of text as only the first line would be + * indented. + */ + printf(" message: |\n"); + printf(" "); + } else { + printf(";; "); + } + + va_start(args, format); + vprintf(format, args); + va_end(args); + + if (!yaml) { + printf("\n"); + } +} + +static void +dig_warning(const char *format, ...) { + va_list args; + + if (!yaml) { + printf(";; "); + + va_start(args, format); + vprintf(format, args); + va_end(args); + + printf("\n"); + } +} + +static void +dig_comments(dig_lookup_t *lookup, const char *format, ...) { + va_list args; + + if (lookup->comments && !yaml) { + printf(";; "); + + va_start(args, format); + vprintf(format, args); + va_end(args); + + printf("\n"); + } +} + +void +dig_setup(int argc, char **argv) { + isc_result_t result; + + ISC_LIST_INIT(lookup_list); + ISC_LIST_INIT(server_list); + ISC_LIST_INIT(search_list); + + debug("dig_setup()"); + + /* setup dighost callbacks */ + dighost_printmessage = printmessage; + dighost_received = received; + dighost_trying = trying; + dighost_shutdown = query_finished; + dighost_error = dig_error; + dighost_warning = dig_warning; + dighost_comments = dig_comments; + + progname = argv[0]; + preparse_args(argc, argv); + + result = isc_app_start(); + check_result(result, "isc_app_start"); + + setup_libs(); + setup_system(ipv4only, ipv6only); +} + +void +dig_query_setup(bool is_batchfile, bool config_only, int argc, char **argv) { + debug("dig_query_setup"); + + parse_args(is_batchfile, config_only, argc, argv); + if (keyfile[0] != 0) { + setup_file_key(); + } else if (keysecret[0] != 0) { + setup_text_key(); + } + if (domainopt[0] != '\0') { + set_search_domain(domainopt); + usesearch = true; + } +} + +void +dig_startup() { + isc_result_t result; + + debug("dig_startup()"); + + result = isc_app_onrun(mctx, global_task, onrun_callback, NULL); + check_result(result, "isc_app_onrun"); + isc_app_run(); +} + +void +dig_query_start() { + start_lookup(); +} + +void +dig_shutdown() { + destroy_lookup(default_lookup); + if (atomic_load(&batchname) != 0) { + if (batchfp != stdin) { + fclose(batchfp); + } + atomic_store(&batchname, 0); + } + cancel_all(); + destroy_libs(); + isc_app_finish(); +} + +/*% Main processing routine for dig */ +int +main(int argc, char **argv) { + dig_setup(argc, argv); + dig_query_setup(false, false, argc, argv); + dig_startup(); + dig_shutdown(); + + return (exitcode); +} diff --git a/bin/dig/dig.rst b/bin/dig/dig.rst new file mode 100644 index 0000000..d27b1fd --- /dev/null +++ b/bin/dig/dig.rst @@ -0,0 +1,645 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. _man_dig: + +dig - DNS lookup utility +------------------------ + +Synopsis +~~~~~~~~ +:program:`dig` [@server] [**-b** address] [**-c** class] [**-f** filename] [**-k** filename] [**-m**] [**-p** port#] [**-q** name] [**-t** type] [**-v**] [**-x** addr] [**-y** [hmac:]name:key] [ [**-4**] | [**-6**] ] [name] [type] [class] [queryopt...] + +:program:`dig` [**-h**] + +:program:`dig` [global-queryopt...] [query...] + +Description +~~~~~~~~~~~ + +``dig`` is a flexible tool for interrogating DNS name servers. It +performs DNS lookups and displays the answers that are returned from the +name server(s) that were queried. Most DNS administrators use ``dig`` to +troubleshoot DNS problems because of its flexibility, ease of use, and +clarity of output. Other lookup tools tend to have less functionality +than ``dig``. + +Although ``dig`` is normally used with command-line arguments, it also +has a batch mode of operation for reading lookup requests from a file. A +brief summary of its command-line arguments and options is printed when +the ``-h`` option is given. The BIND 9 +implementation of ``dig`` allows multiple lookups to be issued from the +command line. + +Unless it is told to query a specific name server, ``dig`` tries each +of the servers listed in ``/etc/resolv.conf``. If no usable server +addresses are found, ``dig`` sends the query to the local host. + +When no command-line arguments or options are given, ``dig`` +performs an NS query for "." (the root). + +It is possible to set per-user defaults for ``dig`` via +``${HOME}/.digrc``. This file is read and any options in it are applied +before the command-line arguments. The ``-r`` option disables this +feature, for scripts that need predictable behavior. + +The IN and CH class names overlap with the IN and CH top-level domain +names. Either use the ``-t`` and ``-c`` options to specify the type and +class, use the ``-q`` to specify the domain name, or use "IN." and +"CH." when looking up these top-level domains. + +Simple Usage +~~~~~~~~~~~~ + +A typical invocation of ``dig`` looks like: + +:: + + dig @server name type + +where: + +``server`` + is the name or IP address of the name server to query. This can be an + IPv4 address in dotted-decimal notation or an IPv6 address in + colon-delimited notation. When the supplied ``server`` argument is a + hostname, ``dig`` resolves that name before querying that name + server. + + If no ``server`` argument is provided, ``dig`` consults + ``/etc/resolv.conf``; if an address is found there, it queries the + name server at that address. If either of the ``-4`` or ``-6`` + options are in use, then only addresses for the corresponding + transport are tried. If no usable addresses are found, ``dig`` + sends the query to the local host. The reply from the name server + that responds is displayed. + +``name`` + is the name of the resource record that is to be looked up. + +``type`` + indicates what type of query is required - ANY, A, MX, SIG, etc. + ``type`` can be any valid query type. If no ``type`` argument is + supplied, ``dig`` performs a lookup for an A record. + +Options +~~~~~~~ + +``-4`` + This option indicates that only IPv4 should be used. + +``-6`` + This option indicates that only IPv6 should be used. + +``-b address[#port]`` + This option sets the source IP address of the query. The ``address`` must be a + valid address on one of the host's network interfaces, or "0.0.0.0" + or "::". An optional port may be specified by appending ``#port``. + +``-c class`` + This option sets the query class. The default ``class`` is IN; other classes are + HS for Hesiod records or CH for Chaosnet records. + +``-f file`` + This option sets batch mode, in which ``dig`` reads a list of lookup requests to process from + the given ``file``. Each line in the file should be organized in the + same way it would be presented as a query to ``dig`` using the + command-line interface. + +``-k keyfile`` + This option tells ``named`` to sign queries using TSIG using a key read from the given file. Key + files can be generated using ``tsig-keygen``. When using TSIG + authentication with ``dig``, the name server that is queried needs to + know the key and algorithm that is being used. In BIND, this is done + by providing appropriate ``key`` and ``server`` statements in + ``named.conf``. + +``-m`` + This option enables memory usage debugging. + +``-p port`` + This option sends the query to a non-standard port on the server, instead of the + default port 53. This option is used to test a name server that + has been configured to listen for queries on a non-standard port + number. + +``-q name`` + This option specifies the domain name to query. This is useful to distinguish the ``name`` + from other arguments. + +``-r`` + This option indicates that options from ``${HOME}/.digrc`` should not be read. This is useful for + scripts that need predictable behavior. + +``-t type`` + This option indicates the resource record type to query, which can be any valid query type. If + it is a resource record type supported in BIND 9, it can be given by + the type mnemonic (such as ``NS`` or ``AAAA``). The default query type is + ``A``, unless the ``-x`` option is supplied to indicate a reverse + lookup. A zone transfer can be requested by specifying a type of + AXFR. When an incremental zone transfer (IXFR) is required, set the + ``type`` to ``ixfr=N``. The incremental zone transfer contains + all changes made to the zone since the serial number in the zone's + SOA record was ``N``. + + All resource record types can be expressed as ``TYPEnn``, where ``nn`` is + the number of the type. If the resource record type is not supported + in BIND 9, the result is displayed as described in :rfc:`3597`. + +``-u`` + This option indicates that print query times should be provided in microseconds instead of milliseconds. + +``-v`` + This option prints the version number and exits. + +``-x addr`` + This option sets simplified reverse lookups, for mapping addresses to names. The + ``addr`` is an IPv4 address in dotted-decimal notation, or a + colon-delimited IPv6 address. When the ``-x`` option is used, there is no + need to provide the ``name``, ``class``, and ``type`` arguments. + ``dig`` automatically performs a lookup for a name like + ``94.2.0.192.in-addr.arpa`` and sets the query type and class to PTR + and IN respectively. IPv6 addresses are looked up using nibble format + under the IP6.ARPA domain. + +``-y [hmac:]keyname:secret`` + This option signs queries using TSIG with the given authentication key. + ``keyname`` is the name of the key, and ``secret`` is the + base64-encoded shared secret. ``hmac`` is the name of the key algorithm; + valid choices are ``hmac-md5``, ``hmac-sha1``, ``hmac-sha224``, + ``hmac-sha256``, ``hmac-sha384``, or ``hmac-sha512``. If ``hmac`` is + not specified, the default is ``hmac-md5``; if MD5 was disabled, the default is + ``hmac-sha256``. + +.. note:: Only the ``-k`` option should be used, rather than the ``-y`` option, + because with ``-y`` the shared secret is supplied as a command-line + argument in clear text. This may be visible in the output from ``ps1`` or + in a history file maintained by the user's shell. + +Query Options +~~~~~~~~~~~~~ + +``dig`` provides a number of query options which affect the way in which +lookups are made and the results displayed. Some of these set or reset +flag bits in the query header, some determine which sections of the +answer get printed, and others determine the timeout and retry +strategies. + +Each query option is identified by a keyword preceded by a plus sign +(``+``). Some keywords set or reset an option; these may be preceded by +the string ``no`` to negate the meaning of that keyword. Other keywords +assign values to options, like the timeout interval. They have the form +``+keyword=value``. Keywords may be abbreviated, provided the +abbreviation is unambiguous; for example, ``+cd`` is equivalent to +``+cdflag``. The query options are: + +``+[no]aaflag`` + This option is a synonym for ``+[no]aaonly``. + +``+[no]aaonly`` + This option sets the ``aa`` flag in the query. + +``+[no]additional`` + This option displays [or does not display] the additional section of a reply. The + default is to display it. + +``+[no]adflag`` + This option sets [or does not set] the AD (authentic data) bit in the query. This + requests the server to return whether all of the answer and authority + sections have been validated as secure, according to the security + policy of the server. ``AD=1`` indicates that all records have been + validated as secure and the answer is not from a OPT-OUT range. ``AD=0`` + indicates that some part of the answer was insecure or not validated. + This bit is set by default. + +``+[no]all`` + This option sets or clears all display flags. + +``+[no]answer`` + This option displays [or does not display] the answer section of a reply. The default + is to display it. + +``+[no]authority`` + This option displays [or does not display] the authority section of a reply. The + default is to display it. + +``+[no]badcookie`` + This option retries the lookup with a new server cookie if a BADCOOKIE response is + received. + +``+[no]besteffort`` + This option attempts to display the contents of messages which are malformed. The + default is to not display malformed answers. + +``+bufsize[=B]`` + This option sets the UDP message buffer size advertised using EDNS0 + to ``B`` bytes. The maximum and minimum sizes of this buffer are + 65535 and 0, respectively. ``+bufsize=0`` disables EDNS (use + ``+bufsize=0 +edns`` to send an EDNS message with an advertised size + of 0 bytes). ``+bufsize`` restores the default buffer size. + +``+[no]cdflag`` + This option sets [or does not set] the CD (checking disabled) bit in the query. This + requests the server to not perform DNSSEC validation of responses. + +``+[no]class`` + This option displays [or does not display] the CLASS when printing the record. + +``+[no]cmd`` + This option toggles the printing of the initial comment in the output, identifying the + version of ``dig`` and the query options that have been applied. This option + always has a global effect; it cannot be set globally and then overridden on a + per-lookup basis. The default is to print this comment. + +``+[no]comments`` + This option toggles the display of some comment lines in the output, with + information about the packet header and OPT pseudosection, and the names of + the response section. The default is to print these comments. + + Other types of comments in the output are not affected by this option, but + can be controlled using other command-line switches. These include + ``+[no]cmd``, ``+[no]question``, ``+[no]stats``, and ``+[no]rrcomments``. + +``+[no]cookie=####`` + This option sends [or does not send] a COOKIE EDNS option, with an optional value. Replaying a COOKIE + from a previous response allows the server to identify a previous + client. The default is ``+cookie``. + + ``+cookie`` is also set when ``+trace`` is set to better emulate the + default queries from a nameserver. + +``+[no]crypto`` + This option toggles the display of cryptographic fields in DNSSEC records. The + contents of these fields are unnecessary for debugging most DNSSEC + validation failures and removing them makes it easier to see the + common failures. The default is to display the fields. When omitted, + they are replaced by the string ``[omitted]`` or, in the DNSKEY case, the + key ID is displayed as the replacement, e.g. ``[ key id = value ]``. + +``+[no]defname`` + This option, which is deprecated, is treated as a synonym for ``+[no]search``. + +``+[no]dnssec`` + This option requests that DNSSEC records be sent by setting the DNSSEC OK (DO) bit in + the OPT record in the additional section of the query. + +``+domain=somename`` + This option sets the search list to contain the single domain ``somename``, as if + specified in a ``domain`` directive in ``/etc/resolv.conf``, and + enables search list processing as if the ``+search`` option were + given. + +``+dscp=value`` + This option sets the DSCP code point to be used when sending the query. Valid DSCP + code points are in the range [0...63]. By default no code point is + explicitly set. + +``+[no]edns[=#]`` + This option specifies the EDNS version to query with. Valid values are 0 to 255. + Setting the EDNS version causes an EDNS query to be sent. + ``+noedns`` clears the remembered EDNS version. EDNS is set to 0 by + default. + +``+[no]ednsflags[=#]`` + This option sets the must-be-zero EDNS flags bits (Z bits) to the specified value. + Decimal, hex, and octal encodings are accepted. Setting a named flag + (e.g., DO) is silently ignored. By default, no Z bits are set. + +``+[no]ednsnegotiation`` + This option enables/disables EDNS version negotiation. By default, EDNS version + negotiation is enabled. + +``+[no]ednsopt[=code[:value]]`` + This option specifies the EDNS option with code point ``code`` and an optional payload + of ``value`` as a hexadecimal string. ``code`` can be either an EDNS + option name (for example, ``NSID`` or ``ECS``) or an arbitrary + numeric value. ``+noednsopt`` clears the EDNS options to be sent. + +``+[no]expire`` + This option sends an EDNS Expire option. + +``+[no]fail`` + This option indicates that ``named`` should try [or not try] the next server if a SERVFAIL is received. The default is + to not try the next server, which is the reverse of normal stub + resolver behavior. + +``+[no]header-only`` + This option sends a query with a DNS header without a question section. The + default is to add a question section. The query type and query name + are ignored when this is set. + +``+[no]identify`` + This option shows [or does not show] the IP address and port number that supplied + the answer, when the ``+short`` option is enabled. If short form + answers are requested, the default is not to show the source address + and port number of the server that provided the answer. + +``+[no]idnin`` + This option processes [or does not process] IDN domain names on input. This requires + ``IDN SUPPORT`` to have been enabled at compile time. + + The default is to process IDN input when standard output is a tty. + The IDN processing on input is disabled when ``dig`` output is redirected + to files, pipes, and other non-tty file descriptors. + +``+[no]idnout`` + This option converts [or does not convert] puny code on output. This requires + ``IDN SUPPORT`` to have been enabled at compile time. + + The default is to process puny code on output when standard output is + a tty. The puny code processing on output is disabled when ``dig`` output + is redirected to files, pipes, and other non-tty file descriptors. + +``+[no]ignore`` + This option ignores [or does not ignore] truncation in UDP responses instead of retrying with TCP. By + default, TCP retries are performed. + +``+[no]keepalive`` + This option sends [or does not send] an EDNS Keepalive option. + +``+[no]keepopen`` + This option keeps [or does not keep] the TCP socket open between queries, and reuses it rather than + creating a new TCP socket for each lookup. The default is + ``+nokeepopen``. + +``+[no]mapped`` + This option allows [or does not allow] mapped IPv4-over-IPv6 addresses to be used. The default is + ``+mapped``. + +``+[no]multiline`` + This option prints [or does not print] records, like the SOA records, in a verbose multi-line format + with human-readable comments. The default is to print each record on + a single line to facilitate machine parsing of the ``dig`` output. + +``+ndots=D`` + This option sets the number of dots (``D``) that must appear in ``name`` for + it to be considered absolute. The default value is that defined using + the ``ndots`` statement in ``/etc/resolv.conf``, or 1 if no ``ndots`` + statement is present. Names with fewer dots are interpreted as + relative names, and are searched for in the domains listed in the + ``search`` or ``domain`` directive in ``/etc/resolv.conf`` if + ``+search`` is set. + +``+[no]nsid`` + When enabled, this option includes an EDNS name server ID request when sending a query. + +``+[no]nssearch`` + When this option is set, ``dig`` attempts to find the authoritative + name servers for the zone containing the name being looked up, and + display the SOA record that each name server has for the zone. + Addresses of servers that did not respond are also printed. + +``+[no]onesoa`` + When enabled, this option prints only one (starting) SOA record when performing an AXFR. The + default is to print both the starting and ending SOA records. + +``+[no]opcode=value`` + When enabled, this option sets (restores) the DNS message opcode to the specified value. The + default value is QUERY (0). + +``+padding=value`` + This option pads the size of the query packet using the EDNS Padding option to + blocks of ``value`` bytes. For example, ``+padding=32`` causes a + 48-byte query to be padded to 64 bytes. The default block size is 0, + which disables padding; the maximum is 512. Values are ordinarily + expected to be powers of two, such as 128; however, this is not + mandatory. Responses to padded queries may also be padded, but only + if the query uses TCP or DNS COOKIE. + +``+[no]qr`` + This option toggles the display of the query message as it is sent. By default, the query + is not printed. + +``+[no]question`` + This option toggles the display of the question section of a query when an answer is + returned. The default is to print the question section as a comment. + +``+[no]raflag`` + This option sets [or does not set] the RA (Recursion Available) bit in the query. The + default is ``+noraflag``. This bit is ignored by the server for + QUERY. + +``+[no]rdflag`` + This option is a synonym for ``+[no]recurse``. + +``+[no]recurse`` + This option toggles the setting of the RD (recursion desired) bit in the query. + This bit is set by default, which means ``dig`` normally sends + recursive queries. Recursion is automatically disabled when the + ``+nssearch`` or ``+trace`` query option is used. + +``+retry=T`` + This option sets the number of times to retry UDP and TCP queries to server to ``T`` + instead of the default, 2. Unlike ``+tries``, this does not include + the initial query. + +``+[no]rrcomments`` + This option toggles the display of per-record comments in the output (for example, + human-readable key information about DNSKEY records). The default is + not to print record comments unless multiline mode is active. + +``+[no]search`` + This option uses [or does not use] the search list defined by the searchlist or domain + directive in ``resolv.conf``, if any. The search list is not used by + default. + + ``ndots`` from ``resolv.conf`` (default 1), which may be overridden by + ``+ndots``, determines whether the name is treated as relative + and hence whether a search is eventually performed. + +``+[no]short`` + This option toggles whether a terse answer is provided. The default is to print the answer in a verbose + form. This option always has a global effect; it cannot be set globally and + then overridden on a per-lookup basis. + +``+[no]showsearch`` + This option performs [or does not perform] a search showing intermediate results. + +``+[no]sigchase`` + This feature is now obsolete and has been removed; use ``delv`` + instead. + +``+split=W`` + This option splits long hex- or base64-formatted fields in resource records into + chunks of ``W`` characters (where ``W`` is rounded up to the nearest + multiple of 4). ``+nosplit`` or ``+split=0`` causes fields not to be + split at all. The default is 56 characters, or 44 characters when + multiline mode is active. + +``+[no]stats`` + This option toggles the printing of statistics: when the query was made, the size of the + reply, etc. The default behavior is to print the query statistics as a + comment after each lookup. + +``+[no]subnet=addr[/prefix-length]`` + This option sends [or does not send] an EDNS CLIENT-SUBNET option with the specified IP + address or network prefix. + + ``dig +subnet=0.0.0.0/0``, or simply ``dig +subnet=0`` for short, + sends an EDNS CLIENT-SUBNET option with an empty address and a source + prefix-length of zero, which signals a resolver that the client's + address information must *not* be used when resolving this query. + +``+[no]tcflag`` + This option sets [or does not set] the TC (TrunCation) bit in the query. The default is + ``+notcflag``. This bit is ignored by the server for QUERY. + +``+[no]tcp`` + This option uses [or does not use] TCP when querying name servers. + The default behavior is to use UDP unless a type ``any`` or + ``ixfr=N`` query is requested, in which case the default is TCP. + AXFR queries always use TCP. To prevent retry over TCP when TC=1 + is returned from a UDP query, use ``+ignore``. + +``+timeout=T`` + This option sets the timeout for a query to ``T`` seconds. The default timeout is + 5 seconds. An attempt to set ``T`` to less than 1 is silently set to 1. + +``+[no]topdown`` + This feature is related to ``dig +sigchase``, which is obsolete and + has been removed. Use ``delv`` instead. + +``+[no]trace`` + This option toggles tracing of the delegation path from the root name servers for + the name being looked up. Tracing is disabled by default. When + tracing is enabled, ``dig`` makes iterative queries to resolve the + name being looked up. It follows referrals from the root servers, + showing the answer from each server that was used to resolve the + lookup. + + If ``@server`` is also specified, it affects only the initial query for + the root zone name servers. + + ``+dnssec`` is also set when ``+trace`` is set, to better emulate the + default queries from a name server. + +``+tries=T`` + This option sets the number of times to try UDP and TCP queries to server to ``T`` + instead of the default, 3. If ``T`` is less than or equal to zero, + the number of tries is silently rounded up to 1. + +``+trusted-key=####`` + This option formerly specified trusted keys for use with ``dig +sigchase``. This + feature is now obsolete and has been removed; use ``delv`` instead. + +``+[no]ttlid`` + This option displays [or does not display] the TTL when printing the record. + +``+[no]ttlunits`` + This option displays [or does not display] the TTL in friendly human-readable time + units of ``s``, ``m``, ``h``, ``d``, and ``w``, representing seconds, minutes, + hours, days, and weeks. This implies ``+ttlid``. + +``+[no]unexpected`` + This option accepts [or does not accept] answers from unexpected sources. By default, ``dig`` + will not accept a reply from a source other than the one to which it sent the + query. + +``+[no]unknownformat`` + This option prints all RDATA in unknown RR type presentation format (:rfc:`3597`). + The default is to print RDATA for known types in the type's + presentation format. + +``+[no]vc`` + This option uses [or does not use] TCP when querying name servers. This alternate + syntax to ``+[no]tcp`` is provided for backwards compatibility. The + ``vc`` stands for "virtual circuit." + +``+[no]yaml`` + When enabled, this option prints the responses (and, if ``+qr`` is in use, also the + outgoing queries) in a detailed YAML format. + +``+[no]zflag`` + This option sets [or does not set] the last unassigned DNS header flag in a DNS query. + This flag is off by default. + +Multiple Queries +~~~~~~~~~~~~~~~~ + +The BIND 9 implementation of ``dig`` supports specifying multiple +queries on the command line (in addition to supporting the ``-f`` batch +file option). Each of those queries can be supplied with its own set of +flags, options, and query options. + +In this case, each ``query`` argument represents an individual query in +the command-line syntax described above. Each consists of any of the +standard options and flags, the name to be looked up, an optional query +type and class, and any query options that should be applied to that +query. + +A global set of query options, which should be applied to all queries, +can also be supplied. These global query options must precede the first +tuple of name, class, type, options, flags, and query options supplied +on the command line. Any global query options (except ``+[no]cmd`` and +``+[no]short`` options) can be overridden by a query-specific set of +query options. For example: + +:: + + dig +qr www.isc.org any -x 127.0.0.1 isc.org ns +noqr + +shows how ``dig`` can be used from the command line to make three +lookups: an ANY query for ``www.isc.org``, a reverse lookup of 127.0.0.1, +and a query for the NS records of ``isc.org``. A global query option of +``+qr`` is applied, so that ``dig`` shows the initial query it made for +each lookup. The final query has a local query option of ``+noqr`` which +means that ``dig`` does not print the initial query when it looks up the +NS records for ``isc.org``. + +IDN Support +~~~~~~~~~~~ + +If ``dig`` has been built with IDN (internationalized domain name) +support, it can accept and display non-ASCII domain names. ``dig`` +appropriately converts character encoding of a domain name before sending +a request to a DNS server or displaying a reply from the server. +To turn off IDN support, use the parameters +``+noidnin`` and ``+noidnout``, or define the ``IDN_DISABLE`` environment +variable. + +Return Codes +~~~~~~~~~~~~ + +``dig`` return codes are: + +``0`` + DNS response received, including NXDOMAIN status + +``1`` + Usage error + +``8`` + Couldn't open batch file + +``9`` + No reply from server + +``10`` + Internal error + +Files +~~~~~ + +``/etc/resolv.conf`` + +``${HOME}/.digrc`` + +See Also +~~~~~~~~ + +:manpage:`delv(1)`, :manpage:`host(1)`, :manpage:`named(8)`, :manpage:`dnssec-keygen(8)`, :rfc:`1035`. + +Bugs +~~~~ + +There are probably too many query options. diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c new file mode 100644 index 0000000..ac7e1fb --- /dev/null +++ b/bin/dig/dighost.c @@ -0,0 +1,4604 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file + * \note + * Notice to programmers: Do not use this code as an example of how to + * use the ISC library to perform DNS lookups. Dig and Host both operate + * on the request level, since they allow fine-tuning of output and are + * intended as debugging tools. As a result, they perform many of the + * functions which could be better handled using the dns_resolver + * functions in most applications. + */ + +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif /* ifdef HAVE_LOCALE_H */ + +#ifdef HAVE_LIBIDN2 +#include <idn2.h> +#endif /* HAVE_LIBIDN2 */ + +#include <isc/app.h> +#include <isc/base64.h> +#include <isc/file.h> +#include <isc/hex.h> +#include <isc/lang.h> +#include <isc/log.h> +#include <isc/managers.h> +#include <isc/netaddr.h> +#include <isc/netdb.h> +#include <isc/nonce.h> +#include <isc/parseint.h> +#include <isc/print.h> +#include <isc/random.h> +#include <isc/result.h> +#include <isc/safe.h> +#include <isc/serial.h> +#include <isc/sockaddr.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/timer.h> +#include <isc/types.h> +#include <isc/util.h> + +#include <pk11/site.h> + +#include <dns/byaddr.h> +#include <dns/fixedname.h> +#include <dns/log.h> +#include <dns/message.h> +#include <dns/name.h> +#include <dns/opcode.h> +#include <dns/rcode.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdatalist.h> +#include <dns/rdataset.h> +#include <dns/rdatastruct.h> +#include <dns/rdatatype.h> +#include <dns/result.h> +#include <dns/tsig.h> + +#include <dst/dst.h> +#include <dst/result.h> + +#include <isccfg/namedconf.h> + +#include <irs/resconf.h> + +#include <bind9/getaddresses.h> + +#include <dig/dig.h> + +#if USE_PKCS11 +#include <pk11/result.h> +#endif /* if USE_PKCS11 */ + +#if !defined(NS_INADDRSZ) +#define NS_INADDRSZ 4 +#endif /* if !defined(NS_INADDRSZ) */ + +#if !defined(NS_IN6ADDRSZ) +#define NS_IN6ADDRSZ 16 +#endif /* if !defined(NS_IN6ADDRSZ) */ + +#if HAVE_SETLOCALE +#define systemlocale(l) (void)setlocale(l, "") +#define resetlocale(l) (void)setlocale(l, "C") +#else +#define systemlocale(l) +#define resetlocale(l) +#endif /* HAVE_SETLOCALE */ + +dig_lookuplist_t lookup_list; +dig_serverlist_t server_list; +dig_searchlistlist_t search_list; + +bool check_ra = false, have_ipv4 = false, have_ipv6 = false, + specified_source = false, free_now = false, cancel_now = false, + usesearch = false, showsearch = false, is_dst_up = false, + keep_open = false, verbose = false, yaml = false; +in_port_t port = 53; +unsigned int timeout = 0; +unsigned int extrabytes; +isc_mem_t *mctx = NULL; +isc_log_t *lctx = NULL; +isc_nm_t *netmgr = NULL; +isc_taskmgr_t *taskmgr = NULL; +isc_task_t *global_task = NULL; +isc_timermgr_t *timermgr = NULL; +isc_socketmgr_t *socketmgr = NULL; +isc_sockaddr_t bind_address; +isc_sockaddr_t bind_any; +int sendcount = 0; +int recvcount = 0; +int sockcount = 0; +int ndots = -1; +int tries = 3; +int lookup_counter = 0; + +static char servercookie[256]; + +#ifdef HAVE_LIBIDN2 +static void +idn_locale_to_ace(const char *src, char *dst, size_t dstlen); +static void +idn_ace_to_locale(const char *src, char **dst); +static isc_result_t +idn_output_filter(isc_buffer_t *buffer, unsigned int used_org); +#endif /* HAVE_LIBIDN2 */ + +isc_socket_t *keep = NULL; +isc_sockaddr_t keepaddr; + +/*% + * Exit Codes: + * + *\li 0 Everything went well, including things like NXDOMAIN + *\li 1 Usage error + *\li 7 Got too many RR's or Names + *\li 8 Couldn't open batch file + *\li 9 No reply from server + *\li 10 Internal error + */ +int exitcode = 0; +int fatalexit = 0; +char keynametext[MXNAME]; +char keyfile[MXNAME] = ""; +char keysecret[MXNAME] = ""; +unsigned char cookie_secret[33]; +unsigned char cookie[8]; +const dns_name_t *hmacname = NULL; +unsigned int digestbits = 0; +isc_buffer_t *namebuf = NULL; +dns_tsigkey_t *tsigkey = NULL; +bool validated = true; +bool debugging = false; +bool debugtiming = false; +bool memdebugging = false; +char *progname = NULL; +isc_mutex_t lookup_lock; +dig_lookup_t *current_lookup = NULL; + +#define DIG_MAX_ADDRESSES 20 + +/*% + * Apply and clear locks at the event level in global task. + * Can I get rid of these using shutdown events? XXX + */ +#define LOCK_LOOKUP \ + { \ + debug("lock_lookup %s:%d", __FILE__, __LINE__); \ + check_result(isc_mutex_lock((&lookup_lock)), "isc_mutex_" \ + "lock"); \ + debug("success"); \ + } +#define UNLOCK_LOOKUP \ + { \ + debug("unlock_lookup %s:%d", __FILE__, __LINE__); \ + check_result(isc_mutex_unlock((&lookup_lock)), "isc_mutex_" \ + "unlock"); \ + } + +static void +default_warnerr(const char *format, ...) { + va_list args; + + printf(";; "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); +} + +static void +default_comments(dig_lookup_t *lookup, const char *format, ...) { + va_list args; + + if (lookup->comments) { + printf(";; "); + va_start(args, format); + vprintf(format, args); + va_end(args); + printf("\n"); + } +} + +/* dynamic callbacks */ + +isc_result_t (*dighost_printmessage)(dig_query_t *query, + const isc_buffer_t *msgbuf, + dns_message_t *msg, bool headers); + +void (*dighost_error)(const char *format, ...) = default_warnerr; + +void (*dighost_warning)(const char *format, ...) = default_warnerr; + +void (*dighost_comments)(dig_lookup_t *lookup, const char *format, + ...) = default_comments; + +void (*dighost_received)(unsigned int bytes, isc_sockaddr_t *from, + dig_query_t *query); + +void (*dighost_trying)(char *frm, dig_lookup_t *lookup); + +void (*dighost_shutdown)(void); + +/* forward declarations */ + +static void +cancel_lookup(dig_lookup_t *lookup); + +static void +recv_done(isc_task_t *task, isc_event_t *event); + +static void +send_udp(dig_query_t *query); + +static void +connect_timeout(isc_task_t *task, isc_event_t *event); + +static void +launch_next_query(dig_query_t *query, bool include_question); + +static void +check_next_lookup(dig_lookup_t *lookup); + +static bool +next_origin(dig_lookup_t *oldlookup); + +static int +count_dots(char *string) { + char *s; + int i = 0; + + s = string; + while (*s != '\0') { + if (*s == '.') { + i++; + } + s++; + } + return (i); +} + +static void +hex_dump(isc_buffer_t *b) { + unsigned int len, i; + isc_region_t r; + + isc_buffer_usedregion(b, &r); + + printf("%u bytes\n", r.length); + for (len = 0; len < r.length; len++) { + printf("%02x ", r.base[len]); + if (len % 16 == 15) { + fputs(" ", stdout); + for (i = len - 15; i <= len; i++) { + if (r.base[i] >= '!' && r.base[i] <= '}') { + putchar(r.base[i]); + } else { + putchar('.'); + } + } + printf("\n"); + } + } + if (len % 16 != 0) { + for (i = len; (i % 16) != 0; i++) { + fputs(" ", stdout); + } + fputs(" ", stdout); + for (i = ((len >> 4) << 4); i < len; i++) { + if (r.base[i] >= '!' && r.base[i] <= '}') { + putchar(r.base[i]); + } else { + putchar('.'); + } + } + printf("\n"); + } +} + +/*% + * Append 'len' bytes of 'text' at '*p', failing with + * ISC_R_NOSPACE if that would advance p past 'end'. + */ +static isc_result_t +append(const char *text, size_t len, char **p, char *end) { + if (*p + len > end) { + return (ISC_R_NOSPACE); + } + memmove(*p, text, len); + *p += len; + return (ISC_R_SUCCESS); +} + +static isc_result_t +reverse_octets(const char *in, char **p, char *end) { + const char *dot = strchr(in, '.'); + size_t len; + if (dot != NULL) { + isc_result_t result; + result = reverse_octets(dot + 1, p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + result = append(".", 1, p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + len = (int)(dot - in); + } else { + len = (int)strlen(in); + } + return (append(in, len, p, end)); +} + +isc_result_t +get_reverse(char *reverse, size_t len, char *value, bool strict) { + int r; + isc_result_t result; + isc_netaddr_t addr; + + addr.family = AF_INET6; + r = inet_pton(AF_INET6, value, &addr.type.in6); + if (r > 0) { + /* This is a valid IPv6 address. */ + dns_fixedname_t fname; + dns_name_t *name; + unsigned int options = 0; + + name = dns_fixedname_initname(&fname); + result = dns_byaddr_createptrname(&addr, options, name); + if (result != ISC_R_SUCCESS) { + return (result); + } + dns_name_format(name, reverse, (unsigned int)len); + return (ISC_R_SUCCESS); + } else { + /* + * Not a valid IPv6 address. Assume IPv4. + * If 'strict' is not set, construct the + * in-addr.arpa name by blindly reversing + * octets whether or not they look like integers, + * so that this can be used for RFC2317 names + * and such. + */ + char *p = reverse; + char *end = reverse + len; + if (strict && inet_pton(AF_INET, value, &addr.type.in) != 1) { + return (DNS_R_BADDOTTEDQUAD); + } + result = reverse_octets(value, &p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + /* Append .in-addr.arpa. and a terminating NUL. */ + result = append(".in-addr.arpa.", 15, &p, end); + if (result != ISC_R_SUCCESS) { + return (result); + } + return (ISC_R_SUCCESS); + } +} + +void (*dighost_pre_exit_hook)(void) = NULL; + +#if TARGET_OS_IPHONE +void +warn(const char *format, ...) { + va_list args; + + fflush(stdout); + fprintf(stderr, ";; Warning: "); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} +#else /* if TARGET_OS_IPHONE */ +void +warn(const char *format, ...) { + va_list args; + + fflush(stdout); + fprintf(stderr, "%s: ", progname); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} +#endif /* if TARGET_OS_IPHONE */ + +void +digexit(void) { + if (exitcode < 10) { + exitcode = 10; + } + if (fatalexit != 0) { + exitcode = fatalexit; + } + if (dighost_pre_exit_hook != NULL) { + dighost_pre_exit_hook(); + } + exit(exitcode); +} + +void +fatal(const char *format, ...) { + va_list args; + + fflush(stdout); + fprintf(stderr, "%s: ", progname); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + digexit(); +} + +void +debug(const char *format, ...) { + va_list args; + isc_time_t t; + + if (debugging) { + fflush(stdout); + if (debugtiming) { + TIME_NOW(&t); + fprintf(stderr, "%u.%06u: ", isc_time_seconds(&t), + isc_time_nanoseconds(&t) / 1000); + } + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + } +} + +void +check_result(isc_result_t result, const char *msg) { + if (result != ISC_R_SUCCESS) { + fatal("%s: %s", msg, isc_result_totext(result)); + } +} + +/*% + * Create a server structure, which is part of the lookup structure. + * This is little more than a linked list of servers to query in hopes + * of finding the answer the user is looking for + */ +dig_server_t * +make_server(const char *servname, const char *userarg) { + dig_server_t *srv; + + REQUIRE(servname != NULL); + + debug("make_server(%s)", servname); + srv = isc_mem_allocate(mctx, sizeof(struct dig_server)); + strlcpy(srv->servername, servname, MXNAME); + strlcpy(srv->userarg, userarg, MXNAME); + ISC_LINK_INIT(srv, link); + return (srv); +} + +/*% + * Create a copy of the server list from the resolver configuration structure. + * The dest list must have already had ISC_LIST_INIT applied. + */ +static void +get_server_list(irs_resconf_t *resconf) { + isc_sockaddrlist_t *servers; + isc_sockaddr_t *sa; + dig_server_t *newsrv; + char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + + sizeof("%4000000000")]; + debug("get_server_list()"); + servers = irs_resconf_getnameservers(resconf); + for (sa = ISC_LIST_HEAD(*servers); sa != NULL; + sa = ISC_LIST_NEXT(sa, link)) + { + int pf = isc_sockaddr_pf(sa); + isc_netaddr_t na; + isc_result_t result; + isc_buffer_t b; + + if (pf == AF_INET && !have_ipv4) { + continue; + } + if (pf == AF_INET6 && !have_ipv6) { + continue; + } + + isc_buffer_init(&b, tmp, sizeof(tmp)); + isc_netaddr_fromsockaddr(&na, sa); + result = isc_netaddr_totext(&na, &b); + if (result != ISC_R_SUCCESS) { + continue; + } + isc_buffer_putuint8(&b, 0); + if (pf == AF_INET6 && na.zone != 0) { + char buf[sizeof("%4000000000")]; + snprintf(buf, sizeof(buf), "%%%u", na.zone); + strlcat(tmp, buf, sizeof(tmp)); + } + newsrv = make_server(tmp, tmp); + ISC_LINK_INIT(newsrv, link); + ISC_LIST_APPEND(server_list, newsrv, link); + } +} + +void +flush_server_list(void) { + dig_server_t *s, *ps; + + debug("flush_server_list()"); + s = ISC_LIST_HEAD(server_list); + while (s != NULL) { + ps = s; + s = ISC_LIST_NEXT(s, link); + ISC_LIST_DEQUEUE(server_list, ps, link); + isc_mem_free(mctx, ps); + } +} + +void +set_nameserver(char *opt) { + isc_result_t result; + isc_sockaddr_t sockaddrs[DIG_MAX_ADDRESSES]; + isc_netaddr_t netaddr; + int count, i; + dig_server_t *srv; + char tmp[ISC_NETADDR_FORMATSIZE]; + + if (opt == NULL) { + return; + } + + result = bind9_getaddresses(opt, 0, sockaddrs, DIG_MAX_ADDRESSES, + &count); + if (result != ISC_R_SUCCESS) { + fatal("couldn't get address for '%s': %s", opt, + isc_result_totext(result)); + } + + flush_server_list(); + + for (i = 0; i < count; i++) { + isc_netaddr_fromsockaddr(&netaddr, &sockaddrs[i]); + isc_netaddr_format(&netaddr, tmp, sizeof(tmp)); + srv = make_server(tmp, opt); + if (srv == NULL) { + fatal("memory allocation failure"); + } + ISC_LIST_APPEND(server_list, srv, link); + } +} + +/*% + * Produce a cloned server list. The dest list must have already had + * ISC_LIST_INIT applied. + */ +void +clone_server_list(dig_serverlist_t src, dig_serverlist_t *dest) { + dig_server_t *srv, *newsrv; + + debug("clone_server_list()"); + srv = ISC_LIST_HEAD(src); + while (srv != NULL) { + newsrv = make_server(srv->servername, srv->userarg); + ISC_LINK_INIT(newsrv, link); + ISC_LIST_ENQUEUE(*dest, newsrv, link); + srv = ISC_LIST_NEXT(srv, link); + } +} + +/*% + * Create an empty lookup structure, which holds all the information needed + * to get an answer to a user's question. This structure contains two + * linked lists: the server list (servers to query) and the query list + * (outstanding queries which have been made to the listed servers). + */ +dig_lookup_t * +make_empty_lookup(void) { + dig_lookup_t *looknew; + + debug("make_empty_lookup()"); + + INSIST(!free_now); + + looknew = isc_mem_allocate(mctx, sizeof(struct dig_lookup)); + looknew->pending = true; + looknew->textname[0] = 0; + looknew->cmdline[0] = 0; + looknew->rdtype = dns_rdatatype_a; + looknew->qrdtype = dns_rdatatype_a; + looknew->rdclass = dns_rdataclass_in; + looknew->rdtypeset = false; + looknew->rdclassset = false; + looknew->sendspace = NULL; + looknew->sendmsg = NULL; + looknew->name = NULL; + looknew->oname = NULL; + looknew->xfr_q = NULL; + looknew->current_query = NULL; + looknew->doing_xfr = false; + looknew->ixfr_serial = 0; + looknew->trace = false; + looknew->trace_root = false; + looknew->identify = false; + looknew->identify_previous_line = false; + looknew->ignore = false; + looknew->servfail_stops = true; + looknew->besteffort = true; + looknew->dnssec = false; + looknew->ednsflags = 0; + looknew->opcode = dns_opcode_query; + looknew->expire = false; + looknew->nsid = false; + looknew->tcp_keepalive = false; + looknew->padding = 0; + looknew->header_only = false; + looknew->sendcookie = false; + looknew->seenbadcookie = false; + looknew->badcookie = true; + looknew->multiline = false; + looknew->nottl = false; + looknew->noclass = false; + looknew->onesoa = false; + looknew->use_usec = false; + looknew->nocrypto = false; + looknew->ttlunits = false; + looknew->expandaaaa = false; + looknew->qr = false; + looknew->accept_reply_unexpected_src = false; +#ifdef HAVE_LIBIDN2 + looknew->idnin = isatty(1) ? (getenv("IDN_DISABLE") == NULL) : false; + looknew->idnout = looknew->idnin; +#else /* ifdef HAVE_LIBIDN2 */ + looknew->idnin = false; + looknew->idnout = false; +#endif /* HAVE_LIBIDN2 */ + looknew->udpsize = -1; + looknew->edns = -1; + looknew->recurse = true; + looknew->aaonly = false; + looknew->adflag = false; + looknew->cdflag = false; + looknew->raflag = false; + looknew->tcflag = false; + looknew->print_unknown_format = false; + looknew->zflag = false; + looknew->ns_search_only = false; + looknew->origin = NULL; + looknew->tsigctx = NULL; + looknew->querysig = NULL; + looknew->retries = tries; + looknew->nsfound = 0; + looknew->tcp_mode = false; + looknew->tcp_mode_set = false; + looknew->comments = true; + looknew->stats = true; + looknew->section_question = true; + looknew->section_answer = true; + looknew->section_authority = true; + looknew->section_additional = true; + looknew->new_search = false; + looknew->done_as_is = false; + looknew->need_search = false; + looknew->ecs_addr = NULL; + looknew->cookie = NULL; + looknew->ednsopts = NULL; + looknew->ednsoptscnt = 0; + looknew->ednsneg = true; + looknew->mapped = true; + looknew->dscp = -1; + looknew->rrcomments = 0; + looknew->eoferr = 0; + dns_fixedname_init(&looknew->fdomain); + ISC_LINK_INIT(looknew, link); + ISC_LIST_INIT(looknew->q); + ISC_LIST_INIT(looknew->connecting); + ISC_LIST_INIT(looknew->my_server_list); + return (looknew); +} + +#define EDNSOPT_OPTIONS 100U + +static void +cloneopts(dig_lookup_t *looknew, dig_lookup_t *lookold) { + size_t len = sizeof(looknew->ednsopts[0]) * EDNSOPT_OPTIONS; + size_t i; + looknew->ednsopts = isc_mem_allocate(mctx, len); + for (i = 0; i < EDNSOPT_OPTIONS; i++) { + looknew->ednsopts[i].code = 0; + looknew->ednsopts[i].length = 0; + looknew->ednsopts[i].value = NULL; + } + looknew->ednsoptscnt = 0; + if (lookold == NULL || lookold->ednsopts == NULL) { + return; + } + + for (i = 0; i < lookold->ednsoptscnt; i++) { + len = lookold->ednsopts[i].length; + if (len != 0) { + INSIST(lookold->ednsopts[i].value != NULL); + looknew->ednsopts[i].value = isc_mem_allocate(mctx, + len); + memmove(looknew->ednsopts[i].value, + lookold->ednsopts[i].value, len); + } + looknew->ednsopts[i].code = lookold->ednsopts[i].code; + looknew->ednsopts[i].length = len; + } + looknew->ednsoptscnt = lookold->ednsoptscnt; +} + +/*% + * Clone a lookup, perhaps copying the server list. This does not clone + * the query list, since it will be regenerated by the setup_lookup() + * function, nor does it queue up the new lookup for processing. + * Caution: If you don't clone the servers, you MUST clone the server + * list separately from somewhere else, or construct it by hand. + */ +dig_lookup_t * +clone_lookup(dig_lookup_t *lookold, bool servers) { + dig_lookup_t *looknew; + + debug("clone_lookup(%p)", lookold); + + INSIST(!free_now); + + looknew = make_empty_lookup(); + INSIST(looknew != NULL); + strlcpy(looknew->textname, lookold->textname, MXNAME); + strlcpy(looknew->cmdline, lookold->cmdline, MXNAME); + looknew->textname[MXNAME - 1] = 0; + looknew->rdtype = lookold->rdtype; + looknew->qrdtype = lookold->qrdtype; + looknew->rdclass = lookold->rdclass; + looknew->rdtypeset = lookold->rdtypeset; + looknew->rdclassset = lookold->rdclassset; + looknew->doing_xfr = lookold->doing_xfr; + looknew->ixfr_serial = lookold->ixfr_serial; + looknew->trace = lookold->trace; + looknew->trace_root = lookold->trace_root; + looknew->identify = lookold->identify; + looknew->identify_previous_line = lookold->identify_previous_line; + looknew->ignore = lookold->ignore; + looknew->servfail_stops = lookold->servfail_stops; + looknew->besteffort = lookold->besteffort; + looknew->dnssec = lookold->dnssec; + looknew->ednsflags = lookold->ednsflags; + looknew->opcode = lookold->opcode; + looknew->expire = lookold->expire; + looknew->nsid = lookold->nsid; + looknew->tcp_keepalive = lookold->tcp_keepalive; + looknew->header_only = lookold->header_only; + looknew->sendcookie = lookold->sendcookie; + looknew->seenbadcookie = lookold->seenbadcookie; + looknew->badcookie = lookold->badcookie; + looknew->cookie = lookold->cookie; + if (lookold->ednsopts != NULL) { + cloneopts(looknew, lookold); + } else { + looknew->ednsopts = NULL; + looknew->ednsoptscnt = 0; + } + looknew->ednsneg = lookold->ednsneg; + looknew->padding = lookold->padding; + looknew->mapped = lookold->mapped; + looknew->multiline = lookold->multiline; + looknew->nottl = lookold->nottl; + looknew->noclass = lookold->noclass; + looknew->onesoa = lookold->onesoa; + looknew->use_usec = lookold->use_usec; + looknew->nocrypto = lookold->nocrypto; + looknew->ttlunits = lookold->ttlunits; + looknew->expandaaaa = lookold->expandaaaa; + looknew->qr = lookold->qr; + looknew->accept_reply_unexpected_src = + lookold->accept_reply_unexpected_src; + looknew->idnin = lookold->idnin; + looknew->idnout = lookold->idnout; + looknew->udpsize = lookold->udpsize; + looknew->edns = lookold->edns; + looknew->recurse = lookold->recurse; + looknew->aaonly = lookold->aaonly; + looknew->adflag = lookold->adflag; + looknew->cdflag = lookold->cdflag; + looknew->raflag = lookold->raflag; + looknew->tcflag = lookold->tcflag; + looknew->print_unknown_format = lookold->print_unknown_format; + looknew->zflag = lookold->zflag; + looknew->ns_search_only = lookold->ns_search_only; + looknew->tcp_mode = lookold->tcp_mode; + looknew->tcp_mode_set = lookold->tcp_mode_set; + looknew->comments = lookold->comments; + looknew->stats = lookold->stats; + looknew->section_question = lookold->section_question; + looknew->section_answer = lookold->section_answer; + looknew->section_authority = lookold->section_authority; + looknew->section_additional = lookold->section_additional; + looknew->origin = lookold->origin; + looknew->retries = lookold->retries; + looknew->tsigctx = NULL; + looknew->need_search = lookold->need_search; + looknew->done_as_is = lookold->done_as_is; + looknew->dscp = lookold->dscp; + looknew->rrcomments = lookold->rrcomments; + looknew->eoferr = lookold->eoferr; + + if (lookold->ecs_addr != NULL) { + size_t len = sizeof(isc_sockaddr_t); + looknew->ecs_addr = isc_mem_allocate(mctx, len); + memmove(looknew->ecs_addr, lookold->ecs_addr, len); + } + + dns_name_copynf(dns_fixedname_name(&lookold->fdomain), + dns_fixedname_name(&looknew->fdomain)); + + if (servers) { + clone_server_list(lookold->my_server_list, + &looknew->my_server_list); + } + return (looknew); +} + +/*% + * Requeue a lookup for further processing, perhaps copying the server + * list. The new lookup structure is returned to the caller, and is + * queued for processing. If servers are not cloned in the requeue, they + * must be added before allowing the current event to complete, since the + * completion of the event may result in the next entry on the lookup + * queue getting run. + */ +dig_lookup_t * +requeue_lookup(dig_lookup_t *lookold, bool servers) { + dig_lookup_t *looknew; + + debug("requeue_lookup(%p)", lookold); + + lookup_counter++; + if (lookup_counter > LOOKUP_LIMIT) { + fatal("too many lookups"); + } + + looknew = clone_lookup(lookold, servers); + INSIST(looknew != NULL); + + debug("before insertion, init@%p -> %p, new@%p -> %p", lookold, + lookold->link.next, looknew, looknew->link.next); + ISC_LIST_PREPEND(lookup_list, looknew, link); + debug("after insertion, init -> %p, new = %p, new -> %p", lookold, + looknew, looknew->link.next); + return (looknew); +} + +void +setup_text_key(void) { + isc_result_t result; + dns_name_t keyname; + isc_buffer_t secretbuf; + unsigned int secretsize; + unsigned char *secretstore; + + debug("setup_text_key()"); + isc_buffer_allocate(mctx, &namebuf, MXNAME); + dns_name_init(&keyname, NULL); + isc_buffer_putstr(namebuf, keynametext); + secretsize = (unsigned int)strlen(keysecret) * 3 / 4; + secretstore = isc_mem_allocate(mctx, secretsize); + isc_buffer_init(&secretbuf, secretstore, secretsize); + result = isc_base64_decodestring(keysecret, &secretbuf); + if (result != ISC_R_SUCCESS) { + goto failure; + } + + secretsize = isc_buffer_usedlength(&secretbuf); + + if (hmacname == NULL) { + result = DST_R_UNSUPPORTEDALG; + goto failure; + } + + result = dns_name_fromtext(&keyname, namebuf, dns_rootname, 0, namebuf); + if (result != ISC_R_SUCCESS) { + goto failure; + } + + result = dns_tsigkey_create(&keyname, hmacname, secretstore, + (int)secretsize, false, NULL, 0, 0, mctx, + NULL, &tsigkey); +failure: + if (result != ISC_R_SUCCESS) { + printf(";; Couldn't create key %s: %s\n", keynametext, + isc_result_totext(result)); + } else { + dst_key_setbits(tsigkey->key, digestbits); + } + + isc_mem_free(mctx, secretstore); + dns_name_invalidate(&keyname); + isc_buffer_free(&namebuf); +} + +static isc_result_t +parse_uint_helper(uint32_t *uip, const char *value, uint32_t max, + const char *desc, int base) { + uint32_t n; + isc_result_t result = isc_parse_uint32(&n, value, base); + if (result == ISC_R_SUCCESS && n > max) { + result = ISC_R_RANGE; + } + if (result != ISC_R_SUCCESS) { + printf("invalid %s '%s': %s\n", desc, value, + isc_result_totext(result)); + return (result); + } + *uip = n; + return (ISC_R_SUCCESS); +} + +isc_result_t +parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc) { + return (parse_uint_helper(uip, value, max, desc, 10)); +} + +isc_result_t +parse_xint(uint32_t *uip, const char *value, uint32_t max, const char *desc) { + return (parse_uint_helper(uip, value, max, desc, 0)); +} + +static uint32_t +parse_bits(char *arg, const char *desc, uint32_t max) { + isc_result_t result; + uint32_t tmp; + + result = parse_uint(&tmp, arg, max, desc); + if (result != ISC_R_SUCCESS) { + fatal("couldn't parse digest bits"); + } + tmp = (tmp + 7) & ~0x7U; + return (tmp); +} + +isc_result_t +parse_netprefix(isc_sockaddr_t **sap, const char *value) { + isc_result_t result = ISC_R_SUCCESS; + isc_sockaddr_t *sa = NULL; + struct in_addr in4; + struct in6_addr in6; + uint32_t prefix_length = 0xffffffff; + char *slash = NULL; + bool parsed = false; + bool prefix_parsed = false; + char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX/128")]; + + REQUIRE(sap != NULL && *sap == NULL); + + if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) { + fatal("invalid prefix '%s'\n", value); + } + + sa = isc_mem_allocate(mctx, sizeof(*sa)); + memset(sa, 0, sizeof(*sa)); + + if (strcmp(buf, "0") == 0) { + sa->type.sa.sa_family = AF_UNSPEC; + prefix_length = 0; + goto done; + } + + slash = strchr(buf, '/'); + if (slash != NULL) { + *slash = '\0'; + result = isc_parse_uint32(&prefix_length, slash + 1, 10); + if (result != ISC_R_SUCCESS) { + fatal("invalid prefix length in '%s': %s\n", value, + isc_result_totext(result)); + } + prefix_parsed = true; + } + + if (inet_pton(AF_INET6, buf, &in6) == 1) { + parsed = true; + isc_sockaddr_fromin6(sa, &in6, 0); + if (prefix_length > 128) { + prefix_length = 128; + } + } else if (inet_pton(AF_INET, buf, &in4) == 1) { + parsed = true; + isc_sockaddr_fromin(sa, &in4, 0); + if (prefix_length > 32) { + prefix_length = 32; + } + } else if (prefix_parsed) { + int i; + + for (i = 0; i < 3 && strlen(buf) < sizeof(buf) - 2; i++) { + strlcat(buf, ".0", sizeof(buf)); + if (inet_pton(AF_INET, buf, &in4) == 1) { + parsed = true; + isc_sockaddr_fromin(sa, &in4, 0); + break; + } + } + + if (prefix_length > 32) { + prefix_length = 32; + } + } + + if (!parsed) { + fatal("invalid address '%s'", value); + } + +done: + sa->length = prefix_length; + *sap = sa; + + return (ISC_R_SUCCESS); +} + +/* + * Parse HMAC algorithm specification + */ +void +parse_hmac(const char *hmac) { + char buf[20]; + size_t len; + + REQUIRE(hmac != NULL); + + len = strlen(hmac); + if (len >= sizeof(buf)) { + fatal("unknown key type '%.*s'", (int)len, hmac); + } + strlcpy(buf, hmac, sizeof(buf)); + + digestbits = 0; + + if (strcasecmp(buf, "hmac-md5") == 0) { + hmacname = DNS_TSIG_HMACMD5_NAME; + } else if (strncasecmp(buf, "hmac-md5-", 9) == 0) { + hmacname = DNS_TSIG_HMACMD5_NAME; + digestbits = parse_bits(&buf[9], "digest-bits [0..128]", 128); + } else if (strcasecmp(buf, "hmac-sha1") == 0) { + hmacname = DNS_TSIG_HMACSHA1_NAME; + digestbits = 0; + } else if (strncasecmp(buf, "hmac-sha1-", 10) == 0) { + hmacname = DNS_TSIG_HMACSHA1_NAME; + digestbits = parse_bits(&buf[10], "digest-bits [0..160]", 160); + } else if (strcasecmp(buf, "hmac-sha224") == 0) { + hmacname = DNS_TSIG_HMACSHA224_NAME; + } else if (strncasecmp(buf, "hmac-sha224-", 12) == 0) { + hmacname = DNS_TSIG_HMACSHA224_NAME; + digestbits = parse_bits(&buf[12], "digest-bits [0..224]", 224); + } else if (strcasecmp(buf, "hmac-sha256") == 0) { + hmacname = DNS_TSIG_HMACSHA256_NAME; + } else if (strncasecmp(buf, "hmac-sha256-", 12) == 0) { + hmacname = DNS_TSIG_HMACSHA256_NAME; + digestbits = parse_bits(&buf[12], "digest-bits [0..256]", 256); + } else if (strcasecmp(buf, "hmac-sha384") == 0) { + hmacname = DNS_TSIG_HMACSHA384_NAME; + } else if (strncasecmp(buf, "hmac-sha384-", 12) == 0) { + hmacname = DNS_TSIG_HMACSHA384_NAME; + digestbits = parse_bits(&buf[12], "digest-bits [0..384]", 384); + } else if (strcasecmp(buf, "hmac-sha512") == 0) { + hmacname = DNS_TSIG_HMACSHA512_NAME; + } else if (strncasecmp(buf, "hmac-sha512-", 12) == 0) { + hmacname = DNS_TSIG_HMACSHA512_NAME; + digestbits = parse_bits(&buf[12], "digest-bits [0..512]", 512); + } else { + fprintf(stderr, + ";; Warning, ignoring " + "invalid TSIG algorithm %s\n", + buf); + } +} + +/* + * Get a key from a named.conf format keyfile + */ +static isc_result_t +read_confkey(void) { + cfg_parser_t *pctx = NULL; + cfg_obj_t *file = NULL; + const cfg_obj_t *keyobj = NULL; + const cfg_obj_t *secretobj = NULL; + const cfg_obj_t *algorithmobj = NULL; + const char *keyname; + const char *secretstr; + const char *algorithm; + isc_result_t result; + + if (!isc_file_exists(keyfile)) { + return (ISC_R_FILENOTFOUND); + } + + result = cfg_parser_create(mctx, NULL, &pctx); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = cfg_parse_file(pctx, keyfile, &cfg_type_sessionkey, &file); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = cfg_map_get(file, "key", &keyobj); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + (void)cfg_map_get(keyobj, "secret", &secretobj); + (void)cfg_map_get(keyobj, "algorithm", &algorithmobj); + if (secretobj == NULL || algorithmobj == NULL) { + fatal("key must have algorithm and secret"); + } + + keyname = cfg_obj_asstring(cfg_map_getname(keyobj)); + secretstr = cfg_obj_asstring(secretobj); + algorithm = cfg_obj_asstring(algorithmobj); + + strlcpy(keynametext, keyname, sizeof(keynametext)); + strlcpy(keysecret, secretstr, sizeof(keysecret)); + parse_hmac(algorithm); + setup_text_key(); + +cleanup: + if (pctx != NULL) { + if (file != NULL) { + cfg_obj_destroy(pctx, &file); + } + cfg_parser_destroy(&pctx); + } + + return (result); +} + +void +setup_file_key(void) { + isc_result_t result; + dst_key_t *dstkey = NULL; + + debug("setup_file_key()"); + + /* Try reading the key from a K* pair */ + result = dst_key_fromnamedfile( + keyfile, NULL, DST_TYPE_PRIVATE | DST_TYPE_KEY, mctx, &dstkey); + + /* If that didn't work, try reading it as a session.key keyfile */ + if (result != ISC_R_SUCCESS) { + result = read_confkey(); + if (result == ISC_R_SUCCESS) { + return; + } + } + + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "Couldn't read key from %s: %s\n", keyfile, + isc_result_totext(result)); + goto failure; + } + + switch (dst_key_alg(dstkey)) { + case DST_ALG_HMACMD5: + hmacname = DNS_TSIG_HMACMD5_NAME; + break; + case DST_ALG_HMACSHA1: + hmacname = DNS_TSIG_HMACSHA1_NAME; + break; + case DST_ALG_HMACSHA224: + hmacname = DNS_TSIG_HMACSHA224_NAME; + break; + case DST_ALG_HMACSHA256: + hmacname = DNS_TSIG_HMACSHA256_NAME; + break; + case DST_ALG_HMACSHA384: + hmacname = DNS_TSIG_HMACSHA384_NAME; + break; + case DST_ALG_HMACSHA512: + hmacname = DNS_TSIG_HMACSHA512_NAME; + break; + default: + printf(";; Couldn't create key %s: bad algorithm\n", + keynametext); + goto failure; + } + result = dns_tsigkey_createfromkey(dst_key_name(dstkey), hmacname, + dstkey, false, NULL, 0, 0, mctx, + NULL, &tsigkey); + if (result != ISC_R_SUCCESS) { + printf(";; Couldn't create key %s: %s\n", keynametext, + isc_result_totext(result)); + goto failure; + } +failure: + if (dstkey != NULL) { + dst_key_free(&dstkey); + } +} + +static dig_searchlist_t * +make_searchlist_entry(char *domain) { + dig_searchlist_t *search; + search = isc_mem_allocate(mctx, sizeof(*search)); + strlcpy(search->origin, domain, MXNAME); + search->origin[MXNAME - 1] = 0; + ISC_LINK_INIT(search, link); + return (search); +} + +static void +clear_searchlist(void) { + dig_searchlist_t *search; + while ((search = ISC_LIST_HEAD(search_list)) != NULL) { + ISC_LIST_UNLINK(search_list, search, link); + isc_mem_free(mctx, search); + } +} + +static void +create_search_list(irs_resconf_t *resconf) { + irs_resconf_searchlist_t *list; + irs_resconf_search_t *entry; + dig_searchlist_t *search; + + debug("create_search_list()"); + clear_searchlist(); + + list = irs_resconf_getsearchlist(resconf); + for (entry = ISC_LIST_HEAD(*list); entry != NULL; + entry = ISC_LIST_NEXT(entry, link)) + { + search = make_searchlist_entry(entry->domain); + ISC_LIST_APPEND(search_list, search, link); + } +} + +/*% + * Append 'addr' to the list of servers to be queried. This function is only + * called when no server addresses are explicitly specified and either libirs + * returns an empty list of servers to use or none of the addresses returned by + * libirs are usable due to the specified address family restrictions. + */ +static void +add_fallback_nameserver(const char *addr) { + dig_server_t *server = make_server(addr, addr); + ISC_LINK_INIT(server, link); + ISC_LIST_APPEND(server_list, server, link); +} + +/*% + * Setup the system as a whole, reading key information and resolv.conf + * settings. + */ +void +setup_system(bool ipv4only, bool ipv6only) { + irs_resconf_t *resconf = NULL; + isc_result_t result; + + debug("setup_system()"); + + if (ipv4only) { + if (have_ipv4) { + isc_net_disableipv6(); + have_ipv6 = false; + } else { + fatal("can't find IPv4 networking"); + } + } + + if (ipv6only) { + if (have_ipv6) { + isc_net_disableipv4(); + have_ipv4 = false; + } else { + fatal("can't find IPv6 networking"); + } + } + + result = irs_resconf_load(mctx, RESOLV_CONF, &resconf); + if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) { + fatal("parse of %s failed", RESOLV_CONF); + } + + create_search_list(resconf); + if (ndots == -1) { + ndots = irs_resconf_getndots(resconf); + debug("ndots is %d.", ndots); + } + + /* If user doesn't specify server use nameservers from resolv.conf. */ + if (ISC_LIST_EMPTY(server_list)) { + get_server_list(resconf); + } + + /* If we don't find a nameserver fall back to localhost */ + if (ISC_LIST_EMPTY(server_list)) { + if (have_ipv6) { + add_fallback_nameserver("::1"); + } + if (have_ipv4) { + add_fallback_nameserver("127.0.0.1"); + } + } + + irs_resconf_destroy(&resconf); + + if (keyfile[0] != 0) { + setup_file_key(); + } else if (keysecret[0] != 0) { + setup_text_key(); + } + + isc_nonce_buf(cookie_secret, sizeof(cookie_secret)); +} + +/*% + * Override the search list derived from resolv.conf by 'domain'. + */ +void +set_search_domain(char *domain) { + dig_searchlist_t *search; + + clear_searchlist(); + search = make_searchlist_entry(domain); + ISC_LIST_APPEND(search_list, search, link); +} + +/*% + * Setup the ISC and DNS libraries for use by the system. + */ +void +setup_libs(void) { + isc_result_t result; + isc_logconfig_t *logconfig = NULL; + + debug("setup_libs()"); + +#if USE_PKCS11 + pk11_result_register(); +#endif /* if USE_PKCS11 */ + dns_result_register(); + + result = isc_net_probeipv4(); + if (result == ISC_R_SUCCESS) { + have_ipv4 = true; + } + + result = isc_net_probeipv6(); + if (result == ISC_R_SUCCESS) { + have_ipv6 = true; + } + if (!have_ipv6 && !have_ipv4) { + fatal("can't find either v4 or v6 networking"); + } + + isc_mem_create(&mctx); + isc_mem_setname(mctx, "dig", NULL); + + isc_log_create(mctx, &lctx, &logconfig); + isc_log_setcontext(lctx); + dns_log_init(lctx); + dns_log_setcontext(lctx); + + result = isc_log_usechannel(logconfig, "default_debug", NULL, NULL); + check_result(result, "isc_log_usechannel"); + + isc_log_setdebuglevel(lctx, 0); + + result = isc_managers_create(mctx, 1, 0, &netmgr, &taskmgr); + check_result(result, "isc_managers_create"); + + result = isc_task_create(taskmgr, 0, &global_task); + check_result(result, "isc_task_create"); + isc_task_setname(global_task, "dig", NULL); + + result = isc_timermgr_create(mctx, &timermgr); + check_result(result, "isc_timermgr_create"); + + result = isc_socketmgr_create(mctx, &socketmgr); + check_result(result, "isc_socketmgr_create"); + + result = dst_lib_init(mctx, NULL); + check_result(result, "dst_lib_init"); + is_dst_up = true; + + isc_mutex_init(&lookup_lock); +} + +typedef struct dig_ednsoptname { + uint32_t code; + const char *name; +} dig_ednsoptname_t; + +dig_ednsoptname_t optnames[] = { + { 1, "LLQ" }, /* draft-sekar-dns-llq */ + { 3, "NSID" }, /* RFC 5001 */ + { 5, "DAU" }, /* RFC 6975 */ + { 6, "DHU" }, /* RFC 6975 */ + { 7, "N3U" }, /* RFC 6975 */ + { 8, "ECS" }, /* RFC 7871 */ + { 9, "EXPIRE" }, /* RFC 7314 */ + { 10, "COOKIE" }, /* RFC 7873 */ + { 11, "KEEPALIVE" }, /* RFC 7828 */ + { 12, "PADDING" }, /* RFC 7830 */ + { 12, "PAD" }, /* shorthand */ + { 13, "CHAIN" }, /* RFC 7901 */ + { 14, "KEY-TAG" }, /* RFC 8145 */ + { 15, "EDE" }, /* ietf-dnsop-extended-error-16 */ + { 16, "CLIENT-TAG" }, /* draft-bellis-dnsop-edns-tags */ + { 17, "SERVER-TAG" }, /* draft-bellis-dnsop-edns-tags */ + { 26946, "DEVICEID" }, /* Brian Hartvigsen */ +}; + +#define N_EDNS_OPTNAMES (sizeof(optnames) / sizeof(optnames[0])) + +void +save_opt(dig_lookup_t *lookup, char *code, char *value) { + isc_result_t result; + uint32_t num = 0; + isc_buffer_t b; + bool found = false; + unsigned int i; + + if (lookup->ednsoptscnt >= EDNSOPT_OPTIONS) { + fatal("too many ednsopts"); + } + + for (i = 0; i < N_EDNS_OPTNAMES; i++) { + if (strcasecmp(code, optnames[i].name) == 0) { + num = optnames[i].code; + found = true; + break; + } + } + + if (!found) { + result = parse_uint(&num, code, 65535, "ednsopt"); + if (result != ISC_R_SUCCESS) { + fatal("bad edns code point: %s", code); + } + } + + if (lookup->ednsopts == NULL) { + cloneopts(lookup, NULL); + } + + if (lookup->ednsopts[lookup->ednsoptscnt].value != NULL) { + isc_mem_free(mctx, lookup->ednsopts[lookup->ednsoptscnt].value); + } + + lookup->ednsopts[lookup->ednsoptscnt].code = num; + lookup->ednsopts[lookup->ednsoptscnt].length = 0; + lookup->ednsopts[lookup->ednsoptscnt].value = NULL; + + if (value != NULL) { + char *buf; + buf = isc_mem_allocate(mctx, strlen(value) / 2 + 1); + isc_buffer_init(&b, buf, (unsigned int)strlen(value) / 2 + 1); + result = isc_hex_decodestring(value, &b); + check_result(result, "isc_hex_decodestring"); + lookup->ednsopts[lookup->ednsoptscnt].value = + isc_buffer_base(&b); + lookup->ednsopts[lookup->ednsoptscnt].length = + isc_buffer_usedlength(&b); + } + + lookup->ednsoptscnt++; +} + +/*% + * Add EDNS0 option record to a message. Currently, the only supported + * options are UDP buffer size, the DO bit, and EDNS options + * (e.g., NSID, COOKIE, client-subnet) + */ +static void +add_opt(dns_message_t *msg, uint16_t udpsize, uint16_t edns, unsigned int flags, + dns_ednsopt_t *opts, size_t count) { + dns_rdataset_t *rdataset = NULL; + isc_result_t result; + + debug("add_opt()"); + result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags, + opts, count); + check_result(result, "dns_message_buildopt"); + result = dns_message_setopt(msg, rdataset); + check_result(result, "dns_message_setopt"); +} + +/*% + * Add a question section to a message, asking for the specified name, + * type, and class. + */ +static void +add_question(dns_message_t *message, dns_name_t *name, dns_rdataclass_t rdclass, + dns_rdatatype_t rdtype) { + dns_rdataset_t *rdataset; + isc_result_t result; + + debug("add_question()"); + rdataset = NULL; + result = dns_message_gettemprdataset(message, &rdataset); + check_result(result, "dns_message_gettemprdataset()"); + dns_rdataset_makequestion(rdataset, rdclass, rdtype); + ISC_LIST_APPEND(name->list, rdataset, link); +} + +/*% + * Check if we're done with all the queued lookups, which is true iff + * all sockets, sends, and recvs are accounted for (counters == 0), + * and the lookup list is empty. + * If we are done, pass control back out to dighost_shutdown() (which is + * part of dig.c, host.c, or nslookup.c) to either shutdown the system as + * a whole or reseed the lookup list. + */ +static void +check_if_done(void) { + debug("check_if_done()"); + debug("list %s", ISC_LIST_EMPTY(lookup_list) ? "empty" : "full"); + if (ISC_LIST_EMPTY(lookup_list) && current_lookup == NULL && + sendcount == 0) + { + INSIST(sockcount == 0); + INSIST(recvcount == 0); + debug("shutting down"); + dighost_shutdown(); + } +} + +/*% + * Clear out a query when we're done with it. WARNING: This routine + * WILL invalidate the query pointer. + */ +static void +clear_query(dig_query_t *query) { + dig_lookup_t *lookup; + + REQUIRE(query != NULL); + + debug("clear_query(%p)", query); + + if (query->timer != NULL) { + isc_timer_destroy(&query->timer); + } + lookup = query->lookup; + + if (lookup->current_query == query) { + lookup->current_query = NULL; + } + + if (ISC_LINK_LINKED(query, link)) { + query->saved_next = ISC_LIST_NEXT(query, link); + ISC_LIST_UNLINK(lookup->q, query, link); + } + if (ISC_LINK_LINKED(query, clink)) { + ISC_LIST_UNLINK(lookup->connecting, query, clink); + } + INSIST(query->recvspace != NULL); + + if (query->sock != NULL) { + isc_socket_detach(&query->sock); + sockcount--; + debug("sockcount=%d", sockcount); + } + isc_mem_put(mctx, query->recvspace, COMMSIZE); + isc_mem_put(mctx, query->tmpsendspace, COMMSIZE); + isc_buffer_invalidate(&query->recvbuf); + isc_buffer_invalidate(&query->lengthbuf); + + if (query->waiting_senddone) { + debug("waiting senddone, delay freeing query"); + query->pending_free = true; + } else { + query->magic = 0; + isc_mem_free(mctx, query); + } +} + +/*% + * Try and clear out a lookup if we're done with it. Return true if + * the lookup was successfully cleared. If true is returned, the + * lookup pointer has been invalidated. + */ +static bool +try_clear_lookup(dig_lookup_t *lookup) { + dig_query_t *q; + + REQUIRE(lookup != NULL); + + debug("try_clear_lookup(%p)", lookup); + + if (ISC_LIST_HEAD(lookup->q) != NULL || + ISC_LIST_HEAD(lookup->connecting) != NULL) + { + if (debugging) { + q = ISC_LIST_HEAD(lookup->q); + while (q != NULL) { + debug("query to %s still pending", q->servname); + q = ISC_LIST_NEXT(q, link); + } + + q = ISC_LIST_HEAD(lookup->connecting); + while (q != NULL) { + debug("query to %s still connecting", + q->servname); + q = ISC_LIST_NEXT(q, clink); + } + } + return (false); + } + + /* + * At this point, we know there are no queries on the lookup, + * so can make it go away also. + */ + destroy_lookup(lookup); + return (true); +} + +void +destroy_lookup(dig_lookup_t *lookup) { + dig_server_t *s; + void *ptr; + + debug("destroy"); + s = ISC_LIST_HEAD(lookup->my_server_list); + while (s != NULL) { + debug("freeing server %p belonging to %p", s, lookup); + ptr = s; + s = ISC_LIST_NEXT(s, link); + ISC_LIST_DEQUEUE(lookup->my_server_list, (dig_server_t *)ptr, + link); + isc_mem_free(mctx, ptr); + } + if (lookup->sendmsg != NULL) { + dns_message_detach(&lookup->sendmsg); + } + if (lookup->querysig != NULL) { + debug("freeing buffer %p", lookup->querysig); + isc_buffer_free(&lookup->querysig); + } + if (lookup->sendspace != NULL) { + isc_mem_put(mctx, lookup->sendspace, COMMSIZE); + } + + if (lookup->tsigctx != NULL) { + dst_context_destroy(&lookup->tsigctx); + } + + if (lookup->ecs_addr != NULL) { + isc_mem_free(mctx, lookup->ecs_addr); + } + + if (lookup->ednsopts != NULL) { + size_t i; + for (i = 0; i < EDNSOPT_OPTIONS; i++) { + if (lookup->ednsopts[i].value != NULL) { + isc_mem_free(mctx, lookup->ednsopts[i].value); + } + } + isc_mem_free(mctx, lookup->ednsopts); + } + + isc_mem_free(mctx, lookup); +} + +/*% + * If we can, start the next lookup in the queue running. + * This assumes that the lookup on the head of the queue hasn't been + * started yet. It also removes the lookup from the head of the queue, + * setting the current_lookup pointer pointing to it. + */ +void +start_lookup(void) { + debug("start_lookup()"); + if (cancel_now) { + return; + } + + /* + * If there's a current lookup running, we really shouldn't get + * here. + */ + INSIST(current_lookup == NULL); + + current_lookup = ISC_LIST_HEAD(lookup_list); + /* + * Put the current lookup somewhere so cancel_all can find it + */ + if (current_lookup != NULL) { + ISC_LIST_DEQUEUE(lookup_list, current_lookup, link); + if (setup_lookup(current_lookup)) { + do_lookup(current_lookup); + } else if (next_origin(current_lookup)) { + check_next_lookup(current_lookup); + } + } else { + check_if_done(); + } +} + +/*% + * If we can, clear the current lookup and start the next one running. + * This calls try_clear_lookup, so may invalidate the lookup pointer. + */ +static void +check_next_lookup(dig_lookup_t *lookup) { + INSIST(!free_now); + + debug("check_next_lookup(%p)", lookup); + + if (ISC_LIST_HEAD(lookup->q) != NULL) { + debug("still have a worker"); + return; + } + if (try_clear_lookup(lookup)) { + current_lookup = NULL; + start_lookup(); + } +} + +/*% + * Create and queue a new lookup as a followup to the current lookup, + * based on the supplied message and section. This is used in trace and + * name server search modes to start a new lookup using servers from + * NS records in a reply. Returns the number of followup lookups made. + */ +static int +followup_lookup(dns_message_t *msg, dig_query_t *query, dns_section_t section) { + dig_lookup_t *lookup = NULL; + dig_server_t *srv = NULL; + dns_rdataset_t *rdataset = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_name_t *name = NULL; + isc_result_t result; + bool success = false; + int numLookups = 0; + int num; + isc_result_t lresult, addresses_result; + char bad_namestr[DNS_NAME_FORMATSIZE]; + dns_name_t *domain; + bool horizontal = false, bad = false; + + INSIST(!free_now); + + debug("following up %s", query->lookup->textname); + + addresses_result = ISC_R_SUCCESS; + bad_namestr[0] = '\0'; + for (result = dns_message_firstname(msg, section); + result == ISC_R_SUCCESS; + result = dns_message_nextname(msg, section)) + { + name = NULL; + dns_message_currentname(msg, section, &name); + + if (section == DNS_SECTION_AUTHORITY) { + rdataset = NULL; + result = dns_message_findtype(name, dns_rdatatype_soa, + 0, &rdataset); + if (result == ISC_R_SUCCESS) { + return (0); + } + } + rdataset = NULL; + result = dns_message_findtype(name, dns_rdatatype_ns, 0, + &rdataset); + if (result != ISC_R_SUCCESS) { + continue; + } + + debug("found NS set"); + + if (query->lookup->trace && !query->lookup->trace_root) { + dns_namereln_t namereln; + unsigned int nlabels; + int order; + + domain = dns_fixedname_name(&query->lookup->fdomain); + namereln = dns_name_fullcompare(name, domain, &order, + &nlabels); + if (namereln == dns_namereln_equal) { + if (!horizontal) { + dighost_warning("BAD (HORIZONTAL) " + "REFERRAL"); + } + horizontal = true; + } else if (namereln != dns_namereln_subdomain) { + if (!bad) { + dighost_warning("BAD REFERRAL"); + } + bad = true; + continue; + } + } + + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + char namestr[DNS_NAME_FORMATSIZE]; + dns_rdata_ns_t ns; + + if (query->lookup->trace_root && + query->lookup->nsfound >= MXSERV) + { + break; + } + + dns_rdataset_current(rdataset, &rdata); + + query->lookup->nsfound++; + result = dns_rdata_tostruct(&rdata, &ns, NULL); + check_result(result, "dns_rdata_tostruct"); + dns_name_format(&ns.name, namestr, sizeof(namestr)); + dns_rdata_freestruct(&ns); + + /* Initialize lookup if we've not yet */ + debug("found NS %s", namestr); + if (!success) { + success = true; + lookup_counter++; + lookup = requeue_lookup(query->lookup, false); + cancel_lookup(query->lookup); + lookup->doing_xfr = false; + if (!lookup->trace_root && + section == DNS_SECTION_ANSWER) + { + lookup->trace = false; + } else { + lookup->trace = query->lookup->trace; + } + lookup->ns_search_only = + query->lookup->ns_search_only; + lookup->trace_root = false; + if (lookup->ns_search_only) { + lookup->recurse = false; + } + domain = dns_fixedname_name(&lookup->fdomain); + dns_name_copynf(name, domain); + } + debug("adding server %s", namestr); + num = getaddresses(lookup, namestr, &lresult); + if (lresult != ISC_R_SUCCESS) { + printf("couldn't get address for '%s': %s\n", + namestr, isc_result_totext(lresult)); + if (addresses_result == ISC_R_SUCCESS) { + addresses_result = lresult; + strlcpy(bad_namestr, namestr, + sizeof(bad_namestr)); + } + } + numLookups += num; + dns_rdata_reset(&rdata); + } + } + if (numLookups == 0 && addresses_result != ISC_R_SUCCESS) { + fatal("couldn't get address for '%s': %s", bad_namestr, + isc_result_totext(result)); + } + + if (lookup == NULL && section == DNS_SECTION_ANSWER && + (query->lookup->trace || query->lookup->ns_search_only)) + { + return (followup_lookup(msg, query, DNS_SECTION_AUTHORITY)); + } + + /* + * Randomize the order the nameserver will be tried. + */ + if (numLookups > 1) { + uint32_t i, j; + dig_serverlist_t my_server_list; + dig_server_t *next; + + ISC_LIST_INIT(my_server_list); + + i = numLookups; + for (srv = ISC_LIST_HEAD(lookup->my_server_list); srv != NULL; + srv = ISC_LIST_HEAD(lookup->my_server_list)) + { + INSIST(i > 0); + j = isc_random_uniform(i); + next = ISC_LIST_NEXT(srv, link); + while (j-- > 0 && next != NULL) { + srv = next; + next = ISC_LIST_NEXT(srv, link); + } + ISC_LIST_DEQUEUE(lookup->my_server_list, srv, link); + ISC_LIST_APPEND(my_server_list, srv, link); + i--; + } + ISC_LIST_APPENDLIST(lookup->my_server_list, my_server_list, + link); + } + + return (numLookups); +} + +/*% + * Create and queue a new lookup using the next origin from the search + * list, read in setup_system(). + * + * Return true iff there was another searchlist entry. + */ +static bool +next_origin(dig_lookup_t *oldlookup) { + dig_lookup_t *newlookup; + dig_searchlist_t *search; + dns_fixedname_t fixed; + dns_name_t *name; + isc_result_t result; + + INSIST(!free_now); + + debug("next_origin(%p)", oldlookup); + debug("following up %s", oldlookup->textname); + + if (!usesearch) { + /* + * We're not using a search list, so don't even think + * about finding the next entry. + */ + return (false); + } + + /* + * Check for a absolute name or ndots being met. + */ + name = dns_fixedname_initname(&fixed); + result = dns_name_fromstring2(name, oldlookup->textname, NULL, 0, NULL); + if (result == ISC_R_SUCCESS && + (dns_name_isabsolute(name) || + (int)dns_name_countlabels(name) > ndots)) + { + return (false); + } + + if (oldlookup->origin == NULL && !oldlookup->need_search) { + /* + * Then we just did rootorg; there's nothing left. + */ + return (false); + } + if (oldlookup->origin == NULL && oldlookup->need_search) { + newlookup = requeue_lookup(oldlookup, true); + newlookup->origin = ISC_LIST_HEAD(search_list); + newlookup->need_search = false; + } else { + search = ISC_LIST_NEXT(oldlookup->origin, link); + if (search == NULL && oldlookup->done_as_is) { + return (false); + } + newlookup = requeue_lookup(oldlookup, true); + newlookup->origin = search; + } + cancel_lookup(oldlookup); + return (true); +} + +/*% + * Insert an SOA record into the sendmessage in a lookup. Used for + * creating IXFR queries. + */ +static void +insert_soa(dig_lookup_t *lookup) { + isc_result_t result; + dns_rdata_soa_t soa; + dns_rdata_t *rdata = NULL; + dns_rdatalist_t *rdatalist = NULL; + dns_rdataset_t *rdataset = NULL; + dns_name_t *soaname = NULL; + + debug("insert_soa(%p)", lookup); + soa.mctx = mctx; + soa.serial = lookup->ixfr_serial; + soa.refresh = 0; + soa.retry = 0; + soa.expire = 0; + soa.minimum = 0; + soa.common.rdclass = lookup->rdclass; + soa.common.rdtype = dns_rdatatype_soa; + + dns_name_init(&soa.origin, NULL); + dns_name_init(&soa.contact, NULL); + + dns_name_clone(dns_rootname, &soa.origin); + dns_name_clone(dns_rootname, &soa.contact); + + isc_buffer_init(&lookup->rdatabuf, lookup->rdatastore, + sizeof(lookup->rdatastore)); + + result = dns_message_gettemprdata(lookup->sendmsg, &rdata); + check_result(result, "dns_message_gettemprdata"); + + result = dns_rdata_fromstruct(rdata, lookup->rdclass, dns_rdatatype_soa, + &soa, &lookup->rdatabuf); + check_result(result, "isc_rdata_fromstruct"); + + result = dns_message_gettemprdatalist(lookup->sendmsg, &rdatalist); + check_result(result, "dns_message_gettemprdatalist"); + + result = dns_message_gettemprdataset(lookup->sendmsg, &rdataset); + check_result(result, "dns_message_gettemprdataset"); + + dns_rdatalist_init(rdatalist); + rdatalist->type = dns_rdatatype_soa; + rdatalist->rdclass = lookup->rdclass; + ISC_LIST_APPEND(rdatalist->rdata, rdata, link); + + dns_rdatalist_tordataset(rdatalist, rdataset); + + result = dns_message_gettempname(lookup->sendmsg, &soaname); + check_result(result, "dns_message_gettempname"); + dns_name_clone(lookup->name, soaname); + ISC_LIST_INIT(soaname->list); + ISC_LIST_APPEND(soaname->list, rdataset, link); + dns_message_addname(lookup->sendmsg, soaname, DNS_SECTION_AUTHORITY); +} + +static void +compute_cookie(unsigned char *clientcookie, size_t len) { + /* XXXMPA need to fix, should be per server. */ + INSIST(len >= 8U); + memmove(clientcookie, cookie_secret, 8); +} + +/*% + * Setup the supplied lookup structure, making it ready to start sending + * queries to servers. Create and initialize the message to be sent as + * well as the query structures and buffer space for the replies. If the + * server list is empty, clone it from the system default list. + */ +bool +setup_lookup(dig_lookup_t *lookup) { + isc_result_t result; + unsigned int len; + dig_server_t *serv; + dig_query_t *query; + isc_buffer_t b; + dns_compress_t cctx; + char store[MXNAME]; + char ecsbuf[20]; + char cookiebuf[256]; + char *origin = NULL; + char *textname = NULL; + + REQUIRE(lookup != NULL); + +#ifdef HAVE_LIBIDN2 + char idn_origin[MXNAME], idn_textname[MXNAME]; + + result = dns_name_settotextfilter(lookup->idnout ? idn_output_filter + : NULL); + check_result(result, "dns_name_settotextfilter"); +#endif /* HAVE_LIBIDN2 */ + + INSIST(!free_now); + + debug("setup_lookup(%p)", lookup); + + dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &lookup->sendmsg); + + if (lookup->new_search) { + debug("resetting lookup counter."); + lookup_counter = 0; + } + + if (ISC_LIST_EMPTY(lookup->my_server_list)) { + debug("cloning server list"); + clone_server_list(server_list, &lookup->my_server_list); + } + result = dns_message_gettempname(lookup->sendmsg, &lookup->name); + check_result(result, "dns_message_gettempname"); + + isc_buffer_init(&lookup->namebuf, lookup->name_space, + sizeof(lookup->name_space)); + isc_buffer_init(&lookup->onamebuf, lookup->oname_space, + sizeof(lookup->oname_space)); + + /* + * We cannot convert `textname' and `origin' separately. + * `textname' doesn't contain TLD, but local mapping needs + * TLD. + */ + textname = lookup->textname; +#ifdef HAVE_LIBIDN2 + if (lookup->idnin) { + idn_locale_to_ace(textname, idn_textname, sizeof(idn_textname)); + debug("idn_textname: %s", idn_textname); + textname = idn_textname; + } +#endif /* HAVE_LIBIDN2 */ + + /* + * If the name has too many dots, force the origin to be NULL + * (which produces an absolute lookup). Otherwise, take the origin + * we have if there's one in the struct already. If it's NULL, + * take the first entry in the searchlist iff either usesearch + * is TRUE or we got a domain line in the resolv.conf file. + */ + if (lookup->new_search) { + if ((count_dots(textname) >= ndots) || !usesearch) { + lookup->origin = NULL; /* Force abs lookup */ + lookup->done_as_is = true; + lookup->need_search = usesearch; + } else if (lookup->origin == NULL && usesearch) { + lookup->origin = ISC_LIST_HEAD(search_list); + lookup->need_search = false; + } + } + + if (lookup->origin != NULL) { + debug("trying origin %s", lookup->origin->origin); + result = dns_message_gettempname(lookup->sendmsg, + &lookup->oname); + check_result(result, "dns_message_gettempname"); + /* XXX Helper funct to conv char* to name? */ + origin = lookup->origin->origin; +#ifdef HAVE_LIBIDN2 + if (lookup->idnin) { + idn_locale_to_ace(origin, idn_origin, + sizeof(idn_origin)); + debug("trying idn origin %s", idn_origin); + origin = idn_origin; + } +#endif /* HAVE_LIBIDN2 */ + len = (unsigned int)strlen(origin); + isc_buffer_init(&b, origin, len); + isc_buffer_add(&b, len); + result = dns_name_fromtext(lookup->oname, &b, dns_rootname, 0, + &lookup->onamebuf); + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(lookup->sendmsg, &lookup->name); + dns_message_puttempname(lookup->sendmsg, + &lookup->oname); + fatal("'%s' is not in legal name syntax (%s)", origin, + isc_result_totext(result)); + } + if (lookup->trace && lookup->trace_root) { + dns_name_clone(dns_rootname, lookup->name); + } else { + dns_fixedname_t fixed; + dns_name_t *name; + + name = dns_fixedname_initname(&fixed); + len = (unsigned int)strlen(textname); + isc_buffer_init(&b, textname, len); + isc_buffer_add(&b, len); + result = dns_name_fromtext(name, &b, NULL, 0, NULL); + if (result == ISC_R_SUCCESS) { + if (!dns_name_isabsolute(name)) { + result = dns_name_concatenate( + name, lookup->oname, + lookup->name, &lookup->namebuf); + } else { + result = dns_name_copy( + name, lookup->name, + &lookup->namebuf); + } + } + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(lookup->sendmsg, + &lookup->name); + dns_message_puttempname(lookup->sendmsg, + &lookup->oname); + if (result == DNS_R_NAMETOOLONG) { + return (false); + } + fatal("'%s' is not in legal name syntax (%s)", + lookup->textname, + isc_result_totext(result)); + } + } + dns_message_puttempname(lookup->sendmsg, &lookup->oname); + } else { + debug("using root origin"); + if (lookup->trace && lookup->trace_root) { + dns_name_clone(dns_rootname, lookup->name); + } else { + len = (unsigned int)strlen(textname); + isc_buffer_init(&b, textname, len); + isc_buffer_add(&b, len); + result = dns_name_fromtext(lookup->name, &b, + dns_rootname, 0, + &lookup->namebuf); + } + if (result != ISC_R_SUCCESS) { + dns_message_puttempname(lookup->sendmsg, &lookup->name); + warn("'%s' is not a legal name " + "(%s)", + lookup->textname, isc_result_totext(result)); +#if TARGET_OS_IPHONE + check_next_lookup(current_lookup); + return (false); +#else /* if TARGET_OS_IPHONE */ + digexit(); +#endif /* if TARGET_OS_IPHONE */ + } + } + dns_name_format(lookup->name, store, sizeof(store)); + dighost_trying(store, lookup); + INSIST(dns_name_isabsolute(lookup->name)); + + lookup->sendmsg->id = (dns_messageid_t)isc_random16(); + lookup->sendmsg->opcode = lookup->opcode; + lookup->msgcounter = 0; + + /* + * If this is a trace request, completely disallow recursion after + * looking up the root name servers, since it's meaningless for traces. + */ + if ((lookup->trace || lookup->ns_search_only) && !lookup->trace_root) { + lookup->recurse = false; + } + + if (lookup->recurse && lookup->rdtype != dns_rdatatype_axfr && + lookup->rdtype != dns_rdatatype_ixfr) + { + debug("recursive query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_RD; + } + + /* XXX aaflag */ + if (lookup->aaonly) { + debug("AA query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_AA; + } + + if (lookup->adflag) { + debug("AD query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_AD; + } + + if (lookup->cdflag) { + debug("CD query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_CD; + } + + if (lookup->raflag) { + debug("RA query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_RA; + } + + if (lookup->tcflag) { + debug("TC query"); + lookup->sendmsg->flags |= DNS_MESSAGEFLAG_TC; + } + + if (lookup->zflag) { + debug("Z query"); + lookup->sendmsg->flags |= 0x0040U; + } + + dns_message_addname(lookup->sendmsg, lookup->name, + DNS_SECTION_QUESTION); + + if (lookup->trace && lookup->trace_root) { + lookup->qrdtype = lookup->rdtype; + lookup->rdtype = dns_rdatatype_ns; + } + + if ((lookup->rdtype == dns_rdatatype_axfr) || + (lookup->rdtype == dns_rdatatype_ixfr)) + { + /* + * Force TCP mode if we're doing an axfr. + */ + if (lookup->rdtype == dns_rdatatype_axfr) { + lookup->doing_xfr = true; + lookup->tcp_mode = true; + } else if (lookup->tcp_mode) { + lookup->doing_xfr = true; + } + } + + if (!lookup->header_only) { + add_question(lookup->sendmsg, lookup->name, lookup->rdclass, + lookup->rdtype); + } + + /* add_soa */ + if (lookup->rdtype == dns_rdatatype_ixfr) { + insert_soa(lookup); + } + + /* XXX Insist this? */ + lookup->tsigctx = NULL; + lookup->querysig = NULL; + if (tsigkey != NULL) { + debug("initializing keys"); + result = dns_message_settsigkey(lookup->sendmsg, tsigkey); + check_result(result, "dns_message_settsigkey"); + } + + lookup->sendspace = isc_mem_get(mctx, COMMSIZE); + + result = dns_compress_init(&cctx, -1, mctx); + check_result(result, "dns_compress_init"); + + debug("starting to render the message"); + isc_buffer_init(&lookup->renderbuf, lookup->sendspace, COMMSIZE); + result = dns_message_renderbegin(lookup->sendmsg, &cctx, + &lookup->renderbuf); + check_result(result, "dns_message_renderbegin"); + if (lookup->udpsize > 0 || lookup->dnssec || lookup->edns > -1 || + lookup->ecs_addr != NULL) + { +#define MAXOPTS (EDNSOPT_OPTIONS + DNS_EDNSOPTIONS) + dns_ednsopt_t opts[MAXOPTS]; + unsigned int flags; + unsigned int i = 0; + + /* + * There can't be more than MAXOPTS options to send: + * a maximum of EDNSOPT_OPTIONS set by +ednsopt + * and DNS_EDNSOPTIONS set by other arguments + * (+nsid, +cookie, etc). + */ + if (lookup->udpsize < 0) { + lookup->udpsize = DEFAULT_EDNS_BUFSIZE; + } + if (lookup->edns < 0) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + + if (lookup->nsid) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_NSID; + opts[i].length = 0; + opts[i].value = NULL; + i++; + } + + if (lookup->ecs_addr != NULL) { + uint8_t addr[16]; + uint16_t family = 0; + uint32_t plen; + struct sockaddr *sa; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + size_t addrl; + + sa = &lookup->ecs_addr->type.sa; + plen = lookup->ecs_addr->length; + + /* Round up prefix len to a multiple of 8 */ + addrl = (plen + 7) / 8; + + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_CLIENT_SUBNET; + opts[i].length = (uint16_t)addrl + 4; + check_result(result, "isc_buffer_allocate"); + + /* + * XXXMUKS: According to RFC7871, "If there is + * no ADDRESS set, i.e., SOURCE PREFIX-LENGTH is + * set to 0, then FAMILY SHOULD be set to the + * transport over which the query is sent." + * + * However, at this point we don't know what + * transport(s) we'll be using, so we can't + * set the value now. For now, we're using + * IPv4 as the default the +subnet option + * used an IPv4 prefix, or for +subnet=0, + * and IPv6 if the +subnet option used an + * IPv6 prefix. + * + * (For future work: preserve the offset into + * the buffer where the family field is; + * that way we can update it in send_udp() + * or send_tcp_connect() once we know + * what it outght to be.) + */ + switch (sa->sa_family) { + case AF_UNSPEC: + INSIST(plen == 0); + family = 1; + break; + case AF_INET: + INSIST(plen <= 32); + family = 1; + sin = (struct sockaddr_in *)sa; + memmove(addr, &sin->sin_addr, addrl); + break; + case AF_INET6: + INSIST(plen <= 128); + family = 2; + sin6 = (struct sockaddr_in6 *)sa; + memmove(addr, &sin6->sin6_addr, addrl); + break; + default: + UNREACHABLE(); + } + + isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf)); + /* family */ + isc_buffer_putuint16(&b, family); + /* source prefix-length */ + isc_buffer_putuint8(&b, plen); + /* scope prefix-length */ + isc_buffer_putuint8(&b, 0); + + /* address */ + if (addrl > 0) { + /* Mask off last address byte */ + if ((plen % 8) != 0) { + addr[addrl - 1] &= ~0U + << (8 - (plen % 8)); + } + isc_buffer_putmem(&b, addr, (unsigned)addrl); + } + + opts[i].value = (uint8_t *)ecsbuf; + i++; + } + + if (lookup->sendcookie) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_COOKIE; + if (lookup->cookie != NULL) { + isc_buffer_init(&b, cookiebuf, + sizeof(cookiebuf)); + result = isc_hex_decodestring(lookup->cookie, + &b); + check_result(result, "isc_hex_decodestring"); + opts[i].value = isc_buffer_base(&b); + opts[i].length = isc_buffer_usedlength(&b); + } else { + compute_cookie(cookie, sizeof(cookie)); + opts[i].length = 8; + opts[i].value = cookie; + } + i++; + } + + if (lookup->expire) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_EXPIRE; + opts[i].length = 0; + opts[i].value = NULL; + i++; + } + + if (lookup->tcp_keepalive) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_TCP_KEEPALIVE; + opts[i].length = 0; + opts[i].value = NULL; + i++; + } + + if (lookup->ednsoptscnt != 0) { + INSIST(i + lookup->ednsoptscnt <= MAXOPTS); + memmove(&opts[i], lookup->ednsopts, + sizeof(dns_ednsopt_t) * lookup->ednsoptscnt); + i += lookup->ednsoptscnt; + } + + if (lookup->padding != 0 && (i >= MAXOPTS)) { + debug("turned off padding because of EDNS overflow"); + lookup->padding = 0; + } + + if (lookup->padding != 0) { + INSIST(i < MAXOPTS); + opts[i].code = DNS_OPT_PAD; + opts[i].length = 0; + opts[i].value = NULL; + i++; + dns_message_setpadding(lookup->sendmsg, + lookup->padding); + } + + flags = lookup->ednsflags; + flags &= ~DNS_MESSAGEEXTFLAG_DO; + if (lookup->dnssec) { + flags |= DNS_MESSAGEEXTFLAG_DO; + } + add_opt(lookup->sendmsg, lookup->udpsize, lookup->edns, flags, + opts, i); + } + + result = dns_message_rendersection(lookup->sendmsg, + DNS_SECTION_QUESTION, 0); + check_result(result, "dns_message_rendersection"); + result = dns_message_rendersection(lookup->sendmsg, + DNS_SECTION_AUTHORITY, 0); + check_result(result, "dns_message_rendersection"); + result = dns_message_renderend(lookup->sendmsg); + check_result(result, "dns_message_renderend"); + debug("done rendering"); + + dns_compress_invalidate(&cctx); + + /* + * Force TCP mode if the request is larger than 512 bytes. + */ + if (isc_buffer_usedlength(&lookup->renderbuf) > 512) { + lookup->tcp_mode = true; + } + + lookup->pending = false; + + for (serv = ISC_LIST_HEAD(lookup->my_server_list); serv != NULL; + serv = ISC_LIST_NEXT(serv, link)) + { + query = isc_mem_allocate(mctx, sizeof(dig_query_t)); + debug("create query %p linked to lookup %p", query, lookup); + query->lookup = lookup; + query->timer = NULL; + query->waiting_connect = false; + query->waiting_senddone = false; + query->pending_free = false; + query->recv_made = false; + query->first_pass = true; + query->first_soa_rcvd = false; + query->second_rr_rcvd = false; + query->first_repeat_rcvd = false; + query->warn_id = true; + query->timedout = false; + query->first_rr_serial = 0; + query->second_rr_serial = 0; + query->servname = serv->servername; + query->userarg = serv->userarg; + query->rr_count = 0; + query->msg_count = 0; + query->byte_count = 0; + query->ixfr_axfr = false; + query->sock = NULL; + query->recvspace = isc_mem_get(mctx, COMMSIZE); + query->tmpsendspace = isc_mem_get(mctx, COMMSIZE); + if (query->recvspace == NULL) { + fatal("memory allocation failure"); + } + + isc_buffer_init(&query->recvbuf, query->recvspace, COMMSIZE); + isc_buffer_init(&query->lengthbuf, query->lengthspace, 2); + isc_buffer_init(&query->tmpsendbuf, query->tmpsendspace, + COMMSIZE); + query->sendbuf = lookup->renderbuf; + + isc_time_settoepoch(&query->time_sent); + isc_time_settoepoch(&query->time_recv); + + ISC_LINK_INIT(query, clink); + ISC_LINK_INIT(query, link); + query->saved_next = NULL; + + query->magic = DIG_QUERY_MAGIC; + + ISC_LIST_ENQUEUE(lookup->q, query, link); + } + + return (true); +} + +/*% + * Event handler for send completion. Track send counter, and clear out + * the query if the send was canceled. + */ +static void +send_done(isc_task_t *_task, isc_event_t *event) { + dig_query_t *query, *next; + dig_lookup_t *l; + + REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE); + + UNUSED(_task); + + LOCK_LOOKUP; + + debug("send_done(%p)", event->ev_arg); + sendcount--; + debug("sendcount=%d", sendcount); + INSIST(sendcount >= 0); + + query = event->ev_arg; + REQUIRE(DIG_VALID_QUERY(query)); + query->waiting_senddone = false; + l = query->lookup; + + if (l == current_lookup && l->ns_search_only && !l->trace_root && + !l->tcp_mode) + { + debug("sending next, since searching"); + next = query->pending_free ? query->saved_next + : ISC_LIST_NEXT(query, link); + if (next != NULL) { + send_udp(next); + } + } + + isc_event_free(&event); + + if (query->pending_free) { + query->magic = 0; + isc_mem_free(mctx, query); + } + + check_if_done(); + UNLOCK_LOOKUP; +} + +/*% + * Cancel a lookup, sending isc_socket_cancel() requests to all outstanding + * IO sockets. The cancel handlers should take care of cleaning up the + * query and lookup structures + */ +static void +cancel_lookup(dig_lookup_t *lookup) { + dig_query_t *query, *next; + + debug("cancel_lookup(%p)", lookup); + query = ISC_LIST_HEAD(lookup->q); + while (query != NULL) { + REQUIRE(DIG_VALID_QUERY(query)); + next = ISC_LIST_NEXT(query, link); + if (query->sock != NULL) { + isc_socket_cancel(query->sock, global_task, + ISC_SOCKCANCEL_ALL); + check_if_done(); + } else { + clear_query(query); + } + query = next; + } + lookup->pending = false; + lookup->retries = 0; +} + +static void +bringup_timer(dig_query_t *query, unsigned int default_timeout) { + dig_lookup_t *l; + unsigned int local_timeout; + isc_result_t result; + REQUIRE(DIG_VALID_QUERY(query)); + + debug("bringup_timer(%p)", query); + /* + * If the timer already exists, that means we're calling this + * a second time (for a retry). Don't need to recreate it, + * just reset it. + */ + l = query->lookup; + if (ISC_LINK_LINKED(query, link) && ISC_LIST_NEXT(query, link) != NULL) + { + local_timeout = SERVER_TIMEOUT; + } else { + if (timeout == 0) { + local_timeout = default_timeout; + } else { + local_timeout = timeout; + } + } + debug("have local timeout of %d", local_timeout); + isc_interval_set(&l->interval, local_timeout, 0); + if (query->timer != NULL) { + isc_timer_destroy(&query->timer); + } + result = isc_timer_create(timermgr, isc_timertype_once, NULL, + &l->interval, global_task, connect_timeout, + query, &query->timer); + check_result(result, "isc_timer_create"); +} + +static void +force_timeout(dig_query_t *query) { + isc_event_t *event; + + debug("force_timeout(%p)", query); + event = isc_event_allocate(mctx, query, ISC_TIMEREVENT_IDLE, + connect_timeout, query, sizeof(isc_event_t)); + isc_task_send(global_task, &event); + + /* + * The timer may have expired if, for example, get_address() takes + * long time and the timer was running on a different thread. + * We need to cancel the possible timeout event not to confuse + * ourselves due to the duplicate events. + */ + if (query->timer != NULL) { + isc_timer_destroy(&query->timer); + } +} + +static void +connect_done(isc_task_t *task, isc_event_t *event); + +/*% + * Unlike send_udp, this can't be called multiple times with the same + * query. When we retry TCP, we requeue the whole lookup, which should + * start anew. + */ +static void +send_tcp_connect(dig_query_t *query) { + isc_result_t result; + dig_query_t *next; + dig_lookup_t *l; + REQUIRE(DIG_VALID_QUERY(query)); + + debug("send_tcp_connect(%p)", query); + + l = query->lookup; + query->waiting_connect = true; + query->lookup->current_query = query; + result = get_address(query->servname, port, &query->sockaddr); + if (result != ISC_R_SUCCESS) { + /* + * This servname doesn't have an address. Try the next server + * by triggering an immediate 'timeout' (we lie, but the effect + * is the same). + */ + force_timeout(query); + return; + } + + if (!l->mapped && isc_sockaddr_pf(&query->sockaddr) == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&query->sockaddr.type.sin6.sin6_addr)) + { + isc_netaddr_t netaddr; + char buf[ISC_NETADDR_FORMATSIZE]; + + isc_netaddr_fromsockaddr(&netaddr, &query->sockaddr); + isc_netaddr_format(&netaddr, buf, sizeof(buf)); + dighost_warning("Skipping mapped address '%s'", buf); + + query->waiting_connect = false; + if (ISC_LINK_LINKED(query, link)) { + next = ISC_LIST_NEXT(query, link); + } else { + next = NULL; + } + l = query->lookup; + clear_query(query); + if (next == NULL) { + dighost_warning("No acceptable nameservers"); + check_next_lookup(l); + return; + } + send_tcp_connect(next); + return; + } + + INSIST(query->sock == NULL); + + if (keep != NULL && isc_sockaddr_equal(&keepaddr, &query->sockaddr)) { + sockcount++; + isc_socket_attach(keep, &query->sock); + query->waiting_connect = false; + launch_next_query(query, true); + goto search; + } + + result = isc_socket_create(socketmgr, isc_sockaddr_pf(&query->sockaddr), + isc_sockettype_tcp, &query->sock); + check_result(result, "isc_socket_create"); + sockcount++; + debug("sockcount=%d", sockcount); + if (query->lookup->dscp != -1) { + isc_socket_dscp(query->sock, query->lookup->dscp); + } + isc_socket_ipv6only(query->sock, !query->lookup->mapped); + if (specified_source) { + result = isc_socket_bind(query->sock, &bind_address, + ISC_SOCKET_REUSEADDRESS); + } else { + if ((isc_sockaddr_pf(&query->sockaddr) == AF_INET) && have_ipv4) + { + isc_sockaddr_any(&bind_any); + } else { + isc_sockaddr_any6(&bind_any); + } + result = isc_socket_bind(query->sock, &bind_any, 0); + } + check_result(result, "isc_socket_bind"); + bringup_timer(query, TCP_TIMEOUT); + result = isc_socket_connect(query->sock, &query->sockaddr, global_task, + connect_done, query); + check_result(result, "isc_socket_connect"); +search: + /* + * If we're at the endgame of a nameserver search, we need to + * immediately bring up all the queries. Do it here. + */ + if (l->ns_search_only && !l->trace_root) { + debug("sending next, since searching"); + if (ISC_LINK_LINKED(query, link)) { + next = ISC_LIST_NEXT(query, link); + ISC_LIST_DEQUEUE(l->q, query, link); + } else { + next = NULL; + } + ISC_LIST_ENQUEUE(l->connecting, query, clink); + if (next != NULL) { + send_tcp_connect(next); + } + } +} + +static void +print_query_size(dig_query_t *query) { + if (!yaml) { + printf(";; QUERY SIZE: %u\n\n", + isc_buffer_usedlength(&query->lookup->renderbuf)); + } +} + +/*% + * Send a UDP packet to the remote nameserver, possible starting the + * recv action as well. Also make sure that the timer is running and + * is properly reset. + */ +static void +send_udp(dig_query_t *query) { + dig_lookup_t *l = NULL; + isc_result_t result; + dig_query_t *next; + isc_region_t r; + isc_socketevent_t *sevent; + REQUIRE(DIG_VALID_QUERY(query)); + + debug("send_udp(%p)", query); + + l = query->lookup; + bringup_timer(query, UDP_TIMEOUT); + l->current_query = query; + debug("working on lookup %p, query %p", query->lookup, query); + if (!query->recv_made) { + /* XXX Check the sense of this, need assertion? */ + query->waiting_connect = false; + result = get_address(query->servname, port, &query->sockaddr); + if (result != ISC_R_SUCCESS) { + /* This servname doesn't have an address. */ + force_timeout(query); + return; + } + + if (!l->mapped && + isc_sockaddr_pf(&query->sockaddr) == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&query->sockaddr.type.sin6.sin6_addr)) + { + isc_netaddr_t netaddr; + char buf[ISC_NETADDR_FORMATSIZE]; + + isc_netaddr_fromsockaddr(&netaddr, &query->sockaddr); + isc_netaddr_format(&netaddr, buf, sizeof(buf)); + dighost_warning("Skipping mapped address '%s'", buf); + next = ISC_LIST_NEXT(query, link); + l = query->lookup; + clear_query(query); + if (next == NULL) { + dighost_warning("No acceptable nameservers"); + check_next_lookup(l); + } else { + send_udp(next); + } + return; + } + + result = isc_socket_create(socketmgr, + isc_sockaddr_pf(&query->sockaddr), + isc_sockettype_udp, &query->sock); + check_result(result, "isc_socket_create"); + sockcount++; + debug("sockcount=%d", sockcount); + if (query->lookup->dscp != -1) { + isc_socket_dscp(query->sock, query->lookup->dscp); + } + isc_socket_ipv6only(query->sock, !query->lookup->mapped); + if (specified_source) { + result = isc_socket_bind(query->sock, &bind_address, + ISC_SOCKET_REUSEADDRESS); + } else { + isc_sockaddr_anyofpf(&bind_any, + isc_sockaddr_pf(&query->sockaddr)); + result = isc_socket_bind(query->sock, &bind_any, 0); + } + check_result(result, "isc_socket_bind"); + + query->recv_made = true; + isc_buffer_availableregion(&query->recvbuf, &r); + debug("recving with lookup=%p, query=%p, sock=%p", + query->lookup, query, query->sock); + result = isc_socket_recv(query->sock, &r, 1, global_task, + recv_done, query); + check_result(result, "isc_socket_recv"); + recvcount++; + debug("recvcount=%d", recvcount); + } + isc_buffer_usedregion(&query->sendbuf, &r); + debug("sending a request"); + if (query->lookup->use_usec) { + TIME_NOW_HIRES(&query->time_sent); + } else { + TIME_NOW(&query->time_sent); + } + INSIST(query->sock != NULL); + query->waiting_senddone = true; + sevent = isc_socket_socketevent( + mctx, query->sock, ISC_SOCKEVENT_SENDDONE, send_done, query); + result = isc_socket_sendto2(query->sock, &r, global_task, + &query->sockaddr, NULL, sevent, + ISC_SOCKFLAG_NORETRY); + check_result(result, "isc_socket_sendto2"); + sendcount++; + + /* XXX qrflag, print_query, etc... */ + if (!ISC_LIST_EMPTY(query->lookup->q) && query->lookup->qr) { + extrabytes = 0; + dighost_printmessage(ISC_LIST_HEAD(query->lookup->q), + &query->lookup->renderbuf, + query->lookup->sendmsg, true); + if (query->lookup->stats) { + print_query_size(query); + } + } +} + +/*% + * If there are more servers available for querying within 'lookup', initiate a + * TCP or UDP query to the next available server and return true; otherwise, + * return false. + */ +static bool +try_next_server(dig_lookup_t *lookup) { + dig_query_t *current_query, *next_query; + + current_query = lookup->current_query; + if (current_query == NULL || !ISC_LINK_LINKED(current_query, link)) { + return (false); + } + + next_query = ISC_LIST_NEXT(current_query, link); + if (next_query == NULL) { + return (false); + } + + debug("trying next server..."); + + if (lookup->tcp_mode) { + send_tcp_connect(next_query); + } else { + send_udp(next_query); + } + + return (true); +} + +/*% + * IO timeout handler, used for both connect and recv timeouts. If + * retries are still allowed, either resend the UDP packet or queue a + * new TCP lookup. Otherwise, cancel the lookup. + */ +static void +connect_timeout(isc_task_t *task, isc_event_t *event) { + dig_lookup_t *l = NULL; + dig_query_t *query = NULL; + + UNUSED(task); + REQUIRE(event->ev_type == ISC_TIMEREVENT_IDLE); + + debug("connect_timeout(%p)", event->ev_arg); + + LOCK_LOOKUP; + query = event->ev_arg; + REQUIRE(DIG_VALID_QUERY(query)); + l = query->lookup; + isc_event_free(&event); + + INSIST(!free_now); + + if (cancel_now) { + UNLOCK_LOOKUP; + return; + } + + if (try_next_server(l)) { + if (l->tcp_mode) { + if (query->sock != NULL) { + isc_socket_cancel(query->sock, NULL, + ISC_SOCKCANCEL_ALL); + } else { + clear_query(query); + } + } + UNLOCK_LOOKUP; + return; + } + + if (l->tcp_mode && query->sock != NULL) { + query->timedout = true; + isc_socket_cancel(query->sock, NULL, ISC_SOCKCANCEL_ALL); + } + + if (l->retries > 1) { + if (!l->tcp_mode) { + l->retries--; + debug("resending UDP request to first server"); + send_udp(ISC_LIST_HEAD(l->q)); + } else { + debug("making new TCP request, %d tries left", + l->retries); + l->retries--; + requeue_lookup(l, true); + cancel_lookup(l); + check_next_lookup(l); + } + } else { + if (l->ns_search_only) { + isc_netaddr_t netaddr; + char buf[ISC_NETADDR_FORMATSIZE]; + + isc_netaddr_fromsockaddr(&netaddr, &query->sockaddr); + isc_netaddr_format(&netaddr, buf, sizeof(buf)); + + dighost_error("no response from %s\n", buf); + } else { + fputs(l->cmdline, stdout); + dighost_error("connection timed out; " + "no servers could be reached\n"); + } + cancel_lookup(l); + check_next_lookup(l); + if (exitcode < 9) { + exitcode = 9; + } + } + UNLOCK_LOOKUP; +} + +/*% + * Called when a peer closes a TCP socket prematurely. + */ +static void +requeue_or_update_exitcode(dig_lookup_t *lookup) { + if (lookup->eoferr == 0U && lookup->retries > 1) { + --lookup->retries; + /* + * Peer closed the connection prematurely for the first time + * for this lookup. Try again, keeping track of this failure. + */ + dig_lookup_t *requeued_lookup = requeue_lookup(lookup, true); + requeued_lookup->eoferr++; + } else { + /* + * Peer closed the connection prematurely and it happened + * previously for this lookup. Indicate an error. + */ + exitcode = 9; + } +} + +/*% + * Event handler for the TCP recv which gets the length header of TCP + * packets. Start the next recv of length bytes. + */ +static void +tcp_length_done(isc_task_t *task, isc_event_t *event) { + isc_socketevent_t *sevent; + isc_buffer_t b; + isc_region_t r; + isc_result_t result; + dig_query_t *query = NULL; + dig_lookup_t *l; + uint16_t length; + + REQUIRE(event->ev_type == ISC_SOCKEVENT_RECVDONE); + INSIST(!free_now); + + UNUSED(task); + + debug("tcp_length_done(%p)", event->ev_arg); + + LOCK_LOOKUP; + sevent = (isc_socketevent_t *)event; + query = event->ev_arg; + REQUIRE(DIG_VALID_QUERY(query)); + + recvcount--; + INSIST(recvcount >= 0); + + if (sevent->result == ISC_R_CANCELED) { + isc_event_free(&event); + l = query->lookup; + clear_query(query); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + if (sevent->result != ISC_R_SUCCESS) { + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr)); + dighost_error("communications error to %s: %s\n", sockstr, + isc_result_totext(sevent->result)); + if (keep != NULL) { + isc_socket_detach(&keep); + } + l = query->lookup; + isc_socket_detach(&query->sock); + sockcount--; + debug("sockcount=%d", sockcount); + INSIST(sockcount >= 0); + if (sevent->result == ISC_R_EOF) { + requeue_or_update_exitcode(l); + } + isc_event_free(&event); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + isc_buffer_init(&b, sevent->region.base, sevent->n); + isc_buffer_add(&b, sevent->n); + length = isc_buffer_getuint16(&b); + + if (length == 0) { + isc_event_free(&event); + launch_next_query(query, false); + UNLOCK_LOOKUP; + return; + } + + /* + * Even though the buffer was already init'ed, we need + * to redo it now, to force the length we want. + */ + isc_buffer_invalidate(&query->recvbuf); + isc_buffer_init(&query->recvbuf, query->recvspace, length); + isc_buffer_availableregion(&query->recvbuf, &r); + debug("recving with lookup=%p, query=%p", query->lookup, query); + result = isc_socket_recv(query->sock, &r, length, task, recv_done, + query); + check_result(result, "isc_socket_recv"); + recvcount++; + debug("resubmitted recv request with length %d, recvcount=%d", length, + recvcount); + isc_event_free(&event); + UNLOCK_LOOKUP; +} + +/*% + * For transfers that involve multiple recvs (XFR's in particular), + * launch the next recv. + */ +static void +launch_next_query(dig_query_t *query, bool include_question) { + isc_result_t result; + dig_lookup_t *l; + isc_region_t r; + REQUIRE(DIG_VALID_QUERY(query)); + + INSIST(!free_now); + + debug("launch_next_query(%p)", query); + + if (!query->lookup->pending) { + debug("ignoring launch_next_query because !pending"); + isc_socket_detach(&query->sock); + sockcount--; + debug("sockcount=%d", sockcount); + INSIST(sockcount >= 0); + query->waiting_connect = false; + l = query->lookup; + clear_query(query); + check_next_lookup(l); + return; + } + + isc_buffer_clear(&query->lengthbuf); + isc_buffer_availableregion(&query->lengthbuf, &r); + result = isc_socket_recv(query->sock, &r, 0, global_task, + tcp_length_done, query); + check_result(result, "isc_socket_recv"); + recvcount++; + debug("recvcount=%d", recvcount); + if (!query->first_soa_rcvd) { + debug("sending a request in launch_next_query"); + if (query->lookup->use_usec) { + TIME_NOW_HIRES(&query->time_sent); + } else { + TIME_NOW(&query->time_sent); + } + query->waiting_senddone = true; + isc_buffer_clear(&query->tmpsendbuf); + isc_buffer_putuint16(&query->tmpsendbuf, + isc_buffer_usedlength(&query->sendbuf)); + if (include_question) { + isc_buffer_usedregion(&query->sendbuf, &r); + isc_buffer_copyregion(&query->tmpsendbuf, &r); + } + isc_buffer_usedregion(&query->tmpsendbuf, &r); + result = isc_socket_send(query->sock, &r, global_task, + send_done, query); + check_result(result, "isc_socket_send"); + sendcount++; + debug("sendcount=%d", sendcount); + + /* XXX qrflag, print_query, etc... */ + if (!ISC_LIST_EMPTY(query->lookup->q) && query->lookup->qr) { + extrabytes = 0; + dighost_printmessage(ISC_LIST_HEAD(query->lookup->q), + &query->lookup->renderbuf, + query->lookup->sendmsg, true); + if (query->lookup->stats) { + print_query_size(query); + } + } + } + query->waiting_connect = false; +#if 0 + check_next_lookup(query->lookup); +#endif /* if 0 */ + return; +} + +/*% + * Event handler for TCP connect complete. Make sure the connection was + * successful, then pass into launch_next_query to actually send the + * question. + */ +static void +connect_done(isc_task_t *task, isc_event_t *event) { + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + isc_socketevent_t *sevent = NULL; + dig_query_t *query = NULL, *next; + dig_lookup_t *l; + + UNUSED(task); + + REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT); + INSIST(!free_now); + + debug("connect_done(%p)", event->ev_arg); + + LOCK_LOOKUP; + sevent = (isc_socketevent_t *)event; + query = sevent->ev_arg; + REQUIRE(DIG_VALID_QUERY(query)); + + INSIST(query->waiting_connect); + + query->waiting_connect = false; + + if (sevent->result == ISC_R_CANCELED) { + debug("in cancel handler"); + isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr)); + if (query->timedout) { + dighost_warning("Connection to %s(%s) for %s failed: " + "%s.", + sockstr, query->servname, + query->lookup->textname, + isc_result_totext(ISC_R_TIMEDOUT)); + } + isc_socket_detach(&query->sock); + INSIST(sockcount > 0); + sockcount--; + debug("sockcount=%d", sockcount); + query->waiting_connect = false; + isc_event_free(&event); + l = query->lookup; + clear_query(query); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + if (sevent->result != ISC_R_SUCCESS) { + debug("unsuccessful connection: %s", + isc_result_totext(sevent->result)); + isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr)); + if (sevent->result != ISC_R_CANCELED) { + dighost_warning("Connection to %s(%s) for %s failed: " + "%s.", + sockstr, query->servname, + query->lookup->textname, + isc_result_totext(sevent->result)); + } + isc_socket_detach(&query->sock); + INSIST(sockcount > 0); + sockcount--; + /* XXX Clean up exitcodes */ + if (exitcode < 9) { + exitcode = 9; + } + debug("sockcount=%d", sockcount); + query->waiting_connect = false; + isc_event_free(&event); + l = query->lookup; + if ((l->current_query != NULL) && + (ISC_LINK_LINKED(l->current_query, link))) + { + next = ISC_LIST_NEXT(l->current_query, link); + } else { + next = NULL; + } + clear_query(query); + if (next != NULL) { + bringup_timer(next, TCP_TIMEOUT); + send_tcp_connect(next); + } else { + check_next_lookup(l); + } + UNLOCK_LOOKUP; + return; + } + exitcode = 0; + if (keep_open) { + if (keep != NULL) { + isc_socket_detach(&keep); + } + isc_socket_attach(query->sock, &keep); + keepaddr = query->sockaddr; + } + launch_next_query(query, true); + isc_event_free(&event); + UNLOCK_LOOKUP; +} + +/*% + * Check if the ongoing XFR needs more data before it's complete, using + * the semantics of IXFR and AXFR protocols. Much of the complexity of + * this routine comes from determining when an IXFR is complete. + * false means more data is on the way, and the recv has been issued. + */ +static bool +check_for_more_data(dig_query_t *query, dns_message_t *msg, + isc_socketevent_t *sevent) { + dns_rdataset_t *rdataset = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdata_soa_t soa; + uint32_t ixfr_serial = query->lookup->ixfr_serial, serial; + isc_result_t result; + bool ixfr = query->lookup->rdtype == dns_rdatatype_ixfr; + bool axfr = query->lookup->rdtype == dns_rdatatype_axfr; + + if (ixfr) { + axfr = query->ixfr_axfr; + } + + debug("check_for_more_data(%p)", query); + + /* + * By the time we're in this routine, we know we're doing + * either an AXFR or IXFR. If there's no second_rr_type, + * then we don't yet know which kind of answer we got back + * from the server. Here, we're going to walk through the + * rr's in the message, acting as necessary whenever we hit + * an SOA rr. + */ + + query->msg_count++; + query->byte_count += sevent->n; + result = dns_message_firstname(msg, DNS_SECTION_ANSWER); + if (result != ISC_R_SUCCESS) { + puts("; Transfer failed."); + return (true); + } + do { + dns_name_t *name; + name = NULL; + dns_message_currentname(msg, DNS_SECTION_ANSWER, &name); + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + result = dns_rdataset_first(rdataset); + if (result != ISC_R_SUCCESS) { + continue; + } + do { + query->rr_count++; + dns_rdata_reset(&rdata); + dns_rdataset_current(rdataset, &rdata); + /* + * If this is the first rr, make sure + * it's an SOA + */ + if ((!query->first_soa_rcvd) && + (rdata.type != dns_rdatatype_soa)) + { + puts("; Transfer failed. " + "Didn't start with SOA answer."); + return (true); + } + if ((!query->second_rr_rcvd) && + (rdata.type != dns_rdatatype_soa)) + { + query->second_rr_rcvd = true; + query->second_rr_serial = 0; + debug("got the second rr as nonsoa"); + axfr = query->ixfr_axfr = true; + goto next_rdata; + } + + /* + * If the record is anything except an SOA + * now, just continue on... + */ + if (rdata.type != dns_rdatatype_soa) { + goto next_rdata; + } + + /* Now we have an SOA. Work with it. */ + debug("got an SOA"); + result = dns_rdata_tostruct(&rdata, &soa, NULL); + check_result(result, "dns_rdata_tostruct"); + serial = soa.serial; + dns_rdata_freestruct(&soa); + if (!query->first_soa_rcvd) { + query->first_soa_rcvd = true; + query->first_rr_serial = serial; + debug("this is the first serial %u", + serial); + if (ixfr && + isc_serial_ge(ixfr_serial, serial)) + { + debug("got up to date " + "response"); + goto doexit; + } + goto next_rdata; + } + if (axfr) { + debug("doing axfr, got second SOA"); + goto doexit; + } + if (!query->second_rr_rcvd) { + if (query->first_rr_serial == serial) { + debug("doing ixfr, got " + "empty zone"); + goto doexit; + } + debug("this is the second serial %u", + serial); + query->second_rr_rcvd = true; + query->second_rr_serial = serial; + goto next_rdata; + } + /* + * If we get to this point, we're doing an + * IXFR and have to start really looking + * at serial numbers. + */ + if (query->first_rr_serial == serial) { + debug("got a match for ixfr"); + if (!query->first_repeat_rcvd) { + query->first_repeat_rcvd = true; + goto next_rdata; + } + debug("done with ixfr"); + goto doexit; + } + debug("meaningless soa %u", serial); + next_rdata: + result = dns_rdataset_next(rdataset); + } while (result == ISC_R_SUCCESS); + } + result = dns_message_nextname(msg, DNS_SECTION_ANSWER); + } while (result == ISC_R_SUCCESS); + launch_next_query(query, false); + return (false); +doexit: + dighost_received(sevent->n, &sevent->address, query); + return (true); +} + +static void +process_cookie(dig_lookup_t *l, dns_message_t *msg, isc_buffer_t *optbuf, + size_t optlen) { + char bb[256]; + isc_buffer_t hexbuf; + size_t len; + const unsigned char *sent; + bool copy = true; + isc_result_t result; + + if (l->cookie != NULL) { + isc_buffer_init(&hexbuf, bb, sizeof(bb)); + result = isc_hex_decodestring(l->cookie, &hexbuf); + check_result(result, "isc_hex_decodestring"); + sent = isc_buffer_base(&hexbuf); + len = isc_buffer_usedlength(&hexbuf); + } else { + sent = cookie; + len = sizeof(cookie); + } + + INSIST(msg->cc_ok == 0 && msg->cc_bad == 0); + if (len >= 8 && optlen >= 8U) { + if (isc_safe_memequal(isc_buffer_current(optbuf), sent, 8)) { + msg->cc_ok = 1; + } else { + dighost_warning("Warning: Client COOKIE mismatch"); + msg->cc_bad = 1; + copy = false; + } + } else { + dighost_warning("Warning: COOKIE bad token (too short)"); + msg->cc_bad = 1; + copy = false; + } + if (copy) { + isc_region_t r; + + r.base = isc_buffer_current(optbuf); + r.length = (unsigned int)optlen; + isc_buffer_init(&hexbuf, servercookie, sizeof(servercookie)); + result = isc_hex_totext(&r, 2, "", &hexbuf); + check_result(result, "isc_hex_totext"); + if (isc_buffer_availablelength(&hexbuf) > 0) { + isc_buffer_putuint8(&hexbuf, 0); + l->cookie = servercookie; + } + } + isc_buffer_forward(optbuf, (unsigned int)optlen); +} + +static void +process_opt(dig_lookup_t *l, dns_message_t *msg) { + dns_rdata_t rdata; + isc_result_t result; + isc_buffer_t optbuf; + uint16_t optcode, optlen; + dns_rdataset_t *opt = msg->opt; + bool seen_cookie = false; + + result = dns_rdataset_first(opt); + if (result == ISC_R_SUCCESS) { + dns_rdata_init(&rdata); + dns_rdataset_current(opt, &rdata); + isc_buffer_init(&optbuf, rdata.data, rdata.length); + isc_buffer_add(&optbuf, rdata.length); + while (isc_buffer_remaininglength(&optbuf) >= 4) { + optcode = isc_buffer_getuint16(&optbuf); + optlen = isc_buffer_getuint16(&optbuf); + switch (optcode) { + case DNS_OPT_COOKIE: + /* + * Only process the first cookie option. + */ + if (seen_cookie) { + isc_buffer_forward(&optbuf, optlen); + break; + } + process_cookie(l, msg, &optbuf, optlen); + seen_cookie = true; + break; + default: + isc_buffer_forward(&optbuf, optlen); + break; + } + } + } +} + +static int +ednsvers(dns_rdataset_t *opt) { + return ((opt->ttl >> 16) & 0xff); +} + +/*% + * Event handler for recv complete. Perform whatever actions are necessary, + * based on the specifics of the user's request. + */ +static void +recv_done(isc_task_t *task, isc_event_t *event) { + isc_socketevent_t *sevent = NULL; + isc_region_t r; + dig_query_t *query = NULL; + isc_buffer_t b; + dns_message_t *msg = NULL; + isc_result_t result; + dig_lookup_t *n, *l; + bool docancel = false; + bool match = true; + bool done_process_opt = false; + unsigned int parseflags; + dns_messageid_t id; + unsigned int msgflags; + int newedns; + + UNUSED(task); + INSIST(!free_now); + + debug("recv_done(%p)", event->ev_arg); + + LOCK_LOOKUP; + recvcount--; + debug("recvcount=%d", recvcount); + INSIST(recvcount >= 0); + + query = event->ev_arg; + if (query->lookup->use_usec) { + TIME_NOW_HIRES(&query->time_recv); + } else { + TIME_NOW(&query->time_recv); + } + + l = query->lookup; + + REQUIRE(event->ev_type == ISC_SOCKEVENT_RECVDONE); + sevent = (isc_socketevent_t *)event; + + isc_buffer_init(&b, sevent->region.base, sevent->n); + isc_buffer_add(&b, sevent->n); + + if ((l->tcp_mode) && (query->timer != NULL)) { + isc_timer_touch(query->timer); + } + if ((!l->pending && !l->ns_search_only) || cancel_now) { + debug("no longer pending. Got %s", + isc_result_totext(sevent->result)); + query->waiting_connect = false; + + isc_event_free(&event); + clear_query(query); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + + if (sevent->result != ISC_R_SUCCESS) { + if (sevent->result == ISC_R_CANCELED) { + debug("in recv cancel handler"); + query->waiting_connect = false; + } else { + dighost_error("communications error: %s\n", + isc_result_totext(sevent->result)); + if (keep != NULL) { + isc_socket_detach(&keep); + } + isc_socket_detach(&query->sock); + sockcount--; + debug("sockcount=%d", sockcount); + INSIST(sockcount >= 0); + } + if (sevent->result == ISC_R_EOF) { + requeue_or_update_exitcode(l); + } + isc_event_free(&event); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + + if (!l->tcp_mode && + !isc_sockaddr_compare(&sevent->address, &query->sockaddr, + ISC_SOCKADDR_CMPADDR | ISC_SOCKADDR_CMPPORT | + ISC_SOCKADDR_CMPSCOPE | + ISC_SOCKADDR_CMPSCOPEZERO)) + { + char buf1[ISC_SOCKADDR_FORMATSIZE]; + char buf2[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t any; + + if (isc_sockaddr_pf(&query->sockaddr) == AF_INET) { + isc_sockaddr_any(&any); + } else { + isc_sockaddr_any6(&any); + } + + /* + * We don't expect a match when the packet is + * sent to 0.0.0.0, :: or to a multicast addresses. + * XXXMPA broadcast needs to be handled here as well. + */ + if ((!isc_sockaddr_eqaddr(&query->sockaddr, &any) && + !isc_sockaddr_ismulticast(&query->sockaddr)) || + isc_sockaddr_getport(&query->sockaddr) != + isc_sockaddr_getport(&sevent->address)) + { + isc_sockaddr_format(&sevent->address, buf1, + sizeof(buf1)); + isc_sockaddr_format(&query->sockaddr, buf2, + sizeof(buf2)); + dighost_warning("reply from unexpected source: %s," + " expected %s\n", + buf1, buf2); + if (!l->accept_reply_unexpected_src) { + match = false; + } + } + } + + result = dns_message_peekheader(&b, &id, &msgflags); + if (result != ISC_R_SUCCESS || l->sendmsg->id != id) { + match = false; + if (l->tcp_mode) { + bool fail = true; + if (result == ISC_R_SUCCESS) { + if ((!query->first_soa_rcvd || query->warn_id)) + { + dighost_warning("%s: ID mismatch: " + "expected ID %u, got " + "%u", + query->first_soa_rcvd + ? "WARNING" + : "ERROR", + l->sendmsg->id, id); + } + if (query->first_soa_rcvd) { + fail = false; + } + query->warn_id = false; + } else { + dighost_warning("ERROR: short (< header size) " + "message"); + } + if (fail) { + isc_event_free(&event); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + match = true; + } else if (result == ISC_R_SUCCESS) { + dighost_warning("Warning: ID mismatch: expected ID %u," + " got %u", + l->sendmsg->id, id); + } else { + dighost_warning("Warning: short (< header size) " + "message received"); + } + } + + if (result == ISC_R_SUCCESS && (msgflags & DNS_MESSAGEFLAG_QR) == 0) { + dighost_warning("Warning: query response not set"); + } + + if (!match) { + goto udp_mismatch; + } + + dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &msg); + + if (tsigkey != NULL) { + if (l->querysig == NULL) { + debug("getting initial querysig"); + result = dns_message_getquerytsig(l->sendmsg, mctx, + &l->querysig); + check_result(result, "dns_message_getquerytsig"); + } + result = dns_message_setquerytsig(msg, l->querysig); + check_result(result, "dns_message_setquerytsig"); + result = dns_message_settsigkey(msg, tsigkey); + check_result(result, "dns_message_settsigkey"); + msg->tsigctx = l->tsigctx; + l->tsigctx = NULL; + if (l->msgcounter != 0) { + msg->tcp_continuation = 1; + } + l->msgcounter++; + } + + debug("before parse starts"); + parseflags = DNS_MESSAGEPARSE_PRESERVEORDER; + if (l->besteffort) { + parseflags |= DNS_MESSAGEPARSE_BESTEFFORT; + parseflags |= DNS_MESSAGEPARSE_IGNORETRUNCATION; + } + result = dns_message_parse(msg, &b, parseflags); + if (result == DNS_R_RECOVERABLE) { + dighost_warning("Warning: Message parser reports malformed " + "message packet."); + result = ISC_R_SUCCESS; + } + if (result != ISC_R_SUCCESS) { + if (!yaml) { + printf(";; Got bad packet: %s\n", + isc_result_totext(result)); + hex_dump(&b); + } + query->waiting_connect = false; + dns_message_detach(&msg); + isc_event_free(&event); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + if (msg->opcode != l->opcode) { + char expect[20] = { 0 }, got[20] = { 0 }; + + isc_buffer_init(&b, &expect, sizeof(expect)); + result = dns_opcode_totext(l->opcode, &b); + check_result(result, "dns_opcode_totext"); + + isc_buffer_init(&b, &got, sizeof(got)); + result = dns_opcode_totext(msg->opcode, &b); + check_result(result, "dns_opcode_totext"); + + dighost_warning("Warning: Opcode mismatch: expected %s, got %s", + expect, got); + + dns_message_detach(&msg); + if (l->tcp_mode) { + isc_event_free(&event); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } else { + goto udp_mismatch; + } + } + if (msg->counts[DNS_SECTION_QUESTION] != 0) { + match = true; + for (result = dns_message_firstname(msg, DNS_SECTION_QUESTION); + result == ISC_R_SUCCESS && match; + result = dns_message_nextname(msg, DNS_SECTION_QUESTION)) + { + dns_name_t *name = NULL; + dns_rdataset_t *rdataset; + + dns_message_currentname(msg, DNS_SECTION_QUESTION, + &name); + for (rdataset = ISC_LIST_HEAD(name->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (l->rdtype != rdataset->type || + l->rdclass != rdataset->rdclass || + !dns_name_equal(l->name, name)) + { + char namestr[DNS_NAME_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + char classbuf[DNS_RDATACLASS_FORMATSIZE]; + dns_name_format(name, namestr, + sizeof(namestr)); + dns_rdatatype_format(rdataset->type, + typebuf, + sizeof(typebuf)); + dns_rdataclass_format(rdataset->rdclass, + classbuf, + sizeof(classbuf)); + dighost_warning(";; Question section " + "mismatch: got " + "%s/%s/%s", + namestr, typebuf, + classbuf); + match = false; + } + } + } + if (!match) { + dns_message_detach(&msg); + if (l->tcp_mode) { + isc_event_free(&event); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } else { + goto udp_mismatch; + } + } + } + if (msg->rcode == dns_rcode_badvers && msg->opt != NULL && + (newedns = ednsvers(msg->opt)) < l->edns && l->ednsneg) + { + /* + * Add minimum EDNS version required checks here if needed. + */ + dighost_comments(l, "BADVERS, retrying with EDNS version %u.", + (unsigned int)newedns); + l->edns = newedns; + n = requeue_lookup(l, true); + if (l->trace && l->trace_root) { + n->rdtype = l->qrdtype; + } + dns_message_detach(&msg); + isc_event_free(&event); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0 && !l->ignore && + !l->tcp_mode) + { + if (l->cookie == NULL && l->sendcookie && msg->opt != NULL) { + process_opt(l, msg); + } + dighost_comments(l, "Truncated, retrying in TCP mode."); + n = requeue_lookup(l, true); + n->tcp_mode = true; + if (l->trace && l->trace_root) { + n->rdtype = l->qrdtype; + } + dns_message_detach(&msg); + isc_event_free(&event); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + if (msg->rcode == dns_rcode_badcookie && !l->tcp_mode && + l->sendcookie && l->badcookie) + { + process_opt(l, msg); + if (msg->cc_ok) { + dighost_comments(l, "BADCOOKIE, retrying%s.", + l->seenbadcookie ? " in TCP mode" + : ""); + n = requeue_lookup(l, true); + if (l->seenbadcookie) { + n->tcp_mode = true; + } + n->seenbadcookie = true; + if (l->trace && l->trace_root) { + n->rdtype = l->qrdtype; + } + dns_message_detach(&msg); + isc_event_free(&event); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + UNLOCK_LOOKUP; + return; + } + done_process_opt = true; + } + if ((msg->rcode == dns_rcode_servfail && !l->servfail_stops) || + (check_ra && (msg->flags & DNS_MESSAGEFLAG_RA) == 0 && l->recurse)) + { + dig_query_t *next = ISC_LIST_NEXT(query, link); + if (l->current_query == query) { + l->current_query = NULL; + } + if (next != NULL) { + debug("sending query %p\n", next); + if (l->tcp_mode) { + send_tcp_connect(next); + } else { + send_udp(next); + } + } + /* + * If our query is at the head of the list and there + * is no next, we're the only one left, so fall + * through to print the message. + */ + if ((ISC_LIST_HEAD(l->q) != query) || + (ISC_LIST_NEXT(query, link) != NULL)) + { + dighost_comments(l, + "Got %s from %s, trying next " + "server", + msg->rcode == dns_rcode_servfail + ? "SERVFAIL reply" + : "recursion not available", + query->servname); + clear_query(query); + check_next_lookup(l); + dns_message_detach(&msg); + isc_event_free(&event); + UNLOCK_LOOKUP; + return; + } + } + + if (tsigkey != NULL) { + result = dns_tsig_verify(&b, msg, NULL, NULL); + if (result != ISC_R_SUCCESS) { + dighost_warning("Couldn't verify signature: %s", + isc_result_totext(result)); + validated = false; + } + l->tsigctx = msg->tsigctx; + msg->tsigctx = NULL; + if (l->querysig != NULL) { + debug("freeing querysig buffer %p", l->querysig); + isc_buffer_free(&l->querysig); + } + result = dns_message_getquerytsig(msg, mctx, &l->querysig); + check_result(result, "dns_message_getquerytsig"); + } + + extrabytes = isc_buffer_remaininglength(&b); + + debug("after parse"); + if (l->doing_xfr && l->xfr_q == NULL) { + l->xfr_q = query; + /* + * Once we are in the XFR message, increase + * the timeout to much longer, so brief network + * outages won't cause the XFR to abort + */ + if (timeout != INT_MAX && query->timer != NULL) { + unsigned int local_timeout; + + if (timeout == 0) { + if (l->tcp_mode) { + local_timeout = TCP_TIMEOUT * 4; + } else { + local_timeout = UDP_TIMEOUT * 4; + } + } else { + if (timeout < (INT_MAX / 4)) { + local_timeout = timeout * 4; + } else { + local_timeout = INT_MAX; + } + } + debug("have local timeout of %d", local_timeout); + isc_interval_set(&l->interval, local_timeout, 0); + result = isc_timer_reset(query->timer, + isc_timertype_once, NULL, + &l->interval, false); + check_result(result, "isc_timer_reset"); + } + } + + if (!done_process_opt) { + if (l->cookie != NULL) { + if (msg->opt == NULL) { + dighost_warning("expected opt record in " + "response"); + } else { + process_opt(l, msg); + } + } else if (l->sendcookie && msg->opt != NULL) { + process_opt(l, msg); + } + } + if (!l->doing_xfr || l->xfr_q == query) { + if (msg->rcode == dns_rcode_nxdomain && + (l->origin != NULL || l->need_search)) + { + if (!next_origin(query->lookup) || showsearch) { + dighost_printmessage(query, &b, msg, true); + dighost_received(isc_buffer_usedlength(&b), + &sevent->address, query); + } + } else if (!l->trace && !l->ns_search_only) { + dighost_printmessage(query, &b, msg, true); + } else if (l->trace) { + int nl = 0; + int count = msg->counts[DNS_SECTION_ANSWER]; + + debug("in TRACE code"); + if (!l->ns_search_only) { + dighost_printmessage(query, &b, msg, true); + } + + l->rdtype = l->qrdtype; + if (l->trace_root || (l->ns_search_only && count > 0)) { + if (!l->trace_root) { + l->rdtype = dns_rdatatype_soa; + } + nl = followup_lookup(msg, query, + DNS_SECTION_ANSWER); + l->trace_root = false; + } else if (count == 0) { + nl = followup_lookup(msg, query, + DNS_SECTION_AUTHORITY); + } + if (nl == 0) { + docancel = true; + } + } else { + debug("in NSSEARCH code"); + + if (l->trace_root) { + /* + * This is the initial NS query. + */ + int nl; + + l->rdtype = dns_rdatatype_soa; + nl = followup_lookup(msg, query, + DNS_SECTION_ANSWER); + if (nl == 0) { + docancel = true; + } + l->trace_root = false; + usesearch = false; + } else { + dighost_printmessage(query, &b, msg, true); + } + } + } + + if (l->pending) { + debug("still pending."); + } + if (l->doing_xfr) { + if (query != l->xfr_q) { + dns_message_detach(&msg); + isc_event_free(&event); + query->waiting_connect = false; + UNLOCK_LOOKUP; + return; + } + if (!docancel) { + docancel = check_for_more_data(query, msg, sevent); + } + if (docancel) { + dns_message_detach(&msg); + clear_query(query); + cancel_lookup(l); + check_next_lookup(l); + } + } else { + if (msg->rcode == dns_rcode_noerror || l->origin == NULL) { + dighost_received(isc_buffer_usedlength(&b), + &sevent->address, query); + } + + if (!query->lookup->ns_search_only) { + query->lookup->pending = false; + } + if (!query->lookup->ns_search_only || + query->lookup->trace_root || docancel) + { + dns_message_detach(&msg); + cancel_lookup(l); + } + clear_query(query); + check_next_lookup(l); + } + if (msg != NULL) { + dns_message_detach(&msg); + } + isc_event_free(&event); + UNLOCK_LOOKUP; + return; + +udp_mismatch: + isc_buffer_invalidate(&query->recvbuf); + isc_buffer_init(&query->recvbuf, query->recvspace, COMMSIZE); + isc_buffer_availableregion(&query->recvbuf, &r); + result = isc_socket_recv(query->sock, &r, 1, global_task, recv_done, + query); + check_result(result, "isc_socket_recv"); + recvcount++; + isc_event_free(&event); + UNLOCK_LOOKUP; + return; +} + +/*% + * Turn a name into an address, using system-supplied routines. This is + * used in looking up server names, etc... and needs to use system-supplied + * routines, since they may be using a non-DNS system for these lookups. + */ +isc_result_t +get_address(char *host, in_port_t myport, isc_sockaddr_t *sockaddr) { + int count; + isc_result_t result; + bool is_running; + + is_running = isc_app_isrunning(); + if (is_running) { + isc_app_block(); + } + result = bind9_getaddresses(host, myport, sockaddr, 1, &count); + if (is_running) { + isc_app_unblock(); + } + if (result != ISC_R_SUCCESS) { + return (result); + } + + INSIST(count == 1); + + return (ISC_R_SUCCESS); +} + +int +getaddresses(dig_lookup_t *lookup, const char *host, isc_result_t *resultp) { + isc_result_t result; + isc_sockaddr_t sockaddrs[DIG_MAX_ADDRESSES]; + isc_netaddr_t netaddr; + int count, i; + dig_server_t *srv; + char tmp[ISC_NETADDR_FORMATSIZE]; + + result = bind9_getaddresses(host, 0, sockaddrs, DIG_MAX_ADDRESSES, + &count); + if (resultp != NULL) { + *resultp = result; + } + if (result != ISC_R_SUCCESS) { + if (resultp == NULL) { + fatal("couldn't get address for '%s': %s", host, + isc_result_totext(result)); + } + return (0); + } + + for (i = 0; i < count; i++) { + isc_netaddr_fromsockaddr(&netaddr, &sockaddrs[i]); + isc_netaddr_format(&netaddr, tmp, sizeof(tmp)); + srv = make_server(tmp, host); + ISC_LIST_APPEND(lookup->my_server_list, srv, link); + } + + return (count); +} + +/*% + * Initiate either a TCP or UDP lookup + */ +void +do_lookup(dig_lookup_t *lookup) { + dig_query_t *query; + + REQUIRE(lookup != NULL); + + debug("do_lookup(%p)", lookup); + lookup->pending = true; + query = ISC_LIST_HEAD(lookup->q); + if (query != NULL) { + REQUIRE(DIG_VALID_QUERY(query)); + if (lookup->tcp_mode) { + send_tcp_connect(query); + } else { + send_udp(query); + } + } +} + +/*% + * Start everything in action upon task startup. + */ +void +onrun_callback(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + isc_event_free(&event); + LOCK_LOOKUP; + start_lookup(); + UNLOCK_LOOKUP; +} + +/*% + * Make everything on the lookup queue go away. Mainly used by the + * SIGINT handler. + */ +void +cancel_all(void) { + dig_lookup_t *l, *n; + dig_query_t *q, *nq; + + debug("cancel_all()"); + + LOCK_LOOKUP; + if (free_now) { + UNLOCK_LOOKUP; + return; + } + cancel_now = true; + if (current_lookup != NULL) { + for (q = ISC_LIST_HEAD(current_lookup->q); q != NULL; q = nq) { + nq = ISC_LIST_NEXT(q, link); + debug("canceling pending query %p, belonging to %p", q, + current_lookup); + if (q->sock != NULL) { + isc_socket_cancel(q->sock, NULL, + ISC_SOCKCANCEL_ALL); + } else { + clear_query(q); + } + } + for (q = ISC_LIST_HEAD(current_lookup->connecting); q != NULL; + q = nq) + { + nq = ISC_LIST_NEXT(q, clink); + debug("canceling connecting query %p, belonging to %p", + q, current_lookup); + if (q->sock != NULL) { + isc_socket_cancel(q->sock, NULL, + ISC_SOCKCANCEL_ALL); + } else { + clear_query(q); + } + } + } + l = ISC_LIST_HEAD(lookup_list); + while (l != NULL) { + n = ISC_LIST_NEXT(l, link); + ISC_LIST_DEQUEUE(lookup_list, l, link); + try_clear_lookup(l); + l = n; + } + UNLOCK_LOOKUP; +} + +/*% + * Destroy all of the libs we are using, and get everything ready for a + * clean shutdown. + */ +void +destroy_libs(void) { +#ifdef HAVE_LIBIDN2 + isc_result_t result; +#endif /* HAVE_LIBIDN2 */ + + if (keep != NULL) { + isc_socket_detach(&keep); + } + debug("destroy_libs()"); + if (global_task != NULL) { + debug("freeing task"); + isc_task_detach(&global_task); + } + + if (taskmgr != NULL) { + debug("freeing taskmgr"); + isc_managers_destroy(&netmgr, &taskmgr); + } + LOCK_LOOKUP; + REQUIRE(sockcount == 0); + REQUIRE(recvcount == 0); + REQUIRE(sendcount == 0); + + INSIST(ISC_LIST_HEAD(lookup_list) == NULL); + INSIST(current_lookup == NULL); + INSIST(!free_now); + + free_now = true; + + flush_server_list(); + + clear_searchlist(); + +#ifdef HAVE_LIBIDN2 + result = dns_name_settotextfilter(NULL); + check_result(result, "dns_name_settotextfilter"); +#endif /* HAVE_LIBIDN2 */ + + if (socketmgr != NULL) { + debug("freeing socketmgr"); + isc_socketmgr_destroy(&socketmgr); + } + if (timermgr != NULL) { + debug("freeing timermgr"); + isc_timermgr_destroy(&timermgr); + } + if (tsigkey != NULL) { + debug("freeing key %p", tsigkey); + dns_tsigkey_detach(&tsigkey); + } + if (namebuf != NULL) { + isc_buffer_free(&namebuf); + } + + if (is_dst_up) { + debug("destroy DST lib"); + dst_lib_destroy(); + is_dst_up = false; + } + + UNLOCK_LOOKUP; + isc_mutex_destroy(&lookup_lock); + debug("Removing log context"); + isc_log_destroy(&lctx); + + debug("Destroy memory"); + if (memdebugging != 0) { + isc_mem_stats(mctx, stderr); + } + if (mctx != NULL) { + isc_mem_destroy(&mctx); + } +} + +#ifdef HAVE_LIBIDN2 +static isc_result_t +idn_output_filter(isc_buffer_t *buffer, unsigned int used_org) { + char src[MXNAME], *dst = NULL; + size_t srclen, dstlen; + isc_result_t result = ISC_R_SUCCESS; + + /* + * Copy name from 'buffer' to 'src' and terminate it with NULL. + */ + srclen = isc_buffer_usedlength(buffer) - used_org; + if (srclen >= sizeof(src)) { + warn("Input name too long to perform IDN conversion"); + goto cleanup; + } + memmove(src, (char *)isc_buffer_base(buffer) + used_org, srclen); + src[srclen] = '\0'; + + systemlocale(LC_ALL); + + /* + * Convert 'src' to the current locale's character encoding. + */ + idn_ace_to_locale(src, &dst); + + resetlocale(LC_ALL); + + /* + * Check whether the converted name will fit back into 'buffer'. + */ + dstlen = strlen(dst); + if (isc_buffer_length(buffer) < used_org + dstlen) { + result = ISC_R_NOSPACE; + goto cleanup; + } + + /* + * Put the converted name back into 'buffer'. + */ + isc_buffer_subtract(buffer, srclen); + memmove(isc_buffer_used(buffer), dst, dstlen); + isc_buffer_add(buffer, dstlen); + + /* + * Clean up. + */ +cleanup: + if (dst != NULL) { + idn2_free(dst); + } + + return (result); +} + +/*% + * Convert 'src', which is a string using the current locale's character + * encoding, into an ACE string suitable for use in the DNS, storing the + * conversion result in 'dst', which is 'dstlen' bytes large. + * + * 'dst' MUST be large enough to hold any valid domain name. + */ +static void +idn_locale_to_ace(const char *src, char *dst, size_t dstlen) { + const char *final_src; + char *ascii_src; + int res; + + systemlocale(LC_ALL); + + /* + * We trust libidn2 to return an error if 'src' is too large to be a + * valid domain name. + */ + res = idn2_to_ascii_lz(src, &ascii_src, IDN2_NONTRANSITIONAL); + if (res == IDN2_DISALLOWED) { + res = idn2_to_ascii_lz(src, &ascii_src, IDN2_TRANSITIONAL); + } + if (res != IDN2_OK) { + fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnin", + src, idn2_strerror(res)); + } + + /* + * idn2_to_ascii_lz() normalizes all strings to lower case, but we + * generally don't want to lowercase all input strings; make sure to + * return the original case if the two strings differ only in case. + */ + final_src = (strcasecmp(src, ascii_src) == 0 ? src : ascii_src); + + (void)strlcpy(dst, final_src, dstlen); + + idn2_free(ascii_src); + + resetlocale(LC_ALL); +} + +/*% + * Convert 'src', which is an ACE string suitable for use in the DNS, into a + * string using the current locale's character encoding, storing the conversion + * result in 'dst'. + * + * The caller MUST subsequently release 'dst' using idn2_free(). + */ +static void +idn_ace_to_locale(const char *src, char **dst) { + char *local_src, *utf8_src; + int res; + + systemlocale(LC_ALL); + + /* + * We need to: + * + * 1) check whether 'src' is a valid IDNA2008 name, + * 2) if it is, output it in the current locale's character encoding. + * + * Unlike idn2_to_ascii_*(), idn2_to_unicode_*() functions are unable + * to perform IDNA2008 validity checks. Thus, we need to decode any + * Punycode in 'src', check if the resulting name is a valid IDNA2008 + * name, and only once we ensure it is, output that name in the current + * locale's character encoding. + * + * We could just use idn2_to_unicode_8zlz() + idn2_to_ascii_lz(), but + * then we would not be able to universally tell invalid names and + * character encoding errors apart (if the current locale uses ASCII + * for character encoding, the former function would fail even for a + * valid IDNA2008 name, as long as it contained any non-ASCII + * character). Thus, we need to take a longer route. + * + * First, convert 'src' to UTF-8, ignoring the current locale. + */ + res = idn2_to_unicode_8z8z(src, &utf8_src, 0); + if (res != IDN2_OK) { + fatal("Bad ACE string '%s' (%s), use +noidnout", src, + idn2_strerror(res)); + } + + /* + * Then, check whether decoded 'src' is a valid IDNA2008 name + * and if disallowed character is found, fallback to IDNA2003. + */ + res = idn2_to_ascii_8z(utf8_src, NULL, IDN2_NONTRANSITIONAL); + if (res == IDN2_DISALLOWED) { + res = idn2_to_ascii_8z(utf8_src, NULL, IDN2_TRANSITIONAL); + } + if (res != IDN2_OK) { + fatal("'%s' is not a legal IDNA2008 name (%s), use +noidnout", + src, idn2_strerror(res)); + } + + /* + * Finally, try converting the decoded 'src' into the current locale's + * character encoding. + */ + res = idn2_to_unicode_8zlz(utf8_src, &local_src, 0); + if (res != IDN2_OK) { + static bool warned = false; + + res = idn2_to_ascii_8z(utf8_src, &local_src, 0); + if (res != IDN2_OK) { + fatal("Cannot represent '%s' " + "in the current locale nor ascii (%s), " + "use +noidnout or a different locale", + src, idn2_strerror(res)); + } else if (!warned) { + fprintf(stderr, + ";; Warning: cannot represent '%s' " + "in the current locale", + local_src); + warned = true; + } + } + + /* + * Free the interim conversion result. + */ + idn2_free(utf8_src); + + *dst = local_src; + + resetlocale(LC_ALL); +} +#endif /* HAVE_LIBIDN2 */ diff --git a/bin/dig/host.c b/bin/dig/host.c new file mode 100644 index 0000000..db2e8d4 --- /dev/null +++ b/bin/dig/host.c @@ -0,0 +1,927 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <inttypes.h> +#include <limits.h> +#include <locale.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <isc/app.h> +#include <isc/commandline.h> +#include <isc/netaddr.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/util.h> + +#include <dns/byaddr.h> +#include <dns/fixedname.h> +#include <dns/message.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdataset.h> +#include <dns/rdatastruct.h> +#include <dns/rdatatype.h> + +#include <dig/dig.h> + +static bool short_form = true, listed_server = false; +static bool default_lookups = true; +static int seen_error = -1; +static bool list_addresses = true; +static bool list_almost_all = false; +static dns_rdatatype_t list_type = dns_rdatatype_a; +static bool printed_server = false; +static bool ipv4only = false, ipv6only = false; + +static const char *opcodetext[] = { "QUERY", "IQUERY", "STATUS", + "RESERVED3", "NOTIFY", "UPDATE", + "RESERVED6", "RESERVED7", "RESERVED8", + "RESERVED9", "RESERVED10", "RESERVED11", + "RESERVED12", "RESERVED13", "RESERVED14", + "RESERVED15" }; + +static const char *rcodetext[] = { "NOERROR", "FORMERR", "SERVFAIL", + "NXDOMAIN", "NOTIMP", "REFUSED", + "YXDOMAIN", "YXRRSET", "NXRRSET", + "NOTAUTH", "NOTZONE", "RESERVED11", + "RESERVED12", "RESERVED13", "RESERVED14", + "RESERVED15", "BADVERS" }; + +struct rtype { + unsigned int type; + const char *text; +}; + +struct rtype rtypes[] = { { 1, "has address" }, + { 2, "name server" }, + { 5, "is an alias for" }, + { 11, "has well known services" }, + { 12, "domain name pointer" }, + { 13, "host information" }, + { 15, "mail is handled by" }, + { 16, "descriptive text" }, + { 19, "x25 address" }, + { 20, "ISDN address" }, + { 24, "has signature" }, + { 25, "has key" }, + { 28, "has IPv6 address" }, + { 29, "location" }, + { 0, NULL } }; + +static char * +rcode_totext(dns_rcode_t rcode) { + static char buf[sizeof("?65535")]; + union { + const char *consttext; + char *deconsttext; + } totext; + + if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) { + snprintf(buf, sizeof(buf), "?%u", rcode); + totext.deconsttext = buf; + } else { + totext.consttext = rcodetext[rcode]; + } + return (totext.deconsttext); +} + +ISC_PLATFORM_NORETURN_PRE static void +show_usage(void) ISC_PLATFORM_NORETURN_POST; + +static void +show_usage(void) { + fputs("Usage: host [-aCdilrTvVw] [-c class] [-N ndots] [-t type] [-W " + "time]\n" + " [-R number] [-m flag] [-p port] hostname [server]\n" + " -a is equivalent to -v -t ANY\n" + " -A is like -a but omits RRSIG, NSEC, NSEC3\n" + " -c specifies query class for non-IN data\n" + " -C compares SOA records on authoritative nameservers\n" + " -d is equivalent to -v\n" + " -l lists all hosts in a domain, using AXFR\n" + " -m set memory debugging flag (trace|record|usage)\n" + " -N changes the number of dots allowed before root lookup " + "is done\n" + " -p specifies the port on the server to query\n" + " -r disables recursive processing\n" + " -R specifies number of retries for UDP packets\n" + " -s a SERVFAIL response should stop query\n" + " -t specifies the query type\n" + " -T enables TCP/IP mode\n" + " -U enables UDP mode\n" + " -v enables verbose output\n" + " -V print version number and exit\n" + " -w specifies to wait forever for a reply\n" + " -W specifies how long to wait for a reply\n" + " -4 use IPv4 query transport only\n" + " -6 use IPv6 query transport only\n", + stderr); + exit(1); +} + +static void +host_shutdown(void) { + (void)isc_app_shutdown(); +} + +static void +received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { + isc_time_t now; + int diff; + + if (!short_form) { + char fromtext[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(from, fromtext, sizeof(fromtext)); + if (query->lookup->use_usec) { + TIME_NOW_HIRES(&now); + } else { + TIME_NOW(&now); + } + diff = (int)isc_time_microdiff(&now, &query->time_sent); + printf("Received %u bytes from %s in %d ms\n", bytes, fromtext, + diff / 1000); + } +} + +static void +trying(char *frm, dig_lookup_t *lookup) { + UNUSED(lookup); + + if (!short_form) { + printf("Trying \"%s\"\n", frm); + } +} + +static void +say_message(dns_name_t *name, const char *msg, dns_rdata_t *rdata, + dig_query_t *query) { + isc_buffer_t *b = NULL; + char namestr[DNS_NAME_FORMATSIZE]; + isc_region_t r; + isc_result_t result; + unsigned int bufsize = BUFSIZ; + + dns_name_format(name, namestr, sizeof(namestr)); +retry: + isc_buffer_allocate(mctx, &b, bufsize); + result = dns_rdata_totext(rdata, NULL, b); + if (result == ISC_R_NOSPACE) { + isc_buffer_free(&b); + bufsize *= 2; + goto retry; + } + check_result(result, "dns_rdata_totext"); + isc_buffer_usedregion(b, &r); + if (query->lookup->identify_previous_line) { + printf("Nameserver %s:\n\t", query->servname); + } + printf("%s %s %.*s", namestr, msg, (int)r.length, (char *)r.base); + if (query->lookup->identify) { + printf(" on server %s", query->servname); + } + printf("\n"); + isc_buffer_free(&b); +} + +static isc_result_t +printsection(dns_message_t *msg, dns_section_t sectionid, + const char *section_name, bool headers, dig_query_t *query) { + dns_name_t *name, *print_name; + dns_rdataset_t *rdataset; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_buffer_t target; + isc_result_t result, loopresult; + isc_region_t r; + dns_name_t empty_name; + char tbuf[4096] = { 0 }; + bool first; + bool no_rdata = (sectionid == DNS_SECTION_QUESTION); + + if (headers) { + printf(";; %s SECTION:\n", section_name); + } + + dns_name_init(&empty_name, NULL); + + result = dns_message_firstname(msg, sectionid); + if (result == ISC_R_NOMORE) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + return (result); + } + + for (;;) { + name = NULL; + dns_message_currentname(msg, sectionid, &name); + + isc_buffer_init(&target, tbuf, sizeof(tbuf)); + first = true; + print_name = name; + + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (query->lookup->rdtype == dns_rdatatype_axfr && + !((!list_addresses && + (list_type == dns_rdatatype_any || + rdataset->type == list_type)) || + (list_addresses && + (rdataset->type == dns_rdatatype_a || + rdataset->type == dns_rdatatype_aaaa || + rdataset->type == dns_rdatatype_ns || + rdataset->type == dns_rdatatype_ptr)))) + { + continue; + } + if (list_almost_all && + (rdataset->type == dns_rdatatype_rrsig || + rdataset->type == dns_rdatatype_nsec || + rdataset->type == dns_rdatatype_nsec3)) + { + continue; + } + if (!short_form) { + result = dns_rdataset_totext(rdataset, + print_name, false, + no_rdata, &target); + if (result != ISC_R_SUCCESS) { + return (result); + } +#ifdef USEINITALWS + if (first) { + print_name = &empty_name; + first = false; + } +#else /* ifdef USEINITALWS */ + UNUSED(first); /* Shut up compiler. */ +#endif /* ifdef USEINITALWS */ + } else { + loopresult = dns_rdataset_first(rdataset); + while (loopresult == ISC_R_SUCCESS) { + struct rtype *t; + const char *rtt; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + char typebuf2[DNS_RDATATYPE_FORMATSIZE + + 20]; + dns_rdataset_current(rdataset, &rdata); + + for (t = rtypes; t->text != NULL; t++) { + if (t->type == rdata.type) { + rtt = t->text; + goto found; + } + } + + dns_rdatatype_format(rdata.type, + typebuf, + sizeof(typebuf)); + snprintf(typebuf2, sizeof(typebuf2), + "has %s record", typebuf); + rtt = typebuf2; + found: + say_message(print_name, rtt, &rdata, + query); + dns_rdata_reset(&rdata); + loopresult = + dns_rdataset_next(rdataset); + } + } + } + if (!short_form) { + isc_buffer_usedregion(&target, &r); + if (no_rdata) { + printf(";%.*s", (int)r.length, (char *)r.base); + } else { + printf("%.*s", (int)r.length, (char *)r.base); + } + } + + result = dns_message_nextname(msg, sectionid); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + return (result); + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +printrdata(dns_message_t *msg, dns_rdataset_t *rdataset, + const dns_name_t *owner, const char *set_name, bool headers) { + isc_buffer_t target; + isc_result_t result; + isc_region_t r; + char tbuf[4096]; + + UNUSED(msg); + if (headers) { + printf(";; %s SECTION:\n", set_name); + } + + isc_buffer_init(&target, tbuf, sizeof(tbuf)); + + result = dns_rdataset_totext(rdataset, owner, false, false, &target); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_usedregion(&target, &r); + printf("%.*s", (int)r.length, (char *)r.base); + + return (ISC_R_SUCCESS); +} + +static void +chase_cnamechain(dns_message_t *msg, dns_name_t *qname) { + isc_result_t result; + dns_rdataset_t *rdataset; + dns_rdata_cname_t cname; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int i = msg->counts[DNS_SECTION_ANSWER]; + + while (i-- > 0) { + rdataset = NULL; + result = dns_message_findname(msg, DNS_SECTION_ANSWER, qname, + dns_rdatatype_cname, 0, NULL, + &rdataset); + if (result != ISC_R_SUCCESS) { + return; + } + result = dns_rdataset_first(rdataset); + check_result(result, "dns_rdataset_first"); + dns_rdata_reset(&rdata); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &cname, NULL); + check_result(result, "dns_rdata_tostruct"); + dns_name_copynf(&cname.cname, qname); + dns_rdata_freestruct(&cname); + } +} + +static isc_result_t +printmessage(dig_query_t *query, const isc_buffer_t *msgbuf, dns_message_t *msg, + bool headers) { + bool did_flag = false; + dns_rdataset_t *opt, *tsig = NULL; + const dns_name_t *tsigname; + isc_result_t result = ISC_R_SUCCESS; + int force_error; + + UNUSED(msgbuf); + UNUSED(headers); + + /* + * We get called multiple times. + * Preserve any existing error status. + */ + force_error = (seen_error == 1) ? 1 : 0; + seen_error = 1; + if (listed_server && !printed_server) { + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + + printf("Using domain server:\n"); + printf("Name: %s\n", query->userarg); + isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr)); + printf("Address: %s\n", sockstr); + printf("Aliases: \n\n"); + printed_server = true; + } + + if (msg->rcode != 0) { + char namestr[DNS_NAME_FORMATSIZE]; + dns_name_format(query->lookup->name, namestr, sizeof(namestr)); + + if (query->lookup->identify_previous_line) { + printf("Nameserver %s:\n\t%s not found: %d(%s)\n", + query->servname, + (msg->rcode != dns_rcode_nxdomain) + ? namestr + : query->lookup->textname, + msg->rcode, rcode_totext(msg->rcode)); + } else { + printf("Host %s not found: %d(%s)\n", + (msg->rcode != dns_rcode_nxdomain) + ? namestr + : query->lookup->textname, + msg->rcode, rcode_totext(msg->rcode)); + } + return (ISC_R_SUCCESS); + } + + if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) { + char namestr[DNS_NAME_FORMATSIZE]; + dig_lookup_t *lookup; + dns_fixedname_t fixed; + dns_name_t *name; + + /* Add AAAA and MX lookups. */ + name = dns_fixedname_initname(&fixed); + dns_name_copynf(query->lookup->name, name); + chase_cnamechain(msg, name); + dns_name_format(name, namestr, sizeof(namestr)); + lookup = clone_lookup(query->lookup, false); + if (lookup != NULL) { + strlcpy(lookup->textname, namestr, + sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_aaaa; + lookup->rdtypeset = true; + lookup->origin = NULL; + lookup->retries = tries; + ISC_LIST_APPEND(lookup_list, lookup, link); + } + lookup = clone_lookup(query->lookup, false); + if (lookup != NULL) { + strlcpy(lookup->textname, namestr, + sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_mx; + lookup->rdtypeset = true; + lookup->origin = NULL; + lookup->retries = tries; + ISC_LIST_APPEND(lookup_list, lookup, link); + } + } + + if (!short_form) { + printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n", + opcodetext[msg->opcode], rcode_totext(msg->rcode), + msg->id); + printf(";; flags: "); + if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) { + printf("qr"); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) { + printf("%saa", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) { + printf("%stc", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) { + printf("%srd", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) { + printf("%sra", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) { + printf("%sad", did_flag ? " " : ""); + did_flag = true; + } + if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) { + printf("%scd", did_flag ? " " : ""); + did_flag = true; + POST(did_flag); + } + printf("; QUERY: %u, ANSWER: %u, " + "AUTHORITY: %u, ADDITIONAL: %u\n", + msg->counts[DNS_SECTION_QUESTION], + msg->counts[DNS_SECTION_ANSWER], + msg->counts[DNS_SECTION_AUTHORITY], + msg->counts[DNS_SECTION_ADDITIONAL]); + opt = dns_message_getopt(msg); + if (opt != NULL) { + printf(";; EDNS: version: %u, udp=%u\n", + (unsigned int)((opt->ttl & 0x00ff0000) >> 16), + (unsigned int)opt->rdclass); + } + tsigname = NULL; + tsig = dns_message_gettsig(msg, &tsigname); + if (tsig != NULL) { + printf(";; PSEUDOSECTIONS: TSIG\n"); + } + } + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_QUESTION]) && !short_form) + { + printf("\n"); + result = printsection(msg, DNS_SECTION_QUESTION, "QUESTION", + true, query); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) { + if (!short_form) { + printf("\n"); + } + result = printsection(msg, DNS_SECTION_ANSWER, "ANSWER", + !short_form, query); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_AUTHORITY]) && + !short_form) + { + printf("\n"); + result = printsection(msg, DNS_SECTION_AUTHORITY, "AUTHORITY", + true, query); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ADDITIONAL]) && + !short_form) + { + printf("\n"); + result = printsection(msg, DNS_SECTION_ADDITIONAL, "ADDITIONAL", + true, query); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + if ((tsig != NULL) && !short_form) { + printf("\n"); + result = printrdata(msg, tsig, tsigname, "PSEUDOSECTION TSIG", + true); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + if (!short_form) { + printf("\n"); + } + + if (short_form && !default_lookups && + ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) + { + char namestr[DNS_NAME_FORMATSIZE]; + char typestr[DNS_RDATATYPE_FORMATSIZE]; + dns_name_format(query->lookup->name, namestr, sizeof(namestr)); + dns_rdatatype_format(query->lookup->rdtype, typestr, + sizeof(typestr)); + printf("%s has no %s record\n", namestr, typestr); + } + seen_error = force_error; + return (result); +} + +static const char *optstring = "46aAc:dilnm:p:rst:vVwCDN:R:TUW:"; + +/*% version */ +static void +version(void) { + fputs("host " VERSION "\n", stderr); +} + +static void +pre_parse_args(int argc, char **argv) { + int c; + + while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) { + switch (c) { + case 'm': + memdebugging = true; + if (strcasecmp("trace", isc_commandline_argument) == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGTRACE; + } else if (strcasecmp("record", + isc_commandline_argument) == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGRECORD; + } else if (strcasecmp("usage", + isc_commandline_argument) == 0) + { + isc_mem_debugging |= ISC_MEM_DEBUGUSAGE; + } + break; + + case '4': + if (ipv6only) { + fatal("only one of -4 and -6 allowed"); + } + ipv4only = true; + break; + case '6': + if (ipv4only) { + fatal("only one of -4 and -6 allowed"); + } + ipv6only = true; + break; + case 'a': + break; + case 'A': + break; + case 'c': + break; + case 'C': + break; + case 'd': + break; + case 'D': + if (debugging) { + debugtiming = true; + } + debugging = true; + break; + case 'i': + break; + case 'l': + break; + case 'n': + break; + case 'N': + break; + case 'p': + break; + case 'r': + break; + case 'R': + break; + case 's': + break; + case 't': + break; + case 'T': + break; + case 'U': + break; + case 'v': + break; + case 'V': + version(); + exit(0); + break; + case 'w': + break; + case 'W': + break; + default: + show_usage(); + } + } + isc_commandline_reset = true; + isc_commandline_index = 1; +} + +static void +parse_args(bool is_batchfile, int argc, char **argv) { + char hostname[MXNAME]; + dig_lookup_t *lookup; + int c; + char store[MXNAME]; + isc_textregion_t tr; + isc_result_t result = ISC_R_SUCCESS; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + uint32_t serial = 0; + + UNUSED(is_batchfile); + + lookup = make_empty_lookup(); + + lookup->servfail_stops = false; + lookup->besteffort = false; + lookup->comments = false; + short_form = !verbose; + + while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) { + switch (c) { + case 'l': + lookup->tcp_mode = true; + lookup->rdtype = dns_rdatatype_axfr; + lookup->rdtypeset = true; + fatalexit = 3; + break; + case 'v': + case 'd': + short_form = false; + break; + case 'r': + lookup->recurse = false; + break; + case 't': + if (strncasecmp(isc_commandline_argument, "ixfr=", 5) == + 0) + { + rdtype = dns_rdatatype_ixfr; + /* XXXMPA add error checking */ + serial = strtoul(isc_commandline_argument + 5, + NULL, 10); + result = ISC_R_SUCCESS; + } else { + tr.base = isc_commandline_argument; + tr.length = strlen(isc_commandline_argument); + result = dns_rdatatype_fromtext( + &rdtype, (isc_textregion_t *)&tr); + } + + if (result != ISC_R_SUCCESS) { + fatalexit = 2; + fatal("invalid type: %s\n", + isc_commandline_argument); + } + if (!lookup->rdtypeset || + lookup->rdtype != dns_rdatatype_axfr) + { + lookup->rdtype = rdtype; + } + lookup->rdtypeset = true; + if (rdtype == dns_rdatatype_axfr) { + /* -l -t any -v */ + list_type = dns_rdatatype_any; + short_form = false; + lookup->tcp_mode = true; + } else if (rdtype == dns_rdatatype_ixfr) { + lookup->ixfr_serial = serial; + lookup->tcp_mode = true; + list_type = rdtype; + } else if (rdtype == dns_rdatatype_any) { + if (!lookup->tcp_mode_set) { + lookup->tcp_mode = true; + } + } else { + list_type = rdtype; + } + list_addresses = false; + default_lookups = false; + break; + case 'c': + tr.base = isc_commandline_argument; + tr.length = strlen(isc_commandline_argument); + result = dns_rdataclass_fromtext( + &rdclass, (isc_textregion_t *)&tr); + + if (result != ISC_R_SUCCESS) { + fatalexit = 2; + fatal("invalid class: %s\n", + isc_commandline_argument); + } else { + lookup->rdclass = rdclass; + lookup->rdclassset = true; + } + default_lookups = false; + break; + case 'A': + list_almost_all = true; + FALLTHROUGH; + case 'a': + if (!lookup->rdtypeset || + lookup->rdtype != dns_rdatatype_axfr) + { + lookup->rdtype = dns_rdatatype_any; + } + list_type = dns_rdatatype_any; + list_addresses = false; + lookup->rdtypeset = true; + short_form = false; + default_lookups = false; + break; + case 'i': + /* deprecated */ + break; + case 'n': + /* deprecated */ + break; + case 'm': + /* Handled by pre_parse_args(). */ + break; + case 'w': + /* + * The timer routines are coded such that + * timeout==MAXINT doesn't enable the timer + */ + timeout = INT_MAX; + break; + case 'W': + timeout = atoi(isc_commandline_argument); + if (timeout < 1) { + timeout = 1; + } + break; + case 'R': + tries = atoi(isc_commandline_argument) + 1; + if (tries < 2) { + tries = 2; + } + break; + case 'T': + lookup->tcp_mode = true; + lookup->tcp_mode_set = true; + break; + case 'U': + lookup->tcp_mode = false; + lookup->tcp_mode_set = true; + break; + case 'C': + debug("showing all SOAs"); + lookup->rdtype = dns_rdatatype_ns; + lookup->rdtypeset = true; + lookup->rdclass = dns_rdataclass_in; + lookup->rdclassset = true; + lookup->ns_search_only = true; + lookup->trace_root = true; + lookup->identify_previous_line = true; + default_lookups = false; + break; + case 'N': + debug("setting NDOTS to %s", isc_commandline_argument); + ndots = atoi(isc_commandline_argument); + break; + case 'D': + /* Handled by pre_parse_args(). */ + break; + case '4': + /* Handled by pre_parse_args(). */ + break; + case '6': + /* Handled by pre_parse_args(). */ + break; + case 's': + lookup->servfail_stops = true; + break; + case 'p': + port = atoi(isc_commandline_argument); + break; + } + } + + lookup->retries = tries; + + if (isc_commandline_index >= argc) { + show_usage(); + } + + strlcpy(hostname, argv[isc_commandline_index], sizeof(hostname)); + + if (argc > isc_commandline_index + 1) { + set_nameserver(argv[isc_commandline_index + 1]); + debug("server is %s", argv[isc_commandline_index + 1]); + listed_server = true; + } else { + check_ra = true; + } + + lookup->pending = false; + if (get_reverse(store, sizeof(store), hostname, true) == ISC_R_SUCCESS) + { + strlcpy(lookup->textname, store, sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_ptr; + lookup->rdtypeset = true; + default_lookups = false; + } else { + strlcpy(lookup->textname, hostname, sizeof(lookup->textname)); + usesearch = true; + } + lookup->new_search = true; + ISC_LIST_APPEND(lookup_list, lookup, link); +} + +int +main(int argc, char **argv) { + isc_result_t result; + + tries = 2; + + ISC_LIST_INIT(lookup_list); + ISC_LIST_INIT(server_list); + ISC_LIST_INIT(search_list); + + fatalexit = 1; + + /* setup dighost callbacks */ + dighost_printmessage = printmessage; + dighost_received = received; + dighost_trying = trying; + dighost_shutdown = host_shutdown; + + debug("main()"); + progname = argv[0]; + pre_parse_args(argc, argv); + result = isc_app_start(); + check_result(result, "isc_app_start"); + setup_libs(); + setup_system(ipv4only, ipv6only); + parse_args(false, argc, argv); + if (keyfile[0] != 0) { + setup_file_key(); + } else if (keysecret[0] != 0) { + setup_text_key(); + } + result = isc_app_onrun(mctx, global_task, onrun_callback, NULL); + check_result(result, "isc_app_onrun"); + isc_app_run(); + cancel_all(); + destroy_libs(); + isc_app_finish(); + return ((seen_error == 0) ? 0 : 1); +} diff --git a/bin/dig/host.rst b/bin/dig/host.rst new file mode 100644 index 0000000..8e855c3 --- /dev/null +++ b/bin/dig/host.rst @@ -0,0 +1,171 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. _man_host: + +host - DNS lookup utility +------------------------- + +Synopsis +~~~~~~~~ + +:program:`host` [**-aACdlnrsTUwv**] [**-c** class] [**-N** ndots] [**-p** port] [**-R** number] [**-t** type] [**-W** wait] [**-m** flag] [ [**-4**] | [**-6**] ] [**-v**] [**-V**] {name} [server] + +Description +~~~~~~~~~~~ + +``host`` is a simple utility for performing DNS lookups. It is normally +used to convert names to IP addresses and vice versa. When no arguments +or options are given, ``host`` prints a short summary of its +command-line arguments and options. + +``name`` is the domain name that is to be looked up. It can also be a +dotted-decimal IPv4 address or a colon-delimited IPv6 address, in which +case ``host`` by default performs a reverse lookup for that address. +``server`` is an optional argument which is either the name or IP +address of the name server that ``host`` should query instead of the +server or servers listed in ``/etc/resolv.conf``. + +Options +~~~~~~~ + +``-4`` + This option specifies that only IPv4 should be used for query transport. See also the ``-6`` option. + +``-6`` + This option specifies that only IPv6 should be used for query transport. See also the ``-4`` option. + +``-a`` + The ``-a`` ("all") option is normally equivalent to ``-v -t ANY``. It + also affects the behavior of the ``-l`` list zone option. + +``-A`` + The ``-A`` ("almost all") option is equivalent to ``-a``, except that RRSIG, + NSEC, and NSEC3 records are omitted from the output. + +``-c class`` + This option specifies the query class, which can be used to lookup HS (Hesiod) or CH (Chaosnet) + class resource records. The default class is IN (Internet). + +``-C`` + This option indicates that ``named`` should check consistency, meaning that ``host`` queries the SOA records for zone + ``name`` from all the listed authoritative name servers for that + zone. The list of name servers is defined by the NS records that are + found for the zone. + +``-d`` + This option prints debugging traces, and is equivalent to the ``-v`` verbose option. + +``-l`` + This option tells ``named`` to list the zone, meaning the ``host`` command performs a zone transfer of zone + ``name`` and prints out the NS, PTR, and address records (A/AAAA). + + Together, the ``-l -a`` options print all records in the zone. + +``-N ndots`` + This option specifies the number of dots (``ndots``) that have to be in ``name`` for it to be + considered absolute. The default value is that defined using the + ``ndots`` statement in ``/etc/resolv.conf``, or 1 if no ``ndots`` statement + is present. Names with fewer dots are interpreted as relative names, + and are searched for in the domains listed in the ``search`` or + ``domain`` directive in ``/etc/resolv.conf``. + +``-p port`` + This option specifies the port to query on the server. The default is 53. + +``-r`` + This option specifies a non-recursive query; setting this option clears the RD (recursion + desired) bit in the query. This means that the name server + receiving the query does not attempt to resolve ``name``. The ``-r`` + option enables ``host`` to mimic the behavior of a name server by + making non-recursive queries, and expecting to receive answers to + those queries that can be referrals to other name servers. + +``-R number`` + This option specifies the number of retries for UDP queries. If ``number`` is negative or zero, + the number of retries is silently set to 1. The default value is 1, or + the value of the ``attempts`` option in ``/etc/resolv.conf``, if set. + +``-s`` + This option tells ``named`` *not* to send the query to the next nameserver if any server responds + with a SERVFAIL response, which is the reverse of normal stub + resolver behavior. + +``-t type`` + This option specifies the query type. The ``type`` argument can be any recognized query type: + CNAME, NS, SOA, TXT, DNSKEY, AXFR, etc. + + When no query type is specified, ``host`` automatically selects an + appropriate query type. By default, it looks for A, AAAA, and MX + records. If the ``-C`` option is given, queries are made for SOA + records. If ``name`` is a dotted-decimal IPv4 address or + colon-delimited IPv6 address, ``host`` queries for PTR records. + + If a query type of IXFR is chosen, the starting serial number can be + specified by appending an equals sign (=), followed by the starting serial + number, e.g., ``-t IXFR=12345678``. + +``-T``; ``-U`` + This option specifies TCP or UDP. By default, ``host`` uses UDP when making queries; the + ``-T`` option makes it use a TCP connection when querying the name + server. TCP is automatically selected for queries that require + it, such as zone transfer (AXFR) requests. Type ``ANY`` queries default + to TCP, but can be forced to use UDP initially via ``-U``. + +``-m flag`` + This option sets memory usage debugging: the flag can be ``record``, ``usage``, or + ``trace``. The ``-m`` option can be specified more than once to set + multiple flags. + +``-v`` + This option sets verbose output, and is equivalent to the ``-d`` debug option. Verbose output + can also be enabled by setting the ``debug`` option in + ``/etc/resolv.conf``. + +``-V`` + This option prints the version number and exits. + +``-w`` + This option sets "wait forever": the query timeout is set to the maximum possible. See + also the ``-W`` option. + +``-W wait`` + This options sets the length of the wait timeout, indicating that ``named`` should wait for up to ``wait`` seconds for a reply. If ``wait`` is + less than 1, the wait interval is set to 1 second. + + By default, ``host`` waits for 5 seconds for UDP responses and 10 + seconds for TCP connections. These defaults can be overridden by the + ``timeout`` option in ``/etc/resolv.conf``. + + See also the ``-w`` option. + +IDN Support +~~~~~~~~~~~ + +If ``host`` has been built with IDN (internationalized domain name) +support, it can accept and display non-ASCII domain names. ``host`` +appropriately converts character encoding of a domain name before sending +a request to a DNS server or displaying a reply from the server. +To turn off IDN support, define the ``IDN_DISABLE`` +environment variable. IDN support is disabled if the variable is set +when ``host`` runs. + +Files +~~~~~ + +``/etc/resolv.conf`` + +See Also +~~~~~~~~ + +:manpage:`dig(1)`, :manpage:`named(8)`. diff --git a/bin/dig/include/.clang-format b/bin/dig/include/.clang-format new file mode 120000 index 0000000..0e62f72 --- /dev/null +++ b/bin/dig/include/.clang-format @@ -0,0 +1 @@ +../../../.clang-format.headers
\ No newline at end of file diff --git a/bin/dig/include/dig/dig.h b/bin/dig/include/dig/dig.h new file mode 100644 index 0000000..8bf9613 --- /dev/null +++ b/bin/dig/include/dig/dig.h @@ -0,0 +1,425 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef DIG_H +#define DIG_H + +/*! \file */ + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/buffer.h> +#include <isc/bufferlist.h> +#include <isc/formatcheck.h> +#include <isc/lang.h> +#include <isc/list.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/sockaddr.h> +#include <isc/socket.h> + +#include <dns/rdatalist.h> + +#include <dst/dst.h> + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif /* ifdef __APPLE__ */ + +#define MXSERV 20 +#define MXNAME (DNS_NAME_MAXTEXT + 1) +#define MXRD 32 +/*% Buffer Size */ +#define BUFSIZE 512 +#define COMMSIZE 0xffff +#ifndef RESOLV_CONF +/*% location of resolve.conf */ +#define RESOLV_CONF "/etc/resolv.conf" +#endif /* ifndef RESOLV_CONF */ +/*% output buffer */ +#define OUTPUTBUF 32767 +/*% Max RR Limit */ +#define MAXRRLIMIT 0xffffffff +#define MAXTIMEOUT 0xffff +/*% Max number of tries */ +#define MAXTRIES 0xffffffff +/*% Max number of dots */ +#define MAXNDOTS 0xffff +/*% Max number of ports */ +#define MAXPORT 0xffff +/*% Max serial number */ +#define MAXSERIAL 0xffffffff + +/*% Default TCP Timeout */ +#define TCP_TIMEOUT 10 +/*% Default UDP Timeout */ +#define UDP_TIMEOUT 5 + +#define SERVER_TIMEOUT 1 + +#define LOOKUP_LIMIT 64 + +#define DEFAULT_EDNS_VERSION 0 +#define DEFAULT_EDNS_BUFSIZE 4096 + +/*% + * Lookup_limit is just a limiter, keeping too many lookups from being + * created. It's job is mainly to prevent the program from running away + * in a tight loop of constant lookups. It's value is arbitrary. + */ + +ISC_LANG_BEGINDECLS + +typedef struct dig_lookup dig_lookup_t; +typedef struct dig_query dig_query_t; +typedef struct dig_server dig_server_t; +typedef ISC_LIST(dig_server_t) dig_serverlist_t; +typedef struct dig_searchlist dig_searchlist_t; + +#define DIG_QUERY_MAGIC ISC_MAGIC('D', 'i', 'g', 'q') + +#define DIG_VALID_QUERY(x) ISC_MAGIC_VALID((x), DIG_QUERY_MAGIC) + +/*% The dig_lookup structure */ +struct dig_lookup { + bool pending, /*%< Pending a successful answer */ + waiting_connect, doing_xfr, ns_search_only, /*%< dig + * +nssearch, + * host -C */ + identify, /*%< Append an "on server <foo>" message */ + identify_previous_line, /*% Prepend a "Nameserver <foo>:" + * message, with newline and tab */ + ignore, recurse, aaonly, adflag, cdflag, raflag, tcflag, zflag, + trace, /*% dig +trace */ + trace_root, /*% initial query for either +trace or +nssearch + * */ + tcp_mode, tcp_mode_set, comments, stats, section_question, + section_answer, section_authority, section_additional, + servfail_stops, new_search, need_search, done_as_is, besteffort, + dnssec, expire, sendcookie, seenbadcookie, badcookie, + nsid, /*% Name Server ID (RFC 5001) */ + tcp_keepalive, header_only, ednsneg, mapped, + print_unknown_format, multiline, nottl, noclass, onesoa, + use_usec, nocrypto, ttlunits, idnin, idnout, expandaaaa, qr, + accept_reply_unexpected_src; /*% print replies from + * unexpected + * sources. */ + char textname[MXNAME]; /*% Name we're going to be + * looking up */ + char cmdline[MXNAME]; + dns_rdatatype_t rdtype; + dns_rdatatype_t qrdtype; + dns_rdataclass_t rdclass; + bool rdtypeset; + bool rdclassset; + char name_space[BUFSIZE]; + char oname_space[BUFSIZE]; + isc_buffer_t namebuf; + isc_buffer_t onamebuf; + isc_buffer_t renderbuf; + char *sendspace; + dns_name_t *name; + isc_interval_t interval; + dns_message_t *sendmsg; + dns_name_t *oname; + ISC_LINK(dig_lookup_t) link; + ISC_LIST(dig_query_t) q; + ISC_LIST(dig_query_t) connecting; + dig_query_t *current_query; + dig_serverlist_t my_server_list; + dig_searchlist_t *origin; + dig_query_t *xfr_q; + uint32_t retries; + int nsfound; + int16_t udpsize; + int16_t edns; + int16_t padding; + uint32_t ixfr_serial; + isc_buffer_t rdatabuf; + char rdatastore[MXNAME]; + dst_context_t *tsigctx; + isc_buffer_t *querysig; + uint32_t msgcounter; + dns_fixedname_t fdomain; + isc_sockaddr_t *ecs_addr; + char *cookie; + dns_ednsopt_t *ednsopts; + unsigned int ednsoptscnt; + isc_dscp_t dscp; + unsigned int ednsflags; + dns_opcode_t opcode; + int rrcomments; + unsigned int eoferr; +}; + +/*% The dig_query structure */ +struct dig_query { + unsigned int magic; + dig_lookup_t *lookup; + bool waiting_connect, pending_free, waiting_senddone, first_pass, + first_soa_rcvd, second_rr_rcvd, first_repeat_rcvd, recv_made, + warn_id, timedout; + uint32_t first_rr_serial; + uint32_t second_rr_serial; + uint32_t msg_count; + uint32_t rr_count; + bool ixfr_axfr; + char *servname; + char *userarg; + isc_buffer_t recvbuf, lengthbuf, tmpsendbuf, sendbuf; + char *recvspace, *tmpsendspace, lengthspace[4]; + isc_socket_t *sock; + ISC_LINK(dig_query_t) link; + ISC_LINK(dig_query_t) clink; + dig_query_t *saved_next; + isc_sockaddr_t sockaddr; + isc_time_t time_sent; + isc_time_t time_recv; + uint64_t byte_count; + isc_timer_t *timer; +}; + +struct dig_server { + char servername[MXNAME]; + char userarg[MXNAME]; + ISC_LINK(dig_server_t) link; +}; + +struct dig_searchlist { + char origin[MXNAME]; + ISC_LINK(dig_searchlist_t) link; +}; + +typedef ISC_LIST(dig_searchlist_t) dig_searchlistlist_t; +typedef ISC_LIST(dig_lookup_t) dig_lookuplist_t; + +/* + * Externals from dighost.c + */ + +extern dig_lookuplist_t lookup_list; +extern dig_serverlist_t server_list; +extern dig_searchlistlist_t search_list; +extern unsigned int extrabytes; + +extern bool check_ra, have_ipv4, have_ipv6, specified_source, usesearch, + showsearch, yaml; +extern in_port_t port; +extern unsigned int timeout; +extern isc_mem_t *mctx; +extern int sendcount; +extern int ndots; +extern int lookup_counter; +extern int exitcode; +extern isc_sockaddr_t bind_address; +extern char keynametext[MXNAME]; +extern char keyfile[MXNAME]; +extern char keysecret[MXNAME]; +extern const dns_name_t *hmacname; +extern unsigned int digestbits; +extern dns_tsigkey_t *tsigkey; +extern bool validated; +extern isc_taskmgr_t *taskmgr; +extern isc_task_t *global_task; +extern bool free_now; +extern bool debugging, debugtiming, memdebugging; +extern bool keep_open; + +extern char *progname; +extern int tries; +extern int fatalexit; +extern bool verbose; + +/* + * Routines in dighost.c. + */ +isc_result_t +get_address(char *host, in_port_t myport, isc_sockaddr_t *sockaddr); + +int +getaddresses(dig_lookup_t *lookup, const char *host, isc_result_t *resultp); + +isc_result_t +get_reverse(char *reverse, size_t len, char *value, bool strict); + +ISC_PLATFORM_NORETURN_PRE void +fatal(const char *format, ...) + ISC_FORMAT_PRINTF(1, 2) ISC_PLATFORM_NORETURN_POST; + +void +warn(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +ISC_PLATFORM_NORETURN_PRE void +digexit(void) ISC_PLATFORM_NORETURN_POST; + +void +debug(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); + +void +check_result(isc_result_t result, const char *msg); + +bool +setup_lookup(dig_lookup_t *lookup); + +void +destroy_lookup(dig_lookup_t *lookup); + +void +do_lookup(dig_lookup_t *lookup); + +void +start_lookup(void); + +void +onrun_callback(isc_task_t *task, isc_event_t *event); + +int +dhmain(int argc, char **argv); + +void +setup_libs(void); + +void +setup_system(bool ipv4only, bool ipv6only); + +isc_result_t +parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc); + +isc_result_t +parse_xint(uint32_t *uip, const char *value, uint32_t max, const char *desc); + +isc_result_t +parse_netprefix(isc_sockaddr_t **sap, const char *value); + +void +parse_hmac(const char *hmacstr); + +dig_lookup_t * +requeue_lookup(dig_lookup_t *lookold, bool servers); + +dig_lookup_t * +make_empty_lookup(void); + +dig_lookup_t * +clone_lookup(dig_lookup_t *lookold, bool servers); + +dig_server_t * +make_server(const char *servname, const char *userarg); + +void +flush_server_list(void); + +void +set_nameserver(char *opt); + +void +clone_server_list(dig_serverlist_t src, dig_serverlist_t *dest); + +void +cancel_all(void); + +void +destroy_libs(void); + +void +set_search_domain(char *domain); + +/* + * Routines to be defined in dig.c, host.c, and nslookup.c. and + * then assigned to the appropriate function pointer + */ +extern isc_result_t (*dighost_printmessage)(dig_query_t *query, + const isc_buffer_t *msgbuf, + dns_message_t *msg, bool headers); + +/* + * Print an error message in the appropriate format. + */ +extern void (*dighost_error)(const char *format, ...); + +/* + * Print a warning message in the appropriate format. + */ +extern void (*dighost_warning)(const char *format, ...); + +/* + * Print a comment in the appropriate format. + */ +extern void (*dighost_comments)(dig_lookup_t *lookup, const char *format, ...); + +/*%< + * Print the final result of the lookup. + */ + +extern void (*dighost_received)(unsigned int bytes, isc_sockaddr_t *from, + dig_query_t *query); +/*%< + * Print a message about where and when the response + * was received from, like the final comment in the + * output of "dig". + */ + +extern void (*dighost_trying)(char *frm, dig_lookup_t *lookup); + +extern void (*dighost_shutdown)(void); + +extern void (*dighost_pre_exit_hook)(void); + +void +save_opt(dig_lookup_t *lookup, char *code, char *value); + +void +setup_file_key(void); +void +setup_text_key(void); + +/* + * Routines exported from dig.c for use by dig for iOS + */ + +/*%< + * Call once only to set up libraries, parse global + * parameters and initial command line query parameters + */ +void +dig_setup(int argc, char **argv); + +/*%< + * Call to supply new parameters for the next lookup + */ +void +dig_query_setup(bool, bool, int argc, char **argv); + +/*%< + * set the main application event cycle running + */ +void +dig_startup(void); + +/*%< + * Initiates the next lookup cycle + */ +void +dig_query_start(void); + +/*%< + * Cleans up the application + */ +void +dig_shutdown(void); + +ISC_LANG_ENDDECLS + +#endif /* ifndef DIG_H */ diff --git a/bin/dig/nslookup.c b/bin/dig/nslookup.c new file mode 100644 index 0000000..0fa691d --- /dev/null +++ b/bin/dig/nslookup.c @@ -0,0 +1,1047 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> + +#include <isc/app.h> +#include <isc/buffer.h> +#include <isc/commandline.h> +#include <isc/event.h> +#include <isc/netaddr.h> +#include <isc/parseint.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/util.h> + +#include <dns/byaddr.h> +#include <dns/fixedname.h> +#include <dns/message.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rdataset.h> +#include <dns/rdatastruct.h> +#include <dns/rdatatype.h> + +#include <dig/dig.h> + +#if defined(HAVE_READLINE) +#if defined(HAVE_EDIT_READLINE_READLINE_H) +#include <edit/readline/readline.h> +#if defined(HAVE_EDIT_READLINE_HISTORY_H) +#include <edit/readline/history.h> +#endif /* if defined(HAVE_EDIT_READLINE_HISTORY_H) */ +#elif defined(HAVE_EDITLINE_READLINE_H) +#include <editline/readline.h> +#elif defined(HAVE_READLINE_READLINE_H) +/* Prevent deprecated functions being declared. */ +#define _FUNCTION_DEF 1 +/* Ensure rl_message() gets prototype. */ +#define USE_VARARGS 1 +#define PREFER_STDARG 1 +#include <readline/readline.h> +#if defined(HAVE_READLINE_HISTORY_H) +#include <readline/history.h> +#endif /* if defined(HAVE_READLINE_HISTORY_H) */ +#endif /* if defined(HAVE_EDIT_READLINE_READLINE_H) */ +#endif /* if defined(HAVE_READLINE) */ + +static bool short_form = true, tcpmode = false, tcpmode_set = false, + identify = false, stats = true, comments = true, + section_question = true, section_answer = true, + section_authority = true, section_additional = true, recurse = true, + aaonly = false, nofail = true, default_lookups = true, + a_noanswer = false; + +static bool interactive; + +static bool in_use = false; +static char defclass[MXRD] = "IN"; +static char deftype[MXRD] = "A"; +static isc_event_t *global_event = NULL; +static int query_error = 1, print_error = 0; + +static char domainopt[DNS_NAME_MAXTEXT]; + +static const char *rcodetext[] = { "NOERROR", "FORMERR", "SERVFAIL", + "NXDOMAIN", "NOTIMP", "REFUSED", + "YXDOMAIN", "YXRRSET", "NXRRSET", + "NOTAUTH", "NOTZONE", "RESERVED11", + "RESERVED12", "RESERVED13", "RESERVED14", + "RESERVED15", "BADVERS" }; + +static const char *rtypetext[] = { + "rtype_0 = ", /* 0 */ + "internet address = ", /* 1 */ + "nameserver = ", /* 2 */ + "md = ", /* 3 */ + "mf = ", /* 4 */ + "canonical name = ", /* 5 */ + "soa = ", /* 6 */ + "mb = ", /* 7 */ + "mg = ", /* 8 */ + "mr = ", /* 9 */ + "rtype_10 = ", /* 10 */ + "protocol = ", /* 11 */ + "name = ", /* 12 */ + "hinfo = ", /* 13 */ + "minfo = ", /* 14 */ + "mail exchanger = ", /* 15 */ + "text = ", /* 16 */ + "rp = ", /* 17 */ + "afsdb = ", /* 18 */ + "x25 address = ", /* 19 */ + "isdn address = ", /* 20 */ + "rt = ", /* 21 */ + "nsap = ", /* 22 */ + "nsap_ptr = ", /* 23 */ + "signature = ", /* 24 */ + "key = ", /* 25 */ + "px = ", /* 26 */ + "gpos = ", /* 27 */ + "has AAAA address ", /* 28 */ + "loc = ", /* 29 */ + "next = ", /* 30 */ + "rtype_31 = ", /* 31 */ + "rtype_32 = ", /* 32 */ + "service = ", /* 33 */ + "rtype_34 = ", /* 34 */ + "naptr = ", /* 35 */ + "kx = ", /* 36 */ + "cert = ", /* 37 */ + "v6 address = ", /* 38 */ + "dname = ", /* 39 */ + "rtype_40 = ", /* 40 */ + "optional = " /* 41 */ +}; + +#define N_KNOWN_RRTYPES (sizeof(rtypetext) / sizeof(rtypetext[0])) + +static void +flush_lookup_list(void); +static void +getinput(isc_task_t *task, isc_event_t *event); + +static char * +rcode_totext(dns_rcode_t rcode) { + static char buf[sizeof("?65535")]; + union { + const char *consttext; + char *deconsttext; + } totext; + + if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) { + snprintf(buf, sizeof(buf), "?%u", rcode); + totext.deconsttext = buf; + } else { + totext.consttext = rcodetext[rcode]; + } + return (totext.deconsttext); +} + +static void +query_finished(void) { + isc_event_t *event = global_event; + + flush_lookup_list(); + debug("dighost_shutdown()"); + + if (!in_use) { + isc_app_shutdown(); + return; + } + + isc_task_send(global_task, &event); +} + +static void +printsoa(dns_rdata_t *rdata) { + dns_rdata_soa_t soa; + isc_result_t result; + char namebuf[DNS_NAME_FORMATSIZE]; + + result = dns_rdata_tostruct(rdata, &soa, NULL); + check_result(result, "dns_rdata_tostruct"); + + dns_name_format(&soa.origin, namebuf, sizeof(namebuf)); + printf("\torigin = %s\n", namebuf); + dns_name_format(&soa.contact, namebuf, sizeof(namebuf)); + printf("\tmail addr = %s\n", namebuf); + printf("\tserial = %u\n", soa.serial); + printf("\trefresh = %u\n", soa.refresh); + printf("\tretry = %u\n", soa.retry); + printf("\texpire = %u\n", soa.expire); + printf("\tminimum = %u\n", soa.minimum); + dns_rdata_freestruct(&soa); +} + +static void +printaddr(dns_rdata_t *rdata) { + isc_result_t result; + char text[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + isc_buffer_t b; + + isc_buffer_init(&b, text, sizeof(text)); + result = dns_rdata_totext(rdata, NULL, &b); + check_result(result, "dns_rdata_totext"); + printf("Address: %.*s\n", (int)isc_buffer_usedlength(&b), + (char *)isc_buffer_base(&b)); +} + +static void +printrdata(dns_rdata_t *rdata) { + isc_result_t result; + isc_buffer_t *b = NULL; + unsigned int size = 1024; + bool done = false; + + if (rdata->type < N_KNOWN_RRTYPES) { + printf("%s", rtypetext[rdata->type]); + } else { + printf("rdata_%d = ", rdata->type); + } + + while (!done) { + isc_buffer_allocate(mctx, &b, size); + result = dns_rdata_totext(rdata, NULL, b); + if (result == ISC_R_SUCCESS) { + printf("%.*s\n", (int)isc_buffer_usedlength(b), + (char *)isc_buffer_base(b)); + done = true; + } else if (result != ISC_R_NOSPACE) { + check_result(result, "dns_rdata_totext"); + } + isc_buffer_free(&b); + size *= 2; + } +} + +static isc_result_t +printsection(dig_query_t *query, dns_message_t *msg, bool headers, + dns_section_t section) { + isc_result_t result, loopresult; + dns_name_t *name; + dns_rdataset_t *rdataset = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + char namebuf[DNS_NAME_FORMATSIZE]; + + UNUSED(query); + UNUSED(headers); + + debug("printsection()"); + + result = dns_message_firstname(msg, section); + if (result == ISC_R_NOMORE) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + return (result); + } + for (;;) { + name = NULL; + dns_message_currentname(msg, section, &name); + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + loopresult = dns_rdataset_first(rdataset); + while (loopresult == ISC_R_SUCCESS) { + dns_rdataset_current(rdataset, &rdata); + switch (rdata.type) { + case dns_rdatatype_a: + case dns_rdatatype_aaaa: + if (section != DNS_SECTION_ANSWER) { + goto def_short_section; + } + dns_name_format(name, namebuf, + sizeof(namebuf)); + printf("Name:\t%s\n", namebuf); + printaddr(&rdata); + break; + case dns_rdatatype_soa: + dns_name_format(name, namebuf, + sizeof(namebuf)); + printf("%s\n", namebuf); + printsoa(&rdata); + break; + default: + def_short_section: + dns_name_format(name, namebuf, + sizeof(namebuf)); + printf("%s\t", namebuf); + printrdata(&rdata); + break; + } + dns_rdata_reset(&rdata); + loopresult = dns_rdataset_next(rdataset); + } + } + result = dns_message_nextname(msg, section); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + return (result); + } + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +detailsection(dig_query_t *query, dns_message_t *msg, bool headers, + dns_section_t section) { + isc_result_t result, loopresult; + dns_name_t *name; + dns_rdataset_t *rdataset = NULL; + dns_rdata_t rdata = DNS_RDATA_INIT; + char namebuf[DNS_NAME_FORMATSIZE]; + + UNUSED(query); + + debug("detailsection()"); + + if (headers) { + switch (section) { + case DNS_SECTION_QUESTION: + puts(" QUESTIONS:"); + break; + case DNS_SECTION_ANSWER: + puts(" ANSWERS:"); + break; + case DNS_SECTION_AUTHORITY: + puts(" AUTHORITY RECORDS:"); + break; + case DNS_SECTION_ADDITIONAL: + puts(" ADDITIONAL RECORDS:"); + break; + } + } + + result = dns_message_firstname(msg, section); + if (result == ISC_R_NOMORE) { + return (ISC_R_SUCCESS); + } else if (result != ISC_R_SUCCESS) { + return (result); + } + for (;;) { + name = NULL; + dns_message_currentname(msg, section, &name); + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (section == DNS_SECTION_QUESTION) { + dns_name_format(name, namebuf, sizeof(namebuf)); + printf("\t%s, ", namebuf); + dns_rdatatype_format(rdataset->type, namebuf, + sizeof(namebuf)); + printf("type = %s, ", namebuf); + dns_rdataclass_format(rdataset->rdclass, + namebuf, sizeof(namebuf)); + printf("class = %s\n", namebuf); + } + loopresult = dns_rdataset_first(rdataset); + while (loopresult == ISC_R_SUCCESS) { + dns_rdataset_current(rdataset, &rdata); + + dns_name_format(name, namebuf, sizeof(namebuf)); + printf(" -> %s\n", namebuf); + + switch (rdata.type) { + case dns_rdatatype_soa: + printsoa(&rdata); + break; + default: + printf("\t"); + printrdata(&rdata); + } + dns_rdata_reset(&rdata); + printf("\tttl = %u\n", rdataset->ttl); + loopresult = dns_rdataset_next(rdataset); + } + } + result = dns_message_nextname(msg, section); + if (result == ISC_R_NOMORE) { + break; + } else if (result != ISC_R_SUCCESS) { + return (result); + } + } + return (ISC_R_SUCCESS); +} + +static void +received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { + UNUSED(bytes); + UNUSED(from); + UNUSED(query); +} + +static void +trying(char *frm, dig_lookup_t *lookup) { + UNUSED(frm); + UNUSED(lookup); +} + +static void +chase_cnamechain(dns_message_t *msg, dns_name_t *qname) { + isc_result_t result; + dns_rdataset_t *rdataset; + dns_rdata_cname_t cname; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int i = msg->counts[DNS_SECTION_ANSWER]; + + while (i-- > 0) { + rdataset = NULL; + result = dns_message_findname(msg, DNS_SECTION_ANSWER, qname, + dns_rdatatype_cname, 0, NULL, + &rdataset); + if (result != ISC_R_SUCCESS) { + return; + } + result = dns_rdataset_first(rdataset); + check_result(result, "dns_rdataset_first"); + dns_rdata_reset(&rdata); + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &cname, NULL); + check_result(result, "dns_rdata_tostruct"); + dns_name_copynf(&cname.cname, qname); + dns_rdata_freestruct(&cname); + } +} + +static isc_result_t +printmessage(dig_query_t *query, const isc_buffer_t *msgbuf, dns_message_t *msg, + bool headers) { + char servtext[ISC_SOCKADDR_FORMATSIZE]; + + UNUSED(msgbuf); + + /* I've we've gotten this far, we've reached a server. */ + query_error = 0; + + debug("printmessage()"); + + if (!default_lookups || query->lookup->rdtype == dns_rdatatype_a) { + isc_sockaddr_format(&query->sockaddr, servtext, + sizeof(servtext)); + printf("Server:\t\t%s\n", query->userarg); + printf("Address:\t%s\n", servtext); + + puts(""); + } + + if (!short_form) { + puts("------------"); + /* detailheader(query, msg);*/ + detailsection(query, msg, true, DNS_SECTION_QUESTION); + detailsection(query, msg, true, DNS_SECTION_ANSWER); + detailsection(query, msg, true, DNS_SECTION_AUTHORITY); + detailsection(query, msg, true, DNS_SECTION_ADDITIONAL); + puts("------------"); + } + + if (msg->rcode != 0) { + char nametext[DNS_NAME_FORMATSIZE]; + dns_name_format(query->lookup->name, nametext, + sizeof(nametext)); + printf("** server can't find %s: %s\n", nametext, + rcode_totext(msg->rcode)); + debug("returning with rcode == 0"); + + /* the lookup failed */ + print_error |= 1; + return (ISC_R_SUCCESS); + } + + if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) { + char namestr[DNS_NAME_FORMATSIZE]; + dig_lookup_t *lookup; + dns_fixedname_t fixed; + dns_name_t *name; + + /* Add AAAA lookup. */ + name = dns_fixedname_initname(&fixed); + dns_name_copynf(query->lookup->name, name); + chase_cnamechain(msg, name); + dns_name_format(name, namestr, sizeof(namestr)); + lookup = clone_lookup(query->lookup, false); + if (lookup != NULL) { + strlcpy(lookup->textname, namestr, + sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_aaaa; + lookup->rdtypeset = true; + lookup->origin = NULL; + lookup->retries = tries; + ISC_LIST_APPEND(lookup_list, lookup, link); + } + } + + if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0 && + (!default_lookups || query->lookup->rdtype == dns_rdatatype_a)) + { + puts("Non-authoritative answer:"); + } + if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) { + printsection(query, msg, headers, DNS_SECTION_ANSWER); + } else { + if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) + { + a_noanswer = true; + } else if (!default_lookups || + (query->lookup->rdtype == dns_rdatatype_aaaa && + a_noanswer)) + { + printf("*** Can't find %s: No answer\n", + query->lookup->textname); + } + } + + if (((msg->flags & DNS_MESSAGEFLAG_AA) == 0) && + (query->lookup->rdtype != dns_rdatatype_a) && + (query->lookup->rdtype != dns_rdatatype_aaaa)) + { + puts("\nAuthoritative answers can be found from:"); + printsection(query, msg, headers, DNS_SECTION_AUTHORITY); + printsection(query, msg, headers, DNS_SECTION_ADDITIONAL); + } + return (ISC_R_SUCCESS); +} + +static void +show_settings(bool full, bool serv_only) { + dig_server_t *srv; + isc_sockaddr_t sockaddr; + dig_searchlist_t *listent; + isc_result_t result; + + srv = ISC_LIST_HEAD(server_list); + + while (srv != NULL) { + char sockstr[ISC_SOCKADDR_FORMATSIZE]; + + result = get_address(srv->servername, port, &sockaddr); + check_result(result, "get_address"); + + isc_sockaddr_format(&sockaddr, sockstr, sizeof(sockstr)); + printf("Default server: %s\nAddress: %s\n", srv->userarg, + sockstr); + if (!full) { + return; + } + srv = ISC_LIST_NEXT(srv, link); + } + if (serv_only) { + return; + } + printf("\nSet options:\n"); + printf(" %s\t\t\t%s\t\t%s\n", tcpmode ? "vc" : "novc", + short_form ? "nodebug" : "debug", debugging ? "d2" : "nod2"); + printf(" %s\t\t%s\n", usesearch ? "search" : "nosearch", + recurse ? "recurse" : "norecurse"); + printf(" timeout = %u\t\tretry = %d\tport = %u\tndots = %d\n", timeout, + tries, port, ndots); + printf(" querytype = %-8s\tclass = %s\n", deftype, defclass); + printf(" srchlist = "); + for (listent = ISC_LIST_HEAD(search_list); listent != NULL; + listent = ISC_LIST_NEXT(listent, link)) + { + printf("%s", listent->origin); + if (ISC_LIST_NEXT(listent, link) != NULL) { + printf("/"); + } + } + printf("\n"); +} + +static bool +testtype(char *typetext) { + isc_result_t result; + isc_textregion_t tr; + dns_rdatatype_t rdtype; + + tr.base = typetext; + tr.length = strlen(typetext); + result = dns_rdatatype_fromtext(&rdtype, &tr); + if (result == ISC_R_SUCCESS) { + return (true); + } else { + printf("unknown query type: %s\n", typetext); + return (false); + } +} + +static bool +testclass(char *typetext) { + isc_result_t result; + isc_textregion_t tr; + dns_rdataclass_t rdclass; + + tr.base = typetext; + tr.length = strlen(typetext); + result = dns_rdataclass_fromtext(&rdclass, &tr); + if (result == ISC_R_SUCCESS) { + return (true); + } else { + printf("unknown query class: %s\n", typetext); + return (false); + } +} + +static void +set_port(const char *value) { + uint32_t n; + isc_result_t result = parse_uint(&n, value, 65535, "port"); + if (result == ISC_R_SUCCESS) { + port = (uint16_t)n; + } +} + +static void +set_timeout(const char *value) { + uint32_t n; + isc_result_t result = parse_uint(&n, value, UINT_MAX, "timeout"); + if (result == ISC_R_SUCCESS) { + timeout = n; + } +} + +static void +set_tries(const char *value) { + uint32_t n; + isc_result_t result = parse_uint(&n, value, INT_MAX, "tries"); + if (result == ISC_R_SUCCESS) { + tries = n; + } +} + +static void +set_ndots(const char *value) { + uint32_t n; + isc_result_t result = parse_uint(&n, value, 128, "ndots"); + if (result == ISC_R_SUCCESS) { + ndots = n; + } +} + +static void +version(void) { + fputs("nslookup " VERSION "\n", stderr); +} + +static void +setoption(char *opt) { + size_t l = strlen(opt); + +#define CHECKOPT(A, N) \ + ((l >= N) && (l < sizeof(A)) && (strncasecmp(opt, A, l) == 0)) + + if (CHECKOPT("all", 3)) { + show_settings(true, false); + } else if (strncasecmp(opt, "class=", 6) == 0) { + if (testclass(&opt[6])) { + strlcpy(defclass, &opt[6], sizeof(defclass)); + } + } else if (strncasecmp(opt, "cl=", 3) == 0) { + if (testclass(&opt[3])) { + strlcpy(defclass, &opt[3], sizeof(defclass)); + } + } else if (strncasecmp(opt, "type=", 5) == 0) { + if (testtype(&opt[5])) { + strlcpy(deftype, &opt[5], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "ty=", 3) == 0) { + if (testtype(&opt[3])) { + strlcpy(deftype, &opt[3], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "querytype=", 10) == 0) { + if (testtype(&opt[10])) { + strlcpy(deftype, &opt[10], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "query=", 6) == 0) { + if (testtype(&opt[6])) { + strlcpy(deftype, &opt[6], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "qu=", 3) == 0) { + if (testtype(&opt[3])) { + strlcpy(deftype, &opt[3], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "q=", 2) == 0) { + if (testtype(&opt[2])) { + strlcpy(deftype, &opt[2], sizeof(deftype)); + default_lookups = false; + } + } else if (strncasecmp(opt, "domain=", 7) == 0) { + strlcpy(domainopt, &opt[7], sizeof(domainopt)); + set_search_domain(domainopt); + usesearch = true; + } else if (strncasecmp(opt, "do=", 3) == 0) { + strlcpy(domainopt, &opt[3], sizeof(domainopt)); + set_search_domain(domainopt); + usesearch = true; + } else if (strncasecmp(opt, "port=", 5) == 0) { + set_port(&opt[5]); + } else if (strncasecmp(opt, "po=", 3) == 0) { + set_port(&opt[3]); + } else if (strncasecmp(opt, "timeout=", 8) == 0) { + set_timeout(&opt[8]); + } else if (strncasecmp(opt, "t=", 2) == 0) { + set_timeout(&opt[2]); + } else if (CHECKOPT("recurse", 3)) { + recurse = true; + } else if (CHECKOPT("norecurse", 5)) { + recurse = false; + } else if (strncasecmp(opt, "retry=", 6) == 0) { + set_tries(&opt[6]); + } else if (strncasecmp(opt, "ret=", 4) == 0) { + set_tries(&opt[4]); + } else if (CHECKOPT("defname", 3)) { + usesearch = true; + } else if (CHECKOPT("nodefname", 5)) { + usesearch = false; + } else if (CHECKOPT("vc", 2)) { + tcpmode = true; + tcpmode_set = true; + } else if (CHECKOPT("novc", 4)) { + tcpmode = false; + tcpmode_set = true; + } else if (CHECKOPT("debug", 3)) { + short_form = false; + showsearch = true; + } else if (CHECKOPT("nodebug", 5)) { + short_form = true; + showsearch = false; + } else if (CHECKOPT("d2", 2)) { + debugging = true; + } else if (CHECKOPT("nod2", 4)) { + debugging = false; + } else if (CHECKOPT("search", 3)) { + usesearch = true; + } else if (CHECKOPT("nosearch", 5)) { + usesearch = false; + } else if (CHECKOPT("sil", 3)) { + /* deprecation_msg = false; */ + } else if (CHECKOPT("fail", 3)) { + nofail = false; + } else if (CHECKOPT("nofail", 5)) { + nofail = true; + } else if (strncasecmp(opt, "ndots=", 6) == 0) { + set_ndots(&opt[6]); + } else { + printf("*** Invalid option: %s\n", opt); + } +} + +static void +addlookup(char *opt) { + dig_lookup_t *lookup; + isc_result_t result; + isc_textregion_t tr; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + char store[MXNAME]; + + debug("addlookup()"); + + a_noanswer = false; + + tr.base = deftype; + tr.length = strlen(deftype); + result = dns_rdatatype_fromtext(&rdtype, &tr); + if (result != ISC_R_SUCCESS) { + printf("unknown query type: %s\n", deftype); + rdclass = dns_rdatatype_a; + } + tr.base = defclass; + tr.length = strlen(defclass); + result = dns_rdataclass_fromtext(&rdclass, &tr); + if (result != ISC_R_SUCCESS) { + printf("unknown query class: %s\n", defclass); + rdclass = dns_rdataclass_in; + } + lookup = make_empty_lookup(); + if (get_reverse(store, sizeof(store), opt, true) == ISC_R_SUCCESS) { + strlcpy(lookup->textname, store, sizeof(lookup->textname)); + lookup->rdtype = dns_rdatatype_ptr; + lookup->rdtypeset = true; + } else { + strlcpy(lookup->textname, opt, sizeof(lookup->textname)); + lookup->rdtype = rdtype; + lookup->rdtypeset = true; + } + lookup->rdclass = rdclass; + lookup->rdclassset = true; + lookup->trace = false; + lookup->trace_root = lookup->trace; + lookup->ns_search_only = false; + lookup->identify = identify; + lookup->recurse = recurse; + lookup->aaonly = aaonly; + lookup->retries = tries; + lookup->comments = comments; + if (lookup->rdtype == dns_rdatatype_any && !tcpmode_set) { + lookup->tcp_mode = true; + } else { + lookup->tcp_mode = tcpmode; + } + lookup->stats = stats; + lookup->section_question = section_question; + lookup->section_answer = section_answer; + lookup->section_authority = section_authority; + lookup->section_additional = section_additional; + lookup->new_search = true; + lookup->besteffort = false; + if (nofail) { + lookup->servfail_stops = false; + } + ISC_LIST_INIT(lookup->q); + ISC_LINK_INIT(lookup, link); + ISC_LIST_APPEND(lookup_list, lookup, link); + lookup->origin = NULL; + ISC_LIST_INIT(lookup->my_server_list); + debug("looking up %s", lookup->textname); +} + +static void +do_next_command(char *input) { + char *ptr, *arg, *last; + + if ((ptr = strtok_r(input, " \t\r\n", &last)) == NULL) { + return; + } + arg = strtok_r(NULL, " \t\r\n", &last); + if ((strcasecmp(ptr, "set") == 0) && (arg != NULL)) { + setoption(arg); + } else if ((strcasecmp(ptr, "server") == 0) || + (strcasecmp(ptr, "lserver") == 0)) + { + isc_app_block(); + set_nameserver(arg); + check_ra = false; + isc_app_unblock(); + show_settings(true, true); + } else if (strcasecmp(ptr, "exit") == 0) { + in_use = false; + } else if (strcasecmp(ptr, "help") == 0 || strcasecmp(ptr, "?") == 0) { + printf("The '%s' command is not yet implemented.\n", ptr); + } else if (strcasecmp(ptr, "finger") == 0 || + strcasecmp(ptr, "root") == 0 || strcasecmp(ptr, "ls") == 0 || + strcasecmp(ptr, "view") == 0) + { + printf("The '%s' command is not implemented.\n", ptr); + } else { + addlookup(ptr); + } +} + +static void +get_next_command(void) { + char *buf; + char *ptr; + + fflush(stdout); + buf = isc_mem_allocate(mctx, COMMSIZE); + isc_app_block(); + if (interactive) { +#ifdef HAVE_READLINE + ptr = readline("> "); + if (ptr != NULL) { + add_history(ptr); + } +#else /* ifdef HAVE_READLINE */ + fputs("> ", stderr); + fflush(stderr); + ptr = fgets(buf, COMMSIZE, stdin); +#endif /* ifdef HAVE_READLINE */ + } else { + ptr = fgets(buf, COMMSIZE, stdin); + } + isc_app_unblock(); + if (ptr == NULL) { + in_use = false; + } else { + do_next_command(ptr); + } +#ifdef HAVE_READLINE + if (interactive) { + free(ptr); + } +#endif /* ifdef HAVE_READLINE */ + isc_mem_free(mctx, buf); +} + +ISC_PLATFORM_NORETURN_PRE static void +usage(void) ISC_PLATFORM_NORETURN_POST; + +static void +usage(void) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " nslookup [-opt ...] # interactive mode " + "using default server\n"); + fprintf(stderr, " nslookup [-opt ...] - server # interactive mode " + "using 'server'\n"); + fprintf(stderr, " nslookup [-opt ...] host # just look up " + "'host' using default server\n"); + fprintf(stderr, " nslookup [-opt ...] host server # just look up " + "'host' using 'server'\n"); + exit(1); +} + +static void +parse_args(int argc, char **argv) { + bool have_lookup = false; + + usesearch = true; + for (argc--, argv++; argc > 0 && argv[0] != NULL; argc--, argv++) { + debug("main parsing %s", argv[0]); + if (argv[0][0] == '-') { + if (strncasecmp(argv[0], "-ver", 4) == 0) { + version(); + exit(0); + } else if (argv[0][1] != 0) { + setoption(&argv[0][1]); + } else { + have_lookup = true; + } + } else { + if (!have_lookup) { + have_lookup = true; + in_use = true; + addlookup(argv[0]); + } else { + if (argv[1] != NULL) { + usage(); + } + set_nameserver(argv[0]); + check_ra = false; + } + } + } +} + +static void +flush_lookup_list(void) { + dig_lookup_t *l, *lp; + dig_query_t *q, *qp; + dig_server_t *s, *sp; + + lookup_counter = 0; + l = ISC_LIST_HEAD(lookup_list); + while (l != NULL) { + q = ISC_LIST_HEAD(l->q); + while (q != NULL) { + if (q->sock != NULL) { + isc_socket_cancel(q->sock, NULL, + ISC_SOCKCANCEL_ALL); + isc_socket_detach(&q->sock); + } + isc_buffer_invalidate(&q->recvbuf); + isc_buffer_invalidate(&q->lengthbuf); + qp = q; + q = ISC_LIST_NEXT(q, link); + ISC_LIST_DEQUEUE(l->q, qp, link); + isc_mem_free(mctx, qp); + } + s = ISC_LIST_HEAD(l->my_server_list); + while (s != NULL) { + sp = s; + s = ISC_LIST_NEXT(s, link); + ISC_LIST_DEQUEUE(l->my_server_list, sp, link); + isc_mem_free(mctx, sp); + } + if (l->sendmsg != NULL) { + dns_message_detach(&l->sendmsg); + } + lp = l; + l = ISC_LIST_NEXT(l, link); + ISC_LIST_DEQUEUE(lookup_list, lp, link); + isc_mem_free(mctx, lp); + } +} + +static void +getinput(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + if (global_event == NULL) { + global_event = event; + } + while (in_use) { + get_next_command(); + if (ISC_LIST_HEAD(lookup_list) != NULL) { + start_lookup(); + return; + } + } + isc_app_shutdown(); +} + +int +main(int argc, char **argv) { + isc_result_t result; + + interactive = isatty(0); + + ISC_LIST_INIT(lookup_list); + ISC_LIST_INIT(server_list); + ISC_LIST_INIT(search_list); + + check_ra = true; + + /* setup dighost callbacks */ + dighost_printmessage = printmessage; + dighost_received = received; + dighost_trying = trying; + dighost_shutdown = query_finished; + + result = isc_app_start(); + check_result(result, "isc_app_start"); + + setup_libs(); + progname = argv[0]; + + setup_system(false, false); + parse_args(argc, argv); + if (keyfile[0] != 0) { + setup_file_key(); + } else if (keysecret[0] != 0) { + setup_text_key(); + } + if (domainopt[0] != '\0') { + set_search_domain(domainopt); + } + if (in_use) { + result = isc_app_onrun(mctx, global_task, onrun_callback, NULL); + } else { + result = isc_app_onrun(mctx, global_task, getinput, NULL); + } + check_result(result, "isc_app_onrun"); + in_use = !in_use; + + (void)isc_app_run(); + + puts(""); + debug("done, and starting to shut down"); + if (global_event != NULL) { + isc_event_free(&global_event); + } + cancel_all(); + destroy_libs(); + isc_app_finish(); + + return (query_error | print_error); +} diff --git a/bin/dig/nslookup.rst b/bin/dig/nslookup.rst new file mode 100644 index 0000000..2d1e123 --- /dev/null +++ b/bin/dig/nslookup.rst @@ -0,0 +1,206 @@ +.. Copyright (C) Internet Systems Consortium, Inc. ("ISC") +.. +.. SPDX-License-Identifier: MPL-2.0 +.. +.. This Source Code Form is subject to the terms of the Mozilla Public +.. License, v. 2.0. If a copy of the MPL was not distributed with this +.. file, you can obtain one at https://mozilla.org/MPL/2.0/. +.. +.. See the COPYRIGHT file distributed with this work for additional +.. information regarding copyright ownership. + +.. highlight: console + +.. _man_nslookup: + +nslookup - query Internet name servers interactively +---------------------------------------------------- + +Synopsis +~~~~~~~~ + +:program:`nslookup` [-option] [name | -] [server] + +Description +~~~~~~~~~~~ + +``nslookup`` is a program to query Internet domain name servers. +``nslookup`` has two modes: interactive and non-interactive. Interactive +mode allows the user to query name servers for information about various +hosts and domains or to print a list of hosts in a domain. +Non-interactive mode prints just the name and requested +information for a host or domain. + +Arguments +~~~~~~~~~ + +Interactive mode is entered in the following cases: + +a. when no arguments are given (the default name server is used); + +b. when the first argument is a hyphen (-) and the second argument is + the host name or Internet address of a name server. + +Non-interactive mode is used when the name or Internet address of the +host to be looked up is given as the first argument. The optional second +argument specifies the host name or address of a name server. + +Options can also be specified on the command line if they precede the +arguments and are prefixed with a hyphen. For example, to change the +default query type to host information, with an initial timeout of 10 +seconds, type: + +:: + + nslookup -query=hinfo -timeout=10 + +The ``-version`` option causes ``nslookup`` to print the version number +and immediately exit. + +Interactive Commands +~~~~~~~~~~~~~~~~~~~~ + +``host [server]`` + This command looks up information for ``host`` using the current default server or + using ``server``, if specified. If ``host`` is an Internet address and the + query type is A or PTR, the name of the host is returned. If ``host`` is + a name and does not have a trailing period (``.``), the search list is used + to qualify the name. + + To look up a host not in the current domain, append a period to the + name. + +``server domain`` | ``lserver domain`` + These commands change the default server to ``domain``; ``lserver`` uses the initial + server to look up information about ``domain``, while ``server`` uses the + current default server. If an authoritative answer cannot be found, + the names of servers that might have the answer are returned. + +``root`` + This command is not implemented. + +``finger`` + This command is not implemented. + +``ls`` + This command is not implemented. + +``view`` + This command is not implemented. + +``help`` + This command is not implemented. + +``?`` + This command is not implemented. + +``exit`` + This command exits the program. + +``set keyword[=value]`` + This command is used to change state information that affects the + lookups. Valid keywords are: + + ``all`` + This keyword prints the current values of the frequently used options to + ``set``. Information about the current default server and host is + also printed. + + ``class=value`` + This keyword changes the query class to one of: + + ``IN`` + the Internet class + + ``CH`` + the Chaos class + + ``HS`` + the Hesiod class + + ``ANY`` + wildcard + + The class specifies the protocol group of the information. The default + is ``IN``; the abbreviation for this keyword is ``cl``. + + ``nodebug`` + This keyword turns on or off the display of the full response packet, and any + intermediate response packets, when searching. The default for this keyword is + ``nodebug``; the abbreviation for this keyword is ``[no]deb``. + + ``nod2`` + This keyword turns debugging mode on or off. This displays more about what + nslookup is doing. The default is ``nod2``. + + ``domain=name`` + This keyword sets the search list to ``name``. + + ``nosearch`` + If the lookup request contains at least one period, but does not end + with a trailing period, this keyword appends the domain names in the domain + search list to the request until an answer is received. The default is ``search``. + + ``port=value`` + This keyword changes the default TCP/UDP name server port to ``value`` from + its default, port 53. The abbreviation for this keyword is ``po``. + + ``querytype=value`` | ``type=value`` + This keyword changes the type of the information query to ``value``. The + defaults are A and then AAAA; the abbreviations for these keywords are + ``q`` and ``ty``. + + Please note that it is only possible to specify one query type. Only the default + behavior looks up both when an alternative is not specified. + + ``norecurse`` + This keyword tells the name server to query other servers if it does not have + the information. The default is ``recurse``; the abbreviation for this + keyword is ``[no]rec``. + + ``ndots=number`` + This keyword sets the number of dots (label separators) in a domain that + disables searching. Absolute names always stop searching. + + ``retry=number`` + This keyword sets the number of retries to ``number``. + + ``timeout=number`` + This keyword changes the initial timeout interval to wait for a reply to + ``number``, in seconds. + + ``novc`` + This keyword indicates that a virtual circuit should always be used when sending requests to the server. + ``novc`` is the default. + + ``nofail`` + This keyword tries the next nameserver if a nameserver responds with SERVFAIL or + a referral (nofail), or terminates the query (fail) on such a response. The + default is ``nofail``. + +Return Values +~~~~~~~~~~~~~ + +``nslookup`` returns with an exit status of 1 if any query failed, and 0 +otherwise. + +IDN Support +~~~~~~~~~~~ + +If ``nslookup`` has been built with IDN (internationalized domain name) +support, it can accept and display non-ASCII domain names. ``nslookup`` +appropriately converts character encoding of a domain name before sending +a request to a DNS server or displaying a reply from the server. +To turn off IDN support, define the ``IDN_DISABLE`` +environment variable. IDN support is disabled if the variable is set +when ``nslookup`` runs, or when the standard output is not a tty. + +Files +~~~~~ + +``/etc/resolv.conf`` + +See Also +~~~~~~~~ + +:manpage:`dig(1)`, :manpage:`host(1)`, :manpage:`named(8)`. diff --git a/bin/dig/win32/dig.vcxproj.filters.in b/bin/dig/win32/dig.vcxproj.filters.in new file mode 100644 index 0000000..1d92732 --- /dev/null +++ b/bin/dig/win32/dig.vcxproj.filters.in @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\include\dig\dig.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\dig.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/bin/dig/win32/dig.vcxproj.in b/bin/dig/win32/dig.vcxproj.in new file mode 100644 index 0000000..c65a103 --- /dev/null +++ b/bin/dig/win32/dig.vcxproj.in @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|@PLATFORM@"> + <Configuration>Debug</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|@PLATFORM@"> + <Configuration>Release</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{F938F9B8-D395-4A40-BEC7-0122D289C692}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>dig</RootNamespace> + @WINDOWS_TARGET_PLATFORM_VERSION@ + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>MultiByte</CharacterSet> + @PLATFORM_TOOLSET@ + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + @PLATFORM_TOOLSET@ + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <TreatWarningAsError>false</TreatWarningAsError> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <BrowseInformation>true</BrowseInformation> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\include;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@IDN_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\include;..\..\..\lib\dns\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\irs\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dighost.lib;libisc.lib;libisccfg.lib;libirs.lib;libdns.lib;libbind9.lib;@IDN_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <ClCompile> + <WarningLevel>Level1</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <WholeProgramOptimization>false</WholeProgramOptimization> + <StringPooling>true</StringPooling> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\include;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@IDN_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\include;..\..\..\lib\dns\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\irs\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dighost.lib;libisc.lib;libisccfg.lib;libirs.lib;libdns.lib;libbind9.lib;@IDN_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="..\include\dig\dig.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\dig.c" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/bin/dig/win32/dig.vcxproj.user b/bin/dig/win32/dig.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/bin/dig/win32/dig.vcxproj.user @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +</Project>
\ No newline at end of file diff --git a/bin/dig/win32/dighost.vcxproj.filters.in b/bin/dig/win32/dighost.vcxproj.filters.in new file mode 100644 index 0000000..d108bfb --- /dev/null +++ b/bin/dig/win32/dighost.vcxproj.filters.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\dighost.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/bin/dig/win32/dighost.vcxproj.in b/bin/dig/win32/dighost.vcxproj.in new file mode 100644 index 0000000..1ed120c --- /dev/null +++ b/bin/dig/win32/dighost.vcxproj.in @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|@PLATFORM@"> + <Configuration>Debug</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|@PLATFORM@"> + <Configuration>Release</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{140DE800-E552-43CC-B0C7-A33A92E368CA}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>dighost</RootNamespace> + @WINDOWS_TARGET_PLATFORM_VERSION@ + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>MultiByte</CharacterSet> + @PLATFORM_TOOLSET@ + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + @PLATFORM_TOOLSET@ + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>.\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>.\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <TreatWarningAsError>false</TreatWarningAsError> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <BrowseInformation>true</BrowseInformation> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@IDN_INC@..\include;..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\include;..\..\..\lib\dns\include;..\..\..\lib\irs\include;..\..\..\lib\irs\win32\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <ClCompile> + <WarningLevel>Level1</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <WholeProgramOptimization>false</WholeProgramOptimization> + <StringPooling>true</StringPooling> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@IDN_INC@..\include;..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\include;..\..\..\lib\dns\include;..\..\..\lib\irs\include;..\..\..\lib\irs\win32\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <LinkTimeCodeGeneration>false</LinkTimeCodeGeneration> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\dighost.c" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/bin/dig/win32/dighost.vcxproj.user b/bin/dig/win32/dighost.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/bin/dig/win32/dighost.vcxproj.user @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +</Project>
\ No newline at end of file diff --git a/bin/dig/win32/host.vcxproj.filters.in b/bin/dig/win32/host.vcxproj.filters.in new file mode 100644 index 0000000..56e7818 --- /dev/null +++ b/bin/dig/win32/host.vcxproj.filters.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\host.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/bin/dig/win32/host.vcxproj.in b/bin/dig/win32/host.vcxproj.in new file mode 100644 index 0000000..091b4fb --- /dev/null +++ b/bin/dig/win32/host.vcxproj.in @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|@PLATFORM@"> + <Configuration>Debug</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|@PLATFORM@"> + <Configuration>Release</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{BA1048A8-6961-4A20-BE12-08BE20611C9D}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>host</RootNamespace> + @WINDOWS_TARGET_PLATFORM_VERSION@ + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>MultiByte</CharacterSet> + @PLATFORM_TOOLSET@ + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + @PLATFORM_TOOLSET@ + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <TreatWarningAsError>false</TreatWarningAsError> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <BrowseInformation>true</BrowseInformation> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\include;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@IDN_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\include;..\..\..\lib\dns\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\irs\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dighost.lib;@IDN_LIB@libisc.lib;libisccfg.lib;libirs.lib;libdns.lib;libbind9.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <ClCompile> + <WarningLevel>Level1</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <WholeProgramOptimization>false</WholeProgramOptimization> + <StringPooling>true</StringPooling> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\include;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@IDN_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\include;..\..\..\lib\dns\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <AdditionalLibraryDirectories>$(Configuration);..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\irs\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@dighost.lib;@IDN_LIB@libisc.lib;libisccfg.lib;libirs.lib;libdns.lib;libbind9.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\host.c" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/bin/dig/win32/host.vcxproj.user b/bin/dig/win32/host.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/bin/dig/win32/host.vcxproj.user @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +</Project>
\ No newline at end of file diff --git a/bin/dig/win32/nslookup.vcxproj.filters.in b/bin/dig/win32/nslookup.vcxproj.filters.in new file mode 100644 index 0000000..7f4756e --- /dev/null +++ b/bin/dig/win32/nslookup.vcxproj.filters.in @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\dighost.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\nslookup.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/bin/dig/win32/nslookup.vcxproj.in b/bin/dig/win32/nslookup.vcxproj.in new file mode 100644 index 0000000..565eb96 --- /dev/null +++ b/bin/dig/win32/nslookup.vcxproj.in @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|@PLATFORM@"> + <Configuration>Debug</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|@PLATFORM@"> + <Configuration>Release</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{C15A6E1A-94CE-4686-99F9-6BC5FD623EB5}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>nslookup</RootNamespace> + @WINDOWS_TARGET_PLATFORM_VERSION@ + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>MultiByte</CharacterSet> + @PLATFORM_TOOLSET@ + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + @PLATFORM_TOOLSET@ + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <TreatWarningAsError>false</TreatWarningAsError> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;USE_READLINE_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <BrowseInformation>true</BrowseInformation> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\include;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@READLINE_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\include;..\..\..\lib\irs\include;..\..\..\lib\irs\win32\include;..\..\..\lib\dns\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <AdditionalLibraryDirectories>..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\irs\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@READLINE_LIBD@@IDN_LIB@libisc.lib;libisccfg.lib;libirs.lib;libdns.lib;libbind9.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <ClCompile> + <WarningLevel>Level1</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;USE_READLINE_STATIC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <WholeProgramOptimization>false</WholeProgramOptimization> + <StringPooling>true</StringPooling> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\include;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@READLINE_INC@..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\isccfg\include;..\..\..\lib\irs\include;..\..\..\lib\irs\win32\include;..\..\..\lib\dns\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <AdditionalLibraryDirectories>..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\irs\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@READLINE_LIB@@IDN_LIB@libisc.lib;libisccfg.lib;libirs.lib;libdns.lib;libbind9.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\dighost.c" /> + <ClCompile Include="..\nslookup.c" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/bin/dig/win32/nslookup.vcxproj.user b/bin/dig/win32/nslookup.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/bin/dig/win32/nslookup.vcxproj.user @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +</Project>
\ No newline at end of file |