diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index ddbbdaf81701bb..968440d99cdfa6 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -1415,7 +1415,7 @@ void ConnectionPrivate::handleReceived() { } auto encryptedInts = ints + kExternalHeaderIntsCount; - auto encryptedIntsCount = (intsCount - kExternalHeaderIntsCount); + auto encryptedIntsCount = (intsCount - kExternalHeaderIntsCount) & ~0x03U; auto encryptedBytesCount = encryptedIntsCount * kIntSize; auto decryptedBuffer = QByteArray(encryptedBytesCount, Qt::Uninitialized); auto msgKey = *(MTPint128*)(ints + 2); @@ -3062,35 +3062,22 @@ template bool ConnectionPrivate::readNotSecureResponse(Response &response) { onReceivedSome(); - try { - if (_connection->received().empty()) { - LOG(("AuthKey Error: trying to read response from empty received list")); - return false; - } + if (_connection->received().empty()) { + LOG(("AuthKey Error: " + "trying to read response from empty received list")); + return false; + } - auto buffer = std::move(_connection->received().front()); - _connection->received().pop_front(); + const auto buffer = std::move(_connection->received().front()); + _connection->received().pop_front(); - auto answer = buffer.constData(); - auto len = buffer.size(); - if (len < 5) { - LOG(("AuthKey Error: bad request answer, len = %1").arg(len * sizeof(mtpPrime))); - DEBUG_LOG(("AuthKey Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); - return false; - } - if (answer[0] != 0 || answer[1] != 0 || (((uint32)answer[2]) & 0x03) != 1/* || (unixtime() - answer[3] > 300) || (answer[3] - unixtime() > 60)*/) { // didnt sync time yet - LOG(("AuthKey Error: bad request answer start (%1 %2 %3)").arg(answer[0]).arg(answer[1]).arg(answer[2])); - DEBUG_LOG(("AuthKey Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); - return false; - } - uint32 answerLen = (uint32)answer[4]; - if (answerLen != (len - 5) * sizeof(mtpPrime)) { - LOG(("AuthKey Error: bad request answer %1 <> %2").arg(answerLen).arg((len - 5) * sizeof(mtpPrime))); - DEBUG_LOG(("AuthKey Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); - return false; - } - const mtpPrime *from(answer + 5), *end(from + len - 5); - response.read(from, end); + const auto answer = _connection->parseNotSecureResponse(buffer); + if (answer.empty()) { + return false; + } + try { + auto from = answer.data(); + response.read(from, from + answer.size()); } catch (Exception &) { return false; } diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.cpp b/Telegram/SourceFiles/mtproto/connection_abstract.cpp index 8d72dfec4c5a8f..ee27bc0b261f6f 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.cpp +++ b/Telegram/SourceFiles/mtproto/connection_abstract.cpp @@ -97,33 +97,56 @@ mtpBuffer AbstractConnection::prepareSecurePacket( return result; } +gsl::span AbstractConnection::parseNotSecureResponse( + const mtpBuffer &buffer) const { + const auto answer = buffer.data(); + const auto len = buffer.size(); + if (len < 6) { + LOG(("Not Secure Error: bad request answer, len = %1" + ).arg(len * sizeof(mtpPrime))); + DEBUG_LOG(("Not Secure Error: answer bytes %1" + ).arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); + return {}; + } + if (answer[0] != 0 + || answer[1] != 0 + || (((uint32)answer[2]) & 0x03) != 1 + //|| (unixtime() - answer[3] > 300) // We didn't sync time yet. + //|| (answer[3] - unixtime() > 60) + || false) { + LOG(("Not Secure Error: bad request answer start (%1 %2 %3)" + ).arg(answer[0] + ).arg(answer[1] + ).arg(answer[2])); + DEBUG_LOG(("Not Secure Error: answer bytes %1" + ).arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); + return {}; + } + const auto answerLen = (uint32)answer[4]; + if (answerLen < 1 || answerLen > (len - 5) * sizeof(mtpPrime)) { + LOG(("Not Secure Error: bad request answer 1 <= %1 <= %2" + ).arg(answerLen + ).arg((len - 5) * sizeof(mtpPrime))); + DEBUG_LOG(("Not Secure Error: answer bytes %1" + ).arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); + return {}; + } + return gsl::make_span(answer + 5, answerLen); +} + mtpBuffer AbstractConnection::preparePQFake(const MTPint128 &nonce) const { return prepareNotSecurePacket(MTPReq_pq(nonce)); } MTPResPQ AbstractConnection::readPQFakeReply( const mtpBuffer &buffer) const { - const mtpPrime *answer(buffer.constData()); - uint32 len = buffer.size(); - if (len < 5) { - LOG(("Fake PQ Error: bad request answer, len = %1").arg(len * sizeof(mtpPrime))); - DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); - throw Exception("bad pq reply"); - } - if (answer[0] != 0 || answer[1] != 0 || (((uint32)answer[2]) & 0x03) != 1/* || (unixtime() - answer[3] > 300) || (answer[3] - unixtime() > 60)*/) { // didnt sync time yet - LOG(("Fake PQ Error: bad request answer start (%1 %2 %3)").arg(answer[0]).arg(answer[1]).arg(answer[2])); - DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); - throw Exception("bad pq reply"); - } - uint32 answerLen = (uint32)answer[4]; - if (answerLen != (len - 5) * sizeof(mtpPrime)) { - LOG(("Fake PQ Error: bad request answer %1 <> %2").arg(answerLen).arg((len - 5) * sizeof(mtpPrime))); - DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); + const auto answer = parseNotSecureResponse(buffer); + if (answer.empty()) { throw Exception("bad pq reply"); } - const mtpPrime *from(answer + 5), *end(from + len - 5); + auto from = answer.data(); MTPResPQ response; - response.read(from, end); + response.read(from, from + answer.size()); return response; } diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.h b/Telegram/SourceFiles/mtproto/connection_abstract.h index 70fe80e30e5fc9..b206ac1b2d927d 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.h +++ b/Telegram/SourceFiles/mtproto/connection_abstract.h @@ -108,6 +108,9 @@ class AbstractConnection : public QObject { MTPint128 msgKey, uint32 size) const; + gsl::span parseNotSecureResponse( + const mtpBuffer &buffer) const; + // Used to emit error(...) with no real code from the server. static constexpr auto kErrorCodeOther = -499; diff --git a/Telegram/SourceFiles/mtproto/connection_http.cpp b/Telegram/SourceFiles/mtproto/connection_http.cpp index 7882a09cefd54e..4a404695cb78a7 100644 --- a/Telegram/SourceFiles/mtproto/connection_http.cpp +++ b/Telegram/SourceFiles/mtproto/connection_http.cpp @@ -169,16 +169,24 @@ void HttpConnection::requestFinished(QNetworkReply *reply) { emit receivedData(); } else { try { - auto res_pq = readPQFakeReply(data); - const auto &res_pq_data(res_pq.c_resPQ()); - if (res_pq_data.vnonce == _checkNonce) { - DEBUG_LOG(("Connection Info: HTTP-transport to %1 connected by pq-response").arg(_address)); + const auto res_pq = readPQFakeReply(data); + const auto &data = res_pq.c_resPQ(); + if (data.vnonce == _checkNonce) { + DEBUG_LOG(("Connection Info: " + "HTTP-transport to %1 connected by pq-response" + ).arg(_address)); _status = Status::Ready; _pingTime = getms() - _pingTime; emit connected(); + } else { + DEBUG_LOG(("Connection Error: " + "Wrong nonce received in HTTP fake pq-responce")); + emit error(kErrorCodeOther); } } catch (Exception &e) { - DEBUG_LOG(("Connection Error: exception in parsing HTTP fake pq-responce, %1").arg(e.what())); + DEBUG_LOG(("Connection Error: " + "Exception in parsing HTTP fake pq-responce, %1" + ).arg(e.what())); emit error(kErrorCodeOther); } } diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.cpp b/Telegram/SourceFiles/mtproto/connection_tcp.cpp index 156aa956fb9753..74a1b6163ed7f0 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.cpp +++ b/Telegram/SourceFiles/mtproto/connection_tcp.cpp @@ -19,19 +19,9 @@ namespace MTP { namespace internal { namespace { -constexpr auto kPacketSizeMax = 64 * 1024 * 1024U; +constexpr auto kPacketSizeMax = 0x01000000 * sizeof(mtpPrime); constexpr auto kFullConnectionTimeout = 8 * TimeMs(1000); -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(packet); - result = (((uint32(bytes[3]) << 8) | uint32(bytes[2])) << 8) | uint32(bytes[1]); - return (result << 2) + 4; - } - return (result << 2) + 1; -} - using ErrorSignal = void(QTcpSocket::*)(QAbstractSocket::SocketError); const auto QTcpSocket_error = ErrorSignal(&QAbstractSocket::error); @@ -42,11 +32,17 @@ class TcpConnection::Protocol { static std::unique_ptr Create(bytes::vector &&secret); virtual uint32 id() const = 0; - virtual bool requiresExtendedPadding() const = 0; virtual bool supportsArbitraryLength() const = 0; + + virtual bool requiresExtendedPadding() const = 0; virtual void prepareKey(bytes::span key, bytes::const_span source) = 0; virtual bytes::span finalizePacket(mtpBuffer &buffer) = 0; + static constexpr auto kUnknownSize = uint32(-1); + static constexpr auto kInvalidSize = uint32(-2); + virtual uint32 readPacketLength(bytes::const_span bytes) const = 0; + virtual bytes::const_span readPacket(bytes::const_span bytes) const = 0; + virtual ~Protocol() = default; private: @@ -59,22 +55,26 @@ class TcpConnection::Protocol { class TcpConnection::Protocol::Version0 : public Protocol { public: uint32 id() const override; - bool requiresExtendedPadding() const override; bool supportsArbitraryLength() const override; + + bool requiresExtendedPadding() const override; void prepareKey(bytes::span key, bytes::const_span source) override; bytes::span finalizePacket(mtpBuffer &buffer) override; + uint32 readPacketLength(bytes::const_span bytes) const override; + bytes::const_span readPacket(bytes::const_span bytes) const override; + }; uint32 TcpConnection::Protocol::Version0::id() const { return 0xEFEFEFEFU; } -bool TcpConnection::Protocol::Version0::requiresExtendedPadding() const { +bool TcpConnection::Protocol::Version0::supportsArbitraryLength() const { return false; } -bool TcpConnection::Protocol::Version0::supportsArbitraryLength() const { +bool TcpConnection::Protocol::Version0::requiresExtendedPadding() const { return false; } @@ -105,6 +105,37 @@ bytes::span TcpConnection::Protocol::Version0::finalizePacket( return bytes::make_span(buffer).subspan(8 - added, added + bytesSize); } +uint32 TcpConnection::Protocol::Version0::readPacketLength( + bytes::const_span bytes) const { + if (bytes.empty()) { + return kUnknownSize; + } + const auto first = static_cast(bytes[0]); + if (first == 0x7F) { + if (bytes.size() < 4) { + return kUnknownSize; + } + const auto ints = static_cast(bytes[1]) + | (static_cast(bytes[2]) << 8) + | (static_cast(bytes[3]) << 16); + return (ints >= 0x7F) ? ((ints << 2) + 4) : kInvalidSize; + } else if (first > 0 && first < 0x7F) { + const auto ints = uint32(first); + return (ints << 2) + 1; + } + return kInvalidSize; +} + +bytes::const_span TcpConnection::Protocol::Version0::readPacket( + bytes::const_span bytes) const { + const auto size = readPacketLength(bytes); + Assert(size != kUnknownSize + && size != kInvalidSize + && size <= bytes.size()); + const auto sizeLength = (static_cast(bytes[0]) == 0x7F) ? 4 : 1; + return bytes.subspan(sizeLength, size - sizeLength); +} + class TcpConnection::Protocol::Version1 : public Version0 { public: explicit Version1(bytes::vector &&secret); @@ -138,8 +169,12 @@ class TcpConnection::Protocol::VersionD : public Version1 { uint32 id() const override; bool supportsArbitraryLength() const override; + bytes::span finalizePacket(mtpBuffer &buffer) override; + uint32 readPacketLength(bytes::const_span bytes) const override; + bytes::const_span readPacket(bytes::const_span bytes) const override; + }; uint32 TcpConnection::Protocol::VersionD::id() const { @@ -165,6 +200,25 @@ bytes::span TcpConnection::Protocol::VersionD::finalizePacket( return bytes::make_span(buffer).subspan(4, 4 + bytesSize); } +uint32 TcpConnection::Protocol::VersionD::readPacketLength( + bytes::const_span bytes) const { + if (bytes.size() < 4) { + return kUnknownSize; + } + const auto value = *reinterpret_cast(bytes.data()) + 4; + return (value >= 8 && value < kPacketSizeMax) ? value : kInvalidSize; +} + +bytes::const_span TcpConnection::Protocol::VersionD::readPacket( + bytes::const_span bytes) const { + const auto size = readPacketLength(bytes); + Assert(size != kUnknownSize + && size != kInvalidSize + && size <= bytes.size()); + const auto sizeLength = 4; + return bytes.subspan(sizeLength, size - sizeLength); +} + auto TcpConnection::Protocol::Create(bytes::vector &&secret) -> std::unique_ptr { if (secret.size() == 17 && static_cast(secret[0]) == 0xDD) { @@ -251,26 +305,36 @@ void TcpConnection::socketRead() { if (_packetLeft) { _packetLeft -= bytes; if (!_packetLeft) { - socketPacket(_currentPosition - _packetRead, _packetRead); + socketPacket(bytes::make_span( + _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 = CountTcpPacketSize(_currentPosition - _packetRead); - if (packetSize < 5 || packetSize > kPacketSizeMax) { + const auto packetSize = _protocol->readPacketLength( + bytes::make_span( + _currentPosition - _packetRead, + _packetRead)); + if (packetSize == Protocol::kUnknownSize + || packetSize == Protocol::kInvalidSize) { LOG(("TCP Error: packet size = %1").arg(packetSize)); emit error(kErrorCodeOther); return; } if (_packetRead >= packetSize) { - socketPacket(_currentPosition - _packetRead, packetSize); + socketPacket(bytes::make_span( + _currentPosition - _packetRead, + packetSize)); _packetRead -= packetSize; _packetLeft = 0; move = true; @@ -305,39 +369,30 @@ void TcpConnection::socketRead() { } while (_socket.state() == QAbstractSocket::ConnectedState && _socket.bytesAvailable()); } -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); - } - int32 size = packet[0], len = length - 1; - if (size == 0x7f) { - const uchar *bytes = reinterpret_cast(packet); - size = (((uint32(bytes[3]) << 8) | uint32(bytes[2])) << 8) | uint32(bytes[1]); - len -= 3; - } - if (size * int32(sizeof(mtpPrime)) != len) { - LOG(("TCP Error: bad packet header")); - TCP_LOG(("TCP Error: bad packet header, packet: %1").arg(Logs::mb(packet, length).str())); - return mtpBuffer(1, -500); - } - const mtpPrime *packetdata = reinterpret_cast(packet + (length - len)); - TCP_LOG(("TCP Info: packet received, size = %1").arg(size * sizeof(mtpPrime))); - if (size == 1) { - LOG(("TCP Error: " - "error packet received, endpoint: '%1:%2', " - "protocolDcId: %3, code = %4" - ).arg(_address.isEmpty() ? ("proxy_" + _proxy.host) : _address - ).arg(_address.isEmpty() ? _proxy.port : _port - ).arg(_protocolDcId - ).arg(*packetdata)); - return mtpBuffer(1, *packetdata); +mtpBuffer TcpConnection::parsePacket(bytes::const_span bytes) { + const auto packet = _protocol->readPacket(bytes); + TCP_LOG(("TCP Info: packet received, size = %1" + ).arg(packet.size())); + const auto ints = gsl::make_span( + reinterpret_cast(packet.data()), + packet.size() / sizeof(mtpPrime)); + Assert(!ints.empty()); + if (ints.size() < 3) { + // nop or error or new quickack, latter is not yet supported. + if (ints[0] != 0) { + LOG(("TCP Error: " + "error packet received, endpoint: '%1:%2', " + "protocolDcId: %3, code = %4" + ).arg(_address.isEmpty() ? ("prx_" + _proxy.host) : _address + ).arg(_address.isEmpty() ? _proxy.port : _port + ).arg(_protocolDcId + ).arg(ints[0])); + } + return mtpBuffer(1, ints[0]); } - - mtpBuffer data(size); - memcpy(data.data(), packetdata, size * sizeof(mtpPrime)); - - return data; + auto result = mtpBuffer(ints.size()); + memcpy(result.data(), ints.data(), ints.size() * sizeof(mtpPrime)); + return result; } void TcpConnection::handleError(QAbstractSocket::SocketError e, QTcpSocket &socket) { @@ -541,12 +596,19 @@ TimeMs TcpConnection::fullConnectTimeout() const { return kFullConnectionTimeout; } -void TcpConnection::socketPacket(const char *packet, uint32 length) { +void TcpConnection::socketPacket(bytes::const_span bytes) { if (_status == Status::Finished) return; - const auto data = handleResponse(packet, length); + // old quickack?.. + const auto data = parsePacket(bytes); if (data.size() == 1) { - emit error(data[0]); + if (data[0] != 0) { + emit error(data[0]); + } else { + // nop + } + //} else if (data.size() == 2) { + // new quickack?.. } else if (_status == Status::Ready) { _receivedQueue.push_back(data); emit receivedData(); @@ -564,9 +626,15 @@ void TcpConnection::socketPacket(const char *packet, uint32 length) { nullptr); _pingTime = (getms() - _pingTime); emit connected(); + } else { + DEBUG_LOG(("Connection Error: " + "Wrong nonce received in TCP fake pq-responce")); + emit error(kErrorCodeOther); } } catch (Exception &e) { - DEBUG_LOG(("Connection Error: exception in parsing TCP fake pq-responce, %1").arg(e.what())); + DEBUG_LOG(("Connection Error: " + "Exception in parsing TCP fake pq-responce, %1" + ).arg(e.what())); emit error(kErrorCodeOther); } } diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.h b/Telegram/SourceFiles/mtproto/connection_tcp.h index c98c66087e7e90..ed3ecb85afc679 100644 --- a/Telegram/SourceFiles/mtproto/connection_tcp.h +++ b/Telegram/SourceFiles/mtproto/connection_tcp.h @@ -52,13 +52,13 @@ class TcpConnection : public AbstractConnection { void socketRead(); void writeConnectionStart(); - void socketPacket(const char *packet, uint32 length); + void socketPacket(bytes::const_span bytes); void socketConnected(); void socketDisconnected(); void socketError(QAbstractSocket::SocketError e); - mtpBuffer handleResponse(const char *packet, uint32 length); + mtpBuffer parsePacket(bytes::const_span bytes); 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 };