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