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