diff --git a/nano/rpc/rpc_connection.cpp b/nano/rpc/rpc_connection.cpp index 751033ab2a..54c75b7d20 100644 --- a/nano/rpc/rpc_connection.cpp +++ b/nano/rpc/rpc_connection.cpp @@ -1,12 +1,11 @@ #include -#include -#include #include -#include #include #include -#include +#ifdef NANO_SECURE_RPC +#include +#endif #include nano::rpc_connection::rpc_connection (nano::rpc_config const & rpc_config, boost::asio::io_context & io_ctx, nano::logger_mt & logger, nano::rpc_handler_interface & rpc_handler_interface) : @@ -22,7 +21,7 @@ rpc_handler_interface (rpc_handler_interface) void nano::rpc_connection::parse_connection () { - read (); + read (socket); } void nano::rpc_connection::prepare_head (unsigned version, boost::beast::http::status status) @@ -51,12 +50,19 @@ void nano::rpc_connection::write_result (std::string body, unsigned version, boo } } -void nano::rpc_connection::read () +void nano::rpc_connection::write_completion_handler (std::shared_ptr rpc_connection) +{ + // Intentional no-op +} + +template +void nano::rpc_connection::read (STREAM_TYPE & stream) { auto this_l (shared_from_this ()); auto header_parser (std::make_shared> ()); header_parser->body_limit (rpc_config.max_request_size); - boost::beast::http::async_read_header (socket, buffer, *header_parser, boost::asio::bind_executor (strand, [this_l, header_parser](boost::system::error_code const & ec, size_t bytes_transferred) { + + boost::beast::http::async_read_header (stream, buffer, *header_parser, boost::asio::bind_executor (strand, [this_l, &stream, header_parser](boost::system::error_code const & ec, size_t bytes_transferred) { if (!ec) { if (boost::iequals (header_parser->get ()[boost::beast::http::field::expect], "100-continue")) @@ -65,45 +71,46 @@ void nano::rpc_connection::read () continue_response->version (11); continue_response->result (boost::beast::http::status::continue_); continue_response->set (boost::beast::http::field::server, "nano"); - boost::beast::http::async_write (this_l->socket, *continue_response, boost::asio::bind_executor (this_l->strand, [this_l, continue_response](boost::system::error_code const & ec, size_t bytes_transferred) {})); + boost::beast::http::async_write (stream, *continue_response, boost::asio::bind_executor (this_l->strand, [this_l, continue_response](boost::system::error_code const & ec, size_t bytes_transferred) {})); } - this_l->parse_request (header_parser); + this_l->parse_request (stream, header_parser); } else { this_l->logger.always_log ("RPC header error: ", ec.message ()); // Respond with the reason for the invalid header - auto response_handler ([this_l](std::string const & tree_a) { + auto response_handler ([this_l, &stream](std::string const & tree_a) { this_l->write_result (tree_a, 11); - boost::beast::http::async_write (this_l->socket, this_l->res, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) { + boost::beast::http::async_write (stream, this_l->res, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) { this_l->write_completion_handler (this_l); })); }); - json_error_response (response_handler, std::string ("Invalid header: ") + ec.message ()); + nano::json_error_response (response_handler, std::string ("Invalid header: ") + ec.message ()); } })); } -void nano::rpc_connection::parse_request (std::shared_ptr> header_parser) +template +void nano::rpc_connection::parse_request (STREAM_TYPE & stream, std::shared_ptr> header_parser) { auto this_l (shared_from_this ()); auto body_parser (std::make_shared> (std::move (*header_parser))); - boost::beast::http::async_read (socket, buffer, *body_parser, boost::asio::bind_executor (strand, [this_l, body_parser](boost::system::error_code const & ec, size_t bytes_transferred) { + boost::beast::http::async_read (stream, buffer, *body_parser, boost::asio::bind_executor (strand, [this_l, body_parser, &stream](boost::system::error_code const & ec, size_t bytes_transferred) { if (!ec) { - this_l->io_ctx.post ([this_l, body_parser]() { + this_l->io_ctx.post ([this_l, body_parser, &stream]() { auto & req (body_parser->get ()); auto start (std::chrono::steady_clock::now ()); auto version (req.version ()); std::stringstream ss; ss << std::hex << std::showbase << reinterpret_cast (this_l.get ()); auto request_id = ss.str (); - auto response_handler ([this_l, version, start, request_id](std::string const & tree_a) { + auto response_handler ([this_l, version, start, request_id, &stream](std::string const & tree_a) { auto body = tree_a; this_l->write_result (body, version); - boost::beast::http::async_write (this_l->socket, this_l->res, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) { + boost::beast::http::async_write (stream, this_l->res, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) { this_l->write_completion_handler (this_l); })); @@ -124,14 +131,14 @@ void nano::rpc_connection::parse_request (std::shared_ptrprepare_head (version); this_l->res.prepare_payload (); - boost::beast::http::async_write (this_l->socket, this_l->res, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) { + boost::beast::http::async_write (stream, this_l->res, boost::asio::bind_executor (this_l->strand, [this_l](boost::system::error_code const & ec, size_t bytes_transferred) { this_l->write_completion_handler (this_l); })); break; } default: { - json_error_response (response_handler, "Can only POST requests"); + nano::json_error_response (response_handler, "Can only POST requests"); break; } } @@ -144,7 +151,9 @@ void nano::rpc_connection::parse_request (std::shared_ptr rpc_connection) -{ - // Intentional no-op -} +template void nano::rpc_connection::read (socket_type &); +template void nano::rpc_connection::parse_request (socket_type &, std::shared_ptr>); +#ifdef NANO_SECURE_RPC +template void nano::rpc_connection::read (boost::asio::ssl::stream &); +template void nano::rpc_connection::parse_request (boost::asio::ssl::stream &, std::shared_ptr>); +#endif diff --git a/nano/rpc/rpc_connection.hpp b/nano/rpc/rpc_connection.hpp index ee0287fb9c..50ab9bf9f3 100644 --- a/nano/rpc/rpc_connection.hpp +++ b/nano/rpc/rpc_connection.hpp @@ -1,7 +1,11 @@ #pragma once +#include +#include +#include #include +#include #include #include @@ -29,9 +33,6 @@ class rpc_connection : public std::enable_shared_from_this virtual void write_completion_handler (std::shared_ptr rpc_connection); void prepare_head (unsigned version, boost::beast::http::status status = boost::beast::http::status::ok); void write_result (std::string body, unsigned version, boost::beast::http::status status = boost::beast::http::status::ok); - void parse_request (std::shared_ptr> header_parser); - - void read (); socket_type socket; boost::beast::flat_buffer buffer; @@ -42,5 +43,12 @@ class rpc_connection : public std::enable_shared_from_this nano::logger_mt & logger; nano::rpc_config const & rpc_config; nano::rpc_handler_interface & rpc_handler_interface; + +protected: + template + void read (STREAM_TYPE & stream); + + template + void parse_request (STREAM_TYPE & stream, std::shared_ptr> header_parser); }; } diff --git a/nano/rpc/rpc_connection_secure.cpp b/nano/rpc/rpc_connection_secure.cpp index cb1998f88c..d69efde4a9 100644 --- a/nano/rpc/rpc_connection_secure.cpp +++ b/nano/rpc/rpc_connection_secure.cpp @@ -14,9 +14,9 @@ void nano::rpc_connection_secure::parse_connection () // Perform the SSL handshake auto this_l = std::static_pointer_cast (shared_from_this ()); stream.async_handshake (boost::asio::ssl::stream_base::server, - [this_l](auto & ec) { + boost::asio::bind_executor (this_l->strand, [this_l](auto & ec) { this_l->handle_handshake (ec); - }); + })); } void nano::rpc_connection_secure::on_shutdown (const boost::system::error_code & error) @@ -29,7 +29,7 @@ void nano::rpc_connection_secure::handle_handshake (const boost::system::error_c { if (!error) { - read (); + read (stream); } else { diff --git a/nano/rpc/rpc_secure.cpp b/nano/rpc/rpc_secure.cpp index df13ea4508..b404d491f0 100644 --- a/nano/rpc/rpc_secure.cpp +++ b/nano/rpc/rpc_secure.cpp @@ -61,33 +61,42 @@ bool nano::rpc_secure::on_verify_certificate (bool preverified, boost::asio::ssl void nano::rpc_secure::load_certs (boost::asio::ssl::context & context_a) { - // This is called if the key is password protected - context_a.set_password_callback ( - [this](std::size_t, - boost::asio::ssl::context_base::password_purpose) { - return config.secure.server_key_passphrase; - }); + try + { + // This is called if the key is password protected + context_a.set_password_callback ( + [this](std::size_t, + boost::asio::ssl::context_base::password_purpose) { + return config.secure.server_key_passphrase; + }); - // The following two options disables the session cache and enables stateless session resumption. - // This is necessary because of the way the RPC server abruptly terminate connections. - SSL_CTX_set_session_cache_mode (context_a.native_handle (), SSL_SESS_CACHE_OFF); - SSL_CTX_set_options (context_a.native_handle (), SSL_OP_NO_TICKET); + // The following two options disables the session cache and enables stateless session resumption. + // This is necessary because of the way the RPC server abruptly terminate connections. + SSL_CTX_set_session_cache_mode (context_a.native_handle (), SSL_SESS_CACHE_OFF); + SSL_CTX_set_options (context_a.native_handle (), SSL_OP_NO_TICKET); - context_a.set_options ( - boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::single_dh_use); + context_a.set_options ( + boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::single_dh_use); - context_a.use_certificate_chain_file (config.secure.server_cert_path); - context_a.use_private_key_file (config.secure.server_key_path, boost::asio::ssl::context::pem); - context_a.use_tmp_dh_file (config.secure.server_dh_path); + context_a.use_certificate_chain_file (config.secure.server_cert_path); + context_a.use_private_key_file (config.secure.server_key_path, boost::asio::ssl::context::pem); + context_a.use_tmp_dh_file (config.secure.server_dh_path); - // Verify client certificates? - if (config.secure.client_certs_path.size () > 0) + // Verify client certificates? + if (!config.secure.client_certs_path.empty ()) + { + context_a.set_verify_mode (boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_peer); + context_a.add_verify_path (config.secure.client_certs_path); + context_a.set_verify_callback ([this](auto preverified, auto & ctx) { + return this->on_verify_certificate (preverified, ctx); + }); + } + } + catch (boost::system::system_error const & err) { - context_a.set_verify_mode (boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_peer); - context_a.add_verify_path (config.secure.client_certs_path); - context_a.set_verify_callback ([this](auto preverified, auto & ctx) { - return this->on_verify_certificate (preverified, ctx); - }); + auto error (boost::str (boost::format ("Could not load certificate information: %1%. Make sure the paths in the secure rpc configuration are correct.") % err.what ())); + std::cerr << error << std::endl; + logger.always_log (error); } }