ConfigRecoverer: support tls obfuscation
GitOrigin-RevId: 9a924ee86fe042a9fd1acf44eeeb25c0ab4e8bbb
This commit is contained in:
parent
64517c9165
commit
8fdd8910e7
@ -53,9 +53,85 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
namespace td {
|
||||
|
||||
int VERBOSITY_NAME(config_recoverer) = VERBOSITY_NAME(INFO);
|
||||
Result<int64> HttpDate::to_unix_time(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second) {
|
||||
int64 res = 0;
|
||||
if (year < 1970 || year > 2037) {
|
||||
return td::Status::Error("invalid year");
|
||||
}
|
||||
if (month < 1 || month > 12) {
|
||||
return td::Status::Error("invalid month");
|
||||
}
|
||||
if (day < 1 || day > days_in_month(year, month)) {
|
||||
return td::Status::Error("invalid day");
|
||||
}
|
||||
if (hour < 0 || hour > 24) { // is hour == 24 possible?
|
||||
return td::Status::Error("invalid hour");
|
||||
}
|
||||
if (minute < 0 || minute > 60) {
|
||||
return td::Status::Error("invalid minute");
|
||||
}
|
||||
if (second < 0 || second > 60) {
|
||||
return td::Status::Error("invalid second");
|
||||
}
|
||||
for (int y = 1970; y < year; y++) {
|
||||
res += (is_leap(y) + 365) * seconds_in_day();
|
||||
}
|
||||
for (int m = 1; m < month; m++) {
|
||||
res += days_in_month(year, m) * seconds_in_day();
|
||||
}
|
||||
res += (day - 1) * seconds_in_day();
|
||||
res += hour * 60 * 60;
|
||||
res += minute * 60;
|
||||
res += second;
|
||||
return res;
|
||||
}
|
||||
|
||||
Result<int64> HttpDate::parse_http_date(std::string slice) {
|
||||
td::Parser p(slice);
|
||||
p.read_till(','); // ignore week day
|
||||
p.skip(',');
|
||||
p.skip_whitespaces();
|
||||
TRY_RESULT(day, to_integer_safe<int32>(p.read_word()));
|
||||
auto month_name = p.read_word();
|
||||
to_lower_inplace(month_name);
|
||||
TRY_RESULT(year, to_integer_safe<int32>(p.read_word()));
|
||||
p.skip_whitespaces();
|
||||
p.skip_nofail('0');
|
||||
TRY_RESULT(hour, to_integer_safe<int32>(p.read_till(':')));
|
||||
p.skip(':');
|
||||
p.skip_nofail('0');
|
||||
TRY_RESULT(minute, to_integer_safe<int32>(p.read_till(':')));
|
||||
p.skip(':');
|
||||
p.skip_nofail('0');
|
||||
TRY_RESULT(second, to_integer_safe<int32>(p.read_word()));
|
||||
auto gmt = p.read_word();
|
||||
TRY_STATUS(std::move(p.status()));
|
||||
if (gmt != "GMT") {
|
||||
return Status::Error("timezone must be GMT");
|
||||
}
|
||||
|
||||
Slice month_names[12] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"};
|
||||
|
||||
int month = 0;
|
||||
|
||||
for (int m = 1; m <= 12; m++) {
|
||||
if (month_names[m - 1] == month_name) {
|
||||
month = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (month == 0) {
|
||||
return Status::Error("Unknown month name");
|
||||
}
|
||||
|
||||
return HttpDate::to_unix_time(year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
Result<SimpleConfig> decode_config(Slice input) {
|
||||
static auto rsa = td::RSA::from_pem(
|
||||
@ -117,8 +193,8 @@ Result<SimpleConfig> decode_config(Slice input) {
|
||||
return std::move(config);
|
||||
}
|
||||
|
||||
static ActorOwn<> get_simple_config_impl(Promise<SimpleConfig> promise, int32 scheduler_id, string url, string host,
|
||||
bool prefer_ipv6) {
|
||||
static ActorOwn<> get_simple_config_impl(Promise<SimpleConfigResult> promise, int32 scheduler_id, string url,
|
||||
string host, bool prefer_ipv6) {
|
||||
VLOG(config_recoverer) << "Request simple config from " << url;
|
||||
#if TD_EMSCRIPTEN // FIXME
|
||||
return ActorOwn<>();
|
||||
@ -128,9 +204,12 @@ static ActorOwn<> get_simple_config_impl(Promise<SimpleConfig> promise, int32 sc
|
||||
return ActorOwn<>(create_actor_on_scheduler<Wget>(
|
||||
"Wget", scheduler_id,
|
||||
PromiseCreator::lambda([promise = std::move(promise)](Result<unique_ptr<HttpQuery>> r_query) mutable {
|
||||
promise.set_result([&]() -> Result<SimpleConfig> {
|
||||
promise.set_result([&]() -> Result<SimpleConfigResult> {
|
||||
TRY_RESULT(http_query, std::move(r_query));
|
||||
return decode_config(http_query->content_);
|
||||
SimpleConfigResult res;
|
||||
res.r_http_date = HttpDate::parse_http_date(http_query->get_arg("date").str());
|
||||
res.r_config = decode_config(http_query->content_);
|
||||
return res;
|
||||
}());
|
||||
}),
|
||||
std::move(url), std::vector<std::pair<string, string>>({{"Host", std::move(host)}}), timeout, ttl, prefer_ipv6,
|
||||
@ -138,7 +217,7 @@ static ActorOwn<> get_simple_config_impl(Promise<SimpleConfig> promise, int32 sc
|
||||
#endif
|
||||
}
|
||||
|
||||
ActorOwn<> get_simple_config_azure(Promise<SimpleConfig> promise, const ConfigShared *shared_config, bool is_test,
|
||||
ActorOwn<> get_simple_config_azure(Promise<SimpleConfigResult> promise, const ConfigShared *shared_config, bool is_test,
|
||||
int32 scheduler_id) {
|
||||
string url = PSTRING() << "https://software-download.microsoft.com/" << (is_test ? "test" : "prod")
|
||||
<< "v2/config.txt";
|
||||
@ -146,8 +225,8 @@ ActorOwn<> get_simple_config_azure(Promise<SimpleConfig> promise, const ConfigSh
|
||||
return get_simple_config_impl(std::move(promise), scheduler_id, std::move(url), "tcdnb.azureedge.net", prefer_ipv6);
|
||||
}
|
||||
|
||||
ActorOwn<> get_simple_config_google_dns(Promise<SimpleConfig> promise, const ConfigShared *shared_config, bool is_test,
|
||||
int32 scheduler_id) {
|
||||
ActorOwn<> get_simple_config_google_dns(Promise<SimpleConfigResult> promise, const ConfigShared *shared_config,
|
||||
bool is_test, int32 scheduler_id) {
|
||||
VLOG(config_recoverer) << "Request simple config from Google DNS";
|
||||
#if TD_EMSCRIPTEN // FIXME
|
||||
return ActorOwn<>();
|
||||
@ -156,40 +235,47 @@ ActorOwn<> get_simple_config_google_dns(Promise<SimpleConfig> promise, const Con
|
||||
const int timeout = 10;
|
||||
const int ttl = 3;
|
||||
const bool prefer_ipv6 = shared_config == nullptr ? false : shared_config->get_option_boolean("prefer_ipv6");
|
||||
if (name.empty()) {
|
||||
name = is_test ? "tapv2.stel.com" : "apv2.stel.com";
|
||||
if (name.empty() || true) {
|
||||
name = is_test ? "tapv3.stel.com" : "apv3.stel.com";
|
||||
}
|
||||
return ActorOwn<>(create_actor_on_scheduler<Wget>(
|
||||
"Wget", scheduler_id,
|
||||
PromiseCreator::lambda([promise = std::move(promise)](Result<unique_ptr<HttpQuery>> r_query) mutable {
|
||||
promise.set_result([&]() -> Result<SimpleConfig> {
|
||||
promise.set_result([&]() -> Result<SimpleConfigResult> {
|
||||
TRY_RESULT(http_query, std::move(r_query));
|
||||
TRY_RESULT(json, json_decode(http_query->content_));
|
||||
if (json.type() != JsonValue::Type::Object) {
|
||||
return Status::Error("json error");
|
||||
}
|
||||
auto &answer_object = json.get_object();
|
||||
TRY_RESULT(answer, get_json_object_field(answer_object, "Answer", JsonValue::Type::Array, false));
|
||||
auto &answer_array = answer.get_array();
|
||||
vector<string> parts;
|
||||
for (auto &v : answer_array) {
|
||||
if (v.type() != JsonValue::Type::Object) {
|
||||
LOG(ERROR) << *http_query;
|
||||
|
||||
SimpleConfigResult res;
|
||||
res.r_http_date = HttpDate::parse_http_date(http_query->get_arg("date").str());
|
||||
res.r_config = [&]() -> Result<SimpleConfig> {
|
||||
TRY_RESULT(json, json_decode(http_query->content_));
|
||||
if (json.type() != JsonValue::Type::Object) {
|
||||
return Status::Error("json error");
|
||||
}
|
||||
auto &data_object = v.get_object();
|
||||
TRY_RESULT(part, get_json_object_string_field(data_object, "data", false));
|
||||
parts.push_back(std::move(part));
|
||||
}
|
||||
if (parts.size() != 2) {
|
||||
return Status::Error("Expected data in two parts");
|
||||
}
|
||||
string data;
|
||||
if (parts[0].size() < parts[1].size()) {
|
||||
data = parts[1] + parts[0];
|
||||
} else {
|
||||
data = parts[0] + parts[1];
|
||||
}
|
||||
return decode_config(data);
|
||||
auto &answer_object = json.get_object();
|
||||
TRY_RESULT(answer, get_json_object_field(answer_object, "Answer", JsonValue::Type::Array, false));
|
||||
auto &answer_array = answer.get_array();
|
||||
vector<string> parts;
|
||||
for (auto &v : answer_array) {
|
||||
if (v.type() != JsonValue::Type::Object) {
|
||||
return Status::Error("json error");
|
||||
}
|
||||
auto &data_object = v.get_object();
|
||||
TRY_RESULT(part, get_json_object_string_field(data_object, "data", false));
|
||||
parts.push_back(std::move(part));
|
||||
}
|
||||
if (parts.size() != 2) {
|
||||
return Status::Error("Expected data in two parts");
|
||||
}
|
||||
string data;
|
||||
if (parts[0].size() < parts[1].size()) {
|
||||
data = parts[1] + parts[0];
|
||||
} else {
|
||||
data = parts[0] + parts[1];
|
||||
}
|
||||
return decode_config(data);
|
||||
}();
|
||||
return res;
|
||||
}());
|
||||
}),
|
||||
PSTRING() << "https://www.google.com/resolve?name=" << url_encode(name) << "&type=16",
|
||||
@ -198,11 +284,10 @@ ActorOwn<> get_simple_config_google_dns(Promise<SimpleConfig> promise, const Con
|
||||
#endif
|
||||
}
|
||||
|
||||
ActorOwn<> get_full_config(DcId dc_id, IPAddress ip_address, mtproto::ProxySecret secret, Promise<FullConfig> promise) {
|
||||
ActorOwn<> get_full_config(DcOption option, Promise<FullConfig> promise) {
|
||||
class SessionCallback : public Session::Callback {
|
||||
public:
|
||||
SessionCallback(ActorShared<> parent, IPAddress address, mtproto::ProxySecret secret)
|
||||
: parent_(std::move(parent)), address_(std::move(address)), secret_(std::move(secret)) {
|
||||
SessionCallback(ActorShared<> parent, DcOption option) : parent_(std::move(parent)), option_(std::move(option)) {
|
||||
}
|
||||
void on_failed() final {
|
||||
}
|
||||
@ -211,10 +296,14 @@ ActorOwn<> get_full_config(DcId dc_id, IPAddress ip_address, mtproto::ProxySecre
|
||||
void request_raw_connection(unique_ptr<mtproto::AuthData> auth_data,
|
||||
Promise<unique_ptr<mtproto::RawConnection>> promise) final {
|
||||
request_raw_connection_cnt_++;
|
||||
VLOG(config_recoverer) << "Request full config from " << address_ << ", try = " << request_raw_connection_cnt_;
|
||||
VLOG(config_recoverer) << "Request full config from " << option_.get_ip_address()
|
||||
<< ", try = " << request_raw_connection_cnt_;
|
||||
if (request_raw_connection_cnt_ <= 2) {
|
||||
send_closure(G()->connection_creator(), &ConnectionCreator::request_raw_connection_by_ip, address_,
|
||||
mtproto::TransportType{mtproto::TransportType::ObfuscatedTcp, 0, secret_}, std::move(promise));
|
||||
send_closure(G()->connection_creator(), &ConnectionCreator::request_raw_connection_by_ip,
|
||||
option_.get_ip_address(),
|
||||
mtproto::TransportType{mtproto::TransportType::ObfuscatedTcp,
|
||||
narrow_cast<int16>(option_.get_dc_id().get_raw_id()), option_.get_secret()},
|
||||
std::move(promise));
|
||||
} else {
|
||||
// Delay all queries except first forever
|
||||
delay_forever_.push_back(std::move(promise));
|
||||
@ -229,8 +318,7 @@ ActorOwn<> get_full_config(DcId dc_id, IPAddress ip_address, mtproto::ProxySecre
|
||||
|
||||
private:
|
||||
ActorShared<> parent_;
|
||||
IPAddress address_;
|
||||
mtproto::ProxySecret secret_;
|
||||
DcOption option_;
|
||||
size_t request_raw_connection_cnt_{0};
|
||||
std::vector<Promise<unique_ptr<mtproto::RawConnection>>> delay_forever_;
|
||||
};
|
||||
@ -311,17 +399,16 @@ ActorOwn<> get_full_config(DcId dc_id, IPAddress ip_address, mtproto::ProxySecre
|
||||
|
||||
class GetConfigActor : public NetQueryCallback {
|
||||
public:
|
||||
GetConfigActor(DcId dc_id, IPAddress ip_address, mtproto::ProxySecret secret, Promise<FullConfig> promise)
|
||||
: dc_id_(dc_id), ip_address_(std::move(ip_address)), secret_(std::move(secret)), promise_(std::move(promise)) {
|
||||
GetConfigActor(DcOption option, Promise<FullConfig> promise)
|
||||
: option_(std::move(option)), promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
private:
|
||||
void start_up() override {
|
||||
auto session_callback =
|
||||
make_unique<SessionCallback>(actor_shared(this, 1), std::move(ip_address_), std::move(secret_));
|
||||
auto auth_data = std::make_shared<SimpleAuthData>(option_.get_dc_id());
|
||||
int32 int_dc_id = option_.get_dc_id().get_raw_id();
|
||||
auto session_callback = make_unique<SessionCallback>(actor_shared(this, 1), std::move(option_));
|
||||
|
||||
auto auth_data = std::make_shared<SimpleAuthData>(dc_id_);
|
||||
int32 int_dc_id = dc_id_.get_raw_id();
|
||||
if (G()->is_test_dc()) {
|
||||
int_dc_id += 10000;
|
||||
}
|
||||
@ -357,15 +444,12 @@ ActorOwn<> get_full_config(DcId dc_id, IPAddress ip_address, mtproto::ProxySecre
|
||||
session_.reset();
|
||||
}
|
||||
|
||||
DcId dc_id_;
|
||||
IPAddress ip_address_;
|
||||
DcOption option_;
|
||||
ActorOwn<Session> session_;
|
||||
mtproto::ProxySecret secret_;
|
||||
Promise<FullConfig> promise_;
|
||||
};
|
||||
|
||||
return ActorOwn<>(create_actor<GetConfigActor>("GetConfigActor", dc_id, std::move(ip_address), std::move(secret),
|
||||
std::move(promise)));
|
||||
return ActorOwn<>(create_actor<GetConfigActor>("GetConfigActor", option, std::move(promise)));
|
||||
}
|
||||
|
||||
class ConfigRecoverer : public Actor {
|
||||
@ -434,9 +518,31 @@ class ConfigRecoverer : public Actor {
|
||||
return found;
|
||||
}
|
||||
|
||||
void on_simple_config(Result<SimpleConfig> r_simple_config, bool dummy) {
|
||||
void on_simple_config(Result<SimpleConfigResult> r_simple_config_result, bool dummy) {
|
||||
simple_config_query_.reset();
|
||||
dc_options_i_ = 0;
|
||||
|
||||
SimpleConfigResult cfg;
|
||||
if (r_simple_config_result.is_error()) {
|
||||
cfg.r_http_date = r_simple_config_result.error().clone();
|
||||
cfg.r_config = r_simple_config_result.move_as_error();
|
||||
} else {
|
||||
cfg = r_simple_config_result.move_as_ok();
|
||||
}
|
||||
|
||||
if (cfg.r_http_date.is_ok() && (date_option_i_ == 0 || cfg.r_config.is_error())) {
|
||||
G()->update_dns_time_difference(cfg.r_http_date.ok() - Time::now());
|
||||
} else if (cfg.r_config.is_ok()) {
|
||||
G()->update_dns_time_difference(cfg.r_config.ok()->date_ - Time::now());
|
||||
}
|
||||
date_option_i_ = (date_option_i_ + 1) % 2;
|
||||
|
||||
do_on_simple_config(std::move(cfg.r_config));
|
||||
update_dc_options();
|
||||
loop();
|
||||
}
|
||||
|
||||
void do_on_simple_config(Result<SimpleConfig> r_simple_config) {
|
||||
if (r_simple_config.is_ok()) {
|
||||
auto config = r_simple_config.move_as_ok();
|
||||
VLOG(config_recoverer) << "Receive raw " << to_string(config);
|
||||
@ -460,7 +566,6 @@ class ConfigRecoverer : public Actor {
|
||||
VLOG(config_recoverer) << "Config has expired at " << config->expires_;
|
||||
}
|
||||
|
||||
G()->update_dns_time_difference(config->date_ - Time::now());
|
||||
simple_config_expires_at_ = get_config_expire_time();
|
||||
simple_config_at_ = Time::now_cached();
|
||||
for (size_t i = 1; i < simple_config_.dc_options.size(); i++) {
|
||||
@ -471,8 +576,6 @@ class ConfigRecoverer : public Actor {
|
||||
simple_config_ = DcOptions();
|
||||
simple_config_expires_at_ = get_failed_config_expire_time();
|
||||
}
|
||||
update_dc_options();
|
||||
loop();
|
||||
}
|
||||
|
||||
void on_full_config(Result<FullConfig> r_full_config, bool dummy) {
|
||||
@ -526,6 +629,8 @@ class ConfigRecoverer : public Actor {
|
||||
double dc_options_at_{0};
|
||||
size_t dc_options_i_;
|
||||
|
||||
size_t date_option_i_{0};
|
||||
|
||||
FullConfig full_config_;
|
||||
double full_config_expires_at_{0};
|
||||
ActorOwn<> full_config_query_;
|
||||
@ -593,9 +698,10 @@ class ConfigRecoverer : public Actor {
|
||||
if (need_simple_config) {
|
||||
ref_cnt_++;
|
||||
VLOG(config_recoverer) << "ASK SIMPLE CONFIG";
|
||||
auto promise = PromiseCreator::lambda([actor_id = actor_shared(this)](Result<SimpleConfig> r_simple_config) {
|
||||
send_closure(actor_id, &ConfigRecoverer::on_simple_config, std::move(r_simple_config), false);
|
||||
});
|
||||
auto promise =
|
||||
PromiseCreator::lambda([actor_id = actor_shared(this)](Result<SimpleConfigResult> r_simple_config) {
|
||||
send_closure(actor_id, &ConfigRecoverer::on_simple_config, std::move(r_simple_config), false);
|
||||
});
|
||||
auto get_simple_config = [&]() {
|
||||
switch (simple_config_turn_ % 3) {
|
||||
case 1:
|
||||
@ -614,12 +720,11 @@ class ConfigRecoverer : public Actor {
|
||||
if (need_full_config) {
|
||||
ref_cnt_++;
|
||||
VLOG(config_recoverer) << "ASK FULL CONFIG";
|
||||
full_config_query_ = get_full_config(
|
||||
dc_options_.dc_options[dc_options_i_].get_dc_id(), dc_options_.dc_options[dc_options_i_].get_ip_address(),
|
||||
dc_options_.dc_options[dc_options_i_].get_secret(),
|
||||
PromiseCreator::lambda([actor_id = actor_shared(this)](Result<FullConfig> r_full_config) {
|
||||
send_closure(actor_id, &ConfigRecoverer::on_full_config, std::move(r_full_config), false);
|
||||
}));
|
||||
full_config_query_ =
|
||||
get_full_config(dc_options_.dc_options[dc_options_i_],
|
||||
PromiseCreator::lambda([actor_id = actor_shared(this)](Result<FullConfig> r_full_config) {
|
||||
send_closure(actor_id, &ConfigRecoverer::on_full_config, std::move(r_full_config), false);
|
||||
}));
|
||||
dc_options_i_ = (dc_options_i_ + 1) % dc_options_.dc_options.size();
|
||||
}
|
||||
|
||||
|
@ -28,14 +28,34 @@ extern int VERBOSITY_NAME(config_recoverer);
|
||||
class ConfigShared;
|
||||
|
||||
using SimpleConfig = tl_object_ptr<telegram_api::help_configSimple>;
|
||||
struct SimpleConfigResult {
|
||||
Result<SimpleConfig> r_config;
|
||||
Result<int64> r_http_date;
|
||||
};
|
||||
|
||||
Result<SimpleConfig> decode_config(Slice input);
|
||||
|
||||
ActorOwn<> get_simple_config_azure(Promise<SimpleConfig> promise, const ConfigShared *shared_config, bool is_test,
|
||||
ActorOwn<> get_simple_config_azure(Promise<SimpleConfigResult> promise, const ConfigShared *shared_config, bool is_test,
|
||||
int32 scheduler_id);
|
||||
|
||||
ActorOwn<> get_simple_config_google_dns(Promise<SimpleConfig> promise, const ConfigShared *shared_config, bool is_test,
|
||||
int32 scheduler_id);
|
||||
ActorOwn<> get_simple_config_google_dns(Promise<SimpleConfigResult> promise, const ConfigShared *shared_config,
|
||||
bool is_test, int32 scheduler_id);
|
||||
|
||||
class HttpDate {
|
||||
public:
|
||||
static bool is_leap(int year) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||
}
|
||||
static int32 days_in_month(int year, int month) {
|
||||
static int cnt[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
return cnt[month - 1] + (month == 2 && is_leap(year));
|
||||
}
|
||||
static int64 seconds_in_day() {
|
||||
return 24 * 60 * 60;
|
||||
}
|
||||
static Result<int64> to_unix_time(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second);
|
||||
static Result<int64> parse_http_date(std::string slice);
|
||||
};
|
||||
|
||||
using FullConfig = tl_object_ptr<telegram_api::config>;
|
||||
|
||||
|
@ -753,6 +753,9 @@ void ConnectionCreator::request_raw_connection_by_ip(IPAddress ip_address, mtpro
|
||||
};
|
||||
auto token = next_token();
|
||||
auto callback = td::make_unique<Callback>(std::move(socket_fd_promise));
|
||||
LOG(INFO) << "Tls in ConfigRecoverer " << ip_address << " " << transport_type.secret.get_domain() << " "
|
||||
<< transport_type.secret.emulate_tls() << " " << transport_type.secret.get_proxy_secret().size()
|
||||
<< transport_type.secret.get_encoded_secret();
|
||||
children_[token] = {false, create_actor<mtproto::TlsInit>(
|
||||
"TlsInit", std::move(socket_fd), ip_address, transport_type.secret.get_domain(),
|
||||
transport_type.secret.get_proxy_secret().str(), std::move(callback),
|
||||
@ -984,7 +987,8 @@ void ConnectionCreator::client_loop(ClientInfo &client) {
|
||||
bool was_connected_{false};
|
||||
unique_ptr<detail::StatsCallback> stats_callback_;
|
||||
};
|
||||
LOG(INFO) << "Start " << (proxy.use_socks5_proxy() ? "Socks5" : "HTTP") << ": " << extra.debug_str;
|
||||
LOG(INFO) << "Start " << (proxy.use_socks5_proxy() ? "Socks5" : (proxy.use_http_tcp_proxy() ? "HTTP" : "Tls"))
|
||||
<< ": " << extra.debug_str;
|
||||
auto token = next_token();
|
||||
auto callback = td::make_unique<Callback>(std::move(promise), std::move(stats_callback));
|
||||
if (proxy.use_socks5_proxy()) {
|
||||
|
102
test/mtproto.cpp
102
test/mtproto.cpp
@ -121,105 +121,17 @@ TEST(Mtproto, GetHostByNameActor) {
|
||||
sched.finish();
|
||||
}
|
||||
|
||||
class Date {
|
||||
public:
|
||||
static bool is_leap(int year) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||
}
|
||||
static int32 days_in_month(int year, int month) {
|
||||
int cnt[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
return cnt[month - 1] + (month == 2 && is_leap(year));
|
||||
}
|
||||
static int64 seconds_in_day() {
|
||||
return 24 * 60 * 60;
|
||||
}
|
||||
static Result<int64> to_unix_time(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second) {
|
||||
int64 res = 0;
|
||||
if (year < 1970 || year > 2037) {
|
||||
return td::Status::Error("invalid year");
|
||||
}
|
||||
if (month < 1 || month > 12) {
|
||||
return td::Status::Error("invalid month");
|
||||
}
|
||||
if (day < 1 || day > days_in_month(year, month)) {
|
||||
return td::Status::Error("invalid day");
|
||||
}
|
||||
if (hour < 0 || hour > 24) { // is hour == 24 possible?
|
||||
return td::Status::Error("invalid hour");
|
||||
}
|
||||
if (minute < 0 || minute > 60) {
|
||||
return td::Status::Error("invalid minute");
|
||||
}
|
||||
if (second < 0 || second > 60) {
|
||||
return td::Status::Error("invalid second");
|
||||
}
|
||||
for (int y = 1970; y < year; y++) {
|
||||
res += (is_leap(y) + 365) * seconds_in_day();
|
||||
}
|
||||
for (int m = 1; m < month; m++) {
|
||||
res += days_in_month(year, m) * seconds_in_day();
|
||||
}
|
||||
res += (day - 1) * seconds_in_day();
|
||||
res += hour * 60 * 60;
|
||||
res += minute * 60;
|
||||
res += second;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Time, to_unix_time) {
|
||||
ASSERT_EQ(0, Date::to_unix_time(1970, 1, 1, 0, 0, 0).move_as_ok());
|
||||
ASSERT_EQ(60 * 60 + 60 + 1, Date::to_unix_time(1970, 1, 1, 1, 1, 1).move_as_ok());
|
||||
ASSERT_EQ(24 * 60 * 60, Date::to_unix_time(1970, 1, 2, 0, 0, 0).move_as_ok());
|
||||
ASSERT_EQ(31 * 24 * 60 * 60, Date::to_unix_time(1970, 2, 1, 0, 0, 0).move_as_ok());
|
||||
ASSERT_EQ(365 * 24 * 60 * 60, Date::to_unix_time(1971, 1, 1, 0, 0, 0).move_as_ok());
|
||||
ASSERT_EQ(1562780559, Date::to_unix_time(2019, 7, 10, 17, 42, 39).move_as_ok());
|
||||
}
|
||||
|
||||
Result<int64> parse_http_date(std::string slice) {
|
||||
td::Parser p(slice);
|
||||
p.read_till(','); // ignore week day
|
||||
p.skip(',');
|
||||
p.skip_whitespaces();
|
||||
TRY_RESULT(day, to_integer_safe<int32>(p.read_word()));
|
||||
auto month_name = p.read_word();
|
||||
to_lower_inplace(month_name);
|
||||
TRY_RESULT(year, to_integer_safe<int32>(p.read_word()));
|
||||
p.skip_whitespaces();
|
||||
p.skip_nofail('0');
|
||||
TRY_RESULT(hour, to_integer_safe<int32>(p.read_till(':')));
|
||||
p.skip(':');
|
||||
p.skip_nofail('0');
|
||||
TRY_RESULT(minute, to_integer_safe<int32>(p.read_till(':')));
|
||||
p.skip(':');
|
||||
p.skip_nofail('0');
|
||||
TRY_RESULT(second, to_integer_safe<int32>(p.read_word()));
|
||||
auto gmt = p.read_word();
|
||||
TRY_STATUS(std::move(p.status()));
|
||||
if (gmt != "GMT") {
|
||||
return Status::Error("timezone must be GMT");
|
||||
}
|
||||
|
||||
Slice month_names[12] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"};
|
||||
|
||||
int month = 0;
|
||||
|
||||
for (int m = 1; m <= 12; m++) {
|
||||
if (month_names[m - 1] == month_name) {
|
||||
month = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (month == 0) {
|
||||
return Status::Error("Unknown month name");
|
||||
}
|
||||
|
||||
return Date::to_unix_time(year, month, day, hour, minute, second);
|
||||
ASSERT_EQ(0, HttpDate::to_unix_time(1970, 1, 1, 0, 0, 0).move_as_ok());
|
||||
ASSERT_EQ(60 * 60 + 60 + 1, HttpDate::to_unix_time(1970, 1, 1, 1, 1, 1).move_as_ok());
|
||||
ASSERT_EQ(24 * 60 * 60, HttpDate::to_unix_time(1970, 1, 2, 0, 0, 0).move_as_ok());
|
||||
ASSERT_EQ(31 * 24 * 60 * 60, HttpDate::to_unix_time(1970, 2, 1, 0, 0, 0).move_as_ok());
|
||||
ASSERT_EQ(365 * 24 * 60 * 60, HttpDate::to_unix_time(1971, 1, 1, 0, 0, 0).move_as_ok());
|
||||
ASSERT_EQ(1562780559, HttpDate::to_unix_time(2019, 7, 10, 17, 42, 39).move_as_ok());
|
||||
}
|
||||
|
||||
TEST(Time, parse_http_date) {
|
||||
ASSERT_EQ(784887151, parse_http_date("Tue, 15 Nov 1994 08:12:31 GMT").move_as_ok());
|
||||
ASSERT_EQ(784887151, HttpDate::parse_http_date("Tue, 15 Nov 1994 08:12:31 GMT").move_as_ok());
|
||||
}
|
||||
|
||||
TEST(Mtproto, config) {
|
||||
|
Reference in New Issue
Block a user