]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] dnstap
authorEvan Hunt <each@isc.org>
Fri, 2 Oct 2015 19:32:42 +0000 (12:32 -0700)
committerEvan Hunt <each@isc.org>
Fri, 2 Oct 2015 19:32:42 +0000 (12:32 -0700)
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]

65 files changed:
CHANGES
COPYRIGHT
README
bin/named/Makefile.in
bin/named/client.c
bin/named/config.c
bin/named/include/named/client.h
bin/named/include/named/globals.h
bin/named/include/named/server.h
bin/named/server.c
bin/named/win32/os.c
bin/tests/system/conf.sh.in
bin/tests/system/dnstap/clean.sh [new file with mode: 0644]
bin/tests/system/dnstap/ns1/named.conf [new file with mode: 0644]
bin/tests/system/dnstap/ns1/root.db [new file with mode: 0644]
bin/tests/system/dnstap/ns2/example.db [new file with mode: 0644]
bin/tests/system/dnstap/ns2/named.conf [new file with mode: 0644]
bin/tests/system/dnstap/ns3/named.conf [new file with mode: 0644]
bin/tests/system/dnstap/tests.sh [new file with mode: 0644]
bin/tools/.gitignore
bin/tools/Makefile.in
bin/tools/dnstap-read.1 [new file with mode: 0644]
bin/tools/dnstap-read.c [new file with mode: 0644]
bin/tools/dnstap-read.docbook [new file with mode: 0644]
bin/tools/dnstap-read.html [new file with mode: 0644]
config.h.in
configure
configure.in
doc/arm/Bv9ARM-book.xml
doc/arm/man.dnstap-read.html [new file with mode: 0644]
doc/arm/notes.xml
lib/dns/Makefile.in
lib/dns/dnstap.c [new file with mode: 0644]
lib/dns/dnstap.pb-c.c [new file with mode: 0644]
lib/dns/dnstap.pb-c.h [new file with mode: 0644]
lib/dns/dnstap.proto [new file with mode: 0644]
lib/dns/include/dns/Makefile.in
lib/dns/include/dns/dnstap.h [new file with mode: 0644]
lib/dns/include/dns/dnstap.pb-c.h [new file with mode: 0644]
lib/dns/include/dns/log.h
lib/dns/include/dns/masterdump.h
lib/dns/include/dns/result.h
lib/dns/include/dns/types.h
lib/dns/include/dns/view.h
lib/dns/log.c
lib/dns/masterdump.c
lib/dns/message.c
lib/dns/resolver.c
lib/dns/result.c
lib/dns/tests/Makefile.in
lib/dns/tests/dnstap_test.c [new file with mode: 0644]
lib/dns/tests/dnstest.c
lib/dns/tests/dnstest.h
lib/dns/tests/testdata/dnstap/dnstap.saved [new file with mode: 0644]
lib/dns/tests/testdata/dnstap/dnstap.text [new file with mode: 0644]
lib/dns/tests/testdata/dnstap/query.auth [new file with mode: 0644]
lib/dns/tests/testdata/dnstap/query.recursive [new file with mode: 0644]
lib/dns/tests/testdata/dnstap/response.auth [new file with mode: 0644]
lib/dns/tests/testdata/dnstap/response.recursive [new file with mode: 0644]
lib/dns/view.c
lib/isc/include/isc/task.h
lib/isc/task.c
lib/isc/win32/libisc.def.in
lib/isccfg/namedconf.c
win32utils/Configure

diff --git a/CHANGES b/CHANGES
index d93b1ad95315960b111db7afee921840983a7344..f050fc8fb38056abeac408527064cd160e761b12 100644 (file)
--- 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]
 
