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/ConfigManager.h"
8 #include "td/telegram/net/DcId.h"
9 #include "td/telegram/net/PublicRsaKeyShared.h"
10 #include "td/telegram/net/Session.h"
11 #include "td/telegram/NotificationManager.h"
12 
13 #include "td/mtproto/AuthData.h"
14 #include "td/mtproto/DhCallback.h"
15 #include "td/mtproto/DhHandshake.h"
16 #include "td/mtproto/Handshake.h"
17 #include "td/mtproto/HandshakeActor.h"
18 #include "td/mtproto/Ping.h"
19 #include "td/mtproto/PingConnection.h"
20 #include "td/mtproto/ProxySecret.h"
21 #include "td/mtproto/RawConnection.h"
22 #include "td/mtproto/RSA.h"
23 #include "td/mtproto/TlsInit.h"
24 #include "td/mtproto/TransportType.h"
25 
26 #include "td/net/GetHostByNameActor.h"
27 #include "td/net/Socks5.h"
28 #include "td/net/TransparentProxy.h"
29 
30 #include "td/actor/actor.h"
31 #include "td/actor/ConcurrentScheduler.h"
32 #include "td/actor/PromiseFuture.h"
33 
34 #include "td/utils/base64.h"
35 #include "td/utils/BufferedFd.h"
36 #include "td/utils/common.h"
37 #include "td/utils/crypto.h"
38 #include "td/utils/logging.h"
39 #include "td/utils/port/Clocks.h"
40 #include "td/utils/port/IPAddress.h"
41 #include "td/utils/port/SocketFd.h"
42 #include "td/utils/Random.h"
43 #include "td/utils/Slice.h"
44 #include "td/utils/SliceBuilder.h"
45 #include "td/utils/Status.h"
46 #include "td/utils/tests.h"
47 #include "td/utils/Time.h"
48 
49 using namespace td;
50 
TEST(Mtproto,GetHostByNameActor)51 TEST(Mtproto, GetHostByNameActor) {
52   ConcurrentScheduler sched;
53   int threads_n = 1;
54   sched.init(threads_n);
55 
56   int cnt = 1;
57   vector<ActorOwn<GetHostByNameActor>> actors;
58   {
59     auto guard = sched.get_main_guard();
60 
61     auto run = [&](ActorId<GetHostByNameActor> actor_id, string host, bool prefer_ipv6, bool allow_ok,
62                    bool allow_error) {
63       auto promise = PromiseCreator::lambda([&cnt, &actors, num = cnt, host, allow_ok,
64                                              allow_error](Result<IPAddress> r_ip_address) {
65         if (r_ip_address.is_error() && !allow_error) {
66           LOG(ERROR) << num << " \"" << host << "\" " << r_ip_address.error();
67         }
68         if (r_ip_address.is_ok() && !allow_ok && (r_ip_address.ok().is_ipv6() || r_ip_address.ok().get_ipv4() != 0)) {
69           LOG(ERROR) << num << " \"" << host << "\" " << r_ip_address.ok();
70         }
71         if (--cnt == 0) {
72           actors.clear();
73           Scheduler::instance()->finish();
74         }
75       });
76       cnt++;
77       send_closure_later(actor_id, &GetHostByNameActor::run, host, 443, prefer_ipv6, std::move(promise));
78     };
79 
80     std::vector<std::string> hosts = {"127.0.0.2",
81                                       "1.1.1.1",
82                                       "localhost",
83                                       "web.telegram.org",
84                                       "web.telegram.org.",
85                                       "москва.рф",
86                                       "",
87                                       "%",
88                                       " ",
89                                       "a",
90                                       "\x80",
91                                       "[]",
92                                       "127.0.0.1.",
93                                       "0x12.0x34.0x56.0x78",
94                                       "0x7f.001",
95                                       "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
96                                       "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
97                                       "[[2001:0db8:85a3:0000:0000:8a2e:0370:7334]]"};
98     for (const auto &types : {vector<GetHostByNameActor::ResolverType>{GetHostByNameActor::ResolverType::Native},
99                               vector<GetHostByNameActor::ResolverType>{GetHostByNameActor::ResolverType::Google},
100                               vector<GetHostByNameActor::ResolverType>{GetHostByNameActor::ResolverType::Google,
101                                                                        GetHostByNameActor::ResolverType::Google,
102                                                                        GetHostByNameActor::ResolverType::Native}}) {
103       GetHostByNameActor::Options options;
104       options.resolver_types = types;
105       options.scheduler_id = threads_n;
106 
107       auto actor = create_actor<GetHostByNameActor>("GetHostByNameActor", std::move(options));
108       auto actor_id = actor.get();
109       actors.push_back(std::move(actor));
110 
111       for (auto host : hosts) {
112         for (auto prefer_ipv6 : {false, true}) {
113           bool allow_ok = host.size() > 2 && host[1] != '[';
114           bool allow_both = host == "127.0.0.1." || host == "localhost" || (host == "москва.рф" && prefer_ipv6);
115           bool allow_error = !allow_ok || allow_both;
116           run(actor_id, host, prefer_ipv6, allow_ok, allow_error);
117         }
118       }
119     }
120   }
121   cnt--;
122   sched.start();
123   while (sched.run_main(10)) {
124     // empty
125   }
126   sched.finish();
127 }
128 
TEST(Time,to_unix_time)129 TEST(Time, to_unix_time) {
130   ASSERT_EQ(0, HttpDate::to_unix_time(1970, 1, 1, 0, 0, 0).move_as_ok());
131   ASSERT_EQ(60 * 60 + 60 + 1, HttpDate::to_unix_time(1970, 1, 1, 1, 1, 1).move_as_ok());
132   ASSERT_EQ(24 * 60 * 60, HttpDate::to_unix_time(1970, 1, 2, 0, 0, 0).move_as_ok());
133   ASSERT_EQ(31 * 24 * 60 * 60, HttpDate::to_unix_time(1970, 2, 1, 0, 0, 0).move_as_ok());
134   ASSERT_EQ(365 * 24 * 60 * 60, HttpDate::to_unix_time(1971, 1, 1, 0, 0, 0).move_as_ok());
135   ASSERT_EQ(1562780559, HttpDate::to_unix_time(2019, 7, 10, 17, 42, 39).move_as_ok());
136 }
137 
TEST(Time,parse_http_date)138 TEST(Time, parse_http_date) {
139   ASSERT_EQ(784887151, HttpDate::parse_http_date("Tue, 15 Nov 1994 08:12:31 GMT").move_as_ok());
140 }
141 
TEST(Mtproto,config)142 TEST(Mtproto, config) {
143   ConcurrentScheduler sched;
144   int threads_n = 0;
145   sched.init(threads_n);
146 
147   int cnt = 1;
148   {
149     auto guard = sched.get_main_guard();
150 
151     auto run = [&](auto &func, bool is_test) {
152       auto promise = PromiseCreator::lambda([&, num = cnt](Result<SimpleConfigResult> r_simple_config_result) {
153         if (r_simple_config_result.is_ok()) {
154           auto simple_config_result = r_simple_config_result.move_as_ok();
155           auto date = simple_config_result.r_http_date.is_ok()
156                           ? to_string(simple_config_result.r_http_date.ok())
157                           : (PSTRING() << simple_config_result.r_http_date.error());
158           auto config = simple_config_result.r_config.is_ok() ? to_string(simple_config_result.r_config.ok())
159                                                               : (PSTRING() << simple_config_result.r_config.error());
160           LOG(ERROR) << num << " " << date << " " << config;
161         } else {
162           LOG(ERROR) << num << " " << r_simple_config_result.error();
163         }
164         if (--cnt == 0) {
165           Scheduler::instance()->finish();
166         }
167       });
168       cnt++;
169       func(std::move(promise), nullptr, is_test, -1).release();
170     };
171 
172     run(get_simple_config_azure, false);
173     run(get_simple_config_google_dns, false);
174     run(get_simple_config_mozilla_dns, false);
175     run(get_simple_config_azure, true);
176     run(get_simple_config_google_dns, true);
177     run(get_simple_config_mozilla_dns, true);
178     run(get_simple_config_firebase_remote_config, false);
179     run(get_simple_config_firebase_realtime, false);
180     run(get_simple_config_firebase_firestore, false);
181   }
182   cnt--;
183   if (cnt != 0) {
184     sched.start();
185     while (sched.run_main(10)) {
186       // empty;
187     }
188     sched.finish();
189   }
190 }
191 
TEST(Mtproto,encrypted_config)192 TEST(Mtproto, encrypted_config) {
193   string data =
194       "   hO//tt \b\n\tiwPVovorKtIYtQ8y2ik7CqfJiJ4pJOCLRa4fBmNPixuRPXnBFF/3mTAAZoSyHq4SNylGHz0Cv1/"
195       "FnWWdEV+BPJeOTk+ARHcNkuJBt0CqnfcVCoDOpKqGyq0U31s2MOpQvHgAG+Tlpg02syuH0E4dCGRw5CbJPARiynteb9y5fT5x/"
196       "kmdp6BMR5tWQSQF0liH16zLh8BDSIdiMsikdcwnAvBwdNhRqQBqGx9MTh62MDmlebjtczE9Gz0z5cscUO2yhzGdphgIy6SP+"
197       "bwaqLWYF0XdPGjKLMUEJW+rou6fbL1t/EUXPtU0XmQAnO0Fh86h+AqDMOe30N4qKrPQ==   ";
198   auto config = decode_config(data).move_as_ok();
199 }
200 
201 class TestPingActor final : public Actor {
202  public:
TestPingActor(IPAddress ip_address,Status * result)203   TestPingActor(IPAddress ip_address, Status *result) : ip_address_(ip_address), result_(result) {
204   }
205 
206  private:
207   IPAddress ip_address_;
208   unique_ptr<mtproto::PingConnection> ping_connection_;
209   Status *result_;
210   bool is_inited_ = false;
211 
start_up()212   void start_up() final {
213     auto r_socket = SocketFd::open(ip_address_);
214     if (r_socket.is_error()) {
215       LOG(ERROR) << "Failed to open socket: " << r_socket.error();
216       return stop();
217     }
218 
219     ping_connection_ = mtproto::PingConnection::create_req_pq(
220         mtproto::RawConnection::create(ip_address_, BufferedFd<SocketFd>(r_socket.move_as_ok()),
221                                        mtproto::TransportType{mtproto::TransportType::Tcp, 0, mtproto::ProxySecret()},
222                                        nullptr),
223         3);
224 
225     Scheduler::subscribe(ping_connection_->get_poll_info().extract_pollable_fd(this));
226     is_inited_ = true;
227     set_timeout_in(10);
228     yield();
229   }
tear_down()230   void tear_down() final {
231     if (is_inited_) {
232       Scheduler::unsubscribe_before_close(ping_connection_->get_poll_info().get_pollable_fd_ref());
233     }
234     Scheduler::instance()->finish();
235   }
236 
loop()237   void loop() final {
238     auto status = ping_connection_->flush();
239     if (status.is_error()) {
240       *result_ = std::move(status);
241       return stop();
242     }
243     if (ping_connection_->was_pong()) {
244       LOG(INFO) << "GOT PONG";
245       return stop();
246     }
247   }
248 
timeout_expired()249   void timeout_expired() final {
250     *result_ = Status::Error("Timeout expired");
251     stop();
252   }
253 };
254 
get_default_ip_address()255 static IPAddress get_default_ip_address() {
256   IPAddress ip_address;
257 #if TD_EMSCRIPTEN
258   ip_address.init_host_port("venus.web.telegram.org/apiws", 443).ensure();
259 #else
260   ip_address.init_ipv4_port("149.154.167.40", 80).ensure();
261 #endif
262   return ip_address;
263 }
264 
get_default_dc_id()265 static int32 get_default_dc_id() {
266   return 10002;
267 }
268 
269 class Mtproto_ping final : public Test {
270  public:
271   using Test::Test;
step()272   bool step() final {
273     if (!is_inited_) {
274       sched_.init(0);
275       sched_.create_actor_unsafe<TestPingActor>(0, "Pinger", get_default_ip_address(), &result_).release();
276       sched_.start();
277       is_inited_ = true;
278     }
279 
280     bool ret = sched_.run_main(10);
281     if (ret) {
282       return true;
283     }
284     sched_.finish();
285     if (result_.is_error()) {
286       LOG(ERROR) << result_;
287     }
288     return false;
289   }
290 
291  private:
292   bool is_inited_ = false;
293   ConcurrentScheduler sched_;
294   Status result_;
295 };
296 RegisterTest<Mtproto_ping> mtproto_ping("Mtproto_ping");
297 
298 class HandshakeContext final : public mtproto::AuthKeyHandshakeContext {
299  public:
get_dh_callback()300   mtproto::DhCallback *get_dh_callback() final {
301     return nullptr;
302   }
get_public_rsa_key_interface()303   mtproto::PublicRsaKeyInterface *get_public_rsa_key_interface() final {
304     return &public_rsa_key;
305   }
306 
307  private:
308   PublicRsaKeyShared public_rsa_key{DcId::empty(), true};
309 };
310 
311 class HandshakeTestActor final : public Actor {
312  public:
HandshakeTestActor(int32 dc_id,Status * result)313   HandshakeTestActor(int32 dc_id, Status *result) : dc_id_(dc_id), result_(result) {
314   }
315 
316  private:
317   int32 dc_id_ = 0;
318   Status *result_;
319   bool wait_for_raw_connection_ = false;
320   unique_ptr<mtproto::RawConnection> raw_connection_;
321   bool wait_for_handshake_ = false;
322   unique_ptr<mtproto::AuthKeyHandshake> handshake_;
323   Status status_;
324   bool wait_for_result_ = false;
325 
tear_down()326   void tear_down() final {
327     if (raw_connection_) {
328       raw_connection_->close();
329     }
330     finish(Status::Error("Interrupted"));
331   }
loop()332   void loop() final {
333     if (!wait_for_raw_connection_ && !raw_connection_) {
334       auto ip_address = get_default_ip_address();
335       auto r_socket = SocketFd::open(ip_address);
336       if (r_socket.is_error()) {
337         finish(Status::Error(PSTRING() << "Failed to open socket: " << r_socket.error()));
338         return stop();
339       }
340 
341       raw_connection_ = mtproto::RawConnection::create(
342           ip_address, BufferedFd<SocketFd>(r_socket.move_as_ok()),
343           mtproto::TransportType{mtproto::TransportType::Tcp, 0, mtproto::ProxySecret()}, nullptr);
344     }
345     if (!wait_for_handshake_ && !handshake_) {
346       handshake_ = make_unique<mtproto::AuthKeyHandshake>(dc_id_, 3600);
347     }
348     if (raw_connection_ && handshake_) {
349       if (wait_for_result_) {
350         wait_for_result_ = false;
351         if (status_.is_error()) {
352           finish(std::move(status_));
353           return stop();
354         }
355         if (!handshake_->is_ready_for_finish()) {
356           finish(Status::Error("Key is not ready.."));
357           return stop();
358         }
359         finish(Status::OK());
360         return stop();
361       }
362 
363       wait_for_result_ = true;
364       create_actor<mtproto::HandshakeActor>(
365           "HandshakeActor", std::move(handshake_), std::move(raw_connection_), make_unique<HandshakeContext>(), 10.0,
366           PromiseCreator::lambda([self = actor_id(this)](Result<unique_ptr<mtproto::RawConnection>> raw_connection) {
367             send_closure(self, &HandshakeTestActor::got_connection, std::move(raw_connection), 1);
368           }),
369           PromiseCreator::lambda([self = actor_id(this)](Result<unique_ptr<mtproto::AuthKeyHandshake>> handshake) {
370             send_closure(self, &HandshakeTestActor::got_handshake, std::move(handshake), 1);
371           }))
372           .release();
373       wait_for_raw_connection_ = true;
374       wait_for_handshake_ = true;
375     }
376   }
377 
got_connection(Result<unique_ptr<mtproto::RawConnection>> r_raw_connection,int32 dummy)378   void got_connection(Result<unique_ptr<mtproto::RawConnection>> r_raw_connection, int32 dummy) {
379     CHECK(wait_for_raw_connection_);
380     wait_for_raw_connection_ = false;
381     if (r_raw_connection.is_ok()) {
382       raw_connection_ = r_raw_connection.move_as_ok();
383       status_ = Status::OK();
384     } else {
385       status_ = r_raw_connection.move_as_error();
386     }
387     // TODO: save error
388     loop();
389   }
390 
got_handshake(Result<unique_ptr<mtproto::AuthKeyHandshake>> r_handshake,int32 dummy)391   void got_handshake(Result<unique_ptr<mtproto::AuthKeyHandshake>> r_handshake, int32 dummy) {
392     CHECK(wait_for_handshake_);
393     wait_for_handshake_ = false;
394     CHECK(r_handshake.is_ok());
395     handshake_ = r_handshake.move_as_ok();
396     loop();
397   }
398 
finish(Status status)399   void finish(Status status) {
400     if (!result_) {
401       return;
402     }
403     *result_ = std::move(status);
404     result_ = nullptr;
405     Scheduler::instance()->finish();
406   }
407 };
408 
409 class Mtproto_handshake final : public Test {
410  public:
411   using Test::Test;
step()412   bool step() final {
413     if (!is_inited_) {
414       sched_.init(0);
415       sched_.create_actor_unsafe<HandshakeTestActor>(0, "HandshakeTestActor", get_default_dc_id(), &result_).release();
416       sched_.start();
417       is_inited_ = true;
418     }
419 
420     bool ret = sched_.run_main(10);
421     if (ret) {
422       return true;
423     }
424     sched_.finish();
425     if (result_.is_error()) {
426       LOG(ERROR) << result_;
427     }
428     return false;
429   }
430 
431  private:
432   bool is_inited_ = false;
433   ConcurrentScheduler sched_;
434   Status result_;
435 };
436 RegisterTest<Mtproto_handshake> mtproto_handshake("Mtproto_handshake");
437 
438 class Socks5TestActor final : public Actor {
439  public:
start_up()440   void start_up() final {
441     auto promise = PromiseCreator::lambda([actor_id = actor_id(this)](Result<BufferedFd<SocketFd>> res) {
442       send_closure(actor_id, &Socks5TestActor::on_result, std::move(res), false);
443     });
444 
445     class Callback final : public TransparentProxy::Callback {
446      public:
447       explicit Callback(Promise<BufferedFd<SocketFd>> promise) : promise_(std::move(promise)) {
448       }
449       void set_result(Result<BufferedFd<SocketFd>> result) final {
450         promise_.set_result(std::move(result));
451       }
452       void on_connected() final {
453       }
454 
455      private:
456       Promise<BufferedFd<SocketFd>> promise_;
457     };
458 
459     IPAddress socks5_ip;
460     socks5_ip.init_ipv4_port("131.191.89.104", 43077).ensure();
461     IPAddress mtproto_ip_address = get_default_ip_address();
462 
463     auto r_socket = SocketFd::open(socks5_ip);
464     if (r_socket.is_error()) {
465       return promise.set_error(Status::Error(PSTRING() << "Failed to open socket: " << r_socket.error()));
466     }
467     create_actor<Socks5>("socks5", r_socket.move_as_ok(), mtproto_ip_address, "", "",
468                          make_unique<Callback>(std::move(promise)), actor_shared(this))
469         .release();
470   }
471 
472  private:
on_result(Result<BufferedFd<SocketFd>> res,bool dummy)473   void on_result(Result<BufferedFd<SocketFd>> res, bool dummy) {
474     res.ensure();
475     Scheduler::instance()->finish();
476   }
477 };
478 
TEST(Mtproto,socks5)479 TEST(Mtproto, socks5) {
480   return;
481   ConcurrentScheduler sched;
482   int threads_n = 0;
483   sched.init(threads_n);
484 
485   sched.create_actor_unsafe<Socks5TestActor>(0, "Socks5TestActor").release();
486   sched.start();
487   while (sched.run_main(10)) {
488     // empty;
489   }
490   sched.finish();
491 }
492 
TEST(Mtproto,notifications)493 TEST(Mtproto, notifications) {
494   vector<string> pushes = {
495       "eyJwIjoiSkRnQ3NMRWxEaWhyVWRRN1pYM3J1WVU4TlRBMFhMb0N6UWRNdzJ1cWlqMkdRbVR1WXVvYXhUeFJHaG1QQm8yVElYZFBzX2N3b2RIb3lY"
496       "b2drVjM1dVl0UzdWeElNX1FNMDRKMG1mV3ZZWm4zbEtaVlJ0aFVBNGhYUWlaN0pfWDMyZDBLQUlEOWgzRnZwRjNXUFRHQWRaVkdFYzg3bnFPZ3hD"
497       "NUNMRkM2SU9fZmVqcEpaV2RDRlhBWWpwc1k2aktrbVNRdFZ1MzE5ZW04UFVieXZudFpfdTNud2hjQ0czMk96TGp4S1kyS1lzU21JZm1GMzRmTmw1"
498       "QUxaa2JvY2s2cE5rZEdrak9qYmRLckJyU0ZtWU8tQ0FsRE10dEplZFFnY1U5bVJQdU80b1d2NG5sb1VXS19zSlNTaXdIWEZyb1pWTnZTeFJ0Z1dN"
499       "ZyJ9",
500       "eyJwIjoiSkRnQ3NMRWxEaWlZby1GRWJndk9WaTFkUFdPVmZndzBBWHYwTWNzWDFhWEtNZC03T1Q2WWNfT0taRURHZDJsZ0h0WkhMSllyVG50RE95"
501       "TkY1aXJRQlZ4UUFLQlRBekhPTGZIS3BhQXdoaWd5b3NQd0piWnJVV2xRWmh4eEozUFUzZjBNRTEwX0xNT0pFN0xsVUFaY2dabUNaX2V1QmNPZWNK"
502       "VERxRkpIRGZjN2pBOWNrcFkyNmJRT2dPUEhCeHlEMUVrNVdQcFpLTnlBODVuYzQ1eHFPdERqcU5aVmFLU3pKb2VIcXBQMnJqR29kN2M5YkxsdGd5"
503       "Q0NGd2NBU3dJeDc3QWNWVXY1UnVZIn0"};
504   string key =
505       "uBa5yu01a-nJJeqsR3yeqMs6fJLYXjecYzFcvS6jIwS3nefBIr95LWrTm-IbRBNDLrkISz1Sv0KYpDzhU8WFRk1D0V_"
506       "qyO7XsbDPyrYxRBpGxofJUINSjb1uCxoSdoh1_F0UXEA2fWWKKVxL0DKUQssZfbVj3AbRglsWpH-jDK1oc6eBydRiS3i4j-"
507       "H0yJkEMoKRgaF9NaYI4u26oIQ-Ez46kTVU-R7e3acdofOJKm7HIKan_5ZMg82Dvec2M6vc_"
508       "I54Vs28iBx8IbBO1y5z9WSScgW3JCvFFKP2MXIu7Jow5-cpUx6jXdzwRUb9RDApwAFKi45zpv8eb3uPCDAmIQ";
509   vector<string> decrypted_payloads = {
510       "eyJsb2Nfa2V5IjoiTUVTU0FHRV9URVhUIiwibG9jX2FyZ3MiOlsiQXJzZW55IFNtaXJub3YiLCJhYmNkZWZnIl0sImN1c3RvbSI6eyJtc2dfaWQi"
511       "OiI1OTAwNDciLCJmcm9tX2lkIjoiNjI4MTQifSwiYmFkZ2UiOiI0MDkifQ",
512       "eyJsb2Nfa2V5IjoiIiwibG9jX2FyZ3MiOltdLCJjdXN0b20iOnsiY2hhbm5lbF9pZCI6IjExNzY4OTU0OTciLCJtYXhfaWQiOiIxMzU5In0sImJh"
513       "ZGdlIjoiMCJ9"};
514   key = base64url_decode(key).move_as_ok();
515 
516   for (size_t i = 0; i < pushes.size(); i++) {
517     auto push = base64url_decode(pushes[i]).move_as_ok();
518     auto decrypted_payload = base64url_decode(decrypted_payloads[i]).move_as_ok();
519 
520     auto key_id = mtproto::DhHandshake::calc_key_id(key);
521     ASSERT_EQ(key_id, NotificationManager::get_push_receiver_id(push).ok());
522     ASSERT_EQ(decrypted_payload, NotificationManager::decrypt_push(key_id, key, push).ok());
523   }
524 }
525 
526 class FastPingTestActor final : public Actor {
527  public:
FastPingTestActor(Status * result)528   explicit FastPingTestActor(Status *result) : result_(result) {
529   }
530 
531  private:
532   Status *result_;
533   unique_ptr<mtproto::RawConnection> connection_;
534   unique_ptr<mtproto::AuthKeyHandshake> handshake_;
535   ActorOwn<> fast_ping_;
536   int iteration_{0};
537 
start_up()538   void start_up() final {
539     // Run handshake to create key and salt
540     auto ip_address = get_default_ip_address();
541     auto r_socket = SocketFd::open(ip_address);
542     if (r_socket.is_error()) {
543       *result_ = Status::Error(PSTRING() << "Failed to open socket: " << r_socket.error());
544       return stop();
545     }
546 
547     auto raw_connection = mtproto::RawConnection::create(
548         ip_address, BufferedFd<SocketFd>(r_socket.move_as_ok()),
549         mtproto::TransportType{mtproto::TransportType::Tcp, 0, mtproto::ProxySecret()}, nullptr);
550     auto handshake = make_unique<mtproto::AuthKeyHandshake>(get_default_dc_id(), 60 * 100 /*temp*/);
551     create_actor<mtproto::HandshakeActor>(
552         "HandshakeActor", std::move(handshake), std::move(raw_connection), make_unique<HandshakeContext>(), 10.0,
553         PromiseCreator::lambda([self = actor_id(this)](Result<unique_ptr<mtproto::RawConnection>> raw_connection) {
554           send_closure(self, &FastPingTestActor::got_connection, std::move(raw_connection), 1);
555         }),
556         PromiseCreator::lambda([self = actor_id(this)](Result<unique_ptr<mtproto::AuthKeyHandshake>> handshake) {
557           send_closure(self, &FastPingTestActor::got_handshake, std::move(handshake), 1);
558         }))
559         .release();
560   }
got_connection(Result<unique_ptr<mtproto::RawConnection>> r_raw_connection,int32 dummy)561   void got_connection(Result<unique_ptr<mtproto::RawConnection>> r_raw_connection, int32 dummy) {
562     if (r_raw_connection.is_error()) {
563       *result_ = r_raw_connection.move_as_error();
564       LOG(INFO) << "Receive " << *result_ << " instead of a connection";
565       return stop();
566     }
567     connection_ = r_raw_connection.move_as_ok();
568     loop();
569   }
570 
got_handshake(Result<unique_ptr<mtproto::AuthKeyHandshake>> r_handshake,int32 dummy)571   void got_handshake(Result<unique_ptr<mtproto::AuthKeyHandshake>> r_handshake, int32 dummy) {
572     if (r_handshake.is_error()) {
573       *result_ = r_handshake.move_as_error();
574       LOG(INFO) << "Receive " << *result_ << " instead of a handshake";
575       return stop();
576     }
577     handshake_ = r_handshake.move_as_ok();
578     loop();
579   }
580 
got_raw_connection(Result<unique_ptr<mtproto::RawConnection>> r_connection)581   void got_raw_connection(Result<unique_ptr<mtproto::RawConnection>> r_connection) {
582     if (r_connection.is_error()) {
583       *result_ = r_connection.move_as_error();
584       LOG(INFO) << "Receive " << *result_ << " instead of a handshake";
585       return stop();
586     }
587     connection_ = r_connection.move_as_ok();
588     LOG(INFO) << "RTT: " << connection_->extra().rtt;
589     connection_->extra().rtt = 0;
590     loop();
591   }
592 
loop()593   void loop() final {
594     if (handshake_ && connection_) {
595       LOG(INFO) << "Iteration " << iteration_;
596       if (iteration_ == 6) {
597         return stop();
598       }
599       unique_ptr<mtproto::AuthData> auth_data;
600       if (iteration_ % 2 == 0) {
601         auth_data = make_unique<mtproto::AuthData>();
602         auth_data->set_tmp_auth_key(handshake_->get_auth_key());
603         auth_data->set_server_time_difference(handshake_->get_server_time_diff());
604         auth_data->set_server_salt(handshake_->get_server_salt(), Time::now());
605         auth_data->set_future_salts({mtproto::ServerSalt{0u, 1e20, 1e30}}, Time::now());
606         auth_data->set_use_pfs(true);
607         uint64 session_id = 0;
608         do {
609           Random::secure_bytes(reinterpret_cast<uint8 *>(&session_id), sizeof(session_id));
610         } while (session_id == 0);
611         auth_data->set_session_id(session_id);
612       }
613       iteration_++;
614       fast_ping_ = create_ping_actor(
615           td::Slice(), std::move(connection_), std::move(auth_data),
616           PromiseCreator::lambda([self = actor_id(this)](Result<unique_ptr<mtproto::RawConnection>> r_raw_connection) {
617             send_closure(self, &FastPingTestActor::got_raw_connection, std::move(r_raw_connection));
618           }),
619           ActorShared<>());
620     }
621   }
622 
tear_down()623   void tear_down() final {
624     Scheduler::instance()->finish();
625   }
626 };
627 
628 class Mtproto_FastPing final : public Test {
629  public:
630   using Test::Test;
step()631   bool step() final {
632     if (!is_inited_) {
633       sched_.init(0);
634       sched_.create_actor_unsafe<FastPingTestActor>(0, "FastPingTestActor", &result_).release();
635       sched_.start();
636       is_inited_ = true;
637     }
638 
639     bool ret = sched_.run_main(10);
640     if (ret) {
641       return true;
642     }
643     sched_.finish();
644     if (result_.is_error()) {
645       LOG(ERROR) << result_;
646     }
647     return false;
648   }
649 
650  private:
651   bool is_inited_ = false;
652   ConcurrentScheduler sched_;
653   Status result_;
654 };
655 RegisterTest<Mtproto_FastPing> mtproto_fastping("Mtproto_FastPing");
656 
TEST(Mtproto,Grease)657 TEST(Mtproto, Grease) {
658   std::string s(10000, '0');
659   mtproto::Grease::init(s);
660   for (auto c : s) {
661     CHECK((c & 0xF) == 0xA);
662   }
663   for (size_t i = 1; i < s.size(); i += 2) {
664     CHECK(s[i] != s[i - 1]);
665   }
666 }
667 
TEST(Mtproto,TlsTransport)668 TEST(Mtproto, TlsTransport) {
669   ConcurrentScheduler sched;
670   int threads_n = 1;
671   sched.init(threads_n);
672   {
673     auto guard = sched.get_main_guard();
674     class RunTest final : public Actor {
675       void start_up() final {
676         class Callback final : public TransparentProxy::Callback {
677          public:
678           void set_result(Result<BufferedFd<SocketFd>> result) final {
679             if (result.is_ok()) {
680               LOG(ERROR) << "Unexpectedly succeeded to connect to MTProto proxy";
681             } else if (result.error().message() != "Response hash mismatch") {
682               LOG(ERROR) << "Receive unexpected result " << result.error();
683             }
684             Scheduler::instance()->finish();
685           }
686           void on_connected() final {
687           }
688         };
689 
690         const std::string domain = "www.google.com";
691         IPAddress ip_address;
692         auto resolve_status = ip_address.init_host_port(domain, 443);
693         if (resolve_status.is_error()) {
694           LOG(ERROR) << resolve_status;
695           Scheduler::instance()->finish();
696           return;
697         }
698         auto r_socket = SocketFd::open(ip_address);
699         if (r_socket.is_error()) {
700           LOG(ERROR) << "Failed to open socket: " << r_socket.error();
701           Scheduler::instance()->finish();
702           return;
703         }
704         create_actor<mtproto::TlsInit>("TlsInit", r_socket.move_as_ok(), domain, "0123456789secret",
705                                        make_unique<Callback>(), ActorShared<>(), Clocks::system() - Time::now())
706             .release();
707       }
708     };
709     create_actor<RunTest>("RunTest").release();
710   }
711 
712   sched.start();
713   while (sched.run_main(10)) {
714     // empty
715   }
716   sched.finish();
717 }
718 
TEST(Mtproto,RSA)719 TEST(Mtproto, RSA) {
720   auto pem = td::Slice(
721       "-----BEGIN RSA PUBLIC KEY-----\n"
722       "MIIBCgKCAQEAr4v4wxMDXIaMOh8bayF/NyoYdpcysn5EbjTIOZC0RkgzsRj3SGlu\n"
723       "52QSz+ysO41dQAjpFLgxPVJoOlxXokaOq827IfW0bGCm0doT5hxtedu9UCQKbE8j\n"
724       "lDOk+kWMXHPZFJKWRgKgTu9hcB3y3Vk+JFfLpq3d5ZB48B4bcwrRQnzkx5GhWOFX\n"
725       "x73ZgjO93eoQ2b/lDyXxK4B4IS+hZhjzezPZTI5upTRbs5ljlApsddsHrKk6jJNj\n"
726       "8Ygs/ps8e6ct82jLXbnndC9s8HjEvDvBPH9IPjv5JUlmHMBFZ5vFQIfbpo0u0+1P\n"
727       "n6bkEi5o7/ifoyVv2pAZTRwppTz0EuXD8QIDAQAB\n"
728       "-----END RSA PUBLIC KEY-----");
729   auto rsa = td::mtproto::RSA::from_pem_public_key(pem).move_as_ok();
730   ASSERT_EQ(-7596991558377038078, rsa.get_fingerprint());
731   ASSERT_EQ(256u, rsa.size());
732 
733   td::string to(256, '\0');
734   rsa.encrypt(pem.substr(0, 256), to);
735   ASSERT_EQ("U2nJEtB2AgpHrm3HB0yhpTQgb0wbesi9Pv/W1v/vULU=", td::base64_encode(td::sha256(to)));
736 }
737