1 // 2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 // 7 #include "td/telegram/net/PublicRsaKeyWatchdog.h" 8 9 #include "td/telegram/Global.h" 10 #include "td/telegram/net/NetQueryCreator.h" 11 #include "td/telegram/TdDb.h" 12 #include "td/telegram/telegram_api.h" 13 #include "td/telegram/Version.h" 14 15 #include "td/mtproto/RSA.h" 16 17 #include "td/utils/logging.h" 18 #include "td/utils/Time.h" 19 20 namespace td { 21 PublicRsaKeyWatchdog(ActorShared<> parent)22PublicRsaKeyWatchdog::PublicRsaKeyWatchdog(ActorShared<> parent) : parent_(std::move(parent)) { 23 } 24 add_public_rsa_key(std::shared_ptr<PublicRsaKeyShared> key)25void PublicRsaKeyWatchdog::add_public_rsa_key(std::shared_ptr<PublicRsaKeyShared> key) { 26 class Listener final : public PublicRsaKeyShared::Listener { 27 public: 28 explicit Listener(ActorId<PublicRsaKeyWatchdog> parent) : parent_(std::move(parent)) { 29 } 30 bool notify() final { 31 send_event(parent_, Event::yield()); 32 return parent_.is_alive(); 33 } 34 35 private: 36 ActorId<PublicRsaKeyWatchdog> parent_; 37 }; 38 39 key->add_listener(make_unique<Listener>(actor_id(this))); 40 sync_key(key); 41 keys_.push_back(std::move(key)); 42 loop(); 43 } 44 start_up()45void PublicRsaKeyWatchdog::start_up() { 46 flood_control_.add_limit(1, 1); 47 flood_control_.add_limit(2, 60); 48 flood_control_.add_limit(3, 2 * 60); 49 50 string version = G()->td_db()->get_binlog_pmc()->get("cdn_config_version"); 51 current_version_ = to_string(MTPROTO_LAYER); 52 if (version != current_version_) { 53 G()->td_db()->get_binlog_pmc()->erase("cdn_config" + version); 54 } else { 55 sync(BufferSlice(G()->td_db()->get_binlog_pmc()->get("cdn_config" + version))); 56 } 57 CHECK(keys_.empty()); 58 } 59 loop()60void PublicRsaKeyWatchdog::loop() { 61 if (has_query_) { 62 return; 63 } 64 if (Time::now_cached() < flood_control_.get_wakeup_at()) { 65 set_timeout_in(flood_control_.get_wakeup_at() - Time::now_cached() + 0.01); 66 return; 67 } 68 bool ok = true; 69 for (auto &key : keys_) { 70 if (!key->has_keys()) { 71 ok = false; 72 } 73 } 74 if (ok) { 75 return; 76 } 77 flood_control_.add_event(static_cast<int32>(Time::now_cached())); 78 has_query_ = true; 79 auto query = G()->net_query_creator().create(telegram_api::help_getCdnConfig()); 80 query->total_timeout_limit_ = 60 * 60 * 24; 81 G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this)); 82 } 83 on_result(NetQueryPtr net_query)84void PublicRsaKeyWatchdog::on_result(NetQueryPtr net_query) { 85 has_query_ = false; 86 yield(); 87 if (net_query->is_error()) { 88 LOG(ERROR) << "Receive error for GetCdnConfig: " << net_query->move_as_error(); 89 loop(); 90 return; 91 } 92 93 auto buf = net_query->move_as_ok(); 94 G()->td_db()->get_binlog_pmc()->set("cdn_config_version", current_version_); 95 G()->td_db()->get_binlog_pmc()->set("cdn_config" + current_version_, buf.as_slice().str()); 96 sync(std::move(buf)); 97 } 98 sync(BufferSlice cdn_config_serialized)99void PublicRsaKeyWatchdog::sync(BufferSlice cdn_config_serialized) { 100 if (cdn_config_serialized.empty()) { 101 loop(); 102 return; 103 } 104 auto r_keys = fetch_result<telegram_api::help_getCdnConfig>(cdn_config_serialized); 105 if (r_keys.is_error()) { 106 LOG(WARNING) << "Failed to deserialize help_getCdnConfig (probably not a problem) " << r_keys.error(); 107 loop(); 108 return; 109 } 110 cdn_config_ = r_keys.move_as_ok(); 111 if (keys_.empty()) { 112 LOG(INFO) << "Load " << to_string(cdn_config_); 113 } else { 114 LOG(INFO) << "Receive " << to_string(cdn_config_); 115 for (auto &key : keys_) { 116 sync_key(key); 117 } 118 } 119 } 120 sync_key(std::shared_ptr<PublicRsaKeyShared> & key)121void PublicRsaKeyWatchdog::sync_key(std::shared_ptr<PublicRsaKeyShared> &key) { 122 if (!cdn_config_) { 123 return; 124 } 125 for (auto &config_key : cdn_config_->public_keys_) { 126 if (key->dc_id().get_raw_id() == config_key->dc_id_) { 127 auto r_rsa = mtproto::RSA::from_pem_public_key(config_key->public_key_); 128 if (r_rsa.is_error()) { 129 LOG(ERROR) << r_rsa.error(); 130 continue; 131 } 132 LOG(INFO) << "Add CDN " << key->dc_id() << " key with fingerprint " << r_rsa.ok().get_fingerprint(); 133 key->add_rsa(r_rsa.move_as_ok()); 134 } 135 } 136 } 137 138 } // namespace td 139