Merge branch 'artem-client-tls-sessions-reuse-backport-v9_18' into 'v9_18'

[Backport to 9.18]: TLS session resumption support

See merge request isc-projects/bind9!6445
This commit is contained in:
Artem Boldariev 2022-06-15 15:08:11 +00:00
commit 63aef08ea2
16 changed files with 1053 additions and 259 deletions

View file

@ -32,6 +32,9 @@
5895. [test] Add new set of unit test macros and move the unit
tests under single namespace in /tests/. [GL !6243]
5893. [func] Add TLS session resumption support to the client-side
TLS code. [GL !6274]
5891. [func] Key timing options for `dnssec-settime` and related
utilities now accept "UNSET" times as printed by
`dnssec-settime -p`. [GL #3361]

View file

@ -2760,7 +2760,8 @@ _cancel_lookup(dig_lookup_t *lookup, const char *file, unsigned int line) {
debug("canceling pending query %p, belonging to %p", query,
query->lookup);
query->canceled = true;
if (query->readhandle != NULL) {
if (query->readhandle != NULL &&
!isc_nm_is_http_handle(query->readhandle)) {
isc_nm_cancelread(query->readhandle);
}
query_detach(&query);
@ -2772,7 +2773,8 @@ _cancel_lookup(dig_lookup_t *lookup, const char *file, unsigned int line) {
}
static isc_tlsctx_t *
get_create_tls_context(dig_query_t *query, const bool is_https) {
get_create_tls_context(dig_query_t *query, const bool is_https,
isc_tlsctx_client_session_cache_t **psess_cache) {
isc_result_t result;
isc_tlsctx_t *ctx = NULL, *found_ctx = NULL;
isc_tls_cert_store_t *store = NULL, *found_store = NULL;
@ -2783,6 +2785,8 @@ get_create_tls_context(dig_query_t *query, const bool is_https) {
isc_tlsctx_cache_transport_t transport =
is_https ? isc_tlsctx_cache_https : isc_tlsctx_cache_tls;
const bool hostname_ignore_subject = !is_https;
isc_tlsctx_client_session_cache_t *sess_cache = NULL,
*found_sess_cache = NULL;
if (query->lookup->tls_key_file_set != query->lookup->tls_cert_file_set)
{
@ -2793,7 +2797,7 @@ get_create_tls_context(dig_query_t *query, const bool is_https) {
result = isc_tlsctx_cache_find(query->lookup->tls_ctx_cache, tlsctxname,
transport, family, &found_ctx,
&found_store);
&found_store, &found_sess_cache);
if (result != ISC_R_SUCCESS) {
if (query->lookup->tls_ca_set) {
if (found_store == NULL) {
@ -2852,19 +2856,39 @@ get_create_tls_context(dig_query_t *query, const bool is_https) {
}
#endif /* HAVE_LIBNGHTTP2 */
result = isc_tlsctx_cache_add(query->lookup->tls_ctx_cache,
tlsctxname, transport, family,
ctx, store, NULL, NULL);
sess_cache = isc_tlsctx_client_session_cache_new(
mctx, ctx,
ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE);
result = isc_tlsctx_cache_add(
query->lookup->tls_ctx_cache, tlsctxname, transport,
family, ctx, store, sess_cache, NULL, NULL, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if (psess_cache != NULL) {
INSIST(*psess_cache == NULL);
*psess_cache = sess_cache;
}
return (ctx);
}
if (psess_cache != NULL) {
INSIST(*psess_cache == NULL);
*psess_cache = found_sess_cache;
}
INSIST(!query->lookup->tls_ca_set || found_store != NULL);
return (found_ctx);
failure:
if (ctx != NULL && found_ctx != ctx) {
if (ctx != NULL) {
isc_tlsctx_free(&ctx);
}
/*
* The 'found_store' is being managed by the TLS context
* cache. Thus, we should keep it as it is, as it will get
* destroyed alongside the cache. As there is one store per
* multiple TLS contexts, we need to handle store deletion in a
* special way.
*/
if (store != NULL && store != found_store) {
isc_tls_cert_store_free(&store);
}
@ -2886,6 +2910,7 @@ start_tcp(dig_query_t *query) {
dig_query_t *connectquery = NULL;
isc_tlsctx_t *tlsctx = NULL;
bool tls_mode = false;
isc_tlsctx_client_session_cache_t *sess_cache = NULL;
REQUIRE(DIG_VALID_QUERY(query));
debug("start_tcp(%p)", query);
@ -2980,14 +3005,15 @@ start_tcp(dig_query_t *query) {
query_attach(query, &connectquery);
if (tls_mode) {
tlsctx = get_create_tls_context(connectquery, false);
tlsctx = get_create_tls_context(connectquery, false,
&sess_cache);
if (tlsctx == NULL) {
goto failure_tls;
}
isc_nm_tlsdnsconnect(netmgr, &localaddr,
&query->sockaddr, tcp_connected,
connectquery, local_timeout, 0,
tlsctx);
tlsctx, sess_cache);
#if HAVE_LIBNGHTTP2
} else if (query->lookup->https_mode) {
char uri[4096] = { 0 };
@ -2997,8 +3023,8 @@ start_tcp(dig_query_t *query) {
uri, sizeof(uri));
if (!query->lookup->http_plain) {
tlsctx = get_create_tls_context(connectquery,
true);
tlsctx = get_create_tls_context(
connectquery, true, &sess_cache);
if (tlsctx == NULL) {
goto failure_tls;
}
@ -3007,7 +3033,7 @@ start_tcp(dig_query_t *query) {
isc_nm_httpconnect(netmgr, &localaddr, &query->sockaddr,
uri, !query->lookup->https_get,
tcp_connected, connectquery, tlsctx,
local_timeout, 0);
sess_cache, 0, local_timeout);
#endif
} else {
isc_nm_tcpdnsconnect(netmgr, &localaddr,
@ -4597,7 +4623,8 @@ cancel_all(void) {
debug("canceling pending query %p, belonging to %p", q,
current_lookup);
q->canceled = true;
if (q->readhandle != NULL) {
if (q->readhandle != NULL &&
!isc_nm_is_http_handle(q->readhandle)) {
isc_nm_cancelread(q->readhandle);
}
query_detach(&q);

View file

@ -407,7 +407,8 @@ run(void) {
isc_tlsctx_createclient(&tls_ctx);
isc_nm_tlsdnsconnect(netmgr, &sockaddr_local, &sockaddr_remote,
connect_cb, NULL, timeout, 0, tls_ctx);
connect_cb, NULL, timeout, 0, tls_ctx,
NULL);
break;
}
#if HAVE_LIBNGHTTP2
@ -428,7 +429,7 @@ run(void) {
}
isc_nm_httpconnect(netmgr, &sockaddr_local, &sockaddr_remote,
req_url, is_post, connect_cb, NULL, tls_ctx,
timeout, 0);
NULL, timeout, 0);
} break;
#endif
default:

View file

@ -663,6 +663,7 @@ AC_CHECK_FUNCS([SSL_CTX_set_min_proto_version])
AC_CHECK_FUNCS([SSL_CTX_up_ref])
AC_CHECK_FUNCS([SSL_read_ex SSL_peek_ex SSL_write_ex])
AC_CHECK_FUNCS([SSL_CTX_set1_cert_store X509_STORE_up_ref])
AC_CHECK_FUNCS([SSL_SESSION_is_resumable])
#
# Check for algorithm support in OpenSSL

View file

@ -927,13 +927,223 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_nm_t *netmgr,
*xfrp = xfr;
}
static isc_result_t
get_create_tlsctx(const dns_xfrin_ctx_t *xfr, isc_tlsctx_t **pctx,
isc_tlsctx_client_session_cache_t **psess_cache) {
isc_result_t result = ISC_R_FAILURE;
isc_tlsctx_t *tlsctx = NULL, *found = NULL;
isc_tls_cert_store_t *store = NULL, *found_store = NULL;
isc_tlsctx_client_session_cache_t *sess_cache = NULL,
*found_sess_cache = NULL;
uint32_t tls_versions;
const char *ciphers = NULL;
bool prefer_server_ciphers;
const uint16_t family = isc_sockaddr_pf(&xfr->primaryaddr) == PF_INET6
? AF_INET6
: AF_INET;
const char *tlsname = NULL;
REQUIRE(psess_cache != NULL && *psess_cache == NULL);
REQUIRE(pctx != NULL && *pctx == NULL);
INSIST(xfr->transport != NULL);
tlsname = dns_transport_get_tlsname(xfr->transport);
INSIST(tlsname != NULL && *tlsname != '\0');
/*
* Let's try to re-use the already created context. This way
* we have a chance to resume the TLS session, bypassing the
* full TLS handshake procedure, making establishing
* subsequent TLS connections for XoT faster.
*/
result = isc_tlsctx_cache_find(xfr->tlsctx_cache, tlsname,
isc_tlsctx_cache_tls, family, &found,
&found_store, &found_sess_cache);
if (result != ISC_R_SUCCESS) {
const char *hostname =
dns_transport_get_remote_hostname(xfr->transport);
const char *ca_file = dns_transport_get_cafile(xfr->transport);
const char *cert_file =
dns_transport_get_certfile(xfr->transport);
const char *key_file =
dns_transport_get_keyfile(xfr->transport);
char primary_addr_str[INET6_ADDRSTRLEN] = { 0 };
isc_netaddr_t primary_netaddr = { 0 };
bool hostname_ignore_subject;
/*
* So, no context exists. Let's create one using the
* parameters from the configuration file and try to
* store it for further reuse.
*/
result = isc_tlsctx_createclient(&tlsctx);
if (result != ISC_R_SUCCESS) {
goto failure;
}
tls_versions = dns_transport_get_tls_versions(xfr->transport);
if (tls_versions != 0) {
isc_tlsctx_set_protocols(tlsctx, tls_versions);
}
ciphers = dns_transport_get_ciphers(xfr->transport);
if (ciphers != NULL) {
isc_tlsctx_set_cipherlist(tlsctx, ciphers);
}
if (dns_transport_get_prefer_server_ciphers(
xfr->transport, &prefer_server_ciphers))
{
isc_tlsctx_prefer_server_ciphers(tlsctx,
prefer_server_ciphers);
}
if (hostname != NULL || ca_file != NULL) {
/*
* The situation when 'found_store != NULL' while 'found
* == NULL' might appear as there is one to many
* relation between per transport TLS contexts and cert
* stores. That is, there could be one store shared
* between multiple contexts.
*/
if (found_store == NULL) {
/*
* 'ca_file' can equal 'NULL' here, in
* that case the store with system-wide
* CA certificates will be created, just
* as planned.
*/
result = isc_tls_cert_store_create(ca_file,
&store);
if (result != ISC_R_SUCCESS) {
goto failure;
}
} else {
store = found_store;
}
INSIST(store != NULL);
if (hostname == NULL) {
/*
* If CA bundle file is specified, but
* hostname is not, then use the primary
* IP address for validation, just like
* dig does.
*/
INSIST(ca_file != NULL);
isc_netaddr_fromsockaddr(&primary_netaddr,
&xfr->primaryaddr);
isc_netaddr_format(&primary_netaddr,
primary_addr_str,
sizeof(primary_addr_str));
hostname = primary_addr_str;
}
/*
* According to RFC 8310, Subject field MUST NOT
* be inspected when verifying hostname for DoT.
* Only SubjectAltName must be checked.
*/
hostname_ignore_subject = true;
result = isc_tlsctx_enable_peer_verification(
tlsctx, false, store, hostname,
hostname_ignore_subject);
if (result != ISC_R_SUCCESS) {
goto failure;
}
/*
* Let's load client certificate and enable
* Mutual TLS. We do that only in the case when
* Strict TLS is enabled, because Mutual TLS is
* an extension of it.
*/
if (cert_file != NULL) {
INSIST(key_file != NULL);
result = isc_tlsctx_load_certificate(
tlsctx, key_file, cert_file);
if (result != ISC_R_SUCCESS) {
goto failure;
}
}
}
isc_tlsctx_enable_dot_client_alpn(tlsctx);
sess_cache = isc_tlsctx_client_session_cache_new(
xfr->mctx, tlsctx,
ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE);
found_store = NULL;
result = isc_tlsctx_cache_add(xfr->tlsctx_cache, tlsname,
isc_tlsctx_cache_tls, family,
tlsctx, store, sess_cache, &found,
&found_store, &found_sess_cache);
if (result == ISC_R_EXISTS) {
/*
* It seems the entry has just been created from within
* another thread while we were initialising
* ours. Although this is unlikely, it could happen
* after startup/re-initialisation. In such a case,
* discard the new context and associated data and use
* the already established one from now on.
*
* Such situation will not occur after the
* initial 'warm-up', so it is not critical
* performance-wise.
*/
INSIST(found != NULL);
isc_tlsctx_free(&tlsctx);
isc_tls_cert_store_free(&store);
isc_tlsctx_client_session_cache_detach(&sess_cache);
/* Let's return the data from the cache. */
*psess_cache = found_sess_cache;
*pctx = found;
} else {
/*
* Adding the fresh values into the cache has been
* successful, let's return them
*/
INSIST(result == ISC_R_SUCCESS);
*psess_cache = sess_cache;
*pctx = tlsctx;
}
} else {
/*
* The cache lookup has been successful, let's return the
* results.
*/
INSIST(result == ISC_R_SUCCESS);
*psess_cache = found_sess_cache;
*pctx = found;
}
return (ISC_R_SUCCESS);
failure:
if (tlsctx != NULL) {
isc_tlsctx_free(&tlsctx);
}
/*
* The 'found_store' is being managed by the TLS context
* cache. Thus, we should keep it as it is, as it will get
* destroyed alongside the cache. As there is one store per
* multiple TLS contexts, we need to handle store deletion in a
* special way.
*/
if (store != NULL && store != found_store) {
isc_tls_cert_store_free(&store);
}
return (result);
}
static isc_result_t
xfrin_start(dns_xfrin_ctx_t *xfr) {
isc_result_t result;
dns_xfrin_ctx_t *connect_xfr = NULL;
dns_transport_type_t transport_type = DNS_TRANSPORT_TCP;
isc_tlsctx_t *tlsctx = NULL, *found = NULL;
isc_tls_cert_store_t *store = NULL, *found_store = NULL;
isc_tlsctx_t *tlsctx = NULL;
isc_tlsctx_client_session_cache_t *sess_cache = NULL;
(void)isc_refcount_increment0(&xfr->connects);
dns_xfrin_attach(xfr, &connect_xfr);
@ -953,162 +1163,14 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
connect_xfr, 30000, 0);
break;
case DNS_TRANSPORT_TLS: {
uint32_t tls_versions;
const char *ciphers = NULL;
bool prefer_server_ciphers;
const uint16_t family = isc_sockaddr_pf(&xfr->primaryaddr) ==
PF_INET6
? AF_INET6
: AF_INET;
const char *tlsname = NULL;
INSIST(xfr->transport != NULL);
tlsname = dns_transport_get_tlsname(xfr->transport);
INSIST(tlsname != NULL && *tlsname != '\0');
/*
* Let's try to re-use the already created context. This way
* we have a chance to resume the TLS session, bypassing the
* full TLS handshake procedure, making establishing
* subsequent TLS connections for XoT faster.
*/
result = isc_tlsctx_cache_find(xfr->tlsctx_cache, tlsname,
isc_tlsctx_cache_tls, family,
&tlsctx, &found_store);
result = get_create_tlsctx(xfr, &tlsctx, &sess_cache);
if (result != ISC_R_SUCCESS) {
const char *hostname =
dns_transport_get_remote_hostname(
xfr->transport);
const char *ca_file =
dns_transport_get_cafile(xfr->transport);
const char *cert_file =
dns_transport_get_certfile(xfr->transport);
const char *key_file =
dns_transport_get_keyfile(xfr->transport);
char primary_addr_str[INET6_ADDRSTRLEN] = { 0 };
isc_netaddr_t primary_netaddr = { 0 };
bool hostname_ignore_subject;
/*
* So, no context exists. Let's create one using the
* parameters from the configuration file and try to
* store it for further reuse.
*/
CHECK(isc_tlsctx_createclient(&tlsctx));
tls_versions =
dns_transport_get_tls_versions(xfr->transport);
if (tls_versions != 0) {
isc_tlsctx_set_protocols(tlsctx, tls_versions);
}
ciphers = dns_transport_get_ciphers(xfr->transport);
if (ciphers != NULL) {
isc_tlsctx_set_cipherlist(tlsctx, ciphers);
}
if (dns_transport_get_prefer_server_ciphers(
xfr->transport, &prefer_server_ciphers))
{
isc_tlsctx_prefer_server_ciphers(
tlsctx, prefer_server_ciphers);
}
if (hostname != NULL || ca_file != NULL) {
if (found_store == NULL) {
/*
* 'ca_file' can equal 'NULL' here, in
* that case the store with system-wide
* CA certificates will be created, just
* as planned.
*/
result = isc_tls_cert_store_create(
ca_file, &store);
if (result != ISC_R_SUCCESS) {
goto failure;
}
} else {
store = found_store;
}
INSIST(store != NULL);
if (hostname == NULL) {
/*
* If CA bundle file is specified, but
* hostname is not, then use the primary
* IP address for validation, just like
* dig does.
*/
INSIST(ca_file != NULL);
isc_netaddr_fromsockaddr(
&primary_netaddr,
&xfr->primaryaddr);
isc_netaddr_format(
&primary_netaddr,
primary_addr_str,
sizeof(primary_addr_str));
hostname = primary_addr_str;
}
/*
* According to RFC 8310, Subject field MUST NOT
* be inspected when verifying hostname for DoT.
* Only SubjectAltName must be checked.
*/
hostname_ignore_subject = true;
result = isc_tlsctx_enable_peer_verification(
tlsctx, false, store, hostname,
hostname_ignore_subject);
if (result != ISC_R_SUCCESS) {
goto failure;
}
/*
* Let's load client certificate and enable
* Mutual TLS. We do that only in the case when
* Strict TLS is enabled, because Mutual TLS is
* an extension of it.
*/
if (cert_file != NULL) {
INSIST(key_file != NULL);
result = isc_tlsctx_load_certificate(
tlsctx, key_file, cert_file);
if (result != ISC_R_SUCCESS) {
goto failure;
}
}
}
isc_tlsctx_enable_dot_client_alpn(tlsctx);
found_store = NULL;
result = isc_tlsctx_cache_add(
xfr->tlsctx_cache, tlsname,
isc_tlsctx_cache_tls, family, tlsctx, store,
&found, &found_store);
if (result == ISC_R_EXISTS) {
/*
* It seems the entry has just been created
* from within another thread while we were
* initialising ours. Although this is
* unlikely, it could happen after
* startup/re-initialisation. In such a case,
* discard the new context and use the already
* established one from now on.
*
* Such situation will not occur after the
* initial 'warm-up', so it is not critical
* performance-wise.
*/
INSIST(found != NULL);
isc_tlsctx_free(&tlsctx);
isc_tls_cert_store_free(&store);
tlsctx = found;
} else {
INSIST(result == ISC_R_SUCCESS);
}
goto failure;
}
INSIST(tlsctx != NULL);
isc_nm_tlsdnsconnect(xfr->netmgr, &xfr->sourceaddr,
&xfr->primaryaddr, xfrin_connect_done,
connect_xfr, 30000, 0, tlsctx);
connect_xfr, 30000, 0, tlsctx, sess_cache);
} break;
default:
UNREACHABLE();
@ -1117,18 +1179,6 @@ xfrin_start(dns_xfrin_ctx_t *xfr) {
return (ISC_R_SUCCESS);
failure:
/*
* The 'found' context is being managed by the TLS context cache.
* Thus, we should keep it as it is, as it will get destroyed
* alongside the cache.
*/
if (tlsctx != NULL && found != tlsctx) {
isc_tlsctx_free(&tlsctx);
}
if (store != NULL && store != found_store) {
isc_tls_cert_store_free(&store);
}
isc_refcount_decrement0(&xfr->connects);
dns_xfrin_detach(&connect_xfr);
return (result);

View file

@ -519,7 +519,8 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
void
isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
size_t extrahandlesize, isc_tlsctx_t *sslctx);
size_t extrahandlesize, isc_tlsctx_t *sslctx,
isc_tlsctx_client_session_cache_t *client_sess_cache);
/*%<
* Establish a DNS client connection via a TCP or TLS connection, bound to
* the address 'local' and connected to the address 'peer'.
@ -559,13 +560,15 @@ isc_nm_listentls(isc_nm_t *mgr, isc_sockaddr_t *iface,
void
isc_nm_tlsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *ctx,
isc_tlsctx_client_session_cache_t *client_sess_cache,
unsigned int timeout, size_t extrahandlesize);
void
isc_nm_httpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
const char *uri, bool POST, isc_nm_cb_t cb, void *cbarg,
isc_tlsctx_t *ctx, unsigned int timeout,
size_t extrahandlesize);
isc_tlsctx_t *ctx,
isc_tlsctx_client_session_cache_t *client_sess_cache,
unsigned int timeout, size_t extrahandlesize);
isc_result_t
isc_nm_listenhttp(isc_nm_t *mgr, isc_sockaddr_t *iface, int backlog,

View file

@ -267,6 +267,169 @@ isc_tls_cert_store_free(isc_tls_cert_store_t **pstore);
*\li 'pstore' is a valid pointer to a pointer containing a non-'NULL' value.
*/
typedef struct isc_tlsctx_client_session_cache isc_tlsctx_client_session_cache_t;
/*%<
* TLS client session cache is an object which allows efficient
* storing and retrieval of previously saved TLS sessions so that they
* can be resumed. This object is supposed to be a foundation for
* implementing TLS session resumption - a standard technique to
* reduce the cost of re-establishing a connection to the remote
* server endpoint.
*
* OpenSSL does server-side TLS session caching transparently by
* default. However, on the client-side, a TLS session to resume must
* be manually specified when establishing the TLS connection. The TLS
* client session cache is precisely the foundation for that.
*
* The cache has been designed to have the following characteristics:
*
* - Fixed maximal number of entries to not keep too many obsolete
* sessions within the cache;
*
* - The cache is indexed by character string keys. Each string is a
* key representing a remote endpoint (unique remote endpoint name,
* e.g. combination of the remote IP address and port);
*
* - Least Recently Used (LRU) cache replacement policy while allowing
* easy removal of obsolete entries;
*
* - Ability to store multiple TLS sessions associated with the given
* key (remote endpoint name). This characteristic is important if
* multiple connections to the same remote server can be established;
*
* - Ability to efficiently retrieve the most recent TLS sessions
* associated with the key (remote endpoint name).
*
* Because of these characteristics, the cache will end up having the
* necessary amount of resumable TLS session parameters to the most
* used remote endpoints ("hot entries") after a short period of
* initial use ("warmup").
*
* Attempting to resume TLS sessions is an optimisation, which is not
* guaranteed to succeed because it requires the same session to be
* present in the server session caches. If it is not the case, the
* usual handshake procedure takes place. However, when session
* resumption is successful, it reduces the amount of the
* computational resources required as well as the amount of data to
* be transmitted when (re)establishing the connection. Also, it
* reduces round trip time (by reducing the number of packets to
* transmit).
*
* This optimisation is important in the context of DNS because the
* amount of data within the full handshake messages might be
* comparable to or surpass the size of a typical DNS message.
*/
isc_tlsctx_client_session_cache_t *
isc_tlsctx_client_session_cache_new(isc_mem_t *mctx, isc_tlsctx_t *ctx,
const size_t max_entries);
/*%<
* Create a new TLS client session cache object.
*
* Requires:
*\li 'mctx' is a valid memory context object;
*\li 'ctx' is a valid TLS context object;
*\li 'max_entries' is a positive number;
*/
void
isc_tlsctx_client_session_cache_attach(
isc_tlsctx_client_session_cache_t *source,
isc_tlsctx_client_session_cache_t **targetp);
/*%<
* Create a reference to the TLS client session cache object.
*
* Requires:
*\li 'source' is a valid TLS client session cache object;
*\li 'targetp' is a valid pointer to a pointer which must equal NULL.
*/
void
isc_tlsctx_client_session_cache_detach(
isc_tlsctx_client_session_cache_t **cachep);
/*%<
* Remove a reference to the TLS client session cache object.
*
* Requires:
*\li 'cachep' is a pointer to a pointer to a valid TLS client session cache
*object.
*/
void
isc_tlsctx_client_session_cache_keep(isc_tlsctx_client_session_cache_t *cache,
char *remote_peer_name, isc_tls_t *tls);
/*%<
* Add a resumable TLS client session within 'tls' to the TLS client
* session cache object 'cache' and associate it with
* 'remote_server_name' string. Also, the oldest entry from the cache
* might get removed if the cache is full.
*
* Requires:
*\li 'cache' is a pointer to a valid TLS client session cache object;
*\li 'remote_peer_name' is a pointer to a non empty character string;
*\li 'tls' is a valid, non-'NULL' pointer to a TLS connection state object.
*/
void
isc_tlsctx_client_session_cache_keep_sockaddr(
isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
isc_tls_t *tls);
/*%<
* The same as 'isc_tlsctx_client_session_cache_keep()', but uses a
* 'isc_sockaddr_t' as a key, instead of a character string.
*
* Requires:
*\li 'remote_peer' is a valid, non-'NULL', pointer to an 'isc_sockaddr_t'
*object.
*/
void
isc_tlsctx_client_session_cache_reuse(isc_tlsctx_client_session_cache_t *cache,
char *remote_server_name, isc_tls_t *tls);
/*%
* Try to restore a previously stored TLS session denoted by a remote
* server name as a key ('remote_server_name') into the given TLS
* connection state object ('tls'). The successfully restored session
* gets removed from the cache.
*
* Requires:
*\li 'cache' is a pointer to a valid TLS client session cache object;
*\li 'remote_peer_name' is a pointer to a non empty character string;
*\li 'tls' is a valid, non-'NULL' pointer to a TLS connection state object.
*/
void
isc_tlsctx_client_session_cache_reuse_sockaddr(
isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
isc_tls_t *tls);
/*%<
* The same as 'isc_tlsctx_client_session_cache_reuse()', but uses a
* 'isc_sockaddr_t' as a key, instead of a character string.
*
* Requires:
*\li 'remote_peer' is a valid, non-'NULL' pointer to an 'isc_sockaddr_t'
*object.
*/
const isc_tlsctx_t *
isc_tlsctx_client_session_cache_getctx(isc_tlsctx_client_session_cache_t *cache);
/*%<
* Returns a TLS context associated with the given TLS client
* session cache object. The function is intended to be used to
* implement the sanity checks ('INSIST()'s and 'REQUIRE()'s).
*
* Requires:
*\li 'cache' is a pointer to a valid TLS client session cache object.
*/
#define ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE (150)
/*%<
* The default maximum size of a TLS client session cache. The value
* should be large enough to hold enough sessions to successfully
* re-establish connections to the most remote TLS servers, but not
* too big to avoid keeping too much obsolete sessions.
*/
typedef struct isc_tlsctx_cache isc_tlsctx_cache_t;
/*%<
* The TLS context cache is an object which allows retrieving a
@ -334,28 +497,39 @@ isc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep);
*/
isc_result_t
isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
const isc_tlsctx_cache_transport_t transport,
const uint16_t family, isc_tlsctx_t *ctx,
isc_tls_cert_store_t *store, isc_tlsctx_t **pfound,
isc_tls_cert_store_t **pfound_store);
isc_tlsctx_cache_add(
isc_tlsctx_cache_t *cache, const char *name,
const isc_tlsctx_cache_transport_t transport, const uint16_t family,
isc_tlsctx_t *ctx, isc_tls_cert_store_t *store,
isc_tlsctx_client_session_cache_t *client_sess_cache,
isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store,
isc_tlsctx_client_session_cache_t **pfound_client_sess_cache);
/*%<
*
* Add a new TLS context to the TLS context cache. 'pfound' is an
* optional pointer, which can be used to retrieve an already
* existing TLS context object in a case it exists.
* Add a new TLS context and its associated data to the TLS context
* cache. 'pfound' is an optional pointer, which can be used to
* retrieve an already existing TLS context object in a case it
* exists.
*
* The passed certificates store object ('store') possession is
* transferred to the cache object in a case of success. In some cases
* it might be destroyed immediately upon the call completion.
*
* The possession of the passed TLS client session cache
* ('client_sess_cache') is also transferred to the cache object in a
* case of success.
*
* Requires:
*\li 'cache' is a valid pointer to a TLS context cache object;
*\li 'name' is a valid pointer to a non-empty string;
*\li 'transport' is a valid transport identifier (currently only
* TLS/DoT and HTTPS/DoH are supported);
*\li 'family' - either 'AF_INET' or 'AF_INET6';
*\li 'ctx' - a valid pointer to a valid TLS context object.
*\li 'ctx' - a valid pointer to a valid TLS context object;
*\li 'store' - a valid pointer to a valid TLS certificates store object or
* 'NULL';
*\li 'client_sess_cache' - a valid pointer to a valid TLS client sessions
*cache object or 'NULL.
*
* Returns:
*\li #ISC_R_EXISTS - node of the same key already exists;
@ -363,12 +537,13 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
*/
isc_result_t
isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name,
const isc_tlsctx_cache_transport_t transport,
const uint16_t family, isc_tlsctx_t **pctx,
isc_tls_cert_store_t **pstore);
isc_tlsctx_cache_find(
isc_tlsctx_cache_t *cache, const char *name,
const isc_tlsctx_cache_transport_t transport, const uint16_t family,
isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore,
isc_tlsctx_client_session_cache_t **pfound_client_sess_cache);
/*%<
* Look up a TLS context in the TLS context cache.
* Look up a TLS context and its associated data in the TLS context cache.
*
* Requires:
*\li 'cache' is a valid pointer to a TLS context cache object;
@ -376,9 +551,14 @@ isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name,
*\li 'transport' - a valid transport identifier (currently only
* TLS/DoT and HTTPS/DoH are supported;
*\li 'family' - either 'AF_INET' or 'AF_INET6';
*\li 'pctx' - a valid pointer to a non-NULL pointer.
*\li 'pctx' - a valid pointer to a non-NULL pointer;
*\li 'pstore' - a valid pointer to a non-NULL pointer or 'NULL'.
*\li 'pfound_client_sess_cache' - a valid pointer to a non-NULL pointer or
*'NULL'.
*
* Returns:
*\li #ISC_R_SUCCESS - the context has been found;
*\li #ISC_R_NOTFOUND - the context has not been found.
*\li #ISC_R_NOTFOUND - the context has not been found. In such a case,
* 'pstore' still might get initialised as there is one to many
* relation between stores and contexts.
*/

View file

@ -1424,8 +1424,9 @@ error:
void
isc_nm_httpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
const char *uri, bool post, isc_nm_cb_t cb, void *cbarg,
isc_tlsctx_t *tlsctx, unsigned int timeout,
size_t extrahandlesize) {
isc_tlsctx_t *tlsctx,
isc_tlsctx_client_session_cache_t *client_sess_cache,
unsigned int timeout, size_t extrahandlesize) {
isc_sockaddr_t local_interface;
isc_nmsocket_t *sock = NULL;
@ -1487,7 +1488,7 @@ isc_nm_httpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
if (tlsctx != NULL) {
isc_nm_tlsconnect(mgr, local, peer, transport_connect_cb, sock,
tlsctx, timeout, 0);
tlsctx, client_sess_cache, timeout, 0);
} else {
isc_nm_tcpconnect(mgr, local, peer, transport_connect_cb, sock,
timeout, 0);
@ -1507,7 +1508,12 @@ client_send(isc_nmhandle_t *handle, const isc_region_t *region) {
REQUIRE(region != NULL);
REQUIRE(region->base != NULL);
REQUIRE(region->length <= MAX_DNS_MESSAGE_SIZE);
REQUIRE(cstream != NULL);
if (session->closed) {
return (ISC_R_CANCELED);
}
INSIST(cstream != NULL);
if (cstream->post) {
/* POST */

View file

@ -940,6 +940,8 @@ struct isc_nmsocket {
struct tls {
isc_tls_t *tls;
isc_tlsctx_t *ctx;
isc_tlsctx_client_session_cache_t *client_sess_cache;
bool client_session_saved;
BIO *app_rbio;
BIO *app_wbio;
BIO *ssl_rbio;
@ -971,6 +973,8 @@ struct isc_nmsocket {
isc_tlsctx_t **listener_tls_ctx; /*%< A context reference per
worker */
size_t n_listener_tls_ctx;
isc_tlsctx_client_session_cache_t *client_sess_cache;
bool client_session_saved;
isc_nmsocket_t *tlslistener;
isc_nmsocket_t *tlssocket;
atomic_bool result_updated;
@ -2175,3 +2179,6 @@ isc__nmsocket_writetimeout_cb(void *data, isc_result_t eresult);
isc_error_fatal(__FILE__, __LINE__, "%s failed: %s\n", #func, \
uv_strerror(ret)); \
}
void
isc__nmsocket_log_tls_session_reuse(isc_nmsocket_t *sock, isc_tls_t *tls);

View file

@ -2077,6 +2077,10 @@ void
isc__nmsocket_timer_restart(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
if (uv_is_closing((uv_handle_t *)&sock->read_timer)) {
return;
}
if (atomic_load(&sock->connecting)) {
int r;
@ -3729,6 +3733,27 @@ isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle) {
return (NULL);
}
void
isc__nmsocket_log_tls_session_reuse(isc_nmsocket_t *sock, isc_tls_t *tls) {
const int log_level = ISC_LOG_DEBUG(1);
char client_sabuf[ISC_SOCKADDR_FORMATSIZE];
char local_sabuf[ISC_SOCKADDR_FORMATSIZE];
REQUIRE(tls != NULL);
if (!isc_log_wouldlog(isc_lctx, log_level)) {
return;
};
isc_sockaddr_format(&sock->peer, client_sabuf, sizeof(client_sabuf));
isc_sockaddr_format(&sock->iface, local_sabuf, sizeof(local_sabuf));
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
log_level, "TLS %s session %s for %s on %s",
SSL_is_server(tls) ? "server" : "client",
SSL_session_reused(tls) ? "resumed" : "created",
client_sabuf, local_sabuf);
}
#ifdef NETMGR_TRACE
/*
* Dump all active sockets in netmgr. We output to stderr

View file

@ -80,6 +80,14 @@ tls_cycle(isc_nmsocket_t *sock);
static void
call_pending_send_callbacks(isc_nmsocket_t *sock, const isc_result_t result);
static void
tlsdns_keep_client_tls_session(isc_nmsocket_t *sock);
static void
tlsdns_set_tls_shutdown(isc_tls_t *tls) {
(void)SSL_set_shutdown(tls, SSL_SENT_SHUTDOWN);
}
static bool
peer_verification_has_failed(isc_nmsocket_t *sock) {
if (sock->tls.tls != NULL && sock->tls.state == TLS_STATE_HANDSHAKE &&
@ -295,11 +303,17 @@ tlsdns_connect_cb(uv_connect_t *uvreq, int status) {
SSL_set_bio(sock->tls.tls, sock->tls.ssl_rbio, sock->tls.ssl_wbio);
#endif
SSL_set_connect_state(sock->tls.tls);
result = isc_sockaddr_fromsockaddr(&sock->peer, (struct sockaddr *)&ss);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if (sock->tls.client_sess_cache != NULL) {
isc_tlsctx_client_session_cache_reuse_sockaddr(
sock->tls.client_sess_cache, &sock->peer,
sock->tls.tls);
}
SSL_set_connect_state(sock->tls.tls);
/* Setting pending req */
sock->tls.pending_req = req;
@ -324,7 +338,8 @@ error:
void
isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
size_t extrahandlesize, isc_tlsctx_t *sslctx) {
size_t extrahandlesize, isc_tlsctx_t *sslctx,
isc_tlsctx_client_session_cache_t *client_sess_cache) {
isc_result_t result = ISC_R_SUCCESS;
isc_nmsocket_t *sock = NULL;
isc__netievent_tlsdnsconnect_t *ievent = NULL;
@ -355,6 +370,13 @@ isc_nm_tlsdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
req->local = *local;
req->handle = isc__nmhandle_get(sock, &req->peer, &sock->iface);
if (client_sess_cache != NULL) {
INSIST(isc_tlsctx_client_session_cache_getctx(
client_sess_cache) == sslctx);
isc_tlsctx_client_session_cache_attach(
client_sess_cache, &sock->tls.client_sess_cache);
}
result = isc__nm_socket(sa_family, SOCK_STREAM, 0, &sock->fd);
if (result != ISC_R_SUCCESS) {
goto failure;
@ -1014,6 +1036,11 @@ isc__nm_tlsdns_processbuffer(isc_nmsocket_t *sock) {
isc_nmhandle_detach(&handle);
if (isc__nmsocket_closing(sock)) {
tlsdns_set_tls_shutdown(sock->tls.tls);
tlsdns_keep_client_tls_session(sock);
}
return (ISC_R_SUCCESS);
}
@ -1116,6 +1143,8 @@ tls_cycle_input(isc_nmsocket_t *sock) {
const unsigned char *alpn = NULL;
unsigned int alpnlen = 0;
isc__nmsocket_log_tls_session_reuse(sock, sock->tls.tls);
isc_tls_get_selected_alpn(sock->tls.tls, &alpn, &alpnlen);
if (alpn != NULL && alpnlen == ISC_TLS_DOT_PROTO_ALPN_ID_LEN &&
memcmp(ISC_TLS_DOT_PROTO_ALPN_ID, alpn,
@ -1841,6 +1870,12 @@ tlsdns_close_sock(isc_nmsocket_t *sock) {
atomic_store(&sock->connected, false);
if (sock->tls.tls != NULL) {
/*
* Let's shutdown the TLS session properly so that the session
* will remain resumable, if required.
*/
tlsdns_set_tls_shutdown(sock->tls.tls);
tlsdns_keep_client_tls_session(sock);
isc_tls_free(&sock->tls.tls);
}
@ -2021,7 +2056,7 @@ isc__nm_tlsdns_shutdown(isc_nmsocket_t *sock) {
if (sock->tls.tls) {
/* Shutdown any active TLS connections */
(void)SSL_shutdown(sock->tls.tls);
tlsdns_set_tls_shutdown(sock->tls.tls);
}
if (atomic_load(&sock->accepting)) {
@ -2152,10 +2187,35 @@ isc__nm_async_tlsdns_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx,
void
isc__nm_tlsdns_cleanup_data(isc_nmsocket_t *sock) {
if ((sock->type == isc_nm_tlsdnslistener ||
sock->type == isc_nm_tlsdnssocket) &&
sock->tls.ctx != NULL)
{
isc_tlsctx_free(&sock->tls.ctx);
if (sock->type == isc_nm_tlsdnslistener ||
sock->type == isc_nm_tlsdnssocket) {
if (sock->tls.client_sess_cache != NULL) {
INSIST(atomic_load(&sock->client));
INSIST(sock->type == isc_nm_tlsdnssocket);
isc_tlsctx_client_session_cache_detach(
&sock->tls.client_sess_cache);
}
if (sock->tls.ctx != NULL) {
INSIST(ISC_LIST_EMPTY(sock->tls.sendreqs));
isc_tlsctx_free(&sock->tls.ctx);
}
}
}
static void
tlsdns_keep_client_tls_session(isc_nmsocket_t *sock) {
/*
* Ensure that the isc_tls_t is being accessed from
* within the worker thread the socket is bound to.
*/
REQUIRE(sock->tid == isc_nm_tid());
if (sock->tls.client_sess_cache != NULL &&
sock->tls.client_session_saved == false)
{
INSIST(atomic_load(&sock->client));
isc_tlsctx_client_session_cache_keep_sockaddr(
sock->tls.client_sess_cache, &sock->peer,
sock->tls.tls);
sock->tls.client_session_saved = true;
}
}

View file

@ -86,6 +86,12 @@ tls_cleanup_listener_tlsctx(isc_nmsocket_t *listener);
static isc_tlsctx_t *
tls_get_listener_tlsctx(isc_nmsocket_t *listener, const int tid);
static void
tls_keep_client_tls_session(isc_nmsocket_t *sock);
static void
tls_try_shutdown(isc_tls_t *tls, const bool quite);
/*
* The socket is closing, outerhandle has been detached, listener is
* inactive, or the netmgr is closing: any operation on it should abort
@ -128,6 +134,10 @@ tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
tlssock = send_req->tlssock;
send_req->tlssock = NULL;
if (finish) {
tls_try_shutdown(tlssock->tlsstream.tls, true);
}
if (send_req->cb != NULL) {
INSIST(VALID_NMHANDLE(tlssock->statichandle));
send_req->cb(send_req->handle, eresult, send_req->cbarg);
@ -241,10 +251,9 @@ tls_send_outgoing(isc_nmsocket_t *sock, bool finish, isc_nmhandle_t *tlshandle,
return (0);
}
if (finish && (SSL_get_shutdown(sock->tlsstream.tls) &
SSL_SENT_SHUTDOWN) != SSL_SENT_SHUTDOWN)
{
(void)SSL_shutdown(sock->tlsstream.tls);
if (finish) {
tls_try_shutdown(sock->tlsstream.tls, false);
tls_keep_client_tls_session(sock);
}
pending = BIO_pending(sock->tlsstream.bio_out);
@ -293,22 +302,21 @@ tls_process_outgoing(isc_nmsocket_t *sock, bool finish,
isc__nm_uvreq_t *send_data) {
int pending;
bool received_shutdown = ((SSL_get_shutdown(sock->tlsstream.tls) &
SSL_RECEIVED_SHUTDOWN) != 0);
bool sent_shutdown = ((SSL_get_shutdown(sock->tlsstream.tls) &
SSL_SENT_SHUTDOWN) != 0);
if (received_shutdown && !sent_shutdown) {
finish = true;
}
/* Data from TLS to network */
if (send_data != NULL) {
pending = tls_send_outgoing(sock, finish, send_data->handle,
send_data->cb.send,
send_data->cbarg);
} else {
bool received_shutdown =
((SSL_get_shutdown(sock->tlsstream.tls) &
SSL_RECEIVED_SHUTDOWN) != 0);
bool sent_shutdown = ((SSL_get_shutdown(sock->tlsstream.tls) &
SSL_SENT_SHUTDOWN) != 0);
if (received_shutdown && !sent_shutdown) {
finish = true;
(void)SSL_shutdown(sock->tlsstream.tls);
}
pending = tls_send_outgoing(sock, finish, NULL, NULL, NULL);
}
@ -331,6 +339,7 @@ tls_try_handshake(isc_nmsocket_t *sock) {
isc_result_t result = ISC_R_SUCCESS;
INSIST(SSL_is_init_finished(sock->tlsstream.tls) == 1);
INSIST(sock->statichandle == NULL);
isc__nmsocket_log_tls_session_reuse(sock, sock->tlsstream.tls);
tlshandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface);
if (sock->tlsstream.server) {
sock->listener->accept_cb(tlshandle, result,
@ -889,7 +898,8 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t result, void *cbarg);
void
isc_nm_tlsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
isc_nm_cb_t cb, void *cbarg, SSL_CTX *ctx,
isc_nm_cb_t cb, void *cbarg, isc_tlsctx_t *ctx,
isc_tlsctx_client_session_cache_t *client_sess_cache,
unsigned int timeout, size_t extrahandlesize) {
isc_nmsocket_t *nsock = NULL;
#if defined(NETMGR_TRACE) && defined(NETMGR_TRACE_VERBOSE)
@ -907,6 +917,13 @@ isc_nm_tlsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
nsock->connect_cbarg = cbarg;
nsock->connect_timeout = timeout;
isc_tlsctx_attach(ctx, &nsock->tlsstream.ctx);
atomic_init(&nsock->client, true);
if (client_sess_cache != NULL) {
INSIST(isc_tlsctx_client_session_cache_getctx(
client_sess_cache) == ctx);
isc_tlsctx_client_session_cache_attach(
client_sess_cache, &nsock->tlsstream.client_sess_cache);
}
isc_nm_tcpconnect(mgr, local, peer, tcp_connected, nsock,
nsock->connect_timeout, 0);
@ -949,6 +966,12 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmhandle_attach(handle, &tlssock->outerhandle);
atomic_store(&tlssock->active, true);
if (tlssock->tlsstream.client_sess_cache != NULL) {
isc_tlsctx_client_session_cache_reuse_sockaddr(
tlssock->tlsstream.client_sess_cache, &tlssock->peer,
tlssock->tlsstream.tls);
}
/*
* Hold a reference to tlssock in the TCP socket: it will
* detached in isc__nm_tls_cleanup_data().
@ -1026,15 +1049,26 @@ isc__nm_tls_cleanup_data(isc_nmsocket_t *sock) {
} else if (sock->type == isc_nm_tlslistener) {
tls_cleanup_listener_tlsctx(sock);
} else if (sock->type == isc_nm_tlssocket) {
if (sock->tlsstream.ctx != NULL) {
isc_tlsctx_free(&sock->tlsstream.ctx);
}
if (sock->tlsstream.tls != NULL) {
/*
* Let's shutdown the TLS session properly so that the
* session will remain resumable, if required.
*/
tls_try_shutdown(sock->tlsstream.tls, true);
tls_keep_client_tls_session(sock);
isc_tls_free(&sock->tlsstream.tls);
/* These are destroyed when we free SSL */
sock->tlsstream.bio_out = NULL;
sock->tlsstream.bio_in = NULL;
}
if (sock->tlsstream.ctx != NULL) {
isc_tlsctx_free(&sock->tlsstream.ctx);
}
if (sock->tlsstream.client_sess_cache != NULL) {
INSIST(atomic_load(&sock->client));
isc_tlsctx_client_session_cache_detach(
&sock->tlsstream.client_sess_cache);
}
} else if (sock->type == isc_nm_tcpsocket &&
sock->tlsstream.tlssocket != NULL) {
/*
@ -1164,3 +1198,30 @@ isc__nm_async_tls_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx,
isc_tlsctx_free(&listener->tlsstream.listener_tls_ctx[tid]);
isc_tlsctx_attach(tlsctx, &listener->tlsstream.listener_tls_ctx[tid]);
}
static void
tls_keep_client_tls_session(isc_nmsocket_t *sock) {
/*
* Ensure that the isc_tls_t is being accessed from
* within the worker thread the socket is bound to.
*/
REQUIRE(sock->tid == isc_nm_tid());
if (sock->tlsstream.client_sess_cache != NULL &&
sock->tlsstream.client_session_saved == false)
{
INSIST(atomic_load(&sock->client));
isc_tlsctx_client_session_cache_keep_sockaddr(
sock->tlsstream.client_sess_cache, &sock->peer,
sock->tlsstream.tls);
sock->tlsstream.client_session_saved = true;
}
}
static void
tls_try_shutdown(isc_tls_t *tls, const bool force) {
if (force) {
(void)SSL_set_shutdown(tls, SSL_SENT_SHUTDOWN);
} else if ((SSL_get_shutdown(tls) & SSL_SENT_SHUTDOWN) == 0) {
(void)SSL_shutdown(tls);
}
}

View file

@ -43,6 +43,7 @@
#include <isc/random.h>
#include <isc/refcount.h>
#include <isc/rwlock.h>
#include <isc/sockaddr.h>
#include <isc/thread.h>
#include <isc/tls.h>
#include <isc/util.h>
@ -1074,6 +1075,10 @@ isc_tls_cert_store_free(isc_tls_cert_store_t **pstore) {
#define TLSCTX_CACHE_MAGIC ISC_MAGIC('T', 'l', 'S', 'c')
#define VALID_TLSCTX_CACHE(t) ISC_MAGIC_VALID(t, TLSCTX_CACHE_MAGIC)
#define TLSCTX_CLIENT_SESSION_CACHE_MAGIC ISC_MAGIC('T', 'l', 'C', 'c')
#define VALID_TLSCTX_CLIENT_SESSION_CACHE(t) \
ISC_MAGIC_VALID(t, TLSCTX_CLIENT_SESSION_CACHE_MAGIC)
typedef struct isc_tlsctx_cache_entry {
/*
* We need a TLS context entry for each transport on both IPv4 and
@ -1081,6 +1086,8 @@ typedef struct isc_tlsctx_cache_entry {
* session-resumption cache.
*/
isc_tlsctx_t *ctx[isc_tlsctx_cache_count - 1][2];
isc_tlsctx_client_session_cache_t
*client_sess_cache[isc_tlsctx_cache_count - 1][2];
/*
* One certificate store is enough for all the contexts defined
* above. We need that for peer validation.
@ -1133,6 +1140,11 @@ tlsctx_cache_entry_destroy(isc_mem_t *mctx, isc_tlsctx_cache_entry_t *entry) {
if (entry->ctx[i][k] != NULL) {
isc_tlsctx_free(&entry->ctx[i][k]);
}
if (entry->client_sess_cache[i][k] != NULL) {
isc_tlsctx_client_session_cache_detach(
&entry->client_sess_cache[i][k]);
}
}
}
if (entry->ca_store != NULL) {
@ -1182,17 +1194,21 @@ isc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep) {
}
isc_result_t
isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
const isc_tlsctx_cache_transport_t transport,
const uint16_t family, isc_tlsctx_t *ctx,
isc_tls_cert_store_t *store, isc_tlsctx_t **pfound,
isc_tls_cert_store_t **pfound_store) {
isc_tlsctx_cache_add(
isc_tlsctx_cache_t *cache, const char *name,
const isc_tlsctx_cache_transport_t transport, const uint16_t family,
isc_tlsctx_t *ctx, isc_tls_cert_store_t *store,
isc_tlsctx_client_session_cache_t *client_sess_cache,
isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store,
isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) {
isc_result_t result = ISC_R_FAILURE;
size_t name_len, tr_offset;
isc_tlsctx_cache_entry_t *entry = NULL;
bool ipv6;
REQUIRE(VALID_TLSCTX_CACHE(cache));
REQUIRE(client_sess_cache == NULL ||
VALID_TLSCTX_CLIENT_SESSION_CACHE(client_sess_cache));
REQUIRE(name != NULL && *name != '\0');
REQUIRE(transport > isc_tlsctx_cache_none &&
transport < isc_tlsctx_cache_count);
@ -1208,6 +1224,7 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
result = isc_ht_find(cache->data, (const uint8_t *)name, name_len,
(void **)&entry);
if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) {
isc_tlsctx_client_session_cache_t *found_client_sess_cache;
/* The entry exists. */
if (pfound != NULL) {
INSIST(*pfound == NULL);
@ -1218,6 +1235,14 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
INSIST(*pfound_store == NULL);
*pfound_store = entry->ca_store;
}
found_client_sess_cache =
entry->client_sess_cache[tr_offset][ipv6];
if (pfound_client_sess_cache != NULL &&
found_client_sess_cache != NULL) {
INSIST(*pfound_client_sess_cache == NULL);
*pfound_client_sess_cache = found_client_sess_cache;
}
result = ISC_R_EXISTS;
} else if (result == ISC_R_SUCCESS &&
entry->ctx[tr_offset][ipv6] == NULL) {
@ -1226,10 +1251,11 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
* particular transport/IP type combination.
*/
entry->ctx[tr_offset][ipv6] = ctx;
entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache;
/*
* As the passed certificates store object is supposed to be
* internally managed by the cache object anyway, we might
* destroy the unneeded store object right now.
* As the passed certificates store object is supposed
* to be internally managed by the cache object anyway,
* we might destroy the unneeded store object right now.
*/
if (store != NULL && store != entry->ca_store) {
isc_tls_cert_store_free(&store);
@ -1244,6 +1270,7 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
/* Oracle/Red Hat Linux, GCC bug #53119 */
memset(entry, 0, sizeof(*entry));
entry->ctx[tr_offset][ipv6] = ctx;
entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache;
entry->ca_store = store;
RUNTIME_CHECK(isc_ht_add(cache->data, (const uint8_t *)name,
name_len,
@ -1257,10 +1284,11 @@ isc_tlsctx_cache_add(isc_tlsctx_cache_t *cache, const char *name,
}
isc_result_t
isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name,
const isc_tlsctx_cache_transport_t transport,
const uint16_t family, isc_tlsctx_t **pctx,
isc_tls_cert_store_t **pstore) {
isc_tlsctx_cache_find(
isc_tlsctx_cache_t *cache, const char *name,
const isc_tlsctx_cache_transport_t transport, const uint16_t family,
isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore,
isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) {
isc_result_t result = ISC_R_FAILURE;
size_t tr_offset;
isc_tlsctx_cache_entry_t *entry = NULL;
@ -1287,7 +1315,16 @@ isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name,
}
if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) {
isc_tlsctx_client_session_cache_t *found_client_sess_cache =
entry->client_sess_cache[tr_offset][ipv6];
*pctx = entry->ctx[tr_offset][ipv6];
if (pfound_client_sess_cache != NULL &&
found_client_sess_cache != NULL) {
INSIST(*pfound_client_sess_cache == NULL);
*pfound_client_sess_cache = found_client_sess_cache;
}
} else if (result == ISC_R_SUCCESS &&
entry->ctx[tr_offset][ipv6] == NULL) {
result = ISC_R_NOTFOUND;
@ -1299,3 +1336,319 @@ isc_tlsctx_cache_find(isc_tlsctx_cache_t *cache, const char *name,
return (result);
}
typedef struct client_session_cache_entry client_session_cache_entry_t;
typedef struct client_session_cache_bucket {
char *bucket_key;
size_t bucket_key_len;
/* Cache entries within the bucket (from the oldest to the newest). */
ISC_LIST(client_session_cache_entry_t) entries;
} client_session_cache_bucket_t;
struct client_session_cache_entry {
SSL_SESSION *session;
client_session_cache_bucket_t *bucket; /* "Parent" bucket pointer. */
ISC_LINK(client_session_cache_entry_t) bucket_link;
ISC_LINK(client_session_cache_entry_t) cache_link;
};
struct isc_tlsctx_client_session_cache {
uint32_t magic;
isc_refcount_t references;
isc_mem_t *mctx;
/*
* We need to keep a reference to the related TLS context in order
* to ensure that it remains valid while the TLS client sessions
* cache object is valid, as every TLS session object
* (SSL_SESSION) is "tied" to a particular context.
*/
isc_tlsctx_t *ctx;
/*
* The idea is to have one bucket per remote server. Each bucket,
* can maintain multiple TLS sessions to that server, as BIND
* might want to establish multiple TLS connections to the remote
* server at once.
*/
isc_ht_t *buckets;
/*
* The list of all current entries within the cache maintained in
* LRU-manner, so that the oldest entry might be efficiently
* removed.
*/
ISC_LIST(client_session_cache_entry_t) lru_entries;
/* Number of the entries within the cache. */
size_t nentries;
/* Maximum number of the entries within the cache. */
size_t max_entries;
isc_mutex_t lock;
};
isc_tlsctx_client_session_cache_t *
isc_tlsctx_client_session_cache_new(isc_mem_t *mctx, isc_tlsctx_t *ctx,
const size_t max_entries) {
isc_tlsctx_client_session_cache_t *nc;
REQUIRE(ctx != NULL);
REQUIRE(max_entries > 0);
nc = isc_mem_get(mctx, sizeof(*nc));
*nc = (isc_tlsctx_client_session_cache_t){ .max_entries = max_entries };
isc_refcount_init(&nc->references, 1);
isc_mem_attach(mctx, &nc->mctx);
isc_tlsctx_attach(ctx, &nc->ctx);
isc_ht_init(&nc->buckets, mctx, 5);
ISC_LIST_INIT(nc->lru_entries);
isc_mutex_init(&nc->lock);
nc->magic = TLSCTX_CLIENT_SESSION_CACHE_MAGIC;
return (nc);
}
void
isc_tlsctx_client_session_cache_attach(
isc_tlsctx_client_session_cache_t *source,
isc_tlsctx_client_session_cache_t **targetp) {
REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(source));
REQUIRE(targetp != NULL && *targetp == NULL);
isc_refcount_increment(&source->references);
*targetp = source;
}
static void
client_cache_entry_delete(isc_tlsctx_client_session_cache_t *restrict cache,
client_session_cache_entry_t *restrict entry) {
client_session_cache_bucket_t *restrict bucket = entry->bucket;
/* Unlink and free the cache entry */
ISC_LIST_UNLINK(bucket->entries, entry, bucket_link);
ISC_LIST_UNLINK(cache->lru_entries, entry, cache_link);
cache->nentries--;
(void)SSL_SESSION_free(entry->session);
isc_mem_put(cache->mctx, entry, sizeof(*entry));
/* The bucket is empty - let's remove it */
if (ISC_LIST_EMPTY(bucket->entries)) {
RUNTIME_CHECK(isc_ht_delete(cache->buckets,
(const uint8_t *)bucket->bucket_key,
bucket->bucket_key_len) ==
ISC_R_SUCCESS);
isc_mem_free(cache->mctx, bucket->bucket_key);
isc_mem_put(cache->mctx, bucket, sizeof(*bucket));
}
}
void
isc_tlsctx_client_session_cache_detach(
isc_tlsctx_client_session_cache_t **cachep) {
isc_tlsctx_client_session_cache_t *cache = NULL;
client_session_cache_entry_t *entry = NULL, *next = NULL;
REQUIRE(cachep != NULL);
cache = *cachep;
*cachep = NULL;
REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
if (isc_refcount_decrement(&cache->references) != 1) {
return;
}
cache->magic = 0;
isc_refcount_destroy(&cache->references);
entry = ISC_LIST_HEAD(cache->lru_entries);
while (entry != NULL) {
next = ISC_LIST_NEXT(entry, cache_link);
client_cache_entry_delete(cache, entry);
entry = next;
}
RUNTIME_CHECK(isc_ht_count(cache->buckets) == 0);
isc_ht_destroy(&cache->buckets);
isc_mutex_destroy(&cache->lock);
isc_tlsctx_free(&cache->ctx);
isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
}
static bool
ssl_session_seems_resumable(const SSL_SESSION *sess) {
#ifdef HAVE_SSL_SESSION_IS_RESUMABLE
/*
* If SSL_SESSION_is_resumable() is available, let's use that. It
* is expected to be available on OpenSSL >= 1.1.1 and its modern
* siblings.
*/
return (SSL_SESSION_is_resumable(sess) != 0);
#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L)
/*
* Taking into consideration that OpenSSL 1.1.0 uses opaque
* pointers for SSL_SESSION, we cannot implement a replacement for
* SSL_SESSION_is_resumable() manually. Let's use a sensible
* approximation for that, then: if there is an associated session
* ticket or session ID, then, most likely, the session is
* resumable.
*/
unsigned int session_id_len = 0;
(void)SSL_SESSION_get_id(sess, &session_id_len);
return (SSL_SESSION_has_ticket(sess) || session_id_len > 0);
#else
return (!sess->not_resumable &&
(sess->session_id_length > 0 || sess->tlsext_ticklen > 0));
#endif
}
void
isc_tlsctx_client_session_cache_keep(isc_tlsctx_client_session_cache_t *cache,
char *remote_peer_name, isc_tls_t *tls) {
size_t name_len;
isc_result_t result;
SSL_SESSION *sess;
client_session_cache_bucket_t *restrict bucket = NULL;
client_session_cache_entry_t *restrict entry = NULL;
REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
REQUIRE(remote_peer_name != NULL && *remote_peer_name != '\0');
REQUIRE(tls != NULL);
sess = SSL_get1_session(tls);
if (sess == NULL) {
return;
} else if (!ssl_session_seems_resumable(sess)) {
SSL_SESSION_free(sess);
return;
}
isc_mutex_lock(&cache->lock);
name_len = strlen(remote_peer_name);
result = isc_ht_find(cache->buckets, (const uint8_t *)remote_peer_name,
name_len, (void **)&bucket);
if (result != ISC_R_SUCCESS) {
/* Let's create a new bucket */
INSIST(bucket == NULL);
bucket = isc_mem_get(cache->mctx, sizeof(*bucket));
*bucket = (client_session_cache_bucket_t){
.bucket_key = isc_mem_strdup(cache->mctx,
remote_peer_name),
.bucket_key_len = name_len
};
ISC_LIST_INIT(bucket->entries);
RUNTIME_CHECK(isc_ht_add(cache->buckets,
(const uint8_t *)remote_peer_name,
name_len,
(void *)bucket) == ISC_R_SUCCESS);
}
/* Let's add a new cache entry to the new/found bucket */
entry = isc_mem_get(cache->mctx, sizeof(*entry));
*entry = (client_session_cache_entry_t){ .session = sess,
.bucket = bucket };
ISC_LINK_INIT(entry, bucket_link);
ISC_LINK_INIT(entry, cache_link);
ISC_LIST_APPEND(bucket->entries, entry, bucket_link);
ISC_LIST_APPEND(cache->lru_entries, entry, cache_link);
cache->nentries++;
if (cache->nentries > cache->max_entries) {
/*
* Cache overrun. We need to remove the oldest entry from the
* cache
*/
client_session_cache_entry_t *restrict oldest;
INSIST((cache->nentries - 1) == cache->max_entries);
oldest = ISC_LIST_HEAD(cache->lru_entries);
client_cache_entry_delete(cache, oldest);
}
isc_mutex_unlock(&cache->lock);
}
void
isc_tlsctx_client_session_cache_reuse(isc_tlsctx_client_session_cache_t *cache,
char *remote_peer_name, isc_tls_t *tls) {
client_session_cache_bucket_t *restrict bucket = NULL;
client_session_cache_entry_t *restrict entry;
size_t name_len;
isc_result_t result;
REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
REQUIRE(remote_peer_name != NULL && *remote_peer_name != '\0');
REQUIRE(tls != NULL);
isc_mutex_lock(&cache->lock);
/* Let's find the bucket */
name_len = strlen(remote_peer_name);
result = isc_ht_find(cache->buckets, (const uint8_t *)remote_peer_name,
name_len, (void **)&bucket);
if (result != ISC_R_SUCCESS) {
goto exit;
}
INSIST(bucket != NULL);
/*
* If the bucket has been found, let's use the newest session from
* the bucket, as it has the highest chance to be successfully
* resumed.
*/
INSIST(!ISC_LIST_EMPTY(bucket->entries));
entry = ISC_LIST_TAIL(bucket->entries);
RUNTIME_CHECK(SSL_set_session(tls, entry->session) == 1);
client_cache_entry_delete(cache, entry);
exit:
isc_mutex_unlock(&cache->lock);
}
void
isc_tlsctx_client_session_cache_keep_sockaddr(
isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
isc_tls_t *tls) {
char peername[ISC_SOCKADDR_FORMATSIZE] = { 0 };
REQUIRE(remote_peer != NULL);
isc_sockaddr_format(remote_peer, peername, sizeof(peername));
isc_tlsctx_client_session_cache_keep(cache, peername, tls);
}
void
isc_tlsctx_client_session_cache_reuse_sockaddr(
isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer,
isc_tls_t *tls) {
char peername[ISC_SOCKADDR_FORMATSIZE] = { 0 };
REQUIRE(remote_peer != NULL);
isc_sockaddr_format(remote_peer, peername, sizeof(peername));
isc_tlsctx_client_session_cache_reuse(cache, peername, tls);
}
const isc_tlsctx_t *
isc_tlsctx_client_session_cache_getctx(
isc_tlsctx_client_session_cache_t *cache) {
REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache));
return (cache->ctx);
}

View file

@ -49,7 +49,7 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
*/
result = isc_tlsctx_cache_find(tlsctx_cache, tls_params->name,
transport, family, &sslctx,
&found_store);
&found_store, NULL);
if (result != ISC_R_SUCCESS) {
/*
* The lookup failed, let's try to create a new context
@ -150,7 +150,8 @@ listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
RUNTIME_CHECK(isc_tlsctx_cache_add(
tlsctx_cache, tls_params->name,
transport, family, sslctx, store,
NULL, NULL) == ISC_R_SUCCESS);
NULL, NULL, NULL,
NULL) == ISC_R_SUCCESS);
} else {
INSIST(sslctx != NULL);
}

View file

@ -79,6 +79,7 @@ static atomic_bool slowdown = false;
static atomic_bool use_TLS = false;
static isc_tlsctx_t *server_tlsctx = NULL;
static isc_tlsctx_t *client_tlsctx = NULL;
static isc_tlsctx_client_session_cache_t *client_sess_cache = NULL;
static isc_quota_t listener_quota;
static atomic_bool check_listener_quota = false;
@ -176,7 +177,8 @@ connect_send_request(isc_nm_t *mgr, const char *uri, bool post,
}
isc_nm_httpconnect(mgr, NULL, &tcp_listen_addr, uri, post,
connect_send_cb, data, ctx, timeout, 0);
connect_send_cb, data, ctx, client_sess_cache,
timeout, 0);
}
static int
@ -321,6 +323,9 @@ setup_test(void **state) {
client_tlsctx = NULL;
isc_tlsctx_createclient(&client_tlsctx);
isc_tlsctx_enable_http2client_alpn(client_tlsctx);
client_sess_cache = isc_tlsctx_client_session_cache_new(
mctx, client_tlsctx,
ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE);
isc_quota_init(&listener_quota, 0);
atomic_store(&check_listener_quota, false);
@ -350,6 +355,8 @@ teardown_test(void **state) {
isc_tlsctx_free(&client_tlsctx);
}
isc_tlsctx_client_session_cache_detach(&client_sess_cache);
isc_quota_destroy(&listener_quota);
isc_nm_http_endpoints_detach(&endpoints);
@ -644,7 +651,7 @@ doh_timeout_recovery(void **state) {
ISC_NM_HTTP_DEFAULT_PATH);
isc_nm_httpconnect(connect_nm, NULL, &tcp_listen_addr, req_url,
atomic_load(&POST), timeout_request_cb, NULL, ctx,
T_SOFT, 0);
client_sess_cache, T_SOFT, 0);
/*
* Sleep until sends reaches 5.
@ -932,7 +939,7 @@ doh_recv_two(void **state) {
isc_nm_httpconnect(connect_nm, NULL, &tcp_listen_addr, req_url,
atomic_load(&POST), doh_connect_send_two_requests_cb,
NULL, ctx, 5000, 0);
NULL, ctx, client_sess_cache, 5000, 0);
while (atomic_load(&nsends) > 0) {
if (atomic_load(&was_error)) {

View file

@ -28,6 +28,7 @@
#include <isc/refcount.h>
#include <isc/sockaddr.h>
#include <isc/thread.h>
#include <isc/tls.h>
#include <isc/util.h>
#include "uv_wrap.h"
@ -62,6 +63,7 @@ static isc_sockaddr_t tcp_listen_addr;
static isc_sockaddr_t tcp_connect_addr;
static isc_tlsctx_t *tcp_listen_tlsctx = NULL;
static isc_tlsctx_t *tcp_connect_tlsctx = NULL;
static isc_tlsctx_client_session_cache_t *tcp_tlsctx_client_sess_cache = NULL;
static uint64_t send_magic = 0;
static uint64_t stop_magic = 0;
@ -333,6 +335,10 @@ setup_test(void **state __attribute__((unused))) {
isc_tlsctx_enable_dot_client_alpn(tcp_connect_tlsctx);
tcp_tlsctx_client_sess_cache = isc_tlsctx_client_session_cache_new(
mctx, tcp_connect_tlsctx,
ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE);
return (0);
}
@ -361,6 +367,8 @@ teardown_test(void **state __attribute__((unused))) {
isc_refcount_destroy(&active_ssends);
isc_refcount_destroy(&active_sreads);
isc_tlsctx_client_session_cache_detach(&tcp_tlsctx_client_sess_cache);
return (0);
}
@ -1156,7 +1164,8 @@ stream_connect(isc_nm_cb_t cb, void *cbarg, unsigned int timeout,
if (stream_use_TLS) {
isc_nm_tlsconnect(connect_nm, &tcp_connect_addr,
&tcp_listen_addr, cb, cbarg,
tcp_connect_tlsctx, timeout, extrahandlesize);
tcp_connect_tlsctx,
tcp_tlsctx_client_sess_cache, timeout, 0);
return;
}
#endif
@ -2059,7 +2068,7 @@ static void
tls_connect(isc_nm_t *nm) {
isc_nm_tlsconnect(nm, &tcp_connect_addr, &tcp_listen_addr,
connect_connect_cb, NULL, tcp_connect_tlsctx,
T_CONNECT, 0);
tcp_tlsctx_client_sess_cache, T_CONNECT, 0);
}
ISC_RUN_TEST_IMPL(tls_noop) {
@ -2220,7 +2229,7 @@ static void
tlsdns_connect(isc_nm_t *nm) {
isc_nm_tlsdnsconnect(nm, &tcp_connect_addr, &tcp_listen_addr,
connect_connect_cb, NULL, T_CONNECT, 0,
tcp_connect_tlsctx);
tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache);
}
ISC_RUN_TEST_IMPL(tlsdns_noop) {
@ -2240,7 +2249,7 @@ ISC_RUN_TEST_IMPL(tlsdns_noop) {
isc_refcount_increment0(&active_cconnects);
isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr,
connect_connect_cb, NULL, T_CONNECT, 0,
tcp_connect_tlsctx);
tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache);
isc__netmgr_shutdown(connect_nm);
@ -2267,7 +2276,7 @@ ISC_RUN_TEST_IMPL(tlsdns_noresponse) {
isc_refcount_increment0(&active_cconnects);
isc_nm_tlsdnsconnect(connect_nm, &connect_addr, &tcp_listen_addr,
connect_connect_cb, NULL, T_CONNECT, 0,
tcp_connect_tlsctx);
tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache);
WAIT_FOR_EQ(cconnects, 1);
WAIT_FOR_EQ(csends, 1);
@ -2321,7 +2330,7 @@ ISC_RUN_TEST_IMPL(tlsdns_timeout_recovery) {
isc_refcount_increment0(&active_cconnects);
isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr,
connect_connect_cb, NULL, T_SOFT, 0,
tcp_connect_tlsctx);
tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache);
WAIT_FOR_EQ(cconnects, 1);
WAIT_FOR_GE(csends, 1);
@ -2352,7 +2361,7 @@ ISC_RUN_TEST_IMPL(tlsdns_recv_one) {
isc_refcount_increment0(&active_cconnects);
isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr,
connect_connect_cb, NULL, T_CONNECT, 0,
tcp_connect_tlsctx);
tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache);
WAIT_FOR_EQ(cconnects, 1);
WAIT_FOR_LE(nsends, 0);
@ -2394,14 +2403,14 @@ ISC_RUN_TEST_IMPL(tlsdns_recv_two) {
isc_refcount_increment0(&active_cconnects);
isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr,
connect_connect_cb, NULL, T_CONNECT, 0,
tcp_connect_tlsctx);
tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache);
WAIT_FOR_EQ(cconnects, 1);
isc_refcount_increment0(&active_cconnects);
isc_nm_tlsdnsconnect(connect_nm, &tcp_connect_addr, &tcp_listen_addr,
connect_connect_cb, NULL, T_CONNECT, 0,
tcp_connect_tlsctx);
tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache);
WAIT_FOR_EQ(cconnects, 2);
@ -2664,7 +2673,7 @@ ISC_RUN_TEST_IMPL(tlsdns_connect_noalpn) {
isc_refcount_increment0(&active_cconnects);
isc_nm_tlsdnsconnect(connect_nm, &connect_addr, &tcp_listen_addr,
tlsdns_connect_connect_noalpn, NULL, T_CONNECT, 0,
connect_tlsctx_noalpn);
connect_tlsctx_noalpn, NULL);
WAIT_FOR_EQ(active_cconnects, 0);
@ -2730,7 +2739,7 @@ ISC_RUN_TEST_IMPL(tlsdns_listen_noalpn) {
isc_refcount_increment0(&active_cconnects);
isc_nm_tlsdnsconnect(connect_nm, &connect_addr, &tcp_listen_addr,
connect_connect_cb, NULL, T_CONNECT, 0,
tcp_connect_tlsctx);
tcp_connect_tlsctx, tcp_tlsctx_client_sess_cache);
WAIT_FOR_EQ(saccepts, 1);
WAIT_FOR_EQ(cconnects, 1);