From 68b1024dd4b5738e9c502d10659059393ba56fac Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 10 Jul 2019 17:03:48 +0200 Subject: [PATCH] Sync local time with HTTP 'Date' header value. --- .../SourceFiles/mtproto/config_loader.cpp | 8 +- Telegram/SourceFiles/mtproto/config_loader.h | 4 +- Telegram/SourceFiles/mtproto/connection.cpp | 12 ++- Telegram/SourceFiles/mtproto/connection.h | 1 + .../mtproto/connection_abstract.cpp | 5 +- .../SourceFiles/mtproto/connection_abstract.h | 4 + .../SourceFiles/mtproto/connection_tcp.cpp | 22 ++++- Telegram/SourceFiles/mtproto/connection_tcp.h | 7 +- .../mtproto/mtp_abstract_socket.cpp | 5 +- .../SourceFiles/mtproto/mtp_abstract_socket.h | 9 +- Telegram/SourceFiles/mtproto/mtp_instance.cpp | 68 ++++++++++++--- Telegram/SourceFiles/mtproto/mtp_instance.h | 21 +++-- Telegram/SourceFiles/mtproto/mtp_pch.cpp | 9 ++ Telegram/SourceFiles/mtproto/mtp_pch.h | 19 +++++ .../SourceFiles/mtproto/mtp_tcp_socket.cpp | 5 ++ Telegram/SourceFiles/mtproto/mtp_tcp_socket.h | 1 + .../SourceFiles/mtproto/mtp_tls_socket.cpp | 36 ++++++-- Telegram/SourceFiles/mtproto/mtp_tls_socket.h | 5 +- .../mtproto/special_config_request.cpp | 82 +++++++++++++++++++ .../mtproto/special_config_request.h | 16 ++-- Telegram/gyp/Telegram.gyp | 1 + Telegram/gyp/lib_base.gyp | 1 + Telegram/gyp/lib_mtproto.gyp | 64 +++++++++++++++ Telegram/gyp/telegram_sources.txt | 6 -- 24 files changed, 356 insertions(+), 55 deletions(-) create mode 100644 Telegram/SourceFiles/mtproto/mtp_pch.cpp create mode 100644 Telegram/SourceFiles/mtproto/mtp_pch.h create mode 100644 Telegram/gyp/lib_mtproto.gyp diff --git a/Telegram/SourceFiles/mtproto/config_loader.cpp b/Telegram/SourceFiles/mtproto/config_loader.cpp index 890c15b00..81501301b 100644 --- a/Telegram/SourceFiles/mtproto/config_loader.cpp +++ b/Telegram/SourceFiles/mtproto/config_loader.cpp @@ -24,11 +24,13 @@ ConfigLoader::ConfigLoader( not_null instance, const QString &phone, RPCDoneHandlerPtr onDone, - RPCFailHandlerPtr onFail) + RPCFailHandlerPtr onFail, + Fn updateHttpUnixtime) : _instance(instance) , _phone(phone) , _doneHandler(onDone) -, _failHandler(onFail) { +, _failHandler(onFail) +, _updateHttpUnixtime(updateHttpUnixtime) { _enumDCTimer.setCallback([this] { enumerate(); }); _specialEnumTimer.setCallback([this] { sendSpecialRequest(); }); } @@ -129,7 +131,7 @@ void ConfigLoader::createSpecialLoader() { int port, bytes::const_span secret) { addSpecialEndpoint(dcId, ip, port, secret); - }, _phone); + }, _updateHttpUnixtime, _phone); } void ConfigLoader::addSpecialEndpoint( diff --git a/Telegram/SourceFiles/mtproto/config_loader.h b/Telegram/SourceFiles/mtproto/config_loader.h index 923dc2e25..f9166bed1 100644 --- a/Telegram/SourceFiles/mtproto/config_loader.h +++ b/Telegram/SourceFiles/mtproto/config_loader.h @@ -25,7 +25,8 @@ public: not_null instance, const QString &phone, RPCDoneHandlerPtr onDone, - RPCFailHandlerPtr onFail); + RPCFailHandlerPtr onFail, + Fn updateHttpUnixtime); ~ConfigLoader(); void load(); @@ -69,6 +70,7 @@ private: RPCDoneHandlerPtr _doneHandler; RPCFailHandlerPtr _failHandler; + Fn _updateHttpUnixtime; }; diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index a07cf2a0e..1f6535c0f 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -379,6 +379,9 @@ void ConnectionPrivate::appendTestConnection( connect(weak, &AbstractConnection::disconnected, [=] { onDisconnected(weak); }); + connect(weak, &AbstractConnection::syncTimeRequest, [=] { + _instance->syncHttpUnixtime(); + }); InvokeQueued(_testConnections.back().data, [=] { weak->connectToServer(ip, port, protocolSecret, getProtocolDcId()); @@ -1332,7 +1335,7 @@ void ConnectionPrivate::waitConnectedFailed() { _waitForConnected = std::min(maxTimeout, 2 * _waitForConnected); } - doDisconnect(); + connectingTimedOut(); restarted = true; DEBUG_LOG(("MTP Info: immediate restart!")); @@ -1343,6 +1346,13 @@ void ConnectionPrivate::waitBetterFailed() { confirmBestConnection(); } +void ConnectionPrivate::connectingTimedOut() { + for (const auto &connection : _testConnections) { + connection.data->timedOut(); + } + doDisconnect(); +} + void ConnectionPrivate::doDisconnect() { destroyAllConnections(); diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index 00aababb5..079864db9 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -152,6 +152,7 @@ private: int priority = 0; }; void connectToServer(bool afterConfig = false); + void connectingTimedOut(); void doDisconnect(); void restart(); void finishAndDestroy(); diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.cpp b/Telegram/SourceFiles/mtproto/connection_abstract.cpp index b8c2f5fd2..8b20a126b 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.cpp +++ b/Telegram/SourceFiles/mtproto/connection_abstract.cpp @@ -165,7 +165,10 @@ ConnectionPointer AbstractConnection::Create( const ProxyData &proxy) { auto result = [&] { if (protocol == DcOptions::Variants::Tcp) { - return ConnectionPointer::New(thread, proxy); + return ConnectionPointer::New( + instance, + thread, + proxy); } else { return ConnectionPointer::New(thread, proxy); } diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.h b/Telegram/SourceFiles/mtproto/connection_abstract.h index dd3f0b464..28f773c58 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.h +++ b/Telegram/SourceFiles/mtproto/connection_abstract.h @@ -75,6 +75,8 @@ public: int port, const bytes::vector &protocolSecret, int16 protocolDcId) = 0; + virtual void timedOut() { + } virtual bool isConnected() const = 0; virtual bool usingHttpWait() { return false; @@ -122,6 +124,8 @@ signals: void connected(); void disconnected(); + void syncTimeRequest(); + protected: BuffersQueue _receivedQueue; // list of received packets, not processed yet bool _sentEncrypted = false; diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.cpp b/Telegram/SourceFiles/mtproto/connection_tcp.cpp index 5a7fbfe8e..749de0017 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.cpp +++ b/Telegram/SourceFiles/mtproto/connection_tcp.cpp @@ -237,13 +237,17 @@ auto TcpConnection::Protocol::Create(bytes::const_span secret) Unexpected("Secret bytes in TcpConnection::Protocol::Create."); } -TcpConnection::TcpConnection(QThread *thread, const ProxyData &proxy) +TcpConnection::TcpConnection( + not_null instance, + QThread *thread, + const ProxyData &proxy) : AbstractConnection(thread, proxy) +, _instance(instance) , _checkNonce(rand_value()) { } ConnectionPointer TcpConnection::clone(const ProxyData &proxy) { - return ConnectionPointer::New(thread(), proxy); + return ConnectionPointer::New(_instance, thread(), proxy); } void TcpConnection::ensureAvailableInBuffer(int amount) { @@ -554,7 +558,8 @@ void TcpConnection::connectToServer( _socket = AbstractSocket::Create( thread(), secret, - ToNetworkProxy(_proxy)); + ToNetworkProxy(_proxy), + [=] { return _instance->httpUnixtime(); }); _protocolDcId = protocolDcId; _socket->connected( @@ -577,6 +582,11 @@ void TcpConnection::connectToServer( socketError(); }, _lifetime); + _socket->syncTimeRequests( + ) | rpl::start_with_next([=] { + emit syncTimeRequest(); + }, _lifetime); + _socket->connectToHost(_address, _port); } @@ -628,6 +638,12 @@ void TcpConnection::socketPacket(bytes::const_span bytes) { } } +void TcpConnection::timedOut() { + if (_socket) { + _socket->timedOut(); + } +} + bool TcpConnection::isConnected() const { return (_status == Status::Ready); } diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.h b/Telegram/SourceFiles/mtproto/connection_tcp.h index f474b0ea3..10da72760 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.h +++ b/Telegram/SourceFiles/mtproto/connection_tcp.h @@ -17,7 +17,10 @@ class AbstractSocket; class TcpConnection : public AbstractConnection { public: - TcpConnection(QThread *thread, const ProxyData &proxy); + TcpConnection( + not_null instance, + QThread *thread, + const ProxyData &proxy); ConnectionPointer clone(const ProxyData &proxy) override; @@ -30,6 +33,7 @@ public: int port, const bytes::vector &protocolSecret, int16 protocolDcId) override; + void timedOut() override; bool isConnected() const override; bool requiresExtendedPadding() const override; @@ -63,6 +67,7 @@ private: return *reinterpret_cast(ch); } + const not_null _instance; std::unique_ptr _socket; bool _connectionStarted = false; diff --git a/Telegram/SourceFiles/mtproto/mtp_abstract_socket.cpp b/Telegram/SourceFiles/mtproto/mtp_abstract_socket.cpp index 1de6e6654..6363ca2b4 100644 --- a/Telegram/SourceFiles/mtproto/mtp_abstract_socket.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_abstract_socket.cpp @@ -16,9 +16,10 @@ namespace internal { std::unique_ptr AbstractSocket::Create( not_null thread, const bytes::vector &secret, - const QNetworkProxy &proxy) { + const QNetworkProxy &proxy, + Fn unixtime) { if (secret.size() >= 21 && secret[0] == bytes::type(0xEE)) { - return std::make_unique(thread, secret, proxy); + return std::make_unique(thread, secret, proxy, unixtime); } else { return std::make_unique(thread, proxy); } diff --git a/Telegram/SourceFiles/mtproto/mtp_abstract_socket.h b/Telegram/SourceFiles/mtproto/mtp_abstract_socket.h index f86fe3939..522114fe7 100644 --- a/Telegram/SourceFiles/mtproto/mtp_abstract_socket.h +++ b/Telegram/SourceFiles/mtproto/mtp_abstract_socket.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/bytes.h" +#include "base/basic_types.h" namespace MTP { namespace internal { @@ -17,7 +18,8 @@ public: static std::unique_ptr Create( not_null thread, const bytes::vector &secret, - const QNetworkProxy &proxy); + const QNetworkProxy &proxy, + Fn unixtime); explicit AbstractSocket(not_null thread) { moveToThread(thread); @@ -36,8 +38,12 @@ public: [[nodiscard]] rpl::producer<> error() const { return _error.events(); } + [[nodiscard]] rpl::producer<> syncTimeRequests() const { + return _syncTimeRequests.events(); + } virtual void connectToHost(const QString &address, int port) = 0; + virtual void timedOut() = 0; [[nodiscard]] virtual bool isConnected() = 0; [[nodiscard]] virtual bool hasBytesAvailable() = 0; [[nodiscard]] virtual int64 read(bytes::span buffer) = 0; @@ -52,6 +58,7 @@ protected: rpl::event_stream<> _disconnected; rpl::event_stream<> _readyRead; rpl::event_stream<> _error; + rpl::event_stream<> _syncTimeRequests; }; diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 5691e7ee0..01d61d870 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -42,31 +42,36 @@ public: void setGoodProxyDomain(const QString &host, const QString &ip); void suggestMainDcId(DcId mainDcId); void setMainDcId(DcId mainDcId); - DcId mainDcId() const; + [[nodiscard]] DcId mainDcId() const; void setKeyForWrite(DcId dcId, const AuthKeyPtr &key); - AuthKeysList getKeysForWrite() const; + [[nodiscard]] AuthKeysList getKeysForWrite() const; void addKeysForDestroy(AuthKeysList &&keys); - not_null dcOptions(); + [[nodiscard]] not_null dcOptions(); // Thread safe. - QString deviceModel() const; - QString systemVersion() const; + [[nodiscard]] QString deviceModel() const; + [[nodiscard]] QString systemVersion() const; + // Main thread. void requestConfig(); void requestConfigIfOld(); void requestCDNConfig(); void setUserPhone(const QString &phone); void badConfigurationError(); + // Thread safe. + void syncHttpUnixtime(); + [[nodiscard]] int32 httpUnixtime() const; + void restart(); void restart(ShiftedDcId shiftedDcId); - int32 dcstate(ShiftedDcId shiftedDcId = 0); - QString dctransport(ShiftedDcId shiftedDcId = 0); + [[nodiscard]] int32 dcstate(ShiftedDcId shiftedDcId = 0); + [[nodiscard]] QString dctransport(ShiftedDcId shiftedDcId = 0); void ping(); void cancel(mtpRequestId requestId); - int32 state(mtpRequestId requestId); // < 0 means waiting for such count of ms + [[nodiscard]] int32 state(mtpRequestId requestId); // < 0 means waiting for such count of ms void killSession(ShiftedDcId shiftedDcId); void killSession(std::unique_ptr session); void stopSession(ShiftedDcId shiftedDcId); @@ -123,9 +128,6 @@ public: bool isKeysDestroyer() const { return (_mode == Instance::Mode::KeysDestroyer); } - bool isSpecialConfigRequester() const { - return (_mode == Instance::Mode::SpecialConfigRequester); - } void scheduleKeyDestroy(ShiftedDcId shiftedDcId); void performKeyDestroy(ShiftedDcId shiftedDcId); @@ -166,6 +168,7 @@ private: void clearCallbacks(const std::vector &ids); void checkDelayedRequests(); + [[nodiscard]] Fn updateHttpUnixtime(); not_null _instance; not_null _dcOptions; @@ -186,10 +189,13 @@ private: std::unique_ptr _configLoader; std::unique_ptr _domainResolver; + std::unique_ptr _httpUnixtimeLoader; QString _userPhone; mtpRequestId _cdnConfigLoadRequestId = 0; crl::time _lastConfigLoadedTime = 0; crl::time _configExpiresAt = 0; + std::atomic _httpUnixtimeValid = false; + std::atomic _httpUnixtimeShift = 0; std::map _keysForWrite; mutable QReadWriteLock _keysForWriteLock; @@ -407,7 +413,8 @@ void Instance::Private::requestConfig() { _instance, _userPhone, rpcDone([=](const MTPConfig &result) { configLoadDone(result); }), - rpcFail([=](const RPCError &error) { return configLoadFail(error); })); + rpcFail([=](const RPCError &error) { return configLoadFail(error); }), + updateHttpUnixtime()); _configLoader->load(); } @@ -426,6 +433,35 @@ void Instance::Private::badConfigurationError() { } } +int32 Instance::Private::httpUnixtime() const { + return unixtime() + _httpUnixtimeShift; +} + +void Instance::Private::syncHttpUnixtime() { + if (_httpUnixtimeValid) { + return; + } + InvokeQueued(_instance, [=] { + if (_httpUnixtimeValid || _httpUnixtimeLoader) { + return; + } + _httpUnixtimeLoader = std::make_unique( + updateHttpUnixtime()); + }); +} + +Fn Instance::Private::updateHttpUnixtime() { + return [=](TimeId httpUnixtime) { + _httpUnixtimeValid = true; + _httpUnixtimeShift = httpUnixtime - unixtime(); + InvokeQueued(_instance, [=] { + if (_httpUnixtimeValid) { + _httpUnixtimeLoader = nullptr; + } + }); + }; +} + void Instance::Private::requestConfigIfOld() { const auto timeout = Global::BlockedMode() ? kConfigBecomesOldForBlockedIn @@ -1579,6 +1615,14 @@ void Instance::badConfigurationError() { _private->badConfigurationError(); } +int32 Instance::httpUnixtime() const { + return _private->httpUnixtime(); +} + +void Instance::syncHttpUnixtime() { + _private->syncHttpUnixtime(); +} + void Instance::requestConfigIfOld() { _private->requestConfigIfOld(); } diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.h b/Telegram/SourceFiles/mtproto/mtp_instance.h index c492afc68..8f1224dcf 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.h +++ b/Telegram/SourceFiles/mtproto/mtp_instance.h @@ -40,7 +40,6 @@ public: }; enum class Mode { Normal, - SpecialConfigRequester, KeysDestroyer, }; Instance(not_null options, Mode mode, Config &&config); @@ -52,20 +51,20 @@ public: void setGoodProxyDomain(const QString &host, const QString &ip); void suggestMainDcId(DcId mainDcId); void setMainDcId(DcId mainDcId); - DcId mainDcId() const; - QString systemLangCode() const; - QString cloudLangCode() const; - QString langPackName() const; + [[nodiscard]] DcId mainDcId() const; + [[nodiscard]] QString systemLangCode() const; + [[nodiscard]] QString cloudLangCode() const; + [[nodiscard]] QString langPackName() const; // Thread safe. - QString deviceModel() const; - QString systemVersion() const; + [[nodiscard]] QString deviceModel() const; + [[nodiscard]] QString systemVersion() const; void setKeyForWrite(DcId dcId, const AuthKeyPtr &key); - AuthKeysList getKeysForWrite() const; + [[nodiscard]] AuthKeysList getKeysForWrite() const; void addKeysForDestroy(AuthKeysList &&keys); - not_null dcOptions(); + [[nodiscard]] not_null dcOptions(); template mtpRequestId send( @@ -181,6 +180,10 @@ public: void setUserPhone(const QString &phone); void badConfigurationError(); + // Thread safe. + [[nodiscard]] int32 httpUnixtime() const; + void syncHttpUnixtime(); + ~Instance(); public slots: diff --git a/Telegram/SourceFiles/mtproto/mtp_pch.cpp b/Telegram/SourceFiles/mtproto/mtp_pch.cpp new file mode 100644 index 000000000..7146195e1 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/mtp_pch.cpp @@ -0,0 +1,9 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "mtproto/mtp_pch.h" + diff --git a/Telegram/SourceFiles/mtproto/mtp_pch.h b/Telegram/SourceFiles/mtproto/mtp_pch.h new file mode 100644 index 000000000..2804191f4 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/mtp_pch.h @@ -0,0 +1,19 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include +#include +#include +#include + +#include +#include + +#include "base/bytes.h" + +#include "logs.h" +#include "scheme.h" diff --git a/Telegram/SourceFiles/mtproto/mtp_tcp_socket.cpp b/Telegram/SourceFiles/mtproto/mtp_tcp_socket.cpp index f5a5f28fe..4c0731bc7 100644 --- a/Telegram/SourceFiles/mtproto/mtp_tcp_socket.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_tcp_socket.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "mtproto/mtp_tcp_socket.h" +#include "base/invoke_queued.h" + namespace MTP { namespace internal { @@ -45,6 +47,9 @@ void TcpSocket::connectToHost(const QString &address, int port) { _socket.connectToHost(address, port); } +void TcpSocket::timedOut() { +} + bool TcpSocket::isConnected() { return (_socket.state() == QAbstractSocket::ConnectedState); } diff --git a/Telegram/SourceFiles/mtproto/mtp_tcp_socket.h b/Telegram/SourceFiles/mtproto/mtp_tcp_socket.h index 61ad897d6..231edd5ed 100644 --- a/Telegram/SourceFiles/mtproto/mtp_tcp_socket.h +++ b/Telegram/SourceFiles/mtproto/mtp_tcp_socket.h @@ -17,6 +17,7 @@ public: TcpSocket(not_null thread, const QNetworkProxy &proxy); void connectToHost(const QString &address, int port) override; + void timedOut() override; bool isConnected() override; bool hasBytesAvailable() override; int64 read(bytes::span buffer) override; diff --git a/Telegram/SourceFiles/mtproto/mtp_tls_socket.cpp b/Telegram/SourceFiles/mtproto/mtp_tls_socket.cpp index b0da6885a..ef0e1b918 100644 --- a/Telegram/SourceFiles/mtproto/mtp_tls_socket.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_tls_socket.cpp @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtp_tcp_socket.h" #include "base/openssl_help.h" +#include "base/bytes.h" +#include "base/invoke_queued.h" + +#include namespace MTP { namespace internal { @@ -132,7 +136,8 @@ public: ClientHelloGenerator( const MTPTlsClientHello &rules, bytes::const_span domain, - bytes::const_span key); + bytes::const_span key, + Fn unixtime); [[nodiscard]] ClientHello result(); private: @@ -151,6 +156,7 @@ private: bytes::const_span _domain; bytes::const_span _key; + Fn _unixtime; bytes::vector _greases; std::vector _scopeStack; QByteArray _result; @@ -163,9 +169,11 @@ private: ClientHelloGenerator::ClientHelloGenerator( const MTPTlsClientHello &rules, bytes::const_span domain, - bytes::const_span key) + bytes::const_span key, + Fn unixtime) : _domain(domain) , _key(key) +, _unixtime(unixtime) , _greases(PrepareGreases()) { _result.reserve(kClientHelloLength); writeBlocks(rules.match([&](const MTPDtlsClientHello &data) { @@ -296,7 +304,7 @@ void ClientHelloGenerator::writeTimestamp() { sizeof(int32)); auto already = int32(); bytes::copy(bytes::object_as_span(&already), storage); - already ^= qToLittleEndian(int32(unixtime())); + already ^= qToLittleEndian(_unixtime()); bytes::copy(storage, bytes::object_as_span(&already)); _digest = QByteArray(kHelloDigestLength, Qt::Uninitialized); @@ -310,8 +318,9 @@ void ClientHelloGenerator::writeTimestamp() { [[nodiscard]] ClientHello PrepareClientHello( const MTPTlsClientHello &rules, bytes::const_span domain, - bytes::const_span key) { - return ClientHelloGenerator(rules, domain, key).result(); + bytes::const_span key, + Fn unixtime) { + return ClientHelloGenerator(rules, domain, key, unixtime).result(); } [[nodiscard]] bool CheckPart(bytes::const_span data, QLatin1String check) { @@ -334,10 +343,13 @@ void ClientHelloGenerator::writeTimestamp() { TlsSocket::TlsSocket( not_null thread, const bytes::vector &secret, - const QNetworkProxy &proxy) + const QNetworkProxy &proxy, + Fn unixtime) : AbstractSocket(thread) -, _secret(secret) { +, _secret(secret) +, _unixtime(unixtime) { Expects(_secret.size() >= 21 && _secret[0] == bytes::type(0xEE)); + Expects(_unixtime != nullptr); _socket.moveToThread(thread); _socket.setProxy(proxy); @@ -385,7 +397,8 @@ void TlsSocket::plainConnected() { const auto hello = PrepareClientHello( kClientHelloRules, domainFromSecret(), - keyFromSecret()); + keyFromSecret(), + _unixtime); if (hello.data.isEmpty()) { LOG(("TLS Error: Could not generate Client Hello!")); _state = State::Error; @@ -570,6 +583,10 @@ void TlsSocket::connectToHost(const QString &address, int port) { _socket.connectToHost(address, port); } +void TlsSocket::timedOut() { + _syncTimeRequests.fire({}); +} + bool TlsSocket::isConnected() { return (_state == State::Connected); } @@ -648,6 +665,9 @@ int32 TlsSocket::debugState() { } void TlsSocket::handleError(int errorCode) { + if (_state != State::Connected) { + _syncTimeRequests.fire({}); + } if (errorCode) { TcpSocket::LogError(errorCode, _socket.errorString()); } diff --git a/Telegram/SourceFiles/mtproto/mtp_tls_socket.h b/Telegram/SourceFiles/mtproto/mtp_tls_socket.h index cc6da1976..10d4e50b8 100644 --- a/Telegram/SourceFiles/mtproto/mtp_tls_socket.h +++ b/Telegram/SourceFiles/mtproto/mtp_tls_socket.h @@ -17,9 +17,11 @@ public: TlsSocket( not_null thread, const bytes::vector &secret, - const QNetworkProxy &proxy); + const QNetworkProxy &proxy, + Fn unixtime); void connectToHost(const QString &address, int port) override; + void timedOut() override; bool isConnected() override; bool hasBytesAvailable() override; int64 read(bytes::span buffer) override; @@ -54,6 +56,7 @@ private: const bytes::vector _secret; QTcpSocket _socket; + Fn _unixtime; State _state = State::NotConnected; QByteArray _incoming; int _incomingGoodDataOffset = 0; diff --git a/Telegram/SourceFiles/mtproto/special_config_request.cpp b/Telegram/SourceFiles/mtproto/special_config_request.cpp index 94c07c18d..9994dcc76 100644 --- a/Telegram/SourceFiles/mtproto/special_config_request.cpp +++ b/Telegram/SourceFiles/mtproto/special_config_request.cpp @@ -170,6 +170,52 @@ QByteArray ConcatenateDnsTxtFields(const std::vector &response) { return QStringList(entries.values()).join(QString()).toLatin1(); } +[[nodiscard]] QDateTime ParseHttpDate(const QString &date) { + // Wed, 10 Jul 2019 14:33:38 GMT + static const auto expression = QRegularExpression( + R"(\w\w\w, (\d\d) (\w\w\w) (\d\d\d\d) (\d\d):(\d\d):(\d\d) GMT)"); + const auto match = expression.match(date); + if (!match.hasMatch()) { + return QDateTime(); + } + + const auto number = [&](int index) { + return match.capturedRef(index).toInt(); + }; + const auto day = number(1); + const auto month = [&] { + static const auto months = { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + }; + const auto captured = match.capturedRef(2); + for (auto i = begin(months); i != end(months); ++i) { + if (captured == (*i)) { + return 1 + int(i - begin(months)); + } + } + return 0; + }(); + const auto year = number(3); + const auto hour = number(4); + const auto minute = number(5); + const auto second = number(6); + return QDateTime( + QDate(year, month, day), + QTime(hour, minute, second), + Qt::UTC); +} + } // namespace ServiceWebRequest::ServiceWebRequest(not_null reply) @@ -212,8 +258,10 @@ SpecialConfigRequest::SpecialConfigRequest( const std::string &ip, int port, bytes::const_span secret)> callback, + Fn timeCallback, const QString &phone) : _callback(std::move(callback)) +, _timeCallback(std::move(timeCallback)) , _phone(phone) { _manager.setProxy(QNetworkProxy::NoProxy); _attempts = { @@ -227,6 +275,10 @@ SpecialConfigRequest::SpecialConfigRequest( sendNextRequest(); } +SpecialConfigRequest::SpecialConfigRequest(Fn timeCallback) +: SpecialConfigRequest(nullptr, std::move(timeCallback), QString()) { +} + void SpecialConfigRequest::sendNextRequest() { Expects(!_attempts.empty()); @@ -272,10 +324,40 @@ void SpecialConfigRequest::performRequest(const Attempt &attempt) { }); } +void SpecialConfigRequest::handleHeaderUnixtime( + not_null reply) { + if (!_timeCallback || reply->error() != QNetworkReply::NoError) { + return; + } + const auto date = QString::fromLatin1([&] { + for (const auto &pair : reply->rawHeaderPairs()) { + if (pair.first == "Date") { + return pair.second; + } + } + return QByteArray(); + }()); + if (date.isEmpty()) { + LOG(("Config Error: No 'Date' header received.")); + return; + } + const auto parsed = ParseHttpDate(date); + if (!parsed.isValid()) { + LOG(("Config Error: Bad 'Date' header received: %1").arg(date)); + return; + } + _timeCallback(parsed.toTime_t()); +} + void SpecialConfigRequest::requestFinished( Type type, not_null reply) { + handleHeaderUnixtime(reply); const auto result = finalizeRequest(reply); + if (!_callback) { + return; + } + switch (type) { //case Type::App: handleResponse(result); break; case Type::Dns: { diff --git a/Telegram/SourceFiles/mtproto/special_config_request.h b/Telegram/SourceFiles/mtproto/special_config_request.h index 08469c283..42c732636 100644 --- a/Telegram/SourceFiles/mtproto/special_config_request.h +++ b/Telegram/SourceFiles/mtproto/special_config_request.h @@ -12,14 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace MTP { struct ServiceWebRequest { - ServiceWebRequest(not_null reply); - ServiceWebRequest(ServiceWebRequest &&other); - ServiceWebRequest &operator=(ServiceWebRequest &&other); - ~ServiceWebRequest(); + ServiceWebRequest(not_null reply); + ServiceWebRequest(ServiceWebRequest &&other); + ServiceWebRequest &operator=(ServiceWebRequest &&other); + ~ServiceWebRequest(); - void destroy(); + void destroy(); - QPointer reply; + QPointer reply; }; @@ -31,7 +31,9 @@ public: const std::string &ip, int port, bytes::const_span secret)> callback, + Fn timeCallback, const QString &phone); + explicit SpecialConfigRequest(Fn timeCallback); private: enum class Type { @@ -46,6 +48,7 @@ private: void sendNextRequest(); void performRequest(const Attempt &attempt); void requestFinished(Type type, not_null reply); + void handleHeaderUnixtime(not_null reply); QByteArray finalizeRequest(not_null reply); void handleResponse(const QByteArray &bytes); bool decryptSimpleConfig(const QByteArray &bytes); @@ -55,6 +58,7 @@ private: const std::string &ip, int port, bytes::const_span secret)> _callback; + Fn _timeCallback; QString _phone; MTPhelp_ConfigSimple _simpleConfig; diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 0bf2dce39..ec2ed28b7 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -83,6 +83,7 @@ 'lib_storage.gyp:lib_storage', 'lib_lottie.gyp:lib_lottie', 'lib_ffmpeg.gyp:lib_ffmpeg', + 'lib_mtproto.gyp:lib_mtproto', ], 'defines': [ diff --git a/Telegram/gyp/lib_base.gyp b/Telegram/gyp/lib_base.gyp index 10026eee4..c4b9d567d 100644 --- a/Telegram/gyp/lib_base.gyp +++ b/Telegram/gyp/lib_base.gyp @@ -57,6 +57,7 @@ '<(src_loc)/base/flat_set.h', '<(src_loc)/base/functors.h', '<(src_loc)/base/index_based_iterator.h', + '<(src_loc)/base/invoke_queued.h', '<(src_loc)/base/last_used_cache.h', '<(src_loc)/base/match_method.h', '<(src_loc)/base/observer.cpp', diff --git a/Telegram/gyp/lib_mtproto.gyp b/Telegram/gyp/lib_mtproto.gyp new file mode 100644 index 000000000..b592cb796 --- /dev/null +++ b/Telegram/gyp/lib_mtproto.gyp @@ -0,0 +1,64 @@ +# This file is part of Telegram Desktop, +# the official desktop application for the Telegram messaging service. +# +# For license and copyright information please follow this link: +# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL + +{ + 'includes': [ + 'common.gypi', + ], + 'targets': [{ + 'target_name': 'lib_mtproto', + 'type': 'static_library', + 'includes': [ + 'common.gypi', + 'qt.gypi', + 'telegram_linux.gypi', + 'pch.gypi', + 'openssl.gypi', + ], + 'variables': { + 'src_loc': '../SourceFiles', + 'res_loc': '../Resources', + 'libs_loc': '../../../Libraries', + 'official_build_target%': '', + 'submodules_loc': '../ThirdParty', + 'pch_source': '<(src_loc)/mtproto/mtp_pch.cpp', + 'pch_header': '<(src_loc)/mtproto/mtp_pch.h', + }, + 'defines': [ + ], + 'dependencies': [ + 'lib_scheme.gyp:lib_scheme', + 'crl.gyp:crl', + ], + 'export_dependent_settings': [ + 'lib_scheme.gyp:lib_scheme', + ], + 'conditions': [[ 'build_macold', { + 'xcode_settings': { + 'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ], + }, + 'include_dirs': [ + '/usr/local/macold/include/c++/v1', + ], + }]], + 'include_dirs': [ + '<(src_loc)', + '<(SHARED_INTERMEDIATE_DIR)', + '<(libs_loc)/range-v3/include', + '<(submodules_loc)/GSL/include', + '<(submodules_loc)/variant/include', + '<(submodules_loc)/crl/src', + ], + 'sources': [ + '<(src_loc)/mtproto/mtp_abstract_socket.cpp', + '<(src_loc)/mtproto/mtp_abstract_socket.h', + '<(src_loc)/mtproto/mtp_tcp_socket.cpp', + '<(src_loc)/mtproto/mtp_tcp_socket.h', + '<(src_loc)/mtproto/mtp_tls_socket.cpp', + '<(src_loc)/mtproto/mtp_tls_socket.h', + ], + }], +} diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 556f314da..35a4c5740 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -531,12 +531,6 @@ <(src_loc)/mtproto/dedicated_file_loader.h <(src_loc)/mtproto/facade.cpp <(src_loc)/mtproto/facade.h -<(src_loc)/mtproto/mtp_abstract_socket.cpp -<(src_loc)/mtproto/mtp_abstract_socket.h -<(src_loc)/mtproto/mtp_tcp_socket.cpp -<(src_loc)/mtproto/mtp_tcp_socket.h -<(src_loc)/mtproto/mtp_tls_socket.cpp -<(src_loc)/mtproto/mtp_tls_socket.h <(src_loc)/mtproto/mtp_instance.cpp <(src_loc)/mtproto/mtp_instance.h <(src_loc)/mtproto/rsa_public_key.cpp