update libsimulator. add simulation/test for announcing twice to trackers over IPv4 and IPv6. fix bugs preventing libtorrent from doing that correctly

This commit is contained in:
arvidn 2015-10-27 00:21:07 -04:00
parent b2796af5b3
commit 729a8e9152
6 changed files with 152 additions and 14 deletions

View File

@ -124,6 +124,13 @@ namespace libtorrent
address ip = address::from_string(device_name, ec); address ip = address::from_string(device_name, ec);
if (!ec) if (!ec)
{ {
#if TORRENT_USE_IPV6
// this is to cover the case where "0.0.0.0" is considered any IPv4 or
// IPv6 address. If we're asking to be bound to an IPv6 address and
// providing 0.0.0.0 as the device, turn it into "::0"
if (ip == address_v4::any() && !ipv4)
ip = address_v6::any();
#endif
bind_ep.address(ip); bind_ep.address(ip);
// it appears to be an IP. Just bind to that address // it appears to be an IP. Just bind to that address
sock.bind(bind_ep, ec); sock.bind(bind_ep, ec);

@ -1 +1 @@
Subproject commit dedd409321589f8ed11d244e1b65772435ef91d1 Subproject commit 47ccf576c6ed7700752ba294962d8e297b1ce76f

View File

@ -127,11 +127,8 @@ struct swarm
bool term = false; bool term = false;
ses->pop_alerts(&alerts); ses->pop_alerts(&alerts);
for (std::vector<lt::alert*>::iterator i = alerts.begin(); for (lt::alert* a : alerts)
i != alerts.end(); ++i)
{ {
lt::alert* a = *i;
lt::time_duration d = a->timestamp() - m_start_time; lt::time_duration d = a->timestamp() - m_start_time;
boost::uint32_t millis = lt::duration_cast<lt::milliseconds>(d).count(); boost::uint32_t millis = lt::duration_cast<lt::milliseconds>(d).count();
printf("%4d.%03d: [%02d] %s\n", millis / 1000, millis % 1000, printf("%4d.%03d: [%02d] %s\n", millis / 1000, millis % 1000,

View File

@ -42,6 +42,8 @@ using namespace libtorrent;
using namespace sim; using namespace sim;
namespace lt = libtorrent; namespace lt = libtorrent;
using chrono::duration_cast;
// seconds // seconds
const int duration = 10000; const int duration = 10000;
@ -257,6 +259,131 @@ TORRENT_TEST(announce_interval_1200)
test_interval(3600); test_interval(3600);
} }
struct sim_config : sim::default_config
{
chrono::high_resolution_clock::duration hostname_lookup(
asio::ip::address const& requestor
, std::string hostname
, std::vector<asio::ip::address>& result
, boost::system::error_code& ec)
{
if (hostname == "tracker.com")
{
result.push_back(address_v4::from_string("10.0.0.2"));
result.push_back(address_v6::from_string("ff::dead:beef"));
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
}
return default_config::hostname_lookup(requestor, hostname, result, ec);
}
};
void on_alert_notify(lt::session* ses)
{
std::vector<lt::alert*> alerts;
ses->pop_alerts(&alerts);
for (lt::alert* a : alerts)
{
lt::time_duration d = a->timestamp().time_since_epoch();
boost::uint32_t millis = lt::duration_cast<lt::milliseconds>(d).count();
printf("%4d.%03d: %s\n", millis / 1000, millis % 1000,
a->message().c_str());
}
}
// this test makes sure that a tracker whose host name resolves to both IPv6 and
// IPv4 addresses will be announced to twice, once for each address family
TORRENT_TEST(ipv6_support)
{
using sim::asio::ip::address_v4;
sim_config network_cfg;
sim::simulation sim{network_cfg};
sim::asio::io_service web_server_v4(sim, address_v4::from_string("10.0.0.2"));
sim::asio::io_service web_server_v6(sim, address_v6::from_string("ff::dead:beef"));
// listen on port 8080
sim::http_server http_v4(web_server_v4, 8080);
sim::http_server http_v6(web_server_v6, 8080);
// the timestamps (in seconds) of all announces
std::vector<std::string> announces;
int v4_announces = 0;
int v6_announces = 0;
http_v4.register_handler("/announce"
, [&v4_announces](std::string method, std::string req)
{
++v4_announces;
TEST_EQUAL(method, "GET");
char response[500];
int size = snprintf(response, sizeof(response), "d8:intervali1800e5:peers0:e");
return sim::send_response(200, "OK", size) + response;
});
http_v6.register_handler("/announce"
, [&v6_announces](std::string method, std::string req)
{
++v6_announces;
TEST_EQUAL(method, "GET");
char response[500];
int size = snprintf(response, sizeof(response), "d8:intervali1800e5:peers0:e");
return sim::send_response(200, "OK", size) + response;
});
{
lt::session_proxy zombie;
asio::io_service ios(sim, { address_v4::from_string("10.0.0.3")
, address_v6::from_string("ffff::1337") });
lt::settings_pack sett = settings();
std::unique_ptr<lt::session> ses(new lt::session(sett, ios));
ses->set_alert_notify(std::bind(&on_alert_notify, ses.get()));
lt::add_torrent_params p;
p.name = "test-torrent";
p.save_path = ".";
p.info_hash.assign("abababababababababab");
//TODO: parameterize http vs. udp here
p.trackers.push_back("http://tracker.com:8080/announce");
ses->async_add_torrent(p);
// stop the torrent 5 seconds in
asio::high_resolution_timer stop(ios);
stop.expires_from_now(chrono::seconds(5));
stop.async_wait([&ses](boost::system::error_code const& ec)
{
std::vector<lt::torrent_handle> torrents = ses->get_torrents();
for (auto const& t : torrents)
{
t.pause();
}
});
// then shut down 10 seconds in
asio::high_resolution_timer terminate(ios);
terminate.expires_from_now(chrono::seconds(10));
terminate.async_wait([&ses,&zombie](boost::system::error_code const& ec)
{
zombie = ses->abort();
ses.reset();
});
sim.run();
}
// 2 because there's one announce on startup and one when shutting down
TEST_EQUAL(v4_announces, 2);
TEST_EQUAL(v6_announces, 2);
}
// TODO: test with different queuing settings // TODO: test with different queuing settings
// TODO: test when a torrent transitions from downloading to finished and // TODO: test when a torrent transitions from downloading to finished and
// finished to seeding // finished to seeding

View File

@ -1940,7 +1940,9 @@ retry:
{ {
error_code err; error_code err;
address test_family = address::from_string(device.c_str(), err); address test_family = address::from_string(device.c_str(), err);
if (!err && test_family.is_v4() != address_family) if (!err
&& test_family.is_v4() != address_family
&& !is_any(test_family))
continue; continue;
listen_socket_t s = setup_listener(device, address_family, port listen_socket_t s = setup_listener(device, address_family, port

View File

@ -3531,13 +3531,11 @@ namespace libtorrent
// do it if the bind IP for the tracker request that just completed // do it if the bind IP for the tracker request that just completed
// matches one of the listen interfaces, since that means this // matches one of the listen interfaces, since that means this
// announce was the second one // announce was the second one
// don't connect twice just to tell it we're stopping
if (((!is_any(m_ses.get_ipv6_interface().address()) && tracker_ip.is_v4()) 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())) || (!is_any(m_ses.get_ipv4_interface().address()) && tracker_ip.is_v6()))
&& r.bind_ip != m_ses.get_ipv4_interface().address() && r.bind_ip != m_ses.get_ipv4_interface().address()
&& r.bind_ip != m_ses.get_ipv6_interface().address() && r.bind_ip != m_ses.get_ipv6_interface().address())
&& r.event != tracker_request::stopped)
{ {
std::list<address>::const_iterator i = std::find_if(tracker_ips.begin() std::list<address>::const_iterator i = std::find_if(tracker_ips.begin()
, tracker_ips.end(), boost::bind(&address::is_v4, _1) != tracker_ip.is_v4()); , tracker_ips.end(), boost::bind(&address::is_v4, _1) != tracker_ip.is_v4());
@ -3546,15 +3544,22 @@ namespace libtorrent
// the tracker did resolve to a different type of address, so announce // the tracker did resolve to a different type of address, so announce
// to that as well // to that as well
// TODO 2: 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,
// and they should be announced to in parallel
tracker_request req = r;
// tell the tracker to bind to the opposite protocol type // tell the tracker to bind to the opposite protocol type
address bind_interface = tracker_ip.is_v4() req.bind_ip = tracker_ip.is_v4()
? m_ses.get_ipv6_interface().address() ? m_ses.get_ipv6_interface().address()
: m_ses.get_ipv4_interface().address(); : m_ses.get_ipv4_interface().address();
announce_with_tracker(r.event, bind_interface);
#ifndef TORRENT_DISABLE_LOGGING #ifndef TORRENT_DISABLE_LOGGING
debug_log("announce again using %s as the bind interface" debug_log("announce again using %s as the bind interface"
, print_address(bind_interface).c_str()); , print_address(req.bind_ip).c_str());
#endif #endif
m_ses.queue_tracker_request(req, shared_from_this());
} }
} }