Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multi->proto_hash, key-value store for protocols #13345

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 24 additions & 13 deletions lib/hash.c
Expand Up @@ -40,7 +40,10 @@ hash_element_dtor(void *user, void *element)
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;
}

Expand Down Expand Up @@ -77,7 +80,8 @@ Curl_hash_init(struct Curl_hash *h,
}

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) +
Expand All @@ -87,22 +91,15 @@ mk_hash_element(const void *key, size_t key_len, const void *p)
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;
Expand Down Expand Up @@ -130,7 +127,7 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
}
}

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;
Expand All @@ -140,6 +137,20 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
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.
*
Expand Down
5 changes: 5 additions & 0 deletions lib/hash.h
Expand Up @@ -58,9 +58,12 @@ struct Curl_hash {
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 */
};
Expand All @@ -78,6 +81,8 @@ void Curl_hash_init(struct Curl_hash *h,
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);
void Curl_hash_apply(struct Curl_hash *h, void *user,
Expand Down
20 changes: 16 additions & 4 deletions lib/multi.c
Expand Up @@ -366,6 +366,17 @@ static void sh_init(struct Curl_hash *hash, int hashsize)
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()
*
Expand All @@ -392,6 +403,9 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */

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;

Expand Down Expand Up @@ -427,6 +441,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
error:

sockhash_destroy(&multi->sockhash);
Curl_hash_destroy(&multi->proto_hash);
Curl_hash_destroy(&multi->hostcache);
Curl_conncache_destroy(&multi->conn_cache);
free(multi);
Expand Down Expand Up @@ -2848,6 +2863,7 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *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);
Expand All @@ -2861,10 +2877,6 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
#endif
#endif

#ifdef USE_SSL
Curl_free_multi_ssl_backend_data(multi->ssl_backend_data);
#endif

multi_xfer_bufs_free(multi);
free(multi);

Expand Down
15 changes: 7 additions & 8 deletions lib/multihandle.h
Expand Up @@ -79,10 +79,6 @@ typedef enum {
/* 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
Expand Down Expand Up @@ -131,14 +127,17 @@ struct Curl_multi {
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;
Expand Down
1 change: 0 additions & 1 deletion lib/vtls/bearssl.c
Expand Up @@ -1219,7 +1219,6 @@ const struct Curl_ssl Curl_ssl_bearssl = {
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 */
};
Expand Down
1 change: 0 additions & 1 deletion lib/vtls/gtls.c
Expand Up @@ -1813,7 +1813,6 @@ const struct Curl_ssl Curl_ssl_gnutls = {
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 */
};
Expand Down
1 change: 0 additions & 1 deletion lib/vtls/mbedtls.c
Expand Up @@ -1354,7 +1354,6 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
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 */
};
Expand Down
104 changes: 58 additions & 46 deletions lib/vtls/openssl.c
Expand Up @@ -298,14 +298,6 @@ typedef unsigned long sslerr_t;
#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); \
Expand Down Expand Up @@ -3353,8 +3345,33 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
}

#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();
Expand All @@ -3367,9 +3384,9 @@ static bool cached_x509_store_expired(const struct Curl_easy *data,
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)
Expand All @@ -3382,15 +3399,17 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
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;
Expand All @@ -3402,20 +3421,28 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
{
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;

Expand All @@ -3427,14 +3454,14 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
}
}

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;
}
}

Expand Down Expand Up @@ -4961,20 +4988,6 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl,
(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 */

Expand Down Expand Up @@ -5016,7 +5029,6 @@ const struct Curl_ssl Curl_ssl_openssl = {
#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 */
};
Expand Down
1 change: 0 additions & 1 deletion lib/vtls/rustls.c
Expand Up @@ -751,7 +751,6 @@ const struct Curl_ssl Curl_ssl_rustls = {
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 */
};
Expand Down