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