mirror of
https://github.com/telegramdesktop/tdesktop
synced 2024-12-27 17:13:40 +00:00
Fix mtproto-proxy working with domain names.
Also refactor a bit TcpConnection and HttpConnection classes.
This commit is contained in:
parent
694e8cd19f
commit
a053384618
@ -41,7 +41,7 @@ QMap<QString, QString> url_parse_params(
|
||||
bool is_ipv6(const QString &ip) {
|
||||
//static const auto regexp = QRegularExpression("^[a-fA-F0-9:]+$");
|
||||
//return regexp.match(ip).hasMatch();
|
||||
return ip.indexOf(':') >= 0;
|
||||
return ip.indexOf('.') < 0 && ip.indexOf(':') >= 0;
|
||||
}
|
||||
|
||||
} // namespace qthelp
|
||||
|
@ -17,8 +17,6 @@ constexpr str_const AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"; // used in
|
||||
constexpr str_const AppFile = "Telegram";
|
||||
|
||||
enum {
|
||||
MTPShortBufferSize = 65535, // of ints, 256 kb
|
||||
MTPPacketSizeMax = 67108864, // 64 mb
|
||||
MTPIdsBufferSize = 400, // received msgIds and wereAcked msgIds count stored
|
||||
MTPCheckResendTimeout = 10000, // how much time passed from send till we resend request or check it's state, in ms
|
||||
MTPCheckResendWaiting = 1000, // how much time to wait for some more requests, when resending request or checking it's state, in ms
|
||||
|
@ -38,6 +38,7 @@ class HistoryInner
|
||||
: public Ui::RpWidget
|
||||
, public Ui::AbstractTooltipShower
|
||||
, private base::Subscriber {
|
||||
// The Q_OBJECT meta info is used for qobject_cast to HistoryInner!
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -130,9 +130,9 @@ ConnectionPointer AbstractConnection::create(
|
||||
DcOptions::Variants::Protocol protocol,
|
||||
QThread *thread) {
|
||||
if (protocol == DcOptions::Variants::Tcp) {
|
||||
return ConnectionPointer(new TCPConnection(thread));
|
||||
return ConnectionPointer(new TcpConnection(thread));
|
||||
} else {
|
||||
return ConnectionPointer(new HTTPConnection(thread));
|
||||
return ConnectionPointer(new HttpConnection(thread));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,75 @@ constexpr auto kForceHttpPort = 80;
|
||||
|
||||
} // namespace
|
||||
|
||||
mtpBuffer HTTPConnection::handleResponse(QNetworkReply *reply) {
|
||||
HttpConnection::HttpConnection(QThread *thread)
|
||||
: AbstractConnection(thread)
|
||||
, _checkNonce(rand_value<MTPint128>()) {
|
||||
_manager.moveToThread(thread);
|
||||
}
|
||||
|
||||
void HttpConnection::setProxyOverride(const ProxyData &proxy) {
|
||||
_manager.setProxy(ToNetworkProxy(proxy));
|
||||
}
|
||||
|
||||
void HttpConnection::sendData(mtpBuffer &buffer) {
|
||||
if (_status == Status::Finished) 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;
|
||||
}
|
||||
|
||||
int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime);
|
||||
|
||||
QNetworkRequest request(url());
|
||||
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 %2").arg(requestSize).arg(Logs::mb(&buffer[2], requestSize).str()));
|
||||
_requests.insert(_manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
|
||||
}
|
||||
|
||||
void HttpConnection::disconnectFromServer() {
|
||||
if (_status == Status::Finished) return;
|
||||
_status = Status::Finished;
|
||||
|
||||
for (const auto request : base::take(_requests)) {
|
||||
request->abort();
|
||||
request->deleteLater();
|
||||
}
|
||||
|
||||
disconnect(
|
||||
&_manager,
|
||||
&QNetworkAccessManager::finished,
|
||||
this,
|
||||
&HttpConnection::requestFinished);
|
||||
}
|
||||
|
||||
void HttpConnection::connectToServer(
|
||||
const QString &address,
|
||||
int port,
|
||||
const bytes::vector &protocolSecret,
|
||||
int16 protocolDcId) {
|
||||
_address = address;
|
||||
TCP_LOG(("HTTP Info: address is %1").arg(url().toDisplayString()));
|
||||
connect(
|
||||
&_manager,
|
||||
&QNetworkAccessManager::finished,
|
||||
this,
|
||||
&HttpConnection::requestFinished);
|
||||
|
||||
mtpBuffer buffer(preparePQFake(_checkNonce));
|
||||
|
||||
DEBUG_LOG(("Connection Info: "
|
||||
"sending fake req_pq through HTTP transport to '%1'").arg(address));
|
||||
|
||||
_pingTime = getms();
|
||||
sendData(buffer);
|
||||
}
|
||||
|
||||
mtpBuffer HttpConnection::handleResponse(QNetworkReply *reply) {
|
||||
QByteArray response = reply->readAll();
|
||||
TCP_LOG(("HTTP Info: read %1 bytes").arg(response.size()));
|
||||
|
||||
@ -34,7 +102,7 @@ mtpBuffer HTTPConnection::handleResponse(QNetworkReply *reply) {
|
||||
return data;
|
||||
}
|
||||
|
||||
qint32 HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad key"
|
||||
qint32 HttpConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad key"
|
||||
auto result = qint32(kErrorCodeOther);
|
||||
|
||||
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
@ -81,92 +149,31 @@ qint32 HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe ba
|
||||
return result;
|
||||
}
|
||||
|
||||
HTTPConnection::HTTPConnection(QThread *thread) : AbstractConnection(thread)
|
||||
, status(WaitingHttp)
|
||||
, httpNonce(rand_value<MTPint128>()) {
|
||||
manager.moveToThread(thread);
|
||||
bool HttpConnection::isConnected() const {
|
||||
return (_status == Status::Ready);
|
||||
}
|
||||
|
||||
void HTTPConnection::setProxyOverride(const ProxyData &proxy) {
|
||||
manager.setProxy(ToNetworkProxy(proxy));
|
||||
}
|
||||
|
||||
void HTTPConnection::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;
|
||||
}
|
||||
|
||||
int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime);
|
||||
|
||||
QNetworkRequest request(url());
|
||||
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 %2").arg(requestSize).arg(Logs::mb(&buffer[2], requestSize).str()));
|
||||
requests.insert(manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize)));
|
||||
}
|
||||
|
||||
void HTTPConnection::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*)));
|
||||
}
|
||||
|
||||
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 transport to %1").arg(ip));
|
||||
|
||||
_pingTime = getms();
|
||||
sendData(buffer);
|
||||
}
|
||||
|
||||
bool HTTPConnection::isConnected() const {
|
||||
return (status == UsingHttp);
|
||||
}
|
||||
|
||||
void HTTPConnection::requestFinished(QNetworkReply *reply) {
|
||||
if (status == FinishedWork) return;
|
||||
void HttpConnection::requestFinished(QNetworkReply *reply) {
|
||||
if (_status == Status::Finished) return;
|
||||
|
||||
reply->deleteLater();
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
requests.remove(reply);
|
||||
_requests.remove(reply);
|
||||
|
||||
mtpBuffer data = handleResponse(reply);
|
||||
if (data.size() == 1) {
|
||||
emit error(data[0]);
|
||||
} else if (!data.isEmpty()) {
|
||||
if (status == UsingHttp) {
|
||||
if (_status == Status::Ready) {
|
||||
_receivedQueue.push_back(data);
|
||||
emit receivedData();
|
||||
} else {
|
||||
try {
|
||||
auto res_pq = readPQFakeReply(data);
|
||||
const auto &res_pq_data(res_pq.c_resPQ());
|
||||
if (res_pq_data.vnonce == httpNonce) {
|
||||
if (res_pq_data.vnonce == _checkNonce) {
|
||||
DEBUG_LOG(("Connection Info: HTTP-transport to %1 connected by pq-response").arg(_address));
|
||||
status = UsingHttp;
|
||||
_status = Status::Ready;
|
||||
_pingTime = getms() - _pingTime;
|
||||
emit connected();
|
||||
}
|
||||
@ -177,7 +184,7 @@ void HTTPConnection::requestFinished(QNetworkReply *reply) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!requests.remove(reply)) {
|
||||
if (!_requests.remove(reply)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -185,23 +192,23 @@ void HTTPConnection::requestFinished(QNetworkReply *reply) {
|
||||
}
|
||||
}
|
||||
|
||||
TimeMs HTTPConnection::pingTime() const {
|
||||
TimeMs HttpConnection::pingTime() const {
|
||||
return isConnected() ? _pingTime : TimeMs(0);
|
||||
}
|
||||
|
||||
bool HTTPConnection::usingHttpWait() {
|
||||
bool HttpConnection::usingHttpWait() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HTTPConnection::needHttpWait() {
|
||||
return requests.isEmpty();
|
||||
bool HttpConnection::needHttpWait() {
|
||||
return _requests.isEmpty();
|
||||
}
|
||||
|
||||
int32 HTTPConnection::debugState() const {
|
||||
int32 HttpConnection::debugState() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString HTTPConnection::transport() const {
|
||||
QString HttpConnection::transport() const {
|
||||
if (!isConnected()) {
|
||||
return QString();
|
||||
}
|
||||
@ -212,7 +219,7 @@ QString HTTPConnection::transport() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
QString HTTPConnection::tag() const {
|
||||
QString HttpConnection::tag() const {
|
||||
auto result = qsl("HTTP");
|
||||
if (qthelp::is_ipv6(_address)) {
|
||||
result += qsl("/IPv6");
|
||||
@ -222,7 +229,7 @@ QString HTTPConnection::tag() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
QUrl HTTPConnection::url() const {
|
||||
QUrl HttpConnection::url() const {
|
||||
const auto pattern = qthelp::is_ipv6(_address)
|
||||
? qsl("http://[%1]:%2/api")
|
||||
: qsl("http://%1:%2/api");
|
||||
|
@ -12,18 +12,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace MTP {
|
||||
namespace internal {
|
||||
|
||||
class HTTPConnection : public AbstractConnection {
|
||||
Q_OBJECT
|
||||
|
||||
class HttpConnection : public AbstractConnection {
|
||||
public:
|
||||
HTTPConnection(QThread *thread);
|
||||
HttpConnection(QThread *thread);
|
||||
|
||||
void setProxyOverride(const ProxyData &proxy) override;
|
||||
TimeMs pingTime() const override;
|
||||
void sendData(mtpBuffer &buffer) override;
|
||||
void disconnectFromServer() override;
|
||||
void connectToServer(
|
||||
const QString &ip,
|
||||
const QString &address,
|
||||
int port,
|
||||
const bytes::vector &protocolSecret,
|
||||
int16 protocolDcId) override;
|
||||
@ -39,25 +37,23 @@ public:
|
||||
static mtpBuffer handleResponse(QNetworkReply *reply);
|
||||
static qint32 handleError(QNetworkReply *reply); // returnes error code
|
||||
|
||||
public slots:
|
||||
void requestFinished(QNetworkReply *reply);
|
||||
|
||||
private:
|
||||
QUrl url() const;
|
||||
|
||||
enum Status {
|
||||
WaitingHttp = 0,
|
||||
UsingHttp,
|
||||
FinishedWork
|
||||
};
|
||||
Status status;
|
||||
MTPint128 httpNonce;
|
||||
void requestFinished(QNetworkReply *reply);
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
enum class Status {
|
||||
Waiting = 0,
|
||||
Ready,
|
||||
Finished,
|
||||
};
|
||||
Status _status = Status::Waiting;
|
||||
MTPint128 _checkNonce;
|
||||
|
||||
QNetworkAccessManager _manager;
|
||||
QString _address;
|
||||
|
||||
typedef QSet<QNetworkReply*> Requests;
|
||||
Requests requests;
|
||||
QSet<QNetworkReply*> _requests;
|
||||
|
||||
TimeMs _pingTime = 0;
|
||||
|
||||
|
@ -18,8 +18,9 @@ namespace {
|
||||
|
||||
constexpr auto kMinReceiveTimeout = TimeMs(2000);
|
||||
constexpr auto kMaxReceiveTimeout = TimeMs(8000);
|
||||
constexpr auto kPacketSizeMax = 64 * 1024 * 1024U;
|
||||
|
||||
uint32 tcpPacketSize(const char *packet) { // must have at least 4 bytes readable
|
||||
uint32 CountTcpPacketSize(const char *packet) { // must have at least 4 bytes readable
|
||||
uint32 result = (packet[0] > 0) ? packet[0] : 0;
|
||||
if (result == 0x7f) {
|
||||
const uchar *bytes = reinterpret_cast<const uchar*>(packet);
|
||||
@ -29,92 +30,113 @@ uint32 tcpPacketSize(const char *packet) { // must have at least 4 bytes readabl
|
||||
return (result << 2) + 1;
|
||||
}
|
||||
|
||||
using ErrorSignal = void(QTcpSocket::*)(QAbstractSocket::SocketError);
|
||||
const auto QTcpSocket_error = ErrorSignal(&QAbstractSocket::error);
|
||||
|
||||
} // namespace
|
||||
|
||||
AbstractTCPConnection::AbstractTCPConnection(
|
||||
QThread *thread)
|
||||
TcpConnection::TcpConnection(QThread *thread)
|
||||
: AbstractConnection(thread)
|
||||
, currentPos((char*)shortBuffer) {
|
||||
, _currentPosition(reinterpret_cast<char*>(_shortBuffer))
|
||||
, _checkNonce(rand_value<MTPint128>())
|
||||
, _timeout(kMinReceiveTimeout)
|
||||
, _timeoutTimer(thread, [=] { handleTimeout(); }) {
|
||||
_socket.moveToThread(thread);
|
||||
connect(&_socket, QTcpSocket_error, this, &TcpConnection::socketError);
|
||||
connect(
|
||||
&_socket,
|
||||
&QTcpSocket::connected,
|
||||
this,
|
||||
&TcpConnection::socketConnected);
|
||||
connect(
|
||||
&_socket,
|
||||
&QTcpSocket::disconnected,
|
||||
this,
|
||||
&TcpConnection::socketDisconnected);
|
||||
}
|
||||
|
||||
void AbstractTCPConnection::setProxyOverride(const ProxyData &proxy) {
|
||||
sock.setProxy(ToNetworkProxy(proxy));
|
||||
void TcpConnection::setProxyOverride(const ProxyData &proxy) {
|
||||
_socket.setProxy(ToNetworkProxy(proxy));
|
||||
}
|
||||
|
||||
AbstractTCPConnection::~AbstractTCPConnection() = default;
|
||||
|
||||
void AbstractTCPConnection::socketRead() {
|
||||
if (sock.state() != QAbstractSocket::ConnectedState) {
|
||||
LOG(("MTP error: socket not connected in socketRead(), state: %1").arg(sock.state()));
|
||||
void TcpConnection::socketRead() {
|
||||
if (_socket.state() != QAbstractSocket::ConnectedState) {
|
||||
LOG(("MTP error: "
|
||||
"socket not connected in socketRead(), state: %1"
|
||||
).arg(_socket.state()));
|
||||
emit error(kErrorCodeOther);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
uint32 toRead = packetLeft ? packetLeft : (readingToShort ? (MTPShortBufferSize * sizeof(mtpPrime) - packetRead) : 4);
|
||||
if (readingToShort) {
|
||||
if (currentPos + toRead > ((char*)shortBuffer) + MTPShortBufferSize * sizeof(mtpPrime)) {
|
||||
longBuffer.resize(((packetRead + toRead) >> 2) + 1);
|
||||
memcpy(&longBuffer[0], shortBuffer, packetRead);
|
||||
currentPos = ((char*)&longBuffer[0]) + packetRead;
|
||||
readingToShort = false;
|
||||
uint32 toRead = _packetLeft
|
||||
? _packetLeft
|
||||
: (_readingToShort
|
||||
? (kShortBufferSize * sizeof(mtpPrime) - _packetRead)
|
||||
: 4);
|
||||
if (_readingToShort) {
|
||||
if (_currentPosition + toRead > ((char*)_shortBuffer) + kShortBufferSize * sizeof(mtpPrime)) {
|
||||
_longBuffer.resize(((_packetRead + toRead) >> 2) + 1);
|
||||
memcpy(&_longBuffer[0], _shortBuffer, _packetRead);
|
||||
_currentPosition = ((char*)&_longBuffer[0]) + _packetRead;
|
||||
_readingToShort = false;
|
||||
}
|
||||
} else {
|
||||
if (longBuffer.size() * sizeof(mtpPrime) < packetRead + toRead) {
|
||||
longBuffer.resize(((packetRead + toRead) >> 2) + 1);
|
||||
currentPos = ((char*)&longBuffer[0]) + packetRead;
|
||||
if (_longBuffer.size() * sizeof(mtpPrime) < _packetRead + toRead) {
|
||||
_longBuffer.resize(((_packetRead + toRead) >> 2) + 1);
|
||||
_currentPosition = ((char*)&_longBuffer[0]) + _packetRead;
|
||||
}
|
||||
}
|
||||
int32 bytes = (int32)sock.read(currentPos, toRead);
|
||||
int32 bytes = (int32)_socket.read(_currentPosition, toRead);
|
||||
if (bytes > 0) {
|
||||
aesCtrEncrypt(currentPos, bytes, _receiveKey, &_receiveState);
|
||||
aesCtrEncrypt(_currentPosition, bytes, _receiveKey, &_receiveState);
|
||||
TCP_LOG(("TCP Info: read %1 bytes").arg(bytes));
|
||||
|
||||
packetRead += bytes;
|
||||
currentPos += bytes;
|
||||
if (packetLeft) {
|
||||
packetLeft -= bytes;
|
||||
if (!packetLeft) {
|
||||
socketPacket(currentPos - packetRead, packetRead);
|
||||
currentPos = (char*)shortBuffer;
|
||||
packetRead = packetLeft = 0;
|
||||
readingToShort = true;
|
||||
longBuffer.clear();
|
||||
_packetRead += bytes;
|
||||
_currentPosition += bytes;
|
||||
if (_packetLeft) {
|
||||
_packetLeft -= bytes;
|
||||
if (!_packetLeft) {
|
||||
socketPacket(_currentPosition - _packetRead, _packetRead);
|
||||
_currentPosition = (char*)_shortBuffer;
|
||||
_packetRead = _packetLeft = 0;
|
||||
_readingToShort = true;
|
||||
_longBuffer.clear();
|
||||
} else {
|
||||
TCP_LOG(("TCP Info: not enough %1 for packet! read %2").arg(packetLeft).arg(packetRead));
|
||||
TCP_LOG(("TCP Info: not enough %1 for packet! read %2").arg(_packetLeft).arg(_packetRead));
|
||||
emit receivedSome();
|
||||
}
|
||||
} else {
|
||||
bool move = false;
|
||||
while (packetRead >= 4) {
|
||||
uint32 packetSize = tcpPacketSize(currentPos - packetRead);
|
||||
if (packetSize < 5 || packetSize > MTPPacketSizeMax) {
|
||||
while (_packetRead >= 4) {
|
||||
uint32 packetSize = CountTcpPacketSize(_currentPosition - _packetRead);
|
||||
if (packetSize < 5 || packetSize > kPacketSizeMax) {
|
||||
LOG(("TCP Error: packet size = %1").arg(packetSize));
|
||||
emit error(kErrorCodeOther);
|
||||
return;
|
||||
}
|
||||
if (packetRead >= packetSize) {
|
||||
socketPacket(currentPos - packetRead, packetSize);
|
||||
packetRead -= packetSize;
|
||||
packetLeft = 0;
|
||||
if (_packetRead >= packetSize) {
|
||||
socketPacket(_currentPosition - _packetRead, packetSize);
|
||||
_packetRead -= packetSize;
|
||||
_packetLeft = 0;
|
||||
move = true;
|
||||
} else {
|
||||
packetLeft = packetSize - packetRead;
|
||||
TCP_LOG(("TCP Info: not enough %1 for packet! size %2 read %3").arg(packetLeft).arg(packetSize).arg(packetRead));
|
||||
_packetLeft = packetSize - _packetRead;
|
||||
TCP_LOG(("TCP Info: not enough %1 for packet! size %2 read %3").arg(_packetLeft).arg(packetSize).arg(_packetRead));
|
||||
emit receivedSome();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (move) {
|
||||
if (!packetRead) {
|
||||
currentPos = (char*)shortBuffer;
|
||||
readingToShort = true;
|
||||
longBuffer.clear();
|
||||
} else if (!readingToShort && packetRead < MTPShortBufferSize * sizeof(mtpPrime)) {
|
||||
memcpy(shortBuffer, currentPos - packetRead, packetRead);
|
||||
currentPos = (char*)shortBuffer + packetRead;
|
||||
readingToShort = true;
|
||||
longBuffer.clear();
|
||||
if (!_packetRead) {
|
||||
_currentPosition = (char*)_shortBuffer;
|
||||
_readingToShort = true;
|
||||
_longBuffer.clear();
|
||||
} else if (!_readingToShort && _packetRead < kShortBufferSize * sizeof(mtpPrime)) {
|
||||
memcpy(_shortBuffer, _currentPosition - _packetRead, _packetRead);
|
||||
_currentPosition = (char*)_shortBuffer + _packetRead;
|
||||
_readingToShort = true;
|
||||
_longBuffer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,11 +148,11 @@ void AbstractTCPConnection::socketRead() {
|
||||
TCP_LOG(("TCP Info: no bytes read, but bytes available was true..."));
|
||||
break;
|
||||
}
|
||||
} while (sock.state() == QAbstractSocket::ConnectedState && sock.bytesAvailable());
|
||||
} while (_socket.state() == QAbstractSocket::ConnectedState && _socket.bytesAvailable());
|
||||
}
|
||||
|
||||
mtpBuffer AbstractTCPConnection::handleResponse(const char *packet, uint32 length) {
|
||||
if (length < 5 || length > MTPPacketSizeMax) {
|
||||
mtpBuffer TcpConnection::handleResponse(const char *packet, uint32 length) {
|
||||
if (length < 5 || length > kPacketSizeMax) {
|
||||
LOG(("TCP Error: bad packet size %1").arg(length));
|
||||
return mtpBuffer(1, -500);
|
||||
}
|
||||
@ -158,26 +180,26 @@ mtpBuffer AbstractTCPConnection::handleResponse(const char *packet, uint32 lengt
|
||||
return data;
|
||||
}
|
||||
|
||||
void AbstractTCPConnection::handleError(QAbstractSocket::SocketError e, QTcpSocket &sock) {
|
||||
void TcpConnection::handleError(QAbstractSocket::SocketError e, QTcpSocket &socket) {
|
||||
switch (e) {
|
||||
case QAbstractSocket::ConnectionRefusedError:
|
||||
LOG(("TCP Error: socket connection refused - %1").arg(sock.errorString()));
|
||||
LOG(("TCP Error: socket connection refused - %1").arg(socket.errorString()));
|
||||
break;
|
||||
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
TCP_LOG(("TCP Info: remote host closed socket connection - %1").arg(sock.errorString()));
|
||||
TCP_LOG(("TCP Info: remote host closed socket connection - %1").arg(socket.errorString()));
|
||||
break;
|
||||
|
||||
case QAbstractSocket::HostNotFoundError:
|
||||
LOG(("TCP Error: host not found - %1").arg(sock.errorString()));
|
||||
LOG(("TCP Error: host not found - %1").arg(socket.errorString()));
|
||||
break;
|
||||
|
||||
case QAbstractSocket::SocketTimeoutError:
|
||||
LOG(("TCP Error: socket timeout - %1").arg(sock.errorString()));
|
||||
LOG(("TCP Error: socket timeout - %1").arg(socket.errorString()));
|
||||
break;
|
||||
|
||||
case QAbstractSocket::NetworkError:
|
||||
LOG(("TCP Error: network - %1").arg(sock.errorString()));
|
||||
LOG(("TCP Error: network - %1").arg(socket.errorString()));
|
||||
break;
|
||||
|
||||
case QAbstractSocket::ProxyAuthenticationRequiredError:
|
||||
@ -186,77 +208,62 @@ void AbstractTCPConnection::handleError(QAbstractSocket::SocketError e, QTcpSock
|
||||
case QAbstractSocket::ProxyConnectionTimeoutError:
|
||||
case QAbstractSocket::ProxyNotFoundError:
|
||||
case QAbstractSocket::ProxyProtocolError:
|
||||
LOG(("TCP Error: proxy (%1) - %2").arg(e).arg(sock.errorString()));
|
||||
LOG(("TCP Error: proxy (%1) - %2").arg(e).arg(socket.errorString()));
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG(("TCP Error: other (%1) - %2").arg(e).arg(sock.errorString()));
|
||||
LOG(("TCP Error: other (%1) - %2").arg(e).arg(socket.errorString()));
|
||||
break;
|
||||
}
|
||||
|
||||
TCP_LOG(("TCP Error %1, restarting! - %2").arg(e).arg(sock.errorString()));
|
||||
TCP_LOG(("TCP Error %1, restarting! - %2").arg(e).arg(socket.errorString()));
|
||||
}
|
||||
|
||||
TCPConnection::TCPConnection(QThread *thread)
|
||||
: AbstractTCPConnection(thread)
|
||||
, status(WaitingTcp)
|
||||
, tcpNonce(rand_value<MTPint128>())
|
||||
, _tcpTimeout(kMinReceiveTimeout) {
|
||||
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 TCPConnection::onSocketConnected() {
|
||||
if (status == WaitingTcp) {
|
||||
mtpBuffer buffer(preparePQFake(tcpNonce));
|
||||
void TcpConnection::socketConnected() {
|
||||
if (_status == Status::Waiting) {
|
||||
mtpBuffer buffer(preparePQFake(_checkNonce));
|
||||
|
||||
DEBUG_LOG(("Connection Info: sending fake req_pq through TCP transport to %1").arg(_address));
|
||||
|
||||
if (_tcpTimeout < 0) _tcpTimeout = -_tcpTimeout;
|
||||
tcpTimeoutTimer.start(_tcpTimeout);
|
||||
if (_timeout < 0) _timeout = -_timeout;
|
||||
_timeoutTimer.callOnce(_timeout);
|
||||
|
||||
_pingTime = getms();
|
||||
sendData(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void TCPConnection::onTcpTimeoutTimer() {
|
||||
if (status == WaitingTcp) {
|
||||
if (_tcpTimeout < kMaxReceiveTimeout) {
|
||||
_tcpTimeout *= 2;
|
||||
void TcpConnection::handleTimeout() {
|
||||
if (_status == Status::Waiting) {
|
||||
if (_timeout < kMaxReceiveTimeout) {
|
||||
_timeout *= 2;
|
||||
}
|
||||
_tcpTimeout = -_tcpTimeout;
|
||||
_timeout = -_timeout;
|
||||
|
||||
QAbstractSocket::SocketState state = sock.state();
|
||||
QAbstractSocket::SocketState state = _socket.state();
|
||||
if (state == QAbstractSocket::ConnectedState || state == QAbstractSocket::ConnectingState || state == QAbstractSocket::HostLookupState) {
|
||||
sock.disconnectFromHost();
|
||||
_socket.disconnectFromHost();
|
||||
} else if (state != QAbstractSocket::ClosingState) {
|
||||
sock.connectToHost(QHostAddress(_address), _port);
|
||||
_socket.connectToHost(_address, _port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TCPConnection::onSocketDisconnected() {
|
||||
if (_tcpTimeout < 0) {
|
||||
_tcpTimeout = -_tcpTimeout;
|
||||
if (status == WaitingTcp) {
|
||||
sock.connectToHost(QHostAddress(_address), _port);
|
||||
void TcpConnection::socketDisconnected() {
|
||||
if (_timeout < 0) {
|
||||
_timeout = -_timeout;
|
||||
if (_status == Status::Waiting) {
|
||||
_socket.connectToHost(_address, _port);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (status == WaitingTcp || status == UsingTcp) {
|
||||
if (_status == Status::Waiting || _status == Status::Ready) {
|
||||
emit disconnected();
|
||||
}
|
||||
}
|
||||
|
||||
void TCPConnection::sendData(mtpBuffer &buffer) {
|
||||
if (status == FinishedWork) return;
|
||||
void TcpConnection::sendData(mtpBuffer &buffer) {
|
||||
if (_status == Status::Finished) return;
|
||||
|
||||
if (buffer.size() < 3) {
|
||||
LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime)));
|
||||
@ -265,10 +272,10 @@ void TCPConnection::sendData(mtpBuffer &buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
tcpSend(buffer);
|
||||
sendBuffer(buffer);
|
||||
}
|
||||
|
||||
void AbstractTCPConnection::writeConnectionStart() {
|
||||
void TcpConnection::writeConnectionStart() {
|
||||
// prepare random part
|
||||
auto nonceBytes = bytes::vector(64);
|
||||
const auto nonce = bytes::make_span(nonceBytes);
|
||||
@ -280,8 +287,7 @@ void AbstractTCPConnection::writeConnectionStart() {
|
||||
const auto reserved11 = 0x44414548U;
|
||||
const auto reserved12 = 0x54534F50U;
|
||||
const auto reserved13 = 0x20544547U;
|
||||
const auto reserved14 = 0x20544547U;
|
||||
const auto reserved15 = 0xEEEEEEEEU;
|
||||
const auto reserved14 = 0xEEEEEEEEU;
|
||||
const auto reserved21 = 0x00000000U;
|
||||
do {
|
||||
bytes::set_random(nonce);
|
||||
@ -290,7 +296,6 @@ void AbstractTCPConnection::writeConnectionStart() {
|
||||
|| *first == reserved12
|
||||
|| *first == reserved13
|
||||
|| *first == reserved14
|
||||
|| *first == reserved15
|
||||
|| *second == reserved21);
|
||||
|
||||
const auto prepareKey = [&](bytes::span key, bytes::const_span from) {
|
||||
@ -330,80 +335,79 @@ void AbstractTCPConnection::writeConnectionStart() {
|
||||
const auto dcId = reinterpret_cast<int16*>(nonce.data() + 60);
|
||||
*dcId = _protocolDcId;
|
||||
|
||||
sock.write(reinterpret_cast<const char*>(nonce.data()), 56);
|
||||
_socket.write(reinterpret_cast<const char*>(nonce.data()), 56);
|
||||
aesCtrEncrypt(nonce.data(), 64, _sendKey, &_sendState);
|
||||
sock.write(reinterpret_cast<const char*>(nonce.subspan(56).data()), 8);
|
||||
_socket.write(reinterpret_cast<const char*>(nonce.subspan(56).data()), 8);
|
||||
}
|
||||
|
||||
void AbstractTCPConnection::tcpSend(mtpBuffer &buffer) {
|
||||
if (!packetNum) {
|
||||
void TcpConnection::sendBuffer(mtpBuffer &buffer) {
|
||||
if (!_packetIndex++) {
|
||||
writeConnectionStart();
|
||||
}
|
||||
++packetNum;
|
||||
|
||||
uint32 size = buffer.size() - 3, len = size * 4;
|
||||
char *data = reinterpret_cast<char*>(&buffer[0]);
|
||||
if (size < 0x7f) {
|
||||
data[7] = char(size);
|
||||
TCP_LOG(("TCP Info: write %1 packet %2").arg(packetNum).arg(len + 1));
|
||||
TCP_LOG(("TCP Info: write %1 packet %2").arg(_packetIndex).arg(len + 1));
|
||||
|
||||
aesCtrEncrypt(data + 7, len + 1, _sendKey, &_sendState);
|
||||
sock.write(data + 7, len + 1);
|
||||
_socket.write(data + 7, len + 1);
|
||||
} else {
|
||||
data[4] = 0x7f;
|
||||
reinterpret_cast<uchar*>(data)[5] = uchar(size & 0xFF);
|
||||
reinterpret_cast<uchar*>(data)[6] = uchar((size >> 8) & 0xFF);
|
||||
reinterpret_cast<uchar*>(data)[7] = uchar((size >> 16) & 0xFF);
|
||||
TCP_LOG(("TCP Info: write %1 packet %2").arg(packetNum).arg(len + 4));
|
||||
TCP_LOG(("TCP Info: write %1 packet %2").arg(_packetIndex).arg(len + 4));
|
||||
|
||||
aesCtrEncrypt(data + 4, len + 4, _sendKey, &_sendState);
|
||||
sock.write(data + 4, len + 4);
|
||||
_socket.write(data + 4, len + 4);
|
||||
}
|
||||
}
|
||||
|
||||
void TCPConnection::disconnectFromServer() {
|
||||
if (status == FinishedWork) return;
|
||||
status = FinishedWork;
|
||||
void TcpConnection::disconnectFromServer() {
|
||||
if (_status == Status::Finished) return;
|
||||
_status = Status::Finished;
|
||||
|
||||
disconnect(&sock, SIGNAL(readyRead()), 0, 0);
|
||||
sock.close();
|
||||
disconnect(&_socket, &QTcpSocket::readyRead, nullptr, nullptr);
|
||||
_socket.close();
|
||||
}
|
||||
|
||||
void TCPConnection::connectToServer(
|
||||
const QString &ip,
|
||||
void TcpConnection::connectToServer(
|
||||
const QString &address,
|
||||
int port,
|
||||
const bytes::vector &protocolSecret,
|
||||
int16 protocolDcId) {
|
||||
_address = ip;
|
||||
_address = address;
|
||||
_port = port;
|
||||
_protocolSecret = protocolSecret;
|
||||
_protocolDcId = protocolDcId;
|
||||
|
||||
connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead()));
|
||||
sock.connectToHost(QHostAddress(_address), _port);
|
||||
connect(&_socket, &QTcpSocket::readyRead, this, &TcpConnection::socketRead);
|
||||
_socket.connectToHost(_address, _port);
|
||||
}
|
||||
|
||||
TimeMs TCPConnection::pingTime() const {
|
||||
TimeMs TcpConnection::pingTime() const {
|
||||
return isConnected() ? _pingTime : TimeMs(0);
|
||||
}
|
||||
|
||||
void TCPConnection::socketPacket(const char *packet, uint32 length) {
|
||||
if (status == FinishedWork) return;
|
||||
void TcpConnection::socketPacket(const char *packet, uint32 length) {
|
||||
if (_status == Status::Finished) return;
|
||||
|
||||
mtpBuffer data = handleResponse(packet, length);
|
||||
if (data.size() == 1) {
|
||||
emit error(data[0]);
|
||||
} else if (status == UsingTcp) {
|
||||
} else if (_status == Status::Ready) {
|
||||
_receivedQueue.push_back(data);
|
||||
emit receivedData();
|
||||
} else if (status == WaitingTcp) {
|
||||
tcpTimeoutTimer.stop();
|
||||
} else if (_status == Status::Waiting) {
|
||||
_timeoutTimer.cancel();
|
||||
try {
|
||||
auto res_pq = readPQFakeReply(data);
|
||||
const auto &res_pq_data(res_pq.c_resPQ());
|
||||
if (res_pq_data.vnonce == tcpNonce) {
|
||||
if (res_pq_data.vnonce == _checkNonce) {
|
||||
DEBUG_LOG(("Connection Info: TCP-transport to %1 chosen by pq-response").arg(_address));
|
||||
status = UsingTcp;
|
||||
_status = Status::Ready;
|
||||
_pingTime = (getms() - _pingTime);
|
||||
emit connected();
|
||||
}
|
||||
@ -414,15 +418,15 @@ void TCPConnection::socketPacket(const char *packet, uint32 length) {
|
||||
}
|
||||
}
|
||||
|
||||
bool TCPConnection::isConnected() const {
|
||||
return (status == UsingTcp);
|
||||
bool TcpConnection::isConnected() const {
|
||||
return (_status == Status::Ready);
|
||||
}
|
||||
|
||||
int32 TCPConnection::debugState() const {
|
||||
return sock.state();
|
||||
int32 TcpConnection::debugState() const {
|
||||
return _socket.state();
|
||||
}
|
||||
|
||||
QString TCPConnection::transport() const {
|
||||
QString TcpConnection::transport() const {
|
||||
if (!isConnected()) {
|
||||
return QString();
|
||||
}
|
||||
@ -433,7 +437,7 @@ QString TCPConnection::transport() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
QString TCPConnection::tag() const {
|
||||
QString TcpConnection::tag() const {
|
||||
auto result = qsl("TCP");
|
||||
if (qthelp::is_ipv6(_address)) {
|
||||
result += qsl("/IPv6");
|
||||
@ -443,10 +447,10 @@ QString TCPConnection::tag() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
void TCPConnection::socketError(QAbstractSocket::SocketError e) {
|
||||
if (status == FinishedWork) return;
|
||||
void TcpConnection::socketError(QAbstractSocket::SocketError e) {
|
||||
if (_status == Status::Finished) return;
|
||||
|
||||
handleError(e, sock);
|
||||
handleError(e, _socket);
|
||||
emit error(kErrorCodeOther);
|
||||
}
|
||||
|
||||
|
@ -9,64 +9,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "mtproto/auth_key.h"
|
||||
#include "mtproto/connection_abstract.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace MTP {
|
||||
namespace internal {
|
||||
|
||||
class AbstractTCPConnection : public AbstractConnection {
|
||||
Q_OBJECT
|
||||
|
||||
class TcpConnection : public AbstractConnection {
|
||||
public:
|
||||
AbstractTCPConnection(QThread *thread);
|
||||
TcpConnection(QThread *thread);
|
||||
|
||||
void setProxyOverride(const ProxyData &proxy) override;
|
||||
virtual ~AbstractTCPConnection() = 0;
|
||||
|
||||
public slots:
|
||||
void socketRead();
|
||||
|
||||
protected:
|
||||
void writeConnectionStart();
|
||||
|
||||
QTcpSocket sock;
|
||||
uint32 packetNum = 0; // sent packet number
|
||||
|
||||
uint32 packetRead = 0;
|
||||
uint32 packetLeft = 0; // reading from socket
|
||||
bool readingToShort = true;
|
||||
char *currentPos;
|
||||
mtpBuffer longBuffer;
|
||||
mtpPrime shortBuffer[MTPShortBufferSize];
|
||||
virtual void socketPacket(const char *packet, uint32 length) = 0;
|
||||
|
||||
static mtpBuffer handleResponse(const char *packet, uint32 length);
|
||||
static void handleError(QAbstractSocket::SocketError e, QTcpSocket &sock);
|
||||
static uint32 fourCharsToUInt(char ch1, char ch2, char ch3, char ch4) {
|
||||
char ch[4] = { ch1, ch2, ch3, ch4 };
|
||||
return *reinterpret_cast<uint32*>(ch);
|
||||
}
|
||||
|
||||
void tcpSend(mtpBuffer &buffer);
|
||||
uchar _sendKey[CTRState::KeySize];
|
||||
CTRState _sendState;
|
||||
uchar _receiveKey[CTRState::KeySize];
|
||||
CTRState _receiveState;
|
||||
int16 _protocolDcId = 0;
|
||||
bytes::vector _protocolSecret;
|
||||
|
||||
};
|
||||
|
||||
class TCPConnection : public AbstractTCPConnection {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TCPConnection(QThread *thread);
|
||||
|
||||
TimeMs pingTime() const override;
|
||||
void sendData(mtpBuffer &buffer) override;
|
||||
void disconnectFromServer() override;
|
||||
void connectToServer(
|
||||
const QString &ip,
|
||||
const QString &address,
|
||||
int port,
|
||||
const bytes::vector &protocolSecret,
|
||||
int16 protocolDcId) override;
|
||||
@ -77,29 +35,57 @@ public:
|
||||
QString transport() const override;
|
||||
QString tag() const override;
|
||||
|
||||
public slots:
|
||||
void socketError(QAbstractSocket::SocketError e);
|
||||
|
||||
void onSocketConnected();
|
||||
void onSocketDisconnected();
|
||||
|
||||
void onTcpTimeoutTimer();
|
||||
|
||||
protected:
|
||||
void socketPacket(const char *packet, uint32 length) override;
|
||||
|
||||
private:
|
||||
enum Status {
|
||||
WaitingTcp = 0,
|
||||
UsingTcp,
|
||||
FinishedWork
|
||||
enum class Status {
|
||||
Waiting = 0,
|
||||
Ready,
|
||||
Finished,
|
||||
};
|
||||
Status status;
|
||||
MTPint128 tcpNonce;
|
||||
static constexpr auto kShortBufferSize = 65535; // Of ints, 256 kb.
|
||||
|
||||
void socketRead();
|
||||
void writeConnectionStart();
|
||||
|
||||
void socketPacket(const char *packet, uint32 length);
|
||||
|
||||
void socketConnected();
|
||||
void socketDisconnected();
|
||||
void socketError(QAbstractSocket::SocketError e);
|
||||
void handleTimeout();
|
||||
|
||||
static mtpBuffer handleResponse(const char *packet, uint32 length);
|
||||
static void handleError(QAbstractSocket::SocketError e, QTcpSocket &sock);
|
||||
static uint32 fourCharsToUInt(char ch1, char ch2, char ch3, char ch4) {
|
||||
char ch[4] = { ch1, ch2, ch3, ch4 };
|
||||
return *reinterpret_cast<uint32*>(ch);
|
||||
}
|
||||
|
||||
void sendBuffer(mtpBuffer &buffer);
|
||||
|
||||
QTcpSocket _socket;
|
||||
uint32 _packetIndex = 0; // sent packet number
|
||||
|
||||
uint32 _packetRead = 0;
|
||||
uint32 _packetLeft = 0; // reading from socket
|
||||
bool _readingToShort = true;
|
||||
mtpBuffer _longBuffer;
|
||||
mtpPrime _shortBuffer[kShortBufferSize];
|
||||
char *_currentPosition = nullptr;
|
||||
|
||||
uchar _sendKey[CTRState::KeySize];
|
||||
CTRState _sendState;
|
||||
uchar _receiveKey[CTRState::KeySize];
|
||||
CTRState _receiveState;
|
||||
int16 _protocolDcId = 0;
|
||||
bytes::vector _protocolSecret;
|
||||
|
||||
Status _status = Status::Waiting;
|
||||
MTPint128 _checkNonce;
|
||||
|
||||
QString _address;
|
||||
int32 _port, _tcpTimeout;
|
||||
QTimer tcpTimeoutTimer;
|
||||
int32 _port = 0;
|
||||
int32 _timeout = 0;
|
||||
base::Timer _timeoutTimer;
|
||||
TimeMs _pingTime = 0;
|
||||
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Platform {
|
||||
|
||||
class MainWindow : public Window::MainWindow {
|
||||
// The Q_OBJECT meta info is used for qobject_cast to MainWindow!
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user