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