Handle new paddings in improved TCP protocol.

This commit is contained in:
John Preston 2018-06-25 22:18:27 +01:00
parent 22441ef80c
commit 941288b58e
6 changed files with 199 additions and 110 deletions

View File

@ -1415,7 +1415,7 @@ void ConnectionPrivate::handleReceived() {
} }
auto encryptedInts = ints + kExternalHeaderIntsCount; auto encryptedInts = ints + kExternalHeaderIntsCount;
auto encryptedIntsCount = (intsCount - kExternalHeaderIntsCount); auto encryptedIntsCount = (intsCount - kExternalHeaderIntsCount) & ~0x03U;
auto encryptedBytesCount = encryptedIntsCount * kIntSize; auto encryptedBytesCount = encryptedIntsCount * kIntSize;
auto decryptedBuffer = QByteArray(encryptedBytesCount, Qt::Uninitialized); auto decryptedBuffer = QByteArray(encryptedBytesCount, Qt::Uninitialized);
auto msgKey = *(MTPint128*)(ints + 2); auto msgKey = *(MTPint128*)(ints + 2);
@ -3062,35 +3062,22 @@ template <typename Response>
bool ConnectionPrivate::readNotSecureResponse(Response &response) { bool ConnectionPrivate::readNotSecureResponse(Response &response) {
onReceivedSome(); onReceivedSome();
if (_connection->received().empty()) {
LOG(("AuthKey Error: "
"trying to read response from empty received list"));
return false;
}
const auto buffer = std::move(_connection->received().front());
_connection->received().pop_front();
const auto answer = _connection->parseNotSecureResponse(buffer);
if (answer.empty()) {
return false;
}
try { try {
if (_connection->received().empty()) { auto from = answer.data();
LOG(("AuthKey Error: trying to read response from empty received list")); response.read(from, from + answer.size());
return false;
}
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);
} catch (Exception &) { } catch (Exception &) {
return false; return false;
} }

View File

@ -97,33 +97,56 @@ mtpBuffer AbstractConnection::prepareSecurePacket(
return result; return result;
} }
gsl::span<const mtpPrime> 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 { mtpBuffer AbstractConnection::preparePQFake(const MTPint128 &nonce) const {
return prepareNotSecurePacket(MTPReq_pq(nonce)); return prepareNotSecurePacket(MTPReq_pq(nonce));
} }
MTPResPQ AbstractConnection::readPQFakeReply( MTPResPQ AbstractConnection::readPQFakeReply(
const mtpBuffer &buffer) const { const mtpBuffer &buffer) const {
const mtpPrime *answer(buffer.constData()); const auto answer = parseNotSecureResponse(buffer);
uint32 len = buffer.size(); if (answer.empty()) {
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"); 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 auto from = answer.data();
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()));
throw Exception("bad pq reply");
}
const mtpPrime *from(answer + 5), *end(from + len - 5);
MTPResPQ response; MTPResPQ response;
response.read(from, end); response.read(from, from + answer.size());
return response; return response;
} }

View File

@ -108,6 +108,9 @@ public:
MTPint128 msgKey, MTPint128 msgKey,
uint32 size) const; uint32 size) const;
gsl::span<const mtpPrime> parseNotSecureResponse(
const mtpBuffer &buffer) const;
// Used to emit error(...) with no real code from the server. // Used to emit error(...) with no real code from the server.
static constexpr auto kErrorCodeOther = -499; static constexpr auto kErrorCodeOther = -499;

View File

@ -169,16 +169,24 @@ void HttpConnection::requestFinished(QNetworkReply *reply) {
emit receivedData(); emit receivedData();
} else { } else {
try { try {
auto res_pq = readPQFakeReply(data); const auto res_pq = readPQFakeReply(data);
const auto &res_pq_data(res_pq.c_resPQ()); const auto &data = res_pq.c_resPQ();
if (res_pq_data.vnonce == _checkNonce) { if (data.vnonce == _checkNonce) {
DEBUG_LOG(("Connection Info: HTTP-transport to %1 connected by pq-response").arg(_address)); DEBUG_LOG(("Connection Info: "
"HTTP-transport to %1 connected by pq-response"
).arg(_address));
_status = Status::Ready; _status = Status::Ready;
_pingTime = getms() - _pingTime; _pingTime = getms() - _pingTime;
emit connected(); emit connected();
} else {
DEBUG_LOG(("Connection Error: "
"Wrong nonce received in HTTP fake pq-responce"));
emit error(kErrorCodeOther);
} }
} catch (Exception &e) { } 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); emit error(kErrorCodeOther);
} }
} }

View File

