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)22 PublicRsaKeyWatchdog::PublicRsaKeyWatchdog(ActorShared<> parent) : parent_(std::move(parent)) {
23 }
24 
add_public_rsa_key(std::shared_ptr<PublicRsaKeyShared> key)25 void 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()45 void 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()60 void 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)84 void 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)99 void 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)121 void 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