Files

561 lines
13 KiB
C++

/*
Copyright (c) 2015-2016, 2018-2019, 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 "transfer_sim.hpp"
#include "libtorrent/load_torrent.hpp"
using namespace sim;
using namespace lt;
TORRENT_TEST(socks4_tcp)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses0, settings_pack::socks4);
filter_ips(ses1);
},
[](lt::session&, lt::alert const*) {},
expect_seed(true)
);
}
TORRENT_TEST(socks5_tcp_connect)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses0, settings_pack::socks5);
filter_ips(ses1);
},
[](lt::session&, lt::alert const*) {},
expect_seed(true)
);
}
TORRENT_TEST(encryption_tcp)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{ enable_enc(ses0); enable_enc(ses1); },
[](lt::session&, lt::alert const*) {},
expect_seed(true)
);
}
TORRENT_TEST(no_proxy_tcp_ipv6)
{
run_test(
no_init,
[](lt::session&, lt::alert const*) {},
expect_seed(true),
tx::ipv6
);
}
TORRENT_TEST(no_proxy_utp_ipv6)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{ utp_only(ses0); utp_only(ses1); },
[](lt::session&, lt::alert const*) {},
expect_seed(true),
tx::ipv6
);
}
// TODO: the socks server does not support IPv6 addresses yet
/*
TORRENT_TEST(socks5_tcp_ipv6)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses0, settings_pack::socks5);
filter_ips(ses1);
},
[](lt::session&, lt::alert const*) {},
[](std::shared_ptr<lt::session> ses[2]) {
TEST_EQUAL(is_seed(*ses[0]), true);
},
tx::ipv6
);
}
*/
TORRENT_TEST(no_proxy_tcp)
{
run_test(
no_init,
[](lt::session&, lt::alert const*) {},
expect_seed(true)
);
}
TORRENT_TEST(no_proxy_utp)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{ utp_only(ses0); utp_only(ses1); },
[](lt::session&, lt::alert const*) {},
expect_seed(true)
);
}
TORRENT_TEST(encryption_utp)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
enable_enc(ses0);
enable_enc(ses1);
utp_only(ses0);
utp_only(ses1);
},
[](lt::session&, lt::alert const*) {},
expect_seed(true)
);
}
TORRENT_TEST(socks5_utp)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses0, settings_pack::socks5);
utp_only(ses0);
filter_ips(ses1);
utp_only(ses1);
},
[](lt::session&, lt::alert const*) {},
expect_seed(true)
);
}
TORRENT_TEST(socks5_utp_incoming)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses1, settings_pack::socks5);
utp_only(ses0);
utp_only(ses1);
filter_ips(ses0);
},
[](lt::session&, lt::alert const*) {},
expect_seed(true),
tx::connect_proxy
);
}
TORRENT_TEST(socks5_utp_circumvent_proxy_reject)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses1, settings_pack::socks5);
utp_only(ses0);
utp_only(ses1);
},
[](lt::session&, lt::alert const*) {},
expect_seed(false)
);
}
// if we're not proxying peer connections, it's OK to accept incoming
// connections
TORRENT_TEST(socks5_utp_circumvent_proxy_ok)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses1, settings_pack::socks5, {}, false);
utp_only(ses0);
utp_only(ses1);
},
[](lt::session&, lt::alert const*) {},
// the UDP socket socks5 proxy support doesn't allow accepting direct
// connections, circumventing the proxy, so this transfer will fail,
// even though it would be reasonable for it to pass as well
expect_seed(false)
);
}
TORRENT_TEST(http_tcp_circumvent_proxy_reject)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses1, settings_pack::http);
},
[](lt::session&, lt::alert const*) {},
expect_seed(false)
);
}
// if we're not proxying peer connections, it's OK to accept incoming
// connections
TORRENT_TEST(http_tcp_circumvent_proxy_ok)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
set_proxy(ses1, settings_pack::http, {}, false);
},
[](lt::session&, lt::alert const*) {},
expect_seed(true)
);
}
// the purpose of these tests is to make sure that the sessions can't actually
// talk directly to each other. i.e. they are negative tests. If they can talk
// directly to each other, all other tests in here may be broken.
TORRENT_TEST(no_proxy_tcp_banned)
{
run_test(
[](lt::session&, lt::session& ses1) { filter_ips(ses1); },
[](lt::session&, lt::alert const*) {},
expect_seed(false)
);
}
TORRENT_TEST(no_proxy_utp_banned)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{ utp_only(ses0); utp_only(ses1); filter_ips(ses1); },
[](lt::session&, lt::alert const*) {},
expect_seed(false)
);
}
TORRENT_TEST(piece_extent_affinity)
{
run_test(
[](lt::session& ses0, lt::session& ses1)
{
settings_pack p;
p.set_bool(settings_pack::piece_extent_affinity, true);
ses0.apply_settings(p);
ses1.apply_settings(p);
},
[](lt::session&, lt::alert const*) {},
expect_seed(true)
);
}
// When the downloader has the flag, v1 validation is skipped and the transfer
// succeeds. Without it, the downloader detects the hash mismatch and aborts.
void run_disable_v1_hashes_test(bool const enable_on_downloader)
{
bool got_inconsistent_error = false;
run_test(
no_init,
[&](lt::session&, lt::alert const* a)
{
if (auto e = lt::alert_cast<lt::torrent_error_alert>(a))
if (e->error == lt::errors::torrent_inconsistent_hashes)
got_inconsistent_error = true;
},
[&](std::shared_ptr<lt::session> ses[2])
{
TEST_EQUAL(is_seed(*ses[0]), enable_on_downloader);
TEST_EQUAL(got_inconsistent_error, !enable_on_downloader);
},
tx::bad_v1_hashes | (enable_on_downloader ? tx::disable_v1_hashes : test_transfer_flags_t{})
);
}
// setting ON + bad v1 hashes -> should succeed, v1 validation is skipped
TORRENT_TEST(disable_v1_hashes_bad_v1_enabled)
{ run_disable_v1_hashes_test(true); }
// setting OFF + bad v1 hashes -> should fail with torrent_inconsistent_hashes
TORRENT_TEST(disable_v1_hashes_bad_v1_disabled)
{ run_disable_v1_hashes_test(false); }
TORRENT_TEST(is_finished)
{
run_test(no_init
, [](lt::session& ses, lt::alert const* a) {
if (alert_cast<piece_finished_alert>(a))
{
TEST_EQUAL(is_finished(ses), false);
std::vector<download_priority_t> prio(4, dont_download);
ses.get_torrents()[0].prioritize_files(prio);
// applying the priorities is asynchronous. the torrent may not
// finish immediately
}
},
[](std::shared_ptr<lt::session> ses[2]) {
TEST_EQUAL(is_finished(*ses[0]), true);
TEST_EQUAL(is_finished(*ses[1]), true);
}
);
}
TORRENT_TEST(v1_only_magnet)
{
std::set<piece_index_t> passed;
run_test(no_init
, record_finished_pieces(passed)
, expect_seed(true)
, tx::v1_only | tx::magnet_download
);
TEST_EQUAL(passed.size(), 11);
}
TORRENT_TEST(disk_full)
{
run_test(no_init
, [](lt::session&, lt::alert const*) {}
// the disk filled up, we failed to complete the download
, expect_seed(false)
, {}
, test_disk().set_space_left(5 * lt::default_block_size)
);
}
TORRENT_TEST(disk_full_recover)
{
run_test(
[](lt::session& ses0, lt::session&)
{
settings_pack p;
p.set_int(settings_pack::optimistic_disk_retry, 30);
ses0.apply_settings(p);
},
[](lt::session&, lt::alert const* a) {
if (auto ta = alert_cast<lt::add_torrent_alert>(a))
{
// the torrent has to be auto-managed in order to automatically
// leave upload mode after it hits disk-full
ta->handle.set_flags(torrent_flags::auto_managed);
}
}
// the disk filled up, we failed to complete the download, but then the
// disk recovered and we completed it
, expect_seed(true)
, {}
, test_disk().set_space_left(10 * lt::default_block_size).set_recover_full_disk()
, test_disk()
, lt::seconds(65)
);
}
TORRENT_TEST(disk_full_recover_large_pieces)
{
run_test(
[](lt::session& ses0, lt::session&)
{
settings_pack p;
p.set_int(settings_pack::optimistic_disk_retry, 30);
ses0.apply_settings(p);
},
[](lt::session&, lt::alert const* a) {
if (auto ta = alert_cast<lt::add_torrent_alert>(a))
{
// the torrent has to be auto-managed in order to automatically
// leave upload mode after it hits disk-full
ta->handle.set_flags(torrent_flags::auto_managed);
}
}
// the disk filled up, we failed to complete the download, but then the
// disk recovered and we completed it
, expect_seed(true)
, tx::large_pieces
, test_disk().set_space_left(10 * lt::default_block_size).set_recover_full_disk()
, test_disk()
, lt::seconds(70)
);
}
// Below is a series of tests to transfer torrents with varying pad-file related
// traits
void run_torrent_test(lt::add_torrent_params atp)
{
using asio::ip::address;
address peer0 = addr("50.0.0.1");
address peer1 = addr("50.0.0.2");
// setup the simulation
sim::default_config network_cfg;
sim::simulation sim{network_cfg};
sim::asio::io_context ios0 { sim, peer0 };
sim::asio::io_context ios1 { sim, peer1 };
lt::session_proxy zombie[2];
lt::session_params params;
// setup settings pack to use for the session (customization point)
lt::settings_pack& pack = params.settings;
pack = settings();
pack.set_bool(settings_pack::disable_hash_checks, false);
// disable utp by default
pack.set_bool(settings_pack::enable_outgoing_utp, false);
pack.set_bool(settings_pack::enable_incoming_utp, false);
// disable encryption by default
pack.set_bool(settings_pack::prefer_rc4, false);
pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled);
pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled);
pack.set_int(settings_pack::allowed_enc_level, settings_pack::pe_plaintext);
pack.set_str(settings_pack::listen_interfaces, "50.0.0.1:6881");
// create session
std::shared_ptr<lt::session> ses[2];
// session 0 is a downloader, session 1 is a seed
params.disk_io_constructor = test_disk();
ses[0] = std::make_shared<lt::session>(params, ios0);
pack.set_str(settings_pack::listen_interfaces, "50.0.0.2:6881");
params.disk_io_constructor = test_disk().set_files(existing_files_mode::full_valid);
ses[1] = std::make_shared<lt::session>(params, ios1);
// only monitor alerts for session 0 (the downloader)
print_alerts(*ses[0], [=](lt::session& ses, lt::alert const* a) {
if (auto ta = alert_cast<lt::add_torrent_alert>(a))
{
ta->handle.connect_peer(lt::tcp::endpoint(peer1, 6881));
}
}, 0);
print_alerts(*ses[1], [](lt::session&, lt::alert const*){}, 1);
atp.save_path = ".";
atp.flags &= ~lt::torrent_flags::auto_managed;
atp.flags &= ~lt::torrent_flags::paused;
ses[1]->async_add_torrent(atp);
ses[0]->async_add_torrent(atp);
sim::timer t(sim, lt::seconds(10), [&](boost::system::error_code const&)
{
auto h = ses[0]->get_torrents();
#if TORRENT_ABI_VERSION < 4
auto ti = h[0].torrent_file_with_hashes();
if (ti->v2())
TEST_EQUAL(ti->v2_piece_hashes_verified(), true);
auto downloaded = serialize(*ti);
auto added = serialize(atp);
TEST_CHECK(downloaded == added);
#endif
TEST_CHECK(is_seed(*ses[0]));
TEST_CHECK(is_seed(*ses[1]));
h[0].force_recheck();
});
sim::timer t2(sim, lt::minutes(1), [&](boost::system::error_code const&)
{
// shut down
int idx = 0;
for (auto& s : ses)
{
zombie[idx++] = s->abort();
s.reset();
}
});
sim.run();
}
namespace {
lt::add_torrent_params test_torrent(std::vector<lt::create_file_entry> fs
, int const piece_size, lt::create_flags_t const flags)
{
lt::create_torrent ct(std::move(fs), piece_size, flags);
lt::settings_pack pack;
lt::error_code ec;
lt::set_piece_hashes(ct, "", pack, test_disk().set_files(existing_files_mode::full_valid)
, [](lt::piece_index_t p) { std::cout << "."; std::cout.flush();}, ec);
return lt::load_torrent_buffer(lt::bencode(ct.generate()));
}
}
TORRENT_TEST(simple_torrent)
{
run_torrent_test(test_torrent(make_files(
{{0x3ff0, false}, {0x10, true}}), 0x4000, {}));
}
TORRENT_TEST(odd_last_pad_file)
{
run_torrent_test(test_torrent(make_files(
{{0x4100, false}, {0x10, true}}), 0x4000, {}));
}
TORRENT_TEST(small_piece_size)
{
run_torrent_test(test_torrent(make_files(
{{0x3ff0, false}, {0x10, true}}), 0x2000, {}));
}
TORRENT_TEST(odd_piece_size)
{
run_torrent_test(test_torrent(make_files(
{{0x1ffe, false}, {0x1, true}}), 0x1fff, {}));
}
TORRENT_TEST(large_pad_file)
{
run_torrent_test(test_torrent(make_files(
{{0x5000, false}, {0x100000000 - 0x5000, true}}), 0x100000, {}));
}
TORRENT_TEST(unaligned_pad_file)
{
run_torrent_test(test_torrent(make_files(
{{0x3fff, false}, {0x10, true}}), 0x4000, {}));
}
TORRENT_TEST(piece_size_pad_file)
{
run_torrent_test(test_torrent(make_files(
{{0x8000, false}, {0x8000, true}}), 0x8000, {}));
}
TORRENT_TEST(block_size_pad_file)
{
run_torrent_test(test_torrent(make_files(
{{0x4000, false}, {0x4000, true}}), 0x4000, {}));
}
TORRENT_TEST(back_to_back_pad_file)
{
run_torrent_test(test_torrent(make_files(
{{0x3000, false}, {0x800, true}, {0x800, true}}), 0x4000, {}));
}
TORRENT_TEST(small_file_large_piece)
{
run_torrent_test(test_torrent(make_files(
{{0x833ed, false}, {0x7cc13, true}, {0x3d, false}, {0x7ffc3, true}, {0x14000, false}}), 0x80000, {}));
}
TORRENT_TEST(empty_file)
{
run_torrent_test(test_torrent(make_files(
{{0x3000, false}, {0, false}, {0x8000, false}}), 0x4000, {}));
}