]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/example/common/detect_ssl.hpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / beast / example / common / detect_ssl.hpp
1 //
2 // Copyright (c) 2016-2017 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_EXAMPLE_COMMON_DETECT_SSL_HPP
11 #define BOOST_BEAST_EXAMPLE_COMMON_DETECT_SSL_HPP
12
13 #include <boost/assert.hpp>
14 #include <boost/config.hpp>
15
16 //------------------------------------------------------------------------------
17 //
18 // Example: Detect TLS/SSL
19 //
20 //------------------------------------------------------------------------------
21
22 //[example_core_detect_ssl_1
23
24 #include <boost/beast/core.hpp>
25 #include <boost/logic/tribool.hpp>
26
27 /** Return `true` if a buffer contains a TLS/SSL client handshake.
28
29 This function returns `true` if the beginning of the buffer
30 indicates that a TLS handshake is being negotiated, and that
31 there are at least four octets in the buffer.
32
33 If the content of the buffer cannot possibly be a TLS handshake
34 request, the function returns `false`. Otherwise, if additional
35 octets are required, `boost::indeterminate` is returned.
36
37 @param buffer The input buffer to inspect. This type must meet
38 the requirements of @b ConstBufferSequence.
39
40 @return `boost::tribool` indicating whether the buffer contains
41 a TLS client handshake, does not contain a handshake, or needs
42 additional octets.
43
44 @see
45
46 http://www.ietf.org/rfc/rfc2246.txt
47 7.4. Handshake protocol
48 */
49 template<class ConstBufferSequence>
50 boost::tribool
51 is_ssl_handshake(ConstBufferSequence const& buffers);
52
53 //]
54
55 //[example_core_detect_ssl_2
56
57 template<
58 class ConstBufferSequence>
59 boost::tribool
60 is_ssl_handshake(
61 ConstBufferSequence const& buffers)
62 {
63 // Make sure buffers meets the requirements
64 static_assert(
65 boost::asio::is_const_buffer_sequence<ConstBufferSequence>::value,
66 "ConstBufferSequence requirements not met");
67
68 // We need at least one byte to really do anything
69 if(boost::asio::buffer_size(buffers) < 1)
70 return boost::indeterminate;
71
72 // Extract the first byte, which holds the
73 // "message" type for the Handshake protocol.
74 unsigned char v;
75 boost::asio::buffer_copy(boost::asio::buffer(&v, 1), buffers);
76
77 // Check that the message type is "SSL Handshake" (rfc2246)
78 if(v != 0x16)
79 {
80 // This is definitely not a handshake
81 return false;
82 }
83
84 // At least four bytes are needed for the handshake
85 // so make sure that we get them before returning `true`
86 if(boost::asio::buffer_size(buffers) < 4)
87 return boost::indeterminate;
88
89 // This can only be a TLS/SSL handshake
90 return true;
91 }
92
93 //]
94
95 //[example_core_detect_ssl_3
96
97 /** Detect a TLS/SSL handshake on a stream.
98
99 This function reads from a stream to determine if a TLS/SSL
100 handshake is being received. The function call will block
101 until one of the following conditions is true:
102
103 @li The disposition of the handshake is determined
104
105 @li An error occurs
106
107 Octets read from the stream will be stored in the passed dynamic
108 buffer, which may be used to perform the TLS handshake if the
109 detector returns true, or otherwise consumed by the caller based
110 on the expected protocol.
111
112 @param stream The stream to read from. This type must meet the
113 requirements of @b SyncReadStream.
114
115 @param buffer The dynamic buffer to use. This type must meet the
116 requirements of @b DynamicBuffer.
117
118 @param ec Set to the error if any occurred.
119
120 @return `boost::tribool` indicating whether the buffer contains
121 a TLS client handshake, does not contain a handshake, or needs
122 additional octets. If an error occurs, the return value is
123 undefined.
124 */
125 template<
126 class SyncReadStream,
127 class DynamicBuffer>
128 boost::tribool
129 detect_ssl(
130 SyncReadStream& stream,
131 DynamicBuffer& buffer,
132 boost::beast::error_code& ec)
133 {
134 namespace beast = boost::beast;
135
136 // Make sure arguments meet the requirements
137 static_assert(beast::is_sync_read_stream<SyncReadStream>::value,
138 "SyncReadStream requirements not met");
139 static_assert(
140 boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
141 "DynamicBuffer requirements not met");
142
143 // Loop until an error occurs or we get a definitive answer
144 for(;;)
145 {
146 // There could already be data in the buffer
147 // so we do this first, before reading from the stream.
148 auto const result = is_ssl_handshake(buffer.data());
149
150 // If we got an answer, return it
151 if(! boost::indeterminate(result))
152 {
153 // This is a fast way to indicate success
154 // without retrieving the default category.
155 ec.assign(0, ec.category());
156 return result;
157 }
158
159 // The algorithm should never need more than 4 bytes
160 BOOST_ASSERT(buffer.size() < 4);
161
162 // Prepare the buffer's output area.
163 auto const mutable_buffer = buffer.prepare(beast::read_size(buffer, 1536));
164
165 // Try to fill our buffer by reading from the stream
166 std::size_t const bytes_transferred = stream.read_some(mutable_buffer, ec);
167
168 // Check for an error
169 if(ec)
170 break;
171
172 // Commit what we read into the buffer's input area.
173 buffer.commit(bytes_transferred);
174 }
175
176 // error
177 return false;
178 }
179
180 //]
181
182 //[example_core_detect_ssl_4
183
184 /** Detect a TLS/SSL handshake asynchronously on a stream.
185
186 This function is used to asynchronously determine if a TLS/SSL
187 handshake is being received.
188 The function call always returns immediately. The asynchronous
189 operation will continue until one of the following conditions
190 is true:
191
192 @li The disposition of the handshake is determined
193
194 @li An error occurs
195
196 This operation is implemented in terms of zero or more calls to
197 the next layer's `async_read_some` function, and is known as a
198 <em>composed operation</em>. The program must ensure that the
199 stream performs no other operations until this operation completes.
200
201 Octets read from the stream will be stored in the passed dynamic
202 buffer, which may be used to perform the TLS handshake if the
203 detector returns true, or otherwise consumed by the caller based
204 on the expected protocol.
205
206 @param stream The stream to read from. This type must meet the
207 requirements of @b AsyncReadStream.
208
209 @param buffer The dynamic buffer to use. This type must meet the
210 requirements of @b DynamicBuffer.
211
212 @param handler The handler to be called when the request
213 completes. Copies will be made of the handler as required.
214 The equivalent function signature of the handler must be:
215 @code
216 void handler(
217 error_code const& error, // Set to the error, if any
218 boost::tribool result // The result of the detector
219 );
220 @endcode
221 Regardless of whether the asynchronous operation completes
222 immediately or not, the handler will not be invoked from within
223 this function. Invocation of the handler will be performed in a
224 manner equivalent to using `boost::asio::io_context::post`.
225 */
226 template<
227 class AsyncReadStream,
228 class DynamicBuffer,
229 class CompletionToken>
230 BOOST_ASIO_INITFN_RESULT_TYPE( /*< `BOOST_ASIO_INITFN_RESULT_TYPE` customizes the return value based on the completion token >*/
231 CompletionToken,
232 void(boost::beast::error_code, boost::tribool)) /*< This is the signature for the completion handler >*/
233 async_detect_ssl(
234 AsyncReadStream& stream,
235 DynamicBuffer& buffer,
236 CompletionToken&& token);
237
238 //]
239
240 //[example_core_detect_ssl_5
241
242 // This is the composed operation.
243 template<
244 class AsyncReadStream,
245 class DynamicBuffer,
246 class Handler>
247 class detect_ssl_op;
248
249 // Here is the implementation of the asynchronous initation function
250 template<
251 class AsyncReadStream,
252 class DynamicBuffer,
253 class CompletionToken>
254 BOOST_ASIO_INITFN_RESULT_TYPE(
255 CompletionToken,
256 void(boost::beast::error_code, boost::tribool))
257 async_detect_ssl(
258 AsyncReadStream& stream,
259 DynamicBuffer& buffer,
260 CompletionToken&& token)
261 {
262 namespace beast = boost::beast;
263
264 // Make sure arguments meet the requirements
265 static_assert(beast::is_async_read_stream<AsyncReadStream>::value,
266 "SyncReadStream requirements not met");
267 static_assert(
268 boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
269 "DynamicBuffer requirements not met");
270
271 // This helper manages some of the handler's lifetime and
272 // uses the result and handler specializations associated with
273 // the completion token to help customize the return value.
274 //
275 boost::asio::async_completion<
276 CompletionToken, void(beast::error_code, boost::tribool)> init{token};
277
278 // Create the composed operation and launch it. This is a constructor
279 // call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE
280 // to convert the completion token into the correct handler type,
281 // allowing user defined specializations of the async result template
282 // to take effect.
283 //
284 detect_ssl_op<
285 AsyncReadStream,
286 DynamicBuffer,
287 BOOST_ASIO_HANDLER_TYPE(
288 CompletionToken, void(beast::error_code, boost::tribool))>{
289 stream, buffer, init.completion_handler}(beast::error_code{}, 0);
290
291 // This hook lets the caller see a return value when appropriate.
292 // For example this might return std::future<error_code, boost::tribool> if
293 // CompletionToken is boost::asio::use_future.
294 //
295 // If a coroutine is used for the token, the return value from
296 // this function will be the `boost::tribool` representing the result.
297 //
298 return init.result.get();
299 }
300
301 //]
302
303 //[example_core_detect_ssl_6
304
305 // Read from a stream to invoke is_tls_handshake asynchronously
306 //
307 template<
308 class AsyncReadStream,
309 class DynamicBuffer,
310 class Handler>
311 class detect_ssl_op
312 {
313 // This composed operation has trivial state,
314 // so it is just kept inside the class and can
315 // be cheaply copied as needed by the implementation.
316
317 // Indicates what step in the operation's state
318 // machine to perform next, starting from zero.
319 int step_ = 0;
320
321 AsyncReadStream& stream_;
322 DynamicBuffer& buffer_;
323 Handler handler_;
324 boost::tribool result_ = false;
325
326 public:
327 // Boost.Asio requires that handlers are CopyConstructible.
328 // The state for this operation is cheap to copy.
329 detect_ssl_op(detect_ssl_op const&) = default;
330
331 // The constructor just keeps references the callers varaibles.
332 //
333 template<class DeducedHandler>
334 detect_ssl_op(
335 AsyncReadStream& stream,
336 DynamicBuffer& buffer,
337 DeducedHandler&& handler)
338 : stream_(stream)
339 , buffer_(buffer)
340 , handler_(std::forward<DeducedHandler>(handler))
341 {
342 }
343
344 // Associated allocator support. This is Asio's system for
345 // allowing the final completion handler to customize the
346 // memory allocation strategy used for composed operation
347 // states. A composed operation needs to use the same allocator
348 // as the final handler. These declarations achieve that.
349
350 using allocator_type =
351 boost::asio::associated_allocator_t<Handler>;
352
353 allocator_type
354 get_allocator() const noexcept
355 {
356 return boost::asio::get_associated_allocator(handler_);
357 }
358
359 // Executor hook. This is Asio's system for customizing the
360 // manner in which asynchronous completion handlers are invoked.
361 // A composed operation needs to use the same executor to invoke
362 // intermediate completion handlers as that used to invoke the
363 // final handler.
364
365 using executor_type = boost::asio::associated_executor_t<
366 Handler, decltype(std::declval<AsyncReadStream&>().get_executor())>;
367
368 executor_type get_executor() const noexcept
369 {
370 return boost::asio::get_associated_executor(handler_, stream_.get_executor());
371 }
372
373 // Determines if the next asynchronous operation represents a
374 // continuation of the asynchronous flow of control associated
375 // with the final handler. If we are past step two, it means
376 // we have performed an asynchronous operation therefore any
377 // subsequent operation would represent a continuation.
378 // Otherwise, we propagate the handler's associated value of
379 // is_continuation. Getting this right means the implementation
380 // may schedule the invokation of the invoked functions more
381 // efficiently.
382 //
383 friend bool asio_handler_is_continuation(detect_ssl_op* op)
384 {
385 // This next call is structured to permit argument
386 // dependent lookup to take effect.
387 using boost::asio::asio_handler_is_continuation;
388
389 // Always use std::addressof to pass the pointer to the handler,
390 // otherwise an unwanted overload of operator& may be called instead.
391 return op->step_ > 2 ||
392 asio_handler_is_continuation(std::addressof(op->handler_));
393 }
394
395 // Our main entry point. This will get called as our
396 // intermediate operations complete. Definition below.
397 //
398 void operator()(boost::beast::error_code ec, std::size_t bytes_transferred);
399 };
400
401 //]
402
403 //[example_core_detect_ssl_7
404
405 // detect_ssl_op is callable with the signature
406 // void(error_code, bytes_transferred),
407 // allowing `*this` to be used as a ReadHandler
408 //
409 template<
410 class AsyncStream,
411 class DynamicBuffer,
412 class Handler>
413 void
414 detect_ssl_op<AsyncStream, DynamicBuffer, Handler>::
415 operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
416 {
417 namespace beast = boost::beast;
418
419 // Execute the state machine
420 switch(step_)
421 {
422 // Initial state
423 case 0:
424 // See if we can detect the handshake
425 result_ = is_ssl_handshake(buffer_.data());
426
427 // If there's a result, call the handler
428 if(! boost::indeterminate(result_))
429 {
430 // We need to invoke the handler, but the guarantee
431 // is that the handler will not be called before the
432 // call to async_detect_ssl returns, so we must post
433 // the operation to the executor. The helper function
434 // `bind_handler` lets us bind arguments in a safe way
435 // that preserves the type customization hooks of the
436 // original handler.
437 step_ = 1;
438 return boost::asio::post(
439 stream_.get_executor(),
440 beast::bind_handler(std::move(*this), ec, 0));
441 }
442
443 // The algorithm should never need more than 4 bytes
444 BOOST_ASSERT(buffer_.size() < 4);
445
446 step_ = 2;
447
448 do_read:
449 // We need more bytes, but no more than four total.
450 return stream_.async_read_some(buffer_.prepare(beast::read_size(buffer_, 1536)), std::move(*this));
451
452 case 1:
453 // Call the handler
454 break;
455
456 case 2:
457 // Set this so that asio_handler_is_continuation knows that
458 // the next asynchronous operation represents a continuation
459 // of the initial asynchronous operation.
460 step_ = 3;
461 BOOST_FALLTHROUGH;
462
463 case 3:
464 if(ec)
465 {
466 // Deliver the error to the handler
467 result_ = false;
468
469 // We don't need bind_handler here because we were invoked
470 // as a result of an intermediate asynchronous operation.
471 break;
472 }
473
474 // Commit the bytes that we read
475 buffer_.commit(bytes_transferred);
476
477 // See if we can detect the handshake
478 result_ = is_ssl_handshake(buffer_.data());
479
480 // If it is detected, call the handler
481 if(! boost::indeterminate(result_))
482 {
483 // We don't need bind_handler here because we were invoked
484 // as a result of an intermediate asynchronous operation.
485 break;
486 }
487
488 // Read some more
489 goto do_read;
490 }
491
492 // Invoke the final handler.
493 handler_(ec, result_);
494 }
495
496 //]
497
498 #endif