• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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