diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp
index 6076eab3b..897b87fee 100644
--- a/include/libtorrent/aux_/session_impl.hpp
+++ b/include/libtorrent/aux_/session_impl.hpp
@@ -1262,14 +1262,7 @@ namespace libtorrent
void tracker_response(tracker_request const&
, libtorrent::address const& tracker_ip
, std::list
const& ip_list
- , std::vector& peers
- , int interval
- , int min_interval
- , int complete
- , int incomplete
- , int downloaded
- , address const& external_ip
- , std::string const& tracker_id);
+ , struct tracker_response const& resp);
void tracker_request_timed_out(
tracker_request const&);
void tracker_request_error(tracker_request const& r
diff --git a/include/libtorrent/peer.hpp b/include/libtorrent/peer.hpp
index 33baf52d3..9517eb9e1 100644
--- a/include/libtorrent/peer.hpp
+++ b/include/libtorrent/peer.hpp
@@ -42,21 +42,31 @@ namespace libtorrent
struct TORRENT_EXTRA_EXPORT peer_entry
{
- std::string ip;
- int port;
+ std::string hostname;
peer_id pid;
+ boost::uint16_t port;
bool operator==(const peer_entry& p) const
- {
- return pid == p.pid;
- }
+ { return pid == p.pid; }
bool operator<(const peer_entry& p) const
- {
- return pid < p.pid;
- }
+ { return pid < p.pid; }
};
+ struct ipv4_peer_entry
+ {
+ address_v4::bytes_type ip;
+ boost::uint16_t port;
+ };
+
+#if TORRENT_USE_IPV6
+ struct ipv6_peer_entry
+ {
+ address_v6::bytes_type ip;
+ boost::uint16_t port;
+ };
+#endif
+
}
#endif // TORRENT_PEER_HPP_INCLUDED
diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp
index 07bb67f2f..c54bb02e5 100644
--- a/include/libtorrent/torrent.hpp
+++ b/include/libtorrent/torrent.hpp
@@ -661,9 +661,7 @@ namespace libtorrent
tracker_request const& r
, address const& tracker_ip
, std::list const& ip_list
- , std::vector& e, int interval, int min_interval
- , int complete, int incomplete, int downloaded
- , address const& external_ip, std::string const& trackerid);
+ , struct tracker_response const& resp);
virtual void tracker_request_error(tracker_request const& r
, int response_code, error_code const& ec, const std::string& msg
, int retry_interval);
diff --git a/include/libtorrent/tracker_manager.hpp b/include/libtorrent/tracker_manager.hpp
index ceb7f95b8..7ce29106a 100644
--- a/include/libtorrent/tracker_manager.hpp
+++ b/include/libtorrent/tracker_manager.hpp
@@ -143,6 +143,30 @@ namespace libtorrent
#endif
};
+ struct tracker_response
+ {
+ tracker_response()
+ : interval(1800)
+ , min_interval(120)
+ , complete(-1)
+ , incomplete(-1)
+ , downloaded(-1)
+ {}
+
+ std::vector peers;
+ std::vector peers4;
+#if TORRENT_USE_IPV6
+ std::vector peers6;
+#endif
+ address external_ip;
+ std::string trackerid;
+ int interval;
+ int min_interval;
+ int complete;
+ int incomplete;
+ int downloaded;
+ };
+
struct TORRENT_EXTRA_EXPORT request_callback
{
friend class tracker_manager;
@@ -157,14 +181,7 @@ namespace libtorrent
tracker_request const& req
, address const& tracker_ip
, std::list const& ip_list
- , std::vector& peers
- , int interval
- , int min_interval
- , int complete
- , int incomplete
- , int downloaded
- , address const& external_ip
- , std::string const& trackerid) = 0;
+ , struct tracker_response const& response) = 0;
virtual void tracker_request_error(
tracker_request const& req
, int response_code
diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp
index 784bb35c8..9cb7a77f0 100644
--- a/src/http_tracker_connection.cpp
+++ b/src/http_tracker_connection.cpp
@@ -369,7 +369,7 @@ namespace libtorrent
fail(error_code(errors::invalid_tracker_response));
return false;
}
- ret.ip = i->string_value();
+ ret.hostname = i->string_value();
// extract port
i = info.dict_find_int("port");
@@ -383,21 +383,25 @@ namespace libtorrent
return true;
}
+ // TODO: 2 make this a free function that can be easily unit tested
void http_tracker_connection::parse(int status_code, lazy_entry const& e)
{
boost::shared_ptr cb = requester();
if (!cb) return;
int interval = int(e.dict_find_int_value("interval", 0));
- int min_interval = int(e.dict_find_int_value("min interval", 30));
-
// if no interval is specified, default to 30 minutes
if (interval == 0) interval = 1800;
-
- std::string trackerid;
+ int min_interval = int(e.dict_find_int_value("min interval", 30));
+
+ tracker_response resp;
+ resp.interval = interval;
+ resp.min_interval = min_interval;
+
lazy_entry const* tracker_id = e.dict_find_string("tracker id");
if (tracker_id)
- trackerid = tracker_id->string_value();
+ resp.trackerid = tracker_id->string_value();
+
// parse the response
lazy_entry const* failure = e.dict_find_string("failure reason");
if (failure)
@@ -411,8 +415,6 @@ namespace libtorrent
if (warning)
cb->tracker_warning(tracker_req(), warning->string_value());
- std::vector peer_list;
-
if (tracker_req().kind == tracker_request::scrape_request)
{
std::string ih = tracker_req().info_hash.to_string();
@@ -447,27 +449,27 @@ namespace libtorrent
{
char const* peers = peers_ent->string_ptr();
int len = peers_ent->string_length();
+ resp.peers4.reserve(len / 6);
for (int i = 0; i < len; i += 6)
{
if (len - i < 6) break;
- peer_entry p;
- p.pid.clear();
+ ipv4_peer_entry p;
error_code ec;
- p.ip = detail::read_v4_address(peers).to_string(ec);
+ p.ip = detail::read_v4_address(peers).to_v4().to_bytes();
p.port = detail::read_uint16(peers);
- if (ec) continue;
- peer_list.push_back(p);
+ resp.peers4.push_back(p);
}
}
else if (peers_ent && peers_ent->type() == lazy_entry::list_t)
{
int len = peers_ent->list_size();
+ resp.peers.reserve(len);
for (int i = 0; i < len; ++i)
{
peer_entry p;
if (!extract_peer_info(*peers_ent->list_at(i), p)) return;
- peer_list.push_back(p);
+ resp.peers.push_back(p);
}
}
else
@@ -481,17 +483,15 @@ namespace libtorrent
{
char const* peers = ipv6_peers->string_ptr();
int len = ipv6_peers->string_length();
+ resp.peers6.reserve(len / 18);
for (int i = 0; i < len; i += 18)
{
if (len - i < 18) break;
- peer_entry p;
- p.pid.clear();
- error_code ec;
- p.ip = detail::read_v6_address(peers).to_string(ec);
+ ipv6_peer_entry p;
+ p.ip = detail::read_v6_address(peers).to_v6().to_bytes();
p.port = detail::read_uint16(peers);
- if (ec) continue;
- peer_list.push_back(p);
+ resp.peers6.push_back(p);
}
}
else
@@ -511,25 +511,22 @@ namespace libtorrent
return;
}
-
- // look for optional scrape info
- address external_ip;
-
lazy_entry const* ip_ent = e.dict_find_string("external ip");
if (ip_ent)
{
char const* p = ip_ent->string_ptr();
if (ip_ent->string_length() == int(address_v4::bytes_type().size()))
- external_ip = detail::read_v4_address(p);
+ resp.external_ip = detail::read_v4_address(p);
#if TORRENT_USE_IPV6
else if (ip_ent->string_length() == int(address_v6::bytes_type().size()))
- external_ip = detail::read_v6_address(p);
+ resp.external_ip = detail::read_v6_address(p);
#endif
}
- int complete = int(e.dict_find_int_value("complete", -1));
- int incomplete = int(e.dict_find_int_value("incomplete", -1));
- int downloaded = int(e.dict_find_int_value("downloaded", -1));
+ // look for optional scrape info
+ resp.complete = int(e.dict_find_int_value("complete", -1));
+ resp.incomplete = int(e.dict_find_int_value("incomplete", -1));
+ resp.downloaded = int(e.dict_find_int_value("downloaded", -1));
std::list ip_list;
if (m_tracker_connection)
@@ -544,8 +541,7 @@ namespace libtorrent
}
}
- cb->tracker_response(tracker_req(), m_tracker_ip, ip_list, peer_list
- , interval, min_interval, complete, incomplete, downloaded, external_ip, trackerid);
+ cb->tracker_response(tracker_req(), m_tracker_ip, ip_list, resp);
}
}
diff --git a/src/session_impl.cpp b/src/session_impl.cpp
index 92da3ff52..8b58463bf 100644
--- a/src/session_impl.cpp
+++ b/src/session_impl.cpp
@@ -7778,33 +7778,38 @@ retry:
void tracker_logger::tracker_response(tracker_request const&
, libtorrent::address const& tracker_ip
, std::list const& ip_list
- , std::vector& peers
- , int interval
- , int min_interval
- , int complete
- , int incomplete
- , int downloaded
- , address const& external_ip
- , std::string const& tracker_id)
+ , struct tracker_response const& resp)
{
- std::string s;
- s = "TRACKER RESPONSE:\n";
- char tmp[200];
- snprintf(tmp, 200, "interval: %d\nmin_interval: %d\npeers:\n", interval, min_interval);
- s += tmp;
- for (std::vector::const_iterator i = peers.begin();
- i != peers.end(); ++i)
- {
- char pid[41];
- to_hex((const char*)&i->pid[0], 20, pid);
- if (i->pid.is_all_zeros()) pid[0] = 0;
+#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
+ debug_log("TRACKER RESPONSE\n"
+ "interval: %d\n"
+ "external ip: %s\n"
+ "we connected to: %s\n"
+ "peers:"
+ , interval
+ , print_address(resp.external_ip).c_str()
+ , print_address(resp.tracker_ip).c_str());
- snprintf(tmp, 200, " %-16s %-5d %s\n", i->ip.c_str(), i->port, pid);
- s += tmp;
+ for (std::vector::const_iterator i = resp.peers.begin();
+ i != resp.peers.end(); ++i)
+ {
+ debug_log(" %16s %5d %s %s", i->hostname.c_str(), i->port
+ , i->pid.is_all_zeros()?"":to_hex(i->pid.to_string()).c_str()
+ , identify_client(i->pid).c_str());
}
- snprintf(tmp, 200, "external ip: %s\n", print_address(external_ip).c_str());
- s += tmp;
- debug_log("%s", s.c_str());
+ for (std::vector::const_iterator i = resp.peers4.begin();
+ i != resp.peers4.end(); ++i)
+ {
+ debug_log(" %16s %5d", print_address(i->ip).c_str(), i->port);
+ }
+#if TORRENT_USE_IPV6
+ for (std::vector::const_iterator i = resp.peers6.begin();
+ i != resp.peers6.end(); ++i)
+ {
+ debug_log(" %16s %5d", print_address(i->ip).c_str(), i->port);
+ }
+#endif
+#endif
}
void tracker_logger::tracker_request_timed_out(
diff --git a/src/torrent.cpp b/src/torrent.cpp
index 29ca60604..98a669d91 100644
--- a/src/torrent.cpp
+++ b/src/torrent.cpp
@@ -3117,42 +3117,34 @@ namespace libtorrent
m_need_save_resume_data = true;
}
- // TODO: 3 change the tracker_response interface to take a type capturing
- // the response. Have multiple peer lists. IPv4, IPv6 and hostnames. That
- // way we don't need to render addresses into strings
void torrent::tracker_response(
tracker_request const& r
, address const& tracker_ip // this is the IP we connected to
, 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
- , int downloaded
- , address const& external_ip
- , const std::string& trackerid)
+ , struct tracker_response const& resp)
{
TORRENT_ASSERT(is_single_thread());
INVARIANT_CHECK;
TORRENT_ASSERT(r.kind == tracker_request::announce_request);
- if (external_ip != address() && !tracker_ips.empty())
- m_ses.set_external_address(external_ip, aux::session_interface::source_tracker
+ if (resp.external_ip != address() && !tracker_ips.empty())
+ m_ses.set_external_address(resp.external_ip
+ , aux::session_interface::source_tracker
, *tracker_ips.begin());
ptime now = time_now();
+ int interval = resp.interval;
if (interval < settings().get_int(settings_pack::min_announce_interval))
interval = settings().get_int(settings_pack::min_announce_interval);
announce_entry* ae = find_tracker(r);
if (ae)
{
- if (incomplete >= 0) ae->scrape_incomplete = incomplete;
- if (complete >= 0) ae->scrape_complete = complete;
- if (downloaded >= 0) ae->scrape_downloaded = downloaded;
+ 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)
@@ -3161,22 +3153,23 @@ namespace libtorrent
ae->updating = false;
ae->fails = 0;
ae->next_announce = now + seconds(interval);
- ae->min_announce = now + seconds(min_interval);
+ ae->min_announce = now + seconds(resp.min_interval);
int tracker_index = ae - &m_trackers[0];
m_last_working_tracker = prioritize_tracker(tracker_index);
- if ((!trackerid.empty()) && (ae->trackerid != trackerid))
+ if ((!resp.trackerid.empty()) && (ae->trackerid != resp.trackerid))
{
- ae->trackerid = trackerid;
+ ae->trackerid = resp.trackerid;
if (m_ses.alerts().should_post())
- m_ses.alerts().post_alert(trackerid_alert(get_handle(), r.url, trackerid));
+ m_ses.alerts().post_alert(trackerid_alert(get_handle()
+ , r.url, resp.trackerid));
}
update_scrape_state();
}
update_tracker_timer(now);
- if (complete >= 0 && incomplete >= 0)
+ if (resp.complete >= 0 && resp.incomplete >= 0)
m_last_scrape = m_ses.session_time();
#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING
@@ -3186,89 +3179,109 @@ namespace libtorrent
"we connected to: %s\n"
"peers:"
, interval
- , print_address(external_ip).c_str()
- , print_address(tracker_ip).c_str());
+ , print_address(resp.external_ip).c_str()
+ , print_address(resp.tracker_ip).c_str());
- for (std::vector::const_iterator i = peer_list.begin();
- i != peer_list.end(); ++i)
+ for (std::vector::const_iterator i = resp.peers.begin();
+ i != resp.peers.end(); ++i)
{
- debug_log(" %16s %5d %s %s", i->ip.c_str(), i->port
+ debug_log(" %16s %5d %s %s", i->hostname.c_str(), i->port
, i->pid.is_all_zeros()?"":to_hex(i->pid.to_string()).c_str()
, identify_client(i->pid).c_str());
}
+ for (std::vector::const_iterator i = resp.peers4.begin();
+ i != resp.peers4.end(); ++i)
+ {
+ debug_log(" %16s %5d", print_address(i->ip).c_str(), i->port);
+ }
+#if TORRENT_USE_IPV6
+ for (std::vector::const_iterator i = resp.peers6.begin();
+ i != resp.peers6.end(); ++i)
+ {
+ debug_log(" %16s %5d", print_address(i->ip).c_str(), i->port);
+ }
#endif
+#endif
+
// for each of the peers we got from the tracker
- for (std::vector::iterator i = peer_list.begin();
- i != peer_list.end(); ++i)
+ for (std::vector::const_iterator i = resp.peers.begin();
+ i != resp.peers.end(); ++i)
{
// don't make connections to ourself
if (i->pid == m_ses.get_peer_id())
continue;
- error_code ec;
- tcp::endpoint a(address::from_string(i->ip, ec), i->port);
-
- if (ec)
- {
- // assume this is because we got a hostname instead of
- // an ip address from the tracker
-
#if TORRENT_USE_I2P
- char const* top_domain = strrchr(i->ip.c_str(), '.');
- if (top_domain && strcmp(top_domain, ".i2p") == 0)
- {
- // this is an i2p name, we need to use the sam connection
- // to do the name lookup
- /*
- m_ses.m_i2p_conn.async_name_lookup(i->ip.c_str()
- , boost::bind(&torrent::on_i2p_resolve
- , shared_from_this(), _1));
- */
- // it seems like you're not supposed to do a name lookup
- // on the peers returned from the tracker, but just strip
- // the .i2p and use it as a destination
- i->ip.resize(i->ip.size() - 4);
- torrent_state st = get_policy_state();
- need_policy();
- if (m_policy->add_i2p_peer(i->ip.c_str(), peer_info::tracker, 0, &st))
- state_updated();
- peers_erased(st.erased);
- }
- else
-#endif
- {
-#if defined TORRENT_ASIO_DEBUGGING
- add_outstanding_async("torrent::on_peer_name_lookup");
-#endif
- tcp::resolver::query q(i->ip, to_string(i->port).elems);
- // TODO: instead, borrow host resolvers from a pool in session_impl. That
- // would make the torrent object smaller
- m_ses.get_resolver().async_resolve(i->ip, 0
- , boost::bind(&torrent::on_peer_name_lookup
- , shared_from_this(), _1, _2, i->port));
- }
+ char const* top_domain = strrchr(i->hostname.c_str(), '.');
+ if (top_domain && strcmp(top_domain, ".i2p") == 0)
+ {
+ // this is an i2p name, we need to use the sam connection
+ // to do the name lookup
+ /*
+ m_ses.m_i2p_conn.async_name_lookup(i->ip.c_str()
+ , boost::bind(&torrent::on_i2p_resolve
+ , shared_from_this(), _1));
+ */
+ // it seems like you're not supposed to do a name lookup
+ // on the peers returned from the tracker, but just strip
+ // the .i2p and use it as a destination
+ std::string hostname = i->hostname.substr(i->hostname.size() - 4);
+ torrent_state st = get_policy_state();
+ need_policy();
+ if (m_policy->add_i2p_peer(hostname.c_str(), peer_info::tracker, 0, &st))
+ state_updated();
+ peers_erased(st.erased);
}
else
+#endif
{
- // ignore local addresses from the tracker (unless the tracker is local too)
- // there are 2 reasons to allow this:
- // 1. retrackers are popular in russia, where an ISP runs a tracker within
- // the AS (but not on the local network) giving out peers only from the
- // local network
- // 2. it might make sense to have a tracker extension in the future where
- // trackers records a peer's internal and external IP, and match up
- // peers on the same local network
-// if (is_local(a.address()) && !is_local(tracker_ip)) continue;
- if (add_peer(a, peer_info::tracker))
- state_updated();
+#if defined TORRENT_ASIO_DEBUGGING
+ add_outstanding_async("torrent::on_peer_name_lookup");
+#endif
+ tcp::resolver::query q(i->hostname, to_string(i->port).elems);
+ m_ses.get_resolver().async_resolve(i->hostname, 0
+ , boost::bind(&torrent::on_peer_name_lookup
+ , shared_from_this(), _1, _2, i->port));
}
}
+
+ // there are 2 reasons to allow local IPs to be returned from a
+ // non-local tracker
+ // 1. retrackers are popular in russia, where an ISP runs a tracker within
+ // the AS (but not on the local network) giving out peers only from the
+ // local network
+ // 2. it might make sense to have a tracker extension in the future where
+ // trackers records a peer's internal and external IP, and match up
+ // peers on the same local network
+
+ bool need_update = false;
+ for (std::vector::const_iterator i = resp.peers4.begin();
+ i != resp.peers4.end(); ++i)
+ {
+ tcp::endpoint a(address_v4(i->ip), i->port);
+ need_update |= bool(add_peer(a, peer_info::tracker));
+ }
+
+#if TORRENT_USE_IPV6
+ for (std::vector::const_iterator i = resp.peers6.begin();
+ i != resp.peers6.end(); ++i)
+ {
+ tcp::endpoint a(address_v6(i->ip), i->port);
+ need_update |= bool(add_peer(a, peer_info::tracker));
+ }
+#endif
+ if (need_update) state_updated();
+
update_want_peers();
if (m_ses.alerts().should_post())
{
m_ses.alerts().post_alert(tracker_reply_alert(
- get_handle(), peer_list.size(), r.url));
+ get_handle(), resp.peers.size() + resp.peers4.size()
+#if TORRENT_USE_IPV6
+ + resp.peers6.size()
+#endif
+ , r.url));
}
m_got_tracker_response = true;
diff --git a/src/udp_tracker_connection.cpp b/src/udp_tracker_connection.cpp
index 148690f27..c884ca5c6 100644
--- a/src/udp_tracker_connection.cpp
+++ b/src/udp_tracker_connection.cpp
@@ -522,10 +522,13 @@ 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);
+
+ tracker_response resp;
+
+ resp.interval = detail::read_int32(buf);
+ resp.min_interval = 60;
+ resp.incomplete = detail::read_int32(buf);
+ resp.complete = detail::read_int32(buf);
int num_peers = (size - 20) / 6;
if ((size - 20) % 6 != 0)
{
@@ -549,22 +552,14 @@ namespace libtorrent
}
std::vector peer_list;
+ resp.peers4.reserve(num_peers);
for (int i = 0; i < num_peers; ++i)
{
- // TODO: it would be more efficient to not use a string here.
- // however, the problem is that some trackers will respond
- // with actual strings. For example i2p trackers
- peer_entry e;
- char ip_string[100];
- unsigned int a = detail::read_uint8(buf);
- unsigned int b = detail::read_uint8(buf);
- unsigned int c = detail::read_uint8(buf);
- unsigned int d = detail::read_uint8(buf);
- snprintf(ip_string, 100, "%u.%u.%u.%u", a, b, c, d);
- e.ip = ip_string;
+ ipv4_peer_entry e;
+ memcpy(&e.ip[0], buf, 4);
+ buf += 4;
e.port = detail::read_uint16(buf);
- e.pid.clear();
- peer_list.push_back(e);
+ resp.peers4.push_back(e);
}
std::list ip_list;
@@ -575,7 +570,7 @@ namespace libtorrent
}
cb->tracker_response(tracker_req(), m_target.address(), ip_list
- , peer_list, interval, min_interval, complete, incomplete, 0, address(), "" /*trackerid*/);
+ , resp);
close();
return true;