fix IPv6 tracker announce issue

This commit is contained in:
arvidn 2017-11-06 01:55:15 +01:00 committed by Arvid Norberg
parent 1593916e62
commit 5e7666526e
16 changed files with 89 additions and 84 deletions

View File

@ -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

View File

@ -54,6 +54,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/unordered_map.hpp>
#endif
#include <boost/optional.hpp>
#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<tcp::endpoint> get_ipv6_interface() const TORRENT_OVERRIDE;
boost::optional<tcp::endpoint> get_ipv4_interface() const TORRENT_OVERRIDE;
void async_accept(boost::shared_ptr<tcp::acceptor> const& listener, bool ssl);
void on_accept_connection(boost::shared_ptr<socket_type> 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<tcp::endpoint> m_ipv6_interface;
boost::optional<tcp::endpoint> m_ipv4_interface;
// since we might be listening on multiple interfaces
// we might need more than one listen socket

View File

@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/weak_ptr.hpp>
#include <boost/function.hpp>
#include <boost/optional.hpp>
#ifndef TORRENT_DISABLE_LOGGING
#include <boost/shared_ptr.hpp>
@ -255,8 +256,8 @@ namespace libtorrent { namespace aux
virtual void prioritize_connections(boost::weak_ptr<torrent> t) = 0;
virtual tcp::endpoint get_ipv6_interface() const = 0;
virtual tcp::endpoint get_ipv4_interface() const = 0;
virtual boost::optional<tcp::endpoint> get_ipv6_interface() const = 0;
virtual boost::optional<tcp::endpoint> get_ipv4_interface() const = 0;
virtual void trigger_auto_manage() = 0;

View File

@ -42,6 +42,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/noncopyable.hpp>
#include <boost/optional.hpp>
#include <vector>
#include <string>
@ -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<address> 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<address> 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<address> m_bind_addr;
// if username password was passed in, remember it in case we need to
// re-issue the request for a redirect

View File

@ -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<boost::int16_t>::min)()

View File

@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/weak_ptr.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/unordered_map.hpp>
#include <boost/optional.hpp>
#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<address> 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&

View File

@ -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;

View File

@ -175,7 +175,7 @@ boost::shared_ptr<http_connection> 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;
}

View File

@ -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;

View File

@ -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<address> 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<address> 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();

View File

@ -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

View File

@ -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<tcp::endpoint> session_impl::get_ipv6_interface() const
{
return m_ipv6_interface;
}
tcp::endpoint session_impl::get_ipv4_interface() const
boost::optional<tcp::endpoint> 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<ip_interface> ifs = enum_net_interfaces(m_io_service, ec);
for (std::vector<ip_interface>::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<ip_interface> ifs = enum_net_interfaces(m_io_service, ec);
for (std::vector<ip_interface>::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);

View File

@ -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<tcp::endpoint> 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<peer_entry>::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<ipv4_peer_entry>::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<ipv6_peer_entry>::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<address>::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());
}

View File

@ -282,6 +282,12 @@ namespace libtorrent
if (req.event == tracker_request::stopped)
req.num_want = 0;
#ifndef TORRENT_DISABLE_LOGGING
boost::shared_ptr<request_callback> 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;

View File

@ -243,23 +243,23 @@ namespace libtorrent
std::vector<tcp::endpoint>::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<request_callback> 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);

View File

@ -121,8 +121,7 @@ void run_test(std::string const& url, int size, int status, int connected
boost::shared_ptr<http_connection> 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);