diff --git a/ChangeLog b/ChangeLog index ee70cc0ab..c892193d0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -81,6 +81,7 @@ * added session::is_dht_running() function * added torrent_handle::force_dht_announce() * added torrent_info::remap_files() + * support min_interval tracker extension release 0.14.8 diff --git a/docs/manual.rst b/docs/manual.rst index 4203ddb39..5c4d99522 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1713,6 +1713,10 @@ ones with lower tier will always be tried before the one with higher tier number { announce_entry(std::string const& url); std::string url; + + int next_announce_in() const; + int min_announce_in() const; + boost::uint8_t tier; boost::uint8_t fail_limit; boost::uint8_t fails; @@ -1732,6 +1736,10 @@ ones with lower tier will always be tried before the one with higher tier number bool complete_sent:1; }; +``next_announce_in()`` 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. + ``fail_limit`` is the max number of failures to announce to this tracker in a row, before this tracker is not used anymore. diff --git a/include/libtorrent/http_tracker_connection.hpp b/include/libtorrent/http_tracker_connection.hpp index d970b509f..69ccec1c7 100644 --- a/include/libtorrent/http_tracker_connection.hpp +++ b/include/libtorrent/http_tracker_connection.hpp @@ -45,9 +45,10 @@ POSSIBILITY OF SUCH DAMAGE. #pragma warning(pop) #endif +#include "libtorrent/config.hpp" +#include "libtorrent/lazy_entry.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/tracker_manager.hpp" -#include "libtorrent/config.hpp" #include "libtorrent/i2p_stream.hpp" namespace libtorrent @@ -95,8 +96,8 @@ namespace libtorrent virtual void on_timeout() {} - void parse(int status_code, const entry& e); - bool extract_peer_info(const entry& e, peer_entry& ret); + void parse(int status_code, lazy_entry const& e); + bool extract_peer_info(lazy_entry const& e, peer_entry& ret); tracker_manager& m_man; boost::shared_ptr m_tracker_connection; diff --git a/include/libtorrent/lazy_entry.hpp b/include/libtorrent/lazy_entry.hpp index c693e762c..36313e274 100644 --- a/include/libtorrent/lazy_entry.hpp +++ b/include/libtorrent/lazy_entry.hpp @@ -153,6 +153,7 @@ namespace libtorrent lazy_entry const* dict_find_dict(char const* name) const; lazy_entry const* dict_find_list(char const* name) const; lazy_entry const* dict_find_string(char const* name) const; + lazy_entry const* dict_find_int(char const* name) const; std::pair dict_at(int i) const; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 0623fcefe..a1513ee12 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -391,7 +391,7 @@ namespace libtorrent tracker_request const& r , address const& tracker_ip , std::list
const& ip_list - , std::vector& e, int interval + , std::vector& e, int interval, int min_interval , int complete, int incomplete, address const& external_ip); virtual void tracker_request_timed_out( tracker_request const& r); diff --git a/include/libtorrent/torrent_info.hpp b/include/libtorrent/torrent_info.hpp index d213d72f4..004875dd8 100644 --- a/include/libtorrent/torrent_info.hpp +++ b/include/libtorrent/torrent_info.hpp @@ -89,9 +89,15 @@ namespace libtorrent std::string url; + int next_announce_in() const; + int min_announce_in() const; + // the time of next tracker announce ptime next_announce; + // no announces before this time + ptime min_announce; + boost::uint8_t tier; // the number of times this tracker can fail // in a row before it's removed. 0 means unlimited @@ -131,6 +137,7 @@ namespace libtorrent { start_sent = false; next_announce = min_time(); + min_announce = min_time(); } void failed(); @@ -138,6 +145,7 @@ namespace libtorrent bool can_announce(ptime now) const { return now >= next_announce + && now >= min_announce && (fails < fail_limit || fail_limit == 0) && !updating; } diff --git a/include/libtorrent/tracker_manager.hpp b/include/libtorrent/tracker_manager.hpp index 6ed8102e7..a7bb42c81 100644 --- a/include/libtorrent/tracker_manager.hpp +++ b/include/libtorrent/tracker_manager.hpp @@ -129,6 +129,7 @@ namespace libtorrent , std::list
const& ip_list , std::vector& peers , int interval + , int min_interval , int complete , int incomplete , address const& external_ip) = 0; diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp index 62e247102..3ead8b045 100644 --- a/src/http_tracker_connection.cpp +++ b/src/http_tracker_connection.cpp @@ -290,16 +290,16 @@ namespace libtorrent received_bytes(size + parser.body_start()); // handle tracker response - entry e; - e = bdecode(data, data + size); + lazy_entry e; + int res = lazy_bdecode(data, data + size, e); - if (e.type() == entry::dictionary_t) + if (res == 0 && e.type() == lazy_entry::dict_t) { parse(parser.status_code(), e); } else { - std::string error_str("invalid bencoding of tracker response: \""); + std::string error_str("invalid encoding of tracker response: \""); for (char const* i = data, *end(data + size); i != end; ++i) { if (*i >= ' ' && *i <= '~') error_str += *i; @@ -316,23 +316,18 @@ namespace libtorrent close(); } - bool http_tracker_connection::extract_peer_info(const entry& info, peer_entry& ret) + bool http_tracker_connection::extract_peer_info(lazy_entry const& info, peer_entry& ret) { // extract peer id (if any) - if (info.type() != entry::dictionary_t) + if (info.type() != lazy_entry::dict_t) { fail(-1, "invalid response from tracker (invalid peer entry)"); return false; } - entry const* i = info.find_key("peer id"); - if (i != 0) + lazy_entry const* i = info.dict_find_string("peer id"); + if (i != 0 && i->string_length() == 20) { - if (i->type() != entry::string_t || i->string().length() != 20) - { - fail(-1, "invalid response from tracker (invalid peer id)"); - return false; - } - std::copy(i->string().begin(), i->string().end(), ret.pid.begin()); + std::copy(i->string_ptr(), i->string_ptr()+20, ret.pid.begin()); } else { @@ -341,44 +336,42 @@ namespace libtorrent } // extract ip - i = info.find_key("ip"); - if (i == 0 || i->type() != entry::string_t) + i = info.dict_find_string("ip"); + if (i == 0) { fail(-1, "invalid response from tracker"); return false; } - ret.ip = i->string(); + ret.ip = i->string_value(); // extract port - i = info.find_key("port"); - if (i == 0 || i->type() != entry::int_t) + i = info.dict_find_int("port"); + if (i == 0) { fail(-1, "invalid response from tracker"); return false; } - ret.port = (unsigned short)i->integer(); + ret.port = (unsigned short)i->int_value(); return true; } - void http_tracker_connection::parse(int status_code, entry const& e) + void http_tracker_connection::parse(int status_code, lazy_entry const& e) { boost::shared_ptr cb = requester(); if (!cb) return; // parse the response - entry const* failure = e.find_key("failure reason"); - if (failure && failure->type() == entry::string_t) + lazy_entry const* failure = e.dict_find_string("failure reason"); + if (failure) { - fail(status_code, failure->string().c_str()); + fail(status_code, failure->string_value().c_str()); return; } - entry const* warning = e.find_key("warning message"); - if (warning && warning->type() == entry::string_t) - { - cb->tracker_warning(tracker_req(), warning->string()); - } + lazy_entry const* warning = e.dict_find_string("warning message"); + if (warning) + cb->tracker_warning(tracker_req(), warning->string_value()); std::vector peer_list; @@ -386,67 +379,55 @@ namespace libtorrent { std::string ih = tracker_req().info_hash.to_string(); - entry const* files = e.find_key("files"); - if (files == 0 || files->type() != entry::dictionary_t) + lazy_entry const* files = e.dict_find_dict("files"); + if (files == 0) { fail(-1, "invalid or missing 'files' entry in scrape response"); return; } - entry const* scrape_data = files->find_key(ih); - if (scrape_data == 0 || scrape_data->type() != entry::dictionary_t) + lazy_entry const* scrape_data = files->dict_find_dict(ih.c_str()); + if (scrape_data == 0) { fail(-1, "missing or invalid info-hash entry in scrape response"); return; } - entry const* complete = scrape_data->find_key("complete"); - entry const* incomplete = scrape_data->find_key("incomplete"); - entry const* downloaded = scrape_data->find_key("downloaded"); - if (complete == 0 || incomplete == 0 || downloaded == 0 - || complete->type() != entry::int_t - || incomplete->type() != entry::int_t - || downloaded->type() != entry::int_t) - { - fail(-1, "missing 'complete' or 'incomplete' entries in scrape response"); - return; - } - cb->tracker_scrape_response(tracker_req(), int(complete->integer()) - , int(incomplete->integer()), int(downloaded->integer())); + int complete = scrape_data->dict_find_int_value("complete", -1); + int incomplete = scrape_data->dict_find_int_value("incomplete", -1); + int downloaded = scrape_data->dict_find_int_value("downloaded", -1); + cb->tracker_scrape_response(tracker_req(), complete + , incomplete, downloaded); return; } - entry const* interval = e.find_key("interval"); - if (interval == 0 || interval->type() != entry::int_t) - { - fail(-1, "missing or invalid 'interval' entry in tracker response"); - return; - } + int interval = e.dict_find_int_value("interval", 1800); + int min_interval = e.dict_find_int_value("min interval", 60); - entry const* peers_ent = e.find_key("peers"); - if (peers_ent && peers_ent->type() == entry::string_t) + lazy_entry const* peers_ent = e.dict_find("peers"); + if (peers_ent && peers_ent->type() == lazy_entry::string_t) { - std::string const& peers = peers_ent->string(); - for (std::string::const_iterator i = peers.begin(); - i != peers.end();) + char const* peers = peers_ent->string_ptr(); + int len = peers_ent->string_length(); + for (int i = 0; i < len; ++i) { - if (peers.end() - i < 6) break; + if (len - i < 6) break; peer_entry p; p.pid.clear(); error_code ec; - p.ip = detail::read_v4_address(i).to_string(ec); + p.ip = detail::read_v4_address(peers).to_string(ec); + p.port = detail::read_uint16(peers); if (ec) continue; - p.port = detail::read_uint16(i); peer_list.push_back(p); } } - else if (peers_ent && peers_ent->type() == entry::list_t) + else if (peers_ent && peers_ent->type() == lazy_entry::list_t) { - entry::list_type const& l = peers_ent->list(); - for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i) + int len = peers_ent->list_size(); + for (int i = 0; i < len; ++i) { peer_entry p; - if (!extract_peer_info(*i, p)) return; + if (!extract_peer_info(*peers_ent->list_at(i), p)) return; peer_list.push_back(p); } } @@ -456,21 +437,21 @@ namespace libtorrent } #if TORRENT_USE_IPV6 - entry const* ipv6_peers = e.find_key("peers6"); - if (ipv6_peers && ipv6_peers->type() == entry::string_t) + lazy_entry const* ipv6_peers = e.dict_find_string("peers6"); + if (ipv6_peers) { - std::string const& peers = ipv6_peers->string(); - for (std::string::const_iterator i = peers.begin(); - i != peers.end();) + char const* peers = ipv6_peers->string_ptr(); + int len = ipv6_peers->string_length(); + for (int i = 0; i < len; ++i) { - if (peers.end() - i < 18) break; + if (len - i < 18) break; peer_entry p; p.pid.clear(); error_code ec; - p.ip = detail::read_v6_address(i).to_string(ec); + p.ip = detail::read_v6_address(peers).to_string(ec); + p.port = detail::read_uint16(peers); if (ec) continue; - p.port = detail::read_uint16(i); peer_list.push_back(p); } } @@ -479,7 +460,7 @@ namespace libtorrent ipv6_peers = 0; } #else - entry const* ipv6_peers = 0; + lazy_entry const* ipv6_peers = 0; #endif if (peers_ent == 0 && ipv6_peers == 0) @@ -490,30 +471,22 @@ namespace libtorrent // look for optional scrape info - int complete = -1; - int incomplete = -1; address external_ip; - entry const* ip_ent = e.find_key("external ip"); - if (ip_ent && ip_ent->type() == entry::string_t) + lazy_entry const* ip_ent = e.dict_find_string("external ip"); + if (ip_ent) { - std::string const& ip = ip_ent->string(); - char const* p = &ip[0]; - if (ip.size() == address_v4::bytes_type::static_size) + char const* p = ip_ent->string_ptr(); + if (ip_ent->string_length() == address_v4::bytes_type::static_size) external_ip = detail::read_v4_address(p); #if TORRENT_USE_IPV6 - else if (ip.size() == address_v6::bytes_type::static_size) + else if (ip_ent->string_length() == address_v6::bytes_type::static_size) external_ip = detail::read_v6_address(p); #endif } - entry const* complete_ent = e.find_key("complete"); - if (complete_ent && complete_ent->type() == entry::int_t) - complete = int(complete_ent->integer()); - - entry const* incomplete_ent = e.find_key("incomplete"); - if (incomplete_ent && incomplete_ent->type() == entry::int_t) - incomplete = int(incomplete_ent->integer()); + int complete = e.dict_find_int_value("complete", -1); + int incomplete = e.dict_find_int_value("incomplete", -1); std::list
ip_list; if (m_tracker_connection) @@ -527,7 +500,7 @@ namespace libtorrent } cb->tracker_response(tracker_req(), m_tracker_ip, ip_list, peer_list - , interval->integer(), complete, incomplete, external_ip); + , interval, min_interval, complete, incomplete, external_ip); } } diff --git a/src/lazy_bdecode.cpp b/src/lazy_bdecode.cpp index c6585ab54..327a3e3e2 100644 --- a/src/lazy_bdecode.cpp +++ b/src/lazy_bdecode.cpp @@ -286,6 +286,13 @@ namespace libtorrent return e; } + lazy_entry const* lazy_entry::dict_find_int(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::int_t) return 0; + return e; + } + size_type lazy_entry::dict_find_int_value(char const* name, size_type default_val) const { lazy_entry const* e = dict_find(name); diff --git a/src/torrent.cpp b/src/torrent.cpp index 1dd8bfb4e..fd3d2f4ef 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1342,6 +1342,8 @@ namespace libtorrent void torrent::scrape_tracker() { + m_last_scrape = time_now(); + if (m_trackers.empty()) return; int i = m_last_working_tracker; @@ -1354,8 +1356,6 @@ namespace libtorrent req.bind_ip = m_ses.m_listen_interface.address(); m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req , tracker_login(), shared_from_this()); - - m_last_scrape = time_now(); } void torrent::tracker_warning(tracker_request const& req, std::string const& msg) @@ -1392,6 +1392,7 @@ namespace libtorrent , std::list
const& tracker_ips // these are all the IPs it resolved to , std::vector& peer_list , int interval + , int min_interval , int complete , int incomplete , address const& external_ip) @@ -1420,6 +1421,7 @@ namespace libtorrent ae->updating = false; ae->fails = 0; ae->next_announce = now + seconds(interval); + ae->min_announce = now + seconds(min_interval); int tracker_index = ae - &m_trackers[0]; m_last_working_tracker = prioritize_tracker(tracker_index); } @@ -4333,6 +4335,7 @@ namespace libtorrent { if (i->complete_sent) continue; i->next_announce = now; + i->min_announce = now; } announce_with_tracker(); } @@ -5195,7 +5198,10 @@ namespace libtorrent ptime now = time_now(); for (std::vector::iterator i = m_trackers.begin() , end(m_trackers.end()); i != end; ++i) + { i->next_announce = now; + i->min_announce = now; + } announce_with_tracker(tracker_request::stopped); } diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index e02c09dc7..a62217230 100644 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -400,6 +400,12 @@ namespace libtorrent return 0; } + int announce_entry::next_announce_in() const + { return total_seconds(time_now() - next_announce); } + + int announce_entry::min_announce_in() const + { return total_seconds(time_now() - min_announce); } + void announce_entry::failed() { ++fails; diff --git a/src/udp_tracker_connection.cpp b/src/udp_tracker_connection.cpp index f6f04ff02..17ed09f35 100644 --- a/src/udp_tracker_connection.cpp +++ b/src/udp_tracker_connection.cpp @@ -395,6 +395,7 @@ namespace libtorrent buf += 8; // skip header restart_read_timeout(); int interval = detail::read_int32(buf); + int min_interval = 60; int incomplete = detail::read_int32(buf); int complete = detail::read_int32(buf); int num_peers = (size - 20) / 6; @@ -446,7 +447,7 @@ namespace libtorrent } cb->tracker_response(tracker_req(), m_target.address(), ip_list - , peer_list, interval, complete, incomplete, address()); + , peer_list, interval, min_interval, complete, incomplete, address()); m_man.remove_request(this); close();