index 2d3c6ac47b34b77c58b537a679ca31c073d69298..24310b70c6c3959a5fdfc80be0568d14b68e7124 100644 (file)
--- 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 2390e57bae4124dfff85dc83bd5b5d5903596b3c..36e4771f7f9cbc113bec061713773c92211a3afb 100644 (file)
--- 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
index 0f65097e6b522df0f1ade3e5885043db72869217..899756a1defa743511438f409d386c266bc18ee7 100644 (file)
@@ -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}"; \
index cad69d8f4c04b35eeaf6f7ca8ba5a0dc34e08005..f2e41f01cba32febd65c37bb185ce40ae30fae68 100644 (file)
@@ -38,6 +38,7 @@
 #include <dns/badcache.h>
 #include <dns/db.h>
 #include <dns/dispatch.h>
+#include <dns/dnstap.h>
 #include <dns/edns.h>
 #include <dns/events.h>
 #include <dns/message.h>
@@ -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);
index aed352316bc72f94e633d7d60c4b67b56f1ca936..4743be09d192ade7df6eae7c4fa6a0718abd3451 100644 (file)
@@ -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\
index 60d5a6be053a1ad80f54ec36892a7034adbdb8c5..9e2ab242d2b4c50d2123e7aaa0cbfa390b299f20 100644 (file)
@@ -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 */
index c518f835181b2e6607c5ce5b788898e54c39ceae..df46adcd23dbf8d92395947b024e3d66e70ab1f4 100644 (file)
@@ -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)
index cdae240fe77e401c171b74b2deda49cbbfa3ee92..e1d354ddbbd6643f896603d997724f6988a25c39 100644 (file)
@@ -28,6 +28,7 @@
 #include <isc/xml.h>
 
 #include <dns/acl.h>
+#include <dns/dnstap.h>
 #include <dns/types.h>
 
 #include <named/types.h>
@@ -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;
 };
 
index 0ecd8feeb75b23953ffc300955dfcc40f08aeba3..15496d3ddbc076e2ef3ebbed4604af0b64fd5f10 100644 (file)
@@ -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);
index 34a4fbf772917a65845ea9d6f11bcededab4d230..7a2815813f05fce6e0046c5c9f02da3dc24f46d3 100644 (file)
@@ -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;
 }
index 027ee44b823f2059cd22e3b0bec27302560a1f7f..e0767bdc4e1f0d75f07247929a2937c60ae6e749 100644 (file)
@@ -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 (file)
index 0000000..5003c13
--- /dev/null
@@ -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 (file)
index 0000000..888b5a1
--- /dev/null
@@ -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 (file)
index 0000000..0bbfda9
--- /dev/null
@@ -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 (file)
index 0000000..7d4a11a
--- /dev/null
@@ -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 (file)
index 0000000..bae32ec
--- /dev/null
@@ -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 (file)
index 0000000..643b549
--- /dev/null
@@ -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 (file)
index 0000000..2f811e4
--- /dev/null
@@ -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
index f22b7f75d350e994a2e8403406cf864a24b8acfe..cff80a5bd7152fba8fcf28107ede3315e0a16cdb 100644 (file)
@@ -1,4 +1,5 @@
 arpaname
+dnstap-read
 genrandom
 isc-hmac-fixup
 mdig
