314 lines
8.1 KiB
C++
314 lines
8.1 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#pragma once
|
|
|
|
#include "mtproto/auth_key.h"
|
|
#include "mtproto/dc_options.h"
|
|
#include "mtproto/connection_abstract.h"
|
|
#include "base/openssl_help.h"
|
|
#include "base/timer.h"
|
|
|
|
namespace MTP {
|
|
|
|
// How much time to wait for some more requests, when sending msg acks.
|
|
constexpr auto kAckSendWaiting = crl::time(10000);
|
|
|
|
class Instance;
|
|
|
|
bool IsPrimeAndGood(bytes::const_span primeBytes, int g);
|
|
struct ModExpFirst {
|
|
static constexpr auto kRandomPowerSize = 256;
|
|
|
|
bytes::vector modexp;
|
|
bytes::vector randomPower;
|
|
};
|
|
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime);
|
|
ModExpFirst CreateModExp(int g, bytes::const_span primeBytes, bytes::const_span randomSeed);
|
|
bytes::vector CreateAuthKey(bytes::const_span firstBytes, bytes::const_span randomBytes, bytes::const_span primeBytes);
|
|
|
|
namespace internal {
|
|
|
|
class AbstractConnection;
|
|
class ConnectionPrivate;
|
|
class SessionData;
|
|
class RSAPublicKey;
|
|
struct ConnectionOptions;
|
|
|
|
class Thread : public QThread {
|
|
// The Q_OBJECT meta info is used for qobject_cast!
|
|
Q_OBJECT
|
|
|
|
public:
|
|
Thread() {
|
|
static int ThreadCounter = 0;
|
|
_threadIndex = ++ThreadCounter;
|
|
}
|
|
int getThreadIndex() const {
|
|
return _threadIndex;
|
|
}
|
|
|
|
private:
|
|
int _threadIndex = 0;
|
|
|
|
};
|
|
|
|
class Connection {
|
|
public:
|
|
enum ConnectionType {
|
|
TcpConnection,
|
|
HttpConnection
|
|
};
|
|
|
|
Connection(not_null<Instance*> instance);
|
|
|
|
void start(SessionData *data, ShiftedDcId shiftedDcId);
|
|
|
|
void kill();
|
|
void waitTillFinish();
|
|
~Connection();
|
|
|
|
static const int UpdateAlways = 666;
|
|
|
|
int32 state() const;
|
|
QString transport() const;
|
|
|
|
private:
|
|
not_null<Instance*> _instance;
|
|
std::unique_ptr<QThread> _thread;
|
|
ConnectionPrivate *_private = nullptr;
|
|
|
|
};
|
|
|
|
class ConnectionPrivate : public QObject {
|
|
Q_OBJECT
|
|
|
|
public:
|
|
ConnectionPrivate(
|
|
not_null<Instance*> instance,
|
|
not_null<QThread*> thread,
|
|
not_null<Connection*> owner,
|
|
not_null<SessionData*> data,
|
|
ShiftedDcId shiftedDcId);
|
|
~ConnectionPrivate();
|
|
|
|
void stop();
|
|
|
|
int32 getShiftedDcId() const;
|
|
|
|
int32 getState() const;
|
|
QString transport() const;
|
|
|
|
signals:
|
|
void needToReceive();
|
|
void needToRestart();
|
|
void stateChanged(qint32 newState);
|
|
void sessionResetDone();
|
|
|
|
void needToSendAsync();
|
|
void sendAnythingAsync(qint64 msWait);
|
|
void sendHttpWaitAsync();
|
|
void sendPongAsync(quint64 msgId, quint64 pingId);
|
|
void sendMsgsStateInfoAsync(quint64 msgId, QByteArray data);
|
|
void resendAsync(quint64 msgId, qint64 msCanWait, bool forceContainer, bool sendMsgStateInfo);
|
|
void resendManyAsync(QVector<quint64> msgIds, qint64 msCanWait, bool forceContainer, bool sendMsgStateInfo);
|
|
void resendAllAsync();
|
|
|
|
void finished(internal::Connection *connection);
|
|
|
|
public slots:
|
|
void restartNow();
|
|
|
|
void onPingSendForce();
|
|
|
|
void onSentSome(uint64 size);
|
|
void onReceivedSome();
|
|
|
|
void onReadyData();
|
|
|
|
// 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();
|
|
void onCDNConfigLoaded();
|
|
|
|
private:
|
|
struct TestConnection {
|
|
ConnectionPointer data;
|
|
int priority = 0;
|
|
};
|
|
void connectToServer(bool afterConfig = false);
|
|
void doDisconnect();
|
|
void restart();
|
|
void finishAndDestroy();
|
|
void requestCDNConfig();
|
|
void handleError(int errorCode);
|
|
void onError(
|
|
not_null<AbstractConnection*> connection,
|
|
qint32 errorCode);
|
|
void onConnected(not_null<AbstractConnection*> connection);
|
|
void onDisconnected(not_null<AbstractConnection*> connection);
|
|
|
|
void retryByTimer();
|
|
void waitConnectedFailed();
|
|
void waitReceivedFailed();
|
|
void waitBetterFailed();
|
|
void markConnectionOld();
|
|
void sendPingByTimer();
|
|
|
|
void destroyAllConnections();
|
|
void confirmBestConnection();
|
|
void removeTestConnection(not_null<AbstractConnection*> connection);
|
|
int16 getProtocolDcId() const;
|
|
|
|
mtpMsgId placeToContainer(
|
|
SecureRequest &toSendRequest,
|
|
mtpMsgId &bigMsgId,
|
|
mtpMsgId *&haveSentArr,
|
|
SecureRequest &req);
|
|
mtpMsgId prepareToSend(SecureRequest &request, mtpMsgId currentLastId);
|
|
mtpMsgId replaceMsgId(SecureRequest &request, mtpMsgId newId);
|
|
|
|
bool sendSecureRequest(
|
|
SecureRequest &&request,
|
|
bool needAnyResponse,
|
|
QReadLocker &lockFinished);
|
|
mtpRequestId wasSent(mtpMsgId msgId) const;
|
|
|
|
enum class HandleResult {
|
|
Success,
|
|
Ignored,
|
|
RestartConnection,
|
|
ResetSession,
|
|
};
|
|
HandleResult 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 QByteArray &states, QVector<MTPlong> &acked);
|
|
|
|
void clearMessages();
|
|
|
|
bool setState(int32 state, int32 ifState = Connection::UpdateAlways);
|
|
|
|
bytes::vector encryptPQInnerRSA(const MTPP_Q_inner_data &data, const internal::RSAPublicKey &key);
|
|
std::string encryptClientDHInner(const MTPClient_DH_Inner_Data &data);
|
|
void appendTestConnection(
|
|
DcOptions::Variants::Protocol protocol,
|
|
const QString &ip,
|
|
int port,
|
|
const bytes::vector &protocolSecret);
|
|
|
|
// 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);
|
|
|
|
void resend(quint64 msgId, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
|
|
void resendMany(QVector<quint64> msgIds, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
|
|
|
|
template <typename Request>
|
|
void sendNotSecureRequest(const Request &request);
|
|
|
|
template <typename Response>
|
|
bool readNotSecureResponse(Response &response);
|
|
|
|
not_null<Instance*> _instance;
|
|
DcType _dcType = DcType::Regular;
|
|
|
|
mutable QReadWriteLock stateConnMutex;
|
|
int32 _state = DisconnectedState;
|
|
|
|
bool _needSessionReset = false;
|
|
void resetSession();
|
|
|
|
ShiftedDcId _shiftedDcId = 0;
|
|
not_null<Connection*> _owner;
|
|
ConnectionPointer _connection;
|
|
std::vector<TestConnection> _testConnections;
|
|
crl::time _startedConnectingAt = 0;
|
|
|
|
base::Timer _retryTimer; // exp retry timer
|
|
int _retryTimeout = 1;
|
|
qint64 _retryWillFinish = 0;
|
|
|
|
base::Timer _oldConnectionTimer;
|
|
bool _oldConnection = true;
|
|
|
|
base::Timer _waitForConnectedTimer;
|
|
base::Timer _waitForReceivedTimer;
|
|
base::Timer _waitForBetterTimer;
|
|
crl::time _waitForReceived = 0;
|
|
crl::time _waitForConnected = 0;
|
|
crl::time firstSentAt = -1;
|
|
|
|
QVector<MTPlong> ackRequestData, resendRequestData;
|
|
|
|
mtpPingId _pingId = 0;
|
|
mtpPingId _pingIdToSend = 0;
|
|
crl::time _pingSendAt = 0;
|
|
mtpMsgId _pingMsgId = 0;
|
|
base::Timer _pingSender;
|
|
|
|
bool restarted = false;
|
|
bool _finished = false;
|
|
|
|
uint64 keyId = 0;
|
|
QReadWriteLock sessionDataMutex;
|
|
SessionData *sessionData = nullptr;
|
|
std::unique_ptr<ConnectionOptions> _connectionOptions;
|
|
|
|
bool myKeyLock = false;
|
|
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)) {
|
|
}
|
|
MTPint128 nonce, server_nonce;
|
|
uchar new_nonce_buf[41] = { 0 }; // 32 bytes new_nonce + 1 check byte + 8 bytes of auth_key_aux_hash
|
|
MTPint256 &new_nonce;
|
|
MTPlong &auth_key_aux_hash;
|
|
|
|
uint32 retries = 0;
|
|
MTPlong retry_id;
|
|
|
|
int32 g = 0;
|
|
|
|
uchar aesKey[32] = { 0 };
|
|
uchar aesIV[32] = { 0 };
|
|
MTPlong auth_key_hash;
|
|
};
|
|
struct AuthKeyCreateStrings {
|
|
bytes::vector dh_prime;
|
|
bytes::vector g_a;
|
|
AuthKey::Data auth_key = { { gsl::byte{} } };
|
|
};
|
|
std::unique_ptr<AuthKeyCreateData> _authKeyData;
|
|
std::unique_ptr<AuthKeyCreateStrings> _authKeyStrings;
|
|
|
|
void dhClientParamsSend();
|
|
void authKeyCreated();
|
|
void clearAuthKeyData();
|
|
|
|
};
|
|
|
|
} // namespace internal
|
|
} // namespace MTP
|