![]() |
|
|||
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
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
![]() ![]() |