index cf3c31644a7062cb58bfe51e59b0ccd210009c85..7f0b5c0bb183b6bac4a01dd4577fe27c8f152cfa 100644 (file)
@@ -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@
+               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
+               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 (file)
index 0000000..6319a26
--- /dev/null
@@ -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 <http://docbook.sf.net/>
+.\"      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 (file)
index 0000000..ff7ec7b
--- /dev/null
@@ -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 <config.h>
+
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/dnstap.h>
+#include <dns/fixedname.h>
+#include <dns/masterdump.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/result.h>
+
+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 (file)
index 0000000..58d0175
--- /dev/null
@@ -0,0 +1,125 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+              [<!ENTITY mdash "&#8212;">]>
+<!--
+ - 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.
+-->
+
+<refentry id="man.dnstap-read">
+  <refentryinfo>
+    <date>September 13, 2015</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle><application>dnstap-read</application></refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo>BIND9</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname><application>dnstap-read</application></refname>
+    <refpurpose>print dnstap data in human-readable form</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2015</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>dnstap-read</command>
+      <arg><option>-m</option></arg>
+      <arg><option>-p</option></arg>
+      <arg><option>-y</option></arg>
+      <arg choice="req"><replaceable class="parameter">file</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>
+      <command>dnstap-read</command>
+      reads <command>dnstap</command> data from a specified file
+      and prints it in a human-readable format.  By default,
+      <command>dnstap</command> data is printed in a short summary
+      format, but if the <option>-y</option> option is specified,
+      then a longer and more detailed YAML format is used instead.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>OPTIONS</title>
+
+    <variablelist>
+      <varlistentry>
+        <term>-m</term>
+        <listitem>
+          <para>
+            Trace memory allocations; used for debugging memory leaks.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-p</term>
+        <listitem>
+          <para>
+            After printing the <command>dnstap</command> data, print
+            the text form of the DNS message that was encapsulated in the
+            <command>dnstap</command> frame.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-y</term>
+        <listitem>
+          <para>
+            Print <command>dnstap</command> data in a detailed YAML
+            format. Implies <option>-p</option>.
+          </para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>named</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>nsupdate</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citetitle>BIND 9 Administrator Reference Manual</citetitle>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>AUTHOR</title>
+    <para><corpauthor>Internet Systems Consortium</corpauthor>
+    </para>
+  </refsect1>
+
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/bin/tools/dnstap-read.html b/bin/tools/dnstap-read.html
new file mode 100644 (file)
index 0000000..e6917e2
--- /dev/null
@@ -0,0 +1,78 @@
+<!--
+ - 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$ -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>dnstap-read</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.78.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry">
+<a name="man.dnstap-read"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><span class="application">dnstap-read</span> &#8212; print dnstap data in human-readable form</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">dnstap-read</code>  [<code class="option">-m</code>] [<code class="option">-p</code>] [<code class="option">-y</code>] {<em class="replaceable"><code>file</code></em>}</p></div>
+</div>
+<div class="refsect1">
+<a name="idp53505312"></a><h2>DESCRIPTION</h2>
+<p>
+      <span class="command"><strong>dnstap-read</strong></span>
+      reads <span class="command"><strong>dnstap</strong></span> data from a specified file
+      and prints it in a human-readable format.  By default,
+      <span class="command"><strong>dnstap</strong></span> data is printed in a short summary
+      format, but if the <code class="option">-y</code> option is specified,
+      then a longer and more detailed YAML format is used instead.
+    </p>
+</div>
+<div class="refsect1">
+<a name="idp53508000"></a><h2>OPTIONS</h2>
+<div class="variablelist"><dl class="variablelist">
+<dt><span class="term">-m</span></dt>
+<dd><p>
+            Trace memory allocations; used for debugging memory leaks.
+          </p></dd>
+<dt><span class="term">-p</span></dt>
+<dd><p>
+            After printing the <span class="command"><strong>dnstap</strong></span> data, print
+            the text form of the DNS message that was encapsulated in the
+            <span class="command"><strong>dnstap</strong></span> frame.
+          </p></dd>
+<dt><span class="term">-y</span></dt>
+<dd><p>
+            Print <span class="command"><strong>dnstap</strong></span> data in a detailed YAML
+            format. Implies <code class="option">-p</code>.
+          </p></dd>
+</dl></div>
+</div>
+<div class="refsect1">
+<a name="idp53515296"></a><h2>SEE ALSO</h2>
+<p>
+      <span class="citerefentry"><span class="refentrytitle">named</span>(8)</span>,
+      <span class="citerefentry"><span class="refentrytitle">nsupdate</span>(8)</span>,
+      <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+    </p>
+</div>
+<div class="refsect1">
+<a name="idp53518880"></a><h2>AUTHOR</h2>
+<p><span class="corpauthor">Internet Systems Consortium</span>
+    </p>
+</div>
+</div></body>
+</html>
index 4a092e2407b0359a8bf4cebabd238cf5e948ad2e..7fa8664ec65c613d227a6595ae791754928588e8 100644 (file)
@@ -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
 
