premiere-libtorrent/include/libtorrent/peer_list.hpp

286 lines
9.4 KiB
C++

/*
Copyright (c) 2003-2016, Arvid Norberg
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.
*/
#ifndef TORRENT_POLICY_HPP_INCLUDED
#define TORRENT_POLICY_HPP_INCLUDED
#include <algorithm>
#include "libtorrent/string_util.hpp" // for allocate_string_copy
#include "libtorrent/request_blocks.hpp" // for source_rank
#include "libtorrent/torrent_peer.hpp"
#include "libtorrent/piece_picker.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/address.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/ip_voter.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/debug.hpp"
#include "libtorrent/peer_connection_interface.hpp"
#include "libtorrent/aux_/deque.hpp"
#include "libtorrent/peer_info.hpp" // for peer_source_flags_t
#include "libtorrent/string_view.hpp"
namespace libtorrent {
struct ip_filter;
class port_filter;
struct torrent_peer_allocator_interface;
// this object is used to communicate torrent state and
// some configuration to the peer_list object. This make
// the peer_list type not depend on the torrent type directly.
struct torrent_state
{
torrent_state()
: is_paused(false)
, is_finished(false)
, allow_multiple_connections_per_ip(false)
, first_time_seen(false)
, max_peerlist_size(1000)
, min_reconnect_time(60)
, loop_counter(0)
, port(0)
, max_failcount(3)
{}
bool is_paused;
bool is_finished;
bool allow_multiple_connections_per_ip;
// this is set by peer_list::add_peer to either true or false
// true means the peer we just added was new, false means
// we already knew about the peer
bool first_time_seen;
int max_peerlist_size;
int min_reconnect_time;
// the number of iterations over the peer list for this operation
int loop_counter;
// these are used only by find_connect_candidates in order
// to implement peer ranking. See:
// http://blog.libtorrent.org/2012/12/swarm-connectivity/
external_ip ip;
int port;
// the number of times a peer must fail before it's no longer considered
// a connect candidate
int max_failcount;
// if any peer were removed during this call, they are returned in
// this vector. The caller would want to make sure there are no
// references to these torrent_peers anywhere
std::vector<torrent_peer*> erased;
};
class TORRENT_EXTRA_EXPORT peer_list : single_threaded
{
public:
explicit peer_list(torrent_peer_allocator_interface& alloc);
~peer_list();
#if TORRENT_USE_I2P
torrent_peer* add_i2p_peer(string_view destination
, peer_source_flags_t src, char flags
, torrent_state* state);
#endif
enum
{
// these flags match the flags passed in ut_pex
// messages
flag_encryption = 0x1,
flag_seed = 0x2,
flag_utp = 0x4,
flag_holepunch = 0x8
};
// this is called once for every torrent_peer we get from
// the tracker, pex, lsd or dht.
torrent_peer* add_peer(const tcp::endpoint& remote
, peer_source_flags_t source, char flags, torrent_state* state);
// false means duplicate connection
bool update_peer_port(int port, torrent_peer* p, peer_source_flags_t src
, torrent_state* state);
// called when an incoming connection is accepted
// false means the connection was refused or failed
bool new_connection(peer_connection_interface& c, int session_time, torrent_state* state);
// the given connection was just closed
void connection_closed(const peer_connection_interface& c
, int session_time, torrent_state* state);
bool ban_peer(torrent_peer* p);
void set_connection(torrent_peer* p, peer_connection_interface* c);
void set_failcount(torrent_peer* p, int f);
void inc_failcount(torrent_peer* p);
void apply_ip_filter(ip_filter const& filter, torrent_state* state
, std::vector<address>& banned);
void apply_port_filter(port_filter const& filter, torrent_state* state
, std::vector<address>& banned);
void set_seed(torrent_peer* p, bool s);
// this clears all cached peer priorities. It's called when
// our external IP changes
void clear_peer_prio();
#if TORRENT_USE_ASSERTS
bool has_connection(const peer_connection_interface* p);
#endif
#if TORRENT_USE_INVARIANT_CHECKS
void check_invariant() const;
#endif
int num_peers() const { return int(m_peers.size()); }
using peers_t = aux::deque<torrent_peer*>;
using iterator = peers_t::iterator;
using const_iterator = peers_t::const_iterator;
iterator begin() { return m_peers.begin(); }
iterator end() { return m_peers.end(); }
const_iterator begin() const { return m_peers.begin(); }
const_iterator end() const { return m_peers.end(); }
std::pair<iterator, iterator> find_peers(address const& a)
{
#if TORRENT_USE_I2P
if (a == address())
return std::pair<iterator, iterator>(m_peers.end(), m_peers.end());
#endif
return std::equal_range(
m_peers.begin(), m_peers.end(), a, peer_address_compare());
}
std::pair<const_iterator, const_iterator> find_peers(address const& a) const
{
return std::equal_range(
m_peers.begin(), m_peers.end(), a, peer_address_compare());
}
torrent_peer* connect_one_peer(int session_time, torrent_state* state);
bool has_peer(torrent_peer const* p) const;
int num_seeds() const { return int(m_num_seeds); }
int num_connect_candidates() const { return m_num_connect_candidates; }
void erase_peer(torrent_peer* p, torrent_state* state);
void erase_peer(iterator i, torrent_state* state);
void set_max_failcount(torrent_state* st);
private:
// not copyable
peer_list(peer_list const&);
peer_list& operator=(peer_list const&);
void recalculate_connect_candidates(torrent_state* state);
void update_connect_candidates(int delta);
void update_peer(torrent_peer* p, peer_source_flags_t src, int flags
, tcp::endpoint const& remote);
bool insert_peer(torrent_peer* p, iterator iter, int flags, torrent_state* state);
bool compare_peer_erase(torrent_peer const& lhs, torrent_peer const& rhs) const;
bool compare_peer(torrent_peer const* lhs, torrent_peer const* rhs
, external_ip const& external, int source_port) const;
void find_connect_candidates(std::vector<torrent_peer*>& peers
, int session_time, torrent_state* state);
bool is_connect_candidate(torrent_peer const& p) const;
bool is_erase_candidate(torrent_peer const& p) const;
bool is_force_erase_candidate(torrent_peer const& pe) const;
bool should_erase_immediately(torrent_peer const& p) const;
enum flags_t { force_erase = 1 };
void erase_peers(torrent_state* state, int flags = 0);
peers_t m_peers;
// this should be nullptr for the most part. It's set
// to point to a valid torrent_peer object if that
// object needs to be kept alive. If we ever feel
// like removing a torrent_peer from m_peers, we
// first check if the peer matches this one, and
// if so, don't delete it.
torrent_peer* m_locked_peer;
// the peer allocator, as stored from the constructor
// this must be available in the destructor to free all peers
torrent_peer_allocator_interface& m_peer_allocator;
// the number of seeds in the torrent_peer list
std::uint32_t m_num_seeds:31;
// this was the state of the torrent the
// last time we recalculated the number of
// connect candidates. Since seeds (or upload
// only) peers are not connect candidates
// when we're finished, the set depends on
// this state. Every time m_torrent->is_finished()
// is different from this state, we need to
// recalculate the connect candidates.
std::uint32_t m_finished:1;
// since the torrent_peer list can grow too large
// to scan all of it, start at this index
int m_round_robin = 0;
// a list of good connect candidates
std::vector<torrent_peer*> m_candidate_cache;
// The number of peers in our torrent_peer list
// that are connect candidates. i.e. they're
// not already connected and they have not
// yet reached their max try count and they
// have the connectable state (we have a listen
// port for them).
int m_num_connect_candidates = 0;
// if a peer has failed this many times or more, we don't consider
// it a connect candidate anymore.
int m_max_failcount = 3;
};
}
#endif // TORRENT_POLICY_HPP_INCLUDED