From 5e7666526e3377877c01d214840ac63ef12f44ab Mon Sep 17 00:00:00 2001 From: arvidn Date: Mon, 6 Nov 2017 01:55:15 +0100 Subject: [PATCH] fix IPv6 tracker announce issue --- ChangeLog | 1 + include/libtorrent/aux_/session_impl.hpp | 10 ++-- include/libtorrent/aux_/session_interface.hpp | 5 +- include/libtorrent/http_connection.hpp | 10 ++-- include/libtorrent/torrent.hpp | 3 +- include/libtorrent/tracker_manager.hpp | 4 +- simulation/setup_dht.cpp | 2 +- simulation/test_http_connection.cpp | 2 +- simulation/test_tracker.cpp | 2 + src/http_connection.cpp | 14 ++--- src/http_tracker_connection.cpp | 2 +- src/session_impl.cpp | 54 +++++++++++++------ src/torrent.cpp | 45 ++++------------ src/tracker_manager.cpp | 6 +++ src/udp_tracker_connection.cpp | 10 ++-- test/test_http_connection.cpp | 3 +- 16 files changed, 89 insertions(+), 84 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3cc012b98..6bc16214e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,5 @@ + * fix IPv6 tracker announce issue * restore path sanitization behavior of ":" * fix listen socket issue when disabling "force_proxy" mode * fix full allocation failure on APFS diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index e912963bc..a7637f7aa 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -54,6 +54,8 @@ POSSIBILITY OF SUCH DAMAGE. #include #endif +#include + #ifdef TORRENT_USE_OPENSSL #include "libtorrent/ssl_stream.hpp" #endif @@ -231,8 +233,8 @@ namespace libtorrent // if we are listening on an IPv6 interface // this will return one of the IPv6 addresses on this // machine, otherwise just an empty endpoint - tcp::endpoint get_ipv6_interface() const TORRENT_OVERRIDE; - tcp::endpoint get_ipv4_interface() const TORRENT_OVERRIDE; + boost::optional get_ipv6_interface() const TORRENT_OVERRIDE; + boost::optional get_ipv4_interface() const TORRENT_OVERRIDE; void async_accept(boost::shared_ptr const& listener, bool ssl); void on_accept_connection(boost::shared_ptr const& s @@ -870,8 +872,8 @@ namespace libtorrent // if we're listening on an IPv6 interface // this is one of the non local IPv6 interfaces // on this machine - tcp::endpoint m_ipv6_interface; - tcp::endpoint m_ipv4_interface; + boost::optional m_ipv6_interface; + boost::optional m_ipv4_interface; // since we might be listening on multiple interfaces // we might need more than one listen socket diff --git a/include/libtorrent/aux_/session_interface.hpp b/include/libtorrent/aux_/session_interface.hpp index f2d9ed16a..8a646a23d 100644 --- a/include/libtorrent/aux_/session_interface.hpp +++ b/include/libtorrent/aux_/session_interface.hpp @@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include #ifndef TORRENT_DISABLE_LOGGING #include @@ -255,8 +256,8 @@ namespace libtorrent { namespace aux virtual void prioritize_connections(boost::weak_ptr t) = 0; - virtual tcp::endpoint get_ipv6_interface() const = 0; - virtual tcp::endpoint get_ipv4_interface() const = 0; + virtual boost::optional get_ipv6_interface() const = 0; + virtual boost::optional get_ipv4_interface() const = 0; virtual void trigger_auto_manage() = 0; diff --git a/include/libtorrent/http_connection.hpp b/include/libtorrent/http_connection.hpp index c2131a147..e9618e02f 100644 --- a/include/libtorrent/http_connection.hpp +++ b/include/libtorrent/http_connection.hpp @@ -42,6 +42,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include @@ -113,7 +114,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 = NULL, int handle_redirects = 5 , std::string const& user_agent = std::string() - , address const& bind_addr = address_v4::any() + , boost::optional
bind_addr = boost::none , int resolve_flags = 0, std::string const& auth_ = std::string() #if TORRENT_USE_I2P , i2p_connection* i2p_conn = 0 @@ -123,7 +124,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 = NULL , bool ssl = false, int handle_redirect = 5 - , address const& bind_addr = address_v4::any() + , boost::optional
bind_addr = boost::none , int resolve_flags = 0 #if TORRENT_USE_I2P , i2p_connection* i2p_conn = 0 @@ -199,9 +200,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. unset means do not bind + boost::optional
m_bind_addr; // if username password was passed in, remember it in case we need to // re-issue the request for a redirect diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 62ca79cd1..1a63ad079 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -744,8 +744,7 @@ namespace libtorrent void force_tracker_request(time_point, int tracker_idx); void scrape_tracker(int idx, bool user_triggered); void announce_with_tracker(boost::uint8_t e - = tracker_request::none - , address const& bind_interface = address_v4::any()); + = tracker_request::none); int seconds_since_last_scrape() const { return m_last_scrape == (std::numeric_limits::min)() diff --git a/include/libtorrent/tracker_manager.hpp b/include/libtorrent/tracker_manager.hpp index 59e478552..27060018f 100644 --- a/include/libtorrent/tracker_manager.hpp +++ b/include/libtorrent/tracker_manager.hpp @@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #ifdef TORRENT_USE_OPENSSL // there is no forward declaration header for asio @@ -164,7 +165,7 @@ namespace libtorrent #endif sha1_hash info_hash; peer_id pid; - address bind_ip; + boost::optional
bind_ip; bool send_stats; @@ -329,7 +330,6 @@ namespace libtorrent , int interval = 0, int min_interval = 0); virtual void start() = 0; virtual void close(); - address const& bind_interface() const { return m_req.bind_ip; } void sent_bytes(int bytes); void received_bytes(int bytes); virtual bool on_receive(error_code const&, udp::endpoint const& diff --git a/simulation/setup_dht.cpp b/simulation/setup_dht.cpp index 64bce3385..82561a2b1 100644 --- a/simulation/setup_dht.cpp +++ b/simulation/setup_dht.cpp @@ -142,7 +142,7 @@ struct dht_node final : lt::dht::udp_socket_interface // since the simulation is single threaded, we can get away with just // allocating a single of these static bdecode_node msg; - int ret = bdecode(m_buffer, m_buffer + bytes_transferred, msg, err, &pos, 10, 500); + int const ret = bdecode(m_buffer, m_buffer + bytes_transferred, msg, err, &pos, 10, 500); if (ret != 0) return; if (msg.type() != bdecode_node::dict_t) return; diff --git a/simulation/test_http_connection.cpp b/simulation/test_http_connection.cpp index 0fde8936f..cde746bfa 100644 --- a/simulation/test_http_connection.cpp +++ b/simulation/test_http_connection.cpp @@ -175,7 +175,7 @@ boost::shared_ptr test_request(io_service& ios 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", boost::none , 0, auth); return h; } diff --git a/simulation/test_tracker.cpp b/simulation/test_tracker.cpp index 260412366..f887801da 100644 --- a/simulation/test_tracker.cpp +++ b/simulation/test_tracker.cpp @@ -311,6 +311,7 @@ void test_ipv6_support(char const* listen_interfaces ++v4_announces; TEST_EQUAL(method, "GET"); + TEST_CHECK(req.find("&port=6881") != std::string::npos); char response[500]; int size = snprintf(response, sizeof(response), "d8:intervali1800e5:peers0:e"); return sim::send_response(200, "OK", size) + response; @@ -323,6 +324,7 @@ void test_ipv6_support(char const* listen_interfaces ++v6_announces; TEST_EQUAL(method, "GET"); + TEST_CHECK(req.find("&port=6881") != std::string::npos); char response[500]; int size = snprintf(response, sizeof(response), "d8:intervali1800e5:peers0:e"); return sim::send_response(200, "OK", size) + response; diff --git a/src/http_connection.cpp b/src/http_connection.cpp index a18a12a56..f94e169ab 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -118,7 +118,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
bind_addr, int resolve_flags, std::string const& auth_ #if TORRENT_USE_I2P , i2p_connection* i2p_conn #endif @@ -227,7 +227,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
bind_addr , int resolve_flags #if TORRENT_USE_I2P , i2p_connection* i2p_conn @@ -359,10 +359,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, NULL, 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(boost::bind(&http_connection::callback @@ -560,10 +560,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 - if (m_bind_addr != address_v4::any()) + if (m_bind_addr) std::partition(m_endpoints.begin(), m_endpoints.end() , boost::bind(&address::is_v4, boost::bind(&tcp::endpoint::address, _1)) - == m_bind_addr.is_v4()); + == m_bind_addr->is_v4()); #endif connect(); diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp index ad39f1e60..e255039d6 100644 --- a/src/http_tracker_connection.cpp +++ b/src/http_tracker_connection.cpp @@ -227,7 +227,7 @@ namespace libtorrent m_tracker_connection->get(url, seconds(timeout) , tracker_req().event == tracker_request::stopped ? 2 : 1 , ps.proxy_tracker_connections ? &ps : NULL - , 5, user_agent, bind_interface() + , 5, user_agent, tracker_req().bind_ip , tracker_req().event == tracker_request::stopped ? resolver_interface::cache_only : 0 | resolver_interface::abort_on_shutdown diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 3f91931e6..9553106fb 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1349,7 +1349,7 @@ namespace aux { } #endif - if (is_any(req.bind_ip)) req.bind_ip = m_listen_interface.address(); + if (!req.bind_ip) req.bind_ip = m_listen_interface.address(); m_tracker_manager.queue_request(get_io_service(), req, c); } @@ -1709,12 +1709,12 @@ namespace aux { } #endif - tcp::endpoint session_impl::get_ipv6_interface() const + boost::optional session_impl::get_ipv6_interface() const { return m_ipv6_interface; } - tcp::endpoint session_impl::get_ipv4_interface() const + boost::optional session_impl::get_ipv4_interface() const { return m_ipv4_interface; } @@ -1887,8 +1887,8 @@ retry: if (m_abort) return; - m_ipv6_interface = tcp::endpoint(); - m_ipv4_interface = tcp::endpoint(); + m_ipv6_interface = boost::none; + m_ipv4_interface = boost::none; // TODO: instead of having a special case for this, just make the // default listen interfaces be "0.0.0.0:6881,[::]:6881" and use @@ -1960,18 +1960,6 @@ retry: } #endif // TORRENT_USE_IPV6 - // set our main IPv4 and IPv6 interfaces - // used to send to the tracker - std::vector ifs = enum_net_interfaces(m_io_service, ec); - for (std::vector::const_iterator i = ifs.begin() - , end(ifs.end()); i != end; ++i) - { - address const& addr = i->interface_address; - if (addr.is_v6() && !is_local(addr) && !is_loopback(addr)) - m_ipv6_interface = tcp::endpoint(addr, m_listen_interface.port()); - else if (addr.is_v4() && !is_local(addr) && !is_loopback(addr)) - m_ipv4_interface = tcp::endpoint(addr, m_listen_interface.port()); - } } else if (!m_settings.get_bool(settings_pack::force_proxy)) { @@ -2074,6 +2062,38 @@ retry: return; } +#if TORRENT_USE_IPV6 + bool want_v6 = (m_ipv6_interface && is_any(m_ipv6_interface->address())) + || m_listen_interfaces.empty(); +#else + bool const want_v6 = false; +#endif + bool want_v4 = (m_ipv4_interface && is_any(m_ipv4_interface->address())) + || m_listen_interfaces.empty(); + if (want_v6 || want_v4) + { + // set our main IPv4 and IPv6 interfaces + // used to send to the tracker + std::vector ifs = enum_net_interfaces(m_io_service, ec); + for (std::vector::const_iterator i = ifs.begin() + , end(ifs.end()); i != end && (want_v4 && want_v6); ++i) + { + address const& addr = i->interface_address; + if (want_v4 && addr.is_v4() && !is_local(addr) && !is_loopback(addr)) + { + m_ipv4_interface = tcp::endpoint(addr, m_listen_interface.port()); + want_v4 = false; + } +#if TORRENT_USE_IPV6 + else if (want_v6 && addr.is_v6() && !is_local(addr) && !is_loopback(addr)) + { + m_ipv6_interface = tcp::endpoint(addr, m_listen_interface.port()); + want_v6 = false; + } +#endif + } + } + #ifdef TORRENT_USE_OPENSSL int const ssl_port = m_settings.get_int(settings_pack::ssl_listen); udp::endpoint ssl_bind_if(m_listen_interface.address(), ssl_port); diff --git a/src/torrent.cpp b/src/torrent.cpp index 17f07dc45..78a64265f 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -3121,8 +3121,7 @@ namespace { #endif - void torrent::announce_with_tracker(boost::uint8_t e - , address const& bind_interface) + void torrent::announce_with_tracker(boost::uint8_t e) { TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(e == tracker_request::stopped || state() != torrent_status::checking_files); @@ -3196,9 +3195,8 @@ namespace { && m_torrent_file && m_torrent_file->priv()) { - tcp::endpoint ep; - ep = m_ses.get_ipv6_interface(); - if (ep != tcp::endpoint()) req.ipv6 = ep.address().to_v6(); + boost::optional ep = m_ses.get_ipv6_interface(); + if (ep) req.ipv6 = ep->address().to_v6(); } #endif @@ -3261,8 +3259,6 @@ namespace { req.triggered_manually = ae.triggered_manually; ae.triggered_manually = false; - req.bind_ip = bind_interface; - if (settings().get_bool(settings_pack::force_proxy)) { // in force_proxy mode we don't talk directly to trackers @@ -3533,31 +3529,10 @@ namespace { "external ip: %s\n" "resolved to: %s\n" "we connected to: %s\n" - "peers:" , interval , print_address(resp.external_ip).c_str() , resolved_to.c_str() , print_address(tracker_ip).c_str()); - - 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()); - } - for (std::vector::const_iterator i = resp.peers4.begin(); - i != resp.peers4.end(); ++i) - { - debug_log(" %s:%d", print_address(address_v4(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(" [%s]:%d", print_address(address_v6(i->ip)).c_str(), i->port); - } -#endif #endif // for each of the peers we got from the tracker @@ -3651,8 +3626,8 @@ namespace { // in order to avoid triggering this case over and over, check whether // this announce was itself triggered by this logic (second_announce) - 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())) + if (((m_ses.get_ipv6_interface() && tracker_ip.is_v4()) + || (m_ses.get_ipv4_interface() && tracker_ip.is_v6())) && !r.second_announce) { std::list
::const_iterator i = std::find_if(tracker_ips.begin() @@ -3662,7 +3637,7 @@ namespace { // the tracker did resolve to a different type of address, so announce // to that as well - // TODO 2: there's a bug when removing a torrent or shutting down the session, + // 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, @@ -3675,11 +3650,11 @@ namespace { // 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(); + ? m_ses.get_ipv6_interface()->address() + : m_ses.get_ipv4_interface()->address(); #ifndef TORRENT_DISABLE_LOGGING - debug_log("announce again using %s as the bind interface" - , print_address(req.bind_ip).c_str()); + debug_log("announce again using %s as the bind interface. port: %d" + , print_address(*req.bind_ip).c_str(), req.listen_port); #endif m_ses.queue_tracker_request(req, shared_from_this()); } diff --git a/src/tracker_manager.cpp b/src/tracker_manager.cpp index 6da3c7a01..e999452e0 100644 --- a/src/tracker_manager.cpp +++ b/src/tracker_manager.cpp @@ -282,6 +282,12 @@ namespace libtorrent if (req.event == tracker_request::stopped) req.num_want = 0; +#ifndef TORRENT_DISABLE_LOGGING + boost::shared_ptr cb = c.lock(); + if (cb) cb->debug_log("*** QUEUE_TRACKER_REQUEST [ listen_port: %d ]" + , req.listen_port); +#endif + TORRENT_ASSERT(!m_abort || req.event == tracker_request::stopped); if (m_abort && req.event != tracker_request::stopped) return; diff --git a/src/udp_tracker_connection.cpp b/src/udp_tracker_connection.cpp index 785ebcbc1..58875608e 100644 --- a/src/udp_tracker_connection.cpp +++ b/src/udp_tracker_connection.cpp @@ -243,23 +243,23 @@ namespace libtorrent std::vector::const_iterator iter = m_endpoints.begin(); udp::endpoint target = udp::endpoint(iter->address(), iter->port()); - if (bind_interface() != address_v4::any()) + if (tracker_req().bind_ip) { // find first endpoint that matches our bind interface type for (; iter != m_endpoints.end() && iter->address().is_v4() - != bind_interface().is_v4(); ++iter); + != tracker_req().bind_ip->is_v4(); ++iter); if (iter == m_endpoints.end()) { - TORRENT_ASSERT(target.address().is_v4() != bind_interface().is_v4()); + TORRENT_ASSERT(target.address().is_v4() != tracker_req().bind_ip->is_v4()); boost::shared_ptr cb = requester(); if (cb) { char const* tracker_address_type = target.address().is_v4() ? "IPv4" : "IPv6"; - char const* bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6"; + char const* bind_address_type = tracker_req().bind_ip->is_v4() ? "IPv4" : "IPv6"; char msg[200]; snprintf(msg, sizeof(msg) - , "the tracker only resolves to an %s address, and you're " + , "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); diff --git a/test/test_http_connection.cpp b/test/test_http_connection.cpp index b68fb85e9..722c45338 100644 --- a/test/test_http_connection.cpp +++ b/test/test_http_connection.cpp @@ -121,8 +121,7 @@ void run_test(std::string const& url, int size, int status, int connected boost::shared_ptr h(new 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() - , 0, auth); + h->get(url, seconds(1), 0, &ps, 5, "test/user-agent", boost::none, 0, auth); ios.reset(); error_code e; ios.run(e);