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