From 1cb0161f0e761460576eb62404e4819943bf9006 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Fri, 26 Jul 2013 16:59:10 +0000 Subject: [PATCH] merge fix from RC_0_16 --- ChangeLog | 1 + include/libtorrent/peer_connection.hpp | 6 +++- src/peer_connection.cpp | 46 +++++++++++++++++++++----- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index b453a0168..addeab0aa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,7 @@ * fix uTP edge case where udp socket buffer fills up * fix nagle implementation in uTP + * introduce bandwidth exhaustion attack-mitigation in allowed-fast pieces * python binding fix issue where torrent_info objects where destructing when their torrents were deleted * added missing field to scrape_failed_alert in python bindings * GCC 4.8 fix diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index c2cf67e8f..0c53a477e 100644 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -915,6 +915,11 @@ namespace libtorrent // if requested (regardless of choke state) std::vector m_accept_fast; + // a sent-piece counter for the allowed fast set + // to avoid exploitation. Each slot is a counter + // for one of the pieces from the allowed-fast set + std::vector m_accept_fast_piece_cnt; + // the pieces the peer will send us if // requested (regardless of choke state) std::vector m_allowed_fast; @@ -1308,4 +1313,3 @@ namespace libtorrent } #endif // TORRENT_PEER_CONNECTION_HPP_INCLUDED - diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 784e8c97b..99f53e7cb 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -392,9 +392,10 @@ namespace libtorrent } else if (m_ses.settings().seed_choking_algorithm == session_settings::anti_leech) { - // the anti-leech seeding algorithm ranks peers based on how many pieces - // they have, prefering to unchoke peers that just started and peers that - // are close to completing. Like this: + // the anti-leech seeding algorithm is based on the paper "Improving + // BitTorrent: A Simple Approach" from Chow et. al. and ranks peers based + // on how many pieces they have, prefering to unchoke peers that just + // started and peers that are close to completing. Like this: // ^ // | \ / | // | \ / | @@ -603,8 +604,13 @@ namespace libtorrent TORRENT_ASSERT(std::find(m_accept_fast.begin() , m_accept_fast.end(), i) == m_accept_fast.end()); - if (m_accept_fast.empty()) m_accept_fast.reserve(10); + if (m_accept_fast.empty()) + { + m_accept_fast.reserve(10); + m_accept_fast_piece_cnt.reserve(10); + } m_accept_fast.push_back(i); + m_accept_fast_piece_cnt.push_back(0); } return; } @@ -639,8 +645,13 @@ namespace libtorrent peer_log("==> ALLOWED_FAST [ %d ]", piece); #endif write_allow_fast(piece); - if (m_accept_fast.empty()) m_accept_fast.reserve(10); + if (m_accept_fast.empty()) + { + m_accept_fast.reserve(10); + m_accept_fast_piece_cnt.reserve(10); + } m_accept_fast.push_back(piece); + m_accept_fast_piece_cnt.push_back(0); if (int(m_accept_fast.size()) >= num_allowed_pieces || int(m_accept_fast.size()) == num_pieces) return; } @@ -1981,6 +1992,11 @@ namespace libtorrent return; } + int fast_idx = -1; + std::vector::iterator fast_iter = std::find(m_accept_fast.begin() + , m_accept_fast.end(), r.piece); + if (fast_iter != m_accept_fast.end()) fast_idx = fast_iter - m_accept_fast.begin(); + // make sure this request // is legal and that the peer // is not choked @@ -1996,8 +2012,18 @@ namespace libtorrent { // if we have choked the client // ignore the request - if (m_choked && std::find(m_accept_fast.begin(), m_accept_fast.end() - , r.piece) == m_accept_fast.end()) + const int blocks_per_piece = static_cast( + (t->torrent_file().piece_length() + t->block_size() - 1) / t->block_size()); + + // disconnect peers that downloads more than foo times an allowed + // fast piece + if (m_choked && fast_idx != -1 && m_accept_fast_piece_cnt[fast_idx] >= 3 * blocks_per_piece) + { + disconnect(errors::too_many_requests_when_choked); + return; + } + + if (m_choked && fast_idx == -1) { #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING peer_log("*** REJECTING REQUEST [ peer choked and piece not in allowed fast set ]"); @@ -2021,6 +2047,10 @@ namespace libtorrent } else { + // increase the allowed fast set counter + if (fast_idx != -1) + ++m_accept_fast_piece_cnt[fast_idx]; + m_requests.push_back(r); #ifdef TORRENT_REQUEST_LOGGING if (m_ses.m_request_log) @@ -5650,6 +5680,7 @@ namespace libtorrent { TORRENT_ASSERT(m_in_use == 1337); TORRENT_ASSERT(m_queued_time_critical <= int(m_request_queue.size())); + TORRENT_ASSERT(m_accept_fast.size() == m_accept_fast_piece_cnt.size()); TORRENT_ASSERT(bool(m_disk_recv_buffer) == (m_disk_recv_buffer_size > 0)); @@ -5972,4 +6003,3 @@ namespace libtorrent } } -