@ -19,19 +19,9 @@ namespace MTP {
namespace internal { namespace internal {
namespace { namespace {
constexpr auto kPacketSizeMax = 64 * 1024 * 1024U; constexpr auto kPacketSizeMax = 0x01000000 * sizeof(mtpPrime);
constexpr auto kFullConnectionTimeout = 8 * TimeMs(1000); 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<const uchar*>(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); using ErrorSignal = void(QTcpSocket::*)(QAbstractSocket::SocketError);
const auto QTcpSocket_error = ErrorSignal(&QAbstractSocket::error); const auto QTcpSocket_error = ErrorSignal(&QAbstractSocket::error);
@ -42,11 +32,17 @@ public:
static std::unique_ptr<Protocol> Create(bytes::vector &&secret); static std::unique_ptr<Protocol> Create(bytes::vector &&secret);
virtual uint32 id() const = 0; virtual uint32 id() const = 0;
virtual bool requiresExtendedPadding() const = 0;
virtual bool supportsArbitraryLength() const = 0; virtual bool supportsArbitraryLength() const = 0;
virtual bool requiresExtendedPadding() const = 0;
virtual void prepareKey(bytes::span key, bytes::const_span source) = 0; virtual void prepareKey(bytes::span key, bytes::const_span source) = 0;
virtual bytes::span finalizePacket(mtpBuffer &buffer) = 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; virtual ~Protocol() = default;
private: private:
@ -59,22 +55,26 @@ private:
class TcpConnection::Protocol::Version0 : public Protocol { class TcpConnection::Protocol::Version0 : public Protocol {
public: public:
uint32 id() const override; uint32 id() const override;
bool requiresExtendedPadding() const override;
bool supportsArbitraryLength() const override; bool supportsArbitraryLength() const override;
bool requiresExtendedPadding() const override;
void prepareKey(bytes::span key, bytes::const_span source) override; void prepareKey(bytes::span key, bytes::const_span source) override;
bytes::span finalizePacket(mtpBuffer &buffer) 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 { uint32 TcpConnection::Protocol::Version0::id() const {
return 0xEFEFEFEFU; return 0xEFEFEFEFU;
} }
bool TcpConnection::Protocol::Version0::requiresExtendedPadding() const { bool TcpConnection::Protocol::Version0::supportsArbitraryLength() const {
return false; return false;
} }
bool TcpConnection::Protocol::Version0::supportsArbitraryLength() const { bool TcpConnection::Protocol::Version0::requiresExtendedPadding() const {
return false; return false;
} }
@ -105,6 +105,37 @@ bytes::span TcpConnection::Protocol::Version0::finalizePacket(
return bytes::make_span(buffer).subspan(8 - added, added + bytesSize); 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<char>(bytes[0]);
if (first == 0x7F) {
if (bytes.size() < 4) {
return kUnknownSize;
}
const auto ints = static_cast<uint32>(bytes[1])
| (static_cast<uint32>(bytes[2]) << 8)
| (static_cast<uint32>(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<char>(bytes[0]) == 0x7F) ? 4 : 1;
return bytes.subspan(sizeLength, size - sizeLength);
}
class TcpConnection::Protocol::Version1 : public Version0 { class TcpConnection::Protocol::Version1 : public Version0 {
public: public:
explicit Version1(bytes::vector &&secret); explicit Version1(bytes::vector &&secret);
@ -138,8 +169,12 @@ public:
uint32 id() const override; uint32 id() const override;
bool supportsArbitraryLength() const override; bool supportsArbitraryLength() const override;
bytes::span finalizePacket(mtpBuffer &buffer) 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 { 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); 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<const uint32*>(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) auto TcpConnection::Protocol::Create(bytes::vector &&secret)
-> std::unique_ptr<Protocol> { -> std::unique_ptr<Protocol> {
if (secret.size() == 17 && static_cast<uchar>(secret[0]) == 0xDD) { if (secret.size() == 17 && static_cast<uchar>(secret[0]) == 0xDD) {
@ -251,26 +305,36 @@ void TcpConnection::socketRead() {
if (_packetLeft) { if (_packetLeft) {
_packetLeft -= bytes; _packetLeft -= bytes;
if (!_packetLeft) { if (!_packetLeft) {
socketPacket(_currentPosition - _packetRead, _packetRead); socketPacket(bytes::make_span(
_currentPosition - _packetRead,
_packetRead));
_currentPosition = (char*)_shortBuffer; _currentPosition = (char*)_shortBuffer;
_packetRead = _packetLeft = 0; _packetRead = _packetLeft = 0;
_readingToShort = true; _readingToShort = true;
_longBuffer.clear(); _longBuffer.clear();
} else { } 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(); emit receivedSome();
} }
} else { } else {
bool move = false; bool move = false;
while (_packetRead >= 4) { while (_packetRead >= 4) {
uint32 packetSize = CountTcpPacketSize(_currentPosition - _packetRead); const auto packetSize = _protocol->readPacketLength(
if (packetSize < 5 || packetSize > kPacketSizeMax) { bytes::make_span(
_currentPosition - _packetRead,
_packetRead));
if (packetSize == Protocol::kUnknownSize
|| packetSize == Protocol::kInvalidSize) {
LOG(("TCP Error: packet size = %1").arg(packetSize)); LOG(("TCP Error: packet size = %1").arg(packetSize));
emit error(kErrorCodeOther); emit error(kErrorCodeOther);
return; return;
} }
if (_packetRead >= packetSize) { if (_packetRead >= packetSize) {
socketPacket(_currentPosition - _packetRead, packetSize); socketPacket(bytes::make_span(
_currentPosition - _packetRead,
packetSize));
_packetRead -= packetSize; _packetRead -= packetSize;
_packetLeft = 0; _packetLeft = 0;
move = true; move = true;
@ -305,39 +369,30 @@ void TcpConnection::socketRead() {
} while (_socket.state() == QAbstractSocket::ConnectedState && _socket.bytesAvailable()); } while (_socket.state() == QAbstractSocket::ConnectedState && _socket.bytesAvailable());
} }
mtpBuffer TcpConnection::handleResponse(const char *packet, uint32 length) { mtpBuffer TcpConnection::parsePacket(bytes::const_span bytes) {
if (length < 5 || length > kPacketSizeMax) { const auto packet = _protocol->readPacket(bytes);
LOG(("TCP Error: bad packet size %1").arg(length)); TCP_LOG(("TCP Info: packet received, size = %1"
return mtpBuffer(1, -500); ).arg(packet.size()));
const auto ints = gsl::make_span(
reinterpret_cast<const mtpPrime*>(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]);
} }
int32 size = packet[0], len = length - 1; auto result = mtpBuffer(ints.size());
if (size == 0x7f) { memcpy(result.data(), ints.data(), ints.size() * sizeof(mtpPrime));
const uchar *bytes = reinterpret_cast<const uchar*>(packet); return result;
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<const mtpPrime*>(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 data(size);
memcpy(data.data(), packetdata, size * sizeof(mtpPrime));
return data;
} }
void TcpConnection::handleError(QAbstractSocket::SocketError e, QTcpSocket &socket) { void TcpConnection::handleError(QAbstractSocket::SocketError e, QTcpSocket &socket) {
@ -541,12 +596,19 @@ TimeMs TcpConnection::fullConnectTimeout() const {
return kFullConnectionTimeout; return kFullConnectionTimeout;
} }
void TcpConnection::socketPacket(const char *packet, uint32 length) { void TcpConnection::socketPacket(bytes::const_span bytes) {
if (_status == Status::Finished) return; if (_status == Status::Finished) return;
const auto data = handleResponse(packet, length); // old quickack?..
const auto data = parsePacket(bytes);
if (data.size() == 1) { 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) { } else if (_status == Status::Ready) {
_receivedQueue.push_back(data); _receivedQueue.push_back(data);
emit receivedData(); emit receivedData();
@ -564,9 +626,15 @@ void TcpConnection::socketPacket(const char *packet, uint32 length) {
nullptr); nullptr);
_pingTime = (getms() - _pingTime); _pingTime = (getms() - _pingTime);
emit connected(); emit connected();
} else {
DEBUG_LOG(("Connection Error: "
"Wrong nonce received in TCP fake pq-responce"));
emit error(kErrorCodeOther);
} }
} catch (Exception &e) { } 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); emit error(kErrorCodeOther);
} }
} }

View File

@ -52,13 +52,13 @@ private:
void socketRead(); void socketRead();
void writeConnectionStart(); void writeConnectionStart();
void socketPacket(const char *packet, uint32 length); void socketPacket(bytes::const_span bytes);
void socketConnected(); void socketConnected();
void socketDisconnected(); void socketDisconnected();
void socketError(QAbstractSocket::SocketError e); 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 void handleError(QAbstractSocket::SocketError e, QTcpSocket &sock);
static uint32 fourCharsToUInt(char ch1, char ch2, char ch3, char ch4) { static uint32 fourCharsToUInt(char ch1, char ch2, char ch3, char ch4) {
char ch[4] = { ch1, ch2, ch3, ch4 }; char ch[4] = { ch1, ch2, ch3, ch4 };