diff --git a/adnl/adnl-address-list.cpp b/adnl/adnl-address-list.cpp index 7bd36374..912943a8 100644 --- a/adnl/adnl-address-list.cpp +++ b/adnl/adnl-address-list.cpp @@ -185,7 +185,8 @@ td::Ref AdnlAddressImpl::create(const tl_object_ptr(addr.get()), td::overloaded([&](const ton_api::adnl_address_udp &obj) { res = td::make_ref(obj); }, [&](const ton_api::adnl_address_udp6 &obj) { res = td::make_ref(obj); }, - [&](const ton_api::adnl_address_tunnel &obj) { res = td::make_ref(obj); })); + [&](const ton_api::adnl_address_tunnel &obj) { res = td::make_ref(obj); }, + [&](const ton_api::adnl_address_reverse &obj) { res = td::make_ref(); })); return res; } @@ -202,7 +203,12 @@ AdnlAddressList::AdnlAddressList(const tl_object_ptr version_ = static_cast(addrs->version_); std::vector> vec; for (auto &addr : addrs->addrs_) { - vec.push_back(AdnlAddressImpl::create(addr)); + auto obj = AdnlAddressImpl::create(addr); + if (obj->is_reverse()) { + has_reverse_ = true; + } else { + vec.push_back(std::move(obj)); + } } addrs_ = std::move(vec); reinit_date_ = addrs->reinit_date_; @@ -215,6 +221,9 @@ tl_object_ptr AdnlAddressList::tl() const { for (auto &v : addrs_) { addrs.emplace_back(v->tl()); } + if (has_reverse_) { + addrs.push_back(create_tl_object()); + } return create_tl_object(std::move(addrs), version_, reinit_date_, priority_, expire_at_); } diff --git a/adnl/adnl-address-list.h b/adnl/adnl-address-list.h index 80d7f96b..ebc7473a 100644 --- a/adnl/adnl-address-list.h +++ b/adnl/adnl-address-list.h @@ -39,6 +39,9 @@ class AdnlAddressImpl : public td::CntObject { virtual td::actor::ActorOwn create_connection( td::actor::ActorId network_manager, td::actor::ActorId adnl, std::unique_ptr callback) const = 0; + virtual bool is_reverse() const { + return false; + } static td::Ref create(const tl_object_ptr &addr); }; @@ -54,6 +57,7 @@ class AdnlAddressList { td::int32 priority_; td::int32 expire_at_; std::vector addrs_; + bool has_reverse_{false}; public: static constexpr td::uint32 max_serialized_size() { @@ -102,6 +106,13 @@ class AdnlAddressList { static td::Result create(const tl_object_ptr &addr_list); td::Status add_udp_address(td::IPAddress addr); + + void set_reverse(bool x = true) { + has_reverse_ = x; + } + bool has_reverse() const { + return has_reverse_; + } }; } // namespace adnl diff --git a/adnl/adnl-address-list.hpp b/adnl/adnl-address-list.hpp index efc226d2..0c869378 100644 --- a/adnl/adnl-address-list.hpp +++ b/adnl/adnl-address-list.hpp @@ -116,6 +116,31 @@ class AdnlAddressTunnel : public AdnlAddressImpl { std::unique_ptr callback) const override; }; +class AdnlAddressReverse : public AdnlAddressImpl { + public: + AdnlAddressReverse *make_copy() const override { + return new AdnlAddressReverse(); + } + bool is_public() const override { + return true; + } + td::uint32 serialized_size() const override { + return 4; + } + tl_object_ptr tl() const override { + return create_tl_object(); + } + td::actor::ActorOwn create_connection( + td::actor::ActorId network_manager, td::actor::ActorId adnl, + std::unique_ptr callback) const override { + LOG(ERROR) << "Cannot create connection for AdnlAddressReverse"; + return {}; + } + bool is_reverse() const override { + return true; + } +}; + } // namespace adnl } // namespace ton diff --git a/adnl/adnl-local-id.cpp b/adnl/adnl-local-id.cpp index 3461efe1..b4818276 100644 --- a/adnl/adnl-local-id.cpp +++ b/adnl/adnl-local-id.cpp @@ -121,7 +121,7 @@ void AdnlLocalId::update_address_list(AdnlAddressList addr_list) { } void AdnlLocalId::publish_address_list() { - if (dht_node_.empty() || addr_list_.empty() || addr_list_.size() == 0) { + if (dht_node_.empty() || addr_list_.empty() || (addr_list_.size() == 0 && !addr_list_.has_reverse())) { VLOG(ADNL_NOTICE) << this << ": skipping public addr list, because localid (or dht node) not fully initialized"; return; } @@ -175,6 +175,17 @@ void AdnlLocalId::publish_address_list() { td::actor::send_closure(keyring_, &keyring::Keyring::sign_message, short_id_.pubkey_hash(), std::move(B), std::move(P)); + + if (addr_list_.has_reverse()) { + td::actor::send_closure( + dht_node_, &dht::Dht::register_reverse_connection, id_, [print_id = print_id()](td::Result R) { + if (R.is_error()) { + VLOG(ADNL_NOTICE) << print_id << ": failed to register reverse connection in DHT: " << R.move_as_error(); + } else { + VLOG(ADNL_INFO) << print_id << ": registered reverse connection"; + } + }); + } } AdnlLocalId::AdnlLocalId(AdnlNodeIdFull id, AdnlAddressList addr_list, td::uint32 mode, diff --git a/adnl/adnl-peer.cpp b/adnl/adnl-peer.cpp index 35ba2a11..440ab1c3 100644 --- a/adnl/adnl-peer.cpp +++ b/adnl/adnl-peer.cpp @@ -113,6 +113,7 @@ void AdnlPeerPairImpl::discover() { } void AdnlPeerPairImpl::receive_packet_checked(AdnlPacket packet) { + request_reverse_ping_after_ = td::Timestamp::in(15.0); auto d = Adnl::adnl_start_time(); if (packet.dst_reinit_date() > d) { VLOG(ADNL_WARNING) << this << ": dropping IN message: too new our reinit date " << packet.dst_reinit_date(); @@ -653,10 +654,15 @@ td::Result, bool>> AdnlPeerP } if (conns_.size() == 0 && priority_conns_.size() == 0) { - return td::Status::Error(ErrorCode::notready, PSTRING() - << "empty network information: version=" << addr_list_.version() - << " reinit_date=" << addr_list_.reinit_date() - << " real_reinit_date=" << reinit_date_); + if (has_reverse_addr_) { + request_reverse_ping(); + return td::Status::Error(ErrorCode::notready, "waiting for reverse ping"); + } else { + return td::Status::Error(ErrorCode::notready, PSTRING() + << "empty network information: version=" << addr_list_.version() + << " reinit_date=" << addr_list_.reinit_date() + << " real_reinit_date=" << reinit_date_); + } } for (auto &conn : priority_conns_) { @@ -704,11 +710,18 @@ void AdnlPeerPairImpl::update_addr_list(AdnlAddressList addr_list) { VLOG(ADNL_INFO) << this << ": updating addr list to version " << addr_list.version() << " size=" << addr_list.size(); const auto addrs = addr_list.addrs(); + has_reverse_addr_ = addr_list.has_reverse(); + if (has_reverse_addr_ && addrs.empty()) { + return; + } std::vector conns; auto &old_conns = priority ? priority_conns_ : conns_; size_t idx = 0; for (const auto &addr : addrs) { + if (addr->is_reverse()) { + continue; + } if ((mode_ & static_cast(AdnlLocalIdMode::direct_only)) && !addr->is_public()) { continue; } @@ -730,7 +743,7 @@ void AdnlPeerPairImpl::get_conn_ip_str(td::Promise promise) { promise.set_value("undefined"); return; } - + for (auto &conn : priority_conns_) { if (conn.ready()) { td::actor::send_closure(conn.conn, &AdnlNetworkConnection::get_ip_str, std::move(promise)); @@ -743,7 +756,7 @@ void AdnlPeerPairImpl::get_conn_ip_str(td::Promise promise) { return; } } - + promise.set_value("undefined"); } @@ -868,7 +881,7 @@ void AdnlPeerImpl::get_conn_ip_str(AdnlNodeIdShort l_id, td::Promise if (it == peer_pairs_.end()) { promise.set_value("undefined"); return; - } + } td::actor::send_closure(it->second, &AdnlPeerPair::get_conn_ip_str, std::move(promise)); } @@ -944,6 +957,36 @@ void AdnlPeerPairImpl::update_peer_id(AdnlNodeIdFull id) { CHECK(!peer_id_.empty()); } +void AdnlPeerPairImpl::request_reverse_ping() { + if (request_reverse_ping_active_ || !request_reverse_ping_after_.is_in_past()) { + return; + } + VLOG(ADNL_INFO) << this << ": requesting reverse ping"; + request_reverse_ping_after_ = td::Timestamp::in(15.0); + request_reverse_ping_active_ = true; + td::actor::send_closure( + local_actor_, &AdnlLocalId::get_self_node, + [SelfId = actor_id(this), peer = peer_id_short_, dht = dht_node_](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &AdnlPeerPairImpl::request_reverse_ping_result, R.move_as_error()); + return; + } + td::actor::send_closure( + dht, &dht::Dht::request_reverse_ping, R.move_as_ok(), peer, [SelfId](td::Result R) { + td::actor::send_closure(SelfId, &AdnlPeerPairImpl::request_reverse_ping_result, std::move(R)); + }); + }); +} + +void AdnlPeerPairImpl::request_reverse_ping_result(td::Result R) { + request_reverse_ping_active_ = false; + if (R.is_ok()) { + VLOG(ADNL_INFO) << this << ": reverse ping requested"; + } else { + VLOG(ADNL_INFO) << this << ": failed to request reverse ping: " << R.move_as_error(); + } +} + } // namespace adnl } // namespace ton diff --git a/adnl/adnl-peer.hpp b/adnl/adnl-peer.hpp index 410c0f75..0efe827d 100644 --- a/adnl/adnl-peer.hpp +++ b/adnl/adnl-peer.hpp @@ -153,6 +153,9 @@ class AdnlPeerPairImpl : public AdnlPeerPair { } } + void request_reverse_ping(); + void request_reverse_ping_result(td::Result R); + struct Conn { class ConnCallback : public AdnlNetworkConnection::Callback { public: @@ -250,6 +253,10 @@ class AdnlPeerPairImpl : public AdnlPeerPair { td::Timestamp next_dht_query_at_ = td::Timestamp::never(); td::Timestamp next_db_update_at_ = td::Timestamp::never(); td::Timestamp retry_send_at_ = td::Timestamp::never(); + + bool has_reverse_addr_ = false; + td::Timestamp request_reverse_ping_after_ = td::Timestamp::now(); + bool request_reverse_ping_active_ = false; }; class AdnlPeerImpl : public AdnlPeer { diff --git a/dht/dht-in.hpp b/dht/dht-in.hpp index c4f67819..fe106b0d 100644 --- a/dht/dht-in.hpp +++ b/dht/dht-in.hpp @@ -66,6 +66,15 @@ class DhtMemberImpl : public DhtMember { DhtKeyId last_republish_key_ = DhtKeyId::zero(); DhtKeyId last_check_key_ = DhtKeyId::zero(); + adnl::AdnlNodeIdShort last_check_reverse_conn_ = adnl::AdnlNodeIdShort::zero(); + + struct ReverseConnection { + adnl::AdnlNodeIdShort dht_node_; + DhtKeyId key_id_; + td::Timestamp ttl_; + }; + std::map reverse_connections_; + std::set our_reverse_connections_; class Callback : public adnl::Adnl::Callback { public: @@ -122,6 +131,10 @@ class DhtMemberImpl : public DhtMember { void process_query(adnl::AdnlNodeIdShort src, ton_api::dht_store &query, td::Promise promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::dht_getSignedAddressList &query, td::Promise promise); + void process_query(adnl::AdnlNodeIdShort src, ton_api::dht_registerReverseConnection &query, + td::Promise promise); + void process_query(adnl::AdnlNodeIdShort src, ton_api::dht_requestReversePing &query, + td::Promise promise); public: DhtMemberImpl(adnl::AdnlNodeIdShort id, std::string db_root, td::actor::ActorId keyring, @@ -143,6 +156,12 @@ class DhtMemberImpl : public DhtMember { void set_value(DhtValue key_value, td::Promise result) override; td::uint32 distance(DhtKeyId key_id, td::uint32 max_value); + void register_reverse_connection(adnl::AdnlNodeIdFull client, td::Promise promise) override; + void request_reverse_ping(adnl::AdnlNode target, adnl::AdnlNodeIdShort client, + td::Promise promise) override; + void request_reverse_ping_cont(adnl::AdnlNode target, td::BufferSlice signature, adnl::AdnlNodeIdShort client, + td::Promise promise); + td::Status store_in(DhtValue value) override; void send_store(DhtValue value, td::Promise promise); diff --git a/dht/dht-query.cpp b/dht/dht-query.cpp index 7e1f1b92..e0e878d0 100644 --- a/dht/dht-query.cpp +++ b/dht/dht-query.cpp @@ -279,6 +279,131 @@ void DhtQueryStore::store_ready(td::Result R) { } } +DhtQueryRegisterReverseConnection::DhtQueryRegisterReverseConnection( + DhtKeyId key_id, adnl::AdnlNodeIdFull client, td::uint32 ttl, td::BufferSlice signature, + DhtMember::PrintId print_id, adnl::AdnlNodeIdShort src, DhtNodesList list, td::uint32 k, td::uint32 a, DhtNode self, + bool client_only, td::actor::ActorId node, td::actor::ActorId adnl, + td::Promise promise) + : print_id_(print_id) + , k_(k) + , a_(a) + , promise_(std::move(promise)) + , key_id_(key_id) + , list_(std::move(list)) + , self_(std::move(self)) + , client_only_(client_only) { + node_ = node; + adnl_ = adnl; + src_ = src; + query_ = create_serialize_tl_object(client.tl(), ttl, std::move(signature)); +} + +void DhtQueryRegisterReverseConnection::start_up() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result res) { + td::actor::send_closure(SelfId, &DhtQueryRegisterReverseConnection::send_queries, std::move(res)); + }); + + auto A = td::actor::create_actor("FindNodesQuery", key_id_, print_id_, src_, std::move(list_), k_, + a_, self_.clone(), client_only_, node_, adnl_, std::move(P)); + A.release(); +} + +void DhtQueryRegisterReverseConnection::send_queries(td::Result R) { + if (R.is_error()) { + auto S = R.move_as_error(); + VLOG(DHT_NOTICE) << this << ": failed to get nearest nodes to " << key_id_ << ": " << S; + promise_.set_error(std::move(S)); + stop(); + return; + } + auto list = R.move_as_ok(); + + remaining_ = static_cast(list.size()); + if (remaining_ == 0) { + VLOG(DHT_NOTICE) << this << ": failed to get nearest nodes to " << key_id_ << ": no nodes"; + promise_.set_error(td::Status::Error("no dht nodes")); + stop(); + return; + } + + for (auto &node : list.list()) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + td::actor::send_closure(SelfId, &DhtQueryRegisterReverseConnection::ready, std::move(R)); + }); + td::actor::send_closure(adnl_, &adnl::Adnl::send_query, src_, node.adnl_id().compute_short_id(), "dht regrevcon", + std::move(P), td::Timestamp::in(2.0 + td::Random::fast(0, 20) * 0.1), query_.clone()); + } +} + +void DhtQueryRegisterReverseConnection::ready(td::Result R) { + if (R.is_error()) { + fail_++; + VLOG(DHT_INFO) << this << ": failed register reverse connection query: " << R.move_as_error(); + } else { + auto R2 = fetch_tl_object(R.move_as_ok(), true); + if (R2.is_error()) { + fail_++; + VLOG(DHT_WARNING) << this << ": can not parse answer (expected dht.stored): " << R2.move_as_error(); + } else { + success_++; + } + } + CHECK(remaining_ > 0); + remaining_--; + if (remaining_ == 0) { + if (success_ > 0) { + promise_.set_value(td::Unit()); + } else { + promise_.set_result(td::Status::Error("failed to make actual query")); + } + stop(); + } +} + +void DhtQueryRequestReversePing::send_one_query(adnl::AdnlNodeIdShort id) { + td::BufferSlice B; + if (client_only_) { + B = query_.clone(); + } else { + B = create_serialize_tl_object_suffix(query_.as_slice(), self_.tl()); + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), dst = id](td::Result R) { + td::actor::send_closure(SelfId, &DhtQueryRequestReversePing::on_result, std::move(R), dst); + }); + td::actor::send_closure(adnl_, &adnl::Adnl::send_query, get_src(), id, "dht requestReversePing", std::move(P), + td::Timestamp::in(2.0 + td::Random::fast(0, 20) * 0.1), std::move(B)); +} + +void DhtQueryRequestReversePing::on_result(td::Result R, adnl::AdnlNodeIdShort dst) { + if (R.is_error()) { + VLOG(DHT_INFO) << this << ": failed reverse ping query " << get_src() << "->" << dst << ": " << R.move_as_error(); + finish_query(); + return; + } + auto Res = fetch_tl_object(R.move_as_ok(), true); + if (Res.is_error()) { + VLOG(DHT_WARNING) << this << ": dropping incorrect answer on dht.requestReversePing query from " << dst << ": " + << Res.move_as_error(); + finish_query(); + return; + } + + auto A = Res.move_as_ok(); + ton_api::downcast_call(*A, td::overloaded( + [&](ton_api::dht_reversePingOk &v) { + promise_.set_value(td::Unit()); + stop(); + }, + [&](ton_api::dht_clientNotFound &v) { + add_nodes(DhtNodesList{std::move(v.nodes_)}); + finish_query(); + })); +} + +void DhtQueryRequestReversePing::finish(DhtNodesList list) { + promise_.set_error(td::Status::Error(ErrorCode::notready, "dht key not found")); +} + } // namespace dht } // namespace ton diff --git a/dht/dht-query.hpp b/dht/dht-query.hpp index aa607f56..fe2f2d70 100644 --- a/dht/dht-query.hpp +++ b/dht/dht-query.hpp @@ -165,6 +165,62 @@ class DhtQueryStore : public td::actor::Actor { } }; +class DhtQueryRegisterReverseConnection : public td::actor::Actor { + private: + DhtMember::PrintId print_id_; + td::uint32 k_; + td::uint32 a_; + td::Promise promise_; + td::actor::ActorId node_; + td::actor::ActorId adnl_; + adnl::AdnlNodeIdShort src_; + DhtKeyId key_id_; + td::BufferSlice query_; + td::uint32 success_ = 0; + td::uint32 fail_ = 0; + td::uint32 remaining_; + DhtNodesList list_; + DhtNode self_; + bool client_only_; + + public: + DhtQueryRegisterReverseConnection(DhtKeyId key_id, adnl::AdnlNodeIdFull client, td::uint32 ttl, + td::BufferSlice signature, DhtMember::PrintId print_id, adnl::AdnlNodeIdShort src, + DhtNodesList list, td::uint32 k, td::uint32 a, DhtNode self, bool client_only, + td::actor::ActorId node, td::actor::ActorId adnl, + td::Promise promise); + void send_queries(td::Result R); + void ready(td::Result R); + void start_up() override; + DhtMember::PrintId print_id() const { + return print_id_; + } +}; + +class DhtQueryRequestReversePing : public DhtQuery { + private: + td::Promise promise_; + td::BufferSlice query_; + + public: + DhtQueryRequestReversePing(adnl::AdnlNodeIdShort client, adnl::AdnlNode target, td::BufferSlice signature, + DhtMember::PrintId print_id, adnl::AdnlNodeIdShort src, DhtNodesList list, td::uint32 k, + td::uint32 a, DhtNode self, bool client_only, td::actor::ActorId node, + td::actor::ActorId adnl, td::Promise promise) + : DhtQuery(DhtMember::get_reverse_connection_key(client).compute_key_id(), print_id, src, std::move(list), k, a, + std::move(self), client_only, node, adnl) + , promise_(std::move(promise)) + , query_(create_serialize_tl_object(target.tl(), std::move(signature), + client.bits256_value(), k)) { + } + void send_one_query(adnl::AdnlNodeIdShort id) override; + void on_result(td::Result R, adnl::AdnlNodeIdShort dst); + void finish(DhtNodesList list) override; + std::string get_name() const override { + return "request remote ping"; + } +}; + inline td::StringBuilder &operator<<(td::StringBuilder &sb, const DhtQuery &dht) { sb << dht.print_id(); return sb; diff --git a/dht/dht.cpp b/dht/dht.cpp index 0d441427..e5a310e9 100644 --- a/dht/dht.cpp +++ b/dht/dht.cpp @@ -90,8 +90,11 @@ void DhtMemberImpl::start_up() { ton_api::dht_findValue::ID, ton_api::dht_store::ID, ton_api::dht_ping::ID, + ton_api::dht_registerReverseConnection::ID, + ton_api::dht_requestReversePing::ID, ton_api::dht_query::ID, - ton_api::dht_message::ID}; + ton_api::dht_message::ID, + ton_api::dht_requestReversePingCont::ID}; for (auto it : methods) { td::actor::send_closure(adnl_, &adnl::Adnl::subscribe, id_, adnl::Adnl::int_to_bytestring(it), @@ -131,8 +134,11 @@ void DhtMemberImpl::tear_down() { ton_api::dht_findValue::ID, ton_api::dht_store::ID, ton_api::dht_ping::ID, + ton_api::dht_registerReverseConnection::ID, + ton_api::dht_requestReversePing::ID, ton_api::dht_query::ID, - ton_api::dht_message::ID}; + ton_api::dht_message::ID, + ton_api::dht_requestReversePingCont::ID}; for (auto it : methods) { td::actor::send_closure(adnl_, &adnl::Adnl::unsubscribe, id_, adnl::Adnl::int_to_bytestring(it)); @@ -299,6 +305,61 @@ void DhtMemberImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::dht_getSig get_self_node(std::move(P)); } +static td::BufferSlice register_reverse_connection_to_sign(adnl::AdnlNodeIdShort client, adnl::AdnlNodeIdShort dht_id, + td::uint32 ttl) { + td::BufferSlice result(32 + 32 + 4); + td::MutableSlice s = result.as_slice(); + s.copy_from(client.as_slice()); + s.remove_prefix(32); + s.copy_from(dht_id.as_slice()); + s.remove_prefix(32); + s.copy_from(std::string(reinterpret_cast(&ttl), 4)); + return result; +} + +void DhtMemberImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::dht_registerReverseConnection &query, + td::Promise promise) { + td::uint32 ttl = query.ttl_, now = (td::uint32)td::Clocks::system(); + if (ttl <= now) { + return; + } + PublicKey pub{query.node_}; + adnl::AdnlNodeIdShort client_id{pub.compute_short_id()}; + td::BufferSlice to_sign = register_reverse_connection_to_sign(client_id, src, ttl); + TRY_RESULT_PROMISE(promise, encryptor, pub.create_encryptor()); + TRY_STATUS_PROMISE(promise, encryptor->check_signature(to_sign, query.signature_)); + DhtKeyId key_id = get_reverse_connection_key(client_id).compute_key_id(); + reverse_connections_[client_id] = ReverseConnection{src, key_id, td::Timestamp::at_unix(std::min(ttl, now + 300))}; + promise.set_value(create_serialize_tl_object()); +} + +void DhtMemberImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::dht_requestReversePing &query, + td::Promise promise) { + adnl::AdnlNodeIdShort client{query.client_}; + auto it = reverse_connections_.find(client); + if (it != reverse_connections_.end()) { + if (it->second.ttl_.is_in_past()) { + reverse_connections_.erase(it); + } else { + PublicKey pub{query.target_->id_}; + TRY_RESULT_PROMISE(promise, encryptor, pub.create_encryptor()); + TRY_STATUS_PROMISE(promise, + encryptor->check_signature(serialize_tl_object(query.target_, true), query.signature_)); + td::actor::send_closure(adnl_, &adnl::Adnl::send_message, id_, it->second.dht_node_, + create_serialize_tl_object( + std::move(query.target_), std::move(query.signature_), query.client_)); + promise.set_result(create_serialize_tl_object()); + return; + } + } + auto k = static_cast(query.k_); + if (k > max_k()) { + k = max_k(); + } + auto R = get_nearest_nodes(get_reverse_connection_key(client).compute_key_id(), k); + promise.set_value(create_serialize_tl_object(R.tl())); +} + void DhtMemberImpl::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Promise promise) { if (client_only_) { @@ -369,6 +430,27 @@ void DhtMemberImpl::receive_ping(DhtKeyId key, DhtNode result) { } void DhtMemberImpl::receive_message(adnl::AdnlNodeIdShort src, td::BufferSlice data) { + auto F = fetch_tl_object(data, true); + if (F.is_ok()) { + auto S = [&]() -> td::Status { + auto f = F.move_as_ok(); + adnl::AdnlNodeIdShort client{f->client_}; + if (!our_reverse_connections_.count(client)) { + return td::Status::Error(PSTRING() << ": unknown id for reverse ping: " << client); + } + TRY_RESULT_PREFIX(node, adnl::AdnlNode::create(f->target_), "failed to parse node: "); + TRY_RESULT_PREFIX(encryptor, node.pub_id().pubkey().create_encryptor(), "failed to create encryptor: "); + TRY_STATUS_PREFIX(encryptor->check_signature(serialize_tl_object(f->target_, true), f->signature_), + "invalid signature: "); + VLOG(DHT_INFO) << this << ": sending reverse ping to " << node.compute_short_id(); + td::actor::send_closure(adnl_, &adnl::Adnl::add_peer, client, node.pub_id(), node.addr_list()); + td::actor::send_closure(adnl_, &adnl::Adnl::send_message, client, node.compute_short_id(), td::BufferSlice()); + return td::Status::OK(); + }(); + if (S.is_error()) { + VLOG(DHT_INFO) << this << ": " << S; + } + } } void DhtMemberImpl::set_value(DhtValue value, td::Promise promise) { @@ -396,6 +478,70 @@ void DhtMemberImpl::get_value_in(DhtKeyId key, td::Promise result) { get_self_node(std::move(P)); } +void DhtMemberImpl::register_reverse_connection(adnl::AdnlNodeIdFull client, td::Promise promise) { + auto client_short = client.compute_short_id(); + td::uint32 ttl = (td::uint32)td::Clocks::system() + 300; + our_reverse_connections_.insert(client_short); + auto key_id = get_reverse_connection_key(client_short).compute_key_id(); + td::actor::send_closure(keyring_, &keyring::Keyring::sign_message, client_short.pubkey_hash(), + register_reverse_connection_to_sign(client_short, id_, ttl), + [=, print_id = print_id(), list = get_nearest_nodes(key_id, k_), SelfId = actor_id(this), + promise = std::move(promise)](td::Result R) mutable { + TRY_RESULT_PROMISE_PREFIX(promise, signature, std::move(R), "Failed to sign: "); + td::actor::send_closure(SelfId, &DhtMemberImpl::get_self_node, + [=, list = std::move(list), signature = std::move(signature), + promise = std::move(promise)](td::Result R) mutable { + R.ensure(); + td::actor::create_actor( + "RegisterReverseQuery", key_id, std::move(client), ttl, + std::move(signature), print_id, id_, std::move(list), k_, a_, + R.move_as_ok(), client_only_, SelfId, adnl_, + std::move(promise)) + .release(); + }); + }); +} + +void DhtMemberImpl::request_reverse_ping(adnl::AdnlNode target, adnl::AdnlNodeIdShort client, + td::Promise promise) { + auto pubkey_hash = target.compute_short_id().pubkey_hash(); + td::BufferSlice to_sign = serialize_tl_object(target.tl(), true); + td::actor::send_closure(keyring_, &keyring::Keyring::sign_message, pubkey_hash, std::move(to_sign), + [SelfId = actor_id(this), promise = std::move(promise), target = std::move(target), + client](td::Result R) mutable { + TRY_RESULT_PROMISE(promise, signature, std::move(R)); + td::actor::send_closure(SelfId, &DhtMemberImpl::request_reverse_ping_cont, + std::move(target), std::move(signature), client, + std::move(promise)); + }); +} + +void DhtMemberImpl::request_reverse_ping_cont(adnl::AdnlNode target, td::BufferSlice signature, + adnl::AdnlNodeIdShort client, td::Promise promise) { + auto it = reverse_connections_.find(client); + if (it != reverse_connections_.end()) { + if (it->second.ttl_.is_in_past()) { + reverse_connections_.erase(it); + } else { + td::actor::send_closure(adnl_, &adnl::Adnl::send_message, id_, it->second.dht_node_, + create_serialize_tl_object( + target.tl(), std::move(signature), client.bits256_value())); + promise.set_result(td::Unit()); + return; + } + } + auto key_id = get_reverse_connection_key(client).compute_key_id(); + get_self_node([=, target = std::move(target), signature = std::move(signature), promise = std::move(promise), + SelfId = actor_id(this), print_id = print_id(), list = get_nearest_nodes(key_id, k_), + client_only = client_only_](td::Result R) mutable { + R.ensure(); + td::actor::create_actor("RequestReversePing", client, std::move(target), + std::move(signature), print_id, id_, std::move(list), k_, a_, + R.move_as_ok(), client_only, SelfId, adnl_, std::move(promise)) + .release(); + }); +} + void DhtMemberImpl::check() { VLOG(DHT_INFO) << this << ": ping=" << ping_queries_ << " fnode=" << find_node_queries_ << " fvalue=" << find_value_queries_ << " store=" << store_queries_ @@ -454,6 +600,16 @@ void DhtMemberImpl::check() { } } } + if (reverse_connections_.size() > 0) { + auto it = reverse_connections_.upper_bound(last_check_reverse_conn_); + if (it == reverse_connections_.end()) { + it = reverse_connections_.begin(); + } + last_check_reverse_conn_ = it->first; + if (it->second.ttl_.is_in_past()) { + reverse_connections_.erase(it); + } + } if (republish_att_.is_in_past()) { auto it = our_values_.lower_bound(last_republish_key_); diff --git a/dht/dht.h b/dht/dht.h index eacb2e4b..d4c5edf7 100644 --- a/dht/dht.h +++ b/dht/dht.h @@ -54,6 +54,10 @@ class Dht : public td::actor::Actor { virtual void set_value(DhtValue key_value, td::Promise result) = 0; virtual void get_value(DhtKey key, td::Promise result) = 0; + virtual void register_reverse_connection(adnl::AdnlNodeIdFull client, td::Promise promise) = 0; + virtual void request_reverse_ping(adnl::AdnlNode target, adnl::AdnlNodeIdShort client, + td::Promise promise) = 0; + virtual void dump(td::StringBuilder &sb) const = 0; virtual ~Dht() = default; diff --git a/dht/dht.hpp b/dht/dht.hpp index b8d73c8e..5a4c4cfb 100644 --- a/dht/dht.hpp +++ b/dht/dht.hpp @@ -101,6 +101,10 @@ class DhtMember : public Dht { virtual void get_self_node(td::Promise promise) = 0; virtual PrintId print_id() const = 0; + + static DhtKey get_reverse_connection_key(adnl::AdnlNodeIdShort node) { + return DhtKey{node.pubkey_hash(), "address", 0}; + } }; inline td::StringBuilder &operator<<(td::StringBuilder &sb, const DhtMember::PrintId &id) { diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index 891fd629..524d2592 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -68,6 +68,7 @@ adnl.address.udp6 ip:int128 port:int = adnl.Address; //adnl.address.tcp6 ip:int128 port:int = adnl.Address; adnl.address.tunnel to:int256 pubkey:PublicKey = adnl.Address; +adnl.address.reverse = adnl.Address; adnl.addressList addrs:(vector adnl.Address) version:int reinit_date:int priority:int expire_at:int = adnl.AddressList; @@ -183,8 +184,12 @@ dht.pong random_id:long = dht.Pong; dht.valueNotFound nodes:dht.nodes = dht.ValueResult; dht.valueFound value:dht.Value = dht.ValueResult; +dht.clientNotFound nodes:dht.nodes = dht.ReversePingResult; +dht.reversePingOk = dht.ReversePingResult; + dht.stored = dht.Stored; dht.message node:dht.node = dht.Message; +dht.requestReversePingCont target:adnl.Node signature:bytes client:int256 = dht.RequestReversePingCont; dht.db.bucket nodes:dht.nodes = dht.db.Bucket; dht.db.key.bucket id:int = dht.db.Key; @@ -196,6 +201,8 @@ dht.store value:dht.value = dht.Stored; dht.findNode key:int256 k:int = dht.Nodes; dht.findValue key:int256 k:int = dht.ValueResult; dht.getSignedAddressList = dht.Node; +dht.registerReverseConnection node:PublicKey ttl:int signature:bytes = dht.Stored; +dht.requestReversePing target:adnl.Node signature:bytes client:int256 k:int = dht.ReversePingResult; dht.query node:dht.node = True; diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index d8940288..0eb060b0 100644 Binary files a/tl/generate/scheme/ton_api.tlo and b/tl/generate/scheme/ton_api.tlo differ