2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
2014-02-23 20:12:25 +01:00
|
|
|
Copyright (c) 2003-2014, Arvid Norberg
|
2003-10-23 01:00:57 +02:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2007-03-17 18:15:16 +01:00
|
|
|
#include "libtorrent/pch.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push, 1)
|
|
|
|
#endif
|
|
|
|
|
2005-09-28 20:32:05 +02:00
|
|
|
#include <boost/bind.hpp>
|
2007-04-10 23:23:13 +02:00
|
|
|
#include <boost/utility.hpp>
|
2014-01-23 03:28:04 +01:00
|
|
|
#include <boost/crc.hpp>
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
|
|
|
|
2007-03-17 18:15:16 +01:00
|
|
|
#include "libtorrent/peer_connection.hpp"
|
2006-04-25 23:04:48 +02:00
|
|
|
#include "libtorrent/web_peer_connection.hpp"
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/policy.hpp"
|
|
|
|
#include "libtorrent/torrent.hpp"
|
|
|
|
#include "libtorrent/socket.hpp"
|
2004-01-18 20:12:18 +01:00
|
|
|
#include "libtorrent/alert_types.hpp"
|
2005-05-12 01:03:12 +02:00
|
|
|
#include "libtorrent/invariant_check.hpp"
|
2007-04-05 00:27:36 +02:00
|
|
|
#include "libtorrent/time.hpp"
|
2006-10-11 16:02:21 +02:00
|
|
|
#include "libtorrent/aux_/session_impl.hpp"
|
2007-07-04 04:16:49 +02:00
|
|
|
#include "libtorrent/piece_picker.hpp"
|
2008-12-26 08:00:21 +01:00
|
|
|
#include "libtorrent/broadcast_socket.hpp"
|
2009-11-26 06:45:43 +01:00
|
|
|
#include "libtorrent/peer_info.hpp"
|
2011-02-26 08:55:51 +01:00
|
|
|
#include "libtorrent/random.hpp"
|
2011-05-02 03:45:56 +02:00
|
|
|
#include "libtorrent/extensions.hpp"
|
2004-01-12 04:05:10 +01:00
|
|
|
|
2008-11-29 22:33:21 +01:00
|
|
|
#ifdef TORRENT_DEBUG
|
2007-07-04 17:46:10 +02:00
|
|
|
#include "libtorrent/bt_peer_connection.hpp"
|
|
|
|
#endif
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
using namespace libtorrent;
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2007-09-19 08:05:14 +02:00
|
|
|
struct match_peer_endpoint
|
2007-06-09 01:02:31 +02:00
|
|
|
{
|
2007-09-19 08:05:14 +02:00
|
|
|
match_peer_endpoint(tcp::endpoint const& ep)
|
|
|
|
: m_ep(ep)
|
2007-06-09 01:02:31 +02:00
|
|
|
{}
|
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
bool operator()(policy::peer const* p) const
|
2012-04-08 18:03:39 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(p->in_use);
|
|
|
|
return p->address() == m_ep.address() && p->port == m_ep.port();
|
|
|
|
}
|
2007-06-09 01:02:31 +02:00
|
|
|
|
2007-09-19 08:05:14 +02:00
|
|
|
tcp::endpoint const& m_ep;
|
2007-02-12 10:20:49 +01:00
|
|
|
};
|
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2007-02-12 10:20:49 +01:00
|
|
|
struct match_peer_connection
|
|
|
|
{
|
2010-01-18 23:20:42 +01:00
|
|
|
match_peer_connection(peer_connection const& c) : m_conn(c) {}
|
|
|
|
|
|
|
|
bool operator()(policy::peer const* p) const
|
2012-04-08 18:03:39 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(p->in_use);
|
|
|
|
return p->connection == &m_conn;
|
|
|
|
}
|
2010-01-18 23:20:42 +01:00
|
|
|
|
|
|
|
peer_connection const& m_conn;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct match_peer_connection_or_endpoint
|
|
|
|
{
|
|
|
|
match_peer_connection_or_endpoint(peer_connection const& c) : m_conn(c) {}
|
2007-02-12 10:20:49 +01:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
bool operator()(policy::peer const* p) const
|
2007-06-09 01:02:31 +02:00
|
|
|
{
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2009-05-07 00:36:24 +02:00
|
|
|
return p->connection == &m_conn
|
|
|
|
|| (p->ip() == m_conn.remote()
|
|
|
|
&& p->connectable);
|
2007-06-09 01:02:31 +02:00
|
|
|
}
|
2007-02-12 10:20:49 +01:00
|
|
|
|
2007-06-09 01:02:31 +02:00
|
|
|
peer_connection const& m_conn;
|
2007-02-12 10:20:49 +01:00
|
|
|
};
|
2008-05-28 20:25:48 +02:00
|
|
|
#endif
|
2007-02-12 10:20:49 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
2012-12-31 07:54:54 +01:00
|
|
|
|
|
|
|
void apply_mask(boost::uint8_t* b, boost::uint8_t const* mask, int size)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < size; ++i)
|
|
|
|
{
|
|
|
|
*b &= *mask;
|
|
|
|
++b;
|
|
|
|
++mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1. if the IP addresses are identical, hash the ports in 16 bit network-order
|
|
|
|
// binary representation, ordered lowest first.
|
|
|
|
// 2. if the IPs are in the same /24, hash the IPs ordered, lowest first.
|
|
|
|
// 3. if the IPs are in the ame /16, mask the IPs by 0xffffff55, hash them
|
|
|
|
// ordered, lowest first.
|
|
|
|
// 4. if IPs are not in the same /16, mask the IPs by 0xffff5555, hash them
|
|
|
|
// ordered, lowest first.
|
|
|
|
//
|
|
|
|
// * for IPv6 peers, just use the first 64 bits and widen the masks.
|
|
|
|
// like this: 0xffff5555 -> 0xffffffff55555555
|
|
|
|
// the lower 64 bits are always unmasked
|
|
|
|
//
|
|
|
|
// * for IPv6 addresses, compare /32 and /48 instead of /16 and /24
|
|
|
|
//
|
|
|
|
// * the two IP addresses that are used to calculate the rank must
|
|
|
|
// always be of the same address family
|
|
|
|
//
|
|
|
|
// * all IP addresses are in network byte order when hashed
|
|
|
|
boost::uint32_t peer_priority(tcp::endpoint e1, tcp::endpoint e2)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(e1.address().is_v4() == e2.address().is_v4());
|
|
|
|
|
|
|
|
using std::swap;
|
|
|
|
|
2014-01-23 03:28:04 +01:00
|
|
|
// this is the crc32c (Castagnoli) polynomial
|
|
|
|
// TODO: 2 this could be optimized if SSE 4.2 is
|
|
|
|
// available. It could also be optimized given
|
|
|
|
// that we have a fixed length
|
|
|
|
boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc;
|
|
|
|
|
2012-12-31 07:54:54 +01:00
|
|
|
if (e1.address() == e2.address())
|
|
|
|
{
|
|
|
|
if (e1.port() > e2.port())
|
|
|
|
swap(e1, e2);
|
|
|
|
boost::uint16_t p[2];
|
|
|
|
p[0] = htons(e1.port());
|
|
|
|
p[1] = htons(e2.port());
|
2014-01-23 09:10:09 +01:00
|
|
|
crc.process_bytes((char const*)&p[0], 4);
|
2012-12-31 07:54:54 +01:00
|
|
|
}
|
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
else if (e1.address().is_v6())
|
|
|
|
{
|
|
|
|
const static boost::uint8_t v6mask[][8] = {
|
|
|
|
{ 0xff, 0xff, 0xff, 0xff, 0x55, 0x55, 0x55, 0x55 },
|
|
|
|
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x55, 0x55 },
|
|
|
|
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
|
|
|
|
};
|
|
|
|
|
2013-09-09 09:07:09 +02:00
|
|
|
if (e2 < e1) swap(e1, e2);
|
2012-12-31 07:54:54 +01:00
|
|
|
address_v6::bytes_type b1 = e1.address().to_v6().to_bytes();
|
|
|
|
address_v6::bytes_type b2 = e2.address().to_v6().to_bytes();
|
|
|
|
int mask = memcmp(&b1[0], &b2[0], 4) ? 0
|
|
|
|
: memcmp(&b1[0], &b2[0], 6) ? 1 : 2;
|
|
|
|
apply_mask(&b1[0], v6mask[mask], 8);
|
|
|
|
apply_mask(&b2[0], v6mask[mask], 8);
|
2014-01-23 03:28:04 +01:00
|
|
|
|
2014-01-23 09:10:09 +01:00
|
|
|
crc.process_bytes((char const*)&b1[0], 16);
|
|
|
|
crc.process_bytes((char const*)&b2[0], 16);
|
2012-12-31 07:54:54 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const static boost::uint8_t v4mask[][4] = {
|
|
|
|
{ 0xff, 0xff, 0x55, 0x55 },
|
|
|
|
{ 0xff, 0xff, 0xff, 0x55 },
|
|
|
|
{ 0xff, 0xff, 0xff, 0xff }
|
|
|
|
};
|
|
|
|
|
2013-09-09 09:07:09 +02:00
|
|
|
if (e2 < e1) swap(e1, e2);
|
2012-12-31 07:54:54 +01:00
|
|
|
address_v4::bytes_type b1 = e1.address().to_v4().to_bytes();
|
|
|
|
address_v4::bytes_type b2 = e2.address().to_v4().to_bytes();
|
|
|
|
int mask = memcmp(&b1[0], &b2[0], 2) ? 0
|
|
|
|
: memcmp(&b1[0], &b2[0], 3) ? 1 : 2;
|
|
|
|
apply_mask(&b1[0], v4mask[mask], 4);
|
|
|
|
apply_mask(&b2[0], v4mask[mask], 4);
|
2014-01-23 03:28:04 +01:00
|
|
|
|
2014-01-23 09:10:09 +01:00
|
|
|
crc.process_bytes((char const*)&b1[0], 4);
|
|
|
|
crc.process_bytes((char const*)&b2[0], 4);
|
2012-12-31 07:54:54 +01:00
|
|
|
}
|
|
|
|
|
2014-01-23 03:28:04 +01:00
|
|
|
return crc.checksum();
|
2012-12-31 07:54:54 +01:00
|
|
|
}
|
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
// returns the rank of a peer's source. We have an affinity
|
|
|
|
// to connecting to peers with higher rank. This is to avoid
|
2010-11-29 02:33:05 +01:00
|
|
|
// problems when our peer list is diluted by stale peers from
|
2009-05-07 00:36:24 +02:00
|
|
|
// the resume data for instance
|
|
|
|
int source_rank(int source_bitmask)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
if (source_bitmask & peer_info::tracker) ret |= 1 << 5;
|
|
|
|
if (source_bitmask & peer_info::lsd) ret |= 1 << 4;
|
|
|
|
if (source_bitmask & peer_info::dht) ret |= 1 << 3;
|
|
|
|
if (source_bitmask & peer_info::pex) ret |= 1 << 2;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2004-02-01 11:45:54 +01:00
|
|
|
// the case where ignore_peer is motivated is if two peers
|
2004-02-01 10:47:58 +01:00
|
|
|
// have only one piece that we don't have, and it's the
|
|
|
|
// same piece for both peers. Then they might get into an
|
2005-08-15 00:04:58 +02:00
|
|
|
// infinite loop, fighting to request the same blocks.
|
2007-08-14 19:47:48 +02:00
|
|
|
void request_a_block(torrent& t, peer_connection& c)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2007-08-16 21:36:33 +02:00
|
|
|
if (t.is_seed()) return;
|
2008-11-06 09:34:56 +01:00
|
|
|
if (c.no_download()) return;
|
2009-06-19 00:32:55 +02:00
|
|
|
if (t.upload_mode()) return;
|
2010-10-02 23:01:11 +02:00
|
|
|
if (c.is_disconnecting()) return;
|
2007-08-16 21:36:33 +02:00
|
|
|
|
2009-10-05 02:10:35 +02:00
|
|
|
// don't request pieces before we have the metadata
|
|
|
|
if (!t.valid_metadata()) return;
|
|
|
|
|
2009-10-31 19:39:46 +01:00
|
|
|
// don't request pieces before the peer is properly
|
|
|
|
// initialized after we have the metadata
|
|
|
|
if (!t.are_files_checked()) return;
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(t.valid_metadata());
|
2009-11-02 21:43:38 +01:00
|
|
|
TORRENT_ASSERT(c.peer_info_struct() != 0 || c.type() != peer_connection::bittorrent_connection);
|
2014-04-22 06:21:14 +02:00
|
|
|
|
|
|
|
bool time_critical_mode = t.num_time_critical_pieces() > 0;
|
|
|
|
|
|
|
|
int desired_queue_size = c.desired_queue_size();
|
|
|
|
|
|
|
|
// in time critical mode, only have 1 outstanding request at a time
|
|
|
|
// via normal requests
|
|
|
|
if (time_critical_mode)
|
|
|
|
desired_queue_size = (std::min)(1, desired_queue_size);
|
|
|
|
|
|
|
|
int num_requests = desired_queue_size
|
2006-04-25 23:04:48 +02:00
|
|
|
- (int)c.download_queue().size()
|
2005-09-27 10:07:24 +02:00
|
|
|
- (int)c.request_queue().size();
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2007-08-14 19:47:48 +02:00
|
|
|
#ifdef TORRENT_VERBOSE_LOGGING
|
2010-12-18 11:19:34 +01:00
|
|
|
c.peer_log("*** PIECE_PICKER [ req: %d engame: %d ]", num_requests, c.endgame());
|
2007-08-14 19:47:48 +02:00
|
|
|
#endif
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(c.desired_queue_size() > 0);
|
2003-11-05 00:27:06 +01:00
|
|
|
// if our request queue is already full, we
|
|
|
|
// don't have to make any new requests yet
|
|
|
|
if (num_requests <= 0) return;
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
piece_picker& p = t.picker();
|
|
|
|
std::vector<piece_block> interesting_pieces;
|
|
|
|
interesting_pieces.reserve(100);
|
2003-11-02 22:06:50 +01:00
|
|
|
|
2007-09-03 23:16:24 +02:00
|
|
|
int prefer_whole_pieces = c.prefer_whole_pieces();
|
2007-05-16 06:12:13 +02:00
|
|
|
|
2014-04-22 06:21:14 +02:00
|
|
|
if (prefer_whole_pieces == 0 && !time_critical_mode)
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
|
|
|
prefer_whole_pieces = c.statistics().download_payload_rate()
|
|
|
|
* t.settings().whole_pieces_threshold
|
2007-09-03 23:16:24 +02:00
|
|
|
> t.torrent_file().piece_length() ? 1 : 0;
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// if we prefer whole pieces, the piece picker will pick at least
|
|
|
|
// the number of blocks we want, but it will try to make the picked
|
|
|
|
// blocks be from whole pieces, possibly by returning more blocks
|
|
|
|
// than we requested.
|
2008-11-29 22:33:21 +01:00
|
|
|
#ifdef TORRENT_DEBUG
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2008-02-08 11:22:05 +01:00
|
|
|
TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec);
|
|
|
|
#endif
|
2006-11-14 01:08:16 +01:00
|
|
|
|
2011-02-09 03:56:00 +01:00
|
|
|
aux::session_impl& ses = t.session();
|
2007-04-27 02:27:37 +02:00
|
|
|
|
2011-02-09 03:56:00 +01:00
|
|
|
std::vector<pending_block> const& dq = c.download_queue();
|
|
|
|
std::vector<pending_block> const& rq = c.request_queue();
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2007-09-05 23:21:11 +02:00
|
|
|
std::vector<int> const& suggested = c.suggested_pieces();
|
2011-02-09 03:56:00 +01:00
|
|
|
bitfield const* bits = &c.get_bitfield();
|
|
|
|
bitfield fast_mask;
|
|
|
|
|
2007-08-14 19:47:48 +02:00
|
|
|
if (c.has_peer_choked())
|
|
|
|
{
|
|
|
|
// if we are choked we can only pick pieces from the
|
|
|
|
// allowed fast set. The allowed fast set is sorted
|
|
|
|
// in ascending priority order
|
|
|
|
std::vector<int> const& allowed_fast = c.allowed_fast();
|
|
|
|
|
2007-09-05 23:21:11 +02:00
|
|
|
// build a bitmask with only the allowed pieces in it
|
2011-02-09 03:56:00 +01:00
|
|
|
fast_mask.resize(c.get_bitfield().size(), false);
|
2007-09-05 23:21:11 +02:00
|
|
|
for (std::vector<int>::const_iterator i = allowed_fast.begin()
|
|
|
|
, end(allowed_fast.end()); i != end; ++i)
|
2011-02-09 03:56:00 +01:00
|
|
|
if ((*bits)[*i]) fast_mask.set_bit(*i);
|
|
|
|
bits = &fast_mask;
|
2007-08-14 19:47:48 +02:00
|
|
|
}
|
|
|
|
|
2011-02-09 03:56:00 +01:00
|
|
|
piece_picker::piece_state_t state;
|
|
|
|
peer_connection::peer_speed_t speed = c.peer_speed();
|
|
|
|
if (speed == peer_connection::fast) state = piece_picker::fast;
|
|
|
|
else if (speed == peer_connection::medium) state = piece_picker::medium;
|
|
|
|
else state = piece_picker::slow;
|
|
|
|
|
|
|
|
// picks the interesting pieces from this peer
|
|
|
|
// the integer is the number of pieces that
|
|
|
|
// should be guaranteed to be available for download
|
|
|
|
// (if num_requests is too big, too many pieces are
|
|
|
|
// picked and cpu-time is wasted)
|
|
|
|
// the last argument is if we should prefer whole pieces
|
|
|
|
// for this peer. If we're downloading one piece in 20 seconds
|
|
|
|
// then use this mode.
|
|
|
|
p.pick_pieces(*bits, interesting_pieces
|
|
|
|
, num_requests, prefer_whole_pieces, c.peer_info_struct()
|
2011-08-15 01:16:12 +02:00
|
|
|
, state, c.picker_options(), suggested, t.num_peers());
|
2011-02-09 03:56:00 +01:00
|
|
|
|
2007-08-14 19:47:48 +02:00
|
|
|
#ifdef TORRENT_VERBOSE_LOGGING
|
2010-10-31 23:12:26 +01:00
|
|
|
c.peer_log("*** PIECE_PICKER [ prefer_whole: %d picked: %d ]"
|
|
|
|
, prefer_whole_pieces, int(interesting_pieces.size()));
|
2007-08-14 19:47:48 +02:00
|
|
|
#endif
|
2011-02-08 05:08:04 +01:00
|
|
|
|
|
|
|
// if the number of pieces we have + the number of pieces
|
|
|
|
// we're requesting from is less than the number of pieces
|
|
|
|
// in the torrent, there are still some unrequested pieces
|
|
|
|
// and we're not strictly speaking in end-game mode yet
|
|
|
|
// also, if we already have at least one outstanding
|
|
|
|
// request, we shouldn't pick any busy pieces either
|
2014-04-22 06:21:14 +02:00
|
|
|
// in time critical mode, it's OK to request busy blocks
|
|
|
|
bool dont_pick_busy_blocks = ((ses.m_settings.strict_end_game_mode
|
2012-07-02 05:27:32 +02:00
|
|
|
&& p.num_downloading_pieces() < p.num_want_left())
|
2014-04-22 06:21:14 +02:00
|
|
|
|| dq.size() + rq.size() > 0)
|
|
|
|
&& !time_critical_mode;
|
2011-02-08 05:08:04 +01:00
|
|
|
|
2011-02-09 03:56:00 +01:00
|
|
|
// this is filled with an interesting piece
|
|
|
|
// that some other peer is currently downloading
|
|
|
|
piece_block busy_block = piece_block::invalid;
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
for (std::vector<piece_block>::iterator i = interesting_pieces.begin();
|
2005-05-25 12:01:01 +02:00
|
|
|
i != interesting_pieces.end(); ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2011-02-07 00:40:21 +01:00
|
|
|
#ifdef TORRENT_STATS
|
|
|
|
++ses.m_piece_picker_blocks;
|
|
|
|
#endif
|
|
|
|
|
2007-09-10 10:07:18 +02:00
|
|
|
if (prefer_whole_pieces == 0 && num_requests <= 0) break;
|
|
|
|
|
2014-04-22 06:21:14 +02:00
|
|
|
if (time_critical_mode && p.piece_priority(i->piece_index) != 7)
|
|
|
|
{
|
|
|
|
// assume the subsequent pieces are not prio 7 and
|
|
|
|
// be done
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-02-07 00:40:21 +01:00
|
|
|
int num_block_requests = p.num_peers(*i);
|
|
|
|
if (num_block_requests > 0)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2011-02-08 05:08:04 +01:00
|
|
|
// have we picked enough pieces?
|
2007-08-21 03:17:42 +02:00
|
|
|
if (num_requests <= 0) break;
|
2011-02-07 00:40:21 +01:00
|
|
|
|
2011-02-08 05:08:04 +01:00
|
|
|
// this block is busy. This means all the following blocks
|
|
|
|
// in the interesting_pieces list are busy as well, we might
|
|
|
|
// as well just exit the loop
|
|
|
|
if (dont_pick_busy_blocks) break;
|
|
|
|
|
2008-02-11 07:52:40 +01:00
|
|
|
TORRENT_ASSERT(p.num_peers(*i) > 0);
|
2011-02-09 03:56:00 +01:00
|
|
|
busy_block = *i;
|
2003-10-23 01:00:57 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(p.num_peers(*i) == 0);
|
2009-03-17 10:34:44 +01:00
|
|
|
|
|
|
|
// don't request pieces we already have in our request queue
|
2011-01-01 18:09:25 +01:00
|
|
|
// This happens when pieces time out or the peer sends us
|
|
|
|
// pieces we didn't request. Those aren't marked in the
|
|
|
|
// piece picker, but we still keep track of them in the
|
|
|
|
// download queue
|
2009-03-17 10:34:44 +01:00
|
|
|
if (std::find_if(dq.begin(), dq.end(), has_block(*i)) != dq.end()
|
2009-12-25 17:52:57 +01:00
|
|
|
|| std::find_if(rq.begin(), rq.end(), has_block(*i)) != rq.end())
|
2010-12-18 11:19:34 +01:00
|
|
|
{
|
2011-01-01 18:09:25 +01:00
|
|
|
#ifdef TORRENT_DEBUG
|
|
|
|
std::vector<pending_block>::const_iterator j
|
|
|
|
= std::find_if(dq.begin(), dq.end(), has_block(*i));
|
|
|
|
if (j != dq.end()) TORRENT_ASSERT(j->timed_out || j->not_wanted);
|
|
|
|
#endif
|
2009-03-17 10:34:44 +01:00
|
|
|
continue;
|
2010-12-18 11:19:34 +01:00
|
|
|
}
|
2009-03-17 10:34:44 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
// ok, we found a piece that's not being downloaded
|
|
|
|
// by somebody else. request it from this peer
|
2004-02-01 10:47:58 +01:00
|
|
|
// and return
|
2009-12-25 17:52:57 +01:00
|
|
|
if (!c.add_request(*i, 0)) continue;
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(p.num_peers(*i) == 1);
|
|
|
|
TORRENT_ASSERT(p.is_requested(*i));
|
2003-11-05 00:27:06 +01:00
|
|
|
num_requests--;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2010-12-18 11:19:34 +01:00
|
|
|
// we have picked as many blocks as we should
|
|
|
|
// we're done!
|
|
|
|
if (num_requests <= 0)
|
|
|
|
{
|
|
|
|
// since we could pick as many blocks as we
|
|
|
|
// requested without having to resort to picking
|
|
|
|
// busy ones, we're not in end-game mode
|
|
|
|
c.set_endgame(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we did not pick as many pieces as we wanted, because
|
|
|
|
// there aren't enough. This means we're in end-game mode
|
|
|
|
// as long as we have at least one request outstanding,
|
|
|
|
// we shouldn't pick another piece
|
2011-06-01 11:14:21 +02:00
|
|
|
// if we are attempting to download 'allowed' pieces
|
|
|
|
// and can't find any, that doesn't count as end-game
|
|
|
|
if (!c.has_peer_choked())
|
|
|
|
c.set_endgame(true);
|
2010-12-18 11:19:34 +01:00
|
|
|
|
2010-02-18 18:26:21 +01:00
|
|
|
// if we don't have any potential busy blocks to request
|
|
|
|
// or if we already have outstanding requests, don't
|
|
|
|
// pick a busy piece
|
2011-02-09 03:56:00 +01:00
|
|
|
if (busy_block == piece_block::invalid
|
2010-02-18 18:26:21 +01:00
|
|
|
|| dq.size() + rq.size() > 0)
|
2003-11-05 00:27:06 +01:00
|
|
|
{
|
2007-07-06 19:15:35 +02:00
|
|
|
return;
|
|
|
|
}
|
2004-02-04 12:00:29 +01:00
|
|
|
|
2011-02-06 04:07:00 +01:00
|
|
|
#ifdef TORRENT_STATS
|
2011-02-07 01:51:20 +01:00
|
|
|
++ses.m_end_game_piece_picker_blocks;
|
2011-02-06 04:07:00 +01:00
|
|
|
#endif
|
|
|
|
|
2008-11-29 22:33:21 +01:00
|
|
|
#ifdef TORRENT_DEBUG
|
2007-08-03 08:13:26 +02:00
|
|
|
piece_picker::downloading_piece st;
|
2011-02-09 03:56:00 +01:00
|
|
|
p.piece_info(busy_block.piece_index, st);
|
|
|
|
TORRENT_ASSERT(st.requested + st.finished + st.writing
|
|
|
|
== p.blocks_in_piece(busy_block.piece_index));
|
2007-08-03 08:13:26 +02:00
|
|
|
#endif
|
2011-02-09 03:56:00 +01:00
|
|
|
TORRENT_ASSERT(p.is_requested(busy_block));
|
2011-02-23 17:47:18 +01:00
|
|
|
TORRENT_ASSERT(!p.is_downloaded(busy_block));
|
|
|
|
TORRENT_ASSERT(!p.is_finished(busy_block));
|
2011-02-09 03:56:00 +01:00
|
|
|
TORRENT_ASSERT(p.num_peers(busy_block) > 0);
|
2010-02-18 18:26:21 +01:00
|
|
|
|
2011-02-09 03:56:00 +01:00
|
|
|
c.add_request(busy_block, peer_connection::req_busy);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
policy::policy(torrent* t)
|
2010-02-14 02:39:55 +01:00
|
|
|
: m_torrent(t)
|
2012-11-21 21:42:40 +01:00
|
|
|
, m_locked_peer(NULL)
|
2010-02-14 02:39:55 +01:00
|
|
|
, m_round_robin(0)
|
2008-03-29 19:47:24 +01:00
|
|
|
, m_num_connect_candidates(0)
|
2008-05-07 09:35:36 +02:00
|
|
|
, m_num_seeds(0)
|
2009-05-06 09:06:26 +02:00
|
|
|
, m_finished(false)
|
2007-10-05 02:30:00 +02:00
|
|
|
{ TORRENT_ASSERT(t); }
|
2007-07-26 09:04:35 +02:00
|
|
|
|
2014-01-23 04:31:36 +01:00
|
|
|
void policy::clear_peer_prio()
|
|
|
|
{
|
|
|
|
for (peers_t::iterator i = m_peers.begin()
|
|
|
|
, end(m_peers.end()); i != end; ++i)
|
|
|
|
(*i)->peer_rank = 0;
|
|
|
|
}
|
|
|
|
|
2007-07-26 09:04:35 +02:00
|
|
|
// disconnects and removes all peers that are now filtered
|
|
|
|
void policy::ip_filter_updated()
|
|
|
|
{
|
2009-08-02 00:48:43 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-07-26 09:04:35 +02:00
|
|
|
aux::session_impl& ses = m_torrent->session();
|
2011-02-27 18:26:57 +01:00
|
|
|
if (!m_torrent->apply_ip_filter()) return;
|
2009-05-11 22:23:47 +02:00
|
|
|
|
2008-10-17 09:32:46 +02:00
|
|
|
for (iterator i = m_peers.begin(); i != m_peers.end();)
|
2007-07-26 09:04:35 +02:00
|
|
|
{
|
2009-05-07 00:36:24 +02:00
|
|
|
if ((ses.m_ip_filter.access((*i)->address()) & ip_filter::blocked) == 0)
|
2007-07-26 09:04:35 +02:00
|
|
|
{
|
|
|
|
++i;
|
|
|
|
continue;
|
|
|
|
}
|
2012-11-21 21:42:40 +01:00
|
|
|
|
|
|
|
if (*i == m_locked_peer)
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
continue;
|
|
|
|
}
|
2007-07-26 09:04:35 +02:00
|
|
|
|
2011-04-28 09:32:42 +02:00
|
|
|
if (ses.m_alerts.should_post<peer_blocked_alert>())
|
2014-02-07 18:35:56 +01:00
|
|
|
ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle()
|
|
|
|
, (*i)->address(), peer_blocked_alert::ip_filter));
|
2011-04-28 09:32:42 +02:00
|
|
|
|
|
|
|
int current = i - m_peers.begin();
|
|
|
|
TORRENT_ASSERT(current >= 0);
|
|
|
|
TORRENT_ASSERT(m_peers.size() > 0);
|
|
|
|
TORRENT_ASSERT(i != m_peers.end());
|
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
if ((*i)->connection)
|
2007-07-26 09:04:35 +02:00
|
|
|
{
|
2011-04-28 09:32:42 +02:00
|
|
|
// disconnecting the peer here may also delete the
|
|
|
|
// peer_info_struct. If that is the case, just continue
|
|
|
|
int count = m_peers.size();
|
|
|
|
peer_connection* p = (*i)->connection;
|
|
|
|
|
|
|
|
p->disconnect(errors::banned_by_ip_filter);
|
|
|
|
// what *i refers to has changed, i.e. cur was deleted
|
|
|
|
if (m_peers.size() < count)
|
|
|
|
{
|
|
|
|
i = m_peers.begin() + current;
|
|
|
|
continue;
|
|
|
|
}
|
2009-05-07 00:36:24 +02:00
|
|
|
TORRENT_ASSERT((*i)->connection == 0
|
|
|
|
|| (*i)->connection->peer_info_struct() == 0);
|
2007-07-26 09:04:35 +02:00
|
|
|
}
|
2011-04-28 09:32:42 +02:00
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
erase_peer(i);
|
|
|
|
i = m_peers.begin() + current;
|
2007-07-26 09:04:35 +02:00
|
|
|
}
|
|
|
|
}
|
2008-04-24 09:49:23 +02:00
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
void policy::erase_peer(policy::peer* p)
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
std::pair<iterator, iterator> range = find_peers(p->address());
|
|
|
|
iterator iter = std::find_if(range.first, range.second, match_peer_endpoint(p->ip()));
|
|
|
|
if (iter == range.second) return;
|
|
|
|
erase_peer(iter);
|
|
|
|
}
|
|
|
|
|
2008-04-24 09:49:23 +02:00
|
|
|
// any peer that is erased from m_peers will be
|
|
|
|
// erased through this function. This way we can make
|
|
|
|
// sure that any references to the peer are removed
|
|
|
|
// as well, such as in the piece picker.
|
|
|
|
void policy::erase_peer(iterator i)
|
|
|
|
{
|
2009-05-03 08:57:04 +02:00
|
|
|
INVARIANT_CHECK;
|
2010-07-15 04:27:12 +02:00
|
|
|
TORRENT_ASSERT(i != m_peers.end());
|
2012-11-21 21:42:40 +01:00
|
|
|
TORRENT_ASSERT(m_locked_peer != *i);
|
2009-05-03 08:57:04 +02:00
|
|
|
|
2008-04-24 09:49:23 +02:00
|
|
|
if (m_torrent->has_picker())
|
2009-05-07 00:36:24 +02:00
|
|
|
m_torrent->picker().clear_peer(*i);
|
|
|
|
if ((*i)->seed) --m_num_seeds;
|
|
|
|
if (is_connect_candidate(**i, m_finished))
|
2009-06-18 18:16:41 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_num_connect_candidates > 0);
|
2008-04-24 09:49:23 +02:00
|
|
|
--m_num_connect_candidates;
|
2009-06-18 18:16:41 +02:00
|
|
|
}
|
2010-10-16 17:24:45 +02:00
|
|
|
TORRENT_ASSERT(m_num_connect_candidates < int(m_peers.size()));
|
2009-05-07 00:36:24 +02:00
|
|
|
if (m_round_robin > i - m_peers.begin()) --m_round_robin;
|
2010-10-16 17:24:45 +02:00
|
|
|
if (m_round_robin >= int(m_peers.size())) m_round_robin = 0;
|
2008-04-24 09:49:23 +02:00
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2010-07-18 21:28:22 +02:00
|
|
|
TORRENT_ASSERT((*i)->in_use);
|
|
|
|
(*i)->in_use = false;
|
|
|
|
#endif
|
|
|
|
|
2009-05-24 23:49:19 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
if ((*i)->is_v6_addr)
|
2010-07-15 04:27:12 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_torrent->session().m_ipv6_peer_pool.is_from(
|
|
|
|
static_cast<ipv6_peer*>(*i)));
|
2009-05-24 23:49:19 +02:00
|
|
|
m_torrent->session().m_ipv6_peer_pool.destroy(
|
|
|
|
static_cast<ipv6_peer*>(*i));
|
2010-07-15 04:27:12 +02:00
|
|
|
}
|
2009-05-24 23:49:19 +02:00
|
|
|
else
|
2009-08-20 05:19:12 +02:00
|
|
|
#endif
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
if ((*i)->is_i2p_addr)
|
2010-07-15 04:27:12 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_torrent->session().m_i2p_peer_pool.is_from(
|
|
|
|
static_cast<i2p_peer*>(*i)));
|
2009-08-20 05:19:12 +02:00
|
|
|
m_torrent->session().m_i2p_peer_pool.destroy(
|
|
|
|
static_cast<i2p_peer*>(*i));
|
2010-07-15 04:27:12 +02:00
|
|
|
}
|
2009-08-20 05:19:12 +02:00
|
|
|
else
|
2009-05-24 23:49:19 +02:00
|
|
|
#endif
|
2010-07-15 04:27:12 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_torrent->session().m_ipv4_peer_pool.is_from(
|
|
|
|
static_cast<ipv4_peer*>(*i)));
|
2009-05-24 23:49:19 +02:00
|
|
|
m_torrent->session().m_ipv4_peer_pool.destroy(
|
|
|
|
static_cast<ipv4_peer*>(*i));
|
2010-07-15 04:27:12 +02:00
|
|
|
}
|
2008-04-24 09:49:23 +02:00
|
|
|
m_peers.erase(i);
|
|
|
|
}
|
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
bool policy::should_erase_immediately(peer const& p) const
|
|
|
|
{
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p.in_use);
|
2012-11-21 21:42:40 +01:00
|
|
|
if (&p == m_locked_peer) return false;
|
2011-11-24 18:50:57 +01:00
|
|
|
return p.source == peer_info::resume_data;
|
2009-05-11 22:23:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool policy::is_erase_candidate(peer const& pe, bool finished) const
|
|
|
|
{
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(pe.in_use);
|
2012-11-21 21:42:40 +01:00
|
|
|
if (&pe == m_locked_peer) return false;
|
2011-11-24 18:50:57 +01:00
|
|
|
if (pe.connection) return false;
|
|
|
|
if (is_connect_candidate(pe, finished)) return false;
|
|
|
|
|
|
|
|
return (pe.failcount > 0)
|
|
|
|
|| (pe.source == peer_info::resume_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool policy::is_force_erase_candidate(peer const& pe) const
|
|
|
|
{
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(pe.in_use);
|
2012-11-21 21:42:40 +01:00
|
|
|
if (&pe == m_locked_peer) return false;
|
2011-11-24 18:50:57 +01:00
|
|
|
return pe.connection == 0;
|
2009-05-11 22:23:47 +02:00
|
|
|
}
|
|
|
|
|
2011-11-24 18:50:57 +01:00
|
|
|
void policy::erase_peers(int flags)
|
2009-05-11 22:23:47 +02:00
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2009-05-13 03:34:10 +02:00
|
|
|
int max_peerlist_size = m_torrent->is_paused()
|
2010-07-15 04:27:12 +02:00
|
|
|
? m_torrent->settings().max_paused_peerlist_size
|
|
|
|
: m_torrent->settings().max_peerlist_size;
|
2009-05-13 03:34:10 +02:00
|
|
|
|
|
|
|
if (max_peerlist_size == 0 || m_peers.empty()) return;
|
2009-05-11 22:23:47 +02:00
|
|
|
|
|
|
|
int erase_candidate = -1;
|
2011-11-24 18:50:57 +01:00
|
|
|
int force_erase_candidate = -1;
|
2009-05-11 22:23:47 +02:00
|
|
|
|
|
|
|
TORRENT_ASSERT(m_finished == m_torrent->is_finished());
|
|
|
|
|
2011-02-26 08:55:51 +01:00
|
|
|
int round_robin = random() % m_peers.size();
|
2009-05-11 22:23:47 +02:00
|
|
|
|
2011-11-24 18:50:57 +01:00
|
|
|
int low_watermark = max_peerlist_size * 95 / 100;
|
|
|
|
if (low_watermark == max_peerlist_size) --low_watermark;
|
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
for (int iterations = (std::min)(int(m_peers.size()), 300);
|
|
|
|
iterations > 0; --iterations)
|
|
|
|
{
|
2011-11-24 18:50:57 +01:00
|
|
|
if (int(m_peers.size()) < low_watermark)
|
2009-05-11 22:23:47 +02:00
|
|
|
break;
|
|
|
|
|
2010-10-16 17:24:45 +02:00
|
|
|
if (round_robin == int(m_peers.size())) round_robin = 0;
|
2009-05-11 22:23:47 +02:00
|
|
|
|
|
|
|
peer& pe = *m_peers[round_robin];
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(pe.in_use);
|
2009-05-11 22:23:47 +02:00
|
|
|
int current = round_robin;
|
|
|
|
|
2011-11-28 10:55:00 +01:00
|
|
|
if (is_erase_candidate(pe, m_finished)
|
|
|
|
&& (erase_candidate == -1
|
|
|
|
|| !compare_peer_erase(*m_peers[erase_candidate], pe)))
|
2009-05-11 22:23:47 +02:00
|
|
|
{
|
2011-11-28 10:55:00 +01:00
|
|
|
if (should_erase_immediately(pe))
|
2009-05-11 22:23:47 +02:00
|
|
|
{
|
2011-11-28 10:55:00 +01:00
|
|
|
if (erase_candidate > current) --erase_candidate;
|
|
|
|
if (force_erase_candidate > current) --force_erase_candidate;
|
|
|
|
TORRENT_ASSERT(current >= 0 && current < int(m_peers.size()));
|
|
|
|
erase_peer(m_peers.begin() + current);
|
|
|
|
continue;
|
2009-05-11 22:23:47 +02:00
|
|
|
}
|
2011-11-28 10:55:00 +01:00
|
|
|
else
|
2011-11-24 18:50:57 +01:00
|
|
|
{
|
2011-11-28 10:55:00 +01:00
|
|
|
erase_candidate = current;
|
2011-11-24 18:50:57 +01:00
|
|
|
}
|
2009-05-11 22:23:47 +02:00
|
|
|
}
|
2011-11-28 10:55:00 +01:00
|
|
|
if (is_force_erase_candidate(pe)
|
|
|
|
&& (force_erase_candidate == -1
|
|
|
|
|| !compare_peer_erase(*m_peers[force_erase_candidate], pe)))
|
|
|
|
{
|
|
|
|
force_erase_candidate = current;
|
|
|
|
}
|
2009-05-11 22:23:47 +02:00
|
|
|
|
|
|
|
++round_robin;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (erase_candidate > -1)
|
2010-07-15 04:27:12 +02:00
|
|
|
{
|
2010-10-16 17:24:45 +02:00
|
|
|
TORRENT_ASSERT(erase_candidate >= 0 && erase_candidate < int(m_peers.size()));
|
2009-05-11 22:23:47 +02:00
|
|
|
erase_peer(m_peers.begin() + erase_candidate);
|
2010-07-15 04:27:12 +02:00
|
|
|
}
|
2011-11-24 18:50:57 +01:00
|
|
|
else if ((flags & force_erase) && force_erase_candidate > -1)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(force_erase_candidate >= 0 && force_erase_candidate < int(m_peers.size()));
|
|
|
|
erase_peer(m_peers.begin() + force_erase_candidate);
|
|
|
|
}
|
2009-05-11 22:23:47 +02:00
|
|
|
}
|
|
|
|
|
2009-06-18 18:16:41 +02:00
|
|
|
void policy::ban_peer(policy::peer* p)
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2012-04-11 05:25:09 +02:00
|
|
|
|
|
|
|
if (!m_torrent->settings().ban_web_seeds && p->web_seed)
|
|
|
|
return;
|
|
|
|
|
2009-06-18 18:16:41 +02:00
|
|
|
if (is_connect_candidate(*p, m_finished))
|
|
|
|
--m_num_connect_candidates;
|
|
|
|
|
2011-03-17 06:31:06 +01:00
|
|
|
#ifdef TORRENT_STATS
|
|
|
|
aux::session_impl& ses = m_torrent->session();
|
|
|
|
++ses.m_num_banned_peers;
|
|
|
|
#endif
|
|
|
|
|
2009-06-18 18:16:41 +02:00
|
|
|
p->banned = true;
|
2009-08-02 00:48:43 +02:00
|
|
|
TORRENT_ASSERT(!is_connect_candidate(*p, m_finished));
|
|
|
|
}
|
|
|
|
|
|
|
|
void policy::set_connection(policy::peer* p, peer_connection* c)
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2009-08-02 00:48:43 +02:00
|
|
|
TORRENT_ASSERT(c);
|
|
|
|
|
|
|
|
const bool was_conn_cand = is_connect_candidate(*p, m_finished);
|
|
|
|
p->connection = c;
|
|
|
|
if (was_conn_cand) --m_num_connect_candidates;
|
|
|
|
}
|
|
|
|
|
|
|
|
void policy::set_failcount(policy::peer* p, int f)
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2009-08-02 00:48:43 +02:00
|
|
|
const bool was_conn_cand = is_connect_candidate(*p, m_finished);
|
|
|
|
p->failcount = f;
|
|
|
|
if (was_conn_cand != is_connect_candidate(*p, m_finished))
|
|
|
|
{
|
|
|
|
if (was_conn_cand) --m_num_connect_candidates;
|
|
|
|
else ++m_num_connect_candidates;
|
|
|
|
}
|
2009-06-18 18:16:41 +02:00
|
|
|
}
|
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
bool policy::is_connect_candidate(peer const& p, bool finished) const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p.in_use);
|
2008-03-29 19:47:24 +01:00
|
|
|
if (p.connection
|
|
|
|
|| p.banned
|
2012-03-24 02:29:31 +01:00
|
|
|
|| p.web_seed
|
2009-04-30 07:49:46 +02:00
|
|
|
|| !p.connectable
|
2008-03-29 19:47:24 +01:00
|
|
|
|| (p.seed && finished)
|
2011-02-21 06:24:41 +01:00
|
|
|
|| int(p.failcount) >= m_torrent->settings().max_failcount)
|
2008-03-29 19:47:24 +01:00
|
|
|
return false;
|
2005-04-05 02:54:33 +02:00
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
aux::session_impl const& ses = m_torrent->session();
|
2008-07-14 13:15:35 +02:00
|
|
|
if (ses.m_port_filter.access(p.port) & port_filter::blocked)
|
2008-03-29 19:47:24 +01:00
|
|
|
return false;
|
2010-12-31 01:33:23 +01:00
|
|
|
|
2011-01-08 10:04:31 +01:00
|
|
|
// only apply this to peers we've only heard
|
|
|
|
// about from the DHT
|
|
|
|
if (ses.m_settings.no_connect_privileged_ports
|
|
|
|
&& p.port < 1024
|
|
|
|
&& p.source == peer_info::dht)
|
2010-12-31 01:33:23 +01:00
|
|
|
return false;
|
|
|
|
|
2008-03-29 19:47:24 +01:00
|
|
|
return true;
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
policy::iterator policy::find_connect_candidate(int session_time)
|
2004-01-14 17:18:53 +01:00
|
|
|
{
|
2009-05-03 05:41:33 +02:00
|
|
|
INVARIANT_CHECK;
|
2007-04-13 19:47:40 +02:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
int candidate = -1;
|
2009-05-11 22:23:47 +02:00
|
|
|
int erase_candidate = -1;
|
2009-05-07 00:36:24 +02:00
|
|
|
|
|
|
|
TORRENT_ASSERT(m_finished == m_torrent->is_finished());
|
2007-04-14 23:47:07 +02:00
|
|
|
|
|
|
|
int min_reconnect_time = m_torrent->settings().min_reconnect_time;
|
2013-01-02 00:12:16 +01:00
|
|
|
external_ip const& external = m_torrent->session().external_address();
|
|
|
|
int external_port = m_torrent->session().listen_port();
|
2004-01-14 17:18:53 +01:00
|
|
|
|
2010-10-16 17:24:45 +02:00
|
|
|
if (m_round_robin >= int(m_peers.size())) m_round_robin = 0;
|
2008-04-05 06:53:22 +02:00
|
|
|
|
2008-05-28 20:25:48 +02:00
|
|
|
#ifndef TORRENT_DISABLE_DHT
|
|
|
|
bool pinged = false;
|
|
|
|
#endif
|
|
|
|
|
2009-05-13 03:34:10 +02:00
|
|
|
int max_peerlist_size = m_torrent->is_paused()
|
|
|
|
?m_torrent->settings().max_paused_peerlist_size
|
|
|
|
:m_torrent->settings().max_peerlist_size;
|
|
|
|
|
2008-04-24 09:49:23 +02:00
|
|
|
for (int iterations = (std::min)(int(m_peers.size()), 300);
|
2008-05-28 20:25:48 +02:00
|
|
|
iterations > 0; --iterations)
|
2004-01-14 17:18:53 +01:00
|
|
|
{
|
2010-06-28 03:30:59 +02:00
|
|
|
if (m_round_robin >= int(m_peers.size())) m_round_robin = 0;
|
2007-12-24 09:17:32 +01:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
peer& pe = *m_peers[m_round_robin];
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(pe.in_use);
|
2009-05-07 00:36:24 +02:00
|
|
|
int current = m_round_robin;
|
2008-05-28 20:25:48 +02:00
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_DHT
|
|
|
|
// try to send a DHT ping to this peer
|
|
|
|
// as well, to figure out if it supports
|
|
|
|
// DHT (uTorrent and BitComet doesn't
|
|
|
|
// advertise support)
|
|
|
|
if (!pinged && !pe.added_to_dht)
|
|
|
|
{
|
2009-04-30 07:49:46 +02:00
|
|
|
udp::endpoint node(pe.address(), pe.port);
|
2008-05-28 20:25:48 +02:00
|
|
|
m_torrent->session().add_dht_node(node);
|
|
|
|
pe.added_to_dht = true;
|
|
|
|
pinged = true;
|
|
|
|
}
|
|
|
|
#endif
|
2008-06-13 06:50:02 +02:00
|
|
|
// if the number of peers is growing large
|
|
|
|
// we need to start weeding.
|
2009-05-11 22:23:47 +02:00
|
|
|
|
2010-10-16 17:24:45 +02:00
|
|
|
if (int(m_peers.size()) >= max_peerlist_size * 0.95
|
2009-05-13 03:34:10 +02:00
|
|
|
&& max_peerlist_size > 0)
|
2008-05-28 20:25:48 +02:00
|
|
|
{
|
2009-05-11 22:23:47 +02:00
|
|
|
if (is_erase_candidate(pe, m_finished)
|
|
|
|
&& (erase_candidate == -1
|
|
|
|
|| !compare_peer_erase(*m_peers[erase_candidate], pe)))
|
|
|
|
{
|
|
|
|
if (should_erase_immediately(pe))
|
|
|
|
{
|
|
|
|
if (erase_candidate > current) --erase_candidate;
|
|
|
|
if (candidate > current) --candidate;
|
|
|
|
erase_peer(m_peers.begin() + current);
|
2012-04-09 05:04:57 +02:00
|
|
|
continue;
|
2009-05-11 22:23:47 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
erase_candidate = current;
|
|
|
|
}
|
|
|
|
}
|
2008-05-28 20:25:48 +02:00
|
|
|
}
|
2008-05-30 09:58:49 +02:00
|
|
|
|
2008-05-28 20:25:48 +02:00
|
|
|
++m_round_robin;
|
|
|
|
|
2009-05-06 09:06:26 +02:00
|
|
|
if (!is_connect_candidate(pe, m_finished)) continue;
|
2004-01-14 17:22:49 +01:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
// compare peer returns true if lhs is better than rhs. In this
|
|
|
|
// case, it returns true if the current candidate is better than
|
|
|
|
// pe, which is the peer m_round_robin points to. If it is, just
|
|
|
|
// keep looking.
|
|
|
|
if (candidate != -1
|
2013-01-02 00:12:16 +01:00
|
|
|
&& compare_peer(*m_peers[candidate], pe, external, external_port)) continue;
|
2008-02-28 04:09:34 +01:00
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
if (pe.last_connected
|
|
|
|
&& session_time - pe.last_connected <
|
|
|
|
(int(pe.failcount) + 1) * min_reconnect_time)
|
2008-04-24 09:49:23 +02:00
|
|
|
continue;
|
2008-04-05 06:53:22 +02:00
|
|
|
|
2008-05-28 20:25:48 +02:00
|
|
|
candidate = current;
|
2004-01-14 17:18:53 +01:00
|
|
|
}
|
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
if (erase_candidate > -1)
|
|
|
|
{
|
|
|
|
if (candidate > erase_candidate) --candidate;
|
|
|
|
erase_peer(m_peers.begin() + erase_candidate);
|
|
|
|
}
|
|
|
|
|
2008-02-28 04:09:34 +01:00
|
|
|
#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING
|
2009-05-07 18:30:14 +02:00
|
|
|
if (candidate != -1)
|
2008-02-28 04:09:34 +01:00
|
|
|
{
|
2008-02-28 08:34:07 +01:00
|
|
|
(*m_torrent->session().m_logger) << time_now_string()
|
|
|
|
<< " *** FOUND CONNECTION CANDIDATE ["
|
2009-05-07 18:30:14 +02:00
|
|
|
" ip: " << m_peers[candidate]->ip() <<
|
2013-01-06 20:53:17 +01:00
|
|
|
" d: " << cidr_distance(external.external_address(m_peers[candidate]->address()), m_peers[candidate]->address()) <<
|
2013-01-02 00:12:16 +01:00
|
|
|
" rank: " << m_peers[candidate]->rank(external, external_port) <<
|
|
|
|
" external: " << external.external_address(m_peers[candidate]->address()) <<
|
2009-05-07 18:30:14 +02:00
|
|
|
" t: " << (session_time - m_peers[candidate]->last_connected) <<
|
2008-02-28 04:09:34 +01:00
|
|
|
" ]\n";
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
if (candidate == -1) return m_peers.end();
|
|
|
|
return m_peers.begin() + candidate;
|
2004-01-14 17:18:53 +01:00
|
|
|
}
|
2004-05-14 01:34:42 +02:00
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
bool policy::new_connection(peer_connection& c, int session_time)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2012-02-07 04:46:21 +01:00
|
|
|
TORRENT_ASSERT(!c.is_outgoing());
|
2006-11-14 16:53:38 +01:00
|
|
|
|
2009-05-03 05:41:33 +02:00
|
|
|
INVARIANT_CHECK;
|
2006-11-14 16:53:38 +01:00
|
|
|
|
2004-03-01 01:50:00 +01:00
|
|
|
// if the connection comes from the tracker,
|
|
|
|
// it's probably just a NAT-check. Ignore the
|
|
|
|
// num connections constraint then.
|
2004-09-16 03:14:16 +02:00
|
|
|
|
2004-03-28 19:45:37 +02:00
|
|
|
// TODO: only allow _one_ connection to use this
|
|
|
|
// override at a time
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2008-01-07 02:10:46 +01:00
|
|
|
TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec);
|
2009-05-13 03:34:10 +02:00
|
|
|
TORRENT_ASSERT(!m_torrent->is_paused());
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2008-02-28 04:09:34 +01:00
|
|
|
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
2006-04-25 23:04:48 +02:00
|
|
|
if (c.remote().address() == m_torrent->current_tracker().address())
|
2004-03-01 01:50:00 +01:00
|
|
|
{
|
|
|
|
m_torrent->debug_log("overriding connection limit for tracker NAT-check");
|
|
|
|
}
|
|
|
|
#endif
|
2004-01-21 01:59:38 +01:00
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
iterator iter;
|
|
|
|
peer* i = 0;
|
2004-01-15 02:29:43 +01:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
bool found = false;
|
2006-11-14 16:53:38 +01:00
|
|
|
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
|
|
|
{
|
2007-10-31 10:48:20 +01:00
|
|
|
tcp::endpoint remote = c.remote();
|
2009-05-07 00:36:24 +02:00
|
|
|
std::pair<iterator, iterator> range = find_peers(remote.address());
|
2009-04-30 07:49:46 +02:00
|
|
|
iter = std::find_if(range.first, range.second, match_peer_endpoint(remote));
|
2007-10-31 10:48:20 +01:00
|
|
|
|
2012-11-21 21:42:40 +01:00
|
|
|
if (iter != range.second)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT((*iter)->in_use);
|
|
|
|
found = true;
|
|
|
|
}
|
2006-11-14 16:53:38 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-05-25 12:15:35 +02:00
|
|
|
iter = std::lower_bound(
|
|
|
|
m_peers.begin(), m_peers.end()
|
2009-05-25 19:23:03 +02:00
|
|
|
, c.remote().address(), peer_address_compare()
|
2009-05-25 12:15:35 +02:00
|
|
|
);
|
|
|
|
|
2012-11-21 21:42:40 +01:00
|
|
|
if (iter != m_peers.end() && (*iter)->address() == c.remote().address())
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT((*iter)->in_use);
|
|
|
|
found = true;
|
|
|
|
}
|
2006-11-14 16:53:38 +01:00
|
|
|
}
|
2006-09-28 15:27:34 +02:00
|
|
|
|
2011-05-16 18:37:37 +02:00
|
|
|
// make sure the iterator we got is properly sorted relative
|
|
|
|
// to the connection's address
|
|
|
|
// TORRENT_ASSERT(m_peers.empty()
|
|
|
|
// || (iter == m_peers.end() && (*(iter-1))->address() < c.remote().address())
|
|
|
|
// || (iter != m_peers.end() && c.remote().address() < (*iter)->address())
|
|
|
|
// || (iter != m_peers.end() && iter != m_peers.begin() && (*(iter-1))->address() < c.remote().address()));
|
|
|
|
|
2013-05-10 06:47:56 +02:00
|
|
|
#if !defined TORRENT_DISABLE_GEO_IP || TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING
|
2013-01-15 06:33:24 +01:00
|
|
|
aux::session_impl& ses = m_torrent->session();
|
2013-05-10 06:47:56 +02:00
|
|
|
#endif
|
2013-01-15 06:33:24 +01:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
if (found)
|
2006-09-28 15:27:34 +02:00
|
|
|
{
|
2009-05-07 00:36:24 +02:00
|
|
|
i = *iter;
|
2012-11-21 21:42:40 +01:00
|
|
|
TORRENT_ASSERT(i->in_use);
|
2010-12-30 04:46:11 +01:00
|
|
|
TORRENT_ASSERT(i->connection != &c);
|
2013-08-15 07:29:05 +02:00
|
|
|
TORRENT_ASSERT(i->address() == c.remote().address());
|
2009-04-30 07:49:46 +02:00
|
|
|
|
2013-08-15 07:29:05 +02:00
|
|
|
#ifdef TORRENT_VERBOSE_LOGGING
|
|
|
|
c.peer_log("*** DUPLICATE PEER [ this: \"%s\" that: \"%s\" ]"
|
|
|
|
, print_address(c.remote().address()).c_str()
|
|
|
|
, print_address(i->address()).c_str());
|
|
|
|
#endif
|
2009-04-30 07:49:46 +02:00
|
|
|
if (i->banned)
|
2008-01-07 02:10:46 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
c.disconnect(errors::peer_banned);
|
2008-01-07 02:10:46 +01:00
|
|
|
return false;
|
|
|
|
}
|
2006-09-28 15:27:34 +02:00
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
if (i->connection != 0)
|
2006-09-28 15:27:34 +02:00
|
|
|
{
|
2008-06-16 15:54:14 +02:00
|
|
|
boost::shared_ptr<socket_type> other_socket
|
2009-04-30 07:49:46 +02:00
|
|
|
= i->connection->get_socket();
|
2008-06-16 15:54:14 +02:00
|
|
|
boost::shared_ptr<socket_type> this_socket
|
|
|
|
= c.get_socket();
|
|
|
|
|
|
|
|
error_code ec1;
|
|
|
|
error_code ec2;
|
|
|
|
bool self_connection =
|
|
|
|
other_socket->remote_endpoint(ec2) == this_socket->local_endpoint(ec1)
|
|
|
|
|| other_socket->local_endpoint(ec2) == this_socket->remote_endpoint(ec1);
|
|
|
|
|
|
|
|
if (ec1)
|
|
|
|
{
|
2009-06-12 18:40:38 +02:00
|
|
|
c.disconnect(ec1);
|
2008-06-16 15:54:14 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self_connection)
|
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
c.disconnect(errors::self_connection, 1);
|
|
|
|
i->connection->disconnect(errors::self_connection, 1);
|
2010-03-08 09:03:53 +01:00
|
|
|
TORRENT_ASSERT(i->connection == 0);
|
2008-06-16 15:54:14 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
TORRENT_ASSERT(i->connection != &c);
|
2006-09-28 15:27:34 +02:00
|
|
|
// the new connection is a local (outgoing) connection
|
|
|
|
// or the current one is already connected
|
2008-06-18 15:31:41 +02:00
|
|
|
if (ec2)
|
|
|
|
{
|
2012-11-21 21:42:40 +01:00
|
|
|
TORRENT_ASSERT(m_locked_peer == NULL);
|
|
|
|
m_locked_peer = i;
|
2009-06-12 18:40:38 +02:00
|
|
|
i->connection->disconnect(ec2);
|
2010-03-08 09:03:53 +01:00
|
|
|
TORRENT_ASSERT(i->connection == 0);
|
2012-11-21 21:42:40 +01:00
|
|
|
m_locked_peer = NULL;
|
2008-06-18 15:31:41 +02:00
|
|
|
}
|
2012-10-10 06:40:18 +02:00
|
|
|
else if (i->connection->is_outgoing() == c.is_outgoing())
|
2006-09-28 15:27:34 +02:00
|
|
|
{
|
2012-10-10 06:40:18 +02:00
|
|
|
// if the other end connected to us both times, just drop
|
|
|
|
// the second one. Or if we made both connections.
|
2009-11-29 08:06:38 +01:00
|
|
|
c.disconnect(errors::duplicate_peer_id);
|
2008-01-07 02:10:46 +01:00
|
|
|
return false;
|
2006-09-28 15:27:34 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-10-10 06:40:18 +02:00
|
|
|
// at this point, we need to disconnect either
|
|
|
|
// i->connection or c. In order for both this client
|
|
|
|
// and the client on the other end to decide to
|
|
|
|
// disconnect the same one, we need a consistent rule to
|
|
|
|
// select which one.
|
|
|
|
|
|
|
|
bool outgoing1 = c.is_outgoing();
|
|
|
|
|
|
|
|
// for this, we compare our endpoints (IP and port)
|
|
|
|
// and whoever has the lower IP,port should be the
|
|
|
|
// one keeping its outgoing connection. Since outgoing
|
|
|
|
// ports are selected at random by the OS, we need
|
|
|
|
// to be careful to only look at the target end of a
|
|
|
|
// connection for the endpoint.
|
|
|
|
|
2013-08-15 07:29:05 +02:00
|
|
|
int our_port = outgoing1 ? other_socket->local_endpoint(ec1).port() : this_socket->local_endpoint(ec1).port();
|
|
|
|
int other_port = outgoing1 ? this_socket->remote_endpoint(ec1).port() : other_socket->remote_endpoint(ec1).port();
|
2012-10-10 06:40:18 +02:00
|
|
|
|
2013-08-15 07:29:05 +02:00
|
|
|
if (our_port < other_port)
|
2012-10-10 06:40:18 +02:00
|
|
|
{
|
|
|
|
#ifdef TORRENT_VERBOSE_LOGGING
|
2013-08-15 07:29:05 +02:00
|
|
|
c.peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" < \"%d\" ]", our_port, other_port);
|
|
|
|
i->connection->peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" < \"%d\" ]", our_port, other_port);
|
2006-09-28 15:27:34 +02:00
|
|
|
#endif
|
2012-10-10 06:40:18 +02:00
|
|
|
|
|
|
|
// we should keep our outgoing connection
|
|
|
|
if (!outgoing1)
|
|
|
|
{
|
|
|
|
c.disconnect(errors::duplicate_peer_id);
|
|
|
|
return false;
|
|
|
|
}
|
2012-11-21 21:42:40 +01:00
|
|
|
TORRENT_ASSERT(m_locked_peer == NULL);
|
|
|
|
m_locked_peer = i;
|
2012-10-10 06:40:18 +02:00
|
|
|
i->connection->disconnect(errors::duplicate_peer_id);
|
2012-11-21 21:42:40 +01:00
|
|
|
m_locked_peer = NULL;
|
2012-10-10 06:40:18 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifdef TORRENT_VERBOSE_LOGGING
|
2013-08-15 07:29:05 +02:00
|
|
|
c.peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" >= \"%d\" ]", our_port, other_port);
|
|
|
|
i->connection->peer_log("*** DUPLICATE PEER RESOLUTION [ \"%d\" >= \"%d\" ]", our_port, other_port);
|
2012-10-10 06:40:18 +02:00
|
|
|
#endif
|
|
|
|
// they should keep their outgoing connection
|
|
|
|
if (outgoing1)
|
|
|
|
{
|
|
|
|
c.disconnect(errors::duplicate_peer_id);
|
|
|
|
return false;
|
|
|
|
}
|
2012-11-21 21:42:40 +01:00
|
|
|
TORRENT_ASSERT(m_locked_peer == NULL);
|
|
|
|
m_locked_peer = i;
|
2012-10-10 06:40:18 +02:00
|
|
|
i->connection->disconnect(errors::duplicate_peer_id);
|
2012-11-21 21:42:40 +01:00
|
|
|
m_locked_peer = NULL;
|
2012-10-10 06:40:18 +02:00
|
|
|
}
|
2006-09-28 15:27:34 +02:00
|
|
|
}
|
|
|
|
}
|
2009-06-26 07:58:24 +02:00
|
|
|
|
|
|
|
if (is_connect_candidate(*i, m_finished))
|
|
|
|
{
|
|
|
|
m_num_connect_candidates--;
|
|
|
|
TORRENT_ASSERT(m_num_connect_candidates >= 0);
|
|
|
|
if (m_num_connect_candidates < 0) m_num_connect_candidates = 0;
|
|
|
|
}
|
2006-09-28 15:27:34 +02:00
|
|
|
}
|
|
|
|
else
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2007-07-10 19:25:10 +02:00
|
|
|
// we don't have any info about this peer.
|
2003-12-01 06:01:40 +01:00
|
|
|
// add a new entry
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2008-01-07 02:10:46 +01:00
|
|
|
TORRENT_ASSERT(c.remote() == c.get_socket()->remote_endpoint(ec) || ec);
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2008-06-07 16:03:21 +02:00
|
|
|
if (int(m_peers.size()) >= m_torrent->settings().max_peerlist_size)
|
2008-05-28 20:25:48 +02:00
|
|
|
{
|
2011-05-16 18:37:37 +02:00
|
|
|
// this may invalidate our iterator!
|
2011-11-24 18:50:57 +01:00
|
|
|
erase_peers(force_erase);
|
2011-05-01 20:37:49 +02:00
|
|
|
if (int(m_peers.size()) >= m_torrent->settings().max_peerlist_size)
|
|
|
|
{
|
2011-11-24 18:50:57 +01:00
|
|
|
#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING
|
|
|
|
(*m_torrent->session().m_logger) << time_now_string()
|
|
|
|
<< " *** TOO MANY CONNECTIONS ["
|
|
|
|
" torrent: " << m_torrent->name() <<
|
|
|
|
" torrent peers: " << m_torrent->num_peers() <<
|
|
|
|
" torrent limit: " << m_torrent->max_connections() <<
|
|
|
|
" global peers: " << ses.num_connections() <<
|
|
|
|
" global limit: " << ses.settings().connections_limit <<
|
|
|
|
" global list peers " << int(m_peers.size()) <<
|
|
|
|
" global list limit: " << m_torrent->settings().max_peerlist_size <<
|
|
|
|
" ]\n";
|
|
|
|
#endif
|
2011-05-01 20:37:49 +02:00
|
|
|
c.disconnect(errors::too_many_connections);
|
|
|
|
return false;
|
|
|
|
}
|
2011-05-16 18:37:37 +02:00
|
|
|
// restore it
|
|
|
|
iter = std::lower_bound(
|
|
|
|
m_peers.begin(), m_peers.end()
|
|
|
|
, c.remote().address(), peer_address_compare()
|
|
|
|
);
|
2008-05-28 20:25:48 +02:00
|
|
|
}
|
|
|
|
|
2009-05-24 23:49:19 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
bool is_v6 = c.remote().address().is_v6();
|
|
|
|
#endif
|
|
|
|
peer* p =
|
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
is_v6 ? (peer*)m_torrent->session().m_ipv6_peer_pool.malloc() :
|
|
|
|
#endif
|
|
|
|
(peer*)m_torrent->session().m_ipv4_peer_pool.malloc();
|
2009-05-11 22:23:47 +02:00
|
|
|
if (p == 0) return false;
|
2010-07-18 21:28:22 +02:00
|
|
|
|
2009-05-24 23:49:19 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
if (is_v6)
|
|
|
|
m_torrent->session().m_ipv6_peer_pool.set_next_size(500);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
m_torrent->session().m_ipv4_peer_pool.set_next_size(500);
|
|
|
|
|
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
if (is_v6)
|
|
|
|
new (p) ipv6_peer(c.remote(), false, 0);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
new (p) ipv4_peer(c.remote(), false, 0);
|
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2010-07-18 21:28:22 +02:00
|
|
|
p->in_use = true;
|
|
|
|
#endif
|
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
iter = m_peers.insert(iter, p);
|
|
|
|
|
2010-06-28 03:30:59 +02:00
|
|
|
if (m_round_robin >= iter - m_peers.begin()) ++m_round_robin;
|
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
i = *iter;
|
2008-04-05 06:53:22 +02:00
|
|
|
#ifndef TORRENT_DISABLE_GEO_IP
|
|
|
|
int as = ses.as_for_ip(c.remote().address());
|
2008-11-29 22:33:21 +01:00
|
|
|
#ifdef TORRENT_DEBUG
|
2009-04-30 07:49:46 +02:00
|
|
|
i->inet_as_num = as;
|
2008-04-05 06:53:22 +02:00
|
|
|
#endif
|
2009-04-30 07:49:46 +02:00
|
|
|
i->inet_as = ses.lookup_as(as);
|
2008-04-05 06:53:22 +02:00
|
|
|
#endif
|
2009-05-16 01:22:11 +02:00
|
|
|
i->source = peer_info::incoming;
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
2007-07-10 19:25:10 +02:00
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
TORRENT_ASSERT(i);
|
|
|
|
c.set_peer_info(i);
|
|
|
|
TORRENT_ASSERT(i->connection == 0);
|
2011-06-01 08:47:57 +02:00
|
|
|
c.add_stat(size_type(i->prev_amount_download) << 10, size_type(i->prev_amount_upload) << 10);
|
2010-01-15 04:06:05 +01:00
|
|
|
|
|
|
|
// restore transfer rate limits
|
|
|
|
int rate_limit;
|
|
|
|
rate_limit = i->upload_rate_limit;
|
|
|
|
if (rate_limit) c.set_upload_limit(rate_limit);
|
|
|
|
rate_limit = i->download_rate_limit;
|
|
|
|
if (rate_limit) c.set_download_limit(rate_limit);
|
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
i->prev_amount_download = 0;
|
|
|
|
i->prev_amount_upload = 0;
|
|
|
|
i->connection = &c;
|
|
|
|
TORRENT_ASSERT(i->connection);
|
2007-10-15 07:03:29 +02:00
|
|
|
if (!c.fast_reconnect())
|
2009-04-30 07:49:46 +02:00
|
|
|
i->last_connected = session_time;
|
2009-06-18 18:16:41 +02:00
|
|
|
|
2009-06-26 07:58:24 +02:00
|
|
|
// this cannot be a connect candidate anymore, since i->connection is set
|
|
|
|
TORRENT_ASSERT(!is_connect_candidate(*i, m_finished));
|
2010-03-08 09:03:53 +01:00
|
|
|
TORRENT_ASSERT(has_connection(&c));
|
2011-11-15 03:34:00 +01:00
|
|
|
m_torrent->state_updated();
|
2008-01-07 02:10:46 +01:00
|
|
|
return true;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2008-01-07 02:10:46 +01:00
|
|
|
bool policy::update_peer_port(int port, policy::peer* p, int src)
|
2007-10-31 10:48:20 +01:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(p != 0);
|
2008-01-07 02:10:46 +01:00
|
|
|
TORRENT_ASSERT(p->connection);
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2008-01-07 02:10:46 +01:00
|
|
|
|
2009-06-18 18:16:41 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2008-07-14 13:15:35 +02:00
|
|
|
if (p->port == port) return true;
|
2007-10-31 10:48:20 +01:00
|
|
|
|
|
|
|
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
|
|
|
{
|
2009-04-30 07:49:46 +02:00
|
|
|
tcp::endpoint remote(p->address(), port);
|
2009-05-07 00:36:24 +02:00
|
|
|
std::pair<iterator, iterator> range = find_peers(remote.address());
|
2007-10-31 10:48:20 +01:00
|
|
|
iterator i = std::find_if(range.first, range.second
|
|
|
|
, match_peer_endpoint(remote));
|
2010-11-26 21:48:04 +01:00
|
|
|
if (i != range.second)
|
2007-10-31 10:48:20 +01:00
|
|
|
{
|
2009-05-07 00:36:24 +02:00
|
|
|
policy::peer& pp = **i;
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(pp.in_use);
|
2007-10-31 10:48:20 +01:00
|
|
|
if (pp.connection)
|
|
|
|
{
|
2009-06-18 18:16:41 +02:00
|
|
|
bool was_conn_cand = is_connect_candidate(pp, m_finished);
|
|
|
|
// if we already have an entry with this
|
|
|
|
// new endpoint, disconnect this one
|
|
|
|
pp.connectable = true;
|
|
|
|
pp.source |= src;
|
|
|
|
if (!was_conn_cand && is_connect_candidate(pp, m_finished))
|
|
|
|
++m_num_connect_candidates;
|
2012-04-21 05:49:27 +02:00
|
|
|
// calling disconnect() on a peer, may actually end
|
|
|
|
// up "garbage collecting" its policy::peer entry
|
|
|
|
// as well, if it's considered useless (which this specific)
|
|
|
|
// case will, since it was an incoming peer that just disconnected
|
|
|
|
// and we allow multiple connections per IP. Because of that,
|
|
|
|
// we need to make sure we don't let it do that, by unlinking
|
|
|
|
// the peer_connection from the policy::peer first.
|
|
|
|
p->connection->set_peer_info(0);
|
2012-11-21 21:42:40 +01:00
|
|
|
TORRENT_ASSERT(m_locked_peer == NULL);
|
|
|
|
m_locked_peer = p;
|
2009-11-29 08:06:38 +01:00
|
|
|
p->connection->disconnect(errors::duplicate_peer_id);
|
2012-11-21 21:42:40 +01:00
|
|
|
m_locked_peer = NULL;
|
2009-06-18 18:16:41 +02:00
|
|
|
erase_peer(p);
|
2008-01-07 02:10:46 +01:00
|
|
|
return false;
|
2007-10-31 10:48:20 +01:00
|
|
|
}
|
2008-04-24 09:49:23 +02:00
|
|
|
erase_peer(i);
|
2007-10-31 10:48:20 +01:00
|
|
|
}
|
|
|
|
}
|
2009-05-07 00:36:24 +02:00
|
|
|
#ifdef TORRENT_DEBUG
|
2007-10-31 10:48:20 +01:00
|
|
|
else
|
|
|
|
{
|
2013-11-02 04:35:45 +01:00
|
|
|
#if TORRENT_USE_I2P
|
2013-10-27 20:56:37 +01:00
|
|
|
if (!p->is_i2p_addr)
|
2013-11-02 04:35:45 +01:00
|
|
|
#endif
|
2013-10-27 20:56:37 +01:00
|
|
|
{
|
|
|
|
std::pair<iterator, iterator> range = find_peers(p->address());
|
|
|
|
TORRENT_ASSERT(range.second - range.first == 1);
|
|
|
|
}
|
2007-10-31 10:48:20 +01:00
|
|
|
}
|
2009-05-07 00:36:24 +02:00
|
|
|
#endif
|
|
|
|
|
2009-05-06 09:06:26 +02:00
|
|
|
bool was_conn_cand = is_connect_candidate(*p, m_finished);
|
2008-07-14 13:15:35 +02:00
|
|
|
p->port = port;
|
2007-10-31 10:48:20 +01:00
|
|
|
p->source |= src;
|
2009-06-09 18:33:10 +02:00
|
|
|
p->connectable = true;
|
2008-04-20 02:19:31 +02:00
|
|
|
|
2009-05-06 09:06:26 +02:00
|
|
|
if (was_conn_cand != is_connect_candidate(*p, m_finished))
|
2008-04-20 02:19:31 +02:00
|
|
|
{
|
|
|
|
m_num_connect_candidates += was_conn_cand ? -1 : 1;
|
2009-06-18 18:16:41 +02:00
|
|
|
TORRENT_ASSERT(m_num_connect_candidates >= 0);
|
2008-04-20 02:19:31 +02:00
|
|
|
if (m_num_connect_candidates < 0) m_num_connect_candidates = 0;
|
|
|
|
}
|
2008-01-07 02:10:46 +01:00
|
|
|
return true;
|
2007-10-31 10:48:20 +01:00
|
|
|
}
|
|
|
|
|
2009-07-28 01:34:50 +02:00
|
|
|
// it's important that we don't dereference
|
|
|
|
// p here, since it is allowed to be a dangling
|
|
|
|
// pointer. see smart_ban.cpp
|
2007-12-27 21:57:58 +01:00
|
|
|
bool policy::has_peer(policy::peer const* p) const
|
|
|
|
{
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2007-12-27 21:57:58 +01:00
|
|
|
// find p in m_peers
|
2009-04-30 07:49:46 +02:00
|
|
|
for (const_iterator i = m_peers.begin()
|
2007-12-27 21:57:58 +01:00
|
|
|
, end(m_peers.end()); i != end; ++i)
|
|
|
|
{
|
2009-05-07 00:36:24 +02:00
|
|
|
if (*i == p) return true;
|
2007-12-27 21:57:58 +01:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-06-13 06:14:41 +02:00
|
|
|
void policy::set_seed(policy::peer* p, bool s)
|
|
|
|
{
|
|
|
|
if (p == 0) return;
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2009-06-13 06:14:41 +02:00
|
|
|
if (p->seed == s) return;
|
2009-06-18 18:16:41 +02:00
|
|
|
bool was_conn_cand = is_connect_candidate(*p, m_finished);
|
2009-06-13 06:14:41 +02:00
|
|
|
p->seed = s;
|
2009-06-18 18:16:41 +02:00
|
|
|
if (was_conn_cand && !is_connect_candidate(*p, m_finished))
|
|
|
|
{
|
|
|
|
--m_num_connect_candidates;
|
2009-08-01 23:16:11 +02:00
|
|
|
TORRENT_ASSERT(m_num_connect_candidates >= 0);
|
2009-06-18 18:16:41 +02:00
|
|
|
if (m_num_connect_candidates < 0) m_num_connect_candidates = 0;
|
|
|
|
}
|
|
|
|
|
2012-03-24 02:29:31 +01:00
|
|
|
if (p->web_seed) return;
|
2009-06-13 06:14:41 +02:00
|
|
|
if (s) ++m_num_seeds;
|
|
|
|
else --m_num_seeds;
|
2009-08-01 23:16:11 +02:00
|
|
|
TORRENT_ASSERT(m_num_seeds >= 0);
|
2010-10-16 17:24:45 +02:00
|
|
|
TORRENT_ASSERT(m_num_seeds <= int(m_peers.size()));
|
2009-06-13 06:14:41 +02:00
|
|
|
}
|
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
bool policy::insert_peer(policy::peer* p, iterator iter, int flags)
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(p);
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2009-08-20 05:19:12 +02:00
|
|
|
|
|
|
|
int max_peerlist_size = m_torrent->is_paused()
|
|
|
|
?m_torrent->settings().max_paused_peerlist_size
|
|
|
|
:m_torrent->settings().max_peerlist_size;
|
|
|
|
|
|
|
|
if (max_peerlist_size
|
|
|
|
&& int(m_peers.size()) >= max_peerlist_size)
|
|
|
|
{
|
|
|
|
if (p->source == peer_info::resume_data) return false;
|
|
|
|
|
|
|
|
erase_peers();
|
|
|
|
if (int(m_peers.size()) >= max_peerlist_size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// since some peers were removed, we need to
|
|
|
|
// update the iterator to make it valid again
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
if (p->is_i2p_addr)
|
|
|
|
{
|
|
|
|
iter = std::lower_bound(
|
|
|
|
m_peers.begin(), m_peers.end()
|
|
|
|
, p->dest(), peer_address_compare());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
iter = std::lower_bound(
|
|
|
|
m_peers.begin(), m_peers.end()
|
|
|
|
, p->address(), peer_address_compare());
|
|
|
|
}
|
|
|
|
|
|
|
|
iter = m_peers.insert(iter, p);
|
|
|
|
|
2010-06-28 03:30:59 +02:00
|
|
|
if (m_round_robin >= iter - m_peers.begin()) ++m_round_robin;
|
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
#ifndef TORRENT_DISABLE_ENCRYPTION
|
|
|
|
if (flags & 0x01) p->pe_support = true;
|
|
|
|
#endif
|
|
|
|
if (flags & 0x02)
|
|
|
|
{
|
|
|
|
p->seed = true;
|
|
|
|
++m_num_seeds;
|
|
|
|
}
|
2010-11-29 02:33:05 +01:00
|
|
|
if (flags & 0x04)
|
|
|
|
p->supports_utp = true;
|
|
|
|
if (flags & 0x08)
|
|
|
|
p->supports_holepunch = true;
|
2009-08-20 05:19:12 +02:00
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_GEO_IP
|
2009-08-21 23:28:28 +02:00
|
|
|
int as = m_torrent->session().as_for_ip(p->address());
|
2009-08-20 05:19:12 +02:00
|
|
|
#ifdef TORRENT_DEBUG
|
|
|
|
p->inet_as_num = as;
|
|
|
|
#endif
|
2009-08-21 23:28:28 +02:00
|
|
|
p->inet_as = m_torrent->session().lookup_as(as);
|
2009-08-20 05:19:12 +02:00
|
|
|
#endif
|
|
|
|
if (is_connect_candidate(*p, m_finished))
|
|
|
|
++m_num_connect_candidates;
|
|
|
|
|
2011-11-15 03:34:00 +01:00
|
|
|
m_torrent->state_updated();
|
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void policy::update_peer(policy::peer* p, int src, int flags
|
|
|
|
, tcp::endpoint const& remote, char const* destination)
|
|
|
|
{
|
|
|
|
bool was_conn_cand = is_connect_candidate(*p, m_finished);
|
|
|
|
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2009-08-20 05:19:12 +02:00
|
|
|
p->connectable = true;
|
|
|
|
|
|
|
|
TORRENT_ASSERT(p->address() == remote.address());
|
|
|
|
p->port = remote.port();
|
|
|
|
p->source |= src;
|
|
|
|
|
|
|
|
// if this peer has failed before, decrease the
|
|
|
|
// counter to allow it another try, since somebody
|
|
|
|
// else is appearantly able to connect to it
|
|
|
|
// only trust this if it comes from the tracker
|
|
|
|
if (p->failcount > 0 && src == peer_info::tracker)
|
|
|
|
--p->failcount;
|
|
|
|
|
|
|
|
// if we're connected to this peer
|
|
|
|
// we already know if it's a seed or not
|
|
|
|
// so we don't have to trust this source
|
|
|
|
if ((flags & 0x02) && !p->connection)
|
|
|
|
{
|
|
|
|
if (!p->seed) ++m_num_seeds;
|
|
|
|
p->seed = true;
|
|
|
|
}
|
2010-11-29 02:33:05 +01:00
|
|
|
if (flags & 0x04)
|
|
|
|
p->supports_utp = true;
|
|
|
|
if (flags & 0x08)
|
|
|
|
p->supports_holepunch = true;
|
2009-08-20 05:19:12 +02:00
|
|
|
|
|
|
|
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
|
|
|
|
if (p->connection)
|
|
|
|
{
|
|
|
|
// this means we're already connected
|
|
|
|
// to this peer. don't connect to
|
|
|
|
// it again.
|
|
|
|
|
|
|
|
error_code ec;
|
|
|
|
char hex_pid[41];
|
|
|
|
to_hex((char*)&p->connection->pid()[0], 20, hex_pid);
|
|
|
|
char msg[200];
|
|
|
|
snprintf(msg, 200, "already connected to peer: %s %s"
|
|
|
|
, print_endpoint(remote).c_str(), hex_pid);
|
|
|
|
m_torrent->debug_log(msg);
|
|
|
|
|
|
|
|
TORRENT_ASSERT(p->connection->associated_torrent().lock().get() == m_torrent);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (was_conn_cand != is_connect_candidate(*p, m_finished))
|
|
|
|
{
|
|
|
|
m_num_connect_candidates += was_conn_cand ? -1 : 1;
|
|
|
|
if (m_num_connect_candidates < 0) m_num_connect_candidates = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-19 04:53:36 +02:00
|
|
|
#if TORRENT_USE_I2P
|
2009-08-20 05:19:12 +02:00
|
|
|
policy::peer* policy::add_i2p_peer(char const* destination, int src, char flags)
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
bool found = false;
|
|
|
|
iterator iter = std::lower_bound(
|
|
|
|
m_peers.begin(), m_peers.end()
|
|
|
|
, destination, peer_address_compare()
|
|
|
|
);
|
|
|
|
|
|
|
|
if (iter != m_peers.end() && strcmp((*iter)->dest(), destination) == 0)
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
peer* p = 0;
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
// we don't have any info about this peer.
|
|
|
|
// add a new entry
|
|
|
|
p = (peer*)m_torrent->session().m_i2p_peer_pool.malloc();
|
|
|
|
if (p == 0) return 0;
|
|
|
|
m_torrent->session().m_i2p_peer_pool.set_next_size(500);
|
|
|
|
new (p) i2p_peer(destination, true, src);
|
2010-07-18 21:28:22 +02:00
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2010-07-18 21:28:22 +02:00
|
|
|
p->in_use = true;
|
|
|
|
#endif
|
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
if (!insert_peer(p, iter, flags))
|
|
|
|
{
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2010-07-18 21:28:22 +02:00
|
|
|
p->in_use = false;
|
|
|
|
#endif
|
|
|
|
|
2012-04-10 07:28:48 +02:00
|
|
|
m_torrent->session().m_i2p_peer_pool.destroy((i2p_peer*)p);
|
2009-08-20 05:19:12 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p = *iter;
|
|
|
|
update_peer(p, src, flags, tcp::endpoint(), destination);
|
|
|
|
}
|
2011-11-15 03:34:00 +01:00
|
|
|
m_torrent->state_updated();
|
2009-08-20 05:19:12 +02:00
|
|
|
return p;
|
|
|
|
}
|
2009-10-19 04:53:36 +02:00
|
|
|
#endif // TORRENT_USE_I2P
|
2009-08-20 05:19:12 +02:00
|
|
|
|
2009-05-16 00:07:19 +02:00
|
|
|
policy::peer* policy::add_peer(tcp::endpoint const& remote, peer_id const& pid
|
2007-04-10 23:23:13 +02:00
|
|
|
, int src, char flags)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2009-05-03 05:41:33 +02:00
|
|
|
INVARIANT_CHECK;
|
2005-09-28 18:12:47 +02:00
|
|
|
|
2007-05-05 02:29:33 +02:00
|
|
|
// just ignore the obviously invalid entries
|
2007-08-14 19:47:48 +02:00
|
|
|
if (remote.address() == address() || remote.port() == 0)
|
2007-10-02 22:30:53 +02:00
|
|
|
return 0;
|
2004-01-16 17:19:27 +01:00
|
|
|
|
2012-12-02 18:58:32 +01:00
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
// don't allow link-local IPv6 addresses since they
|
|
|
|
// can't be used like normal addresses, they require an interface
|
|
|
|
// and will just cause connect() to fail with EINVAL
|
|
|
|
if (remote.address().is_v6() && remote.address().to_v6().is_link_local())
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
|
2007-06-01 03:05:57 +02:00
|
|
|
aux::session_impl& ses = m_torrent->session();
|
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
// if this is an i2p torrent, and we don't allow mixed mode
|
|
|
|
// no regular peers should ever be added!
|
|
|
|
if (!ses.m_settings.allow_i2p_mixed && m_torrent->torrent_file().is_i2p())
|
2009-09-07 03:47:30 +02:00
|
|
|
{
|
|
|
|
if (ses.m_alerts.should_post<peer_blocked_alert>())
|
2014-02-07 18:35:56 +01:00
|
|
|
ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle()
|
|
|
|
, remote.address(), peer_blocked_alert::ip_filter));
|
2009-08-20 05:19:12 +02:00
|
|
|
return 0;
|
2009-09-07 03:47:30 +02:00
|
|
|
}
|
2009-08-20 05:19:12 +02:00
|
|
|
|
2007-06-01 03:05:57 +02:00
|
|
|
port_filter const& pf = ses.m_port_filter;
|
|
|
|
if (pf.access(remote.port()) & port_filter::blocked)
|
|
|
|
{
|
2008-07-06 14:22:56 +02:00
|
|
|
if (ses.m_alerts.should_post<peer_blocked_alert>())
|
2014-02-07 18:35:56 +01:00
|
|
|
ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle()
|
|
|
|
, remote.address(), peer_blocked_alert::port_filter));
|
2011-05-02 03:45:56 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::filtered);
|
|
|
|
#endif
|
2007-10-02 22:30:53 +02:00
|
|
|
return 0;
|
2007-06-01 03:05:57 +02:00
|
|
|
}
|
|
|
|
|
2010-12-31 01:33:23 +01:00
|
|
|
if (ses.m_settings.no_connect_privileged_ports && remote.port() < 1024)
|
|
|
|
{
|
|
|
|
if (ses.m_alerts.should_post<peer_blocked_alert>())
|
2014-02-07 18:35:56 +01:00
|
|
|
ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle()
|
|
|
|
, remote.address(), peer_blocked_alert::privileged_ports));
|
2011-05-02 03:45:56 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::filtered);
|
|
|
|
#endif
|
2010-12-31 01:33:23 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
// if the IP is blocked, don't add it
|
2011-03-02 18:37:10 +01:00
|
|
|
if (m_torrent->apply_ip_filter()
|
|
|
|
&& (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked))
|
2009-05-11 22:23:47 +02:00
|
|
|
{
|
|
|
|
if (ses.m_alerts.should_post<peer_blocked_alert>())
|
2014-02-07 18:35:56 +01:00
|
|
|
ses.m_alerts.post_alert(peer_blocked_alert(m_torrent->get_handle()
|
|
|
|
, remote.address(), peer_blocked_alert::ip_filter));
|
2011-05-02 03:45:56 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::filtered);
|
|
|
|
#endif
|
2009-05-11 22:23:47 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
iterator iter;
|
2009-08-20 05:19:12 +02:00
|
|
|
peer* p = 0;
|
2008-04-07 03:29:21 +02:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
bool found = false;
|
2008-04-07 03:29:21 +02:00
|
|
|
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
2003-10-27 17:06:00 +01:00
|
|
|
{
|
2009-05-07 00:36:24 +02:00
|
|
|
std::pair<iterator, iterator> range = find_peers(remote.address());
|
2009-04-30 07:49:46 +02:00
|
|
|
iter = std::find_if(range.first, range.second, match_peer_endpoint(remote));
|
2009-05-07 00:36:24 +02:00
|
|
|
if (iter != range.second) found = true;
|
2008-04-07 03:29:21 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-05-25 12:15:35 +02:00
|
|
|
iter = std::lower_bound(
|
|
|
|
m_peers.begin(), m_peers.end()
|
2009-05-25 19:23:03 +02:00
|
|
|
, remote.address(), peer_address_compare()
|
2009-05-25 12:15:35 +02:00
|
|
|
);
|
2009-05-24 23:49:19 +02:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
if (iter != m_peers.end() && (*iter)->address() == remote.address()) found = true;
|
2008-04-07 03:29:21 +02:00
|
|
|
}
|
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
if (!found)
|
2008-04-07 03:29:21 +02:00
|
|
|
{
|
|
|
|
// we don't have any info about this peer.
|
|
|
|
// add a new entry
|
2009-08-20 05:19:12 +02:00
|
|
|
|
2009-05-24 23:49:19 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
bool is_v6 = remote.address().is_v6();
|
|
|
|
#endif
|
2009-08-20 05:19:12 +02:00
|
|
|
p =
|
2009-05-24 23:49:19 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
is_v6 ? (peer*)m_torrent->session().m_ipv6_peer_pool.malloc() :
|
|
|
|
#endif
|
|
|
|
(peer*)m_torrent->session().m_ipv4_peer_pool.malloc();
|
2009-05-11 22:23:47 +02:00
|
|
|
if (p == 0) return 0;
|
2009-05-24 23:49:19 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
if (is_v6)
|
|
|
|
m_torrent->session().m_ipv6_peer_pool.set_next_size(500);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
m_torrent->session().m_ipv4_peer_pool.set_next_size(500);
|
|
|
|
|
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
if (is_v6)
|
|
|
|
new (p) ipv6_peer(remote, true, src);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
new (p) ipv4_peer(remote, true, src);
|
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2010-07-18 21:28:22 +02:00
|
|
|
p->in_use = true;
|
|
|
|
#endif
|
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
if (!insert_peer(p, iter, flags))
|
2008-04-07 03:29:21 +02:00
|
|
|
{
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2010-07-18 21:28:22 +02:00
|
|
|
p->in_use = false;
|
|
|
|
#endif
|
2009-08-20 05:19:12 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
2012-04-10 07:28:48 +02:00
|
|
|
if (is_v6) m_torrent->session().m_ipv6_peer_pool.destroy((ipv6_peer*)p);
|
2009-08-20 05:19:12 +02:00
|
|
|
else
|
2008-04-05 06:53:22 +02:00
|
|
|
#endif
|
2012-04-10 07:28:48 +02:00
|
|
|
m_torrent->session().m_ipv4_peer_pool.destroy((ipv4_peer*)p);
|
2009-08-20 05:19:12 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2011-05-02 03:45:56 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
m_torrent->notify_extension_add_peer(remote, src, torrent_plugin::first_time);
|
|
|
|
#endif
|
2008-04-07 03:29:21 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-08-20 05:19:12 +02:00
|
|
|
p = *iter;
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2009-08-20 05:19:12 +02:00
|
|
|
update_peer(p, src, flags, remote, 0);
|
2011-05-02 03:45:56 +02:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
m_torrent->notify_extension_add_peer(remote, src, 0);
|
|
|
|
#endif
|
2004-01-12 04:05:10 +01:00
|
|
|
}
|
2008-04-07 03:29:21 +02:00
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
return p;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
bool policy::connect_one_peer(int session_time)
|
2004-01-14 17:18:53 +01:00
|
|
|
{
|
2009-05-03 05:41:33 +02:00
|
|
|
INVARIANT_CHECK;
|
2007-04-13 19:47:40 +02:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_torrent->want_more_peers());
|
2007-04-12 12:21:55 +02:00
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
iterator i = find_connect_candidate(session_time);
|
|
|
|
if (i == m_peers.end()) return false;
|
2009-05-07 00:36:24 +02:00
|
|
|
peer& p = **i;
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p.in_use);
|
2007-05-06 00:55:34 +02:00
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
TORRENT_ASSERT(!p.banned);
|
|
|
|
TORRENT_ASSERT(!p.connection);
|
|
|
|
TORRENT_ASSERT(p.connectable);
|
2004-02-25 14:18:41 +01:00
|
|
|
|
2009-05-06 09:06:26 +02:00
|
|
|
TORRENT_ASSERT(m_finished == m_torrent->is_finished());
|
|
|
|
TORRENT_ASSERT(is_connect_candidate(p, m_finished));
|
2009-04-30 07:49:46 +02:00
|
|
|
if (!m_torrent->connect_to_peer(&p))
|
2004-02-26 19:55:10 +01:00
|
|
|
{
|
2009-04-30 07:49:46 +02:00
|
|
|
// failcount is a 5 bit value
|
2009-08-02 00:48:43 +02:00
|
|
|
const bool was_conn_cand = is_connect_candidate(p, m_finished);
|
2009-04-30 07:49:46 +02:00
|
|
|
if (p.failcount < 31) ++p.failcount;
|
2009-08-02 00:48:43 +02:00
|
|
|
if (was_conn_cand && !is_connect_candidate(p, m_finished))
|
2009-06-18 18:16:41 +02:00
|
|
|
--m_num_connect_candidates;
|
2007-05-06 00:55:34 +02:00
|
|
|
return false;
|
2007-04-12 12:21:55 +02:00
|
|
|
}
|
2009-04-30 07:49:46 +02:00
|
|
|
TORRENT_ASSERT(p.connection);
|
2009-05-06 09:06:26 +02:00
|
|
|
TORRENT_ASSERT(!is_connect_candidate(p, m_finished));
|
2008-04-07 03:29:21 +02:00
|
|
|
return true;
|
2004-01-14 17:18:53 +01:00
|
|
|
}
|
|
|
|
|
2003-12-01 06:01:40 +01:00
|
|
|
// this is called whenever a peer connection is closed
|
2009-04-30 07:49:46 +02:00
|
|
|
void policy::connection_closed(const peer_connection& c, int session_time)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2009-05-03 05:41:33 +02:00
|
|
|
INVARIANT_CHECK;
|
2005-09-28 18:12:47 +02:00
|
|
|
|
2007-08-17 01:53:14 +02:00
|
|
|
peer* p = c.peer_info_struct();
|
2005-05-12 01:03:12 +02:00
|
|
|
|
2007-08-21 19:45:28 +02:00
|
|
|
// if we couldn't find the connection in our list, just ignore it.
|
|
|
|
if (p == 0) return;
|
|
|
|
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
|
|
|
|
2012-03-24 02:29:31 +01:00
|
|
|
// web seeds are special, they're not connected via the peer list
|
|
|
|
// so they're not kept in m_peers
|
|
|
|
TORRENT_ASSERT(p->web_seed
|
|
|
|
|| std::find_if(
|
|
|
|
m_peers.begin()
|
|
|
|
, m_peers.end()
|
|
|
|
, match_peer_connection(c))
|
|
|
|
!= m_peers.end());
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(p->connection == &c);
|
2009-06-26 07:58:24 +02:00
|
|
|
TORRENT_ASSERT(!is_connect_candidate(*p, m_finished));
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2010-01-15 04:06:05 +01:00
|
|
|
// save transfer rate limits
|
|
|
|
p->upload_rate_limit = c.upload_limit();
|
|
|
|
p->download_rate_limit = c.download_limit();
|
|
|
|
|
2007-08-17 01:53:14 +02:00
|
|
|
p->connection = 0;
|
|
|
|
p->optimistically_unchoked = false;
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2007-10-04 23:26:50 +02:00
|
|
|
// if fast reconnect is true, we won't
|
|
|
|
// update the timestamp, and it will remain
|
|
|
|
// the time when we initiated the connection.
|
|
|
|
if (!c.fast_reconnect())
|
2009-04-30 07:49:46 +02:00
|
|
|
p->last_connected = session_time;
|
2004-01-14 02:19:30 +01:00
|
|
|
|
2004-03-21 03:03:37 +01:00
|
|
|
if (c.failed())
|
|
|
|
{
|
2009-04-30 07:49:46 +02:00
|
|
|
// failcount is a 5 bit value
|
|
|
|
if (p->failcount < 31) ++p->failcount;
|
2004-03-21 03:03:37 +01:00
|
|
|
}
|
|
|
|
|
2009-05-06 09:06:26 +02:00
|
|
|
if (is_connect_candidate(*p, m_finished))
|
2008-03-29 19:47:24 +01:00
|
|
|
++m_num_connect_candidates;
|
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
// if we're already a seed, it's not as important
|
|
|
|
// to keep all the possibly stale peers
|
|
|
|
// if we're not a seed, but we have too many peers
|
|
|
|
// start weeding the ones we only know from resume
|
|
|
|
// data first
|
2010-03-08 09:03:53 +01:00
|
|
|
// at this point it may be tempting to erase peers
|
|
|
|
// from the peer list, but keep in mind that we might
|
|
|
|
// have gotten to this point through new_connection, just
|
|
|
|
// disconnecting an old peer, relying on this policy::peer
|
|
|
|
// to still exist when we get back there, to assign the new
|
|
|
|
// peer connection pointer to it. The peer list must
|
|
|
|
// be left intact.
|
2011-04-25 01:26:54 +02:00
|
|
|
|
|
|
|
// if we allow multiple connections per IP, and this peer
|
|
|
|
// was incoming and it never advertised its listen
|
|
|
|
// port, we don't really know which peer it was. In order
|
|
|
|
// to avoid adding one entry for every single connection
|
|
|
|
// the peer makes to us, don't save this entry
|
|
|
|
if (m_torrent->settings().allow_multiple_connections_per_ip
|
2012-11-21 21:42:40 +01:00
|
|
|
&& !p->connectable
|
|
|
|
&& p != m_locked_peer)
|
2011-04-25 01:26:54 +02:00
|
|
|
{
|
|
|
|
erase_peer(p);
|
|
|
|
}
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
void policy::peer_is_interesting(peer_connection& c)
|
|
|
|
{
|
2009-05-03 05:41:33 +02:00
|
|
|
INVARIANT_CHECK;
|
2005-09-28 18:12:47 +02:00
|
|
|
|
2009-05-12 20:24:16 +02:00
|
|
|
// no peer should be interesting if we're finished
|
|
|
|
TORRENT_ASSERT(!m_torrent->is_finished());
|
|
|
|
|
2008-08-29 19:21:56 +02:00
|
|
|
if (c.in_handshake()) return;
|
2004-01-12 21:31:27 +01:00
|
|
|
c.send_interested();
|
2007-08-14 19:47:48 +02:00
|
|
|
if (c.has_peer_choked()
|
|
|
|
&& c.allowed_fast().empty())
|
|
|
|
return;
|
2003-10-23 01:00:57 +02:00
|
|
|
request_a_block(*m_torrent, c);
|
2008-07-08 23:33:36 +02:00
|
|
|
c.send_block_requests();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2009-05-03 08:57:04 +02:00
|
|
|
void policy::recalculate_connect_candidates()
|
|
|
|
{
|
2009-06-18 18:16:41 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2009-05-03 08:57:04 +02:00
|
|
|
const bool is_finished = m_torrent->is_finished();
|
2009-05-06 09:06:26 +02:00
|
|
|
if (is_finished == m_finished) return;
|
|
|
|
|
2009-06-18 18:16:41 +02:00
|
|
|
m_num_connect_candidates = 0;
|
2009-05-06 09:06:26 +02:00
|
|
|
m_finished = is_finished;
|
2009-05-03 08:57:04 +02:00
|
|
|
for (const_iterator i = m_peers.begin();
|
|
|
|
i != m_peers.end(); ++i)
|
|
|
|
{
|
2009-05-07 00:36:24 +02:00
|
|
|
m_num_connect_candidates += is_connect_candidate(**i, m_finished);
|
2009-05-03 08:57:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2004-01-15 02:29:43 +01:00
|
|
|
bool policy::has_connection(const peer_connection* c)
|
2003-12-01 06:01:40 +01:00
|
|
|
{
|
2009-05-03 05:41:33 +02:00
|
|
|
INVARIANT_CHECK;
|
2007-04-13 19:47:40 +02:00
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(c);
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2010-11-29 02:33:05 +01:00
|
|
|
if (c->remote() != c->get_socket()->remote_endpoint(ec) && !ec)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "c->remote: %s\nc->get_socket()->remote_endpoint: %s\n"
|
|
|
|
, print_endpoint(c->remote()).c_str()
|
|
|
|
, print_endpoint(c->get_socket()->remote_endpoint(ec)).c_str());
|
|
|
|
TORRENT_ASSERT(false);
|
|
|
|
}
|
2007-02-12 06:46:29 +01:00
|
|
|
|
2004-01-15 17:45:34 +01:00
|
|
|
return std::find_if(
|
|
|
|
m_peers.begin()
|
|
|
|
, m_peers.end()
|
2010-01-18 23:20:42 +01:00
|
|
|
, match_peer_connection_or_endpoint(*c)) != m_peers.end();
|
2003-12-01 06:01:40 +01:00
|
|
|
}
|
2011-05-08 11:04:59 +02:00
|
|
|
#endif
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2014-01-21 20:26:09 +01:00
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
2004-05-10 08:12:29 +02:00
|
|
|
void policy::check_invariant() const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2008-03-29 19:47:24 +01:00
|
|
|
TORRENT_ASSERT(m_num_connect_candidates >= 0);
|
2010-10-16 17:24:45 +02:00
|
|
|
TORRENT_ASSERT(m_num_connect_candidates <= int(m_peers.size()));
|
2005-05-12 01:14:58 +02:00
|
|
|
if (m_torrent->is_aborted()) return;
|
2007-10-31 10:48:20 +01:00
|
|
|
|
2009-05-03 05:41:33 +02:00
|
|
|
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
|
2005-09-28 22:22:34 +02:00
|
|
|
int connected_peers = 0;
|
2005-11-02 17:28:39 +01:00
|
|
|
|
|
|
|
int total_connections = 0;
|
|
|
|
int nonempty_connections = 0;
|
2009-06-18 18:16:41 +02:00
|
|
|
int connect_candidates = 0;
|
2007-08-14 19:47:48 +02:00
|
|
|
|
2007-10-02 21:11:04 +02:00
|
|
|
std::set<tcp::endpoint> unique_test;
|
2009-05-07 00:36:24 +02:00
|
|
|
const_iterator prev = m_peers.end();
|
2007-04-10 23:23:13 +02:00
|
|
|
for (const_iterator i = m_peers.begin();
|
2005-05-12 01:03:12 +02:00
|
|
|
i != m_peers.end(); ++i)
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
2009-05-07 00:36:24 +02:00
|
|
|
if (prev != m_peers.end()) ++prev;
|
|
|
|
if (i == m_peers.begin() + 1) prev = m_peers.begin();
|
|
|
|
if (prev != m_peers.end())
|
|
|
|
{
|
|
|
|
if (m_torrent->settings().allow_multiple_connections_per_ip)
|
|
|
|
TORRENT_ASSERT(!((*i)->address() < (*prev)->address()));
|
|
|
|
else
|
|
|
|
TORRENT_ASSERT((*prev)->address() < (*i)->address());
|
|
|
|
}
|
|
|
|
peer const& p = **i;
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p.in_use);
|
2009-06-18 18:16:41 +02:00
|
|
|
if (is_connect_candidate(p, m_finished)) ++connect_candidates;
|
2008-04-05 06:53:22 +02:00
|
|
|
#ifndef TORRENT_DISABLE_GEO_IP
|
|
|
|
TORRENT_ASSERT(p.inet_as == 0 || p.inet_as->first == p.inet_as_num);
|
|
|
|
#endif
|
2007-10-02 21:11:04 +02:00
|
|
|
if (!m_torrent->settings().allow_multiple_connections_per_ip)
|
|
|
|
{
|
2009-05-11 22:23:47 +02:00
|
|
|
std::pair<const_iterator, const_iterator> range = find_peers(p.address());
|
2009-05-07 00:36:24 +02:00
|
|
|
TORRENT_ASSERT(range.second - range.first == 1);
|
2007-10-02 21:11:04 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-07-14 13:15:35 +02:00
|
|
|
TORRENT_ASSERT(unique_test.count(p.ip()) == 0);
|
|
|
|
unique_test.insert(p.ip());
|
|
|
|
// TORRENT_ASSERT(p.connection == 0 || p.ip() == p.connection->remote());
|
2007-10-02 21:11:04 +02:00
|
|
|
}
|
2005-11-02 17:28:39 +01:00
|
|
|
++total_connections;
|
2007-09-19 08:05:14 +02:00
|
|
|
if (!p.connection)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2007-08-21 03:17:42 +02:00
|
|
|
if (p.optimistically_unchoked)
|
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(p.connection);
|
|
|
|
TORRENT_ASSERT(!p.connection->is_choked());
|
2007-08-21 03:17:42 +02:00
|
|
|
}
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(p.connection->peer_info_struct() == 0
|
2007-07-04 17:46:10 +02:00
|
|
|
|| p.connection->peer_info_struct() == &p);
|
2005-11-02 17:28:39 +01:00
|
|
|
++nonempty_connections;
|
2007-07-04 17:46:10 +02:00
|
|
|
if (!p.connection->is_disconnecting())
|
2005-09-29 01:58:55 +02:00
|
|
|
++connected_peers;
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2005-09-28 22:22:34 +02:00
|
|
|
|
2009-06-18 18:16:41 +02:00
|
|
|
TORRENT_ASSERT(m_num_connect_candidates == connect_candidates);
|
|
|
|
|
2005-09-29 01:58:55 +02:00
|
|
|
int num_torrent_peers = 0;
|
|
|
|
for (torrent::const_peer_iterator i = m_torrent->begin();
|
|
|
|
i != m_torrent->end(); ++i)
|
|
|
|
{
|
2007-10-31 10:48:20 +01:00
|
|
|
if ((*i)->is_disconnecting()) continue;
|
2006-04-25 23:04:48 +02:00
|
|
|
// ignore web_peer_connections since they are not managed
|
|
|
|
// by the policy class
|
2009-11-02 21:43:38 +01:00
|
|
|
if ((*i)->type() != peer_connection::bittorrent_connection) continue;
|
2005-09-29 01:58:55 +02:00
|
|
|
++num_torrent_peers;
|
|
|
|
}
|
2005-09-28 22:22:34 +02:00
|
|
|
|
2007-07-04 04:16:49 +02:00
|
|
|
if (m_torrent->has_picker())
|
|
|
|
{
|
|
|
|
piece_picker& p = m_torrent->picker();
|
|
|
|
std::vector<piece_picker::downloading_piece> downloaders = p.get_download_queue();
|
|
|
|
|
|
|
|
std::set<void*> peer_set;
|
|
|
|
std::vector<void*> peers;
|
|
|
|
for (std::vector<piece_picker::downloading_piece>::iterator i = downloaders.begin()
|
|
|
|
, end(downloaders.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
p.get_downloaders(peers, i->index);
|
|
|
|
std::copy(peers.begin(), peers.end()
|
|
|
|
, std::insert_iterator<std::set<void*> >(peer_set, peer_set.begin()));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (std::set<void*>::iterator i = peer_set.begin()
|
|
|
|
, end(peer_set.end()); i != end; ++i)
|
|
|
|
{
|
|
|
|
policy::peer* p = static_cast<policy::peer*>(*i);
|
|
|
|
if (p == 0) continue;
|
2012-04-08 18:03:39 +02:00
|
|
|
TORRENT_ASSERT(p->in_use);
|
2007-10-06 23:15:44 +02:00
|
|
|
if (p->connection == 0) continue;
|
2012-03-24 02:29:31 +01:00
|
|
|
// web seeds are special, they're not connected via the peer list
|
|
|
|
// so they're not kept in m_peers
|
|
|
|
if (p->connection->type() != peer_connection::bittorrent_connection) continue;
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(std::find_if(m_peers.begin(), m_peers.end()
|
2010-01-18 23:20:42 +01:00
|
|
|
, match_peer_connection_or_endpoint(*p->connection)) != m_peers.end());
|
2007-07-04 04:16:49 +02:00
|
|
|
}
|
|
|
|
}
|
2009-05-03 08:57:58 +02:00
|
|
|
#endif // TORRENT_EXPENSIVE_INVARIANT_CHECKS
|
2007-07-04 04:16:49 +02:00
|
|
|
|
2005-10-01 17:12:10 +02:00
|
|
|
// this invariant is a bit complicated.
|
|
|
|
// the usual case should be that connected_peers
|
|
|
|
// == num_torrent_peers. But when there's an incoming
|
|
|
|
// connection, it will first be added to the policy
|
|
|
|
// and then be added to the torrent.
|
|
|
|
// When there's an outgoing connection, it will first
|
|
|
|
// be added to the torrent and then to the policy.
|
|
|
|
// that's why the two second cases are in there.
|
2007-05-25 19:06:30 +02:00
|
|
|
/*
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(connected_peers == num_torrent_peers
|
2005-10-01 17:12:10 +02:00
|
|
|
|| (connected_peers == num_torrent_peers + 1
|
|
|
|
&& connected_peers > 0)
|
|
|
|
|| (connected_peers + 1 == num_torrent_peers
|
|
|
|
&& num_torrent_peers > 0));
|
2007-05-25 19:06:30 +02:00
|
|
|
*/
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2009-05-03 08:57:58 +02:00
|
|
|
#endif // TORRENT_DEBUG
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2009-05-24 23:49:19 +02:00
|
|
|
policy::peer::peer(boost::uint16_t port, bool conn, int src)
|
2008-07-14 13:15:35 +02:00
|
|
|
: prev_amount_upload(0)
|
|
|
|
, prev_amount_download(0)
|
|
|
|
, connection(0)
|
2012-12-31 07:54:54 +01:00
|
|
|
, peer_rank(0)
|
2008-04-05 06:53:22 +02:00
|
|
|
#ifndef TORRENT_DISABLE_GEO_IP
|
|
|
|
, inet_as(0)
|
2009-04-30 07:49:46 +02:00
|
|
|
#endif
|
|
|
|
, last_optimistically_unchoked(0)
|
|
|
|
, last_connected(0)
|
2009-05-24 23:49:19 +02:00
|
|
|
, port(port)
|
2010-01-15 04:06:05 +01:00
|
|
|
, upload_rate_limit(0)
|
|
|
|
, download_rate_limit(0)
|
2010-02-12 03:50:23 +01:00
|
|
|
, hashfails(0)
|
2007-03-28 21:56:53 +02:00
|
|
|
, failcount(0)
|
2009-04-30 07:49:46 +02:00
|
|
|
, connectable(conn)
|
|
|
|
, optimistically_unchoked(false)
|
|
|
|
, seed(false)
|
|
|
|
, fast_reconnects(0)
|
2008-04-01 19:38:19 +02:00
|
|
|
, trust_points(0)
|
|
|
|
, source(src)
|
|
|
|
#ifndef TORRENT_DISABLE_ENCRYPTION
|
2013-04-01 08:27:27 +02:00
|
|
|
// assume no support in order to
|
|
|
|
// prefer opening non-encrypyed
|
|
|
|
// connections. If it fails, we'll
|
|
|
|
// retry with encryption
|
|
|
|
, pe_support(false)
|
2008-04-01 19:38:19 +02:00
|
|
|
#endif
|
2009-04-30 07:49:46 +02:00
|
|
|
#if TORRENT_USE_IPV6
|
|
|
|
, is_v6_addr(false)
|
2009-08-20 05:19:12 +02:00
|
|
|
#endif
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
, is_i2p_addr(false)
|
2009-04-30 07:49:46 +02:00
|
|
|
#endif
|
2007-05-25 21:42:10 +02:00
|
|
|
, on_parole(false)
|
2003-12-14 06:56:12 +01:00
|
|
|
, banned(false)
|
2008-04-01 19:38:19 +02:00
|
|
|
#ifndef TORRENT_DISABLE_DHT
|
|
|
|
, added_to_dht(false)
|
|
|
|
#endif
|
2010-11-29 02:33:05 +01:00
|
|
|
, supports_utp(true) // assume peers support utp
|
|
|
|
, confirmed_supports_utp(false)
|
|
|
|
, supports_holepunch(false)
|
2012-03-24 02:29:31 +01:00
|
|
|
, web_seed(false)
|
2014-01-19 20:45:50 +01:00
|
|
|
#if TORRENT_USE_ASSERTS
|
2011-02-21 06:24:41 +01:00
|
|
|
, in_use(false)
|
|
|
|
#endif
|
2004-01-14 17:18:53 +01:00
|
|
|
{
|
2008-04-01 19:38:19 +02:00
|
|
|
TORRENT_ASSERT((src & 0xff) == src);
|
2004-01-14 17:18:53 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
|
2012-12-31 07:54:54 +01:00
|
|
|
// TOOD: pass in both an IPv6 and IPv4 address here
|
2013-01-02 00:12:16 +01:00
|
|
|
boost::uint32_t policy::peer::rank(external_ip const& external, int external_port) const
|
2012-12-31 07:54:54 +01:00
|
|
|
{
|
|
|
|
if (peer_rank == 0)
|
2013-01-02 00:12:16 +01:00
|
|
|
peer_rank = peer_priority(
|
|
|
|
tcp::endpoint(external.external_address(this->address()), external_port)
|
|
|
|
, tcp::endpoint(this->address(), this->port));
|
2012-12-31 07:54:54 +01:00
|
|
|
return peer_rank;
|
|
|
|
}
|
|
|
|
|
2004-01-21 01:59:38 +01:00
|
|
|
size_type policy::peer::total_download() const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
if (connection != 0)
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(prev_amount_download == 0);
|
2004-02-25 00:55:42 +01:00
|
|
|
return connection->statistics().total_payload_download();
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
else
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2011-06-01 08:47:57 +02:00
|
|
|
return size_type(prev_amount_download) << 10;
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
|
|
|
|
2004-01-21 01:59:38 +01:00
|
|
|
size_type policy::peer::total_upload() const
|
2003-12-14 06:56:12 +01:00
|
|
|
{
|
|
|
|
if (connection != 0)
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(prev_amount_upload == 0);
|
2004-02-25 00:55:42 +01:00
|
|
|
return connection->statistics().total_payload_upload();
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
else
|
2004-01-15 17:45:34 +01:00
|
|
|
{
|
2011-06-01 08:47:57 +02:00
|
|
|
return size_type(prev_amount_upload) << 10;
|
2004-01-15 17:45:34 +01:00
|
|
|
}
|
2003-12-14 06:56:12 +01:00
|
|
|
}
|
2008-04-24 09:49:23 +02:00
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
// this returns true if lhs is a better erase candidate than rhs
|
|
|
|
bool policy::compare_peer_erase(policy::peer const& lhs, policy::peer const& rhs) const
|
|
|
|
{
|
2011-11-24 18:50:57 +01:00
|
|
|
TORRENT_ASSERT(lhs.connection == 0);
|
|
|
|
TORRENT_ASSERT(rhs.connection == 0);
|
|
|
|
|
|
|
|
// primarily, prefer getting rid of peers we've already tried and failed
|
|
|
|
if (lhs.failcount != rhs.failcount)
|
|
|
|
return lhs.failcount > rhs.failcount;
|
|
|
|
|
2009-05-11 22:23:47 +02:00
|
|
|
bool lhs_resume_data_source = lhs.source == peer_info::resume_data;
|
|
|
|
bool rhs_resume_data_source = rhs.source == peer_info::resume_data;
|
|
|
|
|
|
|
|
// prefer to drop peers whose only source is resume data
|
|
|
|
if (lhs_resume_data_source != rhs_resume_data_source)
|
|
|
|
return lhs_resume_data_source > rhs_resume_data_source;
|
|
|
|
|
2011-04-25 02:13:30 +02:00
|
|
|
if (lhs.connectable != rhs.connectable)
|
|
|
|
return lhs.connectable < rhs.connectable;
|
|
|
|
|
2011-11-24 18:50:57 +01:00
|
|
|
return lhs.trust_points < rhs.trust_points;
|
2009-05-11 22:23:47 +02:00
|
|
|
}
|
|
|
|
|
2008-04-24 09:49:23 +02:00
|
|
|
// this returns true if lhs is a better connect candidate than rhs
|
|
|
|
bool policy::compare_peer(policy::peer const& lhs, policy::peer const& rhs
|
2013-01-02 00:12:16 +01:00
|
|
|
, external_ip const& external, int external_port) const
|
2008-04-24 09:49:23 +02:00
|
|
|
{
|
|
|
|
// prefer peers with lower failcount
|
2008-04-24 18:34:43 +02:00
|
|
|
if (lhs.failcount != rhs.failcount)
|
|
|
|
return lhs.failcount < rhs.failcount;
|
2008-04-24 09:49:23 +02:00
|
|
|
|
|
|
|
// Local peers should always be tried first
|
2009-04-30 07:49:46 +02:00
|
|
|
bool lhs_local = is_local(lhs.address());
|
|
|
|
bool rhs_local = is_local(rhs.address());
|
2008-04-24 18:34:43 +02:00
|
|
|
if (lhs_local != rhs_local) return lhs_local > rhs_local;
|
2008-04-24 09:49:23 +02:00
|
|
|
|
2009-04-30 07:49:46 +02:00
|
|
|
if (lhs.last_connected != rhs.last_connected)
|
|
|
|
return lhs.last_connected < rhs.last_connected;
|
2008-04-24 09:49:23 +02:00
|
|
|
|
2009-05-07 00:36:24 +02:00
|
|
|
int lhs_rank = source_rank(lhs.source);
|
|
|
|
int rhs_rank = source_rank(rhs.source);
|
|
|
|
if (lhs_rank != rhs_rank) return lhs_rank > rhs_rank;
|
|
|
|
|
2008-04-24 09:49:23 +02:00
|
|
|
#ifndef TORRENT_DISABLE_GEO_IP
|
|
|
|
// don't bias fast peers when seeding
|
2009-05-06 09:06:26 +02:00
|
|
|
if (!m_finished && m_torrent->session().has_asnum_db())
|
2008-04-24 09:49:23 +02:00
|
|
|
{
|
2008-04-24 18:34:43 +02:00
|
|
|
int lhs_as = lhs.inet_as ? lhs.inet_as->second : 0;
|
|
|
|
int rhs_as = rhs.inet_as ? rhs.inet_as->second : 0;
|
|
|
|
if (lhs_as != rhs_as) return lhs_as > rhs_as;
|
2008-04-24 09:49:23 +02:00
|
|
|
}
|
|
|
|
#endif
|
2013-01-02 00:12:16 +01:00
|
|
|
boost::uint32_t lhs_peer_rank = lhs.rank(external, external_port);
|
|
|
|
boost::uint32_t rhs_peer_rank = rhs.rank(external, external_port);
|
2012-12-31 07:54:54 +01:00
|
|
|
if (lhs_peer_rank > rhs_peer_rank) return true;
|
2008-04-24 09:49:23 +02:00
|
|
|
return false;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2005-08-18 22:38:03 +02:00
|
|
|
|