summaryrefslogtreecommitdiffstats
path: root/src/output/dnssim/connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/output/dnssim/connection.c')
-rw-r--r--src/output/dnssim/connection.c471
1 files changed, 0 insertions, 471 deletions
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;
-}