From 9f12ac9cf3f3f0c34f546bd2b494e3c388dc53f7 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Thu, 8 Aug 2013 00:36:55 -0700 Subject: [PATCH] Add MultiSocket and RippleTlsContext --- Builds/VisualStudio2012/RippleD.vcxproj | 9 + .../VisualStudio2012/RippleD.vcxproj.filters | 15 + modules/ripple_asio/ripple_asio.cpp | 5 +- modules/ripple_asio/ripple_asio.h | 3 + modules/ripple_asio/ripple_asio_impl.h | 2 + .../sockets/ripple_MultiSocket.cpp | 686 ++++++++++++++++++ .../ripple_asio/sockets/ripple_MultiSocket.h | 49 ++ .../sockets/ripple_RippleTlsContext.cpp | 151 ++++ .../sockets/ripple_RippleTlsContext.h | 32 + .../ripple_asio/tests/ripple_AsioTests.cpp | 207 ++---- 10 files changed, 1014 insertions(+), 145 deletions(-) create mode 100644 modules/ripple_asio/sockets/ripple_MultiSocket.cpp create mode 100644 modules/ripple_asio/sockets/ripple_MultiSocket.h create mode 100644 modules/ripple_asio/sockets/ripple_RippleTlsContext.cpp create mode 100644 modules/ripple_asio/sockets/ripple_RippleTlsContext.h diff --git a/Builds/VisualStudio2012/RippleD.vcxproj b/Builds/VisualStudio2012/RippleD.vcxproj index b0f7086195..af2cf8faa3 100644 --- a/Builds/VisualStudio2012/RippleD.vcxproj +++ b/Builds/VisualStudio2012/RippleD.vcxproj @@ -690,6 +690,12 @@ true + + true + true + true + true + true true @@ -1491,6 +1497,9 @@ + + + diff --git a/Builds/VisualStudio2012/RippleD.vcxproj.filters b/Builds/VisualStudio2012/RippleD.vcxproj.filters index 64278d439a..1a14689e5c 100644 --- a/Builds/VisualStudio2012/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2012/RippleD.vcxproj.filters @@ -163,6 +163,9 @@ {2fdf2a12-4f97-45f6-a63d-7e99e97835dd} + + {625992a9-7333-4782-8ee3-6ed072d02fe5} + @@ -894,6 +897,9 @@ [1] Ripple\ripple_asio\boost + + [1] Ripple\ripple_asio\sockets + @@ -1688,6 +1694,15 @@ [1] Ripple\ripple_asio\boost + + [1] Ripple\ripple_asio\sockets + + + [1] Ripple\ripple_asio\sockets + + + [1] Ripple\ripple_asio\sockets + diff --git a/modules/ripple_asio/ripple_asio.cpp b/modules/ripple_asio/ripple_asio.cpp index cad76571c6..c3889ad947 100644 --- a/modules/ripple_asio/ripple_asio.cpp +++ b/modules/ripple_asio/ripple_asio.cpp @@ -15,7 +15,6 @@ #include "BeastConfig.h" #include "beast/modules/beast_basics/beast_basics.h" -#include "beast/modules/beast_asio/beast_asio.h" #include "ripple_asio.h" @@ -27,6 +26,10 @@ namespace ripple #include "boost/ripple_IoService.cpp" #include "boost/ripple_SslContext.cpp" +#include "sockets/ripple_RippleTlsContext.h" // private +#include "sockets/ripple_RippleTlsContext.cpp" +#include "sockets/ripple_MultiSocket.cpp" + #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4100) diff --git a/modules/ripple_asio/ripple_asio.h b/modules/ripple_asio/ripple_asio.h index acc6798491..a949d6bca7 100644 --- a/modules/ripple_asio/ripple_asio.h +++ b/modules/ripple_asio/ripple_asio.h @@ -8,6 +8,7 @@ #define RIPPLE_ASIO_H_INCLUDED #include "beast/modules/beast_core/beast_core.h" +#include "beast/modules/beast_asio/beast_asio.h" // Must be outside the namespace @@ -30,6 +31,8 @@ using namespace beast; #include "boost/ripple_IoService.h" #include "boost/ripple_SslContext.h" +#include "sockets/ripple_MultiSocket.h" + } #endif diff --git a/modules/ripple_asio/ripple_asio_impl.h b/modules/ripple_asio/ripple_asio_impl.h index c1d96b7edc..da4371d61b 100644 --- a/modules/ripple_asio/ripple_asio_impl.h +++ b/modules/ripple_asio/ripple_asio_impl.h @@ -7,6 +7,8 @@ #ifndef RIPPLE_ASIO_IMPL_H_INCLUDED #define RIPPLE_ASIO_IMPL_H_INCLUDED +#include + // Once everything is converted, these can be moved to ripple_asio.cpp #include #include diff --git a/modules/ripple_asio/sockets/ripple_MultiSocket.cpp b/modules/ripple_asio/sockets/ripple_MultiSocket.cpp new file mode 100644 index 0000000000..3442b22225 --- /dev/null +++ b/modules/ripple_asio/sockets/ripple_MultiSocket.cpp @@ -0,0 +1,686 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +RippleMultiSocket::Options::Options (Flags flags) + : useClientSsl (false) + , enableServerSsl (false) + , requireServerSsl (false) + , requireServerProxy (false) +{ + setFromFlags (flags); +} + +void RippleMultiSocket::Options::setFromFlags (Flags flags) +{ + useClientSsl = (flags & client_ssl) != 0; + enableServerSsl = (flags & (server_ssl | server_ssl_required)) != 0; + requireServerSsl = (flags & server_ssl_required) != 0; + requireServerProxy = (flags & server_proxy) !=0; +} + +//------------------------------------------------------------------------------ + +template +class RippleMultiSocketType : public RippleMultiSocket +{ +public: + // This shouldn't be needed + /* + struct SocketInterfaces + : SocketInterface::Socket + , SocketInterface::Stream + , SocketInterface::Handshake + {}; + */ + + typedef typename boost::remove_reference ::type next_layer_type; + typedef typename next_layer_type::lowest_layer_type lowest_layer_type; + + typedef typename boost::add_reference ::type next_layer_type_ref; + typedef typename boost::asio::ssl::stream SslStreamType; + + typedef RippleMultiSocketType ThisType; + + enum Status + { + needMore, + proxy, + plain, + ssl + }; + + template + explicit RippleMultiSocketType (Arg& arg, Options options = Options()) + : m_options (options) + , m_context (RippleTlsContext::New ()) + , m_next_layer (arg) + , m_io_service (m_next_layer.get_io_service ()) + , m_strand (m_io_service) + , m_status (needMore) + , m_role (Socket::client) + + { + } + + //-------------------------------------------------------------------------- + + bool is_handshaked () + { + return true; + } + + void* native_object_raw () + { + return &m_next_layer; + } + + boost::asio::io_service& get_io_service () noexcept + { + return m_io_service; + } + + next_layer_type& next_layer () noexcept + { + return m_next_layer; + } + + next_layer_type const& next_layer () const noexcept + { + return m_next_layer; + } + + lowest_layer_type& lowest_layer () noexcept + { + return m_next_layer.lowest_layer (); + } + + lowest_layer_type const& lowest_layer () const noexcept + { + return m_next_layer.lowest_layer (); + } + + //-------------------------------------------------------------------------- + + Socket& stream () const noexcept + { + fatal_assert (m_stream != nullptr); + return *m_stream; + } + + //-------------------------------------------------------------------------- + // + // SocketInterface + // + //-------------------------------------------------------------------------- + + boost::system::error_code cancel (boost::system::error_code& ec) + { + return lowest_layer ().cancel (ec); + } + + boost::system::error_code close (boost::system::error_code& ec) + { + return lowest_layer ().close (ec); + } + + boost::system::error_code shutdown (Socket::shutdown_type what, boost::system::error_code& ec) + { + return lowest_layer ().shutdown (what, ec); + } + + //-------------------------------------------------------------------------- + // + // StreamInterface + // + //-------------------------------------------------------------------------- + + std::size_t read_some (MutableBuffers const& buffers, boost::system::error_code& ec) + { + if (m_buffer.size () > 0) + { + ec = boost::system::error_code (); + std::size_t const amount = boost::asio::buffer_copy (buffers, m_buffer.data ()); + m_buffer.consume (amount); + return amount; + } + return stream ().read_some (buffers, ec); + } + + std::size_t write_some (ConstBuffers const& buffers, boost::system::error_code& ec) + { + return stream ().write_some (buffers, ec); + } + + BOOST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (boost::system::error_code, std::size_t)) + async_read_some (MutableBuffers const& buffers, BOOST_ASIO_MOVE_ARG(TransferCall) handler) + { + if (m_buffer.size () > 0) + { + // Return the leftover bytes from the handshake + std::size_t const amount = boost::asio::buffer_copy (buffers, m_buffer.data ()); + m_buffer.consume (amount); + return m_io_service.post (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(TransferCall)(handler), boost::system::error_code (), amount))); + } + return stream ().async_read_some (buffers, m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(TransferCall)(handler), boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + } + + BOOST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (boost::system::error_code, std::size_t)) + async_write_some (ConstBuffers const& buffers, BOOST_ASIO_MOVE_ARG(TransferCall) handler) + { + return stream ().async_write_some (buffers, + BOOST_ASIO_MOVE_CAST(TransferCall)(handler)); + } + + //-------------------------------------------------------------------------- + + boost::system::error_code handshake (Socket::handshake_type role, boost::system::error_code& ec) + { + Action action = calcAction (role); + + switch (action) + { + default: + case actionPlain: + handshakePlain (ec); + break; + + case actionSsl: + handshakeSsl (ec); + break; + + case actionDetect: + detectHandshake (ec); + if (! ec) + { + action = calcDetectAction (ec); + switch (action) + { + default: + case actionPlain: + handshakePlain (ec); + break; + case actionSsl: + handshakeSsl (ec); + break; + }; + } + break; + } + + return ec; + } + + BOOST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (boost::system::error_code)) + async_handshake (handshake_type type, BOOST_ASIO_MOVE_ARG(ErrorCall) handler) + { + Action const action = calcAction (type); + switch (action) + { + default: + case actionPlain: + return handshakePlainAsync (BOOST_ASIO_MOVE_CAST(ErrorCall)(handler)); + break; + + case actionSsl: + return handshakeSslAsync (BOOST_ASIO_MOVE_CAST(ErrorCall)(handler)); + break; + + case actionDetect: + return detectHandshakeAsync (BOOST_ASIO_MOVE_CAST(ErrorCall)(handler)); + break; + } + } + +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE + boost::system::error_code handshake (handshake_type type, + ConstBuffers const& buffers, boost::system::error_code& ec) + { + Action action = calcAction (type); + ec = boost::system::error_code (); + switch (action) + { + default: + case actionPlain: + handshakePlain (buffers, ec); + break; + case actionSsl: + handshakeSsl (buffers, ec); + break; + case actionDetect: + detectHandshake (buffers, ec); + if (! ec) + { + action = calcDetectAction (ec); + switch (action) + { + default: + case actionPlain: + handshakePlain (buffers, ec); + break; + case actionSsl: + handshakeSsl (buffers, ec); + break; + }; + } + break; + } + return ec; + } + + BOOST_ASIO_INITFN_RESULT_TYPE_MEMBER(TransferCall, void (boost::system::error_code, std::size_t)) + async_handshake (handshake_type type, ConstBuffers const& buffers, + BOOST_ASIO_MOVE_ARG(TransferCall) handler) + { + Action const action = calcAction (type); + switch (action) + { + default: + case actionPlain: + return handshakePlainAsync (buffers, + BOOST_ASIO_MOVE_CAST(TransferCall)(handler)); + break; + + case actionSsl: + return handshakeSslAsync (buffers, + BOOST_ASIO_MOVE_CAST(TransferCall)(handler)); + break; + + case actionDetect: + return detectHandshakeAsync (buffers, + BOOST_ASIO_MOVE_CAST(TransferCall)(handler)); + break; + } + } +#endif + + boost::system::error_code shutdown (boost::system::error_code& ec) + { + if (m_status == ssl) + { + return m_ssl_stream->shutdown (ec); + } + else + { + // we need to close the lwest layer + return m_next_layer.shutdown (next_layer_type::shutdown_both, ec); + } + } + + BOOST_ASIO_INITFN_RESULT_TYPE_MEMBER(ErrorCall, void (boost::system::error_code)) + async_shutdown (BOOST_ASIO_MOVE_ARG(ErrorCall) handler) + { + if (m_status == ssl) + { + m_ssl_stream->async_shutdown (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), + boost::asio::placeholders::error))); + } + else + { + boost::system::error_code ec; + m_next_layer.shutdown (next_layer_type::shutdown_both, ec); + m_io_service.post (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec))); + } + } + + //-------------------------------------------------------------------------- + + enum Action + { + actionDetect, + actionPlain, + actionSsl, + actionFail + }; + + // Determines what action to take based on + // the stream options and the desired role. + // + Action calcAction (Socket::handshake_type role) + { + m_role = role; + + if (role == Socket::server) + { + if (! m_options.enableServerSsl && + ! m_options.requireServerSsl && + ! m_options.requireServerProxy) + { + return actionPlain; + } + else if (m_options.requireServerSsl && ! m_options.requireServerProxy) + { + return actionSsl; + } + else + { + return actionDetect; + } + } + else if (m_role == Socket::client) + { + if (m_options.useClientSsl) + { + return actionSsl; + } + else + { + return actionPlain; + } + } + + return actionPlain; + } + + // Determines what action to take based on the auto-detected + // handshake, the stream options, and desired role. + // + Action calcDetectAction (boost::system::error_code& ec) + { + ec = boost::system::error_code (); + + if (m_status == plain) + { + if (! m_options.requireServerProxy && ! m_options.requireServerSsl) + { + return actionPlain; + } + else + { + failedHandshake (ec); + return actionFail; + } + } + else if (m_status == ssl) + { + if (! m_options.requireServerProxy) + { + if (m_options.enableServerSsl || m_options.requireServerSsl) + { + return actionSsl; + } + else + { + failedHandshake (ec); + return actionFail; + } + } + else + { + failedHandshake (ec); + return actionFail; + } + } + else if (m_status == proxy) + { + if (m_options.requireServerProxy) + { + // read the rest of the proxy string + // then transition to SSL handshake mode + failedHandshake (ec); + return actionFail; + } + else + { + // Can we make PROXY optional? + failedHandshake (ec); + return actionFail; + } + } + + failedHandshake (ec); + return actionFail; + } + + //-------------------------------------------------------------------------- + + // called when options disallow handshake + void failedHandshake (boost::system::error_code& ec) + { + // VFALCO TODO maybe use a ripple error category? + // set this to something custom that we can recognize later? + ec = boost::asio::error::invalid_argument; + } + + void createPlainStream () + { + m_status = plain; + m_stream = new SocketWrapper (m_next_layer); + } + + void handshakePlain (boost::system::error_code& ec) + { + ec = boost::system::error_code (); + createPlainStream (); + } + + void handshakePlain (ConstBuffers const& buffers, boost::system::error_code& ec) + { + fatal_assert (boost::asio::buffer_size (buffers) == 0 ); + ec = boost::system::error_code (); + createPlainStream (); + } + + BOOST_ASIO_INITFN_RESULT_TYPE(ErrorCall, void (boost::system::error_code)) + handshakePlainAsync (BOOST_ASIO_MOVE_ARG(ErrorCall) handler) + { + createPlainStream (); + return m_io_service.post (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), boost::system::error_code()))); + } + + void createSslStream () + { + m_status = ssl; + m_ssl_stream = new SslStreamType (m_next_layer, m_context->getBoostContext ()); + m_stream = new SocketWrapper (*m_ssl_stream); + } + + void handshakeSsl (boost::system::error_code& ec) + { + createSslStream (); + m_ssl_stream->handshake (m_role, ec); + } + + BOOST_ASIO_INITFN_RESULT_TYPE(ErrorCall, void (boost::system::error_code)) + handshakeSslAsync (BOOST_ASIO_MOVE_ARG(ErrorCall) handler) + { + createSslStream (); + return m_ssl_stream->async_handshake (m_role, + BOOST_ASIO_MOVE_CAST(ErrorCall)(handler)); + } + +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE + BOOST_ASIO_INITFN_RESULT_TYPE(TransferCall, void (boost::system::error_code, std::size_t)) + handshakePlainAsync (ConstBuffers const& buffers, + BOOST_ASIO_MOVE_ARG (TransferCall) handler) + { + fatal_assert (boost::asio::buffer_size (buffers) == 0); + createPlainStream (); + return m_io_service.post (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(TransferCall)(handler), + boost::system::error_code(), 0))); + } + + void handshakeSsl (ConstBuffers const& buffers, boost::system::error_code& ec) + { + createSslStream (); + m_ssl_stream->handshake (m_role, buffers, ec); + } + + BOOST_ASIO_INITFN_RESULT_TYPE (TransferCall, void (boost::system::error_code, std::size_t)) + handshakeSslAsync (ConstBuffers const& buffers, BOOST_ASIO_MOVE_ARG(TransferCall) handler) + { + createSslStream (); + return m_ssl_stream->async_handshake (m_role, buffers, + BOOST_ASIO_MOVE_CAST(TransferCall)(handler)); + } +#endif + + //-------------------------------------------------------------------------- + + enum + { + autoDetectBytes = 5 + }; + + void detectHandshake (boost::system::error_code& ec) + { + // Top up our buffer + bassert (m_buffer.size () == 0); + std::size_t const needed = autoDetectBytes; + std::size_t const amount = m_next_layer.receive ( + m_buffer.prepare (needed), boost::asio::socket_base::message_peek, ec); + m_buffer.commit (amount); + if (! ec) + { + analyzeHandshake (m_buffer.data ()); + m_buffer.consume (amount); + if (m_status == needMore) + ec = boost::asio::error::invalid_argument; // should never happen + } + } + +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE + void detectHandshake (ConstBuffers const& buffers, boost::system::error_code& ec) + { + m_buffer.commit (boost::asio::buffer_copy ( + m_buffer.prepare (boost::asio::buffer_size (buffers)), buffers)); + detectHandshake (ec); + } +#endif + + //-------------------------------------------------------------------------- + + void onDetectRead (BOOST_ASIO_MOVE_ARG(ErrorCall) handler, + boost::system::error_code const& ec, std::size_t bytes_transferred) + { + m_buffer.commit (bytes_transferred); + + if (! ec) + { + analyzeHandshake (m_buffer.data ()); + + boost::system::error_code ec; + + if (m_status != needMore) + { + m_buffer.consume (bytes_transferred); + + Action action = calcDetectAction (ec); + if (! ec) + { + switch (action) + { + default: + case actionPlain: + handshakePlainAsync ( + BOOST_ASIO_MOVE_CAST(ErrorCall)(handler)); + break; + case actionSsl: + handshakeSslAsync ( + BOOST_ASIO_MOVE_CAST(ErrorCall)(handler)); + break; + }; + } + } + else + { + ec = boost::asio::error::invalid_argument; + } + + if (ec) + { + m_io_service.post (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), ec))); + } + } + } + + BOOST_ASIO_INITFN_RESULT_TYPE(ErrorCall, void (boost::system::error_code)) + detectHandshakeAsync (BOOST_ASIO_MOVE_ARG(ErrorCall) handler) + { + bassert (m_buffer.size () == 0); + return m_next_layer.async_receive ( + m_buffer.prepare (autoDetectBytes), boost::asio::socket_base::message_peek, + m_strand.wrap (boost::bind (&ThisType::onDetectRead, this, + BOOST_ASIO_MOVE_CAST(ErrorCall)(handler), + boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); + } + +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE + BOOST_ASIO_INITFN_RESULT_TYPE(TransferCall, void (boost::system::error_code, std::size_t)) + detectHandshakeAsync (ConstBuffers const& buffers, BOOST_ASIO_MOVE_ARG(TransferCall) handler) + { + fatal_error ("unimplemented"); + } +#endif + + //-------------------------------------------------------------------------- + + static inline bool isPrintable (unsigned char c) + { + return (c < 127) && (c > 31); + } + + template + void analyzeHandshake (ConstBufferSequence const& buffers) + { + m_status = needMore; + + unsigned char data [5]; + + std::size_t const bytes = boost::asio::buffer_copy (boost::asio::buffer (data), buffers); + + if (bytes > 0) + { + if ( isPrintable (data [0]) && + ((bytes < 2) || isPrintable (data [1])) && + ((bytes < 3) || isPrintable (data [2])) && + ((bytes < 4) || isPrintable (data [3])) && + ((bytes < 5) || isPrintable (data [4]))) + { + if (bytes < 5 || memcmp (data, "PROXY", 5) != 0) + { + m_status = plain; + } + else + { + m_status = proxy; + } + } + else + { + m_status = ssl; + } + } + } + +private: + Options m_options; + ScopedPointer m_context; + Stream m_next_layer; + boost::asio::io_service& m_io_service; + boost::asio::io_service::strand m_strand; + Status m_status; + Socket::handshake_type m_role; + ScopedPointer m_stream; + ScopedPointer m_ssl_stream; + boost::asio::streambuf m_buffer; +}; + +//------------------------------------------------------------------------------ + +RippleMultiSocket* RippleMultiSocket::New (boost::asio::io_service& io_service, + Options const& options) +{ + return new RippleMultiSocketType (io_service, options); +} + diff --git a/modules/ripple_asio/sockets/ripple_MultiSocket.h b/modules/ripple_asio/sockets/ripple_MultiSocket.h new file mode 100644 index 0000000000..19c88260f7 --- /dev/null +++ b/modules/ripple_asio/sockets/ripple_MultiSocket.h @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_MULTISOCKET_H_INCLUDED +#define RIPPLE_MULTISOCKET_H_INCLUDED + +/** A Socket that can handshake with multiple protocols. +*/ +class RippleMultiSocket : public Socket +{ +public: + enum Flags + { + none = 0, + client_ssl = 1, + server_ssl = 2, + server_ssl_required = 4, + server_proxy = 8 + }; + + struct Options + { + Options (Flags flags = none); + + // Always perform SSL handshake as client role + bool useClientSsl; + + // Enable optional SSL capability as server role + bool enableServerSsl; + + // Require SSL as server role. + // Does not require that enableServerSsl is set + bool requireServerSsl; + + // Require PROXY protocol handshake as server role + bool requireServerProxy; + + private: + void setFromFlags (Flags flags); + }; + + static RippleMultiSocket* New (boost::asio::io_service& io_service, + Options const& options = none); +}; + +#endif diff --git a/modules/ripple_asio/sockets/ripple_RippleTlsContext.cpp b/modules/ripple_asio/sockets/ripple_RippleTlsContext.cpp new file mode 100644 index 0000000000..dacb338b54 --- /dev/null +++ b/modules/ripple_asio/sockets/ripple_RippleTlsContext.cpp @@ -0,0 +1,151 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +//------------------------------------------------------------------------------ + +class RippleTlsContextImp : public RippleTlsContext +{ +public: + RippleTlsContextImp () + : m_context (boost::asio::ssl::context::sslv23) + { + initBoostContext (m_context); + } + + ~RippleTlsContextImp () + { + } + + BoostContextType& getBoostContext () noexcept + { + return m_context; + } + + //-------------------------------------------------------------------------- + +private: + boost::asio::ssl::context m_context; +}; + +//------------------------------------------------------------------------------ + +void RippleTlsContext::initBoostContext (BoostContextType& context) +{ + struct Helpers + { + typedef boost::array RawDHParams; + + // A simple RAII container for a DH + struct ScopedDHPointer + { + explicit ScopedDHPointer (DH* dh) + : m_dh (dh) + { + } + + ~ScopedDHPointer () + { + if (m_dh != nullptr) + DH_free (m_dh); + } + + operator DH* () const + { + return get (); + } + + DH* get () const + { + return m_dh; + } + + private: + unsigned char const* m_p; + DH* m_dh; + }; + + //---------------------------------------------------------------------- + + // These are the DH parameters that OpenCoin has chosen for Ripple + // + static RawDHParams const& getRaw512Params () noexcept + { + static RawDHParams params = + { { + 0x30, 0x46, 0x02, 0x41, 0x00, 0x98, 0x15, 0xd2, 0xd0, 0x08, 0x32, 0xda, + 0xaa, 0xac, 0xc4, 0x71, 0xa3, 0x1b, 0x11, 0xf0, 0x6c, 0x62, 0xb2, 0x35, + 0x8a, 0x10, 0x92, 0xc6, 0x0a, 0xa3, 0x84, 0x7e, 0xaf, 0x17, 0x29, 0x0b, + 0x70, 0xef, 0x07, 0x4f, 0xfc, 0x9d, 0x6d, 0x87, 0x99, 0x19, 0x09, 0x5b, + 0x6e, 0xdb, 0x57, 0x72, 0x4a, 0x7e, 0xcd, 0xaf, 0xbd, 0x3a, 0x97, 0x55, + 0x51, 0x77, 0x5a, 0x34, 0x7c, 0xe8, 0xc5, 0x71, 0x63, 0x02, 0x01, 0x02 + } }; + + return params; + } + + static DH* createDH (RawDHParams const& rawParams) + { + RawDHParams::const_iterator iter = rawParams.begin (); + return d2i_DHparams (nullptr, &iter, rawParams.size ()); + } + + //---------------------------------------------------------------------- + + static DH* getDhParameters (int keyLength) + { + if (keyLength == 512 || keyLength == 1024) + { + static ScopedDHPointer dh512 (createDH (getRaw512Params ())); + return dh512.get (); + } + else + { + FatalError ("unsupported key length", __FILE__, __LINE__); + } + + return nullptr; + } + + static DH* tmp_dh_handler (SSL*, int, int key_length) + { + return DHparams_dup (getDhParameters (key_length)); + } + + static char const* getCipherList () + { + static char const* ciphers = "ALL:!LOW:!EXP:!MD5:@STRENGTH"; + + return ciphers; + } + }; + + //-------------------------------------------------------------------------- + + context.set_options ( + boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::single_dh_use); + + context.set_verify_mode (boost::asio::ssl::verify_none); + + SSL_CTX_set_tmp_dh_callback ( + context.native_handle (), + Helpers::tmp_dh_handler); + + int const result = SSL_CTX_set_cipher_list ( + context.native_handle (), + Helpers::getCipherList ()); + + if (result != 1) + FatalError ("invalid cipher list", __FILE__, __LINE__); +} + +//------------------------------------------------------------------------------ + +RippleTlsContext* RippleTlsContext::New () +{ + return new RippleTlsContextImp (); +} diff --git a/modules/ripple_asio/sockets/ripple_RippleTlsContext.h b/modules/ripple_asio/sockets/ripple_RippleTlsContext.h new file mode 100644 index 0000000000..8dc7b8ba9b --- /dev/null +++ b/modules/ripple_asio/sockets/ripple_RippleTlsContext.h @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_RIPPLETLSCONTEXT_H_INCLUDED +#define RIPPLE_RIPPLETLSCONTEXT_H_INCLUDED + +/** A boost SSL context which is set to Generic SSL/TLS (sslv23). + + This is what Ripple uses for its secure connections. The + curve parameters are predefined and verified to be secure. + + The context is set to sslv23, Transport Layer Security / General. + This is primarily used for peer to peer servers that don't care + about certificates or identity verification. + + Usually you don't instantiate this directly, you will need to derive + a class and initialize the context in your constructor. + + @see SslContext +*/ +class RippleTlsContext : public SslContextBase +{ +public: + static RippleTlsContext* New (); + + static void initBoostContext (BoostContextType& context); +}; + +#endif diff --git a/modules/ripple_asio/tests/ripple_AsioTests.cpp b/modules/ripple_asio/tests/ripple_AsioTests.cpp index 37aba9a047..81347d2012 100644 --- a/modules/ripple_asio/tests/ripple_AsioTests.cpp +++ b/modules/ripple_asio/tests/ripple_AsioTests.cpp @@ -13,119 +13,6 @@ namespace AsioUnitTestsNamespace using namespace boost; -class SslContext : public Uncopyable -{ -public: - SslContext () - { - } - - virtual ~SslContext () { } - - virtual asio::ssl::context& get_object () = 0; - -protected: -}; - -//------------------------------------------------------------------------------ - -class RippleSslContext : public SslContext -{ -public: - RippleSslContext () - : m_context (asio::ssl::context::sslv23) - { - init_ssl_context (m_context); - } - - asio::ssl::context& get_object () - { - return m_context; - } - -public: - static char const* get_ciphers () - { - static char const* ciphers = "ALL:!LOW:!EXP:!MD5:@STRENGTH"; - - return ciphers; - } - - static DH* get_dh_params (int /*key_length*/) - { - static const unsigned char raw512DHParams [] = - { - 0x30, 0x46, 0x02, 0x41, 0x00, 0x98, 0x15, 0xd2, 0xd0, 0x08, 0x32, 0xda, - 0xaa, 0xac, 0xc4, 0x71, 0xa3, 0x1b, 0x11, 0xf0, 0x6c, 0x62, 0xb2, 0x35, - 0x8a, 0x10, 0x92, 0xc6, 0x0a, 0xa3, 0x84, 0x7e, 0xaf, 0x17, 0x29, 0x0b, - 0x70, 0xef, 0x07, 0x4f, 0xfc, 0x9d, 0x6d, 0x87, 0x99, 0x19, 0x09, 0x5b, - 0x6e, 0xdb, 0x57, 0x72, 0x4a, 0x7e, 0xcd, 0xaf, 0xbd, 0x3a, 0x97, 0x55, - 0x51, 0x77, 0x5a, 0x34, 0x7c, 0xe8, 0xc5, 0x71, 0x63, 0x02, 0x01, 0x02 - }; - - struct ScopedDH - { - explicit ScopedDH (DH* dh) - : m_dh (dh) - { - } - - explicit ScopedDH (unsigned char const* rawData, std::size_t bytes) - : m_dh (d2i_DHparams (nullptr, &rawData, bytes)) - { - } - - ~ScopedDH () - { - if (m_dh != nullptr) - DH_free (m_dh); - } - - DH* get () const - { - return m_dh; - } - - operator DH* () const - { - return get (); - } - - private: - DH* m_dh; - }; - - static ScopedDH dh512 (raw512DHParams, sizeof (raw512DHParams)); - - return dh512; - } - - static DH* tmp_dh_handler (SSL* /*ssl*/, int /*is_export*/, int key_length) - { - return DHparams_dup (get_dh_params (key_length)); - } - - static void init_ssl_context (asio::ssl::context& context) - { - context.set_options ( - asio::ssl::context::default_workarounds | - asio::ssl::context::no_sslv2 | - asio::ssl::context::single_dh_use); - - context.set_verify_mode (asio::ssl::verify_none); - - SSL_CTX_set_tmp_dh_callback (context.native_handle (), tmp_dh_handler); - - int const result = SSL_CTX_set_cipher_list (context.native_handle (), get_ciphers ()); - - if (result != 1) - FatalError ("invalid cipher list", __FILE__, __LINE__); - } - -private: - asio::ssl::context m_context; -}; - //------------------------------------------------------------------------------ // A handshaking stream that can distinguish multiple protocols @@ -193,6 +80,7 @@ public: template explicit RippleHandshakeStreamType (Arg& arg, Options options = Options ()) : m_options (options) + , m_context (RippleTlsContext::New ()) , m_next_layer (arg) , m_io_service (m_next_layer.get_io_service ()) , m_strand (m_io_service) @@ -293,16 +181,19 @@ public: std::size_t const amount = asio::buffer_copy (buffers, m_buffer.data ()); m_buffer.consume (amount); return m_io_service.post (m_strand.wrap (boost::bind ( - handler, system::error_code (), amount))); + BOOST_ASIO_MOVE_CAST(ReadHandler)(handler), system::error_code (), amount))); } - return stream ().async_read_some (buffers, m_strand.wrap (handler)); + return stream ().async_read_some (buffers, m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(ReadHandler)(handler), boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); } template BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, void (boost::system::error_code, std::size_t)) async_write_some (ConstBufferSequence const& buffers, BOOST_ASIO_MOVE_ARG(WriteHandler) handler) { - return stream ().async_write_some (buffers, handler); + return stream ().async_write_some (buffers, + BOOST_ASIO_MOVE_CAST(WriteHandler)(handler)); } //-------------------------------------------------------------------------- @@ -344,7 +235,7 @@ public: return ec; } -#if (BOOST_VERSION / 100) >= 1054 +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE template boost::system::error_code handshake (handshake_type type, ConstBufferSequence const& buffers, boost::system::error_code& ec) @@ -391,39 +282,42 @@ public: { default: case actionPlain: - return handshakePlainAsync (handler); + return handshakePlainAsync (BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler)); break; case actionSsl: - return handshakeSslAsync (handler); + return handshakeSslAsync (BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler)); break; case actionDetect: - return detectHandshakeAsync (handler); + return detectHandshakeAsync (BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler)); break; } } -#if (BOOST_VERSION / 100) >= 1054 +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE template BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void (boost::system::error_code, std::size_t)) async_handshake (handshake_type type, const ConstBufferSequence& buffers, - BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler) + BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler) { Action const action = calcAction (type); switch (action) { default: case actionPlain: - return handshakePlainAsync (buffers, handler); + return handshakePlainAsync (buffers, + BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler)); break; case actionSsl: - return handshakeSslAsync (buffers, handler); + return handshakeSslAsync (buffers, + BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler)); break; case actionDetect: - return detectHandshakeAsync (buffers, handler); + return detectHandshakeAsync (buffers, + BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler)); break; } } @@ -448,13 +342,16 @@ public: { if (m_status == ssl) { - m_ssl_stream->async_shutdown (m_strand.wrap (handler)); + m_ssl_stream->async_shutdown (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(ShutdownHandler)(handler), + boost::asio::placeholders::error))); } else { system::error_code ec; m_next_layer.shutdown (next_layer_type::shutdown_both, ec); - m_io_service.post (m_strand.wrap (boost::bind (handler, ec))); + m_io_service.post (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(ShutdownHandler)(handler), ec))); } } @@ -602,10 +499,11 @@ public: handshakePlainAsync (BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler) { createPlainStream (); - return m_io_service.post (m_strand.wrap (boost::bind (handler, system::error_code()))); + return m_io_service.post (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler), system::error_code()))); } -#if (BOOST_VERSION / 100) >= 1054 +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE template BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void (boost::system::error_code, std::size_t)) handshakePlainAsync (ConstBufferSequence const& buffers, @@ -613,14 +511,16 @@ public: { fatal_assert (asio::buffer_size (buffers) == 0); createPlainStream (); - return m_io_service.post (m_strand.wrap (boost::bind (handler, system::error_code(), 0))); + return m_io_service.post (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler), + system::error_code(), 0))); } #endif void createSslStream () { m_status = ssl; - m_ssl_stream = new SslStreamType (m_next_layer, m_context.get_object ()); + m_ssl_stream = new SslStreamType (m_next_layer, m_context->getBoostContext ()); m_stream = new SocketWrapper (*m_ssl_stream); } @@ -630,7 +530,7 @@ public: m_ssl_stream->handshake (m_role, ec); } -#if (BOOST_VERSION / 100) >= 1054 +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE template void handshakeSsl (ConstBufferSequence const& buffers, system::error_code& ec) { @@ -644,17 +544,19 @@ public: handshakeSslAsync (BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler) { createSslStream (); - return m_ssl_stream->async_handshake (m_role, handler); + return m_ssl_stream->async_handshake (m_role, + BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler)); } -#if (BOOST_VERSION / 100) >= 1054 +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE template BOOST_ASIO_INITFN_RESULT_TYPE (BufferedHandshakeHandler, void (boost::system::error_code, std::size_t)) handshakeSslAsync (ConstBufferSequence const& buffers, BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler) { createSslStream (); - return m_ssl_stream->async_handshake (m_role, buffers, handler); + return m_ssl_stream->async_handshake (m_role, buffers, + BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler)); } #endif @@ -682,7 +584,7 @@ public: } } -#if (BOOST_VERSION / 100) >= 1054 +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE template void detectHandshake (ConstBufferSequence const& buffers, system::error_code& ec) { @@ -695,7 +597,7 @@ public: //-------------------------------------------------------------------------- template - void onDetectRead (HandshakeHandler handler, + void onDetectRead (BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler, system::error_code const& ec, std::size_t bytes_transferred) { m_buffer.commit (bytes_transferred); @@ -717,10 +619,12 @@ public: { default: case actionPlain: - handshakePlainAsync (handler); + handshakePlainAsync ( + BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler)); break; case actionSsl: - handshakeSslAsync (handler); + handshakeSslAsync ( + BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler)); break; }; } @@ -732,7 +636,8 @@ public: if (ec) { - m_io_service.post (m_strand.wrap (boost::bind (handler, ec))); + m_io_service.post (m_strand.wrap (boost::bind ( + BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler), ec))); } } } @@ -744,14 +649,16 @@ public: bassert (m_buffer.size () == 0); return m_next_layer.async_receive ( m_buffer.prepare (autoDetectBytes), asio::socket_base::message_peek, - m_strand.wrap (boost::bind (&ThisType::onDetectRead , this, handler, + m_strand.wrap (boost::bind (&ThisType::onDetectRead , this, + BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler), asio::placeholders::error, asio::placeholders::bytes_transferred))); } -#if (BOOST_VERSION / 100) >= 1054 +#if BOOST_ASIO_HAS_BUFFEREDHANDSHAKE template BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void (boost::system::error_code, std::size_t)) - detectHandshakeAsync (ConstBufferSequence const& buffers, BufferedHandshakeHandler handler) + detectHandshakeAsync (ConstBufferSequence const& buffers, + BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler) { fatal_error ("unimplemented"); } @@ -799,7 +706,8 @@ public: private: Options m_options; - RippleSslContext m_context; + //RippleSslContext m_context; + ScopedPointer m_context; Stream m_next_layer; asio::io_service& m_io_service; asio::io_service::strand m_strand; @@ -2180,3 +2088,14 @@ public: static AsioUnitTests asioUnitTests; } + + +/* + +What we want is a MultiSocket + Derived from beast::Socket so it can be used generically + +BUT, conforms to + + +*/