From b66b333f59cf51ef87f973084a5023acd9317fb2 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 2 Oct 2015 12:32:42 -0700 Subject: [PATCH] [master] dnstap 4235. [func] Added support in named for "dnstap", a fast method of capturing and logging DNS traffic, and a new command "dnstap-read" to read a dnstap log file. Use "configure --enable-dnstap" to enable this feature (note that this requires libprotobuf-c and libfstrm). See the ARM for configuration details. Thanks to Robert Edmonds of Farsight Security. [RT #40211] --- CHANGES | 10 + COPYRIGHT | 48 + README | 5 + bin/named/Makefile.in | 5 + bin/named/client.c | 74 +- bin/named/config.c | 5 + bin/named/include/named/client.h | 2 +- bin/named/include/named/globals.h | 10 +- bin/named/include/named/server.h | 3 + bin/named/server.c | 133 +++ bin/named/win32/os.c | 1 + bin/tests/system/conf.sh.in | 3 +- bin/tests/system/dnstap/clean.sh | 22 + bin/tests/system/dnstap/ns1/named.conf | 50 + bin/tests/system/dnstap/ns1/root.db | 27 + bin/tests/system/dnstap/ns2/example.db | 31 + bin/tests/system/dnstap/ns2/named.conf | 49 + bin/tests/system/dnstap/ns3/named.conf | 52 + bin/tests/system/dnstap/tests.sh | 177 +++ bin/tools/.gitignore | 1 + bin/tools/Makefile.in | 29 +- bin/tools/dnstap-read.1 | 97 ++ bin/tools/dnstap-read.c | 321 +++++ bin/tools/dnstap-read.docbook | 125 ++ bin/tools/dnstap-read.html | 78 ++ config.h.in | 3 + configure | 257 ++++ configure.in | 78 ++ doc/arm/Bv9ARM-book.xml | 127 ++ doc/arm/man.dnstap-read.html | 118 ++ doc/arm/notes.xml | 23 + lib/dns/Makefile.in | 19 +- lib/dns/dnstap.c | 1047 +++++++++++++++++ lib/dns/dnstap.pb-c.c | 523 ++++++++ lib/dns/dnstap.pb-c.h | 343 ++++++ lib/dns/dnstap.proto | 268 +++++ lib/dns/include/dns/Makefile.in | 5 +- lib/dns/include/dns/dnstap.h | 347 ++++++ lib/dns/include/dns/dnstap.pb-c.h | 343 ++++++ lib/dns/include/dns/log.h | 2 + lib/dns/include/dns/masterdump.h | 20 + lib/dns/include/dns/result.h | 5 +- lib/dns/include/dns/types.h | 4 + lib/dns/include/dns/view.h | 8 +- lib/dns/log.c | 2 + lib/dns/masterdump.c | 52 +- lib/dns/message.c | 44 +- lib/dns/resolver.c | 64 + lib/dns/result.c | 7 +- lib/dns/tests/Makefile.in | 7 + lib/dns/tests/dnstap_test.c | 354 ++++++ lib/dns/tests/dnstest.c | 70 ++ lib/dns/tests/dnstest.h | 4 + lib/dns/tests/testdata/dnstap/dnstap.saved | Bin 0 -> 29534 bytes lib/dns/tests/testdata/dnstap/dnstap.text | 96 ++ lib/dns/tests/testdata/dnstap/query.auth | 4 + lib/dns/tests/testdata/dnstap/query.recursive | 4 + lib/dns/tests/testdata/dnstap/response.auth | 19 + .../tests/testdata/dnstap/response.recursive | 19 + lib/dns/view.c | 6 + lib/isc/include/isc/task.h | 7 +- lib/isc/task.c | 34 +- lib/isc/win32/libisc.def.in | 1 + lib/isccfg/namedconf.c | 89 +- win32utils/Configure | 3 + 65 files changed, 5741 insertions(+), 43 deletions(-) create mode 100644 bin/tests/system/dnstap/clean.sh create mode 100644 bin/tests/system/dnstap/ns1/named.conf create mode 100644 bin/tests/system/dnstap/ns1/root.db create mode 100644 bin/tests/system/dnstap/ns2/example.db create mode 100644 bin/tests/system/dnstap/ns2/named.conf create mode 100644 bin/tests/system/dnstap/ns3/named.conf create mode 100644 bin/tests/system/dnstap/tests.sh create mode 100644 bin/tools/dnstap-read.1 create mode 100644 bin/tools/dnstap-read.c create mode 100644 bin/tools/dnstap-read.docbook create mode 100644 bin/tools/dnstap-read.html create mode 100644 doc/arm/man.dnstap-read.html create mode 100644 lib/dns/dnstap.c create mode 100644 lib/dns/dnstap.pb-c.c create mode 100644 lib/dns/dnstap.pb-c.h create mode 100644 lib/dns/dnstap.proto create mode 100644 lib/dns/include/dns/dnstap.h create mode 100644 lib/dns/include/dns/dnstap.pb-c.h create mode 100644 lib/dns/tests/dnstap_test.c create mode 100644 lib/dns/tests/testdata/dnstap/dnstap.saved create mode 100644 lib/dns/tests/testdata/dnstap/dnstap.text create mode 100644 lib/dns/tests/testdata/dnstap/query.auth create mode 100644 lib/dns/tests/testdata/dnstap/query.recursive create mode 100644 lib/dns/tests/testdata/dnstap/response.auth create mode 100644 lib/dns/tests/testdata/dnstap/response.recursive diff --git a/CHANGES b/CHANGES index d93b1ad953..f050fc8fb3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,13 @@ +4235. [func] Added support in named for "dnstap", a fast method of + capturing and logging DNS traffic, and a new command + "dnstap-read" to read a dnstap log file. Use + "configure --enable-dnstap" to enable this + feature (note that this requires libprotobuf-c + and libfstrm). See the ARM for configuration details. + + Thanks to Robert Edmonds of Farsight Security. + [RT #40211] + 4234. [func] Add deflate compression in statistics channel HTTP server. [RT #40861] diff --git a/COPYRIGHT b/COPYRIGHT index 2d3c6ac47b..24310b70c6 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -551,3 +551,51 @@ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +----------------------------------------------------------------------------- + +Copyright (c) 2013-2014, Farsight Security, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- + +Copyright (c) 2014 by Farsight Security, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README b/README index 2390e57bae..36e4771f7f 100644 --- a/README +++ b/README @@ -56,6 +56,11 @@ BIND 9.11.0 BIND 9.11.0 includes a number of changes from BIND 9.10 and earlier releases. New features include: + - Added support for "dnstap", a fast and flexible method of + capturing and logging DNS traffic. + - Added support for "dyndb", a new API for loading zone data + from an external database, developed by Red Hat for the FreeIPA + project. - New "fetchlimit" quotas are now available for the use of recursive resolvers that are are under high query load for domains whose authoritative servers are nonresponsive or are diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in index 0f65097e6b..899756a1de 100644 --- a/bin/named/Makefile.in +++ b/bin/named/Makefile.in @@ -139,6 +139,11 @@ config.@O@: config.c -DNS_SYSCONFDIR=\"${sysconfdir}\" \ -c ${srcdir}/config.c +server.@O@: server.c + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \ + -DPRODUCT=\"${PRODUCT}\" \ + -DVERSION=\"${VERSION}\" -c ${srcdir}/server.c + named@EXEEXT@: ${OBJS} ${UOBJS} ${DEPLIBS} export MAKE_SYMTABLE="yes"; \ export BASEOBJS="${OBJS} ${UOBJS}"; \ diff --git a/bin/named/client.c b/bin/named/client.c index cad69d8f4c..f2e41f01cb 100644 --- a/bin/named/client.c +++ b/bin/named/client.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -985,6 +986,11 @@ client_send(ns_client_t *client) { unsigned int preferred_glue; isc_boolean_t opt_included = ISC_FALSE; size_t respsize; +#ifdef HAVE_DNSTAP + unsigned char zone[DNS_NAME_MAXWIRE]; + dns_dtmsgtype_t dtmsgtype; + isc_region_t zr; +#endif /* HAVE_DNSTAP */ REQUIRE(NS_CLIENT_VALID(client)); @@ -1123,6 +1129,28 @@ client_send(ns_client_t *client) { if (result != ISC_R_SUCCESS) goto done; +#ifdef HAVE_DNSTAP + memset(&zr, 0, sizeof(zr)); + if (((client->message->flags & DNS_MESSAGEFLAG_AA) != 0) && + (client->query.authzone != NULL)) + { + isc_buffer_t b; + dns_name_t *zo = + dns_zone_getorigin(client->query.authzone); + + isc_buffer_init(&b, zone, sizeof(zone)); + dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE); + result = dns_name_towire(zo, &cctx, &b); + if (result == ISC_R_SUCCESS) + isc_buffer_usedregion(&b, &zr); + } + + if ((client->message->flags & DNS_MESSAGEFLAG_RD) != 0) + dtmsgtype = DNS_DTTYPE_CR; + else + dtmsgtype = DNS_DTTYPE_AR; +#endif /* HAVE_DNSTAP */ + if (cleanup_cctx) { dns_compress_invalidate(&cctx); cleanup_cctx = ISC_FALSE; @@ -1136,12 +1164,28 @@ client_send(ns_client_t *client) { respsize = isc_buffer_usedlength(&tcpbuffer); result = client_sendpkg(client, &tcpbuffer); +#ifdef HAVE_DNSTAP + if (client->view != NULL) { + dns_dt_send(client->view, dtmsgtype, + &client->peeraddr, ISC_TRUE, &zr, + &client->requesttime, NULL, &buffer); + } +#endif /* HAVE_DNSTAP */ + isc_stats_increment(ns_g_server->tcpoutstats, ISC_MIN(respsize / 16, 256)); } else { respsize = isc_buffer_usedlength(&buffer); result = client_sendpkg(client, &buffer); +#ifdef HAVE_DNSTAP + if (client->view != NULL) { + dns_dt_send(client->view, dtmsgtype, + &client->peeraddr, ISC_FALSE, &zr, + &client->requesttime, NULL, &buffer); + } +#endif /* HAVE_DNSTAP */ + isc_stats_increment(ns_g_server->udpoutstats, ISC_MIN(respsize / 16, 256)); } @@ -1396,7 +1440,9 @@ ns_client_error(ns_client_t *client, isc_result_t result) { if (isc_sockaddr_equal(&client->peeraddr, &client->formerrcache.addr) && message->id == client->formerrcache.id && - client->requesttime - client->formerrcache.time < 2) { + (isc_time_seconds(&client->requesttime) - + client->formerrcache.time) < 2) + { /* Drop packet. */ ns_client_log(client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1), @@ -1406,7 +1452,8 @@ ns_client_error(ns_client_t *client, isc_result_t result) { return; } client->formerrcache.addr = client->peeraddr; - client->formerrcache.time = client->requesttime; + client->formerrcache.time = + isc_time_seconds(&client->requesttime); client->formerrcache.id = message->id; } else if (rcode == dns_rcode_servfail && client->query.qname != NULL && client->view != NULL && client->view->fail_ttl != 0 && @@ -2036,6 +2083,9 @@ client_request(isc_task_t *task, isc_event_t *event) { unsigned int flags; isc_boolean_t notimp; size_t reqsize; +#ifdef HAVE_DNSTAP + dns_dtmsgtype_t dtmsgtype; +#endif REQUIRE(event != NULL); client = event->ev_arg; @@ -2095,9 +2145,9 @@ client_request(isc_task_t *task, isc_event_t *event) { goto cleanup; client->state = client->newstate = NS_CLIENTSTATE_WORKING; - isc_task_getcurrenttime(task, &client->requesttime); - client->now = client->requesttime; - isc_time_set(&client->tnow, client->now, 0); + isc_task_getcurrenttimex(task, &client->requesttime); + client->tnow = client->requesttime; + client->now = isc_time_seconds(&client->tnow); if (result != ISC_R_SUCCESS) { if (TCP_CLIENT(client)) { @@ -2588,6 +2638,17 @@ client_request(isc_task_t *task, isc_event_t *event) { switch (client->message->opcode) { case dns_opcode_query: CTRACE("query"); +#ifdef HAVE_DNSTAP + if ((client->message->flags & DNS_MESSAGEFLAG_RD) != 0) + dtmsgtype = DNS_DTTYPE_CQ; + else + dtmsgtype = DNS_DTTYPE_AQ; + + dns_dt_send(view, dtmsgtype, &client->peeraddr, + TCP_CLIENT(client), NULL, + &client->requesttime, NULL, buffer); +#endif /* HAVE_DNSTAP */ + ns_query_start(client); break; case dns_opcode_update: @@ -3652,7 +3713,8 @@ ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager) { fprintf(f, "; client %s%s%s: id %u '%s/%s/%s'%s%s " "requesttime %d\n", peerbuf, sep, name, client->message->id, namebuf, typebuf, classbuf, - origfor, original, client->requesttime); + origfor, original, + isc_time_seconds(&client->requesttime)); client = ISC_LIST_NEXT(client, rlink); } UNLOCK(&manager->reclock); diff --git a/bin/named/config.c b/bin/named/config.c index aed352316b..4743be09d1 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -186,6 +186,11 @@ options {\n\ require-server-cookie no;\n\ v6-bias 50;\n\ " +#ifdef HAVE_DNSTAP +"\ + dnstap-identity hostname;\n\ +" +#endif #ifdef HAVE_GEOIP "\ geoip-use-ecs yes;\n\ diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h index 60d5a6be05..9e2ab242d2 100644 --- a/bin/named/include/named/client.h +++ b/bin/named/include/named/client.h @@ -129,7 +129,7 @@ struct ns_client { void (*shutdown)(void *arg, isc_result_t result); void *shutdown_arg; ns_query_t query; - isc_stdtime_t requesttime; + isc_time_t requesttime; isc_stdtime_t now; isc_time_t tnow; dns_name_t signername; /*%< [T]SIG key name */ diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h index c518f83518..df46adcd23 100644 --- a/bin/named/include/named/globals.h +++ b/bin/named/include/named/globals.h @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: globals.h,v 1.92 2011/11/09 18:44:04 each Exp $ */ - #ifndef NAMED_GLOBALS_H #define NAMED_GLOBALS_H 1 @@ -153,6 +151,14 @@ EXTERN const char * lwresd_g_defaultpidfile INIT(NS_LOCALSTATEDIR "/run/lwresd.pid"); #endif +#ifdef HAVE_DNSTAP +EXTERN const char * ns_g_defaultdnstap + INIT(NS_LOCALSTATEDIR "/run/named/" + "dnstap.sock"); +#else +EXTERN const char * ns_g_defaultdnstap INIT(NULL); +#endif /* HAVE_DNSTAP */ + EXTERN const char * ns_g_username INIT(NULL); #if defined(USE_PKCS11) diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index cdae240fe7..e1d354ddbb 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -122,6 +123,8 @@ struct ns_server { unsigned char secret[32]; /*%< Server Cookie Secret */ ns_cookiealg_t cookiealg; + dns_dtenv_t *dtenv; /*%< Dnstap environment */ + char * lockfile; }; diff --git a/bin/named/server.c b/bin/named/server.c index 0ecd8feeb7..15496d3ddb 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -2398,6 +2398,122 @@ create_empty_zone(dns_zone_t *zone, dns_name_t *name, dns_view_t *view, return (result); } +#ifdef HAVE_DNSTAP +static isc_result_t +configure_dnstap(const cfg_obj_t **maps, dns_view_t *view) { + isc_result_t result; + const cfg_obj_t *obj, *obj2; + const cfg_listelt_t *element; + const char *dpath = ns_g_defaultdnstap; + const cfg_obj_t *dlist = NULL; + dns_dtmsgtype_t dttypes = 0; + dns_dtmode_t dmode; + + result = ns_config_get(maps, "dnstap", &dlist); + if (result != ISC_R_SUCCESS) + return (ISC_R_SUCCESS); + + for (element = cfg_list_first(dlist); + element != NULL; + element = cfg_list_next(element)) + { + const char *str; + dns_dtmsgtype_t dt = 0; + + obj = cfg_listelt_value(element); + obj2 = cfg_tuple_get(obj, "type"); + str = cfg_obj_asstring(obj2); + if (strcasecmp(str, "client") == 0) { + dt |= DNS_DTTYPE_CQ|DNS_DTTYPE_CR; + } else if (strcasecmp(str, "auth") == 0) { + dt |= DNS_DTTYPE_AQ|DNS_DTTYPE_AR; + } else if (strcasecmp(str, "resolver") == 0) { + dt |= DNS_DTTYPE_RQ|DNS_DTTYPE_RR; + } else if (strcasecmp(str, "forwarder") == 0) { + dt |= DNS_DTTYPE_FQ|DNS_DTTYPE_FR; + } else if (strcasecmp(str, "all") == 0) { + dt |= DNS_DTTYPE_CQ|DNS_DTTYPE_CR| + DNS_DTTYPE_AQ|DNS_DTTYPE_AR| + DNS_DTTYPE_RQ|DNS_DTTYPE_RR| + DNS_DTTYPE_FQ|DNS_DTTYPE_FR; + } + + obj2 = cfg_tuple_get(obj, "mode"); + if (obj2 == NULL || cfg_obj_isvoid(obj2)) { + dttypes |= dt; + continue; + } + + str = cfg_obj_asstring(obj2); + if (strcasecmp(str, "query")) { + dt &= ~DNS_DTTYPE_RESPONSE; + } else if (strcasecmp(str, "response")) { + dt &= ~DNS_DTTYPE_QUERY; + } + + dttypes |= dt; + } + + if (ns_g_server->dtenv == NULL && dttypes != 0) { + obj = NULL; + CHECKM(ns_config_get(maps, "dnstap-output", &obj), + "'dnstap-output' must be set if 'dnstap' is set"); + + obj2 = cfg_tuple_get(obj, "mode"); + if (obj2 == NULL) + CHECKM(ISC_R_FAILURE, "dnstap-output mode not found"); + if (strcasecmp(cfg_obj_asstring(obj2), "file") == 0) + dmode = dns_dtmode_file; + else + dmode = dns_dtmode_unix; + + obj2 = cfg_tuple_get(obj, "path"); + if (obj2 == NULL) + CHECKM(ISC_R_FAILURE, "dnstap-output path not found"); + + dpath = cfg_obj_asstring(obj2); + + CHECKM(dns_dt_create(ns_g_mctx, dmode, dpath, ns_g_cpus, + &ns_g_server->dtenv), + "unable to create dnstap environment"); + } + + if (ns_g_server->dtenv == NULL) + return (ISC_R_SUCCESS); + + obj = NULL; + result = ns_config_get(maps, "dnstap-version", &obj); + if (result != ISC_R_SUCCESS) { + /* not specified; use the product and version */ + dns_dt_setversion(ns_g_server->dtenv, PRODUCT " " VERSION); + } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) { + /* Quoted string */ + dns_dt_setversion(ns_g_server->dtenv, cfg_obj_asstring(obj)); + } + + obj = NULL; + result = ns_config_get(maps, "dnstap-identity", &obj); + if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) { + /* "hostname" is interpreted as boolean ISC_TRUE */ + char buf[256]; + result = ns_os_gethostname(buf, sizeof(buf)); + if (result == ISC_R_SUCCESS) + dns_dt_setidentity(ns_g_server->dtenv, buf); + } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) { + /* Quoted string */ + dns_dt_setidentity(ns_g_server->dtenv, cfg_obj_asstring(obj)); + } + + dns_dt_attach(ns_g_server->dtenv, &view->dtenv); + view->dttypes = dttypes; + + result = ISC_R_SUCCESS; + + cleanup: + return (result); +} +#endif /* HAVE_DNSTAP */ + /* * Configure 'view' according to 'vconfig', taking defaults from 'config' * where values are missing in 'vconfig'. @@ -4008,6 +4124,13 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, } else view->redirectzone = NULL; +#ifdef HAVE_DNSTAP + /* + * Set up the dnstap environment and configure message + * types to log. + */ + CHECK(configure_dnstap(maps, view)); +#endif /* HAVE_DNSTAP */ result = ISC_R_SUCCESS; @@ -7016,6 +7139,9 @@ shutdown_server(isc_task_t *task, isc_event_t *event) { if (server->blackholeacl != NULL) dns_acl_detach(&server->blackholeacl); +#ifdef HAVE_DNSTAP + dns_dt_shutdown(); +#endif #ifdef HAVE_GEOIP dns_geoip_shutdown(); #endif @@ -7217,6 +7343,8 @@ ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) { server->lockfile = NULL; + server->dtenv = NULL; + server->magic = NS_SERVER_MAGIC; *serverp = server; } @@ -7226,6 +7354,11 @@ ns_server_destroy(ns_server_t **serverp) { ns_server_t *server = *serverp; REQUIRE(NS_SERVER_VALID(server)); +#ifdef HAVE_DNSTAP + if (server->dtenv != NULL) + dns_dt_detach(&server->dtenv); +#endif /* HAVE_DNSTAP */ + ns_controls_destroy(&server->controls); isc_stats_detach(&server->nsstats); diff --git a/bin/named/win32/os.c b/bin/named/win32/os.c index 34a4fbf772..7a2815813f 100644 --- a/bin/named/win32/os.c +++ b/bin/named/win32/os.c @@ -71,6 +71,7 @@ ns_paths_init(void) { ns_g_defaultlockfile = isc_ntpaths_get(NAMED_LOCK_PATH); ns_g_keyfile = isc_ntpaths_get(RNDC_KEY_PATH); ns_g_defaultsessionkeyfile = isc_ntpaths_get(SESSION_KEY_PATH); + ns_g_defaultdnstap = NULL; Initialized = TRUE; } diff --git a/bin/tests/system/conf.sh.in b/bin/tests/system/conf.sh.in index 027ee44b82..e0767bdc4e 100644 --- a/bin/tests/system/conf.sh.in +++ b/bin/tests/system/conf.sh.in @@ -58,6 +58,7 @@ RESOLVE=$TOP/lib/samples/resolve RRCHECKER=$TOP/bin/tools/named-rrchecker GENRANDOM=$TOP/bin/tools/genrandom NSLOOKUP=$TOP/bin/dig/nslookup +DNSTAPREAD="$TOP/bin/tools/dnstap-read" RANDFILE=$TOP/bin/tests/system/random.data @@ -67,7 +68,7 @@ RANDFILE=$TOP/bin/tests/system/random.data SUBDIRS="acl additional allow_query addzone autosign builtin cacheclean case checkconf @CHECKDS@ checknames checkzone cookie @COVERAGE@ database digdelv dlv dlvauto dlz dlzexternal - dname dns64 dnssec dsdigest dscp ecdsa ednscompliance + dname dns64 dnssec dsdigest dscp @DNSTAP@ ecdsa ednscompliance emptyzones fetchlimit filter-aaaa formerr forward geoip glue gost ixfr inline legacy limits logfileconfig lwresd masterfile masterformat metadata mkeys notify nslookup nsupdate pending diff --git a/bin/tests/system/dnstap/clean.sh b/bin/tests/system/dnstap/clean.sh new file mode 100644 index 0000000000..5003c135ad --- /dev/null +++ b/bin/tests/system/dnstap/clean.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +rm -f */named.memstats +rm -f */named.run +rm -f */named.stats +rm -f dig.out* +rm -f ns*/named.lock +rm -f ns*/dnstap.out diff --git a/bin/tests/system/dnstap/ns1/named.conf b/bin/tests/system/dnstap/ns1/named.conf new file mode 100644 index 0000000000..888b5a1a95 --- /dev/null +++ b/bin/tests/system/dnstap/ns1/named.conf @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +controls { /* empty */ }; + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify yes; + statistics-file "named.stats"; + dnstap-identity "ns1"; + dnstap-version "xxx"; + dnstap-output file "dnstap.out"; + dnstap { all; }; + send-cookie no; + require-server-cookie no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.1 port 9953 allow { any; } keys { rndc_key; }; +}; + +zone "." { + type master; + file "root.db"; +}; diff --git a/bin/tests/system/dnstap/ns1/root.db b/bin/tests/system/dnstap/ns1/root.db new file mode 100644 index 0000000000..0bbfda9b8f --- /dev/null +++ b/bin/tests/system/dnstap/ns1/root.db @@ -0,0 +1,27 @@ +; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$TTL 300 +. IN SOA gson.nominum.com. a.root.servers.nil. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 + +example. NS ns2.example. +ns2.example. A 10.53.0.2 diff --git a/bin/tests/system/dnstap/ns2/example.db b/bin/tests/system/dnstap/ns2/example.db new file mode 100644 index 0000000000..7d4a11af99 --- /dev/null +++ b/bin/tests/system/dnstap/ns2/example.db @@ -0,0 +1,31 @@ +; Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +; +; Permission to use, copy, modify, and/or distribute this software for any +; purpose with or without fee is hereby granted, provided that the above +; copyright notice and this permission notice appear in all copies. +; +; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +; AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +; PERFORMANCE OF THIS SOFTWARE. + +$ORIGIN . +$TTL 300 ; 5 minutes +example IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) +example. NS ns2.example. +ns2.example. A 10.53.0.2 + +$ORIGIN example. +a A 10.0.0.1 + MX 10 mail.example. + +mail A 10.0.0.2 diff --git a/bin/tests/system/dnstap/ns2/named.conf b/bin/tests/system/dnstap/ns2/named.conf new file mode 100644 index 0000000000..bae32ec886 --- /dev/null +++ b/bin/tests/system/dnstap/ns2/named.conf @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +controls { /* empty */ }; + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + recursion yes; + notify yes; + statistics-file "named.stats"; + dnstap-identity "ns2"; + dnstap-version "xxx"; + dnstap-output file "dnstap.out"; + dnstap { all; }; + send-cookie no; + require-server-cookie no; +}; + +include "../../common/controls.conf"; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "example" { + type master; + file "example.db"; + allow-update { any; }; +}; diff --git a/bin/tests/system/dnstap/ns3/named.conf b/bin/tests/system/dnstap/ns3/named.conf new file mode 100644 index 0000000000..643b5497b7 --- /dev/null +++ b/bin/tests/system/dnstap/ns3/named.conf @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +controls { /* empty */ }; + +options { + query-source address 10.53.0.3; + notify-source 10.53.0.3; + transfer-source 10.53.0.3; + port 5300; + directory "."; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + recursion yes; + notify yes; + dnstap-identity "ns3"; + dnstap-version "xxx"; + dnstap-output file "dnstap.out"; + dnstap { all; }; + send-cookie no; + require-server-cookie no; +}; + +server 10.53.0.1 { tcp-only yes; }; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +controls { + inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; diff --git a/bin/tests/system/dnstap/tests.sh b/bin/tests/system/dnstap/tests.sh new file mode 100644 index 0000000000..2f811e4239 --- /dev/null +++ b/bin/tests/system/dnstap/tests.sh @@ -0,0 +1,177 @@ +#!/bin/sh +# +# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +RNDCCMD="$RNDC -p 9953 -c ../common/rndc.conf" + +status=0 + +$DIG +short @10.53.0.3 -p 5300 a.example > dig.out +sleep 1 + +# XXX: file output should be flushed once a second according +# to the libfstrm source, but it doesn't seem to happen until +# enough data has accumulated. to get all the output, we stop +# the name servers, forcing a flush on shutdown. it would be +# nice to find a better way to do this. +$RNDCCMD -s 10.53.0.1 stop | sed 's/^/I:ns1 /' +$RNDCCMD -s 10.53.0.2 stop | sed 's/^/I:ns2 /' +$RNDCCMD -s 10.53.0.3 stop | sed 's/^/I:ns3 /' +sleep 1 + +udp1=`$DNSTAPREAD ns1/dnstap.out | grep "UDP " | wc -l` +tcp1=`$DNSTAPREAD ns1/dnstap.out | grep "TCP " | wc -l` +aq1=`$DNSTAPREAD ns1/dnstap.out | grep "AQ " | wc -l` +ar1=`$DNSTAPREAD ns1/dnstap.out | grep "AR " | wc -l` +cq1=`$DNSTAPREAD ns1/dnstap.out | grep "CQ " | wc -l` +cr1=`$DNSTAPREAD ns1/dnstap.out | grep "CR " | wc -l` +rq1=`$DNSTAPREAD ns1/dnstap.out | grep "RQ " | wc -l` +rr1=`$DNSTAPREAD ns1/dnstap.out | grep "RR " | wc -l` + +udp2=`$DNSTAPREAD ns2/dnstap.out | grep "UDP " | wc -l` +tcp2=`$DNSTAPREAD ns2/dnstap.out | grep "TCP " | wc -l` +aq2=`$DNSTAPREAD ns2/dnstap.out | grep "AQ " | wc -l` +ar2=`$DNSTAPREAD ns2/dnstap.out | grep "AR " | wc -l` +cq2=`$DNSTAPREAD ns2/dnstap.out | grep "CQ " | wc -l` +cr2=`$DNSTAPREAD ns2/dnstap.out | grep "CR " | wc -l` +rq2=`$DNSTAPREAD ns2/dnstap.out | grep "RQ " | wc -l` +rr2=`$DNSTAPREAD ns2/dnstap.out | grep "RR " | wc -l` + +udp3=`$DNSTAPREAD ns3/dnstap.out | grep "UDP " | wc -l` +tcp3=`$DNSTAPREAD ns3/dnstap.out | grep "TCP " | wc -l` +aq3=`$DNSTAPREAD ns3/dnstap.out | grep "AQ " | wc -l` +ar3=`$DNSTAPREAD ns3/dnstap.out | grep "AR " | wc -l` +cq3=`$DNSTAPREAD ns3/dnstap.out | grep "CQ " | wc -l` +cr3=`$DNSTAPREAD ns3/dnstap.out | grep "CR " | wc -l` +rq3=`$DNSTAPREAD ns3/dnstap.out | grep "RQ " | wc -l` +rr3=`$DNSTAPREAD ns3/dnstap.out | grep "RR " | wc -l` + +echo "I:checking UDP message counts" +ret=0 +[ $udp1 -eq 0 ] || { + echo "ns1 $udp1 expected 0" ; ret=1 +} +[ $udp2 -eq 2 ] || { + echo "ns2 $udp2 expected 2" ; ret=1 +} +[ $udp3 -eq 4 ] || { + echo "ns3 $udp3 expected 4" ; ret=1 +} +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:checking TCP message counts" +ret=0 +[ $tcp1 -eq 6 ] || { + echo "ns1 $tcp1 expected 6" ; ret=1 +} +[ $tcp2 -eq 2 ] || { + echo "ns2 $tcp2 expected 2" ; ret=1 +} +[ $tcp3 -eq 6 ] || { + echo "ns3 $tcp3 expected 6" ; ret=1 +} +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:checking AUTH_QUERY message counts" +ret=0 +[ $aq1 -eq 2 ] || { + echo "ns1 $aq1 exepcted 2" ; ret=1 +} +[ $aq2 -eq 1 ] || { + echo "ns2 $aq2 expected 1" ; ret=1 +} +[ $aq3 -eq 0 ] || { + echo "ns3 $aq3 expected 0" ; ret=1 +} +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:checking AUTH_RESPONSE message counts" +ret=0 +[ $ar1 -eq 2 ] || { + echo "ns1 $ar1 expected 2" ; ret=1 +} +[ $ar2 -eq 1 ] || { + echo "ns2 $ar2 expected 1" ; ret=1 +} +[ $ar3 -eq 0 ] || { + echo "ns3 $ar3 expected 0" ; ret=1 +} +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:checking CLIENT_QUERY message counts" +ret=0 +[ $cq1 -eq 1 ] || { + echo "ns1 $cq1 expected 1" ; ret=1 +} +[ $cq2 -eq 1 ] || { + echo "ns2 $cq2 expected 1" ; ret=1 +} +[ $cq3 -eq 2 ] || { + echo "ns3 $cq3 expected 2" ; ret=1 +} +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:checking CLIENT_RESPONSE message counts" +ret=0 +[ $cr1 -eq 1 ] || { + echo "ns1 $cr1 expected 1" ; ret=1 +} +[ $cr2 -eq 1 ] || { + echo "ns2 $cr2 expected 1" ; ret=1 +} +[ $cr3 -eq 2 ] || { + echo "ns3 $cr3 expected 2" ; ret=1 +} +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:checking RESOLVER_QUERY message counts" +ret=0 +[ $rq1 -eq 0 ] || { + echo "ns1 $rq1 expected 0" ; ret=1 +} +[ $rq2 -eq 0 ] || { + echo "ns2 $rq2 expected 0" ; ret=1 +} +[ $rq3 -eq 3 ] || { + echo "ns3 $rq3 expected 3" ; ret=1 +} +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:checking RESOLVER_RESPONSE message counts" +ret=0 +[ $rr1 -eq 0 ] || { + echo "ns1 $rr1 expected 0" ; ret=1 +} +[ $rr2 -eq 0 ] || { + echo "ns2 $rr2 expected 0" ; ret=1 +} +[ $rr3 -eq 3 ] || { + echo "ns3 $rr3 expected 3" ; ret=1 +} +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:exit status: $status" +exit $status diff --git a/bin/tools/.gitignore b/bin/tools/.gitignore index f22b7f75d3..cff80a5bd7 100644 --- a/bin/tools/.gitignore +++ b/bin/tools/.gitignore @@ -1,4 +1,5 @@ arpaname +dnstap-read genrandom isc-hmac-fixup mdig diff --git a/bin/tools/Makefile.in b/bin/tools/Makefile.in index cf3c31644a..7f0b5c0bb1 100644 --- a/bin/tools/Makefile.in +++ b/bin/tools/Makefile.in @@ -46,17 +46,24 @@ NOSYMLIBS = ${ISCNOSYMLIBS} @LIBS@ SUBDIRS = +DNSTAPTARGETS = dnstap-read@EXEEXT@ TARGETS = arpaname@EXEEXT@ named-journalprint@EXEEXT@ \ named-rrchecker@EXEEXT@ nsec3hash@EXEEXT@ \ - genrandom@EXEEXT@ isc-hmac-fixup@EXEEXT@ mdig@EXEEXT@ -SRCS = arpaname.c named-journalprint.c named-rrchecker.c \ - nsec3hash.c genrandom.c isc-hmac-fixup.c mdig.c + genrandom@EXEEXT@ isc-hmac-fixup@EXEEXT@ mdig@EXEEXT@ \ + @DNSTAPTARGETS@ +DNSTAPSRCS = dnstap-read.c +SRCS = arpaname.c named-journalprint.c named-rrchecker.c \ + nsec3hash.c genrandom.c isc-hmac-fixup.c mdig.c \ + @DNSTAPSRCS@ + +DNSTAPMAN = dnstap-read.1 MANPAGES = arpaname.1 named-journalprint.8 named-rrchecker.1 nsec3hash.8 \ - genrandom.8 isc-hmac-fixup.8 mdig.1 + genrandom.8 isc-hmac-fixup.8 mdig.1 @DNSTAPMAN@ +DNSTAPHTML = dnstap-read.html HTMLPAGES = arpaname.html named-journalprint.html named-rrchecker.html \ nsec3hash.html genrandom.html isc-hmac-fixup.html \ - mdig.html + mdig.html @DNSTAPHTML@ MANOBJS = ${MANPAGES} ${HTMLPAGES} @BIND9_MAKE_RULES@ @@ -94,6 +101,11 @@ mdig@EXEEXT@: mdig.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} ${BIND9DEPLIBS} export LIBS0="${DNSLIBS} ${BIND9LIBS}"; \ ${FINALBUILDCMD} +dnstap-read@EXEEXT@: dnstap-read.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} + export BASEOBJS="dnstap-read.@O@"; \ + export LIBS0="${DNSLIBS}"; \ + ${FINALBUILDCMD} + doc man:: ${MANOBJS} docclean manclean maintainer-clean:: @@ -104,7 +116,11 @@ installdirs: $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man1 $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man8 -install:: ${TARGETS} installdirs +dnstap: + ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} dnstap-read@EXEEXT@ \ + ${DESTDIR}${sbindir} + +install:: ${TARGETS} @DNSTAP@ installdirs ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} arpaname@EXEEXT@ \ ${DESTDIR}${sbindir} ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} named-journalprint@EXEEXT@ \ @@ -126,6 +142,7 @@ install:: ${TARGETS} installdirs ${INSTALL_DATA} ${srcdir}/nsec3hash.8 ${DESTDIR}${mandir}/man8 ${INSTALL_DATA} ${srcdir}/genrandom.8 ${DESTDIR}${mandir}/man8 ${INSTALL_DATA} ${srcdir}/mdig.1 ${DESTDIR}${mandir}/man1 + ${INSTALL_DATA} ${srcdir}/dnstap-read.1 ${DESTDIR}${mandir}/man1 clean distclean:: rm -f ${TARGETS} diff --git a/bin/tools/dnstap-read.1 b/bin/tools/dnstap-read.1 new file mode 100644 index 0000000000..6319a2658c --- /dev/null +++ b/bin/tools/dnstap-read.1 @@ -0,0 +1,97 @@ +.\" Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +.\" +.\" Permission to use, copy, modify, and/or distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +.\" PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $Id$ +.\" +.hy 0 +.ad l +'\" t +.\" Title: dnstap-read +.\" Author: [see the "AUTHOR" section] +.\" Generator: DocBook XSL Stylesheets v1.78.1 +.\" Date: September 13, 2015 +.\" Manual: BIND9 +.\" Source: BIND9 +.\" Language: English +.\" +.TH "DNSTAP\-READ" "1" "September 13, 2015" "BIND9" "BIND9" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +dnstap-read \- print dnstap data in human\-readable form +.SH "SYNOPSIS" +.HP 12 +\fBdnstap\-read\fR [\fB\-m\fR] [\fB\-p\fR] [\fB\-y\fR] {\fIfile\fR} +.SH "DESCRIPTION" +.PP +\fBdnstap\-read\fR +reads +\fBdnstap\fR +data from a specified file and prints it in a human\-readable format\&. By default, +\fBdnstap\fR +data is printed in a short summary format, but if the +\fB\-y\fR +option is specified, then a longer and more detailed YAML format is used instead\&. +.SH "OPTIONS" +.PP +\-m +.RS 4 +Trace memory allocations; used for debugging memory leaks\&. +.RE +.PP +\-p +.RS 4 +After printing the +\fBdnstap\fR +data, print the text form of the DNS message that was encapsulated in the +\fBdnstap\fR +frame\&. +.RE +.PP +\-y +.RS 4 +Print +\fBdnstap\fR +data in a detailed YAML format\&. Implies +\fB\-p\fR\&. +.RE +.SH "SEE ALSO" +.PP +\fBnamed\fR(8), +\fBnsupdate\fR(8), +BIND 9 Administrator Reference Manual\&. +.SH "AUTHOR" +.PP +Internet Systems Consortium +.SH "COPYRIGHT" +.br +Copyright \(co 2015 Internet Systems Consortium, Inc. ("ISC") +.br diff --git a/bin/tools/dnstap-read.c b/bin/tools/dnstap-read.c new file mode 100644 index 0000000000..ff7ec7b69a --- /dev/null +++ b/bin/tools/dnstap-read.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +/* + * Portions of this code were adapted from dnstap-ldns: + * + * Copyright (c) 2014 by Farsight Security, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +isc_mem_t *mctx = NULL; +isc_boolean_t memrecord = ISC_FALSE; +isc_boolean_t printmessage = ISC_FALSE; +isc_boolean_t yaml = ISC_FALSE; + +const char *program = "dnstap-read"; + +#define CHECKM(op, msg) \ + do { result = (op); \ + if (result != ISC_R_SUCCESS) { \ + fprintf(stderr, \ + "%s: %s: %s\n", program, msg, \ + isc_result_totext(result)); \ + goto cleanup; \ + } \ + } while (0) + +ISC_PLATFORM_NORETURN_PRE static void +fatal(const char *format, ...) ISC_PLATFORM_NORETURN_POST; + +static void +fatal(const char *format, ...) { + va_list args; + + fprintf(stderr, "%s: fatal: ", program); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + exit(1); +} + +static void +usage(void) { + fprintf(stderr, "dnstap-read [-mp] [filename]\n"); + fprintf(stderr, "\t-m\ttrace memory allocations\n"); + fprintf(stderr, "\t-p\tprint the full DNS message\n"); + fprintf(stderr, "\t-y\tprint YAML format (implies -p)\n"); +} + +static void +print_yaml(dns_dtdata_t *d) { + Dnstap__Dnstap *frame = d->frame; + Dnstap__Message *m = frame->message; + const ProtobufCEnumValue *ftype, *mtype; + + ftype = protobuf_c_enum_descriptor_get_value( + &dnstap__dnstap__type__descriptor, + frame->type); + if (ftype == NULL) + return; + + printf("type: %s\n", ftype->name); + + if (frame->has_identity) + printf("identity: %.*s\n", (int) frame->identity.len, + frame->identity.data); + + if (frame->has_version) + printf("version: %.*s\n", (int) frame->version.len, + frame->version.data); + + if (frame->type != DNSTAP__DNSTAP__TYPE__MESSAGE) + return; + + printf("message:\n"); + + mtype = protobuf_c_enum_descriptor_get_value( + &dnstap__message__type__descriptor, + m->type); + if (mtype == NULL) + return; + + printf(" type: %s\n", mtype->name); + + if (!isc_time_isepoch(&d->qtime)) { + char buf[100]; + isc_time_formatISO8601(&d->qtime, buf, sizeof(buf)); + printf(" query_time: !!timestamp %s\n", buf); + } + + if (!isc_time_isepoch(&d->rtime)) { + char buf[100]; + isc_time_formatISO8601(&d->rtime, buf, sizeof(buf)); + printf(" response_time: !!timestamp %s\n", buf); + } + + if (m->has_socket_family) { + const ProtobufCEnumValue *type = + protobuf_c_enum_descriptor_get_value( + &dnstap__socket_family__descriptor, + m->socket_family); + if (type != NULL) + printf(" socket_family: %s\n", type->name); + } + + printf(" socket_protocol: %s\n", d->tcp ? "TCP" : "UDP"); + + if (m->has_query_address) { + ProtobufCBinaryData *ip = &m->query_address; + char buf[100]; + + (void)inet_ntop(ip->len == 4 ? AF_INET : AF_INET6, + ip->data, buf, sizeof(buf)); + printf(" query_address: %s\n", buf); + } + + if (m->has_response_address) { + ProtobufCBinaryData *ip = &m->response_address; + char buf[100]; + + (void)inet_ntop(ip->len == 4 ? AF_INET : AF_INET6, + ip->data, buf, sizeof(buf)); + printf(" response_address: %s\n", buf); + } + + if (m->has_query_port) + printf(" query_port: %u\n", m->query_port); + + if (m->has_response_port) + printf(" response_port: %u\n", m->response_port); + + if (m->has_query_zone) { + isc_result_t result; + dns_fixedname_t fn; + dns_name_t *name; + isc_buffer_t b; + dns_decompress_t dctx; + + dns_fixedname_init(&fn); + name = dns_fixedname_name(&fn); + + isc_buffer_init(&b, m->query_zone.data, m->query_zone.len); + isc_buffer_add(&b, m->query_zone.len); + + dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_NONE); + result = dns_name_fromwire(name, &b, &dctx, 0, NULL); + if (result == ISC_R_SUCCESS) { + printf(" query_zone: "); + dns_name_print(name, stdout); + printf("\n"); + } + } + + if (d->msg != NULL) + printf(" %s: |\n", ((d->type & DNS_DTTYPE_QUERY) != 0) + ? "query_message" : "response_message"); +}; + +int +main(int argc, char *argv[]) { + isc_result_t result; + dns_message_t *message = NULL; + isc_buffer_t *b = NULL; + dns_dtdata_t *dt = NULL; + const dns_master_style_t *style = &dns_master_style_debug; + dns_dthandle_t handle = {dns_dtmode_none, NULL}; + int rv = 0, ch; + + while ((ch = isc_commandline_parse(argc, argv, "mpy")) != -1) { + switch (ch) { + case 'm': + isc_mem_debugging |= ISC_MEM_DEBUGRECORD; + memrecord = ISC_TRUE; + break; + case 'p': + printmessage = ISC_TRUE; + break; + case 'y': + yaml = ISC_TRUE; + style = &dns_master_style_indent; + dns_master_indentstr = " "; + printmessage = ISC_TRUE; + break; + default: + usage(); + exit(1); + } + } + + argc -= isc_commandline_index; + argv += isc_commandline_index; + + if (argc < 1) + fatal("no file specified"); + + RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS); + + CHECKM(dns_dt_open(argv[0], dns_dtmode_file, &handle), + "dns_dt_openfile"); + + for (;;) { + isc_region_t input; + isc_uint8_t *data; + size_t datalen; + + result = dns_dt_getframe(&handle, &data, &datalen); + if (result == ISC_R_NOMORE) + break; + else + CHECKM(result, "dns_dt_getframe"); + + input.base = data; + input.length = datalen; + + if (b != NULL) + isc_buffer_free(&b); + isc_buffer_allocate(mctx, &b, 2048); + if (b == NULL) + fatal("out of memory"); + + result = dns_dt_parse(mctx, &input, &dt); + if (result != ISC_R_SUCCESS) { + isc_buffer_free(&b); + continue; + } + + if (yaml) + print_yaml(dt); + else { + CHECKM(dns_dt_datatotext(dt, &b), "dns_dt_datatotext"); + printf("%.*s\n", (int) isc_buffer_usedlength(b), + (char *) isc_buffer_base(b)); + } + + if (printmessage && dt->msg != NULL) { + size_t textlen = 2048; + + isc_buffer_clear(b); + + for (;;) { + isc_buffer_reserve(&b, textlen); + if (b == NULL) + fatal("out of memory"); + result = dns_message_totext(dt->msg, style, + 0, b); + if (result == ISC_R_NOSPACE) { + textlen *= 2; + continue; + } else if (result == ISC_R_SUCCESS) { + printf("%.*s", + (int) isc_buffer_usedlength(b), + (char *) isc_buffer_base(b)); + isc_buffer_free(&b); + } else { + isc_buffer_free(&b); + CHECKM(result, "dns_message_totext"); + } + break; + } + } + + if (yaml) + printf("---\n"); + + dns_dtdata_free(&dt); + } + + cleanup: + if (dt != NULL) + dns_dtdata_free(&dt); + dns_dt_close(&handle); + if (message != NULL) + dns_message_destroy(&message); + if (b != NULL) + isc_buffer_free(&b); + isc_mem_destroy(&mctx); + + exit(rv); +} diff --git a/bin/tools/dnstap-read.docbook b/bin/tools/dnstap-read.docbook new file mode 100644 index 0000000000..58d01751f0 --- /dev/null +++ b/bin/tools/dnstap-read.docbook @@ -0,0 +1,125 @@ +]> + + + + + September 13, 2015 + + + + dnstap-read + 1 + BIND9 + + + + dnstap-read + print dnstap data in human-readable form + + + + + 2015 + Internet Systems Consortium, Inc. ("ISC") + + + + + + dnstap-read + + + + file + + + + + DESCRIPTION + + dnstap-read + reads dnstap data from a specified file + and prints it in a human-readable format. By default, + dnstap data is printed in a short summary + format, but if the option is specified, + then a longer and more detailed YAML format is used instead. + + + + + OPTIONS + + + + -m + + + Trace memory allocations; used for debugging memory leaks. + + + + + + -p + + + After printing the dnstap data, print + the text form of the DNS message that was encapsulated in the + dnstap frame. + + + + + + -y + + + Print dnstap data in a detailed YAML + format. Implies . + + + + + + + + + SEE ALSO + + + named8 + , + + nsupdate8 + , + BIND 9 Administrator Reference Manual. + + + + + AUTHOR + Internet Systems Consortium + + + + diff --git a/bin/tools/dnstap-read.html b/bin/tools/dnstap-read.html new file mode 100644 index 0000000000..e6917e20ab --- /dev/null +++ b/bin/tools/dnstap-read.html @@ -0,0 +1,78 @@ + + + + + +dnstap-read + + +
+
+
+

Name

+

dnstap-read — print dnstap data in human-readable form

+
+
+

Synopsis

+

dnstap-read [-m] [-p] [-y] {file}

+
+
+

DESCRIPTION

+

+ dnstap-read + reads dnstap data from a specified file + and prints it in a human-readable format. By default, + dnstap data is printed in a short summary + format, but if the -y option is specified, + then a longer and more detailed YAML format is used instead. +

+
+
+

OPTIONS

+
+
-m
+

+ Trace memory allocations; used for debugging memory leaks. +

+
-p
+

+ After printing the dnstap data, print + the text form of the DNS message that was encapsulated in the + dnstap frame. +

+
-y
+

+ Print dnstap data in a detailed YAML + format. Implies -p. +

+
+
+
+

SEE ALSO

+

+ named(8), + nsupdate(8), + BIND 9 Administrator Reference Manual. +

+
+
+

AUTHOR

+

Internet Systems Consortium +

+
+
+ diff --git a/config.h.in b/config.h.in index 4a092e2407..7fa8664ec6 100644 --- a/config.h.in +++ b/config.h.in @@ -209,6 +209,9 @@ int sigwait(const unsigned int *set, int *sig); /* Define to 1 if you have the `dlsym' function. */ #undef HAVE_DLSYM +/* Define to 1 to enable dnstap support */ +#undef HAVE_DNSTAP + /* Define to 1 if you have the `EVP_sha256' function. */ #undef HAVE_EVP_SHA256 diff --git a/configure b/configure index 4538d2c549..859bebb897 100755 --- a/configure +++ b/configure @@ -703,6 +703,13 @@ XSLTPROC W3M PDFLATEX LATEX +DNSTAPHTML +DNSTAPMAN +DNSTAPTARGETS +DNSTAPOBJS +DNSTAPSRCS +DNSTAP +PROTOC_C ISC_ARCH_DIR ISC_PLATFORM_USEMACASM ISC_PLATFORM_USESTDASM @@ -1021,6 +1028,9 @@ enable_fixed_rrset enable_rpz_nsip enable_rpz_nsdname enable_filter_aaaa +enable_dnstap +with_protobuf_c +with_libfstrm with_docbook_xsl with_idn with_libiconv @@ -1699,6 +1709,7 @@ Optional Features: --disable-rpz-nsip disable rpz-nsip rules [default=enabled] --disable-rpz-nsdname disable rpz-nsdname rules [default=enabled] --enable-filter-aaaa enable filtering of AAAA records [default=no] + --enable-dnstap enable dnstap support (requires fstrm, protobuf-c) --enable-querytrace enable very verbose query trace logging [default=no] --enable-full-report report values of all configure options @@ -1732,6 +1743,8 @@ Optional Packages: --with-kame=PATH use Kame IPv6 default path /usr/local/v6 --with-readline=LIBSPEC specify readline library default auto + --with-protobuf-c=path Path where protobuf-c is installed, for dnstap + --with-libfstrm=path Path where libfstrm is installed, for dnstap --with-docbook-xsl=PATH specify path for Docbook-XSL stylesheets --with-idn=MPREFIX enable IDN support using idnkit default PREFIX --with-libiconv=IPREFIX GNU libiconv are in IPREFIX default PREFIX @@ -19940,6 +19953,246 @@ $as_echo "#define ALLOW_FILTER_AAAA 1" >>confdefs.h ;; esac +# +# Activate dnstap? +# +# Check whether --enable-dnstap was given. +if test "${enable_dnstap+set}" = set; then : + enableval=$enable_dnstap; use_dnstap=$enableval +else + use_dnstap=no +fi + + +DNSTAP= +DNSTAPSRCS= +DNSTAPOBJS= +DNSTAPTARGETS= +DNSTAPMAN= +DNSTAPHTML= +if test "x$use_dnstap" != "xno"; then + if ! $use_threads; then + as_fn_error $? "Dnstap requires threads." "$LINENO" 5 + fi + # Extract the first word of "protoc-c", so it can be a program name with args. +set dummy protoc-c; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PROTOC_C+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PROTOC_C in + [\\/]* | ?:[\\/]*) + ac_cv_path_PROTOC_C="$PROTOC_C" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PROTOC_C="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PROTOC_C=$ac_cv_path_PROTOC_C +if test -n "$PROTOC_C"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROTOC_C" >&5 +$as_echo "$PROTOC_C" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -z "$PROTOC_C"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: The protoc-c program was not found." >&5 +$as_echo "$as_me: WARNING: The protoc-c program was not found." >&2;} + fi + +# Check whether --with-protobuf-c was given. +if test "${with_protobuf_c+set}" = set; then : + withval=$with_protobuf_c; + # workaround for protobuf-c includes at old dir + # before protobuf-c-1.0.0 + if test -f $withval/include/google/protobuf-c/protobuf-c.h + then + CFLAGS="$CFLAGS -I$withval/include/google" + else + CFLAGS="$CFLAGS -I$withval/include" + fi + LDFLAGS="$LDFLAGS -L$withval/lib" + +else + + # workaround for protobuf-c includes at old dir + # before protobuf-c-1.0.0 + if test -f /usr/include/google/protobuf-c/protobuf-c.h + then + CFLAGS="$CFLAGS -I/usr/include/google" + else + if test -f /usr/local/include/google/protobuf-c/protobuf-c.h + then + CFLAGS="$CFLAGS -I/usr/local/include/google" + LDFLAGS="$LDFLAGS -L/usr/local/lib" + fi + fi + +fi + + +# Check whether --with-libfstrm was given. +if test "${with_libfstrm+set}" = set; then : + withval=$with_libfstrm; + CFLAGS="$CFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing fstrm_iothr_init" >&5 +$as_echo_n "checking for library containing fstrm_iothr_init... " >&6; } +if ${ac_cv_search_fstrm_iothr_init+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char fstrm_iothr_init (); +int +main () +{ +return fstrm_iothr_init (); + ; + return 0; +} +_ACEOF +for ac_lib in '' fstrm; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_fstrm_iothr_init=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_fstrm_iothr_init+:} false; then : + break +fi +done +if ${ac_cv_search_fstrm_iothr_init+:} false; then : + +else + ac_cv_search_fstrm_iothr_init=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_fstrm_iothr_init" >&5 +$as_echo "$ac_cv_search_fstrm_iothr_init" >&6; } +ac_res=$ac_cv_search_fstrm_iothr_init +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + as_fn_error $? "The fstrm library was not found. Please install fstrm!" "$LINENO" 5 +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing protobuf_c_message_pack" >&5 +$as_echo_n "checking for library containing protobuf_c_message_pack... " >&6; } +if ${ac_cv_search_protobuf_c_message_pack+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char protobuf_c_message_pack (); +int +main () +{ +return protobuf_c_message_pack (); + ; + return 0; +} +_ACEOF +for ac_lib in '' protobuf-c; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_protobuf_c_message_pack=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_protobuf_c_message_pack+:} false; then : + break +fi +done +if ${ac_cv_search_protobuf_c_message_pack+:} false; then : + +else + ac_cv_search_protobuf_c_message_pack=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_protobuf_c_message_pack" >&5 +$as_echo "$ac_cv_search_protobuf_c_message_pack" >&6; } +ac_res=$ac_cv_search_protobuf_c_message_pack +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + as_fn_error $? "The protobuf-c library was not found. Please install protobuf-c!" "$LINENO" 5 +fi + + + +$as_echo "#define HAVE_DNSTAP 1" >>confdefs.h + + DNSTAP=dnstap + DNSTAPSRCS='${DNSTAPSRCS}' + DNSTAPOBJS='${DNSTAPOBJS}' + DNSTAPTARGETS='${DNSTAPTARGETS}' + DNSTAPMAN='${DNSTAPMAN}' + DNSTAPHTML='${DNSTAPHTML}' +fi + + + + + + + # # The following sets up how non-blocking i/o is established. # Sunos, cygwin and solaris 2.x (x<5) require special handling. @@ -24603,6 +24856,8 @@ if $use_threads; then fi fi test "$use_tuning" = "large" && echo " Large-system tuning (--with-tuning)" +test "$use_dnstap" = "no" || \ + echo " Allow 'dnstap' packet logging (--enable-dnstap)" test "$use_geoip" = "no" || echo " GeoIP access control (--with-geoip)" test "$use_gssapi" = "no" || echo " GSS-API (--with-gssapi)" if test "$enable_full_report" = "yes" -o "$with_cc_alg" != "aes"; then @@ -24675,6 +24930,8 @@ test "$enable_ipv6" = "no" -o "$found_ipv6" = "no" && \ echo " IPv6 support (--enable-ipv6)" test "$use_tuning" = "large" || echo " Large-system tuning (--with-tuning)" +test "$use_dnstap" = "no" && \ + echo " Allow 'dnstap' packet logging (--enable-dnstap)" test "$use_geoip" = "no" && echo " GeoIP access control (--with-geoip)" test "$use_gssapi" = "no" && echo " GSS-API (--with-gssapi)" diff --git a/configure.in b/configure.in index 2d4a038af2..946f0240da 100644 --- a/configure.in +++ b/configure.in @@ -3929,6 +3929,80 @@ case "$enable_filter" in ;; esac +# +# Activate dnstap? +# +AC_ARG_ENABLE(dnstap, + [ --enable-dnstap enable dnstap support (requires fstrm, protobuf-c)], + use_dnstap=$enableval, + use_dnstap=no) + +DNSTAP= +DNSTAPSRCS= +DNSTAPOBJS= +DNSTAPTARGETS= +DNSTAPMAN= +DNSTAPHTML= +if test "x$use_dnstap" != "xno"; then + if ! $use_threads; then + AC_MSG_ERROR([Dnstap requires threads.]) + fi + AC_PATH_PROG([PROTOC_C], [protoc-c]) + if test -z "$PROTOC_C"; then + AC_MSG_WARN([The protoc-c program was not found.]) + fi + AC_ARG_WITH([protobuf-c], + AC_HELP_STRING([--with-protobuf-c=path], + [Path where protobuf-c is installed, for dnstap]), [ + # workaround for protobuf-c includes at old dir + # before protobuf-c-1.0.0 + if test -f $withval/include/google/protobuf-c/protobuf-c.h + then + CFLAGS="$CFLAGS -I$withval/include/google" + else + CFLAGS="$CFLAGS -I$withval/include" + fi + LDFLAGS="$LDFLAGS -L$withval/lib" + ], [ + # workaround for protobuf-c includes at old dir + # before protobuf-c-1.0.0 + if test -f /usr/include/google/protobuf-c/protobuf-c.h + then + CFLAGS="$CFLAGS -I/usr/include/google" + else + if test -f /usr/local/include/google/protobuf-c/protobuf-c.h + then + CFLAGS="$CFLAGS -I/usr/local/include/google" + LDFLAGS="$LDFLAGS -L/usr/local/lib" + fi + fi + ]) + AC_ARG_WITH([libfstrm], AC_HELP_STRING([--with-libfstrm=path], + [Path where libfstrm is installed, for dnstap]), [ + CFLAGS="$CFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + ]) + + AC_SEARCH_LIBS([fstrm_iothr_init], [fstrm], [], + AC_MSG_ERROR([The fstrm library was not found. Please install fstrm!])) + AC_SEARCH_LIBS([protobuf_c_message_pack], [protobuf-c], [], + AC_MSG_ERROR([The protobuf-c library was not found. Please install protobuf-c!])) + + AC_DEFINE(HAVE_DNSTAP, 1, [Define to 1 to enable dnstap support]) + DNSTAP=dnstap + DNSTAPSRCS='${DNSTAPSRCS}' + DNSTAPOBJS='${DNSTAPOBJS}' + DNSTAPTARGETS='${DNSTAPTARGETS}' + DNSTAPMAN='${DNSTAPMAN}' + DNSTAPHTML='${DNSTAPHTML}' +fi +AC_SUBST(DNSTAP) +AC_SUBST(DNSTAPSRCS) +AC_SUBST(DNSTAPOBJS) +AC_SUBST(DNSTAPTARGETS) +AC_SUBST(DNSTAPMAN) +AC_SUBST(DNSTAPHTML) + # # The following sets up how non-blocking i/o is established. # Sunos, cygwin and solaris 2.x (x<5) require special handling. @@ -4846,6 +4920,8 @@ if $use_threads; then fi fi test "$use_tuning" = "large" && echo " Large-system tuning (--with-tuning)" +test "$use_dnstap" = "no" || \ + echo " Allow 'dnstap' packet logging (--enable-dnstap)" test "$use_geoip" = "no" || echo " GeoIP access control (--with-geoip)" test "$use_gssapi" = "no" || echo " GSS-API (--with-gssapi)" if test "$enable_full_report" = "yes" -o "$with_cc_alg" != "aes"; then @@ -4918,6 +4994,8 @@ test "$enable_ipv6" = "no" -o "$found_ipv6" = "no" && \ echo " IPv6 support (--enable-ipv6)" test "$use_tuning" = "large" || echo " Large-system tuning (--with-tuning)" +test "$use_dnstap" = "no" && \ + echo " Allow 'dnstap' packet logging (--enable-dnstap)" test "$use_geoip" = "no" && echo " GeoIP access control (--with-geoip)" test "$use_gssapi" = "no" && echo " GSS-API (--with-gssapi)" diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index 636d2d2d3b..8af74ca92f 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -4447,6 +4447,10 @@ badresp:1,adberr:0,findfail:0,valfail:0] hostname hostname_string; server-id server_id_string; directory path_name; + dnstap { message_type; ... }; + dnstap-output ( file | unix ) path_name; + dnstap-identity ( string | hostname | none ); + dnstap-version ( string | none ); geoip-directory path_name; key-directory path_name; managed-keys-directory path_name; @@ -4849,6 +4853,126 @@ badresp:1,adberr:0,findfail:0,valfail:0] + + dnstap + + + dnstap is a fast, flexible method + for capturing and logging DNS traffic. Developed by + Robert Edmonds at Farsight Security, Inc., and supported + by multiple DNS implementations, dnstap + uses + libfstrm (a lightweight high-speed + framing library, see + https://github.com/farsightsec/fstrm) to send + event payloads which are encoded using Protocol Buffers + (libprotobuf-c, a mechanism for + serializing structured data developed + by Google, Inc.; see + https://developers.google.com/protocol-buffers). + + + To enable dnstap at compile time, + the fstrm and protobuf-c + libraries must be available, and BIND must be configured with + . + + + The dnstap option is a bracketed list + of message types to be logged. These may be set differently + for each view. Supported types are client, + auth, resolver, and + forwarder. Specifying type + all will cause all dnstap + messages to be logged, regardless of type. + + + Each type may take an additional argument to indicate whether + to log query messages or + response messages; if not specified, + both queries and responses are logged. + + + Example: To log all authoritative queries and responses, + recursive client responses, and upstream queries sent by + the resolver, use: +dnstap { + auth; + client response; + resolver query; +}; + + + + Logged dnstap messages can be parsed + using the dnstap-read utility (see + for details). + + + For more information on dnstap, see + http://dnstap.info. + + + + + + dnstap-output + + + Configures the path to which the dnstap + frame stream will be sent if dnstap + is enabled at compile time and active. + + + The first argument is either file or + unix, indicating whether the destination + is a file or a UNIX domain socket. The second argument + is the path of the file or socket. (Note: when using a + socket, dnstap messages will + only be sent if another process such as + fstrm_capture + (provided with libfstrm) is listening on + the socket.) + + + dnstap-output can only be set globally + in options. Currently, it can only be + set once while named is running; + once set, it cannot be changed by + rndc reload or + rndc reconfig. + + + + + + dnstap-identity + + + Specifies an identity string to send in + dnstap messages. If set to + hostname, which is the default, the + server's hostname will be sent. If set to + none, no identity string will be sent. + + + + + + dnstap-version + + + Specifies a version string to send in + dnstap messages. The default is the + version number of the BIND release. If set to + none, no version string will be sent. + + + + geoip-directory @@ -18614,8 +18738,11 @@ allow-query { !{ !10/8; any; }; key example; }; + + + diff --git a/doc/arm/man.dnstap-read.html b/doc/arm/man.dnstap-read.html new file mode 100644 index 0000000000..2e917979dc --- /dev/null +++ b/doc/arm/man.dnstap-read.html @@ -0,0 +1,118 @@ + + + + + +dnstap-read + + + + + + + + +
+
+
+

Name

+

dnstap-read — print dnstap data in human-readable form

+
+
+

Synopsis

+

dnstap-read [-m] [-p] [-y] {file}

+
+
+

DESCRIPTION

+

+ dnstap-read + reads dnstap data from a specified file + and prints it in a human-readable format. By default, + dnstap data is printed in a short summary + format, but if the -y option is specified, + then a longer and more detailed YAML format is used instead. +

+
+
+

OPTIONS

+
+
-m
+

+ Trace memory allocations; used for debugging memory leaks. +

+
-p
+

+ After printing the dnstap data, print + the text form of the DNS message that was encapsulated in the + dnstap frame. +

+
-y
+

+ Print dnstap data in a detailed YAML + format. Implies -p. +

+
+
+
+

SEE ALSO

+

+ named(8), + nsupdate(8), + BIND 9 Administrator Reference Manual. +

+
+
+

AUTHOR

+

Internet Systems Consortium +

+
+
+ +

BIND 9.11.0pre-alpha

+ + diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 4906f30a83..392b0386f3 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -197,6 +197,29 @@ of queries affected by these quotas.
+ + + Added support for dnstap, a fast, + flexible method for capturing and logging DNS traffic, + developed by Robert Edmonds at Farsight Security, Inc., + whose assistance is gratefully acknowledged. + + + To enable dnstap at compile time, + the fstrm and protobuf-c + libraries must be available, and BIND must be configured with + . + + + A new utility dnstap-read has been added + to allow dnstap data to be presented in + a human-readable format. + + + For more information on dnstap, see + http://dnstap.info. + + New statistics counters have been added to track traffic diff --git a/lib/dns/Makefile.in b/lib/dns/Makefile.in index be539b60da..f219cfc2c1 100644 --- a/lib/dns/Makefile.in +++ b/lib/dns/Makefile.in @@ -61,6 +61,8 @@ DSTOBJS = @DST_EXTRA_OBJS@ @OPENSSLLINKOBJS@ @PKCS11LINKOBJS@ \ GEOIPLINKOBJS = geoip.@O@ +DNSTAPOBJS = dnstap.@O@ dnstap.pb-c.@O@ + # Alphabetically DNSOBJS = acache.@O@ acl.@O@ adb.@O@ badcache.@O@ byaddr.@O@ \ cache.@O@ callbacks.@O@ clientinfo.@O@ compress.@O@ \ @@ -82,7 +84,7 @@ DNSOBJS = acache.@O@ acl.@O@ adb.@O@ badcache.@O@ byaddr.@O@ \ PORTDNSOBJS = client.@O@ ecdb.@O@ OBJS= ${DNSOBJS} ${OTHEROBJS} ${DSTOBJS} ${PORTDNSOBJS} \ - @GEOIPLINKOBJS@ + @DNSTAPOBJS@ @GEOIPLINKOBJS@ # Alphabetically @@ -100,6 +102,8 @@ DSTSRCS = @DST_EXTRA_SRCS@ @OPENSSLLINKSRCS@ @PKCS11LINKSRCS@ \ GEOIPLINKSRCS = geoip.c +DNSTAPSRCS = dnstap.c dnstap.pb-c.c + DNSSRCS = acache.c acl.c adb.c badcache. byaddr.c \ cache.c callbacks.c clientinfo.c compress.c \ db.c dbiterator.c dbtable.c diff.c dispatch.c \ @@ -117,7 +121,7 @@ DNSSRCS = acache.c acl.c adb.c badcache. byaddr.c \ version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS} PORTDNSSRCS = client.c ecdb.c -SRCS = ${DSTSRCS} ${DNSSRCS} ${PORTDNSSRCS} @GEOIPLINKSRCS@ +SRCS = ${DSTSRCS} ${DNSSRCS} ${PORTDNSSRCS} @DNSTAPSRCS@ @GEOIPLINKSRCS@ SUBDIRS = include TARGETS = include/dns/enumtype.h include/dns/enumclass.h \ @@ -129,6 +133,8 @@ DEPENDEXTRA = ./gen -F include/dns/rdatastruct.h \ @BIND9_MAKE_RULES@ +PROTOC_C = @PROTOC_C@ + version.@O@: version.c ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \ -DVERSION=\"${VERSION}\" \ @@ -201,4 +207,13 @@ subdirs: include/dns/enumtype.h include/dns/enumclass.h \ ${OBJS}: include/dns/enumtype.h include/dns/enumclass.h \ include/dns/rdatastruct.h +# dnstap +dnstap.@O@: dnstap.c dnstap.pb-c.c + +dnstap.pb-c.c dnstap.pb-c.h include/dns/dnstap.pb-c.h: dnstap.proto + $(PROTOC_C) --c_out=. dnstap.proto + cp -f dnstap.pb-c.h include/dns + +dnstap.pb-c.@O@: dnstap.pb-c.c + spnego.@O@: spnego_asn1.c spnego.h diff --git a/lib/dns/dnstap.c b/lib/dns/dnstap.c new file mode 100644 index 0000000000..793cbc0e71 --- /dev/null +++ b/lib/dns/dnstap.c @@ -0,0 +1,1047 @@ +/* + * Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Copyright (c) 2013-2014, Farsight Security, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*! \file */ + +#include + +#ifndef HAVE_DNSTAP +#error DNSTAP not configured. +#endif /* HAVE_DNSTAP */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DTENV_MAGIC ISC_MAGIC('D', 't', 'n', 'v') +#define VALID_DTENV(env) ISC_MAGIC_VALID(env, DTENV_MAGIC) + +#define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap" +#define DNSTAP_INITIAL_BUF_SIZE 256 + +typedef struct dns_dtmsg { + void *buf; + size_t len; + Dnstap__Dnstap d; + Dnstap__Message m; +} dns_dtmsg_t; + +#define CHECK(x) do { \ + result = (x); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +static isc_mutex_t dt_mutex; +static isc_boolean_t dt_initialized = ISC_FALSE; +static isc_thread_key_t dt_key; +static isc_once_t mutex_once = ISC_ONCE_INIT; +static isc_mem_t *dt_mctx = NULL; + +static void +mutex_init(void) { + RUNTIME_CHECK(isc_mutex_init(&dt_mutex) == ISC_R_SUCCESS); +} + +static void +dtfree(void *arg) { + UNUSED(arg); + isc_thread_key_setspecific(dt_key, NULL); +} + +static isc_result_t +dt_init(void) { + isc_result_t result; + + result = isc_once_do(&mutex_once, mutex_init); + if (result != ISC_R_SUCCESS) + return (result); + + if (dt_initialized) + return (ISC_R_SUCCESS); + + LOCK(&dt_mutex); + if (!dt_initialized) { + int ret; + + if (dt_mctx == NULL) + result = isc_mem_create2(0, 0, &dt_mctx, 0); + if (result != ISC_R_SUCCESS) + goto unlock; + isc_mem_setname(dt_mctx, "dt", NULL); + isc_mem_setdestroycheck(dt_mctx, ISC_FALSE); + + ret = isc_thread_key_create(&dt_key, dtfree); + if (ret == 0) + dt_initialized = ISC_TRUE; + else + result = ISC_R_FAILURE; + } +unlock: + UNLOCK(&dt_mutex); + + return (result); +} + +isc_result_t +dns_dt_create(isc_mem_t *mctx, dns_dtmode_t mode, const char *path, + unsigned int workers, dns_dtenv_t **envp) +{ + isc_result_t result = ISC_R_SUCCESS; + fstrm_res res; + struct fstrm_iothr_options *fopt = NULL; + struct fstrm_unix_writer_options *fuwopt = NULL; + struct fstrm_file_options *ffwopt = NULL; + struct fstrm_writer_options *fwopt = NULL; + struct fstrm_writer *fw = NULL; + dns_dtenv_t *env = NULL; + + REQUIRE(path != NULL); + REQUIRE(envp != NULL && *envp == NULL); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, + DNS_LOGMODULE_DNSTAP, ISC_LOG_INFO, + "opening dnstap destination '%s'", path); + + env = isc_mem_get(mctx, sizeof(dns_dtenv_t)); + if (env == NULL) + CHECK(ISC_R_NOMEMORY); + + memset(env, 0, sizeof(dns_dtenv_t)); + + CHECK(isc_refcount_init(&env->refcount, 1)); + + fwopt = fstrm_writer_options_init(); + if (fwopt == NULL) + CHECK(ISC_R_NOMEMORY); + + res = fstrm_writer_options_add_content_type(fwopt, + DNSTAP_CONTENT_TYPE, + sizeof(DNSTAP_CONTENT_TYPE) - 1); + if (res != fstrm_res_success) + CHECK(ISC_R_FAILURE); + + + if (mode == dns_dtmode_file) { + ffwopt = fstrm_file_options_init(); + if (ffwopt != NULL) { + fstrm_file_options_set_file_path(ffwopt, path); + fw = fstrm_file_writer_init(ffwopt, fwopt); + } + } else if (mode == dns_dtmode_unix) { + fuwopt = fstrm_unix_writer_options_init(); + if (fuwopt != NULL) { + fstrm_unix_writer_options_set_socket_path(fuwopt, path); + fw = fstrm_unix_writer_init(fuwopt, fwopt); + } + } else + CHECK(ISC_R_FAILURE); + + if (fw == NULL) + CHECK(ISC_R_FAILURE); + + fopt = fstrm_iothr_options_init(); + fstrm_iothr_options_set_num_input_queues(fopt, workers); + + env->iothr = fstrm_iothr_init(fopt, &fw); + if (env->iothr == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, + DNS_LOGMODULE_DNSTAP, ISC_LOG_WARNING, + "unable to initialize dnstap I/O thread"); + fstrm_writer_destroy(&fw); + CHECK(ISC_R_FAILURE); + } + + isc_mem_attach(mctx, &env->mctx); + + env->magic = DTENV_MAGIC; + *envp = env; + + cleanup: + if (fopt != NULL) + fstrm_iothr_options_destroy(&fopt); + + if (fuwopt != NULL) + fstrm_unix_writer_options_destroy(&fuwopt); + + if (fwopt != NULL) + fstrm_writer_options_destroy(&fwopt); + + if (result != ISC_R_SUCCESS) { + if (env->mctx != NULL) + isc_mem_detach(&env->mctx); + if (env != NULL) + isc_mem_put(mctx, env, sizeof(dns_dtenv_t)); + } + + return (result); +} + +static isc_result_t +toregion(dns_dtenv_t *env, isc_region_t *r, const char *str) { + unsigned char *p = NULL; + + REQUIRE(r != NULL); + + if (str != NULL) { + p = (unsigned char *) isc_mem_strdup(env->mctx, str); + if (p == NULL) + return (ISC_R_NOMEMORY); + } + + if (r->base != NULL) { + isc_mem_free(env->mctx, r->base); + r->length = 0; + } + + if (p != NULL) { + r->base = p; + r->length = strlen((char *) p); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +dns_dt_setidentity(dns_dtenv_t *env, const char *identity) { + REQUIRE(VALID_DTENV(env)); + + return (toregion(env, &env->identity, identity)); +} + +isc_result_t +dns_dt_setversion(dns_dtenv_t *env, const char *version) { + REQUIRE(VALID_DTENV(env)); + + return (toregion(env, &env->version, version)); +} + +static struct fstrm_iothr_queue * +dt_queue(dns_dtenv_t *env) { + isc_result_t result; + struct fstrm_iothr_queue *ioq; + + REQUIRE(VALID_DTENV(env)); + + result = dt_init(); + if (result != ISC_R_SUCCESS) + return (NULL); + + ioq = (struct fstrm_iothr_queue *) isc_thread_key_getspecific(dt_key); + if (ioq == NULL) { + ioq = fstrm_iothr_get_input_queue(env->iothr); + if (ioq != NULL) { + result = isc_thread_key_setspecific(dt_key, ioq); + if (result != ISC_R_SUCCESS) + ioq = NULL; + } + } + + return (ioq); +} + +void +dns_dt_attach(dns_dtenv_t *source, dns_dtenv_t **destp) { + REQUIRE(VALID_DTENV(source)); + REQUIRE(destp != NULL && *destp == NULL); + + isc_refcount_increment(&source->refcount, NULL); + *destp = source; +} + +static void +destroy(dns_dtenv_t **envp) { + dns_dtenv_t *env; + + REQUIRE(envp != NULL && VALID_DTENV(*envp)); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, + DNS_LOGMODULE_DNSTAP, ISC_LOG_INFO, + "closing dnstap"); + env = *envp; + + env->magic = 0; + + fstrm_iothr_destroy(&env->iothr); + + if (env->identity.base != NULL) { + isc_mem_free(env->mctx, env->identity.base); + env->identity.length = 0; + } + if (env->version.base != NULL) { + isc_mem_free(env->mctx, env->version.base); + env->version.length = 0; + } + + isc_mem_putanddetach(&env->mctx, env, sizeof(*env)); + + *envp = NULL; +} + +void +dns_dt_detach(dns_dtenv_t **envp) { + unsigned int refs; + dns_dtenv_t *env = *envp; + REQUIRE(VALID_DTENV(env)); + + isc_refcount_decrement(&env->refcount, &refs); + if (refs == 0) + destroy(&env); + + *envp = NULL; +} + +static isc_result_t +pack_dt(const Dnstap__Dnstap *d, void **buf, size_t *sz) { + ProtobufCBufferSimple sbuf; + + REQUIRE(d != NULL); + REQUIRE(sz != NULL); + + memset(&sbuf, 0, sizeof(sbuf)); + sbuf.base.append = protobuf_c_buffer_simple_append; + sbuf.len = 0; + sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE; + + /* Need to use malloc() here because protobuf uses free() */ + sbuf.data = malloc(sbuf.alloced); + if (sbuf.data == NULL) + return (ISC_R_NOMEMORY); + sbuf.must_free_data = 1; + + *sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *) &sbuf); + if (sbuf.data == NULL) + return (ISC_R_FAILURE); + *buf = sbuf.data; + + return (ISC_R_SUCCESS); +} + +static void +send_dt(dns_dtenv_t *env, void *buf, size_t len) { + struct fstrm_iothr_queue *ioq; + fstrm_res res; + + REQUIRE(env != NULL); + + if (buf == NULL) + return; + + ioq = dt_queue(env); + if (ioq == NULL) { + free(buf); + return; + } + + res = fstrm_iothr_submit(env->iothr, ioq, buf, len, + fstrm_free_wrapper, NULL); + if (res != fstrm_res_success) + free(buf); +} + +static void +init_msg(dns_dtenv_t *env, dns_dtmsg_t *dm, Dnstap__Message__Type mtype) { + memset(dm, 0, sizeof(*dm)); + dm->d.base.descriptor = &dnstap__dnstap__descriptor; + dm->m.base.descriptor = &dnstap__message__descriptor; + dm->d.type = DNSTAP__DNSTAP__TYPE__MESSAGE; + dm->d.message = &dm->m; + dm->m.type = mtype; + + if (env->identity.length != 0) { + dm->d.identity.data = env->identity.base; + dm->d.identity.len = env->identity.length; + dm->d.has_identity = ISC_TRUE; + } + + if (env->version.length != 0) { + dm->d.version.data = env->version.base; + dm->d.version.len = env->version.length; + dm->d.has_version = ISC_TRUE; + } +} + +static Dnstap__Message__Type +dnstap_type(dns_dtmsgtype_t msgtype) { + switch (msgtype) { + case DNS_DTTYPE_SQ: + return (DNSTAP__MESSAGE__TYPE__STUB_QUERY); + case DNS_DTTYPE_SR: + return (DNSTAP__MESSAGE__TYPE__STUB_RESPONSE); + case DNS_DTTYPE_CQ: + return (DNSTAP__MESSAGE__TYPE__CLIENT_QUERY); + case DNS_DTTYPE_CR: + return (DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE); + case DNS_DTTYPE_AQ: + return (DNSTAP__MESSAGE__TYPE__AUTH_QUERY); + case DNS_DTTYPE_AR: + return (DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE); + case DNS_DTTYPE_RQ: + return (DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY); + case DNS_DTTYPE_RR: + return (DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE); + case DNS_DTTYPE_FQ: + return (DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY); + case DNS_DTTYPE_FR: + return (DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE); + case DNS_DTTYPE_TQ: + return (DNSTAP__MESSAGE__TYPE__TOOL_QUERY); + case DNS_DTTYPE_TR: + return (DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE); + default: + INSIST(0); + } +} + +static void +cpbuf(isc_buffer_t *buf, ProtobufCBinaryData *p, protobuf_c_boolean *has) { + p->data = isc_buffer_base(buf); + p->len = isc_buffer_usedlength(buf); + *has = 1; +} + +static void +setaddr(dns_dtmsg_t *dm, isc_sockaddr_t *sa, isc_boolean_t tcp, + ProtobufCBinaryData *addr, protobuf_c_boolean *has_addr, + isc_uint32_t *port, protobuf_c_boolean *has_port) +{ + int family = isc_sockaddr_pf(sa); + + if (family != AF_INET6 && family != AF_INET) + return; + + if (family == AF_INET6) { + dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET6; + addr->data = sa->type.sin6.sin6_addr.s6_addr; + addr->len = 16; + *port = ntohs(sa->type.sin6.sin6_port); + } else { + dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET; + addr->data = (uint8_t *) &sa->type.sin.sin_addr.s_addr; + addr->len = 4; + *port = ntohs(sa->type.sin.sin_port); + } + + if (tcp) + dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP; + else + dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP; + + dm->m.has_socket_protocol = 1; + dm->m.has_socket_family = 1; + *has_addr = 1; + *has_port = 1; +} + +void +dns_dt_send(dns_view_t *view, dns_dtmsgtype_t msgtype, + isc_sockaddr_t *sa, isc_boolean_t tcp, isc_region_t *zone, + isc_time_t *qtime, isc_time_t *rtime, isc_buffer_t *buf) +{ + isc_time_t now, *t; + dns_dtmsg_t dm; + + REQUIRE(DNS_VIEW_VALID(view)); + + if ((msgtype & view->dttypes) == 0) + return; + + if (view->dtenv == NULL) + return; + + REQUIRE(VALID_DTENV(view->dtenv)); + + TIME_NOW(&now); + t = &now; + + init_msg(view->dtenv, &dm, dnstap_type(msgtype)); + + /* Query/response times */ + switch (msgtype) { + case DNS_DTTYPE_AR: + case DNS_DTTYPE_CR: + case DNS_DTTYPE_RR: + case DNS_DTTYPE_FR: + case DNS_DTTYPE_SR: + case DNS_DTTYPE_TR: + if (rtime != NULL) + t = rtime; + + dm.m.response_time_sec = isc_time_seconds(t); + dm.m.has_response_time_sec = 1; + dm.m.response_time_nsec = isc_time_nanoseconds(t); + dm.m.has_response_time_nsec = 1; + + cpbuf(buf, &dm.m.response_message, &dm.m.has_response_message); + + /* Types RR and FR get both query and response times */ + if (msgtype == DNS_DTTYPE_CR || msgtype == DNS_DTTYPE_AR) + break; + + /* FALLTHROUGH */ + case DNS_DTTYPE_AQ: + case DNS_DTTYPE_CQ: + case DNS_DTTYPE_FQ: + case DNS_DTTYPE_RQ: + case DNS_DTTYPE_SQ: + case DNS_DTTYPE_TQ: + if (qtime != NULL) + t = qtime; + + dm.m.query_time_sec = isc_time_seconds(t); + dm.m.has_query_time_sec = 1; + dm.m.query_time_nsec = isc_time_nanoseconds(t); + dm.m.has_query_time_nsec = 1; + + cpbuf(buf, &dm.m.query_message, &dm.m.has_query_message); + break; + default: + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, + DNS_LOGMODULE_DNSTAP, ISC_LOG_ERROR, + "invalid dnstap message type %d", msgtype); + return; + } + + /* Zone/bailiwick */ + switch (msgtype) { + case DNS_DTTYPE_AR: + case DNS_DTTYPE_RQ: + case DNS_DTTYPE_RR: + case DNS_DTTYPE_FQ: + case DNS_DTTYPE_FR: + if (zone != NULL && zone->base != NULL && zone->length != 0) { + dm.m.query_zone.data = zone->base; + dm.m.query_zone.len = zone->length; + dm.m.has_query_zone = 1; + } + break; + default: + break; + } + + switch (msgtype) { + case DNS_DTTYPE_RQ: + case DNS_DTTYPE_RR: + case DNS_DTTYPE_FQ: + case DNS_DTTYPE_FR: + setaddr(&dm, sa, tcp, + &dm.m.response_address, &dm.m.has_response_address, + &dm.m.response_port, &dm.m.has_response_port); + break; + default: + setaddr(&dm, sa, tcp, + &dm.m.query_address, &dm.m.has_query_address, + &dm.m.query_port, &dm.m.has_query_port); + } + + if (pack_dt(&dm.d, &dm.buf, &dm.len) == ISC_R_SUCCESS) + send_dt(view->dtenv, dm.buf, dm.len); + + return; +} + +void +dns_dt_shutdown() { + if (dt_mctx != NULL) + isc_mem_detach(&dt_mctx); +} + +static isc_result_t +putstr(isc_buffer_t **b, const char *str) { + isc_result_t result; + + result = isc_buffer_reserve(b, strlen(str)); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOSPACE); + + isc_buffer_putstr(*b, str); + return (ISC_R_SUCCESS); +} + +static isc_result_t +putaddr(isc_buffer_t **b, isc_region_t *ip) { + char buf[64]; + + if (ip->length == 4) { + if (!inet_ntop(AF_INET, ip->base, buf, sizeof(buf))) + return (ISC_R_FAILURE); + } else if (ip->length == 16) { + if (!inet_ntop(AF_INET6, ip->base, buf, sizeof(buf))) + return (ISC_R_FAILURE); + } else + return (ISC_R_BADADDRESSFORM); + + return (putstr(b, buf)); +} + +static isc_boolean_t +dnstap_file(struct fstrm_reader *r) { + fstrm_res res; + const struct fstrm_control *control = NULL; + const uint8_t *rtype = NULL; + size_t dlen = strlen(DNSTAP_CONTENT_TYPE), rlen = 0; + size_t n = 0; + + res = fstrm_reader_get_control(r, FSTRM_CONTROL_START, &control); + if (res != fstrm_res_success) + return (ISC_FALSE); + + res = fstrm_control_get_num_field_content_type(control, &n); + if (res != fstrm_res_success) + return (ISC_FALSE); + if (n > 0) { + res = fstrm_control_get_field_content_type(control, 0, + &rtype, &rlen); + if (res != fstrm_res_success) + return (ISC_FALSE); + + if (rlen != dlen) + return (ISC_FALSE); + + if (memcmp(DNSTAP_CONTENT_TYPE, rtype, dlen) == 0) + return (ISC_TRUE); + } + + return (ISC_FALSE); +} + +isc_result_t +dns_dt_open(const char *filename, dns_dtmode_t mode, dns_dthandle_t *handle) { + isc_result_t result; + struct fstrm_file_options *fopt; + fstrm_res res; + + REQUIRE(handle != NULL); + + handle->mode = mode; + + switch(mode) { + case dns_dtmode_file: + fopt = fstrm_file_options_init(); + if (fopt == NULL) + CHECK(ISC_R_NOMEMORY); + + fstrm_file_options_set_file_path(fopt, filename); + + handle->reader = fstrm_file_reader_init(fopt, NULL); + if (handle->reader == NULL) + CHECK(ISC_R_NOMEMORY); + + res = fstrm_reader_open(handle->reader); + if (res != fstrm_res_success) + CHECK(ISC_R_FAILURE); + + if (!dnstap_file(handle->reader)) + CHECK(DNS_R_BADDNSTAP); + break; + case dns_dtmode_unix: + return (ISC_R_NOTIMPLEMENTED); + default: + INSIST(0); + } + + result = ISC_R_SUCCESS; + + cleanup: + if (result != ISC_R_SUCCESS && handle->reader != NULL) { + fstrm_reader_destroy(&handle->reader); + handle->reader = NULL; + } + if (fopt != NULL) + fstrm_file_options_destroy(&fopt); + return (result); +} + +isc_result_t +dns_dt_getframe(dns_dthandle_t *handle, isc_uint8_t **bufp, size_t *sizep) { + const isc_uint8_t *data; + fstrm_res res; + + REQUIRE(handle != NULL); + REQUIRE(bufp != NULL); + REQUIRE(sizep != NULL); + + data = (const isc_uint8_t *) *bufp; + + res = fstrm_reader_read(handle->reader, &data, sizep); + switch (res) { + case fstrm_res_success: + if (data == NULL) + return (ISC_R_FAILURE); + DE_CONST(data, *bufp); + return (ISC_R_SUCCESS); + case fstrm_res_stop: + return (ISC_R_NOMORE); + default: + return (ISC_R_FAILURE); + } +} + +void +dns_dt_close(dns_dthandle_t *handle) { + REQUIRE(handle != NULL); + + if (handle->reader != NULL) { + fstrm_reader_destroy(&handle->reader); + handle->reader = NULL; + } +} + +isc_result_t +dns_dt_parse(isc_mem_t *mctx, isc_region_t *src, dns_dtdata_t **destp) { + isc_result_t result; + Dnstap__Message *m; + dns_dtdata_t *d = NULL; + isc_buffer_t b; + + REQUIRE(src != NULL); + REQUIRE(destp != NULL && *destp == NULL); + + d = isc_mem_get(mctx, sizeof(*d)); + if (d == NULL) + return (ISC_R_NOMEMORY); + + memset(d, 0, sizeof(*d)); + isc_mem_attach(mctx, &d->mctx); + + d->frame = dnstap__dnstap__unpack(NULL, src->length, src->base); + if (d->frame == NULL) + CHECK(ISC_R_NOMEMORY); + + if (d->frame->type != DNSTAP__DNSTAP__TYPE__MESSAGE) + CHECK(DNS_R_BADDNSTAP); + + m = d->frame->message; + + /* Message type */ + switch (m->type) { + case DNSTAP__MESSAGE__TYPE__AUTH_QUERY: + d->type = DNS_DTTYPE_AQ; + break; + case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE: + d->type = DNS_DTTYPE_AR; + break; + case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY: + d->type = DNS_DTTYPE_CQ; + break; + case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE: + d->type = DNS_DTTYPE_CR; + break; + case DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY: + d->type = DNS_DTTYPE_FQ; + break; + case DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE: + d->type = DNS_DTTYPE_FR; + break; + case DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY: + d->type = DNS_DTTYPE_RQ; + break; + case DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE: + d->type = DNS_DTTYPE_RR; + break; + case DNSTAP__MESSAGE__TYPE__STUB_QUERY: + d->type = DNS_DTTYPE_SQ; + break; + case DNSTAP__MESSAGE__TYPE__STUB_RESPONSE: + d->type = DNS_DTTYPE_SR; + break; + case DNSTAP__MESSAGE__TYPE__TOOL_QUERY: + d->type = DNS_DTTYPE_TQ; + break; + case DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE: + d->type = DNS_DTTYPE_TR; + break; + default: + CHECK(DNS_R_BADDNSTAP); + } + + /* Query? */ + if ((d->type & DNS_DTTYPE_QUERY) != 0) + d->query = ISC_TRUE; + else + d->query = ISC_FALSE; + + /* Parse DNS message */ + if (d->query && m->has_query_message) { + d->msgdata.base = m->query_message.data; + d->msgdata.length = m->query_message.len; + } else if (!d->query && m->has_response_message) { + d->msgdata.base = m->response_message.data; + d->msgdata.length = m->response_message.len; + } + + isc_buffer_init(&b, d->msgdata.base, d->msgdata.length); + isc_buffer_add(&b, d->msgdata.length); + CHECK(dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &d->msg)); + result = dns_message_parse(d->msg, &b, 0); + if (result != ISC_R_SUCCESS) { + if (result != DNS_R_RECOVERABLE) + dns_message_destroy(&d->msg); + result = ISC_R_SUCCESS; + } + + /* Timestamp */ + if (d->query) { + if (m->has_query_time_sec && m->has_query_time_nsec) + isc_time_set(&d->qtime, m->query_time_sec, + m->query_time_nsec); + } else { + if (m->has_response_time_sec && m->has_response_time_nsec) + isc_time_set(&d->rtime, m->response_time_sec, + m->response_time_nsec); + } + + /* Peer address */ + if (m->has_query_address) { + d->qaddr.base = m->query_address.data; + d->qaddr.length = m->query_address.len; + } + + if (m->has_response_address) { + d->raddr.base = m->response_address.data; + d->raddr.length = m->response_address.len; + } + + /* Socket protocol */ + if (m->has_socket_protocol) { + const ProtobufCEnumValue *type = + protobuf_c_enum_descriptor_get_value( + &dnstap__socket_protocol__descriptor, + m->socket_protocol); + if (type != NULL && + type->value == DNSTAP__SOCKET_PROTOCOL__TCP) + d->tcp = ISC_TRUE; + else + d->tcp = ISC_FALSE; + } + + /* Query tuple */ + if (d->msg != NULL) { + dns_name_t *name = NULL; + dns_rdataset_t *rdataset; + + CHECK(dns_message_firstname(d->msg, DNS_SECTION_QUESTION)); + dns_message_currentname(d->msg, DNS_SECTION_QUESTION, &name); + rdataset = ISC_LIST_HEAD(name->list); + + dns_name_format(name, d->namebuf, sizeof(d->namebuf)); + dns_rdatatype_format(rdataset->type, d->typebuf, + sizeof(d->typebuf)); + dns_rdataclass_format(rdataset->rdclass, d->classbuf, + sizeof(d->classbuf)); + } + + *destp = d; + + cleanup: + if (result != ISC_R_SUCCESS) + dns_dtdata_free(&d); + + return (result); +} + +isc_result_t +dns_dt_datatotext(dns_dtdata_t *d, isc_buffer_t **dest) { + isc_result_t result; + char buf[100]; + + REQUIRE(d != NULL); + REQUIRE(dest != NULL && *dest != NULL); + + memset(buf, 0, sizeof(buf)); + + /* Timestamp */ + if (d->query && !isc_time_isepoch(&d->qtime)) + isc_time_formattimestamp(&d->qtime, buf, sizeof(buf)); + else if (!d->query && !isc_time_isepoch(&d->rtime)) + isc_time_formattimestamp(&d->rtime, buf, sizeof(buf)); + + if (buf[0] == '\0') + CHECK(putstr(dest, "???\?-?\?-?? ??:??:??.??? ")); + else { + CHECK(putstr(dest, buf)); + CHECK(putstr(dest, " ")); + } + + /* Type mnemonic */ + switch (d->type) { + case DNS_DTTYPE_AQ: + CHECK(putstr(dest, "AQ ")); + break; + case DNS_DTTYPE_AR: + CHECK(putstr(dest, "AR ")); + break; + case DNS_DTTYPE_CQ: + CHECK(putstr(dest, "CQ ")); + break; + case DNS_DTTYPE_CR: + CHECK(putstr(dest, "CR ")); + break; + case DNS_DTTYPE_FQ: + CHECK(putstr(dest, "FQ ")); + break; + case DNS_DTTYPE_FR: + CHECK(putstr(dest, "FR ")); + break; + case DNS_DTTYPE_RQ: + CHECK(putstr(dest, "RQ ")); + break; + case DNS_DTTYPE_RR: + CHECK(putstr(dest, "RR ")); + break; + case DNS_DTTYPE_SQ: + CHECK(putstr(dest, "SQ ")); + break; + case DNS_DTTYPE_SR: + CHECK(putstr(dest, "SR ")); + break; + case DNS_DTTYPE_TQ: + CHECK(putstr(dest, "TQ ")); + break; + case DNS_DTTYPE_TR: + CHECK(putstr(dest, "TR ")); + break; + default: + return (DNS_R_BADDNSTAP); + } + + /* Peer address */ + switch (d->type) { + case DNS_DTTYPE_RQ: + case DNS_DTTYPE_RR: + case DNS_DTTYPE_FQ: + case DNS_DTTYPE_FR: + if (d->raddr.length != 0) + CHECK(putaddr(dest, &d->raddr)); + else + CHECK(putstr(dest, "?")); + break; + default: + if (d->qaddr.length != 0) + CHECK(putaddr(dest, &d->qaddr)); + else + CHECK(putstr(dest, "?")); + break; + } + + CHECK(putstr(dest, " ")); + + /* Protocol */ + if (d->tcp) + CHECK(putstr(dest, "TCP ")); + else + CHECK(putstr(dest, "UDP ")); + + /* Message size */ + if (d->msgdata.base != NULL) { + snprintf(buf, sizeof(buf), "%zdb ", (size_t) d->msgdata.length); + CHECK(putstr(dest, buf)); + } else + CHECK(putstr(dest, "0b ")); + + /* Query tuple */ + if (d->namebuf[0] == '\0') + CHECK(putstr(dest, "?/")); + else { + CHECK(putstr(dest, d->namebuf)); + CHECK(putstr(dest, "/")); + } + + if (d->classbuf[0] == '\0') + CHECK(putstr(dest, "?/")); + else { + CHECK(putstr(dest, d->classbuf)); + CHECK(putstr(dest, "/")); + } + + if (d->typebuf[0] == '\0') + CHECK(putstr(dest, "?")); + else + CHECK(putstr(dest, d->typebuf)); + + CHECK(isc_buffer_reserve(dest, 1)); + isc_buffer_putuint8(*dest, 0); + + cleanup: + return (result); +} + +void +dns_dtdata_free(dns_dtdata_t **dp) { + dns_dtdata_t *d; + + REQUIRE(dp != NULL && *dp != NULL); + + d = *dp; + + if (d->msg != NULL) + dns_message_destroy(&d->msg); + if (d->frame != NULL) + dnstap__dnstap__free_unpacked(d->frame, NULL); + + isc_mem_putanddetach(&d->mctx, d, sizeof(*d)); + + *dp = NULL; +} diff --git a/lib/dns/dnstap.pb-c.c b/lib/dns/dnstap.pb-c.c new file mode 100644 index 0000000000..50000bd649 --- /dev/null +++ b/lib/dns/dnstap.pb-c.c @@ -0,0 +1,523 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: dnstap.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "dnstap.pb-c.h" +void dnstap__dnstap__init + (Dnstap__Dnstap *message) +{ + static Dnstap__Dnstap init_value = DNSTAP__DNSTAP__INIT; + *message = init_value; +} +size_t dnstap__dnstap__get_packed_size + (const Dnstap__Dnstap *message) +{ + assert(message->base.descriptor == &dnstap__dnstap__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t dnstap__dnstap__pack + (const Dnstap__Dnstap *message, + uint8_t *out) +{ + assert(message->base.descriptor == &dnstap__dnstap__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t dnstap__dnstap__pack_to_buffer + (const Dnstap__Dnstap *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &dnstap__dnstap__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Dnstap__Dnstap * + dnstap__dnstap__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Dnstap__Dnstap *) + protobuf_c_message_unpack (&dnstap__dnstap__descriptor, + allocator, len, data); +} +void dnstap__dnstap__free_unpacked + (Dnstap__Dnstap *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &dnstap__dnstap__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void dnstap__message__init + (Dnstap__Message *message) +{ + static Dnstap__Message init_value = DNSTAP__MESSAGE__INIT; + *message = init_value; +} +size_t dnstap__message__get_packed_size + (const Dnstap__Message *message) +{ + assert(message->base.descriptor == &dnstap__message__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t dnstap__message__pack + (const Dnstap__Message *message, + uint8_t *out) +{ + assert(message->base.descriptor == &dnstap__message__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t dnstap__message__pack_to_buffer + (const Dnstap__Message *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &dnstap__message__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Dnstap__Message * + dnstap__message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Dnstap__Message *) + protobuf_c_message_unpack (&dnstap__message__descriptor, + allocator, len, data); +} +void dnstap__message__free_unpacked + (Dnstap__Message *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &dnstap__message__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +static const ProtobufCEnumValue dnstap__dnstap__type__enum_values_by_number[1] = +{ + { "MESSAGE", "DNSTAP__DNSTAP__TYPE__MESSAGE", 1 }, +}; +static const ProtobufCIntRange dnstap__dnstap__type__value_ranges[] = { +{1, 0},{0, 1} +}; +static const ProtobufCEnumValueIndex dnstap__dnstap__type__enum_values_by_name[1] = +{ + { "MESSAGE", 0 }, +}; +const ProtobufCEnumDescriptor dnstap__dnstap__type__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "dnstap.Dnstap.Type", + "Type", + "Dnstap__Dnstap__Type", + "dnstap", + 1, + dnstap__dnstap__type__enum_values_by_number, + 1, + dnstap__dnstap__type__enum_values_by_name, + 1, + dnstap__dnstap__type__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCFieldDescriptor dnstap__dnstap__field_descriptors[5] = +{ + { + "identity", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Dnstap__Dnstap, has_identity), + offsetof(Dnstap__Dnstap, identity), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "version", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Dnstap__Dnstap, has_version), + offsetof(Dnstap__Dnstap, version), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "extra", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Dnstap__Dnstap, has_extra), + offsetof(Dnstap__Dnstap, extra), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "message", + 14, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_MESSAGE, + 0, /* quantifier_offset */ + offsetof(Dnstap__Dnstap, message), + &dnstap__message__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "type", + 15, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(Dnstap__Dnstap, type), + &dnstap__dnstap__type__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned dnstap__dnstap__field_indices_by_name[] = { + 2, /* field[2] = extra */ + 0, /* field[0] = identity */ + 3, /* field[3] = message */ + 4, /* field[4] = type */ + 1, /* field[1] = version */ +}; +static const ProtobufCIntRange dnstap__dnstap__number_ranges[2 + 1] = +{ + { 1, 0 }, + { 14, 3 }, + { 0, 5 } +}; +const ProtobufCMessageDescriptor dnstap__dnstap__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "dnstap.Dnstap", + "Dnstap", + "Dnstap__Dnstap", + "dnstap", + sizeof(Dnstap__Dnstap), + 5, + dnstap__dnstap__field_descriptors, + dnstap__dnstap__field_indices_by_name, + 2, dnstap__dnstap__number_ranges, + (ProtobufCMessageInit) dnstap__dnstap__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCEnumValue dnstap__message__type__enum_values_by_number[12] = +{ + { "AUTH_QUERY", "DNSTAP__MESSAGE__TYPE__AUTH_QUERY", 1 }, + { "AUTH_RESPONSE", "DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE", 2 }, + { "RESOLVER_QUERY", "DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY", 3 }, + { "RESOLVER_RESPONSE", "DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE", 4 }, + { "CLIENT_QUERY", "DNSTAP__MESSAGE__TYPE__CLIENT_QUERY", 5 }, + { "CLIENT_RESPONSE", "DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE", 6 }, + { "FORWARDER_QUERY", "DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY", 7 }, + { "FORWARDER_RESPONSE", "DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE", 8 }, + { "STUB_QUERY", "DNSTAP__MESSAGE__TYPE__STUB_QUERY", 9 }, + { "STUB_RESPONSE", "DNSTAP__MESSAGE__TYPE__STUB_RESPONSE", 10 }, + { "TOOL_QUERY", "DNSTAP__MESSAGE__TYPE__TOOL_QUERY", 11 }, + { "TOOL_RESPONSE", "DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE", 12 }, +}; +static const ProtobufCIntRange dnstap__message__type__value_ranges[] = { +{1, 0},{0, 12} +}; +static const ProtobufCEnumValueIndex dnstap__message__type__enum_values_by_name[12] = +{ + { "AUTH_QUERY", 0 }, + { "AUTH_RESPONSE", 1 }, + { "CLIENT_QUERY", 4 }, + { "CLIENT_RESPONSE", 5 }, + { "FORWARDER_QUERY", 6 }, + { "FORWARDER_RESPONSE", 7 }, + { "RESOLVER_QUERY", 2 }, + { "RESOLVER_RESPONSE", 3 }, + { "STUB_QUERY", 8 }, + { "STUB_RESPONSE", 9 }, + { "TOOL_QUERY", 10 }, + { "TOOL_RESPONSE", 11 }, +}; +const ProtobufCEnumDescriptor dnstap__message__type__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "dnstap.Message.Type", + "Type", + "Dnstap__Message__Type", + "dnstap", + 12, + dnstap__message__type__enum_values_by_number, + 12, + dnstap__message__type__enum_values_by_name, + 1, + dnstap__message__type__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCFieldDescriptor dnstap__message__field_descriptors[14] = +{ + { + "type", + 1, + PROTOBUF_C_LABEL_REQUIRED, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(Dnstap__Message, type), + &dnstap__message__type__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "socket_family", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_ENUM, + offsetof(Dnstap__Message, has_socket_family), + offsetof(Dnstap__Message, socket_family), + &dnstap__socket_family__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "socket_protocol", + 3, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_ENUM, + offsetof(Dnstap__Message, has_socket_protocol), + offsetof(Dnstap__Message, socket_protocol), + &dnstap__socket_protocol__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "query_address", + 4, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Dnstap__Message, has_query_address), + offsetof(Dnstap__Message, query_address), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "response_address", + 5, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Dnstap__Message, has_response_address), + offsetof(Dnstap__Message, response_address), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "query_port", + 6, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Dnstap__Message, has_query_port), + offsetof(Dnstap__Message, query_port), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "response_port", + 7, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Dnstap__Message, has_response_port), + offsetof(Dnstap__Message, response_port), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "query_time_sec", + 8, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT64, + offsetof(Dnstap__Message, has_query_time_sec), + offsetof(Dnstap__Message, query_time_sec), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "query_time_nsec", + 9, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_FIXED32, + offsetof(Dnstap__Message, has_query_time_nsec), + offsetof(Dnstap__Message, query_time_nsec), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "query_message", + 10, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Dnstap__Message, has_query_message), + offsetof(Dnstap__Message, query_message), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "query_zone", + 11, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Dnstap__Message, has_query_zone), + offsetof(Dnstap__Message, query_zone), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "response_time_sec", + 12, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT64, + offsetof(Dnstap__Message, has_response_time_sec), + offsetof(Dnstap__Message, response_time_sec), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "response_time_nsec", + 13, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_FIXED32, + offsetof(Dnstap__Message, has_response_time_nsec), + offsetof(Dnstap__Message, response_time_nsec), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "response_message", + 14, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_BYTES, + offsetof(Dnstap__Message, has_response_message), + offsetof(Dnstap__Message, response_message), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned dnstap__message__field_indices_by_name[] = { + 3, /* field[3] = query_address */ + 9, /* field[9] = query_message */ + 5, /* field[5] = query_port */ + 8, /* field[8] = query_time_nsec */ + 7, /* field[7] = query_time_sec */ + 10, /* field[10] = query_zone */ + 4, /* field[4] = response_address */ + 13, /* field[13] = response_message */ + 6, /* field[6] = response_port */ + 12, /* field[12] = response_time_nsec */ + 11, /* field[11] = response_time_sec */ + 1, /* field[1] = socket_family */ + 2, /* field[2] = socket_protocol */ + 0, /* field[0] = type */ +}; +static const ProtobufCIntRange dnstap__message__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 14 } +}; +const ProtobufCMessageDescriptor dnstap__message__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "dnstap.Message", + "Message", + "Dnstap__Message", + "dnstap", + sizeof(Dnstap__Message), + 14, + dnstap__message__field_descriptors, + dnstap__message__field_indices_by_name, + 1, dnstap__message__number_ranges, + (ProtobufCMessageInit) dnstap__message__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCEnumValue dnstap__socket_family__enum_values_by_number[2] = +{ + { "INET", "DNSTAP__SOCKET_FAMILY__INET", 1 }, + { "INET6", "DNSTAP__SOCKET_FAMILY__INET6", 2 }, +}; +static const ProtobufCIntRange dnstap__socket_family__value_ranges[] = { +{1, 0},{0, 2} +}; +static const ProtobufCEnumValueIndex dnstap__socket_family__enum_values_by_name[2] = +{ + { "INET", 0 }, + { "INET6", 1 }, +}; +const ProtobufCEnumDescriptor dnstap__socket_family__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "dnstap.SocketFamily", + "SocketFamily", + "Dnstap__SocketFamily", + "dnstap", + 2, + dnstap__socket_family__enum_values_by_number, + 2, + dnstap__socket_family__enum_values_by_name, + 1, + dnstap__socket_family__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; +static const ProtobufCEnumValue dnstap__socket_protocol__enum_values_by_number[2] = +{ + { "UDP", "DNSTAP__SOCKET_PROTOCOL__UDP", 1 }, + { "TCP", "DNSTAP__SOCKET_PROTOCOL__TCP", 2 }, +}; +static const ProtobufCIntRange dnstap__socket_protocol__value_ranges[] = { +{1, 0},{0, 2} +}; +static const ProtobufCEnumValueIndex dnstap__socket_protocol__enum_values_by_name[2] = +{ + { "TCP", 1 }, + { "UDP", 0 }, +}; +const ProtobufCEnumDescriptor dnstap__socket_protocol__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "dnstap.SocketProtocol", + "SocketProtocol", + "Dnstap__SocketProtocol", + "dnstap", + 2, + dnstap__socket_protocol__enum_values_by_number, + 2, + dnstap__socket_protocol__enum_values_by_name, + 1, + dnstap__socket_protocol__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; diff --git a/lib/dns/dnstap.pb-c.h b/lib/dns/dnstap.pb-c.h new file mode 100644 index 0000000000..71fd82919d --- /dev/null +++ b/lib/dns/dnstap.pb-c.h @@ -0,0 +1,343 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: dnstap.proto */ + +#ifndef PROTOBUF_C_dnstap_2eproto__INCLUDED +#define PROTOBUF_C_dnstap_2eproto__INCLUDED + +#include + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1000000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1001001 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + + +typedef struct _Dnstap__Dnstap Dnstap__Dnstap; +typedef struct _Dnstap__Message Dnstap__Message; + + +/* --- enums --- */ + +/* + * Identifies which field below is filled in. + */ +typedef enum _Dnstap__Dnstap__Type { + DNSTAP__DNSTAP__TYPE__MESSAGE = 1 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(DNSTAP__DNSTAP__TYPE) +} Dnstap__Dnstap__Type; +typedef enum _Dnstap__Message__Type { + /* + * AUTH_QUERY is a DNS query message received from a resolver by an + * authoritative name server, from the perspective of the authorative + * name server. + */ + DNSTAP__MESSAGE__TYPE__AUTH_QUERY = 1, + /* + * AUTH_RESPONSE is a DNS response message sent from an authoritative + * name server to a resolver, from the perspective of the authoritative + * name server. + */ + DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE = 2, + /* + * RESOLVER_QUERY is a DNS query message sent from a resolver to an + * authoritative name server, from the perspective of the resolver. + * Resolvers typically clear the RD (recursion desired) bit when + * sending queries. + */ + DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY = 3, + /* + * RESOLVER_RESPONSE is a DNS response message received from an + * authoritative name server by a resolver, from the perspective of + * the resolver. + */ + DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE = 4, + /* + * CLIENT_QUERY is a DNS query message sent from a client to a DNS + * server which is expected to perform further recursion, from the + * perspective of the DNS server. The client may be a stub resolver or + * forwarder or some other type of software which typically sets the RD + * (recursion desired) bit when querying the DNS server. The DNS server + * may be a simple forwarding proxy or it may be a full recursive + * resolver. + */ + DNSTAP__MESSAGE__TYPE__CLIENT_QUERY = 5, + /* + * CLIENT_RESPONSE is a DNS response message sent from a DNS server to + * a client, from the perspective of the DNS server. The DNS server + * typically sets the RA (recursion available) bit when responding. + */ + DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE = 6, + /* + * FORWARDER_QUERY is a DNS query message sent from a downstream DNS + * server to an upstream DNS server which is expected to perform + * further recursion, from the perspective of the downstream DNS + * server. + */ + DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY = 7, + /* + * FORWARDER_RESPONSE is a DNS response message sent from an upstream + * DNS server performing recursion to a downstream DNS server, from the + * perspective of the downstream DNS server. + */ + DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE = 8, + /* + * STUB_QUERY is a DNS query message sent from a stub resolver to a DNS + * server, from the perspective of the stub resolver. + */ + DNSTAP__MESSAGE__TYPE__STUB_QUERY = 9, + /* + * STUB_RESPONSE is a DNS response message sent from a DNS server to a + * stub resolver, from the perspective of the stub resolver. + */ + DNSTAP__MESSAGE__TYPE__STUB_RESPONSE = 10, + /* + * TOOL_QUERY is a DNS query message sent from a DNS software tool to a + * DNS server, from the perspective of the tool. + */ + DNSTAP__MESSAGE__TYPE__TOOL_QUERY = 11, + /* + * TOOL_RESPONSE is a DNS response message received by a DNS software + * tool from a DNS server, from the perspective of the tool. + */ + DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE = 12 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(DNSTAP__MESSAGE__TYPE) +} Dnstap__Message__Type; +/* + * SocketFamily: the network protocol family of a socket. This specifies how + * to interpret "network address" fields. + */ +typedef enum _Dnstap__SocketFamily { + /* + * IPv4 (RFC 791) + */ + DNSTAP__SOCKET_FAMILY__INET = 1, + /* + * IPv6 (RFC 2460) + */ + DNSTAP__SOCKET_FAMILY__INET6 = 2 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(DNSTAP__SOCKET_FAMILY) +} Dnstap__SocketFamily; +/* + * SocketProtocol: the transport protocol of a socket. This specifies how to + * interpret "transport port" fields. + */ +typedef enum _Dnstap__SocketProtocol { + /* + * User Datagram Protocol (RFC 768) + */ + DNSTAP__SOCKET_PROTOCOL__UDP = 1, + /* + * Transmission Control Protocol (RFC 793) + */ + DNSTAP__SOCKET_PROTOCOL__TCP = 2 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(DNSTAP__SOCKET_PROTOCOL) +} Dnstap__SocketProtocol; + +/* --- messages --- */ + +/* + * "Dnstap": this is the top-level dnstap type, which is a "union" type that + * contains other kinds of dnstap payloads, although currently only one type + * of dnstap payload is defined. + * See: https://developers.google.com/protocol-buffers/docs/techniques#union + */ +struct _Dnstap__Dnstap +{ + ProtobufCMessage base; + /* + * DNS server identity. + * If enabled, this is the identity string of the DNS server which generated + * this message. Typically this would be the same string as returned by an + * "NSID" (RFC 5001) query. + */ + protobuf_c_boolean has_identity; + ProtobufCBinaryData identity; + /* + * DNS server version. + * If enabled, this is the version string of the DNS server which generated + * this message. Typically this would be the same string as returned by a + * "version.bind" query. + */ + protobuf_c_boolean has_version; + ProtobufCBinaryData version; + /* + * Extra data for this payload. + * This field can be used for adding an arbitrary byte-string annotation to + * the payload. No encoding or interpretation is applied or enforced. + */ + protobuf_c_boolean has_extra; + ProtobufCBinaryData extra; + Dnstap__Dnstap__Type type; + /* + * One of the following will be filled in. + */ + Dnstap__Message *message; +}; +#define DNSTAP__DNSTAP__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&dnstap__dnstap__descriptor) \ + , 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL}, 0, NULL } + + +/* + * Message: a wire-format (RFC 1035 section 4) DNS message and associated + * metadata. Applications generating "Message" payloads should follow + * certain requirements based on the MessageType, see below. + */ +struct _Dnstap__Message +{ + ProtobufCMessage base; + /* + * One of the Type values described above. + */ + Dnstap__Message__Type type; + /* + * One of the SocketFamily values described above. + */ + protobuf_c_boolean has_socket_family; + Dnstap__SocketFamily socket_family; + /* + * One of the SocketProtocol values described above. + */ + protobuf_c_boolean has_socket_protocol; + Dnstap__SocketProtocol socket_protocol; + /* + * The network address of the message initiator. + * For SocketFamily INET, this field is 4 octets (IPv4 address). + * For SocketFamily INET6, this field is 16 octets (IPv6 address). + */ + protobuf_c_boolean has_query_address; + ProtobufCBinaryData query_address; + /* + * The network address of the message responder. + * For SocketFamily INET, this field is 4 octets (IPv4 address). + * For SocketFamily INET6, this field is 16 octets (IPv6 address). + */ + protobuf_c_boolean has_response_address; + ProtobufCBinaryData response_address; + /* + * The transport port of the message initiator. + * This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + */ + protobuf_c_boolean has_query_port; + uint32_t query_port; + /* + * The transport port of the message responder. + * This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + */ + protobuf_c_boolean has_response_port; + uint32_t response_port; + /* + * The time at which the DNS query message was sent or received, depending + * on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY. + * This is the number of seconds since the UNIX epoch. + */ + protobuf_c_boolean has_query_time_sec; + uint64_t query_time_sec; + /* + * The time at which the DNS query message was sent or received. + * This is the seconds fraction, expressed as a count of nanoseconds. + */ + protobuf_c_boolean has_query_time_nsec; + uint32_t query_time_nsec; + /* + * The initiator's original wire-format DNS query message, verbatim. + */ + protobuf_c_boolean has_query_message; + ProtobufCBinaryData query_message; + /* + * The "zone" or "bailiwick" pertaining to the DNS query message. + * This is a wire-format DNS domain name. + */ + protobuf_c_boolean has_query_zone; + ProtobufCBinaryData query_zone; + /* + * The time at which the DNS response message was sent or received, + * depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or + * CLIENT_RESPONSE. + * This is the number of seconds since the UNIX epoch. + */ + protobuf_c_boolean has_response_time_sec; + uint64_t response_time_sec; + /* + * The time at which the DNS response message was sent or received. + * This is the seconds fraction, expressed as a count of nanoseconds. + */ + protobuf_c_boolean has_response_time_nsec; + uint32_t response_time_nsec; + /* + * The responder's original wire-format DNS response message, verbatim. + */ + protobuf_c_boolean has_response_message; + ProtobufCBinaryData response_message; +}; +#define DNSTAP__MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&dnstap__message__descriptor) \ + , 0, 0,0, 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,0, 0,0, 0,0, 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,0, 0,0, 0,{0,NULL} } + + +/* Dnstap__Dnstap methods */ +void dnstap__dnstap__init + (Dnstap__Dnstap *message); +size_t dnstap__dnstap__get_packed_size + (const Dnstap__Dnstap *message); +size_t dnstap__dnstap__pack + (const Dnstap__Dnstap *message, + uint8_t *out); +size_t dnstap__dnstap__pack_to_buffer + (const Dnstap__Dnstap *message, + ProtobufCBuffer *buffer); +Dnstap__Dnstap * + dnstap__dnstap__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void dnstap__dnstap__free_unpacked + (Dnstap__Dnstap *message, + ProtobufCAllocator *allocator); +/* Dnstap__Message methods */ +void dnstap__message__init + (Dnstap__Message *message); +size_t dnstap__message__get_packed_size + (const Dnstap__Message *message); +size_t dnstap__message__pack + (const Dnstap__Message *message, + uint8_t *out); +size_t dnstap__message__pack_to_buffer + (const Dnstap__Message *message, + ProtobufCBuffer *buffer); +Dnstap__Message * + dnstap__message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void dnstap__message__free_unpacked + (Dnstap__Message *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*Dnstap__Dnstap_Closure) + (const Dnstap__Dnstap *message, + void *closure_data); +typedef void (*Dnstap__Message_Closure) + (const Dnstap__Message *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCEnumDescriptor dnstap__socket_family__descriptor; +extern const ProtobufCEnumDescriptor dnstap__socket_protocol__descriptor; +extern const ProtobufCMessageDescriptor dnstap__dnstap__descriptor; +extern const ProtobufCEnumDescriptor dnstap__dnstap__type__descriptor; +extern const ProtobufCMessageDescriptor dnstap__message__descriptor; +extern const ProtobufCEnumDescriptor dnstap__message__type__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_dnstap_2eproto__INCLUDED */ diff --git a/lib/dns/dnstap.proto b/lib/dns/dnstap.proto new file mode 100644 index 0000000000..1ed1bb00e2 --- /dev/null +++ b/lib/dns/dnstap.proto @@ -0,0 +1,268 @@ +// dnstap: flexible, structured event replication format for DNS software +// +// This file contains the protobuf schemas for the "dnstap" structured event +// replication format for DNS software. + +// Written in 2013-2014 by Farsight Security, Inc. +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this file to the public +// domain worldwide. This file is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication along +// with this file. If not, see: +// +// . + +package dnstap; + +// "Dnstap": this is the top-level dnstap type, which is a "union" type that +// contains other kinds of dnstap payloads, although currently only one type +// of dnstap payload is defined. +// See: https://developers.google.com/protocol-buffers/docs/techniques#union +message Dnstap { + // DNS server identity. + // If enabled, this is the identity string of the DNS server which generated + // this message. Typically this would be the same string as returned by an + // "NSID" (RFC 5001) query. + optional bytes identity = 1; + + // DNS server version. + // If enabled, this is the version string of the DNS server which generated + // this message. Typically this would be the same string as returned by a + // "version.bind" query. + optional bytes version = 2; + + // Extra data for this payload. + // This field can be used for adding an arbitrary byte-string annotation to + // the payload. No encoding or interpretation is applied or enforced. + optional bytes extra = 3; + + // Identifies which field below is filled in. + enum Type { + MESSAGE = 1; + } + required Type type = 15; + + // One of the following will be filled in. + optional Message message = 14; +} + +// SocketFamily: the network protocol family of a socket. This specifies how +// to interpret "network address" fields. +enum SocketFamily { + INET = 1; // IPv4 (RFC 791) + INET6 = 2; // IPv6 (RFC 2460) +} + +// SocketProtocol: the transport protocol of a socket. This specifies how to +// interpret "transport port" fields. +enum SocketProtocol { + UDP = 1; // User Datagram Protocol (RFC 768) + TCP = 2; // Transmission Control Protocol (RFC 793) +} + +// Message: a wire-format (RFC 1035 section 4) DNS message and associated +// metadata. Applications generating "Message" payloads should follow +// certain requirements based on the MessageType, see below. +message Message { + + // There are eight types of "Message" defined that correspond to the + // four arrows in the following diagram, slightly modified from RFC 1035 + // section 2: + + // +---------+ +----------+ +--------+ + // | | query | | query | | + // | Stub |-SQ--------CQ->| Recursive|-RQ----AQ->| Auth. | + // | Resolver| | Server | | Name | + // | |<-SR--------CR-| |<-RR----AR-| Server | + // +---------+ response | | response | | + // +----------+ +--------+ + + // Each arrow has two Type values each, one for each "end" of each arrow, + // because these are considered to be distinct events. Each end of each + // arrow on the diagram above has been marked with a two-letter Type + // mnemonic. Clockwise from upper left, these mnemonic values are: + // + // SQ: STUB_QUERY + // CQ: CLIENT_QUERY + // RQ: RESOLVER_QUERY + // AQ: AUTH_QUERY + // AR: AUTH_RESPONSE + // RR: RESOLVER_RESPONSE + // CR: CLIENT_RESPONSE + // SR: STUB_RESPONSE + + // Two additional types of "Message" have been defined for the + // "forwarding" case where an upstream DNS server is responsible for + // further recursion. These are not shown on the diagram above, but have + // the following mnemonic values: + + // FQ: FORWARDER_QUERY + // FR: FORWARDER_RESPONSE + + // The "Message" Type values are defined below. + + enum Type { + // AUTH_QUERY is a DNS query message received from a resolver by an + // authoritative name server, from the perspective of the authorative + // name server. + AUTH_QUERY = 1; + + // AUTH_RESPONSE is a DNS response message sent from an authoritative + // name server to a resolver, from the perspective of the authoritative + // name server. + AUTH_RESPONSE = 2; + + // RESOLVER_QUERY is a DNS query message sent from a resolver to an + // authoritative name server, from the perspective of the resolver. + // Resolvers typically clear the RD (recursion desired) bit when + // sending queries. + RESOLVER_QUERY = 3; + + // RESOLVER_RESPONSE is a DNS response message received from an + // authoritative name server by a resolver, from the perspective of + // the resolver. + RESOLVER_RESPONSE = 4; + + // CLIENT_QUERY is a DNS query message sent from a client to a DNS + // server which is expected to perform further recursion, from the + // perspective of the DNS server. The client may be a stub resolver or + // forwarder or some other type of software which typically sets the RD + // (recursion desired) bit when querying the DNS server. The DNS server + // may be a simple forwarding proxy or it may be a full recursive + // resolver. + CLIENT_QUERY = 5; + + // CLIENT_RESPONSE is a DNS response message sent from a DNS server to + // a client, from the perspective of the DNS server. The DNS server + // typically sets the RA (recursion available) bit when responding. + CLIENT_RESPONSE = 6; + + // FORWARDER_QUERY is a DNS query message sent from a downstream DNS + // server to an upstream DNS server which is expected to perform + // further recursion, from the perspective of the downstream DNS + // server. + FORWARDER_QUERY = 7; + + // FORWARDER_RESPONSE is a DNS response message sent from an upstream + // DNS server performing recursion to a downstream DNS server, from the + // perspective of the downstream DNS server. + FORWARDER_RESPONSE = 8; + + // STUB_QUERY is a DNS query message sent from a stub resolver to a DNS + // server, from the perspective of the stub resolver. + STUB_QUERY = 9; + + // STUB_RESPONSE is a DNS response message sent from a DNS server to a + // stub resolver, from the perspective of the stub resolver. + STUB_RESPONSE = 10; + + // TOOL_QUERY is a DNS query message sent from a DNS software tool to a + // DNS server, from the perspective of the tool. + TOOL_QUERY = 11; + + // TOOL_RESPONSE is a DNS response message received by a DNS software + // tool from a DNS server, from the perspective of the tool. + TOOL_RESPONSE = 12; + } + + // One of the Type values described above. + required Type type = 1; + + // One of the SocketFamily values described above. + optional SocketFamily socket_family = 2; + + // One of the SocketProtocol values described above. + optional SocketProtocol socket_protocol = 3; + + // The network address of the message initiator. + // For SocketFamily INET, this field is 4 octets (IPv4 address). + // For SocketFamily INET6, this field is 16 octets (IPv6 address). + optional bytes query_address = 4; + + // The network address of the message responder. + // For SocketFamily INET, this field is 4 octets (IPv4 address). + // For SocketFamily INET6, this field is 16 octets (IPv6 address). + optional bytes response_address = 5; + + // The transport port of the message initiator. + // This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + optional uint32 query_port = 6; + + // The transport port of the message responder. + // This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + optional uint32 response_port = 7; + + // The time at which the DNS query message was sent or received, depending + // on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY. + // This is the number of seconds since the UNIX epoch. + optional uint64 query_time_sec = 8; + + // The time at which the DNS query message was sent or received. + // This is the seconds fraction, expressed as a count of nanoseconds. + optional fixed32 query_time_nsec = 9; + + // The initiator's original wire-format DNS query message, verbatim. + optional bytes query_message = 10; + + // The "zone" or "bailiwick" pertaining to the DNS query message. + // This is a wire-format DNS domain name. + optional bytes query_zone = 11; + + // The time at which the DNS response message was sent or received, + // depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or + // CLIENT_RESPONSE. + // This is the number of seconds since the UNIX epoch. + optional uint64 response_time_sec = 12; + + // The time at which the DNS response message was sent or received. + // This is the seconds fraction, expressed as a count of nanoseconds. + optional fixed32 response_time_nsec = 13; + + // The responder's original wire-format DNS response message, verbatim. + optional bytes response_message = 14; +} + +// All fields except for 'type' in the Message schema are optional. +// It is recommended that at least the following fields be filled in for +// particular types of Messages. + +// AUTH_QUERY: +// socket_family, socket_protocol +// query_address, query_port +// query_message +// query_time_sec, query_time_nsec + +// AUTH_RESPONSE: +// socket_family, socket_protocol +// query_address, query_port +// query_time_sec, query_time_nsec +// response_message +// response_time_sec, response_time_nsec + +// RESOLVER_QUERY: +// socket_family, socket_protocol +// query_message +// query_time_sec, query_time_nsec +// query_zone +// response_address, response_port + +// RESOLVER_RESPONSE: +// socket_family, socket_protocol +// query_time_sec, query_time_nsec +// query_zone +// response_address, response_port +// response_message +// response_time_sec, response_time_nsec + +// CLIENT_QUERY: +// socket_family, socket_protocol +// query_message +// query_time_sec, query_time_nsec + +// CLIENT_RESPONSE: +// socket_family, socket_protocol +// query_time_sec, query_time_nsec +// response_message +// response_time_sec, response_time_nsec diff --git a/lib/dns/include/dns/Makefile.in b/lib/dns/include/dns/Makefile.in index bc34c43c4e..3fa4b4e805 100644 --- a/lib/dns/include/dns/Makefile.in +++ b/lib/dns/include/dns/Makefile.in @@ -22,8 +22,9 @@ top_srcdir = @top_srcdir@ HEADERS = acache.h acl.h adb.h badcache.h bit.h byaddr.h \ cache.h callbacks.h cert.h \ client.h clientinfo.h compress.h \ - db.h dbiterator.h dbtable.h diff.h dispatch.h dlz.h \ - dlz_dlopen.h dns64.h dnssec.h ds.h dsdigest.h dynamic_db.h \ + db.h dbiterator.h dbtable.h diff.h dispatch.h \ + dlz.h dlz_dlopen.h dns64.h dnssec.h ds.h dsdigest.h \ + dnstap.h dnstap.pb-c.h dynamic_db.h \ edns.h ecdb.h events.h fixedname.h forward.h geoip.h iptable.h \ journal.h keydata.h keyflags.h keytable.h keyvalues.h \ lib.h lookup.h log.h master.h masterdump.h message.h \ diff --git a/lib/dns/include/dns/dnstap.h b/lib/dns/include/dns/dnstap.h new file mode 100644 index 0000000000..3e8a60686c --- /dev/null +++ b/lib/dns/include/dns/dnstap.h @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DNSTAP_H +#define _DNSTAP_H + +/***** + ***** Module Info + *****/ + +/*! \file + * \brief + * The dt (dnstap) module provides fast passive logging of DNS messages. + * Protocol Buffers. The protobuf schema for Dnstap messages is in the + * file dnstap.proto, which is compiled to dnstap.pb-c.c and dnstap.pb-c.h. + */ + +#ifdef HAVE_DNSTAP +#include +#include +#include +#endif /* HAVE_DNSTAP */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/*% + * Dnstap message types: + * + * STUB QUERY: SQ + * STUB RESPONSE: SR + * CLIENT QUERY: CQ + * CLIENT RESPONSE: CR + * AUTH QUERY: AQ + * AUTH RESPONSE: AR + * RESOLVER QUERY: RQ + * RESOLVER RESPONSE: RR + * FORWARDER QUERY: FQ + * FORWARDER RESPONSE: FR + */ + +#define DNS_DTTYPE_SQ 0x0001 +#define DNS_DTTYPE_SR 0x0002 +#define DNS_DTTYPE_CQ 0x0004 +#define DNS_DTTYPE_CR 0x0008 +#define DNS_DTTYPE_AQ 0x0010 +#define DNS_DTTYPE_AR 0x0020 +#define DNS_DTTYPE_RQ 0x0040 +#define DNS_DTTYPE_RR 0x0080 +#define DNS_DTTYPE_FQ 0x0100 +#define DNS_DTTYPE_FR 0x0200 +#define DNS_DTTYPE_TQ 0x0400 +#define DNS_DTTYPE_TR 0x0800 + +#define DNS_DTTYPE_QUERY \ + (DNS_DTTYPE_SQ|DNS_DTTYPE_CQ|DNS_DTTYPE_AQ|\ + DNS_DTTYPE_RQ|DNS_DTTYPE_FQ|DNS_DTTYPE_TQ) +#define DNS_DTTYPE_RESPONSE \ + (DNS_DTTYPE_SR|DNS_DTTYPE_CR|DNS_DTTYPE_AR|\ + DNS_DTTYPE_RR|DNS_DTTYPE_FR|DNS_DTTYPE_TR) +#define DNS_DTTYPE_ALL \ + (DNS_DTTYPE_QUERY|DNS_DTTYPE_RESPONSE) + +typedef enum { + dns_dtmode_none = 0, + dns_dtmode_file, + dns_dtmode_unix +} dns_dtmode_t; + +typedef struct dns_dthandle { + dns_dtmode_t mode; + struct fstrm_reader *reader; +} dns_dthandle_t; + +#ifdef HAVE_DNSTAP +struct dns_dtenv { + unsigned int magic; + isc_refcount_t refcount; + + isc_mem_t *mctx; + + struct fstrm_iothr *iothr; + + isc_region_t identity; + isc_region_t version; +}; + +struct dns_dtdata { + isc_mem_t *mctx; + + Dnstap__Dnstap *frame; + + isc_boolean_t query; + isc_boolean_t tcp; + dns_dtmsgtype_t type; + + isc_time_t qtime; + isc_time_t rtime; + + isc_region_t qaddr; + isc_region_t raddr; + + isc_region_t msgdata; + dns_message_t *msg; + + char namebuf[DNS_NAME_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + char classbuf[DNS_RDATACLASS_FORMATSIZE]; +}; +#endif /* HAVE_DNSTAP */ + +isc_result_t +dns_dt_create(isc_mem_t *mctx, dns_dtmode_t mode, const char *path, + unsigned int workers, dns_dtenv_t **envp); +/*%< + * Create and initialize the dnstap environment. + * + * There should be a single global dnstap environment for the server; + * copies of it will be attached to each view. + * + * Notes: + * + *\li 'path' refers to a UNIX domain socket by default. It may + * optionally be prepended with "socket:" or "file:". If prepended + * with "file:", then dnstap logs are sent to a file instead of a + * socket. + * + *\li This creates an I/O thread in libfstrm, and prepares + * 'workers' input queues. 'workers' MUST be equal to the number + * of worker threads in named; if it's more, some queues will be + * wasted and if it's less, some threads will have no queue and + * will not log any dnstap events. + * + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li 'path' is a valid C string. + * + *\li envp != NULL && *envp == NULL + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + * + *\li Other errors are possible. + */ + +isc_result_t +dns_dt_setidentity(dns_dtenv_t *env, const char *identity); +isc_result_t +dns_dt_setversion(dns_dtenv_t *env, const char *version); +/*%< + * Set the "identity" and "version" strings to be sent in dnstap messages. + * + * Requires: + * + *\li 'env' is a valid dnstap environment. + */ + +void +dns_dt_attach(dns_dtenv_t *source, dns_dtenv_t **destp); +/*%< + * Attach '*destp' to 'source', incrementing the reference counter. + * + * Requires: + * + *\li 'source' is a valid dnstap environment. + * + *\li 'destp' is not NULL and '*destp' is NULL. + * + *\li *destp is attached to source. + */ + +void +dns_dt_detach(dns_dtenv_t **envp); +/*%< + * Detach '*envp', decrementing the reference counter. + * + * Requires: + * + *\li '*envp' is a valid dnstap environment. + * + * Ensures: + * + *\li '*envp' will be destroyed when the number of references reaches zero. + * + *\li '*envp' is NULL. + */ + +void +dns_dt_shutdown(void); +/*%< + * Shuts down dnstap and frees global resources. This function must only + * be called immediately before server shutdown. + */ + +void +dns_dt_send(dns_view_t *view, dns_dtmsgtype_t msgtype, + isc_sockaddr_t *sa, isc_boolean_t tcp, isc_region_t *zone, + isc_time_t *qtime, isc_time_t *rtime, isc_buffer_t *buf); +/*%< + * Sends a dnstap message to the log, if 'msgtype' is one of the message + * types represented in 'view->dttypes'. + * + * Parameters are: 'sa' (address of the peer in the DNS transaction being + * logged); 'tcp' (boolean indicating whether the transaction was over + * TCP); 'zone' (the authoritative zone or bailiwick, in uncompressed + * wire format), 'qtime' and 'rtime' (query and response times; if + * NULL, they are set to the current time); and 'buf' (the DNS message + * being logged, in wire format). + * + * Requires: + * + *\li 'view' is a valid view, and 'view->dtenv' is NULL or is a + * valid dnstap environment. + */ + +isc_result_t +dns_dt_parse(isc_mem_t *mctx, isc_region_t *src, dns_dtdata_t **destp); +/*%< + * Converts a raw dnstap frame in 'src' to a parsed dnstap data structure + * in '*destp'. + * + * Requires: + *\li 'src' is not NULL + * + *\li 'destp' is not NULL and '*destp' points to a valid buffer. + * + * Returns: + *\li #ISC_R_SUCCESS on success + * + *\li Other errors are possible. + */ + +isc_result_t +dns_dt_datatotext(dns_dtdata_t *d, isc_buffer_t **dest); +/*%< + * Converts a parsed dnstap data structure 'd' to text, storing + * the result in the buffer 'dest'. If 'dest' points to a dynamically + * allocated buffer, then it may be reallocated as needed. + * + * (XXX: add a 'long_form' option to generate a detailed listing of + * dnstap data instead * of a one-line summary.) + * + * Requires: + *\li 'd' is not NULL + * + *\li 'dest' is not NULL and '*dest' points to a valid buffer. + * + * Returns: + *\li #ISC_R_SUCCESS on success + *\li #ISC_R_NOSPACE if buffer is not dynamic and runs out of space + *\li #ISC_R_NOMEMORY if buffer is dynamic but memory could not be allocated + * + *\li Other errors are possible. + */ + +void +dns_dtdata_free(dns_dtdata_t **dp); +/*%< + * Frees the specified dns_dtdata structure and all its members, + * and sets *dp to NULL. + */ + +isc_result_t +dns_dt_open(const char *filename, dns_dtmode_t mode, dns_dthandle_t *handle); +/*%< + * Opens a dnstap framestream at 'filename' and stores a pointer to the + * reader object in a dns_dthandle_t structure. + * + * The caller is responsible for allocating the handle structure. + * + * (XXX: Currently only file readers are supported, not unix-domain socket + * readers.) + * + * Requires: + * + *\li 'handle' is not NULL + * + * Returns: + * + *\li #ISC_R_SUCCESS on success + *\li #ISC_R_NOTIMPLEMENTED if 'mode' is not dns_dtmode_file. (XXX) + *\li #ISC_R_NOMEMORY if the fstrm library was unable to allocate a + * reader or options structure + *\li #ISC_R_FAILURE if 'filename' could not be opened. + *\li #ISC_R_BADDNSTAP if 'filename' does not contain a dnstap + * framestream. + */ + +isc_result_t +dns_dt_getframe(dns_dthandle_t *handle, isc_uint8_t **bufp, size_t *sizep); +/*%< + * Read a dnstap frame from the framstream reader in 'handle', storing + * a pointer to it in '*bufp' and its size in '*sizep'. + * + * Requires: + * + *\li 'handle' is not NULL + *\li 'bufp' is not NULL + *\li 'sizep' is not NULL + * + * Ensures: + * \li if returning ISC_R_SUCCESS then '*bufp' is not NULL + * + * Returns: + * + *\li #ISC_R_SUCCESS on success + *\li #ISC_R_NOMORE at the end of the frame stream + *\li #ISC_R_FAILURE for any other failure + */ + +void +dns_dt_close(dns_dthandle_t *handle); +/*%< + * Closes the dnstap file referenced by 'handle'. + * + * Requires: + * + *\li 'handle' is not NULL + */ + +#endif /* _DNSTAP_H */ diff --git a/lib/dns/include/dns/dnstap.pb-c.h b/lib/dns/include/dns/dnstap.pb-c.h new file mode 100644 index 0000000000..71fd82919d --- /dev/null +++ b/lib/dns/include/dns/dnstap.pb-c.h @@ -0,0 +1,343 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: dnstap.proto */ + +#ifndef PROTOBUF_C_dnstap_2eproto__INCLUDED +#define PROTOBUF_C_dnstap_2eproto__INCLUDED + +#include + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1000000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1001001 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + + +typedef struct _Dnstap__Dnstap Dnstap__Dnstap; +typedef struct _Dnstap__Message Dnstap__Message; + + +/* --- enums --- */ + +/* + * Identifies which field below is filled in. + */ +typedef enum _Dnstap__Dnstap__Type { + DNSTAP__DNSTAP__TYPE__MESSAGE = 1 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(DNSTAP__DNSTAP__TYPE) +} Dnstap__Dnstap__Type; +typedef enum _Dnstap__Message__Type { + /* + * AUTH_QUERY is a DNS query message received from a resolver by an + * authoritative name server, from the perspective of the authorative + * name server. + */ + DNSTAP__MESSAGE__TYPE__AUTH_QUERY = 1, + /* + * AUTH_RESPONSE is a DNS response message sent from an authoritative + * name server to a resolver, from the perspective of the authoritative + * name server. + */ + DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE = 2, + /* + * RESOLVER_QUERY is a DNS query message sent from a resolver to an + * authoritative name server, from the perspective of the resolver. + * Resolvers typically clear the RD (recursion desired) bit when + * sending queries. + */ + DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY = 3, + /* + * RESOLVER_RESPONSE is a DNS response message received from an + * authoritative name server by a resolver, from the perspective of + * the resolver. + */ + DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE = 4, + /* + * CLIENT_QUERY is a DNS query message sent from a client to a DNS + * server which is expected to perform further recursion, from the + * perspective of the DNS server. The client may be a stub resolver or + * forwarder or some other type of software which typically sets the RD + * (recursion desired) bit when querying the DNS server. The DNS server + * may be a simple forwarding proxy or it may be a full recursive + * resolver. + */ + DNSTAP__MESSAGE__TYPE__CLIENT_QUERY = 5, + /* + * CLIENT_RESPONSE is a DNS response message sent from a DNS server to + * a client, from the perspective of the DNS server. The DNS server + * typically sets the RA (recursion available) bit when responding. + */ + DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE = 6, + /* + * FORWARDER_QUERY is a DNS query message sent from a downstream DNS + * server to an upstream DNS server which is expected to perform + * further recursion, from the perspective of the downstream DNS + * server. + */ + DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY = 7, + /* + * FORWARDER_RESPONSE is a DNS response message sent from an upstream + * DNS server performing recursion to a downstream DNS server, from the + * perspective of the downstream DNS server. + */ + DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE = 8, + /* + * STUB_QUERY is a DNS query message sent from a stub resolver to a DNS + * server, from the perspective of the stub resolver. + */ + DNSTAP__MESSAGE__TYPE__STUB_QUERY = 9, + /* + * STUB_RESPONSE is a DNS response message sent from a DNS server to a + * stub resolver, from the perspective of the stub resolver. + */ + DNSTAP__MESSAGE__TYPE__STUB_RESPONSE = 10, + /* + * TOOL_QUERY is a DNS query message sent from a DNS software tool to a + * DNS server, from the perspective of the tool. + */ + DNSTAP__MESSAGE__TYPE__TOOL_QUERY = 11, + /* + * TOOL_RESPONSE is a DNS response message received by a DNS software + * tool from a DNS server, from the perspective of the tool. + */ + DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE = 12 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(DNSTAP__MESSAGE__TYPE) +} Dnstap__Message__Type; +/* + * SocketFamily: the network protocol family of a socket. This specifies how + * to interpret "network address" fields. + */ +typedef enum _Dnstap__SocketFamily { + /* + * IPv4 (RFC 791) + */ + DNSTAP__SOCKET_FAMILY__INET = 1, + /* + * IPv6 (RFC 2460) + */ + DNSTAP__SOCKET_FAMILY__INET6 = 2 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(DNSTAP__SOCKET_FAMILY) +} Dnstap__SocketFamily; +/* + * SocketProtocol: the transport protocol of a socket. This specifies how to + * interpret "transport port" fields. + */ +typedef enum _Dnstap__SocketProtocol { + /* + * User Datagram Protocol (RFC 768) + */ + DNSTAP__SOCKET_PROTOCOL__UDP = 1, + /* + * Transmission Control Protocol (RFC 793) + */ + DNSTAP__SOCKET_PROTOCOL__TCP = 2 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(DNSTAP__SOCKET_PROTOCOL) +} Dnstap__SocketProtocol; + +/* --- messages --- */ + +/* + * "Dnstap": this is the top-level dnstap type, which is a "union" type that + * contains other kinds of dnstap payloads, although currently only one type + * of dnstap payload is defined. + * See: https://developers.google.com/protocol-buffers/docs/techniques#union + */ +struct _Dnstap__Dnstap +{ + ProtobufCMessage base; + /* + * DNS server identity. + * If enabled, this is the identity string of the DNS server which generated + * this message. Typically this would be the same string as returned by an + * "NSID" (RFC 5001) query. + */ + protobuf_c_boolean has_identity; + ProtobufCBinaryData identity; + /* + * DNS server version. + * If enabled, this is the version string of the DNS server which generated + * this message. Typically this would be the same string as returned by a + * "version.bind" query. + */ + protobuf_c_boolean has_version; + ProtobufCBinaryData version; + /* + * Extra data for this payload. + * This field can be used for adding an arbitrary byte-string annotation to + * the payload. No encoding or interpretation is applied or enforced. + */ + protobuf_c_boolean has_extra; + ProtobufCBinaryData extra; + Dnstap__Dnstap__Type type; + /* + * One of the following will be filled in. + */ + Dnstap__Message *message; +}; +#define DNSTAP__DNSTAP__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&dnstap__dnstap__descriptor) \ + , 0,{0,NULL}, 0,{0,NULL}, 0,{0,NULL}, 0, NULL } + + +/* + * Message: a wire-format (RFC 1035 section 4) DNS message and associated + * metadata. Applications generating "Message" payloads should follow + * certain requirements based on the MessageType, see below. + */ +struct _Dnstap__Message +{ + ProtobufCMessage base; + /* + * One of the Type values described above. + */ + Dnstap__Message__Type type; + /* + * One of the SocketFamily values described above. + */ + protobuf_c_boolean has_socket_family; + Dnstap__SocketFamily socket_family; + /* + * One of the SocketProtocol values described above. + */ + protobuf_c_boolean has_socket_protocol; + Dnstap__SocketProtocol socket_protocol; + /* + * The network address of the message initiator. + * For SocketFamily INET, this field is 4 octets (IPv4 address). + * For SocketFamily INET6, this field is 16 octets (IPv6 address). + */ + protobuf_c_boolean has_query_address; + ProtobufCBinaryData query_address; + /* + * The network address of the message responder. + * For SocketFamily INET, this field is 4 octets (IPv4 address). + * For SocketFamily INET6, this field is 16 octets (IPv6 address). + */ + protobuf_c_boolean has_response_address; + ProtobufCBinaryData response_address; + /* + * The transport port of the message initiator. + * This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + */ + protobuf_c_boolean has_query_port; + uint32_t query_port; + /* + * The transport port of the message responder. + * This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + */ + protobuf_c_boolean has_response_port; + uint32_t response_port; + /* + * The time at which the DNS query message was sent or received, depending + * on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY. + * This is the number of seconds since the UNIX epoch. + */ + protobuf_c_boolean has_query_time_sec; + uint64_t query_time_sec; + /* + * The time at which the DNS query message was sent or received. + * This is the seconds fraction, expressed as a count of nanoseconds. + */ + protobuf_c_boolean has_query_time_nsec; + uint32_t query_time_nsec; + /* + * The initiator's original wire-format DNS query message, verbatim. + */ + protobuf_c_boolean has_query_message; + ProtobufCBinaryData query_message; + /* + * The "zone" or "bailiwick" pertaining to the DNS query message. + * This is a wire-format DNS domain name. + */ + protobuf_c_boolean has_query_zone; + ProtobufCBinaryData query_zone; + /* + * The time at which the DNS response message was sent or received, + * depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or + * CLIENT_RESPONSE. + * This is the number of seconds since the UNIX epoch. + */ + protobuf_c_boolean has_response_time_sec; + uint64_t response_time_sec; + /* + * The time at which the DNS response message was sent or received. + * This is the seconds fraction, expressed as a count of nanoseconds. + */ + protobuf_c_boolean has_response_time_nsec; + uint32_t response_time_nsec; + /* + * The responder's original wire-format DNS response message, verbatim. + */ + protobuf_c_boolean has_response_message; + ProtobufCBinaryData response_message; +}; +#define DNSTAP__MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&dnstap__message__descriptor) \ + , 0, 0,0, 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,0, 0,0, 0,0, 0,0, 0,{0,NULL}, 0,{0,NULL}, 0,0, 0,0, 0,{0,NULL} } + + +/* Dnstap__Dnstap methods */ +void dnstap__dnstap__init + (Dnstap__Dnstap *message); +size_t dnstap__dnstap__get_packed_size + (const Dnstap__Dnstap *message); +size_t dnstap__dnstap__pack + (const Dnstap__Dnstap *message, + uint8_t *out); +size_t dnstap__dnstap__pack_to_buffer + (const Dnstap__Dnstap *message, + ProtobufCBuffer *buffer); +Dnstap__Dnstap * + dnstap__dnstap__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void dnstap__dnstap__free_unpacked + (Dnstap__Dnstap *message, + ProtobufCAllocator *allocator); +/* Dnstap__Message methods */ +void dnstap__message__init + (Dnstap__Message *message); +size_t dnstap__message__get_packed_size + (const Dnstap__Message *message); +size_t dnstap__message__pack + (const Dnstap__Message *message, + uint8_t *out); +size_t dnstap__message__pack_to_buffer + (const Dnstap__Message *message, + ProtobufCBuffer *buffer); +Dnstap__Message * + dnstap__message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void dnstap__message__free_unpacked + (Dnstap__Message *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*Dnstap__Dnstap_Closure) + (const Dnstap__Dnstap *message, + void *closure_data); +typedef void (*Dnstap__Message_Closure) + (const Dnstap__Message *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCEnumDescriptor dnstap__socket_family__descriptor; +extern const ProtobufCEnumDescriptor dnstap__socket_protocol__descriptor; +extern const ProtobufCMessageDescriptor dnstap__dnstap__descriptor; +extern const ProtobufCEnumDescriptor dnstap__dnstap__type__descriptor; +extern const ProtobufCMessageDescriptor dnstap__message__descriptor; +extern const ProtobufCEnumDescriptor dnstap__message__type__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_dnstap_2eproto__INCLUDED */ diff --git a/lib/dns/include/dns/log.h b/lib/dns/include/dns/log.h index 91ebbe0964..613773c59d 100644 --- a/lib/dns/include/dns/log.h +++ b/lib/dns/include/dns/log.h @@ -44,6 +44,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[]; #define DNS_LOGCATEGORY_RRL (&dns_categories[13]) #define DNS_LOGCATEGORY_CNAME (&dns_categories[14]) #define DNS_LOGCATEGORY_SPILL (&dns_categories[15]) +#define DNS_LOGCATEGORY_DNSTAP (&dns_categories[16]) /* Backwards compatibility. */ #define DNS_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL @@ -80,6 +81,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[]; #define DNS_LOGMODULE_PACKETS (&dns_modules[29]) #define DNS_LOGMODULE_NTA (&dns_modules[30]) #define DNS_LOGMODULE_DYNDB (&dns_modules[31]) +#define DNS_LOGMODULE_DNSTAP (&dns_modules[32]) ISC_LANG_BEGINDECLS diff --git a/lib/dns/include/dns/masterdump.h b/lib/dns/include/dns/masterdump.h index 7318184a95..4c5344340d 100644 --- a/lib/dns/include/dns/masterdump.h +++ b/lib/dns/include/dns/masterdump.h @@ -109,6 +109,9 @@ typedef struct dns_master_style dns_master_style_t; /*% Print TTL with human-readable units. */ #define DNS_STYLEFLAG_TTL_UNITS 0x20000000U +/*% Indent output. */ +#define DNS_STYLEFLAG_INDENT 0x40000000U + ISC_LANG_BEGINDECLS /*** @@ -162,11 +165,25 @@ LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_debug; */ LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_comment; +/*% + * Similar to dns_master_style_debug but data is indented with + * dns_master_indentstr (defaults to tab). + */ +LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_indent; + /*% * The style used for dumping "key" zones. */ LIBDNS_EXTERNAL_DATA extern const dns_master_style_t dns_master_style_keyzone; +/*% + * The default indent string to prepend lines with when using + * styleflag DNS_STYLEFLAG_INDENT. This is set to "\t" by default. + * The indent preceeds everything else on the line, including comment + * characters (;). + */ +LIBDNS_EXTERNAL_DATA extern const char *dns_master_indentstr; + /*** *** Functions ***/ @@ -370,6 +387,9 @@ dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node, dns_name_t *name, const dns_master_style_t *style, const char *filename); +unsigned int +dns_master_styleflags(const dns_master_style_t *style); + isc_result_t dns_master_stylecreate(dns_master_style_t **style, unsigned int flags, unsigned int ttl_column, unsigned int class_column, diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index 3027f047ab..1a8966dae9 100644 --- a/lib/dns/include/dns/result.h +++ b/lib/dns/include/dns/result.h @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.h,v 1.123 2011/03/21 07:22:14 each Exp $ */ - #ifndef DNS_RESULT_H #define DNS_RESULT_H 1 @@ -157,8 +155,9 @@ #define DNS_R_BADCDS (ISC_RESULTCLASS_DNS + 111) #define DNS_R_BADCDNSKEY (ISC_RESULTCLASS_DNS + 112) #define DNS_R_OPTERR (ISC_RESULTCLASS_DNS + 113) +#define DNS_R_BADDNSTAP (ISC_RESULTCLASS_DNS + 114) -#define DNS_R_NRESULTS 114 /*%< Number of results */ +#define DNS_R_NRESULTS 115 /*%< Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index dc02cd2b02..54fe547e15 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -76,6 +76,10 @@ typedef ISC_LIST(dns_dns64_t) dns_dns64list_t; typedef struct dns_dnsseckey dns_dnsseckey_t; typedef ISC_LIST(dns_dnsseckey_t) dns_dnsseckeylist_t; typedef isc_uint8_t dns_dsdigest_t; +typedef struct dns_dtdata dns_dtdata_t; +typedef struct dns_dtenv dns_dtenv_t; +typedef struct dns_dtmsg dns_dtmsg_t; +typedef isc_uint16_t dns_dtmsgtype_t; typedef struct dns_dumpctx dns_dumpctx_t; typedef struct dns_ednsopt dns_ednsopt_t; typedef struct dns_fetch dns_fetch_t; diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 51b41ae417..fe52e7b6e7 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -71,6 +71,7 @@ #include #include +#include #include #include #include @@ -200,7 +201,8 @@ struct dns_view { dns_zone_t * managed_keys; dns_zone_t * redirect; - dns_name_t * redirectzone; /* points to redirectfixed + dns_name_t * redirectzone; /* points to + redirectfixed when valid */ dns_fixedname_t redirectfixed; @@ -217,6 +219,10 @@ struct dns_view { unsigned char secret[32]; /* Client secret */ unsigned int v6bias; + + dns_dtenv_t *dtenv; /* Dnstap environment */ + dns_dtmsgtype_t dttypes; /* Dnstap message types + to log */ }; #define DNS_VIEW_MAGIC ISC_MAGIC('V','i','e','w') diff --git a/lib/dns/log.c b/lib/dns/log.c index cff764bf54..623e027542 100644 --- a/lib/dns/log.c +++ b/lib/dns/log.c @@ -46,6 +46,7 @@ LIBDNS_EXTERNAL_DATA isc_logcategory_t dns_categories[] = { { "rate-limit", 0 }, { "cname", 0 }, { "spill", 0 }, + { "dnstap", 0 }, { NULL, 0 } }; @@ -86,6 +87,7 @@ LIBDNS_EXTERNAL_DATA isc_logmodule_t dns_modules[] = { { "dns/packets", 0 }, { "dns/nta", 0 }, { "dns/dyndb", 0 }, + { "dns/dnstap", 0 }, { NULL, 0 } }; diff --git a/lib/dns/masterdump.c b/lib/dns/masterdump.c index 31a7db59aa..c0e2de9611 100644 --- a/lib/dns/masterdump.c +++ b/lib/dns/masterdump.c @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id$ */ - /*! \file */ #include @@ -174,6 +172,16 @@ dns_master_style_debug = { 24, 32, 40, 48, 80, 8, UINT_MAX }; +/*% + * Similar, but indented (i.e., prepended with dns_master_indentstr). + */ +LIBDNS_EXTERNAL_DATA const dns_master_style_t +dns_master_style_indent = { + DNS_STYLEFLAG_REL_OWNER | + DNS_STYLEFLAG_INDENT, + 24, 32, 40, 48, 80, 8, UINT_MAX +}; + /*% * Similar, but with each line commented out. */ @@ -187,6 +195,11 @@ dns_master_style_comment = { }; +/*% + * Default indent string. + */ +LIBDNS_EXTERNAL_DATA const char *dns_master_indentstr = "\t"; + #define N_SPACES 10 static char spaces[N_SPACES+1] = " "; @@ -317,6 +330,16 @@ totext_ctx_init(const dns_master_style_t *style, dns_totext_ctx_t *ctx) { r.base[0] = '\n'; isc_buffer_add(&buf, 1); + if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0) { + size_t ilen = strlen(dns_master_indentstr); + isc_buffer_availableregion(&buf, &r); + if (r.length < ilen) + return (DNS_R_TEXTTOOLONG); + isc_buffer_putmem(&buf, + (const isc_uint8_t *) dns_master_indentstr, + ilen); + } + if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) { isc_buffer_availableregion(&buf, &r); if (r.length < 1) @@ -470,6 +493,12 @@ rdataset_totext(dns_rdataset_t *rdataset, while (result == ISC_R_SUCCESS) { column = 0; + /* + * Indent? + */ + if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0) + RETERR(str_totext(dns_master_indentstr, target)); + /* * Comment? */ @@ -585,6 +614,9 @@ rdataset_totext(dns_rdataset_t *rdataset, */ INDENT_TO(rdata_column); if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) { + if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0) + RETERR(str_totext(dns_master_indentstr, + target)); if (NXDOMAIN(rdataset)) RETERR(str_totext(";-$NXDOMAIN\n", target)); else @@ -945,7 +977,10 @@ dump_rdatasets_text(isc_mem_t *mctx, dns_name_t *name, for (i = 0; i < n; i++) { dns_rdataset_t *rds = sorted[i]; if (ctx->style.flags & DNS_STYLEFLAG_TRUST) - fprintf(f, "; %s\n", dns_trust_totext(rds->trust)); + fprintf(f, "%s; %s\n", + (ctx->style.flags & DNS_STYLEFLAG_INDENT) + ? dns_master_indentstr : "", + dns_trust_totext(rds->trust)); if (((rds->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) && (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) { /* Omit negative cache entries */ @@ -965,7 +1000,10 @@ dump_rdatasets_text(isc_mem_t *mctx, dns_name_t *name, memset(buf, 0, sizeof(buf)); isc_buffer_init(&b, buf, sizeof(buf) - 1); dns_time64_totext((isc_uint64_t)rds->resign, &b); - fprintf(f, "; resign=%s\n", buf); + fprintf(f, "%s; resign=%s\n", + (ctx->style.flags & DNS_STYLEFLAG_INDENT) + ? dns_master_indentstr : "", + buf); } dns_rdataset_disassociate(rds); } @@ -1979,6 +2017,12 @@ dns_master_dumpnode(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version, return (result); } +unsigned int +dns_master_styleflags(const dns_master_style_t *style) { + REQUIRE(style != NULL); + return (style->flags); +} + isc_result_t dns_master_stylecreate(dns_master_style_t **stylep, unsigned int flags, unsigned int ttl_column, unsigned int class_column, diff --git a/lib/dns/message.c b/lib/dns/message.c index 896f31d9a5..37477c532b 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -3156,6 +3156,7 @@ dns_message_sectiontotext(dns_message_t *msg, dns_section_t section, dns_rdataset_t *rdataset; isc_result_t result; isc_boolean_t seensoa = ISC_FALSE; + unsigned int sflags = dns_master_styleflags(style); REQUIRE(DNS_MESSAGE_VALID(msg)); REQUIRE(target != NULL); @@ -3165,6 +3166,8 @@ dns_message_sectiontotext(dns_message_t *msg, dns_section_t section, return (ISC_R_SUCCESS); if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) { + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); ADD_STRING(target, ";; "); if (msg->opcode != dns_opcode_update) { ADD_STRING(target, sectiontext[section]); @@ -3195,6 +3198,9 @@ dns_message_sectiontotext(dns_message_t *msg, dns_section_t section, seensoa = ISC_TRUE; } if (section == DNS_SECTION_QUESTION) { + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, + dns_master_indentstr); ADD_STRING(target, ";"); result = dns_master_questiontotext(name, rdataset, @@ -3212,8 +3218,11 @@ dns_message_sectiontotext(dns_message_t *msg, dns_section_t section, result = dns_message_nextname(msg, section); } while (result == ISC_R_SUCCESS); if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 && - (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) + (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) { + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); ADD_STRING(target, "\n"); + } if (result == ISC_R_NOMORE) result = ISC_R_SUCCESS; return (result); @@ -3278,6 +3287,7 @@ dns_message_pseudosectiontotext(dns_message_t *msg, isc_buffer_t optbuf; isc_uint16_t optcode, optlen; unsigned char *optdata; + unsigned int sflags = dns_master_styleflags(style); REQUIRE(DNS_MESSAGE_VALID(msg)); REQUIRE(target != NULL); @@ -3288,8 +3298,14 @@ dns_message_pseudosectiontotext(dns_message_t *msg, ps = dns_message_getopt(msg); if (ps == NULL) return (ISC_R_SUCCESS); - if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) + if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) { + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); ADD_STRING(target, ";; OPT PSEUDOSECTION:\n"); + } + + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); ADD_STRING(target, "; EDNS: version: "); snprintf(buf, sizeof(buf), "%u", (unsigned int)((ps->ttl & 0x00ff0000) >> 16)); @@ -3331,6 +3347,9 @@ dns_message_pseudosectiontotext(dns_message_t *msg, optlen = isc_buffer_getuint16(&optbuf); INSIST(isc_buffer_remaininglength(&optbuf) >= optlen); + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); + if (optcode == DNS_OPT_NSID) { ADD_STRING(target, "; NSID"); } else if (optcode == DNS_OPT_COOKIE) { @@ -3421,6 +3440,8 @@ dns_message_pseudosectiontotext(dns_message_t *msg, ps = dns_message_gettsig(msg, &name); if (ps == NULL) return (ISC_R_SUCCESS); + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n"); result = dns_master_rdatasettotext(name, ps, style, target); @@ -3432,6 +3453,8 @@ dns_message_pseudosectiontotext(dns_message_t *msg, ps = dns_message_getsig0(msg, &name); if (ps == NULL) return (ISC_R_SUCCESS); + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n"); result = dns_master_rdatasettotext(name, ps, style, target); @@ -3446,6 +3469,7 @@ dns_message_pseudosectiontotext(dns_message_t *msg, isc_result_t dns_message_totext(dns_message_t *msg, const dns_master_style_t *style, dns_messagetextflag_t flags, isc_buffer_t *target) { + unsigned int sflags = dns_master_styleflags(style); char buf[sizeof("1234567890")]; isc_result_t result; @@ -3453,6 +3477,8 @@ dns_message_totext(dns_message_t *msg, const dns_master_style_t *style, REQUIRE(target != NULL); if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) { + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); ADD_STRING(target, ";; ->>HEADER<<- opcode: "); ADD_STRING(target, opcodetext[msg->opcode]); ADD_STRING(target, ", status: "); @@ -3465,7 +3491,10 @@ dns_message_totext(dns_message_t *msg, const dns_master_style_t *style, ADD_STRING(target, ", id: "); snprintf(buf, sizeof(buf), "%6u", msg->id); ADD_STRING(target, buf); - ADD_STRING(target, "\n;; flags:"); + ADD_STRING(target, "\n"); + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); + ADD_STRING(target, ";; flags:"); if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) ADD_STRING(target, " qr"); if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) @@ -3483,11 +3512,18 @@ dns_message_totext(dns_message_t *msg, const dns_master_style_t *style, /* * The final unnamed flag must be zero. */ - if ((msg->flags & 0x0040U) != 0) + if ((msg->flags & 0x0040U) != 0) { + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); ADD_STRING(target, "; MBZ: 0x4"); + } if (msg->opcode != dns_opcode_update) { + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); ADD_STRING(target, "; QUESTION: "); } else { + if ((sflags & DNS_STYLEFLAG_INDENT) != 0) + ADD_STRING(target, dns_master_indentstr); ADD_STRING(target, "; ZONE: "); } snprintf(buf, sizeof(buf), "%1u", diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index dbbda3a02e..03c2649efc 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -2096,6 +2097,12 @@ resquery_send(resquery_t *query) { dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS]; unsigned ednsopt = 0; isc_uint16_t hint = 0, udpsize = 0; /* No EDNS */ +#ifdef HAVE_DNSTAP + unsigned char zone[DNS_NAME_MAXWIRE]; + dns_dtmsgtype_t dtmsgtype; + isc_region_t zr; + isc_buffer_t zb; +#endif /* HAVE_DNSTAP */ fctx = query->fctx; QTRACE("send"); @@ -2440,6 +2447,15 @@ resquery_send(resquery_t *query) { if (result != ISC_R_SUCCESS) goto cleanup_message; +#ifdef HAVE_DNSTAP + memset(&zr, 0, sizeof(zr)); + isc_buffer_init(&zb, zone, sizeof(zone)); + dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE); + result = dns_name_towire(&fctx->domain, &cctx, &zb); + if (result == ISC_R_SUCCESS) + isc_buffer_usedregion(&zb, &zr); +#endif /* HAVE_DNSTAP */ + dns_compress_invalidate(&cctx); cleanup_cctx = ISC_FALSE; @@ -2539,6 +2555,20 @@ resquery_send(resquery_t *query) { QTRACE("sent"); +#ifdef HAVE_DNSTAP + /* + * Log the outgoing query via dnstap. + */ + if ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0) + dtmsgtype = DNS_DTTYPE_FQ; + else + dtmsgtype = DNS_DTTYPE_RQ; + + dns_dt_send(fctx->res->view, dtmsgtype, &query->addrinfo->sockaddr, + ISC_TF((query->options & DNS_FETCHOPT_TCP) != 0), + &zr, &query->start, NULL, &query->buffer); +#endif /* HAVE_DNSTAP */ + return (ISC_R_SUCCESS); cleanup_message: @@ -7579,6 +7609,13 @@ resquery_response(isc_task_t *task, isc_event_t *event) { isc_result_t broken_server; badnstype_t broken_type = badns_response; isc_boolean_t no_response; +#ifdef HAVE_DNSTAP + unsigned char zone[DNS_NAME_MAXWIRE]; + dns_dtmsgtype_t dtmsgtype; + dns_compress_t cctx; + isc_region_t zr; + isc_buffer_t zb; +#endif /* HAVE_DNSTAP */ REQUIRE(VALID_QUERY(query)); fctx = query->fctx; @@ -7766,6 +7803,33 @@ resquery_response(isc_task_t *task, isc_event_t *event) { &dns_master_style_comment, ISC_LOG_DEBUG(10), fctx->res->mctx); + +#ifdef HAVE_DNSTAP + /* + * Log the response via dnstap. + */ + memset(&zr, 0, sizeof(zr)); + result = dns_compress_init(&cctx, -1, fctx->res->mctx); + if (result == ISC_R_SUCCESS) { + isc_buffer_init(&zb, zone, sizeof(zone)); + dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE); + result = dns_name_towire(&fctx->domain, &cctx, &zb); + if (result == ISC_R_SUCCESS) + isc_buffer_usedregion(&zb, &zr); + dns_compress_invalidate(&cctx); + } + + if ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0) + dtmsgtype = DNS_DTTYPE_FR; + else + dtmsgtype = DNS_DTTYPE_RR; + + dns_dt_send(fctx->res->view, dtmsgtype, + &query->addrinfo->sockaddr, + ISC_TF((query->options & DNS_FETCHOPT_TCP) != 0), + &zr, &query->start, NULL, &devent->buffer); +#endif /* HAVE_DNSTAP */ + /* * Process receive opt record. */ diff --git a/lib/dns/result.c b/lib/dns/result.c index 981356b9a3..204d2eeac9 100644 --- a/lib/dns/result.c +++ b/lib/dns/result.c @@ -165,7 +165,8 @@ static const char *text[DNS_R_NRESULTS] = { "covered by negative trust anchor", /*%< 110 DNS_R_NTACOVERED */ "bad CDS", /*%< 111 DNS_R_BADCSD */ "bad CDNSKEY", /*%< 112 DNS_R_BADCDNSKEY */ - "malformed OPT option" /*%< 113 DNS_R_OPTERR */ + "malformed OPT option", /*%< 113 DNS_R_OPTERR */ + "malformed DNSTAP data", /*%< 114 DNS_R_BADDNSTAP*/ }; static const char *ids[DNS_R_NRESULTS] = { @@ -284,6 +285,10 @@ static const char *ids[DNS_R_NRESULTS] = { "DNS_R_NOTDYNAMIC", "DNS_R_BADEUI", "DNS_R_NTACOVERED", + "DNS_R_BADCSD", + "DNS_R_BADCDNSKEY", + "DNS_R_OPTERR", + "DNS_R_BADDNSTAP", }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff --git a/lib/dns/tests/Makefile.in b/lib/dns/tests/Makefile.in index 6cfe520e3f..7b77d31c54 100644 --- a/lib/dns/tests/Makefile.in +++ b/lib/dns/tests/Makefile.in @@ -43,6 +43,7 @@ SRCS = db_test.c \ dbiterator_test.c \ dh_test.c \ dispatch_test.c \ + dnstap_test.c \ dnstest.c \ geoip_test.c \ gost_test.c \ @@ -69,6 +70,7 @@ TARGETS = db_test@EXEEXT@ \ dbversion_test@EXEEXT@ \ dh_test@EXEEXT@ \ dispatch_test@EXEEXT@ \ + dnstap_test@EXEEXT@ \ geoip_test@EXEEXT@ \ gost_test@EXEEXT@ \ keytable_test@EXEEXT@ \ @@ -173,6 +175,11 @@ dispatch_test@EXEEXT@: dispatch_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} dispatch_test.@O@ dnstest.@O@ ${DNSLIBS} \ ${ISCLIBS} ${LIBS} +dnstap_test@EXEEXT@: dnstap_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + dnstap_test.@O@ dnstest.@O@ ${DNSLIBS} \ + ${ISCLIBS} ${LIBS} + rdatasetstats_test@EXEEXT@: rdatasetstats_test.@O@ dnstest.@O@ ${ISCDEPLIBS} ${DNSDEPLIBS} ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ rdatasetstats_test.@O@ dnstest.@O@ ${DNSLIBS} \ diff --git a/lib/dns/tests/dnstap_test.c b/lib/dns/tests/dnstap_test.c new file mode 100644 index 0000000000..e85dea70ab --- /dev/null +++ b/lib/dns/tests/dnstap_test.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \file */ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "dnstest.h" + +#ifdef HAVE_DNSTAP +#include +#include + +#define TAPFILE "testdata/dnstap/dnstap.file" +#define TAPSOCK "testdata/dnstap/dnstap.sock" + +#define TAPSAVED "testdata/dnstap/dnstap.saved" +#define TAPTEXT "testdata/dnstap/dnstap.text" + +/* + * Helper functions + */ +static void +cleanup() { + (void) isc_file_remove(TAPFILE); + (void) isc_file_remove(TAPSOCK); +} + +/* + * Individual unit tests + */ + +ATF_TC(create); +ATF_TC_HEAD(create, tc) { + atf_tc_set_md_var(tc, "descr", "set up dnstap environment"); +} +ATF_TC_BODY(create, tc) { + isc_result_t result; + dns_dtenv_t *dtenv = NULL; + + UNUSED(tc); + + cleanup(); + + result = dns_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE(result == ISC_R_SUCCESS); + + result = dns_dt_create(mctx, dns_dtmode_file, TAPFILE, 1, &dtenv); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + if (dtenv != NULL) + dns_dt_detach(&dtenv); + + ATF_CHECK(isc_file_exists(TAPFILE)); + + result = dns_dt_create(mctx, dns_dtmode_unix, TAPSOCK, 1, &dtenv); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + if (dtenv != NULL) + dns_dt_detach(&dtenv); + + /* 'create' should succeed, but the file shouldn't exist yet */ + ATF_CHECK(!isc_file_exists(TAPSOCK)); + + result = dns_dt_create(mctx, 33, TAPSOCK, 1, &dtenv); + ATF_CHECK_EQ(result, ISC_R_FAILURE); + ATF_CHECK_EQ(dtenv, NULL); + + cleanup(); + + dns_dt_shutdown(); + dns_test_end(); +} + +ATF_TC(send); +ATF_TC_HEAD(send, tc) { + atf_tc_set_md_var(tc, "descr", "send dnstap messages"); +} +ATF_TC_BODY(send, tc) { + isc_result_t result; + dns_dtenv_t *dtenv = NULL; + dns_dthandle_t handle; + isc_uint8_t *data; + size_t dsize; + unsigned char zone[DNS_NAME_MAXWIRE]; + unsigned char qambuffer[4096], rambuffer[4096]; + unsigned char qrmbuffer[4096], rrmbuffer[4096]; + isc_buffer_t zb, qamsg, ramsg, qrmsg, rrmsg; + size_t qasize, qrsize, rasize, rrsize; + dns_fixedname_t zfname; + dns_name_t *zname; + dns_dtmsgtype_t dt; + dns_view_t *view = NULL; + dns_compress_t cctx; + isc_region_t zr; + isc_sockaddr_t addr; + struct in_addr in; + isc_stdtime_t now; + isc_time_t p, f; + + UNUSED(tc); + + cleanup(); + + result = dns_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE(result == ISC_R_SUCCESS); + + result = dns_test_makeview("test", &view); + + result = dns_dt_create(mctx, dns_dtmode_file, TAPFILE, 1, &dtenv); + ATF_REQUIRE(result == ISC_R_SUCCESS); + + dns_dt_attach(dtenv, &view->dtenv); + view->dttypes = DNS_DTTYPE_ALL; + + /* + * Set up some test data + */ + dns_fixedname_init(&zfname); + zname = dns_fixedname_name(&zfname); + isc_buffer_constinit(&zb, "example.com.", 12); + isc_buffer_add(&zb, 12); + result = dns_name_fromtext(zname, &zb, NULL, 0, NULL); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + memset(&zr, 0, sizeof(zr)); + isc_buffer_init(&zb, zone, sizeof(zone)); + result = dns_compress_init(&cctx, -1, mctx); + dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE); + result = dns_name_towire(zname, &cctx, &zb); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + dns_compress_invalidate(&cctx); + isc_buffer_usedregion(&zb, &zr); + + in.s_addr = inet_addr("10.53.0.1"); + isc_sockaddr_fromin(&addr, &in, 2112); + + isc_stdtime_get(&now); + isc_time_set(&p, now - 3600, 0); /* past */ + isc_time_set(&f, now + 3600, 0); /* future */ + + result = dns_test_getdata("testdata/dnstap/query.auth", + qambuffer, sizeof(qambuffer), &qasize); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_buffer_init(&qamsg, qambuffer, qasize); + isc_buffer_add(&qamsg, qasize); + + result = dns_test_getdata("testdata/dnstap/response.auth", + rambuffer, sizeof(rambuffer), &rasize); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_buffer_init(&ramsg, rambuffer, rasize); + isc_buffer_add(&ramsg, rasize); + + result = dns_test_getdata("testdata/dnstap/query.recursive", qrmbuffer, + sizeof(qrmbuffer), &qrsize); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_buffer_init(&qrmsg, qrmbuffer, qrsize); + isc_buffer_add(&qrmsg, qrsize); + + result = dns_test_getdata("testdata/dnstap/response.recursive", + rrmbuffer, sizeof(rrmbuffer), &rrsize); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_buffer_init(&rrmsg, rrmbuffer, rrsize); + isc_buffer_add(&rrmsg, rrsize); + + for (dt = DNS_DTTYPE_SQ; dt <= DNS_DTTYPE_TR; dt <<= 1) { + isc_buffer_t *m; + + switch (dt) { + case DNS_DTTYPE_AQ: + m = &qamsg; + break; + case DNS_DTTYPE_AR: + m = &ramsg; + break; + default: + m = &qrmsg; + if ((dt & DNS_DTTYPE_RESPONSE) != 0) + m = &ramsg; + break; + } + + dns_dt_send(view, dt, &addr, ISC_FALSE, &zr, &p, &f, m); + dns_dt_send(view, dt, &addr, ISC_FALSE, &zr, NULL, &f, m); + dns_dt_send(view, dt, &addr, ISC_FALSE, &zr, &p, NULL, m); + dns_dt_send(view, dt, &addr, ISC_FALSE, &zr, NULL, NULL, m); + dns_dt_send(view, dt, &addr, ISC_TRUE, &zr, &p, &f, m); + dns_dt_send(view, dt, &addr, ISC_TRUE, &zr, NULL, &f, m); + dns_dt_send(view, dt, &addr, ISC_TRUE, &zr, &p, NULL, m); + dns_dt_send(view, dt, &addr, ISC_TRUE, &zr, NULL, NULL, m); + } + + dns_dt_detach(&view->dtenv); + dns_dt_detach(&dtenv); + dns_dt_shutdown(); + dns_view_detach(&view); + + /* + * XXX now read back and check content. + */ + + result = dns_dt_open(TAPFILE, dns_dtmode_file, &handle); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + while (dns_dt_getframe(&handle, &data, &dsize) == ISC_R_SUCCESS) { + dns_dtdata_t *dtdata = NULL; + isc_region_t r; + static dns_dtmsgtype_t expected = DNS_DTTYPE_SQ; + static int n = 0; + + r.base = data; + r.length = dsize; + + result = dns_dt_parse(mctx, &r, &dtdata); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) { + n++; + continue; + } + + ATF_CHECK_EQ(dtdata->type, expected); + if (++n % 8 == 0) + expected <<= 1; + + dns_dtdata_free(&dtdata); + } + + dns_dt_close(&handle); + cleanup(); + + dns_test_end(); +} + +ATF_TC(totext); +ATF_TC_HEAD(totext, tc) { + atf_tc_set_md_var(tc, "descr", "dnstap message to text"); +} +ATF_TC_BODY(totext, tc) { + isc_result_t result; + dns_dthandle_t handle; + isc_uint8_t *data; + size_t dsize; + FILE *fp = NULL; + + UNUSED(tc); + + result = dns_test_begin(NULL, ISC_TRUE); + ATF_REQUIRE(result == ISC_R_SUCCESS); + + result = dns_dt_open(TAPSAVED, dns_dtmode_file, &handle); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_stdio_open(TAPTEXT, "r", &fp); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* make sure text conversion gets the right local time */ + setenv("TZ", "MST7", 1); + + while (dns_dt_getframe(&handle, &data, &dsize) == ISC_R_SUCCESS) { + dns_dtdata_t *dtdata = NULL; + isc_buffer_t *b = NULL; + isc_region_t r; + char s[BUFSIZ], *p; + + r.base = data; + r.length = dsize; + + /* read the corresponding line of text */ + p = fgets(s, sizeof(s), fp); + ATF_CHECK_EQ(p, s); + if (p == NULL) + break; + + p = strchr(p, '\n'); + if (p != NULL) + *p = '\0'; + + /* parse dnstap frame */ + result = dns_dt_parse(mctx, &r, &dtdata); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + continue; + + isc_buffer_allocate(mctx, &b, 2048); + ATF_CHECK(b != NULL); + if (b == NULL) + break; + + /* convert to text and compare */ + result = dns_dt_datatotext(dtdata, &b); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK_STREQ((char *) isc_buffer_base(b), s); + + dns_dtdata_free(&dtdata); + isc_buffer_free(&b); + } + + dns_dt_close(&handle); + cleanup(); + + dns_test_end(); +} + +#else +ATF_TC(untested); +ATF_TC_HEAD(untested, tc) { + atf_tc_set_md_var(tc, "descr", "skipping dnstap test"); +} +ATF_TC_BODY(untested, tc) { + UNUSED(tc); + atf_tc_skip("dnstap not available"); +} +#endif + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { +#ifdef HAVE_DNSTAP + ATF_TP_ADD_TC(tp, create); + ATF_TP_ADD_TC(tp, send); + ATF_TP_ADD_TC(tp, totext); +#else + ATF_TP_ADD_TC(tp, untested); +#endif + + return (atf_no_error()); +} diff --git a/lib/dns/tests/dnstest.c b/lib/dns/tests/dnstest.c index 6aa6adc924..7f22d033bf 100644 --- a/lib/dns/tests/dnstest.c +++ b/lib/dns/tests/dnstest.c @@ -26,11 +26,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -341,3 +343,71 @@ dns_test_loaddb(dns_db_t **db, dns_dbtype_t dbtype, const char *origin, result = dns_db_load(*db, testfile); return (result); } + +static int +fromhex(char c) { + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + + printf("bad input format: %02x\n", c); + exit(3); + /* NOTREACHED */ +} + +isc_result_t +dns_test_getdata(const char *file, unsigned char *buf, + size_t bufsiz, size_t *sizep) +{ + isc_result_t result; + unsigned char *bp; + char *rp, *wp; + char s[BUFSIZ]; + size_t len, i; + FILE *f; + int n; + + result = isc_stdio_open(file, "r", &f); + if (result != ISC_R_SUCCESS) + return (result); + + bp = buf; + while (fgets(s, sizeof(s), f) != NULL) { + rp = s; + wp = s; + len = 0; + while (*rp != '\0') { + if (*rp == '#') + break; + if (*rp != ' ' && *rp != '\t' && + *rp != '\r' && *rp != '\n') { + *wp++ = *rp; + len++; + } + rp++; + } + if (len == 0U) + continue; + if (len % 2 != 0U) + return (ISC_R_UNEXPECTEDEND); + if (len > bufsiz * 2) + return (ISC_R_NOSPACE); + rp = s; + for (i = 0; i < len; i += 2) { + n = fromhex(*rp++); + n *= 16; + n += fromhex(*rp++); + *bp++ = n; + } + } + + isc_stdio_close(f); + + *sizep = bp - buf; + + return (ISC_R_SUCCESS); +} + diff --git a/lib/dns/tests/dnstest.h b/lib/dns/tests/dnstest.h index b8ccdd45cb..12aa34d799 100644 --- a/lib/dns/tests/dnstest.h +++ b/lib/dns/tests/dnstest.h @@ -82,3 +82,7 @@ dns_test_nap(isc_uint32_t usec); isc_result_t dns_test_loaddb(dns_db_t **db, dns_dbtype_t dbtype, const char *origin, const char *testfile); + +isc_result_t +dns_test_getdata(const char *file, unsigned char *buf, + size_t bufsiz, size_t *sizep); diff --git a/lib/dns/tests/testdata/dnstap/dnstap.saved b/lib/dns/tests/testdata/dnstap/dnstap.saved new file mode 100644 index 0000000000000000000000000000000000000000..9ccb166275f157703786b53f8ec5bb31186e9c41 GIT binary patch literal 29534 zcmeHPPiP!v6#wQ=He=Jm9z+wUWvvyW(2|2fED8}S>_rHzAb2sGHfaLgKb@&$LA-fT zp&mpf2hkopn3{M45)SXG^sDI#P z;mi!{b3a}G=liiY<$L1v^j6?Bv;}F$TU%T6R-?0CH$1P*O2aSI)oJqRt53DOS()-Sgc{^bV;atZd0d->RhL?&O1|BqqA zgTyoOs_?&fa_)^`T~b<7?QlaJ@>=bv7ImWIy~Sajy)o`b%ZshyYwV>dw-YJmPPCR+ zFRZqrvQtYUZIbaAL0L8bB@U8hqphn-f+f*SWtY|$IS2(ey%to zpC`|~xO1wQ8J>>MmH&z-pR;rQd-6=RTGcVd7S_U_|H>8ARxgJDOMWTxOUMOFMWIZ_ zX%H!-0mK3j!8nWMnaenqtiZS;#_6orUb#D{A;ziPRw~AkbeR25rf4v(0K^4ioB|7s zvtt|+tB7&Xal0aVlUbE94mu8u+grvV##u0qvTFaCO=MNwqcGQTEXI+JYd*gT9ajkA zwvJO^LC2ZsINhTi%WcQm_j^;eh`hI7lH{#yi-q3Gw#ct5bEW-a3$(Id%oh38Ywl0K zrr)yl%XvNg(G6+|f}x>g*-!$As@*M*vQ2YUKV*Wams@)!Lb{-fC`AJ0At27a4-joZ zi~~PnV(FL&5KCQ1OvXe8BE`hvr&!JvmNS94@fkog15wu@PC_Oo=1wjj6Ag9MT^?q0 zCn;N`tHpOpzgD%y^?p@b&4cr95-Z?bB z)Ni$W7nZ!U-L^mqwoSpnbf+OD$AGGj_UMdkfGOpc8@G^K%yNs#+9ym`?f_F;Oyi`1 zcv`VT@nG!()6vJ%d`xNW-|?>En!!#!d+0=E98Zfd9ZmpKvv?}r4$lOhlBX}~x%=6Y zbN8tjtRzL2X_EK-r>|OEmN7KVt0U991q@A-*E7$J%4>J`ux6&o>)R(DN3XA(tmw7- zL3qy({9IyVGO=JXF|{;Mq*@lKnFPexnE4P72v|+kXA+PlNkl-)2uN~$eF@UiOj>3V zkVMJ+MVa0~z)}QM*kQ)ZWX3EeAXCMNfJni6alfppfHMgQ4G020QUW3ZmM0)-!2ah* z!L}NZrC`#4&99&Vi$P3R!9+lX9WE)6P0}*RB{qs{%i>zga%^v7v=>lZ+oTAH+NQj; z1Od&l%OxO7H;90i5s;+i{NIq4#j-q?fFw#9cV&790ZS23VTanrq_&AQAXCMNfaYl= zRn%d|q123n(twPB(10M|-uAas1VjWZPe9Uu&8cfh!A2v=nJJhw;N-K{paBa(Os8NX zpu!Fs&_o03`&TDa8*oJXfTwH`mv5bsTsH3C>gKrGgFR)71?I}O$glNxOZyeyLXfzB z)pT+^@jo(KV;poG x7`L~KLyWUv9O<|lzagvI>NpnTNXM=J4joqv;`YAj3M}Y26CI~_BJ|~>cOU)%cJcrK literal 0 HcmV?d00001 diff --git a/lib/dns/tests/testdata/dnstap/dnstap.text b/lib/dns/tests/testdata/dnstap/dnstap.text new file mode 100644 index 0000000000..e052ab4fa0 --- /dev/null +++ b/lib/dns/tests/testdata/dnstap/dnstap.text @@ -0,0 +1,96 @@ +18-Sep-2015 12:06:38.000 SQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 SQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 SQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 SQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 SQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 SQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 SQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 SQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 SR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 SR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 SR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 SR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 SR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 SR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 SR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 SR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 CQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 CQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 CQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 CQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 CQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 CQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 CQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 CQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 CR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 CR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 CR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 CR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 CR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 CR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 CR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 CR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 AQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 AQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 AQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 AQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 AQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 AQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 AQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 AQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 AR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 AR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 AR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 AR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 AR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 AR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 AR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 AR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 RQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 RQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 RQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 RQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 RQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 RQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 RQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 RQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 RR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 RR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 RR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 RR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 RR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 RR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 RR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 RR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 FQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 FQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 FQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 FQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 FQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 FQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 FQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 FQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 FR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 FR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 FR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 FR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 FR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 FR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 FR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 FR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 TQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 TQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 TQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 TQ 10.53.0.1 UDP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 TQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 TQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 12:06:38.000 TQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 TQ 10.53.0.1 TCP 40b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 TR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 TR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 TR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 TR 10.53.0.1 UDP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 TR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 14:06:38.000 TR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 TR 10.53.0.1 TCP 287b www.isc.org/IN/A +18-Sep-2015 13:06:38.112 TR 10.53.0.1 TCP 287b www.isc.org/IN/A diff --git a/lib/dns/tests/testdata/dnstap/query.auth b/lib/dns/tests/testdata/dnstap/query.auth new file mode 100644 index 0000000000..a14f85091e --- /dev/null +++ b/lib/dns/tests/testdata/dnstap/query.auth @@ -0,0 +1,4 @@ +# authoritative query, www.isc.org/A +8d 24 00 20 00 01 00 00 00 00 00 01 03 77 77 77 +03 69 73 63 03 6f 72 67 00 00 01 00 01 00 00 29 +10 00 00 00 00 00 00 00 diff --git a/lib/dns/tests/testdata/dnstap/query.recursive b/lib/dns/tests/testdata/dnstap/query.recursive new file mode 100644 index 0000000000..8ee705f432 --- /dev/null +++ b/lib/dns/tests/testdata/dnstap/query.recursive @@ -0,0 +1,4 @@ +# recursive query for www.isc.org/A +bf 08 01 20 00 01 00 00 00 00 00 01 03 77 77 77 +03 69 73 63 03 6f 72 67 00 00 01 00 01 00 00 29 +10 00 00 00 00 00 00 00 diff --git a/lib/dns/tests/testdata/dnstap/response.auth b/lib/dns/tests/testdata/dnstap/response.auth new file mode 100644 index 0000000000..4d0ea815a9 --- /dev/null +++ b/lib/dns/tests/testdata/dnstap/response.auth @@ -0,0 +1,19 @@ +# authoritative response, www.isc.org/A +8d 24 84 00 00 01 00 01 00 04 00 07 03 77 77 77 +03 69 73 63 03 6f 72 67 00 00 01 00 01 c0 0c 00 +01 00 01 00 00 00 3c 00 04 95 14 40 45 c0 10 00 +02 00 01 00 00 1c 20 00 0d 03 61 6d 73 06 73 6e +73 2d 70 62 c0 10 c0 10 00 02 00 01 00 00 1c 20 +00 07 04 73 66 62 61 c0 3d c0 10 00 02 00 01 00 +00 1c 20 00 19 02 6e 73 03 69 73 63 0b 61 66 69 +6c 69 61 73 2d 6e 73 74 04 69 6e 66 6f 00 c0 10 +00 02 00 01 00 00 1c 20 00 06 03 6f 72 64 c0 3d +c0 39 00 01 00 01 00 00 1c 20 00 04 c7 06 01 1e +c0 39 00 1c 00 01 00 00 1c 20 00 10 20 01 05 00 +00 60 00 00 00 00 00 00 00 00 00 30 c0 8a 00 01 +00 01 00 00 1c 20 00 04 c7 06 00 1e c0 8a 00 1c +00 01 00 00 1c 20 00 10 20 01 05 00 00 71 00 00 +00 00 00 00 00 00 00 30 c0 52 00 01 00 01 00 00 +1c 20 00 04 95 14 40 03 c0 52 00 1c 00 01 00 00 +1c 20 00 10 20 01 04 f8 00 00 00 02 00 00 00 00 +00 00 00 19 00 00 29 10 00 00 00 00 00 00 00 diff --git a/lib/dns/tests/testdata/dnstap/response.recursive b/lib/dns/tests/testdata/dnstap/response.recursive new file mode 100644 index 0000000000..6e3a3cf34b --- /dev/null +++ b/lib/dns/tests/testdata/dnstap/response.recursive @@ -0,0 +1,19 @@ +# recursive response, www.isc.org/A +bf 08 81 a0 00 01 00 01 00 04 00 07 03 77 77 77 +03 69 73 63 03 6f 72 67 00 00 01 00 01 c0 0c 00 +01 00 01 00 00 00 15 00 04 95 14 40 45 c0 10 00 +02 00 01 00 00 1b a6 00 0e 04 73 66 62 61 06 73 +6e 73 2d 70 62 c0 10 c0 10 00 02 00 01 00 00 1b +a6 00 06 03 6f 72 64 c0 3e c0 10 00 02 00 01 00 +00 1b a6 00 19 02 6e 73 03 69 73 63 0b 61 66 69 +6c 69 61 73 2d 6e 73 74 04 69 6e 66 6f 00 c0 10 +00 02 00 01 00 00 1b a6 00 06 03 61 6d 73 c0 3e +c0 8a 00 01 00 01 00 00 b1 d5 00 04 c7 06 01 1e +c0 8a 00 1c 00 01 00 00 b1 d5 00 10 20 01 05 00 +00 60 00 00 00 00 00 00 00 00 00 30 c0 53 00 01 +00 01 00 00 b1 d5 00 04 c7 06 00 1e c0 53 00 1c +00 01 00 00 b1 d5 00 10 20 01 05 00 00 71 00 00 +00 00 00 00 00 00 00 30 c0 39 00 01 00 01 00 00 +b1 d5 00 04 95 14 40 03 c0 39 00 1c 00 01 00 00 +b1 d5 00 10 20 01 04 f8 00 00 00 02 00 00 00 00 +00 00 00 19 00 00 29 10 00 00 00 00 00 00 00 diff --git a/lib/dns/view.c b/lib/dns/view.c index 6d47aebc71..612849cddb 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -242,6 +242,8 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass, view->failcache = NULL; view->v6bias = 0; dns_badcache_init(view->mctx, DNS_VIEW_FAILCACHESIZE, &view->failcache); + view->dtenv = NULL; + view->dttypes = 0; if (isc_bind9) { result = dns_order_create(view->mctx, &view->order); @@ -490,6 +492,10 @@ destroy(dns_view_t *view) { dns_zone_detach(&view->managed_keys); if (view->redirect != NULL) dns_zone_detach(&view->redirect); +#ifdef HAVE_DNSTAP + if (view->dtenv != NULL) + dns_dt_detach(&view->dtenv); +#endif /* HAVE_DNSTAP */ dns_view_setnewzones(view, ISC_FALSE, NULL, NULL); dns_fwdtable_destroy(&view->fwdtable); dns_aclenv_destroy(&view->aclenv); diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h index f8dc368a9f..c6ff940f19 100644 --- a/lib/isc/include/isc/task.h +++ b/lib/isc/include/isc/task.h @@ -603,9 +603,14 @@ isc_task_endexclusive(isc_task_t *task); void isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t); +void +isc_task_getcurrenttimex(isc_task_t *task, isc_time_t *t); /*%< * Provide the most recent timestamp on the task. The timestamp is considered - * as the "current time" in the second-order granularity. + * as the "current time". + * + * isc_task_getcurrentime() returns the time in one-second granularity; + * isc_task_getcurrentimex() returns it in nanosecond granularity. * * Requires: *\li 'task' is a valid task. diff --git a/lib/isc/task.c b/lib/isc/task.c index 416f89c8f7..e8a0430cbd 100644 --- a/lib/isc/task.c +++ b/lib/isc/task.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -116,6 +117,7 @@ struct isc__task { unsigned int quantum; unsigned int flags; isc_stdtime_t now; + isc_time_t tnow; char name[16]; void * tag; /* Locked by task manager lock. */ @@ -228,6 +230,8 @@ void * isc__task_gettag(isc_task_t *task0); void isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t); +void +isc__task_getcurrenttimex(isc_task_t *task0, isc_time_t *t); isc_result_t isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, unsigned int default_quantum, isc_taskmgr_t **managerp); @@ -265,7 +269,8 @@ static struct isc__taskmethods { /*% * The following are defined just for avoiding unused static functions. */ - void *purgeevent, *unsendrange, *getname, *gettag, *getcurrenttime; + void *purgeevent, *unsendrange, *getname, *gettag, + *getcurrenttime, *getcurrenttimex; } taskmethods = { { isc__task_attach, @@ -288,7 +293,8 @@ static struct isc__taskmethods { (void *)isc__task_unsendrange, (void *)isc__task_getname, (void *)isc__task_gettag, - (void *)isc__task_getcurrenttime + (void *)isc__task_getcurrenttime, + (void *)isc__task_getcurrenttimex }; static isc_taskmgrmethods_t taskmgrmethods = { @@ -367,6 +373,7 @@ isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, task->quantum = quantum; task->flags = 0; task->now = 0; + isc_time_settoepoch(&task->tnow); memset(task->name, 0, sizeof(task->name)); task->tag = NULL; INIT_LINK(task, link); @@ -923,6 +930,18 @@ isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) { UNLOCK(&task->lock); } +void +isc__task_getcurrenttimex(isc_task_t *task0, isc_time_t *t) { + isc__task_t *task = (isc__task_t *)task0; + + REQUIRE(VALID_TASK(task)); + REQUIRE(t != NULL); + + LOCK(&task->lock); + *t = task->tnow; + UNLOCK(&task->lock); +} + /*** *** Task Manager. ***/ @@ -1111,7 +1130,8 @@ dispatch(isc__taskmgr_t *manager) { task->state = task_state_running; XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_RUNNING, "running")); - isc_stdtime_get(&task->now); + TIME_NOW(&task->tnow); + task->now = isc_time_seconds(&task->tnow); do { if (!EMPTY(task->events)) { event = HEAD(task->events); @@ -2282,6 +2302,14 @@ isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t) { isc__task_getcurrenttime(task, t); } +void +isc_task_getcurrenttimex(isc_task_t *task, isc_time_t *t) { + if (!isc_bind9) + return; + + isc__task_getcurrenttimex(task, t); +} + /*% * This is necessary for libisc's internal timer implementation. Other * implementation might skip implementing this. diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index b08795612b..0362ca3c67 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -636,6 +636,7 @@ isc_task_detach isc_task_endexclusive isc_task_exiting isc_task_getcurrenttime +isc_task_getcurrenttimex isc_task_onshutdown isc_task_privilege isc_task_purge diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index cb8a8cbf5c..f335ab7a0b 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -145,6 +145,8 @@ static cfg_type_t cfg_type_zoneopts; static cfg_type_t cfg_type_filter_aaaa; static cfg_type_t cfg_type_dlz; static cfg_type_t cfg_type_dyndb; +static cfg_type_t cfg_type_dnstap; +static cfg_type_t cfg_type_dnstapoutput; /*% tkey-dhkey */ @@ -926,20 +928,23 @@ static cfg_type_t cfg_type_fetchquota = { static const char *response_enums[] = { "drop", "fail", NULL }; static isc_result_t -parse_optional_response(cfg_parser_t *pctx, const cfg_type_t *type, - cfg_obj_t **ret) +parse_optional_enum(cfg_parser_t *pctx, const cfg_type_t *type, + cfg_obj_t **ret) { return (parse_enum_or_other(pctx, type, &cfg_type_void, ret)); } static void -doc_optional_response(cfg_printer_t *pctx, const cfg_type_t *type) { - doc_enum_or_other(pctx, type, &cfg_type_void); +doc_optional_enum(cfg_printer_t *pctx, const cfg_type_t *type) { + UNUSED(type); + cfg_print_cstr(pctx, "[ "); + cfg_doc_enum(pctx, type); + cfg_print_cstr(pctx, " ]"); } static cfg_type_t cfg_type_responsetype = { - "responsetype", parse_optional_response, cfg_print_ustring, - doc_optional_response, &cfg_rep_string, response_enums + "responsetype", parse_optional_enum, cfg_print_ustring, + doc_optional_enum, &cfg_rep_string, response_enums }; static cfg_tuplefielddef_t fetchesper_fields[] = { @@ -1011,6 +1016,16 @@ options_clauses[] = { { "cookie-algorithm", &cfg_type_cookiealg, 0 }, { "coresize", &cfg_type_size, 0 }, { "datasize", &cfg_type_size, 0 }, +#ifdef HAVE_DNSTAP + { "dnstap-output", &cfg_type_dnstapoutput, 0 }, + { "dnstap-identity", &cfg_type_serverid, 0 }, + { "dnstap-version", &cfg_type_qstringornone, 0 }, +#else + { "dnstap-output", &cfg_type_dnstapoutput, + CFG_CLAUSEFLAG_NOTCONFIGURED }, + { "dnstap-identity", &cfg_type_serverid, CFG_CLAUSEFLAG_NOTCONFIGURED }, + { "dnstap-version", &cfg_type_qstringornone, CFG_CLAUSEFLAG_NOTCONFIGURED }, +#endif /* HAVE_DNSTAP */ { "session-keyfile", &cfg_type_qstringornone, 0 }, { "session-keyname", &cfg_type_astring, 0 }, { "session-keyalg", &cfg_type_astring, 0 }, @@ -1173,6 +1188,63 @@ static cfg_type_t cfg_type_masterstyle = { &cfg_rep_string, &masterstyle_enums }; +/*% + * dnstap { + * [query | response] ; + * ... + * } + * + * ... where message type is one of: client, resolver, auth, forwarder, all + */ +static const char *dnstap_types[] = { "client", "resolver", + "auth", "forwarder", "all", NULL }; + +static const char *dnstap_modes[] = { "query", "response", NULL }; + +static cfg_type_t cfg_type_dnstap_type = { + "dnstap_type", cfg_parse_enum, cfg_print_ustring, + cfg_doc_enum, &cfg_rep_string, dnstap_types +}; + +static cfg_type_t cfg_type_dnstap_mode = { + "dnstap_mode", parse_optional_enum, cfg_print_ustring, + doc_optional_enum, &cfg_rep_string, dnstap_modes +}; + +static cfg_tuplefielddef_t dnstap_fields[] = { + { "type", &cfg_type_dnstap_type, 0 }, + { "mode", &cfg_type_dnstap_mode, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_dnstap_entry = { + "dnstap_value", cfg_parse_tuple, cfg_print_tuple, + cfg_doc_tuple, &cfg_rep_tuple, dnstap_fields }; + +static cfg_type_t cfg_type_dnstap = { + "dnstap", cfg_parse_bracketed_list, cfg_print_bracketed_list, + cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_dnstap_entry +}; + +/*% + * dnstap-output + */ +static const char *dtoutmode_enums[] = { "file", "unix", NULL }; +static cfg_type_t cfg_type_dtmode = { + "dtmode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, + &cfg_rep_string, &dtoutmode_enums +}; + +static cfg_tuplefielddef_t dtout_fields[] = { + { "mode", &cfg_type_dtmode, 0 }, + { "path", &cfg_type_qstring, 0 }, + { NULL, NULL, 0 } +}; + +static cfg_type_t cfg_type_dnstapoutput = { + "dnstapoutput", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, dtout_fields +}; /*% * response-policy { @@ -1572,6 +1644,11 @@ view_clauses[] = { { "dnssec-must-be-secure", &cfg_type_mustbesecure, CFG_CLAUSEFLAG_MULTI }, { "dnssec-validation", &cfg_type_boolorauto, 0 }, +#ifdef HAVE_DNSTAP + { "dnstap", &cfg_type_dnstap, 0 }, +#else + { "dnstap", &cfg_type_dnstap, CFG_CLAUSEFLAG_NOTCONFIGURED }, +#endif /* HAVE_DNSTAP */ { "dual-stack-servers", &cfg_type_nameportiplist, 0 }, { "edns-udp-size", &cfg_type_uint32, 0 }, { "empty-contact", &cfg_type_astring, 0 }, diff --git a/win32utils/Configure b/win32utils/Configure index 93393bbc49..25fb03ff4e 100644 --- a/win32utils/Configure +++ b/win32utils/Configure @@ -3049,6 +3049,7 @@ exit 0; # Notes: Unix configure.in options # --enable-developer partially supported +# --enable-dnstap not supported (requires libfstrm support on win32) # --enable-newstats (9.9/9.9sub only) # --enable-native-pkcs11 supported # --enable-openssl-version-check included without a way to disable it @@ -3086,6 +3087,8 @@ exit 0; # --with-idn support # --with-[lib]iconv (part of IDN) # --with-atf not supported on WIN32 (package not available on WIN32) +# --with-libfrtrm not supported (not yet available on WIN32) +# --with-protobuf-c not supported (no reason to until libfstrm is ready) # --with-tuning supported # --with-dlopen included without a way to disable it # --with-dlz-* ?