Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / beast / core / detect_ssl.hpp
1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9
10 #ifndef BOOST_BEAST_CORE_DETECT_SSL_HPP
11 #define BOOST_BEAST_CORE_DETECT_SSL_HPP
12
13 #include <boost/beast/core/detail/config.hpp>
14 #include <boost/beast/core/async_base.hpp>
15 #include <boost/beast/core/error.hpp>
16 #include <boost/beast/core/read_size.hpp>
17 #include <boost/beast/core/stream_traits.hpp>
18 #include <boost/logic/tribool.hpp>
19 #include <boost/asio/async_result.hpp>
20 #include <boost/asio/coroutine.hpp>
21 #include <type_traits>
22
23 namespace boost {
24 namespace beast {
25
26 //------------------------------------------------------------------------------
27 //
28 // Example: Detect TLS client_hello
29 //
30 //  This is an example and also a public interface. It implements
31 //  an algorithm for determining if a "TLS client_hello" message
32 //  is received. It can be used to implement a listening port that
33 //  can handle both plain and TLS encrypted connections.
34 //
35 //------------------------------------------------------------------------------
36
37 //[example_core_detect_ssl_1
38
39 // By convention, the "detail" namespace means "not-public."
40 // Identifiers in a detail namespace are not visible in the documentation,
41 // and users should not directly use those identifiers in programs, otherwise
42 // their program may break in the future.
43 //
44 // Using a detail namespace gives the library writer the freedom to change
45 // the interface or behavior later, and maintain backward-compatibility.
46
47 namespace detail {
48
49 /** Return `true` if the buffer contains a TLS Protocol client_hello message.
50
51     This function analyzes the bytes at the beginning of the buffer
52     and compares it to a valid client_hello message. This is the
53     message required to be sent by a client at the beginning of
54     any TLS (encrypted communication) session, including when
55     resuming a session.
56
57     The return value will be:
58
59     @li `true` if the contents of the buffer unambiguously define
60     contain a client_hello message,
61
62     @li `false` if the contents of the buffer cannot possibly
63     be a valid client_hello message, or
64
65     @li `boost::indeterminate` if the buffer contains an
66     insufficient number of bytes to determine the result. In
67     this case the caller should read more data from the relevant
68     stream, append it to the buffers, and call this function again.
69
70     @param buffers The buffer sequence to inspect.
71     This type must meet the requirements of <em>ConstBufferSequence</em>.
72
73     @return `boost::tribool` indicating whether the buffer contains
74     a TLS client handshake, does not contain a handshake, or needs
75     additional bytes to determine an outcome.
76
77     @see
78
79     <a href="https://tools.ietf.org/html/rfc2246#section-7.4">7.4. Handshake protocol</a>
80     (RFC2246: The TLS Protocol)
81 */
82 template <class ConstBufferSequence>
83 boost::tribool
84 is_tls_client_hello (ConstBufferSequence const& buffers);
85
86 } // detail
87
88 //]
89
90 //[example_core_detect_ssl_2
91
92 namespace detail {
93
94 template <class ConstBufferSequence>
95 boost::tribool
96 is_tls_client_hello (ConstBufferSequence const& buffers)
97 {
98     // Make sure buffers meets the requirements
99     static_assert(
100         net::is_const_buffer_sequence<ConstBufferSequence>::value,
101         "ConstBufferSequence type requirements not met");
102
103 /*
104     The first message on a TLS connection must be the client_hello,
105     which is a type of handshake record, and it cannot be compressed
106     or encrypted. A plaintext record has this format:
107
108          0      byte    record_type      // 0x16 = handshake
109          1      byte    major            // major protocol version
110          2      byte    minor            // minor protocol version
111        3-4      uint16  length           // size of the payload
112          5      byte    handshake_type   // 0x01 = client_hello
113          6      uint24  length           // size of the ClientHello
114          9      byte    major            // major protocol version
115         10      byte    minor            // minor protocol version
116         11      uint32  gmt_unix_time
117         15      byte    random_bytes[28]
118                 ...
119 */
120
121     // Flatten the input buffers into a single contiguous range
122     // of bytes on the stack to make it easier to work with the data.
123     unsigned char buf[9];
124     auto const n = net::buffer_copy(
125         net::mutable_buffer(buf, sizeof(buf)), buffers);
126
127     // Can't do much without any bytes
128     if(n < 1)
129         return boost::indeterminate;
130
131     // Require the first byte to be 0x16, indicating a TLS handshake record
132     if(buf[0] != 0x16)
133         return false;
134
135     // We need at least 5 bytes to know the record payload size
136     if(n < 5)
137         return boost::indeterminate;
138
139     // Calculate the record payload size
140     std::uint32_t const length = (buf[3] << 8) + buf[4];
141
142     // A ClientHello message payload is at least 34 bytes.
143     // There can be multiple handshake messages in the same record.
144     if(length < 34)
145         return false;
146
147     // We need at least 6 bytes to know the handshake type
148     if(n < 6)
149         return boost::indeterminate;
150
151     // The handshake_type must be 0x01 == client_hello
152     if(buf[5] != 0x01)
153         return false;
154
155     // We need at least 9 bytes to know the payload size
156     if(n < 9)
157         return boost::indeterminate;
158
159     // Calculate the message payload size
160     std::uint32_t const size =
161         (buf[6] << 16) + (buf[7] << 8) + buf[8];
162
163     // The message payload can't be bigger than the enclosing record
164     if(size + 4 > length)
165         return false;
166
167     // This can only be a TLS client_hello message
168     return true;
169 }
170
171 } // detail
172
173 //]
174
175 //[example_core_detect_ssl_3
176
177 /** Detect a TLS client handshake on a stream.
178
179     This function reads from a stream to determine if a client
180     handshake message is being received.
181     
182     The call blocks until one of the following is true:
183
184     @li A TLS client opening handshake is detected,
185
186     @li The received data is invalid for a TLS client handshake, or
187
188     @li An error occurs.
189
190     The algorithm, known as a <em>composed operation</em>, is implemented
191     in terms of calls to the next layer's `read_some` function.
192
193     Bytes read from the stream will be stored in the passed dynamic
194     buffer, which may be used to perform the TLS handshake if the
195     detector returns true, or be otherwise consumed by the caller based
196     on the expected protocol.
197
198     @param stream The stream to read from. This type must meet the
199     requirements of <em>SyncReadStream</em>.
200
201     @param buffer The dynamic buffer to use. This type must meet the
202     requirements of <em>DynamicBuffer</em>.
203
204     @param ec Set to the error if any occurred.
205
206     @return `true` if the buffer contains a TLS client handshake and
207     no error occurred, otherwise `false`.
208 */
209 template<
210     class SyncReadStream,
211     class DynamicBuffer>
212 bool
213 detect_ssl(
214     SyncReadStream& stream,
215     DynamicBuffer& buffer,
216     error_code& ec)
217 {
218     namespace beast = boost::beast;
219
220     // Make sure arguments meet the requirements
221
222     static_assert(
223         is_sync_read_stream<SyncReadStream>::value,
224         "SyncReadStream type requirements not met");
225     
226     static_assert(
227         net::is_dynamic_buffer<DynamicBuffer>::value,
228         "DynamicBuffer type requirements not met");
229
230     // Loop until an error occurs or we get a definitive answer
231     for(;;)
232     {
233         // There could already be data in the buffer
234         // so we do this first, before reading from the stream.
235         auto const result = detail::is_tls_client_hello(buffer.data());
236
237         // If we got an answer, return it
238         if(! boost::indeterminate(result))
239         {
240             // A definite answer is a success
241             ec = {};
242             return static_cast<bool>(result);
243         }
244
245         // Try to fill our buffer by reading from the stream.
246         // The function read_size calculates a reasonable size for the
247         // amount to read next, using existing capacity if possible to
248         // avoid allocating memory, up to the limit of 1536 bytes which
249         // is the size of a normal TCP frame.
250
251         std::size_t const bytes_transferred = stream.read_some(
252             buffer.prepare(beast::read_size(buffer, 1536)), ec);
253
254         // Commit what we read into the buffer's input area.
255         buffer.commit(bytes_transferred);
256
257         // Check for an error
258         if(ec)
259             break;
260     }
261
262     // error
263     return false;
264 }
265
266 //]
267
268 //[example_core_detect_ssl_4
269
270 /** Detect a TLS/SSL handshake asynchronously on a stream.
271
272     This function reads asynchronously from a stream to determine
273     if a client handshake message is being received.
274
275     This call always returns immediately. The asynchronous operation
276     will continue until one of the following conditions is true:
277
278     @li A TLS client opening handshake is detected,
279
280     @li The received data is invalid for a TLS client handshake, or
281
282     @li An error occurs.
283
284     The algorithm, known as a <em>composed asynchronous operation</em>,
285     is implemented in terms of calls to the next layer's `async_read_some`
286     function. The program must ensure that no other calls to
287     `async_read_some` are performed until this operation completes.
288
289     Bytes read from the stream will be stored in the passed dynamic
290     buffer, which may be used to perform the TLS handshake if the
291     detector returns true, or be otherwise consumed by the caller based
292     on the expected protocol.
293
294     @param stream The stream to read from. This type must meet the
295     requirements of <em>AsyncReadStream</em>.
296
297     @param buffer The dynamic buffer to use. This type must meet the
298     requirements of <em>DynamicBuffer</em>.
299
300     @param token The completion token used to determine the method
301     used to provide the result of the asynchronous operation. If
302     this is a completion handler, the implementation takes ownership
303     of the handler by performing a decay-copy, and the equivalent
304     function signature of the handler must be:
305     @code
306     void handler(
307         error_code const& error,    // Set to the error, if any
308         bool result                 // The result of the detector
309     );
310     @endcode
311     Regardless of whether the asynchronous operation completes
312     immediately or not, the handler will not be invoked from within
313     this function. Invocation of the handler will be performed in a
314     manner equivalent to using `net::post`.
315 */
316 template<
317     class AsyncReadStream,
318     class DynamicBuffer,
319     class CompletionToken =
320         net::default_completion_token_t<beast::executor_type<AsyncReadStream>>
321 >
322 #if BOOST_BEAST_DOXYGEN
323 BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, bool))
324 #else
325 auto
326 #endif
327 async_detect_ssl(
328     AsyncReadStream& stream,
329     DynamicBuffer& buffer,
330     CompletionToken&& token = net::default_completion_token_t<
331             beast::executor_type<AsyncReadStream>>{}) ->
332         typename net::async_result<
333             typename std::decay<CompletionToken>::type, /*< `async_result` customizes the return value based on the completion token >*/
334             void(error_code, bool)>::return_type; /*< This is the signature for the completion handler >*/
335 //]
336
337 //[example_core_detect_ssl_5
338
339 // These implementation details don't need to be public
340
341 namespace detail {
342
343 // The composed operation object
344 template<
345     class DetectHandler,
346     class AsyncReadStream,
347     class DynamicBuffer>
348 class detect_ssl_op;
349
350 // This is a function object which `net::async_initiate` can use to launch
351 // our composed operation. This is a relatively new feature in networking
352 // which allows the asynchronous operation to be "lazily" executed (meaning
353 // that it is launched later). Users don't need to worry about this, but
354 // authors of composed operations need to write it this way to get the
355 // very best performance, for example when using Coroutines TS (`co_await`).
356
357 struct run_detect_ssl_op
358 {
359     // The implementation of `net::async_initiate` captures the
360     // arguments of the initiating function, and then calls this
361     // function object later with the captured arguments in order
362     // to launch the composed operation. All we need to do here
363     // is take those arguments and construct our composed operation
364     // object.
365     //
366     // `async_initiate` takes care of transforming the completion
367     // token into the "real handler" which must have the correct
368     // signature, in this case `void(error_code, boost::tri_bool)`.
369
370     template<
371         class DetectHandler,
372         class AsyncReadStream,
373         class DynamicBuffer>
374     void operator()(
375         DetectHandler&& h,
376         AsyncReadStream* s, // references are passed as pointers
377         DynamicBuffer& b)
378     {
379         detect_ssl_op<
380             typename std::decay<DetectHandler>::type,
381             AsyncReadStream,
382             DynamicBuffer>(
383                 std::forward<DetectHandler>(h), *s, b);
384     }
385 };
386
387 } // detail
388
389 //]
390
391 //[example_core_detect_ssl_6
392
393 // Here is the implementation of the asynchronous initiation function
394 template<
395     class AsyncReadStream,
396     class DynamicBuffer,
397     class CompletionToken>
398 #if BOOST_BEAST_DOXYGEN
399 BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, bool))
400 #else
401 auto
402 #endif
403 async_detect_ssl(
404     AsyncReadStream& stream,
405     DynamicBuffer& buffer,
406     CompletionToken&& token)
407         -> typename net::async_result<
408             typename std::decay<CompletionToken>::type,
409             void(error_code, bool)>::return_type
410 {
411     // Make sure arguments meet the type requirements
412
413     static_assert(
414         is_async_read_stream<AsyncReadStream>::value,
415         "SyncReadStream type requirements not met");
416
417     static_assert(
418         net::is_dynamic_buffer<DynamicBuffer>::value,
419         "DynamicBuffer type requirements not met");
420
421     // The function `net::async_initate` uses customization points
422     // to allow one asynchronous initiating function to work with
423     // all sorts of notification systems, such as callbacks but also
424     // fibers, futures, coroutines, and user-defined types.
425     //
426     // It works by capturing all of the arguments using perfect
427     // forwarding, and then depending on the specialization of
428     // `net::async_result` for the type of `CompletionToken`,
429     // the `initiation` object will be invoked with the saved
430     // parameters and the actual completion handler. Our
431     // initiating object is `run_detect_ssl_op`.
432     //
433     // Non-const references need to be passed as pointers,
434     // since we don't want a decay-copy.
435
436     return net::async_initiate<
437         CompletionToken,
438         void(error_code, bool)>(
439             detail::run_detect_ssl_op{},
440             token,
441             &stream, // pass the reference by pointer
442             buffer);
443 }
444
445 //]
446
447 //[example_core_detect_ssl_7
448
449 namespace detail {
450
451 // Read from a stream, calling is_tls_client_hello on the data
452 // data to determine if the TLS client handshake is present.
453 //
454 // This will be implemented using Asio's "stackless coroutines"
455 // which are based on macros forming a switch statement. The
456 // operation is derived from `coroutine` for this reason.
457 //
458 // The library type `async_base` takes care of all of the
459 // boilerplate for writing composed operations, including:
460 //
461 //  * Storing the user's completion handler
462 //  * Maintaining the work guard for the handler's associated executor
463 //  * Propagating the associated allocator of the handler
464 //  * Propagating the associated executor of the handler
465 //  * Deallocating temporary storage before invoking the handler
466 //  * Posting the handler to the executor on an immediate completion
467 //
468 // `async_base` needs to know the type of the handler, as well
469 // as the executor of the I/O object being used. The metafunction
470 // `executor_type` returns the type of executor used by an
471 // I/O object.
472 //
473 template<
474     class DetectHandler,
475     class AsyncReadStream,
476     class DynamicBuffer>
477 class detect_ssl_op
478     : public boost::asio::coroutine
479     , public async_base<
480         DetectHandler, executor_type<AsyncReadStream>>
481 {
482     // This composed operation has trivial state,
483     // so it is just kept inside the class and can
484     // be cheaply copied as needed by the implementation.
485
486     AsyncReadStream& stream_;
487
488     // The callers buffer is used to hold all received data
489     DynamicBuffer& buffer_;
490
491     // We're going to need this in case we have to post the handler
492     error_code ec_;
493
494     boost::tribool result_ = false;
495
496 public:
497     // Completion handlers must be MoveConstructible.
498     detect_ssl_op(detect_ssl_op&&) = default;
499
500     // Construct the operation. The handler is deduced through
501     // the template type `DetectHandler_`, this lets the same constructor
502     // work properly for both lvalues and rvalues.
503     //
504     template<class DetectHandler_>
505     detect_ssl_op(
506         DetectHandler_&& handler,
507         AsyncReadStream& stream,
508         DynamicBuffer& buffer)
509         : beast::async_base<
510             DetectHandler,
511             beast::executor_type<AsyncReadStream>>(
512                 std::forward<DetectHandler_>(handler),
513                 stream.get_executor())
514         , stream_(stream)
515         , buffer_(buffer)
516     {
517         // This starts the operation. We pass `false` to tell the
518         // algorithm that it needs to use net::post if it wants to
519         // complete immediately. This is required by Networking,
520         // as initiating functions are not allowed to invoke the
521         // completion handler on the caller's thread before
522         // returning.
523         (*this)({}, 0, false);
524     }
525
526     // Our main entry point. This will get called as our
527     // intermediate operations complete. Definition below.
528     //
529     // The parameter `cont` indicates if we are being called subsequently
530     // from the original invocation
531     //
532     void operator()(
533         error_code ec,
534         std::size_t bytes_transferred,
535         bool cont = true);
536 };
537
538 } // detail
539
540 //]
541
542 //[example_core_detect_ssl_8
543
544 namespace detail {
545
546 // This example uses the Asio's stackless "fauxroutines", implemented
547 // using a macro-based solution. It makes the code easier to write and
548 // easier to read. This include file defines the necessary macros and types.
549 #include <boost/asio/yield.hpp>
550
551 // detect_ssl_op is callable with the signature void(error_code, bytes_transferred),
552 // allowing `*this` to be used as a ReadHandler
553 //
554 template<
555     class AsyncStream,
556     class DynamicBuffer,
557     class Handler>
558 void
559 detect_ssl_op<AsyncStream, DynamicBuffer, Handler>::
560 operator()(error_code ec, std::size_t bytes_transferred, bool cont)
561 {
562     namespace beast = boost::beast;
563
564     // This introduces the scope of the stackless coroutine
565     reenter(*this)
566     {
567         // Loop until an error occurs or we get a definitive answer
568         for(;;)
569         {
570             // There could already be a hello in the buffer so check first
571             result_ = is_tls_client_hello(buffer_.data());
572
573             // If we got an answer, then the operation is complete
574             if(! boost::indeterminate(result_))
575                 break;
576
577             // Try to fill our buffer by reading from the stream.
578             // The function read_size calculates a reasonable size for the
579             // amount to read next, using existing capacity if possible to
580             // avoid allocating memory, up to the limit of 1536 bytes which
581             // is the size of a normal TCP frame.
582             //
583             // `async_read_some` expects a ReadHandler as the completion
584             // handler. The signature of a read handler is void(error_code, size_t),
585             // and this function matches that signature (the `cont` parameter has
586             // a default of true). We pass `std::move(*this)` as the completion
587             // handler for the read operation. This transfers ownership of this
588             // entire state machine back into the `async_read_some` operation.
589             // Care must be taken with this idiom, to ensure that parameters
590             // passed to the initiating function which could be invalidated
591             // by the move, are first moved to the stack before calling the
592             // initiating function.
593
594             yield stream_.async_read_some(buffer_.prepare(
595                 read_size(buffer_, 1536)), std::move(*this));
596
597             // Commit what we read into the buffer's input area.
598             buffer_.commit(bytes_transferred);
599
600             // Check for an error
601             if(ec)
602                 break;
603         }
604
605         // If `cont` is true, the handler will be invoked directly.
606         //
607         // Otherwise, the handler cannot be invoked directly, because
608         // initiating functions are not allowed to call the handler
609         // before returning. Instead, the handler must be posted to
610         // the I/O context. We issue a zero-byte read using the same
611         // type of buffers used in the ordinary read above, to prevent
612         // the compiler from creating an extra instantiation of the
613         // function template. This reduces compile times and the size
614         // of the program executable.
615
616         if(! cont)
617         {
618             // Save the error, otherwise it will be overwritten with
619             // a successful error code when this read completes
620             // immediately.
621             ec_ = ec;
622
623             // Zero-byte reads and writes are guaranteed to complete
624             // immediately with succcess. The type of buffers and the
625             // type of handler passed here need to exactly match the types
626             // used in the call to async_read_some above, to avoid
627             // instantiating another version of the function template.
628
629             yield stream_.async_read_some(buffer_.prepare(0), std::move(*this));
630
631             // Restore the saved error code
632             ec = ec_;
633         }
634
635         // Invoke the final handler.
636         // At this point, we are guaranteed that the original initiating
637         // function is no longer on our stack frame.
638
639         this->complete_now(ec, static_cast<bool>(result_));
640     }
641 }
642
643 // Including this file undefines the macros used by the stackless fauxroutines.
644 #include <boost/asio/unyield.hpp>
645
646 } // detail
647
648 //]
649
650 } // beast
651 } // boost
652
653 #endif