+
+
+/* Define the predeclared (but externally opaque) "ERR_FNS" type */
+struct st_ERR_FNS
+ {
+ /* Works on the "error_hash" string table */
+ LHASH *(*cb_err_get)(void);
+ void (*cb_err_del)(void);
+ ERR_STRING_DATA *(*cb_err_get_item)(const ERR_STRING_DATA *);
+ ERR_STRING_DATA *(*cb_err_set_item)(ERR_STRING_DATA *);
+ ERR_STRING_DATA *(*cb_err_del_item)(ERR_STRING_DATA *);
+ /* Works on the "thread_hash" error-state table */
+ LHASH *(*cb_thread_get)(void);
+ ERR_STATE *(*cb_thread_get_item)(const ERR_STATE *);
+ ERR_STATE *(*cb_thread_set_item)(ERR_STATE *);
+ void (*cb_thread_del_item)(const ERR_STATE *);
+ /* Returns the next available error "library" numbers */
+ int (*cb_get_next_lib)(void);
+ };
+
+/* Predeclarations of the "err_defaults" functions */
+static LHASH *int_err_get(void);
+static void int_err_del(void);
+static ERR_STRING_DATA *int_err_get_item(const ERR_STRING_DATA *);
+static ERR_STRING_DATA *int_err_set_item(ERR_STRING_DATA *);
+static ERR_STRING_DATA *int_err_del_item(ERR_STRING_DATA *);
+static LHASH *int_thread_get(void);
+static ERR_STATE *int_thread_get_item(const ERR_STATE *);
+static ERR_STATE *int_thread_set_item(ERR_STATE *);
+static void int_thread_del_item(const ERR_STATE *);
+static int int_err_get_next_lib(void);
+/* The static ERR_FNS table using these defaults functions */
+static const ERR_FNS err_defaults =
+ {
+ int_err_get,
+ int_err_del,
+ int_err_get_item,
+ int_err_set_item,
+ int_err_del_item,
+ int_thread_get,
+ int_thread_get_item,
+ int_thread_set_item,
+ int_thread_del_item,
+ int_err_get_next_lib
+ };
+
+/* The replacable table of ERR_FNS functions we use at run-time */
+static const ERR_FNS *err_fns = NULL;
+
+/* Eg. rather than using "err_get()", use "ERRFN(err_get)()". */
+#define ERRFN(a) err_fns->cb_##a
+
+/* The internal state used by "err_defaults" - as such, the setting, reading,
+ * creating, and deleting of this data should only be permitted via the
+ * "err_defaults" functions. This way, a linked module can completely defer all
+ * ERR state operation (together with requisite locking) to the implementations
+ * and state in the loading application. */
+static LHASH *int_error_hash;
+static int int_error_hash_set = 0;
+static LHASH *int_thread_hash;
+static int int_thread_hash_set = 0;
+static int int_err_library_number=ERR_LIB_USER;
+
+/* Internal function that checks whether "err_fns" is set and if not, sets it to
+ * the defaults. */
+static void err_fns_check(void)
+ {
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ if(!err_fns)
+ err_fns = &err_defaults;
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+ }
+
+/* API functions to get or set the underlying ERR functions. */
+
+const ERR_FNS *ERR_get_implementation(void)
+ {
+ err_fns_check();
+ return err_fns;
+ }
+
+int ERR_set_implementation(const ERR_FNS *fns)
+ {
+ int toret = 0;
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ /* It's too late if 'err_fns' is non-NULL. BTW: not much point setting
+ * an error is there?! */
+ if(!err_fns)
+ {
+ err_fns = fns;
+ toret = 1;
+ }
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+ return toret;
+ }
+
+/* These are the callbacks provided to "lh_new()" when creating the LHASH tables
+ * internal to the "err_defaults" implementation. */
+
+/* static unsigned long err_hash(ERR_STRING_DATA *a); */
+static unsigned long err_hash(const void *a_void);
+/* static int err_cmp(ERR_STRING_DATA *a, ERR_STRING_DATA *b); */
+static int err_cmp(const void *a_void, const void *b_void);
+/* static unsigned long pid_hash(ERR_STATE *pid); */
+static unsigned long pid_hash(const void *pid_void);
+/* static int pid_cmp(ERR_STATE *a,ERR_STATE *pid); */
+static int pid_cmp(const void *a_void,const void *pid_void);
+static unsigned long get_error_values(int inc,const char **file,int *line,
+ const char **data,int *flags);
+
+/* The internal functions used in the "err_defaults" implementation */
+
+static LHASH *int_err_get(void)
+ {
+ LHASH *toret = NULL;
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ if(!int_error_hash_set)
+ int_error_hash = lh_new(err_hash, err_cmp);
+ if(int_error_hash)
+ {
+ int_error_hash_set = 1;
+ toret = int_error_hash;
+ }
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+ return(toret);
+ }
+static void int_err_del(void)
+ {
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ if(int_error_hash_set)
+ {
+ lh_free(int_error_hash);
+ int_error_hash = NULL;
+ int_error_hash_set = 0;
+ }
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+ }
+static ERR_STRING_DATA *int_err_get_item(const ERR_STRING_DATA *d)
+ {
+ ERR_STRING_DATA *p;
+ LHASH *hash;
+ err_fns_check();
+ hash = ERRFN(err_get)();
+ if(!hash)
+ return NULL;
+ CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+ p = (ERR_STRING_DATA *)lh_retrieve(hash, d);
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ return p;
+ }
+static ERR_STRING_DATA *int_err_set_item(ERR_STRING_DATA *d)
+ {
+ ERR_STRING_DATA *p;
+ LHASH *hash;
+ err_fns_check();
+ hash = ERRFN(err_get)();
+ if(!hash)
+ return NULL;
+ CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+ p = (ERR_STRING_DATA *)lh_insert(hash, d);
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ return p;
+ }
+static ERR_STRING_DATA *int_err_del_item(ERR_STRING_DATA *d)
+ {
+ ERR_STRING_DATA *p;
+ LHASH *hash;
+ err_fns_check();
+ hash = ERRFN(err_get)();
+ if(!hash)
+ return NULL;
+ CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+ p = (ERR_STRING_DATA *)lh_delete(hash, d);
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ return p;
+ }
+static LHASH *int_thread_get(void)
+ {
+ LHASH *toret = NULL;
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ if(!int_thread_hash_set)
+ int_thread_hash = lh_new(pid_hash, pid_cmp);
+ if(int_thread_hash)
+ {
+ int_thread_hash_set = 1;
+ toret = int_thread_hash;
+ }
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+ return(toret);
+ }
+static ERR_STATE *int_thread_get_item(const ERR_STATE *d)
+ {
+ ERR_STATE *p;
+ LHASH *hash;
+ err_fns_check();
+ hash = ERRFN(thread_get)();
+ if(!hash)
+ return NULL;
+ CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+ p = (ERR_STATE *)lh_retrieve(hash, d);
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ return p;
+ }
+static ERR_STATE *int_thread_set_item(ERR_STATE *d)
+ {
+ ERR_STATE *p;
+ LHASH *hash;
+ err_fns_check();
+ hash = ERRFN(thread_get)();
+ if(!hash)
+ return NULL;
+ CRYPTO_r_lock(CRYPTO_LOCK_ERR);
+ p = (ERR_STATE *)lh_insert(hash, d);
+ CRYPTO_r_unlock(CRYPTO_LOCK_ERR);
+ return p;
+ }
+static void int_thread_del_item(const ERR_STATE *d)
+ {
+ ERR_STATE *p;
+ LHASH *hash;
+ err_fns_check();
+ hash = ERRFN(thread_get)();
+ if(!hash)
+ return;
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ p = (ERR_STATE *)lh_delete(hash, d);
+ /* make sure we don't leak memory */
+ if(int_thread_hash_set && (lh_num_items(int_thread_hash) == 0))
+ {
+ lh_free(int_thread_hash);
+ int_thread_hash = NULL;
+ int_thread_hash_set = 0;
+ }
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+ if(p)
+ ERR_STATE_free(p);
+ }
+static int int_err_get_next_lib(void)
+ {
+ int toret;
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR);
+ toret = int_err_library_number++;
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR);
+ return toret;
+ }
+
+
+#define NUM_SYS_STR_REASONS 127
+#define LEN_SYS_STR_REASON 32
+
+static ERR_STRING_DATA SYS_str_reasons[NUM_SYS_STR_REASONS + 1];
+/* SYS_str_reasons is filled with copies of strerror() results at
+ * initialization.
+ * 'errno' values up to 127 should cover all usual errors,
+ * others will be displayed numerically by ERR_error_string.
+ * It is crucial that we have something for each reason code
+ * that occurs in ERR_str_reasons, or bogus reason strings
+ * will be returned for SYSerr(), which always gets an errno
+ * value and never one of those 'standard' reason codes. */
+
+static void build_SYS_str_reasons()
+ {
+ /* OPENSSL_malloc cannot be used here, use static storage instead */
+ static char strerror_tab[NUM_SYS_STR_REASONS][LEN_SYS_STR_REASON];
+ int i;
+
+ CRYPTO_w_lock(CRYPTO_LOCK_ERR_HASH);
+
+ for (i = 1; i <= NUM_SYS_STR_REASONS; i++)
+ {
+ ERR_STRING_DATA *str = &SYS_str_reasons[i - 1];
+
+ str->error = (unsigned long)i;
+ if (str->string == NULL)
+ {
+ char (*dest)[LEN_SYS_STR_REASON] = &(strerror_tab[i - 1]);
+ char *src = strerror(i);
+ if (src != NULL)
+ {
+ strncpy(*dest, src, sizeof *dest);
+ (*dest)[sizeof *dest - 1] = '\0';
+ str->string = *dest;
+ }
+ }
+ if (str->string == NULL)
+ str->string = "unknown";
+ }
+
+ /* Now we still have SYS_str_reasons[NUM_SYS_STR_REASONS] = {0, NULL},
+ * as required by ERR_load_strings. */
+
+ CRYPTO_w_unlock(CRYPTO_LOCK_ERR_HASH);
+ }