Fix mtproto-proxy working with domain names.

Also refactor a bit TcpConnection and HttpConnection classes.
This commit is contained in:
John Preston 2018-05-17 13:27:27 +03:00
parent 694e8cd19f
commit a053384618
9 changed files with 302 additions and 309 deletions

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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));
}
}

View File

@ -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");

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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: