summaryrefslogtreecommitdiffstats
path: root/lib/dns/transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/transport.c')
-rw-r--r--lib/dns/transport.c474
1 files changed, 474 insertions, 0 deletions
diff --git a/lib/dns/transport.c b/lib/dns/transport.c
new file mode 100644
index 0000000..ae1ab74
--- /dev/null
+++ b/lib/dns/transport.c
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+
+#include <isc/list.h>
+#include <isc/mem.h>
+#include <isc/refcount.h>
+#include <isc/result.h>
+#include <isc/rwlock.h>
+#include <isc/util.h>
+
+#include <dns/name.h>
+#include <dns/rbt.h>
+#include <dns/transport.h>
+
+#define TRANSPORT_MAGIC ISC_MAGIC('T', 'r', 'n', 's')
+#define VALID_TRANSPORT(ptr) ISC_MAGIC_VALID(ptr, TRANSPORT_MAGIC)
+
+#define TRANSPORT_LIST_MAGIC ISC_MAGIC('T', 'r', 'L', 's')
+#define VALID_TRANSPORT_LIST(ptr) ISC_MAGIC_VALID(ptr, TRANSPORT_LIST_MAGIC)
+
+struct dns_transport_list {
+ unsigned int magic;
+ isc_refcount_t references;
+ isc_mem_t *mctx;
+ isc_rwlock_t lock;
+ dns_rbt_t *transports[DNS_TRANSPORT_COUNT];
+};
+
+typedef enum ternary { ter_none = 0, ter_true = 1, ter_false = 2 } ternary_t;
+
+struct dns_transport {
+ unsigned int magic;
+ isc_refcount_t references;
+ isc_mem_t *mctx;
+ dns_transport_type_t type;
+ struct {
+ char *tlsname;
+ char *certfile;
+ char *keyfile;
+ char *cafile;
+ char *remote_hostname;
+ char *ciphers;
+ uint32_t protocol_versions;
+ ternary_t prefer_server_ciphers;
+ } tls;
+ struct {
+ char *endpoint;
+ dns_http_mode_t mode;
+ } doh;
+};
+
+static void
+free_dns_transport(void *node, void *arg) {
+ dns_transport_t *transport = node;
+
+ REQUIRE(node != NULL);
+
+ UNUSED(arg);
+
+ dns_transport_detach(&transport);
+}
+
+static isc_result_t
+list_add(dns_transport_list_t *list, const dns_name_t *name,
+ const dns_transport_type_t type, dns_transport_t *transport) {
+ isc_result_t result;
+ dns_rbt_t *rbt = NULL;
+
+ RWLOCK(&list->lock, isc_rwlocktype_write);
+ rbt = list->transports[type];
+ INSIST(rbt != NULL);
+
+ result = dns_rbt_addname(rbt, name, transport);
+
+ RWUNLOCK(&list->lock, isc_rwlocktype_write);
+
+ return (result);
+}
+
+dns_transport_type_t
+dns_transport_get_type(dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->type);
+}
+
+char *
+dns_transport_get_certfile(dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->tls.certfile);
+}
+
+char *
+dns_transport_get_keyfile(dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->tls.keyfile);
+}
+
+char *
+dns_transport_get_cafile(dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->tls.cafile);
+}
+
+char *
+dns_transport_get_remote_hostname(dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->tls.remote_hostname);
+}
+
+char *
+dns_transport_get_endpoint(dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->doh.endpoint);
+}
+
+dns_http_mode_t
+dns_transport_get_mode(dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->doh.mode);
+}
+
+dns_transport_t *
+dns_transport_new(const dns_name_t *name, dns_transport_type_t type,
+ dns_transport_list_t *list) {
+ dns_transport_t *transport = isc_mem_get(list->mctx,
+ sizeof(*transport));
+ *transport = (dns_transport_t){ .type = type };
+ isc_refcount_init(&transport->references, 1);
+ isc_mem_attach(list->mctx, &transport->mctx);
+ transport->magic = TRANSPORT_MAGIC;
+
+ list_add(list, name, type, transport);
+
+ return (transport);
+}
+
+void
+dns_transport_set_certfile(dns_transport_t *transport, const char *certfile) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
+ transport->type == DNS_TRANSPORT_HTTP);
+
+ if (transport->tls.certfile != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.certfile);
+ }
+
+ if (certfile != NULL) {
+ transport->tls.certfile = isc_mem_strdup(transport->mctx,
+ certfile);
+ }
+}
+
+void
+dns_transport_set_keyfile(dns_transport_t *transport, const char *keyfile) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
+ transport->type == DNS_TRANSPORT_HTTP);
+
+ if (transport->tls.keyfile != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.keyfile);
+ }
+
+ if (keyfile != NULL) {
+ transport->tls.keyfile = isc_mem_strdup(transport->mctx,
+ keyfile);
+ }
+}
+
+void
+dns_transport_set_cafile(dns_transport_t *transport, const char *cafile) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
+ transport->type == DNS_TRANSPORT_HTTP);
+
+ if (transport->tls.cafile != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.cafile);
+ }
+
+ if (cafile != NULL) {
+ transport->tls.cafile = isc_mem_strdup(transport->mctx, cafile);
+ }
+}
+
+void
+dns_transport_set_remote_hostname(dns_transport_t *transport,
+ const char *hostname) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
+ transport->type == DNS_TRANSPORT_HTTP);
+
+ if (transport->tls.remote_hostname != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.remote_hostname);
+ }
+
+ if (hostname != NULL) {
+ transport->tls.remote_hostname = isc_mem_strdup(transport->mctx,
+ hostname);
+ }
+}
+
+void
+dns_transport_set_endpoint(dns_transport_t *transport, const char *endpoint) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_HTTP);
+
+ if (transport->doh.endpoint != NULL) {
+ isc_mem_free(transport->mctx, transport->doh.endpoint);
+ }
+
+ if (endpoint != NULL) {
+ transport->doh.endpoint = isc_mem_strdup(transport->mctx,
+ endpoint);
+ }
+}
+
+void
+dns_transport_set_mode(dns_transport_t *transport, dns_http_mode_t mode) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_HTTP);
+
+ transport->doh.mode = mode;
+}
+
+void
+dns_transport_set_tls_versions(dns_transport_t *transport,
+ const uint32_t tls_versions) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_HTTP ||
+ transport->type == DNS_TRANSPORT_TLS);
+
+ transport->tls.protocol_versions = tls_versions;
+}
+
+uint32_t
+dns_transport_get_tls_versions(const dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->tls.protocol_versions);
+}
+
+void
+dns_transport_set_ciphers(dns_transport_t *transport, const char *ciphers) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
+ transport->type == DNS_TRANSPORT_HTTP);
+
+ if (transport->tls.ciphers != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.ciphers);
+ }
+
+ if (ciphers != NULL) {
+ transport->tls.ciphers = isc_mem_strdup(transport->mctx,
+ ciphers);
+ }
+}
+
+void
+dns_transport_set_tlsname(dns_transport_t *transport, const char *tlsname) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
+ transport->type == DNS_TRANSPORT_HTTP);
+
+ if (transport->tls.tlsname != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.tlsname);
+ }
+
+ if (tlsname != NULL) {
+ transport->tls.tlsname = isc_mem_strdup(transport->mctx,
+ tlsname);
+ }
+}
+
+char *
+dns_transport_get_ciphers(dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->tls.ciphers);
+}
+
+char *
+dns_transport_get_tlsname(dns_transport_t *transport) {
+ REQUIRE(VALID_TRANSPORT(transport));
+
+ return (transport->tls.tlsname);
+}
+
+void
+dns_transport_set_prefer_server_ciphers(dns_transport_t *transport,
+ const bool prefer) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(transport->type == DNS_TRANSPORT_TLS ||
+ transport->type == DNS_TRANSPORT_HTTP);
+
+ transport->tls.prefer_server_ciphers = prefer ? ter_true : ter_false;
+}
+
+bool
+dns_transport_get_prefer_server_ciphers(const dns_transport_t *transport,
+ bool *preferp) {
+ REQUIRE(VALID_TRANSPORT(transport));
+ REQUIRE(preferp != NULL);
+ if (transport->tls.prefer_server_ciphers == ter_none) {
+ return (false);
+ } else if (transport->tls.prefer_server_ciphers == ter_true) {
+ *preferp = true;
+ return (true);
+ } else if (transport->tls.prefer_server_ciphers == ter_false) {
+ *preferp = false;
+ return (true);
+ }
+
+ UNREACHABLE();
+ return false;
+}
+
+static void
+transport_destroy(dns_transport_t *transport) {
+ isc_refcount_destroy(&transport->references);
+ transport->magic = 0;
+
+ if (transport->doh.endpoint != NULL) {
+ isc_mem_free(transport->mctx, transport->doh.endpoint);
+ }
+ if (transport->tls.remote_hostname != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.remote_hostname);
+ }
+ if (transport->tls.cafile != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.cafile);
+ }
+ if (transport->tls.keyfile != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.keyfile);
+ }
+ if (transport->tls.certfile != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.certfile);
+ }
+ if (transport->tls.ciphers != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.ciphers);
+ }
+
+ if (transport->tls.tlsname != NULL) {
+ isc_mem_free(transport->mctx, transport->tls.tlsname);
+ }
+
+ isc_mem_putanddetach(&transport->mctx, transport, sizeof(*transport));
+}
+
+void
+dns_transport_attach(dns_transport_t *source, dns_transport_t **targetp) {
+ REQUIRE(source != NULL);
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+void
+dns_transport_detach(dns_transport_t **transportp) {
+ dns_transport_t *transport = NULL;
+
+ REQUIRE(transportp != NULL);
+ REQUIRE(VALID_TRANSPORT(*transportp));
+
+ transport = *transportp;
+ *transportp = NULL;
+
+ if (isc_refcount_decrement(&transport->references) == 1) {
+ transport_destroy(transport);
+ }
+}
+
+dns_transport_t *
+dns_transport_find(const dns_transport_type_t type, const dns_name_t *name,
+ dns_transport_list_t *list) {
+ isc_result_t result;
+ dns_transport_t *transport = NULL;
+ dns_rbt_t *rbt = NULL;
+
+ REQUIRE(VALID_TRANSPORT_LIST(list));
+ REQUIRE(list->transports[type] != NULL);
+
+ rbt = list->transports[type];
+
+ RWLOCK(&list->lock, isc_rwlocktype_read);
+ result = dns_rbt_findname(rbt, name, 0, NULL, (void *)&transport);
+ if (result == ISC_R_SUCCESS) {
+ isc_refcount_increment(&transport->references);
+ }
+ RWUNLOCK(&list->lock, isc_rwlocktype_read);
+
+ return (transport);
+}
+
+dns_transport_list_t *
+dns_transport_list_new(isc_mem_t *mctx) {
+ dns_transport_list_t *list = isc_mem_get(mctx, sizeof(*list));
+
+ *list = (dns_transport_list_t){ 0 };
+
+ isc_rwlock_init(&list->lock, 0, 0);
+
+ isc_mem_attach(mctx, &list->mctx);
+ isc_refcount_init(&list->references, 1);
+
+ list->magic = TRANSPORT_LIST_MAGIC;
+
+ for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) {
+ isc_result_t result;
+ result = dns_rbt_create(list->mctx, free_dns_transport, NULL,
+ &list->transports[type]);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ return (list);
+}
+
+void
+dns_transport_list_attach(dns_transport_list_t *source,
+ dns_transport_list_t **targetp) {
+ REQUIRE(VALID_TRANSPORT_LIST(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ isc_refcount_increment(&source->references);
+
+ *targetp = source;
+}
+
+static void
+transport_list_destroy(dns_transport_list_t *list) {
+ isc_refcount_destroy(&list->references);
+ list->magic = 0;
+
+ for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) {
+ if (list->transports[type] != NULL) {
+ dns_rbt_destroy(&list->transports[type]);
+ }
+ }
+ isc_rwlock_destroy(&list->lock);
+ isc_mem_putanddetach(&list->mctx, list, sizeof(*list));
+}
+
+void
+dns_transport_list_detach(dns_transport_list_t **listp) {
+ dns_transport_list_t *list = NULL;
+
+ REQUIRE(listp != NULL);
+ REQUIRE(VALID_TRANSPORT_LIST(*listp));
+
+ list = *listp;
+ *listp = NULL;
+
+ if (isc_refcount_decrement(&list->references) == 1) {
+ transport_list_destroy(list);
+ }
+}