summaryrefslogtreecommitdiffstats
path: root/src/output/dnssim
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--include/dnsjit/filter/copy.h (renamed from src/output/dnssim.h)13
-rw-r--r--src/output/dnssim.c502
-rw-r--r--src/output/dnssim.hh123
-rw-r--r--src/output/dnssim.lua433
-rw-r--r--src/output/dnssim/CHANGELOG.md16
-rw-r--r--src/output/dnssim/common.c384
-rw-r--r--src/output/dnssim/connection.c471
-rw-r--r--src/output/dnssim/https2.c592
-rw-r--r--src/output/dnssim/internal.h343
-rw-r--r--src/output/dnssim/ll.h83
-rw-r--r--src/output/dnssim/tcp.c356
-rw-r--r--src/output/dnssim/tls.c475
-rw-r--r--src/output/dnssim/udp.c156
13 files changed, 6 insertions, 3941 deletions
diff --git a/src/output/dnssim.h b/include/dnsjit/filter/copy.h
index f843000..d65a552 100644
--- a/src/output/dnssim.h
+++ b/include/dnsjit/filter/copy.h
@@ -18,14 +18,13 @@
* along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "core/log.h"
-#include "core/receiver.h"
+#include <dnsjit/core/log.h>
+#include <dnsjit/core/object.h>
+#include <dnsjit/core/receiver.h>
-#ifndef __dnsjit_output_dnssim_h
-#define __dnsjit_output_dnssim_h
+#ifndef __dnsjit_filter_copy_h
+#define __dnsjit_filter_copy_h
-#include <stdbool.h>
-
-#include "output/dnssim.hh"
+#include <dnsjit/filter/copy.hh>
#endif
diff --git a/src/output/dnssim.c b/src/output/dnssim.c
deleted file mode 100644
index acd0a05..0000000
--- a/src/output/dnssim.c
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * 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 "core/object/ip.h"
-#include "core/object/ip6.h"
-
-#include <gnutls/gnutls.h>
-#include <string.h>
-
-static core_log_t _log = LOG_T_INIT("output.dnssim");
-static output_dnssim_t _defaults = { LOG_T_INIT_OBJ("output.dnssim") };
-
-static uint64_t _now_ms()
-{
-#if HAVE_CLOCK_NANOSLEEP
- struct timespec ts;
- uint64_t now_ms;
- if (clock_gettime(CLOCK_REALTIME, &ts)) {
- mlfatal("clock_gettime()");
- }
- now_ms = ts.tv_sec * 1000;
- now_ms += ts.tv_nsec / 1000000;
- return now_ms;
-#else
- mlfatal("clock_gettime() not available");
- return 0;
-#endif
-}
-
-core_log_t* output_dnssim_log()
-{
- return &_log;
-}
-
-output_dnssim_t* output_dnssim_new(size_t max_clients)
-{
- output_dnssim_t* self;
- int ret, i;
-
- mlfatal_oom(self = calloc(1, sizeof(_output_dnssim_t)));
- *self = _defaults;
- self->handshake_timeout_ms = 5000;
- self->idle_timeout_ms = 10000;
- output_dnssim_timeout_ms(self, 2000);
-
- _self->source = NULL;
- _self->transport = OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY;
- _self->h2_zero_out_msgid = false;
-
- self->max_clients = max_clients;
- lfatal_oom(_self->client_arr = calloc(max_clients, sizeof(_output_dnssim_client_t)));
-
- for (i = 0; i < max_clients; ++i) {
- _self->client_arr[i].dnssim = self;
- }
-
- ret = gnutls_certificate_allocate_credentials(&_self->tls_cred);
- if (ret < 0)
- lfatal("failed to allocated TLS credentials (%s)", gnutls_strerror(ret));
-
- ret = uv_loop_init(&_self->loop);
- if (ret < 0)
- lfatal("failed to initialize uv_loop (%s)", uv_strerror(ret));
- ldebug("initialized uv_loop");
-
- return self;
-}
-
-void output_dnssim_free(output_dnssim_t* self)
-{
- mlassert_self();
- int ret, i;
- _output_dnssim_source_t* source;
- _output_dnssim_source_t* first = _self->source;
- output_dnssim_stats_t* stats_prev;
-
- free(self->stats_sum->latency);
- free(self->stats_sum);
- do {
- stats_prev = self->stats_current->prev;
- free(self->stats_current->latency);
- free(self->stats_current);
- self->stats_current = stats_prev;
- } while (self->stats_current != NULL);
-
- if (_self->source != NULL) {
- // free cilcular linked list
- do {
- source = _self->source->next;
- free(_self->source);
- _self->source = source;
- } while (_self->source != first);
- }
-
- for (i = 0; i < self->max_clients; ++i) {
- if (_self->client_arr[i].tls_ticket.size != 0) {
- gnutls_free(_self->client_arr[i].tls_ticket.data);
- }
- }
- free(_self->client_arr);
-
- ret = uv_loop_close(&_self->loop);
- if (ret < 0) {
- lcritical("failed to close uv_loop (%s)", uv_strerror(ret));
- } else {
- ldebug("closed uv_loop");
- }
-
- gnutls_certificate_free_credentials(_self->tls_cred);
- if (_self->tls_priority != NULL) {
- gnutls_priority_deinit(*_self->tls_priority);
- free(_self->tls_priority);
- }
-
- free(self);
-}
-
-void output_dnssim_log_name(output_dnssim_t* self, const char* name)
-{
- mlassert_self();
- lassert(name, "name is nil");
-
- strncpy(self->_log.name, name, sizeof(self->_log.name) - 1);
- self->_log.name[sizeof(self->_log.name) - 1] = 0;
- self->_log.is_obj = false;
-}
-
-static uint32_t _extract_client(const core_object_t* obj)
-{
- uint32_t client;
- uint8_t* ip;
-
- switch (obj->obj_type) {
- case CORE_OBJECT_IP:
- ip = ((core_object_ip_t*)obj)->dst;
- break;
- case CORE_OBJECT_IP6:
- ip = ((core_object_ip6_t*)obj)->dst;
- break;
- default:
- return -1;
- }
-
- memcpy(&client, ip, sizeof(client));
- return client;
-}
-
-static void _receive(output_dnssim_t* self, const core_object_t* obj)
-{
- mlassert_self();
- core_object_t* current = (core_object_t*)obj;
- core_object_payload_t* payload;
- uint32_t client;
-
- self->processed++;
-
- /* get payload from packet */
- for (;;) {
- if (current->obj_type == CORE_OBJECT_PAYLOAD) {
- payload = (core_object_payload_t*)current;
- break;
- }
- if (current->obj_prev == NULL) {
- self->discarded++;
- lwarning("packet discarded (missing payload object)");
- return;
- }
- current = (core_object_t*)current->obj_prev;
- }
-
- /* extract client information from IP/IP6 layer */
- for (;;) {
- if (current->obj_type == CORE_OBJECT_IP || current->obj_type == CORE_OBJECT_IP6) {
- client = _extract_client(current);
- break;
- }
- if (current->obj_prev == NULL) {
- self->discarded++;
- lwarning("packet discarded (missing ip/ip6 object)");
- return;
- }
- current = (core_object_t*)current->obj_prev;
- }
-
- if (self->free_after_use) {
- /* free all objects except payload */
- current = (core_object_t*)obj;
- core_object_t* parent = current;
- while (current != NULL) {
- parent = current;
- current = (core_object_t*)current->obj_prev;
- if (parent->obj_type != CORE_OBJECT_PAYLOAD) {
- core_object_free(parent);
- }
- }
- }
-
- if (_self->h2_zero_out_msgid) {
- lassert(_self->transport == OUTPUT_DNSSIM_TRANSPORT_HTTPS2, "must use HTTP/2 to zero-out msgid");
- if (payload->len < 2) {
- self->discarded++;
- lwarning("packet discarded (payload len < 2)");
- return;
- }
- uint8_t* data = (uint8_t*)payload->payload;
- data[0] = 0x00;
- data[1] = 0x00;
- }
-
- if (client >= self->max_clients) {
- self->discarded++;
- lwarning("packet discarded (client exceeded max_clients)");
- return;
- }
-
- ldebug("client(c): %d", client);
- _output_dnssim_create_request(self, &_self->client_arr[client], payload);
-}
-
-core_receiver_t output_dnssim_receiver()
-{
- return (core_receiver_t)_receive;
-}
-
-void output_dnssim_set_transport(output_dnssim_t* self, output_dnssim_transport_t tr)
-{
- mlassert_self();
-
- switch (tr) {
- case OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY:
- lnotice("transport set to UDP (no TCP fallback)");
- break;
- case OUTPUT_DNSSIM_TRANSPORT_TCP:
- lnotice("transport set to TCP");
- break;
- case OUTPUT_DNSSIM_TRANSPORT_TLS:
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- lnotice("transport set to TLS");
-#else
- lfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- break;
- case OUTPUT_DNSSIM_TRANSPORT_HTTPS2:
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- lnotice("transport set to HTTP/2 over TLS");
- if (&_self->h2_uri_authority[0])
- lnotice("set uri authority to: %s", _self->h2_uri_authority);
-#else
- lfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- break;
- case OUTPUT_DNSSIM_TRANSPORT_UDP:
- lfatal("UDP transport with TCP fallback is not supported yet.");
- break;
- default:
- lfatal("unknown or unsupported transport");
- break;
- }
-
- _self->transport = tr;
-}
-
-int output_dnssim_target(output_dnssim_t* self, const char* ip, uint16_t port)
-{
- int ret;
- mlassert_self();
- lassert(ip, "ip is nil");
- lassert(port, "port is nil");
-
- ret = uv_ip6_addr(ip, port, (struct sockaddr_in6*)&_self->target);
- if (ret != 0) {
- ret = uv_ip4_addr(ip, port, (struct sockaddr_in*)&_self->target);
- if (ret != 0) {
- lfatal("failed to parse IPv4 or IPv6 from \"%s\"", ip);
- } else {
- ret = snprintf(_self->h2_uri_authority, _MAX_URI_LEN, "%s:%d", ip, port);
- }
- } else {
- ret = snprintf(_self->h2_uri_authority, _MAX_URI_LEN, "[%s]:%d", ip, port);
- }
-
- if (ret > 0) {
- if (_self->transport == OUTPUT_DNSSIM_TRANSPORT_HTTPS2)
- lnotice("set uri authority to: %s", _self->h2_uri_authority);
- } else {
- _self->h2_uri_authority[0] = '\0';
- if (_self->transport == OUTPUT_DNSSIM_TRANSPORT_HTTPS2)
- lfatal("failed to set authority");
- }
-
- lnotice("set target to %s port %d", ip, port);
- return 0;
-}
-
-int output_dnssim_bind(output_dnssim_t* self, const char* ip)
-{
- int ret;
- mlassert_self();
- lassert(ip, "ip is nil");
-
- _output_dnssim_source_t* source;
- lfatal_oom(source = malloc(sizeof(_output_dnssim_source_t)));
-
- ret = uv_ip6_addr(ip, 0, (struct sockaddr_in6*)&source->addr);
- if (ret != 0) {
- ret = uv_ip4_addr(ip, 0, (struct sockaddr_in*)&source->addr);
- if (ret != 0) {
- lfatal("failed to parse IPv4 or IPv6 from \"%s\"", ip);
- }
- }
-
- if (_self->source == NULL) {
- source->next = source;
- _self->source = source;
- } else {
- source->next = _self->source->next;
- _self->source->next = source;
- }
-
- lnotice("bind to source address %s", ip);
- return 0;
-}
-
-int output_dnssim_tls_priority(output_dnssim_t* self, const char* priority)
-{
- mlassert_self();
- lassert(priority, "priority is nil");
-
- if (_self->tls_priority != NULL) {
- gnutls_priority_deinit(*_self->tls_priority);
- free(_self->tls_priority);
- }
- lfatal_oom(_self->tls_priority = malloc(sizeof(gnutls_priority_t)));
-
- int ret = gnutls_priority_init(_self->tls_priority, priority, NULL);
- if (ret < 0) {
- lfatal("failed to initialize TLS priority cache: %s", gnutls_strerror(ret));
- } else {
- lnotice("GnuTLS priority set: %s", priority);
- }
-
- return 0;
-}
-
-int output_dnssim_run_nowait(output_dnssim_t* self)
-{
- mlassert_self();
-
- return uv_run(&_self->loop, UV_RUN_NOWAIT);
-}
-
-void output_dnssim_timeout_ms(output_dnssim_t* self, uint64_t timeout_ms)
-{
- mlassert_self();
- lassert(timeout_ms > 0, "timeout must be greater than 0");
-
- if (self->stats_sum != NULL) {
- free(self->stats_sum->latency);
- free(self->stats_sum);
- self->stats_sum = 0;
- }
- if (self->stats_current != NULL) {
- output_dnssim_stats_t* stats_prev;
- do {
- stats_prev = self->stats_current->prev;
- free(self->stats_current->latency);
- free(self->stats_current);
- self->stats_current = stats_prev;
- } while (self->stats_current != NULL);
- }
-
- self->timeout_ms = timeout_ms;
-
- lfatal_oom(self->stats_sum = calloc(1, sizeof(output_dnssim_stats_t)));
- lfatal_oom(self->stats_sum->latency = calloc(self->timeout_ms + 1, sizeof(uint64_t)));
-
- lfatal_oom(self->stats_current = calloc(1, sizeof(output_dnssim_stats_t)));
- lfatal_oom(self->stats_current->latency = calloc(self->timeout_ms + 1, sizeof(uint64_t)));
-
- self->stats_first = self->stats_current;
-}
-
-void output_dnssim_h2_uri_path(output_dnssim_t* self, const char* uri_path)
-{
- mlassert_self();
- lassert(uri_path, "uri_path is nil");
- lassert(strlen(uri_path) < _MAX_URI_LEN, "uri_path too long");
-
- strncpy(_self->h2_uri_path, uri_path, _MAX_URI_LEN - 1);
- _self->h2_uri_path[_MAX_URI_LEN - 1] = 0;
- lnotice("http2: set uri path to: %s", _self->h2_uri_path);
-}
-
-void output_dnssim_h2_method(output_dnssim_t* self, const char* method)
-{
- mlassert_self();
- lassert(method, "method is nil");
-
- if (strcmp("GET", method) == 0) {
- _self->h2_method = OUTPUT_DNSSIM_H2_GET;
- } else if (strcmp("POST", method) == 0) {
- _self->h2_method = OUTPUT_DNSSIM_H2_POST;
- } else {
- lfatal("http2: unsupported method: \"%s\"", method);
- }
-
- lnotice("http2: set method to %s", method);
-}
-
-void output_dnssim_h2_zero_out_msgid(output_dnssim_t* self, bool zero_out_msgid)
-{
- mlassert_self();
-
- if (zero_out_msgid) {
- lassert(_self->transport == OUTPUT_DNSSIM_TRANSPORT_HTTPS2, "transport must be set to HTTP/2 to set zero_out_msgid");
- _self->h2_zero_out_msgid = zero_out_msgid;
- }
-}
-
-static void _on_stats_timer_tick(uv_timer_t* handle)
-{
- uint64_t now_ms = _now_ms();
- output_dnssim_t* self;
- mlassert(handle, "handle is nil");
- self = (output_dnssim_t*)handle->data;
- mlassert_self();
- lassert(self->stats_sum, "stats_sum is nil");
- lassert(self->stats_current, "stats_current is nil");
-
- lnotice("total processed:%10ld; answers:%10ld; discarded:%10ld; ongoing:%10ld",
- self->processed, self->stats_sum->answers, self->discarded, self->ongoing);
-
- output_dnssim_stats_t* stats_next;
- lfatal_oom(stats_next = calloc(1, sizeof(output_dnssim_stats_t)));
- lfatal_oom(stats_next->latency = calloc(self->timeout_ms + 1, sizeof(uint64_t)));
-
- self->stats_current->until_ms = now_ms;
- stats_next->since_ms = now_ms;
- stats_next->conn_active = self->stats_current->conn_active;
-
- stats_next->ongoing = self->ongoing;
- stats_next->prev = self->stats_current;
- self->stats_current->next = stats_next;
- self->stats_current = stats_next;
-}
-
-void output_dnssim_stats_collect(output_dnssim_t* self, uint64_t interval_ms)
-{
- uint64_t now_ms = _now_ms();
- mlassert_self();
- lassert(self->stats_sum, "stats_sum is nil");
- lassert(self->stats_current, "stats_current is nil");
-
- if (self->stats_interval_ms != 0) {
- lfatal("statistics collection has already started!");
- }
- self->stats_interval_ms = interval_ms;
-
- self->stats_sum->since_ms = now_ms;
- self->stats_current->since_ms = now_ms;
-
- _self->stats_timer.data = (void*)self;
- uv_timer_init(&_self->loop, &_self->stats_timer);
- uv_timer_start(&_self->stats_timer, _on_stats_timer_tick, interval_ms, interval_ms);
-}
-
-void output_dnssim_stats_finish(output_dnssim_t* self)
-{
- uint64_t now_ms = _now_ms();
- mlassert_self();
- lassert(self->stats_sum, "stats_sum is nil");
- lassert(self->stats_current, "stats_current is nil");
-
- self->stats_sum->until_ms = now_ms;
- self->stats_current->until_ms = now_ms;
-
- uv_timer_stop(&_self->stats_timer);
- uv_close((uv_handle_t*)&_self->stats_timer, NULL);
-}
diff --git a/src/output/dnssim.hh b/src/output/dnssim.hh
deleted file mode 100644
index a17125d..0000000
--- a/src/output/dnssim.hh
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (c) 2018-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/>.
- */
-
-//lua:require("dnsjit.core.log")
-//lua:require("dnsjit.core.receiver_h")
-
-typedef enum output_dnssim_transport {
- OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY,
- OUTPUT_DNSSIM_TRANSPORT_UDP,
- OUTPUT_DNSSIM_TRANSPORT_TCP,
- OUTPUT_DNSSIM_TRANSPORT_TLS,
- OUTPUT_DNSSIM_TRANSPORT_HTTPS2
-} output_dnssim_transport_t;
-
-typedef enum output_dnssim_h2_method {
- OUTPUT_DNSSIM_H2_GET,
- OUTPUT_DNSSIM_H2_POST
-} output_dnssim_h2_method_t;
-
-typedef struct output_dnssim_stats output_dnssim_stats_t;
-struct output_dnssim_stats {
- output_dnssim_stats_t* prev;
- output_dnssim_stats_t* next;
-
- uint64_t* latency;
-
- uint64_t since_ms;
- uint64_t until_ms;
-
- uint64_t requests;
- uint64_t ongoing;
- uint64_t answers;
-
- /* Number of connections that are open at the end of the stats interval. */
- uint64_t conn_active;
-
- /* Number of connection handshake attempts during the stats interval. */
- uint64_t conn_handshakes;
-
- /* Number of connection that have been resumed with TLS session resumption. */
- uint64_t conn_resumed;
-
- /* Number of timed out connection handshakes during the stats interval. */
- uint64_t conn_handshakes_failed;
-
- uint64_t rcode_noerror;
- uint64_t rcode_formerr;
- uint64_t rcode_servfail;
- uint64_t rcode_nxdomain;
- uint64_t rcode_notimp;
- uint64_t rcode_refused;
- uint64_t rcode_yxdomain;
- uint64_t rcode_yxrrset;
- uint64_t rcode_nxrrset;
- uint64_t rcode_notauth;
- uint64_t rcode_notzone;
- uint64_t rcode_badvers;
- uint64_t rcode_badkey;
- uint64_t rcode_badtime;
- uint64_t rcode_badmode;
- uint64_t rcode_badname;
- uint64_t rcode_badalg;
- uint64_t rcode_badtrunc;
- uint64_t rcode_badcookie;
- uint64_t rcode_other;
-};
-
-typedef struct output_dnssim {
- core_log_t _log;
-
- uint64_t processed;
- uint64_t discarded;
- uint64_t ongoing;
-
- output_dnssim_stats_t* stats_sum;
- output_dnssim_stats_t* stats_current;
- output_dnssim_stats_t* stats_first;
-
- size_t max_clients;
- bool free_after_use;
-
- uint64_t timeout_ms;
- uint64_t idle_timeout_ms;
- uint64_t handshake_timeout_ms;
- uint64_t stats_interval_ms;
-} output_dnssim_t;
-
-core_log_t* output_dnssim_log();
-
-output_dnssim_t* output_dnssim_new(size_t max_clients);
-void output_dnssim_free(output_dnssim_t* self);
-
-void output_dnssim_log_name(output_dnssim_t* self, const char* name);
-void output_dnssim_set_transport(output_dnssim_t* self, output_dnssim_transport_t tr);
-int output_dnssim_target(output_dnssim_t* self, const char* ip, uint16_t port);
-int output_dnssim_bind(output_dnssim_t* self, const char* ip);
-int output_dnssim_tls_priority(output_dnssim_t* self, const char* priority);
-int output_dnssim_run_nowait(output_dnssim_t* self);
-void output_dnssim_timeout_ms(output_dnssim_t* self, uint64_t timeout_ms);
-void output_dnssim_h2_uri_path(output_dnssim_t* self, const char* uri_path);
-void output_dnssim_h2_method(output_dnssim_t* self, const char* method);
-void output_dnssim_h2_zero_out_msgid(output_dnssim_t* self, bool zero_out_msgid);
-void output_dnssim_stats_collect(output_dnssim_t* self, uint64_t interval_ms);
-void output_dnssim_stats_finish(output_dnssim_t* self);
-
-core_receiver_t output_dnssim_receiver();
diff --git a/src/output/dnssim.lua b/src/output/dnssim.lua
deleted file mode 100644
index 25193c4..0000000
--- a/src/output/dnssim.lua
+++ /dev/null
@@ -1,433 +0,0 @@
--- Copyright (c) 2018-2021, 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/>.
-
--- dnsjit.output.dnssim
--- Simulate independent DNS clients over various transports
--- output = require("dnsjit.output.dnssim").new()
--- .SS Usage
--- output:udp()
--- output:target("::1", 53)
--- recv, rctx = output:receive()
--- -- pass in objects using recv(rctx, obj)
--- -- repeatedly call output:run_nowait() until it returns 0
--- .SS DNS-over-TLS example configuration
--- output:tls("NORMAL:-VERS-ALL:+VERS-TLS1.3") -- enforce TLS 1.3
--- .SS DNS-over-HTTPS/2 example configuration
--- output:https2({ method = "POST", uri_path = "/doh" })
---
--- Output module for simulating traffic from huge number of independent,
--- individual DNS clients.
--- Uses libuv for asynchronous communication.
--- There may only be a single DnsSim in a thread.
--- Use
--- .I dnsjit.core.thread
--- to have multiple DnsSim instances.
--- .P
--- With proper use of this component, it is possible to simulate hundreds of
--- thousands of clients when using a high-performance server.
--- This also applies for state-full transports.
--- The complete set-up is quite complex and requires other components.
--- See DNS Shotgun
--- .RI ( https://gitlab.nic.cz/knot/shotgun )
--- for dnsjit scripts ready for use for high-performance
--- benchmarking.
-module(...,package.seeall)
-
-require("dnsjit.output.dnssim_h")
-local bit = require("bit")
-local object = require("dnsjit.core.objects")
-local ffi = require("ffi")
-local C = ffi.C
-
-local DnsSim = {}
-
-local _DNSSIM_VERSION = 20210129
-local _DNSSIM_JSON_VERSION = 20200527
-
--- Create a new DnsSim output for up to max_clients.
-function DnsSim.new(max_clients)
- local self = {
- obj = C.output_dnssim_new(max_clients),
- max_clients = max_clients,
- }
- ffi.gc(self.obj, C.output_dnssim_free)
- return setmetatable(self, { __index = DnsSim })
-end
-
-local function _check_version(version, req_version)
- if req_version == nil then
- return version
- end
- local min_version = tonumber(req_version)
- if min_version == nil then
- C.output_dnssim_log():fatal("invalid version number: "..req_version)
- return nil
- end
- if version >= min_version then
- return version
- end
- return nil
-end
-
--- Check that version of dnssim is at minimum the one passed as
--- .B req_version
--- and return the actual version number.
--- Return nil if the condition is not met.
---
--- If no
--- .B req_version
--- is specified no check is done and only the version number is returned.
-function DnsSim.check_version(req_version)
- return _check_version(_DNSSIM_VERSION, req_version)
-end
-
--- Check that version of dnssim's JSON data format is at minimum the one passed as
--- .B req_version
--- and return the actual version number.
--- Return nil if the condition is not met.
---
--- If no
--- .B req_version
--- is specified no check is done and only the version number is returned.
-function DnsSim.check_json_version(req_version)
- return _check_version(_DNSSIM_JSON_VERSION, req_version)
-end
-
--- Return the Log object to control logging of this instance or module.
--- Optionally, set the instance's log name.
--- Unique name should be used for each instance.
-function DnsSim:log(name)
- if self == nil then
- return C.output_dnssim_log()
- end
- if name ~= nil then
- C.output_dnssim_log_name(self.obj, name)
- end
- return self.obj._log
-end
-
--- Set the target IPv4/IPv6 address where queries will be sent to.
-function DnsSim:target(ip, port)
- local nport = tonumber(port)
- if nport == nil then
- self.obj._log:fatal("invalid port: "..port)
- return -1
- end
- if nport <= 0 or nport > 65535 then
- self.obj._log:fatal("invalid port number: "..nport)
- return -1
- end
- return C.output_dnssim_target(self.obj, ip, nport)
-end
-
--- Specify source IPv4/IPv6 address for sending queries.
--- Can be set multiple times.
--- Addresses are selected round-robin when sending.
-function DnsSim:bind(ip)
- return C.output_dnssim_bind(self.obj, ip)
-end
-
--- Set the preferred transport to UDP.
---
--- When the optional argument
--- .B tcp_fallback
--- is set to true, individual queries are re-tried over TCP when TC bit is set in the answer.
--- Defaults to
--- .B false
--- (aka only UDP is used).
-function DnsSim:udp(tcp_fallback)
- if tcp_fallback == true then
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_UDP)
- else
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY)
- end
-end
-
--- Set the transport to TCP.
-function DnsSim:tcp()
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_TCP)
-end
-
--- Set the transport to TLS.
---
--- The optional argument
--- .B tls_priority
--- is a GnuTLS priority string, which can be used to select TLS versions, cipher suites etc.
--- For example:
---
--- .RB "- """ NORMAL:%NO_TICKETS """"
--- will use defaults without TLS session resumption.
---
--- .RB "- """ SECURE128:-VERS-ALL:+VERS-TLS1.3 """"
--- will use only TLS 1.3 with 128-bit secure ciphers.
---
--- Refer to:
--- .I https://gnutls.org/manual/html_node/Priority-Strings.html
-function DnsSim:tls(tls_priority)
- if tls_priority ~= nil then
- C.output_dnssim_tls_priority(self.obj, tls_priority)
- end
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_TLS)
-end
-
--- Set the transport to HTTP/2 over TLS.
---
--- .B http2_options
--- is a lua table which supports the following keys:
---
--- .B method:
--- .B GET
--- (default)
--- or
--- .B POST
---
--- .B uri_path:
--- where queries will be sent.
--- Defaults to
--- .B /dns-query
---
--- .B zero_out_msgid:
--- when
--- .B true
--- (default), query ID is always set to 0
---
--- See tls() method for
--- .B tls_priority
--- documentation.
-function DnsSim:https2(http2_options, tls_priority)
- if tls_priority ~= nil then
- C.output_dnssim_tls_priority(self.obj, tls_priority)
- end
-
- uri_path = "/dns-query"
- zero_out_msgid = true
- method = "GET"
-
- if http2_options ~= nil then
- if type(http2_options) ~= "table" then
- self.obj._log:fatal("http2_options must be a table")
- else
- if http2_options["uri_path"] ~= nil then
- uri_path = http2_options["uri_path"]
- end
- if http2_options["zero_out_msgid"] ~= nil and http2_options["zero_out_msgid"] ~= true then
- zero_out_msgid = false
- end
- if http2_options["method"] ~= nil then
- method = http2_options["method"]
- end
- end
- end
-
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_HTTPS2)
- C.output_dnssim_h2_uri_path(self.obj, uri_path)
- C.output_dnssim_h2_method(self.obj, method)
- C.output_dnssim_h2_zero_out_msgid(self.obj, zero_out_msgid)
-end
-
--- Set timeout for the individual requests in seconds (default 2s).
---
--- .BR Beware :
--- increasing this value while the target resolver isn't very responsive
--- (cold cache, heavy load) may degrade DnsSim's performance and skew
--- the results.
-function DnsSim:timeout(seconds)
- if seconds == nil then
- seconds = 2
- end
- timeout_ms = math.floor(seconds * 1000)
- C.output_dnssim_timeout_ms(self.obj, timeout_ms)
-end
-
--- Set TCP connection idle timeout for connection reuse according to RFC7766,
--- Section 6.2.3 (defaults to 10s).
--- When set to zero, connections are closed immediately after there are no
--- more pending queries.
-function DnsSim:idle_timeout(seconds)
- if seconds == nil then
- seconds = 10
- end
- self.obj.idle_timeout_ms = math.floor(seconds * 1000)
-end
-
--- Set TCP connection handshake timeout (defaults to 5s).
--- During heavy load, the server may no longer accept new connections.
--- This parameter ensures such connection attempts are aborted after the
--- timeout expires.
-function DnsSim:handshake_timeout(seconds)
- if seconds == nil then
- seconds = 5
- end
- self.obj.handshake_timeout_ms = math.floor(seconds * 1000)
-end
-
--- Run the libuv loop once without blocking when there is no I/O.
--- This should be called repeatedly until 0 is returned and no more data
--- is expected to be received by DnsSim.
-function DnsSim:run_nowait()
- return C.output_dnssim_run_nowait(self.obj)
-end
-
--- Set this to true if DnsSim should free the memory of passed-in objects
--- (useful when using
--- .I dnsjit.filter.copy
--- to pass objects from different thread).
-function DnsSim:free_after_use(free_after_use)
- self.obj.free_after_use = free_after_use
-end
-
--- Number of input packets discarded due to various reasons.
--- To investigate causes, run with increased logging level.
-function DnsSim:discarded()
- return tonumber(self.obj.discarded)
-end
-
--- Number of valid requests (input packets) processed.
-function DnsSim:requests()
- return tonumber(self.obj.stats_sum.requests)
-end
-
--- Number of requests that received an answer
-function DnsSim:answers()
- return tonumber(self.obj.stats_sum.answers)
-end
-
--- Number of requests that received a NOERROR response
-function DnsSim:noerror()
- return tonumber(self.obj.stats_sum.rcode_noerror)
-end
-
--- Configure statistics to be collected every N seconds.
-function DnsSim:stats_collect(seconds)
- if seconds == nil then
- self.obj._log:fatal("number of seconds must be set for stats_collect()")
- end
- interval_ms = math.floor(seconds * 1000)
- C.output_dnssim_stats_collect(self.obj, interval_ms)
-end
-
--- Stop the collection of statistics.
-function DnsSim:stats_finish()
- C.output_dnssim_stats_finish(self.obj)
-end
-
--- Export the results to a JSON file.
-function DnsSim:export(filename)
- local file = io.open(filename, "w")
- if file == nil then
- self.obj._log:fatal("export failed: no filename")
- return
- end
-
- local function write_stats(file, stats)
- file:write(
- "{ ",
- '"since_ms":', tonumber(stats.since_ms), ',',
- '"until_ms":', tonumber(stats.until_ms), ',',
- '"requests":', tonumber(stats.requests), ',',
- '"ongoing":', tonumber(stats.ongoing), ',',
- '"answers":', tonumber(stats.answers), ',',
- '"conn_active":', tonumber(stats.conn_active), ',',
- '"conn_handshakes":', tonumber(stats.conn_handshakes), ',',
- '"conn_resumed":', tonumber(stats.conn_resumed), ',',
- '"conn_handshakes_failed":', tonumber(stats.conn_handshakes_failed), ',',
- '"rcode_noerror":', tonumber(stats.rcode_noerror), ',',
- '"rcode_formerr":', tonumber(stats.rcode_formerr), ',',
- '"rcode_servfail":', tonumber(stats.rcode_servfail), ',',
- '"rcode_nxdomain":', tonumber(stats.rcode_nxdomain), ',',
- '"rcode_notimp":', tonumber(stats.rcode_notimp), ',',
- '"rcode_refused":', tonumber(stats.rcode_refused), ',',
- '"rcode_yxdomain":', tonumber(stats.rcode_yxdomain), ',',
- '"rcode_yxrrset":', tonumber(stats.rcode_yxrrset), ',',
- '"rcode_nxrrset":', tonumber(stats.rcode_nxrrset), ',',
- '"rcode_notauth":', tonumber(stats.rcode_notauth), ',',
- '"rcode_notzone":', tonumber(stats.rcode_notzone), ',',
- '"rcode_badvers":', tonumber(stats.rcode_badvers), ',',
- '"rcode_badkey":', tonumber(stats.rcode_badkey), ',',
- '"rcode_badtime":', tonumber(stats.rcode_badtime), ',',
- '"rcode_badmode":', tonumber(stats.rcode_badmode), ',',
- '"rcode_badname":', tonumber(stats.rcode_badname), ',',
- '"rcode_badalg":', tonumber(stats.rcode_badalg), ',',
- '"rcode_badtrunc":', tonumber(stats.rcode_badtrunc), ',',
- '"rcode_badcookie":', tonumber(stats.rcode_badcookie), ',',
- '"rcode_other":', tonumber(stats.rcode_other), ',',
- '"latency":[')
- file:write(tonumber(stats.latency[0]))
- for i=1,tonumber(self.obj.timeout_ms) do
- file:write(',', tonumber(stats.latency[i]))
- end
- file:write("]}")
- end
-
- file:write(
- "{ ",
- '"version":', _DNSSIM_JSON_VERSION, ',',
- '"merged":false,',
- '"stats_interval_ms":', tonumber(self.obj.stats_interval_ms), ',',
- '"timeout_ms":', tonumber(self.obj.timeout_ms), ',',
- '"idle_timeout_ms":', tonumber(self.obj.idle_timeout_ms), ',',
- '"handshake_timeout_ms":', tonumber(self.obj.handshake_timeout_ms), ',',
- '"discarded":', self:discarded(), ',',
- '"stats_sum":')
- write_stats(file, self.obj.stats_sum)
- file:write(
- ',',
- '"stats_periodic":[')
-
- local stats = self.obj.stats_first
- write_stats(file, stats)
-
- while (stats.next ~= nil) do
- stats = stats.next
- file:write(',')
- write_stats(file, stats)
- end
-
- file:write(']}')
- file:close()
- self.obj._log:notice("results exported to "..filename)
-end
-
--- Return the C function and context for receiving objects.
--- Only
--- .I dnsjit.filter.core.object.ip
--- or
--- .I dnsjit.filter.core.object.ip6
--- objects are supported.
--- The component expects a 32bit integer (in host order) ranging from 0
--- to max_clients written to first 4 bytes of destination IP.
--- See
--- .IR dnsjit.filter.ipsplit .
-function DnsSim:receive()
- local receive = C.output_dnssim_receiver()
- return receive, self.obj
-end
-
--- Deprecated: use udp() instead.
---
--- Set the transport to UDP (without any TCP fallback).
-function DnsSim:udp_only()
- C.output_dnssim_set_transport(self.obj, C.OUTPUT_DNSSIM_TRANSPORT_UDP_ONLY)
-end
-
--- dnsjit.filter.copy (3),
--- dnsjit.filter.ipsplit (3),
--- dnsjit.filter.core.object.ip (3),
--- dnsjit.filter.core.object.ip6 (3),
--- https://gitlab.nic.cz/knot/shotgun
-return DnsSim
diff --git a/src/output/dnssim/CHANGELOG.md b/src/output/dnssim/CHANGELOG.md
deleted file mode 100644
index 9cbfa55..0000000
--- a/src/output/dnssim/CHANGELOG.md
+++ /dev/null
@@ -1,16 +0,0 @@
-dnssim v20210129
-================
-
-- Added DNS-over-HTTPS support with https2()
-- Added IPv4 support
-- Abort operation on insufficient file descriptors
-- Match QUESTION section of received responses
-- Improvements in connection state handling
-- Deprecate udp_only() in favor of udp()
-- Allow setting logger name with log(name)
-- Added check_version() and check_json_version()
-
-dnssim v20200723
-================
-
-- First released dnssim version with UDP, TCP and DoT support
diff --git a/src/output/dnssim/common.c b/src/output/dnssim/common.c
deleted file mode 100644
index e170aec..0000000
--- a/src/output/dnssim/common.c
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * 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;
-}
diff --git a/src/output/dnssim/connection.c b/src/output/dnssim/connection.c
deleted file mode 100644
index eeb1ce8..0000000
--- a/src/output/dnssim/connection.c
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * Copyright (c) 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>
-
-static core_log_t _log = LOG_T_INIT("output.dnssim");
-
-static bool _conn_is_connecting(_output_dnssim_connection_t* conn)
-{
- return (conn->state >= _OUTPUT_DNSSIM_CONN_TCP_HANDSHAKE && conn->state <= _OUTPUT_DNSSIM_CONN_ACTIVE);
-}
-
-void _output_dnssim_conn_maybe_free(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn can't be nil");
- mlassert(conn->client, "conn must belong to a client");
- if (conn->handle == NULL && conn->handshake_timer == NULL && conn->idle_timer == NULL) {
- _ll_try_remove(conn->client->conn, conn);
- if (conn->tls != NULL) {
- free(conn->tls);
- conn->tls = NULL;
- }
- if (conn->http2 != NULL) {
- free(conn->http2);
- conn->http2 = NULL;
- }
- free(conn);
- }
-}
-
-static void _on_handshake_timer_closed(uv_handle_t* handle)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)handle->data;
- mlassert(conn, "conn is nil");
- mlassert(conn->handshake_timer, "conn must have handshake timer when closing it");
- free(conn->handshake_timer);
- conn->handshake_timer = NULL;
- _output_dnssim_conn_maybe_free(conn);
-}
-
-static void _on_idle_timer_closed(uv_handle_t* handle)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)handle->data;
- mlassert(conn, "conn is nil");
- mlassert(conn->idle_timer, "conn must have idle timer when closing it");
- free(conn->idle_timer);
- conn->is_idle = false;
- conn->idle_timer = NULL;
- _output_dnssim_conn_maybe_free(conn);
-}
-
-void _output_dnssim_conn_close(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn can't be nil");
- mlassert(conn->stats, "conn must have stats");
- mlassert(conn->client, "conn must have client");
- mlassert(conn->client->dnssim, "client must have dnssim");
-
- output_dnssim_t* self = conn->client->dnssim;
-
- switch (conn->state) {
- case _OUTPUT_DNSSIM_CONN_CLOSING:
- case _OUTPUT_DNSSIM_CONN_CLOSED:
- return;
- case _OUTPUT_DNSSIM_CONN_TCP_HANDSHAKE:
- case _OUTPUT_DNSSIM_CONN_TLS_HANDSHAKE:
- conn->stats->conn_handshakes_failed++;
- self->stats_sum->conn_handshakes_failed++;
- break;
- case _OUTPUT_DNSSIM_CONN_ACTIVE:
- case _OUTPUT_DNSSIM_CONN_CONGESTED:
- self->stats_current->conn_active--;
- break;
- case _OUTPUT_DNSSIM_CONN_INITIALIZED:
- case _OUTPUT_DNSSIM_CONN_CLOSE_REQUESTED:
- break;
- default:
- lfatal("unknown conn state: %d", conn->state);
- }
- if (conn->prevent_close) {
- lassert(conn->state <= _OUTPUT_DNSSIM_CONN_CLOSE_REQUESTED, "conn already closing");
- conn->state = _OUTPUT_DNSSIM_CONN_CLOSE_REQUESTED;
- return;
- }
- conn->state = _OUTPUT_DNSSIM_CONN_CLOSING;
-
- if (conn->handshake_timer != NULL) {
- uv_timer_stop(conn->handshake_timer);
- uv_close((uv_handle_t*)conn->handshake_timer, _on_handshake_timer_closed);
- }
- if (conn->idle_timer != NULL) {
- conn->is_idle = false;
- uv_timer_stop(conn->idle_timer);
- uv_close((uv_handle_t*)conn->idle_timer, _on_idle_timer_closed);
- }
-
- switch (_self->transport) {
- case OUTPUT_DNSSIM_TRANSPORT_TCP:
- _output_dnssim_tcp_close(conn);
- break;
- case OUTPUT_DNSSIM_TRANSPORT_TLS:
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- _output_dnssim_tls_close(conn);
-#else
- lfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- break;
- case OUTPUT_DNSSIM_TRANSPORT_HTTPS2:
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- _output_dnssim_https2_close(conn);
-#else
- lfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- break;
- default:
- lfatal("unsupported transport");
- break;
- }
-}
-
-/* Close connection or run idle timer when there are no more outstanding queries. */
-void _output_dnssim_conn_idle(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn can't be nil");
-
- if (conn->queued == NULL && conn->sent == NULL) {
- if (conn->idle_timer == NULL)
- _output_dnssim_conn_close(conn);
- else if (!conn->is_idle) {
- conn->is_idle = true;
- uv_timer_again(conn->idle_timer);
- }
- }
-}
-
-static void _send_pending_queries(_output_dnssim_connection_t* conn)
-{
- _output_dnssim_query_tcp_t* qry;
- mlassert(conn, "conn is nil");
- mlassert(conn->client, "conn->client is nil");
- qry = (_output_dnssim_query_tcp_t*)conn->client->pending;
-
- while (qry != NULL && conn->state == _OUTPUT_DNSSIM_CONN_ACTIVE) {
- _output_dnssim_query_tcp_t* next = (_output_dnssim_query_tcp_t*)qry->qry.next;
- if (qry->qry.state == _OUTPUT_DNSSIM_QUERY_PENDING_WRITE) {
- switch (qry->qry.transport) {
- case OUTPUT_DNSSIM_TRANSPORT_TCP:
- _output_dnssim_tcp_write_query(conn, qry);
- break;
- case OUTPUT_DNSSIM_TRANSPORT_TLS:
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- _output_dnssim_tls_write_query(conn, qry);
-#else
- mlfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- break;
- case OUTPUT_DNSSIM_TRANSPORT_HTTPS2:
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- _output_dnssim_https2_write_query(conn, qry);
-#else
- mlfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- break;
- default:
- mlfatal("unsupported protocol");
- break;
- }
- }
- qry = next;
- }
-}
-
-int _output_dnssim_handle_pending_queries(_output_dnssim_client_t* client)
-{
- int ret = 0;
- mlassert(client, "client is nil");
-
- if (client->pending == NULL)
- return ret;
-
- output_dnssim_t* self = client->dnssim;
- mlassert(self, "client must belong to dnssim");
-
- /* Get active connection or find out whether new connection has to be opened. */
- bool is_connecting = false;
- _output_dnssim_connection_t* conn = client->conn;
- while (conn != NULL) {
- if (conn->state == _OUTPUT_DNSSIM_CONN_ACTIVE)
- break;
- else if (_conn_is_connecting(conn))
- is_connecting = true;
- conn = conn->next;
- }
-
- if (conn != NULL) { /* Send data right away over active connection. */
- _send_pending_queries(conn);
- } else if (!is_connecting) { /* No active or connecting connection -> open a new one. */
- lfatal_oom(conn = calloc(1, sizeof(_output_dnssim_connection_t)));
- conn->state = _OUTPUT_DNSSIM_CONN_INITIALIZED;
- conn->client = client;
- conn->stats = self->stats_current;
- if (_self->transport == OUTPUT_DNSSIM_TRANSPORT_TLS) {
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- ret = _output_dnssim_tls_init(conn);
- if (ret < 0) {
- free(conn);
- return ret;
- }
-#else
- lfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- } else if (_self->transport == OUTPUT_DNSSIM_TRANSPORT_HTTPS2) {
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- ret = _output_dnssim_https2_init(conn);
- if (ret < 0) {
- free(conn);
- return ret;
- }
-#else
- lfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- }
- ret = _output_dnssim_tcp_connect(self, conn);
- if (ret < 0)
- return ret;
- _ll_append(client->conn, conn);
- } /* Otherwise, pending queries wil be sent after connected callback. */
-
- return ret;
-}
-
-void _output_dnssim_conn_activate(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn is nil");
- mlassert(conn->client, "conn must be associated with a client");
- mlassert(conn->client->dnssim, "client must be associated with dnssim");
-
- uv_timer_stop(conn->handshake_timer);
-
- conn->state = _OUTPUT_DNSSIM_CONN_ACTIVE;
- conn->client->dnssim->stats_current->conn_active++;
- conn->read_state = _OUTPUT_DNSSIM_READ_STATE_DNSLEN;
- conn->dnsbuf_len = 2;
- conn->dnsbuf_pos = 0;
- conn->dnsbuf_free_after_use = false;
-
- _send_pending_queries(conn);
- _output_dnssim_conn_idle(conn);
-}
-
-int _process_dnsmsg(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn can't be nil");
- mlassert(conn->client, "conn must have client");
- mlassert(conn->client->dnssim, "client must have dnssim");
-
- output_dnssim_t* self = conn->client->dnssim;
-
- core_object_payload_t payload = CORE_OBJECT_PAYLOAD_INIT(NULL);
- core_object_dns_t dns_a = CORE_OBJECT_DNS_INIT(&payload);
-
- payload.payload = (uint8_t*)conn->dnsbuf_data;
- payload.len = conn->dnsbuf_len;
-
- dns_a.obj_prev = (core_object_t*)&payload;
- int ret = core_object_dns_parse_header(&dns_a);
- if (ret != 0) {
- lwarning("tcp response malformed");
- return _ERR_MALFORMED;
- }
- ldebug("tcp recv dnsmsg id: %04x", dns_a.id);
-
- _output_dnssim_query_t* qry;
-
- if (_self->transport == OUTPUT_DNSSIM_TRANSPORT_HTTPS2) {
- lassert(conn->http2, "conn must have http2 ctx");
- lassert(conn->http2->current_qry, "http2 has no current_qry");
- lassert(conn->http2->current_qry->qry.req, "current_qry has no req");
- lassert(conn->http2->current_qry->qry.req->dns_q, "req has no dns_q");
-
- ret = _output_dnssim_answers_request(conn->http2->current_qry->qry.req, &dns_a);
- switch (ret) {
- case 0:
- _output_dnssim_request_answered(conn->http2->current_qry->qry.req, &dns_a);
- break;
- case _ERR_MSGID:
- lwarning("https2 QID mismatch: request=0x%04x, response=0x%04x",
- conn->http2->current_qry->qry.req->dns_q->id, dns_a.id);
- break;
- case _ERR_QUESTION:
- default:
- lwarning("https2 response question mismatch");
- break;
- }
- } else {
- qry = conn->sent;
- while (qry != NULL) {
- if (qry->req->dns_q->id == dns_a.id) {
- ret = _output_dnssim_answers_request(qry->req, &dns_a);
- if (ret != 0) {
- lwarning("response question mismatch");
- } else {
- _output_dnssim_request_answered(qry->req, &dns_a);
- }
- break;
- }
- qry = qry->next;
- }
- }
-
- return 0;
-}
-
-static int _parse_dnsbuf_data(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn can't be nil");
- mlassert(conn->dnsbuf_pos == conn->dnsbuf_len, "attempt to parse incomplete dnsbuf_data");
- int ret = 0;
-
- switch (conn->read_state) {
- case _OUTPUT_DNSSIM_READ_STATE_DNSLEN: {
- uint16_t* p_dnslen = (uint16_t*)conn->dnsbuf_data;
- conn->dnsbuf_len = ntohs(*p_dnslen);
- if (conn->dnsbuf_len == 0) {
- mlwarning("invalid dnslen received: 0");
- conn->dnsbuf_len = 2;
- conn->read_state = _OUTPUT_DNSSIM_READ_STATE_DNSLEN;
- } else if (conn->dnsbuf_len < 12) {
- mldebug("invalid dnslen received: %d", conn->dnsbuf_len);
- ret = -1;
- } else {
- mldebug("dnslen: %d", conn->dnsbuf_len);
- conn->read_state = _OUTPUT_DNSSIM_READ_STATE_DNSMSG;
- }
- break;
- }
- case _OUTPUT_DNSSIM_READ_STATE_DNSMSG:
- ret = _process_dnsmsg(conn);
- if (ret) {
- conn->read_state = _OUTPUT_DNSSIM_READ_STATE_INVALID;
- } else {
- conn->dnsbuf_len = 2;
- conn->read_state = _OUTPUT_DNSSIM_READ_STATE_DNSLEN;
- }
- break;
- default:
- mlfatal("tcp invalid connection read_state");
- break;
- }
-
- conn->dnsbuf_pos = 0;
- if (conn->dnsbuf_free_after_use) {
- conn->dnsbuf_free_after_use = false;
- free(conn->dnsbuf_data);
- }
- conn->dnsbuf_data = NULL;
-
- return ret;
-}
-
-static unsigned int _read_dns_stream_chunk(_output_dnssim_connection_t* conn, size_t len, const char* data)
-{
- mlassert(conn, "conn can't be nil");
- mlassert(data, "data can't be nil");
- mlassert(len > 0, "no data to read");
- mlassert((conn->read_state == _OUTPUT_DNSSIM_READ_STATE_DNSLEN || conn->read_state == _OUTPUT_DNSSIM_READ_STATE_DNSMSG),
- "connection has invalid read_state");
-
- int ret = 0;
- unsigned int nread;
- size_t expected = conn->dnsbuf_len - conn->dnsbuf_pos;
- mlassert(expected > 0, "no data expected");
-
- if (conn->dnsbuf_free_after_use == false && expected > len) {
- /* Start of partial read. */
- mlassert(conn->dnsbuf_pos == 0, "conn->dnsbuf_pos must be 0 at start of partial read");
- mlassert(conn->dnsbuf_len > 0, "conn->dnsbuf_len must be set at start of partial read");
- mlfatal_oom(conn->dnsbuf_data = malloc(conn->dnsbuf_len * sizeof(char)));
- conn->dnsbuf_free_after_use = true;
- }
-
- if (conn->dnsbuf_free_after_use) { /* Partial read is in progress. */
- char* dest = conn->dnsbuf_data + conn->dnsbuf_pos;
- if (expected < len)
- len = expected;
- memcpy(dest, data, len);
- conn->dnsbuf_pos += len;
- nread = len;
- } else { /* Complete and clean read. */
- mlassert(expected <= len, "not enough data to perform complete read");
- conn->dnsbuf_data = (char*)data;
- conn->dnsbuf_pos = conn->dnsbuf_len;
- nread = expected;
- }
-
- /* If entire dnslen/dnsmsg was read, attempt to parse it. */
- if (conn->dnsbuf_len == conn->dnsbuf_pos) {
- ret = _parse_dnsbuf_data(conn);
- if (ret < 0)
- return ret;
- }
-
- return nread;
-}
-
-void _output_dnssim_read_dns_stream(_output_dnssim_connection_t* conn, size_t len, const char* data)
-{
- int pos = 0;
- int chunk = 0;
- while (pos < len) {
- chunk = _read_dns_stream_chunk(conn, len - pos, data + pos);
- if (chunk < 0) {
- mlwarning("lost orientation in DNS stream, closing");
- _output_dnssim_conn_close(conn);
- break;
- } else {
- pos += chunk;
- }
- }
- mlassert((pos == len) || (chunk < 0), "dns stream read invalid, pos != len");
-}
-
-void _output_dnssim_read_dnsmsg(_output_dnssim_connection_t* conn, size_t len, const char* data)
-{
- mlassert(conn, "conn is nil");
- mlassert(len > 0, "len is zero");
- mlassert(data, "no data");
- mlassert(conn->dnsbuf_pos == 0, "dnsbuf not empty");
- mlassert(conn->dnsbuf_free_after_use == false, "dnsbuf read in progress");
-
- /* Read dnsmsg of given length from input data. */
- conn->dnsbuf_len = len;
- conn->read_state = _OUTPUT_DNSSIM_READ_STATE_DNSMSG;
- int nread = _read_dns_stream_chunk(conn, len, data);
-
- if (nread != len) {
- mlwarning("failed to read received dnsmsg");
- if (conn->dnsbuf_free_after_use)
- free(conn->dnsbuf_data);
- }
-
- /* Clean state afterwards. */
- conn->read_state = _OUTPUT_DNSSIM_READ_STATE_DNSLEN;
- conn->dnsbuf_len = 2;
- conn->dnsbuf_pos = 0;
- conn->dnsbuf_free_after_use = false;
-}
diff --git a/src/output/dnssim/https2.c b/src/output/dnssim/https2.c
deleted file mode 100644
index 72fcdaf..0000000
--- a/src/output/dnssim/https2.c
+++ /dev/null
@@ -1,592 +0,0 @@
-/*
- * Copyright (c) 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 "lib/base64url.h"
-
-#include <gnutls/gnutls.h>
-#include <string.h>
-
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
-
-#define OUTPUT_DNSSIM_MAKE_NV(NAME, VALUE, VALUELEN) \
- { \
- (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, VALUELEN, \
- NGHTTP2_NV_FLAG_NONE \
- }
-
-#define OUTPUT_DNSSIM_MAKE_NV2(NAME, VALUE) \
- { \
- (uint8_t*)NAME, (uint8_t*)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
- NGHTTP2_NV_FLAG_NONE \
- }
-
-#define OUTPUT_DNSSIM_HTTP_GET_TEMPLATE "?dns="
-#define OUTPUT_DNSSIM_HTTP_GET_TEMPLATE_LEN (sizeof(OUTPUT_DNSSIM_HTTP_GET_TEMPLATE) - 1)
-#define OUTPUT_DNSSIM_HTTP2_INITIAL_MAX_CONCURRENT_STREAMS 100
-#define OUTPUT_DNSSIM_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu
-
-static core_log_t _log = LOG_T_INIT("output.dnssim");
-
-static ssize_t _http2_send(nghttp2_session* session, const uint8_t* data, size_t length, int flags, void* user_data)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)user_data;
- mlassert(conn, "conn can't be null");
- mlassert(conn->tls, "conn must have tls ctx");
- mlassert(conn->tls->session, "conn must have tls session");
-
- mldebug("http2 (%p): sending data, len=%ld", session, length);
-
- ssize_t len = 0;
- if ((len = gnutls_record_send(conn->tls->session, data, length)) < 0) {
- mlwarning("gnutls_record_send failed: %s", gnutls_strerror(len));
- len = NGHTTP2_ERR_CALLBACK_FAILURE;
- }
-
- return len;
-}
-
-static ssize_t _http2_on_data_provider_read(nghttp2_session* session, int32_t stream_id, uint8_t* buf, size_t length, uint32_t* data_flags, nghttp2_data_source* source, void* user_data)
-{
- _output_dnssim_https2_data_provider_t* buffer = source->ptr;
- mlassert(buffer, "no data provider");
- mlassert(buffer->len <= MAX_DNSMSG_SIZE, "invalid dnsmsg size: %zu B", buffer->len);
-
- ssize_t sent = (length < buffer->len) ? length : buffer->len;
- mlassert(sent >= 0, "negative length of bytes to send");
-
- memcpy(buf, buffer->buf, sent);
- buffer->buf += sent;
- buffer->len -= sent;
- if (buffer->len == 0)
- *data_flags |= NGHTTP2_DATA_FLAG_EOF;
-
- return sent;
-}
-
-static _output_dnssim_query_tcp_t* _http2_get_stream_qry(_output_dnssim_connection_t* conn, int32_t stream_id)
-{
- mlassert(conn, "conn is nil");
- mlassert(stream_id >= 0, "invalid stream_id");
-
- _output_dnssim_query_tcp_t* qry = (_output_dnssim_query_tcp_t*)conn->sent;
- while (qry != NULL && qry->stream_id != stream_id) {
- qry = (_output_dnssim_query_tcp_t*)qry->qry.next;
- }
-
- return qry;
-}
-
-static int _http2_on_header(nghttp2_session* session, const nghttp2_frame* frame, const uint8_t* name, size_t namelen, const uint8_t* value, size_t valuelen, uint8_t flags, void* user_data)
-{
- if (frame->hd.type == NGHTTP2_HEADERS && frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
- if (namelen == 7 && strncmp((char*)name, ":status", 7) == 0) {
- if (valuelen != 3 || (value[0] != '1' && value[0] != '2')) {
- /* When reponse code isn't 1xx or 2xx, close the query.
- * This will result in request timeout, which currently seems
- * slightly better than mocking SERVFAIL for statistics. */
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)user_data;
- mlassert(conn, "conn is nil");
- _output_dnssim_query_tcp_t* qry = _http2_get_stream_qry(conn, frame->hd.stream_id);
-
- if (qry != NULL) {
- _output_dnssim_close_query_https2(qry);
- mlinfo("http response %s, closing query", value);
- }
- }
- }
- }
- return 0;
-}
-
-static int _http2_on_data_recv(nghttp2_session* session, uint8_t flags, int32_t stream_id, const uint8_t* data, size_t len, void* user_data)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)user_data;
- mlassert(conn, "conn is nil");
-
- _output_dnssim_query_tcp_t* qry = _http2_get_stream_qry(conn, stream_id);
-
- mldebug("http2: data chunk recv, session=%p, len=%d", session, len);
-
- if (qry) {
- if (qry->recv_buf_len == 0) {
- if (len > MAX_DNSMSG_SIZE) {
- mlwarning("http response exceeded maximum size of dns message");
- return -1;
- }
- mlfatal_oom(qry->recv_buf = malloc(len));
- memcpy(qry->recv_buf, data, len);
- qry->recv_buf_len = len;
- } else {
- size_t total_len = qry->recv_buf_len + len;
- if (total_len > MAX_DNSMSG_SIZE) {
- mlwarning("http response exceeded maximum size of dns message");
- return -1;
- }
- mlfatal_oom(qry->recv_buf = realloc(qry->recv_buf, total_len));
- memcpy(qry->recv_buf + qry->recv_buf_len, data, len);
- qry->recv_buf_len = total_len;
- }
- } else {
- mldebug("no query associated with this stream id, ignoring");
- }
-
- return 0;
-}
-
-static void _http2_check_max_streams(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn can't be null");
- mlassert(conn->http2, "conn must have http2 ctx");
-
- switch (conn->state) {
- case _OUTPUT_DNSSIM_CONN_ACTIVE:
- if (conn->http2->open_streams >= conn->http2->max_concurrent_streams) {
- mlinfo("http2 (%p): reached maximum number of concurrent streams (%ld)",
- conn->http2->session, conn->http2->max_concurrent_streams);
- conn->state = _OUTPUT_DNSSIM_CONN_CONGESTED;
- }
- break;
- case _OUTPUT_DNSSIM_CONN_CONGESTED:
- if (conn->http2->open_streams < conn->http2->max_concurrent_streams)
- conn->state = _OUTPUT_DNSSIM_CONN_ACTIVE;
- break;
- default:
- break;
- }
-}
-
-static int _http2_on_stream_close(nghttp2_session* session, int32_t stream_id, uint32_t error_code, void* user_data)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)user_data;
- mlassert(conn, "conn can't be null");
- mlassert(conn->http2, "conn must have http2 ctx");
- mlassert(conn->http2->open_streams > 0, "conn has no open streams");
-
- conn->http2->open_streams--;
- _http2_check_max_streams(conn);
- return 0;
-}
-
-static int _http2_on_frame_recv(nghttp2_session* session, const nghttp2_frame* frame, void* user_data)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)user_data;
- mlassert(conn, "conn can't be null");
- mlassert(conn->tls, "conn must have tls ctx");
- mlassert(conn->tls->session, "conn must have tls session");
- mlassert(conn->http2, "conn must have http2 ctx");
-
- switch (frame->hd.type) {
- case NGHTTP2_DATA:
- if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
- mldebug("http2 (%p): final DATA frame recv", session);
- _output_dnssim_query_tcp_t* qry = _http2_get_stream_qry(conn, frame->hd.stream_id);
-
- if (qry != NULL) {
- conn->http2->current_qry = qry;
- _output_dnssim_read_dnsmsg(conn, qry->recv_buf_len, (char*)qry->recv_buf);
- }
- }
- break;
- case NGHTTP2_SETTINGS:
- if (!conn->http2->remote_settings_received) {
- /* On the first SETTINGS frame, set concurrent streams to unlimited, same as nghttp2. */
- conn->http2->remote_settings_received = true;
- conn->http2->max_concurrent_streams = OUTPUT_DNSSIM_HTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
- _http2_check_max_streams(conn);
- }
- nghttp2_settings* settings = (nghttp2_settings*)frame;
- int i;
- for (i = 0; i < settings->niv; i++) {
- switch (settings->iv[i].settings_id) {
- case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
- conn->http2->max_concurrent_streams = settings->iv[i].value;
- _http2_check_max_streams(conn);
- break;
- default:
- break;
- }
- }
- break;
- default:
- break;
- }
- return 0;
-}
-
-int _output_dnssim_https2_init(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn is nil");
- mlassert(conn->tls == NULL, "conn already has tls context");
- mlassert(conn->http2 == NULL, "conn already has http2 context");
- mlassert(conn->client, "conn must be associated with a client");
- mlassert(conn->client->dnssim, "client must have dnssim");
-
- int ret = -1;
- nghttp2_session_callbacks* callbacks;
- nghttp2_option* option;
- output_dnssim_t* self = conn->client->dnssim;
-
- /* Initialize TLS session. */
- ret = _output_dnssim_tls_init(conn);
- if (ret < 0)
- return ret;
-
- /* Configure ALPN to negotiate HTTP/2. */
- const gnutls_datum_t protos[] = {
- { (unsigned char*)"h2", 2 }
- };
- ret = gnutls_alpn_set_protocols(conn->tls->session, protos, 1, 0);
- if (ret < 0) {
- lwarning("failed to set ALPN protocol: %s", gnutls_strerror(ret));
- return ret;
- }
-
- lfatal_oom(conn->http2 = calloc(1, sizeof(_output_dnssim_http2_ctx_t)));
- conn->http2->max_concurrent_streams = OUTPUT_DNSSIM_HTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
-
- /* Set up HTTP/2 callbacks and client. */
- lassert(nghttp2_session_callbacks_new(&callbacks) == 0, "out of memory");
- nghttp2_session_callbacks_set_send_callback(callbacks, _http2_send);
- nghttp2_session_callbacks_set_on_header_callback(callbacks, _http2_on_header);
- nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, _http2_on_data_recv);
- nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, _http2_on_frame_recv);
- nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, _http2_on_stream_close);
-
- lassert(nghttp2_option_new(&option) == 0, "out of memory");
- nghttp2_option_set_peer_max_concurrent_streams(option, conn->http2->max_concurrent_streams);
-
- ret = nghttp2_session_client_new2(&conn->http2->session, callbacks, conn, option);
-
- nghttp2_session_callbacks_del(callbacks);
- nghttp2_option_del(option);
-
- if (ret < 0) {
- free(conn->http2);
- conn->http2 = NULL;
- }
-
- return ret;
-}
-
-int _output_dnssim_https2_setup(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn is nil");
- mlassert(conn->tls, "conn must have tls ctx");
- mlassert(conn->tls->session, "conn must have tls session");
- mlassert(conn->http2, "conn must have http2 ctx");
- mlassert(conn->http2->session, "conn must have http2 session");
-
- int ret = -1;
-
- /* Check "h2" protocol was negotiated with ALPN. */
- gnutls_datum_t proto;
- ret = gnutls_alpn_get_selected_protocol(conn->tls->session, &proto);
- if (ret < 0) {
- mlwarning("http2: failed to get negotiated protocol: %s", gnutls_strerror(ret));
- return ret;
- }
- if (proto.size != 2 || memcmp("h2", proto.data, 2) != 0) {
- mlwarning("http2: protocol is not negotiated");
- return ret;
- }
-
- /* Submit SETTIGNS frame. */
- static const nghttp2_settings_entry iv[] = {
- { NGHTTP2_SETTINGS_MAX_FRAME_SIZE, MAX_DNSMSG_SIZE },
- { NGHTTP2_SETTINGS_ENABLE_PUSH, 0 }, /* Only we can initiate streams. */
- };
- ret = nghttp2_submit_settings(conn->http2->session, NGHTTP2_FLAG_NONE, iv, sizeof(iv) / sizeof(*iv));
- if (ret < 0) {
- mlwarning("http2: failed to submit SETTINGS: %s", nghttp2_strerror(ret));
- return ret;
- }
-
- ret = 0;
- return ret;
-}
-
-void _output_dnssim_https2_process_input_data(_output_dnssim_connection_t* conn, size_t len, const char* data)
-{
- mlassert(conn, "conn is nil");
- mlassert(conn->http2, "conn must have http2 ctx");
- mlassert(conn->http2->session, "conn must have http2 session");
-
- /* Process incoming frames. */
- ssize_t ret = 0;
- conn->prevent_close = true;
- ret = nghttp2_session_mem_recv(conn->http2->session, (uint8_t*)data, len);
- conn->prevent_close = false;
- if (ret < 0) {
- mlwarning("failed nghttp2_session_mem_recv: %s", nghttp2_strerror(ret));
- _output_dnssim_conn_close(conn);
- return;
- } else if (conn->state == _OUTPUT_DNSSIM_CONN_CLOSE_REQUESTED) {
- _output_dnssim_conn_close(conn);
- return;
- }
- mlassert(ret == len, "nghttp2_session_mem_recv didn't process all data");
-
- /* Send any frames the read might have triggered. */
- ret = nghttp2_session_send(conn->http2->session);
- if (ret < 0) {
- mlwarning("failed nghttp2_session_send: %s", nghttp2_strerror(ret));
- _output_dnssim_conn_close(conn);
- return;
- }
-}
-
-int _output_dnssim_create_query_https2(output_dnssim_t* self, _output_dnssim_request_t* req)
-{
- mlassert_self();
- lassert(req, "req is nil");
- lassert(req->client, "request must have a client associated with it");
-
- _output_dnssim_query_tcp_t* qry;
-
- lfatal_oom(qry = calloc(1, sizeof(_output_dnssim_query_tcp_t)));
-
- qry->qry.transport = OUTPUT_DNSSIM_TRANSPORT_HTTPS2;
- qry->qry.req = req;
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_PENDING_WRITE;
- qry->stream_id = -1;
- req->qry = &qry->qry; // TODO change when adding support for multiple Qs for req
- _ll_append(req->client->pending, &qry->qry);
-
- return _output_dnssim_handle_pending_queries(req->client);
-}
-
-void _output_dnssim_close_query_https2(_output_dnssim_query_tcp_t* qry)
-{
- mlassert(qry, "qry can't be null");
- mlassert(qry->qry.req, "query must be part of a request");
- _output_dnssim_request_t* req = qry->qry.req;
- mlassert(req->client, "request must belong to a client");
-
- _ll_try_remove(req->client->pending, &qry->qry);
- if (qry->conn) {
- _output_dnssim_connection_t* conn = qry->conn;
- _ll_try_remove(conn->sent, &qry->qry);
- qry->conn = NULL;
- _output_dnssim_conn_idle(conn);
- }
-
- if (qry->recv_buf != NULL)
- free(qry->recv_buf);
-
- _ll_remove(req->qry, &qry->qry);
- free(qry);
-}
-
-void _output_dnssim_https2_close(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn can't be nil");
- mlassert(conn->http2, "conn must have http2 ctx");
-
- nghttp2_session_del(conn->http2->session);
- _output_dnssim_tls_close(conn);
-}
-
-static int _http2_send_query_get(_output_dnssim_connection_t* conn, _output_dnssim_query_tcp_t* qry)
-{
- mlassert(conn, "conn can't be null");
- mlassert(qry, "qry can't be null");
- mlassert(qry->qry.req, "req can't be null");
- mlassert(qry->qry.req->payload, "payload can't be null");
- mlassert(qry->qry.req->payload->len <= MAX_DNSMSG_SIZE, "payload too big");
- mlassert(conn->client, "conn must be associated with client");
- mlassert(conn->client->dnssim, "client must have dnssim");
-
- output_dnssim_t* self = conn->client->dnssim;
- core_object_payload_t* content = qry->qry.req->payload;
-
- const size_t uri_path_len = strlen(_self->h2_uri_path);
- const size_t path_len = uri_path_len
- + OUTPUT_DNSSIM_HTTP_GET_TEMPLATE_LEN
- + (content->len * 4) / 3 + 3 /* upper limit of base64 encoding */
- + 1; /* terminating null byte */
- if (path_len >= _MAX_URI_LEN) {
- self->discarded++;
- linfo("http2: uri path with query too long, query discarded");
- return 0;
- }
- char path[path_len];
- memcpy(path, _self->h2_uri_path, uri_path_len);
- memcpy(&path[uri_path_len], OUTPUT_DNSSIM_HTTP_GET_TEMPLATE, OUTPUT_DNSSIM_HTTP_GET_TEMPLATE_LEN);
-
- int32_t ret = base64url_encode(content->payload, content->len,
- (uint8_t*)&path[uri_path_len + OUTPUT_DNSSIM_HTTP_GET_TEMPLATE_LEN],
- sizeof(path) - uri_path_len - OUTPUT_DNSSIM_HTTP_GET_TEMPLATE_LEN - 1);
- if (ret < 0) {
- self->discarded++;
- linfo("http2: base64url encode of query failed, query discarded");
- return 0;
- }
-
- nghttp2_nv hdrs[] = {
- OUTPUT_DNSSIM_MAKE_NV2(":method", "GET"),
- OUTPUT_DNSSIM_MAKE_NV2(":scheme", "https"),
- OUTPUT_DNSSIM_MAKE_NV(":authority", _self->h2_uri_authority, strlen(_self->h2_uri_authority)),
- OUTPUT_DNSSIM_MAKE_NV(":path", path, uri_path_len + sizeof(OUTPUT_DNSSIM_HTTP_GET_TEMPLATE) - 1 + ret),
- OUTPUT_DNSSIM_MAKE_NV2("accept", "application/dns-message"),
- };
-
- qry->stream_id = nghttp2_submit_request(conn->http2->session, NULL, hdrs, sizeof(hdrs) / sizeof(nghttp2_nv), NULL, NULL);
-
- if (qry->stream_id < 0) {
- mldebug("http2 (%p): failed to submit request: %s", conn->http2->session, nghttp2_strerror(qry->stream_id));
- return -1;
- }
- mldebug("http2 (%p): GET %s", conn->http2->session, path);
- conn->http2->open_streams++;
- _http2_check_max_streams(conn);
-
- ret = nghttp2_session_send(conn->http2->session);
- if (ret < 0) {
- mldebug("http2 (%p): failed session send: %s", conn->http2->session, nghttp2_strerror(ret));
- return -1;
- }
-
- return 0;
-}
-
-static int _http2_send_query_post(_output_dnssim_connection_t* conn, _output_dnssim_query_tcp_t* qry)
-{
- mlassert(conn, "conn can't be null");
- mlassert(qry, "qry can't be null");
- mlassert(qry->qry.req, "req can't be null");
- mlassert(qry->qry.req->payload, "payload can't be null");
- mlassert(qry->qry.req->payload->len <= MAX_DNSMSG_SIZE, "payload too big");
- mlassert(conn->client, "conn must be associated with client");
- mlassert(conn->client->dnssim, "client must have dnssim");
-
- output_dnssim_t* self = conn->client->dnssim;
-
- core_object_payload_t* content = qry->qry.req->payload;
-
- int window_size = nghttp2_session_get_remote_window_size(conn->http2->session);
- if (content->len > window_size) {
- mldebug("http2 (%p): insufficient remote window size, deferring", conn->http2->session);
- return 0;
- }
-
- char content_length[6]; /* max dnslen "65535" */
- int content_length_len = snprintf(content_length, 6, "%zd", content->len);
-
- nghttp2_nv hdrs[] = {
- OUTPUT_DNSSIM_MAKE_NV2(":method", "POST"),
- OUTPUT_DNSSIM_MAKE_NV2(":scheme", "https"),
- OUTPUT_DNSSIM_MAKE_NV(":authority", _self->h2_uri_authority, strlen(_self->h2_uri_authority)),
- OUTPUT_DNSSIM_MAKE_NV(":path", _self->h2_uri_path, strlen(_self->h2_uri_path)),
- OUTPUT_DNSSIM_MAKE_NV2("accept", "application/dns-message"),
- OUTPUT_DNSSIM_MAKE_NV2("content-type", "application/dns-message"),
- OUTPUT_DNSSIM_MAKE_NV("content-length", content_length, content_length_len)
- };
-
- _output_dnssim_https2_data_provider_t data = {
- .buf = content->payload,
- .len = content->len
- };
-
- nghttp2_data_provider data_provider = {
- .source.ptr = &data,
- .read_callback = _http2_on_data_provider_read
- };
-
- qry->stream_id = nghttp2_submit_request(conn->http2->session, NULL, hdrs, sizeof(hdrs) / sizeof(nghttp2_nv), &data_provider, NULL);
-
- if (qry->stream_id < 0) {
- mldebug("http2 (%p): failed to submit request: %s", conn->http2->session, nghttp2_strerror(qry->stream_id));
- return -1;
- }
- mldebug("http2 (%p): POST payload len=%ld", conn->http2->session, content->len);
- conn->http2->open_streams++;
- _http2_check_max_streams(conn);
-
- window_size = nghttp2_session_get_stream_remote_window_size(conn->http2->session, qry->stream_id);
- mlassert(content->len <= window_size,
- "unsupported: http2 stream window size (%ld B) is smaller than dns payload (%ld B)",
- window_size, content->len);
-
- int ret = nghttp2_session_send(conn->http2->session);
- if (ret < 0) {
- mldebug("http2 (%p): failed session send: %s", conn->http2->session, nghttp2_strerror(ret));
- return -1;
- }
-
- return 0;
-}
-
-void _output_dnssim_https2_write_query(_output_dnssim_connection_t* conn, _output_dnssim_query_tcp_t* qry)
-{
- mlassert(qry, "qry can't be null");
- mlassert(qry->qry.state == _OUTPUT_DNSSIM_QUERY_PENDING_WRITE, "qry must be pending write");
- mlassert(conn, "conn can't be null");
- mlassert(conn->state == _OUTPUT_DNSSIM_CONN_ACTIVE, "connection state != ACTIVE");
- mlassert(conn->http2, "conn must have http2 ctx");
- mlassert(conn->http2->session, "conn must have http2 session");
- mlassert(conn->client, "conn must be associated with client");
- mlassert(conn->client->pending, "conn has no pending queries");
- mlassert(conn->client->dnssim, "client must have dnssim");
-
- int ret = 0;
- output_dnssim_t* self = conn->client->dnssim;
-
- if (!nghttp2_session_check_request_allowed(conn->http2->session)) {
- mldebug("http2 (%p): request not allowed", conn->http2->session);
- _output_dnssim_conn_close(conn);
- return;
- }
-
- switch (_self->h2_method) {
- case OUTPUT_DNSSIM_H2_POST:
- ret = _http2_send_query_post(conn, qry);
- break;
- case OUTPUT_DNSSIM_H2_GET:
- ret = _http2_send_query_get(conn, qry);
- break;
- default:
- lfatal("http2: unsupported method");
- }
-
- if (ret < 0) {
- _output_dnssim_conn_close(conn);
- return;
- }
-
- qry->conn = conn;
- _ll_remove(conn->client->pending, &qry->qry);
- _ll_append(conn->sent, &qry->qry);
-
- /* Stop idle timer, since there are queries to answer now. */
- if (conn->idle_timer != NULL) {
- conn->is_idle = false;
- uv_timer_stop(conn->idle_timer);
- }
-
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_SENT;
-}
-
-#endif
diff --git a/src/output/dnssim/internal.h b/src/output/dnssim/internal.h
deleted file mode 100644
index b9feddf..0000000
--- a/src/output/dnssim/internal.h
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * 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/>.
- */
-
-#ifndef __dnsjit_output_dnssim_internal_h
-#define __dnsjit_output_dnssim_internal_h
-
-#include <gnutls/gnutls.h>
-#include <nghttp2/nghttp2.h>
-#include <uv.h>
-#include "core/object/dns.h"
-#include "core/object/payload.h"
-
-#define DNSSIM_MIN_GNUTLS_VERSION 0x030603
-#define DNSSIM_MIN_GNUTLS_ERRORMSG "dnssim tls/https2 transport requires GnuTLS >= 3.6.3"
-
-#define _self ((_output_dnssim_t*)self)
-#define _ERR_MALFORMED -2
-#define _ERR_MSGID -3
-#define _ERR_TC -4
-#define _ERR_QUESTION -5
-
-#define _MAX_URI_LEN 65536
-#define MAX_DNSMSG_SIZE 65535
-#define WIRE_BUF_SIZE (MAX_DNSMSG_SIZE + 2 + 16384) /** max tcplen + 2b tcplen + 16kb tls record */
-
-typedef struct _output_dnssim_request _output_dnssim_request_t;
-typedef struct _output_dnssim_connection _output_dnssim_connection_t;
-typedef struct _output_dnssim_client _output_dnssim_client_t;
-
-/*
- * Query-related structures.
- */
-
-typedef struct _output_dnssim_query _output_dnssim_query_t;
-struct _output_dnssim_query {
- /*
- * Next query in the list.
- *
- * Currently, next is used for TCP clients/connection, which makes it
- * impossible to use for tracking multiple queries of a single request.
- *
- * TODO: refactor the linked lists to allow query to be part of multiple lists
- */
- _output_dnssim_query_t* next;
-
- output_dnssim_transport_t transport;
- _output_dnssim_request_t* req;
-
- /* Query state, currently used only for TCP. */
- enum {
- _OUTPUT_DNSSIM_QUERY_PENDING_WRITE,
- _OUTPUT_DNSSIM_QUERY_PENDING_WRITE_CB,
- _OUTPUT_DNSSIM_QUERY_PENDING_CLOSE,
- _OUTPUT_DNSSIM_QUERY_WRITE_FAILED,
- _OUTPUT_DNSSIM_QUERY_SENT,
- _OUTPUT_DNSSIM_QUERY_ORPHANED
- } state;
-};
-
-typedef struct _output_dnssim_query_udp _output_dnssim_query_udp_t;
-struct _output_dnssim_query_udp {
- _output_dnssim_query_t qry;
-
- uv_udp_t* handle;
- uv_buf_t buf;
-};
-
-typedef struct _output_dnssim_query_tcp _output_dnssim_query_tcp_t;
-struct _output_dnssim_query_tcp {
- _output_dnssim_query_t qry;
-
- /* Connection this query is assigned to. */
- _output_dnssim_connection_t* conn;
-
- uv_write_t write_req;
-
- /* Send buffers for libuv; 0 is for dnslen, 1 is for dnsmsg. */
- uv_buf_t bufs[2];
-
- /* HTTP/2 stream id that was used to send this query. */
- int32_t stream_id;
-
- /* HTTP/2 expected content length. */
- int32_t content_len;
-
- /* Receive buffer (currently used only by HTTP/2). */
- uint8_t* recv_buf;
- ssize_t recv_buf_len;
-};
-
-struct _output_dnssim_request {
- /* List of queries associated with this request. */
- _output_dnssim_query_t* qry;
-
- /* Client this request belongs to. */
- _output_dnssim_client_t* client;
-
- /* The DNS question to be resolved. */
- core_object_payload_t* payload;
- core_object_dns_t* dns_q;
- const uint8_t* question;
- ssize_t question_len;
-
- /* Timestamps for latency calculation. */
- uint64_t created_at;
- uint64_t ended_at;
-
- /* Timer for tracking timeout of the request. */
- uv_timer_t* timer;
-
- /* The output component of this request. */
- output_dnssim_t* dnssim;
-
- /* State of the request. */
- enum {
- _OUTPUT_DNSSIM_REQ_ONGOING,
- _OUTPUT_DNSSIM_REQ_CLOSING
- } state;
-
- /* Statistics interval in which this request is tracked. */
- output_dnssim_stats_t* stats;
-};
-
-/*
- * Connection-related structures.
- */
-
-/* Read-state of connection's data stream. */
-typedef enum _output_dnssim_read_state {
- _OUTPUT_DNSSIM_READ_STATE_CLEAN,
- _OUTPUT_DNSSIM_READ_STATE_DNSLEN, /* Expecting bytes of dnslen. */
- _OUTPUT_DNSSIM_READ_STATE_DNSMSG, /* Expecting bytes of dnsmsg. */
- _OUTPUT_DNSSIM_READ_STATE_INVALID
-} _output_dnssim_read_state_t;
-
-/* TLS-related data for a single connection. */
-typedef struct _output_dnssim_tls_ctx {
- gnutls_session_t session;
- uint8_t* buf;
- ssize_t buf_len;
- ssize_t buf_pos;
- size_t write_queue_size;
-} _output_dnssim_tls_ctx_t;
-
-/* HTTP2 context for a single connection. */
-typedef struct _output_dnssim_http2_ctx {
- nghttp2_session* session;
-
- /* Query to which the dnsbuf currently being processed belongs to. */
- _output_dnssim_query_tcp_t* current_qry;
-
- /* Maximum number of concurrent and currently open streams. */
- uint32_t max_concurrent_streams;
- uint32_t open_streams;
-
- /* Flag indicating whether we received the peer's initial SETTINGS frame. */
- bool remote_settings_received;
-} _output_dnssim_http2_ctx_t;
-
-struct _output_dnssim_connection {
- _output_dnssim_connection_t* next;
-
- uv_tcp_t* handle;
-
- /* Timeout timer for establishing the connection. */
- uv_timer_t* handshake_timer;
-
- /* Idle timer for connection reuse. rfc7766#section-6.2.3 */
- uv_timer_t* idle_timer;
- bool is_idle;
-
- /* List of queries that have been queued (pending write callback). */
- _output_dnssim_query_t* queued;
-
- /* List of queries that have been sent over this connection. */
- _output_dnssim_query_t* sent;
-
- /* Client this connection belongs to. */
- _output_dnssim_client_t* client;
-
- /* State of the connection.
- * Numeric ordering of constants is significant and follows the typical connection lifecycle.
- * Ensure new states are added to a proper place. */
- enum {
- _OUTPUT_DNSSIM_CONN_INITIALIZED = 0,
- _OUTPUT_DNSSIM_CONN_TCP_HANDSHAKE = 10,
- _OUTPUT_DNSSIM_CONN_TLS_HANDSHAKE = 20,
- _OUTPUT_DNSSIM_CONN_ACTIVE = 30,
- _OUTPUT_DNSSIM_CONN_CONGESTED = 35,
- _OUTPUT_DNSSIM_CONN_CLOSE_REQUESTED = 38,
- _OUTPUT_DNSSIM_CONN_CLOSING = 40,
- _OUTPUT_DNSSIM_CONN_CLOSED = 50
- } state;
-
- /* State of the data stream read. */
- _output_dnssim_read_state_t read_state;
-
- /* Total length of the expected dns data (either 2 for dnslen, or dnslen itself). */
- size_t dnsbuf_len;
-
- /* Current position in the receive dns buffer. */
- size_t dnsbuf_pos;
-
- /* Receive buffer used for incomplete messages or dnslen. */
- char* dnsbuf_data;
- bool dnsbuf_free_after_use;
-
- /* Statistics interval in which the handshake is tracked. */
- output_dnssim_stats_t* stats;
-
- /* TLS-related data. */
- _output_dnssim_tls_ctx_t* tls;
-
- /* HTTP/2-related data. */
- _output_dnssim_http2_ctx_t* http2;
-
- /* Prevents immediate closure of connection. Instead, connection is moved
- * to CLOSE_REQUESTED state and setter of this flag is responsible for
- * closing the connection when clearing this flag. */
- bool prevent_close;
-};
-
-/*
- * Client structure.
- */
-
-struct _output_dnssim_client {
- /* Dnssim component this client belongs to. */
- output_dnssim_t* dnssim;
-
- /* List of connections.
- * Multiple connections may be used (e.g. some are already closed for writing).
- */
- _output_dnssim_connection_t* conn;
-
- /* List of queries that are pending to be sent over any available connection. */
- _output_dnssim_query_t* pending;
-
- /* TLS-ticket for session resumption. */
- gnutls_datum_t tls_ticket;
-};
-
-/*
- * DnsSim-related structures.
- */
-
-typedef struct _output_dnssim_source _output_dnssim_source_t;
-struct _output_dnssim_source {
- _output_dnssim_source_t* next;
- struct sockaddr_storage addr;
-};
-
-typedef struct _output_dnssim _output_dnssim_t;
-struct _output_dnssim {
- output_dnssim_t pub;
-
- uv_loop_t loop;
- uv_timer_t stats_timer;
-
- struct sockaddr_storage target;
- _output_dnssim_source_t* source;
- output_dnssim_transport_t transport;
-
- char h2_uri_authority[_MAX_URI_LEN];
- char h2_uri_path[_MAX_URI_LEN];
- bool h2_zero_out_msgid;
- output_dnssim_h2_method_t h2_method;
-
- /* Array of clients, mapped by client ID (ranges from 0 to max_clients). */
- _output_dnssim_client_t* client_arr;
-
- gnutls_priority_t* tls_priority;
- gnutls_certificate_credentials_t tls_cred;
- char wire_buf[WIRE_BUF_SIZE]; /* thread-local buffer for processing tls input */
-};
-
-/* Provides data for HTTP/2 data frames. */
-typedef struct {
- const uint8_t* buf;
- size_t len;
-} _output_dnssim_https2_data_provider_t;
-
-/*
- * Forward function declarations.
- */
-
-int _output_dnssim_bind_before_connect(output_dnssim_t* self, uv_handle_t* handle);
-int _output_dnssim_create_query_udp(output_dnssim_t* self, _output_dnssim_request_t* req);
-int _output_dnssim_create_query_tcp(output_dnssim_t* self, _output_dnssim_request_t* req);
-void _output_dnssim_close_query_udp(_output_dnssim_query_udp_t* qry);
-void _output_dnssim_close_query_tcp(_output_dnssim_query_tcp_t* qry);
-int _output_dnssim_answers_request(_output_dnssim_request_t* req, core_object_dns_t* response);
-void _output_dnssim_request_answered(_output_dnssim_request_t* req, core_object_dns_t* msg);
-void _output_dnssim_maybe_free_request(_output_dnssim_request_t* req);
-void _output_dnssim_on_uv_alloc(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
-void _output_dnssim_create_request(output_dnssim_t* self, _output_dnssim_client_t* client, core_object_payload_t* payload);
-int _output_dnssim_handle_pending_queries(_output_dnssim_client_t* client);
-int _output_dnssim_tcp_connect(output_dnssim_t* self, _output_dnssim_connection_t* conn);
-void _output_dnssim_tcp_close(_output_dnssim_connection_t* conn);
-void _output_dnssim_tcp_write_query(_output_dnssim_connection_t* conn, _output_dnssim_query_tcp_t* qry);
-void _output_dnssim_conn_close(_output_dnssim_connection_t* conn);
-void _output_dnssim_conn_idle(_output_dnssim_connection_t* conn);
-int _output_dnssim_handle_pending_queries(_output_dnssim_client_t* client);
-void _output_dnssim_conn_activate(_output_dnssim_connection_t* conn);
-void _output_dnssim_conn_maybe_free(_output_dnssim_connection_t* conn);
-void _output_dnssim_read_dns_stream(_output_dnssim_connection_t* conn, size_t len, const char* data);
-void _output_dnssim_read_dnsmsg(_output_dnssim_connection_t* conn, size_t len, const char* data);
-
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
-int _output_dnssim_create_query_tls(output_dnssim_t* self, _output_dnssim_request_t* req);
-void _output_dnssim_close_query_tls(_output_dnssim_query_tcp_t* qry);
-int _output_dnssim_tls_init(_output_dnssim_connection_t* conn);
-void _output_dnssim_tls_process_input_data(_output_dnssim_connection_t* conn);
-void _output_dnssim_tls_close(_output_dnssim_connection_t* conn);
-void _output_dnssim_tls_write_query(_output_dnssim_connection_t* conn, _output_dnssim_query_tcp_t* qry);
-
-int _output_dnssim_create_query_https2(output_dnssim_t* self, _output_dnssim_request_t* req);
-void _output_dnssim_close_query_https2(_output_dnssim_query_tcp_t* qry);
-int _output_dnssim_https2_init(_output_dnssim_connection_t* conn);
-int _output_dnssim_https2_setup(_output_dnssim_connection_t* conn);
-void _output_dnssim_https2_process_input_data(_output_dnssim_connection_t* conn, size_t len, const char* data);
-void _output_dnssim_https2_close(_output_dnssim_connection_t* conn);
-void _output_dnssim_https2_write_query(_output_dnssim_connection_t* conn, _output_dnssim_query_tcp_t* qry);
-#endif
-
-#endif
diff --git a/src/output/dnssim/ll.h b/src/output/dnssim/ll.h
deleted file mode 100644
index 8e0b07a..0000000
--- a/src/output/dnssim/ll.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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/>.
- */
-
-#ifndef __dnsjit_output_dnssim_ll_h
-#define __dnsjit_output_dnssim_ll_h
-
-#include "core/assert.h"
-
-/* Utility macros for linked list structures.
- *
- * - "list" is the pointer to the first node of the linked list
- * - "list" can be NULL if there are no nodes
- * - every node has "next", which points to the next node (can be NULL)
- */
-
-/* Append a node to the list.
- *
- * Only a single node can be appended - node->next must be NULL.
- */
-#define _ll_append(list, node) \
- { \
- glassert((node)->next == NULL, "node->next must be null when appending"); \
- if ((list) == NULL) \
- (list) = (node); \
- else if ((node) != NULL) { \
- typeof(list) _current = (list); \
- while (_current->next != NULL) \
- _current = _current->next; \
- _current->next = node; \
- } \
- }
-
-/* Remove a node from the list.
- *
- * In strict mode, the node must be present in the list.
- */
-#define _ll_remove_template(list, node, strict) \
- { \
- if (strict) \
- glassert((list), "list can't be null when removing nodes"); \
- if ((list) != NULL && (node) != NULL) { \
- if ((list) == (node)) { \
- (list) = (node)->next; \
- (node)->next = NULL; \
- } else { \
- typeof(list) _current = (list); \
- while (_current != NULL && _current->next != (node)) { \
- if (strict) \
- glassert((_current->next), "list doesn't contain the node to be removed"); \
- _current = _current->next; \
- } \
- if (_current != NULL) { \
- _current->next = (node)->next; \
- (node)->next = NULL; \
- } \
- } \
- } \
- }
-
-/* Remove a node from the list. */
-#define _ll_remove(list, node) _ll_remove_template((list), (node), true)
-
-/* Remove a node from the list if it's present. */
-#define _ll_try_remove(list, node) _ll_remove_template((list), (node), false)
-
-#endif
diff --git a/src/output/dnssim/tcp.c b/src/output/dnssim/tcp.c
deleted file mode 100644
index 1f2b619..0000000
--- a/src/output/dnssim/tcp.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * 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>
-
-static core_log_t _log = LOG_T_INIT("output.dnssim");
-
-static void _move_queries_to_pending(_output_dnssim_query_tcp_t* qry)
-{
- _output_dnssim_query_tcp_t* qry_tmp;
- while (qry != NULL) {
- mlassert(qry->conn, "query must be associated with conn");
- mlassert(qry->conn->state == _OUTPUT_DNSSIM_CONN_CLOSED, "conn must be closed");
- mlassert(qry->conn->client, "conn must be associated with client");
- qry_tmp = (_output_dnssim_query_tcp_t*)qry->qry.next;
- qry->qry.next = NULL;
- _ll_append(qry->conn->client->pending, &qry->qry);
- qry->conn = NULL;
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_ORPHANED;
- qry->stream_id = -1;
- qry->recv_buf_len = 0;
- if (qry->recv_buf != NULL) {
- free(qry->recv_buf);
- qry->recv_buf = NULL;
- }
- qry = qry_tmp;
- }
-}
-
-static void _on_tcp_closed(uv_handle_t* handle)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)handle->data;
- mlassert(conn, "conn is nil");
- conn->state = _OUTPUT_DNSSIM_CONN_CLOSED;
-
- /* Orphan any queries that are still unresolved. */
- _move_queries_to_pending((_output_dnssim_query_tcp_t*)conn->queued);
- conn->queued = NULL;
- _move_queries_to_pending((_output_dnssim_query_tcp_t*)conn->sent);
- conn->sent = NULL;
-
- /* TODO Improve client re-connect behavior in case the connection fails to
- * establish. Currently, queries are orphaned and attempted to be re-sent
- * along with the next query that triggers a new connection.
- *
- * Attempting to establish new connection immediately leads to performance
- * issues if the number of these attempts doesn't have upper limit. */
- ///* Ensure orhpaned queries are re-sent over a different connection. */
- //if (_output_dnssim_handle_pending_queries(conn->client) != 0)
- // mlinfo("tcp: orphaned queries failed to be re-sent");
-
- mlassert(conn->handle, "conn must have tcp handle when closing it");
- free(conn->handle);
- conn->handle = NULL;
- _output_dnssim_conn_maybe_free(conn);
-}
-
-static void _on_tcp_query_written(uv_write_t* wr_req, int status)
-{
- _output_dnssim_query_tcp_t* qry = (_output_dnssim_query_tcp_t*)wr_req->data;
- mlassert(qry, "qry/wr_req->data is nil");
- mlassert(qry->conn, "query must be associated with connection");
- _output_dnssim_connection_t* conn = qry->conn;
-
- free(((_output_dnssim_query_tcp_t*)qry)->bufs[0].base);
-
- if (qry->qry.state == _OUTPUT_DNSSIM_QUERY_PENDING_CLOSE) {
- qry->qry.state = status < 0 ? _OUTPUT_DNSSIM_QUERY_WRITE_FAILED : _OUTPUT_DNSSIM_QUERY_SENT;
- _output_dnssim_request_t* req = qry->qry.req;
- _output_dnssim_close_query_tcp(qry);
- _output_dnssim_maybe_free_request(req);
- qry = NULL;
- }
-
- if (status < 0) {
- if (status != UV_ECANCELED)
- mlinfo("tcp write failed: %s", uv_strerror(status));
- if (qry != NULL)
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_WRITE_FAILED;
- _output_dnssim_conn_close(conn);
- return;
- }
-
- if (qry == NULL)
- return;
-
- /* Mark query as sent and assign it to connection. */
- mlassert(qry->qry.state == _OUTPUT_DNSSIM_QUERY_PENDING_WRITE_CB, "invalid query state");
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_SENT;
- if (qry->conn->state == _OUTPUT_DNSSIM_CONN_ACTIVE) {
- mlassert(qry->conn->queued, "conn has no queued queries");
- _ll_remove(qry->conn->queued, &qry->qry);
- _ll_append(qry->conn->sent, &qry->qry);
- }
-}
-
-void _output_dnssim_tcp_write_query(_output_dnssim_connection_t* conn, _output_dnssim_query_tcp_t* qry)
-{
- mlassert(qry, "qry can't be null");
- mlassert(qry->qry.state == _OUTPUT_DNSSIM_QUERY_PENDING_WRITE, "qry must be pending write");
- mlassert(qry->qry.req, "req can't be null");
- mlassert(qry->qry.req->dns_q, "dns_q can't be null");
- mlassert(qry->qry.req->dns_q->obj_prev, "payload can't be null");
- mlassert(conn, "conn can't be null");
- mlassert(conn->state == _OUTPUT_DNSSIM_CONN_ACTIVE, "connection state != ACTIVE");
- mlassert(conn->client, "conn must be associated with client");
- mlassert(conn->client->pending, "conn has no pending queries");
-
- mldebug("tcp write dnsmsg id: %04x", qry->qry.req->dns_q->id);
-
- core_object_payload_t* payload = (core_object_payload_t*)qry->qry.req->dns_q->obj_prev;
- uint16_t* len;
- mlfatal_oom(len = malloc(sizeof(uint16_t)));
- *len = htons(payload->len);
- qry->bufs[0] = uv_buf_init((char*)len, 2);
- qry->bufs[1] = uv_buf_init((char*)payload->payload, payload->len);
-
- qry->conn = conn;
- _ll_remove(conn->client->pending, &qry->qry);
- _ll_append(conn->queued, &qry->qry);
-
- /* Stop idle timer, since there are queries to answer now. */
- if (conn->idle_timer != NULL) {
- conn->is_idle = false;
- uv_timer_stop(conn->idle_timer);
- }
-
- qry->write_req.data = (void*)qry;
- uv_write(&qry->write_req, (uv_stream_t*)conn->handle, qry->bufs, 2, _on_tcp_query_written);
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_PENDING_WRITE_CB;
-}
-
-static void _on_tcp_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)handle->data;
- output_dnssim_t* self = conn->client->dnssim;
-
- if (nread > 0) {
- mldebug("tcp nread: %d", nread);
- switch (_self->transport) {
- case OUTPUT_DNSSIM_TRANSPORT_TCP:
- _output_dnssim_read_dns_stream(conn, nread, buf->base);
- break;
- case OUTPUT_DNSSIM_TRANSPORT_TLS:
- case OUTPUT_DNSSIM_TRANSPORT_HTTPS2:
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- mlassert(conn->tls, "con must have tls ctx");
- conn->tls->buf = (uint8_t*)buf->base;
- conn->tls->buf_pos = 0;
- conn->tls->buf_len = nread;
- _output_dnssim_tls_process_input_data(conn);
-#else
- mlfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- break;
- default:
- mlfatal("unsupported transport");
- break;
- }
- } else if (nread < 0) {
- if (nread != UV_EOF)
- mlinfo("tcp conn unexpected close: %s", uv_strerror(nread));
- _output_dnssim_conn_close(conn);
- }
-
- if (buf->base != NULL)
- free(buf->base);
-}
-
-static void _on_tcp_connected(uv_connect_t* conn_req, int status)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)conn_req->handle->data;
- mlassert(conn, "conn is nil");
-
- free(conn_req);
-
- if (status < 0) {
- mldebug("tcp connect failed: %s", uv_strerror(status));
- _output_dnssim_conn_close(conn);
- return;
- }
-
- mlassert(conn->state == _OUTPUT_DNSSIM_CONN_TCP_HANDSHAKE, "connection state != TCP_HANDSHAKE");
- int ret = uv_read_start((uv_stream_t*)conn->handle, _output_dnssim_on_uv_alloc, _on_tcp_read);
- if (ret < 0) {
- mlwarning("tcp uv_read_start() failed: %s", uv_strerror(ret));
- _output_dnssim_conn_close(conn);
- return;
- }
-
- mldebug("tcp connected");
- mlassert(conn->client, "conn must be associated with a client");
- mlassert(conn->client->dnssim, "client must be associated with dnssim");
- output_dnssim_t* self = conn->client->dnssim;
- switch (_self->transport) {
- case OUTPUT_DNSSIM_TRANSPORT_TCP:
- _output_dnssim_conn_activate(conn);
- break;
- case OUTPUT_DNSSIM_TRANSPORT_TLS:
- case OUTPUT_DNSSIM_TRANSPORT_HTTPS2:
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
- mldebug("init tls handshake");
- _output_dnssim_tls_process_input_data(conn); /* Initiate TLS handshake. */
-#else
- mlfatal(DNSSIM_MIN_GNUTLS_ERRORMSG);
-#endif
- break;
- default:
- lfatal("unsupported transport protocol");
- break;
- }
-}
-
-static void _on_connection_timeout(uv_timer_t* handle)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)handle->data;
- _output_dnssim_conn_close(conn);
-}
-
-int _output_dnssim_tcp_connect(output_dnssim_t* self, _output_dnssim_connection_t* conn)
-{
- mlassert_self();
- lassert(conn, "connection can't be null");
- lassert(conn->handle == NULL, "connection already has a handle");
- lassert(conn->handshake_timer == NULL, "connection already has a handshake timer");
- lassert(conn->idle_timer == NULL, "connection already has idle timer");
- lassert(conn->state == _OUTPUT_DNSSIM_CONN_INITIALIZED, "connection state != INITIALIZED");
-
- lfatal_oom(conn->handle = malloc(sizeof(uv_tcp_t)));
- conn->handle->data = (void*)conn;
- int ret = uv_tcp_init(&_self->loop, conn->handle);
- if (ret < 0) {
- lwarning("failed to init uv_tcp_t");
- goto failure;
- }
-
- ret = _output_dnssim_bind_before_connect(self, (uv_handle_t*)conn->handle);
- if (ret < 0)
- goto failure;
-
- /* Set connection parameters. */
- ret = uv_tcp_nodelay(conn->handle, 1);
- if (ret < 0)
- lwarning("tcp: failed to set TCP_NODELAY: %s", uv_strerror(ret));
-
- /* Set connection handshake timeout. */
- lfatal_oom(conn->handshake_timer = malloc(sizeof(uv_timer_t)));
- uv_timer_init(&_self->loop, conn->handshake_timer);
- conn->handshake_timer->data = (void*)conn;
- uv_timer_start(conn->handshake_timer, _on_connection_timeout, self->handshake_timeout_ms, 0);
-
- /* Set idle connection timer. */
- if (self->idle_timeout_ms > 0) {
- lfatal_oom(conn->idle_timer = malloc(sizeof(uv_timer_t)));
- uv_timer_init(&_self->loop, conn->idle_timer);
- conn->idle_timer->data = (void*)conn;
-
- /* Start and stop the timer to set the repeat value without running the timer. */
- uv_timer_start(conn->idle_timer, _on_connection_timeout, self->idle_timeout_ms, self->idle_timeout_ms);
- uv_timer_stop(conn->idle_timer);
- }
-
- mldebug("tcp connecting");
- uv_connect_t* conn_req;
- lfatal_oom(conn_req = malloc(sizeof(uv_connect_t)));
- ret = uv_tcp_connect(conn_req, conn->handle, (struct sockaddr*)&_self->target, _on_tcp_connected);
- if (ret < 0)
- goto failure;
-
- conn->stats->conn_handshakes++;
- conn->client->dnssim->stats_sum->conn_handshakes++;
- conn->state = _OUTPUT_DNSSIM_CONN_TCP_HANDSHAKE;
- return 0;
-failure:
- _output_dnssim_conn_close(conn);
- return ret;
-}
-
-void _output_dnssim_tcp_close(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn can't be nil");
-
- if (conn->handle != NULL) {
- uv_read_stop((uv_stream_t*)conn->handle);
- uv_close((uv_handle_t*)conn->handle, _on_tcp_closed);
- }
-}
-
-int _output_dnssim_create_query_tcp(output_dnssim_t* self, _output_dnssim_request_t* req)
-{
- mlassert_self();
- lassert(req, "req is nil");
- lassert(req->client, "request must have a client associated with it");
-
- _output_dnssim_query_tcp_t* qry;
-
- lfatal_oom(qry = calloc(1, sizeof(_output_dnssim_query_tcp_t)));
-
- qry->qry.transport = OUTPUT_DNSSIM_TRANSPORT_TCP;
- qry->qry.req = req;
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_PENDING_WRITE;
- req->qry = &qry->qry; // TODO change when adding support for multiple Qs for req
- _ll_append(req->client->pending, &qry->qry);
-
- return _output_dnssim_handle_pending_queries(req->client);
-}
-
-void _output_dnssim_close_query_tcp(_output_dnssim_query_tcp_t* qry)
-{
- mlassert(qry, "qry can't be null");
- mlassert(qry->qry.req, "query must be part of a request");
- _output_dnssim_request_t* req = qry->qry.req;
- mlassert(req->client, "request must belong to a client");
-
- if ((qry->qry.state == _OUTPUT_DNSSIM_QUERY_PENDING_WRITE_CB || qry->qry.state == _OUTPUT_DNSSIM_QUERY_PENDING_CLOSE)) {
- /* Query can't be freed until uv callback is called. */
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_PENDING_CLOSE;
- return;
- }
-
- _ll_try_remove(req->client->pending, &qry->qry);
- if (qry->conn) {
- _output_dnssim_connection_t* conn = qry->conn;
- _ll_try_remove(conn->queued, &qry->qry); /* edge-case of cancelled queries */
- _ll_try_remove(conn->sent, &qry->qry);
- qry->conn = NULL;
- _output_dnssim_conn_idle(conn);
- }
-
- _ll_remove(req->qry, &qry->qry);
- free(qry);
-}
diff --git a/src/output/dnssim/tls.c b/src/output/dnssim/tls.c
deleted file mode 100644
index e87ca47..0000000
--- a/src/output/dnssim/tls.c
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright (c) 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 <gnutls/gnutls.h>
-#include <string.h>
-
-#if GNUTLS_VERSION_NUMBER >= DNSSIM_MIN_GNUTLS_VERSION
-
-#ifndef MIN
-#define MIN(a, b) (((a) < (b)) ? (a) : (b)) /** Minimum of two numbers **/
-#endif
-
-static core_log_t _log = LOG_T_INIT("output.dnssim");
-
-struct async_write_ctx {
- uv_write_t write_req;
- _output_dnssim_connection_t* conn;
- char buf[];
-};
-
-static int _tls_handshake(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn is nil");
- mlassert(conn->tls, "conn must have tls context");
- mlassert(conn->client, "conn must belong to a client");
- mlassert(conn->state <= _OUTPUT_DNSSIM_CONN_TLS_HANDSHAKE, "conn in invalid state");
-
- /* Set TLS session resumption ticket if available. */
- if (conn->state < _OUTPUT_DNSSIM_CONN_TLS_HANDSHAKE && conn->client->tls_ticket.size != 0) {
- gnutls_datum_t* ticket = &conn->client->tls_ticket;
- gnutls_session_set_data(conn->tls->session, ticket->data, ticket->size);
- }
- conn->state = _OUTPUT_DNSSIM_CONN_TLS_HANDSHAKE;
-
- return gnutls_handshake(conn->tls->session);
-}
-
-void _output_dnssim_tls_process_input_data(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn is nil");
- mlassert(conn->client, "conn must have client");
- mlassert(conn->client->dnssim, "client must have dnssim");
- mlassert(conn->tls, "conn must have tls ctx");
-
- if (conn->state >= _OUTPUT_DNSSIM_CONN_CLOSING)
- return;
-
- output_dnssim_t* self = conn->client->dnssim;
-
- /* Ensure TLS handshake is performed before receiving data.
- * See https://www.gnutls.org/manual/html_node/TLS-handshake.html */
- while (conn->state <= _OUTPUT_DNSSIM_CONN_TLS_HANDSHAKE) {
- int err = _tls_handshake(conn);
- mldebug("tls handshake returned: %s", gnutls_strerror(err));
- if (err == GNUTLS_E_SUCCESS) {
- if (gnutls_session_is_resumed(conn->tls->session))
- conn->stats->conn_resumed++;
- if (_self->transport == OUTPUT_DNSSIM_TRANSPORT_HTTPS2) {
- if (_output_dnssim_https2_setup(conn) < 0) {
- _output_dnssim_conn_close(conn);
- return;
- }
- }
- _output_dnssim_conn_activate(conn);
- break;
- } else if (err == GNUTLS_E_AGAIN) {
- return; /* Wait for more data */
- } else if (err == GNUTLS_E_FATAL_ALERT_RECEIVED) {
- gnutls_alert_description_t alert = gnutls_alert_get(conn->tls->session);
- mlwarning("gnutls_handshake failed: %s", gnutls_alert_get_name(alert));
- _output_dnssim_conn_close(conn);
- return;
- } else if (gnutls_error_is_fatal(err)) {
- mlwarning("gnutls_handshake failed: %s", gnutls_strerror_name(err));
- _output_dnssim_conn_close(conn);
- return;
- }
- }
-
- /* See https://gnutls.org/manual/html_node/Data-transfer-and-termination.html#Data-transfer-and-termination */
- while (true) {
- /* Connection might have been closed due to an error, don't try to use it. */
- if (conn->state < _OUTPUT_DNSSIM_CONN_ACTIVE || conn->state >= _OUTPUT_DNSSIM_CONN_CLOSING)
- return;
-
- ssize_t count = gnutls_record_recv(conn->tls->session, _self->wire_buf, WIRE_BUF_SIZE);
- if (count > 0) {
- switch (_self->transport) {
- case OUTPUT_DNSSIM_TRANSPORT_TLS:
- _output_dnssim_read_dns_stream(conn, count, _self->wire_buf);
- break;
- case OUTPUT_DNSSIM_TRANSPORT_HTTPS2:
- _output_dnssim_https2_process_input_data(conn, count, _self->wire_buf);
- break;
- default:
- lfatal("unsupported transport layer");
- break;
- }
- } else if (count == GNUTLS_E_AGAIN) {
- if (conn->tls->buf_pos == conn->tls->buf_len) {
- /* See https://www.gnutls.org/manual/html_node/Asynchronous-operation.html */
- break; /* No more data available in this libuv buffer */
- }
- continue;
- } else if (count == GNUTLS_E_INTERRUPTED) {
- continue;
- } else if (count == GNUTLS_E_REHANDSHAKE) {
- continue; /* Ignore rehandshake request. */
- } else if (count < 0) {
- mlwarning("gnutls_record_recv failed: %s", gnutls_strerror_name(count));
- _output_dnssim_conn_close(conn);
- return;
- } else if (count == 0) {
- break;
- }
- }
- mlassert(conn->tls->buf_len == conn->tls->buf_pos, "tls didn't read the entire buffer");
-}
-
-static ssize_t _tls_pull(gnutls_transport_ptr_t ptr, void* buf, size_t len)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)ptr;
- mlassert(conn != NULL, "conn is null");
- mlassert(conn->tls != NULL, "conn must have tls ctx");
-
- ssize_t avail = conn->tls->buf_len - conn->tls->buf_pos;
- if (avail <= 0) {
- mldebug("tls pull: no more data");
- errno = EAGAIN;
- return -1;
- }
-
- ssize_t transfer = MIN(avail, len);
- memcpy(buf, conn->tls->buf + conn->tls->buf_pos, transfer);
- conn->tls->buf_pos += transfer;
- return transfer;
-}
-
-static void _tls_on_write_complete(uv_write_t* req, int status)
-{
- mlassert(req->data != NULL, "uv_write req has no data pointer");
- struct async_write_ctx* async_ctx = (struct async_write_ctx*)req->data;
- _output_dnssim_connection_t* conn = async_ctx->conn;
- mlassert(conn, "conn is nil");
- mlassert(conn->tls, "conn must have tls ctx");
- mlassert(conn->tls->write_queue_size > 0, "invalid write_queue_size: %d", conn->tls->write_queue_size);
- conn->tls->write_queue_size -= 1;
- free(req->data);
-
- if (status < 0)
- _output_dnssim_conn_close(conn);
-}
-
-static ssize_t _tls_vec_push(gnutls_transport_ptr_t ptr, const giovec_t* iov, int iovcnt)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)ptr;
- mlassert(conn != NULL, "conn is null");
- mlassert(conn->tls != NULL, "conn must have tls ctx");
-
- if (iovcnt == 0)
- return 0;
-
- /*
- * This is a little bit complicated. There are two different writes:
- * 1. Immediate, these don't need to own the buffered data and return immediately
- * 2. Asynchronous, these need to own the buffers until the write completes
- * In order to avoid copying the buffer, an immediate write is tried first if possible.
- * If it isn't possible to write the data without queueing, an asynchronous write
- * is created (with copied buffered data).
- */
-
- size_t total_len = 0;
- uv_buf_t uv_buf[iovcnt];
- int i;
- for (i = 0; i < iovcnt; ++i) {
- uv_buf[i].base = iov[i].iov_base;
- uv_buf[i].len = iov[i].iov_len;
- total_len += iov[i].iov_len;
- }
-
- /* Try to perform the immediate write first to avoid copy */
- int ret = 0;
- if (conn->tls->write_queue_size == 0) {
- ret = uv_try_write((uv_stream_t*)conn->handle, uv_buf, iovcnt);
- /* from libuv documentation -
- uv_try_write will return either:
- > 0: number of bytes written (can be less than the supplied buffer size).
- < 0: negative error code (UV_EAGAIN is returned if no data can be sent immediately).
- */
- if (ret == total_len) {
- /* All the data were buffered by libuv.
- * Return. */
- return ret;
- }
-
- if (ret < 0 && ret != UV_EAGAIN) {
- /* uv_try_write() has returned error code other then UV_EAGAIN.
- * Return. */
- errno = EIO;
- return -1;
- }
- /* Since we are here expression below is true
- * (ret != total_len) && (ret >= 0 || ret == UV_EAGAIN)
- * or the same
- * (ret != total_len && ret >= 0) || (ret != total_len && ret == UV_EAGAIN)
- * i.e. either occurs partial write or UV_EAGAIN.
- * Proceed and copy data amount to owned memory and perform async write.
- */
- if (ret == UV_EAGAIN) {
- /* No data were buffered, so we must buffer all the data. */
- ret = 0;
- }
- }
-
- /* Fallback when the queue is full, and it's not possible to do an immediate write */
- char* p = malloc(sizeof(struct async_write_ctx) + total_len - ret);
- if (p != NULL) {
- struct async_write_ctx* async_ctx = (struct async_write_ctx*)p;
- async_ctx->conn = conn;
- char* buf = async_ctx->buf;
- /* Skip data written in the partial write */
- size_t to_skip = ret;
- /* Copy the buffer into owned memory */
- size_t off = 0;
- int i;
- for (i = 0; i < iovcnt; ++i) {
- if (to_skip > 0) {
- /* Ignore current buffer if it's all skipped */
- if (to_skip >= uv_buf[i].len) {
- to_skip -= uv_buf[i].len;
- continue;
- }
- /* Skip only part of the buffer */
- uv_buf[i].base += to_skip;
- uv_buf[i].len -= to_skip;
- to_skip = 0;
- }
- memcpy(buf + off, uv_buf[i].base, uv_buf[i].len);
- off += uv_buf[i].len;
- }
- uv_buf[0].base = buf;
- uv_buf[0].len = off;
-
- /* Create an asynchronous write request */
- uv_write_t* write_req = &async_ctx->write_req;
- memset(write_req, 0, sizeof(uv_write_t));
- write_req->data = p;
-
- /* Perform an asynchronous write with a callback */
- if (uv_write(write_req, (uv_stream_t*)conn->handle, uv_buf, 1, _tls_on_write_complete) == 0) {
- ret = total_len;
- conn->tls->write_queue_size += 1;
- } else {
- free(p);
- errno = EIO;
- ret = -1;
- }
- } else {
- errno = ENOMEM;
- ret = -1;
- }
-
- return ret;
-}
-
-int _tls_pull_timeout(gnutls_transport_ptr_t ptr, unsigned int ms)
-{
- _output_dnssim_connection_t* conn = (_output_dnssim_connection_t*)ptr;
- mlassert(conn != NULL, "conn is null");
- mlassert(conn->tls != NULL, "conn must have tls ctx");
-
- ssize_t avail = conn->tls->buf_len - conn->tls->buf_pos;
- if (avail <= 0) {
- errno = EAGAIN;
- return -1;
- }
- return avail;
-}
-
-int _output_dnssim_tls_init(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn is nil");
- mlassert(conn->tls == NULL, "conn already has tls context");
-
- int ret;
- mlfatal_oom(conn->tls = malloc(sizeof(_output_dnssim_tls_ctx_t)));
- conn->tls->buf = NULL;
- conn->tls->buf_len = 0;
- conn->tls->buf_pos = 0;
- conn->tls->write_queue_size = 0;
-
- ret = gnutls_init(&conn->tls->session, GNUTLS_CLIENT | GNUTLS_NONBLOCK);
- if (ret < 0) {
- mldebug("failed gnutls_init() (%s)", gnutls_strerror(ret));
- free(conn->tls);
- conn->tls = 0;
- return ret;
- }
-
- output_dnssim_t* self = conn->client->dnssim;
- if (_self->tls_priority == NULL) {
- ret = gnutls_set_default_priority(conn->tls->session);
- if (ret < 0) {
- mldebug("failed gnutls_set_default_priority() (%s)", gnutls_strerror(ret));
- gnutls_deinit(conn->tls->session);
- free(conn->tls);
- conn->tls = 0;
- return ret;
- }
- } else {
- ret = gnutls_priority_set(conn->tls->session, *_self->tls_priority);
- if (ret < 0) {
- mldebug("failed gnutls_priority_set() (%s)", gnutls_strerror(ret));
- gnutls_deinit(conn->tls->session);
- free(conn->tls);
- conn->tls = 0;
- return ret;
- }
- }
-
- ret = gnutls_credentials_set(conn->tls->session, GNUTLS_CRD_CERTIFICATE, _self->tls_cred);
- if (ret < 0) {
- mldebug("failed gnutls_credentials_set() (%s)", gnutls_strerror(ret));
- gnutls_deinit(conn->tls->session);
- free(conn->tls);
- conn->tls = 0;
- return ret;
- }
-
- gnutls_transport_set_pull_function(conn->tls->session, _tls_pull);
- gnutls_transport_set_pull_timeout_function(conn->tls->session, _tls_pull_timeout);
- gnutls_transport_set_vec_push_function(conn->tls->session, _tls_vec_push);
- gnutls_transport_set_ptr(conn->tls->session, conn);
-
- return 0;
-}
-
-int _output_dnssim_create_query_tls(output_dnssim_t* self, _output_dnssim_request_t* req)
-{
- mlassert_self();
- lassert(req, "req is nil");
- lassert(req->client, "request must have a client associated with it");
-
- _output_dnssim_query_tcp_t* qry;
-
- lfatal_oom(qry = calloc(1, sizeof(_output_dnssim_query_tcp_t)));
-
- qry->qry.transport = OUTPUT_DNSSIM_TRANSPORT_TLS;
- qry->qry.req = req;
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_PENDING_WRITE;
- req->qry = &qry->qry; // TODO change when adding support for multiple Qs for req
- _ll_append(req->client->pending, &qry->qry);
-
- return _output_dnssim_handle_pending_queries(req->client);
-}
-
-void _output_dnssim_close_query_tls(_output_dnssim_query_tcp_t* qry)
-{
- mlassert(qry, "qry can't be null");
- mlassert(qry->qry.req, "query must be part of a request");
- _output_dnssim_request_t* req = qry->qry.req;
- mlassert(req->client, "request must belong to a client");
-
- _ll_try_remove(req->client->pending, &qry->qry);
- if (qry->conn) {
- _output_dnssim_connection_t* conn = qry->conn;
- _ll_try_remove(conn->sent, &qry->qry);
- qry->conn = NULL;
- _output_dnssim_conn_idle(conn);
- }
-
- _ll_remove(req->qry, &qry->qry);
- free(qry);
-}
-
-void _output_dnssim_tls_close(_output_dnssim_connection_t* conn)
-{
- mlassert(conn, "conn can't be nil");
- mlassert(conn->tls, "conn must have tls ctx");
- mlassert(conn->client, "conn must belong to a client");
-
- /* Try and get a TLS session ticket for potential resumption. */
- int ret;
- if (gnutls_session_get_flags(conn->tls->session) & GNUTLS_SFLAGS_SESSION_TICKET) {
- if (conn->client->tls_ticket.size != 0) {
- gnutls_free(conn->client->tls_ticket.data);
- }
- ret = gnutls_session_get_data2(conn->tls->session, &conn->client->tls_ticket);
- if (ret < 0) {
- mldebug("gnutls_session_get_data2 failed: %s", gnutls_strerror(ret));
- conn->client->tls_ticket.size = 0;
- }
- }
-
- gnutls_deinit(conn->tls->session);
- _output_dnssim_tcp_close(conn);
-}
-
-void _output_dnssim_tls_write_query(_output_dnssim_connection_t* conn, _output_dnssim_query_tcp_t* qry)
-{
- mlassert(qry, "qry can't be null");
- mlassert(qry->qry.state == _OUTPUT_DNSSIM_QUERY_PENDING_WRITE, "qry must be pending write");
- mlassert(qry->qry.req, "req can't be null");
- mlassert(qry->qry.req->dns_q, "dns_q can't be null");
- mlassert(qry->qry.req->dns_q->obj_prev, "payload can't be null");
- mlassert(conn, "conn can't be null");
- mlassert(conn->state == _OUTPUT_DNSSIM_CONN_ACTIVE, "connection state != ACTIVE");
- mlassert(conn->tls, "conn must have tls ctx");
- mlassert(conn->client, "conn must be associated with client");
- mlassert(conn->client->pending, "conn has no pending queries");
-
- core_object_payload_t* payload = (core_object_payload_t*)qry->qry.req->dns_q->obj_prev;
- uint16_t len = htons(payload->len);
-
- gnutls_record_cork(conn->tls->session);
- ssize_t count = 0;
- if ((count = gnutls_record_send(conn->tls->session, &len, sizeof(len)) < 0) || (count = gnutls_record_send(conn->tls->session, payload->payload, payload->len) < 0)) {
- mlwarning("gnutls_record_send failed: %s", gnutls_strerror_name(count));
- _output_dnssim_conn_close(conn);
- return;
- }
-
- const ssize_t submitted = sizeof(len) + payload->len;
-
- int ret = gnutls_record_uncork(conn->tls->session, GNUTLS_RECORD_WAIT);
- if (gnutls_error_is_fatal(ret)) {
- mlinfo("gnutls_record_uncorck failed: %s", gnutls_strerror_name(ret));
- _output_dnssim_conn_close(conn);
- return;
- }
-
- if (ret != submitted) {
- mlwarning("gnutls_record_uncork didn't send all data");
- _output_dnssim_conn_close(conn);
- return;
- }
-
- qry->conn = conn;
- _ll_remove(conn->client->pending, &qry->qry);
- _ll_append(conn->sent, &qry->qry);
-
- /* Stop idle timer, since there are queries to answer now. */
- if (conn->idle_timer != NULL) {
- conn->is_idle = false;
- uv_timer_stop(conn->idle_timer);
- }
-
- qry->qry.state = _OUTPUT_DNSSIM_QUERY_SENT;
-}
-
-#endif
diff --git a/src/output/dnssim/udp.c b/src/output/dnssim/udp.c
deleted file mode 100644
index 74f8569..0000000
--- a/src/output/dnssim/udp.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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"
-
-static core_log_t _log = LOG_T_INIT("output.dnssim");
-
-static int _process_udp_response(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf)
-{
- _output_dnssim_query_udp_t* qry = (_output_dnssim_query_udp_t*)handle->data;
- _output_dnssim_request_t* req;
- core_object_payload_t payload = CORE_OBJECT_PAYLOAD_INIT(NULL);
- core_object_dns_t dns_a = CORE_OBJECT_DNS_INIT(&payload);
- mlassert(qry, "qry is nil");
- mlassert(qry->qry.req, "query must be part of a request");
- req = qry->qry.req;
-
- payload.payload = (uint8_t*)buf->base;
- payload.len = nread;
-
- dns_a.obj_prev = (core_object_t*)&payload;
- int ret = core_object_dns_parse_header(&dns_a);
- if (ret != 0) {
- mldebug("udp response malformed");
- return _ERR_MALFORMED;
- }
- if (dns_a.id != req->dns_q->id) {
- mldebug("udp response msgid mismatch %x(q) != %x(a)", req->dns_q->id, dns_a.id);
- return _ERR_MSGID;
- }
- if (dns_a.tc == 1) {
- mldebug("udp response has TC=1");
- return _ERR_TC;
- }
- ret = _output_dnssim_answers_request(req, &dns_a);
- if (ret != 0) {
- mlwarning("udp reponse question mismatch");
- return _ERR_QUESTION;
- }
-
- _output_dnssim_request_answered(req, &dns_a);
- return 0;
-}
-
-static void _on_udp_query_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags)
-{
- if (nread > 0) {
- mldebug("udp recv: %d", nread);
-
- // TODO handle TC=1
- _process_udp_response(handle, nread, buf);
- }
-
- if (buf->base != NULL) {
- free(buf->base);
- }
-}
-
-static void _on_query_udp_closed(uv_handle_t* handle)
-{
- _output_dnssim_query_udp_t* qry = (_output_dnssim_query_udp_t*)handle->data;
- _output_dnssim_request_t* req;
- mlassert(qry, "qry is nil");
- mlassert(qry->qry.req, "query must be part of a request");
- req = qry->qry.req;
-
- free(qry->handle);
-
- _ll_remove(req->qry, &qry->qry);
- free(qry);
-
- if (req->qry == NULL)
- _output_dnssim_maybe_free_request(req);
-}
-
-void _output_dnssim_close_query_udp(_output_dnssim_query_udp_t* qry)
-{
- int ret;
- mlassert(qry, "qry is nil");
-
- ret = uv_udp_recv_stop(qry->handle);
- if (ret < 0) {
- mldebug("failed uv_udp_recv_stop(): %s", uv_strerror(ret));
- }
-
- uv_close((uv_handle_t*)qry->handle, _on_query_udp_closed);
-}
-
-int _output_dnssim_create_query_udp(output_dnssim_t* self, _output_dnssim_request_t* req)
-{
- int ret;
- _output_dnssim_query_udp_t* qry;
- core_object_payload_t* payload;
- mlassert_self();
- lassert(req, "req is nil");
- payload = (core_object_payload_t*)req->dns_q->obj_prev;
-
- lfatal_oom(qry = calloc(1, sizeof(_output_dnssim_query_udp_t)));
- lfatal_oom(qry->handle = malloc(sizeof(uv_udp_t)));
-
- qry->qry.transport = OUTPUT_DNSSIM_TRANSPORT_UDP;
- qry->qry.req = req;
- qry->buf = uv_buf_init((char*)payload->payload, payload->len);
- qry->handle->data = (void*)qry;
- ret = uv_udp_init(&_self->loop, qry->handle);
- if (ret < 0) {
- lwarning("failed to init uv_udp_t");
- goto failure;
- }
- _ll_append(req->qry, &qry->qry);
-
- ret = _output_dnssim_bind_before_connect(self, (uv_handle_t*)qry->handle);
- if (ret < 0)
- return ret;
-
- ret = uv_udp_try_send(qry->handle, &qry->buf, 1, (struct sockaddr*)&_self->target);
- if (ret < 0) {
- lwarning("failed to send udp packet: %s", uv_strerror(ret));
- return ret;
- }
-
- // listen for reply
- ret = uv_udp_recv_start(qry->handle, _output_dnssim_on_uv_alloc, _on_udp_query_recv);
- if (ret < 0) {
- lwarning("failed uv_udp_recv_start(): %s", uv_strerror(ret));
- return ret;
- }
-
- return 0;
-failure:
- free(qry->handle);
- free(qry);
- return ret;
-}