summaryrefslogtreecommitdiffstats
path: root/src/output/dnssim
diff options
context:
space:
mode:
Diffstat (limited to 'src/output/dnssim')
-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
9 files changed, 0 insertions, 2876 deletions
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;
-}