#include "cryptlib.h"
#include <openssl/evp.h>
#include <openssl/lhash.h>
#include "eng_int.h"
typedef struct st_engine_pile
{
int nid;
STACK_OF(ENGINE) *sk;
ENGINE *funct;
int uptodate;
} ENGINE_PILE;
struct st_engine_table
{
LHASH piles;
};
static unsigned int table_flags = 0;
unsigned int ENGINE_get_table_flags(void)
{
return table_flags;
}
void ENGINE_set_table_flags(unsigned int flags)
{
table_flags = flags;
}
static unsigned long engine_pile_hash(const ENGINE_PILE *c)
{
return c->nid;
}
static int engine_pile_cmp(const ENGINE_PILE *a, const ENGINE_PILE *b)
{
return a->nid - b->nid;
}
static IMPLEMENT_LHASH_HASH_FN(engine_pile_hash, const ENGINE_PILE *)
static IMPLEMENT_LHASH_COMP_FN(engine_pile_cmp, const ENGINE_PILE *)
static int int_table_check(ENGINE_TABLE **t, int create)
{
LHASH *lh;
if(*t) return 1;
if(!create) return 0;
if((lh = lh_new(LHASH_HASH_FN(engine_pile_hash),
LHASH_COMP_FN(engine_pile_cmp))) == NULL)
return 0;
*t = (ENGINE_TABLE *)lh;
return 1;
}
int engine_table_register(ENGINE_TABLE **table, ENGINE_CLEANUP_CB *cleanup,
ENGINE *e, const int *nids, int num_nids, int setdefault)
{
int ret = 0, added = 0;
ENGINE_PILE tmplate, *fnd;
CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
if(!(*table))
added = 1;
if(!int_table_check(table, 1))
goto end;
if(added)
engine_cleanup_add_first(cleanup);
while(num_nids--)
{
tmplate.nid = *nids;
fnd = lh_retrieve(&(*table)->piles, &tmplate);
if(!fnd)
{
fnd = OPENSSL_malloc(sizeof(ENGINE_PILE));
if(!fnd) goto end;
fnd->uptodate = 1;
fnd->nid = *nids;
fnd->sk = sk_ENGINE_new_null();
if(!fnd->sk)
{
OPENSSL_free(fnd);
goto end;
}
fnd->funct = NULL;
lh_insert(&(*table)->piles, fnd);
}
(void)sk_ENGINE_delete_ptr(fnd->sk, e);
if(!sk_ENGINE_push(fnd->sk, e))
goto end;
fnd->uptodate = 0;
if(setdefault)
{
if(!engine_unlocked_init(e))
{
ENGINEerr(ENGINE_F_ENGINE_TABLE_REGISTER,
ENGINE_R_INIT_FAILED);
goto end;
}
if(fnd->funct)
engine_unlocked_finish(fnd->funct, 0);
fnd->funct = e;
fnd->uptodate = 1;
}
nids++;
}
ret = 1;
end:
CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
return ret;
}
static void int_unregister_cb(ENGINE_PILE *pile, ENGINE *e)
{
int n;
while((n = sk_ENGINE_find(pile->sk, e)) >= 0)
{
(void)sk_ENGINE_delete(pile->sk, n);
pile->uptodate = 0;
}
if(pile->funct == e)
{
engine_unlocked_finish(e, 0);
pile->funct = NULL;
}
}
static IMPLEMENT_LHASH_DOALL_ARG_FN(int_unregister_cb,ENGINE_PILE *,ENGINE *)
void engine_table_unregister(ENGINE_TABLE **table, ENGINE *e)
{
CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
if(int_table_check(table, 0))
lh_doall_arg(&(*table)->piles,
LHASH_DOALL_ARG_FN(int_unregister_cb), e);
CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
}
static void int_cleanup_cb(ENGINE_PILE *p)
{
sk_ENGINE_free(p->sk);
if(p->funct)
engine_unlocked_finish(p->funct, 0);
OPENSSL_free(p);
}
static IMPLEMENT_LHASH_DOALL_FN(int_cleanup_cb,ENGINE_PILE *)
void engine_table_cleanup(ENGINE_TABLE **table)
{
CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
if(*table)
{
lh_doall(&(*table)->piles, LHASH_DOALL_FN(int_cleanup_cb));
lh_free(&(*table)->piles);
*table = NULL;
}
CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
}
#ifndef ENGINE_TABLE_DEBUG
ENGINE *engine_table_select(ENGINE_TABLE **table, int nid)
#else
ENGINE *engine_table_select_tmp(ENGINE_TABLE **table, int nid, const char *f, int l)
#endif
{
ENGINE *ret = NULL;
ENGINE_PILE tmplate, *fnd=NULL;
int initres, loop = 0;
if(!(*table))
{
#ifdef ENGINE_TABLE_DEBUG
fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, nothing "
"registered!\n", f, l, nid);
#endif
return NULL;
}
ERR_set_mark();
CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
if(!int_table_check(table, 0)) goto end;
tmplate.nid = nid;
fnd = lh_retrieve(&(*table)->piles, &tmplate);
if(!fnd) goto end;
if(fnd->funct && engine_unlocked_init(fnd->funct))
{
#ifdef ENGINE_TABLE_DEBUG
fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
"ENGINE '%s' cached\n", f, l, nid, fnd->funct->id);
#endif
ret = fnd->funct;
goto end;
}
if(fnd->uptodate)
{
ret = fnd->funct;
goto end;
}
trynext:
ret = sk_ENGINE_value(fnd->sk, loop++);
if(!ret)
{
#ifdef ENGINE_TABLE_DEBUG
fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, no "
"registered implementations would initialise\n",
f, l, nid);
#endif
goto end;
}
if((ret->funct_ref > 0) || !(table_flags & ENGINE_TABLE_FLAG_NOINIT))
initres = engine_unlocked_init(ret);
else
initres = 0;
if(initres)
{
if((fnd->funct != ret) && engine_unlocked_init(ret))
{
if(fnd->funct)
engine_unlocked_finish(fnd->funct, 0);
fnd->funct = ret;
#ifdef ENGINE_TABLE_DEBUG
fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, "
"setting default to '%s'\n", f, l, nid, ret->id);
#endif
}
#ifdef ENGINE_TABLE_DEBUG
fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, using "
"newly initialised '%s'\n", f, l, nid, ret->id);
#endif
goto end;
}
goto trynext;
end:
if(fnd) fnd->uptodate = 1;
#ifdef ENGINE_TABLE_DEBUG
if(ret)
fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
"ENGINE '%s'\n", f, l, nid, ret->id);
else
fprintf(stderr, "engine_table_dbg: %s:%d, nid=%d, caching "
"'no matching ENGINE'\n", f, l, nid);
#endif
CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
ERR_pop_to_mark();
return ret;
}