tdesktop/Telegram/SourceFiles/mtproto/mtpConnection.h

577 lines
14 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "mtproto/mtpCoreTypes.h"
#include "mtproto/mtpScheme.h"
enum {
MTPDmessage_flag_unread = (1 << 0),
MTPDmessage_flag_out = (1 << 1),
MTPDmessage_flag_notify_by_from = (1 << 4),
MTPDmessage_flag_media_unread = (1 << 5),
MTPDmessage_flag_HAS_TEXT_LINKS = (1 << 31), // client side flag for having links
MTPmessages_SendMessage_flag_skipWebPage = (1 << 1),
MTPmessages_SendMessage_flag_broadcast = (1 << 4),
MTPmessages_ForwardMessages_flag_broadcast = (1 << 4),
MTPDdcOption_flag_ipv6 = (1 << 0),
MTPDdcOption_flag_files = (1 << 1),
MTPDuser_flag_self = (1 << 10),
MTPDuser_flag_contact = (1 << 11),
MTPDuser_flag_mutual_contact = (1 << 12),
MTPDuser_flag_deleted = (1 << 13),
MTPDuser_flag_bot = (1 << 14),
MTPDuser_flag_bot_reads_all = (1 << 15),
MTPDuser_flag_bot_cant_join = (1 << 16),
MTPDreplyKeyboardMarkup_flag_resize = (1 << 0),
MTPDreplyKeyboardMarkup_flag_single_use = (1 << 1),
MTPDreplyKeyboardMarkup_flag_personal = (1 << 2),
MTPDreplyKeyboardMarkup_flag_FORCE_REPLY = (1 << 30), // client side flag for forceReply
MTPDreplyKeyboardMarkup_flag_ZERO = (1 << 31), // client side flag for zeroMarkup
MTPDstickerSet_flag_installed = (1 << 0),
MTPDstickerSet_flag_disabled = (1 << 1),
MTPDstickerSet_flag_official = (1 << 2),
MTPDstickerSet_flag_NOT_LOADED = (1 << 31), // client side flag for not yet loaded set
MTPDchannel_flag_am_creator = (1 << 0),
MTPDchannel_flag_was_kicked = (1 << 1),
MTPDchannel_flag_have_left = (1 << 2),
MTPDchannel_flag_am_editor = (1 << 3),
MTPDchannel_flag_am_moderator = (1 << 4),
MTPDchannel_flag_is_broadcast = (1 << 5),
MTPDchannel_flag_is_verified = (1 << 7),
MTPDchannelFull_flag_can_view_participants = (1 << 3),
MTPDchat_flag_creator = (1 << 0),
MTPDchat_flag_kicked = (1 << 1),
MTPDchat_flag_left = (1 << 2),
MTPDchatInvite_flag_is_channel = (1 << 0),
MTPDchatInvite_flag_is_broadcast = (1 << 1),
MTPDchatInvite_flag_is_public = (1 << 2),
MTPupdates_ChannelDifference_flag_final = (1 << 0),
MTPDchannelMessagesFilter_flag_only_important = (1 << 0),
MTPmessages_CreateChannel_flag_broadcast = (1 << 0),
MTPmessages_Search_flag_only_important = (1 << 0),
};
static const MTPReplyMarkup MTPnullMarkup = MTP_replyKeyboardMarkup(MTP_int(0), MTP_vector<MTPKeyboardButtonRow>(0));
static const MTPVector<MTPMessageEntity> MTPnullEntities = MTP_vector<MTPMessageEntity>(0);
#include "mtproto/mtpPublicRSA.h"
#include "mtproto/mtpAuthKey.h"
inline bool mtpRequestData::isSentContainer(const mtpRequest &request) { // "request-like" wrap for msgIds vector
if (request->size() < 9) return false;
return (!request->msDate && !(*request)[6]); // msDate = 0, seqNo = 0
}
inline bool mtpRequestData::isStateRequest(const mtpRequest &request) {
if (request->size() < 9) return false;
return (mtpTypeId((*request)[8]) == mtpc_msgs_state_req);
}
inline bool mtpRequestData::needAck(const mtpRequest &request) {
if (request->size() < 9) return false;
return mtpRequestData::needAckByType((*request)[8]);
}
inline bool mtpRequestData::needAckByType(mtpTypeId type) {
switch (type) {
case mtpc_msg_container:
case mtpc_msgs_ack:
case mtpc_http_wait:
case mtpc_bad_msg_notification:
case mtpc_msgs_all_info:
case mtpc_msgs_state_info:
case mtpc_msg_detailed_info:
case mtpc_msg_new_detailed_info:
return false;
}
return true;
}
class MTProtoConnectionPrivate;
class MTPSessionData;
class MTPThread : public QThread {
Q_OBJECT
public:
MTPThread(QObject *parent = 0);
uint32 getThreadId() const;
private:
uint32 threadId;
};
class MTProtoConnection {
public:
enum ConnectionType {
TcpConnection,
HttpConnection
};
MTProtoConnection();
int32 start(MTPSessionData *data, int32 dc = 0); // return dc
void stop();
void stopped();
~MTProtoConnection();
enum {
Disconnected = 0,
Connecting = 1,
Connected = 2,
UpdateAlways = 666
};
int32 state() const;
QString transport() const;
private:
QThread *thread;
MTProtoConnectionPrivate *data;
};
class MTPabstractConnection : public QObject {
Q_OBJECT
typedef QList<mtpBuffer> BuffersQueue;
public:
MTPabstractConnection() : _sentEncrypted(false) {
}
void setSentEncrypted() {
_sentEncrypted = true;
}
virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32
virtual void disconnectFromServer() = 0;
virtual void connectToServer(const QString &addr, int32 port, int32 flags) = 0;
virtual bool isConnected() const = 0;
virtual bool usingHttpWait() {
return false;
}
virtual bool needHttpWait() {
return false;
}
virtual int32 debugState() const = 0;
virtual QString transport() const = 0;
BuffersQueue &received() {
return receivedQueue;
}
signals:
void receivedData();
void receivedSome(); // to stop restart timer
void error(bool maybeBadKey = false);
void connected();
void disconnected();
protected:
BuffersQueue receivedQueue; // list of received packets, not processed yet
bool _sentEncrypted;
};
class MTPabstractTcpConnection : public MTPabstractConnection {
Q_OBJECT
public:
MTPabstractTcpConnection();
public slots:
void socketRead();
protected:
QTcpSocket sock;
uint32 packetNum; // sent packet number
uint32 packetRead, packetLeft; // reading from socket
bool readingToShort;
char *currentPos;
mtpBuffer longBuffer;
mtpPrime shortBuffer[MTPShortBufferSize];
virtual void socketPacket(mtpPrime *packet, uint32 packetSize) = 0;
};
class MTPautoConnection : public MTPabstractTcpConnection {
Q_OBJECT
public:
MTPautoConnection(QThread *thread);
void sendData(mtpBuffer &buffer);
void disconnectFromServer();
void connectToServer(const QString &addr, int32 port, int32 flags);
bool isConnected() const;
bool usingHttpWait();
bool needHttpWait();
int32 debugState() const;
QString transport() const;
public slots:
void socketError(QAbstractSocket::SocketError e);
void requestFinished(QNetworkReply *reply);
void onSocketConnected();
void onSocketDisconnected();
void onHttpStart();
void onTcpTimeoutTimer();
protected:
void socketPacket(mtpPrime *packet, uint32 packetSize);
private:
void tcpSend(mtpBuffer &buffer);
void httpSend(mtpBuffer &buffer);
enum Status {
WaitingBoth = 0,
WaitingHttp,
WaitingTcp,
HttpReady,
UsingHttp,
UsingTcp,
FinishedWork
};
Status status;
MTPint128 tcpNonce, httpNonce;
QTimer httpStartTimer;
QNetworkAccessManager manager;
QUrl address;
typedef QSet<QNetworkReply*> Requests;
Requests requests;
QString _addr;
int32 _port, _tcpTimeout, _flags;
QTimer tcpTimeoutTimer;
};
class MTPtcpConnection : public MTPabstractTcpConnection {
Q_OBJECT
public:
MTPtcpConnection(QThread *thread);
void sendData(mtpBuffer &buffer);
void disconnectFromServer();
void connectToServer(const QString &addr, int32 port, int32 flags);
bool isConnected() const;
int32 debugState() const;
QString transport() const;
public slots:
void socketError(QAbstractSocket::SocketError e);
void onSocketConnected();
void onSocketDisconnected();
void onTcpTimeoutTimer();
protected:
void socketPacket(mtpPrime *packet, uint32 packetSize);
private:
enum Status {
WaitingTcp = 0,
UsingTcp,
FinishedWork
};
Status status;
MTPint128 tcpNonce;
QString _addr;
int32 _port, _tcpTimeout, _flags;
QTimer tcpTimeoutTimer;
};
class MTPhttpConnection : public MTPabstractConnection {
Q_OBJECT
public:
MTPhttpConnection(QThread *thread);
void sendData(mtpBuffer &buffer);
void disconnectFromServer();
void connectToServer(const QString &addr, int32 port, int32 flags);
bool isConnected() const;
bool usingHttpWait();
bool needHttpWait();
int32 debugState() const;
QString transport() const;
public slots:
void requestFinished(QNetworkReply *reply);
private:
enum Status {
WaitingHttp = 0,
UsingHttp,
FinishedWork
};
Status status;
MTPint128 httpNonce;
int32 _flags;
QNetworkAccessManager manager;
QUrl address;
typedef QSet<QNetworkReply*> Requests;
Requests requests;
};
class MTProtoConnectionPrivate : public QObject {
Q_OBJECT
public:
MTProtoConnectionPrivate(QThread *thread, MTProtoConnection *owner, MTPSessionData *data, uint32 dc);
~MTProtoConnectionPrivate();
void stop();
int32 getDC() const;
int32 getState() const;
QString transport() const;
signals:
void needToReceive();
void needToRestart();
void stateChanged(qint32 newState);
void sessionResetDone();
void needToSendAsync();
void sendAnythingAsync(quint64 msWait);
void sendHttpWaitAsync();
void sendPongAsync(quint64 msgId, quint64 pingId);
void sendMsgsStateInfoAsync(quint64 msgId, QByteArray data);
void resendAsync(quint64 msgId, quint64 msCanWait, bool forceContainer, bool sendMsgStateInfo);
void resendManyAsync(QVector<quint64> msgIds, quint64 msCanWait, bool forceContainer, bool sendMsgStateInfo);
void resendAllAsync();
public slots:
void retryByTimer();
void restartNow();
void restart(bool maybeBadKey = false);
void onPingSender();
void onPingSendForce();
void onWaitConnectedFailed();
void onWaitReceivedFailed();
void onWaitIPv4Failed();
void onOldConnection();
void onSentSome(uint64 size);
void onReceivedSome();
void onReadyData();
void socketStart(bool afterConfig = false);
void onConnected4();
void onConnected6();
void onDisconnected4();
void onDisconnected6();
void onError4(bool maybeBadKey = false);
void onError6(bool maybeBadKey = false);
void doFinish();
// Auth key creation packet receive slots
void pqAnswered();
void dhParamsAnswered();
void dhClientParamsAnswered();
// General packet receive slot, connected to conn->receivedData signal
void handleReceived();
// Sessions signals, when we need to send something
void tryToSend();
void updateAuthKey();
void onConfigLoaded();
private:
void doDisconnect();
void createConn(bool createIPv4, bool createIPv6);
void destroyConn(MTPabstractConnection **conn = 0); // 0 - destory all
mtpMsgId placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req);
mtpMsgId prepareToSend(mtpRequest &request, mtpMsgId currentLastId);
mtpMsgId replaceMsgId(mtpRequest &request, mtpMsgId newId);
bool sendRequest(mtpRequest &request, bool needAnyResponse, QReadLocker &lockFinished);
mtpRequestId wasSent(mtpMsgId msgId) const;
int32 handleOneReceived(const mtpPrime *from, const mtpPrime *end, uint64 msgId, int32 serverTime, uint64 serverSalt, bool badTime);
mtpBuffer ungzip(const mtpPrime *from, const mtpPrime *end) const;
void handleMsgsStates(const QVector<MTPlong> &ids, const string &states, QVector<MTPlong> &acked);
void clearMessages();
bool setState(int32 state, int32 ifState = MTProtoConnection::UpdateAlways);
mutable QReadWriteLock stateConnMutex;
int32 _state;
bool _needSessionReset;
void resetSession();
uint32 dc;
MTProtoConnection *_owner;
MTPabstractConnection *_conn, *_conn4, *_conn6;
SingleTimer retryTimer; // exp retry timer
uint32 retryTimeout;
quint64 retryWillFinish;
SingleTimer oldConnectionTimer;
bool oldConnection;
SingleTimer _waitForConnectedTimer, _waitForReceivedTimer, _waitForIPv4Timer;
uint32 _waitForReceived, _waitForConnected;
int64 firstSentAt;
QVector<MTPlong> ackRequestData, resendRequestData;
// if badTime received - search for ids in sessionData->haveSent and sessionData->wereAcked and sync time/salt, return true if found
bool requestsFixTimeSalt(const QVector<MTPlong> &ids, int32 serverTime, uint64 serverSalt);
// remove msgs with such ids from sessionData->haveSent, add to sessionData->wereAcked
void requestsAcked(const QVector<MTPlong> &ids, bool byResponse = false);
mtpPingId _pingId, _pingIdToSend;
uint64 _pingSendAt;
mtpMsgId _pingMsgId;
SingleTimer _pingSender;
void resend(quint64 msgId, quint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
void resendMany(QVector<quint64> msgIds, quint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
template <typename TRequest>
void sendRequestNotSecure(const TRequest &request);
template <typename TResponse>
bool readResponseNotSecure(TResponse &response);
bool restarted;
uint64 keyId;
QReadWriteLock sessionDataMutex;
MTPSessionData *sessionData;
bool myKeyLock;
void lockKey();
void unlockKey();
// Auth key creation fields and methods
struct AuthKeyCreateData {
AuthKeyCreateData()
: new_nonce(*(MTPint256*)((uchar*)new_nonce_buf))
, auth_key_aux_hash(*(MTPlong*)((uchar*)new_nonce_buf + 33))
, retries(0)
, g(0)
, req_num(0)
, msgs_sent(0) {
memset(new_nonce_buf, 0, sizeof(new_nonce_buf));
memset(aesKey, 0, sizeof(aesKey));
memset(aesIV, 0, sizeof(aesIV));
memset(auth_key, 0, sizeof(auth_key));
}
MTPint128 nonce, server_nonce;
uchar new_nonce_buf[41]; // 32 bytes new_nonce + 1 check byte + 8 bytes of auth_key_aux_hash
MTPint256 &new_nonce;
MTPlong &auth_key_aux_hash;
uint32 retries;
MTPlong retry_id;
int32 g;
uchar aesKey[32], aesIV[32];
uint32 auth_key[64];
MTPlong auth_key_hash;
uint32 req_num; // sent not encrypted request number
uint32 msgs_sent;
};
struct AuthKeyCreateStrings {
QByteArray dh_prime;
QByteArray g_a;
};
AuthKeyCreateData *authKeyData;
AuthKeyCreateStrings *authKeyStrings;
void dhClientParamsSend();
void authKeyCreated();
void clearAuthKeyData();
};