Mercurial > irccd
changeset 563:17891017c661
Irccd: keep resolver in irc connection
Iterators were invalidated because the resolver is destroyed, keep it in
both irc::ip_connection and irc::tls_connection.
While here, bring back SSL support in irccd.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 26 Nov 2017 12:01:28 +0100 |
parents | 75e3711c95f8 |
children | b8ebbc74da0c |
files | libirccd/irccd/config.cpp libirccd/irccd/irc.cpp libirccd/irccd/irc.hpp libirccd/irccd/server.cpp tests/CMakeLists.txt |
diffstat | 5 files changed, 247 insertions(+), 131 deletions(-) [+] |
line wrap: on
line diff
--- a/libirccd/irccd/config.cpp Sun Nov 26 09:02:09 2017 +0100 +++ b/libirccd/irccd/config.cpp Sun Nov 26 12:01:28 2017 +0100 @@ -333,7 +333,11 @@ if ((it = sc.find("ipv6")) != sc.end() && string_util::is_boolean(it->value())) sv->set_flags(sv->flags() | server::ipv6); if ((it = sc.find("ssl")) != sc.end() && string_util::is_boolean(it->value())) +#if defined(HAVE_SSL) sv->set_flags(sv->flags() | server::ssl); +#else + throw std::invalid_argument(string_util::sprintf("server %s: SSL support disabled", sv->name())); +#endif if ((it = sc.find("ssl-verify")) != sc.end() && string_util::is_boolean(it->value())) sv->set_flags(sv->flags() | server::ssl_verify);
--- a/libirccd/irccd/irc.cpp Sun Nov 26 09:02:09 2017 +0100 +++ b/libirccd/irccd/irc.cpp Sun Nov 26 12:01:28 2017 +0100 @@ -31,35 +31,128 @@ using boost::asio::ip::tcp; template <typename Socket> -void do_connect(Socket& socket, tcp::resolver::iterator it, connection::connect_t handler) +void wrap_connect(Socket& socket, tcp::resolver::iterator it, connection::connect_t handler) { + assert(handler); + socket.close(); socket.async_connect(*it, [&socket, it, handler] (auto code) mutable { - if (code && it != tcp::resolver::iterator()) - do_connect(socket, ++it, std::move(handler)); + if (code && ++it != tcp::resolver::iterator()) + wrap_connect(socket, it, std::move(handler)); else handler(code); }); } template <typename Socket> -void do_resolve(const std::string& host, +void wrap_resolve(Socket& socket, + tcp::resolver& resolver, + const std::string& host, const std::string& port, - Socket& socket, connection::connect_t handler) { - auto resolver = std::make_shared<tcp::resolver>(socket.get_io_service()); + assert(handler); - resolver->async_resolve(tcp::resolver::query(host, port), [&socket, handler, resolver] (auto code, auto it) { + tcp::resolver::query query(host, port); + + resolver.async_resolve(query, [&socket, handler] (auto code, auto it) { if (code) handler(code); else - do_connect(socket, it, std::move(handler)); + wrap_connect(socket, it, std::move(handler)); + }); +} + +template <typename Socket> +void wrap_recv(Socket& socket, boost::asio::streambuf& buffer, connection::recv_t handler) +{ + assert(handler); + + boost::asio::async_read_until(socket, buffer, "\r\n", [&socket, &buffer, handler] (auto code, auto xfer) { + if (code || xfer == 0U) + handler(std::move(code), message()); + else { + std::string str( + boost::asio::buffers_begin(buffer.data()), + boost::asio::buffers_begin(buffer.data()) + xfer - 2 + ); + + buffer.consume(xfer); + handler(std::move(code), message::parse(str)); + } + }); +} + +template <typename Socket> +void wrap_send(Socket& socket, const std::string& message, connection::send_t handler) +{ + assert(handler); + + boost::asio::async_write(socket, boost::asio::buffer(message), [handler, message] (auto code, auto) { + // TODO: xfer + handler(code); }); } } // !namespace +void connection::rflush() +{ + if (input_.empty()) + return; + + do_recv(buffer_, [this] (auto code, auto message) { + input_.front()(code, std::move(message)); + input_.pop_front(); + + if (!code) + rflush(); + }); +} + +void connection::sflush() +{ + if (output_.empty()) + return; + + do_send(output_.front().first, [this] (auto code) { + if (output_.front().second) + output_.front().second(code); + + output_.pop_front(); + + if (!code) + sflush(); + }); +} + +void connection::connect(const std::string& host, const std::string& service, connect_t handler) +{ + assert(handler); + + do_connect(host, service, std::move(handler)); +} + +void connection::recv(recv_t handler) +{ + auto in_progress = !input_.empty(); + + input_.push_back(std::move(handler)); + + if (!in_progress) + rflush(); +} + +void connection::send(std::string message, send_t handler) +{ + auto in_progress = !output_.empty(); + + output_.emplace_back(std::move(message + "\r\n"), std::move(handler)); + + if (!in_progress) + sflush(); +} + message message::parse(const std::string& line) { std::istringstream iss(line); @@ -134,11 +227,49 @@ return {line.substr(0, pos), line.substr(pos + 1)}; } -void ip_connection::connect(const std::string& host, const std::string& service, connect_t handler) +void ip_connection::do_connect(const std::string& host, const std::string& service, connect_t handler) noexcept +{ + wrap_resolve(socket_, resolver_, host, service, std::move(handler)); +} + +void ip_connection::do_recv(boost::asio::streambuf& buffer, recv_t handler) noexcept +{ + wrap_recv(socket_, buffer, std::move(handler)); +} + +void ip_connection::do_send(const std::string& data, send_t handler) noexcept +{ + wrap_send(socket_, data, std::move(handler)); +} + +#if defined(HAVE_SSL) + +void tls_connection::do_connect(const std::string& host, const std::string& service, connect_t handler) noexcept { - do_resolve(host, service, socket_, std::move(handler)); + using boost::asio::ssl::stream_base; + + wrap_resolve(socket_.lowest_layer(), resolver_, host, service, [this, handler] (auto code) { + if (code) + handler(code); + else + socket_.async_handshake(stream_base::client, [this, handler] (auto code) { + handler(code); + }); + }); } +void tls_connection::do_recv(boost::asio::streambuf& buffer, recv_t handler) noexcept +{ + wrap_recv(socket_, buffer, std::move(handler)); +} + +void tls_connection::do_send(const std::string& data, send_t handler) noexcept +{ + wrap_send(socket_, data, std::move(handler)); +} + +#endif // !HAVE_SSL + } // !irc } // !irccd
--- a/libirccd/irccd/irc.hpp Sun Nov 26 09:02:09 2017 +0100 +++ b/libirccd/irccd/irc.hpp Sun Nov 26 12:01:28 2017 +0100 @@ -24,6 +24,8 @@ * \brief Low level IRC functions. */ +#include "sysconfig.hpp" + #include <deque> #include <functional> #include <string> @@ -371,6 +373,45 @@ */ using send_t = std::function<void (boost::system::error_code)>; +private: + using buffer_t = boost::asio::streambuf; + using input_t = std::deque<recv_t>; + using output_t = std::deque<std::pair<std::string, send_t>>; + + buffer_t buffer_; + input_t input_; + output_t output_; + + void rflush(); + void sflush(); + +protected: + /** + * Do the connection. + * + * \param host the hostname + * \param service the service or port number + * \param handler the non-null handler + */ + virtual void do_connect(const std::string& host, const std::string& service, connect_t handler) noexcept = 0; + + /** + * Receive some data. + * + * \param buffer the buffer to complete + * \param handler the non-null handler + */ + virtual void do_recv(boost::asio::streambuf& buffer, recv_t handler) noexcept = 0; + + /** + * Send data. + * + * \param data the data to send + * \param handler the non-null handler + */ + virtual void do_send(const std::string& data, send_t handler) noexcept = 0; + +public: /** * Default constructor. */ @@ -384,18 +425,19 @@ /** * Connect to the host. * + * \pre handler the handler * \param host the host * \param service the service or port number * \param handler the non-null handler */ - virtual void connect(const std::string& host, const std::string& service, connect_t handler) = 0; + void connect(const std::string& host, const std::string& service, connect_t handler); /** * Start receiving data. * * \param handler the handler to call */ - virtual void recv(recv_t handler) = 0; + void recv(recv_t handler); /** * Start sending data. @@ -403,149 +445,88 @@ * \param message the raw message * \param handler the handler to call */ - virtual void send(std::string message, send_t handler = nullptr) = 0; + void send(std::string message, send_t handler = nullptr); }; /** - * \brief Implementation for Boost.Asio sockets. - * - * To use this class, derive from it and implement the connect function. + * \brief Clear TCP connection */ -template <typename Socket> -class basic_connection : public connection { -protected: - Socket socket_; - +class ip_connection : public connection { private: - using buffer_t = boost::asio::streambuf; - using input_t = std::deque<recv_t>; - using output_t = std::deque<std::pair<std::string, send_t>>; + boost::asio::ip::tcp::socket socket_; + boost::asio::ip::tcp::resolver resolver_; + +protected: + /** + * \copydoc connection::do_connect + */ + void do_connect(const std::string& host, const std::string& service, connect_t handler) noexcept override; - buffer_t buffer_; - input_t input_; - output_t output_; + /** + * \copydoc connection::do_recv + */ + void do_recv(boost::asio::streambuf& buffer, recv_t handler) noexcept override; - void rflush(); - void sflush(); - void do_recv(recv_t); - void do_send(const std::string&, send_t); + /** + * \copydoc connection::do_send + */ + void do_send(const std::string& data, send_t handler) noexcept override; public: /** * Constructor. * - * \param args the arguments to pass to the socket + * \param service the io service */ - template <typename... Args> - inline basic_connection(Args&&... args) - : socket_(std::forward<Args>(args)...) + inline ip_connection(boost::asio::io_service& service) noexcept + : socket_(service) + , resolver_(service) { } - - /** - * \copydoc connection::recv - */ - void recv(recv_t handler) override; - - /** - * \copydoc connection::send - */ - void send(std::string message, send_t handler = nullptr) override; }; -template <typename Socket> -void basic_connection<Socket>::rflush() -{ - if (input_.empty()) - return; - - do_recv([this] (auto code, auto message) { - input_.front()(code, std::move(message)); - input_.pop_front(); - - if (!code) - rflush(); - }); -} +#if defined(HAVE_SSL) -template <typename Socket> -void basic_connection<Socket>::sflush() -{ - if (output_.empty()) - return; - - do_send(output_.front().first, [this] (auto code) { - if (output_.front().second) - output_.front().second(code); - - output_.pop_front(); - - if (!code) - sflush(); - }); -} +/** + * \brief SSL connection + */ +class tls_connection : public connection { +private: + boost::asio::ssl::context context_; + boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_; + boost::asio::ip::tcp::resolver resolver_; -template <typename Socket> -void basic_connection<Socket>::do_recv(recv_t handler) -{ - boost::asio::async_read_until(socket_, buffer_, "\r\n", [this, handler] (auto code, auto xfer) { - if (code || xfer == 0U) - handler(std::move(code), message()); - else { - std::string str( - boost::asio::buffers_begin(buffer_.data()), - boost::asio::buffers_begin(buffer_.data()) + xfer - 2 - ); - - buffer_.consume(xfer); - handler(std::move(code), message::parse(str)); - } - }); -} +protected: + /** + * \copydoc connection::do_connect + */ + void do_connect(const std::string& host, const std::string& service, connect_t handler) noexcept override; -template <typename Socket> -void basic_connection<Socket>::do_send(const std::string& message, send_t handler) -{ - boost::asio::async_write(socket_, boost::asio::buffer(message), [handler, message] (auto code, auto) { - // TODO: xfer - handler(code); - }); -} - -template <typename Socket> -void basic_connection<Socket>::recv(recv_t handler) -{ - auto in_progress = !input_.empty(); - - input_.push_back(std::move(handler)); + /** + * \copydoc connection::do_recv + */ + void do_recv(boost::asio::streambuf& buffer, recv_t handler) noexcept override; - if (!in_progress) - rflush(); -} + /** + * \copydoc connection::do_send + */ + void do_send(const std::string& data, send_t handler) noexcept override; -template <typename Socket> -void basic_connection<Socket>::send(std::string message, send_t handler) -{ - auto in_progress = !output_.empty(); - - output_.emplace_back(std::move(message + "\r\n"), std::move(handler)); - - if (!in_progress) - sflush(); -} - -class ip_connection : public basic_connection<boost::asio::ip::tcp::socket> { public: /** - * Inherited constructors. + * Constructor. + * + * \param service the io service */ - using basic_connection::basic_connection; + inline tls_connection(boost::asio::io_service& service) noexcept + : context_(boost::asio::ssl::context::sslv23) + , socket_(service, context_) + , resolver_(service) + { + } +}; - /** - * \copydoc basic_connection::connect - */ - void connect(const std::string& host, const std::string& service, connect_t handler) override; -}; +#endif // !HAVE_SSL } // !irc
--- a/libirccd/irccd/server.cpp Sun Nov 26 09:02:09 2017 +0100 +++ b/libirccd/irccd/server.cpp Sun Nov 26 12:01:28 2017 +0100 @@ -534,7 +534,7 @@ if (flags_ & ssl) { #if defined(HAVE_SSL) - //conn_ = std::make_unique<irc::tls_connection>(service_); + conn_ = std::make_unique<irc::tls_connection>(service_); #else /* * If SSL is not compiled in, the caller is responsible of not setting
--- a/tests/CMakeLists.txt Sun Nov 26 09:02:09 2017 +0100 +++ b/tests/CMakeLists.txt Sun Nov 26 12:01:28 2017 +0100 @@ -34,7 +34,7 @@ add_subdirectory(cmd-rule-remove) add_subdirectory(cmd-server-cmode) add_subdirectory(cmd-server-cnotice) - #add_subdirectory(cmd-server-connect) + add_subdirectory(cmd-server-connect) add_subdirectory(cmd-server-disconnect) add_subdirectory(cmd-server-info) add_subdirectory(cmd-server-invite)