xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/dns/dnstap.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
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 
92 struct dns_dtmsg {
93 	void *buf;
94 	size_t len;
95 	Dnstap__Dnstap d;
96 	Dnstap__Message m;
97 };
98 
99 struct dns_dthandle {
100 	dns_dtmode_t mode;
101 	struct fstrm_reader *reader;
102 	isc_mem_t *mctx;
103 };
104 
105 struct 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 
136 typedef struct ioq {
137 	unsigned int generation;
138 	struct fstrm_iothr_queue *ioq;
139 } dt__ioq_t;
140 
141 ISC_THREAD_LOCAL dt__ioq_t dt_ioq = { 0 };
142 
143 static atomic_uint_fast32_t global_generation;
144 
145 isc_result_t
dns_dt_create(isc_mem_t * mctx,dns_dtmode_t mode,const char * path,struct fstrm_iothr_options ** foptp,isc_task_t * reopen_task,dns_dtenv_t ** envp)146 dns_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 
226 cleanup:
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 
251 isc_result_t
dns_dt_setupfile(dns_dtenv_t * env,uint64_t max_size,int rolls,isc_log_rollsuffix_t suffix)252 dns_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 
277 isc_result_t
dns_dt_reopen(dns_dtenv_t * env,int roll)278 dns_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 
372 cleanup:
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 
394 static isc_result_t
toregion(dns_dtenv_t * env,isc_region_t * r,const char * str)395 toregion(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 
417 isc_result_t
dns_dt_setidentity(dns_dtenv_t * env,const char * identity)418 dns_dt_setidentity(dns_dtenv_t *env, const char *identity) {
419 	REQUIRE(VALID_DTENV(env));
420 
421 	return (toregion(env, &env->identity, identity));
422 }
423 
424 isc_result_t
dns_dt_setversion(dns_dtenv_t * env,const char * version)425 dns_dt_setversion(dns_dtenv_t *env, const char *version) {
426 	REQUIRE(VALID_DTENV(env));
427 
428 	return (toregion(env, &env->version, version));
429 }
430 
431 static void
set_dt_ioq(unsigned int generation,struct fstrm_iothr_queue * ioq)432 set_dt_ioq(unsigned int generation, struct fstrm_iothr_queue *ioq) {
433 	dt_ioq.generation = generation;
434 	dt_ioq.ioq = ioq;
435 }
436 
437 static struct fstrm_iothr_queue *
dt_queue(dns_dtenv_t * env)438 dt_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 
460 void
dns_dt_attach(dns_dtenv_t * source,dns_dtenv_t ** destp)461 dns_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 
469 isc_result_t
dns_dt_getstats(dns_dtenv_t * env,isc_stats_t ** statsp)470 dns_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 
481 static void
destroy(dns_dtenv_t * env)482 destroy(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 
514 void
dns_dt_detach(dns_dtenv_t ** envp)515 dns_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 
526 static isc_result_t
pack_dt(const Dnstap__Dnstap * d,void ** buf,size_t * sz)527 pack_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 
554 static void
send_dt(dns_dtenv_t * env,void * buf,size_t len)555 send_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 
586 static void
init_msg(dns_dtenv_t * env,dns_dtmsg_t * dm,Dnstap__Message__Type mtype)587 init_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 
608 static Dnstap__Message__Type
dnstap_type(dns_dtmsgtype_t msgtype)609 dnstap_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 
644 static void
cpbuf(isc_buffer_t * buf,ProtobufCBinaryData * p,protobuf_c_boolean * has)645 cpbuf(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 
651 static void
setaddr(dns_dtmsg_t * dm,isc_sockaddr_t * sa,bool tcp,ProtobufCBinaryData * addr,protobuf_c_boolean * has_addr,uint32_t * port,protobuf_c_boolean * has_port)652 setaddr(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  */
690 static void
perform_reopen(isc_task_t * task,isc_event_t * event)691 perform_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  */
725 static void
check_file_size_and_maybe_reopen(dns_dtenv_t * env)726 check_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 
763 unlock_and_return:
764 	UNLOCK(&env->reopen_lock);
765 }
766 
767 void
dns_dt_send(dns_view_t * view,dns_dtmsgtype_t msgtype,isc_sockaddr_t * qaddr,isc_sockaddr_t * raddr,bool tcp,isc_region_t * zone,isc_time_t * qtime,isc_time_t * rtime,isc_buffer_t * buf)768 dns_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 
885 static isc_result_t
putstr(isc_buffer_t ** b,const char * str)886 putstr(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 
898 static isc_result_t
putaddr(isc_buffer_t ** b,isc_region_t * ip)899 putaddr(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 
917 static bool
dnstap_file(struct fstrm_reader * r)918 dnstap_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 
953 isc_result_t
dns_dt_open(const char * filename,dns_dtmode_t mode,isc_mem_t * mctx,dns_dthandle_t ** handlep)954 dns_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 
1003 cleanup:
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 
1017 isc_result_t
dns_dt_getframe(dns_dthandle_t * handle,uint8_t ** bufp,size_t * sizep)1018 dns_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 
1043 void
dns_dt_close(dns_dthandle_t ** handlep)1044 dns_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 
1059 isc_result_t
dns_dt_parse(isc_mem_t * mctx,isc_region_t * src,dns_dtdata_t ** destp)1060 dns_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 
1225 cleanup:
1226 	if (result != ISC_R_SUCCESS) {
1227 		dns_dtdata_free(&d);
1228 	}
1229 
1230 	return (result);
1231 }
1232 
1233 isc_result_t
dns_dt_datatotext(dns_dtdata_t * d,isc_buffer_t ** dest)1234 dns_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 
1367 cleanup:
1368 	return (result);
1369 }
1370 
1371 void
dns_dtdata_free(dns_dtdata_t ** dp)1372 dns_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