merged RC_1_2 into RC_2_0
This commit is contained in:
@ -53,6 +53,7 @@
|
||||
* libtorrent now requires C++14 to build
|
||||
* added support for GnuTLS for HTTPS and torrents over SSL
|
||||
|
||||
* fix issue with paths starting with ./
|
||||
* fix integer overflow when setting a high DHT upload rate limit
|
||||
* improve Path MTU discovery logic in uTP
|
||||
* fix overflow issue when rlimit_nofile is set to infinity
|
||||
|
@ -107,8 +107,6 @@ namespace {
|
||||
ec.assign(value, boost::asio::error::get_addrinfo_category());
|
||||
else if (category == "asio.misc")
|
||||
ec.assign(value, boost::asio::error::get_misc_category());
|
||||
else if (category == "asio.misc")
|
||||
ec.assign(value, boost::asio::error::get_misc_category());
|
||||
#if TORRENT_USE_SSL
|
||||
else if (category == "asio.ssl")
|
||||
ec.assign(value, boost::asio::error::get_ssl_category());
|
||||
|
@ -570,6 +570,8 @@ CANCELIO
|
||||
fd
|
||||
socketpair
|
||||
fileno
|
||||
SSRF
|
||||
IDNA
|
||||
destructors
|
||||
fclose
|
||||
fopen
|
||||
|
@ -1,2 +0,0 @@
|
||||
convert $1 -resize 150x130 $1
|
||||
|
@ -352,7 +352,6 @@ namespace errors {
|
||||
deprecated_124,
|
||||
#endif
|
||||
|
||||
|
||||
// The resume data file is missing the ``file sizes`` entry
|
||||
missing_file_sizes = 130,
|
||||
// The resume data file ``file sizes`` entry is empty
|
||||
@ -453,6 +452,10 @@ namespace errors {
|
||||
|
||||
// random number generation failed
|
||||
no_entropy = 200,
|
||||
// blocked by SSRF mitigation
|
||||
ssrf_mitigation,
|
||||
// blocked because IDNA host names are banned
|
||||
blocked_by_idna,
|
||||
|
||||
// the torrent file has an unknown meta version
|
||||
torrent_unknown_version = 210,
|
||||
|
@ -56,4 +56,5 @@ run test_fast_extensions.cpp ;
|
||||
#run test_file_pool.cpp ;
|
||||
run test_save_resume.cpp ;
|
||||
run test_error_handling.cpp ;
|
||||
run test_timeout.cpp ;
|
||||
|
||||
|
@ -105,6 +105,19 @@ struct fake_peer
|
||||
lt::aux::write_uint8(2, ptr);
|
||||
}
|
||||
|
||||
void send_request(lt::piece_index_t p, int block)
|
||||
{
|
||||
int const len = 4 + 1 + 4 * 3;
|
||||
m_send_buffer.resize(m_send_buffer.size() + len);
|
||||
char* ptr = m_send_buffer.data() + m_send_buffer.size() - len;
|
||||
|
||||
lt::aux::write_uint32(len - 4, ptr);
|
||||
lt::aux::write_uint8(6, ptr);
|
||||
lt::aux::write_uint32(static_cast<int>(p), ptr);
|
||||
lt::aux::write_uint32(block * 0x4000, ptr);
|
||||
lt::aux::write_uint32(0x4000, ptr);
|
||||
}
|
||||
|
||||
void send_bitfield(std::vector<bool> const& pieces)
|
||||
{
|
||||
int const bytes = (int(pieces.size()) + 7) / 8;
|
||||
@ -208,12 +221,12 @@ private:
|
||||
, std::bind(&fake_peer::on_read, this, _1, _2));
|
||||
}
|
||||
|
||||
void on_read(lt::error_code const& ec, size_t /* bytes_transferred */)
|
||||
void on_read(lt::error_code const& ec, size_t bytes_transferred)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
|
||||
std::printf("fake_peer::on_read -> (%d) %s\n"
|
||||
, ec.value(), ec.message().c_str());
|
||||
std::printf("fake_peer::on_read(%d bytes) -> (%d) %s\n"
|
||||
, int(bytes_transferred), ec.value(), ec.message().c_str());
|
||||
if (ec)
|
||||
{
|
||||
std::printf(" closing\n");
|
||||
|
@ -75,7 +75,7 @@ namespace {
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
typedef sim::chrono::high_resolution_clock::duration duration;
|
||||
using duration = sim::chrono::high_resolution_clock::duration;
|
||||
using sim::chrono::milliseconds;
|
||||
|
||||
dsl_config::dsl_config(int kb_per_second, int send_queue_size)
|
||||
|
152
simulation/test_timeout.cpp
Normal file
152
simulation/test_timeout.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2021, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/torrent_handle.hpp"
|
||||
#include "libtorrent/settings_pack.hpp"
|
||||
#include "libtorrent/alert_types.hpp"
|
||||
#include "libtorrent/deadline_timer.hpp"
|
||||
#include "libtorrent/disabled_disk_io.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "fake_peer.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "setup_transfer.hpp"
|
||||
#include "create_torrent.hpp"
|
||||
#include "simulator/simulator.hpp"
|
||||
#include "simulator/utils.hpp"
|
||||
#include "simulator/queue.hpp"
|
||||
|
||||
using namespace sim;
|
||||
using namespace lt;
|
||||
|
||||
using disconnects_t = std::vector<std::pair<lt::seconds, lt::error_code>>;
|
||||
|
||||
disconnects_t test_timeout(sim::configuration& cfg)
|
||||
{
|
||||
auto const start_time = lt::clock_type::now();
|
||||
sim::simulation sim{cfg};
|
||||
std::unique_ptr<sim::asio::io_context> ios = make_io_context(sim, 0);
|
||||
lt::session_proxy zombie;
|
||||
|
||||
// setup settings pack to use for the session (customization point)
|
||||
lt::session_params sp;
|
||||
sp.settings = settings();
|
||||
sp.settings.set_int(settings_pack::alert_mask, alert_category::all & ~alert_category::stats);
|
||||
sp.settings.set_bool(settings_pack::disable_hash_checks, true);
|
||||
sp.disk_io_constructor = lt::disabled_disk_io_constructor;
|
||||
|
||||
// create session
|
||||
std::shared_ptr<lt::session> ses = std::make_shared<lt::session>(sp, *ios);
|
||||
|
||||
fake_peer p1(sim, "60.0.0.0");
|
||||
|
||||
// add torrent
|
||||
lt::add_torrent_params params = ::create_torrent(0, false);
|
||||
params.flags &= ~lt::torrent_flags::auto_managed;
|
||||
params.flags &= ~lt::torrent_flags::paused;
|
||||
params.flags |= lt::torrent_flags::seed_mode;
|
||||
lt::sha1_hash info_hash = params.ti->info_hash();
|
||||
ses->async_add_torrent(std::move(params));
|
||||
|
||||
disconnects_t disconnects;
|
||||
|
||||
lt::torrent_handle h;
|
||||
print_alerts(*ses, [&](lt::session& ses, lt::alert const* a) {
|
||||
if (auto* at = lt::alert_cast<add_torrent_alert>(a))
|
||||
{
|
||||
h = at->handle;
|
||||
|
||||
p1.connect_to(ep("50.0.0.1", 6881), info_hash);
|
||||
p1.send_interested();
|
||||
p1.send_request(piece_index_t{0}, 0);
|
||||
}
|
||||
else if (auto* pd = lt::alert_cast<peer_disconnected_alert>(a))
|
||||
{
|
||||
disconnects.emplace_back(duration_cast<lt::seconds>(pd->timestamp() - start_time), pd->error);
|
||||
}
|
||||
});
|
||||
|
||||
// set up a timer to fire later, to shut down
|
||||
sim::timer t2(sim, lt::seconds(400)
|
||||
, [&](boost::system::error_code const&)
|
||||
{
|
||||
// shut down
|
||||
zombie = ses->abort();
|
||||
ses.reset();
|
||||
});
|
||||
|
||||
sim.run();
|
||||
|
||||
return disconnects;
|
||||
}
|
||||
|
||||
// the inactive timeout is 60 seconds. If we don't receive a request from a peer
|
||||
// that's interested in us for 60 seconds, we disconnect them.
|
||||
TORRENT_TEST(inactive_timeout)
|
||||
{
|
||||
sim::default_config network_cfg;
|
||||
auto disconnects = test_timeout(network_cfg);
|
||||
TEST_CHECK((disconnects == disconnects_t{{lt::seconds{60}, lt::errors::timed_out_no_request}}));
|
||||
}
|
||||
|
||||
struct slow_upload : sim::default_config
|
||||
{
|
||||
sim::route outgoing_route(asio::ip::address ip) override
|
||||
{
|
||||
// only affect the libtorrent instance, not the fake peer
|
||||
if (ip != addr("50.0.0.1")) return sim::default_config::outgoing_route(ip);
|
||||
|
||||
int const rate = 1;
|
||||
|
||||
using duration = sim::chrono::high_resolution_clock::duration;
|
||||
|
||||
auto it = m_outgoing.find(ip);
|
||||
if (it != m_outgoing.end()) return sim::route().append(it->second);
|
||||
it = m_outgoing.insert(it, std::make_pair(ip, std::make_shared<queue>(
|
||||
std::ref(m_sim->get_io_context())
|
||||
, rate * 1000
|
||||
, lt::duration_cast<duration>(milliseconds(rate / 2))
|
||||
, 200 * 1000, "slow upload rate")));
|
||||
return sim::route().append(it->second);
|
||||
}
|
||||
};
|
||||
|
||||
// if the upload capacity is so low, that we're still trying to respond to the
|
||||
// last request, we don't trugger the inactivity timeout, we don't expect the
|
||||
// other peer to keep requesting more pieces before receiving the previous ones
|
||||
TORRENT_TEST(inactive_timeout_slow_upload)
|
||||
{
|
||||
slow_upload cfg;
|
||||
auto disconnects = test_timeout(cfg);
|
||||
TEST_CHECK((disconnects == disconnects_t{{lt::seconds{73}, lt::errors::timed_out_no_request}}));
|
||||
}
|
||||
|
@ -276,8 +276,8 @@ namespace libtorrent {
|
||||
"", "", "", "", "",
|
||||
#endif
|
||||
"random number generator failed",
|
||||
"",
|
||||
"",
|
||||
"blocked by SSRF mitigation",
|
||||
"blocked by IDNA ban",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
|
@ -150,7 +150,7 @@ void http_connection::get(std::string const& url, time_duration timeout, int pri
|
||||
|
||||
if (m_hostname_filter_handler && !m_hostname_filter_handler(*this, hostname))
|
||||
{
|
||||
error_code err(errors::banned_by_ip_filter);
|
||||
error_code err(errors::blocked_by_idna);
|
||||
post(m_ios, std::bind(&http_connection::callback
|
||||
, me, err, span<char>{}));
|
||||
return;
|
||||
|
@ -110,7 +110,7 @@ namespace libtorrent {
|
||||
bool const ssrf_mitigation = settings.get_bool(settings_pack::ssrf_mitigation);
|
||||
if (ssrf_mitigation && has_tracker_query_string(string_view(url).substr(arguments_start + 1)))
|
||||
{
|
||||
fail(errors::banned_by_ip_filter, operation_t::bittorrent);
|
||||
fail(errors::ssrf_mitigation, operation_t::bittorrent);
|
||||
return;
|
||||
}
|
||||
url += "&";
|
||||
@ -342,7 +342,7 @@ namespace libtorrent {
|
||||
|
||||
if (endpoints.empty())
|
||||
{
|
||||
fail(errors::banned_by_ip_filter, operation_t::bittorrent);
|
||||
fail(errors::ssrf_mitigation, operation_t::bittorrent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1055,7 +1055,8 @@ namespace {
|
||||
std::string complete(string_view f)
|
||||
{
|
||||
if (is_complete(f)) return f.to_string();
|
||||
if (f == ".") return current_working_directory();
|
||||
auto parts = lsplit_path(f);
|
||||
if (parts.first == ".") f = parts.second;
|
||||
return combine_path(current_working_directory(), f);
|
||||
}
|
||||
|
||||
|
@ -1151,6 +1151,7 @@ namespace libtorrent {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (bytes_payload > 0) m_last_sent_payload.set(m_connect, clock_type::now());
|
||||
if (m_ignore_stats) return;
|
||||
std::shared_ptr<torrent> t = m_torrent.lock();
|
||||
if (!t) return;
|
||||
@ -4902,16 +4903,19 @@ namespace libtorrent {
|
||||
}
|
||||
}
|
||||
|
||||
// if we can't read, it means we're blocked on the rate-limiter
|
||||
// or the disk, not the peer itself. In this case, don't blame
|
||||
// the peer and disconnect it
|
||||
bool const may_timeout = bool(m_channel_state[download_channel] & peer_info::bw_network);
|
||||
// if the bw_network flag isn't set, it means we are not even trying to
|
||||
// read from this peer's socket. Most likely because we're applying a
|
||||
// rate limit. If the peer is "slow" because we are rate limiting it,
|
||||
// don't enforce timeouts. However, as soon as we *do* read from the
|
||||
// socket, we expect to receive data, and not have timed out. Then we
|
||||
// can enforce the timeouts.
|
||||
bool const reading_socket = bool(m_channel_state[download_channel] & peer_info::bw_network);
|
||||
|
||||
// TODO: 2 use a deadline_timer for timeouts. Don't rely on second_tick()!
|
||||
// Hook this up to connect timeout as well. This would improve performance
|
||||
// because of less work in second_tick(), and might let use remove ticking
|
||||
// entirely eventually
|
||||
if (may_timeout && d > seconds(timeout()) && !m_connecting && m_reading_bytes == 0
|
||||
if (reading_socket && d > seconds(timeout()) && !m_connecting && m_reading_bytes == 0
|
||||
&& can_disconnect(errors::timed_out_inactivity))
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
@ -4927,7 +4931,7 @@ namespace libtorrent {
|
||||
#if TORRENT_USE_I2P
|
||||
timeout *= is_i2p(m_socket) ? 4 : 1;
|
||||
#endif
|
||||
if (may_timeout
|
||||
if (reading_socket
|
||||
&& !m_connecting
|
||||
&& in_handshake()
|
||||
&& d > seconds(timeout))
|
||||
@ -4948,7 +4952,7 @@ namespace libtorrent {
|
||||
, m_last_incoming_request.get(m_connect))
|
||||
, m_last_sent_payload.get(m_connect));
|
||||
|
||||
if (may_timeout
|
||||
if (reading_socket
|
||||
&& !m_connecting
|
||||
&& m_requests.empty()
|
||||
&& m_reading_bytes == 0
|
||||
@ -4977,7 +4981,7 @@ namespace libtorrent {
|
||||
// don't bother disconnect peers we haven't been interested
|
||||
// in (and that hasn't been interested in us) for a while
|
||||
// unless we have used up all our connection slots
|
||||
if (may_timeout
|
||||
if (reading_socket
|
||||
&& !m_interesting
|
||||
&& !m_peer_interested
|
||||
&& d1 > time_limit
|
||||
@ -4997,7 +5001,7 @@ namespace libtorrent {
|
||||
return;
|
||||
}
|
||||
|
||||
if (may_timeout
|
||||
if (reading_socket
|
||||
&& !m_download_queue.empty()
|
||||
&& m_quota[download_channel] > 0
|
||||
&& now > m_requested.get(m_connect) + seconds(request_timeout()))
|
||||
|
@ -6077,7 +6077,7 @@ namespace {
|
||||
if (m_ses.alerts().should_post<url_seed_alert>())
|
||||
{
|
||||
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
||||
, web->url, error_code(errors::banned_by_ip_filter));
|
||||
, web->url, error_code(errors::blocked_by_idna));
|
||||
}
|
||||
// never try it again
|
||||
remove_web_seed_iter(web);
|
||||
@ -6459,7 +6459,7 @@ namespace {
|
||||
if (m_ses.alerts().should_post<url_seed_alert>())
|
||||
{
|
||||
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
||||
, web->url, error_code(errors::banned_by_ip_filter));
|
||||
, web->url, error_code(errors::blocked_by_idna));
|
||||
}
|
||||
// never try it again
|
||||
remove_web_seed_iter(web);
|
||||
@ -6481,7 +6481,7 @@ namespace {
|
||||
#endif
|
||||
if (m_ses.alerts().should_post<url_seed_alert>())
|
||||
m_ses.alerts().emplace_alert<url_seed_alert>(get_handle()
|
||||
, web->url, errors::banned_by_ip_filter);
|
||||
, web->url, errors::ssrf_mitigation);
|
||||
if (m_ses.alerts().should_post<peer_blocked_alert>())
|
||||
m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle()
|
||||
, a, peer_blocked_alert::ssrf_mitigation);
|
||||
|
@ -263,6 +263,12 @@ TORRENT_TEST(paths)
|
||||
#endif
|
||||
|
||||
TEST_EQUAL(complete("."), current_working_directory());
|
||||
|
||||
#ifdef TORRENT_WINDOWS
|
||||
TEST_EQUAL(complete(".\\foobar"), current_working_directory() + "\\foobar");
|
||||
#else
|
||||
TEST_EQUAL(complete("./foobar"), current_working_directory() + "/foobar");
|
||||
#endif
|
||||
}
|
||||
|
||||
TORRENT_TEST(path_compare)
|
||||
|
@ -76,6 +76,8 @@ TORRENT_TEST(error_code)
|
||||
TEST_CHECK(error_code(errors::no_i2p_router).message() == "no i2p router is set up");
|
||||
TEST_CHECK(error_code(errors::http_parse_error).message() == "Invalid HTTP header");
|
||||
TEST_CHECK(error_code(errors::error_code_max).message() == "Unknown error");
|
||||
TEST_CHECK(error_code(errors::ssrf_mitigation).message() == "blocked by SSRF mitigation");
|
||||
TEST_CHECK(error_code(errors::blocked_by_idna).message() == "blocked by IDNA ban");
|
||||
|
||||
TEST_CHECK(error_code(errors::torrent_inconsistent_hashes).message() == "v1 and v2 hashes do not describe the same data");
|
||||
|
||||
|
Reference in New Issue
Block a user