diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index 8eecc3088a..d5328d9d71 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -40,7 +40,6 @@ namespace MTP { namespace internal { namespace { -constexpr auto kRecreateKeyId = AuthKey::KeyId(0xFFFFFFFFFFFFFFFFULL); constexpr auto kIntSize = static_cast(sizeof(mtpPrime)); constexpr auto kWaitForBetterTimeout = crl::time(2000); constexpr auto kMinConnectedTimeout = crl::time(1000); @@ -100,6 +99,14 @@ Connection::Connection(not_null instance) : _instance(instance) { } +Connection::~Connection() { + Expects(_private == nullptr); + + if (_thread) { + waitTillFinish(); + } +} + void Connection::start(SessionData *sessionData, ShiftedDcId shiftedDcId) { Expects(_thread == nullptr && _private == nullptr); @@ -144,14 +151,6 @@ QString Connection::transport() const { return _private->transport(); } -Connection::~Connection() { - Expects(_private == nullptr); - - if (_thread) { - waitTillFinish(); - } -} - void ConnectionPrivate::appendTestConnection( DcOptions::Variants::Protocol protocol, const QString &ip, @@ -215,11 +214,14 @@ int16 ConnectionPrivate::getProtocolDcId() const { } void ConnectionPrivate::destroyAllConnections() { + { + QReadLocker lockFinished(&_sessionDataMutex); + clearKeyCreatorOnFail(); + } _waitForBetterTimer.cancel(); _waitForReceivedTimer.cancel(); _waitForConnectedTimer.cancel(); _testConnections.clear(); - _keyCreator = nullptr; _connection = nullptr; } @@ -250,7 +252,7 @@ ConnectionPrivate::ConnectionPrivate( connect(thread, &QThread::started, this, [=] { connectToServer(); }); connect(thread, &QThread::finished, this, [=] { finishAndDestroy(); }); - connect(_sessionData->owner(), SIGNAL(authKeyCreated()), this, SLOT(updateAuthKey()), Qt::QueuedConnection); + connect(_sessionData->owner(), SIGNAL(authKeyChanged()), this, SLOT(updateAuthKey()), Qt::QueuedConnection); connect(_sessionData->owner(), SIGNAL(needToRestart()), this, SLOT(restartNow()), Qt::QueuedConnection); connect(this, SIGNAL(needToReceive()), _sessionData->owner(), SLOT(tryToReceive()), Qt::QueuedConnection); connect(this, SIGNAL(stateChanged(qint32)), _sessionData->owner(), SLOT(onConnectionStateChange(qint32)), Qt::QueuedConnection); @@ -274,6 +276,13 @@ ConnectionPrivate::ConnectionPrivate( connect(this, SIGNAL(resendAllAsync()), _sessionData->owner(), SLOT(resendAll()), Qt::QueuedConnection); } +ConnectionPrivate::~ConnectionPrivate() { + Expects(_finished); + Expects(!_connection); + Expects(_testConnections.empty()); + Expects(!_keyCreator); +} + void ConnectionPrivate::onConfigLoaded() { connectToServer(true); } @@ -562,11 +571,11 @@ mtpMsgId ConnectionPrivate::placeToContainer(SecureRequest &toSendRequest, mtpMs void ConnectionPrivate::tryToSend() { QReadLocker lockFinished(&_sessionDataMutex); - if (!_sessionData || !_connection) { + if (!_sessionData || !_connection || !_keyId) { return; } - auto needsLayer = !_connectionOptions->inited; + auto needsLayer = !_sessionData->owner()->connectionInited(); auto state = getState(); auto sendOnlyFirstPing = (state != ConnectedState); if (sendOnlyFirstPing && !_pingIdToSend) { @@ -650,7 +659,7 @@ void ConnectionPrivate::tryToSend() { _shiftedDcId, keyForCheck); checkDcKeyRequest = _keyChecker->prepareRequest( - _sessionData->getKey(), + _key, _sessionData->getSessionId()); // This is a special request with msgId used inside the message @@ -771,7 +780,9 @@ void ConnectionPrivate::tryToSend() { auto &haveSent = _sessionData->haveSentMap(); haveSent.insert(msgId, toSendRequest); - if (needsLayer && !toSendRequest->needsLayer) needsLayer = false; + if (needsLayer && !toSendRequest->needsLayer) { + needsLayer = false; + } if (toSendRequest->after) { const auto toSendSize = tl::count_length(toSendRequest) >> 2; auto wrappedRequest = SecureRequest::Prepare( @@ -844,6 +855,7 @@ void ConnectionPrivate::tryToSend() { // prepare "request-like" wrap for msgId vector auto haveSentIdsWrap = SecureRequest::Prepare(idsWrapSize); + haveSentIdsWrap->msDate = 0; // Container: msDate = 0, seqNo = 0. haveSentIdsWrap->requestId = 0; haveSentIdsWrap->resize(haveSentIdsWrap->size() + idsWrapSize); auto haveSentArr = (mtpMsgId*)(haveSentIdsWrap->data() + 8); @@ -931,15 +943,6 @@ void ConnectionPrivate::retryByTimer() { } else if (_retryTimeout < 64000) { _retryTimeout *= 2; } - if (_keyId == kRecreateKeyId) { - if (_sessionData->getKey()) { - unlockKey(); - - QWriteLocker lock(_sessionData->keyMutex()); - _sessionData->owner()->destroyKey(); - } - _keyId = 0; - } connectToServer(); } @@ -964,7 +967,8 @@ void ConnectionPrivate::connectToServer(bool afterConfig) { } _connectionOptions = std::make_unique( _sessionData->connectionOptions()); - const auto hasKey = (_sessionData->getKey() != nullptr); + // #TODO race. + const auto hasKey = (_sessionData->owner()->getKey() != nullptr); lockFinished.unlock(); const auto bareDc = BareDcId(_shiftedDcId); @@ -1212,13 +1216,6 @@ void ConnectionPrivate::connectingTimedOut() { void ConnectionPrivate::doDisconnect() { destroyAllConnections(); - { - QReadLocker lockFinished(&_sessionDataMutex); - if (_sessionData) { - unlockKey(); - } - } - setState(DisconnectedState); _restarted = false; } @@ -1257,21 +1254,6 @@ void ConnectionPrivate::handleReceived() { restart(); }; - ReadLockerAttempt lock(_sessionData->keyMutex()); - if (!lock) { - DEBUG_LOG(("MTP Error: auth_key for dc %1 busy, cant lock").arg(_shiftedDcId)); - clearMessages(); - _keyId = 0; - - return restartOnError(); - } - - auto key = _sessionData->getKey(); - if (!key || key->keyId() != _keyId) { - DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(_shiftedDcId)); - return restartOnError(); - } - while (!_connection->received().empty()) { auto intsBuffer = std::move(_connection->received().front()); _connection->received().pop_front(); @@ -1302,9 +1284,9 @@ void ConnectionPrivate::handleReceived() { auto msgKey = *(MTPint128*)(ints + 2); #ifdef TDESKTOP_MTPROTO_OLD - aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, key, msgKey); + aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _key, msgKey); #else // TDESKTOP_MTPROTO_OLD - aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, key, msgKey); + aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _key, msgKey); #endif // TDESKTOP_MTPROTO_OLD auto decryptedInts = reinterpret_cast(decryptedBuffer.constData()); @@ -1351,7 +1333,7 @@ void ConnectionPrivate::handleReceived() { SHA256_CTX msgKeyLargeContext; SHA256_Init(&msgKeyLargeContext); - SHA256_Update(&msgKeyLargeContext, key->partForMsgKey(false), 32); + SHA256_Update(&msgKeyLargeContext, _key->partForMsgKey(false), 32); SHA256_Update(&msgKeyLargeContext, decryptedInts, encryptedBytesCount); SHA256_Final(sha256Buffer.data(), &msgKeyLargeContext); @@ -1960,10 +1942,7 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr // An error could be some RPC_CALL_FAIL or other error inside // the initConnection, so we're not sure yet that it was inited. // Wait till a good response is received. - if (!_connectionOptions->inited) { - _connectionOptions->inited = true; - _sessionData->notifyConnectionInited(*_connectionOptions); - } + _sessionData->notifyConnectionInited(*_connectionOptions); } if (_keyChecker && _keyChecker->handleResponse(reqMsgId, response)) { @@ -2346,7 +2325,7 @@ void ConnectionPrivate::onConnected( _testConnections.clear(); lockFinished.unlock(); - updateAuthKey(); + checkAuthKey(); } } @@ -2383,7 +2362,7 @@ void ConnectionPrivate::confirmBestConnection() { _connection = std::move(i->data); _testConnections.clear(); - updateAuthKey(); + checkAuthKey(); } void ConnectionPrivate::removeTestConnection( @@ -2396,51 +2375,58 @@ void ConnectionPrivate::removeTestConnection( end(_testConnections)); } +void ConnectionPrivate::checkAuthKey() { + if (!_keyId) { + updateAuthKey(); + } else { + authKeyChecked(); + } +} + void ConnectionPrivate::updateAuthKey() { QReadLocker lockFinished(&_sessionDataMutex); - if (!_sessionData || !_connection) { + if (!_sessionData || _keyCreator) { return; } DEBUG_LOG(("AuthKey Info: Connection updating key from Session, dc %1").arg(_shiftedDcId)); - uint64 newKeyId = 0; - { - ReadLockerAttempt lock(_sessionData->keyMutex()); - if (!lock) { - DEBUG_LOG(("MTP Info: could not lock auth_key for read, waiting signal emit")); - clearMessages(); - _keyId = newKeyId; - return; // some other connection is getting key + _key = _sessionData->owner()->getKey(); + const auto newKeyId = _key ? _key->keyId() : 0; + if (newKeyId) { + if (_keyId == newKeyId) { + return; } - auto key = _sessionData->getKey(); - newKeyId = key ? key->keyId() : 0; + _sessionData->setCurrentKeyId(newKeyId); } - if (_keyId != newKeyId) { - clearMessages(); - _keyId = newKeyId; + _keyId = newKeyId; + if (!_connection) { + return; + } + if (const auto already = _connection->sentEncryptedWithKeyId()) { + Assert(already != newKeyId); + DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(_shiftedDcId)); + + lockFinished.unlock(); + restart(); + return; } DEBUG_LOG(("AuthKey Info: Connection update key from Session, dc %1 result: %2").arg(_shiftedDcId).arg(Logs::mb(&_keyId, sizeof(_keyId)).str())); if (_keyId) { - return authKeyCreated(); + return authKeyChecked(); } - DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), will be creating auth_key")); - lockKey(); - - const auto &key = _sessionData->getKey(); - if (key) { - if (_keyId != key->keyId()) clearMessages(); - _keyId = key->keyId(); - unlockKey(); - return authKeyCreated(); - } else if (_instance->isKeysDestroyer()) { + if (_instance->isKeysDestroyer()) { // We are here to destroy an old key, so we're done. LOG(("MTP Error: No key %1 in updateAuthKey() for destroying.").arg(_shiftedDcId)); _instance->checkIfKeyWasDestroyed(_shiftedDcId); return; + } else if (!_sessionData->owner()->acquireKeyCreation()) { + DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), but someone is creating already.")); + return; } lockFinished.unlock(); + DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), creating.")); createDcKey(); } @@ -2449,23 +2435,24 @@ void ConnectionPrivate::createDcKey() { using Error = DcKeyCreator::Error; auto delegate = DcKeyCreator::Delegate(); delegate.done = [=](base::expected result) { - _keyCreator = nullptr; + QReadLocker lockFinished(&_sessionDataMutex); + if (!_sessionData) return; if (result) { - QReadLocker lockFinished(&_sessionDataMutex); - if (!_sessionData) return; + DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(result->key->keyId()).arg(result->serverSalt)); _sessionData->setSalt(result->serverSalt); + _sessionData->clearForNewKey(_instance); - auto authKey = std::move(result->key); + _keyCreator = nullptr; + _sessionData->owner()->releaseKeyCreationOnDone( + std::move(result->key)); - DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(authKey->keyId()).arg(result->serverSalt)); - - // slot will call authKeyCreated(). - _sessionData->owner()->notifyKeyCreated(std::move(authKey)); - _sessionData->clear(_instance); - unlockKey(); - } else if (result.error() == Error::UnknownPublicKey) { + updateAuthKey(); + return; + } + clearKeyCreatorOnFail(); + if (result.error() == Error::UnknownPublicKey) { if (_dcType == DcType::Cdn) { LOG(("Warning: CDN public RSA key not found")); requestCDNConfig(); @@ -2489,26 +2476,18 @@ void ConnectionPrivate::createDcKey() { expireIn); } -void ConnectionPrivate::clearMessages() { - if (_keyId && _keyId != kRecreateKeyId && _connection) { - _connection->received().clear(); - } -} - -void ConnectionPrivate::authKeyCreated() { - _keyCreator = nullptr; - +void ConnectionPrivate::authKeyChecked() { connect(_connection, &AbstractConnection::receivedData, [=] { handleReceived(); }); - if (_sessionData->getSalt()) { // else receive salt in bad_server_salt first, then try to send all the requests + if (_sessionData->getSalt()) { setState(ConnectedState); if (_restarted) { emit resendAllAsync(); _restarted = false; } - } + } // else receive salt in bad_server_salt first, then try to send all the requests _pingIdToSend = rand_value(); // get server_salt @@ -2542,8 +2521,7 @@ void ConnectionPrivate::handleError(int errorCode) { if (errorCode == -404) { if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) { LOG(("MTP Info: -404 error received in CDN dc %1, assuming it was destroyed, recreating.").arg(_shiftedDcId)); - clearMessages(); - _keyId = kRecreateKeyId; + destroyCdnKey(); return restart(); } else { LOG(("MTP Info: -404 error received, informing instance.")); @@ -2557,7 +2535,16 @@ void ConnectionPrivate::handleError(int errorCode) { return restart(); } -void ConnectionPrivate::onReadyData() { +void ConnectionPrivate::destroyCdnKey() { + if (_key) { + QReadLocker lockFinished(&_sessionDataMutex); + if (_sessionData) { + _sessionData->owner()->destroyCdnKey(_keyId); + } + } + _key = nullptr; + _keyId = 0; + } bool ConnectionPrivate::sendSecureRequest( @@ -2581,24 +2568,6 @@ bool ConnectionPrivate::sendSecureRequest( return false; } - auto lock = ReadLockerAttempt(_sessionData->keyMutex()); - if (!lock) { - DEBUG_LOG(("MTP Info: could not lock key for read in sendBuffer(), dc %1, restarting...").arg(_shiftedDcId)); - - lockFinished.unlock(); - restart(); - return false; - } - - auto key = _sessionData->getKey(); - if (!key || key->keyId() != _keyId) { - DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(_shiftedDcId)); - - lockFinished.unlock(); - restart(); - return false; - } - auto session = _sessionData->getSessionId(); auto salt = _sessionData->getSalt(); @@ -2626,7 +2595,7 @@ bool ConnectionPrivate::sendSecureRequest( request->constData(), &packet[prefix], fullSize * sizeof(mtpPrime), - key, + _key, msgKey); #else // TDESKTOP_MTPROTO_OLD uchar encryptedSHA256[32]; @@ -2634,7 +2603,7 @@ bool ConnectionPrivate::sendSecureRequest( SHA256_CTX msgKeyLargeContext; SHA256_Init(&msgKeyLargeContext); - SHA256_Update(&msgKeyLargeContext, key->partForMsgKey(true), 32); + SHA256_Update(&msgKeyLargeContext, _key->partForMsgKey(true), 32); SHA256_Update(&msgKeyLargeContext, request->constData(), fullSize * sizeof(mtpPrime)); SHA256_Final(encryptedSHA256, &msgKeyLargeContext); @@ -2646,13 +2615,13 @@ bool ConnectionPrivate::sendSecureRequest( request->constData(), &packet[prefix], fullSize * sizeof(mtpPrime), - key, + _key, msgKey); #endif // TDESKTOP_MTPROTO_OLD DEBUG_LOG(("MTP Info: sending request, size: %1, num: %2, time: %3").arg(fullSize + 6).arg((*request)[4]).arg((*request)[5])); - _connection->setSentEncrypted(); + _connection->setSentEncryptedWithKeyId(_keyId); _connection->sendData(std::move(packet)); if (needAnyResponse) { @@ -2689,39 +2658,24 @@ mtpRequestId ConnectionPrivate::wasSent(mtpMsgId msgId) const { return 0; } -void ConnectionPrivate::lockKey() { - unlockKey(); - if (const auto mutex = _sessionData->keyMutex()) { - mutex->lockForWrite(); - } - _myKeyLock = true; -} +// _sessionDataMutex must be locked for read. +void ConnectionPrivate::clearKeyCreatorOnFail() { + if (_keyCreator) { + _keyCreator = nullptr; -void ConnectionPrivate::unlockKey() { - if (_myKeyLock) { - _myKeyLock = false; - if (const auto mutex = _sessionData->keyMutex()) { - mutex->unlock(); - } + Assert(_sessionData != nullptr); + _sessionData->owner()->releaseKeyCreationOnFail(); } } -ConnectionPrivate::~ConnectionPrivate() { - Expects(_finished); - Expects(!_connection); - Expects(_testConnections.empty()); - Expects(!_keyCreator); -} - void ConnectionPrivate::stop() { QWriteLocker lockFinished(&_sessionDataMutex); - if (_sessionData) { - if (_myKeyLock) { - _sessionData->owner()->notifyKeyCreated(AuthKeyPtr()); // release key lock, let someone else create it - unlockKey(); - } - _sessionData = nullptr; + if (!_sessionData) { + Assert(_keyCreator == nullptr); + return; } + clearKeyCreatorOnFail(); + _sessionData = nullptr; } } // namespace internal diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index 8a8339f987..8a60844743 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -41,12 +41,12 @@ public: }; Connection(not_null instance); + ~Connection(); void start(SessionData *data, ShiftedDcId shiftedDcId); void kill(); void waitTillFinish(); - ~Connection(); static const int UpdateAlways = 666; @@ -99,14 +99,6 @@ public slots: void onPingSendForce(); - void onSentSome(uint64 size); - void onReceivedSome(); - - void onReadyData(); - - // General packet receive slot, connected to conn->receivedData signal - void handleReceived(); - // Sessions signals, when we need to send something void tryToSend(); @@ -132,6 +124,10 @@ private: qint32 errorCode); void onConnected(not_null connection); void onDisconnected(not_null connection); + void onSentSome(uint64 size); + void onReceivedSome(); + + void handleReceived(); void retryByTimer(); void waitConnectedFailed(); @@ -140,7 +136,9 @@ private: void markConnectionOld(); void sendPingByTimer(); + // Locks _sessionDataMutex. void destroyAllConnections(); + void confirmBestConnection(); void removeTestConnection(not_null connection); int16 getProtocolDcId() const; @@ -170,8 +168,6 @@ private: mtpBuffer ungzip(const mtpPrime *from, const mtpPrime *end) const; void handleMsgsStates(const QVector &ids, const QByteArray &states, QVector &acked); - void clearMessages(); - bool setState(int32 state, int32 ifState = Connection::UpdateAlways); void appendTestConnection( @@ -191,9 +187,12 @@ private: void createDcKey(); void resetSession(); - void lockKey(); - void unlockKey(); - void authKeyCreated(); + void checkAuthKey(); + void authKeyChecked(); + void destroyCdnKey(); + + // _sessionDataMutex must be locked for read. + void clearKeyCreatorOnFail(); not_null _instance; DcType _dcType = DcType::Regular; @@ -235,13 +234,12 @@ private: bool _restarted = false; bool _finished = false; + AuthKeyPtr _key; uint64 _keyId = 0; QReadWriteLock _sessionDataMutex; SessionData *_sessionData = nullptr; std::unique_ptr _connectionOptions; - bool _myKeyLock = false; - std::unique_ptr _keyCreator; std::unique_ptr _keyChecker; diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.h b/Telegram/SourceFiles/mtproto/connection_abstract.h index 802f9fbd9d..b84ae0d080 100644 --- a/Telegram/SourceFiles/mtproto/connection_abstract.h +++ b/Telegram/SourceFiles/mtproto/connection_abstract.h @@ -100,8 +100,11 @@ public: [[nodiscard]] virtual QString transport() const = 0; [[nodiscard]] virtual QString tag() const = 0; - void setSentEncrypted() { - _sentEncrypted = true; + void setSentEncryptedWithKeyId(uint64 keyId) { + _sentEncryptedWithKeyId = keyId; + } + [[nodiscard]] uint64 sentEncryptedWithKeyId() const { + return _sentEncryptedWithKeyId; } using BuffersQueue = std::deque; @@ -137,7 +140,6 @@ signals: protected: BuffersQueue _receivedQueue; // list of received packets, not processed yet - bool _sentEncrypted = false; int _pingTime = 0; ProxyData _proxy; @@ -150,6 +152,8 @@ protected: private: [[nodiscard]] uint32 extendedNotSecurePadding() const; + uint64 _sentEncryptedWithKeyId = 0; + }; template diff --git a/Telegram/SourceFiles/mtproto/dcenter.cpp b/Telegram/SourceFiles/mtproto/dcenter.cpp index f1ce5c7694..970ca3f39e 100644 --- a/Telegram/SourceFiles/mtproto/dcenter.cpp +++ b/Telegram/SourceFiles/mtproto/dcenter.cpp @@ -23,53 +23,81 @@ constexpr auto kSpecialRequestTimeoutMs = 6000; // 4 seconds timeout for it to w } // namespace -Dcenter::Dcenter(not_null instance, DcId dcId, AuthKeyPtr &&key) -: _instance(instance) -, _id(dcId) +Dcenter::Dcenter(DcId dcId, AuthKeyPtr &&key) +: _id(dcId) , _key(std::move(key)) { - connect(this, SIGNAL(authKeyCreated()), this, SLOT(authKeyWrite()), Qt::QueuedConnection); } -void Dcenter::authKeyWrite() { - DEBUG_LOG(("AuthKey Info: MTProtoDC::authKeyWrite() slot, dc %1").arg(_id)); - if (_key) { - Local::writeMtpData(); - } +DcId Dcenter::id() const { + return _id; } -void Dcenter::setKey(AuthKeyPtr &&key) { - DEBUG_LOG(("AuthKey Info: MTProtoDC::setKey(%1), emitting authKeyCreated, dc %2").arg(key ? key->keyId() : 0).arg(_id)); - _key = std::move(key); - _connectionInited = false; - _instance->setKeyForWrite(_id, _key); - emit authKeyCreated(); -} - -QReadWriteLock *Dcenter::keyMutex() const { - return &keyLock; -} - -const AuthKeyPtr &Dcenter::getKey() const { +AuthKeyPtr Dcenter::getKey() const { + QReadLocker lock(&_mutex); return _key; } -void Dcenter::destroyKey() { - setKey(AuthKeyPtr()); +void Dcenter::destroyCdnKey(uint64 keyId) { + destroyKey(keyId); +} + +bool Dcenter::destroyConfirmedForgottenKey(uint64 keyId) { + return destroyKey(keyId); +} + +bool Dcenter::destroyKey(uint64 keyId) { + Expects(!_creatingKey || !_key); + + QWriteLocker lock(&_mutex); + if (_key->keyId() != keyId) { + return false; + } + _key = nullptr; + _connectionInited = false; + lock.unlock(); + + emit authKeyChanged(); + return true; } bool Dcenter::connectionInited() const { - const auto lock = QMutexLocker(&_initLock); + QReadLocker lock(&_mutex); return _connectionInited; } void Dcenter::setConnectionInited(bool connectionInited) { - auto lock = QMutexLocker(&_initLock); + QWriteLocker lock(&_mutex); _connectionInited = connectionInited; +} + +bool Dcenter::acquireKeyCreation() { + QReadLocker lock(&_mutex); + if (_key != nullptr) { + return false; + } + auto expected = false; + return _creatingKey.compare_exchange_strong(expected, true); +} + +void Dcenter::releaseKeyCreationOnFail() { + Expects(_creatingKey); + Expects(_key == nullptr); + + _creatingKey = false; +} + +void Dcenter::releaseKeyCreationOnDone(AuthKeyPtr &&key) { + Expects(_creatingKey); + Expects(_key == nullptr); + + QWriteLocker lock(&_mutex); + DEBUG_LOG(("AuthKey Info: Dcenter::releaseKeyCreationOnDone(%1), emitting authKeyChanged, dc %2").arg(key ? key->keyId() : 0).arg(_id)); + _key = std::move(key); + _connectionInited = false; + _creatingKey = false; lock.unlock(); - if (connectionInited) { - emit connectionWasInited(); - } + emit authKeyChanged(); } } // namespace internal diff --git a/Telegram/SourceFiles/mtproto/dcenter.h b/Telegram/SourceFiles/mtproto/dcenter.h index 14e0643390..002762b1cb 100644 --- a/Telegram/SourceFiles/mtproto/dcenter.h +++ b/Telegram/SourceFiles/mtproto/dcenter.h @@ -19,30 +19,35 @@ class Dcenter : public QObject { Q_OBJECT public: - Dcenter(not_null instance, DcId dcId, AuthKeyPtr &&key); + // Main thread. + Dcenter(DcId dcId, AuthKeyPtr &&key); - QReadWriteLock *keyMutex() const; - const AuthKeyPtr &getKey() const; - void setKey(AuthKeyPtr &&key); - void destroyKey(); + // Thread-safe. + [[nodiscard]] DcId id() const; + + [[nodiscard]] AuthKeyPtr getKey() const; + void destroyCdnKey(uint64 keyId); + bool destroyConfirmedForgottenKey(uint64 keyId); + void releaseKeyCreationOnDone(AuthKeyPtr &&key); [[nodiscard]] bool connectionInited() const; void setConnectionInited(bool connectionInited = true); -signals: - void authKeyCreated(); - void connectionWasInited(); + [[nodiscard]] bool acquireKeyCreation(); + void releaseKeyCreationOnFail(); -private slots: - void authKeyWrite(); +signals: + void authKeyChanged(); private: - mutable QReadWriteLock keyLock; - mutable QMutex _initLock; - not_null _instance; - DcId _id = 0; + bool destroyKey(uint64 keyId); + + const DcId _id = 0; + mutable QReadWriteLock _mutex; + AuthKeyPtr _key; bool _connectionInited = false; + std::atomic _creatingKey = false; }; diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp index 9cb1cade2d..b9532ec089 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp +++ b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.cpp @@ -172,6 +172,9 @@ DcKeyCreator::DcKeyCreator( } DcKeyCreator::~DcKeyCreator() { + if (_delegate.done) { + stopReceiving(); + } const auto clearBytes = [](bytes::span bytes) { OPENSSL_cleanse(bytes.data(), bytes.size()); }; @@ -191,11 +194,7 @@ void DcKeyCreator::pqSend() { } void DcKeyCreator::pqAnswered() { - QObject::disconnect( - _connection, - &AbstractConnection::receivedData, - nullptr, - nullptr); + stopReceiving(); DEBUG_LOG(("AuthKey Info: receiving Req_pq answer...")); MTPReq_pq::ResponseType res_pq; @@ -272,11 +271,7 @@ void DcKeyCreator::pqAnswered() { } void DcKeyCreator::dhParamsAnswered() { - QObject::disconnect( - _connection, - &AbstractConnection::receivedData, - nullptr, - nullptr); + stopReceiving(); DEBUG_LOG(("AuthKey Info: receiving Req_DH_params answer...")); MTPReq_DH_params::ResponseType res_DH_params; @@ -450,11 +445,7 @@ void DcKeyCreator::dhClientParamsSend() { } void DcKeyCreator::dhClientParamsAnswered() { - QObject::disconnect( - _connection, - &AbstractConnection::receivedData, - nullptr, - nullptr); + stopReceiving(); DEBUG_LOG(("AuthKey Info: receiving Req_client_DH_params answer...")); MTPSet_client_DH_params::ResponseType res_client_DH_params; @@ -578,7 +569,8 @@ bool DcKeyCreator::readNotSecureResponse(Response &response) { } void DcKeyCreator::failed(Error error) { - auto onstack = std::move(_delegate.done); + stopReceiving(); + auto onstack = base::take(_delegate.done); onstack(tl::unexpected(error)); } @@ -589,8 +581,18 @@ void DcKeyCreator::done(uint64 serverSalt) { _dcId, _authKey); result.serverSalt = serverSalt; - auto onstack = std::move(_delegate.done); + + stopReceiving(); + auto onstack = base::take(_delegate.done); onstack(std::move(result)); } +void DcKeyCreator::stopReceiving() { + QObject::disconnect( + _connection, + &AbstractConnection::receivedData, + nullptr, + nullptr); +} + } // namespace MTP::details diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.h b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.h index 2dfa2da86c..c5738cd486 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.h +++ b/Telegram/SourceFiles/mtproto/details/mtproto_dc_key_creator.h @@ -83,6 +83,7 @@ private: void dhClientParamsSend(); void dhClientParamsAnswered(); + void stopReceiving(); void failed(Error error = Error::Other); void done(uint64 serverSalt); diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 769810e005..62cd32a3a4 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -645,7 +645,7 @@ not_null Instance::Private::addDc( const auto dcId = BareDcId(shiftedDcId); return _dcenters.emplace( shiftedDcId, - std::make_unique(_instance, dcId, std::move(key)) + std::make_unique(dcId, std::move(key)) ).first->second.get(); } @@ -690,6 +690,10 @@ void Instance::Private::setKeyForWrite(DcId dcId, const AuthKeyPtr &key) { } else { _keysForWrite.erase(dcId); } + crl::on_main(_instance, [=] { + DEBUG_LOG(("AuthKey Info: writing auth keys, called by dc %1").arg(dcId)); + Local::writeMtpData(); + }); } AuthKeysList Instance::Private::getKeysForWrite() const { @@ -1578,17 +1582,16 @@ void Instance::Private::checkMainDcKey() { } void Instance::Private::keyDestroyedOnServer(DcId dcId, uint64 keyId) { - if (dcId == _mainDcId) { - for (const auto &[id, dc] : _dcenters) { - dc->destroyKey(); + LOG(("Destroying key for dc: %1").arg(dcId)); + if (const auto dc = findDc(dcId)) { + if (dc->destroyConfirmedForgottenKey(keyId)) { + LOG(("Key destroyed!")); + setKeyForWrite(dcId, nullptr); + } else { + LOG(("Key already is different.")); } - restart(); - } else { - if (const auto dc = findDc(dcId)) { - return dc->destroyKey(); - } - restart(dcId); } + restart(dcId); } void Instance::Private::setUpdatesHandler(RPCDoneHandlerPtr onDone) { @@ -1737,7 +1740,9 @@ void Instance::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) { } void Instance::setKeyForWrite(DcId dcId, const AuthKeyPtr &key) { - _private->setKeyForWrite(dcId, key); + InvokeQueued(this, [=] { + _private->setKeyForWrite(dcId, key); + }); } AuthKeysList Instance::getKeysForWrite() const { diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.h b/Telegram/SourceFiles/mtproto/mtp_instance.h index 2fdbdf23fa..04a1afb1c4 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.h +++ b/Telegram/SourceFiles/mtproto/mtp_instance.h @@ -38,14 +38,16 @@ public: QString deviceModel; QString systemVersion; }; + enum class Mode { Normal, KeysDestroyer, }; - Instance(not_null options, Mode mode, Config &&config); + Instance(not_null options, Mode mode, Config &&config); Instance(const Instance &other) = delete; Instance &operator=(const Instance &other) = delete; + ~Instance(); void resolveProxyDomain(const QString &host); void setGoodProxyDomain(const QString &host, const QString &ip); @@ -56,16 +58,71 @@ public: [[nodiscard]] QString cloudLangCode() const; [[nodiscard]] QString langPackName() const; - // Thread safe. + // Thread-safe. [[nodiscard]] QString deviceModel() const; [[nodiscard]] QString systemVersion() const; - void setKeyForWrite(DcId dcId, const AuthKeyPtr &key); + + // Main thread. [[nodiscard]] AuthKeysList getKeysForWrite() const; void addKeysForDestroy(AuthKeysList &&keys); [[nodiscard]] not_null dcOptions(); + void restart(); + void restart(ShiftedDcId shiftedDcId); + int32 dcstate(ShiftedDcId shiftedDcId = 0); + QString dctransport(ShiftedDcId shiftedDcId = 0); + void ping(); + void cancel(mtpRequestId requestId); + int32 state(mtpRequestId requestId); // < 0 means waiting for such count of ms + + // Main thread. + void killSession(ShiftedDcId shiftedDcId); + void stopSession(ShiftedDcId shiftedDcId); + void reInitConnection(DcId dcId); + void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail); + + void unpaused(); + + void queueQuittingConnection(std::unique_ptr &&connection); + + void setUpdatesHandler(RPCDoneHandlerPtr onDone); + void setGlobalFailHandler(RPCFailHandlerPtr onFail); + void setStateChangedHandler(Fn handler); + void setSessionResetHandler(Fn handler); + void clearGlobalHandlers(); + + void onStateChange(ShiftedDcId shiftedDcId, int32 state); + void onSessionReset(ShiftedDcId shiftedDcId); + + void clearCallbacksDelayed(std::vector &&ids); + + void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end); + bool hasCallbacks(mtpRequestId requestId); + void globalCallback(const mtpPrime *from, const mtpPrime *end); + + // return true if need to clean request data + bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); + + bool isKeysDestroyer() const; + void scheduleKeyDestroy(ShiftedDcId shiftedDcId); + void checkIfKeyWasDestroyed(ShiftedDcId shiftedDcId); + void keyDestroyedOnServer(DcId dcId, uint64 keyId); + + void requestConfig(); + void requestConfigIfOld(); + void requestCDNConfig(); + void setUserPhone(const QString &phone); + void badConfigurationError(); + + void syncHttpUnixtime(); + + void connectionFinished(not_null connection); + + void sendAnything(ShiftedDcId shiftedDcId = 0, crl::time msCanWait = 0); + void sendDcKeyCheck(ShiftedDcId shiftedDcId, const AuthKeyPtr &key); + template mtpRequestId send( const Request &request, @@ -134,60 +191,6 @@ public: afterRequestId); } - void sendAnything(ShiftedDcId shiftedDcId = 0, crl::time msCanWait = 0); - void sendDcKeyCheck(ShiftedDcId shiftedDcId, const AuthKeyPtr &key); - - void restart(); - void restart(ShiftedDcId shiftedDcId); - int32 dcstate(ShiftedDcId shiftedDcId = 0); - QString dctransport(ShiftedDcId shiftedDcId = 0); - void ping(); - void cancel(mtpRequestId requestId); - int32 state(mtpRequestId requestId); // < 0 means waiting for such count of ms - void killSession(ShiftedDcId shiftedDcId); - void stopSession(ShiftedDcId shiftedDcId); - void reInitConnection(DcId dcId); - void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail); - - void unpaused(); - - void queueQuittingConnection(std::unique_ptr &&connection); - - void setUpdatesHandler(RPCDoneHandlerPtr onDone); - void setGlobalFailHandler(RPCFailHandlerPtr onFail); - void setStateChangedHandler(Fn handler); - void setSessionResetHandler(Fn handler); - void clearGlobalHandlers(); - - void onStateChange(ShiftedDcId shiftedDcId, int32 state); - void onSessionReset(ShiftedDcId shiftedDcId); - - void clearCallbacksDelayed(std::vector &&ids); - - void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end); - bool hasCallbacks(mtpRequestId requestId); - void globalCallback(const mtpPrime *from, const mtpPrime *end); - - // return true if need to clean request data - bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); - - bool isKeysDestroyer() const; - void scheduleKeyDestroy(ShiftedDcId shiftedDcId); - void checkIfKeyWasDestroyed(ShiftedDcId shiftedDcId); - void keyDestroyedOnServer(DcId dcId, uint64 keyId); - - void requestConfig(); - void requestConfigIfOld(); - void requestCDNConfig(); - void setUserPhone(const QString &phone); - void badConfigurationError(); - - void syncHttpUnixtime(); - - void connectionFinished(not_null connection); - - ~Instance(); - signals: void configLoaded(); void cdnConfigLoaded(); diff --git a/Telegram/SourceFiles/mtproto/rpc_sender.h b/Telegram/SourceFiles/mtproto/rpc_sender.h index 068bd4d323..4de1165487 100644 --- a/Telegram/SourceFiles/mtproto/rpc_sender.h +++ b/Telegram/SourceFiles/mtproto/rpc_sender.h @@ -261,13 +261,13 @@ private: }; struct RPCCallbackClear { - RPCCallbackClear(mtpRequestId id , int32 code = RPCError::NoError) + RPCCallbackClear(mtpRequestId id, int32 code = RPCError::NoError) : requestId(id) , errorCode(code) { } - mtpRequestId requestId; - int32 errorCode; + mtpRequestId requestId = 0; + int32 errorCode = 0; }; diff --git a/Telegram/SourceFiles/mtproto/session.cpp b/Telegram/SourceFiles/mtproto/session.cpp index 709ebf1ff2..3d525cf7c4 100644 --- a/Telegram/SourceFiles/mtproto/session.cpp +++ b/Telegram/SourceFiles/mtproto/session.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/dcenter.h" #include "mtproto/auth_key.h" #include "base/unixtime.h" +#include "base/openssl_help.h" #include "core/crash_reports.h" #include "facades.h" @@ -63,19 +64,15 @@ ConnectionOptions::ConnectionOptions( , useTcp(useTcp) { } -void SessionData::setKey(const AuthKeyPtr &key) { - if (_authKey != key) { - const auto sessionId = rand_value(); - _authKey = key; - - DEBUG_LOG(("MTP Info: new auth key set in SessionData, id %1, setting random server_session %2").arg(key ? key->keyId() : 0).arg(sessionId)); - QWriteLocker locker(&_lock); - if (_sessionId != sessionId) { - _sessionId = sessionId; - _messagesSent = 0; - } - _layerInited = false; +void SessionData::setCurrentKeyId(uint64 keyId) { + QWriteLocker locker(&_lock); + if (_keyId == keyId) { + return; } + _keyId = keyId; + _sessionId = openssl::RandomValue(); + _messagesSent = 0; + DEBUG_LOG(("MTP Info: new auth key set in SessionData, id %1, setting random server_session %2").arg(_keyId).arg(_sessionId)); } void SessionData::setKeyForCheck(const AuthKeyPtr &key) { @@ -83,25 +80,24 @@ void SessionData::setKeyForCheck(const AuthKeyPtr &key) { } void SessionData::notifyConnectionInited(const ConnectionOptions &options) { - QWriteLocker locker(&_lock); - if (options.cloudLangCode == _options.cloudLangCode - && options.systemLangCode == _options.systemLangCode - && options.langPackName == _options.langPackName - && options.proxy == _options.proxy - && !_options.inited) { - _options.inited = true; - - locker.unlock(); + // #TODO race + const auto current = connectionOptions(); + if (current.cloudLangCode == _options.cloudLangCode + && current.systemLangCode == _options.systemLangCode + && current.langPackName == _options.langPackName + && current.proxy == _options.proxy) { owner()->notifyDcConnectionInited(); } } -void SessionData::clear(Instance *instance) { +void SessionData::clearForNewKey(not_null instance) { auto clearCallbacks = std::vector(); { - QReadLocker locker1(haveSentMutex()), locker2(toResendMutex()), locker3(haveReceivedMutex()), locker4(wereAckedMutex()); - auto receivedResponsesEnd = _receivedResponses.cend(); - clearCallbacks.reserve(_haveSent.size() + _wereAcked.size()); + QReadLocker locker1(haveSentMutex()); + QReadLocker locker2(toResendMutex()); + QReadLocker locker3(haveReceivedMutex()); + QReadLocker locker4(wereAckedMutex()); + clearCallbacks.reserve(_haveSent.size() + _toResend.size() + _wereAcked.size()); for (auto i = _haveSent.cbegin(), e = _haveSent.cend(); i != e; ++i) { auto requestId = i.value()->requestId; if (!_receivedResponses.contains(requestId)) { @@ -147,21 +143,15 @@ Session::Session( : QObject() , _instance(instance) , _shiftedDcId(shiftedDcId) -, _dc(dc) +, _ownedDc(dc ? nullptr : std::make_unique(shiftedDcId, nullptr)) +, _dc(dc ? dc : _ownedDc.get()) , _data(this) , _timeouter([=] { checkRequestsByTimer(); }) , _sender([=] { needToResumeAndSend(); }) { _timeouter.callEach(1000); refreshOptions(); - if (_dc) { - if (const auto lock = ReadLockerAttempt(keyMutex())) { - _data.setKey(_dc->getKey()); - if (_dc->connectionInited()) { - _data.setConnectionInited(); - } - } - connect(_dc, SIGNAL(authKeyCreated()), this, SLOT(authKeyCreatedForDC()), Qt::QueuedConnection); - connect(_dc, SIGNAL(connectionWasInited()), this, SLOT(connectionWasInitedForDC()), Qt::QueuedConnection); + if (sharedDc()) { + connect(_dc, SIGNAL(authKeyChanged()), this, SLOT(authKeyChangedForDC()), Qt::QueuedConnection); } } @@ -199,7 +189,7 @@ void Session::refreshOptions() { const auto useHttp = (proxyType != ProxyData::Type::Mtproto); const auto useIPv4 = true; const auto useIPv6 = Global::TryIPv6(); - _data.applyConnectionOptions(ConnectionOptions( + _data.setConnectionOptions(ConnectionOptions( _instance->systemLangCode(), _instance->cloudLangCode(), _instance->langPackName(), @@ -213,10 +203,7 @@ void Session::refreshOptions() { } void Session::reInitConnection() { - if (_dc) { - _dc->setConnectionInited(false); - } - _data.setConnectionInited(false); + _dc->setConnectionInited(false); restart(); } @@ -315,6 +302,10 @@ void Session::sendMsgsStateInfo(quint64 msgId, QByteArray data) { MTP_msgs_state_info(MTP_long(msgId), MTP_bytes(data)))); } +bool Session::sharedDc() const { + return (_ownedDc == nullptr); +} + void Session::checkRequestsByTimer() { QVector resendingIds; QVector removingIds; // remove very old (10 minutes) containers and resend requests @@ -555,51 +546,44 @@ void Session::sendPrepared( sendAnything(msCanWait); } -QReadWriteLock *Session::keyMutex() const { - return _dc ? _dc->keyMutex() : nullptr; +void Session::authKeyChangedForDC() { + DEBUG_LOG(("AuthKey Info: Session::authKeyCreatedForDC slot, emitting authKeyChanged(), dcWithShift %1").arg(_shiftedDcId)); + emit authKeyChanged(); } -void Session::authKeyCreatedForDC() { - Expects(_dc != nullptr); - - DEBUG_LOG(("AuthKey Info: Session::authKeyCreatedForDC slot, emitting authKeyCreated(), dcWithShift %1").arg(_shiftedDcId)); - _data.setKey(_dc->getKey()); - emit authKeyCreated(); +bool Session::acquireKeyCreation() { + return _dc->acquireKeyCreation(); } -void Session::notifyKeyCreated(AuthKeyPtr &&key) { - DEBUG_LOG(("AuthKey Info: Session::keyCreated(), setting, dcWithShift %1").arg(_shiftedDcId)); - if (_dc) { - _dc->setKey(std::move(key)); - } else { - _data.setKey(std::move(key)); - emit authKeyCreated(); +void Session::releaseKeyCreationOnFail() { + _dc->releaseKeyCreationOnFail(); +} + +void Session::releaseKeyCreationOnDone(AuthKeyPtr &&key) { + DEBUG_LOG(("AuthKey Info: Session key created, setting, dcWithShift %1").arg(_shiftedDcId)); + if (sharedDc()) { + const auto dcId = _dc->id(); + const auto instance = _instance; + InvokeQueued(instance, [=] { + instance->setKeyForWrite(dcId, key); + }); } -} - -void Session::connectionWasInitedForDC() { - Expects(_dc != nullptr); - - DEBUG_LOG(("MTP Info: Session::connectionWasInitedForDC slot, dcWithShift %1").arg(_shiftedDcId)); - _data.setConnectionInited(); + _dc->releaseKeyCreationOnDone(std::move(key)); } void Session::notifyDcConnectionInited() { DEBUG_LOG(("MTP Info: emitting MTProtoDC::connectionWasInited(), dcWithShift %1").arg(_shiftedDcId)); - if (_dc) { - _dc->setConnectionInited(); - } else { - _data.setConnectionInited(); - } + _dc->setConnectionInited(); } -void Session::destroyKey() { - if (const auto key = _data.getKey()) { - DEBUG_LOG(("MTP Info: destroying auth_key for dcWithShift %1").arg(_shiftedDcId)); - if (_dc && _dc->getKey() == key) { - _dc->destroyKey(); - } - _data.setKey(nullptr); +void Session::destroyCdnKey(uint64 keyId) { + _dc->destroyCdnKey(keyId); + if (sharedDc()) { + const auto dcId = _dc->id(); + const auto instance = _instance; + InvokeQueued(instance, [=] { + instance->setKeyForWrite(dcId, nullptr); + }); } } @@ -607,6 +591,14 @@ int32 Session::getDcWithShift() const { return _shiftedDcId; } +AuthKeyPtr Session::getKey() const { + return _dc->getKey(); +} + +bool Session::connectionInited() const { + return _dc->connectionInited(); +} + void Session::tryToReceive() { if (_killed) { DEBUG_LOG(("Session Error: can't receive in a killed session")); diff --git a/Telegram/SourceFiles/mtproto/session.h b/Telegram/SourceFiles/mtproto/session.h index 536998bd87..1d6ffb68c3 100644 --- a/Telegram/SourceFiles/mtproto/session.h +++ b/Telegram/SourceFiles/mtproto/session.h @@ -131,7 +131,6 @@ struct ConnectionOptions { bool useIPv6 = true; bool useHttp = true; bool useTcp = true; - bool inited = false; }; @@ -141,6 +140,7 @@ public: SessionData(not_null creator) : _owner(creator) { } + void setCurrentKeyId(uint64 keyId); void setSessionId(uint64 sessionId) { DEBUG_LOG(("MTP Info: setting server_session: %1").arg(sessionId)); @@ -150,22 +150,16 @@ public: _messagesSent = 0; } } - uint64 getSessionId() const { + [[nodiscard]] uint64 getSessionId() const { QReadLocker locker(&_lock); return _sessionId; } - void setConnectionInited(bool inited = true) { - QWriteLocker locker(&_lock); - _options.inited = inited; - } void notifyConnectionInited(const ConnectionOptions &options); - void applyConnectionOptions(ConnectionOptions options) { + void setConnectionOptions(ConnectionOptions options) { QWriteLocker locker(&_lock); - const auto inited = _options.inited; _options = options; - _options.inited = inited; } - ConnectionOptions connectionOptions() const { + [[nodiscard]] ConnectionOptions connectionOptions() const { QReadLocker locker(&_lock); return _options; } @@ -174,23 +168,16 @@ public: QWriteLocker locker(&_lock); _salt = salt; } - uint64 getSalt() const { + [[nodiscard]] uint64 getSalt() const { QReadLocker locker(&_lock); return _salt; } - const AuthKeyPtr &getKey() const { - return _authKey; - } - void setKey(const AuthKeyPtr &key); - - const AuthKeyPtr &getKeyForCheck() const { + [[nodiscard]] const AuthKeyPtr &getKeyForCheck() const { return _dcKeyForCheck; } void setKeyForCheck(const AuthKeyPtr &key); - QReadWriteLock *keyMutex() const; - not_null toSendMutex() const { return &_toSendLock; } @@ -276,19 +263,17 @@ public: return result * 2 + (needAck ? 1 : 0); } - void clear(Instance *instance); + void clearForNewKey(not_null instance); private: + uint64 _keyId = 0; uint64 _sessionId = 0; uint64 _salt = 0; - uint32 _messagesSent = 0; not_null _owner; - AuthKeyPtr _authKey; AuthKeyPtr _dcKeyForCheck; - bool _layerInited = false; ConnectionOptions _options; PreRequestMap _toSend; // map of request_id -> request, that is waiting to be sent @@ -317,25 +302,34 @@ class Session : public QObject { Q_OBJECT public: + // Main thread. Session( not_null instance, ShiftedDcId shiftedDcId, Dcenter *dc); + ~Session(); void start(); + void reInitConnection(); + void restart(); void refreshOptions(); - void reInitConnection(); void stop(); void kill(); void unpaused(); - ShiftedDcId getDcWithShift() const; + // Thread-safe. + [[nodiscard]] ShiftedDcId getDcWithShift() const; + [[nodiscard]] AuthKeyPtr getKey() const; + [[nodiscard]] bool connectionInited() const; + + // Connection thread. + [[nodiscard]] bool acquireKeyCreation(); + void releaseKeyCreationOnFail(); + void releaseKeyCreationOnDone(AuthKeyPtr &&key); + void destroyCdnKey(uint64 keyId); - QReadWriteLock *keyMutex() const; - void notifyKeyCreated(AuthKeyPtr &&key); - void destroyKey(); void notifyDcConnectionInited(); void ping(); @@ -352,10 +346,8 @@ public: crl::time msCanWait = 0, bool newRequest = true); - ~Session(); - signals: - void authKeyCreated(); + void authKeyChanged(); void needToSend(); void needToPing(); void needToRestart(); @@ -367,8 +359,7 @@ public slots: void resendMany(QVector msgIds, qint64 msCanWait, bool forceContainer, bool sendMsgStateInfo); void resendAll(); // after connection restart - void authKeyCreatedForDC(); - void connectionWasInitedForDC(); + void authKeyChangedForDC(); void tryToReceive(); void onConnectionStateChange(qint32 newState); @@ -379,13 +370,15 @@ public slots: void sendMsgsStateInfo(quint64 msgId, QByteArray data); private: + [[nodiscard]] bool sharedDc() const; void checkRequestsByTimer(); bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); const not_null _instance; const ShiftedDcId _shiftedDcId = 0; - Dcenter *_dc = nullptr; + const std::unique_ptr _ownedDc; + const not_null _dc; std::unique_ptr _connection; @@ -406,38 +399,5 @@ private: }; -inline QReadWriteLock *SessionData::keyMutex() const { - return _owner->keyMutex(); -} - -class ReadLockerAttempt { -public: - ReadLockerAttempt(QReadWriteLock *lock) : _lock(lock), _locked(_lock ? _lock->tryLockForRead() : true) { - } - ReadLockerAttempt(const ReadLockerAttempt &other) = delete; - ReadLockerAttempt &operator=(const ReadLockerAttempt &other) = delete; - ReadLockerAttempt(ReadLockerAttempt &&other) : _lock(other._lock), _locked(base::take(other._locked)) { - } - ReadLockerAttempt &operator=(ReadLockerAttempt &&other) { - _lock = other._lock; - _locked = base::take(other._locked); - return *this; - } - ~ReadLockerAttempt() { - if (_lock && _locked) { - _lock->unlock(); - } - } - - operator bool() const { - return _locked; - } - -private: - QReadWriteLock *_lock = nullptr; - bool _locked = false; - -}; - } // namespace internal } // namespace MTP