#include <isc/magic.h>
#include <isc/quota.h>
#include <isc/sockaddr.h>
+#include <isc/tls.h>
#include <isc/types.h>
#include <dns/acl.h>
dns_dtenv_t *dtenv; /*%< Dnstap environment */
char *lockfile;
+
+ isc_tlsctx_cache_t *tlsctx_server_cache;
};
#define NAMED_SERVER_MAGIC ISC_MAGIC('S', 'V', 'E', 'R')
#ifdef HAVE_LIBNGHTTP2
static isc_result_t
-listenelt_http(const cfg_obj_t *http, bool tls,
- const ns_listen_tls_params_t *tls_params, in_port_t port,
+listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls,
+ const ns_listen_tls_params_t *tls_params,
+ isc_tlsctx_cache_t *tlsctx_cache, in_port_t port,
isc_mem_t *mctx, ns_listenelt_t **target);
#endif
static isc_result_t
listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
- ns_listenelt_t **target);
+ isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target);
static isc_result_t
listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
+ isc_tlsctx_cache_t *tlsctx_cache,
ns_listenlist_t **target);
static isc_result_t
*/
CHECK(bind9_check_namedconf(config, false, named_g_lctx, named_g_mctx));
+ /* Let's recreate the TLS context cache */
+ if (server->tlsctx_server_cache != NULL) {
+ isc_tlsctx_cache_detach(&server->tlsctx_server_cache);
+ }
+
+ server->tlsctx_server_cache = isc_tlsctx_cache_new(named_g_mctx);
+
/*
* Fill in the maps array, used for resolving defaults.
*/
if (clistenon != NULL) {
CHECK(listenlist_fromconfig(
clistenon, config, named_g_aclconfctx,
- named_g_mctx, AF_INET, &listenon));
+ named_g_mctx, AF_INET,
+ server->tlsctx_server_cache, &listenon));
} else {
/*
* Not specified, use default.
*/
CHECK(ns_listenlist_default(named_g_mctx, listen_port,
- -1, true, &listenon));
+ -1, true, AF_INET,
+ &listenon));
}
if (listenon != NULL) {
ns_interfacemgr_setlistenon4(server->interfacemgr,
if (clistenon != NULL) {
CHECK(listenlist_fromconfig(
clistenon, config, named_g_aclconfctx,
- named_g_mctx, AF_INET6, &listenon));
+ named_g_mctx, AF_INET6,
+ server->tlsctx_server_cache, &listenon));
} else {
/*
* Not specified, use default.
*/
CHECK(ns_listenlist_default(named_g_mctx, listen_port,
- -1, true, &listenon));
+ -1, true, AF_INET6,
+ &listenon));
}
if (listenon != NULL) {
ns_interfacemgr_setlistenon6(server->interfacemgr,
server->dtenv = NULL;
server->magic = NAMED_SERVER_MAGIC;
+
+ server->tlsctx_server_cache = NULL;
+
*serverp = server;
}
INSIST(ISC_LIST_EMPTY(server->viewlist));
INSIST(ISC_LIST_EMPTY(server->cachelist));
+ if (server->tlsctx_server_cache != NULL) {
+ isc_tlsctx_cache_detach(&server->tlsctx_server_cache);
+ }
+
server->magic = 0;
isc_mem_put(server->mctx, server, sizeof(*server));
*serverp = NULL;
static isc_result_t
listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
+ isc_tlsctx_cache_t *tlsctx_cache,
ns_listenlist_t **target) {
isc_result_t result;
const cfg_listelt_t *element;
ns_listenelt_t *delt = NULL;
const cfg_obj_t *listener = cfg_listelt_value(element);
result = listenelt_fromconfig(listener, config, actx, mctx,
- family, &delt);
+ family, tlsctx_cache, &delt);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
static isc_result_t
listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family,
+ isc_tlsctx_cache_t *tlsctx_cache,
ns_listenelt_t **target) {
isc_result_t result;
const cfg_obj_t *ltup = NULL;
ns_listenelt_t *delt = NULL;
uint32_t tls_protos = 0;
ns_listen_tls_params_t tls_params = { 0 };
+ const char *tlsname = NULL;
REQUIRE(target != NULL && *target == NULL);
tlsobj = cfg_tuple_get(ltup, "tls");
if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) {
- const char *tlsname = cfg_obj_asstring(tlsobj);
+ tlsname = cfg_obj_asstring(tlsobj);
if (strcasecmp(tlsname, "none") == 0) {
no_tls = true;
}
tls_params = (ns_listen_tls_params_t){
+ .name = tlsname,
.key = key,
.cert = cert,
.protocols = tls_protos,
#ifdef HAVE_LIBNGHTTP2
if (http) {
- CHECK(listenelt_http(http_server, do_tls, &tls_params, port,
- mctx, &delt));
+ CHECK(listenelt_http(http_server, family, do_tls, &tls_params,
+ tlsctx_cache, port, mctx, &delt));
}
#endif /* HAVE_LIBNGHTTP2 */
if (!http) {
- CHECK(ns_listenelt_create(mctx, port, dscp, NULL, do_tls,
- &tls_params, &delt));
+ CHECK(ns_listenelt_create(mctx, port, dscp, NULL, family,
+ do_tls, &tls_params, tlsctx_cache,
+ &delt));
}
result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"), config,
#ifdef HAVE_LIBNGHTTP2
static isc_result_t
-listenelt_http(const cfg_obj_t *http, bool tls,
- const ns_listen_tls_params_t *tls_params, in_port_t port,
+listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls,
+ const ns_listen_tls_params_t *tls_params,
+ isc_tlsctx_cache_t *tlsctx_cache, in_port_t port,
isc_mem_t *mctx, ns_listenelt_t **target) {
isc_result_t result = ISC_R_SUCCESS;
ns_listenelt_t *delt = NULL;
quota = isc_mem_get(mctx, sizeof(isc_quota_t));
isc_quota_init(quota, max_clients);
}
- result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, tls,
- tls_params, endpoints, len, quota,
- max_streams, &delt);
+ result = ns_listenelt_create_http(
+ mctx, port, named_g_dscp, NULL, family, tls, tls_params,
+ tlsctx_cache, endpoints, len, quota, max_streams, &delt);
if (result != ISC_R_SUCCESS) {
goto error;
}
isc_nm_http_endpoints_attach(eps, &sock->h2.listener_endpoints);
if (ctx != NULL) {
- isc_tlsctx_enable_http2server_alpn(ctx);
result = isc_nm_listentls(mgr, iface, httplisten_acceptcb, sock,
sizeof(isc_nm_http_session_t),
backlog, quota, ctx, &sock->outer);
sock->tid = 0;
sock->fd = -1;
- isc_tlsctx_enable_dot_server_alpn(sslctx);
-
#if !HAVE_SO_REUSEPORT_LB
fd = isc__nm_tlsdns_lb_socket(iface->type.sa.sa_family);
#endif
server_tlsctx = NULL;
isc_tlsctx_createserver(NULL, NULL, &server_tlsctx);
+ isc_tlsctx_enable_http2server_alpn(server_tlsctx);
client_tlsctx = NULL;
isc_tlsctx_createclient(&client_tlsctx);
isc_tlsctx_enable_http2client_alpn(client_tlsctx);
struct ns_listenelt {
isc_mem_t *mctx;
- in_port_t port;
- bool is_http;
- isc_dscp_t dscp; /* -1 = not set, 0..63 */
+ in_port_t port;
+ bool is_http;
+ isc_dscp_t dscp; /* -1 = not set, 0..63 */
dns_acl_t *acl;
- isc_tlsctx_t *sslctx;
- char **http_endpoints;
- size_t http_endpoints_number;
- isc_quota_t *http_quota;
- uint32_t max_concurrent_streams;
+ isc_tlsctx_t *sslctx;
+ isc_tlsctx_cache_t *sslctx_cache;
+ char **http_endpoints;
+ size_t http_endpoints_number;
+ isc_quota_t *http_quota;
+ uint32_t max_concurrent_streams;
ISC_LINK(ns_listenelt_t) link;
};
};
typedef struct ns_listen_tls_params {
+ const char *name;
const char *key;
const char *cert;
uint32_t protocols;
isc_result_t
ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
- dns_acl_t *acl, bool tls,
+ dns_acl_t *acl, const uint16_t family, bool tls,
const ns_listen_tls_params_t *tls_params,
- ns_listenelt_t **target);
+ isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target);
/*%<
* Create a listen-on list element.
*
* Requires:
* \li 'targetp' is a valid pointer to a pointer containing 'NULL';
* \li 'tls_params' is a valid, non-'NULL' pointer if 'tls' equals 'true'.
+ * \li 'tlsctx_cache' is a valid, non-'NULL' pointer if 'tls' equals 'true'.
*/
isc_result_t
ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp,
- dns_acl_t *acl, bool tls,
+ dns_acl_t *acl, const uint16_t family, bool tls,
const ns_listen_tls_params_t *tls_params,
- char **endpoints, size_t nendpoints,
- isc_quota_t *quota, const uint32_t max_streams,
- ns_listenelt_t **target);
+ isc_tlsctx_cache_t *tlsctx_cache, char **endpoints,
+ size_t nendpoints, isc_quota_t *quota,
+ const uint32_t max_streams, ns_listenelt_t **target);
/*%<
* Create a listen-on list element for HTTP(S).
*/
isc_result_t
ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
- bool enabled, ns_listenlist_t **target);
+ bool enabled, const uint16_t family,
+ ns_listenlist_t **target);
/*%<
* Create a listen-on list with default contents, matching
* all addresses with port 'port' (if 'enabled' is true),
static void
destroy(ns_listenlist_t *list);
-isc_result_t
-ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
- dns_acl_t *acl, bool tls,
- const ns_listen_tls_params_t *tls_params,
- ns_listenelt_t **target) {
+static isc_result_t
+listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
+ dns_acl_t *acl, const uint16_t family, const bool is_http,
+ bool tls, const ns_listen_tls_params_t *tls_params,
+ isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) {
ns_listenelt_t *elt = NULL;
isc_result_t result = ISC_R_SUCCESS;
isc_tlsctx_t *sslctx = NULL;
REQUIRE(target != NULL && *target == NULL);
- REQUIRE(!tls || tls_params != NULL);
+ REQUIRE(!tls || (tls_params != NULL && tlsctx_cache != NULL));
if (tls) {
- result = isc_tlsctx_createserver(tls_params->key,
- tls_params->cert, &sslctx);
+ const isc_tlsctx_cache_transport_t transport =
+ is_http ? isc_tlsctx_cache_https : isc_tlsctx_cache_tls;
+
+ /*
+ * Let's try to reuse the existing context from the cache in
+ * order to avoid excessive TLS contexts creation.
+ */
+ result = isc_tlsctx_cache_find(tlsctx_cache, tls_params->name,
+ transport, family, &sslctx);
if (result != ISC_R_SUCCESS) {
- return (result);
- }
+ /*
+ * The lookup failed, let's try to create a new context
+ * and store it within the cache.
+ */
+ INSIST(tls_params->name != NULL &&
+ *tls_params->name != '\0');
- if (tls_params->protocols != 0) {
- isc_tlsctx_set_protocols(sslctx, tls_params->protocols);
- }
+ result = isc_tlsctx_createserver(
+ tls_params->key, tls_params->cert, &sslctx);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
- if (tls_params->dhparam_file != NULL) {
- if (!isc_tlsctx_load_dhparams(sslctx,
- tls_params->dhparam_file))
- {
- isc_tlsctx_free(&sslctx);
- return (ISC_R_FAILURE);
+ if (tls_params->protocols != 0) {
+ isc_tlsctx_set_protocols(sslctx,
+ tls_params->protocols);
}
- }
- if (tls_params->ciphers != NULL) {
- isc_tlsctx_set_cipherlist(sslctx, tls_params->ciphers);
- }
+ if (tls_params->dhparam_file != NULL) {
+ if (!isc_tlsctx_load_dhparams(
+ sslctx, tls_params->dhparam_file)) {
+ isc_tlsctx_free(&sslctx);
+ return (ISC_R_FAILURE);
+ }
+ }
- if (tls_params->prefer_server_ciphers_set) {
- isc_tlsctx_prefer_server_ciphers(
- sslctx, tls_params->prefer_server_ciphers);
- }
+ if (tls_params->ciphers != NULL) {
+ isc_tlsctx_set_cipherlist(sslctx,
+ tls_params->ciphers);
+ }
+
+ if (tls_params->prefer_server_ciphers_set) {
+ isc_tlsctx_prefer_server_ciphers(
+ sslctx,
+ tls_params->prefer_server_ciphers);
+ }
- if (tls_params->session_tickets_set) {
- isc_tlsctx_session_tickets(sslctx,
- tls_params->session_tickets);
+ if (tls_params->session_tickets_set) {
+ isc_tlsctx_session_tickets(
+ sslctx, tls_params->session_tickets);
+ }
+
+#ifdef HAVE_LIBNGHTTP2
+ if (is_http) {
+ isc_tlsctx_enable_http2server_alpn(sslctx);
+ }
+#endif /* HAVE_LIBNGHTTP2 */
+
+ if (!is_http) {
+ isc_tlsctx_enable_dot_server_alpn(sslctx);
+ }
+
+ /*
+ * The storing in the cache should not fail because the
+ * (re)initialisation happens from within a single
+ * thread.
+ */
+ RUNTIME_CHECK(isc_tlsctx_cache_add(
+ tlsctx_cache, tls_params->name,
+ transport, family, sslctx,
+ NULL) == ISC_R_SUCCESS);
+ } else {
+ INSIST(sslctx != NULL);
}
}
elt->dscp = dscp;
elt->acl = acl;
elt->sslctx = sslctx;
+ elt->sslctx_cache = NULL;
+ if (sslctx != NULL && tlsctx_cache != NULL) {
+ isc_tlsctx_cache_attach(tlsctx_cache, &elt->sslctx_cache);
+ }
elt->http_endpoints = NULL;
elt->http_endpoints_number = 0;
elt->http_quota = NULL;
return (ISC_R_SUCCESS);
}
+isc_result_t
+ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
+ dns_acl_t *acl, const uint16_t family, bool tls,
+ const ns_listen_tls_params_t *tls_params,
+ isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) {
+ return listenelt_create(mctx, port, dscp, acl, family, false, tls,
+ tls_params, tlsctx_cache, target);
+}
+
isc_result_t
ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp,
- dns_acl_t *acl, bool tls,
+ dns_acl_t *acl, const uint16_t family, bool tls,
const ns_listen_tls_params_t *tls_params,
- char **endpoints, size_t nendpoints,
- isc_quota_t *quota, const uint32_t max_streams,
- ns_listenelt_t **target) {
+ isc_tlsctx_cache_t *tlsctx_cache, char **endpoints,
+ size_t nendpoints, isc_quota_t *quota,
+ const uint32_t max_streams, ns_listenelt_t **target) {
isc_result_t result;
REQUIRE(target != NULL && *target == NULL);
REQUIRE(endpoints != NULL && *endpoints != NULL);
REQUIRE(nendpoints > 0);
- result = ns_listenelt_create(mctx, http_port, dscp, acl, tls,
- tls_params, target);
+ result = listenelt_create(mctx, http_port, dscp, acl, family, true, tls,
+ tls_params, tlsctx_cache, target);
if (result == ISC_R_SUCCESS) {
(*target)->is_http = true;
(*target)->http_endpoints = endpoints;
if (elt->acl != NULL) {
dns_acl_detach(&elt->acl);
}
- if (elt->sslctx != NULL) {
- isc_tlsctx_free(&elt->sslctx);
+
+ elt->sslctx = NULL; /* this one is going to be destroyed alongside the
+ sslctx_cache */
+ if (elt->sslctx_cache != NULL) {
+ isc_tlsctx_cache_detach(&elt->sslctx_cache);
}
if (elt->http_endpoints != NULL) {
size_t i;
isc_result_t
ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
- bool enabled, ns_listenlist_t **target) {
+ bool enabled, const uint16_t family,
+ ns_listenlist_t **target) {
isc_result_t result;
dns_acl_t *acl = NULL;
ns_listenelt_t *elt = NULL;
goto cleanup;
}
- result = ns_listenelt_create(mctx, port, dscp, acl, false, NULL, &elt);
+ result = ns_listenelt_create(mctx, port, dscp, acl, family, false, NULL,
+ NULL, &elt);
if (result != ISC_R_SUCCESS) {
goto cleanup_acl;
}
UNUSED(state);
- result = ns_listenlist_default(mctx, port, -1, false, &list);
+ result = ns_listenlist_default(mctx, port, -1, false, AF_INET, &list);
assert_int_equal(result, ISC_R_SUCCESS);
assert_non_null(list);
ns_listenlist_detach(&list);
- result = ns_listenlist_default(mctx, port, -1, true, &list);
+ result = ns_listenlist_default(mctx, port, -1, true, AF_INET, &list);
assert_int_equal(result, ISC_R_SUCCESS);
assert_false(ISC_LIST_EMPTY(list->elts));
/*! \file */
-#include "nstest.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ns/interfacemgr.h>
#include <ns/server.h>
+#include "nstest.h"
+
isc_mem_t *mctx = NULL;
isc_log_t *lctx = NULL;
isc_nm_t *netmgr = NULL;
dispatchmgr, maintask, NULL, ncpus, false,
&interfacemgr));
- CHECK(ns_listenlist_default(mctx, port, -1, true, &listenon));
+ CHECK(ns_listenlist_default(mctx, port, -1, true, AF_INET, &listenon));
ns_interfacemgr_setlistenon4(interfacemgr, listenon);
ns_listenlist_detach(&listenon);