announce to trackers for all listen interfaces

This commit is contained in:
Steven Siloti 2017-04-27 20:34:39 -07:00 committed by Arvid Norberg
parent 23ba9d12d1
commit fd50630020
22 changed files with 974 additions and 461 deletions

View File

@ -1,3 +1,6 @@
* added support for running separate DHT nodes on each network interface
* added support for establishing UTP connections on any network interface
* added support for sending tracker announces on every network interface
* introduce "lt" namespace alias
* need_save_resume_data() will no longer return true every 15 minutes
* make the file_status interface explicitly public types

View File

@ -261,34 +261,92 @@ list trackers(torrent_handle& h)
dict d;
d["url"] = i->url;
d["trackerid"] = i->trackerid;
d["message"] = i->message;
dict last_error;
last_error["value"] = i->last_error.value();
last_error["category"] = i->last_error.category().name();
d["last_error"] = last_error;
if (i->next_announce > min_time()) {
d["next_announce"] = to_ptime(i->next_announce);
}
else {
d["next_announce"] = object();
}
if (i->min_announce > min_time()) {
d["min_announce"] = to_ptime(i->min_announce);
}
else {
d["min_announce"] = object();
}
d["scrape_incomplete"] = i->scrape_incomplete;
d["scrape_complete"] = i->scrape_complete;
d["scrape_downloaded"] = i->scrape_downloaded;
d["tier"] = i->tier;
d["fail_limit"] = i->fail_limit;
d["fails"] = i->fails;
d["source"] = i->source;
d["verified"] = i->verified;
d["updating"] = i->updating;
d["start_sent"] = i->start_sent;
d["complete_sent"] = i->complete_sent;
#ifndef TORRENT_NO_DEPRECATE
if (!i->endpoints.empty())
{
announce_endpoint const& aep = i->endpoints.front();
d["message"] = aep.message;
dict last_error;
last_error["value"] = aep.last_error.value();
last_error["category"] = aep.last_error.category().name();
d["last_error"] = last_error;
if (aep.next_announce > min_time()) {
d["next_announce"] = to_ptime(aep.next_announce);
}
else {
d["next_announce"] = object();
}
if (aep.min_announce > min_time()) {
d["min_announce"] = to_ptime(aep.min_announce);
}
else {
d["min_announce"] = object();
}
d["scrape_incomplete"] = aep.scrape_incomplete;
d["scrape_complete"] = aep.scrape_complete;
d["scrape_downloaded"] = aep.scrape_downloaded;
d["fails"] = aep.fails;
d["updating"] = aep.updating;
d["start_sent"] = aep.start_sent;
d["complete_sent"] = aep.complete_sent;
}
else
{
d["message"] = std::string();
dict last_error;
last_error["value"] = 0;
last_error["category"] = "";
d["last_error"] = last_error;
d["next_announce"] = object();
d["min_announce"] = object();
d["scrape_incomplete"] = 0;
d["scrape_complete"] = 0;
d["scrape_downloaded"] = 0;
d["fails"] = 0;
d["updating"] = false;
d["start_sent"] = false;
d["complete_sent"] = false;
}
#endif
list aeps;
for (auto const& aep : i->endpoints)
{
dict e;
e["message"] = aep.message;
e["local_address"] = boost::python::make_tuple(aep.local_endpoint.address().to_string(), aep.local_endpoint.port());
dict last_error;
last_error["value"] = aep.last_error.value();
last_error["category"] = aep.last_error.category().name();
e["last_error"] = last_error;
if (aep.next_announce > min_time()) {
e["next_announce"] = to_ptime(aep.next_announce);
}
else {
e["next_announce"] = object();
}
if (aep.min_announce > min_time()) {
e["min_announce"] = to_ptime(aep.min_announce);
}
else {
e["min_announce"] = object();
}
e["scrape_incomplete"] = aep.scrape_incomplete;
e["scrape_complete"] = aep.scrape_complete;
e["scrape_downloaded"] = aep.scrape_downloaded;
e["fails"] = aep.fails;
e["updating"] = aep.updating;
e["start_sent"] = aep.start_sent;
e["complete_sent"] = aep.complete_sent;
aeps.append(e);
}
d["endpoints"] = aeps;
#ifndef TORRENT_NO_DEPRECATE
d["send_stats"] = i->send_stats;
#endif
@ -345,19 +403,19 @@ void set_metadata(torrent_handle& handle, std::string const& buf)
std::shared_ptr<const torrent_info> get_torrent_info(torrent_handle const& h)
{
allow_threading_guard guard;
return h.torrent_file();
allow_threading_guard guard;
return h.torrent_file();
}
#else
std::shared_ptr<torrent_info> get_torrent_info(torrent_handle const& h)
{
// I can't figure out how to expose shared_ptr<const torrent_info>
// as well as supporting mutable instances. So, this hack is better
// than compilation errors. It seems to work on newer versions of boost though
allow_threading_guard guard;
return std::const_pointer_cast<torrent_info>(h.torrent_file());
// I can't figure out how to expose shared_ptr<const torrent_info>
// as well as supporting mutable instances. So, this hack is better
// than compilation errors. It seems to work on newer versions of boost though
allow_threading_guard guard;
return std::const_pointer_cast<torrent_info>(h.torrent_file());
}
#endif
@ -391,7 +449,7 @@ void bind_torrent_handle()
void (torrent_handle::*rename_file1)(file_index_t, std::wstring const&) const = &torrent_handle::rename_file;
#endif
std::vector<open_file_state> (torrent_handle::*file_status0)() const = &torrent_handle::file_status;
std::vector<open_file_state> (torrent_handle::*file_status0)() const = &torrent_handle::file_status;
#define _ allow_threads

View File

