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/connection_resolving.h"
9
10 #include "mtproto/mtp_instance.h"
11
12 namespace MTP {
13 namespace details {
14 namespace {
15
16 constexpr auto kOneConnectionTimeout = 4000;
17
18 } // namespace
19
ResolvingConnection(not_null<Instance * > instance,QThread * thread,const ProxyData & proxy,ConnectionPointer && child)20 ResolvingConnection::ResolvingConnection(
21 not_null<Instance*> instance,
22 QThread *thread,
23 const ProxyData &proxy,
24 ConnectionPointer &&child)
25 : AbstractConnection(thread, proxy)
26 , _instance(instance)
27 , _timeoutTimer([=] { handleError(kErrorCodeOther); }) {
28 setChild(std::move(child));
29 if (proxy.resolvedExpireAt < crl::now()) {
30 const auto host = proxy.host;
31 connect(
32 instance,
33 &Instance::proxyDomainResolved,
34 this,
35 &ResolvingConnection::domainResolved,
36 Qt::QueuedConnection);
__anon9807b31a0302null37 InvokeQueued(instance, [=] {
38 instance->resolveProxyDomain(host);
39 });
40 }
41 if (!proxy.resolvedIPs.empty()) {
42 refreshChild();
43 }
44 }
45
clone(const ProxyData & proxy)46 ConnectionPointer ResolvingConnection::clone(const ProxyData &proxy) {
47 Unexpected("ResolvingConnection::clone call.");
48 }
49
setChild(ConnectionPointer && child)50 void ResolvingConnection::setChild(ConnectionPointer &&child) {
51 _child = std::move(child);
52 connect(
53 _child,
54 &AbstractConnection::receivedData,
55 this,
56 &ResolvingConnection::handleReceivedData);
57 connect(
58 _child,
59 &AbstractConnection::receivedSome,
60 this,
61 &ResolvingConnection::receivedSome);
62 connect(
63 _child,
64 &AbstractConnection::error,
65 this,
66 &ResolvingConnection::handleError);
67 connect(_child,
68 &AbstractConnection::connected,
69 this,
70 &ResolvingConnection::handleConnected);
71 connect(_child,
72 &AbstractConnection::disconnected,
73 this,
74 &ResolvingConnection::handleDisconnected);
75 DEBUG_LOG(("Resolving Info: dc:%1 proxy '%2' got new child '%3'").arg(
76 QString::number(_protocolDcId),
77 _proxy.host + ':' + QString::number(_proxy.port),
78 (_ipIndex >= 0 && _ipIndex < _proxy.resolvedIPs.size())
79 ? _proxy.resolvedIPs[_ipIndex]
80 : _proxy.host));
81 if (_protocolDcId) {
82 _child->connectToServer(
83 _address,
84 _port,
85 _protocolSecret,
86 _protocolDcId,
87 _protocolForFiles);
88 }
89 }
90
domainResolved(const QString & host,const QStringList & ips,qint64 expireAt)91 void ResolvingConnection::domainResolved(
92 const QString &host,
93 const QStringList &ips,
94 qint64 expireAt) {
95 if (host != _proxy.host || !_child) {
96 return;
97 }
98 _proxy.resolvedExpireAt = expireAt;
99
100 auto index = 0;
101 for (const auto &ip : ips) {
102 if (index >= _proxy.resolvedIPs.size()) {
103 _proxy.resolvedIPs.push_back(ip);
104 } else if (_proxy.resolvedIPs[index] != ip) {
105 _proxy.resolvedIPs[index] = ip;
106 if (_ipIndex >= index) {
107 _ipIndex = index - 1;
108 refreshChild();
109 }
110 }
111 ++index;
112 }
113 if (index < _proxy.resolvedIPs.size()) {
114 _proxy.resolvedIPs.resize(index);
115 if (_ipIndex >= index) {
116 emitError(kErrorCodeOther);
117 }
118 }
119 if (_ipIndex < 0) {
120 refreshChild();
121 }
122 }
123
refreshChild()124 bool ResolvingConnection::refreshChild() {
125 if (!_child) {
126 return true;
127 } else if (++_ipIndex >= _proxy.resolvedIPs.size()) {
128 return false;
129 }
130 setChild(_child->clone(ToDirectIpProxy(_proxy, _ipIndex)));
131 _timeoutTimer.callOnce(kOneConnectionTimeout);
132 return true;
133 }
134
emitError(int errorCode)135 void ResolvingConnection::emitError(int errorCode) {
136 _ipIndex = -1;
137 _child = nullptr;
138 error(errorCode);
139 }
140
handleError(int errorCode)141 void ResolvingConnection::handleError(int errorCode) {
142 if (_connected) {
143 emitError(errorCode);
144 } else if (!_proxy.resolvedIPs.empty()) {
145 if (!refreshChild()) {
146 emitError(errorCode);
147 }
148 } else {
149 // Wait for the domain to be resolved.
150 }
151 }
152
handleDisconnected()153 void ResolvingConnection::handleDisconnected() {
154 if (_connected) {
155 disconnected();
156 } else {
157 handleError(kErrorCodeOther);
158 }
159 }
160
handleReceivedData()161 void ResolvingConnection::handleReceivedData() {
162 auto &my = received();
163 auto &his = _child->received();
164 for (auto &item : his) {
165 my.push_back(std::move(item));
166 }
167 his.clear();
168 receivedData();
169 }
170
handleConnected()171 void ResolvingConnection::handleConnected() {
172 _connected = true;
173 _timeoutTimer.cancel();
174 if (_ipIndex >= 0) {
175 const auto host = _proxy.host;
176 const auto good = _proxy.resolvedIPs[_ipIndex];
177 const auto instance = _instance;
178 InvokeQueued(_instance, [=] {
179 instance->setGoodProxyDomain(host, good);
180 });
181 }
182 connected();
183 }
184
pingTime() const185 crl::time ResolvingConnection::pingTime() const {
186 Expects(_child != nullptr);
187
188 return _child->pingTime();
189 }
190
fullConnectTimeout() const191 crl::time ResolvingConnection::fullConnectTimeout() const {
192 return kOneConnectionTimeout * qMax(int(_proxy.resolvedIPs.size()), 1);
193 }
194
sendData(mtpBuffer && buffer)195 void ResolvingConnection::sendData(mtpBuffer &&buffer) {
196 Expects(_child != nullptr);
197
198 _child->sendData(std::move(buffer));
199 }
200
disconnectFromServer()201 void ResolvingConnection::disconnectFromServer() {
202 _address = QString();
203 _port = 0;
204 _protocolSecret = bytes::vector();
205 _protocolDcId = 0;
206 if (!_child) {
207 return;
208 }
209 _child->disconnectFromServer();
210 }
211
connectToServer(const QString & address,int port,const bytes::vector & protocolSecret,int16 protocolDcId,bool protocolForFiles)212 void ResolvingConnection::connectToServer(
213 const QString &address,
214 int port,
215 const bytes::vector &protocolSecret,
216 int16 protocolDcId,
217 bool protocolForFiles) {
218 if (!_child) {
219 InvokeQueued(this, [=] { emitError(kErrorCodeOther); });
220 return;
221 }
222 _address = address;
223 _port = port;
224 _protocolSecret = protocolSecret;
225 _protocolDcId = protocolDcId;
226 _protocolForFiles = protocolForFiles;
227 DEBUG_LOG(("Resolving Info: dc:%1 proxy '%2' connects a child '%3'").arg(
228 QString::number(_protocolDcId),
229 _proxy.host +':' + QString::number(_proxy.port),
230 (_ipIndex >= 0 && _ipIndex < _proxy.resolvedIPs.size())
231 ? _proxy.resolvedIPs[_ipIndex]
232 : _proxy.host));
233 return _child->connectToServer(
234 address,
235 port,
236 protocolSecret,
237 protocolDcId,
238 protocolForFiles);
239 }
240
isConnected() const241 bool ResolvingConnection::isConnected() const {
242 return _child ? _child->isConnected() : false;
243 }
244
debugState() const245 int32 ResolvingConnection::debugState() const {
246 return _child ? _child->debugState() : -1;
247 }
248
transport() const249 QString ResolvingConnection::transport() const {
250 return _child ? _child->transport() : QString();
251 }
252
tag() const253 QString ResolvingConnection::tag() const {
254 return _child ? _child->tag() : QString();
255 }
256
257 } // namespace details
258 } // namespace MTP
259