+#include <openssl/lhash.h>
+
+/* What an "implementation of ex_data functionality" looks like */
+struct st_CRYPTO_EX_DATA_IMPL
+ {
+ /*********************/
+ /* GLOBAL OPERATIONS */
+ /* Return a new class index */
+ int (*cb_new_class)(void);
+ /* Cleanup all state used by the implementation */
+ void (*cb_cleanup)(void);
+ /************************/
+ /* PER-CLASS OPERATIONS */
+ /* Get a new method index within a class */
+ int (*cb_get_new_index)(int class_index, long argl, void *argp,
+ CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
+ CRYPTO_EX_free *free_func);
+ /* Initialise a new CRYPTO_EX_DATA of a given class */
+ int (*cb_new_ex_data)(int class_index, void *obj,
+ CRYPTO_EX_DATA *ad);
+ /* Duplicate a CRYPTO_EX_DATA of a given class onto a copy */
+ int (*cb_dup_ex_data)(int class_index, CRYPTO_EX_DATA *to,
+ CRYPTO_EX_DATA *from);
+ /* Cleanup a CRYPTO_EX_DATA of a given class */
+ void (*cb_free_ex_data)(int class_index, void *obj,
+ CRYPTO_EX_DATA *ad);
+ };
+
+/* The implementation we use at run-time */
+static const CRYPTO_EX_DATA_IMPL *impl = NULL;
+
+/* To call "impl" functions, use this macro rather than referring to 'impl' directly, eg.
+ * EX_IMPL(get_new_index)(...); */
+#define EX_IMPL(a) impl->cb_##a
+
+/* Predeclare the "default" ex_data implementation */
+static int int_new_class(void);
+static void int_cleanup(void);
+static int int_get_new_index(int class_index, long argl, void *argp,
+ CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func,
+ CRYPTO_EX_free *free_func);
+static int int_new_ex_data(int class_index, void *obj,
+ CRYPTO_EX_DATA *ad);
+static int int_dup_ex_data(int class_index, CRYPTO_EX_DATA *to,
+ CRYPTO_EX_DATA *from);
+static void int_free_ex_data(int class_index, void *obj,
+ CRYPTO_EX_DATA *ad);
+static CRYPTO_EX_DATA_IMPL impl_default =
+ {
+ int_new_class,
+ int_cleanup,
+ int_get_new_index,
+ int_new_ex_data,
+ int_dup_ex_data,
+ int_free_ex_data
+ };
+
+/* Internal function that checks whether "impl" is set and if not, sets it to
+ * the default. */
+static void impl_check(void)
+ {
+ CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+ if(!impl)
+ impl = &impl_default;
+ CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+ }
+/* A macro wrapper for impl_check that first uses a non-locked test before
+ * invoking the function (which checks again inside a lock). */
+#define IMPL_CHECK if(!impl) impl_check();
+
+/* API functions to get/set the "ex_data" implementation */
+const CRYPTO_EX_DATA_IMPL *CRYPTO_get_ex_data_implementation(void)
+ {
+ IMPL_CHECK
+ return impl;
+ }
+int CRYPTO_set_ex_data_implementation(const CRYPTO_EX_DATA_IMPL *i)
+ {
+ int toret = 0;
+ CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+ if(!impl)
+ {
+ impl = i;
+ toret = 1;
+ }
+ CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+ return toret;
+ }
+
+/****************************************************************************/
+/* Interal (default) implementation of "ex_data" support. API functions are
+ * further down. */
+
+/* The type that represents what each "class" used to implement locally. A STACK
+ * of CRYPTO_EX_DATA_FUNCS plus a index-counter. The 'class_index' is the global
+ * value representing the class that is used to distinguish these items. */
+typedef struct st_ex_class_item {
+ int class_index;
+ STACK_OF(CRYPTO_EX_DATA_FUNCS) *meth;
+ int meth_num;
+} EX_CLASS_ITEM;
+
+/* When assigning new class indexes, this is our counter */
+static int ex_class = CRYPTO_EX_INDEX_USER;
+
+/* The global hash table of EX_CLASS_ITEM items */
+static LHASH *ex_data = NULL;
+
+/* The callbacks required in the "ex_data" hash table */
+static unsigned long ex_hash_cb(const void *a_void)
+ {
+ return ((const EX_CLASS_ITEM *)a_void)->class_index;
+ }
+static int ex_cmp_cb(const void *a_void, const void *b_void)
+ {
+ return (((const EX_CLASS_ITEM *)a_void)->class_index -
+ ((const EX_CLASS_ITEM *)b_void)->class_index);
+ }
+
+/* Internal functions used by the "impl_default" implementation to access the
+ * state */
+
+static int ex_data_check(void)
+ {
+ int toret = 1;
+ CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
+ if(!ex_data && ((ex_data = lh_new(ex_hash_cb, ex_cmp_cb)) == NULL))
+ toret = 0;
+ CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
+ return toret;
+ }
+/* This macros helps reduce the locking from repeated checks because the
+ * ex_data_check() function checks ex_data again inside a lock. */
+#define EX_DATA_CHECK(iffail) if(!ex_data && !ex_data_check()) {iffail}
+
+/* This "inner" callback is used by the callback function that follows it */
+static void def_cleanup_util_cb(CRYPTO_EX_DATA_FUNCS *funcs)
+ {
+ OPENSSL_free(funcs);
+ }