blob: 0d9c2f18f1cb753df9094112648410e94d2aa480 [file] [log] [blame]
//
// ssl/detail/openssl_init.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
// Copyright (c) 2005-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_ASIO_SSL_DETAIL_OPENSSL_INIT_HPP
#define BOOST_ASIO_SSL_DETAIL_OPENSSL_INIT_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <cstring>
#include <vector>
#include <boost/assert.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio/detail/mutex.hpp>
#include <boost/asio/detail/tss_ptr.hpp>
#include <boost/asio/ssl/detail/openssl_types.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace ssl {
namespace detail {
template <bool Do_Init = true>
class openssl_init
: private boost::noncopyable
{
private:
// Structure to perform the actual initialisation.
class do_init
{
public:
do_init()
{
if (Do_Init)
{
::SSL_library_init();
::SSL_load_error_strings();
::OpenSSL_add_ssl_algorithms();
mutexes_.resize(::CRYPTO_num_locks());
for (size_t i = 0; i < mutexes_.size(); ++i)
mutexes_[i].reset(new boost::asio::detail::mutex);
::CRYPTO_set_locking_callback(&do_init::openssl_locking_func);
::CRYPTO_set_id_callback(&do_init::openssl_id_func);
}
}
~do_init()
{
if (Do_Init)
{
::CRYPTO_set_id_callback(0);
::CRYPTO_set_locking_callback(0);
::ERR_free_strings();
::ERR_remove_state(0);
::EVP_cleanup();
::CRYPTO_cleanup_all_ex_data();
::CONF_modules_unload(1);
::ENGINE_cleanup();
}
}
// Helper function to manage a do_init singleton. The static instance of the
// openssl_init object ensures that this function is always called before
// main, and therefore before any other threads can get started. The do_init
// instance must be static in this function to ensure that it gets
// initialised before any other global objects try to use it.
static boost::shared_ptr<do_init> instance()
{
static boost::shared_ptr<do_init> init(new do_init);
return init;
}
private:
static unsigned long openssl_id_func()
{
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
return ::GetCurrentThreadId();
#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
void* id = instance()->thread_id_;
if (id == 0)
instance()->thread_id_ = id = &id; // Ugh.
BOOST_ASSERT(sizeof(unsigned long) >= sizeof(void*));
return reinterpret_cast<unsigned long>(id);
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
}
static void openssl_locking_func(int mode, int n,
const char* /*file*/, int /*line*/)
{
if (mode & CRYPTO_LOCK)
instance()->mutexes_[n]->lock();
else
instance()->mutexes_[n]->unlock();
}
// Mutexes to be used in locking callbacks.
std::vector<boost::shared_ptr<boost::asio::detail::mutex> > mutexes_;
#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
// The thread identifiers to be used by openssl.
boost::asio::detail::tss_ptr<void> thread_id_;
#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
};
public:
// Constructor.
openssl_init()
: ref_(do_init::instance())
{
using namespace std; // For memmove.
// Ensure openssl_init::instance_ is linked in.
openssl_init* tmp = &instance_;
memmove(&tmp, &tmp, sizeof(openssl_init*));
}
// Destructor.
~openssl_init()
{
}
private:
// Instance to force initialisation of openssl at global scope.
static openssl_init instance_;
// Reference to singleton do_init object to ensure that openssl does not get
// cleaned up until the last user has finished with it.
boost::shared_ptr<do_init> ref_;
};
template <bool Do_Init>
openssl_init<Do_Init> openssl_init<Do_Init>::instance_;
} // namespace detail
} // namespace ssl
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_SSL_DETAIL_OPENSSL_INIT_HPP