Skip to content

[#600] Add TLS SNI hostname to client options #602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions boost/network/protocol/http/client/async_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ struct async_client
typedef typename string<Tag>::type string_type;

typedef std::function<void(boost::iterator_range<char const*> const&,
std::error_code const&)> body_callback_function_type;
std::error_code const&)>
body_callback_function_type;

typedef std::function<bool(string_type&)> body_generator_function_type;

Expand All @@ -43,11 +44,11 @@ struct async_client
optional<string_type> verify_path,
optional<string_type> certificate_file,
optional<string_type> private_key_file,
optional<string_type> ciphers, long ssl_options)
optional<string_type> ciphers,
optional<string_type> sni_hostname, long ssl_options)
: connection_base(cache_resolved, follow_redirect, timeout),
service_ptr(service.get()
? service
: std::make_shared<asio::io_service>()),
service_ptr(service.get() ? service
: std::make_shared<asio::io_service>()),
service_(*service_ptr),
resolver_(service_),
sentinel_(new asio::io_service::work(service_)),
Expand All @@ -56,12 +57,13 @@ 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(
new asio::io_service::strand(service_));
if (!service)
lifetime_thread_.reset(new std::thread([this] () { service_.run(); }));
lifetime_thread_.reset(new std::thread([this]() { service_.run(); }));
}

~async_client() throw() = default;
Expand All @@ -82,7 +84,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);
}
Expand All @@ -97,6 +99,7 @@ struct async_client
optional<string_type> certificate_file_;
optional<string_type> private_key_file_;
optional<string_type> ciphers_;
optional<string_type> sni_hostname_;
long ssl_options_;
bool always_verify_peer_;
};
Expand Down
3 changes: 2 additions & 1 deletion boost/network/protocol/http/client/connection/async_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ struct async_connection_base {
optional<string_type> certificate_file = optional<string_type>(),
optional<string_type> private_key_file = optional<string_type>(),
optional<string_type> ciphers = optional<string_type>(),
optional<string_type> sni_hostname = optional<string_type>(),
long ssl_options = 0) {
typedef http_async_connection<Tag, version_major, version_minor>
async_connection;
typedef typename delegate_factory<Tag>::type delegate_factory_type;
auto delegate = 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);
ciphers, sni_hostname, ssl_options);
auto temp = std::make_shared<async_connection>(
resolver, resolve, follow_redirect, timeout, std::move(delegate));
BOOST_ASSERT(temp != nullptr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ struct connection_delegate_factory {
optional<string_type> certificate_filename,
optional<string_type> verify_path, optional<string_type> certificate_file,
optional<string_type> private_key_file, optional<string_type> ciphers,
long ssl_options) {
optional<string_type> sni_hostname, long ssl_options) {
connection_delegate_ptr delegate;
if (https) {
#ifdef BOOST_NETWORK_ENABLE_HTTPS
delegate = std::make_shared<ssl_delegate>(
service, always_verify_peer, certificate_filename, verify_path,
certificate_file, private_key_file, ciphers, ssl_options);
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 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ struct ssl_delegate : public connection_delegate,
optional<std::string> verify_path,
optional<std::string> certificate_file,
optional<std::string> private_key_file,
optional<std::string> ciphers, long ssl_options);
optional<std::string> ciphers,
optional<std::string> sni_hostname, long ssl_options);

void connect(asio::ip::tcp::endpoint &endpoint, std::string host,
std::uint16_t source_port,
Expand All @@ -50,6 +51,7 @@ struct ssl_delegate : public connection_delegate,
optional<std::string> certificate_file_;
optional<std::string> private_key_file_;
optional<std::string> ciphers_;
optional<std::string> sni_hostname_;
long ssl_options_;
std::unique_ptr<asio::ssl::context> context_;
std::unique_ptr<asio::ip::tcp::socket> tcp_socket_;
Expand Down
19 changes: 9 additions & 10 deletions boost/network/protocol/http/client/connection/ssl_delegate.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ boost::network::http::impl::ssl_delegate::ssl_delegate(
optional<std::string> certificate_filename,
optional<std::string> verify_path, optional<std::string> certificate_file,
optional<std::string> private_key_file, optional<std::string> ciphers,
long ssl_options)
optional<std::string> 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) {}

Expand Down Expand Up @@ -58,25 +59,23 @@ void boost::network::http::impl::ssl_delegate::connect(
}
}
if (certificate_file_)
context_->use_certificate_file(*certificate_file_,
asio::ssl::context::pem);
context_->use_certificate_file(*certificate_file_, asio::ssl::context::pem);
if (private_key_file_)
context_->use_private_key_file(*private_key_file_,
asio::ssl::context::pem);
context_->use_private_key_file(*private_key_file_, asio::ssl::context::pem);

