From c8ffcf055771d8abb5abbba392c826614bcc5bfb Mon Sep 17 00:00:00 2001 From: Teddy Reed Date: Thu, 18 Feb 2016 16:38:39 -0800 Subject: [PATCH] [#600] Add TLS SNI hostname to client options --- .../protocol/http/client/async_impl.hpp | 7 ++-- .../http/client/connection/async_base.hpp | 3 +- .../connection_delegate_factory.hpp | 23 ++++++------ .../http/client/connection/ssl_delegate.hpp | 20 ++++++----- .../http/client/connection/ssl_delegate.ipp | 5 ++- .../http/client/connection/sync_ssl.hpp | 23 +++++++----- boost/network/protocol/http/client/facade.hpp | 35 +++++++++---------- .../network/protocol/http/client/options.hpp | 13 +++++++ boost/network/protocol/http/client/pimpl.hpp | 5 +-- .../protocol/http/client/sync_impl.hpp | 17 +++++---- .../http/policies/async_connection.hpp | 26 +++++++------- 11 files changed, 106 insertions(+), 71 deletions(-) diff --git a/boost/network/protocol/http/client/async_impl.hpp b/boost/network/protocol/http/client/async_impl.hpp index a70a90b36..bd94ad0a1 100644 --- a/boost/network/protocol/http/client/async_impl.hpp +++ b/boost/network/protocol/http/client/async_impl.hpp @@ -44,7 +44,8 @@ struct async_client optional verify_path, optional certificate_file, optional private_key_file, - optional ciphers, long ssl_options) + optional ciphers, + optional sni_hostname, long ssl_options) : connection_base(cache_resolved, follow_redirect, timeout), service_ptr(service.get() ? service @@ -57,6 +58,7 @@ struct async_client certificate_file_(std::move(certificate_file)), private_key_file_(std::move(private_key_file)), ciphers_(std::move(ciphers)), + sni_hostname_(std::move(sni_hostname)), ssl_options_(ssl_options), always_verify_peer_(always_verify_peer) { connection_base::resolver_strand_.reset( @@ -84,7 +86,7 @@ struct async_client connection_ = connection_base::get_connection( resolver_, request_, always_verify_peer_, certificate_filename_, verify_path_, certificate_file_, private_key_file_, ciphers_, - ssl_options_); + sni_hostname_, ssl_options_); return connection_->send_request(method, request_, get_body, callback, generator); } @@ -99,6 +101,7 @@ struct async_client optional certificate_file_; optional private_key_file_; optional ciphers_; + optional sni_hostname_; long ssl_options_; bool always_verify_peer_; }; diff --git a/boost/network/protocol/http/client/connection/async_base.hpp b/boost/network/protocol/http/client/connection/async_base.hpp index a3fe481d1..93e2960f8 100644 --- a/boost/network/protocol/http/client/connection/async_base.hpp +++ b/boost/network/protocol/http/client/connection/async_base.hpp @@ -45,6 +45,7 @@ struct async_connection_base { optional certificate_file = optional(), optional private_key_file = optional(), optional ciphers = optional(), + optional sni_hostname = optional(), long ssl_options = 0) { typedef http_async_connection async_connection; @@ -55,7 +56,7 @@ struct async_connection_base { delegate_factory_type::new_connection_delegate( resolver.get_io_service(), https, always_verify_peer, certificate_filename, verify_path, certificate_file, - private_key_file, ciphers, ssl_options))); + private_key_file, ciphers, sni_hostname, ssl_options))); BOOST_ASSERT(temp.get() != 0); return temp; } diff --git a/boost/network/protocol/http/client/connection/connection_delegate_factory.hpp b/boost/network/protocol/http/client/connection/connection_delegate_factory.hpp index 35f05bcfe..addf87d09 100644 --- a/boost/network/protocol/http/client/connection/connection_delegate_factory.hpp +++ b/boost/network/protocol/http/client/connection/connection_delegate_factory.hpp @@ -39,13 +39,14 @@ struct connection_delegate_factory { optional certificate_filename, optional verify_path, optional certificate_file, optional private_key_file, optional ciphers, - long ssl_options) { + optional sni_hostname, long ssl_options) { connection_delegate_ptr delegate; if (https) { #ifdef BOOST_NETWORK_ENABLE_HTTPS - delegate.reset(new ssl_delegate( - service, always_verify_peer, certificate_filename, verify_path, - certificate_file, private_key_file, ciphers, ssl_options)); + delegate.reset(new ssl_delegate(service, always_verify_peer, + certificate_filename, verify_path, + certificate_file, private_key_file, + ciphers, sni_hostname, ssl_options)); #else BOOST_THROW_EXCEPTION(std::runtime_error("HTTPS not supported.")); #endif /* BOOST_NETWORK_ENABLE_HTTPS */ @@ -57,13 +58,13 @@ struct connection_delegate_factory { }; } // namespace impl - /* impl */ -} // namespace http - /* http */ -} // namespace network - /* network */ -} // namespace boost - /* boost */ + /* impl */ +} // namespace http + /* http */ +} // namespace network + /* network */ +} // namespace boost + /* boost */ #endif /* BOOST_NETWORK_PROTOCOL_HTTP_CLIENT_CONNECTION_DELEGATE_FACTORY_HPP_20110819 \ */ diff --git a/boost/network/protocol/http/client/connection/ssl_delegate.hpp b/boost/network/protocol/http/client/connection/ssl_delegate.hpp index d084befb5..389933815 100644 --- a/boost/network/protocol/http/client/connection/ssl_delegate.hpp +++ b/boost/network/protocol/http/client/connection/ssl_delegate.hpp @@ -28,17 +28,18 @@ struct ssl_delegate : connection_delegate, optional verify_path, optional certificate_file, optional private_key_file, - optional ciphers, long ssl_options); + optional ciphers, + optional sni_hostname, long ssl_options); void connect(asio::ip::tcp::endpoint &endpoint, std::string host, - boost::uint16_t source_port, - function handler) override; - void write(asio::streambuf &command_streambuf, - function handler) - override; - void read_some(asio::mutable_buffers_1 const &read_buffer, - function handler) - override; + boost::uint16_t source_port, + function handler) override; + void write( + asio::streambuf &command_streambuf, + function handler) override; + void read_some( + asio::mutable_buffers_1 const &read_buffer, + function handler) override; void disconnect() override; ~ssl_delegate() override; @@ -49,6 +50,7 @@ struct ssl_delegate : connection_delegate, optional certificate_file_; optional private_key_file_; optional ciphers_; + optional sni_hostname_; long ssl_options_; std::unique_ptr context_; std::unique_ptr tcp_socket_; diff --git a/boost/network/protocol/http/client/connection/ssl_delegate.ipp b/boost/network/protocol/http/client/connection/ssl_delegate.ipp index 539751488..3b6e9046b 100644 --- a/boost/network/protocol/http/client/connection/ssl_delegate.ipp +++ b/boost/network/protocol/http/client/connection/ssl_delegate.ipp @@ -16,13 +16,14 @@ boost::network::http::impl::ssl_delegate::ssl_delegate( optional certificate_filename, optional verify_path, optional certificate_file, optional private_key_file, optional ciphers, - long ssl_options) + optional sni_hostname, long ssl_options) : service_(service), certificate_filename_(std::move(certificate_filename)), verify_path_(std::move(verify_path)), certificate_file_(std::move(certificate_file)), private_key_file_(std::move(private_key_file)), ciphers_(std::move(ciphers)), + sni_hostname_(std::move(sni_hostname)), ssl_options_(ssl_options), always_verify_peer_(always_verify_peer) {} @@ -68,6 +69,8 @@ void boost::network::http::impl::ssl_delegate::connect( socket_.reset(new asio::ssl::stream( *(tcp_socket_.get()), *context_)); + if (sni_hostname_) + SSL_set_tlsext_host_name(socket_->native_handle(), sni_hostname_->c_str()); if (always_verify_peer_) socket_->set_verify_callback(boost::asio::ssl::rfc2818_verification(host)); socket_->lowest_layer().async_connect( diff --git a/boost/network/protocol/http/client/connection/sync_ssl.hpp b/boost/network/protocol/http/client/connection/sync_ssl.hpp index 44dd0eee3..babe0978b 100644 --- a/boost/network/protocol/http/client/connection/sync_ssl.hpp +++ b/boost/network/protocol/http/client/connection/sync_ssl.hpp @@ -52,12 +52,13 @@ struct https_sync_connection https_sync_connection( resolver_type& resolver, resolver_function_type resolve, bool always_verify_peer, int timeout, - optional /*unused*/const& certificate_filename = + optional /*unused*/ const& certificate_filename = optional(), optional const& verify_path = optional(), optional const& certificate_file = optional(), optional const& private_key_file = optional(), optional const& ciphers = optional(), + optional const& sni_hostname = optional(), long ssl_options = 0) : connection_base(), timeout_(timeout), @@ -93,15 +94,18 @@ struct https_sync_connection if (private_key_file) context_.use_private_key_file(*private_key_file, boost::asio::ssl::context::pem); + if (sni_hostname) + SSL_set_tlsext_host_name(socket_.native_handle(), sni_hostname->c_str()); } - void init_socket(string_type /*unused*/const& hostname, string_type const& port) { + void init_socket(string_type /*unused*/ const& hostname, + string_type const& port) { connection_base::init_socket(socket_.lowest_layer(), resolver_, hostname, port, resolve_); socket_.handshake(boost::asio::ssl::stream_base::client); } - void send_request_impl(string_type /*unused*/const& method, + void send_request_impl(string_type /*unused*/ const& method, basic_request const& request_, body_generator_function_type generator) { boost::asio::streambuf request_buffer; @@ -142,7 +146,8 @@ struct https_sync_connection connection_base::read_body(socket_, response_, response_buffer); typename headers_range >::type connection_range = headers(response_)["Connection"]; - if (version_major == 1 && version_minor == 1 && !boost::empty(connection_range) && + if (version_major == 1 && version_minor == 1 && + !boost::empty(connection_range) && boost::iequals(boost::begin(connection_range)->second, "close")) { close_socket(); } else if (version_major == 1 && version_minor == 0) { @@ -157,8 +162,9 @@ struct https_sync_connection boost::system::error_code ignored; socket_.lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored); - if (ignored != nullptr) { return; -} + if (ignored != nullptr) { + return; + } socket_.lowest_layer().close(ignored); } @@ -166,8 +172,9 @@ struct https_sync_connection private: void handle_timeout(boost::system::error_code const& ec) { - if (!ec) { close_socket(); -} + if (!ec) { + close_socket(); + } } int timeout_; diff --git a/boost/network/protocol/http/client/facade.hpp b/boost/network/protocol/http/client/facade.hpp index d59815d6d..4546601c1 100644 --- a/boost/network/protocol/http/client/facade.hpp +++ b/boost/network/protocol/http/client/facade.hpp @@ -25,7 +25,6 @@ struct basic_response; template struct basic_client_facade { - typedef typename string::type string_type; typedef basic_request request; typedef basic_response response; @@ -53,12 +52,12 @@ struct basic_client_facade { body_generator_function_type()); } - response post(request request, string_type const& body = string_type(), - string_type const& content_type = string_type(), - body_callback_function_type body_handler = - body_callback_function_type(), - body_generator_function_type body_generator = - body_generator_function_type()) { + response post( + request request, string_type const& body = string_type(), + string_type const& content_type = string_type(), + body_callback_function_type body_handler = body_callback_function_type(), + body_generator_function_type body_generator = + body_generator_function_type()) { if (body != string_type()) { request << remove_header("Content-Length") << header("Content-Length", @@ -82,10 +81,9 @@ struct basic_client_facade { body_generator); } - response post(request const& request, - body_generator_function_type body_generator, - body_callback_function_type callback = - body_generator_function_type()) { + response post( + request const& request, body_generator_function_type body_generator, + body_callback_function_type callback = body_generator_function_type()) { return pimpl->request_skeleton(request, "POST", true, callback, body_generator); } @@ -104,12 +102,12 @@ struct basic_client_facade { return post(request, body, string_type(), callback, body_generator); } - response put(request request, string_type const& body = string_type(), - string_type const& content_type = string_type(), - body_callback_function_type body_handler = - body_callback_function_type(), - body_generator_function_type body_generator = - body_generator_function_type()) { + response put( + request request, string_type const& body = string_type(), + string_type const& content_type = string_type(), + body_callback_function_type body_handler = body_callback_function_type(), + body_generator_function_type body_generator = + body_generator_function_type()) { if (body != string_type()) { request << remove_header("Content-Length") << header("Content-Length", @@ -164,7 +162,8 @@ struct basic_client_facade { options.always_verify_peer(), options.openssl_certificate(), options.openssl_verify_path(), options.openssl_certificate_file(), options.openssl_private_key_file(), options.openssl_ciphers(), - options.openssl_options(), options.io_service(), options.timeout())); + options.openssl_sni_hostname(), options.openssl_options(), + options.io_service(), options.timeout())); } }; diff --git a/boost/network/protocol/http/client/options.hpp b/boost/network/protocol/http/client/options.hpp index c83bc32d7..1a949b347 100644 --- a/boost/network/protocol/http/client/options.hpp +++ b/boost/network/protocol/http/client/options.hpp @@ -28,6 +28,7 @@ struct client_options { openssl_certificate_file_(), openssl_private_key_file_(), openssl_ciphers_(), + openssl_sni_hostname_(), openssl_options_(0), io_service_(), always_verify_peer_(false), @@ -41,6 +42,7 @@ struct client_options { openssl_certificate_file_(other.openssl_certificate_file_), openssl_private_key_file_(other.openssl_private_key_file_), openssl_ciphers_(other.openssl_ciphers_), + openssl_sni_hostname_(other.openssl_sni_hostname_), openssl_options_(other.openssl_options_), io_service_(other.io_service_), always_verify_peer_(other.always_verify_peer_), @@ -60,6 +62,7 @@ struct client_options { swap(openssl_certificate_file_, other.openssl_certificate_file_); swap(openssl_private_key_file_, other.openssl_private_key_file_); swap(openssl_ciphers_, other.openssl_ciphers_); + swap(openssl_sni_hostname_, other.openssl_sni_hostname_); swap(openssl_options_, other.openssl_options_); swap(io_service_, other.io_service_); swap(always_verify_peer_, other.always_verify_peer_); @@ -101,6 +104,11 @@ struct client_options { return *this; } + client_options& openssl_sni_hostname(string_type const& v) { + openssl_sni_hostname_ = v; + return *this; + } + client_options& openssl_options(long o) { openssl_options_ = o; return *this; @@ -145,6 +153,10 @@ struct client_options { return openssl_ciphers_; } + boost::optional openssl_sni_hostname() const { + return openssl_sni_hostname_; + } + long openssl_options() const { return openssl_options_; } boost::shared_ptr io_service() const { @@ -163,6 +175,7 @@ struct client_options { boost::optional openssl_certificate_file_; boost::optional openssl_private_key_file_; boost::optional openssl_ciphers_; + boost::optional openssl_sni_hostname_; long openssl_options_; boost::shared_ptr io_service_; bool always_verify_peer_; diff --git a/boost/network/protocol/http/client/pimpl.hpp b/boost/network/protocol/http/client/pimpl.hpp index 2ae7ff6a8..3ab3a962f 100644 --- a/boost/network/protocol/http/client/pimpl.hpp +++ b/boost/network/protocol/http/client/pimpl.hpp @@ -73,12 +73,13 @@ struct basic_client_impl optional const& verify_path, optional const& certificate_file, optional const& private_key_file, - optional const& ciphers, long ssl_options, + optional const& ciphers, + optional const& sni_hostname, long ssl_options, boost::shared_ptr service, int timeout) : base_type(cache_resolved, follow_redirect, always_verify_peer, timeout, service, certificate_filename, verify_path, certificate_file, - private_key_file, ciphers, ssl_options) {} + private_key_file, ciphers, sni_hostname, ssl_options) {} ~basic_client_impl() = default; }; diff --git a/boost/network/protocol/http/client/sync_impl.hpp b/boost/network/protocol/http/client/sync_impl.hpp index 39e90bc1a..bf32e4e1e 100644 --- a/boost/network/protocol/http/client/sync_impl.hpp +++ b/boost/network/protocol/http/client/sync_impl.hpp @@ -46,18 +46,19 @@ struct sync_client optional certificate_file_; optional private_key_file_; optional ciphers_; + optional sni_hostname_; long ssl_options_; bool always_verify_peer_; sync_client( bool cache_resolved, bool follow_redirect, bool always_verify_peer, int timeout, boost::shared_ptr service, - optional certificate_filename = - optional(), - optional verify_path = optional(), - optional certificate_file = optional(), - optional private_key_file = optional(), - optional ciphers = optional(), + optional certificate_filename = optional(), + optional verify_path = optional(), + optional certificate_file = optional(), + optional private_key_file = optional(), + optional ciphers = optional(), + optional sni_hostname = optional(), long ssl_options = 0) : connection_base(cache_resolved, follow_redirect, timeout), service_ptr(service.get() ? service @@ -69,6 +70,7 @@ struct sync_client certificate_file_(std::move(certificate_file)), private_key_file_(std::move(private_key_file)), ciphers_(std::move(ciphers)), + sni_hostname_(std::move(sni_hostname)), ssl_options_(ssl_options), always_verify_peer_(always_verify_peer) {} @@ -83,7 +85,8 @@ struct sync_client typename connection_base::connection_ptr connection_; connection_ = connection_base::get_connection( resolver_, request_, always_verify_peer_, certificate_filename_, - verify_path_, certificate_file_, private_key_file_, ciphers_); + verify_path_, certificate_file_, private_key_file_, ciphers_, + sni_hostname_); return connection_->send_request(method, request_, get_body, callback, generator); } diff --git a/boost/network/protocol/http/policies/async_connection.hpp b/boost/network/protocol/http/policies/async_connection.hpp index 297038f15..de244eafa 100644 --- a/boost/network/protocol/http/policies/async_connection.hpp +++ b/boost/network/protocol/http/policies/async_connection.hpp @@ -37,24 +37,25 @@ struct async_connection_policy : resolver_policy::type { typedef function body_generator_function_type; struct connection_impl { - connection_impl(bool follow_redirect, bool always_verify_peer, - resolve_function resolve, resolver_type& resolver, - bool https, int timeout, - optional /*unused*/const& certificate_filename, - optional const& verify_path, - optional const& certificate_file, - optional const& private_key_file, - optional const& ciphers, long ssl_options) { + connection_impl( + bool follow_redirect, bool always_verify_peer, resolve_function resolve, + resolver_type& resolver, bool https, int timeout, + optional /*unused*/ const& certificate_filename, + optional const& verify_path, + optional const& certificate_file, + optional const& private_key_file, + optional const& ciphers, + optional const& sni_hostname, long ssl_options) { pimpl = impl::async_connection_base< Tag, version_major, version_minor>::new_connection(resolve, resolver, follow_redirect, always_verify_peer, https, timeout, certificate_filename, verify_path, certificate_file, private_key_file, - ciphers, ssl_options); + ciphers, sni_hostname, ssl_options); } - basic_response send_request(string_type /*unused*/const& method, + basic_response send_request(string_type /*unused*/ const& method, basic_request const& request_, bool get_body, body_callback_function_type callback, @@ -71,12 +72,13 @@ struct async_connection_policy : resolver_policy::type { connection_ptr get_connection( resolver_type& resolver, basic_request const& request_, bool always_verify_peer, - optional /*unused*/const& certificate_filename = + optional /*unused*/ const& certificate_filename = optional(), optional const& verify_path = optional(), optional const& certificate_file = optional(), optional const& private_key_file = optional(), optional const& ciphers = optional(), + optional const& sni_hostname = optional(), long ssl_options = 0) { string_type protocol_ = protocol(request_); connection_ptr connection_(new connection_impl( @@ -87,7 +89,7 @@ struct async_connection_policy : resolver_policy::type { boost::arg<4>()), resolver, boost::iequals(protocol_, string_type("https")), timeout_, certificate_filename, verify_path, certificate_file, private_key_file, - ciphers, ssl_options)); + ciphers, sni_hostname, ssl_options)); return connection_; }