Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:29:27

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 struct run_detect_ssl_op
0352 {
0353     // The implementation of `net::async_initiate` captures the
0354     // arguments of the initiating function, and then calls this
0355     // function object later with the captured arguments in order
0356     // to launch the composed operation. All we need to do here
0357     // is take those arguments and construct our composed operation
0358     // object.
0359     //
0360     // `async_initiate` takes care of transforming the completion
0361     // token into the "real handler" which must have the correct
0362     // signature, in this case `void(error_code, boost::tri_bool)`.
0363 
0364     template<
0365         class DetectHandler,
0366         class AsyncReadStream,
0367         class DynamicBuffer>
0368     void operator()(
0369         DetectHandler&& h,
0370         AsyncReadStream* s, // references are passed as pointers
0371         DynamicBuffer* b)
0372     {
0373         detect_ssl_op<
0374             typename std::decay<DetectHandler>::type,
0375             AsyncReadStream,
0376             DynamicBuffer>(
0377                 std::forward<DetectHandler>(h), *s, *b);
0378     }
0379 };
0380 
0381 } // detail
0382 
0383 //]
0384 
0385 //[example_core_detect_ssl_6
0386 
0387 // Here is the implementation of the asynchronous initiation function
0388 template<
0389     class AsyncReadStream,
0390     class DynamicBuffer,
0391     class CompletionToken>
0392 BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool))
0393 async_detect_ssl(
0394     AsyncReadStream& stream,
0395     DynamicBuffer& buffer,
0396     CompletionToken&& token)
0397 {
0398     // Make sure arguments meet the type requirements
0399 
0400     static_assert(
0401         is_async_read_stream<AsyncReadStream>::value,
0402         "SyncReadStream type requirements not met");
0403 
0404     static_assert(
0405         net::is_dynamic_buffer<DynamicBuffer>::value,
0406         "DynamicBuffer type requirements not met");
0407 
0408     // The function `net::async_initate` uses customization points
0409     // to allow one asynchronous initiating function to work with
0410     // all sorts of notification systems, such as callbacks but also
0411     // fibers, futures, coroutines, and user-defined types.
0412     //
0413     // It works by capturing all of the arguments using perfect
0414     // forwarding, and then depending on the specialization of
0415     // `net::async_result` for the type of `CompletionToken`,
0416     // the `initiation` object will be invoked with the saved
0417     // parameters and the actual completion handler. Our
0418     // initiating object is `run_detect_ssl_op`.
0419     //
0420     // Non-const references need to be passed as pointers,
0421     // since we don't want a decay-copy.
0422 
0423     return net::async_initiate<
0424         CompletionToken,
0425         void(error_code, bool)>(
0426             detail::run_detect_ssl_op{},
0427             token,
0428             &stream, // pass the reference by pointer
0429             &buffer);
0430 }
0431 
0432 //]
0433 
0434 //[example_core_detect_ssl_7
0435 
0436 namespace detail {
0437 
0438 // Read from a stream, calling is_tls_client_hello on the data
0439 // data to determine if the TLS client handshake is present.
0440 //
0441 // This will be implemented using Asio's "stackless coroutines"
0442 // which are based on macros forming a switch statement. The
0443 // operation is derived from `coroutine` for this reason.
0444 //
0445 // The library type `async_base` takes care of all of the
0446 // boilerplate for writing composed operations, including:
0447 //
0448 //  * Storing the user's completion handler
0449 //  * Maintaining the work guard for the handler's associated executor
0450 //  * Propagating the associated allocator of the handler
0451 //  * Propagating the associated executor of the handler
0452 //  * Deallocating temporary storage before invoking the handler
0453 //  * Posting the handler to the executor on an immediate completion
0454 //
0455 // `async_base` needs to know the type of the handler, as well
0456 // as the executor of the I/O object being used. The metafunction
0457 // `executor_type` returns the type of executor used by an
0458 // I/O object.
0459 //
0460 template<
0461     class DetectHandler,
0462     class AsyncReadStream,
0463     class DynamicBuffer>
0464 class detect_ssl_op
0465     : public boost::asio::coroutine
0466     , public async_base<
0467         DetectHandler, executor_type<AsyncReadStream>>
0468 {
0469     // This composed operation has trivial state,
0470     // so it is just kept inside the class and can
0471     // be cheaply copied as needed by the implementation.
0472 
0473     AsyncReadStream& stream_;
0474 
0475     // The callers buffer is used to hold all received data
0476     DynamicBuffer& buffer_;
0477 
0478     // We're going to need this in case we have to post the handler
0479     error_code ec_;
0480 
0481     boost::tribool result_ = false;
0482 
0483 public:
0484     // Completion handlers must be MoveConstructible.
0485     detect_ssl_op(detect_ssl_op&&) = default;
0486 
0487     // Construct the operation. The handler is deduced through
0488     // the template type `DetectHandler_`, this lets the same constructor
0489     // work properly for both lvalues and rvalues.
0490     //
0491     template<class DetectHandler_>
0492     detect_ssl_op(
0493         DetectHandler_&& handler,
0494         AsyncReadStream& stream,
0495         DynamicBuffer& buffer)
0496         : beast::async_base<
0497             DetectHandler,
0498             beast::executor_type<AsyncReadStream>>(
0499                 std::forward<DetectHandler_>(handler),
0500                 stream.get_executor())
0501         , stream_(stream)
0502         , buffer_(buffer)
0503     {
0504         // This starts the operation. We pass `false` to tell the
0505         // algorithm that it needs to use net::post if it wants to
0506         // complete immediately. This is required by Networking,
0507         // as initiating functions are not allowed to invoke the
0508         // completion handler on the caller's thread before
0509         // returning.
0510         (*this)({}, 0, false);
0511     }
0512 
0513     // Our main entry point. This will get called as our
0514     // intermediate operations complete. Definition below.
0515     //
0516     // The parameter `cont` indicates if we are being called subsequently
0517     // from the original invocation
0518     //
0519     void operator()(
0520         error_code ec,
0521         std::size_t bytes_transferred,
0522         bool cont = true);
0523 };
0524 
0525 } // detail
0526 
0527 //]
0528 
0529 //[example_core_detect_ssl_8
0530 
0531 namespace detail {
0532 
0533 // This example uses the Asio's stackless "fauxroutines", implemented
0534 // using a macro-based solution. It makes the code easier to write and
0535 // easier to read. This include file defines the necessary macros and types.
0536 #include <boost/asio/yield.hpp>
0537 
0538 // detect_ssl_op is callable with the signature void(error_code, bytes_transferred),
0539 // allowing `*this` to be used as a ReadHandler
0540 //
0541 template<
0542     class AsyncStream,
0543     class DynamicBuffer,
0544     class Handler>
0545 void
0546 detect_ssl_op<AsyncStream, DynamicBuffer, Handler>::
0547 operator()(error_code ec, std::size_t bytes_transferred, bool cont)
0548 {
0549     namespace beast = boost::beast;
0550 
0551     // This introduces the scope of the stackless coroutine
0552     reenter(*this)
0553     {
0554         // Loop until an error occurs or we get a definitive answer
0555         for(;;)
0556         {
0557             // There could already be a hello in the buffer so check first
0558             result_ = is_tls_client_hello(buffer_.data());
0559 
0560             // If we got an answer, then the operation is complete
0561             if(! boost::indeterminate(result_))
0562                 break;
0563 
0564             // Try to fill our buffer by reading from the stream.
0565             // The function read_size calculates a reasonable size for the
0566             // amount to read next, using existing capacity if possible to
0567             // avoid allocating memory, up to the limit of 1536 bytes which
0568             // is the size of a normal TCP frame.
0569             //
0570             // `async_read_some` expects a ReadHandler as the completion
0571             // handler. The signature of a read handler is void(error_code, size_t),
0572             // and this function matches that signature (the `cont` parameter has
0573             // a default of true). We pass `std::move(*this)` as the completion
0574             // handler for the read operation. This transfers ownership of this
0575             // entire state machine back into the `async_read_some` operation.
0576             // Care must be taken with this idiom, to ensure that parameters
0577             // passed to the initiating function which could be invalidated
0578             // by the move, are first moved to the stack before calling the
0579             // initiating function.
0580 
0581             yield
0582             {
0583                 // This macro facilitates asynchrnous handler tracking and
0584                 // debugging when the preprocessor macro
0585                 // BOOST_ASIO_CUSTOM_HANDLER_TRACKING is defined.
0586 
0587                 BOOST_ASIO_HANDLER_LOCATION((
0588                     __FILE__, __LINE__,
0589                     "async_detect_ssl"));
0590 
0591                 stream_.async_read_some(buffer_.prepare(
0592                     read_size(buffer_, 1536)), std::move(*this));
0593             }
0594 
0595             // Commit what we read into the buffer's input area.
0596             buffer_.commit(bytes_transferred);
0597 
0598             // Check for an error
0599             if(ec)
0600                 break;
0601         }
0602 
0603         // If `cont` is true, the handler will be invoked directly.
0604         //
0605         // Otherwise, the handler cannot be invoked directly, because
0606         // initiating functions are not allowed to call the handler
0607         // before returning. Instead, the handler must be posted to
0608         // the I/O context. We issue a zero-byte read using the same
0609         // type of buffers used in the ordinary read above, to prevent
0610         // the compiler from creating an extra instantiation of the
0611         // function template. This reduces compile times and the size
0612         // of the program executable.
0613 
0614         if(! cont)
0615         {
0616             // Save the error, otherwise it will be overwritten with
0617             // a successful error code when this read completes
0618             // immediately.
0619             ec_ = ec;
0620 
0621             // Zero-byte reads and writes are guaranteed to complete
0622             // immediately with succcess. The type of buffers and the
0623             // type of handler passed here need to exactly match the types
0624             // used in the call to async_read_some above, to avoid
0625             // instantiating another version of the function template.
0626 
0627             yield
0628             {
0629                 BOOST_ASIO_HANDLER_LOCATION((
0630                     __FILE__, __LINE__,
0631                     "async_detect_ssl"));
0632 
0633                 stream_.async_read_some(buffer_.prepare(0), std::move(*this));
0634             }
0635 
0636             // Restore the saved error code
0637             BOOST_BEAST_ASSIGN_EC(ec, ec_);
0638         }
0639 
0640         // Invoke the final handler.
0641         // At this point, we are guaranteed that the original initiating
0642         // function is no longer on our stack frame.
0643 
0644         this->complete_now(ec, static_cast<bool>(result_));
0645     }
0646 }
0647 
0648 // Including this file undefines the macros used by the stackless fauxroutines.
0649 #include <boost/asio/unyield.hpp>
0650 
0651 } // detail
0652 
0653 //]
0654 
0655 } // beast
0656 } // boost
0657 
0658 #endif