Support many config endpoints for one dc+params.

This commit is contained in:
John Preston 2018-04-24 23:09:20 +04:00
parent 7482025c10
commit 93f6d4b6e7
21 changed files with 819 additions and 917 deletions

View File

@ -38,4 +38,10 @@ QMap<QString, QString> url_parse_params(
return result;
}
bool is_ipv6(const QString &ip) {
//static const auto regexp = QRegularExpression("^[a-fA-F0-9:]+$");
//return regexp.match(ip).hasMatch();
return ip.indexOf(':') >= 0;
}
} // namespace qthelp

View File

@ -24,4 +24,6 @@ enum class UrlParamNameTransform {
// Parses a string like "p1=v1&p2=v2&..&pn=vn" to a map.
QMap<QString, QString> url_parse_params(const QString &params, UrlParamNameTransform transform = UrlParamNameTransform::NoTransform);
bool is_ipv6(const QString &ip);
} // namespace qthelp

View File

@ -17,12 +17,26 @@ QObject *TimersAdjuster() {
} // namespace
Timer::Timer(base::lambda<void()> callback) : QObject(nullptr)
Timer::Timer(
not_null<QThread*> thread,
base::lambda<void()> callback)
: Timer(std::move(callback)) {
moveToThread(thread);
}
Timer::Timer(base::lambda<void()> callback)
: QObject(nullptr)
, _callback(std::move(callback))
, _type(Qt::PreciseTimer)
, _adjusted(false) {
setRepeat(Repeat::Interval);
connect(TimersAdjuster(), &QObject::destroyed, this, [this] { adjust(); }, Qt::QueuedConnection);
connect(
TimersAdjuster(),
&QObject::destroyed,
this,
[this] { adjust(); },
Qt::QueuedConnection);
}
void Timer::start(TimeMs timeout, Qt::TimerType type, Repeat repeat) {
@ -56,7 +70,11 @@ TimeMs Timer::remainingTime() const {
void Timer::Adjust() {
QObject emitter;
connect(&emitter, &QObject::destroyed, TimersAdjuster(), &QObject::destroyed);
connect(
&emitter,
&QObject::destroyed,
TimersAdjuster(),
&QObject::destroyed);
}
void Timer::adjust() {
@ -70,6 +88,7 @@ void Timer::adjust() {
void Timer::setTimeout(TimeMs timeout) {
Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
_timeout = static_cast<unsigned int>(timeout);
}
@ -93,8 +112,12 @@ void Timer::timerEvent(QTimerEvent *e) {
}
}
int DelayedCallTimer::call(TimeMs timeout, lambda_once<void()> callback, Qt::TimerType type) {
int DelayedCallTimer::call(
TimeMs timeout,
lambda_once<void()> callback,
Qt::TimerType type) {
Expects(timeout >= 0);
if (!callback) {
return 0;
}
@ -108,7 +131,7 @@ int DelayedCallTimer::call(TimeMs timeout, lambda_once<void()> callback, Qt::Tim
void DelayedCallTimer::cancel(int callId) {
if (callId) {
killTimer(callId);
_callbacks.erase(callId);
_callbacks.remove(callId);
}
}

View File

@ -14,7 +14,10 @@ namespace base {
class Timer final : private QObject {
public:
Timer(base::lambda<void()> callback = base::lambda<void()>());
explicit Timer(
not_null<QThread*> thread,
base::lambda<void()> callback = nullptr);
explicit Timer(base::lambda<void()> callback = nullptr);
static Qt::TimerType DefaultType(TimeMs timeout) {
constexpr auto kThreshold = TimeMs(1000);
@ -85,17 +88,23 @@ private:
class DelayedCallTimer final : private QObject {
public:
int call(TimeMs timeout, lambda_once<void()> callback) {
return call(timeout, std::move(callback), Timer::DefaultType(timeout));
return call(
timeout,
std::move(callback),
Timer::DefaultType(timeout));
}
int call(TimeMs timeout, lambda_once<void()> callback, Qt::TimerType type);
int call(
TimeMs timeout,
lambda_once<void()> callback,
Qt::TimerType type);
void cancel(int callId);
protected:
void timerEvent(QTimerEvent *e) override;
private:
std::map<int, lambda_once<void()>> _callbacks; // Better to use flatmap.
base::flat_map<int, lambda_once<void()>> _callbacks;
};

View File

@ -87,7 +87,7 @@ bool ConnectionBox::badProxyValue() const {
void ConnectionBox::updateControlsVisibility() {
auto newHeight = st::boxOptionListPadding.top() + _autoRadio->heightNoMargins() + st::boxOptionListSkip + _httpProxyRadio->heightNoMargins() + st::boxOptionListSkip + _tcpProxyRadio->heightNoMargins() + st::boxOptionListSkip + st::connectionIPv6Skip + _tryIPv6->heightNoMargins() + st::defaultCheckbox.margin.bottom() + st::boxOptionListPadding.bottom() + st::boxPadding.bottom();
if (_typeGroup->value() == dbictAuto && badProxyValue()) {
if (!proxyFieldsVisible()) {
_hostInput->hide();
_portInput->hide();
_userInput->hide();
@ -104,6 +104,13 @@ void ConnectionBox::updateControlsVisibility() {
updateControlsPosition();
}
bool ConnectionBox::proxyFieldsVisible() const {
return (_typeGroup->value() != dbictAuto)
|| (!badProxyValue()
&& (_currentProxyType == ProxyData::Type::Http
|| _currentProxyType == ProxyData::Type::Socks5));
}
void ConnectionBox::setInnerFocus() {
if (_typeGroup->value() == dbictAuto) {
setFocus();
@ -124,7 +131,7 @@ void ConnectionBox::updateControlsPosition() {
_httpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _autoRadio->bottomNoMargins() + st::boxOptionListSkip);
auto inputy = 0;
auto fieldsVisible = (type != dbictAuto) || (!badProxyValue() && _currentProxyType != ProxyData::Type::None);
auto fieldsVisible = proxyFieldsVisible();
auto fieldsBelowHttp = fieldsVisible && (type == dbictHttpProxy || (type == dbictAuto && _currentProxyType == ProxyData::Type::Http));
auto fieldsBelowTcp = fieldsVisible && (type == dbictTcpProxy || (type == dbictAuto && _currentProxyType == ProxyData::Type::Socks5));
if (fieldsBelowHttp) {

View File

@ -44,6 +44,7 @@ private:
void updateControlsVisibility();
void updateControlsPosition();
bool badProxyValue() const;
bool proxyFieldsVisible() const;
object_ptr<Ui::InputField> _hostInput;
object_ptr<Ui::PortInput> _portInput;

View File

@ -25,13 +25,6 @@ enum {
MTPAckSendWaiting = 10000, // how much time to wait for some more requests, when sending msg acks
MTPResendThreshold = 1, // how much ints should message contain for us not to resend, but to check it's state
MTPContainerLives = 600, // container lives 10 minutes in haveSent map
MTPMinReceiveDelay = 4000, // 4 seconds
MTPMaxReceiveDelay = 64000, // 64 seconds
MTPMinConnectDelay = 1000, // tcp connect should take less then 1 second
MTPMaxConnectDelay = 8000, // tcp connect should take 8 seconds max
MTPConnectionOldTimeout = 192000, // 192 seconds
MTPTcpConnectionWaitTimeout = 2000, // 2 seconds waiting for tcp, until we accept http
MTPIPv4ConnectionWaitTimeout = 1000, // 1 seconds waiting for ipv4, until we accept ipv6
MTPKillFileSessionTimeout = 5000, // how much time without upload / download causes additional session kill
@ -39,10 +32,6 @@ enum {
MaxUsersPerInvite = 100, // max users in one super group invite request
MTPPingDelayDisconnect = 60, // 1 min
MTPPingSendAfterAuto = 30, // send new ping starting from 30 seconds (add to existing container)
MTPPingSendAfter = 45, // send new ping after 45 seconds without ping
MTPChannelGetDifferenceLimit = 100,
MaxSelectedItems = 100,

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/auth_key.h"
#include "mtproto/dc_options.h"
#include "core/single_timer.h"
#include "mtproto/connection_abstract.h"
#include "base/timer.h"
namespace MTP {
@ -57,7 +58,7 @@ public:
HttpConnection
};
Connection(Instance *instance);
Connection(not_null<Instance*> instance);
void start(SessionData *data, ShiftedDcId shiftedDcId);
@ -71,9 +72,9 @@ public:
QString transport() const;
private:
Instance *_instance = nullptr;
std::unique_ptr<QThread> thread;
ConnectionPrivate *data = nullptr;
not_null<Instance*> _instance;
std::unique_ptr<QThread> _thread;
ConnectionPrivate *_private = nullptr;
};
@ -81,7 +82,12 @@ class ConnectionPrivate : public QObject {
Q_OBJECT
public:
ConnectionPrivate(Instance *instance, QThread *thread, Connection *owner, SessionData *data, ShiftedDcId shiftedDcId);
ConnectionPrivate(
not_null<Instance*> instance,
not_null<QThread*> thread,
not_null<Connection*> owner,
not_null<SessionData*> data,
ShiftedDcId shiftedDcId);
~ConnectionPrivate();
void stop();
@ -109,29 +115,15 @@ signals:
void finished(internal::Connection *connection);
public slots:
void retryByTimer();
void restartNow();
void onPingSender();
void onPingSendForce();
void onWaitConnectedFailed();
void onWaitReceivedFailed();
void onWaitIPv4Failed();
void onOldConnection();
void onSentSome(uint64 size);
void onReceivedSome();
void onReadyData();
void onConnected4();
void onConnected6();
void onDisconnected4();
void onDisconnected6();
void onError4(qint32 errorCode);
void onError6(qint32 errorCode);
// Auth key creation packet receive slots
void pqAnswered();
void dhParamsAnswered();
@ -149,16 +141,32 @@ public slots:
void onCDNConfigLoaded();
private:
struct TestConnection {
ConnectionPointer data;
int priority = 0;
};
void connectToServer(bool afterConfig = false);
void doDisconnect();
void restart();
void finishAndDestroy();
void requestCDNConfig();
void handleError(int errorCode);
void onError(
not_null<AbstractConnection*> connection,
qint32 errorCode);
void onConnected(not_null<AbstractConnection*> connection);
void onDisconnected(not_null<AbstractConnection*> connection);
void retryByTimer();
void waitConnectedFailed();
void waitReceivedFailed();
void waitBetterFailed();
void markConnectionOld();
void sendPingByTimer();
void createConn(bool createIPv4, bool createIPv6);
void destroyAllConnections();
void destroyConnection(AbstractConnection *&connection);
void confirmBestConnection();
void removeTestConnection(not_null<AbstractConnection*> connection);
mtpMsgId placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req);
mtpMsgId prepareToSend(mtpRequest &request, mtpMsgId currentLastId);
@ -183,6 +191,26 @@ private:
base::byte_vector encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey &key);
std::string encryptClientDHInner(const MTPClient_DH_Inner_Data &data);
void appendTestConnection(
DcOptions::Variants::Protocol protocol,
const QString &ip,
int port,
const bytes::vector &protocolSecret);
// if badTime received - search for ids in sessionData->haveSent and sessionData->wereAcked and sync time/salt, return true if found
bool requestsFixTimeSalt(const QVector<MTPlong> &ids, int32 serverTime, uint64 serverSalt);
// remove msgs with such ids from sessionData->haveSent, add to sessionData->wereAcked
void requestsAcked(const QVector<MTPlong> &ids, bool byResponse = false);
void resend(quint64 msgId, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
void resendMany(QVector<quint64> msgIds, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
template <typename TRequest>
void sendRequestNotSecure(const TRequest &request);
template <typename TResponse>
bool readResponseNotSecure(TResponse &response);
Instance *_instance = nullptr;
DcType _dcType = DcType::Regular;
@ -194,45 +222,32 @@ private:
void resetSession();
ShiftedDcId _shiftedDcId = 0;
Connection *_owner = nullptr;
AbstractConnection *_conn = nullptr;
AbstractConnection *_conn4 = nullptr;
AbstractConnection *_conn6 = nullptr;
not_null<Connection*> _owner;
ConnectionPointer _connection;
std::vector<TestConnection> _testConnections;
TimeMs _configWasFineAt = 0;
SingleTimer retryTimer; // exp retry timer
int retryTimeout = 1;
qint64 retryWillFinish;
base::Timer _retryTimer; // exp retry timer
int _retryTimeout = 1;
qint64 _retryWillFinish = 0;
SingleTimer oldConnectionTimer;
bool oldConnection = true;
base::Timer _oldConnectionTimer;
bool _oldConnection = true;
SingleTimer _waitForConnectedTimer, _waitForReceivedTimer, _waitForIPv4Timer;
uint32 _waitForReceived, _waitForConnected;
base::Timer _waitForConnectedTimer;
base::Timer _waitForReceivedTimer;
base::Timer _waitForBetterTimer;
uint32 _waitForReceived = 0;
uint32 _waitForConnected = 0;
TimeMs firstSentAt = -1;
QVector<MTPlong> ackRequestData, resendRequestData;
// if badTime received - search for ids in sessionData->haveSent and sessionData->wereAcked and sync time/salt, return true if found
bool requestsFixTimeSalt(const QVector<MTPlong> &ids, int32 serverTime, uint64 serverSalt);
// remove msgs with such ids from sessionData->haveSent, add to sessionData->wereAcked
void requestsAcked(const QVector<MTPlong> &ids, bool byResponse = false);
mtpPingId _pingId = 0;
mtpPingId _pingIdToSend = 0;
TimeMs _pingSendAt = 0;
mtpMsgId _pingMsgId = 0;
SingleTimer _pingSender;
void resend(quint64 msgId, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
void resendMany(QVector<quint64> msgIds, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
template <typename TRequest>
void sendRequestNotSecure(const TRequest &request);
template <typename TResponse>
bool readResponseNotSecure(TResponse &response);
base::Timer _pingSender;
bool restarted = false;
bool _finished = false;

View File

@ -9,12 +9,71 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/connection_tcp.h"
#include "mtproto/connection_http.h"
#include "mtproto/connection_auto.h"
#include "mtproto/session.h"
namespace MTP {
namespace internal {
ConnectionPointer::ConnectionPointer() = default;
ConnectionPointer::ConnectionPointer(std::nullptr_t) {
}
ConnectionPointer::ConnectionPointer(AbstractConnection *value)
: _value(value) {
}
ConnectionPointer::ConnectionPointer(ConnectionPointer &&other)
: _value(base::take(other._value)) {
}
ConnectionPointer &ConnectionPointer::operator=(ConnectionPointer &&other) {
reset(base::take(other._value));
return *this;
}
AbstractConnection *ConnectionPointer::get() const {
return _value;
}
void ConnectionPointer::reset(AbstractConnection *value) {
if (_value == value) {
return;
} else if (const auto old = base::take(_value)) {
const auto disconnect = [&](auto signal) {
old->disconnect(old, signal, nullptr, nullptr);
};
disconnect(&AbstractConnection::receivedData);
disconnect(&AbstractConnection::receivedSome);
disconnect(&AbstractConnection::error);
disconnect(&AbstractConnection::connected);
disconnect(&AbstractConnection::disconnected);
old->disconnectFromServer();
old->deleteLater();
}
_value = value;
}
ConnectionPointer::operator AbstractConnection*() const {
return get();
}
AbstractConnection *ConnectionPointer::operator->() const {
return get();
}
AbstractConnection &ConnectionPointer::operator*() const {
return *get();
}
ConnectionPointer::operator bool() const {
return get() != nullptr;
}
ConnectionPointer::~ConnectionPointer() {
reset();
}
AbstractConnection::~AbstractConnection() {
}
@ -63,20 +122,14 @@ MTPResPQ AbstractConnection::readPQFakeReply(const mtpBuffer &buffer) {
return response;
}
AbstractConnection *AbstractConnection::create(
const ConnectionOptions &options,
ShiftedDcId shiftedDcId,
DcType type,
ConnectionPointer AbstractConnection::create(
DcOptions::Variants::Protocol protocol,
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);
if (protocol == DcOptions::Variants::Tcp) {
return ConnectionPointer(new TCPConnection(thread));
} else {
return ConnectionPointer(new HTTPConnection(thread));
}
return new AutoConnection(thread, protocolDcId);
}
} // namespace internal

View File

@ -8,12 +8,37 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "mtproto/dc_options.h"
#include "base/bytes.h"
namespace MTP {
namespace internal {
struct ConnectionOptions;
class AbstractConnection;
class ConnectionPointer {
public:
ConnectionPointer();
ConnectionPointer(std::nullptr_t);
explicit ConnectionPointer(AbstractConnection *value);
ConnectionPointer(ConnectionPointer &&other);
ConnectionPointer &operator=(ConnectionPointer &&other);
AbstractConnection *get() const;
void reset(AbstractConnection *value = nullptr);
operator AbstractConnection*() const;
AbstractConnection *operator->() const;
AbstractConnection &operator*() const;
explicit operator bool() const;
~ConnectionPointer();
private:
AbstractConnection *_value = nullptr;
};
class AbstractConnection : public QObject {
Q_OBJECT
@ -26,10 +51,8 @@ public:
virtual ~AbstractConnection() = 0;
// virtual constructor
static AbstractConnection *create(
const ConnectionOptions &options,
ShiftedDcId shiftedDcId,
DcType type,
static ConnectionPointer create(
DcOptions::Variants::Protocol protocol,
QThread *thread);
void setSentEncrypted() {
@ -38,8 +61,11 @@ public:
virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32
virtual void disconnectFromServer() = 0;
virtual void connectTcp(const DcOptions::Endpoint &endpoint) = 0;
virtual void connectHttp(const DcOptions::Endpoint &endpoint) = 0;
virtual void connectToServer(
const QString &ip,
int port,
const bytes::vector &protocolSecret,
int16 protocolDcId) = 0;
virtual bool isConnected() const = 0;
virtual bool usingHttpWait() {
return false;
@ -51,6 +77,7 @@ public:
virtual int32 debugState() const = 0;
virtual QString transport() const = 0;
virtual QString tag() const = 0;
using BuffersQueue = std::deque<mtpBuffer>;
BuffersQueue &received() {

View File

@ -1,323 +0,0 @@
/*
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_auto.h"
#include "mtproto/connection_http.h"
namespace MTP {
namespace internal {
AutoConnection::AutoConnection(QThread *thread, int16 protocolDcId)
: AbstractTCPConnection(thread, protocolDcId)
, status(WaitingBoth)
, tcpNonce(rand_value<MTPint128>())
, httpNonce(rand_value<MTPint128>())
, _flagsTcp(0)
, _flagsHttp(0)
, _tcpTimeout(MTPMinReceiveDelay) {
manager.moveToThread(thread);
httpStartTimer.moveToThread(thread);
httpStartTimer.setSingleShot(true);
connect(&httpStartTimer, SIGNAL(timeout()), this, SLOT(onHttpStart()));
tcpTimeoutTimer.moveToThread(thread);
tcpTimeoutTimer.setSingleShot(true);
connect(&tcpTimeoutTimer, SIGNAL(timeout()), this, SLOT(onTcpTimeoutTimer()));
sock.moveToThread(thread);
connect(&sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
connect(&sock, SIGNAL(connected()), this, SLOT(onSocketConnected()));
connect(&sock, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
}
void AutoConnection::onHttpStart() {
if (status == HttpReady) {
DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by timer").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
status = UsingHttp;
sock.disconnectFromHost();
emit connected();
}
}
void AutoConnection::onSocketConnected() {
if (status == HttpReady || status == WaitingBoth || status == WaitingTcp) {
mtpBuffer buffer(preparePQFake(tcpNonce));
DEBUG_LOG(("Connection Info: sending fake req_pq through TCP/%1 transport").arg((_flagsTcp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
if (_tcpTimeout < 0) _tcpTimeout = -_tcpTimeout;
tcpTimeoutTimer.start(_tcpTimeout);
tcpSend(buffer);
} else if (status == WaitingHttp || status == UsingHttp) {
sock.disconnectFromHost();
}
}
void AutoConnection::onTcpTimeoutTimer() {
if (status == HttpReady || status == WaitingBoth || status == WaitingTcp) {
if (_tcpTimeout < MTPMaxReceiveDelay) _tcpTimeout *= 2;
_tcpTimeout = -_tcpTimeout;
QAbstractSocket::SocketState state = sock.state();
if (state == QAbstractSocket::ConnectedState || state == QAbstractSocket::ConnectingState || state == QAbstractSocket::HostLookupState) {
sock.disconnectFromHost();
} else if (state != QAbstractSocket::ClosingState) {
sock.connectToHost(QHostAddress(_addrTcp), _portTcp);
}
}
}
void AutoConnection::onSocketDisconnected() {
if (_tcpTimeout < 0) {
_tcpTimeout = -_tcpTimeout;
if (status == HttpReady || status == WaitingBoth || status == WaitingTcp) {
sock.connectToHost(QHostAddress(_addrTcp), _portTcp);
return;
}
}
if (status == WaitingBoth) {
status = WaitingHttp;
} else if (status == WaitingTcp || status == UsingTcp) {
emit disconnected();
} else if (status == HttpReady) {
DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by socket disconnect").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
status = UsingHttp;
emit connected();
}
}
void AutoConnection::sendData(mtpBuffer &buffer) {
if (status == FinishedWork) return;
if (buffer.size() < 3) {
LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime)));
TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str()));
emit error(kErrorCodeOther);
return;
}
if (status == UsingTcp) {
tcpSend(buffer);
} else {
httpSend(buffer);
}
}
void AutoConnection::httpSend(mtpBuffer &buffer) {
int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime);
QNetworkRequest request(address);
request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize));
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded")));
TCP_LOG(("HTTP Info: sending %1 len request").arg(requestSize));
requests.insert(manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
}
void AutoConnection::disconnectFromServer() {
if (status == FinishedWork) return;
status = FinishedWork;
Requests copy = requests;
requests.clear();
for (Requests::const_iterator i = copy.cbegin(), e = copy.cend(); i != e; ++i) {
(*i)->abort();
(*i)->deleteLater();
}
disconnect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
address = QUrl();
disconnect(&sock, SIGNAL(readyRead()), 0, 0);
sock.close();
httpStartTimer.stop();
}
void AutoConnection::connectTcp(const DcOptions::Endpoint &endpoint) {
_addrTcp = QString::fromStdString(endpoint.ip);
_portTcp = endpoint.port;
_flagsTcp = endpoint.flags;
connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead()));
sock.connectToHost(QHostAddress(_addrTcp), _portTcp);
}
void AutoConnection::connectHttp(const DcOptions::Endpoint &endpoint) {
_addrHttp = QString::fromStdString(endpoint.ip);
_portHttp = endpoint.port;
_flagsHttp = endpoint.flags;
// not endpoint.port - always 80 port for http transport
address = QUrl(((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(_addrHttp).arg(80));
TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString()));
connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
mtpBuffer buffer(preparePQFake(httpNonce));
DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
httpSend(buffer);
}
bool AutoConnection::isConnected() const {
return (status == UsingTcp) || (status == UsingHttp);
}
void AutoConnection::requestFinished(QNetworkReply *reply) {
if (status == FinishedWork) return;
reply->deleteLater();
if (reply->error() == QNetworkReply::NoError) {
requests.remove(reply);
mtpBuffer data = HTTPConnection::handleResponse(reply);
if (data.size() == 1) {
if (status == WaitingBoth) {
status = WaitingTcp;
} else {
emit error(data[0]);
}
} else if (!data.isEmpty()) {
if (status == UsingHttp) {
_receivedQueue.push_back(data);
emit receivedData();
} else if (status == WaitingBoth || status == WaitingHttp) {
try {
auto res_pq = readPQFakeReply(data);
const auto &res_pq_data(res_pq.c_resPQ());
if (res_pq_data.vnonce == httpNonce) {
if (status == WaitingBoth) {
status = HttpReady;
httpStartTimer.start(MTPTcpConnectionWaitTimeout);
} else {
DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by pq-response, awaited").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
status = UsingHttp;
sock.disconnectFromHost();
emit connected();
}
}
} catch (Exception &e) {
DEBUG_LOG(("Connection Error: exception in parsing HTTP fake pq-responce, %1").arg(e.what()));
if (status == WaitingBoth) {
status = WaitingTcp;
} else {
emit error(kErrorCodeOther);
}
}
} else if (status == UsingTcp) {
DEBUG_LOG(("Connection Info: already using tcp, ignoring http response"));
}
}
} else {
if (!requests.remove(reply)) {
return;
}
if (status == WaitingBoth) {
status = WaitingTcp;
} else if (status == WaitingHttp || status == UsingHttp) {
emit error(HTTPConnection::handleError(reply));
} else {
LOG(("Strange Http Error: status %1").arg(status));
}
}
}
void AutoConnection::socketPacket(const char *packet, uint32 length) {
if (status == FinishedWork) return;
mtpBuffer data = AbstractTCPConnection::handleResponse(packet, length);
if (data.size() == 1) {
if (status == WaitingBoth) {
status = WaitingHttp;
sock.disconnectFromHost();
} else if (status == HttpReady) {
DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by bad tcp response, ready").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
status = UsingHttp;
sock.disconnectFromHost();
emit connected();
} else if (status == WaitingTcp || status == UsingTcp) {
emit error(data[0]);
} else {
LOG(("Strange Tcp Error; status %1").arg(status));
}
} else if (status == UsingTcp) {
_receivedQueue.push_back(data);
emit receivedData();
} else if (status == WaitingBoth || status == WaitingTcp || status == HttpReady) {
tcpTimeoutTimer.stop();
try {
auto res_pq = readPQFakeReply(data);
const auto &res_pq_data(res_pq.c_resPQ());
if (res_pq_data.vnonce == tcpNonce) {
DEBUG_LOG(("Connection Info: TCP/%1-transport chosen by pq-response").arg((_flagsTcp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
status = UsingTcp;
emit connected();
}
} catch (Exception &e) {
DEBUG_LOG(("Connection Error: exception in parsing TCP fake pq-responce, %1").arg(e.what()));
if (status == WaitingBoth) {
status = WaitingHttp;
sock.disconnectFromHost();
} else if (status == HttpReady) {
DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by bad tcp response, awaited").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
status = UsingHttp;
sock.disconnectFromHost();
emit connected();
} else {
emit error(kErrorCodeOther);
}
}
}
}
bool AutoConnection::usingHttpWait() {
return (status == UsingHttp);
}
bool AutoConnection::needHttpWait() {
return (status == UsingHttp) ? requests.isEmpty() : false;
}
int32 AutoConnection::debugState() const {
return (status == UsingHttp) ? -1 : ((status == UsingTcp) ? sock.state() : -777);
}
QString AutoConnection::transport() const {
if (status == UsingTcp) {
return qsl("TCP");
} else if (status == UsingHttp) {
return qsl("HTTP");
} else {
return QString();
}
}
void AutoConnection::socketError(QAbstractSocket::SocketError e) {
if (status == FinishedWork) return;
AbstractTCPConnection::handleError(e, sock);
if (status == WaitingBoth) {
status = WaitingHttp;
} else if (status == HttpReady) {
DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by tcp error, ready").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
status = UsingHttp;
emit connected();
} else if (status == WaitingTcp || status == UsingTcp) {
emit error(kErrorCodeOther);
} else {
LOG(("Strange Tcp Error: status %1").arg(status));
}
}
} // namespace internal
} // namespace MTP

View File

@ -1,76 +0,0 @@
/*
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/connection_tcp.h"
namespace MTP {
namespace internal {
class AutoConnection : public AbstractTCPConnection {
Q_OBJECT
public:
AutoConnection(QThread *thread, int16 protocolDcId);
void sendData(mtpBuffer &buffer) override;
void disconnectFromServer() override;
void connectTcp(const DcOptions::Endpoint &endpoint) override;
void connectHttp(const DcOptions::Endpoint &endpoint) override;
bool isConnected() const override;
bool usingHttpWait() override;
bool needHttpWait() override;
int32 debugState() const override;
QString transport() const override;
public slots:
void socketError(QAbstractSocket::SocketError e);
void requestFinished(QNetworkReply *reply);
void onSocketConnected();
void onSocketDisconnected();
void onHttpStart();
void onTcpTimeoutTimer();
protected:
void socketPacket(const char *packet, uint32 length) override;
private:
void httpSend(mtpBuffer &buffer);
enum Status {
WaitingBoth = 0,
WaitingHttp,
WaitingTcp,
HttpReady,
UsingHttp,
UsingTcp,
FinishedWork
};
Status status;
MTPint128 tcpNonce, httpNonce;
QTimer httpStartTimer;
QNetworkAccessManager manager;
QUrl address;
typedef QSet<QNetworkReply*> Requests;
Requests requests;
QString _addrTcp, _addrHttp;
int32 _portTcp, _portHttp;
MTPDdcOption::Flags _flagsTcp, _flagsHttp;
int32 _tcpTimeout;
QTimer tcpTimeoutTimer;
};
} // namespace internal
} // namespace MTP

View File

@ -7,8 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "mtproto/connection_http.h"
#include "base/qthelp_url.h"
namespace MTP {
namespace internal {
namespace {
constexpr auto kForceHttpPort = 80;
} // namespace
mtpBuffer HTTPConnection::handleResponse(QNetworkReply *reply) {
QByteArray response = reply->readAll();
@ -76,8 +83,7 @@ qint32 HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe ba
HTTPConnection::HTTPConnection(QThread *thread) : AbstractConnection(thread)
, status(WaitingHttp)
, httpNonce(rand_value<MTPint128>())
, _flags(0) {
, httpNonce(rand_value<MTPint128>()) {
manager.moveToThread(thread);
}
@ -93,7 +99,7 @@ void HTTPConnection::sendData(mtpBuffer &buffer) {
int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime);
QNetworkRequest request(address);
QNetworkRequest request(url());
request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize));
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded")));
@ -113,22 +119,20 @@ void HTTPConnection::disconnectFromServer() {
}
disconnect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
address = QUrl();
}
void HTTPConnection::connectHttp(const DcOptions::Endpoint &endpoint) {
_flags = endpoint.flags;
auto addr = QString::fromStdString(endpoint.ip);
// not endpoint.port - always 80 port for http transport
address = QUrl(((_flags & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));
TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString()));
void HTTPConnection::connectToServer(
const QString &ip,
int port,
const bytes::vector &protocolSecret,
int16 protocolDcId) {
_address = ip;
TCP_LOG(("HTTP Info: address is %1").arg(url().toDisplayString()));
connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
mtpBuffer buffer(preparePQFake(httpNonce));
DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP transport to %1").arg(ip));
sendData(buffer);
}
@ -156,7 +160,7 @@ void HTTPConnection::requestFinished(QNetworkReply *reply) {
auto res_pq = readPQFakeReply(data);
const auto &res_pq_data(res_pq.c_resPQ());
if (res_pq_data.vnonce == httpNonce) {
DEBUG_LOG(("Connection Info: HTTP/%1-transport connected by pq-response").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
DEBUG_LOG(("Connection Info: HTTP-transport to %1 connected by pq-response").arg(_address));
status = UsingHttp;
emit connected();
}
@ -188,11 +192,33 @@ int32 HTTPConnection::debugState() const {
}
QString HTTPConnection::transport() const {
if (status == UsingHttp) {
return qsl("HTTP");
} else {
if (!isConnected()) {
return QString();
}
auto result = qsl("HTTP");
if (qthelp::is_ipv6(_address)) {
result += qsl("/IPv6");
}
return result;
}
QString HTTPConnection::tag() const {
auto result = qsl("HTTP");
if (qthelp::is_ipv6(_address)) {
result += qsl("/IPv6");
} else {
result += qsl("/IPv4");
}
return result;
}
QUrl HTTPConnection::url() const {
const auto pattern = qthelp::is_ipv6(_address)
? qsl("http://[%1]:%2/api")
: qsl("http://%1:%2/api");
// Not endpoint.port - always 80 port for http transport.
return QUrl(pattern.arg(_address).arg(kForceHttpPort));
}
} // namespace internal

View File

@ -20,9 +20,11 @@ public:
void sendData(mtpBuffer &buffer) override;
void disconnectFromServer() override;
void connectTcp(const DcOptions::Endpoint &endpoint) override { // not supported
}
void connectHttp(const DcOptions::Endpoint &endpoint) override;
void connectToServer(
const QString &ip,
int port,
const bytes::vector &protocolSecret,
int16 protocolDcId) override;
bool isConnected() const override;
bool usingHttpWait() override;
bool needHttpWait() override;
@ -30,6 +32,7 @@ public:
int32 debugState() const override;
QString transport() const override;
QString tag() const override;
static mtpBuffer handleResponse(QNetworkReply *reply);
static qint32 handleError(QNetworkReply *reply); // returnes error code
@ -38,6 +41,8 @@ public slots:
void requestFinished(QNetworkReply *reply);
private:
QUrl url() const;
enum Status {
WaitingHttp = 0,
UsingHttp,
@ -45,10 +50,9 @@ private:
};
Status status;
MTPint128 httpNonce;
MTPDdcOption::Flags _flags;
QNetworkAccessManager manager;
QUrl address;
QString _address;
typedef QSet<QNetworkReply*> Requests;
Requests requests;

View File

@ -9,13 +9,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/bytes.h"
#include "base/openssl_help.h"
#include "base/qthelp_url.h"
#include <openssl/aes.h>
namespace MTP {
namespace internal {
namespace {
constexpr auto kMinReceiveTimeout = TimeMs(2000);
constexpr auto kMaxReceiveTimeout = TimeMs(8000);
uint32 tcpPacketSize(const char *packet) { // must have at least 4 bytes readable
uint32 result = (packet[0] > 0) ? packet[0] : 0;
if (result == 0x7f) {
@ -29,14 +32,8 @@ uint32 tcpPacketSize(const char *packet) { // must have at least 4 bytes readabl
} // namespace
AbstractTCPConnection::AbstractTCPConnection(
QThread *thread,
int16 protocolDcId)
QThread *thread)
: AbstractConnection(thread)
, _protocolDcId(protocolDcId)
, packetNum(0)
, packetRead(0)
, packetLeft(0)
, readingToShort(true)
, currentPos((char*)shortBuffer) {
}
@ -197,12 +194,11 @@ void AbstractTCPConnection::handleError(QAbstractSocket::SocketError e, QTcpSock
TCP_LOG(("TCP Error %1, restarting! - %2").arg(e).arg(sock.errorString()));
}
TCPConnection::TCPConnection(QThread *thread, int16 protocolDcId)
: AbstractTCPConnection(thread, protocolDcId)
TCPConnection::TCPConnection(QThread *thread)
: AbstractTCPConnection(thread)
, status(WaitingTcp)
, tcpNonce(rand_value<MTPint128>())
, _tcpTimeout(MTPMinReceiveDelay)
, _flags(0) {
, _tcpTimeout(kMinReceiveTimeout) {
tcpTimeoutTimer.moveToThread(thread);
tcpTimeoutTimer.setSingleShot(true);
connect(&tcpTimeoutTimer, SIGNAL(timeout()), this, SLOT(onTcpTimeoutTimer()));
@ -217,7 +213,7 @@ void TCPConnection::onSocketConnected() {
if (status == WaitingTcp) {
mtpBuffer buffer(preparePQFake(tcpNonce));
DEBUG_LOG(("Connection Info: sending fake req_pq through TCP/%1 transport").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
DEBUG_LOG(("Connection Info: sending fake req_pq through TCP transport to %1").arg(_address));
if (_tcpTimeout < 0) _tcpTimeout = -_tcpTimeout;
tcpTimeoutTimer.start(_tcpTimeout);
@ -228,14 +224,16 @@ void TCPConnection::onSocketConnected() {
void TCPConnection::onTcpTimeoutTimer() {
if (status == WaitingTcp) {
if (_tcpTimeout < MTPMaxReceiveDelay) _tcpTimeout *= 2;
if (_tcpTimeout < kMaxReceiveTimeout) {
_tcpTimeout *= 2;
}
_tcpTimeout = -_tcpTimeout;
QAbstractSocket::SocketState state = sock.state();
if (state == QAbstractSocket::ConnectedState || state == QAbstractSocket::ConnectingState || state == QAbstractSocket::HostLookupState) {
sock.disconnectFromHost();
} else if (state != QAbstractSocket::ClosingState) {
sock.connectToHost(QHostAddress(_addr), _port);
sock.connectToHost(QHostAddress(_address), _port);
}
}
}
@ -244,7 +242,7 @@ void TCPConnection::onSocketDisconnected() {
if (_tcpTimeout < 0) {
_tcpTimeout = -_tcpTimeout;
if (status == WaitingTcp) {
sock.connectToHost(QHostAddress(_addr), _port);
sock.connectToHost(QHostAddress(_address), _port);
return;
}
}
@ -291,8 +289,19 @@ void AbstractTCPConnection::writeConnectionStart() {
|| *first == reserved15
|| *second == reserved21);
const auto prepareKey = [&](bytes::span key, bytes::const_span from) {
if (_protocolSecret.size() == 16) {
const auto payload = bytes::concatenate(from, _protocolSecret);
bytes::copy(key, openssl::Sha256(payload));
} else if (_protocolSecret.empty()) {
bytes::copy(key, from);
} else {
bytes::set_with_const(key, gsl::byte{});
}
};
// prepare encryption key/iv
bytes::copy(
prepareKey(
bytes::make_span(_sendKey),
nonce.subspan(8, CTRState::KeySize));
bytes::copy(
@ -304,7 +313,7 @@ void AbstractTCPConnection::writeConnectionStart() {
const auto reversed = bytes::make_span(reversedBytes);
bytes::copy(reversed, nonce.subspan(8, reversed.size()));
std::reverse(reversed.begin(), reversed.end());
bytes::copy(
prepareKey(
bytes::make_span(_receiveKey),
reversed.subspan(0, CTRState::KeySize));
bytes::copy(
@ -356,14 +365,18 @@ void TCPConnection::disconnectFromServer() {
sock.close();
}
void TCPConnection::connectTcp(const DcOptions::Endpoint &endpoint) {
_addr = QString::fromStdString(endpoint.ip);
_port = endpoint.port;
_flags = endpoint.flags;
void TCPConnection::connectToServer(
const QString &ip,
int port,
const bytes::vector &protocolSecret,
int16 protocolDcId) {
_address = ip;
_port = port;
_protocolSecret = protocolSecret;
_protocolDcId = protocolDcId;
connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead()));
sock.connectToHost(QHostAddress(_addr), _port);
auto proxy = sock.proxy();
sock.connectToHost(QHostAddress(_address), _port);
}
void TCPConnection::socketPacket(const char *packet, uint32 length) {
@ -381,7 +394,7 @@ void TCPConnection::socketPacket(const char *packet, uint32 length) {
auto res_pq = readPQFakeReply(data);
const auto &res_pq_data(res_pq.c_resPQ());
if (res_pq_data.vnonce == tcpNonce) {
DEBUG_LOG(("Connection Info: TCP/%1-transport chosen by pq-response").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4"));
DEBUG_LOG(("Connection Info: TCP-transport to %1 chosen by pq-response").arg(_address));
status = UsingTcp;
emit connected();
}
@ -401,7 +414,24 @@ int32 TCPConnection::debugState() const {
}
QString TCPConnection::transport() const {
return isConnected() ? qsl("TCP") : QString();
if (!isConnected()) {
return QString();
}
auto result = qsl("TCP");
if (qthelp::is_ipv6(_address)) {
result += qsl("/IPv6");
}
return result;
}
QString TCPConnection::tag() const {
auto result = qsl("TCP");
if (qthelp::is_ipv6(_address)) {
result += qsl("/IPv6");
} else {
result += qsl("/IPv4");
}
return result;
}
void TCPConnection::socketError(QAbstractSocket::SocketError e) {

View File

@ -17,7 +17,7 @@ class AbstractTCPConnection : public AbstractConnection {
Q_OBJECT
public:
AbstractTCPConnection(QThread *thread, int16 protocolDcId);
AbstractTCPConnection(QThread *thread);
virtual ~AbstractTCPConnection() = 0;
public slots:
@ -27,10 +27,11 @@ protected:
void writeConnectionStart();
QTcpSocket sock;
uint32 packetNum; // sent packet number
uint32 packetNum = 0; // sent packet number
uint32 packetRead, packetLeft; // reading from socket
bool readingToShort;
uint32 packetRead = 0;
uint32 packetLeft = 0; // reading from socket
bool readingToShort = true;
char *currentPos;
mtpBuffer longBuffer;
mtpPrime shortBuffer[MTPShortBufferSize];
@ -49,6 +50,7 @@ protected:
uchar _receiveKey[CTRState::KeySize];
CTRState _receiveState;
int16 _protocolDcId = 0;
bytes::vector _protocolSecret;
};
@ -56,21 +58,23 @@ class TCPConnection : public AbstractTCPConnection {
Q_OBJECT
public:
TCPConnection(QThread *thread, int16 protocolDcId);
TCPConnection(QThread *thread);
void sendData(mtpBuffer &buffer) override;
void disconnectFromServer() override;
void connectTcp(const DcOptions::Endpoint &endpoint) override;
void connectHttp(const DcOptions::Endpoint &endpoint) override { // not supported
}
void connectToServer(
const QString &ip,
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;
public slots:
void socketError(QAbstractSocket::SocketError e);
void onSocketConnected();
@ -79,11 +83,9 @@ public slots:
void onTcpTimeoutTimer();
protected:
void socketPacket(const char *packet, uint32 length) override;
private:
enum Status {
WaitingTcp = 0,
UsingTcp,
@ -92,9 +94,8 @@ private:
Status status;
MTPint128 tcpNonce;
QString _addr;
QString _address;
int32 _port, _tcpTimeout;
MTPDdcOption::Flags _flags;
QTimer tcpTimeoutTimer;
};

View File

@ -105,22 +105,36 @@ void DcOptions::constructFromBuiltIn() {
auto bdcs = builtInDcs();
for (auto i = 0, l = builtInDcsCount(); i != l; ++i) {
auto flags = MTPDdcOption::Flags(0);
auto idWithShift = MTP::shiftDcId(bdcs[i].id, flags);
_data.emplace(idWithShift, Option(bdcs[i].id, flags, bdcs[i].ip, bdcs[i].port));
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port));
const auto flags = MTPDdcOption::Flags(0);
const auto bdc = bdcs[i];
const auto idWithShift = MTP::shiftDcId(bdc.id, flags);
_data.emplace(
idWithShift,
std::vector<Option>(
1,
Option(bdc.id, flags, bdc.ip, bdc.port)));
DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: "
"%2:%3").arg(bdc.id).arg(bdc.ip).arg(bdc.port));
}
auto bdcsipv6 = builtInDcsIPv6();
for (auto i = 0, l = builtInDcsCountIPv6(); i != l; ++i) {
auto flags = MTPDdcOption::Flags(MTPDdcOption::Flag::f_ipv6);
auto idWithShift = MTP::shiftDcId(bdcsipv6[i].id, flags);
_data.emplace(idWithShift, Option(bdcsipv6[i].id, flags, bdcsipv6[i].ip, bdcsipv6[i].port));
DEBUG_LOG(("MTP Info: adding built in DC %1 IPv6 connect option: %2:%3").arg(bdcsipv6[i].id).arg(bdcsipv6[i].ip).arg(bdcsipv6[i].port));
const auto flags = MTPDdcOption::Flags(MTPDdcOption::Flag::f_ipv6);
const auto bdc = bdcsipv6[i];
const auto idWithShift = MTP::shiftDcId(bdc.id, flags);
_data.emplace(
idWithShift,
std::vector<Option>(
1,
Option(bdc.id, flags, bdc.ip, bdc.port)));
DEBUG_LOG(("MTP Info: adding built in DC %1 IPv6 connect option: "
"%2:%3").arg(bdc.id).arg(bdc.ip).arg(bdc.port));
}
}
void DcOptions::processFromList(const QVector<MTPDcOption> &options, bool overwrite) {
void DcOptions::processFromList(
const QVector<MTPDcOption> &options,
bool overwrite) {
if (options.empty() || _immutable) {
return;
}
@ -144,12 +158,15 @@ void DcOptions::processFromList(const QVector<MTPDcOption> &options, bool overwr
auto dcId = option.vid.v;
auto flags = option.vflags.v;
auto dcIdWithShift = MTP::shiftDcId(dcId, flags);
if (base::contains(shiftedIdsProcessed, dcIdWithShift)) {
continue;
if (overwrite) {
if (!base::contains(shiftedIdsProcessed, dcIdWithShift)) {
shiftedIdsProcessed.push_back(dcIdWithShift);
_data.erase(dcIdWithShift);
}
}
shiftedIdsProcessed.push_back(dcIdWithShift);
auto ip = std::string(option.vip_address.v.constData(), option.vip_address.v.size());
auto ip = std::string(
option.vip_address.v.constData(),
option.vip_address.v.size());
auto port = option.vport.v;
if (applyOneGuarded(dcId, flags, ip, port)) {
if (!base::contains(idsChanged, dcId)) {
@ -162,8 +179,10 @@ void DcOptions::processFromList(const QVector<MTPDcOption> &options, bool overwr
if (base::contains(shiftedIdsProcessed, i->first)) {
++i;
} else {
if (!base::contains(idsChanged, i->second.id)) {
idsChanged.push_back(i->second.id);
const auto &options = i->second;
Assert(!options.empty());
if (!base::contains(idsChanged, options.front().id)) {
idsChanged.push_back(options.front().id);
}
i = _data.erase(i);
}
@ -199,14 +218,16 @@ void DcOptions::addFromOther(DcOptions &&options) {
idsChanged.reserve(options._data.size());
{
WriteLocker lock(this);
for (auto &item : base::take(options._data)) {
auto dcId = item.second.id;
auto flags = item.second.flags;
auto &ip = item.second.ip;
auto port = item.second.port;
if (applyOneGuarded(dcId, flags, ip, port)) {
if (!base::contains(idsChanged, dcId)) {
idsChanged.push_back(dcId);
for (const auto &item : base::take(options._data)) {
for (const auto &option : item.second) {
auto dcId = option.id;
auto flags = option.flags;
auto &ip = option.ip;
auto port = option.port;
if (applyOneGuarded(dcId, flags, ip, port)) {
if (!base::contains(idsChanged, dcId)) {
idsChanged.push_back(dcId);
}
}
}
}
@ -228,17 +249,24 @@ void DcOptions::constructAddOne(int id, MTPDdcOption::Flags flags, const std::st
applyOneGuarded(bareDcId(id), flags, ip, port);
}
bool DcOptions::applyOneGuarded(DcId dcId, MTPDdcOption::Flags flags, const std::string &ip, int port) {
bool DcOptions::applyOneGuarded(
DcId dcId,
MTPDdcOption::Flags flags,
const std::string &ip,
int port) {
auto dcIdWithShift = MTP::shiftDcId(dcId, flags);
auto i = _data.find(dcIdWithShift);
if (i != _data.cend()) {
if (i->second.ip == ip && i->second.port == port) {
return false;
for (auto &option : i->second) {
if (option.ip == ip && option.port == port) {
return false;
}
}
i->second.ip = ip;
i->second.port = port;
i->second.push_back(Option(dcId, flags, ip, port));
} else {
_data.emplace(dcIdWithShift, Option(dcId, flags, ip, port));
_data.emplace(dcIdWithShift, std::vector<Option>(
1,
Option(dcId, flags, ip, port)));
}
return true;
}
@ -252,12 +280,14 @@ QByteArray DcOptions::serialize() const {
ReadLocker lock(this);
auto size = sizeof(qint32);
for (auto &item : _data) {
for (const auto &item : _data) {
if (isTemporaryDcId(item.first)) {
continue;
}
size += sizeof(qint32) + sizeof(qint32) + sizeof(qint32); // id + flags + port
size += sizeof(qint32) + item.second.ip.size();
for (const auto &option : item.second) {
size += sizeof(qint32) + sizeof(qint32) + sizeof(qint32); // id + flags + port
size += sizeof(qint32) + option.ip.size();
}
}
auto count = 0;
@ -271,8 +301,8 @@ QByteArray DcOptions::serialize() const {
};
std::vector<SerializedPublicKey> publicKeys;
publicKeys.reserve(count);
for (auto &keysInDc : _cdnPublicKeys) {
for (auto &entry : keysInDc.second) {
for (const auto &keysInDc : _cdnPublicKeys) {
for (const auto &entry : keysInDc.second) {
publicKeys.push_back({ keysInDc.first, entry.second.getN(), entry.second.getE() });
size += sizeof(qint32) + Serialize::bytesSize(publicKeys.back().n) + Serialize::bytesSize(publicKeys.back().e);
}
@ -284,13 +314,15 @@ QByteArray DcOptions::serialize() const {
QDataStream stream(&result, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_1);
stream << qint32(_data.size());
for (auto &item : _data) {
for (const auto &item : _data) {
if (isTemporaryDcId(item.first)) {
continue;
}
stream << qint32(item.second.id) << qint32(item.second.flags) << qint32(item.second.port);
stream << qint32(item.second.ip.size());
stream.writeRawData(item.second.ip.data(), item.second.ip.size());
for (const auto &option : item.second) {
stream << qint32(option.id) << qint32(option.flags) << qint32(option.port);
stream << qint32(option.ip.size());
stream.writeRawData(option.ip.data(), option.ip.size());
}
}
stream << qint32(publicKeys.size());
for (auto &key : publicKeys) {
@ -368,10 +400,12 @@ DcOptions::Ids DcOptions::configEnumDcIds() const {
ReadLocker lock(this);
result.reserve(_data.size());
for (auto &item : _data) {
if (!isCdnDc(item.second.flags)
const auto dcId = bareDcId(item.first);
Assert(!item.second.empty());
if (!isCdnDc(item.second.front().flags)
&& !isTemporaryDcId(item.first)
&& !base::contains(result, item.second.id)) {
result.push_back(item.second.id);
&& !base::contains(result, dcId)) {
result.push_back(dcId);
}
}
}
@ -438,10 +472,11 @@ bool DcOptions::getDcRSAKey(DcId dcId, const QVector<MTPlong> &fingerprints, int
return findKey(_publicKeys);
}
DcOptions::Variants DcOptions::lookup(DcId dcId, DcType type) const {
auto lookupDesiredFlags = [type](int address, int protocol) -> std::vector<MTPDdcOption::Flags> {
auto throughProxy = (Global::ConnectionType() != dbictAuto);
DcOptions::Variants DcOptions::lookup(
DcId dcId,
DcType type,
bool throughProxy) const {
auto lookupDesiredFlags = [&](int address, int protocol) -> std::vector<MTPDdcOption::Flags> {
switch (type) {
case DcType::Regular:
case DcType::Temporary: {
@ -575,14 +610,15 @@ DcOptions::Variants DcOptions::lookup(DcId dcId, DcType type) const {
for (auto protocol = 0; protocol != Variants::ProtocolCount; ++protocol) {
auto desiredFlags = lookupDesiredFlags(address, protocol);
for (auto flags : desiredFlags) {
auto shift = static_cast<int>(flags);
if (shift < 0) continue;
auto it = _data.find(shiftDcId(dcId, shift));
const auto shift = static_cast<int>(flags);
const auto it = _data.find(shiftDcId(dcId, shift));
if (it != _data.cend()) {
result.data[address][protocol].ip = it->second.ip;
result.data[address][protocol].flags = it->second.flags;
result.data[address][protocol].port = it->second.port;
for (const auto &option : it->second) {
result.data[address][protocol].push_back({
option.ip,
option.port
});
}
break;
}
}
@ -595,8 +631,9 @@ DcOptions::Variants DcOptions::lookup(DcId dcId, DcType type) const {
void DcOptions::computeCdnDcIds() {
_cdnDcIds.clear();
for (auto &item : _data) {
if (item.second.flags & MTPDdcOption::Flag::f_cdn) {
_cdnDcIds.insert(item.second.id);
Assert(!item.second.empty());
if (item.second.front().flags & MTPDdcOption::Flag::f_cdn) {
_cdnDcIds.insert(bareDcId(item.first));
}
}
}
@ -670,16 +707,21 @@ bool DcOptions::writeToFile(const QString &path) const {
stream.setCodec("UTF-8");
ReadLocker lock(this);
for (auto &item : _data) {
auto &endpoint = item.second;
stream << endpoint.id << ' ' << QString::fromStdString(endpoint.ip) << ' ' << endpoint.port;
if (endpoint.flags & MTPDdcOption::Flag::f_tcpo_only) {
stream << " tcpo_only";
for (const auto &item : _data) {
for (const auto &option : item.second) {
stream
<< option.id
<< ' '
<< QString::fromStdString(option.ip)
<< ' ' << option.port;
if (option.flags & MTPDdcOption::Flag::f_tcpo_only) {
stream << " tcpo_only";
}
if (option.flags & MTPDdcOption::Flag::f_media_only) {
stream << " media_only";
}
stream << '\n';
}
if (endpoint.flags & MTPDdcOption::Flag::f_media_only) {
stream << " media_only";
}
stream << '\n';
}
return true;
}

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "base/observer.h"
#include "base/bytes.h"
#include "mtproto/rsa_public_key.h"
#include <string>
#include <vector>
@ -26,7 +27,11 @@ public:
// construct methods don't notify "changed" subscribers.
void constructFromSerialized(const QByteArray &serialized);
void constructFromBuiltIn();
void constructAddOne(int id, MTPDdcOption::Flags flags, const std::string &ip, int port);
void constructAddOne(
int id,
MTPDdcOption::Flags flags,
const std::string &ip,
int port);
QByteArray serialize() const;
using Ids = std::vector<DcId>;
@ -42,22 +47,22 @@ public:
struct Endpoint {
std::string ip;
int port = 0;
MTPDdcOption::Flags flags = 0;
bytes::vector protocolSecret;
};
struct Variants {
enum {
enum Address {
IPv4 = 0,
IPv6 = 1,
AddressTypeCount = 2,
};
enum {
enum Protocol {
Tcp = 0,
Http = 1,
ProtocolCount = 2,
};
Endpoint data[AddressTypeCount][ProtocolCount];
std::vector<Endpoint> data[AddressTypeCount][ProtocolCount];
};
Variants lookup(DcId dcId, DcType type) const;
Variants lookup(DcId dcId, DcType type, bool throughProxy) const;
DcType dcType(ShiftedDcId shiftedDcId) const;
void setCDNConfig(const MTPDcdnConfig &config);
@ -91,7 +96,7 @@ private:
class ReadLocker;
friend class ReadLocker;
std::map<ShiftedDcId, Option> _data;
std::map<ShiftedDcId, std::vector<Option>> _data;
std::set<DcId> _cdnDcIds;
std::map<uint64, internal::RSAPublicKey> _publicKeys;
std::map<DcId, std::map<uint64, internal::RSAPublicKey>> _cdnPublicKeys;

View File

@ -178,8 +178,7 @@ void Session::refreshDataFields() {
(connectionType == dbictAuto
|| connectionType == dbictHttpProxy);
const auto useIPv4 = true;
const auto useIPv6 = (proxyType != ProxyData::Type::Mtproto) &&
Global::TryIPv6();
const auto useIPv6 = Global::TryIPv6();
data.setConnectionOptions(ConnectionOptions(
_instance->systemLangCode(),
_instance->cloudLangCode(),

View File

@ -422,8 +422,6 @@
<(src_loc)/mtproto/connection.h
<(src_loc)/mtproto/connection_abstract.cpp
<(src_loc)/mtproto/connection_abstract.h
<(src_loc)/mtproto/connection_auto.cpp
<(src_loc)/mtproto/connection_auto.h
<(src_loc)/mtproto/connection_http.cpp
<(src_loc)/mtproto/connection_http.h
<(src_loc)/mtproto/connection_tcp.cpp