From 9f045500d12ae77c8b4ff1769c01ba5bf3de2268 Mon Sep 17 00:00:00 2001 From: FranciscoPombal Date: Fri, 21 Feb 2020 00:37:28 +0000 Subject: [PATCH 1/4] Fix IPv6 endianness --- src/base/bittorrent/tracker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/bittorrent/tracker.cpp b/src/base/bittorrent/tracker.cpp index 5f5d15bc4..058a6cfc2 100644 --- a/src/base/bittorrent/tracker.cpp +++ b/src/base/bittorrent/tracker.cpp @@ -107,8 +107,8 @@ namespace case QAbstractSocket::IPv6Protocol: { const Q_IPV6ADDR ipv6 = addr.toIPv6Address(); QByteArray ret; - for (int i = (sizeof(ipv6.c) - 1); i >= 0; --i) - ret.append(static_cast(ipv6.c[i])); + for (const quint8 i : ipv6.c) + ret.append(i); return ret; } From abc69dfd205b4941157100198f0e835a738a5033 Mon Sep 17 00:00:00 2001 From: FranciscoPombal Date: Fri, 21 Feb 2020 00:43:02 +0000 Subject: [PATCH 2/4] Fix BEP-23 support The embedded tracker was returning a non-compliant response. The peer lists should be packed strings instead of lists. Also clarify level of compliance with BEP-7. --- src/base/bittorrent/tracker.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/base/bittorrent/tracker.cpp b/src/base/bittorrent/tracker.cpp index 058a6cfc2..3ee533983 100644 --- a/src/base/bittorrent/tracker.cpp +++ b/src/base/bittorrent/tracker.cpp @@ -392,26 +392,26 @@ void Tracker::prepareAnnounceResponse(const TrackerAnnounceRequest &announceReq) }; // peer list - // [BEP-7] IPv6 Tracker Extension (partial support) + // [BEP-7] IPv6 Tracker Extension (partial support - only the part that concerns BEP-23) // [BEP-23] Tracker Returns Compact Peer Lists if (announceReq.compact) { - lt::entry::list_type peerList; - lt::entry::list_type peer6List; + lt::entry::string_type peers; + lt::entry::string_type peers6; int counter = 0; for (const Peer &peer : asConst(torrentStats.peers)) { if (counter++ >= announceReq.numwant) break; - if (peer.endpoint.size() == 6) // IPv4 - peerList.emplace_back(peer.endpoint); - else if (peer.endpoint.size() == 18) // IPv6 - peer6List.emplace_back(peer.endpoint); + if (peer.endpoint.size() == 6) // IPv4 + port + peers.append(peer.endpoint); + else if (peer.endpoint.size() == 18) // IPv6 + port + peers6.append(peer.endpoint); } - replyDict[ANNOUNCE_RESPONSE_PEERS] = peerList; // required, even it's empty - if (!peer6List.empty()) - replyDict[ANNOUNCE_RESPONSE_PEERS6] = peer6List; + replyDict[ANNOUNCE_RESPONSE_PEERS] = peers; // required, even it's empty + if (!peers6.empty()) + replyDict[ANNOUNCE_RESPONSE_PEERS6] = peers6; } else { lt::entry::list_type peerList; From af6f63f82cff6756e6948c2804c80fdd6c778ab1 Mon Sep 17 00:00:00 2001 From: FranciscoPombal Date: Fri, 21 Feb 2020 00:48:19 +0000 Subject: [PATCH 3/4] Don't use IPv4-mapped IPv6 Always register peers with either IPv4 or IPv6. Fixes potential connection issues. Also clarify related BEP-24 support. --- src/base/bittorrent/tracker.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/base/bittorrent/tracker.cpp b/src/base/bittorrent/tracker.cpp index 3ee533983..8e741de03 100644 --- a/src/base/bittorrent/tracker.cpp +++ b/src/base/bittorrent/tracker.cpp @@ -272,6 +272,12 @@ void Tracker::processAnnounceRequest() announceReq.socketAddress = m_env.clientAddress; announceReq.claimedAddress = queryParams.value(ANNOUNCE_REQUEST_IP); + // Enforce using IPv4 if address is indeed IPv4 or if it is an IPv4-mapped IPv6 address + bool ok = false; + const qint32 decimalIPv4 = announceReq.socketAddress.toIPv4Address(&ok); + if (ok) + announceReq.socketAddress = QHostAddress(decimalIPv4); + // 1. info_hash const auto infoHashIter = queryParams.find(ANNOUNCE_REQUEST_INFO_HASH); if (infoHashIter == queryParams.end()) @@ -387,7 +393,7 @@ void Tracker::prepareAnnounceResponse(const TrackerAnnounceRequest &announceReq) {ANNOUNCE_RESPONSE_COMPLETE, torrentStats.seeders}, {ANNOUNCE_RESPONSE_INCOMPLETE, (torrentStats.peers.size() - torrentStats.seeders)}, - // [BEP-24] Tracker Returns External IP + // [BEP-24] Tracker Returns External IP (partial support - might not work properly for all IPv6 cases) {ANNOUNCE_RESPONSE_EXTERNAL_IP, toBigEndianByteArray(announceReq.socketAddress).toStdString()} }; From 966f891d91d99ad3a0629e908c3b8654864de279 Mon Sep 17 00:00:00 2001 From: FranciscoPombal Date: Mon, 24 Feb 2020 02:01:03 +0000 Subject: [PATCH 4/4] Fix response to event=stopped BEP-3 says all tracker responses to valid announce requests should be bencoded dictionaries with at least the "peers" and "interval" keys; the "peers" key can be empty. Also clarify support for BEP-21. --- src/base/bittorrent/tracker.cpp | 48 ++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/base/bittorrent/tracker.cpp b/src/base/bittorrent/tracker.cpp index 8e741de03..06cc4f8d5 100644 --- a/src/base/bittorrent/tracker.cpp +++ b/src/base/bittorrent/tracker.cpp @@ -349,9 +349,9 @@ void Tracker::processAnnounceRequest() || (announceReq.event == ANNOUNCE_REQUEST_EVENT_COMPLETED) || (announceReq.event == ANNOUNCE_REQUEST_EVENT_STARTED) || (announceReq.event == ANNOUNCE_REQUEST_EVENT_PAUSED)) { - // [BEP-21] Extension for partial seeds (partial support) + // [BEP-21] Extension for partial seeds + // (partial support - we don't support BEP-48 so the part that concerns that is not supported) registerPeer(announceReq); - prepareAnnounceResponse(announceReq); } else if (announceReq.event == ANNOUNCE_REQUEST_EVENT_STOPPED) { unregisterPeer(announceReq); @@ -359,6 +359,8 @@ void Tracker::processAnnounceRequest() else { throw TrackerError("Invalid \"event\" parameter"); } + + prepareAnnounceResponse(announceReq); } void Tracker::registerPeer(const TrackerAnnounceRequest &announceReq) @@ -404,15 +406,17 @@ void Tracker::prepareAnnounceResponse(const TrackerAnnounceRequest &announceReq) lt::entry::string_type peers; lt::entry::string_type peers6; - int counter = 0; - for (const Peer &peer : asConst(torrentStats.peers)) { - if (counter++ >= announceReq.numwant) - break; + if (announceReq.event != ANNOUNCE_REQUEST_EVENT_STOPPED) { + int counter = 0; + for (const Peer &peer : asConst(torrentStats.peers)) { + if (counter++ >= announceReq.numwant) + break; - if (peer.endpoint.size() == 6) // IPv4 + port - peers.append(peer.endpoint); - else if (peer.endpoint.size() == 18) // IPv6 + port - peers6.append(peer.endpoint); + if (peer.endpoint.size() == 6) // IPv4 + port + peers.append(peer.endpoint); + else if (peer.endpoint.size() == 18) // IPv6 + port + peers6.append(peer.endpoint); + } } replyDict[ANNOUNCE_RESPONSE_PEERS] = peers; // required, even it's empty @@ -422,20 +426,22 @@ void Tracker::prepareAnnounceResponse(const TrackerAnnounceRequest &announceReq) else { lt::entry::list_type peerList; - int counter = 0; - for (const Peer &peer : torrentStats.peers) { - if (counter++ >= announceReq.numwant) - break; + if (announceReq.event != ANNOUNCE_REQUEST_EVENT_STOPPED) { + int counter = 0; + for (const Peer &peer : torrentStats.peers) { + if (counter++ >= announceReq.numwant) + break; - lt::entry::dictionary_type peerDict = { - {ANNOUNCE_RESPONSE_PEERS_IP, peer.address}, - {ANNOUNCE_RESPONSE_PEERS_PORT, peer.port} - }; + lt::entry::dictionary_type peerDict = { + {ANNOUNCE_RESPONSE_PEERS_IP, peer.address}, + {ANNOUNCE_RESPONSE_PEERS_PORT, peer.port} + }; - if (!announceReq.noPeerId) - peerDict[ANNOUNCE_RESPONSE_PEERS_PEER_ID] = peer.peerId.constData(); + if (!announceReq.noPeerId) + peerDict[ANNOUNCE_RESPONSE_PEERS_PEER_ID] = peer.peerId.constData(); - peerList.emplace_back(peerDict); + peerList.emplace_back(peerDict); + } } replyDict[ANNOUNCE_RESPONSE_PEERS] = peerList;