summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/test/mtproto.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/test/mtproto.cpp')
-rw-r--r--protocols/Telegram/tdlib/td/test/mtproto.cpp663
1 files changed, 528 insertions, 135 deletions
diff --git a/protocols/Telegram/tdlib/td/test/mtproto.cpp b/protocols/Telegram/tdlib/td/test/mtproto.cpp
index 7702a1e37b..89f5441ab5 100644
--- a/protocols/Telegram/tdlib/td/test/mtproto.cpp
+++ b/protocols/Telegram/tdlib/td/test/mtproto.cpp
@@ -1,143 +1,276 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
-#include "td/utils/tests.h"
-
-#include "td/actor/actor.h"
-#include "td/actor/PromiseFuture.h"
+#include "td/telegram/ConfigManager.h"
+#include "td/telegram/net/DcId.h"
+#include "td/telegram/net/PublicRsaKeyShared.h"
+#include "td/telegram/net/Session.h"
+#include "td/telegram/NotificationManager.h"
-#include "td/mtproto/crypto.h"
+#include "td/mtproto/AuthData.h"
+#include "td/mtproto/DhCallback.h"
+#include "td/mtproto/DhHandshake.h"
#include "td/mtproto/Handshake.h"
#include "td/mtproto/HandshakeActor.h"
-#include "td/mtproto/HandshakeConnection.h"
+#include "td/mtproto/Ping.h"
#include "td/mtproto/PingConnection.h"
+#include "td/mtproto/ProxySecret.h"
#include "td/mtproto/RawConnection.h"
+#include "td/mtproto/RSA.h"
+#include "td/mtproto/TlsInit.h"
+#include "td/mtproto/TransportType.h"
+#include "td/net/GetHostByNameActor.h"
#include "td/net/Socks5.h"
+#include "td/net/TransparentProxy.h"
-#include "td/telegram/ConfigManager.h"
-#include "td/telegram/net/PublicRsaKeyShared.h"
+#include "td/actor/actor.h"
+#include "td/actor/ConcurrentScheduler.h"
+#include "td/utils/base64.h"
+#include "td/utils/BufferedFd.h"
+#include "td/utils/common.h"
+#include "td/utils/crypto.h"
#include "td/utils/logging.h"
+#include "td/utils/port/Clocks.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/port/SocketFd.h"
+#include "td/utils/Promise.h"
+#include "td/utils/Random.h"
+#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
+#include "td/utils/tests.h"
+#include "td/utils/Time.h"
-REGISTER_TESTS(mtproto);
-
-using namespace td;
-using namespace mtproto;
-
-#if !TD_WINDOWS && !TD_EMSCRIPTEN // TODO
-TEST(Mtproto, config) {
- ConcurrentScheduler sched;
- int threads_n = 0;
- sched.init(threads_n);
+TEST(Mtproto, GetHostByNameActor) {
+ int threads_n = 1;
+ td::ConcurrentScheduler sched(threads_n, 0);
- int cnt = 3;
+ int cnt = 1;
+ td::vector<td::ActorOwn<td::GetHostByNameActor>> actors;
{
- auto guard = sched.get_current_guard();
- get_simple_config_azure(PromiseCreator::lambda([&](Result<SimpleConfig> r_simple_config) {
- LOG(ERROR) << to_string(r_simple_config.ok());
- if (--cnt == 0) {
- Scheduler::instance()->finish();
- }
- }))
- .release();
-
- get_simple_config_google_app(PromiseCreator::lambda([&](Result<SimpleConfig> r_simple_config) {
- LOG(ERROR) << to_string(r_simple_config.ok());
- if (--cnt == 0) {
- Scheduler::instance()->finish();
- }
- }))
- .release();
+ auto guard = sched.get_main_guard();
+
+ auto run = [&](td::ActorId<td::GetHostByNameActor> actor_id, td::string host, bool prefer_ipv6, bool allow_ok,
+ bool allow_error) {
+ auto promise = td::PromiseCreator::lambda([&cnt, &actors, num = cnt, host, allow_ok,
+ allow_error](td::Result<td::IPAddress> r_ip_address) {
+ if (r_ip_address.is_error() && !allow_error) {
+ LOG(ERROR) << num << " \"" << host << "\" " << r_ip_address.error();
+ }
+ if (r_ip_address.is_ok() && !allow_ok && (r_ip_address.ok().is_ipv6() || r_ip_address.ok().get_ipv4() != 0)) {
+ LOG(ERROR) << num << " \"" << host << "\" " << r_ip_address.ok();
+ }
+ if (--cnt == 0) {
+ actors.clear();
+ td::Scheduler::instance()->finish();
+ }
+ });
+ cnt++;
+ td::send_closure_later(actor_id, &td::GetHostByNameActor::run, host, 443, prefer_ipv6, std::move(promise));
+ };
- get_simple_config_google_dns(PromiseCreator::lambda([&](Result<SimpleConfig> r_simple_config) {
- LOG(ERROR) << to_string(r_simple_config.ok());
- if (--cnt == 0) {
- Scheduler::instance()->finish();
+ td::vector<td::string> hosts = {"127.0.0.2",
+ "1.1.1.1",
+ "localhost",
+ "web.telegram.org",
+ "web.telegram.org.",
+ "москва.рф",
+ "",
+ "%",
+ " ",
+ "a",
+ "\x80",
+ "[]",
+ "127.0.0.1.",
+ "0x12.0x34.0x56.0x78",
+ "0x7f.001",
+ "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+ "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
+ "[[2001:0db8:85a3:0000:0000:8a2e:0370:7334]]"};
+ for (const auto &types :
+ {td::vector<td::GetHostByNameActor::ResolverType>{td::GetHostByNameActor::ResolverType::Native},
+ td::vector<td::GetHostByNameActor::ResolverType>{td::GetHostByNameActor::ResolverType::Google},
+ td::vector<td::GetHostByNameActor::ResolverType>{td::GetHostByNameActor::ResolverType::Google,
+ td::GetHostByNameActor::ResolverType::Google,
+ td::GetHostByNameActor::ResolverType::Native}}) {
+ td::GetHostByNameActor::Options options;
+ options.resolver_types = types;
+ options.scheduler_id = threads_n;
+
+ auto actor = td::create_actor<td::GetHostByNameActor>("GetHostByNameActor", std::move(options));
+ auto actor_id = actor.get();
+ actors.push_back(std::move(actor));
+
+ for (auto host : hosts) {
+ for (auto prefer_ipv6 : {false, true}) {
+ bool allow_ok = host.size() > 2 && host[1] != '[';
+ bool allow_both = host == "127.0.0.1." || host == "localhost" || (host == "москва.рф" && prefer_ipv6);
+ bool allow_error = !allow_ok || allow_both;
+ run(actor_id, host, prefer_ipv6, allow_ok, allow_error);
+ }
}
- }))
- .release();
+ }
}
+ cnt--;
sched.start();
while (sched.run_main(10)) {
- // empty;
+ // empty
}
sched.finish();
}
-#endif
+
+TEST(Time, to_unix_time) {
+ ASSERT_EQ(0, td::HttpDate::to_unix_time(1970, 1, 1, 0, 0, 0).move_as_ok());
+ ASSERT_EQ(60 * 60 + 60 + 1, td::HttpDate::to_unix_time(1970, 1, 1, 1, 1, 1).move_as_ok());
+ ASSERT_EQ(24 * 60 * 60, td::HttpDate::to_unix_time(1970, 1, 2, 0, 0, 0).move_as_ok());
+ ASSERT_EQ(31 * 24 * 60 * 60, td::HttpDate::to_unix_time(1970, 2, 1, 0, 0, 0).move_as_ok());
+ ASSERT_EQ(365 * 24 * 60 * 60, td::HttpDate::to_unix_time(1971, 1, 1, 0, 0, 0).move_as_ok());
+ ASSERT_EQ(1562780559, td::HttpDate::to_unix_time(2019, 7, 10, 17, 42, 39).move_as_ok());
+}
+
+TEST(Time, parse_http_date) {
+ ASSERT_EQ(784887151, td::HttpDate::parse_http_date("Tue, 15 Nov 1994 08:12:31 GMT").move_as_ok());
+}
+
+TEST(Mtproto, config) {
+ int threads_n = 0;
+ td::ConcurrentScheduler sched(threads_n, 0);
+
+ int cnt = 1;
+ {
+ auto guard = sched.get_main_guard();
+
+ auto run = [&](auto &func, bool is_test) {
+ auto promise =
+ td::PromiseCreator::lambda([&, num = cnt](td::Result<td::SimpleConfigResult> r_simple_config_result) {
+ if (r_simple_config_result.is_ok()) {
+ auto simple_config_result = r_simple_config_result.move_as_ok();
+ auto date = simple_config_result.r_http_date.is_ok()
+ ? td::to_string(simple_config_result.r_http_date.ok())
+ : (PSTRING() << simple_config_result.r_http_date.error());
+ auto config = simple_config_result.r_config.is_ok()
+ ? to_string(simple_config_result.r_config.ok())
+ : (PSTRING() << simple_config_result.r_config.error());
+ LOG(ERROR) << num << " " << date << " " << config;
+ } else {
+ LOG(ERROR) << num << " " << r_simple_config_result.error();
+ }
+ if (--cnt == 0) {
+ td::Scheduler::instance()->finish();
+ }
+ });
+ cnt++;
+ func(std::move(promise), false, td::Slice(), is_test, -1).release();
+ };
+
+ run(td::get_simple_config_azure, false);
+ run(td::get_simple_config_google_dns, false);
+ run(td::get_simple_config_mozilla_dns, false);
+ run(td::get_simple_config_azure, true);
+ run(td::get_simple_config_google_dns, true);
+ run(td::get_simple_config_mozilla_dns, true);
+ run(td::get_simple_config_firebase_remote_config, false);
+ run(td::get_simple_config_firebase_realtime, false);
+ run(td::get_simple_config_firebase_firestore, false);
+ }
+ cnt--;
+ if (cnt != 0) {
+ sched.start();
+ while (sched.run_main(10)) {
+ // empty;
+ }
+ sched.finish();
+ }
+}
TEST(Mtproto, encrypted_config) {
- string data =
- " LQ2 \b\n\tru6xVXpHHckW4eQWK0X3uThupVOor5sXT8t298IjDksYeUseQTOIrnUqiQj7o"
- "+ZgPfhnfe+lfcQA+naax9akgllimjlJtL5riTf3O7iqZSnJ614qmCucxqqVTbXk/"
- "hY2KaJTtsMqk7cShJjM3aQ4DD40h2InTaG7uyVO2q7K0GMUTeY3AM0Rt1lUjKHLD"
- "g4RwjTzZaG8TwfgL/mZ7jsvgTTTATPWKUo7SmxQ9Hsj+07NMGqr6JKZS6aiU1Knz"
- "VGCZ3OJEyRYocktN4HjaLpkguilaHWlVM2UNFUd5a+ajfLIiiKlH0FRC3XZ12CND"
- "Y+NBjv0I57N2O4fBfswTlA== ";
- auto config = decode_config(data).move_as_ok();
+ td::string data =
+ " hO//tt \b\n\tiwPVovorKtIYtQ8y2ik7CqfJiJ4pJOCLRa4fBmNPixuRPXnBFF/3mTAAZoSyHq4SNylGHz0Cv1/"
+ "FnWWdEV+BPJeOTk+ARHcNkuJBt0CqnfcVCoDOpKqGyq0U31s2MOpQvHgAG+Tlpg02syuH0E4dCGRw5CbJPARiynteb9y5fT5x/"
+ "kmdp6BMR5tWQSQF0liH16zLh8BDSIdiMsikdcwnAvBwdNhRqQBqGx9MTh62MDmlebjtczE9Gz0z5cscUO2yhzGdphgIy6SP+"
+ "bwaqLWYF0XdPGjKLMUEJW+rou6fbL1t/EUXPtU0XmQAnO0Fh86h+AqDMOe30N4qKrPQ== ";
+ auto config = td::decode_config(data).move_as_ok();
}
-class TestPingActor : public Actor {
+class TestPingActor final : public td::Actor {
public:
- TestPingActor(IPAddress ip_address, Status *result) : ip_address_(ip_address), result_(result) {
+ TestPingActor(td::IPAddress ip_address, td::Status *result) : ip_address_(ip_address), result_(result) {
}
private:
- IPAddress ip_address_;
- std::unique_ptr<mtproto::PingConnection> ping_connection_;
- Status *result_;
+ td::IPAddress ip_address_;
+ td::unique_ptr<td::mtproto::PingConnection> ping_connection_;
+ td::Status *result_;
+ bool is_inited_ = false;
- void start_up() override {
- ping_connection_ = std::make_unique<mtproto::PingConnection>(std::make_unique<mtproto::RawConnection>(
- SocketFd::open(ip_address_).move_as_ok(), mtproto::TransportType::Tcp, nullptr));
+ void start_up() final {
+ auto r_socket = td::SocketFd::open(ip_address_);
+ if (r_socket.is_error()) {
+ LOG(ERROR) << "Failed to open socket: " << r_socket.error();
+ return stop();
+ }
- ping_connection_->get_pollable().set_observer(this);
- subscribe(ping_connection_->get_pollable());
+ ping_connection_ = td::mtproto::PingConnection::create_req_pq(
+ td::mtproto::RawConnection::create(
+ ip_address_, td::BufferedFd<td::SocketFd>(r_socket.move_as_ok()),
+ td::mtproto::TransportType{td::mtproto::TransportType::Tcp, 0, td::mtproto::ProxySecret()}, nullptr),
+ 3);
+
+ td::Scheduler::subscribe(ping_connection_->get_poll_info().extract_pollable_fd(this));
+ is_inited_ = true;
set_timeout_in(10);
yield();
}
- void tear_down() override {
- unsubscribe_before_close(ping_connection_->get_pollable());
- ping_connection_->close();
- Scheduler::instance()->finish();
+
+ void tear_down() final {
+ if (is_inited_) {
+ td::Scheduler::unsubscribe_before_close(ping_connection_->get_poll_info().get_pollable_fd_ref());
+ }
+ td::Scheduler::instance()->finish();
}
- void loop() override {
+ void loop() final {
auto status = ping_connection_->flush();
if (status.is_error()) {
*result_ = std::move(status);
return stop();
}
if (ping_connection_->was_pong()) {
- LOG(ERROR) << "GOT PONG";
+ LOG(INFO) << "GOT PONG";
return stop();
}
}
- void timeout_expired() override {
- *result_ = Status::Error("Timeout expired");
+ void timeout_expired() final {
+ *result_ = td::Status::Error("Timeout expired");
stop();
}
};
-static IPAddress get_default_ip_address() {
- IPAddress ip_address;
+static td::IPAddress get_default_ip_address() {
+ td::IPAddress ip_address;
+#if TD_EMSCRIPTEN
+ ip_address.init_host_port("venus.web.telegram.org/apiws", 443).ensure();
+#else
ip_address.init_ipv4_port("149.154.167.40", 80).ensure();
+#endif
return ip_address;
}
-class Mtproto_ping : public td::Test {
+static td::int32 get_default_dc_id() {
+ return 10002;
+}
+
+class Mtproto_ping final : public td::Test {
public:
using Test::Test;
bool step() final {
if (!is_inited_) {
- sched_.init(0);
sched_.create_actor_unsafe<TestPingActor>(0, "Pinger", get_default_ip_address(), &result_).release();
sched_.start();
is_inited_ = true;
@@ -151,57 +284,65 @@ class Mtproto_ping : public td::Test {
if (result_.is_error()) {
LOG(ERROR) << result_;
}
- ASSERT_TRUE(result_.is_ok());
return false;
}
private:
bool is_inited_ = false;
- ConcurrentScheduler sched_;
- Status result_;
+ td::ConcurrentScheduler sched_{0, 0};
+ td::Status result_;
};
-Mtproto_ping mtproto_ping("Mtproto_ping");
+td::RegisterTest<Mtproto_ping> mtproto_ping("Mtproto_ping");
-class Context : public AuthKeyHandshakeContext {
+class HandshakeContext final : public td::mtproto::AuthKeyHandshakeContext {
public:
- DhCallback *get_dh_callback() override {
+ td::mtproto::DhCallback *get_dh_callback() final {
return nullptr;
}
- PublicRsaKeyInterface *get_public_rsa_key_interface() override {
+ td::mtproto::PublicRsaKeyInterface *get_public_rsa_key_interface() final {
return &public_rsa_key;
}
private:
- PublicRsaKeyShared public_rsa_key{DcId::empty()};
+ td::PublicRsaKeyShared public_rsa_key{td::DcId::empty(), true};
};
-class HandshakeTestActor : public Actor {
+class HandshakeTestActor final : public td::Actor {
public:
- explicit HandshakeTestActor(Status *result) : result_(result) {
+ HandshakeTestActor(td::int32 dc_id, td::Status *result) : dc_id_(dc_id), result_(result) {
}
private:
- Status *result_;
+ td::int32 dc_id_ = 0;
+ td::Status *result_;
bool wait_for_raw_connection_ = false;
- std::unique_ptr<RawConnection> raw_connection_;
+ td::unique_ptr<td::mtproto::RawConnection> raw_connection_;
bool wait_for_handshake_ = false;
- std::unique_ptr<AuthKeyHandshake> handshake_;
- Status status_;
+ td::unique_ptr<td::mtproto::AuthKeyHandshake> handshake_;
+ td::Status status_;
bool wait_for_result_ = false;
- void tear_down() override {
+ void tear_down() final {
if (raw_connection_) {
raw_connection_->close();
}
- finish(Status::Error("Interrupted"));
+ finish(td::Status::Error("Interrupted"));
}
- void loop() override {
+ void loop() final {
if (!wait_for_raw_connection_ && !raw_connection_) {
- raw_connection_ = std::make_unique<mtproto::RawConnection>(SocketFd::open(get_default_ip_address()).move_as_ok(),
- mtproto::TransportType::Tcp, nullptr);
+ auto ip_address = get_default_ip_address();
+ auto r_socket = td::SocketFd::open(ip_address);
+ if (r_socket.is_error()) {
+ finish(td::Status::Error(PSTRING() << "Failed to open socket: " << r_socket.error()));
+ return stop();
+ }
+
+ raw_connection_ = td::mtproto::RawConnection::create(
+ ip_address, td::BufferedFd<td::SocketFd>(r_socket.move_as_ok()),
+ td::mtproto::TransportType{td::mtproto::TransportType::Tcp, 0, td::mtproto::ProxySecret()}, nullptr);
}
if (!wait_for_handshake_ && !handshake_) {
- handshake_ = std::make_unique<AuthKeyHandshake>(0);
+ handshake_ = td::make_unique<td::mtproto::AuthKeyHandshake>(dc_id_, 3600);
}
if (raw_connection_ && handshake_) {
if (wait_for_result_) {
@@ -211,34 +352,37 @@ class HandshakeTestActor : public Actor {
return stop();
}
if (!handshake_->is_ready_for_finish()) {
- finish(Status::Error("Key is not ready.."));
+ finish(td::Status::Error("Key is not ready.."));
return stop();
}
- finish(Status::OK());
+ finish(td::Status::OK());
return stop();
}
wait_for_result_ = true;
- create_actor<HandshakeActor>(
- "HandshakeActor", std::move(handshake_), std::move(raw_connection_), std::make_unique<Context>(), 10.0,
- PromiseCreator::lambda([self = actor_id(this)](Result<std::unique_ptr<RawConnection>> raw_connection) {
- send_closure(self, &HandshakeTestActor::got_connection, std::move(raw_connection), 1);
- }),
- PromiseCreator::lambda([self = actor_id(this)](Result<std::unique_ptr<AuthKeyHandshake>> handshake) {
- send_closure(self, &HandshakeTestActor::got_handshake, std::move(handshake), 1);
- }))
+ td::create_actor<td::mtproto::HandshakeActor>(
+ "HandshakeActor", std::move(handshake_), std::move(raw_connection_), td::make_unique<HandshakeContext>(),
+ 10.0,
+ td::PromiseCreator::lambda(
+ [actor_id = actor_id(this)](td::Result<td::unique_ptr<td::mtproto::RawConnection>> raw_connection) {
+ td::send_closure(actor_id, &HandshakeTestActor::got_connection, std::move(raw_connection), 1);
+ }),
+ td::PromiseCreator::lambda(
+ [actor_id = actor_id(this)](td::Result<td::unique_ptr<td::mtproto::AuthKeyHandshake>> handshake) {
+ td::send_closure(actor_id, &HandshakeTestActor::got_handshake, std::move(handshake), 1);
+ }))
.release();
wait_for_raw_connection_ = true;
wait_for_handshake_ = true;
}
}
- void got_connection(Result<std::unique_ptr<RawConnection>> r_raw_connection, int32 dummy) {
+ void got_connection(td::Result<td::unique_ptr<td::mtproto::RawConnection>> r_raw_connection, bool dummy) {
CHECK(wait_for_raw_connection_);
wait_for_raw_connection_ = false;
if (r_raw_connection.is_ok()) {
raw_connection_ = r_raw_connection.move_as_ok();
- status_ = Status::OK();
+ status_ = td::Status::OK();
} else {
status_ = r_raw_connection.move_as_error();
}
@@ -246,7 +390,7 @@ class HandshakeTestActor : public Actor {
loop();
}
- void got_handshake(Result<std::unique_ptr<AuthKeyHandshake>> r_handshake, int32 dummy) {
+ void got_handshake(td::Result<td::unique_ptr<td::mtproto::AuthKeyHandshake>> r_handshake, bool dummy) {
CHECK(wait_for_handshake_);
wait_for_handshake_ = false;
CHECK(r_handshake.is_ok());
@@ -254,23 +398,22 @@ class HandshakeTestActor : public Actor {
loop();
}
- void finish(Status status) {
+ void finish(td::Status status) {
if (!result_) {
return;
}
*result_ = std::move(status);
result_ = nullptr;
- Scheduler::instance()->finish();
+ td::Scheduler::instance()->finish();
}
};
-class Mtproto_handshake : public td::Test {
+class Mtproto_handshake final : public td::Test {
public:
using Test::Test;
bool step() final {
if (!is_inited_) {
- sched_.init(0);
- sched_.create_actor_unsafe<HandshakeTestActor>(0, "HandshakeTestActor", &result_).release();
+ sched_.create_actor_unsafe<HandshakeTestActor>(0, "HandshakeTestActor", get_default_dc_id(), &result_).release();
sched_.start();
is_inited_ = true;
}
@@ -283,60 +426,62 @@ class Mtproto_handshake : public td::Test {
if (result_.is_error()) {
LOG(ERROR) << result_;
}
- ASSERT_TRUE(result_.is_ok());
return false;
}
private:
bool is_inited_ = false;
- ConcurrentScheduler sched_;
- Status result_;
+ td::ConcurrentScheduler sched_{0, 0};
+ td::Status result_;
};
-Mtproto_handshake mtproto_handshake("Mtproto_handshake");
+td::RegisterTest<Mtproto_handshake> mtproto_handshake("Mtproto_handshake");
-class Socks5TestActor : public Actor {
+class Socks5TestActor final : public td::Actor {
public:
- void start_up() override {
- auto promise = PromiseCreator::lambda([actor_id = actor_id(this)](Result<SocketFd> res) {
- send_closure(actor_id, &Socks5TestActor::on_result, std::move(res), false);
- });
+ void start_up() final {
+ auto promise =
+ td::PromiseCreator::lambda([actor_id = actor_id(this)](td::Result<td::BufferedFd<td::SocketFd>> res) {
+ td::send_closure(actor_id, &Socks5TestActor::on_result, std::move(res), false);
+ });
- class Callback : public Socks5::Callback {
+ class Callback final : public td::TransparentProxy::Callback {
public:
- explicit Callback(Promise<SocketFd> promise) : promise_(std::move(promise)) {
+ explicit Callback(td::Promise<td::BufferedFd<td::SocketFd>> promise) : promise_(std::move(promise)) {
}
- void set_result(Result<SocketFd> result) override {
+ void set_result(td::Result<td::BufferedFd<td::SocketFd>> result) final {
promise_.set_result(std::move(result));
}
- void on_connected() override {
+ void on_connected() final {
}
private:
- Promise<SocketFd> promise_;
+ td::Promise<td::BufferedFd<td::SocketFd>> promise_;
};
- IPAddress socks5_ip;
+ td::IPAddress socks5_ip;
socks5_ip.init_ipv4_port("131.191.89.104", 43077).ensure();
- IPAddress mtproto_ip = get_default_ip_address();
+ td::IPAddress mtproto_ip_address = get_default_ip_address();
- auto r_socket = SocketFd::open(socks5_ip);
- create_actor<Socks5>("socks5", r_socket.move_as_ok(), mtproto_ip, "", "",
- std::make_unique<Callback>(std::move(promise)), actor_shared())
+ auto r_socket = td::SocketFd::open(socks5_ip);
+ if (r_socket.is_error()) {
+ return promise.set_error(td::Status::Error(PSTRING() << "Failed to open socket: " << r_socket.error()));
+ }
+ td::create_actor<td::Socks5>("Socks5", r_socket.move_as_ok(), mtproto_ip_address, "", "",
+ td::make_unique<Callback>(std::move(promise)), actor_shared(this))
.release();
}
private:
- void on_result(Result<SocketFd> res, bool dummy) {
+ void on_result(td::Result<td::BufferedFd<td::SocketFd>> res, bool dummy) {
res.ensure();
- Scheduler::instance()->finish();
+ td::Scheduler::instance()->finish();
}
};
TEST(Mtproto, socks5) {
return;
- ConcurrentScheduler sched;
int threads_n = 0;
- sched.init(threads_n);
+ td::ConcurrentScheduler sched(threads_n, 0);
sched.create_actor_unsafe<Socks5TestActor>(0, "Socks5TestActor").release();
sched.start();
@@ -345,3 +490,251 @@ TEST(Mtproto, socks5) {
}
sched.finish();
}
+
+TEST(Mtproto, notifications) {
+ td::vector<td::string> pushes = {
+ "eyJwIjoiSkRnQ3NMRWxEaWhyVWRRN1pYM3J1WVU4TlRBMFhMb0N6UWRNdzJ1cWlqMkdRbVR1WXVvYXhUeFJHaG1QQm8yVElYZFBzX2N3b2RIb3lY"
+ "b2drVjM1dVl0UzdWeElNX1FNMDRKMG1mV3ZZWm4zbEtaVlJ0aFVBNGhYUWlaN0pfWDMyZDBLQUlEOWgzRnZwRjNXUFRHQWRaVkdFYzg3bnFPZ3hD"
+ "NUNMRkM2SU9fZmVqcEpaV2RDRlhBWWpwc1k2aktrbVNRdFZ1MzE5ZW04UFVieXZudFpfdTNud2hjQ0czMk96TGp4S1kyS1lzU21JZm1GMzRmTmw1"
+ "QUxaa2JvY2s2cE5rZEdrak9qYmRLckJyU0ZtWU8tQ0FsRE10dEplZFFnY1U5bVJQdU80b1d2NG5sb1VXS19zSlNTaXdIWEZyb1pWTnZTeFJ0Z1dN"
+ "ZyJ9",
+ "eyJwIjoiSkRnQ3NMRWxEaWlZby1GRWJndk9WaTFkUFdPVmZndzBBWHYwTWNzWDFhWEtNZC03T1Q2WWNfT0taRURHZDJsZ0h0WkhMSllyVG50RE95"
+ "TkY1aXJRQlZ4UUFLQlRBekhPTGZIS3BhQXdoaWd5b3NQd0piWnJVV2xRWmh4eEozUFUzZjBNRTEwX0xNT0pFN0xsVUFaY2dabUNaX2V1QmNPZWNK"
+ "VERxRkpIRGZjN2pBOWNrcFkyNmJRT2dPUEhCeHlEMUVrNVdQcFpLTnlBODVuYzQ1eHFPdERqcU5aVmFLU3pKb2VIcXBQMnJqR29kN2M5YkxsdGd5"
+ "Q0NGd2NBU3dJeDc3QWNWVXY1UnVZIn0"};
+ td::string key =
+ "uBa5yu01a-nJJeqsR3yeqMs6fJLYXjecYzFcvS6jIwS3nefBIr95LWrTm-IbRBNDLrkISz1Sv0KYpDzhU8WFRk1D0V_"
+ "qyO7XsbDPyrYxRBpGxofJUINSjb1uCxoSdoh1_F0UXEA2fWWKKVxL0DKUQssZfbVj3AbRglsWpH-jDK1oc6eBydRiS3i4j-"
+ "H0yJkEMoKRgaF9NaYI4u26oIQ-Ez46kTVU-R7e3acdofOJKm7HIKan_5ZMg82Dvec2M6vc_"
+ "I54Vs28iBx8IbBO1y5z9WSScgW3JCvFFKP2MXIu7Jow5-cpUx6jXdzwRUb9RDApwAFKi45zpv8eb3uPCDAmIQ";
+ td::vector<td::string> decrypted_payloads = {
+ "eyJsb2Nfa2V5IjoiTUVTU0FHRV9URVhUIiwibG9jX2FyZ3MiOlsiQXJzZW55IFNtaXJub3YiLCJhYmNkZWZnIl0sImN1c3RvbSI6eyJtc2dfaWQi"
+ "OiI1OTAwNDciLCJmcm9tX2lkIjoiNjI4MTQifSwiYmFkZ2UiOiI0MDkifQ",
+ "eyJsb2Nfa2V5IjoiIiwibG9jX2FyZ3MiOltdLCJjdXN0b20iOnsiY2hhbm5lbF9pZCI6IjExNzY4OTU0OTciLCJtYXhfaWQiOiIxMzU5In0sImJh"
+ "ZGdlIjoiMCJ9"};
+ key = td::base64url_decode(key).move_as_ok();
+
+ for (size_t i = 0; i < pushes.size(); i++) {
+ auto push = td::base64url_decode(pushes[i]).move_as_ok();
+ auto decrypted_payload = td::base64url_decode(decrypted_payloads[i]).move_as_ok();
+
+ auto key_id = td::mtproto::DhHandshake::calc_key_id(key);
+ ASSERT_EQ(key_id, td::NotificationManager::get_push_receiver_id(push).ok());
+ ASSERT_EQ(decrypted_payload, td::NotificationManager::decrypt_push(key_id, key, push).ok());
+ }
+}
+
+class FastPingTestActor final : public td::Actor {
+ public:
+ explicit FastPingTestActor(td::Status *result) : result_(result) {
+ }
+
+ private:
+ td::Status *result_;
+ td::unique_ptr<td::mtproto::RawConnection> connection_;
+ td::unique_ptr<td::mtproto::AuthKeyHandshake> handshake_;
+ td::ActorOwn<> fast_ping_;
+ int iteration_{0};
+
+ void start_up() final {
+ // Run handshake to create key and salt
+ auto ip_address = get_default_ip_address();
+ auto r_socket = td::SocketFd::open(ip_address);
+ if (r_socket.is_error()) {
+ *result_ = td::Status::Error(PSTRING() << "Failed to open socket: " << r_socket.error());
+ return stop();
+ }
+
+ auto raw_connection = td::mtproto::RawConnection::create(
+ ip_address, td::BufferedFd<td::SocketFd>(r_socket.move_as_ok()),
+ td::mtproto::TransportType{td::mtproto::TransportType::Tcp, 0, td::mtproto::ProxySecret()}, nullptr);
+ auto handshake = td::make_unique<td::mtproto::AuthKeyHandshake>(get_default_dc_id(), 60 * 100 /*temp*/);
+ td::create_actor<td::mtproto::HandshakeActor>(
+ "HandshakeActor", std::move(handshake), std::move(raw_connection), td::make_unique<HandshakeContext>(), 10.0,
+ td::PromiseCreator::lambda(
+ [actor_id = actor_id(this)](td::Result<td::unique_ptr<td::mtproto::RawConnection>> raw_connection) {
+ td::send_closure(actor_id, &FastPingTestActor::got_connection, std::move(raw_connection), 1);
+ }),
+ td::PromiseCreator::lambda(
+ [actor_id = actor_id(this)](td::Result<td::unique_ptr<td::mtproto::AuthKeyHandshake>> handshake) {
+ td::send_closure(actor_id, &FastPingTestActor::got_handshake, std::move(handshake), 1);
+ }))
+ .release();
+ }
+
+ void got_connection(td::Result<td::unique_ptr<td::mtproto::RawConnection>> r_raw_connection, bool dummy) {
+ if (r_raw_connection.is_error()) {
+ *result_ = r_raw_connection.move_as_error();
+ LOG(INFO) << "Receive " << *result_ << " instead of a connection";
+ return stop();
+ }
+ connection_ = r_raw_connection.move_as_ok();
+ loop();
+ }
+
+ void got_handshake(td::Result<td::unique_ptr<td::mtproto::AuthKeyHandshake>> r_handshake, bool dummy) {
+ if (r_handshake.is_error()) {
+ *result_ = r_handshake.move_as_error();
+ LOG(INFO) << "Receive " << *result_ << " instead of a handshake";
+ return stop();
+ }
+ handshake_ = r_handshake.move_as_ok();
+ loop();
+ }
+
+ void got_raw_connection(td::Result<td::unique_ptr<td::mtproto::RawConnection>> r_connection) {
+ if (r_connection.is_error()) {
+ *result_ = r_connection.move_as_error();
+ LOG(INFO) << "Receive " << *result_ << " instead of a handshake";
+ return stop();
+ }
+ connection_ = r_connection.move_as_ok();
+ LOG(INFO) << "RTT: " << connection_->extra().rtt;
+ connection_->extra().rtt = 0;
+ loop();
+ }
+
+ void loop() final {
+ if (handshake_ && connection_) {
+ LOG(INFO) << "Iteration " << iteration_;
+ if (iteration_ == 6) {
+ return stop();
+ }
+ td::unique_ptr<td::mtproto::AuthData> auth_data;
+ if (iteration_ % 2 == 0) {
+ auth_data = td::make_unique<td::mtproto::AuthData>();
+ auth_data->set_tmp_auth_key(handshake_->get_auth_key());
+ auth_data->set_server_time_difference(handshake_->get_server_time_diff());
+ auth_data->set_server_salt(handshake_->get_server_salt(), td::Time::now());
+ auth_data->set_future_salts({td::mtproto::ServerSalt{0u, 1e20, 1e30}}, td::Time::now());
+ auth_data->set_use_pfs(true);
+ td::uint64 session_id = 0;
+ do {
+ td::Random::secure_bytes(reinterpret_cast<td::uint8 *>(&session_id), sizeof(session_id));
+ } while (session_id == 0);
+ auth_data->set_session_id(session_id);
+ }
+ iteration_++;
+ fast_ping_ = create_ping_actor(
+ td::Slice(), std::move(connection_), std::move(auth_data),
+ td::PromiseCreator::lambda(
+ [actor_id = actor_id(this)](td::Result<td::unique_ptr<td::mtproto::RawConnection>> r_raw_connection) {
+ td::send_closure(actor_id, &FastPingTestActor::got_raw_connection, std::move(r_raw_connection));
+ }),
+ td::ActorShared<>());
+ }
+ }
+
+ void tear_down() final {
+ td::Scheduler::instance()->finish();
+ }
+};
+
+class Mtproto_FastPing final : public td::Test {
+ public:
+ using Test::Test;
+ bool step() final {
+ if (!is_inited_) {
+ sched_.create_actor_unsafe<FastPingTestActor>(0, "FastPingTestActor", &result_).release();
+ sched_.start();
+ is_inited_ = true;
+ }
+
+ bool ret = sched_.run_main(10);
+ if (ret) {
+ return true;
+ }
+ sched_.finish();
+ if (result_.is_error()) {
+ LOG(ERROR) << result_;
+ }
+ return false;
+ }
+
+ private:
+ bool is_inited_ = false;
+ td::ConcurrentScheduler sched_{0, 0};
+ td::Status result_;
+};
+td::RegisterTest<Mtproto_FastPing> mtproto_fastping("Mtproto_FastPing");
+
+TEST(Mtproto, Grease) {
+ td::string s(10000, '0');
+ td::mtproto::Grease::init(s);
+ for (auto c : s) {
+ CHECK((c & 0xF) == 0xA);
+ }
+ for (size_t i = 1; i < s.size(); i += 2) {
+ CHECK(s[i] != s[i - 1]);
+ }
+}
+
+TEST(Mtproto, TlsTransport) {
+ int threads_n = 1;
+ td::ConcurrentScheduler sched(threads_n, 0);
+ {
+ auto guard = sched.get_main_guard();
+ class RunTest final : public td::Actor {
+ void start_up() final {
+ class Callback final : public td::TransparentProxy::Callback {
+ public:
+ void set_result(td::Result<td::BufferedFd<td::SocketFd>> result) final {
+ if (result.is_ok()) {
+ LOG(ERROR) << "Unexpectedly succeeded to connect to MTProto proxy";
+ } else if (result.error().message() != "Response hash mismatch") {
+ LOG(ERROR) << "Receive unexpected result " << result.error();
+ }
+ td::Scheduler::instance()->finish();
+ }
+ void on_connected() final {
+ }
+ };
+
+ const td::string domain = "www.google.com";
+ td::IPAddress ip_address;
+ auto resolve_status = ip_address.init_host_port(domain, 443);
+ if (resolve_status.is_error()) {
+ LOG(ERROR) << resolve_status;
+ td::Scheduler::instance()->finish();
+ return;
+ }
+ auto r_socket = td::SocketFd::open(ip_address);
+ if (r_socket.is_error()) {
+ LOG(ERROR) << "Failed to open socket: " << r_socket.error();
+ td::Scheduler::instance()->finish();
+ return;
+ }
+ td::create_actor<td::mtproto::TlsInit>("TlsInit", r_socket.move_as_ok(), domain, "0123456789secret",
+ td::make_unique<Callback>(), td::ActorShared<>(),
+ td::Clocks::system() - td::Time::now())
+ .release();
+ }
+ };
+ td::create_actor<RunTest>("RunTest").release();
+ }
+
+ sched.start();
+ while (sched.run_main(10)) {
+ // empty
+ }
+ sched.finish();
+}
+
+TEST(Mtproto, RSA) {
+ auto pem = td::Slice(
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIIBCgKCAQEAr4v4wxMDXIaMOh8bayF/NyoYdpcysn5EbjTIOZC0RkgzsRj3SGlu\n"
+ "52QSz+ysO41dQAjpFLgxPVJoOlxXokaOq827IfW0bGCm0doT5hxtedu9UCQKbE8j\n"
+ "lDOk+kWMXHPZFJKWRgKgTu9hcB3y3Vk+JFfLpq3d5ZB48B4bcwrRQnzkx5GhWOFX\n"
+ "x73ZgjO93eoQ2b/lDyXxK4B4IS+hZhjzezPZTI5upTRbs5ljlApsddsHrKk6jJNj\n"
+ "8Ygs/ps8e6ct82jLXbnndC9s8HjEvDvBPH9IPjv5JUlmHMBFZ5vFQIfbpo0u0+1P\n"
+ "n6bkEi5o7/ifoyVv2pAZTRwppTz0EuXD8QIDAQAB\n"
+ "-----END RSA PUBLIC KEY-----");
+ auto rsa = td::mtproto::RSA::from_pem_public_key(pem).move_as_ok();
+ ASSERT_EQ(-7596991558377038078, rsa.get_fingerprint());
+ ASSERT_EQ(256u, rsa.size());
+
+ td::string to(256, '\0');
+ rsa.encrypt(pem.substr(0, 256), to);
+ ASSERT_EQ("U2nJEtB2AgpHrm3HB0yhpTQgb0wbesi9Pv/W1v/vULU=", td::base64_encode(td::sha256(to)));
+}