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