From 7482025c1013e5115618298e00405a8f52dc017a Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 24 Apr 2018 12:46:27 +0400 Subject: [PATCH] Support work with different dcs on a single IP. --- Telegram/SourceFiles/application.cpp | 3 +- Telegram/SourceFiles/base/bytes.h | 137 ++++++++++++++++++ Telegram/SourceFiles/base/openssl_help.h | 11 ++ Telegram/SourceFiles/core/utils.h | 1 + .../SourceFiles/mtproto/config_loader.cpp | 2 +- Telegram/SourceFiles/mtproto/connection.cpp | 92 ++++++++---- Telegram/SourceFiles/mtproto/connection.h | 2 + .../mtproto/connection_abstract.cpp | 18 ++- .../SourceFiles/mtproto/connection_abstract.h | 8 +- .../SourceFiles/mtproto/connection_auto.cpp | 3 +- .../SourceFiles/mtproto/connection_auto.h | 6 +- .../SourceFiles/mtproto/connection_tcp.cpp | 95 ++++++++---- Telegram/SourceFiles/mtproto/connection_tcp.h | 9 +- Telegram/SourceFiles/mtproto/session.cpp | 46 +++++- Telegram/SourceFiles/mtproto/session.h | 43 ++++-- Telegram/SourceFiles/storage/localstorage.cpp | 12 +- 16 files changed, 391 insertions(+), 97 deletions(-) create mode 100644 Telegram/SourceFiles/base/bytes.h diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 754554f46..29db39b38 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -322,7 +322,8 @@ void Application::refreshGlobalProxy() { } return Sandbox::PreLaunchProxy(); }(); - if (proxy.type != ProxyData::Type::None) { + if (proxy.type == ProxyData::Type::Socks5 + || proxy.type == ProxyData::Type::Http) { QNetworkProxy::setApplicationProxy(QNetworkProxy( (proxy.type == ProxyData::Type::Socks5 ? QNetworkProxy::Socks5Proxy diff --git a/Telegram/SourceFiles/base/bytes.h b/Telegram/SourceFiles/base/bytes.h new file mode 100644 index 000000000..411bc2077 --- /dev/null +++ b/Telegram/SourceFiles/base/bytes.h @@ -0,0 +1,137 @@ +/* +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 +*/ +#pragma once + +#include + +namespace bytes { + +using span = gsl::span; +using const_span = gsl::span; +using vector = std::vector; + +template < + typename Container, + typename = std::enable_if_t>> +inline span make_span(Container &container) { + return gsl::as_writeable_bytes(gsl::make_span(container)); +} + +template +inline const_span make_span(const Container &container) { + return gsl::as_bytes(gsl::make_span(container)); +} + +template +inline span make_span(gsl::span container) { + return gsl::as_writeable_bytes(container); +} + +template +inline const_span make_span(gsl::span container) { + return gsl::as_bytes(container); +} + +template +inline span make_span(Type *value, std::size_t count) { + return gsl::as_writeable_bytes(gsl::make_span(value, count)); +} + +template +inline const_span make_span(const Type *value, std::size_t count) { + return gsl::as_bytes(gsl::make_span(value, count)); +} + +template +inline vector make_vector(const Container &container) { + const auto buffer = bytes::make_span(container); + return { buffer.begin(), buffer.end() }; +} + +inline void copy(span destination, const_span source) { + Expects(destination.size() >= source.size()); + + memcpy(destination.data(), source.data(), source.size()); +} + +inline void move(span destination, const_span source) { + Expects(destination.size() >= source.size()); + + memmove(destination.data(), source.data(), source.size()); +} + +inline void set_with_const(span destination, gsl::byte value) { + memset( + destination.data(), + gsl::to_integer(value), + destination.size()); +} + +inline int compare(const_span a, const_span b) { + const auto aSize = a.size(), bSize = b.size(); + return (aSize > bSize) + ? 1 + : (aSize < bSize) + ? -1 + : memcmp(a.data(), b.data(), aSize); +} + +namespace details { + +template +std::size_t spansLength(Arg &&arg) { + return bytes::make_span(arg).size(); +} + +template +std::size_t spansLength(Arg &&arg, Args &&...args) { + return bytes::make_span(arg).size() + spansLength(args...); +} + +template +void spansAppend(span destination, Arg &&arg) { + bytes::copy(destination, bytes::make_span(arg)); +} + +template +void spansAppend(span destination, Arg &&arg, Args &&...args) { + const auto data = bytes::make_span(arg); + bytes::copy(destination, data); + spansAppend(destination.subspan(data.size()), args...); +} + +} // namespace details + +template < + typename ...Args, + typename = std::enable_if_t<(sizeof...(Args) > 1)>> +vector concatenate(Args &&...args) { + const auto size = details::spansLength(args...); + auto result = vector(size); + details::spansAppend(make_span(result), args...); + return result; +} + +template < + typename SpanRange> +vector concatenate(SpanRange args) { + auto size = std::size_t(0); + for (const auto &arg : args) { + size += bytes::make_span(arg).size(); + } + auto result = vector(size); + auto buffer = make_span(result); + for (const auto &arg : args) { + const auto part = bytes::make_span(arg); + bytes::copy(buffer, part); + buffer = buffer.subspan(part.size()); + } + return result; +} + +} // namespace bytes diff --git a/Telegram/SourceFiles/base/openssl_help.h b/Telegram/SourceFiles/base/openssl_help.h index f4a8bec98..087d6555a 100644 --- a/Telegram/SourceFiles/base/openssl_help.h +++ b/Telegram/SourceFiles/base/openssl_help.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include "base/bytes.h" namespace openssl { @@ -223,3 +224,13 @@ inline int FillRandom(base::byte_span bytes) { } } // namespace openssl + +namespace bytes { + +inline void set_random(span destination) { + RAND_bytes( + reinterpret_cast(destination.data()), + destination.size()); +} + +} // namespace bytes diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 08ec25862..68235af7b 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -432,6 +432,7 @@ struct ProxyData { None, Socks5, Http, + Mtproto, }; Type type = Type::None; QString host; diff --git a/Telegram/SourceFiles/mtproto/config_loader.cpp b/Telegram/SourceFiles/mtproto/config_loader.cpp index 527a8b196..eec1b6fad 100644 --- a/Telegram/SourceFiles/mtproto/config_loader.cpp +++ b/Telegram/SourceFiles/mtproto/config_loader.cpp @@ -100,7 +100,7 @@ void ConfigLoader::enumerate() { } void ConfigLoader::createSpecialLoader() { - if (Global::ConnectionType() != dbictAuto) { + if (Global::ConnectionProxy().type != ProxyData::Type::None) { _specialLoader.reset(); return; } diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index 047bd46a8..93a2a0a12 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -180,7 +180,7 @@ ModExpFirst CreateModExp( ModExpFirst result; constexpr auto kMaxModExpFirstTries = 5; for (auto tries = 0; tries != kMaxModExpFirstTries; ++tries) { - FillRandom(result.randomPower); + bytes::set_random(result.randomPower); for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) { result.randomPower[i] ^= randomSeed[i]; } @@ -303,6 +303,7 @@ QString Connection::transport() const { Connection::~Connection() { Expects(data == nullptr); + if (thread) { waitTillFinish(); } @@ -312,13 +313,21 @@ void ConnectionPrivate::createConn(bool createIPv4, bool createIPv6) { destroyAllConnections(); if (createIPv4) { QWriteLocker lock(&stateConnMutex); - _conn4 = AbstractConnection::create(_dcType, thread()); + _conn4 = AbstractConnection::create( + *_connectionOptions, + _shiftedDcId, + _dcType, + thread()); connect(_conn4, SIGNAL(error(qint32)), this, SLOT(onError4(qint32))); connect(_conn4, SIGNAL(receivedSome()), this, SLOT(onReceivedSome())); } if (createIPv6) { QWriteLocker lock(&stateConnMutex); - _conn6 = AbstractConnection::create(_dcType, thread()); + _conn6 = AbstractConnection::create( + *_connectionOptions, + _shiftedDcId, + _dcType, + thread()); connect(_conn6, SIGNAL(error(qint32)), this, SLOT(onError6(qint32))); connect(_conn6, SIGNAL(receivedSome()), this, SLOT(onReceivedSome())); } @@ -440,8 +449,12 @@ QString ConnectionPrivate::transport() const { if ((!_conn4 && !_conn6) || (_conn4 && _conn6) || (_state < 0)) { return QString(); } - QString result = (_conn4 ? _conn4 : _conn6)->transport(); - if (!result.isEmpty() && Global::TryIPv6()) result += (_conn4 ? "/IPv4" : "/IPv6"); + + Assert(_connectionOptions != nullptr); + auto result = (_conn4 ? _conn4 : _conn6)->transport(); + if (!result.isEmpty() && _connectionOptions->useIPv6) { + result += (_conn4 ? "/IPv4" : "/IPv6"); + } return result; } @@ -783,8 +796,9 @@ void ConnectionPrivate::tryToSend() { MTPInitConnection initWrapper; int32 initSize = 0, initSizeInInts = 0; if (needsLayer) { - auto systemLangCode = sessionData->systemLangCode(); - auto cloudLangCode = sessionData->cloudLangCode(); + Assert(_connectionOptions != nullptr); + auto systemLangCode = _connectionOptions->systemLangCode; + auto cloudLangCode = _connectionOptions->cloudLangCode; auto langPack = "tdesktop"; auto deviceModel = (_dcType == DcType::Cdn) ? "n/a" : cApiDeviceModel(); auto systemVersion = (_dcType == DcType::Cdn) ? "n/a" : cApiSystemVersion(); @@ -996,16 +1010,28 @@ void ConnectionPrivate::restartNow() { void ConnectionPrivate::connectToServer(bool afterConfig) { if (_finished) { - DEBUG_LOG(("MTP Error: connectToServer() called for finished connection!")); + DEBUG_LOG(("MTP Error: " + "connectToServer() called for finished connection!")); return; } + auto hasKey = true; + { + QReadLocker lockFinished(&sessionDataMutex); + if (!sessionData) { + DEBUG_LOG(("MTP Error: " + "connectToServer() called for stopped connection!")); + return; + } + _connectionOptions = std::make_unique( + sessionData->connectionOptions()); + hasKey = (sessionData->getKey() != nullptr); + } auto bareDc = bareDcId(_shiftedDcId); _dcType = _instance->dcOptions()->dcType(_shiftedDcId); - if (_dcType == DcType::MediaDownload) { // using media_only addresses only if key for this dc is already created - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData || !sessionData->getKey()) { - _dcType = DcType::Regular; - } + + // Use media_only addresses only if key for this dc is already created. + if (_dcType == DcType::MediaDownload && !hasKey) { + _dcType = DcType::Regular; } else if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) { if (!_instance->dcOptions()->hasCDNKeysForDc(bareDc)) { requestCDNConfig(); @@ -1014,26 +1040,30 @@ void ConnectionPrivate::connectToServer(bool afterConfig) { } using Variants = DcOptions::Variants; - auto kIPv4 = Variants::IPv4; - auto kIPv6 = Variants::IPv6; - auto kTcp = Variants::Tcp; - auto kHttp = Variants::Http; - auto variants = _instance->dcOptions()->lookup(bareDc, _dcType); - auto noIPv4 = (_dcType == DcType::Temporary) ? (variants.data[kIPv4][kTcp].port == 0) : (variants.data[kIPv4][kHttp].port == 0); - auto noIPv6 = (_dcType == DcType::Temporary) ? true : (!Global::TryIPv6() || (variants.data[kIPv6][kHttp].port == 0)); + const auto kIPv4 = Variants::IPv4; + const auto kIPv6 = Variants::IPv6; + const auto kTcp = Variants::Tcp; + const auto kHttp = Variants::Http; + const auto variants = _instance->dcOptions()->lookup(bareDc, _dcType); + const auto useIPv4 = (_dcType == DcType::Temporary) ? true : _connectionOptions->useIPv4; + const auto useIPv6 = (_dcType == DcType::Temporary) ? false : _connectionOptions->useIPv6; + const auto useTcp = (_dcType == DcType::Temporary) ? true : _connectionOptions->useTcp; + const auto useHttp = (_dcType == DcType::Temporary) ? false : _connectionOptions->useHttp; + auto noIPv4 = (_dcType == DcType::Temporary) ? (variants.data[kIPv4][kTcp].port == 0) : (!useIPv4 || (variants.data[kIPv4][kHttp].port == 0)); + auto noIPv6 = (_dcType == DcType::Temporary) ? true : (!useIPv6 || (variants.data[kIPv6][kHttp].port == 0)); if (noIPv4 && noIPv6) { if (_instance->isKeysDestroyer()) { LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found for auth key destruction!").arg(_shiftedDcId)); - if (Global::TryIPv6() && noIPv6) LOG(("MTP Error: DC %1 options for IPv6 over HTTP not found for auth key destruction!").arg(_shiftedDcId)); + if (useIPv6 && noIPv6) LOG(("MTP Error: DC %1 options for IPv6 over HTTP not found for auth key destruction!").arg(_shiftedDcId)); emit _instance->keyDestroyed(_shiftedDcId); return; } else if (afterConfig) { LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found right after config load!").arg(_shiftedDcId)); - if (Global::TryIPv6() && noIPv6) LOG(("MTP Error: DC %1 options for IPv6 over HTTP not found right after config load!").arg(_shiftedDcId)); + if (useIPv6 && noIPv6) LOG(("MTP Error: DC %1 options for IPv6 over HTTP not found right after config load!").arg(_shiftedDcId)); return restart(); } DEBUG_LOG(("MTP Info: DC %1 options for IPv4 over HTTP not found, waiting for config").arg(_shiftedDcId)); - if (Global::TryIPv6() && noIPv6) DEBUG_LOG(("MTP Info: DC %1 options for IPv6 over HTTP not found, waiting for config").arg(_shiftedDcId)); + if (useIPv6 && noIPv6) DEBUG_LOG(("MTP Info: DC %1 options for IPv6 over HTTP not found, waiting for config").arg(_shiftedDcId)); connect(_instance, SIGNAL(configLoaded()), this, SLOT(onConfigLoaded()), Qt::UniqueConnection); InvokeQueued(_instance, [instance = _instance] { instance->requestConfig(); @@ -1064,9 +1094,17 @@ void ConnectionPrivate::connectToServer(bool afterConfig) { _waitForConnectedTimer.start(_waitForConnected); if (auto conn = _conn4) { + auto endpoint = DcOptions::Endpoint(); + if (_connectionOptions->proxy.type == ProxyData::Type::Mtproto) { + endpoint.ip = _connectionOptions->proxy.host.toStdString(); + endpoint.port = _connectionOptions->proxy.port; + endpoint.flags = MTPDdcOption::Flag::f_tcpo_only; + } else { + endpoint = variants.data[kIPv4][kTcp]; + } connect(conn, SIGNAL(connected()), this, SLOT(onConnected4())); connect(conn, SIGNAL(disconnected()), this, SLOT(onDisconnected4())); - conn->connectTcp(variants.data[kIPv4][kTcp]); + conn->connectTcp(endpoint); conn->connectHttp(variants.data[kIPv4][kHttp]); } if (auto conn = _conn6) { @@ -1180,7 +1218,9 @@ void ConnectionPrivate::onPingSendForce() { } void ConnectionPrivate::onWaitReceivedFailed() { - if (Global::ConnectionType() != dbictAuto && Global::ConnectionType() != dbictTcpProxy) { + Expects(_connectionOptions != nullptr); + + if (!_connectionOptions->useTcp) { return; } @@ -2625,7 +2665,7 @@ void ConnectionPrivate::dhClientParamsSend() { // gen rand 'b' auto randomSeed = std::array(); - openssl::FillRandom(randomSeed); + bytes::set_random(randomSeed); auto g_b_data = CreateModExp(_authKeyData->g, _authKeyStrings->dh_prime, randomSeed); if (g_b_data.modexp.empty()) { LOG(("AuthKey Error: could not generate good g_b.")); diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index 1e88755e7..8542d2cca 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -31,6 +31,7 @@ class AbstractConnection; class ConnectionPrivate; class SessionData; class RSAPublicKey; +struct ConnectionOptions; class Thread : public QThread { Q_OBJECT @@ -239,6 +240,7 @@ private: uint64 keyId = 0; QReadWriteLock sessionDataMutex; SessionData *sessionData = nullptr; + std::unique_ptr _connectionOptions; bool myKeyLock = false; void lockKey(); diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.cpp b/Telegram/SourceFiles/mtproto/connection_abstract.cpp index a514ad345..36cff0919 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.cpp +++ b/Telegram/SourceFiles/mtproto/connection_abstract.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/connection_tcp.h" #include "mtproto/connection_http.h" #include "mtproto/connection_auto.h" +#include "mtproto/session.h" namespace MTP { namespace internal { @@ -62,13 +63,20 @@ MTPResPQ AbstractConnection::readPQFakeReply(const mtpBuffer &buffer) { return response; } -AbstractConnection *AbstractConnection::create(DcType type, QThread *thread) { - if ((type == DcType::Temporary) || (Global::ConnectionType() == dbictTcpProxy)) { - return new TCPConnection(thread); - } else if (Global::ConnectionType() == dbictHttpProxy) { +AbstractConnection *AbstractConnection::create( + const ConnectionOptions &options, + ShiftedDcId shiftedDcId, + DcType type, + QThread *thread) { + const auto protocolDcId = (type == DcType::MediaDownload) + ? -MTP::bareDcId(shiftedDcId) + : MTP::bareDcId(shiftedDcId); + if ((type == DcType::Temporary) || (!options.useHttp)) { + return new TCPConnection(thread, protocolDcId); + } else if (!options.useTcp) { return new HTTPConnection(thread); } - return new AutoConnection(thread); + return new AutoConnection(thread, protocolDcId); } } // namespace internal diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.h b/Telegram/SourceFiles/mtproto/connection_abstract.h index 1751eb0b8..e1082e076 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.h +++ b/Telegram/SourceFiles/mtproto/connection_abstract.h @@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace MTP { namespace internal { +struct ConnectionOptions; + class AbstractConnection : public QObject { Q_OBJECT @@ -24,7 +26,11 @@ public: virtual ~AbstractConnection() = 0; // virtual constructor - static AbstractConnection *create(DcType type, QThread *thread); + static AbstractConnection *create( + const ConnectionOptions &options, + ShiftedDcId shiftedDcId, + DcType type, + QThread *thread); void setSentEncrypted() { _sentEncrypted = true; diff --git a/Telegram/SourceFiles/mtproto/connection_auto.cpp b/Telegram/SourceFiles/mtproto/connection_auto.cpp index 195814a46..87f895c3d 100644 --- a/Telegram/SourceFiles/mtproto/connection_auto.cpp +++ b/Telegram/SourceFiles/mtproto/connection_auto.cpp @@ -12,7 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace MTP { namespace internal { -AutoConnection::AutoConnection(QThread *thread) : AbstractTCPConnection(thread) +AutoConnection::AutoConnection(QThread *thread, int16 protocolDcId) +: AbstractTCPConnection(thread, protocolDcId) , status(WaitingBoth) , tcpNonce(rand_value()) , httpNonce(rand_value()) diff --git a/Telegram/SourceFiles/mtproto/connection_auto.h b/Telegram/SourceFiles/mtproto/connection_auto.h index 3066729d2..b43afe9c4 100644 --- a/Telegram/SourceFiles/mtproto/connection_auto.h +++ b/Telegram/SourceFiles/mtproto/connection_auto.h @@ -16,8 +16,7 @@ class AutoConnection : public AbstractTCPConnection { Q_OBJECT public: - - AutoConnection(QThread *thread); + AutoConnection(QThread *thread, int16 protocolDcId); void sendData(mtpBuffer &buffer) override; void disconnectFromServer() override; @@ -32,7 +31,6 @@ public: QString transport() const override; public slots: - void socketError(QAbstractSocket::SocketError e); void requestFinished(QNetworkReply *reply); @@ -43,11 +41,9 @@ public slots: void onTcpTimeoutTimer(); protected: - void socketPacket(const char *packet, uint32 length) override; private: - void httpSend(mtpBuffer &buffer); enum Status { WaitingBoth = 0, diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.cpp b/Telegram/SourceFiles/mtproto/connection_tcp.cpp index 854c3051f..255c5f646 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.cpp +++ b/Telegram/SourceFiles/mtproto/connection_tcp.cpp @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "mtproto/connection_tcp.h" +#include "base/bytes.h" +#include "base/openssl_help.h" #include namespace MTP { @@ -26,7 +28,11 @@ uint32 tcpPacketSize(const char *packet) { // must have at least 4 bytes readabl } // namespace -AbstractTCPConnection::AbstractTCPConnection(QThread *thread) : AbstractConnection(thread) +AbstractTCPConnection::AbstractTCPConnection( + QThread *thread, + int16 protocolDcId) +: AbstractConnection(thread) +, _protocolDcId(protocolDcId) , packetNum(0) , packetRead(0) , packetLeft(0) @@ -191,7 +197,8 @@ void AbstractTCPConnection::handleError(QAbstractSocket::SocketError e, QTcpSock TCP_LOG(("TCP Error %1, restarting! - %2").arg(e).arg(sock.errorString())); } -TCPConnection::TCPConnection(QThread *thread) : AbstractTCPConnection(thread) +TCPConnection::TCPConnection(QThread *thread, int16 protocolDcId) +: AbstractTCPConnection(thread, protocolDcId) , status(WaitingTcp) , tcpNonce(rand_value()) , _tcpTimeout(MTPMinReceiveDelay) @@ -259,35 +266,65 @@ void TCPConnection::sendData(mtpBuffer &buffer) { tcpSend(buffer); } +void AbstractTCPConnection::writeConnectionStart() { + // prepare random part + auto nonceBytes = bytes::vector(64); + const auto nonce = bytes::make_span(nonceBytes); + + const auto zero = reinterpret_cast(nonce.data()); + const auto first = reinterpret_cast(nonce.data()); + const auto second = first + 1; + const auto reserved01 = 0x000000EFU; + const auto reserved11 = 0x44414548U; + const auto reserved12 = 0x54534F50U; + const auto reserved13 = 0x20544547U; + const auto reserved14 = 0x20544547U; + const auto reserved15 = 0xEEEEEEEEU; + const auto reserved21 = 0x00000000U; + do { + bytes::set_random(nonce); + } while (*zero == reserved01 + || *first == reserved11 + || *first == reserved12 + || *first == reserved13 + || *first == reserved14 + || *first == reserved15 + || *second == reserved21); + + // prepare encryption key/iv + bytes::copy( + bytes::make_span(_sendKey), + nonce.subspan(8, CTRState::KeySize)); + bytes::copy( + bytes::make_span(_sendState.ivec), + nonce.subspan(8 + CTRState::KeySize, CTRState::IvecSize)); + + // prepare decryption key/iv + auto reversedBytes = bytes::vector(48); + const auto reversed = bytes::make_span(reversedBytes); + bytes::copy(reversed, nonce.subspan(8, reversed.size())); + std::reverse(reversed.begin(), reversed.end()); + bytes::copy( + bytes::make_span(_receiveKey), + reversed.subspan(0, CTRState::KeySize)); + bytes::copy( + bytes::make_span(_receiveState.ivec), + reversed.subspan(CTRState::KeySize, CTRState::IvecSize)); + + // write protocol and dc ids + const auto protocol = reinterpret_cast(nonce.data() + 56); + *protocol = 0xEFEFEFEFU; + const auto dcId = reinterpret_cast(nonce.data() + 60); + *dcId = _protocolDcId; + + sock.write(reinterpret_cast(nonce.data()), 56); + aesCtrEncrypt(nonce.data(), 64, _sendKey, &_sendState); + sock.write(reinterpret_cast(nonce.subspan(56).data()), 8); +} + void AbstractTCPConnection::tcpSend(mtpBuffer &buffer) { if (!packetNum) { - // prepare random part - char nonce[64]; - uint32 *first = reinterpret_cast(nonce), *second = first + 1; - uint32 first1 = 0x44414548U, first2 = 0x54534f50U, first3 = 0x20544547U, first4 = 0x20544547U, first5 = 0xeeeeeeeeU; - uint32 second1 = 0; - do { - memset_rand(nonce, sizeof(nonce)); - } while (*first == first1 || *first == first2 || *first == first3 || *first == first4 || *first == first5 || *second == second1 || *reinterpret_cast(nonce) == 0xef); - //sock.write(nonce, 64); - - // prepare encryption key/iv - memcpy(_sendKey, nonce + 8, CTRState::KeySize); - memcpy(_sendState.ivec, nonce + 8 + CTRState::KeySize, CTRState::IvecSize); - - // prepare decryption key/iv - char reversed[48]; - memcpy(reversed, nonce + 8, sizeof(reversed)); - std::reverse(reversed, reversed + base::array_size(reversed)); - memcpy(_receiveKey, reversed, CTRState::KeySize); - memcpy(_receiveState.ivec, reversed + CTRState::KeySize, CTRState::IvecSize); - - // write protocol identifier - *reinterpret_cast(nonce + 56) = 0xefefefefU; - - sock.write(nonce, 56); - aesCtrEncrypt(nonce, 64, _sendKey, &_sendState); - sock.write(nonce + 56, 8); + writeConnectionStart(); } ++packetNum; diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.h b/Telegram/SourceFiles/mtproto/connection_tcp.h index 93edea04e..9470ee0e2 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.h +++ b/Telegram/SourceFiles/mtproto/connection_tcp.h @@ -17,15 +17,14 @@ class AbstractTCPConnection : public AbstractConnection { Q_OBJECT public: - - AbstractTCPConnection(QThread *thread); + AbstractTCPConnection(QThread *thread, int16 protocolDcId); virtual ~AbstractTCPConnection() = 0; public slots: - void socketRead(); protected: + void writeConnectionStart(); QTcpSocket sock; uint32 packetNum; // sent packet number @@ -49,6 +48,7 @@ protected: CTRState _sendState; uchar _receiveKey[CTRState::KeySize]; CTRState _receiveState; + int16 _protocolDcId = 0; }; @@ -56,8 +56,7 @@ class TCPConnection : public AbstractTCPConnection { Q_OBJECT public: - - TCPConnection(QThread *thread); + TCPConnection(QThread *thread, int16 protocolDcId); void sendData(mtpBuffer &buffer) override; void disconnectFromServer() override; diff --git a/Telegram/SourceFiles/mtproto/session.cpp b/Telegram/SourceFiles/mtproto/session.cpp index 86ceca588..bf0b6b6d4 100644 --- a/Telegram/SourceFiles/mtproto/session.cpp +++ b/Telegram/SourceFiles/mtproto/session.cpp @@ -27,6 +27,23 @@ QString LogIds(const QVector &ids) { } // namespace +ConnectionOptions::ConnectionOptions( + const QString &systemLangCode, + const QString &cloudLangCode, + const ProxyData &proxy, + bool useIPv4, + bool useIPv6, + bool useHttp, + bool useTcp) +: systemLangCode(systemLangCode) +, cloudLangCode(cloudLangCode) +, proxy(proxy) +, useIPv4(useIPv4) +, useIPv6(useIPv6) +, useHttp(useHttp) +, useTcp(useTcp) { +} + void SessionData::setKey(const AuthKeyPtr &key) { if (_authKey != key) { uint64 session = rand_value(); @@ -93,8 +110,7 @@ Session::Session(not_null instance, ShiftedDcId shiftedDcId) : QObjec connect(&timeouter, SIGNAL(timeout()), this, SLOT(checkRequestsByTimer())); timeouter.start(1000); - data.setSystemLangCode(instance->systemLangCode()); - data.setCloudLangCode(instance->cloudLangCode()); + refreshDataFields(); connect(&sender, SIGNAL(timeout()), this, SLOT(needToResumeAndSend())); } @@ -147,11 +163,33 @@ void Session::restart() { DEBUG_LOG(("Session Error: can't restart a killed session")); return; } - data.setSystemLangCode(_instance->systemLangCode()); - data.setCloudLangCode(_instance->cloudLangCode()); + refreshDataFields(); emit needToRestart(); } +void Session::refreshDataFields() { + const auto connectionType = Global::ConnectionType(); + const auto proxyType = Global::ConnectionProxy().type; + const auto useTcp = (proxyType != ProxyData::Type::Http) && + (connectionType == dbictAuto + || connectionType == dbictTcpProxy + || proxyType == ProxyData::Type::Mtproto); + const auto useHttp = (proxyType != ProxyData::Type::Mtproto) && + (connectionType == dbictAuto + || connectionType == dbictHttpProxy); + const auto useIPv4 = true; + const auto useIPv6 = (proxyType != ProxyData::Type::Mtproto) && + Global::TryIPv6(); + data.setConnectionOptions(ConnectionOptions( + _instance->systemLangCode(), + _instance->cloudLangCode(), + Global::ConnectionProxy(), + useIPv4, + useIPv6, + useHttp, + useTcp)); +} + void Session::stop() { if (_killed) { DEBUG_LOG(("Session Error: can't kill a killed session")); diff --git a/Telegram/SourceFiles/mtproto/session.h b/Telegram/SourceFiles/mtproto/session.h index 17fdeb79b..fd27cdc6a 100644 --- a/Telegram/SourceFiles/mtproto/session.h +++ b/Telegram/SourceFiles/mtproto/session.h @@ -85,6 +85,29 @@ inline bool ResponseNeedsAck(const SerializedMessage &response) { return (seqNo & 0x01) ? true : false; } +struct ConnectionOptions { + ConnectionOptions() = default; + ConnectionOptions( + const QString &systemLangCode, + const QString &cloudLangCode, + const ProxyData &proxy, + bool useIPv4, + bool useIPv6, + bool useHttp, + bool useTcp); + ConnectionOptions(const ConnectionOptions &other) = default; + ConnectionOptions &operator=(const ConnectionOptions &other) = default; + + QString systemLangCode; + QString cloudLangCode; + ProxyData proxy; + bool useIPv4 = true; + bool useIPv6 = true; + bool useHttp = true; + bool useTcp = true; + +}; + class Session; class SessionData { public: @@ -113,21 +136,13 @@ public: _layerInited = was; } - QString systemLangCode() const { - QReadLocker locker(&_lock); - return _systemLangCode; - } - void setSystemLangCode(const QString &code) { + void setConnectionOptions(ConnectionOptions options) { QWriteLocker locker(&_lock); - _systemLangCode = code; + _options = options; } - QString cloudLangCode() const { + ConnectionOptions connectionOptions() const { QReadLocker locker(&_lock); - return _cloudLangCode; - } - void setCloudLangCode(const QString &code) { - QWriteLocker locker(&_lock); - _cloudLangCode = code; + return _options; } void setSalt(uint64 salt) { @@ -253,8 +268,7 @@ private: AuthKeyPtr _authKey; bool _keyChecked = false; bool _layerInited = false; - QString _systemLangCode; - QString _cloudLangCode; + ConnectionOptions _options; mtpPreRequestMap _toSend; // map of request_id -> request, that is waiting to be sent mtpRequestMap _haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers @@ -347,6 +361,7 @@ public slots: private: void createDcData(); + void refreshDataFields(); void registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift); mtpRequestId storeRequest( diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index aca0b1394..710eec1bb 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -1178,14 +1178,16 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting } proxy.port = port; - proxy.type = (proxyType == kProxyTypeShift + int(ProxyData::Type::Socks5)) - ? ProxyData::Type::Socks5 - : (proxyType == kProxyTypeShift + int(ProxyData::Type::Http)) - ? ProxyData::Type::Http - : (proxyType == dbictTcpProxy) + proxy.type = (proxyType == dbictTcpProxy) ? ProxyData::Type::Socks5 : (proxyType == dbictHttpProxy) ? ProxyData::Type::Http + : (proxyType == kProxyTypeShift + int(ProxyData::Type::Socks5)) + ? ProxyData::Type::Socks5 + : (proxyType == kProxyTypeShift + int(ProxyData::Type::Http)) + ? ProxyData::Type::Http + : (proxyType == kProxyTypeShift + int(ProxyData::Type::Mtproto)) + ? ProxyData::Type::Mtproto : ProxyData::Type::None; switch (connectionType) { case dbictHttpProxy: