2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 18:19:42 +00:00
bind/bin/tools/mdig.c

2264 lines
56 KiB
C
Raw Normal View History

2015-02-05 07:56:05 +11:00
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
2015-02-05 07:56:05 +11:00
*
* 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.
2015-02-05 07:56:05 +11:00
*/
#include <inttypes.h>
#include <stdbool.h>
2015-02-05 07:56:05 +11:00
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <isc/app.h>
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
#include <isc/attributes.h>
2015-02-05 07:56:05 +11:00
#include <isc/base64.h>
#include <isc/hash.h>
#include <isc/hex.h>
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/net.h>
#include <isc/nonce.h>
2015-02-05 07:56:05 +11:00
#include <isc/parseint.h>
2015-02-05 13:25:38 +01:00
#include <isc/print.h>
#include <isc/random.h>
2015-02-05 07:56:05 +11:00
#include <isc/sockaddr.h>
#include <isc/socket.h>
#include <isc/string.h>
#include <isc/task.h>
#include <isc/timer.h>
#include <isc/util.h>
#include <dns/byaddr.h>
#include <dns/dispatch.h>
#include <dns/events.h>
2015-02-05 07:56:05 +11:00
#include <dns/fixedname.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
2015-02-05 07:56:05 +11:00
#include <dns/rdataset.h>
#include <dns/rdatatype.h>
#include <dns/request.h>
#include <dns/resolver.h>
#include <dns/result.h>
2015-02-05 07:56:05 +11:00
#include <dns/types.h>
#include <dns/view.h>
2015-02-05 07:56:05 +11:00
#include <dst/result.h>
2015-02-05 07:56:05 +11:00
#include <bind9/getaddresses.h>
#define CHECK(str, x) \
{ \
if ((x) != ISC_R_SUCCESS) { \
fprintf(stderr, "mdig: %s failed with %s\n", (str), \
isc_result_totext(x)); \
exit(-1); \
} \
}
2015-02-05 07:56:05 +11:00
#define RUNCHECK(x) RUNTIME_CHECK((x) == ISC_R_SUCCESS)
#define ADD_STRING(b, s) \
{ \
if (strlen(s) >= isc_buffer_availablelength(b)) \
return ((ISC_R_NOSPACE)); \
else \
isc_buffer_putstr(b, s); \
}
2015-02-05 07:56:05 +11:00
2020-02-13 14:44:37 -08:00
#define MXNAME (DNS_NAME_MAXTEXT + 1)
#define COMMSIZE 0xffff
#define OUTPUTBUF 32767
#define MAXPORT 0xffff
#define PORT 53
2015-02-05 07:56:05 +11:00
#define MAXTIMEOUT 0xffff
#define TCPTIMEOUT 10
#define UDPTIMEOUT 5
2020-02-13 14:44:37 -08:00
#define MAXTRIES 0xffffffff
2015-02-05 07:56:05 +11:00
#define NS_PER_US 1000 /*%< Nanoseconds per microsecond. */
#define US_PER_SEC 1000000 /*%< Microseconds per second. */
#define US_PER_MS 1000 /*%< Microseconds per millisecond. */
2020-02-13 14:44:37 -08:00
static isc_mem_t *mctx;
2015-02-05 07:56:05 +11:00
static dns_requestmgr_t *requestmgr;
2020-02-13 14:44:37 -08:00
static const char *batchname;
static FILE *batchfp;
static bool burst = false;
2020-02-13 14:44:37 -08:00
static bool have_ipv4 = false;
static bool have_ipv6 = false;
static bool have_src = false;
static bool tcp_mode = false;
static bool besteffort = true;
static bool display_short_form = false;
static bool display_headers = true;
static bool display_comments = true;
static int display_rrcomments = 0;
static bool display_ttlunits = true;
static bool display_ttl = true;
static bool display_class = true;
static bool display_crypto = true;
static bool display_multiline = false;
static bool display_question = true;
static bool display_answer = true;
static bool display_authority = true;
static bool display_additional = true;
static bool display_unknown_format = false;
static bool yaml = false;
static bool continue_on_error = false;
static uint32_t display_splitwidth = 0xffffffff;
static isc_sockaddr_t srcaddr;
static char *server;
static isc_sockaddr_t dstaddr;
static in_port_t port = 53;
static isc_dscp_t dscp = -1;
static unsigned char cookie_secret[33];
static int onfly = 0;
static char hexcookie[81];
2015-02-05 07:56:05 +11:00
struct query {
char textname[MXNAME]; /*% Name we're going to be
* looking up */
2020-02-13 14:44:37 -08:00
bool recurse;
bool have_aaonly;
bool have_adflag;
bool have_cdflag;
bool have_zflag;
bool dnssec;
bool expire;
bool send_cookie;
char *cookie;
bool nsid;
dns_rdatatype_t rdtype;
2015-02-05 07:56:05 +11:00
dns_rdataclass_t rdclass;
2020-02-13 14:44:37 -08:00
uint16_t udpsize;
int16_t edns;
dns_ednsopt_t *ednsopts;
unsigned int ednsoptscnt;
unsigned int ednsflags;
isc_sockaddr_t *ecs_addr;
unsigned int timeout;
unsigned int udptimeout;
unsigned int udpretries;
2015-02-05 07:56:05 +11:00
ISC_LINK(struct query) link;
};
static struct query default_query;
static ISC_LIST(struct query) queries;
#define EDNSOPTS 100U
/*% opcode text */
static const char *const opcodetext[] = {
"QUERY", "IQUERY", "STATUS", "RESERVED3",
"NOTIFY", "UPDATE", "RESERVED6", "RESERVED7",
"RESERVED8", "RESERVED9", "RESERVED10", "RESERVED11",
"RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15"
2015-02-05 07:56:05 +11:00
};
/*% return code text */
static const char *const rcodetext[] = {
"NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP",
"REFUSED", "YXDOMAIN", "YXRRSET", "NXRRSET", "NOTAUTH",
"NOTZONE", "RESERVED11", "RESERVED12", "RESERVED13", "RESERVED14",
"RESERVED15", "BADVERS"
2015-02-05 07:56:05 +11:00
};
/*% safe rcodetext[] */
static char *
2020-02-13 14:44:37 -08:00
rcode_totext(dns_rcode_t rcode) {
2015-02-05 07:56:05 +11:00
static char buf[sizeof("?65535")];
union {
const char *consttext;
2020-02-13 14:44:37 -08:00
char *deconsttext;
2015-02-05 07:56:05 +11:00
} totext;
if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) {
2015-02-05 07:56:05 +11:00
snprintf(buf, sizeof(buf), "?%u", rcode);
totext.deconsttext = buf;
} else {
2015-02-05 07:56:05 +11:00
totext.consttext = rcodetext[rcode];
}
return (totext.deconsttext);
2015-02-05 07:56:05 +11:00
}
/* receive response event handler */
static void
2020-02-13 14:44:37 -08:00
recvresponse(isc_task_t *task, isc_event_t *event) {
dns_requestevent_t *reqev = (dns_requestevent_t *)event;
isc_result_t result;
dns_message_t *query = NULL, *response = NULL;
unsigned int parseflags = 0;
isc_buffer_t *msgbuf = NULL, *buf = NULL;
unsigned int len = OUTPUTBUF;
dns_master_style_t *style = NULL;
unsigned int styleflags = 0;
2015-02-05 07:56:05 +11:00
dns_messagetextflag_t flags;
UNUSED(task);
REQUIRE(reqev != NULL);
query = reqev->ev_arg;
2015-02-05 07:56:05 +11:00
if (reqev->result != ISC_R_SUCCESS) {
fprintf(stderr, "response failed with %s\n",
isc_result_totext(reqev->result));
if (continue_on_error) {
goto cleanup;
} else {
exit(-1);
}
2015-02-05 07:56:05 +11:00
}
dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &response);
2015-02-05 07:56:05 +11:00
parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER;
if (besteffort) {
parseflags |= DNS_MESSAGEPARSE_BESTEFFORT;
parseflags |= DNS_MESSAGEPARSE_IGNORETRUNCATION;
}
2019-07-20 14:35:59 -04:00
msgbuf = dns_request_getanswer(reqev->request);
2015-02-05 07:56:05 +11:00
result = dns_request_getresponse(reqev->request, response, parseflags);
CHECK("dns_request_getresponse", result);
styleflags |= DNS_STYLEFLAG_REL_OWNER;
2019-07-20 14:35:59 -04:00
if (yaml) {
styleflags |= DNS_STYLEFLAG_YAML;
response->indent.string = " ";
response->indent.count = 3;
2019-07-20 14:35:59 -04:00
} else {
if (display_comments) {
styleflags |= DNS_STYLEFLAG_COMMENT;
}
if (display_unknown_format) {
styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT;
}
if (display_rrcomments > 0) {
styleflags |= DNS_STYLEFLAG_RRCOMMENT;
}
2019-07-20 14:35:59 -04:00
if (display_ttlunits) {
styleflags |= DNS_STYLEFLAG_TTL_UNITS;
}
if (!display_ttl) {
styleflags |= DNS_STYLEFLAG_NO_TTL;
}
if (!display_class) {
styleflags |= DNS_STYLEFLAG_NO_CLASS;
}
if (!display_crypto) {
styleflags |= DNS_STYLEFLAG_NOCRYPTO;
}
if (display_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;
styleflags |= DNS_STYLEFLAG_COMMENT;
/* Turn on rrcomments unless explicitly disabled */
if (display_rrcomments >= 0) {
styleflags |= DNS_STYLEFLAG_RRCOMMENT;
}
}
2015-02-05 07:56:05 +11:00
}
if (display_multiline || (!display_ttl && !display_class)) {
result = dns_master_stylecreate(&style, styleflags, 24, 24, 24,
32, 80, 8, display_splitwidth,
mctx);
} else if (!display_ttl || !display_class) {
result = dns_master_stylecreate(&style, styleflags, 24, 24, 32,
40, 80, 8, display_splitwidth,
mctx);
} else {
result = dns_master_stylecreate(&style, styleflags, 24, 32, 40,
48, 80, 8, display_splitwidth,
mctx);
}
2015-02-05 07:56:05 +11:00
CHECK("dns_master_stylecreate2", result);
flags = 0;
if (!display_headers) {
flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
}
if (!display_comments) {
2015-02-05 07:56:05 +11:00
flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
}
2015-02-05 07:56:05 +11:00
isc_buffer_allocate(mctx, &buf, len);
2015-02-05 07:56:05 +11:00
2019-07-20 14:35:59 -04:00
if (yaml) {
2020-02-13 14:44:37 -08:00
char sockstr[ISC_SOCKADDR_FORMATSIZE];
2019-07-20 14:35:59 -04:00
uint16_t sport;
2020-02-13 14:44:37 -08:00
char *hash;
int pf;
2019-07-20 14:35:59 -04:00
printf("-\n");
printf(" type: MESSAGE\n");
printf(" message:\n");
if (((response->flags & DNS_MESSAGEFLAG_RD) != 0) &&
2020-02-13 14:44:37 -08:00
((response->flags & DNS_MESSAGEFLAG_RA) != 0))
{
2019-07-20 14:35:59 -04:00
printf(" type: RECURSIVE_RESPONSE\n");
} else {
printf(" type: AUTH_RESPONSE\n");
}
printf(" message_size: %ub\n",
isc_buffer_usedlength(msgbuf));
pf = isc_sockaddr_pf(&dstaddr);
if (pf == PF_INET || pf == PF_INET6) {
printf(" socket_family: %s\n",
pf == PF_INET ? "INET" : "INET6");
printf(" socket_protocol: %s\n",
tcp_mode ? "TCP" : "UDP");
sport = isc_sockaddr_getport(&dstaddr);
isc_sockaddr_format(&dstaddr, sockstr, sizeof(sockstr));
hash = strchr(sockstr, '#');
if (hash != NULL) {
*hash = '\0';
}
printf(" response_address: %s\n", sockstr);
printf(" response_port: %u\n", sport);
}
if (have_src) {
sport = isc_sockaddr_getport(&srcaddr);
isc_sockaddr_format(&srcaddr, sockstr, sizeof(sockstr));
hash = strchr(sockstr, '#');
if (hash != NULL) {
*hash = '\0';
}
printf(" query_address: %s\n", sockstr);
printf(" query_port: %u\n", sport);
}
printf(" %s:\n", "response_message_data");
result = dns_message_headertotext(response, style, flags, buf);
CHECK("dns_message_headertotext", result);
2019-07-20 14:35:59 -04:00
} else if (display_comments && !display_short_form) {
2015-02-05 07:56:05 +11:00
printf(";; Got answer:\n");
2015-02-05 13:34:18 +01:00
2015-02-05 07:56:05 +11:00
if (display_headers) {
printf(";; ->>HEADER<<- opcode: %s, status: %s, "
"id: %u\n",
opcodetext[response->opcode],
rcode_totext(response->rcode), response->id);
2015-02-05 07:56:05 +11:00
printf(";; flags:");
if ((response->flags & DNS_MESSAGEFLAG_QR) != 0) {
2015-02-05 07:56:05 +11:00
printf(" qr");
}
if ((response->flags & DNS_MESSAGEFLAG_AA) != 0) {
2015-02-05 07:56:05 +11:00
printf(" aa");
}
if ((response->flags & DNS_MESSAGEFLAG_TC) != 0) {
2015-02-05 07:56:05 +11:00
printf(" tc");
}
if ((response->flags & DNS_MESSAGEFLAG_RD) != 0) {
2015-02-05 07:56:05 +11:00
printf(" rd");
}
if ((response->flags & DNS_MESSAGEFLAG_RA) != 0) {
2015-02-05 07:56:05 +11:00
printf(" ra");
}
if ((response->flags & DNS_MESSAGEFLAG_AD) != 0) {
2015-02-05 07:56:05 +11:00
printf(" ad");
}
if ((response->flags & DNS_MESSAGEFLAG_CD) != 0) {
2015-02-05 07:56:05 +11:00
printf(" cd");
}
if ((response->flags & 0x0040U) != 0) {
2015-02-05 07:56:05 +11:00
printf("; MBZ: 0x4");
}
2015-02-05 07:56:05 +11:00
printf("; QUERY: %u, ANSWER: %u, "
"AUTHORITY: %u, ADDITIONAL: %u\n",
response->counts[DNS_SECTION_QUESTION],
response->counts[DNS_SECTION_ANSWER],
response->counts[DNS_SECTION_AUTHORITY],
response->counts[DNS_SECTION_ADDITIONAL]);
if ((response->flags & DNS_MESSAGEFLAG_RD) != 0 &&
2020-02-13 14:44:37 -08:00
(response->flags & DNS_MESSAGEFLAG_RA) == 0)
{
2015-02-05 07:56:05 +11:00
printf(";; WARNING: recursion requested "
"but not available\n");
}
2015-02-05 07:56:05 +11:00
}
}
repopulate_buffer:
if (display_comments && display_headers && !display_short_form) {
result = dns_message_pseudosectiontotext(
response, DNS_PSEUDOSECTION_OPT, style, flags, buf);
2015-02-05 07:56:05 +11:00
if (result == ISC_R_NOSPACE) {
buftoosmall:
2015-02-05 07:56:05 +11:00
len += OUTPUTBUF;
isc_buffer_free(&buf);
isc_buffer_allocate(mctx, &buf, len);
goto repopulate_buffer;
2015-02-05 07:56:05 +11:00
}
CHECK("dns_message_pseudosectiontotext", result);
}
if (display_question && display_headers && !display_short_form) {
result = dns_message_sectiontotext(
response, DNS_SECTION_QUESTION, style, flags, buf);
if (result == ISC_R_NOSPACE) {
2015-02-05 07:56:05 +11:00
goto buftoosmall;
}
2015-02-05 07:56:05 +11:00
CHECK("dns_message_sectiontotext", result);
}
if (display_answer && !display_short_form) {
result = dns_message_sectiontotext(response, DNS_SECTION_ANSWER,
2015-02-05 07:56:05 +11:00
style, flags, buf);
if (result == ISC_R_NOSPACE) {
2015-02-05 07:56:05 +11:00
goto buftoosmall;
}
2015-02-05 07:56:05 +11:00
CHECK("dns_message_sectiontotext", result);
} else if (display_answer) {
2020-02-13 14:44:37 -08:00
dns_name_t *name;
2015-02-05 07:56:05 +11:00
dns_rdataset_t *rdataset;
2020-02-13 14:44:37 -08:00
isc_result_t loopresult;
dns_name_t empty_name;
dns_rdata_t rdata = DNS_RDATA_INIT;
unsigned int answerstyleflags = 0;
2015-02-05 07:56:05 +11:00
if (!display_crypto) {
2015-02-05 07:56:05 +11:00
answerstyleflags |= DNS_STYLEFLAG_NOCRYPTO;
}
if (display_unknown_format) {
answerstyleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT;
}
2015-02-05 07:56:05 +11:00
dns_name_init(&empty_name, NULL);
result = dns_message_firstname(response, DNS_SECTION_ANSWER);
if (result != ISC_R_NOMORE) {
2015-02-05 07:56:05 +11:00
CHECK("dns_message_firstname", result);
}
2015-02-05 07:56:05 +11:00
for (;;) {
if (result == ISC_R_NOMORE) {
2015-02-05 07:56:05 +11:00
break;
}
2015-02-05 07:56:05 +11:00
CHECK("dns_message_nextname", result);
name = NULL;
dns_message_currentname(response, DNS_SECTION_ANSWER,
2015-02-05 07:56:05 +11:00
&name);
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
2020-02-13 14:44:37 -08:00
rdataset = ISC_LIST_NEXT(rdataset, link))
{
2015-02-05 07:56:05 +11:00
loopresult = dns_rdataset_first(rdataset);
while (loopresult == ISC_R_SUCCESS) {
dns_rdataset_current(rdataset, &rdata);
result = dns_rdata_tofmttext(
&rdata, NULL, answerstyleflags,
0, 60, " ", buf);
if (result == ISC_R_NOSPACE) {
2015-02-05 07:56:05 +11:00
goto buftoosmall;
}
2015-02-05 07:56:05 +11:00
CHECK("dns_rdata_tofmttext", result);
loopresult =
dns_rdataset_next(rdataset);
2015-02-05 07:56:05 +11:00
dns_rdata_reset(&rdata);
if (strlen("\n") >=
isc_buffer_availablelength(buf)) {
2015-02-05 07:56:05 +11:00
goto buftoosmall;
}
2015-02-05 07:56:05 +11:00
isc_buffer_putstr(buf, "\n");
}
}
result = dns_message_nextname(response,
DNS_SECTION_ANSWER);
}
}
if (display_authority && !display_short_form) {
result = dns_message_sectiontotext(
response, DNS_SECTION_AUTHORITY, style, flags, buf);
if (result == ISC_R_NOSPACE) {
2015-02-05 07:56:05 +11:00
goto buftoosmall;
}
2015-02-05 07:56:05 +11:00
CHECK("dns_message_sectiontotext", result);
}
if (display_additional && !display_short_form) {
result = dns_message_sectiontotext(
response, DNS_SECTION_ADDITIONAL, style, flags, buf);
if (result == ISC_R_NOSPACE) {
2015-02-05 07:56:05 +11:00
goto buftoosmall;
}
2015-02-05 07:56:05 +11:00
CHECK("dns_message_sectiontotext", result);
}
if (display_additional && !display_short_form && display_headers) {
/*
* Only print the signature on the first record.
*/
result = dns_message_pseudosectiontotext(
response, DNS_PSEUDOSECTION_TSIG, style, flags, buf);
if (result == ISC_R_NOSPACE) {
2015-02-05 07:56:05 +11:00
goto buftoosmall;
}
2015-02-05 07:56:05 +11:00
CHECK("dns_message_pseudosectiontotext", result);
result = dns_message_pseudosectiontotext(
response, DNS_PSEUDOSECTION_SIG0, style, flags, buf);
if (result == ISC_R_NOSPACE) {
2015-02-05 07:56:05 +11:00
goto buftoosmall;
}
2015-02-05 07:56:05 +11:00
CHECK("dns_message_pseudosectiontotext", result);
}
2020-02-13 14:44:37 -08:00
if (display_headers && display_comments && !display_short_form && !yaml)
{
2015-02-05 07:56:05 +11:00
printf("\n");
2019-07-20 14:35:59 -04:00
}
2015-02-05 07:56:05 +11:00
printf("%.*s", (int)isc_buffer_usedlength(buf),
(char *)isc_buffer_base(buf));
isc_buffer_free(&buf);
cleanup:
fflush(stdout);
if (style != NULL) {
2015-02-05 07:56:05 +11:00
dns_master_styledestroy(&style, mctx);
}
if (query != NULL) {
dns_message_detach(&query);
}
if (response != NULL) {
dns_message_detach(&response);
}
2015-02-05 07:56:05 +11:00
dns_request_destroy(&reqev->request);
isc_event_free(&event);
if (--onfly == 0) {
2015-02-05 07:56:05 +11:00
isc_app_shutdown();
}
2015-02-05 07:56:05 +11:00
return;
}
/*%
* 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)
2015-02-05 07:56:05 +11:00
*/
static void
add_opt(dns_message_t *msg, uint16_t udpsize, uint16_t edns, unsigned int flags,
2020-02-13 14:44:37 -08:00
dns_ednsopt_t *opts, size_t count) {
2015-02-05 07:56:05 +11:00
dns_rdataset_t *rdataset = NULL;
2020-02-13 14:44:37 -08:00
isc_result_t result;
2015-02-05 07:56:05 +11:00
result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags,
2018-03-19 17:31:53 +00:00
opts, count);
2015-02-05 07:56:05 +11:00
CHECK("dns_message_buildopt", result);
result = dns_message_setopt(msg, rdataset);
CHECK("dns_message_setopt", result);
}
static void
2020-02-13 14:44:37 -08:00
compute_cookie(unsigned char *cookie, size_t len) {
2015-02-05 07:56:05 +11:00
/* XXXMPA need to fix, should be per server. */
INSIST(len >= 8U);
memmove(cookie, cookie_secret, 8);
}
static isc_result_t
2020-02-13 14:44:37 -08:00
sendquery(struct query *query, isc_task_t *task) {
dns_request_t *request;
dns_message_t *message;
dns_name_t *qname;
2015-02-05 07:56:05 +11:00
dns_rdataset_t *qrdataset;
2020-02-13 14:44:37 -08:00
isc_result_t result;
2015-02-05 07:56:05 +11:00
dns_fixedname_t queryname;
2020-02-13 14:44:37 -08:00
isc_buffer_t buf;
unsigned int options;
2015-02-05 07:56:05 +11:00
onfly++;
dns_fixedname_init(&queryname);
isc_buffer_init(&buf, query->textname, strlen(query->textname));
isc_buffer_add(&buf, strlen(query->textname));
result = dns_name_fromtext(dns_fixedname_name(&queryname), &buf,
dns_rootname, 0, NULL);
CHECK("dns_name_fromtext", result);
message = NULL;
dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &message);
2015-02-05 07:56:05 +11:00
message->opcode = dns_opcode_query;
if (query->recurse) {
2015-02-05 07:56:05 +11:00
message->flags |= DNS_MESSAGEFLAG_RD;
}
if (query->have_aaonly) {
2015-02-05 07:56:05 +11:00
message->flags |= DNS_MESSAGEFLAG_AA;
}
if (query->have_adflag) {
2015-02-05 07:56:05 +11:00
message->flags |= DNS_MESSAGEFLAG_AD;
}
if (query->have_cdflag) {
2015-02-05 07:56:05 +11:00
message->flags |= DNS_MESSAGEFLAG_CD;
}
if (query->have_zflag) {
2015-02-05 07:56:05 +11:00
message->flags |= 0x0040U;
}
2015-02-05 07:56:05 +11:00
message->rdclass = query->rdclass;
message->id = (unsigned short)(random() & 0xFFFF);
qname = NULL;
result = dns_message_gettempname(message, &qname);
CHECK("dns_message_gettempname", result);
qrdataset = NULL;
result = dns_message_gettemprdataset(message, &qrdataset);
CHECK("dns_message_gettemprdataset", result);
dns_name_init(qname, NULL);
dns_name_clone(dns_fixedname_name(&queryname), qname);
dns_rdataset_makequestion(qrdataset, query->rdclass, query->rdtype);
2015-02-05 07:56:05 +11:00
ISC_LIST_APPEND(qname->list, qrdataset, link);
dns_message_addname(message, qname, DNS_SECTION_QUESTION);
if (query->udpsize > 0 || query->dnssec || query->edns > -1 ||
2020-02-13 14:44:37 -08:00
query->ecs_addr != NULL)
{
2015-02-05 07:56:05 +11:00
dns_ednsopt_t opts[EDNSOPTS + DNS_EDNSOPTIONS];
2020-02-13 14:44:37 -08:00
unsigned int flags;
int i = 0;
char ecsbuf[20];
unsigned char cookie[40];
2015-02-05 07:56:05 +11:00
if (query->udpsize == 0) {
query->udpsize = 1232;
}
if (query->edns < 0) {
2015-02-05 07:56:05 +11:00
query->edns = 0;
}
2015-02-05 07:56:05 +11:00
if (query->nsid) {
INSIST(i < DNS_EDNSOPTIONS);
opts[i].code = DNS_OPT_NSID;
opts[i].length = 0;
opts[i].value = NULL;
i++;
}
if (query->ecs_addr != NULL) {
2020-02-13 14:44:37 -08:00
uint8_t addr[16], family;
uint32_t plen;
struct sockaddr *sa;
struct sockaddr_in *sin;
2015-02-05 07:56:05 +11:00
struct sockaddr_in6 *sin6;
2020-02-13 14:44:37 -08:00
size_t addrl;
isc_buffer_t b;
2015-02-05 07:56:05 +11:00
sa = &query->ecs_addr->type.sa;
plen = query->ecs_addr->length;
2015-02-05 07:56:05 +11:00
/* Round up prefix len to a multiple of 8 */
addrl = (plen + 7) / 8;
2015-02-05 07:56:05 +11:00
INSIST(i < DNS_EDNSOPTIONS);
opts[i].code = DNS_OPT_CLIENT_SUBNET;
opts[i].length = (uint16_t)addrl + 4;
2015-02-05 07:56:05 +11:00
CHECK("isc_buffer_allocate", result);
isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf));
if (sa->sa_family == AF_INET) {
family = 1;
sin = (struct sockaddr_in *)sa;
memmove(addr, &sin->sin_addr, 4);
if ((plen % 8) != 0) {
addr[addrl - 1] &= ~0U
<< (8 - (plen % 8));
}
2015-02-05 07:56:05 +11:00
} else {
family = 2;
sin6 = (struct sockaddr_in6 *)sa;
memmove(addr, &sin6->sin6_addr, 16);
2015-02-05 07:56:05 +11:00
}
/* Mask off last address byte */
if (addrl > 0 && (plen % 8) != 0) {
2016-07-13 13:54:12 +05:30
addr[addrl - 1] &= ~0U << (8 - (plen % 8));
}
/* 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) {
isc_buffer_putmem(&b, addr, (unsigned)addrl);
}
opts[i].value = (uint8_t *)ecsbuf;
2015-02-05 07:56:05 +11:00
i++;
}
if (query->send_cookie) {
2015-02-05 07:56:05 +11:00
INSIST(i < DNS_EDNSOPTIONS);
opts[i].code = DNS_OPT_COOKIE;
if (query->cookie != NULL) {
2015-02-05 07:56:05 +11:00
isc_buffer_t b;
isc_buffer_init(&b, cookie, sizeof(cookie));
2020-02-13 14:44:37 -08:00
result = isc_hex_decodestring(query->cookie,
&b);
2015-02-05 07:56:05 +11:00
CHECK("isc_hex_decodestring", result);
opts[i].value = isc_buffer_base(&b);
opts[i].length = isc_buffer_usedlength(&b);
} else {
compute_cookie(cookie, 8);
2015-02-05 07:56:05 +11:00
opts[i].length = 8;
opts[i].value = cookie;
}
i++;
}
if (query->expire) {
INSIST(i < DNS_EDNSOPTIONS);
opts[i].code = DNS_OPT_EXPIRE;
opts[i].length = 0;
opts[i].value = NULL;
i++;
}
if (query->ednsoptscnt != 0) {
memmove(&opts[i], query->ednsopts,
sizeof(dns_ednsopt_t) * query->ednsoptscnt);
i += query->ednsoptscnt;
}
flags = query->ednsflags;
flags &= ~DNS_MESSAGEEXTFLAG_DO;
if (query->dnssec) {
2015-02-05 07:56:05 +11:00
flags |= DNS_MESSAGEEXTFLAG_DO;
}
2015-02-05 07:56:05 +11:00
add_opt(message, query->udpsize, query->edns, flags, opts, i);
}
options = 0;
if (tcp_mode) {
2015-02-05 07:56:05 +11:00
options |= DNS_REQUESTOPT_TCP | DNS_REQUESTOPT_SHARE;
}
2015-02-05 07:56:05 +11:00
request = NULL;
result = dns_request_createvia(
requestmgr, message, have_src ? &srcaddr : NULL, &dstaddr, dscp,
options, NULL, query->timeout, query->udptimeout,
query->udpretries, task, recvresponse, message, &request);
2015-02-05 07:56:05 +11:00
CHECK("dns_request_createvia4", result);
return (ISC_R_SUCCESS);
2015-02-05 07:56:05 +11:00
}
static void
2020-02-13 14:44:37 -08:00
sendqueries(isc_task_t *task, isc_event_t *event) {
2015-02-05 07:56:05 +11:00
struct query *query = (struct query *)event->ev_arg;
isc_event_free(&event);
while (query != NULL) {
struct query *next = ISC_LIST_NEXT(query, link);
sendquery(query, task);
query = next;
}
if (onfly == 0) {
2015-02-05 07:56:05 +11:00
isc_app_shutdown();
}
2015-02-05 07:56:05 +11:00
return;
}
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
ISC_NORETURN static void
usage(void);
2015-02-05 07:56:05 +11:00
static void
2020-02-13 14:44:37 -08:00
usage(void) {
2020-04-02 18:51:06 -07:00
fprintf(stderr, "Usage: mdig @server {global-opt} host\n"
" {local-opt} [ host {local-opt} [...]]\n"
"\nUse \"mdig -h\" (or \"mdig -h | more\") "
"for complete list of options\n");
2015-02-05 07:56:05 +11:00
exit(1);
}
/*% help */
static void
2020-02-13 14:44:37 -08:00
help(void) {
2020-04-02 18:51:06 -07:00
printf("Usage: mdig @server {global-opt} host\n"
" {local-opt} [ host {local-opt} [...]]\n"
"Where:\n"
" anywhere opt is one of:\n"
" -f filename (batch mode)\n"
" -h (print help and exit)\n"
" -v (print version and exit)\n"
" global 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"
" -p port (specify port number)\n"
" -m (enable memory usage "
"debugging)\n"
" +[no]dscp[=###] (Set the DSCP value to "
"### "
"[0..63])\n"
" +[no]vc (TCP mode)\n"
" +[no]tcp (TCP mode, alternate "
"syntax)\n"
" +[no]besteffort (Try to parse even "
"illegal "
"messages)\n"
" +[no]cl (Control display of class "
"in records)\n"
" +[no]comments (Control display of "
"comment lines)\n"
" +[no]rrcomments (Control display of "
"per-record "
"comments)\n"
" +[no]crypto (Control display of "
"cryptographic "
"fields in records)\n"
" +[no]question (Control display of "
"question)\n"
" +[no]answer (Control display of "
"answer)\n"
" +[no]authority (Control display of "
"authority)\n"
" +[no]additional (Control display of "
"additional)\n"
" +[no]short (Disable everything "
"except "
"short\n"
" form of answer)\n"
" +[no]ttlid (Control display of ttls "
"in records)\n"
" +[no]ttlunits (Display TTLs in "
"human-readable units)\n"
" +[no]unknownformat (Print RDATA in RFC 3597 "
"\"unknown\" format)\n"
" +[no]all (Set or clear all display "
"flags)\n"
" +[no]multiline (Print records in an "
"expanded format)\n"
" +[no]split=## (Split hex/base64 fields "
"into chunks)\n"
" local opt is one of:\n"
" -c class (specify query class)\n"
" -t type (specify query type)\n"
" -x dot-notation (shortcut for reverse "
"lookups)\n"
" +timeout=### (Set query timeout) "
"[UDP=5,TCP=10]\n"
" +udptimeout=### (Set timeout before UDP "
"retry)\n"
" +tries=### (Set number of UDP "
"attempts) [3]\n"
" +retry=### (Set number of UDP "
"retries) [2]\n"
" +bufsize=### (Set EDNS0 Max UDP packet "
"size)\n"
" +subnet=addr (Set edns-client-subnet "
"option)\n"
" +[no]edns[=###] (Set EDNS version) [0]\n"
" +ednsflags=### (Set EDNS flag bits)\n"
" +ednsopt=###[:value] (Send specified EDNS "
"option)\n"
" +noednsopt (Clear list of +ednsopt "
"options)\n"
" +[no]recurse (Recursive mode)\n"
" +[no]aaonly (Set AA flag in query "
"(+[no]aaflag))\n"
" +[no]adflag (Set AD flag in query)\n"
" +[no]cdflag (Set CD flag in query)\n"
" +[no]zflag (Set Z flag in query)\n"
" +[no]dnssec (Request DNSSEC records)\n"
" +[no]expire (Request time to expire)\n"
" +[no]cookie[=###] (Send a COOKIE option)\n"
" +[no]nsid (Request Name "
"Server ID)\n");
2015-02-05 07:56:05 +11:00
}
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
ISC_NORETURN static void
fatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
2015-02-05 07:56:05 +11:00
static void
2020-02-13 14:44:37 -08:00
fatal(const char *format, ...) {
2015-02-05 07:56:05 +11:00
va_list args;
fflush(stdout);
fprintf(stderr, "mdig: ");
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
fprintf(stderr, "\n");
exit(-2);
}
static isc_result_t
parse_uint_helper(uint32_t *uip, const char *value, uint32_t max,
2020-02-13 14:44:37 -08:00
const char *desc, int base) {
uint32_t n;
2015-02-05 07:56:05 +11:00
isc_result_t result = isc_parse_uint32(&n, value, base);
if (result == ISC_R_SUCCESS && n > max) {
2015-02-05 07:56:05 +11:00
result = ISC_R_RANGE;
}
2015-02-05 07:56:05 +11:00
if (result != ISC_R_SUCCESS) {
printf("invalid %s '%s': %s\n", desc, value,
isc_result_totext(result));
2015-02-05 07:56:05 +11:00
return (result);
}
*uip = n;
return (ISC_R_SUCCESS);
}
static isc_result_t
2020-02-13 14:44:37 -08:00
parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc) {
2015-02-05 07:56:05 +11:00
return (parse_uint_helper(uip, value, max, desc, 10));
}
static isc_result_t
2020-02-13 14:44:37 -08:00
parse_xint(uint32_t *uip, const char *value, uint32_t max, const char *desc) {
2015-02-05 07:56:05 +11:00
return (parse_uint_helper(uip, value, max, desc, 0));
}
static void
2020-02-13 14:44:37 -08:00
newopts(struct query *query) {
2018-08-23 10:19:43 +02:00
size_t len = sizeof(query->ednsopts[0]) * EDNSOPTS;
size_t i;
query->ednsopts = isc_mem_allocate(mctx, len);
for (i = 0; i < EDNSOPTS; i++) {
query->ednsopts[i].code = 0;
query->ednsopts[i].length = 0;
query->ednsopts[i].value = NULL;
}
}
2015-02-05 07:56:05 +11:00
static void
2020-02-13 14:44:37 -08:00
save_opt(struct query *query, char *code, char *value) {
uint32_t num;
2015-02-05 07:56:05 +11:00
isc_buffer_t b;
isc_result_t result;
if (query->ednsopts == NULL) {
newopts(query);
}
if (query->ednsoptscnt == EDNSOPTS) {
2015-02-05 07:56:05 +11:00
fatal("too many ednsopts");
}
2015-02-05 07:56:05 +11:00
result = parse_uint(&num, code, 65535, "ednsopt");
if (result != ISC_R_SUCCESS) {
2015-02-05 07:56:05 +11:00
fatal("bad edns code point: %s", code);
}
2015-02-05 07:56:05 +11:00
query->ednsopts[query->ednsoptscnt].code = num;
query->ednsopts[query->ednsoptscnt].length = 0;
query->ednsopts[query->ednsoptscnt].value = NULL;
2015-02-05 07:56:05 +11:00
if (value != NULL) {
char *buf;
buf = isc_mem_allocate(mctx, strlen(value) / 2 + 1);
isc_buffer_init(&b, buf, strlen(value) / 2 + 1);
2015-02-05 07:56:05 +11:00
result = isc_hex_decodestring(value, &b);
CHECK("isc_hex_decodestring", result);
query->ednsopts[query->ednsoptscnt].value = isc_buffer_base(&b);
query->ednsopts[query->ednsoptscnt].length =
isc_buffer_usedlength(&b);
2015-02-05 07:56:05 +11:00
}
query->ednsoptscnt++;
}
static isc_result_t
2020-02-13 14:44:37 -08:00
parse_netprefix(isc_sockaddr_t **sap, const char *value) {
2015-02-05 07:56:05 +11:00
isc_sockaddr_t *sa = NULL;
2020-02-13 14:44:37 -08:00
struct in_addr in4;
2015-02-05 07:56:05 +11:00
struct in6_addr in6;
2020-02-13 14:44:37 -08:00
uint32_t netmask = 0xffffffff;
char *slash = NULL;
bool parsed = false;
char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX/128")];
2015-02-05 07:56:05 +11:00
if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) {
fatal("invalid prefix '%s'\n", value);
}
slash = strchr(buf, '/');
if (slash != NULL) {
2019-08-08 13:52:44 +10:00
isc_result_t result;
*slash = '\0';
2015-02-05 07:56:05 +11:00
result = isc_parse_uint32(&netmask, slash + 1, 10);
if (result != ISC_R_SUCCESS) {
fatal("invalid prefix length in '%s': %s\n", value,
isc_result_totext(result));
2015-02-05 07:56:05 +11:00
}
} else if (strcmp(value, "0") == 0) {
netmask = 0;
2015-02-05 07:56:05 +11:00
}
sa = isc_mem_allocate(mctx, sizeof(*sa));
if (inet_pton(AF_INET6, buf, &in6) == 1) {
parsed = true;
isc_sockaddr_fromin6(sa, &in6, 0);
if (netmask > 128) {
2015-02-05 07:56:05 +11:00
netmask = 128;
}
} else if (inet_pton(AF_INET, buf, &in4) == 1) {
parsed = true;
2015-02-05 07:56:05 +11:00
isc_sockaddr_fromin(sa, &in4, 0);
if (netmask > 32) {
2015-02-05 07:56:05 +11:00
netmask = 32;
}
} else if (netmask != 0xffffffff) {
2015-02-05 07:56:05 +11:00
int i;
for (i = 0; i < 3 && strlen(buf) < sizeof(buf) - 2; i++) {
2015-02-05 07:56:05 +11:00
strlcat(buf, ".0", sizeof(buf));
2015-02-05 13:34:18 +01:00
if (inet_pton(AF_INET, buf, &in4) == 1) {
parsed = true;
2015-02-05 07:56:05 +11:00
isc_sockaddr_fromin(sa, &in4, 0);
break;
}
}
if (netmask > 32) {
netmask = 32;
}
2015-02-05 07:56:05 +11:00
}
if (!parsed) {
2015-02-05 07:56:05 +11:00
fatal("invalid address '%s'", value);
}
2015-02-05 07:56:05 +11:00
sa->length = netmask;
*sap = sa;
return (ISC_R_SUCCESS);
}
/*%
* Append 'len' bytes of 'text' at '*p', failing with
* ISC_R_NOSPACE if that would advance p past 'end'.
*/
static isc_result_t
2020-02-13 14:44:37 -08:00
append(const char *text, int len, char **p, char *end) {
if (len > end - *p) {
2015-02-05 07:56:05 +11:00
return (ISC_R_NOSPACE);
}
2015-02-05 07:56:05 +11:00
memmove(*p, text, len);
*p += len;
return (ISC_R_SUCCESS);
}
static isc_result_t
2020-02-13 14:44:37 -08:00
reverse_octets(const char *in, char **p, char *end) {
const char *dot = strchr(in, '.');
2020-02-13 14:44:37 -08:00
int len;
2015-02-05 07:56:05 +11:00
if (dot != NULL) {
isc_result_t result;
result = reverse_octets(dot + 1, p, end);
CHECK("reverse_octets", result);
result = append(".", 1, p, end);
CHECK("append", result);
len = (int)(dot - in);
} else {
len = strlen(in);
}
return (append(in, len, p, end));
}
static void
2020-02-13 14:44:37 -08:00
get_reverse(char *reverse, size_t len, const char *value) {
int r;
isc_result_t result;
2015-02-05 07:56:05 +11:00
isc_netaddr_t addr;
addr.family = AF_INET6;
2015-02-05 13:34:18 +01:00
r = inet_pton(AF_INET6, value, &addr.type.in6);
2015-02-05 07:56:05 +11:00
if (r > 0) {
/* This is a valid IPv6 address. */
dns_fixedname_t fname;
2020-02-13 14:44:37 -08:00
dns_name_t *name;
unsigned int options = 0;
2015-02-05 07:56:05 +11:00
name = dns_fixedname_initname(&fname);
result = dns_byaddr_createptrname(&addr, options, name);
2015-02-05 07:56:05 +11:00
CHECK("dns_byaddr_createptrname2", result);
dns_name_format(name, reverse, (unsigned int)len);
return;
} else {
/*
* Not a valid IPv6 address. Assume IPv4.
* 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;
result = reverse_octets(value, &p, end);
CHECK("reverse_octets", result);
/* Append .in-addr.arpa. and a terminating NUL. */
result = append(".in-addr.arpa.", 15, &p, end);
CHECK("append", result);
return;
}
}
/*%
* We're not using isc_commandline_parse() here since the command line
* syntax of mdig is quite a bit different from that which can be described
* by that routine.
* XXX doc options
*/
static void
2020-02-13 14:44:37 -08:00
plus_option(char *option, struct query *query, bool global) {
2015-02-05 07:56:05 +11:00
isc_result_t result;
2020-02-13 14:44:37 -08:00
char *cmd, *value, *last = NULL, *code;
uint32_t num;
bool state = true;
size_t n;
2015-02-05 07:56:05 +11:00
INSIST(option != NULL);
if ((cmd = strtok_r(option, "=", &last)) == NULL) {
printf(";; Invalid option %s\n", option);
2015-02-05 07:56:05 +11:00
return;
}
if (strncasecmp(cmd, "no", 2) == 0) {
cmd += 2;
state = false;
2015-02-05 07:56:05 +11:00
}
/* parse the rest of the string */
value = strtok_r(NULL, "", &last);
2015-02-05 07:56:05 +11:00
#define FULLCHECK(A) \
do { \
size_t _l = strlen(cmd); \
2015-02-05 07:56:05 +11:00
if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
goto invalid_option; \
2015-02-05 07:56:05 +11:00
} while (0)
#define FULLCHECK2(A, B) \
do { \
size_t _l = strlen(cmd); \
2015-02-05 07:56:05 +11:00
if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
(_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
goto invalid_option; \
2015-02-05 07:56:05 +11:00
} while (0)
#define GLOBAL() \
do { \
if (!global) \
2015-02-05 07:56:05 +11:00
goto global_option; \
} while (0)
switch (cmd[0]) {
case 'a':
switch (cmd[1]) {
case 'a': /* aaonly / aaflag */
FULLCHECK2("aaonly", "aaflag");
query->have_aaonly = state;
break;
case 'd':
switch (cmd[2]) {
case 'd': /* additional */
FULLCHECK("additional");
display_additional = state;
break;
case 'f': /* adflag */
2015-02-05 07:56:05 +11:00
case '\0': /* +ad is a synonym for +adflag */
FULLCHECK("adflag");
query->have_adflag = state;
break;
default:
goto invalid_option;
}
break;
case 'l': /* all */
FULLCHECK("all");
GLOBAL();
display_question = state;
display_answer = state;
display_authority = state;
display_additional = state;
display_comments = state;
display_rrcomments = state ? 1 : -1;
2015-02-05 07:56:05 +11:00
break;
case 'n': /* answer */
FULLCHECK("answer");
GLOBAL();
display_answer = state;
break;
case 'u': /* authority */
FULLCHECK("authority");
GLOBAL();
display_authority = state;
break;
default:
goto invalid_option;
}
break;
case 'b':
switch (cmd[1]) {
case 'e': /* besteffort */
2015-02-05 07:56:05 +11:00
FULLCHECK("besteffort");
GLOBAL();
besteffort = state;
break;
case 'u':
switch (cmd[2]) {
case 'f': /* bufsize */
FULLCHECK("bufsize");
if (value == NULL) {
goto need_value;
}
if (!state) {
goto invalid_option;
}
result = parse_uint(&num, value, COMMSIZE,
"buffer size");
CHECK("parse_uint(buffer size)", result);
query->udpsize = num;
break;
case 'r': /* burst */
FULLCHECK("burst");
GLOBAL();
burst = state;
break;
default:
2015-02-05 07:56:05 +11:00
goto invalid_option;
}
2015-02-05 07:56:05 +11:00
break;
default:
goto invalid_option;
}
break;
case 'c':
switch (cmd[1]) {
case 'd': /* cdflag */
2015-02-05 07:56:05 +11:00
switch (cmd[2]) {
case 'f': /* cdflag */
2015-02-05 07:56:05 +11:00
case '\0': /* +cd is a synonym for +cdflag */
FULLCHECK("cdflag");
query->have_cdflag = state;
break;
default:
goto invalid_option;
}
break;
case 'l': /* cl */
FULLCHECK("cl");
GLOBAL();
display_class = state;
break;
case 'o': /* comments */
switch (cmd[2]) {
case 'm':
FULLCHECK("comments");
GLOBAL();
display_comments = state;
break;
case 'n':
FULLCHECK("continue");
GLOBAL();
continue_on_error = state;
break;
case 'o':
FULLCHECK("cookie");
if (state && query->edns == -1) {
query->edns = 0;
}
query->send_cookie = state;
if (value != NULL) {
n = strlcpy(hexcookie, value,
sizeof(hexcookie));
if (n >= sizeof(hexcookie)) {
fatal("COOKIE data too large");
}
query->cookie = hexcookie;
} else {
query->cookie = NULL;
}
break;
default:
goto invalid_option;
}
2015-02-05 07:56:05 +11:00
break;
case 'r':
FULLCHECK("crypto");
GLOBAL();
display_crypto = state;
break;
default:
goto invalid_option;
}
break;
case 'd':
switch (cmd[1]) {
case 'n': /* dnssec */
FULLCHECK("dnssec");
if (state && query->edns == -1) {
2015-02-05 07:56:05 +11:00
query->edns = 0;
}
2015-02-05 07:56:05 +11:00
query->dnssec = state;
break;
case 's': /* dscp */
FULLCHECK("dscp");
GLOBAL();
if (!state) {
dscp = -1;
break;
}
if (value == NULL) {
2015-02-05 07:56:05 +11:00
goto need_value;
}
2015-02-05 07:56:05 +11:00
result = parse_uint(&num, value, 0x3f, "DSCP");
CHECK("parse_uint(DSCP)", result);
dscp = num;
break;
default:
goto invalid_option;
}
break;
case 'e':
switch (cmd[1]) {
case 'd':
switch (cmd[2]) {
2015-02-05 07:56:05 +11:00
case 'n':
switch (cmd[3]) {
case 's':
switch (cmd[4]) {
case 0:
FULLCHECK("edns");
if (!state) {
query->edns = -1;
break;
}
if (value == NULL) {
query->edns = 0;
break;
}
result = parse_uint(&num, value,
2015-02-05 07:56:05 +11:00
255,
"edns");
CHECK("parse_uint(edns)",
result);
query->edns = num;
break;
case 'f':
FULLCHECK("ednsflags");
if (!state) {
query->ednsflags = 0;
break;
}
if (value == NULL) {
query->ednsflags = 0;
break;
}
result = parse_xint(
&num, value, 0xffff,
"ednsflags");
2015-02-05 07:56:05 +11:00
CHECK("parse_xint(ednsflags)",
result);
query->ednsflags = num;
break;
case 'o':
FULLCHECK("ednsopt");
if (!state) {
query->ednsoptscnt = 0;
break;
}
code = NULL;
if (value != NULL) {
code = strtok_r(value,
":",
&last);
}
if (code == NULL) {
2015-02-05 07:56:05 +11:00
fatal("ednsopt no "
"code point "
"specified");
}
value = strtok_r(NULL, "\0",
2018-08-23 10:19:43 +02:00
&last);
2015-02-05 07:56:05 +11:00
save_opt(query, code, value);
break;
default:
goto invalid_option;
}
break;
default:
goto invalid_option;
}
break;
default:
goto invalid_option;
}
break;
case 'x':
FULLCHECK("expire");
query->expire = state;
break;
default:
goto invalid_option;
}
break;
case 'm': /* multiline */
FULLCHECK("multiline");
GLOBAL();
display_multiline = state;
break;
case 'n':
FULLCHECK("nsid");
if (state && query->edns == -1) {
2015-02-05 07:56:05 +11:00
query->edns = 0;
}
2015-02-05 07:56:05 +11:00
query->nsid = state;
break;
case 'q':
FULLCHECK("question");
GLOBAL();
display_question = state;
break;
case 'r':
switch (cmd[1]) {
case 'e':
switch (cmd[2]) {
case 'c': /* recurse */
FULLCHECK("recurse");
query->recurse = state;
break;
case 't': /* retry / retries */
FULLCHECK2("retry", "retries");
if (value == NULL) {
2015-02-05 07:56:05 +11:00
goto need_value;
}
if (!state) {
2015-02-05 07:56:05 +11:00
goto invalid_option;
}
result = parse_uint(&query->udpretries, value,
MAXTRIES - 1, "udpretries");
2015-02-05 07:56:05 +11:00
CHECK("parse_uint(udpretries)", result);
query->udpretries++;
break;
default:
goto invalid_option;
}
break;
case 'r':
FULLCHECK("rrcomments");
GLOBAL();
display_rrcomments = state ? 1 : -1;
2015-02-05 07:56:05 +11:00
break;
default:
goto invalid_option;
}
break;
case 's':
switch (cmd[1]) {
case 'h':
FULLCHECK("short");
GLOBAL();
display_short_form = state;
if (state) {
display_question = false;
display_answer = true;
display_authority = false;
display_additional = false;
display_comments = false;
display_rrcomments = -1;
2015-02-05 07:56:05 +11:00
}
break;
case 'p': /* split */
FULLCHECK("split");
GLOBAL();
if (value != NULL && !state) {
2015-02-05 07:56:05 +11:00
goto invalid_option;
}
2015-02-05 07:56:05 +11:00
if (!state) {
display_splitwidth = 0;
break;
} else if (value == NULL) {
2015-02-05 07:56:05 +11:00
break;
}
2015-02-05 07:56:05 +11:00
result = parse_uint(&display_splitwidth, value, 1023,
"split");
2018-02-14 19:09:23 +11:00
if ((display_splitwidth % 4) != 0) {
2015-02-05 07:56:05 +11:00
display_splitwidth =
((display_splitwidth + 3) / 4) * 4;
fprintf(stderr,
";; Warning, split must be "
"a multiple of 4; adjusting "
"to %u\n",
2015-02-05 07:56:05 +11:00
display_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 (display_splitwidth) {
2015-02-05 07:56:05 +11:00
display_splitwidth += 3;
}
2015-02-05 07:56:05 +11:00
CHECK("parse_uint(split)", result);
break;
case 'u': /* subnet */
FULLCHECK("subnet");
if (state && value == NULL) {
2015-02-05 07:56:05 +11:00
goto need_value;
}
2015-02-05 07:56:05 +11:00
if (!state) {
if (query->ecs_addr != NULL) {
isc_mem_free(mctx, query->ecs_addr);
query->ecs_addr = NULL;
}
break;
}
if (query->edns == -1) {
2015-02-05 07:56:05 +11:00
query->edns = 0;
}
2015-02-05 07:56:05 +11:00
result = parse_netprefix(&query->ecs_addr, value);
CHECK("parse_netprefix", result);
break;
default:
goto invalid_option;
}
break;
case 't':
switch (cmd[1]) {
case 'c': /* tcp */
FULLCHECK("tcp");
GLOBAL();
tcp_mode = state;
break;
case 'i': /* timeout */
FULLCHECK("timeout");
if (value == NULL) {
2015-02-05 07:56:05 +11:00
goto need_value;
}
if (!state) {
2015-02-05 07:56:05 +11:00
goto invalid_option;
}
result = parse_uint(&query->timeout, value, MAXTIMEOUT,
"timeout");
2015-02-05 07:56:05 +11:00
CHECK("parse_uint(timeout)", result);
if (query->timeout == 0) {
2015-02-05 07:56:05 +11:00
query->timeout = 1;
}
2015-02-05 07:56:05 +11:00
break;
case 'r':
FULLCHECK("tries");
if (value == NULL) {
2015-02-05 07:56:05 +11:00
goto need_value;
}
if (!state) {
2015-02-05 07:56:05 +11:00
goto invalid_option;
}
result = parse_uint(&query->udpretries, value, MAXTRIES,
"udpretries");
2015-02-05 07:56:05 +11:00
CHECK("parse_uint(udpretries)", result);
if (query->udpretries == 0) {
2015-02-05 07:56:05 +11:00
query->udpretries = 1;
}
2015-02-05 07:56:05 +11:00
break;
case 't':
switch (cmd[2]) {
case 'l':
switch (cmd[3]) {
case 0:
case 'i': /* ttlid */
FULLCHECK2("ttl", "ttlid");
GLOBAL();
display_ttl = state;
break;
case 'u': /* ttlunits */
FULLCHECK("ttlunits");
GLOBAL();
display_ttl = true;
2015-02-05 07:56:05 +11:00
display_ttlunits = state;
break;
default:
goto invalid_option;
}
break;
default:
goto invalid_option;
}
break;
default:
goto invalid_option;
}
break;
case 'u':
switch (cmd[1]) {
case 'd':
FULLCHECK("udptimeout");
if (value == NULL) {
goto need_value;
}
if (!state) {
goto invalid_option;
}
result = parse_uint(&query->udptimeout, value,
MAXTIMEOUT, "udptimeout");
CHECK("parse_uint(udptimeout)", result);
break;
case 'n':
FULLCHECK("unknownformat");
display_unknown_format = state;
break;
default:
2015-02-05 07:56:05 +11:00
goto invalid_option;
}
2015-02-05 07:56:05 +11:00
break;
case 'v':
FULLCHECK("vc");
GLOBAL();
tcp_mode = state;
break;
2019-07-20 14:35:59 -04:00
case 'y': /* yaml */
FULLCHECK("yaml");
yaml = state;
if (state) {
display_rrcomments = state;
}
break;
2015-02-05 07:56:05 +11:00
case 'z': /* zflag */
FULLCHECK("zflag");
query->have_zflag = state;
break;
global_option:
fprintf(stderr, "Ignored late global option: +%s\n", option);
break;
default:
invalid_option:
need_value:
fprintf(stderr, "Invalid option: +%s\n", option);
usage();
}
return;
}
/*%
* #true returned if value was used
2015-02-05 07:56:05 +11:00
*/
static const char *single_dash_opts = "46himv";
static const char *dash_opts = "46bcfhiptvx";
static bool
dash_option(const char *option, char *next, struct query *query, bool global,
2020-02-13 14:44:37 -08:00
bool *setname) {
char opt;
const char *value;
isc_result_t result;
bool value_from_next;
isc_consttextregion_t tr;
2020-02-13 14:44:37 -08:00
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;
uint32_t num;
2015-02-05 07:56:05 +11:00
while (strpbrk(option, single_dash_opts) == &option[0]) {
/*
* Since the -[46hiv] options do not take an argument,
* account for them (in any number and/or combination)
* if they appear as the first character(s) of an opt.
*/
opt = option[0];
switch (opt) {
case '4':
GLOBAL();
if (have_ipv4) {
isc_net_disableipv6();
have_ipv6 = false;
2015-02-05 07:56:05 +11:00
} else {
fatal("can't find IPv4 networking");
/* NOTREACHED */
return (false);
2015-02-05 07:56:05 +11:00
}
break;
case '6':
GLOBAL();
if (have_ipv6) {
isc_net_disableipv4();
have_ipv4 = false;
2015-02-05 07:56:05 +11:00
} else {
fatal("can't find IPv6 networking");
/* NOTREACHED */
return (false);
2015-02-05 07:56:05 +11:00
}
break;
case 'h':
help();
exit(0);
break;
case 'i':
/* deprecated */
2015-02-05 07:56:05 +11:00
break;
case 'm':
/*
* handled by preparse_args()
*/
break;
2015-02-05 07:56:05 +11:00
case 'v':
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
fprintf(stderr, "mDiG %s\n", PACKAGE_VERSION);
2015-02-05 07:56:05 +11:00
exit(0);
break;
}
if (strlen(option) > 1U) {
2015-02-05 07:56:05 +11:00
option = &option[1];
} else {
return (false);
}
2015-02-05 07:56:05 +11:00
}
opt = option[0];
if (strlen(option) > 1U) {
value_from_next = false;
2015-02-05 07:56:05 +11:00
value = &option[1];
} else {
value_from_next = true;
2015-02-05 07:56:05 +11:00
value = next;
}
if (value == NULL) {
2015-02-05 07:56:05 +11:00
goto invalid_option;
}
2015-02-05 07:56:05 +11:00
switch (opt) {
case 'b':
GLOBAL();
hash = strchr(value, '#');
if (hash != NULL) {
result = parse_uint(&num, hash + 1, MAXPORT,
"port number");
CHECK("parse_uint(srcport)", result);
srcport = num;
*hash = '\0';
} else {
2015-02-05 07:56:05 +11:00
srcport = 0;
}
2015-02-05 13:34:18 +01:00
if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) {
2015-02-05 07:56:05 +11:00
isc_sockaddr_fromin6(&srcaddr, &in6, srcport);
isc_net_disableipv4();
2015-02-05 13:34:18 +01:00
} else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) {
2015-02-05 07:56:05 +11:00
isc_sockaddr_fromin(&srcaddr, &in4, srcport);
isc_net_disableipv6();
} else {
if (hash != NULL) {
2015-02-05 07:56:05 +11:00
*hash = '#';
}
2015-02-05 07:56:05 +11:00
fatal("invalid address %s", value);
}
if (hash != NULL) {
2015-02-05 07:56:05 +11:00
*hash = '#';
}
have_src = true;
2015-02-05 07:56:05 +11:00
return (value_from_next);
case 'c':
tr.base = value;
tr.length = strlen(value);
result = dns_rdataclass_fromtext(&rdclass,
(isc_textregion_t *)&tr);
CHECK("dns_rdataclass_fromtext", result);
query->rdclass = rdclass;
return (value_from_next);
case 'f':
batchname = value;
return (value_from_next);
case 'p':
GLOBAL();
result = parse_uint(&num, value, MAXPORT, "port number");
CHECK("parse_uint(port)", result);
port = num;
return (value_from_next);
case 't':
tr.base = value;
tr.length = strlen(value);
result = dns_rdatatype_fromtext(&rdtype,
(isc_textregion_t *)&tr);
CHECK("dns_rdatatype_fromtext", result);
query->rdtype = rdtype;
return (value_from_next);
case 'x':
get_reverse(textname, sizeof(textname), value);
strlcpy(query->textname, textname, sizeof(query->textname));
2015-02-05 07:56:05 +11:00
query->rdtype = dns_rdatatype_ptr;
query->rdclass = dns_rdataclass_in;
*setname = true;
2015-02-05 07:56:05 +11:00
return (value_from_next);
global_option:
fprintf(stderr, "Ignored late global option: -%s\n", option);
usage();
default:
invalid_option:
fprintf(stderr, "Invalid option: -%s\n", option);
usage();
}
/* NOTREACHED */
return (false);
2015-02-05 07:56:05 +11:00
}
static struct query *
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
clone_default_query(void) {
2015-02-05 07:56:05 +11:00
struct query *query;
query = isc_mem_allocate(mctx, sizeof(struct query));
memmove(query, &default_query, sizeof(struct query));
if (default_query.ecs_addr != NULL) {
size_t len = sizeof(isc_sockaddr_t);
query->ecs_addr = isc_mem_allocate(mctx, len);
memmove(query->ecs_addr, default_query.ecs_addr, len);
}
if (query->timeout == 0) {
2015-02-05 07:56:05 +11:00
query->timeout = tcp_mode ? TCPTIMEOUT : UDPTIMEOUT;
}
2015-02-05 07:56:05 +11:00
return (query);
2015-02-05 07:56:05 +11:00
}
/*%
* 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 mdig involves memory allocation
* by its nature, so it can't be done in the main argument parser.
*/
static void
2020-02-13 14:44:37 -08:00
preparse_args(int argc, char **argv) {
int rc;
char **rv;
2020-02-13 14:44:37 -08:00
char *option;
bool ipv4only = false, ipv6only = false;
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 'm':
isc_mem_debugging = ISC_MEM_DEBUGTRACE |
ISC_MEM_DEBUGRECORD;
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] ||
strlen(option) > 1U) {
/* Error or value in option. */
continue;
}
/* Dash value is next argument so we need to skip it. */
rc--, rv++;
/* Handle missing argument */
if (rc == 0) {
break;
}
}
}
2015-02-05 07:56:05 +11:00
static void
2020-02-13 14:44:37 -08:00
parse_args(bool is_batchfile, int argc, char **argv) {
2015-02-05 07:56:05 +11:00
struct query *query = NULL;
2020-02-13 14:44:37 -08:00
char batchline[MXNAME];
int bargc;
char *bargv[64];
int rc;
char **rv;
bool global = true;
char *last;
2015-02-05 07:56:05 +11:00
/*
* 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 query" which won't actually be used
* anywhere, except for cloning into new queries
*/
if (!is_batchfile) {
default_query.textname[0] = 0;
default_query.recurse = true;
default_query.have_aaonly = false;
default_query.have_adflag = true; /*XXX*/
default_query.have_cdflag = false;
default_query.have_zflag = false;
default_query.dnssec = false;
default_query.expire = false;
default_query.send_cookie = false;
default_query.cookie = NULL;
default_query.nsid = false;
2015-02-05 07:56:05 +11:00
default_query.rdtype = dns_rdatatype_a;
default_query.rdclass = dns_rdataclass_in;
default_query.udpsize = 0;
default_query.edns = 0; /*XXX*/
default_query.ednsopts = NULL;
default_query.ednsoptscnt = 0;
default_query.ednsflags = 0;
default_query.ecs_addr = NULL;
default_query.timeout = 0;
default_query.udptimeout = 0;
default_query.udpretries = 3;
ISC_LINK_INIT(&default_query, link);
}
if (is_batchfile) {
/* Processing '-f batchfile'. */
query = clone_default_query();
global = false;
} else {
2015-02-05 07:56:05 +11:00
query = &default_query;
}
2015-02-05 07:56:05 +11:00
rc = argc;
rv = argv;
for (rc--, rv++; rc > 0; rc--, rv++) {
if (strncmp(rv[0], "%", 1) == 0) {
2015-02-05 07:56:05 +11:00
break;
}
2015-02-05 07:56:05 +11:00
if (rv[0][0] == '@') {
if (server != NULL) {
2015-02-05 07:56:05 +11:00
fatal("server already set to @%s", server);
}
2015-02-05 07:56:05 +11:00
server = &rv[0][1];
} else if (rv[0][0] == '+') {
plus_option(&rv[0][1], query, global);
} else if (rv[0][0] == '-') {
bool setname = false;
2015-02-05 07:56:05 +11:00
if (rc <= 1) {
if (dash_option(&rv[0][1], NULL, query, global,
&setname)) {
2015-02-05 07:56:05 +11:00
rc--;
rv++;
}
} else {
if (dash_option(&rv[0][1], rv[1], query, global,
&setname)) {
2015-02-05 07:56:05 +11:00
rc--;
rv++;
}
}
if (setname) {
if (query == &default_query) {
2015-02-05 07:56:05 +11:00
query = clone_default_query();
}
2015-02-05 07:56:05 +11:00
ISC_LIST_APPEND(queries, query, link);
default_query.textname[0] = 0;
query = clone_default_query();
global = false;
2015-02-05 07:56:05 +11:00
}
} else {
/*
* Anything which isn't an option
*/
if (query == &default_query) {
2015-02-05 07:56:05 +11:00
query = clone_default_query();
}
strlcpy(query->textname, rv[0],
2015-02-05 07:56:05 +11:00
sizeof(query->textname));
ISC_LIST_APPEND(queries, query, link);
query = clone_default_query();
global = false;
2015-02-05 07:56:05 +11:00
/* XXX Error message */
}
}
/*
* If we have a batchfile, read the query list from it.
*/
if ((batchname != NULL) && !is_batchfile) {
if (strcmp(batchname, "-") == 0) {
2015-02-05 07:56:05 +11:00
batchfp = stdin;
} else {
2015-02-05 07:56:05 +11:00
batchfp = fopen(batchname, "r");
}
2015-02-05 07:56:05 +11:00
if (batchfp == NULL) {
perror(batchname);
fatal("couldn't open batch file '%s'", batchname);
}
while (fgets(batchline, sizeof(batchline), batchfp) != 0) {
if (batchline[0] == '\r' || batchline[0] == '\n' ||
2020-02-13 14:44:37 -08:00
batchline[0] == '#' || batchline[0] == ';')
{
2015-02-19 15:53:20 +11:00
continue;
}
for (bargc = 1, bargv[bargc] = strtok_r(
batchline, " \t\r\n", &last);
(bargc < 14) && bargv[bargc]; bargc++,
2020-02-13 14:44:37 -08:00
bargv[bargc] = strtok_r(NULL, " \t\r\n", &last))
{
/* empty body */
2015-02-05 07:56:05 +11:00
}
bargv[0] = argv[0];
parse_args(true, bargc, (char **)bargv);
2015-02-05 07:56:05 +11:00
}
if (batchfp != stdin) {
2015-02-05 07:56:05 +11:00
fclose(batchfp);
}
2015-02-05 07:56:05 +11:00
}
if (query != &default_query) {
if (query->ecs_addr != NULL) {
2015-02-05 07:56:05 +11:00
isc_mem_free(mctx, query->ecs_addr);
}
2015-02-05 07:56:05 +11:00
isc_mem_free(mctx, query);
}
}
#ifdef WIN32
static void
usleep(unsigned int usec) {
HANDLE timer;
LARGE_INTEGER ft;
ft.QuadPart = -(10 * (__int64)usec);
timer = CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
}
#endif
2015-02-05 07:56:05 +11:00
/*% Main processing routine for mdig */
int
2020-02-13 14:44:37 -08:00
main(int argc, char *argv[]) {
struct query *query;
isc_result_t result;
isc_sockaddr_t bind_any;
isc_log_t *lctx;
isc_logconfig_t *lcfg;
isc_taskmgr_t *taskmgr;
isc_task_t *task;
isc_timermgr_t *timermgr;
isc_socketmgr_t *socketmgr;
2015-02-05 07:56:05 +11:00
dns_dispatchmgr_t *dispatchmgr;
2020-02-13 14:44:37 -08:00
unsigned int attrs, attrmask;
dns_dispatch_t *dispatchvx;
dns_view_t *view;
int ns;
unsigned int i;
2015-02-05 07:56:05 +11:00
RUNCHECK(isc_app_start());
dns_result_register();
if (isc_net_probeipv4() == ISC_R_SUCCESS) {
have_ipv4 = true;
}
if (isc_net_probeipv6() == ISC_R_SUCCESS) {
have_ipv6 = true;
}
if (!have_ipv4 && !have_ipv6) {
2015-02-05 07:56:05 +11:00
fatal("could not find either IPv4 or IPv6");
}
2015-02-05 07:56:05 +11:00
preparse_args(argc, argv);
2015-02-05 07:56:05 +11:00
mctx = NULL;
isc_mem_create(&mctx);
2015-02-05 07:56:05 +11:00
lctx = NULL;
lcfg = NULL;
isc_log_create(mctx, &lctx, &lcfg);
2015-02-05 07:56:05 +11:00
RUNCHECK(dst_lib_init(mctx, NULL));
isc_nonce_buf(cookie_secret, sizeof(cookie_secret));
2015-02-05 07:56:05 +11:00
ISC_LIST_INIT(queries);
parse_args(false, argc, argv);
if (server == NULL) {
2015-02-05 07:56:05 +11:00
fatal("a server '@xxx' is required");
}
2015-02-05 07:56:05 +11:00
ns = 0;
result = bind9_getaddresses(server, port, &dstaddr, 1, &ns);
if (result != ISC_R_SUCCESS) {
fatal("couldn't get address for '%s': %s", server,
isc_result_totext(result));
}
2015-02-05 07:56:05 +11:00
if (isc_sockaddr_pf(&dstaddr) == PF_INET && have_ipv6) {
isc_net_disableipv6();
have_ipv6 = false;
2015-02-05 07:56:05 +11:00
} else if (isc_sockaddr_pf(&dstaddr) == PF_INET6 && have_ipv4) {
isc_net_disableipv4();
have_ipv4 = false;
2015-02-05 07:56:05 +11:00
}
if (have_ipv4 && have_ipv6) {
2015-02-05 07:56:05 +11:00
fatal("can't choose between IPv4 and IPv6");
}
2015-02-05 07:56:05 +11:00
taskmgr = NULL;
RUNCHECK(isc_taskmgr_create(mctx, 1, 0, NULL, &taskmgr));
2015-02-05 07:56:05 +11:00
task = NULL;
RUNCHECK(isc_task_create(taskmgr, 0, &task));
timermgr = NULL;
RUNCHECK(isc_timermgr_create(mctx, &timermgr));
socketmgr = NULL;
RUNCHECK(isc_socketmgr_create(mctx, &socketmgr));
dispatchmgr = NULL;
RUNCHECK(dns_dispatchmgr_create(mctx, &dispatchmgr));
2015-02-05 07:56:05 +11:00
attrs = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_MAKEQUERY;
2015-02-05 07:56:05 +11:00
if (have_ipv4) {
isc_sockaddr_any(&bind_any);
attrs |= DNS_DISPATCHATTR_IPV4;
} else {
isc_sockaddr_any6(&bind_any);
attrs |= DNS_DISPATCHATTR_IPV6;
}
attrmask = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP |
DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
2015-02-05 07:56:05 +11:00
dispatchvx = NULL;
RUNCHECK(dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
have_src ? &srcaddr : &bind_any, 4096, 100,
100, 17, 19, attrs, attrmask,
&dispatchvx));
2015-02-05 07:56:05 +11:00
requestmgr = NULL;
RUNCHECK(dns_requestmgr_create(
mctx, timermgr, socketmgr, taskmgr, dispatchmgr,
have_ipv4 ? dispatchvx : NULL, have_ipv6 ? dispatchvx : NULL,
&requestmgr));
2015-02-05 07:56:05 +11:00
view = NULL;
RUNCHECK(dns_view_create(mctx, 0, "_test", &view));
query = ISC_LIST_HEAD(queries);
RUNCHECK(isc_app_onrun(mctx, task, sendqueries, query));
/*
* Stall to the start of a new second.
*/
if (burst) {
isc_time_t start, now;
RUNCHECK(isc_time_now(&start));
/*
* Sleep to 1ms of the end of the second then run a busy loop
* until the second changes.
*/
do {
RUNCHECK(isc_time_now(&now));
if (isc_time_seconds(&start) == isc_time_seconds(&now))
{
int us = US_PER_SEC -
(isc_time_nanoseconds(&now) /
NS_PER_US);
if (us > US_PER_MS) {
usleep(us - US_PER_MS);
}
} else {
break;
}
} while (1);
}
2015-02-05 07:56:05 +11:00
(void)isc_app_run();
dns_view_detach(&view);
dns_requestmgr_shutdown(requestmgr);
dns_requestmgr_detach(&requestmgr);
dns_dispatch_detach(&dispatchvx);
dns_dispatchmgr_destroy(&dispatchmgr);
isc_socketmgr_destroy(&socketmgr);
isc_timermgr_destroy(&timermgr);
isc_task_shutdown(task);
isc_task_detach(&task);
isc_taskmgr_destroy(&taskmgr);
dst_lib_destroy();
isc_log_destroy(&lctx);
2015-02-05 07:56:05 +11:00
query = ISC_LIST_HEAD(queries);
while (query != NULL) {
struct query *next = ISC_LIST_NEXT(query, link);
if (query->ednsopts != NULL) {
for (i = 0; i < EDNSOPTS; i++) {
if (query->ednsopts[i].value != NULL) {
isc_mem_free(mctx,
query->ednsopts[i].value);
}
}
isc_mem_free(mctx, query->ednsopts);
}
if (query->ecs_addr != NULL) {
2015-02-05 07:56:05 +11:00
isc_mem_free(mctx, query->ecs_addr);
query->ecs_addr = NULL;
}
2015-02-05 07:56:05 +11:00
isc_mem_free(mctx, query);
query = next;
}
if (default_query.ecs_addr != NULL) {
isc_mem_free(mctx, default_query.ecs_addr);
}
2015-02-05 07:56:05 +11:00
isc_mem_destroy(&mctx);
isc_app_finish();
return (0);
}