WebSocket++ 0.8.4
C++ websocket client/server library
Loading...
Searching...
No Matches
tls.hpp
1/*
2 * Copyright (c) 2015, Peter Thorson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * Neither the name of the WebSocket++ Project nor the
12 * names of its contributors may be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28#ifndef WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
29#define WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
30
31#include <websocketpp/transport/asio/security/base.hpp>
32
33#include <websocketpp/uri.hpp>
34
35#include <websocketpp/common/asio_ssl.hpp>
36#include <websocketpp/common/asio.hpp>
37#include <websocketpp/common/connection_hdl.hpp>
38#include <websocketpp/common/functional.hpp>
39#include <websocketpp/common/memory.hpp>
40
41#include <sstream>
42#include <string>
43
44namespace websocketpp {
45namespace transport {
46namespace asio {
47/// A socket policy for the asio transport that implements a TLS encrypted
48/// socket by wrapping with an asio::ssl::stream
49namespace tls_socket {
50
51/// The signature of the socket_init_handler for this socket policy
52typedef lib::function<void(connection_hdl,lib::asio::ssl::stream<
53 lib::asio::ip::tcp::socket>&)> socket_init_handler;
54/// The signature of the tls_init_handler for this socket policy
55typedef lib::function<lib::shared_ptr<lib::asio::ssl::context>(connection_hdl)>
57
58/// TLS enabled Asio connection socket component
59/**
60 * transport::asio::tls_socket::connection implements a secure connection socket
61 * component that uses Asio's ssl::stream to wrap an ip::tcp::socket.
62 */
64public:
65 /// Type of this connection socket component
67 /// Type of a shared pointer to this connection socket component
68 typedef lib::shared_ptr<type> ptr;
69
70 /// Type of the ASIO socket being used
71 typedef lib::asio::ssl::stream<lib::asio::ip::tcp::socket> socket_type;
72 /// Type of a shared pointer to the ASIO socket being used
73 typedef lib::shared_ptr<socket_type> socket_ptr;
74 /// Type of a pointer to the ASIO io_context being used
75 typedef lib::asio::io_context * io_context_ptr;
76 /// Type of a pointer to the ASIO io_context being used (deprecated)
77 /**
78 * @deprecated Use io_context_ptr instead
79 */
81 /// Type of a pointer to the ASIO io_context strand being used
82 typedef lib::shared_ptr<lib::asio::io_context::strand> strand_ptr;
83 /// Type of a shared pointer to the ASIO TLS context being used
84 typedef lib::shared_ptr<lib::asio::ssl::context> context_ptr;
85
86 explicit connection() {
87 //std::cout << "transport::asio::tls_socket::connection constructor"
88 // << std::endl;
89 }
90
91 /// Get a shared pointer to this component
93 return shared_from_this();
94 }
95
96 /// Check whether or not this connection is secure
97 /**
98 * @return Whether or not this connection is secure
99 */
100 bool is_secure() const {
101 return true;
102 }
103
104 /// Retrieve a pointer to the underlying socket
105 /**
106 * This is used internally. It can also be used to set socket options, etc
107 */
109 return m_socket->lowest_layer();
110 }
111
112 /// Retrieve a pointer to the layer below the ssl stream
113 /**
114 * This is used internally.
115 */
117 return m_socket->next_layer();
118 }
119
120 /// Retrieve a pointer to the wrapped socket
121 /**
122 * This is used internally.
123 */
125 return *m_socket;
126 }
127
128 /// Set the socket initialization handler
129 /**
130 * The socket initialization handler is called after the socket object is
131 * created but before it is used. This gives the application a chance to
132 * set any ASIO socket options it needs.
133 *
134 * @param h The new socket_init_handler
135 */
137 m_socket_init_handler = h;
138 }
139
140 /// Set TLS init handler
141 /**
142 * The tls init handler is called when needed to request a TLS context for
143 * the library to use. A TLS init handler must be set and it must return a
144 * valid TLS context in order for this endpoint to be able to initialize
145 * TLS connections
146 *
147 * @param h The new tls_init_handler
148 */
150 m_tls_init_handler = h;
151 }
152
153 /// Get the remote endpoint address
154 /**
155 * The iostream transport has no information about the ultimate remote
156 * endpoint. It will return the string "iostream transport". To indicate
157 * this.
158 *
159 * TODO: allow user settable remote endpoint addresses if this seems useful
160 *
161 * @return A string identifying the address of the remote endpoint
162 */
163 std::string get_remote_endpoint(lib::error_code & ec) const {
164 std::stringstream s;
165
166 lib::asio::error_code aec;
167 lib::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(aec);
168
169 if (aec) {
170 ec = error::make_error_code(error::pass_through);
171 s << "Error getting remote endpoint: " << aec
172 << " (" << aec.message() << ")";
173 return s.str();
174 } else {
175 ec = lib::error_code();
176 s << ep;
177 return s.str();
178 }
179 }
180protected:
181 /// Perform one time initializations
182 /**
183 * init_asio is called once immediately after construction to initialize
184 * Asio components to the io_context
185 *
186 * @param context A pointer to the endpoint's io_context
187 * @param strand A pointer to the connection's strand
188 * @param is_server Whether or not the endpoint is a server or not.
189 */
190 lib::error_code init_asio (io_context_ptr context, strand_ptr strand,
191 bool is_server)
192 {
193 if (!m_tls_init_handler) {
194 return socket::make_error_code(socket::error::missing_tls_init_handler);
195 }
196 m_context = m_tls_init_handler(m_hdl);
197
198 if (!m_context) {
199 return socket::make_error_code(socket::error::invalid_tls_context);
200 }
201 m_socket.reset(new socket_type(*context, *m_context));
202
203 m_io_context = context;
204 m_strand = strand;
205 m_is_server = is_server;
206
207 return lib::error_code();
208 }
209
210 /// Set hostname hook
211 /**
212 * Called by the transport as a connection is being established to provide
213 * the hostname being connected to to the security/socket layer.
214 *
215 * This socket policy uses the hostname to set the appropriate TLS SNI
216 * header.
217 *
218 * @since 0.6.0
219 *
220 * @param u The uri to set
221 */
222 void set_uri(uri_ptr u) {
223 m_uri = u;
224 }
225
226 /// Pre-initialize security policy
227 /**
228 * Called by the transport after a new connection is created to initialize
229 * the socket component of the connection. This method is not allowed to
230 * write any bytes to the wire. This initialization happens before any
231 * proxies or other intermediate wrappers are negotiated.
232 *
233 * @param callback Handler to call back with completion information
234 */
235 void pre_init(init_handler callback) {
236 // TODO: is this the best way to check whether this function is
237 // available in the version of OpenSSL being used?
238#if OPENSSL_VERSION_NUMBER >= 0x90812f
239 if (!m_is_server) {
240 // For clients on systems with a suitable OpenSSL version, set the
241 // TLS SNI hostname header so connecting to TLS servers using SNI
242 // will work.
243 std::string const & host = m_uri->get_host();
244 lib::asio::error_code ec_addr;
245
246 // run the hostname through make_address to check if it is a valid IP literal
247 lib::asio::ip::address addr = lib::asio::ip::make_address(host, ec_addr);
248 (void)addr;
249
250 // If the parsing as an IP literal fails, proceed to register the hostname
251 // with the TLS handshake via SNI.
252 // The SNI applies only to DNS host names, not for IP addresses
253 // See RFC3546 Section 3.1
254 if (ec_addr) {
255 long res = SSL_set_tlsext_host_name(
256 get_socket().native_handle(), host.c_str());
257 if (!(1 == res)) {
258 callback(socket::make_error_code(socket::error::tls_failed_sni_hostname));
259 }
260 }
261 }
262#endif
263 if (m_socket_init_handler) {
264 m_socket_init_handler(m_hdl, get_socket());
265 }
266
267 callback(lib::error_code());
268 }
269
270 /// Post-initialize security policy
271 /**
272 * Called by the transport after all intermediate proxies have been
273 * negotiated. This gives the security policy the chance to talk with the
274 * real remote endpoint for a bit before the websocket handshake.
275 *
276 * @param callback Handler to call back with completion information
277 */
278 void post_init(init_handler callback) {
279 m_ec = socket::make_error_code(socket::error::tls_handshake_timeout);
280
281 // TLS handshake
282 if (m_strand) {
283 m_socket->async_handshake(
284 get_handshake_type(),
285 lib::asio::bind_executor(*m_strand, lib::bind(
286 &type::handle_init, get_shared(),
287 callback,
288 lib::placeholders::_1
289 ))
290 );
291 } else {
292 m_socket->async_handshake(
293 get_handshake_type(),
294 lib::bind(
295 &type::handle_init, get_shared(),
296 callback,
297 lib::placeholders::_1
298 )
299 );
300 }
301 }
302
303 /// Sets the connection handle
304 /**
305 * The connection handle is passed to any handlers to identify the
306 * connection
307 *
308 * @param hdl The new handle
309 */
311 m_hdl = hdl;
312 }
313
314 void handle_init(init_handler callback,lib::asio::error_code const & ec) {
315 if (ec) {
316 m_ec = socket::make_error_code(socket::error::tls_handshake_failed);
317 } else {
318 m_ec = lib::error_code();
319 }
320
321 callback(m_ec);
322 }
323
324 lib::error_code get_ec() const {
325 return m_ec;
326 }
327
328 /// Cancel all async operations on this socket
329 /**
330 * Attempts to cancel all async operations on this socket and reports any
331 * failures.
332 *
333 * NOTE: Windows XP and earlier do not support socket cancellation.
334 *
335 * @return The error that occurred, if any.
336 */
337 lib::asio::error_code cancel_socket() {
338 lib::asio::error_code ec;
339 get_raw_socket().cancel(ec);
340 return ec;
341 }
342
343 void async_shutdown(socket::shutdown_handler callback) {
344 if (m_strand) {
345 m_socket->async_shutdown(lib::asio::bind_executor(*m_strand, callback));
346 } else {
347 m_socket->async_shutdown(callback);
348 }
349 }
350
351public:
352 /// Translate any security policy specific information about an error code
353 /**
354 * Translate_ec takes an Asio error code and attempts to convert its value
355 * to an appropriate websocketpp error code. In the case that the Asio and
356 * Websocketpp error types are the same (such as using boost::asio and
357 * boost::system_error or using standalone asio and std::system_error the
358 * code will be passed through natively.
359 *
360 * In the case of a mismatch (boost::asio with std::system_error) a
361 * translated code will be returned. Any error that is determined to be
362 * related to TLS but does not have a more specific websocketpp error code
363 * is returned under the catch all error `tls_error`. Non-TLS related errors
364 * are returned as the transport generic error `pass_through`
365 *
366 * @since 0.3.0
367 *
368 * @param ec The error code to translate_ec
369 * @return The translated error code
370 */
371 template <typename ErrorCodeType>
372 static
373 lib::error_code translate_ec(ErrorCodeType ec) {
374 if (ec.category() == lib::asio::error::get_ssl_category()) {
375 // We know it is a TLS related error, but otherwise don't know more.
376 // Pass through as TLS generic.
377 return make_error_code(transport::error::tls_error);
378 } else {
379 // We don't know any more information about this error so pass
380 // through
381 return make_error_code(transport::error::pass_through);
382 }
383 }
384
385 static
386 /// Overload of translate_ec to catch cases where lib::error_code is the
387 /// same type as lib::asio::error_code
388 lib::error_code translate_ec(lib::error_code ec) {
389 return ec;
390 }
391private:
392 socket_type::handshake_type get_handshake_type() {
393 if (m_is_server) {
394 return lib::asio::ssl::stream_base::server;
395 } else {
396 return lib::asio::ssl::stream_base::client;
397 }
398 }
399
400 io_context_ptr m_io_context;
401 strand_ptr m_strand;
402 context_ptr m_context;
403 socket_ptr m_socket;
404 uri_ptr m_uri;
405 bool m_is_server;
406
407 lib::error_code m_ec;
408
409 connection_hdl m_hdl;
410 socket_init_handler m_socket_init_handler;
411 tls_init_handler m_tls_init_handler;
412};
413
414/// TLS enabled Asio endpoint socket component
415/**
416 * transport::asio::tls_socket::endpoint implements a secure endpoint socket
417 * component that uses Asio's ssl::stream to wrap an ip::tcp::socket.
418 */
419class endpoint {
420public:
421 /// The type of this endpoint socket component
422 typedef endpoint type;
423
424 /// The type of the corresponding connection socket component
426 /// The type of a shared pointer to the corresponding connection socket
427 /// component.
429
430 explicit endpoint() {}
431
432 /// Checks whether the endpoint creates secure connections
433 /**
434 * @return Whether or not the endpoint creates secure connections
435 */
436 bool is_secure() const {
437 return true;
438 }
439
440 /// Set socket init handler
441 /**
442 * The socket init handler is called after a connection's socket is created
443 * but before it is used. This gives the end application an opportunity to
444 * set asio socket specific parameters.
445 *
446 * @param h The new socket_init_handler
447 */
449 m_socket_init_handler = h;
450 }
451
452 /// Set TLS init handler
453 /**
454 * The tls init handler is called when needed to request a TLS context for
455 * the library to use. A TLS init handler must be set and it must return a
456 * valid TLS context in order for this endpoint to be able to initialize
457 * TLS connections
458 *
459 * @param h The new tls_init_handler
460 */
462 m_tls_init_handler = h;
463 }
464protected:
465 /// Initialize a connection
466 /**
467 * Called by the transport after a new connection is created to initialize
468 * the socket component of the connection.
469 *
470 * @param scon Pointer to the socket component of the connection
471 *
472 * @return Error code (empty on success)
473 */
474 lib::error_code init(socket_con_ptr scon) {
475 scon->set_socket_init_handler(m_socket_init_handler);
476 scon->set_tls_init_handler(m_tls_init_handler);
477 return lib::error_code();
478 }
479
480private:
481 socket_init_handler m_socket_init_handler;
482 tls_init_handler m_tls_init_handler;
483};
484
485} // namespace tls_socket
486} // namespace asio
487} // namespace transport
488} // namespace websocketpp
489
490#endif // WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
TLS enabled Asio connection socket component.
Definition tls.hpp:63
bool is_secure() const
Check whether or not this connection is secure.
Definition tls.hpp:100
connection type
Type of this connection socket component.
Definition tls.hpp:66
void pre_init(init_handler callback)
Pre-initialize security policy.
Definition tls.hpp:235
std::string get_remote_endpoint(lib::error_code &ec) const
Get the remote endpoint address.
Definition tls.hpp:163
void set_handle(connection_hdl hdl)
Sets the connection handle.
Definition tls.hpp:310
lib::shared_ptr< lib::asio::ssl::context > context_ptr
Type of a shared pointer to the ASIO TLS context being used.
Definition tls.hpp:84
lib::shared_ptr< type > ptr
Type of a shared pointer to this connection socket component.
Definition tls.hpp:68
io_context_ptr io_service_ptr
Type of a pointer to the ASIO io_context being used (deprecated).
Definition tls.hpp:80
lib::shared_ptr< socket_type > socket_ptr
Type of a shared pointer to the ASIO socket being used.
Definition tls.hpp:73
socket_type & get_socket()
Retrieve a pointer to the wrapped socket.
Definition tls.hpp:124
static lib::error_code translate_ec(lib::error_code ec)
Definition tls.hpp:388
lib::error_code init_asio(io_context_ptr context, strand_ptr strand, bool is_server)
Perform one time initializations.
Definition tls.hpp:190
socket_type::lowest_layer_type & get_raw_socket()
Retrieve a pointer to the underlying socket.
Definition tls.hpp:108
void set_socket_init_handler(socket_init_handler h)
Set the socket initialization handler.
Definition tls.hpp:136
void set_tls_init_handler(tls_init_handler h)
Set TLS init handler.
Definition tls.hpp:149
void set_uri(uri_ptr u)
Set hostname hook.
Definition tls.hpp:222
lib::asio::ssl::stream< lib::asio::ip::tcp::socket > socket_type
Type of the ASIO socket being used.
Definition tls.hpp:71
static lib::error_code translate_ec(ErrorCodeType ec)
Translate any security policy specific information about an error code.
Definition tls.hpp:373
void post_init(init_handler callback)
Post-initialize security policy.
Definition tls.hpp:278
ptr get_shared()
Get a shared pointer to this component.
Definition tls.hpp:92
socket_type::next_layer_type & get_next_layer()
Retrieve a pointer to the layer below the ssl stream.
Definition tls.hpp:116
lib::asio::io_context * io_context_ptr
Type of a pointer to the ASIO io_context being used.
Definition tls.hpp:75
lib::shared_ptr< lib::asio::io_context::strand > strand_ptr
Type of a pointer to the ASIO io_context strand being used.
Definition tls.hpp:82
lib::asio::error_code cancel_socket()
Cancel all async operations on this socket.
Definition tls.hpp:337
TLS enabled Asio endpoint socket component.
Definition tls.hpp:419
lib::error_code init(socket_con_ptr scon)
Initialize a connection.
Definition tls.hpp:474
connection socket_con_type
The type of the corresponding connection socket component.
Definition tls.hpp:425
void set_tls_init_handler(tls_init_handler h)
Set TLS init handler.
Definition tls.hpp:461
bool is_secure() const
Checks whether the endpoint creates secure connections.
Definition tls.hpp:436
endpoint type
The type of this endpoint socket component.
Definition tls.hpp:422
void set_socket_init_handler(socket_init_handler h)
Set socket init handler.
Definition tls.hpp:448
Library level error codes.
Definition error.hpp:44
Errors related to asio transport sockets.
Definition base.hpp:75
@ missing_tls_init_handler
Required tls_init handler not present.
Definition base.hpp:99
@ tls_failed_sni_hostname
Failed to set TLS SNI hostname.
Definition base.hpp:105
@ tls_handshake_failed
TLS Handshake Failed.
Definition base.hpp:102
@ tls_handshake_timeout
TLS Handshake Timeout.
Definition base.hpp:93
lib::function< void(connection_hdl, lib::asio::ssl::stream< lib::asio::ip::tcp::socket > &)> socket_init_handler
The signature of the socket_init_handler for this socket policy.
Definition tls.hpp:53
lib::function< lib::shared_ptr< lib::asio::ssl::context >(connection_hdl)> tls_init_handler
The signature of the tls_init_handler for this socket policy.
Definition tls.hpp:56
Transport policy that uses asio.
Definition endpoint.hpp:46
Transport policies provide network connectivity and timers.
Definition endpoint.hpp:45
Namespace for the WebSocket++ project.
lib::weak_ptr< void > connection_hdl
A handle to uniquely identify a connection.
lib::shared_ptr< uri > uri_ptr
Pointer to a URI.
Definition uri.hpp:796