From b89f1f5869c1bf289e0c166897c289afb20ccc31 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 7 Nov 2015 10:32:08 +0900 Subject: [PATCH] asio: ALPN support --- src/asio_client_session_tls_impl.cc | 8 ++++++ src/asio_client_tls_context.cc | 6 ++++ src/asio_common.cc | 43 +++++++++++++++++++++++++++++ src/asio_common.h | 8 ++++++ src/asio_server.cc | 11 ++++++-- src/asio_server_tls_context.cc | 18 ++++++++++++ src/includes/nghttp2/asio_http2.h | 31 ++++++++++++++------- 7 files changed, 113 insertions(+), 12 deletions(-) diff --git a/src/asio_client_session_tls_impl.cc b/src/asio_client_session_tls_impl.cc index cde2ab31..8db75c17 100644 --- a/src/asio_client_session_tls_impl.cc +++ b/src/asio_client_session_tls_impl.cc @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "asio_client_session_tls_impl.h" +#include "asio_common.h" namespace nghttp2 { namespace asio_http2 { @@ -59,6 +60,13 @@ void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) { not_connected(ec); return; } + + if (!tls_h2_negotiated(socket_)) { + not_connected( + make_error_code(NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED)); + return; + } + connected(endpoint_it); }); }); diff --git a/src/asio_client_tls_context.cc b/src/asio_client_tls_context.cc index 55138341..75830013 100644 --- a/src/asio_client_tls_context.cc +++ b/src/asio_client_tls_context.cc @@ -56,6 +56,12 @@ configure_tls_context(boost::system::error_code &ec, SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + auto proto_list = util::get_default_alpn(); + + SSL_CTX_set_alpn_protos(ctx, proto_list.data(), proto_list.size()); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + return ec; } diff --git a/src/asio_common.cc b/src/asio_common.cc index c3f98e7c..f8567a5f 100644 --- a/src/asio_common.cc +++ b/src/asio_common.cc @@ -48,6 +48,31 @@ boost::system::error_code make_error_code(nghttp2_error ev) { return boost::system::error_code(static_cast(ev), nghttp2_category()); } +class nghttp2_asio_category_impl : public boost::system::error_category { +public: + const char *name() const noexcept { return "nghttp2_asio"; } + std::string message(int ev) const { + switch (ev) { + case NGHTTP2_ASIO_ERR_NO_ERROR: + return "no error"; + case NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED: + return "tls: no application protocol negotiated"; + default: + return "unknown"; + } + } +}; + +const boost::system::error_category &nghttp2_asio_category() noexcept { + static nghttp2_asio_category_impl cat; + return cat; +} + +boost::system::error_code make_error_code(nghttp2_asio_error ev) { + return boost::system::error_code(static_cast(ev), + nghttp2_asio_category()); +} + generator_cb string_generator(std::string data) { auto strio = std::make_shared>(std::move(data), data.size()); @@ -144,5 +169,23 @@ boost::system::error_code host_service_from_uri(boost::system::error_code &ec, return ec; } +bool tls_h2_negotiated(ssl_socket &socket) { + auto ssl = socket.native_handle(); + + const unsigned char *next_proto = nullptr; + unsigned int next_proto_len = 0; + + SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); + if (next_proto == nullptr) { + SSL_get0_alpn_selected(ssl, &next_proto, &next_proto_len); + } + + if (next_proto == nullptr) { + return false; + } + + return util::check_h2_is_selected(next_proto, next_proto_len); +} + } // namespace asio_http2 } // namespace nghttp2 diff --git a/src/asio_common.h b/src/asio_common.h index 8f59a0d1..b2384649 100644 --- a/src/asio_common.h +++ b/src/asio_common.h @@ -39,6 +39,8 @@ namespace asio_http2 { boost::system::error_code make_error_code(nghttp2_error ev); +boost::system::error_code make_error_code(nghttp2_asio_error ev); + generator_cb string_generator(std::string data); // Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED @@ -58,6 +60,12 @@ void split_path(uri_ref &dst, InputIt first, InputIt last) { dst.raw_query.assign(query_first, last); } +using boost::asio::ip::tcp; + +using ssl_socket = boost::asio::ssl::stream; + +bool tls_h2_negotiated(ssl_socket &socket); + } // namespace asio_http2 } // namespace nghttp2 diff --git a/src/asio_server.cc b/src/asio_server.cc index 310e672c..11c6c31d 100644 --- a/src/asio_server.cc +++ b/src/asio_server.cc @@ -37,6 +37,7 @@ #include "asio_server.h" #include "asio_server_connection.h" +#include "asio_common.h" #include "util.h" namespace nghttp2 { @@ -130,9 +131,15 @@ void server::start_accept(boost::asio::ssl::context &tls_context, new_connection->socket().async_handshake( boost::asio::ssl::stream_base::server, [new_connection](const boost::system::error_code &e) { - if (!e) { - new_connection->start(); + if (e) { + return; } + + if (!tls_h2_negotiated(new_connection->socket())) { + return; + } + + new_connection->start(); }); } diff --git a/src/asio_server_tls_context.cc b/src/asio_server_tls_context.cc index 4790a956..53fc9883 100644 --- a/src/asio_server_tls_context.cc +++ b/src/asio_server_tls_context.cc @@ -42,6 +42,19 @@ std::vector &get_alpn_token() { } } // namespace +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +namespace { +int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg) { + if (!util::select_h2(out, outlen, in, inlen)) { + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; +} +} // namespace +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + boost::system::error_code configure_tls_context_easy(boost::system::error_code &ec, boost::asio::ssl::context &tls_context) { @@ -81,6 +94,11 @@ configure_tls_context_easy(boost::system::error_code &ec, }, nullptr); +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // ALPN selection callback + SSL_CTX_set_alpn_select_cb(ctx, alpn_select_proto_cb, nullptr); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L + return ec; } diff --git a/src/includes/nghttp2/asio_http2.h b/src/includes/nghttp2/asio_http2.h index a54a9e8e..a95d7b95 100644 --- a/src/includes/nghttp2/asio_http2.h +++ b/src/includes/nghttp2/asio_http2.h @@ -38,16 +38,6 @@ #include -namespace boost { -namespace system { - -template <> struct is_error_code_enum { - BOOST_STATIC_CONSTANT(bool, value = true); -}; - -} // namespace system -} // namespace boost - namespace nghttp2 { namespace asio_http2 { @@ -132,8 +122,29 @@ boost::system::error_code host_service_from_uri(boost::system::error_code &ec, std::string &service, const std::string &uri); +enum nghttp2_asio_error { + NGHTTP2_ASIO_ERR_NO_ERROR = 0, + NGHTTP2_ASIO_ERR_TLS_NO_APP_PROTO_NEGOTIATED = 1, +}; + } // namespace asio_http2 } // namespace nghttp2 +namespace boost { + +namespace system { + +template <> struct is_error_code_enum { + BOOST_STATIC_CONSTANT(bool, value = true); +}; + +template <> struct is_error_code_enum { + BOOST_STATIC_CONSTANT(bool, value = true); +}; + +} // namespace system + +} // namespace boost + #endif // ASIO_HTTP2_H