diff --git a/rldp-http-proxy/CMakeLists.txt b/rldp-http-proxy/CMakeLists.txt index 8fa4ac55..7ba66ccb 100644 --- a/rldp-http-proxy/CMakeLists.txt +++ b/rldp-http-proxy/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) -add_executable(rldp-http-proxy rldp-http-proxy.cpp) +add_executable(rldp-http-proxy rldp-http-proxy.cpp DNSResolver.h TonlibClient.h TonlibClient.cpp DNSResolver.cpp) target_include_directories(rldp-http-proxy PUBLIC $) target_link_libraries(rldp-http-proxy PRIVATE tonhttp rldp dht tonlib git) diff --git a/rldp-http-proxy/DNSResolver.cpp b/rldp-http-proxy/DNSResolver.cpp new file mode 100644 index 00000000..b6dc1bba --- /dev/null +++ b/rldp-http-proxy/DNSResolver.cpp @@ -0,0 +1,104 @@ +/* + This file is part of TON Blockchain source code. + + TON Blockchain is free software; you can redistribute it and/or + modify it under the terms of the GNU 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TON Blockchain. If not, see . + + In addition, as a special exception, the copyright holders give permission + to link the code of portions of this program with the OpenSSL library. + You must obey the GNU General Public License in all respects for all + of the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. If you delete this exception statement + from all source files in the program, then also delete it here. +*/ +#include "DNSResolver.h" +#include "td/utils/overloaded.h" + +static const double CACHE_TIMEOUT_HARD = 300.0; +static const double CACHE_TIMEOUT_SOFT = 270.0; + +DNSResolver::DNSResolver(td::actor::ActorId tonlib_client) : tonlib_client_(std::move(tonlib_client)) { +} + +void DNSResolver::start_up() { + auto obj = tonlib_api::make_object(); + auto P = td::PromiseCreator::lambda([](td::Result>) {}); + td::actor::send_closure(tonlib_client_, &TonlibClient::send_request, std::move(obj), std::move(P)); +} + +void DNSResolver::resolve(std::string host, td::Promise promise) { + auto it = cache_.find(host); + if (it != cache_.end()) { + const CacheEntry &entry = it->second; + double now = td::Time::now(); + if (now < entry.created_at_ + CACHE_TIMEOUT_HARD) { + promise.set_result(entry.id_); + promise.reset(); + if (now < entry.created_at_ + CACHE_TIMEOUT_SOFT) { + return; + } + } + } + + auto obj = tonlib_api::make_object(nullptr, host, 0, 16); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), promise = std::move(promise), host = std::move(host)]( + td::Result> R) mutable { + if (R.is_error()) { + if (promise) { + promise.set_result(R.move_as_error()); + } + } else { + auto v = R.move_as_ok(); + auto obj = dynamic_cast(v.get()); + if (obj == nullptr) { + promise.set_result(td::Status::Error("invalid response from tonlib")); + return; + } + ton::adnl::AdnlNodeIdShort id; + td::uint32 cnt = 0; + for (auto &e : obj->entries_) { + tonlib_api::downcast_call(*e->entry_.get(), + td::overloaded( + [&](tonlib_api::dns_entryDataAdnlAddress &x) { + if (td::Random::fast(0, cnt) == 0) { + auto R = ton::adnl::AdnlNodeIdShort::parse(x.adnl_address_->adnl_address_); + if (R.is_ok()) { + id = R.move_as_ok(); + cnt++; + } + } + }, + [&](auto &x) {})); + } + if (cnt == 0) { + if (promise) { + promise.set_error(td::Status::Error("no DNS entries")); + } + } else { + td::actor::send_closure(SelfId, &DNSResolver::save_to_cache, std::move(host), id); + if (promise) { + promise.set_result(id); + } + } + } + }); + td::actor::send_closure(tonlib_client_, &TonlibClient::send_request, std::move(obj), std::move(P)); +} + +void DNSResolver::save_to_cache(std::string host, ton::adnl::AdnlNodeIdShort id) { + CacheEntry &entry = cache_[host]; + entry.id_ = id; + entry.created_at_ = td::Time::now(); +} diff --git a/rldp-http-proxy/DNSResolver.h b/rldp-http-proxy/DNSResolver.h new file mode 100644 index 00000000..f87b5e5a --- /dev/null +++ b/rldp-http-proxy/DNSResolver.h @@ -0,0 +1,49 @@ +/* + This file is part of TON Blockchain source code. + + TON Blockchain is free software; you can redistribute it and/or + modify it under the terms of the GNU 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TON Blockchain. If not, see . + + In addition, as a special exception, the copyright holders give permission + to link the code of portions of this program with the OpenSSL library. + You must obey the GNU General Public License in all respects for all + of the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. If you delete this exception statement + from all source files in the program, then also delete it here. +*/ +#pragma once +#include "td/actor/actor.h" +#include "TonlibClient.h" +#include "adnl/adnl.h" +#include "td/actor/PromiseFuture.h" + +class DNSResolver : public td::actor::Actor { + public: + explicit DNSResolver(td::actor::ActorId tonlib_client); + + void start_up() override; + void resolve(std::string host, td::Promise promise); + + private: + void save_to_cache(std::string host, ton::adnl::AdnlNodeIdShort id); + + td::actor::ActorId tonlib_client_; + + struct CacheEntry { + ton::adnl::AdnlNodeIdShort id_; + double created_at_; + }; + std::map cache_; +}; \ No newline at end of file diff --git a/rldp-http-proxy/TonlibClient.cpp b/rldp-http-proxy/TonlibClient.cpp new file mode 100644 index 00000000..a8e18b81 --- /dev/null +++ b/rldp-http-proxy/TonlibClient.cpp @@ -0,0 +1,72 @@ +/* + This file is part of TON Blockchain source code. + + TON Blockchain is free software; you can redistribute it and/or + modify it under the terms of the GNU 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TON Blockchain. If not, see . + + In addition, as a special exception, the copyright holders give permission + to link the code of portions of this program with the OpenSSL library. + You must obey the GNU General Public License in all respects for all + of the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. If you delete this exception statement + from all source files in the program, then also delete it here. +*/ +#include "TonlibClient.h" + +TonlibClient::TonlibClient(ton::tl_object_ptr options) : options_(std::move(options)) { +} + +void TonlibClient::start_up() { + class Cb : public tonlib::TonlibCallback { + public: + explicit Cb(td::actor::ActorId self_id) : self_id_(self_id) { + } + void on_result(std::uint64_t id, tonlib_api::object_ptr result) override { + td::actor::send_closure(self_id_, &TonlibClient::receive_request_result, id, std::move(result)); + } + void on_error(std::uint64_t id, tonlib_api::object_ptr error) override { + td::actor::send_closure(self_id_, &TonlibClient::receive_request_result, id, + td::Status::Error(error->code_, std::move(error->message_))); + } + + private: + td::actor::ActorId self_id_; + }; + + tonlib_client_ = td::actor::create_actor("tonlibclient", td::make_unique(actor_id(this))); + auto init = tonlib_api::make_object(std::move(options_)); + auto P = td::PromiseCreator::lambda([](td::Result> R) mutable { + R.ensure(); + }); + send_request(std::move(init), std::move(P)); +} + +void TonlibClient::send_request(tonlib_api::object_ptr obj, + td::Promise> promise) { + auto id = next_request_id_++; + CHECK(requests_.emplace(id, std::move(promise)).second); + td::actor::send_closure(tonlib_client_, &tonlib::TonlibClient::request, id, std::move(obj)); +} + +void TonlibClient::receive_request_result(td::uint64 id, td::Result> R) { + if (id == 0) { + return; + } + auto it = requests_.find(id); + CHECK(it != requests_.end()); + auto promise = std::move(it->second); + requests_.erase(it); + promise.set_result(std::move(R)); +} \ No newline at end of file diff --git a/rldp-http-proxy/TonlibClient.h b/rldp-http-proxy/TonlibClient.h new file mode 100644 index 00000000..0b75c6c0 --- /dev/null +++ b/rldp-http-proxy/TonlibClient.h @@ -0,0 +1,47 @@ +/* + This file is part of TON Blockchain source code. + + TON Blockchain is free software; you can redistribute it and/or + modify it under the terms of the GNU 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 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with TON Blockchain. If not, see . + + In addition, as a special exception, the copyright holders give permission + to link the code of portions of this program with the OpenSSL library. + You must obey the GNU General Public License in all respects for all + of the code used other than OpenSSL. If you modify file(s) with this + exception, you may extend this exception to your version of the file(s), + but you are not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. If you delete this exception statement + from all source files in the program, then also delete it here. +*/ +#pragma once +#include "td/actor/actor.h" +#include "auto/tl/tonlib_api.hpp" +#include "tonlib/tonlib/TonlibClient.h" + +class TonlibClient : public td::actor::Actor { + public: + explicit TonlibClient(ton::tl_object_ptr options); + + void start_up() override; + + void send_request(tonlib_api::object_ptr obj, + td::Promise> promise); + + private: + void receive_request_result(td::uint64 id, td::Result> R); + + ton::tl_object_ptr options_; + td::actor::ActorOwn tonlib_client_; + std::map>> requests_; + td::uint64 next_request_id_{1}; +}; diff --git a/rldp-http-proxy/rldp-http-proxy.cpp b/rldp-http-proxy/rldp-http-proxy.cpp index 98088713..a5faa002 100644 --- a/rldp-http-proxy/rldp-http-proxy.cpp +++ b/rldp-http-proxy/rldp-http-proxy.cpp @@ -55,6 +55,9 @@ #include "td/utils/BufferedFd.h" #include "common/delay.h" +#include "TonlibClient.h" +#include "DNSResolver.h" + #if TD_DARWIN || TD_LINUX #include #endif @@ -126,7 +129,7 @@ class HttpRemote : public td::actor::Actor { td::actor::ActorOwn client_; }; -td::BufferSlice create_error_response(const std::string& proto_version, int code, const std::string& reason) { +td::BufferSlice create_error_response(const std::string &proto_version, int code, const std::string &reason) { return ton::create_serialize_tl_object( proto_version, code, reason, std::vector>(), true); } @@ -328,8 +331,8 @@ class HttpRldpPayloadSender : public td::actor::Actor { size_t watermark_; }; - payload_->add_callback(std::make_unique(actor_id(this), - is_tunnel_ ? 1 : ton::http::HttpRequest::low_watermark())); + payload_->add_callback( + std::make_unique(actor_id(this), is_tunnel_ ? 1 : ton::http::HttpRequest::low_watermark())); alarm_timestamp() = td::Timestamp::in(is_tunnel_ ? 60.0 : 10.0); } @@ -352,9 +355,11 @@ class HttpRldpPayloadSender : public td::actor::Actor { answer_query(); } else if (!active_timer_) { active_timer_ = true; - ton::delay_action([SelfId = actor_id(this)]() { - td::actor::send_closure(SelfId, &HttpRldpPayloadSender::try_answer_query, true); - }, td::Timestamp::in(0.001)); + ton::delay_action( + [SelfId = actor_id(this)]() { + td::actor::send_closure(SelfId, &HttpRldpPayloadSender::try_answer_query, true); + }, + td::Timestamp::in(0.001)); } } @@ -437,7 +442,6 @@ class HttpRldpPayloadSender : public td::actor::Actor { td::Bits256 id_; - bool sent_ = false; td::int32 seqno_ = 0; ton::adnl::AdnlNodeIdShort local_id_; @@ -458,7 +462,7 @@ class TcpToRldpRequestSender : public td::actor::Actor { std::shared_ptr request_payload, td::Promise, std::shared_ptr>> promise, td::actor::ActorId adnl, td::actor::ActorId dht, - td::actor::ActorId rldp, td::actor::ActorId proxy) + td::actor::ActorId rldp, td::actor::ActorId dns_resolver) : local_id_(local_id) , host_(std::move(host)) , request_(std::move(request)) @@ -467,7 +471,7 @@ class TcpToRldpRequestSender : public td::actor::Actor { , adnl_(adnl) , dht_(dht) , rldp_(rldp) - , proxy_(proxy) { + , dns_resolver_(dns_resolver) { } void start_up() override { resolve(); @@ -503,8 +507,8 @@ class TcpToRldpRequestSender : public td::actor::Actor { return; } auto f = F.move_as_ok(); - auto R = ton::http::HttpResponse::create( - f->http_version_, f->status_code_, f->reason_, f->no_payload_, true, is_tunnel() && f->status_code_ == 200); + auto R = ton::http::HttpResponse::create(f->http_version_, f->status_code_, f->reason_, f->no_payload_, true, + is_tunnel() && f->status_code_ == 200); if (R.is_error()) { abort_query(R.move_as_error()); return; @@ -575,7 +579,7 @@ class TcpToRldpRequestSender : public td::actor::Actor { td::actor::ActorId adnl_; td::actor::ActorId dht_; td::actor::ActorId rldp_; - td::actor::ActorId proxy_; + td::actor::ActorId dns_resolver_; std::unique_ptr response_; std::shared_ptr response_payload_; @@ -718,9 +722,11 @@ class RldpTcpTunnel : public td::actor::Actor, private td::ObserverBase { if (!from_timer && !close_ && !allow_empty && input.size() < ton::http::HttpRequest::low_watermark()) { if (!active_timer_) { active_timer_ = true; - ton::delay_action([SelfId = actor_id(this)]() { - td::actor::send_closure(SelfId, &RldpTcpTunnel::answer_query, false, true); - }, td::Timestamp::in(0.001)); + ton::delay_action( + [SelfId = actor_id(this)]() { + td::actor::send_closure(SelfId, &RldpTcpTunnel::answer_query, false, true); + }, + td::Timestamp::in(0.001)); } return; } @@ -853,8 +859,7 @@ class RldpToTcpRequestSender : public td::actor::Actor { class RldpHttpProxy : public td::actor::Actor { public: - RldpHttpProxy() { - } + RldpHttpProxy() = default; void set_port(td::uint16 port) { if (port_) { @@ -881,27 +886,6 @@ class RldpHttpProxy : public td::actor::Actor { hosts_[host].ports_[port].remote_addr_ = remote; } - void receive_request_result(td::uint64 id, td::Result> R) { - if (id == 0) { - return; - } - auto it = tonlib_requests_.find(id); - CHECK(it != tonlib_requests_.end()); - auto promise = std::move(it->second); - tonlib_requests_.erase(it); - - promise.set_result(std::move(R)); - } - - void send_tonlib_request(tonlib_api::object_ptr obj, - td::Promise> promise) { - auto id = next_tonlib_requests_id_++; - - CHECK(tonlib_requests_.emplace(id, std::move(promise)).second); - - td::actor::send_closure(tonlib_client_, &tonlib::TonlibClient::request, id, std::move(obj)); - } - td::Status load_global_config() { TRY_RESULT_PREFIX(conf_data, td::read_file(global_config_), "failed to read: "); TRY_RESULT_PREFIX(conf_json, td::json_decode(conf_data.as_slice()), "failed to parse json: "); @@ -916,24 +900,6 @@ class RldpHttpProxy : public td::actor::Actor { TRY_RESULT_PREFIX(dht, ton::dht::Dht::create_global_config(std::move(conf.dht_)), "bad [dht] section: "); dht_config_ = std::move(dht); - class Cb : public tonlib::TonlibCallback { - public: - Cb(td::actor::ActorId self_id) : self_id_(self_id) { - } - void on_result(std::uint64_t id, tonlib_api::object_ptr result) override { - td::actor::send_closure(self_id_, &RldpHttpProxy::receive_request_result, id, std::move(result)); - } - void on_error(std::uint64_t id, tonlib_api::object_ptr error) override { - td::actor::send_closure(self_id_, &RldpHttpProxy::receive_request_result, id, - td::Status::Error(error->code_, std::move(error->message_))); - } - - private: - td::actor::ActorId self_id_; - }; - - tonlib_client_ = td::actor::create_actor("tonlibclient", td::make_unique(actor_id(this))); - return td::Status::OK(); } @@ -972,7 +938,7 @@ class RldpHttpProxy : public td::actor::Actor { { auto S = load_global_config(); if (S.is_error()) { - LOG(INFO) << S; + LOG(ERROR) << S; std::_Exit(2); } } @@ -999,19 +965,15 @@ class RldpHttpProxy : public td::actor::Actor { }); td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, x.pubkey_hash(), std::move(Q)); } - auto Q = td::PromiseCreator::lambda( - [promise = ig.get_promise()](td::Result> R) mutable { - R.ensure(); - promise.set_value(td::Unit()); - }); auto conf_dataR = td::read_file(global_config_); conf_dataR.ensure(); - auto req = tonlib_api::make_object(tonlib_api::make_object( + auto tonlib_options = tonlib_api::make_object( tonlib_api::make_object(conf_dataR.move_as_ok().as_slice().str(), "", false, false), - tonlib_api::make_object())); - send_tonlib_request(std::move(req), std::move(Q)); + tonlib_api::make_object()); + tonlib_client_ = td::actor::create_actor("tonlibclient", std::move(tonlib_options)); + dns_resolver_ = td::actor::create_actor("dnsresolver", tonlib_client_.get()); } void run_cont() { @@ -1178,7 +1140,7 @@ class RldpHttpProxy : public td::actor::Actor { td::actor::create_actor("outboundreq", local_id_, host, std::move(request), std::move(payload), std::move(promise), adnl_.get(), dht_.get(), - rldp_.get(), actor_id(this)) + rldp_.get(), dns_resolver_.get()) .release(); } @@ -1326,7 +1288,6 @@ class RldpHttpProxy : public td::actor::Actor { ton::adnl::AdnlNodeIdShort dht_id_; td::actor::ActorOwn server_; - std::map dns_; std::map hosts_; td::actor::ActorOwn keyring_; @@ -1340,9 +1301,8 @@ class RldpHttpProxy : public td::actor::Actor { std::string db_root_ = "."; bool proxy_all_ = false; - td::actor::ActorOwn tonlib_client_; - std::map>> tonlib_requests_; - td::uint64 next_tonlib_requests_id_{1}; + td::actor::ActorOwn tonlib_client_; + td::actor::ActorOwn dns_resolver_; }; void TcpToRldpRequestSender::resolve() { @@ -1357,63 +1317,15 @@ void TcpToRldpRequestSender::resolve() { resolved(R.move_as_ok()); return; } - if (false) { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { - if (R.is_error()) { - td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, R.move_as_error()); - return; - } - auto value = R.move_as_ok(); - if (value.value().size() != 32) { - td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, td::Status::Error("bad value in dht")); - return; - } - - ton::PublicKeyHash h{value.value().as_slice()}; - td::actor::send_closure(SelfId, &TcpToRldpRequestSender::resolved, ton::adnl::AdnlNodeIdShort{h}); - }); - - ton::PublicKey key = ton::pubkeys::Unenc{"http." + host_}; - ton::dht::DhtKey dht_key{key.compute_short_id(), "http." + host_, 0}; - td::actor::send_closure(dht_, &ton::dht::Dht::get_value, std::move(dht_key), std::move(P)); - } else { - td::Bits256 category = td::sha256_bits256(td::Slice("site", 4)); - auto obj = tonlib_api::make_object(nullptr, host_, category, 16); - - auto P = - td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { - if (R.is_error()) { - td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, - R.move_as_error_prefix("failed to resolve: ")); - } else { - auto v = R.move_as_ok(); - auto obj = static_cast(v.get()); - ton::adnl::AdnlNodeIdShort id; - td::uint32 cnt = 0; - for (auto &e : obj->entries_) { - tonlib_api::downcast_call( - *e->entry_.get(), td::overloaded( - [&](tonlib_api::dns_entryDataAdnlAddress &x) { - if (td::Random::fast(0, cnt) == 0) { - auto R = ton::adnl::AdnlNodeIdShort::parse(x.adnl_address_->adnl_address_); - if (R.is_ok()) { - id = R.move_as_ok(); - cnt++; - } - } - }, - [&](auto &x) {})); - } - if (cnt == 0) { - td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, - td::Status::Error(ton::ErrorCode::notready, "failed to resolve")); - } else { - td::actor::send_closure(SelfId, &TcpToRldpRequestSender::resolved, id); - } - } - }); - td::actor::send_closure(proxy_, &RldpHttpProxy::send_tonlib_request, std::move(obj), std::move(P)); - } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, + R.move_as_error_prefix("failed to resolve: ")); + } else { + td::actor::send_closure(SelfId, &TcpToRldpRequestSender::resolved, R.move_as_ok()); + } + }); + td::actor::send_closure(dns_resolver_, &DNSResolver::resolve, host_, std::move(P)); } int main(int argc, char *argv[]) {