tcp_socket_.reset(new asio::ip::tcp::socket(
service_, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), source_port)));
socket_.reset(new asio::ssl::stream<asio::ip::tcp::socket &>(
*(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(asio::ssl::rfc2818_verification(host));
auto self = this->shared_from_this();
socket_->lowest_layer().async_connect(
endpoint,
[=] (std::error_code const &ec) {
self->handle_connected(ec, handler);
});
[=](std::error_code const &ec) { self->handle_connected(ec, handler); });
}

void boost::network::http::impl::ssl_delegate::handle_connected(
Expand Down Expand Up @@ -104,8 +103,8 @@ void boost::network::http::impl::ssl_delegate::read_some(
void boost::network::http::impl::ssl_delegate::disconnect() {
if (socket_.get() && socket_->lowest_layer().is_open()) {
std::error_code ignored;
socket_->lowest_layer().shutdown(
asio::ip::tcp::socket::shutdown_both, ignored);
socket_->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both,
ignored);
if (!ignored) {
socket_->lowest_layer().close(ignored);
}
Expand Down
32 changes: 16 additions & 16 deletions boost/network/protocol/http/client/connection/sync_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ struct sync_connection_base_impl {

template <class Socket>
void init_socket(Socket& socket_, resolver_type& resolver_,
string_type /*unused*/const& hostname, string_type const& port,
resolver_function_type resolve_) {
string_type /*unused*/ const& hostname,
string_type const& port, resolver_function_type resolve_) {
using asio::ip::tcp;
std::error_code error = asio::error::host_not_found;
typename resolver_type::iterator endpoint_iterator, end;
Expand Down Expand Up @@ -100,7 +100,7 @@ struct sync_connection_base_impl {
}

template <class Socket>
void send_request_impl(Socket& socket_, string_type /*unused*/const& method,
void send_request_impl(Socket& socket_, string_type /*unused*/ const& method,
asio::streambuf& request_buffer) {
// TODO(dberris): review parameter necessity.
(void)method;
Expand All @@ -118,8 +118,8 @@ struct sync_connection_base_impl {
std::error_code error;
if (response_buffer.size() > 0) body_stream << &response_buffer;

while (asio::read(socket_, response_buffer,
asio::transfer_at_least(1), error)) {
while (asio::read(socket_, response_buffer, asio::transfer_at_least(1),
error)) {
body_stream << &response_buffer;
}
}
Expand Down Expand Up @@ -171,8 +171,7 @@ struct sync_connection_base_impl {
read(socket_, response_buffer,
asio::transfer_at_least(bytes_to_read), error);
if (chunk_bytes_read == 0) {
if (error != asio::error::eof)
throw std::system_error(error);
if (error != asio::error::eof) throw std::system_error(error);
stopping_inner = true;
}
}
Expand All @@ -195,14 +194,14 @@ struct sync_connection_base_impl {
} else {
size_t already_read = response_buffer.size();
if (already_read) body_stream << &response_buffer;
size_t length = std::stoul(std::begin(content_length_range)->second) -
already_read;
if (length == 0) { return;
}
size_t length =
std::stoul(std::begin(content_length_range)->second) - already_read;
if (length == 0) {
return;
}
size_t bytes_read = 0;
while ((bytes_read = asio::read(socket_, response_buffer,
asio::transfer_at_least(1),
error))) {
asio::transfer_at_least(1), error))) {
body_stream << &response_buffer;
length -= bytes_read;
if ((length <= 0) || error) break;
Expand All @@ -223,7 +222,7 @@ struct sync_connection_base_impl {
} else {
read_body_transfer_chunk_encoding(socket_, response_, response_buffer,
body_stream);
}
}
} else {
throw std::runtime_error("Unsupported HTTP version number.");
}
Expand All @@ -249,14 +248,15 @@ struct sync_connection_base {
new_connection(
resolver_type& resolver, resolver_function_type resolve, bool https,
bool always_verify_peer, int timeout,
optional<string_type> /*unused*/const& certificate_filename =
optional<string_type> /*unused*/ const& certificate_filename =
optional<string_type>(),
optional<string_type> const& verify_path = optional<string_type>(),
optional<string_type> const& certificate_file =
optional<string_type>(),
optional<string_type> const& private_key_file =
optional<string_type>(),
optional<string_type> const& ciphers = optional<string_type>(),
optional<string_type> const& sni_hostname = optional<string_type>(),
long ssl_options = 0) {
if (https) {
#ifdef BOOST_NETWORK_ENABLE_HTTPS
Expand All @@ -265,7 +265,7 @@ struct sync_connection_base {
new https_sync_connection<Tag, version_major, version_minor>(
resolver, resolve, always_verify_peer, timeout,
certificate_filename, verify_path, certificate_file,
private_key_file, ciphers, ssl_options));
private_key_file, ciphers, sni_hostname, ssl_options));
#else
throw std::runtime_error("HTTPS not supported.");
#endif
Expand Down
36 changes: 20 additions & 16 deletions boost/network/protocol/http/client/connection/sync_ssl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ struct https_sync_connection
https_sync_connection(
resolver_type& resolver, resolver_function_type resolve,
bool always_verify_peer, int timeout,
optional<string_type> /*unused*/const& certificate_filename =
optional<string_type> /*unused*/ const& certificate_filename =
optional<string_type>(),
optional<string_type> const& verify_path = optional<string_type>(),
optional<string_type> const& certificate_file = optional<string_type>(),
optional<string_type> const& private_key_file = optional<string_type>(),
optional<string_type> const& ciphers = optional<string_type>(),
optional<string_type> const& sni_hostname = optional<string_type>(),
long ssl_options = 0)
: connection_base(),
timeout_(timeout),
timer_(resolver.get_io_service()),
resolver_(resolver),
resolve_(std::move(resolve)),
context_(resolver.get_io_service(),
asio::ssl::context::sslv23_client),
context_(resolver.get_io_service(), asio::ssl::context::sslv23_client),
socket_(resolver.get_io_service(), context_) {
if (ciphers) {
::SSL_CTX_set_cipher_list(context_.native_handle(), ciphers->c_str());
Expand All @@ -86,20 +86,21 @@ struct https_sync_connection
context_.set_verify_mode(asio::ssl::context_base::verify_none);
}
if (certificate_file)
context_.use_certificate_file(*certificate_file,
asio::ssl::context::pem);
context_.use_certificate_file(*certificate_file, asio::ssl::context::pem);
if (private_key_file)
context_.use_private_key_file(*private_key_file,
asio::ssl::context::pem);
context_.use_private_key_file(*private_key_file, 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(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<Tag> const& request_,
body_generator_function_type generator) {
asio::streambuf request_buffer;
Expand All @@ -120,9 +121,8 @@ struct https_sync_connection
if (timeout_ > 0) {
timer_.expires_from_now(boost::posix_time::seconds(timeout_));
auto self = this->shared_from_this();
timer_.async_wait([=] (std::error_code const &ec) {
self->handle_timeout(ec);
});
timer_.async_wait(
[=](std::error_code const& ec) { self->handle_timeout(ec); });
}
}

Expand All @@ -141,7 +141,8 @@ struct https_sync_connection
connection_base::read_body(socket_, response_, response_buffer);
typename headers_range<basic_response<Tag> >::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(std::begin(connection_range)->second, "close")) {
close_socket();
} else if (version_major == 1 && version_minor == 0) {
Expand All @@ -156,16 +157,19 @@ struct https_sync_connection
std::error_code ignored;
socket_.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both,
ignored);
if (ignored) { return; }
if (ignored) {
return;
}
socket_.lowest_layer().close(ignored);
}

~https_sync_connection() { close_socket(); }

private:
void handle_timeout(std::error_code const& ec) {
if (!ec) { close_socket();
}
if (!ec) {
close_socket();
}
}

int timeout_;
Expand Down
Loading