index 4538d2c549ffa107ce655135795023756a8922a0..859bebb897f27af0b4b23c22effc139aacbf01e3 100755 (executable)
--- 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)"
 
index 2d4a038af24c4f6bed0c057eff1b756c50686349..946f0240da746bf1c07bd294dd33681702d21a2b 100644 (file)
@@ -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)"
 
index 636d2d2d3b82e53b6ad869a440ed76ba129580b4..8af74ca92f98e43c71dd0ccb0a94a6488b1d4c64 100644 (file)
@@ -4447,6 +4447,10 @@ badresp:1,adberr:0,findfail:0,valfail:0]
     <optional> hostname <replaceable>hostname_string</replaceable>; </optional>
     <optional> server-id <replaceable>server_id_string</replaceable>; </optional>
     <optional> directory <replaceable>path_name</replaceable>; </optional>
+    <optional> dnstap { <replaceable>message_type</replaceable>; ... }; </optional>
+    <optional> dnstap-output ( <literal>file</literal> | <literal>unix</literal> ) <replaceable>path_name</replaceable>; </optional>
+    <optional> dnstap-identity ( <replaceable>string</replaceable> | <literal>hostname</literal> | <literal>none</literal> ); </optional>
+    <optional> dnstap-version ( <replaceable>string</replaceable> | <literal>none</literal> ); </optional>
     <optional> geoip-directory <replaceable>path_name</replaceable>; </optional>
     <optional> key-directory <replaceable>path_name</replaceable>; </optional>
     <optional> managed-keys-directory <replaceable>path_name</replaceable>; </optional>
@@ -4849,6 +4853,126 @@ badresp:1,adberr:0,findfail:0,valfail:0]
            </listitem>
          </varlistentry>
 
