/* This file is part of TON Blockchain Library. TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. TON Blockchain Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . Copyright 2017-2019 Telegram Systems LLP */ #include "adnl-ext-client.hpp" #include "adnl-ext-client.h" namespace ton { namespace adnl { void AdnlExtClientImpl::alarm() { if (is_closing_) { return; } if (conn_.empty() || !conn_.is_alive()) { next_create_at_ = td::Timestamp::in(10.0); alarm_timestamp() = next_create_at_; auto fd = td::SocketFd::open(dst_addr_); if (fd.is_error()) { LOG(INFO) << "failed to connect to " << dst_addr_ << ": " << fd.move_as_error(); return; } class Cb : public AdnlExtConnection::Callback { private: td::actor::ActorId id_; public: void on_ready(td::actor::ActorId conn) { td::actor::send_closure(id_, &AdnlExtClientImpl::conn_ready, conn); } void on_close(td::actor::ActorId conn) { td::actor::send_closure(id_, &AdnlExtClientImpl::conn_stopped, conn); } Cb(td::actor::ActorId id) : id_(id) { } }; conn_ = td::actor::create_actor(td::actor::ActorOptions().with_name("outconn").with_poll(), fd.move_as_ok(), std::make_unique(actor_id(this)), dst_, local_id_, actor_id(this)); } } void AdnlExtClientImpl::hangup() { conn_ = {}; is_closing_ = true; ref_cnt_--; try_stop(); } void AdnlExtClientImpl::try_stop() { if (is_closing_ && ref_cnt_ == 0 && out_queries_.empty()) { stop(); } } td::Status AdnlOutboundConnection::process_custom_packet(td::BufferSlice &data, bool &processed) { if (data.size() == 12) { auto F = fetch_tl_object(data.clone(), true); if (F.is_ok()) { processed = true; return td::Status::OK(); } } if (!local_id_.empty() && nonce_.size() != 0) { auto F = fetch_tl_object(data.clone(), true); if (F.is_ok()) { auto f = F.move_as_ok(); if (f->nonce_.size() == 0 || f->nonce_.size() > 512) { return td::Status::Error(ErrorCode::protoviolation, "bad nonce size"); } td::SecureString ss{nonce_.size() + f->nonce_.size()}; ss.as_mutable_slice().copy_from(nonce_.as_slice()); ss.as_mutable_slice().remove_prefix(nonce_.size()).copy_from(f->nonce_.as_slice()); TRY_RESULT(dec, local_id_.create_decryptor()); TRY_RESULT(B, dec->sign(ss.as_slice())); auto obj = create_tl_object(local_id_.compute_public_key().tl(), std::move(B)); send(serialize_tl_object(obj, true)); nonce_.clear(); processed = true; authorization_complete_ = true; return td::Status::OK(); } } return td::Status::OK(); } void AdnlOutboundConnection::start_up() { AdnlExtConnection::start_up(); auto X = dst_.pubkey().create_encryptor(); if (X.is_error()) { LOG(ERROR) << "failed to init encryptor: " << X.move_as_error(); stop(); return; } auto enc = X.move_as_ok(); td::BufferSlice d{256}; auto id = dst_.compute_short_id(); auto S = d.as_slice(); S.copy_from(id.as_slice()); S.remove_prefix(32); S.truncate(256 - 64 - 32); td::Random::secure_bytes(S); init_crypto(S); auto R = enc->encrypt(S); if (R.is_error()) { LOG(ERROR) << "failed to encrypt: " << R.move_as_error(); stop(); return; } auto data = R.move_as_ok(); LOG_CHECK(data.size() == 256 - 32) << "size=" << data.size(); S = d.as_slice(); S.remove_prefix(32); CHECK(S.size() == data.size()); S.copy_from(data.as_slice()); send_uninit(std::move(d)); if (!local_id_.empty()) { nonce_ = td::SecureString{32}; td::Random::secure_bytes(nonce_.as_mutable_slice()); auto obj = create_tl_object(td::BufferSlice{nonce_.as_slice()}); send(serialize_tl_object(obj, true)); } } void AdnlExtClientImpl::check_ready(td::Promise promise) { if (conn_.empty() || !conn_.is_alive()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not ready")); return; } td::actor::send_closure(td::actor::ActorId{conn_.get()}, &AdnlExtConnection::check_ready_async, std::move(promise)); } td::actor::ActorOwn AdnlExtClient::create(AdnlNodeIdFull dst, td::IPAddress dst_addr, std::unique_ptr callback) { return td::actor::create_actor("extclient", std::move(dst), dst_addr, std::move(callback)); } td::actor::ActorOwn AdnlExtClient::create(AdnlNodeIdFull dst, PrivateKey local_id, td::IPAddress dst_addr, std::unique_ptr callback) { return td::actor::create_actor("extclient", std::move(dst), std::move(local_id), dst_addr, std::move(callback)); } td::Status AdnlOutboundConnection::process_packet(td::BufferSlice data) { TRY_RESULT(F, fetch_tl_object(std::move(data), true)); td::actor::send_closure(ext_client_, &AdnlExtClientImpl::answer_query, F->query_id_, std::move(F->answer_)); return td::Status::OK(); } void AdnlExtMultiClientImpl::start_up() { for (auto &id : ids_) { add_server(id.first, id.second, [](td::Result R) {}); } ids_.clear(); } void AdnlExtMultiClientImpl::add_server(AdnlNodeIdFull dst, td::IPAddress dst_addr, td::Promise promise) { for (auto &c : clients_) { if (c.second->addr == dst_addr) { promise.set_error(td::Status::Error(ErrorCode::error, "duplicate ip")); return; } } auto g = ++generation_; auto cli = std::make_unique(AdnlExtClient::create(dst, dst_addr, make_callback(g)), dst, dst_addr, g); clients_[g] = std::move(cli); } void AdnlExtMultiClientImpl::del_server(td::IPAddress dst_addr, td::Promise promise) { for (auto &c : clients_) { if (c.second->addr == dst_addr) { if (c.second->ready) { total_ready_--; if (!total_ready_) { callback_->on_stop_ready(); } } clients_.erase(c.first); promise.set_value(td::Unit()); return; } } promise.set_error(td::Status::Error(ErrorCode::error, "ip not found")); } void AdnlExtMultiClientImpl::send_query(std::string name, td::BufferSlice data, td::Timestamp timeout, td::Promise promise) { if (total_ready_ == 0) { promise.set_error(td::Status::Error(ErrorCode::notready, "conn not ready")); return; } std::vector vec; for (auto &c : clients_) { if (c.second->ready) { vec.push_back(c.first); } } CHECK(vec.size() == total_ready_); auto &c = clients_[vec[td::Random::fast(0, td::narrow_cast(vec.size() - 1))]]; td::actor::send_closure(c->client, &AdnlExtClient::send_query, std::move(name), std::move(data), timeout, std::move(promise)); } void AdnlExtMultiClientImpl::client_ready(td::uint32 idx, bool value) { auto it = clients_.find(idx); if (it == clients_.end()) { return; } auto &c = it->second; if (c->ready == value) { return; } c->ready = value; if (value) { total_ready_++; if (total_ready_ == 1) { callback_->on_ready(); } } else { total_ready_--; if (total_ready_ == 0) { callback_->on_stop_ready(); } } } std::unique_ptr AdnlExtMultiClientImpl::make_callback(td::uint32 g) { class Cb : public Callback { public: Cb(td::actor::ActorId id, td::uint32 idx) : id_(id), idx_(idx) { } void on_ready() override { td::actor::send_closure(id_, &AdnlExtMultiClientImpl::client_ready, idx_, true); } void on_stop_ready() override { td::actor::send_closure(id_, &AdnlExtMultiClientImpl::client_ready, idx_, false); } private: td::actor::ActorId id_; td::uint32 idx_; }; return std::make_unique(actor_id(this), g); } td::actor::ActorOwn AdnlExtMultiClient::create( std::vector> ids, std::unique_ptr callback) { return td::actor::create_actor("extmulticlient", std::move(ids), std::move(callback)); } } // namespace adnl } // namespace ton