config_loader.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "mtproto/config_loader.h"
  8. #include "base/random.h"
  9. #include "mtproto/special_config_request.h"
  10. #include "mtproto/facade.h"
  11. #include "mtproto/mtproto_dc_options.h"
  12. #include "mtproto/mtproto_config.h"
  13. #include "mtproto/mtp_instance.h"
  14. namespace MTP {
  15. namespace details {
  16. namespace {
  17. constexpr auto kEnumerateDcTimeout = 8000; // 8 seconds timeout for help_getConfig to work (then move to other dc)
  18. constexpr auto kSpecialRequestTimeoutMs = 6000; // 4 seconds timeout for it to work in a specially requested dc.
  19. } // namespace
  20. ConfigLoader::ConfigLoader(
  21. not_null<Instance*> instance,
  22. const QString &phone,
  23. Fn<void(const MTPConfig &result)> onDone,
  24. FailHandler onFail,
  25. bool proxyEnabled)
  26. : _instance(instance)
  27. , _phone(phone)
  28. , _proxyEnabled(proxyEnabled)
  29. , _doneHandler(onDone)
  30. , _failHandler(onFail) {
  31. _enumDCTimer.setCallback([this] { enumerate(); });
  32. _specialEnumTimer.setCallback([this] { sendSpecialRequest(); });
  33. }
  34. void ConfigLoader::load() {
  35. if (!_instance->isKeysDestroyer()) {
  36. sendRequest(_instance->mainDcId());
  37. _enumDCTimer.callOnce(kEnumerateDcTimeout);
  38. } else {
  39. auto ids = _instance->dcOptions().configEnumDcIds();
  40. Assert(!ids.empty());
  41. _enumCurrent = ids.front();
  42. enumerate();
  43. }
  44. }
  45. mtpRequestId ConfigLoader::sendRequest(ShiftedDcId shiftedDcId) {
  46. auto done = [done = _doneHandler](const Response &response) {
  47. auto from = response.reply.constData();
  48. auto result = MTPConfig();
  49. if (!result.read(from, from + response.reply.size())) {
  50. return false;
  51. }
  52. done(result);
  53. return true;
  54. };
  55. return _instance->send(
  56. MTPhelp_GetConfig(),
  57. std::move(done),
  58. base::duplicate(_failHandler),
  59. shiftedDcId);
  60. }
  61. DcId ConfigLoader::specialToRealDcId(DcId specialDcId) {
  62. return getTemporaryIdFromRealDcId(specialDcId);
  63. }
  64. void ConfigLoader::terminateRequest() {
  65. if (_enumRequest) {
  66. _instance->cancel(base::take(_enumRequest));
  67. }
  68. if (_enumCurrent) {
  69. _instance->killSession(MTP::configDcId(_enumCurrent));
  70. }
  71. }
  72. void ConfigLoader::terminateSpecialRequest() {
  73. if (_specialEnumRequest) {
  74. _instance->cancel(base::take(_specialEnumRequest));
  75. }
  76. if (_specialEnumCurrent) {
  77. _instance->killSession(_specialEnumCurrent);
  78. }
  79. }
  80. ConfigLoader::~ConfigLoader() {
  81. terminateRequest();
  82. terminateSpecialRequest();
  83. }
  84. void ConfigLoader::enumerate() {
  85. terminateRequest();
  86. if (!_enumCurrent) {
  87. _enumCurrent = _instance->mainDcId();
  88. }
  89. auto ids = _instance->dcOptions().configEnumDcIds();
  90. Assert(!ids.empty());
  91. auto i = std::find(ids.cbegin(), ids.cend(), _enumCurrent);
  92. if (i == ids.cend() || (++i) == ids.cend()) {
  93. _enumCurrent = ids.front();
  94. } else {
  95. _enumCurrent = *i;
  96. }
  97. _enumRequest = sendRequest(MTP::configDcId(_enumCurrent));
  98. _enumDCTimer.callOnce(kEnumerateDcTimeout);
  99. refreshSpecialLoader();
  100. }
  101. void ConfigLoader::refreshSpecialLoader() {
  102. if (_proxyEnabled || _instance->isKeysDestroyer()) {
  103. _specialLoader.reset();
  104. return;
  105. }
  106. if (!_specialLoader
  107. || (!_specialEnumRequest && _specialEndpoints.empty())) {
  108. createSpecialLoader();
  109. }
  110. }
  111. void ConfigLoader::setPhone(const QString &phone) {
  112. if (_phone != phone) {
  113. _phone = phone;
  114. if (_specialLoader) {
  115. createSpecialLoader();
  116. }
  117. }
  118. }
  119. void ConfigLoader::createSpecialLoader() {
  120. const auto testMode = _instance->isTestMode();
  121. _triedSpecialEndpoints.clear();
  122. _specialLoader = std::make_unique<SpecialConfigRequest>([=](
  123. DcId dcId,
  124. const std::string &ip,
  125. int port,
  126. bytes::const_span secret) {
  127. if (ip.empty()) {
  128. _specialLoader = nullptr;
  129. } else {
  130. addSpecialEndpoint(dcId, ip, port, secret);
  131. }
  132. }, testMode, _instance->configValues().txtDomainString, _phone);
  133. }
  134. void ConfigLoader::addSpecialEndpoint(
  135. DcId dcId,
  136. const std::string &ip,
  137. int port,
  138. bytes::const_span secret) {
  139. const auto endpoint = SpecialEndpoint {
  140. dcId,
  141. ip,
  142. port,
  143. bytes::make_vector(secret)
  144. };
  145. if (base::contains(_specialEndpoints, endpoint)
  146. || base::contains(_triedSpecialEndpoints, endpoint)) {
  147. return;
  148. }
  149. DEBUG_LOG(("MTP Info: Special endpoint received, '%1:%2'").arg(ip.c_str()).arg(port));
  150. _specialEndpoints.push_back(endpoint);
  151. if (!_specialEnumTimer.isActive()) {
  152. _specialEnumTimer.callOnce(1);
  153. }
  154. }
  155. void ConfigLoader::sendSpecialRequest() {
  156. terminateSpecialRequest();
  157. if (_proxyEnabled) {
  158. _specialLoader.reset();
  159. return;
  160. }
  161. if (_specialEndpoints.empty()) {
  162. refreshSpecialLoader();
  163. return;
  164. }
  165. const auto weak = base::make_weak(this);
  166. const auto index = base::RandomValue<uint32>() % _specialEndpoints.size();
  167. const auto endpoint = _specialEndpoints.begin() + index;
  168. _specialEnumCurrent = specialToRealDcId(endpoint->dcId);
  169. using Flag = MTPDdcOption::Flag;
  170. const auto flags = Flag::f_tcpo_only
  171. | (endpoint->secret.empty() ? Flag(0) : Flag::f_secret);
  172. _instance->dcOptions().constructAddOne(
  173. _specialEnumCurrent,
  174. flags,
  175. endpoint->ip,
  176. endpoint->port,
  177. endpoint->secret);
  178. _specialEnumRequest = _instance->send(
  179. MTPhelp_GetConfig(),
  180. [weak](const Response &response) {
  181. auto result = MTPConfig();
  182. auto from = response.reply.constData();
  183. if (!result.read(from, from + response.reply.size())) {
  184. return false;
  185. }
  186. if (const auto strong = weak.get()) {
  187. strong->specialConfigLoaded(result);
  188. }
  189. return true;
  190. },
  191. base::duplicate(_failHandler),
  192. _specialEnumCurrent);
  193. _triedSpecialEndpoints.push_back(*endpoint);
  194. _specialEndpoints.erase(endpoint);
  195. _specialEnumTimer.callOnce(kSpecialRequestTimeoutMs);
  196. }
  197. void ConfigLoader::specialConfigLoaded(const MTPConfig &result) {
  198. Expects(result.type() == mtpc_config);
  199. const auto &data = result.c_config();
  200. if (data.vdc_options().v.empty()) {
  201. LOG(("MTP Error: config with empty dc_options received!"));
  202. return;
  203. }
  204. // We use special config only for dc options.
  205. // For everything else we wait for normal config from main dc.
  206. _instance->dcOptions().setFromList(data.vdc_options());
  207. }
  208. void ConfigLoader::setProxyEnabled(bool value) {
  209. _proxyEnabled = value;
  210. }
  211. } // namespace details
  212. } // namespace MTP