announce to trackers for all listen interfaces
This commit is contained in:
parent
23ba9d12d1
commit
fd50630020
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
;
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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]))
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
445
src/torrent.cpp
445
src/torrent.cpp
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue