How to get a hostname from connection handle in on_tls_init callback
Hello! I would like to verify TLS certificate. A convenient way of doing that seems to be ctx->set_verify_callback(asio::ssl::rfc2818_verification("host.name"));, but it needs a hostname passed to it.
Handler for set_tls_init_handler has connection_hdl parameter and I would expect to be able to somehow retrieve hostname from it, but I have not succeeded. In particular:
auto wss_connection = client_endpoint_tls.get_con_from_hdl(hdl);
ws_connection->get_uri(); // returns nullptr
ws_connection->get_request().get_uri(); // returns emtpy string
std::shared_ptr<asio::ssl::context> on_tls_init(websocketpp::connection_hdl hdl)
{
auto ctx = std::make_shared<asio::ssl::context>(asio::ssl::context::sslv23);
try {
ctx->set_options(asio::ssl::context::default_workarounds
| asio::ssl::context::no_sslv2
| asio::ssl::context::no_sslv3
| asio::ssl::context::single_dh_use);
ctx->set_verify_mode(asio::ssl::verify_peer);
ctx->set_default_verify_paths();
ctx->set_verify_callback(asio::ssl::rfc2818_verification("ws.ifelse.io"));
} catch (std::exception& e) {
LOG_ERROR("Couldn't initialize ssl context: {}", e.what());
}
return ctx;
}
Add a hostname argument as the first arg in your on_tls_init function.
Extract the hostname from the URL that you pass to m_client.get_connection. Pass that hostname here:
m_client.set_tls_init_handler (bind (&on_tls_init, hostname, _1));
I am calling set_tls_init_handler as a step during tls_client_endpoint initialization. Binding hostname to on_tls_init callback requires that hostname is known at the time when set_tls_init_handler is called and also limits endpoint to be able to connect to only this hostname (at least without calling set_tls_init_handler again).
I would much prefer to be able to reuse a single tls client endpoint to be able to connect to different hostnames. Or is it not an intended way to use client endpoint?
In the code excerpt below, create_connection calls on_tls_init which occurs before the set_uri call in the excerpt below which explains why get_uri returned null during the call to on_tls_init. That seems to contradict the documentation which says:
A new WebSocket connection is initiated via a three step process. First, a connection request is created by endpoint::get_connection(uri). Next, the connection request is configured. Lastly, the connection request is submitted back to the endpoint via endpoint::connect() which adds it to the queue of new connections to make.
That quote says get_connection makes a request, but it seems to imply the request is not executed until the call to endpoint::connect, but since on_tls_init is called at the request creation stage that shows that it did connect.
You said "calling set_tls_init_handler again". You seem reluctant to do that, and it seems strange you would need to do that, but I think that might be a way to solve your problem.
connection_ptr get_connection(uri_ptr location, lib::error_code & ec) {
if (location->get_secure() && !transport_type::is_secure()) {
ec = error::make_error_code(error::endpoint_not_secure);
return connection_ptr();
}
connection_ptr con = endpoint_type::create_connection();
if (!con) {
ec = error::make_error_code(error::con_creation_failed);
return con;
}
con->set_uri(location);
ec = lib::error_code();
return con;
}
That quote says get_connection makes a request, but it seems to imply the request is not executed until the call to endpoint::connect, but since on_tls_init is called at the request creation stage that shows that it did connect.
Yes, so in other words handlers are normally set on the connection object before connect():
auto con = client_endpoint.get_connection(uri, ec);
con->set_open_handler();
con->set_fail_handler();
con->set_close_handler();
client_endpoint.connect(con);
but in case of set_tls_init_handler() there is no point to call this on connection object after client_endpoint.get_connection(uri, ec) (even though connection object has such method) as handler will already have executed. Instead we should set it on the endpoint object right before client_endpoint.get_connection(uri, ec);.