+#ifdef EXPERIMENTAL_TLS_RESUME
+/* On the client, get any stashed session for the given IP from hints db
+and apply it to the ssl-connection for attempted resumption. */
+
+static void
+tls_retrieve_session(tls_support * tlsp, SSL * ssl, const uschar * key)
+{
+tlsp->resumption |= RESUME_SUPPORTED;
+if (tlsp->host_resumable)
+ {
+ dbdata_tls_session * dt;
+ int len;
+ open_db dbblock, * dbm_file;
+
+ tlsp->resumption |= RESUME_CLIENT_REQUESTED;
+ DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", key);
+ if ((dbm_file = dbfn_open(US"tls", O_RDONLY, &dbblock, FALSE, FALSE)))
+ {
+ /* key for the db is the IP */
+ if ((dt = dbfn_read_with_length(dbm_file, key, &len)))
+ {
+ SSL_SESSION * ss = NULL;
+ const uschar * sess_asn1 = dt->session;
+
+ len -= sizeof(dbdata_tls_session);
+ if (!(d2i_SSL_SESSION(&ss, &sess_asn1, (long)len)))
+ {
+ DEBUG(D_tls)
+ {
+ ERR_error_string_n(ERR_get_error(),
+ ssl_errstring, sizeof(ssl_errstring));
+ debug_printf("decoding session: %s\n", ssl_errstring);
+ }
+ }
+#ifdef EXIM_HAVE_SESSION_TICKET
+ else if ( SSL_SESSION_get_ticket_lifetime_hint(ss) + dt->time_stamp
+ < time(NULL))
+ {
+ DEBUG(D_tls) debug_printf("session expired\n");
+ dbfn_delete(dbm_file, key);
+ }
+#endif
+ else if (!SSL_set_session(ssl, ss))
+ {
+ DEBUG(D_tls)
+ {
+ ERR_error_string_n(ERR_get_error(),
+ ssl_errstring, sizeof(ssl_errstring));
+ debug_printf("applying session to ssl: %s\n", ssl_errstring);
+ }
+ }
+ else
+ {
+ DEBUG(D_tls) debug_printf("good session\n");
+ tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
+ tlsp->verify_override = dt->verify_override;
+ tlsp->ocsp = dt->ocsp;
+ }
+ }
+ else
+ DEBUG(D_tls) debug_printf("no session record\n");
+ dbfn_close(dbm_file);
+ }
+ }
+}
+
+
+/* On the client, save the session for later resumption */
+
+static int
+tls_save_session_cb(SSL * ssl, SSL_SESSION * ss)
+{
+tls_ext_ctx_cb * cbinfo = SSL_get_ex_data(ssl, tls_exdata_idx);
+tls_support * tlsp;
+
+DEBUG(D_tls) debug_printf("tls_save_session_cb\n");
+
+if (!cbinfo || !(tlsp = cbinfo->tlsp)->host_resumable) return 0;
+
+# ifdef OPENSSL_HAVE_NUM_TICKETS
+if (SSL_SESSION_is_resumable(ss)) /* 1.1.1 */
+# endif
+ {
+ int len = i2d_SSL_SESSION(ss, NULL);
+ int dlen = sizeof(dbdata_tls_session) + len;
+ dbdata_tls_session * dt = store_get(dlen);
+ uschar * s = dt->session;
+ open_db dbblock, * dbm_file;
+
+ DEBUG(D_tls) debug_printf("session is resumable\n");
+ tlsp->resumption |= RESUME_SERVER_TICKET; /* server gave us a ticket */
+
+ dt->verify_override = tlsp->verify_override;
+ dt->ocsp = tlsp->ocsp;
+ (void) i2d_SSL_SESSION(ss, &s); /* s gets bumped to end */
+
+ if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
+ {
+ const uschar * key = cbinfo->host->address;
+ dbfn_delete(dbm_file, key);
+ dbfn_write(dbm_file, key, dt, dlen);
+ dbfn_close(dbm_file);
+ DEBUG(D_tls) debug_printf("wrote session (len %u) to db\n",
+ (unsigned)dlen);
+ }
+ }
+return 1;
+}
+
+
+static void
+tls_client_ctx_resume_prehandshake(
+ exim_openssl_client_tls_ctx * exim_client_ctx, tls_support * tlsp,
+ smtp_transport_options_block * ob, host_item * host)
+{
+/* Should the client request a session resumption ticket? */
+if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
+ {
+ tlsp->host_resumable = TRUE;
+
+ SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx,
+ SSL_SESS_CACHE_CLIENT
+ | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+ SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb);
+ }
+}
+
+static BOOL
+tls_client_ssl_resume_prehandshake(SSL * ssl, tls_support * tlsp,
+ host_item * host, uschar ** errstr)
+{
+if (tlsp->host_resumable)
+ {
+ DEBUG(D_tls)
+ debug_printf("tls_resumption_hosts overrides openssl_options, enabling tickets\n");
+ SSL_clear_options(ssl, SSL_OP_NO_TICKET);
+
+ tls_exdata_idx = SSL_get_ex_new_index(0, 0, 0, 0, 0);
+ if (!SSL_set_ex_data(ssl, tls_exdata_idx, client_static_cbinfo))
+ {
+ tls_error(US"set ex_data", host, NULL, errstr);
+ return FALSE;
+ }
+ debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_cbinfo);
+ }
+
+tlsp->resumption = RESUME_SUPPORTED;
+/* Pick up a previous session, saved on an old ticket */
+tls_retrieve_session(tlsp, ssl, host->address);
+return TRUE;
+}
+
+static void
+tls_client_resume_posthandshake(exim_openssl_client_tls_ctx * exim_client_ctx,
+ tls_support * tlsp)
+{
+if (SSL_session_reused(exim_client_ctx->ssl))
+ {
+ DEBUG(D_tls) debug_printf("The session was reused\n");
+ tlsp->resumption |= RESUME_USED;
+ }
+}
+#endif /* EXPERIMENTAL_TLS_RESUME */
+
+