From 13e56ca02d4250b037853953afca493eb7d40bb2 Mon Sep 17 00:00:00 2001 From: Aurelien Desbrieres Date: Fri, 19 Dec 2014 18:50:17 +0100 Subject: BitTorrent library written in C++ with eXtended patches. --- pcr/libtorrent-extended/ex_friend.patch | 461 ++++++++++++++++++++++++++++++++ 1 file changed, 461 insertions(+) create mode 100644 pcr/libtorrent-extended/ex_friend.patch (limited to 'pcr/libtorrent-extended/ex_friend.patch') diff --git a/pcr/libtorrent-extended/ex_friend.patch b/pcr/libtorrent-extended/ex_friend.patch new file mode 100644 index 000000000..e51716c0f --- /dev/null +++ b/pcr/libtorrent-extended/ex_friend.patch @@ -0,0 +1,461 @@ +diff --git a/src/download/choke_manager.cc b/src/download/choke_manager.cc +index 4915a96..3c422c0 100644 +--- a/src/download/choke_manager.cc ++++ b/src/download/choke_manager.cc +@@ -129,8 +129,14 @@ ChokeManager::cycle(uint32_t quota) { + if (m_unchoked.size() > quota) + choke_range(m_unchoked.begin(), m_unchoked.end() - unchoked, m_unchoked.size() - quota); + ++ // In case there were friends who are not to be choked, try again with twice ++ // as many as remain unchoked (friends who will not be choked again plus as ++ // many non-friends). + if (m_unchoked.size() > quota) +- throw internal_error("ChokeManager::cycle() m_unchoked.size() > quota."); ++ choke_range(m_unchoked.begin(), m_unchoked.end() - unchoked, std::min((m_unchoked.size() - quota) * 2, m_unchoked.size())); ++ ++ // If there were friends in the n+1..2n range, we still have too many unchoked here. ++ // FIXME: Change choke_range to take the number of peers to unchoke, instead of a fixed range. + + return m_unchoked.size() - oldSize; + } +@@ -145,10 +151,11 @@ ChokeManager::set_queued(PeerConnectionBase* pc, ChokeManagerNode* base) { + if (base->snubbed()) + return; + +- if (!is_full() && (m_flags & flag_unchoke_all_new || m_slotCanUnchoke()) && +- base->time_last_choke() + rak::timer::from_seconds(10) < cachedTime) { ++ if (pc->peer_info()->is_friend() || ++ (!is_full() && (m_flags & flag_unchoke_all_new || m_slotCanUnchoke()) && ++ base->time_last_choke() + rak::timer::from_seconds(10) < cachedTime)) { + m_unchoked.push_back(value_type(pc, 0)); +- m_slotConnection(pc, false); ++ m_slotConnection(pc, 0); + + m_slotUnchoke(1); + +@@ -169,7 +176,7 @@ ChokeManager::set_not_queued(PeerConnectionBase* pc, ChokeManagerNode* base) { + + if (base->unchoked()) { + choke_manager_erase(&m_unchoked, pc); +- m_slotConnection(pc, true); ++ m_slotConnection(pc, flag_connection_choke); + m_slotUnchoke(-1); + + } else { +@@ -186,7 +193,7 @@ ChokeManager::set_snubbed(PeerConnectionBase* pc, ChokeManagerNode* base) { + + if (base->unchoked()) { + choke_manager_erase(&m_unchoked, pc); +- m_slotConnection(pc, true); ++ m_slotConnection(pc, flag_connection_choke); + m_slotUnchoke(-1); + + } else if (base->queued()) { +@@ -212,7 +219,7 @@ ChokeManager::set_not_snubbed(PeerConnectionBase* pc, ChokeManagerNode* base) { + if (!is_full() && (m_flags & flag_unchoke_all_new || m_slotCanUnchoke()) && + base->time_last_choke() + rak::timer::from_seconds(10) < cachedTime) { + m_unchoked.push_back(value_type(pc, 0)); +- m_slotConnection(pc, false); ++ m_slotConnection(pc, 0); + + m_slotUnchoke(1); + +@@ -346,20 +353,20 @@ ChokeManager::choke_range(iterator first, iterator last, uint32_t max) { + (itr - 1)->second > m_unchoked.end()) + throw internal_error("ChokeManager::choke_range(...) bad iterator range."); + +- count += (itr - 1)->first; +- + // We move the connections that return true, while the ones that +- // return false get thrown out. The function called must update +- // ChunkManager::m_queued if false is returned. ++ // return false stay unchoked. + // + // The C++ standard says std::partition will call the predicate + // max 'last - first' times, so we can assume it gets called once + // per element. + iterator split = std::partition(itr->second - (itr - 1)->first, itr->second, +- rak::on(rak::mem_ref(&value_type::first), std::bind2nd(m_slotConnection, true))); ++ rak::on(rak::mem_ref(&value_type::first), ++ std::bind2nd(m_slotConnection, flag_connection_choke | flag_connection_may_ignore))); + + m_queued.insert(m_queued.end(), itr->second - (itr - 1)->first, split); +- m_unchoked.erase(itr->second - (itr - 1)->first, itr->second); ++ m_unchoked.erase(itr->second - (itr - 1)->first, split); ++ ++ count += split - (itr->second - (itr - 1)->first); + } + + if (count > max) +@@ -392,7 +399,7 @@ ChokeManager::unchoke_range(iterator first, iterator last, uint32_t max) { + count += (itr - 1)->first; + + std::for_each(itr->second - (itr - 1)->first, itr->second, +- rak::on(rak::mem_ref(&value_type::first), std::bind2nd(m_slotConnection, false))); ++ rak::on(rak::mem_ref(&value_type::first), std::bind2nd(m_slotConnection, 0))); + + m_unchoked.insert(m_unchoked.end(), itr->second - (itr - 1)->first, itr->second); + m_queued.erase(itr->second - (itr - 1)->first, itr->second); +diff --git a/src/download/choke_manager.h b/src/download/choke_manager.h +index d9ab886..3a7ec67 100644 +--- a/src/download/choke_manager.h ++++ b/src/download/choke_manager.h +@@ -53,7 +53,7 @@ class ChokeManager { + public: + typedef rak::mem_fun1 slot_unchoke; + typedef rak::mem_fun0 slot_can_unchoke; +- typedef std::mem_fun1_t slot_connection; ++ typedef std::mem_fun1_t slot_connection; + + typedef std::vector > container_type; + typedef container_type::value_type value_type; +@@ -65,6 +65,9 @@ public: + + static const int flag_unchoke_all_new = 0x1; + ++ static const int flag_connection_choke = 0x1; ++ static const int flag_connection_may_ignore = 0x2; ++ + static const uint32_t order_base = (1 << 30); + static const uint32_t order_max_size = 4; + static const uint32_t weight_size_bytes = order_max_size * sizeof(uint32_t); +diff --git a/src/protocol/handshake.cc b/src/protocol/handshake.cc +index 7fb389b..ea3eee8 100644 +--- a/src/protocol/handshake.cc ++++ b/src/protocol/handshake.cc +@@ -840,7 +840,7 @@ Handshake::validate_download() { + throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_unknown_download); + if (!m_download->info()->is_active()) + throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_inactive_download); +- if (!m_download->info()->is_accepting_new_peers()) ++ if (!m_download->info()->is_accepting_new_peers() && m_peerInfo != NULL && !m_peerInfo->is_friend()) + throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_not_accepting_connections); + } + +@@ -1024,6 +1024,9 @@ Handshake::prepare_peer_info() { + if (m_peerInfo == NULL) + throw handshake_error(ConnectionManager::handshake_failed, e_handshake_network_error); + ++ if (!m_download->info()->is_accepting_new_peers() && !m_peerInfo->is_friend()) ++ throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_not_accepting_connections); ++ + if (m_peerInfo->failed_counter() > m_manager->max_failed) + throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_toomanyfailed); + +diff --git a/src/protocol/handshake_manager.cc b/src/protocol/handshake_manager.cc +index e06f594..2dea2e5 100644 +--- a/src/protocol/handshake_manager.cc ++++ b/src/protocol/handshake_manager.cc +@@ -108,7 +108,7 @@ HandshakeManager::erase_download(DownloadMain* info) { + void + HandshakeManager::add_incoming(SocketFd fd, const rak::socket_address& sa) { + if (!manager->connection_manager()->can_connect() || +- !manager->connection_manager()->filter(sa.c_sockaddr()) || ++ manager->connection_manager()->filter(sa.c_sockaddr()) == ConnectionManager::filter_blocked || + !setup_socket(fd)) { + fd.close(); + return; +@@ -126,7 +126,7 @@ HandshakeManager::add_incoming(SocketFd fd, const rak::socket_address& sa) { + void + HandshakeManager::add_outgoing(const rak::socket_address& sa, DownloadMain* download) { + if (!manager->connection_manager()->can_connect() || +- !manager->connection_manager()->filter(sa.c_sockaddr())) ++ manager->connection_manager()->filter(sa.c_sockaddr()) == ConnectionManager::filter_blocked) + return; + + create_outgoing(sa, download, manager->connection_manager()->encryption_options()); +diff --git a/src/protocol/peer_connection_base.cc b/src/protocol/peer_connection_base.cc +index 815ea93..9eddbb8 100644 +--- a/src/protocol/peer_connection_base.cc ++++ b/src/protocol/peer_connection_base.cc +@@ -203,10 +203,14 @@ PeerConnectionBase::set_upload_snubbed(bool v) { + } + + bool +-PeerConnectionBase::receive_upload_choke(bool choke) { ++PeerConnectionBase::receive_upload_choke(int flags) { ++ bool choke = flags & ChokeManager::flag_connection_choke; + if (choke == m_upChoke.choked()) + throw internal_error("PeerConnectionBase::receive_upload_choke(...) already set to the same state."); + ++ if (choke && m_peerInfo->is_friend() && (flags & ChokeManager::flag_connection_may_ignore)) ++ return false; ++ + write_insert_poll_safe(); + + m_sendChoked = true; +@@ -217,7 +221,8 @@ PeerConnectionBase::receive_upload_choke(bool choke) { + } + + bool +-PeerConnectionBase::receive_download_choke(bool choke) { ++PeerConnectionBase::receive_download_choke(int flags) { ++ bool choke = flags & ChokeManager::flag_connection_choke; + if (choke == m_downChoke.choked()) + throw internal_error("PeerConnectionBase::receive_download_choke(...) already set to the same state."); + +@@ -427,7 +432,7 @@ PeerConnectionBase::down_chunk() { + transfer->adjust_position(bytesTransfered); + + m_down->throttle()->node_used(m_peerChunks.download_throttle(), bytesTransfered); +- m_download->info()->down_rate()->insert(bytesTransfered); ++ m_download->info()->down_rate()->insert(bytesTransfered, m_peerInfo->is_friend()); + + return transfer->is_finished(); + } +@@ -497,7 +502,7 @@ PeerConnectionBase::down_chunk_process(const void* buffer, uint32_t length) { + transfer->adjust_position(length); + + m_down->throttle()->node_used(m_peerChunks.download_throttle(), length); +- m_download->info()->down_rate()->insert(length); ++ m_download->info()->down_rate()->insert(length, m_peerInfo->is_friend()); + + return length; + } +@@ -516,8 +521,8 @@ PeerConnectionBase::down_chunk_skip_process(const void* buffer, uint32_t length) + // Hmm, this might result in more bytes than nessesary being + // counted. + m_down->throttle()->node_used(m_peerChunks.download_throttle(), length); +- m_download->info()->down_rate()->insert(length); +- m_download->info()->skip_rate()->insert(length); ++ m_download->info()->down_rate()->insert(length, m_peerInfo->is_friend()); ++ m_download->info()->skip_rate()->insert(length, m_peerInfo->is_friend()); + + if (!transfer->is_valid()) { + transfer->adjust_position(length); +@@ -662,7 +667,7 @@ PeerConnectionBase::up_chunk() { + } + + m_up->throttle()->node_used(m_peerChunks.upload_throttle(), bytesTransfered); +- m_download->info()->up_rate()->insert(bytesTransfered); ++ m_download->info()->up_rate()->insert(bytesTransfered, m_peerInfo->is_friend()); + + // Just modifying the piece to cover the remaining data ends up + // being much cleaner and we avoid an unnessesary position variable. +diff --git a/src/protocol/peer_connection_base.h b/src/protocol/peer_connection_base.h +index d131341..dc0dc41 100644 +--- a/src/protocol/peer_connection_base.h ++++ b/src/protocol/peer_connection_base.h +@@ -127,8 +127,8 @@ public: + virtual void update_interested() = 0; + virtual bool receive_keepalive() = 0; + +- bool receive_upload_choke(bool choke); +- bool receive_download_choke(bool choke); ++ bool receive_upload_choke(int flags); ++ bool receive_download_choke(int flags); + + virtual void event_error(); + +diff --git a/src/protocol/peer_connection_leech.cc b/src/protocol/peer_connection_leech.cc +index 36c6d7a..7918dff 100644 +--- a/src/protocol/peer_connection_leech.cc ++++ b/src/protocol/peer_connection_leech.cc +@@ -516,6 +516,7 @@ PeerConnection::fill_write_buffer() { + if (type == Download::CONNECTION_LEECH && m_tryRequest) { + if (!(m_tryRequest = !should_request()) && + !(m_tryRequest = try_request_pieces()) && ++ !m_peerInfo->is_friend() && + + !download_queue()->is_interested_in_active()) { + m_sendInterested = true; +diff --git a/src/torrent/connection_manager.cc b/src/torrent/connection_manager.cc +index 152a0a1..efc919b 100644 +--- a/src/torrent/connection_manager.cc ++++ b/src/torrent/connection_manager.cc +@@ -163,7 +163,7 @@ ConnectionManager::set_proxy_address(const sockaddr* sa) { + uint32_t + ConnectionManager::filter(const sockaddr* sa) { + if (m_slotFilter.empty()) +- return 1; ++ return filter_permitted; + else + return m_slotFilter(sa); + } +diff --git a/src/torrent/connection_manager.h b/src/torrent/connection_manager.h +index 07ac736..c5e81c0 100644 +--- a/src/torrent/connection_manager.h ++++ b/src/torrent/connection_manager.h +@@ -84,6 +84,10 @@ public: + static const uint32_t encryption_enable_retry = (1 << 4); + static const uint32_t encryption_prefer_plaintext = (1 << 5); + ++ static const uint32_t filter_blocked = 0; ++ static const uint32_t filter_permitted = (1 << 0); ++ static const uint32_t filter_friend = (1 << 1); ++ + // Internal to libtorrent. + static const uint32_t encryption_use_proxy = (1 << 6); + +diff --git a/src/torrent/peer/connection_list.cc b/src/torrent/peer/connection_list.cc +index e50606e..fa9e912 100644 +--- a/src/torrent/peer/connection_list.cc ++++ b/src/torrent/peer/connection_list.cc +@@ -70,7 +70,7 @@ ConnectionList::clear() { + + PeerConnectionBase* + ConnectionList::insert(PeerInfo* peerInfo, const SocketFd& fd, Bitfield* bitfield, EncryptionInfo* encryptionInfo, ProtocolExtension* extensions) { +- if (size() >= m_maxSize) ++ if (size() >= m_maxSize && !peerInfo->is_friend()) + return NULL; + + PeerConnectionBase* peerConnection = m_slotNewConnection(encryptionInfo->is_encrypted()); +diff --git a/src/torrent/peer/peer.h b/src/torrent/peer/peer.h +index 70169ad..13e2880 100644 +--- a/src/torrent/peer/peer.h ++++ b/src/torrent/peer/peer.h +@@ -71,6 +71,8 @@ public: + void set_snubbed(bool v); + void set_banned(); + ++ bool is_friend() const { return peer_info()->is_friend(); } ++ + const HashString& id() const { return peer_info()->id(); } + const char* options() const { return peer_info()->options(); } + const sockaddr* address() const { return peer_info()->socket_address(); } +diff --git a/src/torrent/peer/peer_info.h b/src/torrent/peer/peer_info.h +index fe80027..8f56f73 100644 +--- a/src/torrent/peer/peer_info.h ++++ b/src/torrent/peer/peer_info.h +@@ -40,6 +40,10 @@ + #include + #include + ++// For conditional compilation depending on whether this patch was applied. ++// Remove for release. ++#define LIBTORRENT_FRIENDS 1 ++ + namespace torrent { + + class LIBTORRENT_EXPORT PeerInfo { +@@ -56,8 +60,11 @@ public: + static const int flag_handshake = (1 << 2); + static const int flag_blocked = (1 << 3); // For initial seeding. + static const int flag_restart = (1 << 4); ++ static const int flag_filtered = (1 << 5); ++ static const int flag_friend = (1 << 6); + +- PeerInfo(const sockaddr* address); ++ // Constructor is private. Use PeerList::insert_address or ++ // PeerList::create_peer_info instead. + ~PeerInfo(); + + bool is_connected() const { return m_flags & flag_connected; } +@@ -65,6 +72,8 @@ public: + bool is_handshake() const { return m_flags & flag_handshake; } + bool is_blocked() const { return m_flags & flag_blocked; } + bool is_restart() const { return m_flags & flag_restart; } ++ bool is_filtered() const { return m_flags & flag_filtered; } ++ bool is_friend() const { return m_flags & flag_friend; } + + int flags() const { return m_flags; } + +@@ -108,6 +117,7 @@ protected: + void set_connection(PeerConnectionBase* c) { m_connection = c; } + + private: ++ PeerInfo(const sockaddr* address); + PeerInfo(const PeerInfo&); + void operator = (const PeerInfo&); + +diff --git a/src/torrent/peer/peer_list.cc b/src/torrent/peer/peer_list.cc +index cef840b..7164b91 100644 +--- a/src/torrent/peer/peer_list.cc ++++ b/src/torrent/peer/peer_list.cc +@@ -42,7 +42,9 @@ + #include + + #include "download/available_list.h" ++#include "torrent/connection_manager.h" + #include "torrent/peer/client_list.h" ++#include "torrent.h" + + #include "exceptions.h" + #include "globals.h" +@@ -102,6 +104,20 @@ PeerList::~PeerList() { + } + + PeerInfo* ++PeerList::create_peer_info(const sockaddr* sa) { ++ PeerInfo* peerInfo = new PeerInfo(sa); ++ uint32_t filter = connection_manager()->filter(sa); ++ ++ if (filter == ConnectionManager::filter_blocked) ++ peerInfo->set_flags(PeerInfo::flag_filtered); ++ ++ else if (filter & ConnectionManager::filter_friend) ++ peerInfo->set_flags(PeerInfo::flag_friend); ++ ++ return peerInfo; ++} ++ ++PeerInfo* + PeerList::insert_address(const sockaddr* sa, int flags) { + if (!socket_address_key::is_comparable(sa)) + return NULL; +@@ -118,7 +134,7 @@ PeerList::insert_address(const sockaddr* sa, int flags) { + + const rak::socket_address* address = rak::socket_address::cast_from(sa); + +- PeerInfo* peerInfo = new PeerInfo(sa); ++ PeerInfo* peerInfo = create_peer_info(sa); + peerInfo->set_listen_port(address->port()); + + manager->client_list()->retrieve_unknown(&peerInfo->mutable_client_info()); +@@ -216,7 +232,7 @@ PeerList::connected(const sockaddr* sa, int flags) { + + if (range.first == range.second) { + // Create a new entry. +- peerInfo = new PeerInfo(sa); ++ peerInfo = create_peer_info(sa); + + base_type::insert(range.second, value_type(socket_address_key(peerInfo->socket_address()), peerInfo)); + +diff --git a/src/torrent/peer/peer_list.h b/src/torrent/peer/peer_list.h +index fece106..2ee5e1e 100644 +--- a/src/torrent/peer/peer_list.h ++++ b/src/torrent/peer/peer_list.h +@@ -111,6 +111,8 @@ public: + const_reverse_iterator rbegin() const { return base_type::rbegin(); } + const_reverse_iterator rend() const { return base_type::rend(); } + ++ static PeerInfo* create_peer_info(const sockaddr* sa); ++ + protected: + // Insert, or find a PeerInfo with socket address 'sa'. Returns end + // if no more connections are allowed from that host. +diff --git a/src/torrent/rate.cc b/src/torrent/rate.cc +index c28d718..586350d 100644 +--- a/src/torrent/rate.cc ++++ b/src/torrent/rate.cc +@@ -58,7 +58,7 @@ Rate::rate() const { + } + + void +-Rate::insert(rate_type bytes) { ++Rate::insert(rate_type bytes, bool rate_only) { + discard_old(); + + if (m_current > ((rate_type)1 << 40) || bytes > ((rate_type)1 << 28)) +@@ -69,7 +69,7 @@ Rate::insert(rate_type bytes) { + else + m_container.front().second += bytes; + +- m_total += bytes; ++ m_total += rate_only ? 0 : bytes; + m_current += bytes; + } + +diff --git a/src/torrent/rate.h b/src/torrent/rate.h +index 239e4b6..4c48aea 100644 +--- a/src/torrent/rate.h ++++ b/src/torrent/rate.h +@@ -68,7 +68,7 @@ public: + timer_type span() const { return m_span; } + void set_span(timer_type s) { m_span = s; } + +- void insert(rate_type bytes); ++ void insert(rate_type bytes, bool rate_only = false); + + void reset_rate() { m_current = 0; m_container.clear(); } + -- cgit v1.2.3