Chris@16: // Chris@16: // ssl/detail/impl/engine.ipp Chris@16: // ~~~~~~~~~~~~~~~~~~~~~~~~~~ Chris@16: // Chris@101: // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: Chris@16: #ifndef BOOST_ASIO_SSL_DETAIL_IMPL_ENGINE_IPP Chris@16: #define BOOST_ASIO_SSL_DETAIL_IMPL_ENGINE_IPP Chris@16: Chris@16: #if defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: # pragma once Chris@16: #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: Chris@16: #include Chris@16: Chris@16: #if !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost { Chris@16: namespace asio { Chris@16: namespace ssl { Chris@16: namespace detail { Chris@16: Chris@16: #if !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: Chris@16: engine::engine(SSL_CTX* context) Chris@16: : ssl_(::SSL_new(context)) Chris@16: { Chris@16: if (!ssl_) Chris@16: { Chris@16: boost::system::error_code ec( Chris@16: static_cast(::ERR_get_error()), Chris@16: boost::asio::error::get_ssl_category()); Chris@16: boost::asio::detail::throw_error(ec, "engine"); Chris@16: } Chris@16: Chris@16: accept_mutex().init(); Chris@16: Chris@16: ::SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE); Chris@16: ::SSL_set_mode(ssl_, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); Chris@16: #if defined(SSL_MODE_RELEASE_BUFFERS) Chris@16: ::SSL_set_mode(ssl_, SSL_MODE_RELEASE_BUFFERS); Chris@16: #endif // defined(SSL_MODE_RELEASE_BUFFERS) Chris@16: Chris@16: ::BIO* int_bio = 0; Chris@16: ::BIO_new_bio_pair(&int_bio, 0, &ext_bio_, 0); Chris@16: ::SSL_set_bio(ssl_, int_bio, int_bio); Chris@16: } Chris@16: Chris@16: engine::~engine() Chris@16: { Chris@16: if (SSL_get_app_data(ssl_)) Chris@16: { Chris@16: delete static_cast(SSL_get_app_data(ssl_)); Chris@16: SSL_set_app_data(ssl_, 0); Chris@16: } Chris@16: Chris@16: ::BIO_free(ext_bio_); Chris@16: ::SSL_free(ssl_); Chris@16: } Chris@16: Chris@16: SSL* engine::native_handle() Chris@16: { Chris@16: return ssl_; Chris@16: } Chris@16: Chris@16: boost::system::error_code engine::set_verify_mode( Chris@16: verify_mode v, boost::system::error_code& ec) Chris@16: { Chris@16: ::SSL_set_verify(ssl_, v, ::SSL_get_verify_callback(ssl_)); Chris@16: Chris@16: ec = boost::system::error_code(); Chris@16: return ec; Chris@16: } Chris@16: Chris@16: boost::system::error_code engine::set_verify_depth( Chris@16: int depth, boost::system::error_code& ec) Chris@16: { Chris@16: ::SSL_set_verify_depth(ssl_, depth); Chris@16: Chris@16: ec = boost::system::error_code(); Chris@16: return ec; Chris@16: } Chris@16: Chris@16: boost::system::error_code engine::set_verify_callback( Chris@16: verify_callback_base* callback, boost::system::error_code& ec) Chris@16: { Chris@16: if (SSL_get_app_data(ssl_)) Chris@16: delete static_cast(SSL_get_app_data(ssl_)); Chris@16: Chris@16: SSL_set_app_data(ssl_, callback); Chris@16: Chris@16: ::SSL_set_verify(ssl_, ::SSL_get_verify_mode(ssl_), Chris@16: &engine::verify_callback_function); Chris@16: Chris@16: ec = boost::system::error_code(); Chris@16: return ec; Chris@16: } Chris@16: Chris@16: int engine::verify_callback_function(int preverified, X509_STORE_CTX* ctx) Chris@16: { Chris@16: if (ctx) Chris@16: { Chris@16: if (SSL* ssl = static_cast( Chris@16: ::X509_STORE_CTX_get_ex_data( Chris@16: ctx, ::SSL_get_ex_data_X509_STORE_CTX_idx()))) Chris@16: { Chris@16: if (SSL_get_app_data(ssl)) Chris@16: { Chris@16: verify_callback_base* callback = Chris@16: static_cast( Chris@16: SSL_get_app_data(ssl)); Chris@16: Chris@16: verify_context verify_ctx(ctx); Chris@16: return callback->call(preverified != 0, verify_ctx) ? 1 : 0; Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: return 0; Chris@16: } Chris@16: Chris@16: engine::want engine::handshake( Chris@16: stream_base::handshake_type type, boost::system::error_code& ec) Chris@16: { Chris@16: return perform((type == boost::asio::ssl::stream_base::client) Chris@16: ? &engine::do_connect : &engine::do_accept, 0, 0, ec, 0); Chris@16: } Chris@16: Chris@16: engine::want engine::shutdown(boost::system::error_code& ec) Chris@16: { Chris@16: return perform(&engine::do_shutdown, 0, 0, ec, 0); Chris@16: } Chris@16: Chris@16: engine::want engine::write(const boost::asio::const_buffer& data, Chris@16: boost::system::error_code& ec, std::size_t& bytes_transferred) Chris@16: { Chris@16: if (boost::asio::buffer_size(data) == 0) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: return engine::want_nothing; Chris@16: } Chris@16: Chris@16: return perform(&engine::do_write, Chris@16: const_cast(boost::asio::buffer_cast(data)), Chris@16: boost::asio::buffer_size(data), ec, &bytes_transferred); Chris@16: } Chris@16: Chris@16: engine::want engine::read(const boost::asio::mutable_buffer& data, Chris@16: boost::system::error_code& ec, std::size_t& bytes_transferred) Chris@16: { Chris@16: if (boost::asio::buffer_size(data) == 0) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: return engine::want_nothing; Chris@16: } Chris@16: Chris@16: return perform(&engine::do_read, Chris@16: boost::asio::buffer_cast(data), Chris@16: boost::asio::buffer_size(data), ec, &bytes_transferred); Chris@16: } Chris@16: Chris@16: boost::asio::mutable_buffers_1 engine::get_output( Chris@16: const boost::asio::mutable_buffer& data) Chris@16: { Chris@16: int length = ::BIO_read(ext_bio_, Chris@16: boost::asio::buffer_cast(data), Chris@16: static_cast(boost::asio::buffer_size(data))); Chris@16: Chris@16: return boost::asio::buffer(data, Chris@16: length > 0 ? static_cast(length) : 0); Chris@16: } Chris@16: Chris@16: boost::asio::const_buffer engine::put_input( Chris@16: const boost::asio::const_buffer& data) Chris@16: { Chris@16: int length = ::BIO_write(ext_bio_, Chris@16: boost::asio::buffer_cast(data), Chris@16: static_cast(boost::asio::buffer_size(data))); Chris@16: Chris@16: return boost::asio::buffer(data + Chris@16: (length > 0 ? static_cast(length) : 0)); Chris@16: } Chris@16: Chris@16: const boost::system::error_code& engine::map_error_code( Chris@16: boost::system::error_code& ec) const Chris@16: { Chris@16: // We only want to map the error::eof code. Chris@16: if (ec != boost::asio::error::eof) Chris@16: return ec; Chris@16: Chris@16: // If there's data yet to be read, it's an error. Chris@16: if (BIO_wpending(ext_bio_)) Chris@16: { Chris@16: ec = boost::system::error_code( Chris@16: ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ), Chris@16: boost::asio::error::get_ssl_category()); Chris@16: return ec; Chris@16: } Chris@16: Chris@16: // SSL v2 doesn't provide a protocol-level shutdown, so an eof on the Chris@16: // underlying transport is passed through. Chris@101: if (ssl_->version == SSL2_VERSION) Chris@16: return ec; Chris@16: Chris@16: // Otherwise, the peer should have negotiated a proper shutdown. Chris@16: if ((::SSL_get_shutdown(ssl_) & SSL_RECEIVED_SHUTDOWN) == 0) Chris@16: { Chris@16: ec = boost::system::error_code( Chris@16: ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ), Chris@16: boost::asio::error::get_ssl_category()); Chris@16: } Chris@16: Chris@16: return ec; Chris@16: } Chris@16: Chris@16: boost::asio::detail::static_mutex& engine::accept_mutex() Chris@16: { Chris@16: static boost::asio::detail::static_mutex mutex = BOOST_ASIO_STATIC_MUTEX_INIT; Chris@16: return mutex; Chris@16: } Chris@16: Chris@16: engine::want engine::perform(int (engine::* op)(void*, std::size_t), Chris@16: void* data, std::size_t length, boost::system::error_code& ec, Chris@16: std::size_t* bytes_transferred) Chris@16: { Chris@16: std::size_t pending_output_before = ::BIO_ctrl_pending(ext_bio_); Chris@101: ::ERR_clear_error(); Chris@16: int result = (this->*op)(data, length); Chris@16: int ssl_error = ::SSL_get_error(ssl_, result); Chris@16: int sys_error = static_cast(::ERR_get_error()); Chris@16: std::size_t pending_output_after = ::BIO_ctrl_pending(ext_bio_); Chris@16: Chris@16: if (ssl_error == SSL_ERROR_SSL) Chris@16: { Chris@16: ec = boost::system::error_code(sys_error, Chris@16: boost::asio::error::get_ssl_category()); Chris@16: return want_nothing; Chris@16: } Chris@16: Chris@16: if (ssl_error == SSL_ERROR_SYSCALL) Chris@16: { Chris@16: ec = boost::system::error_code(sys_error, Chris@16: boost::asio::error::get_system_category()); Chris@16: return want_nothing; Chris@16: } Chris@16: Chris@16: if (result > 0 && bytes_transferred) Chris@16: *bytes_transferred = static_cast(result); Chris@16: Chris@16: if (ssl_error == SSL_ERROR_WANT_WRITE) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: return want_output_and_retry; Chris@16: } Chris@16: else if (pending_output_after > pending_output_before) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: return result > 0 ? want_output : want_output_and_retry; Chris@16: } Chris@16: else if (ssl_error == SSL_ERROR_WANT_READ) Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: return want_input_and_retry; Chris@16: } Chris@16: else if (::SSL_get_shutdown(ssl_) & SSL_RECEIVED_SHUTDOWN) Chris@16: { Chris@16: ec = boost::asio::error::eof; Chris@16: return want_nothing; Chris@16: } Chris@16: else Chris@16: { Chris@16: ec = boost::system::error_code(); Chris@16: return want_nothing; Chris@16: } Chris@16: } Chris@16: Chris@16: int engine::do_accept(void*, std::size_t) Chris@16: { Chris@16: boost::asio::detail::static_mutex::scoped_lock lock(accept_mutex()); Chris@16: return ::SSL_accept(ssl_); Chris@16: } Chris@16: Chris@16: int engine::do_connect(void*, std::size_t) Chris@16: { Chris@16: return ::SSL_connect(ssl_); Chris@16: } Chris@16: Chris@16: int engine::do_shutdown(void*, std::size_t) Chris@16: { Chris@16: int result = ::SSL_shutdown(ssl_); Chris@16: if (result == 0) Chris@16: result = ::SSL_shutdown(ssl_); Chris@16: return result; Chris@16: } Chris@16: Chris@16: int engine::do_read(void* data, std::size_t length) Chris@16: { Chris@16: return ::SSL_read(ssl_, data, Chris@16: length < INT_MAX ? static_cast(length) : INT_MAX); Chris@16: } Chris@16: Chris@16: int engine::do_write(void* data, std::size_t length) Chris@16: { Chris@16: return ::SSL_write(ssl_, data, Chris@16: length < INT_MAX ? static_cast(length) : INT_MAX); Chris@16: } Chris@16: Chris@16: #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: Chris@16: } // namespace detail Chris@16: } // namespace ssl Chris@16: } // namespace asio Chris@16: } // namespace boost Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // BOOST_ASIO_SSL_DETAIL_IMPL_ENGINE_IPP