1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "mtproto/session.h"
9 
10 #include "mtproto/details/mtproto_dcenter.h"
11 #include "mtproto/session_private.h"
12 #include "mtproto/mtproto_auth_key.h"
13 #include "core/application.h"
14 #include "core/core_settings.h"
15 #include "base/unixtime.h"
16 
17 namespace MTP {
18 namespace details {
19 
SessionOptions(const QString & systemLangCode,const QString & cloudLangCode,const QString & langPackName,const ProxyData & proxy,bool useIPv4,bool useIPv6,bool useHttp,bool useTcp)20 SessionOptions::SessionOptions(
21 	const QString &systemLangCode,
22 	const QString &cloudLangCode,
23 	const QString &langPackName,
24 	const ProxyData &proxy,
25 	bool useIPv4,
26 	bool useIPv6,
27 	bool useHttp,
28 	bool useTcp)
29 : systemLangCode(systemLangCode)
30 , cloudLangCode(cloudLangCode)
31 , langPackName(langPackName)
32 , proxy(proxy)
33 , useIPv4(useIPv4)
34 , useIPv6(useIPv6)
35 , useHttp(useHttp)
36 , useTcp(useTcp) {
37 }
38 
39 template <typename Callback>
withSession(Callback && callback)40 void SessionData::withSession(Callback &&callback) {
41 	QMutexLocker lock(&_ownerMutex);
42 	if (const auto session = _owner) {
43 		InvokeQueued(session, [
44 			session,
45 			callback = std::forward<Callback>(callback)
46 		] {
47 			callback(session);
48 		});
49 	}
50 }
51 
notifyConnectionInited(const SessionOptions & options)52 void SessionData::notifyConnectionInited(const SessionOptions &options) {
53 	// #TODO race
54 	const auto current = this->options();
55 	if (current.cloudLangCode == _options.cloudLangCode
56 		&& current.systemLangCode == _options.systemLangCode
57 		&& current.langPackName == _options.langPackName
58 		&& current.proxy == _options.proxy) {
59 		QMutexLocker lock(&_ownerMutex);
60 		if (_owner) {
61 			_owner->notifyDcConnectionInited();
62 		}
63 	}
64 }
65 
queueTryToReceive()66 void SessionData::queueTryToReceive() {
67 	withSession([](not_null<Session*> session) {
68 		session->tryToReceive();
69 	});
70 }
71 
queueNeedToResumeAndSend()72 void SessionData::queueNeedToResumeAndSend() {
73 	withSession([](not_null<Session*> session) {
74 		session->needToResumeAndSend();
75 	});
76 }
77 
queueConnectionStateChange(int newState)78 void SessionData::queueConnectionStateChange(int newState) {
79 	withSession([=](not_null<Session*> session) {
80 		session->connectionStateChange(newState);
81 	});
82 }
83 
queueResetDone()84 void SessionData::queueResetDone() {
85 	withSession([](not_null<Session*> session) {
86 		session->resetDone();
87 	});
88 }
89 
queueSendAnything(crl::time msCanWait)90 void SessionData::queueSendAnything(crl::time msCanWait) {
91 	withSession([=](not_null<Session*> session) {
92 		session->sendAnything(msCanWait);
93 	});
94 }
95 
connectionInited() const96 bool SessionData::connectionInited() const {
97 	QMutexLocker lock(&_ownerMutex);
98 	return _owner ? _owner->connectionInited() : false;
99 }
100 
getTemporaryKey(TemporaryKeyType type) const101 AuthKeyPtr SessionData::getTemporaryKey(TemporaryKeyType type) const {
102 	QMutexLocker lock(&_ownerMutex);
103 	return _owner ? _owner->getTemporaryKey(type) : nullptr;
104 }
105 
getPersistentKey() const106 AuthKeyPtr SessionData::getPersistentKey() const {
107 	QMutexLocker lock(&_ownerMutex);
108 	return _owner ? _owner->getPersistentKey() : nullptr;
109 }
110 
acquireKeyCreation(DcType type)111 CreatingKeyType SessionData::acquireKeyCreation(DcType type) {
112 	QMutexLocker lock(&_ownerMutex);
113 	return _owner ? _owner->acquireKeyCreation(type) : CreatingKeyType::None;
114 }
115 
releaseKeyCreationOnDone(const AuthKeyPtr & temporaryKey,const AuthKeyPtr & persistentKeyUsedForBind)116 bool SessionData::releaseKeyCreationOnDone(
117 		const AuthKeyPtr &temporaryKey,
118 		const AuthKeyPtr &persistentKeyUsedForBind) {
119 	QMutexLocker lock(&_ownerMutex);
120 	return _owner
121 		? _owner->releaseKeyCreationOnDone(
122 			temporaryKey,
123 			persistentKeyUsedForBind)
124 		: false;
125 }
126 
releaseCdnKeyCreationOnDone(const AuthKeyPtr & temporaryKey)127 bool SessionData::releaseCdnKeyCreationOnDone(
128 		const AuthKeyPtr &temporaryKey) {
129 	QMutexLocker lock(&_ownerMutex);
130 	return _owner
131 		? _owner->releaseCdnKeyCreationOnDone(temporaryKey)
132 		: false;
133 }
134 
releaseKeyCreationOnFail()135 void SessionData::releaseKeyCreationOnFail() {
136 	QMutexLocker lock(&_ownerMutex);
137 	if (_owner) {
138 		_owner->releaseKeyCreationOnFail();
139 	}
140 }
141 
destroyTemporaryKey(uint64 keyId)142 void SessionData::destroyTemporaryKey(uint64 keyId) {
143 	QMutexLocker lock(&_ownerMutex);
144 	if (_owner) {
145 		_owner->destroyTemporaryKey(keyId);
146 	}
147 }
148 
detach()149 void SessionData::detach() {
150 	QMutexLocker lock(&_ownerMutex);
151 	_owner = nullptr;
152 }
153 
Session(not_null<Instance * > instance,not_null<QThread * > thread,ShiftedDcId shiftedDcId,not_null<Dcenter * > dc)154 Session::Session(
155 	not_null<Instance*> instance,
156 	not_null<QThread*> thread,
157 	ShiftedDcId shiftedDcId,
158 	not_null<Dcenter*> dc)
159 : _instance(instance)
160 , _shiftedDcId(shiftedDcId)
161 , _dc(dc)
162 , _data(std::make_shared<SessionData>(this))
163 , _thread(thread)
164 , _sender([=] { needToResumeAndSend(); }) {
165 	refreshOptions();
166 	watchDcKeyChanges();
167 	watchDcOptionsChanges();
168 	start();
169 }
170 
~Session()171 Session::~Session() {
172 	Expects(!_private);
173 
174 	if (_myKeyCreation != CreatingKeyType::None) {
175 		releaseKeyCreationOnFail();
176 	}
177 }
178 
watchDcKeyChanges()179 void Session::watchDcKeyChanges() {
180 	_instance->dcTemporaryKeyChanged(
181 	) | rpl::filter([=](DcId dcId) {
182 		return (dcId == _shiftedDcId) || (dcId == BareDcId(_shiftedDcId));
183 	}) | rpl::start_with_next([=] {
184 		DEBUG_LOG(("AuthKey Info: dcTemporaryKeyChanged in Session %1"
185 			).arg(_shiftedDcId));
186 		if (const auto captured = _private) {
187 			InvokeQueued(captured, [=] {
188 				DEBUG_LOG(("AuthKey Info: calling Connection::updateAuthKey in Session %1"
189 					).arg(_shiftedDcId));
190 				captured->updateAuthKey();
191 			});
192 		}
193 	}, _lifetime);
194 }
195 
watchDcOptionsChanges()196 void Session::watchDcOptionsChanges() {
197 	_instance->dcOptions().changed(
198 	) | rpl::filter([=](DcId dcId) {
199 		return (BareDcId(_shiftedDcId) == dcId) && (_private != nullptr);
200 	}) | rpl::start_with_next([=] {
201 		InvokeQueued(_private, [captured = _private] {
202 			captured->dcOptionsChanged();
203 		});
204 	}, _lifetime);
205 
206 	_instance->dcOptions().cdnConfigChanged(
207 	) | rpl::filter([=] {
208 		return (_private != nullptr)
209 			&& (_instance->dcOptions().dcType(_shiftedDcId) == DcType::Cdn);
210 	}) | rpl::start_with_next([=] {
211 		InvokeQueued(_private, [captured = _private] {
212 			captured->cdnConfigChanged();
213 		});
214 	}, _lifetime);
215 }
216 
start()217 void Session::start() {
218 	killConnection();
219 	_private = new SessionPrivate(
220 		_instance,
221 		_thread.get(),
222 		_data,
223 		_shiftedDcId);
224 }
225 
restart()226 void Session::restart() {
227 	if (_killed) {
228 		DEBUG_LOG(("Session Error: can't restart a killed session"));
229 		return;
230 	}
231 	refreshOptions();
232 	if (const auto captured = _private) {
233 		InvokeQueued(captured, [=] {
234 			captured->restartNow();
235 		});
236 	}
237 }
238 
refreshOptions()239 void Session::refreshOptions() {
240 	auto &settings = Core::App().settings().proxy();
241 	const auto &proxy = settings.selected();
242 	const auto isEnabled = settings.isEnabled();
243 	const auto proxyType = (isEnabled ? proxy.type : ProxyData::Type::None);
244 	const auto useTcp = (proxyType != ProxyData::Type::Http);
245 	const auto useHttp = (proxyType != ProxyData::Type::Mtproto);
246 	const auto useIPv4 = true;
247 	const auto useIPv6 = settings.tryIPv6();
248 	_data->setOptions(SessionOptions(
249 		_instance->systemLangCode(),
250 		_instance->cloudLangCode(),
251 		_instance->langPackName(),
252 		(isEnabled ? proxy : ProxyData()),
253 		useIPv4,
254 		useIPv6,
255 		useHttp,
256 		useTcp));
257 }
258 
reInitConnection()259 void Session::reInitConnection() {
260 	_dc->setConnectionInited(false);
261 	restart();
262 }
263 
stop()264 void Session::stop() {
265 	if (_killed) {
266 		DEBUG_LOG(("Session Error: can't stop a killed session"));
267 		return;
268 	}
269 	DEBUG_LOG(("Session Info: stopping session dcWithShift %1").arg(_shiftedDcId));
270 	killConnection();
271 }
272 
kill()273 void Session::kill() {
274 	stop();
275 	_killed = true;
276 	_data->detach();
277 	DEBUG_LOG(("Session Info: marked session dcWithShift %1 as killed").arg(_shiftedDcId));
278 }
279 
unpaused()280 void Session::unpaused() {
281 	if (_needToReceive) {
282 		_needToReceive = false;
283 		InvokeQueued(this, [=] {
284 			tryToReceive();
285 		});
286 	}
287 }
288 
sendAnything(crl::time msCanWait)289 void Session::sendAnything(crl::time msCanWait) {
290 	if (_killed) {
291 		DEBUG_LOG(("Session Error: can't send anything in a killed session"));
292 		return;
293 	}
294 	const auto ms = crl::now();
295 	if (_msSendCall) {
296 		if (ms > _msSendCall + _msWait) {
297 			_msWait = 0;
298 		} else {
299 			_msWait = (_msSendCall + _msWait) - ms;
300 			if (_msWait > msCanWait) {
301 				_msWait = msCanWait;
302 			}
303 		}
304 	} else {
305 		_msWait = msCanWait;
306 	}
307 	if (_msWait) {
308 		DEBUG_LOG(("MTP Info: dcWithShift %1 can wait for %2ms from current %3").arg(_shiftedDcId).arg(_msWait).arg(_msSendCall));
309 		_msSendCall = ms;
310 		_sender.callOnce(_msWait);
311 	} else {
312 		DEBUG_LOG(("MTP Info: dcWithShift %1 stopped send timer, can wait for %2ms from current %3").arg(_shiftedDcId).arg(_msWait).arg(_msSendCall));
313 		_sender.cancel();
314 		_msSendCall = 0;
315 		needToResumeAndSend();
316 	}
317 }
318 
needToResumeAndSend()319 void Session::needToResumeAndSend() {
320 	if (_killed) {
321 		DEBUG_LOG(("Session Info: can't resume a killed session"));
322 		return;
323 	}
324 	if (!_private) {
325 		DEBUG_LOG(("Session Info: resuming session dcWithShift %1").arg(_shiftedDcId));
326 		start();
327 	}
328 	const auto captured = _private;
329 	const auto ping = base::take(_ping);
330 	InvokeQueued(captured, [=] {
331 		if (ping) {
332 			captured->sendPingForce();
333 		} else {
334 			captured->tryToSend();
335 		}
336 	});
337 }
338 
connectionStateChange(int newState)339 void Session::connectionStateChange(int newState) {
340 	_instance->onStateChange(_shiftedDcId, newState);
341 }
342 
resetDone()343 void Session::resetDone() {
344 	_instance->onSessionReset(_shiftedDcId);
345 }
346 
cancel(mtpRequestId requestId,mtpMsgId msgId)347 void Session::cancel(mtpRequestId requestId, mtpMsgId msgId) {
348 	if (requestId) {
349 		QWriteLocker locker(_data->toSendMutex());
350 		_data->toSendMap().remove(requestId);
351 	}
352 	if (msgId) {
353 		QWriteLocker locker(_data->haveSentMutex());
354 		_data->haveSentMap().remove(msgId);
355 	}
356 }
357 
ping()358 void Session::ping() {
359 	_ping = true;
360 	sendAnything();
361 }
362 
requestState(mtpRequestId requestId) const363 int32 Session::requestState(mtpRequestId requestId) const {
364 	int32 result = MTP::RequestSent;
365 
366 	bool connected = false;
367 	if (_private) {
368 		const auto s = _private->getState();
369 		if (s == ConnectedState) {
370 			connected = true;
371 		} else if (s == ConnectingState || s == DisconnectedState) {
372 			if (result < 0 || result == MTP::RequestSent) {
373 				result = MTP::RequestConnecting;
374 			}
375 		} else if (s < 0) {
376 			if ((result < 0 && s > result) || result == MTP::RequestSent) {
377 				result = s;
378 			}
379 		}
380 	}
381 	if (!connected) {
382 		return result;
383 	} else if (!requestId) {
384 		return MTP::RequestSent;
385 	}
386 
387 	QWriteLocker locker(_data->toSendMutex());
388 	return _data->toSendMap().contains(requestId)
389 		? MTP::RequestSending
390 		: MTP::RequestSent;
391 }
392 
getState() const393 int32 Session::getState() const {
394 	int32 result = -86400000;
395 
396 	if (_private) {
397 		const auto s = _private->getState();
398 		if (s == ConnectedState) {
399 			return s;
400 		} else if (s == ConnectingState || s == DisconnectedState) {
401 			if (result < 0) {
402 				return s;
403 			}
404 		} else if (s < 0) {
405 			if (result < 0 && s > result) {
406 				result = s;
407 			}
408 		}
409 	}
410 	if (result == -86400000) {
411 		result = DisconnectedState;
412 	}
413 	return result;
414 }
415 
transport() const416 QString Session::transport() const {
417 	return _private ? _private->transport() : QString();
418 }
419 
sendPrepared(const SerializedRequest & request,crl::time msCanWait)420 void Session::sendPrepared(
421 		const SerializedRequest &request,
422 		crl::time msCanWait) {
423 	DEBUG_LOG(("MTP Info: adding request to toSendMap, msCanWait %1"
424 		).arg(msCanWait));
425 	{
426 		QWriteLocker locker(_data->toSendMutex());
427 		_data->toSendMap().emplace(request->requestId, request);
428 		*(mtpMsgId*)(request->data() + 4) = 0;
429 		*(request->data() + 6) = 0;
430 	}
431 
432 	DEBUG_LOG(("MTP Info: added, requestId %1").arg(request->requestId));
433 	if (msCanWait >= 0) {
434 		InvokeQueued(this, [=] {
435 			sendAnything(msCanWait);
436 		});
437 	}
438 }
439 
acquireKeyCreation(DcType type)440 CreatingKeyType Session::acquireKeyCreation(DcType type) {
441 	Expects(_myKeyCreation == CreatingKeyType::None);
442 
443 	_myKeyCreation = _dc->acquireKeyCreation(type);
444 	return _myKeyCreation;
445 }
446 
releaseKeyCreationOnDone(const AuthKeyPtr & temporaryKey,const AuthKeyPtr & persistentKeyUsedForBind)447 bool Session::releaseKeyCreationOnDone(
448 		const AuthKeyPtr &temporaryKey,
449 		const AuthKeyPtr &persistentKeyUsedForBind) {
450 	Expects(_myKeyCreation != CreatingKeyType::None);
451 	Expects(persistentKeyUsedForBind != nullptr);
452 
453 	return releaseGenericKeyCreationOnDone(
454 		temporaryKey,
455 		persistentKeyUsedForBind);
456 }
457 
releaseCdnKeyCreationOnDone(const AuthKeyPtr & temporaryKey)458 bool Session::releaseCdnKeyCreationOnDone(
459 		const AuthKeyPtr &temporaryKey) {
460 	Expects(_myKeyCreation == CreatingKeyType::TemporaryRegular);
461 
462 	return releaseGenericKeyCreationOnDone(temporaryKey, nullptr);
463 }
464 
releaseGenericKeyCreationOnDone(const AuthKeyPtr & temporaryKey,const AuthKeyPtr & persistentKeyUsedForBind)465 bool Session::releaseGenericKeyCreationOnDone(
466 		const AuthKeyPtr &temporaryKey,
467 		const AuthKeyPtr &persistentKeyUsedForBind) {
468 	const auto wasKeyCreation = std::exchange(
469 		_myKeyCreation,
470 		CreatingKeyType::None);
471 	const auto result = _dc->releaseKeyCreationOnDone(
472 		wasKeyCreation,
473 		temporaryKey,
474 		persistentKeyUsedForBind);
475 
476 	if (!result) {
477 		DEBUG_LOG(("AuthKey Info: Persistent key changed "
478 			"while binding temporary, dcWithShift %1"
479 			).arg(_shiftedDcId));
480 		return false;
481 	}
482 
483 	DEBUG_LOG(("AuthKey Info: Session key bound, setting, dcWithShift %1"
484 		).arg(_shiftedDcId));
485 
486 	const auto dcId = _dc->id();
487 	const auto instance = _instance;
488 	InvokeQueued(instance, [=] {
489 		if (wasKeyCreation == CreatingKeyType::Persistent) {
490 			instance->dcPersistentKeyChanged(dcId, persistentKeyUsedForBind);
491 		} else {
492 			instance->dcTemporaryKeyChanged(dcId);
493 		}
494 	});
495 	return true;
496 }
497 
releaseKeyCreationOnFail()498 void Session::releaseKeyCreationOnFail() {
499 	Expects(_myKeyCreation != CreatingKeyType::None);
500 
501 	const auto wasKeyCreation = std::exchange(
502 		_myKeyCreation,
503 		CreatingKeyType::None);
504 	_dc->releaseKeyCreationOnFail(wasKeyCreation);
505 }
506 
notifyDcConnectionInited()507 void Session::notifyDcConnectionInited() {
508 	DEBUG_LOG(("MTP Info: MTProtoDC::connectionWasInited(), dcWithShift %1"
509 		).arg(_shiftedDcId));
510 	_dc->setConnectionInited();
511 }
512 
destroyTemporaryKey(uint64 keyId)513 void Session::destroyTemporaryKey(uint64 keyId) {
514 	if (!_dc->destroyTemporaryKey(keyId)) {
515 		return;
516 	}
517 	const auto dcId = _dc->id();
518 	const auto instance = _instance;
519 	InvokeQueued(instance, [=] {
520 		instance->dcTemporaryKeyChanged(dcId);
521 	});
522 }
523 
getDcWithShift() const524 int32 Session::getDcWithShift() const {
525 	return _shiftedDcId;
526 }
527 
getTemporaryKey(TemporaryKeyType type) const528 AuthKeyPtr Session::getTemporaryKey(TemporaryKeyType type) const {
529 	return _dc->getTemporaryKey(type);
530 }
531 
getPersistentKey() const532 AuthKeyPtr Session::getPersistentKey() const {
533 	return _dc->getPersistentKey();
534 }
535 
connectionInited() const536 bool Session::connectionInited() const {
537 	return _dc->connectionInited();
538 }
539 
tryToReceive()540 void Session::tryToReceive() {
541 	if (_killed) {
542 		DEBUG_LOG(("Session Error: can't receive in a killed session"));
543 		return;
544 	}
545 	if (paused()) {
546 		_needToReceive = true;
547 		return;
548 	}
549 	while (true) {
550 		auto lock = QWriteLocker(_data->haveReceivedMutex());
551 		const auto messages = base::take(_data->haveReceivedMessages());
552 		lock.unlock();
553 		if (messages.empty()) {
554 			break;
555 		}
556 		for (const auto &message : messages) {
557 			if (message.requestId) {
558 				_instance->processCallback(message);
559 			} else if (_shiftedDcId == BareDcId(_shiftedDcId)) {
560 				// Process updates only in main session.
561 				_instance->processUpdate(message);
562 			}
563 		}
564 	}
565 }
566 
killConnection()567 void Session::killConnection() {
568 	if (!_private) {
569 		return;
570 	}
571 
572 	base::take(_private)->deleteLater();
573 
574 	Ensures(_private == nullptr);
575 }
576 
577 } // namespace details
578 } // namespace MTP
579