connection_tcp.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  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/connection_tcp.h"
  8. #include "mtproto/details/mtproto_abstract_socket.h"
  9. #include "base/bytes.h"
  10. #include "base/openssl_help.h"
  11. #include "base/random.h"
  12. #include "base/qthelp_url.h"
  13. namespace MTP {
  14. namespace details {
  15. namespace {
  16. constexpr auto kPacketSizeMax = int(0x01000000 * sizeof(mtpPrime));
  17. constexpr auto kFullConnectionTimeout = 8 * crl::time(1000);
  18. constexpr auto kSmallBufferSize = 256 * 1024;
  19. constexpr auto kMinPacketBuffer = 256;
  20. constexpr auto kConnectionStartPrefixSize = 64;
  21. } // namespace
  22. class TcpConnection::Protocol {
  23. public:
  24. static std::unique_ptr<Protocol> Create(bytes::const_span secret);
  25. virtual uint32 id() const = 0;
  26. virtual bool supportsArbitraryLength() const = 0;
  27. virtual void prepareKey(bytes::span key, bytes::const_span source) = 0;
  28. virtual bytes::span finalizePacket(mtpBuffer &buffer) = 0;
  29. static constexpr auto kUnknownSize = -1;
  30. static constexpr auto kInvalidSize = -2;
  31. virtual int readPacketLength(bytes::const_span bytes) const = 0;
  32. virtual bytes::const_span readPacket(bytes::const_span bytes) const = 0;
  33. virtual QString debugPostfix() const = 0;
  34. virtual ~Protocol() = default;
  35. private:
  36. class Version0;
  37. class Version1;
  38. class VersionD;
  39. };
  40. class TcpConnection::Protocol::Version0 : public Protocol {
  41. public:
  42. uint32 id() const override;
  43. bool supportsArbitraryLength() const override;
  44. void prepareKey(bytes::span key, bytes::const_span source) override;
  45. bytes::span finalizePacket(mtpBuffer &buffer) override;
  46. int readPacketLength(bytes::const_span bytes) const override;
  47. bytes::const_span readPacket(bytes::const_span bytes) const override;
  48. QString debugPostfix() const override;
  49. };
  50. uint32 TcpConnection::Protocol::Version0::id() const {
  51. return 0xEFEFEFEFU;
  52. }
  53. bool TcpConnection::Protocol::Version0::supportsArbitraryLength() const {
  54. return false;
  55. }
  56. void TcpConnection::Protocol::Version0::prepareKey(
  57. bytes::span key,
  58. bytes::const_span source) {
  59. bytes::copy(key, source);
  60. }
  61. bytes::span TcpConnection::Protocol::Version0::finalizePacket(
  62. mtpBuffer &buffer) {
  63. Expects(buffer.size() > 2 && buffer.size() < 0x1000003U);
  64. const auto intsSize = uint32(buffer.size() - 2);
  65. const auto bytesSize = intsSize * sizeof(mtpPrime);
  66. const auto data = reinterpret_cast<uchar*>(&buffer[0]);
  67. const auto added = [&] {
  68. if (intsSize < 0x7F) {
  69. data[7] = uchar(intsSize);
  70. return 1;
  71. }
  72. data[4] = uchar(0x7F);
  73. data[5] = uchar(intsSize & 0xFF);
  74. data[6] = uchar((intsSize >> 8) & 0xFF);
  75. data[7] = uchar((intsSize >> 16) & 0xFF);
  76. return 4;
  77. }();
  78. return bytes::make_span(buffer).subspan(8 - added, added + bytesSize);
  79. }
  80. int TcpConnection::Protocol::Version0::readPacketLength(
  81. bytes::const_span bytes) const {
  82. if (bytes.empty()) {
  83. return kUnknownSize;
  84. }
  85. const auto first = static_cast<char>(bytes[0]);
  86. if (first == 0x7F) {
  87. if (bytes.size() < 4) {
  88. return kUnknownSize;
  89. }
  90. const auto ints = static_cast<uint32>(bytes[1])
  91. | (static_cast<uint32>(bytes[2]) << 8)
  92. | (static_cast<uint32>(bytes[3]) << 16);
  93. return (ints >= 0x7F) ? (int(ints << 2) + 4) : kInvalidSize;
  94. } else if (first > 0 && first < 0x7F) {
  95. const auto ints = uint32(first);
  96. return int(ints << 2) + 1;
  97. }
  98. return kInvalidSize;
  99. }
  100. bytes::const_span TcpConnection::Protocol::Version0::readPacket(
  101. bytes::const_span bytes) const {
  102. const auto size = readPacketLength(bytes);
  103. Assert(size != kUnknownSize
  104. && size != kInvalidSize
  105. && size <= bytes.size());
  106. const auto sizeLength = (static_cast<char>(bytes[0]) == 0x7F) ? 4 : 1;
  107. return bytes.subspan(sizeLength, size - sizeLength);
  108. }
  109. QString TcpConnection::Protocol::Version0::debugPostfix() const {
  110. return QString();
  111. }
  112. class TcpConnection::Protocol::Version1 : public Version0 {
  113. public:
  114. explicit Version1(bytes::vector &&secret);
  115. void prepareKey(bytes::span key, bytes::const_span source) override;
  116. QString debugPostfix() const override;
  117. private:
  118. bytes::vector _secret;
  119. };
  120. TcpConnection::Protocol::Version1::Version1(bytes::vector &&secret)
  121. : _secret(std::move(secret)) {
  122. }
  123. void TcpConnection::Protocol::Version1::prepareKey(
  124. bytes::span key,
  125. bytes::const_span source) {
  126. const auto payload = bytes::concatenate(source, _secret);
  127. bytes::copy(key, openssl::Sha256(payload));
  128. }
  129. QString TcpConnection::Protocol::Version1::debugPostfix() const {
  130. return u"_obf"_q;
  131. }
  132. class TcpConnection::Protocol::VersionD : public Version1 {
  133. public:
  134. using Version1::Version1;
  135. uint32 id() const override;
  136. bool supportsArbitraryLength() const override;
  137. bytes::span finalizePacket(mtpBuffer &buffer) override;
  138. int readPacketLength(bytes::const_span bytes) const override;
  139. bytes::const_span readPacket(bytes::const_span bytes) const override;
  140. QString debugPostfix() const override;
  141. };
  142. uint32 TcpConnection::Protocol::VersionD::id() const {
  143. return 0xDDDDDDDDU;
  144. }
  145. bool TcpConnection::Protocol::VersionD::supportsArbitraryLength() const {
  146. return true;
  147. }
  148. bytes::span TcpConnection::Protocol::VersionD::finalizePacket(
  149. mtpBuffer &buffer) {
  150. Expects(buffer.size() > 2 && buffer.size() < 0x1000003U);
  151. const auto intsSize = uint32(buffer.size() - 2);
  152. const auto padding = base::RandomValue<uint32>() & 0x0F;
  153. const auto bytesSize = intsSize * sizeof(mtpPrime) + padding;
  154. buffer[1] = bytesSize;
  155. for (auto added = 0; added < padding; added += 4) {
  156. buffer.push_back(base::RandomValue<mtpPrime>());
  157. }
  158. return bytes::make_span(buffer).subspan(4, 4 + bytesSize);
  159. }
  160. int TcpConnection::Protocol::VersionD::readPacketLength(
  161. bytes::const_span bytes) const {
  162. if (bytes.size() < 4) {
  163. return kUnknownSize;
  164. }
  165. const auto value = *reinterpret_cast<const uint32*>(bytes.data()) + 4;
  166. return (value >= 8 && value < kPacketSizeMax)
  167. ? int(value)
  168. : kInvalidSize;
  169. }
  170. bytes::const_span TcpConnection::Protocol::VersionD::readPacket(
  171. bytes::const_span bytes) const {
  172. const auto size = readPacketLength(bytes);
  173. Assert(size != kUnknownSize
  174. && size != kInvalidSize
  175. && size <= bytes.size());
  176. const auto sizeLength = 4;
  177. return bytes.subspan(sizeLength, size - sizeLength);
  178. }
  179. QString TcpConnection::Protocol::VersionD::debugPostfix() const {
  180. return u"_dd"_q;
  181. }
  182. auto TcpConnection::Protocol::Create(bytes::const_span secret)
  183. -> std::unique_ptr<Protocol> {
  184. // See also DcOptions::ValidateSecret.
  185. if ((secret.size() >= 21 && secret[0] == bytes::type(0xEE))
  186. || (secret.size() == 17 && secret[0] == bytes::type(0xDD))) {
  187. return std::make_unique<VersionD>(
  188. bytes::make_vector(secret.subspan(1, 16)));
  189. } else if (secret.size() == 16) {
  190. return std::make_unique<Version1>(bytes::make_vector(secret));
  191. } else if (secret.empty()) {
  192. return std::make_unique<Version0>();
  193. }
  194. Unexpected("Secret bytes in TcpConnection::Protocol::Create.");
  195. }
  196. TcpConnection::TcpConnection(
  197. not_null<Instance*> instance,
  198. QThread *thread,
  199. const ProxyData &proxy)
  200. : AbstractConnection(thread, proxy)
  201. , _instance(instance)
  202. , _checkNonce(base::RandomValue<MTPint128>()) {
  203. }
  204. ConnectionPointer TcpConnection::clone(const ProxyData &proxy) {
  205. return ConnectionPointer::New<TcpConnection>(_instance, thread(), proxy);
  206. }
  207. void TcpConnection::ensureAvailableInBuffer(int amount) {
  208. auto &buffer = _usingLargeBuffer ? _largeBuffer : _smallBuffer;
  209. const auto full = bytes::make_span(buffer).subspan(
  210. _offsetBytes);
  211. if (full.size() >= amount) {
  212. return;
  213. }
  214. const auto read = full.subspan(0, _readBytes);
  215. if (amount <= _smallBuffer.size()) {
  216. if (_usingLargeBuffer) {
  217. bytes::copy(_smallBuffer, read);
  218. _usingLargeBuffer = false;
  219. _largeBuffer.clear();
  220. } else {
  221. bytes::move(_smallBuffer, read);
  222. }
  223. } else if (amount <= _largeBuffer.size()) {
  224. Assert(_usingLargeBuffer);
  225. bytes::move(_largeBuffer, read);
  226. } else {
  227. auto enough = bytes::vector(amount);
  228. bytes::copy(enough, read);
  229. _largeBuffer = std::move(enough);
  230. _usingLargeBuffer = true;
  231. }
  232. _offsetBytes = 0;
  233. }
  234. void TcpConnection::socketRead() {
  235. Expects(_leftBytes > 0 || !_usingLargeBuffer);
  236. if (!_socket || !_socket->isConnected()) {
  237. CONNECTION_LOG_ERROR("Socket not connected in socketRead()");
  238. error(kErrorCodeOther);
  239. return;
  240. }
  241. if (_smallBuffer.empty()) {
  242. _smallBuffer.resize(kSmallBufferSize);
  243. }
  244. do {
  245. const auto readLimit = (_leftBytes > 0)
  246. ? _leftBytes
  247. : (kSmallBufferSize - _offsetBytes - _readBytes);
  248. Assert(readLimit > 0);
  249. auto &buffer = _usingLargeBuffer ? _largeBuffer : _smallBuffer;
  250. const auto full = bytes::make_span(buffer).subspan(_offsetBytes);
  251. const auto free = full.subspan(_readBytes);
  252. const auto readCount = _socket->read(free.subspan(0, readLimit));
  253. if (readCount > 0) {
  254. const auto read = free.subspan(0, readCount);
  255. aesCtrEncrypt(read, _receiveKey, &_receiveState);
  256. CONNECTION_LOG_INFO(u"Read %1 bytes"_q.arg(readCount));
  257. _readBytes += readCount;
  258. if (_leftBytes > 0) {
  259. Assert(readCount <= _leftBytes);
  260. _leftBytes -= readCount;
  261. if (!_leftBytes) {
  262. socketPacket(full.subspan(0, _readBytes));
  263. if (!_socket || !_socket->isConnected()) {
  264. return;
  265. }
  266. _usingLargeBuffer = false;
  267. _largeBuffer.clear();
  268. _offsetBytes = _readBytes = 0;
  269. } else {
  270. CONNECTION_LOG_INFO(
  271. u"Not enough %1 for packet! read %2"_q
  272. .arg(_leftBytes)
  273. .arg(_readBytes));
  274. receivedSome();
  275. }
  276. } else {
  277. auto available = full.subspan(0, _readBytes);
  278. while (_readBytes > 0) {
  279. const auto packetSize = _protocol->readPacketLength(
  280. available);
  281. if (packetSize == Protocol::kUnknownSize) {
  282. // Not enough bytes yet.
  283. break;
  284. } else if (packetSize <= 0) {
  285. CONNECTION_LOG_ERROR(
  286. u"Bad packet size in 4 bytes: %1"_q
  287. .arg(packetSize));
  288. error(kErrorCodeOther);
  289. return;
  290. } else if (available.size() >= packetSize) {
  291. socketPacket(available.subspan(0, packetSize));
  292. if (!_socket || !_socket->isConnected()) {
  293. return;
  294. }
  295. available = available.subspan(packetSize);
  296. _offsetBytes += packetSize;
  297. _readBytes -= packetSize;
  298. // If we have too little space left in the buffer.
  299. ensureAvailableInBuffer(kMinPacketBuffer);
  300. } else {
  301. _leftBytes = packetSize - available.size();
  302. // If the next packet won't fit in the buffer.
  303. ensureAvailableInBuffer(packetSize);
  304. CONNECTION_LOG_INFO(u"Not enough %1 for packet! "
  305. "full size %2 read %3"_q
  306. .arg(_leftBytes)
  307. .arg(packetSize)
  308. .arg(available.size()));
  309. receivedSome();
  310. break;
  311. }
  312. }
  313. }
  314. } else if (readCount < 0) {
  315. CONNECTION_LOG_ERROR(u"Socket read return %1."_q.arg(readCount));
  316. error(kErrorCodeOther);
  317. return;
  318. } else {
  319. CONNECTION_LOG_INFO(
  320. "No bytes read, but bytes available was true...");
  321. break;
  322. }
  323. } while (_socket
  324. && _socket->isConnected()
  325. && _socket->hasBytesAvailable());
  326. }
  327. mtpBuffer TcpConnection::parsePacket(bytes::const_span bytes) {
  328. const auto packet = _protocol->readPacket(bytes);
  329. CONNECTION_LOG_INFO(u"Packet received, size = %1."_q.arg(packet.size()));
  330. const auto ints = gsl::make_span(
  331. reinterpret_cast<const mtpPrime*>(packet.data()),
  332. packet.size() / sizeof(mtpPrime));
  333. Assert(!ints.empty());
  334. if (ints.size() < 3) {
  335. // nop or error or new quickack, latter is not yet supported.
  336. if (ints[0] != 0) {
  337. CONNECTION_LOG_ERROR(u"Error packet received, code = %1"_q
  338. .arg(ints[0]));
  339. }
  340. return mtpBuffer(1, ints[0]);
  341. }
  342. auto result = mtpBuffer(ints.size());
  343. memcpy(result.data(), ints.data(), ints.size() * sizeof(mtpPrime));
  344. return result;
  345. }
  346. void TcpConnection::socketConnected() {
  347. Expects(_status == Status::Waiting);
  348. auto buffer = preparePQFake(_checkNonce);
  349. CONNECTION_LOG_INFO("Sending fake req_pq.");
  350. _pingTime = crl::now();
  351. sendData(std::move(buffer));
  352. }
  353. void TcpConnection::socketDisconnected() {
  354. if (_status == Status::Waiting || _status == Status::Ready) {
  355. disconnected();
  356. }
  357. }
  358. void TcpConnection::sendData(mtpBuffer &&buffer) {
  359. Expects(buffer.size() > 2);
  360. if (!_socket) {
  361. return;
  362. }
  363. char connectionStartPrefixBytes[kConnectionStartPrefixSize];
  364. const auto connectionStartPrefix = prepareConnectionStartPrefix(
  365. bytes::make_span(connectionStartPrefixBytes));
  366. // buffer: 2 available int-s + data + available int.
  367. const auto bytes = _protocol->finalizePacket(buffer);
  368. CONNECTION_LOG_INFO(u"TCP Info: write packet %1 bytes."_q
  369. .arg(bytes.size()));
  370. aesCtrEncrypt(bytes, _sendKey, &_sendState);
  371. _socket->write(connectionStartPrefix, bytes);
  372. }
  373. bytes::const_span TcpConnection::prepareConnectionStartPrefix(
  374. bytes::span buffer) {
  375. Expects(_socket != nullptr);
  376. Expects(_protocol != nullptr);
  377. if (_connectionStarted) {
  378. return {};
  379. }
  380. _connectionStarted = true;
  381. // prepare random part
  382. char nonceBytes[64];
  383. const auto nonce = bytes::make_span(nonceBytes);
  384. do {
  385. bytes::set_random(nonce);
  386. } while (!_socket->isGoodStartNonce(nonce));
  387. // prepare encryption key/iv
  388. _protocol->prepareKey(
  389. bytes::make_span(_sendKey),
  390. nonce.subspan(8, CTRState::KeySize));
  391. bytes::copy(
  392. bytes::make_span(_sendState.ivec),
  393. nonce.subspan(8 + CTRState::KeySize, CTRState::IvecSize));
  394. // prepare decryption key/iv
  395. auto reversedBytes = bytes::vector(48);
  396. const auto reversed = bytes::make_span(reversedBytes);
  397. bytes::copy(reversed, nonce.subspan(8, reversed.size()));
  398. std::reverse(reversed.begin(), reversed.end());
  399. _protocol->prepareKey(
  400. bytes::make_span(_receiveKey),
  401. reversed.subspan(0, CTRState::KeySize));
  402. bytes::copy(
  403. bytes::make_span(_receiveState.ivec),
  404. reversed.subspan(CTRState::KeySize, CTRState::IvecSize));
  405. // write protocol and dc ids
  406. const auto protocol = reinterpret_cast<uint32*>(nonce.data() + 56);
  407. *protocol = _protocol->id();
  408. const auto dcId = reinterpret_cast<int16*>(nonce.data() + 60);
  409. *dcId = _protocolDcId;
  410. bytes::copy(buffer, nonce.subspan(0, 56));
  411. aesCtrEncrypt(nonce, _sendKey, &_sendState);
  412. bytes::copy(buffer.subspan(56), nonce.subspan(56));
  413. return buffer;
  414. }
  415. void TcpConnection::disconnectFromServer() {
  416. if (_status == Status::Finished) {
  417. return;
  418. }
  419. _status = Status::Finished;
  420. _connectedLifetime.destroy();
  421. _lifetime.destroy();
  422. _socket = nullptr;
  423. }
  424. void TcpConnection::connectToServer(
  425. const QString &address,
  426. int port,
  427. const bytes::vector &protocolSecret,
  428. int16 protocolDcId,
  429. bool protocolForFiles) {
  430. Expects(_address.isEmpty());
  431. Expects(_port == 0);
  432. Expects(_protocol == nullptr);
  433. Expects(_protocolDcId == 0);
  434. const auto secret = (_proxy.type == ProxyData::Type::Mtproto)
  435. ? _proxy.secretFromMtprotoPassword()
  436. : protocolSecret;
  437. if (_proxy.type == ProxyData::Type::Mtproto) {
  438. _address = _proxy.host;
  439. _port = _proxy.port;
  440. _protocol = Protocol::Create(secret);
  441. } else {
  442. _address = address;
  443. _port = port;
  444. _protocol = Protocol::Create(secret);
  445. }
  446. _socket = AbstractSocket::Create(
  447. thread(),
  448. secret,
  449. ToNetworkProxy(_proxy),
  450. protocolForFiles);
  451. _protocolDcId = protocolDcId;
  452. const auto postfix = _socket->debugPostfix();
  453. _debugId = u"%1(dc:%2,%3%4:%5%6)"_q
  454. .arg(_debugId.toInt())
  455. .arg(
  456. ProtocolDcDebugId(_protocolDcId),
  457. (_proxy.type == ProxyData::Type::Mtproto) ? "mtproxy " : "",
  458. _address)
  459. .arg(_port)
  460. .arg(postfix.isEmpty() ? _protocol->debugPostfix() : postfix);
  461. _socket->setDebugId(_debugId);
  462. CONNECTION_LOG_INFO("Connecting...");
  463. _socket->connected(
  464. ) | rpl::start_with_next([=] {
  465. socketConnected();
  466. }, _connectedLifetime);
  467. _socket->disconnected(
  468. ) | rpl::start_with_next([=] {
  469. socketDisconnected();
  470. }, _lifetime);
  471. _socket->readyRead(
  472. ) | rpl::start_with_next([=] {
  473. socketRead();
  474. }, _lifetime);
  475. _socket->error(
  476. ) | rpl::start_with_next([=] {
  477. socketError();
  478. }, _lifetime);
  479. _socket->syncTimeRequests(
  480. ) | rpl::start_with_next([=] {
  481. syncTimeRequest();
  482. }, _lifetime);
  483. _socket->connectToHost(_address, _port);
  484. }
  485. crl::time TcpConnection::pingTime() const {
  486. return isConnected() ? _pingTime : crl::time(0);
  487. }
  488. crl::time TcpConnection::fullConnectTimeout() const {
  489. return kFullConnectionTimeout;
  490. }
  491. void TcpConnection::socketPacket(bytes::const_span bytes) {
  492. Expects(_socket != nullptr);
  493. // old quickack?..
  494. const auto data = parsePacket(bytes);
  495. if (data.size() == 1) {
  496. if (data[0] != 0) {
  497. error(data[0]);
  498. } else {
  499. // nop
  500. }
  501. //} else if (data.size() == 2) {
  502. // new quickack?..
  503. } else if (_status == Status::Ready) {
  504. _receivedQueue.push_back(data);
  505. receivedData();
  506. } else if (_status == Status::Waiting) {
  507. if (const auto res_pq = readPQFakeReply(data)) {
  508. const auto &data = res_pq->c_resPQ();
  509. if (data.vnonce() == _checkNonce) {
  510. CONNECTION_LOG_INFO("Valid pq response by TCP.");
  511. _status = Status::Ready;
  512. _connectedLifetime.destroy();
  513. _pingTime = (crl::now() - _pingTime);
  514. connected();
  515. } else {
  516. CONNECTION_LOG_ERROR(
  517. "Wrong nonce received in TCP fake pq-responce");
  518. error(kErrorCodeOther);
  519. }
  520. } else {
  521. CONNECTION_LOG_ERROR("Could not parse TCP fake pq-responce");
  522. error(kErrorCodeOther);
  523. }
  524. }
  525. }
  526. void TcpConnection::timedOut() {
  527. if (_socket) {
  528. _socket->timedOut();
  529. }
  530. }
  531. bool TcpConnection::isConnected() const {
  532. return (_status == Status::Ready);
  533. }
  534. int32 TcpConnection::debugState() const {
  535. return _socket ? _socket->debugState() : -1;
  536. }
  537. QString TcpConnection::transport() const {
  538. if (!isConnected()) {
  539. return QString();
  540. }
  541. auto result = u"TCP"_q;
  542. if (qthelp::is_ipv6(_address)) {
  543. result += u"/IPv6"_q;
  544. }
  545. return result;
  546. }
  547. QString TcpConnection::tag() const {
  548. auto result = u"TCP"_q;
  549. if (qthelp::is_ipv6(_address)) {
  550. result += u"/IPv6"_q;
  551. } else {
  552. result += u"/IPv4"_q;
  553. }
  554. return result;
  555. }
  556. void TcpConnection::socketError() {
  557. if (!_socket) {
  558. return;
  559. }
  560. error(kErrorCodeOther);
  561. }
  562. TcpConnection::~TcpConnection() = default;
  563. } // namespace details
  564. } // namespace MTP