// // stream_service.hpp // ~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com // Copyright (c) 2005 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 #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace asio { namespace ssl { namespace detail { class openssl_stream_service : public boost::asio::io_service::service { private: //Base handler for asyncrhonous operations template class base_handler { public: typedef boost::function func_t; base_handler(boost::asio::io_service& io_service) : op_(NULL) , io_service_(io_service) , work_(io_service) {} void do_func(const boost::asio::error& error, size_t size) { func_(error, size); } void set_operation(openssl_operation* op) { op_ = op; } void set_func(func_t func) { func_ = func; } ~base_handler() { delete op_; } private: func_t func_; openssl_operation* op_; boost::asio::io_service& io_service_; boost::asio::io_service::work work_; }; // class base_handler // Handler for asynchronous IO (write/read) operations template class io_handler : public base_handler { public: io_handler(Handler handler, boost::asio::io_service& io_service) : base_handler(io_service) , handler_(handler) { set_func(boost::bind( &io_handler::handler_impl, this, boost::arg<1>(), boost::arg<2>() )); } private: Handler handler_; void handler_impl(const boost::asio::error& error, size_t size) { handler_(error, size); delete this; } }; // class io_handler // Handler for asyncrhonous handshake (connect, accept) functions template class handshake_handler : public base_handler { public: handshake_handler(Handler handler, boost::asio::io_service& io_service) : base_handler(io_service) , handler_(handler) { set_func(boost::bind( &handshake_handler::handler_impl, this, boost::arg<1>(), boost::arg<2>() )); } private: Handler handler_; void handler_impl(const boost::asio::error& error, size_t) { handler_(error); delete this; } }; // class handshake_handler // Handler for asyncrhonous shutdown template class shutdown_handler : public base_handler { public: shutdown_handler(Handler handler, boost::asio::io_service& io_service) : base_handler(io_service), handler_(handler) { set_func(boost::bind( &shutdown_handler::handler_impl, this, boost::arg<1>(), boost::arg<2>() )); } private: Handler handler_; void handler_impl(const boost::asio::error& error, size_t) { handler_(error); delete this; } }; // class shutdown_handler public: // The implementation type. typedef struct impl_struct { ::SSL* ssl; ::BIO* ext_bio; } * 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::io_service::service(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 void create(impl_type& impl, Stream& next_layer, basic_context& context) { impl = new impl_struct; impl->ssl = ::SSL_new(context.impl()); ::SSL_set_mode(impl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); ::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 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 void handshake(impl_type& impl, Stream& next_layer, stream_base::handshake_type type, Error_Handler error_handler) { try { openssl_operation op( type == stream_base::client ? &ssl_wrap::SSL_connect: &ssl_wrap::SSL_accept, next_layer, impl->ssl, impl->ext_bio); op.start(); } catch (boost::asio::error& e) { error_handler(e); return; } boost::asio::error e; error_handler(e); } // Start an asynchronous SSL handshake. template void async_handshake(impl_type& impl, Stream& next_layer, stream_base::handshake_type type, Handler handler) { typedef handshake_handler connect_handler; connect_handler* local_handler = new connect_handler(handler, owner()); openssl_operation* op = new openssl_operation ( type == stream_base::client ? &ssl_wrap::SSL_connect: &ssl_wrap::SSL_accept, next_layer, impl->ssl, impl->ext_bio, boost::bind ( &base_handler::do_func, local_handler, boost::arg<1>(), boost::arg<2>() ) ); local_handler->set_operation(op); owner().post(boost::bind(&openssl_operation::start, op)); } // Shut down SSL on the stream. template void shutdown(impl_type& impl, Stream& next_layer, Error_Handler error_handler) { try { openssl_operation op( &ssl_wrap::SSL_shutdown, next_layer, impl->ssl, impl->ext_bio); op.start(); } catch (boost::asio::error& e) { error_handler(e); return; } boost::asio::error e; error_handler(e); } // Asynchronously shut down SSL on the stream. template void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler) { typedef shutdown_handler disconnect_handler; disconnect_handler* local_handler = new disconnect_handler(handler, owner()); openssl_operation* op = new openssl_operation ( &ssl_wrap::SSL_shutdown, next_layer, impl->ssl, impl->ext_bio, boost::bind ( &base_handler::do_func, local_handler, boost::arg<1>(), boost::arg<2>() ) ); local_handler->set_operation(op); owner().post(boost::bind(&openssl_operation::start, op)); } // Write some data to the stream. template std::size_t write_some(impl_type& impl, Stream& next_layer, const Const_Buffers& buffers, Error_Handler error_handler) { size_t bytes_transferred = 0; try { boost::function send_func = boost::bind(&::SSL_write, boost::arg<1>(), boost::asio::buffer_cast(*buffers.begin()), static_cast(boost::asio::buffer_size(*buffers.begin()))); openssl_operation op( send_func, next_layer, impl->ssl, impl->ext_bio ); bytes_transferred = static_cast(op.start()); } catch (boost::asio::error& e) { error_handler(e); return 0; } boost::asio::error e; error_handler(e); return bytes_transferred; } // Start an asynchronous write. template void async_write_some(impl_type& impl, Stream& next_layer, const Const_Buffers& buffers, Handler handler) { typedef io_handler send_handler; send_handler* local_handler = new send_handler(handler, owner()); boost::function send_func = boost::bind(&::SSL_write, boost::arg<1>(), boost::asio::buffer_cast(*buffers.begin()), static_cast(boost::asio::buffer_size(*buffers.begin()))); openssl_operation* op = new openssl_operation ( send_func, next_layer, impl->ssl, impl->ext_bio, boost::bind ( &base_handler::do_func, local_handler, boost::arg<1>(), boost::arg<2>() ) ); local_handler->set_operation(op); owner().post(boost::bind(&openssl_operation::start, op)); } // Read some data from the stream. template std::size_t read_some(impl_type& impl, Stream& next_layer, const Mutable_Buffers& buffers, Error_Handler error_handler) { size_t bytes_transferred = 0; try { boost::function recv_func = boost::bind(&::SSL_read, boost::arg<1>(), boost::asio::buffer_cast(*buffers.begin()), boost::asio::buffer_size(*buffers.begin())); openssl_operation op(recv_func, next_layer, impl->ssl, impl->ext_bio ); bytes_transferred = static_cast(op.start()); } catch (boost::asio::error& e) { error_handler(e); return 0; } boost::asio::error e; error_handler(e); return bytes_transferred; } // Start an asynchronous read. template void async_read_some(impl_type& impl, Stream& next_layer, const Mutable_Buffers& buffers, Handler handler) { typedef io_handler recv_handler; recv_handler* local_handler = new recv_handler(handler, owner()); boost::function recv_func = boost::bind(&::SSL_read, boost::arg<1>(), boost::asio::buffer_cast(*buffers.begin()), boost::asio::buffer_size(*buffers.begin())); openssl_operation* op = new openssl_operation ( recv_func, next_layer, impl->ssl, impl->ext_bio, boost::bind ( &base_handler::do_func, local_handler, boost::arg<1>(), boost::arg<2>() ) ); local_handler->set_operation(op); owner().post(boost::bind(&openssl_operation::start, op)); } // Peek at the incoming data on the stream. template std::size_t peek(impl_type& impl, Stream& next_layer, const Mutable_Buffers& buffers, Error_Handler error_handler) { boost::asio::error e; error_handler(e); return 0; } // Determine the amount of data that may be read without blocking. template std::size_t in_avail(impl_type& impl, Stream& next_layer, Error_Handler error_handler) { boost::asio::error e; error_handler(e); return 0; } private: typedef boost::asio::detail::mutex mutex_type; template 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 Mutex openssl_stream_service::ssl_wrap::ssl_mutex_; } // namespace detail } // namespace ssl } // namespace asio } // namespace boost #include #endif // BOOST_ASIO_SSL_DETAIL_OPENSSL_STREAM_SERVICE_HPP