[1mdiff --git a/src/tlsrequest.c b/src/tlsrequest.c[m
[1mindex 22fb215..943a493 100644[m
[1m--- a/src/tlsrequest.c[m
[1m+++ b/src/tlsrequest.c[m
[36m@@ -28,6 +28,7 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.[m
#include "the_Foundation/tlsrequest.h"[m
#include "the_Foundation/buffer.h"[m
#include "the_Foundation/socket.h"[m
[32m+[m[32m#include "the_Foundation/stringhash.h"[m
#include "the_Foundation/thread.h"[m
#include "the_Foundation/time.h"[m
[m
[36m@@ -53,13 +54,120 @@[m [mstatic void initContext_(void);[m
static iTlsCertificate *newX509Chain_TlsCertificate_(X509 *cert, STACK_OF(X509) *chain);[m
static void certificateVerifyFailed_TlsRequest_(iTlsRequest *, const iTlsCertificate *cert);[m
[m
[32m+[m[32mstatic iBool readAllFromBIO_(BIO *bio, iBlock *out) {[m
[32m+[m[32m char buf[DEFAULT_BUF_SIZE];[m
[32m+[m[32m int n;[m
[32m+[m[32m do {[m
[32m+[m[32m n = BIO_read(bio, buf, sizeof(buf));[m
[32m+[m[32m if (n > 0) {[m
[32m+[m[32m appendData_Block(out, buf, n);[m
[32m+[m[32m }[m
[32m+[m[32m else if (!BIO_should_retry(bio)) {[m
[32m+[m[32m return iFalse;[m
[32m+[m[32m }[m
[32m+[m[32m } while (n > 0);[m
[32m+[m[32m return iTrue;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32m/----------------------------------------------------------------------------------------------/[m
[32m+[m
[32m+[m[32miDeclareClass(CachedSession)[m
[32m+[m[41m [m
[32m+[m[32mstatic const int maxSessionAge_CachedSession_ = 10 * 60; /* seconds */[m
[32m+[m[41m [m
[32m+[m[32mstruct Impl_CachedSession {[m
[32m+[m[32m iObject object;[m
[32m+[m[32m iBlock pemSession;[m
[32m+[m[32m iTime timestamp;[m
[32m+[m[32m iTlsCertificate cert; / not sent if session reused */[m
[32m+[m[32m};[m
[32m+[m
[32m+[m[32mstatic void init_CachedSession(iCachedSession *d, SSL_SESSION *sess, const iTlsCertificate *cert) {[m
[32m+[m[32m BIO *buf = BIO_new(BIO_s_mem());[m
[32m+[m[32m PEM_write_bio_SSL_SESSION(buf, sess);[m
[32m+[m[32m init_Block(&d->pemSession, 0);[m
[32m+[m[32m readAllFromBIO_(buf, &d->pemSession);[m
[32m+[m[32m BIO_free(buf);[m
[32m+[m[32m initCurrent_Time(&d->timestamp);[m
[32m+[m[32m d->cert = copy_TlsCertificate(cert);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void deinit_CachedSession(iCachedSession *d) {[m
[32m+[m[32m deinit_Block(&d->pemSession);[m
[32m+[m[32m delete_TlsCertificate(d->cert);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32miDefineClass(CachedSession)[m
[32m+[m[32miDefineObjectConstructionArgs(CachedSession,[m
[32m+[m[32m (SSL_SESSION *sess, const iTlsCertificate *cert),[m
[32m+[m[32m sess, cert)[m
[32m+[m
[32m+[m[32mstatic void reuse_CachedSession(const iCachedSession *d, SSL *ssl) {[m
[32m+[m[32m BIO *buf = BIO_new_mem_buf(constData_Block(&d->pemSession), size_Block(&d->pemSession));[m
[32m+[m[32m SSL_SESSION *sess = NULL;[m
[32m+[m[32m PEM_read_bio_SSL_SESSION(buf, &sess, NULL, NULL);[m
[32m+[m[32m SSL_SESSION_up_ref(sess);[m
[32m+[m[32m SSL_set_session(ssl, sess);[m
[32m+[m[32m BIO_free(buf);[m
[32m+[m[32m SSL_SESSION_free(sess);[m
[32m+[m[32m}[m
[32m+[m
struct Impl_Context {[m
SSL_CTX * ctx;[m
X509_STORE * certStore;[m
iTlsRequestVerifyFunc userVerifyFunc;[m
tss_t tssKeyCurrentRequest;[m
[32m+[m[32m iMutex cacheMutex;[m
[32m+[m[32m iStringHash * cache; /* key is "address:port"; these could be saved persistently */[m
};[m
[m
[32m+[m[32mstatic iString *cacheKey_(const iString *host, uint16_t port) {[m
[32m+[m[32m iString *key = copy_String(host);[m
[32m+[m[32m appendFormat_String(key, ":%u", port);[m
[32m+[m[32m return key;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic iBool isExpired_CachedSession_(const iCachedSession *d) {[m
[32m+[m[32m if (!d) return iTrue;[m
[32m+[m[32m return elapsedSeconds_Time(&d->timestamp) > maxSessionAge_CachedSession_;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic iTlsCertificate *maybeReuseSession_Context_(iContext *d, SSL *ssl, const iString *host,[m
[32m+[m[32m uint16_t port) {[m
[32m+[m[32m iTlsCertificate *cert = NULL;[m
[32m+[m[32m iString *key = cacheKey_(host, port);[m
[32m+[m[32m lock_Mutex(&d->cacheMutex);[m
[32m+[m[32m /* Remove old entries from the session cache. */[m
[32m+[m[32m iForEach(StringHash, i, d->cache) {[m
[32m+[m[32m iCachedSession *cs = i.value->object;[m
[32m+[m[32m if (isExpired_CachedSession_(cs)) {[m
[32m+[m[32m iDebug("[TlsRequest] session for %s
has expired\n", cstr_Block(&i.value->keyBlock));[m
[32m+[m[32m remove_StringHashIterator(&i);[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m iCachedSession *cs = value_StringHash(d->cache, key);[m
[32m+[m[32m if (cs) {[m
[32m+[m[32m reuse_CachedSession(cs, ssl);[m
[32m+[m[32m cert = copy_TlsCertificate(cs->cert);[m
[32m+[m[32m iDebug("[TlsRequest] reusing session for %s
\n", cstr_String(key));[m
[32m+[m[32m }[m
[32m+[m[32m unlock_Mutex(&d->cacheMutex);[m
[32m+[m[32m delete_String(key);[m
[32m+[m[32m return cert; /* caller gets ownership */[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void saveSession_Context_(iContext *d, const iString *host, uint16_t port,[m
[32m+[m[32m SSL_SESSION *sess, const iTlsCertificate *cert) {[m
[32m+[m[32m if (sess && cert) {[m
[32m+[m[32m iString *key = cacheKey_(host, port);[m
[32m+[m[32m lock_Mutex(&d->cacheMutex);[m
[32m+[m[32m insert_StringHash(d->cache, key, new_CachedSession(sess, cert));[m
[32m+[m[32m unlock_Mutex(&d->cacheMutex);[m
[32m+[m[32m iDebug("[TlsRequest] saved session for %s
\n", cstr_String(key));[m
[32m+[m[32m delete_String(key);[m
[32m+[m[32m }[m
[32m+[m[32m}[m
[32m+[m
static iTlsRequest *currentRequestForThread_Context_(iContext *d) {[m
return tss_get(context_->tssKeyCurrentRequest);[m
}[m
[36m@@ -118,17 +226,22 @@[m [mvoid init_Context(iContext *d) {[m
ERR_load_BIO_strings();[m
d->ctx = SSL_CTX_new(TLS_client_method());[m
if (!d->ctx) {[m
[31m- iDebug("[TlsRequest] Failed to initialize OpenSSL\n");[m
[32m+[m[32m iDebug("[TlsRequest] failed to initialize OpenSSL\n");[m
iAssert(d->ctx);[m
}[m
[32m+[m[32m d->certStore = NULL;[m
d->userVerifyFunc = NULL;[m
SSL_CTX_set_verify(d->ctx, SSL_VERIFY_PEER, verifyCallback_Context_);[m
/* Bug workarounds: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html */[m
SSL_CTX_set_options(d->ctx, SSL_OP_ALL);[m
[31m- d->certStore = NULL;[m
[32m+[m[32m init_Mutex(&d->cacheMutex);[m
[32m+[m[32m d->cache = new_StringHash();[m
[32m+[m[32m SSL_CTX_set_session_cache_mode(d->ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE);[m
}[m
[m
void deinit_Context(iContext *d) {[m
[32m+[m[32m iRelease(d->cache);[m
[32m+[m[32m deinit_Mutex(&d->cacheMutex);[m
SSL_CTX_free(d->ctx);[m
tss_delete(d->tssKeyCurrentRequest);[m
}[m
[36m@@ -137,6 +250,8 @@[m [miBool isValid_Context(iContext *d) {[m
return d->ctx != NULL;[m
}[m
[m
[32m+[m[32m/----------------------------------------------------------------------------------------------/[m
[32m+[m
void setCACertificates_TlsRequest(const iString *caFile, const iString *caPath) {[m
initContext_();[m
iContext *d = context_;[m
[36m@@ -172,21 +287,6 @@[m [mstatic void initContext_(void) {[m
}[m
}[m
[m
[31m-static iBool readAllFromBIO_(BIO *bio, iBlock *out) {[m
[31m- char buf[DEFAULT_BUF_SIZE];[m
[31m- int n;[m
[31m- do {[m
[31m- n = BIO_read(bio, buf, sizeof(buf));[m
[31m- if (n > 0) {[m
[31m- appendData_Block(out, buf, n);[m
[31m- }[m
[31m- else if (!BIO_should_retry(bio)) {[m
[31m- return iFalse;[m
[31m- }[m
[31m- } while (n > 0);[m
[31m- return iTrue;[m
[31m-}[m
[31m-[m
/----------------------------------------------------------------------------------------------/[m
[m
struct Impl_TlsCertificate {[m
[36m@@ -203,12 +303,19 @@[m [mvoid init_TlsCertificate(iTlsCertificate *d) {[m
d->pkey = NULL;[m
}[m
[m
[32m+[m[32mstatic void freeX509Chain_(STACK_OF(X509) *chain) {[m
[32m+[m[32m for (int i = 0; i < sk_X509_num(chain); i++) {[m
[32m+[m[32m X509_free(sk_X509_value(chain, i));[m
[32m+[m[32m }[m
[32m+[m[32m sk_X509_free(chain);[m
[32m+[m[32m}[m
[32m+[m
void deinit_TlsCertificate(iTlsCertificate *d) {[m
if (d->cert) {[m
X509_free(d->cert);[m
}[m
if (d->chain) {[m
[31m- sk_X509_free(d->chain);[m
[32m+[m[32m freeX509Chain_(d->chain);[m
}[m
if (d->pkey) {[m
EVP_PKEY_free(d->pkey);[m
[36m@@ -355,7 +462,7 @@[m [miTlsCertificate *copy_TlsCertificate(const iTlsCertificate *d) {[m
X509_up_ref(d->cert);[m
copy->cert = d->cert;[m
}[m
[31m- copy->chain = d->chain ? sk_X509_dup(d->chain) : NULL;[m
[32m+[m[32m copy->chain = d->chain ? X509_chain_up_ref(d->chain) : NULL;[m
if (d->pkey) {[m
EVP_PKEY_up_ref(d->pkey);[m
copy->pkey = d->pkey;[m
[36m@@ -796,9 +903,9 @@[m [mstatic int processIncoming_TlsRequest_(iTlsRequest *d, const char *src, size_t l[m
}[m
}[m
if (!d->cert) {[m
[31m- const STACK_OF(X509) *chain = SSL_get_peer_cert_chain(d->ssl);[m
[32m+[m[32m STACK_OF(X509) *chain = SSL_get_peer_cert_chain(d->ssl);[m
d->cert = newX509Chain_TlsCertificate_(SSL_get_peer_certificate(d->ssl),[m
[31m- sk_X509_dup(chain));[m
[32m+[m[32m X509_chain_up_ref(chain));[m
}[m
/* The encrypted data is now in the input bio so now we can perform actual[m
read of unencrypted data. */[m
[36m@@ -881,6 +988,9 @@[m [mstatic iThreadResult run_TlsRequest_(iThread *thread) {[m
}[m
}[m
}[m
[32m+[m[32m if (!SSL_session_reused(d->ssl) && d->status != error_TlsRequestStatus) {[m
[32m+[m[32m saveSession_Context_(context_, d->hostName, d->port, SSL_get0_session(d->ssl), d->cert);[m
[32m+[m[32m }[m
readIncoming_TlsRequest_(d);[m
iNotifyAudience(d, finished, TlsRequestFinished);[m
iDebug("[TlsRequest] finished\n");[m
[36m@@ -941,6 +1051,7 @@[m [mvoid submit_TlsRequest(iTlsRequest *d) {[m
SSL_use_certificate(d->ssl, d->clientCert->cert);[m
SSL_use_PrivateKey(d->ssl, d->clientCert->pkey);[m
}[m
[32m+[m[32m d->cert = maybeReuseSession_Context_(context_, d->ssl, d->hostName, d->port);[m
d->socket = new_Socket(cstr_String(d->hostName), d->port);[m
iConnect(Socket, d->socket, connected, d, connected_TlsRequest_);[m
iConnect(Socket, d->socket, disconnected, d, disconnected_TlsRequest_);[m
text/plain
This content has been proxied by September (ba2dc).