mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-28 04:34:54 -04:00
previously, there were over 40 separate definitions of CHECK macros, of
which most used "goto cleanup", and the rest "goto failure" or "goto
out". there were another 10 definitions of RETERR, of which most were
identical to CHECK, but some simply returned a result code instead of
jumping to a cleanup label.
this has now been standardized throughout the code base: RETERR is for
returning an error code in the case of an error, and CHECK is for jumping
to a cleanup tag, which is now always called "cleanup". both macros are
defined in isc/util.h.
(cherry picked from commit 52bba5cc34)
1390 lines
32 KiB
C
1390 lines
32 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2013-2014, Farsight Security, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*! \file */
|
|
|
|
#ifndef HAVE_DNSTAP
|
|
#error DNSTAP not configured.
|
|
#endif /* HAVE_DNSTAP */
|
|
|
|
#include <fstrm.h>
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <isc/async.h>
|
|
#include <isc/buffer.h>
|
|
#include <isc/file.h>
|
|
#include <isc/log.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/mutex.h>
|
|
#include <isc/once.h>
|
|
#include <isc/result.h>
|
|
#include <isc/sockaddr.h>
|
|
#include <isc/thread.h>
|
|
#include <isc/time.h>
|
|
#include <isc/types.h>
|
|
#include <isc/util.h>
|
|
|
|
#include <dns/dnstap.h>
|
|
#include <dns/log.h>
|
|
#include <dns/message.h>
|
|
#include <dns/name.h>
|
|
#include <dns/rdataset.h>
|
|
#include <dns/stats.h>
|
|
#include <dns/types.h>
|
|
#include <dns/view.h>
|
|
|
|
#include "dnstap.pb-c.h"
|
|
|
|
#define DTENV_MAGIC ISC_MAGIC('D', 't', 'n', 'v')
|
|
#define VALID_DTENV(env) ISC_MAGIC_VALID(env, DTENV_MAGIC)
|
|
|
|
#define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap"
|
|
#define DNSTAP_INITIAL_BUF_SIZE 256
|
|
|
|
struct dns_dtmsg {
|
|
void *buf;
|
|
size_t len;
|
|
Dnstap__Dnstap d;
|
|
Dnstap__Message m;
|
|
};
|
|
|
|
struct dns_dthandle {
|
|
dns_dtmode_t mode;
|
|
struct fstrm_reader *reader;
|
|
isc_mem_t *mctx;
|
|
};
|
|
|
|
struct dns_dtenv {
|
|
unsigned int magic;
|
|
isc_refcount_t refcount;
|
|
|
|
isc_mem_t *mctx;
|
|
isc_loop_t *loop;
|
|
|
|
struct fstrm_iothr *iothr;
|
|
struct fstrm_iothr_options *fopt;
|
|
|
|
isc_mutex_t reopen_lock; /* locks 'reopen_queued' */
|
|
bool reopen_queued;
|
|
|
|
isc_region_t identity;
|
|
isc_region_t version;
|
|
char *path;
|
|
dns_dtmode_t mode;
|
|
off_t max_size;
|
|
int rolls;
|
|
isc_log_rollsuffix_t suffix;
|
|
isc_stats_t *stats;
|
|
};
|
|
|
|
typedef struct ioq {
|
|
unsigned int generation;
|
|
struct fstrm_iothr_queue *ioq;
|
|
} dt__ioq_t;
|
|
|
|
static thread_local dt__ioq_t dt_ioq = { 0 };
|
|
|
|
static atomic_uint_fast32_t global_generation;
|
|
|
|
isc_result_t
|
|
dns_dt_create(isc_mem_t *mctx, dns_dtmode_t mode, const char *path,
|
|
struct fstrm_iothr_options **foptp, isc_loop_t *loop,
|
|
dns_dtenv_t **envp) {
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
fstrm_res res;
|
|
struct fstrm_unix_writer_options *fuwopt = NULL;
|
|
struct fstrm_file_options *ffwopt = NULL;
|
|
struct fstrm_writer_options *fwopt = NULL;
|
|
struct fstrm_writer *fw = NULL;
|
|
dns_dtenv_t *env = NULL;
|
|
|
|
REQUIRE(path != NULL);
|
|
REQUIRE(envp != NULL && *envp == NULL);
|
|
REQUIRE(foptp != NULL && *foptp != NULL);
|
|
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, DNS_LOGMODULE_DNSTAP,
|
|
ISC_LOG_INFO, "opening dnstap destination '%s'", path);
|
|
|
|
atomic_fetch_add_release(&global_generation, 1);
|
|
|
|
env = isc_mem_get(mctx, sizeof(*env));
|
|
*env = (dns_dtenv_t){
|
|
.loop = loop,
|
|
.reopen_queued = false,
|
|
};
|
|
|
|
isc_mem_attach(mctx, &env->mctx);
|
|
isc_mutex_init(&env->reopen_lock);
|
|
env->path = isc_mem_strdup(env->mctx, path);
|
|
isc_refcount_init(&env->refcount, 1);
|
|
isc_stats_create(env->mctx, &env->stats, dns_dnstapcounter_max);
|
|
|
|
fwopt = fstrm_writer_options_init();
|
|
if (fwopt == NULL) {
|
|
CHECK(ISC_R_NOMEMORY);
|
|
}
|
|
|
|
res = fstrm_writer_options_add_content_type(
|
|
fwopt, DNSTAP_CONTENT_TYPE, sizeof(DNSTAP_CONTENT_TYPE) - 1);
|
|
if (res != fstrm_res_success) {
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
|
|
if (mode == dns_dtmode_file) {
|
|
ffwopt = fstrm_file_options_init();
|
|
if (ffwopt != NULL) {
|
|
fstrm_file_options_set_file_path(ffwopt, env->path);
|
|
fw = fstrm_file_writer_init(ffwopt, fwopt);
|
|
}
|
|
} else if (mode == dns_dtmode_unix) {
|
|
fuwopt = fstrm_unix_writer_options_init();
|
|
if (fuwopt != NULL) {
|
|
fstrm_unix_writer_options_set_socket_path(fuwopt,
|
|
env->path);
|
|
fw = fstrm_unix_writer_init(fuwopt, fwopt);
|
|
}
|
|
} else {
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
|
|
if (fw == NULL) {
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
|
|
env->iothr = fstrm_iothr_init(*foptp, &fw);
|
|
if (env->iothr == NULL) {
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
|
|
DNS_LOGMODULE_DNSTAP, ISC_LOG_WARNING,
|
|
"unable to initialize dnstap I/O thread");
|
|
fstrm_writer_destroy(&fw);
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
env->mode = mode;
|
|
env->max_size = 0;
|
|
env->rolls = ISC_LOG_ROLLINFINITE;
|
|
env->fopt = *foptp;
|
|
*foptp = NULL;
|
|
|
|
env->magic = DTENV_MAGIC;
|
|
*envp = env;
|
|
|
|
cleanup:
|
|
if (ffwopt != NULL) {
|
|
fstrm_file_options_destroy(&ffwopt);
|
|
}
|
|
|
|
if (fuwopt != NULL) {
|
|
fstrm_unix_writer_options_destroy(&fuwopt);
|
|
}
|
|
|
|
if (fwopt != NULL) {
|
|
fstrm_writer_options_destroy(&fwopt);
|
|
}
|
|
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_mutex_destroy(&env->reopen_lock);
|
|
isc_mem_free(env->mctx, env->path);
|
|
if (env->stats != NULL) {
|
|
isc_stats_detach(&env->stats);
|
|
}
|
|
isc_mem_putanddetach(&env->mctx, env, sizeof(dns_dtenv_t));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dt_setupfile(dns_dtenv_t *env, uint64_t max_size, int rolls,
|
|
isc_log_rollsuffix_t suffix) {
|
|
REQUIRE(VALID_DTENV(env));
|
|
|
|
/*
|
|
* If we're using unix domain socket mode, then any
|
|
* change from the default values is invalid.
|
|
*/
|
|
if (env->mode == dns_dtmode_unix) {
|
|
if (max_size == 0 && rolls == ISC_LOG_ROLLINFINITE &&
|
|
suffix == isc_log_rollsuffix_increment)
|
|
{
|
|
return ISC_R_SUCCESS;
|
|
} else {
|
|
return ISC_R_INVALIDFILE;
|
|
}
|
|
}
|
|
|
|
env->max_size = max_size;
|
|
env->rolls = rolls;
|
|
env->suffix = suffix;
|
|
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dt_reopen(dns_dtenv_t *env, int roll) {
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
fstrm_res res;
|
|
isc_logfile_t file;
|
|
struct fstrm_unix_writer_options *fuwopt = NULL;
|
|
struct fstrm_file_options *ffwopt = NULL;
|
|
struct fstrm_writer_options *fwopt = NULL;
|
|
struct fstrm_writer *fw = NULL;
|
|
isc_loopmgr_t *loopmgr = NULL;
|
|
|
|
REQUIRE(VALID_DTENV(env));
|
|
|
|
loopmgr = isc_loop_getloopmgr(env->loop);
|
|
isc_loopmgr_pause(loopmgr);
|
|
|
|
/*
|
|
* Check that we can create a new fw object.
|
|
*/
|
|
fwopt = fstrm_writer_options_init();
|
|
if (fwopt == NULL) {
|
|
CHECK(ISC_R_NOMEMORY);
|
|
}
|
|
|
|
res = fstrm_writer_options_add_content_type(
|
|
fwopt, DNSTAP_CONTENT_TYPE, sizeof(DNSTAP_CONTENT_TYPE) - 1);
|
|
if (res != fstrm_res_success) {
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
|
|
if (env->mode == dns_dtmode_file) {
|
|
ffwopt = fstrm_file_options_init();
|
|
if (ffwopt != NULL) {
|
|
fstrm_file_options_set_file_path(ffwopt, env->path);
|
|
fw = fstrm_file_writer_init(ffwopt, fwopt);
|
|
}
|
|
} else if (env->mode == dns_dtmode_unix) {
|
|
fuwopt = fstrm_unix_writer_options_init();
|
|
if (fuwopt != NULL) {
|
|
fstrm_unix_writer_options_set_socket_path(fuwopt,
|
|
env->path);
|
|
fw = fstrm_unix_writer_init(fuwopt, fwopt);
|
|
}
|
|
} else {
|
|
CHECK(ISC_R_NOTIMPLEMENTED);
|
|
}
|
|
|
|
if (fw == NULL) {
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
|
|
/*
|
|
* We are committed here.
|
|
*/
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, DNS_LOGMODULE_DNSTAP,
|
|
ISC_LOG_INFO, "%s dnstap destination '%s'",
|
|
(roll < 0) ? "reopening" : "rolling", env->path);
|
|
|
|
atomic_fetch_add_release(&global_generation, 1);
|
|
|
|
if (env->iothr != NULL) {
|
|
fstrm_iothr_destroy(&env->iothr);
|
|
}
|
|
|
|
if (roll == 0) {
|
|
roll = env->rolls;
|
|
}
|
|
|
|
if (env->mode == dns_dtmode_file && roll != 0) {
|
|
/*
|
|
* Create a temporary isc_logfile_t structure so we can
|
|
* take advantage of the logfile rolling facility.
|
|
*/
|
|
char *filename = isc_mem_strdup(env->mctx, env->path);
|
|
file.name = filename;
|
|
file.stream = NULL;
|
|
file.versions = roll;
|
|
file.maximum_size = 0;
|
|
file.maximum_reached = false;
|
|
file.suffix = env->suffix;
|
|
result = isc_logfile_roll(&file);
|
|
isc_mem_free(env->mctx, filename);
|
|
CHECK(result);
|
|
}
|
|
|
|
env->iothr = fstrm_iothr_init(env->fopt, &fw);
|
|
if (env->iothr == NULL) {
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
|
|
DNS_LOGMODULE_DNSTAP, ISC_LOG_WARNING,
|
|
"unable to initialize dnstap I/O thread");
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
|
|
cleanup:
|
|
if (fw != NULL) {
|
|
fstrm_writer_destroy(&fw);
|
|
}
|
|
|
|
if (fuwopt != NULL) {
|
|
fstrm_unix_writer_options_destroy(&fuwopt);
|
|
}
|
|
|
|
if (ffwopt != NULL) {
|
|
fstrm_file_options_destroy(&ffwopt);
|
|
}
|
|
|
|
if (fwopt != NULL) {
|
|
fstrm_writer_options_destroy(&fwopt);
|
|
}
|
|
|
|
isc_loopmgr_resume(loopmgr);
|
|
|
|
return result;
|
|
}
|
|
|
|
static isc_result_t
|
|
toregion(dns_dtenv_t *env, isc_region_t *r, const char *str) {
|
|
unsigned char *p = NULL;
|
|
|
|
REQUIRE(r != NULL);
|
|
|
|
if (str != NULL) {
|
|
p = (unsigned char *)isc_mem_strdup(env->mctx, str);
|
|
}
|
|
|
|
if (r->base != NULL) {
|
|
isc_mem_free(env->mctx, r->base);
|
|
r->length = 0;
|
|
}
|
|
|
|
if (p != NULL) {
|
|
r->base = p;
|
|
r->length = strlen((char *)p);
|
|
}
|
|
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dt_setidentity(dns_dtenv_t *env, const char *identity) {
|
|
REQUIRE(VALID_DTENV(env));
|
|
|
|
return toregion(env, &env->identity, identity);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dt_setversion(dns_dtenv_t *env, const char *version) {
|
|
REQUIRE(VALID_DTENV(env));
|
|
|
|
return toregion(env, &env->version, version);
|
|
}
|
|
|
|
static void
|
|
set_dt_ioq(unsigned int generation, struct fstrm_iothr_queue *ioq) {
|
|
dt_ioq.generation = generation;
|
|
dt_ioq.ioq = ioq;
|
|
}
|
|
|
|
static struct fstrm_iothr_queue *
|
|
dt_queue(dns_dtenv_t *env) {
|
|
REQUIRE(VALID_DTENV(env));
|
|
|
|
unsigned int generation;
|
|
|
|
if (env->iothr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
generation = atomic_load_acquire(&global_generation);
|
|
if (dt_ioq.ioq != NULL && dt_ioq.generation != generation) {
|
|
set_dt_ioq(0, NULL);
|
|
}
|
|
if (dt_ioq.ioq == NULL) {
|
|
struct fstrm_iothr_queue *ioq =
|
|
fstrm_iothr_get_input_queue(env->iothr);
|
|
set_dt_ioq(generation, ioq);
|
|
}
|
|
|
|
return dt_ioq.ioq;
|
|
}
|
|
|
|
void
|
|
dns_dt_attach(dns_dtenv_t *source, dns_dtenv_t **destp) {
|
|
REQUIRE(VALID_DTENV(source));
|
|
REQUIRE(destp != NULL && *destp == NULL);
|
|
|
|
isc_refcount_increment(&source->refcount);
|
|
*destp = source;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dt_getstats(dns_dtenv_t *env, isc_stats_t **statsp) {
|
|
REQUIRE(VALID_DTENV(env));
|
|
REQUIRE(statsp != NULL && *statsp == NULL);
|
|
|
|
if (env->stats == NULL) {
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
isc_stats_attach(env->stats, statsp);
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
destroy(dns_dtenv_t *env) {
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, DNS_LOGMODULE_DNSTAP,
|
|
ISC_LOG_INFO, "closing dnstap");
|
|
env->magic = 0;
|
|
|
|
atomic_fetch_add(&global_generation, 1);
|
|
|
|
if (env->iothr != NULL) {
|
|
fstrm_iothr_destroy(&env->iothr);
|
|
}
|
|
if (env->fopt != NULL) {
|
|
fstrm_iothr_options_destroy(&env->fopt);
|
|
}
|
|
|
|
if (env->identity.base != NULL) {
|
|
isc_mem_free(env->mctx, env->identity.base);
|
|
env->identity.length = 0;
|
|
}
|
|
if (env->version.base != NULL) {
|
|
isc_mem_free(env->mctx, env->version.base);
|
|
env->version.length = 0;
|
|
}
|
|
if (env->path != NULL) {
|
|
isc_mem_free(env->mctx, env->path);
|
|
}
|
|
if (env->stats != NULL) {
|
|
isc_stats_detach(&env->stats);
|
|
}
|
|
|
|
isc_mem_putanddetach(&env->mctx, env, sizeof(*env));
|
|
}
|
|
|
|
void
|
|
dns_dt_detach(dns_dtenv_t **envp) {
|
|
REQUIRE(envp != NULL && VALID_DTENV(*envp));
|
|
dns_dtenv_t *env = *envp;
|
|
*envp = NULL;
|
|
|
|
if (isc_refcount_decrement(&env->refcount) == 1) {
|
|
isc_refcount_destroy(&env->refcount);
|
|
destroy(env);
|
|
}
|
|
}
|
|
|
|
static isc_result_t
|
|
pack_dt(const Dnstap__Dnstap *d, void **buf, size_t *sz) {
|
|
ProtobufCBufferSimple sbuf;
|
|
|
|
REQUIRE(d != NULL);
|
|
REQUIRE(sz != NULL);
|
|
|
|
memset(&sbuf, 0, sizeof(sbuf));
|
|
sbuf.base.append = protobuf_c_buffer_simple_append;
|
|
sbuf.len = 0;
|
|
sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE;
|
|
|
|
/* Need to use malloc() here because protobuf uses free() */
|
|
sbuf.data = malloc(sbuf.alloced);
|
|
if (sbuf.data == NULL) {
|
|
return ISC_R_NOMEMORY;
|
|
}
|
|
sbuf.must_free_data = 1;
|
|
|
|
*sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *)&sbuf);
|
|
if (sbuf.data == NULL) {
|
|
return ISC_R_FAILURE;
|
|
}
|
|
*buf = sbuf.data;
|
|
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
send_dt(dns_dtenv_t *env, void *buf, size_t len) {
|
|
struct fstrm_iothr_queue *ioq;
|
|
fstrm_res res;
|
|
|
|
REQUIRE(env != NULL);
|
|
|
|
if (buf == NULL) {
|
|
return;
|
|
}
|
|
|
|
ioq = dt_queue(env);
|
|
if (ioq == NULL) {
|
|
free(buf);
|
|
return;
|
|
}
|
|
|
|
res = fstrm_iothr_submit(env->iothr, ioq, buf, len, fstrm_free_wrapper,
|
|
NULL);
|
|
if (res != fstrm_res_success) {
|
|
if (env->stats != NULL) {
|
|
isc_stats_increment(env->stats, dns_dnstapcounter_drop);
|
|
}
|
|
free(buf);
|
|
} else {
|
|
if (env->stats != NULL) {
|
|
isc_stats_increment(env->stats,
|
|
dns_dnstapcounter_success);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
init_msg(dns_dtenv_t *env, dns_dtmsg_t *dm, Dnstap__Message__Type mtype) {
|
|
memset(dm, 0, sizeof(*dm));
|
|
dm->d.base.descriptor = &dnstap__dnstap__descriptor;
|
|
dm->m.base.descriptor = &dnstap__message__descriptor;
|
|
dm->d.type = DNSTAP__DNSTAP__TYPE__MESSAGE;
|
|
dm->d.message = &dm->m;
|
|
dm->m.type = mtype;
|
|
|
|
if (env->identity.length != 0) {
|
|
dm->d.identity.data = env->identity.base;
|
|
dm->d.identity.len = env->identity.length;
|
|
dm->d.has_identity = true;
|
|
}
|
|
|
|
if (env->version.length != 0) {
|
|
dm->d.version.data = env->version.base;
|
|
dm->d.version.len = env->version.length;
|
|
dm->d.has_version = true;
|
|
}
|
|
}
|
|
|
|
static Dnstap__Message__Type
|
|
dnstap_type(dns_dtmsgtype_t msgtype) {
|
|
switch (msgtype) {
|
|
case DNS_DTTYPE_SQ:
|
|
return DNSTAP__MESSAGE__TYPE__STUB_QUERY;
|
|
case DNS_DTTYPE_SR:
|
|
return DNSTAP__MESSAGE__TYPE__STUB_RESPONSE;
|
|
case DNS_DTTYPE_CQ:
|
|
return DNSTAP__MESSAGE__TYPE__CLIENT_QUERY;
|
|
case DNS_DTTYPE_CR:
|
|
return DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE;
|
|
case DNS_DTTYPE_AQ:
|
|
return DNSTAP__MESSAGE__TYPE__AUTH_QUERY;
|
|
case DNS_DTTYPE_AR:
|
|
return DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE;
|
|
case DNS_DTTYPE_RQ:
|
|
return DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY;
|
|
case DNS_DTTYPE_RR:
|
|
return DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE;
|
|
case DNS_DTTYPE_FQ:
|
|
return DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY;
|
|
case DNS_DTTYPE_FR:
|
|
return DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE;
|
|
case DNS_DTTYPE_TQ:
|
|
return DNSTAP__MESSAGE__TYPE__TOOL_QUERY;
|
|
case DNS_DTTYPE_TR:
|
|
return DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE;
|
|
case DNS_DTTYPE_UQ:
|
|
return DNSTAP__MESSAGE__TYPE__UPDATE_QUERY;
|
|
case DNS_DTTYPE_UR:
|
|
return DNSTAP__MESSAGE__TYPE__UPDATE_RESPONSE;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
static void
|
|
cpbuf(isc_buffer_t *buf, ProtobufCBinaryData *p, protobuf_c_boolean *has) {
|
|
p->data = isc_buffer_base(buf);
|
|
p->len = isc_buffer_usedlength(buf);
|
|
*has = 1;
|
|
}
|
|
|
|
static void
|
|
setaddr(dns_dtmsg_t *dm, isc_sockaddr_t *sa, dns_transport_type_t transport,
|
|
ProtobufCBinaryData *addr, protobuf_c_boolean *has_addr, uint32_t *port,
|
|
protobuf_c_boolean *has_port) {
|
|
int family = isc_sockaddr_pf(sa);
|
|
|
|
if (family != AF_INET6 && family != AF_INET) {
|
|
return;
|
|
}
|
|
|
|
if (family == AF_INET6) {
|
|
dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET6;
|
|
addr->data = sa->type.sin6.sin6_addr.s6_addr;
|
|
addr->len = 16;
|
|
*port = ntohs(sa->type.sin6.sin6_port);
|
|
} else {
|
|
dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET;
|
|
addr->data = (uint8_t *)&sa->type.sin.sin_addr.s_addr;
|
|
addr->len = 4;
|
|
*port = ntohs(sa->type.sin.sin_port);
|
|
}
|
|
|
|
switch (transport) {
|
|
case DNS_TRANSPORT_TCP:
|
|
dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP;
|
|
break;
|
|
case DNS_TRANSPORT_UDP:
|
|
dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP;
|
|
break;
|
|
case DNS_TRANSPORT_TLS:
|
|
dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__DOT;
|
|
break;
|
|
case DNS_TRANSPORT_HTTP:
|
|
dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__DOH;
|
|
break;
|
|
case DNS_TRANSPORT_NONE:
|
|
case DNS_TRANSPORT_COUNT:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
dm->m.has_socket_protocol = 1;
|
|
dm->m.has_socket_family = 1;
|
|
*has_addr = 1;
|
|
*has_port = 1;
|
|
}
|
|
|
|
/*%
|
|
* Invoke dns_dt_reopen() and re-allow dnstap output file rolling.
|
|
*/
|
|
static void
|
|
perform_reopen(void *arg) {
|
|
dns_dtenv_t *env = (dns_dtenv_t *)arg;
|
|
|
|
REQUIRE(VALID_DTENV(env));
|
|
|
|
/* Roll output file. */
|
|
dns_dt_reopen(env, env->rolls);
|
|
|
|
/* Re-allow output file rolling. */
|
|
LOCK(&env->reopen_lock);
|
|
env->reopen_queued = false;
|
|
UNLOCK(&env->reopen_lock);
|
|
}
|
|
|
|
/*%
|
|
* Check whether a dnstap output file roll is due and if so, initiate it (the
|
|
* actual roll happens asynchronously).
|
|
*/
|
|
static void
|
|
check_file_size_and_maybe_reopen(dns_dtenv_t *env) {
|
|
struct stat statbuf;
|
|
|
|
/* If a loopmgr wasn't specified, abort. */
|
|
if (env->loop == NULL) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If an output file roll is not currently queued, check the current
|
|
* size of the output file to see whether a roll is needed. Return if
|
|
* it is not.
|
|
*/
|
|
LOCK(&env->reopen_lock);
|
|
if (env->reopen_queued || stat(env->path, &statbuf) < 0 ||
|
|
statbuf.st_size <= env->max_size)
|
|
{
|
|
goto unlock_and_return;
|
|
}
|
|
|
|
/*
|
|
* Send an event to roll the output file, then disallow output file
|
|
* rolling until the roll we queue is completed.
|
|
*/
|
|
isc_async_run(env->loop, perform_reopen, env);
|
|
env->reopen_queued = true;
|
|
|
|
unlock_and_return:
|
|
UNLOCK(&env->reopen_lock);
|
|
}
|
|
|
|
void
|
|
dns_dt_send(dns_view_t *view, dns_dtmsgtype_t msgtype, isc_sockaddr_t *qaddr,
|
|
isc_sockaddr_t *raddr, dns_transport_type_t transport,
|
|
isc_region_t *zone, isc_time_t *qtime, isc_time_t *rtime,
|
|
isc_buffer_t *buf) {
|
|
isc_time_t now, *t;
|
|
dns_dtmsg_t dm;
|
|
|
|
REQUIRE(DNS_VIEW_VALID(view));
|
|
|
|
if ((msgtype & view->dttypes) == 0) {
|
|
return;
|
|
}
|
|
|
|
if (view->dtenv == NULL) {
|
|
return;
|
|
}
|
|
|
|
REQUIRE(VALID_DTENV(view->dtenv));
|
|
|
|
if (view->dtenv->max_size != 0) {
|
|
check_file_size_and_maybe_reopen(view->dtenv);
|
|
}
|
|
|
|
now = isc_time_now();
|
|
t = &now;
|
|
|
|
init_msg(view->dtenv, &dm, dnstap_type(msgtype));
|
|
|
|
/* Query/response times */
|
|
switch (msgtype) {
|
|
case DNS_DTTYPE_AR:
|
|
case DNS_DTTYPE_CR:
|
|
case DNS_DTTYPE_RR:
|
|
case DNS_DTTYPE_FR:
|
|
case DNS_DTTYPE_SR:
|
|
case DNS_DTTYPE_TR:
|
|
case DNS_DTTYPE_UR:
|
|
if (rtime != NULL) {
|
|
t = rtime;
|
|
}
|
|
|
|
dm.m.response_time_sec = isc_time_seconds(t);
|
|
dm.m.has_response_time_sec = 1;
|
|
dm.m.response_time_nsec = isc_time_nanoseconds(t);
|
|
dm.m.has_response_time_nsec = 1;
|
|
|
|
/*
|
|
* Types RR and FR can fall through and get the query
|
|
* time set as well. Any other response type, break.
|
|
*/
|
|
if (msgtype != DNS_DTTYPE_RR && msgtype != DNS_DTTYPE_FR) {
|
|
break;
|
|
}
|
|
|
|
FALLTHROUGH;
|
|
case DNS_DTTYPE_AQ:
|
|
case DNS_DTTYPE_CQ:
|
|
case DNS_DTTYPE_FQ:
|
|
case DNS_DTTYPE_RQ:
|
|
case DNS_DTTYPE_SQ:
|
|
case DNS_DTTYPE_TQ:
|
|
case DNS_DTTYPE_UQ:
|
|
if (qtime != NULL) {
|
|
t = qtime;
|
|
}
|
|
|
|
dm.m.query_time_sec = isc_time_seconds(t);
|
|
dm.m.has_query_time_sec = 1;
|
|
dm.m.query_time_nsec = isc_time_nanoseconds(t);
|
|
dm.m.has_query_time_nsec = 1;
|
|
break;
|
|
default:
|
|
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP,
|
|
DNS_LOGMODULE_DNSTAP, ISC_LOG_ERROR,
|
|
"invalid dnstap message type %d", msgtype);
|
|
return;
|
|
}
|
|
|
|
/* Query and response messages */
|
|
if ((msgtype & DNS_DTTYPE_QUERY) != 0) {
|
|
cpbuf(buf, &dm.m.query_message, &dm.m.has_query_message);
|
|
} else if ((msgtype & DNS_DTTYPE_RESPONSE) != 0) {
|
|
cpbuf(buf, &dm.m.response_message, &dm.m.has_response_message);
|
|
}
|
|
|
|
/* Zone/bailiwick */
|
|
switch (msgtype) {
|
|
case DNS_DTTYPE_AR:
|
|
case DNS_DTTYPE_RQ:
|
|
case DNS_DTTYPE_RR:
|
|
case DNS_DTTYPE_FQ:
|
|
case DNS_DTTYPE_FR:
|
|
if (zone != NULL && zone->base != NULL && zone->length != 0) {
|
|
dm.m.query_zone.data = zone->base;
|
|
dm.m.query_zone.len = zone->length;
|
|
dm.m.has_query_zone = 1;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (qaddr != NULL) {
|
|
setaddr(&dm, qaddr, transport, &dm.m.query_address,
|
|
&dm.m.has_query_address, &dm.m.query_port,
|
|
&dm.m.has_query_port);
|
|
}
|
|
if (raddr != NULL) {
|
|
setaddr(&dm, raddr, transport, &dm.m.response_address,
|
|
&dm.m.has_response_address, &dm.m.response_port,
|
|
&dm.m.has_response_port);
|
|
}
|
|
|
|
if (pack_dt(&dm.d, &dm.buf, &dm.len) == ISC_R_SUCCESS) {
|
|
send_dt(view->dtenv, dm.buf, dm.len);
|
|
}
|
|
}
|
|
|
|
static isc_result_t
|
|
putstr(isc_buffer_t **b, const char *str) {
|
|
isc_result_t result;
|
|
|
|
result = isc_buffer_reserve(*b, strlen(str));
|
|
if (result != ISC_R_SUCCESS) {
|
|
return ISC_R_NOSPACE;
|
|
}
|
|
|
|
isc_buffer_putstr(*b, str);
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
static isc_result_t
|
|
putaddr(isc_buffer_t **b, isc_region_t *ip) {
|
|
char buf[64];
|
|
|
|
if (ip->length == 4) {
|
|
if (!inet_ntop(AF_INET, ip->base, buf, sizeof(buf))) {
|
|
return ISC_R_FAILURE;
|
|
}
|
|
} else if (ip->length == 16) {
|
|
if (!inet_ntop(AF_INET6, ip->base, buf, sizeof(buf))) {
|
|
return ISC_R_FAILURE;
|
|
}
|
|
} else {
|
|
return ISC_R_BADADDRESSFORM;
|
|
}
|
|
|
|
return putstr(b, buf);
|
|
}
|
|
|
|
static bool
|
|
dnstap_file(struct fstrm_reader *r) {
|
|
fstrm_res res;
|
|
const struct fstrm_control *control = NULL;
|
|
const uint8_t *rtype = NULL;
|
|
size_t dlen = strlen(DNSTAP_CONTENT_TYPE), rlen = 0;
|
|
size_t n = 0;
|
|
|
|
res = fstrm_reader_get_control(r, FSTRM_CONTROL_START, &control);
|
|
if (res != fstrm_res_success) {
|
|
return false;
|
|
}
|
|
|
|
res = fstrm_control_get_num_field_content_type(control, &n);
|
|
if (res != fstrm_res_success) {
|
|
return false;
|
|
}
|
|
if (n > 0) {
|
|
res = fstrm_control_get_field_content_type(control, 0, &rtype,
|
|
&rlen);
|
|
if (res != fstrm_res_success) {
|
|
return false;
|
|
}
|
|
|
|
if (rlen != dlen) {
|
|
return false;
|
|
}
|
|
|
|
if (memcmp(DNSTAP_CONTENT_TYPE, rtype, dlen) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dt_open(const char *filename, dns_dtmode_t mode, isc_mem_t *mctx,
|
|
dns_dthandle_t **handlep) {
|
|
isc_result_t result;
|
|
struct fstrm_file_options *fopt = NULL;
|
|
fstrm_res res;
|
|
dns_dthandle_t *handle;
|
|
|
|
REQUIRE(handlep != NULL && *handlep == NULL);
|
|
|
|
handle = isc_mem_get(mctx, sizeof(*handle));
|
|
|
|
handle->mode = mode;
|
|
handle->mctx = NULL;
|
|
|
|
switch (mode) {
|
|
case dns_dtmode_file:
|
|
fopt = fstrm_file_options_init();
|
|
if (fopt == NULL) {
|
|
CHECK(ISC_R_NOMEMORY);
|
|
}
|
|
|
|
fstrm_file_options_set_file_path(fopt, filename);
|
|
|
|
handle->reader = fstrm_file_reader_init(fopt, NULL);
|
|
if (handle->reader == NULL) {
|
|
CHECK(ISC_R_NOMEMORY);
|
|
}
|
|
|
|
res = fstrm_reader_open(handle->reader);
|
|
if (res != fstrm_res_success) {
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
|
|
if (!dnstap_file(handle->reader)) {
|
|
CHECK(DNS_R_BADDNSTAP);
|
|
}
|
|
break;
|
|
case dns_dtmode_unix:
|
|
result = ISC_R_NOTIMPLEMENTED;
|
|
goto cleanup;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
isc_mem_attach(mctx, &handle->mctx);
|
|
result = ISC_R_SUCCESS;
|
|
*handlep = handle;
|
|
handle = NULL;
|
|
|
|
cleanup:
|
|
if (result != ISC_R_SUCCESS && handle->reader != NULL) {
|
|
fstrm_reader_destroy(&handle->reader);
|
|
handle->reader = NULL;
|
|
}
|
|
if (fopt != NULL) {
|
|
fstrm_file_options_destroy(&fopt);
|
|
}
|
|
if (handle != NULL) {
|
|
isc_mem_put(mctx, handle, sizeof(*handle));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dt_getframe(dns_dthandle_t *handle, uint8_t **bufp, size_t *sizep) {
|
|
const uint8_t *data;
|
|
fstrm_res res;
|
|
|
|
REQUIRE(handle != NULL);
|
|
REQUIRE(bufp != NULL);
|
|
REQUIRE(sizep != NULL);
|
|
|
|
data = (const uint8_t *)*bufp;
|
|
|
|
res = fstrm_reader_read(handle->reader, &data, sizep);
|
|
switch (res) {
|
|
case fstrm_res_success:
|
|
if (data == NULL) {
|
|
return ISC_R_FAILURE;
|
|
}
|
|
*bufp = UNCONST(data);
|
|
return ISC_R_SUCCESS;
|
|
case fstrm_res_stop:
|
|
return ISC_R_NOMORE;
|
|
default:
|
|
return ISC_R_FAILURE;
|
|
}
|
|
}
|
|
|
|
void
|
|
dns_dt_close(dns_dthandle_t **handlep) {
|
|
dns_dthandle_t *handle;
|
|
|
|
REQUIRE(handlep != NULL && *handlep != NULL);
|
|
|
|
handle = *handlep;
|
|
*handlep = NULL;
|
|
|
|
if (handle->reader != NULL) {
|
|
fstrm_reader_destroy(&handle->reader);
|
|
handle->reader = NULL;
|
|
}
|
|
isc_mem_putanddetach(&handle->mctx, handle, sizeof(*handle));
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dt_parse(isc_mem_t *mctx, isc_region_t *src, dns_dtdata_t **destp) {
|
|
isc_result_t result;
|
|
Dnstap__Dnstap *frame;
|
|
Dnstap__Message *m;
|
|
dns_dtdata_t *d = NULL;
|
|
isc_buffer_t b;
|
|
|
|
REQUIRE(src != NULL);
|
|
REQUIRE(destp != NULL && *destp == NULL);
|
|
|
|
d = isc_mem_get(mctx, sizeof(*d));
|
|
*d = (dns_dtdata_t){ 0 };
|
|
|
|
isc_mem_attach(mctx, &d->mctx);
|
|
|
|
d->frame = dnstap__dnstap__unpack(NULL, src->length, src->base);
|
|
if (d->frame == NULL) {
|
|
CHECK(ISC_R_NOMEMORY);
|
|
}
|
|
|
|
frame = (Dnstap__Dnstap *)d->frame;
|
|
|
|
if (frame->type != DNSTAP__DNSTAP__TYPE__MESSAGE) {
|
|
CHECK(DNS_R_BADDNSTAP);
|
|
}
|
|
|
|
m = frame->message;
|
|
|
|
/* Message type */
|
|
switch (m->type) {
|
|
case DNSTAP__MESSAGE__TYPE__AUTH_QUERY:
|
|
d->type = DNS_DTTYPE_AQ;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE:
|
|
d->type = DNS_DTTYPE_AR;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY:
|
|
d->type = DNS_DTTYPE_CQ;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE:
|
|
d->type = DNS_DTTYPE_CR;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY:
|
|
d->type = DNS_DTTYPE_FQ;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE:
|
|
d->type = DNS_DTTYPE_FR;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY:
|
|
d->type = DNS_DTTYPE_RQ;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE:
|
|
d->type = DNS_DTTYPE_RR;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__STUB_QUERY:
|
|
d->type = DNS_DTTYPE_SQ;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__STUB_RESPONSE:
|
|
d->type = DNS_DTTYPE_SR;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__TOOL_QUERY:
|
|
d->type = DNS_DTTYPE_TQ;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE:
|
|
d->type = DNS_DTTYPE_TR;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__UPDATE_QUERY:
|
|
d->type = DNS_DTTYPE_UQ;
|
|
break;
|
|
case DNSTAP__MESSAGE__TYPE__UPDATE_RESPONSE:
|
|
d->type = DNS_DTTYPE_UR;
|
|
break;
|
|
default:
|
|
CHECK(DNS_R_BADDNSTAP);
|
|
}
|
|
|
|
/* Query? */
|
|
if ((d->type & DNS_DTTYPE_QUERY) != 0) {
|
|
d->query = true;
|
|
} else {
|
|
d->query = false;
|
|
}
|
|
|
|
/* Parse DNS message */
|
|
if (d->query && m->has_query_message) {
|
|
d->msgdata.base = m->query_message.data;
|
|
d->msgdata.length = m->query_message.len;
|
|
} else if (!d->query && m->has_response_message) {
|
|
d->msgdata.base = m->response_message.data;
|
|
d->msgdata.length = m->response_message.len;
|
|
}
|
|
|
|
isc_buffer_init(&b, d->msgdata.base, d->msgdata.length);
|
|
isc_buffer_add(&b, d->msgdata.length);
|
|
dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, &d->msg);
|
|
result = dns_message_parse(d->msg, &b, 0);
|
|
if (result != ISC_R_SUCCESS) {
|
|
if (result != DNS_R_RECOVERABLE) {
|
|
dns_message_detach(&d->msg);
|
|
}
|
|
result = ISC_R_SUCCESS;
|
|
}
|
|
|
|
/* Timestamp */
|
|
if (d->query) {
|
|
if (m->has_query_time_sec && m->has_query_time_nsec) {
|
|
isc_time_set(&d->qtime, m->query_time_sec,
|
|
m->query_time_nsec);
|
|
}
|
|
} else {
|
|
if (m->has_response_time_sec && m->has_response_time_nsec) {
|
|
isc_time_set(&d->rtime, m->response_time_sec,
|
|
m->response_time_nsec);
|
|
}
|
|
}
|
|
|
|
/* Peer address */
|
|
if (m->has_query_address) {
|
|
d->qaddr.base = m->query_address.data;
|
|
d->qaddr.length = m->query_address.len;
|
|
}
|
|
if (m->has_query_port) {
|
|
d->qport = m->query_port;
|
|
}
|
|
|
|
if (m->has_response_address) {
|
|
d->raddr.base = m->response_address.data;
|
|
d->raddr.length = m->response_address.len;
|
|
}
|
|
if (m->has_response_port) {
|
|
d->rport = m->response_port;
|
|
}
|
|
|
|
/* Socket protocol */
|
|
if (m->has_socket_protocol) {
|
|
const ProtobufCEnumValue *type =
|
|
protobuf_c_enum_descriptor_get_value(
|
|
&dnstap__socket_protocol__descriptor,
|
|
m->socket_protocol);
|
|
|
|
if (type != NULL) {
|
|
switch (type->value) {
|
|
case DNSTAP__SOCKET_PROTOCOL__DNSCryptUDP:
|
|
case DNSTAP__SOCKET_PROTOCOL__DOQ:
|
|
case DNSTAP__SOCKET_PROTOCOL__UDP:
|
|
d->transport = DNS_TRANSPORT_UDP;
|
|
break;
|
|
case DNSTAP__SOCKET_PROTOCOL__DNSCryptTCP:
|
|
case DNSTAP__SOCKET_PROTOCOL__TCP:
|
|
d->transport = DNS_TRANSPORT_TCP;
|
|
break;
|
|
case DNSTAP__SOCKET_PROTOCOL__DOT:
|
|
d->transport = DNS_TRANSPORT_TLS;
|
|
break;
|
|
case DNSTAP__SOCKET_PROTOCOL__DOH:
|
|
d->transport = DNS_TRANSPORT_HTTP;
|
|
break;
|
|
}
|
|
} else {
|
|
d->transport = DNS_TRANSPORT_UDP;
|
|
}
|
|
}
|
|
|
|
/* Query tuple */
|
|
if (d->msg != NULL) {
|
|
dns_name_t *name = NULL;
|
|
dns_rdataset_t *rdataset;
|
|
|
|
CHECK(dns_message_firstname(d->msg, DNS_SECTION_QUESTION));
|
|
dns_message_currentname(d->msg, DNS_SECTION_QUESTION, &name);
|
|
rdataset = ISC_LIST_HEAD(name->list);
|
|
|
|
dns_name_format(name, d->namebuf, sizeof(d->namebuf));
|
|
dns_rdatatype_format(rdataset->type, d->typebuf,
|
|
sizeof(d->typebuf));
|
|
dns_rdataclass_format(rdataset->rdclass, d->classbuf,
|
|
sizeof(d->classbuf));
|
|
}
|
|
|
|
*destp = d;
|
|
|
|
cleanup:
|
|
if (result != ISC_R_SUCCESS) {
|
|
dns_dtdata_free(&d);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_dt_datatotext(dns_dtdata_t *d, isc_buffer_t **dest) {
|
|
isc_result_t result;
|
|
char buf[100];
|
|
|
|
REQUIRE(d != NULL);
|
|
REQUIRE(dest != NULL && *dest != NULL);
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
|
|
/* Timestamp */
|
|
if (d->query && !isc_time_isepoch(&d->qtime)) {
|
|
isc_time_formattimestamp(&d->qtime, buf, sizeof(buf));
|
|
} else if (!d->query && !isc_time_isepoch(&d->rtime)) {
|
|
isc_time_formattimestamp(&d->rtime, buf, sizeof(buf));
|
|
}
|
|
|
|
if (buf[0] == '\0') {
|
|
CHECK(putstr(dest, "???\?-?\?-?? ??:??:??.??? "));
|
|
} else {
|
|
CHECK(putstr(dest, buf));
|
|
CHECK(putstr(dest, " "));
|
|
}
|
|
|
|
/* Type mnemonic */
|
|
switch (d->type) {
|
|
case DNS_DTTYPE_AQ:
|
|
CHECK(putstr(dest, "AQ "));
|
|
break;
|
|
case DNS_DTTYPE_AR:
|
|
CHECK(putstr(dest, "AR "));
|
|
break;
|
|
case DNS_DTTYPE_CQ:
|
|
CHECK(putstr(dest, "CQ "));
|
|
break;
|
|
case DNS_DTTYPE_CR:
|
|
CHECK(putstr(dest, "CR "));
|
|
break;
|
|
case DNS_DTTYPE_FQ:
|
|
CHECK(putstr(dest, "FQ "));
|
|
break;
|
|
case DNS_DTTYPE_FR:
|
|
CHECK(putstr(dest, "FR "));
|
|
break;
|
|
case DNS_DTTYPE_RQ:
|
|
CHECK(putstr(dest, "RQ "));
|
|
break;
|
|
case DNS_DTTYPE_RR:
|
|
CHECK(putstr(dest, "RR "));
|
|
break;
|
|
case DNS_DTTYPE_SQ:
|
|
CHECK(putstr(dest, "SQ "));
|
|
break;
|
|
case DNS_DTTYPE_SR:
|
|
CHECK(putstr(dest, "SR "));
|
|
break;
|
|
case DNS_DTTYPE_TQ:
|
|
CHECK(putstr(dest, "TQ "));
|
|
break;
|
|
case DNS_DTTYPE_TR:
|
|
CHECK(putstr(dest, "TR "));
|
|
break;
|
|
case DNS_DTTYPE_UQ:
|
|
CHECK(putstr(dest, "UQ "));
|
|
break;
|
|
case DNS_DTTYPE_UR:
|
|
CHECK(putstr(dest, "UR "));
|
|
break;
|
|
default:
|
|
return DNS_R_BADDNSTAP;
|
|
}
|
|
|
|
/* Query and response addresses */
|
|
if (d->qaddr.length != 0) {
|
|
CHECK(putaddr(dest, &d->qaddr));
|
|
snprintf(buf, sizeof(buf), ":%u", d->qport);
|
|
CHECK(putstr(dest, buf));
|
|
} else {
|
|
CHECK(putstr(dest, "?"));
|
|
}
|
|
if ((d->type & DNS_DTTYPE_QUERY) != 0) {
|
|
CHECK(putstr(dest, " -> "));
|
|
} else {
|
|
CHECK(putstr(dest, " <- "));
|
|
}
|
|
if (d->raddr.length != 0) {
|
|
CHECK(putaddr(dest, &d->raddr));
|
|
snprintf(buf, sizeof(buf), ":%u", d->rport);
|
|
CHECK(putstr(dest, buf));
|
|
} else {
|
|
CHECK(putstr(dest, "?"));
|
|
}
|
|
|
|
CHECK(putstr(dest, " "));
|
|
|
|
/* Protocol */
|
|
switch (d->transport) {
|
|
case DNS_TRANSPORT_NONE:
|
|
CHECK(putstr(dest, "NUL "));
|
|
break;
|
|
case DNS_TRANSPORT_UDP:
|
|
CHECK(putstr(dest, "UDP "));
|
|
break;
|
|
case DNS_TRANSPORT_TCP:
|
|
CHECK(putstr(dest, "TCP "));
|
|
break;
|
|
case DNS_TRANSPORT_TLS:
|
|
CHECK(putstr(dest, "DOT "));
|
|
break;
|
|
case DNS_TRANSPORT_HTTP:
|
|
CHECK(putstr(dest, "DOH "));
|
|
break;
|
|
case DNS_TRANSPORT_COUNT:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
/* Message size */
|
|
if (d->msgdata.base != NULL) {
|
|
snprintf(buf, sizeof(buf), "%zub ", (size_t)d->msgdata.length);
|
|
CHECK(putstr(dest, buf));
|
|
} else {
|
|
CHECK(putstr(dest, "0b "));
|
|
}
|
|
|
|
/* Query tuple */
|
|
if (d->namebuf[0] == '\0') {
|
|
CHECK(putstr(dest, "?/"));
|
|
} else {
|
|
CHECK(putstr(dest, d->namebuf));
|
|
CHECK(putstr(dest, "/"));
|
|
}
|
|
|
|
if (d->classbuf[0] == '\0') {
|
|
CHECK(putstr(dest, "?/"));
|
|
} else {
|
|
CHECK(putstr(dest, d->classbuf));
|
|
CHECK(putstr(dest, "/"));
|
|
}
|
|
|
|
if (d->typebuf[0] == '\0') {
|
|
CHECK(putstr(dest, "?"));
|
|
} else {
|
|
CHECK(putstr(dest, d->typebuf));
|
|
}
|
|
|
|
CHECK(isc_buffer_reserve(*dest, 1));
|
|
isc_buffer_putuint8(*dest, 0);
|
|
|
|
cleanup:
|
|
return result;
|
|
}
|
|
|
|
void
|
|
dns_dtdata_free(dns_dtdata_t **dp) {
|
|
dns_dtdata_t *d;
|
|
|
|
REQUIRE(dp != NULL && *dp != NULL);
|
|
|
|
d = *dp;
|
|
*dp = NULL;
|
|
|
|
if (d->msg != NULL) {
|
|
dns_message_detach(&d->msg);
|
|
}
|
|
if (d->frame != NULL) {
|
|
dnstap__dnstap__free_unpacked(d->frame, NULL);
|
|
}
|
|
|
|
isc_mem_putanddetach(&d->mctx, d, sizeof(*d));
|
|
}
|