+         <varlistentry>
+           <term><command>dnstap</command></term>
+           <listitem>
+             <para>
+               <command>dnstap</command> 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, <command>dnstap</command>
+               uses
+               <command>libfstrm</command> (a lightweight high-speed
+               framing library, see
+               <ulink url="https://github.com/farsightsec/fstrm"
+                 >https://github.com/farsightsec/fstrm</ulink>) to send
+               event payloads which are encoded using Protocol Buffers
+               (<command>libprotobuf-c</command>, a mechanism for
+               serializing structured data developed
+               by Google, Inc.; see 
+               <ulink url="https://developers.google.com/protocol-buffers/"
+                 >https://developers.google.com/protocol-buffers</ulink>).
+             </para>
+             <para>
+               To enable <command>dnstap</command> at compile time, 
+               the <command>fstrm</command> and <command>protobuf-c</command>
+               libraries must be available, and BIND must be configured with
+               <option>--enable-dnstap</option>.
+             </para>
+             <para>
+               The <command>dnstap</command> option is a bracketed list
+               of message types to be logged. These may be set differently
+               for each view. Supported types are <literal>client</literal>,
+               <literal>auth</literal>, <literal>resolver</literal>, and
+               <literal>forwarder</literal>.  Specifying type
+               <literal>all</literal> will cause all <command>dnstap</command>
+               messages to be logged, regardless of type.
+             </para>
+             <para>
+               Each type may take an additional argument to indicate whether
+               to log <literal>query</literal> messages or
+               <literal>response</literal> messages; if not specified,
+               both queries and responses are logged.
+             </para>
+             <para>
+               Example: To log all authoritative queries and responses,
+               recursive client responses, and upstream queries sent by
+               the resolver, use:
+<programlisting>dnstap {
+  auth;
+  client response;
+  resolver query;
+};
+</programlisting>
+             </para>
+             <para>
+               Logged <command>dnstap</command> messages can be parsed
+               using the <command>dnstap-read</command> utility (see
+               <xref linkend="man.dnstap-read"/> for details).
+             </para>
+             <para>
+               For more information on <command>dnstap</command>, see
+               <ulink url="http://dnstap.info"
+                 >http://dnstap.info</ulink>.
+             </para>
+           </listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><command>dnstap-output</command></term>
+           <listitem>
+             <para>
+               Configures the path to which the <command>dnstap</command>
+               frame stream will be sent if <command>dnstap</command>
+               is enabled at compile time and active.
+             </para>
+             <para>
+               The first argument is either <literal>file</literal> or
+               <literal>unix</literal>, 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, <command>dnstap</command> messages will
+               only be sent if another process such as
+               <command>fstrm_capture</command>
+               (provided with <command>libfstrm</command>) is listening on
+               the socket.)
+             </para>
+             <para>
+               <command>dnstap-output</command> can only be set globally
+               in <command>options</command>. Currently, it can only be
+               set once while <command>named</command> is running;
+               once set, it cannot be changed by
+               <command>rndc reload</command> or
+               <command>rndc reconfig</command>.
+             </para>
+           </listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><command>dnstap-identity</command></term>
+           <listitem>
+             <para>
+               Specifies an <command>identity</command> string to send in
+               <command>dnstap</command> messages. If set to
+               <literal>hostname</literal>, which is the default, the
+               server's hostname will be sent. If set to
+               <literal>none</literal>, no identity string will be sent.
+             </para>
+           </listitem>
+         </varlistentry>
+
+         <varlistentry>
+           <term><command>dnstap-version</command></term>
+           <listitem>
+             <para>
+               Specifies a <command>version</command> string to send in
+               <command>dnstap</command> messages. The default is the
+               version number of the BIND release. If set to
+               <literal>none</literal>, no version string will be sent.
+             </para>
+           </listitem>
+         </varlistentry>
+
          <varlistentry>
            <term><command>geoip-directory</command></term>
            <listitem>
@@ -18614,8 +18738,11 @@ allow-query { !{ !10/8; any; }; key example; };
       <xi:include href="../../bin/confgen/rndc-confgen.docbook"/>
       <xi:include href="../../bin/confgen/ddns-confgen.docbook"/>
       <xi:include href="../../bin/tools/arpaname.docbook"/>
+      <xi:include href="../../bin/tools/dnstap-read.docbook"/>
       <xi:include href="../../bin/tools/genrandom.docbook"/>
       <xi:include href="../../bin/tools/isc-hmac-fixup.docbook"/>
+      <xi:include href="../../bin/tools/named-journalprint.docbook"/>
+      <xi:include href="../../bin/tools/named-rrchecker.docbook"/>
       <xi:include href="../../bin/tools/nsec3hash.docbook"/>
     </reference>
 
diff --git a/doc/arm/man.dnstap-read.html b/doc/arm/man.dnstap-read.html
new file mode 100644 (file)
index 0000000..2e91797
--- /dev/null
@@ -0,0 +1,118 @@
+<!--
+ - Copyright (C) 2004-2015 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2000-2003 Internet Software Consortium.
+ - 
+ - 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$ -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>dnstap-read</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.78.1">
+<link rel="home" href="Bv9ARM.html" title="BIND 9 Administrator Reference Manual">
+<link rel="up" href="Bv9ARM.ch13.html" title="Manual pages">
+<link rel="prev" href="man.arpaname.html" title="arpaname">
+<link rel="next" href="man.genrandom.html" title="genrandom">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
+<div class="navheader">
+<table width="100%" summary="Navigation header">
+<tr><th colspan="3" align="center"><span class="application">dnstap-read</span></th></tr>
+<tr>
+<td width="20%" align="left">
+<a accesskey="p" href="man.arpaname.html">Prev</a> </td>
+<th width="60%" align="center">Manual pages</th>
+<td width="20%" align="right"> <a accesskey="n" href="man.genrandom.html">Next</a>
+</td>
+</tr>
+</table>
+<hr>
+</div>
+<div class="refentry">
+<a name="man.dnstap-read"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><span class="application">dnstap-read</span> &#8212; print dnstap data in human-readable form</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">dnstap-read</code>  [<code class="option">-m</code>] [<code class="option">-p</code>] [<code class="option">-y</code>] {<em class="replaceable"><code>file</code></em>}</p></div>
+</div>
+<div class="refsect1">
+<a name="idp63136656"></a><h2>DESCRIPTION</h2>
+<p>
+      <span class="command"><strong>dnstap-read</strong></span>
+      reads <span class="command"><strong>dnstap</strong></span> data from a specified file
+      and prints it in a human-readable format.  By default,
+      <span class="command"><strong>dnstap</strong></span> data is printed in a short summary
+      format, but if the <code class="option">-y</code> option is specified,
+      then a longer and more detailed YAML format is used instead.
+    </p>
+</div>
+<div class="refsect1">
+<a name="idp55841376"></a><h2>OPTIONS</h2>
+<div class="variablelist"><dl class="variablelist">
+<dt><span class="term">-m</span></dt>
+<dd><p>
+            Trace memory allocations; used for debugging memory leaks.
+          </p></dd>
+<dt><span class="term">-p</span></dt>
+<dd><p>
+            After printing the <span class="command"><strong>dnstap</strong></span> data, print
+            the text form of the DNS message that was encapsulated in the
+            <span class="command"><strong>dnstap</strong></span> frame.
+          </p></dd>
+<dt><span class="term">-y</span></dt>
+<dd><p>
+            Print <span class="command"><strong>dnstap</strong></span> data in a detailed YAML
+            format. Implies <code class="option">-p</code>.
+          </p></dd>
+</dl></div>
+</div>
+<div class="refsect1">
+<a name="idp61840128"></a><h2>SEE ALSO</h2>
+<p>
+      <span class="citerefentry"><span class="refentrytitle">named</span>(8)</span>,
+      <span class="citerefentry"><span class="refentrytitle">nsupdate</span>(8)</span>,
+      <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+    </p>
+</div>
+<div class="refsect1">
+<a name="idp64567456"></a><h2>AUTHOR</h2>
+<p><span class="corpauthor">Internet Systems Consortium</span>
+    </p>
+</div>
+</div>
+<div class="navfooter">
+<hr>
+<table width="100%" summary="Navigation footer">
+<tr>
+<td width="40%" align="left">
+<a accesskey="p" href="man.arpaname.html">Prev</a> </td>
+<td width="20%" align="center"><a accesskey="u" href="Bv9ARM.ch13.html">Up</a></td>
+<td width="40%" align="right"> <a accesskey="n" href="man.genrandom.html">Next</a>
+</td>
+</tr>
+<tr>
+<td width="40%" align="left" valign="top">
+<span class="application">arpaname</span> </td>
+<td width="20%" align="center"><a accesskey="h" href="Bv9ARM.html">Home</a></td>
+<td width="40%" align="right" valign="top"> <span class="application">genrandom</span>
+</td>
+</tr>
+</table>
+</div>
+<p style="text-align: center;">BIND 9.11.0pre-alpha</p>
+</body>
+</html>
index 4906f30a83f842d1793ccd322e774d220f5f0aec..392b0386f34f2216319505a10d6c6e02ddd9cf9f 100644 (file)
          of queries affected by these quotas.
        </para>
       </listitem>
+      <listitem>
+       <para>
+         Added support for <command>dnstap</command>, a fast,
+         flexible method for capturing and logging DNS traffic,
+         developed by Robert Edmonds at Farsight Security, Inc.,
+         whose assistance is gratefully acknowledged.
+       </para>
+       <para>
+         To enable <command>dnstap</command> at compile time, 
+         the <command>fstrm</command> and <command>protobuf-c</command>
+         libraries must be available, and BIND must be configured with
+         <option>--enable-dnstap</option>.
+       </para>
+       <para>
+         A new utility <command>dnstap-read</command> has been added
+         to allow <command>dnstap</command> data to be presented in
+         a human-readable format.
+       </para>
+       <para>
+         For more information on <command>dnstap</command>, see
+         <ulink url="http://dnstap.info">http://dnstap.info</ulink>.
+       </para>
+      </listitem>
       <listitem>
        <para>
          New statistics counters have been added to track traffic
index be539b60da86ca8100a09f93f8b6b52a31775b44..f219cfc2c1b634ce5a6364700672595fd13e9144 100644 (file)
@@ -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 (file)
index 0000000..793cbc0
--- /dev/null
@@ -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 <config.h>
+
+#ifndef HAVE_DNSTAP
+#error DNSTAP not configured.
+#endif /* HAVE_DNSTAP */
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/sockaddr.h>
+#include <isc/thread.h>
+#include <isc/time.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/dnstap.h>
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/message.h>
+#include <dns/rdataset.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+#include <dns/dnstap.pb-c.h>
+#include <protobuf-c/protobuf-c.h>
+
+#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 (file)
index 0000000..50000bd
--- /dev/null
@@ -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 (file)
index 0000000..71fd829
--- /dev/null
@@ -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/protobuf-c.h>
+
+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 (file)
index 0000000..1ed1bb0
--- /dev/null
@@ -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:
+//
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+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
index bc34c43c4e616029e379157616b89dbf335d0b4f..3fa4b4e80588b37fe1fbbc67bda1176e14e5ad7c 100644 (file)
@@ -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 (file)
index 0000000..3e8a606
--- /dev/null
@@ -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 <fstrm.h>
+#include <protobuf-c/protobuf-c.h>
+#include <dns/dnstap.pb-c.h>
+#endif /* HAVE_DNSTAP */
+
+#include <isc/refcount.h>
+#include <isc/region.h>
+#include <isc/sockaddr.h>
+#include <isc/time.h>
+#include <isc/types.h>
+
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/types.h>
+
+/*%
+ * 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 (file)
index 0000000..71fd829
--- /dev/null
@@ -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/protobuf-c.h>
+
+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 */
index 91ebbe0964bc44222c6c89cc60bd0967266912b2..613773c59dc77448233501edd58d9dea45d63399 100644 (file)
@@ -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
 
index 7318184a95d30bcaef255b9e3b69a9611c76757c..4c5344340dec5860b31eabc076aaae91e0eb0b54 100644 (file)
@@ -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,
index 3027f047aba4230e9a37d865867952a150580347..1a8966dae9654a2fb800a18b5c2850e912b09015 100644 (file)
@@ -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
 
 #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.
index dc02cd2b02e23331d2598e232f772094d1e46502..54fe547e15950af143d738f876f7885990372a3a 100644 (file)
@@ -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;
index 51b41ae41781bee5a0f8bde858bc165640b668f2..fe52e7b6e77094b258a57916b00394e56589c08d 100644 (file)
@@ -71,6 +71,7 @@
 
 #include <dns/acl.h>
 #include <dns/clientinfo.h>
+#include <dns/dnstap.h>
 #include <dns/fixedname.h>
 #include <dns/rrl.h>
 #include <dns/rdatastruct.h>
@@ -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')
index cff764bf54c702168b47a9dc933d20c4f769dcd2..623e027542bb3e75b862c0d11b9ad149e627e69e 100644 (file)
@@ -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 }
 };
 
