Files
2026-04-09 16:42:44 +02:00

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);
}