Support work with different dcs on a single IP.

This commit is contained in:
John Preston 2018-04-24 12:46:27 +04:00
parent 909acb25fd
commit 7482025c10
16 changed files with 391 additions and 97 deletions

View File

@ -322,7 +322,8 @@ void Application::refreshGlobalProxy() {
} }
return Sandbox::PreLaunchProxy(); return Sandbox::PreLaunchProxy();
}(); }();
if (proxy.type != ProxyData::Type::None) { if (proxy.type == ProxyData::Type::Socks5
|| proxy.type == ProxyData::Type::Http) {
QNetworkProxy::setApplicationProxy(QNetworkProxy( QNetworkProxy::setApplicationProxy(QNetworkProxy(
(proxy.type == ProxyData::Type::Socks5 (proxy.type == ProxyData::Type::Socks5
? QNetworkProxy::Socks5Proxy ? QNetworkProxy::Socks5Proxy

View File

@ -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 <gsl/gsl_byte>
namespace bytes {
using span = gsl::span<gsl::byte>;
using const_span = gsl::span<const gsl::byte>;
using vector = std::vector<gsl::byte>;
template <
typename Container,
typename = std::enable_if_t<!std::is_const_v<Container>>>
inline span make_span(Container &container) {
return gsl::as_writeable_bytes(gsl::make_span(container));
}
template <typename Container>
inline const_span make_span(const Container &container) {
return gsl::as_bytes(gsl::make_span(container));
}
template <typename Type, std::ptrdiff_t Extent>
inline span make_span(gsl::span<Type, Extent> container) {
return gsl::as_writeable_bytes(container);
}
template <typename Type, std::ptrdiff_t Extent>
inline const_span make_span(gsl::span<const Type, Extent> container) {
return gsl::as_bytes(container);
}
template <typename Type>
inline span make_span(Type *value, std::size_t count) {
return gsl::as_writeable_bytes(gsl::make_span(value, count));
}
template <typename Type>
inline const_span make_span(const Type *value, std::size_t count) {
return gsl::as_bytes(gsl::make_span(value, count));
}
template <typename Container>
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<unsigned char>(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 <typename Arg>
std::size_t spansLength(Arg &&arg) {
return bytes::make_span(arg).size();
}
template <typename Arg, typename ...Args>
std::size_t spansLength(Arg &&arg, Args &&...args) {
return bytes::make_span(arg).size() + spansLength(args...);
}
template <typename Arg>
void spansAppend(span destination, Arg &&arg) {
bytes::copy(destination, bytes::make_span(arg));
}
template <typename Arg, typename ...Args>
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

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <openssl/bn.h> #include <openssl/bn.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include <openssl/rand.h> #include <openssl/rand.h>
#include "base/bytes.h"
namespace openssl { namespace openssl {
@ -223,3 +224,13 @@ inline int FillRandom(base::byte_span bytes) {
} }
} // namespace openssl } // namespace openssl
namespace bytes {
inline void set_random(span destination) {
RAND_bytes(
reinterpret_cast<unsigned char*>(destination.data()),
destination.size());
}
} // namespace bytes

View File

@ -432,6 +432,7 @@ struct ProxyData {
None, None,
Socks5, Socks5,
Http, Http,
Mtproto,
}; };
Type type = Type::None; Type type = Type::None;
QString host; QString host;

View File

@ -100,7 +100,7 @@ void ConfigLoader::enumerate() {
} }
void ConfigLoader::createSpecialLoader() { void ConfigLoader::createSpecialLoader() {
if (Global::ConnectionType() != dbictAuto) { if (Global::ConnectionProxy().type != ProxyData::Type::None) {
_specialLoader.reset(); _specialLoader.reset();
return; return;
} }

View File

@ -180,7 +180,7 @@ ModExpFirst CreateModExp(
ModExpFirst result; ModExpFirst result;
constexpr auto kMaxModExpFirstTries = 5; constexpr auto kMaxModExpFirstTries = 5;
for (auto tries = 0; tries != kMaxModExpFirstTries; ++tries) { for (auto tries = 0; tries != kMaxModExpFirstTries; ++tries) {
FillRandom(result.randomPower); bytes::set_random(result.randomPower);
for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) { for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) {
result.randomPower[i] ^= randomSeed[i]; result.randomPower[i] ^= randomSeed[i];
} }
@ -303,6 +303,7 @@ QString Connection::transport() const {
Connection::~Connection() { Connection::~Connection() {
Expects(data == nullptr); Expects(data == nullptr);
if (thread) { if (thread) {
waitTillFinish(); waitTillFinish();
} }
@ -312,13 +313,21 @@ void ConnectionPrivate::createConn(bool createIPv4, bool createIPv6) {
destroyAllConnections(); destroyAllConnections();
if (createIPv4) { if (createIPv4) {
QWriteLocker lock(&stateConnMutex); 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(error(qint32)), this, SLOT(onError4(qint32)));
connect(_conn4, SIGNAL(receivedSome()), this, SLOT(onReceivedSome())); connect(_conn4, SIGNAL(receivedSome()), this, SLOT(onReceivedSome()));
} }
if (createIPv6) { if (createIPv6) {
QWriteLocker lock(&stateConnMutex); 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(error(qint32)), this, SLOT(onError6(qint32)));
connect(_conn6, SIGNAL(receivedSome()), this, SLOT(onReceivedSome())); connect(_conn6, SIGNAL(receivedSome()), this, SLOT(onReceivedSome()));
} }
@ -440,8 +449,12 @@ QString ConnectionPrivate::transport() const {
if ((!_conn4 && !_conn6) || (_conn4 && _conn6) || (_state < 0)) { if ((!_conn4 && !_conn6) || (_conn4 && _conn6) || (_state < 0)) {
return QString(); 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; return result;
} }
@ -783,8 +796,9 @@ void ConnectionPrivate::tryToSend() {
MTPInitConnection<mtpRequest> initWrapper; MTPInitConnection<mtpRequest> initWrapper;
int32 initSize = 0, initSizeInInts = 0; int32 initSize = 0, initSizeInInts = 0;
if (needsLayer) { if (needsLayer) {
auto systemLangCode = sessionData->systemLangCode(); Assert(_connectionOptions != nullptr);
auto cloudLangCode = sessionData->cloudLangCode(); auto systemLangCode = _connectionOptions->systemLangCode;
auto cloudLangCode = _connectionOptions->cloudLangCode;
auto langPack = "tdesktop"; auto langPack = "tdesktop";
auto deviceModel = (_dcType == DcType::Cdn) ? "n/a" : cApiDeviceModel(); auto deviceModel = (_dcType == DcType::Cdn) ? "n/a" : cApiDeviceModel();
auto systemVersion = (_dcType == DcType::Cdn) ? "n/a" : cApiSystemVersion(); auto systemVersion = (_dcType == DcType::Cdn) ? "n/a" : cApiSystemVersion();
@ -996,16 +1010,28 @@ void ConnectionPrivate::restartNow() {
void ConnectionPrivate::connectToServer(bool afterConfig) { void ConnectionPrivate::connectToServer(bool afterConfig) {
if (_finished) { if (_finished) {
DEBUG_LOG(("MTP Error: connectToServer() called for finished connection!")); DEBUG_LOG(("MTP Error: "
"connectToServer() called for finished connection!"));
return; return;
} }
auto hasKey = true;
{
QReadLocker lockFinished(&sessionDataMutex);
if (!sessionData) {
DEBUG_LOG(("MTP Error: "
"connectToServer() called for stopped connection!"));
return;
}
_connectionOptions = std::make_unique<ConnectionOptions>(
sessionData->connectionOptions());
hasKey = (sessionData->getKey() != nullptr);
}
auto bareDc = bareDcId(_shiftedDcId); auto bareDc = bareDcId(_shiftedDcId);
_dcType = _instance->dcOptions()->dcType(_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); // Use media_only addresses only if key for this dc is already created.
if (!sessionData || !sessionData->getKey()) { if (_dcType == DcType::MediaDownload && !hasKey) {
_dcType = DcType::Regular; _dcType = DcType::Regular;
}
} else if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) { } else if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) {
if (!_instance->dcOptions()->hasCDNKeysForDc(bareDc)) { if (!_instance->dcOptions()->hasCDNKeysForDc(bareDc)) {
requestCDNConfig(); requestCDNConfig();
@ -1014,26 +1040,30 @@ void ConnectionPrivate::connectToServer(bool afterConfig) {
} }
using Variants = DcOptions::Variants; using Variants = DcOptions::Variants;
auto kIPv4 = Variants::IPv4; const auto kIPv4 = Variants::IPv4;
auto kIPv6 = Variants::IPv6; const auto kIPv6 = Variants::IPv6;
auto kTcp = Variants::Tcp; const auto kTcp = Variants::Tcp;
auto kHttp = Variants::Http; const auto kHttp = Variants::Http;
auto variants = _instance->dcOptions()->lookup(bareDc, _dcType); const auto variants = _instance->dcOptions()->lookup(bareDc, _dcType);
auto noIPv4 = (_dcType == DcType::Temporary) ? (variants.data[kIPv4][kTcp].port == 0) : (variants.data[kIPv4][kHttp].port == 0); const auto useIPv4 = (_dcType == DcType::Temporary) ? true : _connectionOptions->useIPv4;
auto noIPv6 = (_dcType == DcType::Temporary) ? true : (!Global::TryIPv6() || (variants.data[kIPv6][kHttp].port == 0)); 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 (noIPv4 && noIPv6) {
if (_instance->isKeysDestroyer()) { if (_instance->isKeysDestroyer()) {
LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found for auth key destruction!").arg(_shiftedDcId)); 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); emit _instance->keyDestroyed(_shiftedDcId);
return; return;
} else if (afterConfig) { } else if (afterConfig) {
LOG(("MTP Error: DC %1 options for IPv4 over HTTP not found right after config load!").arg(_shiftedDcId)); 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(); return restart();
} }
DEBUG_LOG(("MTP Info: DC %1 options for IPv4 over HTTP not found, waiting for config").arg(_shiftedDcId)); 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); connect(_instance, SIGNAL(configLoaded()), this, SLOT(onConfigLoaded()), Qt::UniqueConnection);
InvokeQueued(_instance, [instance = _instance] { InvokeQueued(_instance, [instance = _instance] {
instance->requestConfig(); instance->requestConfig();
@ -1064,9 +1094,17 @@ void ConnectionPrivate::connectToServer(bool afterConfig) {
_waitForConnectedTimer.start(_waitForConnected); _waitForConnectedTimer.start(_waitForConnected);
if (auto conn = _conn4) { 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(connected()), this, SLOT(onConnected4()));
connect(conn, SIGNAL(disconnected()), this, SLOT(onDisconnected4())); connect(conn, SIGNAL(disconnected()), this, SLOT(onDisconnected4()));
conn->connectTcp(variants.data[kIPv4][kTcp]); conn->connectTcp(endpoint);
conn->connectHttp(variants.data[kIPv4][kHttp]); conn->connectHttp(variants.data[kIPv4][kHttp]);
} }
if (auto conn = _conn6) { if (auto conn = _conn6) {
@ -1180,7 +1218,9 @@ void ConnectionPrivate::onPingSendForce() {
} }
void ConnectionPrivate::onWaitReceivedFailed() { void ConnectionPrivate::onWaitReceivedFailed() {
if (Global::ConnectionType() != dbictAuto && Global::ConnectionType() != dbictTcpProxy) { Expects(_connectionOptions != nullptr);
if (!_connectionOptions->useTcp) {
return; return;
} }
@ -2625,7 +2665,7 @@ void ConnectionPrivate::dhClientParamsSend() {
// gen rand 'b' // gen rand 'b'
auto randomSeed = std::array<gsl::byte, ModExpFirst::kRandomPowerSize>(); auto randomSeed = std::array<gsl::byte, ModExpFirst::kRandomPowerSize>();
openssl::FillRandom(randomSeed); bytes::set_random(randomSeed);
auto g_b_data = CreateModExp(_authKeyData->g, _authKeyStrings->dh_prime, randomSeed); auto g_b_data = CreateModExp(_authKeyData->g, _authKeyStrings->dh_prime, randomSeed);
if (g_b_data.modexp.empty()) { if (g_b_data.modexp.empty()) {
LOG(("AuthKey Error: could not generate good g_b.")); LOG(("AuthKey Error: could not generate good g_b."));

View File

@ -31,6 +31,7 @@ class AbstractConnection;
class ConnectionPrivate; class ConnectionPrivate;
class SessionData; class SessionData;
class RSAPublicKey; class RSAPublicKey;
struct ConnectionOptions;
class Thread : public QThread { class Thread : public QThread {
Q_OBJECT Q_OBJECT
@ -239,6 +240,7 @@ private:
uint64 keyId = 0; uint64 keyId = 0;
QReadWriteLock sessionDataMutex; QReadWriteLock sessionDataMutex;
SessionData *sessionData = nullptr; SessionData *sessionData = nullptr;
std::unique_ptr<ConnectionOptions> _connectionOptions;
bool myKeyLock = false; bool myKeyLock = false;
void lockKey(); void lockKey();

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/connection_tcp.h" #include "mtproto/connection_tcp.h"
#include "mtproto/connection_http.h" #include "mtproto/connection_http.h"
#include "mtproto/connection_auto.h" #include "mtproto/connection_auto.h"
#include "mtproto/session.h"
namespace MTP { namespace MTP {
namespace internal { namespace internal {
@ -62,13 +63,20 @@ MTPResPQ AbstractConnection::readPQFakeReply(const mtpBuffer &buffer) {
return response; return response;
} }
AbstractConnection *AbstractConnection::create(DcType type, QThread *thread) { AbstractConnection *AbstractConnection::create(
if ((type == DcType::Temporary) || (Global::ConnectionType() == dbictTcpProxy)) { const ConnectionOptions &options,
return new TCPConnection(thread); ShiftedDcId shiftedDcId,
} else if (Global::ConnectionType() == dbictHttpProxy) { 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 HTTPConnection(thread);
} }
return new AutoConnection(thread); return new AutoConnection(thread, protocolDcId);
} }
} // namespace internal } // namespace internal

View File

@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace MTP { namespace MTP {
namespace internal { namespace internal {
struct ConnectionOptions;
class AbstractConnection : public QObject { class AbstractConnection : public QObject {
Q_OBJECT Q_OBJECT
@ -24,7 +26,11 @@ public:
virtual ~AbstractConnection() = 0; virtual ~AbstractConnection() = 0;
// virtual constructor // virtual constructor
static AbstractConnection *create(DcType type, QThread *thread); static AbstractConnection *create(
const ConnectionOptions &options,
ShiftedDcId shiftedDcId,
DcType type,
QThread *thread);
void setSentEncrypted() { void setSentEncrypted() {
_sentEncrypted = true; _sentEncrypted = true;

View File

@ -12,7 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace MTP { namespace MTP {
namespace internal { namespace internal {
AutoConnection::AutoConnection(QThread *thread) : AbstractTCPConnection(thread) AutoConnection::AutoConnection(QThread *thread, int16 protocolDcId)
: AbstractTCPConnection(thread, protocolDcId)
, status(WaitingBoth) , status(WaitingBoth)
, tcpNonce(rand_value<MTPint128>()) , tcpNonce(rand_value<MTPint128>())
, httpNonce(rand_value<MTPint128>()) , httpNonce(rand_value<MTPint128>())

View File

@ -16,8 +16,7 @@ class AutoConnection : public AbstractTCPConnection {
Q_OBJECT Q_OBJECT
public: public:
AutoConnection(QThread *thread, int16 protocolDcId);
AutoConnection(QThread *thread);
void sendData(mtpBuffer &buffer) override; void sendData(mtpBuffer &buffer) override;
void disconnectFromServer() override; void disconnectFromServer() override;
@ -32,7 +31,6 @@ public:
QString transport() const override; QString transport() const override;
public slots: public slots:
void socketError(QAbstractSocket::SocketError e); void socketError(QAbstractSocket::SocketError e);
void requestFinished(QNetworkReply *reply); void requestFinished(QNetworkReply *reply);
@ -43,11 +41,9 @@ public slots:
void onTcpTimeoutTimer(); void onTcpTimeoutTimer();
protected: protected:
void socketPacket(const char *packet, uint32 length) override; void socketPacket(const char *packet, uint32 length) override;
private: private:
void httpSend(mtpBuffer &buffer); void httpSend(mtpBuffer &buffer);
enum Status { enum Status {
WaitingBoth = 0, WaitingBoth = 0,

View File

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "mtproto/connection_tcp.h" #include "mtproto/connection_tcp.h"
#include "base/bytes.h"
#include "base/openssl_help.h"
#include <openssl/aes.h> #include <openssl/aes.h>
namespace MTP { namespace MTP {
@ -26,7 +28,11 @@ uint32 tcpPacketSize(const char *packet) { // must have at least 4 bytes readabl
} // namespace } // namespace
AbstractTCPConnection::AbstractTCPConnection(QThread *thread) : AbstractConnection(thread) AbstractTCPConnection::AbstractTCPConnection(
QThread *thread,
int16 protocolDcId)
: AbstractConnection(thread)
, _protocolDcId(protocolDcId)
, packetNum(0) , packetNum(0)
, packetRead(0) , packetRead(0)
, packetLeft(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())); 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) , status(WaitingTcp)
, tcpNonce(rand_value<MTPint128>()) , tcpNonce(rand_value<MTPint128>())
, _tcpTimeout(MTPMinReceiveDelay) , _tcpTimeout(MTPMinReceiveDelay)
@ -259,35 +266,65 @@ void TCPConnection::sendData(mtpBuffer &buffer) {
tcpSend(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<uchar*>(nonce.data());
const auto first = reinterpret_cast<uint32*>(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<uint32*>(nonce.data() + 56);
*protocol = 0xEFEFEFEFU;
const auto dcId = reinterpret_cast<int16*>(nonce.data() + 60);
*dcId = _protocolDcId;
sock.write(reinterpret_cast<const char*>(nonce.data()), 56);
aesCtrEncrypt(nonce.data(), 64, _sendKey, &_sendState);
sock.write(reinterpret_cast<const char*>(nonce.subspan(56).data()), 8);
}
void AbstractTCPConnection::tcpSend(mtpBuffer &buffer) { void AbstractTCPConnection::tcpSend(mtpBuffer &buffer) {
if (!packetNum) { if (!packetNum) {
// prepare random part writeConnectionStart();
char nonce[64];
uint32 *first = reinterpret_cast<uint32*>(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<uchar*>(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<uint32*>(nonce + 56) = 0xefefefefU;
sock.write(nonce, 56);
aesCtrEncrypt(nonce, 64, _sendKey, &_sendState);
sock.write(nonce + 56, 8);
} }
++packetNum; ++packetNum;

View File

@ -17,15 +17,14 @@ class AbstractTCPConnection : public AbstractConnection {
Q_OBJECT Q_OBJECT
public: public:
AbstractTCPConnection(QThread *thread, int16 protocolDcId);
AbstractTCPConnection(QThread *thread);
virtual ~AbstractTCPConnection() = 0; virtual ~AbstractTCPConnection() = 0;
public slots: public slots:
void socketRead(); void socketRead();
protected: protected:
void writeConnectionStart();
QTcpSocket sock; QTcpSocket sock;
uint32 packetNum; // sent packet number uint32 packetNum; // sent packet number
@ -49,6 +48,7 @@ protected:
CTRState _sendState; CTRState _sendState;
uchar _receiveKey[CTRState::KeySize]; uchar _receiveKey[CTRState::KeySize];
CTRState _receiveState; CTRState _receiveState;
int16 _protocolDcId = 0;
}; };
@ -56,8 +56,7 @@ class TCPConnection : public AbstractTCPConnection {
Q_OBJECT Q_OBJECT
public: public:
TCPConnection(QThread *thread, int16 protocolDcId);
TCPConnection(QThread *thread);
void sendData(mtpBuffer &buffer) override; void sendData(mtpBuffer &buffer) override;
void disconnectFromServer() override; void disconnectFromServer() override;

View File

@ -27,6 +27,23 @@ QString LogIds(const QVector<uint64> &ids) {
} // namespace } // 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) { void SessionData::setKey(const AuthKeyPtr &key) {
if (_authKey != key) { if (_authKey != key) {
uint64 session = rand_value<uint64>(); uint64 session = rand_value<uint64>();
@ -93,8 +110,7 @@ Session::Session(not_null<Instance*> instance, ShiftedDcId shiftedDcId) : QObjec
connect(&timeouter, SIGNAL(timeout()), this, SLOT(checkRequestsByTimer())); connect(&timeouter, SIGNAL(timeout()), this, SLOT(checkRequestsByTimer()));
timeouter.start(1000); timeouter.start(1000);
data.setSystemLangCode(instance->systemLangCode()); refreshDataFields();
data.setCloudLangCode(instance->cloudLangCode());
connect(&sender, SIGNAL(timeout()), this, SLOT(needToResumeAndSend())); connect(&sender, SIGNAL(timeout()), this, SLOT(needToResumeAndSend()));
} }
@ -147,11 +163,33 @@ void Session::restart() {
DEBUG_LOG(("Session Error: can't restart a killed session")); DEBUG_LOG(("Session Error: can't restart a killed session"));
return; return;
} }
data.setSystemLangCode(_instance->systemLangCode()); refreshDataFields();
data.setCloudLangCode(_instance->cloudLangCode());
emit needToRestart(); 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() { void Session::stop() {
if (_killed) { if (_killed) {
DEBUG_LOG(("Session Error: can't kill a killed session")); DEBUG_LOG(("Session Error: can't kill a killed session"));

View File

@ -85,6 +85,29 @@ inline bool ResponseNeedsAck(const SerializedMessage &response) {
return (seqNo & 0x01) ? true : false; 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 Session;
class SessionData { class SessionData {
public: public:
@ -113,21 +136,13 @@ public:
_layerInited = was; _layerInited = was;
} }
QString systemLangCode() const { void setConnectionOptions(ConnectionOptions options) {
QReadLocker locker(&_lock);
return _systemLangCode;
}
void setSystemLangCode(const QString &code) {
QWriteLocker locker(&_lock); QWriteLocker locker(&_lock);
_systemLangCode = code; _options = options;
} }
QString cloudLangCode() const { ConnectionOptions connectionOptions() const {
QReadLocker locker(&_lock); QReadLocker locker(&_lock);
return _cloudLangCode; return _options;
}
void setCloudLangCode(const QString &code) {
QWriteLocker locker(&_lock);
_cloudLangCode = code;
} }
void setSalt(uint64 salt) { void setSalt(uint64 salt) {
@ -253,8 +268,7 @@ private:
AuthKeyPtr _authKey; AuthKeyPtr _authKey;
bool _keyChecked = false; bool _keyChecked = false;
bool _layerInited = false; bool _layerInited = false;
QString _systemLangCode; ConnectionOptions _options;
QString _cloudLangCode;
mtpPreRequestMap _toSend; // map of request_id -> request, that is waiting to be sent 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 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: private:
void createDcData(); void createDcData();
void refreshDataFields();
void registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift); void registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift);
mtpRequestId storeRequest( mtpRequestId storeRequest(

View File

@ -1178,14 +1178,16 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
} }
proxy.port = port; proxy.port = port;
proxy.type = (proxyType == kProxyTypeShift + int(ProxyData::Type::Socks5)) proxy.type = (proxyType == dbictTcpProxy)
? ProxyData::Type::Socks5
: (proxyType == kProxyTypeShift + int(ProxyData::Type::Http))
? ProxyData::Type::Http
: (proxyType == dbictTcpProxy)
? ProxyData::Type::Socks5 ? ProxyData::Type::Socks5
: (proxyType == dbictHttpProxy) : (proxyType == dbictHttpProxy)
? ProxyData::Type::Http ? 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; : ProxyData::Type::None;
switch (connectionType) { switch (connectionType) {
case dbictHttpProxy: case dbictHttpProxy: