Mercurial > irccd
changeset 831:7b012c42660c
irccd: move authentication part from transport_server, closes #995 @2h
If a server requires a password and a client does not provide, the server
is unable to accept a new client.
Move the authentication/greetings part from the server into the client to let
the transport_service processing input.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 24 Feb 2019 15:50:58 +0100 |
parents | 4f7e46e593fe |
children | 1ddf628464a5 |
files | libirccd-daemon/irccd/daemon/transport_client.cpp libirccd-daemon/irccd/daemon/transport_client.hpp libirccd-daemon/irccd/daemon/transport_server.cpp libirccd-daemon/irccd/daemon/transport_service.cpp libirccd-daemon/irccd/daemon/transport_service.hpp tests/src/libirccd-daemon/CMakeLists.txt tests/src/libirccd-daemon/transports/CMakeLists.txt tests/src/libirccd-daemon/transports/main.cpp |
diffstat | 8 files changed, 217 insertions(+), 94 deletions(-) [+] |
line wrap: on
line diff
--- a/libirccd-daemon/irccd/daemon/transport_client.cpp Mon Feb 11 20:30:00 2019 +0100 +++ b/libirccd-daemon/irccd/daemon/transport_client.cpp Sun Feb 24 15:50:58 2019 +0100 @@ -20,11 +20,49 @@ #include <cassert> +#include <irccd/json_util.hpp> + +#include "bot.hpp" #include "transport_client.hpp" #include "transport_server.hpp" namespace irccd::daemon { +void transport_client::auth(handshake_handler handler) +{ + assert(handler); + + const auto self = shared_from_this(); + + read([this, self, handler] (auto code, auto message) { + auto parent = parent_.lock(); + + if (code) { + handler(std::move(code)); + return; + } + + const json_util::deserializer doc(message); + const auto command = doc.get<std::string>("command"); + const auto password = doc.get<std::string>("password"); + + if (!command || *command != "auth") { + code = bot_error::auth_required; + error(bot_error::auth_required); + } else if (!password || *password != parent->get_password()) { + code = bot_error::invalid_auth; + error(bot_error::invalid_auth); + } else { + code = bot_error::no_error; + state_ = state::ready; + success("auth"); + parent->get_clients().insert(self); + } + + handler(std::move(code)); + }); +} + void transport_client::flush() { if (queue_.empty()) @@ -71,6 +109,46 @@ state_ = state; } +void transport_client::handshake(handshake_handler handler) +{ + assert(handler); + + const auto greetings = nlohmann::json({ + { "program", "irccd" }, + { "major", IRCCD_VERSION_MAJOR }, + { "minor", IRCCD_VERSION_MINOR }, + { "patch", IRCCD_VERSION_PATCH }, +#if defined(IRCCD_HAVE_JS) + { "javascript", true }, +#endif +#if defined(IRCCD_HAVE_SSL) + { "ssl", true }, +#endif + }); + + const auto self = shared_from_this(); + + write(greetings, [this, self, handler] (auto code) { + auto parent = parent_.lock(); + + if (!parent) + return; + + if (code) { + handler(std::move(code)); + return; + } + + if (!parent->get_password().empty()) + auth(std::move(handler)); + else { + state_ = state::ready; + parent->get_clients().insert(self); + handler(std::move(code)); + } + }); +} + void transport_client::read(stream::recv_handler handler) { assert(handler);
--- a/libirccd-daemon/irccd/daemon/transport_client.hpp Mon Feb 11 20:30:00 2019 +0100 +++ b/libirccd-daemon/irccd/daemon/transport_client.hpp Sun Feb 24 15:50:58 2019 +0100 @@ -45,6 +45,11 @@ class transport_client : public std::enable_shared_from_this<transport_client> { public: /** + * Handler for handshaking. + */ + using handshake_handler = std::function<void (std::error_code)>; + + /** * Client state. */ enum class state { @@ -59,6 +64,7 @@ std::shared_ptr<stream> stream_; std::deque<std::pair<nlohmann::json, stream::send_handler>> queue_; + void auth(handshake_handler); void flush(); void erase(); @@ -88,6 +94,14 @@ void set_state(state state) noexcept; /** + * Do greetings and authentication if required. + * + * This function should be called after a new client has been accepted + * by a transport_server. + */ + void handshake(handshake_handler); + + /** * Start receiving if not closed. * * Possible error codes:
--- a/libirccd-daemon/irccd/daemon/transport_server.cpp Mon Feb 11 20:30:00 2019 +0100 +++ b/libirccd-daemon/irccd/daemon/transport_server.cpp Sun Feb 24 15:50:58 2019 +0100 @@ -21,80 +21,11 @@ #include <cassert> #include <system_error> -#include <irccd/json_util.hpp> - -#include "bot.hpp" #include "transport_client.hpp" #include "transport_server.hpp" namespace irccd::daemon { -void transport_server::do_auth(std::shared_ptr<transport_client> client, accept_handler handler) -{ - assert(client); - assert(handler); - - client->read([this, client, handler] (auto code, auto message) { - if (code) { - handler(std::move(code), std::move(client)); - return; - } - - const json_util::deserializer doc(message); - const auto command = doc.get<std::string>("command"); - const auto password = doc.get<std::string>("password"); - - if (!command || *command != "auth") { - client->error(bot_error::auth_required); - code = bot_error::auth_required; - } else if (!password || *password != password_) { - client->error(bot_error::invalid_auth); - code = bot_error::invalid_auth; - } else { - clients_.insert(client); - client->set_state(transport_client::state::ready); - client->success("auth"); - code = bot_error::no_error; - } - - handler(std::move(code), std::move(client)); - }); -} - -void transport_server::do_greetings(std::shared_ptr<transport_client> client, accept_handler handler) -{ - assert(client); - assert(handler); - - const auto greetings = nlohmann::json({ - { "program", "irccd" }, - { "major", IRCCD_VERSION_MAJOR }, - { "minor", IRCCD_VERSION_MINOR }, - { "patch", IRCCD_VERSION_PATCH }, -#if defined(IRCCD_HAVE_JS) - { "javascript", true }, -#endif -#if defined(IRCCD_HAVE_SSL) - { "ssl", true }, -#endif - }); - - client->write(greetings, [this, client, handler] (auto code) { - if (code) { - handler(std::move(code), std::move(client)); - return; - } - - if (!password_.empty()) - do_auth(std::move(client), std::move(handler)); - else { - clients_.insert(client); - client->set_state(transport_client::state::ready); - handler(std::move(code), std::move(client)); - } - }); -} - transport_server::transport_server(std::unique_ptr<acceptor> acceptor) noexcept : acceptor_(std::move(acceptor)) { @@ -124,15 +55,10 @@ void transport_server::accept(accept_handler handler) { acceptor_->accept([this, handler] (auto code, auto stream) { - if (code) { + if (code) handler(code, nullptr); - return; - } - - do_greetings( - std::make_shared<transport_client>(shared_from_this(), std::move(stream)), - std::move(handler) - ); + else + handler(code, std::make_shared<transport_client>(shared_from_this(), std::move(stream))); }); }
--- a/libirccd-daemon/irccd/daemon/transport_service.cpp Mon Feb 11 20:30:00 2019 +0100 +++ b/libirccd-daemon/irccd/daemon/transport_service.cpp Sun Feb 24 15:50:58 2019 +0100 @@ -32,7 +32,7 @@ namespace irccd::daemon { -void transport_service::handle_command(std::shared_ptr<transport_client> tc, const nlohmann::json& object) +void transport_service::handle_command(std::shared_ptr<transport_client> client, const nlohmann::json& object) { assert(object.is_object()); @@ -40,7 +40,7 @@ const auto name = doc.get<std::string>("command"); if (!name) { - tc->error(bot_error::invalid_message); + client->error(bot_error::invalid_message); return; } @@ -49,12 +49,12 @@ }); if (cmd == commands_.end()) - tc->error(bot_error::invalid_command, *name); + client->error(bot_error::invalid_command, *name); else { try { - (*cmd)->exec(bot_, *tc, doc); + (*cmd)->exec(bot_, *client, doc); } catch (const std::system_error& ex) { - tc->error(ex.code(), (*cmd)->get_name()); + client->error(ex.code(), (*cmd)->get_name()); } catch (const std::exception& ex) { bot_.get_log().warning("transport", "") << "unknown error not reported: " @@ -63,23 +63,23 @@ } } -void transport_service::do_recv(std::shared_ptr<transport_client> tc) +void transport_service::recv(std::shared_ptr<transport_client> client) { - tc->read([this, tc] (auto code, auto json) { + client->read([this, client] (auto code, auto json) { switch (static_cast<std::errc>(code.value())) { case std::errc::not_connected: bot_.get_log().info("transport", "") << "client disconnected" << std::endl; break; case std::errc::invalid_argument: - tc->error(bot_error::invalid_message); + client->error(bot_error::invalid_message); break; default: // Other errors. if (!code) { - handle_command(tc, json); + handle_command(client, json); - if (tc->get_state() == transport_client::state::ready) - do_recv(std::move(tc)); + if (client->get_state() == transport_client::state::ready) + recv(std::move(client)); } break; @@ -87,12 +87,25 @@ }); } -void transport_service::do_accept(transport_server& ts) +void transport_service::handshake(std::shared_ptr<transport_client> client) +{ + client->handshake([this, client] (auto code) { + if (code) + bot_.get_log().warning("transport", "") + << "error while handshaking: " << code.message() << std::endl; + else { + bot_.get_log().info("transport", "") << "client ready" << std::endl; + recv(std::move(client)); + } + }); +} + +void transport_service::accept(transport_server& ts) { ts.accept([this, &ts] (auto code, auto client) { if (!code) { - do_accept(ts); - do_recv(std::move(client)); + accept(ts); + handshake(std::move(client)); bot_.get_log().info("transport", "") << "new client connected" << std::endl; } @@ -120,7 +133,7 @@ { assert(ts); - do_accept(*ts); + accept(*ts); servers_.push_back(std::move(ts)); }
--- a/libirccd-daemon/irccd/daemon/transport_service.hpp Mon Feb 11 20:30:00 2019 +0100 +++ b/libirccd-daemon/irccd/daemon/transport_service.hpp Sun Feb 24 15:50:58 2019 +0100 @@ -65,8 +65,9 @@ servers servers_; void handle_command(std::shared_ptr<transport_client>, const nlohmann::json&); - void do_recv(std::shared_ptr<transport_client>); - void do_accept(transport_server&); + void recv(std::shared_ptr<transport_client>); + void handshake(std::shared_ptr<transport_client>); + void accept(transport_server&); public: /**
--- a/tests/src/libirccd-daemon/CMakeLists.txt Mon Feb 11 20:30:00 2019 +0100 +++ b/tests/src/libirccd-daemon/CMakeLists.txt Sun Feb 24 15:50:58 2019 +0100 @@ -51,3 +51,4 @@ add_subdirectory(rule-util) add_subdirectory(server) add_subdirectory(server-util) +add_subdirectory(transports)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-daemon/transports/CMakeLists.txt Sun Feb 24 15:50:58 2019 +0100 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2019 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME transports + SOURCES main.cpp + LIBRARIES libirccd +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libirccd-daemon/transports/main.cpp Sun Feb 24 15:50:58 2019 +0100 @@ -0,0 +1,67 @@ +/* + * main.cpp -- test server object + * + * Copyright (c) 2013-2019 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "transports" +#include <boost/test/unit_test.hpp> + +#include <irccd/daemon/transport_server.hpp> + +namespace asio = boost::asio; + +namespace irccd::daemon { + +namespace { + +BOOST_AUTO_TEST_CASE(fix_995) +{ + asio::io_context ctx; + asio::ip::tcp::socket cl1(ctx); + asio::ip::tcp::socket cl2(ctx); + + /* + * a server that waits for authentication, the client does not send + * anything the handler will never be executed. + */ + auto acc = std::make_unique<ip_acceptor>(ctx, "*", 0, true, false); + auto ep = acc->get_acceptor().local_endpoint(); + auto tpt = std::make_shared<transport_server>(std::move(acc)); + auto accepted = false; + auto connected1 = false; + auto connected2 = false; + + tpt->set_password("test"); + tpt->accept([&accepted] (auto, auto) { + accepted = true; + }); + cl1.async_connect(ep, [&connected1] (auto) { + connected1 = true; + }); + cl2.async_connect(ep, [&connected2] (auto) { + connected2 = true; + }); + + ctx.run(); + + BOOST_TEST(accepted); + BOOST_TEST(connected1); + BOOST_TEST(connected2); +} + +} // !namespace + +} // !irccd::daemon