forked from premiere/premiere-libtorrent
Merge branch 'RC_1_1'
This commit is contained in:
commit
3a4f957b63
|
@ -84,6 +84,7 @@
|
||||||
* almost completely changed the storage interface (for custom storage)
|
* almost completely changed the storage interface (for custom storage)
|
||||||
* added support for hashing pieces in multiple threads
|
* added support for hashing pieces in multiple threads
|
||||||
|
|
||||||
|
* improve reliability of binding listen sockets
|
||||||
* support SNI in https web seeds and trackers
|
* support SNI in https web seeds and trackers
|
||||||
* fix unhandled exception in DHT when receiving a DHT packet over IPv6
|
* fix unhandled exception in DHT when receiving a DHT packet over IPv6
|
||||||
|
|
||||||
|
|
|
@ -411,9 +411,10 @@ void bind_alert()
|
||||||
class_<listen_failed_alert, bases<alert>, noncopyable>(
|
class_<listen_failed_alert, bases<alert>, noncopyable>(
|
||||||
"listen_failed_alert", no_init)
|
"listen_failed_alert", no_init)
|
||||||
#ifndef TORRENT_NO_DEPRECATE
|
#ifndef TORRENT_NO_DEPRECATE
|
||||||
.def_readonly("interface", &listen_failed_alert::interface)
|
.def_readonly("endpoint", &listen_failed_alert::endpoint)
|
||||||
#endif
|
#endif
|
||||||
.def("listen_interface", &listen_failed_alert::listen_interface)
|
.def("listen_interface", &listen_failed_alert::listen_interface)
|
||||||
|
.def_readonly("port", &listen_failed_alert::port)
|
||||||
.def_readonly("error", &listen_failed_alert::error)
|
.def_readonly("error", &listen_failed_alert::error)
|
||||||
.def_readonly("operation", &listen_failed_alert::operation)
|
.def_readonly("operation", &listen_failed_alert::operation)
|
||||||
.def_readonly("sock_type", &listen_failed_alert::sock_type)
|
.def_readonly("sock_type", &listen_failed_alert::sock_type)
|
||||||
|
|
|
@ -1267,7 +1267,8 @@ namespace libtorrent
|
||||||
// internal
|
// internal
|
||||||
listen_failed_alert(
|
listen_failed_alert(
|
||||||
aux::stack_allocator& alloc
|
aux::stack_allocator& alloc
|
||||||
, std::string iface
|
, std::string const& iface
|
||||||
|
, int port
|
||||||
, int op
|
, int op
|
||||||
, error_code const& ec
|
, error_code const& ec
|
||||||
, socket_type_t t);
|
, socket_type_t t);
|
||||||
|
@ -1277,11 +1278,6 @@ namespace libtorrent
|
||||||
static const int static_category = alert::status_notification | alert::error_notification;
|
static const int static_category = alert::status_notification | alert::error_notification;
|
||||||
virtual std::string message() const TORRENT_OVERRIDE;
|
virtual std::string message() const TORRENT_OVERRIDE;
|
||||||
|
|
||||||
#if !defined(TORRENT_NO_DEPRECATE) && !defined(TORRENT_WINRT)
|
|
||||||
// the interface libtorrent attempted to listen on
|
|
||||||
std::string interface;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// the interface libtorrent attempted to listen on that failed.
|
// the interface libtorrent attempted to listen on that failed.
|
||||||
char const* listen_interface() const;
|
char const* listen_interface() const;
|
||||||
|
|
||||||
|
@ -1296,8 +1292,17 @@ namespace libtorrent
|
||||||
// the specific low level operation that failed. See op_t.
|
// the specific low level operation that failed. See op_t.
|
||||||
int operation;
|
int operation;
|
||||||
|
|
||||||
|
// the port attempted to be opened for listening
|
||||||
|
int port;
|
||||||
|
|
||||||
// the type of listen socket this alert refers to.
|
// the type of listen socket this alert refers to.
|
||||||
socket_type_t sock_type;
|
socket_type_t sock_type;
|
||||||
|
|
||||||
|
#ifndef TORRENT_NO_DEPRECATE
|
||||||
|
// the address and port libtorrent attempted to listen on
|
||||||
|
tcp::endpoint endpoint;
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
aux::stack_allocator const& m_alloc;
|
aux::stack_allocator const& m_alloc;
|
||||||
int m_interface_idx;
|
int m_interface_idx;
|
||||||
|
|
|
@ -852,9 +852,9 @@ namespace libtorrent
|
||||||
tcp::endpoint m_listen_interface;
|
tcp::endpoint m_listen_interface;
|
||||||
|
|
||||||
// the network interfaces outgoing connections are opened through. If
|
// the network interfaces outgoing connections are opened through. If
|
||||||
// there is more then one, they are used in a round-robin fasion
|
// there is more then one, they are used in a round-robin fashion
|
||||||
// each element is a device name or IP address (in string form) and
|
// each element is a device name or IP address (in string form) and
|
||||||
// a port number. The port determins which port to bind the listen
|
// a port number. The port determines which port to bind the listen
|
||||||
// socket to, and the device or IP determines which network adapter
|
// socket to, and the device or IP determines which network adapter
|
||||||
// to be used. If no adapter with the specified name exists, the listen
|
// to be used. If no adapter with the specified name exists, the listen
|
||||||
// socket fails.
|
// socket fails.
|
||||||
|
@ -898,7 +898,8 @@ namespace libtorrent
|
||||||
};
|
};
|
||||||
|
|
||||||
listen_socket_t setup_listener(std::string const& device
|
listen_socket_t setup_listener(std::string const& device
|
||||||
, bool ipv4, int port, int flags, error_code& ec);
|
, boost::asio::ip::tcp const& protocol, int port, int flags
|
||||||
|
, error_code& ec);
|
||||||
|
|
||||||
#ifndef TORRENT_DISABLE_DHT
|
#ifndef TORRENT_DISABLE_DHT
|
||||||
entry m_dht_state;
|
entry m_dht_state;
|
||||||
|
@ -979,7 +980,12 @@ namespace libtorrent
|
||||||
|
|
||||||
time_point m_created;
|
time_point m_created;
|
||||||
boost::int64_t session_time() const TORRENT_OVERRIDE
|
boost::int64_t session_time() const TORRENT_OVERRIDE
|
||||||
{ return total_seconds(aux::time_now() - m_created); }
|
{
|
||||||
|
// +1 is here to make it possible to distinguish uninitialized (to
|
||||||
|
// 0) timestamps and timestamps of things that happend during the
|
||||||
|
// first second after the session was constructed
|
||||||
|
return total_seconds(aux::time_now() - m_created) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
time_point m_last_tick;
|
time_point m_last_tick;
|
||||||
time_point m_last_second_tick;
|
time_point m_last_second_tick;
|
||||||
|
@ -1170,15 +1176,19 @@ namespace libtorrent
|
||||||
typedef std::list<boost::shared_ptr<plugin> > ses_extension_list_t;
|
typedef std::list<boost::shared_ptr<plugin> > ses_extension_list_t;
|
||||||
ses_extension_list_t m_ses_extensions;
|
ses_extension_list_t m_ses_extensions;
|
||||||
|
|
||||||
// std::string could be used for the query names if only all common implementations used SSO
|
// the union of all session extensions' implemented_features(). This is
|
||||||
// *glares at gcc*
|
// used to exclude callbacks to the session extensions.
|
||||||
struct extention_dht_query
|
boost::uint32_t m_session_extension_features;
|
||||||
|
|
||||||
|
// std::string could be used for the query names if only all common
|
||||||
|
// implementations used SSO *glares at gcc*
|
||||||
|
struct extension_dht_query
|
||||||
{
|
{
|
||||||
boost::uint8_t query_len;
|
boost::uint8_t query_len;
|
||||||
boost::array<char, max_dht_query_length> query;
|
boost::array<char, max_dht_query_length> query;
|
||||||
dht_extension_handler_t handler;
|
dht_extension_handler_t handler;
|
||||||
};
|
};
|
||||||
typedef std::vector<extention_dht_query> m_extension_dht_queries_t;
|
typedef std::vector<extension_dht_query> m_extension_dht_queries_t;
|
||||||
m_extension_dht_queries_t m_extension_dht_queries;
|
m_extension_dht_queries_t m_extension_dht_queries;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ namespace libtorrent
|
||||||
};
|
};
|
||||||
|
|
||||||
~bt_peer_connection();
|
~bt_peer_connection();
|
||||||
|
|
||||||
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
|
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
|
||||||
bool supports_encryption() const
|
bool supports_encryption() const
|
||||||
{ return m_encrypted; }
|
{ return m_encrypted; }
|
||||||
|
|
|
@ -117,7 +117,8 @@ namespace libtorrent
|
||||||
// in case SO_BINDTODEVICE succeeded and we don't need to verify it).
|
// in case SO_BINDTODEVICE succeeded and we don't need to verify it).
|
||||||
template <class Socket>
|
template <class Socket>
|
||||||
address bind_to_device(io_service& ios, Socket& sock
|
address bind_to_device(io_service& ios, Socket& sock
|
||||||
, bool ipv4, char const* device_name, int port, error_code& ec)
|
, boost::asio::ip::tcp const& protocol
|
||||||
|
, char const* device_name, int port, error_code& ec)
|
||||||
{
|
{
|
||||||
tcp::endpoint bind_ep(address_v4::any(), port);
|
tcp::endpoint bind_ep(address_v4::any(), port);
|
||||||
|
|
||||||
|
@ -127,8 +128,8 @@ namespace libtorrent
|
||||||
#if TORRENT_USE_IPV6
|
#if TORRENT_USE_IPV6
|
||||||
// this is to cover the case where "0.0.0.0" is considered any IPv4 or
|
// this is to cover the case where "0.0.0.0" is considered any IPv4 or
|
||||||
// IPv6 address. If we're asking to be bound to an IPv6 address and
|
// IPv6 address. If we're asking to be bound to an IPv6 address and
|
||||||
// providing 0.0.0.0 as the device, turn it into "::0"
|
// providing 0.0.0.0 as the device, turn it into "::"
|
||||||
if (ip == address_v4::any() && !ipv4)
|
if (ip == address_v4::any() && protocol == boost::asio::ip::tcp::v6())
|
||||||
ip = address_v6::any();
|
ip = address_v6::any();
|
||||||
#endif
|
#endif
|
||||||
bind_ep.address(ip);
|
bind_ep.address(ip);
|
||||||
|
@ -160,7 +161,7 @@ namespace libtorrent
|
||||||
// (which must be of the same family as the address we're
|
// (which must be of the same family as the address we're
|
||||||
// connecting to)
|
// connecting to)
|
||||||
if (strcmp(ifs[i].name, device_name) != 0) continue;
|
if (strcmp(ifs[i].name, device_name) != 0) continue;
|
||||||
if (ifs[i].interface_address.is_v4() != ipv4)
|
if (ifs[i].interface_address.is_v4() != (protocol == boost::asio::ip::tcp::v4()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bind_ep.address(ifs[i].interface_address);
|
bind_ep.address(ifs[i].interface_address);
|
||||||
|
|
|
@ -208,6 +208,27 @@ namespace libtorrent
|
||||||
// hidden
|
// hidden
|
||||||
virtual ~plugin() {}
|
virtual ~plugin() {}
|
||||||
|
|
||||||
|
// these are flags that can be returned by implemented_features()
|
||||||
|
// indicating which callbacks this plugin is interested in
|
||||||
|
enum feature_flags_t
|
||||||
|
{
|
||||||
|
// include this bit if your plugin needs to alter the order of the
|
||||||
|
// optimistic unchoke of peers. i.e. have the on_optimistic_unchoke()
|
||||||
|
// callback be called.
|
||||||
|
optimistic_unchoke_feature = 1,
|
||||||
|
|
||||||
|
// include this bit if your plugin needs to have on_tick() called
|
||||||
|
tick_feature = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
// This function is expected to return a bitmask indicating which features
|
||||||
|
// this plugin implements. Some callbacks on this object may not be called
|
||||||
|
// unless the corresponding feature flag is returned here. Note that
|
||||||
|
// callbacks may still be called even if the corresponding feature is not
|
||||||
|
// specified in the return value here. See feature_flags_t for possible
|
||||||
|
// flags to return.
|
||||||
|
virtual boost::uint32_t implemented_features() { return 0; }
|
||||||
|
|
||||||
// this is called by the session every time a new torrent is added.
|
// this is called by the session every time a new torrent is added.
|
||||||
// The ``torrent*`` points to the internal torrent object created
|
// The ``torrent*`` points to the internal torrent object created
|
||||||
// for the new torrent. The ``void*`` is the userdata pointer as
|
// for the new torrent. The ``void*`` is the userdata pointer as
|
||||||
|
@ -237,12 +258,13 @@ namespace libtorrent
|
||||||
// called once per second
|
// called once per second
|
||||||
virtual void on_tick() {}
|
virtual void on_tick() {}
|
||||||
|
|
||||||
// called when choosing peers to optimisticly unchoke
|
// called when choosing peers to optimisticallly unchoke. peer's will be
|
||||||
// peer's will be unchoked in the order they appear in the given
|
// unchoked in the order they appear in the given vector. if
|
||||||
// vector which is initiallity sorted by when they were last
|
// the plugin returns true then the ordering provided will be used and no
|
||||||
// optimistically unchoked.
|
// other plugin will be allowed to change it. If your plugin expects this
|
||||||
// if the plugin returns true then the ordering provided will be
|
// to be called, make sure to include the flag
|
||||||
// used and no other plugin will be allowed to change it.
|
// ``optimistic_unchoke_feature`` in the return value from
|
||||||
|
// implemented_features().
|
||||||
virtual bool on_optimistic_unchoke(std::vector<peer_connection_handle>& /* peers */)
|
virtual bool on_optimistic_unchoke(std::vector<peer_connection_handle>& /* peers */)
|
||||||
{ return false; }
|
{ return false; }
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace libtorrent
|
||||||
|
|
||||||
#ifndef TORRENT_NO_DEPRECATE
|
#ifndef TORRENT_NO_DEPRECATE
|
||||||
// constructor function for the metadata transfer extension. This
|
// constructor function for the metadata transfer extension. This
|
||||||
// extension has been superceded by the ut_metadata extension and
|
// extension has been superseded by the ut_metadata extension and
|
||||||
// is deprecated. It can be either be passed in the
|
// is deprecated. It can be either be passed in the
|
||||||
// add_torrent_params::extensions field, or
|
// add_torrent_params::extensions field, or
|
||||||
// via torrent_handle::add_extension().
|
// via torrent_handle::add_extension().
|
||||||
|
|
|
@ -50,6 +50,7 @@ struct crypto_plugin;
|
||||||
|
|
||||||
typedef boost::system::error_code error_code;
|
typedef boost::system::error_code error_code;
|
||||||
|
|
||||||
|
// hidden
|
||||||
struct TORRENT_EXPORT peer_connection_handle
|
struct TORRENT_EXPORT peer_connection_handle
|
||||||
{
|
{
|
||||||
peer_connection_handle(boost::weak_ptr<peer_connection> impl)
|
peer_connection_handle(boost::weak_ptr<peer_connection> impl)
|
||||||
|
@ -113,11 +114,11 @@ struct TORRENT_EXPORT peer_connection_handle
|
||||||
time_point time_of_last_unchoke() const;
|
time_point time_of_last_unchoke() const;
|
||||||
|
|
||||||
bool operator==(peer_connection_handle const& o) const
|
bool operator==(peer_connection_handle const& o) const
|
||||||
{ return m_connection.lock() == o.m_connection.lock(); }
|
{ return !(m_connection < o.m_connection) && !(o.m_connection < m_connection); }
|
||||||
bool operator!=(peer_connection_handle const& o) const
|
bool operator!=(peer_connection_handle const& o) const
|
||||||
{ return m_connection.lock() != o.m_connection.lock(); }
|
{ return m_connection < o.m_connection || o.m_connection < m_connection; }
|
||||||
bool operator<(peer_connection_handle const& o) const
|
bool operator<(peer_connection_handle const& o) const
|
||||||
{ return m_connection.lock() < o.m_connection.lock(); }
|
{ return m_connection < o.m_connection; }
|
||||||
|
|
||||||
boost::shared_ptr<peer_connection> native_handle() const
|
boost::shared_ptr<peer_connection> native_handle() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -819,7 +819,7 @@ namespace libtorrent
|
||||||
// the job and either write it to disk or insert it in the write
|
// the job and either write it to disk or insert it in the write
|
||||||
// cache. When this limit is reached, the peer connections will stop
|
// cache. When this limit is reached, the peer connections will stop
|
||||||
// reading data from their sockets, until the disk thread catches up.
|
// reading data from their sockets, until the disk thread catches up.
|
||||||
// Setting this too low will severly limit your download rate.
|
// Setting this too low will severely limit your download rate.
|
||||||
max_queued_disk_bytes,
|
max_queued_disk_bytes,
|
||||||
|
|
||||||
// the number of seconds to wait for a handshake response from a peer.
|
// the number of seconds to wait for a handshake response from a peer.
|
||||||
|
@ -1119,7 +1119,7 @@ namespace libtorrent
|
||||||
// read cache when a read cache miss occurs. Setting this to 0 is
|
// read cache when a read cache miss occurs. Setting this to 0 is
|
||||||
// essentially the same thing as disabling read cache. The number of
|
// essentially the same thing as disabling read cache. The number of
|
||||||
// blocks read into the read cache is always capped by the piece
|
// blocks read into the read cache is always capped by the piece
|
||||||
// boundry.
|
// boundary.
|
||||||
//
|
//
|
||||||
// When a piece in the write cache has ``write_cache_line_size``
|
// When a piece in the write cache has ``write_cache_line_size``
|
||||||
// contiguous blocks in it, they will be flushed. Setting this to 1
|
// contiguous blocks in it, they will be flushed. Setting this to 1
|
||||||
|
@ -1186,7 +1186,7 @@ namespace libtorrent
|
||||||
// reciprocation rate.
|
// reciprocation rate.
|
||||||
//
|
//
|
||||||
// ``increase_est_reciprocation_rate`` specifies how many percent the
|
// ``increase_est_reciprocation_rate`` specifies how many percent the
|
||||||
// extimated reciprocation rate should be increased by each unchoke
|
// estimated reciprocation rate should be increased by each unchoke
|
||||||
// interval a peer is still choking us back. This defaults to 20%.
|
// interval a peer is still choking us back. This defaults to 20%.
|
||||||
// This only applies to the BitTyrant choker.
|
// This only applies to the BitTyrant choker.
|
||||||
//
|
//
|
||||||
|
@ -1200,7 +1200,7 @@ namespace libtorrent
|
||||||
|
|
||||||
// the max number of peers we accept from pex messages from a single
|
// the max number of peers we accept from pex messages from a single
|
||||||
// peer. this limits the number of concurrent peers any of our peers
|
// peer. this limits the number of concurrent peers any of our peers
|
||||||
// claims to be connected to. If they clain to be connected to more
|
// claims to be connected to. If they claim to be connected to more
|
||||||
// than this, we'll ignore any peer that exceeds this limit
|
// than this, we'll ignore any peer that exceeds this limit
|
||||||
max_pex_peers,
|
max_pex_peers,
|
||||||
|
|
||||||
|
|
|
@ -84,29 +84,16 @@ namespace libtorrent
|
||||||
using boost::asio::null_buffers;
|
using boost::asio::null_buffers;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if TORRENT_USE_IPV6
|
|
||||||
#ifdef IPV6_V6ONLY
|
|
||||||
struct v6only
|
|
||||||
{
|
|
||||||
v6only(bool enable): m_value(enable) {}
|
|
||||||
template<class Protocol>
|
|
||||||
int level(Protocol const&) const { return IPPROTO_IPV6; }
|
|
||||||
template<class Protocol>
|
|
||||||
int name(Protocol const&) const { return IPV6_V6ONLY; }
|
|
||||||
template<class Protocol>
|
|
||||||
int const* data(Protocol const&) const { return &m_value; }
|
|
||||||
template<class Protocol>
|
|
||||||
size_t size(Protocol const&) const { return sizeof(m_value); }
|
|
||||||
int m_value;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TORRENT_WINDOWS
|
#ifdef TORRENT_WINDOWS
|
||||||
|
|
||||||
|
#ifndef PROTECTION_LEVEL_UNRESTRICTED
|
||||||
|
#define PROTECTION_LEVEL_UNRESTRICTED 10
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef IPV6_PROTECTION_LEVEL
|
#ifndef IPV6_PROTECTION_LEVEL
|
||||||
#define IPV6_PROTECTION_LEVEL 30
|
#define IPV6_PROTECTION_LEVEL 30
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct v6_protection_level
|
struct v6_protection_level
|
||||||
{
|
{
|
||||||
v6_protection_level(int level): m_value(level) {}
|
v6_protection_level(int level): m_value(level) {}
|
||||||
|
@ -120,7 +107,21 @@ namespace libtorrent
|
||||||
size_t size(Protocol const&) const { return sizeof(m_value); }
|
size_t size(Protocol const&) const { return sizeof(m_value); }
|
||||||
int m_value;
|
int m_value;
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
struct exclusive_address_use
|
||||||
|
{
|
||||||
|
exclusive_address_use(int enable): m_value(enable) {}
|
||||||
|
template<class Protocol>
|
||||||
|
int level(Protocol const&) const { return SOL_SOCKET; }
|
||||||
|
template<class Protocol>
|
||||||
|
int name(Protocol const&) const { return SO_EXCLUSIVEADDRUSE; }
|
||||||
|
template<class Protocol>
|
||||||
|
int const* data(Protocol const&) const { return &m_value; }
|
||||||
|
template<class Protocol>
|
||||||
|
size_t size(Protocol const&) const { return sizeof(m_value); }
|
||||||
|
int m_value;
|
||||||
|
};
|
||||||
|
#endif // TORRENT_WINDOWS
|
||||||
|
|
||||||
#ifdef IPV6_TCLASS
|
#ifdef IPV6_TCLASS
|
||||||
struct traffic_class
|
struct traffic_class
|
||||||
|
|
|
@ -889,7 +889,7 @@ namespace libtorrent
|
||||||
, std::string const& dh_params);
|
, std::string const& dh_params);
|
||||||
|
|
||||||
// Returns the storage implementation for this torrent. This depends on the
|
// Returns the storage implementation for this torrent. This depends on the
|
||||||
// storage contructor function that was passed to add_torrent.
|
// storage constructor function that was passed to add_torrent.
|
||||||
storage_interface* get_storage_impl() const;
|
storage_interface* get_storage_impl() const;
|
||||||
|
|
||||||
// Returns a pointer to the torrent_info object associated with this
|
// Returns a pointer to the torrent_info object associated with this
|
||||||
|
|
|
@ -23,6 +23,7 @@ project
|
||||||
;
|
;
|
||||||
|
|
||||||
alias libtorrent-sims :
|
alias libtorrent-sims :
|
||||||
|
[ run test_optimistic_unchoke.cpp ]
|
||||||
[ run test_transfer.cpp ]
|
[ run test_transfer.cpp ]
|
||||||
[ run test_http_connection.cpp ]
|
[ run test_http_connection.cpp ]
|
||||||
[ run test_auto_manage.cpp ]
|
[ run test_auto_manage.cpp ]
|
||||||
|
|
|
@ -236,7 +236,7 @@ void setup_swarm(int num_nodes
|
||||||
, std::function<void(lt::alert const*, lt::session&)> on_alert
|
, std::function<void(lt::alert const*, lt::session&)> on_alert
|
||||||
, std::function<int(int, lt::session&)> terminate)
|
, std::function<int(int, lt::session&)> terminate)
|
||||||
{
|
{
|
||||||
asio::io_service ios(sim, addr("0.0.0.0"));
|
asio::io_service ios(sim);
|
||||||
lt::time_point start_time(lt::clock_type::now());
|
lt::time_point start_time(lt::clock_type::now());
|
||||||
|
|
||||||
std::vector<boost::shared_ptr<lt::session> > nodes;
|
std::vector<boost::shared_ptr<lt::session> > nodes;
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 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.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "setup_swarm.hpp"
|
||||||
|
#include "test.hpp"
|
||||||
|
#include "create_torrent.hpp"
|
||||||
|
#include "bittorrent_peer.hpp"
|
||||||
|
#include "settings.hpp"
|
||||||
|
#include "print_alerts.hpp"
|
||||||
|
|
||||||
|
#include "libtorrent/alert.hpp"
|
||||||
|
#include "libtorrent/alert_types.hpp"
|
||||||
|
#include "libtorrent/session.hpp"
|
||||||
|
#include "libtorrent/session_stats.hpp"
|
||||||
|
#include "libtorrent/io_service.hpp"
|
||||||
|
#include "libtorrent/torrent_info.hpp"
|
||||||
|
#include "libtorrent/deadline_timer.hpp"
|
||||||
|
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <boost/ref.hpp>
|
||||||
|
|
||||||
|
struct choke_state
|
||||||
|
{
|
||||||
|
choke_state() : unchoke_duration(lt::seconds(0)), choked(true) {}
|
||||||
|
lt::time_duration unchoke_duration;
|
||||||
|
lt::time_point last_unchoke;
|
||||||
|
bool choked;
|
||||||
|
};
|
||||||
|
|
||||||
|
TORRENT_TEST(optimistic_unchoke)
|
||||||
|
{
|
||||||
|
int const num_nodes = 20;
|
||||||
|
lt::time_duration const test_duration = libtorrent::seconds(1201);
|
||||||
|
|
||||||
|
dsl_config network_cfg;
|
||||||
|
sim::simulation sim{network_cfg};
|
||||||
|
|
||||||
|
io_service ios(sim, addr("50.1.0.0"));
|
||||||
|
lt::time_point start_time(lt::clock_type::now());
|
||||||
|
|
||||||
|
libtorrent::add_torrent_params atp = create_torrent(0);
|
||||||
|
atp.flags &= ~add_torrent_params::flag_auto_managed;
|
||||||
|
atp.flags &= ~add_torrent_params::flag_paused;
|
||||||
|
|
||||||
|
lt::settings_pack pack = settings();
|
||||||
|
// only allow an optimistic unchoke slot
|
||||||
|
pack.set_int(settings_pack::unchoke_slots_limit, 1);
|
||||||
|
pack.set_int(settings_pack::num_optimistic_unchoke_slots, 1);
|
||||||
|
|
||||||
|
std::vector<choke_state> peer_choke_state(num_nodes);
|
||||||
|
|
||||||
|
session_proxy proxy;
|
||||||
|
|
||||||
|
boost::shared_ptr<lt::session> ses = boost::make_shared<lt::session>(
|
||||||
|
boost::ref(pack), boost::ref(ios));
|
||||||
|
ses->async_add_torrent(atp);
|
||||||
|
|
||||||
|
std::vector<boost::shared_ptr<sim::asio::io_service> > io_service;
|
||||||
|
std::vector<boost::shared_ptr<peer_conn> > peers;
|
||||||
|
|
||||||
|
ses->set_alert_notify([&]() {
|
||||||
|
// this function is called inside libtorrent and we cannot perform work
|
||||||
|
// immediately in it. We have to notify the outside to pull all the alerts
|
||||||
|
ios.post(boost::bind(&print_alerts, ses.get(), start_time));
|
||||||
|
});
|
||||||
|
|
||||||
|
lt::deadline_timer timer(ios);
|
||||||
|
timer.expires_from_now(libtorrent::seconds(2));
|
||||||
|
timer.async_wait([&](error_code const& ec)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < num_nodes; ++i)
|
||||||
|
{
|
||||||
|
// create a new io_service
|
||||||
|
char ep[30];
|
||||||
|
snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff);
|
||||||
|
io_service.push_back(boost::make_shared<sim::asio::io_service>(
|
||||||
|
boost::ref(sim), addr(ep)));
|
||||||
|
peers.push_back(boost::make_shared<peer_conn>(boost::ref(*io_service.back())
|
||||||
|
, [&,i](int msg, char const* bug, int len)
|
||||||
|
{
|
||||||
|
choke_state& cs = peer_choke_state[i];
|
||||||
|
if (msg == 0)
|
||||||
|
{
|
||||||
|
// choke
|
||||||
|
if (!cs.choked)
|
||||||
|
{
|
||||||
|
cs.choked = true;
|
||||||
|
cs.unchoke_duration += lt::clock_type::now() - cs.last_unchoke;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msg == 1)
|
||||||
|
{
|
||||||
|
// unchoke
|
||||||
|
if (cs.choked)
|
||||||
|
{
|
||||||
|
cs.choked = false;
|
||||||
|
cs.last_unchoke = lt::clock_type::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char const* msg_str[] = {"choke", "unchoke"};
|
||||||
|
|
||||||
|
lt::time_duration d = lt::clock_type::now() - start_time;
|
||||||
|
boost::uint32_t millis = lt::duration_cast<lt::milliseconds>(d).count();
|
||||||
|
printf("\x1b[35m%4d.%03d: [%d] %s (%d ms)\x1b[0m\n"
|
||||||
|
, millis / 1000, millis % 1000, i, msg_str[msg]
|
||||||
|
, int(lt::duration_cast<lt::milliseconds>(cs.unchoke_duration).count()));
|
||||||
|
}
|
||||||
|
, *atp.ti
|
||||||
|
, tcp::endpoint(addr("50.1.0.0"), 6881)
|
||||||
|
, peer_conn::idle));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
lt::deadline_timer end_timer(ios);
|
||||||
|
timer.expires_from_now(test_duration);
|
||||||
|
timer.async_wait([&](error_code const& ec)
|
||||||
|
{
|
||||||
|
for (auto& p : peers)
|
||||||
|
{
|
||||||
|
p->abort();
|
||||||
|
}
|
||||||
|
proxy = ses->abort();
|
||||||
|
ses.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
sim.run();
|
||||||
|
|
||||||
|
boost::int64_t const duration_ms = lt::duration_cast<lt::milliseconds>(test_duration).count();
|
||||||
|
boost::int64_t const average_unchoke_time = duration_ms / num_nodes;
|
||||||
|
printf("EXPECT: %" PRId64 " ms\n", average_unchoke_time);
|
||||||
|
for (auto const& cs : peer_choke_state)
|
||||||
|
{
|
||||||
|
boost::int64_t unchoke_duration = lt::duration_cast<lt::milliseconds>(cs.unchoke_duration).count();
|
||||||
|
printf("%" PRId64 " ms\n", unchoke_duration);
|
||||||
|
TEST_CHECK(std::abs(unchoke_duration - average_unchoke_time) < 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -786,21 +786,31 @@ namespace libtorrent {
|
||||||
"HTTPS",
|
"HTTPS",
|
||||||
"SSL/uTP"
|
"SSL/uTP"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef TORRENT_NO_DEPRECATE
|
||||||
|
tcp::endpoint parse_interface(std::string const& iface, int port)
|
||||||
|
{
|
||||||
|
// ignore errors
|
||||||
|
error_code ec;
|
||||||
|
return tcp::endpoint(address::from_string(iface, ec), port);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
listen_failed_alert::listen_failed_alert(
|
listen_failed_alert::listen_failed_alert(
|
||||||
aux::stack_allocator& alloc
|
aux::stack_allocator& alloc
|
||||||
, std::string iface
|
, std::string const& iface
|
||||||
|
, int prt
|
||||||
, int op
|
, int op
|
||||||
, error_code const& ec
|
, error_code const& ec
|
||||||
, socket_type_t t)
|
, socket_type_t t)
|
||||||
:
|
: error(ec)
|
||||||
#if !defined(TORRENT_NO_DEPRECATE) && !defined(TORRENT_WINRT)
|
|
||||||
interface(iface),
|
|
||||||
#endif
|
|
||||||
error(ec)
|
|
||||||
, operation(op)
|
, operation(op)
|
||||||
|
, port(prt)
|
||||||
, sock_type(t)
|
, sock_type(t)
|
||||||
|
#ifndef TORRENT_NO_DEPRECATE
|
||||||
|
, endpoint(parse_interface(iface, prt))
|
||||||
|
#endif
|
||||||
, m_alloc(alloc)
|
, m_alloc(alloc)
|
||||||
, m_interface_idx(alloc.copy_string(iface))
|
, m_interface_idx(alloc.copy_string(iface))
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -693,13 +693,8 @@ namespace libtorrent
|
||||||
address get_default_gateway(io_service& ios, error_code& ec)
|
address get_default_gateway(io_service& ios, error_code& ec)
|
||||||
{
|
{
|
||||||
std::vector<ip_route> ret = enum_routes(ios, ec);
|
std::vector<ip_route> ret = enum_routes(ios, ec);
|
||||||
#if defined TORRENT_WINDOWS || defined TORRENT_MINGW
|
|
||||||
std::vector<ip_route>::iterator i = std::find_if(ret.begin(), ret.end()
|
|
||||||
, boost::bind(&is_loopback, boost::bind(&ip_route::destination, _1)));
|
|
||||||
#else
|
|
||||||
std::vector<ip_route>::iterator i = std::find_if(ret.begin(), ret.end()
|
std::vector<ip_route>::iterator i = std::find_if(ret.begin(), ret.end()
|
||||||
, boost::bind(&ip_route::destination, _1) == address());
|
, boost::bind(&ip_route::destination, _1) == address());
|
||||||
#endif
|
|
||||||
if (i == ret.end()) return address();
|
if (i == ret.end()) return address();
|
||||||
return i->gateway;
|
return i->gateway;
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,7 @@ namespace libtorrent { namespace
|
||||||
m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size);
|
m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_piece_pass(int)
|
void on_piece_pass(int) TORRENT_OVERRIDE
|
||||||
{
|
{
|
||||||
// if we became a seed, copy the metadata from
|
// if we became a seed, copy the metadata from
|
||||||
// the torrent before it is deallocated
|
// the torrent before it is deallocated
|
||||||
|
|
|
@ -1658,8 +1658,8 @@ namespace libtorrent
|
||||||
|
|
||||||
m_peer_interested = true;
|
m_peer_interested = true;
|
||||||
if (is_disconnecting()) return;
|
if (is_disconnecting()) return;
|
||||||
|
|
||||||
// if the peer is ready to download stuff, it must have metadata
|
// if the peer is ready to download stuff, it must have metadata
|
||||||
m_has_metadata = true;
|
m_has_metadata = true;
|
||||||
|
|
||||||
disconnect_if_redundant();
|
disconnect_if_redundant();
|
||||||
|
@ -4619,10 +4619,14 @@ namespace libtorrent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int download_rate = statistics().download_payload_rate();
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
|
int const previous_queue_size = m_desired_queue_size;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int const download_rate = statistics().download_payload_rate();
|
||||||
|
|
||||||
// the desired download queue size
|
// the desired download queue size
|
||||||
const int queue_time = m_settings.get_int(settings_pack::request_queue_time);
|
int const queue_time = m_settings.get_int(settings_pack::request_queue_time);
|
||||||
|
|
||||||
// when we're in slow-start mode we increase the desired queue size every
|
// when we're in slow-start mode we increase the desired queue size every
|
||||||
// time we receive a piece, no need to adjust it here (other than
|
// time we receive a piece, no need to adjust it here (other than
|
||||||
|
@ -4636,7 +4640,7 @@ namespace libtorrent
|
||||||
// the block size doesn't have to be 16. So we first query the
|
// the block size doesn't have to be 16. So we first query the
|
||||||
// torrent for it
|
// torrent for it
|
||||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||||
const int block_size = t->block_size();
|
int const block_size = t->block_size();
|
||||||
|
|
||||||
TORRENT_ASSERT(block_size > 0);
|
TORRENT_ASSERT(block_size > 0);
|
||||||
|
|
||||||
|
@ -4649,10 +4653,13 @@ namespace libtorrent
|
||||||
m_desired_queue_size = min_request_queue;
|
m_desired_queue_size = min_request_queue;
|
||||||
|
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
peer_log(peer_log_alert::info, "UPDATE_QUEUE_SIZE"
|
if (previous_queue_size != m_desired_queue_size)
|
||||||
, "dqs: %d max: %d dl: %d qt: %d snubbed: %d slow-start: %d"
|
{
|
||||||
, m_desired_queue_size, m_max_out_request_queue
|
peer_log(peer_log_alert::info, "UPDATE_QUEUE_SIZE"
|
||||||
, download_rate, queue_time, int(m_snubbed), int(m_slow_start));
|
, "dqs: %d max: %d dl: %d qt: %d snubbed: %d slow-start: %d"
|
||||||
|
, m_desired_queue_size, m_max_out_request_queue
|
||||||
|
, download_rate, queue_time, int(m_snubbed), int(m_slow_start));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5092,7 +5099,7 @@ namespace libtorrent
|
||||||
|
|
||||||
bool sent_a_piece = false;
|
bool sent_a_piece = false;
|
||||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||||
if (!t || t->is_aborted()) return;
|
if (!t || t->is_aborted() || m_requests.empty()) return;
|
||||||
|
|
||||||
// only add new piece-chunks if the send buffer is small enough
|
// only add new piece-chunks if the send buffer is small enough
|
||||||
// otherwise there will be no end to how large it will be!
|
// otherwise there will be no end to how large it will be!
|
||||||
|
@ -6681,9 +6688,10 @@ namespace libtorrent
|
||||||
TORRENT_ASSERT(t->torrent_file().num_pieces() == int(m_have_piece.size()));
|
TORRENT_ASSERT(t->torrent_file().num_pieces() == int(m_have_piece.size()));
|
||||||
|
|
||||||
// in share mode we don't close redundant connections
|
// in share mode we don't close redundant connections
|
||||||
if (m_settings.get_bool(settings_pack::close_redundant_connections) && !t->share_mode())
|
if (m_settings.get_bool(settings_pack::close_redundant_connections)
|
||||||
|
&& !t->share_mode())
|
||||||
{
|
{
|
||||||
bool ok_to_disconnect =
|
bool const ok_to_disconnect =
|
||||||
can_disconnect(error_code(errors::upload_upload_connection))
|
can_disconnect(error_code(errors::upload_upload_connection))
|
||||||
|| can_disconnect(error_code(errors::uninteresting_upload_peer))
|
|| can_disconnect(error_code(errors::uninteresting_upload_peer))
|
||||||
|| can_disconnect(error_code(errors::too_many_requests_when_choked))
|
|| can_disconnect(error_code(errors::too_many_requests_when_choked))
|
||||||
|
|
|
@ -51,6 +51,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
#include <boost/function_equal.hpp>
|
#include <boost/function_equal.hpp>
|
||||||
#include <boost/make_shared.hpp>
|
#include <boost/make_shared.hpp>
|
||||||
|
#include <boost/asio/ip/v6_only.hpp>
|
||||||
|
|
||||||
#ifdef TORRENT_USE_VALGRIND
|
#ifdef TORRENT_USE_VALGRIND
|
||||||
#include <valgrind/memcheck.h>
|
#include <valgrind/memcheck.h>
|
||||||
|
@ -429,6 +430,9 @@ namespace aux {
|
||||||
, m_download_connect_attempts(0)
|
, m_download_connect_attempts(0)
|
||||||
, m_next_scrape_torrent(0)
|
, m_next_scrape_torrent(0)
|
||||||
, m_tick_residual(0)
|
, m_tick_residual(0)
|
||||||
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||||
|
, m_session_extension_features(0)
|
||||||
|
#endif
|
||||||
, m_deferred_submit_disk_jobs(false)
|
, m_deferred_submit_disk_jobs(false)
|
||||||
, m_pending_auto_manage(false)
|
, m_pending_auto_manage(false)
|
||||||
, m_need_auto_manage(false)
|
, m_need_auto_manage(false)
|
||||||
|
@ -919,6 +923,7 @@ namespace aux {
|
||||||
boost::shared_ptr<plugin> p(new session_plugin_wrapper(ext));
|
boost::shared_ptr<plugin> p(new session_plugin_wrapper(ext));
|
||||||
|
|
||||||
m_ses_extensions.push_back(p);
|
m_ses_extensions.push_back(p);
|
||||||
|
m_session_extension_features |= p->implemented_features();
|
||||||
}
|
}
|
||||||
|
|
||||||
void session_impl::add_ses_extension(boost::shared_ptr<plugin> ext)
|
void session_impl::add_ses_extension(boost::shared_ptr<plugin> ext)
|
||||||
|
@ -929,6 +934,7 @@ namespace aux {
|
||||||
m_ses_extensions.push_back(ext);
|
m_ses_extensions.push_back(ext);
|
||||||
m_alerts.add_extension(ext);
|
m_alerts.add_extension(ext);
|
||||||
ext->added(session_handle(this));
|
ext->added(session_handle(this));
|
||||||
|
m_session_extension_features |= ext->implemented_features();
|
||||||
|
|
||||||
// get any DHT queries the plugin would like to handle
|
// get any DHT queries the plugin would like to handle
|
||||||
// and record them in m_extension_dht_queries for lookup
|
// and record them in m_extension_dht_queries for lookup
|
||||||
|
@ -940,7 +946,7 @@ namespace aux {
|
||||||
{
|
{
|
||||||
TORRENT_ASSERT(e->first.size() <= max_dht_query_length);
|
TORRENT_ASSERT(e->first.size() <= max_dht_query_length);
|
||||||
if (e->first.size() > max_dht_query_length) continue;
|
if (e->first.size() > max_dht_query_length) continue;
|
||||||
extention_dht_query registration;
|
extension_dht_query registration;
|
||||||
registration.query_len = e->first.size();
|
registration.query_len = e->first.size();
|
||||||
std::copy(e->first.begin(), e->first.end(), registration.query.begin());
|
std::copy(e->first.begin(), e->first.end(), registration.query.begin());
|
||||||
registration.handler = e->second;
|
registration.handler = e->second;
|
||||||
|
@ -1682,7 +1688,7 @@ namespace aux {
|
||||||
enum { listen_no_system_port = 0x02 };
|
enum { listen_no_system_port = 0x02 };
|
||||||
|
|
||||||
listen_socket_t session_impl::setup_listener(std::string const& device
|
listen_socket_t session_impl::setup_listener(std::string const& device
|
||||||
, bool ipv4, int port, int flags, error_code& ec)
|
, boost::asio::ip::tcp const& protocol, int port, int flags, error_code& ec)
|
||||||
{
|
{
|
||||||
int retries = m_settings.get_int(settings_pack::max_retry_port_bind);
|
int retries = m_settings.get_int(settings_pack::max_retry_port_bind);
|
||||||
|
|
||||||
|
@ -1692,12 +1698,13 @@ namespace aux {
|
||||||
listen_failed_alert::socket_type_t sock_type = (flags & open_ssl_socket)
|
listen_failed_alert::socket_type_t sock_type = (flags & open_ssl_socket)
|
||||||
? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp;
|
? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp;
|
||||||
ret.sock.reset(new tcp::acceptor(m_io_service));
|
ret.sock.reset(new tcp::acceptor(m_io_service));
|
||||||
ret.sock->open(ipv4 ? tcp::v4() : tcp::v6(), ec);
|
ret.sock->open(protocol, ec);
|
||||||
last_op = listen_failed_alert::open;
|
last_op = listen_failed_alert::open;
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(device, last_op, ec, sock_type);
|
m_alerts.emplace_alert<listen_failed_alert>(device, port, last_op
|
||||||
|
, ec, sock_type);
|
||||||
|
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
session_log("failed to open socket: %s: %s"
|
session_log("failed to open socket: %s: %s"
|
||||||
|
@ -1706,36 +1713,28 @@ namespace aux {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SO_REUSEADDR on windows is a bit special. It actually allows
|
|
||||||
// two active sockets to bind to the same port. That means we
|
|
||||||
// may end up binding to the same socket as some other random
|
|
||||||
// application. Don't do it!
|
|
||||||
#ifndef TORRENT_WINDOWS
|
|
||||||
{
|
{
|
||||||
error_code err; // ignore errors here
|
// this is best-effort. ignore errors
|
||||||
|
error_code err;
|
||||||
|
#ifdef TORRENT_WINDOWS
|
||||||
|
ret.sock->set_option(exclusive_address_use(true), err);
|
||||||
|
#endif
|
||||||
ret.sock->set_option(tcp::acceptor::reuse_address(true), err);
|
ret.sock->set_option(tcp::acceptor::reuse_address(true), err);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TORRENT_USE_IPV6
|
#if TORRENT_USE_IPV6
|
||||||
if (!ipv4)
|
if (protocol == boost::asio::ip::tcp::v6())
|
||||||
{
|
{
|
||||||
error_code err; // ignore errors here
|
error_code err; // ignore errors here
|
||||||
#ifdef IPV6_V6ONLY
|
ret.sock->set_option(boost::asio::ip::v6_only(true), err);
|
||||||
ret.sock->set_option(v6only(true), err);
|
|
||||||
#endif
|
|
||||||
#ifdef TORRENT_WINDOWS
|
#ifdef TORRENT_WINDOWS
|
||||||
|
|
||||||
#ifndef PROTECTION_LEVEL_UNRESTRICTED
|
|
||||||
#define PROTECTION_LEVEL_UNRESTRICTED 10
|
|
||||||
#endif
|
|
||||||
// enable Teredo on windows
|
// enable Teredo on windows
|
||||||
ret.sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err);
|
ret.sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err);
|
||||||
#endif
|
#endif // TORRENT_WINDOWS
|
||||||
}
|
}
|
||||||
#endif // TORRENT_USE_IPV6
|
#endif // TORRENT_USE_IPV6
|
||||||
|
|
||||||
address bind_ip = bind_to_device(m_io_service, *ret.sock, ipv4
|
address bind_ip = bind_to_device(m_io_service, *ret.sock, protocol
|
||||||
, device.c_str(), port, ec);
|
, device.c_str(), port, ec);
|
||||||
|
|
||||||
while (ec == error_code(error::address_in_use) && retries > 0)
|
while (ec == error_code(error::address_in_use) && retries > 0)
|
||||||
|
@ -1752,7 +1751,7 @@ namespace aux {
|
||||||
TORRENT_ASSERT_VAL(!ec, ec);
|
TORRENT_ASSERT_VAL(!ec, ec);
|
||||||
--retries;
|
--retries;
|
||||||
port += 1;
|
port += 1;
|
||||||
bind_ip = bind_to_device(m_io_service, *ret.sock, ipv4
|
bind_ip = bind_to_device(m_io_service, *ret.sock, protocol
|
||||||
, device.c_str(), port, ec);
|
, device.c_str(), port, ec);
|
||||||
last_op = listen_failed_alert::bind;
|
last_op = listen_failed_alert::bind;
|
||||||
}
|
}
|
||||||
|
@ -1762,7 +1761,7 @@ namespace aux {
|
||||||
// instead of giving up, try let the OS pick a port
|
// instead of giving up, try let the OS pick a port
|
||||||
port = 0;
|
port = 0;
|
||||||
ec.clear();
|
ec.clear();
|
||||||
bind_ip = bind_to_device(m_io_service, *ret.sock, ipv4
|
bind_ip = bind_to_device(m_io_service, *ret.sock, protocol
|
||||||
, device.c_str(), port, ec);
|
, device.c_str(), port, ec);
|
||||||
last_op = listen_failed_alert::bind;
|
last_op = listen_failed_alert::bind;
|
||||||
}
|
}
|
||||||
|
@ -1772,10 +1771,11 @@ namespace aux {
|
||||||
|
|
||||||
// not even that worked, give up
|
// not even that worked, give up
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(device, last_op, ec, sock_type);
|
m_alerts.emplace_alert<listen_failed_alert>(device, port, last_op, ec, sock_type);
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
|
error_code err;
|
||||||
session_log("cannot to bind to interface [%s %d] \"%s : %s\": %s"
|
session_log("cannot to bind to interface [%s %d] \"%s : %s\": %s"
|
||||||
, device.c_str(), port, bind_ip.to_string(ec).c_str()
|
, device.c_str(), port, bind_ip.to_string(err).c_str()
|
||||||
, ec.category().name(), ec.message().c_str());
|
, ec.category().name(), ec.message().c_str());
|
||||||
#endif
|
#endif
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1791,7 +1791,7 @@ namespace aux {
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(device, last_op, ec, sock_type);
|
m_alerts.emplace_alert<listen_failed_alert>(device, port, last_op, ec, sock_type);
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
session_log("cannot listen on interface \"%s\": %s"
|
session_log("cannot listen on interface \"%s\": %s"
|
||||||
, device.c_str(), ec.message().c_str());
|
, device.c_str(), ec.message().c_str());
|
||||||
|
@ -1808,7 +1808,7 @@ namespace aux {
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(device, last_op, ec, sock_type);
|
m_alerts.emplace_alert<listen_failed_alert>(device, port, last_op, ec, sock_type);
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
session_log("failed to get peer name \"%s\": %s"
|
session_log("failed to get peer name \"%s\": %s"
|
||||||
, device.c_str(), ec.message().c_str());
|
, device.c_str(), ec.message().c_str());
|
||||||
|
@ -1856,13 +1856,13 @@ retry:
|
||||||
m_ipv4_interface = tcp::endpoint();
|
m_ipv4_interface = tcp::endpoint();
|
||||||
|
|
||||||
// TODO: instead of having a special case for this, just make the
|
// TODO: instead of having a special case for this, just make the
|
||||||
// default listen interfaces be "0.0.0.0:6881,[::1]:6881" and use
|
// default listen interfaces be "0.0.0.0:6881,[::]:6881" and use
|
||||||
// the generic path. That would even allow for not listening at all.
|
// the generic path. That would even allow for not listening at all.
|
||||||
if (m_listen_interfaces.empty())
|
if (m_listen_interfaces.empty())
|
||||||
{
|
{
|
||||||
// this means we should open two listen sockets
|
// this means we should open two listen sockets
|
||||||
// one for IPv4 and one for IPv6
|
// one for IPv4 and one for IPv6
|
||||||
listen_socket_t s = setup_listener("0.0.0.0", true
|
listen_socket_t s = setup_listener("0.0.0.0", boost::asio::ip::tcp::v4()
|
||||||
, m_listen_interface.port()
|
, m_listen_interface.port()
|
||||||
, flags, ec);
|
, flags, ec);
|
||||||
|
|
||||||
|
@ -1880,7 +1880,7 @@ retry:
|
||||||
#ifdef TORRENT_USE_OPENSSL
|
#ifdef TORRENT_USE_OPENSSL
|
||||||
if (m_settings.get_int(settings_pack::ssl_listen))
|
if (m_settings.get_int(settings_pack::ssl_listen))
|
||||||
{
|
{
|
||||||
s = setup_listener("0.0.0.0", true
|
s = setup_listener("0.0.0.0", boost::asio::ip::tcp::v4()
|
||||||
, m_settings.get_int(settings_pack::ssl_listen)
|
, m_settings.get_int(settings_pack::ssl_listen)
|
||||||
, flags | open_ssl_socket, ec);
|
, flags | open_ssl_socket, ec);
|
||||||
|
|
||||||
|
@ -1896,7 +1896,8 @@ retry:
|
||||||
// only try to open the IPv6 port if IPv6 is installed
|
// only try to open the IPv6 port if IPv6 is installed
|
||||||
if (supports_ipv6())
|
if (supports_ipv6())
|
||||||
{
|
{
|
||||||
s = setup_listener("::1", false, m_listen_interface.port()
|
s = setup_listener("::", boost::asio::ip::tcp::v6()
|
||||||
|
, m_listen_interface.port()
|
||||||
, flags, ec);
|
, flags, ec);
|
||||||
|
|
||||||
if (!ec && s.sock)
|
if (!ec && s.sock)
|
||||||
|
@ -1909,7 +1910,7 @@ retry:
|
||||||
if (m_settings.get_int(settings_pack::ssl_listen))
|
if (m_settings.get_int(settings_pack::ssl_listen))
|
||||||
{
|
{
|
||||||
s.ssl = true;
|
s.ssl = true;
|
||||||
s = setup_listener("::1", false
|
s = setup_listener("::", boost::asio::ip::tcp::v6()
|
||||||
, m_settings.get_int(settings_pack::ssl_listen)
|
, m_settings.get_int(settings_pack::ssl_listen)
|
||||||
, flags | open_ssl_socket, ec);
|
, flags | open_ssl_socket, ec);
|
||||||
|
|
||||||
|
@ -1954,6 +1955,9 @@ retry:
|
||||||
#else
|
#else
|
||||||
const int first_family = 1;
|
const int first_family = 1;
|
||||||
#endif
|
#endif
|
||||||
|
boost::asio::ip::tcp protocol[]
|
||||||
|
= { boost::asio::ip::tcp::v6(), boost::asio::ip::tcp::v4() };
|
||||||
|
|
||||||
for (int address_family = first_family; address_family < 2; ++address_family)
|
for (int address_family = first_family; address_family < 2; ++address_family)
|
||||||
{
|
{
|
||||||
error_code err;
|
error_code err;
|
||||||
|
@ -1963,8 +1967,8 @@ retry:
|
||||||
&& !is_any(test_family))
|
&& !is_any(test_family))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
listen_socket_t s = setup_listener(device, address_family, port
|
listen_socket_t s = setup_listener(device, protocol[address_family]
|
||||||
, flags, ec);
|
, port, flags, ec);
|
||||||
|
|
||||||
if (ec == error_code(boost::system::errc::no_such_device, generic_category()))
|
if (ec == error_code(boost::system::errc::no_such_device, generic_category()))
|
||||||
{
|
{
|
||||||
|
@ -1989,7 +1993,8 @@ retry:
|
||||||
#ifdef TORRENT_USE_OPENSSL
|
#ifdef TORRENT_USE_OPENSSL
|
||||||
if (m_settings.get_int(settings_pack::ssl_listen))
|
if (m_settings.get_int(settings_pack::ssl_listen))
|
||||||
{
|
{
|
||||||
listen_socket_t ssl_s = setup_listener(device, address_family
|
listen_socket_t ssl_s = setup_listener(device
|
||||||
|
, protocol[address_family]
|
||||||
, m_settings.get_int(settings_pack::ssl_listen)
|
, m_settings.get_int(settings_pack::ssl_listen)
|
||||||
, flags | open_ssl_socket, ec);
|
, flags | open_ssl_socket, ec);
|
||||||
|
|
||||||
|
@ -2017,8 +2022,11 @@ retry:
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(print_endpoint(m_listen_interface)
|
m_alerts.emplace_alert<listen_failed_alert>(
|
||||||
, listen_failed_alert::bind, ec, listen_failed_alert::udp);
|
m_listen_interface.address().to_string()
|
||||||
|
, m_listen_interface.port()
|
||||||
|
, listen_failed_alert::bind
|
||||||
|
, ec, listen_failed_alert::tcp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2040,8 +2048,8 @@ retry:
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
{
|
{
|
||||||
error_code err;
|
error_code err;
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(print_endpoint(ssl_bind_if)
|
m_alerts.emplace_alert<listen_failed_alert>(ssl_bind_if.address().to_string()
|
||||||
, listen_failed_alert::bind, ec, listen_failed_alert::utp_ssl);
|
, ssl_port, listen_failed_alert::bind, ec, listen_failed_alert::utp_ssl);
|
||||||
}
|
}
|
||||||
ec.clear();
|
ec.clear();
|
||||||
}
|
}
|
||||||
|
@ -2067,8 +2075,10 @@ retry:
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
{
|
{
|
||||||
error_code err;
|
error_code err;
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(print_endpoint(m_listen_interface)
|
m_alerts.emplace_alert<listen_failed_alert>(m_listen_interface.address().to_string()
|
||||||
, listen_failed_alert::bind, ec, listen_failed_alert::udp);
|
, m_listen_interface.port()
|
||||||
|
, listen_failed_alert::bind
|
||||||
|
, ec, listen_failed_alert::udp);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2282,8 +2292,10 @@ retry:
|
||||||
if (e)
|
if (e)
|
||||||
{
|
{
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
m_alerts.emplace_alert<listen_failed_alert>("i2p", listen_failed_alert::accept
|
m_alerts.emplace_alert<listen_failed_alert>("i2p"
|
||||||
, e, listen_failed_alert::i2p);
|
, m_listen_interface.port()
|
||||||
|
, listen_failed_alert::accept
|
||||||
|
, e, listen_failed_alert::i2p);
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
session_log("cannot bind to port %d: %s"
|
session_log("cannot bind to port %d: %s"
|
||||||
, m_listen_interface.port(), e.message().c_str());
|
, m_listen_interface.port(), e.message().c_str());
|
||||||
|
@ -2421,7 +2433,8 @@ retry:
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
{
|
{
|
||||||
error_code err;
|
error_code err;
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(print_endpoint(ep), listen_failed_alert::accept, e
|
m_alerts.emplace_alert<listen_failed_alert>(ep.address().to_string()
|
||||||
|
, ep.port(), listen_failed_alert::accept, e
|
||||||
, ssl ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp);
|
, ssl ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -2592,8 +2605,9 @@ retry:
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
|
error_code err;
|
||||||
session_log(" rejected connection, not allowed local interface: %s"
|
session_log(" rejected connection, not allowed local interface: %s"
|
||||||
, local.address().to_string(ec).c_str());
|
, local.address().to_string(err).c_str());
|
||||||
#endif
|
#endif
|
||||||
if (m_alerts.should_post<peer_blocked_alert>())
|
if (m_alerts.should_post<peer_blocked_alert>())
|
||||||
m_alerts.emplace_alert<peer_blocked_alert>(torrent_handle()
|
m_alerts.emplace_alert<peer_blocked_alert>(torrent_handle()
|
||||||
|
@ -2756,8 +2770,9 @@ retry:
|
||||||
if (e)
|
if (e)
|
||||||
{
|
{
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
m_alerts.emplace_alert<listen_failed_alert>("socks5", listen_failed_alert::accept, e
|
m_alerts.emplace_alert<listen_failed_alert>("socks5"
|
||||||
, listen_failed_alert::socks5);
|
, -1, listen_failed_alert::accept, e
|
||||||
|
, listen_failed_alert::socks5);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
open_new_incoming_socks_connection();
|
open_new_incoming_socks_connection();
|
||||||
|
@ -3018,8 +3033,8 @@ retry:
|
||||||
m_last_second_tick = now;
|
m_last_second_tick = now;
|
||||||
m_tick_residual += tick_interval_ms - 1000;
|
m_tick_residual += tick_interval_ms - 1000;
|
||||||
|
|
||||||
boost::int64_t session_time = total_seconds(now - m_created);
|
boost::int64_t const stime = session_time();
|
||||||
if (session_time > 65000)
|
if (stime > 65000)
|
||||||
{
|
{
|
||||||
// we're getting close to the point where our timestamps
|
// we're getting close to the point where our timestamps
|
||||||
// in torrent_peer are wrapping. We need to step all counters back
|
// in torrent_peer are wrapping. We need to step all counters back
|
||||||
|
@ -3038,12 +3053,15 @@ retry:
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||||
for (ses_extension_list_t::const_iterator i = m_ses_extensions.begin()
|
if (m_session_extension_features & plugin::tick_feature)
|
||||||
, end(m_ses_extensions.end()); i != end; ++i)
|
|
||||||
{
|
{
|
||||||
TORRENT_TRY {
|
for (ses_extension_list_t::const_iterator i = m_ses_extensions.begin()
|
||||||
(*i)->on_tick();
|
, end(m_ses_extensions.end()); i != end; ++i)
|
||||||
} TORRENT_CATCH(std::exception&) {}
|
{
|
||||||
|
TORRENT_TRY {
|
||||||
|
(*i)->on_tick();
|
||||||
|
} TORRENT_CATCH(std::exception&) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -3755,24 +3773,32 @@ retry:
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct last_optimistic_unchoke_cmp
|
bool last_optimistic_unchoke_cmp(torrent_peer const* const l
|
||||||
|
, torrent_peer const* const r)
|
||||||
{
|
{
|
||||||
bool operator()(peer_connection_handle const& l
|
return l->last_optimistically_unchoked
|
||||||
, peer_connection_handle const& r)
|
< r->last_optimistically_unchoked;
|
||||||
{
|
}
|
||||||
return l.native_handle()->peer_info_struct()->last_optimistically_unchoked
|
|
||||||
< r.native_handle()->peer_info_struct()->last_optimistically_unchoked;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void session_impl::recalculate_optimistic_unchoke_slots()
|
void session_impl::recalculate_optimistic_unchoke_slots()
|
||||||
{
|
{
|
||||||
|
INVARIANT_CHECK;
|
||||||
|
|
||||||
TORRENT_ASSERT(is_single_thread());
|
TORRENT_ASSERT(is_single_thread());
|
||||||
if (m_stats_counters[counters::num_unchoke_slots] == 0) return;
|
if (m_stats_counters[counters::num_unchoke_slots] == 0) return;
|
||||||
|
|
||||||
std::vector<peer_connection_handle> opt_unchoke;
|
std::vector<torrent_peer*> opt_unchoke;
|
||||||
|
|
||||||
|
// collect the currently optimistically unchoked peers here, so we can
|
||||||
|
// choke them when we've found new optimistic unchoke candidates.
|
||||||
|
std::vector<torrent_peer*> prev_opt_unchoke;
|
||||||
|
|
||||||
|
// TODO: 3 it would probably make sense to have a separate list of peers
|
||||||
|
// that are eligible for optimistic unchoke, similar to the torrents
|
||||||
|
// perhaps this could even iterate over the pool allocators of
|
||||||
|
// torrent_peer objects. It could probably be done in a single pass and
|
||||||
|
// collect the n best candidates
|
||||||
for (connection_map::iterator i = m_connections.begin()
|
for (connection_map::iterator i = m_connections.begin()
|
||||||
, end(m_connections.end()); i != end; ++i)
|
, end(m_connections.end()); i != end; ++i)
|
||||||
{
|
{
|
||||||
|
@ -3781,90 +3807,139 @@ retry:
|
||||||
torrent_peer* pi = p->peer_info_struct();
|
torrent_peer* pi = p->peer_info_struct();
|
||||||
if (!pi) continue;
|
if (!pi) continue;
|
||||||
if (pi->web_seed) continue;
|
if (pi->web_seed) continue;
|
||||||
torrent* t = p->associated_torrent().lock().get();
|
|
||||||
if (!t) continue;
|
|
||||||
if (t->is_paused()) continue;
|
|
||||||
|
|
||||||
if (pi->optimistically_unchoked)
|
if (pi->optimistically_unchoked)
|
||||||
{
|
{
|
||||||
TORRENT_ASSERT(!p->is_choked());
|
prev_opt_unchoke.push_back(pi);
|
||||||
opt_unchoke.push_back(peer_connection_handle(*i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
torrent* t = p->associated_torrent().lock().get();
|
||||||
|
if (!t) continue;
|
||||||
|
|
||||||
|
// TODO: 3 peers should know whether their torrent is paused or not,
|
||||||
|
// instead of having to ask it over and over again
|
||||||
|
if (t->is_paused()) continue;
|
||||||
|
|
||||||
if (!p->is_connecting()
|
if (!p->is_connecting()
|
||||||
&& !p->is_disconnecting()
|
&& !p->is_disconnecting()
|
||||||
&& p->is_peer_interested()
|
&& p->is_peer_interested()
|
||||||
&& t->free_upload_slots()
|
&& t->free_upload_slots()
|
||||||
&& p->is_choked()
|
&& (p->is_choked() || pi->optimistically_unchoked)
|
||||||
&& !p->ignore_unchoke_slots()
|
&& !p->ignore_unchoke_slots()
|
||||||
&& t->valid_metadata())
|
&& t->valid_metadata())
|
||||||
{
|
{
|
||||||
opt_unchoke.push_back(peer_connection_handle(*i));
|
opt_unchoke.push_back(pi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the peers that has been waiting the longest to be optimistically
|
// find the peers that has been waiting the longest to be optimistically
|
||||||
// unchoked
|
// unchoked
|
||||||
|
|
||||||
// avoid having a bias towards peers that happen to be sorted first
|
int num_opt_unchoke = m_settings.get_int(settings_pack::num_optimistic_unchoke_slots);
|
||||||
std::random_shuffle(opt_unchoke.begin(), opt_unchoke.end(), randint);
|
int const allowed_unchoke_slots = m_stats_counters[counters::num_unchoke_slots];
|
||||||
|
if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, allowed_unchoke_slots / 5);
|
||||||
|
if (num_opt_unchoke > int(opt_unchoke.size())) num_opt_unchoke =
|
||||||
|
int(opt_unchoke.size());
|
||||||
|
|
||||||
// sort all candidates based on when they were last optimistically
|
// find the n best optimistic unchoke candidates
|
||||||
// unchoked.
|
std::partial_sort(opt_unchoke.begin()
|
||||||
std::sort(opt_unchoke.begin(), opt_unchoke.end(), last_optimistic_unchoke_cmp());
|
, opt_unchoke.begin() + num_opt_unchoke
|
||||||
|
, opt_unchoke.end(), &last_optimistic_unchoke_cmp);
|
||||||
|
|
||||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||||
for (ses_extension_list_t::iterator i = m_ses_extensions.begin()
|
if (m_session_extension_features & plugin::optimistic_unchoke_feature)
|
||||||
, end(m_ses_extensions.end()); i != end; ++i)
|
|
||||||
{
|
{
|
||||||
if ((*i)->on_optimistic_unchoke(opt_unchoke))
|
// if there is an extension that wants to reorder the optimistic
|
||||||
break;
|
// unchoke peers, first convert the vector into one containing
|
||||||
|
// peer_connection_handles, since that's the exported API
|
||||||
|
std::vector<peer_connection_handle> peers;
|
||||||
|
peers.reserve(opt_unchoke.size());
|
||||||
|
for (std::vector<torrent_peer*>::iterator i = opt_unchoke.begin()
|
||||||
|
, end(opt_unchoke.end()); i != end; ++i)
|
||||||
|
{
|
||||||
|
peers.push_back(peer_connection_handle(static_cast<peer_connection*>((*i)->connection)->self()));
|
||||||
|
}
|
||||||
|
for (ses_extension_list_t::iterator i = m_ses_extensions.begin()
|
||||||
|
, end(m_ses_extensions.end()); i != end; ++i)
|
||||||
|
{
|
||||||
|
if ((*i)->on_optimistic_unchoke(peers))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// then convert back to the internal torrent_peer pointers
|
||||||
|
opt_unchoke.clear();
|
||||||
|
for (std::vector<peer_connection_handle>::iterator i = peers.begin()
|
||||||
|
, end(peers.end()); i != end; ++i)
|
||||||
|
{
|
||||||
|
opt_unchoke.push_back(i->native_handle()->peer_info_struct());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int num_opt_unchoke = m_settings.get_int(settings_pack::num_optimistic_unchoke_slots);
|
|
||||||
int allowed_unchoke_slots = m_stats_counters[counters::num_unchoke_slots];
|
|
||||||
if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, allowed_unchoke_slots / 5);
|
|
||||||
|
|
||||||
// unchoke the first num_opt_unchoke peers in the candidate set
|
// unchoke the first num_opt_unchoke peers in the candidate set
|
||||||
// and make sure that the others are choked
|
// and make sure that the others are choked
|
||||||
for (std::vector<peer_connection_handle>::iterator i = opt_unchoke.begin()
|
std::vector<torrent_peer*>::iterator opt_unchoke_end = opt_unchoke.begin()
|
||||||
, end(opt_unchoke.end()); i != end; ++i)
|
+ num_opt_unchoke;
|
||||||
|
|
||||||
|
for (std::vector<torrent_peer*>::iterator i = opt_unchoke.begin();
|
||||||
|
i != opt_unchoke_end; ++i)
|
||||||
{
|
{
|
||||||
torrent_peer* pi = i->native_handle()->peer_info_struct();
|
torrent_peer* pi = *i;
|
||||||
if (num_opt_unchoke > 0)
|
peer_connection* p = static_cast<peer_connection*>(pi->connection);
|
||||||
|
if (pi->optimistically_unchoked)
|
||||||
{
|
{
|
||||||
--num_opt_unchoke;
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
if (!pi->optimistically_unchoked)
|
p->peer_log(peer_log_alert::info, "OPTIMISTIC UNCHOKE"
|
||||||
{
|
, "already unchoked | session-time: %d"
|
||||||
peer_connection* p = static_cast<peer_connection*>(pi->connection);
|
, pi->last_optimistically_unchoked);
|
||||||
torrent* t = p->associated_torrent().lock().get();
|
#endif
|
||||||
bool ret = t->unchoke_peer(*p, true);
|
TORRENT_ASSERT(!pi->connection->is_choked());
|
||||||
if (ret)
|
// remove this peer from prev_opt_unchoke, to prevent us from
|
||||||
{
|
// choking it later. This peer gets another round of optimistic
|
||||||
pi->optimistically_unchoked = true;
|
// unchoke
|
||||||
m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic);
|
std::vector<torrent_peer*>::iterator existing =
|
||||||
pi->last_optimistically_unchoked = boost::uint16_t(session_time());
|
std::find(prev_opt_unchoke.begin(), prev_opt_unchoke.end(), pi);
|
||||||
}
|
TORRENT_ASSERT(existing != prev_opt_unchoke.end());
|
||||||
else
|
prev_opt_unchoke.erase(existing);
|
||||||
{
|
|
||||||
// we failed to unchoke it, increment the count again
|
|
||||||
++num_opt_unchoke;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (pi->optimistically_unchoked)
|
TORRENT_ASSERT(p->is_choked());
|
||||||
|
boost::shared_ptr<torrent> t = p->associated_torrent().lock();
|
||||||
|
bool ret = t->unchoke_peer(*p, true);
|
||||||
|
TORRENT_ASSERT(ret);
|
||||||
|
if (ret)
|
||||||
{
|
{
|
||||||
peer_connection* p = static_cast<peer_connection*>(pi->connection);
|
pi->optimistically_unchoked = true;
|
||||||
torrent* t = p->associated_torrent().lock().get();
|
m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic);
|
||||||
pi->optimistically_unchoked = false;
|
pi->last_optimistically_unchoked = boost::uint16_t(session_time());
|
||||||
m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1);
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
t->choke_peer(*p);
|
p->peer_log(peer_log_alert::info, "OPTIMISTIC UNCHOKE"
|
||||||
|
, "session-time: %d", pi->last_optimistically_unchoked);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now, choke all the previous optimistically unchoked peers
|
||||||
|
for (std::vector<torrent_peer*>::iterator i = prev_opt_unchoke.begin()
|
||||||
|
, end(prev_opt_unchoke.end()); i != end; ++i)
|
||||||
|
{
|
||||||
|
torrent_peer* pi = *i;
|
||||||
|
TORRENT_ASSERT(pi->optimistically_unchoked);
|
||||||
|
peer_connection* p = static_cast<peer_connection*>(pi->connection);
|
||||||
|
boost::shared_ptr<torrent> t = p->associated_torrent().lock();
|
||||||
|
pi->optimistically_unchoked = false;
|
||||||
|
m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1);
|
||||||
|
t->choke_peer(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have too many unchoked peers now, we need to trigger the regular
|
||||||
|
// choking logic to choke some
|
||||||
|
if (m_stats_counters[counters::num_unchoke_slots]
|
||||||
|
< m_stats_counters[counters::num_peers_up_unchoked_all])
|
||||||
|
{
|
||||||
|
m_unchoke_time_scaler = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void session_impl::try_connect_more_peers()
|
void session_impl::try_connect_more_peers()
|
||||||
|
@ -4018,6 +4093,8 @@ retry:
|
||||||
|
|
||||||
// build list of all peers that are
|
// build list of all peers that are
|
||||||
// unchokable.
|
// unchokable.
|
||||||
|
// TODO: 3 there should be a pre-calculated list of all peers eligible for
|
||||||
|
// unchoking
|
||||||
std::vector<peer_connection*> peers;
|
std::vector<peer_connection*> peers;
|
||||||
for (connection_map::iterator i = m_connections.begin();
|
for (connection_map::iterator i = m_connections.begin();
|
||||||
i != m_connections.end();)
|
i != m_connections.end();)
|
||||||
|
@ -4079,7 +4156,7 @@ retry:
|
||||||
, performance_alert::bittyrant_with_no_uplimit);
|
, performance_alert::bittyrant_with_no_uplimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
int allowed_upload_slots = unchoke_sort(peers, max_upload_rate
|
int const allowed_upload_slots = unchoke_sort(peers, max_upload_rate
|
||||||
, unchoke_interval, m_settings);
|
, unchoke_interval, m_settings);
|
||||||
m_stats_counters.set_value(counters::num_unchoke_slots
|
m_stats_counters.set_value(counters::num_unchoke_slots
|
||||||
, allowed_upload_slots);
|
, allowed_upload_slots);
|
||||||
|
@ -4088,7 +4165,8 @@ retry:
|
||||||
session_log("RECALCULATE UNCHOKE SLOTS: [ peers: %d "
|
session_log("RECALCULATE UNCHOKE SLOTS: [ peers: %d "
|
||||||
"eligible-peers: %d"
|
"eligible-peers: %d"
|
||||||
" max_upload_rate: %d"
|
" max_upload_rate: %d"
|
||||||
" allowed-slots: %d ]", int(m_connections.size())
|
" allowed-slots: %d ]"
|
||||||
|
, int(m_connections.size())
|
||||||
, int(peers.size())
|
, int(peers.size())
|
||||||
, max_upload_rate
|
, max_upload_rate
|
||||||
, allowed_upload_slots);
|
, allowed_upload_slots);
|
||||||
|
@ -4096,9 +4174,7 @@ retry:
|
||||||
|
|
||||||
int num_opt_unchoke = m_settings.get_int(settings_pack::num_optimistic_unchoke_slots);
|
int num_opt_unchoke = m_settings.get_int(settings_pack::num_optimistic_unchoke_slots);
|
||||||
if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, allowed_upload_slots / 5);
|
if (num_opt_unchoke == 0) num_opt_unchoke = (std::max)(1, allowed_upload_slots / 5);
|
||||||
|
int unchoke_set_size = allowed_upload_slots - num_opt_unchoke;
|
||||||
// reserve some upload slots for optimistic unchokes
|
|
||||||
int unchoke_set_size = allowed_upload_slots;
|
|
||||||
|
|
||||||
// go through all the peers and unchoke the first ones and choke
|
// go through all the peers and unchoke the first ones and choke
|
||||||
// all the other ones.
|
// all the other ones.
|
||||||
|
@ -4882,6 +4958,9 @@ retry:
|
||||||
tcp::endpoint bind_ep(address_v4(), 0);
|
tcp::endpoint bind_ep(address_v4(), 0);
|
||||||
if (m_settings.get_int(settings_pack::outgoing_port) > 0)
|
if (m_settings.get_int(settings_pack::outgoing_port) > 0)
|
||||||
{
|
{
|
||||||
|
#ifdef TORRENT_WINDOWS
|
||||||
|
s.set_option(exclusive_address_use(true), ec);
|
||||||
|
#endif
|
||||||
s.set_option(tcp::acceptor::reuse_address(true), ec);
|
s.set_option(tcp::acceptor::reuse_address(true), ec);
|
||||||
// ignore errors because the underlying socket may not
|
// ignore errors because the underlying socket may not
|
||||||
// be opened yet. This happens when we're routing through
|
// be opened yet. This happens when we're routing through
|
||||||
|
@ -4901,7 +4980,10 @@ retry:
|
||||||
|
|
||||||
if (ec) return bind_ep;
|
if (ec) return bind_ep;
|
||||||
|
|
||||||
bind_ep.address(bind_to_device(m_io_service, s, remote_address.is_v4()
|
bind_ep.address(bind_to_device(m_io_service, s
|
||||||
|
, remote_address.is_v4()
|
||||||
|
? boost::asio::ip::tcp::v4()
|
||||||
|
: boost::asio::ip::tcp::v6()
|
||||||
, ifname.c_str(), bind_ep.port(), ec));
|
, ifname.c_str(), bind_ep.port(), ec));
|
||||||
return bind_ep;
|
return bind_ep;
|
||||||
}
|
}
|
||||||
|
@ -5114,8 +5196,9 @@ retry:
|
||||||
if (strcmp(ifs[i].name, device_name) != 0) continue;
|
if (strcmp(ifs[i].name, device_name) != 0) continue;
|
||||||
m_listen_interface.address(ifs[i].interface_address);
|
m_listen_interface.address(ifs[i].interface_address);
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
|
error_code err;
|
||||||
session_log("binding to %s"
|
session_log("binding to %s"
|
||||||
, m_listen_interface.address().to_string(ec).c_str());
|
, m_listen_interface.address().to_string(err).c_str());
|
||||||
#endif
|
#endif
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -284,7 +284,7 @@ namespace libtorrent
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
libtorrent::address torrent_peer::address() const
|
libtorrent::address torrent_peer::address() const
|
||||||
{
|
{
|
||||||
#if TORRENT_USE_IPV6
|
#if TORRENT_USE_IPV6
|
||||||
|
|
|
@ -48,6 +48,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <boost/system/system_error.hpp>
|
#include <boost/system/system_error.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/system/error_code.hpp>
|
||||||
#include <boost/asio/read.hpp>
|
#include <boost/asio/read.hpp>
|
||||||
|
#include <boost/asio/ip/v6_only.hpp>
|
||||||
|
|
||||||
#if defined TORRENT_ASIO_DEBUGGING
|
#if defined TORRENT_ASIO_DEBUGGING
|
||||||
#include "libtorrent/debug.hpp"
|
#include "libtorrent/debug.hpp"
|
||||||
|
@ -789,6 +790,14 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec)
|
||||||
{
|
{
|
||||||
m_ipv4_sock.open(udp::v4(), ec);
|
m_ipv4_sock.open(udp::v4(), ec);
|
||||||
if (ec) return;
|
if (ec) return;
|
||||||
|
|
||||||
|
// this is best-effort. ignore errors
|
||||||
|
error_code err;
|
||||||
|
#ifdef TORRENT_WINDOWS
|
||||||
|
m_ipv4_sock.set_option(exclusive_address_use(true), err);
|
||||||
|
#endif
|
||||||
|
m_ipv4_sock.set_option(boost::asio::socket_base::reuse_address(true), err);
|
||||||
|
|
||||||
m_ipv4_sock.bind(ep, ec);
|
m_ipv4_sock.bind(ep, ec);
|
||||||
if (ec) return;
|
if (ec) return;
|
||||||
udp::socket::non_blocking_io ioc(true);
|
udp::socket::non_blocking_io ioc(true);
|
||||||
|
@ -807,10 +816,15 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec)
|
||||||
if (is_any(ep.address())) ep6.address(address_v6::any());
|
if (is_any(ep.address())) ep6.address(address_v6::any());
|
||||||
m_ipv6_sock.open(udp::v6(), ec);
|
m_ipv6_sock.open(udp::v6(), ec);
|
||||||
if (ec) return;
|
if (ec) return;
|
||||||
#ifdef IPV6_V6ONLY
|
|
||||||
m_ipv6_sock.set_option(v6only(true), ec);
|
// this is best-effort. ignore errors
|
||||||
ec.clear();
|
error_code err;
|
||||||
|
#ifdef TORRENT_WINDOWS
|
||||||
|
m_ipv4_sock.set_option(exclusive_address_use(true), err);
|
||||||
#endif
|
#endif
|
||||||
|
m_ipv4_sock.set_option(boost::asio::socket_base::reuse_address(true), err);
|
||||||
|
m_ipv6_sock.set_option(boost::asio::ip::v6_only(true), err);
|
||||||
|
|
||||||
m_ipv6_sock.bind(ep6, ec);
|
m_ipv6_sock.bind(ep6, ec);
|
||||||
if (ec != error_code(boost::system::errc::address_not_available
|
if (ec != error_code(boost::system::errc::address_not_available
|
||||||
, boost::system::generic_category()))
|
, boost::system::generic_category()))
|
||||||
|
|
|
@ -167,7 +167,7 @@ namespace libtorrent { namespace
|
||||||
m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size);
|
m_torrent.set_progress_ppm(boost::int64_t(m_metadata_progress) * 1000000 / m_metadata_size);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
void on_piece_pass(int)
|
void on_piece_pass(int) TORRENT_OVERRIDE
|
||||||
{
|
{
|
||||||
// if we became a seed, copy the metadata from
|
// if we became a seed, copy the metadata from
|
||||||
// the torrent before it is deallocated
|
// the torrent before it is deallocated
|
||||||
|
|
|
@ -56,6 +56,8 @@ lib libtorrent_test
|
||||||
dht_server.cpp
|
dht_server.cpp
|
||||||
udp_tracker.cpp
|
udp_tracker.cpp
|
||||||
peer_server.cpp
|
peer_server.cpp
|
||||||
|
bittorrent_peer.cpp
|
||||||
|
print_alerts.cpp
|
||||||
web_seed_suite.cpp
|
web_seed_suite.cpp
|
||||||
swarm_suite.cpp
|
swarm_suite.cpp
|
||||||
test_utils.cpp
|
test_utils.cpp
|
||||||
|
|
|
@ -0,0 +1,562 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 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.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libtorrent/socket.hpp"
|
||||||
|
#include "libtorrent/sha1_hash.hpp"
|
||||||
|
#include "libtorrent/address.hpp"
|
||||||
|
#include "libtorrent/assert.hpp"
|
||||||
|
#include "bittorrent_peer.hpp"
|
||||||
|
#include "libtorrent/torrent_info.hpp"
|
||||||
|
#include "libtorrent/io_service.hpp"
|
||||||
|
#include "libtorrent/io.hpp"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
using namespace libtorrent;
|
||||||
|
|
||||||
|
peer_conn::peer_conn(io_service& ios
|
||||||
|
, boost::function<void(int, char const*, int)> on_msg
|
||||||
|
, torrent_info const& ti
|
||||||
|
, tcp::endpoint const& ep
|
||||||
|
, peer_mode_t mode)
|
||||||
|
: s(ios)
|
||||||
|
, m_mode(mode)
|
||||||
|
, m_ti(ti)
|
||||||
|
, read_pos(0)
|
||||||
|
, m_on_msg(on_msg)
|
||||||
|
, state(handshaking)
|
||||||
|
, choked(true)
|
||||||
|
, current_piece(-1)
|
||||||
|
, m_current_piece_is_allowed(false)
|
||||||
|
, block(0)
|
||||||
|
, m_blocks_per_piece((m_ti.piece_length() + 0x3fff) / 0x4000)
|
||||||
|
, outstanding_requests(0)
|
||||||
|
, fast_extension(false)
|
||||||
|
, blocks_received(0)
|
||||||
|
, blocks_sent(0)
|
||||||
|
, start_time(clock_type::now())
|
||||||
|
, endpoint(ep)
|
||||||
|
, restarting(false)
|
||||||
|
{
|
||||||
|
pieces.reserve(m_ti.num_pieces());
|
||||||
|
start_conn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::start_conn()
|
||||||
|
{
|
||||||
|
restarting = false;
|
||||||
|
s.async_connect(endpoint, boost::bind(&peer_conn::on_connect, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::on_connect(error_code const& ec)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
close("ERROR CONNECT: %s", ec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char handshake[] = "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04"
|
||||||
|
" " // space for info-hash
|
||||||
|
"aaaaaaaaaaaaaaaaaaaa" // peer-id
|
||||||
|
"\0\0\0\x01\x02"; // interested
|
||||||
|
char* h = (char*)malloc(sizeof(handshake));
|
||||||
|
memcpy(h, handshake, sizeof(handshake));
|
||||||
|
std::memcpy(h + 28, m_ti.info_hash().data(), 20);
|
||||||
|
std::generate(h + 48, h + 68, &rand);
|
||||||
|
// for seeds, don't send the interested message
|
||||||
|
boost::asio::async_write(s, boost::asio::buffer(h, (sizeof(handshake) - 1)
|
||||||
|
- (m_mode == uploader ? 5 : 0))
|
||||||
|
, boost::bind(&peer_conn::on_handshake, this, h, _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::on_handshake(char* h, error_code const& ec, size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
free(h);
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
close("ERROR SEND HANDSHAKE: %s", ec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read handshake
|
||||||
|
boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 68)
|
||||||
|
, boost::bind(&peer_conn::on_handshake2, this, _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::on_handshake2(error_code const& ec, size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
close("ERROR READ HANDSHAKE: %s", ec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer is the full 68 byte handshake
|
||||||
|
// look at the extension bits
|
||||||
|
|
||||||
|
fast_extension = ((char*)buffer)[27] & 4;
|
||||||
|
|
||||||
|
if (m_mode == uploader)
|
||||||
|
{
|
||||||
|
write_have_all();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
work_download();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::write_have_all()
|
||||||
|
{
|
||||||
|
using namespace libtorrent::detail;
|
||||||
|
|
||||||
|
if (fast_extension)
|
||||||
|
{
|
||||||
|
char* ptr = write_buf_proto;
|
||||||
|
// have_all
|
||||||
|
write_uint32(1, ptr);
|
||||||
|
write_uint8(0xe, ptr);
|
||||||
|
// unchoke
|
||||||
|
write_uint32(1, ptr);
|
||||||
|
write_uint8(1, ptr);
|
||||||
|
error_code ec;
|
||||||
|
boost::asio::async_write(s, boost::asio::buffer(write_buf_proto, ptr - write_buf_proto)
|
||||||
|
, boost::bind(&peer_conn::on_have_all_sent, this, _1, _2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// bitfield
|
||||||
|
int len = (m_ti.num_pieces() + 7) / 8;
|
||||||
|
char* ptr = (char*)buffer;
|
||||||
|
write_uint32(len + 1, ptr);
|
||||||
|
write_uint8(5, ptr);
|
||||||
|
memset(ptr, 255, len);
|
||||||
|
ptr += len;
|
||||||
|
// unchoke
|
||||||
|
write_uint32(1, ptr);
|
||||||
|
write_uint8(1, ptr);
|
||||||
|
error_code ec;
|
||||||
|
boost::asio::async_write(s, boost::asio::buffer((char*)buffer, len + 10)
|
||||||
|
, boost::bind(&peer_conn::on_have_all_sent, this, _1, _2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::on_have_all_sent(error_code const& ec, size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
close("ERROR SEND HAVE ALL: %s", ec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read message
|
||||||
|
boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 4)
|
||||||
|
, boost::bind(&peer_conn::on_msg_length, this, _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool peer_conn::write_request()
|
||||||
|
{
|
||||||
|
using namespace libtorrent::detail;
|
||||||
|
|
||||||
|
// if we're choked (and there are no allowed-fast pieces left)
|
||||||
|
if (choked && allowed_fast.empty() && !m_current_piece_is_allowed) return false;
|
||||||
|
|
||||||
|
// if there are no pieces left to request
|
||||||
|
if (pieces.empty() && suggested_pieces.empty() && current_piece == -1) return false;
|
||||||
|
|
||||||
|
if (current_piece == -1)
|
||||||
|
{
|
||||||
|
// pick a new piece
|
||||||
|
if (choked && allowed_fast.size() > 0)
|
||||||
|
{
|
||||||
|
current_piece = allowed_fast.front();
|
||||||
|
allowed_fast.erase(allowed_fast.begin());
|
||||||
|
m_current_piece_is_allowed = true;
|
||||||
|
}
|
||||||
|
else if (suggested_pieces.size() > 0)
|
||||||
|
{
|
||||||
|
current_piece = suggested_pieces.front();
|
||||||
|
suggested_pieces.erase(suggested_pieces.begin());
|
||||||
|
m_current_piece_is_allowed = false;
|
||||||
|
}
|
||||||
|
else if (pieces.size() > 0)
|
||||||
|
{
|
||||||
|
current_piece = pieces.front();
|
||||||
|
pieces.erase(pieces.begin());
|
||||||
|
m_current_piece_is_allowed = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TORRENT_ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char msg[] = "\0\0\0\xd\x06"
|
||||||
|
" " // piece
|
||||||
|
" " // offset
|
||||||
|
" "; // length
|
||||||
|
char* m = (char*)malloc(sizeof(msg));
|
||||||
|
memcpy(m, msg, sizeof(msg));
|
||||||
|
char* ptr = m + 5;
|
||||||
|
write_uint32(current_piece, ptr);
|
||||||
|
write_uint32(block * 16 * 1024, ptr);
|
||||||
|
write_uint32(16 * 1024, ptr);
|
||||||
|
error_code ec;
|
||||||
|
boost::asio::async_write(s, boost::asio::buffer(m, sizeof(msg) - 1)
|
||||||
|
, boost::bind(&peer_conn::on_req_sent, this, m, _1, _2));
|
||||||
|
|
||||||
|
++outstanding_requests;
|
||||||
|
++block;
|
||||||
|
if (block == m_blocks_per_piece)
|
||||||
|
{
|
||||||
|
block = 0;
|
||||||
|
current_piece = -1;
|
||||||
|
m_current_piece_is_allowed = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::on_req_sent(char* m, error_code const& ec, size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
free(m);
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
close("ERROR SEND REQUEST: %s", ec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
work_download();
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::close(char const* fmt, error_code const& ec)
|
||||||
|
{
|
||||||
|
end_time = clock_type::now();
|
||||||
|
char tmp[1024];
|
||||||
|
snprintf(tmp, sizeof(tmp), fmt, ec.message().c_str());
|
||||||
|
int time = total_milliseconds(end_time - start_time);
|
||||||
|
if (time == 0) time = 1;
|
||||||
|
float up = (boost::int64_t(blocks_sent) * 0x4000) / time / 1000.f;
|
||||||
|
float down = (boost::int64_t(blocks_received) * 0x4000) / time / 1000.f;
|
||||||
|
error_code e;
|
||||||
|
|
||||||
|
char ep_str[200];
|
||||||
|
address const& addr = s.local_endpoint(e).address();
|
||||||
|
#if TORRENT_USE_IPV6
|
||||||
|
if (addr.is_v6())
|
||||||
|
snprintf(ep_str, sizeof(ep_str), "[%s]:%d", addr.to_string(e).c_str()
|
||||||
|
, s.local_endpoint(e).port());
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
snprintf(ep_str, sizeof(ep_str), "%s:%d", addr.to_string(e).c_str()
|
||||||
|
, s.local_endpoint(e).port());
|
||||||
|
printf("%s ep: %s sent: %d received: %d duration: %d ms up: %.1fMB/s down: %.1fMB/s\n"
|
||||||
|
, tmp, ep_str, blocks_sent, blocks_received, time, up, down);
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::work_download()
|
||||||
|
{
|
||||||
|
if (pieces.empty()
|
||||||
|
&& suggested_pieces.empty()
|
||||||
|
&& current_piece == -1
|
||||||
|
&& outstanding_requests == 0
|
||||||
|
&& blocks_received >= m_ti.num_pieces() * m_blocks_per_piece)
|
||||||
|
{
|
||||||
|
close("COMPLETED DOWNLOAD", error_code());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send requests
|
||||||
|
if (outstanding_requests < 40)
|
||||||
|
{
|
||||||
|
if (write_request()) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read message
|
||||||
|
boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 4)
|
||||||
|
, boost::bind(&peer_conn::on_msg_length, this, _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::on_msg_length(error_code const& ec, size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
using namespace libtorrent::detail;
|
||||||
|
|
||||||
|
if ((ec == boost::asio::error::operation_aborted || ec == boost::asio::error::bad_descriptor)
|
||||||
|
&& restarting)
|
||||||
|
{
|
||||||
|
start_conn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
close("ERROR RECEIVE MESSAGE PREFIX: %s", ec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char* ptr = (char*)buffer;
|
||||||
|
unsigned int length = read_uint32(ptr);
|
||||||
|
if (length > sizeof(buffer))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "len: %d\n", length);
|
||||||
|
close("ERROR RECEIVE MESSAGE PREFIX: packet too big", error_code());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
// keep-alive messate. read another length prefix
|
||||||
|
boost::asio::async_read(s, boost::asio::buffer((char*)buffer, 4)
|
||||||
|
, boost::bind(&peer_conn::on_msg_length, this, _1, _2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boost::asio::async_read(s, boost::asio::buffer((char*)buffer, length)
|
||||||
|
, boost::bind(&peer_conn::on_message, this, _1, _2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::on_message(error_code const& ec, size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
using namespace libtorrent::detail;
|
||||||
|
|
||||||
|
if ((ec == boost::asio::error::operation_aborted || ec == boost::asio::error::bad_descriptor)
|
||||||
|
&& restarting)
|
||||||
|
{
|
||||||
|
start_conn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
close("ERROR RECEIVE MESSAGE: %s", ec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char* ptr = (char*)buffer;
|
||||||
|
int msg = read_uint8(ptr);
|
||||||
|
|
||||||
|
m_on_msg(msg, ptr, bytes_transferred);
|
||||||
|
|
||||||
|
switch (m_mode)
|
||||||
|
{
|
||||||
|
case peer_conn::uploader:
|
||||||
|
if (msg == 6)
|
||||||
|
{
|
||||||
|
if (bytes_transferred != 13)
|
||||||
|
{
|
||||||
|
close("REQUEST packet has invalid size", error_code());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int piece = detail::read_int32(ptr);
|
||||||
|
int start = detail::read_int32(ptr);
|
||||||
|
int length = detail::read_int32(ptr);
|
||||||
|
write_piece(piece, start, length);
|
||||||
|
}
|
||||||
|
else if (msg == 3) // not-interested
|
||||||
|
{
|
||||||
|
close("DONE", error_code());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// read another message
|
||||||
|
boost::asio::async_read(s, boost::asio::buffer(buffer, 4)
|
||||||
|
, boost::bind(&peer_conn::on_msg_length, this, _1, _2));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case peer_conn::downloader:
|
||||||
|
if (msg == 0xe) // have_all
|
||||||
|
{
|
||||||
|
// build a list of all pieces and request them all!
|
||||||
|
pieces.resize(m_ti.num_pieces());
|
||||||
|
for (int i = 0; i < int(pieces.size()); ++i)
|
||||||
|
pieces[i] = i;
|
||||||
|
std::random_shuffle(pieces.begin(), pieces.end());
|
||||||
|
}
|
||||||
|
else if (msg == 4) // have
|
||||||
|
{
|
||||||
|
int piece = detail::read_int32(ptr);
|
||||||
|
if (pieces.empty()) pieces.push_back(piece);
|
||||||
|
else pieces.insert(pieces.begin() + (rand() % pieces.size()), piece);
|
||||||
|
}
|
||||||
|
else if (msg == 5) // bitfield
|
||||||
|
{
|
||||||
|
pieces.reserve(m_ti.num_pieces());
|
||||||
|
int piece = 0;
|
||||||
|
for (int i = 0; i < int(bytes_transferred); ++i)
|
||||||
|
{
|
||||||
|
int mask = 0x80;
|
||||||
|
for (int k = 0; k < 8; ++k)
|
||||||
|
{
|
||||||
|
if (piece > m_ti.num_pieces()) break;
|
||||||
|
if (*ptr & mask) pieces.push_back(piece);
|
||||||
|
mask >>= 1;
|
||||||
|
++piece;
|
||||||
|
}
|
||||||
|
++ptr;
|
||||||
|
}
|
||||||
|
std::random_shuffle(pieces.begin(), pieces.end());
|
||||||
|
}
|
||||||
|
else if (msg == 7) // piece
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
if (verify_downloads)
|
||||||
|
{
|
||||||
|
int piece = read_uint32(ptr);
|
||||||
|
int start = read_uint32(ptr);
|
||||||
|
int size = bytes_transferred - 9;
|
||||||
|
verify_piece(piece, start, ptr, size);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
++blocks_received;
|
||||||
|
--outstanding_requests;
|
||||||
|
int piece = detail::read_int32(ptr);
|
||||||
|
int start = detail::read_int32(ptr);
|
||||||
|
|
||||||
|
if (int((start + bytes_transferred) / 0x4000) == m_blocks_per_piece)
|
||||||
|
{
|
||||||
|
write_have(piece);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msg == 13) // suggest
|
||||||
|
{
|
||||||
|
int piece = detail::read_int32(ptr);
|
||||||
|
std::vector<int>::iterator i = std::find(pieces.begin(), pieces.end(), piece);
|
||||||
|
if (i != pieces.end())
|
||||||
|
{
|
||||||
|
pieces.erase(i);
|
||||||
|
suggested_pieces.push_back(piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msg == 16) // reject request
|
||||||
|
{
|
||||||
|
int piece = detail::read_int32(ptr);
|
||||||
|
int start = detail::read_int32(ptr);
|
||||||
|
int length = detail::read_int32(ptr);
|
||||||
|
|
||||||
|
// put it back!
|
||||||
|
if (current_piece != piece)
|
||||||
|
{
|
||||||
|
if (pieces.empty() || pieces.back() != piece)
|
||||||
|
pieces.push_back(piece);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block = (std::min)(start / 0x4000, block);
|
||||||
|
if (block == 0)
|
||||||
|
{
|
||||||
|
pieces.push_back(current_piece);
|
||||||
|
current_piece = -1;
|
||||||
|
m_current_piece_is_allowed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--outstanding_requests;
|
||||||
|
fprintf(stderr, "REJECT: [ piece: %d start: %d length: %d ]\n", piece, start, length);
|
||||||
|
}
|
||||||
|
else if (msg == 0) // choke
|
||||||
|
{
|
||||||
|
choked = true;
|
||||||
|
}
|
||||||
|
else if (msg == 1) // unchoke
|
||||||
|
{
|
||||||
|
choked = false;
|
||||||
|
}
|
||||||
|
else if (msg == 17) // allowed_fast
|
||||||
|
{
|
||||||
|
int piece = detail::read_int32(ptr);
|
||||||
|
std::vector<int>::iterator i = std::find(pieces.begin(), pieces.end(), piece);
|
||||||
|
if (i != pieces.end())
|
||||||
|
{
|
||||||
|
pieces.erase(i);
|
||||||
|
allowed_fast.push_back(piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
work_download();
|
||||||
|
break;
|
||||||
|
case peer_conn::idle:
|
||||||
|
// read another message
|
||||||
|
boost::asio::async_read(s, boost::asio::buffer(buffer, 4)
|
||||||
|
, boost::bind(&peer_conn::on_msg_length, this, _1, _2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
bool peer_conn::verify_piece(int piece, int start, char const* ptr, int size)
|
||||||
|
{
|
||||||
|
boost::uint32_t* buf = (boost::uint32_t*)ptr;
|
||||||
|
boost::uint32_t fill = (piece << 8) | ((start / 0x4000) & 0xff);
|
||||||
|
for (int i = 0; i < size / 4; ++i)
|
||||||
|
{
|
||||||
|
if (buf[i] != fill)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "received invalid block. piece %d block %d\n", piece, start / 0x4000);
|
||||||
|
exit(1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
void peer_conn::write_piece(int piece, int start, int length)
|
||||||
|
{
|
||||||
|
using namespace libtorrent::detail;
|
||||||
|
|
||||||
|
// generate_block(write_buffer, piece, start, length);
|
||||||
|
|
||||||
|
char* ptr = write_buf_proto;
|
||||||
|
write_uint32(9 + length, ptr);
|
||||||
|
TORRENT_ASSERT(length == 0x4000);
|
||||||
|
write_uint8(7, ptr);
|
||||||
|
write_uint32(piece, ptr);
|
||||||
|
write_uint32(start, ptr);
|
||||||
|
boost::array<boost::asio::const_buffer, 2> vec;
|
||||||
|
vec[0] = boost::asio::buffer(write_buf_proto, ptr - write_buf_proto);
|
||||||
|
vec[1] = boost::asio::buffer(write_buffer, length);
|
||||||
|
boost::asio::async_write(s, vec, boost::bind(&peer_conn::on_have_all_sent, this, _1, _2));
|
||||||
|
++blocks_sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::write_have(int piece)
|
||||||
|
{
|
||||||
|
using namespace libtorrent::detail;
|
||||||
|
|
||||||
|
char* ptr = write_buf_proto;
|
||||||
|
write_uint32(5, ptr);
|
||||||
|
write_uint8(4, ptr);
|
||||||
|
write_uint32(piece, ptr);
|
||||||
|
boost::asio::async_write(s, boost::asio::buffer(write_buf_proto, 9), boost::bind(&peer_conn::on_have_all_sent, this, _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void peer_conn::abort()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
s.close(ec);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 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 BITTORRENT_PEER_HPP
|
||||||
|
#define BITTORRENT_PEER_HPP
|
||||||
|
|
||||||
|
#include "libtorrent/socket.hpp"
|
||||||
|
#include "libtorrent/sha1_hash.hpp"
|
||||||
|
#include "libtorrent/io_service.hpp"
|
||||||
|
#include "libtorrent/time.hpp"
|
||||||
|
#include "libtorrent/address.hpp"
|
||||||
|
#include "libtorrent/torrent_info.hpp"
|
||||||
|
#include "test.hpp" // for EXPORT
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
|
using namespace libtorrent;
|
||||||
|
|
||||||
|
struct EXPORT peer_conn
|
||||||
|
{
|
||||||
|
enum peer_mode_t
|
||||||
|
{ uploader, downloader, idle };
|
||||||
|
|
||||||
|
peer_conn(io_service& ios
|
||||||
|
, boost::function<void(int, char const*, int)> on_msg
|
||||||
|
, libtorrent::torrent_info const& ti
|
||||||
|
, libtorrent::tcp::endpoint const& ep
|
||||||
|
, peer_mode_t mode);
|
||||||
|
|
||||||
|
void start_conn();
|
||||||
|
|
||||||
|
void on_connect(error_code const& ec);
|
||||||
|
void on_handshake(char* h, error_code const& ec, size_t bytes_transferred);
|
||||||
|
void on_handshake2(error_code const& ec, size_t bytes_transferred);
|
||||||
|
void write_have_all();
|
||||||
|
void on_have_all_sent(error_code const& ec, size_t bytes_transferred);
|
||||||
|
bool write_request();
|
||||||
|
void on_req_sent(char* m, error_code const& ec, size_t bytes_transferred);
|
||||||
|
void close(char const* fmt, error_code const& ec);
|
||||||
|
void work_download();
|
||||||
|
void on_msg_length(error_code const& ec, size_t bytes_transferred);
|
||||||
|
void on_message(error_code const& ec, size_t bytes_transferred);
|
||||||
|
bool verify_piece(int piece, int start, char const* ptr, int size);
|
||||||
|
void write_piece(int piece, int start, int length);
|
||||||
|
void write_have(int piece);
|
||||||
|
|
||||||
|
void abort();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
tcp::socket s;
|
||||||
|
char write_buf_proto[100];
|
||||||
|
boost::uint32_t write_buffer[17*1024/4];
|
||||||
|
boost::uint32_t buffer[17*1024/4];
|
||||||
|
|
||||||
|
peer_mode_t m_mode;
|
||||||
|
torrent_info const& m_ti;
|
||||||
|
|
||||||
|
int read_pos;
|
||||||
|
|
||||||
|
boost::function<void(int, char const*, int)> m_on_msg;
|
||||||
|
|
||||||
|
enum state_t
|
||||||
|
{
|
||||||
|
handshaking,
|
||||||
|
sending_request,
|
||||||
|
receiving_message
|
||||||
|
};
|
||||||
|
int state;
|
||||||
|
std::vector<int> pieces;
|
||||||
|
std::vector<int> suggested_pieces;
|
||||||
|
std::vector<int> allowed_fast;
|
||||||
|
bool choked;
|
||||||
|
int current_piece; // the piece we're currently requesting blocks from
|
||||||
|
bool m_current_piece_is_allowed;
|
||||||
|
int block;
|
||||||
|
int const m_blocks_per_piece;
|
||||||
|
int outstanding_requests;
|
||||||
|
// if this is true, this connection is a seed
|
||||||
|
bool fast_extension;
|
||||||
|
int blocks_received;
|
||||||
|
int blocks_sent;
|
||||||
|
time_point start_time;
|
||||||
|
time_point end_time;
|
||||||
|
tcp::endpoint endpoint;
|
||||||
|
bool restarting;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 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.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "print_alerts.hpp"
|
||||||
|
#include "libtorrent/time.hpp"
|
||||||
|
#include "libtorrent/session.hpp"
|
||||||
|
#include "libtorrent/alert_types.hpp"
|
||||||
|
#include "print_alerts.hpp"
|
||||||
|
|
||||||
|
void print_alerts(libtorrent::session* ses, libtorrent::time_point start_time)
|
||||||
|
{
|
||||||
|
using namespace libtorrent;
|
||||||
|
namespace lt = libtorrent;
|
||||||
|
|
||||||
|
if (ses == NULL) return;
|
||||||
|
|
||||||
|
std::vector<lt::alert*> alerts;
|
||||||
|
ses->pop_alerts(&alerts);
|
||||||
|
|
||||||
|
for (std::vector<lt::alert*>::iterator i = alerts.begin()
|
||||||
|
, end(alerts.end()); i != end; ++i)
|
||||||
|
{
|
||||||
|
alert* a = *i;
|
||||||
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
|
if (peer_log_alert* pla = alert_cast<peer_log_alert>(a))
|
||||||
|
{
|
||||||
|
// in order to keep down the amount of logging, just log actual peer
|
||||||
|
// messages
|
||||||
|
if (pla->direction != peer_log_alert::incoming_message
|
||||||
|
&& pla->direction != peer_log_alert::outgoing_message)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
lt::time_duration d = a->timestamp() - start_time;
|
||||||
|
boost::uint32_t millis = lt::duration_cast<lt::milliseconds>(d).count();
|
||||||
|
printf("%4d.%03d: %-25s %s\n", millis / 1000, millis % 1000
|
||||||
|
, a->what()
|
||||||
|
, a->message().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 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 PRINT_ALERTS_HPP
|
||||||
|
#define PRINT_ALERTS_HPP
|
||||||
|
|
||||||
|
#include "libtorrent/time.hpp"
|
||||||
|
#include "libtorrent/session.hpp"
|
||||||
|
#include "test.hpp" // for EXPORT
|
||||||
|
|
||||||
|
void EXPORT print_alerts(libtorrent::session* ses, libtorrent::time_point start_time);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue