mirror of https://github.com/procxx/kepka.git
Resolve domain names for proxy servers.
Also use dc_id-checked auth key creation. Fixes #4695.
This commit is contained in:
parent
a053384618
commit
4478c0a143
|
@ -35,6 +35,9 @@
|
||||||
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
|
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
|
||||||
|
|
||||||
p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
|
p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
|
||||||
|
p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;
|
||||||
|
p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data;
|
||||||
|
p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;
|
||||||
|
|
||||||
server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
|
server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
|
||||||
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
|
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
|
||||||
|
|
|
@ -321,7 +321,8 @@ void Application::refreshGlobalProxy() {
|
||||||
}();
|
}();
|
||||||
if (proxy.type == ProxyData::Type::Socks5
|
if (proxy.type == ProxyData::Type::Socks5
|
||||||
|| proxy.type == ProxyData::Type::Http) {
|
|| proxy.type == ProxyData::Type::Http) {
|
||||||
QNetworkProxy::setApplicationProxy(ToNetworkProxy(proxy));
|
QNetworkProxy::setApplicationProxy(
|
||||||
|
ToNetworkProxy(ToDirectIpProxy(proxy)));
|
||||||
} else {
|
} else {
|
||||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1094,15 +1094,16 @@ void ProxiesBoxController::refreshChecker(Item &item) {
|
||||||
|
|
||||||
item.state = ItemState::Checking;
|
item.state = ItemState::Checking;
|
||||||
const auto setup = [&](Checker &checker) {
|
const auto setup = [&](Checker &checker) {
|
||||||
checker = MTP::internal::AbstractConnection::create(
|
checker = MTP::internal::AbstractConnection::Create(
|
||||||
|
mtproto,
|
||||||
type,
|
type,
|
||||||
QThread::currentThread());
|
QThread::currentThread(),
|
||||||
|
item.data);
|
||||||
setupChecker(item.id, checker);
|
setupChecker(item.id, checker);
|
||||||
};
|
};
|
||||||
setup(item.checker);
|
setup(item.checker);
|
||||||
if (item.data.type == Type::Mtproto) {
|
if (item.data.type == Type::Mtproto) {
|
||||||
item.checkerv6 = nullptr;
|
item.checkerv6 = nullptr;
|
||||||
item.checker->setProxyOverride(item.data);
|
|
||||||
item.checker->connectToServer(
|
item.checker->connectToServer(
|
||||||
item.data.host,
|
item.data.host,
|
||||||
item.data.port,
|
item.data.port,
|
||||||
|
@ -1131,7 +1132,6 @@ void ProxiesBoxController::refreshChecker(Item &item) {
|
||||||
const Checker &checker,
|
const Checker &checker,
|
||||||
const std::vector<MTP::DcOptions::Endpoint> &endpoints) {
|
const std::vector<MTP::DcOptions::Endpoint> &endpoints) {
|
||||||
if (checker) {
|
if (checker) {
|
||||||
checker->setProxyOverride(item.data);
|
|
||||||
checker->connectToServer(
|
checker->connectToServer(
|
||||||
QString::fromStdString(endpoints.front().ip),
|
QString::fromStdString(endpoints.front().ip),
|
||||||
endpoints.front().port,
|
endpoints.front().port,
|
||||||
|
@ -1185,7 +1185,7 @@ object_ptr<BoxContent> ProxiesBoxController::CreateOwningBox() {
|
||||||
|
|
||||||
object_ptr<BoxContent> ProxiesBoxController::create() {
|
object_ptr<BoxContent> ProxiesBoxController::create() {
|
||||||
auto result = Box<ProxiesBox>(this);
|
auto result = Box<ProxiesBox>(this);
|
||||||
for (const auto &item : base::reversed(_list)) {
|
for (const auto &item : _list) {
|
||||||
updateView(item);
|
updateView(item);
|
||||||
}
|
}
|
||||||
return std::move(result);
|
return std::move(result);
|
||||||
|
|
|
@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "core/utils.h"
|
#include "core/utils.h"
|
||||||
|
|
||||||
|
#include "base/qthelp_url.h"
|
||||||
|
#include "application.h"
|
||||||
|
#include "platform/platform_specific.h"
|
||||||
|
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
@ -14,16 +18,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <openssl/engine.h>
|
#include <openssl/engine.h>
|
||||||
#include <openssl/conf.h>
|
#include <openssl/conf.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
}
|
} // extern "C"
|
||||||
|
|
||||||
#include "application.h"
|
|
||||||
#include "platform/platform_specific.h"
|
|
||||||
|
|
||||||
uint64 _SharedMemoryLocation[4] = { 0x00, 0x01, 0x02, 0x03 };
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#elif defined Q_OS_MAC
|
#elif defined Q_OS_MAC
|
||||||
|
@ -32,7 +32,7 @@ uint64 _SharedMemoryLocation[4] = { 0x00, 0x01, 0x02, 0x03 };
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <openssl/rand.h>
|
uint64 _SharedMemoryLocation[4] = { 0x00, 0x01, 0x02, 0x03 };
|
||||||
|
|
||||||
// Base types compile-time check
|
// Base types compile-time check
|
||||||
static_assert(sizeof(char) == 1, "Basic types size check failed");
|
static_assert(sizeof(char) == 1, "Basic types size check failed");
|
||||||
|
@ -249,6 +249,14 @@ bool ProxyData::supportsCalls() const {
|
||||||
return (type == Type::Socks5);
|
return (type == Type::Socks5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProxyData::tryCustomResolve() const {
|
||||||
|
return (type == Type::Socks5 || type == Type::Mtproto)
|
||||||
|
&& !qthelp::is_ipv6(host)
|
||||||
|
&& !QRegularExpression(
|
||||||
|
qsl("^\\d+\\.\\d+\\.\\d+\\.\\d+$")
|
||||||
|
).match(host).hasMatch();
|
||||||
|
}
|
||||||
|
|
||||||
ProxyData::operator bool() const {
|
ProxyData::operator bool() const {
|
||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
|
@ -272,9 +280,25 @@ bool ProxyData::ValidSecret(const QString &secret) {
|
||||||
return QRegularExpression("^[a-fA-F0-9]{32}$").match(secret).hasMatch();
|
return QRegularExpression("^[a-fA-F0-9]{32}$").match(secret).hasMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProxyData ToDirectIpProxy(const ProxyData &proxy, int ipIndex) {
|
||||||
|
if (!proxy.tryCustomResolve()
|
||||||
|
|| ipIndex < 0
|
||||||
|
|| ipIndex >= proxy.resolvedIPs.size()) {
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
proxy.type,
|
||||||
|
proxy.resolvedIPs[ipIndex],
|
||||||
|
proxy.port,
|
||||||
|
proxy.user,
|
||||||
|
proxy.password
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkProxy ToNetworkProxy(const ProxyData &proxy) {
|
QNetworkProxy ToNetworkProxy(const ProxyData &proxy) {
|
||||||
if (proxy.type == ProxyData::Type::None
|
if (proxy.type == ProxyData::Type::None) {
|
||||||
|| proxy.type == ProxyData::Type::Mtproto) {
|
return QNetworkProxy::DefaultProxy;
|
||||||
|
} else if (proxy.type == ProxyData::Type::Mtproto) {
|
||||||
return QNetworkProxy::NoProxy;
|
return QNetworkProxy::NoProxy;
|
||||||
}
|
}
|
||||||
return QNetworkProxy(
|
return QNetworkProxy(
|
||||||
|
|
|
@ -433,8 +433,12 @@ struct ProxyData {
|
||||||
uint32 port = 0;
|
uint32 port = 0;
|
||||||
QString user, password;
|
QString user, password;
|
||||||
|
|
||||||
|
std::vector<QString> resolvedIPs;
|
||||||
|
TimeMs resolvedExpireAt = 0;
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
bool supportsCalls() const;
|
bool supportsCalls() const;
|
||||||
|
bool tryCustomResolve() const;
|
||||||
explicit operator bool() const;
|
explicit operator bool() const;
|
||||||
bool operator==(const ProxyData &other) const;
|
bool operator==(const ProxyData &other) const;
|
||||||
bool operator!=(const ProxyData &other) const;
|
bool operator!=(const ProxyData &other) const;
|
||||||
|
@ -443,6 +447,7 @@ struct ProxyData {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ProxyData ToDirectIpProxy(const ProxyData &proxy, int ipIndex = 0);
|
||||||
QNetworkProxy ToNetworkProxy(const ProxyData &proxy);
|
QNetworkProxy ToNetworkProxy(const ProxyData &proxy);
|
||||||
|
|
||||||
enum DBIScale {
|
enum DBIScale {
|
||||||
|
|
|
@ -39,6 +39,7 @@ constexpr auto kMarkConnectionOldTimeout = TimeMs(192000);
|
||||||
constexpr auto kPingDelayDisconnect = 60;
|
constexpr auto kPingDelayDisconnect = 60;
|
||||||
constexpr auto kPingSendAfter = TimeMs(30000);
|
constexpr auto kPingSendAfter = TimeMs(30000);
|
||||||
constexpr auto kPingSendAfterForce = TimeMs(45000);
|
constexpr auto kPingSendAfterForce = TimeMs(45000);
|
||||||
|
constexpr auto kTestModeDcIdShift = 10000;
|
||||||
|
|
||||||
// If we can't connect for this time we will ask _instance to update config.
|
// If we can't connect for this time we will ask _instance to update config.
|
||||||
constexpr auto kRequestConfigTimeout = TimeMs(8000);
|
constexpr auto kRequestConfigTimeout = TimeMs(8000);
|
||||||
|
@ -55,35 +56,6 @@ QString LogIdsVector(const QVector<MTPlong> &ids) {
|
||||||
return idsStr + "]";
|
return idsStr + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector ProtocolSecretFromPassword(const QString &password) {
|
|
||||||
const auto size = password.size();
|
|
||||||
if (size % 2) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const auto length = size / 2;
|
|
||||||
const auto fromHex = [](QChar ch) -> int {
|
|
||||||
const auto code = int(ch.unicode());
|
|
||||||
if (code >= '0' && code <= '9') {
|
|
||||||
return (code - '0');
|
|
||||||
} else if (code >= 'A' && code <= 'F') {
|
|
||||||
return 10 + (code - 'A');
|
|
||||||
} else if (ch >= 'a' && ch <= 'f') {
|
|
||||||
return 10 + (code - 'a');
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
auto result = bytes::vector(length);
|
|
||||||
for (auto i = 0; i != length; ++i) {
|
|
||||||
const auto high = fromHex(password[2 * i]);
|
|
||||||
const auto low = fromHex(password[2 * i + 1]);
|
|
||||||
if (high < 0 || low < 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
result[i] = static_cast<gsl::byte>(high * 16 + low);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime) {
|
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime) {
|
||||||
auto diff = prime - modexp;
|
auto diff = prime - modexp;
|
||||||
if (modexp.failed() || prime.failed() || diff.failed()) {
|
if (modexp.failed() || prime.failed() || diff.failed()) {
|
||||||
|
@ -365,7 +337,11 @@ void ConnectionPrivate::appendTestConnection(
|
||||||
+ (protocol == DcOptions::Variants::Tcp ? 1 : 0)
|
+ (protocol == DcOptions::Variants::Tcp ? 1 : 0)
|
||||||
+ (protocolSecret.empty() ? 0 : 1);
|
+ (protocolSecret.empty() ? 0 : 1);
|
||||||
_testConnections.push_back({
|
_testConnections.push_back({
|
||||||
AbstractConnection::create(protocol, thread()),
|
AbstractConnection::Create(
|
||||||
|
_instance,
|
||||||
|
protocol,
|
||||||
|
thread(),
|
||||||
|
_connectionOptions->proxy),
|
||||||
priority
|
priority
|
||||||
});
|
});
|
||||||
auto weak = _testConnections.back().data.get();
|
auto weak = _testConnections.back().data.get();
|
||||||
|
@ -388,16 +364,22 @@ void ConnectionPrivate::appendTestConnection(
|
||||||
onDisconnected(weak);
|
onDisconnected(weak);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
InvokeQueued(_testConnections.back().data, [=] {
|
||||||
|
weak->connectToServer(ip, port, protocolSecret, getProtocolDcId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int16 ConnectionPrivate::getProtocolDcId() const {
|
||||||
const auto dcId = MTP::bareDcId(_shiftedDcId);
|
const auto dcId = MTP::bareDcId(_shiftedDcId);
|
||||||
const auto simpleDcId = MTP::isTemporaryDcId(dcId)
|
const auto simpleDcId = MTP::isTemporaryDcId(dcId)
|
||||||
? MTP::getRealIdFromTemporaryDcId(dcId)
|
? MTP::getRealIdFromTemporaryDcId(dcId)
|
||||||
: dcId;
|
: dcId;
|
||||||
const auto protocolDcId = (_dcType == DcType::MediaDownload)
|
const auto testedDcId = cTestMode()
|
||||||
? -simpleDcId
|
? (kTestModeDcIdShift + simpleDcId)
|
||||||
: simpleDcId;
|
: simpleDcId;
|
||||||
InvokeQueued(_testConnections.back().data, [=] {
|
return (_dcType == DcType::MediaDownload)
|
||||||
weak->connectToServer(ip, port, protocolSecret, protocolDcId);
|
? -testedDcId
|
||||||
});
|
: testedDcId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::destroyAllConnections() {
|
void ConnectionPrivate::destroyAllConnections() {
|
||||||
|
@ -732,7 +714,7 @@ void ConnectionPrivate::tryToSend() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needsLayer = !sessionData->layerWasInited();
|
bool needsLayer = !_connectionOptions->inited;
|
||||||
int32 state = getState();
|
int32 state = getState();
|
||||||
bool prependOnly = (state != ConnectedState);
|
bool prependOnly = (state != ConnectedState);
|
||||||
mtpRequest pingRequest;
|
mtpRequest pingRequest;
|
||||||
|
@ -1105,11 +1087,8 @@ void ConnectionPrivate::connectToServer(bool afterConfig) {
|
||||||
|
|
||||||
destroyAllConnections();
|
destroyAllConnections();
|
||||||
if (_connectionOptions->proxy.type == ProxyData::Type::Mtproto) {
|
if (_connectionOptions->proxy.type == ProxyData::Type::Mtproto) {
|
||||||
appendTestConnection(
|
// host, port, secret for mtproto proxy are taken from proxy.
|
||||||
DcOptions::Variants::Tcp,
|
appendTestConnection(DcOptions::Variants::Tcp, {}, 0, {});
|
||||||
_connectionOptions->proxy.host,
|
|
||||||
_connectionOptions->proxy.port,
|
|
||||||
ProtocolSecretFromPassword(_connectionOptions->proxy.password));
|
|
||||||
} else {
|
} else {
|
||||||
using Variants = DcOptions::Variants;
|
using Variants = DcOptions::Variants;
|
||||||
const auto special = (_dcType == DcType::Temporary);
|
const auto special = (_dcType == DcType::Temporary);
|
||||||
|
@ -1252,11 +1231,11 @@ void ConnectionPrivate::onReceivedSome() {
|
||||||
_oldConnectionTimer.callOnce(kMarkConnectionOldTimeout);
|
_oldConnectionTimer.callOnce(kMarkConnectionOldTimeout);
|
||||||
_waitForReceivedTimer.cancel();
|
_waitForReceivedTimer.cancel();
|
||||||
if (firstSentAt > 0) {
|
if (firstSentAt > 0) {
|
||||||
int32 ms = getms(true) - firstSentAt;
|
const auto ms = getms(true) - firstSentAt;
|
||||||
DEBUG_LOG(("MTP Info: response in %1ms, _waitForReceived: %2ms").arg(ms).arg(_waitForReceived));
|
DEBUG_LOG(("MTP Info: response in %1ms, _waitForReceived: %2ms").arg(ms).arg(_waitForReceived));
|
||||||
|
|
||||||
if (ms > 0 && ms * 2 < int32(_waitForReceived)) {
|
if (ms > 0 && ms * 2 < _waitForReceived) {
|
||||||
_waitForReceived = qMax(ms * 2, int32(kMinReceiveTimeout));
|
_waitForReceived = qMax(ms * 2, kMinReceiveTimeout);
|
||||||
}
|
}
|
||||||
firstSentAt = -1;
|
firstSentAt = -1;
|
||||||
}
|
}
|
||||||
|
@ -1307,20 +1286,24 @@ void ConnectionPrivate::waitReceivedFailed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_LOG(("MTP Info: immediate restart!"));
|
DEBUG_LOG(("MTP Info: immediate restart!"));
|
||||||
InvokeQueued(this, [this] { connectToServer(); });
|
InvokeQueued(this, [=] { connectToServer(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::waitConnectedFailed() {
|
void ConnectionPrivate::waitConnectedFailed() {
|
||||||
DEBUG_LOG(("MTP Info: can't connect in %1ms").arg(_waitForConnected));
|
DEBUG_LOG(("MTP Info: can't connect in %1ms").arg(_waitForConnected));
|
||||||
if (_waitForConnected < kMaxConnectedTimeout) {
|
auto maxTimeout = kMaxConnectedTimeout;
|
||||||
_waitForConnected *= 2;
|
for (const auto &connection : _testConnections) {
|
||||||
|
accumulate_max(maxTimeout, connection.data->fullConnectTimeout());
|
||||||
|
}
|
||||||
|
if (_waitForConnected < maxTimeout) {
|
||||||
|
_waitForConnected = std::min(maxTimeout, 2 * _waitForConnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
doDisconnect();
|
doDisconnect();
|
||||||
restarted = true;
|
restarted = true;
|
||||||
|
|
||||||
DEBUG_LOG(("MTP Info: immediate restart!"));
|
DEBUG_LOG(("MTP Info: immediate restart!"));
|
||||||
InvokeQueued(this, [this] { connectToServer(); });
|
InvokeQueued(this, [=] { connectToServer(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::waitBetterFailed() {
|
void ConnectionPrivate::waitBetterFailed() {
|
||||||
|
@ -1351,8 +1334,15 @@ void ConnectionPrivate::finishAndDestroy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::requestCDNConfig() {
|
void ConnectionPrivate::requestCDNConfig() {
|
||||||
connect(_instance, SIGNAL(cdnConfigLoaded()), this, SLOT(onCDNConfigLoaded()), Qt::UniqueConnection);
|
connect(
|
||||||
InvokeQueued(_instance, [instance = _instance] { instance->requestCDNConfig(); });
|
_instance,
|
||||||
|
SIGNAL(cdnConfigLoaded()),
|
||||||
|
this,
|
||||||
|
SLOT(onCDNConfigLoaded()),
|
||||||
|
Qt::UniqueConnection);
|
||||||
|
InvokeQueued(_instance, [instance = _instance] {
|
||||||
|
instance->requestCDNConfig();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionPrivate::handleReceived() {
|
void ConnectionPrivate::handleReceived() {
|
||||||
|
@ -2038,9 +2028,9 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
|
||||||
// An error could be some RPC_CALL_FAIL or other error inside
|
// An error could be some RPC_CALL_FAIL or other error inside
|
||||||
// the initConnection, so we're not sure yet that it was inited.
|
// the initConnection, so we're not sure yet that it was inited.
|
||||||
// Wait till a good response is received.
|
// Wait till a good response is received.
|
||||||
if (!sessionData->layerWasInited()) {
|
if (!_connectionOptions->inited) {
|
||||||
sessionData->setLayerWasInited(true);
|
_connectionOptions->inited = true;
|
||||||
sessionData->owner()->notifyLayerInited(true);
|
sessionData->notifyConnectionInited(*_connectionOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2436,10 +2426,8 @@ void ConnectionPrivate::onDisconnected(
|
||||||
removeTestConnection(connection);
|
removeTestConnection(connection);
|
||||||
|
|
||||||
if (_testConnections.empty()) {
|
if (_testConnections.empty()) {
|
||||||
if (!_connection || _connection == connection.get()) {
|
destroyAllConnections();
|
||||||
destroyAllConnections();
|
restart();
|
||||||
restart();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
confirmBestConnection();
|
confirmBestConnection();
|
||||||
}
|
}
|
||||||
|
@ -2584,7 +2572,22 @@ void ConnectionPrivate::pqAnswered() {
|
||||||
return restart();
|
return restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto p_q_inner = MTP_p_q_inner_data(res_pq_data.vpq, MTP_bytes(std::move(p)), MTP_bytes(std::move(q)), _authKeyData->nonce, _authKeyData->server_nonce, _authKeyData->new_nonce);
|
// #TODO checked key creation
|
||||||
|
//auto p_q_inner = MTP_p_q_inner_data_dc(
|
||||||
|
// res_pq_data.vpq,
|
||||||
|
// MTP_bytes(std::move(p)),
|
||||||
|
// MTP_bytes(std::move(q)),
|
||||||
|
// _authKeyData->nonce,
|
||||||
|
// _authKeyData->server_nonce,
|
||||||
|
// _authKeyData->new_nonce,
|
||||||
|
// MTP_int(getProtocolDcId()));
|
||||||
|
auto p_q_inner = MTP_p_q_inner_data(
|
||||||
|
res_pq_data.vpq,
|
||||||
|
MTP_bytes(std::move(p)),
|
||||||
|
MTP_bytes(std::move(q)),
|
||||||
|
_authKeyData->nonce,
|
||||||
|
_authKeyData->server_nonce,
|
||||||
|
_authKeyData->new_nonce);
|
||||||
auto dhEncString = encryptPQInnerRSA(p_q_inner, rsaKey);
|
auto dhEncString = encryptPQInnerRSA(p_q_inner, rsaKey);
|
||||||
if (dhEncString.empty()) {
|
if (dhEncString.empty()) {
|
||||||
return restart();
|
return restart();
|
||||||
|
@ -2600,6 +2603,9 @@ void ConnectionPrivate::pqAnswered() {
|
||||||
req_DH_params.vnonce = _authKeyData->nonce;
|
req_DH_params.vnonce = _authKeyData->nonce;
|
||||||
req_DH_params.vserver_nonce = _authKeyData->server_nonce;
|
req_DH_params.vserver_nonce = _authKeyData->server_nonce;
|
||||||
req_DH_params.vpublic_key_fingerprint = MTP_long(rsaKey.getFingerPrint());
|
req_DH_params.vpublic_key_fingerprint = MTP_long(rsaKey.getFingerPrint());
|
||||||
|
// #TODO checked key creation
|
||||||
|
//req_DH_params.vp = p_q_inner.c_p_q_inner_data_dc().vp;
|
||||||
|
//req_DH_params.vq = p_q_inner.c_p_q_inner_data_dc().vq;
|
||||||
req_DH_params.vp = p_q_inner.c_p_q_inner_data().vp;
|
req_DH_params.vp = p_q_inner.c_p_q_inner_data().vp;
|
||||||
req_DH_params.vq = p_q_inner.c_p_q_inner_data().vq;
|
req_DH_params.vq = p_q_inner.c_p_q_inner_data().vq;
|
||||||
req_DH_params.vencrypted_data = MTP_bytes(dhEncString);
|
req_DH_params.vencrypted_data = MTP_bytes(dhEncString);
|
||||||
|
@ -2986,19 +2992,13 @@ void ConnectionPrivate::clearAuthKeyData() {
|
||||||
void ConnectionPrivate::onError(
|
void ConnectionPrivate::onError(
|
||||||
not_null<AbstractConnection*> connection,
|
not_null<AbstractConnection*> connection,
|
||||||
qint32 errorCode) {
|
qint32 errorCode) {
|
||||||
if (_connection) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorCode == -429) {
|
if (errorCode == -429) {
|
||||||
LOG(("Protocol Error: -429 flood code returned!"));
|
LOG(("Protocol Error: -429 flood code returned!"));
|
||||||
}
|
}
|
||||||
removeTestConnection(connection);
|
removeTestConnection(connection);
|
||||||
|
|
||||||
if (_testConnections.empty()) {
|
if (_testConnections.empty()) {
|
||||||
if (!_connection || _connection == connection.get()) {
|
handleError(errorCode);
|
||||||
handleError(errorCode);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
confirmBestConnection();
|
confirmBestConnection();
|
||||||
}
|
}
|
||||||
|
@ -3240,8 +3240,4 @@ std::vector<gsl::byte> CreateAuthKey(base::const_byte_span firstBytes, base::con
|
||||||
return internal::CreateAuthKey(firstBytes, randomBytes, primeBytes);
|
return internal::CreateAuthKey(firstBytes, randomBytes, primeBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector ProtocolSecretFromPassword(const QString &password) {
|
|
||||||
return internal::ProtocolSecretFromPassword(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace MTP
|
} // namespace MTP
|
||||||
|
|
|
@ -26,8 +26,6 @@ struct ModExpFirst {
|
||||||
ModExpFirst CreateModExp(int g, base::const_byte_span primeBytes, base::const_byte_span randomSeed);
|
ModExpFirst CreateModExp(int g, base::const_byte_span primeBytes, base::const_byte_span randomSeed);
|
||||||
std::vector<gsl::byte> CreateAuthKey(base::const_byte_span firstBytes, base::const_byte_span randomBytes, base::const_byte_span primeBytes);
|
std::vector<gsl::byte> CreateAuthKey(base::const_byte_span firstBytes, base::const_byte_span randomBytes, base::const_byte_span primeBytes);
|
||||||
|
|
||||||
bytes::vector ProtocolSecretFromPassword(const QString &password);
|
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
class AbstractConnection;
|
class AbstractConnection;
|
||||||
|
@ -169,6 +167,7 @@ private:
|
||||||
void destroyAllConnections();
|
void destroyAllConnections();
|
||||||
void confirmBestConnection();
|
void confirmBestConnection();
|
||||||
void removeTestConnection(not_null<AbstractConnection*> connection);
|
void removeTestConnection(not_null<AbstractConnection*> connection);
|
||||||
|
int16 getProtocolDcId() const;
|
||||||
|
|
||||||
mtpMsgId placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req);
|
mtpMsgId placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req);
|
||||||
mtpMsgId prepareToSend(mtpRequest &request, mtpMsgId currentLastId);
|
mtpMsgId prepareToSend(mtpRequest &request, mtpMsgId currentLastId);
|
||||||
|
@ -214,7 +213,7 @@ private:
|
||||||
template <typename TResponse>
|
template <typename TResponse>
|
||||||
bool readResponseNotSecure(TResponse &response);
|
bool readResponseNotSecure(TResponse &response);
|
||||||
|
|
||||||
Instance *_instance = nullptr;
|
not_null<Instance*> _instance;
|
||||||
DcType _dcType = DcType::Regular;
|
DcType _dcType = DcType::Regular;
|
||||||
|
|
||||||
mutable QReadWriteLock stateConnMutex;
|
mutable QReadWriteLock stateConnMutex;
|
||||||
|
@ -239,8 +238,8 @@ private:
|
||||||
base::Timer _waitForConnectedTimer;
|
base::Timer _waitForConnectedTimer;
|
||||||
base::Timer _waitForReceivedTimer;
|
base::Timer _waitForReceivedTimer;
|
||||||
base::Timer _waitForBetterTimer;
|
base::Timer _waitForBetterTimer;
|
||||||
uint32 _waitForReceived = 0;
|
TimeMs _waitForReceived = 0;
|
||||||
uint32 _waitForConnected = 0;
|
TimeMs _waitForConnected = 0;
|
||||||
TimeMs firstSentAt = -1;
|
TimeMs firstSentAt = -1;
|
||||||
|
|
||||||
QVector<MTPlong> ackRequestData, resendRequestData;
|
QVector<MTPlong> ackRequestData, resendRequestData;
|
||||||
|
|
|
@ -9,10 +9,43 @@ 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_resolving.h"
|
||||||
#include "mtproto/session.h"
|
#include "mtproto/session.h"
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bytes::vector ProtocolSecretFromPassword(const QString &password) {
|
||||||
|
const auto size = password.size();
|
||||||
|
if (size % 2) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto length = size / 2;
|
||||||
|
const auto fromHex = [](QChar ch) -> int {
|
||||||
|
const auto code = int(ch.unicode());
|
||||||
|
if (code >= '0' && code <= '9') {
|
||||||
|
return (code - '0');
|
||||||
|
} else if (code >= 'A' && code <= 'F') {
|
||||||
|
return 10 + (code - 'A');
|
||||||
|
} else if (ch >= 'a' && ch <= 'f') {
|
||||||
|
return 10 + (code - 'a');
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
auto result = bytes::vector(length);
|
||||||
|
for (auto i = 0; i != length; ++i) {
|
||||||
|
const auto high = fromHex(password[2 * i]);
|
||||||
|
const auto low = fromHex(password[2 * i + 1]);
|
||||||
|
if (high < 0 || low < 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
result[i] = static_cast<gsl::byte>(high * 16 + low);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
ConnectionPointer::ConnectionPointer() = default;
|
ConnectionPointer::ConnectionPointer() = default;
|
||||||
|
|
||||||
|
@ -122,19 +155,39 @@ MTPResPQ AbstractConnection::readPQFakeReply(const mtpBuffer &buffer) {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractConnection::AbstractConnection(QThread *thread) {
|
AbstractConnection::AbstractConnection(
|
||||||
|
QThread *thread,
|
||||||
|
const ProxyData &proxy)
|
||||||
|
: _proxy(proxy) {
|
||||||
moveToThread(thread);
|
moveToThread(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionPointer AbstractConnection::create(
|
ConnectionPointer AbstractConnection::Create(
|
||||||
|
not_null<Instance*> instance,
|
||||||
DcOptions::Variants::Protocol protocol,
|
DcOptions::Variants::Protocol protocol,
|
||||||
QThread *thread) {
|
QThread *thread,
|
||||||
if (protocol == DcOptions::Variants::Tcp) {
|
const ProxyData &proxy) {
|
||||||
return ConnectionPointer(new TcpConnection(thread));
|
auto result = [&] {
|
||||||
} else {
|
if (protocol == DcOptions::Variants::Tcp) {
|
||||||
return ConnectionPointer(new HttpConnection(thread));
|
return ConnectionPointer::New<TcpConnection>(thread, proxy);
|
||||||
|
} else {
|
||||||
|
return ConnectionPointer::New<HttpConnection>(thread, proxy);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
if (proxy.tryCustomResolve()) {
|
||||||
|
return ConnectionPointer::New<ResolvingConnection>(
|
||||||
|
instance,
|
||||||
|
thread,
|
||||||
|
proxy,
|
||||||
|
std::move(result));
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
bytes::vector ProtocolSecretFromPassword(const QString &password) {
|
||||||
|
return internal::ProtocolSecretFromPassword(password);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace MTP
|
} // namespace MTP
|
||||||
|
|
|
@ -11,6 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/bytes.h"
|
#include "base/bytes.h"
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
|
||||||
|
bytes::vector ProtocolSecretFromPassword(const QString &password);
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
struct ConnectionOptions;
|
struct ConnectionOptions;
|
||||||
|
@ -21,10 +24,16 @@ class ConnectionPointer {
|
||||||
public:
|
public:
|
||||||
ConnectionPointer();
|
ConnectionPointer();
|
||||||
ConnectionPointer(std::nullptr_t);
|
ConnectionPointer(std::nullptr_t);
|
||||||
explicit ConnectionPointer(AbstractConnection *value);
|
|
||||||
ConnectionPointer(ConnectionPointer &&other);
|
ConnectionPointer(ConnectionPointer &&other);
|
||||||
ConnectionPointer &operator=(ConnectionPointer &&other);
|
ConnectionPointer &operator=(ConnectionPointer &&other);
|
||||||
|
|
||||||
|
template <typename ConnectionType, typename ...Args>
|
||||||
|
static ConnectionPointer New(Args &&...args) {
|
||||||
|
return ConnectionPointer(new ConnectionType(
|
||||||
|
std::forward<Args>(args)...
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
AbstractConnection *get() const;
|
AbstractConnection *get() const;
|
||||||
void reset(AbstractConnection *value = nullptr);
|
void reset(AbstractConnection *value = nullptr);
|
||||||
operator AbstractConnection*() const;
|
operator AbstractConnection*() const;
|
||||||
|
@ -35,6 +44,8 @@ public:
|
||||||
~ConnectionPointer();
|
~ConnectionPointer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
explicit ConnectionPointer(AbstractConnection *value);
|
||||||
|
|
||||||
AbstractConnection *_value = nullptr;
|
AbstractConnection *_value = nullptr;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -43,22 +54,24 @@ class AbstractConnection : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AbstractConnection(QThread *thread);
|
AbstractConnection(
|
||||||
|
QThread *thread,
|
||||||
|
const ProxyData &proxy);
|
||||||
AbstractConnection(const AbstractConnection &other) = delete;
|
AbstractConnection(const AbstractConnection &other) = delete;
|
||||||
AbstractConnection &operator=(const AbstractConnection &other) = delete;
|
AbstractConnection &operator=(const AbstractConnection &other) = delete;
|
||||||
virtual ~AbstractConnection() = 0;
|
virtual ~AbstractConnection() = 0;
|
||||||
|
|
||||||
// virtual constructor
|
// virtual constructor
|
||||||
static ConnectionPointer create(
|
static ConnectionPointer Create(
|
||||||
|
not_null<Instance*> instance,
|
||||||
DcOptions::Variants::Protocol protocol,
|
DcOptions::Variants::Protocol protocol,
|
||||||
QThread *thread);
|
QThread *thread,
|
||||||
|
const ProxyData &proxy);
|
||||||
|
|
||||||
void setSentEncrypted() {
|
virtual ConnectionPointer clone(const ProxyData &proxy) = 0;
|
||||||
_sentEncrypted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void setProxyOverride(const ProxyData &proxy) = 0;
|
|
||||||
virtual TimeMs pingTime() const = 0;
|
virtual TimeMs pingTime() const = 0;
|
||||||
|
virtual TimeMs fullConnectTimeout() const = 0;
|
||||||
virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32
|
virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32
|
||||||
virtual void disconnectFromServer() = 0;
|
virtual void disconnectFromServer() = 0;
|
||||||
virtual void connectToServer(
|
virtual void connectToServer(
|
||||||
|
@ -79,6 +92,10 @@ public:
|
||||||
virtual QString transport() const = 0;
|
virtual QString transport() const = 0;
|
||||||
virtual QString tag() const = 0;
|
virtual QString tag() const = 0;
|
||||||
|
|
||||||
|
void setSentEncrypted() {
|
||||||
|
_sentEncrypted = true;
|
||||||
|
}
|
||||||
|
|
||||||
using BuffersQueue = std::deque<mtpBuffer>;
|
using BuffersQueue = std::deque<mtpBuffer>;
|
||||||
BuffersQueue &received() {
|
BuffersQueue &received() {
|
||||||
return _receivedQueue;
|
return _receivedQueue;
|
||||||
|
@ -100,6 +117,7 @@ protected:
|
||||||
BuffersQueue _receivedQueue; // list of received packets, not processed yet
|
BuffersQueue _receivedQueue; // list of received packets, not processed yet
|
||||||
bool _sentEncrypted = false;
|
bool _sentEncrypted = false;
|
||||||
int _pingTime = 0;
|
int _pingTime = 0;
|
||||||
|
ProxyData _proxy;
|
||||||
|
|
||||||
// first we always send fake MTPReq_pq to see if connection works at all
|
// first we always send fake MTPReq_pq to see if connection works at all
|
||||||
// we send them simultaneously through TCP/HTTP/IPv4/IPv6 to choose the working one
|
// we send them simultaneously through TCP/HTTP/IPv4/IPv6 to choose the working one
|
||||||
|
|
|
@ -14,17 +14,19 @@ namespace internal {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kForceHttpPort = 80;
|
constexpr auto kForceHttpPort = 80;
|
||||||
|
constexpr auto kFullConnectionTimeout = TimeMs(8000);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
HttpConnection::HttpConnection(QThread *thread)
|
HttpConnection::HttpConnection(QThread *thread, const ProxyData &proxy)
|
||||||
: AbstractConnection(thread)
|
: AbstractConnection(thread, proxy)
|
||||||
, _checkNonce(rand_value<MTPint128>()) {
|
, _checkNonce(rand_value<MTPint128>()) {
|
||||||
_manager.moveToThread(thread);
|
_manager.moveToThread(thread);
|
||||||
|
_manager.setProxy(ToNetworkProxy(proxy));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::setProxyOverride(const ProxyData &proxy) {
|
ConnectionPointer HttpConnection::clone(const ProxyData &proxy) {
|
||||||
_manager.setProxy(ToNetworkProxy(proxy));
|
return ConnectionPointer::New<HttpConnection>(thread(), proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::sendData(mtpBuffer &buffer) {
|
void HttpConnection::sendData(mtpBuffer &buffer) {
|
||||||
|
@ -43,7 +45,7 @@ void HttpConnection::sendData(mtpBuffer &buffer) {
|
||||||
request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize));
|
request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize));
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded")));
|
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded")));
|
||||||
|
|
||||||
TCP_LOG(("HTTP Info: sending %1 len request %2").arg(requestSize).arg(Logs::mb(&buffer[2], requestSize).str()));
|
TCP_LOG(("HTTP Info: sending %1 len request").arg(requestSize));
|
||||||
_requests.insert(_manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
|
_requests.insert(_manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,6 +198,10 @@ TimeMs HttpConnection::pingTime() const {
|
||||||
return isConnected() ? _pingTime : TimeMs(0);
|
return isConnected() ? _pingTime : TimeMs(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeMs HttpConnection::fullConnectTimeout() const {
|
||||||
|
return kFullConnectionTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
bool HttpConnection::usingHttpWait() {
|
bool HttpConnection::usingHttpWait() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,12 @@ namespace internal {
|
||||||
|
|
||||||
class HttpConnection : public AbstractConnection {
|
class HttpConnection : public AbstractConnection {
|
||||||
public:
|
public:
|
||||||
HttpConnection(QThread *thread);
|
HttpConnection(QThread *thread, const ProxyData &proxy);
|
||||||
|
|
||||||
|
ConnectionPointer clone(const ProxyData &proxy) override;
|
||||||
|
|
||||||
void setProxyOverride(const ProxyData &proxy) override;
|
|
||||||
TimeMs pingTime() const override;
|
TimeMs pingTime() const override;
|
||||||
|
TimeMs fullConnectTimeout() const override;
|
||||||
void sendData(mtpBuffer &buffer) override;
|
void sendData(mtpBuffer &buffer) override;
|
||||||
void disconnectFromServer() override;
|
void disconnectFromServer() override;
|
||||||
void connectToServer(
|
void connectToServer(
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
/*
|
||||||
|
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/connection_resolving.h"
|
||||||
|
|
||||||
|
namespace MTP {
|
||||||
|
namespace internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kOneConnectionTimeout = 4000;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ResolvingConnection::ResolvingConnection(
|
||||||
|
not_null<Instance*> instance,
|
||||||
|
QThread *thread,
|
||||||
|
const ProxyData &proxy,
|
||||||
|
ConnectionPointer &&child)
|
||||||
|
: AbstractConnection(thread, proxy)
|
||||||
|
, _instance(instance)
|
||||||
|
, _timeoutTimer([=] { handleError(); }) {
|
||||||
|
setChild(std::move(child));
|
||||||
|
if (proxy.resolvedExpireAt < getms(true)) {
|
||||||
|
const auto host = proxy.host;
|
||||||
|
connect(
|
||||||
|
instance,
|
||||||
|
&Instance::proxyDomainResolved,
|
||||||
|
this,
|
||||||
|
&ResolvingConnection::domainResolved,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
InvokeQueued(instance, [=] {
|
||||||
|
instance->resolveProxyDomain(host);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!proxy.resolvedIPs.empty()) {
|
||||||
|
refreshChild();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionPointer ResolvingConnection::clone(const ProxyData &proxy) {
|
||||||
|
Unexpected("ResolvingConnection::clone call.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::setChild(ConnectionPointer &&child) {
|
||||||
|
_child = std::move(child);
|
||||||
|
connect(
|
||||||
|
_child,
|
||||||
|
&AbstractConnection::receivedData,
|
||||||
|
this,
|
||||||
|
&ResolvingConnection::handleReceivedData);
|
||||||
|
connect(
|
||||||
|
_child,
|
||||||
|
&AbstractConnection::receivedSome,
|
||||||
|
this,
|
||||||
|
&ResolvingConnection::receivedSome);
|
||||||
|
connect(
|
||||||
|
_child,
|
||||||
|
&AbstractConnection::error,
|
||||||
|
this,
|
||||||
|
&ResolvingConnection::handleError);
|
||||||
|
connect(_child,
|
||||||
|
&AbstractConnection::connected,
|
||||||
|
this,
|
||||||
|
&ResolvingConnection::handleConnected);
|
||||||
|
connect(_child,
|
||||||
|
&AbstractConnection::disconnected,
|
||||||
|
this,
|
||||||
|
&ResolvingConnection::handleDisconnected);
|
||||||
|
if (_protocolDcId) {
|
||||||
|
_child->connectToServer(
|
||||||
|
_address,
|
||||||
|
_port,
|
||||||
|
_protocolSecret,
|
||||||
|
_protocolDcId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::domainResolved(
|
||||||
|
const QString &host,
|
||||||
|
const QStringList &ips,
|
||||||
|
qint64 expireAt) {
|
||||||
|
if (host != _proxy.host || !_child) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_proxy.resolvedExpireAt = expireAt;
|
||||||
|
|
||||||
|
auto index = 0;
|
||||||
|
for (const auto &ip : ips) {
|
||||||
|
if (index >= _proxy.resolvedIPs.size()) {
|
||||||
|
_proxy.resolvedIPs.push_back(ip);
|
||||||
|
} else if (_proxy.resolvedIPs[index] != ip) {
|
||||||
|
_proxy.resolvedIPs[index] = ip;
|
||||||
|
if (_ipIndex >= index) {
|
||||||
|
_ipIndex = index - 1;
|
||||||
|
refreshChild();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
if (index < _proxy.resolvedIPs.size()) {
|
||||||
|
_proxy.resolvedIPs.resize(index);
|
||||||
|
if (_ipIndex >= index) {
|
||||||
|
emitError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_ipIndex < 0) {
|
||||||
|
refreshChild();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::refreshChild() {
|
||||||
|
if (!_child) {
|
||||||
|
return;
|
||||||
|
} else if (++_ipIndex >= _proxy.resolvedIPs.size()) {
|
||||||
|
emitError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setChild(_child->clone(ToDirectIpProxy(_proxy, _ipIndex)));
|
||||||
|
_timeoutTimer.callOnce(kOneConnectionTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::emitError() {
|
||||||
|
_ipIndex = -1;
|
||||||
|
_child = nullptr;
|
||||||
|
emit error(kErrorCodeOther);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::handleError() {
|
||||||
|
if (_connected) {
|
||||||
|
emitError();
|
||||||
|
} else if (!_proxy.resolvedIPs.empty()) {
|
||||||
|
refreshChild();
|
||||||
|
} else {
|
||||||
|
// Wait for the domain to be resolved.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::handleDisconnected() {
|
||||||
|
if (_connected) {
|
||||||
|
emit disconnected();
|
||||||
|
} else {
|
||||||
|
handleError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::handleReceivedData() {
|
||||||
|
auto &my = received();
|
||||||
|
auto &his = _child->received();
|
||||||
|
for (auto &item : his) {
|
||||||
|
my.push_back(std::move(item));
|
||||||
|
}
|
||||||
|
his.clear();
|
||||||
|
emit receivedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::handleConnected() {
|
||||||
|
_connected = true;
|
||||||
|
_timeoutTimer.cancel();
|
||||||
|
if (_ipIndex >= 0) {
|
||||||
|
const auto host = _proxy.host;
|
||||||
|
const auto good = _proxy.resolvedIPs[_ipIndex];
|
||||||
|
const auto instance = _instance;
|
||||||
|
InvokeQueued(_instance, [=] {
|
||||||
|
instance->setGoodProxyDomain(host, good);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
emit connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeMs ResolvingConnection::pingTime() const {
|
||||||
|
Expects(_child != nullptr);
|
||||||
|
|
||||||
|
return _child->pingTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeMs ResolvingConnection::fullConnectTimeout() const {
|
||||||
|
return kOneConnectionTimeout * std::max(_proxy.resolvedIPs.size(), 1U);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::sendData(mtpBuffer &buffer) {
|
||||||
|
Expects(_child != nullptr);
|
||||||
|
|
||||||
|
_child->sendData(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::disconnectFromServer() {
|
||||||
|
_address = QString();
|
||||||
|
_port = 0;
|
||||||
|
_protocolSecret = bytes::vector();
|
||||||
|
_protocolDcId = 0;
|
||||||
|
if (!_child) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_child->disconnectFromServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolvingConnection::connectToServer(
|
||||||
|
const QString &address,
|
||||||
|
int port,
|
||||||
|
const bytes::vector &protocolSecret,
|
||||||
|
int16 protocolDcId) {
|
||||||
|
if (!_child) {
|
||||||
|
InvokeQueued(this, [=] { emitError(); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_address = address;
|
||||||
|
_port = port;
|
||||||
|
_protocolSecret = protocolSecret;
|
||||||
|
_protocolDcId = protocolDcId;
|
||||||
|
return _child->connectToServer(
|
||||||
|
address,
|
||||||
|
port,
|
||||||
|
protocolSecret,
|
||||||
|
protocolDcId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResolvingConnection::isConnected() const {
|
||||||
|
return _child ? _child->isConnected() : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ResolvingConnection::debugState() const {
|
||||||
|
return _child ? _child->debugState() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ResolvingConnection::transport() const {
|
||||||
|
return _child ? _child->transport() : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ResolvingConnection::tag() const {
|
||||||
|
return _child ? _child->tag() : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace MTP
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
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 "mtproto/auth_key.h"
|
||||||
|
#include "mtproto/connection_abstract.h"
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
|
namespace MTP {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class ResolvingConnection : public AbstractConnection {
|
||||||
|
public:
|
||||||
|
ResolvingConnection(
|
||||||
|
not_null<Instance*> instance,
|
||||||
|
QThread *thread,
|
||||||
|
const ProxyData &proxy,
|
||||||
|
ConnectionPointer &&child);
|
||||||
|
|
||||||
|
ConnectionPointer clone(const ProxyData &proxy) override;
|
||||||
|
|
||||||
|
TimeMs pingTime() const override;
|
||||||
|
TimeMs fullConnectTimeout() const override;
|
||||||
|
void sendData(mtpBuffer &buffer) override;
|
||||||
|
void disconnectFromServer() override;
|
||||||
|
void connectToServer(
|
||||||
|
const QString &address,
|
||||||
|
int port,
|
||||||
|
const bytes::vector &protocolSecret,
|
||||||
|
int16 protocolDcId) override;
|
||||||
|
bool isConnected() const override;
|
||||||
|
|
||||||
|
int32 debugState() const override;
|
||||||
|
|
||||||
|
QString transport() const override;
|
||||||
|
QString tag() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setChild(ConnectionPointer &&child);
|
||||||
|
void refreshChild();
|
||||||
|
void emitError();
|
||||||
|
|
||||||
|
void domainResolved(
|
||||||
|
const QString &host,
|
||||||
|
const QStringList &ips,
|
||||||
|
qint64 expireAt);
|
||||||
|
void handleError();
|
||||||
|
void handleConnected();
|
||||||
|
void handleDisconnected();
|
||||||
|
void handleReceivedData();
|
||||||
|
|
||||||
|
not_null<Instance*> _instance;
|
||||||
|
ConnectionPointer _child;
|
||||||
|
bool _connected = false;
|
||||||
|
int _ipIndex = -1;
|
||||||
|
QString _address;
|
||||||
|
int _port = 0;
|
||||||
|
bytes::vector _protocolSecret;
|
||||||
|
int16 _protocolDcId = 0;
|
||||||
|
base::Timer _timeoutTimer;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace MTP
|
|
@ -35,13 +35,14 @@ const auto QTcpSocket_error = ErrorSignal(&QAbstractSocket::error);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TcpConnection::TcpConnection(QThread *thread)
|
TcpConnection::TcpConnection(QThread *thread, const ProxyData &proxy)
|
||||||
: AbstractConnection(thread)
|
: AbstractConnection(thread, proxy)
|
||||||
, _currentPosition(reinterpret_cast<char*>(_shortBuffer))
|
, _currentPosition(reinterpret_cast<char*>(_shortBuffer))
|
||||||
, _checkNonce(rand_value<MTPint128>())
|
, _checkNonce(rand_value<MTPint128>())
|
||||||
, _timeout(kMinReceiveTimeout)
|
, _timeout(kMinReceiveTimeout)
|
||||||
, _timeoutTimer(thread, [=] { handleTimeout(); }) {
|
, _timeoutTimer(thread, [=] { handleTimeout(); }) {
|
||||||
_socket.moveToThread(thread);
|
_socket.moveToThread(thread);
|
||||||
|
_socket.setProxy(ToNetworkProxy(proxy));
|
||||||
connect(&_socket, QTcpSocket_error, this, &TcpConnection::socketError);
|
connect(&_socket, QTcpSocket_error, this, &TcpConnection::socketError);
|
||||||
connect(
|
connect(
|
||||||
&_socket,
|
&_socket,
|
||||||
|
@ -55,8 +56,8 @@ TcpConnection::TcpConnection(QThread *thread)
|
||||||
&TcpConnection::socketDisconnected);
|
&TcpConnection::socketDisconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpConnection::setProxyOverride(const ProxyData &proxy) {
|
ConnectionPointer TcpConnection::clone(const ProxyData &proxy) {
|
||||||
_socket.setProxy(ToNetworkProxy(proxy));
|
return ConnectionPointer::New<TcpConnection>(thread(), proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpConnection::socketRead() {
|
void TcpConnection::socketRead() {
|
||||||
|
@ -378,12 +379,18 @@ void TcpConnection::connectToServer(
|
||||||
int port,
|
int port,
|
||||||
const bytes::vector &protocolSecret,
|
const bytes::vector &protocolSecret,
|
||||||
int16 protocolDcId) {
|
int16 protocolDcId) {
|
||||||
_address = address;
|
if (_proxy.type == ProxyData::Type::Mtproto) {
|
||||||
_port = port;
|
_address = _proxy.host;
|
||||||
_protocolSecret = protocolSecret;
|
_port = _proxy.port;
|
||||||
|
_protocolSecret = ProtocolSecretFromPassword(_proxy.password);
|
||||||
|
} else {
|
||||||
|
_address = address;
|
||||||
|
_port = port;
|
||||||
|
_protocolSecret = protocolSecret;
|
||||||
|
}
|
||||||
_protocolDcId = protocolDcId;
|
_protocolDcId = protocolDcId;
|
||||||
|
|
||||||
connect(&_socket, &QTcpSocket::readyRead, this, &TcpConnection::socketRead);
|
connect(&_socket, &QTcpSocket::readyRead, this, [=] { socketRead(); });
|
||||||
_socket.connectToHost(_address, _port);
|
_socket.connectToHost(_address, _port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,6 +398,10 @@ TimeMs TcpConnection::pingTime() const {
|
||||||
return isConnected() ? _pingTime : TimeMs(0);
|
return isConnected() ? _pingTime : TimeMs(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeMs TcpConnection::fullConnectTimeout() const {
|
||||||
|
return kMaxReceiveTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
void TcpConnection::socketPacket(const char *packet, uint32 length) {
|
void TcpConnection::socketPacket(const char *packet, uint32 length) {
|
||||||
if (_status == Status::Finished) return;
|
if (_status == Status::Finished) return;
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,14 @@ namespace internal {
|
||||||
|
|
||||||
class TcpConnection : public AbstractConnection {
|
class TcpConnection : public AbstractConnection {
|
||||||
public:
|
public:
|
||||||
TcpConnection(QThread *thread);
|
TcpConnection(
|
||||||
|
QThread *thread,
|
||||||
|
const ProxyData &proxy);
|
||||||
|
|
||||||
void setProxyOverride(const ProxyData &proxy) override;
|
ConnectionPointer clone(const ProxyData &proxy) override;
|
||||||
|
|
||||||
TimeMs pingTime() const override;
|
TimeMs pingTime() const override;
|
||||||
|
TimeMs fullConnectTimeout() const override;
|
||||||
void sendData(mtpBuffer &buffer) override;
|
void sendData(mtpBuffer &buffer) override;
|
||||||
void disconnectFromServer() override;
|
void disconnectFromServer() override;
|
||||||
void connectToServer(
|
void connectToServer(
|
||||||
|
|
|
@ -28,8 +28,7 @@ public:
|
||||||
|
|
||||||
bool connectionInited() const {
|
bool connectionInited() const {
|
||||||
QMutexLocker lock(&initLock);
|
QMutexLocker lock(&initLock);
|
||||||
bool res = _connectionInited;
|
return _connectionInited;
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
void setConnectionInited(bool connectionInited = true) {
|
void setConnectionInited(bool connectionInited = true) {
|
||||||
QMutexLocker lock(&initLock);
|
QMutexLocker lock(&initLock);
|
||||||
|
@ -38,7 +37,7 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void authKeyCreated();
|
void authKeyCreated();
|
||||||
void layerWasInited(bool was);
|
void connectionWasInited();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void authKeyWrite();
|
void authKeyWrite();
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "mtproto/dc_options.h"
|
#include "mtproto/dc_options.h"
|
||||||
#include "mtproto/dcenter.h"
|
#include "mtproto/dcenter.h"
|
||||||
#include "mtproto/config_loader.h"
|
#include "mtproto/config_loader.h"
|
||||||
|
#include "mtproto/special_config_request.h"
|
||||||
#include "mtproto/connection.h"
|
#include "mtproto/connection.h"
|
||||||
#include "mtproto/sender.h"
|
#include "mtproto/sender.h"
|
||||||
#include "mtproto/rsa_public_key.h"
|
#include "mtproto/rsa_public_key.h"
|
||||||
|
@ -38,6 +39,8 @@ public:
|
||||||
void start(Config &&config);
|
void start(Config &&config);
|
||||||
|
|
||||||
void setCurrentProxy(const ProxyData &proxy, bool enabled);
|
void setCurrentProxy(const ProxyData &proxy, bool enabled);
|
||||||
|
void resolveProxyDomain(const QString &host);
|
||||||
|
void setGoodProxyDomain(const QString &host, const QString &ip);
|
||||||
void suggestMainDcId(DcId mainDcId);
|
void suggestMainDcId(DcId mainDcId);
|
||||||
void setMainDcId(DcId mainDcId);
|
void setMainDcId(DcId mainDcId);
|
||||||
DcId mainDcId() const;
|
DcId mainDcId() const;
|
||||||
|
@ -126,6 +129,11 @@ private:
|
||||||
bool exportFail(const RPCError &error, mtpRequestId requestId);
|
bool exportFail(const RPCError &error, mtpRequestId requestId);
|
||||||
bool onErrorDefault(mtpRequestId requestId, const RPCError &error);
|
bool onErrorDefault(mtpRequestId requestId, const RPCError &error);
|
||||||
|
|
||||||
|
void applyDomainIps(
|
||||||
|
const QString &host,
|
||||||
|
const QStringList &ips,
|
||||||
|
TimeMs expireAt);
|
||||||
|
|
||||||
void logoutGuestDcs();
|
void logoutGuestDcs();
|
||||||
bool logoutGuestDone(mtpRequestId requestId);
|
bool logoutGuestDone(mtpRequestId requestId);
|
||||||
|
|
||||||
|
@ -161,6 +169,7 @@ private:
|
||||||
base::set_of_unique_ptr<internal::Connection> _quittingConnections;
|
base::set_of_unique_ptr<internal::Connection> _quittingConnections;
|
||||||
|
|
||||||
std::unique_ptr<internal::ConfigLoader> _configLoader;
|
std::unique_ptr<internal::ConfigLoader> _configLoader;
|
||||||
|
std::unique_ptr<DomainResolver> _domainResolver;
|
||||||
QString _userPhone;
|
QString _userPhone;
|
||||||
mtpRequestId _cdnConfigLoadRequestId = 0;
|
mtpRequestId _cdnConfigLoadRequestId = 0;
|
||||||
TimeMs _lastConfigLoadedTime = 0;
|
TimeMs _lastConfigLoadedTime = 0;
|
||||||
|
@ -203,7 +212,11 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Instance::Private::Private(not_null<Instance*> instance, not_null<DcOptions*> options, Instance::Mode mode) : Sender()
|
Instance::Private::Private(
|
||||||
|
not_null<Instance*> instance,
|
||||||
|
not_null<DcOptions*> options,
|
||||||
|
Instance::Mode mode)
|
||||||
|
: Sender()
|
||||||
, _instance(instance)
|
, _instance(instance)
|
||||||
, _dcOptions(options)
|
, _dcOptions(options)
|
||||||
, _mode(mode) {
|
, _mode(mode) {
|
||||||
|
@ -283,6 +296,85 @@ void Instance::Private::setCurrentProxy(
|
||||||
Global::RefConnectionTypeChanged().notify();
|
Global::RefConnectionTypeChanged().notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Instance::Private::resolveProxyDomain(const QString &host) {
|
||||||
|
if (!_domainResolver) {
|
||||||
|
_domainResolver = std::make_unique<DomainResolver>([=](
|
||||||
|
const QString &host,
|
||||||
|
const QStringList &ips,
|
||||||
|
TimeMs expireAt) {
|
||||||
|
applyDomainIps(host, ips, expireAt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_domainResolver->resolve(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Instance::Private::applyDomainIps(
|
||||||
|
const QString &host,
|
||||||
|
const QStringList &ips,
|
||||||
|
TimeMs expireAt) {
|
||||||
|
const auto applyToProxy = [&](ProxyData &proxy) {
|
||||||
|
if (!proxy.tryCustomResolve() || proxy.host != host) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
proxy.resolvedExpireAt = expireAt;
|
||||||
|
auto copy = ips;
|
||||||
|
auto ¤t = proxy.resolvedIPs;
|
||||||
|
const auto i = ranges::remove_if(current, [&](const QString &ip) {
|
||||||
|
const auto index = copy.indexOf(ip);
|
||||||
|
if (index < 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
copy.removeAt(index);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (i == end(current) && copy.isEmpty()) {
|
||||||
|
// Even if the proxy was changed already, we still want
|
||||||
|
// to refreshOptions in all sessions across all instances.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
current.erase(i, end(current));
|
||||||
|
for (const auto &ip : copy) {
|
||||||
|
proxy.resolvedIPs.push_back(ip);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
for (auto &proxy : Global::RefProxiesList()) {
|
||||||
|
applyToProxy(proxy);
|
||||||
|
}
|
||||||
|
if (applyToProxy(Global::RefSelectedProxy()) && Global::UseProxy()) {
|
||||||
|
for (auto &session : _sessions) {
|
||||||
|
session.second->refreshOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit _instance->proxyDomainResolved(host, ips, expireAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Instance::Private::setGoodProxyDomain(
|
||||||
|
const QString &host,
|
||||||
|
const QString &ip) {
|
||||||
|
const auto applyToProxy = [&](ProxyData &proxy) {
|
||||||
|
if (!proxy.tryCustomResolve() || proxy.host != host) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto ¤t = proxy.resolvedIPs;
|
||||||
|
auto i = ranges::find(current, ip);
|
||||||
|
if (i == end(current) || i == begin(current)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (i != begin(current)) {
|
||||||
|
const auto j = i--;
|
||||||
|
std::swap(*i, *j);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
for (auto &proxy : Global::RefProxiesList()) {
|
||||||
|
applyToProxy(proxy);
|
||||||
|
}
|
||||||
|
if (applyToProxy(Global::RefSelectedProxy()) && Global::UseProxy()) {
|
||||||
|
Sandbox::refreshGlobalProxy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Instance::Private::suggestMainDcId(DcId mainDcId) {
|
void Instance::Private::suggestMainDcId(DcId mainDcId) {
|
||||||
if (_mainDcIdForced) return;
|
if (_mainDcIdForced) return;
|
||||||
setMainDcId(mainDcId);
|
setMainDcId(mainDcId);
|
||||||
|
@ -503,8 +595,11 @@ void Instance::Private::stopSession(ShiftedDcId shiftedDcId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::Private::reInitConnection(DcId dcId) {
|
void Instance::Private::reInitConnection(DcId dcId) {
|
||||||
killSession(dcId);
|
for (auto &session : _sessions) {
|
||||||
getSession(dcId)->notifyLayerInited(false);
|
if (bareDcId(session.second->getDcWithShift()) == dcId) {
|
||||||
|
session.second->reInitConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::Private::logout(
|
void Instance::Private::logout(
|
||||||
|
@ -1376,6 +1471,14 @@ void Instance::setCurrentProxy(const ProxyData &proxy, bool enabled) {
|
||||||
_private->setCurrentProxy(proxy, enabled);
|
_private->setCurrentProxy(proxy, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Instance::resolveProxyDomain(const QString &host) {
|
||||||
|
_private->resolveProxyDomain(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Instance::setGoodProxyDomain(const QString &host, const QString &ip) {
|
||||||
|
_private->setGoodProxyDomain(host, ip);
|
||||||
|
}
|
||||||
|
|
||||||
void Instance::suggestMainDcId(DcId mainDcId) {
|
void Instance::suggestMainDcId(DcId mainDcId) {
|
||||||
_private->suggestMainDcId(mainDcId);
|
_private->suggestMainDcId(mainDcId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ public:
|
||||||
Instance &operator=(const Instance &other) = delete;
|
Instance &operator=(const Instance &other) = delete;
|
||||||
|
|
||||||
void setCurrentProxy(const ProxyData &proxy, bool enabled);
|
void setCurrentProxy(const ProxyData &proxy, bool enabled);
|
||||||
|
void resolveProxyDomain(const QString &host);
|
||||||
|
void setGoodProxyDomain(const QString &host, const QString &ip);
|
||||||
void suggestMainDcId(DcId mainDcId);
|
void suggestMainDcId(DcId mainDcId);
|
||||||
void setMainDcId(DcId mainDcId);
|
void setMainDcId(DcId mainDcId);
|
||||||
DcId mainDcId() const;
|
DcId mainDcId() const;
|
||||||
|
@ -150,6 +152,10 @@ signals:
|
||||||
void cdnConfigLoaded();
|
void cdnConfigLoaded();
|
||||||
void keyDestroyed(qint32 shiftedDcId);
|
void keyDestroyed(qint32 shiftedDcId);
|
||||||
void allKeysDestroyed();
|
void allKeysDestroyed();
|
||||||
|
void proxyDomainResolved(
|
||||||
|
QString host,
|
||||||
|
QStringList ips,
|
||||||
|
qint64 expireAt);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onKeyDestroyed(qint32 shiftedDcId);
|
void onKeyDestroyed(qint32 shiftedDcId);
|
||||||
|
|
|
@ -59,6 +59,19 @@ void SessionData::setKey(const AuthKeyPtr &key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionData::notifyConnectionInited(const ConnectionOptions &options) {
|
||||||
|
QWriteLocker locker(&_lock);
|
||||||
|
if (options.cloudLangCode == _options.cloudLangCode
|
||||||
|
&& options.systemLangCode == _options.systemLangCode
|
||||||
|
&& options.proxy == _options.proxy
|
||||||
|
&& !_options.inited) {
|
||||||
|
_options.inited = true;
|
||||||
|
|
||||||
|
locker.unlock();
|
||||||
|
owner()->notifyDcConnectionInited();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SessionData::clear(Instance *instance) {
|
void SessionData::clear(Instance *instance) {
|
||||||
auto clearCallbacks = std::vector<RPCCallbackClear>();
|
auto clearCallbacks = std::vector<RPCCallbackClear>();
|
||||||
{
|
{
|
||||||
|
@ -110,7 +123,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);
|
||||||
|
|
||||||
refreshDataFields();
|
refreshOptions();
|
||||||
|
|
||||||
connect(&sender, SIGNAL(timeout()), this, SLOT(needToResumeAndSend()));
|
connect(&sender, SIGNAL(timeout()), this, SLOT(needToResumeAndSend()));
|
||||||
}
|
}
|
||||||
|
@ -133,11 +146,11 @@ void Session::createDcData() {
|
||||||
if (auto lock = ReadLockerAttempt(keyMutex())) {
|
if (auto lock = ReadLockerAttempt(keyMutex())) {
|
||||||
data.setKey(dc->getKey());
|
data.setKey(dc->getKey());
|
||||||
if (dc->connectionInited()) {
|
if (dc->connectionInited()) {
|
||||||
data.setLayerWasInited(true);
|
data.setConnectionInited();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
connect(dc.get(), SIGNAL(authKeyCreated()), this, SLOT(authKeyCreatedForDC()), Qt::QueuedConnection);
|
connect(dc.get(), SIGNAL(authKeyCreated()), this, SLOT(authKeyCreatedForDC()), Qt::QueuedConnection);
|
||||||
connect(dc.get(), SIGNAL(layerWasInited(bool)), this, SLOT(layerWasInitedForDC(bool)), Qt::QueuedConnection);
|
connect(dc.get(), SIGNAL(connectionWasInited()), this, SLOT(connectionWasInitedForDC()), Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift) {
|
void Session::registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift) {
|
||||||
|
@ -163,11 +176,11 @@ 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;
|
||||||
}
|
}
|
||||||
refreshDataFields();
|
refreshOptions();
|
||||||
emit needToRestart();
|
emit needToRestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::refreshDataFields() {
|
void Session::refreshOptions() {
|
||||||
const auto &proxy = Global::SelectedProxy();
|
const auto &proxy = Global::SelectedProxy();
|
||||||
const auto proxyType = Global::UseProxy()
|
const auto proxyType = Global::UseProxy()
|
||||||
? proxy.type
|
? proxy.type
|
||||||
|
@ -176,7 +189,7 @@ void Session::refreshDataFields() {
|
||||||
const auto useHttp = (proxyType != ProxyData::Type::Mtproto);
|
const auto useHttp = (proxyType != ProxyData::Type::Mtproto);
|
||||||
const auto useIPv4 = true;
|
const auto useIPv4 = true;
|
||||||
const auto useIPv6 = Global::TryIPv6();
|
const auto useIPv6 = Global::TryIPv6();
|
||||||
data.setConnectionOptions(ConnectionOptions(
|
data.applyConnectionOptions(ConnectionOptions(
|
||||||
_instance->systemLangCode(),
|
_instance->systemLangCode(),
|
||||||
_instance->cloudLangCode(),
|
_instance->cloudLangCode(),
|
||||||
Global::UseProxy() ? proxy : ProxyData(),
|
Global::UseProxy() ? proxy : ProxyData(),
|
||||||
|
@ -186,6 +199,12 @@ void Session::refreshDataFields() {
|
||||||
useTcp));
|
useTcp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Session::reInitConnection() {
|
||||||
|
dc->setConnectionInited(false);
|
||||||
|
data.setConnectionInited(false);
|
||||||
|
restart();
|
||||||
|
}
|
||||||
|
|
||||||
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"));
|
||||||
|
@ -548,15 +567,15 @@ void Session::notifyKeyCreated(AuthKeyPtr &&key) {
|
||||||
dc->setKey(std::move(key));
|
dc->setKey(std::move(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::layerWasInitedForDC(bool wasInited) {
|
void Session::connectionWasInitedForDC() {
|
||||||
DEBUG_LOG(("MTP Info: Session::layerWasInitedForDC slot, dcWithShift %1").arg(dcWithShift));
|
DEBUG_LOG(("MTP Info: Session::connectionWasInitedForDC slot, dcWithShift %1").arg(dcWithShift));
|
||||||
data.setLayerWasInited(wasInited);
|
data.setConnectionInited();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::notifyLayerInited(bool wasInited) {
|
void Session::notifyDcConnectionInited() {
|
||||||
DEBUG_LOG(("MTP Info: emitting MTProtoDC::layerWasInited(%1), dcWithShift %2").arg(Logs::b(wasInited)).arg(dcWithShift));
|
DEBUG_LOG(("MTP Info: emitting MTProtoDC::connectionWasInited(), dcWithShift %1").arg(dcWithShift));
|
||||||
dc->setConnectionInited(wasInited);
|
dc->setConnectionInited();
|
||||||
emit dc->layerWasInited(wasInited);
|
emit dc->connectionWasInited();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::destroyKey() {
|
void Session::destroyKey() {
|
||||||
|
|
|
@ -105,6 +105,7 @@ struct ConnectionOptions {
|
||||||
bool useIPv6 = true;
|
bool useIPv6 = true;
|
||||||
bool useHttp = true;
|
bool useHttp = true;
|
||||||
bool useTcp = true;
|
bool useTcp = true;
|
||||||
|
bool inited = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,18 +128,16 @@ public:
|
||||||
QReadLocker locker(&_lock);
|
QReadLocker locker(&_lock);
|
||||||
return _session;
|
return _session;
|
||||||
}
|
}
|
||||||
bool layerWasInited() const {
|
void setConnectionInited(bool inited = true) {
|
||||||
QReadLocker locker(&_lock);
|
|
||||||
return _layerInited;
|
|
||||||
}
|
|
||||||
void setLayerWasInited(bool was) {
|
|
||||||
QWriteLocker locker(&_lock);
|
QWriteLocker locker(&_lock);
|
||||||
_layerInited = was;
|
_options.inited = inited;
|
||||||
}
|
}
|
||||||
|
void notifyConnectionInited(const ConnectionOptions &options);
|
||||||
void setConnectionOptions(ConnectionOptions options) {
|
void applyConnectionOptions(ConnectionOptions options) {
|
||||||
QWriteLocker locker(&_lock);
|
QWriteLocker locker(&_lock);
|
||||||
|
const auto inited = _options.inited;
|
||||||
_options = options;
|
_options = options;
|
||||||
|
_options.inited = inited;
|
||||||
}
|
}
|
||||||
ConnectionOptions connectionOptions() const {
|
ConnectionOptions connectionOptions() const {
|
||||||
QReadLocker locker(&_lock);
|
QReadLocker locker(&_lock);
|
||||||
|
@ -300,6 +299,8 @@ public:
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
void restart();
|
void restart();
|
||||||
|
void refreshOptions();
|
||||||
|
void reInitConnection();
|
||||||
void stop();
|
void stop();
|
||||||
void kill();
|
void kill();
|
||||||
|
|
||||||
|
@ -310,7 +311,7 @@ public:
|
||||||
QReadWriteLock *keyMutex() const;
|
QReadWriteLock *keyMutex() const;
|
||||||
void notifyKeyCreated(AuthKeyPtr &&key);
|
void notifyKeyCreated(AuthKeyPtr &&key);
|
||||||
void destroyKey();
|
void destroyKey();
|
||||||
void notifyLayerInited(bool wasInited);
|
void notifyDcConnectionInited();
|
||||||
|
|
||||||
void ping();
|
void ping();
|
||||||
void cancel(mtpRequestId requestId, mtpMsgId msgId);
|
void cancel(mtpRequestId requestId, mtpMsgId msgId);
|
||||||
|
@ -348,7 +349,7 @@ public slots:
|
||||||
void resendAll(); // after connection restart
|
void resendAll(); // after connection restart
|
||||||
|
|
||||||
void authKeyCreatedForDC();
|
void authKeyCreatedForDC();
|
||||||
void layerWasInitedForDC(bool wasInited);
|
void connectionWasInitedForDC();
|
||||||
|
|
||||||
void tryToReceive();
|
void tryToReceive();
|
||||||
void checkRequestsByTimer();
|
void checkRequestsByTimer();
|
||||||
|
@ -361,7 +362,6 @@ 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(
|
||||||
|
|
|
@ -16,7 +16,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
struct DnsEntry {
|
||||||
|
QString data;
|
||||||
|
int64 TTL = 0;
|
||||||
|
};
|
||||||
|
|
||||||
constexpr auto kSendNextTimeout = TimeMs(1000);
|
constexpr auto kSendNextTimeout = TimeMs(1000);
|
||||||
|
constexpr auto kMinTimeToLive = 10 * TimeMs(1000);
|
||||||
|
constexpr auto kMaxTimeToLive = 300 * TimeMs(1000);
|
||||||
|
|
||||||
constexpr auto kPublicKey = str_const("\
|
constexpr auto kPublicKey = str_const("\
|
||||||
-----BEGIN RSA PUBLIC KEY-----\n\
|
-----BEGIN RSA PUBLIC KEY-----\n\
|
||||||
|
@ -32,6 +39,16 @@ Y1hZCxdv6cs5UnW9+PWvS+WIbkh+GaWYxwIDAQAB\n\
|
||||||
constexpr auto kUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
constexpr auto kUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36";
|
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36";
|
||||||
|
|
||||||
|
const auto &DnsDomains() {
|
||||||
|
static auto result = std::vector<QString>{
|
||||||
|
qsl("google.com"),
|
||||||
|
qsl("www.google.com"),
|
||||||
|
qsl("google.ru"),
|
||||||
|
qsl("www.google.ru"),
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool CheckPhoneByPrefixesRules(const QString &phone, const QString &rules) {
|
bool CheckPhoneByPrefixesRules(const QString &phone, const QString &rules) {
|
||||||
const auto check = QString(phone).replace(
|
const auto check = QString(phone).replace(
|
||||||
QRegularExpression("[^0-9]"),
|
QRegularExpression("[^0-9]"),
|
||||||
|
@ -49,15 +66,15 @@ bool CheckPhoneByPrefixesRules(const QString &phone, const QString &rules) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray ParseDnsResponse(const QByteArray &response) {
|
std::vector<DnsEntry> ParseDnsResponse(const QByteArray &response) {
|
||||||
// Read and store to "entries" map all the data bytes from the response:
|
// Read and store to "result" all the data bytes from the response:
|
||||||
// { ..,
|
// { ..,
|
||||||
// "Answer": [
|
// "Answer": [
|
||||||
// { .., "data": "bytes1", .. },
|
// { .., "data": "bytes1", "TTL": int, .. },
|
||||||
// { .., "data": "bytes2", .. }
|
// { .., "data": "bytes2", "TTL": int, .. }
|
||||||
// ],
|
// ],
|
||||||
// .. }
|
// .. }
|
||||||
auto entries = QMap<int, QString>();
|
auto result = std::vector<DnsEntry>();
|
||||||
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
|
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
|
||||||
auto document = QJsonDocument::fromJson(response, &error);
|
auto document = QJsonDocument::fromJson(response, &error);
|
||||||
if (error.error != QJsonParseError::NoError) {
|
if (error.error != QJsonParseError::NoError) {
|
||||||
|
@ -82,6 +99,10 @@ QByteArray ParseDnsResponse(const QByteArray &response) {
|
||||||
} else {
|
} else {
|
||||||
auto object = elem.toObject();
|
auto object = elem.toObject();
|
||||||
auto dataIt = object.find(qsl("data"));
|
auto dataIt = object.find(qsl("data"));
|
||||||
|
auto ttlIt = object.find(qsl("TTL"));
|
||||||
|
auto ttl = (ttlIt != object.constEnd())
|
||||||
|
? int64(std::round((*ttlIt).toDouble()))
|
||||||
|
: int64(0);
|
||||||
if (dataIt == object.constEnd()) {
|
if (dataIt == object.constEnd()) {
|
||||||
LOG(("Config Error: Could not find data "
|
LOG(("Config Error: Could not find data "
|
||||||
"in Answer array entry in dns response JSON."));
|
"in Answer array entry in dns response JSON."));
|
||||||
|
@ -89,27 +110,46 @@ QByteArray ParseDnsResponse(const QByteArray &response) {
|
||||||
LOG(("Config Error: Not a string data found "
|
LOG(("Config Error: Not a string data found "
|
||||||
"in Answer array entry in dns response JSON."));
|
"in Answer array entry in dns response JSON."));
|
||||||
} else {
|
} else {
|
||||||
auto data = (*dataIt).toString();
|
result.push_back({ (*dataIt).toString(), ttl });
|
||||||
entries.insertMulti(INT_MAX - data.size(), data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ConcatenateDnsTxtFields(const std::vector<DnsEntry> &response) {
|
||||||
|
auto entries = QMap<int, QString>();
|
||||||
|
for (const auto &entry : response) {
|
||||||
|
entries.insertMulti(INT_MAX - entry.data.size(), entry.data);
|
||||||
|
}
|
||||||
return QStringList(entries.values()).join(QString()).toLatin1();
|
return QStringList(entries.values()).join(QString()).toLatin1();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
SpecialConfigRequest::Request::Request(not_null<QNetworkReply*> reply)
|
struct ServiceWebRequest {
|
||||||
|
ServiceWebRequest(not_null<QNetworkReply*> reply);
|
||||||
|
ServiceWebRequest(ServiceWebRequest &&other);
|
||||||
|
ServiceWebRequest &operator=(ServiceWebRequest &&other);
|
||||||
|
~ServiceWebRequest();
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
QPointer<QNetworkReply> reply;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ServiceWebRequest::ServiceWebRequest(not_null<QNetworkReply*> reply)
|
||||||
: reply(reply.get()) {
|
: reply(reply.get()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SpecialConfigRequest::Request::Request(Request &&other)
|
ServiceWebRequest::ServiceWebRequest(ServiceWebRequest &&other)
|
||||||
: reply(base::take(other.reply)) {
|
: reply(base::take(other.reply)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SpecialConfigRequest::Request::operator=(Request &&other) -> Request& {
|
ServiceWebRequest &ServiceWebRequest::operator=(ServiceWebRequest &&other) {
|
||||||
if (reply != other.reply) {
|
if (reply != other.reply) {
|
||||||
destroy();
|
destroy();
|
||||||
reply = base::take(other.reply);
|
reply = base::take(other.reply);
|
||||||
|
@ -117,14 +157,14 @@ auto SpecialConfigRequest::Request::operator=(Request &&other) -> Request& {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpecialConfigRequest::Request::destroy() {
|
void ServiceWebRequest::destroy() {
|
||||||
if (const auto value = base::take(reply)) {
|
if (const auto value = base::take(reply)) {
|
||||||
value->deleteLater();
|
value->deleteLater();
|
||||||
value->abort();
|
value->abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SpecialConfigRequest::Request::~Request() {
|
ServiceWebRequest::~ServiceWebRequest() {
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,13 +177,13 @@ SpecialConfigRequest::SpecialConfigRequest(
|
||||||
const QString &phone)
|
const QString &phone)
|
||||||
: _callback(std::move(callback))
|
: _callback(std::move(callback))
|
||||||
, _phone(phone) {
|
, _phone(phone) {
|
||||||
|
_manager.setProxy(QNetworkProxy::NoProxy);
|
||||||
_attempts = {
|
_attempts = {
|
||||||
{ Type::App, qsl("software-download.microsoft.com") },
|
{ Type::App, qsl("software-download.microsoft.com") },
|
||||||
{ Type::Dns, qsl("google.com") },
|
|
||||||
{ Type::Dns, qsl("www.google.com") },
|
|
||||||
{ Type::Dns, qsl("google.ru") },
|
|
||||||
{ Type::Dns, qsl("www.google.ru") },
|
|
||||||
};
|
};
|
||||||
|
for (const auto &domain : DnsDomains()) {
|
||||||
|
_attempts.push_back({ Type::Dns, domain });
|
||||||
|
}
|
||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
ranges::shuffle(_attempts, std::mt19937(rd()));
|
ranges::shuffle(_attempts, std::mt19937(rd()));
|
||||||
sendNextRequest();
|
sendNextRequest();
|
||||||
|
@ -200,7 +240,8 @@ void SpecialConfigRequest::requestFinished(
|
||||||
const auto result = finalizeRequest(reply);
|
const auto result = finalizeRequest(reply);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::App: handleResponse(result); break;
|
case Type::App: handleResponse(result); break;
|
||||||
case Type::Dns: handleResponse(ParseDnsResponse(result)); break;
|
case Type::Dns: handleResponse(
|
||||||
|
ConcatenateDnsTxtFields(ParseDnsResponse(result))); break;
|
||||||
default: Unexpected("Type in SpecialConfigRequest::requestFinished.");
|
default: Unexpected("Type in SpecialConfigRequest::requestFinished.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,7 +258,7 @@ QByteArray SpecialConfigRequest::finalizeRequest(
|
||||||
const auto from = ranges::remove(
|
const auto from = ranges::remove(
|
||||||
_requests,
|
_requests,
|
||||||
reply,
|
reply,
|
||||||
[](const Request &request) { return request.reply; });
|
[](const ServiceWebRequest &request) { return request.reply; });
|
||||||
_requests.erase(from, end(_requests));
|
_requests.erase(from, end(_requests));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -347,4 +388,144 @@ void SpecialConfigRequest::handleResponse(const QByteArray &bytes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DomainResolver::DomainResolver(base::lambda<void(
|
||||||
|
const QString &host,
|
||||||
|
const QStringList &ips,
|
||||||
|
TimeMs expireAt)> callback)
|
||||||
|
: _callback(std::move(callback)) {
|
||||||
|
_manager.setProxy(QNetworkProxy::NoProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainResolver::resolve(const QString &domain) {
|
||||||
|
resolve({ domain, false });
|
||||||
|
resolve({ domain, true });
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainResolver::resolve(const AttemptKey &key) {
|
||||||
|
if (_attempts.find(key) != end(_attempts)) {
|
||||||
|
return;
|
||||||
|
} else if (_requests.find(key) != end(_requests)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto i = _cache.find(key);
|
||||||
|
_lastTimestamp = getms(true);
|
||||||
|
if (i != end(_cache) && i->second.expireAt > _lastTimestamp) {
|
||||||
|
checkExpireAndPushResult(key.domain);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto hosts = DnsDomains();
|
||||||
|
std::random_device rd;
|
||||||
|
ranges::shuffle(hosts, std::mt19937(rd()));
|
||||||
|
_attempts.emplace(key, std::move(hosts));
|
||||||
|
sendNextRequest(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainResolver::checkExpireAndPushResult(const QString &domain) {
|
||||||
|
const auto ipv4 = _cache.find({ domain, false });
|
||||||
|
if (ipv4 == end(_cache) || ipv4->second.expireAt <= _lastTimestamp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto result = ipv4->second;
|
||||||
|
const auto ipv6 = _cache.find({ domain, true });
|
||||||
|
if (ipv6 != end(_cache) && ipv6->second.expireAt > _lastTimestamp) {
|
||||||
|
result.ips.append(ipv6->second.ips);
|
||||||
|
accumulate_min(result.expireAt, ipv6->second.expireAt);
|
||||||
|
}
|
||||||
|
InvokeQueued(this, [=] {
|
||||||
|
_callback(domain, result.ips, result.expireAt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainResolver::sendNextRequest(const AttemptKey &key) {
|
||||||
|
auto i = _attempts.find(key);
|
||||||
|
if (i == end(_attempts)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &hosts = i->second;
|
||||||
|
const auto host = hosts.back();
|
||||||
|
hosts.pop_back();
|
||||||
|
|
||||||
|
if (!hosts.empty()) {
|
||||||
|
App::CallDelayed(kSendNextTimeout, this, [=] {
|
||||||
|
sendNextRequest(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
performRequest(key, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainResolver::performRequest(
|
||||||
|
const AttemptKey &key,
|
||||||
|
const QString &host) {
|
||||||
|
auto url = QUrl();
|
||||||
|
url.setScheme(qsl("https"));
|
||||||
|
url.setHost(host);
|
||||||
|
url.setPath(qsl("/resolve"));
|
||||||
|
url.setQuery(
|
||||||
|
qsl("name=%1&type=%2").arg(key.domain).arg(key.ipv6 ? 28 : 1));
|
||||||
|
auto request = QNetworkRequest();
|
||||||
|
request.setRawHeader("Host", "dns.google.com");
|
||||||
|
request.setUrl(url);
|
||||||
|
request.setRawHeader("User-Agent", kUserAgent);
|
||||||
|
const auto i = _requests.emplace(
|
||||||
|
key,
|
||||||
|
std::vector<ServiceWebRequest>()).first;
|
||||||
|
const auto reply = i->second.emplace_back(
|
||||||
|
_manager.get(request)
|
||||||
|
).reply;
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [=] {
|
||||||
|
requestFinished(key, reply);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DomainResolver::requestFinished(
|
||||||
|
const AttemptKey &key,
|
||||||
|
not_null<QNetworkReply*> reply) {
|
||||||
|
const auto result = finalizeRequest(key, reply);
|
||||||
|
const auto response = ParseDnsResponse(result);
|
||||||
|
if (response.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_requests.erase(key);
|
||||||
|
_attempts.erase(key);
|
||||||
|
|
||||||
|
auto entry = CacheEntry();
|
||||||
|
auto ttl = kMaxTimeToLive;
|
||||||
|
for (const auto &item : response) {
|
||||||
|
entry.ips.push_back(item.data);
|
||||||
|
accumulate_min(ttl, std::max(
|
||||||
|
item.TTL * TimeMs(1000),
|
||||||
|
kMinTimeToLive));
|
||||||
|
}
|
||||||
|
_lastTimestamp = getms(true);
|
||||||
|
entry.expireAt = _lastTimestamp + ttl;
|
||||||
|
_cache[key] = std::move(entry);
|
||||||
|
|
||||||
|
checkExpireAndPushResult(key.domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray DomainResolver::finalizeRequest(
|
||||||
|
const AttemptKey &key,
|
||||||
|
not_null<QNetworkReply*> reply) {
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
LOG(("Resolve Error: Failed to get response from %1, error: %2 (%3)"
|
||||||
|
).arg(reply->request().url().toDisplayString()
|
||||||
|
).arg(reply->errorString()
|
||||||
|
).arg(reply->error()));
|
||||||
|
}
|
||||||
|
const auto result = reply->readAll();
|
||||||
|
const auto i = _requests.find(key);
|
||||||
|
if (i != end(_requests)) {
|
||||||
|
auto &requests = i->second;
|
||||||
|
const auto from = ranges::remove(
|
||||||
|
requests,
|
||||||
|
reply,
|
||||||
|
[](const ServiceWebRequest &request) { return request.reply; });
|
||||||
|
requests.erase(from, end(requests));
|
||||||
|
if (requests.empty()) {
|
||||||
|
_requests.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace MTP
|
} // namespace MTP
|
||||||
|
|
|
@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
|
||||||
|
struct ServiceWebRequest;
|
||||||
|
|
||||||
class SpecialConfigRequest : public QObject {
|
class SpecialConfigRequest : public QObject {
|
||||||
public:
|
public:
|
||||||
SpecialConfigRequest(
|
SpecialConfigRequest(
|
||||||
|
@ -30,17 +32,6 @@ private:
|
||||||
Type type;
|
Type type;
|
||||||
QString domain;
|
QString domain;
|
||||||
};
|
};
|
||||||
struct Request {
|
|
||||||
Request(not_null<QNetworkReply*> reply);
|
|
||||||
Request(Request &&other);
|
|
||||||
Request &operator=(Request &&other);
|
|
||||||
~Request();
|
|
||||||
|
|
||||||
void destroy();
|
|
||||||
|
|
||||||
QPointer<QNetworkReply> reply;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void sendNextRequest();
|
void sendNextRequest();
|
||||||
void performRequest(const Attempt &attempt);
|
void performRequest(const Attempt &attempt);
|
||||||
|
@ -59,7 +50,60 @@ private:
|
||||||
|
|
||||||
QNetworkAccessManager _manager;
|
QNetworkAccessManager _manager;
|
||||||
std::vector<Attempt> _attempts;
|
std::vector<Attempt> _attempts;
|
||||||
std::vector<Request> _requests;
|
std::vector<ServiceWebRequest> _requests;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class DomainResolver : public QObject {
|
||||||
|
public:
|
||||||
|
DomainResolver(base::lambda<void(
|
||||||
|
const QString &domain,
|
||||||
|
const QStringList &ips,
|
||||||
|
TimeMs expireAt)> callback);
|
||||||
|
|
||||||
|
void resolve(const QString &domain);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct AttemptKey {
|
||||||
|
QString domain;
|
||||||
|
bool ipv6 = false;
|
||||||
|
|
||||||
|
inline bool operator<(const AttemptKey &other) const {
|
||||||
|
return (domain < other.domain)
|
||||||
|
|| (domain == other.domain && !ipv6 && other.ipv6);
|
||||||
|
}
|
||||||
|
inline bool operator==(const AttemptKey &other) const {
|
||||||
|
return (domain == other.domain) && (ipv6 == other.ipv6);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
struct CacheEntry {
|
||||||
|
QStringList ips;
|
||||||
|
TimeMs expireAt = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void resolve(const AttemptKey &key);
|
||||||
|
void sendNextRequest(const AttemptKey &key);
|
||||||
|
void performRequest(const AttemptKey &key, const QString &host);
|
||||||
|
void checkExpireAndPushResult(const QString &domain);
|
||||||
|
void requestFinished(
|
||||||
|
const AttemptKey &key,
|
||||||
|
not_null<QNetworkReply*> reply);
|
||||||
|
QByteArray finalizeRequest(
|
||||||
|
const AttemptKey &key,
|
||||||
|
not_null<QNetworkReply*> reply);
|
||||||
|
|
||||||
|
base::lambda<void(
|
||||||
|
const QString &domain,
|
||||||
|
const QStringList &ips,
|
||||||
|
TimeMs expireAt)> _callback;
|
||||||
|
|
||||||
|
QNetworkAccessManager _manager;
|
||||||
|
std::map<AttemptKey, std::vector<QString>> _attempts;
|
||||||
|
std::map<AttemptKey, std::vector<ServiceWebRequest>> _requests;
|
||||||
|
std::map<AttemptKey, CacheEntry> _cache;
|
||||||
|
TimeMs _lastTimestamp = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -112,8 +112,8 @@ const QString &Uploader::File::filename() const {
|
||||||
Uploader::Uploader() {
|
Uploader::Uploader() {
|
||||||
nextTimer.setSingleShot(true);
|
nextTimer.setSingleShot(true);
|
||||||
connect(&nextTimer, SIGNAL(timeout()), this, SLOT(sendNext()));
|
connect(&nextTimer, SIGNAL(timeout()), this, SLOT(sendNext()));
|
||||||
killSessionsTimer.setSingleShot(true);
|
stopSessionsTimer.setSingleShot(true);
|
||||||
connect(&killSessionsTimer, SIGNAL(timeout()), this, SLOT(killSessions()));
|
connect(&stopSessionsTimer, SIGNAL(timeout()), this, SLOT(stopSessions()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Uploader::uploadMedia(const FullMsgId &msgId, const SendMediaReady &media) {
|
void Uploader::uploadMedia(const FullMsgId &msgId, const SendMediaReady &media) {
|
||||||
|
@ -183,7 +183,7 @@ void Uploader::currentFailed() {
|
||||||
sendNext();
|
sendNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Uploader::killSessions() {
|
void Uploader::stopSessions() {
|
||||||
for (int i = 0; i < MTP::kUploadSessionsCount; ++i) {
|
for (int i = 0; i < MTP::kUploadSessionsCount; ++i) {
|
||||||
MTP::stopSession(MTP::uploadDcId(i));
|
MTP::stopSession(MTP::uploadDcId(i));
|
||||||
}
|
}
|
||||||
|
@ -192,16 +192,16 @@ void Uploader::killSessions() {
|
||||||
void Uploader::sendNext() {
|
void Uploader::sendNext() {
|
||||||
if (sentSize >= kMaxUploadFileParallelSize || _pausedId.msg) return;
|
if (sentSize >= kMaxUploadFileParallelSize || _pausedId.msg) return;
|
||||||
|
|
||||||
bool killing = killSessionsTimer.isActive();
|
bool stopping = stopSessionsTimer.isActive();
|
||||||
if (queue.empty()) {
|
if (queue.empty()) {
|
||||||
if (!killing) {
|
if (!stopping) {
|
||||||
killSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout);
|
stopSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (killing) {
|
if (stopping) {
|
||||||
killSessionsTimer.stop();
|
stopSessionsTimer.stop();
|
||||||
}
|
}
|
||||||
auto i = uploadingId.msg ? queue.find(uploadingId) : queue.begin();
|
auto i = uploadingId.msg ? queue.find(uploadingId) : queue.begin();
|
||||||
if (!uploadingId.msg) {
|
if (!uploadingId.msg) {
|
||||||
|
@ -415,7 +415,7 @@ void Uploader::clear() {
|
||||||
MTP::stopSession(MTP::uploadDcId(i));
|
MTP::stopSession(MTP::uploadDcId(i));
|
||||||
sentSizes[i] = 0;
|
sentSizes[i] = 0;
|
||||||
}
|
}
|
||||||
killSessionsTimer.stop();
|
stopSessionsTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
|
void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
|
||||||
|
|
|
@ -36,7 +36,7 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void unpause();
|
void unpause();
|
||||||
void sendNext();
|
void sendNext();
|
||||||
void killSessions();
|
void stopSessions();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void photoReady(const FullMsgId &msgId, bool silent, const MTPInputFile &file);
|
void photoReady(const FullMsgId &msgId, bool silent, const MTPInputFile &file);
|
||||||
|
@ -67,7 +67,7 @@ private:
|
||||||
FullMsgId _pausedId;
|
FullMsgId _pausedId;
|
||||||
std::map<FullMsgId, File> queue;
|
std::map<FullMsgId, File> queue;
|
||||||
std::map<FullMsgId, File> uploaded;
|
std::map<FullMsgId, File> uploaded;
|
||||||
QTimer nextTimer, killSessionsTimer;
|
QTimer nextTimer, stopSessionsTimer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -427,6 +427,8 @@
|
||||||
<(src_loc)/mtproto/connection_abstract.h
|
<(src_loc)/mtproto/connection_abstract.h
|
||||||
<(src_loc)/mtproto/connection_http.cpp
|
<(src_loc)/mtproto/connection_http.cpp
|
||||||
<(src_loc)/mtproto/connection_http.h
|
<(src_loc)/mtproto/connection_http.h
|
||||||
|
<(src_loc)/mtproto/connection_resolving.cpp
|
||||||
|
<(src_loc)/mtproto/connection_resolving.h
|
||||||
<(src_loc)/mtproto/connection_tcp.cpp
|
<(src_loc)/mtproto/connection_tcp.cpp
|
||||||
<(src_loc)/mtproto/connection_tcp.h
|
<(src_loc)/mtproto/connection_tcp.h
|
||||||
<(src_loc)/mtproto/core_types.cpp
|
<(src_loc)/mtproto/core_types.cpp
|
||||||
|
|
Loading…
Reference in New Issue