struct Curl_hash_element *e = (struct Curl_hash_element *) element;
if(e->ptr) {
- h->dtor(e->ptr);
+ if(e->dtor)
+ e->dtor(e->key, e->key_len, e->ptr);
+ else
+ h->dtor(e->ptr);
e->ptr = NULL;
}
}
static struct Curl_hash_element *
-mk_hash_element(const void *key, size_t key_len, const void *p)
+mk_hash_element(const void *key, size_t key_len, const void *p,
+ Curl_hash_elem_dtor dtor)
{
/* allocate the struct plus memory after it to store the key */
struct Curl_hash_element *he = malloc(sizeof(struct Curl_hash_element) +
memcpy(he->key, key, key_len);
he->key_len = key_len;
he->ptr = (void *) p;
+ he->dtor = dtor;
}
return he;
}
#define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)]
-/* Insert the data in the hash. If there already was a match in the hash, that
- * data is replaced. This function also "lazily" allocates the table if
- * needed, as it isn't done in the _init function (anymore).
- *
- * @unittest: 1305
- * @unittest: 1602
- * @unittest: 1603
- */
-void *
-Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
+void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p,
+ Curl_hash_elem_dtor dtor)
{
struct Curl_hash_element *he;
struct Curl_llist_element *le;
}
}
- he = mk_hash_element(key, key_len, p);
+ he = mk_hash_element(key, key_len, p, dtor);
if(he) {
Curl_llist_append(l, he, &he->list);
++h->size;
return NULL; /* failure */
}
+/* Insert the data in the hash. If there already was a match in the hash, that
+ * data is replaced. This function also "lazily" allocates the table if
+ * needed, as it isn't done in the _init function (anymore).
+ *
+ * @unittest: 1305
+ * @unittest: 1602
+ * @unittest: 1603
+ */
+void *
+Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
+{
+ return Curl_hash_add2(h, key, key_len, p, NULL);
+}
+
/* Remove the identified hash entry.
* Returns non-zero on failure.
*
size_t size;
};
+typedef void (*Curl_hash_elem_dtor)(void *key, size_t key_len, void *p);
+
struct Curl_hash_element {
struct Curl_llist_element list;
void *ptr;
+ Curl_hash_elem_dtor dtor;
size_t key_len;
char key[1]; /* allocated memory following the struct */
};
Curl_hash_dtor dtor);
void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p);
+void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p,
+ Curl_hash_elem_dtor dtor);
int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len);
void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len);
#define Curl_hash_count(h) ((h)->size)
sh_freeentry);
}
+/* multi->proto_hash destructor. Should never be called as elements
+ * MUST be added with their own destructor */
+static void ph_freeentry(void *p)
+{
+ (void)p;
+ /* Will always be FALSE. Cannot use a 0 assert here since compilers
+ * are not in agreement if they then want a NORETURN attribute or
+ * not. *sigh* */
+ DEBUGASSERT(p == NULL);
+}
+
/*
* multi_addmsg()
*
sh_init(&multi->sockhash, hashsize);
+ Curl_hash_init(&multi->proto_hash, 23,
+ Curl_hash_str, Curl_str_key_compare, ph_freeentry);
+
if(Curl_conncache_init(&multi->conn_cache, chashsize))
goto error;
error:
sockhash_destroy(&multi->sockhash);
+ Curl_hash_destroy(&multi->proto_hash);
Curl_hash_destroy(&multi->hostcache);
Curl_conncache_destroy(&multi->conn_cache);
free(multi);
Curl_conncache_close_all_connections(&multi->conn_cache);
sockhash_destroy(&multi->sockhash);
+ Curl_hash_destroy(&multi->proto_hash);
Curl_conncache_destroy(&multi->conn_cache);
Curl_hash_destroy(&multi->hostcache);
Curl_psl_destroy(&multi->psl);
#endif
#endif
-#ifdef USE_SSL
- Curl_free_multi_ssl_backend_data(multi->ssl_backend_data);
-#endif
-
multi_xfer_bufs_free(multi);
free(multi);
/* value for MAXIMUM CONCURRENT STREAMS upper limit */
#define INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1)
-/* Curl_multi SSL backend-specific data; declared differently by each SSL
- backend */
-struct multi_ssl_backend_data;
-
/* This is the struct known as CURLM on the outside */
struct Curl_multi {
/* First a simple identifier to easier detect if a user mix up
char *xfer_ulbuf; /* the actual buffer */
size_t xfer_ulbuf_len; /* the allocated length */
-#if defined(USE_SSL)
- struct multi_ssl_backend_data *ssl_backend_data;
-#endif
-
/* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
the pluralis form, there can be more than one easy handle waiting on the
same actual socket) */
struct Curl_hash sockhash;
+ /* `proto_hash` is a general key-value store for protocol implementations
+ * with the lifetime of the multi handle. The number of elements kept here
+ * should be in the order of supported protocols (and sub-protocols like
+ * TLS), *not* in the order of connections or current transfers!
+ * Elements need to be added with their own destructor to be invoked when
+ * the multi handle is cleaned up (see Curl_hash_add2()).*/
+ struct Curl_hash proto_hash;
/* Shared connection cache (bundles)*/
struct conncache conn_cache;
bearssl_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- NULL, /* free_multi_ssl_backend_data */
bearssl_recv, /* recv decrypted data */
bearssl_send, /* send data to encrypt */
};
gtls_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- NULL, /* free_multi_ssl_backend_data */
gtls_recv, /* recv decrypted data */
gtls_send, /* send data to encrypt */
};
mbedtls_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- NULL, /* free_multi_ssl_backend_data */
mbed_recv, /* recv decrypted data */
mbed_send, /* send data to encrypt */
};
#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
#endif /* !LIBRESSL_VERSION_NUMBER */
-#if defined(HAVE_SSL_X509_STORE_SHARE)
-struct multi_ssl_backend_data {
- char *CAfile; /* CAfile path used to generate X509 store */
- X509_STORE *store; /* cached X509 store or NULL if none */
- struct curltime time; /* when the cached store was created */
-};
-#endif /* HAVE_SSL_X509_STORE_SHARE */
-
#define push_certinfo(_label, _num) \
do { \
long info_len = BIO_get_mem_data(mem, &ptr); \
}
#if defined(HAVE_SSL_X509_STORE_SHARE)
-static bool cached_x509_store_expired(const struct Curl_easy *data,
- const struct multi_ssl_backend_data *mb)
+
+/* key to use at `multi->proto_hash` */
+#define MPROTO_OSSL_X509_KEY "tls:ossl:x509:share"
+
+struct ossl_x509_share {
+ char *CAfile; /* CAfile path used to generate X509 store */
+ X509_STORE *store; /* cached X509 store or NULL if none */
+ struct curltime time; /* when the cached store was created */
+};
+
+static void oss_x509_share_free(void *key, size_t key_len, void *p)
+{
+ struct ossl_x509_share *share = p;
+ DEBUGASSERT(key_len == (sizeof(MPROTO_OSSL_X509_KEY)-1));
+ DEBUGASSERT(!memcmp(MPROTO_OSSL_X509_KEY, key, key_len));
+ (void)key;
+ (void)key_len;
+ if(share->store) {
+ X509_STORE_free(share->store);
+ }
+ free(share->CAfile);
+ free(share);
+}
+
+static bool
+cached_x509_store_expired(const struct Curl_easy *data,
+ const struct ossl_x509_share *mb)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
struct curltime now = Curl_now();
return elapsed_ms >= timeout_ms;
}
-static bool cached_x509_store_different(
- struct Curl_cfilter *cf,
- const struct multi_ssl_backend_data *mb)
+static bool
+cached_x509_store_different(struct Curl_cfilter *cf,
+ const struct ossl_x509_share *mb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!mb->CAfile || !conn_config->CAfile)
const struct Curl_easy *data)
{
struct Curl_multi *multi = data->multi;
+ struct ossl_x509_share *share;
X509_STORE *store = NULL;
DEBUGASSERT(multi);
- if(multi &&
- multi->ssl_backend_data &&
- multi->ssl_backend_data->store &&
- !cached_x509_store_expired(data, multi->ssl_backend_data) &&
- !cached_x509_store_different(cf, multi->ssl_backend_data)) {
- store = multi->ssl_backend_data->store;
+ share = multi? Curl_hash_pick(&multi->proto_hash,
+ (void *)MPROTO_OSSL_X509_KEY,
+ sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL;
+ if(share && share->store &&
+ !cached_x509_store_expired(data, share) &&
+ !cached_x509_store_different(cf, share)) {
+ store = share->store;
}
return store;
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_multi *multi = data->multi;
- struct multi_ssl_backend_data *mbackend;
+ struct ossl_x509_share *share;
DEBUGASSERT(multi);
if(!multi)
return;
+ share = Curl_hash_pick(&multi->proto_hash,
+ (void *)MPROTO_OSSL_X509_KEY,
+ sizeof(MPROTO_OSSL_X509_KEY)-1);
- if(!multi->ssl_backend_data) {
- multi->ssl_backend_data = calloc(1, sizeof(struct multi_ssl_backend_data));
- if(!multi->ssl_backend_data)
+ if(!share) {
+ share = calloc(1, sizeof(*share));
+ if(!share)
+ return;
+ if(!Curl_hash_add2(&multi->proto_hash,
+ (void *)MPROTO_OSSL_X509_KEY,
+ sizeof(MPROTO_OSSL_X509_KEY)-1,
+ share, oss_x509_share_free)) {
+ free(share);
return;
+ }
}
- mbackend = multi->ssl_backend_data;
-
if(X509_STORE_up_ref(store)) {
char *CAfile = NULL;
}
}
- if(mbackend->store) {
- X509_STORE_free(mbackend->store);
- free(mbackend->CAfile);
+ if(share->store) {
+ X509_STORE_free(share->store);
+ free(share->CAfile);
}
- mbackend->time = Curl_now();
- mbackend->store = store;
- mbackend->CAfile = CAfile;
+ share->time = Curl_now();
+ share->store = store;
+ share->CAfile = CAfile;
}
}
(void *)octx->ssl_ctx : (void *)octx->ssl;
}
-static void ossl_free_multi_ssl_backend_data(
- struct multi_ssl_backend_data *mbackend)
-{
-#if defined(HAVE_SSL_X509_STORE_SHARE)
- if(mbackend->store) {
- X509_STORE_free(mbackend->store);
- }
- free(mbackend->CAfile);
- free(mbackend);
-#else /* HAVE_SSL_X509_STORE_SHARE */
- (void)mbackend;
-#endif /* HAVE_SSL_X509_STORE_SHARE */
-}
-
const struct Curl_ssl Curl_ssl_openssl = {
{ CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */
#endif
NULL, /* use of data in this connection */
NULL, /* remote of data from this connection */
- ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */
ossl_recv, /* recv decrypted data */
ossl_send, /* send data to encrypt */
};
NULL, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- NULL, /* free_multi_ssl_backend_data */
cr_recv, /* recv decrypted data */
cr_send, /* send data to encrypt */
};
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_multi *multi = data->multi;
const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
- struct schannel_multi_ssl_backend_data *mbackend;
+ struct schannel_cert_share *share;
const struct ssl_general_config *cfg = &data->set.general_ssl;
timediff_t timeout_ms;
timediff_t elapsed_ms;
DEBUGASSERT(multi);
- if(!multi || !multi->ssl_backend_data) {
+ if(!multi) {
return NULL;
}
- mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data;
- if(!mbackend->cert_store) {
+ share = Curl_hash_pick(&multi->proto_hash,
+ (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+ sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1);
+ if(!share || !share->cert_store) {
return NULL;
}
timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
if(timeout_ms >= 0) {
now = Curl_now();
- elapsed_ms = Curl_timediff(now, mbackend->time);
+ elapsed_ms = Curl_timediff(now, share->time);
if(elapsed_ms >= timeout_ms) {
return NULL;
}
}
if(ca_info_blob) {
- if(!mbackend->CAinfo_blob_digest) {
+ if(!share->CAinfo_blob_digest) {
return NULL;
}
- if(mbackend->CAinfo_blob_size != ca_info_blob->len) {
+ if(share->CAinfo_blob_size != ca_info_blob->len) {
return NULL;
}
schannel_sha256sum((const unsigned char *)ca_info_blob->data,
ca_info_blob->len,
info_blob_digest,
CURL_SHA256_DIGEST_LENGTH);
- if(memcmp(mbackend->CAinfo_blob_digest,
+ if(memcmp(share->CAinfo_blob_digest,
info_blob_digest,
CURL_SHA256_DIGEST_LENGTH)) {
return NULL;
}
}
else {
- if(!conn_config->CAfile || !mbackend->CAfile ||
- strcmp(mbackend->CAfile, conn_config->CAfile)) {
+ if(!conn_config->CAfile || !share->CAfile ||
+ strcmp(share->CAfile, conn_config->CAfile)) {
return NULL;
}
}
- return mbackend->cert_store;
+ return share->cert_store;
+}
+
+static void schannel_cert_share_free(void *key, size_t key_len, void *p)
+{
+ struct schannel_cert_share *share = p;
+ DEBUGASSERT(key_len == (sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1));
+ DEBUGASSERT(!memcmp(MPROTO_SCHANNEL_CERT_SHARE_KEY, key, key_len));
+ (void)key;
+ (void)key_len;
+ if(share->cert_store) {
+ CertCloseStore(share->cert_store, 0);
+ }
+ free(share->CAinfo_blob_digest);
+ free(share->CAfile);
+ free(share);
}
bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_multi *multi = data->multi;
const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
- struct schannel_multi_ssl_backend_data *mbackend;
+ struct schannel_cert_share *share;
unsigned char *CAinfo_blob_digest = NULL;
size_t CAinfo_blob_size = 0;
char *CAfile = NULL;
return false;
}
- if(!multi->ssl_backend_data) {
- multi->ssl_backend_data =
- calloc(1, sizeof(struct schannel_multi_ssl_backend_data));
- if(!multi->ssl_backend_data) {
+ share = Curl_hash_pick(&multi->proto_hash,
+ (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+ sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1);
+ if(!share) {
+ share = calloc(1, sizeof(*share));
+ if(!share) {
+ return false;
+ }
+ if(!Curl_hash_add2(&multi->proto_hash,
+ (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+ sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1,
+ share, schannel_cert_share_free)) {
+ free(share);
return false;
}
}
- mbackend = (struct schannel_multi_ssl_backend_data *)multi->ssl_backend_data;
-
-
if(ca_info_blob) {
CAinfo_blob_digest = malloc(CURL_SHA256_DIGEST_LENGTH);
if(!CAinfo_blob_digest) {
}
/* free old cache data */
- if(mbackend->cert_store) {
- CertCloseStore(mbackend->cert_store, 0);
+ if(share->cert_store) {
+ CertCloseStore(share->cert_store, 0);
}
- free(mbackend->CAinfo_blob_digest);
- free(mbackend->CAfile);
+ free(share->CAinfo_blob_digest);
+ free(share->CAfile);
- mbackend->time = Curl_now();
- mbackend->cert_store = cert_store;
- mbackend->CAinfo_blob_digest = CAinfo_blob_digest;
- mbackend->CAinfo_blob_size = CAinfo_blob_size;
- mbackend->CAfile = CAfile;
+ share->time = Curl_now();
+ share->cert_store = cert_store;
+ share->CAinfo_blob_digest = CAinfo_blob_digest;
+ share->CAinfo_blob_size = CAinfo_blob_size;
+ share->CAfile = CAfile;
return true;
}
-static void schannel_free_multi_ssl_backend_data(
- struct multi_ssl_backend_data *msbd)
-{
- struct schannel_multi_ssl_backend_data *mbackend =
- (struct schannel_multi_ssl_backend_data*)msbd;
- if(mbackend->cert_store) {
- CertCloseStore(mbackend->cert_store, 0);
- }
- free(mbackend->CAinfo_blob_digest);
- free(mbackend->CAfile);
- free(mbackend);
-}
-
const struct Curl_ssl Curl_ssl_schannel = {
{ CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */
schannel_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- schannel_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */
schannel_recv, /* recv decrypted data */
schannel_send, /* send data to encrypt */
};
#endif
};
-struct schannel_multi_ssl_backend_data {
+/* key to use at `multi->proto_hash` */
+#define MPROTO_SCHANNEL_CERT_SHARE_KEY "tls:schannel:cert:share"
+
+struct schannel_cert_share {
unsigned char *CAinfo_blob_digest; /* CA info blob digest */
size_t CAinfo_blob_size; /* CA info blob size */
char *CAfile; /* CAfile path used to generate
sectransp_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- NULL, /* free_multi_ssl_backend_data */
sectransp_recv, /* recv decrypted data */
sectransp_send, /* send data to encrypt */
};
return CURLE_OK;
}
-void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend)
-{
- if(Curl_ssl->free_multi_ssl_backend_data && mbackend)
- Curl_ssl->free_multi_ssl_backend_data(mbackend);
-}
-
void Curl_ssl_close_all(struct Curl_easy *data)
{
/* kill the session ID cache if not shared */
NULL, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- NULL, /* free_multi_ssl_backend_data */
multissl_recv_plain, /* recv decrypted data */
multissl_send_plain, /* send data to encrypt */
};
/* Curl_multi SSL backend-specific data; declared differently by each SSL
backend */
-struct multi_ssl_backend_data;
struct Curl_cfilter;
CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
bool Curl_ssl_false_start(struct Curl_easy *data);
-void Curl_free_multi_ssl_backend_data(struct multi_ssl_backend_data *mbackend);
-
#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
bool (*attach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
void (*detach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
- void (*free_multi_ssl_backend_data)(struct multi_ssl_backend_data *mbackend);
-
ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *code);
ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
wolfssl_sha256sum, /* sha256sum */
NULL, /* associate_connection */
NULL, /* disassociate_connection */
- NULL, /* free_multi_ssl_backend_data */
wolfssl_recv, /* recv decrypted data */
wolfssl_send, /* send data to encrypt */
};
(void)p; /* unused */
}
+static size_t elem_dtor_calls;
+
+static void my_elem_dtor(void *key, size_t key_len, void *p)
+{
+ (void)p; /* unused */
+ (void)key; /* unused */
+ (void)key_len; /* unused */
+ ++elem_dtor_calls;
+}
+
static CURLcode unit_setup(void)
{
Curl_hash_init(&hash_static, slots, Curl_hash_str,
nodep = Curl_hash_pick(&hash_static, &key3, strlen(key3));
fail_unless(nodep == key3, "hash retrieval failed");
+ /* Add element with own destructor */
+ nodep = Curl_hash_add2(&hash_static, &key1, strlen(key1), &key1,
+ my_elem_dtor);
+ fail_unless(nodep, "add2 insertion into hash failed");
+ fail_unless(elem_dtor_calls == 0, "element destructor count should be 0");
+ /* Add it again, should invoke destructor on first */
+ nodep = Curl_hash_add2(&hash_static, &key1, strlen(key1), &key1,
+ my_elem_dtor);
+ fail_unless(nodep, "add2 again, insertion into hash failed");
+ fail_unless(elem_dtor_calls == 1, "element destructor count should be 1");
+ /* remove, should invoke destructor */
+ rc = Curl_hash_delete(&hash_static, &key1, strlen(key1));
+ fail_unless(rc == 0, "hash delete failed");
+ fail_unless(elem_dtor_calls == 2, "element destructor count should be 1");
+
+
/* Clean up */
Curl_hash_clean(&hash_static);