multi: add multi->proto_hash, a key-value store for protocol data

- add `Curl_hash_add2()` that passes a destructor function for
  the element added. Call element destructor instead of hash
  destructor if present.
- multi: add `proto_hash` for protocol related information,
  remove `struct multi_ssl_backend_data`.
- openssl: use multi->proto_hash to keep x509 shared store
- schannel: use multi->proto_hash to keep x509 shared store
- vtls: remove Curl_free_multi_ssl_backend_data() and its
  equivalents in the TLS backends

Closes #13345
This commit is contained in:
Stefan Eissing 2024-04-11 12:34:40 +02:00 committed by Daniel Stenberg
parent 74e0bb1e7a
commit e101a7a8b0
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
17 changed files with 191 additions and 132 deletions

View file

@ -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;
}
@ -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) +
@ -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;
@ -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;
@ -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.
*

View file

@ -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 */
};
@ -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);
#define Curl_hash_count(h) ((h)->size)

View file

@ -370,6 +370,17 @@ static void sh_init(struct Curl_hash *hash, size_t 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()
*
@ -396,6 +407,9 @@ struct Curl_multi *Curl_multi_handle(size_t 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;
@ -431,6 +445,7 @@ struct Curl_multi *Curl_multi_handle(size_t 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);
@ -2856,6 +2871,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);
@ -2869,10 +2885,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);

View file

@ -80,10 +80,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
@ -132,14 +128,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;

View file

@ -1130,7 +1130,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 */
};

View file

@ -1856,7 +1856,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 */
};

View file

@ -1515,7 +1515,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 */
};

View file

@ -307,14 +307,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); \
@ -3355,8 +3347,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();
@ -3369,9 +3386,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)
@ -3384,15 +3401,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;
@ -3404,20 +3423,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;
@ -3429,14 +3456,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;
}
}
@ -5233,20 +5260,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 */
@ -5290,7 +5303,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 */
};

View file

@ -783,7 +783,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 */
};

View file

@ -2752,7 +2752,7 @@ HCERTSTORE Curl_schannel_get_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;
const struct ssl_general_config *cfg = &data->set.general_ssl;
timediff_t timeout_ms;
timediff_t elapsed_ms;
@ -2761,12 +2761,14 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
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;
}
@ -2781,37 +2783,52 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
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,
@ -2821,7 +2838,7 @@ 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;
@ -2832,17 +2849,23 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
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) {
@ -2864,33 +2887,20 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
}
/* 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 */
@ -2925,7 +2935,6 @@ const struct Curl_ssl Curl_ssl_schannel = {
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 */
};

View file

@ -159,7 +159,10 @@ struct schannel_ssl_backend_data {
#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

View file

@ -3476,7 +3476,6 @@ const struct Curl_ssl Curl_ssl_sectransp = {
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 */
};

View file

@ -753,12 +753,6 @@ out:
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 */
@ -1339,7 +1333,6 @@ static const struct Curl_ssl Curl_ssl_multi = {
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 */
};

View file

@ -52,7 +52,6 @@ struct Curl_ssl_session;
/* 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,
@ -181,8 +180,6 @@ bool Curl_ssl_cert_status_request(void);
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,

View file

@ -135,8 +135,6 @@ struct Curl_ssl {
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,

View file

@ -1518,7 +1518,6 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
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 */
};

View file

@ -39,6 +39,16 @@ static void mydtor(void *p)
(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,
@ -147,6 +157,22 @@ UNITTEST_START
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);