// | |
// ssl/detail/stream_service.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_STREAM_SERVICE_HPP | |
#define BOOST_ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP | |
#if defined(_MSC_VER) && (_MSC_VER >= 1200) | |
# pragma once | |
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |
#include <boost/asio/detail/config.hpp> | |
#include <cstddef> | |
#include <climits> | |
#include <memory> | |
#include <boost/config.hpp> | |
#include <boost/noncopyable.hpp> | |
#include <boost/function.hpp> | |
#include <boost/bind.hpp> | |
#include <boost/asio/detail/buffer_sequence_adapter.hpp> | |
#include <boost/asio/error.hpp> | |
#include <boost/asio/io_service.hpp> | |
#include <boost/asio/ssl/basic_context.hpp> | |
#include <boost/asio/ssl/stream_base.hpp> | |
#include <boost/asio/ssl/detail/openssl_operation.hpp> | |
#include <boost/asio/ssl/detail/openssl_types.hpp> | |
#include <boost/asio/strand.hpp> | |
#include <boost/system/system_error.hpp> | |
#include <boost/asio/detail/push_options.hpp> | |
namespace boost { | |
namespace asio { | |
namespace ssl { | |
namespace detail { | |
class openssl_stream_service | |
: public boost::asio::detail::service_base<openssl_stream_service> | |
{ | |
private: | |
enum { max_buffer_size = INT_MAX }; | |
//Base handler for asyncrhonous operations | |
template <typename Stream> | |
class base_handler | |
{ | |
public: | |
typedef boost::function< | |
void (const boost::system::error_code&, size_t)> func_t; | |
base_handler(boost::asio::io_service& io_service) | |
: op_(NULL) | |
, io_service_(io_service) | |
, work_(io_service) | |
{} | |
void do_func(const boost::system::error_code& error, size_t size) | |
{ | |
func_(error, size); | |
} | |
void set_operation(openssl_operation<Stream>* op) { op_ = op; } | |
void set_func(func_t func) { func_ = func; } | |
~base_handler() | |
{ | |
delete op_; | |
} | |
private: | |
func_t func_; | |
openssl_operation<Stream>* op_; | |
boost::asio::io_service& io_service_; | |
boost::asio::io_service::work work_; | |
}; // class base_handler | |
// Handler for asynchronous IO (write/read) operations | |
template<typename Stream, typename Handler> | |
class io_handler | |
: public base_handler<Stream> | |
{ | |
public: | |
io_handler(Handler handler, boost::asio::io_service& io_service) | |
: base_handler<Stream>(io_service) | |
, handler_(handler) | |
{ | |
this->set_func(boost::bind( | |
&io_handler<Stream, Handler>::handler_impl, | |
this, boost::arg<1>(), boost::arg<2>() )); | |
} | |
private: | |
Handler handler_; | |
void handler_impl(const boost::system::error_code& error, size_t size) | |
{ | |
std::auto_ptr<io_handler<Stream, Handler> > this_ptr(this); | |
handler_(error, size); | |
} | |
}; // class io_handler | |
// Handler for asyncrhonous handshake (connect, accept) functions | |
template <typename Stream, typename Handler> | |
class handshake_handler | |
: public base_handler<Stream> | |
{ | |
public: | |
handshake_handler(Handler handler, boost::asio::io_service& io_service) | |
: base_handler<Stream>(io_service) | |
, handler_(handler) | |
{ | |
this->set_func(boost::bind( | |
&handshake_handler<Stream, Handler>::handler_impl, | |
this, boost::arg<1>(), boost::arg<2>() )); | |
} | |
private: | |
Handler handler_; | |
void handler_impl(const boost::system::error_code& error, size_t) | |
{ | |
std::auto_ptr<handshake_handler<Stream, Handler> > this_ptr(this); | |
handler_(error); | |
} | |
}; // class handshake_handler | |
// Handler for asyncrhonous shutdown | |
template <typename Stream, typename Handler> | |
class shutdown_handler | |
: public base_handler<Stream> | |
{ | |
public: | |
shutdown_handler(Handler handler, boost::asio::io_service& io_service) | |
: base_handler<Stream>(io_service), | |
handler_(handler) | |
{ | |
this->set_func(boost::bind( | |
&shutdown_handler<Stream, Handler>::handler_impl, | |
this, boost::arg<1>(), boost::arg<2>() )); | |
} | |
private: | |
Handler handler_; | |
void handler_impl(const boost::system::error_code& error, size_t) | |
{ | |
std::auto_ptr<shutdown_handler<Stream, Handler> > this_ptr(this); | |
handler_(error); | |
} | |
}; // class shutdown_handler | |
public: | |
// The implementation type. | |
typedef struct impl_struct | |
{ | |
::SSL* ssl; | |
::BIO* ext_bio; | |
net_buffer recv_buf; | |
} * impl_type; | |
// Construct a new stream socket service for the specified io_service. | |
explicit openssl_stream_service(boost::asio::io_service& io_service) | |
: boost::asio::detail::service_base<openssl_stream_service>(io_service), | |
strand_(io_service) | |
{ | |
} | |
// Destroy all user-defined handler objects owned by the service. | |
void shutdown_service() | |
{ | |
} | |
// Return a null stream implementation. | |
impl_type null() const | |
{ | |
return 0; | |
} | |
// Create a new stream implementation. | |
template <typename Stream, typename Context_Service> | |
void create(impl_type& impl, Stream& /*next_layer*/, | |
basic_context<Context_Service>& context) | |
{ | |
impl = new impl_struct; | |
impl->ssl = ::SSL_new(context.impl()); | |
::SSL_set_mode(impl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); | |
::SSL_set_mode(impl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); | |
::BIO* int_bio = 0; | |
impl->ext_bio = 0; | |
::BIO_new_bio_pair(&int_bio, 8192, &impl->ext_bio, 8192); | |
::SSL_set_bio(impl->ssl, int_bio, int_bio); | |
} | |
// Destroy a stream implementation. | |
template <typename Stream> | |
void destroy(impl_type& impl, Stream& /*next_layer*/) | |
{ | |
if (impl != 0) | |
{ | |
::BIO_free(impl->ext_bio); | |
::SSL_free(impl->ssl); | |
delete impl; | |
impl = 0; | |
} | |
} | |
// Perform SSL handshaking. | |
template <typename Stream> | |
boost::system::error_code handshake(impl_type& impl, Stream& next_layer, | |
stream_base::handshake_type type, boost::system::error_code& ec) | |
{ | |
try | |
{ | |
openssl_operation<Stream> op( | |
type == stream_base::client ? | |
&ssl_wrap<mutex_type>::SSL_connect: | |
&ssl_wrap<mutex_type>::SSL_accept, | |
next_layer, | |
impl->recv_buf, | |
impl->ssl, | |
impl->ext_bio); | |
op.start(); | |
} | |
catch (boost::system::system_error& e) | |
{ | |
ec = e.code(); | |
return ec; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Start an asynchronous SSL handshake. | |
template <typename Stream, typename Handler> | |
void async_handshake(impl_type& impl, Stream& next_layer, | |
stream_base::handshake_type type, Handler handler) | |
{ | |
typedef handshake_handler<Stream, Handler> connect_handler; | |
connect_handler* local_handler = | |
new connect_handler(handler, get_io_service()); | |
openssl_operation<Stream>* op = new openssl_operation<Stream> | |
( | |
type == stream_base::client ? | |
&ssl_wrap<mutex_type>::SSL_connect: | |
&ssl_wrap<mutex_type>::SSL_accept, | |
next_layer, | |
impl->recv_buf, | |
impl->ssl, | |
impl->ext_bio, | |
boost::bind | |
( | |
&base_handler<Stream>::do_func, | |
local_handler, | |
boost::arg<1>(), | |
boost::arg<2>() | |
), | |
strand_ | |
); | |
local_handler->set_operation(op); | |
strand_.post(boost::bind(&openssl_operation<Stream>::start, op)); | |
} | |
// Shut down SSL on the stream. | |
template <typename Stream> | |
boost::system::error_code shutdown(impl_type& impl, Stream& next_layer, | |
boost::system::error_code& ec) | |
{ | |
try | |
{ | |
openssl_operation<Stream> op( | |
&ssl_wrap<mutex_type>::SSL_shutdown, | |
next_layer, | |
impl->recv_buf, | |
impl->ssl, | |
impl->ext_bio); | |
op.start(); | |
} | |
catch (boost::system::system_error& e) | |
{ | |
ec = e.code(); | |
return ec; | |
} | |
ec = boost::system::error_code(); | |
return ec; | |
} | |
// Asynchronously shut down SSL on the stream. | |
template <typename Stream, typename Handler> | |
void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler) | |
{ | |
typedef shutdown_handler<Stream, Handler> disconnect_handler; | |
disconnect_handler* local_handler = | |
new disconnect_handler(handler, get_io_service()); | |
openssl_operation<Stream>* op = new openssl_operation<Stream> | |
( | |
&ssl_wrap<mutex_type>::SSL_shutdown, | |
next_layer, | |
impl->recv_buf, | |
impl->ssl, | |
impl->ext_bio, | |
boost::bind | |
( | |
&base_handler<Stream>::do_func, | |
local_handler, | |
boost::arg<1>(), | |
boost::arg<2>() | |
), | |
strand_ | |
); | |
local_handler->set_operation(op); | |
strand_.post(boost::bind(&openssl_operation<Stream>::start, op)); | |
} | |
// Write some data to the stream. | |
template <typename Stream, typename Const_Buffers> | |
std::size_t write_some(impl_type& impl, Stream& next_layer, | |
const Const_Buffers& buffers, boost::system::error_code& ec) | |
{ | |
size_t bytes_transferred = 0; | |
try | |
{ | |
boost::asio::const_buffer buffer = | |
boost::asio::detail::buffer_sequence_adapter< | |
boost::asio::const_buffer, Const_Buffers>::first(buffers); | |
std::size_t buffer_size = boost::asio::buffer_size(buffer); | |
if (buffer_size > max_buffer_size) | |
buffer_size = max_buffer_size; | |
else if (buffer_size == 0) | |
{ | |
ec = boost::system::error_code(); | |
return 0; | |
} | |
boost::function<int (SSL*)> send_func = | |
boost::bind(boost::type<int>(), &::SSL_write, boost::arg<1>(), | |
boost::asio::buffer_cast<const void*>(buffer), | |
static_cast<int>(buffer_size)); | |
openssl_operation<Stream> op( | |
send_func, | |
next_layer, | |
impl->recv_buf, | |
impl->ssl, | |
impl->ext_bio | |
); | |
bytes_transferred = static_cast<size_t>(op.start()); | |
} | |
catch (boost::system::system_error& e) | |
{ | |
ec = e.code(); | |
return 0; | |
} | |
ec = boost::system::error_code(); | |
return bytes_transferred; | |
} | |
// Start an asynchronous write. | |
template <typename Stream, typename Const_Buffers, typename Handler> | |
void async_write_some(impl_type& impl, Stream& next_layer, | |
const Const_Buffers& buffers, Handler handler) | |
{ | |
typedef io_handler<Stream, Handler> send_handler; | |
boost::asio::const_buffer buffer = | |
boost::asio::detail::buffer_sequence_adapter< | |
boost::asio::const_buffer, Const_Buffers>::first(buffers); | |
std::size_t buffer_size = boost::asio::buffer_size(buffer); | |
if (buffer_size > max_buffer_size) | |
buffer_size = max_buffer_size; | |
else if (buffer_size == 0) | |
{ | |
get_io_service().post(boost::asio::detail::bind_handler( | |
handler, boost::system::error_code(), 0)); | |
return; | |
} | |
send_handler* local_handler = new send_handler(handler, get_io_service()); | |
boost::function<int (SSL*)> send_func = | |
boost::bind(boost::type<int>(), &::SSL_write, boost::arg<1>(), | |
boost::asio::buffer_cast<const void*>(buffer), | |
static_cast<int>(buffer_size)); | |
openssl_operation<Stream>* op = new openssl_operation<Stream> | |
( | |
send_func, | |
next_layer, | |
impl->recv_buf, | |
impl->ssl, | |
impl->ext_bio, | |
boost::bind | |
( | |
&base_handler<Stream>::do_func, | |
local_handler, | |
boost::arg<1>(), | |
boost::arg<2>() | |
), | |
strand_ | |
); | |
local_handler->set_operation(op); | |
strand_.post(boost::bind(&openssl_operation<Stream>::start, op)); | |
} | |
// Read some data from the stream. | |
template <typename Stream, typename Mutable_Buffers> | |
std::size_t read_some(impl_type& impl, Stream& next_layer, | |
const Mutable_Buffers& buffers, boost::system::error_code& ec) | |
{ | |
size_t bytes_transferred = 0; | |
try | |
{ | |
boost::asio::mutable_buffer buffer = | |
boost::asio::detail::buffer_sequence_adapter< | |
boost::asio::mutable_buffer, Mutable_Buffers>::first(buffers); | |
std::size_t buffer_size = boost::asio::buffer_size(buffer); | |
if (buffer_size > max_buffer_size) | |
buffer_size = max_buffer_size; | |
else if (buffer_size == 0) | |
{ | |
ec = boost::system::error_code(); | |
return 0; | |
} | |
boost::function<int (SSL*)> recv_func = | |
boost::bind(boost::type<int>(), &::SSL_read, boost::arg<1>(), | |
boost::asio::buffer_cast<void*>(buffer), | |
static_cast<int>(buffer_size)); | |
openssl_operation<Stream> op(recv_func, | |
next_layer, | |
impl->recv_buf, | |
impl->ssl, | |
impl->ext_bio | |
); | |
bytes_transferred = static_cast<size_t>(op.start()); | |
} | |
catch (boost::system::system_error& e) | |
{ | |
ec = e.code(); | |
return 0; | |
} | |
ec = boost::system::error_code(); | |
return bytes_transferred; | |
} | |
// Start an asynchronous read. | |
template <typename Stream, typename Mutable_Buffers, typename Handler> | |
void async_read_some(impl_type& impl, Stream& next_layer, | |
const Mutable_Buffers& buffers, Handler handler) | |
{ | |
typedef io_handler<Stream, Handler> recv_handler; | |
boost::asio::mutable_buffer buffer = | |
boost::asio::detail::buffer_sequence_adapter< | |
boost::asio::mutable_buffer, Mutable_Buffers>::first(buffers); | |
std::size_t buffer_size = boost::asio::buffer_size(buffer); | |
if (buffer_size > max_buffer_size) | |
buffer_size = max_buffer_size; | |
else if (buffer_size == 0) | |
{ | |
get_io_service().post(boost::asio::detail::bind_handler( | |
handler, boost::system::error_code(), 0)); | |
return; | |
} | |
recv_handler* local_handler = new recv_handler(handler, get_io_service()); | |
boost::function<int (SSL*)> recv_func = | |
boost::bind(boost::type<int>(), &::SSL_read, boost::arg<1>(), | |
boost::asio::buffer_cast<void*>(buffer), | |
static_cast<int>(buffer_size)); | |
openssl_operation<Stream>* op = new openssl_operation<Stream> | |
( | |
recv_func, | |
next_layer, | |
impl->recv_buf, | |
impl->ssl, | |
impl->ext_bio, | |
boost::bind | |
( | |
&base_handler<Stream>::do_func, | |
local_handler, | |
boost::arg<1>(), | |
boost::arg<2>() | |
), | |
strand_ | |
); | |
local_handler->set_operation(op); | |
strand_.post(boost::bind(&openssl_operation<Stream>::start, op)); | |
} | |
// Peek at the incoming data on the stream. | |
template <typename Stream, typename Mutable_Buffers> | |
std::size_t peek(impl_type& /*impl*/, Stream& /*next_layer*/, | |
const Mutable_Buffers& /*buffers*/, boost::system::error_code& ec) | |
{ | |
ec = boost::system::error_code(); | |
return 0; | |
} | |
// Determine the amount of data that may be read without blocking. | |
template <typename Stream> | |
std::size_t in_avail(impl_type& /*impl*/, Stream& /*next_layer*/, | |
boost::system::error_code& ec) | |
{ | |
ec = boost::system::error_code(); | |
return 0; | |
} | |
private: | |
boost::asio::io_service::strand strand_; | |
typedef boost::asio::detail::mutex mutex_type; | |
template<typename Mutex> | |
struct ssl_wrap | |
{ | |
static Mutex ssl_mutex_; | |
static int SSL_accept(SSL *ssl) | |
{ | |
typename Mutex::scoped_lock lock(ssl_mutex_); | |
return ::SSL_accept(ssl); | |
} | |
static int SSL_connect(SSL *ssl) | |
{ | |
typename Mutex::scoped_lock lock(ssl_mutex_); | |
return ::SSL_connect(ssl); | |
} | |
static int SSL_shutdown(SSL *ssl) | |
{ | |
typename Mutex::scoped_lock lock(ssl_mutex_); | |
return ::SSL_shutdown(ssl); | |
} | |
}; | |
}; | |
template<typename Mutex> | |
Mutex openssl_stream_service::ssl_wrap<Mutex>::ssl_mutex_; | |
} // namespace detail | |
} // namespace ssl | |
} // namespace asio | |
} // namespace boost | |
#include <boost/asio/detail/pop_options.hpp> | |
#endif // BOOST_ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP |