diff options
Diffstat (limited to '')
-rw-r--r-- | lib/dns/transport.c | 474 |
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); + } +} |