+
+#include "config.h"
+#include "libssl.hh"
+
+#ifdef HAVE_LIBSSL
+
+#include <atomic>
+#include <pthread.h>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#if (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER)
+/* OpenSSL < 1.1.0 needs support for threading/locking in the calling application. */
+static pthread_mutex_t *openssllocks{nullptr};
+
+extern "C" {
+static void openssl_pthreads_locking_callback(int mode, int type, const char *file, int line)
+{
+ if (mode & CRYPTO_LOCK) {
+ pthread_mutex_lock(&(openssllocks[type]));
+
+ } else {
+ pthread_mutex_unlock(&(openssllocks[type]));
+ }
+}
+
+static unsigned long openssl_pthreads_id_callback()
+{
+ return (unsigned long)pthread_self();
+}
+}
+
+static void openssl_thread_setup()
+{
+ openssllocks = (pthread_mutex_t*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
+
+ for (int i = 0; i < CRYPTO_num_locks(); i++)
+ pthread_mutex_init(&(openssllocks[i]), NULL);
+
+ CRYPTO_set_id_callback(openssl_pthreads_id_callback);
+ CRYPTO_set_locking_callback(openssl_pthreads_locking_callback);
+}
+
+static void openssl_thread_cleanup()
+{
+ CRYPTO_set_locking_callback(NULL);
+
+ for (int i=0; i<CRYPTO_num_locks(); i++) {
+ pthread_mutex_destroy(&(openssllocks[i]));
+ }
+
+ OPENSSL_free(openssllocks);
+}
+
+#else
+static void openssl_thread_setup()
+{
+}
+
+static void openssl_thread_cleanup()
+{
+}
+#endif /* (OPENSSL_VERSION_NUMBER < 0x1010000fL || defined LIBRESSL_VERSION_NUMBER) */
+
+static std::atomic<uint64_t> s_users;
+
+void registerOpenSSLUser()
+{
+ if (s_users.fetch_add(1) == 0) {
+ SSL_load_error_strings();
+ OpenSSL_add_ssl_algorithms();
+ openssl_thread_setup();
+ }
+}
+
+void unregisterOpenSSLUser()
+{
+ if (s_users.fetch_sub(1) == 1) {
+ ERR_free_strings();
+
+ EVP_cleanup();
+
+ CONF_modules_finish();
+ CONF_modules_free();
+ CONF_modules_unload(1);
+
+ CRYPTO_cleanup_all_ex_data();
+ openssl_thread_cleanup();
+ }
+}
+
+#endif /* HAVE_LIBSSL */