diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-03-13 07:54:12 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-03-13 07:54:12 +0000 |
commit | 4754ed45b607e82450a5e31fea1da3ba61433b04 (patch) | |
tree | 3554490bdc003e6004f605abe41929cdf98b0651 /src/output/dnssim/common.c | |
parent | Initial commit. (diff) | |
download | dnsjit-4754ed45b607e82450a5e31fea1da3ba61433b04.tar.xz dnsjit-4754ed45b607e82450a5e31fea1da3ba61433b04.zip |
Adding upstream version 1.1.0+debian.upstream/1.1.0+debian
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/output/dnssim/common.c')
-rw-r--r-- | src/output/dnssim/common.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/src/output/dnssim/common.c b/src/output/dnssim/common.c new file mode 100644 index 0000000..e170aec --- /dev/null +++ b/src/output/dnssim/common.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2019-2020, CZ.NIC, z.s.p.o. + * All rights reserved. + * + * This file is part of dnsjit. + * + * dnsjit is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * dnsjit is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with dnsjit. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "output/dnssim.h" +#include "output/dnssim/internal.h" +#include "output/dnssim/ll.h" +#include "core/assert.h" + +#include <string.h> + +#define MAX_LABELS 127 + +static core_log_t _log = LOG_T_INIT("output.dnssim"); + +static void _close_request(_output_dnssim_request_t* req); + +static void _on_request_timeout(uv_timer_t* handle) +{ + _close_request((_output_dnssim_request_t*)handle->data); +} + +static ssize_t parse_qsection(core_object_dns_t* dns) +{ + core_object_dns_q_t q; + static core_object_dns_label_t labels[MAX_LABELS]; + const uint8_t* start; + int i; + int ret; + + if (!dns || !dns->have_qdcount) + return -1; + + start = dns->at; + + for (i = 0; i < dns->qdcount; i++) { + ret = core_object_dns_parse_q(dns, &q, labels, MAX_LABELS); + if (ret < 0) + return -1; + } + + return (dns->at - start); +} + +int _output_dnssim_answers_request(_output_dnssim_request_t* req, core_object_dns_t* response) +{ + const uint8_t* question; + ssize_t len; + + if (!response->have_id || !response->have_qdcount) + return _ERR_MALFORMED; + + if (req->dns_q->id != response->id) + return _ERR_MSGID; + + if (req->dns_q->qdcount != response->qdcount) + return _ERR_QUESTION; + + question = response->at; + len = parse_qsection(response); + + if (req->question_len != len) + return _ERR_QUESTION; + + if (memcmp(req->question, question, len) != 0) + return _ERR_QUESTION; + + return 0; +} + +void _output_dnssim_create_request(output_dnssim_t* self, _output_dnssim_client_t* client, core_object_payload_t* payload) +{ + int ret; + _output_dnssim_request_t* req; + mlassert_self(); + lassert(client, "client is nil"); + lassert(payload, "payload is nil"); + + lfatal_oom(req = calloc(1, sizeof(_output_dnssim_request_t))); + req->dnssim = self; + req->client = client; + req->payload = payload; + req->dns_q = core_object_dns_new(); + req->dns_q->obj_prev = (core_object_t*)req->payload; + req->dnssim->ongoing++; + req->state = _OUTPUT_DNSSIM_REQ_ONGOING; + req->stats = self->stats_current; + + ret = core_object_dns_parse_header(req->dns_q); + if (ret != 0) { + ldebug("discarded malformed dns query: couldn't parse header"); + goto failure; + } + + req->question = req->dns_q->at; + req->question_len = parse_qsection(req->dns_q); + if (req->question_len < 0) { + ldebug("discarded malformed dns query: invalid question"); + goto failure; + } + + req->dnssim->stats_sum->requests++; + req->stats->requests++; + + switch (_self->transport) { + case OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY: + case OUTPUT_DNSSIM_TRANSPORT_UDP: + ret = _output_dnssim_create_query_udp(self, req); + break; + case OUTPUT_DNSSIM_TRANSPORT_TCP: + ret = _output_dnssim_create_query_tcp(self, req); + break; + case OUTPUT_DNSSIM_TRANSPORT_TLS: +#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION + ret = _output_dnssim_create_query_tls(self, req); +#else + lfatal(DNSSIM_MIN_GNUTLS_ERRORMSG); +#endif + break; + case OUTPUT_DNSSIM_TRANSPORT_HTTPS2: +#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION + ret = _output_dnssim_create_query_https2(self, req); +#else + lfatal(DNSSIM_MIN_GNUTLS_ERRORMSG); +#endif + break; + default: + lfatal("unsupported dnssim transport"); + break; + } + if (ret < 0) { + goto failure; + } + + req->created_at = uv_now(&_self->loop); + req->ended_at = req->created_at + self->timeout_ms; + lfatal_oom(req->timer = malloc(sizeof(uv_timer_t))); + uv_timer_init(&_self->loop, req->timer); + req->timer->data = req; + uv_timer_start(req->timer, _on_request_timeout, self->timeout_ms, 0); + + return; +failure: + self->discarded++; + _close_request(req); + return; +} + +/* Bind before connect to be able to send from different source IPs. */ +int _output_dnssim_bind_before_connect(output_dnssim_t* self, uv_handle_t* handle) +{ + mlassert_self(); + lassert(handle, "handle is nil"); + + if (_self->source != NULL) { + struct sockaddr* addr = (struct sockaddr*)&_self->source->addr; + struct sockaddr* dest = (struct sockaddr*)&_self->target; + int ret = -1; + if (addr->sa_family != dest->sa_family) { + lfatal("failed to bind: source/desitnation address family mismatch"); + } + switch (handle->type) { + case UV_UDP: + ret = uv_udp_bind((uv_udp_t*)handle, addr, 0); + break; + case UV_TCP: + ret = uv_tcp_bind((uv_tcp_t*)handle, addr, 0); + break; + default: + lfatal("failed to bind: unsupported handle type"); + break; + } + if (ret < 0) { + /* This typically happens when we run out of file descriptors. + * Quit to prevent skewed results or unexpected behaviour. */ + lfatal("failed to bind: %s", uv_strerror(ret)); + return ret; + } + _self->source = _self->source->next; + } + return 0; +} + +void _output_dnssim_maybe_free_request(_output_dnssim_request_t* req) +{ + mlassert(req, "req is nil"); + + if (req->qry == NULL && req->timer == NULL) { + if (req->dnssim->free_after_use) { + core_object_payload_free(req->payload); + } + core_object_dns_free(req->dns_q); + free(req); + } +} + +static void _close_query(_output_dnssim_query_t* qry) +{ + mlassert(qry, "qry is nil"); + + switch (qry->transport) { + case OUTPUT_DNSSIM_TRANSPORT_UDP: + _output_dnssim_close_query_udp((_output_dnssim_query_udp_t*)qry); + break; + case OUTPUT_DNSSIM_TRANSPORT_TCP: + _output_dnssim_close_query_tcp((_output_dnssim_query_tcp_t*)qry); + break; + case OUTPUT_DNSSIM_TRANSPORT_TLS: +#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION + _output_dnssim_close_query_tls((_output_dnssim_query_tcp_t*)qry); +#else + mlfatal(DNSSIM_MIN_GNUTLS_ERRORMSG); +#endif + break; + case OUTPUT_DNSSIM_TRANSPORT_HTTPS2: +#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION + _output_dnssim_close_query_https2((_output_dnssim_query_tcp_t*)qry); +#else + mlfatal(DNSSIM_MIN_GNUTLS_ERRORMSG); +#endif + break; + default: + mlfatal("invalid query transport"); + break; + } +} + +static void _on_request_timer_closed(uv_handle_t* handle) +{ + _output_dnssim_request_t* req = (_output_dnssim_request_t*)handle->data; + mlassert(req, "req is nil"); + free(handle); + req->timer = NULL; + _output_dnssim_maybe_free_request(req); +} + +static void _close_request(_output_dnssim_request_t* req) +{ + if (req == NULL || req->state == _OUTPUT_DNSSIM_REQ_CLOSING) + return; + mlassert(req->state == _OUTPUT_DNSSIM_REQ_ONGOING, "request to be closed must be ongoing"); + req->state = _OUTPUT_DNSSIM_REQ_CLOSING; + req->dnssim->ongoing--; + + /* Calculate latency. */ + uint64_t latency; + req->ended_at = uv_now(&((_output_dnssim_t*)req->dnssim)->loop); + latency = req->ended_at - req->created_at; + if (latency > req->dnssim->timeout_ms) { + req->ended_at = req->created_at + req->dnssim->timeout_ms; + latency = req->dnssim->timeout_ms; + } + req->stats->latency[latency]++; + req->dnssim->stats_sum->latency[latency]++; + + if (req->timer != NULL) { + uv_timer_stop(req->timer); + uv_close((uv_handle_t*)req->timer, _on_request_timer_closed); + } + + /* Finish any queries in flight. */ + _output_dnssim_query_t* qry = req->qry; + if (qry != NULL) + _close_query(qry); + + _output_dnssim_maybe_free_request(req); +} + +void _output_dnssim_request_answered(_output_dnssim_request_t* req, core_object_dns_t* msg) +{ + mlassert(req, "req is nil"); + mlassert(msg, "msg is nil"); + + req->dnssim->stats_sum->answers++; + req->stats->answers++; + + switch (msg->rcode) { + case CORE_OBJECT_DNS_RCODE_NOERROR: + req->dnssim->stats_sum->rcode_noerror++; + req->stats->rcode_noerror++; + break; + case CORE_OBJECT_DNS_RCODE_FORMERR: + req->dnssim->stats_sum->rcode_formerr++; + req->stats->rcode_formerr++; + break; + case CORE_OBJECT_DNS_RCODE_SERVFAIL: + req->dnssim->stats_sum->rcode_servfail++; + req->stats->rcode_servfail++; + break; + case CORE_OBJECT_DNS_RCODE_NXDOMAIN: + req->dnssim->stats_sum->rcode_nxdomain++; + req->stats->rcode_nxdomain++; + break; + case CORE_OBJECT_DNS_RCODE_NOTIMP: + req->dnssim->stats_sum->rcode_notimp++; + req->stats->rcode_notimp++; + break; + case CORE_OBJECT_DNS_RCODE_REFUSED: + req->dnssim->stats_sum->rcode_refused++; + req->stats->rcode_refused++; + break; + case CORE_OBJECT_DNS_RCODE_YXDOMAIN: + req->dnssim->stats_sum->rcode_yxdomain++; + req->stats->rcode_yxdomain++; + break; + case CORE_OBJECT_DNS_RCODE_YXRRSET: + req->dnssim->stats_sum->rcode_yxrrset++; + req->stats->rcode_yxrrset++; + break; + case CORE_OBJECT_DNS_RCODE_NXRRSET: + req->dnssim->stats_sum->rcode_nxrrset++; + req->stats->rcode_nxrrset++; + break; + case CORE_OBJECT_DNS_RCODE_NOTAUTH: + req->dnssim->stats_sum->rcode_notauth++; + req->stats->rcode_notauth++; + break; + case CORE_OBJECT_DNS_RCODE_NOTZONE: + req->dnssim->stats_sum->rcode_notzone++; + req->stats->rcode_notzone++; + break; + case CORE_OBJECT_DNS_RCODE_BADVERS: + req->dnssim->stats_sum->rcode_badvers++; + req->stats->rcode_badvers++; + break; + case CORE_OBJECT_DNS_RCODE_BADKEY: + req->dnssim->stats_sum->rcode_badkey++; + req->stats->rcode_badkey++; + break; + case CORE_OBJECT_DNS_RCODE_BADTIME: + req->dnssim->stats_sum->rcode_badtime++; + req->stats->rcode_badtime++; + break; + case CORE_OBJECT_DNS_RCODE_BADMODE: + req->dnssim->stats_sum->rcode_badmode++; + req->stats->rcode_badmode++; + break; + case CORE_OBJECT_DNS_RCODE_BADNAME: + req->dnssim->stats_sum->rcode_badname++; + req->stats->rcode_badname++; + break; + case CORE_OBJECT_DNS_RCODE_BADALG: + req->dnssim->stats_sum->rcode_badalg++; + req->stats->rcode_badalg++; + break; + case CORE_OBJECT_DNS_RCODE_BADTRUNC: + req->dnssim->stats_sum->rcode_badtrunc++; + req->stats->rcode_badtrunc++; + break; + case CORE_OBJECT_DNS_RCODE_BADCOOKIE: + req->dnssim->stats_sum->rcode_badcookie++; + req->stats->rcode_badcookie++; + break; + default: + req->dnssim->stats_sum->rcode_other++; + req->stats->rcode_other++; + } + + _close_request(req); +} + +void _output_dnssim_on_uv_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) +{ + mlfatal_oom(buf->base = malloc(suggested_size)); + buf->len = suggested_size; +} |