Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/boost/beast/core/detect_ssl.hpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 //
0002 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // Official repository: https://github.com/boostorg/beast
0008 //
0009 
0010 #ifndef BOOST_BEAST_CORE_DETECT_SSL_HPP
0011 #define BOOST_BEAST_CORE_DETECT_SSL_HPP
0012 
0013 #include <boost/beast/core/detail/config.hpp>
0014 #include <boost/beast/core/async_base.hpp>
0015 #include <boost/beast/core/error.hpp>
0016 #include <boost/beast/core/read_size.hpp>
0017 #include <boost/beast/core/stream_traits.hpp>
0018 #include <boost/logic/tribool.hpp>
0019 #include <boost/asio/async_result.hpp>
0020 #include <boost/asio/coroutine.hpp>
0021 #include <type_traits>
0022 
0023 namespace boost {
0024 namespace beast {
0025 
0026 //------------------------------------------------------------------------------
0027 //
0028 // Example: Detect TLS client_hello
0029 //
0030 //  This is an example and also a public interface. It implements
0031 //  an algorithm for determining if a "TLS client_hello" message
0032 //  is received. It can be used to implement a listening port that
0033 //  can handle both plain and TLS encrypted connections.
0034 //
0035 //------------------------------------------------------------------------------
0036 
0037 //[example_core_detect_ssl_1
0038 
0039 // By convention, the "detail" namespace means "not-public."
0040 // Identifiers in a detail namespace are not visible in the documentation,
0041 // and users should not directly use those identifiers in programs, otherwise
0042 // their program may break in the future.
0043 //
0044 // Using a detail namespace gives the library writer the freedom to change
0045 // the interface or behavior later, and maintain backward-compatibility.
0046 
0047 namespace detail {
0048 
0049 /** Return `true` if the buffer contains a TLS Protocol client_hello message.
0050 
0051     This function analyzes the bytes at the beginning of the buffer
0052     and compares it to a valid client_hello message. This is the
0053     message required to be sent by a client at the beginning of
0054     any TLS (encrypted communication) session, including when
0055     resuming a session.
0056 
0057     The return value will be:
0058 
0059     @li `true` if the contents of the buffer unambiguously define
0060     contain a client_hello message,
0061 
0062     @li `false` if the contents of the buffer cannot possibly
0063     be a valid client_hello message, or
0064 
0065     @li `boost::indeterminate` if the buffer contains an
0066     insufficient number of bytes to determine the result. In
0067     this case the caller should read more data from the relevant
0068     stream, append it to the buffers, and call this function again.
0069 
0070     @param buffers The buffer sequence to inspect.
0071     This type must meet the requirements of <em>ConstBufferSequence</em>.
0072 
0073     @return `boost::tribool` indicating whether the buffer contains
0074     a TLS client handshake, does not contain a handshake, or needs
0075     additional bytes to determine an outcome.
0076 
0077     @see
0078 
0079     <a href="https://tools.ietf.org/html/rfc2246#section-7.4">7.4. Handshake protocol</a>
0080     (RFC2246: The TLS Protocol)
0081 */
0082 template <class ConstBufferSequence>
0083 boost::tribool
0084 is_tls_client_hello (ConstBufferSequence const& buffers);
0085 
0086 } // detail
0087 
0088 //]
0089 
0090 //[example_core_detect_ssl_2
0091 
0092 namespace detail {
0093 
0094 template <class ConstBufferSequence>
0095 boost::tribool
0096 is_tls_client_hello (ConstBufferSequence const& buffers)
0097 {
0098     // Make sure buffers meets the requirements
0099     static_assert(
0100         net::is_const_buffer_sequence<ConstBufferSequence>::value,
0101         "ConstBufferSequence type requirements not met");
0102 
0103 /*
0104     The first message on a TLS connection must be the client_hello,
0105     which is a type of handshake record, and it cannot be compressed
0106     or encrypted. A plaintext record has this format:
0107 
0108          0      byte    record_type      // 0x16 = handshake
0109          1      byte    major            // major protocol version
0110          2      byte    minor            // minor protocol version
0111        3-4      uint16  length           // size of the payload
0112          5      byte    handshake_type   // 0x01 = client_hello
0113          6      uint24  length           // size of the ClientHello
0114          9      byte    major            // major protocol version
0115         10      byte    minor            // minor protocol version
0116         11      uint32  gmt_unix_time
0117         15      byte    random_bytes[28]
0118                 ...
0119 */
0120 
0121     // Flatten the input buffers into a single contiguous range
0122     // of bytes on the stack to make it easier to work with the data.
0123     unsigned char buf[9];
0124     auto const n = net::buffer_copy(
0125         net::mutable_buffer(buf, sizeof(buf)), buffers);
0126 
0127     // Can't do much without any bytes
0128     if(n < 1)
0129         return boost::indeterminate;
0130 
0131     // Require the first byte to be 0x16, indicating a TLS handshake record
0132     if(buf[0] != 0x16)
0133         return false;
0134 
0135     // We need at least 5 bytes to know the record payload size
0136     if(n < 5)
0137         return boost::indeterminate;
0138 
0139     // Calculate the record payload size
0140     std::uint32_t const length = (buf[3] << 8) + buf[4];
0141 
0142     // A ClientHello message payload is at least 34 bytes.
0143     // There can be multiple handshake messages in the same record.
0144     if(length < 34)
0145         return false;
0146 
0147     // We need at least 6 bytes to know the handshake type
0148     if(n < 6)
0149         return boost::indeterminate;
0150 
0151     // The handshake_type must be 0x01 == client_hello
0152     if(buf[5] != 0x01)
0153         return false;
0154 
0155     // We need at least 9 bytes to know the payload size
0156     if(n < 9)
0157         return boost::indeterminate;
0158 
0159     // Calculate the message payload size
0160     std::uint32_t const size =
0161         (buf[6] << 16) + (buf[7] << 8) + buf[8];
0162 
0163     // The message payload can't be bigger than the enclosing record
0164     if(size + 4 > length)
0165         return false;
0166 
0167     // This can only be a TLS client_hello message
0168     return true;
0169 }
0170 
0171 } // detail
0172 
0173 //]
0174 
0175 //[example_core_detect_ssl_3
0176 
0177 /** Detect a TLS client handshake on a stream.
0178 
0179     This function reads from a stream to determine if a client
0180     handshake message is being received.
0181     
0182     The call blocks until one of the following is true:
0183 
0184     @li A TLS client opening handshake is detected,
0185 
0186     @li The received data is invalid for a TLS client handshake, or
0187 
0188     @li An error occurs.
0189 
0190     The algorithm, known as a <em>composed operation</em>, is implemented
0191     in terms of calls to the next layer's `read_some` function.
0192 
0193     Bytes read from the stream will be stored in the passed dynamic
0194     buffer, which may be used to perform the TLS handshake if the
0195     detector returns true, or be otherwise consumed by the caller based
0196     on the expected protocol.
0197 
0198     @param stream The stream to read from. This type must meet the
0199     requirements of <em>SyncReadStream</em>.
0200 
0201     @param buffer The dynamic buffer to use. This type must meet the
0202     requirements of <em>DynamicBuffer</em>.
0203 
0204     @param ec Set to the error if any occurred.
0205 
0206     @return `true` if the buffer contains a TLS client handshake and
0207     no error occurred, otherwise `false`.
0208 */
0209 template<
0210     class SyncReadStream,
0211     class DynamicBuffer>
0212 bool
0213 detect_ssl(
0214     SyncReadStream& stream,
0215     DynamicBuffer& buffer,
0216     error_code& ec)
0217 {
0218     namespace beast = boost::beast;
0219 
0220     // Make sure arguments meet the requirements
0221 
0222     static_assert(
0223         is_sync_read_stream<SyncReadStream>::value,
0224         "SyncReadStream type requirements not met");
0225     
0226     static_assert(
0227         net::is_dynamic_buffer<DynamicBuffer>::value,
0228         "DynamicBuffer type requirements not met");
0229 
0230     // Loop until an error occurs or we get a definitive answer
0231     for(;;)
0232     {
0233         // There could already be data in the buffer
0234         // so we do this first, before reading from the stream.
0235         auto const result = detail::is_tls_client_hello(buffer.data());
0236 
0237         // If we got an answer, return it
0238         if(! boost::indeterminate(result))
0239         {
0240             // A definite answer is a success
0241             ec = {};
0242             return static_cast<bool>(result);
0243         }
0244 
0245         // Try to fill our buffer by reading from the stream.
0246         // The function read_size calculates a reasonable size for the
0247         // amount to read next, using existing capacity if possible to
0248         // avoid allocating memory, up to the limit of 1536 bytes which
0249         // is the size of a normal TCP frame.
0250 
0251         std::size_t const bytes_transferred = stream.read_some(
0252             buffer.prepare(beast::read_size(buffer, 1536)), ec);
0253 
0254         // Commit what we read into the buffer's input area.
0255         buffer.commit(bytes_transferred);
0256 
0257         // Check for an error
0258         if(ec)
0259             break;
0260     }
0261 
0262     // error
0263     return false;
0264 }
0265 
0266 //]
0267 
0268 //[example_core_detect_ssl_4
0269 
0270 /** Detect a TLS/SSL handshake asynchronously on a stream.
0271 
0272     This function reads asynchronously from a stream to determine
0273     if a client handshake message is being received.
0274 
0275     This call always returns immediately. The asynchronous operation
0276     will continue until one of the following conditions is true:
0277 
0278     @li A TLS client opening handshake is detected,
0279 
0280     @li The received data is invalid for a TLS client handshake, or
0281 
0282     @li An error occurs.
0283 
0284     The algorithm, known as a <em>composed asynchronous operation</em>,
0285     is implemented in terms of calls to the next layer's `async_read_some`
0286     function. The program must ensure that no other calls to
0287     `async_read_some` are performed until this operation completes.
0288 
0289     Bytes read from the stream will be stored in the passed dynamic
0290     buffer, which may be used to perform the TLS handshake if the
0291     detector returns true, or be otherwise consumed by the caller based
0292     on the expected protocol.
0293 
0294     @param stream The stream to read from. This type must meet the
0295     requirements of <em>AsyncReadStream</em>.
0296 
0297     @param buffer The dynamic buffer to use. This type must meet the
0298     requirements of <em>DynamicBuffer</em>.
0299 
0300     @param token The completion token used to determine the method
0301     used to provide the result of the asynchronous operation. If
0302     this is a completion handler, the implementation takes ownership
0303     of the handler by performing a decay-copy, and the equivalent
0304     function signature of the handler must be:
0305     @code
0306     void handler(
0307         error_code const& error,    // Set to the error, if any
0308         bool result                 // The result of the detector
0309     );
0310     @endcode
0311     If the handler has an associated immediate executor,
0312     an immediate completion will be dispatched to it.
0313     Otherwise, the handler will not be invoked from within
0314     this function. Invocation of the handler will be performed in a
0315     manner equivalent to using `net::post`.
0316 */
0317 template<
0318     class AsyncReadStream,
0319     class DynamicBuffer,
0320     class CompletionToken =
0321         net::default_completion_token_t<beast::executor_type<AsyncReadStream>>
0322 >
0323 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool))
0324 async_detect_ssl(
0325     AsyncReadStream& stream,
0326     DynamicBuffer& buffer,
0327     CompletionToken&& token = net::default_completion_token_t<
0328             beast::executor_type<AsyncReadStream>>{});
0329 //]
0330 
0331 //[example_core_detect_ssl_5
0332 
0333 // These implementation details don't need to be public
0334 
0335 namespace detail {
0336 
0337 // The composed operation object
0338 template<
0339     class DetectHandler,
0340     class AsyncReadStream,
0341     class DynamicBuffer>
0342 class detect_ssl_op;
0343 
0344 // This is a function object which `net::async_initiate` can use to launch
0345 // our composed operation. This is a relatively new feature in networking
0346 // which allows the asynchronous operation to be "lazily" executed (meaning
0347 // that it is launched later). Users don't need to worry about this, but
0348 // authors of composed operations need to write it this way to get the
0349 // very best performance, for example when using Coroutines TS (`co_await`).
0350 
0351 template <typename AsyncReadStream>
0352 struct run_detect_ssl_op
0353 {
0354     // The implementation of `net::async_initiate` captures the
0355     // arguments of the initiating function, and then calls this
0356     // function object later with the captured arguments in order
0357     // to launch the composed operation. All we need to do here
0358     // is take those arguments and construct our composed operation
0359     // object.
0360     //
0361     // `async_initiate` takes care of transforming the completion
0362     // token into the "real handler" which must have the correct
0363     // signature, in this case `void(error_code, boost::tri_bool)`.
0364 
0365     AsyncReadStream* stream;
0366 
0367     using executor_type = typename AsyncReadStream::executor_type;
0368 
0369     executor_type
0370     get_executor() const noexcept
0371     {
0372         return stream->get_executor();
0373     }
0374 
0375     template<
0376         class DetectHandler,
0377         class DynamicBuffer>
0378     void
0379     operator()(
0380         DetectHandler&& h,
0381         DynamicBuffer* b)
0382     {
0383         detect_ssl_op<
0384             typename std::decay<DetectHandler>::type,
0385             AsyncReadStream,
0386             DynamicBuffer>(
0387                 std::forward<DetectHandler>(h), *stream, *b);
0388     }
0389 };
0390 
0391 } // detail
0392 
0393 //]
0394 
0395 //[example_core_detect_ssl_6
0396 
0397 // Here is the implementation of the asynchronous initiation function
0398 template<
0399     class AsyncReadStream,
0400     class DynamicBuffer,
0401     class CompletionToken>
0402 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool))
0403 async_detect_ssl(
0404     AsyncReadStream& stream,
0405     DynamicBuffer& buffer,
0406     CompletionToken&& token)
0407 {
0408     // Make sure arguments meet the type requirements
0409 
0410     static_assert(
0411         is_async_read_stream<AsyncReadStream>::value,
0412         "SyncReadStream type requirements not met");
0413 
0414     static_assert(
0415         net::is_dynamic_buffer<DynamicBuffer>::value,
0416         "DynamicBuffer type requirements not met");
0417 
0418     // The function `net::async_initate` uses customization points
0419     // to allow one asynchronous initiating function to work with
0420     // all sorts of notification systems, such as callbacks but also
0421     // fibers, futures, coroutines, and user-defined types.
0422     //
0423     // It works by capturing all of the arguments using perfect
0424     // forwarding, and then depending on the specialization of
0425     // `net::async_result` for the type of `CompletionToken`,
0426     // the `initiation` object will be invoked with the saved
0427     // parameters and the actual completion handler. Our
0428     // initiating object is `run_detect_ssl_op`.
0429     //
0430     // Non-const references need to be passed as pointers,
0431     // since we don't want a decay-copy.
0432 
0433     return net::async_initiate<
0434         CompletionToken,
0435         void(error_code, bool)>(
0436             detail::run_detect_ssl_op<AsyncReadStream>{&stream},
0437             token,
0438             &buffer);
0439 }
0440 
0441 //]
0442 
0443 //[example_core_detect_ssl_7
0444 
0445 namespace detail {
0446 
0447 // Read from a stream, calling is_tls_client_hello on the data
0448 // data to determine if the TLS client handshake is present.
0449 //
0450 // This will be implemented using Asio's "stackless coroutines"
0451 // which are based on macros forming a switch statement. The
0452 // operation is derived from `coroutine` for this reason.
0453 //
0454 // The library type `async_base` takes care of all of the
0455 // boilerplate for writing composed operations, including:
0456 //
0457 //  * Storing the user's completion handler
0458 //  * Maintaining the work guard for the handler's associated executor
0459 //  * Propagating the associated allocator of the handler
0460 //  * Propagating the associated executor of the handler
0461 //  * Deallocating temporary storage before invoking the handler
0462 //  * Posting the handler to the executor on an immediate completion
0463 //
0464 // `async_base` needs to know the type of the handler, as well
0465 // as the executor of the I/O object being used. The metafunction
0466 // `executor_type` returns the type of executor used by an
0467 // I/O object.
0468 //
0469 template<
0470     class DetectHandler,
0471     class AsyncReadStream,
0472     class DynamicBuffer>
0473 class detect_ssl_op
0474     : public boost::asio::coroutine
0475     , public async_base<
0476         DetectHandler, executor_type<AsyncReadStream>>
0477 {
0478     // This composed operation has trivial state,
0479     // so it is just kept inside the class and can
0480     // be cheaply copied as needed by the implementation.
0481 
0482     AsyncReadStream& stream_;
0483 
0484     // The callers buffer is used to hold all received data
0485     DynamicBuffer& buffer_;
0486 
0487     // We're going to need this in case we have to post the handler
0488     error_code ec_;
0489 
0490     boost::tribool result_ = false;
0491 
0492 public:
0493     // Completion handlers must be MoveConstructible.
0494     detect_ssl_op(detect_ssl_op&&) = default;
0495 
0496     // Construct the operation. The handler is deduced through
0497     // the template type `DetectHandler_`, this lets the same constructor
0498     // work properly for both lvalues and rvalues.
0499     //
0500     template<class DetectHandler_>
0501     detect_ssl_op(
0502         DetectHandler_&& handler,
0503         AsyncReadStream& stream,
0504         DynamicBuffer& buffer)
0505         : beast::async_base<
0506             DetectHandler,
0507             beast::executor_type<AsyncReadStream>>(
0508                 std::forward<DetectHandler_>(handler),
0509                 stream.get_executor())
0510         , stream_(stream)
0511         , buffer_(buffer)
0512     {
0513         // This starts the operation. We pass `false` to tell the
0514         // algorithm that it needs to use net::post if it wants to
0515         // complete immediately. This is required by Networking,
0516         // as initiating functions are not allowed to invoke the
0517         // completion handler on the caller's thread before
0518         // returning.
0519         (*this)({}, 0, false);
0520     }
0521 
0522     // Our main entry point. This will get called as our
0523     // intermediate operations complete. Definition below.
0524     //
0525     // The parameter `cont` indicates if we are being called subsequently
0526     // from the original invocation
0527     //
0528     void operator()(
0529         error_code ec,
0530         std::size_t bytes_transferred,
0531         bool cont = true);
0532 };
0533 
0534 } // detail
0535 
0536 //]
0537 
0538 //[example_core_detect_ssl_8
0539 
0540 namespace detail {
0541 
0542 // This example uses the Asio's stackless "fauxroutines", implemented
0543 // using a macro-based solution. It makes the code easier to write and
0544 // easier to read. This include file defines the necessary macros and types.
0545 #include <boost/asio/yield.hpp>
0546 
0547 // detect_ssl_op is callable with the signature void(error_code, bytes_transferred),
0548 // allowing `*this` to be used as a ReadHandler
0549 //
0550 template<
0551     class AsyncStream,
0552     class DynamicBuffer,
0553     class Handler>
0554 void
0555 detect_ssl_op<AsyncStream, DynamicBuffer, Handler>::
0556 operator()(error_code ec, std::size_t bytes_transferred, bool cont)
0557 {
0558     namespace beast = boost::beast;
0559 
0560     // This introduces the scope of the stackless coroutine
0561     reenter(*this)
0562     {
0563         // Loop until an error occurs or we get a definitive answer
0564         for(;;)
0565         {
0566             // There could already be a hello in the buffer so check first
0567             result_ = is_tls_client_hello(buffer_.data());
0568 
0569             // If we got an answer, then the operation is complete
0570             if(! boost::indeterminate(result_))
0571                 break;
0572 
0573             // Try to fill our buffer by reading from the stream.
0574             // The function read_size calculates a reasonable size for the
0575             // amount to read next, using existing capacity if possible to
0576             // avoid allocating memory, up to the limit of 1536 bytes which
0577             // is the size of a normal TCP frame.
0578             //
0579             // `async_read_some` expects a ReadHandler as the completion
0580             // handler. The signature of a read handler is void(error_code, size_t),
0581             // and this function matches that signature (the `cont` parameter has
0582             // a default of true). We pass `std::move(*this)` as the completion
0583             // handler for the read operation. This transfers ownership of this
0584             // entire state machine back into the `async_read_some` operation.
0585             // Care must be taken with this idiom, to ensure that parameters
0586             // passed to the initiating function which could be invalidated
0587             // by the move, are first moved to the stack before calling the
0588             // initiating function.
0589 
0590             yield
0591             {
0592                 // This macro facilitates asynchrnous handler tracking and
0593                 // debugging when the preprocessor macro
0594                 // BOOST_ASIO_CUSTOM_HANDLER_TRACKING is defined.
0595 
0596                 BOOST_ASIO_HANDLER_LOCATION((
0597                     __FILE__, __LINE__,
0598                     "async_detect_ssl"));
0599 
0600                 stream_.async_read_some(buffer_.prepare(
0601                     read_size(buffer_, 1536)), std::move(*this));
0602             }
0603 
0604             // Commit what we read into the buffer's input area.
0605             buffer_.commit(bytes_transferred);
0606 
0607             // Check for an error
0608             if(ec)
0609                 break;
0610         }
0611 
0612         // If `cont` is true, the handler will be invoked directly.
0613         //
0614         // Otherwise, the handler cannot be invoked directly, because
0615         // initiating functions are not allowed to call the handler
0616         // before returning. Instead, the handler must be posted to
0617         // the I/O context. We issue a zero-byte read using the same
0618         // type of buffers used in the ordinary read above, to prevent
0619         // the compiler from creating an extra instantiation of the
0620         // function template. This reduces compile times and the size
0621         // of the program executable.
0622 
0623         if(! cont)
0624         {
0625             // Save the error, otherwise it will be overwritten with
0626             // a successful error code when this read completes
0627             // immediately.
0628             ec_ = ec;
0629 
0630             // Zero-byte reads and writes are guaranteed to complete
0631             // immediately with succcess. The type of buffers and the
0632             // type of handler passed here need to exactly match the types
0633             // used in the call to async_read_some above, to avoid
0634             // instantiating another version of the function template.
0635 
0636             yield
0637             {
0638                 BOOST_ASIO_HANDLER_LOCATION((
0639                     __FILE__, __LINE__,
0640                     "async_detect_ssl"));
0641 
0642                 stream_.async_read_some(buffer_.prepare(0), std::move(*this));
0643             }
0644 
0645             // Restore the saved error code
0646             BOOST_BEAST_ASSIGN_EC(ec, ec_);
0647         }
0648 
0649         // Invoke the final handler.
0650         // At this point, we are guaranteed that the original initiating
0651         // function is no longer on our stack frame.
0652 
0653         this->complete_now(ec, static_cast<bool>(result_));
0654     }
0655 }
0656 
0657 // Including this file undefines the macros used by the stackless fauxroutines.
0658 #include <boost/asio/unyield.hpp>
0659 
0660 } // detail
0661 
0662 //]
0663 
0664 } // beast
0665 } // boost
0666 
0667 #endif