/* 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 using namespace sim; using namespace lt; using socks_flags_t = lt::flags::bitfield_flag; 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& 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::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 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 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 ses = std::make_shared(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(alert)) { if (a->socket_type == socket_type_t::utp) { alert_port = a->port; } } }, [&tracker_port](sim::simulation& sim, lt::session& , std::shared_ptr 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&) { 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(alert)) tracker_alert = true; }, [&](sim::simulation& sim, lt::session& , std::shared_ptr 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 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 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 ses = std::make_shared(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); }