Home | History | Annotate | Line # | Download | only in dns
      1  1.1  christos /*	$NetBSD: dnstap.c,v 1.1 2024/02/18 20:57:31 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /*
      4  1.1  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  1.1  christos  *
      6  1.1  christos  * SPDX-License-Identifier: MPL-2.0
      7  1.1  christos  *
      8  1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      9  1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  1.1  christos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  1.1  christos  *
     12  1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     13  1.1  christos  * information regarding copyright ownership.
     14  1.1  christos  */
     15  1.1  christos 
     16  1.1  christos /*
     17  1.1  christos  * Copyright (c) 2013-2014, Farsight Security, Inc.
     18  1.1  christos  * All rights reserved.
     19  1.1  christos  *
     20  1.1  christos  * Redistribution and use in source and binary forms, with or without
     21  1.1  christos  * modification, are permitted provided that the following conditions
     22  1.1  christos  * are met:
     23  1.1  christos  *
     24  1.1  christos  * 1. Redistributions of source code must retain the above copyright
     25  1.1  christos  * notice, this list of conditions and the following disclaimer.
     26  1.1  christos  *
     27  1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     28  1.1  christos  * notice, this list of conditions and the following disclaimer in the
     29  1.1  christos  * documentation and/or other materials provided with the distribution.
     30  1.1  christos  *
     31  1.1  christos  * 3. Neither the name of the copyright holder nor the names of its
     32  1.1  christos  * contributors may be used to endorse or promote products derived from
     33  1.1  christos  * this software without specific prior written permission.
     34  1.1  christos  *
     35  1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     36  1.1  christos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     37  1.1  christos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     38  1.1  christos  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
     39  1.1  christos  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     40  1.1  christos  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     41  1.1  christos  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     42  1.1  christos  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     43  1.1  christos  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     44  1.1  christos  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     45  1.1  christos  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     46  1.1  christos  */
     47  1.1  christos 
     48  1.1  christos /*! \file */
     49  1.1  christos 
     50  1.1  christos #ifndef HAVE_DNSTAP
     51  1.1  christos #error DNSTAP not configured.
     52  1.1  christos #endif /* HAVE_DNSTAP */
     53  1.1  christos 
     54  1.1  christos #include <fstrm.h>
     55  1.1  christos #include <inttypes.h>
     56  1.1  christos #include <stdbool.h>
     57  1.1  christos #include <stdlib.h>
     58  1.1  christos 
     59  1.1  christos #include <isc/buffer.h>
     60  1.1  christos #include <isc/file.h>
     61  1.1  christos #include <isc/log.h>
     62  1.1  christos #include <isc/mem.h>
     63  1.1  christos #include <isc/mutex.h>
     64  1.1  christos #include <isc/once.h>
     65  1.1  christos #include <isc/print.h>
     66  1.1  christos #include <isc/sockaddr.h>
     67  1.1  christos #include <isc/task.h>
     68  1.1  christos #include <isc/thread.h>
     69  1.1  christos #include <isc/time.h>
     70  1.1  christos #include <isc/types.h>
     71  1.1  christos #include <isc/util.h>
     72  1.1  christos 
     73  1.1  christos #include <dns/dnstap.h>
     74  1.1  christos #include <dns/events.h>
     75  1.1  christos #include <dns/log.h>
     76  1.1  christos #include <dns/message.h>
     77  1.1  christos #include <dns/name.h>
     78  1.1  christos #include <dns/rdataset.h>
     79  1.1  christos #include <dns/result.h>
     80  1.1  christos #include <dns/stats.h>
     81  1.1  christos #include <dns/types.h>
     82  1.1  christos #include <dns/view.h>
     83  1.1  christos 
     84  1.1  christos #include "dnstap.pb-c.h"
     85  1.1  christos 
     86  1.1  christos #define DTENV_MAGIC	 ISC_MAGIC('D', 't', 'n', 'v')
     87  1.1  christos #define VALID_DTENV(env) ISC_MAGIC_VALID(env, DTENV_MAGIC)
     88  1.1  christos 
     89  1.1  christos #define DNSTAP_CONTENT_TYPE	"protobuf:dnstap.Dnstap"
     90  1.1  christos #define DNSTAP_INITIAL_BUF_SIZE 256
     91  1.1  christos 
     92  1.1  christos struct dns_dtmsg {
     93  1.1  christos 	void *buf;
     94  1.1  christos 	size_t len;
     95  1.1  christos 	Dnstap__Dnstap d;
     96  1.1  christos 	Dnstap__Message m;
     97  1.1  christos };
     98  1.1  christos 
     99  1.1  christos struct dns_dthandle {
    100  1.1  christos 	dns_dtmode_t mode;
    101  1.1  christos 	struct fstrm_reader *reader;
    102  1.1  christos 	isc_mem_t *mctx;
    103  1.1  christos };
    104  1.1  christos 
    105  1.1  christos struct dns_dtenv {
    106  1.1  christos 	unsigned int magic;
    107  1.1  christos 	isc_refcount_t refcount;
    108  1.1  christos 
    109  1.1  christos 	isc_mem_t *mctx;
    110  1.1  christos 
    111  1.1  christos 	struct fstrm_iothr *iothr;
    112  1.1  christos 	struct fstrm_iothr_options *fopt;
    113  1.1  christos 
    114  1.1  christos 	isc_task_t *reopen_task;
    115  1.1  christos 	isc_mutex_t reopen_lock; /* locks 'reopen_queued'
    116  1.1  christos 				  * */
    117  1.1  christos 	bool reopen_queued;
    118  1.1  christos 
    119  1.1  christos 	isc_region_t identity;
    120  1.1  christos 	isc_region_t version;
    121  1.1  christos 	char *path;
    122  1.1  christos 	dns_dtmode_t mode;
    123  1.1  christos 	isc_offset_t max_size;
    124  1.1  christos 	int rolls;
    125  1.1  christos 	isc_log_rollsuffix_t suffix;
    126  1.1  christos 	isc_stats_t *stats;
    127  1.1  christos };
    128  1.1  christos 
    129  1.1  christos #define CHECK(x)                             \
    130  1.1  christos 	do {                                 \
    131  1.1  christos 		result = (x);                \
    132  1.1  christos 		if (result != ISC_R_SUCCESS) \
    133  1.1  christos 			goto cleanup;        \
    134  1.1  christos 	} while (0)
    135  1.1  christos 
    136  1.1  christos typedef struct ioq {
    137  1.1  christos 	unsigned int generation;
    138  1.1  christos 	struct fstrm_iothr_queue *ioq;
    139  1.1  christos } dt__ioq_t;
    140  1.1  christos 
    141  1.1  christos ISC_THREAD_LOCAL dt__ioq_t dt_ioq = { 0 };
    142  1.1  christos 
    143  1.1  christos static atomic_uint_fast32_t global_generation;
    144  1.1  christos 
    145  1.1  christos isc_result_t
    146  1.1  christos dns_dt_create(isc_mem_t *mctx, dns_dtmode_t mode, const char *path,
    147  1.1  christos 	      struct fstrm_iothr_options **foptp, isc_task_t *reopen_task,
    148  1.1  christos 	      dns_dtenv_t **envp) {
    149  1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
    150  1.1  christos 	fstrm_res res;
    151  1.1  christos 	struct fstrm_unix_writer_options *fuwopt = NULL;
    152  1.1  christos 	struct fstrm_file_options *ffwopt = NULL;
    153  1.1  christos 	struct fstrm_writer_options *fwopt = NULL;
    154  1.1  christos 	struct fstrm_writer *fw = NULL;
    155  1.1  christos 	dns_dtenv_t *env = NULL;
    156  1.1  christos 
    157  1.1  christos 	REQUIRE(path != NULL);
    158  1.1  christos 	REQUIRE(envp != NULL && *envp == NULL);
    159  1.1  christos 	REQUIRE(foptp != NULL && *foptp != NULL);
    160  1.1  christos 
    161  1.1  christos 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, DNS_LOGMODULE_DNSTAP,
    162  1.1  christos 		      ISC_LOG_INFO, "opening dnstap destination '%s'", path);
    163  1.1  christos 
    164  1.1  christos 	atomic_fetch_add_release(&global_generation, 1);
    165  1.1  christos 
    166  1.1  christos 	env = isc_mem_get(mctx, sizeof(dns_dtenv_t));
    167  1.1  christos 
    168  1.1  christos 	memset(env, 0, sizeof(dns_dtenv_t));
    169  1.1  christos 	isc_mem_attach(mctx, &env->mctx);
    170  1.1  christos 	env->reopen_task = reopen_task;
    171  1.1  christos 	isc_mutex_init(&env->reopen_lock);
    172  1.1  christos 	env->reopen_queued = false;
    173  1.1  christos 	env->path = isc_mem_strdup(env->mctx, path);
    174  1.1  christos 	isc_refcount_init(&env->refcount, 1);
    175  1.1  christos 	CHECK(isc_stats_create(env->mctx, &env->stats, dns_dnstapcounter_max));
    176  1.1  christos 
    177  1.1  christos 	fwopt = fstrm_writer_options_init();
    178  1.1  christos 	if (fwopt == NULL) {
    179  1.1  christos 		CHECK(ISC_R_NOMEMORY);
    180  1.1  christos 	}
    181  1.1  christos 
    182  1.1  christos 	res = fstrm_writer_options_add_content_type(
    183  1.1  christos 		fwopt, DNSTAP_CONTENT_TYPE, sizeof(DNSTAP_CONTENT_TYPE) - 1);
    184  1.1  christos 	if (res != fstrm_res_success) {
    185  1.1  christos 		CHECK(ISC_R_FAILURE);
    186  1.1  christos 	}
    187  1.1  christos 
    188  1.1  christos 	if (mode == dns_dtmode_file) {
    189  1.1  christos 		ffwopt = fstrm_file_options_init();
    190  1.1  christos 		if (ffwopt != NULL) {
    191  1.1  christos 			fstrm_file_options_set_file_path(ffwopt, env->path);
    192  1.1  christos 			fw = fstrm_file_writer_init(ffwopt, fwopt);
    193  1.1  christos 		}
    194  1.1  christos 	} else if (mode == dns_dtmode_unix) {
    195  1.1  christos 		fuwopt = fstrm_unix_writer_options_init();
    196  1.1  christos 		if (fuwopt != NULL) {
    197  1.1  christos 			fstrm_unix_writer_options_set_socket_path(fuwopt,
    198  1.1  christos 								  env->path);
    199  1.1  christos 			fw = fstrm_unix_writer_init(fuwopt, fwopt);
    200  1.1  christos 		}
    201  1.1  christos 	} else {
    202  1.1  christos 		CHECK(ISC_R_FAILURE);
    203  1.1  christos 	}
    204  1.1  christos 
    205  1.1  christos 	if (fw == NULL) {
    206  1.1  christos 		CHECK(ISC_R_FAILURE);
    207  1.1  christos 	}
    208  1.1  christos 
    209  1.1  christos 	env->iothr = fstrm_iothr_init(*foptp, &fw);
    210  1.1  christos 	if (env->iothr == NULL) {
    211  1.1  christos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
    212  1.1  christos 			      DNS_LOGMODULE_DNSTAP, ISC_LOG_WARNING,
    213  1.1  christos 			      "unable to initialize dnstap I/O thread");
    214  1.1  christos 		fstrm_writer_destroy(&fw);
    215  1.1  christos 		CHECK(ISC_R_FAILURE);
    216  1.1  christos 	}
    217  1.1  christos 	env->mode = mode;
    218  1.1  christos 	env->max_size = 0;
    219  1.1  christos 	env->rolls = ISC_LOG_ROLLINFINITE;
    220  1.1  christos 	env->fopt = *foptp;
    221  1.1  christos 	*foptp = NULL;
    222  1.1  christos 
    223  1.1  christos 	env->magic = DTENV_MAGIC;
    224  1.1  christos 	*envp = env;
    225  1.1  christos 
    226  1.1  christos cleanup:
    227  1.1  christos 	if (ffwopt != NULL) {
    228  1.1  christos 		fstrm_file_options_destroy(&ffwopt);
    229  1.1  christos 	}
    230  1.1  christos 
    231  1.1  christos 	if (fuwopt != NULL) {
    232  1.1  christos 		fstrm_unix_writer_options_destroy(&fuwopt);
    233  1.1  christos 	}
    234  1.1  christos 
    235  1.1  christos 	if (fwopt != NULL) {
    236  1.1  christos 		fstrm_writer_options_destroy(&fwopt);
    237  1.1  christos 	}
    238  1.1  christos 
    239  1.1  christos 	if (result != ISC_R_SUCCESS) {
    240  1.1  christos 		isc_mutex_destroy(&env->reopen_lock);
    241  1.1  christos 		isc_mem_free(env->mctx, env->path);
    242  1.1  christos 		if (env->stats != NULL) {
    243  1.1  christos 			isc_stats_detach(&env->stats);
    244  1.1  christos 		}
    245  1.1  christos 		isc_mem_putanddetach(&env->mctx, env, sizeof(dns_dtenv_t));
    246  1.1  christos 	}
    247  1.1  christos 
    248  1.1  christos 	return (result);
    249  1.1  christos }
    250  1.1  christos 
    251  1.1  christos isc_result_t
    252  1.1  christos dns_dt_setupfile(dns_dtenv_t *env, uint64_t max_size, int rolls,
    253  1.1  christos 		 isc_log_rollsuffix_t suffix) {
    254  1.1  christos 	REQUIRE(VALID_DTENV(env));
    255  1.1  christos 
    256  1.1  christos 	/*
    257  1.1  christos 	 * If we're using unix domain socket mode, then any
    258  1.1  christos 	 * change from the default values is invalid.
    259  1.1  christos 	 */
    260  1.1  christos 	if (env->mode == dns_dtmode_unix) {
    261  1.1  christos 		if (max_size == 0 && rolls == ISC_LOG_ROLLINFINITE &&
    262  1.1  christos 		    suffix == isc_log_rollsuffix_increment)
    263  1.1  christos 		{
    264  1.1  christos 			return (ISC_R_SUCCESS);
    265  1.1  christos 		} else {
    266  1.1  christos 			return (ISC_R_INVALIDFILE);
    267  1.1  christos 		}
    268  1.1  christos 	}
    269  1.1  christos 
    270  1.1  christos 	env->max_size = max_size;
    271  1.1  christos 	env->rolls = rolls;
    272  1.1  christos 	env->suffix = suffix;
    273  1.1  christos 
    274  1.1  christos 	return (ISC_R_SUCCESS);
    275  1.1  christos }
    276  1.1  christos 
    277  1.1  christos isc_result_t
    278  1.1  christos dns_dt_reopen(dns_dtenv_t *env, int roll) {
    279  1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
    280  1.1  christos 	fstrm_res res;
    281  1.1  christos 	isc_logfile_t file;
    282  1.1  christos 	struct fstrm_unix_writer_options *fuwopt = NULL;
    283  1.1  christos 	struct fstrm_file_options *ffwopt = NULL;
    284  1.1  christos 	struct fstrm_writer_options *fwopt = NULL;
    285  1.1  christos 	struct fstrm_writer *fw = NULL;
    286  1.1  christos 
    287  1.1  christos 	REQUIRE(VALID_DTENV(env));
    288  1.1  christos 
    289  1.1  christos 	/*
    290  1.1  christos 	 * Run in task-exclusive mode.
    291  1.1  christos 	 */
    292  1.1  christos 	result = isc_task_beginexclusive(env->reopen_task);
    293  1.1  christos 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    294  1.1  christos 
    295  1.1  christos 	/*
    296  1.1  christos 	 * Check that we can create a new fw object.
    297  1.1  christos 	 */
    298  1.1  christos 	fwopt = fstrm_writer_options_init();
    299  1.1  christos 	if (fwopt == NULL) {
    300  1.1  christos 		CHECK(ISC_R_NOMEMORY);
    301  1.1  christos 	}
    302  1.1  christos 
    303  1.1  christos 	res = fstrm_writer_options_add_content_type(
    304  1.1  christos 		fwopt, DNSTAP_CONTENT_TYPE, sizeof(DNSTAP_CONTENT_TYPE) - 1);
    305  1.1  christos 	if (res != fstrm_res_success) {
    306  1.1  christos 		CHECK(ISC_R_FAILURE);
    307  1.1  christos 	}
    308  1.1  christos 
    309  1.1  christos 	if (env->mode == dns_dtmode_file) {
    310  1.1  christos 		ffwopt = fstrm_file_options_init();
    311  1.1  christos 		if (ffwopt != NULL) {
    312  1.1  christos 			fstrm_file_options_set_file_path(ffwopt, env->path);
    313  1.1  christos 			fw = fstrm_file_writer_init(ffwopt, fwopt);
    314  1.1  christos 		}
    315  1.1  christos 	} else if (env->mode == dns_dtmode_unix) {
    316  1.1  christos 		fuwopt = fstrm_unix_writer_options_init();
    317  1.1  christos 		if (fuwopt != NULL) {
    318  1.1  christos 			fstrm_unix_writer_options_set_socket_path(fuwopt,
    319  1.1  christos 								  env->path);
    320  1.1  christos 			fw = fstrm_unix_writer_init(fuwopt, fwopt);
    321  1.1  christos 		}
    322  1.1  christos 	} else {
    323  1.1  christos 		CHECK(ISC_R_NOTIMPLEMENTED);
    324  1.1  christos 	}
    325  1.1  christos 
    326  1.1  christos 	if (fw == NULL) {
    327  1.1  christos 		CHECK(ISC_R_FAILURE);
    328  1.1  christos 	}
    329  1.1  christos 
    330  1.1  christos 	/*
    331  1.1  christos 	 * We are committed here.
    332  1.1  christos 	 */
    333  1.1  christos 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, DNS_LOGMODULE_DNSTAP,
    334  1.1  christos 		      ISC_LOG_INFO, "%s dnstap destination '%s'",
    335  1.1  christos 		      (roll < 0) ? "reopening" : "rolling", env->path);
    336  1.1  christos 
    337  1.1  christos 	atomic_fetch_add_release(&global_generation, 1);
    338  1.1  christos 
    339  1.1  christos 	if (env->iothr != NULL) {
    340  1.1  christos 		fstrm_iothr_destroy(&env->iothr);
    341  1.1  christos 	}
    342  1.1  christos 
    343  1.1  christos 	if (roll == 0) {
    344  1.1  christos 		roll = env->rolls;
    345  1.1  christos 	}
    346  1.1  christos 
    347  1.1  christos 	if (env->mode == dns_dtmode_file && roll != 0) {
    348  1.1  christos 		/*
    349  1.1  christos 		 * Create a temporary isc_logfile_t structure so we can
    350  1.1  christos 		 * take advantage of the logfile rolling facility.
    351  1.1  christos 		 */
    352  1.1  christos 		char *filename = isc_mem_strdup(env->mctx, env->path);
    353  1.1  christos 		file.name = filename;
    354  1.1  christos 		file.stream = NULL;
    355  1.1  christos 		file.versions = roll;
    356  1.1  christos 		file.maximum_size = 0;
    357  1.1  christos 		file.maximum_reached = false;
    358  1.1  christos 		file.suffix = env->suffix;
    359  1.1  christos 		result = isc_logfile_roll(&file);
    360  1.1  christos 		isc_mem_free(env->mctx, filename);
    361  1.1  christos 		CHECK(result);
    362  1.1  christos 	}
    363  1.1  christos 
    364  1.1  christos 	env->iothr = fstrm_iothr_init(env->fopt, &fw);
    365  1.1  christos 	if (env->iothr == NULL) {
    366  1.1  christos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
    367  1.1  christos 			      DNS_LOGMODULE_DNSTAP, ISC_LOG_WARNING,
    368  1.1  christos 			      "unable to initialize dnstap I/O thread");
    369  1.1  christos 		CHECK(ISC_R_FAILURE);
    370  1.1  christos 	}
    371  1.1  christos 
    372  1.1  christos cleanup:
    373  1.1  christos 	if (fw != NULL) {
    374  1.1  christos 		fstrm_writer_destroy(&fw);
    375  1.1  christos 	}
    376  1.1  christos 
    377  1.1  christos 	if (fuwopt != NULL) {
    378  1.1  christos 		fstrm_unix_writer_options_destroy(&fuwopt);
    379  1.1  christos 	}
    380  1.1  christos 
    381  1.1  christos 	if (ffwopt != NULL) {
    382  1.1  christos 		fstrm_file_options_destroy(&ffwopt);
    383  1.1  christos 	}
    384  1.1  christos 
    385  1.1  christos 	if (fwopt != NULL) {
    386  1.1  christos 		fstrm_writer_options_destroy(&fwopt);
    387  1.1  christos 	}
    388  1.1  christos 
    389  1.1  christos 	isc_task_endexclusive(env->reopen_task);
    390  1.1  christos 
    391  1.1  christos 	return (result);
    392  1.1  christos }
    393  1.1  christos 
    394  1.1  christos static isc_result_t
    395  1.1  christos toregion(dns_dtenv_t *env, isc_region_t *r, const char *str) {
    396  1.1  christos 	unsigned char *p = NULL;
    397  1.1  christos 
    398  1.1  christos 	REQUIRE(r != NULL);
    399  1.1  christos 
    400  1.1  christos 	if (str != NULL) {
    401  1.1  christos 		p = (unsigned char *)isc_mem_strdup(env->mctx, str);
    402  1.1  christos 	}
    403  1.1  christos 
    404  1.1  christos 	if (r->base != NULL) {
    405  1.1  christos 		isc_mem_free(env->mctx, r->base);
    406  1.1  christos 		r->length = 0;
    407  1.1  christos 	}
    408  1.1  christos 
    409  1.1  christos 	if (p != NULL) {
    410  1.1  christos 		r->base = p;
    411  1.1  christos 		r->length = strlen((char *)p);
    412  1.1  christos 	}
    413  1.1  christos 
    414  1.1  christos 	return (ISC_R_SUCCESS);
    415  1.1  christos }
    416  1.1  christos 
    417  1.1  christos isc_result_t
    418  1.1  christos dns_dt_setidentity(dns_dtenv_t *env, const char *identity) {
    419  1.1  christos 	REQUIRE(VALID_DTENV(env));
    420  1.1  christos 
    421  1.1  christos 	return (toregion(env, &env->identity, identity));
    422  1.1  christos }
    423  1.1  christos 
    424  1.1  christos isc_result_t
    425  1.1  christos dns_dt_setversion(dns_dtenv_t *env, const char *version) {
    426  1.1  christos 	REQUIRE(VALID_DTENV(env));
    427  1.1  christos 
    428  1.1  christos 	return (toregion(env, &env->version, version));
    429  1.1  christos }
    430  1.1  christos 
    431  1.1  christos static void
    432  1.1  christos set_dt_ioq(unsigned int generation, struct fstrm_iothr_queue *ioq) {
    433  1.1  christos 	dt_ioq.generation = generation;
    434  1.1  christos 	dt_ioq.ioq = ioq;
    435  1.1  christos }
    436  1.1  christos 
    437  1.1  christos static struct fstrm_iothr_queue *
    438  1.1  christos dt_queue(dns_dtenv_t *env) {
    439  1.1  christos 	REQUIRE(VALID_DTENV(env));
    440  1.1  christos 
    441  1.1  christos 	unsigned int generation;
    442  1.1  christos 
    443  1.1  christos 	if (env->iothr == NULL) {
    444  1.1  christos 		return (NULL);
    445  1.1  christos 	}
    446  1.1  christos 
    447  1.1  christos 	generation = atomic_load_acquire(&global_generation);
    448  1.1  christos 	if (dt_ioq.ioq != NULL && dt_ioq.generation != generation) {
    449  1.1  christos 		set_dt_ioq(0, NULL);
    450  1.1  christos 	}
    451  1.1  christos 	if (dt_ioq.ioq == NULL) {
    452  1.1  christos 		struct fstrm_iothr_queue *ioq =
    453  1.1  christos 			fstrm_iothr_get_input_queue(env->iothr);
    454  1.1  christos 		set_dt_ioq(generation, ioq);
    455  1.1  christos 	}
    456  1.1  christos 
    457  1.1  christos 	return (dt_ioq.ioq);
    458  1.1  christos }
    459  1.1  christos 
    460  1.1  christos void
    461  1.1  christos dns_dt_attach(dns_dtenv_t *source, dns_dtenv_t **destp) {
    462  1.1  christos 	REQUIRE(VALID_DTENV(source));
    463  1.1  christos 	REQUIRE(destp != NULL && *destp == NULL);
    464  1.1  christos 
    465  1.1  christos 	isc_refcount_increment(&source->refcount);
    466  1.1  christos 	*destp = source;
    467  1.1  christos }
    468  1.1  christos 
    469  1.1  christos isc_result_t
    470  1.1  christos dns_dt_getstats(dns_dtenv_t *env, isc_stats_t **statsp) {
    471  1.1  christos 	REQUIRE(VALID_DTENV(env));
    472  1.1  christos 	REQUIRE(statsp != NULL && *statsp == NULL);
    473  1.1  christos 
    474  1.1  christos 	if (env->stats == NULL) {
    475  1.1  christos 		return (ISC_R_NOTFOUND);
    476  1.1  christos 	}
    477  1.1  christos 	isc_stats_attach(env->stats, statsp);
    478  1.1  christos 	return (ISC_R_SUCCESS);
    479  1.1  christos }
    480  1.1  christos 
    481  1.1  christos static void
    482  1.1  christos destroy(dns_dtenv_t *env) {
    483  1.1  christos 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, DNS_LOGMODULE_DNSTAP,
    484  1.1  christos 		      ISC_LOG_INFO, "closing dnstap");
    485  1.1  christos 	env->magic = 0;
    486  1.1  christos 
    487  1.1  christos 	atomic_fetch_add(&global_generation, 1);
    488  1.1  christos 
    489  1.1  christos 	if (env->iothr != NULL) {
    490  1.1  christos 		fstrm_iothr_destroy(&env->iothr);
    491  1.1  christos 	}
    492  1.1  christos 	if (env->fopt != NULL) {
    493  1.1  christos 		fstrm_iothr_options_destroy(&env->fopt);
    494  1.1  christos 	}
    495  1.1  christos 
    496  1.1  christos 	if (env->identity.base != NULL) {
    497  1.1  christos 		isc_mem_free(env->mctx, env->identity.base);
    498  1.1  christos 		env->identity.length = 0;
    499  1.1  christos 	}
    500  1.1  christos 	if (env->version.base != NULL) {
    501  1.1  christos 		isc_mem_free(env->mctx, env->version.base);
    502  1.1  christos 		env->version.length = 0;
    503  1.1  christos 	}
    504  1.1  christos 	if (env->path != NULL) {
    505  1.1  christos 		isc_mem_free(env->mctx, env->path);
    506  1.1  christos 	}
    507  1.1  christos 	if (env->stats != NULL) {
    508  1.1  christos 		isc_stats_detach(&env->stats);
    509  1.1  christos 	}
    510  1.1  christos 
    511  1.1  christos 	isc_mem_putanddetach(&env->mctx, env, sizeof(*env));
    512  1.1  christos }
    513  1.1  christos 
    514  1.1  christos void
    515  1.1  christos dns_dt_detach(dns_dtenv_t **envp) {
    516  1.1  christos 	REQUIRE(envp != NULL && VALID_DTENV(*envp));
    517  1.1  christos 	dns_dtenv_t *env = *envp;
    518  1.1  christos 	*envp = NULL;
    519  1.1  christos 
    520  1.1  christos 	if (isc_refcount_decrement(&env->refcount) == 1) {
    521  1.1  christos 		isc_refcount_destroy(&env->refcount);
    522  1.1  christos 		destroy(env);
    523  1.1  christos 	}
    524  1.1  christos }
    525  1.1  christos 
    526  1.1  christos static isc_result_t
    527  1.1  christos pack_dt(const Dnstap__Dnstap *d, void **buf, size_t *sz) {
    528  1.1  christos 	ProtobufCBufferSimple sbuf;
    529  1.1  christos 
    530  1.1  christos 	REQUIRE(d != NULL);
    531  1.1  christos 	REQUIRE(sz != NULL);
    532  1.1  christos 
    533  1.1  christos 	memset(&sbuf, 0, sizeof(sbuf));
    534  1.1  christos 	sbuf.base.append = protobuf_c_buffer_simple_append;
    535  1.1  christos 	sbuf.len = 0;
    536  1.1  christos 	sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE;
    537  1.1  christos 
    538  1.1  christos 	/* Need to use malloc() here because protobuf uses free() */
    539  1.1  christos 	sbuf.data = malloc(sbuf.alloced);
    540  1.1  christos 	if (sbuf.data == NULL) {
    541  1.1  christos 		return (ISC_R_NOMEMORY);
    542  1.1  christos 	}
    543  1.1  christos 	sbuf.must_free_data = 1;
    544  1.1  christos 
    545  1.1  christos 	*sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *)&sbuf);
    546  1.1  christos 	if (sbuf.data == NULL) {
    547  1.1  christos 		return (ISC_R_FAILURE);
    548  1.1  christos 	}
    549  1.1  christos 	*buf = sbuf.data;
    550  1.1  christos 
    551  1.1  christos 	return (ISC_R_SUCCESS);
    552  1.1  christos }
    553  1.1  christos 
    554  1.1  christos static void
    555  1.1  christos send_dt(dns_dtenv_t *env, void *buf, size_t len) {
    556  1.1  christos 	struct fstrm_iothr_queue *ioq;
    557  1.1  christos 	fstrm_res res;
    558  1.1  christos 
    559  1.1  christos 	REQUIRE(env != NULL);
    560  1.1  christos 
    561  1.1  christos 	if (buf == NULL) {
    562  1.1  christos 		return;
    563  1.1  christos 	}
    564  1.1  christos 
    565  1.1  christos 	ioq = dt_queue(env);
    566  1.1  christos 	if (ioq == NULL) {
    567  1.1  christos 		free(buf);
    568  1.1  christos 		return;
    569  1.1  christos 	}
    570  1.1  christos 
    571  1.1  christos 	res = fstrm_iothr_submit(env->iothr, ioq, buf, len, fstrm_free_wrapper,
    572  1.1  christos 				 NULL);
    573  1.1  christos 	if (res != fstrm_res_success) {
    574  1.1  christos 		if (env->stats != NULL) {
    575  1.1  christos 			isc_stats_increment(env->stats, dns_dnstapcounter_drop);
    576  1.1  christos 		}
    577  1.1  christos 		free(buf);
    578  1.1  christos 	} else {
    579  1.1  christos 		if (env->stats != NULL) {
    580  1.1  christos 			isc_stats_increment(env->stats,
    581  1.1  christos 					    dns_dnstapcounter_success);
    582  1.1  christos 		}
    583  1.1  christos 	}
    584  1.1  christos }
    585  1.1  christos 
    586  1.1  christos static void
    587  1.1  christos init_msg(dns_dtenv_t *env, dns_dtmsg_t *dm, Dnstap__Message__Type mtype) {
    588  1.1  christos 	memset(dm, 0, sizeof(*dm));
    589  1.1  christos 	dm->d.base.descriptor = &dnstap__dnstap__descriptor;
    590  1.1  christos 	dm->m.base.descriptor = &dnstap__message__descriptor;
    591  1.1  christos 	dm->d.type = DNSTAP__DNSTAP__TYPE__MESSAGE;
    592  1.1  christos 	dm->d.message = &dm->m;
    593  1.1  christos 	dm->m.type = mtype;
    594  1.1  christos 
    595  1.1  christos 	if (env->identity.length != 0) {
    596  1.1  christos 		dm->d.identity.data = env->identity.base;
    597  1.1  christos 		dm->d.identity.len = env->identity.length;
    598  1.1  christos 		dm->d.has_identity = true;
    599  1.1  christos 	}
    600  1.1  christos 
    601  1.1  christos 	if (env->version.length != 0) {
    602  1.1  christos 		dm->d.version.data = env->version.base;
    603  1.1  christos 		dm->d.version.len = env->version.length;
    604  1.1  christos 		dm->d.has_version = true;
    605  1.1  christos 	}
    606  1.1  christos }
    607  1.1  christos 
    608  1.1  christos static Dnstap__Message__Type
    609  1.1  christos dnstap_type(dns_dtmsgtype_t msgtype) {
    610  1.1  christos 	switch (msgtype) {
    611  1.1  christos 	case DNS_DTTYPE_SQ:
    612  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__STUB_QUERY);
    613  1.1  christos 	case DNS_DTTYPE_SR:
    614  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__STUB_RESPONSE);
    615  1.1  christos 	case DNS_DTTYPE_CQ:
    616  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__CLIENT_QUERY);
    617  1.1  christos 	case DNS_DTTYPE_CR:
    618  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE);
    619  1.1  christos 	case DNS_DTTYPE_AQ:
    620  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__AUTH_QUERY);
    621  1.1  christos 	case DNS_DTTYPE_AR:
    622  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE);
    623  1.1  christos 	case DNS_DTTYPE_RQ:
    624  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY);
    625  1.1  christos 	case DNS_DTTYPE_RR:
    626  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE);
    627  1.1  christos 	case DNS_DTTYPE_FQ:
    628  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY);
    629  1.1  christos 	case DNS_DTTYPE_FR:
    630  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE);
    631  1.1  christos 	case DNS_DTTYPE_TQ:
    632  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__TOOL_QUERY);
    633  1.1  christos 	case DNS_DTTYPE_TR:
    634  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE);
    635  1.1  christos 	case DNS_DTTYPE_UQ:
    636  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__UPDATE_QUERY);
    637  1.1  christos 	case DNS_DTTYPE_UR:
    638  1.1  christos 		return (DNSTAP__MESSAGE__TYPE__UPDATE_RESPONSE);
    639  1.1  christos 	default:
    640  1.1  christos 		UNREACHABLE();
    641  1.1  christos 	}
    642  1.1  christos }
    643  1.1  christos 
    644  1.1  christos static void
    645  1.1  christos cpbuf(isc_buffer_t *buf, ProtobufCBinaryData *p, protobuf_c_boolean *has) {
    646  1.1  christos 	p->data = isc_buffer_base(buf);
    647  1.1  christos 	p->len = isc_buffer_usedlength(buf);
    648  1.1  christos 	*has = 1;
    649  1.1  christos }
    650  1.1  christos 
    651  1.1  christos static void
    652  1.1  christos setaddr(dns_dtmsg_t *dm, isc_sockaddr_t *sa, bool tcp,
    653  1.1  christos 	ProtobufCBinaryData *addr, protobuf_c_boolean *has_addr, uint32_t *port,
    654  1.1  christos 	protobuf_c_boolean *has_port) {
    655  1.1  christos 	int family = isc_sockaddr_pf(sa);
    656  1.1  christos 
    657  1.1  christos 	if (family != AF_INET6 && family != AF_INET) {
    658  1.1  christos 		return;
    659  1.1  christos 	}
    660  1.1  christos 
    661  1.1  christos 	if (family == AF_INET6) {
    662  1.1  christos 		dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET6;
    663  1.1  christos 		addr->data = sa->type.sin6.sin6_addr.s6_addr;
    664  1.1  christos 		addr->len = 16;
    665  1.1  christos 		*port = ntohs(sa->type.sin6.sin6_port);
    666  1.1  christos 	} else {
    667  1.1  christos 		dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET;
    668  1.1  christos 		addr->data = (uint8_t *)&sa->type.sin.sin_addr.s_addr;
    669  1.1  christos 		addr->len = 4;
    670  1.1  christos 		*port = ntohs(sa->type.sin.sin_port);
    671  1.1  christos 	}
    672  1.1  christos 
    673  1.1  christos 	if (tcp) {
    674  1.1  christos 		dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP;
    675  1.1  christos 	} else {
    676  1.1  christos 		dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP;
    677  1.1  christos 	}
    678  1.1  christos 
    679  1.1  christos 	dm->m.has_socket_protocol = 1;
    680  1.1  christos 	dm->m.has_socket_family = 1;
    681  1.1  christos 	*has_addr = 1;
    682  1.1  christos 	*has_port = 1;
    683  1.1  christos }
    684  1.1  christos 
    685  1.1  christos /*%
    686  1.1  christos  * Invoke dns_dt_reopen() and re-allow dnstap output file rolling.  This
    687  1.1  christos  * function is run in the context of the task stored in the 'reopen_task' field
    688  1.1  christos  * of the dnstap environment structure.
    689  1.1  christos  */
    690  1.1  christos static void
    691  1.1  christos perform_reopen(isc_task_t *task, isc_event_t *event) {
    692  1.1  christos 	dns_dtenv_t *env;
    693  1.1  christos 
    694  1.1  christos 	REQUIRE(event != NULL);
    695  1.1  christos 	REQUIRE(event->ev_type == DNS_EVENT_FREESTORAGE);
    696  1.1  christos 
    697  1.1  christos 	env = (dns_dtenv_t *)event->ev_arg;
    698  1.1  christos 
    699  1.1  christos 	REQUIRE(VALID_DTENV(env));
    700  1.1  christos 	REQUIRE(task == env->reopen_task);
    701  1.1  christos 
    702  1.1  christos 	/*
    703  1.1  christos 	 * Roll output file in the context of env->reopen_task.
    704  1.1  christos 	 */
    705  1.1  christos 	dns_dt_reopen(env, env->rolls);
    706  1.1  christos 
    707  1.1  christos 	/*
    708  1.1  christos 	 * Clean up.
    709  1.1  christos 	 */
    710  1.1  christos 	isc_event_free(&event);
    711  1.1  christos 	isc_task_detach(&task);
    712  1.1  christos 
    713  1.1  christos 	/*
    714  1.1  christos 	 * Re-allow output file rolling.
    715  1.1  christos 	 */
    716  1.1  christos 	LOCK(&env->reopen_lock);
    717  1.1  christos 	env->reopen_queued = false;
    718  1.1  christos 	UNLOCK(&env->reopen_lock);
    719  1.1  christos }
    720  1.1  christos 
    721  1.1  christos /*%
    722  1.1  christos  * Check whether a dnstap output file roll is due and if so, initiate it (the
    723  1.1  christos  * actual roll happens asynchronously).
    724  1.1  christos  */
    725  1.1  christos static void
    726  1.1  christos check_file_size_and_maybe_reopen(dns_dtenv_t *env) {
    727  1.1  christos 	isc_task_t *reopen_task = NULL;
    728  1.1  christos 	isc_event_t *event;
    729  1.1  christos 	struct stat statbuf;
    730  1.1  christos 
    731  1.1  christos 	/*
    732  1.1  christos 	 * If the task from which the output file should be reopened was not
    733  1.1  christos 	 * specified, abort.
    734  1.1  christos 	 */
    735  1.1  christos 	if (env->reopen_task == NULL) {
    736  1.1  christos 		return;
    737  1.1  christos 	}
    738  1.1  christos 
    739  1.1  christos 	/*
    740  1.1  christos 	 * If an output file roll is not currently queued, check the current
    741  1.1  christos 	 * size of the output file to see whether a roll is needed.  Return if
    742  1.1  christos 	 * it is not.
    743  1.1  christos 	 */
    744  1.1  christos 	LOCK(&env->reopen_lock);
    745  1.1  christos 	if (env->reopen_queued || stat(env->path, &statbuf) < 0 ||
    746  1.1  christos 	    statbuf.st_size <= env->max_size)
    747  1.1  christos 	{
    748  1.1  christos 		goto unlock_and_return;
    749  1.1  christos 	}
    750  1.1  christos 
    751  1.1  christos 	/*
    752  1.1  christos 	 * We need to roll the output file, but it needs to be done in the
    753  1.1  christos 	 * context of env->reopen_task.  Allocate and send an event to achieve
    754  1.1  christos 	 * that, then disallow output file rolling until the roll we queue is
    755  1.1  christos 	 * completed.
    756  1.1  christos 	 */
    757  1.1  christos 	event = isc_event_allocate(env->mctx, NULL, DNS_EVENT_FREESTORAGE,
    758  1.1  christos 				   perform_reopen, env, sizeof(*event));
    759  1.1  christos 	isc_task_attach(env->reopen_task, &reopen_task);
    760  1.1  christos 	isc_task_send(reopen_task, &event);
    761  1.1  christos 	env->reopen_queued = true;
    762  1.1  christos 
    763  1.1  christos unlock_and_return:
    764  1.1  christos 	UNLOCK(&env->reopen_lock);
    765  1.1  christos }
    766  1.1  christos 
    767  1.1  christos void
    768  1.1  christos dns_dt_send(dns_view_t *view, dns_dtmsgtype_t msgtype, isc_sockaddr_t *qaddr,
    769  1.1  christos 	    isc_sockaddr_t *raddr, bool tcp, isc_region_t *zone,
    770  1.1  christos 	    isc_time_t *qtime, isc_time_t *rtime, isc_buffer_t *buf) {
    771  1.1  christos 	isc_time_t now, *t;
    772  1.1  christos 	dns_dtmsg_t dm;
    773  1.1  christos 
    774  1.1  christos 	REQUIRE(DNS_VIEW_VALID(view));
    775  1.1  christos 
    776  1.1  christos 	if ((msgtype & view->dttypes) == 0) {
    777  1.1  christos 		return;
    778  1.1  christos 	}
    779  1.1  christos 
    780  1.1  christos 	if (view->dtenv == NULL) {
    781  1.1  christos 		return;
    782  1.1  christos 	}
    783  1.1  christos 
    784  1.1  christos 	REQUIRE(VALID_DTENV(view->dtenv));
    785  1.1  christos 
    786  1.1  christos 	if (view->dtenv->max_size != 0) {
    787  1.1  christos 		check_file_size_and_maybe_reopen(view->dtenv);
    788  1.1  christos 	}
    789  1.1  christos 
    790  1.1  christos 	TIME_NOW(&now);
    791  1.1  christos 	t = &now;
    792  1.1  christos 
    793  1.1  christos 	init_msg(view->dtenv, &dm, dnstap_type(msgtype));
    794  1.1  christos 
    795  1.1  christos 	/* Query/response times */
    796  1.1  christos 	switch (msgtype) {
    797  1.1  christos 	case DNS_DTTYPE_AR:
    798  1.1  christos 	case DNS_DTTYPE_CR:
    799  1.1  christos 	case DNS_DTTYPE_RR:
    800  1.1  christos 	case DNS_DTTYPE_FR:
    801  1.1  christos 	case DNS_DTTYPE_SR:
    802  1.1  christos 	case DNS_DTTYPE_TR:
    803  1.1  christos 	case DNS_DTTYPE_UR:
    804  1.1  christos 		if (rtime != NULL) {
    805  1.1  christos 			t = rtime;
    806  1.1  christos 		}
    807  1.1  christos 
    808  1.1  christos 		dm.m.response_time_sec = isc_time_seconds(t);
    809  1.1  christos 		dm.m.has_response_time_sec = 1;
    810  1.1  christos 		dm.m.response_time_nsec = isc_time_nanoseconds(t);
    811  1.1  christos 		dm.m.has_response_time_nsec = 1;
    812  1.1  christos 
    813  1.1  christos 		/*
    814  1.1  christos 		 * Types RR and FR can fall through and get the query
    815  1.1  christos 		 * time set as well. Any other response type, break.
    816  1.1  christos 		 */
    817  1.1  christos 		if (msgtype != DNS_DTTYPE_RR && msgtype != DNS_DTTYPE_FR) {
    818  1.1  christos 			break;
    819  1.1  christos 		}
    820  1.1  christos 
    821  1.1  christos 		FALLTHROUGH;
    822  1.1  christos 	case DNS_DTTYPE_AQ:
    823  1.1  christos 	case DNS_DTTYPE_CQ:
    824  1.1  christos 	case DNS_DTTYPE_FQ:
    825  1.1  christos 	case DNS_DTTYPE_RQ:
    826  1.1  christos 	case DNS_DTTYPE_SQ:
    827  1.1  christos 	case DNS_DTTYPE_TQ:
    828  1.1  christos 	case DNS_DTTYPE_UQ:
    829  1.1  christos 		if (qtime != NULL) {
    830  1.1  christos 			t = qtime;
    831  1.1  christos 		}
    832  1.1  christos 
    833  1.1  christos 		dm.m.query_time_sec = isc_time_seconds(t);
    834  1.1  christos 		dm.m.has_query_time_sec = 1;
    835  1.1  christos 		dm.m.query_time_nsec = isc_time_nanoseconds(t);
    836  1.1  christos 		dm.m.has_query_time_nsec = 1;
    837  1.1  christos 		break;
    838  1.1  christos 	default:
    839  1.1  christos 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
    840  1.1  christos 			      DNS_LOGMODULE_DNSTAP, ISC_LOG_ERROR,
    841  1.1  christos 			      "invalid dnstap message type %d", msgtype);
    842  1.1  christos 		return;
    843  1.1  christos 	}
    844  1.1  christos 
    845  1.1  christos 	/* Query and response messages */
    846  1.1  christos 	if ((msgtype & DNS_DTTYPE_QUERY) != 0) {
    847  1.1  christos 		cpbuf(buf, &dm.m.query_message, &dm.m.has_query_message);
    848  1.1  christos 	} else if ((msgtype & DNS_DTTYPE_RESPONSE) != 0) {
    849  1.1  christos 		cpbuf(buf, &dm.m.response_message, &dm.m.has_response_message);
    850  1.1  christos 	}
    851  1.1  christos 
    852  1.1  christos 	/* Zone/bailiwick */
    853  1.1  christos 	switch (msgtype) {
    854  1.1  christos 	case DNS_DTTYPE_AR:
    855  1.1  christos 	case DNS_DTTYPE_RQ:
    856  1.1  christos 	case DNS_DTTYPE_RR:
    857  1.1  christos 	case DNS_DTTYPE_FQ:
    858  1.1  christos 	case DNS_DTTYPE_FR:
    859  1.1  christos 		if (zone != NULL && zone->base != NULL && zone->length != 0) {
    860  1.1  christos 			dm.m.query_zone.data = zone->base;
    861  1.1  christos 			dm.m.query_zone.len = zone->length;
    862  1.1  christos 			dm.m.has_query_zone = 1;
    863  1.1  christos 		}
    864  1.1  christos 		break;
    865  1.1  christos 	default:
    866  1.1  christos 		break;
    867  1.1  christos 	}
    868  1.1  christos 
    869  1.1  christos 	if (qaddr != NULL) {
    870  1.1  christos 		setaddr(&dm, qaddr, tcp, &dm.m.query_address,
    871  1.1  christos 			&dm.m.has_query_address, &dm.m.query_port,
    872  1.1  christos 			&dm.m.has_query_port);
    873  1.1  christos 	}
    874  1.1  christos 	if (raddr != NULL) {
    875  1.1  christos 		setaddr(&dm, raddr, tcp, &dm.m.response_address,
    876  1.1  christos 			&dm.m.has_response_address, &dm.m.response_port,
    877  1.1  christos 			&dm.m.has_response_port);
    878  1.1  christos 	}
    879  1.1  christos 
    880  1.1  christos 	if (pack_dt(&dm.d, &dm.buf, &dm.len) == ISC_R_SUCCESS) {
    881  1.1  christos 		send_dt(view->dtenv, dm.buf, dm.len);
    882  1.1  christos 	}
    883  1.1  christos }
    884  1.1  christos 
    885  1.1  christos static isc_result_t
    886  1.1  christos putstr(isc_buffer_t **b, const char *str) {
    887  1.1  christos 	isc_result_t result;
    888  1.1  christos 
    889  1.1  christos 	result = isc_buffer_reserve(b, strlen(str));
    890  1.1  christos 	if (result != ISC_R_SUCCESS) {
    891  1.1  christos 		return (ISC_R_NOSPACE);
    892  1.1  christos 	}
    893  1.1  christos 
    894  1.1  christos 	isc_buffer_putstr(*b, str);
    895  1.1  christos 	return (ISC_R_SUCCESS);
    896  1.1  christos }
    897  1.1  christos 
    898  1.1  christos static isc_result_t
    899  1.1  christos putaddr(isc_buffer_t **b, isc_region_t *ip) {
    900  1.1  christos 	char buf[64];
    901  1.1  christos 
    902  1.1  christos 	if (ip->length == 4) {
    903  1.1  christos 		if (!inet_ntop(AF_INET, ip->base, buf, sizeof(buf))) {
    904  1.1  christos 			return (ISC_R_FAILURE);
    905  1.1  christos 		}
    906  1.1  christos 	} else if (ip->length == 16) {
    907  1.1  christos 		if (!inet_ntop(AF_INET6, ip->base, buf, sizeof(buf))) {
    908  1.1  christos 			return (ISC_R_FAILURE);
    909  1.1  christos 		}
    910  1.1  christos 	} else {
    911  1.1  christos 		return (ISC_R_BADADDRESSFORM);
    912  1.1  christos 	}
    913  1.1  christos 
    914  1.1  christos 	return (putstr(b, buf));
    915  1.1  christos }
    916  1.1  christos 
    917  1.1  christos static bool
    918  1.1  christos dnstap_file(struct fstrm_reader *r) {
    919  1.1  christos 	fstrm_res res;
    920  1.1  christos 	const struct fstrm_control *control = NULL;
    921  1.1  christos 	const uint8_t *rtype = NULL;
    922  1.1  christos 	size_t dlen = strlen(DNSTAP_CONTENT_TYPE), rlen = 0;
    923  1.1  christos 	size_t n = 0;
    924  1.1  christos 
    925  1.1  christos 	res = fstrm_reader_get_control(r, FSTRM_CONTROL_START, &control);
    926  1.1  christos 	if (res != fstrm_res_success) {
    927  1.1  christos 		return (false);
    928  1.1  christos 	}
    929  1.1  christos 
    930  1.1  christos 	res = fstrm_control_get_num_field_content_type(control, &n);
    931  1.1  christos 	if (res != fstrm_res_success) {
    932  1.1  christos 		return (false);
    933  1.1  christos 	}
    934  1.1  christos 	if (n > 0) {
    935  1.1  christos 		res = fstrm_control_get_field_content_type(control, 0, &rtype,
    936  1.1  christos 							   &rlen);
    937  1.1  christos 		if (res != fstrm_res_success) {
    938  1.1  christos 			return (false);
    939  1.1  christos 		}
    940  1.1  christos 
    941  1.1  christos 		if (rlen != dlen) {
    942  1.1  christos 			return (false);
    943  1.1  christos 		}
    944  1.1  christos 
    945  1.1  christos 		if (memcmp(DNSTAP_CONTENT_TYPE, rtype, dlen) == 0) {
    946  1.1  christos 			return (true);
    947  1.1  christos 		}
    948  1.1  christos 	}
    949  1.1  christos 
    950  1.1  christos 	return (false);
    951  1.1  christos }
    952  1.1  christos 
    953  1.1  christos isc_result_t
    954  1.1  christos dns_dt_open(const char *filename, dns_dtmode_t mode, isc_mem_t *mctx,
    955  1.1  christos 	    dns_dthandle_t **handlep) {
    956  1.1  christos 	isc_result_t result;
    957  1.1  christos 	struct fstrm_file_options *fopt = NULL;
    958  1.1  christos 	fstrm_res res;
    959  1.1  christos 	dns_dthandle_t *handle;
    960  1.1  christos 
    961  1.1  christos 	REQUIRE(handlep != NULL && *handlep == NULL);
    962  1.1  christos 
    963  1.1  christos 	handle = isc_mem_get(mctx, sizeof(*handle));
    964  1.1  christos 
    965  1.1  christos 	handle->mode = mode;
    966  1.1  christos 	handle->mctx = NULL;
    967  1.1  christos 
    968  1.1  christos 	switch (mode) {
    969  1.1  christos 	case dns_dtmode_file:
    970  1.1  christos 		fopt = fstrm_file_options_init();
    971  1.1  christos 		if (fopt == NULL) {
    972  1.1  christos 			CHECK(ISC_R_NOMEMORY);
    973  1.1  christos 		}
    974  1.1  christos 
    975  1.1  christos 		fstrm_file_options_set_file_path(fopt, filename);
    976  1.1  christos 
    977  1.1  christos 		handle->reader = fstrm_file_reader_init(fopt, NULL);
    978  1.1  christos 		if (handle->reader == NULL) {
    979  1.1  christos 			CHECK(ISC_R_NOMEMORY);
    980  1.1  christos 		}
    981  1.1  christos 
    982  1.1  christos 		res = fstrm_reader_open(handle->reader);
    983  1.1  christos 		if (res != fstrm_res_success) {
    984  1.1  christos 			CHECK(ISC_R_FAILURE);
    985  1.1  christos 		}
    986  1.1  christos 
    987  1.1  christos 		if (!dnstap_file(handle->reader)) {
    988  1.1  christos 			CHECK(DNS_R_BADDNSTAP);
    989  1.1  christos 		}
    990  1.1  christos 		break;
    991  1.1  christos 	case dns_dtmode_unix:
    992  1.1  christos 		result = ISC_R_NOTIMPLEMENTED;
    993  1.1  christos 		goto cleanup;
    994  1.1  christos 	default:
    995  1.1  christos 		UNREACHABLE();
    996  1.1  christos 	}
    997  1.1  christos 
    998  1.1  christos 	isc_mem_attach(mctx, &handle->mctx);
    999  1.1  christos 	result = ISC_R_SUCCESS;
   1000  1.1  christos 	*handlep = handle;
   1001  1.1  christos 	handle = NULL;
   1002  1.1  christos 
   1003  1.1  christos cleanup:
   1004  1.1  christos 	if (result != ISC_R_SUCCESS && handle->reader != NULL) {
   1005  1.1  christos 		fstrm_reader_destroy(&handle->reader);
   1006  1.1  christos 		handle->reader = NULL;
   1007  1.1  christos 	}
   1008  1.1  christos 	if (fopt != NULL) {
   1009  1.1  christos 		fstrm_file_options_destroy(&fopt);
   1010  1.1  christos 	}
   1011  1.1  christos 	if (handle != NULL) {
   1012  1.1  christos 		isc_mem_put(mctx, handle, sizeof(*handle));
   1013  1.1  christos 	}
   1014  1.1  christos 	return (result);
   1015  1.1  christos }
   1016  1.1  christos 
   1017  1.1  christos isc_result_t
   1018  1.1  christos dns_dt_getframe(dns_dthandle_t *handle, uint8_t **bufp, size_t *sizep) {
   1019  1.1  christos 	const uint8_t *data;
   1020  1.1  christos 	fstrm_res res;
   1021  1.1  christos 
   1022  1.1  christos 	REQUIRE(handle != NULL);
   1023  1.1  christos 	REQUIRE(bufp != NULL);
   1024  1.1  christos 	REQUIRE(sizep != NULL);
   1025  1.1  christos 
   1026  1.1  christos 	data = (const uint8_t *)*bufp;
   1027  1.1  christos 
   1028  1.1  christos 	res = fstrm_reader_read(handle->reader, &data, sizep);
   1029  1.1  christos 	switch (res) {
   1030  1.1  christos 	case fstrm_res_success:
   1031  1.1  christos 		if (data == NULL) {
   1032  1.1  christos 			return (ISC_R_FAILURE);
   1033  1.1  christos 		}
   1034  1.1  christos 		DE_CONST(data, *bufp);
   1035  1.1  christos 		return (ISC_R_SUCCESS);
   1036  1.1  christos 	case fstrm_res_stop:
   1037  1.1  christos 		return (ISC_R_NOMORE);
   1038  1.1  christos 	default:
   1039  1.1  christos 		return (ISC_R_FAILURE);
   1040  1.1  christos 	}
   1041  1.1  christos }
   1042  1.1  christos 
   1043  1.1  christos void
   1044  1.1  christos dns_dt_close(dns_dthandle_t **handlep) {
   1045  1.1  christos 	dns_dthandle_t *handle;
   1046  1.1  christos 
   1047  1.1  christos 	REQUIRE(handlep != NULL && *handlep != NULL);
   1048  1.1  christos 
   1049  1.1  christos 	handle = *handlep;
   1050  1.1  christos 	*handlep = NULL;
   1051  1.1  christos 
   1052  1.1  christos 	if (handle->reader != NULL) {
   1053  1.1  christos 		fstrm_reader_destroy(&handle->reader);
   1054  1.1  christos 		handle->reader = NULL;
   1055  1.1  christos 	}
   1056  1.1  christos 	isc_mem_putanddetach(&handle->mctx, handle, sizeof(*handle));
   1057  1.1  christos }
   1058  1.1  christos 
   1059  1.1  christos isc_result_t
   1060  1.1  christos dns_dt_parse(isc_mem_t *mctx, isc_region_t *src, dns_dtdata_t **destp) {
   1061  1.1  christos 	isc_result_t result;
   1062  1.1  christos 	Dnstap__Dnstap *frame;
   1063  1.1  christos 	Dnstap__Message *m;
   1064  1.1  christos 	dns_dtdata_t *d = NULL;
   1065  1.1  christos 	isc_buffer_t b;
   1066  1.1  christos 
   1067  1.1  christos 	REQUIRE(src != NULL);
   1068  1.1  christos 	REQUIRE(destp != NULL && *destp == NULL);
   1069  1.1  christos 
   1070  1.1  christos 	d = isc_mem_get(mctx, sizeof(*d));
   1071  1.1  christos 
   1072  1.1  christos 	memset(d, 0, sizeof(*d));
   1073  1.1  christos 	isc_mem_attach(mctx, &d->mctx);
   1074  1.1  christos 
   1075  1.1  christos 	d->frame = dnstap__dnstap__unpack(NULL, src->length, src->base);
   1076  1.1  christos 	if (d->frame == NULL) {
   1077  1.1  christos 		CHECK(ISC_R_NOMEMORY);
   1078  1.1  christos 	}
   1079  1.1  christos 
   1080  1.1  christos 	frame = (Dnstap__Dnstap *)d->frame;
   1081  1.1  christos 
   1082  1.1  christos 	if (frame->type != DNSTAP__DNSTAP__TYPE__MESSAGE) {
   1083  1.1  christos 		CHECK(DNS_R_BADDNSTAP);
   1084  1.1  christos 	}
   1085  1.1  christos 
   1086  1.1  christos 	m = frame->message;
   1087  1.1  christos 
   1088  1.1  christos 	/* Message type */
   1089  1.1  christos 	switch (m->type) {
   1090  1.1  christos 	case DNSTAP__MESSAGE__TYPE__AUTH_QUERY:
   1091  1.1  christos 		d->type = DNS_DTTYPE_AQ;
   1092  1.1  christos 		break;
   1093  1.1  christos 	case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE:
   1094  1.1  christos 		d->type = DNS_DTTYPE_AR;
   1095  1.1  christos 		break;
   1096  1.1  christos 	case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY:
   1097  1.1  christos 		d->type = DNS_DTTYPE_CQ;
   1098  1.1  christos 		break;
   1099  1.1  christos 	case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE:
   1100  1.1  christos 		d->type = DNS_DTTYPE_CR;
   1101  1.1  christos 		break;
   1102  1.1  christos 	case DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY:
   1103  1.1  christos 		d->type = DNS_DTTYPE_FQ;
   1104  1.1  christos 		break;
   1105  1.1  christos 	case DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE:
   1106  1.1  christos 		d->type = DNS_DTTYPE_FR;
   1107  1.1  christos 		break;
   1108  1.1  christos 	case DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY:
   1109  1.1  christos 		d->type = DNS_DTTYPE_RQ;
   1110  1.1  christos 		break;
   1111  1.1  christos 	case DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE:
   1112  1.1  christos 		d->type = DNS_DTTYPE_RR;
   1113  1.1  christos 		break;
   1114  1.1  christos 	case DNSTAP__MESSAGE__TYPE__STUB_QUERY:
   1115  1.1  christos 		d->type = DNS_DTTYPE_SQ;
   1116  1.1  christos 		break;
   1117  1.1  christos 	case DNSTAP__MESSAGE__TYPE__STUB_RESPONSE:
   1118  1.1  christos 		d->type = DNS_DTTYPE_SR;
   1119  1.1  christos 		break;
   1120  1.1  christos 	case DNSTAP__MESSAGE__TYPE__TOOL_QUERY:
   1121  1.1  christos 		d->type = DNS_DTTYPE_TQ;
   1122  1.1  christos 		break;
   1123  1.1  christos 	case DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE:
   1124  1.1  christos 		d->type = DNS_DTTYPE_TR;
   1125  1.1  christos 		break;
   1126  1.1  christos 	case DNSTAP__MESSAGE__TYPE__UPDATE_QUERY:
   1127  1.1  christos 		d->type = DNS_DTTYPE_UQ;
   1128  1.1  christos 		break;
   1129  1.1  christos 	case DNSTAP__MESSAGE__TYPE__UPDATE_RESPONSE:
   1130  1.1  christos 		d->type = DNS_DTTYPE_UR;
   1131  1.1  christos 		break;
   1132  1.1  christos 	default:
   1133  1.1  christos 		CHECK(DNS_R_BADDNSTAP);
   1134  1.1  christos 	}
   1135  1.1  christos 
   1136  1.1  christos 	/* Query? */
   1137  1.1  christos 	if ((d->type & DNS_DTTYPE_QUERY) != 0) {
   1138  1.1  christos 		d->query = true;
   1139  1.1  christos 	} else {
   1140  1.1  christos 		d->query = false;
   1141  1.1  christos 	}
   1142  1.1  christos 
   1143  1.1  christos 	/* Parse DNS message */
   1144  1.1  christos 	if (d->query && m->has_query_message) {
   1145  1.1  christos 		d->msgdata.base = m->query_message.data;
   1146  1.1  christos 		d->msgdata.length = m->query_message.len;
   1147  1.1  christos 	} else if (!d->query && m->has_response_message) {
   1148  1.1  christos 		d->msgdata.base = m->response_message.data;
   1149  1.1  christos 		d->msgdata.length = m->response_message.len;
   1150  1.1  christos 	}
   1151  1.1  christos 
   1152  1.1  christos 	isc_buffer_init(&b, d->msgdata.base, d->msgdata.length);
   1153  1.1  christos 	isc_buffer_add(&b, d->msgdata.length);
   1154  1.1  christos 	dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &d->msg);
   1155  1.1  christos 	result = dns_message_parse(d->msg, &b, 0);
   1156  1.1  christos 	if (result != ISC_R_SUCCESS) {
   1157  1.1  christos 		if (result != DNS_R_RECOVERABLE) {
   1158  1.1  christos 			dns_message_detach(&d->msg);
   1159  1.1  christos 		}
   1160  1.1  christos 		result = ISC_R_SUCCESS;
   1161  1.1  christos 	}
   1162  1.1  christos 
   1163  1.1  christos 	/* Timestamp */
   1164  1.1  christos 	if (d->query) {
   1165  1.1  christos 		if (m->has_query_time_sec && m->has_query_time_nsec) {
   1166  1.1  christos 			isc_time_set(&d->qtime, m->query_time_sec,
   1167  1.1  christos 				     m->query_time_nsec);
   1168  1.1  christos 		}
   1169  1.1  christos 	} else {
   1170  1.1  christos 		if (m->has_response_time_sec && m->has_response_time_nsec) {
   1171  1.1  christos 			isc_time_set(&d->rtime, m->response_time_sec,
   1172  1.1  christos 				     m->response_time_nsec);
   1173  1.1  christos 		}
   1174  1.1  christos 	}
   1175  1.1  christos 
   1176  1.1  christos 	/* Peer address */
   1177  1.1  christos 	if (m->has_query_address) {
   1178  1.1  christos 		d->qaddr.base = m->query_address.data;
   1179  1.1  christos 		d->qaddr.length = m->query_address.len;
   1180  1.1  christos 	}
   1181  1.1  christos 	if (m->has_query_port) {
   1182  1.1  christos 		d->qport = m->query_port;
   1183  1.1  christos 	}
   1184  1.1  christos 
   1185  1.1  christos 	if (m->has_response_address) {
   1186  1.1  christos 		d->raddr.base = m->response_address.data;
   1187  1.1  christos 		d->raddr.length = m->response_address.len;
   1188  1.1  christos 	}
   1189  1.1  christos 	if (m->has_response_port) {
   1190  1.1  christos 		d->rport = m->response_port;
   1191  1.1  christos 	}
   1192  1.1  christos 
   1193  1.1  christos 	/* Socket protocol */
   1194  1.1  christos 	if (m->has_socket_protocol) {
   1195  1.1  christos 		const ProtobufCEnumValue *type =
   1196  1.1  christos 			protobuf_c_enum_descriptor_get_value(
   1197  1.1  christos 				&dnstap__socket_protocol__descriptor,
   1198  1.1  christos 				m->socket_protocol);
   1199  1.1  christos 		if (type != NULL && type->value == DNSTAP__SOCKET_PROTOCOL__TCP)
   1200  1.1  christos 		{
   1201  1.1  christos 			d->tcp = true;
   1202  1.1  christos 		} else {
   1203  1.1  christos 			d->tcp = false;
   1204  1.1  christos 		}
   1205  1.1  christos 	}
   1206  1.1  christos 
   1207  1.1  christos 	/* Query tuple */
   1208  1.1  christos 	if (d->msg != NULL) {
   1209  1.1  christos 		dns_name_t *name = NULL;
   1210  1.1  christos 		dns_rdataset_t *rdataset;
   1211  1.1  christos 
   1212  1.1  christos 		CHECK(dns_message_firstname(d->msg, DNS_SECTION_QUESTION));
   1213  1.1  christos 		dns_message_currentname(d->msg, DNS_SECTION_QUESTION, &name);
   1214  1.1  christos 		rdataset = ISC_LIST_HEAD(name->list);
   1215  1.1  christos 
   1216  1.1  christos 		dns_name_format(name, d->namebuf, sizeof(d->namebuf));
   1217  1.1  christos 		dns_rdatatype_format(rdataset->type, d->typebuf,
   1218  1.1  christos 				     sizeof(d->typebuf));
   1219  1.1  christos 		dns_rdataclass_format(rdataset->rdclass, d->classbuf,
   1220  1.1  christos 				      sizeof(d->classbuf));
   1221  1.1  christos 	}
   1222  1.1  christos 
   1223  1.1  christos 	*destp = d;
   1224  1.1  christos 
   1225  1.1  christos cleanup:
   1226  1.1  christos 	if (result != ISC_R_SUCCESS) {
   1227  1.1  christos 		dns_dtdata_free(&d);
   1228  1.1  christos 	}
   1229  1.1  christos 
   1230  1.1  christos 	return (result);
   1231  1.1  christos }
   1232  1.1  christos 
   1233  1.1  christos isc_result_t
   1234  1.1  christos dns_dt_datatotext(dns_dtdata_t *d, isc_buffer_t **dest) {
   1235  1.1  christos 	isc_result_t result;
   1236  1.1  christos 	char buf[100];
   1237  1.1  christos 
   1238  1.1  christos 	REQUIRE(d != NULL);
   1239  1.1  christos 	REQUIRE(dest != NULL && *dest != NULL);
   1240  1.1  christos 
   1241  1.1  christos 	memset(buf, 0, sizeof(buf));
   1242  1.1  christos 
   1243  1.1  christos 	/* Timestamp */
   1244  1.1  christos 	if (d->query && !isc_time_isepoch(&d->qtime)) {
   1245  1.1  christos 		isc_time_formattimestamp(&d->qtime, buf, sizeof(buf));
   1246  1.1  christos 	} else if (!d->query && !isc_time_isepoch(&d->rtime)) {
   1247  1.1  christos 		isc_time_formattimestamp(&d->rtime, buf, sizeof(buf));
   1248  1.1  christos 	}
   1249  1.1  christos 
   1250  1.1  christos 	if (buf[0] == '\0') {
   1251  1.1  christos 		CHECK(putstr(dest, "???\?-?\?-?? ??:??:??.??? "));
   1252  1.1  christos 	} else {
   1253  1.1  christos 		CHECK(putstr(dest, buf));
   1254  1.1  christos 		CHECK(putstr(dest, " "));
   1255  1.1  christos 	}
   1256  1.1  christos 
   1257  1.1  christos 	/* Type mnemonic */
   1258  1.1  christos 	switch (d->type) {
   1259  1.1  christos 	case DNS_DTTYPE_AQ:
   1260  1.1  christos 		CHECK(putstr(dest, "AQ "));
   1261  1.1  christos 		break;
   1262  1.1  christos 	case DNS_DTTYPE_AR:
   1263  1.1  christos 		CHECK(putstr(dest, "AR "));
   1264  1.1  christos 		break;
   1265  1.1  christos 	case DNS_DTTYPE_CQ:
   1266  1.1  christos 		CHECK(putstr(dest, "CQ "));
   1267  1.1  christos 		break;
   1268  1.1  christos 	case DNS_DTTYPE_CR:
   1269  1.1  christos 		CHECK(putstr(dest, "CR "));
   1270  1.1  christos 		break;
   1271  1.1  christos 	case DNS_DTTYPE_FQ:
   1272  1.1  christos 		CHECK(putstr(dest, "FQ "));
   1273  1.1  christos 		break;
   1274  1.1  christos 	case DNS_DTTYPE_FR:
   1275  1.1  christos 		CHECK(putstr(dest, "FR "));
   1276  1.1  christos 		break;
   1277  1.1  christos 	case DNS_DTTYPE_RQ:
   1278  1.1  christos 		CHECK(putstr(dest, "RQ "));
   1279  1.1  christos 		break;
   1280  1.1  christos 	case DNS_DTTYPE_RR:
   1281  1.1  christos 		CHECK(putstr(dest, "RR "));
   1282  1.1  christos 		break;
   1283  1.1  christos 	case DNS_DTTYPE_SQ:
   1284  1.1  christos 		CHECK(putstr(dest, "SQ "));
   1285  1.1  christos 		break;
   1286  1.1  christos 	case DNS_DTTYPE_SR:
   1287  1.1  christos 		CHECK(putstr(dest, "SR "));
   1288  1.1  christos 		break;
   1289  1.1  christos 	case DNS_DTTYPE_TQ:
   1290  1.1  christos 		CHECK(putstr(dest, "TQ "));
   1291  1.1  christos 		break;
   1292  1.1  christos 	case DNS_DTTYPE_TR:
   1293  1.1  christos 		CHECK(putstr(dest, "TR "));
   1294  1.1  christos 		break;
   1295  1.1  christos 	case DNS_DTTYPE_UQ:
   1296  1.1  christos 		CHECK(putstr(dest, "UQ "));
   1297  1.1  christos 		break;
   1298  1.1  christos 	case DNS_DTTYPE_UR:
   1299  1.1  christos 		CHECK(putstr(dest, "UR "));
   1300  1.1  christos 		break;
   1301  1.1  christos 	default:
   1302  1.1  christos 		return (DNS_R_BADDNSTAP);
   1303  1.1  christos 	}
   1304  1.1  christos 
   1305  1.1  christos 	/* Query and response addresses */
   1306  1.1  christos 	if (d->qaddr.length != 0) {
   1307  1.1  christos 		CHECK(putaddr(dest, &d->qaddr));
   1308  1.1  christos 		snprintf(buf, sizeof(buf), ":%u", d->qport);
   1309  1.1  christos 		CHECK(putstr(dest, buf));
   1310  1.1  christos 	} else {
   1311  1.1  christos 		CHECK(putstr(dest, "?"));
   1312  1.1  christos 	}
   1313  1.1  christos 	if ((d->type & DNS_DTTYPE_QUERY) != 0) {
   1314  1.1  christos 		CHECK(putstr(dest, " -> "));
   1315  1.1  christos 	} else {
   1316  1.1  christos 		CHECK(putstr(dest, " <- "));
   1317  1.1  christos 	}
   1318  1.1  christos 	if (d->raddr.length != 0) {
   1319  1.1  christos 		CHECK(putaddr(dest, &d->raddr));
   1320  1.1  christos 		snprintf(buf, sizeof(buf), ":%u", d->rport);
   1321  1.1  christos 		CHECK(putstr(dest, buf));
   1322  1.1  christos 	} else {
   1323  1.1  christos 		CHECK(putstr(dest, "?"));
   1324  1.1  christos 	}
   1325  1.1  christos 
   1326  1.1  christos 	CHECK(putstr(dest, " "));
   1327  1.1  christos 
   1328  1.1  christos 	/* Protocol */
   1329  1.1  christos 	if (d->tcp) {
   1330  1.1  christos 		CHECK(putstr(dest, "TCP "));
   1331  1.1  christos 	} else {
   1332  1.1  christos 		CHECK(putstr(dest, "UDP "));
   1333  1.1  christos 	}
   1334  1.1  christos 
   1335  1.1  christos 	/* Message size */
   1336  1.1  christos 	if (d->msgdata.base != NULL) {
   1337  1.1  christos 		snprintf(buf, sizeof(buf), "%zub ", (size_t)d->msgdata.length);
   1338  1.1  christos 		CHECK(putstr(dest, buf));
   1339  1.1  christos 	} else {
   1340  1.1  christos 		CHECK(putstr(dest, "0b "));
   1341  1.1  christos 	}
   1342  1.1  christos 
   1343  1.1  christos 	/* Query tuple */
   1344  1.1  christos 	if (d->namebuf[0] == '\0') {
   1345  1.1  christos 		CHECK(putstr(dest, "?/"));
   1346  1.1  christos 	} else {
   1347  1.1  christos 		CHECK(putstr(dest, d->namebuf));
   1348  1.1  christos 		CHECK(putstr(dest, "/"));
   1349  1.1  christos 	}
   1350  1.1  christos 
   1351  1.1  christos 	if (d->classbuf[0] == '\0') {
   1352  1.1  christos 		CHECK(putstr(dest, "?/"));
   1353  1.1  christos 	} else {
   1354  1.1  christos 		CHECK(putstr(dest, d->classbuf));
   1355  1.1  christos 		CHECK(putstr(dest, "/"));
   1356  1.1  christos 	}
   1357  1.1  christos 
   1358  1.1  christos 	if (d->typebuf[0] == '\0') {
   1359  1.1  christos 		CHECK(putstr(dest, "?"));
   1360  1.1  christos 	} else {
   1361  1.1  christos 		CHECK(putstr(dest, d->typebuf));
   1362  1.1  christos 	}
   1363  1.1  christos 
   1364  1.1  christos 	CHECK(isc_buffer_reserve(dest, 1));
   1365  1.1  christos 	isc_buffer_putuint8(*dest, 0);
   1366  1.1  christos 
   1367  1.1  christos cleanup:
   1368  1.1  christos 	return (result);
   1369  1.1  christos }
   1370  1.1  christos 
   1371  1.1  christos void
   1372  1.1  christos dns_dtdata_free(dns_dtdata_t **dp) {
   1373  1.1  christos 	dns_dtdata_t *d;
   1374  1.1  christos 
   1375  1.1  christos 	REQUIRE(dp != NULL && *dp != NULL);
   1376  1.1  christos 
   1377  1.1  christos 	d = *dp;
   1378  1.1  christos 	*dp = NULL;
   1379  1.1  christos 
   1380  1.1  christos 	if (d->msg != NULL) {
   1381  1.1  christos 		dns_message_detach(&d->msg);
   1382  1.1  christos 	}
   1383  1.1  christos 	if (d->frame != NULL) {
   1384  1.1  christos 		dnstap__dnstap__free_unpacked(d->frame, NULL);
   1385  1.1  christos 	}
   1386  1.1  christos 
   1387  1.1  christos 	isc_mem_putanddetach(&d->mctx, d, sizeof(*d));
   1388  1.1  christos }
   1389