From 876c03b7eb4c68f165687aa918086926d21b4324 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Tue, 26 Apr 2022 23:21:41 +0300 Subject: [PATCH 01/31] introduce connection initiator --- CMakeLists.txt | 1 + src/model/device_id.cpp | 9 +- src/model/device_id.h | 4 +- src/net/initiator_actor.cpp | 189 +++++++++++++++++++++ src/net/initiator_actor.h | 99 +++++++++++ src/net/peer_actor.cpp | 27 +-- src/net/peer_actor.h | 2 - src/transport/base.h | 3 +- src/transport/stream.cpp | 32 ++-- src/transport/stream.h | 7 +- tests/077-initiator.cpp | 322 ++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 4 + 12 files changed, 656 insertions(+), 43 deletions(-) create mode 100644 src/net/initiator_actor.cpp create mode 100644 src/net/initiator_actor.h create mode 100644 tests/077-initiator.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e8a68a0d..91a32510 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,7 @@ add_library(syncspirit_lib src/net/dialer_actor.cpp src/net/global_discovery_actor.cpp src/net/http_actor.cpp + src/net/initiator_actor.cpp src/net/local_discovery_actor.cpp src/net/messages.cpp src/net/net_supervisor.cpp diff --git a/src/model/device_id.cpp b/src/model/device_id.cpp index 0a5a802a..19c26945 100644 --- a/src/model/device_id.cpp +++ b/src/model/device_id.cpp @@ -21,23 +21,20 @@ device_id_t::device_id_t(std::string_view value_, std::string_view sha256_) noex std::copy(sha256_.begin(), sha256_.end(), hash + 1); } -device_id_t::device_id_t(device_id_t &&other) noexcept { - *this = std::move(other); -} +device_id_t::device_id_t(device_id_t &&other) noexcept { *this = std::move(other); } -device_id_t& device_id_t::operator=(device_id_t &&other) noexcept { +device_id_t &device_id_t::operator=(device_id_t &&other) noexcept { value = std::move(other.value); std::copy(other.hash, other.hash + data_length, hash); return *this; } -device_id_t& device_id_t::operator=(const device_id_t &other) noexcept { +device_id_t &device_id_t::operator=(const device_id_t &other) noexcept { value = other.value; std::copy(other.hash, other.hash + data_length, hash); return *this; } - std::string_view device_id_t::get_short() const noexcept { return std::string_view(value.data(), DASH_INT); } std::optional device_id_t::from_string(std::string_view value) noexcept { diff --git a/src/model/device_id.h b/src/model/device_id.h index 1b22557d..84faca25 100644 --- a/src/model/device_id.h +++ b/src/model/device_id.h @@ -33,8 +33,8 @@ struct SYNCSPIRIT_API device_id_t { device_id_t(device_id_t &&other) noexcept; device_id_t(const device_id_t &other) = default; - device_id_t& operator=(device_id_t &&other) noexcept; - device_id_t& operator=(const device_id_t &other) noexcept; + device_id_t &operator=(device_id_t &&other) noexcept; + device_id_t &operator=(const device_id_t &other) noexcept; bool operator==(const device_id_t &other) const noexcept { return get_sha256() == other.get_sha256(); } bool operator!=(const device_id_t &other) const noexcept { return !(*this == other); } diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp new file mode 100644 index 00000000..2b31fb1c --- /dev/null +++ b/src/net/initiator_actor.cpp @@ -0,0 +1,189 @@ +#include "initiator_actor.h" +#include "constants.h" +#include "names.h" +#include "utils/error_code.h" +#include +#include + +using namespace syncspirit::net; + +namespace { +namespace resource { +r::plugin::resource_id_t initializing = 0; +r::plugin::resource_id_t resolving = 1; +r::plugin::resource_id_t connect = 2; +r::plugin::resource_id_t handshake = 3; +} // namespace resource +} // namespace + +initiator_actor_t::initiator_actor_t(config_t &cfg) + : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, uris{cfg.uris}, ssl_pair{*cfg.ssl_pair}, + sock(std::move(cfg.sock)) { + log = utils::get_logger("net.initator"); + active = !sock.has_value(); +} + +void initiator_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { + r::actor_base_t::configure(plugin); + + plugin.with_casted([&](auto &p) { + if (active) { + auto value = fmt::format("init/active:{}", peer_device_id.get_short()); + p.set_identity(value, false); + } else { + auto ep = sock.value().remote_endpoint(); + auto value = fmt::format("init/passive:{}", ep); + p.set_identity(value, false); + } + }); + plugin.with_casted([&](auto &p) { + p.subscribe_actor(&initiator_actor_t::on_resolve); + resources->acquire(resource::initializing); + if (active) { + initiate_active(); + } else { + initiate_passive(); + } + }); + plugin.with_casted( + [&](auto &p) { p.discover_name(names::resolver, resolver).link(false); }); +} + +void initiator_actor_t::initiate_active() noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + + while (uri_idx < uris.size()) { + auto &uri = uris[uri_idx++]; + if (uri.proto == "tcp") { + auto sup = static_cast(supervisor); + auto trans = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri); + initiate(std::move(trans), uri); + return; + } else { + LOG_DEBUG(log, "{}, unsupported proto '{}' for the url '{}'", identity, uri.proto, uri.full); + } + } + + LOG_TRACE(log, "{}, try_next_uri, no way to connect found, shut down", identity); + auto ec = utils::make_error_code(utils::error_code_t::connection_impossible); + do_shutdown(make_error(ec)); +} + +void initiator_actor_t::initiate_passive() noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + + auto sup = static_cast(supervisor); + transport = transport::initiate_tls_passive(*sup, ssl_pair, std::move(sock.value())); + initiate_handshake(); +} + +void initiator_actor_t::on_start() noexcept { + r::actor_base_t::on_start(); + LOG_TRACE(log, "{}, on_start", identity); + auto &addr = supervisor->get_address(); + send(addr, std::move(transport), peer_device_id); + do_shutdown(); +} + +void initiator_actor_t::shutdown_start() noexcept { + LOG_TRACE(log, "{}, shutdown_start", identity); + if (resources->has(resource::initializing)) { + resources->release(resource::initializing); + } + if (resources->has(resource::connect)) { + transport->cancel(); + } + if (resources->has(resource::handshake)) { + transport->cancel(); + } + r::actor_base_t::shutdown_start(); +} + +void initiator_actor_t::shutdown_finish() noexcept { + LOG_TRACE(log, "{}, shutdown_finish", identity); + r::actor_base_t::shutdown_finish(); +} + +void initiator_actor_t::initiate(transport::stream_sp_t stream, const utils::URI &uri) noexcept { + transport = std::move(stream); + LOG_TRACE(log, "{}, resolving {} (transport = {})", identity, uri.full, (void *)transport.get()); + pt::time_duration resolve_timeout = init_timeout / 2; + auto port = std::to_string(uri.port); + request(resolver, uri.host, port).send(resolve_timeout); + resources->acquire(resource::resolving); +} + +void initiator_actor_t::on_resolve(message::resolve_response_t &res) noexcept { + LOG_TRACE(log, "{}, on_resolve", identity); + resources->release(resource::resolving); + if (state > r::state_t::OPERATIONAL) { + return; + } + + auto &ee = res.payload.ee; + if (ee) { + LOG_WARN(log, "{}, on_resolve error : {}", identity, ee->message()); + return initiate_active(); + } + + auto &addresses = res.payload.res->results; + transport::connect_fn_t on_connect = [&](auto arg) { this->on_connect(arg); }; + transport::error_fn_t on_error = [&](auto arg) { this->on_io_error(arg, resource::connect); }; + transport->async_connect(addresses, on_connect, on_error); + resources->acquire(resource::connect); +} + +void initiator_actor_t::on_io_error(const sys::error_code &ec, r::plugin::resource_id_t resource) noexcept { + LOG_TRACE(log, "{}, on_io_error: {}", identity, ec.message()); + resources->release(resource); + if (ec != asio::error::operation_aborted) { + LOG_WARN(log, "{}, on_io_error: {}", identity, ec.message()); + } + if (state < r::state_t::SHUTTING_DOWN) { + if (!connected && active) { + initiate_active(); + } else { + connected = false; + LOG_DEBUG(log, "{}, on_io_error, initiating shutdown...", identity); + do_shutdown(make_error(ec)); + } + } +} + +void initiator_actor_t::on_connect(resolve_it_t) noexcept { + LOG_TRACE(log, "{}, on_connect, device_id = {}", identity, peer_device_id.get_short()); + initiate_handshake(); + resources->release(resource::connect); +} + +void initiator_actor_t::initiate_handshake() noexcept { + LOG_TRACE(log, "{}, connected, initializing handshake", identity); + connected = true; + transport::handshake_fn_t handshake_fn([&](auto &&...args) { on_handshake(args...); }); + transport::error_fn_t error_fn([&](auto arg) { on_io_error(arg, resource::handshake); }); + transport->async_handshake(handshake_fn, error_fn); + resources->acquire(resource::handshake); +} + +void initiator_actor_t::on_handshake(bool valid_peer, utils::x509_t &cert, const tcp::endpoint &peer_endpoint, + const model::device_id_t *peer_device) noexcept { + resources->release(resource::handshake); + if (!peer_device) { + LOG_WARN(log, "{}, on_handshake, missing peer device id", identity); + auto ec = utils::make_error_code(utils::error_code_t::missing_device_id); + return do_shutdown(make_error(ec)); + } + + auto cert_name = utils::get_common_name(cert); + if (!cert_name) { + LOG_WARN(log, "{}, on_handshake, can't get certificate name: {}", identity, cert_name.error().message()); + auto ec = utils::make_error_code(utils::error_code_t::missing_cn); + return do_shutdown(make_error(ec)); + } + LOG_TRACE(log, "{}, on_handshake, valid = {}, issued by {}", identity, valid_peer, cert_name.value()); + resources->release(resource::initializing); +} diff --git a/src/net/initiator_actor.h b/src/net/initiator_actor.h new file mode 100644 index 00000000..16029b59 --- /dev/null +++ b/src/net/initiator_actor.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include "model/cluster.h" +#include "config/bep.h" +#include "messages.h" +#include "utils/log.h" +#include "transport/stream.h" + +namespace syncspirit::net { + +namespace r = rotor; + +struct initiator_actor_config_t : public r::actor_config_t { + model::device_id_t peer_device_id; + utils::uri_container_t uris; + const utils::key_pair_t *ssl_pair; + std::optional sock; +}; + +template struct initiator_actor_config_builder_t : r::actor_config_builder_t { + using builder_t = typename Actor::template config_builder_t; + using parent_t = r::actor_config_builder_t; + using parent_t::parent_t; + + builder_t &&peer_device_id(const model::device_id_t &value) &&noexcept { + parent_t::config.peer_device_id = value; + return std::move(*static_cast(this)); + } + + builder_t &&uris(const utils::uri_container_t &value) &&noexcept { + parent_t::config.uris = value; + return std::move(*static_cast(this)); + } + + builder_t &&ssl_pair(const utils::key_pair_t *value) &&noexcept { + parent_t::config.ssl_pair = value; + return std::move(*static_cast(this)); + } + + builder_t &&sock(tcp_socket_t value) &&noexcept { + parent_t::config.sock = std::move(value); + return std::move(*static_cast(this)); + } +}; + +struct initiator_actor_t : r::actor_base_t { + using config_t = initiator_actor_config_t; + template using config_builder_t = initiator_actor_config_builder_t; + + initiator_actor_t(config_t &config); + void configure(r::plugin::plugin_base_t &plugin) noexcept override; + void on_start() noexcept override; + void shutdown_start() noexcept override; + void shutdown_finish() noexcept override; + + private: + using resolve_it_t = payload::address_response_t::resolve_results_t::iterator; + + void initiate_passive() noexcept; + void initiate_active() noexcept; + void initiate(transport::stream_sp_t stream, const utils::URI &uri) noexcept; + void initiate_handshake() noexcept; + + void on_resolve(message::resolve_response_t &res) noexcept; + void on_connect(resolve_it_t) noexcept; + void on_io_error(const sys::error_code &ec, r::plugin::resource_id_t resource) noexcept; + void on_handshake(bool valid_peer, utils::x509_t &peer_cert, const tcp::endpoint &peer_endpoint, + const model::device_id_t *peer_device) noexcept; + + model::device_id_t peer_device_id; + utils::uri_container_t uris; + const utils::key_pair_t &ssl_pair; + std::optional sock; + + transport::stream_sp_t transport; + r::address_ptr_t resolver; + size_t uri_idx = 0; + utils::logger_t log; + bool connected = false; + bool active = false; +}; + +namespace payload { + +struct peer_connected_t { + transport::stream_sp_t transport; + model::device_id_t peer_device_id; +}; + +} // namespace payload + +namespace message { + +using peer_connected_t = r::message_t; + +} + +} // namespace syncspirit::net diff --git a/src/net/peer_actor.cpp b/src/net/peer_actor.cpp index 1c73c1f5..732320ce 100644 --- a/src/net/peer_actor.cpp +++ b/src/net/peer_actor.cpp @@ -81,11 +81,8 @@ void peer_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { void peer_actor_t::instantiate_transport() noexcept { if (sock) { - transport::ssl_junction_t ssl{peer_device_id, &ssl_pair, false, ""}; - auto uri = utils::parse("tcp://0.0.0.0/").value(); auto sup = static_cast(supervisor); - transport::transport_config_t cfg{transport::ssl_option_t(ssl), uri, *sup, std::move(sock)}; - transport = transport::initiate_stream(cfg); + transport = transport::initiate_tls_passive(*sup, ssl_pair, std::move(sock.value())); auto timeout = r::pt::milliseconds{bep_config.connect_timeout}; timer_request = start_timer(timeout, *this, &peer_actor_t::on_timer); resources->acquire(resource::io_timer); @@ -97,18 +94,15 @@ void peer_actor_t::instantiate_transport() noexcept { } void peer_actor_t::try_next_uri() noexcept { - transport::ssl_junction_t ssl{peer_device_id, &ssl_pair, false, constants::protocol_name}; while (++uri_idx < (std::int32_t)uris.size()) { auto &uri = uris[uri_idx]; auto sup = static_cast(supervisor); // log->warn("url: {}", uri.full); - transport::transport_config_t cfg{transport::ssl_option_t(ssl), uri, *sup, {}}; - auto result = transport::initiate_stream(cfg); - if (result) { - initiate(std::move(result), uri); - resources->release(resource::uris); - return; - } + auto result = + transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri, false, constants::protocol_name); + initiate(std::move(result), uri); + resources->release(resource::uris); + return; } LOG_TRACE(log, "{}, try_next_uri, no way to conenct found, shut down", identity); @@ -268,15 +262,6 @@ void peer_actor_t::on_handshake(bool valid_peer, utils::x509_t &cert, const tcp: read_action = &peer_actor_t::read_hello; } -#if 0 -void peer_actor_t::on_handshake_error(sys::error_code ec) noexcept { - resources->release(resource::io); - if (ec != asio::error::operation_aborted) { - LOG_WARN(log, "{}, on_handshake_error: {}", identity, ec.message()); - } -} -#endif - void peer_actor_t::read_more() noexcept { if (state > r::state_t::OPERATIONAL) { return; diff --git a/src/net/peer_actor.h b/src/net/peer_actor.h index a4893915..ee91ee71 100644 --- a/src/net/peer_actor.h +++ b/src/net/peer_actor.h @@ -22,7 +22,6 @@ struct peer_actor_config_t : public r::actor_config_t { model::device_id_t peer_device_id; utils::uri_container_t uris; std::optional sock; - std::optional peer_identity; const utils::key_pair_t *ssl_pair; config::bep_config_t bep_config; r::address_ptr_t coordinator; @@ -123,7 +122,6 @@ struct SYNCSPIRIT_API peer_actor_t : public r::actor_base_t { void initiate(transport::stream_sp_t tran, const utils::URI &url) noexcept; void on_handshake(bool valid_peer, utils::x509_t &peer_cert, const tcp::endpoint &peer_endpoint, const model::device_id_t *peer_device) noexcept; - void on_handshake_error(sys::error_code ec) noexcept; void on_timer(r::request_id_t, bool cancelled) noexcept; void read_more() noexcept; void push_write(fmt::memory_buffer &&buff, bool final) noexcept; diff --git a/src/transport/base.h b/src/transport/base.h index 9f740da2..431c0b4a 100644 --- a/src/transport/base.h +++ b/src/transport/base.h @@ -18,6 +18,7 @@ namespace syncspirit::transport { namespace asio = boost::asio; namespace sys = boost::system; +namespace ra = rotor::asio; using tcp = asio::ip::tcp; @@ -44,7 +45,7 @@ using ssl_option_t = std::optional; struct transport_config_t { ssl_option_t ssl_junction; utils::URI uri; - rotor::asio::supervisor_asio_t &supervisor; + ra::supervisor_asio_t &supervisor; std::optional sock; }; diff --git a/src/transport/stream.cpp b/src/transport/stream.cpp index 6cf9eb01..5c694e8c 100644 --- a/src/transport/stream.cpp +++ b/src/transport/stream.cpp @@ -15,18 +15,30 @@ template struct steam_impl_t : base_impl_t, interface_t(cfg); +} + +stream_sp_t initiate_tls_active(ra::supervisor_asio_t &sup, const utils::key_pair_t &my_keys, + const model::device_id_t &expected_peer, const utils::URI &uri, bool sni, + std::string_view alpn) noexcept { + ssl_junction_t ssl{expected_peer, &my_keys, sni, alpn}; + transport_config_t cfg{ssl_option_t(ssl), uri, sup, {}}; + return new steam_impl_t(cfg); +} + stream_sp_t initiate_stream(transport_config_t &config) noexcept { - auto &proto = config.uri.proto; - if (proto == "tcp") { - if (config.ssl_junction) { - using socket_t = ssl_socket_t; - return new steam_impl_t(config); - } else { - using socket_t = tcp_socket_t; - return new steam_impl_t(config); - } + assert(config.uri.proto == "tcp"); + if (config.ssl_junction) { + using socket_t = ssl_socket_t; + return new steam_impl_t(config); + } else { + using socket_t = tcp_socket_t; + return new steam_impl_t(config); } - return stream_sp_t(); } } // namespace syncspirit::transport diff --git a/src/transport/stream.h b/src/transport/stream.h index 3e6b4ddd..23f53e0a 100644 --- a/src/transport/stream.h +++ b/src/transport/stream.h @@ -22,6 +22,11 @@ struct stream_base_t : model::arc_base_t, stream_interface_t { using stream_sp_t = model::intrusive_ptr_t; -stream_sp_t initiate_stream(transport_config_t &config) noexcept; +stream_sp_t initiate_tls_active(ra::supervisor_asio_t &supervisor, const utils::key_pair_t &my_keys, + const model::device_id_t &expected_peer, const utils::URI &uri, bool sni = false, + std::string_view alpn = "") noexcept; +stream_sp_t initiate_tls_passive(ra::supervisor_asio_t &supervisor, const utils::key_pair_t &my_keys, + tcp::socket sock) noexcept; +// stream_sp_t initiate_stream(transport_config_t &config) noexcept; } // namespace syncspirit::transport diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp new file mode 100644 index 00000000..218b36c0 --- /dev/null +++ b/tests/077-initiator.cpp @@ -0,0 +1,322 @@ +#include "catch.hpp" +#include "test-utils.h" +#include "access.h" + +#include "utils/tls.h" +#include "model/cluster.h" +#include "net/names.h" +#include "net/initiator_actor.h" +#include "net/resolver_actor.h" +#include "transport/stream.h" +#include + +using namespace syncspirit; +using namespace syncspirit::test; +using namespace syncspirit::model; +using namespace syncspirit::net; + +namespace asio = boost::asio; +namespace sys = boost::system; +namespace r = rotor; +namespace ra = r::asio; + +using configure_callback_t = std::function; + +auto timeout = r::pt::time_duration{r::pt::millisec{1500}}; +auto host = "127.0.0.1"; + +struct supervisor_t : ra::supervisor_asio_t { + using ra::supervisor_asio_t::supervisor_asio_t; + + void configure(r::plugin::plugin_base_t &plugin) noexcept override { + ra::supervisor_asio_t::configure(plugin); + plugin.with_casted( + [&](auto &p) { p.register_name(names::coordinator, get_address()); }); + if (configure_callback) { + configure_callback(plugin); + } + } + + void shutdown_finish() noexcept override { + ra::supervisor_asio_t::shutdown_finish(); + if (acceptor) { + acceptor->cancel(); + } + } + + auto get_state() noexcept { return state; } + + asio::ip::tcp::acceptor *acceptor = nullptr; + configure_callback_t configure_callback; +}; + +using supervisor_ptr_t = r::intrusive_ptr_t; +using actor_ptr_t = r::intrusive_ptr_t; + +struct fixture_t { + using acceptor_t = asio::ip::tcp::acceptor; + using msg_ptr_t = r::intrusive_ptr_t; + + fixture_t() noexcept : ctx(io_ctx), acceptor(io_ctx), peer_sock(io_ctx) { + utils::set_default("trace"); + log = utils::get_logger("fixture"); + } + + void run() noexcept { + auto strand = std::make_shared(io_ctx); + sup = ctx.create_supervisor().strand(strand).timeout(timeout).create_registry().finish(); + sup->configure_callback = [&](r::plugin::plugin_base_t &plugin) { + plugin.template with_casted([&](auto &p) { + using msg_t = message::peer_connected_t; + p.subscribe_actor(r::lambda([&](msg_t &msg) { + message = &msg; + LOG_INFO(log, "received message::peer_connected_t"); + })); + }); + }; + sup->start(); + + sup->create_actor().resolve_timeout(timeout / 2).timeout(timeout).finish(); + sup->do_process(); + + auto ep = asio::ip::tcp::endpoint(asio::ip::make_address(host), 0); + acceptor.open(ep.protocol()); + acceptor.bind(ep); + acceptor.listen(); + listening_ep = acceptor.local_endpoint(); + peer_uri = utils::parse(get_uri(listening_ep)).value(); + log->debug("listening on {}", peer_uri.full); + initiate_accept(); + + my_keys = utils::generate_pair("me").value(); + peer_keys = utils::generate_pair("peer").value(); + + my_device = model::device_id_t::from_cert(my_keys.cert_data).value(); + peer_device = model::device_id_t::from_cert(peer_keys.cert_data).value(); + + main(); + } + + virtual void initiate_accept() noexcept { + acceptor.async_accept(peer_sock, [this](auto ec) { this->accept(ec); }); + sup->acceptor = &acceptor; + } + + virtual std::string get_uri(const asio::ip::tcp::endpoint &endpoint) noexcept { + return fmt::format("tcp://{}", listening_ep); + } + + virtual void accept(const sys::error_code &ec) noexcept { + LOG_INFO(log, "accept, ec: {}", ec.message()); + peer_trans = transport::initiate_tls_passive(*sup, peer_keys, std::move(peer_sock)); + initiate_peer_handshake(); + } + + virtual void initiate_peer_handshake() noexcept { + transport::handshake_fn_t handshake_fn = [this](bool valid_peer, utils::x509_t &cert, + const tcp::endpoint &peer_endpoint, + const model::device_id_t *peer_device) { + valid_handshake = true; + LOG_INFO(log, "peer handshake"); + }; + transport::error_fn_t on_error = [](const auto &) {}; + peer_trans->async_handshake(handshake_fn, on_error); + } + + void initiate_active() noexcept { + tcp::resolver resolver(io_ctx); + auto addresses = resolver.resolve(host, std::to_string(listening_ep.port())); + peer_trans = transport::initiate_tls_active(*sup, peer_keys, my_device, peer_uri); + + transport::error_fn_t on_error = [&](auto &ec) { + LOG_WARN(log, "initiate_active/connect, err: {}", ec.message()); + }; + transport::connect_fn_t on_connect = [&](auto arg) { + LOG_INFO(log, "initiate_active/peer connect"); + active_connect(); + }; + + peer_trans->async_connect(addresses, on_connect, on_error); + } + + virtual void active_connect() { + transport::handshake_fn_t handshake_fn = [this](bool valid_peer, utils::x509_t &cert, + const tcp::endpoint &peer_endpoint, + const model::device_id_t *peer_device) { + valid_handshake = true; + LOG_INFO(log, "test_passive_success/peer handshake"); + }; + transport::error_fn_t on_hs_error = [&](const auto &ec) { + LOG_WARN(log, "test_passive_success/peer handshake, err: {}", ec.message()); + }; + peer_trans->async_handshake(handshake_fn, on_hs_error); + } + + virtual void main() noexcept {} + + virtual actor_ptr_t create_actor() noexcept { + return sup->create_actor() + .timeout(timeout) + .peer_device_id(peer_device) + .uris({peer_uri}) + .ssl_pair(&my_keys) + .escalate_failure() + .finish(); + } + + virtual actor_ptr_t create_passive_actor() noexcept { + return sup->create_actor() + .timeout(timeout) + .sock(std::move(peer_sock)) + .ssl_pair(&my_keys) + .escalate_failure() + .finish(); + } + + asio::io_context io_ctx{1}; + ra::system_context_asio_t ctx; + acceptor_t acceptor; + supervisor_ptr_t sup; + asio::ip::tcp::endpoint listening_ep; + utils::logger_t log; + asio::ip::tcp::socket peer_sock; + config::bep_config_t bep_config; + utils::key_pair_t my_keys; + utils::key_pair_t peer_keys; + utils::URI peer_uri; + model::device_id_t my_device; + model::device_id_t peer_device; + transport::stream_sp_t peer_trans; + msg_ptr_t message; + bool valid_handshake = false; +}; + +void test_connect_timeout() { + struct F : fixture_t { + void initiate_accept() noexcept override {} + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!message); + } + }; + F().run(); +} + +void test_handshake_timeout() { + struct F : fixture_t { + + void accept(const sys::error_code &ec) noexcept override { LOG_INFO(log, "accept (ignoring)", ec.message()); } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!message); + } + }; + F().run(); +} + +void test_connection_refused() { + struct F : fixture_t { + + std::string get_uri(const asio::ip::tcp::endpoint &) noexcept override { + return fmt::format("tcp://{}:0", host); + } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!message); + } + }; + F().run(); +} + +void test_resolve_failure() { + struct F : fixture_t { + + std::string get_uri(const asio::ip::tcp::endpoint &) noexcept override { return "tcp://x.example.com"; } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!message); + } + }; + F().run(); +} + +void test_success() { + struct F : fixture_t { + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::OPERATIONAL); + CHECK(message); + CHECK(valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + } + }; + F().run(); +} + +void test_passive_success() { + struct F : fixture_t { + + actor_ptr_t act; + + void accept(const sys::error_code &ec) noexcept override { + LOG_INFO(log, "test_passive_success/accept, ec: {}", ec.message()); + act = create_passive_actor(); + } + + void main() noexcept override { + initiate_active(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::OPERATIONAL); + CHECK(message); + CHECK(valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + } + }; + F().run(); +} + +void test_passive_timeout() { + struct F : fixture_t { + + actor_ptr_t act; + + void accept(const sys::error_code &ec) noexcept override { + LOG_INFO(log, "test_passive_timeout/accept, ec: {}", ec.message()); + act = create_passive_actor(); + } + + void active_connect() noexcept override { LOG_INFO(log, "test_passive_timeout/active_connect NOOP"); } + + void main() noexcept override { + initiate_active(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!message); + } + }; + F().run(); +} + +REGISTER_TEST_CASE(test_connect_timeout, "test_connect_timeout", "[initiator]"); +REGISTER_TEST_CASE(test_handshake_timeout, "test_handshake_timeout", "[initiator]"); +REGISTER_TEST_CASE(test_connection_refused, "test_connection_refused", "[initiator]"); +REGISTER_TEST_CASE(test_resolve_failure, "test_resolve_failure", "[initiator]"); +REGISTER_TEST_CASE(test_success, "test_success", "[initiator]"); +REGISTER_TEST_CASE(test_passive_success, "test_passive_success", "[initiator]"); +REGISTER_TEST_CASE(test_passive_timeout, "test_passive_timeout", "[initiator]"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 10ca5982..6c91f5bd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -154,3 +154,7 @@ add_test(075-controller "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/075-controller") add_executable(076-scan_actor 076-scan_actor.cpp $<$:win32-resource.rc>) target_link_libraries(076-scan_actor syncspirit_test_lib) add_test(076-scan_actor "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/076-scan_actor") + +add_executable(077-initiator 077-initiator.cpp $<$:win32-resource.rc>) +target_link_libraries(077-initiator syncspirit_test_lib) +add_test(077-initiator "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/077-initiator") From 0f2f5a2002bbb3e04a75b31aa13a468fce8c1253 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Wed, 27 Apr 2022 20:56:20 +0300 Subject: [PATCH 02/31] add toolchain file --- misc/ubuntu14.04.toolchain | 8 ++++++++ syncspirit.toml | 18 +++++++++++++----- todo.txt | 27 +++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 misc/ubuntu14.04.toolchain diff --git a/misc/ubuntu14.04.toolchain b/misc/ubuntu14.04.toolchain new file mode 100644 index 00000000..d18c676b --- /dev/null +++ b/misc/ubuntu14.04.toolchain @@ -0,0 +1,8 @@ +set(TOOLCHAIN_HOME "/home/b/x-tools/x86_64-ubuntu14.04-linux-gnu") +set(CMAKE_C_COMPILER ${TOOLCHAIN_HOME}/bin/x86_64-ubuntu14.04-linux-gnu-gcc) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_HOME}/bin/x86_64-ubuntu14.04-linux-gnu-g++) +set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") +set(CMAKE_FIND_ROOT_PATH + ${TOOLCHAIN_HOME}/x86_64-ubuntu14.04-linux-gnu/sysroot/ + /home/b/development/cpp/syncspirit-cross/sysroot +) diff --git a/syncspirit.toml b/syncspirit.toml index 1d4ebf13..049fe1de 100644 --- a/syncspirit.toml +++ b/syncspirit.toml @@ -4,7 +4,7 @@ connect_timeout = 5000 request_timeout = 60000 rx_timeout = 300000 tx_timeout = 10000 -blocks_max_requested = 16 +blocks_max_requested = 8 [dialer] enabled = true @@ -16,7 +16,7 @@ temporally_timeout = 86400000 mru_size = 10 [db] -upper_limit = 0x400000000 +upper_limit = 0x40000000 uncommited_threshold = 150 [global_discovery] @@ -31,7 +31,7 @@ timeout = 4000 [local_discovery] enabled = true frequency = 10000 -port = 21026 +port = 21027 [[log]] name = 'default' @@ -40,7 +40,15 @@ sinks = ['stdout', 'file:/tmp/log.txt'] [[log]] name = 'net.db' -level = 'debug' +level = 'info' + +[[log]] +name = 'net.peer_actor' +level = 'trace' + +[[log]] +name = 'net.hasher.actor' +level = 'info' [main] default_location = '/tmp/syncspirit' @@ -54,5 +62,5 @@ discovery_attempts = 2 external_port = 22001 max_wait = 1 rx_buff_size = 65536 -debug = true +debug = false diff --git a/todo.txt b/todo.txt index 116e15fd..38a301ca 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,28 @@ -- дока - - configuration +- introduce initiator + -- connect_request_t + -- update_contact_t + -- relay_connect_request_t + -- relay_update_contact_t + + + device_lock + +=========== +5. разобраться с лог-левелами? + +1. ^ если в block iterator'е были проскипанные блоки (из-за того, что были залочены), то файл(!) надо добавить на рескан через некоторое время + + +3. скан-актор не детектит скаченные файлы + + +4. надо как-то понимать, что folder is up-to-date? + + + +- syncspirit-daemon: /home/b/development/cpp/syncspirit/src/net/dialer_actor.cpp:88: void syncspirit::net::dialer_actor_t::on_timer(rotor::request_id_t, bool): Assertion `it != redial_map.end()' failed. + +- 2022-03-28 20:47:54.664] [E/25546] fs::scan_actor, on_hash, file: my_label/Camera/VID_20210122_121411.mp4, error: fs::scan_actor request timeout + ===================== - идёт много file_clone, но нету скачки файла - check 4 symlinks test (scaner) ? From 59cad431da032d00dd999f21e6d954bfd3d80c91 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Wed, 27 Apr 2022 21:26:28 +0300 Subject: [PATCH 03/31] introduce device state --- src/model/device.cpp | 9 ++++++--- src/model/device.h | 8 +++++--- src/model/diff/peer/peer_state.cpp | 4 +++- src/net/peer_actor.cpp | 4 ++-- src/net/peer_supervisor.cpp | 2 +- tests/033-diffs-trivial.cpp | 6 +++--- 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/model/device.cpp b/src/model/device.cpp index 96df44ed..8be342f6 100644 --- a/src/model/device.cpp +++ b/src/model/device.cpp @@ -50,7 +50,8 @@ outcome::result device_t::create(const device_id_t &device_id, std device_t::device_t(const device_id_t &device_id_, std::string_view name_, std::string_view cert_name_) noexcept : id(std::move(device_id_)), name{name_}, compression{proto::Compression::METADATA}, cert_name{cert_name_}, - introducer{false}, auto_accept{false}, paused{false}, skip_introduction_removals{false}, online{false} {} + introducer{false}, auto_accept{false}, paused{false}, + skip_introduction_removals{false}, state{device_state_t::offline} {} void device_t::update(const db::Device &source) noexcept { assign(source); } @@ -82,14 +83,16 @@ std::string device_t::serialize() noexcept { return r.SerializeAsString(); } -void device_t::mark_online(bool value) noexcept { online = value; } +void device_t::update_state(device_state_t new_state) { state = new_state; } std::string_view device_t::get_key() const noexcept { return id.get_key(); } void device_t::assing_uris(const uris_t &uris_) noexcept { uris = uris_; } local_device_t::local_device_t(const device_id_t &device_id, std::string_view name, std::string_view cert_name) noexcept - : device_t(device_id, name, cert_name) {} + : device_t(device_id, name, cert_name) { + state = device_state_t::online; +} std::string_view local_device_t::get_key() const noexcept { return local_device_id.get_key(); } diff --git a/src/model/device.h b/src/model/device.h index c1cbfa83..a1d4e65c 100644 --- a/src/model/device.h +++ b/src/model/device.h @@ -20,6 +20,8 @@ namespace outcome = boost::outcome_v2; struct device_t; using device_ptr_t = intrusive_ptr_t; +enum class device_state_t { offline, dialing, online }; + struct SYNCSPIRIT_API device_t : arc_base_t { using uris_t = std::vector; using name_option_t = std::optional; @@ -35,8 +37,8 @@ struct SYNCSPIRIT_API device_t : arc_base_t { std::string serialize() noexcept; inline bool is_dynamic() const noexcept { return static_uris.empty(); } - void mark_online(bool value) noexcept; - inline bool is_online() const noexcept { return online; } + inline device_state_t get_state() const noexcept { return state; } + void update_state(device_state_t new_state); inline device_id_t &device_id() noexcept { return id; } inline const device_id_t &device_id() const noexcept { return id; } inline std::string_view get_name() const noexcept { return name; } @@ -66,7 +68,7 @@ struct SYNCSPIRIT_API device_t : arc_base_t { bool auto_accept; bool paused; bool skip_introduction_removals; - bool online = false; + device_state_t state = device_state_t::offline; }; struct local_device_t final : device_t { diff --git a/src/model/diff/peer/peer_state.cpp b/src/model/diff/peer/peer_state.cpp index ca20a77b..e9838699 100644 --- a/src/model/diff/peer/peer_state.cpp +++ b/src/model/diff/peer/peer_state.cpp @@ -16,9 +16,11 @@ peer_state_t::peer_state_t(cluster_t &cluster, std::string_view peer_id_, const } auto peer_state_t::apply_impl(cluster_t &cluster) const noexcept -> outcome::result { + using State = model::device_state_t; if (known) { auto peer = cluster.get_devices().by_sha256(peer_id); - peer->mark_online(online); + auto state = online ? State::online : State::offline; + peer->update_state(state); } return outcome::success(); } diff --git a/src/net/peer_actor.cpp b/src/net/peer_actor.cpp index 732320ce..dc10ee6c 100644 --- a/src/net/peer_actor.cpp +++ b/src/net/peer_actor.cpp @@ -381,7 +381,7 @@ void peer_actor_t::shutdown_finish() noexcept { if (handshaked) { auto sha256 = peer_device_id.get_sha256(); auto device = cluster->get_devices().by_sha256(sha256); - if (device && device->is_online()) { + if (device && device->get_state() == model::device_state_t::online) { auto diff = model::diff::cluster_diff_ptr_t(); diff = new model::diff::peer::peer_state_t(*cluster, sha256, address, false); send(coordinator, std::move(diff)); @@ -478,7 +478,7 @@ void peer_actor_t::read_hello(proto::message::message_t &&msg) noexcept { LOG_TRACE(log, "{}, read_hello, from {} ({} {})", identity, msg->device_name(), msg->client_name(), msg->client_version()); auto peer = cluster->get_devices().by_sha256(peer_device_id.get_sha256()); - if (peer && peer->is_online()) { + if (peer && peer->get_state() == model::device_state_t::online) { auto ec = utils::make_error_code(utils::error_code_t::already_connected); return do_shutdown(make_error(ec)); } diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index 949b6fd8..8b38476b 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -104,7 +104,7 @@ auto peer_supervisor_t::operator()(const model::diff::modify::update_contact_t & if (!diff.self && diff.known) { auto &devices = cluster->get_devices(); auto peer = devices.by_sha256(diff.device.get_sha256()); - if (!peer->is_online()) { + if (peer->get_state() != model::device_state_t::online) { auto &uris = diff.uris; auto connect_timeout = r::pt::milliseconds{bep_config.connect_timeout}; LOG_DEBUG(log, "{} initiating connection with {}", identity, peer->device_id()); diff --git a/tests/033-diffs-trivial.cpp b/tests/033-diffs-trivial.cpp index d0c350bf..6f6d389d 100644 --- a/tests/033-diffs-trivial.cpp +++ b/tests/033-diffs-trivial.cpp @@ -29,14 +29,14 @@ TEST_CASE("peer state update", "[model]") { rotor::address_ptr_t addr; auto diff = diff::cluster_diff_ptr_t(new diff::peer::peer_state_t(*cluster, peer_id.get_sha256(), addr, true)); - CHECK(peer_device->is_online() == false); + CHECK(peer_device->get_state() == model::device_state_t::offline); REQUIRE(diff->apply(*cluster)); - CHECK(peer_device->is_online() == true); + CHECK(peer_device->get_state() == model::device_state_t::online); diff = new diff::peer::peer_state_t(*cluster, peer_id.get_sha256(), addr, false); REQUIRE(diff->apply(*cluster)); - CHECK(peer_device->is_online() == false); + CHECK(peer_device->get_state() == model::device_state_t::offline); } TEST_CASE("with file", "[model]") { From 181eb66e6512cb147e1ae55fd032ec738b601c52 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Wed, 27 Apr 2022 22:23:32 +0300 Subject: [PATCH 04/31] more device state usages --- src/model/diff/peer/peer_state.cpp | 5 ++--- src/model/diff/peer/peer_state.h | 7 ++++--- src/net/cluster_supervisor.cpp | 5 ++--- src/net/dialer_actor.cpp | 8 ++++---- src/net/peer_actor.cpp | 7 ++++--- src/net/peer_supervisor.cpp | 6 +++--- tests/033-diffs-trivial.cpp | 5 +++-- tests/073-dialer.cpp | 4 ++-- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/model/diff/peer/peer_state.cpp b/src/model/diff/peer/peer_state.cpp index e9838699..fc2c5cbc 100644 --- a/src/model/diff/peer/peer_state.cpp +++ b/src/model/diff/peer/peer_state.cpp @@ -8,10 +8,10 @@ using namespace syncspirit::model::diff::peer; peer_state_t::peer_state_t(cluster_t &cluster, std::string_view peer_id_, const r::address_ptr_t &peer_addr_, - bool online_, std::string cert_name_, tcp::endpoint endpoint_, + model::device_state_t state_, std::string cert_name_, tcp::endpoint endpoint_, std::string_view client_name_) noexcept : peer_id{peer_id_}, peer_addr{peer_addr_}, cert_name{cert_name_}, endpoint{endpoint_}, - client_name{client_name_}, online{online_} { + client_name{client_name_}, state{state_} { known = (bool)cluster.get_devices().by_sha256(peer_id); } @@ -19,7 +19,6 @@ auto peer_state_t::apply_impl(cluster_t &cluster) const noexcept -> outcome::res using State = model::device_state_t; if (known) { auto peer = cluster.get_devices().by_sha256(peer_id); - auto state = online ? State::online : State::offline; peer->update_state(state); } return outcome::success(); diff --git a/src/model/diff/peer/peer_state.h b/src/model/diff/peer/peer_state.h index def58553..7a609200 100644 --- a/src/model/diff/peer/peer_state.h +++ b/src/model/diff/peer/peer_state.h @@ -5,6 +5,7 @@ #include #include "../cluster_diff.h" +#include "model/device.h" #include namespace syncspirit::model::diff::peer { @@ -14,8 +15,8 @@ using tcp = boost::asio::ip::tcp; struct SYNCSPIRIT_API peer_state_t final : cluster_diff_t { - peer_state_t(cluster_t &cluster, std::string_view peer_id_, const r::address_ptr_t &peer_addr_, bool online_, - std::string cert_name_ = {}, tcp::endpoint endpoint_ = {}, + peer_state_t(cluster_t &cluster, std::string_view peer_id_, const r::address_ptr_t &peer_addr_, + model::device_state_t state, std::string cert_name_ = {}, tcp::endpoint endpoint_ = {}, std::string_view client_name_ = {}) noexcept; outcome::result apply_impl(cluster_t &) const noexcept override; @@ -26,7 +27,7 @@ struct SYNCSPIRIT_API peer_state_t final : cluster_diff_t { std::string cert_name; tcp::endpoint endpoint; std::string client_name; - bool online; + model::device_state_t state; bool known; }; diff --git a/src/net/cluster_supervisor.cpp b/src/net/cluster_supervisor.cpp index 3f77e96b..3043ef85 100644 --- a/src/net/cluster_supervisor.cpp +++ b/src/net/cluster_supervisor.cpp @@ -61,9 +61,8 @@ void cluster_supervisor_t::on_model_update(model::message::model_update_t &messa auto cluster_supervisor_t::operator()(const model::diff::peer::peer_state_t &diff) noexcept -> outcome::result { if (!cluster->is_tainted() && diff.known) { auto peer = cluster->get_devices().by_sha256(diff.peer_id); - LOG_TRACE(log, "{}, visiting peer_state_t, {} is online: {}", identity, peer->device_id(), - (diff.online ? "yes" : "no")); - if (diff.online) { + LOG_TRACE(log, "{}, visiting peer_state_t, {}, state: {}", identity, peer->device_id(), (int)diff.state); + if (diff.state == model::device_state_t::online) { /* auto addr = */ create_actor() .request_pool(bep_config.rx_buff_size) diff --git a/src/net/dialer_actor.cpp b/src/net/dialer_actor.cpp index 2f26db92..1b0e5ce5 100644 --- a/src/net/dialer_actor.cpp +++ b/src/net/dialer_actor.cpp @@ -123,13 +123,13 @@ void dialer_actor_t::on_model_update(model::message::model_update_t &msg) noexce } } -auto dialer_actor_t::operator()(const model::diff::peer::peer_state_t &state) noexcept -> outcome::result { - if (state.known) { +auto dialer_actor_t::operator()(const model::diff::peer::peer_state_t &diff) noexcept -> outcome::result { + if (diff.known) { auto &devices = cluster->get_devices(); - auto peer = devices.by_sha256(state.peer_id); + auto peer = devices.by_sha256(diff.peer_id); if (peer) { - if (!state.online) { + if (diff.state == model::device_state_t::offline) { schedule_redial(peer); } else { auto it = redial_map.find(peer); diff --git a/src/net/peer_actor.cpp b/src/net/peer_actor.cpp index dc10ee6c..9b62dcb9 100644 --- a/src/net/peer_actor.cpp +++ b/src/net/peer_actor.cpp @@ -383,7 +383,7 @@ void peer_actor_t::shutdown_finish() noexcept { auto device = cluster->get_devices().by_sha256(sha256); if (device && device->get_state() == model::device_state_t::online) { auto diff = model::diff::cluster_diff_ptr_t(); - diff = new model::diff::peer::peer_state_t(*cluster, sha256, address, false); + diff = new model::diff::peer::peer_state_t(*cluster, sha256, address, model::device_state_t::offline); send(coordinator, std::move(diff)); } } @@ -483,8 +483,9 @@ void peer_actor_t::read_hello(proto::message::message_t &&msg) noexcept { return do_shutdown(make_error(ec)); } auto diff = cluster_diff_ptr_t(); - diff = new peer::peer_state_t(*cluster, peer_device_id.get_sha256(), get_address(), true, cert_name, - peer_endpoint, msg->client_name()); + diff = + new peer::peer_state_t(*cluster, peer_device_id.get_sha256(), get_address(), + model::device_state_t::online, cert_name, peer_endpoint, msg->client_name()); send(coordinator, std::move(diff)); } else { LOG_WARN(log, "{}, read_hello, unexpected_message", identity); diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index 8b38476b..de5776ea 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -68,9 +68,9 @@ void peer_supervisor_t::on_contact_update(model::message::contact_update_t &msg) } } -auto peer_supervisor_t::operator()(const model::diff::peer::peer_state_t &state) noexcept -> outcome::result { - auto &peer_addr = state.peer_addr; - if (!state.known && state.online) { +auto peer_supervisor_t::operator()(const model::diff::peer::peer_state_t &diff) noexcept -> outcome::result { + auto &peer_addr = diff.peer_addr; + if (!diff.known && diff.state == model::device_state_t::online) { auto ec = model::make_error_code(model::error_code_t::unknown_device); auto ee = make_error(ec); send(address, peer_addr, ee); diff --git a/tests/033-diffs-trivial.cpp b/tests/033-diffs-trivial.cpp index 6f6d389d..7a4a32d1 100644 --- a/tests/033-diffs-trivial.cpp +++ b/tests/033-diffs-trivial.cpp @@ -28,13 +28,14 @@ TEST_CASE("peer state update", "[model]") { cluster->get_devices().put(peer_device); rotor::address_ptr_t addr; - auto diff = diff::cluster_diff_ptr_t(new diff::peer::peer_state_t(*cluster, peer_id.get_sha256(), addr, true)); + auto diff = diff::cluster_diff_ptr_t( + new diff::peer::peer_state_t(*cluster, peer_id.get_sha256(), addr, device_state_t::online)); CHECK(peer_device->get_state() == model::device_state_t::offline); REQUIRE(diff->apply(*cluster)); CHECK(peer_device->get_state() == model::device_state_t::online); - diff = new diff::peer::peer_state_t(*cluster, peer_id.get_sha256(), addr, false); + diff = new diff::peer::peer_state_t(*cluster, peer_id.get_sha256(), addr, device_state_t::offline); REQUIRE(diff->apply(*cluster)); CHECK(peer_device->get_state() == model::device_state_t::offline); } diff --git a/tests/073-dialer.cpp b/tests/073-dialer.cpp index 890a5a55..4187beeb 100644 --- a/tests/073-dialer.cpp +++ b/tests/073-dialer.cpp @@ -104,14 +104,14 @@ void test_dialer() { auto diff = model::diff::cluster_diff_ptr_t{}; auto sample_addr = sup->get_address(); auto peer_id = peer_device->device_id().get_sha256(); - diff = new model::diff::peer::peer_state_t(*cluster, peer_id, sample_addr, true); + diff = new model::diff::peer::peer_state_t(*cluster, peer_id, sample_addr, device_state_t::online); sup->send(sup->get_address(), diff); sup->do_process(); CHECK(!discovery); CHECK(sup->timers.size() == 0); - diff = new model::diff::peer::peer_state_t(*cluster, peer_id, sample_addr, false); + diff = new model::diff::peer::peer_state_t(*cluster, peer_id, sample_addr, device_state_t::offline); sup->send(sup->get_address(), diff); sup->do_process(); From 0e8fd62f8d242bf572cb502858bd4914ffd1b6d7 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Wed, 27 Apr 2022 23:59:10 +0300 Subject: [PATCH 05/31] link initiator & peer actors --- src/net/initiator_actor.cpp | 27 +++- src/net/initiator_actor.h | 11 ++ src/net/peer_actor.cpp | 240 ++++++------------------------------ src/net/peer_actor.h | 46 ++----- src/net/peer_supervisor.cpp | 54 ++++---- src/net/peer_supervisor.h | 9 ++ tests/077-initiator.cpp | 67 +++++++--- 7 files changed, 176 insertions(+), 278 deletions(-) diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 2b31fb1c..321509d0 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -1,7 +1,9 @@ #include "initiator_actor.h" #include "constants.h" #include "names.h" +#include "model/messages.h" #include "utils/error_code.h" +#include "model/diff/peer/peer_state.h" #include #include @@ -18,7 +20,7 @@ r::plugin::resource_id_t handshake = 3; initiator_actor_t::initiator_actor_t(config_t &cfg) : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, uris{cfg.uris}, ssl_pair{*cfg.ssl_pair}, - sock(std::move(cfg.sock)) { + sock(std::move(cfg.sock)), cluster{std::move(cfg.cluster)} { log = utils::get_logger("net.initator"); active = !sock.has_value(); } @@ -40,13 +42,20 @@ void initiator_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { p.subscribe_actor(&initiator_actor_t::on_resolve); resources->acquire(resource::initializing); if (active) { + auto diff = model::diff::cluster_diff_ptr_t(); + auto state = model::device_state_t::dialing; + auto sha256 = peer_device_id.get_sha256(); + diff = new model::diff::peer::peer_state_t(*cluster, sha256, nullptr, state); + send(coordinator, std::move(diff)); initiate_active(); } else { initiate_passive(); } }); - plugin.with_casted( - [&](auto &p) { p.discover_name(names::resolver, resolver).link(false); }); + plugin.with_casted([&](auto &p) { + p.discover_name(names::resolver, resolver).link(false); + p.discover_name(names::coordinator, coordinator).link(false); + }); } void initiator_actor_t::initiate_active() noexcept { @@ -85,7 +94,8 @@ void initiator_actor_t::on_start() noexcept { r::actor_base_t::on_start(); LOG_TRACE(log, "{}, on_start", identity); auto &addr = supervisor->get_address(); - send(addr, std::move(transport), peer_device_id); + send(addr, std::move(transport), peer_device_id, remote_endpoint); + success = true; do_shutdown(); } @@ -105,6 +115,13 @@ void initiator_actor_t::shutdown_start() noexcept { void initiator_actor_t::shutdown_finish() noexcept { LOG_TRACE(log, "{}, shutdown_finish", identity); + if (active && !success) { + auto diff = model::diff::cluster_diff_ptr_t(); + auto state = model::device_state_t::offline; + auto sha256 = peer_device_id.get_sha256(); + diff = new model::diff::peer::peer_state_t(*cluster, sha256, nullptr, state); + send(coordinator, std::move(diff)); + } r::actor_base_t::shutdown_finish(); } @@ -185,5 +202,7 @@ void initiator_actor_t::on_handshake(bool valid_peer, utils::x509_t &cert, const return do_shutdown(make_error(ec)); } LOG_TRACE(log, "{}, on_handshake, valid = {}, issued by {}", identity, valid_peer, cert_name.value()); + peer_device_id = *peer_device; + remote_endpoint = peer_endpoint; resources->release(resource::initializing); } diff --git a/src/net/initiator_actor.h b/src/net/initiator_actor.h index 16029b59..a1d2230f 100644 --- a/src/net/initiator_actor.h +++ b/src/net/initiator_actor.h @@ -16,6 +16,7 @@ struct initiator_actor_config_t : public r::actor_config_t { utils::uri_container_t uris; const utils::key_pair_t *ssl_pair; std::optional sock; + model::cluster_ptr_t cluster; }; template struct initiator_actor_config_builder_t : r::actor_config_builder_t { @@ -42,6 +43,11 @@ template struct initiator_actor_config_builder_t : r::actor_con parent_t::config.sock = std::move(value); return std::move(*static_cast(this)); } + + builder_t &&cluster(const model::cluster_ptr_t &value) &&noexcept { + parent_t::config.cluster = value; + return std::move(*static_cast(this)); + } }; struct initiator_actor_t : r::actor_base_t { @@ -72,13 +78,17 @@ struct initiator_actor_t : r::actor_base_t { utils::uri_container_t uris; const utils::key_pair_t &ssl_pair; std::optional sock; + model::cluster_ptr_t cluster; transport::stream_sp_t transport; r::address_ptr_t resolver; + r::address_ptr_t coordinator; size_t uri_idx = 0; utils::logger_t log; + tcp::endpoint remote_endpoint; bool connected = false; bool active = false; + bool success = false; }; namespace payload { @@ -86,6 +96,7 @@ namespace payload { struct peer_connected_t { transport::stream_sp_t transport; model::device_id_t peer_device_id; + tcp::endpoint remote_endpoint; }; } // namespace payload diff --git a/src/net/peer_actor.cpp b/src/net/peer_actor.cpp index 9b62dcb9..281ba5a6 100644 --- a/src/net/peer_actor.cpp +++ b/src/net/peer_actor.cpp @@ -10,155 +10,55 @@ #include "model/messages.h" #include "model/diff/peer/peer_state.h" #include +#include using namespace syncspirit::net; using namespace syncspirit; namespace { namespace resource { -r::plugin::resource_id_t resolving = 0; -r::plugin::resource_id_t uris = 1; -r::plugin::resource_id_t io_handshake = 2; -r::plugin::resource_id_t io_read = 3; -r::plugin::resource_id_t io_write = 4; -r::plugin::resource_id_t io_connect = 5; -r::plugin::resource_id_t io_timer = 6; -r::plugin::resource_id_t tx_timer = 7; -r::plugin::resource_id_t rx_timer = 8; -r::plugin::resource_id_t finalization = 9; +r::plugin::resource_id_t io_read = 0; +r::plugin::resource_id_t io_write = 1; +r::plugin::resource_id_t io_timer = 2; +r::plugin::resource_id_t tx_timer = 3; +r::plugin::resource_id_t rx_timer = 4; +r::plugin::resource_id_t finalization = 5; } // namespace resource } // namespace peer_actor_t::peer_actor_t(config_t &config) : r::actor_base_t{config}, cluster{config.cluster}, device_name{config.device_name}, bep_config{config.bep_config}, - coordinator{config.coordinator}, peer_device_id{config.peer_device_id}, uris{config.uris}, - sock(std::move(config.sock)), ssl_pair{*config.ssl_pair} { + coordinator{config.coordinator}, peer_device_id{config.peer_device_id}, + transport(std::move(config.transport)), peer_endpoint{config.peer_endpoint} { rx_buff.resize(config.bep_config.rx_buff_size); log = utils::get_logger("net.peer_actor"); } -static std::string generate_id(const model::device_id_t *device_id, const tcp::endpoint *remote) noexcept { - std::string r; - if (device_id) { - r += device_id->get_short(); - } else { - r += "[?]"; - } - r += "/"; - if (remote) { - r += remote->address().to_string(); - r += ":"; - r += std::to_string(remote->port()); - } else { - r += "[?]"; - } - return r; -} - void peer_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { r::actor_base_t::configure(plugin); plugin.with_casted([&](auto &p) { - sys::error_code ec; - tcp::endpoint remote; - if (sock) { - remote = sock.value().remote_endpoint(ec); - } - auto value = generate_id((sock ? nullptr : &peer_device_id), (ec ? nullptr : &remote)); - p.set_identity(value, false); + std::stringstream ss; + ss << peer_device_id.get_short() << "/" << peer_endpoint; + p.set_identity(ss.str(), false); }); plugin.with_casted([&](auto &p) { - p.subscribe_actor(&peer_actor_t::on_resolve); p.subscribe_actor(&peer_actor_t::on_start_reading); p.subscribe_actor(&peer_actor_t::on_termination); p.subscribe_actor(&peer_actor_t::on_block_request); p.subscribe_actor(&peer_actor_t::on_forward); - instantiate_transport(); }); - plugin.with_casted( - [&](auto &p) { p.discover_name(names::resolver, resolver).link(false); }); -} - -void peer_actor_t::instantiate_transport() noexcept { - if (sock) { - auto sup = static_cast(supervisor); - transport = transport::initiate_tls_passive(*sup, ssl_pair, std::move(sock.value())); - auto timeout = r::pt::milliseconds{bep_config.connect_timeout}; - timer_request = start_timer(timeout, *this, &peer_actor_t::on_timer); - resources->acquire(resource::io_timer); - initiate_handshake(); - } else { - resources->acquire(resource::uris); - try_next_uri(); - } } -void peer_actor_t::try_next_uri() noexcept { - while (++uri_idx < (std::int32_t)uris.size()) { - auto &uri = uris[uri_idx]; - auto sup = static_cast(supervisor); - // log->warn("url: {}", uri.full); - auto result = - transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri, false, constants::protocol_name); - initiate(std::move(result), uri); - resources->release(resource::uris); - return; - } - - LOG_TRACE(log, "{}, try_next_uri, no way to conenct found, shut down", identity); - resources->release(resource::uris); - auto ec = utils::make_error_code(utils::error_code_t::connection_impossible); - do_shutdown(make_error(ec)); -} - -void peer_actor_t::initiate(transport::stream_sp_t tran, const utils::URI &url) noexcept { - transport = std::move(tran); - - LOG_TRACE(log, "{}, try_next_uri, will initate connection with via {} (transport = {})", identity, url.full, - (void *)transport.get()); - pt::time_duration resolve_timeout = init_timeout / 2; - auto port = std::to_string(url.port); - request(resolver, url.host, port).send(resolve_timeout); - resources->acquire(resource::resolving); - return; -} +void peer_actor_t::on_start() noexcept { + LOG_TRACE(log, "{}, on_start", identity); -void peer_actor_t::on_resolve(message::resolve_response_t &res) noexcept { - resources->release(resource::resolving); - if (state > r::state_t::OPERATIONAL) { - return; - } - - auto &ee = res.payload.ee; - if (ee) { - LOG_WARN(log, "{}, on_resolve error : {}", identity, ee->message()); - resources->acquire(resource::uris); - return try_next_uri(); - } - - auto &addresses = res.payload.res->results; - transport::connect_fn_t on_connect = [&](auto arg) { this->on_connect(arg); }; - transport::error_fn_t on_error = [&](auto arg) { this->on_io_error(arg, resource::io_connect); }; - transport->async_connect(addresses, on_connect, on_error); - resources->acquire(resource::io_connect); - - auto timeout = r::pt::milliseconds{bep_config.connect_timeout}; - timer_request = start_timer(timeout, *this, &peer_actor_t::on_timer); - resources->acquire(resource::io_timer); -} - -void peer_actor_t::on_connect(resolve_it_t) noexcept { - LOG_TRACE(log, "{}, on_connect, device_id = {}", identity, peer_device_id.get_short()); - initiate_handshake(); - resources->release(resource::io_connect); -} + fmt::memory_buffer buff; + proto::make_hello_message(buff, device_name); + push_write(std::move(buff), false); -void peer_actor_t::initiate_handshake() noexcept { - connected = true; - transport::handshake_fn_t handshake_fn([&](auto &&...args) { on_handshake(args...); }); - transport::error_fn_t error_fn([&](auto arg) { on_io_error(arg, resource::io_handshake); }); - transport->async_handshake(handshake_fn, error_fn); - resources->acquire(resource::io_handshake); + read_more(); + read_action = &peer_actor_t::read_hello; } void peer_actor_t::on_io_error(const sys::error_code &ec, rotor::plugin::resource_id_t resource) noexcept { @@ -174,14 +74,7 @@ void peer_actor_t::on_io_error(const sys::error_code &ec, rotor::plugin::resourc } io_error = true; if (state < r::state_t::SHUTTING_DOWN) { - if (!connected) { - resources->acquire(resource::uris); - try_next_uri(); - } else { - connected = false; - LOG_DEBUG(log, "{}, on_io_error, initiating shutdown...", identity); - do_shutdown(make_error(ec)); - } + do_shutdown(make_error(ec)); } } @@ -226,42 +119,6 @@ void peer_actor_t::push_write(fmt::memory_buffer &&buff, bool final) noexcept { } } -void peer_actor_t::on_handshake(bool valid_peer, utils::x509_t &cert, const tcp::endpoint &peer_endpoint, - const model::device_id_t *peer_device) noexcept { - resources->release(resource::io_handshake); - handshaked = true; - if (!peer_device) { - LOG_WARN(log, "{}, on_handshake, missing peer device id", identity); - auto ec = utils::make_error_code(utils::error_code_t::missing_device_id); - return do_shutdown(make_error(ec)); - } - - auto new_id = generate_id(peer_device, &peer_endpoint); - LOG_DEBUG(log, "{} now becomes {}", identity, new_id); - identity = new_id; - - identity = new_id; - auto cert_name = utils::get_common_name(cert); - if (!cert_name) { - LOG_WARN(log, "{}, on_handshake, can't get certificate name: {}", identity, cert_name.error().message()); - auto ec = utils::make_error_code(utils::error_code_t::missing_cn); - return do_shutdown(make_error(ec)); - } - LOG_TRACE(log, "{}, on_handshake, valid = {}, issued by {}", identity, valid_peer, cert_name.value()); - - this->cert_name = cert_name.value(); - this->valid_peer = valid_peer; - this->peer_device_id = *peer_device; - this->peer_endpoint = peer_endpoint; - - fmt::memory_buffer buff; - proto::make_hello_message(buff, device_name); - push_write(std::move(buff), false); - - read_more(); - read_action = &peer_actor_t::read_hello; -} - void peer_actor_t::read_more() noexcept { if (state > r::state_t::OPERATIONAL) { return; @@ -330,10 +187,8 @@ void peer_actor_t::on_timer(r::request_id_t, bool cancelled) noexcept { LOG_TRACE(log, "{}, on_timer_trigger, cancelled = {}", identity, cancelled); if (!cancelled) { cancel_io(); - if (connected) { - auto ec = r::make_error_code(r::shutdown_code_t::normal); - do_shutdown(make_error(ec)); - } + auto ec = r::make_error_code(r::shutdown_code_t::normal); + do_shutdown(make_error(ec)); } } @@ -352,17 +207,13 @@ void peer_actor_t::shutdown_start() noexcept { send(controller, shutdown_reason); } - if (handshaked) { - fmt::memory_buffer buff; - proto::Close close; - close.set_reason(shutdown_reason->message()); - proto::serialize(buff, close); - tx_queue.clear(); - push_write(std::move(buff), true); - LOG_TRACE(log, "{}, going to send close message", identity); - } else { - cancel_io(); - } + fmt::memory_buffer buff; + proto::Close close; + close.set_reason(shutdown_reason->message()); + proto::serialize(buff, close); + tx_queue.clear(); + push_write(std::move(buff), true); + LOG_TRACE(log, "{}, going to send close message", identity); r::actor_base_t::shutdown_start(); } @@ -378,15 +229,14 @@ void peer_actor_t::shutdown_finish() noexcept { send(controller, shutdown_reason); } r::actor_base_t::shutdown_finish(); - if (handshaked) { - auto sha256 = peer_device_id.get_sha256(); - auto device = cluster->get_devices().by_sha256(sha256); - if (device && device->get_state() == model::device_state_t::online) { - auto diff = model::diff::cluster_diff_ptr_t(); - diff = new model::diff::peer::peer_state_t(*cluster, sha256, address, model::device_state_t::offline); - send(coordinator, std::move(diff)); - } - } + auto sha256 = peer_device_id.get_sha256(); + auto device = cluster->get_devices().by_sha256(sha256); + assert(device && device->get_state() == model::device_state_t::online); + + auto diff = model::diff::cluster_diff_ptr_t(); + auto state = model::device_state_t::offline; + diff = new model::diff::peer::peer_state_t(*cluster, sha256, address, state); + send(coordinator, std::move(diff)); } void peer_actor_t::cancel_timer() noexcept { @@ -407,16 +257,6 @@ void peer_actor_t::cancel_io() noexcept { transport->cancel(); return; } - if (resources->has(resource::io_connect)) { - LOG_TRACE(log, "{}, cancelling I/O (connect)", identity); - transport->cancel(); - return; - } - if (resources->has(resource::io_handshake)) { - LOG_TRACE(log, "{}, cancelling I/O (handshake)", identity); - transport->cancel(); - return; - } } void peer_actor_t::on_start_reading(message::start_reading_t &message) noexcept { @@ -483,9 +323,9 @@ void peer_actor_t::read_hello(proto::message::message_t &&msg) noexcept { return do_shutdown(make_error(ec)); } auto diff = cluster_diff_ptr_t(); - diff = - new peer::peer_state_t(*cluster, peer_device_id.get_sha256(), get_address(), - model::device_state_t::online, cert_name, peer_endpoint, msg->client_name()); + auto state = model::device_state_t::online; + diff = new peer::peer_state_t(*cluster, peer_device_id.get_sha256(), get_address(), state, cert_name, + peer_endpoint, msg->client_name()); send(coordinator, std::move(diff)); } else { LOG_WARN(log, "{}, read_hello, unexpected_message", identity); diff --git a/src/net/peer_actor.h b/src/net/peer_actor.h index ee91ee71..df1009bd 100644 --- a/src/net/peer_actor.h +++ b/src/net/peer_actor.h @@ -20,12 +20,11 @@ namespace net { struct peer_actor_config_t : public r::actor_config_t { std::string_view device_name; model::device_id_t peer_device_id; - utils::uri_container_t uris; - std::optional sock; - const utils::key_pair_t *ssl_pair; config::bep_config_t bep_config; + transport::stream_sp_t transport; r::address_ptr_t coordinator; model::cluster_ptr_t cluster; + tcp::endpoint peer_endpoint; }; template struct peer_actor_config_builder_t : r::actor_config_builder_t { @@ -38,16 +37,6 @@ template struct peer_actor_config_builder_t : r::actor_config_b return std::move(*static_cast(this)); } - builder_t &&uris(const utils::uri_container_t &value) &&noexcept { - parent_t::config.uris = value; - return std::move(*static_cast(this)); - } - - builder_t &&ssl_pair(const utils::key_pair_t *value) &&noexcept { - parent_t::config.ssl_pair = value; - return std::move(*static_cast(this)); - } - builder_t &&device_name(std::string_view value) &&noexcept { parent_t::config.device_name = value; return std::move(*static_cast(this)); @@ -63,13 +52,18 @@ template struct peer_actor_config_builder_t : r::actor_config_b return std::move(*static_cast(this)); } - builder_t &&sock(std::optional &&value) &&noexcept { - parent_t::config.sock = std::move(value); + builder_t &&cluster(const model::cluster_ptr_t &value) &&noexcept { + parent_t::config.cluster = value; return std::move(*static_cast(this)); } - builder_t &&cluster(const model::cluster_ptr_t &value) &&noexcept { - parent_t::config.cluster = value; + builder_t &&transport(transport::stream_sp_t value) &&noexcept { + parent_t::config.transport = std::move(value); + return std::move(*static_cast(this)); + } + + builder_t &&peer_endpoint(const tcp::endpoint &value) &&noexcept { + parent_t::config.peer_endpoint = value; return std::move(*static_cast(this)); } }; @@ -80,6 +74,7 @@ struct SYNCSPIRIT_API peer_actor_t : public r::actor_base_t { peer_actor_t(config_t &config); void configure(r::plugin::plugin_base_t &plugin) noexcept override; + void on_start() noexcept override; void shutdown_start() noexcept override; void shutdown_finish() noexcept override; @@ -100,7 +95,6 @@ struct SYNCSPIRIT_API peer_actor_t : public r::actor_base_t { }; }; - using resolve_it_t = payload::address_response_t::resolve_results_t::iterator; using tx_item_t = model::intrusive_ptr_t; using tx_message_t = confidential::message::tx_item_t; using tx_queue_t = std::list; @@ -108,28 +102,20 @@ struct SYNCSPIRIT_API peer_actor_t : public r::actor_base_t { using block_request_ptr_t = r::intrusive_ptr_t; using block_requests_t = std::list; - void on_resolve(message::resolve_response_t &res) noexcept; void on_start_reading(message::start_reading_t &) noexcept; void on_termination(message::termination_signal_t &) noexcept; void on_block_request(message::block_request_t &) noexcept; void on_forward(message::forwarded_message_t &message) noexcept; - void on_connect(resolve_it_t) noexcept; void on_io_error(const sys::error_code &ec, r::plugin::resource_id_t resource) noexcept; void on_write(std::size_t bytes) noexcept; void on_read(std::size_t bytes) noexcept; - void try_next_uri() noexcept; - void initiate(transport::stream_sp_t tran, const utils::URI &url) noexcept; - void on_handshake(bool valid_peer, utils::x509_t &peer_cert, const tcp::endpoint &peer_endpoint, - const model::device_id_t *peer_device) noexcept; void on_timer(r::request_id_t, bool cancelled) noexcept; void read_more() noexcept; void push_write(fmt::memory_buffer &&buff, bool final) noexcept; void process_tx_queue() noexcept; void cancel_timer() noexcept; void cancel_io() noexcept; - void instantiate_transport() noexcept; - void initiate_handshake() noexcept; void on_tx_timeout(r::request_id_t, bool cancelled) noexcept; void on_rx_timeout(r::request_id_t, bool cancelled) noexcept; @@ -148,12 +134,7 @@ struct SYNCSPIRIT_API peer_actor_t : public r::actor_base_t { config::bep_config_t bep_config; r::address_ptr_t coordinator; model::device_id_t peer_device_id; - utils::uri_container_t uris; - std::optional sock; - const utils::key_pair_t &ssl_pair; - r::address_ptr_t resolver; transport::stream_sp_t transport; - std::int32_t uri_idx = -1; std::optional timer_request; std::optional tx_timer_request; std::optional rx_timer_request; @@ -161,9 +142,6 @@ struct SYNCSPIRIT_API peer_actor_t : public r::actor_base_t { tx_item_t tx_item; fmt::memory_buffer rx_buff; std::size_t rx_idx = 0; - bool connected = false; - bool handshaked = false; - bool valid_peer = false; bool finished = false; bool io_error = false; std::string cert_name; diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index de5776ea..5fddb7f5 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -3,6 +3,7 @@ #include "peer_supervisor.h" #include "peer_actor.h" +#include "initiator_actor.h" #include "names.h" #include "utils/error_code.h" #include "model/diff/peer/peer_state.h" @@ -30,6 +31,7 @@ void peer_supervisor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { auto plugin = static_cast(p); plugin->subscribe_actor(&peer_supervisor_t::on_model_update, coordinator); plugin->subscribe_actor(&peer_supervisor_t::on_contact_update, coordinator); + plugin->subscribe_actor(&peer_supervisor_t::on_ready); } }); }); @@ -68,6 +70,22 @@ void peer_supervisor_t::on_contact_update(model::message::contact_update_t &msg) } } +void peer_supervisor_t::on_ready(message::peer_connected_t &msg) noexcept { + LOG_TRACE(log, "{}, on_ready", identity); + auto timeout = r::pt::milliseconds{bep_config.connect_timeout}; + auto &p = msg.payload; + create_actor() + .transport(std::move(p.transport)) + .peer_device_id(p.peer_device_id) + .device_name(device_name) + .bep_config(bep_config) + .coordinator(coordinator) + .peer_endpoint(p.remote_endpoint) + .timeout(timeout) + .cluster(cluster) + .finish(); +} + auto peer_supervisor_t::operator()(const model::diff::peer::peer_state_t &diff) noexcept -> outcome::result { auto &peer_addr = diff.peer_addr; if (!diff.known && diff.state == model::device_state_t::online) { @@ -86,16 +104,12 @@ auto peer_supervisor_t::operator()(const model::diff::modify::connect_request_t diff.sock.reset(); auto timeout = r::pt::milliseconds{bep_config.connect_timeout}; - auto peer_addr = create_actor() - .ssl_pair(&ssl_pair) - .device_name(device_name) - .bep_config(bep_config) - .coordinator(coordinator) - .timeout(timeout) - .sock(std::optional(std::move(sock))) - .cluster(cluster) - .finish() - ->get_address(); + create_actor() + .cluster(cluster) + .ssl_pair(&ssl_pair) + .sock(std::move(sock)) + .timeout(timeout) + .finish(); return outcome::success(); } @@ -108,18 +122,14 @@ auto peer_supervisor_t::operator()(const model::diff::modify::update_contact_t & auto &uris = diff.uris; auto connect_timeout = r::pt::milliseconds{bep_config.connect_timeout}; LOG_DEBUG(log, "{} initiating connection with {}", identity, peer->device_id()); - auto peer_addr = create_actor() - .ssl_pair(&ssl_pair) - .device_name(device_name) - .bep_config(bep_config) - .coordinator(coordinator) - .init_timeout(connect_timeout * (uris.size() + 1)) - .shutdown_timeout(connect_timeout) - .peer_device_id(diff.device) - .uris(uris) - .cluster(cluster) - .finish() - ->get_address(); + create_actor() + .ssl_pair(&ssl_pair) + .peer_device_id(diff.device) + .uris(uris) + .cluster(cluster) + .init_timeout(connect_timeout * (uris.size() + 1)) + .shutdown_timeout(connect_timeout) + .finish(); } } return outcome::success(); diff --git a/src/net/peer_supervisor.h b/src/net/peer_supervisor.h index c3467d23..4f1cb114 100644 --- a/src/net/peer_supervisor.h +++ b/src/net/peer_supervisor.h @@ -16,6 +16,14 @@ namespace syncspirit { namespace net { +namespace payload { +struct peer_connected_t; +} + +namespace message { +using peer_connected_t = r::message_t; +} + namespace outcome = boost::outcome_v2; struct peer_supervisor_config_t : ra::supervisor_config_asio_t { @@ -67,6 +75,7 @@ struct SYNCSPIRIT_API peer_supervisor_t : public ra::supervisor_asio_t, private: void on_model_update(model::message::model_update_t &) noexcept; void on_contact_update(model::message::contact_update_t &) noexcept; + void on_ready(message::peer_connected_t &) noexcept; outcome::result operator()(const model::diff::peer::peer_state_t &) noexcept override; outcome::result operator()(const model::diff::modify::update_contact_t &) noexcept override; diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp index 218b36c0..2aa26183 100644 --- a/tests/077-initiator.cpp +++ b/tests/077-initiator.cpp @@ -4,6 +4,7 @@ #include "utils/tls.h" #include "model/cluster.h" +#include "model/messages.h" #include "net/names.h" #include "net/initiator_actor.h" #include "net/resolver_actor.h" @@ -55,7 +56,9 @@ using actor_ptr_t = r::intrusive_ptr_t; struct fixture_t { using acceptor_t = asio::ip::tcp::acceptor; - using msg_ptr_t = r::intrusive_ptr_t; + using ready_ptr_t = r::intrusive_ptr_t; + using diff_ptr_t = r::intrusive_ptr_t; + using diff_msgs_t = std::vector; fixture_t() noexcept : ctx(io_ctx), acceptor(io_ctx), peer_sock(io_ctx) { utils::set_default("trace"); @@ -63,15 +66,21 @@ struct fixture_t { } void run() noexcept { + auto strand = std::make_shared(io_ctx); sup = ctx.create_supervisor().strand(strand).timeout(timeout).create_registry().finish(); sup->configure_callback = [&](r::plugin::plugin_base_t &plugin) { plugin.template with_casted([&](auto &p) { - using msg_t = message::peer_connected_t; - p.subscribe_actor(r::lambda([&](msg_t &msg) { - message = &msg; + using connected_t = typename ready_ptr_t::element_type; + using diff_t = typename diff_ptr_t::element_type; + p.subscribe_actor(r::lambda([&](connected_t &msg) { + connected_message = &msg; LOG_INFO(log, "received message::peer_connected_t"); })); + p.subscribe_actor(r::lambda([&](diff_t &msg) { + diff_msgs.emplace_back(&msg); + LOG_INFO(log, "received diff message"); + })); }); }; sup->start(); @@ -91,8 +100,15 @@ struct fixture_t { my_keys = utils::generate_pair("me").value(); peer_keys = utils::generate_pair("peer").value(); - my_device = model::device_id_t::from_cert(my_keys.cert_data).value(); - peer_device = model::device_id_t::from_cert(peer_keys.cert_data).value(); + auto md = model::device_id_t::from_cert(my_keys.cert_data).value(); + auto pd = model::device_id_t::from_cert(peer_keys.cert_data).value(); + + my_device = device_t::create(md, "my-device").value(); + peer_device = device_t::create(pd, "peer-device").value(); + cluster = new cluster_t(my_device, 1); + + cluster->get_devices().put(my_device); + cluster->get_devices().put(peer_device); main(); } @@ -126,7 +142,7 @@ struct fixture_t { void initiate_active() noexcept { tcp::resolver resolver(io_ctx); auto addresses = resolver.resolve(host, std::to_string(listening_ep.port())); - peer_trans = transport::initiate_tls_active(*sup, peer_keys, my_device, peer_uri); + peer_trans = transport::initiate_tls_active(*sup, peer_keys, my_device->device_id(), peer_uri); transport::error_fn_t on_error = [&](auto &ec) { LOG_WARN(log, "initiate_active/connect, err: {}", ec.message()); @@ -157,8 +173,9 @@ struct fixture_t { virtual actor_ptr_t create_actor() noexcept { return sup->create_actor() .timeout(timeout) - .peer_device_id(peer_device) + .peer_device_id(peer_device->device_id()) .uris({peer_uri}) + .cluster(cluster) .ssl_pair(&my_keys) .escalate_failure() .finish(); @@ -169,10 +186,12 @@ struct fixture_t { .timeout(timeout) .sock(std::move(peer_sock)) .ssl_pair(&my_keys) + .cluster(cluster) .escalate_failure() .finish(); } + cluster_ptr_t cluster; asio::io_context io_ctx{1}; ra::system_context_asio_t ctx; acceptor_t acceptor; @@ -184,10 +203,12 @@ struct fixture_t { utils::key_pair_t my_keys; utils::key_pair_t peer_keys; utils::URI peer_uri; - model::device_id_t my_device; - model::device_id_t peer_device; + model::device_ptr_t my_device; + model::device_ptr_t peer_device; transport::stream_sp_t peer_trans; - msg_ptr_t message; + ready_ptr_t connected_message; + diff_msgs_t diff_msgs; + ; bool valid_handshake = false; }; @@ -198,7 +219,7 @@ void test_connect_timeout() { auto act = create_actor(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::SHUT_DOWN); - CHECK(!message); + CHECK(!connected_message); } }; F().run(); @@ -213,7 +234,12 @@ void test_handshake_timeout() { auto act = create_actor(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::SHUT_DOWN); - CHECK(!message); + CHECK(!connected_message); + REQUIRE(diff_msgs.size() == 2); + CHECK(diff_msgs[0]->payload.diff->apply(*cluster)); + CHECK(peer_device->get_state() == device_state_t::dialing); + CHECK(diff_msgs[1]->payload.diff->apply(*cluster)); + CHECK(peer_device->get_state() == device_state_t::offline); } }; F().run(); @@ -230,7 +256,7 @@ void test_connection_refused() { auto act = create_actor(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::SHUT_DOWN); - CHECK(!message); + CHECK(!connected_message); } }; F().run(); @@ -245,7 +271,7 @@ void test_resolve_failure() { auto act = create_actor(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::SHUT_DOWN); - CHECK(!message); + CHECK(!connected_message); } }; F().run(); @@ -257,11 +283,15 @@ void test_success() { auto act = create_actor(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::OPERATIONAL); - CHECK(message); + CHECK(connected_message); + CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); CHECK(valid_handshake); sup->do_shutdown(); sup->do_process(); CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + REQUIRE(diff_msgs.size() == 1); + CHECK(diff_msgs[0]->payload.diff->apply(*cluster)); + CHECK(peer_device->get_state() == device_state_t::dialing); } }; F().run(); @@ -281,7 +311,8 @@ void test_passive_success() { initiate_active(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::OPERATIONAL); - CHECK(message); + CHECK(connected_message); + CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); CHECK(valid_handshake); sup->do_shutdown(); sup->do_process(); @@ -307,7 +338,7 @@ void test_passive_timeout() { initiate_active(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::SHUT_DOWN); - CHECK(!message); + CHECK(!connected_message); } }; F().run(); From eb0bdf1ec80048b019709ac6171df84c548a5f29 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Thu, 28 Apr 2022 20:43:27 +0300 Subject: [PATCH 06/31] moar initiator tests --- tests/077-initiator.cpp | 76 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp index 2aa26183..20a6cfcc 100644 --- a/tests/077-initiator.cpp +++ b/tests/077-initiator.cpp @@ -225,6 +225,21 @@ void test_connect_timeout() { F().run(); } +void test_connect_unsupproted_proto() { + struct F : fixture_t { + std::string get_uri(const asio::ip::tcp::endpoint &) noexcept override { + return fmt::format("xxx://{}", listening_ep); + } + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + } + }; + F().run(); +} + void test_handshake_timeout() { struct F : fixture_t { @@ -245,6 +260,29 @@ void test_handshake_timeout() { F().run(); } +void test_handshake_garbage() { + struct F : fixture_t { + + void accept(const sys::error_code &ec) noexcept override { + auto buff = asio::buffer("garbage-garbage-garbage"); + peer_sock.write_some(buff); + } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + REQUIRE(diff_msgs.size() == 2); + CHECK(diff_msgs[0]->payload.diff->apply(*cluster)); + CHECK(peer_device->get_state() == device_state_t::dialing); + CHECK(diff_msgs[1]->payload.diff->apply(*cluster)); + CHECK(peer_device->get_state() == device_state_t::offline); + } + }; + F().run(); +} + void test_connection_refused() { struct F : fixture_t { @@ -322,6 +360,41 @@ void test_passive_success() { F().run(); } +void test_passive_garbage() { + struct F : fixture_t { + + actor_ptr_t act; + tcp::socket client_sock; + tcp::resolver::results_type addresses; + + F() : client_sock{io_ctx} {} + + void accept(const sys::error_code &ec) noexcept override { + LOG_INFO(log, "test_passive_garbage/accept, ec: {}", ec.message()); + act = create_passive_actor(); + } + + void initiate_active() noexcept { + tcp::resolver resolver(io_ctx); + addresses = resolver.resolve(host, std::to_string(listening_ep.port())); + asio::async_connect(client_sock, addresses.begin(), addresses.end(), [&](auto ec, auto addr) { + LOG_INFO(log, "test_passive_garbage/peer connect, ec: {}", ec.message()); + auto buff = asio::buffer("garbage-garbage-garbage"); + client_sock.write_some(buff); + sup->do_process(); + }); + } + + void main() noexcept override { + initiate_active(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + } + }; + F().run(); +} + void test_passive_timeout() { struct F : fixture_t { @@ -344,10 +417,13 @@ void test_passive_timeout() { F().run(); } +REGISTER_TEST_CASE(test_connect_unsupproted_proto, "test_connect_unsupproted_proto", "[initiator]"); REGISTER_TEST_CASE(test_connect_timeout, "test_connect_timeout", "[initiator]"); REGISTER_TEST_CASE(test_handshake_timeout, "test_handshake_timeout", "[initiator]"); +REGISTER_TEST_CASE(test_handshake_garbage, "test_handshake_garbage", "[initiator]"); REGISTER_TEST_CASE(test_connection_refused, "test_connection_refused", "[initiator]"); REGISTER_TEST_CASE(test_resolve_failure, "test_resolve_failure", "[initiator]"); REGISTER_TEST_CASE(test_success, "test_success", "[initiator]"); REGISTER_TEST_CASE(test_passive_success, "test_passive_success", "[initiator]"); +REGISTER_TEST_CASE(test_passive_garbage, "test_passive_garbage", "[initiator]"); REGISTER_TEST_CASE(test_passive_timeout, "test_passive_timeout", "[initiator]"); From 548a23c91353d2984bad3685413540f5b66a0f58 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 1 May 2022 00:05:04 +0300 Subject: [PATCH 07/31] implement relay proto + tests --- CMakeLists.txt | 1 + src/proto/relay_support.cpp | 281 ++++++++++++++++++++++++++++++++++++ src/proto/relay_support.h | 60 ++++++++ tests/CMakeLists.txt | 4 + 4 files changed, 346 insertions(+) create mode 100644 src/proto/relay_support.cpp create mode 100644 src/proto/relay_support.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 91a32510..fb0c1de5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,6 +141,7 @@ add_library(syncspirit_lib src/proto/bep_support.cpp src/proto/discovery_support.cpp src/proto/luhn32.cpp + src/proto/relay_support.cpp src/proto/upnp_support.cpp src/transport/stream.cpp src/transport/http.cpp diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp new file mode 100644 index 00000000..238a9a0a --- /dev/null +++ b/src/proto/relay_support.cpp @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2022 Ivan Baidakou + +#include "relay_support.h" +#include +#include + +namespace be = boost::endian; + +template inline constexpr bool always_false_v = false; + +namespace syncspirit::proto { + +struct header_t { + uint32_t magic; + uint32_t type; + uint32_t length; +}; + +enum class type_t { + ping = 0, + pong = 1, + join_relay_request = 2, + join_session_request = 3, + response = 4, + connect_request = 5, + session_invitation = 6, + LAST = session_invitation, +}; + +static constexpr auto header_sz = sizeof(header_t); +static constexpr auto max_packet_sz = size_t{1400}; +static constexpr uint32_t magic = 0x9E79BC40; + +static void serialize_header(char *ptr, type_t type, size_t payload_sz) noexcept { + auto h = header_t{be::native_to_big(magic), be::native_to_big(static_cast(type)), + be::native_to_big(static_cast(payload_sz))}; + auto in = reinterpret_cast(&h); + std::copy(in, in + header_sz, ptr); +} + +size_t serialize(const relay::message_t &msg, std::string &out) noexcept { + return std::visit( + [&](auto &it) -> size_t { + using T = std::decay_t; + if constexpr (std::is_same_v) { + out.resize(header_sz); + serialize_header(out.data(), type_t::ping, 0); + return header_sz; + } else if constexpr (std::is_same_v) { + out.resize(header_sz); + serialize_header(out.data(), type_t::pong, 0); + return header_sz; + } else if constexpr (std::is_same_v) { + out.resize(header_sz); + serialize_header(out.data(), type_t::join_relay_request, 0); + return header_sz; + } else if constexpr (std::is_same_v) { + auto key_sz = it.key.size(); + auto payload_sz = sizeof(uint32_t) + key_sz; + auto sz = header_sz + payload_sz; + out.resize(sz); + auto ptr = out.data(); + serialize_header(ptr, type_t::join_session_request, payload_sz); + ptr += header_sz; + *(reinterpret_cast(ptr)) = be::native_to_big((uint32_t)key_sz); + ptr += sizeof(uint32_t); + std::copy(it.key.begin(), it.key.end(), ptr); + return sz; + } else if constexpr (std::is_same_v) { + auto msg_sz = it.details.size(); + auto payload_sz = sizeof(uint32_t) + sizeof(uint32_t) + msg_sz; + auto sz = header_sz + payload_sz; + out.resize(sz); + auto ptr = out.data(); + serialize_header(ptr, type_t::response, payload_sz); + ptr += header_sz; + *(reinterpret_cast(ptr)) = be::native_to_big(it.code); + ptr += sizeof(int32_t); + *(reinterpret_cast(ptr)) = be::native_to_big((uint32_t)msg_sz); + ptr += sizeof(uint32_t); + std::copy(it.details.begin(), it.details.end(), ptr); + return sz; + } else if constexpr (std::is_same_v) { + auto id_sz = it.device_id.size(); + auto payload_sz = sizeof(uint32_t) + id_sz; + auto sz = header_sz + payload_sz; + out.resize(sz); + auto ptr = out.data(); + serialize_header(ptr, type_t::connect_request, payload_sz); + ptr += header_sz; + *(reinterpret_cast(ptr)) = be::native_to_big((uint32_t)id_sz); + ptr += sizeof(uint32_t); + std::copy(it.device_id.begin(), it.device_id.end(), ptr); + return sz; + } else if constexpr (std::is_same_v) { + auto &from = it.from; + auto &key = it.key; + auto &address = it.address; + auto payload_sz = sizeof(uint32_t) * 6 + from.size() + key.size() + address.size(); + auto sz = header_sz + payload_sz; + out.resize(sz); + auto ptr = out.data(); + serialize_header(ptr, type_t::session_invitation, payload_sz); + ptr += header_sz; + *(reinterpret_cast(ptr)) = be::native_to_big((uint32_t)from.size()); + ptr += sizeof(uint32_t); + std::copy(from.begin(), from.end(), ptr); + ptr += from.size(); + + *(reinterpret_cast(ptr)) = be::native_to_big((uint32_t)key.size()); + ptr += sizeof(uint32_t); + std::copy(key.begin(), key.end(), ptr); + ptr += key.size(); + + *(reinterpret_cast(ptr)) = be::native_to_big((uint32_t)address.size()); + ptr += sizeof(uint32_t); + std::copy(address.begin(), address.end(), ptr); + ptr += address.size(); + + *(reinterpret_cast(ptr)) = be::native_to_big((uint32_t)it.port); + ptr += sizeof(uint32_t); + + *(reinterpret_cast(ptr)) = be::native_to_big((uint32_t)it.server_socket); + return sz; + } else { + static_assert(always_false_v, "non-exhaustive visitor!"); + } + }, + msg); +} + +static relay::parse_result_t parse_ping(std::string_view data) noexcept { + return relay::wrapped_message_t{header_sz, relay::ping_t{}}; +} + +static relay::parse_result_t parse_pong(std::string_view data) noexcept { + return relay::wrapped_message_t{header_sz, relay::pong_t{}}; +} + +static relay::parse_result_t parse_join_relay_request(std::string_view data) noexcept { + return relay::wrapped_message_t{header_sz, relay::join_relay_request_t{}}; +} + +static relay::parse_result_t parse_join_session_request(std::string_view data) noexcept { + if (data.size() < sizeof(uint32_t)) { + return relay::protocol_error_t{}; + } + auto ptr = data.data(); + auto sz = be::big_to_native(*reinterpret_cast(ptr)); + if (sz + sizeof(uint32_t) != data.size()) { + return relay::protocol_error_t{}; + } + auto tail = data.substr(sizeof(uint32_t)); + return relay::wrapped_message_t{header_sz + data.size(), relay::join_session_request_t{std::string(tail)}}; +} + +static relay::parse_result_t parse_response(std::string_view data) noexcept { + if (data.size() < sizeof(uint32_t) * 2) { + return relay::protocol_error_t{}; + } + auto ptr = data.data(); + auto code = be::big_to_native(*reinterpret_cast(ptr)); + ptr += sizeof(uint32_t); + auto sz = be::big_to_native(*reinterpret_cast(ptr)); + if (sz + sizeof(uint32_t) * 2 != data.size()) { + return relay::protocol_error_t{}; + } + auto tail = data.substr(sizeof(uint32_t) * 2); + return relay::wrapped_message_t{header_sz + data.size(), relay::response_t{code, std::string(tail)}}; +} + +static relay::parse_result_t parse_connect_request(std::string_view data) noexcept { + if (data.size() < sizeof(uint32_t)) { + return relay::protocol_error_t{}; + } + auto ptr = data.data(); + auto sz = be::big_to_native(*reinterpret_cast(ptr)); + if (sz + sizeof(uint32_t) != data.size()) { + return relay::protocol_error_t{}; + } + auto tail = data.substr(sizeof(uint32_t)); + return relay::wrapped_message_t{header_sz + data.size(), relay::connect_request_t{std::string(tail)}}; +} + +static relay::parse_result_t parse_session_invitation(std::string_view data) noexcept { + if (data.size() < sizeof(uint32_t)) { + return relay::protocol_error_t{}; + } + auto orig = data; + + auto from_sz = be::big_to_native(*reinterpret_cast(data.data())); + data = data.substr(sizeof(uint32_t)); + if (data.size() < from_sz) { + return relay::protocol_error_t{}; + } + auto from = data.substr(0, from_sz); + data = data.substr(from_sz); + + if (data.size() < sizeof(uint32_t)) { + return relay::protocol_error_t{}; + } + auto key_sz = be::big_to_native(*reinterpret_cast(data.data())); + data = data.substr(sizeof(uint32_t)); + if (data.size() < key_sz) { + return relay::protocol_error_t{}; + } + auto key = data.substr(0, key_sz); + data = data.substr(key_sz); + + if (data.size() < sizeof(uint32_t)) { + return relay::protocol_error_t{}; + } + auto addr_sz = be::big_to_native(*reinterpret_cast(data.data())); + data = data.substr(sizeof(uint32_t)); + if (data.size() < key_sz) { + return relay::protocol_error_t{}; + } + auto addr = data.substr(0, addr_sz); + data = data.substr(addr_sz); + + if (data.size() < sizeof(uint32_t)) { + return relay::protocol_error_t{}; + } + auto port = be::big_to_native(*reinterpret_cast(data.data())); + data = data.substr(sizeof(uint32_t)); + + if (data.size() < sizeof(uint32_t)) { + return relay::protocol_error_t{}; + } + auto server_socket = be::big_to_native(*reinterpret_cast(data.data())); + data = data.substr(sizeof(uint32_t)); + + return relay::wrapped_message_t{ + header_sz + orig.size(), + relay::session_invitation_t{std::string(from), std::string(key), std::string(addr), port, (bool)server_socket}}; +} + +relay::parse_result_t parse(std::string_view data) noexcept { + if (data.size() < header_sz) { + return relay::incomplete_t{}; + } + auto ptr = data.data(); + auto header = reinterpret_cast(ptr); + if (be::big_to_native(header->magic) != magic) { + return relay::protocol_error_t{}; + } + auto type = be::big_to_native(header->type); + if (type > (uint32_t)type_t::LAST) { + return relay::protocol_error_t{}; + } + auto sz = be::big_to_native(header->length); + if (sz > max_packet_sz) { + return relay::protocol_error_t{}; + } + if (data.size() < header_sz + sz) { + return relay::incomplete_t{}; + } + auto tail = data.substr(header_sz); + + switch ((type_t)type) { + case type_t::ping: + return parse_ping(tail); + case type_t::pong: + return parse_pong(tail); + case type_t::join_relay_request: + return parse_join_relay_request(tail); + case type_t::join_session_request: + return parse_join_session_request(tail); + case type_t::response: + return parse_response(tail); + case type_t::connect_request: + return parse_connect_request(tail); + case type_t::session_invitation: + return parse_session_invitation(tail); + default: + return relay::protocol_error_t{}; + } +} + +} // namespace syncspirit::proto diff --git a/src/proto/relay_support.h b/src/proto/relay_support.h new file mode 100644 index 00000000..3088ff40 --- /dev/null +++ b/src/proto/relay_support.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2022 Ivan Baidakou + +#pragma once + +#include +#include +#include + +namespace syncspirit::proto { + +namespace relay { + +struct ping_t {}; + +struct pong_t {}; + +struct join_relay_request_t {}; + +struct join_session_request_t { + std::string key; +}; + +struct response_t { + std::uint32_t code; + std::string details; +}; + +struct connect_request_t { + std::string device_id; +}; + +struct session_invitation_t { + std::string from; + std::string key; + std::string address; + std::uint32_t port; + bool server_socket; +}; + +using message_t = std::variant; + +// support +struct incomplete_t {}; +struct protocol_error_t {}; +struct wrapped_message_t { + size_t length; + message_t message; +}; + +using parse_result_t = std::variant; + +} // namespace relay + +size_t serialize(const relay::message_t &, std::string &out) noexcept; + +relay::parse_result_t parse(std::string_view data) noexcept; + +} // namespace syncspirit::proto diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6c91f5bd..d1e44138 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -59,6 +59,10 @@ add_executable(015-logger 015-logger.cpp $<$:win32-resource target_link_libraries(015-logger syncspirit_test_lib) add_test(015-logger "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/015-logger") +add_executable(016-relay-support 016-relay-support.cpp $<$:win32-resource.rc>) +target_link_libraries(016-relay-support syncspirit_test_lib) +add_test(016-relay-support "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/016-relay-support") + add_executable(020-generic-map 020-generic-map.cpp $<$:win32-resource.rc>) target_link_libraries(020-generic-map syncspirit_test_lib) add_test(020-generic-map "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/020-generic-map") From 4999299ddf16aa1022d3cfaef39c50643147a130 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 1 May 2022 16:33:20 +0300 Subject: [PATCH 08/31] refactor --- src/proto/relay_support.cpp | 92 ++++++++++++------------ src/proto/relay_support.h | 9 +-- tests/016-relay-support.cpp | 136 ++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 52 deletions(-) create mode 100644 tests/016-relay-support.cpp diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp index 238a9a0a..8491f0ff 100644 --- a/src/proto/relay_support.cpp +++ b/src/proto/relay_support.cpp @@ -9,7 +9,7 @@ namespace be = boost::endian; template inline constexpr bool always_false_v = false; -namespace syncspirit::proto { +namespace syncspirit::proto::relay { struct header_t { uint32_t magic; @@ -39,23 +39,23 @@ static void serialize_header(char *ptr, type_t type, size_t payload_sz) noexcept std::copy(in, in + header_sz, ptr); } -size_t serialize(const relay::message_t &msg, std::string &out) noexcept { +size_t serialize(const message_t &msg, std::string &out) noexcept { return std::visit( [&](auto &it) -> size_t { using T = std::decay_t; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { out.resize(header_sz); serialize_header(out.data(), type_t::ping, 0); return header_sz; - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { out.resize(header_sz); serialize_header(out.data(), type_t::pong, 0); return header_sz; - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { out.resize(header_sz); serialize_header(out.data(), type_t::join_relay_request, 0); return header_sz; - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { auto key_sz = it.key.size(); auto payload_sz = sizeof(uint32_t) + key_sz; auto sz = header_sz + payload_sz; @@ -67,7 +67,7 @@ size_t serialize(const relay::message_t &msg, std::string &out) noexcept { ptr += sizeof(uint32_t); std::copy(it.key.begin(), it.key.end(), ptr); return sz; - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { auto msg_sz = it.details.size(); auto payload_sz = sizeof(uint32_t) + sizeof(uint32_t) + msg_sz; auto sz = header_sz + payload_sz; @@ -81,7 +81,7 @@ size_t serialize(const relay::message_t &msg, std::string &out) noexcept { ptr += sizeof(uint32_t); std::copy(it.details.begin(), it.details.end(), ptr); return sz; - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { auto id_sz = it.device_id.size(); auto payload_sz = sizeof(uint32_t) + id_sz; auto sz = header_sz + payload_sz; @@ -93,7 +93,7 @@ size_t serialize(const relay::message_t &msg, std::string &out) noexcept { ptr += sizeof(uint32_t); std::copy(it.device_id.begin(), it.device_id.end(), ptr); return sz; - } else if constexpr (std::is_same_v) { + } else if constexpr (std::is_same_v) { auto &from = it.from; auto &key = it.key; auto &address = it.address; @@ -130,131 +130,127 @@ size_t serialize(const relay::message_t &msg, std::string &out) noexcept { msg); } -static relay::parse_result_t parse_ping(std::string_view data) noexcept { - return relay::wrapped_message_t{header_sz, relay::ping_t{}}; -} +static parse_result_t parse_ping(std::string_view data) noexcept { return wrapped_message_t{header_sz, ping_t{}}; } -static relay::parse_result_t parse_pong(std::string_view data) noexcept { - return relay::wrapped_message_t{header_sz, relay::pong_t{}}; -} +static parse_result_t parse_pong(std::string_view data) noexcept { return wrapped_message_t{header_sz, pong_t{}}; } -static relay::parse_result_t parse_join_relay_request(std::string_view data) noexcept { - return relay::wrapped_message_t{header_sz, relay::join_relay_request_t{}}; +static parse_result_t parse_join_relay_request(std::string_view data) noexcept { + return wrapped_message_t{header_sz, join_relay_request_t{}}; } -static relay::parse_result_t parse_join_session_request(std::string_view data) noexcept { +static parse_result_t parse_join_session_request(std::string_view data) noexcept { if (data.size() < sizeof(uint32_t)) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto ptr = data.data(); auto sz = be::big_to_native(*reinterpret_cast(ptr)); if (sz + sizeof(uint32_t) != data.size()) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto tail = data.substr(sizeof(uint32_t)); - return relay::wrapped_message_t{header_sz + data.size(), relay::join_session_request_t{std::string(tail)}}; + return wrapped_message_t{header_sz + data.size(), join_session_request_t{std::string(tail)}}; } -static relay::parse_result_t parse_response(std::string_view data) noexcept { +static parse_result_t parse_response(std::string_view data) noexcept { if (data.size() < sizeof(uint32_t) * 2) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto ptr = data.data(); auto code = be::big_to_native(*reinterpret_cast(ptr)); ptr += sizeof(uint32_t); auto sz = be::big_to_native(*reinterpret_cast(ptr)); if (sz + sizeof(uint32_t) * 2 != data.size()) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto tail = data.substr(sizeof(uint32_t) * 2); - return relay::wrapped_message_t{header_sz + data.size(), relay::response_t{code, std::string(tail)}}; + return wrapped_message_t{header_sz + data.size(), response_t{code, std::string(tail)}}; } -static relay::parse_result_t parse_connect_request(std::string_view data) noexcept { +static parse_result_t parse_connect_request(std::string_view data) noexcept { if (data.size() < sizeof(uint32_t)) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto ptr = data.data(); auto sz = be::big_to_native(*reinterpret_cast(ptr)); if (sz + sizeof(uint32_t) != data.size()) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto tail = data.substr(sizeof(uint32_t)); - return relay::wrapped_message_t{header_sz + data.size(), relay::connect_request_t{std::string(tail)}}; + return wrapped_message_t{header_sz + data.size(), connect_request_t{std::string(tail)}}; } -static relay::parse_result_t parse_session_invitation(std::string_view data) noexcept { +static parse_result_t parse_session_invitation(std::string_view data) noexcept { if (data.size() < sizeof(uint32_t)) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto orig = data; auto from_sz = be::big_to_native(*reinterpret_cast(data.data())); data = data.substr(sizeof(uint32_t)); if (data.size() < from_sz) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto from = data.substr(0, from_sz); data = data.substr(from_sz); if (data.size() < sizeof(uint32_t)) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto key_sz = be::big_to_native(*reinterpret_cast(data.data())); data = data.substr(sizeof(uint32_t)); if (data.size() < key_sz) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto key = data.substr(0, key_sz); data = data.substr(key_sz); if (data.size() < sizeof(uint32_t)) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto addr_sz = be::big_to_native(*reinterpret_cast(data.data())); data = data.substr(sizeof(uint32_t)); if (data.size() < key_sz) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto addr = data.substr(0, addr_sz); data = data.substr(addr_sz); if (data.size() < sizeof(uint32_t)) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto port = be::big_to_native(*reinterpret_cast(data.data())); data = data.substr(sizeof(uint32_t)); if (data.size() < sizeof(uint32_t)) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto server_socket = be::big_to_native(*reinterpret_cast(data.data())); data = data.substr(sizeof(uint32_t)); - return relay::wrapped_message_t{ + return wrapped_message_t{ header_sz + orig.size(), - relay::session_invitation_t{std::string(from), std::string(key), std::string(addr), port, (bool)server_socket}}; + session_invitation_t{std::string(from), std::string(key), std::string(addr), port, (bool)server_socket}}; } -relay::parse_result_t parse(std::string_view data) noexcept { +parse_result_t parse(std::string_view data) noexcept { if (data.size() < header_sz) { - return relay::incomplete_t{}; + return incomplete_t{}; } auto ptr = data.data(); auto header = reinterpret_cast(ptr); if (be::big_to_native(header->magic) != magic) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto type = be::big_to_native(header->type); if (type > (uint32_t)type_t::LAST) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } auto sz = be::big_to_native(header->length); if (sz > max_packet_sz) { - return relay::protocol_error_t{}; + return protocol_error_t{}; } if (data.size() < header_sz + sz) { - return relay::incomplete_t{}; + return incomplete_t{}; } auto tail = data.substr(header_sz); @@ -274,8 +270,8 @@ relay::parse_result_t parse(std::string_view data) noexcept { case type_t::session_invitation: return parse_session_invitation(tail); default: - return relay::protocol_error_t{}; + return protocol_error_t{}; } } -} // namespace syncspirit::proto +} // namespace syncspirit::proto::relay diff --git a/src/proto/relay_support.h b/src/proto/relay_support.h index 3088ff40..83902fd2 100644 --- a/src/proto/relay_support.h +++ b/src/proto/relay_support.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include "syncspirit-export.h" namespace syncspirit::proto { @@ -51,10 +53,9 @@ struct wrapped_message_t { using parse_result_t = std::variant; -} // namespace relay - -size_t serialize(const relay::message_t &, std::string &out) noexcept; +SYNCSPIRIT_API size_t serialize(const relay::message_t &, std::string &out) noexcept; +SYNCSPIRIT_API parse_result_t parse(std::string_view data) noexcept; -relay::parse_result_t parse(std::string_view data) noexcept; +} // namespace relay } // namespace syncspirit::proto diff --git a/tests/016-relay-support.cpp b/tests/016-relay-support.cpp new file mode 100644 index 00000000..61fd3aba --- /dev/null +++ b/tests/016-relay-support.cpp @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2022 Ivan Baidakou + +#include "catch.hpp" +#include "proto/relay_support.h" + +using namespace syncspirit::proto::relay; + +TEST_CASE("relay proto", "[relay]") { + std::string buff; + + SECTION("successful cases") { + SECTION("ping") { + auto sz = serialize(ping_t{}, buff); + REQUIRE(sz); + auto r = parse(buff); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == sz); + auto target = std::get_if(&msg->message); + REQUIRE(target); + } + + SECTION("pong") { + auto sz = serialize(pong_t{}, buff); + REQUIRE(sz); + auto r = parse(buff); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == sz); + auto target = std::get_if(&msg->message); + REQUIRE(target); + } + + SECTION("join_relay_request") { + auto sz = serialize(join_relay_request_t{}, buff); + REQUIRE(sz); + auto r = parse(buff); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == sz); + auto target = std::get_if(&msg->message); + REQUIRE(target); + } + + SECTION("join_session_request") { + auto source = join_session_request_t{"lorem impsum dolor"}; + auto sz = serialize(source, buff); + REQUIRE(sz); + auto r = parse(buff); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == sz); + auto target = std::get_if(&msg->message); + REQUIRE(target); + CHECK(target->key == source.key); + } + + SECTION("response") { + auto source = response_t{404, "not found"}; + auto sz = serialize(source, buff); + REQUIRE(sz); + auto r = parse(buff); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == sz); + auto target = std::get_if(&msg->message); + REQUIRE(target); + CHECK(target->code == source.code); + CHECK(target->details == source.details); + } + + SECTION("connect_request") { + auto source = connect_request_t{"lorem impsum dolor"}; + auto sz = serialize(source, buff); + REQUIRE(sz); + auto r = parse(buff); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == sz); + auto target = std::get_if(&msg->message); + REQUIRE(target); + CHECK(target->device_id == source.device_id); + } + + SECTION("session_invitation") { + auto source = session_invitation_t{"lorem", "impsum", "dolor", 1234, true}; + auto sz = serialize(source, buff); + REQUIRE(sz); + auto r = parse(buff); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == sz); + auto target = std::get_if(&msg->message); + REQUIRE(target); + CHECK(target->from == source.from); + CHECK(target->key == source.key); + CHECK(target->address == source.address); + CHECK(target->server_socket == source.server_socket); + } + } + + SECTION("incompplete") { + auto r = parse(buff); + auto msg = std::get_if(&r); + REQUIRE(msg); + serialize(ping_t{}, buff); + r = parse(std::string_view(buff.data(), buff.size() - 1)); + msg = std::get_if(&r); + REQUIRE(msg); + } + + SECTION("protocol errors") { + SECTION("wrong magic") { + auto sz = serialize(ping_t{}, buff); + REQUIRE(sz); + buff[0] = -1; + auto r = parse(buff); + CHECK(std::get_if(&r)); + } + SECTION("wrong size") { + auto sz = serialize(ping_t{}, buff); + REQUIRE(sz); + buff[4] = -1; + auto r = parse(buff); + CHECK(std::get_if(&r)); + } + SECTION("wrong type") { + auto sz = serialize(ping_t{}, buff); + REQUIRE(sz); + buff[9] = -1; + auto r = parse(buff); + CHECK(std::get_if(&r)); + } + } +} From 0f7c3d5342b2949d82ebe4d5453d1f96f9a88ce1 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 1 May 2022 17:39:37 +0300 Subject: [PATCH 09/31] parse relays endpoint --- src/proto/relay_support.cpp | 68 +++++++++++++++++++++++++++++++++++++ src/proto/relay_support.h | 29 +++++++++++++--- tests/016-relay-support.cpp | 66 +++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 4 deletions(-) diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp index 8491f0ff..a434fc55 100644 --- a/src/proto/relay_support.cpp +++ b/src/proto/relay_support.cpp @@ -2,10 +2,13 @@ // SPDX-FileCopyrightText: 2022 Ivan Baidakou #include "relay_support.h" +#include "utils/error_code.h" +#include #include #include namespace be = boost::endian; +using json = nlohmann::json; template inline constexpr bool always_false_v = false; @@ -274,4 +277,69 @@ parse_result_t parse(std::string_view data) noexcept { } } +outcome::result parse_endpoint(std::string_view buff) noexcept { + using namespace syncspirit::utils; + auto data = json::parse(buff.begin(), buff.end(), nullptr, false); + if (data.is_discarded()) { + return make_error_code(error_code_t::malformed_json); + } + if (!data.is_object()) { + return make_error_code(error_code_t::incorrect_json); + } + + auto &relays = data["relays"]; + if (!relays.is_array()) { + return make_error_code(error_code_t::incorrect_json); + } + + auto r = relay_infos_t{}; + for (auto &it : relays) { + if (!it.is_object()) { + return make_error_code(error_code_t::incorrect_json); + } + auto &url = it["url"]; + if (!url.is_string()) { + return make_error_code(error_code_t::incorrect_json); + } + auto uri_str = url.get(); + auto uri_option = utils::parse(uri_str.c_str()); + if (!uri_option) { + return make_error_code(error_code_t::incorrect_json); + } + auto &location = it["location"]; + if (!location.is_object()) { + return make_error_code(error_code_t::incorrect_json); + } + auto &latitude = location["latitude"]; + if (!latitude.is_number_float()) { + return make_error_code(error_code_t::incorrect_json); + } + auto &longitude = location["longitude"]; + if (!longitude.is_number_float()) { + return make_error_code(error_code_t::incorrect_json); + } + auto &city = location["city"]; + if (!city.is_string()) { + return make_error_code(error_code_t::incorrect_json); + } + auto &country = location["country"]; + if (!country.is_string()) { + return make_error_code(error_code_t::incorrect_json); + } + auto &continent = location["continent"]; + if (!continent.is_string()) { + return make_error_code(error_code_t::incorrect_json); + } + auto relay = relay_info_ptr_t{new relay_info_t{std::move(uri_option.value()), location_t{ + latitude.get(), + longitude.get(), + city.get(), + country.get(), + continent.get(), + }}}; + r.emplace_back(std::move(relay)); + } + return std::move(r); +} + } // namespace syncspirit::proto::relay diff --git a/src/proto/relay_support.h b/src/proto/relay_support.h index 83902fd2..aa1fa607 100644 --- a/src/proto/relay_support.h +++ b/src/proto/relay_support.h @@ -6,12 +6,15 @@ #include #include #include +#include #include +#include "utils/uri.h" #include "syncspirit-export.h" +#include -namespace syncspirit::proto { +namespace syncspirit::proto::relay { -namespace relay { +namespace outcome = boost::outcome_v2; struct ping_t {}; @@ -56,6 +59,24 @@ using parse_result_t = std::variant { + inline relay_info_t(utils::URI uri_, location_t location_) noexcept + : uri(std::move(uri_)), location{std::move(location_)} {} + utils::URI uri; + location_t location; +}; + +using relay_info_ptr_t = model::intrusive_ptr_t; +using relay_infos_t = std::vector; + +SYNCSPIRIT_API outcome::result parse_endpoint(std::string_view data) noexcept; -} // namespace syncspirit::proto +} // namespace syncspirit::proto::relay diff --git a/tests/016-relay-support.cpp b/tests/016-relay-support.cpp index 61fd3aba..9729891f 100644 --- a/tests/016-relay-support.cpp +++ b/tests/016-relay-support.cpp @@ -134,3 +134,69 @@ TEST_CASE("relay proto", "[relay]") { } } } + +TEST_CASE("endpoing parsing", "[relay]") { + std::string body = R""( +{ + "relays": [ + { + "url": "relay://130.61.176.206:22067/?id=OAKAXEX-7HE764M-5EWVN7U-SZCQU4D-ZPXF2TY-SNTL2LL-Y5RVGVM-U7WBRA3&pingInterval=1m0s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=ina", + "location": { + "latitude": 50.1049, + "longitude": 8.6295, + "city": "Frankfurt am Main", + "country": "DE", + "continent": "EU" + }, + "stats": { + "startTime": "2022-04-16T09:12:21.941097618Z", + "uptimeSeconds": 1312802, + "numPendingSessionKeys": 0, + "numActiveSessions": 46, + "numConnections": 494, + "numProxies": 88, + "bytesProxied": 493207937616, + "goVersion": "go1.16.3", + "goOS": "linux", + "goArch": "arm64", + "goMaxProcs": 4, + "goNumRoutine": 1136, + "kbps10s1m5m15m30m60m": [ + 241, + 284, + 309, + 332, + 355, + 312 + ], + "options": { + "network-timeout": 120, + "ping-interval": 60, + "message-timeout": 60, + "per-session-rate": 0, + "global-rate": 0, + "pools": [ + "https://relays.syncthing.net/endpoint" + ], + "provided-by": "ina" + } + }, + "statsRetrieved": "2022-05-01T13:52:24.524417759Z" + } +] +} + )""; + auto r = parse_endpoint(body); + REQUIRE(r); + REQUIRE(r.value().size() == 1); + auto relay = r.value()[0]; + CHECK(relay->uri.full == "relay://130.61.176.206:22067/" + "?id=OAKAXEX-7HE764M-5EWVN7U-SZCQU4D-ZPXF2TY-SNTL2LL-Y5RVGVM-U7WBRA3&pingInterval=1m0s&" + "networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=ina"); + auto &l = relay->location; + CHECK(abs(l.latitude - 50.1049) < 0.0001); + CHECK(abs(l.longitude - 8.6295) < 0.0001); + CHECK(l.city == "Frankfurt am Main"); + CHECK(l.country == "DE"); + CHECK(l.continent == "EU"); +} From 9883ae700c76240e77de815a7d5457a178551e77 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 1 May 2022 18:16:23 +0300 Subject: [PATCH 10/31] refactor --- src/config/main.h | 2 ++ src/config/relay.h | 15 +++++++++++ src/config/utils.cpp | 46 -------------------------------- src/config/utils.h | 11 -------- tests/014-configuration.cpp | 53 +++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 6 ++++- 6 files changed, 75 insertions(+), 58 deletions(-) create mode 100644 src/config/relay.h diff --git a/src/config/main.h b/src/config/main.h index 80d2312e..b096dbbb 100644 --- a/src/config/main.h +++ b/src/config/main.h @@ -11,6 +11,7 @@ #include "global_announce.h" #include "local_announce.h" #include "log.h" +#include "relay.h" #include "upnp.h" #include @@ -31,6 +32,7 @@ struct main_t { dialer_config_t dialer_config; fs_config_t fs_config; db_config_t db_config; + relay_config_t relay_config; std::uint32_t timeout; std::string device_name; diff --git a/src/config/relay.h b/src/config/relay.h new file mode 100644 index 00000000..87db59b1 --- /dev/null +++ b/src/config/relay.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2022 Ivan Baidakou + +#pragma once + +#include + +namespace syncspirit::config { + +struct relay_config_t { + bool enabled; + std::string discovery_url; +}; + +} // namespace syncspirit::config diff --git a/src/config/utils.cpp b/src/config/utils.cpp index 94fd3c61..347c36f2 100644 --- a/src/config/utils.cpp +++ b/src/config/utils.cpp @@ -22,51 +22,6 @@ static const std::string home_path = "~/.config/syncspirit"; namespace syncspirit::config { -bool operator==(const bep_config_t &lhs, const bep_config_t &rhs) noexcept { - return lhs.rx_buff_size == rhs.rx_buff_size && lhs.connect_timeout == rhs.connect_timeout && - lhs.request_timeout == rhs.request_timeout && lhs.tx_timeout == rhs.tx_timeout && - lhs.rx_timeout == rhs.rx_timeout && lhs.blocks_max_requested == rhs.blocks_max_requested; -} - -bool operator==(const dialer_config_t &lhs, const dialer_config_t &rhs) noexcept { - return lhs.enabled == rhs.enabled && lhs.redial_timeout == rhs.redial_timeout; -} - -bool operator==(const fs_config_t &lhs, const fs_config_t &rhs) noexcept { - return lhs.temporally_timeout == rhs.temporally_timeout && lhs.mru_size == rhs.mru_size; -} - -bool operator==(const db_config_t &lhs, const db_config_t &rhs) noexcept { - return lhs.upper_limit == rhs.upper_limit && lhs.uncommited_threshold == rhs.uncommited_threshold; -} - -bool operator==(const global_announce_config_t &lhs, const global_announce_config_t &rhs) noexcept { - return lhs.enabled == rhs.enabled && lhs.announce_url == rhs.announce_url && lhs.device_id == rhs.device_id && - lhs.cert_file == rhs.cert_file && lhs.key_file == rhs.key_file && lhs.rx_buff_size == rhs.rx_buff_size && - lhs.timeout == rhs.timeout && lhs.reannounce_after == rhs.reannounce_after; -} - -bool operator==(const local_announce_config_t &lhs, const local_announce_config_t &rhs) noexcept { - return lhs.enabled == rhs.enabled && lhs.port == rhs.port && lhs.frequency == rhs.frequency; -} - -bool operator==(const log_config_t &lhs, const log_config_t &rhs) noexcept { - return lhs.name == rhs.name && lhs.level == rhs.level && lhs.sinks == rhs.sinks; -} - -bool operator==(const main_t &lhs, const main_t &rhs) noexcept { - return lhs.local_announce_config == rhs.local_announce_config && lhs.upnp_config == rhs.upnp_config && - lhs.global_announce_config == rhs.global_announce_config && lhs.bep_config == rhs.bep_config && - lhs.db_config == rhs.db_config && lhs.timeout == rhs.timeout && lhs.device_name == rhs.device_name && - lhs.config_path == rhs.config_path && lhs.log_configs == rhs.log_configs && - lhs.hasher_threads == rhs.hasher_threads; -} - -bool operator==(const upnp_config_t &lhs, const upnp_config_t &rhs) noexcept { - return lhs.enabled == rhs.enabled && lhs.max_wait == rhs.max_wait && lhs.external_port == rhs.external_port && - lhs.rx_buff_size == rhs.rx_buff_size && lhs.debug == rhs.debug; -} - using device_name_t = outcome::result; static std::string expand_home(const std::string &path, const char *home) { @@ -562,5 +517,4 @@ outcome::result generate_config(const boost::filesystem::path &config_pa return cfg; } - } diff --git a/src/config/utils.h b/src/config/utils.h index 296b160f..56863da9 100644 --- a/src/config/utils.h +++ b/src/config/utils.h @@ -13,17 +13,6 @@ namespace outcome = boost::outcome_v2; // comparators -SYNCSPIRIT_API bool operator==(const bep_config_t &lhs, const bep_config_t &rhs) noexcept; -SYNCSPIRIT_API bool operator==(const dialer_config_t &lhs, const dialer_config_t &rhs) noexcept; -SYNCSPIRIT_API bool operator==(const fs_config_t &lhs, const fs_config_t &rhs) noexcept; -SYNCSPIRIT_API bool operator==(const db_config_t &lhs, const db_config_t &rhs) noexcept; -SYNCSPIRIT_API bool operator==(const global_announce_config_t &lhs, const global_announce_config_t &rhs) noexcept; -SYNCSPIRIT_API bool operator==(const local_announce_config_t &lhs, const local_announce_config_t &rhs) noexcept; -SYNCSPIRIT_API bool operator==(const main_t &lhs, const main_t &rhs) noexcept; -SYNCSPIRIT_API bool operator==(const upnp_config_t &lhs, const upnp_config_t &rhs) noexcept; - -// misc - using config_result_t = outcome::outcome; SYNCSPIRIT_API config_result_t get_config(std::istream &config, const boost::filesystem::path &config_path); diff --git a/tests/014-configuration.cpp b/tests/014-configuration.cpp index f45fb9e3..994e0afa 100644 --- a/tests/014-configuration.cpp +++ b/tests/014-configuration.cpp @@ -8,6 +8,59 @@ #include #include +namespace syncspirit::config { + +bool operator==(const bep_config_t &lhs, const bep_config_t &rhs) noexcept { + return lhs.rx_buff_size == rhs.rx_buff_size && lhs.connect_timeout == rhs.connect_timeout && + lhs.request_timeout == rhs.request_timeout && lhs.tx_timeout == rhs.tx_timeout && + lhs.rx_timeout == rhs.rx_timeout && lhs.blocks_max_requested == rhs.blocks_max_requested; +} + +bool operator==(const dialer_config_t &lhs, const dialer_config_t &rhs) noexcept { + return lhs.enabled == rhs.enabled && lhs.redial_timeout == rhs.redial_timeout; +} + +bool operator==(const fs_config_t &lhs, const fs_config_t &rhs) noexcept { + return lhs.temporally_timeout == rhs.temporally_timeout && lhs.mru_size == rhs.mru_size; +} + +bool operator==(const db_config_t &lhs, const db_config_t &rhs) noexcept { + return lhs.upper_limit == rhs.upper_limit && lhs.uncommited_threshold == rhs.uncommited_threshold; +} + +bool operator==(const global_announce_config_t &lhs, const global_announce_config_t &rhs) noexcept { + return lhs.enabled == rhs.enabled && lhs.announce_url == rhs.announce_url && lhs.device_id == rhs.device_id && + lhs.cert_file == rhs.cert_file && lhs.key_file == rhs.key_file && lhs.rx_buff_size == rhs.rx_buff_size && + lhs.timeout == rhs.timeout && lhs.reannounce_after == rhs.reannounce_after; +} + +bool operator==(const local_announce_config_t &lhs, const local_announce_config_t &rhs) noexcept { + return lhs.enabled == rhs.enabled && lhs.port == rhs.port && lhs.frequency == rhs.frequency; +} + +bool operator==(const log_config_t &lhs, const log_config_t &rhs) noexcept { + return lhs.name == rhs.name && lhs.level == rhs.level && lhs.sinks == rhs.sinks; +} + +bool operator==(const upnp_config_t &lhs, const upnp_config_t &rhs) noexcept { + return lhs.enabled == rhs.enabled && lhs.max_wait == rhs.max_wait && lhs.external_port == rhs.external_port && + lhs.rx_buff_size == rhs.rx_buff_size && lhs.debug == rhs.debug; +} + +bool operator==(const relay_config_t &lhs, const relay_config_t &rhs) noexcept { + return lhs.enabled == rhs.enabled && lhs.discovery_url == rhs.discovery_url; +} + +bool operator==(const main_t &lhs, const main_t &rhs) noexcept { + return lhs.local_announce_config == rhs.local_announce_config && lhs.upnp_config == rhs.upnp_config && + lhs.global_announce_config == rhs.global_announce_config && lhs.bep_config == rhs.bep_config && + lhs.db_config == rhs.db_config && lhs.timeout == rhs.timeout && lhs.device_name == rhs.device_name && + lhs.config_path == rhs.config_path && lhs.log_configs == rhs.log_configs && + lhs.hasher_threads == rhs.hasher_threads; +} + +} // namespace syncspirit::config + namespace sys = boost::system; namespace fs = boost::filesystem; namespace st = syncspirit::test; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d1e44138..9239ece4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -160,5 +160,9 @@ target_link_libraries(076-scan_actor syncspirit_test_lib) add_test(076-scan_actor "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/076-scan_actor") add_executable(077-initiator 077-initiator.cpp $<$:win32-resource.rc>) -target_link_libraries(077-initiator syncspirit_test_lib) +target_link_libraries(077-initiator syncspirit_test_lib + $<$:wsock32> + $<$:ws2_32> + $<$:iphlpapi> +) add_test(077-initiator "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/077-initiator") From e99d4830f8e6633e35aab19c95943a55e20b179c Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 1 May 2022 23:12:39 +0300 Subject: [PATCH 11/31] relay_actor, WIP --- CMakeLists.txt | 1 + src/config/relay.h | 1 + src/config/utils.cpp | 34 ++++++++++++ src/hasher/hasher_actor.cpp | 1 - src/hasher/hasher_proxy_actor.cpp | 1 - src/net/names.cpp | 1 + src/net/names.h | 1 + src/net/net_supervisor.cpp | 29 +++++++++++ src/proto/relay_support.cpp | 35 +++++++------ src/transport/http.cpp | 14 ++--- src/transport/impl.hpp | 87 ++++++++++++++++--------------- src/utils/error_code.cpp | 3 ++ src/utils/error_code.h | 1 + tests/014-configuration.cpp | 3 +- 14 files changed, 142 insertions(+), 70 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb0c1de5..7f97cff7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,6 +134,7 @@ add_library(syncspirit_lib src/net/net_supervisor.cpp src/net/peer_actor.cpp src/net/peer_supervisor.cpp + src/net/relay_actor.cpp src/net/resolver_actor.cpp src/net/names.cpp src/net/ssdp_actor.cpp diff --git a/src/config/relay.h b/src/config/relay.h index 87db59b1..356065ae 100644 --- a/src/config/relay.h +++ b/src/config/relay.h @@ -10,6 +10,7 @@ namespace syncspirit::config { struct relay_config_t { bool enabled; std::string discovery_url; + std::uint32_t rx_buff_size; }; } // namespace syncspirit::config diff --git a/src/config/utils.cpp b/src/config/utils.cpp index 347c36f2..1d334ce4 100644 --- a/src/config/utils.cpp +++ b/src/config/utils.cpp @@ -237,6 +237,30 @@ config_result_t get_config(std::istream &config, const boost::filesystem::path & c.debug = debug.value(); }; + // relay + { + auto t = root_tbl["relay"]; + auto &c = cfg.relay_config; + + auto enabled = t["enabled"].value(); + if (!enabled) { + return "relay/enabled is incorrect or missing"; + } + c.enabled = enabled.value(); + + auto discovery_url = t["discovery_url"].value(); + if (!discovery_url) { + return "upnp/discovery_url is incorrect or missing"; + } + c.discovery_url = discovery_url.value(); + + auto rx_buff_size = t["rx_buff_size"].value(); + if (!rx_buff_size) { + return "relay/rx_buff_size is incorrect or missing"; + } + c.rx_buff_size = rx_buff_size.value(); + }; + // bep { auto t = root_tbl["bep"]; @@ -422,6 +446,11 @@ outcome::result serialize(const main_t cfg, std::ostream &out) noexcept { {"upper_limit", cfg.db_config.upper_limit}, {"uncommited_threshold", cfg.db_config.uncommited_threshold}, }}}, + {"relay", toml::table{{ + {"enabled", cfg.relay_config.enabled}, + {"discovery_url", cfg.relay_config.discovery_url}, + {"rx_buff_size", cfg.relay_config.rx_buff_size}, + }}}, }}; // clang-format on out << tbl; @@ -514,6 +543,11 @@ outcome::result generate_config(const boost::filesystem::path &config_pa 0x400000000, /* upper_limit, 16Gb */ 150, /* uncommited_threshold */ }; + cfg.relay_config = relay_config_t { + true, /* enabled */ + "https://relays.syncthing.net/endpoint", /* discovery url */ + 1024 * 1024, /* rx buff size */ + }; return cfg; } diff --git a/src/hasher/hasher_actor.cpp b/src/hasher/hasher_actor.cpp index abf9a4c7..19ea914c 100644 --- a/src/hasher/hasher_actor.cpp +++ b/src/hasher/hasher_actor.cpp @@ -2,7 +2,6 @@ // SPDX-FileCopyrightText: 2019-2022 Ivan Baidakou #include "hasher_actor.h" -#include "../net/names.h" #include "../utils/tls.h" #include #include diff --git a/src/hasher/hasher_proxy_actor.cpp b/src/hasher/hasher_proxy_actor.cpp index ea1f2dbf..089f4109 100644 --- a/src/hasher/hasher_proxy_actor.cpp +++ b/src/hasher/hasher_proxy_actor.cpp @@ -2,7 +2,6 @@ // SPDX-FileCopyrightText: 2019-2022 Ivan Baidakou #include "hasher_proxy_actor.h" -#include "../net/names.h" #include "../utils/error_code.h" #include #include diff --git a/src/net/names.cpp b/src/net/names.cpp index 079f378e..7743a57d 100644 --- a/src/net/names.cpp +++ b/src/net/names.cpp @@ -9,4 +9,5 @@ const char *names::coordinator = "net::coodinator"; const char *names::resolver = "net::resolver"; const char *names::http10 = "net::http10"; const char *names::http11_gda = "net::http11_gda"; +const char *names::http11_relay = "net::http11_relay"; const char *names::hasher_proxy = "net::hasher_proxy"; diff --git a/src/net/names.h b/src/net/names.h index f62835a8..09b8f1fe 100644 --- a/src/net/names.h +++ b/src/net/names.h @@ -13,6 +13,7 @@ struct SYNCSPIRIT_API names { static const char *resolver; static const char *http10; static const char *http11_gda; + static const char *http11_relay; static const char *hasher_proxy; }; diff --git a/src/net/net_supervisor.cpp b/src/net/net_supervisor.cpp index c7923de6..5b42131a 100644 --- a/src/net/net_supervisor.cpp +++ b/src/net/net_supervisor.cpp @@ -14,6 +14,7 @@ #include "peer_supervisor.h" #include "dialer_actor.h" #include "db_actor.h" +#include "relay_actor.h" #include "names.h" #include #include @@ -297,6 +298,34 @@ void net_supervisor_t::launch_net() noexcept { .spawn(); } + if (app_config.relay_config.enabled) { + auto timeout = shutdown_timeout * 9 / 10; + auto io_timeout = shutdown_timeout * 8 / 10; + create_actor() + .timeout(timeout) + .request_timeout(io_timeout) + .resolve_timeout(io_timeout) + .registry_name(names::http11_relay) + .keep_alive(true) + .escalate_failure() + .finish(); + + auto factory = [this](r::supervisor_t &sup, const r::address_ptr_t &spawner) -> r::actor_ptr_t { + auto timeout = shutdown_timeout * 9 / 10; + return create_actor() + .timeout(timeout) + .relay_config(app_config.relay_config) + .cluster(cluster) + .spawner_address(spawner) + .finish(); + }; + spawn(factory) + .restart_period(pt::seconds{5}) + .restart_period(r::pt::seconds{10}) + .restart_policy(r::restart_policy_t::fail_only) + .spawn(); + } + auto timeout = shutdown_timeout * 9 / 10; create_actor().cluster(cluster).timeout(timeout).escalate_failure().finish(); create_actor() diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp index a434fc55..efb4b876 100644 --- a/src/proto/relay_support.cpp +++ b/src/proto/relay_support.cpp @@ -295,48 +295,53 @@ outcome::result parse_endpoint(std::string_view buff) noexcept { auto r = relay_infos_t{}; for (auto &it : relays) { if (!it.is_object()) { + continue; return make_error_code(error_code_t::incorrect_json); } auto &url = it["url"]; if (!url.is_string()) { - return make_error_code(error_code_t::incorrect_json); + continue; } auto uri_str = url.get(); auto uri_option = utils::parse(uri_str.c_str()); if (!uri_option) { - return make_error_code(error_code_t::incorrect_json); + continue; + } + auto& uri = uri_option.value(); + if (uri.proto != "relay") { + continue; } auto &location = it["location"]; if (!location.is_object()) { - return make_error_code(error_code_t::incorrect_json); + continue; } auto &latitude = location["latitude"]; if (!latitude.is_number_float()) { - return make_error_code(error_code_t::incorrect_json); + continue; } auto &longitude = location["longitude"]; if (!longitude.is_number_float()) { - return make_error_code(error_code_t::incorrect_json); + continue; } auto &city = location["city"]; if (!city.is_string()) { - return make_error_code(error_code_t::incorrect_json); + continue; } auto &country = location["country"]; if (!country.is_string()) { - return make_error_code(error_code_t::incorrect_json); + continue; } auto &continent = location["continent"]; if (!continent.is_string()) { - return make_error_code(error_code_t::incorrect_json); + continue; } - auto relay = relay_info_ptr_t{new relay_info_t{std::move(uri_option.value()), location_t{ - latitude.get(), - longitude.get(), - city.get(), - country.get(), - continent.get(), - }}}; + auto relay = relay_info_ptr_t{new relay_info_t{std::move(uri), location_t{ + latitude.get(), + longitude.get(), + city.get(), + country.get(), + continent.get(), + }}}; r.emplace_back(std::move(relay)); } return std::move(r); diff --git a/src/transport/http.cpp b/src/transport/http.cpp index 8b84aa7f..a428918c 100644 --- a/src/transport/http.cpp +++ b/src/transport/http.cpp @@ -17,8 +17,6 @@ template struct http_impl_t : base_impl_t, interface_t; - // virtual ~http_impl_t() {}; - void async_read(rx_buff_t &rx_buff, response_t &response, io_fn_t &on_read, error_fn_t &on_error) noexcept override { auto owner = curry_io(*this, on_read, on_error); @@ -40,14 +38,10 @@ template struct http_impl_t : base_impl_t, interface_t(config); - } - } else { - if (proto == "https") { - return new http_impl_t(config); - } + if (proto == "http") { + return new http_impl_t(config); + } else if (proto == "https") { + return new http_impl_t(config); } return http_sp_t(); } diff --git a/src/transport/impl.hpp b/src/transport/impl.hpp index e34638b4..00f3f766 100644 --- a/src/transport/impl.hpp +++ b/src/transport/impl.hpp @@ -70,18 +70,16 @@ template <> struct base_impl_t { : supervisor{config.supervisor}, strand{supervisor.get_strand()}, sock(strand.context()) {} tcp_socket_t &get_physical_layer() noexcept { return sock; } - // virtual ~base_impl_t(){} }; template <> struct base_impl_t { using self_t = base_impl_t; - // virtual ~base_impl_t(){} rotor::asio::supervisor_asio_t &supervisor; strand_t &strand; model::device_id_t expected_peer; model::device_id_t actual_peer; - const utils::key_pair_t &me; + const utils::key_pair_t *me = nullptr; ssl::context ctx; ssl::stream_base::handshake_type role; ssl_socket_t sock; @@ -92,10 +90,13 @@ template <> struct base_impl_t { ssl::context ctx(ssl::context::tls); ctx.set_options(ssl::context::default_workarounds | ssl::context::no_sslv2); - auto &cert_data = source.me.cert_data.bytes; - auto &key_data = source.me.key_data.bytes; - ctx.use_certificate(asio::const_buffer(cert_data.c_str(), cert_data.size()), ssl::context::asn1); - ctx.use_private_key(asio::const_buffer(key_data.c_str(), key_data.size()), ssl::context::asn1); + auto me = source.me; + if (me) { + auto &cert_data = me->cert_data.bytes; + auto &key_data = me->key_data.bytes; + ctx.use_certificate(asio::const_buffer(cert_data.c_str(), cert_data.size()), ssl::context::asn1); + ctx.use_private_key(asio::const_buffer(key_data.c_str(), key_data.size()), ssl::context::asn1); + } if (alpn.size()) { std::byte wire_alpn[alpn.size() + 1]; @@ -122,7 +123,7 @@ template <> struct base_impl_t { base_impl_t(transport_config_t &config) noexcept : supervisor{config.supervisor}, strand{supervisor.get_strand()}, expected_peer{config.ssl_junction->peer}, - me(*config.ssl_junction->me), ctx(get_context(*this, config.ssl_junction->alpn)), + me(config.ssl_junction->me), ctx(get_context(*this, config.ssl_junction->alpn)), role(config.sock ? ssl::stream_base::server : ssl::stream_base::client), sock(mk_sock(config, ctx, strand)) { if (config.ssl_junction->sni_extension) { auto &host = config.uri.host; @@ -131,44 +132,46 @@ template <> struct base_impl_t { spdlog::error("http_actor_t:: Set SNI Hostname : {}", ec.message()); } } - auto mode = ssl::verify_peer | ssl::verify_fail_if_no_peer_cert | ssl::verify_client_once; - sock.set_verify_depth(1); - sock.set_verify_mode(mode); - sock.set_verify_callback([&](bool, ssl::verify_context &peer_ctx) -> bool { - auto native = peer_ctx.native_handle(); - auto peer_cert = X509_STORE_CTX_get_current_cert(native); - if (!peer_cert) { - spdlog::warn("no peer certificate"); - return false; - } - auto der_option = utils::as_serialized_der(peer_cert); - if (!der_option) { - spdlog::warn("peer certificate cannot be serialized as der : {}", der_option.error().message()); - return false; - } + if (me) { + auto mode = ssl::verify_peer | ssl::verify_fail_if_no_peer_cert | ssl::verify_client_once; + sock.set_verify_mode(mode); + sock.set_verify_depth(1); + sock.set_verify_callback([&](bool, ssl::verify_context &peer_ctx) -> bool { + auto native = peer_ctx.native_handle(); + auto peer_cert = X509_STORE_CTX_get_current_cert(native); + if (!peer_cert) { + spdlog::warn("no peer certificate"); + return false; + } + auto der_option = utils::as_serialized_der(peer_cert); + if (!der_option) { + spdlog::warn("peer certificate cannot be serialized as der : {}", der_option.error().message()); + return false; + } - utils::cert_data_t cert_data{std::move(der_option.value())}; - auto peer_option = model::device_id_t::from_cert(cert_data); - if (!peer_option) { - spdlog::warn("cannot get device_id from peer"); - return false; - } + utils::cert_data_t cert_data{std::move(der_option.value())}; + auto peer_option = model::device_id_t::from_cert(cert_data); + if (!peer_option) { + spdlog::warn("cannot get device_id from peer"); + return false; + } - auto peer = std::move(peer_option.value()); - if (!actual_peer) { - actual_peer = std::move(peer); - spdlog::trace("tls, peer device_id = {}", actual_peer); - } + auto peer = std::move(peer_option.value()); + if (!actual_peer) { + actual_peer = std::move(peer); + spdlog::trace("tls, peer device_id = {}", actual_peer); + } - if (role == ssl::stream_base::handshake_type::client) { - if (actual_peer != expected_peer) { - spdlog::warn("unexcpected peer device_id. Got: {}, expected: {}", actual_peer, expected_peer); - return false; + if (role == ssl::stream_base::handshake_type::client) { + if (actual_peer != expected_peer) { + spdlog::warn("unexcpected peer device_id. Got: {}, expected: {}", actual_peer, expected_peer); + return false; + } } - } - validation_passed = true; - return true; - }); + validation_passed = true; + return true; + }); + } } tcp_socket_t &get_physical_layer() noexcept { return sock.next_layer(); } diff --git a/src/utils/error_code.cpp b/src/utils/error_code.cpp index 0234d7dd..a9973b0c 100644 --- a/src/utils/error_code.cpp +++ b/src/utils/error_code.cpp @@ -101,6 +101,9 @@ std::string error_code_category::message(int c) const { case error_code_t::already_connected: r = "peer is already connected"; break; + case error_code_t::cannot_get_public_relays: + r = "cannot get public relays"; + break; default: r = "unknown"; } diff --git a/src/utils/error_code.h b/src/utils/error_code.h index 41225d4b..36e2c0fa 100644 --- a/src/utils/error_code.h +++ b/src/utils/error_code.h @@ -68,6 +68,7 @@ enum class error_code_t { unknown_sink, misconfigured_default_logger, already_connected, + cannot_get_public_relays, }; enum class bep_error_code_t { diff --git a/tests/014-configuration.cpp b/tests/014-configuration.cpp index 994e0afa..3f95f243 100644 --- a/tests/014-configuration.cpp +++ b/tests/014-configuration.cpp @@ -48,7 +48,8 @@ bool operator==(const upnp_config_t &lhs, const upnp_config_t &rhs) noexcept { } bool operator==(const relay_config_t &lhs, const relay_config_t &rhs) noexcept { - return lhs.enabled == rhs.enabled && lhs.discovery_url == rhs.discovery_url; + return lhs.enabled == rhs.enabled && lhs.discovery_url == rhs.discovery_url && + lhs.rx_buff_size == rhs.rx_buff_size; } bool operator==(const main_t &lhs, const main_t &rhs) noexcept { From 596af2e3914dc56f1ff9e21a3fc8644035330903 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Mon, 2 May 2022 09:35:43 +0300 Subject: [PATCH 12/31] refactor --- src/net/http_actor.h | 4 ++-- src/proto/relay_support.cpp | 14 +++++++------- tests/014-configuration.cpp | 3 +-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/net/http_actor.h b/src/net/http_actor.h index 6b0ad548..27b9e23a 100644 --- a/src/net/http_actor.h +++ b/src/net/http_actor.h @@ -3,8 +3,8 @@ #pragma once -#include "../transport/http.h" -#include "../utils/log.h" +#include "transport/http.h" +#include "utils/log.h" #include "messages.h" #include #include diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp index efb4b876..1960081c 100644 --- a/src/proto/relay_support.cpp +++ b/src/proto/relay_support.cpp @@ -307,7 +307,7 @@ outcome::result parse_endpoint(std::string_view buff) noexcept { if (!uri_option) { continue; } - auto& uri = uri_option.value(); + auto &uri = uri_option.value(); if (uri.proto != "relay") { continue; } @@ -336,12 +336,12 @@ outcome::result parse_endpoint(std::string_view buff) noexcept { continue; } auto relay = relay_info_ptr_t{new relay_info_t{std::move(uri), location_t{ - latitude.get(), - longitude.get(), - city.get(), - country.get(), - continent.get(), - }}}; + latitude.get(), + longitude.get(), + city.get(), + country.get(), + continent.get(), + }}}; r.emplace_back(std::move(relay)); } return std::move(r); diff --git a/tests/014-configuration.cpp b/tests/014-configuration.cpp index 3f95f243..6e9dc2b7 100644 --- a/tests/014-configuration.cpp +++ b/tests/014-configuration.cpp @@ -48,8 +48,7 @@ bool operator==(const upnp_config_t &lhs, const upnp_config_t &rhs) noexcept { } bool operator==(const relay_config_t &lhs, const relay_config_t &rhs) noexcept { - return lhs.enabled == rhs.enabled && lhs.discovery_url == rhs.discovery_url && - lhs.rx_buff_size == rhs.rx_buff_size; + return lhs.enabled == rhs.enabled && lhs.discovery_url == rhs.discovery_url && lhs.rx_buff_size == rhs.rx_buff_size; } bool operator==(const main_t &lhs, const main_t &rhs) noexcept { From df6da897afc079826438b8a4cbc39cda1784d39c Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Fri, 6 May 2022 16:59:06 +0300 Subject: [PATCH 13/31] relay_actor, WIP --- src/net/initiator_actor.cpp | 20 +- src/net/initiator_actor.h | 17 ++ src/net/messages.h | 13 ++ src/net/names.cpp | 1 + src/net/names.h | 1 + src/net/peer_supervisor.cpp | 35 +++- src/net/peer_supervisor.h | 5 +- src/net/relay_actor.cpp | 398 ++++++++++++++++++++++++++++++++++++ src/net/relay_actor.h | 108 ++++++++++ src/proto/relay_support.cpp | 82 +++++++- src/proto/relay_support.h | 10 +- src/transport/base.h | 4 + src/transport/stream.h | 2 - src/utils/error_code.cpp | 9 + src/utils/error_code.h | 3 + src/utils/uri.cpp | 18 ++ src/utils/uri.h | 6 + tests/016-relay-support.cpp | 22 +- tests/077-initiator.cpp | 47 ++++- 19 files changed, 769 insertions(+), 32 deletions(-) create mode 100644 src/net/relay_actor.cpp create mode 100644 src/net/relay_actor.h diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 321509d0..40d60f6e 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -20,7 +20,8 @@ r::plugin::resource_id_t handshake = 3; initiator_actor_t::initiator_actor_t(config_t &cfg) : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, uris{cfg.uris}, ssl_pair{*cfg.ssl_pair}, - sock(std::move(cfg.sock)), cluster{std::move(cfg.cluster)} { + sock(std::move(cfg.sock)), cluster{std::move(cfg.cluster)}, sink(std::move(cfg.sink)), + custom(std::move(cfg.custom)) { log = utils::get_logger("net.initator"); active = !sock.has_value(); } @@ -42,11 +43,13 @@ void initiator_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { p.subscribe_actor(&initiator_actor_t::on_resolve); resources->acquire(resource::initializing); if (active) { - auto diff = model::diff::cluster_diff_ptr_t(); - auto state = model::device_state_t::dialing; - auto sha256 = peer_device_id.get_sha256(); - diff = new model::diff::peer::peer_state_t(*cluster, sha256, nullptr, state); - send(coordinator, std::move(diff)); + if (cluster) { + auto diff = model::diff::cluster_diff_ptr_t(); + auto state = model::device_state_t::dialing; + auto sha256 = peer_device_id.get_sha256(); + diff = new model::diff::peer::peer_state_t(*cluster, sha256, nullptr, state); + send(coordinator, std::move(diff)); + } initiate_active(); } else { initiate_passive(); @@ -93,8 +96,7 @@ void initiator_actor_t::initiate_passive() noexcept { void initiator_actor_t::on_start() noexcept { r::actor_base_t::on_start(); LOG_TRACE(log, "{}, on_start", identity); - auto &addr = supervisor->get_address(); - send(addr, std::move(transport), peer_device_id, remote_endpoint); + send(sink, std::move(transport), peer_device_id, remote_endpoint, std::move(custom)); success = true; do_shutdown(); } @@ -115,7 +117,7 @@ void initiator_actor_t::shutdown_start() noexcept { void initiator_actor_t::shutdown_finish() noexcept { LOG_TRACE(log, "{}, shutdown_finish", identity); - if (active && !success) { + if (active && !success && cluster) { auto diff = model::diff::cluster_diff_ptr_t(); auto state = model::device_state_t::offline; auto sha256 = peer_device_id.get_sha256(); diff --git a/src/net/initiator_actor.h b/src/net/initiator_actor.h index a1d2230f..015ac3ec 100644 --- a/src/net/initiator_actor.h +++ b/src/net/initiator_actor.h @@ -17,6 +17,8 @@ struct initiator_actor_config_t : public r::actor_config_t { const utils::key_pair_t *ssl_pair; std::optional sock; model::cluster_ptr_t cluster; + r::address_ptr_t sink; + r::message_ptr_t custom; }; template struct initiator_actor_config_builder_t : r::actor_config_builder_t { @@ -48,6 +50,16 @@ template struct initiator_actor_config_builder_t : r::actor_con parent_t::config.cluster = value; return std::move(*static_cast(this)); } + + builder_t &&sink(const r::address_ptr_t &value) &&noexcept { + parent_t::config.sink = value; + return std::move(*static_cast(this)); + } + + builder_t &&custom(r::message_ptr_t value) &&noexcept { + parent_t::config.custom = std::move(value); + return std::move(*static_cast(this)); + } }; struct initiator_actor_t : r::actor_base_t { @@ -60,6 +72,8 @@ struct initiator_actor_t : r::actor_base_t { void shutdown_start() noexcept override; void shutdown_finish() noexcept override; + template auto &access() noexcept; + private: using resolve_it_t = payload::address_response_t::resolve_results_t::iterator; @@ -79,6 +93,8 @@ struct initiator_actor_t : r::actor_base_t { const utils::key_pair_t &ssl_pair; std::optional sock; model::cluster_ptr_t cluster; + r::address_ptr_t sink; + r::message_ptr_t custom; transport::stream_sp_t transport; r::address_ptr_t resolver; @@ -97,6 +113,7 @@ struct peer_connected_t { transport::stream_sp_t transport; model::device_id_t peer_device_id; tcp::endpoint remote_endpoint; + r::message_ptr_t custom; }; } // namespace payload diff --git a/src/net/messages.h b/src/net/messages.h index b6fb2612..dbbe87f1 100644 --- a/src/net/messages.h +++ b/src/net/messages.h @@ -137,6 +137,16 @@ struct SYNCSPIRIT_API block_request_t { ~block_request_t(); }; +struct connect_response_t { + transport::stream_sp_t transport; +}; + +struct connect_request_t { + using response_t = connect_response_t; + model::device_id_t device_id; + utils::URI uri; +}; + } // end of namespace payload namespace message { @@ -164,6 +174,9 @@ using termination_signal_t = r::message_t; using block_request_t = r::request_traits_t::request::message_t; using block_response_t = r::request_traits_t::response::message_t; +using connect_request_t = r::request_traits_t::request::message_t; +using connect_response_t = r::request_traits_t::response::message_t; + } // end of namespace message } // namespace net diff --git a/src/net/names.cpp b/src/net/names.cpp index 7743a57d..69df0ce1 100644 --- a/src/net/names.cpp +++ b/src/net/names.cpp @@ -5,6 +5,7 @@ using namespace syncspirit::net; +const char *names::peer_supervisor = "net::peer_supervisor"; const char *names::coordinator = "net::coodinator"; const char *names::resolver = "net::resolver"; const char *names::http10 = "net::http10"; diff --git a/src/net/names.h b/src/net/names.h index 09b8f1fe..fb6fd8f5 100644 --- a/src/net/names.h +++ b/src/net/names.h @@ -9,6 +9,7 @@ namespace syncspirit { namespace net { struct SYNCSPIRIT_API names { + static const char *peer_supervisor; static const char *coordinator; static const char *resolver; static const char *http10; diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index 5fddb7f5..24746eb3 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -23,15 +23,21 @@ peer_supervisor_t::peer_supervisor_t(peer_supervisor_config_t &cfg) void peer_supervisor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { r::actor_base_t::configure(plugin); - plugin.with_casted([&](auto &p) { p.set_identity("peer_supervisor", false); }); + plugin.with_casted([&](auto &p) { + p.set_identity("peer_supervisor", false); + addr_unknown = p.create_address(); + }); plugin.with_casted([&](auto &p) { + p.register_name(names::peer_supervisor, get_address()); p.discover_name(names::coordinator, coordinator, true).link(false).callback([&](auto phase, auto &ee) { if (!ee && phase == r::plugin::registry_plugin_t::phase_t::linking) { auto p = get_plugin(r::plugin::starter_plugin_t::class_identity); auto plugin = static_cast(p); plugin->subscribe_actor(&peer_supervisor_t::on_model_update, coordinator); plugin->subscribe_actor(&peer_supervisor_t::on_contact_update, coordinator); - plugin->subscribe_actor(&peer_supervisor_t::on_ready); + plugin->subscribe_actor(&peer_supervisor_t::on_peer_ready); + plugin->subscribe_actor(&peer_supervisor_t::on_connect); + plugin->subscribe_actor(&peer_supervisor_t::on_connected, addr_unknown); } }); }); @@ -70,8 +76,8 @@ void peer_supervisor_t::on_contact_update(model::message::contact_update_t &msg) } } -void peer_supervisor_t::on_ready(message::peer_connected_t &msg) noexcept { - LOG_TRACE(log, "{}, on_ready", identity); +void peer_supervisor_t::on_peer_ready(message::peer_connected_t &msg) noexcept { + LOG_TRACE(log, "{}, on_peer_ready", identity); auto timeout = r::pt::milliseconds{bep_config.connect_timeout}; auto &p = msg.payload; create_actor() @@ -86,6 +92,27 @@ void peer_supervisor_t::on_ready(message::peer_connected_t &msg) noexcept { .finish(); } +void peer_supervisor_t::on_connected(message::peer_connected_t &msg) noexcept { + LOG_TRACE(log, "{}, on_connected", identity); + auto &p = msg.payload; + auto req = static_cast(p.custom.get()); + reply_to(*req, std::move(p.transport)); +} + +void peer_supervisor_t::on_connect(message::connect_request_t &msg) noexcept { + auto &p = msg.payload.request_payload; + auto connect_timeout = r::pt::milliseconds{bep_config.connect_timeout}; + create_actor() + .ssl_pair(&ssl_pair) + .peer_device_id(p.device_id) + .uris({p.uri}) + .custom(&msg) + .sink(addr_unknown) + .init_timeout(connect_timeout * 2) + .shutdown_timeout(connect_timeout) + .finish(); +} + auto peer_supervisor_t::operator()(const model::diff::peer::peer_state_t &diff) noexcept -> outcome::result { auto &peer_addr = diff.peer_addr; if (!diff.known && diff.state == model::device_state_t::online) { diff --git a/src/net/peer_supervisor.h b/src/net/peer_supervisor.h index 4f1cb114..0d7ff11f 100644 --- a/src/net/peer_supervisor.h +++ b/src/net/peer_supervisor.h @@ -73,9 +73,11 @@ struct SYNCSPIRIT_API peer_supervisor_t : public ra::supervisor_asio_t, void on_start() noexcept override; private: + void on_connect(message::connect_request_t &) noexcept; void on_model_update(model::message::model_update_t &) noexcept; void on_contact_update(model::message::contact_update_t &) noexcept; - void on_ready(message::peer_connected_t &) noexcept; + void on_peer_ready(message::peer_connected_t &) noexcept; + void on_connected(message::peer_connected_t &) noexcept; outcome::result operator()(const model::diff::peer::peer_state_t &) noexcept override; outcome::result operator()(const model::diff::modify::update_contact_t &) noexcept override; @@ -84,6 +86,7 @@ struct SYNCSPIRIT_API peer_supervisor_t : public ra::supervisor_asio_t, model::cluster_ptr_t cluster; utils::logger_t log; r::address_ptr_t coordinator; + r::address_ptr_t addr_unknown; std::string_view device_name; const utils::key_pair_t &ssl_pair; config::bep_config_t bep_config; diff --git a/src/net/relay_actor.cpp b/src/net/relay_actor.cpp new file mode 100644 index 00000000..c123266f --- /dev/null +++ b/src/net/relay_actor.cpp @@ -0,0 +1,398 @@ +#include "relay_actor.h" +#include "names.h" +#include "utils/error_code.h" +#include "utils/beast_support.h" +#include +#include "messages.h" +#include +#include + +using namespace syncspirit::net; + +static const constexpr size_t BUFF_SZ = 1500; +template inline constexpr bool always_false_v = false; + +namespace { +namespace resource { +r::plugin::resource_id_t init = 0; +r::plugin::resource_id_t http = 1; +r::plugin::resource_id_t io_read = 2; +r::plugin::resource_id_t io_write = 3; +} // namespace resource +} // namespace + +relay_actor_t::relay_actor_t(config_t &config) noexcept + : r::actor_base_t(config), cluster{std::move(config.cluster)}, config{config.config} { + log = utils::get_logger("net.relay"); + http_rx_buff = std::make_shared(); +} + +void relay_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { + r::actor_base_t::configure(plugin); + plugin.with_casted([&](auto &p) { p.set_identity("relay", false); }); + plugin.with_casted([&](auto &p) { + p.discover_name(names::http11_relay, http_client, true).link(true); + p.discover_name(names::coordinator, coordinator, false).link(false); + p.discover_name(names::peer_supervisor, peer_supervisor, true).link(false); + }); + plugin.with_casted([&](auto &p) { + p.subscribe_actor(&relay_actor_t::on_list); + p.subscribe_actor(&relay_actor_t::on_connect); + resources->acquire(resource::init); + request_relay_list(); + }); +} + +void relay_actor_t::shutdown_start() noexcept { + LOG_TRACE(log, "{}, shutdown_start", identity); + r::actor_base_t::shutdown_start(); + + if (resources->has(resource::init)) { + resources->release(resource::init); + } + if (resources->has(resource::http)) { + send(http_client, *http_request, get_address()); + } + if (resources->has(resource::io_read) || resources->has(resource::io_write)) { + master->cancel(); + } +} + +void relay_actor_t::on_start() noexcept { + LOG_TRACE(log, "{}, on_start", identity); + r::actor_base_t::on_start(); + connect_to_relay(); +} + +void relay_actor_t::connect_to_relay() noexcept { + size_t attempts = 0; + while (++attempts < 10) { + relay_index = rand() % relays.size(); + auto relay = relays[relay_index]; + if (!relay_index) { + continue; + } + auto &l = relay->location; + auto &u = relay->uri; + LOG_INFO(log, "{}, chosen relay({}) {}:{}, city: {}, country: {}, continent: {}", identity, relay_index, + relay->uri.host, relay->uri.port, l.city, l.country, l.continent); + + auto uri = utils::parse(fmt::format("tcp://{}:{}", u.host, u.port)).value(); + request(peer_supervisor, relay->device_id, std::move(uri)).send(init_timeout); + return; + } + if (attempts >= 10) { + do_shutdown(); + } +} + +void relay_actor_t::request_relay_list() noexcept { + auto timeout = init_timeout * 9 / 10; + auto url_opt = utils::parse(config.discovery_url); + if (!url_opt) { + LOG_WARN(log, "{}, malformed discovery url '{}'", identity, config.discovery_url); + auto ec = utils::make_error_code(utils::error_code_t::malformed_url); + return do_shutdown(make_error(ec)); + } + + auto uri = url_opt.value(); + assert(uri.proto == "https"); + http::request req; + req.method(http::verb::get); + req.version(10); + req.target(uri.relative()); + req.set(http::field::host, uri.host); + req.set(http::field::connection, "close"); + + fmt::memory_buffer tx_buff; + auto r = utils::serialize(req, tx_buff); + if (!r) { + auto &ec = r.assume_error(); + LOG_WARN(log, "{}, cannot serialize request: {}'", identity, r.assume_error().message()); + return do_shutdown(make_error(ec)); + } + resources->acquire(resource::http); + transport::ssl_junction_t ssl{ + model::device_id_t{}, + nullptr, + true, + }; + http_request = request(http_client, uri, std::move(tx_buff), http_rx_buff, + config.rx_buff_size, std::move(ssl)) + .send(timeout); +} + +void relay_actor_t::on_list(message::http_response_t &msg) noexcept { + LOG_TRACE(log, "{}, on_list", identity); + resources->release(resource::http); + + auto &ee = msg.payload.ee; + if (ee) { + LOG_WARN(log, "{}, get public relays failed: {}", identity, ee->message()); + auto inner = utils::make_error_code(utils::error_code_t::cannot_get_public_relays); + return do_shutdown(make_error(inner, ee)); + } + if (state > r::state_t::OPERATIONAL) { + return; + } + + auto &body = msg.payload.res->response.body(); + auto result = proto::relay::parse_endpoint(body); + if (!result) { + auto &ec = result.assume_error(); + LOG_WARN(log, "{}, cannot parse relays: {}", identity, ec.message()); + return do_shutdown(make_error(ec)); + } + auto &list = result.assume_value(); + if (list.empty()) { + LOG_WARN(log, "{}, empty list of public relays", identity); + auto ec = utils::make_error_code(utils::error_code_t::cannot_get_public_relays); + return do_shutdown(make_error(ec)); + } + relays = std::move(list); + http_rx_buff.reset(); + resources->release(resource::init); +} + +void relay_actor_t::read_master() noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + transport::io_fn_t on_read = [&](auto arg) { this->on_read(arg); }; + transport::error_fn_t on_error = [&](auto arg) { this->on_io_error(arg, resource::io_read); }; + resources->acquire(resource::io_read); + auto buff = asio::buffer(rx_buff.data() + rx_idx, rx_buff.size() - rx_idx); + LOG_TRACE(log, "{}, read_more", identity); + master->async_recv(buff, on_read, on_error); +} + +void relay_actor_t::push_master(std::string data) noexcept { + tx_queue.emplace_back(tx_item_t(new std::string(std::move(data)))); + if (!resources->has(resource::io_write)) { + write_master(); + } +} + +void relay_actor_t::write_master() noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + auto &item = tx_queue.front(); + transport::io_fn_t on_write = [&](auto sz) { + LOG_TRACE(log, "{}, write {} bytes", identity, sz); + resources->release(resource::io_write); + tx_queue.pop_front(); + if (tx_timer) { + cancel_timer(*tx_timer); + } + if (!tx_queue.empty()) { + write_master(); + } + }; + transport::error_fn_t on_error = [&](auto arg) { this->on_io_error(arg, resource::io_write); }; + auto buff = asio::buffer(*item); + master->async_send(buff, on_write, on_error); + resources->acquire(resource::io_write); + respawn_tx_timer(); +} + +void relay_actor_t::on_connect(message::connect_response_t &res) noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + LOG_TRACE(log, "{}, on_connect", identity); + auto &ee = res.payload.ee; + auto &r = relays[relay_index]; + if (ee) { + LOG_TRACE(log, "{}, failed to connect to relay {}: {}", identity, r->device_id.get_short(), ee->message()); + relays[relay_index].reset(); + return connect_to_relay(); + } + LOG_DEBUG(log, "{}, connected to relay {}", identity, r->device_id.get_short()); + master = std::move(res.payload.res.transport); + rx_buff.resize(BUFF_SZ); + read_master(); + rx_state |= rx_state_t::response; + respawn_rx_timer(); + + auto tx = std::string{}; + proto::relay::serialize(proto::relay::join_relay_request_t{}, tx); + push_master(tx); +} + +void relay_actor_t::on_io_error(const sys::error_code &ec, rotor::plugin::resource_id_t resource) noexcept { + LOG_TRACE(log, "{}, on_io_error: {}", identity, ec.message()); + resources->release(resource); + if (ec != asio::error::operation_aborted) { + LOG_WARN(log, "{}, on_io_error: {}", identity, ec.message()); + if (state < r::state_t::SHUTTING_DOWN) { + do_shutdown(make_error(ec)); + } + } +} + +void relay_actor_t::on_read(std::size_t bytes) noexcept { + enum process_t { stop = 1 << 0, more = 1 << 1, incomplete = 1 << 2 }; + + LOG_TRACE(log, "{}, on_read: {} bytes, data: {}", identity, bytes, + spdlog::to_hex(rx_buff.begin(), rx_buff.begin() + bytes)); + resources->release(resource::io_read); + rx_idx += bytes; + size_t from = 0; + auto process_op = process_t::more; + while (process_op == process_t::more && from < rx_idx) { + auto start = rx_buff.data() + from; + auto sz = rx_idx - from; + auto r = proto::relay::parse({start, sz}); + process_op = std::visit( + [&](auto &it) -> process_t { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return process_t::incomplete; + } else if constexpr (std::is_same_v) { + LOG_ERROR(log, "{}, protocol error (master)", identity); + auto ec = utils::make_error_code(utils::error_code_t::protocol_error); + do_shutdown(make_error(ec)); + return process_t::stop; + } else if constexpr (std::is_same_v) { + from += it.length; + auto ok = on(it.message); + return ok ? process_t::more : process_t::stop; + } else { + static_assert(always_false_v, "non-exhaustive visitor!"); + } + }, + r); + } + rx_idx -= from; + if (process_op != process_t::stop) { + read_master(); + } +} + +bool relay_actor_t::on(proto::relay::message_t &msg) noexcept { + return std::visit( + [&](auto &it) -> bool { + using T = std::decay_t; + bool err = false; + bool cancel_rx_timer = false; + if constexpr (std::is_same_v) { + if (rx_state & rx_state_t::pong) { + rx_state = ~rx_state & rx_state_t::pong; + cancel_rx_timer = true; + } else { + err = true; + } + } else if constexpr (std::is_same_v) { + auto tx = std::string{}; + proto::relay::serialize(proto::relay::pong_t{}, tx); + push_master(tx); + } else if constexpr (std::is_same_v) { + if (rx_state & rx_state_t::response) { + cancel_rx_timer = true; + rx_state = ~rx_state & rx_state_t::response; + err = !on(it); + } else { + err = true; + } + } else if constexpr (std::is_same_v) { + if (rx_state & rx_state_t::invitation) { + return on(it); + } else { + err = true; + } + } else { + err = true; + } + if (err) { + LOG_ERROR(log, "{}, protocol error (master, unexpected message)", identity); + auto ec = utils::make_error_code(utils::error_code_t::protocol_error); + do_shutdown(make_error(ec)); + } + if (cancel_rx_timer && rx_timer) { + cancel_timer(*rx_timer); + rx_timer.reset(); + } + return !err; + }, + msg); +} + +bool relay_actor_t::on(proto::relay::response_t &res) noexcept { + LOG_DEBUG(log, "{}, on response code = {}", identity, res.code); + if (res.code) { + LOG_WARN(log, "{}, response error, details = {}", identity, res.details); + auto ec = utils::make_error_code(utils::error_code_t::relay_failure); + do_shutdown(make_error(ec)); + return false; + } + respawn_ping_timer(); + rx_state |= rx_state_t::invitation; + return true; +} + +bool relay_actor_t::on(proto::relay::session_invitation_t &) noexcept { std::abort(); } + +void relay_actor_t::respawn_ping_timer() noexcept { + if (ping_timer) { + cancel_timer(*ping_timer); + } + if (state > r::state_t::OPERATIONAL) { + return; + } + ping_timer = start_timer(relays[relay_index]->ping_interval, *this, &relay_actor_t::on_ping_timer); +} + +void relay_actor_t::respawn_tx_timer() noexcept { + if (tx_timer) { + cancel_timer(*tx_timer); + } + if (state > r::state_t::OPERATIONAL) { + return; + } + tx_timer = start_timer(relays[relay_index]->ping_interval, *this, &relay_actor_t::on_tx_timer); +} + +void relay_actor_t::respawn_rx_timer() noexcept { + if (rx_timer) { + cancel_timer(*rx_timer); + } + if (state > r::state_t::OPERATIONAL) { + return; + } + rx_timer = start_timer(relays[relay_index]->ping_interval, *this, &relay_actor_t::on_rx_timer); +} + +void relay_actor_t::on_ping_timer(r::request_id_t, bool cancelled) noexcept { + ping_timer.reset(); + if (!cancelled) { + send_ping(); + } +} + +void relay_actor_t::on_tx_timer(r::request_id_t, bool cancelled) noexcept { + tx_timer.reset(); + if (!cancelled) { + auto ec = utils::make_error_code(utils::error_code_t::tx_timeout); + do_shutdown(make_error(ec)); + } +} + +void relay_actor_t::on_rx_timer(r::request_id_t, bool cancelled) noexcept { + rx_timer.reset(); + if (!cancelled) { + auto ec = utils::make_error_code(utils::error_code_t::rx_timeout); + do_shutdown(make_error(ec)); + } +} + +void relay_actor_t::send_ping() noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + + auto buff = std::string{}; + proto::relay::serialize(proto::relay::ping_t{}, buff); + push_master(std::move(buff)); + rx_state |= rx_state_t::pong; +} diff --git a/src/net/relay_actor.h b/src/net/relay_actor.h new file mode 100644 index 00000000..c0548344 --- /dev/null +++ b/src/net/relay_actor.h @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2022 Ivan Baidakou + +#pragma once + +#include "messages.h" +#include "utils/log.h" +#include "config/relay.h" +#include "proto/relay_support.h" +#include "transport/stream.h" +#include +#include +#include +#include +#include +#include + +namespace syncspirit::net { + +struct relay_actor_config_t : r::actor_config_t { + model::cluster_ptr_t cluster; + config::relay_config_t config; +}; + +template struct relay_actor_config_builder_t : r::actor_config_builder_t { + using builder_t = typename Actor::template config_builder_t; + using parent_t = r::actor_config_builder_t; + using parent_t::parent_t; + + builder_t &&cluster(const model::cluster_ptr_t &value) &&noexcept { + parent_t::config.cluster = value; + return std::move(*static_cast(this)); + } + + builder_t &&relay_config(const config::relay_config_t &value) &&noexcept { + parent_t::config.config = value; + return std::move(*static_cast(this)); + } +}; + +struct SYNCSPIRIT_API relay_actor_t : public r::actor_base_t { + using config_t = relay_actor_config_t; + template using config_builder_t = relay_actor_config_builder_t; + + relay_actor_t(config_t &config) noexcept; + + void configure(r::plugin::plugin_base_t &plugin) noexcept override; + void on_start() noexcept override; + void shutdown_start() noexcept override; + + private: + using http_rx_buff_t = payload::http_request_t::rx_buff_ptr_t; + using request_option_t = std::optional; + using relays_t = proto::relay::relay_infos_t; + using tx_item_t = boost::local_shared_ptr; + using tx_queue_t = std::deque; + + enum rx_state_t : std::uint32_t { + response = 1 << 0, + ping = 1 << 1, + pong = 1 << 2, + invitation = 1 << 3, + }; + + void request_relay_list() noexcept; + void connect_to_relay() noexcept; + void push_master(std::string data) noexcept; + void write_master() noexcept; + void read_master() noexcept; + void respawn_ping_timer() noexcept; + void respawn_tx_timer() noexcept; + void respawn_rx_timer() noexcept; + void send_ping() noexcept; + + void on_list(message::http_response_t &res) noexcept; + void on_connect(message::connect_response_t &res) noexcept; + void on_write(std::size_t sz) noexcept; + void on_read(std::size_t bytes) noexcept; + void on_io_error(const sys::error_code &ec, r::plugin::resource_id_t resource) noexcept; + bool on(proto::relay::message_t &) noexcept; + bool on(proto::relay::response_t &) noexcept; + bool on(proto::relay::session_invitation_t &) noexcept; + void on_ping_timer(r::request_id_t, bool cancelled) noexcept; + void on_tx_timer(r::request_id_t, bool cancelled) noexcept; + void on_rx_timer(r::request_id_t, bool cancelled) noexcept; + + model::cluster_ptr_t cluster; + config::relay_config_t config; + utils::logger_t log; + + r::address_ptr_t http_client; + r::address_ptr_t coordinator; + r::address_ptr_t peer_supervisor; + http_rx_buff_t http_rx_buff; + request_option_t http_request; + relays_t relays; + int relay_index = -1; + transport::stream_sp_t master; + fmt::memory_buffer rx_buff; + std::size_t rx_idx = 0; + std::uint32_t rx_state = 0; + tx_queue_t tx_queue; + std::optional ping_timer; + std::optional tx_timer; + std::optional rx_timer; +}; + +} // namespace syncspirit::net diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp index 1960081c..37d52440 100644 --- a/src/proto/relay_support.cpp +++ b/src/proto/relay_support.cpp @@ -35,6 +35,46 @@ static constexpr auto header_sz = sizeof(header_t); static constexpr auto max_packet_sz = size_t{1400}; static constexpr uint32_t magic = 0x9E79BC40; +static pt::time_duration parse_interval(const std::string_view in) noexcept { + auto ptr = in.data(); + auto end = in.data() + in.size(); + auto s = ptr; + auto e = ptr; + while (ptr < end && *ptr != 'm') { + ++ptr; + } + if (*ptr == 'm') { + e = ptr++; + }; + int mins = 0; + while (s < e) { + mins *= 10; + mins += *s - '0'; + ++s; + } + + s = e = ptr; + while (ptr < end && *ptr != 's') { + ++ptr; + } + if (*ptr == 's') { + e = ptr; + }; + + int secs = 0; + while (s < e) { + secs *= 10; + secs += *s - '0'; + ++s; + } + + if (mins == 0 && secs == 0) { + mins = 1; + } + + return pt::minutes{mins} + pt::seconds{secs}; +} + static void serialize_header(char *ptr, type_t type, size_t payload_sz) noexcept { auto h = header_t{be::native_to_big(magic), be::native_to_big(static_cast(type)), be::native_to_big(static_cast(payload_sz))}; @@ -147,7 +187,7 @@ static parse_result_t parse_join_session_request(std::string_view data) noexcept } auto ptr = data.data(); auto sz = be::big_to_native(*reinterpret_cast(ptr)); - if (sz + sizeof(uint32_t) != data.size()) { + if (sz + sizeof(uint32_t) > data.size()) { return protocol_error_t{}; } auto tail = data.substr(sizeof(uint32_t)); @@ -162,10 +202,10 @@ static parse_result_t parse_response(std::string_view data) noexcept { auto code = be::big_to_native(*reinterpret_cast(ptr)); ptr += sizeof(uint32_t); auto sz = be::big_to_native(*reinterpret_cast(ptr)); - if (sz + sizeof(uint32_t) * 2 != data.size()) { + if (sz + sizeof(uint32_t) * 2 > data.size()) { return protocol_error_t{}; } - auto tail = data.substr(sizeof(uint32_t) * 2); + auto tail = data.substr(sizeof(uint32_t) * 2, sz); return wrapped_message_t{header_sz + data.size(), response_t{code, std::string(tail)}}; } @@ -311,6 +351,26 @@ outcome::result parse_endpoint(std::string_view buff) noexcept { if (uri.proto != "relay") { continue; } + auto device_id_str = std::string{}; + auto ping_interval_str = std::string{}; + auto q = uri.decompose_query(); + for (auto &pair : q) { + if (pair.first == "id") { + device_id_str = std::move(pair.second); + } else if (pair.first == "pingInterval") { + ping_interval_str = std::move(pair.second); + } + } + if (device_id_str.empty()) { + continue; + } + auto device_id = model::device_id_t::from_string(device_id_str); + if (!device_id) { + continue; + } + + auto ping_interval = parse_interval(ping_interval_str); + auto &location = it["location"]; if (!location.is_object()) { continue; @@ -335,13 +395,15 @@ outcome::result parse_endpoint(std::string_view buff) noexcept { if (!continent.is_string()) { continue; } - auto relay = relay_info_ptr_t{new relay_info_t{std::move(uri), location_t{ - latitude.get(), - longitude.get(), - city.get(), - country.get(), - continent.get(), - }}}; + auto relay = relay_info_ptr_t{new relay_info_t{std::move(uri), device_id.value(), + location_t{ + latitude.get(), + longitude.get(), + city.get(), + country.get(), + continent.get(), + }, + ping_interval}}; r.emplace_back(std::move(relay)); } return std::move(r); diff --git a/src/proto/relay_support.h b/src/proto/relay_support.h index aa1fa607..7736ce48 100644 --- a/src/proto/relay_support.h +++ b/src/proto/relay_support.h @@ -8,13 +8,16 @@ #include #include #include +#include #include "utils/uri.h" #include "syncspirit-export.h" #include +#include namespace syncspirit::proto::relay { namespace outcome = boost::outcome_v2; +namespace pt = boost::posix_time; struct ping_t {}; @@ -68,10 +71,13 @@ struct location_t { }; struct relay_info_t : model::arc_base_t { - inline relay_info_t(utils::URI uri_, location_t location_) noexcept - : uri(std::move(uri_)), location{std::move(location_)} {} + inline relay_info_t(utils::URI uri_, const model::device_id_t &device_id_, location_t location_, + const pt::time_duration &ping_interval_) noexcept + : uri(std::move(uri_)), device_id{device_id_}, location{std::move(location_)}, ping_interval{ping_interval_} {} utils::URI uri; + model::device_id_t device_id; location_t location; + pt::time_duration ping_interval; }; using relay_info_ptr_t = model::intrusive_ptr_t; diff --git a/src/transport/base.h b/src/transport/base.h index 431c0b4a..07a5a74e 100644 --- a/src/transport/base.h +++ b/src/transport/base.h @@ -49,4 +49,8 @@ struct transport_config_t { std::optional sock; }; +struct stream_base_t; + +using stream_sp_t = model::intrusive_ptr_t; + } // namespace syncspirit::transport diff --git a/src/transport/stream.h b/src/transport/stream.h index 23f53e0a..37a5d926 100644 --- a/src/transport/stream.h +++ b/src/transport/stream.h @@ -20,8 +20,6 @@ struct stream_base_t : model::arc_base_t, stream_interface_t { virtual ~stream_base_t(); }; -using stream_sp_t = model::intrusive_ptr_t; - stream_sp_t initiate_tls_active(ra::supervisor_asio_t &supervisor, const utils::key_pair_t &my_keys, const model::device_id_t &expected_peer, const utils::URI &uri, bool sni = false, std::string_view alpn = "") noexcept; diff --git a/src/utils/error_code.cpp b/src/utils/error_code.cpp index a9973b0c..da29a2e2 100644 --- a/src/utils/error_code.cpp +++ b/src/utils/error_code.cpp @@ -74,6 +74,9 @@ std::string error_code_category::message(int c) const { case error_code_t::rx_timeout: r = "rx timeout"; break; + case error_code_t::tx_timeout: + r = "tx timeout"; + break; case error_code_t::announce_failed: r = "announce failed"; break; @@ -104,6 +107,12 @@ std::string error_code_category::message(int c) const { case error_code_t::cannot_get_public_relays: r = "cannot get public relays"; break; + case error_code_t::protocol_error: + r = "protocol error"; + break; + case error_code_t::relay_failure: + r = "relay failure"; + break; default: r = "unknown"; } diff --git a/src/utils/error_code.h b/src/utils/error_code.h index 36e2c0fa..6005db4f 100644 --- a/src/utils/error_code.h +++ b/src/utils/error_code.h @@ -62,6 +62,7 @@ enum class error_code_t { unparseable_control_url, external_ip_failed, rx_timeout, + tx_timeout, fs_error, scan_aborted, already_shared, @@ -69,6 +70,8 @@ enum class error_code_t { misconfigured_default_logger, already_connected, cannot_get_public_relays, + protocol_error, + relay_failure, }; enum class bep_error_code_t { diff --git a/src/utils/uri.cpp b/src/utils/uri.cpp index 560c7112..83ac84a6 100644 --- a/src/utils/uri.cpp +++ b/src/utils/uri.cpp @@ -77,4 +77,22 @@ void URI::set_query(const std::string &value) noexcept { std::string URI::relative() const noexcept { return path + query; } +auto URI::decompose_query() const noexcept -> StringPairs { + StringPairs r; + UriQueryListA *queryList; + int itemCount; + auto q = query.data(); + if (uriDissectQueryMallocA(&queryList, &itemCount, q, q + query.size()) != URI_SUCCESS) { + return {}; + } + using guard_t = std::unique_ptr>; + guard_t guard(queryList, [](auto ptr) { uriFreeQueryListA(ptr); }); + auto p = queryList; + while (p) { + r.emplace_back(StringPair(p->key, p->value)); + p = p->next; + } + return r; +} + }; // namespace syncspirit::utils diff --git a/src/utils/uri.h b/src/utils/uri.h index dad88f8b..3db01ebe 100644 --- a/src/utils/uri.h +++ b/src/utils/uri.h @@ -8,10 +8,14 @@ #include #include #include +#include namespace syncspirit::utils { struct SYNCSPIRIT_API URI { + using StringPair = std::pair; + using StringPairs = std::vector; + std::string full; std::string host; std::uint16_t port; @@ -25,6 +29,8 @@ struct SYNCSPIRIT_API URI { void set_query(const std::string &value) noexcept; std::string relative() const noexcept; + StringPairs decompose_query() const noexcept; + inline bool operator==(const URI &other) const noexcept { return full == other.full; } inline operator bool() const noexcept { return !full.empty(); } diff --git a/tests/016-relay-support.cpp b/tests/016-relay-support.cpp index 9729891f..dfbe4572 100644 --- a/tests/016-relay-support.cpp +++ b/tests/016-relay-support.cpp @@ -98,6 +98,22 @@ TEST_CASE("relay proto", "[relay]") { CHECK(target->address == source.address); CHECK(target->server_socket == source.server_socket); } + + SECTION("response piece") { + const unsigned char data[] = {0x9e, 0x79, 0xbc, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x00}; + auto ptr = reinterpret_cast(data); + auto r = parse({ptr, sizeof(data)}); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == 28); + auto target = std::get_if(&msg->message); + REQUIRE(target); + CHECK(target->code == 0); + auto details = std::string_view("success"); + CHECK(target->details == details); + } } SECTION("incompplete") { @@ -140,7 +156,7 @@ TEST_CASE("endpoing parsing", "[relay]") { { "relays": [ { - "url": "relay://130.61.176.206:22067/?id=OAKAXEX-7HE764M-5EWVN7U-SZCQU4D-ZPXF2TY-SNTL2LL-Y5RVGVM-U7WBRA3&pingInterval=1m0s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=ina", + "url": "relay://130.61.176.206:22067/?id=OAKAXEX-7HE764M-5EWVN7U-SZCQU4D-ZPXF2TY-SNTL2LL-Y5RVGVM-U7WBRA3&pingInterval=1m30s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=ina", "location": { "latitude": 50.1049, "longitude": 8.6295, @@ -191,12 +207,14 @@ TEST_CASE("endpoing parsing", "[relay]") { REQUIRE(r.value().size() == 1); auto relay = r.value()[0]; CHECK(relay->uri.full == "relay://130.61.176.206:22067/" - "?id=OAKAXEX-7HE764M-5EWVN7U-SZCQU4D-ZPXF2TY-SNTL2LL-Y5RVGVM-U7WBRA3&pingInterval=1m0s&" + "?id=OAKAXEX-7HE764M-5EWVN7U-SZCQU4D-ZPXF2TY-SNTL2LL-Y5RVGVM-U7WBRA3&pingInterval=1m30s&" "networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=ina"); + CHECK(relay->device_id.get_short() == "OAKAXEX"); auto &l = relay->location; CHECK(abs(l.latitude - 50.1049) < 0.0001); CHECK(abs(l.longitude - 8.6295) < 0.0001); CHECK(l.city == "Frankfurt am Main"); CHECK(l.country == "DE"); CHECK(l.continent == "EU"); + CHECK(relay->ping_interval == pt::seconds{90}); } diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp index 20a6cfcc..aa7dd2de 100644 --- a/tests/077-initiator.cpp +++ b/tests/077-initiator.cpp @@ -175,7 +175,8 @@ struct fixture_t { .timeout(timeout) .peer_device_id(peer_device->device_id()) .uris({peer_uri}) - .cluster(cluster) + .cluster(use_model ? cluster : nullptr) + .sink(sup->get_address()) .ssl_pair(&my_keys) .escalate_failure() .finish(); @@ -187,6 +188,7 @@ struct fixture_t { .sock(std::move(peer_sock)) .ssl_pair(&my_keys) .cluster(cluster) + .sink(sup->get_address()) .escalate_failure() .finish(); } @@ -208,7 +210,8 @@ struct fixture_t { transport::stream_sp_t peer_trans; ready_ptr_t connected_message; diff_msgs_t diff_msgs; - ; + bool use_model = true; + bool valid_handshake = false; }; @@ -300,6 +303,24 @@ void test_connection_refused() { F().run(); } +void test_connection_refused_no_model() { + struct F : fixture_t { + F() { use_model = false; } + + std::string get_uri(const asio::ip::tcp::endpoint &) noexcept override { + return fmt::format("tcp://{}:0", host); + } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + } + }; + F().run(); +} + void test_resolve_failure() { struct F : fixture_t { @@ -335,6 +356,26 @@ void test_success() { F().run(); } +void test_success_no_model() { + struct F : fixture_t { + F() { use_model = false; } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::OPERATIONAL); + CHECK(connected_message); + CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); + CHECK(valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + REQUIRE(diff_msgs.size() == 0); + } + }; + F().run(); +} + void test_passive_success() { struct F : fixture_t { @@ -422,8 +463,10 @@ REGISTER_TEST_CASE(test_connect_timeout, "test_connect_timeout", "[initiator]"); REGISTER_TEST_CASE(test_handshake_timeout, "test_handshake_timeout", "[initiator]"); REGISTER_TEST_CASE(test_handshake_garbage, "test_handshake_garbage", "[initiator]"); REGISTER_TEST_CASE(test_connection_refused, "test_connection_refused", "[initiator]"); +REGISTER_TEST_CASE(test_connection_refused_no_model, "test_connection_refused_no_model", "[initiator]"); REGISTER_TEST_CASE(test_resolve_failure, "test_resolve_failure", "[initiator]"); REGISTER_TEST_CASE(test_success, "test_success", "[initiator]"); +REGISTER_TEST_CASE(test_success_no_model, "test_success_no_model", "[initiator]"); REGISTER_TEST_CASE(test_passive_success, "test_passive_success", "[initiator]"); REGISTER_TEST_CASE(test_passive_garbage, "test_passive_garbage", "[initiator]"); REGISTER_TEST_CASE(test_passive_timeout, "test_passive_timeout", "[initiator]"); From 56d5af49b732f3bd1fad790dad56415900540bbc Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sat, 7 May 2022 22:36:32 +0300 Subject: [PATCH 14/31] relay_actor, more tests and fixes --- src/model/diff/modify/update_contact.cpp | 2 +- src/model/diff/modify/update_contact.h | 2 +- src/net/relay_actor.cpp | 29 +- src/transport/impl.hpp | 13 +- src/transport/stream.h | 2 +- tests/078-relay.cpp | 320 +++++++++++++++++++++++ tests/CMakeLists.txt | 10 + 7 files changed, 371 insertions(+), 7 deletions(-) create mode 100644 tests/078-relay.cpp diff --git a/src/model/diff/modify/update_contact.cpp b/src/model/diff/modify/update_contact.cpp index d35f3d38..7c8831e7 100644 --- a/src/model/diff/modify/update_contact.cpp +++ b/src/model/diff/modify/update_contact.cpp @@ -8,7 +8,7 @@ using namespace syncspirit::model::diff::modify; -update_contact_t::update_contact_t(const model::cluster_t &cluster, const model::device_id_t device_, +update_contact_t::update_contact_t(const model::cluster_t &cluster, const model::device_id_t& device_, const utils::uri_container_t &uris_) noexcept : device{device_}, uris{uris_} { auto &devices = cluster.get_devices(); diff --git a/src/model/diff/modify/update_contact.h b/src/model/diff/modify/update_contact.h index 88cc0750..e5841c6a 100644 --- a/src/model/diff/modify/update_contact.h +++ b/src/model/diff/modify/update_contact.h @@ -11,7 +11,7 @@ namespace syncspirit::model::diff::modify { struct SYNCSPIRIT_API update_contact_t final : contact_diff_t { using ip_addresses_t = std::vector; - update_contact_t(const model::cluster_t &cluster, const model::device_id_t device, + update_contact_t(const model::cluster_t &cluster, const model::device_id_t& device, const utils::uri_container_t &uris) noexcept; update_contact_t(const model::cluster_t &cluster, const ip_addresses_t &addresses) noexcept; diff --git a/src/net/relay_actor.cpp b/src/net/relay_actor.cpp index c123266f..01a0aaec 100644 --- a/src/net/relay_actor.cpp +++ b/src/net/relay_actor.cpp @@ -2,6 +2,8 @@ #include "names.h" #include "utils/error_code.h" #include "utils/beast_support.h" +#include "model/messages.h" +#include "model/diff/modify/update_contact.h" #include #include "messages.h" #include @@ -56,6 +58,19 @@ void relay_actor_t::shutdown_start() noexcept { if (resources->has(resource::io_read) || resources->has(resource::io_write)) { master->cancel(); } + if (rx_state) { + auto self = cluster->get_device(); + utils::uri_container_t uris; + for(auto& uri: self->get_uris()) { + if (uri.proto != "relay") { + uris.emplace_back(uri); + } + } + using namespace model::diff; + auto diff = model::diff::contact_diff_ptr_t{}; + diff = new modify::update_contact_t(*cluster, self->device_id(), uris); + send(coordinator, std::move(diff), this); + } } void relay_actor_t::on_start() noexcept { @@ -69,7 +84,7 @@ void relay_actor_t::connect_to_relay() noexcept { while (++attempts < 10) { relay_index = rand() % relays.size(); auto relay = relays[relay_index]; - if (!relay_index) { + if (!relay) { continue; } auto &l = relay->location; @@ -82,7 +97,8 @@ void relay_actor_t::connect_to_relay() noexcept { return; } if (attempts >= 10) { - do_shutdown(); + auto ec = utils::make_error_code(utils::error_code_t::relay_failure); + do_shutdown(make_error(ec)); } } @@ -162,7 +178,7 @@ void relay_actor_t::read_master() noexcept { transport::error_fn_t on_error = [&](auto arg) { this->on_io_error(arg, resource::io_read); }; resources->acquire(resource::io_read); auto buff = asio::buffer(rx_buff.data() + rx_idx, rx_buff.size() - rx_idx); - LOG_TRACE(log, "{}, read_more", identity); + LOG_TRACE(log, "{}, read_master, sz = {}", identity, buff.size()); master->async_recv(buff, on_read, on_error); } @@ -328,6 +344,13 @@ bool relay_actor_t::on(proto::relay::response_t &res) noexcept { } respawn_ping_timer(); rx_state |= rx_state_t::invitation; + auto self = cluster->get_device(); + auto uris = self->get_uris(); + uris.emplace_back(relays[relay_index]->uri); + using namespace model::diff; + auto diff = model::diff::contact_diff_ptr_t{}; + diff = new modify::update_contact_t(*cluster, self->device_id(), uris); + send(coordinator, std::move(diff), this); return true; } diff --git a/src/transport/impl.hpp b/src/transport/impl.hpp index 00f3f766..14028424 100644 --- a/src/transport/impl.hpp +++ b/src/transport/impl.hpp @@ -66,8 +66,19 @@ template <> struct base_impl_t { strand_t &strand; tcp_socket_t sock; bool cancelling = false; + + static tcp_socket_t mk_sock(transport_config_t &config, strand_t &strand) noexcept { + if (config.sock) { + tcp::socket sock(std::move(config.sock.value())); + return {std::move(sock)}; + } else { + tcp::socket sock(strand.context()); + return {std::move(sock)}; + } + } + base_impl_t(transport_config_t &config) noexcept - : supervisor{config.supervisor}, strand{supervisor.get_strand()}, sock(strand.context()) {} + : supervisor{config.supervisor}, strand{supervisor.get_strand()}, sock(mk_sock(config, strand)) {} tcp_socket_t &get_physical_layer() noexcept { return sock; } }; diff --git a/src/transport/stream.h b/src/transport/stream.h index 37a5d926..c53b9a3f 100644 --- a/src/transport/stream.h +++ b/src/transport/stream.h @@ -25,6 +25,6 @@ stream_sp_t initiate_tls_active(ra::supervisor_asio_t &supervisor, const utils:: std::string_view alpn = "") noexcept; stream_sp_t initiate_tls_passive(ra::supervisor_asio_t &supervisor, const utils::key_pair_t &my_keys, tcp::socket sock) noexcept; -// stream_sp_t initiate_stream(transport_config_t &config) noexcept; +stream_sp_t initiate_stream(transport_config_t &config) noexcept; } // namespace syncspirit::transport diff --git a/tests/078-relay.cpp b/tests/078-relay.cpp new file mode 100644 index 00000000..9ff5eaba --- /dev/null +++ b/tests/078-relay.cpp @@ -0,0 +1,320 @@ +#include "catch.hpp" +#include "test-utils.h" +#include "access.h" + +#include "utils/tls.h" +#include "model/cluster.h" +#include "model/messages.h" +#include "net/names.h" +#include "net/messages.h" +#include "net/relay_actor.h" +#include "transport/stream.h" +#include +#include + +using namespace syncspirit; +using namespace syncspirit::test; +using namespace syncspirit::model; +using namespace syncspirit::net; + +namespace asio = boost::asio; +namespace sys = boost::system; +namespace r = rotor; +namespace ra = r::asio; + +using configure_callback_t = std::function; + +auto timeout = r::pt::time_duration{r::pt::millisec{1500}}; +auto host = "127.0.0.1"; + +struct supervisor_t : ra::supervisor_asio_t { + using parent_t = ra::supervisor_asio_t; + using parent_t::parent_t; + + void configure(r::plugin::plugin_base_t &plugin) noexcept override { + parent_t::configure(plugin); + plugin.with_casted([&](auto &p) { + p.register_name(names::coordinator, get_address()); + p.register_name(names::peer_supervisor, get_address()); + p.register_name(names::http11_relay, get_address()); + }); + if (configure_callback) { + configure_callback(plugin); + } + } + + void on_child_shutdown(actor_base_t *actor) noexcept override { + if (actor) { + spdlog::info("child shutdown: {}, reason: {}", actor->get_identity(), actor->get_shutdown_reason()->message()); + } + parent_t::on_child_shutdown(actor); + } + + + void shutdown_finish() noexcept override { + parent_t::shutdown_finish(); + if (acceptor) { + acceptor->cancel(); + } + } + + auto get_state() noexcept { return state; } + + asio::ip::tcp::acceptor *acceptor = nullptr; + configure_callback_t configure_callback; +}; + +using supervisor_ptr_t = r::intrusive_ptr_t; +using actor_ptr_t = r::intrusive_ptr_t; + +struct fixture_t { + using acceptor_t = asio::ip::tcp::acceptor; + + fixture_t() noexcept : ctx(io_ctx), acceptor(io_ctx), peer_sock(io_ctx) { + utils::set_default("trace"); + log = utils::get_logger("fixture"); + relay_config = config::relay_config_t { + true, "https://some-endpoint.com/", 1024 * 1024, + }; + } + + void run() noexcept { + + auto strand = std::make_shared(io_ctx); + sup = ctx.create_supervisor().strand(strand).timeout(timeout).create_registry().finish(); + sup->configure_callback = [&](r::plugin::plugin_base_t &plugin) { + plugin.template with_casted([&](auto &p) { + using contact_update_t = model::message::contact_update_t; + p.subscribe_actor(r::lambda([&](contact_update_t &msg) { + on(msg); + })); + using http_req_t = net::message::http_request_t; + p.subscribe_actor(r::lambda([&](http_req_t &req) { + LOG_INFO(log, "received http request"); + http::response res; + res.result(200); + res.body() = public_relays; + sup->reply_to(req, std::move(res), public_relays.size()); + })); + using connect_req_t = net::message::connect_request_t; + p.subscribe_actor(r::lambda([&](connect_req_t &req) { + LOG_INFO(log, "(connect request)"); + on(req); + })); + + }); + }; + sup->start(); + sup->do_process(); + + auto ep = asio::ip::tcp::endpoint(asio::ip::make_address(host), 0); + acceptor.open(ep.protocol()); + acceptor.bind(ep); + acceptor.listen(); + listening_ep = acceptor.local_endpoint(); + + my_keys = utils::generate_pair("me").value(); + relay_keys = utils::generate_pair("peer").value(); + + auto md = model::device_id_t::from_cert(my_keys.cert_data).value(); + auto pd = model::device_id_t::from_cert(relay_keys.cert_data).value(); + + my_device = device_t::create(md, "my-device").value(); + relay_device = device_t::create(pd, "relay-device").value(); + + public_relays = generate_public_relays(listening_ep, relay_device); + log->debug("public relays json: {}", public_relays); + initiate_accept(); + + cluster = new cluster_t(my_device, 1); + + cluster->get_devices().put(my_device); + + main(); + } + + virtual void main() noexcept {} + + virtual std::string generate_public_relays(const asio::ip::tcp::endpoint &endpoint, model::device_ptr_t& relay_device) noexcept { + std::string pattern = R""( + { + "relays": [ + { + "url": "##URL##&pingInterval=0m5s&networkTimeout=2m0s&sessionLimitBps=0&globalLimitBps=0&statusAddr=:22070&providedBy=ina", + "location": { + "latitude": 50.1049, + "longitude": 8.6295, + "city": "Frankfurt am Main", + "country": "DE", + "continent": "EU" + } + } + ] + } + )""; + auto url = fmt::format("relay://{}/?id={}", listening_ep, relay_device->device_id().get_value()); + return boost::algorithm::replace_first_copy(pattern, "##URL##", url); + } + + virtual void initiate_accept() noexcept { + acceptor.async_accept(peer_sock, [this](auto ec) { this->accept(ec); }); + sup->acceptor = &acceptor; + } + + + virtual void accept(const sys::error_code &ec) noexcept { + LOG_INFO(log, "accept (relay), ec: {}, sock = {}", ec.message(), peer_sock.native_handle()); + auto uri = utils::parse("tcp://127.0.0.1:0/").value(); + auto cfg = transport::transport_config_t { {}, uri, *sup, std::move(peer_sock) }; + relay_trans = transport::initiate_stream(cfg); + relay_read(); + } + + + virtual actor_ptr_t create_actor() noexcept { + return sup->create_actor() + .timeout(timeout) + .cluster(cluster) + .relay_config(relay_config) + .escalate_failure() + .finish(); + } + + virtual void on(net::message::connect_request_t& req) noexcept { + auto& uri = req.payload.request_payload.uri; + log->info("requested connect to {}", uri.full); + auto cfg = transport::transport_config_t { {}, uri, *sup, {} }; + tcp::resolver resolver(io_ctx); + auto addresses = resolver.resolve(host, std::to_string(uri.port)); + auto addresses_ptr = std::make_shared(addresses); + auto trans = transport::initiate_stream(cfg); + transport::error_fn_t on_error = [&](auto &ec) { + LOG_WARN(log, "active/connect, err: {}", ec.message()); + }; + using ptr_t = model::intrusive_ptr_t>; + auto ptr = ptr_t(&req); + transport::connect_fn_t on_connect = [ptr, trans, addresses_ptr, this](auto arg) { + LOG_INFO(log, "active/connected"); + sup->reply_to(*ptr, trans); + }; + trans->async_connect(*addresses_ptr, on_connect, on_error); + } + + + void on(proto::relay::ping_t&) noexcept { + + }; + + void on(proto::relay::pong_t&) noexcept { + + }; + void on(proto::relay::join_relay_request_t&) noexcept { + LOG_INFO(log, "join_relay_request_t"); + proto::relay::serialize(proto::relay::response_t{0, "ok"}, relay_tx); + + transport::error_fn_t on_error = [&](auto &ec) { + LOG_WARN(log, "relay/write, err: {}", ec.message()); + }; + transport::io_fn_t on_write = [&](size_t bytes) { + LOG_TRACE(log, "relay/write, {} bytes", bytes); + }; + relay_trans->async_send(asio::buffer(relay_tx), on_write, on_error); + //relay_read(); + }; + void on(proto::relay::join_session_request_t&) noexcept { + + }; + void on(proto::relay::response_t&) noexcept { + + }; + void on(proto::relay::connect_request_t&) noexcept { + + }; + void on(proto::relay::session_invitation_t&) noexcept { + + }; + virtual void on(model::message::contact_update_t& update) noexcept { + auto r = update.payload.diff->apply(*cluster); + if (!r) { + LOG_ERROR(log, "error applying diff: {}", r.error().message()); + } + } + + + + void relay_read() noexcept { + transport::error_fn_t on_error = [&](auto &ec) { + LOG_WARN(log, "relay/read, err: {}", ec.message()); + }; + transport::io_fn_t on_read = [&](size_t bytes) { + LOG_TRACE(log, "relay/read, {} bytes", bytes); + auto msg = proto::relay::parse({relay_rx.data(), bytes}); + auto wrapped = std::get_if(&msg); + if (!wrapped) { + LOG_ERROR(log, "relay/read non-message?"); + return; + } + std::visit([&](auto& it) { + on(it); + }, wrapped->message); + }; + relay_rx.resize(1500); + auto buff = asio::buffer(relay_rx.data(), relay_rx.size()); + relay_trans->async_recv(buff, on_read, on_error); + LOG_TRACE(log, "relay/async recv"); + } + + config::relay_config_t relay_config; + cluster_ptr_t cluster; + asio::io_context io_ctx; + ra::system_context_asio_t ctx; + acceptor_t acceptor; + supervisor_ptr_t sup; + asio::ip::tcp::endpoint listening_ep; + utils::logger_t log; + asio::ip::tcp::socket peer_sock; + std::string public_relays; + utils::key_pair_t my_keys; + utils::key_pair_t relay_keys; + model::device_ptr_t my_device; + model::device_ptr_t relay_device; + transport::stream_sp_t relay_trans; + std::string relay_rx; + std::string relay_tx; +}; + + +void test_master_connect() { + struct F : fixture_t { + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + + CHECK(sup->get_state() == r::state_t::OPERATIONAL); + REQUIRE(my_device->get_uris().size() == 1); + CHECK(my_device->get_uris()[0].proto == "relay"); + + + sup->shutdown(); + io_ctx.restart(); + io_ctx.run(); + + CHECK(my_device->get_uris().size() == 0); + io_ctx.restart(); + io_ctx.run(); + + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + } + + void on(model::message::contact_update_t& update) noexcept override { + LOG_INFO(log, "contact_update_t"); + fixture_t::on(update); + io_ctx.stop(); + } + }; + + F().run(); +} + + +REGISTER_TEST_CASE(test_master_connect, "test_master_connect", "[relay]"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9239ece4..a5e78d4a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -166,3 +166,13 @@ target_link_libraries(077-initiator syncspirit_test_lib $<$:iphlpapi> ) add_test(077-initiator "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/077-initiator") + +add_executable(078-relay 078-relay.cpp $<$:win32-resource.rc>) +target_link_libraries(078-relay syncspirit_test_lib + $<$:wsock32> + $<$:ws2_32> + $<$:iphlpapi> +) +add_test(078-relay "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/078-relay") + + From b52c21fb79b593f97ea673240d469d19bf167ae8 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sat, 7 May 2022 22:58:06 +0300 Subject: [PATCH 15/31] relay_actor, db_actor fix use-after-free --- src/model/diff/modify/update_contact.cpp | 2 +- src/model/diff/modify/update_contact.h | 2 +- src/net/db_actor.cpp | 1 + src/net/initiator_actor.cpp | 6 +- src/net/initiator_actor.h | 7 +++ src/net/peer_supervisor.cpp | 1 + src/net/relay_actor.cpp | 2 +- tests/077-initiator.cpp | 2 + tests/078-relay.cpp | 76 +++++++++--------------- 9 files changed, 46 insertions(+), 53 deletions(-) diff --git a/src/model/diff/modify/update_contact.cpp b/src/model/diff/modify/update_contact.cpp index 7c8831e7..f5d95c83 100644 --- a/src/model/diff/modify/update_contact.cpp +++ b/src/model/diff/modify/update_contact.cpp @@ -8,7 +8,7 @@ using namespace syncspirit::model::diff::modify; -update_contact_t::update_contact_t(const model::cluster_t &cluster, const model::device_id_t& device_, +update_contact_t::update_contact_t(const model::cluster_t &cluster, const model::device_id_t &device_, const utils::uri_container_t &uris_) noexcept : device{device_}, uris{uris_} { auto &devices = cluster.get_devices(); diff --git a/src/model/diff/modify/update_contact.h b/src/model/diff/modify/update_contact.h index e5841c6a..2a159536 100644 --- a/src/model/diff/modify/update_contact.h +++ b/src/model/diff/modify/update_contact.h @@ -11,7 +11,7 @@ namespace syncspirit::model::diff::modify { struct SYNCSPIRIT_API update_contact_t final : contact_diff_t { using ip_addresses_t = std::vector; - update_contact_t(const model::cluster_t &cluster, const model::device_id_t& device, + update_contact_t(const model::cluster_t &cluster, const model::device_id_t &device, const utils::uri_container_t &uris) noexcept; update_contact_t(const model::cluster_t &cluster, const ip_addresses_t &addresses) noexcept; diff --git a/src/net/db_actor.cpp b/src/net/db_actor.cpp index d918de66..29f9d4b4 100644 --- a/src/net/db_actor.cpp +++ b/src/net/db_actor.cpp @@ -171,6 +171,7 @@ void db_actor_t::shutdown_finish() noexcept { if (r != MDBX_SUCCESS) { LOG_ERROR(log, "{}, open, mbdx close error ({}): {}", identity, r, mdbx_strerror(r)); } + env = nullptr; r::actor_base_t::shutdown_finish(); } diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 40d60f6e..0ffdf246 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -21,7 +21,7 @@ r::plugin::resource_id_t handshake = 3; initiator_actor_t::initiator_actor_t(config_t &cfg) : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, uris{cfg.uris}, ssl_pair{*cfg.ssl_pair}, sock(std::move(cfg.sock)), cluster{std::move(cfg.cluster)}, sink(std::move(cfg.sink)), - custom(std::move(cfg.custom)) { + custom(std::move(cfg.custom)), router{*cfg.router} { log = utils::get_logger("net.initator"); active = !sock.has_value(); } @@ -69,7 +69,7 @@ void initiator_actor_t::initiate_active() noexcept { while (uri_idx < uris.size()) { auto &uri = uris[uri_idx++]; if (uri.proto == "tcp") { - auto sup = static_cast(supervisor); + auto sup = static_cast(&router); auto trans = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri); initiate(std::move(trans), uri); return; @@ -88,7 +88,7 @@ void initiator_actor_t::initiate_passive() noexcept { return; } - auto sup = static_cast(supervisor); + auto sup = static_cast(&router); transport = transport::initiate_tls_passive(*sup, ssl_pair, std::move(sock.value())); initiate_handshake(); } diff --git a/src/net/initiator_actor.h b/src/net/initiator_actor.h index 015ac3ec..bdc55347 100644 --- a/src/net/initiator_actor.h +++ b/src/net/initiator_actor.h @@ -19,6 +19,7 @@ struct initiator_actor_config_t : public r::actor_config_t { model::cluster_ptr_t cluster; r::address_ptr_t sink; r::message_ptr_t custom; + r::supervisor_t *router; }; template struct initiator_actor_config_builder_t : r::actor_config_builder_t { @@ -60,6 +61,11 @@ template struct initiator_actor_config_builder_t : r::actor_con parent_t::config.custom = std::move(value); return std::move(*static_cast(this)); } + + builder_t &&router(r::supervisor_t &value) &&noexcept { + parent_t::config.router = &value; + return std::move(*static_cast(this)); + } }; struct initiator_actor_t : r::actor_base_t { @@ -95,6 +101,7 @@ struct initiator_actor_t : r::actor_base_t { model::cluster_ptr_t cluster; r::address_ptr_t sink; r::message_ptr_t custom; + r::supervisor_t &router; transport::stream_sp_t transport; r::address_ptr_t resolver; diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index 24746eb3..9f76d871 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -107,6 +107,7 @@ void peer_supervisor_t::on_connect(message::connect_request_t &msg) noexcept { .peer_device_id(p.device_id) .uris({p.uri}) .custom(&msg) + .router(*locality_leader) .sink(addr_unknown) .init_timeout(connect_timeout * 2) .shutdown_timeout(connect_timeout) diff --git a/src/net/relay_actor.cpp b/src/net/relay_actor.cpp index 01a0aaec..df71ad44 100644 --- a/src/net/relay_actor.cpp +++ b/src/net/relay_actor.cpp @@ -61,7 +61,7 @@ void relay_actor_t::shutdown_start() noexcept { if (rx_state) { auto self = cluster->get_device(); utils::uri_container_t uris; - for(auto& uri: self->get_uris()) { + for (auto &uri : self->get_uris()) { if (uri.proto != "relay") { uris.emplace_back(uri); } diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp index aa7dd2de..de5ea29d 100644 --- a/tests/077-initiator.cpp +++ b/tests/077-initiator.cpp @@ -178,6 +178,7 @@ struct fixture_t { .cluster(use_model ? cluster : nullptr) .sink(sup->get_address()) .ssl_pair(&my_keys) + .router(*sup) .escalate_failure() .finish(); } @@ -187,6 +188,7 @@ struct fixture_t { .timeout(timeout) .sock(std::move(peer_sock)) .ssl_pair(&my_keys) + .router(*sup) .cluster(cluster) .sink(sup->get_address()) .escalate_failure() diff --git a/tests/078-relay.cpp b/tests/078-relay.cpp index 9ff5eaba..e5734d70 100644 --- a/tests/078-relay.cpp +++ b/tests/078-relay.cpp @@ -45,12 +45,12 @@ struct supervisor_t : ra::supervisor_asio_t { void on_child_shutdown(actor_base_t *actor) noexcept override { if (actor) { - spdlog::info("child shutdown: {}, reason: {}", actor->get_identity(), actor->get_shutdown_reason()->message()); + spdlog::info("child shutdown: {}, reason: {}", actor->get_identity(), + actor->get_shutdown_reason()->message()); } parent_t::on_child_shutdown(actor); } - void shutdown_finish() noexcept override { parent_t::shutdown_finish(); if (acceptor) { @@ -73,8 +73,10 @@ struct fixture_t { fixture_t() noexcept : ctx(io_ctx), acceptor(io_ctx), peer_sock(io_ctx) { utils::set_default("trace"); log = utils::get_logger("fixture"); - relay_config = config::relay_config_t { - true, "https://some-endpoint.com/", 1024 * 1024, + relay_config = config::relay_config_t{ + true, + "https://some-endpoint.com/", + 1024 * 1024, }; } @@ -85,9 +87,7 @@ struct fixture_t { sup->configure_callback = [&](r::plugin::plugin_base_t &plugin) { plugin.template with_casted([&](auto &p) { using contact_update_t = model::message::contact_update_t; - p.subscribe_actor(r::lambda([&](contact_update_t &msg) { - on(msg); - })); + p.subscribe_actor(r::lambda([&](contact_update_t &msg) { on(msg); })); using http_req_t = net::message::http_request_t; p.subscribe_actor(r::lambda([&](http_req_t &req) { LOG_INFO(log, "received http request"); @@ -101,7 +101,6 @@ struct fixture_t { LOG_INFO(log, "(connect request)"); on(req); })); - }); }; sup->start(); @@ -135,7 +134,8 @@ struct fixture_t { virtual void main() noexcept {} - virtual std::string generate_public_relays(const asio::ip::tcp::endpoint &endpoint, model::device_ptr_t& relay_device) noexcept { + virtual std::string generate_public_relays(const asio::ip::tcp::endpoint &endpoint, + model::device_ptr_t &relay_device) noexcept { std::string pattern = R""( { "relays": [ @@ -161,16 +161,14 @@ struct fixture_t { sup->acceptor = &acceptor; } - virtual void accept(const sys::error_code &ec) noexcept { LOG_INFO(log, "accept (relay), ec: {}, sock = {}", ec.message(), peer_sock.native_handle()); auto uri = utils::parse("tcp://127.0.0.1:0/").value(); - auto cfg = transport::transport_config_t { {}, uri, *sup, std::move(peer_sock) }; + auto cfg = transport::transport_config_t{{}, uri, *sup, std::move(peer_sock)}; relay_trans = transport::initiate_stream(cfg); relay_read(); } - virtual actor_ptr_t create_actor() noexcept { return sup->create_actor() .timeout(timeout) @@ -180,17 +178,15 @@ struct fixture_t { .finish(); } - virtual void on(net::message::connect_request_t& req) noexcept { - auto& uri = req.payload.request_payload.uri; + virtual void on(net::message::connect_request_t &req) noexcept { + auto &uri = req.payload.request_payload.uri; log->info("requested connect to {}", uri.full); - auto cfg = transport::transport_config_t { {}, uri, *sup, {} }; + auto cfg = transport::transport_config_t{{}, uri, *sup, {}}; tcp::resolver resolver(io_ctx); auto addresses = resolver.resolve(host, std::to_string(uri.port)); - auto addresses_ptr = std::make_shared(addresses); + auto addresses_ptr = std::make_shared(addresses); auto trans = transport::initiate_stream(cfg); - transport::error_fn_t on_error = [&](auto &ec) { - LOG_WARN(log, "active/connect, err: {}", ec.message()); - }; + transport::error_fn_t on_error = [&](auto &ec) { LOG_WARN(log, "active/connect, err: {}", ec.message()); }; using ptr_t = model::intrusive_ptr_t>; auto ptr = ptr_t(&req); transport::connect_fn_t on_connect = [ptr, trans, addresses_ptr, this](auto arg) { @@ -200,52 +196,43 @@ struct fixture_t { trans->async_connect(*addresses_ptr, on_connect, on_error); } - - void on(proto::relay::ping_t&) noexcept { + void on(proto::relay::ping_t &) noexcept { }; - void on(proto::relay::pong_t&) noexcept { + void on(proto::relay::pong_t &) noexcept { }; - void on(proto::relay::join_relay_request_t&) noexcept { + void on(proto::relay::join_relay_request_t &) noexcept { LOG_INFO(log, "join_relay_request_t"); proto::relay::serialize(proto::relay::response_t{0, "ok"}, relay_tx); - transport::error_fn_t on_error = [&](auto &ec) { - LOG_WARN(log, "relay/write, err: {}", ec.message()); - }; - transport::io_fn_t on_write = [&](size_t bytes) { - LOG_TRACE(log, "relay/write, {} bytes", bytes); - }; + transport::error_fn_t on_error = [&](auto &ec) { LOG_WARN(log, "relay/write, err: {}", ec.message()); }; + transport::io_fn_t on_write = [&](size_t bytes) { LOG_TRACE(log, "relay/write, {} bytes", bytes); }; relay_trans->async_send(asio::buffer(relay_tx), on_write, on_error); - //relay_read(); + // relay_read(); }; - void on(proto::relay::join_session_request_t&) noexcept { + void on(proto::relay::join_session_request_t &) noexcept { }; - void on(proto::relay::response_t&) noexcept { + void on(proto::relay::response_t &) noexcept { }; - void on(proto::relay::connect_request_t&) noexcept { + void on(proto::relay::connect_request_t &) noexcept { }; - void on(proto::relay::session_invitation_t&) noexcept { + void on(proto::relay::session_invitation_t &) noexcept { }; - virtual void on(model::message::contact_update_t& update) noexcept { + virtual void on(model::message::contact_update_t &update) noexcept { auto r = update.payload.diff->apply(*cluster); if (!r) { LOG_ERROR(log, "error applying diff: {}", r.error().message()); } } - - void relay_read() noexcept { - transport::error_fn_t on_error = [&](auto &ec) { - LOG_WARN(log, "relay/read, err: {}", ec.message()); - }; + transport::error_fn_t on_error = [&](auto &ec) { LOG_WARN(log, "relay/read, err: {}", ec.message()); }; transport::io_fn_t on_read = [&](size_t bytes) { LOG_TRACE(log, "relay/read, {} bytes", bytes); auto msg = proto::relay::parse({relay_rx.data(), bytes}); @@ -254,9 +241,7 @@ struct fixture_t { LOG_ERROR(log, "relay/read non-message?"); return; } - std::visit([&](auto& it) { - on(it); - }, wrapped->message); + std::visit([&](auto &it) { on(it); }, wrapped->message); }; relay_rx.resize(1500); auto buff = asio::buffer(relay_rx.data(), relay_rx.size()); @@ -283,7 +268,6 @@ struct fixture_t { std::string relay_tx; }; - void test_master_connect() { struct F : fixture_t { void main() noexcept override { @@ -294,7 +278,6 @@ void test_master_connect() { REQUIRE(my_device->get_uris().size() == 1); CHECK(my_device->get_uris()[0].proto == "relay"); - sup->shutdown(); io_ctx.restart(); io_ctx.run(); @@ -306,7 +289,7 @@ void test_master_connect() { CHECK(sup->get_state() == r::state_t::SHUT_DOWN); } - void on(model::message::contact_update_t& update) noexcept override { + void on(model::message::contact_update_t &update) noexcept override { LOG_INFO(log, "contact_update_t"); fixture_t::on(update); io_ctx.stop(); @@ -316,5 +299,4 @@ void test_master_connect() { F().run(); } - REGISTER_TEST_CASE(test_master_connect, "test_master_connect", "[relay]"); From cfd12d6306a3add770b9a97c54079761f4b5c53b Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 8 May 2022 16:29:21 +0300 Subject: [PATCH 16/31] relay_actor (master), WIP --- src/model/diff/contact_visitor.cpp | 4 + src/model/diff/contact_visitor.h | 2 + src/model/diff/modify/connect_request.cpp | 1 - src/model/diff/modify/connect_request.h | 2 +- src/net/messages.h | 1 + src/net/peer_supervisor.cpp | 2 +- src/net/relay_actor.cpp | 36 ++++++++- src/net/relay_actor.h | 1 + src/proto/relay_support.cpp | 2 +- src/utils/error_code.cpp | 3 + src/utils/error_code.h | 1 + tests/016-relay-support.cpp | 25 ++++++- tests/078-relay.cpp | 90 +++++++++++++++++++---- 13 files changed, 150 insertions(+), 20 deletions(-) diff --git a/src/model/diff/contact_visitor.cpp b/src/model/diff/contact_visitor.cpp index 9d91ba8c..03eede1e 100644 --- a/src/model/diff/contact_visitor.cpp +++ b/src/model/diff/contact_visitor.cpp @@ -12,3 +12,7 @@ auto contact_visitor_t::operator()(const modify::connect_request_t &) noexcept - auto contact_visitor_t::operator()(const modify::update_contact_t &) noexcept -> outcome::result { return outcome::success(); } + +auto contact_visitor_t::operator()(const modify::relay_connect_request_t &) noexcept -> outcome::result { + return outcome::success(); +} diff --git a/src/model/diff/contact_visitor.h b/src/model/diff/contact_visitor.h index 1325cbc8..bb32932b 100644 --- a/src/model/diff/contact_visitor.h +++ b/src/model/diff/contact_visitor.h @@ -11,6 +11,7 @@ namespace syncspirit::model::diff { namespace modify { struct update_contact_t; struct connect_request_t; +struct relay_connect_request_t; } // namespace modify template <> struct SYNCSPIRIT_API generic_visitor_t { @@ -18,6 +19,7 @@ template <> struct SYNCSPIRIT_API generic_visitor_t { virtual outcome::result operator()(const modify::update_contact_t &) noexcept; virtual outcome::result operator()(const modify::connect_request_t &) noexcept; + virtual outcome::result operator()(const modify::relay_connect_request_t &) noexcept; }; using contact_visitor_t = generic_visitor_t; diff --git a/src/model/diff/modify/connect_request.cpp b/src/model/diff/modify/connect_request.cpp index cee80b18..89a9fb5b 100644 --- a/src/model/diff/modify/connect_request.cpp +++ b/src/model/diff/modify/connect_request.cpp @@ -3,7 +3,6 @@ #include "connect_request.h" #include "../contact_visitor.h" -#include "../../cluster.h" using namespace syncspirit::model::diff::modify; diff --git a/src/model/diff/modify/connect_request.h b/src/model/diff/modify/connect_request.h index 65b43680..10993d7f 100644 --- a/src/model/diff/modify/connect_request.h +++ b/src/model/diff/modify/connect_request.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include "../contact_diff.h" #include "model/cluster.h" diff --git a/src/net/messages.h b/src/net/messages.h index dbbe87f1..d99f1c24 100644 --- a/src/net/messages.h +++ b/src/net/messages.h @@ -139,6 +139,7 @@ struct SYNCSPIRIT_API block_request_t { struct connect_response_t { transport::stream_sp_t transport; + tcp::endpoint remote_endpoint; }; struct connect_request_t { diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index 9f76d871..f46c9e38 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -96,7 +96,7 @@ void peer_supervisor_t::on_connected(message::peer_connected_t &msg) noexcept { LOG_TRACE(log, "{}, on_connected", identity); auto &p = msg.payload; auto req = static_cast(p.custom.get()); - reply_to(*req, std::move(p.transport)); + reply_to(*req, std::move(p.transport), std::move(p.remote_endpoint)); } void peer_supervisor_t::on_connect(message::connect_request_t &msg) noexcept { diff --git a/src/net/relay_actor.cpp b/src/net/relay_actor.cpp index df71ad44..ad648a4f 100644 --- a/src/net/relay_actor.cpp +++ b/src/net/relay_actor.cpp @@ -4,6 +4,7 @@ #include "utils/beast_support.h" #include "model/messages.h" #include "model/diff/modify/update_contact.h" +#include "model/diff/modify/relay_connect_request.h" #include #include "messages.h" #include @@ -225,7 +226,9 @@ void relay_actor_t::on_connect(message::connect_response_t &res) noexcept { return connect_to_relay(); } LOG_DEBUG(log, "{}, connected to relay {}", identity, r->device_id.get_short()); - master = std::move(res.payload.res.transport); + auto &p = res.payload.res; + master = std::move(p.transport); + master_endpoint = std::move(p.remote_endpoint); rx_buff.resize(BUFF_SZ); read_master(); rx_state |= rx_state_t::response; @@ -354,7 +357,36 @@ bool relay_actor_t::on(proto::relay::response_t &res) noexcept { return true; } -bool relay_actor_t::on(proto::relay::session_invitation_t &) noexcept { std::abort(); } +bool relay_actor_t::on(proto::relay::session_invitation_t &msg) noexcept { + auto diff = model::diff::contact_diff_ptr_t{}; + auto device_opt = model::device_id_t::from_sha256(msg.from); + if (!device_opt) { + LOG_ERROR(log, "{}, not valid device: {}", identity, spdlog::to_hex(msg.from.begin(), msg.from.end())); + auto ec = utils::make_error_code(utils::error_code_t::invalid_deviceid); + do_shutdown(make_error(ec)); + return false; + } + + asio::ip::tcp::endpoint relay_ep; + if (!msg.address.empty()) { + sys::error_code ec; + auto ip = asio::ip::make_address(msg.address, ec); + if (ec) { + LOG_ERROR(log, "{}, invalid ip address: {}", identity, + spdlog::to_hex(msg.address.begin(), msg.address.end())); + do_shutdown(make_error(ec)); + return false; + } + relay_ep = asio::ip::tcp::endpoint{ip, (uint16_t)msg.port}; + } else { + relay_ep = asio::ip::tcp::endpoint{master_endpoint.address(), (uint16_t)msg.port}; + } + + diff = new model::diff::modify::relay_connect_request_t(std::move(device_opt.value()), std::move(msg.key), + std::move(relay_ep)); + send(coordinator, std::move(diff), this); + return true; +} void relay_actor_t::respawn_ping_timer() noexcept { if (ping_timer) { diff --git a/src/net/relay_actor.h b/src/net/relay_actor.h index c0548344..83592d1f 100644 --- a/src/net/relay_actor.h +++ b/src/net/relay_actor.h @@ -96,6 +96,7 @@ struct SYNCSPIRIT_API relay_actor_t : public r::actor_base_t { relays_t relays; int relay_index = -1; transport::stream_sp_t master; + tcp::endpoint master_endpoint; fmt::memory_buffer rx_buff; std::size_t rx_idx = 0; std::uint32_t rx_state = 0; diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp index 37d52440..b72108f3 100644 --- a/src/proto/relay_support.cpp +++ b/src/proto/relay_support.cpp @@ -252,7 +252,7 @@ static parse_result_t parse_session_invitation(std::string_view data) noexcept { } auto addr_sz = be::big_to_native(*reinterpret_cast(data.data())); data = data.substr(sizeof(uint32_t)); - if (data.size() < key_sz) { + if (data.size() < addr_sz) { return protocol_error_t{}; } auto addr = data.substr(0, addr_sz); diff --git a/src/utils/error_code.cpp b/src/utils/error_code.cpp index da29a2e2..310010b9 100644 --- a/src/utils/error_code.cpp +++ b/src/utils/error_code.cpp @@ -113,6 +113,9 @@ std::string error_code_category::message(int c) const { case error_code_t::relay_failure: r = "relay failure"; break; + case error_code_t::invalid_deviceid: + r = "invalid device id"; + break; default: r = "unknown"; } diff --git a/src/utils/error_code.h b/src/utils/error_code.h index 6005db4f..ce4a9b1f 100644 --- a/src/utils/error_code.h +++ b/src/utils/error_code.h @@ -72,6 +72,7 @@ enum class error_code_t { cannot_get_public_relays, protocol_error, relay_failure, + invalid_deviceid, }; enum class bep_error_code_t { diff --git a/tests/016-relay-support.cpp b/tests/016-relay-support.cpp index dfbe4572..c5d1f2f7 100644 --- a/tests/016-relay-support.cpp +++ b/tests/016-relay-support.cpp @@ -99,7 +99,7 @@ TEST_CASE("relay proto", "[relay]") { CHECK(target->server_socket == source.server_socket); } - SECTION("response piece") { + SECTION("response sample") { const unsigned char data[] = {0x9e, 0x79, 0xbc, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x00}; @@ -114,6 +114,29 @@ TEST_CASE("relay proto", "[relay]") { auto details = std::string_view("success"); CHECK(target->details == details); } + + SECTION("session invitation sample") { + const unsigned char data[] = {0x9e, 0x79, 0xbc, 0x40, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4a, 0x00, + 0x00, 0x00, 0x20, 0xf8, 0x31, 0xf5, 0x75, 0xea, 0x61, 0x8a, 0x2f, 0x15, 0xef, + 0x67, 0x68, 0x36, 0x4a, 0x62, 0x89, 0xfc, 0x76, 0xb6, 0x73, 0xc8, 0x5a, 0x2a, + 0xbe, 0x60, 0x8f, 0x4a, 0xff, 0x27, 0xba, 0x39, 0x02, 0x00, 0x00, 0x00, 0x12, + 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x2d, 0x69, 0x6d, 0x73, 0x70, 0x75, 0x6d, 0x2d, + 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; + auto ptr = reinterpret_cast(data); + auto str = std::string_view(ptr, sizeof(data)); + auto r = parse(str); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == str.size()); + auto target = std::get_if(&msg->message); + REQUIRE(target); + CHECK(target->from.size() == 32); + CHECK(target->key == "lorem-imspum-dolor"); + CHECK(target->address == ""); + CHECK(target->port == 12345); + CHECK(target->server_socket); + } } SECTION("incompplete") { diff --git a/tests/078-relay.cpp b/tests/078-relay.cpp index e5734d70..a95f148d 100644 --- a/tests/078-relay.cpp +++ b/tests/078-relay.cpp @@ -5,6 +5,7 @@ #include "utils/tls.h" #include "model/cluster.h" #include "model/messages.h" +#include "model/diff/modify/relay_connect_request.h" #include "net/names.h" #include "net/messages.h" #include "net/relay_actor.h" @@ -67,7 +68,7 @@ struct supervisor_t : ra::supervisor_asio_t { using supervisor_ptr_t = r::intrusive_ptr_t; using actor_ptr_t = r::intrusive_ptr_t; -struct fixture_t { +struct fixture_t : private model::diff::contact_visitor_t { using acceptor_t = asio::ip::tcp::acceptor; fixture_t() noexcept : ctx(io_ctx), acceptor(io_ctx), peer_sock(io_ctx) { @@ -113,13 +114,16 @@ struct fixture_t { listening_ep = acceptor.local_endpoint(); my_keys = utils::generate_pair("me").value(); - relay_keys = utils::generate_pair("peer").value(); + relay_keys = utils::generate_pair("relay").value(); + peer_keys = utils::generate_pair("peer").value(); auto md = model::device_id_t::from_cert(my_keys.cert_data).value(); - auto pd = model::device_id_t::from_cert(relay_keys.cert_data).value(); + auto rd = model::device_id_t::from_cert(relay_keys.cert_data).value(); + auto pd = model::device_id_t::from_cert(peer_keys.cert_data).value(); my_device = device_t::create(md, "my-device").value(); - relay_device = device_t::create(pd, "relay-device").value(); + relay_device = device_t::create(rd, "relay-device").value(); + peer_device = device_t::create(rd, "peer-device").value(); public_relays = generate_public_relays(listening_ep, relay_device); log->debug("public relays json: {}", public_relays); @@ -128,6 +132,9 @@ struct fixture_t { cluster = new cluster_t(my_device, 1); cluster->get_devices().put(my_device); + cluster->get_devices().put(peer_device); + + session_key = "lorem-imspum-dolor"; main(); } @@ -189,13 +196,20 @@ struct fixture_t { transport::error_fn_t on_error = [&](auto &ec) { LOG_WARN(log, "active/connect, err: {}", ec.message()); }; using ptr_t = model::intrusive_ptr_t>; auto ptr = ptr_t(&req); - transport::connect_fn_t on_connect = [ptr, trans, addresses_ptr, this](auto arg) { + transport::connect_fn_t on_connect = [ptr, trans, addresses_ptr, this](transport::resolved_item_t it) { LOG_INFO(log, "active/connected"); - sup->reply_to(*ptr, trans); + sup->reply_to(*ptr, trans, it->endpoint()); }; trans->async_connect(*addresses_ptr, on_connect, on_error); } + void send_relay(const proto::relay::message_t &msg) noexcept { + proto::relay::serialize(msg, relay_tx); + transport::error_fn_t on_error = [&](auto &ec) { LOG_WARN(log, "relay/write, err: {}", ec.message()); }; + transport::io_fn_t on_write = [&](size_t bytes) { LOG_TRACE(log, "relay/write, {} bytes", bytes); }; + relay_trans->async_send(asio::buffer(relay_tx), on_write, on_error); + } + void on(proto::relay::ping_t &) noexcept { }; @@ -205,13 +219,9 @@ struct fixture_t { }; void on(proto::relay::join_relay_request_t &) noexcept { LOG_INFO(log, "join_relay_request_t"); - proto::relay::serialize(proto::relay::response_t{0, "ok"}, relay_tx); - - transport::error_fn_t on_error = [&](auto &ec) { LOG_WARN(log, "relay/write, err: {}", ec.message()); }; - transport::io_fn_t on_write = [&](size_t bytes) { LOG_TRACE(log, "relay/write, {} bytes", bytes); }; - relay_trans->async_send(asio::buffer(relay_tx), on_write, on_error); - // relay_read(); + send_relay(proto::relay::response_t{0, "ok"}); }; + void on(proto::relay::join_session_request_t &) noexcept { }; @@ -225,10 +235,15 @@ struct fixture_t { }; virtual void on(model::message::contact_update_t &update) noexcept { - auto r = update.payload.diff->apply(*cluster); + auto &diff = *update.payload.diff; + auto r = diff.apply(*cluster); if (!r) { LOG_ERROR(log, "error applying diff: {}", r.error().message()); } + r = diff.visit(*this); + if (!r) { + LOG_ERROR(log, "error visiting diff: {}", r.error().message()); + } } void relay_read() noexcept { @@ -261,11 +276,14 @@ struct fixture_t { std::string public_relays; utils::key_pair_t my_keys; utils::key_pair_t relay_keys; + utils::key_pair_t peer_keys; model::device_ptr_t my_device; model::device_ptr_t relay_device; + model::device_ptr_t peer_device; transport::stream_sp_t relay_trans; std::string relay_rx; std::string relay_tx; + std::string session_key; }; void test_master_connect() { @@ -299,4 +317,50 @@ void test_master_connect() { F().run(); } +void test_passive() { + struct F : fixture_t { + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sent); + CHECK(received); + CHECK(sup->get_state() == r::state_t::OPERATIONAL); + + sup->shutdown(); + io_ctx.restart(); + io_ctx.run(); + + CHECK(my_device->get_uris().size() == 0); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + } + + void on(model::message::contact_update_t &update) noexcept override { + LOG_INFO(log, "contact_update_t"); + fixture_t::on(update); + if (my_device->get_uris().size() == 1 && !sent) { + sent = true; + auto msg = proto::relay::session_invitation_t{ + std::string(peer_device->device_id().get_sha256()), session_key, {}, 12345, true}; + send_relay(msg); + } + } + + outcome::result operator()(const model::diff::modify::relay_connect_request_t &diff) noexcept override { + CHECK(diff.peer == peer_device->device_id()); + CHECK(diff.session_key == session_key); + CHECK(diff.relay.port() == 12345); + CHECK(diff.relay.address().to_string() == "127.0.0.1"); + received = true; + io_ctx.stop(); + return outcome::success(); + } + + bool sent = false; + bool received = false; + }; + + F().run(); +} + REGISTER_TEST_CASE(test_master_connect, "test_master_connect", "[relay]"); +REGISTER_TEST_CASE(test_passive, "test_passive", "[relay]"); From c474a40a5359ff461295db0bc8333ac3e43f4153 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 8 May 2022 22:30:02 +0300 Subject: [PATCH 17/31] initiator_actor, passive connecting to relay --- CMakeLists.txt | 1 + src/net/http_actor.cpp | 2 +- src/net/initiator_actor.cpp | 130 +++++++++++++++++++++++++++++++----- src/net/initiator_actor.h | 16 ++++- src/transport/base.h | 1 + src/transport/http.cpp | 2 - src/transport/http.h | 2 +- src/transport/impl.hpp | 8 ++- src/transport/stream.cpp | 44 ++++++++---- src/transport/stream.h | 6 +- tests/077-initiator.cpp | 71 +++++++++++++++++++- tests/078-relay.cpp | 4 +- 12 files changed, 246 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f97cff7..b0c3f527 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,7 @@ add_library(syncspirit_lib src/model/diff/modify/local_update.cpp src/model/diff/modify/lock_file.cpp src/model/diff/modify/new_file.cpp + src/model/diff/modify/relay_connect_request.cpp src/model/diff/modify/share_folder.cpp src/model/diff/modify/update_contact.cpp src/model/diff/modify/update_peer.cpp diff --git a/src/net/http_actor.cpp b/src/net/http_actor.cpp index 745ddfcb..d950b6c9 100644 --- a/src/net/http_actor.cpp +++ b/src/net/http_actor.cpp @@ -149,7 +149,7 @@ void http_actor_t::on_resolve(message::resolve_response_t &res) noexcept { auto &payload = queue.front()->payload.request_payload; auto &ssl_ctx = payload->ssl_context; auto sup = static_cast(supervisor); - transport::transport_config_t cfg{std::move(ssl_ctx), payload->url, *sup, {}}; + transport::transport_config_t cfg{std::move(ssl_ctx), payload->url, *sup, {}, true}; transport = transport::initiate_http(cfg); if (!transport) { auto ec = utils::make_error_code(utils::error_code_t::transport_not_available); diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 0ffdf246..66fe4dee 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -2,9 +2,11 @@ #include "constants.h" #include "names.h" #include "model/messages.h" +#include "proto/relay_support.h" #include "utils/error_code.h" #include "model/diff/peer/peer_state.h" #include +#include #include using namespace syncspirit::net; @@ -15,34 +17,55 @@ r::plugin::resource_id_t initializing = 0; r::plugin::resource_id_t resolving = 1; r::plugin::resource_id_t connect = 2; r::plugin::resource_id_t handshake = 3; +r::plugin::resource_id_t read = 4; +r::plugin::resource_id_t write = 5; } // namespace resource } // namespace +static constexpr size_t BUFF_SZ = 256; + initiator_actor_t::initiator_actor_t(config_t &cfg) - : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, uris{cfg.uris}, ssl_pair{*cfg.ssl_pair}, + : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, uris{cfg.uris}, + relay_session(std::move(cfg.relay_session)), ssl_pair{*cfg.ssl_pair}, sock(std::move(cfg.sock)), cluster{std::move(cfg.cluster)}, sink(std::move(cfg.sink)), custom(std::move(cfg.custom)), router{*cfg.router} { log = utils::get_logger("net.initator"); - active = !sock.has_value(); + if (sock.has_value()) { + role = role_t::passive; + } else { + if (!relay_session.empty()) { + role = role_t::relay_passive; + } else { + role = role_t::active; + } + } } void initiator_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { r::actor_base_t::configure(plugin); plugin.with_casted([&](auto &p) { - if (active) { - auto value = fmt::format("init/active:{}", peer_device_id.get_short()); - p.set_identity(value, false); - } else { + std::string value; + switch (role) { + case role_t::active: + value = fmt::format("init/active:{}", peer_device_id.get_short()); + break; + case role_t::passive: { auto ep = sock.value().remote_endpoint(); - auto value = fmt::format("init/passive:{}", ep); - p.set_identity(value, false); + value = fmt::format("init/passive:{}", ep); + break; } + case role_t::relay_passive: { + value = fmt::format("init/relay:{}", peer_device_id.get_short()); + break; + } + } + p.set_identity(value, false); }); plugin.with_casted([&](auto &p) { p.subscribe_actor(&initiator_actor_t::on_resolve); resources->acquire(resource::initializing); - if (active) { + if (role == role_t::active) { if (cluster) { auto diff = model::diff::cluster_diff_ptr_t(); auto state = model::device_state_t::dialing; @@ -51,8 +74,10 @@ void initiator_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { send(coordinator, std::move(diff)); } initiate_active(); - } else { + } else if (role == role_t::passive) { initiate_passive(); + } else if (role == role_t::relay_passive) { + initiate_relay_passive(); } }); plugin.with_casted([&](auto &p) { @@ -93,6 +118,19 @@ void initiator_actor_t::initiate_passive() noexcept { initiate_handshake(); } +void initiator_actor_t::initiate_relay_passive() noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + + auto sup = static_cast(&router); + auto &uri = uris.at(0); + transport::transport_config_t cfg{{}, uri, *sup, {}, true}; + transport = transport::initiate_stream(cfg); + assert(transport); + resolve(uri); +} + void initiator_actor_t::on_start() noexcept { r::actor_base_t::on_start(); LOG_TRACE(log, "{}, on_start", identity); @@ -117,7 +155,8 @@ void initiator_actor_t::shutdown_start() noexcept { void initiator_actor_t::shutdown_finish() noexcept { LOG_TRACE(log, "{}, shutdown_finish", identity); - if (active && !success && cluster) { + bool notify_offline = (role == role_t::active || role == role_t::relay_passive) && !success && cluster; + if (notify_offline) { auto diff = model::diff::cluster_diff_ptr_t(); auto state = model::device_state_t::offline; auto sha256 = peer_device_id.get_sha256(); @@ -127,8 +166,7 @@ void initiator_actor_t::shutdown_finish() noexcept { r::actor_base_t::shutdown_finish(); } -void initiator_actor_t::initiate(transport::stream_sp_t stream, const utils::URI &uri) noexcept { - transport = std::move(stream); +void initiator_actor_t::resolve(const utils::URI &uri) noexcept { LOG_TRACE(log, "{}, resolving {} (transport = {})", identity, uri.full, (void *)transport.get()); pt::time_duration resolve_timeout = init_timeout / 2; auto port = std::to_string(uri.port); @@ -136,6 +174,11 @@ void initiator_actor_t::initiate(transport::stream_sp_t stream, const utils::URI resources->acquire(resource::resolving); } +void initiator_actor_t::initiate(transport::stream_sp_t stream, const utils::URI &uri) noexcept { + transport = std::move(stream); + resolve(uri); +} + void initiator_actor_t::on_resolve(message::resolve_response_t &res) noexcept { LOG_TRACE(log, "{}, on_resolve", identity); resources->release(resource::resolving); @@ -163,7 +206,7 @@ void initiator_actor_t::on_io_error(const sys::error_code &ec, r::plugin::resour LOG_WARN(log, "{}, on_io_error: {}", identity, ec.message()); } if (state < r::state_t::SHUTTING_DOWN) { - if (!connected && active) { + if (!connected && role == role_t::active) { initiate_active(); } else { connected = false; @@ -175,17 +218,37 @@ void initiator_actor_t::on_io_error(const sys::error_code &ec, r::plugin::resour void initiator_actor_t::on_connect(resolve_it_t) noexcept { LOG_TRACE(log, "{}, on_connect, device_id = {}", identity, peer_device_id.get_short()); - initiate_handshake(); resources->release(resource::connect); + if (role == role_t::active) { + initiate_handshake(); + } else { + assert(role == role_t::relay_passive); + join_session(); + } } void initiator_actor_t::initiate_handshake() noexcept { - LOG_TRACE(log, "{}, connected, initializing handshake", identity); + LOG_TRACE(log, "{}, initializing handshake", identity); connected = true; transport::handshake_fn_t handshake_fn([&](auto &&...args) { on_handshake(args...); }); transport::error_fn_t error_fn([&](auto arg) { on_io_error(arg, resource::handshake); }); - transport->async_handshake(handshake_fn, error_fn); resources->acquire(resource::handshake); + transport->async_handshake(handshake_fn, error_fn); +} + +void initiator_actor_t::join_session() noexcept { + transport::error_fn_t read_err_fn([&](auto arg) { on_io_error(arg, resource::read); }); + transport::io_fn_t read_fn = [this](size_t bytes) { on_read(bytes); }; + rx_buff.resize(BUFF_SZ); + transport->async_recv(asio::buffer(rx_buff), read_fn, read_err_fn); + resources->acquire(resource::read); + + auto msg = proto::relay::join_session_request_t{std::move(relay_session)}; + proto::relay::serialize(msg, relay_session); + transport::error_fn_t write_err_fn([&](auto arg) { on_io_error(arg, resource::write); }); + transport::io_fn_t write_fn = [this](size_t bytes) { on_write(bytes); }; + transport->async_send(asio::buffer(relay_session), write_fn, write_err_fn); + resources->acquire(resource::write); } void initiator_actor_t::on_handshake(bool valid_peer, utils::x509_t &cert, const tcp::endpoint &peer_endpoint, @@ -208,3 +271,36 @@ void initiator_actor_t::on_handshake(bool valid_peer, utils::x509_t &cert, const remote_endpoint = peer_endpoint; resources->release(resource::initializing); } + +void initiator_actor_t::on_write(size_t bytes) noexcept { + LOG_TRACE(log, "{}, on_write, {} bytes", identity, bytes); + resources->release(resource::write); +} + +void initiator_actor_t::on_read(size_t bytes) noexcept { + LOG_TRACE(log, "{}, on_read, {} bytes", identity, bytes); + resources->release(resource::read); + auto buff = std::string_view(rx_buff.data(), bytes); + auto r = proto::relay::parse(buff); + auto wrapped = std::get_if(&r); + if (!wrapped) { + LOG_WARN(log, "{}, unexpected incoming relay data: {}", identity, spdlog::to_hex(buff.begin(), buff.end())); + auto ec = utils::make_error_code(utils::error_code_t::relay_failure); + return do_shutdown(make_error(ec)); + } + auto reply = std::get_if(&wrapped->message); + if (!reply) { + LOG_WARN(log, "{}, unexpected relay message: {}", identity, spdlog::to_hex(buff.begin(), buff.end())); + auto ec = utils::make_error_code(utils::error_code_t::relay_failure); + return do_shutdown(make_error(ec)); + } + if (reply->code) { + LOG_WARN(log, "{}, relay join failure({}): {}", identity, reply->code, reply->details); + auto ec = utils::make_error_code(utils::error_code_t::relay_failure); + return do_shutdown(make_error(ec)); + } + auto upgradeable = static_cast(transport.get()); + auto ssl = transport::ssl_junction_t{peer_device_id, &ssl_pair, false, "bep"}; + transport = upgradeable->upgrade(ssl, false); + initiate_handshake(); +} diff --git a/src/net/initiator_actor.h b/src/net/initiator_actor.h index bdc55347..9d2643e1 100644 --- a/src/net/initiator_actor.h +++ b/src/net/initiator_actor.h @@ -14,6 +14,7 @@ namespace r = rotor; struct initiator_actor_config_t : public r::actor_config_t { model::device_id_t peer_device_id; utils::uri_container_t uris; + std::string relay_session; const utils::key_pair_t *ssl_pair; std::optional sock; model::cluster_ptr_t cluster; @@ -37,6 +38,11 @@ template struct initiator_actor_config_builder_t : r::actor_con return std::move(*static_cast(this)); } + builder_t &&relay_session(const std::string &value) &&noexcept { + parent_t::config.relay_session = value; + return std::move(*static_cast(this)); + } + builder_t &&ssl_pair(const utils::key_pair_t *value) &&noexcept { parent_t::config.ssl_pair = value; return std::move(*static_cast(this)); @@ -82,20 +88,27 @@ struct initiator_actor_t : r::actor_base_t { private: using resolve_it_t = payload::address_response_t::resolve_results_t::iterator; + enum class role_t { active, passive, relay_passive }; void initiate_passive() noexcept; void initiate_active() noexcept; + void initiate_relay_passive() noexcept; void initiate(transport::stream_sp_t stream, const utils::URI &uri) noexcept; void initiate_handshake() noexcept; + void join_session() noexcept; + void resolve(const utils::URI &uri) noexcept; void on_resolve(message::resolve_response_t &res) noexcept; void on_connect(resolve_it_t) noexcept; void on_io_error(const sys::error_code &ec, r::plugin::resource_id_t resource) noexcept; void on_handshake(bool valid_peer, utils::x509_t &peer_cert, const tcp::endpoint &peer_endpoint, const model::device_id_t *peer_device) noexcept; + void on_read(size_t bytes) noexcept; + void on_write(size_t bytes) noexcept; model::device_id_t peer_device_id; utils::uri_container_t uris; + std::string relay_session; const utils::key_pair_t &ssl_pair; std::optional sock; model::cluster_ptr_t cluster; @@ -110,7 +123,8 @@ struct initiator_actor_t : r::actor_base_t { utils::logger_t log; tcp::endpoint remote_endpoint; bool connected = false; - bool active = false; + role_t role = role_t::passive; + std::string rx_buff; bool success = false; }; diff --git a/src/transport/base.h b/src/transport/base.h index 07a5a74e..262d4643 100644 --- a/src/transport/base.h +++ b/src/transport/base.h @@ -47,6 +47,7 @@ struct transport_config_t { utils::URI uri; ra::supervisor_asio_t &supervisor; std::optional sock; + bool active; }; struct stream_base_t; diff --git a/src/transport/http.cpp b/src/transport/http.cpp index a428918c..a85bbca0 100644 --- a/src/transport/http.cpp +++ b/src/transport/http.cpp @@ -6,8 +6,6 @@ namespace syncspirit::transport { -http_base_t::~http_base_t() {} - template struct http_impl_t : base_impl_t, interface_t, Sock, http_base_t> { using self_t = http_impl_t; using socket_t = Sock; diff --git a/src/transport/http.h b/src/transport/http.h index 06db1f24..e319ba5b 100644 --- a/src/transport/http.h +++ b/src/transport/http.h @@ -18,7 +18,7 @@ struct http_interface_t { }; struct SYNCSPIRIT_API http_base_t : model::arc_base_t, http_interface_t, stream_interface_t { - virtual ~http_base_t(); + virtual ~http_base_t() = default; }; using http_sp_t = model::intrusive_ptr_t; diff --git a/src/transport/impl.hpp b/src/transport/impl.hpp index 14028424..e5944f2c 100644 --- a/src/transport/impl.hpp +++ b/src/transport/impl.hpp @@ -66,6 +66,7 @@ template <> struct base_impl_t { strand_t &strand; tcp_socket_t sock; bool cancelling = false; + bool active; static tcp_socket_t mk_sock(transport_config_t &config, strand_t &strand) noexcept { if (config.sock) { @@ -78,7 +79,8 @@ template <> struct base_impl_t { } base_impl_t(transport_config_t &config) noexcept - : supervisor{config.supervisor}, strand{supervisor.get_strand()}, sock(mk_sock(config, strand)) {} + : supervisor{config.supervisor}, strand{supervisor.get_strand()}, + sock(mk_sock(config, strand)), active{config.active} {} tcp_socket_t &get_physical_layer() noexcept { return sock; } }; @@ -96,6 +98,7 @@ template <> struct base_impl_t { ssl_socket_t sock; bool validation_passed = false; bool cancelling = false; + bool active; static ssl::context get_context(self_t &source, std::string_view alpn) noexcept { ssl::context ctx(ssl::context::tls); @@ -135,7 +138,8 @@ template <> struct base_impl_t { base_impl_t(transport_config_t &config) noexcept : supervisor{config.supervisor}, strand{supervisor.get_strand()}, expected_peer{config.ssl_junction->peer}, me(config.ssl_junction->me), ctx(get_context(*this, config.ssl_junction->alpn)), - role(config.sock ? ssl::stream_base::server : ssl::stream_base::client), sock(mk_sock(config, ctx, strand)) { + role(!config.active ? ssl::stream_base::server : ssl::stream_base::client), + sock(mk_sock(config, ctx, strand)) { if (config.ssl_junction->sni_extension) { auto &host = config.uri.host; if (!SSL_set_tlsext_host_name(sock.native_handle(), host.c_str())) { diff --git a/src/transport/stream.cpp b/src/transport/stream.cpp index 5c694e8c..c43cb26a 100644 --- a/src/transport/stream.cpp +++ b/src/transport/stream.cpp @@ -6,38 +6,58 @@ namespace syncspirit::transport { -stream_base_t::~stream_base_t() {} - -template struct steam_impl_t : base_impl_t, interface_t, Sock, stream_base_t> { - using self_t = steam_impl_t; +template +struct generic_steam_impl_t : base_impl_t, + interface_t, Sock, upgradeable_stream_base_t> { + using self_t = generic_steam_impl_t; using socket_t = Sock; using parent_t = base_impl_t; using parent_t::parent_t; }; +struct ssl_stream_impl_t final : generic_steam_impl_t { + using parent_t = generic_steam_impl_t; + using parent_t::parent_t; + + stream_sp_t upgrade(ssl_junction_t &, bool) noexcept override { assert(0 && "should not happen"); } +}; + +struct tcp_stream_impl_t final : generic_steam_impl_t { + using parent_t = generic_steam_impl_t; + + tcp_stream_impl_t(transport_config_t &config) noexcept : parent_t(config), uri(config.uri) {} + + using parent_t::parent_t; + + stream_sp_t upgrade(ssl_junction_t &ssl, bool active_role) noexcept { + transport_config_t cfg{ssl_option_t(ssl), uri, supervisor, std::move(sock), active_role}; + return new ssl_stream_impl_t(cfg); + } + + utils::URI uri; +}; + stream_sp_t initiate_tls_passive(ra::supervisor_asio_t &sup, const utils::key_pair_t &my_keys, tcp::socket peer_sock) noexcept { ssl_junction_t ssl{{}, &my_keys, false, ""}; - transport_config_t cfg{ssl_option_t(ssl), {}, sup, std::move(peer_sock)}; - return new steam_impl_t(cfg); + transport_config_t cfg{ssl_option_t(ssl), {}, sup, std::move(peer_sock), false}; + return new ssl_stream_impl_t(cfg); } stream_sp_t initiate_tls_active(ra::supervisor_asio_t &sup, const utils::key_pair_t &my_keys, const model::device_id_t &expected_peer, const utils::URI &uri, bool sni, std::string_view alpn) noexcept { ssl_junction_t ssl{expected_peer, &my_keys, sni, alpn}; - transport_config_t cfg{ssl_option_t(ssl), uri, sup, {}}; - return new steam_impl_t(cfg); + transport_config_t cfg{ssl_option_t(ssl), uri, sup, {}, true}; + return new ssl_stream_impl_t(cfg); } stream_sp_t initiate_stream(transport_config_t &config) noexcept { assert(config.uri.proto == "tcp"); if (config.ssl_junction) { - using socket_t = ssl_socket_t; - return new steam_impl_t(config); + return new ssl_stream_impl_t(config); } else { - using socket_t = tcp_socket_t; - return new steam_impl_t(config); + return new tcp_stream_impl_t(config); } } diff --git a/src/transport/stream.h b/src/transport/stream.h index c53b9a3f..548f7236 100644 --- a/src/transport/stream.h +++ b/src/transport/stream.h @@ -17,7 +17,11 @@ struct stream_interface_t { }; struct stream_base_t : model::arc_base_t, stream_interface_t { - virtual ~stream_base_t(); + virtual ~stream_base_t() = default; +}; + +struct upgradeable_stream_base_t : stream_base_t { + virtual stream_sp_t upgrade(ssl_junction_t &ssl, bool active) noexcept = 0; }; stream_sp_t initiate_tls_active(ra::supervisor_asio_t &supervisor, const utils::key_pair_t &my_keys, diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp index de5ea29d..e11e8980 100644 --- a/tests/077-initiator.cpp +++ b/tests/077-initiator.cpp @@ -8,6 +8,7 @@ #include "net/names.h" #include "net/initiator_actor.h" #include "net/resolver_actor.h" +#include "proto/relay_support.h" #include "transport/stream.h" #include @@ -23,7 +24,7 @@ namespace ra = r::asio; using configure_callback_t = std::function; -auto timeout = r::pt::time_duration{r::pt::millisec{1500}}; +auto timeout = r::pt::time_duration{r::pt::millisec{2000}}; auto host = "127.0.0.1"; struct supervisor_t : ra::supervisor_asio_t { @@ -174,6 +175,7 @@ struct fixture_t { return sup->create_actor() .timeout(timeout) .peer_device_id(peer_device->device_id()) + .relay_session(relay_session) .uris({peer_uri}) .cluster(use_model ? cluster : nullptr) .sink(sup->get_address()) @@ -212,6 +214,7 @@ struct fixture_t { transport::stream_sp_t peer_trans; ready_ptr_t connected_message; diff_msgs_t diff_msgs; + std::string relay_session; bool use_model = true; bool valid_handshake = false; @@ -440,7 +443,6 @@ void test_passive_garbage() { void test_passive_timeout() { struct F : fixture_t { - actor_ptr_t act; void accept(const sys::error_code &ec) noexcept override { @@ -460,6 +462,70 @@ void test_passive_timeout() { F().run(); } +void test_relay_passive_success() { + struct F : fixture_t { + + F() { rx_buff.resize(128); } + + void on_read(size_t bytes) noexcept { + LOG_TRACE(log, "read (relay/passive), {} bytes", bytes); + auto r = proto::relay::parse({rx_buff.data(), bytes}); + auto &wrapped = std::get(r); + auto &msg = std::get(wrapped.message); + CHECK(msg.key == relay_session); + relay_reply(); + } + + void on_write(size_t bytes) noexcept { + LOG_TRACE(log, "write (relay/passive), {} bytes", bytes); + + auto upgradeable = static_cast(peer_trans.get()); + auto ssl = transport::ssl_junction_t{my_device->device_id(), &peer_keys, false, "bep"}; + peer_trans = upgradeable->upgrade(ssl, true); + initiate_peer_handshake(); + } + + virtual void relay_reply() noexcept { write(proto::relay::response_t{0, "success"}); } + + void write(const proto::relay::message_t &msg) noexcept { + proto::relay::serialize(msg, rx_buff); + auto buff = asio::buffer(rx_buff); + transport::error_fn_t err_fn([&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); + transport::io_fn_t write_fn = [this](size_t bytes) { on_write(bytes); }; + peer_trans->async_send(asio::buffer(rx_buff), write_fn, err_fn); + } + + void accept(const sys::error_code &ec) noexcept override { + LOG_INFO(log, "accept (relay/passive), ec: {}", ec.message()); + auto uri = utils::parse("tcp://127.0.0.1:0/").value(); + auto cfg = transport::transport_config_t{{}, uri, *sup, std::move(peer_sock)}; + peer_trans = transport::initiate_stream(cfg); + + transport::error_fn_t read_err_fn( + [&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); + transport::io_fn_t read_fn = [this](size_t bytes) { on_read(bytes); }; + peer_trans->async_recv(asio::buffer(rx_buff), read_fn, read_err_fn); + } + + void main() noexcept override { + relay_session = "relay-session-key"; + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::OPERATIONAL); + CHECK(connected_message); + CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); + CHECK(valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + REQUIRE(diff_msgs.size() == 0); + } + + std::string rx_buff; + }; + F().run(); +} + REGISTER_TEST_CASE(test_connect_unsupproted_proto, "test_connect_unsupproted_proto", "[initiator]"); REGISTER_TEST_CASE(test_connect_timeout, "test_connect_timeout", "[initiator]"); REGISTER_TEST_CASE(test_handshake_timeout, "test_handshake_timeout", "[initiator]"); @@ -472,3 +538,4 @@ REGISTER_TEST_CASE(test_success_no_model, "test_success_no_model", "[initiator]" REGISTER_TEST_CASE(test_passive_success, "test_passive_success", "[initiator]"); REGISTER_TEST_CASE(test_passive_garbage, "test_passive_garbage", "[initiator]"); REGISTER_TEST_CASE(test_passive_timeout, "test_passive_timeout", "[initiator]"); +REGISTER_TEST_CASE(test_relay_passive_success, "test_relay_passive_success", "[initiator]"); diff --git a/tests/078-relay.cpp b/tests/078-relay.cpp index a95f148d..f8e30d2c 100644 --- a/tests/078-relay.cpp +++ b/tests/078-relay.cpp @@ -171,7 +171,7 @@ struct fixture_t : private model::diff::contact_visitor_t { virtual void accept(const sys::error_code &ec) noexcept { LOG_INFO(log, "accept (relay), ec: {}, sock = {}", ec.message(), peer_sock.native_handle()); auto uri = utils::parse("tcp://127.0.0.1:0/").value(); - auto cfg = transport::transport_config_t{{}, uri, *sup, std::move(peer_sock)}; + auto cfg = transport::transport_config_t{{}, uri, *sup, std::move(peer_sock), false}; relay_trans = transport::initiate_stream(cfg); relay_read(); } @@ -188,7 +188,7 @@ struct fixture_t : private model::diff::contact_visitor_t { virtual void on(net::message::connect_request_t &req) noexcept { auto &uri = req.payload.request_payload.uri; log->info("requested connect to {}", uri.full); - auto cfg = transport::transport_config_t{{}, uri, *sup, {}}; + auto cfg = transport::transport_config_t{{}, uri, *sup, {}, true}; tcp::resolver resolver(io_ctx); auto addresses = resolver.resolve(host, std::to_string(uri.port)); auto addresses_ptr = std::make_shared(addresses); From 39033b289ae9d7c719b0f93f71cca86d48775af2 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 8 May 2022 22:32:54 +0300 Subject: [PATCH 18/31] initiator_actor, moar safe shutdonw --- lib/rotor | 2 +- src/net/initiator_actor.cpp | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/rotor b/lib/rotor index 757d207e..0396d3e8 160000 --- a/lib/rotor +++ b/lib/rotor @@ -1 +1 @@ -Subproject commit 757d207ea737024144e171d04d4e59353b58f1d3 +Subproject commit 0396d3e857f1dcf21d82abcedeaffdf3ec6e3b0c diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 66fe4dee..4a64afe1 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -144,10 +144,9 @@ void initiator_actor_t::shutdown_start() noexcept { if (resources->has(resource::initializing)) { resources->release(resource::initializing); } - if (resources->has(resource::connect)) { - transport->cancel(); - } - if (resources->has(resource::handshake)) { + bool cancel_transport = resources->has(resource::connect) || resources->has(resource::handshake) || + resources->has(resource::read) || resources->has(resource::write); + if (cancel_transport) { transport->cancel(); } r::actor_base_t::shutdown_start(); From 22ed32d3f74c879d20bf6942d7578251a0712040 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 8 May 2022 23:06:33 +0300 Subject: [PATCH 19/31] initiator_actor, WIP on passive relays (tests) --- src/net/initiator_actor.cpp | 2 +- tests/077-initiator.cpp | 149 ++++++++++++++++++++++++++++-------- 2 files changed, 116 insertions(+), 35 deletions(-) diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 4a64afe1..e9ebcf51 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -154,7 +154,7 @@ void initiator_actor_t::shutdown_start() noexcept { void initiator_actor_t::shutdown_finish() noexcept { LOG_TRACE(log, "{}, shutdown_finish", identity); - bool notify_offline = (role == role_t::active || role == role_t::relay_passive) && !success && cluster; + bool notify_offline = role == role_t::active && !success && cluster; if (notify_offline) { auto diff = model::diff::cluster_diff_ptr_t(); auto state = model::device_state_t::offline; diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp index e11e8980..c625394c 100644 --- a/tests/077-initiator.cpp +++ b/tests/077-initiator.cpp @@ -462,66 +462,144 @@ void test_passive_timeout() { F().run(); } -void test_relay_passive_success() { - struct F : fixture_t { - - F() { rx_buff.resize(128); } +struct passive_relay_fixture_t: fixture_t { + std::string rx_buff; + bool initiate_handshake = true; + passive_relay_fixture_t() { + relay_session = "relay-session-key"; + rx_buff.resize(128); + } - void on_read(size_t bytes) noexcept { - LOG_TRACE(log, "read (relay/passive), {} bytes", bytes); - auto r = proto::relay::parse({rx_buff.data(), bytes}); - auto &wrapped = std::get(r); - auto &msg = std::get(wrapped.message); - CHECK(msg.key == relay_session); - relay_reply(); - } + void on_read(size_t bytes) noexcept { + LOG_TRACE(log, "read (relay/passive), {} bytes", bytes); + auto r = proto::relay::parse({rx_buff.data(), bytes}); + auto &wrapped = std::get(r); + auto &msg = std::get(wrapped.message); + CHECK(msg.key == relay_session); + relay_reply(); + } - void on_write(size_t bytes) noexcept { - LOG_TRACE(log, "write (relay/passive), {} bytes", bytes); + virtual void on_write(size_t bytes) noexcept { + LOG_TRACE(log, "write (relay/passive), {} bytes", bytes); + if (initiate_handshake) { auto upgradeable = static_cast(peer_trans.get()); auto ssl = transport::ssl_junction_t{my_device->device_id(), &peer_keys, false, "bep"}; peer_trans = upgradeable->upgrade(ssl, true); initiate_peer_handshake(); } + } + + virtual void relay_reply() noexcept { write(proto::relay::response_t{0, "success"}); } + + virtual void write(const proto::relay::message_t &msg) noexcept { + proto::relay::serialize(msg, rx_buff); + auto buff = asio::buffer(rx_buff); + transport::error_fn_t err_fn([&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); + transport::io_fn_t write_fn = [this](size_t bytes) { on_write(bytes); }; + peer_trans->async_send(asio::buffer(rx_buff), write_fn, err_fn); + } - virtual void relay_reply() noexcept { write(proto::relay::response_t{0, "success"}); } + void accept(const sys::error_code &ec) noexcept override { + LOG_INFO(log, "accept (relay/passive), ec: {}", ec.message()); + auto uri = utils::parse("tcp://127.0.0.1:0/").value(); + auto cfg = transport::transport_config_t{{}, uri, *sup, std::move(peer_sock)}; + peer_trans = transport::initiate_stream(cfg); - void write(const proto::relay::message_t &msg) noexcept { - proto::relay::serialize(msg, rx_buff); + transport::error_fn_t read_err_fn( + [&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); + transport::io_fn_t read_fn = [this](size_t bytes) { on_read(bytes); }; + peer_trans->async_recv(asio::buffer(rx_buff), read_fn, read_err_fn); + } +}; + +void test_relay_passive_success() { + struct F : passive_relay_fixture_t { + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::OPERATIONAL); + CHECK(connected_message); + CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); + CHECK(valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 0); + } + + }; + F().run(); +} + +void test_relay_passive_gargabe() { + struct F : passive_relay_fixture_t { + + void write(const proto::relay::message_t &) noexcept override { + rx_buff = "garbage-garbage-garbae"; + initiate_handshake = false; auto buff = asio::buffer(rx_buff); transport::error_fn_t err_fn([&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); transport::io_fn_t write_fn = [this](size_t bytes) { on_write(bytes); }; peer_trans->async_send(asio::buffer(rx_buff), write_fn, err_fn); } - void accept(const sys::error_code &ec) noexcept override { - LOG_INFO(log, "accept (relay/passive), ec: {}", ec.message()); - auto uri = utils::parse("tcp://127.0.0.1:0/").value(); - auto cfg = transport::transport_config_t{{}, uri, *sup, std::move(peer_sock)}; - peer_trans = transport::initiate_stream(cfg); - - transport::error_fn_t read_err_fn( - [&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); - transport::io_fn_t read_fn = [this](size_t bytes) { on_read(bytes); }; - peer_trans->async_recv(asio::buffer(rx_buff), read_fn, read_err_fn); + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + CHECK(!valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 0); } + }; + F().run(); +} + +void test_relay_passive_wrong_message() { + struct F : passive_relay_fixture_t { + + void relay_reply() noexcept override { write(proto::relay::pong_t{}); } + void main() noexcept override { - relay_session = "relay-session-key"; + initiate_handshake = false; auto act = create_actor(); io_ctx.run(); - CHECK(sup->get_state() == r::state_t::OPERATIONAL); - CHECK(connected_message); - CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); - CHECK(valid_handshake); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + CHECK(!valid_handshake); sup->do_shutdown(); sup->do_process(); CHECK(sup->get_state() == r::state_t::SHUT_DOWN); - REQUIRE(diff_msgs.size() == 0); + CHECK(diff_msgs.size() == 0); + } + + }; + F().run(); +} + +void test_relay_passive_unsuccessful_join() { + struct F : passive_relay_fixture_t { + + void relay_reply() noexcept override { write(proto::relay::response_t{5, "some-fail-reason"}); } + + void main() noexcept override { + initiate_handshake = false; + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + CHECK(!valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 0); } - std::string rx_buff; }; F().run(); } @@ -539,3 +617,6 @@ REGISTER_TEST_CASE(test_passive_success, "test_passive_success", "[initiator]"); REGISTER_TEST_CASE(test_passive_garbage, "test_passive_garbage", "[initiator]"); REGISTER_TEST_CASE(test_passive_timeout, "test_passive_timeout", "[initiator]"); REGISTER_TEST_CASE(test_relay_passive_success, "test_relay_passive_success", "[initiator]"); +REGISTER_TEST_CASE(test_relay_passive_gargabe, "test_relay_passive_gargabe", "[initiator]"); +REGISTER_TEST_CASE(test_relay_passive_wrong_message, "test_relay_passive_wrong_message", "[initiator]"); +REGISTER_TEST_CASE(test_relay_passive_unsuccessful_join, "test_relay_passive_unsuccessful_join", "[initiator]"); From b127e1aabf983914653e6e1cb3cfe81e1ba3b8f3 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Mon, 9 May 2022 10:32:06 +0300 Subject: [PATCH 20/31] WIP --- src/net/initiator_actor.cpp | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index e9ebcf51..d5c7bf19 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -6,6 +6,7 @@ #include "utils/error_code.h" #include "model/diff/peer/peer_state.h" #include +#include #include #include @@ -25,7 +26,7 @@ r::plugin::resource_id_t write = 5; static constexpr size_t BUFF_SZ = 256; initiator_actor_t::initiator_actor_t(config_t &cfg) - : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, uris{cfg.uris}, + : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, relay_session(std::move(cfg.relay_session)), ssl_pair{*cfg.ssl_pair}, sock(std::move(cfg.sock)), cluster{std::move(cfg.cluster)}, sink(std::move(cfg.sink)), custom(std::move(cfg.custom)), router{*cfg.router} { @@ -39,6 +40,23 @@ initiator_actor_t::initiator_actor_t(config_t &cfg) role = role_t::active; } } + for (auto &uri : cfg.uris) { + if (uri.proto != "tcp" && uri.proto != "relay") { + LOG_DEBUG(log, "{}, unsupported proto '{}' for the url '{}'", identity, uri.proto, uri.full); + } else { + uris.emplace_back(std::move(uri)); + } + } + auto comparator = [](const utils::URI &a, const utils::URI &b) noexcept -> bool { + if (a.proto == b.proto) { + return std::lexicographical_compare(a.full.begin(), a.full.end(), b.full.begin(), b.full.end()); + } + if (a.proto == "relay") { + return true; + } + return false; + }; + std::sort(uris.begin(), uris.end(), comparator); } void initiator_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { @@ -56,7 +74,7 @@ void initiator_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { break; } case role_t::relay_passive: { - value = fmt::format("init/relay:{}", peer_device_id.get_short()); + value = fmt::format("init/relay-passive:{}", peer_device_id.get_short()); break; } } @@ -93,14 +111,10 @@ void initiator_actor_t::initiate_active() noexcept { while (uri_idx < uris.size()) { auto &uri = uris[uri_idx++]; - if (uri.proto == "tcp") { - auto sup = static_cast(&router); - auto trans = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri); - initiate(std::move(trans), uri); - return; - } else { - LOG_DEBUG(log, "{}, unsupported proto '{}' for the url '{}'", identity, uri.proto, uri.full); - } + auto sup = static_cast(&router); + auto trans = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri); + initiate(std::move(trans), uri); + return; } LOG_TRACE(log, "{}, try_next_uri, no way to connect found, shut down", identity); From b902563c2178716279bdfe1ff5c307bb9d6ef413 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Tue, 10 May 2022 22:25:25 +0300 Subject: [PATCH 21/31] initiator_actor, implement active relaying --- .../diff/modify/relay_connect_request.cpp | 17 + src/model/diff/modify/relay_connect_request.h | 28 ++ src/net/initiator_actor.cpp | 160 +++++++-- src/net/initiator_actor.h | 13 +- src/net/peer_supervisor.cpp | 4 + src/proto/relay_support.cpp | 22 ++ src/proto/relay_support.h | 2 + src/transport/impl.hpp | 3 +- tests/016-relay-support.cpp | 4 + tests/077-initiator.cpp | 306 +++++++++++++++++- 10 files changed, 510 insertions(+), 49 deletions(-) create mode 100644 src/model/diff/modify/relay_connect_request.cpp create mode 100644 src/model/diff/modify/relay_connect_request.h diff --git a/src/model/diff/modify/relay_connect_request.cpp b/src/model/diff/modify/relay_connect_request.cpp new file mode 100644 index 00000000..03385658 --- /dev/null +++ b/src/model/diff/modify/relay_connect_request.cpp @@ -0,0 +1,17 @@ +#include "relay_connect_request.h" +#include "../contact_visitor.h" + +using namespace syncspirit::model::diff::modify; + +relay_connect_request_t::relay_connect_request_t(model::device_id_t peer_, std::string session_key_, + tcp::endpoint relay_) noexcept + : peer{std::move(peer_)}, session_key{std::move(session_key_)}, relay{std::move(relay_)} {} + +auto relay_connect_request_t::apply_impl(cluster_t &) const noexcept -> outcome::result { + return outcome::success(); +} + +auto relay_connect_request_t::visit(contact_visitor_t &visitor) const noexcept -> outcome::result { + LOG_TRACE(log, "visiting relay_connect_request_t"); + return visitor(*this); +} diff --git a/src/model/diff/modify/relay_connect_request.h b/src/model/diff/modify/relay_connect_request.h new file mode 100644 index 00000000..313c21b6 --- /dev/null +++ b/src/model/diff/modify/relay_connect_request.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2019-2022 Ivan Baidakou + +#pragma once + +#include +#include +#include "../contact_diff.h" +#include "model/cluster.h" +#include "model/device_id.h" + +namespace syncspirit::model::diff::modify { + +namespace asio = boost::asio; +using tcp = asio::ip::tcp; + +struct SYNCSPIRIT_API relay_connect_request_t final : contact_diff_t { + relay_connect_request_t(model::device_id_t peer, std::string session_key, tcp::endpoint relay) noexcept; + + outcome::result apply_impl(cluster_t &) const noexcept override; + outcome::result visit(contact_visitor_t &) const noexcept override; + + model::device_id_t peer; + std::string session_key; + tcp::endpoint relay; +}; + +} // namespace syncspirit::model::diff::modify diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index d5c7bf19..8023d9a2 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -27,19 +27,10 @@ static constexpr size_t BUFF_SZ = 256; initiator_actor_t::initiator_actor_t(config_t &cfg) : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, - relay_session(std::move(cfg.relay_session)), ssl_pair{*cfg.ssl_pair}, + relay_key(std::move(cfg.relay_session)), ssl_pair{*cfg.ssl_pair}, sock(std::move(cfg.sock)), cluster{std::move(cfg.cluster)}, sink(std::move(cfg.sink)), custom(std::move(cfg.custom)), router{*cfg.router} { log = utils::get_logger("net.initator"); - if (sock.has_value()) { - role = role_t::passive; - } else { - if (!relay_session.empty()) { - role = role_t::relay_passive; - } else { - role = role_t::active; - } - } for (auto &uri : cfg.uris) { if (uri.proto != "tcp" && uri.proto != "relay") { LOG_DEBUG(log, "{}, unsupported proto '{}' for the url '{}'", identity, uri.proto, uri.full); @@ -57,6 +48,16 @@ initiator_actor_t::initiator_actor_t(config_t &cfg) return false; }; std::sort(uris.begin(), uris.end(), comparator); + + if (sock.has_value()) { + role = role_t::passive; + } else { + if (!relay_key.empty()) { + role = role_t::relay_passive; + } else { + role = role_t::active; + } + } } void initiator_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { @@ -111,9 +112,11 @@ void initiator_actor_t::initiate_active() noexcept { while (uri_idx < uris.size()) { auto &uri = uris[uri_idx++]; - auto sup = static_cast(&router); - auto trans = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri); - initiate(std::move(trans), uri); + if (uri.proto == "tcp") { + initiate_active_tls(uri); + } else if (uri.proto == "relay") { + initiate_active_relay(uri); + } return; } @@ -187,8 +190,24 @@ void initiator_actor_t::resolve(const utils::URI &uri) noexcept { resources->acquire(resource::resolving); } -void initiator_actor_t::initiate(transport::stream_sp_t stream, const utils::URI &uri) noexcept { - transport = std::move(stream); +void initiator_actor_t::initiate_active_tls(const utils::URI &uri) noexcept { + auto sup = static_cast(&router); + transport = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri); + active_uri = &uri; + relaying = false; + resolve(uri); +} + +void initiator_actor_t::initiate_active_relay(const utils::URI &uri) noexcept { + auto relay_device = proto::relay::parse_device(uri); + if (!relay_device) { + LOG_WARN(log, "{}, relay url '{}' does not contains valid device_id", identity, uri.full); + return initiate_active(); + } + active_uri = &uri; + relaying = true; + auto sup = static_cast(&router); + transport = transport::initiate_tls_active(*sup, ssl_pair, relay_device.value(), *active_uri); resolve(uri); } @@ -202,7 +221,11 @@ void initiator_actor_t::on_resolve(message::resolve_response_t &res) noexcept { auto &ee = res.payload.ee; if (ee) { LOG_WARN(log, "{}, on_resolve error : {}", identity, ee->message()); - return initiate_active(); + if (role == role_t::active) { + return initiate_active(); + } else { + return do_shutdown(ee); + } } auto &addresses = res.payload.res->results; @@ -230,17 +253,24 @@ void initiator_actor_t::on_io_error(const sys::error_code &ec, r::plugin::resour } void initiator_actor_t::on_connect(resolve_it_t) noexcept { - LOG_TRACE(log, "{}, on_connect, device_id = {}", identity, peer_device_id.get_short()); + LOG_TRACE(log, "{}, on_connect, device_id = {}, transport = {}", identity, peer_device_id.get_short(), + (void *)transport.get()); resources->release(resource::connect); - if (role == role_t::active) { + // auto do_handshake = role == role_t::active; + auto do_handshake = (role == role_t::active) && + (active_uri && (active_uri->proto == "relay" && relaying) || active_uri->proto == "tcp"); + if (do_handshake) { initiate_handshake(); } else { - assert(role == role_t::relay_passive); join_session(); } } void initiator_actor_t::initiate_handshake() noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + LOG_TRACE(log, "{}, initializing handshake", identity); connected = true; transport::handshake_fn_t handshake_fn([&](auto &&...args) { on_handshake(args...); }); @@ -250,17 +280,22 @@ void initiator_actor_t::initiate_handshake() noexcept { } void initiator_actor_t::join_session() noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + transport::error_fn_t read_err_fn([&](auto arg) { on_io_error(arg, resource::read); }); - transport::io_fn_t read_fn = [this](size_t bytes) { on_read(bytes); }; + transport::io_fn_t read_fn = [this](size_t bytes) { on_read_relay(bytes); }; rx_buff.resize(BUFF_SZ); transport->async_recv(asio::buffer(rx_buff), read_fn, read_err_fn); resources->acquire(resource::read); - auto msg = proto::relay::join_session_request_t{std::move(relay_session)}; - proto::relay::serialize(msg, relay_session); + LOG_TRACE(log, "{}, join_session", identity); + auto msg = proto::relay::join_session_request_t{std::move(relay_key)}; + proto::relay::serialize(msg, relay_tx); transport::error_fn_t write_err_fn([&](auto arg) { on_io_error(arg, resource::write); }); transport::io_fn_t write_fn = [this](size_t bytes) { on_write(bytes); }; - transport->async_send(asio::buffer(relay_session), write_fn, write_err_fn); + transport->async_send(asio::buffer(relay_tx), write_fn, write_err_fn); resources->acquire(resource::write); } @@ -280,9 +315,13 @@ void initiator_actor_t::on_handshake(bool valid_peer, utils::x509_t &cert, const return do_shutdown(make_error(ec)); } LOG_TRACE(log, "{}, on_handshake, valid = {}, issued by {}", identity, valid_peer, cert_name.value()); - peer_device_id = *peer_device; - remote_endpoint = peer_endpoint; - resources->release(resource::initializing); + if (relaying) { + request_relay_connection(); + } else { + peer_device_id = *peer_device; + remote_endpoint = peer_endpoint; + resources->release(resource::initializing); + } } void initiator_actor_t::on_write(size_t bytes) noexcept { @@ -290,9 +329,13 @@ void initiator_actor_t::on_write(size_t bytes) noexcept { resources->release(resource::write); } -void initiator_actor_t::on_read(size_t bytes) noexcept { - LOG_TRACE(log, "{}, on_read, {} bytes", identity, bytes); +void initiator_actor_t::on_read_relay(size_t bytes) noexcept { + LOG_TRACE(log, "{}, on_read_relay, {} bytes", identity, bytes); resources->release(resource::read); + if (state > r::state_t::OPERATIONAL) { + return; + } + auto buff = std::string_view(rx_buff.data(), bytes); auto r = proto::relay::parse(buff); auto wrapped = std::get_if(&r); @@ -314,6 +357,65 @@ void initiator_actor_t::on_read(size_t bytes) noexcept { } auto upgradeable = static_cast(transport.get()); auto ssl = transport::ssl_junction_t{peer_device_id, &ssl_pair, false, "bep"}; - transport = upgradeable->upgrade(ssl, false); + auto active = role == role_t::active; + transport = upgradeable->upgrade(ssl, active); initiate_handshake(); } + +void initiator_actor_t::request_relay_connection() noexcept { + if (state > r::state_t::OPERATIONAL) { + return; + } + + transport::error_fn_t read_err_fn([&](auto arg) { on_io_error(arg, resource::read); }); + transport::io_fn_t read_fn = [this](size_t bytes) { on_read_relay_active(bytes); }; + rx_buff.resize(BUFF_SZ); + transport->async_recv(asio::buffer(rx_buff), read_fn, read_err_fn); + resources->acquire(resource::read); + + auto msg = proto::relay::connect_request_t{std::string(peer_device_id.get_sha256())}; + proto::relay::serialize(msg, relay_tx); + transport::error_fn_t write_err_fn([&](auto arg) { on_io_error(arg, resource::write); }); + transport::io_fn_t write_fn = [this](size_t bytes) { on_write(bytes); }; + transport->async_send(asio::buffer(relay_tx), write_fn, write_err_fn); + resources->acquire(resource::write); +} + +void initiator_actor_t::on_read_relay_active(size_t bytes) noexcept { + LOG_TRACE(log, "{}, on_read_relay_active, {} bytes", identity, bytes); + resources->release(resource::read); + auto buff = std::string_view(rx_buff.data(), bytes); + auto r = proto::relay::parse(buff); + auto wrapped = std::get_if(&r); + if (!wrapped) { + LOG_WARN(log, "{}, unexpected incoming relay data: {}", identity, spdlog::to_hex(buff.begin(), buff.end())); + auto ec = utils::make_error_code(utils::error_code_t::relay_failure); + return do_shutdown(make_error(ec)); + } + auto inv = std::get_if(&wrapped->message); + if (!inv) { + LOG_WARN(log, "{}, unexpected relay message: {}", identity, spdlog::to_hex(buff.begin(), buff.end())); + auto ec = utils::make_error_code(utils::error_code_t::relay_failure); + return do_shutdown(make_error(ec)); + } + auto &peer = inv->from; + if (peer != peer_device_id.get_sha256()) { + LOG_WARN(log, "{}, unexpected peer device: {}", identity, spdlog::to_hex(peer.begin(), peer.end())); + auto ec = utils::make_error_code(utils::error_code_t::relay_failure); + return do_shutdown(make_error(ec)); + } + auto &addr = inv->address; + relay_key = inv->key; + auto ip = !addr.empty() ? &addr : &active_uri->host; + auto uri_str = fmt::format("tcp://{}:{}", *ip, inv->port); + LOG_DEBUG(log, "{}, going to connect to {}, using key: {}", identity, uri_str, + spdlog::to_hex(relay_key.begin(), relay_key.end())); + auto uri_opt = utils::parse(uri_str); + auto &uri = uri_opt.value(); + relaying = false; + + auto sup = static_cast(&router); + transport::transport_config_t cfg{{}, uri, *sup, {}, true}; + transport = transport::initiate_stream(cfg); + resolve(uri); +} diff --git a/src/net/initiator_actor.h b/src/net/initiator_actor.h index 9d2643e1..c5ad771d 100644 --- a/src/net/initiator_actor.h +++ b/src/net/initiator_actor.h @@ -93,9 +93,11 @@ struct initiator_actor_t : r::actor_base_t { void initiate_passive() noexcept; void initiate_active() noexcept; void initiate_relay_passive() noexcept; - void initiate(transport::stream_sp_t stream, const utils::URI &uri) noexcept; + void initiate_active_tls(const utils::URI &uri) noexcept; + void initiate_active_relay(const utils::URI &uri) noexcept; void initiate_handshake() noexcept; void join_session() noexcept; + void request_relay_connection() noexcept; void resolve(const utils::URI &uri) noexcept; void on_resolve(message::resolve_response_t &res) noexcept; @@ -103,12 +105,15 @@ struct initiator_actor_t : r::actor_base_t { void on_io_error(const sys::error_code &ec, r::plugin::resource_id_t resource) noexcept; void on_handshake(bool valid_peer, utils::x509_t &peer_cert, const tcp::endpoint &peer_endpoint, const model::device_id_t *peer_device) noexcept; - void on_read(size_t bytes) noexcept; + void on_read_relay(size_t bytes) noexcept; + void on_read_relay_active(size_t bytes) noexcept; void on_write(size_t bytes) noexcept; model::device_id_t peer_device_id; utils::uri_container_t uris; - std::string relay_session; + std::string relay_rx; + std::string relay_tx; + std::string relay_key; const utils::key_pair_t &ssl_pair; std::optional sock; model::cluster_ptr_t cluster; @@ -116,6 +121,7 @@ struct initiator_actor_t : r::actor_base_t { r::message_ptr_t custom; r::supervisor_t &router; + const utils::URI *active_uri = nullptr; transport::stream_sp_t transport; r::address_ptr_t resolver; r::address_ptr_t coordinator; @@ -126,6 +132,7 @@ struct initiator_actor_t : r::actor_base_t { role_t role = role_t::passive; std::string rx_buff; bool success = false; + bool relaying = false; }; namespace payload { diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index f46c9e38..24ba3ca8 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -134,6 +134,8 @@ auto peer_supervisor_t::operator()(const model::diff::modify::connect_request_t auto timeout = r::pt::milliseconds{bep_config.connect_timeout}; create_actor() .cluster(cluster) + .router(*locality_leader) + .sink(address) .ssl_pair(&ssl_pair) .sock(std::move(sock)) .timeout(timeout) @@ -151,6 +153,8 @@ auto peer_supervisor_t::operator()(const model::diff::modify::update_contact_t & auto connect_timeout = r::pt::milliseconds{bep_config.connect_timeout}; LOG_DEBUG(log, "{} initiating connection with {}", identity, peer->device_id()); create_actor() + .router(*locality_leader) + .sink(address) .ssl_pair(&ssl_pair) .peer_device_id(diff.device) .uris(uris) diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp index b72108f3..4653620b 100644 --- a/src/proto/relay_support.cpp +++ b/src/proto/relay_support.cpp @@ -317,6 +317,28 @@ parse_result_t parse(std::string_view data) noexcept { } } +std::optional parse_device(const utils::URI &uri) noexcept { + if (uri.proto != "relay") { + return {}; + } + auto device_id_str = std::string{}; + auto q = uri.decompose_query(); + for (auto &pair : q) { + if (pair.first == "id") { + device_id_str = std::move(pair.second); + break; + } + } + if (device_id_str.empty()) { + return {}; + } + auto device_opt = model::device_id_t::from_string(device_id_str); + if (!device_opt) { + return {}; + } + return std::move(device_opt.value()); +} + outcome::result parse_endpoint(std::string_view buff) noexcept { using namespace syncspirit::utils; auto data = json::parse(buff.begin(), buff.end(), nullptr, false); diff --git a/src/proto/relay_support.h b/src/proto/relay_support.h index 7736ce48..eb04b2f5 100644 --- a/src/proto/relay_support.h +++ b/src/proto/relay_support.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include "utils/uri.h" @@ -83,6 +84,7 @@ struct relay_info_t : model::arc_base_t { using relay_info_ptr_t = model::intrusive_ptr_t; using relay_infos_t = std::vector; +SYNCSPIRIT_API std::optional parse_device(const utils::URI &uri) noexcept; SYNCSPIRIT_API outcome::result parse_endpoint(std::string_view data) noexcept; } // namespace syncspirit::proto::relay diff --git a/src/transport/impl.hpp b/src/transport/impl.hpp index e5944f2c..0641fb15 100644 --- a/src/transport/impl.hpp +++ b/src/transport/impl.hpp @@ -179,7 +179,8 @@ template <> struct base_impl_t { if (role == ssl::stream_base::handshake_type::client) { if (actual_peer != expected_peer) { - spdlog::warn("unexcpected peer device_id. Got: {}, expected: {}", actual_peer, expected_peer); + spdlog::warn("unexcpected peer device_id. Got: {}, expected: {}", actual_peer.get_value(), + expected_peer.get_value()); return false; } } diff --git a/tests/016-relay-support.cpp b/tests/016-relay-support.cpp index c5d1f2f7..1e8b3550 100644 --- a/tests/016-relay-support.cpp +++ b/tests/016-relay-support.cpp @@ -240,4 +240,8 @@ TEST_CASE("endpoing parsing", "[relay]") { CHECK(l.country == "DE"); CHECK(l.continent == "EU"); CHECK(relay->ping_interval == pt::seconds{90}); + + auto device = parse_device(relay->uri); + REQUIRE(device); + CHECK(device.value() == relay->device_id); } diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp index c625394c..773ab868 100644 --- a/tests/077-initiator.cpp +++ b/tests/077-initiator.cpp @@ -89,6 +89,15 @@ struct fixture_t { sup->create_actor().resolve_timeout(timeout / 2).timeout(timeout).finish(); sup->do_process(); + my_keys = utils::generate_pair("me").value(); + peer_keys = utils::generate_pair("peer").value(); + + auto md = model::device_id_t::from_cert(my_keys.cert_data).value(); + auto pd = model::device_id_t::from_cert(peer_keys.cert_data).value(); + + my_device = device_t::create(md, "my-device").value(); + peer_device = device_t::create(pd, "peer-device").value(); + auto ep = asio::ip::tcp::endpoint(asio::ip::make_address(host), 0); acceptor.open(ep.protocol()); acceptor.bind(ep); @@ -98,14 +107,6 @@ struct fixture_t { log->debug("listening on {}", peer_uri.full); initiate_accept(); - my_keys = utils::generate_pair("me").value(); - peer_keys = utils::generate_pair("peer").value(); - - auto md = model::device_id_t::from_cert(my_keys.cert_data).value(); - auto pd = model::device_id_t::from_cert(peer_keys.cert_data).value(); - - my_device = device_t::create(md, "my-device").value(); - peer_device = device_t::create(pd, "peer-device").value(); cluster = new cluster_t(my_device, 1); cluster->get_devices().put(my_device); @@ -133,13 +134,15 @@ struct fixture_t { transport::handshake_fn_t handshake_fn = [this](bool valid_peer, utils::x509_t &cert, const tcp::endpoint &peer_endpoint, const model::device_id_t *peer_device) { - valid_handshake = true; - LOG_INFO(log, "peer handshake"); + valid_handshake = valid_peer; + on_peer_hanshake(); }; transport::error_fn_t on_error = [](const auto &) {}; peer_trans->async_handshake(handshake_fn, on_error); } + virtual void on_peer_hanshake() noexcept { LOG_INFO(log, "peer handshake"); } + void initiate_active() noexcept { tcp::resolver resolver(io_ctx); auto addresses = resolver.resolve(host, std::to_string(listening_ep.port())); @@ -462,7 +465,7 @@ void test_passive_timeout() { F().run(); } -struct passive_relay_fixture_t: fixture_t { +struct passive_relay_fixture_t : fixture_t { std::string rx_buff; bool initiate_handshake = true; passive_relay_fixture_t() { @@ -506,8 +509,7 @@ struct passive_relay_fixture_t: fixture_t { auto cfg = transport::transport_config_t{{}, uri, *sup, std::move(peer_sock)}; peer_trans = transport::initiate_stream(cfg); - transport::error_fn_t read_err_fn( - [&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); + transport::error_fn_t read_err_fn([&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); transport::io_fn_t read_fn = [this](size_t bytes) { on_read(bytes); }; peer_trans->async_recv(asio::buffer(rx_buff), read_fn, read_err_fn); } @@ -527,7 +529,6 @@ void test_relay_passive_success() { CHECK(sup->get_state() == r::state_t::SHUT_DOWN); CHECK(diff_msgs.size() == 0); } - }; F().run(); } @@ -555,7 +556,6 @@ void test_relay_passive_gargabe() { CHECK(sup->get_state() == r::state_t::SHUT_DOWN); CHECK(diff_msgs.size() == 0); } - }; F().run(); } @@ -577,7 +577,6 @@ void test_relay_passive_wrong_message() { CHECK(sup->get_state() == r::state_t::SHUT_DOWN); CHECK(diff_msgs.size() == 0); } - }; F().run(); } @@ -599,7 +598,274 @@ void test_relay_passive_unsuccessful_join() { CHECK(sup->get_state() == r::state_t::SHUT_DOWN); CHECK(diff_msgs.size() == 0); } + }; + F().run(); +} +void test_relay_malformed_uri() { + struct F : fixture_t { + std::string get_uri(const asio::ip::tcp::endpoint &endpoint) noexcept override { + return fmt::format("relay://{}", listening_ep); + } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + CHECK(!valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 2); + } + }; + F().run(); +} + +void test_relay_active_wrong_relay_deviceid() { + struct F : fixture_t { + + std::string get_uri(const asio::ip::tcp::endpoint &endpoint) noexcept override { + return fmt::format("relay://{}?id={}", listening_ep, my_device->device_id().get_value()); + } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + CHECK(!valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 2); + } + }; + F().run(); +} + +struct active_relay_fixture_t : fixture_t { + utils::key_pair_t relay_keys; + model::device_id_t relay_device; + std::string rx_buff; + std::string session_key = "lorem-session-dolor"; + transport::stream_sp_t relay_trans; + bool session_mode = false; + + active_relay_fixture_t() { + relay_keys = utils::generate_pair("relay").value(); + relay_device = model::device_id_t::from_cert(relay_keys.cert_data).value(); + rx_buff.resize(128); + } + + std::string get_uri(const asio::ip::tcp::endpoint &endpoint) noexcept override { + return fmt::format("relay://{}?id={}", listening_ep, relay_device.get_value()); + } + + void accept(const sys::error_code &ec) noexcept override { + LOG_INFO(log, "relay/accept, ec: {}", ec.message()); + if (!session_mode) { + relay_trans = transport::initiate_tls_passive(*sup, relay_keys, std::move(peer_sock)); + transport::handshake_fn_t handshake_fn = [this](bool valid_peer, utils::x509_t &cert, + const tcp::endpoint &peer_endpoint, + const model::device_id_t *peer_device) { + valid_handshake = valid_peer; + on_relay_hanshake(); + }; + transport::error_fn_t on_error = [](const auto &) {}; + relay_trans->async_handshake(handshake_fn, on_error); + return; + } + auto uri = utils::parse("tcp://127.0.0.1:0/").value(); + auto cfg = transport::transport_config_t{{}, uri, *sup, std::move(peer_sock)}; + peer_trans = transport::initiate_stream(cfg); + + transport::error_fn_t read_err_fn([&](auto ec) { log->error("(relay/active), read_err: {}", ec.message()); }); + transport::io_fn_t read_fn = [this](size_t bytes) { on_read_peer(bytes); }; + peer_trans->async_recv(asio::buffer(rx_buff), read_fn, read_err_fn); + } + + virtual void on_relay_hanshake() noexcept { + transport::error_fn_t read_err_fn([&](auto ec) { log->error("(relay/active), read_err: {}", ec.message()); }); + transport::io_fn_t read_fn = [this](size_t bytes) { on_read(bytes); }; + relay_trans->async_recv(asio::buffer(rx_buff), read_fn, read_err_fn); + } + + virtual void relay_reply() noexcept { + write(relay_trans, proto::relay::session_invitation_t{std::string(peer_device->device_id().get_sha256()), + session_key, "", listening_ep.port(), false}); + } + + virtual void session_reply() noexcept { write(peer_trans, proto::relay::response_t{0, "ok"}); } + + virtual void write(transport::stream_sp_t &stream, const proto::relay::message_t &msg) noexcept { + proto::relay::serialize(msg, rx_buff); + auto buff = asio::buffer(rx_buff); + transport::error_fn_t err_fn([&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); + transport::io_fn_t write_fn = [this](size_t bytes) { on_write(bytes); }; + stream->async_send(asio::buffer(rx_buff), write_fn, err_fn); + } + + virtual void on_read_peer(size_t bytes) { + log->debug("(relay/active) read peer {} bytes", bytes); + auto r = proto::relay::parse({rx_buff.data(), bytes}); + auto &wrapped = std::get(r); + auto &msg = std::get(wrapped.message); + CHECK(msg.key == session_key); + session_reply(); + } + + virtual void on_read(size_t bytes) { + log->debug("(relay/active) read {} bytes", bytes); + auto r = proto::relay::parse({rx_buff.data(), bytes}); + auto &wrapped = std::get(r); + auto &msg = std::get(wrapped.message); + CHECK(msg.device_id == peer_device->device_id().get_sha256()); + relay_reply(); + } + + virtual void on_write(size_t bytes) { + log->debug("(relay/active) write {} bytes", bytes); + if (!session_mode) { + acceptor.async_accept(peer_sock, [this](auto ec) { this->accept(ec); }); + session_mode = true; + } else { + auto upgradeable = static_cast(peer_trans.get()); + auto ssl = transport::ssl_junction_t{my_device->device_id(), &peer_keys, false, "bep"}; + peer_trans = upgradeable->upgrade(ssl, false); + initiate_peer_handshake(); + } + } +}; + +void test_relay_active_success() { + struct F : active_relay_fixture_t { + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::OPERATIONAL); + REQUIRE(connected_message); + CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); + CHECK(valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + REQUIRE(diff_msgs.size() == 1); + CHECK(diff_msgs[0]->payload.diff->apply(*cluster)); + CHECK(peer_device->get_state() == device_state_t::dialing); + } + }; + F().run(); +} + +void test_relay_wrong_device() { + struct F : active_relay_fixture_t { + + void relay_reply() noexcept override { + write(relay_trans, proto::relay::session_invitation_t{std::string(relay_device.get_sha256()), session_key, + "", listening_ep.port(), false}); + } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + CHECK(valid_handshake); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 2); + } + }; + F().run(); +} + +void test_relay_non_conneteable() { + struct F : active_relay_fixture_t { + + void relay_reply() noexcept override { + write(relay_trans, proto::relay::session_invitation_t{std::string(peer_device->device_id().get_sha256()), + session_key, "", 0, false}); + } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 2); + } + }; + F().run(); +} + +void test_relay_malformed_address() { + struct F : active_relay_fixture_t { + + void relay_reply() noexcept override { + write(relay_trans, proto::relay::session_invitation_t{std::string(peer_device->device_id().get_sha256()), + session_key, "8.8.8.8z", listening_ep.port(), false}); + } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 2); + } + }; + F().run(); +} + +void test_relay_garbage_reply() { + struct F : active_relay_fixture_t { + + void write(transport::stream_sp_t &stream, const proto::relay::message_t &) noexcept override { + rx_buff = "garbage-garbage-garbage"; + auto buff = asio::buffer(rx_buff); + transport::error_fn_t err_fn([&](auto ec) { log->error("(relay/passive), read_err: {}", ec.message()); }); + transport::io_fn_t write_fn = [this](size_t bytes) { on_write(bytes); }; + stream->async_send(asio::buffer(rx_buff), write_fn, err_fn); + } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 2); + } + }; + F().run(); +} + +void test_relay_noninvitation_reply() { + struct F : active_relay_fixture_t { + + void relay_reply() noexcept override { write(relay_trans, proto::relay::pong_t{}); } + + void main() noexcept override { + auto act = create_actor(); + io_ctx.run(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(!connected_message); + sup->do_shutdown(); + sup->do_process(); + CHECK(sup->get_state() == r::state_t::SHUT_DOWN); + CHECK(diff_msgs.size() == 2); + } }; F().run(); } @@ -620,3 +886,11 @@ REGISTER_TEST_CASE(test_relay_passive_success, "test_relay_passive_success", "[i REGISTER_TEST_CASE(test_relay_passive_gargabe, "test_relay_passive_gargabe", "[initiator]"); REGISTER_TEST_CASE(test_relay_passive_wrong_message, "test_relay_passive_wrong_message", "[initiator]"); REGISTER_TEST_CASE(test_relay_passive_unsuccessful_join, "test_relay_passive_unsuccessful_join", "[initiator]"); +REGISTER_TEST_CASE(test_relay_malformed_uri, "test_relay_malformed_uri", "[initiator]"); +REGISTER_TEST_CASE(test_relay_active_wrong_relay_deviceid, "test_relay_active_wrong_relay_deviceid", "[initiator]"); +REGISTER_TEST_CASE(test_relay_active_success, "test_relay_active_success", "[initiator]"); +REGISTER_TEST_CASE(test_relay_wrong_device, "test_relay_wrong_device", "[initiator]"); +REGISTER_TEST_CASE(test_relay_non_conneteable, "test_relay_non_conneteable", "[initiator]"); +REGISTER_TEST_CASE(test_relay_malformed_address, "test_relay_malformed_address", "[initiator]"); +REGISTER_TEST_CASE(test_relay_garbage_reply, "test_relay_garbage_reply", "[initiator]"); +REGISTER_TEST_CASE(test_relay_noninvitation_reply, "test_relay_noninvitation_reply", "[initiator]"); From ffde5e6fb47a506e0f69e5e4fcfcbd7f729cea95 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Wed, 11 May 2022 19:58:45 +0300 Subject: [PATCH 22/31] make initiator tests more reliable (win32) --- src/net/initiator_actor.cpp | 2 ++ tests/077-initiator.cpp | 68 +++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 8023d9a2..912ce13f 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -191,6 +191,7 @@ void initiator_actor_t::resolve(const utils::URI &uri) noexcept { } void initiator_actor_t::initiate_active_tls(const utils::URI &uri) noexcept { + LOG_TRACE(log, "{}, trying '{}' as active tsl", identity, uri.full); auto sup = static_cast(&router); transport = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri); active_uri = &uri; @@ -199,6 +200,7 @@ void initiator_actor_t::initiate_active_tls(const utils::URI &uri) noexcept { } void initiator_actor_t::initiate_active_relay(const utils::URI &uri) noexcept { + LOG_TRACE(log, "{}, trying '{}' as active relay", identity, uri.full); auto relay_device = proto::relay::parse_device(uri); if (!relay_device) { LOG_WARN(log, "{}, relay url '{}' does not contains valid device_id", identity, uri.full); diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp index 773ab868..d2d7357d 100644 --- a/tests/077-initiator.cpp +++ b/tests/077-initiator.cpp @@ -23,6 +23,7 @@ namespace r = rotor; namespace ra = r::asio; using configure_callback_t = std::function; +using finish_callback_t = std::function; auto timeout = r::pt::time_duration{r::pt::millisec{2000}}; auto host = "127.0.0.1"; @@ -41,14 +42,14 @@ struct supervisor_t : ra::supervisor_asio_t { void shutdown_finish() noexcept override { ra::supervisor_asio_t::shutdown_finish(); - if (acceptor) { - acceptor->cancel(); + if (finish_callback) { + finish_callback(); } } auto get_state() noexcept { return state; } - asio::ip::tcp::acceptor *acceptor = nullptr; + finish_callback_t finish_callback; configure_callback_t configure_callback; }; @@ -66,6 +67,13 @@ struct fixture_t { log = utils::get_logger("fixture"); } + virtual void finish() { + acceptor.cancel(); + if (peer_trans) { + peer_trans->cancel(); + } + } + void run() noexcept { auto strand = std::make_shared(io_ctx); @@ -84,6 +92,7 @@ struct fixture_t { })); }); }; + sup->finish_callback = [&]() { finish(); }; sup->start(); sup->create_actor().resolve_timeout(timeout / 2).timeout(timeout).finish(); @@ -117,7 +126,6 @@ struct fixture_t { virtual void initiate_accept() noexcept { acceptor.async_accept(peer_sock, [this](auto ec) { this->accept(ec); }); - sup->acceptor = &acceptor; } virtual std::string get_uri(const asio::ip::tcp::endpoint &endpoint) noexcept { @@ -160,6 +168,7 @@ struct fixture_t { } virtual void active_connect() { + LOG_TRACE(log, "active_connect"); transport::handshake_fn_t handshake_fn = [this](bool valid_peer, utils::x509_t &cert, const tcp::endpoint &peer_endpoint, const model::device_id_t *peer_device) { @@ -384,16 +393,31 @@ void test_success_no_model() { F().run(); } -void test_passive_success() { - struct F : fixture_t { - - actor_ptr_t act; +struct passive_fixture_t : fixture_t { + actor_ptr_t act; + bool active_connect_invoked = false; - void accept(const sys::error_code &ec) noexcept override { - LOG_INFO(log, "test_passive_success/accept, ec: {}", ec.message()); - act = create_passive_actor(); + void active_connect() override { + LOG_TRACE(log, "active_connect"); + if (!act || active_connect_invoked) { + return; } + active_connect_invoked = true; + active_connect_impl(); + } + + virtual void active_connect_impl() { fixture_t::active_connect(); } + void accept(const sys::error_code &ec) noexcept override { + LOG_INFO(log, "test_passive_success/accept, ec: {}", ec.message()); + act = create_passive_actor(); + sup->do_process(); + active_connect(); + } +}; + +void test_passive_success() { + struct F : passive_fixture_t { void main() noexcept override { initiate_active(); io_ctx.run(); @@ -410,20 +434,14 @@ void test_passive_success() { } void test_passive_garbage() { - struct F : fixture_t { + struct F : passive_fixture_t { - actor_ptr_t act; tcp::socket client_sock; tcp::resolver::results_type addresses; F() : client_sock{io_ctx} {} - void accept(const sys::error_code &ec) noexcept override { - LOG_INFO(log, "test_passive_garbage/accept, ec: {}", ec.message()); - act = create_passive_actor(); - } - - void initiate_active() noexcept { + void active_connect_impl() noexcept override { tcp::resolver resolver(io_ctx); addresses = resolver.resolve(host, std::to_string(listening_ep.port())); asio::async_connect(client_sock, addresses.begin(), addresses.end(), [&](auto ec, auto addr) { @@ -445,13 +463,7 @@ void test_passive_garbage() { } void test_passive_timeout() { - struct F : fixture_t { - actor_ptr_t act; - - void accept(const sys::error_code &ec) noexcept override { - LOG_INFO(log, "test_passive_timeout/accept, ec: {}", ec.message()); - act = create_passive_actor(); - } + struct F : passive_fixture_t { void active_connect() noexcept override { LOG_INFO(log, "test_passive_timeout/active_connect NOOP"); } @@ -766,6 +778,7 @@ void test_relay_wrong_device() { write(relay_trans, proto::relay::session_invitation_t{std::string(relay_device.get_sha256()), session_key, "", listening_ep.port(), false}); } + void on_write(size_t bytes) override {} void main() noexcept override { auto act = create_actor(); @@ -837,6 +850,8 @@ void test_relay_garbage_reply() { stream->async_send(asio::buffer(rx_buff), write_fn, err_fn); } + void on_write(size_t bytes) override {} + void main() noexcept override { auto act = create_actor(); io_ctx.run(); @@ -855,6 +870,7 @@ void test_relay_noninvitation_reply() { struct F : active_relay_fixture_t { void relay_reply() noexcept override { write(relay_trans, proto::relay::pong_t{}); } + void on_write(size_t bytes) override {} void main() noexcept override { auto act = create_actor(); From 9024f219146ab61e19db23cbdce57c7d8284851e Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Thu, 12 May 2022 00:01:37 +0300 Subject: [PATCH 23/31] minor relay-related fixes --- src/net/initiator_actor.cpp | 8 +++++--- src/net/peer_supervisor.cpp | 12 ++++++++++-- src/proto/relay_support.cpp | 2 ++ tests/016-relay-support.cpp | 17 +++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 912ce13f..09951c98 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -116,6 +116,8 @@ void initiator_actor_t::initiate_active() noexcept { initiate_active_tls(uri); } else if (uri.proto == "relay") { initiate_active_relay(uri); + } else { + continue; } return; } @@ -183,7 +185,7 @@ void initiator_actor_t::shutdown_finish() noexcept { } void initiator_actor_t::resolve(const utils::URI &uri) noexcept { - LOG_TRACE(log, "{}, resolving {} (transport = {})", identity, uri.full, (void *)transport.get()); + LOG_DEBUG(log, "{}, resolving {} (transport = {})", identity, uri.full, (void *)transport.get()); pt::time_duration resolve_timeout = init_timeout / 2; auto port = std::to_string(uri.port); request(resolver, uri.host, port).send(resolve_timeout); @@ -191,7 +193,7 @@ void initiator_actor_t::resolve(const utils::URI &uri) noexcept { } void initiator_actor_t::initiate_active_tls(const utils::URI &uri) noexcept { - LOG_TRACE(log, "{}, trying '{}' as active tsl", identity, uri.full); + LOG_DEBUG(log, "{}, trying '{}' as active tls", identity, uri.full); auto sup = static_cast(&router); transport = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri); active_uri = &uri; @@ -358,7 +360,7 @@ void initiator_actor_t::on_read_relay(size_t bytes) noexcept { return do_shutdown(make_error(ec)); } auto upgradeable = static_cast(transport.get()); - auto ssl = transport::ssl_junction_t{peer_device_id, &ssl_pair, false, "bep"}; + auto ssl = transport::ssl_junction_t{peer_device_id, &ssl_pair, true, constants::protocol_name}; auto active = role == role_t::active; transport = upgradeable->upgrade(ssl, active); initiate_handshake(); diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index 24ba3ca8..a9b1896d 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -80,9 +80,15 @@ void peer_supervisor_t::on_peer_ready(message::peer_connected_t &msg) noexcept { LOG_TRACE(log, "{}, on_peer_ready", identity); auto timeout = r::pt::milliseconds{bep_config.connect_timeout}; auto &p = msg.payload; + auto &d = p.peer_device_id; + auto peer = cluster->get_devices().by_sha256(d.get_sha256()); + if (peer->get_state() == model::device_state_t::online) { + LOG_DEBUG(log, "{}, peer '{}' is already online, ignoring request", identity, d.get_short()); + return; + } create_actor() .transport(std::move(p.transport)) - .peer_device_id(p.peer_device_id) + .peer_device_id(d) .device_name(device_name) .bep_config(bep_config) .coordinator(coordinator) @@ -148,7 +154,7 @@ auto peer_supervisor_t::operator()(const model::diff::modify::update_contact_t & if (!diff.self && diff.known) { auto &devices = cluster->get_devices(); auto peer = devices.by_sha256(diff.device.get_sha256()); - if (peer->get_state() != model::device_state_t::online) { + if (peer->get_state() == model::device_state_t::offline) { auto &uris = diff.uris; auto connect_timeout = r::pt::milliseconds{bep_config.connect_timeout}; LOG_DEBUG(log, "{} initiating connection with {}", identity, peer->device_id()); @@ -162,6 +168,8 @@ auto peer_supervisor_t::operator()(const model::diff::modify::update_contact_t & .init_timeout(connect_timeout * (uris.size() + 1)) .shutdown_timeout(connect_timeout) .finish(); + } else { + LOG_DEBUG(log, "{}, peer '{}' is not offline, dropping connection", identity, peer->device_id()); } } return outcome::success(); diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp index 4653620b..9a820c41 100644 --- a/src/proto/relay_support.cpp +++ b/src/proto/relay_support.cpp @@ -270,6 +270,8 @@ static parse_result_t parse_session_invitation(std::string_view data) noexcept { auto server_socket = be::big_to_native(*reinterpret_cast(data.data())); data = data.substr(sizeof(uint32_t)); + if(!addr.empty() && !addr[0]) { addr = ""; }; + return wrapped_message_t{ header_sz + orig.size(), session_invitation_t{std::string(from), std::string(key), std::string(addr), port, (bool)server_socket}}; diff --git a/tests/016-relay-support.cpp b/tests/016-relay-support.cpp index 1e8b3550..fb0cee03 100644 --- a/tests/016-relay-support.cpp +++ b/tests/016-relay-support.cpp @@ -99,6 +99,23 @@ TEST_CASE("relay proto", "[relay]") { CHECK(target->server_socket == source.server_socket); } + SECTION("session_invitation (host of zeroes)") { + auto zeros = std::string("\0\0\0\0", 4); + auto source = session_invitation_t{"lorem", "impsum", zeros, 1234, true}; + auto sz = serialize(source, buff); + REQUIRE(sz); + auto r = parse(buff); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == sz); + auto target = std::get_if(&msg->message); + REQUIRE(target); + CHECK(target->from == source.from); + CHECK(target->key == source.key); + CHECK(target->address.empty()); + CHECK(target->server_socket == source.server_socket); + } + SECTION("response sample") { const unsigned char data[] = {0x9e, 0x79, 0xbc, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, From a57ea4156eeafe602aa3860356db290539779c05 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Thu, 12 May 2022 00:24:42 +0300 Subject: [PATCH 24/31] minor relay-related fixes --- src/net/initiator_actor.cpp | 4 ++-- src/proto/relay_support.cpp | 4 +++- src/transport/stream.cpp | 16 +++++----------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 09951c98..fda4f463 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -359,10 +359,10 @@ void initiator_actor_t::on_read_relay(size_t bytes) noexcept { auto ec = utils::make_error_code(utils::error_code_t::relay_failure); return do_shutdown(make_error(ec)); } - auto upgradeable = static_cast(transport.get()); + auto &upgradeable = dynamic_cast(*transport.get()); auto ssl = transport::ssl_junction_t{peer_device_id, &ssl_pair, true, constants::protocol_name}; auto active = role == role_t::active; - transport = upgradeable->upgrade(ssl, active); + transport = upgradeable.upgrade(ssl, active); initiate_handshake(); } diff --git a/src/proto/relay_support.cpp b/src/proto/relay_support.cpp index 9a820c41..d37614d4 100644 --- a/src/proto/relay_support.cpp +++ b/src/proto/relay_support.cpp @@ -270,7 +270,9 @@ static parse_result_t parse_session_invitation(std::string_view data) noexcept { auto server_socket = be::big_to_native(*reinterpret_cast(data.data())); data = data.substr(sizeof(uint32_t)); - if(!addr.empty() && !addr[0]) { addr = ""; }; + if (!addr.empty() && !addr[0]) { + addr = ""; + }; return wrapped_message_t{ header_sz + orig.size(), diff --git a/src/transport/stream.cpp b/src/transport/stream.cpp index c43cb26a..e9353b36 100644 --- a/src/transport/stream.cpp +++ b/src/transport/stream.cpp @@ -6,24 +6,18 @@ namespace syncspirit::transport { -template -struct generic_steam_impl_t : base_impl_t, - interface_t, Sock, upgradeable_stream_base_t> { +template +struct generic_steam_impl_t : base_impl_t, interface_t, Sock, Interface> { using self_t = generic_steam_impl_t; using socket_t = Sock; using parent_t = base_impl_t; using parent_t::parent_t; }; -struct ssl_stream_impl_t final : generic_steam_impl_t { - using parent_t = generic_steam_impl_t; - using parent_t::parent_t; - - stream_sp_t upgrade(ssl_junction_t &, bool) noexcept override { assert(0 && "should not happen"); } -}; +using ssl_stream_impl_t = generic_steam_impl_t; -struct tcp_stream_impl_t final : generic_steam_impl_t { - using parent_t = generic_steam_impl_t; +struct tcp_stream_impl_t final : generic_steam_impl_t { + using parent_t = generic_steam_impl_t; tcp_stream_impl_t(transport_config_t &config) noexcept : parent_t(config), uri(config.uri) {} From 104c04539f6885c035b66e0282be02830d93deca Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Thu, 12 May 2022 12:29:07 +0300 Subject: [PATCH 25/31] cosmetic refactoring --- src/net/initiator_actor.cpp | 13 ++++++++++++- src/net/initiator_actor.h | 1 + src/net/peer_actor.cpp | 4 ++-- src/net/peer_actor.h | 7 +++++++ src/net/peer_supervisor.cpp | 1 + tests/077-initiator.cpp | 10 +++++++--- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index fda4f463..278af6c8 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -153,7 +153,18 @@ void initiator_actor_t::initiate_relay_passive() noexcept { void initiator_actor_t::on_start() noexcept { r::actor_base_t::on_start(); LOG_TRACE(log, "{}, on_start", identity); - send(sink, std::move(transport), peer_device_id, remote_endpoint, std::move(custom)); + std::string proto; + if (active_uri) { + proto = active_uri->proto; + } else { + if (role == role_t::relay_passive) { + proto = "relay"; + } else if (role == role_t::passive) { + proto = "tcp"; + } + } + send(sink, std::move(transport), peer_device_id, remote_endpoint, + std::move(proto), std::move(custom)); success = true; do_shutdown(); } diff --git a/src/net/initiator_actor.h b/src/net/initiator_actor.h index c5ad771d..3d14f92e 100644 --- a/src/net/initiator_actor.h +++ b/src/net/initiator_actor.h @@ -141,6 +141,7 @@ struct peer_connected_t { transport::stream_sp_t transport; model::device_id_t peer_device_id; tcp::endpoint remote_endpoint; + std::string proto; r::message_ptr_t custom; }; diff --git a/src/net/peer_actor.cpp b/src/net/peer_actor.cpp index 281ba5a6..910dd3c0 100644 --- a/src/net/peer_actor.cpp +++ b/src/net/peer_actor.cpp @@ -29,7 +29,7 @@ r::plugin::resource_id_t finalization = 5; peer_actor_t::peer_actor_t(config_t &config) : r::actor_base_t{config}, cluster{config.cluster}, device_name{config.device_name}, bep_config{config.bep_config}, coordinator{config.coordinator}, peer_device_id{config.peer_device_id}, - transport(std::move(config.transport)), peer_endpoint{config.peer_endpoint} { + transport(std::move(config.transport)), peer_endpoint{config.peer_endpoint}, peer_proto(std::move(config.peer_proto)) { rx_buff.resize(config.bep_config.rx_buff_size); log = utils::get_logger("net.peer_actor"); } @@ -39,7 +39,7 @@ void peer_actor_t::configure(r::plugin::plugin_base_t &plugin) noexcept { plugin.with_casted([&](auto &p) { std::stringstream ss; - ss << peer_device_id.get_short() << "/" << peer_endpoint; + ss << peer_device_id.get_short() << "/" << peer_proto << "/" << peer_endpoint; p.set_identity(ss.str(), false); }); plugin.with_casted([&](auto &p) { diff --git a/src/net/peer_actor.h b/src/net/peer_actor.h index df1009bd..b3404111 100644 --- a/src/net/peer_actor.h +++ b/src/net/peer_actor.h @@ -25,6 +25,7 @@ struct peer_actor_config_t : public r::actor_config_t { r::address_ptr_t coordinator; model::cluster_ptr_t cluster; tcp::endpoint peer_endpoint; + std::string peer_proto; }; template struct peer_actor_config_builder_t : r::actor_config_builder_t { @@ -66,6 +67,11 @@ template struct peer_actor_config_builder_t : r::actor_config_b parent_t::config.peer_endpoint = value; return std::move(*static_cast(this)); } + + builder_t &&peer_proto(std::string value) &&noexcept { + parent_t::config.peer_proto = value; + return std::move(*static_cast(this)); + } }; struct SYNCSPIRIT_API peer_actor_t : public r::actor_base_t { @@ -146,6 +152,7 @@ struct SYNCSPIRIT_API peer_actor_t : public r::actor_base_t { bool io_error = false; std::string cert_name; tcp::endpoint peer_endpoint; + std::string peer_proto; read_action_t read_action; r::address_ptr_t controller; block_requests_t block_requests; diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index a9b1896d..dded1f89 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -93,6 +93,7 @@ void peer_supervisor_t::on_peer_ready(message::peer_connected_t &msg) noexcept { .bep_config(bep_config) .coordinator(coordinator) .peer_endpoint(p.remote_endpoint) + .peer_proto(p.proto) .timeout(timeout) .cluster(cluster) .finish(); diff --git a/tests/077-initiator.cpp b/tests/077-initiator.cpp index d2d7357d..6f642138 100644 --- a/tests/077-initiator.cpp +++ b/tests/077-initiator.cpp @@ -359,7 +359,8 @@ void test_success() { auto act = create_actor(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::OPERATIONAL); - CHECK(connected_message); + REQUIRE(connected_message); + CHECK(connected_message->payload.proto == "tcp"); CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); CHECK(valid_handshake); sup->do_shutdown(); @@ -422,7 +423,8 @@ void test_passive_success() { initiate_active(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::OPERATIONAL); - CHECK(connected_message); + REQUIRE(connected_message); + CHECK(connected_message->payload.proto == "tcp"); CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); CHECK(valid_handshake); sup->do_shutdown(); @@ -533,7 +535,8 @@ void test_relay_passive_success() { auto act = create_actor(); io_ctx.run(); CHECK(sup->get_state() == r::state_t::OPERATIONAL); - CHECK(connected_message); + REQUIRE(connected_message); + CHECK(connected_message->payload.proto == "relay"); CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); CHECK(valid_handshake); sup->do_shutdown(); @@ -758,6 +761,7 @@ void test_relay_active_success() { io_ctx.run(); CHECK(sup->get_state() == r::state_t::OPERATIONAL); REQUIRE(connected_message); + CHECK(connected_message->payload.proto == "relay"); CHECK(connected_message->payload.peer_device_id == peer_device->device_id()); CHECK(valid_handshake); sup->do_shutdown(); From 79f1fa94bbe41e37f1eb650107adbc7b897f4097 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Thu, 12 May 2022 16:17:01 +0300 Subject: [PATCH 26/31] [bugfix] global-discovery-actor skipped announcements --- src/net/global_discovery_actor.cpp | 3 +-- src/net/initiator_actor.cpp | 11 ++++++++--- src/net/peer_actor.cpp | 3 ++- src/net/peer_supervisor.cpp | 26 ++++++++++++++++++++++++++ src/net/peer_supervisor.h | 1 + tests/016-relay-support.cpp | 16 ++++++++++++++++ 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/net/global_discovery_actor.cpp b/src/net/global_discovery_actor.cpp index 649aeef0..9fe5695d 100644 --- a/src/net/global_discovery_actor.cpp +++ b/src/net/global_discovery_actor.cpp @@ -241,9 +241,8 @@ auto global_discovery_actor_t::operator()(const model::diff::modify::update_cont auto &uris = diff.uris; if (resources->has(resource::timer)) { cancel_timer(*timer_request); - } else { - announce(); } + announce(); } return outcome::success(); } diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index 278af6c8..aa6b9ebc 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -163,8 +163,8 @@ void initiator_actor_t::on_start() noexcept { proto = "tcp"; } } - send(sink, std::move(transport), peer_device_id, remote_endpoint, - std::move(proto), std::move(custom)); + send(sink, std::move(transport), peer_device_id, remote_endpoint, std::move(proto), + std::move(custom)); success = true; do_shutdown(); } @@ -409,7 +409,12 @@ void initiator_actor_t::on_read_relay_active(size_t bytes) noexcept { } auto inv = std::get_if(&wrapped->message); if (!inv) { - LOG_WARN(log, "{}, unexpected relay message: {}", identity, spdlog::to_hex(buff.begin(), buff.end())); + auto reply = std::get_if(&wrapped->message); + if (reply) { + LOG_DEBUG(log, "{}, reply (expected session invitation) ({}) : {}", identity, reply->code, reply->details); + } else { + LOG_WARN(log, "{}, unexpected relay message: {}", identity, spdlog::to_hex(buff.begin(), buff.end())); + } auto ec = utils::make_error_code(utils::error_code_t::relay_failure); return do_shutdown(make_error(ec)); } diff --git a/src/net/peer_actor.cpp b/src/net/peer_actor.cpp index 910dd3c0..fe9226d4 100644 --- a/src/net/peer_actor.cpp +++ b/src/net/peer_actor.cpp @@ -29,7 +29,8 @@ r::plugin::resource_id_t finalization = 5; peer_actor_t::peer_actor_t(config_t &config) : r::actor_base_t{config}, cluster{config.cluster}, device_name{config.device_name}, bep_config{config.bep_config}, coordinator{config.coordinator}, peer_device_id{config.peer_device_id}, - transport(std::move(config.transport)), peer_endpoint{config.peer_endpoint}, peer_proto(std::move(config.peer_proto)) { + transport(std::move(config.transport)), peer_endpoint{config.peer_endpoint}, + peer_proto(std::move(config.peer_proto)) { rx_buff.resize(config.bep_config.rx_buff_size); log = utils::get_logger("net.peer_actor"); } diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index dded1f89..6fb354d2 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -8,6 +8,7 @@ #include "utils/error_code.h" #include "model/diff/peer/peer_state.h" #include "model/diff/modify/connect_request.h" +#include "model/diff/modify/relay_connect_request.h" #include "model/diff/modify/update_contact.h" #include "model/misc/error_code.h" @@ -150,6 +151,31 @@ auto peer_supervisor_t::operator()(const model::diff::modify::connect_request_t return outcome::success(); } +auto peer_supervisor_t::operator()(const model::diff::modify::relay_connect_request_t &diff) noexcept + -> outcome::result { + + auto peer = cluster->get_devices().by_sha256(diff.peer.get_sha256()); + if (peer->get_state() == model::device_state_t::offline) { + LOG_DEBUG(log, "{} initiating relay connection with {}", identity, peer->device_id()); + auto timeout = r::pt::milliseconds{bep_config.connect_timeout}; + auto uri_str = fmt::format("tcp://{}", diff.relay); + auto uri = utils::parse(uri_str); + create_actor() + .cluster(cluster) + .router(*locality_leader) + .sink(address) + .ssl_pair(&ssl_pair) + .peer_device_id(diff.peer) + .uris({std::move(uri.value())}) + .relay_session(diff.session_key) + .timeout(timeout) + .finish(); + } else { + LOG_DEBUG(log, "{}, peer '{}' is not offline, dropping relay connection request", identity, peer->device_id()); + } + return outcome::success(); +} + auto peer_supervisor_t::operator()(const model::diff::modify::update_contact_t &diff) noexcept -> outcome::result { if (!diff.self && diff.known) { diff --git a/src/net/peer_supervisor.h b/src/net/peer_supervisor.h index 0d7ff11f..dc6ec9cc 100644 --- a/src/net/peer_supervisor.h +++ b/src/net/peer_supervisor.h @@ -82,6 +82,7 @@ struct SYNCSPIRIT_API peer_supervisor_t : public ra::supervisor_asio_t, outcome::result operator()(const model::diff::peer::peer_state_t &) noexcept override; outcome::result operator()(const model::diff::modify::update_contact_t &) noexcept override; outcome::result operator()(const model::diff::modify::connect_request_t &) noexcept override; + outcome::result operator()(const model::diff::modify::relay_connect_request_t &) noexcept override; model::cluster_ptr_t cluster; utils::logger_t log; diff --git a/tests/016-relay-support.cpp b/tests/016-relay-support.cpp index fb0cee03..4d2323ee 100644 --- a/tests/016-relay-support.cpp +++ b/tests/016-relay-support.cpp @@ -132,6 +132,22 @@ TEST_CASE("relay proto", "[relay]") { CHECK(target->details == details); } + SECTION("response sample-2") { + const unsigned char data[] = {0x9e, 0x79, 0xbc, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x6e, 0x6f, + 0x74, 0x20, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x00, 0x00, 0x00}; + auto ptr = reinterpret_cast(data); + auto r = parse({ptr, sizeof(data)}); + auto msg = std::get_if(&r); + REQUIRE(msg); + CHECK(msg->length == 32); + auto target = std::get_if(&msg->message); + REQUIRE(target); + CHECK(target->code == 1); + auto details = std::string_view("not found"); + CHECK(target->details == details); + } + SECTION("session invitation sample") { const unsigned char data[] = {0x9e, 0x79, 0xbc, 0x40, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x20, 0xf8, 0x31, 0xf5, 0x75, 0xea, 0x61, 0x8a, 0x2f, 0x15, 0xef, From b169b693a559e501d09c90312cb8dffca990dd1a Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Thu, 12 May 2022 17:18:57 +0300 Subject: [PATCH 27/31] more strict relay-protocol conformance --- src/constants.cpp | 1 + src/constants.h | 1 + src/net/initiator_actor.cpp | 12 +++++------- src/net/initiator_actor.h | 7 +++++++ src/net/messages.h | 1 + src/net/peer_supervisor.cpp | 1 + src/net/relay_actor.cpp | 5 ++++- 7 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/constants.cpp b/src/constants.cpp index cc3b0f33..aeaa6f76 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -7,5 +7,6 @@ namespace syncspirit::constants { const char *client_name = "syncspirit"; const char *issuer_name = "syncthing"; const char *protocol_name = "bep/1.0"; +const char *relay_protocol_name = "bep-relay"; const char *client_version = "v0.01"; } // namespace syncspirit::constants diff --git a/src/constants.h b/src/constants.h index 1492555b..33b9a4c5 100644 --- a/src/constants.h +++ b/src/constants.h @@ -11,5 +11,6 @@ static const constexpr std::uint32_t rescan_interval = 3600; extern const char *client_name; extern const char *issuer_name; extern const char *protocol_name; +extern const char *relay_protocol_name; } // namespace syncspirit::constants diff --git a/src/net/initiator_actor.cpp b/src/net/initiator_actor.cpp index aa6b9ebc..786f025e 100644 --- a/src/net/initiator_actor.cpp +++ b/src/net/initiator_actor.cpp @@ -29,7 +29,7 @@ initiator_actor_t::initiator_actor_t(config_t &cfg) : r::actor_base_t{cfg}, peer_device_id{cfg.peer_device_id}, relay_key(std::move(cfg.relay_session)), ssl_pair{*cfg.ssl_pair}, sock(std::move(cfg.sock)), cluster{std::move(cfg.cluster)}, sink(std::move(cfg.sink)), - custom(std::move(cfg.custom)), router{*cfg.router} { + custom(std::move(cfg.custom)), router{*cfg.router}, alpn(cfg.alpn) { log = utils::get_logger("net.initator"); for (auto &uri : cfg.uris) { if (uri.proto != "tcp" && uri.proto != "relay") { @@ -204,9 +204,9 @@ void initiator_actor_t::resolve(const utils::URI &uri) noexcept { } void initiator_actor_t::initiate_active_tls(const utils::URI &uri) noexcept { - LOG_DEBUG(log, "{}, trying '{}' as active tls", identity, uri.full); + LOG_DEBUG(log, "{}, trying '{}' as active tls, alpn = {}", identity, uri.full, alpn); auto sup = static_cast(&router); - transport = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri); + transport = transport::initiate_tls_active(*sup, ssl_pair, peer_device_id, uri, false, alpn); active_uri = &uri; relaying = false; resolve(uri); @@ -415,14 +415,12 @@ void initiator_actor_t::on_read_relay_active(size_t bytes) noexcept { } else { LOG_WARN(log, "{}, unexpected relay message: {}", identity, spdlog::to_hex(buff.begin(), buff.end())); } - auto ec = utils::make_error_code(utils::error_code_t::relay_failure); - return do_shutdown(make_error(ec)); + return initiate_active(); } auto &peer = inv->from; if (peer != peer_device_id.get_sha256()) { LOG_WARN(log, "{}, unexpected peer device: {}", identity, spdlog::to_hex(peer.begin(), peer.end())); - auto ec = utils::make_error_code(utils::error_code_t::relay_failure); - return do_shutdown(make_error(ec)); + return initiate_active(); } auto &addr = inv->address; relay_key = inv->key; diff --git a/src/net/initiator_actor.h b/src/net/initiator_actor.h index 3d14f92e..431f3757 100644 --- a/src/net/initiator_actor.h +++ b/src/net/initiator_actor.h @@ -21,6 +21,7 @@ struct initiator_actor_config_t : public r::actor_config_t { r::address_ptr_t sink; r::message_ptr_t custom; r::supervisor_t *router; + std::string_view alpn; }; template struct initiator_actor_config_builder_t : r::actor_config_builder_t { @@ -72,6 +73,11 @@ template struct initiator_actor_config_builder_t : r::actor_con parent_t::config.router = &value; return std::move(*static_cast(this)); } + + builder_t &&alpn(std::string_view value) &&noexcept { + parent_t::config.alpn = value; + return std::move(*static_cast(this)); + } }; struct initiator_actor_t : r::actor_base_t { @@ -120,6 +126,7 @@ struct initiator_actor_t : r::actor_base_t { r::address_ptr_t sink; r::message_ptr_t custom; r::supervisor_t &router; + std::string_view alpn; const utils::URI *active_uri = nullptr; transport::stream_sp_t transport; diff --git a/src/net/messages.h b/src/net/messages.h index d99f1c24..33cb3140 100644 --- a/src/net/messages.h +++ b/src/net/messages.h @@ -146,6 +146,7 @@ struct connect_request_t { using response_t = connect_response_t; model::device_id_t device_id; utils::URI uri; + std::string_view alpn; }; } // end of namespace payload diff --git a/src/net/peer_supervisor.cpp b/src/net/peer_supervisor.cpp index 6fb354d2..e6f4d23a 100644 --- a/src/net/peer_supervisor.cpp +++ b/src/net/peer_supervisor.cpp @@ -117,6 +117,7 @@ void peer_supervisor_t::on_connect(message::connect_request_t &msg) noexcept { .custom(&msg) .router(*locality_leader) .sink(addr_unknown) + .alpn(p.alpn) .init_timeout(connect_timeout * 2) .shutdown_timeout(connect_timeout) .finish(); diff --git a/src/net/relay_actor.cpp b/src/net/relay_actor.cpp index ad648a4f..27c8bc78 100644 --- a/src/net/relay_actor.cpp +++ b/src/net/relay_actor.cpp @@ -1,5 +1,6 @@ #include "relay_actor.h" #include "names.h" +#include "constants.h" #include "utils/error_code.h" #include "utils/beast_support.h" #include "model/messages.h" @@ -94,7 +95,9 @@ void relay_actor_t::connect_to_relay() noexcept { relay->uri.host, relay->uri.port, l.city, l.country, l.continent); auto uri = utils::parse(fmt::format("tcp://{}:{}", u.host, u.port)).value(); - request(peer_supervisor, relay->device_id, std::move(uri)).send(init_timeout); + request(peer_supervisor, relay->device_id, std::move(uri), + constants::relay_protocol_name) + .send(init_timeout); return; } if (attempts >= 10) { From ebefaba942be9e5b1e5bf8bf0d2c2e1ef2f62dac Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Thu, 12 May 2022 17:53:24 +0300 Subject: [PATCH 28/31] [bugfix] fs::scan_actor request timeout, more modern rotot usage, one thread less --- src/ui-daemon/main.cpp | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/ui-daemon/main.cpp b/src/ui-daemon/main.cpp index ede9db17..c3b5e4e2 100644 --- a/src/ui-daemon/main.cpp +++ b/src/ui-daemon/main.cpp @@ -203,8 +203,11 @@ int main(int argc, char **argv) { .create_registry() .guard_context(true) .cluster_copies(cluster_copies) + .shutdown_flag(shutdown_flag, r::pt::millisec{50}) .finish(); sup_net->start(); + // pre-startup + sup_net->do_process(); rth::system_context_thread_t fs_context; auto fs_sup = fs_context.create_supervisor() @@ -234,15 +237,6 @@ int main(int argc, char **argv) { } /* launch actors */ - auto net_thread = std::thread([&]() { -#if defined(__linux__) - pthread_setname_np(pthread_self(), "ss/net"); -#endif - io_context.run(); - shutdown_flag = true; - spdlog::trace("net thread has been terminated"); - }); - auto fs_thread = std::thread([&]() { #if defined(__linux__) pthread_setname_np(pthread_self(), "ss/fs"); @@ -270,18 +264,12 @@ int main(int argc, char **argv) { } // main loop; - while (!shutdown_flag) { - using namespace std::chrono_literals; - std::this_thread::sleep_for(10ms); - } - - // initiate shutdown - spdlog::trace("sending shutdown signal..."); - auto ec = r::make_error_code(r::shutdown_code_t::normal); - auto coordinator = sup_net->get_address(); - auto ee = r::make_error("syncspirit-daemon is terminating", ec); - auto msg = r::make_message(coordinator, coordinator, ee); - sup_net->enqueue(msg); +#if defined(__linux__) + pthread_setname_np(pthread_self(), "ss/net"); +#endif + io_context.run(); + shutdown_flag = true; + spdlog::trace("net thread has been terminated"); spdlog::trace("waiting fs thread termination"); fs_thread.join(); @@ -290,10 +278,6 @@ int main(int argc, char **argv) { for (auto &thread : hasher_threads) { thread.join(); } - - spdlog::trace("waiting net thread termination"); - net_thread.join(); - spdlog::trace("everything has been terminated"); } catch (...) { spdlog::critical("unknown exception"); From 3c5170bc9b5d27e6900e4212541b2ee5db5b7299 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Thu, 12 May 2022 20:30:53 +0300 Subject: [PATCH 29/31] use upx --- CMakeLists.txt | 2 ++ src/ui-daemon/CMakeLists.txt | 27 ++++++++++++--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b0c3f527..261ec711 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -168,6 +168,8 @@ set(BUILD_SHARED_LIBS false CACHE BOOL "BUILD_SHARED_LIBS") set(LZ4_BUILD_CLI false CACHE BOOL "LZ4_BUILD_CLI") set(LZ4_BUILD_LEGACY_LZ4C false CACHE BOOL "LZ4_BUILD_LEGACY_LZ4C") set(MDBX_BUILD_TOOLS false CACHE BOOL "MDBX_BUILD_TOOLS") +set(MDBX_ENABLE_TESTS false CACHE BOOL "MDBX_ENABLE_TESTS") +set(MDBX_BUILD_CXX false CACHE BOOL "MDBX_BUILD_CXX") cmake_policy(SET CMP0077 NEW) set(PUGIXML_NO_EXCEPTIONS on) diff --git a/src/ui-daemon/CMakeLists.txt b/src/ui-daemon/CMakeLists.txt index d82bf529..38d5ef56 100644 --- a/src/ui-daemon/CMakeLists.txt +++ b/src/ui-daemon/CMakeLists.txt @@ -32,20 +32,17 @@ endif() if (CMAKE_BUILD_TYPE MATCHES "^([Rr]elease)|(MinSizeRel)") set_target_properties(syncspirit-daemon PROPERTIES LINK_FLAGS -s) - set(DAEMON_TARGET "${CMAKE_CXX_COMPILER}") - string(REGEX REPLACE ".*/" "" DAEMON_TARGET ${DAEMON_TARGET}) - string(REGEX REPLACE "(.*)-.+" "\\1" DAEMON_TARGET ${DAEMON_TARGET}) - if ("${DAEMON_TARGET}" STREQUAL "") - set(DAEMON_TARGET "unknown") + find_program(UPX upx) + if (NOT ${UPX_FOUND}) + message(WARNING "upx not found") + else() + message(STATUS "upx found") + set(EXE "syncspirit-daemon${CMAKE_EXECUTABLE_SUFFIX}") + add_custom_target(compress_exec ALL + COMMAND upx "--force" "-9" ${EXE} + DEPENDS ${EXE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "compressing ${EXE}" + VERBATIM) endif() - string(JOIN "_" DAEMON_TARGET "syncspirit-daemon" ${SYNCSPIRIT_VERSION} ${DAEMON_TARGET}) - set(DAEMON_TARGET "${DAEMON_TARGET}.zip") - set(ACHIVE_NAME "${syncspirit_BINARY_DIR}/${DAEMON_TARGET}") - message(STATUS "going to make an ${ACHIVE_NAME}") - add_custom_target(make_archive ALL - COMMAND zip "-q9" "${ACHIVE_NAME}" "syncspirit-daemon${CMAKE_EXECUTABLE_SUFFIX}" - DEPENDS "syncspirit-daemon${CMAKE_EXECUTABLE_SUFFIX}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "making release archive ${DAEMON_TARGET}" - VERBATIM) endif() From 7688ef9d32343dd3ef9dd22efa8dd26e20ffbcc5 Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 22 May 2022 15:10:31 +0300 Subject: [PATCH 30/31] update Changes, use upx --- CMakeLists.txt | 2 +- README.md | 47 +++++++++++++++++++++--------------- src/ui-daemon/CMakeLists.txt | 14 ++++++++--- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 261ec711..ebea72f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ find_package(OpenSSL REQUIRED) find_package(Protobuf REQUIRED) find_package(ZLIB REQUIRED) -set(SYNCSPIRIT_VERSION "v0.1.0") +set(SYNCSPIRIT_VERSION "v0.2.0") configure_file(misc/syncspirit-config.h.in include/syncspirit-config.h @ONLY) set(Protobuf_IMPORT_DIRS ${syncspirit_SOURCE_DIR}/src) diff --git a/README.md b/README.md index 9a012a0f..d38d3677 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # syncspirit -sites: [github](https://github.com/basiliscos/syncspirit), [abf](https://github.com/basiliscos/syncspirit) +sites: [github](https://github.com/basiliscos/syncspirit), [abf](https://github.com/basiliscos/syncspirit), +[gitflic](https://gitflic.ru/project/basiliscos/syncspirit) `syncspirit` is a continuous file synchronization program, which synchronizes files between devices. It is build using C++ [rotor](github.com/basiliscos/cpp-rotor) actor framework. It implements [BEP-protocol](https://docs.syncthing.net/specs/bep-v1.html) for files syncrhonization, or, simplistically speaking, it is [syncthing](https://syncthing.net)-compatible syncrhonization -program. +program, which uses [syncthing](https://syncthing.net) infrastructure (for global discovery +and relaying) Despite of being functional `syncspirit` is much less feature-rich then [syncthing](https://syncthing.net) and still is in heavy development. @@ -14,38 +16,37 @@ and still is in heavy development. # status - [x] downloading files from peer devices (aka all folders are receive only) +- [x] downloading files from peer devices (aka all folders are receive only) - [x] [global peer discovery](https://docs.syncthing.net/specs/globaldisco-v3.html) +- [x] [global peer discovery](https://docs.syncthing.net/specs/globaldisco-v3.html) - [x] [local (LAN) peer discovery](https://docs.syncthing.net/specs/localdisco-v4.html) +- [x] [local (LAN) peer discovery](https://docs.syncthing.net/specs/localdisco-v4.html) - [x] upnp & nat passthough +- [x] upnp & nat passthough - [x] certificates generation +- [x] certificates generation +- [x] relay transport # missing features This list is probably incomplete, here are the most important changes - [ ] relay transport +- [ ] full-powered files synchronization (aka send and receive) - [ ] full-powered files synchronization (aka send and receive) +- [ ] conflict resolution - [ ] conflict resolution +- [ ] ignoring files - [ ] ingoring files +- [ ] [QUIC transport](https://en.wikipedia.org/wiki/QUIC) - [ ] [QUIC transport](https://en.wikipedia.org/wiki/QUIC) +- [ ] introducer support - [ ] introducer support +- [ ] outgoing messages compression - [ ] outgoing messages compression +- [ ] [untrusted devices encryption](https://docs.syncthing.net/specs/untrusted.html) - [ ] [untrusted devices encryption](https://docs.syncthing.net/specs/untrusted.html) - - [ ] ... +- [ ] ... # run @@ -63,7 +64,7 @@ the output should be like [![asciicast](https://asciinema.org/a/474217.svg)](https://asciinema.org/a/474217) i.e. it records some peer, adds a folder, then shares the folder with the peer device, connects to -the peer and downloads all files into `/tmp/my_dir/data` . The peer device Currently can be +the peer and downloads all files into `/tmp/my_dir/data` . The peer device currently can be only [syncthing](https://syncthing.net). Then `syncspirit` either exits after 2 minutes of inactivity or when you press `ctrl+c`. The output is successful, because I previousy authorized this device with [syncthing](https://syncthing.net) web interface, and shared the folder with this device @@ -92,7 +93,7 @@ classical desktop and client-server application models. I think, "core" into multiple different user interfaces (GUIs). Currently, there syncspirit has only "daemon-ui", i.e. a simple non-interactive application, -which shows synchronization log, and the only possible just stop it. However, as soon +which shows synchronization log, and the only possibility is just to stop it. However, as soon as the "core" will be complete, there are plans to develop multiple `syncspirit` UIs: [wx-widgets](https://www.wxwidgets.org/), qt, gtk, may be native, may be even native mobile UIs... @@ -117,6 +118,14 @@ after the core completion. # changes +## 0.2.0 (22-May-2022) +- [feature] implement [relay transport](https://docs.syncthing.net/specs/relay-v1.html), +the relay is randombly chosen from the public relays [pool](https://relays.syncthing.net/endpoint) +- [feature] output binary is compressed via [upx](https://upx.github.io) +- [feature] small optimization, use thread less in overall program +- [bugfix] sometimes fs::scan_actor request timeout ocurrs, which is fatal +- [bugfix] global discovery sometimes skipped announcements + ## 0.1.0 (18-Arp-2022) - initial release diff --git a/src/ui-daemon/CMakeLists.txt b/src/ui-daemon/CMakeLists.txt index 38d5ef56..eeaa5c32 100644 --- a/src/ui-daemon/CMakeLists.txt +++ b/src/ui-daemon/CMakeLists.txt @@ -37,12 +37,20 @@ if (CMAKE_BUILD_TYPE MATCHES "^([Rr]elease)|(MinSizeRel)") message(WARNING "upx not found") else() message(STATUS "upx found") + set(DAEMON_TARGET "${CMAKE_CXX_COMPILER}") + string(REGEX REPLACE ".*/" "" DAEMON_TARGET ${DAEMON_TARGET}) + string(REGEX REPLACE "(.*)-.+" "\\1" DAEMON_TARGET ${DAEMON_TARGET}) + if ("${DAEMON_TARGET}" STREQUAL "") + set(DAEMON_TARGET "unknown") + endif() + string(JOIN "_" DAEMON_TARGET "syncspirit-daemon" ${SYNCSPIRIT_VERSION} ${DAEMON_TARGET}) + set(DAEMON_TARGET "${DAEMON_TARGET}${CMAKE_EXECUTABLE_SUFFIX}") + set(EXE "syncspirit-daemon${CMAKE_EXECUTABLE_SUFFIX}") add_custom_target(compress_exec ALL - COMMAND upx "--force" "-9" ${EXE} + COMMAND upx "--force" "-9" "-q" "-o" "${syncspirit_BINARY_DIR}/${DAEMON_TARGET}" ${EXE} DEPENDS ${EXE} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "compressing ${EXE}" - VERBATIM) + COMMENT "compressing ${DAEMON_TARGET}") endif() endif() From cc0a692c2d3a2b6674acb06be3f541b9c0398eff Mon Sep 17 00:00:00 2001 From: Ivan Baidakou Date: Sun, 22 May 2022 15:12:51 +0300 Subject: [PATCH 31/31] update config docs --- docs/config.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/config.md b/docs/config.md index e3ec157e..3f304c16 100644 --- a/docs/config.md +++ b/docs/config.md @@ -70,6 +70,12 @@ timeout = 5000 # the amount of hasher threads hasher_threads = 3 +[relay] +enabled = true +# where pick the list of relay servers pool +discovery_url = 'https://relays.syncthing.net/endpoint' +rx_buff_size = 1048576 + [upnp] enabled = true # do output of upnp requests