index 31a7db59aad6da52e6710d20836416a648a0bed0..c0e2de96111efcfb750763525e64dbcbf3bf8312 100644 (file)
@@ -15,8 +15,6 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id$ */
-
 /*! \file */
 
 #include <config.h>
@@ -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,
index 896f31d9a5a016c76dc71a96d53bd25bbb521543..37477c532bf67fd3f47ffbbefdd982bc07efcda1 100644 (file)
@@ -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",
index dbbda3a02e24ed427973329232a96f15d881e01e..03c2649efcc92ad4cd5b4fb62d5b5f4f0c104a6f 100644 (file)
@@ -44,6 +44,7 @@
 #include <dns/cache.h>
 #include <dns/db.h>
 #include <dns/dispatch.h>
+#include <dns/dnstap.h>
 #include <dns/ds.h>
 #include <dns/edns.h>
 #include <dns/events.h>
@@ -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.
         */
index 981356b9a392767d6d698626687ca893769cd5be..204d2eeac9399536e11112bf515bbaf4a17e7b27 100644 (file)
@@ -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] = {
index 6cfe520e3f339c6d0b1ab19753e6315cd4a81f54..7b77d31c54b56aa6e9d57ef42ba1b5b26e74da19 100644 (file)
@@ -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 (file)
index 0000000..e85dea7
--- /dev/null
@@ -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 <config.h>
+
+#include <atf-c.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/stdio.h>
+#include <isc/print.h>
+#include <isc/types.h>
+
+#include <dns/dnstap.h>
+#include <dns/view.h>
+
+#include "dnstest.h"
+
+#ifdef HAVE_DNSTAP
+#include <dns/dnstap.pb-c.h>
+#include <protobuf-c/protobuf-c.h>
+
+#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());
+}
index 6aa6adc924ac8f554b3b3dcdac2763d2d6939fc2..7f22d033bfbe7f92f1532b7cfa2bed309c69653d 100644 (file)
 #include <isc/app.h>
 #include <isc/buffer.h>
 #include <isc/entropy.h>
