From 437cb94fd0bed274ca89cd0b4997e15022ff6a5e Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Fri, 15 May 2009 21:23:41 +0000 Subject: [PATCH] improved IPv6 support by announcing twice when necessary --- ChangeLog | 1 + include/libtorrent/aux_/session_impl.hpp | 3 +- include/libtorrent/http_connection.hpp | 2 + .../libtorrent/http_tracker_connection.hpp | 2 - include/libtorrent/torrent.hpp | 4 +- include/libtorrent/tracker_manager.hpp | 7 +- include/libtorrent/udp_tracker_connection.hpp | 2 +- src/http_connection.cpp | 6 +- src/http_tracker_connection.cpp | 16 ++-- src/torrent.cpp | 56 +++++++++-- src/tracker_manager.cpp | 9 +- src/udp_tracker_connection.cpp | 94 +++++++++++++------ 12 files changed, 142 insertions(+), 60 deletions(-) diff --git a/ChangeLog b/ChangeLog index 20331c068..ed03f4213 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * improved IPv6 support by announcing twice when necessary * added feature to set a separate global rate limit for local peers * added preset settings for low memory environments and seed machines min_memory_usage() and high_performance_seeder() diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 7f658630b..c40a1f13c 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -719,6 +719,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 complete @@ -737,7 +738,7 @@ namespace libtorrent to_hex((const char*)&i->pid[0], 20, pid); if (i->pid.is_all_zeros()) pid[0] = 0; - snprintf(tmp, 200, " %16s %5d %s\n", i->ip.c_str(), i->port, pid); + snprintf(tmp, 200, " %-16s %-5d %s\n", i->ip.c_str(), i->port, pid); s += tmp; } snprintf(tmp, 200, "external ip: %s\n", print_address(external_ip).c_str()); diff --git a/include/libtorrent/http_connection.hpp b/include/libtorrent/http_connection.hpp index fb88d0649..9bf9727c5 100644 --- a/include/libtorrent/http_connection.hpp +++ b/include/libtorrent/http_connection.hpp @@ -122,6 +122,8 @@ struct http_connection : boost::enable_shared_from_this, boost: #else socket_type const& socket() const { return m_sock; } #endif + + std::list const& endpoints() const { return m_endpoints; } private: diff --git a/include/libtorrent/http_tracker_connection.hpp b/include/libtorrent/http_tracker_connection.hpp index d94637d54..47d7a1ac4 100644 --- a/include/libtorrent/http_tracker_connection.hpp +++ b/include/libtorrent/http_tracker_connection.hpp @@ -70,7 +70,6 @@ namespace libtorrent , connection_queue& cc , tracker_manager& man , tracker_request const& req - , address bind_infc , boost::weak_ptr c , aux::session_impl const& ses , proxy_settings const& ps @@ -97,7 +96,6 @@ namespace libtorrent tracker_manager& m_man; boost::shared_ptr m_tracker_connection; aux::session_impl const& m_ses; - address m_bind_iface; address m_tracker_ip; proxy_settings const& m_ps; connection_queue& m_cc; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 31fad4605..ba2fa5dee 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -363,6 +363,7 @@ namespace libtorrent virtual void tracker_response( tracker_request const& r , address const& tracker_ip + , std::list
const& ip_list , std::vector& e, int interval , int complete, int incomplete, address const& external_ip); virtual void tracker_request_timed_out( @@ -390,7 +391,8 @@ namespace libtorrent void force_tracker_request(ptime); void scrape_tracker(); void announce_with_tracker(tracker_request::event_t e - = tracker_request::none); + = tracker_request::none + , address const& bind_interface = address_v4::any()); ptime const& last_scrape() const { return m_last_scrape; } // sets the username and password that will be sent to diff --git a/include/libtorrent/tracker_manager.hpp b/include/libtorrent/tracker_manager.hpp index d76aea6c6..c8155b7bb 100644 --- a/include/libtorrent/tracker_manager.hpp +++ b/include/libtorrent/tracker_manager.hpp @@ -110,6 +110,7 @@ namespace libtorrent int num_want; std::string ipv6; std::string ipv4; + address bind_ip; }; struct TORRENT_EXPORT request_callback @@ -124,6 +125,7 @@ namespace libtorrent virtual void tracker_response( tracker_request const& req , address const& tracker_ip + , std::list
const& ip_list , std::vector& peers , int interval , int complete @@ -187,7 +189,6 @@ namespace libtorrent tracker_connection(tracker_manager& man , tracker_request const& req , io_service& ios - , address bind_interface , boost::weak_ptr r); boost::shared_ptr requester(); @@ -199,14 +200,13 @@ namespace libtorrent void fail_timeout(); virtual void start() = 0; virtual void close(); - address const& bind_interface() const { return m_bind_interface; } + address const& bind_interface() const { return m_req.bind_ip; } void sent_bytes(int bytes); void received_bytes(int bytes); protected: boost::weak_ptr m_requester; private: - address m_bind_interface; tracker_manager& m_man; const tracker_request m_req; }; @@ -226,7 +226,6 @@ namespace libtorrent , connection_queue& cc , tracker_request r , std::string const& auth - , address bind_infc , boost::weak_ptr c = boost::weak_ptr()); void abort_all_requests(bool all = false); diff --git a/include/libtorrent/udp_tracker_connection.hpp b/include/libtorrent/udp_tracker_connection.hpp index 114e414f2..f0a777a1a 100644 --- a/include/libtorrent/udp_tracker_connection.hpp +++ b/include/libtorrent/udp_tracker_connection.hpp @@ -71,7 +71,6 @@ namespace libtorrent , connection_queue& cc , tracker_manager& man , tracker_request const& req - , address bind_infc , boost::weak_ptr c , aux::session_impl const& ses , proxy_settings const& ps); @@ -112,6 +111,7 @@ namespace libtorrent udp::resolver m_name_lookup; udp_socket m_socket; udp::endpoint m_target; + std::list m_endpoints; int m_transaction_id; boost::int64_t m_connection_id; diff --git a/src/http_connection.cpp b/src/http_connection.cpp index cf35eb5dd..4be7d1150 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -298,8 +298,10 @@ void http_connection::on_resolve(error_code const& e // 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 - std::partition(m_endpoints.begin(), m_endpoints.end() - , boost::bind(&address::is_v4, boost::bind(&tcp::endpoint::address, _1)) == m_bind_addr.is_v4()); + if (m_bind_addr != address_v4::any()) + std::partition(m_endpoints.begin(), m_endpoints.end() + , boost::bind(&address::is_v4, boost::bind(&tcp::endpoint::address, _1)) + == m_bind_addr.is_v4()); #endif queue_connect(); diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp index 1796228ea..80adecab4 100644 --- a/src/http_tracker_connection.cpp +++ b/src/http_tracker_connection.cpp @@ -70,15 +70,13 @@ namespace libtorrent , connection_queue& cc , tracker_manager& man , tracker_request const& req - , address bind_infc , boost::weak_ptr c , aux::session_impl const& ses , proxy_settings const& ps , std::string const& auth) - : tracker_connection(man, req, ios, bind_infc, c) + : tracker_connection(man, req, ios, c) , m_man(man) , m_ses(ses) - , m_bind_iface(bind_infc) , m_ps(ps) , m_cc(cc) , m_ios(ios) @@ -173,7 +171,7 @@ namespace libtorrent :settings.tracker_completion_timeout; m_tracker_connection->get(url, seconds(timeout) - , 1, &m_ps, 5, settings.user_agent, m_bind_iface); + , 1, &m_ps, 5, settings.user_agent, bind_interface()); // the url + 100 estimated header size sent_bytes(url.size() + 100); @@ -476,8 +474,14 @@ namespace libtorrent if (incomplete_ent && incomplete_ent->type() == entry::int_t) incomplete = int(incomplete_ent->integer()); - cb->tracker_response(tracker_req(), m_tracker_ip, peer_list, interval->integer(), complete - , incomplete, external_ip); + std::list
ip_list; + std::transform(m_tracker_connection->endpoints().begin() + , m_tracker_connection->endpoints().end() + , std::back_inserter(ip_list) + , boost::bind(&tcp::endpoint::address, _1)); + + cb->tracker_response(tracker_req(), m_tracker_ip, ip_list, peer_list + , interval->integer(), complete, incomplete, external_ip); } } diff --git a/src/torrent.cpp b/src/torrent.cpp index a5478a24c..03c3e3b08 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1054,7 +1054,8 @@ namespace libtorrent #endif - void torrent::announce_with_tracker(tracker_request::event_t e) + void torrent::announce_with_tracker(tracker_request::event_t e + , address const& bind_interface) { INVARIANT_CHECK; @@ -1106,6 +1107,9 @@ namespace libtorrent if (!ae.complete_sent && is_seed()) req.event = tracker_request::completed; } + if (!is_any(bind_interface)) req.bind_ip = bind_interface; + else req.bind_ip = m_ses.m_listen_interface.address(); + #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING (*m_ses.m_logger) << time_now_string() << " ==> TACKER REQUEST " << req.url << " event=" << (req.event==tracker_request::stopped?"stopped" @@ -1115,13 +1119,12 @@ namespace libtorrent { boost::shared_ptr tl(new aux::tracker_logger(m_ses)); m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req - , tracker_login(), m_ses.m_listen_interface.address(), tl); + , tracker_login(), tl); } else #endif m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req - , tracker_login(), m_ses.m_listen_interface.address() - , m_abort?boost::shared_ptr():shared_from_this()); + , tracker_login() , m_abort?boost::shared_ptr():shared_from_this()); ae.updating = true; if (m_ses.m_alerts.should_post()) @@ -1146,8 +1149,9 @@ namespace libtorrent req.info_hash = m_torrent_file->info_hash(); req.kind = tracker_request::scrape_request; req.url = m_trackers[i].url; + 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(), m_ses.m_listen_interface.address(), shared_from_this()); + , tracker_login(), shared_from_this()); m_last_scrape = time_now(); } @@ -1182,7 +1186,8 @@ namespace libtorrent void torrent::tracker_response( tracker_request const& r - , address const& tracker_ip + , 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 complete @@ -1237,6 +1242,10 @@ namespace libtorrent s << "\n"; } s << "external ip: " << external_ip << "\n"; + s << "tracker ips: "; + std::copy(tracker_ips.begin(), tracker_ips.end(), std::ostream_iterator
(s, " ")); + s << "\n"; + s << "we connected to: " << tracker_ip << "\n"; debug_log(s.str()); #endif // for each of the peers we got from the tracker @@ -1273,6 +1282,41 @@ namespace libtorrent get_handle(), peer_list.size(), r.url)); } m_got_tracker_response = true; + + // 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 + // don't connect twice just to tell it we're stopping + + if (((!is_any(m_ses.m_ipv6_interface.address()) && tracker_ip.is_v4()) + || (!is_any(m_ses.m_ipv4_interface.address()) && tracker_ip.is_v6())) + && r.bind_ip != m_ses.m_ipv4_interface.address() + && r.bind_ip != m_ses.m_ipv6_interface.address() + && r.event != tracker_request::stopped) + { + std::list
::const_iterator i = std::find_if(tracker_ips.begin() + , tracker_ips.end(), boost::bind(&address::is_v4, _1) != tracker_ip.is_v4()); + if (i != tracker_ips.end()) + { + // the tracker did resolve to a different type if address, so announce + // to that as well + + // tell the tracker to bind to the opposite protocol type + address bind_interface = tracker_ip.is_v4() + ?m_ses.m_ipv6_interface.address() + :m_ses.m_ipv4_interface.address(); + announce_with_tracker(r.event, bind_interface); +#if (defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING) && TORRENT_USE_IOSTREAM + debug_log("announce again using " + print_address(bind_interface) + + " as the bind interface"); +#endif + } + } } void torrent::on_peer_name_lookup(error_code const& e, tcp::resolver::iterator host diff --git a/src/tracker_manager.cpp b/src/tracker_manager.cpp index 318d2dbb7..40966ed35 100644 --- a/src/tracker_manager.cpp +++ b/src/tracker_manager.cpp @@ -133,11 +133,9 @@ namespace libtorrent tracker_manager& man , tracker_request const& req , io_service& ios - , address bind_interface_ , boost::weak_ptr r) : timeout_handler(ios) , m_requester(r) - , m_bind_interface(bind_interface_) , m_man(man) , m_req(req) {} @@ -211,7 +209,6 @@ namespace libtorrent , connection_queue& cc , tracker_request req , std::string const& auth - , address bind_infc , boost::weak_ptr c) { mutex_t::scoped_lock l(m_mutex); @@ -236,14 +233,14 @@ namespace libtorrent #endif { con = new http_tracker_connection( - ios, cc, *this, req, bind_infc, c + ios, cc, *this, req, c , m_ses, m_proxy, auth); } else if (protocol == "udp") { con = new udp_tracker_connection( - ios, cc, *this, req, bind_infc - , c, m_ses, m_proxy); + ios, cc, *this, req , c, m_ses + , m_proxy); } else { diff --git a/src/udp_tracker_connection.cpp b/src/udp_tracker_connection.cpp index 26128d771..3a86e1259 100644 --- a/src/udp_tracker_connection.cpp +++ b/src/udp_tracker_connection.cpp @@ -75,11 +75,10 @@ namespace libtorrent , connection_queue& cc , tracker_manager& man , tracker_request const& req - , address bind_infc , boost::weak_ptr c , aux::session_impl const& ses , proxy_settings const& proxy) - : tracker_connection(man, req, ios, bind_infc, c) + : tracker_connection(man, req, ios, c) , m_man(man) , m_name_lookup(ios) , m_socket(ios, boost::bind(&udp_tracker_connection::on_receive, self(), _1, _2, _3, _4), cc) @@ -143,42 +142,69 @@ 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. - udp::resolver::iterator target = i; - udp::resolver::iterator end; - udp::endpoint target_address = *i; - for (; target != end && target->endpoint().address().is_v4() - != bind_interface().is_v4(); ++target); - if (target == end) + + std::transform(i, udp::resolver::iterator(), std::back_inserter(m_endpoints) + , boost::bind(&udp::resolver::iterator::value_type::endpoint, _1)); + + // remove endpoints that are filtered by the IP filter + for (std::list::iterator i = m_endpoints.begin(); + i != m_endpoints.end();) { - TORRENT_ASSERT(target_address.address().is_v4() != bind_interface().is_v4()); - if (cb) - { - std::string tracker_address_type = target_address.address().is_v4() ? "IPv4" : "IPv6"; - std::string bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6"; - cb->tracker_warning(tracker_req(), "the tracker only resolves to an " - + tracker_address_type + " address, and you're listening on an " - + bind_address_type + " socket. This may prevent you from receiving incoming connections."); - } - } - else - { - target_address = *target; + if (m_ses.m_ip_filter.access(i->address()) == ip_filter::blocked) + i = m_endpoints.erase(i); + else + ++i; } - if (m_ses.m_ip_filter.access(target_address.address()) & ip_filter::blocked) + if (m_endpoints.empty()) { fail(-1, "blocked by IP filter"); return; } - if (cb) cb->m_tracker_address = tcp::endpoint(target_address.address(), target_address.port()); - m_target = target_address; - error_code ec; - m_socket.bind(udp::endpoint(bind_interface(), 0), ec); - if (ec) + std::list::iterator iter = m_endpoints.begin(); + m_target = *iter; + + if (bind_interface() != address_v4::any()) { - fail(-1, ec.message().c_str()); - return; + // find first endpoint that matches our bind interface type + for (; iter != m_endpoints.end() && iter->address().is_v4() + != bind_interface().is_v4(); ++iter); + + if (iter == m_endpoints.end()) + { + TORRENT_ASSERT(m_target.address().is_v4() != bind_interface().is_v4()); + if (cb) + { + char const* tracker_address_type = m_target.address().is_v4() ? "IPv4" : "IPv6"; + char const* bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6"; + char msg[200]; + snprintf(msg, sizeof(msg) + , "the tracker only resolves to an %s address, and you're " + "listening on an %s socket. This may prevent you from receiving " + "incoming connections." + , tracker_address_type, bind_address_type); + + cb->tracker_warning(tracker_req(), msg); + } + } + else + { + m_target = *iter; + } + } + + if (cb) cb->m_tracker_address = tcp::endpoint(m_target.address(), m_target.port()); + + if (bind_interface() != address_v4::any()) + { + error_code ec; + m_socket.bind(udp::endpoint(bind_interface(), 0), ec); + if (ec) + { + fail(-1, ec.message().c_str()); + return; + } } send_udp_connect(); } @@ -423,8 +449,14 @@ namespace libtorrent peer_list.push_back(e); } - cb->tracker_response(tracker_req(), m_target.address(), peer_list, interval - , complete, incomplete, address()); + std::list
ip_list; + std::transform(m_endpoints.begin() + , m_endpoints.end() + , std::back_inserter(ip_list) + , boost::bind(&udp::endpoint::address, _1)); + + cb->tracker_response(tracker_req(), m_target.address(), ip_list + , peer_list, interval, complete, incomplete, address()); m_man.remove_request(this); close();