mirror of
https://github.com/arvidn/libtorrent.git
synced 2026-05-07 00:10:28 -04:00
305 lines
8.5 KiB
C++
305 lines
8.5 KiB
C++
/*
|
|
|
|
Copyright (c) 2016, 2021, Alden Torres
|
|
Copyright (c) 2016-2021, Arvid Norberg
|
|
All rights reserved.
|
|
|
|
You may use, distribute and modify this code under the terms of the BSD license,
|
|
see LICENSE file.
|
|
*/
|
|
|
|
#include "libtorrent/session.hpp"
|
|
#include "libtorrent/torrent_handle.hpp"
|
|
#include "libtorrent/settings_pack.hpp"
|
|
#include "libtorrent/alert_types.hpp"
|
|
#include "libtorrent/aux_/deadline_timer.hpp"
|
|
#include "simulator/http_server.hpp"
|
|
#include "settings.hpp"
|
|
#include "create_torrent.hpp"
|
|
#include "setup_transfer.hpp" // for addr()
|
|
#include "simulator/simulator.hpp"
|
|
#include "setup_swarm.hpp"
|
|
#include "utils.hpp"
|
|
#include "simulator/socks_server.hpp"
|
|
#include "simulator/utils.hpp"
|
|
#include "fake_peer.hpp"
|
|
#include <iostream>
|
|
|
|
using namespace sim;
|
|
using namespace lt;
|
|
|
|
using socks_flags_t = lt::flags::bitfield_flag<std::uint64_t, struct socks_test_type_tag>;
|
|
|
|
constexpr socks_flags_t proxy_hostname = 0_bit;
|
|
|
|
namespace {
|
|
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) override
|
|
{
|
|
if (hostname == "tracker.hostname.org")
|
|
{
|
|
result.push_back(make_address_v4("2.2.2.2"));
|
|
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
|
|
}
|
|
|
|
return default_config::hostname_lookup(requestor, hostname, result, ec);
|
|
}
|
|
};
|
|
} // anonymous namespace
|
|
|
|
// this is the general template for these tests. create the session with custom
|
|
// settings (Settings), set up the test, by adding torrents with certain
|
|
// arguments (Setup), run the test and verify the end state (Test)
|
|
template <typename Setup, typename HandleAlerts, typename Test>
|
|
void run_test(Setup const& setup
|
|
, HandleAlerts const& on_alert
|
|
, Test const& test
|
|
, std::uint32_t const flags = 0)
|
|
{
|
|
// setup the simulation
|
|
sim_config network_cfg;
|
|
sim::simulation sim{network_cfg};
|
|
std::unique_ptr<sim::asio::io_context> ios = make_io_context(sim, 0);
|
|
lt::session_proxy zombie;
|
|
|
|
sim::asio::io_context proxy_ios{sim, addr("50.50.50.50") };
|
|
sim::socks_server socks4(proxy_ios, 4444, 4, flags);
|
|
sim::socks_server socks5(proxy_ios, 5555, 5, flags);
|
|
|
|
lt::settings_pack pack = settings();
|
|
// create session
|
|
std::shared_ptr<lt::session> ses = std::make_shared<lt::session>(pack, *ios);
|
|
|
|
// set up test, like adding torrents (customization point)
|
|
setup(*ses);
|
|
|
|
// only monitor alerts for session 0 (the downloader)
|
|
print_alerts(*ses, [=](lt::session& ses, lt::alert const* a) {
|
|
on_alert(ses, a);
|
|
});
|
|
|
|
lt::add_torrent_params params = ::create_torrent(1);
|
|
params.flags &= ~lt::torrent_flags::auto_managed;
|
|
params.flags &= ~lt::torrent_flags::paused;
|
|
params.save_path = save_path(0);
|
|
ses->async_add_torrent(params);
|
|
|
|
// set up a timer to fire later, to verify everything we expected to happen
|
|
// happened
|
|
sim::timer t(sim, lt::seconds(100), [&](boost::system::error_code const&)
|
|
{
|
|
std::printf("shutting down\n");
|
|
// shut down
|
|
zombie = ses->abort();
|
|
ses.reset();
|
|
});
|
|
|
|
test(sim, *ses, params.ti);
|
|
}
|
|
|
|
TORRENT_TEST(socks5_tcp_announce)
|
|
{
|
|
using namespace lt;
|
|
int tracker_port = -1;
|
|
int alert_port = -1;
|
|
run_test(
|
|
[](lt::session& ses)
|
|
{
|
|
set_proxy(ses, settings_pack::socks5);
|
|
|
|
lt::add_torrent_params params;
|
|
params.info_hashes.v1 = sha1_hash("abababababababababab");
|
|
params.trackers.push_back("http://2.2.2.2:8080/announce");
|
|
params.save_path = ".";
|
|
ses.async_add_torrent(params);
|
|
},
|
|
[&alert_port](lt::session&, lt::alert const* alert) {
|
|
if (auto* a = lt::alert_cast<lt::listen_succeeded_alert>(alert))
|
|
{
|
|
if (a->socket_type == socket_type_t::utp)
|
|
{
|
|
alert_port = a->port;
|
|
}
|
|
}
|
|
},
|
|
[&tracker_port](sim::simulation& sim, lt::session&
|
|
, std::shared_ptr<lt::torrent_info const> ti)
|
|
{
|
|
sim::asio::io_context web_server(sim, make_address_v4("2.2.2.2"));
|
|
// listen on port 8080
|
|
sim::http_server http(web_server, 8080);
|
|
|
|
http.register_handler("/announce"
|
|
, [&tracker_port](std::string method, std::string req
|
|
, std::map<std::string, std::string>&)
|
|
{
|
|
if (req.find("&event=started") != std::string::npos)
|
|
{
|
|
std::string::size_type port_pos = req.find("&port=");
|
|
TEST_CHECK(port_pos != std::string::npos);
|
|
if (port_pos != std::string::npos)
|
|
{
|
|
tracker_port = atoi(req.c_str() + port_pos + 6);
|
|
}
|
|
}
|
|
|
|
return sim::send_response(200, "OK", 27) + "d8:intervali1800e5:peers0:e";
|
|
});
|
|
|
|
sim.run();
|
|
}
|
|
);
|
|
|
|
TEST_EQUAL(tracker_port, 1);
|
|
TEST_CHECK(alert_port != -1);
|
|
}
|
|
|
|
void test_udp_tracker(std::uint32_t const flags, socks_flags_t const sflags)
|
|
{
|
|
using namespace lt;
|
|
bool tracker_alert = false;
|
|
bool connected = false;
|
|
bool announced = false;
|
|
run_test(
|
|
[sflags](lt::session& ses)
|
|
{
|
|
set_proxy(ses, settings_pack::socks5);
|
|
|
|
// The socks server in libsimulator does not support forwarding UDP
|
|
// packets to hostnames (just IPv4 destinations)
|
|
settings_pack p;
|
|
p.set_bool(settings_pack::proxy_hostnames, bool(sflags & proxy_hostname));
|
|
ses.apply_settings(p);
|
|
|
|
lt::add_torrent_params params;
|
|
params.info_hashes.v1 = sha1_hash("abababababababababab");
|
|
if (sflags & proxy_hostname)
|
|
params.trackers.push_back("udp://tracker.hostname.org:8080/announce");
|
|
else
|
|
params.trackers.push_back("udp://2.2.2.2:8080/announce");
|
|
params.save_path = ".";
|
|
ses.async_add_torrent(params);
|
|
},
|
|
[&tracker_alert](lt::session&, lt::alert const* alert) {
|
|
if (lt::alert_cast<lt::tracker_announce_alert>(alert))
|
|
tracker_alert = true;
|
|
},
|
|
[&](sim::simulation& sim, lt::session&
|
|
, std::shared_ptr<lt::torrent_info const> ti)
|
|
{
|
|
// listen on port 8080
|
|
udp_server tracker(sim, "2.2.2.2", 8080,
|
|
[&](char const* msg, int size)
|
|
{
|
|
using namespace lt::aux;
|
|
std::vector<char> ret;
|
|
TEST_CHECK(size >= 16);
|
|
|
|
if (size < 16) return ret;
|
|
|
|
std::uint64_t connection_id = read_uint64(msg);
|
|
std::uint32_t action = read_uint32(msg);
|
|
std::uint32_t transaction_id = read_uint32(msg);
|
|
|
|
std::uint64_t const conn_id = 0xfeedface1337ull;
|
|
|
|
if (action == 0)
|
|
{
|
|
std::printf("udp connect\n");
|
|
// udp tracker connect
|
|
TEST_CHECK(connection_id == 0x41727101980ull);
|
|
auto inserter = std::back_inserter(ret);
|
|
write_uint32(0, inserter); // connect
|
|
write_uint32(transaction_id, inserter);
|
|
write_uint64(conn_id, inserter);
|
|
connected = true;
|
|
}
|
|
else if (action == 1)
|
|
{
|
|
std::printf("udp announce\n");
|
|
// udp tracker announce
|
|
TEST_EQUAL(connection_id, conn_id);
|
|
|
|
auto inserter = std::back_inserter(ret);
|
|
write_uint32(1, inserter); // announce
|
|
write_uint32(transaction_id, inserter);
|
|
write_uint32(1800, inserter);
|
|
write_uint32(0, inserter); // leechers
|
|
write_uint32(0, inserter); // seeders
|
|
announced = true;
|
|
}
|
|
else
|
|
{
|
|
std::printf("unsupported udp tracker action: %u\n", action);
|
|
}
|
|
return ret;
|
|
});
|
|
|
|
sim.run();
|
|
}
|
|
, flags
|
|
);
|
|
|
|
TEST_CHECK(tracker_alert);
|
|
TEST_CHECK(connected);
|
|
TEST_CHECK(announced);
|
|
}
|
|
|
|
TORRENT_TEST(udp_tracker)
|
|
{
|
|
test_udp_tracker(0, {});
|
|
}
|
|
|
|
TORRENT_TEST(udp_tracker_empty_domainname)
|
|
{
|
|
test_udp_tracker(socks_flag::udp_associate_respond_empty_hostname, {});
|
|
}
|
|
|
|
TORRENT_TEST(udp_tracker_hostname)
|
|
{
|
|
test_udp_tracker(0, proxy_hostname);
|
|
}
|
|
|
|
TORRENT_TEST(socks5_udp_retry)
|
|
{
|
|
// this test is asserting that when a UDP associate command fails, we have a
|
|
// 5 second delay before we try again. There is no need to actually add a
|
|
// torrent for this test, just to open the udp socket with a socks5 proxy
|
|
using namespace libtorrent;
|
|
|
|
// setup the simulation
|
|
sim::default_config network_cfg;
|
|
sim::simulation sim{network_cfg};
|
|
std::unique_ptr<sim::asio::io_context> ios = make_io_context(sim, 0);
|
|
lt::session_proxy zombie;
|
|
|
|
sim::asio::io_context proxy_ios{sim, addr("50.50.50.50") };
|
|
// close UDP associate connections prematurely
|
|
sim::socks_server socks5(proxy_ios, 5555, 5, socks_flag::disconnect_udp_associate);
|
|
|
|
lt::settings_pack pack = settings();
|
|
// create session
|
|
std::shared_ptr<lt::session> ses = std::make_shared<lt::session>(pack, *ios);
|
|
print_alerts(*ses);
|
|
set_proxy(*ses, settings_pack::socks5);
|
|
|
|
sim::timer t(sim, lt::seconds(60), [&](boost::system::error_code const&)
|
|
{
|
|
fprintf(stderr, "shutting down\n");
|
|
// shut down
|
|
zombie = ses->abort();
|
|
ses.reset();
|
|
});
|
|
sim.run();
|
|
|
|
// number of UDP ASSOCIATE commands invoked on the socks proxy
|
|
// We run for 60 seconds. The sokcks5 retry interval is expected to be 5
|
|
// seconds, meaning there should have been 12 connection attempts
|
|
TEST_EQUAL(socks5.cmd_counts()[2], 12);
|
|
}
|