+#include <isc/file.h>
 #include <isc/hash.h>
 #include <isc/mem.h>
 #include <isc/os.h>
 #include <isc/string.h>
 #include <isc/socket.h>
+#include <isc/stdio.h>
 #include <isc/task.h>
 #include <isc/timer.h>
 #include <isc/util.h>
@@ -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);
+}
+
index b8ccdd45cbb11b025519ac6c681276d4973edc1e..12aa34d7992ac8ac97907a46df61a04066a43ecd 100644 (file)
@@ -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 (file)
index 0000000..9ccb166
Binary files /dev/null and b/lib/dns/tests/testdata/dnstap/dnstap.saved differ
diff --git a/lib/dns/tests/testdata/dnstap/dnstap.text b/lib/dns/tests/testdata/dnstap/dnstap.text
new file mode 100644 (file)
index 0000000..e052ab4
--- /dev/null
@@ -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 (file)
index 0000000..a14f850
--- /dev/null
@@ -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 (file)
index 0000000..8ee705f
--- /dev/null
@@ -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 (file)
index 0000000..4d0ea81
--- /dev/null
@@ -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 (file)
index 0000000..6e3a3cf
--- /dev/null
@@ -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
index 6d47aebc71f7b0c0d80a2f6f80c39f729a3cfd1f..612849cddbd7a67a7b0de741a107c4d1d5bdbae3 100644 (file)
@@ -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);
index f8dc368a9f787ce3b71da52b0f92535fdec38a2c..c6ff940f19ea1357480fcc40effdb63aeb95dc8c 100644 (file)
@@ -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.
index 416f89c8f755a3f05d5b0190f82539cafd48a557..e8a0430cbd1760ea8bef8d040c13e907664c694c 100644 (file)
@@ -41,6 +41,7 @@
 #include <isc/string.h>
 #include <isc/task.h>
 #include <isc/thread.h>
+#include <isc/time.h>
 #include <isc/util.h>
 #include <isc/xml.h>
 
@@ -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.
index b08795612ba469d5f3bac5d1765df21481d77f52..0362ca3c67c44e2f9020d499ed09d707272d0782 100644 (file)
@@ -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
index cb8a8cbf5cde0f7614785f352b3c5a89dfab6461..f335ab7a0b71157b035820e02599d9729d8cb0c3 100644 (file)
@@ -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 {
+ *      <message type> [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 },
index 93393bbc4904ce60fc06995eae0fbbef2ba46885..25fb03ff4efca15943c3d7ea6dabbb12db5e2138 100644 (file)
@@ -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-* ?