@ -124,23 +124,47 @@ namespace
return result;
}
#ifndef TORRENT_NO_DEPRECATE
// Create getters for announce_entry data members with non-trivial types which need converting.
lt::time_point get_next_announce(announce_entry const& ae) { return ae.next_announce; }
lt::time_point get_min_announce(announce_entry const& ae) { return ae.min_announce; }
lt::time_point get_next_announce(announce_entry const& ae)
{ return ae.endpoints.empty() ? lt::time_point() : lt::time_point(ae.endpoints.front().next_announce); }
lt::time_point get_min_announce(announce_entry const& ae)
{ return ae.endpoints.empty() ? lt::time_point() : lt::time_point(ae.endpoints.front().min_announce); }
// announce_entry data member bit-fields.
int get_fails(announce_entry const& ae) { return ae.fails; }
int get_source(announce_entry const& ae) { return ae.source; }
bool get_verified(announce_entry const& ae) { return ae.verified; }
bool get_updating(announce_entry const& ae) { return ae.updating; }
bool get_start_sent(announce_entry const& ae) { return ae.start_sent; }
bool get_complete_sent(announce_entry const& ae) { return ae.complete_sent; }
int get_fails(announce_entry const& ae)
{ return ae.endpoints.empty() ? 0 : ae.endpoints.front().fails; }
bool get_updating(announce_entry const& ae)
{ return ae.endpoints.empty() ? false : ae.endpoints.front().updating; }
bool get_start_sent(announce_entry const& ae)
{ return ae.endpoints.empty() ? false : ae.endpoints.front().start_sent; }
bool get_complete_sent(announce_entry const& ae)
{ return ae.endpoints.empty() ? false : ae.endpoints.front().complete_sent; }
// announce_entry method requires lt::time_point.
bool can_announce(announce_entry const& ae, bool is_seed) {
// an entry without endpoints implies it has never been announced so it can be now
if (ae.endpoints.empty()) return true;
lt::time_point now = lt::clock_type::now();
return ae.can_announce(now, is_seed);
return ae.endpoints.front().can_announce(now, is_seed, ae.fail_limit);
}
bool is_working(announce_entry const& ae)
{ return ae.endpoints.empty() ? false : ae.endpoints.front().is_working(); }
#endif
int get_source(announce_entry const& ae) { return ae.source; }
bool get_verified(announce_entry const& ae) { return ae.verified; }
#ifndef TORRENT_NO_DEPRECATE
std::string get_message(announce_entry const& ae)
{ return ae.endpoints.empty() ? "" : ae.endpoints.front().message; }
error_code get_last_error(announce_entry const& ae)
{ return ae.endpoints.empty() ? error_code() : ae.endpoints.front().last_error; }
int get_scrape_incomplete(announce_entry const& ae)
{ return ae.endpoints.empty() ? 0 : ae.endpoints.front().scrape_incomplete; }
int get_scrape_complete(announce_entry const& ae)
{ return ae.endpoints.empty() ? 0 : ae.endpoints.front().scrape_complete; }
int get_scrape_downloaded(announce_entry const& ae)
{ return ae.endpoints.empty() ? 0 : ae.endpoints.front().scrape_downloaded; }
int next_announce_in(announce_entry const& ae) { return 0; }
int min_announce_in(announce_entry const& ae) { return 0; }
bool get_send_stats(announce_entry const& ae) { return ae.send_stats; }
std::int64_t get_size(file_entry const& fe) { return fe.size; }
std::int64_t get_offset(file_entry const& fe) { return fe.offset; }
@ -158,7 +182,7 @@ std::shared_ptr<torrent_info> buffer_constructor0(char const* buf, int len, int
{
error_code ec;
std::shared_ptr<torrent_info> ret = std::make_shared<torrent_info>(buf
, len, ec, flags);
, len, ec, flags);
#ifndef BOOST_NO_EXCEPTIONS
if (ec) throw system_error(ec);
#endif
@ -167,14 +191,14 @@ std::shared_ptr<torrent_info> buffer_constructor0(char const* buf, int len, int
std::shared_ptr<torrent_info> buffer_constructor1(char const* buf, int len)
{
return buffer_constructor0(buf, len, 0);
return buffer_constructor0(buf, len, 0);
}
std::shared_ptr<torrent_info> file_constructor0(std::string const& filename, int flags)
{
error_code ec;
std::shared_ptr<torrent_info> ret = std::make_shared<torrent_info>(filename
, ec, flags);
, ec, flags);
#ifndef BOOST_NO_EXCEPTIONS
if (ec) throw system_error(ec);
#endif
@ -183,34 +207,34 @@ std::shared_ptr<torrent_info> file_constructor0(std::string const& filename, int
std::shared_ptr<torrent_info> file_constructor1(std::string const& filename)
{
return file_constructor0(filename, 0);
return file_constructor0(filename, 0);
}
std::shared_ptr<torrent_info> bencoded_constructor0(entry const& ent, int flags)
{
std::vector<char> buf;
bencode(std::back_inserter(buf), ent);
std::vector<char> buf;
bencode(std::back_inserter(buf), ent);
bdecode_node e;
error_code ec;
if (buf.size() == 0 || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0)
{
bdecode_node e;
error_code ec;
if (buf.size() == 0 || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0)
{
#ifndef BOOST_NO_EXCEPTIONS
throw system_error(ec);
throw system_error(ec);
#endif
}
}
std::shared_ptr<torrent_info> ret = std::make_shared<torrent_info>(e
, ec, flags);
std::shared_ptr<torrent_info> ret = std::make_shared<torrent_info>(e
, ec, flags);
#ifndef BOOST_NO_EXCEPTIONS
if (ec) throw system_error(ec);
if (ec) throw system_error(ec);
#endif
return ret;
return ret;
}
std::shared_ptr<torrent_info> bencoded_constructor1(entry const& ent)
{
return bencoded_constructor0(ent, 0);
return bencoded_constructor0(ent, 0);
}
using by_value = return_value_policy<return_by_value>;
@ -312,29 +336,31 @@ void bind_torrent_info()
class_<announce_entry>("announce_entry", init<std::string const&>())
.def_readwrite("url", &announce_entry::url)
.def_readonly("trackerid", &announce_entry::trackerid)
.def_readonly("message", &announce_entry::message)
.def_readonly("last_error", &announce_entry::last_error)
#if !defined TORRENT_NO_DEPRECATE
.add_property("message", &get_message)
.add_property("last_error", &get_last_error)
.add_property("next_announce", &get_next_announce)
.add_property("min_announce", &get_min_announce)
.def_readonly("scrape_incomplete", &announce_entry::scrape_incomplete)
.def_readonly("scrape_complete", &announce_entry::scrape_complete)
.def_readonly("scrape_downloaded", &announce_entry::scrape_downloaded)
.add_property("scrape_incomplete", &get_scrape_incomplete)
.add_property("scrape_complete", &get_scrape_complete)
.add_property("scrape_downloaded", &get_scrape_downloaded)
#endif
.def_readwrite("tier", &announce_entry::tier)
.def_readwrite("fail_limit", &announce_entry::fail_limit)
.add_property("fails", &get_fails)
.add_property("source", &get_source)
.add_property("verified", &get_verified)
#if !defined TORRENT_NO_DEPRECATE
.add_property("fails", &get_fails)
.add_property("updating", &get_updating)
.add_property("start_sent", &get_start_sent)
.add_property("complete_sent", &get_complete_sent)
#if !defined TORRENT_NO_DEPRECATE
.add_property("send_stats", &get_send_stats)
.def("next_announce_in", &announce_entry::next_announce_in)
.def("min_announce_in", &announce_entry::min_announce_in)
.def("next_announce_in", &next_announce_in)
.def("min_announce_in", &min_announce_in)
.def("can_announce", &can_announce)
.def("is_working", &is_working)
#endif
.def("reset", &announce_entry::reset)
.def("can_announce", can_announce)
.def("is_working", &announce_entry::is_working)
.def("trim", &announce_entry::trim)
;

View File

@ -98,10 +98,14 @@ class test_torrent_handle(unittest.TestCase):
trackers = [tracker]
self.h.replace_trackers(trackers)
tracker_list = [tracker for tracker in self.h.trackers()]
# wait a bit until the endpoints list gets populated
while len(tracker_list[0]['endpoints']) == 0:
time.sleep(0.1)
tracker_list = [tracker for tracker in self.h.trackers()]
pickled_trackers = pickle.dumps(tracker_list)
unpickled_trackers = pickle.loads(pickled_trackers)
self.assertEqual(unpickled_trackers[0]['url'], 'udp://tracker1.com')
self.assertEqual(unpickled_trackers[0]['last_error']['value'], 0)
self.assertEqual(unpickled_trackers[0]['endpoints'][0]['last_error']['value'], 0)
def test_file_status(self):
self.setup()
@ -129,8 +133,8 @@ class test_torrent_handle(unittest.TestCase):
self.setup()
self.h.add_tracker({'url':'udp://tracker1.com'})
tr = self.h.trackers()[0]
# wait a bit until a valid timestamp appears
while tr['next_announce'] == None:
# wait a bit until the endpoints list gets populated
while len(tr['endpoints']) == 0:
time.sleep(0.1)
tr = self.h.trackers()[0]
import json
@ -281,10 +285,10 @@ class test_torrent_info(unittest.TestCase):
def test_announce_entry(self):
ae = lt.announce_entry('test')
self.assertEquals(ae.can_announce(False), True)
self.assertEquals(ae.scrape_incomplete, -1)
self.assertEquals(ae.next_announce, None)
self.assertEquals(ae.last_error.value(), 0)
self.assertEquals(ae.url, 'test')
self.assertEquals(ae.tier, 0)
self.assertEquals(ae.verified, False)
self.assertEquals(ae.source, 0)
class test_alerts(unittest.TestCase):

View File

@ -1719,14 +1719,17 @@ MAGNETURL is a magnet link
{
for (announce_entry const& ae : h.trackers())
{
auto best_ae = std::min_element(ae.endpoints.begin(), ae.endpoints.end()
, [](announce_endpoint const& l, announce_endpoint const& r) { return l.fails < r.fails; } );
if (pos + 1 >= terminal_height) break;
std::snprintf(str, sizeof(str), "%2d %-55s fails: %-3d (%-3d) %s %s %5d \"%s\" %s\x1b[K\n"
, ae.tier, ae.url.c_str(), ae.fails, ae.fail_limit, ae.verified?"OK ":"- "
, ae.updating?"updating"
:to_string(int(total_seconds(ae.next_announce - now)), 8).c_str()
, int(ae.min_announce > now ? total_seconds(ae.min_announce - now) : 0)
, ae.last_error ? ae.last_error.message().c_str() : ""
, ae.message.c_str());
, ae.tier, ae.url.c_str()
, best_ae != ae.endpoints.end() ? best_ae->fails : 0, ae.fail_limit, ae.verified?"OK ":"- "
, to_string(int(total_seconds(best_ae->next_announce - now)), 8).c_str()
, int(best_ae->min_announce > now ? total_seconds(best_ae->min_announce - now) : 0)
, best_ae != ae.endpoints.end() && best_ae->last_error ? best_ae->last_error.message().c_str() : ""
, best_ae != ae.endpoints.end() ? best_ae->message.c_str() : "");
out += str;
pos += 1;
}

View File

@ -37,12 +37,105 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/time.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/string_view.hpp"
#include "libtorrent/socket.hpp"
#include <string>
#include <cstdint>
#include <vector>
namespace libtorrent {
namespace aux { struct session_listen_socket; }
// announces are sent to each tracker using every listen socket
// this class holds information about one listen socket for one tracker
struct TORRENT_EXPORT announce_endpoint
{
friend class torrent;
friend struct announce_entry;
// internal
explicit announce_endpoint(aux::session_listen_socket* s);
// if this tracker has returned an error or warning message
// that message is stored here
std::string message;
// if this tracker failed the last time it was contacted
// this error code specifies what error occurred
error_code last_error;
// the local endpoint of the listen interface associated with this endpoint
tcp::endpoint local_endpoint;
// the time of next tracker announce
time_point32 next_announce = time_point32::min();
// no announces before this time
time_point32 min_announce = time_point32::min();
private:
// internal
aux::session_listen_socket* socket;
public:
// TODO: include the number of peers received from this tracker, at last
// announce
// these are either -1 or the scrape information this tracker last
// responded with. *incomplete* is the current number of downloaders in
// the swarm, *complete* is the current number of seeds in the swarm and
// *downloaded* is the cumulative number of completed downloads of this
// torrent, since the beginning of time (from this tracker's point of
// view).
// if this tracker has returned scrape data, these fields are filled in
// with valid numbers. Otherwise they are set to -1. the number of
// current downloaders
int scrape_incomplete = -1;
int scrape_complete = -1;
int scrape_downloaded = -1;
// the number of times in a row we have failed to announce to this
// tracker.
std::uint8_t fails : 7;
// true while we're waiting for a response from the tracker.
bool updating : 1;
// set to true when we get a valid response from an announce
// with event=started. If it is set, we won't send start in the subsequent
// announces.
bool start_sent : 1;
// set to true when we send a event=completed.
bool complete_sent : 1;
// internal
bool triggered_manually : 1;
// reset announce counters and clears the started sent flag.
// The announce_endpoint will look like we've never talked to
// the tracker.
void reset();
// updates the failure counter and time-outs for re-trying.
// This is called when the tracker announce fails.
void failed(int backoff_ratio, seconds32 retry_interval = seconds32(0));
// returns true if we can announce to this tracker now.
// The current time is passed in as ``now``. The ``is_seed``
// argument is necessary because once we become a seed, we
// need to announce right away, even if the re-announce timer
// hasn't expired yet.
bool can_announce(time_point now, bool is_seed, std::uint8_t fail_limit) const;
// returns true if the last time we tried to announce to this
// tracker succeeded, or if we haven't tried yet.
bool is_working() const { return fails == 0; }
};
// this class holds information about one bittorrent tracker, as it
// relates to a specific torrent.
struct TORRENT_EXPORT announce_entry
@ -62,50 +155,7 @@ namespace libtorrent {
// trackerid is sent).
std::string trackerid;
// if this tracker has returned an error or warning message
// that message is stored here
std::string message;
// if this tracker failed the last time it was contacted
// this error code specifies what error occurred
error_code last_error;
#ifndef TORRENT_NO_DEPRECATE
// returns the number of seconds to the next announce on this tracker.
// ``min_announce_in()`` returns the number of seconds until we are
// allowed to force another tracker update with this tracker.
//
// If the last time this tracker was contacted failed, ``last_error`` is
// the error code describing what error occurred.
TORRENT_DEPRECATED
int next_announce_in() const;
TORRENT_DEPRECATED
int min_announce_in() const;
#endif
// the time of next tracker announce
time_point32 next_announce = time_point32::min();
// no announces before this time
time_point32 min_announce = time_point32::min();
// TODO: include the number of peers received from this tracker, at last
// announce
// these are either -1 or the scrape information this tracker last
// responded with. *incomplete* is the current number of downloaders in
// the swarm, *complete* is the current number of seeds in the swarm and
// *downloaded* is the cumulative number of completed downloads of this
// torrent, since the beginning of time (from this tracker's point of
// view).
// if this tracker has returned scrape data, these fields are filled in
// with valid numbers. Otherwise they are set to -1. the number of
// current downloaders
int scrape_incomplete = -1;
int scrape_complete = -1;
int scrape_downloaded = -1;
std::vector<announce_endpoint> endpoints;
// the tier this tracker belongs to
std::uint8_t tier = 0;
@ -114,13 +164,6 @@ namespace libtorrent {
// a row, before this tracker is not used anymore. 0 means unlimited
std::uint8_t fail_limit = 0;
// the number of times in a row we have failed to announce to this
// tracker.
std::uint8_t fails:7;
// true while we're waiting for a response from the tracker.
bool updating:1;
// flags for the source bitmask, each indicating where
// we heard about this tracker
enum tracker_source
@ -142,57 +185,49 @@ namespace libtorrent {
// from this tracker.
bool verified:1;
// set to true when we get a valid response from an announce
// with event=started. If it is set, we won't send start in the subsequent
// announces.
bool start_sent:1;
// set to true when we send a event=completed.
bool complete_sent:1;
#ifndef TORRENT_NO_DEPRECATE
// deprecated in 1.2
// this is false the stats sent to this tracker will be 0
bool send_stats:1;
// all of these will be set to false or 0
// use the corresponding members in announce_endpoint
std::uint8_t TORRENT_DEPRECATED_MEMBER fails:7;
bool TORRENT_DEPRECATED_MEMBER send_stats:1;
bool TORRENT_DEPRECATED_MEMBER start_sent:1;
bool TORRENT_DEPRECATED_MEMBER complete_sent:1;
// internal
bool TORRENT_DEPRECATED_MEMBER triggered_manually:1;
bool TORRENT_DEPRECATED_MEMBER updating:1;
#else
// hidden
std::uint8_t deprecated_fails:7;
bool deprecated_send_stats:1;
bool deprecated_start_sent:1;
bool deprecated_complete_sent:1;
bool deprecated_triggered_manually:1;
bool deprecated_updating:1;
#endif
// internal
bool triggered_manually:1;
// reset announce counters and clears the started sent flag.
// The announce_entry will look like we've never talked to
// the tracker.
void reset();
// updates the failure counter and time-outs for re-trying.
// This is called when the tracker announce fails.
void failed(int backoff_ratio, seconds32 retry_interval = seconds32(0));
#ifndef TORRENT_NO_DEPRECATE
// deprecated in 1.0
TORRENT_DEPRECATED
bool will_announce(time_point now) const
{
return now <= next_announce
&& (fails < fail_limit || fail_limit == 0)
&& !updating;
}
#endif
// deprecated in 1.2, use announce_endpoint::can_announce
// returns true if we can announce to this tracker now.
// The current time is passed in as ``now``. The ``is_seed``
// argument is necessary because once we become a seed, we
// need to announce right away, even if the re-announce timer
// hasn't expired yet.
bool can_announce(time_point now, bool is_seed) const;
TORRENT_DEPRECATED bool can_announce(time_point now, bool is_seed) const;
// deprecated in 1.2, use announce_endpoint::is_working
// returns true if the last time we tried to announce to this
// tracker succeeded, or if we haven't tried yet.
bool is_working() const
{ return fails == 0; }
TORRENT_DEPRECATED bool is_working() const;
#endif
// internal
announce_endpoint* find_endpoint(aux::session_listen_socket* s);
// trims whitespace characters from the beginning of the URL.
void trim();

View File

@ -195,6 +195,11 @@ namespace aux {
listen_endpoint_t(address adr, int p, std::string dev, bool s)
: addr(adr), port(p), device(dev), ssl(s) {}
bool operator==(listen_endpoint_t const& o) const
{
return addr == o.addr && port == o.port && device == o.device && ssl == o.ssl;
}
address addr;
int port;
std::string device;
@ -210,6 +215,11 @@ namespace aux {
std::vector<listen_endpoint_t>& eps
, std::list<listen_socket_t>& sockets);
// expand [::] to all IPv6 interfaces for BEP 45 compliance
TORRENT_EXTRA_EXPORT void expand_unspecified_address(
std::vector<ip_interface> const& ifs
, std::vector<listen_endpoint_t>& eps);
// this is the link between the main thread and the
// thread started to run the main downloader loop
struct TORRENT_EXTRA_EXPORT session_impl final
@ -563,6 +573,14 @@ namespace aux {
std::uint16_t ssl_listen_port() const override;
std::uint16_t ssl_listen_port(listen_socket_t* sock) const;
void for_each_listen_socket(std::function<void(aux::session_listen_socket*)> f) override
{
for (auto& s : m_listen_sockets)
{
f(&s);
}
}
alert_manager& alerts() override { return m_alerts; }
disk_interface& disk_thread() override { return m_disk_thread; }

View File

@ -198,6 +198,8 @@ namespace libtorrent { namespace aux {
virtual std::uint16_t listen_port() const = 0;
virtual std::uint16_t ssl_listen_port() const = 0;
virtual void for_each_listen_socket(std::function<void(aux::session_listen_socket*)> f) = 0;
// ask for which interface and port to bind outgoing peer connections on
virtual tcp::endpoint bind_outgoing_socket(socket_type& s, address const&
remote_address, error_code& ec) const = 0;

View File

@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/disable_warnings_push.hpp"
#include <boost/noncopyable.hpp>
#include <boost/optional.hpp>
#ifdef TORRENT_USE_OPENSSL
#include <boost/asio/ssl/context.hpp>
@ -101,7 +102,7 @@ struct TORRENT_EXTRA_EXPORT http_connection
void get(std::string const& url, time_duration timeout = seconds(30)
, int prio = 0, aux::proxy_settings const* ps = 0, int handle_redirects = 5
, std::string const& user_agent = std::string()
, address const& bind_addr = address_v4::any()
, boost::optional<address> const& bind_addr = boost::optional<address>()
, int resolve_flags = 0, std::string const& auth_ = std::string()
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn = 0
@ -111,7 +112,7 @@ struct TORRENT_EXTRA_EXPORT http_connection
void start(std::string const& hostname, int port
, time_duration timeout, int prio = 0, aux::proxy_settings const* ps = 0
, bool ssl = false, int handle_redirect = 5
, address const& bind_addr = address_v4::any()
, boost::optional<address> const& bind_addr = boost::optional<address>()
, int resolve_flags = 0
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn = 0
@ -187,9 +188,8 @@ private:
// configured to use a proxy
aux::proxy_settings m_proxy;
// the address to bind to. address_v4::any()
// means do not bind
address m_bind_addr;
// the address to bind to
boost::optional<address> m_bind_addr;
// if username password was passed in, remember it in case we need to
// re-issue the request for a redirect

View File

@ -76,7 +76,7 @@ namespace libtorrent {
#if TORRENT_USE_I2P
class i2p_connection;
#endif
namespace aux { struct session_logger; struct session_settings; }
namespace aux { struct session_listen_socket; struct session_logger; struct session_settings; }
// returns -1 if gzip header is invalid or the header size in bytes
TORRENT_EXTRA_EXPORT int gzip_header(const char* buf, int size);
@ -94,6 +94,7 @@ namespace libtorrent {
, kind(announce_request)
, key(0)
, num_want(0)
, outgoing_socket(nullptr)
, private_torrent(false)
, triggered_manually(false)
#ifdef TORRENT_USE_OPENSSL
@ -151,7 +152,7 @@ namespace libtorrent {
#endif
sha1_hash info_hash;
peer_id pid;
address bind_ip;
aux::session_listen_socket* outgoing_socket;
// set to true if the .torrent file this tracker announce is for is marked
// as private (i.e. has the "priv": 1 key)
@ -306,7 +307,8 @@ namespace libtorrent {
, seconds32 interval = seconds32(0), seconds32 min_interval = seconds32(0));
virtual void start() = 0;
virtual void close() = 0;
address const& bind_interface() const { return m_req.bind_ip; }
address bind_interface() const;
aux::session_listen_socket* bind_socket() const { return m_req.outgoing_socket; }
void sent_bytes(int bytes);
void received_bytes(int bytes);
@ -336,10 +338,12 @@ namespace libtorrent {
{
public:
typedef std::function<void(udp::endpoint const&
typedef std::function<void(aux::session_listen_socket*
, udp::endpoint const&
, span<char const>
, error_code&, int)> send_fun_t;
typedef std::function<void(char const*, int
typedef std::function<void(aux::session_listen_socket*
, char const*, int
, span<char const>
, error_code&, int)> send_fun_hostname_t;
@ -385,10 +389,12 @@ namespace libtorrent {
aux::session_settings const& settings() const { return m_settings; }
resolver_interface& host_resolver() { return m_host_resolver; }
void send_hostname(char const* hostname, int port, span<char const> p
void send_hostname(aux::session_listen_socket* sock
, char const* hostname, int port, span<char const> p
, error_code& ec, int flags = 0);
void send(udp::endpoint const& ep, span<char const> p
void send(aux::session_listen_socket* sock
, udp::endpoint const& ep, span<char const> p
, error_code& ec, int flags = 0);
private:

View File

@ -171,7 +171,7 @@ std::shared_ptr<http_connection> test_request(io_service& ios
std::printf("CONNECTED: %s\n", url.c_str());
});
h->get(url, seconds(1), 0, &ps, 5, "test/user-agent", address_v4::any()
h->get(url, seconds(1), 0, &ps, 5, "test/user-agent", address(address_v4::any())
, 0, auth);
return h;
}

View File

@ -91,6 +91,10 @@ void test_interval(int interval)
std::vector<lt::time_point> announce_alerts;
lt::settings_pack default_settings = settings();
// since the test tracker is only listening on IPv4 we need to configure the
// client to do the same so that the number of tracker_announce_alerts matches
// the number of announces seen by the tracker
default_settings.set_str(settings_pack::listen_interfaces, "0.0.0.0:6881");
lt::add_torrent_params default_add_torrent;
setup_swarm(1, swarm_test::upload, sim, default_settings, default_add_torrent
@ -261,6 +265,8 @@ TORRENT_TEST(announce_interval_1200)
struct sim_config : sim::default_config
{
explicit sim_config(bool ipv6 = true) : ipv6(ipv6) {}
chrono::high_resolution_clock::duration hostname_lookup(
asio::ip::address const& requestor
, std::string hostname
@ -270,12 +276,15 @@ struct sim_config : sim::default_config
if (hostname == "tracker.com")
{
result.push_back(address_v4::from_string("10.0.0.2"));
result.push_back(address_v6::from_string("ff::dead:beef"));
if (ipv6)
result.push_back(address_v6::from_string("ff::dead:beef"));
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
}
return default_config::hostname_lookup(requestor, hostname, result, ec);
}
bool ipv6;
};
void on_alert_notify(lt::session* ses)
@ -337,11 +346,23 @@ TORRENT_TEST(ipv6_support)
return sim::send_response(200, "OK", size) + response;
});
static const int num_interfaces = 3;
{
lt::session_proxy zombie;
asio::io_service ios(sim, { address_v4::from_string("10.0.0.3")
, address_v6::from_string("ffff::1337") });
std::vector<asio::ip::address> ips;
for (int i = 0; i < num_interfaces; i++)
{
char ep[30];
std::snprintf(ep, sizeof(ep), "10.0.0.%d", i + 1);
ips.push_back(address::from_string(ep));
std::snprintf(ep, sizeof(ep), "ffff::1337:%d", i + 1);
ips.push_back(address::from_string(ep));
}
asio::io_service ios(sim, ips);
lt::settings_pack sett = settings();
std::unique_ptr<lt::session> ses(new lt::session(sett, ios));
@ -380,7 +401,8 @@ TORRENT_TEST(ipv6_support)
// 2 because there's one announce on startup and one when shutting down
TEST_EQUAL(v4_announces, 2);
TEST_EQUAL(v6_announces, 2);
// IPv6 will send announces for each interface
TEST_EQUAL(v6_announces, num_interfaces * 2);
}
// this runs a simulation of a torrent with tracker(s), making sure the request
@ -400,11 +422,14 @@ void tracker_test(Setup setup, Announce a, Test1 test1, Test2 test2
sim::simulation sim{network_cfg};
sim::asio::io_service tracker_ios(sim, address_v4::from_string("10.0.0.2"));
sim::asio::io_service tracker_ios6(sim, address_v6::from_string("ff::dead:beef"));
// listen on port 8080
sim::http_server http(tracker_ios, 8080);
sim::http_server http6(tracker_ios6, 8080);
http.register_handler(url_path, a);
http6.register_handler(url_path, a);
lt::session_proxy zombie;
@ -491,11 +516,15 @@ TORRENT_TEST(test_error)
}
, [](announce_entry const& ae)
{
TEST_EQUAL(ae.is_working(), false);
TEST_EQUAL(ae.message, "test");
TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
TEST_EQUAL(ae.last_error, error_code(errors::tracker_failure));
TEST_EQUAL(ae.fails, 1);
TEST_EQUAL(ae.endpoints.size(), 2);
for (auto const& aep : ae.endpoints)
{
TEST_EQUAL(aep.is_working(), false);
TEST_EQUAL(aep.message, "test");
TEST_EQUAL(aep.last_error, error_code(errors::tracker_failure));
TEST_EQUAL(aep.fails, 1);
}
});
}
@ -513,11 +542,15 @@ TORRENT_TEST(test_warning)
}
, [](announce_entry const& ae)
{
TEST_EQUAL(ae.is_working(), true);
TEST_EQUAL(ae.message, "test2");
TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
TEST_EQUAL(ae.last_error, error_code());
TEST_EQUAL(ae.fails, 0);
TEST_EQUAL(ae.endpoints.size(), 2);
for (auto const& aep : ae.endpoints)
{
TEST_EQUAL(aep.is_working(), true);
TEST_EQUAL(aep.message, "test2");
TEST_EQUAL(aep.last_error, error_code());
TEST_EQUAL(aep.fails, 0);
}
});
}
@ -536,14 +569,18 @@ TORRENT_TEST(test_scrape_data_in_announce)
}
, [](announce_entry const& ae)
{
TEST_EQUAL(ae.is_working(), true);
TEST_EQUAL(ae.message, "");
TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
TEST_EQUAL(ae.last_error, error_code());
TEST_EQUAL(ae.fails, 0);
TEST_EQUAL(ae.scrape_complete, 1);
TEST_EQUAL(ae.scrape_incomplete, 2);
TEST_EQUAL(ae.scrape_downloaded, 3);
TEST_EQUAL(ae.endpoints.size(), 2);
for (auto const& aep : ae.endpoints)
{
TEST_EQUAL(aep.is_working(), true);
TEST_EQUAL(aep.message, "");
TEST_EQUAL(aep.last_error, error_code());
TEST_EQUAL(aep.fails, 0);
TEST_EQUAL(aep.scrape_complete, 1);
TEST_EQUAL(aep.scrape_incomplete, 2);
TEST_EQUAL(aep.scrape_downloaded, 3);
}
});
}
@ -570,9 +607,13 @@ TORRENT_TEST(test_scrape)
TEST_EQUAL(tr.size(), 1);
announce_entry const& ae = tr[0];
TEST_EQUAL(ae.scrape_incomplete, 2);
TEST_EQUAL(ae.scrape_complete, 1);
TEST_EQUAL(ae.scrape_downloaded, 3);
TEST_EQUAL(ae.endpoints.size(), 2);
for (auto const& aep : ae.endpoints)
{
TEST_EQUAL(aep.scrape_incomplete, 2);
TEST_EQUAL(aep.scrape_complete, 1);
TEST_EQUAL(aep.scrape_downloaded, 3);
}
}
, "/scrape");
}
@ -588,11 +629,15 @@ TORRENT_TEST(test_http_status)
}
, [](announce_entry const& ae)
{
TEST_EQUAL(ae.is_working(), false);
TEST_EQUAL(ae.message, "Not A Tracker");
TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
TEST_EQUAL(ae.last_error, error_code(410, http_category()));
TEST_EQUAL(ae.fails, 1);
TEST_EQUAL(ae.endpoints.size(), 2);
for (auto const& aep : ae.endpoints)
{
TEST_EQUAL(aep.is_working(), false);
TEST_EQUAL(aep.message, "Not A Tracker");
TEST_EQUAL(aep.last_error, error_code(410, http_category()));
TEST_EQUAL(aep.fails, 1);
}
});
}
@ -610,11 +655,15 @@ TORRENT_TEST(test_interval)
}
, [](announce_entry const& ae)
{
TEST_EQUAL(ae.is_working(), true);
TEST_EQUAL(ae.message, "");
TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
TEST_EQUAL(ae.last_error, error_code());
TEST_EQUAL(ae.fails, 0);
TEST_EQUAL(ae.endpoints.size(), 2);
for (auto const& aep : ae.endpoints)
{
TEST_EQUAL(aep.is_working(), true);
TEST_EQUAL(aep.message, "");
TEST_EQUAL(aep.last_error, error_code());
TEST_EQUAL(aep.fails, 0);
}
TEST_EQUAL(ae.trackerid, "testtest");
});
@ -634,12 +683,16 @@ TORRENT_TEST(test_invalid_bencoding)
}
, [](announce_entry const& ae)
{
TEST_EQUAL(ae.is_working(), false);
TEST_EQUAL(ae.message, "");
TEST_EQUAL(ae.url, "http://tracker.com:8080/announce");
TEST_EQUAL(ae.last_error, error_code(bdecode_errors::expected_value
, bdecode_category()));
TEST_EQUAL(ae.fails, 1);
TEST_EQUAL(ae.endpoints.size(), 2);
for (auto const& aep : ae.endpoints)
{
TEST_EQUAL(aep.is_working(), false);
TEST_EQUAL(aep.message, "");
TEST_EQUAL(aep.last_error, error_code(bdecode_errors::expected_value
, bdecode_category()));
TEST_EQUAL(aep.fails, 1);
}
});
}
@ -685,22 +738,31 @@ TORRENT_TEST(try_next)
std::printf("tracker \"%s\"\n", tr[i].url.c_str());
if (tr[i].url == "http://tracker.com:8080/announce")
{
TEST_EQUAL(tr[i].fails, 0);
for (auto const& aep : tr[i].endpoints)
{
TEST_EQUAL(aep.fails, 0);
}
TEST_EQUAL(tr[i].verified, true);
}
else if (tr[i].url == "http://failing-tracker.com/announce")
{
TEST_CHECK(tr[i].fails >= 1);
for (auto const& aep : tr[i].endpoints)
{
TEST_CHECK(aep.fails >= 1);
TEST_EQUAL(aep.last_error
, error_code(boost::asio::error::host_not_found));
}
TEST_EQUAL(tr[i].verified, false);
TEST_EQUAL(tr[i].last_error
, error_code(boost::asio::error::host_not_found));
}
else if (tr[i].url == "udp://failing-tracker.com/announce")
{
TEST_CHECK(tr[i].fails >= 1);
TEST_EQUAL(tr[i].verified, false);
TEST_EQUAL(tr[i].last_error
, error_code(boost::asio::error::host_not_found));
for (auto const& aep : tr[i].endpoints)
{
TEST_CHECK(aep.fails >= 1);
TEST_EQUAL(aep.last_error
, error_code(boost::asio::error::host_not_found));
}
}
else
{

View File

@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/string_util.hpp" // for is_space
#include "libtorrent/aux_/time.hpp"
#include "libtorrent/aux_/session_settings.hpp"
#include "libtorrent/aux_/session_listen_socket.hpp"
namespace libtorrent {
@ -46,47 +47,55 @@ namespace libtorrent {
minutes32 constexpr tracker_retry_delay_max{60};
}
announce_entry::announce_entry(string_view u)
: url(u.to_string())
announce_endpoint::announce_endpoint(aux::session_listen_socket* s)
: local_endpoint(s ? s->get_local_endpoint() : tcp::endpoint())
, socket(s)
, fails(0)
, updating(false)
, source(0)
, verified(false)
, start_sent(false)
, complete_sent(false)
, triggered_manually(false)
{}
announce_entry::announce_entry()
: fails(0)
, updating(false)
announce_entry::announce_entry(string_view u)
: url(u.to_string())
, source(0)
, verified(false)
#ifndef TORRENT_NO_DEPRECATE
, fails(0)
, send_stats(false)
, start_sent(false)
, complete_sent(false)
, triggered_manually(false)
, updating(false)
#endif
{}
announce_entry::announce_entry()
: source(0)
, verified(false)
#ifndef TORRENT_NO_DEPRECATE
, fails(0)
, send_stats(false)
, start_sent(false)
, complete_sent(false)
, triggered_manually(false)
, updating(false)
#endif
{}
announce_entry::~announce_entry() = default;
announce_entry::announce_entry(announce_entry const&) = default;
announce_entry& announce_entry::operator=(announce_entry const&) = default;
#ifndef TORRENT_NO_DEPRECATE
int announce_entry::next_announce_in() const
{ return int(total_seconds(next_announce - aux::time_now())); }
int announce_entry::min_announce_in() const
{ return int(total_seconds(min_announce - aux::time_now())); }
#endif
void announce_entry::reset()
void announce_endpoint::reset()
{
start_sent = false;
next_announce = time_point32::min();
min_announce = time_point32::min();
}
void announce_entry::failed(int const backoff_ratio, seconds32 const retry_interval)
void announce_endpoint::failed(int const backoff_ratio, seconds32 const retry_interval)
{
++fails;
// the exponential back-off ends up being:
@ -98,11 +107,11 @@ namespace libtorrent {
, tracker_retry_delay_min
+ fail_square * tracker_retry_delay_min * backoff_ratio / 100
));
next_announce = aux::time_now32() + delay;
if (!is_working()) next_announce = aux::time_now32() + delay;
updating = false;
}
bool announce_entry::can_announce(time_point now, bool is_seed) const
bool announce_endpoint::can_announce(time_point now, bool is_seed, std::uint8_t fail_limit) const
{
// if we're a seed and we haven't sent a completed
// event, we need to let this announce through
@ -114,6 +123,34 @@ namespace libtorrent {
&& !updating;
}
void announce_entry::reset()
{
for (auto& aep : endpoints)
aep.reset();
}
#ifndef TORRENT_NO_DEPRECATE
bool announce_entry::can_announce(time_point now, bool is_seed) const
{
return std::any_of(endpoints.begin(), endpoints.end()
, [&](announce_endpoint const& aep) { return aep.can_announce(now, is_seed, fail_limit); });
}
bool announce_entry::is_working() const
{
return std::any_of(endpoints.begin(), endpoints.end()
, [](announce_endpoint const& aep) { return aep.is_working(); });
}
#endif
announce_endpoint* announce_entry::find_endpoint(aux::session_listen_socket* s)
{
auto aep = std::find_if(endpoints.begin(), endpoints.end()
, [&](announce_endpoint const& a) { return a.socket == s; });
if (aep != endpoints.end()) return &*aep;
else return nullptr;
}
void announce_entry::trim()
{
while (!url.empty() && is_space(url[0]))

View File

@ -109,7 +109,7 @@ http_connection::~http_connection()
void http_connection::get(std::string const& url, time_duration timeout, int prio
, aux::proxy_settings const* ps, int handle_redirects, std::string const& user_agent
, address const& bind_addr, int resolve_flags, std::string const& auth_
, boost::optional<address> const& bind_addr, int resolve_flags, std::string const& auth_
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn
#endif
@ -225,7 +225,7 @@ void http_connection::get(std::string const& url, time_duration timeout, int pri
void http_connection::start(std::string const& hostname, int port
, time_duration timeout, int prio, aux::proxy_settings const* ps, bool ssl
, int handle_redirects
, address const& bind_addr
, boost::optional<address> const& bind_addr
, int resolve_flags
#if TORRENT_USE_I2P
, i2p_connection* i2p_conn
@ -353,10 +353,10 @@ void http_connection::start(std::string const& hostname, int port
instantiate_connection(m_timer.get_io_service()
, proxy ? *proxy : null_proxy, m_sock, userdata, nullptr, false, false);
if (m_bind_addr != address_v4::any())
if (m_bind_addr)
{
m_sock.open(m_bind_addr.is_v4() ? tcp::v4() : tcp::v6(), ec);
m_sock.bind(tcp::endpoint(m_bind_addr, 0), ec);
m_sock.open(m_bind_addr->is_v4() ? tcp::v4() : tcp::v6(), ec);
m_sock.bind(tcp::endpoint(*m_bind_addr, 0), ec);
if (ec)
{
m_timer.get_io_service().post(std::bind(&http_connection::callback
@ -530,13 +530,28 @@ void http_connection::on_resolve(error_code const& e
aux::random_shuffle(m_endpoints.begin(), m_endpoints.end());
// sort the endpoints so that the ones with the same IP version as our
// bound listen socket are first. So that when contacting a tracker,
// we'll talk to it from the same IP that we're listening on
if (m_bind_addr != address_v4::any())
std::partition(m_endpoints.begin(), m_endpoints.end()
// if we have been told to bind to a particular address
// only connect to addresses of the same family
if (m_bind_addr)
{
auto new_end = std::partition(m_endpoints.begin(), m_endpoints.end()
, [this] (tcp::endpoint const& ep)
{ return ep.address().is_v4() == m_bind_addr.is_v4(); });
{
if (ep.address().is_v4() != m_bind_addr->is_v4())
return false;
if (ep.address().is_v4() && m_bind_addr->is_v4())
return true;
TORRENT_ASSERT(ep.address().is_v6() && m_bind_addr->is_v6());
return ep.address().to_v6().scope_id() == m_bind_addr->to_v6().scope_id();
});
m_endpoints.erase(new_end, m_endpoints.end());
if (m_endpoints.empty())
{
callback(error_code(boost::system::errc::address_family_not_supported, generic_category()));
close();
return;
}
}
connect();
}

View File

@ -291,6 +291,49 @@ namespace aux {
});
}
// To comply with BEP 45 multi homed clients must run separate DHT nodes
// on each interface they use to talk to the DHT. For IPv6 this is enforced
// by prohibiting creating a listen socket on [::]. Instead the list of
// interfaces is enumerated and sockets are created for each of them.
// This is not enforced for 0.0.0.0 because multi homed IPv4 configurations
// are much less common and the presence of NAT means that we cannot
// automatically determine which interfaces should have DHT nodes started on
// them.
void expand_unspecified_address(std::vector<ip_interface> const& ifs
, std::vector<listen_endpoint_t>& eps)
{
auto unspeficied_begin = std::partition(eps.begin(), eps.end()
, [](listen_endpoint_t const& ep) { return !(ep.addr.is_v6() && ep.addr.is_unspecified()); });
std::vector<listen_endpoint_t> unspecified_eps(unspeficied_begin, eps.end());
eps.erase(unspeficied_begin, eps.end());
for (auto const& uep : unspecified_eps)
{
for (auto const& ipface : ifs)
{
if (ipface.interface_address.is_v4())
continue;
if (ipface.interface_address.is_loopback())
continue;
if (!uep.device.empty() && uep.device != ipface.name)
continue;
if (std::any_of(eps.begin(), eps.end(), [&](listen_endpoint_t const& e)
{
// ignore device name because we don't want to create
// duplicates if the user explicitly configured an address
// without a device name
return e.addr == ipface.interface_address
&& e.port == uep.port
&& e.ssl == uep.ssl;
}))
{
continue;
}
eps.emplace_back(ipface.interface_address, uep.port, uep.device, uep.ssl);
}
}
}
void session_impl::init_peer_class_filter(bool unlimited_local)
{
// set the default peer_class_filter to use the local peer class
@ -423,8 +466,8 @@ namespace aux {
, m_upload_rate(peer_connection::upload_channel)
, m_host_resolver(m_io_service)
, m_tracker_manager(
std::bind(&session_impl::send_udp_packet_deprecated, this, false, _1, _2, _3, _4)
, std::bind(&session_impl::send_udp_packet_hostname_deprecated, this, _1, _2, _3, _4, _5)
std::bind(&session_impl::send_udp_packet_listen, this, _1, _2, _3, _4, _5)
, std::bind(&session_impl::send_udp_packet_hostname_listen, this, _1, _2, _3, _4, _5, _6)
, m_stats_counters
, m_host_resolver
, m_settings
@ -1151,18 +1194,6 @@ namespace {
void session_impl::queue_tracker_request(tracker_request& req
, std::weak_ptr<request_callback> c)
{
req.listen_port = listen_port();
if (m_key) req.key = m_key;
#ifdef TORRENT_USE_OPENSSL
// SSL torrents use the SSL listen port
// TODO: 2 this need to be more thought through. There isn't necessarily
// just _one_ SSL listen port, which one we use depends on which interface
// we announce from.
if (req.ssl_ctx) req.listen_port = ssl_listen_port();
req.ssl_ctx = &m_ssl_ctx;
#endif
#if TORRENT_USE_I2P
if (!m_settings.get_str(settings_pack::i2p_hostname).empty())
{
@ -1170,8 +1201,36 @@ namespace {
}
#endif
//TODO: should there be an option to announce once per listen interface?
m_tracker_manager.queue_request(get_io_service(), req, c);
if (m_key) req.key = m_key;
#ifdef TORRENT_USE_OPENSSL
bool use_ssl = req.ssl_ctx != nullptr;
req.ssl_ctx = &m_ssl_ctx;
#endif
if (req.outgoing_socket)
{
listen_socket_t* ls = static_cast<listen_socket_t*>(req.outgoing_socket);
req.listen_port = listen_port(ls);
#ifdef TORRENT_USE_OPENSSL
// SSL torrents use the SSL listen port
if (use_ssl) req.listen_port = ssl_listen_port(ls);
#endif
m_tracker_manager.queue_request(get_io_service(), req, c);
}
else
{
for (auto& ls : m_listen_sockets)
{
req.listen_port = listen_port(&ls);
#ifdef TORRENT_USE_OPENSSL
// SSL torrents use the SSL listen port
if (use_ssl) req.listen_port = ssl_listen_port(&ls);
#endif
req.outgoing_socket = &ls;
m_tracker_manager.queue_request(get_io_service(), req, c);
}
}
}
void session_impl::set_peer_class(peer_class_t cid, peer_class_info const& pci)
@ -1876,6 +1935,14 @@ namespace {
interface_to_endpoints(device, port, ssl, eps);
}
#if TORRENT_USE_IPV6
std::vector<ip_interface> const ifs = enum_net_interfaces(m_io_service, ec);
if (!ec)
{
expand_unspecified_address(ifs, eps);
}
#endif
auto remove_iter = partition_listen_sockets(eps, m_listen_sockets);
while (remove_iter != m_listen_sockets.end())

View File

@ -1473,8 +1473,6 @@ namespace libtorrent {
// this is needed for openssl < 1.0 to decrypt keys created by openssl 1.0+
OpenSSL_add_all_algorithms();
TORRENT_ASSERT(RAND_status() == 1);
// create the SSL context for this torrent. We need to
// inject the root certificate, and no other, to
// verify other peers against
@ -2569,6 +2567,28 @@ namespace libtorrent {
#endif
namespace
{
struct announce_state
{
explicit announce_state(aux::session_listen_socket* s)
: socket(s) {}
aux::session_listen_socket* socket;
// the tier is kept as INT_MAX until we find the first
// tracker that works, then it's set to that tracker's
// tier.
int tier = INT_MAX;
// have we sent an announce in this tier yet?
bool sent_announce = false;
// have we finished sending announces on this listen socket?
bool done = false;
};
}
void torrent::announce_with_tracker(std::uint8_t e)
{
TORRENT_ASSERT(is_single_thread());
@ -2666,60 +2686,90 @@ namespace libtorrent {
time_point32 const now = aux::time_now32();
// the tier is kept as INT_MAX until we find the first
// tracker that works, then it's set to that tracker's
// tier.
int tier = INT_MAX;
// have we sent an announce in this tier yet?
bool sent_announce = false;
// each listen socket gets it's own announce state
// so that each one should get at least one announce
std::vector<announce_state> listen_socket_states;
for (auto& ae : m_trackers)
{
// update the endpoint list by adding entries for new listen sockets
// and removing entries for non-existent ones
std::vector<announce_endpoint>::size_type valid_endpoints = 0;
m_ses.for_each_listen_socket([&](aux::session_listen_socket* s) {
for (auto& aep : ae.endpoints)
{
if (aep.socket != s) continue;
std::swap(ae.endpoints[valid_endpoints], aep);
valid_endpoints++;
return;
}
ae.endpoints.emplace_back(s);
std::swap(ae.endpoints[valid_endpoints], ae.endpoints.back());
valid_endpoints++;
});
TORRENT_ASSERT(valid_endpoints <= ae.endpoints.size());
ae.endpoints.erase(ae.endpoints.begin() + int(valid_endpoints), ae.endpoints.end());
// if trackerid is not specified for tracker use default one, probably set explicitly
req.trackerid = ae.trackerid.empty() ? m_trackerid : ae.trackerid;
req.url = ae.url;
for (auto& aep : ae.endpoints)
{
auto aep_state_iter = std::find_if(listen_socket_states.begin(), listen_socket_states.end()
, [&](announce_state const& s) { return s.socket == aep.socket; });
if (aep_state_iter == listen_socket_states.end())
{
listen_socket_states.emplace_back(aep.socket);
aep_state_iter = listen_socket_states.end() - 1;
}
announce_state& state = *aep_state_iter;
if (state.done) continue;
#ifndef TORRENT_DISABLE_LOGGING
if (should_log())
{
debug_log("*** tracker: \"%s\" "
"[ tiers: %d trackers: %d"
" i->tier: %d tier: %d"
" working: %d fails: %d limit: %d upd: %d"
" working: %d limit: %d"
" can: %d sent: %d ]"
, ae.url.c_str(), settings().get_bool(settings_pack::announce_to_all_tiers)
, settings().get_bool(settings_pack::announce_to_all_trackers)
, ae.tier, tier, ae.is_working(), ae.fails, ae.fail_limit
, ae.updating, ae.can_announce(now, is_seed()), sent_announce);
, ae.tier, state.tier, aep.is_working(), ae.fail_limit
, aep.can_announce(now, is_seed(), ae.fail_limit), state.sent_announce);
}
#endif
if (settings().get_bool(settings_pack::announce_to_all_tiers)
&& !settings().get_bool(settings_pack::announce_to_all_trackers)
&& sent_announce
&& ae.tier <= tier
&& tier != INT_MAX)
&& state.sent_announce
&& ae.tier <= state.tier
&& state.tier != INT_MAX)
continue;
// if trackerid is not specified for tracker use default one, probably set explicitly
req.trackerid = ae.trackerid.empty() ? m_trackerid : ae.trackerid;
if (ae.tier > tier && sent_announce
if (ae.tier > state.tier && state.sent_announce
&& !settings().get_bool(settings_pack::announce_to_all_tiers)) break;
if (ae.is_working()) { tier = ae.tier; sent_announce = false; }
if (!ae.can_announce(now, is_seed()))
if (aep.is_working()) { state.tier = ae.tier; state.sent_announce = false; }
if (!aep.can_announce(now, is_seed(), ae.fail_limit))
{
// this counts
if (ae.is_working()) sent_announce = true;
if (aep.is_working()) state.sent_announce = true;
continue;
}
req.url = ae.url;
req.event = e;
if (req.event == tracker_request::none)
{
if (!ae.start_sent) req.event = tracker_request::started;
else if (!ae.complete_sent && is_seed()) req.event = tracker_request::completed;
if (!aep.start_sent) req.event = tracker_request::started;
else if (!aep.complete_sent && is_seed()) req.event = tracker_request::completed;
}
req.triggered_manually = ae.triggered_manually;
ae.triggered_manually = false;
req.triggered_manually = aep.triggered_manually;
aep.triggered_manually = false;
if (settings().get_bool(settings_pack::force_proxy))
{
@ -2734,7 +2784,7 @@ namespace libtorrent {
if ((protocol == "http" || protocol == "https")
&& proxy_type == settings_pack::none)
{
ae.next_announce = now + minutes32(10);
aep.next_announce = now + minutes32(10);
if (m_ses.alerts().should_post<anonymous_mode_alert>()
|| req.triggered_manually)
{
@ -2752,7 +2802,7 @@ namespace libtorrent {
&& proxy_type != settings_pack::socks5_pw
&& proxy_type != settings_pack::i2p_proxy)
{
ae.next_announce = now + minutes32(10);
aep.next_announce = now + minutes32(10);
if (m_ses.alerts().should_post<anonymous_mode_alert>()
|| req.triggered_manually)
{
@ -2775,40 +2825,51 @@ namespace libtorrent {
}
#endif
req.outgoing_socket = aep.socket;
#ifndef TORRENT_DISABLE_LOGGING
debug_log("==> TRACKER REQUEST \"%s\" event: %s abort: %d"
, req.url.c_str()
, (req.event == tracker_request::stopped ? "stopped"
: req.event == tracker_request::started ? "started" : "")
, m_abort);
debug_log("==> TRACKER REQUEST \"%s\" event: %s abort: %d fails: %d upd: %d"
, req.url.c_str()
, (req.event == tracker_request::stopped ? "stopped"
: req.event == tracker_request::started ? "started" : "")
, m_abort
, aep.fails
, aep.updating);
// if we're not logging session logs, don't bother creating an
// observer object just for logging
if (m_abort && m_ses.should_log())
{
auto tl = std::make_shared<aux::tracker_logger>(m_ses);
m_ses.queue_tracker_request(req, tl);
}
else
// if we're not logging session logs, don't bother creating an
// observer object just for logging
if (m_abort && m_ses.should_log())
{
auto tl = std::make_shared<aux::tracker_logger>(m_ses);
m_ses.queue_tracker_request(req, tl);
}
else
#endif
{
m_ses.queue_tracker_request(req, shared_from_this());
{
m_ses.queue_tracker_request(req, shared_from_this());
}
aep.updating = true;
aep.next_announce = now + seconds32(20);
aep.min_announce = now + seconds32(10);
if (m_ses.alerts().should_post<tracker_announce_alert>())
{
m_ses.alerts().emplace_alert<tracker_announce_alert>(
get_handle(), req.url, req.event);
}
state.sent_announce = true;
if (aep.is_working()
&& !settings().get_bool(settings_pack::announce_to_all_trackers)
&& !settings().get_bool(settings_pack::announce_to_all_tiers))
{
state.done = true;
}
}
ae.updating = true;
ae.next_announce = now + seconds32(20);
ae.min_announce = now + seconds32(10);
if (m_ses.alerts().should_post<tracker_announce_alert>())
{
m_ses.alerts().emplace_alert<tracker_announce_alert>(
get_handle(), req.url, req.event);
}
sent_announce = true;
if (ae.is_working()
&& !settings().get_bool(settings_pack::announce_to_all_trackers)
&& !settings().get_bool(settings_pack::announce_to_all_tiers))
if (std::all_of(listen_socket_states.begin(), listen_socket_states.end()
, [](announce_state const& s) { return s.done; }))
break;
}
update_tracker_timer(now);
@ -2852,7 +2913,12 @@ namespace libtorrent {
announce_entry* ae = find_tracker(req.url);
if (ae)
{
ae->message = msg;
for (auto& aep : ae->endpoints)
{
if (aep.socket != req.outgoing_socket) continue;
aep.message = msg;
break;
}
}
if (m_ses.alerts().should_post<tracker_warning_alert>())
@ -2870,11 +2936,15 @@ namespace libtorrent {
announce_entry* ae = find_tracker(req.url);
if (ae)
{
if (incomplete >= 0) ae->scrape_incomplete = incomplete;
if (complete >= 0) ae->scrape_complete = complete;
if (downloaded >= 0) ae->scrape_downloaded = downloaded;
announce_endpoint* aep = ae->find_endpoint(req.outgoing_socket);
if (aep)
{
if (incomplete >= 0) aep->scrape_incomplete = incomplete;
if (complete >= 0) aep->scrape_complete = complete;
if (downloaded >= 0) aep->scrape_downloaded = downloaded;
update_scrape_state();
update_scrape_state();
}
}
// if this was triggered manually we need to post this unconditionally,
@ -2897,9 +2967,12 @@ namespace libtorrent {
int downloaded = -1;
for (auto const& t : m_trackers)
{
complete = (std::max)(t.scrape_complete, complete);
incomplete = (std::max)(t.scrape_incomplete, incomplete);
downloaded = (std::max)(t.scrape_downloaded, downloaded);
for (auto const& aep : t.endpoints)
{
complete = (std::max)(aep.scrape_complete, complete);
incomplete = (std::max)(aep.scrape_incomplete, incomplete);
downloaded = (std::max)(aep.scrape_downloaded, downloaded);
}
}
if ((complete >= 0 && int(m_complete) != complete)
@ -2937,7 +3010,8 @@ namespace libtorrent {
// out external IP counter (and pass along the IP of the tracker to know
// who to attribute this vote to)
if (resp.external_ip != address() && !is_any(tracker_ip))
m_ses.set_external_address(resp.external_ip
m_ses.set_external_address(r.outgoing_socket->get_local_endpoint()
, resp.external_ip
, aux::session_interface::source_tracker, tracker_ip);
time_point32 now = aux::time_now32();
@ -2948,30 +3022,34 @@ namespace libtorrent {
announce_entry* ae = find_tracker(r.url);
if (ae)
{
if (resp.incomplete >= 0) ae->scrape_incomplete = resp.incomplete;
if (resp.complete >= 0) ae->scrape_complete = resp.complete;
if (resp.downloaded >= 0) ae->scrape_downloaded = resp.downloaded;
if (!ae->start_sent && r.event == tracker_request::started)
ae->start_sent = true;
if (!ae->complete_sent && r.event == tracker_request::completed)
ae->complete_sent = true;
ae->verified = true;
ae->updating = false;
ae->fails = 0;
ae->next_announce = now + interval;
ae->min_announce = now + resp.min_interval;
int tracker_index = int(ae - &m_trackers[0]);
m_last_working_tracker = std::int8_t(prioritize_tracker(tracker_index));
if ((!resp.trackerid.empty()) && (ae->trackerid != resp.trackerid))
announce_endpoint* aep = ae->find_endpoint(r.outgoing_socket);
if (aep)
{
ae->trackerid = resp.trackerid;
if (m_ses.alerts().should_post<trackerid_alert>())
m_ses.alerts().emplace_alert<trackerid_alert>(get_handle()
, r.url, resp.trackerid);
}
if (resp.incomplete >= 0) aep->scrape_incomplete = resp.incomplete;
if (resp.complete >= 0) aep->scrape_complete = resp.complete;
if (resp.downloaded >= 0) aep->scrape_downloaded = resp.downloaded;
if (!aep->start_sent && r.event == tracker_request::started)
aep->start_sent = true;
if (!aep->complete_sent && r.event == tracker_request::completed)
aep->complete_sent = true;
ae->verified = true;
aep->next_announce = now + interval;
aep->min_announce = now + resp.min_interval;
aep->updating = false;
aep->fails = 0;
int tracker_index = int(ae - &m_trackers[0]);
m_last_working_tracker = std::int8_t(prioritize_tracker(tracker_index));
update_scrape_state();
if ((!resp.trackerid.empty()) && (ae->trackerid != resp.trackerid))
{
ae->trackerid = resp.trackerid;
if (m_ses.alerts().should_post<trackerid_alert>())
m_ses.alerts().emplace_alert<trackerid_alert>(get_handle()
, r.url, resp.trackerid);
}
update_scrape_state();
}
}
update_tracker_timer(now);
@ -3017,6 +3095,8 @@ namespace libtorrent {
}
#endif
}
#else
TORRENT_UNUSED(tracker_ips);
#endif
// for each of the peers we got from the tracker
@ -3095,54 +3175,6 @@ namespace libtorrent {
, r.url);
}
// we're listening on an interface type that was not used
// when talking to the tracker. If there is a matching interface
// type in the tracker IP list, make another tracker request
// using that interface
// in order to avoid triggering this case over and over, don't
// do it if the bind IP for the tracker request that just completed
// matches one of the listen interfaces, since that means this
// announce was the second one
// TODO: 3 instead of announcing once per IP version, announce once per
// listen interface (i.e. m_listen_sockets)
if (((!is_any(m_ses.get_ipv6_interface().address()) && tracker_ip.is_v4())
|| (!is_any(m_ses.get_ipv4_interface().address()) && tracker_ip.is_v6()))
&& r.bind_ip != m_ses.get_ipv4_interface().address()
&& r.bind_ip != m_ses.get_ipv6_interface().address())
{
auto i = std::find_if(tracker_ips.begin(), tracker_ips.end()
, [&] (address const& a) { return a.is_v4() != tracker_ip.is_v4(); });
if (i != tracker_ips.end())
{
// the tracker did resolve to a different type of address, so announce
// to that as well
// TODO 3: there's a bug when removing a torrent or shutting down the session,
// where the second announce is skipped (in this case, the one to the IPv6
// name). This should be fixed by generalizing the tracker list structure to
// separate the IPv6 and IPv4 addresses as conceptually separate trackers,
// and they should be announced to in parallel
tracker_request req = r;
req.private_torrent = m_torrent_file->priv();
// tell the tracker to bind to the opposite protocol type
req.bind_ip = tracker_ip.is_v4()
? m_ses.get_ipv6_interface().address()
: m_ses.get_ipv4_interface().address();
#ifndef TORRENT_DISABLE_LOGGING
if (should_log())
{
debug_log("announce again using %s as the bind interface"
, print_address(req.bind_ip).c_str());
}
#endif
m_ses.queue_tracker_request(req, shared_from_this());
}
}
do_connect_boost();
state_updated();
@ -3252,9 +3284,12 @@ namespace libtorrent {
{
for (auto& e : m_trackers)
{
e.next_announce = std::max(time_point_cast<seconds32>(t)
, e.min_announce) + seconds(1);
e.triggered_manually = true;
for (auto& aep : e.endpoints)
{
aep.next_announce = std::max(time_point_cast<seconds32>(t)
, aep.min_announce) + seconds(1);
aep.triggered_manually = true;
}
}
}
else
@ -3262,9 +3297,12 @@ namespace libtorrent {
if (tracker_idx < 0 || tracker_idx >= int(m_trackers.size()))
return;
announce_entry& e = m_trackers[tracker_idx];
e.next_announce = std::max(time_point_cast<seconds32>(t)
, e.min_announce) + seconds32(1);
e.triggered_manually = true;
for (auto& aep : e.endpoints)
{
aep.next_announce = std::max(time_point_cast<seconds32>(t)
, aep.min_announce) + seconds32(1);
aep.triggered_manually = true;
}
}
update_tracker_timer(aux::time_now32());
}
@ -5061,8 +5099,10 @@ namespace libtorrent {
m_last_working_tracker = -1;
for (auto& t : m_trackers)
{
t.endpoints.clear();
if (t.source == 0) t.source = announce_entry::source_client;
t.complete_sent = is_seed();
for (auto& aep : t.endpoints)
aep.complete_sent = is_seed();
}
if (settings().get_bool(settings_pack::prefer_udp_trackers))
@ -5115,6 +5155,7 @@ namespace libtorrent {
{ return lhs.tier < rhs.tier; });
if (k - m_trackers.begin() < m_last_working_tracker) ++m_last_working_tracker;
k = m_trackers.insert(k, url);
k->endpoints.clear();
if (k->source == 0) k->source = announce_entry::source_client;
if (!m_paused && !m_trackers.empty()) announce_with_tracker();
return true;
@ -7220,9 +7261,12 @@ namespace libtorrent {
time_point32 const now = aux::time_now32();
for (auto& t : m_trackers)
{
if (t.complete_sent) continue;
t.next_announce = now;
t.min_announce = now;
for (auto& aep : t.endpoints)
{
if (aep.complete_sent) continue;
aep.next_announce = now;
aep.min_announce = now;
}
}
announce_with_tracker();
}
@ -7327,7 +7371,8 @@ namespace libtorrent {
else
{
for (auto& t : m_trackers)
t.complete_sent = true;
for (auto& aep : t.endpoints)
aep.complete_sent = true;
if (m_state != torrent_status::finished
&& m_state != torrent_status::seeding)
@ -8555,6 +8600,21 @@ namespace libtorrent {
do_connect_boost();
}
namespace
{
struct timer_state
{
explicit timer_state(aux::session_listen_socket* s)
: socket(s) {}
aux::session_listen_socket* socket;
int tier = INT_MAX;
bool found_working = false;
bool done = false;
};
}
void torrent::update_tracker_timer(time_point32 const now)
{
TORRENT_ASSERT(is_single_thread());
@ -8567,12 +8627,24 @@ namespace libtorrent {
}
time_point32 next_announce = time_point32::max();
int tier = INT_MAX;
bool found_working = false;
std::vector<timer_state> listen_socket_states;
for (auto const& t : m_trackers)
{
for (auto const& aep : t.endpoints)
{
auto aep_state_iter = std::find_if(listen_socket_states.begin(), listen_socket_states.end()
, [&](timer_state const& s) { return s.socket == aep.socket; });
if (aep_state_iter == listen_socket_states.end())
{
listen_socket_states.emplace_back(aep.socket);
aep_state_iter = listen_socket_states.end() - 1;
}
timer_state& state = *aep_state_iter;
if (state.done) continue;
#ifndef TORRENT_DISABLE_LOGGING
if (should_log())
{
@ -8581,35 +8653,42 @@ namespace libtorrent {
" found: %d i->tier: %d tier: %d"
" working: %d fails: %d limit: %d upd: %d ]"
, t.url.c_str(), settings().get_bool(settings_pack::announce_to_all_tiers)
, settings().get_bool(settings_pack::announce_to_all_trackers), found_working
, t.tier, tier, t.is_working(), t.fails, t.fail_limit
, t.updating);
, settings().get_bool(settings_pack::announce_to_all_trackers), state.found_working
, t.tier, state.tier, aep.is_working(), aep.fails, t.fail_limit
, aep.updating);
}
#endif
if (settings().get_bool(settings_pack::announce_to_all_tiers)
&& found_working
&& t.tier <= tier
&& tier != INT_MAX)
&& state.found_working
&& t.tier <= state.tier
&& state.tier != INT_MAX)
continue;
if (t.tier > tier && !settings().get_bool(settings_pack::announce_to_all_tiers)) break;
if (t.is_working()) { tier = t.tier; found_working = false; }
if (t.fails >= t.fail_limit && t.fail_limit != 0) continue;
if (t.updating)
if (t.tier > state.tier && !settings().get_bool(settings_pack::announce_to_all_tiers)) break;
if (aep.is_working()) { state.tier = t.tier; state.found_working = false; }
if (aep.fails >= t.fail_limit && t.fail_limit != 0) continue;
if (aep.updating)
{
found_working = true;
state.found_working = true;
}
else
{
time_point32 next_tracker_announce = std::max(t.next_announce, t.min_announce);
time_point32 next_tracker_announce = std::max(aep.next_announce, aep.min_announce);
if (next_tracker_announce < next_announce
&& (!found_working || t.is_working()))
&& (!state.found_working || aep.is_working()))
next_announce = next_tracker_announce;
}
if (t.is_working()) found_working = true;
if (found_working
&& !settings().get_bool(settings_pack::announce_to_all_trackers)
&& !settings().get_bool(settings_pack::announce_to_all_tiers)) break;
if (aep.is_working()) state.found_working = true;
if (state.found_working
&& !settings().get_bool(settings_pack::announce_to_all_trackers)
&& !settings().get_bool(settings_pack::announce_to_all_tiers))
state.done = true;
}
if (std::all_of(listen_socket_states.begin(), listen_socket_states.end()
, [](timer_state const& s) { return s.done; }))
break;
}
if (next_announce <= now) next_announce = now;
@ -8711,8 +8790,11 @@ namespace libtorrent {
time_point32 const now = aux::time_now32();
for (auto& t : m_trackers)
{
t.next_announce = now;
t.min_announce = now;
for (auto& aep : t.endpoints)
{
aep.next_announce = now;
aep.min_announce = now;
}
}
announce_with_tracker(tracker_request::stopped);
}
@ -10496,7 +10578,8 @@ namespace {
{
for (auto const& t : m_trackers)
{
if (t.updating) continue;
if (std::any_of(t.endpoints.begin(), t.endpoints.end()
, [](announce_endpoint const& aep) { return aep.updating; })) continue;
if (!t.verified) continue;
st->current_tracker = t.url;
break;
@ -10667,16 +10750,24 @@ namespace {
{
// announce request
announce_entry* ae = find_tracker(r.url);
int fails = 0;
if (ae)
{
ae->failed(settings().get_int(settings_pack::tracker_backoff)
, retry_interval);
ae->last_error = ec;
ae->message = msg;
int const tracker_index = int(ae - m_trackers.data());
for (auto& aep : ae->endpoints)
{
if (aep.socket != r.outgoing_socket) continue;
aep.failed(settings().get_int(settings_pack::tracker_backoff)
, retry_interval);
aep.last_error = ec;
aep.message = msg;
#ifndef TORRENT_DISABLE_LOGGING
debug_log("*** increment tracker fail count [%d]", ae->fails);
debug_log("*** increment tracker fail count [%d]", aep.fails);
#endif
break;
}
int const tracker_index = int(ae - m_trackers.data());
// never talk to this tracker again
if (response_code == 410) ae->fail_limit = 1;
@ -10686,7 +10777,7 @@ namespace {
|| r.triggered_manually)
{
m_ses.alerts().emplace_alert<tracker_error_alert>(get_handle()
, ae ? ae->fails : 0, response_code, r.url, ec, msg);
, fails, response_code, r.url, ec, msg);
}
}
else

View File

@ -171,6 +171,11 @@ namespace libtorrent {
close();
}
address tracker_connection::bind_interface() const
{
return m_req.outgoing_socket->get_local_endpoint().address();
}
void tracker_connection::sent_bytes(int bytes)
{
m_man.sent_bytes(bytes);
@ -370,19 +375,21 @@ namespace libtorrent {
return p->on_receive_hostname(hostname, buf);
}
void tracker_manager::send_hostname(char const* hostname, int const port
void tracker_manager::send_hostname(aux::session_listen_socket* sock
, char const* hostname, int const port
, span<char const> p, error_code& ec, int const flags)
{
TORRENT_ASSERT(is_single_thread());
m_send_fun_hostname(hostname, port, p, ec, flags);
m_send_fun_hostname(sock, hostname, port, p, ec, flags);
}
void tracker_manager::send(udp::endpoint const& ep
void tracker_manager::send(aux::session_listen_socket* sock
, udp::endpoint const& ep
, span<char const> p
, error_code& ec, int const flags)
{
TORRENT_ASSERT(is_single_thread());
m_send_fun(ep, p, ec, flags);
m_send_fun(sock, ep, p, ec, flags);
}
void tracker_manager::abort_all_requests(bool all)

View File

@ -198,9 +198,25 @@ namespace libtorrent {
// look for an address that has the same kind as the one
// we're listening on. To make sure the tracker get our
// correct listening address.
bool is_v4 = bind_interface().is_v4();
#if TORRENT_USE_IPV6
auto scope = is_v4 ? 0 : bind_interface().to_v6().scope_id();
#endif
for (auto const& addr : addresses)
{
if (addr.is_v4() != is_v4) continue;
#if TORRENT_USE_IPV6
if (addr.is_v6() && addr.to_v6().scope_id() != scope)
continue;
#endif
m_endpoints.push_back(tcp::endpoint(addr, std::uint16_t(port)));
}
if (m_endpoints.empty())
{
fail(error_code(boost::asio::error::address_family_not_supported));
return;
}
if (tracker_req().filter)
{
@ -501,13 +517,13 @@ namespace libtorrent {
error_code ec;
if (!m_hostname.empty())
{
m_man.send_hostname(m_hostname.c_str()
m_man.send_hostname(bind_socket(), m_hostname.c_str()
, m_target.port(), buf, ec
, udp_socket::tracker_connection);
}
else
{
m_man.send(m_target, buf, ec
m_man.send(bind_socket(), m_target, buf, ec
, udp_socket::tracker_connection);
}
@ -565,12 +581,12 @@ namespace libtorrent {
error_code ec;
if (!m_hostname.empty())
{
m_man.send_hostname(m_hostname.c_str(), m_target.port()
m_man.send_hostname(bind_socket(), m_hostname.c_str(), m_target.port()
, buf, ec, udp_socket::tracker_connection);
}
else
{
m_man.send(m_target, buf, ec
m_man.send(bind_socket(), m_target, buf, ec
, udp_socket::tracker_connection);
}
m_state = action_t::scrape;
@ -781,13 +797,13 @@ namespace libtorrent {
if (!m_hostname.empty())
{
m_man.send_hostname(m_hostname.c_str()
m_man.send_hostname(bind_socket(), m_hostname.c_str()
, m_target.port(), {buf, std::size_t(sizeof(buf) - out.size())}, ec
, udp_socket::tracker_connection);
}
else
{
m_man.send(m_target, {buf, std::size_t(sizeof(buf) - out.size())}, ec
m_man.send(bind_socket(), m_target, {buf, std::size_t(sizeof(buf) - out.size())}, ec
, udp_socket::tracker_connection);
}
m_state = action_t::announce;

View File

@ -120,7 +120,7 @@ void run_test(std::string const& url, int size, int status, int connected
std::shared_ptr<http_connection> h = std::make_shared<http_connection>(ios
, res, &::http_handler, true, 1024*1024, &::http_connect_handler);
h->get(url, seconds(1), 0, &ps, 5, "test/user-agent", address_v4::any()
h->get(url, seconds(1), 0, &ps, 5, "test/user-agent", address(address_v4::any())
, 0, auth);
ios.reset();
error_code e;

View File

@ -203,3 +203,68 @@ TORRENT_TEST(partition_listen_sockets_op_ports)
TEST_EQUAL(eps.size(), 2);
}
TORRENT_TEST(expand_unspecified)
{
std::vector<ip_interface> ifs;
std::vector<aux::listen_endpoint_t> eps;
ip_interface ipi;
ipi.interface_address = address::from_string("127.0.0.1");
strcpy(ipi.name, "lo");
ifs.push_back(ipi);
ipi.interface_address = address::from_string("192.168.1.2");
strcpy(ipi.name, "eth0");
ifs.push_back(ipi);
ipi.interface_address = address::from_string("24.172.48.90");
strcpy(ipi.name, "eth1");
ifs.push_back(ipi);
ipi.interface_address = address::from_string("::1");
strcpy(ipi.name, "lo");
ifs.push_back(ipi);
ipi.interface_address = address::from_string("fe80::d250:99ff:fe0c:9b74");
strcpy(ipi.name, "eth0");
ifs.push_back(ipi);
ipi.interface_address = address::from_string("2601:646:c600:a3:d250:99ff:fe0c:9b74");
ifs.push_back(ipi);
aux::listen_endpoint_t v4_nossl(address::from_string("0.0.0.0"), 6881, std::string(), false);
aux::listen_endpoint_t v4_ssl(address::from_string("0.0.0.0"), 6882, std::string(), true);
aux::listen_endpoint_t v6_unsp_nossl(address::from_string("::"), 6883, std::string(), false);
aux::listen_endpoint_t v6_unsp_ssl(address::from_string("::"), 6884, std::string(), true);
aux::listen_endpoint_t v6_ll_nossl(address::from_string("fe80::d250:99ff:fe0c:9b74"), 6883, std::string(), false);
aux::listen_endpoint_t v6_ll_ssl(address::from_string("fe80::d250:99ff:fe0c:9b74"), 6884, std::string(), true);
aux::listen_endpoint_t v6_g_nossl(address::from_string("2601:646:c600:a3:d250:99ff:fe0c:9b74"), 6883, std::string(), false);
aux::listen_endpoint_t v6_g_ssl(address::from_string("2601:646:c600:a3:d250:99ff:fe0c:9b74"), 6884, std::string(), true);
eps.push_back(v4_nossl);
eps.push_back(v4_ssl);
eps.push_back(v6_unsp_nossl);
eps.push_back(v6_unsp_ssl);
aux::expand_unspecified_address(ifs, eps);
TEST_EQUAL(eps.size(), 6);
TEST_CHECK(std::count(eps.begin(), eps.end(), v4_nossl) == 1);
TEST_CHECK(std::count(eps.begin(), eps.end(), v4_ssl) == 1);
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_ll_nossl) == 1);
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_ll_ssl) == 1);
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_g_nossl) == 1);
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_g_ssl) == 1);
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_unsp_nossl) == 0);
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_unsp_ssl) == 0);
// test that a user configured endpoint is not duplicated
aux::listen_endpoint_t v6_g_nossl_dev(address::from_string("2601:646:c600:a3:d250:99ff:fe0c:9b74"), 6883, "eth0", false);
eps.clear();
eps.push_back(v6_unsp_nossl);
eps.push_back(v6_g_nossl_dev);
aux::expand_unspecified_address(ifs, eps);
TEST_EQUAL(eps.size(), 2);
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_ll_nossl) == 1);
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_g_nossl) == 0);
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_g_nossl_dev) == 1);
}

View File

@ -50,16 +50,13 @@ TORRENT_TEST(primitives)
// make sure the retry interval keeps growing
// on failing announces
announce_entry ae("dummy");
ae.endpoints.emplace_back(nullptr);
int last = 0;
auto const tracker_backoff = 250;
for (int i = 0; i < 10; ++i)
{
ae.failed(tracker_backoff, seconds32(5));
#ifndef TORRENT_NO_DEPRECATE
int const delay = ae.next_announce_in();
#else
int const delay = static_cast<int>(total_seconds(ae.next_announce - clock_type::now()));
#endif
ae.endpoints.front().failed(tracker_backoff, seconds32(5));
int const delay = static_cast<int>(total_seconds(ae.endpoints.front().next_announce - clock_type::now()));
TEST_CHECK(delay > last);
last = delay;
std::printf("%d, ", delay);

View File

@ -679,8 +679,9 @@ void test_stop_tracker_timeout(bool nostop)
std::snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", port);
announce_entry ae{tracker_url};
// trick to avoid use of tracker immediately
ae.next_announce = aux::time_now32() + seconds32(1);
ae.min_announce = aux::time_now32() + seconds32(1);
// FIXME
//ae.next_announce = aux::time_now32() + seconds32(1);
//ae.min_announce = aux::time_now32() + seconds32(1);
h.add_tracker(ae);
s.remove_torrent(h);