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_SSL_STREAM_HPP
11 #define BOOST_BEAST_CORE_SSL_STREAM_HPP
12
13 #include <boost/beast/core/detail/config.hpp>
14
15 // This include is necessary to work with `ssl::stream` and `boost::beast::websocket::stream`
16 #include <boost/beast/websocket/ssl.hpp>
17
18 #include <boost/beast/core/flat_stream.hpp>
19
20 // VFALCO We include this because anyone who uses ssl will
21 // very likely need to check for ssl::error::stream_truncated
22 #include <boost/asio/ssl/error.hpp>
23
24 #include <boost/asio/ssl/stream.hpp>
25 #include <cstddef>
26 #include <memory>
27 #include <type_traits>
28 #include <utility>
29
30 namespace boost {
31 namespace beast {
32
33 /** Provides stream-oriented functionality using OpenSSL
34
35 The stream class template provides asynchronous and blocking
36 stream-oriented functionality using SSL.
37
38 @par Thread Safety
39 @e Distinct @e objects: Safe.@n
40 @e Shared @e objects: Unsafe. The application must also ensure that all
41 asynchronous operations are performed within the same implicit or explicit
42 strand.
43
44 @par Example
45 To use this template with a @ref tcp_stream, you would write:
46 @code
47 net::io_context ioc;
48 net::ssl::context ctx{net::ssl::context::tlsv12};
49 beast::ssl_stream<beast::tcp_stream> sock{ioc, ctx};
50 @endcode
51
52 In addition to providing an interface identical to `net::ssl::stream`,
53 the wrapper has the following additional properties:
54
55 @li Satisfies @b MoveConstructible
56
57 @li Satisfies @b MoveAssignable
58
59 @li Constructible from a moved socket.
60
61 @li Uses @ref flat_stream internally, as a performance work-around for a
62 limitation of `net::ssl::stream` when writing buffer sequences
63 having length greater than one.
64
65 @par Concepts:
66 @li AsyncReadStream
67 @li AsyncWriteStream
68 @li Stream
69 @li SyncReadStream
70 @li SyncWriteStream
71 */
72 template<class NextLayer>
73 class ssl_stream
74 : public net::ssl::stream_base
75 {
76 using ssl_stream_type = net::ssl::stream<NextLayer>;
77 using stream_type = boost::beast::flat_stream<ssl_stream_type>;
78
79 std::unique_ptr<stream_type> p_;
80
81 public:
82 /// The native handle type of the SSL stream.
83 using native_handle_type =
84 typename ssl_stream_type::native_handle_type;
85
86 /// Structure for use with deprecated impl_type.
87 using impl_struct = typename ssl_stream_type::impl_struct;
88
89 /// The type of the next layer.
90 using next_layer_type = typename ssl_stream_type::next_layer_type;
91
92 /// The type of the executor associated with the object.
93 using executor_type = typename stream_type::executor_type;
94
95 /** Construct a stream.
96
97 This constructor creates a stream and initialises the underlying stream
98 object.
99
100 @param arg The argument to be passed to initialise the underlying stream.
101
102 @param ctx The SSL context to be used for the stream.
103 */
104 template<class Arg>
ssl_stream(Arg && arg,net::ssl::context & ctx)105 ssl_stream(
106 Arg&& arg,
107 net::ssl::context& ctx)
108 : p_(new stream_type{
109 std::forward<Arg>(arg), ctx})
110 {
111 }
112
113 /** Get the executor associated with the object.
114
115 This function may be used to obtain the executor object that the stream
116 uses to dispatch handlers for asynchronous operations.
117
118 @return A copy of the executor that stream will use to dispatch handlers.
119 */
120 executor_type
get_executor()121 get_executor() noexcept
122 {
123 return p_->get_executor();
124 }
125
126 /** Get the underlying implementation in the native type.
127
128 This function may be used to obtain the underlying implementation of the
129 context. This is intended to allow access to context functionality that is
130 not otherwise provided.
131
132 @par Example
133 The native_handle() function returns a pointer of type @c SSL* that is
134 suitable for passing to functions such as @c SSL_get_verify_result and
135 @c SSL_get_peer_certificate:
136 @code
137 boost::beast::ssl_stream<net::ip::tcp::socket> ss{ioc, ctx};
138
139 // ... establish connection and perform handshake ...
140
141 if (X509* cert = SSL_get_peer_certificate(ss.native_handle()))
142 {
143 if (SSL_get_verify_result(ss.native_handle()) == X509_V_OK)
144 {
145 // ...
146 }
147 }
148 @endcode
149 */
150 native_handle_type
native_handle()151 native_handle() noexcept
152 {
153 return p_->next_layer().native_handle();
154 }
155
156 /** Get a reference to the next layer.
157
158 This function returns a reference to the next layer in a stack of stream
159 layers.
160
161 @note The next layer is the wrapped stream and not the @ref flat_stream
162 used in the implementation.
163
164 @return A reference to the next layer in the stack of stream layers.
165 Ownership is not transferred to the caller.
166 */
167 next_layer_type const&
next_layer() const168 next_layer() const noexcept
169 {
170 return p_->next_layer().next_layer();
171 }
172
173 /** Get a reference to the next layer.
174
175 This function returns a reference to the next layer in a stack of stream
176 layers.
177
178 @note The next layer is the wrapped stream and not the @ref flat_stream
179 used in the implementation.
180
181 @return A reference to the next layer in the stack of stream layers.
182 Ownership is not transferred to the caller.
183 */
184 next_layer_type&
next_layer()185 next_layer() noexcept
186 {
187 return p_->next_layer().next_layer();
188 }
189
190 /** Set the peer verification mode.
191
192 This function may be used to configure the peer verification mode used by
193 the stream. The new mode will override the mode inherited from the context.
194
195 @param v A bitmask of peer verification modes.
196
197 @throws boost::system::system_error Thrown on failure.
198
199 @note Calls @c SSL_set_verify.
200 */
201 void
set_verify_mode(net::ssl::verify_mode v)202 set_verify_mode(net::ssl::verify_mode v)
203 {
204 p_->next_layer().set_verify_mode(v);
205 }
206
207 /** Set the peer verification mode.
208
209 This function may be used to configure the peer verification mode used by
210 the stream. The new mode will override the mode inherited from the context.
211
212 @param v A bitmask of peer verification modes. See `verify_mode` for
213 available values.
214
215 @param ec Set to indicate what error occurred, if any.
216
217 @note Calls @c SSL_set_verify.
218 */
219 void
set_verify_mode(net::ssl::verify_mode v,boost::system::error_code & ec)220 set_verify_mode(net::ssl::verify_mode v,
221 boost::system::error_code& ec)
222 {
223 p_->next_layer().set_verify_mode(v, ec);
224 }
225
226 /** Set the peer verification depth.
227
228 This function may be used to configure the maximum verification depth
229 allowed by the stream.
230
231 @param depth Maximum depth for the certificate chain verification that
232 shall be allowed.
233
234 @throws boost::system::system_error Thrown on failure.
235
236 @note Calls @c SSL_set_verify_depth.
237 */
238 void
set_verify_depth(int depth)239 set_verify_depth(int depth)
240 {
241 p_->next_layer().set_verify_depth(depth);
242 }
243
244 /** Set the peer verification depth.
245
246 This function may be used to configure the maximum verification depth
247 allowed by the stream.
248
249 @param depth Maximum depth for the certificate chain verification that
250 shall be allowed.
251
252 @param ec Set to indicate what error occurred, if any.
253
254 @note Calls @c SSL_set_verify_depth.
255 */
256 void
set_verify_depth(int depth,boost::system::error_code & ec)257 set_verify_depth(
258 int depth, boost::system::error_code& ec)
259 {
260 p_->next_layer().set_verify_depth(depth, ec);
261 }
262
263 /** Set the callback used to verify peer certificates.
264
265 This function is used to specify a callback function that will be called
266 by the implementation when it needs to verify a peer certificate.
267
268 @param callback The function object to be used for verifying a certificate.
269 The function signature of the handler must be:
270 @code bool verify_callback(
271 bool preverified, // True if the certificate passed pre-verification.
272 verify_context& ctx // The peer certificate and other context.
273 ); @endcode
274 The return value of the callback is true if the certificate has passed
275 verification, false otherwise.
276
277 @throws boost::system::system_error Thrown on failure.
278
279 @note Calls @c SSL_set_verify.
280 */
281 template<class VerifyCallback>
282 void
set_verify_callback(VerifyCallback callback)283 set_verify_callback(VerifyCallback callback)
284 {
285 p_->next_layer().set_verify_callback(callback);
286 }
287
288 /** Set the callback used to verify peer certificates.
289
290 This function is used to specify a callback function that will be called
291 by the implementation when it needs to verify a peer certificate.
292
293 @param callback The function object to be used for verifying a certificate.
294 The function signature of the handler must be:
295 @code bool verify_callback(
296 bool preverified, // True if the certificate passed pre-verification.
297 net::verify_context& ctx // The peer certificate and other context.
298 ); @endcode
299 The return value of the callback is true if the certificate has passed
300 verification, false otherwise.
301
302 @param ec Set to indicate what error occurred, if any.
303
304 @note Calls @c SSL_set_verify.
305 */
306 template<class VerifyCallback>
307 void
set_verify_callback(VerifyCallback callback,boost::system::error_code & ec)308 set_verify_callback(VerifyCallback callback,
309 boost::system::error_code& ec)
310 {
311 p_->next_layer().set_verify_callback(callback, ec);
312 }
313
314 /** Perform SSL handshaking.
315
316 This function is used to perform SSL handshaking on the stream. The
317 function call will block until handshaking is complete or an error occurs.
318
319 @param type The type of handshaking to be performed, i.e. as a client or as
320 a server.
321
322 @throws boost::system::system_error Thrown on failure.
323 */
324 void
handshake(handshake_type type)325 handshake(handshake_type type)
326 {
327 p_->next_layer().handshake(type);
328 }
329
330 /** Perform SSL handshaking.
331
332 This function is used to perform SSL handshaking on the stream. The
333 function call will block until handshaking is complete or an error occurs.
334
335 @param type The type of handshaking to be performed, i.e. as a client or as
336 a server.
337
338 @param ec Set to indicate what error occurred, if any.
339 */
340 void
handshake(handshake_type type,boost::system::error_code & ec)341 handshake(handshake_type type,
342 boost::system::error_code& ec)
343 {
344 p_->next_layer().handshake(type, ec);
345 }
346
347 /** Perform SSL handshaking.
348
349 This function is used to perform SSL handshaking on the stream. The
350 function call will block until handshaking is complete or an error occurs.
351
352 @param type The type of handshaking to be performed, i.e. as a client or as
353 a server.
354
355 @param buffers The buffered data to be reused for the handshake.
356
357 @throws boost::system::system_error Thrown on failure.
358 */
359 template<class ConstBufferSequence>
360 void
handshake(handshake_type type,ConstBufferSequence const & buffers)361 handshake(
362 handshake_type type, ConstBufferSequence const& buffers)
363 {
364 p_->next_layer().handshake(type, buffers);
365 }
366
367 /** Perform SSL handshaking.
368
369 This function is used to perform SSL handshaking on the stream. The
370 function call will block until handshaking is complete or an error occurs.
371
372 @param type The type of handshaking to be performed, i.e. as a client or as
373 a server.
374
375 @param buffers The buffered data to be reused for the handshake.
376
377 @param ec Set to indicate what error occurred, if any.
378 */
379 template<class ConstBufferSequence>
380 void
handshake(handshake_type type,ConstBufferSequence const & buffers,boost::system::error_code & ec)381 handshake(handshake_type type,
382 ConstBufferSequence const& buffers,
383 boost::system::error_code& ec)
384 {
385 p_->next_layer().handshake(type, buffers, ec);
386 }
387
388 /** Start an asynchronous SSL handshake.
389
390 This function is used to asynchronously perform an SSL handshake on the
391 stream. This function call always returns immediately.
392
393 @param type The type of handshaking to be performed, i.e. as a client or as
394 a server.
395
396 @param handler The handler to be called when the handshake operation
397 completes. Copies will be made of the handler as required. The equivalent
398 function signature of the handler must be:
399 @code void handler(
400 const boost::system::error_code& error // Result of operation.
401 ); @endcode
402 */
403 template<class HandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler,void (boost::system::error_code))404 BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler, void(boost::system::error_code))
405 async_handshake(handshake_type type,
406 BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler)
407 {
408 return p_->next_layer().async_handshake(type,
409 BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler));
410 }
411
412 /** Start an asynchronous SSL handshake.
413
414 This function is used to asynchronously perform an SSL handshake on the
415 stream. This function call always returns immediately.
416
417 @param type The type of handshaking to be performed, i.e. as a client or as
418 a server.
419
420 @param buffers The buffered data to be reused for the handshake. Although
421 the buffers object may be copied as necessary, ownership of the underlying
422 buffers is retained by the caller, which must guarantee that they remain
423 valid until the handler is called.
424
425 @param handler The handler to be called when the handshake operation
426 completes. Copies will be made of the handler as required. The equivalent
427 function signature of the handler must be:
428 @code void handler(
429 const boost::system::error_code& error, // Result of operation.
430 std::size_t bytes_transferred // Amount of buffers used in handshake.
431 ); @endcode
432 */
433 template<class ConstBufferSequence, class BufferedHandshakeHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,void (boost::system::error_code,std::size_t))434 BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void(boost::system::error_code, std::size_t))
435 async_handshake(handshake_type type, ConstBufferSequence const& buffers,
436 BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler)
437 {
438 return p_->next_layer().async_handshake(type, buffers,
439 BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler));
440 }
441
442 /** Shut down SSL on the stream.
443
444 This function is used to shut down SSL on the stream. The function call
445 will block until SSL has been shut down or an error occurs.
446
447 @throws boost::system::system_error Thrown on failure.
448 */
449 void
shutdown()450 shutdown()
451 {
452 p_->next_layer().shutdown();
453 }
454
455 /** Shut down SSL on the stream.
456
457 This function is used to shut down SSL on the stream. The function call
458 will block until SSL has been shut down or an error occurs.
459
460 @param ec Set to indicate what error occurred, if any.
461 */
462 void
shutdown(boost::system::error_code & ec)463 shutdown(boost::system::error_code& ec)
464 {
465 p_->next_layer().shutdown(ec);
466 }
467
468 /** Asynchronously shut down SSL on the stream.
469
470 This function is used to asynchronously shut down SSL on the stream. This
471 function call always returns immediately.
472
473 @param handler The handler to be called when the handshake operation
474 completes. Copies will be made of the handler as required. The equivalent
475 function signature of the handler must be:
476 @code void handler(
477 const boost::system::error_code& error // Result of operation.
478 ); @endcode
479 */
480 template<class ShutdownHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ShutdownHandler,void (boost::system::error_code))481 BOOST_ASIO_INITFN_RESULT_TYPE(ShutdownHandler, void(boost::system::error_code))
482 async_shutdown(BOOST_ASIO_MOVE_ARG(ShutdownHandler) handler)
483 {
484 return p_->next_layer().async_shutdown(
485 BOOST_ASIO_MOVE_CAST(ShutdownHandler)(handler));
486 }
487
488 /** Write some data to the stream.
489
490 This function is used to write data on the stream. The function call will
491 block until one or more bytes of data has been written successfully, or
492 until an error occurs.
493
494 @param buffers The data to be written.
495
496 @returns The number of bytes written.
497
498 @throws boost::system::system_error Thrown on failure.
499
500 @note The `write_some` operation may not transmit all of the data to the
501 peer. Consider using the `net::write` function if you need to
502 ensure that all data is written before the blocking operation completes.
503 */
504 template<class ConstBufferSequence>
505 std::size_t
write_some(ConstBufferSequence const & buffers)506 write_some(ConstBufferSequence const& buffers)
507 {
508 return p_->write_some(buffers);
509 }
510
511 /** Write some data to the stream.
512
513 This function is used to write data on the stream. The function call will
514 block until one or more bytes of data has been written successfully, or
515 until an error occurs.
516
517 @param buffers The data to be written to the stream.
518
519 @param ec Set to indicate what error occurred, if any.
520
521 @returns The number of bytes written. Returns 0 if an error occurred.
522
523 @note The `write_some` operation may not transmit all of the data to the
524 peer. Consider using the `net::write` function if you need to
525 ensure that all data is written before the blocking operation completes.
526 */
527 template<class ConstBufferSequence>
528 std::size_t
write_some(ConstBufferSequence const & buffers,boost::system::error_code & ec)529 write_some(ConstBufferSequence const& buffers,
530 boost::system::error_code& ec)
531 {
532 return p_->write_some(buffers, ec);
533 }
534
535 /** Start an asynchronous write.
536
537 This function is used to asynchronously write one or more bytes of data to
538 the stream. The function call always returns immediately.
539
540 @param buffers The data to be written to the stream. Although the buffers
541 object may be copied as necessary, ownership of the underlying buffers is
542 retained by the caller, which must guarantee that they remain valid until
543 the handler is called.
544
545 @param handler The handler to be called when the write operation completes.
546 Copies will be made of the handler as required. The equivalent function
547 signature of the handler must be:
548 @code void handler(
549 const boost::system::error_code& error, // Result of operation.
550 std::size_t bytes_transferred // Number of bytes written.
551 ); @endcode
552
553 @note The `async_write_some` operation may not transmit all of the data to
554 the peer. Consider using the `net::async_write` function if you
555 need to ensure that all data is written before the asynchronous operation
556 completes.
557 */
558 template<class ConstBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,void (boost::system::error_code,std::size_t))559 BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, void(boost::system::error_code, std::size_t))
560 async_write_some(ConstBufferSequence const& buffers,
561 BOOST_ASIO_MOVE_ARG(WriteHandler) handler)
562 {
563 return p_->async_write_some(buffers,
564 BOOST_ASIO_MOVE_CAST(WriteHandler)(handler));
565 }
566
567 /** Read some data from the stream.
568
569 This function is used to read data from the stream. The function call will
570 block until one or more bytes of data has been read successfully, or until
571 an error occurs.
572
573 @param buffers The buffers into which the data will be read.
574
575 @returns The number of bytes read.
576
577 @throws boost::system::system_error Thrown on failure.
578
579 @note The `read_some` operation may not read all of the requested number of
580 bytes. Consider using the `net::read` function if you need to ensure
581 that the requested amount of data is read before the blocking operation
582 completes.
583 */
584 template<class MutableBufferSequence>
585 std::size_t
read_some(MutableBufferSequence const & buffers)586 read_some(MutableBufferSequence const& buffers)
587 {
588 return p_->read_some(buffers);
589 }
590
591 /** Read some data from the stream.
592
593 This function is used to read data from the stream. The function call will
594 block until one or more bytes of data has been read successfully, or until
595 an error occurs.
596
597 @param buffers The buffers into which the data will be read.
598
599 @param ec Set to indicate what error occurred, if any.
600
601 @returns The number of bytes read. Returns 0 if an error occurred.
602
603 @note The `read_some` operation may not read all of the requested number of
604 bytes. Consider using the `net::read` function if you need to ensure
605 that the requested amount of data is read before the blocking operation
606 completes.
607 */
608 template<class MutableBufferSequence>
609 std::size_t
read_some(MutableBufferSequence const & buffers,boost::system::error_code & ec)610 read_some(MutableBufferSequence const& buffers,
611 boost::system::error_code& ec)
612 {
613 return p_->read_some(buffers, ec);
614 }
615
616 /** Start an asynchronous read.
617
618 This function is used to asynchronously read one or more bytes of data from
619 the stream. The function call always returns immediately.
620
621 @param buffers The buffers into which the data will be read. Although the
622 buffers object may be copied as necessary, ownership of the underlying
623 buffers is retained by the caller, which must guarantee that they remain
624 valid until the handler is called.
625
626 @param handler The handler to be called when the read operation completes.
627 Copies will be made of the handler as required. The equivalent function
628 signature of the handler must be:
629 @code void handler(
630 const boost::system::error_code& error, // Result of operation.
631 std::size_t bytes_transferred // Number of bytes read.
632 ); @endcode
633
634 @note The `async_read_some` operation may not read all of the requested
635 number of bytes. Consider using the `net::async_read` function
636 if you need to ensure that the requested amount of data is read before
637 the asynchronous operation completes.
638 */
639 template<class MutableBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,void (boost::system::error_code,std::size_t))640 BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, void(boost::system::error_code, std::size_t))
641 async_read_some(MutableBufferSequence const& buffers,
642 BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
643 {
644 return p_->async_read_some(buffers,
645 BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
646 }
647
648 // These hooks are used to inform boost::beast::websocket::stream on
649 // how to tear down the connection as part of the WebSocket
650 // protocol specifications
651 #if ! BOOST_BEAST_DOXYGEN
652 template<class SyncStream>
653 friend
654 void
655 teardown(
656 boost::beast::role_type role,
657 ssl_stream<SyncStream>& stream,
658 boost::system::error_code& ec);
659
660 template<class AsyncStream, class TeardownHandler>
661 friend
662 void
663 async_teardown(
664 boost::beast::role_type role,
665 ssl_stream<AsyncStream>& stream,
666 TeardownHandler&& handler);
667 #endif
668 };
669
670 #if ! BOOST_BEAST_DOXYGEN
671 template<class SyncStream>
672 void
teardown(boost::beast::role_type role,ssl_stream<SyncStream> & stream,boost::system::error_code & ec)673 teardown(
674 boost::beast::role_type role,
675 ssl_stream<SyncStream>& stream,
676 boost::system::error_code& ec)
677 {
678 // Just forward it to the underlying ssl::stream
679 using boost::beast::websocket::teardown;
680 teardown(role, *stream.p_, ec);
681 }
682
683 template<class AsyncStream, class TeardownHandler>
684 void
async_teardown(boost::beast::role_type role,ssl_stream<AsyncStream> & stream,TeardownHandler && handler)685 async_teardown(
686 boost::beast::role_type role,
687 ssl_stream<AsyncStream>& stream,
688 TeardownHandler&& handler)
689 {
690 // Just forward it to the underlying ssl::stream
691 using boost::beast::websocket::async_teardown;
692 async_teardown(role, *stream.p_,
693 std::forward<TeardownHandler>(handler));
694 }
695 #endif
696
697 } // beast
698 } // boost
699
700 #endif
701