/src/libtorrent/src/torrent.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | |
3 | | Copyright (c) 2003-2022, Arvid Norberg |
4 | | Copyright (c) 2003, Daniel Wallin |
5 | | Copyright (c) 2004, Magnus Jonsson |
6 | | Copyright (c) 2008, Andrew Resch |
7 | | Copyright (c) 2015, Mikhail Titov |
8 | | Copyright (c) 2015-2020, Steven Siloti |
9 | | Copyright (c) 2016-2020, Alden Torres |
10 | | Copyright (c) 2016-2017, Andrei Kurushin |
11 | | Copyright (c) 2016, Jonathan McDougall |
12 | | Copyright (c) 2016-2018, Pavel Pimenov |
13 | | Copyright (c) 2017, 2020, AllSeeingEyeTolledEweSew |
14 | | Copyright (c) 2017, Falcosc |
15 | | Copyright (c) 2017, ximply |
16 | | Copyright (c) 2018, Fernando Rodriguez |
17 | | Copyright (c) 2018, airium |
18 | | Copyright (c) 2018, d-komarov |
19 | | Copyright (c) 2020, Paul-Louis Ageneau |
20 | | Copyright (c) 2021, AdvenT |
21 | | Copyright (c) 2021, Joris CARRIER |
22 | | Copyright (c) 2021, thrnz |
23 | | Copyright (c) 2024, Elyas EL IDRISSI |
24 | | All rights reserved. |
25 | | |
26 | | Redistribution and use in source and binary forms, with or without |
27 | | modification, are permitted provided that the following conditions |
28 | | are met: |
29 | | |
30 | | * Redistributions of source code must retain the above copyright |
31 | | notice, this list of conditions and the following disclaimer. |
32 | | * Redistributions in binary form must reproduce the above copyright |
33 | | notice, this list of conditions and the following disclaimer in |
34 | | the documentation and/or other materials provided with the distribution. |
35 | | * Neither the name of the author nor the names of its |
36 | | contributors may be used to endorse or promote products derived |
37 | | from this software without specific prior written permission. |
38 | | |
39 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
40 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
41 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
42 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
43 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
44 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
45 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
46 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
47 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
48 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
49 | | POSSIBILITY OF SUCH DAMAGE. |
50 | | |
51 | | */ |
52 | | |
53 | | #include "libtorrent/config.hpp" |
54 | | |
55 | | #include <cstdarg> // for va_list |
56 | | #include <ctime> |
57 | | #include <algorithm> |
58 | | #include <set> |
59 | | #include <map> |
60 | | #include <vector> |
61 | | #include <cctype> |
62 | | #include <numeric> |
63 | | #include <limits> // for numeric_limits |
64 | | #include <cstdio> // for snprintf |
65 | | #include <functional> |
66 | | |
67 | | #include "libtorrent/torrent.hpp" |
68 | | #include "libtorrent/torrent_handle.hpp" |
69 | | #include "libtorrent/announce_entry.hpp" |
70 | | #include "libtorrent/torrent_info.hpp" |
71 | | #include "libtorrent/tracker_manager.hpp" |
72 | | #include "libtorrent/parse_url.hpp" |
73 | | #include "libtorrent/bencode.hpp" |
74 | | #include "libtorrent/hasher.hpp" |
75 | | #include "libtorrent/entry.hpp" |
76 | | #include "libtorrent/peer.hpp" |
77 | | #include "libtorrent/peer_connection.hpp" |
78 | | #include "libtorrent/bt_peer_connection.hpp" |
79 | | #include "libtorrent/web_peer_connection.hpp" |
80 | | #include "libtorrent/http_seed_connection.hpp" |
81 | | #include "libtorrent/peer_connection_handle.hpp" |
82 | | #include "libtorrent/peer_id.hpp" |
83 | | #include "libtorrent/identify_client.hpp" |
84 | | #include "libtorrent/alert_types.hpp" |
85 | | #include "libtorrent/extensions.hpp" |
86 | | #include "libtorrent/aux_/session_interface.hpp" |
87 | | #include "libtorrent/aux_/instantiate_connection.hpp" |
88 | | #include "libtorrent/assert.hpp" |
89 | | #include "libtorrent/kademlia/dht_tracker.hpp" |
90 | | #include "libtorrent/peer_info.hpp" |
91 | | #include "libtorrent/http_connection.hpp" |
92 | | #include "libtorrent/random.hpp" |
93 | | #include "libtorrent/peer_class.hpp" // for peer_class |
94 | | #include "libtorrent/socket_io.hpp" // for read_*_endpoint |
95 | | #include "libtorrent/ip_filter.hpp" |
96 | | #include "libtorrent/request_blocks.hpp" |
97 | | #include "libtorrent/performance_counters.hpp" // for counters |
98 | | #include "libtorrent/aux_/resolver_interface.hpp" |
99 | | #include "libtorrent/aux_/alloca.hpp" |
100 | | #include "libtorrent/resolve_links.hpp" |
101 | | #include "libtorrent/aux_/file_progress.hpp" |
102 | | #include "libtorrent/aux_/has_block.hpp" |
103 | | #include "libtorrent/aux_/alert_manager.hpp" |
104 | | #include "libtorrent/disk_interface.hpp" |
105 | | #include "libtorrent/aux_/ip_helpers.hpp" // for is_ip_address |
106 | | #include "libtorrent/download_priority.hpp" |
107 | | #include "libtorrent/hex.hpp" // to_hex |
108 | | #include "libtorrent/aux_/range.hpp" |
109 | | #include "libtorrent/aux_/merkle.hpp" |
110 | | #include "libtorrent/mmap_disk_io.hpp" // for hasher_thread_divisor |
111 | | #include "libtorrent/aux_/numeric_cast.hpp" |
112 | | #include "libtorrent/aux_/path.hpp" |
113 | | #include "libtorrent/aux_/generate_peer_id.hpp" |
114 | | #include "libtorrent/aux_/announce_entry.hpp" |
115 | | #include "libtorrent/ssl.hpp" |
116 | | #include "libtorrent/aux_/apply_pad_files.hpp" |
117 | | |
118 | | #ifdef TORRENT_SSL_PEERS |
119 | | #include "libtorrent/ssl_stream.hpp" |
120 | | #endif // TORRENT_SSL_PEERS |
121 | | |
122 | | #ifndef TORRENT_DISABLE_LOGGING |
123 | | #include "libtorrent/aux_/session_impl.hpp" // for tracker_logger |
124 | | #endif |
125 | | |
126 | | #include "libtorrent/aux_/torrent_impl.hpp" |
127 | | |
128 | | using namespace std::placeholders; |
129 | | |
130 | | namespace libtorrent { |
131 | | namespace { |
132 | | |
133 | | bool is_downloading_state(int const st) |
134 | 2.72k | { |
135 | 2.72k | switch (st) |
136 | 2.72k | { |
137 | 65 | case torrent_status::checking_files: |
138 | 1.99k | case torrent_status::checking_resume_data: |
139 | 1.99k | return false; |
140 | 0 | case torrent_status::downloading_metadata: |
141 | 466 | case torrent_status::downloading: |
142 | 728 | case torrent_status::finished: |
143 | 728 | case torrent_status::seeding: |
144 | 728 | return true; |
145 | 0 | default: |
146 | | // unexpected state |
147 | 0 | TORRENT_ASSERT_FAIL_VAL(st); |
148 | 0 | return false; |
149 | 2.72k | } |
150 | 2.72k | } |
151 | | } // anonymous namespace |
152 | | |
153 | | constexpr web_seed_flag_t torrent::ephemeral; |
154 | | constexpr web_seed_flag_t torrent::no_local_ips; |
155 | | |
156 | | web_seed_t::web_seed_t(web_seed_entry const& wse) |
157 | 0 | : web_seed_entry(wse) |
158 | 0 | { |
159 | 0 | peer_info.web_seed = true; |
160 | 0 | } |
161 | | |
162 | | web_seed_t::web_seed_t(std::string const& url_, web_seed_entry::type_t type_ |
163 | | , std::string const& auth_ |
164 | | , web_seed_entry::headers_t const& extra_headers_) |
165 | 0 | : web_seed_entry(url_, type_, auth_, extra_headers_) |
166 | 0 | { |
167 | 0 | peer_info.web_seed = true; |
168 | 0 | } |
169 | | |
170 | | torrent_hot_members::torrent_hot_members(aux::session_interface& ses |
171 | | , add_torrent_params const& p, bool const session_paused) |
172 | 1.88k | : m_ses(ses) |
173 | 1.88k | , m_complete(0xffffff) |
174 | 1.88k | , m_upload_mode(p.flags & torrent_flags::upload_mode) |
175 | 1.88k | , m_connections_initialized(false) |
176 | 1.88k | , m_abort(false) |
177 | 1.88k | , m_paused(p.flags & torrent_flags::paused) |
178 | 1.88k | , m_session_paused(session_paused) |
179 | | #ifndef TORRENT_DISABLE_SHARE_MODE |
180 | 1.88k | , m_share_mode(p.flags & torrent_flags::share_mode) |
181 | | #endif |
182 | 1.88k | , m_have_all(false) |
183 | 1.88k | , m_graceful_pause_mode(false) |
184 | 1.88k | , m_state_subscription(p.flags & torrent_flags::update_subscribe) |
185 | 1.88k | , m_max_connections(0xffffff) |
186 | 1.88k | , m_state(torrent_status::checking_resume_data) |
187 | 1.88k | {} |
188 | | |
189 | | torrent::torrent( |
190 | | aux::session_interface& ses |
191 | | , bool const session_paused |
192 | | , add_torrent_params&& p) |
193 | 1.88k | : torrent_hot_members(ses, p, session_paused) |
194 | 1.88k | , m_total_uploaded(p.total_uploaded) |
195 | 1.88k | , m_total_downloaded(p.total_downloaded) |
196 | 1.88k | , m_tracker_timer(ses.get_context()) |
197 | 1.88k | , m_inactivity_timer(ses.get_context()) |
198 | 1.88k | , m_trackerid(p.trackerid) |
199 | 1.88k | , m_save_path(complete(p.save_path)) |
200 | 1.88k | , m_stats_counters(ses.stats_counters()) |
201 | 1.88k | , m_added_time(p.added_time ? p.added_time : aux::posix_time()) |
202 | 1.88k | , m_completed_time(p.completed_time) |
203 | 1.88k | , m_last_seen_complete(p.last_seen_complete) |
204 | 1.88k | , m_swarm_last_seen_complete(p.last_seen_complete) |
205 | 1.88k | , m_info_hash(p.info_hashes) |
206 | 1.88k | , m_error_file(torrent_status::error_file_none) |
207 | 1.88k | , m_sequence_number(-1) |
208 | 1.88k | , m_peer_id(aux::generate_peer_id(settings())) |
209 | 1.88k | , m_announce_to_trackers(!(p.flags & torrent_flags::paused)) |
210 | 1.88k | , m_announce_to_lsd(!(p.flags & torrent_flags::paused)) |
211 | 1.88k | , m_has_incoming(false) |
212 | 1.88k | , m_files_checked(false) |
213 | 1.88k | , m_storage_mode(p.storage_mode) |
214 | 1.88k | , m_announcing(false) |
215 | 1.88k | , m_added(false) |
216 | 1.88k | , m_sequential_download(p.flags & torrent_flags::sequential_download) |
217 | 1.88k | , m_auto_sequential(false) |
218 | 1.88k | , m_seed_mode(false) |
219 | | #ifndef TORRENT_DISABLE_SUPERSEEDING |
220 | 1.88k | , m_super_seeding(p.flags & torrent_flags::super_seeding) |
221 | | #endif |
222 | 1.88k | , m_stop_when_ready(p.flags & torrent_flags::stop_when_ready) |
223 | 1.88k | , m_enable_dht(!bool(p.flags & torrent_flags::disable_dht)) |
224 | 1.88k | , m_enable_lsd(!bool(p.flags & torrent_flags::disable_lsd)) |
225 | 1.88k | , m_i2p(bool(p.flags & torrent_flags::i2p_torrent)) |
226 | 1.88k | , m_max_uploads((1 << 24) - 1) |
227 | 1.88k | , m_num_uploads(0) |
228 | 1.88k | , m_enable_pex(!bool(p.flags & torrent_flags::disable_pex)) |
229 | 1.88k | , m_apply_ip_filter(p.flags & torrent_flags::apply_ip_filter) |
230 | 1.88k | , m_pending_active_change(false) |
231 | 1.88k | , m_v2_piece_layers_validated(false) |
232 | 1.88k | , m_connect_boost_counter(static_cast<std::uint8_t>(settings().get_int(settings_pack::torrent_connect_boost))) |
233 | 1.88k | , m_incomplete(0xffffff) |
234 | 1.88k | , m_announce_to_dht(!(p.flags & torrent_flags::paused)) |
235 | 1.88k | , m_ssl_torrent(false) |
236 | 1.88k | , m_deleted(false) |
237 | 1.88k | , m_last_download(aux::from_time_t(p.last_download)) |
238 | 1.88k | , m_last_upload(aux::from_time_t(p.last_upload)) |
239 | 1.88k | , m_userdata(p.userdata) |
240 | 1.88k | , m_auto_managed(p.flags & torrent_flags::auto_managed) |
241 | 1.88k | , m_current_gauge_state(static_cast<std::uint32_t>(no_gauge_state)) |
242 | 1.88k | , m_moving_storage(false) |
243 | 1.88k | , m_inactive(false) |
244 | 1.88k | , m_downloaded(0xffffff) |
245 | 1.88k | , m_progress_ppm(0) |
246 | 1.88k | , m_torrent_initialized(false) |
247 | 1.88k | , m_outstanding_file_priority(false) |
248 | 1.88k | , m_complete_sent(false) |
249 | 1.88k | { |
250 | 1.88k | if (p.flags & torrent_flags::need_save_resume) |
251 | 862 | { |
252 | 862 | m_need_save_resume_data |= torrent_handle::only_if_modified |
253 | 862 | | torrent_handle::if_metadata_changed; |
254 | 862 | } |
255 | | |
256 | | // we cannot log in the constructor, because it relies on shared_from_this |
257 | | // being initialized, which happens after the constructor returns. |
258 | | |
259 | | #if TORRENT_USE_UNC_PATHS |
260 | | m_save_path = canonicalize_path(m_save_path); |
261 | | #endif |
262 | | |
263 | 1.88k | if (!m_apply_ip_filter) |
264 | 1.27k | { |
265 | 1.27k | inc_stats_counter(counters::non_filter_torrents); |
266 | 1.27k | } |
267 | | |
268 | 1.88k | if (!m_torrent_file) |
269 | 1.88k | m_torrent_file = (p.ti ? p.ti : std::make_shared<torrent_info>(m_info_hash)); |
270 | | |
271 | 1.88k | #if TORRENT_USE_I2P |
272 | 1.88k | if (m_torrent_file->is_i2p()) |
273 | 0 | m_i2p = true; |
274 | 1.88k | #endif |
275 | | |
276 | 1.88k | if (m_torrent_file->is_valid()) |
277 | 1.88k | { |
278 | 1.88k | error_code ec = initialize_merkle_trees(); |
279 | 1.88k | if (ec) throw system_error(ec); |
280 | 1.88k | m_size_on_disk = aux::size_on_disk(m_torrent_file->files()); |
281 | 1.88k | } |
282 | | |
283 | | // --- WEB SEEDS --- |
284 | | |
285 | | // if override web seed flag is set, don't load any web seeds from the |
286 | | // torrent file. |
287 | 1.88k | std::vector<web_seed_t> ws; |
288 | 1.88k | if (!(p.flags & torrent_flags::override_web_seeds)) |
289 | 1.01k | { |
290 | 1.01k | for (auto const& e : m_torrent_file->web_seeds()) |
291 | 0 | ws.emplace_back(e); |
292 | 1.01k | } |
293 | | |
294 | | // add web seeds from add_torrent_params |
295 | 1.88k | bool const multi_file = m_torrent_file->is_valid() |
296 | 1.88k | && m_torrent_file->num_files() > 1; |
297 | | |
298 | 1.88k | for (auto const& u : p.url_seeds) |
299 | 0 | { |
300 | 0 | ws.emplace_back(web_seed_t(u, web_seed_entry::url_seed)); |
301 | | |
302 | | // correct URLs to end with a "/" for multi-file torrents |
303 | 0 | if (multi_file) |
304 | 0 | ensure_trailing_slash(ws.back().url); |
305 | 0 | if (!m_torrent_file->is_valid()) |
306 | 0 | m_torrent_file->add_url_seed(ws.back().url); |
307 | 0 | } |
308 | | |
309 | 1.88k | for (auto const& e : p.http_seeds) |
310 | 0 | { |
311 | 0 | ws.emplace_back(e, web_seed_entry::http_seed); |
312 | 0 | if (!m_torrent_file->is_valid()) |
313 | 0 | m_torrent_file->add_http_seed(e); |
314 | 0 | } |
315 | | |
316 | 1.88k | aux::random_shuffle(ws); |
317 | 1.88k | for (auto& w : ws) m_web_seeds.emplace_back(std::move(w)); |
318 | | |
319 | | // --- TRACKERS --- |
320 | | |
321 | | // if override trackers flag is set, don't load trackers from torrent file |
322 | 1.88k | if (!(p.flags & torrent_flags::override_trackers)) |
323 | 1.07k | { |
324 | 1.07k | m_trackers.clear(); |
325 | 1.07k | for (auto const& ae : m_torrent_file->trackers()) |
326 | 0 | m_trackers.emplace_back(ae); |
327 | 1.07k | } |
328 | | |
329 | 1.88k | int tier = 0; |
330 | 1.88k | auto tier_iter = p.tracker_tiers.begin(); |
331 | 1.88k | for (auto const& url : p.trackers) |
332 | 0 | { |
333 | 0 | aux::announce_entry e(url); |
334 | 0 | if (tier_iter != p.tracker_tiers.end()) |
335 | 0 | tier = *tier_iter++; |
336 | |
|
337 | 0 | e.fail_limit = 0; |
338 | 0 | e.source = announce_entry::source_magnet_link; |
339 | 0 | e.tier = std::uint8_t(tier); |
340 | 0 | if (!find_tracker(e.url)) |
341 | 0 | { |
342 | 0 | if (e.url.empty()) continue; |
343 | 0 | m_trackers.push_back(e); |
344 | | // add the tracker to the m_torrent_file here so that the trackers |
345 | | // will be preserved via create_torrent() when passing in just the |
346 | | // torrent_info object. |
347 | 0 | if (!m_torrent_file->is_valid()) |
348 | 0 | m_torrent_file->add_tracker(e.url, e.tier, announce_entry::tracker_source(e.source)); |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | 1.88k | std::sort(m_trackers.begin(), m_trackers.end() |
353 | 1.88k | , [] (aux::announce_entry const& lhs, aux::announce_entry const& rhs) |
354 | 1.88k | { return lhs.tier < rhs.tier; }); |
355 | | |
356 | 1.88k | if (settings().get_bool(settings_pack::prefer_udp_trackers)) |
357 | 1.88k | prioritize_udp_trackers(); |
358 | | |
359 | 1.88k | if (m_torrent_file->is_valid()) |
360 | 1.88k | { |
361 | | // setting file- or piece priorities for seed mode makes no sense. If a |
362 | | // torrent ends up in seed mode by accident, it can be very confusing, |
363 | | // so assume the seed mode flag is not intended and don't enable it in |
364 | | // that case. Also, if the resume data says we're missing a piece, we |
365 | | // can't be in seed-mode. |
366 | 1.88k | m_seed_mode = (p.flags & torrent_flags::seed_mode) |
367 | 1.88k | && std::find(p.file_priorities.begin(), p.file_priorities.end(), dont_download) == p.file_priorities.end() |
368 | 1.88k | && std::find(p.piece_priorities.begin(), p.piece_priorities.end(), dont_download) == p.piece_priorities.end() |
369 | 1.88k | && std::find(p.have_pieces.begin(), p.have_pieces.end(), false) == p.have_pieces.end(); |
370 | | |
371 | 1.88k | m_connections_initialized = true; |
372 | 1.88k | } |
373 | 0 | else |
374 | 0 | { |
375 | 0 | if (!p.name.empty()) m_name.reset(new std::string(p.name)); |
376 | 0 | } |
377 | | |
378 | 1.88k | TORRENT_ASSERT(is_single_thread()); |
379 | 1.88k | m_file_priority.assign(p.file_priorities.begin(), p.file_priorities.end()); |
380 | | |
381 | 1.88k | if (m_seed_mode) |
382 | 333 | { |
383 | 333 | m_verified.resize(m_torrent_file->num_pieces(), false); |
384 | 333 | m_verifying.resize(m_torrent_file->num_pieces(), false); |
385 | 333 | } |
386 | | |
387 | 1.88k | m_total_uploaded = p.total_uploaded; |
388 | 1.88k | m_total_downloaded = p.total_downloaded; |
389 | | |
390 | | // the number of seconds this torrent has spent in started, finished and |
391 | | // seeding state so far, respectively. |
392 | 1.88k | m_active_time = seconds(p.active_time); |
393 | 1.88k | m_finished_time = seconds(p.finished_time); |
394 | 1.88k | m_seeding_time = seconds(p.seeding_time); |
395 | | |
396 | 1.88k | if (m_completed_time != 0 && m_completed_time < m_added_time) |
397 | 0 | m_completed_time = m_added_time; |
398 | | |
399 | | // --- V2 HASHES --- |
400 | | |
401 | 1.88k | if (m_torrent_file->is_valid() && m_torrent_file->info_hashes().has_v2()) |
402 | 1.88k | { |
403 | 1.88k | if (!p.merkle_trees.empty()) |
404 | 802 | load_merkle_trees( |
405 | 802 | std::move(p.merkle_trees) |
406 | 802 | , std::move(p.merkle_tree_mask) |
407 | 802 | , std::move(p.verified_leaf_hashes)); |
408 | | |
409 | | // we really don't want to store extra copies of the trees |
410 | 1.88k | TORRENT_ASSERT(p.merkle_trees.empty()); |
411 | 1.88k | } |
412 | | |
413 | 1.88k | if (valid_metadata()) |
414 | 1.88k | { |
415 | 1.88k | inc_stats_counter(counters::num_total_pieces_added |
416 | 1.88k | , m_torrent_file->num_pieces()); |
417 | 1.88k | } |
418 | | |
419 | | // TODO: 3 we could probably get away with just saving a few fields here |
420 | 1.88k | m_add_torrent_params = std::make_unique<add_torrent_params>(std::move(p)); |
421 | 1.88k | } |
422 | | |
423 | | void torrent::load_merkle_trees( |
424 | | aux::vector<std::vector<sha256_hash>, file_index_t> trees_import |
425 | | , aux::vector<std::vector<bool>, file_index_t> mask |
426 | | , aux::vector<std::vector<bool>, file_index_t> verified) |
427 | 802 | { |
428 | 802 | auto const& fs = m_torrent_file->orig_files(); |
429 | | |
430 | 802 | std::vector<bool> const empty_verified; |
431 | 1.60k | for (file_index_t i{0}; i < fs.end_file(); ++i) |
432 | 802 | { |
433 | 802 | if (fs.pad_file_at(i) || fs.file_size(i) == 0) |
434 | 0 | continue; |
435 | | |
436 | 802 | if (i >= trees_import.end_index()) break; |
437 | 802 | std::vector<bool> const& verified_bitmask = (i >= verified.end_index()) ? empty_verified : verified[i]; |
438 | 802 | if (i < mask.end_index() && !mask[i].empty()) |
439 | 742 | { |
440 | 742 | mask[i].resize(m_merkle_trees[i].size(), false); |
441 | 742 | m_merkle_trees[i].load_sparse_tree(trees_import[i], mask[i], verified_bitmask); |
442 | 742 | } |
443 | 60 | else |
444 | 60 | { |
445 | 60 | m_merkle_trees[i].load_tree(trees_import[i], verified_bitmask); |
446 | 60 | } |
447 | 802 | } |
448 | 802 | } |
449 | | |
450 | | void torrent::inc_stats_counter(int c, int value) |
451 | 9.64k | { m_ses.stats_counters().inc_stats_counter(c, value); } |
452 | | |
453 | | int torrent::current_stats_state() const |
454 | 15.3k | { |
455 | 15.3k | if (m_abort || !m_added) |
456 | 13.4k | return counters::num_checking_torrents + no_gauge_state; |
457 | | |
458 | 1.89k | if (has_error()) return counters::num_error_torrents; |
459 | 1.89k | if (m_paused || m_graceful_pause_mode) |
460 | 830 | { |
461 | 830 | if (!is_auto_managed()) return counters::num_stopped_torrents; |
462 | 178 | if (is_seed()) return counters::num_queued_seeding_torrents; |
463 | 131 | return counters::num_queued_download_torrents; |
464 | 178 | } |
465 | 1.06k | if (state() == torrent_status::checking_files |
466 | 1.06k | #if TORRENT_ABI_VERSION == 1 |
467 | 1.06k | || state() == torrent_status::queued_for_checking |
468 | 1.06k | #endif |
469 | 1.06k | ) |
470 | 121 | return counters::num_checking_torrents; |
471 | 944 | else if (is_seed()) return counters::num_seeding_torrents; |
472 | 752 | else if (is_upload_only()) return counters::num_upload_only_torrents; |
473 | 490 | return counters::num_downloading_torrents; |
474 | 1.06k | } |
475 | | |
476 | | void torrent::update_gauge() |
477 | 15.3k | { |
478 | 15.3k | int const new_gauge_state = current_stats_state() - counters::num_checking_torrents; |
479 | 15.3k | TORRENT_ASSERT(new_gauge_state >= 0); |
480 | 15.3k | TORRENT_ASSERT(new_gauge_state <= no_gauge_state); |
481 | | |
482 | 15.3k | if (new_gauge_state == int(m_current_gauge_state)) return; |
483 | | |
484 | 3.76k | if (m_current_gauge_state != no_gauge_state) |
485 | 1.88k | inc_stats_counter(m_current_gauge_state + counters::num_checking_torrents, -1); |
486 | 3.76k | if (new_gauge_state != no_gauge_state) |
487 | 1.88k | inc_stats_counter(new_gauge_state + counters::num_checking_torrents, 1); |
488 | | |
489 | 3.76k | TORRENT_ASSERT(new_gauge_state >= 0); |
490 | 3.76k | TORRENT_ASSERT(new_gauge_state <= no_gauge_state); |
491 | 3.76k | m_current_gauge_state = static_cast<std::uint32_t>(new_gauge_state); |
492 | 3.76k | } |
493 | | |
494 | | void torrent::leave_seed_mode(seed_mode_t const checking) |
495 | 0 | { |
496 | 0 | if (!m_seed_mode) return; |
497 | | |
498 | 0 | if (checking == seed_mode_t::check_files) |
499 | 0 | { |
500 | | // this means the user promised we had all the |
501 | | // files, but it turned out we didn't. This is |
502 | | // an error. |
503 | | |
504 | | // TODO: 2 post alert |
505 | |
|
506 | | #ifndef TORRENT_DISABLE_LOGGING |
507 | | debug_log("*** FAILED SEED MODE, rechecking"); |
508 | | #endif |
509 | 0 | } |
510 | |
|
511 | | #ifndef TORRENT_DISABLE_LOGGING |
512 | | debug_log("*** LEAVING SEED MODE (%s)" |
513 | | , checking == seed_mode_t::skip_checking ? "as seed" : "as non-seed"); |
514 | | #endif |
515 | 0 | m_seed_mode = false; |
516 | | // seed is false if we turned out not |
517 | | // to be a seed after all |
518 | 0 | if (checking == seed_mode_t::check_files |
519 | 0 | && state() != torrent_status::checking_resume_data) |
520 | 0 | { |
521 | 0 | m_have_all = false; |
522 | 0 | set_state(torrent_status::downloading); |
523 | 0 | force_recheck(); |
524 | 0 | } |
525 | 0 | m_num_verified = 0; |
526 | 0 | m_verified.clear(); |
527 | 0 | m_verifying.clear(); |
528 | |
|
529 | 0 | set_need_save_resume(torrent_handle::if_state_changed); |
530 | 0 | } |
531 | | |
532 | | void torrent::verified(piece_index_t const piece) |
533 | 0 | { |
534 | 0 | TORRENT_ASSERT(!m_verified.get_bit(piece)); |
535 | 0 | ++m_num_verified; |
536 | 0 | m_verified.set_bit(piece); |
537 | 0 | set_need_save_resume(torrent_handle::if_download_progress); |
538 | 0 | } |
539 | | |
540 | | void torrent::start() |
541 | 1.88k | { |
542 | 1.88k | TORRENT_ASSERT(is_single_thread()); |
543 | 1.88k | TORRENT_ASSERT(m_was_started == false); |
544 | 1.88k | #if TORRENT_USE_ASSERTS |
545 | 1.88k | m_was_started = true; |
546 | 1.88k | #endif |
547 | | |
548 | | // Some of these calls may log to the torrent debug log, which requires a |
549 | | // call to get_handle(), which requires the torrent object to be fully |
550 | | // constructed, as it relies on get_shared_from_this() |
551 | 1.88k | if (m_add_torrent_params) |
552 | 1.88k | { |
553 | 1.88k | #if TORRENT_ABI_VERSION == 1 |
554 | 1.88k | if (m_add_torrent_params->internal_resume_data_error |
555 | 1.88k | && m_ses.alerts().should_post<fastresume_rejected_alert>()) |
556 | 0 | { |
557 | 0 | m_ses.alerts().emplace_alert<fastresume_rejected_alert>(get_handle() |
558 | 0 | , m_add_torrent_params->internal_resume_data_error, "" |
559 | 0 | , operation_t::unknown); |
560 | 0 | } |
561 | 1.88k | #endif |
562 | | |
563 | 1.88k | add_torrent_params const& p = *m_add_torrent_params; |
564 | | |
565 | 1.88k | set_max_uploads(p.max_uploads, false); |
566 | 1.88k | set_max_connections(p.max_connections, false); |
567 | 1.88k | set_limit_impl(p.upload_limit, peer_connection::upload_channel, false); |
568 | 1.88k | set_limit_impl(p.download_limit, peer_connection::download_channel, false); |
569 | | |
570 | 1.88k | for (auto const& peer : p.peers) |
571 | 0 | { |
572 | 0 | add_peer(peer, peer_info::resume_data); |
573 | 0 | } |
574 | | |
575 | 1.88k | if (!p.peers.empty()) |
576 | 0 | { |
577 | 0 | do_connect_boost(); |
578 | 0 | } |
579 | | |
580 | | #ifndef TORRENT_DISABLE_LOGGING |
581 | | if (should_log() && !p.peers.empty()) |
582 | | { |
583 | | std::string str; |
584 | | for (auto const& peer : p.peers) |
585 | | { |
586 | | str += peer.address().to_string(); |
587 | | str += ' '; |
588 | | } |
589 | | debug_log("add_torrent add_peer() [ %s] connect-candidates: %d" |
590 | | , str.c_str(), m_peer_list |
591 | | ? m_peer_list->num_connect_candidates() : -1); |
592 | | } |
593 | | #endif |
594 | 1.88k | } |
595 | | |
596 | | #ifndef TORRENT_DISABLE_LOGGING |
597 | | if (should_log()) |
598 | | { |
599 | | debug_log("creating torrent: %s max-uploads: %d max-connections: %d " |
600 | | "upload-limit: %d download-limit: %d flags: %s%s%s%s%s%s%s%s%s%s%s " |
601 | | "save-path: %s" |
602 | | , torrent_file().name().c_str() |
603 | | , int(m_max_uploads) |
604 | | , int(m_max_connections) |
605 | | , upload_limit() |
606 | | , download_limit() |
607 | | , m_seed_mode ? "seed-mode " : "" |
608 | | , m_upload_mode ? "upload-mode " : "" |
609 | | #ifndef TORRENT_DISABLE_SHARE_MODE |
610 | | , m_share_mode ? "share-mode " : "" |
611 | | #else |
612 | | , "" |
613 | | #endif |
614 | | , m_apply_ip_filter ? "apply-ip-filter " : "" |
615 | | , m_paused ? "paused " : "" |
616 | | , m_auto_managed ? "auto-managed " : "" |
617 | | , m_state_subscription ? "update-subscribe " : "" |
618 | | #ifndef TORRENT_DISABLE_SUPERSEEDING |
619 | | , m_super_seeding ? "super-seeding " : "" |
620 | | #else |
621 | | , "" |
622 | | #endif |
623 | | , m_sequential_download ? "sequential-download " : "" |
624 | | , (m_add_torrent_params && m_add_torrent_params->flags & torrent_flags::override_trackers) |
625 | | ? "override-trackers " : "" |
626 | | , (m_add_torrent_params && m_add_torrent_params->flags & torrent_flags::override_web_seeds) |
627 | | ? "override-web-seeds " : "" |
628 | | , m_save_path.c_str() |
629 | | ); |
630 | | } |
631 | | #endif |
632 | | |
633 | 1.88k | update_gauge(); |
634 | | |
635 | 1.88k | update_want_peers(); |
636 | 1.88k | update_want_scrape(); |
637 | 1.88k | update_want_tick(); |
638 | 1.88k | update_state_list(); |
639 | | |
640 | 1.88k | if (m_torrent_file->is_valid()) |
641 | 1.88k | { |
642 | 1.88k | init(); |
643 | 1.88k | } |
644 | 0 | else |
645 | 0 | { |
646 | | // we need to start announcing since we don't have any |
647 | | // metadata. To receive peers to ask for it. |
648 | 0 | set_state(torrent_status::downloading_metadata); |
649 | 0 | start_announcing(); |
650 | 0 | } |
651 | | |
652 | | #if TORRENT_USE_INVARIANT_CHECKS |
653 | | check_invariant(); |
654 | | #endif |
655 | 1.88k | } |
656 | | |
657 | | void torrent::set_apply_ip_filter(bool b) |
658 | 0 | { |
659 | 0 | if (b == m_apply_ip_filter) return; |
660 | 0 | if (b) |
661 | 0 | { |
662 | 0 | inc_stats_counter(counters::non_filter_torrents, -1); |
663 | 0 | } |
664 | 0 | else |
665 | 0 | { |
666 | 0 | inc_stats_counter(counters::non_filter_torrents); |
667 | 0 | } |
668 | |
|
669 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
670 | |
|
671 | 0 | m_apply_ip_filter = b; |
672 | 0 | ip_filter_updated(); |
673 | 0 | state_updated(); |
674 | 0 | } |
675 | | |
676 | | void torrent::set_ip_filter(std::shared_ptr<const ip_filter> ipf) |
677 | 1.88k | { |
678 | 1.88k | m_ip_filter = std::move(ipf); |
679 | 1.88k | if (!m_apply_ip_filter) return; |
680 | 608 | ip_filter_updated(); |
681 | 608 | } |
682 | | |
683 | | #ifndef TORRENT_DISABLE_DHT |
684 | | bool torrent::should_announce_dht() const |
685 | 0 | { |
686 | 0 | TORRENT_ASSERT(is_single_thread()); |
687 | 0 | if (!m_enable_dht) return false; |
688 | 0 | if (!m_ses.announce_dht()) return false; |
689 | | |
690 | 0 | #if TORRENT_USE_I2P |
691 | | // i2p torrents don't announced on the DHT |
692 | | // unless we allow mixed swarms |
693 | 0 | if (is_i2p() && !settings().get_bool(settings_pack::allow_i2p_mixed)) |
694 | 0 | return false; |
695 | 0 | #endif |
696 | | |
697 | 0 | if (!m_ses.dht()) return false; |
698 | 0 | if (m_torrent_file->is_valid() && !m_files_checked) return false; |
699 | 0 | if (!m_announce_to_dht) return false; |
700 | 0 | if (m_paused) return false; |
701 | | |
702 | | // don't announce private torrents |
703 | 0 | if (m_torrent_file->is_valid() && m_torrent_file->priv()) return false; |
704 | 0 | if (m_trackers.empty()) return true; |
705 | 0 | if (!settings().get_bool(settings_pack::use_dht_as_fallback)) return true; |
706 | | |
707 | 0 | return std::none_of(m_trackers.begin(), m_trackers.end() |
708 | 0 | , [](aux::announce_entry const& tr) { return bool(tr.verified); }); |
709 | 0 | } |
710 | | |
711 | | #endif |
712 | | |
713 | | torrent::~torrent() |
714 | 1.88k | { |
715 | | // TODO: 3 assert there are no outstanding async operations on this |
716 | | // torrent |
717 | | |
718 | 1.88k | #if TORRENT_USE_ASSERTS |
719 | 16.9k | for (torrent_list_index_t i{}; i != m_links.end_index(); ++i) |
720 | 15.0k | { |
721 | 15.0k | if (!m_links[i].in_list()) continue; |
722 | 0 | m_links[i].unlink(m_ses.torrent_list(i), i); |
723 | 0 | } |
724 | 1.88k | #endif |
725 | | |
726 | | // The invariant can't be maintained here, since the torrent |
727 | | // is being destructed, all weak references to it have been |
728 | | // reset, which means that all its peers already have an |
729 | | // invalidated torrent pointer (so it cannot be verified to be correct) |
730 | | |
731 | | // i.e. the invariant can only be maintained if all connections have |
732 | | // been closed by the time the torrent is destructed. And they are |
733 | | // supposed to be closed. So we can still do the invariant check. |
734 | | |
735 | | // however, the torrent object may be destructed from the main |
736 | | // thread when shutting down, if the disk cache has references to it. |
737 | | // this means that the invariant check that this is called from the |
738 | | // network thread cannot be maintained |
739 | | |
740 | 1.88k | TORRENT_ASSERT(m_peer_class == peer_class_t{0}); |
741 | 1.88k | TORRENT_ASSERT(m_connections.empty()); |
742 | | // just in case, make sure the session accounting is kept right |
743 | 1.88k | for (auto p : m_connections) |
744 | 0 | m_ses.close_connection(p); |
745 | 1.88k | } |
746 | | |
747 | | void torrent::read_piece(piece_index_t const piece) |
748 | 0 | { |
749 | 0 | error_code ec; |
750 | 0 | if (m_abort || m_deleted) |
751 | 0 | { |
752 | 0 | ec.assign(boost::system::errc::operation_canceled, generic_category()); |
753 | 0 | } |
754 | 0 | else if (!valid_metadata()) |
755 | 0 | { |
756 | 0 | ec.assign(errors::no_metadata, libtorrent_category()); |
757 | 0 | } |
758 | 0 | else if (!user_have_piece(piece)) |
759 | 0 | { |
760 | 0 | ec.assign(errors::invalid_piece_index, libtorrent_category()); |
761 | 0 | } |
762 | |
|
763 | 0 | if (ec) |
764 | 0 | { |
765 | 0 | m_ses.alerts().emplace_alert<read_piece_alert>(get_handle(), piece, ec); |
766 | 0 | return; |
767 | 0 | } |
768 | | |
769 | 0 | const int piece_size = m_torrent_file->piece_size(piece); |
770 | 0 | const int blocks_in_piece = (piece_size + block_size() - 1) / block_size(); |
771 | |
|
772 | 0 | TORRENT_ASSERT(blocks_in_piece > 0); |
773 | 0 | TORRENT_ASSERT(piece_size > 0); |
774 | |
|
775 | 0 | if (blocks_in_piece == 0) |
776 | 0 | { |
777 | | // this shouldn't actually happen |
778 | 0 | boost::shared_array<char> buf; |
779 | 0 | m_ses.alerts().emplace_alert<read_piece_alert>( |
780 | 0 | get_handle(), piece, buf, 0); |
781 | 0 | return; |
782 | 0 | } |
783 | | |
784 | 0 | std::shared_ptr<read_piece_struct> rp = std::make_shared<read_piece_struct>(); |
785 | 0 | rp->piece_data.reset(new (std::nothrow) char[std::size_t(piece_size)]); |
786 | 0 | if (!rp->piece_data) |
787 | 0 | { |
788 | 0 | m_ses.alerts().emplace_alert<read_piece_alert>( |
789 | 0 | get_handle(), piece, error_code(boost::system::errc::not_enough_memory, generic_category())); |
790 | 0 | return; |
791 | 0 | } |
792 | 0 | rp->blocks_left = blocks_in_piece; |
793 | 0 | rp->fail = false; |
794 | |
|
795 | 0 | disk_job_flags_t flags{}; |
796 | 0 | auto const read_mode = settings().get_int(settings_pack::disk_io_read_mode); |
797 | 0 | if (read_mode == settings_pack::disable_os_cache) |
798 | 0 | flags |= disk_interface::volatile_read; |
799 | |
|
800 | 0 | peer_request r; |
801 | 0 | r.piece = piece; |
802 | 0 | r.start = 0; |
803 | 0 | auto self = shared_from_this(); |
804 | 0 | for (int i = 0; i < blocks_in_piece; ++i, r.start += block_size()) |
805 | 0 | { |
806 | 0 | r.length = std::min(piece_size - r.start, block_size()); |
807 | 0 | m_ses.disk_thread().async_read(m_storage, r |
808 | 0 | , [self, r, rp](disk_buffer_holder block, storage_error const& se) mutable |
809 | 0 | { self->on_disk_read_complete(std::move(block), se, r, rp); } |
810 | 0 | , flags); |
811 | 0 | } |
812 | 0 | m_ses.deferred_submit_jobs(); |
813 | 0 | } |
814 | | |
815 | | #ifndef TORRENT_DISABLE_SHARE_MODE |
816 | | void torrent::send_share_mode() |
817 | 0 | { |
818 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
819 | 0 | for (auto const pc : m_connections) |
820 | 0 | { |
821 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
822 | 0 | if (pc->type() != connection_type::bittorrent) continue; |
823 | 0 | auto* p = static_cast<bt_peer_connection*>(pc); |
824 | 0 | p->write_share_mode(); |
825 | 0 | } |
826 | 0 | #endif |
827 | 0 | } |
828 | | #endif // TORRENT_DISABLE_SHARE_MODE |
829 | | |
830 | | void torrent::send_upload_only() |
831 | 553 | { |
832 | 553 | #ifndef TORRENT_DISABLE_EXTENSIONS |
833 | | |
834 | 553 | #ifndef TORRENT_DISABLE_SHARE_MODE |
835 | 553 | if (share_mode()) return; |
836 | 237 | #endif |
837 | 237 | #ifndef TORRENT_DISABLE_SUPERSEEDING |
838 | 237 | if (super_seeding()) return; |
839 | 192 | #endif |
840 | | |
841 | | // if we send upload-only, the other end is very likely to disconnect |
842 | | // us, at least if it's a seed. If we don't want to close redundant |
843 | | // connections, don't sent upload-only |
844 | 192 | if (!settings().get_bool(settings_pack::close_redundant_connections)) return; |
845 | | |
846 | | // if we're super seeding, we don't want to make peers |
847 | | // think that we only have a single piece and is upload |
848 | | // only, since they might disconnect immediately when |
849 | | // they have downloaded a single piece, although we'll |
850 | | // make another piece available |
851 | 192 | bool const upload_only_enabled = is_upload_only() |
852 | 192 | #ifndef TORRENT_DISABLE_SUPERSEEDING |
853 | 192 | && !super_seeding() |
854 | 192 | #endif |
855 | 192 | ; |
856 | | |
857 | 192 | for (auto p : m_connections) |
858 | 0 | { |
859 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
860 | |
|
861 | 0 | p->send_not_interested(); |
862 | 0 | p->send_upload_only(upload_only_enabled); |
863 | 0 | } |
864 | 192 | #endif // TORRENT_DISABLE_EXTENSIONS |
865 | 192 | } |
866 | | |
867 | | torrent_flags_t torrent::flags() const |
868 | 3 | { |
869 | 3 | torrent_flags_t ret = torrent_flags_t{}; |
870 | 3 | if (m_seed_mode) |
871 | 0 | ret |= torrent_flags::seed_mode; |
872 | 3 | if (m_upload_mode) |
873 | 0 | ret |= torrent_flags::upload_mode; |
874 | 3 | #ifndef TORRENT_DISABLE_SHARE_MODE |
875 | 3 | if (m_share_mode) |
876 | 0 | ret |= torrent_flags::share_mode; |
877 | 3 | #endif |
878 | 3 | if (m_apply_ip_filter) |
879 | 3 | ret |= torrent_flags::apply_ip_filter; |
880 | 3 | if (is_torrent_paused()) |
881 | 0 | ret |= torrent_flags::paused; |
882 | 3 | if (m_auto_managed) |
883 | 3 | ret |= torrent_flags::auto_managed; |
884 | 3 | #ifndef TORRENT_DISABLE_SUPERSEEDING |
885 | 3 | if (m_super_seeding) |
886 | 0 | ret |= torrent_flags::super_seeding; |
887 | 3 | #endif |
888 | 3 | if (m_sequential_download) |
889 | 0 | ret |= torrent_flags::sequential_download; |
890 | 3 | if (m_stop_when_ready) |
891 | 0 | ret |= torrent_flags::stop_when_ready; |
892 | 3 | if (!m_enable_dht) |
893 | 0 | ret |= torrent_flags::disable_dht; |
894 | 3 | if (!m_enable_lsd) |
895 | 0 | ret |= torrent_flags::disable_lsd; |
896 | 3 | if (!m_enable_pex) |
897 | 0 | ret |= torrent_flags::disable_pex; |
898 | 3 | if (m_i2p) |
899 | 0 | ret |= torrent_flags::i2p_torrent; |
900 | 3 | return ret; |
901 | 3 | } |
902 | | |
903 | | void torrent::set_flags(torrent_flags_t const flags |
904 | | , torrent_flags_t const mask) |
905 | 0 | { |
906 | 0 | if (mask & torrent_flags::i2p_torrent) |
907 | 0 | { |
908 | 0 | m_i2p = bool(flags & torrent_flags::i2p_torrent); |
909 | 0 | } |
910 | 0 | if ((mask & torrent_flags::seed_mode) |
911 | 0 | && !(flags & torrent_flags::seed_mode)) |
912 | 0 | { |
913 | 0 | leave_seed_mode(seed_mode_t::check_files); |
914 | 0 | } |
915 | 0 | if (mask & torrent_flags::upload_mode) |
916 | 0 | set_upload_mode(bool(flags & torrent_flags::upload_mode)); |
917 | 0 | #ifndef TORRENT_DISABLE_SHARE_MODE |
918 | 0 | if (mask & torrent_flags::share_mode) |
919 | 0 | set_share_mode(bool(flags & torrent_flags::share_mode)); |
920 | 0 | #endif |
921 | 0 | if (mask & torrent_flags::apply_ip_filter) |
922 | 0 | set_apply_ip_filter(bool(flags & torrent_flags::apply_ip_filter)); |
923 | 0 | if (mask & torrent_flags::paused) |
924 | 0 | { |
925 | 0 | if (flags & torrent_flags::paused) |
926 | 0 | pause(torrent_handle::graceful_pause); |
927 | 0 | else |
928 | 0 | resume(); |
929 | 0 | } |
930 | 0 | if (mask & torrent_flags::auto_managed) |
931 | 0 | auto_managed(bool(flags & torrent_flags::auto_managed)); |
932 | 0 | #ifndef TORRENT_DISABLE_SUPERSEEDING |
933 | 0 | if (mask & torrent_flags::super_seeding) |
934 | 0 | set_super_seeding(bool(flags & torrent_flags::super_seeding)); |
935 | 0 | #endif |
936 | 0 | if (mask & torrent_flags::sequential_download) |
937 | 0 | set_sequential_download(bool(flags & torrent_flags::sequential_download)); |
938 | 0 | if (mask & torrent_flags::stop_when_ready) |
939 | 0 | stop_when_ready(bool(flags & torrent_flags::stop_when_ready)); |
940 | 0 | if (mask & torrent_flags::disable_dht) |
941 | 0 | { |
942 | 0 | bool const new_value = !bool(flags & torrent_flags::disable_dht); |
943 | 0 | if (m_enable_dht != new_value) set_need_save_resume(torrent_handle::if_config_changed); |
944 | 0 | m_enable_dht = new_value; |
945 | 0 | } |
946 | 0 | if (mask & torrent_flags::disable_lsd) |
947 | 0 | { |
948 | 0 | bool const new_value = !bool(flags & torrent_flags::disable_lsd); |
949 | 0 | if (m_enable_dht != new_value) set_need_save_resume(torrent_handle::if_config_changed); |
950 | 0 | m_enable_lsd = new_value; |
951 | 0 | } |
952 | 0 | if (mask & torrent_flags::disable_pex) |
953 | 0 | { |
954 | 0 | bool const new_value = !bool(flags & torrent_flags::disable_pex); |
955 | 0 | if (m_enable_dht != new_value) set_need_save_resume(torrent_handle::if_config_changed); |
956 | 0 | m_enable_pex = new_value; |
957 | 0 | } |
958 | 0 | } |
959 | | |
960 | | #ifndef TORRENT_DISABLE_SHARE_MODE |
961 | | void torrent::set_share_mode(bool s) |
962 | 0 | { |
963 | 0 | if (s == m_share_mode) return; |
964 | | |
965 | 0 | m_share_mode = s; |
966 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
967 | | #ifndef TORRENT_DISABLE_LOGGING |
968 | | debug_log("*** set-share-mode: %d", s); |
969 | | #endif |
970 | 0 | if (m_share_mode) |
971 | 0 | { |
972 | 0 | std::size_t const num_files = valid_metadata() |
973 | 0 | ? std::size_t(m_torrent_file->num_files()) |
974 | 0 | : m_file_priority.size(); |
975 | | // in share mode, all pieces have their priorities initialized to |
976 | | // dont_download |
977 | 0 | prioritize_files(aux::vector<download_priority_t, file_index_t>(num_files, dont_download)); |
978 | 0 | } |
979 | 0 | } |
980 | | #endif // TORRENT_DISABLE_SHARE_MODE |
981 | | |
982 | | void torrent::set_upload_mode(bool b) |
983 | 0 | { |
984 | 0 | if (b == m_upload_mode) return; |
985 | | |
986 | 0 | m_upload_mode = b; |
987 | | #ifndef TORRENT_DISABLE_LOGGING |
988 | | debug_log("*** set-upload-mode: %d", b); |
989 | | #endif |
990 | |
|
991 | 0 | set_need_save_resume(torrent_handle::if_state_changed); |
992 | 0 | update_gauge(); |
993 | 0 | state_updated(); |
994 | 0 | send_upload_only(); |
995 | |
|
996 | 0 | if (m_upload_mode) |
997 | 0 | { |
998 | | // clear request queues of all peers |
999 | 0 | for (auto p : m_connections) |
1000 | 0 | { |
1001 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
1002 | | // we may want to disconnect other upload-only peers |
1003 | 0 | if (p->upload_only()) |
1004 | 0 | p->update_interest(); |
1005 | 0 | p->cancel_all_requests(); |
1006 | 0 | } |
1007 | | // this is used to try leaving upload only mode periodically |
1008 | 0 | m_upload_mode_time = aux::time_now32(); |
1009 | 0 | } |
1010 | 0 | else if (m_peer_list) |
1011 | 0 | { |
1012 | | // reset last_connected, to force fast reconnect after leaving upload mode |
1013 | 0 | for (auto pe : *m_peer_list) |
1014 | 0 | { |
1015 | 0 | pe->last_connected = 0; |
1016 | 0 | } |
1017 | | |
1018 | | // send_block_requests on all peers |
1019 | 0 | for (auto p : m_connections) |
1020 | 0 | { |
1021 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
1022 | | // we may be interested now, or no longer interested |
1023 | 0 | p->update_interest(); |
1024 | 0 | p->send_block_requests(); |
1025 | 0 | } |
1026 | 0 | } |
1027 | 0 | } |
1028 | | |
1029 | | void torrent::need_peer_list() |
1030 | 640 | { |
1031 | 640 | if (m_peer_list) return; |
1032 | 1 | m_peer_list = std::make_unique<peer_list>(m_ses.get_peer_allocator()); |
1033 | 1 | } |
1034 | | |
1035 | | void torrent::handle_exception() |
1036 | 0 | { |
1037 | 0 | try |
1038 | 0 | { |
1039 | 0 | throw; |
1040 | 0 | } |
1041 | 0 | catch (system_error const& err) |
1042 | 0 | { |
1043 | | #ifndef TORRENT_DISABLE_LOGGING |
1044 | | if (should_log()) |
1045 | | { |
1046 | | debug_log("torrent exception: (%d) %s: %s" |
1047 | | , err.code().value(), err.code().message().c_str() |
1048 | | , err.what()); |
1049 | | } |
1050 | | #endif |
1051 | 0 | set_error(err.code(), torrent_status::error_file_exception); |
1052 | 0 | } |
1053 | 0 | catch (std::exception const& err) |
1054 | 0 | { |
1055 | 0 | TORRENT_UNUSED(err); |
1056 | 0 | set_error(error_code(), torrent_status::error_file_exception); |
1057 | | #ifndef TORRENT_DISABLE_LOGGING |
1058 | | if (should_log()) |
1059 | | { |
1060 | | debug_log("torrent exception: %s", err.what()); |
1061 | | } |
1062 | | #endif |
1063 | 0 | } |
1064 | 0 | catch (...) |
1065 | 0 | { |
1066 | 0 | set_error(error_code(), torrent_status::error_file_exception); |
1067 | | #ifndef TORRENT_DISABLE_LOGGING |
1068 | | if (should_log()) |
1069 | | { |
1070 | | debug_log("torrent exception: unknown"); |
1071 | | } |
1072 | | #endif |
1073 | 0 | } |
1074 | 0 | } |
1075 | | |
1076 | | void torrent::handle_disk_error(string_view job_name |
1077 | | , storage_error const& error |
1078 | | , peer_connection* c |
1079 | | , disk_class rw) |
1080 | 0 | { |
1081 | 0 | TORRENT_UNUSED(job_name); |
1082 | 0 | TORRENT_ASSERT(is_single_thread()); |
1083 | 0 | TORRENT_ASSERT(error); |
1084 | |
|
1085 | | #ifndef TORRENT_DISABLE_LOGGING |
1086 | | if (should_log()) |
1087 | | { |
1088 | | debug_log("disk error: (%d) %s [%*s : %s] in file: %s" |
1089 | | , error.ec.value(), error.ec.message().c_str() |
1090 | | , int(job_name.size()), job_name.data() |
1091 | | , operation_name(error.operation) |
1092 | | , resolve_filename(error.file()).c_str()); |
1093 | | } |
1094 | | #endif |
1095 | |
|
1096 | 0 | if (error.ec == boost::system::errc::not_enough_memory) |
1097 | 0 | { |
1098 | 0 | if (alerts().should_post<file_error_alert>()) |
1099 | 0 | alerts().emplace_alert<file_error_alert>(error.ec |
1100 | 0 | , resolve_filename(error.file()), error.operation, get_handle()); |
1101 | 0 | if (c) c->disconnect(errors::no_memory, error.operation); |
1102 | 0 | return; |
1103 | 0 | } |
1104 | | |
1105 | 0 | if (error.ec == boost::asio::error::operation_aborted) return; |
1106 | | |
1107 | | // notify the user of the error |
1108 | 0 | if (alerts().should_post<file_error_alert>()) |
1109 | 0 | alerts().emplace_alert<file_error_alert>(error.ec |
1110 | 0 | , resolve_filename(error.file()), error.operation, get_handle()); |
1111 | | |
1112 | | // if a write operation failed, and future writes are likely to |
1113 | | // fail, while reads may succeed, just set the torrent to upload mode |
1114 | | // if we make an incorrect assumption here, it's not the end of the |
1115 | | // world, if we ever issue a read request and it fails as well, we |
1116 | | // won't get in here and we'll actually end up pausing the torrent |
1117 | 0 | if (rw == disk_class::write |
1118 | 0 | && (error.ec == boost::system::errc::read_only_file_system |
1119 | 0 | || error.ec == boost::system::errc::permission_denied |
1120 | 0 | || error.ec == boost::system::errc::operation_not_permitted |
1121 | 0 | || error.ec == boost::system::errc::no_space_on_device |
1122 | 0 | || error.ec == boost::system::errc::file_too_large)) |
1123 | 0 | { |
1124 | | // if we failed to write, stop downloading and just |
1125 | | // keep seeding. |
1126 | | // TODO: 1 make this depend on the error and on the filesystem the |
1127 | | // files are being downloaded to. If the error is no_space_left_on_device |
1128 | | // and the filesystem doesn't support sparse files, only zero the priorities |
1129 | | // of the pieces that are at the tails of all files, leaving everything |
1130 | | // up to the highest written piece in each file |
1131 | 0 | set_upload_mode(true); |
1132 | 0 | return; |
1133 | 0 | } |
1134 | | |
1135 | | // put the torrent in an error-state |
1136 | 0 | set_error(error.ec, error.file()); |
1137 | | |
1138 | | // if the error appears to be more serious than a full disk, just pause the torrent |
1139 | 0 | pause(); |
1140 | 0 | } |
1141 | | |
1142 | | void torrent::handle_inconsistent_hashes(piece_index_t const piece) |
1143 | 0 | { |
1144 | 0 | auto const file_slices = torrent_file().map_block(piece, 0, 0); |
1145 | 0 | file_index_t const file = file_slices.empty() ? torrent_status::error_file_none : file_slices[0].file_index; |
1146 | 0 | set_error(errors::torrent_inconsistent_hashes, file); |
1147 | | // if this is a hybrid torrent, we may have marked some more pieces |
1148 | | // as "have" but not yet validated them against the v2 hashes. At |
1149 | | // this point, just assume we have no pieces |
1150 | 0 | m_picker.reset(); |
1151 | 0 | m_hash_picker.reset(); |
1152 | 0 | m_file_progress.clear(); |
1153 | 0 | m_have_all = false; |
1154 | 0 | update_gauge(); |
1155 | 0 | pause(); |
1156 | 0 | } |
1157 | | |
1158 | 0 | void torrent::on_piece_fail_sync(piece_index_t const piece, piece_block) try |
1159 | 0 | { |
1160 | 0 | if (m_abort) return; |
1161 | | |
1162 | | // the user may have called force_recheck, which clears |
1163 | | // the piece picker |
1164 | 0 | if (has_picker()) |
1165 | 0 | { |
1166 | | // unlock the piece and restore it, as if no block was |
1167 | | // ever downloaded for it. |
1168 | 0 | m_picker->restore_piece(piece); |
1169 | 0 | } |
1170 | |
|
1171 | 0 | update_gauge(); |
1172 | | // some peers that previously was no longer interesting may |
1173 | | // now have become interesting, since we lack this one piece now. |
1174 | 0 | for (auto i = begin(); i != end();) |
1175 | 0 | { |
1176 | 0 | peer_connection* p = *i; |
1177 | | // update_interest may disconnect the peer and |
1178 | | // invalidate the iterator |
1179 | 0 | ++i; |
1180 | | // no need to do anything with peers that |
1181 | | // already are interested. Gaining a piece may |
1182 | | // only make uninteresting peers interesting again. |
1183 | 0 | if (p->is_interesting()) continue; |
1184 | 0 | p->update_interest(); |
1185 | 0 | if (!m_abort) |
1186 | 0 | { |
1187 | 0 | if (request_a_block(*this, *p)) |
1188 | 0 | inc_stats_counter(counters::hash_fail_piece_picks); |
1189 | 0 | p->send_block_requests(); |
1190 | 0 | } |
1191 | 0 | } |
1192 | 0 | } |
1193 | 0 | catch (...) { handle_exception(); } |
1194 | | |
1195 | | void torrent::on_disk_read_complete(disk_buffer_holder buffer |
1196 | | , storage_error const& se |
1197 | 0 | , peer_request const& r, std::shared_ptr<read_piece_struct> rp) try |
1198 | 0 | { |
1199 | | // hold a reference until this function returns |
1200 | 0 | TORRENT_ASSERT(is_single_thread()); |
1201 | |
|
1202 | 0 | --rp->blocks_left; |
1203 | 0 | if (se) |
1204 | 0 | { |
1205 | 0 | rp->fail = true; |
1206 | 0 | rp->error = se.ec; |
1207 | 0 | handle_disk_error("read", se); |
1208 | 0 | } |
1209 | 0 | else |
1210 | 0 | { |
1211 | 0 | std::memcpy(rp->piece_data.get() + r.start, buffer.data(), aux::numeric_cast<std::size_t>(r.length)); |
1212 | 0 | } |
1213 | |
|
1214 | 0 | if (rp->blocks_left == 0) |
1215 | 0 | { |
1216 | 0 | int size = m_torrent_file->piece_size(r.piece); |
1217 | 0 | if (rp->fail) |
1218 | 0 | { |
1219 | 0 | m_ses.alerts().emplace_alert<read_piece_alert>( |
1220 | 0 | get_handle(), r.piece, rp->error); |
1221 | 0 | } |
1222 | 0 | else |
1223 | 0 | { |
1224 | 0 | m_ses.alerts().emplace_alert<read_piece_alert>( |
1225 | 0 | get_handle(), r.piece, rp->piece_data, size); |
1226 | 0 | } |
1227 | 0 | } |
1228 | 0 | } |
1229 | 0 | catch (...) { handle_exception(); } |
1230 | | |
1231 | | storage_mode_t torrent::storage_mode() const |
1232 | 0 | { return storage_mode_t(m_storage_mode); } |
1233 | | |
1234 | | void torrent::clear_peers() |
1235 | 0 | { |
1236 | 0 | disconnect_all(error_code(), operation_t::unknown); |
1237 | 0 | if (m_peer_list) m_peer_list->clear(); |
1238 | 0 | } |
1239 | | |
1240 | | void torrent::need_picker() |
1241 | 8.42k | { |
1242 | 8.42k | if (m_picker) return; |
1243 | | |
1244 | 1.57k | TORRENT_ASSERT(valid_metadata()); |
1245 | 1.57k | TORRENT_ASSERT(m_connections_initialized); |
1246 | | |
1247 | 1.57k | INVARIANT_CHECK; |
1248 | | |
1249 | | // if we have all pieces we should not have a picker |
1250 | | // unless we're in suggest mode |
1251 | 1.57k | TORRENT_ASSERT(!m_have_all |
1252 | 1.57k | || settings().get_int(settings_pack::suggest_mode) |
1253 | 1.57k | == settings_pack::suggest_read_cache); |
1254 | | |
1255 | 1.57k | auto pp = std::make_unique<piece_picker>(m_torrent_file->total_size() |
1256 | 1.57k | , m_torrent_file->piece_length()); |
1257 | | |
1258 | 1.57k | if (m_have_all) pp->we_have_all(); |
1259 | | |
1260 | | // initialize the file progress too |
1261 | 1.57k | if (m_file_progress.empty()) |
1262 | 1.57k | m_file_progress.init(*pp, m_torrent_file->files()); |
1263 | | |
1264 | 1.57k | m_picker = std::move(pp); |
1265 | | |
1266 | 1.57k | update_gauge(); |
1267 | | |
1268 | 1.57k | for (auto const p : m_connections) |
1269 | 0 | { |
1270 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
1271 | 0 | if (p->is_disconnecting()) continue; |
1272 | 0 | peer_has(p->get_bitfield(), p); |
1273 | 0 | } |
1274 | 1.57k | } |
1275 | | |
1276 | | void torrent::need_hash_picker() |
1277 | 1.02k | { |
1278 | 1.02k | if (m_hash_picker) return; |
1279 | | |
1280 | 1 | TORRENT_ASSERT(valid_metadata()); |
1281 | 1 | TORRENT_ASSERT(m_connections_initialized); |
1282 | | |
1283 | | //INVARIANT_CHECK; |
1284 | | |
1285 | 1 | m_hash_picker.reset(new hash_picker(m_torrent_file->orig_files() |
1286 | 1 | , m_merkle_trees)); |
1287 | 1 | } |
1288 | | |
1289 | | struct piece_refcount |
1290 | | { |
1291 | | piece_refcount(piece_picker& p, piece_index_t piece) |
1292 | 0 | : m_picker(p) |
1293 | 0 | , m_piece(piece) |
1294 | 0 | { |
1295 | 0 | m_picker.inc_refcount(m_piece, nullptr); |
1296 | 0 | } |
1297 | | |
1298 | | piece_refcount(piece_refcount const&) = delete; |
1299 | | piece_refcount& operator=(piece_refcount const&) = delete; |
1300 | | |
1301 | 0 | void disarm() { m_armed = false; } |
1302 | | |
1303 | | ~piece_refcount() |
1304 | 0 | { |
1305 | 0 | if (m_armed) |
1306 | 0 | m_picker.dec_refcount(m_piece, nullptr); |
1307 | 0 | } |
1308 | | |
1309 | | private: |
1310 | | piece_picker& m_picker; |
1311 | | piece_index_t m_piece; |
1312 | | bool m_armed = true; |
1313 | | }; |
1314 | | |
1315 | | void torrent::add_piece_async(piece_index_t const piece |
1316 | | , std::vector<char> data, add_piece_flags_t const flags) |
1317 | 0 | { |
1318 | 0 | TORRENT_ASSERT(is_single_thread()); |
1319 | | |
1320 | | // make sure the piece index is correct |
1321 | 0 | if (piece >= torrent_file().end_piece()) |
1322 | 0 | return; |
1323 | | |
1324 | | // make sure the piece size is correct |
1325 | 0 | if (data.size() != std::size_t(m_torrent_file->piece_size(piece))) |
1326 | 0 | return; |
1327 | | |
1328 | 0 | add_piece(piece, data.data(), flags); |
1329 | 0 | } |
1330 | | |
1331 | | // TODO: 3 there's some duplication between this function and |
1332 | | // peer_connection::incoming_piece(). is there a way to merge something? |
1333 | | void torrent::add_piece(piece_index_t const piece, char const* data |
1334 | | , add_piece_flags_t const flags) |
1335 | 0 | { |
1336 | 0 | TORRENT_ASSERT(is_single_thread()); |
1337 | | |
1338 | | // make sure the piece index is correct |
1339 | 0 | if (piece >= torrent_file().end_piece()) |
1340 | 0 | return; |
1341 | | |
1342 | 0 | int const piece_size = m_torrent_file->piece_size(piece); |
1343 | 0 | int const blocks_in_piece = (piece_size + block_size() - 1) / block_size(); |
1344 | |
|
1345 | 0 | if (m_deleted) return; |
1346 | | |
1347 | | // avoid crash trying to access the picker when there is none |
1348 | 0 | if (m_have_all && !has_picker()) return; |
1349 | | |
1350 | | // we don't support clobbering the piece picker while checking the |
1351 | | // files. We may end up having the same piece multiple times |
1352 | 0 | TORRENT_ASSERT_PRECOND(state() != torrent_status::checking_files |
1353 | 0 | && state() != torrent_status::checking_resume_data); |
1354 | 0 | if (state() == torrent_status::checking_files |
1355 | 0 | || state() == torrent_status::checking_resume_data) |
1356 | 0 | return; |
1357 | | |
1358 | 0 | need_picker(); |
1359 | |
|
1360 | 0 | if (picker().have_piece(piece) |
1361 | 0 | && !(flags & torrent_handle::overwrite_existing)) |
1362 | 0 | return; |
1363 | | |
1364 | 0 | peer_request p; |
1365 | 0 | p.piece = piece; |
1366 | 0 | p.start = 0; |
1367 | 0 | piece_refcount refcount{picker(), piece}; |
1368 | 0 | auto self = shared_from_this(); |
1369 | 0 | for (int i = 0; i < blocks_in_piece; ++i, p.start += block_size()) |
1370 | 0 | { |
1371 | 0 | piece_block const block(piece, i); |
1372 | |
|
1373 | 0 | bool const finished = picker().is_finished(block); |
1374 | | |
1375 | | // if this block is already finished, only resume if we have the |
1376 | | // flag set to overwrite existing data. |
1377 | 0 | if (!(flags & torrent_handle::overwrite_existing) && finished) |
1378 | 0 | continue; |
1379 | | |
1380 | 0 | bool const downloaded = picker().is_downloaded(block); |
1381 | | |
1382 | | // however, if the block is downloaded by not written to disk yet, |
1383 | | // we can't (easily) replace it. We would have to synchronize with |
1384 | | // the disk in a clear_piece() call. Instead, just ignore such |
1385 | | // blocks. |
1386 | 0 | if (downloaded && !finished) |
1387 | 0 | continue; |
1388 | | |
1389 | 0 | p.length = std::min(piece_size - p.start, block_size()); |
1390 | |
|
1391 | 0 | m_stats_counters.inc_stats_counter(counters::queued_write_bytes, p.length); |
1392 | |
|
1393 | 0 | disk_job_flags_t dflags{}; |
1394 | |
|
1395 | 0 | auto const write_mode = settings().get_int(settings_pack::disk_io_write_mode); |
1396 | 0 | if (write_mode == settings_pack::disable_os_cache) |
1397 | 0 | dflags |= disk_interface::flush_piece | disk_interface::volatile_read; |
1398 | |
|
1399 | 0 | m_ses.disk_thread().async_write(m_storage, p, data + p.start, nullptr |
1400 | 0 | , [self, p](storage_error const& error) { self->on_disk_write_complete(error, p); } |
1401 | 0 | , dflags); |
1402 | |
|
1403 | 0 | bool const was_finished = picker().is_piece_finished(p.piece); |
1404 | 0 | bool const multi = picker().num_peers(block) > 1; |
1405 | |
|
1406 | 0 | picker().mark_as_downloading(block, nullptr); |
1407 | 0 | picker().mark_as_writing(block, nullptr); |
1408 | |
|
1409 | 0 | if (multi) cancel_block(block); |
1410 | | |
1411 | | // did we just finish the piece? |
1412 | | // this means all blocks are either written |
1413 | | // to disk or are in the disk write cache |
1414 | 0 | if (picker().is_piece_finished(p.piece) && !was_finished) |
1415 | 0 | { |
1416 | 0 | verify_piece(p.piece); |
1417 | 0 | } |
1418 | 0 | } |
1419 | 0 | m_ses.deferred_submit_jobs(); |
1420 | | // if we don't have a picker anymore, we don't need to (and shouldn't) |
1421 | | // decrement the refcount |
1422 | 0 | if (!m_picker) |
1423 | 0 | refcount.disarm(); |
1424 | 0 | } |
1425 | | |
1426 | | void torrent::on_disk_write_complete(storage_error const& error |
1427 | 0 | , peer_request const& p) try |
1428 | 0 | { |
1429 | 0 | TORRENT_ASSERT(is_single_thread()); |
1430 | |
|
1431 | 0 | m_stats_counters.inc_stats_counter(counters::queued_write_bytes, -p.length); |
1432 | | |
1433 | | // std::fprintf(stderr, "torrent::on_disk_write_complete ret:%d piece:%d block:%d\n" |
1434 | | // , j->ret, j->piece, j->offset/0x4000); |
1435 | |
|
1436 | 0 | INVARIANT_CHECK; |
1437 | 0 | if (m_abort) return; |
1438 | 0 | piece_block const block_finished(p.piece, p.start / block_size()); |
1439 | |
|
1440 | 0 | if (error) |
1441 | 0 | { |
1442 | 0 | handle_disk_error("write", error); |
1443 | 0 | return; |
1444 | 0 | } |
1445 | | |
1446 | 0 | if (!has_picker()) return; |
1447 | | |
1448 | | // if we already have this block, just ignore it. |
1449 | | // this can happen if the same block is passed in through |
1450 | | // add_piece() multiple times |
1451 | 0 | if (picker().is_finished(block_finished)) return; |
1452 | | |
1453 | 0 | picker().mark_as_finished(block_finished, nullptr); |
1454 | 0 | maybe_done_flushing(); |
1455 | |
|
1456 | 0 | if (alerts().should_post<block_finished_alert>()) |
1457 | 0 | { |
1458 | 0 | alerts().emplace_alert<block_finished_alert>(get_handle(), |
1459 | 0 | tcp::endpoint(), peer_id(), block_finished.block_index |
1460 | 0 | , block_finished.piece_index); |
1461 | 0 | } |
1462 | 0 | } |
1463 | 0 | catch (...) { handle_exception(); } |
1464 | | |
1465 | | peer_request torrent::to_req(piece_block const& p) const |
1466 | 188 | { |
1467 | 188 | int const block_offset = p.block_index * block_size(); |
1468 | 188 | int const block = std::min(torrent_file().piece_size( |
1469 | 188 | p.piece_index) - block_offset, block_size()); |
1470 | 188 | TORRENT_ASSERT(block > 0); |
1471 | 188 | TORRENT_ASSERT(block <= block_size()); |
1472 | | |
1473 | 188 | peer_request r; |
1474 | 188 | r.piece = p.piece_index; |
1475 | 188 | r.start = block_offset; |
1476 | 188 | r.length = block; |
1477 | 188 | return r; |
1478 | 188 | } |
1479 | | |
1480 | | std::string torrent::name() const |
1481 | 28.8k | { |
1482 | 28.8k | if (valid_metadata()) return m_torrent_file->name(); |
1483 | 0 | if (m_name) return *m_name; |
1484 | 0 | return ""; |
1485 | 0 | } |
1486 | | |
1487 | | #ifndef TORRENT_DISABLE_EXTENSIONS |
1488 | | |
1489 | | void torrent::add_extension(std::shared_ptr<torrent_plugin> ext) |
1490 | 5.64k | { |
1491 | 5.64k | m_extensions.push_back(std::move(ext)); |
1492 | 5.64k | auto& ext_ref = m_extensions.back(); |
1493 | | |
1494 | 5.64k | for (auto p : m_connections) |
1495 | 0 | { |
1496 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
1497 | 0 | std::shared_ptr<peer_plugin> pp(ext_ref->new_connection(peer_connection_handle(p->self()))); |
1498 | 0 | if (pp) p->add_extension(std::move(pp)); |
1499 | 0 | } |
1500 | | |
1501 | | // if files are checked for this torrent, call the extension |
1502 | | // to let it initialize itself |
1503 | 5.64k | if (m_connections_initialized) |
1504 | 5.64k | ext_ref->on_files_checked(); |
1505 | 5.64k | } |
1506 | | |
1507 | | void torrent::remove_extension(std::shared_ptr<torrent_plugin> ext) |
1508 | 0 | { |
1509 | 0 | auto const i = std::find(m_extensions.begin(), m_extensions.end(), ext); |
1510 | 0 | if (i == m_extensions.end()) return; |
1511 | 0 | m_extensions.erase(i); |
1512 | 0 | } |
1513 | | |
1514 | | void torrent::add_extension_fun(std::function<std::shared_ptr<torrent_plugin>(torrent_handle const&, client_data_t)> const& ext |
1515 | | , client_data_t userdata) |
1516 | 0 | { |
1517 | 0 | std::shared_ptr<torrent_plugin> tp(ext(get_handle(), userdata)); |
1518 | 0 | if (!tp) return; |
1519 | | |
1520 | 0 | add_extension(std::move(tp)); |
1521 | 0 | } |
1522 | | |
1523 | | #endif |
1524 | | |
1525 | | #ifdef TORRENT_SSL_PEERS |
1526 | | bool torrent::verify_peer_cert(bool const preverified, ssl::verify_context& ctx) |
1527 | 0 | { |
1528 | | // if the cert wasn't signed by the correct CA, fail the verification |
1529 | 0 | if (!preverified) return false; |
1530 | | |
1531 | 0 | std::string expected = m_torrent_file->name(); |
1532 | | #ifndef TORRENT_DISABLE_LOGGING |
1533 | | std::string names; |
1534 | | bool match = false; |
1535 | | #endif |
1536 | |
|
1537 | 0 | #ifdef TORRENT_USE_OPENSSL |
1538 | 0 | #ifdef __clang__ |
1539 | 0 | #pragma clang diagnostic push |
1540 | 0 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" |
1541 | 0 | #pragma clang diagnostic ignored "-Wused-but-marked-unused" |
1542 | 0 | #pragma clang diagnostic ignored "-Wold-style-cast" |
1543 | 0 | #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" |
1544 | 0 | #endif |
1545 | | // we're only interested in checking the certificate at the end of the chain. |
1546 | | // any certificate that isn't the leaf (i.e. the one presented by the peer) |
1547 | | // should be accepted automatically, given preverified is true. The leaf certificate |
1548 | | // need to be verified to make sure its DN matches the info-hash |
1549 | 0 | int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle()); |
1550 | 0 | if (depth > 0) return true; |
1551 | | |
1552 | 0 | X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); |
1553 | | |
1554 | | // Go through the alternate names in the certificate looking for matching DNS entries |
1555 | 0 | auto* gens = static_cast<GENERAL_NAMES*>( |
1556 | 0 | X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); |
1557 | |
|
1558 | 0 | for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) |
1559 | 0 | { |
1560 | 0 | GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); |
1561 | 0 | if (gen->type != GEN_DNS) continue; |
1562 | 0 | ASN1_IA5STRING* domain = gen->d.dNSName; |
1563 | 0 | if (domain->type != V_ASN1_IA5STRING || !domain->data || !domain->length) continue; |
1564 | 0 | auto const* torrent_name = reinterpret_cast<char const*>(domain->data); |
1565 | 0 | auto const name_length = aux::numeric_cast<std::size_t>(domain->length); |
1566 | |
|
1567 | | #ifndef TORRENT_DISABLE_LOGGING |
1568 | | if (i > 1) names += " | n: "; |
1569 | | names.append(torrent_name, name_length); |
1570 | | #endif |
1571 | 0 | if (std::strncmp(torrent_name, "*", name_length) == 0 |
1572 | 0 | || std::strncmp(torrent_name, expected.c_str(), name_length) == 0) |
1573 | 0 | { |
1574 | | #ifndef TORRENT_DISABLE_LOGGING |
1575 | | match = true; |
1576 | | // if we're logging, keep looping over all names, |
1577 | | // for completeness of the log |
1578 | | continue; |
1579 | | #else |
1580 | 0 | return true; |
1581 | 0 | #endif |
1582 | 0 | } |
1583 | 0 | } |
1584 | | |
1585 | | // no match in the alternate names, so try the common names. We should only |
1586 | | // use the "most specific" common name, which is the last one in the list. |
1587 | 0 | X509_NAME* name = X509_get_subject_name(cert); |
1588 | 0 | int i = -1; |
1589 | 0 | ASN1_STRING* common_name = nullptr; |
1590 | 0 | while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) |
1591 | 0 | { |
1592 | 0 | X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i); |
1593 | 0 | common_name = X509_NAME_ENTRY_get_data(name_entry); |
1594 | 0 | } |
1595 | 0 | if (common_name && common_name->data && common_name->length) |
1596 | 0 | { |
1597 | 0 | auto const* torrent_name = reinterpret_cast<char const*>(common_name->data); |
1598 | 0 | auto const name_length = aux::numeric_cast<std::size_t>(common_name->length); |
1599 | |
|
1600 | | #ifndef TORRENT_DISABLE_LOGGING |
1601 | | if (!names.empty()) names += " | n: "; |
1602 | | names.append(torrent_name, name_length); |
1603 | | #endif |
1604 | 0 | if (std::strncmp(torrent_name, "*", name_length) == 0 |
1605 | 0 | || std::strncmp(torrent_name, expected.c_str(), name_length) == 0) |
1606 | 0 | { |
1607 | 0 | #ifdef TORRENT_DISABLE_LOGGING |
1608 | 0 | return true; |
1609 | | #else |
1610 | | match = true; |
1611 | | #endif |
1612 | |
|
1613 | 0 | } |
1614 | 0 | } |
1615 | 0 | #ifdef __clang__ |
1616 | 0 | #pragma clang diagnostic pop |
1617 | 0 | #endif |
1618 | | |
1619 | | #elif defined TORRENT_USE_GNUTLS |
1620 | | gnutls_x509_crt_t cert = ctx.native_handle(); |
1621 | | |
1622 | | // We don't use gnutls_x509_crt_check_hostname() |
1623 | | // as it doesn't handle wildcards the way we need here |
1624 | | |
1625 | | char buf[256]; |
1626 | | unsigned int seq = 0; |
1627 | | while(true) { |
1628 | | size_t len = sizeof(buf); |
1629 | | int ret = gnutls_x509_crt_get_subject_alt_name(cert, seq, buf, &len, nullptr); |
1630 | | if(ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) break; |
1631 | | if(ret == GNUTLS_E_SUCCESS) |
1632 | | { |
1633 | | #ifndef TORRENT_DISABLE_LOGGING |
1634 | | if (!names.empty()) names += " | n: "; |
1635 | | names.append(buf, len); |
1636 | | #endif |
1637 | | if (std::strncmp(buf, "*", len) == 0 |
1638 | | || std::strncmp(buf, expected.c_str(), len) == 0) |
1639 | | { |
1640 | | #ifndef TORRENT_DISABLE_LOGGING |
1641 | | match = true; |
1642 | | continue; |
1643 | | #else |
1644 | | return true; |
1645 | | #endif |
1646 | | } |
1647 | | } |
1648 | | ++seq; |
1649 | | } |
1650 | | |
1651 | | // no match in the alternate names, so try the common name |
1652 | | size_t len = sizeof(buf); |
1653 | | int ret = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, buf, &len); |
1654 | | if(ret == GNUTLS_E_SUCCESS) |
1655 | | { |
1656 | | #ifndef TORRENT_DISABLE_LOGGING |
1657 | | if (!names.empty()) names += " | n: "; |
1658 | | names.append(buf, len); |
1659 | | #endif |
1660 | | if (std::strncmp(buf, "*", len) == 0 |
1661 | | || std::strncmp(buf, expected.c_str(), len) == 0) |
1662 | | { |
1663 | | #ifdef TORRENT_DISABLE_LOGGING |
1664 | | return true; |
1665 | | #else |
1666 | | match = true; |
1667 | | #endif |
1668 | | } |
1669 | | } |
1670 | | #endif // TORRENT_USE_GNUTLS |
1671 | | |
1672 | | #ifndef TORRENT_DISABLE_LOGGING |
1673 | | debug_log("<== incoming SSL CONNECTION [ n: %s | match: %s ]" |
1674 | | , names.c_str(), match?"yes":"no"); |
1675 | | return match; |
1676 | | #else |
1677 | 0 | return false; |
1678 | 0 | #endif |
1679 | 0 | } |
1680 | | |
1681 | | void torrent::init_ssl(string_view cert) |
1682 | 0 | { |
1683 | | // create the SSL context for this torrent. We need to |
1684 | | // inject the root certificate, and no other, to |
1685 | | // verify other peers against |
1686 | 0 | std::unique_ptr<ssl::context> ctx(std::make_unique<ssl::context>(ssl::context::tls)); |
1687 | |
|
1688 | 0 | ctx->set_options(ssl::context::default_workarounds |
1689 | 0 | | ssl::context::no_sslv2 |
1690 | 0 | | ssl::context::no_sslv3 |
1691 | 0 | | ssl::context::single_dh_use); |
1692 | |
|
1693 | 0 | error_code ec; |
1694 | 0 | ctx->set_verify_mode(ssl::context::verify_peer |
1695 | 0 | | ssl::context::verify_fail_if_no_peer_cert |
1696 | 0 | | ssl::context::verify_client_once, ec); |
1697 | 0 | if (ec) |
1698 | 0 | { |
1699 | 0 | set_error(ec, torrent_status::error_file_ssl_ctx); |
1700 | 0 | pause(); |
1701 | 0 | return; |
1702 | 0 | } |
1703 | | |
1704 | | // the verification function verifies the distinguished name |
1705 | | // of a peer certificate to make sure it matches the info-hash |
1706 | | // of the torrent, or that it's a "star-cert" |
1707 | 0 | ctx->set_verify_callback( |
1708 | 0 | std::bind(&torrent::verify_peer_cert, this, _1, _2) |
1709 | 0 | , ec); |
1710 | 0 | if (ec) |
1711 | 0 | { |
1712 | 0 | set_error(ec, torrent_status::error_file_ssl_ctx); |
1713 | 0 | pause(); |
1714 | 0 | return; |
1715 | 0 | } |
1716 | | |
1717 | | // set the root certificate as trust |
1718 | 0 | ssl::set_trust_certificate(ctx->native_handle(), cert, ec); |
1719 | 0 | if (ec) |
1720 | 0 | { |
1721 | 0 | set_error(ec, torrent_status::error_file_ssl_ctx); |
1722 | 0 | pause(); |
1723 | 0 | return; |
1724 | 0 | } |
1725 | | |
1726 | | #if 0 |
1727 | | char filename[100]; |
1728 | | std::snprintf(filename, sizeof(filename), "/tmp/%u.pem", random()); |
1729 | | FILE* f = fopen(filename, "w+"); |
1730 | | fwrite(cert.c_str(), cert.size(), 1, f); |
1731 | | fclose(f); |
1732 | | ctx->load_verify_file(filename); |
1733 | | #endif |
1734 | | |
1735 | | // if all went well, set the torrent ssl context to this one |
1736 | 0 | m_ssl_ctx = std::move(ctx); |
1737 | | // tell the client we need a cert for this torrent |
1738 | 0 | alerts().emplace_alert<torrent_need_cert_alert>(get_handle()); |
1739 | 0 | } |
1740 | | #endif // TORRENT_SSL_PEERS |
1741 | | |
1742 | | void torrent::construct_storage() |
1743 | 1.88k | { |
1744 | 1.88k | storage_params params{ |
1745 | 1.88k | m_torrent_file->orig_files(), |
1746 | 1.88k | &m_torrent_file->orig_files() != &m_torrent_file->files() |
1747 | 1.88k | ? &m_torrent_file->files() : nullptr, |
1748 | 1.88k | m_save_path, |
1749 | 1.88k | static_cast<storage_mode_t>(m_storage_mode), |
1750 | 1.88k | m_file_priority, |
1751 | 1.88k | m_info_hash.get_best() |
1752 | 1.88k | }; |
1753 | | |
1754 | | // the shared_from_this() will create an intentional |
1755 | | // cycle of ownership, se the hpp file for description. |
1756 | 1.88k | m_storage = m_ses.disk_thread().new_torrent(params, shared_from_this()); |
1757 | 1.88k | } |
1758 | | |
1759 | | peer_connection* torrent::find_lowest_ranking_peer() const |
1760 | 0 | { |
1761 | 0 | auto lowest_rank = end(); |
1762 | 0 | for (auto i = begin(); i != end(); ++i) |
1763 | 0 | { |
1764 | | // disconnecting peers don't count |
1765 | 0 | if ((*i)->is_disconnecting()) continue; |
1766 | 0 | if (lowest_rank == end() || (*lowest_rank)->peer_rank() > (*i)->peer_rank()) |
1767 | 0 | lowest_rank = i; |
1768 | 0 | } |
1769 | |
|
1770 | 0 | if (lowest_rank == end()) return nullptr; |
1771 | 0 | return *lowest_rank; |
1772 | 0 | } |
1773 | | |
1774 | | // this may not be called from a constructor because of the call to |
1775 | | // shared_from_this(). It's either called when we start() the torrent, or at a |
1776 | | // later time if it's a magnet link, once the metadata is downloaded |
1777 | | void torrent::init() |
1778 | 1.88k | { |
1779 | 1.88k | INVARIANT_CHECK; |
1780 | | |
1781 | 1.88k | TORRENT_ASSERT(is_single_thread()); |
1782 | | |
1783 | | #ifndef TORRENT_DISABLE_LOGGING |
1784 | | debug_log("init torrent: %s", torrent_file().name().c_str()); |
1785 | | #endif |
1786 | | |
1787 | 1.88k | TORRENT_ASSERT(valid_metadata()); |
1788 | 1.88k | TORRENT_ASSERT(m_torrent_file->num_files() > 0); |
1789 | 1.88k | TORRENT_ASSERT(m_torrent_file->total_size() >= 0); |
1790 | | |
1791 | 1.88k | if (int(m_file_priority.size()) > m_torrent_file->num_files()) |
1792 | 84 | m_file_priority.resize(m_torrent_file->num_files()); |
1793 | 1.79k | else if (m_add_torrent_params->flags & torrent_flags::default_dont_download) |
1794 | 398 | m_file_priority.resize(m_torrent_file->num_files(), dont_download); |
1795 | | |
1796 | 1.88k | auto cert = m_torrent_file->ssl_cert(); |
1797 | 1.88k | if (!cert.empty()) |
1798 | 0 | { |
1799 | 0 | m_ssl_torrent = true; |
1800 | 0 | #ifdef TORRENT_SSL_PEERS |
1801 | 0 | init_ssl(cert); |
1802 | 0 | #endif |
1803 | 0 | } |
1804 | | |
1805 | 1.88k | if (m_torrent_file->num_pieces() > piece_picker::max_pieces) |
1806 | 0 | { |
1807 | 0 | set_error(errors::too_many_pieces_in_torrent, torrent_status::error_file_none); |
1808 | 0 | pause(); |
1809 | 0 | return; |
1810 | 0 | } |
1811 | | |
1812 | 1.88k | if (m_torrent_file->num_pieces() == 0) |
1813 | 0 | { |
1814 | 0 | set_error(errors::torrent_invalid_length, torrent_status::error_file_none); |
1815 | 0 | pause(); |
1816 | 0 | return; |
1817 | 0 | } |
1818 | | |
1819 | 1.88k | int const blocks_per_piece |
1820 | 1.88k | = (m_torrent_file->piece_length() + default_block_size - 1) / default_block_size; |
1821 | 1.88k | if (blocks_per_piece > piece_picker::max_blocks_per_piece) |
1822 | 0 | { |
1823 | 0 | set_error(errors::invalid_piece_size, torrent_status::error_file_none); |
1824 | 0 | pause(); |
1825 | 0 | return; |
1826 | 0 | } |
1827 | | |
1828 | | // --- MAPPED FILES --- |
1829 | 1.88k | file_storage const& fs = m_torrent_file->files(); |
1830 | 1.88k | if (m_add_torrent_params) |
1831 | 1.88k | { |
1832 | 1.88k | for (auto const& f : m_add_torrent_params->renamed_files) |
1833 | 0 | { |
1834 | 0 | if (f.first < file_index_t(0) || f.first >= fs.end_file()) continue; |
1835 | 0 | m_torrent_file->rename_file(file_index_t(f.first), f.second); |
1836 | 0 | } |
1837 | 1.88k | } |
1838 | | |
1839 | 1.88k | construct_storage(); |
1840 | | |
1841 | 1.88k | #ifndef TORRENT_DISABLE_SHARE_MODE |
1842 | 1.88k | if (m_share_mode && valid_metadata()) |
1843 | 643 | { |
1844 | | // in share mode, all pieces have their priorities initialized to 0 |
1845 | 643 | m_file_priority.clear(); |
1846 | 643 | m_file_priority.resize(m_torrent_file->num_files(), dont_download); |
1847 | 643 | } |
1848 | 1.88k | #endif |
1849 | | |
1850 | | // it's important to initialize the peers early, because this is what will |
1851 | | // fix up their have-bitmasks to have the correct size |
1852 | | // TODO: 2 add a unit test where we don't have metadata, connect to a peer |
1853 | | // that sends a bitfield that's too large, then we get the metadata |
1854 | 1.88k | if (!m_connections_initialized) |
1855 | 0 | { |
1856 | 0 | m_connections_initialized = true; |
1857 | | // all peer connections have to initialize themselves now that the metadata |
1858 | | // is available |
1859 | | // copy the peer list since peers may disconnect and invalidate |
1860 | | // m_connections as we initialize them |
1861 | 0 | for (auto c : m_connections) |
1862 | 0 | { |
1863 | 0 | auto pc = c->self(); |
1864 | 0 | if (pc->is_disconnecting()) continue; |
1865 | 0 | pc->on_metadata_impl(); |
1866 | 0 | if (pc->is_disconnecting()) continue; |
1867 | 0 | pc->init(); |
1868 | 0 | } |
1869 | 0 | } |
1870 | | |
1871 | | // in case file priorities were passed in via the add_torrent_params |
1872 | | // and also in the case of share mode, we need to update the priorities |
1873 | | // this has to be applied before piece priority |
1874 | 1.88k | if (!m_file_priority.empty()) |
1875 | 1.02k | { |
1876 | | // m_file_priority was loaded from the resume data, this doesn't |
1877 | | // alter any state that needs to be saved in the resume data |
1878 | 1.02k | auto const ns = m_need_save_resume_data; |
1879 | 1.02k | update_piece_priorities(m_file_priority); |
1880 | 1.02k | m_need_save_resume_data = ns; |
1881 | 1.02k | } |
1882 | | |
1883 | 1.88k | if (m_add_torrent_params) |
1884 | 1.88k | { |
1885 | 1.88k | piece_index_t idx(0); |
1886 | 1.88k | if (m_add_torrent_params->piece_priorities.size() > std::size_t(m_torrent_file->num_pieces())) |
1887 | 98 | m_add_torrent_params->piece_priorities.resize(std::size_t(m_torrent_file->num_pieces())); |
1888 | | |
1889 | 1.88k | for (auto prio : m_add_torrent_params->piece_priorities) |
1890 | 1.54k | { |
1891 | 1.54k | if (has_picker() || prio != default_priority) |
1892 | 1.54k | { |
1893 | 1.54k | need_picker(); |
1894 | 1.54k | m_picker->set_piece_priority(idx, prio); |
1895 | 1.54k | } |
1896 | 1.54k | ++idx; |
1897 | 1.54k | } |
1898 | 1.88k | update_gauge(); |
1899 | 1.88k | } |
1900 | | |
1901 | 1.88k | if (m_seed_mode) |
1902 | 333 | { |
1903 | 333 | m_have_all = true; |
1904 | 333 | update_gauge(); |
1905 | 333 | update_state_list(); |
1906 | 333 | update_want_tick(); |
1907 | 333 | } |
1908 | 1.54k | else |
1909 | 1.54k | { |
1910 | 1.54k | need_picker(); |
1911 | | |
1912 | 1.54k | TORRENT_ASSERT(block_size() > 0); |
1913 | | |
1914 | 1.54k | m_padding_bytes = 0; |
1915 | 1.54k | std::vector<piece_index_t> have_pieces; |
1916 | | |
1917 | 1.54k | aux::apply_pad_files(fs, [&](piece_index_t const piece, int const bytes) |
1918 | 1.54k | { |
1919 | 0 | m_padding_bytes += bytes; |
1920 | 0 | if (bytes == fs.piece_size(piece)) |
1921 | 0 | have_pieces.push_back(piece); |
1922 | 0 | m_picker->set_pad_bytes(piece, bytes); |
1923 | 0 | }); |
1924 | | |
1925 | 1.54k | for (auto i : have_pieces) |
1926 | 0 | { |
1927 | | // we may have this piece already, if we picked it up from |
1928 | | // resume data. |
1929 | 0 | if (m_picker->have_piece(i)) continue; |
1930 | | // picker().piece_passed(i); |
1931 | | // TORRENT_ASSERT(picker().have_piece(i)); |
1932 | 0 | we_have(i); |
1933 | 0 | } |
1934 | 1.54k | } |
1935 | | |
1936 | 1.88k | set_state(torrent_status::checking_resume_data); |
1937 | | |
1938 | 1.88k | aux::vector<std::string, file_index_t> links; |
1939 | 1.88k | #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS |
1940 | 1.88k | if (!m_torrent_file->similar_torrents().empty() |
1941 | 1.88k | || !m_torrent_file->collections().empty()) |
1942 | 0 | { |
1943 | 0 | resolve_links res(m_torrent_file); |
1944 | |
|
1945 | 0 | for (auto const& ih : m_torrent_file->similar_torrents()) |
1946 | 0 | { |
1947 | 0 | std::shared_ptr<torrent> t = m_ses.find_torrent(info_hash_t(ih)).lock(); |
1948 | 0 | if (!t) continue; |
1949 | | |
1950 | | // Only attempt to reuse files from torrents that are seeding. |
1951 | | // TODO: this could be optimized by looking up which files are |
1952 | | // complete and just look at those |
1953 | 0 | if (!t->is_seed()) continue; |
1954 | | |
1955 | 0 | res.match(t->get_torrent_file(), t->save_path()); |
1956 | 0 | } |
1957 | 0 | for (auto const& c : m_torrent_file->collections()) |
1958 | 0 | { |
1959 | 0 | std::vector<std::shared_ptr<torrent>> ts = m_ses.find_collection(c); |
1960 | |
|
1961 | 0 | for (auto const& t : ts) |
1962 | 0 | { |
1963 | | // Only attempt to reuse files from torrents that are seeding. |
1964 | | // TODO: this could be optimized by looking up which files are |
1965 | | // complete and just look at those |
1966 | 0 | if (!t->is_seed()) continue; |
1967 | | |
1968 | 0 | res.match(t->get_torrent_file(), t->save_path()); |
1969 | 0 | } |
1970 | 0 | } |
1971 | |
|
1972 | 0 | std::vector<resolve_links::link_t> const& l = res.get_links(); |
1973 | 0 | if (!l.empty()) |
1974 | 0 | { |
1975 | 0 | links.resize(m_torrent_file->files().num_files()); |
1976 | 0 | for (auto const& i : l) |
1977 | 0 | { |
1978 | 0 | if (!i.ti) continue; |
1979 | 0 | links[i.file_idx] = combine_path(i.save_path |
1980 | 0 | , i.ti->files().file_path(i.file_idx)); |
1981 | 0 | } |
1982 | 0 | } |
1983 | 0 | } |
1984 | 1.88k | #endif // TORRENT_DISABLE_MUTABLE_TORRENTS |
1985 | | |
1986 | 1.88k | #if TORRENT_USE_ASSERTS |
1987 | 1.88k | TORRENT_ASSERT(m_outstanding_check_files == false); |
1988 | 1.88k | m_outstanding_check_files = true; |
1989 | 1.88k | #endif |
1990 | | |
1991 | 1.88k | if (!m_add_torrent_params || !(m_add_torrent_params->flags & torrent_flags::no_verify_files)) |
1992 | 806 | { |
1993 | 806 | m_ses.disk_thread().async_check_files( |
1994 | 806 | m_storage, m_add_torrent_params ? m_add_torrent_params.get() : nullptr |
1995 | 806 | , std::move(links), [self = shared_from_this()](status_t st, storage_error const& error) |
1996 | 806 | { self->on_resume_data_checked(st, error); }); |
1997 | | #ifndef TORRENT_DISABLE_LOGGING |
1998 | | debug_log("init, async_check_files"); |
1999 | | #endif |
2000 | 806 | m_ses.deferred_submit_jobs(); |
2001 | 806 | } |
2002 | 1.07k | else |
2003 | 1.07k | { |
2004 | 1.07k | on_resume_data_checked(status_t::no_error, storage_error{}); |
2005 | 1.07k | } |
2006 | | |
2007 | 1.88k | update_want_peers(); |
2008 | 1.88k | update_want_tick(); |
2009 | | |
2010 | | // this will remove the piece picker, if we're done with it |
2011 | 1.88k | maybe_done_flushing(); |
2012 | | |
2013 | 1.88k | m_torrent_initialized = true; |
2014 | 1.88k | } |
2015 | | |
2016 | | bt_peer_connection* torrent::find_introducer(tcp::endpoint const& ep) const |
2017 | 0 | { |
2018 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
2019 | 0 | for (auto pe : m_connections) |
2020 | 0 | { |
2021 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
2022 | 0 | if (pe->type() != connection_type::bittorrent) continue; |
2023 | 0 | auto* p = static_cast<bt_peer_connection*>(pe); |
2024 | 0 | if (!p->supports_holepunch()) continue; |
2025 | 0 | if (p->was_introduced_by(ep)) return p; |
2026 | 0 | } |
2027 | | #else |
2028 | | TORRENT_UNUSED(ep); |
2029 | | #endif |
2030 | 0 | return nullptr; |
2031 | 0 | } |
2032 | | |
2033 | | bt_peer_connection* torrent::find_peer(tcp::endpoint const& ep) const |
2034 | 0 | { |
2035 | 0 | for (auto p : m_connections) |
2036 | 0 | { |
2037 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
2038 | 0 | if (p->type() != connection_type::bittorrent) continue; |
2039 | 0 | if (p->remote() == ep) return static_cast<bt_peer_connection*>(p); |
2040 | 0 | } |
2041 | 0 | return nullptr; |
2042 | 0 | } |
2043 | | |
2044 | | peer_connection* torrent::find_peer(peer_id const& pid) |
2045 | 282 | { |
2046 | 282 | for (auto p : m_connections) |
2047 | 282 | { |
2048 | 282 | if (p->pid() == pid) return p; |
2049 | 282 | } |
2050 | 282 | return nullptr; |
2051 | 282 | } |
2052 | | |
2053 | | bool torrent::is_self_connection(peer_id const& pid) const |
2054 | 282 | { |
2055 | 282 | return m_outgoing_pids.count(pid) > 0; |
2056 | 282 | } |
2057 | | |
2058 | | void torrent::on_resume_data_checked(status_t status |
2059 | 2.95k | , storage_error const& error) try |
2060 | 2.95k | { |
2061 | 2.95k | #if TORRENT_USE_ASSERTS |
2062 | 2.95k | TORRENT_ASSERT(m_outstanding_check_files); |
2063 | 2.95k | m_outstanding_check_files = false; |
2064 | 2.95k | #endif |
2065 | | |
2066 | | // when applying some of the resume data to the torrent, we will |
2067 | | // trigger calls that set m_need_save_resume_data, even though we're |
2068 | | // just applying the state of the resume data we loaded with. We don't |
2069 | | // want anything in this function to affect the state of |
2070 | | // m_need_save_resume_data, so we save it in a local variable and reset |
2071 | | // it at the end of the function. |
2072 | 2.95k | auto const need_save_resume_data = m_need_save_resume_data; |
2073 | | |
2074 | 2.95k | TORRENT_ASSERT(is_single_thread()); |
2075 | | |
2076 | 2.95k | if (m_abort) return; |
2077 | | |
2078 | 2.15k | if ((status & status_t::oversized_file) != status_t{}) |
2079 | 0 | { |
2080 | | // clear the flag |
2081 | 0 | status = status & ~status_t::oversized_file; |
2082 | 0 | if (m_ses.alerts().should_post<oversized_file_alert>()) |
2083 | 0 | m_ses.alerts().emplace_alert<oversized_file_alert>(get_handle()); |
2084 | 0 | } |
2085 | | |
2086 | 2.15k | if (status == status_t::fatal_disk_error) |
2087 | 0 | { |
2088 | 0 | TORRENT_ASSERT(m_outstanding_check_files == false); |
2089 | 0 | handle_disk_error("check_resume_data", error); |
2090 | 0 | auto_managed(false); |
2091 | 0 | pause(); |
2092 | 0 | } |
2093 | | |
2094 | 2.15k | state_updated(); |
2095 | | |
2096 | 2.15k | if (m_add_torrent_params) |
2097 | 1.07k | { |
2098 | | // --- PEERS --- |
2099 | | |
2100 | 1.07k | for (auto const& p : m_add_torrent_params->peers) |
2101 | 0 | { |
2102 | 0 | add_peer(p , peer_info::resume_data); |
2103 | 0 | } |
2104 | | |
2105 | | #ifndef TORRENT_DISABLE_LOGGING |
2106 | | if (should_log() && !m_add_torrent_params->peers.empty()) |
2107 | | { |
2108 | | std::string str; |
2109 | | for (auto const& peer : m_add_torrent_params->peers) |
2110 | | { |
2111 | | str += peer.address().to_string(); |
2112 | | str += ' '; |
2113 | | } |
2114 | | debug_log("resume-checked add_peer() [ %s] connect-candidates: %d" |
2115 | | , str.c_str(), m_peer_list |
2116 | | ? m_peer_list->num_connect_candidates() : -1); |
2117 | | } |
2118 | | #endif |
2119 | | |
2120 | 1.07k | for (auto const& p : m_add_torrent_params->banned_peers) |
2121 | 0 | { |
2122 | 0 | torrent_peer* peer = add_peer(p, peer_info::resume_data); |
2123 | 0 | if (peer) ban_peer(peer); |
2124 | 0 | } |
2125 | | |
2126 | 1.07k | if (!m_add_torrent_params->peers.empty() |
2127 | 1.07k | || !m_add_torrent_params->banned_peers.empty()) |
2128 | 0 | { |
2129 | 0 | update_want_peers(); |
2130 | 0 | } |
2131 | | |
2132 | | #ifndef TORRENT_DISABLE_LOGGING |
2133 | | if (m_peer_list && m_peer_list->num_peers() > 0) |
2134 | | debug_log("resume added peers (total peers: %d)" |
2135 | | , m_peer_list->num_peers()); |
2136 | | #endif |
2137 | 1.07k | } |
2138 | | |
2139 | | // only report this error if the user actually provided resume data |
2140 | | // (i.e. m_add_torrent_params->have_pieces) |
2141 | 2.15k | if ((error || status != status_t::no_error) |
2142 | 2.15k | && m_add_torrent_params |
2143 | 2.15k | && aux::contains_resume_data(*m_add_torrent_params) |
2144 | 2.15k | && m_ses.alerts().should_post<fastresume_rejected_alert>()) |
2145 | 0 | { |
2146 | 0 | m_ses.alerts().emplace_alert<fastresume_rejected_alert>(get_handle() |
2147 | 0 | , error.ec |
2148 | 0 | , resolve_filename(error.file()) |
2149 | 0 | , error.operation); |
2150 | 0 | } |
2151 | | |
2152 | | #ifndef TORRENT_DISABLE_LOGGING |
2153 | | if (should_log()) |
2154 | | { |
2155 | | if (status != status_t::no_error || error) |
2156 | | { |
2157 | | debug_log("fastresume data rejected: ret: %d (%d) op: %s file: %d %s" |
2158 | | , static_cast<int>(status), error.ec.value() |
2159 | | , operation_name(error.operation) |
2160 | | , static_cast<int>(error.file()) |
2161 | | , error.ec.message().c_str()); |
2162 | | } |
2163 | | else |
2164 | | { |
2165 | | debug_log("fastresume data accepted"); |
2166 | | } |
2167 | | } |
2168 | | #endif |
2169 | | |
2170 | 2.15k | bool should_start_full_check = (status != status_t::no_error); |
2171 | | |
2172 | | // if we got a partial pieces bitfield, it means we were in the middle of |
2173 | | // checking this torrent. pick it up where we left off |
2174 | 2.15k | if (status == status_t::no_error |
2175 | 2.15k | && m_add_torrent_params |
2176 | 2.15k | && !m_add_torrent_params->have_pieces.empty() |
2177 | 2.15k | && m_add_torrent_params->have_pieces.size() < m_torrent_file->num_pieces()) |
2178 | 156 | { |
2179 | 156 | m_checking_piece = m_num_checked_pieces |
2180 | 156 | = m_add_torrent_params->have_pieces.end_index(); |
2181 | 156 | should_start_full_check = true; |
2182 | 156 | } |
2183 | | |
2184 | | // if ret != 0, it means we need a full check. We don't necessarily need |
2185 | | // that when the resume data check fails. For instance, if the resume data |
2186 | | // is incorrect, but we don't have any files, we skip the check and initialize |
2187 | | // the storage to not have anything. |
2188 | 2.15k | if (status == status_t::no_error) |
2189 | 1.07k | { |
2190 | | // there are either no files for this torrent |
2191 | | // or the resume_data was accepted |
2192 | | |
2193 | 1.07k | if (m_seed_mode) |
2194 | 179 | { |
2195 | 179 | m_have_all = true; |
2196 | 179 | update_gauge(); |
2197 | 179 | update_state_list(); |
2198 | | |
2199 | 179 | if (!error && m_add_torrent_params) |
2200 | 179 | { |
2201 | 179 | int const num_pieces2 = std::min(m_add_torrent_params->verified_pieces.size() |
2202 | 179 | , torrent_file().num_pieces()); |
2203 | 179 | for (piece_index_t i = piece_index_t(0); |
2204 | 432 | i < piece_index_t(num_pieces2); ++i) |
2205 | 253 | { |
2206 | 253 | if (!m_add_torrent_params->verified_pieces[i]) continue; |
2207 | 68 | m_verified.set_bit(i); |
2208 | 68 | } |
2209 | 179 | } |
2210 | 179 | } |
2211 | 897 | else if (!error && m_add_torrent_params) |
2212 | 897 | { |
2213 | | // --- PIECES --- |
2214 | | |
2215 | 897 | int const num_pieces = std::min(m_add_torrent_params->have_pieces.size() |
2216 | 897 | , torrent_file().num_pieces()); |
2217 | 2.12k | for (piece_index_t i = piece_index_t(0); i < piece_index_t(num_pieces); ++i) |
2218 | 1.22k | { |
2219 | 1.22k | if (!m_add_torrent_params->have_pieces[i]) continue; |
2220 | 663 | need_picker(); |
2221 | 663 | m_picker->piece_flushed(i); |
2222 | 663 | inc_stats_counter(counters::num_piece_passed); |
2223 | 663 | update_gauge(); |
2224 | 663 | we_have(i, true); |
2225 | 663 | } |
2226 | | |
2227 | | // --- UNFINISHED PIECES --- |
2228 | | |
2229 | 897 | int const num_blocks_per_piece = torrent_file().blocks_per_piece(); |
2230 | | |
2231 | 897 | for (auto const& p : m_add_torrent_params->unfinished_pieces) |
2232 | 1.43k | { |
2233 | 1.43k | piece_index_t const piece = p.first; |
2234 | 1.43k | bitfield const& blocks = p.second; |
2235 | | |
2236 | 1.43k | if (piece < piece_index_t(0) || piece >= torrent_file().end_piece()) |
2237 | 647 | { |
2238 | 647 | continue; |
2239 | 647 | } |
2240 | | |
2241 | 784 | if (have_piece(piece)) |
2242 | 34 | continue; |
2243 | | |
2244 | | // being in seed mode and missing a piece is not compatible. |
2245 | | // Leave seed mode if that happens |
2246 | 750 | if (m_seed_mode) leave_seed_mode(seed_mode_t::skip_checking); |
2247 | | |
2248 | 750 | if (has_picker() && m_picker->have_piece(piece)) |
2249 | 0 | { |
2250 | 0 | m_picker->we_dont_have(piece); |
2251 | 0 | update_gauge(); |
2252 | 0 | } |
2253 | | |
2254 | 750 | need_picker(); |
2255 | | |
2256 | 750 | const int num_bits = std::min(num_blocks_per_piece, int(blocks.size())); |
2257 | 8.39k | for (int k = 0; k < num_bits; ++k) |
2258 | 7.64k | { |
2259 | 7.64k | if (blocks.get_bit(k)) |
2260 | 6.19k | { |
2261 | 6.19k | m_picker->mark_as_finished(piece_block(piece, k), nullptr); |
2262 | 6.19k | } |
2263 | 7.64k | } |
2264 | 750 | if (m_picker->is_piece_finished(piece)) |
2265 | 0 | { |
2266 | 0 | verify_piece(piece); |
2267 | 0 | } |
2268 | 750 | } |
2269 | 897 | } |
2270 | 1.07k | } |
2271 | 1.07k | else |
2272 | 1.07k | { |
2273 | 1.07k | m_seed_mode = false; |
2274 | | // either the fastresume data was rejected or there are |
2275 | | // some files |
2276 | 1.07k | m_have_all = false; |
2277 | 1.07k | update_gauge(); |
2278 | 1.07k | update_state_list(); |
2279 | 1.07k | } |
2280 | | |
2281 | 2.15k | if (should_start_full_check) |
2282 | 156 | { |
2283 | 156 | stop_announcing(); |
2284 | 156 | set_state(torrent_status::checking_files); |
2285 | 156 | if (should_check_files()) start_checking(); |
2286 | | |
2287 | | // start the checking right away (potentially) |
2288 | 156 | m_ses.trigger_auto_manage(); |
2289 | 156 | } |
2290 | 1.99k | else |
2291 | 1.99k | { |
2292 | 1.99k | files_checked(); |
2293 | 1.99k | } |
2294 | | |
2295 | | // this will remove the piece picker, if we're done with it |
2296 | 2.15k | maybe_done_flushing(); |
2297 | 2.15k | TORRENT_ASSERT(m_outstanding_check_files == false); |
2298 | 2.15k | m_add_torrent_params.reset(); |
2299 | | |
2300 | | // restore m_need_save_resume_data to its state when we entered this |
2301 | | // function. |
2302 | 2.15k | m_need_save_resume_data = need_save_resume_data; |
2303 | 2.15k | } |
2304 | 2.95k | catch (...) { handle_exception(); } |
2305 | | |
2306 | | void torrent::force_recheck() |
2307 | 0 | { |
2308 | 0 | INVARIANT_CHECK; |
2309 | |
|
2310 | 0 | if (!valid_metadata()) return; |
2311 | | |
2312 | | // if the torrent is already queued to check its files |
2313 | | // don't do anything |
2314 | 0 | if (should_check_files() |
2315 | 0 | || m_state == torrent_status::checking_resume_data) |
2316 | 0 | return; |
2317 | | |
2318 | 0 | clear_error(); |
2319 | |
|
2320 | 0 | disconnect_all(errors::stopping_torrent, operation_t::bittorrent); |
2321 | 0 | stop_announcing(); |
2322 | | |
2323 | | // we're checking everything anyway, no point in assuming we are a seed |
2324 | | // now. |
2325 | 0 | leave_seed_mode(seed_mode_t::skip_checking); |
2326 | | |
2327 | | // forget that we have any pieces |
2328 | 0 | m_have_all = false; |
2329 | | |
2330 | | // removing the piece picker will clear the user priorities |
2331 | | // instead, just clear which pieces we have |
2332 | 0 | if (m_picker) |
2333 | 0 | { |
2334 | 0 | m_picker->resize(m_torrent_file->total_size(), m_torrent_file->piece_length()); |
2335 | |
|
2336 | 0 | m_file_progress.clear(); |
2337 | 0 | m_file_progress.init(picker(), m_torrent_file->files()); |
2338 | 0 | } |
2339 | | |
2340 | | // assume that we don't have anything |
2341 | 0 | m_files_checked = false; |
2342 | |
|
2343 | 0 | update_gauge(); |
2344 | 0 | update_want_tick(); |
2345 | 0 | set_state(torrent_status::checking_resume_data); |
2346 | |
|
2347 | 0 | set_queue_position(last_pos); |
2348 | |
|
2349 | 0 | TORRENT_ASSERT(m_outstanding_check_files == false); |
2350 | 0 | m_add_torrent_params.reset(); |
2351 | | |
2352 | | // this will clear the stat cache, to make us actually query the |
2353 | | // filesystem for files again |
2354 | 0 | m_ses.disk_thread().async_release_files(m_storage); |
2355 | |
|
2356 | 0 | m_ses.disk_thread().async_check_files(m_storage, nullptr |
2357 | 0 | , {}, [self = shared_from_this()](status_t st, storage_error const& error) |
2358 | 0 | { self->on_force_recheck(st, error); }); |
2359 | 0 | m_ses.deferred_submit_jobs(); |
2360 | 0 | } |
2361 | | |
2362 | 0 | void torrent::on_force_recheck(status_t status, storage_error const& error) try |
2363 | 0 | { |
2364 | 0 | TORRENT_ASSERT(is_single_thread()); |
2365 | | |
2366 | | // hold a reference until this function returns |
2367 | 0 | state_updated(); |
2368 | |
|
2369 | 0 | if (m_abort) return; |
2370 | | |
2371 | 0 | if ((status & status_t::oversized_file) != status_t{}) |
2372 | 0 | { |
2373 | | // clear the flag |
2374 | 0 | status = status & ~status_t::oversized_file; |
2375 | 0 | if (m_ses.alerts().should_post<oversized_file_alert>()) |
2376 | 0 | m_ses.alerts().emplace_alert<oversized_file_alert>(get_handle()); |
2377 | 0 | } |
2378 | |
|
2379 | 0 | if (error) |
2380 | 0 | { |
2381 | 0 | handle_disk_error("force_recheck", error); |
2382 | 0 | return; |
2383 | 0 | } |
2384 | 0 | if (status == status_t::no_error) |
2385 | 0 | { |
2386 | | // if there are no files, just start |
2387 | 0 | files_checked(); |
2388 | 0 | } |
2389 | 0 | else |
2390 | 0 | { |
2391 | 0 | m_progress_ppm = 0; |
2392 | 0 | m_checking_piece = piece_index_t(0); |
2393 | 0 | m_num_checked_pieces = piece_index_t(0); |
2394 | |
|
2395 | 0 | set_state(torrent_status::checking_files); |
2396 | 0 | if (m_auto_managed) pause(torrent_handle::graceful_pause); |
2397 | 0 | if (should_check_files()) start_checking(); |
2398 | 0 | else m_ses.trigger_auto_manage(); |
2399 | 0 | } |
2400 | 0 | } |
2401 | 0 | catch (...) { handle_exception(); } |
2402 | | |
2403 | 242 | void torrent::start_checking() try |
2404 | 242 | { |
2405 | 242 | TORRENT_ASSERT(should_check_files()); |
2406 | | |
2407 | 242 | int num_outstanding = settings().get_int(settings_pack::checking_mem_usage) * block_size() |
2408 | 242 | / m_torrent_file->piece_length(); |
2409 | | // if we only keep a single read operation in-flight at a time, we suffer |
2410 | | // significant performance degradation. Always keep at least 4 jobs |
2411 | | // outstanding per hasher thread |
2412 | 242 | int const min_outstanding |
2413 | 242 | = std::max(1, settings().get_int(settings_pack::hashing_threads)) * 2; |
2414 | 242 | if (num_outstanding < min_outstanding) num_outstanding = min_outstanding; |
2415 | | |
2416 | | // subtract the number of pieces we already have outstanding |
2417 | 242 | num_outstanding -= (static_cast<int>(m_checking_piece) |
2418 | 242 | - static_cast<int>(m_num_checked_pieces)); |
2419 | 242 | if (num_outstanding <= 0) return; |
2420 | | |
2421 | | // we might already have some outstanding jobs, if we were paused and |
2422 | | // resumed quickly, before the outstanding jobs completed |
2423 | 242 | if (m_checking_piece >= m_torrent_file->end_piece()) |
2424 | 0 | { |
2425 | | #ifndef TORRENT_DISABLE_LOGGING |
2426 | | debug_log("start_checking, checking_piece >= num_pieces. %d >= %d" |
2427 | | , static_cast<int>(m_checking_piece), m_torrent_file->num_pieces()); |
2428 | | #endif |
2429 | 0 | return; |
2430 | 0 | } |
2431 | | |
2432 | 696 | for (int i = 0; i < num_outstanding; ++i) |
2433 | 469 | { |
2434 | 469 | if (has_picker()) |
2435 | 428 | { |
2436 | | // skip pieces we already have |
2437 | 428 | while (m_checking_piece < m_torrent_file->end_piece() |
2438 | 428 | && m_picker->have_piece(m_checking_piece)) |
2439 | 0 | { |
2440 | 0 | ++m_checking_piece; |
2441 | 0 | ++m_num_checked_pieces; |
2442 | 0 | } |
2443 | 428 | } |
2444 | | |
2445 | 469 | if (m_checking_piece >= m_torrent_file->end_piece()) break; |
2446 | | |
2447 | 469 | auto flags = disk_interface::sequential_access | disk_interface::volatile_read; |
2448 | 469 | if (torrent_file().info_hashes().has_v1()) |
2449 | 469 | flags |= disk_interface::v1_hash; |
2450 | 469 | aux::vector<sha256_hash> hashes; |
2451 | 469 | if (torrent_file().info_hashes().has_v2()) |
2452 | 469 | hashes.resize(torrent_file().orig_files().blocks_in_piece2(m_checking_piece)); |
2453 | | |
2454 | 469 | span<sha256_hash> v2_span(hashes); |
2455 | 469 | m_ses.disk_thread().async_hash(m_storage, m_checking_piece, v2_span, flags |
2456 | 469 | , [self = shared_from_this(), hashes1 = std::move(hashes)] |
2457 | 469 | (piece_index_t p, sha1_hash const& h, storage_error const& error) mutable |
2458 | 469 | { self->on_piece_hashed(std::move(hashes1), p, h, error); }); |
2459 | 469 | ++m_checking_piece; |
2460 | 469 | if (m_checking_piece >= m_torrent_file->end_piece()) break; |
2461 | 469 | } |
2462 | 242 | m_ses.deferred_submit_jobs(); |
2463 | | #ifndef TORRENT_DISABLE_LOGGING |
2464 | | debug_log("start_checking, m_checking_piece: %d" |
2465 | | , static_cast<int>(m_checking_piece)); |
2466 | | #endif |
2467 | 242 | } |
2468 | 242 | catch (...) { handle_exception(); } |
2469 | | |
2470 | | // This is only used for checking of torrents. i.e. force-recheck or initial checking |
2471 | | // of existing files |
2472 | | void torrent::on_piece_hashed(aux::vector<sha256_hash> block_hashes |
2473 | | , piece_index_t const piece, sha1_hash const& piece_hash |
2474 | 469 | , storage_error const& error) try |
2475 | 469 | { |
2476 | 469 | TORRENT_ASSERT(is_single_thread()); |
2477 | 469 | INVARIANT_CHECK; |
2478 | | |
2479 | 469 | if (m_abort) return; |
2480 | 0 | if (m_deleted) return; |
2481 | | |
2482 | 0 | state_updated(); |
2483 | |
|
2484 | 0 | ++m_num_checked_pieces; |
2485 | |
|
2486 | 0 | if (error) |
2487 | 0 | { |
2488 | 0 | if (error.ec == boost::system::errc::no_such_file_or_directory |
2489 | 0 | || error.ec == boost::asio::error::eof |
2490 | 0 | || error.ec == lt::errors::file_too_short |
2491 | | #ifdef TORRENT_WINDOWS |
2492 | | || error.ec == error_code(ERROR_HANDLE_EOF, system_category()) |
2493 | | #endif |
2494 | 0 | ) |
2495 | 0 | { |
2496 | 0 | TORRENT_ASSERT(error.file() >= file_index_t(0)); |
2497 | | |
2498 | | // skip this file by updating m_checking_piece to the first piece following it |
2499 | 0 | file_storage const& st = m_torrent_file->files(); |
2500 | 0 | std::int64_t file_size = st.file_size(error.file()); |
2501 | 0 | piece_index_t last = st.map_file(error.file(), file_size, 0).piece; |
2502 | 0 | if (m_checking_piece < last) |
2503 | 0 | { |
2504 | 0 | int diff = static_cast<int>(last) - static_cast<int>(m_checking_piece); |
2505 | 0 | m_num_checked_pieces = piece_index_t(static_cast<int>(m_num_checked_pieces) + diff); |
2506 | 0 | m_checking_piece = last; |
2507 | 0 | } |
2508 | 0 | } |
2509 | 0 | else |
2510 | 0 | { |
2511 | 0 | m_checking_piece = piece_index_t{0}; |
2512 | 0 | m_num_checked_pieces = piece_index_t{0}; |
2513 | 0 | if (m_ses.alerts().should_post<file_error_alert>()) |
2514 | 0 | m_ses.alerts().emplace_alert<file_error_alert>(error.ec, |
2515 | 0 | resolve_filename(error.file()), error.operation, get_handle()); |
2516 | |
|
2517 | | #ifndef TORRENT_DISABLE_LOGGING |
2518 | | if (should_log()) |
2519 | | { |
2520 | | debug_log("on_piece_hashed, fatal disk error: (%d) %s", error.ec.value() |
2521 | | , error.ec.message().c_str()); |
2522 | | } |
2523 | | #endif |
2524 | 0 | auto_managed(false); |
2525 | 0 | pause(); |
2526 | 0 | set_error(error.ec, error.file()); |
2527 | | |
2528 | | // recalculate auto-managed torrents sooner |
2529 | | // in order to start checking the next torrent |
2530 | 0 | m_ses.trigger_auto_manage(); |
2531 | 0 | return; |
2532 | 0 | } |
2533 | 0 | } |
2534 | | |
2535 | 0 | m_progress_ppm = std::uint32_t(std::int64_t(static_cast<int>(m_num_checked_pieces)) |
2536 | 0 | * 1000000 / torrent_file().num_pieces()); |
2537 | |
|
2538 | 0 | boost::tribool hash_passed[2] |
2539 | 0 | = { boost::indeterminate, boost::indeterminate }; |
2540 | |
|
2541 | 0 | if (!settings().get_bool(settings_pack::disable_hash_checks)) |
2542 | 0 | { |
2543 | 0 | if (torrent_file().info_hashes().has_v1()) |
2544 | 0 | hash_passed[0] = piece_hash == m_torrent_file->hash_for_piece(piece); |
2545 | | |
2546 | | // if the v1 hash failed the check, don't add the v2 hashes to the |
2547 | | // merkle tree. They are most likely invalid. |
2548 | 0 | if (torrent_file().info_hashes().has_v2() && !bool(hash_passed[0] == false)) |
2549 | 0 | { |
2550 | 0 | hash_passed[1] = on_blocks_hashed(piece, block_hashes); |
2551 | 0 | } |
2552 | 0 | } |
2553 | 0 | else |
2554 | 0 | { |
2555 | 0 | hash_passed[0] = hash_passed[1] = true; |
2556 | 0 | } |
2557 | |
|
2558 | 0 | if ((hash_passed[0] && !hash_passed[1]) || (!hash_passed[0] && hash_passed[1])) |
2559 | 0 | { |
2560 | 0 | handle_inconsistent_hashes(piece); |
2561 | 0 | return; |
2562 | 0 | } |
2563 | 0 | else if (hash_passed[0] || hash_passed[1]) |
2564 | 0 | { |
2565 | 0 | if (has_picker() || !m_have_all) |
2566 | 0 | { |
2567 | 0 | need_picker(); |
2568 | 0 | m_picker->piece_flushed(piece); |
2569 | 0 | set_need_save_resume(torrent_handle::if_download_progress); |
2570 | 0 | update_gauge(); |
2571 | 0 | } |
2572 | 0 | we_have(piece); |
2573 | 0 | } |
2574 | 0 | else if (!error |
2575 | 0 | && boost::indeterminate(hash_passed[0]) |
2576 | 0 | && boost::indeterminate(hash_passed[1])) |
2577 | 0 | { |
2578 | | // The data exists but we don't have the hashes needed to verify |
2579 | | // it yet. This is a special case because we want to say we have |
2580 | | // the piece once the hash is verified and not download the data |
2581 | | // unless the hash check fails. To get this effect we setup the |
2582 | | // piece's state in the piece picker so that it looks like a piece |
2583 | | // which is finished but not hash checked. |
2584 | 0 | need_picker(); |
2585 | 0 | int const blocks_in_piece = m_picker->blocks_in_piece(piece); |
2586 | 0 | for (int i = 0; i < blocks_in_piece; ++i) |
2587 | 0 | m_picker->mark_as_finished(piece_block(piece, i), nullptr); |
2588 | 0 | } |
2589 | | |
2590 | 0 | if (m_checking_piece < m_torrent_file->end_piece() && has_picker()) |
2591 | 0 | { |
2592 | | // skip pieces we already have |
2593 | 0 | while (m_picker->have_piece(m_checking_piece)) |
2594 | 0 | { |
2595 | 0 | ++m_checking_piece; |
2596 | 0 | ++m_num_checked_pieces; |
2597 | 0 | if (m_checking_piece >= m_torrent_file->end_piece()) |
2598 | 0 | { |
2599 | | // actually, we already have outstanding jobs for |
2600 | | // the remaining pieces. We just need to wait for them |
2601 | | // to finish |
2602 | 0 | break; |
2603 | 0 | } |
2604 | 0 | } |
2605 | 0 | } |
2606 | |
|
2607 | 0 | if (m_num_checked_pieces < m_torrent_file->end_piece()) |
2608 | 0 | { |
2609 | | // we paused the checking |
2610 | 0 | if (!should_check_files()) |
2611 | 0 | { |
2612 | | #ifndef TORRENT_DISABLE_LOGGING |
2613 | | debug_log("on_piece_hashed, checking paused"); |
2614 | | #endif |
2615 | 0 | if (m_checking_piece == m_num_checked_pieces) |
2616 | 0 | { |
2617 | | // we are paused, and we just completed the last outstanding job. |
2618 | | // now we can be considered paused |
2619 | 0 | if (alerts().should_post<torrent_paused_alert>()) |
2620 | 0 | alerts().emplace_alert<torrent_paused_alert>(get_handle()); |
2621 | 0 | } |
2622 | 0 | return; |
2623 | 0 | } |
2624 | | |
2625 | 0 | if (m_checking_piece >= m_torrent_file->end_piece()) |
2626 | 0 | return; |
2627 | | |
2628 | 0 | auto flags = disk_interface::sequential_access | disk_interface::volatile_read; |
2629 | |
|
2630 | 0 | if (torrent_file().info_hashes().has_v1()) |
2631 | 0 | flags |= disk_interface::v1_hash; |
2632 | 0 | if (torrent_file().info_hashes().has_v2()) |
2633 | 0 | block_hashes.resize(torrent_file().orig_files().blocks_in_piece2(m_checking_piece)); |
2634 | |
|
2635 | 0 | span<sha256_hash> v2_span(block_hashes); |
2636 | 0 | m_ses.disk_thread().async_hash(m_storage, m_checking_piece, v2_span, flags |
2637 | 0 | , [self = shared_from_this(), hashes = std::move(block_hashes)] |
2638 | 0 | (piece_index_t p, sha1_hash const& h, storage_error const& e) |
2639 | 0 | { self->on_piece_hashed(std::move(hashes), p, h, e); }); |
2640 | 0 | ++m_checking_piece; |
2641 | 0 | m_ses.deferred_submit_jobs(); |
2642 | | #ifndef TORRENT_DISABLE_LOGGING |
2643 | | debug_log("on_piece_hashed, m_checking_piece: %d" |
2644 | | , static_cast<int>(m_checking_piece)); |
2645 | | #endif |
2646 | 0 | return; |
2647 | 0 | } |
2648 | | |
2649 | | #ifndef TORRENT_DISABLE_LOGGING |
2650 | | debug_log("on_piece_hashed, completed"); |
2651 | | #endif |
2652 | | |
2653 | 0 | auto const& pack = settings(); |
2654 | 0 | auto get_int_setting = [&pack](int const name) { |
2655 | 0 | int const v = pack.get_int(name); |
2656 | 0 | if (v < 0) return std::numeric_limits<int>::max(); |
2657 | 0 | return v; |
2658 | 0 | }; |
2659 | 0 | int const limit = std::min({ |
2660 | 0 | get_int_setting(settings_pack::active_downloads) |
2661 | 0 | , get_int_setting(settings_pack::active_seeds) |
2662 | 0 | , get_int_setting(settings_pack::active_limit)}); |
2663 | 0 | int const num_torrents = m_ses.num_torrents(); |
2664 | 0 | if (m_auto_managed && num_torrents > limit) |
2665 | 0 | { |
2666 | | // if we're auto managed and we've reached one of the limits. Assume |
2667 | | // we need to be paused until the auto managed logic runs again |
2668 | | // (which is triggered further down) |
2669 | 0 | set_paused(true); |
2670 | 0 | } |
2671 | | |
2672 | | // we're done checking! (this should cause a call to trigger_auto_manage) |
2673 | 0 | files_checked(); |
2674 | | |
2675 | | // reset the checking state |
2676 | 0 | m_checking_piece = piece_index_t(0); |
2677 | 0 | m_num_checked_pieces = piece_index_t(0); |
2678 | 0 | } |
2679 | 469 | catch (...) { handle_exception(); } |
2680 | | |
2681 | | #if TORRENT_ABI_VERSION == 1 |
2682 | | void torrent::use_interface(std::string net_interfaces) |
2683 | 0 | { |
2684 | 0 | std::shared_ptr<settings_pack> p = std::make_shared<settings_pack>(); |
2685 | 0 | p->set_str(settings_pack::outgoing_interfaces, std::move(net_interfaces)); |
2686 | 0 | m_ses.apply_settings_pack(p); |
2687 | 0 | } |
2688 | | #endif |
2689 | | |
2690 | 0 | void torrent::on_tracker_announce(error_code const& ec) try |
2691 | 0 | { |
2692 | 0 | COMPLETE_ASYNC("tracker::on_tracker_announce"); |
2693 | 0 | TORRENT_ASSERT(is_single_thread()); |
2694 | 0 | TORRENT_ASSERT(m_waiting_tracker > 0); |
2695 | 0 | --m_waiting_tracker; |
2696 | 0 | if (ec) return; |
2697 | 0 | if (m_abort) return; |
2698 | 0 | announce_with_tracker(); |
2699 | 0 | } |
2700 | 0 | catch (...) { handle_exception(); } |
2701 | | |
2702 | | void torrent::lsd_announce() |
2703 | 334 | { |
2704 | 334 | if (m_abort) return; |
2705 | 334 | if (!m_enable_lsd) return; |
2706 | | |
2707 | | // if the files haven't been checked yet, we're |
2708 | | // not ready for peers. Except, if we don't have metadata, |
2709 | | // we need peers to download from |
2710 | 189 | if (!m_files_checked && valid_metadata()) return; |
2711 | | |
2712 | 189 | if (!m_announce_to_lsd) return; |
2713 | | |
2714 | | // private torrents are never announced on LSD |
2715 | 189 | if (m_torrent_file->is_valid() && m_torrent_file->priv()) return; |
2716 | | |
2717 | 189 | #if TORRENT_USE_I2P |
2718 | | // i2p torrents are also never announced on LSD |
2719 | | // unless we allow mixed swarms |
2720 | 189 | if (is_i2p() && !settings().get_bool(settings_pack::allow_i2p_mixed)) |
2721 | 0 | return; |
2722 | 189 | #endif |
2723 | | |
2724 | 189 | if (is_paused()) return; |
2725 | | |
2726 | 189 | if (!m_ses.has_lsd()) return; |
2727 | | |
2728 | | // TODO: this pattern is repeated in a few places. Factor this into |
2729 | | // a function and generalize the concept of a torrent having a |
2730 | | // dedicated listen port |
2731 | 0 | #ifdef TORRENT_SSL_PEERS |
2732 | 0 | int port = is_ssl_torrent() ? m_ses.ssl_listen_port() : m_ses.listen_port(); |
2733 | | #else |
2734 | | int port = m_ses.listen_port(); |
2735 | | #endif |
2736 | | |
2737 | | // announce with the local discovery service |
2738 | 0 | m_torrent_file->info_hashes().for_each([&](sha1_hash const& ih, protocol_version) |
2739 | 0 | { |
2740 | 0 | m_ses.announce_lsd(ih, port); |
2741 | 0 | }); |
2742 | 0 | } |
2743 | | |
2744 | | #ifndef TORRENT_DISABLE_DHT |
2745 | | |
2746 | | void torrent::dht_announce() |
2747 | 0 | { |
2748 | 0 | TORRENT_ASSERT(is_single_thread()); |
2749 | 0 | if (!m_ses.dht()) |
2750 | 0 | { |
2751 | | #ifndef TORRENT_DISABLE_LOGGING |
2752 | | debug_log("DHT: no dht initialized"); |
2753 | | #endif |
2754 | 0 | return; |
2755 | 0 | } |
2756 | 0 | if (!should_announce_dht()) |
2757 | 0 | { |
2758 | | #ifndef TORRENT_DISABLE_LOGGING |
2759 | | if (should_log()) |
2760 | | { |
2761 | | #if TORRENT_USE_I2P |
2762 | | if (is_i2p() && !settings().get_bool(settings_pack::allow_i2p_mixed)) |
2763 | | debug_log("DHT: i2p torrent (and mixed peers not allowed)"); |
2764 | | #endif |
2765 | | if (!m_ses.announce_dht()) |
2766 | | debug_log("DHT: no listen sockets"); |
2767 | | |
2768 | | if (m_torrent_file->is_valid() && !m_files_checked) |
2769 | | debug_log("DHT: files not checked, skipping DHT announce"); |
2770 | | |
2771 | | if (!m_announce_to_dht) |
2772 | | debug_log("DHT: queueing disabled DHT announce"); |
2773 | | |
2774 | | if (m_paused) |
2775 | | debug_log("DHT: torrent paused, no DHT announce"); |
2776 | | |
2777 | | if (!m_enable_dht) |
2778 | | debug_log("DHT: torrent has DHT disabled flag"); |
2779 | | |
2780 | | if (m_torrent_file->is_valid() && m_torrent_file->priv()) |
2781 | | debug_log("DHT: private torrent, no DHT announce"); |
2782 | | |
2783 | | if (settings().get_bool(settings_pack::use_dht_as_fallback)) |
2784 | | { |
2785 | | int const verified_trackers = static_cast<int>(std::count_if( |
2786 | | m_trackers.begin(), m_trackers.end() |
2787 | | , [](aux::announce_entry const& t) { return t.verified; })); |
2788 | | |
2789 | | if (verified_trackers > 0) |
2790 | | debug_log("DHT: only using DHT as fallback, and there are %d working trackers", verified_trackers); |
2791 | | } |
2792 | | } |
2793 | | #endif |
2794 | 0 | return; |
2795 | 0 | } |
2796 | | |
2797 | 0 | TORRENT_ASSERT(!m_paused); |
2798 | |
|
2799 | | #ifndef TORRENT_DISABLE_LOGGING |
2800 | | debug_log("START DHT announce"); |
2801 | | m_dht_start_time = aux::time_now(); |
2802 | | #endif |
2803 | | |
2804 | | // if we're a seed, we tell the DHT for better scrape stats |
2805 | 0 | dht::announce_flags_t flags = is_seed() ? dht::announce::seed : dht::announce_flags_t{}; |
2806 | | |
2807 | | // If this is an SSL torrent the announce needs to specify an SSL |
2808 | | // listen port. DHT nodes only operate on non-SSL ports so SSL |
2809 | | // torrents cannot use implied_port. |
2810 | | // if we allow incoming uTP connections and don't overwrite |
2811 | | // the announced port, set the implied_port argument |
2812 | | // in the announce, this will make the DHT node use |
2813 | | // our source port in the packet as our listen port, which is |
2814 | | // likely more accurate when behind a NAT |
2815 | 0 | const auto announce_port = std::uint16_t(settings().get_int(settings_pack::announce_port)); |
2816 | 0 | if (is_ssl_torrent()) |
2817 | 0 | { |
2818 | 0 | flags |= dht::announce::ssl_torrent; |
2819 | 0 | } |
2820 | 0 | else if (!announce_port && settings().get_bool(settings_pack::enable_incoming_utp)) |
2821 | 0 | { |
2822 | 0 | flags |= dht::announce::implied_port; |
2823 | 0 | } |
2824 | |
|
2825 | 0 | std::weak_ptr<torrent> self(shared_from_this()); |
2826 | 0 | m_torrent_file->info_hashes().for_each([&](sha1_hash const& ih, protocol_version v) |
2827 | 0 | { |
2828 | 0 | m_ses.dht()->announce(ih, announce_port, flags |
2829 | 0 | , std::bind(&torrent::on_dht_announce_response_disp, self, v, _1)); |
2830 | 0 | }); |
2831 | 0 | } |
2832 | | |
2833 | | void torrent::on_dht_announce_response_disp(std::weak_ptr<torrent> const t |
2834 | | , protocol_version const v, std::vector<tcp::endpoint> const& peers) |
2835 | 0 | { |
2836 | 0 | std::shared_ptr<torrent> tor = t.lock(); |
2837 | 0 | if (!tor) return; |
2838 | 0 | tor->on_dht_announce_response(v, peers); |
2839 | 0 | } |
2840 | | |
2841 | | void torrent::on_dht_announce_response(protocol_version const v |
2842 | 0 | , std::vector<tcp::endpoint> const& peers) try |
2843 | 0 | { |
2844 | 0 | TORRENT_ASSERT(is_single_thread()); |
2845 | |
|
2846 | | #ifndef TORRENT_DISABLE_LOGGING |
2847 | | debug_log("END DHT announce (%d ms) (%d peers)" |
2848 | | , int(total_milliseconds(clock_type::now() - m_dht_start_time)) |
2849 | | , int(peers.size())); |
2850 | | #endif |
2851 | |
|
2852 | 0 | if (m_abort) return; |
2853 | 0 | if (peers.empty()) return; |
2854 | | |
2855 | 0 | if (m_ses.alerts().should_post<dht_reply_alert>()) |
2856 | 0 | { |
2857 | 0 | m_ses.alerts().emplace_alert<dht_reply_alert>( |
2858 | 0 | get_handle(), int(peers.size())); |
2859 | 0 | } |
2860 | |
|
2861 | 0 | #if TORRENT_USE_I2P |
2862 | 0 | if (is_i2p() && !settings().get_bool(settings_pack::allow_i2p_mixed)) return; |
2863 | 0 | #endif |
2864 | | |
2865 | 0 | if (torrent_file().priv()) return; |
2866 | | |
2867 | 0 | for (auto& p : peers) |
2868 | 0 | add_peer(p, peer_info::dht, v == protocol_version::V2 ? pex_lt_v2 : pex_flags_t(0)); |
2869 | |
|
2870 | | #ifndef TORRENT_DISABLE_LOGGING |
2871 | | if (should_log() && !peers.empty()) |
2872 | | { |
2873 | | std::string str; |
2874 | | for (auto const& peer : peers) |
2875 | | { |
2876 | | str += peer.address().to_string(); |
2877 | | str += ' '; |
2878 | | } |
2879 | | debug_log("DHT add_peer() [ %s] connect-candidates: %d" |
2880 | | , str.c_str(), m_peer_list |
2881 | | ? m_peer_list->num_connect_candidates() : -1); |
2882 | | } |
2883 | | #endif |
2884 | |
|
2885 | 0 | do_connect_boost(); |
2886 | |
|
2887 | 0 | update_want_peers(); |
2888 | 0 | } |
2889 | 0 | catch (...) { handle_exception(); } |
2890 | | |
2891 | | #endif |
2892 | | |
2893 | | namespace { |
2894 | | void refresh_endpoint_list(aux::session_interface& ses |
2895 | | , std::string const& url |
2896 | | , bool const is_ssl, bool const complete_sent |
2897 | | , std::vector<aux::announce_endpoint>& aeps) |
2898 | 0 | { |
2899 | 0 | #if TORRENT_USE_I2P |
2900 | 0 | if (is_i2p_url(url)) |
2901 | 0 | { |
2902 | 0 | if (aeps.size() > 1) |
2903 | 0 | { |
2904 | 0 | aeps.erase(aeps.begin() + 1, aeps.end()); |
2905 | 0 | } |
2906 | 0 | else if (aeps.empty()) |
2907 | 0 | { |
2908 | 0 | aeps.emplace_back(aux::listen_socket_handle(), complete_sent); |
2909 | 0 | } |
2910 | 0 | return; |
2911 | 0 | } |
2912 | | #else |
2913 | | TORRENT_UNUSED(url); |
2914 | | #endif |
2915 | | |
2916 | | // update the endpoint list by adding entries for new listen sockets |
2917 | | // and removing entries for non-existent ones |
2918 | 0 | std::size_t valid_endpoints = 0; |
2919 | 0 | ses.for_each_listen_socket([&](aux::listen_socket_handle const& s) { |
2920 | 0 | if (s.is_ssl() != is_ssl) |
2921 | 0 | return; |
2922 | 0 | for (auto& aep : aeps) |
2923 | 0 | { |
2924 | 0 | if (aep.socket != s) continue; |
2925 | 0 | if (&aeps[valid_endpoints] != &aep) std::swap(aeps[valid_endpoints], aep); |
2926 | 0 | valid_endpoints++; |
2927 | 0 | return; |
2928 | 0 | } |
2929 | | |
2930 | 0 | aeps.emplace_back(s, complete_sent); |
2931 | 0 | std::swap(aeps[valid_endpoints], aeps.back()); |
2932 | 0 | valid_endpoints++; |
2933 | 0 | }); |
2934 | |
|
2935 | 0 | TORRENT_ASSERT(valid_endpoints <= aeps.size()); |
2936 | 0 | aeps.erase(aeps.begin() + int(valid_endpoints), aeps.end()); |
2937 | 0 | } |
2938 | | } |
2939 | | |
2940 | | namespace |
2941 | | { |
2942 | | struct announce_protocol_state |
2943 | | { |
2944 | | // the tier is kept as INT_MAX until we find the first |
2945 | | // tracker that works, then it's set to that tracker's |
2946 | | // tier. |
2947 | | int tier = INT_MAX; |
2948 | | |
2949 | | // have we sent an announce in this tier yet? |
2950 | | bool sent_announce = false; |
2951 | | |
2952 | | // have we finished sending announces on this listen socket? |
2953 | | bool done = false; |
2954 | | }; |
2955 | | |
2956 | | struct announce_state |
2957 | | { |
2958 | | explicit announce_state(aux::listen_socket_handle s) |
2959 | 0 | : socket(std::move(s)) {} |
2960 | | |
2961 | | aux::listen_socket_handle socket; |
2962 | | |
2963 | | aux::array<announce_protocol_state, num_protocols, protocol_version> state; |
2964 | | }; |
2965 | | } |
2966 | | |
2967 | | void torrent::announce_with_tracker(event_t e) |
2968 | 668 | { |
2969 | 668 | TORRENT_ASSERT(is_single_thread()); |
2970 | 668 | TORRENT_ASSERT(e == event_t::stopped || state() != torrent_status::checking_files); |
2971 | 668 | INVARIANT_CHECK; |
2972 | | |
2973 | 668 | if (m_trackers.empty()) |
2974 | 668 | { |
2975 | | #ifndef TORRENT_DISABLE_LOGGING |
2976 | | debug_log("*** announce: no trackers"); |
2977 | | #endif |
2978 | 668 | return; |
2979 | 668 | } |
2980 | | |
2981 | 0 | if (m_abort) e = event_t::stopped; |
2982 | | |
2983 | | // having stop_tracker_timeout <= 0 means that there is |
2984 | | // no need to send any request to trackers or trigger any |
2985 | | // related logic when the event is stopped |
2986 | 0 | if (e == event_t::stopped |
2987 | 0 | && settings().get_int(settings_pack::stop_tracker_timeout) <= 0) |
2988 | 0 | { |
2989 | | #ifndef TORRENT_DISABLE_LOGGING |
2990 | | debug_log("*** announce: event == stopped && stop_tracker_timeout <= 0"); |
2991 | | #endif |
2992 | 0 | return; |
2993 | 0 | } |
2994 | | |
2995 | | // if we're not announcing to trackers, only allow |
2996 | | // stopping |
2997 | 0 | if (e != event_t::stopped && !m_announce_to_trackers) |
2998 | 0 | { |
2999 | | #ifndef TORRENT_DISABLE_LOGGING |
3000 | | debug_log("*** announce: event != stopped && !m_announce_to_trackers"); |
3001 | | #endif |
3002 | 0 | return; |
3003 | 0 | } |
3004 | | |
3005 | | // if we're not allowing peers, there's no point in announcing |
3006 | 0 | if (e != event_t::stopped && m_paused) |
3007 | 0 | { |
3008 | | #ifndef TORRENT_DISABLE_LOGGING |
3009 | | debug_log("*** announce: event != stopped && m_paused"); |
3010 | | #endif |
3011 | 0 | return; |
3012 | 0 | } |
3013 | | |
3014 | 0 | TORRENT_ASSERT(!m_paused || e == event_t::stopped); |
3015 | |
|
3016 | 0 | if (e == event_t::none && is_finished() && !is_seed()) |
3017 | 0 | e = event_t::paused; |
3018 | |
|
3019 | 0 | tracker_request req; |
3020 | 0 | if (settings().get_bool(settings_pack::apply_ip_filter_to_trackers) |
3021 | 0 | && m_apply_ip_filter) |
3022 | 0 | { |
3023 | 0 | req.filter = m_ip_filter; |
3024 | 0 | } |
3025 | |
|
3026 | 0 | req.private_torrent = m_torrent_file->priv(); |
3027 | |
|
3028 | 0 | req.pid = m_peer_id; |
3029 | 0 | req.downloaded = m_stat.total_payload_download() - m_total_failed_bytes; |
3030 | 0 | req.uploaded = m_stat.total_payload_upload(); |
3031 | 0 | req.corrupt = m_total_failed_bytes; |
3032 | 0 | req.left = value_or(bytes_left(), 16*1024); |
3033 | 0 | #ifdef TORRENT_SSL_PEERS |
3034 | | // if this torrent contains an SSL certificate, make sure |
3035 | | // any SSL tracker presents a certificate signed by it |
3036 | 0 | req.ssl_ctx = m_ssl_ctx.get(); |
3037 | 0 | #endif |
3038 | |
|
3039 | 0 | req.redundant = m_total_redundant_bytes; |
3040 | | // exclude redundant bytes if we should |
3041 | 0 | if (!settings().get_bool(settings_pack::report_true_downloaded)) |
3042 | 0 | { |
3043 | 0 | req.downloaded -= m_total_redundant_bytes; |
3044 | | |
3045 | | // if the torrent is complete we know that all incoming pieces will be |
3046 | | // marked redundant so add them to the redundant count |
3047 | | // this is mainly needed to cover the case where a torrent has just completed |
3048 | | // but still has partially downloaded pieces |
3049 | | // if the incoming pieces are not accounted for it could cause the downloaded |
3050 | | // amount to exceed the total size of the torrent which upsets some trackers |
3051 | 0 | if (is_seed()) |
3052 | 0 | { |
3053 | 0 | for (auto c : m_connections) |
3054 | 0 | { |
3055 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
3056 | 0 | auto const pbp = c->downloading_piece_progress(); |
3057 | 0 | if (pbp.bytes_downloaded > 0) |
3058 | 0 | { |
3059 | 0 | req.downloaded -= pbp.bytes_downloaded; |
3060 | 0 | req.redundant += pbp.bytes_downloaded; |
3061 | 0 | } |
3062 | 0 | } |
3063 | 0 | } |
3064 | 0 | } |
3065 | 0 | if (req.downloaded < 0) req.downloaded = 0; |
3066 | |
|
3067 | 0 | req.event = e; |
3068 | | |
3069 | | // since sending our IPv4/v6 address to the tracker may be sensitive. Only |
3070 | | // do that if we're not in anonymous mode and if it's a private torrent |
3071 | 0 | if (!settings().get_bool(settings_pack::anonymous_mode) |
3072 | 0 | && m_torrent_file |
3073 | 0 | && m_torrent_file->priv()) |
3074 | 0 | { |
3075 | 0 | m_ses.for_each_listen_socket([&](aux::listen_socket_handle const& s) |
3076 | 0 | { |
3077 | 0 | if (s.is_ssl() != is_ssl_torrent()) return; |
3078 | 0 | tcp::endpoint const ep = s.get_local_endpoint(); |
3079 | 0 | if (ep.address().is_unspecified()) return; |
3080 | 0 | if (aux::is_v6(ep)) |
3081 | 0 | { |
3082 | 0 | if (!aux::is_local(ep.address()) && !ep.address().is_loopback()) |
3083 | 0 | req.ipv6.push_back(ep.address().to_v6()); |
3084 | 0 | } |
3085 | 0 | else |
3086 | 0 | { |
3087 | 0 | if (!aux::is_local(ep.address()) && !ep.address().is_loopback()) |
3088 | 0 | req.ipv4.push_back(ep.address().to_v4()); |
3089 | 0 | } |
3090 | 0 | }); |
3091 | 0 | } |
3092 | | |
3093 | | // if we are aborting. we don't want any new peers |
3094 | 0 | req.num_want = (req.event == event_t::stopped) |
3095 | 0 | ? 0 : settings().get_int(settings_pack::num_want); |
3096 | | |
3097 | | // some older versions of clang had a bug where it would fire this warning here |
3098 | 0 | #ifdef __clang__ |
3099 | 0 | #pragma clang diagnostic push |
3100 | 0 | #pragma clang diagnostic ignored "-Wmissing-braces" |
3101 | 0 | #endif |
3102 | 0 | aux::array<bool const, num_protocols, protocol_version> const supports_protocol |
3103 | 0 | { { |
3104 | 0 | m_info_hash.has_v1(), |
3105 | 0 | m_info_hash.has_v2() |
3106 | 0 | } }; |
3107 | 0 | #ifdef __clang__ |
3108 | 0 | #pragma clang diagnostic pop |
3109 | 0 | #endif |
3110 | |
|
3111 | 0 | time_point32 const now = aux::time_now32(); |
3112 | | |
3113 | | // each listen socket gets its own announce state |
3114 | | // so that each one should get at least one announce |
3115 | 0 | std::vector<announce_state> listen_socket_states; |
3116 | |
|
3117 | 0 | bool const announce_to_all_tiers = settings().get_bool(settings_pack::announce_to_all_tiers); |
3118 | 0 | bool const announce_to_all_trackers = settings().get_bool(settings_pack::announce_to_all_trackers); |
3119 | | #ifndef TORRENT_DISABLE_LOGGING |
3120 | | int idx = -1; |
3121 | | if (should_log()) |
3122 | | { |
3123 | | debug_log("*** announce: " |
3124 | | "[ announce_to_all_tiers: %d announce_to_all_trackers: %d num_trackers: %d ]" |
3125 | | , announce_to_all_tiers |
3126 | | , announce_to_all_trackers |
3127 | | , int(m_trackers.size())); |
3128 | | } |
3129 | | #endif |
3130 | |
|
3131 | 0 | for (auto& ae : m_trackers) |
3132 | 0 | { |
3133 | | #ifndef TORRENT_DISABLE_LOGGING |
3134 | | ++idx; |
3135 | | #endif |
3136 | 0 | refresh_endpoint_list(m_ses, ae.url, is_ssl_torrent(), bool(m_complete_sent), ae.endpoints); |
3137 | | |
3138 | | // if trackerid is not specified for tracker use default one, probably set explicitly |
3139 | 0 | req.trackerid = ae.trackerid.empty() ? m_trackerid : ae.trackerid; |
3140 | 0 | req.url = ae.url; |
3141 | |
|
3142 | 0 | #if TORRENT_USE_I2P |
3143 | 0 | if (is_i2p_url(req.url)) |
3144 | 0 | { |
3145 | 0 | req.kind |= tracker_request::i2p; |
3146 | 0 | } |
3147 | 0 | else if (is_i2p() && !settings().get_bool(settings_pack::allow_i2p_mixed)) |
3148 | 0 | { |
3149 | | // if we don't allow mixing normal peers into this i2p |
3150 | | // torrent, skip this announce |
3151 | 0 | continue; |
3152 | 0 | } |
3153 | 0 | #endif |
3154 | | |
3155 | 0 | for (auto& aep : ae.endpoints) |
3156 | 0 | { |
3157 | | // do not add code which continues to the next endpoint here! |
3158 | | // listen_socket_states needs to be populated even if none of the endpoints |
3159 | | // will be announcing for this tracker |
3160 | | // otherwise the early bail out when neither announce_to_all_trackers |
3161 | | // nor announce_to_all_tiers is set may be triggered prematurely |
3162 | |
|
3163 | 0 | auto aep_state_iter = std::find_if(listen_socket_states.begin(), listen_socket_states.end() |
3164 | 0 | , [&](announce_state const& s) { return s.socket == aep.socket; }); |
3165 | 0 | if (aep_state_iter == listen_socket_states.end()) |
3166 | 0 | { |
3167 | 0 | listen_socket_states.emplace_back(aep.socket); |
3168 | 0 | aep_state_iter = listen_socket_states.end() - 1; |
3169 | 0 | } |
3170 | 0 | announce_state& ep_state = *aep_state_iter; |
3171 | |
|
3172 | 0 | if (!aep.enabled) continue; |
3173 | | |
3174 | 0 | for (protocol_version const ih : all_versions) |
3175 | 0 | { |
3176 | 0 | if (!supports_protocol[ih]) continue; |
3177 | | |
3178 | 0 | auto& state = ep_state.state[ih]; |
3179 | 0 | auto& a = aep.info_hashes[ih]; |
3180 | | |
3181 | | // if we haven't sent an event=start to the tracker, there's no |
3182 | | // point in sending an event=stopped |
3183 | 0 | if (!a.start_sent && req.event == event_t::stopped) |
3184 | 0 | continue; |
3185 | | |
3186 | 0 | if (state.done) continue; |
3187 | | |
3188 | | #ifndef TORRENT_DISABLE_LOGGING |
3189 | | if (should_log()) |
3190 | | { |
3191 | | debug_log("*** tracker: (%d) [ep: %s ] \"%s\" [ " |
3192 | | " i->tier: %d tier: %d working: %d limit: %d" |
3193 | | " can: %d sent: %d ]" |
3194 | | , idx, print_endpoint(aep.local_endpoint).c_str() |
3195 | | , ae.url.c_str(), ae.tier, state.tier, a.is_working(), ae.fail_limit |
3196 | | , a.can_announce(now, is_seed(), ae.fail_limit), state.sent_announce); |
3197 | | } |
3198 | | #endif |
3199 | | |
3200 | 0 | if (announce_to_all_tiers |
3201 | 0 | && !announce_to_all_trackers |
3202 | 0 | && state.sent_announce |
3203 | 0 | && ae.tier <= state.tier |
3204 | 0 | && state.tier != INT_MAX) |
3205 | 0 | continue; |
3206 | | |
3207 | 0 | if (ae.tier > state.tier && state.sent_announce |
3208 | 0 | && !announce_to_all_tiers) continue; |
3209 | 0 | if (a.is_working()) { state.tier = ae.tier; state.sent_announce = false; } |
3210 | 0 | if (!a.can_announce(now, is_seed(), ae.fail_limit)) |
3211 | 0 | { |
3212 | | // this counts |
3213 | 0 | if (a.is_working()) |
3214 | 0 | { |
3215 | 0 | state.sent_announce = true; |
3216 | 0 | if (!announce_to_all_trackers |
3217 | 0 | && !announce_to_all_tiers) |
3218 | 0 | { |
3219 | 0 | state.done = true; |
3220 | 0 | } |
3221 | 0 | } |
3222 | 0 | continue; |
3223 | 0 | } |
3224 | | |
3225 | 0 | req.event = e; |
3226 | 0 | if (req.event == event_t::none) |
3227 | 0 | { |
3228 | 0 | if (!a.start_sent) req.event = event_t::started; |
3229 | 0 | else if (!m_complete_sent |
3230 | 0 | && !a.complete_sent |
3231 | 0 | && is_seed()) |
3232 | 0 | { |
3233 | 0 | req.event = event_t::completed; |
3234 | 0 | } |
3235 | 0 | } |
3236 | |
|
3237 | 0 | req.triggered_manually = a.triggered_manually; |
3238 | 0 | a.triggered_manually = false; |
3239 | |
|
3240 | 0 | #if TORRENT_ABI_VERSION == 1 |
3241 | 0 | req.auth = tracker_login(); |
3242 | 0 | #endif |
3243 | 0 | req.key = tracker_key(); |
3244 | |
|
3245 | 0 | req.outgoing_socket = aep.socket; |
3246 | 0 | req.info_hash = m_torrent_file->info_hashes().get(ih); |
3247 | |
|
3248 | | #ifndef TORRENT_DISABLE_LOGGING |
3249 | | if (should_log()) |
3250 | | { |
3251 | | debug_log("==> TRACKER REQUEST \"%s\" event: %s abort: %d ssl: %p " |
3252 | | "port: %d ssl-port: %d fails: %d upd: %d ep: %s" |
3253 | | , req.url.c_str() |
3254 | | , (req.event == event_t::stopped ? "stopped" |
3255 | | : req.event == event_t::started ? "started" : "") |
3256 | | , m_abort |
3257 | | #ifdef TORRENT_SSL_PEERS |
3258 | | , static_cast<void*>(req.ssl_ctx) |
3259 | | #else |
3260 | | , static_cast<void*>(nullptr) |
3261 | | #endif |
3262 | | , m_ses.listen_port() |
3263 | | , m_ses.ssl_listen_port() |
3264 | | , a.fails |
3265 | | , a.updating |
3266 | | , print_endpoint(aep.local_endpoint).c_str()); |
3267 | | } |
3268 | | |
3269 | | // if we're not logging session logs, don't bother creating an |
3270 | | // observer object just for logging |
3271 | | if (m_abort && m_ses.should_log()) |
3272 | | { |
3273 | | auto tl = std::make_shared<aux::tracker_logger>(m_ses); |
3274 | | m_ses.queue_tracker_request(req, tl); |
3275 | | } |
3276 | | else |
3277 | | #endif |
3278 | 0 | { |
3279 | 0 | m_ses.queue_tracker_request(req, shared_from_this()); |
3280 | 0 | } |
3281 | |
|
3282 | 0 | a.updating = true; |
3283 | 0 | a.next_announce = now; |
3284 | 0 | a.min_announce = now; |
3285 | |
|
3286 | 0 | if (m_ses.alerts().should_post<tracker_announce_alert>()) |
3287 | 0 | { |
3288 | 0 | m_ses.alerts().emplace_alert<tracker_announce_alert>( |
3289 | 0 | get_handle(), aep.local_endpoint, req.url, ih, req.event); |
3290 | 0 | } |
3291 | |
|
3292 | 0 | state.sent_announce = true; |
3293 | 0 | if (a.is_working() |
3294 | 0 | && !announce_to_all_trackers |
3295 | 0 | && !announce_to_all_tiers) |
3296 | 0 | { |
3297 | 0 | state.done = true; |
3298 | 0 | } |
3299 | 0 | } |
3300 | 0 | } |
3301 | |
|
3302 | 0 | if (std::all_of(listen_socket_states.begin(), listen_socket_states.end() |
3303 | 0 | , [supports_protocol](announce_state const& s) { |
3304 | 0 | for (protocol_version const ih : all_versions) |
3305 | 0 | { |
3306 | 0 | if (supports_protocol[ih] && !s.state[ih].done) |
3307 | 0 | return false; |
3308 | 0 | } |
3309 | 0 | return true; |
3310 | 0 | })) |
3311 | 0 | break; |
3312 | 0 | } |
3313 | 0 | update_tracker_timer(now); |
3314 | 0 | } |
3315 | | |
3316 | | void torrent::scrape_tracker(int idx, bool const user_triggered) |
3317 | 0 | { |
3318 | 0 | TORRENT_ASSERT(is_single_thread()); |
3319 | 0 | #if TORRENT_ABI_VERSION == 1 |
3320 | 0 | m_last_scrape = aux::time_now32(); |
3321 | 0 | #endif |
3322 | |
|
3323 | 0 | if (m_trackers.empty()) return; |
3324 | | |
3325 | 0 | if (idx < 0 || idx >= int(m_trackers.size())) idx = m_last_working_tracker; |
3326 | 0 | if (idx < 0) idx = 0; |
3327 | |
|
3328 | 0 | tracker_request req; |
3329 | 0 | if (settings().get_bool(settings_pack::apply_ip_filter_to_trackers) |
3330 | 0 | && m_apply_ip_filter) |
3331 | 0 | req.filter = m_ip_filter; |
3332 | |
|
3333 | 0 | req.kind |= tracker_request::scrape_request; |
3334 | 0 | auto& ae = m_trackers[idx]; |
3335 | |
|
3336 | 0 | #if TORRENT_USE_I2P |
3337 | 0 | if (is_i2p_url(ae.url)) |
3338 | 0 | req.kind |= tracker_request::i2p; |
3339 | 0 | else if (is_i2p() && !settings().get_bool(settings_pack::allow_i2p_mixed)) |
3340 | 0 | return; |
3341 | 0 | #endif |
3342 | 0 | refresh_endpoint_list(m_ses, ae.url, is_ssl_torrent(), bool(m_complete_sent), ae.endpoints); |
3343 | 0 | req.url = ae.url; |
3344 | 0 | req.private_torrent = m_torrent_file->priv(); |
3345 | 0 | #if TORRENT_ABI_VERSION == 1 |
3346 | 0 | req.auth = tracker_login(); |
3347 | 0 | #endif |
3348 | 0 | req.key = tracker_key(); |
3349 | 0 | req.triggered_manually = user_triggered; |
3350 | 0 | for (aux::announce_endpoint const& aep : ae.endpoints) |
3351 | 0 | { |
3352 | 0 | if (!aep.enabled) continue; |
3353 | 0 | req.outgoing_socket = aep.socket; |
3354 | 0 | m_torrent_file->info_hashes().for_each([&](sha1_hash const& ih, protocol_version) |
3355 | 0 | { |
3356 | 0 | req.info_hash = ih; |
3357 | 0 | m_ses.queue_tracker_request(req, shared_from_this()); |
3358 | 0 | }); |
3359 | 0 | } |
3360 | 0 | } |
3361 | | |
3362 | | void torrent::tracker_warning(tracker_request const& req, std::string const& msg) |
3363 | 0 | { |
3364 | 0 | TORRENT_ASSERT(is_single_thread()); |
3365 | |
|
3366 | 0 | INVARIANT_CHECK; |
3367 | |
|
3368 | 0 | protocol_version const hash_version = req.info_hash == m_info_hash.v1 |
3369 | 0 | ? protocol_version::V1 : protocol_version::V2; |
3370 | |
|
3371 | 0 | aux::announce_entry* ae = find_tracker(req.url); |
3372 | 0 | tcp::endpoint local_endpoint; |
3373 | 0 | if (ae) |
3374 | 0 | { |
3375 | 0 | for (auto& aep : ae->endpoints) |
3376 | 0 | { |
3377 | 0 | if (aep.socket != req.outgoing_socket) continue; |
3378 | 0 | local_endpoint = aep.local_endpoint; |
3379 | 0 | aep.info_hashes[hash_version].message = msg; |
3380 | 0 | break; |
3381 | 0 | } |
3382 | 0 | } |
3383 | |
|
3384 | 0 | if (m_ses.alerts().should_post<tracker_warning_alert>()) |
3385 | 0 | m_ses.alerts().emplace_alert<tracker_warning_alert>(get_handle() |
3386 | 0 | , local_endpoint, req.url, hash_version, msg); |
3387 | 0 | } |
3388 | | |
3389 | | void torrent::tracker_scrape_response(tracker_request const& req |
3390 | | , int const complete, int const incomplete, int const downloaded, int /* downloaders */) |
3391 | 0 | { |
3392 | 0 | TORRENT_ASSERT(is_single_thread()); |
3393 | |
|
3394 | 0 | INVARIANT_CHECK; |
3395 | 0 | TORRENT_ASSERT(req.kind & tracker_request::scrape_request); |
3396 | |
|
3397 | 0 | protocol_version const hash_version = req.info_hash == m_info_hash.v1 |
3398 | 0 | ? protocol_version::V1 : protocol_version::V2; |
3399 | |
|
3400 | 0 | aux::announce_entry* ae = find_tracker(req.url); |
3401 | 0 | tcp::endpoint local_endpoint; |
3402 | 0 | if (ae) |
3403 | 0 | { |
3404 | 0 | auto* aep = ae->find_endpoint(req.outgoing_socket); |
3405 | 0 | if (aep) |
3406 | 0 | { |
3407 | 0 | local_endpoint = aep->local_endpoint; |
3408 | 0 | if (incomplete >= 0) aep->info_hashes[hash_version].scrape_incomplete = incomplete; |
3409 | 0 | if (complete >= 0) aep->info_hashes[hash_version].scrape_complete = complete; |
3410 | 0 | if (downloaded >= 0) aep->info_hashes[hash_version].scrape_downloaded = downloaded; |
3411 | |
|
3412 | 0 | update_scrape_state(); |
3413 | 0 | } |
3414 | 0 | } |
3415 | | |
3416 | | // if this was triggered manually we need to post this unconditionally, |
3417 | | // since the client expects a response from its action, regardless of |
3418 | | // whether all tracker events have been enabled by the alert mask |
3419 | 0 | if (m_ses.alerts().should_post<scrape_reply_alert>() |
3420 | 0 | || req.triggered_manually) |
3421 | 0 | { |
3422 | 0 | m_ses.alerts().emplace_alert<scrape_reply_alert>( |
3423 | 0 | get_handle(), local_endpoint, incomplete, complete, req.url, hash_version); |
3424 | 0 | } |
3425 | 0 | } |
3426 | | |
3427 | | void torrent::update_scrape_state() |
3428 | 0 | { |
3429 | | // loop over all trackers and find the largest numbers for each scrape field |
3430 | | // then update the torrent-wide understanding of number of downloaders and seeds |
3431 | 0 | int complete = -1; |
3432 | 0 | int incomplete = -1; |
3433 | 0 | int downloaded = -1; |
3434 | 0 | for (auto const& t : m_trackers) |
3435 | 0 | { |
3436 | 0 | for (auto const& aep : t.endpoints) |
3437 | 0 | { |
3438 | 0 | for (protocol_version const ih : all_versions) |
3439 | 0 | { |
3440 | 0 | auto& a = aep.info_hashes[ih]; |
3441 | 0 | complete = std::max(a.scrape_complete, complete); |
3442 | 0 | incomplete = std::max(a.scrape_incomplete, incomplete); |
3443 | 0 | downloaded = std::max(a.scrape_downloaded, downloaded); |
3444 | 0 | } |
3445 | 0 | } |
3446 | 0 | } |
3447 | |
|
3448 | 0 | if ((complete >= 0 && int(m_complete) != complete) |
3449 | 0 | || (incomplete >= 0 && int(m_incomplete) != incomplete) |
3450 | 0 | || (downloaded >= 0 && int(m_downloaded) != downloaded)) |
3451 | 0 | state_updated(); |
3452 | |
|
3453 | 0 | if (int(m_complete) != complete |
3454 | 0 | || int(m_incomplete) != incomplete |
3455 | 0 | || int(m_downloaded) != downloaded) |
3456 | 0 | { |
3457 | 0 | m_complete = std::uint32_t(complete); |
3458 | 0 | m_incomplete = std::uint32_t(incomplete); |
3459 | 0 | m_downloaded = std::uint32_t(downloaded); |
3460 | |
|
3461 | 0 | update_auto_sequential(); |
3462 | | |
3463 | | // these numbers are cached in the resume data |
3464 | 0 | set_need_save_resume(torrent_handle::if_counters_changed); |
3465 | 0 | } |
3466 | 0 | } |
3467 | | |
3468 | | void torrent::tracker_response( |
3469 | | tracker_request const& r |
3470 | | , address const& tracker_ip // this is the IP we connected to |
3471 | | , std::list<address> const& tracker_ips // these are all the IPs it resolved to |
3472 | | , struct tracker_response const& resp) |
3473 | 0 | { |
3474 | 0 | TORRENT_ASSERT(is_single_thread()); |
3475 | |
|
3476 | 0 | INVARIANT_CHECK; |
3477 | 0 | TORRENT_ASSERT(!(r.kind & tracker_request::scrape_request)); |
3478 | | |
3479 | | // if the tracker told us what our external IP address is, record it with |
3480 | | // out external IP counter (and pass along the IP of the tracker to know |
3481 | | // who to attribute this vote to) |
3482 | 0 | if (resp.external_ip != address() && !tracker_ip.is_unspecified() && r.outgoing_socket) |
3483 | 0 | m_ses.set_external_address(r.outgoing_socket.get_local_endpoint() |
3484 | 0 | , resp.external_ip |
3485 | 0 | , aux::session_interface::source_tracker, tracker_ip); |
3486 | |
|
3487 | 0 | time_point32 const now = aux::time_now32(); |
3488 | |
|
3489 | 0 | protocol_version const v = r.info_hash == torrent_file().info_hashes().v1 |
3490 | 0 | ? protocol_version::V1 : protocol_version::V2; |
3491 | |
|
3492 | 0 | auto const interval = std::max(resp.interval, seconds32( |
3493 | 0 | settings().get_int(settings_pack::min_announce_interval))); |
3494 | |
|
3495 | 0 | aux::announce_entry* ae = find_tracker(r.url); |
3496 | 0 | tcp::endpoint local_endpoint; |
3497 | 0 | if (ae) |
3498 | 0 | { |
3499 | 0 | auto* aep = ae->find_endpoint(r.outgoing_socket); |
3500 | 0 | if (aep) |
3501 | 0 | { |
3502 | 0 | auto& a = aep->info_hashes[v]; |
3503 | |
|
3504 | 0 | local_endpoint = aep->local_endpoint; |
3505 | 0 | if (resp.incomplete >= 0) a.scrape_incomplete = resp.incomplete; |
3506 | 0 | if (resp.complete >= 0) a.scrape_complete = resp.complete; |
3507 | 0 | if (resp.downloaded >= 0) a.scrape_downloaded = resp.downloaded; |
3508 | 0 | if (!a.start_sent && r.event == event_t::started) |
3509 | 0 | a.start_sent = true; |
3510 | 0 | if (!a.complete_sent && r.event == event_t::completed) |
3511 | 0 | { |
3512 | 0 | a.complete_sent = true; |
3513 | | // we successfully reported event=completed to one tracker. Don't |
3514 | | // send it to any other ones from now on (there may be other |
3515 | | // announces outstanding right now though) |
3516 | 0 | m_complete_sent = true; |
3517 | 0 | } |
3518 | 0 | ae->verified = true; |
3519 | 0 | a.next_announce = now + interval; |
3520 | 0 | a.min_announce = now + resp.min_interval; |
3521 | 0 | a.updating = false; |
3522 | 0 | a.fails = 0; |
3523 | 0 | a.last_error.clear(); |
3524 | 0 | a.message = !resp.warning_message.empty() ? resp.warning_message : std::string(); |
3525 | 0 | int const tracker_index = int(ae - m_trackers.data()); |
3526 | 0 | m_last_working_tracker = std::int8_t(tracker_index); |
3527 | |
|
3528 | 0 | if ((!resp.trackerid.empty()) && (ae->trackerid != resp.trackerid)) |
3529 | 0 | { |
3530 | 0 | ae->trackerid = resp.trackerid; |
3531 | 0 | if (m_ses.alerts().should_post<trackerid_alert>()) |
3532 | 0 | m_ses.alerts().emplace_alert<trackerid_alert>(get_handle() |
3533 | 0 | , aep->local_endpoint, r.url, resp.trackerid); |
3534 | 0 | } |
3535 | |
|
3536 | 0 | update_scrape_state(); |
3537 | 0 | } |
3538 | 0 | } |
3539 | 0 | update_tracker_timer(now); |
3540 | |
|
3541 | 0 | #if TORRENT_ABI_VERSION == 1 |
3542 | 0 | if (resp.complete >= 0 && resp.incomplete >= 0) |
3543 | 0 | m_last_scrape = aux::time_now32(); |
3544 | 0 | #endif |
3545 | |
|
3546 | | #ifndef TORRENT_DISABLE_LOGGING |
3547 | | if (should_log()) |
3548 | | { |
3549 | | std::string resolved_to; |
3550 | | for (auto const& i : tracker_ips) |
3551 | | { |
3552 | | resolved_to += i.to_string(); |
3553 | | resolved_to += ", "; |
3554 | | } |
3555 | | debug_log("TRACKER RESPONSE [ interval: %d | min-interval: %d | " |
3556 | | "external ip: %s | resolved to: %s | we connected to: %s ]" |
3557 | | , interval.count() |
3558 | | , resp.min_interval.count() |
3559 | | , print_address(resp.external_ip).c_str() |
3560 | | , resolved_to.c_str() |
3561 | | , print_address(tracker_ip).c_str()); |
3562 | | } |
3563 | | #else |
3564 | 0 | TORRENT_UNUSED(tracker_ips); |
3565 | 0 | #endif |
3566 | | |
3567 | | // for each of the peers we got from the tracker |
3568 | 0 | for (auto const& i : resp.peers) |
3569 | 0 | { |
3570 | | // don't make connections to ourself |
3571 | 0 | if (i.pid == m_peer_id) |
3572 | 0 | continue; |
3573 | | |
3574 | 0 | #if TORRENT_USE_I2P |
3575 | 0 | if (r.i2pconn) |
3576 | 0 | { |
3577 | | // this is an i2p name, we need to use the SAM connection |
3578 | | // to do the name lookup |
3579 | 0 | if (string_ends_with(i.hostname, ".i2p")) |
3580 | 0 | { |
3581 | 0 | ADD_OUTSTANDING_ASYNC("torrent::on_i2p_resolve"); |
3582 | 0 | r.i2pconn->async_name_lookup(i.hostname.c_str() |
3583 | 0 | , [self = shared_from_this()] (error_code const& ec, char const* dest) |
3584 | 0 | { self->torrent::on_i2p_resolve(ec, dest); }); |
3585 | 0 | } |
3586 | 0 | else |
3587 | 0 | { |
3588 | 0 | torrent_state st = get_peer_list_state(); |
3589 | 0 | need_peer_list(); |
3590 | 0 | if (m_peer_list->add_i2p_peer(i.hostname, peer_info::tracker, {}, &st)) |
3591 | 0 | state_updated(); |
3592 | 0 | peers_erased(st.erased); |
3593 | 0 | } |
3594 | 0 | } |
3595 | 0 | else |
3596 | 0 | #endif |
3597 | 0 | { |
3598 | 0 | ADD_OUTSTANDING_ASYNC("torrent::on_peer_name_lookup"); |
3599 | 0 | m_ses.get_resolver().async_resolve(i.hostname, aux::resolver_interface::abort_on_shutdown |
3600 | 0 | , std::bind(&torrent::on_peer_name_lookup, shared_from_this(), _1, _2, i.port, v)); |
3601 | 0 | } |
3602 | 0 | } |
3603 | |
|
3604 | 0 | #if TORRENT_USE_I2P |
3605 | 0 | if (r.i2pconn) |
3606 | 0 | { |
3607 | 0 | for (auto const& i : resp.i2p_peers) |
3608 | 0 | { |
3609 | 0 | torrent_state st = get_peer_list_state(); |
3610 | 0 | peer_entry p; |
3611 | 0 | std::string destination = base32encode_i2p(i.destination); |
3612 | 0 | destination += ".b32.i2p"; |
3613 | |
|
3614 | 0 | ADD_OUTSTANDING_ASYNC("torrent::on_i2p_resolve"); |
3615 | 0 | r.i2pconn->async_name_lookup(destination.c_str() |
3616 | 0 | , [self = shared_from_this()] (error_code const& ec, char const* dest) |
3617 | 0 | { self->torrent::on_i2p_resolve(ec, dest); }); |
3618 | 0 | } |
3619 | 0 | } |
3620 | 0 | #endif |
3621 | | |
3622 | | // there are 2 reasons to allow local IPs to be returned from a |
3623 | | // non-local tracker |
3624 | | // 1. retrackers are popular in russia, where an ISP runs a tracker within |
3625 | | // the AS (but not on the local network) giving out peers only from the |
3626 | | // local network |
3627 | | // 2. it might make sense to have a tracker extension in the future where |
3628 | | // trackers records a peer's internal and external IP, and match up |
3629 | | // peers on the same local network |
3630 | |
|
3631 | 0 | pex_flags_t flags = v == protocol_version::V2 ? pex_lt_v2 : pex_flags_t(0); |
3632 | |
|
3633 | 0 | bool need_update = false; |
3634 | 0 | for (auto const& i : resp.peers4) |
3635 | 0 | { |
3636 | 0 | tcp::endpoint const a(address_v4(i.ip), i.port); |
3637 | 0 | need_update |= bool(add_peer(a, peer_info::tracker, flags) != nullptr); |
3638 | 0 | } |
3639 | |
|
3640 | 0 | for (auto const& i : resp.peers6) |
3641 | 0 | { |
3642 | 0 | tcp::endpoint const a(address_v6(i.ip), i.port); |
3643 | 0 | need_update |= bool(add_peer(a, peer_info::tracker, flags) != nullptr); |
3644 | 0 | } |
3645 | |
|
3646 | | #ifndef TORRENT_DISABLE_LOGGING |
3647 | | if (should_log() && (!resp.peers4.empty() || !resp.peers6.empty())) |
3648 | | { |
3649 | | std::string str; |
3650 | | for (auto const& peer : resp.peers4) |
3651 | | { |
3652 | | str += address_v4(peer.ip).to_string(); |
3653 | | str += ' '; |
3654 | | } |
3655 | | for (auto const& peer : resp.peers6) |
3656 | | { |
3657 | | str += address_v6(peer.ip).to_string(); |
3658 | | str += ' '; |
3659 | | } |
3660 | | debug_log("tracker add_peer() [ %s] connect-candidates: %d" |
3661 | | , str.c_str(), m_peer_list |
3662 | | ? m_peer_list->num_connect_candidates() : -1); |
3663 | | } |
3664 | | #endif |
3665 | 0 | if (need_update) state_updated(); |
3666 | |
|
3667 | 0 | update_want_peers(); |
3668 | | |
3669 | | // post unconditionally if the announce was triggered manually |
3670 | 0 | if (m_ses.alerts().should_post<tracker_reply_alert>() |
3671 | 0 | || r.triggered_manually) |
3672 | 0 | { |
3673 | 0 | int peer_count = int(resp.peers.size() + resp.peers4.size()); |
3674 | 0 | #if TORRENT_USE_I2P |
3675 | 0 | peer_count += int(resp.i2p_peers.size()); |
3676 | 0 | #endif |
3677 | 0 | m_ses.alerts().emplace_alert<tracker_reply_alert>( |
3678 | 0 | get_handle(), local_endpoint, peer_count |
3679 | 0 | + int(resp.peers6.size()), v, r.url); |
3680 | 0 | } |
3681 | |
|
3682 | 0 | do_connect_boost(); |
3683 | |
|
3684 | 0 | state_updated(); |
3685 | 0 | } |
3686 | | |
3687 | | void torrent::update_auto_sequential() |
3688 | 68 | { |
3689 | 68 | if (!settings().get_bool(settings_pack::auto_sequential)) |
3690 | 0 | { |
3691 | 0 | m_auto_sequential = false; |
3692 | 0 | return; |
3693 | 0 | } |
3694 | | |
3695 | 68 | if (num_peers() - m_num_connecting < 10) |
3696 | 68 | { |
3697 | | // there are too few peers. Be conservative and don't assume it's |
3698 | | // well seeded until we can connect to more peers |
3699 | 68 | m_auto_sequential = false; |
3700 | 68 | return; |
3701 | 68 | } |
3702 | | |
3703 | | // if there are at least 10 seeds, and there are 10 times more |
3704 | | // seeds than downloaders, enter sequential download mode |
3705 | | // (for performance) |
3706 | 0 | int const downloaders = num_downloaders(); |
3707 | 0 | int const seeds = num_seeds(); |
3708 | 0 | m_auto_sequential = downloaders * 10 <= seeds |
3709 | 0 | && seeds > 9; |
3710 | 0 | } |
3711 | | |
3712 | | void torrent::do_connect_boost() |
3713 | 2 | { |
3714 | 2 | if (m_connect_boost_counter == 0) return; |
3715 | | |
3716 | | // this is the first tracker response for this torrent |
3717 | | // instead of waiting one second for session_impl::on_tick() |
3718 | | // to be called, connect to a few peers immediately |
3719 | 2 | int conns = std::min(int(m_connect_boost_counter) |
3720 | 2 | , settings().get_int(settings_pack::connections_limit) - m_ses.num_connections()); |
3721 | | |
3722 | 2 | if (conns == 0) return; |
3723 | | |
3724 | | // if we don't know of any peers |
3725 | 2 | if (!m_peer_list) return; |
3726 | | |
3727 | 0 | while (want_peers() && conns > 0) |
3728 | 0 | { |
3729 | 0 | TORRENT_ASSERT(m_connect_boost_counter > 0); |
3730 | 0 | --conns; |
3731 | 0 | --m_connect_boost_counter; |
3732 | 0 | torrent_state st = get_peer_list_state(); |
3733 | 0 | torrent_peer* p = m_peer_list->connect_one_peer(m_ses.session_time(), &st); |
3734 | 0 | peers_erased(st.erased); |
3735 | 0 | inc_stats_counter(counters::connection_attempt_loops, st.loop_counter); |
3736 | 0 | if (p == nullptr) |
3737 | 0 | { |
3738 | 0 | update_want_peers(); |
3739 | 0 | continue; |
3740 | 0 | } |
3741 | | |
3742 | | #ifndef TORRENT_DISABLE_LOGGING |
3743 | | if (should_log()) |
3744 | | { |
3745 | | external_ip const& external = m_ses.external_address(); |
3746 | | debug_log(" *** FOUND CONNECTION CANDIDATE [" |
3747 | | " ip: %s rank: %u external: %s t: %d ]" |
3748 | | , print_endpoint(p->ip()).c_str() |
3749 | | , p->rank(external, m_ses.listen_port()) |
3750 | | , print_address(external.external_address(p->address())).c_str() |
3751 | | , int(m_ses.session_time() - p->last_connected)); |
3752 | | } |
3753 | | #endif |
3754 | | |
3755 | 0 | if (!connect_to_peer(p)) |
3756 | 0 | { |
3757 | 0 | m_peer_list->inc_failcount(p); |
3758 | 0 | update_want_peers(); |
3759 | 0 | } |
3760 | 0 | else |
3761 | 0 | { |
3762 | | // increase m_ses.m_boost_connections for each connection |
3763 | | // attempt. This will be deducted from the connect speed |
3764 | | // the next time session_impl::on_tick() is triggered |
3765 | 0 | m_ses.inc_boost_connections(); |
3766 | 0 | update_want_peers(); |
3767 | 0 | } |
3768 | 0 | } |
3769 | |
|
3770 | 0 | if (want_peers()) m_ses.prioritize_connections(shared_from_this()); |
3771 | 0 | } |
3772 | | |
3773 | | // this is the entry point for the client to force a re-announce. It's |
3774 | | // considered a client-initiated announce (as opposed to the regular ones, |
3775 | | // issued by libtorrent) |
3776 | | void torrent::force_tracker_request(time_point const t, int const tracker_idx |
3777 | | , reannounce_flags_t const flags) |
3778 | 0 | { |
3779 | 0 | TORRENT_ASSERT_PRECOND((tracker_idx >= 0 |
3780 | 0 | && tracker_idx < int(m_trackers.size())) |
3781 | 0 | || tracker_idx == -1); |
3782 | |
|
3783 | 0 | if (is_paused()) return; |
3784 | | #ifndef TORRENT_DISABLE_LOGGING |
3785 | | bool found_one = false; |
3786 | | #endif |
3787 | 0 | if (tracker_idx == -1) |
3788 | 0 | { |
3789 | 0 | for (auto& e : m_trackers) |
3790 | 0 | { |
3791 | | // make sure we check for new endpoints from the listen sockets |
3792 | 0 | refresh_endpoint_list(m_ses, e.url, is_ssl_torrent(), bool(m_complete_sent), e.endpoints); |
3793 | 0 | for (auto& aep : e.endpoints) |
3794 | 0 | { |
3795 | 0 | for (auto& a : aep.info_hashes) |
3796 | 0 | { |
3797 | 0 | a.next_announce = (flags & torrent_handle::ignore_min_interval) |
3798 | 0 | ? time_point_cast<seconds32>(t) + seconds32(1) |
3799 | 0 | : std::max(time_point_cast<seconds32>(t), a.min_announce) + seconds32(1); |
3800 | 0 | a.min_announce = a.next_announce; |
3801 | 0 | a.triggered_manually = true; |
3802 | 0 | } |
3803 | 0 | } |
3804 | 0 | } |
3805 | 0 | } |
3806 | 0 | else |
3807 | 0 | { |
3808 | 0 | if (tracker_idx < 0 || tracker_idx >= int(m_trackers.size())) |
3809 | 0 | return; |
3810 | 0 | aux::announce_entry& e = m_trackers[tracker_idx]; |
3811 | 0 | for (auto& aep : e.endpoints) |
3812 | 0 | { |
3813 | 0 | for (auto& a : aep.info_hashes) |
3814 | 0 | { |
3815 | 0 | a.next_announce = (flags & torrent_handle::ignore_min_interval) |
3816 | 0 | ? time_point_cast<seconds32>(t) + seconds32(1) |
3817 | 0 | : std::max(time_point_cast<seconds32>(t), a.min_announce) + seconds32(1); |
3818 | 0 | a.min_announce = a.next_announce; |
3819 | 0 | a.triggered_manually = true; |
3820 | | #ifndef TORRENT_DISABLE_LOGGING |
3821 | | found_one = true; |
3822 | | #endif |
3823 | 0 | } |
3824 | 0 | } |
3825 | 0 | } |
3826 | | |
3827 | | #ifndef TORRENT_DISABLE_LOGGING |
3828 | | if (!found_one) |
3829 | | { |
3830 | | debug_log("*** found no tracker endpoints to announce"); |
3831 | | } |
3832 | | #endif |
3833 | 0 | update_tracker_timer(aux::time_now32()); |
3834 | 0 | } |
3835 | | |
3836 | | #if TORRENT_ABI_VERSION == 1 |
3837 | | void torrent::set_tracker_login(std::string const& name |
3838 | | , std::string const& pw) |
3839 | 0 | { |
3840 | 0 | m_username = name; |
3841 | 0 | m_password = pw; |
3842 | 0 | } |
3843 | | #endif |
3844 | | |
3845 | | #if TORRENT_USE_I2P |
3846 | 0 | void torrent::on_i2p_resolve(error_code const& ec, char const* dest) try |
3847 | 0 | { |
3848 | 0 | TORRENT_ASSERT(is_single_thread()); |
3849 | |
|
3850 | 0 | INVARIANT_CHECK; |
3851 | |
|
3852 | 0 | COMPLETE_ASYNC("torrent::on_i2p_resolve"); |
3853 | | #ifndef TORRENT_DISABLE_LOGGING |
3854 | | if (ec && should_log()) |
3855 | | debug_log("i2p_resolve error: %s", ec.message().c_str()); |
3856 | | #endif |
3857 | 0 | if (ec || m_abort || m_ses.is_aborted()) return; |
3858 | | |
3859 | 0 | need_peer_list(); |
3860 | 0 | torrent_state st = get_peer_list_state(); |
3861 | 0 | if (m_peer_list->add_i2p_peer(dest, peer_info::tracker, {}, &st)) |
3862 | 0 | state_updated(); |
3863 | 0 | peers_erased(st.erased); |
3864 | |
|
3865 | 0 | update_want_peers(); |
3866 | 0 | } |
3867 | 0 | catch (...) { handle_exception(); } |
3868 | | #endif |
3869 | | |
3870 | | void torrent::on_peer_name_lookup(error_code const& e |
3871 | | , std::vector<address> const& host_list, int const port |
3872 | 0 | , protocol_version const v) try |
3873 | 0 | { |
3874 | 0 | TORRENT_ASSERT(is_single_thread()); |
3875 | |
|
3876 | 0 | INVARIANT_CHECK; |
3877 | |
|
3878 | 0 | COMPLETE_ASYNC("torrent::on_peer_name_lookup"); |
3879 | |
|
3880 | | #ifndef TORRENT_DISABLE_LOGGING |
3881 | | if (e && should_log()) |
3882 | | debug_log("peer name lookup error: %s", e.message().c_str()); |
3883 | | #endif |
3884 | |
|
3885 | 0 | if (e || m_abort || host_list.empty() || m_ses.is_aborted()) return; |
3886 | | |
3887 | | // TODO: add one peer per IP the hostname resolves to |
3888 | 0 | tcp::endpoint host(host_list.front(), std::uint16_t(port)); |
3889 | |
|
3890 | 0 | if (m_ip_filter && m_ip_filter->access(host.address()) & ip_filter::blocked) |
3891 | 0 | { |
3892 | | #ifndef TORRENT_DISABLE_LOGGING |
3893 | | if (should_log()) |
3894 | | { |
3895 | | debug_log("blocked ip from tracker: %s", host.address().to_string().c_str()); |
3896 | | } |
3897 | | #endif |
3898 | 0 | if (m_ses.alerts().should_post<peer_blocked_alert>()) |
3899 | 0 | m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle() |
3900 | 0 | , host, peer_blocked_alert::ip_filter); |
3901 | 0 | return; |
3902 | 0 | } |
3903 | | |
3904 | 0 | if (add_peer(host, peer_info::tracker, v == protocol_version::V2 ? pex_lt_v2 : pex_flags_t(0))) |
3905 | 0 | { |
3906 | 0 | state_updated(); |
3907 | |
|
3908 | | #ifndef TORRENT_DISABLE_LOGGING |
3909 | | if (should_log()) |
3910 | | { |
3911 | | debug_log("name-lookup add_peer() [ %s ] connect-candidates: %d" |
3912 | | , host.address().to_string().c_str() |
3913 | | , m_peer_list ? m_peer_list->num_connect_candidates() : -1); |
3914 | | } |
3915 | | #endif |
3916 | 0 | } |
3917 | 0 | update_want_peers(); |
3918 | 0 | } |
3919 | 0 | catch (...) { handle_exception(); } |
3920 | | |
3921 | | boost::optional<std::int64_t> torrent::bytes_left() const |
3922 | 0 | { |
3923 | | // if we don't have the metadata yet, we |
3924 | | // cannot tell how big the torrent is. |
3925 | 0 | if (!valid_metadata()) return {}; |
3926 | 0 | TORRENT_ASSERT(m_torrent_file->num_pieces() > 0); |
3927 | 0 | if (m_seed_mode) return std::int64_t(0); |
3928 | 0 | if (!has_picker()) return is_seed() ? std::int64_t(0) : m_torrent_file->total_size(); |
3929 | | |
3930 | 0 | piece_count const pc = m_picker->have(); |
3931 | 0 | std::int64_t left |
3932 | 0 | = m_torrent_file->total_size() |
3933 | 0 | - std::int64_t(pc.num_pieces) * m_torrent_file->piece_length(); |
3934 | | |
3935 | | // if we have the last piece, we may have subtracted too much, as it can |
3936 | | // be smaller than the normal piece size. |
3937 | | // we have to correct it |
3938 | 0 | if (pc.last_piece) |
3939 | 0 | { |
3940 | 0 | left += m_torrent_file->piece_length() - m_torrent_file->piece_size(m_torrent_file->last_piece()); |
3941 | 0 | } |
3942 | |
|
3943 | 0 | return left; |
3944 | 0 | } |
3945 | | |
3946 | | // we assume the last block is never a pad block. Should be a fairly |
3947 | | // safe assumption, and you just get a few kiB off if it is |
3948 | | std::int64_t calc_bytes(file_storage const& fs, piece_count const& pc) |
3949 | 0 | { |
3950 | | // it's an impossible combination to have 0 pieces, but still have one of them be the last piece |
3951 | 0 | TORRENT_ASSERT(!(pc.num_pieces == 0 && pc.last_piece == true)); |
3952 | | |
3953 | | // if we have 0 pieces, we can't have any pad blocks either |
3954 | 0 | TORRENT_ASSERT(!(pc.num_pieces == 0 && pc.pad_bytes > 0)); |
3955 | | |
3956 | | // if we have all pieces, we must also have the last one |
3957 | 0 | TORRENT_ASSERT(!(pc.num_pieces == fs.num_pieces() && pc.last_piece == false)); |
3958 | | |
3959 | | // every block should not be a pad block |
3960 | 0 | TORRENT_ASSERT(pc.pad_bytes <= std::int64_t(pc.num_pieces) * fs.piece_length()); |
3961 | |
|
3962 | 0 | return std::int64_t(pc.num_pieces) * fs.piece_length() |
3963 | 0 | - (pc.last_piece ? fs.piece_length() - fs.piece_size(fs.last_piece()) : 0) |
3964 | 0 | - std::int64_t(pc.pad_bytes); |
3965 | 0 | } |
3966 | | |
3967 | | // fills in total_wanted, total_wanted_done and total_done |
3968 | | // TODO: 3 this could probably be pulled out into a free function |
3969 | | void torrent::bytes_done(torrent_status& st, status_flags_t const flags) const |
3970 | 0 | { |
3971 | 0 | INVARIANT_CHECK; |
3972 | |
|
3973 | 0 | st.total_done = 0; |
3974 | 0 | st.total_wanted_done = 0; |
3975 | 0 | st.total_wanted = m_size_on_disk; |
3976 | |
|
3977 | 0 | TORRENT_ASSERT(st.total_wanted <= m_torrent_file->total_size()); |
3978 | 0 | TORRENT_ASSERT(st.total_wanted >= 0); |
3979 | |
|
3980 | 0 | TORRENT_ASSERT(!valid_metadata() || m_torrent_file->num_pieces() > 0); |
3981 | 0 | if (!valid_metadata()) return; |
3982 | | |
3983 | 0 | if (m_seed_mode || is_seed()) |
3984 | 0 | { |
3985 | | // once we're a seed and remove the piece picker, we stop tracking |
3986 | | // piece- and file priority. We consider everything as being |
3987 | | // "wanted" |
3988 | 0 | st.total_done = m_torrent_file->total_size() - m_padding_bytes; |
3989 | 0 | st.total_wanted_done = m_size_on_disk; |
3990 | 0 | st.total_wanted = m_size_on_disk; |
3991 | 0 | TORRENT_ASSERT(st.total_wanted <= st.total_done); |
3992 | 0 | TORRENT_ASSERT(st.total_wanted_done <= st.total_wanted); |
3993 | 0 | TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size()); |
3994 | 0 | return; |
3995 | 0 | } |
3996 | 0 | else if (!has_picker()) |
3997 | 0 | { |
3998 | 0 | st.total_done = 0; |
3999 | 0 | st.total_wanted_done = 0; |
4000 | 0 | st.total_wanted = m_size_on_disk; |
4001 | 0 | return; |
4002 | 0 | } |
4003 | | |
4004 | 0 | TORRENT_ASSERT(has_picker()); |
4005 | |
|
4006 | 0 | file_storage const& files = m_torrent_file->files(); |
4007 | |
|
4008 | 0 | st.total_wanted = std::min(m_size_on_disk, calc_bytes(files, m_picker->want())); |
4009 | 0 | st.total_wanted_done = std::min(m_file_progress.total_on_disk() |
4010 | 0 | , calc_bytes(files, m_picker->have_want())); |
4011 | 0 | st.total_done = calc_bytes(files, m_picker->have()); |
4012 | 0 | st.total = calc_bytes(files, m_picker->all_pieces()); |
4013 | |
|
4014 | 0 | TORRENT_ASSERT(st.total_done <= calc_bytes(files, m_picker->all_pieces())); |
4015 | 0 | TORRENT_ASSERT(st.total_wanted <= calc_bytes(files, m_picker->all_pieces())); |
4016 | |
|
4017 | 0 | TORRENT_ASSERT(st.total_wanted_done >= 0); |
4018 | 0 | TORRENT_ASSERT(st.total_wanted >= 0); |
4019 | 0 | TORRENT_ASSERT(st.total_wanted >= st.total_wanted_done); |
4020 | 0 | TORRENT_ASSERT(st.total_done >= 0); |
4021 | 0 | TORRENT_ASSERT(st.total_done >= st.total_wanted_done); |
4022 | | |
4023 | | // this is expensive, we might not want to do it all the time |
4024 | 0 | if (!(flags & torrent_handle::query_accurate_download_counters)) return; |
4025 | | |
4026 | | // to get higher accuracy of the download progress, include |
4027 | | // blocks from currently downloading pieces as well |
4028 | 0 | std::vector<piece_picker::downloading_piece> const dl_queue |
4029 | 0 | = m_picker->get_download_queue(); |
4030 | | |
4031 | | // look at all unfinished pieces and add the completed |
4032 | | // blocks to our 'done' counter |
4033 | 0 | for (auto i = dl_queue.begin(); i != dl_queue.end(); ++i) |
4034 | 0 | { |
4035 | 0 | piece_index_t const index = i->index; |
4036 | | |
4037 | | // completed pieces are already accounted for |
4038 | 0 | if (m_picker->have_piece(index)) continue; |
4039 | | |
4040 | 0 | TORRENT_ASSERT(i->finished + i->writing <= m_picker->blocks_in_piece(index)); |
4041 | |
|
4042 | 0 | auto const additional_bytes = std::int64_t(i->finished + i->writing |
4043 | 0 | - m_picker->pad_bytes_in_piece(index) / block_size()) |
4044 | 0 | * block_size(); |
4045 | 0 | TORRENT_ASSERT(additional_bytes >= 0); |
4046 | 0 | st.total_done += additional_bytes; |
4047 | 0 | if (m_picker->piece_priority(index) > dont_download) |
4048 | 0 | st.total_wanted_done += additional_bytes; |
4049 | 0 | } |
4050 | |
|
4051 | 0 | TORRENT_ASSERT(st.total_wanted_done >= 0); |
4052 | 0 | TORRENT_ASSERT(st.total_done >= st.total_wanted_done); |
4053 | 0 | } |
4054 | | |
4055 | | void torrent::on_piece_verified(aux::vector<sha256_hash> block_hashes |
4056 | | , piece_index_t const piece |
4057 | 0 | , sha1_hash const& piece_hash, storage_error const& error) try |
4058 | 0 | { |
4059 | 0 | TORRENT_ASSERT(is_single_thread()); |
4060 | |
|
4061 | 0 | if (m_abort) return; |
4062 | 0 | if (m_deleted) return; |
4063 | | |
4064 | 0 | m_picker->completed_hash_job(piece); |
4065 | |
|
4066 | 0 | boost::tribool passed = boost::indeterminate; |
4067 | 0 | boost::tribool v2_passed = boost::indeterminate; |
4068 | |
|
4069 | 0 | if (settings().get_bool(settings_pack::disable_hash_checks)) |
4070 | 0 | { |
4071 | 0 | passed = v2_passed = true; |
4072 | 0 | } |
4073 | 0 | else if (error) |
4074 | 0 | { |
4075 | 0 | passed = v2_passed = false; |
4076 | 0 | } |
4077 | 0 | else |
4078 | 0 | { |
4079 | 0 | if (torrent_file().info_hashes().has_v1()) |
4080 | 0 | { |
4081 | 0 | passed = sha1_hash(piece_hash) == m_torrent_file->hash_for_piece(piece); |
4082 | 0 | } |
4083 | |
|
4084 | 0 | if (!block_hashes.empty()) |
4085 | 0 | { |
4086 | 0 | TORRENT_ASSERT(torrent_file().info_hashes().has_v2()); |
4087 | 0 | v2_passed = on_blocks_hashed(piece, block_hashes); |
4088 | 0 | } |
4089 | 0 | } |
4090 | |
|
4091 | 0 | if (!error && ((passed && !v2_passed) || (!passed && v2_passed))) |
4092 | 0 | { |
4093 | 0 | handle_inconsistent_hashes(piece); |
4094 | 0 | return; |
4095 | 0 | } |
4096 | | |
4097 | 0 | bool const disk_error = (!passed || !v2_passed) && error; |
4098 | |
|
4099 | 0 | if (disk_error) handle_disk_error("piece_verified", error); |
4100 | |
|
4101 | | #ifndef TORRENT_DISABLE_LOGGING |
4102 | | if (should_log()) |
4103 | | { |
4104 | | debug_log("*** PIECE_FINISHED [ p: %d | chk: %s | size: %d ]" |
4105 | | , static_cast<int>(piece) |
4106 | | , (passed || v2_passed) ? "passed" |
4107 | | : disk_error ? "disk failed" |
4108 | | : (!passed || !v2_passed) ? "failed" |
4109 | | : "-" |
4110 | | , m_torrent_file->piece_size(piece)); |
4111 | | } |
4112 | | #endif |
4113 | 0 | TORRENT_ASSERT(valid_metadata()); |
4114 | | |
4115 | | // if we're a seed we don't have a picker |
4116 | | // and we also don't have to do anything because |
4117 | | // we already have this piece |
4118 | 0 | if (!has_picker() && m_have_all) return; |
4119 | | |
4120 | 0 | need_picker(); |
4121 | |
|
4122 | 0 | TORRENT_ASSERT(!m_picker->have_piece(piece)); |
4123 | |
|
4124 | 0 | state_updated(); |
4125 | | |
4126 | | // even though the piece passed the hash-check |
4127 | | // it might still have failed being written to disk |
4128 | | // if so, piece_picker::write_failed() has been |
4129 | | // called, and the piece is no longer finished. |
4130 | | // in this case, we have to ignore the fact that |
4131 | | // it passed the check |
4132 | 0 | if (!m_picker->is_piece_finished(piece)) return; |
4133 | | |
4134 | 0 | if (disk_error) |
4135 | 0 | { |
4136 | 0 | update_gauge(); |
4137 | 0 | } |
4138 | 0 | else if (passed || v2_passed) |
4139 | 0 | { |
4140 | | // the following call may cause picker to become invalid |
4141 | | // in case we just became a seed |
4142 | 0 | piece_passed(piece); |
4143 | | // if we're in seed mode, we just acquired this piece |
4144 | | // mark it as verified |
4145 | 0 | if (m_seed_mode) verified(piece); |
4146 | 0 | } |
4147 | 0 | else if (!passed || !v2_passed) |
4148 | 0 | { |
4149 | | // piece_failed() will restore the piece |
4150 | 0 | piece_failed(piece); |
4151 | 0 | } |
4152 | 0 | } |
4153 | 0 | catch (...) { handle_exception(); } |
4154 | | |
4155 | | void torrent::add_suggest_piece(piece_index_t const index) |
4156 | 0 | { |
4157 | 0 | TORRENT_ASSERT(settings().get_int(settings_pack::suggest_mode) |
4158 | 0 | == settings_pack::suggest_read_cache); |
4159 | | |
4160 | | // when we care about suggest mode, we keep the piece picker |
4161 | | // around to track piece availability |
4162 | 0 | need_picker(); |
4163 | 0 | int const peers = std::max(num_peers(), 1); |
4164 | 0 | int const availability = m_picker->get_availability(index) * 100 / peers; |
4165 | |
|
4166 | 0 | m_suggest_pieces.add_piece(index, availability |
4167 | 0 | , settings().get_int(settings_pack::max_suggest_pieces)); |
4168 | 0 | } |
4169 | | |
4170 | | // this is called when either: |
4171 | | // * we have completely downloaded piece 'index' and its hash has been verified. |
4172 | | // * during initial file check when we find a piece whose hash is correct |
4173 | | // * if there's a pad-file that extends over the entire piece |
4174 | | // this function does not update the piece picker, telling it we have the |
4175 | | // piece, it just does all the torrent-level accounting that needs to |
4176 | | // happen. It may not be called twice for the same piece (if it is, |
4177 | | // file_progress will assert) |
4178 | | void torrent::we_have(piece_index_t const index, bool const loading_resume) |
4179 | 663 | { |
4180 | 663 | TORRENT_ASSERT(is_single_thread()); |
4181 | 663 | TORRENT_ASSERT(!has_picker() || m_picker->have_piece(index)); |
4182 | | |
4183 | 663 | inc_stats_counter(counters::num_have_pieces); |
4184 | | |
4185 | | // at this point, we have the piece for sure. It has been |
4186 | | // successfully written to disk. We may announce it to peers |
4187 | | // (unless it has already been announced through predictive_piece_announce |
4188 | | // feature). |
4189 | 663 | bool announce_piece = true; |
4190 | 663 | #ifndef TORRENT_DISABLE_PREDICTIVE_PIECES |
4191 | 663 | auto const it = std::lower_bound(m_predictive_pieces.begin() |
4192 | 663 | , m_predictive_pieces.end(), index); |
4193 | 663 | if (it != m_predictive_pieces.end() && *it == index) |
4194 | 0 | { |
4195 | | // this means we've already announced the piece |
4196 | 0 | announce_piece = false; |
4197 | 0 | m_predictive_pieces.erase(it); |
4198 | 0 | } |
4199 | 663 | #endif |
4200 | | |
4201 | | // make a copy of the peer list since peers |
4202 | | // may disconnect while looping |
4203 | 663 | for (auto c : m_connections) |
4204 | 0 | { |
4205 | 0 | auto p = c->self(); |
4206 | | |
4207 | | // received_piece will check to see if we're still interested |
4208 | | // in this peer, and if neither of us is interested in the other, |
4209 | | // disconnect it. |
4210 | 0 | p->received_piece(index); |
4211 | 0 | if (p->is_disconnecting()) continue; |
4212 | | |
4213 | | // if we're not announcing the piece, it means we |
4214 | | // already have, and that we might have received |
4215 | | // a request for it, and not sending it because |
4216 | | // we were waiting to receive the piece, now that |
4217 | | // we have received it, try to send stuff (fill_send_buffer) |
4218 | 0 | if (announce_piece) p->announce_piece(index); |
4219 | 0 | else p->fill_send_buffer(); |
4220 | 0 | } |
4221 | | |
4222 | 663 | #ifndef TORRENT_DISABLE_EXTENSIONS |
4223 | 663 | for (auto& ext : m_extensions) |
4224 | 0 | { |
4225 | 0 | ext->on_piece_pass(index); |
4226 | 0 | } |
4227 | 663 | #endif |
4228 | | |
4229 | | // since this piece just passed, we might have |
4230 | | // become uninterested in some peers where this |
4231 | | // was the last piece we were interested in |
4232 | | // update_interest may disconnect the peer and |
4233 | | // invalidate the iterator |
4234 | 663 | for (auto p : m_connections) |
4235 | 0 | { |
4236 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
4237 | | // if we're not interested already, no need to check |
4238 | 0 | if (!p->is_interesting()) continue; |
4239 | | // if the peer doesn't have the piece we just got, it |
4240 | | // shouldn't affect our interest |
4241 | 0 | if (!p->has_piece(index)) continue; |
4242 | 0 | p->update_interest(); |
4243 | 0 | } |
4244 | | |
4245 | 663 | if (!loading_resume) |
4246 | 0 | { |
4247 | 0 | set_need_save_resume(torrent_handle::if_download_progress); |
4248 | 0 | state_updated(); |
4249 | 0 | update_want_tick(); |
4250 | 0 | } |
4251 | | |
4252 | 663 | if (m_ses.alerts().should_post<piece_finished_alert>()) |
4253 | 0 | m_ses.alerts().emplace_alert<piece_finished_alert>(get_handle(), index); |
4254 | | |
4255 | | // update m_file_progress (if we have one) |
4256 | 663 | m_file_progress.update(m_torrent_file->files(), index |
4257 | 663 | , [this](file_index_t const file_index) |
4258 | 663 | { |
4259 | 23 | if (m_ses.alerts().should_post<file_completed_alert>()) |
4260 | 0 | { |
4261 | | // this file just completed, post alert |
4262 | 0 | m_ses.alerts().emplace_alert<file_completed_alert>( |
4263 | 0 | get_handle(), file_index); |
4264 | 0 | } |
4265 | 23 | }); |
4266 | | |
4267 | 663 | #ifndef TORRENT_DISABLE_STREAMING |
4268 | 663 | remove_time_critical_piece(index, true); |
4269 | 663 | #endif |
4270 | | |
4271 | 663 | if (is_downloading_state(m_state)) |
4272 | 0 | { |
4273 | 0 | if (m_state != torrent_status::finished |
4274 | 0 | && m_state != torrent_status::seeding |
4275 | 0 | && is_finished()) |
4276 | 0 | { |
4277 | | // torrent finished |
4278 | | // i.e. all the pieces we're interested in have |
4279 | | // been downloaded. Release the files (they will open |
4280 | | // in read only mode if needed) |
4281 | 0 | finished(); |
4282 | | // if we just became a seed, picker is now invalid, since it |
4283 | | // is deallocated by the torrent once it starts seeding |
4284 | 0 | } |
4285 | |
|
4286 | 0 | m_last_download = aux::time_now32(); |
4287 | |
|
4288 | 0 | #ifndef TORRENT_DISABLE_SHARE_MODE |
4289 | 0 | if (m_share_mode) |
4290 | 0 | recalc_share_mode(); |
4291 | 0 | #endif |
4292 | 0 | } |
4293 | 663 | update_want_tick(); |
4294 | 663 | } |
4295 | | |
4296 | | boost::tribool torrent::on_blocks_hashed(piece_index_t const piece |
4297 | | , span<sha256_hash const> const block_hashes) |
4298 | 0 | { |
4299 | 0 | boost::tribool ret = boost::indeterminate; |
4300 | 0 | need_hash_picker(); |
4301 | |
|
4302 | 0 | int const blocks_in_piece = torrent_file().orig_files().blocks_in_piece2(piece); |
4303 | 0 | int const blocks_per_piece = torrent_file().blocks_per_piece(); |
4304 | | |
4305 | | // the blocks are guaranteed to represent exactly one piece |
4306 | 0 | TORRENT_ASSERT(blocks_in_piece == int(block_hashes.size())); |
4307 | |
|
4308 | 0 | TORRENT_ALLOCA(block_passed, bool, blocks_in_piece); |
4309 | 0 | std::fill(block_passed.begin(), block_passed.end(), false); |
4310 | |
|
4311 | 0 | set_block_hash_result last_result = set_block_hash_result(set_block_hash_result::result::unknown); |
4312 | |
|
4313 | 0 | for (int i = 0; i < blocks_in_piece; ++i) |
4314 | 0 | { |
4315 | | // if there was an enoent or eof error the block hashes array may be incomplete |
4316 | | // bail if we've hit the end of the valid hashes |
4317 | 0 | if (block_hashes[i].is_all_zeros()) return false; |
4318 | | |
4319 | 0 | auto const result = get_hash_picker().set_block_hash(piece |
4320 | 0 | , i * default_block_size, block_hashes[i]); |
4321 | 0 | last_result = result; |
4322 | | |
4323 | | // all verified ranges should always be full pieces or less |
4324 | 0 | TORRENT_ASSERT(result.first_verified_block >= 0 |
4325 | 0 | || (result.first_verified_block % blocks_per_piece) == 0); |
4326 | 0 | TORRENT_ASSERT(result.num_verified <= blocks_per_piece |
4327 | 0 | || (result.num_verified % blocks_per_piece) == 0); |
4328 | |
|
4329 | 0 | if (result.status == set_block_hash_result::result::success) |
4330 | 0 | { |
4331 | 0 | TORRENT_ASSERT(result.first_verified_block < blocks_in_piece); |
4332 | 0 | TORRENT_ASSERT(blocks_in_piece <= blocks_per_piece); |
4333 | | |
4334 | | // note that result.num_verified may cover pad blocks too, and |
4335 | | // so may be > blocks_in_piece |
4336 | | |
4337 | | // sometimes, completing a single block may "unlock" validating |
4338 | | // multiple pieces. e.g. if we don't have the piece layer yet, |
4339 | | // but we completed the last block in the whole torrent, now we |
4340 | | // can validate everything. For this reason, |
4341 | | // first_verified_block may be negative. |
4342 | | |
4343 | | // In this call, we track the blocks in this piece though, in |
4344 | | // the block_passed array. For that tracking we need to clamp |
4345 | | // the start index to 0. |
4346 | 0 | auto const first_block = std::max(0, result.first_verified_block); |
4347 | 0 | auto const count = std::min(blocks_in_piece - first_block, result.num_verified); |
4348 | 0 | std::fill_n(block_passed.begin() + first_block, count, true); |
4349 | | |
4350 | | // the current block (i) should be part of the range that was |
4351 | | // verified |
4352 | 0 | TORRENT_ASSERT(first_block <= i); |
4353 | 0 | TORRENT_ASSERT(i < first_block + count); |
4354 | | |
4355 | | // if the hashes for more than one piece have been verified, |
4356 | | // check for any pieces which were already checked but couldn't |
4357 | | // be verified and mark them as verified |
4358 | 0 | for (piece_index_t verified_piece : result.piece_range(piece, blocks_per_piece)) |
4359 | 0 | { |
4360 | 0 | if (!has_picker() |
4361 | 0 | || verified_piece == piece |
4362 | 0 | || !m_picker->is_piece_finished(verified_piece) |
4363 | 0 | || m_picker->have_piece(verified_piece)) |
4364 | 0 | continue; |
4365 | | |
4366 | 0 | TORRENT_ASSERT(get_hash_picker().piece_verified(verified_piece)); |
4367 | 0 | m_picker->piece_flushed(verified_piece); |
4368 | 0 | update_gauge(); |
4369 | 0 | we_have(verified_piece); |
4370 | 0 | } |
4371 | 0 | } |
4372 | 0 | else if (result.status == set_block_hash_result::result::block_hash_failed) |
4373 | 0 | { |
4374 | 0 | ret = false; |
4375 | 0 | } |
4376 | 0 | } |
4377 | 0 | if (last_result.status == set_block_hash_result::result::piece_hash_failed) |
4378 | 0 | { |
4379 | | // only if the *last* block causes the piece to fail, do we know |
4380 | | // it actually failed. Otherwise it might have been failing |
4381 | | // because of other, previously existing block hashes. |
4382 | 0 | ret = false; |
4383 | | |
4384 | | // if the hashes for more than one piece have been verified, |
4385 | | // check for any pieces which were already checked but couldn't |
4386 | | // be verified and mark them as verified |
4387 | 0 | for (piece_index_t verified_piece : last_result.piece_range(piece, blocks_per_piece)) |
4388 | 0 | { |
4389 | 0 | if (!has_picker() |
4390 | 0 | || verified_piece == piece) |
4391 | 0 | continue; |
4392 | | |
4393 | 0 | m_picker->we_dont_have(verified_piece); |
4394 | 0 | update_gauge(); |
4395 | 0 | piece_failed(verified_piece); |
4396 | 0 | } |
4397 | 0 | } |
4398 | |
|
4399 | 0 | if (boost::indeterminate(ret) && std::all_of(block_passed.begin(), block_passed.end() |
4400 | 0 | , [](bool e) { return e; })) |
4401 | 0 | { |
4402 | 0 | ret = true; |
4403 | 0 | } |
4404 | 0 | return ret; |
4405 | 0 | } |
4406 | | |
4407 | | // this is called when the piece hash is checked as correct. Note |
4408 | | // that the piece picker and the torrent won't necessarily consider |
4409 | | // us to have this piece yet, since it might not have been flushed |
4410 | | // to disk yet. Only if we have predictive_piece_announce on will |
4411 | | // we announce this piece to peers at this point. |
4412 | | void torrent::piece_passed(piece_index_t const index) |
4413 | 0 | { |
4414 | | // INVARIANT_CHECK; |
4415 | 0 | TORRENT_ASSERT(is_single_thread()); |
4416 | 0 | TORRENT_ASSERT(!m_picker->have_piece(index)); |
4417 | |
|
4418 | | #ifndef TORRENT_DISABLE_LOGGING |
4419 | | if (should_log()) |
4420 | | debug_log("PIECE_PASSED (%d) (num_have: %d)", int(index), num_have()); |
4421 | | #endif |
4422 | | |
4423 | | // std::fprintf(stderr, "torrent::piece_passed piece:%d\n", index); |
4424 | |
|
4425 | 0 | TORRENT_ASSERT(index >= piece_index_t(0)); |
4426 | 0 | TORRENT_ASSERT(index < m_torrent_file->end_piece()); |
4427 | |
|
4428 | 0 | set_need_save_resume(torrent_handle::if_download_progress); |
4429 | |
|
4430 | 0 | inc_stats_counter(counters::num_piece_passed); |
4431 | |
|
4432 | 0 | if (settings().get_int(settings_pack::suggest_mode) |
4433 | 0 | == settings_pack::suggest_read_cache) |
4434 | 0 | { |
4435 | | // we just got a new piece. Chances are that it's actually the |
4436 | | // rarest piece (since we're likely to download pieces rarest first) |
4437 | | // if it's rarer than any other piece that we currently suggest, insert |
4438 | | // it in the suggest set and pop the last one out |
4439 | 0 | add_suggest_piece(index); |
4440 | 0 | } |
4441 | | |
4442 | | // increase the trust point of all peers that sent |
4443 | | // parts of this piece. |
4444 | 0 | std::set<torrent_peer*> const peers = [&] |
4445 | 0 | { |
4446 | 0 | std::vector<torrent_peer*> const downloaders = m_picker->get_downloaders(index); |
4447 | |
|
4448 | 0 | std::set<torrent_peer*> ret; |
4449 | | // these torrent_peer pointers are owned by m_peer_list and they may be |
4450 | | // invalidated if a peer disconnects. We cannot keep them across any |
4451 | | // significant operations, but we should use them right away |
4452 | | // ignore nullptrs |
4453 | 0 | std::remove_copy(downloaders.begin(), downloaders.end() |
4454 | 0 | , std::inserter(ret, ret.begin()), static_cast<torrent_peer*>(nullptr)); |
4455 | 0 | return ret; |
4456 | 0 | }(); |
4457 | |
|
4458 | 0 | for (auto p : peers) |
4459 | 0 | { |
4460 | 0 | TORRENT_ASSERT(p != nullptr); |
4461 | 0 | if (p == nullptr) continue; |
4462 | 0 | TORRENT_ASSERT(p->in_use); |
4463 | 0 | p->on_parole = false; |
4464 | 0 | int trust_points = p->trust_points; |
4465 | 0 | ++trust_points; |
4466 | 0 | if (trust_points > 8) trust_points = 8; |
4467 | 0 | p->trust_points = trust_points; |
4468 | 0 | if (p->connection) |
4469 | 0 | { |
4470 | 0 | auto* peer = static_cast<peer_connection*>(p->connection); |
4471 | 0 | TORRENT_ASSERT(peer->m_in_use == 1337); |
4472 | 0 | peer->received_valid_data(index); |
4473 | 0 | } |
4474 | 0 | } |
4475 | |
|
4476 | 0 | m_picker->piece_passed(index); |
4477 | 0 | update_gauge(); |
4478 | 0 | we_have(index); |
4479 | |
|
4480 | | #ifndef TORRENT_DISABLE_LOGGING |
4481 | | if (should_log()) |
4482 | | debug_log("we_have(%d) (num_have: %d)", int(index), num_have()); |
4483 | | #endif |
4484 | 0 | #ifndef TORRENT_DISABLE_STREAMING |
4485 | 0 | remove_time_critical_piece(index, true); |
4486 | 0 | #endif |
4487 | 0 | } |
4488 | | |
4489 | | #ifndef TORRENT_DISABLE_PREDICTIVE_PIECES |
4490 | | // we believe we will complete this piece very soon |
4491 | | // announce it to peers ahead of time to eliminate the |
4492 | | // round-trip times involved in announcing it, requesting it |
4493 | | // and sending it |
4494 | | // TODO: 2 use chrono type for time duration |
4495 | | void torrent::predicted_have_piece(piece_index_t const index, int const milliseconds) |
4496 | 0 | { |
4497 | 0 | auto const i = std::lower_bound(m_predictive_pieces.begin() |
4498 | 0 | , m_predictive_pieces.end(), index); |
4499 | 0 | if (i != m_predictive_pieces.end() && *i == index) return; |
4500 | | |
4501 | 0 | for (auto p : m_connections) |
4502 | 0 | { |
4503 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
4504 | | #ifndef TORRENT_DISABLE_LOGGING |
4505 | | p->peer_log(peer_log_alert::outgoing, "PREDICTIVE_HAVE", "piece: %d expected in %d ms" |
4506 | | , static_cast<int>(index), milliseconds); |
4507 | | #else |
4508 | 0 | TORRENT_UNUSED(milliseconds); |
4509 | 0 | #endif |
4510 | 0 | p->announce_piece(index); |
4511 | 0 | } |
4512 | |
|
4513 | 0 | m_predictive_pieces.insert(i, index); |
4514 | 0 | } |
4515 | | #endif |
4516 | | |
4517 | | // blocks may contain the block indices of the blocks that failed (if this is |
4518 | | // a v2 torrent). |
4519 | | void torrent::piece_failed(piece_index_t const index, std::vector<int> blocks) |
4520 | 0 | { |
4521 | | // if the last piece fails the peer connection will still |
4522 | | // think that it has received all of it until this function |
4523 | | // resets the download queue. So, we cannot do the |
4524 | | // invariant check here since it assumes: |
4525 | | // (total_done == m_torrent_file->total_size()) => is_seed() |
4526 | 0 | INVARIANT_CHECK; |
4527 | 0 | TORRENT_ASSERT(is_single_thread()); |
4528 | |
|
4529 | 0 | TORRENT_ASSERT(has_picker()); |
4530 | 0 | TORRENT_ASSERT(index >= piece_index_t(0)); |
4531 | 0 | TORRENT_ASSERT(index < m_torrent_file->end_piece()); |
4532 | 0 | TORRENT_ASSERT(std::is_sorted(blocks.begin(), blocks.end())); |
4533 | |
|
4534 | 0 | inc_stats_counter(counters::num_piece_failed); |
4535 | |
|
4536 | 0 | #ifndef TORRENT_DISABLE_PREDICTIVE_PIECES |
4537 | 0 | auto const it = std::lower_bound(m_predictive_pieces.begin() |
4538 | 0 | , m_predictive_pieces.end(), index); |
4539 | 0 | if (it != m_predictive_pieces.end() && *it == index) |
4540 | 0 | { |
4541 | 0 | for (auto p : m_connections) |
4542 | 0 | { |
4543 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
4544 | | // send reject messages for |
4545 | | // potential outstanding requests to this piece |
4546 | 0 | p->reject_piece(index); |
4547 | | // let peers that support the dont-have message |
4548 | | // know that we don't actually have this piece |
4549 | 0 | p->write_dont_have(index); |
4550 | 0 | } |
4551 | 0 | m_predictive_pieces.erase(it); |
4552 | 0 | } |
4553 | 0 | #endif |
4554 | |
|
4555 | 0 | std::vector<torrent_peer*> const downloaders = m_picker->get_downloaders(index); |
4556 | | |
4557 | | // decrease the trust point of all peers that sent |
4558 | | // parts of this piece. |
4559 | | // first, build a set of all peers that participated |
4560 | | // if we know which blocks failed, just include the peer(s) sending those |
4561 | | // blocks |
4562 | 0 | std::set<torrent_peer*> const peers = [&] |
4563 | 0 | { |
4564 | 0 | std::set<torrent_peer*> ret; |
4565 | 0 | if (!blocks.empty() && !downloaders.empty()) |
4566 | 0 | { |
4567 | 0 | for (auto const b : blocks) ret.insert(downloaders[std::size_t(b)]); |
4568 | 0 | } |
4569 | 0 | else |
4570 | 0 | { |
4571 | 0 | std::copy(downloaders.begin(), downloaders.end(), std::inserter(ret, ret.begin())); |
4572 | 0 | } |
4573 | 0 | return ret; |
4574 | 0 | }(); |
4575 | | |
4576 | | // if this piece wasn't downloaded from peers, we just found it on disk. |
4577 | | // In that case, we should just consider it as "not-have" and there's no |
4578 | | // need to try to get higher fidelity hashes (yet) |
4579 | 0 | bool const found_on_disk = peers.size() == 1 && peers.count(nullptr); |
4580 | |
|
4581 | 0 | if (!torrent_file().info_hashes().has_v1() && blocks.empty() && !found_on_disk) |
4582 | 0 | { |
4583 | | // TODO: only do this if the piece size > 1 blocks |
4584 | | // This is a v2 torrent so we can request get block |
4585 | | // level hashes. |
4586 | 0 | verify_block_hashes(index); |
4587 | 0 | } |
4588 | | |
4589 | | // the below code is penalizing peers that sent use bad data. |
4590 | | // increase the total amount of failed bytes |
4591 | 0 | if (!found_on_disk) |
4592 | 0 | { |
4593 | 0 | if (blocks.empty()) |
4594 | 0 | add_failed_bytes(m_torrent_file->piece_size(index)); |
4595 | 0 | else |
4596 | 0 | add_failed_bytes(static_cast<int>(blocks.size()) * default_block_size); |
4597 | |
|
4598 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
4599 | 0 | for (auto& ext : m_extensions) |
4600 | 0 | { |
4601 | 0 | ext->on_piece_failed(index); |
4602 | 0 | } |
4603 | 0 | #endif |
4604 | | |
4605 | | // did we receive this piece from a single peer? |
4606 | | // if we know exactly which blocks failed the hash, we can also be certain |
4607 | | // that all peers in the list sent us bad data |
4608 | 0 | bool const known_bad_peer = (!found_on_disk && peers.size() == 1) || !blocks.empty(); |
4609 | |
|
4610 | 0 | penalize_peers(peers, index, known_bad_peer); |
4611 | 0 | } |
4612 | | |
4613 | | // If m_storage isn't set here, it means we're shutting down |
4614 | 0 | if (m_storage) |
4615 | 0 | { |
4616 | | // it doesn't make much sense to fail to hash a piece |
4617 | | // without having a storage associated with the torrent. |
4618 | | // restoring the piece in the piece picker without calling |
4619 | | // clear piece on the disk thread will make them out of |
4620 | | // sync, and if we try to write more blocks to this piece |
4621 | | // the disk thread will barf, because it hasn't been cleared |
4622 | 0 | TORRENT_ASSERT(m_storage); |
4623 | | |
4624 | | // don't allow picking any blocks from this piece |
4625 | | // until we're done synchronizing with the disk threads. |
4626 | 0 | m_picker->lock_piece(index); |
4627 | | |
4628 | | // don't do this until after the plugins have had a chance |
4629 | | // to read back the blocks that failed, for blame purposes |
4630 | | // this way they have a chance to hit the cache |
4631 | 0 | m_ses.disk_thread().async_clear_piece(m_storage, index |
4632 | 0 | , [self = shared_from_this(), c = std::move(blocks)](piece_index_t const& p) |
4633 | 0 | { self->on_piece_sync(p, c); }); |
4634 | 0 | m_ses.deferred_submit_jobs(); |
4635 | 0 | } |
4636 | 0 | else |
4637 | 0 | { |
4638 | 0 | TORRENT_ASSERT(m_abort); |
4639 | | // it doesn't really matter what we do |
4640 | | // here, since we're about to destruct the |
4641 | | // torrent anyway. |
4642 | 0 | on_piece_sync(index, std::move(blocks)); |
4643 | 0 | } |
4644 | 0 | } |
4645 | | |
4646 | | void torrent::penalize_peers(std::set<torrent_peer*> const& peers |
4647 | | , piece_index_t const index, bool const known_bad_peer) |
4648 | 0 | { |
4649 | 0 | for (auto p : peers) |
4650 | 0 | { |
4651 | 0 | if (p == nullptr) continue; |
4652 | 0 | TORRENT_ASSERT(p->in_use); |
4653 | 0 | bool allow_disconnect = true; |
4654 | 0 | if (p->connection) |
4655 | 0 | { |
4656 | 0 | auto* peer = static_cast<peer_connection*>(p->connection); |
4657 | 0 | TORRENT_ASSERT(peer->m_in_use == 1337); |
4658 | | |
4659 | | // the peer implementation can ask not to be disconnected. |
4660 | | // this is used for web seeds for instance, to instead of |
4661 | | // disconnecting, mark the file as not being had. |
4662 | 0 | allow_disconnect = peer->received_invalid_data(index, known_bad_peer); |
4663 | 0 | } |
4664 | |
|
4665 | 0 | if (settings().get_bool(settings_pack::use_parole_mode)) |
4666 | 0 | p->on_parole = true; |
4667 | |
|
4668 | 0 | int hashfails = p->hashfails; |
4669 | 0 | int trust_points = p->trust_points; |
4670 | | |
4671 | | // we decrease more than we increase, to keep the |
4672 | | // allowed failed/passed ratio low. |
4673 | 0 | trust_points -= 2; |
4674 | 0 | ++hashfails; |
4675 | 0 | if (trust_points < -7) trust_points = -7; |
4676 | 0 | p->trust_points = trust_points; |
4677 | 0 | if (hashfails > 255) hashfails = 255; |
4678 | 0 | p->hashfails = std::uint8_t(hashfails); |
4679 | | |
4680 | | // either, we have received too many failed hashes |
4681 | | // or this was the only peer that sent us this piece. |
4682 | | // if we have failed more than 3 pieces from this peer, |
4683 | | // don't trust it regardless. |
4684 | 0 | if (p->trust_points <= -7 |
4685 | 0 | || (known_bad_peer && allow_disconnect)) |
4686 | 0 | { |
4687 | | // we don't trust this peer anymore |
4688 | | // ban it. |
4689 | 0 | if (m_ses.alerts().should_post<peer_ban_alert>()) |
4690 | 0 | { |
4691 | 0 | peer_id const pid = p->connection |
4692 | 0 | ? p->connection->pid() : peer_id(); |
4693 | 0 | m_ses.alerts().emplace_alert<peer_ban_alert>( |
4694 | 0 | get_handle(), p->ip(), pid); |
4695 | 0 | } |
4696 | | |
4697 | | // mark the peer as banned |
4698 | 0 | ban_peer(p); |
4699 | 0 | update_want_peers(); |
4700 | 0 | inc_stats_counter(counters::banned_for_hash_failure); |
4701 | |
|
4702 | 0 | if (p->connection) |
4703 | 0 | { |
4704 | 0 | auto* peer = static_cast<peer_connection*>(p->connection); |
4705 | | #ifndef TORRENT_DISABLE_LOGGING |
4706 | | if (should_log()) |
4707 | | { |
4708 | | debug_log("*** BANNING PEER: \"%s\" Too many corrupt pieces" |
4709 | | , print_endpoint(p->ip()).c_str()); |
4710 | | } |
4711 | | peer->peer_log(peer_log_alert::info, "BANNING_PEER", "Too many corrupt pieces"); |
4712 | | #endif |
4713 | 0 | peer->disconnect(errors::too_many_corrupt_pieces, operation_t::bittorrent); |
4714 | 0 | } |
4715 | 0 | } |
4716 | 0 | } |
4717 | 0 | } |
4718 | | |
4719 | | void torrent::peer_is_interesting(peer_connection& c) |
4720 | 493 | { |
4721 | 493 | INVARIANT_CHECK; |
4722 | | |
4723 | | // no peer should be interesting if we're finished |
4724 | 493 | TORRENT_ASSERT(!is_finished()); |
4725 | | |
4726 | 493 | if (c.in_handshake()) return; |
4727 | 493 | c.send_interested(); |
4728 | 493 | if (c.has_peer_choked() |
4729 | 493 | && c.allowed_fast().empty()) |
4730 | 100 | return; |
4731 | | |
4732 | 393 | if (request_a_block(*this, c)) |
4733 | 132 | inc_stats_counter(counters::interesting_piece_picks); |
4734 | 393 | c.send_block_requests(); |
4735 | 393 | } |
4736 | | |
4737 | 0 | void torrent::on_piece_sync(piece_index_t const piece, std::vector<int> const& blocks) try |
4738 | 0 | { |
4739 | | // the user may have called force_recheck, which clears |
4740 | | // the piece picker |
4741 | 0 | if (!has_picker()) return; |
4742 | | |
4743 | | // unlock the piece and restore it, as if no block was |
4744 | | // ever downloaded for it. |
4745 | 0 | m_picker->restore_piece(piece, blocks); |
4746 | |
|
4747 | 0 | if (m_ses.alerts().should_post<hash_failed_alert>()) |
4748 | 0 | m_ses.alerts().emplace_alert<hash_failed_alert>(get_handle(), piece); |
4749 | | |
4750 | | // we have to let the piece_picker know that |
4751 | | // this piece failed the check as it can restore it |
4752 | | // and mark it as being interesting for download |
4753 | 0 | TORRENT_ASSERT(!m_picker->have_piece(piece)); |
4754 | | |
4755 | | // loop over all peers and re-request potential duplicate |
4756 | | // blocks to this piece |
4757 | 0 | for (auto p : m_connections) |
4758 | 0 | { |
4759 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
4760 | 0 | for (auto const& b : p->download_queue()) |
4761 | 0 | { |
4762 | 0 | if (b.timed_out || b.not_wanted) continue; |
4763 | 0 | if (b.block.piece_index != piece) continue; |
4764 | 0 | if (!blocks.empty() |
4765 | 0 | && std::find(blocks.begin(), blocks.end(), b.block.block_index) == blocks.end()) |
4766 | 0 | continue; |
4767 | 0 | m_picker->mark_as_downloading(b.block, p->peer_info_struct() |
4768 | 0 | , p->picker_options()); |
4769 | 0 | } |
4770 | 0 | for (auto const& b : p->request_queue()) |
4771 | 0 | { |
4772 | 0 | if (b.block.piece_index != piece) continue; |
4773 | 0 | if (!blocks.empty() |
4774 | 0 | && std::find(blocks.begin(), blocks.end(), b.block.block_index) == blocks.end()) |
4775 | 0 | continue; |
4776 | 0 | m_picker->mark_as_downloading(b.block, p->peer_info_struct() |
4777 | 0 | , p->picker_options()); |
4778 | 0 | } |
4779 | 0 | } |
4780 | 0 | } |
4781 | 0 | catch (...) { handle_exception(); } |
4782 | | |
4783 | | void torrent::peer_has(piece_index_t const index, peer_connection const* peer) |
4784 | 330 | { |
4785 | 330 | if (has_picker()) |
4786 | 330 | { |
4787 | 330 | torrent_peer* pp = peer->peer_info_struct(); |
4788 | 330 | m_picker->inc_refcount(index, pp); |
4789 | 330 | } |
4790 | 0 | else |
4791 | 0 | { |
4792 | 0 | TORRENT_ASSERT(is_seed() || !m_have_all); |
4793 | 0 | } |
4794 | 330 | } |
4795 | | |
4796 | | // when we get a bitfield message, this is called for that piece |
4797 | | void torrent::peer_has(typed_bitfield<piece_index_t> const& bits |
4798 | | , peer_connection const* peer) |
4799 | 2.57k | { |
4800 | 2.57k | if (has_picker()) |
4801 | 2.57k | { |
4802 | 2.57k | TORRENT_ASSERT(bits.size() == torrent_file().num_pieces()); |
4803 | 2.57k | torrent_peer* pp = peer->peer_info_struct(); |
4804 | 2.57k | m_picker->inc_refcount(bits, pp); |
4805 | 2.57k | } |
4806 | 0 | else |
4807 | 0 | { |
4808 | 0 | TORRENT_ASSERT(is_seed() || !m_have_all); |
4809 | 0 | } |
4810 | 2.57k | } |
4811 | | |
4812 | | void torrent::peer_has_all(peer_connection const* peer) |
4813 | 52 | { |
4814 | 52 | if (has_picker()) |
4815 | 52 | { |
4816 | 52 | torrent_peer* pp = peer->peer_info_struct(); |
4817 | 52 | m_picker->inc_refcount_all(pp); |
4818 | 52 | } |
4819 | 0 | else |
4820 | 0 | { |
4821 | 0 | TORRENT_ASSERT(is_seed() || !m_have_all); |
4822 | 0 | } |
4823 | 52 | } |
4824 | | |
4825 | | void torrent::peer_lost(typed_bitfield<piece_index_t> const& bits |
4826 | | , peer_connection const* peer) |
4827 | 2.78k | { |
4828 | 2.78k | if (has_picker()) |
4829 | 2.78k | { |
4830 | 2.78k | TORRENT_ASSERT(bits.size() == torrent_file().num_pieces()); |
4831 | 2.78k | torrent_peer* pp = peer->peer_info_struct(); |
4832 | 2.78k | m_picker->dec_refcount(bits, pp); |
4833 | 2.78k | } |
4834 | 0 | else |
4835 | 0 | { |
4836 | 0 | TORRENT_ASSERT(is_seed() || !m_have_all); |
4837 | 0 | } |
4838 | 2.78k | } |
4839 | | |
4840 | | void torrent::peer_lost(piece_index_t const index, peer_connection const* peer) |
4841 | 21 | { |
4842 | 21 | if (m_picker) |
4843 | 21 | { |
4844 | 21 | torrent_peer* pp = peer->peer_info_struct(); |
4845 | 21 | m_picker->dec_refcount(index, pp); |
4846 | 21 | } |
4847 | 0 | else |
4848 | 0 | { |
4849 | 0 | TORRENT_ASSERT(is_seed() || !m_have_all); |
4850 | 0 | } |
4851 | 21 | } |
4852 | | |
4853 | | void torrent::abort() |
4854 | 1.88k | { |
4855 | 1.88k | TORRENT_ASSERT(is_single_thread()); |
4856 | | |
4857 | 1.88k | if (m_abort) return; |
4858 | | |
4859 | 1.88k | m_abort = true; |
4860 | 1.88k | update_want_peers(); |
4861 | 1.88k | update_want_tick(); |
4862 | 1.88k | update_want_scrape(); |
4863 | 1.88k | update_gauge(); |
4864 | 1.88k | stop_announcing(); |
4865 | | |
4866 | | // remove from download queue |
4867 | 1.88k | m_ses.set_queue_position(this, queue_position_t{-1}); |
4868 | | |
4869 | 1.88k | if (m_peer_class > peer_class_t{0}) |
4870 | 351 | { |
4871 | 351 | remove_class(m_ses.peer_classes(), m_peer_class); |
4872 | 351 | m_ses.peer_classes().decref(m_peer_class); |
4873 | 351 | m_peer_class = peer_class_t{0}; |
4874 | 351 | } |
4875 | | |
4876 | 1.88k | m_inactivity_timer.cancel(); |
4877 | | |
4878 | | #ifndef TORRENT_DISABLE_LOGGING |
4879 | | log_to_all_peers("aborting"); |
4880 | | #endif |
4881 | | |
4882 | | // disconnect all peers and close all |
4883 | | // files belonging to the torrents |
4884 | 1.88k | disconnect_all(errors::torrent_aborted, operation_t::bittorrent); |
4885 | | |
4886 | | // make sure to destruct the peers immediately |
4887 | 1.88k | on_remove_peers(); |
4888 | 1.88k | TORRENT_ASSERT(m_connections.empty()); |
4889 | | |
4890 | | // post a message to the main thread to destruct |
4891 | | // the torrent object from there |
4892 | 1.88k | if (m_storage) |
4893 | 1.88k | { |
4894 | 1.88k | try { |
4895 | 1.88k | m_ses.disk_thread().async_stop_torrent(m_storage |
4896 | 1.88k | , std::bind(&torrent::on_torrent_aborted, shared_from_this())); |
4897 | 1.88k | } |
4898 | 1.88k | catch (std::exception const& e) |
4899 | 1.88k | { |
4900 | 0 | TORRENT_UNUSED(e); |
4901 | 0 | m_storage.reset(); |
4902 | | #ifndef TORRENT_DISABLE_LOGGING |
4903 | | debug_log("Failed to flush disk cache: %s", e.what()); |
4904 | | #endif |
4905 | | // clients may rely on this alert to be posted, so it's probably a |
4906 | | // good idea to post it here, even though we failed |
4907 | | // TODO: 3 should this alert have an error code in it? |
4908 | 0 | if (alerts().should_post<cache_flushed_alert>()) |
4909 | 0 | alerts().emplace_alert<cache_flushed_alert>(get_handle()); |
4910 | 0 | } |
4911 | 1.88k | m_ses.deferred_submit_jobs(); |
4912 | 1.88k | } |
4913 | 0 | else |
4914 | 0 | { |
4915 | 0 | if (alerts().should_post<cache_flushed_alert>()) |
4916 | 0 | alerts().emplace_alert<cache_flushed_alert>(get_handle()); |
4917 | 0 | alerts().emplace_alert<torrent_removed_alert>(get_handle() |
4918 | 0 | , info_hash(), get_userdata()); |
4919 | 0 | } |
4920 | | |
4921 | | // TODO: 2 abort lookups this torrent has made via the |
4922 | | // session host resolver interface |
4923 | | |
4924 | 1.88k | if (!m_apply_ip_filter) |
4925 | 1.27k | { |
4926 | 1.27k | inc_stats_counter(counters::non_filter_torrents, -1); |
4927 | 1.27k | m_apply_ip_filter = true; |
4928 | 1.27k | } |
4929 | | |
4930 | 1.88k | m_paused = false; |
4931 | 1.88k | m_auto_managed = false; |
4932 | 1.88k | update_state_list(); |
4933 | 16.9k | for (torrent_list_index_t i{}; i != m_links.end_index(); ++i) |
4934 | 15.0k | { |
4935 | 15.0k | if (!m_links[i].in_list()) continue; |
4936 | 584 | m_links[i].unlink(m_ses.torrent_list(i), i); |
4937 | 584 | } |
4938 | | // don't re-add this torrent to the state-update list |
4939 | 1.88k | m_state_subscription = false; |
4940 | 1.88k | } |
4941 | | |
4942 | | // this is called when we're destructing non-gracefully. i.e. we're _just_ |
4943 | | // destructing everything. |
4944 | | void torrent::panic() |
4945 | 0 | { |
4946 | 0 | m_storage.reset(); |
4947 | | // if there are any other peers allocated still, we need to clear them |
4948 | | // now. They can't be cleared later because the allocator will already |
4949 | | // have been destructed |
4950 | 0 | if (m_peer_list) m_peer_list->clear(); |
4951 | 0 | m_connections.clear(); |
4952 | 0 | m_outgoing_pids.clear(); |
4953 | 0 | m_peers_to_disconnect.clear(); |
4954 | 0 | m_num_uploads = 0; |
4955 | 0 | m_num_connecting = 0; |
4956 | 0 | m_num_connecting_seeds = 0; |
4957 | 0 | } |
4958 | | |
4959 | | #ifndef TORRENT_DISABLE_SUPERSEEDING |
4960 | | void torrent::set_super_seeding(bool const on) |
4961 | 0 | { |
4962 | 0 | if (on == m_super_seeding) return; |
4963 | | |
4964 | 0 | m_super_seeding = on; |
4965 | 0 | set_need_save_resume(torrent_handle::if_state_changed); |
4966 | 0 | state_updated(); |
4967 | |
|
4968 | 0 | if (m_super_seeding) return; |
4969 | | |
4970 | | // disable super seeding for all peers |
4971 | 0 | for (auto pc : *this) |
4972 | 0 | { |
4973 | 0 | pc->superseed_piece(piece_index_t(-1), piece_index_t(-1)); |
4974 | 0 | } |
4975 | 0 | } |
4976 | | |
4977 | | // TODO: 3 this should return optional<>. piece index -1 should not be |
4978 | | // allowed |
4979 | | piece_index_t torrent::get_piece_to_super_seed(typed_bitfield<piece_index_t> const& bits) |
4980 | 0 | { |
4981 | | // return a piece with low availability that is not in |
4982 | | // the bitfield and that is not currently being super |
4983 | | // seeded by any peer |
4984 | 0 | TORRENT_ASSERT(m_super_seeding); |
4985 | | |
4986 | | // do a linear search from the first piece |
4987 | 0 | int min_availability = 9999; |
4988 | 0 | std::vector<piece_index_t> avail_vec; |
4989 | 0 | for (auto const i : m_torrent_file->piece_range()) |
4990 | 0 | { |
4991 | 0 | if (bits[i]) continue; |
4992 | | |
4993 | 0 | int availability = 0; |
4994 | 0 | for (auto pc : *this) |
4995 | 0 | { |
4996 | 0 | if (pc->super_seeded_piece(i)) |
4997 | 0 | { |
4998 | | // avoid super-seeding the same piece to more than one |
4999 | | // peer if we can avoid it. Do this by artificially |
5000 | | // increase the availability |
5001 | 0 | availability = 999; |
5002 | 0 | break; |
5003 | 0 | } |
5004 | 0 | if (pc->has_piece(i)) ++availability; |
5005 | 0 | } |
5006 | 0 | if (availability > min_availability) continue; |
5007 | 0 | if (availability == min_availability) |
5008 | 0 | { |
5009 | 0 | avail_vec.push_back(i); |
5010 | 0 | continue; |
5011 | 0 | } |
5012 | 0 | TORRENT_ASSERT(availability < min_availability); |
5013 | 0 | min_availability = availability; |
5014 | 0 | avail_vec.clear(); |
5015 | 0 | avail_vec.push_back(i); |
5016 | 0 | } |
5017 | |
|
5018 | 0 | if (avail_vec.empty()) return piece_index_t{-1}; |
5019 | 0 | return avail_vec[random(std::uint32_t(avail_vec.size() - 1))]; |
5020 | 0 | } |
5021 | | #endif |
5022 | | |
5023 | 0 | void torrent::on_files_deleted(storage_error const& error) try |
5024 | 0 | { |
5025 | 0 | TORRENT_ASSERT(is_single_thread()); |
5026 | |
|
5027 | 0 | if (error) |
5028 | 0 | { |
5029 | 0 | if (alerts().should_post<torrent_delete_failed_alert>()) |
5030 | 0 | alerts().emplace_alert<torrent_delete_failed_alert>(get_handle() |
5031 | 0 | , error.ec, m_torrent_file->info_hashes()); |
5032 | 0 | } |
5033 | 0 | else |
5034 | 0 | { |
5035 | 0 | alerts().emplace_alert<torrent_deleted_alert>(get_handle(), m_torrent_file->info_hashes()); |
5036 | 0 | } |
5037 | 0 | } |
5038 | 0 | catch (...) { handle_exception(); } |
5039 | | |
5040 | | void torrent::on_file_renamed(std::string const& filename |
5041 | | , file_index_t const file_idx |
5042 | 0 | , storage_error const& error) try |
5043 | 0 | { |
5044 | 0 | TORRENT_ASSERT(is_single_thread()); |
5045 | |
|
5046 | 0 | if (error) |
5047 | 0 | { |
5048 | 0 | if (alerts().should_post<file_rename_failed_alert>()) |
5049 | 0 | alerts().emplace_alert<file_rename_failed_alert>(get_handle() |
5050 | 0 | , file_idx, error.ec); |
5051 | 0 | } |
5052 | 0 | else |
5053 | 0 | { |
5054 | 0 | if (alerts().should_post<file_renamed_alert>()) |
5055 | 0 | alerts().emplace_alert<file_renamed_alert>(get_handle() |
5056 | 0 | , filename, m_torrent_file->files().file_path(file_idx), file_idx); |
5057 | 0 | m_torrent_file->rename_file(file_idx, filename); |
5058 | |
|
5059 | 0 | set_need_save_resume(torrent_handle::if_state_changed); |
5060 | 0 | } |
5061 | 0 | } |
5062 | 0 | catch (...) { handle_exception(); } |
5063 | | |
5064 | 558 | void torrent::on_torrent_paused() try |
5065 | 558 | { |
5066 | 558 | TORRENT_ASSERT(is_single_thread()); |
5067 | | |
5068 | 558 | if (alerts().should_post<torrent_paused_alert>()) |
5069 | 0 | alerts().emplace_alert<torrent_paused_alert>(get_handle()); |
5070 | 558 | } |
5071 | 558 | catch (...) { handle_exception(); } |
5072 | | |
5073 | | #if TORRENT_ABI_VERSION == 1 |
5074 | | std::string torrent::tracker_login() const |
5075 | 0 | { |
5076 | 0 | if (m_username.empty() && m_password.empty()) return ""; |
5077 | 0 | return m_username + ":" + m_password; |
5078 | 0 | } |
5079 | | #endif |
5080 | | |
5081 | | std::uint32_t torrent::tracker_key() const |
5082 | 0 | { |
5083 | 0 | auto const self = reinterpret_cast<uintptr_t>(this); |
5084 | 0 | auto const ses = reinterpret_cast<uintptr_t>(&m_ses); |
5085 | 0 | std::uint32_t const storage = m_storage |
5086 | 0 | ? static_cast<std::uint32_t>(static_cast<storage_index_t>(m_storage)) |
5087 | 0 | : 0; |
5088 | 0 | sha1_hash const h = hasher(reinterpret_cast<char const*>(&self), sizeof(self)) |
5089 | 0 | .update(reinterpret_cast<char const*>(&storage), sizeof(storage)) |
5090 | 0 | .update(reinterpret_cast<char const*>(&ses), sizeof(ses)) |
5091 | 0 | .final(); |
5092 | 0 | unsigned char const* ptr = &h[0]; |
5093 | 0 | return aux::read_uint32(ptr); |
5094 | 0 | } |
5095 | | |
5096 | | #ifndef TORRENT_DISABLE_STREAMING |
5097 | | void torrent::cancel_non_critical() |
5098 | 0 | { |
5099 | | // if we don't have a piece picker, there's nothing to cancel. |
5100 | | // e.g. We may have become a seed already. |
5101 | 0 | if (!has_picker()) return; |
5102 | | |
5103 | 0 | std::set<piece_index_t> time_critical; |
5104 | 0 | for (auto const& p : m_time_critical_pieces) |
5105 | 0 | time_critical.insert(p.piece); |
5106 | |
|
5107 | 0 | for (auto p : m_connections) |
5108 | 0 | { |
5109 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
5110 | | // for each peer, go through its download and request queue |
5111 | | // and cancel everything, except pieces that are time critical |
5112 | | |
5113 | | // make a copy of the download queue since we may be cancelling entries |
5114 | | // from it from within the loop |
5115 | 0 | std::vector<pending_block> dq = p->download_queue(); |
5116 | 0 | for (auto const& k : dq) |
5117 | 0 | { |
5118 | 0 | if (time_critical.count(k.block.piece_index)) continue; |
5119 | 0 | if (k.not_wanted || k.timed_out) continue; |
5120 | 0 | p->cancel_request(k.block, true); |
5121 | 0 | } |
5122 | | |
5123 | | // make a copy of the download queue since we may be cancelling entries |
5124 | | // from it from within the loop |
5125 | 0 | std::vector<pending_block> rq = p->request_queue(); |
5126 | 0 | for (auto const& k : rq) |
5127 | 0 | { |
5128 | 0 | if (time_critical.count(k.block.piece_index)) continue; |
5129 | 0 | p->cancel_request(k.block, true); |
5130 | 0 | } |
5131 | 0 | } |
5132 | 0 | } |
5133 | | |
5134 | | void torrent::set_piece_deadline(piece_index_t const piece, int const t |
5135 | | , deadline_flags_t const flags) |
5136 | 0 | { |
5137 | 0 | INVARIANT_CHECK; |
5138 | |
|
5139 | 0 | TORRENT_ASSERT_PRECOND(piece >= piece_index_t(0)); |
5140 | 0 | TORRENT_ASSERT_PRECOND(valid_metadata()); |
5141 | 0 | TORRENT_ASSERT_PRECOND(valid_metadata() && piece < m_torrent_file->end_piece()); |
5142 | |
|
5143 | 0 | if (m_abort || !valid_metadata() |
5144 | 0 | || piece < piece_index_t(0) |
5145 | 0 | || piece >= m_torrent_file->end_piece()) |
5146 | 0 | { |
5147 | | // failed |
5148 | 0 | if (flags & torrent_handle::alert_when_available) |
5149 | 0 | { |
5150 | 0 | m_ses.alerts().emplace_alert<read_piece_alert>( |
5151 | 0 | get_handle(), piece, error_code(boost::system::errc::operation_canceled, generic_category())); |
5152 | 0 | } |
5153 | 0 | return; |
5154 | 0 | } |
5155 | | |
5156 | 0 | time_point const deadline = aux::time_now() + milliseconds(t); |
5157 | | |
5158 | | // if we already have the piece, no need to set the deadline. |
5159 | | // however, if the user asked to get the piece data back, we still |
5160 | | // need to read it and post it back to the user |
5161 | 0 | if (is_seed() || (has_picker() && m_picker->have_piece(piece))) |
5162 | 0 | { |
5163 | 0 | if (flags & torrent_handle::alert_when_available) |
5164 | 0 | read_piece(piece); |
5165 | 0 | return; |
5166 | 0 | } |
5167 | | |
5168 | | // if this is the first time critical piece we add. in order to make it |
5169 | | // react quickly, cancel all the currently outstanding requests |
5170 | 0 | if (m_time_critical_pieces.empty()) |
5171 | 0 | { |
5172 | | // defer this by posting it to the end of the message queue. |
5173 | | // this gives the client a chance to specify multiple time-critical |
5174 | | // pieces before libtorrent cancels requests |
5175 | 0 | auto self = shared_from_this(); |
5176 | 0 | post(m_ses.get_context(), [self] { self->wrap(&torrent::cancel_non_critical); }); |
5177 | 0 | } |
5178 | |
|
5179 | 0 | for (auto i = m_time_critical_pieces.begin() |
5180 | 0 | , end(m_time_critical_pieces.end()); i != end; ++i) |
5181 | 0 | { |
5182 | 0 | if (i->piece != piece) continue; |
5183 | 0 | i->deadline = deadline; |
5184 | 0 | i->flags = flags; |
5185 | | |
5186 | | // resort i since deadline might have changed |
5187 | 0 | while (std::next(i) != m_time_critical_pieces.end() && i->deadline > std::next(i)->deadline) |
5188 | 0 | { |
5189 | 0 | std::iter_swap(i, std::next(i)); |
5190 | 0 | ++i; |
5191 | 0 | } |
5192 | 0 | while (i != m_time_critical_pieces.begin() && i->deadline < std::prev(i)->deadline) |
5193 | 0 | { |
5194 | 0 | std::iter_swap(i, std::prev(i)); |
5195 | 0 | --i; |
5196 | 0 | } |
5197 | | // just in case this piece had priority 0 |
5198 | 0 | download_priority_t const prev_prio = m_picker->piece_priority(piece); |
5199 | 0 | bool const was_finished = is_finished(); |
5200 | 0 | bool const filter_updated = m_picker->set_piece_priority(piece, top_priority); |
5201 | 0 | if (prev_prio == dont_download) |
5202 | 0 | { |
5203 | 0 | update_gauge(); |
5204 | 0 | if (filter_updated) update_peer_interest(was_finished); |
5205 | 0 | } |
5206 | 0 | return; |
5207 | 0 | } |
5208 | | |
5209 | 0 | need_picker(); |
5210 | |
|
5211 | 0 | time_critical_piece p; |
5212 | 0 | p.first_requested = min_time(); |
5213 | 0 | p.last_requested = min_time(); |
5214 | 0 | p.flags = flags; |
5215 | 0 | p.deadline = deadline; |
5216 | 0 | p.peers = 0; |
5217 | 0 | p.piece = piece; |
5218 | 0 | auto const critical_piece_it = std::upper_bound(m_time_critical_pieces.begin() |
5219 | 0 | , m_time_critical_pieces.end(), p); |
5220 | 0 | m_time_critical_pieces.insert(critical_piece_it, p); |
5221 | | |
5222 | | // just in case this piece had priority 0 |
5223 | 0 | download_priority_t const prev_prio = m_picker->piece_priority(piece); |
5224 | 0 | bool const was_finished = is_finished(); |
5225 | 0 | bool const filter_updated = m_picker->set_piece_priority(piece, top_priority); |
5226 | 0 | if (prev_prio == dont_download) |
5227 | 0 | { |
5228 | 0 | update_gauge(); |
5229 | 0 | if (filter_updated) update_peer_interest(was_finished); |
5230 | 0 | } |
5231 | |
|
5232 | 0 | piece_picker::downloading_piece pi; |
5233 | 0 | m_picker->piece_info(piece, pi); |
5234 | 0 | if (pi.requested == 0) return; |
5235 | | // this means we have outstanding requests (or queued |
5236 | | // up requests that haven't been sent yet). Promote them |
5237 | | // to deadline pieces immediately |
5238 | 0 | std::vector<torrent_peer*> const downloaders |
5239 | 0 | = m_picker->get_downloaders(piece); |
5240 | |
|
5241 | 0 | int block = 0; |
5242 | 0 | for (auto i = downloaders.begin() |
5243 | 0 | , end(downloaders.end()); i != end; ++i, ++block) |
5244 | 0 | { |
5245 | 0 | torrent_peer* const tp = *i; |
5246 | 0 | if (tp == nullptr || tp->connection == nullptr) continue; |
5247 | 0 | auto* peer = static_cast<peer_connection*>(tp->connection); |
5248 | 0 | peer->make_time_critical(piece_block(piece, block)); |
5249 | 0 | } |
5250 | 0 | } |
5251 | | |
5252 | | void torrent::reset_piece_deadline(piece_index_t piece) |
5253 | 0 | { |
5254 | 0 | remove_time_critical_piece(piece); |
5255 | 0 | } |
5256 | | |
5257 | | void torrent::remove_time_critical_piece(piece_index_t const piece, bool const finished) |
5258 | 663 | { |
5259 | 663 | for (auto i = m_time_critical_pieces.begin(), end(m_time_critical_pieces.end()); |
5260 | 663 | i != end; ++i) |
5261 | 0 | { |
5262 | 0 | if (i->piece != piece) continue; |
5263 | 0 | if (finished) |
5264 | 0 | { |
5265 | 0 | if (i->flags & torrent_handle::alert_when_available) |
5266 | 0 | { |
5267 | 0 | read_piece(i->piece); |
5268 | 0 | } |
5269 | | |
5270 | | // if first_requested is min_time(), it wasn't requested as a critical piece |
5271 | | // and we shouldn't adjust any average download times |
5272 | 0 | if (i->first_requested != min_time()) |
5273 | 0 | { |
5274 | | // update the average download time and average |
5275 | | // download time deviation |
5276 | 0 | int const dl_time = aux::numeric_cast<int>(total_milliseconds(aux::time_now() - i->first_requested)); |
5277 | |
|
5278 | 0 | if (m_average_piece_time == 0) |
5279 | 0 | { |
5280 | 0 | m_average_piece_time = dl_time; |
5281 | 0 | } |
5282 | 0 | else |
5283 | 0 | { |
5284 | 0 | int diff = std::abs(dl_time - m_average_piece_time); |
5285 | 0 | if (m_piece_time_deviation == 0) m_piece_time_deviation = diff; |
5286 | 0 | else m_piece_time_deviation = (m_piece_time_deviation * 9 + diff) / 10; |
5287 | |
|
5288 | 0 | m_average_piece_time = (m_average_piece_time * 9 + dl_time) / 10; |
5289 | 0 | } |
5290 | 0 | } |
5291 | 0 | } |
5292 | 0 | else if (i->flags & torrent_handle::alert_when_available) |
5293 | 0 | { |
5294 | | // post an empty read_piece_alert to indicate it failed |
5295 | 0 | alerts().emplace_alert<read_piece_alert>( |
5296 | 0 | get_handle(), piece, error_code(boost::system::errc::operation_canceled, generic_category())); |
5297 | 0 | } |
5298 | 0 | if (has_picker()) m_picker->set_piece_priority(piece, low_priority); |
5299 | 0 | m_time_critical_pieces.erase(i); |
5300 | 0 | return; |
5301 | 0 | } |
5302 | 663 | } |
5303 | | |
5304 | | void torrent::clear_time_critical() |
5305 | 0 | { |
5306 | 0 | for (auto i = m_time_critical_pieces.begin(); i != m_time_critical_pieces.end();) |
5307 | 0 | { |
5308 | 0 | if (i->flags & torrent_handle::alert_when_available) |
5309 | 0 | { |
5310 | | // post an empty read_piece_alert to indicate it failed |
5311 | 0 | m_ses.alerts().emplace_alert<read_piece_alert>( |
5312 | 0 | get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, generic_category())); |
5313 | 0 | } |
5314 | 0 | if (has_picker()) m_picker->set_piece_priority(i->piece, low_priority); |
5315 | 0 | i = m_time_critical_pieces.erase(i); |
5316 | 0 | } |
5317 | 0 | } |
5318 | | |
5319 | | // remove time critical pieces where priority is 0 |
5320 | | void torrent::remove_time_critical_pieces(aux::vector<download_priority_t, piece_index_t> const& priority) |
5321 | 756 | { |
5322 | 756 | for (auto i = m_time_critical_pieces.begin(); i != m_time_critical_pieces.end();) |
5323 | 0 | { |
5324 | 0 | if (priority[i->piece] == dont_download) |
5325 | 0 | { |
5326 | 0 | if (i->flags & torrent_handle::alert_when_available) |
5327 | 0 | { |
5328 | | // post an empty read_piece_alert to indicate it failed |
5329 | 0 | alerts().emplace_alert<read_piece_alert>( |
5330 | 0 | get_handle(), i->piece, error_code(boost::system::errc::operation_canceled, generic_category())); |
5331 | 0 | } |
5332 | 0 | i = m_time_critical_pieces.erase(i); |
5333 | 0 | continue; |
5334 | 0 | } |
5335 | 0 | ++i; |
5336 | 0 | } |
5337 | 756 | } |
5338 | | #endif // TORRENT_DISABLE_STREAMING |
5339 | | |
5340 | | void torrent::post_piece_availability() |
5341 | 0 | { |
5342 | 0 | aux::vector<int, piece_index_t> avail; |
5343 | 0 | piece_availability(avail); |
5344 | 0 | alerts().emplace_alert<piece_availability_alert>(get_handle(), std::move(avail)); |
5345 | 0 | } |
5346 | | |
5347 | | void torrent::piece_availability(aux::vector<int, piece_index_t>& avail) const |
5348 | 0 | { |
5349 | 0 | INVARIANT_CHECK; |
5350 | |
|
5351 | 0 | TORRENT_ASSERT(valid_metadata()); |
5352 | 0 | if (!has_picker()) |
5353 | 0 | { |
5354 | 0 | avail.clear(); |
5355 | 0 | return; |
5356 | 0 | } |
5357 | | |
5358 | 0 | m_picker->get_availability(avail); |
5359 | 0 | } |
5360 | | |
5361 | | void torrent::set_piece_priority(piece_index_t const index |
5362 | | , download_priority_t const priority) |
5363 | 0 | { |
5364 | | // INVARIANT_CHECK; |
5365 | |
|
5366 | | #ifndef TORRENT_DISABLE_LOGGING |
5367 | | if (!valid_metadata()) |
5368 | | { |
5369 | | debug_log("*** SET_PIECE_PRIORITY [ idx: %d prio: %d ignored. " |
5370 | | "no metadata yet ]", static_cast<int>(index) |
5371 | | , static_cast<std::uint8_t>(priority)); |
5372 | | } |
5373 | | #endif |
5374 | 0 | if (!valid_metadata() || is_seed()) return; |
5375 | | |
5376 | | // this call is only valid on torrents with metadata |
5377 | 0 | if (index < piece_index_t(0) || index >= m_torrent_file->end_piece()) |
5378 | 0 | { |
5379 | 0 | return; |
5380 | 0 | } |
5381 | | |
5382 | 0 | need_picker(); |
5383 | |
|
5384 | 0 | bool const was_finished = is_finished(); |
5385 | 0 | bool const filter_updated = m_picker->set_piece_priority(index, priority); |
5386 | |
|
5387 | 0 | update_gauge(); |
5388 | |
|
5389 | 0 | if (filter_updated) |
5390 | 0 | { |
5391 | 0 | update_peer_interest(was_finished); |
5392 | 0 | #ifndef TORRENT_DISABLE_STREAMING |
5393 | 0 | if (priority == dont_download) remove_time_critical_piece(index); |
5394 | 0 | #endif // TORRENT_DISABLE_STREAMING |
5395 | 0 | } |
5396 | |
|
5397 | 0 | } |
5398 | | |
5399 | | download_priority_t torrent::piece_priority(piece_index_t const index) const |
5400 | 53 | { |
5401 | | // INVARIANT_CHECK; |
5402 | | |
5403 | 53 | if (!has_picker()) return default_priority; |
5404 | | |
5405 | | // this call is only valid on torrents with metadata |
5406 | 53 | TORRENT_ASSERT(valid_metadata()); |
5407 | 53 | if (index < piece_index_t(0) || index >= m_torrent_file->end_piece()) |
5408 | 0 | { |
5409 | 0 | TORRENT_ASSERT_FAIL(); |
5410 | 0 | return dont_download; |
5411 | 0 | } |
5412 | | |
5413 | 53 | return m_picker->piece_priority(index); |
5414 | 53 | } |
5415 | | |
5416 | | void torrent::prioritize_piece_list(std::vector<std::pair<piece_index_t |
5417 | | , download_priority_t>> const& pieces) |
5418 | 0 | { |
5419 | 0 | INVARIANT_CHECK; |
5420 | | |
5421 | | // this call is only valid on torrents with metadata |
5422 | 0 | TORRENT_ASSERT(valid_metadata()); |
5423 | 0 | if (is_seed()) return; |
5424 | | |
5425 | 0 | need_picker(); |
5426 | |
|
5427 | 0 | bool filter_updated = false; |
5428 | 0 | bool const was_finished = is_finished(); |
5429 | 0 | for (auto const& p : pieces) |
5430 | 0 | { |
5431 | 0 | static_assert(std::is_unsigned<decltype(p.second)::underlying_type>::value |
5432 | 0 | , "we need assert p.second >= dont_download"); |
5433 | 0 | TORRENT_ASSERT(p.second <= top_priority); |
5434 | 0 | TORRENT_ASSERT(p.first >= piece_index_t(0)); |
5435 | 0 | TORRENT_ASSERT(p.first < m_torrent_file->end_piece()); |
5436 | |
|
5437 | 0 | if (p.first < piece_index_t(0) |
5438 | 0 | || p.first >= m_torrent_file->end_piece() |
5439 | 0 | || p.second > top_priority) |
5440 | 0 | { |
5441 | 0 | static_assert(std::is_unsigned<decltype(p.second)::underlying_type>::value |
5442 | 0 | , "we need additional condition: p.second < dont_download"); |
5443 | 0 | continue; |
5444 | 0 | } |
5445 | | |
5446 | 0 | filter_updated |= m_picker->set_piece_priority(p.first, p.second); |
5447 | 0 | } |
5448 | 0 | update_gauge(); |
5449 | 0 | if (filter_updated) |
5450 | 0 | { |
5451 | | // we need to save this new state |
5452 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
5453 | |
|
5454 | 0 | update_peer_interest(was_finished); |
5455 | 0 | } |
5456 | |
|
5457 | 0 | state_updated(); |
5458 | 0 | } |
5459 | | |
5460 | | void torrent::prioritize_pieces(aux::vector<download_priority_t, piece_index_t> const& pieces) |
5461 | 1.02k | { |
5462 | 1.02k | INVARIANT_CHECK; |
5463 | | |
5464 | | // this call is only valid on torrents with metadata |
5465 | 1.02k | TORRENT_ASSERT(valid_metadata()); |
5466 | 1.02k | if (is_seed()) return; |
5467 | | |
5468 | 862 | if (!valid_metadata()) |
5469 | 0 | { |
5470 | | #ifndef TORRENT_DISABLE_LOGGING |
5471 | | debug_log("*** PRIORITIZE_PIECES [ ignored. no metadata yet ]"); |
5472 | | #endif |
5473 | 0 | return; |
5474 | 0 | } |
5475 | | |
5476 | 862 | need_picker(); |
5477 | | |
5478 | 862 | piece_index_t index(0); |
5479 | 862 | bool filter_updated = false; |
5480 | 862 | bool const was_finished = is_finished(); |
5481 | 862 | for (auto prio : pieces) |
5482 | 8.62k | { |
5483 | 8.62k | static_assert(std::is_unsigned<decltype(prio)::underlying_type>::value |
5484 | 8.62k | , "we need assert prio >= dont_download"); |
5485 | 8.62k | TORRENT_ASSERT(prio <= top_priority); |
5486 | 8.62k | filter_updated |= m_picker->set_piece_priority(index, prio); |
5487 | 8.62k | ++index; |
5488 | 8.62k | } |
5489 | 862 | update_gauge(); |
5490 | 862 | update_want_tick(); |
5491 | | |
5492 | 862 | if (filter_updated) |
5493 | 756 | { |
5494 | | // we need to save this new state |
5495 | 756 | set_need_save_resume(torrent_handle::if_config_changed); |
5496 | | |
5497 | 756 | update_peer_interest(was_finished); |
5498 | 756 | #ifndef TORRENT_DISABLE_STREAMING |
5499 | 756 | remove_time_critical_pieces(pieces); |
5500 | 756 | #endif |
5501 | 756 | } |
5502 | | |
5503 | 862 | state_updated(); |
5504 | 862 | update_state_list(); |
5505 | 862 | } |
5506 | | |
5507 | | void torrent::piece_priorities(aux::vector<download_priority_t, piece_index_t>* pieces) const |
5508 | 0 | { |
5509 | 0 | INVARIANT_CHECK; |
5510 | | |
5511 | | // this call is only valid on torrents with metadata |
5512 | 0 | if (!valid_metadata()) |
5513 | 0 | { |
5514 | 0 | pieces->clear(); |
5515 | 0 | return; |
5516 | 0 | } |
5517 | | |
5518 | 0 | if (!has_picker()) |
5519 | 0 | { |
5520 | 0 | pieces->clear(); |
5521 | 0 | pieces->resize(m_torrent_file->num_pieces(), default_priority); |
5522 | 0 | return; |
5523 | 0 | } |
5524 | | |
5525 | 0 | TORRENT_ASSERT(m_picker); |
5526 | 0 | m_picker->piece_priorities(*pieces); |
5527 | 0 | } |
5528 | | |
5529 | | namespace |
5530 | | { |
5531 | | aux::vector<download_priority_t, file_index_t> fix_priorities( |
5532 | | aux::vector<download_priority_t, file_index_t> input |
5533 | | , file_storage const* fs) |
5534 | 0 | { |
5535 | 0 | if (fs) input.resize(fs->num_files(), default_priority); |
5536 | |
|
5537 | 0 | for (file_index_t i : input.range()) |
5538 | 0 | { |
5539 | | // initialize pad files to priority 0 |
5540 | 0 | if (input[i] > dont_download && fs && fs->pad_file_at(i)) |
5541 | 0 | input[i] = dont_download; |
5542 | 0 | else if (input[i] > top_priority) |
5543 | 0 | input[i] = top_priority; |
5544 | 0 | } |
5545 | |
|
5546 | 0 | return input; |
5547 | 0 | } |
5548 | | } |
5549 | | |
5550 | | void torrent::on_file_priority(storage_error const& err |
5551 | | , aux::vector<download_priority_t, file_index_t> prios) |
5552 | 0 | { |
5553 | 0 | m_outstanding_file_priority = false; |
5554 | 0 | COMPLETE_ASYNC("file_priority"); |
5555 | |
|
5556 | 0 | if (m_file_priority != prios) |
5557 | 0 | { |
5558 | 0 | update_piece_priorities(prios); |
5559 | 0 | m_file_priority = std::move(prios); |
5560 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
5561 | 0 | #ifndef TORRENT_DISABLE_SHARE_MODE |
5562 | 0 | if (m_share_mode) |
5563 | 0 | recalc_share_mode(); |
5564 | 0 | #endif |
5565 | 0 | } |
5566 | |
|
5567 | 0 | if (err) |
5568 | 0 | { |
5569 | | // in this case, some file priorities failed to get set |
5570 | 0 | if (alerts().should_post<file_error_alert>()) |
5571 | 0 | alerts().emplace_alert<file_error_alert>(err.ec |
5572 | 0 | , resolve_filename(err.file()), err.operation, get_handle()); |
5573 | |
|
5574 | 0 | set_error(err.ec, err.file()); |
5575 | 0 | pause(); |
5576 | 0 | return; |
5577 | 0 | } |
5578 | | |
5579 | 0 | if (alerts().should_post<file_prio_alert>()) |
5580 | 0 | alerts().emplace_alert<file_prio_alert>(get_handle()); |
5581 | |
|
5582 | 0 | if (!m_deferred_file_priorities.empty() && !m_abort) |
5583 | 0 | { |
5584 | 0 | auto new_priority = m_file_priority; |
5585 | | // resize the vector if we have to. The last item in the map has the |
5586 | | // highest file index. |
5587 | 0 | auto const max_idx = std::prev(m_deferred_file_priorities.end())->first; |
5588 | 0 | if (new_priority.end_index() <= max_idx) |
5589 | 0 | { |
5590 | | // any unallocated slot is assumed to have the default priority |
5591 | 0 | new_priority.resize(static_cast<int>(max_idx) + 1, default_priority); |
5592 | 0 | } |
5593 | 0 | for (auto const& p : m_deferred_file_priorities) |
5594 | 0 | { |
5595 | 0 | file_index_t const index = p.first; |
5596 | 0 | download_priority_t const prio = p.second; |
5597 | 0 | new_priority[index] = prio; |
5598 | 0 | } |
5599 | 0 | m_deferred_file_priorities.clear(); |
5600 | 0 | prioritize_files(std::move(new_priority)); |
5601 | 0 | } |
5602 | 0 | } |
5603 | | |
5604 | | void torrent::prioritize_files(aux::vector<download_priority_t, file_index_t> files) |
5605 | 0 | { |
5606 | 0 | INVARIANT_CHECK; |
5607 | |
|
5608 | 0 | auto new_priority = fix_priorities(std::move(files) |
5609 | 0 | , valid_metadata() ? &m_torrent_file->files() : nullptr); |
5610 | |
|
5611 | 0 | m_deferred_file_priorities.clear(); |
5612 | | |
5613 | | // storage may be NULL during shutdown |
5614 | 0 | if (m_storage) |
5615 | 0 | { |
5616 | | // the update of m_file_priority is deferred until the disk job comes |
5617 | | // back, but to preserve sanity and consistency, the piece priorities are |
5618 | | // updated immediately. If, on the off-chance, there's a disk failure, the |
5619 | | // piece priorities still stay the same, but the file priorities are |
5620 | | // possibly not fully updated. |
5621 | |
|
5622 | 0 | m_outstanding_file_priority = true; |
5623 | 0 | ADD_OUTSTANDING_ASYNC("file_priority"); |
5624 | 0 | m_ses.disk_thread().async_set_file_priority(m_storage |
5625 | 0 | , std::move(new_priority) |
5626 | 0 | , [self = shared_from_this()] (storage_error const& ec, aux::vector<download_priority_t, file_index_t> p) |
5627 | 0 | { self->on_file_priority(ec, std::move(p)); }); |
5628 | 0 | m_ses.deferred_submit_jobs(); |
5629 | 0 | } |
5630 | 0 | else |
5631 | 0 | { |
5632 | 0 | m_file_priority = std::move(new_priority); |
5633 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
5634 | 0 | } |
5635 | 0 | } |
5636 | | |
5637 | | void torrent::set_file_priority(file_index_t const index |
5638 | | , download_priority_t prio) |
5639 | 0 | { |
5640 | 0 | INVARIANT_CHECK; |
5641 | | |
5642 | | // setting file priority on a torrent that doesn't have metadata yet is |
5643 | | // similar to having passed in file priorities through add_torrent_params. |
5644 | | // we store the priorities in m_file_priority until we get the metadata |
5645 | 0 | if (index < file_index_t(0) |
5646 | 0 | || (valid_metadata() && index >= m_torrent_file->files().end_file())) |
5647 | 0 | { |
5648 | 0 | return; |
5649 | 0 | } |
5650 | | |
5651 | 0 | prio = aux::clamp(prio, dont_download, top_priority); |
5652 | |
|
5653 | 0 | if (m_outstanding_file_priority) |
5654 | 0 | { |
5655 | 0 | m_deferred_file_priorities[index] = prio; |
5656 | 0 | return; |
5657 | 0 | } |
5658 | | |
5659 | 0 | auto new_priority = m_file_priority; |
5660 | 0 | if (new_priority.end_index() <= index) |
5661 | 0 | { |
5662 | | // any unallocated slot is assumed to have the default priority |
5663 | 0 | new_priority.resize(static_cast<int>(index) + 1, default_priority); |
5664 | 0 | } |
5665 | |
|
5666 | 0 | new_priority[index] = prio; |
5667 | | |
5668 | | // storage may be nullptr during shutdown |
5669 | 0 | if (m_storage) |
5670 | 0 | { |
5671 | 0 | m_outstanding_file_priority = true; |
5672 | 0 | ADD_OUTSTANDING_ASYNC("file_priority"); |
5673 | 0 | m_ses.disk_thread().async_set_file_priority(m_storage |
5674 | 0 | , std::move(new_priority) |
5675 | 0 | , [self = shared_from_this()] (storage_error const& ec, aux::vector<download_priority_t, file_index_t> p) |
5676 | 0 | { self->on_file_priority(ec, std::move(p)); }); |
5677 | 0 | m_ses.deferred_submit_jobs(); |
5678 | 0 | } |
5679 | 0 | else |
5680 | 0 | { |
5681 | 0 | m_file_priority = std::move(new_priority); |
5682 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
5683 | 0 | } |
5684 | 0 | } |
5685 | | |
5686 | | download_priority_t torrent::file_priority(file_index_t const index) const |
5687 | 0 | { |
5688 | 0 | TORRENT_ASSERT_PRECOND(index >= file_index_t(0)); |
5689 | 0 | if (index < file_index_t(0)) return dont_download; |
5690 | | |
5691 | | // if we have metadata, perform additional checks |
5692 | 0 | if (valid_metadata()) |
5693 | 0 | { |
5694 | 0 | file_storage const& fs = m_torrent_file->files(); |
5695 | 0 | TORRENT_ASSERT_PRECOND(index < fs.end_file()); |
5696 | 0 | if (index >= fs.end_file()) return dont_download; |
5697 | | |
5698 | | // pad files always have priority 0 |
5699 | 0 | if (fs.pad_file_at(index)) return dont_download; |
5700 | 0 | } |
5701 | | |
5702 | | // any unallocated slot is assumed to have the default priority |
5703 | 0 | if (m_file_priority.end_index() <= index) return default_priority; |
5704 | | |
5705 | 0 | return m_file_priority[index]; |
5706 | 0 | } |
5707 | | |
5708 | | void torrent::file_priorities(aux::vector<download_priority_t, file_index_t>* files) const |
5709 | 0 | { |
5710 | 0 | INVARIANT_CHECK; |
5711 | |
|
5712 | 0 | files->assign(m_file_priority.begin(), m_file_priority.end()); |
5713 | |
|
5714 | 0 | if (!valid_metadata()) |
5715 | 0 | { |
5716 | 0 | return; |
5717 | 0 | } |
5718 | | |
5719 | 0 | files->resize(m_torrent_file->num_files(), default_priority); |
5720 | 0 | } |
5721 | | |
5722 | | void torrent::update_piece_priorities( |
5723 | | aux::vector<download_priority_t, file_index_t> const& file_prios) |
5724 | 1.02k | { |
5725 | 1.02k | INVARIANT_CHECK; |
5726 | | |
5727 | 1.02k | if (m_torrent_file->num_pieces() == 0) return; |
5728 | | |
5729 | 1.02k | bool need_update = false; |
5730 | | // initialize the piece priorities to 0, then only allow |
5731 | | // setting higher priorities |
5732 | 1.02k | aux::vector<download_priority_t, piece_index_t> pieces(aux::numeric_cast<std::size_t>( |
5733 | 1.02k | m_torrent_file->num_pieces()), dont_download); |
5734 | 1.02k | file_storage const& fs = m_torrent_file->files(); |
5735 | 1.02k | for (auto const i : fs.file_range()) |
5736 | 1.02k | { |
5737 | 1.02k | std::int64_t const size = m_torrent_file->files().file_size(i); |
5738 | 1.02k | if (size == 0) continue; |
5739 | | |
5740 | | // pad files always have priority 0 |
5741 | 1.02k | download_priority_t const file_prio |
5742 | 1.02k | = fs.pad_file_at(i) ? dont_download |
5743 | 1.02k | : i >= file_prios.end_index() ? default_priority |
5744 | 1.02k | : file_prios[i]; |
5745 | | |
5746 | 1.02k | if (file_prio == dont_download) |
5747 | 906 | { |
5748 | | // the pieces already start out as priority 0, no need to update |
5749 | | // the pieces vector in this case |
5750 | 906 | need_update = true; |
5751 | 906 | continue; |
5752 | 906 | } |
5753 | | |
5754 | | // mark all pieces of the file with this file's priority |
5755 | | // but only if the priority is higher than the pieces |
5756 | | // already set (to avoid problems with overlapping pieces) |
5757 | 122 | piece_index_t start; |
5758 | 122 | piece_index_t end; |
5759 | 122 | std::tie(start, end) = file_piece_range_inclusive(fs, i); |
5760 | | |
5761 | | // if one piece spans several files, we might |
5762 | | // come here several times with the same start_piece, end_piece |
5763 | 1.34k | for (piece_index_t p = start; p < end; ++p) |
5764 | 1.22k | pieces[p] = std::max(pieces[p], file_prio); |
5765 | | |
5766 | 122 | need_update = true; |
5767 | 122 | } |
5768 | 1.02k | if (need_update) prioritize_pieces(pieces); |
5769 | 1.02k | } |
5770 | | |
5771 | | // this is called when piece priorities have been updated |
5772 | | // updates the interested flag in peers |
5773 | | void torrent::update_peer_interest(bool const was_finished) |
5774 | 756 | { |
5775 | 756 | for (auto i = begin(); i != end();) |
5776 | 0 | { |
5777 | 0 | peer_connection* p = *i; |
5778 | | // update_interest may disconnect the peer and |
5779 | | // invalidate the iterator |
5780 | 0 | ++i; |
5781 | 0 | p->update_interest(); |
5782 | 0 | } |
5783 | | |
5784 | 756 | if (!is_downloading_state(m_state)) |
5785 | 756 | { |
5786 | | #ifndef TORRENT_DISABLE_LOGGING |
5787 | | debug_log("*** UPDATE_PEER_INTEREST [ skipping, state: %d ]" |
5788 | | , int(m_state)); |
5789 | | #endif |
5790 | 756 | return; |
5791 | 756 | } |
5792 | | |
5793 | | #ifndef TORRENT_DISABLE_LOGGING |
5794 | | if (should_log()) |
5795 | | { |
5796 | | debug_log("*** UPDATE_PEER_INTEREST [ finished: %d was_finished %d ]" |
5797 | | , is_finished(), was_finished); |
5798 | | } |
5799 | | #endif |
5800 | | |
5801 | | // the torrent just became finished |
5802 | 0 | if (!was_finished && is_finished()) |
5803 | 0 | { |
5804 | 0 | finished(); |
5805 | 0 | } |
5806 | 0 | else if (was_finished && !is_finished()) |
5807 | 0 | { |
5808 | | // if we used to be finished, but we aren't anymore |
5809 | | // we may need to connect to peers again |
5810 | 0 | resume_download(); |
5811 | 0 | } |
5812 | 0 | } |
5813 | | |
5814 | | void torrent::post_trackers() |
5815 | 0 | { |
5816 | 0 | auto t = trackers(); |
5817 | 0 | m_ses.alerts().emplace_alert<tracker_list_alert>(get_handle(), std::move(t)); |
5818 | 0 | } |
5819 | | |
5820 | | std::vector<announce_entry> torrent::trackers() const |
5821 | 0 | { |
5822 | 0 | std::vector<announce_entry> ret; |
5823 | 0 | ret.reserve(m_trackers.size()); |
5824 | 0 | for (auto const& t : m_trackers) |
5825 | 0 | { |
5826 | 0 | ret.emplace_back(t.url); |
5827 | 0 | auto& tr = ret.back(); |
5828 | 0 | tr.source = t.source; |
5829 | 0 | tr.trackerid = t.trackerid; |
5830 | 0 | tr.verified = t.verified; |
5831 | 0 | tr.tier = t.tier; |
5832 | 0 | tr.fail_limit = t.fail_limit; |
5833 | 0 | tr.endpoints.reserve(t.endpoints.size()); |
5834 | 0 | for (auto const& ep : t.endpoints) |
5835 | 0 | { |
5836 | 0 | tr.endpoints.emplace_back(); |
5837 | 0 | auto& aep = tr.endpoints.back(); |
5838 | 0 | aep.local_endpoint = ep.local_endpoint; |
5839 | 0 | aep.enabled = ep.enabled; |
5840 | |
|
5841 | 0 | for (protocol_version v : {protocol_version::V1, protocol_version::V2}) |
5842 | 0 | { |
5843 | 0 | aep.info_hashes[v].message = ep.info_hashes[v].message; |
5844 | 0 | aep.info_hashes[v].last_error = ep.info_hashes[v].last_error; |
5845 | 0 | aep.info_hashes[v].next_announce = ep.info_hashes[v].next_announce; |
5846 | 0 | aep.info_hashes[v].min_announce = ep.info_hashes[v].min_announce; |
5847 | 0 | aep.info_hashes[v].scrape_incomplete = ep.info_hashes[v].scrape_incomplete; |
5848 | 0 | aep.info_hashes[v].scrape_complete = ep.info_hashes[v].scrape_complete; |
5849 | 0 | aep.info_hashes[v].scrape_downloaded = ep.info_hashes[v].scrape_downloaded; |
5850 | 0 | aep.info_hashes[v].fails = ep.info_hashes[v].fails; |
5851 | 0 | aep.info_hashes[v].updating = ep.info_hashes[v].updating; |
5852 | 0 | aep.info_hashes[v].start_sent = ep.info_hashes[v].start_sent; |
5853 | 0 | aep.info_hashes[v].complete_sent = ep.info_hashes[v].complete_sent; |
5854 | 0 | aep.info_hashes[v].triggered_manually = ep.info_hashes[v].triggered_manually; |
5855 | 0 | #if TORRENT_ABI_VERSION == 1 |
5856 | 0 | tr.complete_sent |= ep.info_hashes[v].complete_sent; |
5857 | 0 | #endif |
5858 | 0 | } |
5859 | 0 | #if TORRENT_ABI_VERSION <= 2 |
5860 | 0 | #include "libtorrent/aux_/disable_warnings_push.hpp" |
5861 | 0 | aep.message = aep.info_hashes[protocol_version::V1].message; |
5862 | 0 | aep.scrape_incomplete = ep.info_hashes[protocol_version::V1].scrape_incomplete; |
5863 | 0 | aep.scrape_complete = ep.info_hashes[protocol_version::V1].scrape_complete; |
5864 | 0 | aep.scrape_downloaded = ep.info_hashes[protocol_version::V1].scrape_downloaded; |
5865 | 0 | aep.complete_sent = ep.info_hashes[protocol_version::V1].complete_sent; |
5866 | 0 | aep.last_error = ep.info_hashes[protocol_version::V1].last_error; |
5867 | 0 | aep.fails = ep.info_hashes[protocol_version::V1].fails; |
5868 | 0 | aep.next_announce = ep.info_hashes[protocol_version::V1].next_announce; |
5869 | 0 | aep.min_announce = ep.info_hashes[protocol_version::V1].min_announce; |
5870 | 0 | aep.updating = ep.info_hashes[protocol_version::V1].updating; |
5871 | 0 | #include "libtorrent/aux_/disable_warnings_pop.hpp" |
5872 | 0 | #endif |
5873 | 0 | } |
5874 | 0 | } |
5875 | 0 | return ret; |
5876 | 0 | } |
5877 | | |
5878 | | void torrent::replace_trackers(std::vector<announce_entry> const& urls) |
5879 | 0 | { |
5880 | 0 | m_trackers.clear(); |
5881 | 0 | for (auto const& t : urls) |
5882 | 0 | { |
5883 | 0 | if (t.url.empty()) continue; |
5884 | 0 | m_trackers.emplace_back(t); |
5885 | 0 | } |
5886 | | |
5887 | | // make sure the trackers are correctly ordered by tier |
5888 | 0 | std::sort(m_trackers.begin(), m_trackers.end() |
5889 | 0 | , [](aux::announce_entry const& lhs, aux::announce_entry const& rhs) |
5890 | 0 | { return lhs.tier < rhs.tier; }); |
5891 | |
|
5892 | 0 | m_last_working_tracker = -1; |
5893 | |
|
5894 | 0 | if (settings().get_bool(settings_pack::prefer_udp_trackers)) |
5895 | 0 | prioritize_udp_trackers(); |
5896 | |
|
5897 | 0 | if (m_announcing && !m_trackers.empty()) announce_with_tracker(); |
5898 | |
|
5899 | 0 | set_need_save_resume(torrent_handle::if_metadata_changed); |
5900 | 0 | } |
5901 | | |
5902 | | void torrent::prioritize_udp_trackers() |
5903 | 1.88k | { |
5904 | | // look for udp-trackers |
5905 | 1.88k | for (auto i = m_trackers.begin(), end(m_trackers.end()); i != end; ++i) |
5906 | 0 | { |
5907 | 0 | if (i->url.substr(0, 6) != "udp://") continue; |
5908 | | // now, look for trackers with the same hostname |
5909 | | // that is has higher priority than this one |
5910 | | // if we find one, swap with the udp-tracker |
5911 | 0 | error_code ec; |
5912 | 0 | std::string udp_hostname; |
5913 | 0 | using std::ignore; |
5914 | 0 | std::tie(ignore, ignore, udp_hostname, ignore, ignore) |
5915 | 0 | = parse_url_components(i->url, ec); |
5916 | 0 | for (auto j = m_trackers.begin(); j != i; ++j) |
5917 | 0 | { |
5918 | 0 | std::string hostname; |
5919 | 0 | std::tie(ignore, ignore, hostname, ignore, ignore) |
5920 | 0 | = parse_url_components(j->url, ec); |
5921 | 0 | if (hostname != udp_hostname) continue; |
5922 | 0 | if (j->url.substr(0, 6) == "udp://") continue; |
5923 | 0 | using std::swap; |
5924 | 0 | using std::iter_swap; |
5925 | 0 | swap(i->tier, j->tier); |
5926 | 0 | iter_swap(i, j); |
5927 | 0 | break; |
5928 | 0 | } |
5929 | 0 | } |
5930 | 1.88k | } |
5931 | | |
5932 | | bool torrent::add_tracker(announce_entry const& url) |
5933 | 0 | { |
5934 | 0 | if (url.url.empty()) return false; |
5935 | 0 | if(auto k = find_tracker(url.url)) |
5936 | 0 | { |
5937 | 0 | k->source |= url.source; |
5938 | 0 | return false; |
5939 | 0 | } |
5940 | 0 | auto k = std::upper_bound(m_trackers.begin(), m_trackers.end(), url.tier |
5941 | 0 | , [] (int tier, aux::announce_entry const& v) { return tier < v.tier; }); |
5942 | 0 | if (k - m_trackers.begin() < m_last_working_tracker) ++m_last_working_tracker; |
5943 | 0 | k = m_trackers.insert(k, aux::announce_entry(url.url)); |
5944 | 0 | if (url.source == 0) k->source = announce_entry::source_client; |
5945 | 0 | else k->source = url.source; |
5946 | 0 | k->trackerid = url.trackerid; |
5947 | 0 | k->tier = url.tier; |
5948 | 0 | k->fail_limit = url.fail_limit; |
5949 | 0 | set_need_save_resume(torrent_handle::if_metadata_changed); |
5950 | 0 | if (m_announcing && !m_trackers.empty()) announce_with_tracker(); |
5951 | 0 | return true; |
5952 | 0 | } |
5953 | | |
5954 | | bool torrent::choke_peer(peer_connection& c) |
5955 | 0 | { |
5956 | 0 | INVARIANT_CHECK; |
5957 | |
|
5958 | 0 | TORRENT_ASSERT(!c.is_choked()); |
5959 | 0 | TORRENT_ASSERT(!c.ignore_unchoke_slots()); |
5960 | 0 | TORRENT_ASSERT(m_num_uploads > 0); |
5961 | 0 | if (!c.send_choke()) return false; |
5962 | 0 | --m_num_uploads; |
5963 | 0 | state_updated(); |
5964 | 0 | return true; |
5965 | 0 | } |
5966 | | |
5967 | | bool torrent::unchoke_peer(peer_connection& c, bool optimistic) |
5968 | 0 | { |
5969 | 0 | INVARIANT_CHECK; |
5970 | |
|
5971 | 0 | TORRENT_ASSERT(!m_graceful_pause_mode); |
5972 | 0 | TORRENT_ASSERT(c.is_choked()); |
5973 | 0 | TORRENT_ASSERT(!c.ignore_unchoke_slots()); |
5974 | | // when we're unchoking the optimistic slots, we might |
5975 | | // exceed the limit temporarily while we're iterating |
5976 | | // over the peers |
5977 | 0 | if (m_num_uploads >= m_max_uploads && !optimistic) return false; |
5978 | 0 | if (!c.send_unchoke()) return false; |
5979 | 0 | ++m_num_uploads; |
5980 | 0 | state_updated(); |
5981 | 0 | return true; |
5982 | 0 | } |
5983 | | |
5984 | | void torrent::trigger_unchoke() noexcept |
5985 | 0 | { |
5986 | 0 | m_ses.trigger_unchoke(); |
5987 | 0 | } |
5988 | | |
5989 | | void torrent::trigger_optimistic_unchoke() noexcept |
5990 | 0 | { |
5991 | 0 | m_ses.trigger_optimistic_unchoke(); |
5992 | 0 | } |
5993 | | |
5994 | | void torrent::cancel_block(piece_block block) |
5995 | 0 | { |
5996 | 0 | INVARIANT_CHECK; |
5997 | |
|
5998 | 0 | TORRENT_ASSERT(has_picker()); |
5999 | |
|
6000 | 0 | for (auto p : m_connections) |
6001 | 0 | { |
6002 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
6003 | 0 | p->cancel_request(block); |
6004 | 0 | } |
6005 | 0 | } |
6006 | | |
6007 | | #ifdef TORRENT_SSL_PEERS |
6008 | | // certificate is a filename to a .pem file which is our |
6009 | | // certificate. The certificate must be signed by the root |
6010 | | // cert of the torrent file. any peer we connect to or that |
6011 | | // connect to use must present a valid certificate signed |
6012 | | // by the torrent root cert as well |
6013 | | void torrent::set_ssl_cert(std::string const& certificate |
6014 | | , std::string const& private_key |
6015 | | , std::string const& dh_params |
6016 | | , std::string const& passphrase) |
6017 | 0 | { |
6018 | 0 | if (!m_ssl_ctx) |
6019 | 0 | { |
6020 | 0 | if (alerts().should_post<torrent_error_alert>()) |
6021 | 0 | alerts().emplace_alert<torrent_error_alert>(get_handle() |
6022 | 0 | , errors::not_an_ssl_torrent, ""); |
6023 | 0 | return; |
6024 | 0 | } |
6025 | | |
6026 | 0 | error_code ec; |
6027 | 0 | m_ssl_ctx->set_password_callback( |
6028 | 0 | [passphrase](std::size_t, ssl::context::password_purpose purpose) |
6029 | 0 | { |
6030 | 0 | return purpose == ssl::context::for_reading ? passphrase : ""; |
6031 | 0 | } |
6032 | 0 | , ec); |
6033 | 0 | if (ec) |
6034 | 0 | { |
6035 | 0 | if (alerts().should_post<torrent_error_alert>()) |
6036 | 0 | alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, ""); |
6037 | 0 | } |
6038 | 0 | m_ssl_ctx->use_certificate_file(certificate, ssl::context::pem, ec); |
6039 | 0 | if (ec) |
6040 | 0 | { |
6041 | 0 | if (alerts().should_post<torrent_error_alert>()) |
6042 | 0 | alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, certificate); |
6043 | 0 | } |
6044 | | #ifndef TORRENT_DISABLE_LOGGING |
6045 | | if (should_log()) |
6046 | | debug_log("*** use certificate file: %s", ec.message().c_str()); |
6047 | | #endif |
6048 | 0 | m_ssl_ctx->use_private_key_file(private_key, ssl::context::pem, ec); |
6049 | 0 | if (ec) |
6050 | 0 | { |
6051 | 0 | if (alerts().should_post<torrent_error_alert>()) |
6052 | 0 | alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, private_key); |
6053 | 0 | } |
6054 | | #ifndef TORRENT_DISABLE_LOGGING |
6055 | | if (should_log()) |
6056 | | debug_log("*** use private key file: %s", ec.message().c_str()); |
6057 | | #endif |
6058 | 0 | m_ssl_ctx->use_tmp_dh_file(dh_params, ec); |
6059 | 0 | if (ec) |
6060 | 0 | { |
6061 | 0 | if (alerts().should_post<torrent_error_alert>()) |
6062 | 0 | alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, dh_params); |
6063 | 0 | } |
6064 | | #ifndef TORRENT_DISABLE_LOGGING |
6065 | | if (should_log()) |
6066 | | debug_log("*** use DH file: %s", ec.message().c_str()); |
6067 | | #endif |
6068 | 0 | } |
6069 | | |
6070 | | void torrent::set_ssl_cert_buffer(std::string const& certificate |
6071 | | , std::string const& private_key |
6072 | | , std::string const& dh_params) |
6073 | 0 | { |
6074 | 0 | if (!m_ssl_ctx) return; |
6075 | | |
6076 | 0 | boost::asio::const_buffer certificate_buf(certificate.c_str(), certificate.size()); |
6077 | |
|
6078 | 0 | error_code ec; |
6079 | 0 | m_ssl_ctx->use_certificate(certificate_buf, ssl::context::pem, ec); |
6080 | 0 | if (ec) |
6081 | 0 | { |
6082 | 0 | if (alerts().should_post<torrent_error_alert>()) |
6083 | 0 | alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, "[certificate]"); |
6084 | 0 | } |
6085 | |
|
6086 | 0 | boost::asio::const_buffer private_key_buf(private_key.c_str(), private_key.size()); |
6087 | 0 | m_ssl_ctx->use_private_key(private_key_buf, ssl::context::pem, ec); |
6088 | 0 | if (ec) |
6089 | 0 | { |
6090 | 0 | if (alerts().should_post<torrent_error_alert>()) |
6091 | 0 | alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, "[private key]"); |
6092 | 0 | } |
6093 | |
|
6094 | 0 | boost::asio::const_buffer dh_params_buf(dh_params.c_str(), dh_params.size()); |
6095 | 0 | m_ssl_ctx->use_tmp_dh(dh_params_buf, ec); |
6096 | 0 | if (ec) |
6097 | 0 | { |
6098 | 0 | if (alerts().should_post<torrent_error_alert>()) |
6099 | 0 | alerts().emplace_alert<torrent_error_alert>(get_handle(), ec, "[dh params]"); |
6100 | 0 | } |
6101 | 0 | } |
6102 | | |
6103 | | #endif |
6104 | | |
6105 | | void torrent::on_exception(std::exception const&) |
6106 | 0 | { |
6107 | 0 | set_error(errors::no_memory, torrent_status::error_file_none); |
6108 | 0 | } |
6109 | | |
6110 | | void torrent::on_error(error_code const& ec) |
6111 | 0 | { |
6112 | 0 | set_error(ec, torrent_status::error_file_none); |
6113 | 0 | } |
6114 | | |
6115 | | void torrent::remove_connection(peer_connection const* p) |
6116 | 282 | { |
6117 | 282 | TORRENT_ASSERT(m_iterating_connections == 0); |
6118 | 282 | auto const i = sorted_find(m_connections, p); |
6119 | 282 | if (i != m_connections.end()) |
6120 | 282 | m_connections.erase(i); |
6121 | 282 | } |
6122 | | |
6123 | | void torrent::remove_peer(std::shared_ptr<peer_connection> p) noexcept |
6124 | 282 | { |
6125 | 282 | TORRENT_ASSERT(p); |
6126 | 282 | TORRENT_ASSERT(is_single_thread()); |
6127 | 282 | TORRENT_ASSERT(std::count(m_peers_to_disconnect.begin() |
6128 | 282 | , m_peers_to_disconnect.end(), p) == 0); |
6129 | | |
6130 | 282 | auto it = m_outgoing_pids.find(p->our_pid()); |
6131 | 282 | if (it != m_outgoing_pids.end()) |
6132 | 0 | { |
6133 | 0 | m_outgoing_pids.erase(it); |
6134 | 0 | } |
6135 | | |
6136 | | // only schedule the peer for actual removal if in fact |
6137 | | // we can be sure peer_connection will be kept alive until |
6138 | | // the deferred function is called. If a peer_connection |
6139 | | // has not associated torrent, the session_impl object may |
6140 | | // remove it at any time, which may be while the non-owning |
6141 | | // pointer in m_peers_to_disconnect (if added to it) is |
6142 | | // waiting for the deferred function to be called. |
6143 | | // |
6144 | | // one example of this situation is if for example, this |
6145 | | // function is called from the attach_peer path and fail to |
6146 | | // do so because of too many connections. |
6147 | 282 | bool const is_attached = p->associated_torrent().lock().get() == this; |
6148 | 282 | if (is_attached) |
6149 | 282 | { |
6150 | 282 | std::weak_ptr<torrent> weak_t = shared_from_this(); |
6151 | 282 | TORRENT_ASSERT_VAL(m_peers_to_disconnect.capacity() > m_peers_to_disconnect.size() |
6152 | 282 | , m_peers_to_disconnect.capacity()); |
6153 | 282 | m_peers_to_disconnect.push_back(p); |
6154 | | |
6155 | 282 | using deferred_handler_type = aux::handler< |
6156 | 282 | torrent |
6157 | 282 | , decltype(&torrent::on_remove_peers) |
6158 | 282 | , &torrent::on_remove_peers |
6159 | 282 | , &torrent::on_error |
6160 | 282 | , &torrent::on_exception |
6161 | 282 | , decltype(m_deferred_handler_storage) |
6162 | 282 | , &torrent::m_deferred_handler_storage |
6163 | 282 | >; |
6164 | 282 | static_assert(sizeof(deferred_handler_type) == sizeof(std::shared_ptr<peer_connection>) |
6165 | 282 | , "deferred handler does not have the expected size"); |
6166 | 282 | m_deferred_disconnect.post_deferred(m_ses.get_context(), deferred_handler_type(shared_from_this())); |
6167 | 282 | } |
6168 | 0 | else |
6169 | 0 | { |
6170 | | // if the peer was inserted in m_connections but instructed to |
6171 | | // be removed from this torrent, just remove it from it, see |
6172 | | // attach_peer logic. |
6173 | 0 | remove_connection(p.get()); |
6174 | 0 | } |
6175 | | |
6176 | 282 | torrent_peer* pp = p->peer_info_struct(); |
6177 | 282 | if (ready_for_connections()) |
6178 | 282 | { |
6179 | 282 | TORRENT_ASSERT(p->associated_torrent().lock().get() == nullptr |
6180 | 282 | || p->associated_torrent().lock().get() == this); |
6181 | | |
6182 | 282 | if (has_picker()) |
6183 | 282 | { |
6184 | 282 | if (p->is_seed()) |
6185 | 13 | { |
6186 | 13 | m_picker->dec_refcount_all(pp); |
6187 | 13 | } |
6188 | 269 | else |
6189 | 269 | { |
6190 | 269 | auto const& pieces = p->get_bitfield(); |
6191 | 269 | TORRENT_ASSERT(pieces.count() <= pieces.size()); |
6192 | 269 | m_picker->dec_refcount(pieces, pp); |
6193 | 269 | } |
6194 | 282 | } |
6195 | 282 | } |
6196 | | |
6197 | 282 | if (!p->is_choked() && !p->ignore_unchoke_slots()) |
6198 | 0 | { |
6199 | 0 | --m_num_uploads; |
6200 | 0 | trigger_unchoke(); |
6201 | 0 | } |
6202 | | |
6203 | 282 | if (pp) |
6204 | 282 | { |
6205 | 282 | if (pp->optimistically_unchoked) |
6206 | 0 | { |
6207 | 0 | pp->optimistically_unchoked = false; |
6208 | 0 | m_stats_counters.inc_stats_counter( |
6209 | 0 | counters::num_peers_up_unchoked_optimistic, -1); |
6210 | 0 | trigger_optimistic_unchoke(); |
6211 | 0 | } |
6212 | | |
6213 | 282 | TORRENT_ASSERT(pp->prev_amount_upload == 0); |
6214 | 282 | TORRENT_ASSERT(pp->prev_amount_download == 0); |
6215 | 282 | pp->prev_amount_download += aux::numeric_cast<std::uint32_t>(p->statistics().total_payload_download() >> 10); |
6216 | 282 | pp->prev_amount_upload += aux::numeric_cast<std::uint32_t>(p->statistics().total_payload_upload() >> 10); |
6217 | | |
6218 | | // only decrement the seed count if the peer completed attaching to the torrent |
6219 | | // otherwise the seed count did not get incremented for this peer |
6220 | 282 | if (is_attached && pp->seed) |
6221 | 35 | { |
6222 | 35 | TORRENT_ASSERT(m_num_seeds > 0); |
6223 | 35 | --m_num_seeds; |
6224 | 35 | } |
6225 | | |
6226 | 282 | if (pp->connection && m_peer_list) |
6227 | 282 | { |
6228 | 282 | torrent_state st = get_peer_list_state(); |
6229 | 282 | m_peer_list->connection_closed(*p, m_ses.session_time(), &st); |
6230 | 282 | peers_erased(st.erased); |
6231 | 282 | } |
6232 | 282 | } |
6233 | | |
6234 | 282 | p->set_peer_info(nullptr); |
6235 | | |
6236 | 282 | update_want_peers(); |
6237 | 282 | update_want_tick(); |
6238 | 282 | } |
6239 | | |
6240 | | void torrent::on_remove_peers() noexcept |
6241 | 2.16k | { |
6242 | 2.16k | TORRENT_ASSERT(is_single_thread()); |
6243 | 2.16k | INVARIANT_CHECK; |
6244 | | |
6245 | 2.16k | #if TORRENT_USE_ASSERTS |
6246 | 2.16k | auto const num = m_peers_to_disconnect.size(); |
6247 | 2.16k | #endif |
6248 | 2.16k | for (auto const& p : m_peers_to_disconnect) |
6249 | 282 | { |
6250 | 282 | TORRENT_ASSERT(p); |
6251 | 282 | TORRENT_ASSERT(p->associated_torrent().lock().get() == this); |
6252 | | |
6253 | 282 | remove_connection(p.get()); |
6254 | 282 | m_ses.close_connection(p.get()); |
6255 | 282 | } |
6256 | 2.16k | TORRENT_ASSERT_VAL(m_peers_to_disconnect.size() == num, m_peers_to_disconnect.size() - num); |
6257 | 2.16k | m_peers_to_disconnect.clear(); |
6258 | | |
6259 | 2.16k | if (m_graceful_pause_mode && m_connections.empty()) |
6260 | 0 | { |
6261 | | // we're in graceful pause mode and this was the last peer we |
6262 | | // disconnected. This will clear the graceful_pause_mode and post the |
6263 | | // torrent_paused_alert. |
6264 | 0 | TORRENT_ASSERT(is_paused()); |
6265 | | |
6266 | | // this will post torrent_paused alert |
6267 | 0 | set_paused(true); |
6268 | 0 | } |
6269 | | |
6270 | 2.16k | update_want_peers(); |
6271 | 2.16k | update_want_tick(); |
6272 | 2.16k | } |
6273 | | |
6274 | | void torrent::remove_web_seed_iter(std::list<web_seed_t>::iterator web) |
6275 | 0 | { |
6276 | 0 | if (web->resolving) |
6277 | 0 | { |
6278 | 0 | web->removed = true; |
6279 | 0 | } |
6280 | 0 | else |
6281 | 0 | { |
6282 | | #ifndef TORRENT_DISABLE_LOGGING |
6283 | | debug_log("removing web seed: \"%s\"", web->url.c_str()); |
6284 | | #endif |
6285 | |
|
6286 | 0 | auto* peer = static_cast<peer_connection*>(web->peer_info.connection); |
6287 | 0 | if (peer != nullptr) |
6288 | 0 | { |
6289 | | // if we have a connection for this web seed, we also need to |
6290 | | // disconnect it and clear its reference to the peer_info object |
6291 | | // that's part of the web_seed_t we're about to remove |
6292 | 0 | TORRENT_ASSERT(peer->m_in_use == 1337); |
6293 | 0 | peer->disconnect(boost::asio::error::operation_aborted, operation_t::bittorrent); |
6294 | 0 | peer->set_peer_info(nullptr); |
6295 | 0 | } |
6296 | 0 | if (has_picker()) picker().clear_peer(&web->peer_info); |
6297 | |
|
6298 | 0 | m_web_seeds.erase(web); |
6299 | 0 | } |
6300 | |
|
6301 | 0 | update_want_tick(); |
6302 | 0 | } |
6303 | | |
6304 | | void torrent::connect_to_url_seed(std::list<web_seed_t>::iterator web) |
6305 | 0 | { |
6306 | 0 | TORRENT_ASSERT(is_single_thread()); |
6307 | 0 | INVARIANT_CHECK; |
6308 | |
|
6309 | 0 | TORRENT_ASSERT(!web->resolving); |
6310 | 0 | if (web->resolving) return; |
6311 | | |
6312 | 0 | if (num_peers() >= int(m_max_connections) |
6313 | 0 | || m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit)) |
6314 | 0 | return; |
6315 | | |
6316 | 0 | std::string protocol; |
6317 | 0 | std::string auth; |
6318 | 0 | std::string hostname; |
6319 | 0 | int port; |
6320 | 0 | std::string path; |
6321 | 0 | error_code ec; |
6322 | 0 | std::tie(protocol, auth, hostname, port, path) |
6323 | 0 | = parse_url_components(web->url, ec); |
6324 | |
|
6325 | 0 | if (!settings().get_bool(settings_pack::allow_idna) && is_idna(hostname)) |
6326 | 0 | { |
6327 | | #ifndef TORRENT_DISABLE_LOGGING |
6328 | | if (should_log()) |
6329 | | debug_log("IDNA disallowed in web seeds: %s", web->url.c_str()); |
6330 | | #endif |
6331 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6332 | 0 | { |
6333 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle() |
6334 | 0 | , web->url, error_code(errors::blocked_by_idna)); |
6335 | 0 | } |
6336 | | // never try it again |
6337 | 0 | web->disabled = true; |
6338 | 0 | return; |
6339 | 0 | } |
6340 | | |
6341 | 0 | if (port == -1) |
6342 | 0 | { |
6343 | 0 | port = protocol == "http" ? 80 : 443; |
6344 | 0 | } |
6345 | |
|
6346 | 0 | if (ec) |
6347 | 0 | { |
6348 | | #ifndef TORRENT_DISABLE_LOGGING |
6349 | | if (should_log()) |
6350 | | debug_log("failed to parse web seed url: %s", ec.message().c_str()); |
6351 | | #endif |
6352 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6353 | 0 | { |
6354 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle() |
6355 | 0 | , web->url, ec); |
6356 | 0 | } |
6357 | | // never try it again |
6358 | 0 | remove_web_seed_iter(web); |
6359 | 0 | return; |
6360 | 0 | } |
6361 | | |
6362 | 0 | if (web->peer_info.banned) |
6363 | 0 | { |
6364 | | #ifndef TORRENT_DISABLE_LOGGING |
6365 | | debug_log("banned web seed: %s", web->url.c_str()); |
6366 | | #endif |
6367 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6368 | 0 | { |
6369 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url |
6370 | 0 | , libtorrent::errors::peer_banned); |
6371 | 0 | } |
6372 | | // never try it again |
6373 | 0 | web->disabled = true; |
6374 | 0 | return; |
6375 | 0 | } |
6376 | | |
6377 | 0 | #if TORRENT_USE_SSL |
6378 | 0 | if (protocol != "http" && protocol != "https") |
6379 | | #else |
6380 | | if (protocol != "http") |
6381 | | #endif |
6382 | 0 | { |
6383 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6384 | 0 | { |
6385 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url, errors::unsupported_url_protocol); |
6386 | 0 | } |
6387 | | // never try it again for this session |
6388 | 0 | web->disabled = true; |
6389 | 0 | return; |
6390 | 0 | } |
6391 | | |
6392 | 0 | if (hostname.empty()) |
6393 | 0 | { |
6394 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6395 | 0 | { |
6396 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url |
6397 | 0 | , errors::invalid_hostname); |
6398 | 0 | } |
6399 | | // never try it again |
6400 | 0 | remove_web_seed_iter(web); |
6401 | 0 | return; |
6402 | 0 | } |
6403 | | |
6404 | 0 | if (port == 0) |
6405 | 0 | { |
6406 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6407 | 0 | { |
6408 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url |
6409 | 0 | , errors::invalid_port); |
6410 | 0 | } |
6411 | | // never try it again |
6412 | 0 | remove_web_seed_iter(web); |
6413 | 0 | return; |
6414 | 0 | } |
6415 | | |
6416 | 0 | if (m_ses.get_port_filter().access(std::uint16_t(port)) & port_filter::blocked) |
6417 | 0 | { |
6418 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6419 | 0 | { |
6420 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle() |
6421 | 0 | , web->url, errors::port_blocked); |
6422 | 0 | } |
6423 | | // never try it again |
6424 | 0 | web->disabled = true; |
6425 | 0 | return; |
6426 | 0 | } |
6427 | | |
6428 | 0 | if (!web->endpoints.empty()) |
6429 | 0 | { |
6430 | 0 | connect_web_seed(web, web->endpoints.front()); |
6431 | 0 | return; |
6432 | 0 | } |
6433 | | |
6434 | 0 | aux::proxy_settings const& ps = m_ses.proxy(); |
6435 | 0 | if ((ps.type == settings_pack::http |
6436 | 0 | || ps.type == settings_pack::http_pw) |
6437 | 0 | && ps.proxy_peer_connections) |
6438 | 0 | { |
6439 | | #ifndef TORRENT_DISABLE_LOGGING |
6440 | | debug_log("resolving proxy for web seed: %s", web->url.c_str()); |
6441 | | #endif |
6442 | |
|
6443 | 0 | std::uint16_t const proxy_port = ps.port; |
6444 | | |
6445 | | // use proxy |
6446 | 0 | web->resolving = true; |
6447 | 0 | m_ses.get_resolver().async_resolve(ps.hostname, aux::resolver_interface::abort_on_shutdown |
6448 | 0 | , [self = shared_from_this(), web, proxy_port](error_code const& e, std::vector<address> const& addrs) |
6449 | 0 | { self->wrap(&torrent::on_proxy_name_lookup, e, addrs, web, proxy_port); }); |
6450 | 0 | } |
6451 | 0 | else if (ps.proxy_hostnames |
6452 | 0 | && (ps.type == settings_pack::socks5 |
6453 | 0 | || ps.type == settings_pack::socks5_pw) |
6454 | 0 | && ps.proxy_peer_connections) |
6455 | 0 | { |
6456 | 0 | connect_web_seed(web, {address(), std::uint16_t(port)}); |
6457 | 0 | } |
6458 | 0 | else |
6459 | 0 | { |
6460 | | #ifndef TORRENT_DISABLE_LOGGING |
6461 | | debug_log("resolving web seed: \"%s\" %s", hostname.c_str(), web->url.c_str()); |
6462 | | #endif |
6463 | |
|
6464 | 0 | auto self = shared_from_this(); |
6465 | 0 | web->resolving = true; |
6466 | |
|
6467 | 0 | m_ses.get_resolver().async_resolve(hostname, aux::resolver_interface::abort_on_shutdown |
6468 | 0 | , [self, web, port](error_code const& e, std::vector<address> const& addrs) |
6469 | 0 | { |
6470 | 0 | self->wrap(&torrent::on_name_lookup, e, addrs, port, web); |
6471 | 0 | }); |
6472 | 0 | } |
6473 | 0 | } |
6474 | | |
6475 | | void torrent::on_proxy_name_lookup(error_code const& e |
6476 | | , std::vector<address> const& addrs |
6477 | 0 | , std::list<web_seed_t>::iterator web, int port) try |
6478 | 0 | { |
6479 | 0 | TORRENT_ASSERT(is_single_thread()); |
6480 | |
|
6481 | 0 | INVARIANT_CHECK; |
6482 | |
|
6483 | 0 | TORRENT_ASSERT(web->resolving); |
6484 | | #ifndef TORRENT_DISABLE_LOGGING |
6485 | | debug_log("completed resolve proxy hostname for: %s", web->url.c_str()); |
6486 | | if (e && should_log()) |
6487 | | debug_log("proxy name lookup error: %s", e.message().c_str()); |
6488 | | #endif |
6489 | 0 | web->resolving = false; |
6490 | |
|
6491 | 0 | if (web->removed) |
6492 | 0 | { |
6493 | | #ifndef TORRENT_DISABLE_LOGGING |
6494 | | debug_log("removed web seed"); |
6495 | | #endif |
6496 | 0 | remove_web_seed_iter(web); |
6497 | 0 | return; |
6498 | 0 | } |
6499 | | |
6500 | 0 | if (m_abort) return; |
6501 | 0 | if (m_ses.is_aborted()) return; |
6502 | | |
6503 | 0 | if (e || addrs.empty()) |
6504 | 0 | { |
6505 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6506 | 0 | { |
6507 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle() |
6508 | 0 | , web->url, e); |
6509 | 0 | } |
6510 | | |
6511 | | // the name lookup failed for the http host. Don't try |
6512 | | // this host again |
6513 | 0 | web->disabled = true; |
6514 | 0 | return; |
6515 | 0 | } |
6516 | | |
6517 | 0 | if (num_peers() >= int(m_max_connections) |
6518 | 0 | || m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit)) |
6519 | 0 | return; |
6520 | | |
6521 | 0 | tcp::endpoint a(addrs[0], std::uint16_t(port)); |
6522 | |
|
6523 | 0 | std::string hostname; |
6524 | 0 | error_code ec; |
6525 | 0 | std::string protocol; |
6526 | 0 | std::tie(protocol, std::ignore, hostname, port, std::ignore) |
6527 | 0 | = parse_url_components(web->url, ec); |
6528 | 0 | if (port == -1) port = protocol == "http" ? 80 : 443; |
6529 | |
|
6530 | 0 | if (ec) |
6531 | 0 | { |
6532 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6533 | 0 | { |
6534 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle() |
6535 | 0 | , web->url, ec); |
6536 | 0 | } |
6537 | 0 | remove_web_seed_iter(web); |
6538 | 0 | return; |
6539 | 0 | } |
6540 | | |
6541 | 0 | if (m_ip_filter && m_ip_filter->access(a.address()) & ip_filter::blocked) |
6542 | 0 | { |
6543 | 0 | if (m_ses.alerts().should_post<peer_blocked_alert>()) |
6544 | 0 | m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle() |
6545 | 0 | , a, peer_blocked_alert::ip_filter); |
6546 | 0 | return; |
6547 | 0 | } |
6548 | | |
6549 | 0 | auto self = shared_from_this(); |
6550 | 0 | web->resolving = true; |
6551 | 0 | m_ses.get_resolver().async_resolve(hostname, aux::resolver_interface::abort_on_shutdown |
6552 | 0 | , [self, web, port](error_code const& err, std::vector<address> const& addr) |
6553 | 0 | { |
6554 | 0 | self->wrap(&torrent::on_name_lookup, err, addr, port, web); |
6555 | 0 | }); |
6556 | 0 | } |
6557 | 0 | catch (...) { handle_exception(); } |
6558 | | |
6559 | | void torrent::on_name_lookup(error_code const& e |
6560 | | , std::vector<address> const& addrs |
6561 | | , int const port |
6562 | 0 | , std::list<web_seed_t>::iterator web) try |
6563 | 0 | { |
6564 | 0 | TORRENT_ASSERT(is_single_thread()); |
6565 | |
|
6566 | 0 | INVARIANT_CHECK; |
6567 | |
|
6568 | 0 | TORRENT_ASSERT(web->resolving); |
6569 | | #ifndef TORRENT_DISABLE_LOGGING |
6570 | | debug_log("completed resolve: %s", web->url.c_str()); |
6571 | | #endif |
6572 | 0 | web->resolving = false; |
6573 | 0 | if (web->removed) |
6574 | 0 | { |
6575 | | #ifndef TORRENT_DISABLE_LOGGING |
6576 | | debug_log("removed web seed"); |
6577 | | #endif |
6578 | 0 | remove_web_seed_iter(web); |
6579 | 0 | return; |
6580 | 0 | } |
6581 | | |
6582 | 0 | if (m_abort) return; |
6583 | | |
6584 | 0 | if (e || addrs.empty()) |
6585 | 0 | { |
6586 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6587 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url, e); |
6588 | | #ifndef TORRENT_DISABLE_LOGGING |
6589 | | if (should_log()) |
6590 | | { |
6591 | | debug_log("*** HOSTNAME LOOKUP FAILED: %s: (%d) %s" |
6592 | | , web->url.c_str(), e.value(), e.message().c_str()); |
6593 | | } |
6594 | | #endif |
6595 | | |
6596 | | // unavailable, retry in `settings_pack::web_seed_name_lookup_retry` seconds |
6597 | 0 | web->retry = aux::time_now32() |
6598 | 0 | + seconds32(settings().get_int(settings_pack::web_seed_name_lookup_retry)); |
6599 | 0 | return; |
6600 | 0 | } |
6601 | | |
6602 | 0 | for (auto const& addr : addrs) |
6603 | 0 | { |
6604 | | // if this is set, we don't allow this web seed to have resolved to a |
6605 | | // local IP |
6606 | 0 | if (web->no_local_ips && !aux::is_global(addr)) continue; |
6607 | | |
6608 | | // fill in the peer struct's address field |
6609 | 0 | web->endpoints.emplace_back(addr, std::uint16_t(port)); |
6610 | |
|
6611 | | #ifndef TORRENT_DISABLE_LOGGING |
6612 | | if (should_log()) |
6613 | | debug_log(" -> %s", print_endpoint(tcp::endpoint(addr, std::uint16_t(port))).c_str()); |
6614 | | #endif |
6615 | 0 | } |
6616 | |
|
6617 | 0 | if (web->endpoints.empty()) |
6618 | 0 | { |
6619 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6620 | 0 | { |
6621 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle() |
6622 | 0 | , web->url, errors::banned_by_ip_filter); |
6623 | 0 | } |
6624 | | |
6625 | | // the name lookup failed for the http host. Don't try |
6626 | | // this host again |
6627 | 0 | web->disabled = true; |
6628 | 0 | return; |
6629 | 0 | } |
6630 | | |
6631 | 0 | if (num_peers() >= int(m_max_connections) |
6632 | 0 | || m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit)) |
6633 | 0 | return; |
6634 | | |
6635 | 0 | connect_web_seed(web, web->endpoints.front()); |
6636 | 0 | } |
6637 | 0 | catch (...) { handle_exception(); } |
6638 | | |
6639 | | void torrent::connect_web_seed(std::list<web_seed_t>::iterator web, tcp::endpoint a) |
6640 | 0 | { |
6641 | 0 | INVARIANT_CHECK; |
6642 | |
|
6643 | 0 | TORRENT_ASSERT(is_single_thread()); |
6644 | 0 | if (m_abort) return; |
6645 | | |
6646 | 0 | if (m_ip_filter && m_ip_filter->access(a.address()) & ip_filter::blocked) |
6647 | 0 | { |
6648 | 0 | if (m_ses.alerts().should_post<peer_blocked_alert>()) |
6649 | 0 | m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle() |
6650 | 0 | , a, peer_blocked_alert::ip_filter); |
6651 | 0 | return; |
6652 | 0 | } |
6653 | | |
6654 | 0 | TORRENT_ASSERT(!web->resolving); |
6655 | 0 | TORRENT_ASSERT(web->peer_info.connection == nullptr); |
6656 | |
|
6657 | 0 | if (aux::is_v4(a)) |
6658 | 0 | { |
6659 | 0 | web->peer_info.addr = a.address().to_v4(); |
6660 | 0 | web->peer_info.port = a.port(); |
6661 | 0 | } |
6662 | |
|
6663 | 0 | if (is_paused()) return; |
6664 | 0 | if (m_ses.is_aborted()) return; |
6665 | 0 | if (is_upload_only()) return; |
6666 | | |
6667 | | // this web seed may have redirected all files to other URLs, leaving it |
6668 | | // having no file left, and there's no longer any point in connecting to |
6669 | | // it. |
6670 | 0 | if (!web->have_files.empty() |
6671 | 0 | && web->have_files.none_set()) return; |
6672 | | |
6673 | 0 | void* userdata = nullptr; |
6674 | 0 | #if TORRENT_USE_SSL |
6675 | 0 | const bool ssl = string_begins_no_case("https://", web->url.c_str()); |
6676 | 0 | if (ssl) |
6677 | 0 | { |
6678 | 0 | #ifdef TORRENT_SSL_PEERS |
6679 | 0 | userdata = m_ssl_ctx.get(); |
6680 | 0 | #endif |
6681 | 0 | if (!userdata) userdata = m_ses.ssl_ctx(); |
6682 | 0 | } |
6683 | 0 | #endif |
6684 | 0 | aux::socket_type s = instantiate_connection(m_ses.get_context() |
6685 | 0 | , m_ses.proxy(), userdata, nullptr, true, false); |
6686 | |
|
6687 | 0 | if (boost::get<http_stream>(&s)) |
6688 | 0 | { |
6689 | | // the web seed connection will talk immediately to |
6690 | | // the proxy, without requiring CONNECT support |
6691 | 0 | boost::get<http_stream>(s).set_no_connect(true); |
6692 | 0 | } |
6693 | |
|
6694 | 0 | std::string hostname; |
6695 | 0 | std::string path; |
6696 | 0 | error_code ec; |
6697 | 0 | using std::ignore; |
6698 | 0 | std::tie(ignore, ignore, hostname, ignore, path) |
6699 | 0 | = parse_url_components(web->url, ec); |
6700 | 0 | if (ec) |
6701 | 0 | { |
6702 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6703 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url, ec); |
6704 | 0 | return; |
6705 | 0 | } |
6706 | | |
6707 | 0 | if (!settings().get_bool(settings_pack::allow_idna) && is_idna(hostname)) |
6708 | 0 | { |
6709 | | #ifndef TORRENT_DISABLE_LOGGING |
6710 | | if (should_log()) |
6711 | | debug_log("IDNA disallowed in web seeds: %s", web->url.c_str()); |
6712 | | #endif |
6713 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6714 | 0 | { |
6715 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle() |
6716 | 0 | , web->url, error_code(errors::blocked_by_idna)); |
6717 | 0 | } |
6718 | | // never try it again |
6719 | 0 | web->disabled = true; |
6720 | 0 | return; |
6721 | 0 | } |
6722 | | |
6723 | | // The SSRF mitigation for web seeds is that any HTTP server on the |
6724 | | // local network may not use any query string parameters |
6725 | 0 | if (settings().get_bool(settings_pack::ssrf_mitigation) |
6726 | 0 | && aux::is_local(web->peer_info.addr) |
6727 | 0 | && path.find('?') != std::string::npos) |
6728 | 0 | { |
6729 | | #ifndef TORRENT_DISABLE_LOGGING |
6730 | | if (should_log()) |
6731 | | { |
6732 | | debug_log("*** SSRF MITIGATION BLOCKED WEB SEED: %s" |
6733 | | , web->url.c_str()); |
6734 | | } |
6735 | | #endif |
6736 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6737 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle() |
6738 | 0 | , web->url, errors::ssrf_mitigation); |
6739 | 0 | if (m_ses.alerts().should_post<peer_blocked_alert>()) |
6740 | 0 | m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle() |
6741 | 0 | , a, peer_blocked_alert::ssrf_mitigation); |
6742 | | // never try it again |
6743 | 0 | web->disabled = true; |
6744 | 0 | return; |
6745 | 0 | } |
6746 | | |
6747 | 0 | bool const is_ip = aux::is_ip_address(hostname); |
6748 | 0 | if (is_ip) a.address(make_address(hostname, ec)); |
6749 | 0 | bool const proxy_hostnames = settings().get_bool(settings_pack::proxy_hostnames) |
6750 | 0 | && !is_ip; |
6751 | |
|
6752 | 0 | if (proxy_hostnames |
6753 | 0 | && (boost::get<socks5_stream>(&s) |
6754 | 0 | #if TORRENT_USE_SSL |
6755 | 0 | || boost::get<ssl_stream<socks5_stream>>(&s) |
6756 | 0 | #endif |
6757 | 0 | )) |
6758 | 0 | { |
6759 | | // we're using a socks proxy and we're resolving |
6760 | | // hostnames through it |
6761 | 0 | socks5_stream& str = |
6762 | 0 | #if TORRENT_USE_SSL |
6763 | 0 | ssl ? boost::get<ssl_stream<socks5_stream>>(s).next_layer() : |
6764 | 0 | #endif |
6765 | 0 | boost::get<socks5_stream>(s); |
6766 | |
|
6767 | 0 | str.set_dst_name(hostname); |
6768 | 0 | } |
6769 | |
|
6770 | 0 | setup_ssl_hostname(s, hostname, ec); |
6771 | 0 | if (ec) |
6772 | 0 | { |
6773 | 0 | if (m_ses.alerts().should_post<url_seed_alert>()) |
6774 | 0 | m_ses.alerts().emplace_alert<url_seed_alert>(get_handle(), web->url, ec); |
6775 | 0 | return; |
6776 | 0 | } |
6777 | | |
6778 | 0 | peer_connection_args pack{ |
6779 | 0 | &m_ses |
6780 | 0 | , &settings() |
6781 | 0 | , &m_ses.stats_counters() |
6782 | 0 | , &m_ses.disk_thread() |
6783 | 0 | , &m_ses.get_context() |
6784 | 0 | , shared_from_this() |
6785 | 0 | , std::move(s) |
6786 | 0 | , a |
6787 | 0 | , &web->peer_info |
6788 | 0 | , aux::generate_peer_id(settings()) |
6789 | 0 | }; |
6790 | |
|
6791 | 0 | std::shared_ptr<peer_connection> c; |
6792 | 0 | if (web->type == web_seed_entry::url_seed) |
6793 | 0 | { |
6794 | 0 | c = std::make_shared<web_peer_connection>(pack, *web); |
6795 | 0 | } |
6796 | 0 | else if (web->type == web_seed_entry::http_seed) |
6797 | 0 | { |
6798 | 0 | c = std::make_shared<http_seed_connection>(pack, *web); |
6799 | 0 | } |
6800 | 0 | if (!c) return; |
6801 | | |
6802 | 0 | #if TORRENT_USE_ASSERTS |
6803 | 0 | c->m_in_constructor = false; |
6804 | 0 | #endif |
6805 | |
|
6806 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
6807 | 0 | for (auto const& ext : m_extensions) |
6808 | 0 | { |
6809 | 0 | std::shared_ptr<peer_plugin> |
6810 | 0 | pp(ext->new_connection(peer_connection_handle(c->self()))); |
6811 | 0 | if (pp) c->add_extension(pp); |
6812 | 0 | } |
6813 | 0 | #endif |
6814 | |
|
6815 | 0 | TORRENT_ASSERT(!c->m_in_constructor); |
6816 | | // add the newly connected peer to this torrent's peer list |
6817 | 0 | TORRENT_ASSERT(m_iterating_connections == 0); |
6818 | | |
6819 | | // we don't want to have to allocate memory to disconnect this peer, so |
6820 | | // make sure there's enough memory allocated in the deferred_disconnect |
6821 | | // list up-front |
6822 | 0 | m_peers_to_disconnect.reserve(m_connections.size() + 1); |
6823 | |
|
6824 | 0 | sorted_insert(m_connections, c.get()); |
6825 | 0 | update_want_peers(); |
6826 | 0 | update_want_tick(); |
6827 | 0 | m_ses.insert_peer(c); |
6828 | |
|
6829 | 0 | if (web->peer_info.seed) |
6830 | 0 | { |
6831 | 0 | TORRENT_ASSERT(m_num_seeds < 0xffff); |
6832 | 0 | ++m_num_seeds; |
6833 | 0 | } |
6834 | |
|
6835 | 0 | TORRENT_ASSERT(!web->peer_info.connection); |
6836 | 0 | web->peer_info.connection = c.get(); |
6837 | 0 | #if TORRENT_USE_ASSERTS |
6838 | 0 | web->peer_info.in_use = true; |
6839 | 0 | #endif |
6840 | |
|
6841 | 0 | c->add_stat(std::int64_t(web->peer_info.prev_amount_download) << 10 |
6842 | 0 | , std::int64_t(web->peer_info.prev_amount_upload) << 10); |
6843 | 0 | web->peer_info.prev_amount_download = 0; |
6844 | 0 | web->peer_info.prev_amount_upload = 0; |
6845 | | #ifndef TORRENT_DISABLE_LOGGING |
6846 | | if (should_log()) |
6847 | | { |
6848 | | debug_log("web seed connection started: [%s] %s" |
6849 | | , print_endpoint(a).c_str(), web->url.c_str()); |
6850 | | } |
6851 | | #endif |
6852 | |
|
6853 | 0 | c->start(); |
6854 | |
|
6855 | 0 | if (c->is_disconnecting()) return; |
6856 | |
|
6857 | | #ifndef TORRENT_DISABLE_LOGGING |
6858 | | debug_log("START queue peer [%p] (%d)", static_cast<void*>(c.get()) |
6859 | | , num_peers()); |
6860 | | #endif |
6861 | 0 | } |
6862 | | |
6863 | | hash_request torrent::pick_hashes(peer_connection* peer) |
6864 | 1.02k | { |
6865 | 1.02k | need_hash_picker(); |
6866 | 1.02k | if (!m_hash_picker) return {}; |
6867 | 1.02k | return m_hash_picker->pick_hashes(peer->get_bitfield()); |
6868 | 1.02k | } |
6869 | | |
6870 | | std::vector<sha256_hash> torrent::get_hashes(hash_request const& req) const |
6871 | 61 | { |
6872 | 61 | TORRENT_ASSERT(m_torrent_file->is_valid()); |
6873 | 61 | if (!m_torrent_file->is_valid()) return {}; |
6874 | 61 | TORRENT_ASSERT(validate_hash_request(req, m_torrent_file->files())); |
6875 | | |
6876 | 61 | auto const& f = m_merkle_trees[req.file]; |
6877 | | |
6878 | 61 | return f.get_hashes(req.base, req.index, req.count, req.proof_layers); |
6879 | 61 | } |
6880 | | |
6881 | | bool torrent::add_hashes(hash_request const& req, span<sha256_hash> hashes) |
6882 | 0 | { |
6883 | 0 | need_hash_picker(); |
6884 | 0 | if (!m_hash_picker) return true; |
6885 | 0 | add_hashes_result const result = m_hash_picker->add_hashes(req, hashes); |
6886 | 0 | for (auto& p : result.hash_failed) |
6887 | 0 | { |
6888 | 0 | if (torrent_file().info_hashes().has_v1() && have_piece(p.first)) |
6889 | 0 | { |
6890 | 0 | handle_inconsistent_hashes(p.first); |
6891 | 0 | return result.valid; |
6892 | 0 | } |
6893 | | |
6894 | 0 | TORRENT_ASSERT(!have_piece(p.first)); |
6895 | | |
6896 | | // the piece may not have been downloaded in this session |
6897 | | // it should be open for downloading so nothing needs to be done here |
6898 | 0 | if (!m_picker || !m_picker->is_downloading(p.first)) continue; |
6899 | 0 | piece_failed(p.first, p.second); |
6900 | 0 | } |
6901 | 0 | for (piece_index_t p : result.hash_passed) |
6902 | 0 | { |
6903 | 0 | if (torrent_file().info_hashes().has_v1() && !have_piece(p)) |
6904 | 0 | { |
6905 | 0 | handle_inconsistent_hashes(p); |
6906 | 0 | return result.valid; |
6907 | 0 | } |
6908 | | |
6909 | 0 | if (m_picker && m_picker->is_downloading(p) && m_picker->is_piece_finished(p) |
6910 | 0 | && !m_picker->is_hashing(p)) |
6911 | 0 | { |
6912 | 0 | piece_passed(p); |
6913 | 0 | } |
6914 | 0 | } |
6915 | 0 | return result.valid; |
6916 | 0 | } |
6917 | | |
6918 | | void torrent::hashes_rejected(hash_request const& req) |
6919 | 0 | { |
6920 | 0 | if (!m_hash_picker) return; |
6921 | 0 | m_hash_picker->hashes_rejected(req); |
6922 | | // we need to poke all of the v2 peers in case there are no other |
6923 | | // outstanding hash requests |
6924 | 0 | for (auto peer : m_connections) |
6925 | 0 | { |
6926 | 0 | if (peer->type() != connection_type::bittorrent) continue; |
6927 | 0 | auto* const btpeer = static_cast<bt_peer_connection*>(peer); |
6928 | 0 | btpeer->maybe_send_hash_request(); |
6929 | 0 | } |
6930 | 0 | } |
6931 | | |
6932 | | void torrent::verify_block_hashes(piece_index_t index) |
6933 | 0 | { |
6934 | 0 | need_hash_picker(); |
6935 | 0 | if (!m_hash_picker) return; |
6936 | | #ifndef TORRENT_DISABLE_LOGGING |
6937 | | if (should_log()) |
6938 | | { |
6939 | | debug_log("Piece %d hash failure, requesting block hashes", int(index)); |
6940 | | } |
6941 | | #endif |
6942 | 0 | m_hash_picker->verify_block_hashes(index); |
6943 | 0 | } |
6944 | | |
6945 | | std::shared_ptr<const torrent_info> torrent::get_torrent_file() const |
6946 | 0 | { |
6947 | 0 | if (!m_torrent_file->is_valid()) return {}; |
6948 | 0 | return m_torrent_file; |
6949 | 0 | } |
6950 | | |
6951 | | std::shared_ptr<torrent_info> torrent::get_torrent_copy_with_hashes() const |
6952 | 0 | { |
6953 | 0 | if (!m_torrent_file->is_valid()) return {}; |
6954 | 0 | auto ret = std::make_shared<torrent_info>(*m_torrent_file); |
6955 | |
|
6956 | 0 | if (ret->v2()) |
6957 | 0 | { |
6958 | 0 | aux::vector<aux::vector<char>, file_index_t> v2_hashes; |
6959 | 0 | for (auto const& tree : m_merkle_trees) |
6960 | 0 | { |
6961 | 0 | auto const& layer = tree.get_piece_layer(); |
6962 | 0 | std::vector<char> out_layer; |
6963 | 0 | out_layer.reserve(layer.size() * sha256_hash::size()); |
6964 | 0 | for (auto const& h : layer) |
6965 | 0 | { |
6966 | | // we're missing a piece layer. We can't return a valid |
6967 | | // torrent |
6968 | 0 | if (h.is_all_zeros()) return {}; |
6969 | 0 | out_layer.insert(out_layer.end(), h.data(), h.data() + sha256_hash::size()); |
6970 | 0 | } |
6971 | 0 | v2_hashes.emplace_back(std::move(out_layer)); |
6972 | 0 | } |
6973 | 0 | ret->set_piece_layers(std::move(v2_hashes)); |
6974 | 0 | } |
6975 | | |
6976 | 0 | return ret; |
6977 | 0 | } |
6978 | | |
6979 | | std::vector<std::vector<sha256_hash>> torrent::get_piece_layers() const |
6980 | 0 | { |
6981 | 0 | std::vector<std::vector<sha256_hash>> ret; |
6982 | 0 | for (auto const& tree : m_merkle_trees) |
6983 | 0 | ret.emplace_back(tree.get_piece_layer()); |
6984 | 0 | return ret; |
6985 | 0 | } |
6986 | | |
6987 | | void torrent::enable_all_trackers() |
6988 | 0 | { |
6989 | 0 | for (aux::announce_entry& ae : m_trackers) |
6990 | 0 | for (aux::announce_endpoint& aep : ae.endpoints) |
6991 | 0 | aep.enabled = true; |
6992 | 0 | } |
6993 | | |
6994 | | void torrent::write_resume_data(resume_data_flags_t const flags, add_torrent_params& ret) const |
6995 | 0 | { |
6996 | 0 | ret.version = LIBTORRENT_VERSION_NUM; |
6997 | 0 | ret.storage_mode = storage_mode(); |
6998 | 0 | ret.total_uploaded = m_total_uploaded; |
6999 | 0 | ret.total_downloaded = m_total_downloaded; |
7000 | | |
7001 | | // cast to seconds in case that internal values doesn't have ratio<1> |
7002 | 0 | ret.active_time = static_cast<int>(total_seconds(active_time())); |
7003 | 0 | ret.finished_time = static_cast<int>(total_seconds(finished_time())); |
7004 | 0 | ret.seeding_time = static_cast<int>(total_seconds(seeding_time())); |
7005 | 0 | ret.last_seen_complete = m_last_seen_complete; |
7006 | 0 | ret.last_upload = aux::to_time_t(m_last_upload); |
7007 | 0 | ret.last_download = aux::to_time_t(m_last_download); |
7008 | |
|
7009 | 0 | ret.num_complete = m_complete; |
7010 | 0 | ret.num_incomplete = m_incomplete; |
7011 | 0 | ret.num_downloaded = m_downloaded; |
7012 | |
|
7013 | 0 | ret.flags = this->flags(); |
7014 | |
|
7015 | 0 | ret.added_time = m_added_time; |
7016 | 0 | ret.completed_time = m_completed_time; |
7017 | |
|
7018 | 0 | ret.save_path = m_save_path; |
7019 | |
|
7020 | 0 | ret.info_hashes = torrent_file().info_hashes(); |
7021 | 0 | if (valid_metadata()) ret.name = m_torrent_file->name(); |
7022 | 0 | else if (m_name) ret.name = *m_name; |
7023 | |
|
7024 | 0 | #if TORRENT_ABI_VERSION < 3 |
7025 | 0 | ret.info_hash = ret.info_hashes.get_best(); |
7026 | 0 | #endif |
7027 | |
|
7028 | 0 | if (valid_metadata() && (flags & torrent_handle::save_info_dict)) |
7029 | 0 | { |
7030 | 0 | ret.ti = m_torrent_file; |
7031 | 0 | } |
7032 | | |
7033 | | // if this torrent is a seed, we won't have a piece picker |
7034 | | // if we don't have anything, we may also not have a picker |
7035 | | // in either case; there will be no half-finished pieces. |
7036 | 0 | if (has_picker()) |
7037 | 0 | { |
7038 | 0 | int const num_blocks_per_piece = torrent_file().blocks_per_piece(); |
7039 | |
|
7040 | 0 | std::vector<piece_picker::downloading_piece> const q |
7041 | 0 | = m_picker->get_download_queue(); |
7042 | | |
7043 | | // info for each unfinished piece |
7044 | 0 | for (auto const& dp : q) |
7045 | 0 | { |
7046 | 0 | if (dp.finished == 0 && dp.writing == 0) |
7047 | 0 | continue; |
7048 | | |
7049 | 0 | bitfield bitmask; |
7050 | 0 | bitmask.resize(num_blocks_per_piece, false); |
7051 | |
|
7052 | 0 | auto const info = m_picker->blocks_for_piece(dp); |
7053 | 0 | for (int i = 0; i < int(info.size()); ++i) |
7054 | 0 | { |
7055 | 0 | if (info[i].state == piece_picker::block_info::state_finished) |
7056 | 0 | bitmask.set_bit(i); |
7057 | 0 | } |
7058 | 0 | ret.unfinished_pieces.emplace(dp.index, std::move(bitmask)); |
7059 | 0 | } |
7060 | 0 | } |
7061 | | |
7062 | | // save trackers |
7063 | 0 | for (auto const& tr : m_trackers) |
7064 | 0 | { |
7065 | 0 | ret.trackers.push_back(tr.url); |
7066 | 0 | ret.tracker_tiers.push_back(tr.tier); |
7067 | 0 | } |
7068 | | |
7069 | | // save web seeds |
7070 | 0 | for (auto const& ws : m_web_seeds) |
7071 | 0 | { |
7072 | 0 | if (ws.removed || ws.ephemeral) continue; |
7073 | 0 | if (ws.type == web_seed_entry::url_seed) |
7074 | 0 | ret.url_seeds.push_back(ws.url); |
7075 | 0 | else if (ws.type == web_seed_entry::http_seed) |
7076 | 0 | ret.http_seeds.push_back(ws.url); |
7077 | 0 | } |
7078 | |
|
7079 | 0 | ret.dht_nodes = m_torrent_file->nodes(); |
7080 | | |
7081 | | // write have bitmask |
7082 | | // the pieces string has one byte per piece. Each |
7083 | | // byte is a bitmask representing different properties |
7084 | | // for the piece |
7085 | | // bit 0: set if we have the piece |
7086 | | // bit 1: set if we have verified the piece (in seed mode) |
7087 | 0 | bool const is_checking = state() == torrent_status::checking_files; |
7088 | | |
7089 | | // if we are checking, only save the have_pieces bitfield up to the piece |
7090 | | // we have actually checked. This allows us to resume the checking when we |
7091 | | // load this torrent up again. If we have not completed checking nor is |
7092 | | // currently checking, don't save any pieces from the have_pieces |
7093 | | // bitfield. |
7094 | 0 | piece_index_t const max_piece |
7095 | 0 | = is_checking ? m_num_checked_pieces |
7096 | 0 | : m_files_checked ? m_torrent_file->end_piece() |
7097 | 0 | : piece_index_t(0); |
7098 | |
|
7099 | 0 | TORRENT_ASSERT(ret.have_pieces.empty()); |
7100 | 0 | if (max_piece > piece_index_t(0)) |
7101 | 0 | { |
7102 | 0 | if (is_seed()) |
7103 | 0 | { |
7104 | 0 | ret.have_pieces.resize(static_cast<int>(max_piece), true); |
7105 | 0 | } |
7106 | 0 | else if (has_picker()) |
7107 | 0 | { |
7108 | 0 | ret.have_pieces.resize(static_cast<int>(max_piece), false); |
7109 | 0 | for (auto const i : ret.have_pieces.range()) |
7110 | 0 | if (m_picker->is_piece_flushed(i)) ret.have_pieces.set_bit(i); |
7111 | 0 | } |
7112 | |
|
7113 | 0 | if (m_seed_mode) |
7114 | 0 | ret.verified_pieces = m_verified; |
7115 | 0 | } |
7116 | | |
7117 | | // write renamed files |
7118 | 0 | if (valid_metadata() |
7119 | 0 | && &m_torrent_file->files() != &m_torrent_file->orig_files() |
7120 | 0 | && m_torrent_file->files().num_files() == m_torrent_file->orig_files().num_files()) |
7121 | 0 | { |
7122 | 0 | file_storage const& fs = m_torrent_file->files(); |
7123 | 0 | file_storage const& orig_fs = m_torrent_file->orig_files(); |
7124 | 0 | for (auto const i : fs.file_range()) |
7125 | 0 | { |
7126 | 0 | if (fs.file_path(i) != orig_fs.file_path(i)) |
7127 | 0 | ret.renamed_files[i] = fs.file_path(i); |
7128 | 0 | } |
7129 | 0 | } |
7130 | | |
7131 | | // write local peers |
7132 | 0 | std::vector<torrent_peer const*> deferred_peers; |
7133 | 0 | if (m_peer_list) |
7134 | 0 | { |
7135 | 0 | for (auto p : *m_peer_list) |
7136 | 0 | { |
7137 | 0 | #if TORRENT_USE_I2P |
7138 | 0 | if (p->is_i2p_addr) continue; |
7139 | 0 | #endif |
7140 | 0 | if (p->banned) |
7141 | 0 | { |
7142 | 0 | ret.banned_peers.push_back(p->ip()); |
7143 | 0 | continue; |
7144 | 0 | } |
7145 | | |
7146 | | // we cannot save remote connection |
7147 | | // since we don't know their listen port |
7148 | | // unless they gave us their listen port |
7149 | | // through the extension handshake |
7150 | | // so, if the peer is not connectable (i.e. we |
7151 | | // don't know its listen port) or if it has |
7152 | | // been banned, don't save it. |
7153 | 0 | if (!p->connectable) continue; |
7154 | | |
7155 | | // don't save peers that don't work |
7156 | 0 | if (int(p->failcount) > 0) continue; |
7157 | | |
7158 | | // don't save peers that appear to send corrupt data |
7159 | 0 | if (int(p->trust_points) < 0) continue; |
7160 | | |
7161 | 0 | if (p->last_connected == 0) |
7162 | 0 | { |
7163 | | // we haven't connected to this peer. It might still |
7164 | | // be useful to save it, but only save it if we |
7165 | | // don't have enough peers that we actually did connect to |
7166 | 0 | if (int(deferred_peers.size()) < 100) |
7167 | 0 | deferred_peers.push_back(p); |
7168 | 0 | continue; |
7169 | 0 | } |
7170 | | |
7171 | 0 | ret.peers.push_back(p->ip()); |
7172 | 0 | } |
7173 | 0 | } |
7174 | | |
7175 | | // if we didn't save 100 peers, fill in with second choice peers |
7176 | 0 | if (int(ret.peers.size()) < 100) |
7177 | 0 | { |
7178 | 0 | aux::random_shuffle(deferred_peers); |
7179 | 0 | for (auto const p : deferred_peers) |
7180 | 0 | { |
7181 | 0 | ret.peers.push_back(p->ip()); |
7182 | 0 | if (int(ret.peers.size()) >= 100) break; |
7183 | 0 | } |
7184 | 0 | } |
7185 | |
|
7186 | 0 | ret.upload_limit = upload_limit(); |
7187 | 0 | ret.download_limit = download_limit(); |
7188 | 0 | ret.max_connections = max_connections(); |
7189 | 0 | ret.max_uploads = max_uploads(); |
7190 | | |
7191 | | // piece priorities and file priorities are mutually exclusive. If there |
7192 | | // are file priorities set, don't save piece priorities. |
7193 | | // when in seed mode (i.e. the client promises that we have all files) |
7194 | | // it does not make sense to save file priorities. |
7195 | 0 | if (!m_file_priority.empty() && !m_seed_mode) |
7196 | 0 | { |
7197 | | // write file priorities |
7198 | 0 | ret.file_priorities = m_file_priority; |
7199 | 0 | } |
7200 | |
|
7201 | 0 | if (valid_metadata() && has_picker()) |
7202 | 0 | { |
7203 | | // write piece priorities |
7204 | | // but only if they are not set to the default |
7205 | 0 | bool default_prio = true; |
7206 | 0 | for (auto const i : m_torrent_file->piece_range()) |
7207 | 0 | { |
7208 | 0 | if (m_picker->piece_priority(i) == default_priority) continue; |
7209 | 0 | default_prio = false; |
7210 | 0 | break; |
7211 | 0 | } |
7212 | |
|
7213 | 0 | if (!default_prio) |
7214 | 0 | { |
7215 | 0 | ret.piece_priorities.clear(); |
7216 | 0 | ret.piece_priorities.reserve(static_cast<std::size_t>(m_torrent_file->num_pieces())); |
7217 | |
|
7218 | 0 | for (auto const i : m_torrent_file->piece_range()) |
7219 | 0 | ret.piece_priorities.push_back(m_picker->piece_priority(i)); |
7220 | 0 | } |
7221 | 0 | } |
7222 | |
|
7223 | 0 | if (m_torrent_file->info_hashes().has_v2()) |
7224 | 0 | { |
7225 | 0 | auto const num_files = m_merkle_trees.size(); |
7226 | 0 | ret.merkle_trees.clear(); |
7227 | 0 | ret.merkle_trees.reserve(num_files); |
7228 | 0 | ret.merkle_tree_mask.clear(); |
7229 | 0 | ret.merkle_tree_mask.reserve(num_files); |
7230 | 0 | ret.verified_leaf_hashes.clear(); |
7231 | 0 | ret.verified_leaf_hashes.reserve(num_files); |
7232 | 0 | for (auto const& t : m_merkle_trees) |
7233 | 0 | { |
7234 | | // use structured binding in C++17 |
7235 | 0 | aux::vector<bool> mask; |
7236 | 0 | std::vector<sha256_hash> sparse_tree; |
7237 | 0 | std::tie(sparse_tree, mask) = t.build_sparse_vector(); |
7238 | 0 | ret.merkle_trees.emplace_back(std::move(sparse_tree)); |
7239 | 0 | ret.merkle_tree_mask.emplace_back(std::move(mask)); |
7240 | 0 | ret.verified_leaf_hashes.emplace_back(t.verified_leafs()); |
7241 | 0 | } |
7242 | |
|
7243 | 0 | if (!has_hash_picker() && !m_have_all) |
7244 | 0 | { |
7245 | 0 | file_storage const& fs = m_torrent_file->files(); |
7246 | 0 | ret.verified_leaf_hashes.reserve(fs.num_files()); |
7247 | 0 | for (file_index_t f(0); f != fs.end_file(); ++f) |
7248 | 0 | { |
7249 | 0 | if (fs.pad_file_at(f) || fs.file_size(f) == 0) |
7250 | 0 | { |
7251 | 0 | ret.verified_leaf_hashes.emplace_back(); |
7252 | 0 | continue; |
7253 | 0 | } |
7254 | 0 | ret.verified_leaf_hashes.emplace_back( |
7255 | 0 | fs.file_num_blocks(f), false); |
7256 | 0 | } |
7257 | 0 | } |
7258 | 0 | } |
7259 | 0 | } |
7260 | | |
7261 | | #if TORRENT_ABI_VERSION == 1 |
7262 | | void torrent::get_full_peer_list(std::vector<peer_list_entry>* v) const |
7263 | 0 | { |
7264 | 0 | v->clear(); |
7265 | 0 | if (!m_peer_list) return; |
7266 | | |
7267 | 0 | v->reserve(aux::numeric_cast<std::size_t>(m_peer_list->num_peers())); |
7268 | 0 | for (auto p : *m_peer_list) |
7269 | 0 | { |
7270 | 0 | peer_list_entry e; |
7271 | 0 | e.ip = p->ip(); |
7272 | 0 | e.flags = p->banned ? peer_list_entry::banned : 0; |
7273 | 0 | e.failcount = p->failcount; |
7274 | 0 | e.source = p->source; |
7275 | 0 | v->push_back(e); |
7276 | 0 | } |
7277 | 0 | } |
7278 | | #endif |
7279 | | |
7280 | | void torrent::get_peer_info(std::vector<peer_info>* v) |
7281 | 0 | { |
7282 | 0 | v->clear(); |
7283 | 0 | for (auto const peer : *this) |
7284 | 0 | { |
7285 | 0 | TORRENT_ASSERT(peer->m_in_use == 1337); |
7286 | | |
7287 | | // incoming peers that haven't finished the handshake should |
7288 | | // not be included in this list |
7289 | 0 | if (peer->associated_torrent().expired()) continue; |
7290 | | |
7291 | 0 | v->emplace_back(); |
7292 | 0 | peer_info& p = v->back(); |
7293 | |
|
7294 | 0 | peer->get_peer_info(p); |
7295 | 0 | } |
7296 | 0 | } |
7297 | | |
7298 | | void torrent::post_peer_info() |
7299 | 0 | { |
7300 | 0 | std::vector<peer_info> v; |
7301 | 0 | get_peer_info(&v); |
7302 | 0 | alerts().emplace_alert<peer_info_alert>(get_handle(), std::move(v)); |
7303 | 0 | } |
7304 | | |
7305 | | namespace { |
7306 | | void initialize_piece_info(piece_picker const& p |
7307 | | , torrent_info const& ti |
7308 | | , int const block_size |
7309 | | , std::vector<block_info>& blk |
7310 | | , span<piece_picker::downloading_piece const> q |
7311 | | , std::vector<partial_piece_info>* queue) |
7312 | 0 | { |
7313 | 0 | const int blocks_per_piece = p.blocks_in_piece(piece_index_t(0)); |
7314 | 0 | int counter = 0; |
7315 | 0 | for (auto i = q.begin(); i != q.end(); ++i, ++counter) |
7316 | 0 | { |
7317 | 0 | partial_piece_info pi; |
7318 | 0 | pi.blocks_in_piece = p.blocks_in_piece(i->index); |
7319 | 0 | pi.finished = int(i->finished); |
7320 | 0 | pi.writing = int(i->writing); |
7321 | 0 | pi.requested = int(i->requested); |
7322 | 0 | #if TORRENT_ABI_VERSION == 1 |
7323 | 0 | pi.piece_state = partial_piece_info::none; |
7324 | 0 | #endif |
7325 | 0 | TORRENT_ASSERT(counter * blocks_per_piece + pi.blocks_in_piece <= int(blk.size())); |
7326 | 0 | block_info* blocks = &blk[std::size_t(counter * blocks_per_piece)]; |
7327 | 0 | pi.blocks = blocks; |
7328 | 0 | int const piece_size = ti.piece_size(i->index); |
7329 | 0 | int idx = -1; |
7330 | 0 | for (auto const& info : p.blocks_for_piece(*i)) |
7331 | 0 | { |
7332 | 0 | ++idx; |
7333 | 0 | block_info& bi = blocks[idx]; |
7334 | 0 | bi.state = info.state; |
7335 | 0 | bi.block_size = idx < pi.blocks_in_piece - 1 |
7336 | 0 | ? aux::numeric_cast<std::uint32_t>(block_size) |
7337 | 0 | : aux::numeric_cast<std::uint32_t>(piece_size - (idx * block_size)); |
7338 | 0 | bool const complete = bi.state == block_info::writing |
7339 | 0 | || bi.state == block_info::finished; |
7340 | 0 | if (info.peer == nullptr) |
7341 | 0 | { |
7342 | 0 | bi.set_peer(tcp::endpoint()); |
7343 | 0 | bi.bytes_progress = complete ? bi.block_size : 0; |
7344 | 0 | } |
7345 | 0 | else |
7346 | 0 | { |
7347 | 0 | torrent_peer* tp = info.peer; |
7348 | 0 | TORRENT_ASSERT(tp->in_use); |
7349 | 0 | if (tp->connection) |
7350 | 0 | { |
7351 | 0 | auto* peer = static_cast<peer_connection*>(tp->connection); |
7352 | 0 | TORRENT_ASSERT(peer->m_in_use); |
7353 | 0 | bi.set_peer(peer->remote()); |
7354 | 0 | if (bi.state == block_info::requested) |
7355 | 0 | { |
7356 | 0 | auto pbp = peer->downloading_piece_progress(); |
7357 | 0 | if (pbp.piece_index == i->index && pbp.block_index == idx) |
7358 | 0 | { |
7359 | 0 | bi.bytes_progress = aux::numeric_cast<std::uint32_t>(pbp.bytes_downloaded); |
7360 | 0 | TORRENT_ASSERT(bi.bytes_progress <= bi.block_size); |
7361 | 0 | } |
7362 | 0 | else |
7363 | 0 | { |
7364 | 0 | bi.bytes_progress = 0; |
7365 | 0 | } |
7366 | 0 | } |
7367 | 0 | else |
7368 | 0 | { |
7369 | 0 | bi.bytes_progress = complete ? bi.block_size : 0; |
7370 | 0 | } |
7371 | 0 | } |
7372 | 0 | else |
7373 | 0 | { |
7374 | 0 | bi.set_peer(tp->ip()); |
7375 | 0 | bi.bytes_progress = complete ? bi.block_size : 0; |
7376 | 0 | } |
7377 | 0 | } |
7378 | |
|
7379 | 0 | bi.num_peers = info.num_peers; |
7380 | 0 | } |
7381 | 0 | pi.piece_index = i->index; |
7382 | 0 | queue->push_back(pi); |
7383 | 0 | } |
7384 | 0 | } |
7385 | | } |
7386 | | |
7387 | | void torrent::post_download_queue() |
7388 | 0 | { |
7389 | 0 | std::vector<block_info> blk; |
7390 | 0 | if (!valid_metadata() || !has_picker()) return; |
7391 | 0 | piece_picker const& p = picker(); |
7392 | 0 | std::vector<piece_picker::downloading_piece> const q = p.get_download_queue(); |
7393 | 0 | std::vector<partial_piece_info> queue; |
7394 | 0 | if (!q.empty()) |
7395 | 0 | { |
7396 | 0 | const int blocks_per_piece = m_picker->blocks_in_piece(piece_index_t(0)); |
7397 | 0 | blk.resize(q.size() * aux::numeric_cast<std::size_t>(blocks_per_piece)); |
7398 | 0 | initialize_piece_info(p, torrent_file(), block_size(), blk, q, &queue); |
7399 | 0 | } |
7400 | 0 | alerts().emplace_alert<piece_info_alert>(get_handle(), std::move(queue), std::move(blk)); |
7401 | 0 | } |
7402 | | |
7403 | | void torrent::get_download_queue(std::vector<partial_piece_info>* queue) const |
7404 | 0 | { |
7405 | 0 | TORRENT_ASSERT(is_single_thread()); |
7406 | 0 | queue->clear(); |
7407 | 0 | std::vector<block_info>& blk = m_ses.block_info_storage(); |
7408 | 0 | blk.clear(); |
7409 | |
|
7410 | 0 | if (!valid_metadata() || !has_picker()) return; |
7411 | 0 | piece_picker const& p = picker(); |
7412 | 0 | std::vector<piece_picker::downloading_piece> q |
7413 | 0 | = p.get_download_queue(); |
7414 | 0 | if (q.empty()) return; |
7415 | | |
7416 | 0 | const int blocks_per_piece = m_picker->blocks_in_piece(piece_index_t(0)); |
7417 | 0 | blk.resize(q.size() * aux::numeric_cast<std::size_t>(blocks_per_piece)); |
7418 | |
|
7419 | 0 | initialize_piece_info(p, torrent_file(), block_size(), blk, q, queue); |
7420 | 0 | } |
7421 | | |
7422 | | #if defined TORRENT_SSL_PEERS |
7423 | | struct hostname_visitor |
7424 | | { |
7425 | 0 | explicit hostname_visitor(std::string const& h) : hostname_(h) {} |
7426 | | template <typename T> |
7427 | 0 | void operator()(T&) {} Unexecuted instantiation: void libtorrent::hostname_visitor::operator()<libtorrent::aux::noexcept_move_only<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor> > >(libtorrent::aux::noexcept_move_only<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor> >&) Unexecuted instantiation: void libtorrent::hostname_visitor::operator()<libtorrent::socks5_stream>(libtorrent::socks5_stream&) Unexecuted instantiation: void libtorrent::hostname_visitor::operator()<libtorrent::http_stream>(libtorrent::http_stream&) Unexecuted instantiation: void libtorrent::hostname_visitor::operator()<libtorrent::aux::utp_stream>(libtorrent::aux::utp_stream&) Unexecuted instantiation: void libtorrent::hostname_visitor::operator()<libtorrent::i2p_stream>(libtorrent::i2p_stream&) |
7428 | | template <typename T> |
7429 | 0 | void operator()(ssl_stream<T>& s) { s.set_host_name(hostname_); } Unexecuted instantiation: void libtorrent::hostname_visitor::operator()<libtorrent::aux::noexcept_move_only<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor> > >(libtorrent::ssl_stream<libtorrent::aux::noexcept_move_only<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor> > >&) Unexecuted instantiation: void libtorrent::hostname_visitor::operator()<libtorrent::socks5_stream>(libtorrent::ssl_stream<libtorrent::socks5_stream>&) Unexecuted instantiation: void libtorrent::hostname_visitor::operator()<libtorrent::http_stream>(libtorrent::ssl_stream<libtorrent::http_stream>&) Unexecuted instantiation: void libtorrent::hostname_visitor::operator()<libtorrent::aux::utp_stream>(libtorrent::ssl_stream<libtorrent::aux::utp_stream>&) |
7430 | | std::string const& hostname_; |
7431 | | }; |
7432 | | |
7433 | | struct ssl_handle_visitor |
7434 | | { |
7435 | | template <typename T> |
7436 | | ssl::stream_handle_type operator()(T&) |
7437 | 0 | { return nullptr; } Unexecuted instantiation: ssl_st* libtorrent::ssl_handle_visitor::operator()<libtorrent::aux::noexcept_move_only<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor> > >(libtorrent::aux::noexcept_move_only<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor> >&) Unexecuted instantiation: ssl_st* libtorrent::ssl_handle_visitor::operator()<libtorrent::socks5_stream>(libtorrent::socks5_stream&) Unexecuted instantiation: ssl_st* libtorrent::ssl_handle_visitor::operator()<libtorrent::http_stream>(libtorrent::http_stream&) Unexecuted instantiation: ssl_st* libtorrent::ssl_handle_visitor::operator()<libtorrent::aux::utp_stream>(libtorrent::aux::utp_stream&) Unexecuted instantiation: ssl_st* libtorrent::ssl_handle_visitor::operator()<libtorrent::i2p_stream>(libtorrent::i2p_stream&) |
7438 | | |
7439 | | template <typename T> |
7440 | | ssl::stream_handle_type operator()(ssl_stream<T>& s) |
7441 | 0 | { return s.handle(); } Unexecuted instantiation: ssl_st* libtorrent::ssl_handle_visitor::operator()<libtorrent::aux::noexcept_move_only<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor> > >(libtorrent::ssl_stream<libtorrent::aux::noexcept_move_only<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor> > >&) Unexecuted instantiation: ssl_st* libtorrent::ssl_handle_visitor::operator()<libtorrent::socks5_stream>(libtorrent::ssl_stream<libtorrent::socks5_stream>&) Unexecuted instantiation: ssl_st* libtorrent::ssl_handle_visitor::operator()<libtorrent::http_stream>(libtorrent::ssl_stream<libtorrent::http_stream>&) Unexecuted instantiation: ssl_st* libtorrent::ssl_handle_visitor::operator()<libtorrent::aux::utp_stream>(libtorrent::ssl_stream<libtorrent::aux::utp_stream>&) |
7442 | | }; |
7443 | | #endif |
7444 | | |
7445 | | bool torrent::connect_to_peer(torrent_peer* peerinfo, bool const ignore_limit) |
7446 | 0 | { |
7447 | 0 | TORRENT_ASSERT(is_single_thread()); |
7448 | 0 | INVARIANT_CHECK; |
7449 | 0 | TORRENT_UNUSED(ignore_limit); |
7450 | |
|
7451 | 0 | TORRENT_ASSERT(peerinfo); |
7452 | 0 | TORRENT_ASSERT(peerinfo->connection == nullptr); |
7453 | |
|
7454 | 0 | if (m_abort) return false; |
7455 | | |
7456 | 0 | peerinfo->last_connected = m_ses.session_time(); |
7457 | 0 | #if TORRENT_USE_ASSERTS |
7458 | 0 | if (!settings().get_bool(settings_pack::allow_multiple_connections_per_ip)) |
7459 | 0 | { |
7460 | | // this asserts that we don't have duplicates in the peer_list's peer list |
7461 | 0 | peer_iterator i_ = std::find_if(m_connections.begin(), m_connections.end() |
7462 | 0 | , [peerinfo] (peer_connection const* p) |
7463 | 0 | { return !p->is_disconnecting() && p->remote() == peerinfo->ip(); }); |
7464 | 0 | #if TORRENT_USE_I2P |
7465 | 0 | TORRENT_ASSERT(i_ == m_connections.end() |
7466 | 0 | || (*i_)->type() != connection_type::bittorrent |
7467 | 0 | || peerinfo->is_i2p_addr); |
7468 | | #else |
7469 | | TORRENT_ASSERT(i_ == m_connections.end() |
7470 | | || (*i_)->type() != connection_type::bittorrent); |
7471 | | #endif |
7472 | 0 | } |
7473 | 0 | #endif // TORRENT_USE_ASSERTS |
7474 | |
|
7475 | 0 | TORRENT_ASSERT(want_peers() || ignore_limit); |
7476 | 0 | TORRENT_ASSERT(m_ses.num_connections() |
7477 | 0 | < settings().get_int(settings_pack::connections_limit) || ignore_limit); |
7478 | |
|
7479 | 0 | tcp::endpoint a(peerinfo->ip()); |
7480 | 0 | TORRENT_ASSERT(!m_apply_ip_filter |
7481 | 0 | || !m_ip_filter |
7482 | 0 | || (m_ip_filter->access(peerinfo->address()) & ip_filter::blocked) == 0); |
7483 | | |
7484 | | // this is where we determine if we open a regular TCP connection |
7485 | | // or a uTP connection. If the utp_socket_manager pointer is not passed in |
7486 | | // we'll instantiate a TCP connection |
7487 | 0 | aux::utp_socket_manager* sm = nullptr; |
7488 | |
|
7489 | 0 | #if TORRENT_USE_I2P |
7490 | 0 | if (peerinfo->is_i2p_addr) |
7491 | 0 | { |
7492 | 0 | if (settings().get_str(settings_pack::i2p_hostname).empty()) |
7493 | 0 | { |
7494 | | // we have an i2p torrent, but we're not connected to an i2p |
7495 | | // SAM proxy. |
7496 | 0 | if (alerts().should_post<i2p_alert>()) |
7497 | 0 | alerts().emplace_alert<i2p_alert>(errors::no_i2p_router); |
7498 | 0 | return false; |
7499 | 0 | } |
7500 | 0 | } |
7501 | 0 | else |
7502 | 0 | #endif |
7503 | 0 | { |
7504 | 0 | if (settings().get_bool(settings_pack::enable_outgoing_utp) |
7505 | 0 | && (!settings().get_bool(settings_pack::enable_outgoing_tcp) |
7506 | 0 | || peerinfo->supports_utp |
7507 | 0 | || peerinfo->confirmed_supports_utp)) |
7508 | 0 | { |
7509 | 0 | sm = m_ses.utp_socket_manager(); |
7510 | 0 | } |
7511 | | |
7512 | | // don't make a TCP connection if it's disabled |
7513 | 0 | if (sm == nullptr && !settings().get_bool(settings_pack::enable_outgoing_tcp)) |
7514 | 0 | { |
7515 | | #ifndef TORRENT_DISABLE_LOGGING |
7516 | | if (should_log()) |
7517 | | { |
7518 | | debug_log("discarding peer \"%s\": TCP connections disabled " |
7519 | | "[ supports-utp: %d ]", peerinfo->to_string().c_str() |
7520 | | , peerinfo->supports_utp); |
7521 | | } |
7522 | | #endif |
7523 | 0 | return false; |
7524 | 0 | } |
7525 | 0 | } |
7526 | | |
7527 | | // TODO: come up with a better way of doing this, instead of an |
7528 | | // immediately invoked lambda expression. |
7529 | 0 | aux::socket_type s = [&] { |
7530 | |
|
7531 | 0 | #if TORRENT_USE_I2P |
7532 | 0 | if (peerinfo->is_i2p_addr) |
7533 | 0 | { |
7534 | | // It's not entirely obvious why this peer connection is not marked as |
7535 | | // one. The main feature of a peer connection is that whether or not we |
7536 | | // proxy it is configurable. When we use i2p, we want to always prox |
7537 | | // everything via i2p. |
7538 | |
|
7539 | 0 | aux::proxy_settings proxy; |
7540 | 0 | proxy.hostname = settings().get_str(settings_pack::i2p_hostname); |
7541 | 0 | proxy.port = std::uint16_t(settings().get_int(settings_pack::i2p_port)); |
7542 | 0 | proxy.type = settings_pack::i2p_proxy; |
7543 | |
|
7544 | 0 | aux::socket_type ret = instantiate_connection(m_ses.get_context() |
7545 | 0 | , proxy, nullptr, nullptr, false, false); |
7546 | 0 | i2p_stream& str = boost::get<i2p_stream>(ret); |
7547 | 0 | str.set_local_i2p_endpoint(m_ses.local_i2p_endpoint()); |
7548 | 0 | str.set_destination(static_cast<i2p_peer*>(peerinfo)->dest()); |
7549 | 0 | str.set_command(i2p_stream::cmd_connect); |
7550 | 0 | str.set_session_id(m_ses.i2p_session()); |
7551 | 0 | return ret; |
7552 | 0 | } |
7553 | 0 | else |
7554 | 0 | #endif |
7555 | 0 | { |
7556 | 0 | void* userdata = nullptr; |
7557 | 0 | #ifdef TORRENT_SSL_PEERS |
7558 | 0 | if (is_ssl_torrent()) |
7559 | 0 | { |
7560 | 0 | userdata = m_ssl_ctx.get(); |
7561 | | // if we're creating a uTP socket, since this is SSL now, make sure |
7562 | | // to pass in the corresponding utp socket manager |
7563 | 0 | if (sm) sm = m_ses.ssl_utp_socket_manager(); |
7564 | 0 | } |
7565 | 0 | #endif |
7566 | |
|
7567 | 0 | aux::socket_type ret = instantiate_connection(m_ses.get_context() |
7568 | 0 | , m_ses.proxy(), userdata, sm, true, false); |
7569 | |
|
7570 | 0 | #if defined TORRENT_SSL_PEERS |
7571 | 0 | if (is_ssl_torrent()) |
7572 | 0 | { |
7573 | | // for ssl sockets, set the hostname |
7574 | 0 | std::string const host_name = aux::to_hex( |
7575 | 0 | m_torrent_file->info_hashes().get(peerinfo->protocol())); |
7576 | |
|
7577 | 0 | boost::apply_visitor(hostname_visitor{host_name}, ret); |
7578 | 0 | } |
7579 | 0 | #endif |
7580 | 0 | return ret; |
7581 | 0 | } |
7582 | 0 | }(); |
7583 | |
|
7584 | 0 | peer_id const our_pid = aux::generate_peer_id(settings()); |
7585 | 0 | peer_connection_args pack{ |
7586 | 0 | &m_ses |
7587 | 0 | , &settings() |
7588 | 0 | , &m_ses.stats_counters() |
7589 | 0 | , &m_ses.disk_thread() |
7590 | 0 | , &m_ses.get_context() |
7591 | 0 | , shared_from_this() |
7592 | 0 | , std::move(s) |
7593 | 0 | , a |
7594 | 0 | , peerinfo |
7595 | 0 | , our_pid |
7596 | 0 | }; |
7597 | |
|
7598 | 0 | auto c = std::make_shared<bt_peer_connection>(pack); |
7599 | |
|
7600 | 0 | #if TORRENT_USE_ASSERTS |
7601 | 0 | c->m_in_constructor = false; |
7602 | 0 | #endif |
7603 | |
|
7604 | 0 | c->add_stat(std::int64_t(peerinfo->prev_amount_download) << 10 |
7605 | 0 | , std::int64_t(peerinfo->prev_amount_upload) << 10); |
7606 | 0 | peerinfo->prev_amount_download = 0; |
7607 | 0 | peerinfo->prev_amount_upload = 0; |
7608 | |
|
7609 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
7610 | 0 | for (auto const& ext : m_extensions) |
7611 | 0 | { |
7612 | 0 | std::shared_ptr<peer_plugin> pp(ext->new_connection( |
7613 | 0 | peer_connection_handle(c->self()))); |
7614 | 0 | if (pp) c->add_extension(pp); |
7615 | 0 | } |
7616 | 0 | #endif |
7617 | | |
7618 | | // add the newly connected peer to this torrent's peer list |
7619 | 0 | TORRENT_ASSERT(m_iterating_connections == 0); |
7620 | | |
7621 | | // we don't want to have to allocate memory to disconnect this peer, so |
7622 | | // make sure there's enough memory allocated in the deferred_disconnect |
7623 | | // list up-front |
7624 | 0 | m_peers_to_disconnect.reserve(m_connections.size() + 1); |
7625 | |
|
7626 | 0 | sorted_insert(m_connections, c.get()); |
7627 | 0 | try |
7628 | 0 | { |
7629 | 0 | m_outgoing_pids.insert(our_pid); |
7630 | 0 | m_ses.insert_peer(c); |
7631 | 0 | need_peer_list(); |
7632 | 0 | m_peer_list->set_connection(peerinfo, c.get()); |
7633 | 0 | if (peerinfo->seed) |
7634 | 0 | { |
7635 | 0 | TORRENT_ASSERT(m_num_seeds < 0xffff); |
7636 | 0 | ++m_num_seeds; |
7637 | 0 | } |
7638 | 0 | update_want_peers(); |
7639 | 0 | update_want_tick(); |
7640 | 0 | c->start(); |
7641 | |
|
7642 | 0 | if (c->is_disconnecting()) return false; |
7643 | 0 | } |
7644 | 0 | catch (std::exception const&) |
7645 | 0 | { |
7646 | 0 | TORRENT_ASSERT(m_iterating_connections == 0); |
7647 | 0 | c->disconnect(errors::no_error, operation_t::bittorrent, peer_connection_interface::failure); |
7648 | 0 | return false; |
7649 | 0 | } |
7650 | | |
7651 | 0 | #ifndef TORRENT_DISABLE_SHARE_MODE |
7652 | 0 | if (m_share_mode) |
7653 | 0 | recalc_share_mode(); |
7654 | 0 | #endif |
7655 | |
|
7656 | 0 | return peerinfo->connection != nullptr; |
7657 | 0 | } |
7658 | | |
7659 | | error_code torrent::initialize_merkle_trees() |
7660 | 1.88k | { |
7661 | 1.88k | if (!info_hash().has_v2()) return {}; |
7662 | | |
7663 | 1.88k | bool valid = m_torrent_file->v2_piece_hashes_verified(); |
7664 | | |
7665 | 1.88k | file_storage const& fs = m_torrent_file->orig_files(); |
7666 | 1.88k | m_merkle_trees.reserve(fs.num_files()); |
7667 | 1.88k | for (file_index_t i : fs.file_range()) |
7668 | 1.88k | { |
7669 | 1.88k | if (fs.pad_file_at(i) || fs.file_size(i) == 0) |
7670 | 0 | { |
7671 | 0 | m_merkle_trees.emplace_back(); |
7672 | 0 | continue; |
7673 | 0 | } |
7674 | 1.88k | m_merkle_trees.emplace_back(fs.file_num_blocks(i) |
7675 | 1.88k | , fs.blocks_per_piece(), fs.root_ptr(i)); |
7676 | 1.88k | auto const piece_layer = m_torrent_file->piece_layer(i); |
7677 | 1.88k | if (piece_layer.empty()) |
7678 | 0 | { |
7679 | 0 | valid = false; |
7680 | 0 | continue; |
7681 | 0 | } |
7682 | | |
7683 | 1.88k | if (!m_merkle_trees[i].load_piece_layer(piece_layer)) |
7684 | 0 | { |
7685 | 0 | m_merkle_trees[i] = aux::merkle_tree(); |
7686 | 0 | m_v2_piece_layers_validated = false; |
7687 | 0 | return errors::torrent_invalid_piece_layer; |
7688 | 0 | } |
7689 | 1.88k | } |
7690 | | |
7691 | 1.88k | m_v2_piece_layers_validated = valid; |
7692 | | |
7693 | 1.88k | m_torrent_file->free_piece_layers(); |
7694 | 1.88k | return {}; |
7695 | 1.88k | } |
7696 | | |
7697 | | bool torrent::set_metadata(span<char const> metadata_buf) |
7698 | 0 | { |
7699 | 0 | TORRENT_ASSERT(is_single_thread()); |
7700 | 0 | INVARIANT_CHECK; |
7701 | |
|
7702 | 0 | if (m_torrent_file->is_valid()) return false; |
7703 | | |
7704 | 0 | if (m_torrent_file->info_hashes().has_v1()) |
7705 | 0 | { |
7706 | 0 | sha1_hash const info_hash = hasher(metadata_buf).final(); |
7707 | 0 | if (info_hash != m_torrent_file->info_hashes().v1) |
7708 | 0 | { |
7709 | | // check if the v1 hash is a truncated v2 hash |
7710 | 0 | sha256_hash const info_hash2 = hasher256(metadata_buf).final(); |
7711 | 0 | if (sha1_hash(info_hash2.data()) != m_torrent_file->info_hashes().v1) |
7712 | 0 | { |
7713 | 0 | if (alerts().should_post<metadata_failed_alert>()) |
7714 | 0 | { |
7715 | 0 | alerts().emplace_alert<metadata_failed_alert>(get_handle() |
7716 | 0 | , errors::mismatching_info_hash); |
7717 | 0 | } |
7718 | 0 | return false; |
7719 | 0 | } |
7720 | 0 | } |
7721 | 0 | } |
7722 | 0 | if (m_torrent_file->info_hashes().has_v2()) |
7723 | 0 | { |
7724 | | // we don't have to worry about computing the v2 hash twice because |
7725 | | // if the v1 hash was a truncated v2 hash then the torrent_file should |
7726 | | // not have a v2 hash and we shouldn't get here |
7727 | 0 | sha256_hash const info_hash = hasher256(metadata_buf).final(); |
7728 | 0 | if (info_hash != m_torrent_file->info_hashes().v2) |
7729 | 0 | { |
7730 | 0 | if (alerts().should_post<metadata_failed_alert>()) |
7731 | 0 | { |
7732 | 0 | alerts().emplace_alert<metadata_failed_alert>(get_handle() |
7733 | 0 | , errors::mismatching_info_hash); |
7734 | 0 | } |
7735 | 0 | return false; |
7736 | 0 | } |
7737 | 0 | } |
7738 | | |
7739 | | // the torrent's info hash might change |
7740 | | // e.g. it could be a hybrid torrent which we only had one of the hashes for |
7741 | | // so remove the existing entry |
7742 | 0 | info_hash_t const old_ih = m_torrent_file->info_hashes(); |
7743 | |
|
7744 | 0 | error_code ec; |
7745 | 0 | int pos = 0; |
7746 | 0 | bdecode_node const metadata = bdecode(metadata_buf, ec, &pos, 200 |
7747 | 0 | , settings().get_int(settings_pack::metadata_token_limit)); |
7748 | |
|
7749 | 0 | auto info = std::make_shared<torrent_info>(*m_torrent_file); |
7750 | 0 | if (ec || !info->parse_info_section(metadata, ec |
7751 | 0 | , settings().get_int(settings_pack::max_piece_count))) |
7752 | 0 | { |
7753 | 0 | update_gauge(); |
7754 | | // this means the metadata is correct, since we |
7755 | | // verified it against the info-hash, but we |
7756 | | // failed to parse it. Pause the torrent |
7757 | 0 | if (alerts().should_post<metadata_failed_alert>()) |
7758 | 0 | { |
7759 | 0 | alerts().emplace_alert<metadata_failed_alert>(get_handle(), ec); |
7760 | 0 | } |
7761 | 0 | set_error(errors::invalid_swarm_metadata, torrent_status::error_file_none); |
7762 | 0 | pause(); |
7763 | 0 | return false; |
7764 | 0 | } |
7765 | | |
7766 | | // we might already have this torrent in the session. |
7767 | 0 | bool failed = false; |
7768 | 0 | info->info_hashes().for_each([&](sha1_hash const& ih, protocol_version) |
7769 | 0 | { |
7770 | 0 | if (failed) return; |
7771 | | |
7772 | 0 | auto t = m_ses.find_torrent(info_hash_t(ih)).lock(); |
7773 | 0 | if (t && t != shared_from_this()) |
7774 | 0 | { |
7775 | 0 | TORRENT_ASSERT(!t->valid_metadata()); |
7776 | | |
7777 | | // if we get a collision, both torrents fail and have to be |
7778 | | // removed. This is because updating the info_hash_t for this |
7779 | | // torrent would conflict with torrent "t". That would violate |
7780 | | // the invariants: |
7781 | | // 1. an info-hash can only refer to a single torrent |
7782 | | // 2. every torrent needs at least one info-hash. |
7783 | 0 | t->set_error(errors::duplicate_torrent, torrent_status::error_file_metadata); |
7784 | 0 | t->pause(); |
7785 | |
|
7786 | 0 | set_error(errors::duplicate_torrent, torrent_status::error_file_metadata); |
7787 | 0 | pause(); |
7788 | 0 | failed = true; |
7789 | |
|
7790 | 0 | if (alerts().should_post<torrent_conflict_alert>()) |
7791 | 0 | { |
7792 | 0 | alerts().emplace_alert<torrent_conflict_alert>(get_handle() |
7793 | 0 | , torrent_handle(std::move(t)), std::move(info)); |
7794 | 0 | } |
7795 | 0 | } |
7796 | 0 | }); |
7797 | |
|
7798 | 0 | if (failed) return true; |
7799 | 0 | if (m_abort) return true; |
7800 | | |
7801 | 0 | m_torrent_file = info; |
7802 | 0 | m_info_hash = m_torrent_file->info_hashes(); |
7803 | |
|
7804 | 0 | m_size_on_disk = aux::size_on_disk(m_torrent_file->files()); |
7805 | |
|
7806 | 0 | m_ses.update_torrent_info_hash(shared_from_this(), old_ih); |
7807 | |
|
7808 | 0 | ec = initialize_merkle_trees(); |
7809 | 0 | if (ec) |
7810 | 0 | { |
7811 | 0 | set_error(ec, torrent_status::error_file_metadata); |
7812 | 0 | pause(); |
7813 | 0 | return false; |
7814 | 0 | } |
7815 | | |
7816 | 0 | update_gauge(); |
7817 | 0 | update_want_tick(); |
7818 | |
|
7819 | 0 | if (m_ses.alerts().should_post<metadata_received_alert>()) |
7820 | 0 | { |
7821 | 0 | m_ses.alerts().emplace_alert<metadata_received_alert>( |
7822 | 0 | get_handle()); |
7823 | 0 | } |
7824 | | |
7825 | | // we have to initialize the torrent before we start |
7826 | | // disconnecting redundant peers, otherwise we'll think |
7827 | | // we're a seed, because we have all 0 pieces |
7828 | 0 | init(); |
7829 | |
|
7830 | 0 | inc_stats_counter(counters::num_total_pieces_added |
7831 | 0 | , m_torrent_file->num_pieces()); |
7832 | | |
7833 | | // disconnect redundant peers |
7834 | 0 | for (auto p : m_connections) |
7835 | 0 | p->disconnect_if_redundant(); |
7836 | |
|
7837 | 0 | set_need_save_resume(torrent_handle::if_metadata_changed); |
7838 | |
|
7839 | 0 | return true; |
7840 | 0 | } |
7841 | | |
7842 | | namespace { |
7843 | | |
7844 | | bool connecting_time_compare(peer_connection const* lhs, peer_connection const* rhs) |
7845 | 0 | { |
7846 | 0 | bool const lhs_connecting = lhs->is_connecting() && !lhs->is_disconnecting(); |
7847 | 0 | bool const rhs_connecting = rhs->is_connecting() && !rhs->is_disconnecting(); |
7848 | 0 | if (lhs_connecting != rhs_connecting) return (int(lhs_connecting) < int(rhs_connecting)); |
7849 | | |
7850 | | // a lower value of connected_time means it's been waiting |
7851 | | // longer. This is a less-than comparison, so if lhs has |
7852 | | // waited longer than rhs, we should return false. |
7853 | 0 | return lhs->connected_time() > rhs->connected_time(); |
7854 | 0 | } |
7855 | | |
7856 | | } // anonymous namespace |
7857 | | |
7858 | 282 | bool torrent::attach_peer(peer_connection* p) try |
7859 | 282 | { |
7860 | | // INVARIANT_CHECK; |
7861 | | |
7862 | 282 | #ifdef TORRENT_SSL_PEERS |
7863 | 282 | if (is_ssl_torrent()) |
7864 | 0 | { |
7865 | | // if this is an SSL torrent, don't allow non SSL peers on it |
7866 | 0 | aux::socket_type& s = p->get_socket(); |
7867 | |
|
7868 | 0 | auto stream_handle = boost::apply_visitor(ssl_handle_visitor{}, s); |
7869 | |
|
7870 | 0 | if (!stream_handle) |
7871 | 0 | { |
7872 | | // don't allow non SSL peers on SSL torrents |
7873 | 0 | p->disconnect(errors::requires_ssl_connection, operation_t::bittorrent); |
7874 | 0 | return false; |
7875 | 0 | } |
7876 | | |
7877 | 0 | if (!m_ssl_ctx) |
7878 | 0 | { |
7879 | | // we don't have a valid cert, don't accept any connection! |
7880 | 0 | p->disconnect(errors::invalid_ssl_cert, operation_t::ssl_handshake); |
7881 | 0 | return false; |
7882 | 0 | } |
7883 | | |
7884 | 0 | if (!ssl::has_context(stream_handle, ssl::get_handle(*m_ssl_ctx))) |
7885 | 0 | { |
7886 | | // if the SSL context associated with this connection is |
7887 | | // not the one belonging to this torrent, the SSL handshake |
7888 | | // connected to one torrent, and the BitTorrent protocol |
7889 | | // to a different one. This is probably an attempt to circumvent |
7890 | | // access control. Don't allow it. |
7891 | 0 | p->disconnect(errors::invalid_ssl_cert, operation_t::bittorrent); |
7892 | 0 | return false; |
7893 | 0 | } |
7894 | 0 | } |
7895 | | #else // TORRENT_SSL_PEERS |
7896 | | if (is_ssl_torrent()) |
7897 | | { |
7898 | | // Don't accidentally allow seeding of SSL torrents, just |
7899 | | // because libtorrent wasn't built with SSL support |
7900 | | p->disconnect(errors::requires_ssl_connection, operation_t::ssl_handshake); |
7901 | | return false; |
7902 | | } |
7903 | | #endif // TORRENT_SSL_PEERS |
7904 | | |
7905 | 282 | TORRENT_ASSERT(p != nullptr); |
7906 | 282 | TORRENT_ASSERT(!p->is_outgoing()); |
7907 | | |
7908 | 282 | m_has_incoming = true; |
7909 | | |
7910 | 282 | if (m_apply_ip_filter |
7911 | 282 | && m_ip_filter |
7912 | 282 | && m_ip_filter->access(p->remote().address()) & ip_filter::blocked) |
7913 | 0 | { |
7914 | 0 | if (m_ses.alerts().should_post<peer_blocked_alert>()) |
7915 | 0 | m_ses.alerts().emplace_alert<peer_blocked_alert>(get_handle() |
7916 | 0 | , p->remote(), peer_blocked_alert::ip_filter); |
7917 | 0 | p->disconnect(errors::banned_by_ip_filter, operation_t::bittorrent); |
7918 | 0 | return false; |
7919 | 0 | } |
7920 | | |
7921 | 282 | if (!is_downloading_state(m_state) && valid_metadata()) |
7922 | 0 | { |
7923 | 0 | p->disconnect(errors::torrent_not_ready, operation_t::bittorrent); |
7924 | 0 | return false; |
7925 | 0 | } |
7926 | | |
7927 | 282 | if (!m_ses.has_connection(p)) |
7928 | 0 | { |
7929 | 0 | p->disconnect(errors::peer_not_constructed, operation_t::bittorrent); |
7930 | 0 | return false; |
7931 | 0 | } |
7932 | | |
7933 | 282 | if (m_ses.is_aborted()) |
7934 | 0 | { |
7935 | 0 | p->disconnect(errors::session_closing, operation_t::bittorrent); |
7936 | 0 | return false; |
7937 | 0 | } |
7938 | | |
7939 | 282 | int connection_limit_factor = 0; |
7940 | 846 | for (int i = 0; i < p->num_classes(); ++i) |
7941 | 564 | { |
7942 | 564 | peer_class_t pc = p->class_at(i); |
7943 | 564 | if (m_ses.peer_classes().at(pc) == nullptr) continue; |
7944 | 564 | int f = m_ses.peer_classes().at(pc)->connection_limit_factor; |
7945 | 564 | if (connection_limit_factor < f) connection_limit_factor = f; |
7946 | 564 | } |
7947 | 282 | if (connection_limit_factor == 0) connection_limit_factor = 100; |
7948 | | |
7949 | 282 | std::int64_t const limit = std::int64_t(m_max_connections) * 100 / connection_limit_factor; |
7950 | | |
7951 | 282 | bool maybe_replace_peer = false; |
7952 | | |
7953 | 282 | if (m_connections.end_index() >= limit) |
7954 | 0 | { |
7955 | | // if more than 10% of the connections are outgoing |
7956 | | // connection attempts that haven't completed yet, |
7957 | | // disconnect one of them and let this incoming |
7958 | | // connection through. |
7959 | 0 | if (m_num_connecting > m_max_connections / 10) |
7960 | 0 | { |
7961 | | // find one of the connecting peers and disconnect it |
7962 | | // find any peer that's connecting (i.e. a half-open TCP connection) |
7963 | | // that's also not disconnecting |
7964 | | // disconnect the peer that's been waiting to establish a connection |
7965 | | // the longest |
7966 | 0 | auto i = std::max_element(begin(), end(), &connecting_time_compare); |
7967 | |
|
7968 | 0 | if (i == end() || !(*i)->is_connecting() || (*i)->is_disconnecting()) |
7969 | 0 | { |
7970 | | // this seems odd, but we might as well handle it |
7971 | 0 | p->disconnect(errors::too_many_connections, operation_t::bittorrent); |
7972 | 0 | return false; |
7973 | 0 | } |
7974 | 0 | (*i)->disconnect(errors::too_many_connections, operation_t::bittorrent); |
7975 | | |
7976 | | // if this peer was let in via connections slack, |
7977 | | // it has done its duty of causing the disconnection |
7978 | | // of another peer |
7979 | 0 | p->peer_disconnected_other(); |
7980 | 0 | } |
7981 | 0 | else |
7982 | 0 | { |
7983 | 0 | maybe_replace_peer = true; |
7984 | 0 | } |
7985 | 0 | } |
7986 | | |
7987 | 282 | #ifndef TORRENT_DISABLE_EXTENSIONS |
7988 | 282 | for (auto& ext : m_extensions) |
7989 | 846 | { |
7990 | 846 | std::shared_ptr<peer_plugin> pp(ext->new_connection( |
7991 | 846 | peer_connection_handle(p->self()))); |
7992 | 846 | if (pp) p->add_extension(pp); |
7993 | 846 | } |
7994 | 282 | #endif |
7995 | 282 | torrent_state st = get_peer_list_state(); |
7996 | 282 | need_peer_list(); |
7997 | 282 | if (!m_peer_list->new_connection(*p, m_ses.session_time(), &st)) |
7998 | 0 | { |
7999 | 0 | peers_erased(st.erased); |
8000 | | #ifndef TORRENT_DISABLE_LOGGING |
8001 | | if (should_log()) |
8002 | | { |
8003 | | debug_log("CLOSING CONNECTION \"%s\" peer list full " |
8004 | | "connections: %d limit: %d" |
8005 | | , print_endpoint(p->remote()).c_str() |
8006 | | , num_peers() |
8007 | | , m_max_connections); |
8008 | | } |
8009 | | #endif |
8010 | 0 | p->disconnect(errors::too_many_connections, operation_t::bittorrent); |
8011 | 0 | return false; |
8012 | 0 | } |
8013 | 282 | peers_erased(st.erased); |
8014 | | |
8015 | 282 | m_peers_to_disconnect.reserve(m_connections.size() + 1); |
8016 | 282 | m_connections.reserve(m_connections.size() + 1); |
8017 | | |
8018 | 282 | #if TORRENT_USE_ASSERTS |
8019 | 282 | error_code ec; |
8020 | 282 | TORRENT_ASSERT(p->remote() == p->get_socket().remote_endpoint(ec) || ec); |
8021 | 282 | #endif |
8022 | | |
8023 | 282 | TORRENT_ASSERT(p->peer_info_struct() != nullptr); |
8024 | | |
8025 | | // we need to do this after we've added the peer to the peer_list |
8026 | | // since that's when the peer is assigned its peer_info object, |
8027 | | // which holds the rank |
8028 | 282 | if (maybe_replace_peer) |
8029 | 0 | { |
8030 | | // now, find the lowest rank peer and disconnect that |
8031 | | // if it's lower rank than the incoming connection |
8032 | 0 | peer_connection* peer = find_lowest_ranking_peer(); |
8033 | | |
8034 | | // TODO: 2 if peer is a really good peer, maybe we shouldn't disconnect it |
8035 | | // perhaps this logic should be disabled if we have too many idle peers |
8036 | | // (with some definition of idle) |
8037 | 0 | if (peer != nullptr && peer->peer_rank() < p->peer_rank()) |
8038 | 0 | { |
8039 | | #ifndef TORRENT_DISABLE_LOGGING |
8040 | | if (should_log()) |
8041 | | { |
8042 | | debug_log("CLOSING CONNECTION \"%s\" peer list full (low peer rank) " |
8043 | | "connections: %d limit: %d" |
8044 | | , print_endpoint(peer->remote()).c_str() |
8045 | | , num_peers() |
8046 | | , m_max_connections); |
8047 | | } |
8048 | | #endif |
8049 | 0 | peer->disconnect(errors::too_many_connections, operation_t::bittorrent); |
8050 | 0 | p->peer_disconnected_other(); |
8051 | 0 | } |
8052 | 0 | else |
8053 | 0 | { |
8054 | | #ifndef TORRENT_DISABLE_LOGGING |
8055 | | if (should_log()) |
8056 | | { |
8057 | | debug_log("CLOSING CONNECTION \"%s\" peer list full (low peer rank) " |
8058 | | "connections: %d limit: %d" |
8059 | | , print_endpoint(p->remote()).c_str() |
8060 | | , num_peers() |
8061 | | , m_max_connections); |
8062 | | } |
8063 | | #endif |
8064 | 0 | p->disconnect(errors::too_many_connections, operation_t::bittorrent); |
8065 | | // we have to do this here because from the peer's point of view |
8066 | | // it wasn't really attached to the torrent, but we do need |
8067 | | // to let peer_list know we're removing it |
8068 | 0 | remove_peer(p->self()); |
8069 | 0 | return false; |
8070 | 0 | } |
8071 | 0 | } |
8072 | | |
8073 | | #if TORRENT_USE_INVARIANT_CHECKS |
8074 | | if (m_peer_list) m_peer_list->check_invariant(); |
8075 | | #endif |
8076 | | |
8077 | 282 | #ifndef TORRENT_DISABLE_SHARE_MODE |
8078 | 282 | if (m_share_mode) |
8079 | 0 | recalc_share_mode(); |
8080 | 282 | #endif |
8081 | | |
8082 | | // once we add the peer to our m_connections list, we can't throw an |
8083 | | // exception. That will end up violating an invariant between the session, |
8084 | | // torrent and peers |
8085 | 282 | TORRENT_ASSERT(sorted_find(m_connections, p) == m_connections.end()); |
8086 | 282 | TORRENT_ASSERT(m_iterating_connections == 0); |
8087 | 282 | sorted_insert(m_connections, p); |
8088 | 282 | update_want_peers(); |
8089 | 282 | update_want_tick(); |
8090 | | |
8091 | 282 | if (p->peer_info_struct() && p->peer_info_struct()->seed) |
8092 | 35 | { |
8093 | 35 | TORRENT_ASSERT(m_num_seeds < 0xffff); |
8094 | 35 | ++m_num_seeds; |
8095 | 35 | } |
8096 | | |
8097 | | #ifndef TORRENT_DISABLE_LOGGING |
8098 | | if (should_log()) try |
8099 | | { |
8100 | | debug_log("ATTACHED CONNECTION \"%s\" connections: %d limit: %d num-peers: %d" |
8101 | | , print_endpoint(p->remote()).c_str(), num_peers() |
8102 | | , m_max_connections |
8103 | | , num_peers()); |
8104 | | } |
8105 | | catch (std::exception const&) {} |
8106 | | #endif |
8107 | | |
8108 | 282 | return true; |
8109 | 282 | } |
8110 | 282 | catch (...) |
8111 | 282 | { |
8112 | 0 | p->disconnect(errors::torrent_not_ready, operation_t::bittorrent); |
8113 | | // from the peer's point of view it was never really added to the torrent. |
8114 | | // So we need to clean it up here before propagating the error |
8115 | 0 | remove_peer(p->self()); |
8116 | 0 | return false; |
8117 | 0 | } |
8118 | | |
8119 | | bool torrent::want_tick() const |
8120 | 15.7k | { |
8121 | 15.7k | if (m_abort) return false; |
8122 | | |
8123 | 10.1k | if (!m_connections.empty()) return true; |
8124 | | |
8125 | | // we might want to connect web seeds |
8126 | 9.53k | if (!is_finished() && !m_web_seeds.empty() && m_files_checked) |
8127 | 0 | return true; |
8128 | | |
8129 | 9.53k | if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) |
8130 | 0 | return true; |
8131 | | |
8132 | | // if we don't get ticks we won't become inactive |
8133 | 9.53k | if (!m_paused && !m_inactive) return true; |
8134 | | |
8135 | 3.79k | return false; |
8136 | 9.53k | } |
8137 | | |
8138 | | void torrent::update_want_tick() |
8139 | 15.7k | { |
8140 | 15.7k | update_list(aux::session_interface::torrent_want_tick, want_tick()); |
8141 | 15.7k | } |
8142 | | |
8143 | | // this function adjusts which lists this torrent is part of (checking, |
8144 | | // seeding or downloading) |
8145 | | void torrent::update_state_list() |
8146 | 7.68k | { |
8147 | 7.68k | bool is_checking = false; |
8148 | 7.68k | bool is_downloading = false; |
8149 | 7.68k | bool is_seeding = false; |
8150 | | |
8151 | 7.68k | if (is_auto_managed() && !has_error()) |
8152 | 1.93k | { |
8153 | 1.93k | if (m_state == torrent_status::checking_files) |
8154 | 55 | { |
8155 | 55 | is_checking = true; |
8156 | 55 | } |
8157 | 1.88k | else if (m_state == torrent_status::downloading_metadata |
8158 | 1.88k | || m_state == torrent_status::downloading |
8159 | 1.88k | || m_state == torrent_status::finished |
8160 | 1.88k | || m_state == torrent_status::seeding) |
8161 | 413 | { |
8162 | | // torrents that are started (not paused) and |
8163 | | // inactive are not part of any list. They will not be touched because |
8164 | | // they are inactive |
8165 | 413 | if (is_finished()) |
8166 | 270 | is_seeding = true; |
8167 | 143 | else |
8168 | 143 | is_downloading = true; |
8169 | 413 | } |
8170 | 1.93k | } |
8171 | | |
8172 | 7.68k | update_list(aux::session_interface::torrent_downloading_auto_managed |
8173 | 7.68k | , is_downloading); |
8174 | 7.68k | update_list(aux::session_interface::torrent_seeding_auto_managed |
8175 | 7.68k | , is_seeding); |
8176 | 7.68k | update_list(aux::session_interface::torrent_checking_auto_managed |
8177 | 7.68k | , is_checking); |
8178 | 7.68k | } |
8179 | | |
8180 | | // returns true if this torrent is interested in connecting to more peers |
8181 | | bool torrent::want_peers() const |
8182 | 7.03k | { |
8183 | | // if all our connection slots are taken, we can't connect to more |
8184 | 7.03k | if (num_peers() >= int(m_max_connections)) return false; |
8185 | | |
8186 | | // if we're paused, obviously we're not connecting to peers |
8187 | 6.96k | if (is_paused() || m_abort || m_graceful_pause_mode) return false; |
8188 | | |
8189 | | // if metadata are valid and we are either checking files or checking resume data without no_verify_files flag, |
8190 | | // we don't want peers |
8191 | 2.33k | if ((m_state == torrent_status::checking_files |
8192 | 2.33k | || (m_state == torrent_status::checking_resume_data |
8193 | 2.33k | && !(m_add_torrent_params && m_add_torrent_params->flags & torrent_flags::no_verify_files))) |
8194 | 2.33k | && valid_metadata()) |
8195 | 0 | return false; |
8196 | | |
8197 | | // if we don't know of any more potential peers to connect to, there's |
8198 | | // no point in trying |
8199 | 2.33k | if (!m_peer_list || m_peer_list->num_connect_candidates() == 0) |
8200 | 2.08k | return false; |
8201 | | |
8202 | | // if the user disabled outgoing connections for seeding torrents, |
8203 | | // don't make any |
8204 | 244 | if (!settings().get_bool(settings_pack::seeding_outgoing_connections) |
8205 | 244 | && (m_state == torrent_status::seeding |
8206 | 0 | || m_state == torrent_status::finished)) |
8207 | 0 | return false; |
8208 | | |
8209 | 244 | if (!settings().get_bool(settings_pack::enable_outgoing_tcp) |
8210 | 244 | && !settings().get_bool(settings_pack::enable_outgoing_utp)) |
8211 | 244 | return false; |
8212 | | |
8213 | 0 | return true; |
8214 | 244 | } |
8215 | | |
8216 | | bool torrent::want_peers_download() const |
8217 | 13.1k | { |
8218 | 13.1k | return (m_state == torrent_status::downloading |
8219 | 13.1k | || m_state == torrent_status::downloading_metadata) |
8220 | 13.1k | && want_peers(); |
8221 | 13.1k | } |
8222 | | |
8223 | | bool torrent::want_peers_finished() const |
8224 | 13.1k | { |
8225 | 13.1k | return (m_state == torrent_status::finished |
8226 | 13.1k | || m_state == torrent_status::seeding) |
8227 | 13.1k | && want_peers(); |
8228 | 13.1k | } |
8229 | | |
8230 | | void torrent::update_want_peers() |
8231 | 13.1k | { |
8232 | 13.1k | update_list(aux::session_interface::torrent_want_peers_download, want_peers_download()); |
8233 | 13.1k | update_list(aux::session_interface::torrent_want_peers_finished, want_peers_finished()); |
8234 | 13.1k | } |
8235 | | |
8236 | | void torrent::update_want_scrape() |
8237 | 4.21k | { |
8238 | 4.21k | update_list(aux::session_interface::torrent_want_scrape |
8239 | 4.21k | , m_paused && m_auto_managed && !m_abort); |
8240 | 4.21k | } |
8241 | | |
8242 | | namespace { |
8243 | | |
8244 | | #ifndef TORRENT_DISABLE_LOGGING |
8245 | | char const* list_name(torrent_list_index_t const idx) |
8246 | | { |
8247 | | #define TORRENT_LIST_NAME(n) case static_cast<int>(aux::session_interface:: n): return #n |
8248 | | switch (static_cast<int>(idx)) |
8249 | | { |
8250 | | TORRENT_LIST_NAME(torrent_state_updates); |
8251 | | TORRENT_LIST_NAME(torrent_want_tick); |
8252 | | TORRENT_LIST_NAME(torrent_want_peers_download); |
8253 | | TORRENT_LIST_NAME(torrent_want_peers_finished); |
8254 | | TORRENT_LIST_NAME(torrent_want_scrape); |
8255 | | TORRENT_LIST_NAME(torrent_downloading_auto_managed); |
8256 | | TORRENT_LIST_NAME(torrent_seeding_auto_managed); |
8257 | | TORRENT_LIST_NAME(torrent_checking_auto_managed); |
8258 | | default: TORRENT_ASSERT_FAIL_VAL(idx); |
8259 | | } |
8260 | | #undef TORRENT_LIST_NAME |
8261 | | return ""; |
8262 | | } |
8263 | | #endif // TORRENT_DISABLE_LOGGING |
8264 | | |
8265 | | } // anonymous namespace |
8266 | | |
8267 | | void torrent::update_list(torrent_list_index_t const list, bool in) |
8268 | 69.3k | { |
8269 | 69.3k | link& l = m_links[list]; |
8270 | 69.3k | aux::vector<torrent*>& v = m_ses.torrent_list(list); |
8271 | | |
8272 | 69.3k | if (in) |
8273 | 7.02k | { |
8274 | 7.02k | if (l.in_list()) return; |
8275 | 2.01k | l.insert(v, this); |
8276 | 2.01k | } |
8277 | 62.3k | else |
8278 | 62.3k | { |
8279 | 62.3k | if (!l.in_list()) return; |
8280 | 2.01k | l.unlink(v, list); |
8281 | 2.01k | } |
8282 | | |
8283 | | #ifndef TORRENT_DISABLE_LOGGING |
8284 | | if (should_log()) |
8285 | | debug_log("*** UPDATE LIST [ %s : %d ]", list_name(list), int(in)); |
8286 | | #endif |
8287 | 69.3k | } |
8288 | | |
8289 | | void torrent::disconnect_all(error_code const& ec, operation_t op) |
8290 | 2.15k | { |
8291 | 2.15k | TORRENT_ASSERT(m_iterating_connections == 0); |
8292 | 2.15k | for (auto const& p : m_connections) |
8293 | 0 | { |
8294 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
8295 | 0 | TORRENT_ASSERT(p->associated_torrent().lock().get() == this); |
8296 | 0 | p->disconnect(ec, op); |
8297 | 0 | } |
8298 | | |
8299 | 2.15k | update_want_peers(); |
8300 | 2.15k | update_want_tick(); |
8301 | 2.15k | } |
8302 | | |
8303 | | namespace { |
8304 | | |
8305 | | // this returns true if lhs is a better disconnect candidate than rhs |
8306 | | bool compare_disconnect_peer(peer_connection const* lhs, peer_connection const* rhs) |
8307 | 0 | { |
8308 | | // prefer to disconnect peers that are already disconnecting |
8309 | 0 | if (lhs->is_disconnecting() != rhs->is_disconnecting()) |
8310 | 0 | return lhs->is_disconnecting(); |
8311 | | |
8312 | | // prefer to disconnect peers we're not interested in |
8313 | 0 | if (lhs->is_interesting() != rhs->is_interesting()) |
8314 | 0 | return rhs->is_interesting(); |
8315 | | |
8316 | | // prefer to disconnect peers that are not seeds |
8317 | 0 | if (lhs->is_seed() != rhs->is_seed()) |
8318 | 0 | return rhs->is_seed(); |
8319 | | |
8320 | | // prefer to disconnect peers that are on parole |
8321 | 0 | if (lhs->on_parole() != rhs->on_parole()) |
8322 | 0 | return lhs->on_parole(); |
8323 | | |
8324 | | // prefer to disconnect peers that send data at a lower rate |
8325 | 0 | std::int64_t lhs_transferred = lhs->statistics().total_payload_download(); |
8326 | 0 | std::int64_t rhs_transferred = rhs->statistics().total_payload_download(); |
8327 | |
|
8328 | 0 | time_point const now = aux::time_now(); |
8329 | 0 | std::int64_t const lhs_time_connected = total_seconds(now - lhs->connected_time()); |
8330 | 0 | std::int64_t const rhs_time_connected = total_seconds(now - rhs->connected_time()); |
8331 | |
|
8332 | 0 | lhs_transferred /= lhs_time_connected + 1; |
8333 | 0 | rhs_transferred /= (rhs_time_connected + 1); |
8334 | 0 | if (lhs_transferred != rhs_transferred) |
8335 | 0 | return lhs_transferred < rhs_transferred; |
8336 | | |
8337 | | // prefer to disconnect peers that chokes us |
8338 | 0 | if (lhs->is_choked() != rhs->is_choked()) |
8339 | 0 | return lhs->is_choked(); |
8340 | | |
8341 | 0 | return lhs->last_received() < rhs->last_received(); |
8342 | 0 | } |
8343 | | |
8344 | | } // anonymous namespace |
8345 | | |
8346 | | int torrent::disconnect_peers(int const num, error_code const& ec) |
8347 | 0 | { |
8348 | 0 | INVARIANT_CHECK; |
8349 | |
|
8350 | 0 | #if TORRENT_USE_ASSERTS |
8351 | | // make sure we don't have any dangling pointers |
8352 | 0 | for (auto p : m_connections) |
8353 | 0 | { |
8354 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
8355 | 0 | TORRENT_ASSERT(m_ses.has_peer(p)); |
8356 | 0 | } |
8357 | 0 | #endif |
8358 | 0 | aux::vector<peer_connection*> to_disconnect; |
8359 | 0 | to_disconnect.resize(num); |
8360 | 0 | auto end = std::partial_sort_copy(m_connections.begin(), m_connections.end() |
8361 | 0 | , to_disconnect.begin(), to_disconnect.end(), compare_disconnect_peer); |
8362 | 0 | for (auto p : aux::range(to_disconnect.begin(), end)) |
8363 | 0 | { |
8364 | 0 | TORRENT_ASSERT(p->associated_torrent().lock().get() == this); |
8365 | 0 | p->disconnect(ec, operation_t::bittorrent); |
8366 | 0 | } |
8367 | 0 | return static_cast<int>(end - to_disconnect.begin()); |
8368 | 0 | } |
8369 | | |
8370 | | // called when torrent is finished (all interesting |
8371 | | // pieces have been downloaded) |
8372 | | void torrent::finished() |
8373 | 553 | { |
8374 | 553 | update_want_tick(); |
8375 | 553 | update_state_list(); |
8376 | | |
8377 | 553 | INVARIANT_CHECK; |
8378 | | |
8379 | 553 | TORRENT_ASSERT(is_finished()); |
8380 | | |
8381 | 553 | set_state(torrent_status::finished); |
8382 | 553 | set_queue_position(no_pos); |
8383 | | |
8384 | 553 | m_became_finished = aux::time_now32(); |
8385 | | |
8386 | | // we have to call completed() before we start |
8387 | | // disconnecting peers, since there's an assert |
8388 | | // to make sure we're cleared the piece picker |
8389 | 553 | if (is_seed()) completed(); |
8390 | | |
8391 | 553 | send_upload_only(); |
8392 | 553 | state_updated(); |
8393 | | |
8394 | 553 | if (m_completed_time == 0) |
8395 | 553 | m_completed_time = time(nullptr); |
8396 | | |
8397 | | // disconnect all seeds |
8398 | 553 | if (settings().get_bool(settings_pack::close_redundant_connections)) |
8399 | 553 | { |
8400 | | // TODO: 1 should disconnect all peers that have the pieces we have |
8401 | | // not just seeds. It would be pretty expensive to check all pieces |
8402 | | // for all peers though |
8403 | 553 | std::vector<peer_connection*> seeds; |
8404 | 553 | for (auto const p : m_connections) |
8405 | 0 | { |
8406 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
8407 | 0 | TORRENT_ASSERT(p->associated_torrent().lock().get() == this); |
8408 | 0 | if (p->upload_only() && p->can_disconnect(errors::torrent_finished)) |
8409 | 0 | { |
8410 | | #ifndef TORRENT_DISABLE_LOGGING |
8411 | | p->peer_log(peer_log_alert::info, "SEED", "CLOSING CONNECTION"); |
8412 | | #endif |
8413 | 0 | seeds.push_back(p); |
8414 | 0 | } |
8415 | 0 | } |
8416 | 553 | for (auto& p : seeds) |
8417 | 0 | p->disconnect(errors::torrent_finished, operation_t::bittorrent |
8418 | 0 | , peer_connection_interface::normal); |
8419 | 553 | } |
8420 | | |
8421 | 553 | if (m_abort) return; |
8422 | | |
8423 | 553 | update_want_peers(); |
8424 | | |
8425 | 553 | if (m_storage) |
8426 | 553 | { |
8427 | | // we need to keep the object alive during this operation |
8428 | 553 | m_ses.disk_thread().async_release_files(m_storage |
8429 | 553 | , std::bind(&torrent::on_cache_flushed, shared_from_this(), false)); |
8430 | 553 | m_ses.deferred_submit_jobs(); |
8431 | 553 | } |
8432 | | |
8433 | | // this torrent just completed downloads, which means it will fall |
8434 | | // under a different limit with the auto-manager. Make sure we |
8435 | | // update auto-manage torrents in that case |
8436 | 553 | if (m_auto_managed) |
8437 | 115 | m_ses.trigger_auto_manage(); |
8438 | 553 | } |
8439 | | |
8440 | | // this is called when we were finished, but some files were |
8441 | | // marked for downloading, and we are no longer finished |
8442 | | void torrent::resume_download() |
8443 | 0 | { |
8444 | | // the invariant doesn't hold here, because it expects the torrent |
8445 | | // to be in downloading state (which it will be set to shortly) |
8446 | | // INVARIANT_CHECK; |
8447 | |
|
8448 | 0 | TORRENT_ASSERT(m_state != torrent_status::checking_resume_data |
8449 | 0 | && m_state != torrent_status::checking_files); |
8450 | | |
8451 | | // we're downloading now, which means we're no longer in seed mode |
8452 | 0 | if (m_seed_mode) |
8453 | 0 | leave_seed_mode(seed_mode_t::check_files); |
8454 | |
|
8455 | 0 | TORRENT_ASSERT(!is_finished()); |
8456 | 0 | set_state(torrent_status::downloading); |
8457 | 0 | set_queue_position(last_pos); |
8458 | |
|
8459 | 0 | m_completed_time = 0; |
8460 | |
|
8461 | | #ifndef TORRENT_DISABLE_LOGGING |
8462 | | debug_log("*** RESUME_DOWNLOAD"); |
8463 | | #endif |
8464 | 0 | send_upload_only(); |
8465 | 0 | update_want_tick(); |
8466 | 0 | update_state_list(); |
8467 | 0 | } |
8468 | | |
8469 | | void torrent::maybe_done_flushing() |
8470 | 3.14k | { |
8471 | 3.14k | if (!has_picker()) return; |
8472 | | |
8473 | 2.48k | if (m_picker->is_seeding()) |
8474 | 23 | { |
8475 | | // no need for the piece picker anymore |
8476 | | // when we're suggesting read cache pieces, we |
8477 | | // still need the piece picker, to keep track |
8478 | | // of availability counts for pieces |
8479 | 23 | if (settings().get_int(settings_pack::suggest_mode) |
8480 | 23 | != settings_pack::suggest_read_cache) |
8481 | 23 | { |
8482 | 23 | m_picker.reset(); |
8483 | 23 | m_hash_picker.reset(); |
8484 | 23 | m_file_progress.clear(); |
8485 | 23 | } |
8486 | 23 | m_have_all = true; |
8487 | 23 | } |
8488 | 2.48k | update_gauge(); |
8489 | 2.48k | } |
8490 | | |
8491 | | // called when torrent is complete. i.e. all pieces downloaded |
8492 | | // not necessarily flushed to disk |
8493 | | void torrent::completed() |
8494 | 183 | { |
8495 | 183 | maybe_done_flushing(); |
8496 | | |
8497 | 183 | set_state(torrent_status::seeding); |
8498 | 183 | m_became_seed = aux::time_now32(); |
8499 | | |
8500 | 183 | if (!m_announcing) return; |
8501 | | |
8502 | 0 | time_point32 const now = aux::time_now32(); |
8503 | 0 | for (auto& t : m_trackers) |
8504 | 0 | { |
8505 | 0 | for (auto& aep : t.endpoints) |
8506 | 0 | { |
8507 | 0 | if (!aep.enabled) continue; |
8508 | 0 | for (auto& a : aep.info_hashes) |
8509 | 0 | { |
8510 | 0 | if (a.complete_sent) continue; |
8511 | 0 | a.next_announce = now; |
8512 | 0 | a.min_announce = now; |
8513 | 0 | } |
8514 | 0 | } |
8515 | 0 | } |
8516 | 0 | announce_with_tracker(); |
8517 | 0 | } |
8518 | | |
8519 | | int torrent::deprioritize_tracker(int index) |
8520 | 0 | { |
8521 | 0 | INVARIANT_CHECK; |
8522 | |
|
8523 | 0 | TORRENT_ASSERT(index >= 0); |
8524 | 0 | TORRENT_ASSERT(index < int(m_trackers.size())); |
8525 | 0 | if (index >= int(m_trackers.size())) return -1; |
8526 | | |
8527 | 0 | while (index < int(m_trackers.size()) - 1 && m_trackers[index].tier == m_trackers[index + 1].tier) |
8528 | 0 | { |
8529 | 0 | using std::swap; |
8530 | 0 | swap(m_trackers[index], m_trackers[index + 1]); |
8531 | 0 | if (m_last_working_tracker == index) ++m_last_working_tracker; |
8532 | 0 | else if (m_last_working_tracker == index + 1) --m_last_working_tracker; |
8533 | 0 | ++index; |
8534 | 0 | } |
8535 | 0 | return index; |
8536 | 0 | } |
8537 | | |
8538 | | void torrent::files_checked() |
8539 | 920 | { |
8540 | 920 | TORRENT_ASSERT(is_single_thread()); |
8541 | 920 | TORRENT_ASSERT(m_torrent_file->is_valid()); |
8542 | | |
8543 | 920 | if (m_abort) |
8544 | 0 | { |
8545 | | #ifndef TORRENT_DISABLE_LOGGING |
8546 | | debug_log("files_checked(), paused"); |
8547 | | #endif |
8548 | 0 | return; |
8549 | 0 | } |
8550 | | |
8551 | | // calling pause will also trigger the auto managed |
8552 | | // recalculation |
8553 | | // if we just got here by downloading the metadata, |
8554 | | // just keep going, no need to disconnect all peers just |
8555 | | // to restart the torrent in a second |
8556 | 920 | if (m_auto_managed) |
8557 | 360 | { |
8558 | | // if this is an auto managed torrent, force a recalculation |
8559 | | // of which torrents to have active |
8560 | 360 | m_ses.trigger_auto_manage(); |
8561 | 360 | } |
8562 | | |
8563 | 920 | if (!is_seed()) |
8564 | 737 | { |
8565 | 737 | #ifndef TORRENT_DISABLE_SUPERSEEDING |
8566 | | // turn off super seeding if we're not a seed |
8567 | 737 | if (m_super_seeding) |
8568 | 181 | { |
8569 | 181 | m_super_seeding = false; |
8570 | 181 | set_need_save_resume(torrent_handle::if_state_changed); |
8571 | 181 | state_updated(); |
8572 | 181 | } |
8573 | 737 | #endif |
8574 | | |
8575 | 737 | if (m_state != torrent_status::finished && is_finished()) |
8576 | 370 | finished(); |
8577 | 737 | } |
8578 | 183 | else |
8579 | 183 | { |
8580 | | // we just added this torrent as a seed, or force-rechecked it, and we |
8581 | | // have all of it. Assume that we sent the event=completed when we |
8582 | | // finished downloading it, and don't send any more. |
8583 | 183 | m_complete_sent = true; |
8584 | 183 | for (auto& t : m_trackers) |
8585 | 0 | { |
8586 | 0 | for (auto& aep : t.endpoints) |
8587 | 0 | { |
8588 | 0 | for (auto& a : aep.info_hashes) |
8589 | 0 | a.complete_sent = true; |
8590 | 0 | } |
8591 | 0 | } |
8592 | | |
8593 | 183 | if (m_state != torrent_status::finished |
8594 | 183 | && m_state != torrent_status::seeding) |
8595 | 183 | finished(); |
8596 | 183 | } |
8597 | | |
8598 | | // we might be finished already, in which case we should |
8599 | | // not switch to downloading mode. If all files are |
8600 | | // filtered, we're finished when we start. |
8601 | 920 | if (m_state != torrent_status::finished |
8602 | 920 | && m_state != torrent_status::seeding |
8603 | 920 | && !m_seed_mode) |
8604 | 367 | { |
8605 | 367 | set_state(torrent_status::downloading); |
8606 | 367 | } |
8607 | | |
8608 | 920 | INVARIANT_CHECK; |
8609 | | |
8610 | 920 | if (m_ses.alerts().should_post<torrent_checked_alert>()) |
8611 | 2 | { |
8612 | 2 | m_ses.alerts().emplace_alert<torrent_checked_alert>( |
8613 | 2 | get_handle()); |
8614 | 2 | } |
8615 | | |
8616 | 920 | #ifndef TORRENT_DISABLE_EXTENSIONS |
8617 | 920 | for (auto& ext : m_extensions) |
8618 | 6 | { |
8619 | 6 | ext->on_files_checked(); |
8620 | 6 | } |
8621 | 920 | #endif |
8622 | | |
8623 | 920 | bool const notify_initialized = !m_connections_initialized; |
8624 | 920 | m_connections_initialized = true; |
8625 | 920 | m_files_checked = true; |
8626 | | |
8627 | 920 | update_want_tick(); |
8628 | | |
8629 | 920 | for (auto pc : m_connections) |
8630 | 0 | { |
8631 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
8632 | | // all peer connections have to initialize themselves now that the metadata |
8633 | | // is available |
8634 | 0 | if (notify_initialized) |
8635 | 0 | { |
8636 | 0 | if (pc->is_disconnecting()) continue; |
8637 | 0 | pc->on_metadata_impl(); |
8638 | 0 | if (pc->is_disconnecting()) continue; |
8639 | 0 | pc->init(); |
8640 | 0 | } |
8641 | | |
8642 | | #ifndef TORRENT_DISABLE_LOGGING |
8643 | | pc->peer_log(peer_log_alert::info, "ON_FILES_CHECKED"); |
8644 | | #endif |
8645 | 0 | if (pc->is_interesting() && !pc->has_peer_choked()) |
8646 | 0 | { |
8647 | 0 | if (request_a_block(*this, *pc)) |
8648 | 0 | { |
8649 | 0 | inc_stats_counter(counters::unchoke_piece_picks); |
8650 | 0 | pc->send_block_requests(); |
8651 | 0 | } |
8652 | 0 | } |
8653 | 0 | } |
8654 | | |
8655 | 920 | start_announcing(); |
8656 | | |
8657 | 920 | maybe_connect_web_seeds(); |
8658 | 920 | } |
8659 | | |
8660 | | aux::alert_manager& torrent::alerts() const |
8661 | 58.4k | { |
8662 | 58.4k | TORRENT_ASSERT(is_single_thread()); |
8663 | 58.4k | return m_ses.alerts(); |
8664 | 58.4k | } |
8665 | | |
8666 | | bool torrent::is_seed() const |
8667 | 29.2k | { |
8668 | 29.2k | if (!valid_metadata()) return false; |
8669 | 29.2k | if (m_seed_mode) return true; |
8670 | 25.2k | if (m_have_all) return true; |
8671 | 25.0k | if (m_picker && m_picker->is_seeding()) return true; |
8672 | 24.8k | return m_state == torrent_status::seeding; |
8673 | 25.0k | } |
8674 | | |
8675 | | bool torrent::is_finished() const |
8676 | 20.3k | { |
8677 | 20.3k | if (is_seed()) return true; |
8678 | 16.9k | return valid_metadata() && has_picker() && m_picker->is_finished(); |
8679 | 20.3k | } |
8680 | | |
8681 | | bool torrent::is_inactive() const |
8682 | 2 | { |
8683 | 2 | if (!settings().get_bool(settings_pack::dont_count_slow_torrents)) |
8684 | 0 | return false; |
8685 | 2 | return m_inactive; |
8686 | 2 | } |
8687 | | |
8688 | | std::string torrent::save_path() const |
8689 | 0 | { |
8690 | 0 | return m_save_path; |
8691 | 0 | } |
8692 | | |
8693 | | void torrent::rename_file(file_index_t const index, std::string name) |
8694 | 0 | { |
8695 | 0 | INVARIANT_CHECK; |
8696 | |
|
8697 | 0 | file_storage const& fs = m_torrent_file->files(); |
8698 | 0 | TORRENT_ASSERT(index >= file_index_t(0)); |
8699 | 0 | TORRENT_ASSERT(index < fs.end_file()); |
8700 | 0 | TORRENT_UNUSED(fs); |
8701 | | |
8702 | | // storage may be nullptr during shutdown |
8703 | 0 | if (!m_storage) |
8704 | 0 | { |
8705 | 0 | if (alerts().should_post<file_rename_failed_alert>()) |
8706 | 0 | alerts().emplace_alert<file_rename_failed_alert>(get_handle() |
8707 | 0 | , index, errors::session_is_closing); |
8708 | 0 | return; |
8709 | 0 | } |
8710 | | |
8711 | 0 | m_ses.disk_thread().async_rename_file(m_storage, index, std::move(name) |
8712 | 0 | , std::bind(&torrent::on_file_renamed, shared_from_this(), _1, _2, _3)); |
8713 | 0 | m_ses.deferred_submit_jobs(); |
8714 | 0 | } |
8715 | | |
8716 | | void torrent::move_storage(std::string const& save_path, move_flags_t const flags) |
8717 | 0 | { |
8718 | 0 | TORRENT_ASSERT(is_single_thread()); |
8719 | 0 | INVARIANT_CHECK; |
8720 | |
|
8721 | 0 | if (m_abort) |
8722 | 0 | { |
8723 | 0 | if (alerts().should_post<storage_moved_failed_alert>()) |
8724 | 0 | alerts().emplace_alert<storage_moved_failed_alert>(get_handle() |
8725 | 0 | , boost::asio::error::operation_aborted |
8726 | 0 | , "", operation_t::unknown); |
8727 | 0 | return; |
8728 | 0 | } |
8729 | | |
8730 | | // if we don't have metadata yet, we don't know anything about the file |
8731 | | // structure and we have to assume we don't have any file. |
8732 | 0 | if (!valid_metadata()) |
8733 | 0 | { |
8734 | 0 | if (alerts().should_post<storage_moved_alert>()) |
8735 | 0 | alerts().emplace_alert<storage_moved_alert>(get_handle(), save_path, m_save_path); |
8736 | | #if TORRENT_USE_UNC_PATHS |
8737 | | std::string path = canonicalize_path(save_path); |
8738 | | #else |
8739 | 0 | std::string const& path = save_path; |
8740 | 0 | #endif |
8741 | 0 | m_save_path = complete(path); |
8742 | 0 | return; |
8743 | 0 | } |
8744 | | |
8745 | | // storage may be nullptr during shutdown |
8746 | 0 | if (m_storage) |
8747 | 0 | { |
8748 | | #if TORRENT_USE_UNC_PATHS |
8749 | | std::string path = canonicalize_path(save_path); |
8750 | | #else |
8751 | 0 | std::string path = save_path; |
8752 | 0 | #endif |
8753 | 0 | m_ses.disk_thread().async_move_storage(m_storage, std::move(path), flags |
8754 | 0 | , std::bind(&torrent::on_storage_moved, shared_from_this(), _1, _2, _3)); |
8755 | 0 | m_moving_storage = true; |
8756 | 0 | m_ses.deferred_submit_jobs(); |
8757 | 0 | } |
8758 | 0 | else |
8759 | 0 | { |
8760 | 0 | if (alerts().should_post<storage_moved_alert>()) |
8761 | 0 | alerts().emplace_alert<storage_moved_alert>(get_handle(), save_path, m_save_path); |
8762 | |
|
8763 | | #if TORRENT_USE_UNC_PATHS |
8764 | | m_save_path = canonicalize_path(save_path); |
8765 | | #else |
8766 | |
|
8767 | 0 | m_save_path = save_path; |
8768 | 0 | #endif |
8769 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
8770 | 0 | } |
8771 | 0 | } |
8772 | | |
8773 | | void torrent::on_storage_moved(status_t const status, std::string const& path |
8774 | 0 | , storage_error const& error) try |
8775 | 0 | { |
8776 | 0 | TORRENT_ASSERT(is_single_thread()); |
8777 | |
|
8778 | 0 | m_moving_storage = false; |
8779 | 0 | if (status == status_t::no_error |
8780 | 0 | || status == status_t::need_full_check) |
8781 | 0 | { |
8782 | 0 | if (alerts().should_post<storage_moved_alert>()) |
8783 | 0 | alerts().emplace_alert<storage_moved_alert>(get_handle(), path, m_save_path); |
8784 | 0 | m_save_path = path; |
8785 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
8786 | 0 | if (status == status_t::need_full_check) |
8787 | 0 | force_recheck(); |
8788 | 0 | } |
8789 | 0 | else |
8790 | 0 | { |
8791 | 0 | if (alerts().should_post<storage_moved_failed_alert>()) |
8792 | 0 | alerts().emplace_alert<storage_moved_failed_alert>(get_handle(), error.ec |
8793 | 0 | , resolve_filename(error.file()), error.operation); |
8794 | 0 | } |
8795 | 0 | } |
8796 | 0 | catch (...) { handle_exception(); } |
8797 | | |
8798 | | torrent_handle torrent::get_handle() |
8799 | 32.2k | { |
8800 | 32.2k | TORRENT_ASSERT(is_single_thread()); |
8801 | 32.2k | return torrent_handle(shared_from_this()); |
8802 | 32.2k | } |
8803 | | |
8804 | | aux::session_settings const& torrent::settings() const |
8805 | 15.9k | { |
8806 | 15.9k | TORRENT_ASSERT(is_single_thread()); |
8807 | 15.9k | return m_ses.settings(); |
8808 | 15.9k | } |
8809 | | |
8810 | | #if TORRENT_USE_INVARIANT_CHECKS |
8811 | | void torrent::check_invariant() const |
8812 | | { |
8813 | | TORRENT_ASSERT(m_connections.size() >= m_outgoing_pids.size()); |
8814 | | |
8815 | | // the piece picker and the file progress states are supposed to be |
8816 | | // created in sync |
8817 | | TORRENT_ASSERT(has_picker() == !m_file_progress.empty()); |
8818 | | TORRENT_ASSERT(current_stats_state() == int(m_current_gauge_state + counters::num_checking_torrents) |
8819 | | || m_current_gauge_state == no_gauge_state); |
8820 | | |
8821 | | TORRENT_ASSERT(m_sequence_number == no_pos |
8822 | | || m_ses.verify_queue_position(this, m_sequence_number)); |
8823 | | |
8824 | | #ifndef TORRENT_DISABLE_STREAMING |
8825 | | for (auto const& i : m_time_critical_pieces) |
8826 | | { |
8827 | | TORRENT_ASSERT(!is_seed()); |
8828 | | TORRENT_ASSERT(!has_picker() || !m_picker->have_piece(i.piece)); |
8829 | | } |
8830 | | #endif |
8831 | | |
8832 | | switch (current_stats_state()) |
8833 | | { |
8834 | | case counters::num_error_torrents: TORRENT_ASSERT(has_error()); break; |
8835 | | case counters::num_checking_torrents: |
8836 | | #if TORRENT_ABI_VERSION == 1 |
8837 | | TORRENT_ASSERT(state() == torrent_status::checking_files |
8838 | | || state() == torrent_status::queued_for_checking); |
8839 | | #else |
8840 | | TORRENT_ASSERT(state() == torrent_status::checking_files); |
8841 | | #endif |
8842 | | break; |
8843 | | case counters::num_seeding_torrents: TORRENT_ASSERT(is_seed()); break; |
8844 | | case counters::num_upload_only_torrents: TORRENT_ASSERT(is_upload_only()); break; |
8845 | | case counters::num_stopped_torrents: TORRENT_ASSERT(!is_auto_managed() |
8846 | | && (m_paused || m_graceful_pause_mode)); |
8847 | | break; |
8848 | | case counters::num_queued_seeding_torrents: |
8849 | | TORRENT_ASSERT((m_paused || m_graceful_pause_mode) && is_seed()); break; |
8850 | | } |
8851 | | |
8852 | | if (m_torrent_file) |
8853 | | { |
8854 | | TORRENT_ASSERT(m_info_hash.v1 == m_torrent_file->info_hashes().v1); |
8855 | | TORRENT_ASSERT(m_info_hash.v2 == m_torrent_file->info_hashes().v2); |
8856 | | } |
8857 | | |
8858 | | for (torrent_list_index_t i{}; i != m_links.end_index(); ++i) |
8859 | | { |
8860 | | if (!m_links[i].in_list()) continue; |
8861 | | int const index = m_links[i].index; |
8862 | | |
8863 | | TORRENT_ASSERT(index >= 0); |
8864 | | TORRENT_ASSERT(index < int(m_ses.torrent_list(i).size())); |
8865 | | } |
8866 | | |
8867 | | TORRENT_ASSERT(want_peers_download() == m_links[aux::session_interface::torrent_want_peers_download].in_list()); |
8868 | | TORRENT_ASSERT(want_peers_finished() == m_links[aux::session_interface::torrent_want_peers_finished].in_list()); |
8869 | | TORRENT_ASSERT(want_tick() == m_links[aux::session_interface::torrent_want_tick].in_list()); |
8870 | | TORRENT_ASSERT((m_paused && m_auto_managed && !m_abort) == m_links[aux::session_interface::torrent_want_scrape].in_list()); |
8871 | | |
8872 | | bool is_checking = false; |
8873 | | bool is_downloading = false; |
8874 | | bool is_seeding = false; |
8875 | | |
8876 | | if (is_auto_managed() && !has_error()) |
8877 | | { |
8878 | | if (m_state == torrent_status::checking_files) |
8879 | | { |
8880 | | is_checking = true; |
8881 | | } |
8882 | | else if (m_state == torrent_status::downloading_metadata |
8883 | | || m_state == torrent_status::downloading |
8884 | | || m_state == torrent_status::finished |
8885 | | || m_state == torrent_status::seeding) |
8886 | | { |
8887 | | if (is_finished()) |
8888 | | is_seeding = true; |
8889 | | else |
8890 | | is_downloading = true; |
8891 | | } |
8892 | | } |
8893 | | |
8894 | | TORRENT_ASSERT(m_links[aux::session_interface::torrent_checking_auto_managed].in_list() |
8895 | | == is_checking); |
8896 | | TORRENT_ASSERT(m_links[aux::session_interface::torrent_downloading_auto_managed].in_list() |
8897 | | == is_downloading); |
8898 | | TORRENT_ASSERT(m_links[aux::session_interface::torrent_seeding_auto_managed].in_list() |
8899 | | == is_seeding); |
8900 | | |
8901 | | if (m_seed_mode) |
8902 | | { |
8903 | | TORRENT_ASSERT(is_seed()); |
8904 | | } |
8905 | | |
8906 | | TORRENT_ASSERT(is_single_thread()); |
8907 | | // this fires during disconnecting peers |
8908 | | if (is_paused()) TORRENT_ASSERT(num_peers() == 0 || m_graceful_pause_mode); |
8909 | | |
8910 | | int seeds = 0; |
8911 | | int num_uploads = 0; |
8912 | | int num_connecting = 0; |
8913 | | int num_connecting_seeds = 0; |
8914 | | std::map<piece_block, int> num_requests; |
8915 | | for (peer_connection const* peer : *this) |
8916 | | { |
8917 | | peer_connection const& p = *peer; |
8918 | | |
8919 | | if (p.is_connecting()) ++num_connecting; |
8920 | | |
8921 | | if (p.is_connecting() && p.peer_info_struct()->seed) |
8922 | | ++num_connecting_seeds; |
8923 | | |
8924 | | if (p.peer_info_struct()) |
8925 | | { |
8926 | | if (p.peer_info_struct()->seed) |
8927 | | { |
8928 | | ++seeds; |
8929 | | } |
8930 | | else |
8931 | | { |
8932 | | TORRENT_ASSERT(!p.is_seed()); |
8933 | | } |
8934 | | } |
8935 | | |
8936 | | for (auto const& j : p.request_queue()) |
8937 | | { |
8938 | | if (!j.not_wanted && !j.timed_out) ++num_requests[j.block]; |
8939 | | } |
8940 | | |
8941 | | for (auto const& j : p.download_queue()) |
8942 | | { |
8943 | | if (!j.not_wanted && !j.timed_out) ++num_requests[j.block]; |
8944 | | } |
8945 | | |
8946 | | if (!p.is_choked() && !p.ignore_unchoke_slots()) ++num_uploads; |
8947 | | torrent* associated_torrent = p.associated_torrent().lock().get(); |
8948 | | if (associated_torrent != this && associated_torrent != nullptr) |
8949 | | TORRENT_ASSERT_FAIL(); |
8950 | | } |
8951 | | TORRENT_ASSERT_VAL(num_uploads == int(m_num_uploads), int(m_num_uploads) - num_uploads); |
8952 | | TORRENT_ASSERT_VAL(seeds == int(m_num_seeds), int(m_num_seeds) - seeds); |
8953 | | TORRENT_ASSERT_VAL(num_connecting == int(m_num_connecting), int(m_num_connecting) - num_connecting); |
8954 | | TORRENT_ASSERT_VAL(num_connecting_seeds == int(m_num_connecting_seeds) |
8955 | | , int(m_num_connecting_seeds) - num_connecting_seeds); |
8956 | | TORRENT_ASSERT_VAL(int(m_num_uploads) <= num_peers(), m_num_uploads - num_peers()); |
8957 | | TORRENT_ASSERT_VAL(int(m_num_seeds) <= num_peers(), m_num_seeds - num_peers()); |
8958 | | TORRENT_ASSERT_VAL(int(m_num_connecting) <= num_peers(), int(m_num_connecting) - num_peers()); |
8959 | | TORRENT_ASSERT_VAL(int(m_num_connecting_seeds) <= num_peers(), int(m_num_connecting_seeds) - num_peers()); |
8960 | | TORRENT_ASSERT_VAL(int(m_num_connecting) + int(m_num_seeds) >= int(m_num_connecting_seeds) |
8961 | | , int(m_num_connecting_seeds) - (int(m_num_connecting) + int(m_num_seeds))); |
8962 | | TORRENT_ASSERT_VAL(int(m_num_connecting) + int(m_num_seeds) - int(m_num_connecting_seeds) <= num_peers() |
8963 | | , num_peers() - (int(m_num_connecting) + int(m_num_seeds) - int(m_num_connecting_seeds))); |
8964 | | |
8965 | | if (has_picker()) |
8966 | | { |
8967 | | for (std::map<piece_block, int>::iterator i = num_requests.begin() |
8968 | | , end(num_requests.end()); i != end; ++i) |
8969 | | { |
8970 | | piece_block b = i->first; |
8971 | | int count = i->second; |
8972 | | int picker_count = m_picker->num_peers(b); |
8973 | | // if we're no longer downloading the piece |
8974 | | // (for instance, it may be fully downloaded and waiting |
8975 | | // for the hash check to return), the piece picker always |
8976 | | // returns 0 requests, regardless of how many peers may still |
8977 | | // have the block in their queue |
8978 | | if (!m_picker->is_downloaded(b) && m_picker->is_downloading(b.piece_index)) |
8979 | | { |
8980 | | if (picker_count != count) |
8981 | | { |
8982 | | std::fprintf(stderr, "picker count discrepancy: " |
8983 | | "picker: %d != peerlist: %d\n", picker_count, count); |
8984 | | |
8985 | | for (const_peer_iterator j = this->begin(); j != this->end(); ++j) |
8986 | | { |
8987 | | peer_connection const& p = *(*j); |
8988 | | std::fprintf(stderr, "peer: %s\n", print_endpoint(p.remote()).c_str()); |
8989 | | for (auto const& k : p.request_queue()) |
8990 | | { |
8991 | | std::fprintf(stderr, " rq: (%d, %d) %s %s %s\n" |
8992 | | , static_cast<int>(k.block.piece_index) |
8993 | | , k.block.block_index, k.not_wanted ? "not-wanted" : "" |
8994 | | , k.timed_out ? "timed-out" : "", k.busy ? "busy": ""); |
8995 | | } |
8996 | | for (auto const& k : p.download_queue()) |
8997 | | { |
8998 | | std::fprintf(stderr, " dq: (%d, %d) %s %s %s\n" |
8999 | | , static_cast<int>(k.block.piece_index) |
9000 | | , k.block.block_index, k.not_wanted ? "not-wanted" : "" |
9001 | | , k.timed_out ? "timed-out" : "", k.busy ? "busy": ""); |
9002 | | } |
9003 | | } |
9004 | | TORRENT_ASSERT_FAIL(); |
9005 | | } |
9006 | | } |
9007 | | } |
9008 | | } |
9009 | | |
9010 | | if (valid_metadata()) |
9011 | | { |
9012 | | TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == m_torrent_file->num_pieces()); |
9013 | | } |
9014 | | else |
9015 | | { |
9016 | | TORRENT_ASSERT(m_abort || m_error || !m_picker || m_picker->num_pieces() == 0); |
9017 | | } |
9018 | | |
9019 | | #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS |
9020 | | // make sure we haven't modified the peer object |
9021 | | // in a way that breaks the sort order |
9022 | | if (m_peer_list && m_peer_list->begin() != m_peer_list->end()) |
9023 | | { |
9024 | | auto i = m_peer_list->begin(); |
9025 | | auto p = i++; |
9026 | | auto end(m_peer_list->end()); |
9027 | | peer_address_compare cmp; |
9028 | | for (; i != end; ++i, ++p) |
9029 | | { |
9030 | | TORRENT_ASSERT(!cmp(*i, *p)); |
9031 | | } |
9032 | | } |
9033 | | #endif |
9034 | | |
9035 | | /* |
9036 | | if (m_picker && !m_abort) |
9037 | | { |
9038 | | // make sure that pieces that have completed the download |
9039 | | // of all their blocks are in the disk io thread's queue |
9040 | | // to be checked. |
9041 | | std::vector<piece_picker::downloading_piece> dl_queue |
9042 | | = m_picker->get_download_queue(); |
9043 | | for (std::vector<piece_picker::downloading_piece>::const_iterator i = |
9044 | | dl_queue.begin(); i != dl_queue.end(); ++i) |
9045 | | { |
9046 | | const int blocks_per_piece = m_picker->blocks_in_piece(i->index); |
9047 | | |
9048 | | bool complete = true; |
9049 | | for (int j = 0; j < blocks_per_piece; ++j) |
9050 | | { |
9051 | | if (i->info[j].state == piece_picker::block_info::state_finished) |
9052 | | continue; |
9053 | | complete = false; |
9054 | | break; |
9055 | | } |
9056 | | TORRENT_ASSERT(complete); |
9057 | | } |
9058 | | } |
9059 | | */ |
9060 | | if (m_files_checked && valid_metadata()) |
9061 | | { |
9062 | | TORRENT_ASSERT(block_size() > 0); |
9063 | | } |
9064 | | } |
9065 | | #endif |
9066 | | |
9067 | | void torrent::set_sequential_download(bool const sd) |
9068 | 0 | { |
9069 | 0 | TORRENT_ASSERT(is_single_thread()); |
9070 | 0 | if (m_sequential_download == sd) return; |
9071 | 0 | m_sequential_download = sd; |
9072 | | #ifndef TORRENT_DISABLE_LOGGING |
9073 | | debug_log("*** set-sequential-download: %d", sd); |
9074 | | #endif |
9075 | |
|
9076 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
9077 | |
|
9078 | 0 | state_updated(); |
9079 | 0 | } |
9080 | | |
9081 | | void torrent::queue_up() |
9082 | 0 | { |
9083 | | // finished torrents may not change their queue positions, as it's set to |
9084 | | // -1 |
9085 | 0 | if (m_abort || is_finished()) return; |
9086 | | |
9087 | 0 | set_queue_position(queue_position() == queue_position_t{0} |
9088 | 0 | ? queue_position() : prev(queue_position())); |
9089 | 0 | } |
9090 | | |
9091 | | void torrent::queue_down() |
9092 | 0 | { |
9093 | 0 | set_queue_position(next(queue_position())); |
9094 | 0 | } |
9095 | | |
9096 | | void torrent::set_queue_position(queue_position_t const p) |
9097 | 2.43k | { |
9098 | 2.43k | TORRENT_ASSERT(is_single_thread()); |
9099 | | |
9100 | | // finished torrents may not change their queue positions, as it's set to |
9101 | | // -1 |
9102 | 2.43k | if ((m_abort || is_finished()) && p != no_pos) return; |
9103 | | |
9104 | 2.10k | TORRENT_ASSERT((p == no_pos) == is_finished() |
9105 | 2.10k | || (!m_auto_managed && p == no_pos) |
9106 | 2.10k | || (m_abort && p == no_pos) |
9107 | 2.10k | || (!m_added && p == no_pos)); |
9108 | 2.10k | if (p == m_sequence_number) return; |
9109 | | |
9110 | 1.94k | TORRENT_ASSERT(p >= no_pos); |
9111 | | |
9112 | 1.94k | state_updated(); |
9113 | | |
9114 | 1.94k | m_ses.set_queue_position(this, p); |
9115 | 1.94k | } |
9116 | | |
9117 | | void torrent::set_max_uploads(int limit, bool const state_update) |
9118 | 1.88k | { |
9119 | 1.88k | TORRENT_ASSERT(is_single_thread()); |
9120 | | // TODO: perhaps 0 should actually mean 0 |
9121 | 1.88k | if (limit <= 0) limit = (1 << 24) - 1; |
9122 | 1.88k | if (int(m_max_uploads) == limit) return; |
9123 | 397 | if (state_update) state_updated(); |
9124 | 397 | m_max_uploads = aux::numeric_cast<std::uint32_t>(limit); |
9125 | | #ifndef TORRENT_DISABLE_LOGGING |
9126 | | if (should_log() && state_update) |
9127 | | debug_log("*** set-max-uploads: %d", m_max_uploads); |
9128 | | #endif |
9129 | | |
9130 | 397 | if (state_update) |
9131 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
9132 | 397 | } |
9133 | | |
9134 | | void torrent::set_max_connections(int limit, bool const state_update) |
9135 | 1.88k | { |
9136 | 1.88k | TORRENT_ASSERT(is_single_thread()); |
9137 | | // TODO: perhaps 0 should actually mean 0 |
9138 | 1.88k | if (limit <= 0) limit = (1 << 24) - 1; |
9139 | 1.88k | if (int(m_max_connections) == limit) return; |
9140 | 283 | if (state_update) state_updated(); |
9141 | 283 | m_max_connections = aux::numeric_cast<std::uint32_t>(limit); |
9142 | 283 | update_want_peers(); |
9143 | | |
9144 | | #ifndef TORRENT_DISABLE_LOGGING |
9145 | | if (should_log() && state_update) |
9146 | | debug_log("*** set-max-connections: %d", m_max_connections); |
9147 | | #endif |
9148 | | |
9149 | 283 | if (num_peers() > int(m_max_connections)) |
9150 | 0 | { |
9151 | 0 | disconnect_peers(num_peers() - m_max_connections |
9152 | 0 | , errors::too_many_connections); |
9153 | 0 | } |
9154 | | |
9155 | 283 | if (state_update) |
9156 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
9157 | 283 | } |
9158 | | |
9159 | | void torrent::set_upload_limit(int const limit) |
9160 | 0 | { |
9161 | 0 | set_limit_impl(limit, peer_connection::upload_channel); |
9162 | | #ifndef TORRENT_DISABLE_LOGGING |
9163 | | debug_log("*** set-upload-limit: %d", limit); |
9164 | | #endif |
9165 | 0 | } |
9166 | | |
9167 | | void torrent::set_download_limit(int const limit) |
9168 | 0 | { |
9169 | 0 | set_limit_impl(limit, peer_connection::download_channel); |
9170 | | #ifndef TORRENT_DISABLE_LOGGING |
9171 | | debug_log("*** set-download-limit: %d", limit); |
9172 | | #endif |
9173 | 0 | } |
9174 | | |
9175 | | void torrent::set_limit_impl(int limit, int const channel, bool const state_update) |
9176 | 3.76k | { |
9177 | 3.76k | TORRENT_ASSERT(is_single_thread()); |
9178 | 3.76k | if (limit <= 0 || limit == aux::bandwidth_channel::inf) limit = 0; |
9179 | | |
9180 | 3.76k | if (m_peer_class == peer_class_t{0}) |
9181 | 3.51k | { |
9182 | 3.51k | if (limit == 0) return; |
9183 | 351 | setup_peer_class(); |
9184 | 351 | } |
9185 | | |
9186 | 597 | struct peer_class* tpc = m_ses.peer_classes().at(m_peer_class); |
9187 | 597 | TORRENT_ASSERT(tpc); |
9188 | 597 | if (tpc->channel[channel].throttle() == limit) return; |
9189 | 471 | if (state_update) |
9190 | 0 | { |
9191 | 0 | state_updated(); |
9192 | 0 | set_need_save_resume(torrent_handle::if_config_changed); |
9193 | 0 | } |
9194 | 471 | tpc->channel[channel].throttle(limit); |
9195 | 471 | } |
9196 | | |
9197 | | void torrent::setup_peer_class() |
9198 | 351 | { |
9199 | 351 | TORRENT_ASSERT(m_peer_class == peer_class_t{0}); |
9200 | 351 | m_peer_class = m_ses.peer_classes().new_peer_class(name()); |
9201 | 351 | add_class(m_ses.peer_classes(), m_peer_class); |
9202 | 351 | } |
9203 | | |
9204 | | int torrent::limit_impl(int const channel) const |
9205 | 6 | { |
9206 | 6 | TORRENT_ASSERT(is_single_thread()); |
9207 | | |
9208 | 6 | if (m_peer_class == peer_class_t{0}) return -1; |
9209 | 0 | int limit = m_ses.peer_classes().at(m_peer_class)->channel[channel].throttle(); |
9210 | 0 | if (limit == std::numeric_limits<int>::max()) limit = -1; |
9211 | 0 | return limit; |
9212 | 6 | } |
9213 | | |
9214 | | int torrent::upload_limit() const |
9215 | 3 | { |
9216 | 3 | return limit_impl(peer_connection::upload_channel); |
9217 | 3 | } |
9218 | | |
9219 | | int torrent::download_limit() const |
9220 | 3 | { |
9221 | 3 | return limit_impl(peer_connection::download_channel); |
9222 | 3 | } |
9223 | | |
9224 | | bool torrent::delete_files(remove_flags_t const options) |
9225 | 0 | { |
9226 | 0 | TORRENT_ASSERT(is_single_thread()); |
9227 | |
|
9228 | | #ifndef TORRENT_DISABLE_LOGGING |
9229 | | log_to_all_peers("deleting files"); |
9230 | | #endif |
9231 | |
|
9232 | 0 | disconnect_all(errors::torrent_removed, operation_t::bittorrent); |
9233 | 0 | stop_announcing(); |
9234 | | |
9235 | | // storage may be nullptr during shutdown |
9236 | 0 | if (m_storage) |
9237 | 0 | { |
9238 | 0 | TORRENT_ASSERT(m_storage); |
9239 | 0 | m_ses.disk_thread().async_delete_files(m_storage, options |
9240 | 0 | , std::bind(&torrent::on_files_deleted, shared_from_this(), _1)); |
9241 | 0 | m_deleted = true; |
9242 | 0 | m_ses.deferred_submit_jobs(); |
9243 | 0 | return true; |
9244 | 0 | } |
9245 | 0 | return false; |
9246 | 0 | } |
9247 | | |
9248 | | void torrent::clear_error() |
9249 | 2 | { |
9250 | 2 | TORRENT_ASSERT(is_single_thread()); |
9251 | 2 | if (!m_error) return; |
9252 | 0 | bool const checking_files = should_check_files(); |
9253 | 0 | m_ses.trigger_auto_manage(); |
9254 | 0 | m_error.clear(); |
9255 | 0 | m_error_file = torrent_status::error_file_none; |
9256 | |
|
9257 | 0 | update_gauge(); |
9258 | 0 | state_updated(); |
9259 | 0 | update_want_peers(); |
9260 | 0 | update_state_list(); |
9261 | | |
9262 | | // if the error happened during initialization, try again now |
9263 | 0 | if (!m_torrent_initialized && valid_metadata()) init(); |
9264 | 0 | if (!checking_files && should_check_files()) |
9265 | 0 | start_checking(); |
9266 | 0 | } |
9267 | | std::string torrent::resolve_filename(file_index_t const file) const |
9268 | 0 | { |
9269 | 0 | if (file == torrent_status::error_file_none) return ""; |
9270 | 0 | if (file == torrent_status::error_file_ssl_ctx) return "SSL Context"; |
9271 | 0 | if (file == torrent_status::error_file_exception) return "exception"; |
9272 | 0 | if (file == torrent_status::error_file_partfile) return "partfile"; |
9273 | 0 | if (file == torrent_status::error_file_metadata) return "metadata"; |
9274 | | |
9275 | 0 | if (m_storage && file >= file_index_t(0)) |
9276 | 0 | { |
9277 | 0 | file_storage const& st = m_torrent_file->files(); |
9278 | 0 | return st.file_path(file, m_save_path); |
9279 | 0 | } |
9280 | 0 | else |
9281 | 0 | { |
9282 | 0 | return m_save_path; |
9283 | 0 | } |
9284 | 0 | } |
9285 | | |
9286 | | void torrent::set_error(error_code const& ec, file_index_t const error_file) |
9287 | 0 | { |
9288 | 0 | TORRENT_ASSERT(is_single_thread()); |
9289 | 0 | m_error = ec; |
9290 | 0 | m_error_file = error_file; |
9291 | |
|
9292 | 0 | update_gauge(); |
9293 | |
|
9294 | 0 | if (alerts().should_post<torrent_error_alert>()) |
9295 | 0 | alerts().emplace_alert<torrent_error_alert>(get_handle(), ec |
9296 | 0 | , resolve_filename(error_file)); |
9297 | |
|
9298 | | #ifndef TORRENT_DISABLE_LOGGING |
9299 | | if (ec) |
9300 | | { |
9301 | | char buf[1024]; |
9302 | | std::snprintf(buf, sizeof(buf), "error %s: %s", ec.message().c_str() |
9303 | | , resolve_filename(error_file).c_str()); |
9304 | | log_to_all_peers(buf); |
9305 | | } |
9306 | | #endif |
9307 | |
|
9308 | 0 | state_updated(); |
9309 | 0 | update_state_list(); |
9310 | 0 | } |
9311 | | |
9312 | | void torrent::auto_managed(bool a) |
9313 | 446 | { |
9314 | 446 | TORRENT_ASSERT(is_single_thread()); |
9315 | 446 | INVARIANT_CHECK; |
9316 | | |
9317 | 446 | if (m_auto_managed == a) return; |
9318 | 178 | bool const checking_files = should_check_files(); |
9319 | 178 | m_auto_managed = a; |
9320 | 178 | update_gauge(); |
9321 | 178 | update_want_scrape(); |
9322 | 178 | update_state_list(); |
9323 | | |
9324 | 178 | state_updated(); |
9325 | | |
9326 | | // we need to save this new state as well |
9327 | 178 | set_need_save_resume(torrent_handle::if_config_changed); |
9328 | | |
9329 | | // recalculate which torrents should be |
9330 | | // paused |
9331 | 178 | m_ses.trigger_auto_manage(); |
9332 | | |
9333 | 178 | if (!checking_files && should_check_files()) |
9334 | 0 | { |
9335 | 0 | start_checking(); |
9336 | 0 | } |
9337 | 178 | } |
9338 | | |
9339 | | namespace { |
9340 | | |
9341 | | std::uint16_t clamped_subtract_u16(int const a, int const b) |
9342 | 0 | { |
9343 | 0 | if (a < b) return 0; |
9344 | 0 | return std::uint16_t(a - b); |
9345 | 0 | } |
9346 | | |
9347 | | } // anonymous namespace |
9348 | | |
9349 | | // this is called every time the session timer takes a step back. Since the |
9350 | | // session time is meant to fit in 16 bits, it only covers a range of |
9351 | | // about 18 hours. This means every few hours the whole epoch of this |
9352 | | // clock is shifted forward. All timestamp in this clock must then be |
9353 | | // shifted backwards to remain the same. Anything that's shifted back |
9354 | | // beyond the new epoch is clamped to 0 (to represent the oldest timestamp |
9355 | | // currently representable by the session_time) |
9356 | | void torrent::step_session_time(int const seconds) |
9357 | 0 | { |
9358 | 0 | if (m_peer_list) |
9359 | 0 | { |
9360 | 0 | for (auto pe : *m_peer_list) |
9361 | 0 | { |
9362 | 0 | pe->last_optimistically_unchoked |
9363 | 0 | = clamped_subtract_u16(pe->last_optimistically_unchoked, seconds); |
9364 | 0 | pe->last_connected = clamped_subtract_u16(pe->last_connected, seconds); |
9365 | 0 | } |
9366 | 0 | } |
9367 | 0 | } |
9368 | | |
9369 | | // the higher seed rank, the more important to seed |
9370 | | int torrent::seed_rank(aux::session_settings const& s) const |
9371 | 0 | { |
9372 | 0 | TORRENT_ASSERT(is_single_thread()); |
9373 | 0 | enum flags |
9374 | 0 | { |
9375 | 0 | seed_ratio_not_met = 0x40000000, |
9376 | 0 | no_seeds = 0x20000000, |
9377 | 0 | recently_started = 0x10000000, |
9378 | 0 | prio_mask = 0x0fffffff |
9379 | 0 | }; |
9380 | |
|
9381 | 0 | if (!is_finished()) return 0; |
9382 | | |
9383 | 0 | int scale = 1000; |
9384 | 0 | if (!is_seed()) scale = 500; |
9385 | |
|
9386 | 0 | int ret = 0; |
9387 | |
|
9388 | 0 | seconds32 const act_time = active_time(); |
9389 | 0 | seconds32 const fin_time = finished_time(); |
9390 | 0 | seconds32 const download_time = act_time - fin_time; |
9391 | | |
9392 | | // if we haven't yet met the seed limits, set the seed_ratio_not_met |
9393 | | // flag. That will make this seed prioritized |
9394 | | // downloaded may be 0 if the torrent is 0-sized |
9395 | 0 | std::int64_t const downloaded = std::max(m_total_downloaded, m_torrent_file->total_size()); |
9396 | 0 | if (fin_time < seconds(s.get_int(settings_pack::seed_time_limit)) |
9397 | 0 | && (download_time.count() > 1 |
9398 | 0 | && fin_time * 100 / download_time < s.get_int(settings_pack::seed_time_ratio_limit)) |
9399 | 0 | && downloaded > 0 |
9400 | 0 | && m_total_uploaded * 100 / downloaded < s.get_int(settings_pack::share_ratio_limit)) |
9401 | 0 | ret |= seed_ratio_not_met; |
9402 | | |
9403 | | // if this torrent is running, and it was started less |
9404 | | // than 30 minutes ago, give it priority, to avoid oscillation |
9405 | 0 | if (!is_paused() && act_time < minutes(30)) |
9406 | 0 | ret |= recently_started; |
9407 | | |
9408 | | // if we have any scrape data, use it to calculate |
9409 | | // seed rank |
9410 | 0 | int seeds = 0; |
9411 | 0 | int downloaders = 0; |
9412 | | |
9413 | | // If we're currently seeding and using tracker supplied scrape |
9414 | | // data, we should remove ourselves from the seed count |
9415 | 0 | int const self_seed = is_seed() && !is_paused() ? 1 : 0; |
9416 | |
|
9417 | 0 | if (m_complete != 0xffffff) seeds = std::max(0, int(m_complete) - self_seed); |
9418 | 0 | else seeds = m_peer_list ? m_peer_list->num_seeds() : 0; |
9419 | |
|
9420 | 0 | if (m_incomplete != 0xffffff) downloaders = m_incomplete; |
9421 | 0 | else downloaders = m_peer_list ? m_peer_list->num_peers() - m_peer_list->num_seeds() : 0; |
9422 | |
|
9423 | 0 | if (seeds == 0) |
9424 | 0 | { |
9425 | 0 | ret |= no_seeds; |
9426 | 0 | ret |= downloaders & prio_mask; |
9427 | 0 | } |
9428 | 0 | else |
9429 | 0 | { |
9430 | 0 | ret |= ((1 + downloaders) * scale / seeds) & prio_mask; |
9431 | 0 | } |
9432 | |
|
9433 | 0 | return ret; |
9434 | 0 | } |
9435 | | |
9436 | | // this is an async operation triggered by the client |
9437 | | void torrent::save_resume_data(resume_data_flags_t const flags) |
9438 | 0 | { |
9439 | 0 | TORRENT_ASSERT(is_single_thread()); |
9440 | 0 | INVARIANT_CHECK; |
9441 | |
|
9442 | 0 | if (m_abort) |
9443 | 0 | { |
9444 | 0 | alerts().emplace_alert<save_resume_data_failed_alert>(get_handle() |
9445 | 0 | , errors::torrent_removed); |
9446 | 0 | return; |
9447 | 0 | } |
9448 | | |
9449 | 0 | auto conditions = flags & ( |
9450 | 0 | torrent_handle::only_if_modified |
9451 | 0 | | torrent_handle::if_counters_changed |
9452 | 0 | | torrent_handle::if_download_progress |
9453 | 0 | | torrent_handle::if_config_changed |
9454 | 0 | | torrent_handle::if_state_changed |
9455 | 0 | | torrent_handle::if_metadata_changed |
9456 | 0 | ); |
9457 | |
|
9458 | 0 | if (conditions && !(m_need_save_resume_data & conditions)) |
9459 | 0 | { |
9460 | | // if conditions were specified, but none of those conditions are |
9461 | | // met (i.e. none of them have been updated since last |
9462 | | // save_resume_data()), we don't save it. |
9463 | 0 | alerts().emplace_alert<save_resume_data_failed_alert>(get_handle() |
9464 | 0 | , errors::resume_data_not_modified); |
9465 | 0 | return; |
9466 | 0 | } |
9467 | | |
9468 | 0 | m_need_save_resume_data = resume_data_flags_t{}; |
9469 | 0 | state_updated(); |
9470 | |
|
9471 | 0 | if ((flags & torrent_handle::flush_disk_cache) && m_storage) |
9472 | 0 | { |
9473 | 0 | m_ses.disk_thread().async_release_files(m_storage); |
9474 | 0 | m_ses.deferred_submit_jobs(); |
9475 | 0 | } |
9476 | |
|
9477 | 0 | state_updated(); |
9478 | |
|
9479 | 0 | add_torrent_params atp; |
9480 | 0 | write_resume_data(flags, atp); |
9481 | 0 | alerts().emplace_alert<save_resume_data_alert>(std::move(atp), get_handle()); |
9482 | 0 | } |
9483 | | |
9484 | | bool torrent::should_check_files() const |
9485 | 635 | { |
9486 | 635 | TORRENT_ASSERT(is_single_thread()); |
9487 | 635 | return m_state == torrent_status::checking_files |
9488 | 635 | && !m_paused |
9489 | 635 | && !has_error() |
9490 | 635 | && !m_abort |
9491 | 635 | && !m_session_paused; |
9492 | 635 | } |
9493 | | |
9494 | | void torrent::flush_cache() |
9495 | 0 | { |
9496 | 0 | TORRENT_ASSERT(is_single_thread()); |
9497 | | |
9498 | | // storage may be nullptr during shutdown |
9499 | 0 | if (!m_storage) |
9500 | 0 | { |
9501 | 0 | TORRENT_ASSERT(m_abort); |
9502 | 0 | return; |
9503 | 0 | } |
9504 | 0 | m_ses.disk_thread().async_release_files(m_storage |
9505 | 0 | , std::bind(&torrent::on_cache_flushed, shared_from_this(), true)); |
9506 | 0 | m_ses.deferred_submit_jobs(); |
9507 | 0 | } |
9508 | | |
9509 | 553 | void torrent::on_cache_flushed(bool const manually_triggered) try |
9510 | 553 | { |
9511 | 553 | TORRENT_ASSERT(is_single_thread()); |
9512 | | |
9513 | 553 | if (m_ses.is_aborted()) return; |
9514 | | |
9515 | 0 | if (manually_triggered || alerts().should_post<cache_flushed_alert>()) |
9516 | 0 | alerts().emplace_alert<cache_flushed_alert>(get_handle()); |
9517 | 0 | } |
9518 | 553 | catch (...) { handle_exception(); } |
9519 | | |
9520 | | void torrent::on_torrent_aborted() |
9521 | 1.88k | { |
9522 | 1.88k | TORRENT_ASSERT(is_single_thread()); |
9523 | | |
9524 | | // there should be no more disk activity for this torrent now, we can |
9525 | | // release the disk io handle |
9526 | 1.88k | m_storage.reset(); |
9527 | | |
9528 | 1.88k | alerts().emplace_alert<torrent_removed_alert>(get_handle() |
9529 | 1.88k | , info_hash(), get_userdata()); |
9530 | 1.88k | } |
9531 | | |
9532 | | bool torrent::is_paused() const |
9533 | 10.0k | { |
9534 | 10.0k | return m_paused || m_session_paused; |
9535 | 10.0k | } |
9536 | | |
9537 | | void torrent::pause(pause_flags_t const flags) |
9538 | 446 | { |
9539 | 446 | TORRENT_ASSERT(is_single_thread()); |
9540 | 446 | INVARIANT_CHECK; |
9541 | | |
9542 | 446 | if (!m_paused) |
9543 | 279 | { |
9544 | | // we need to save this new state |
9545 | 279 | set_need_save_resume(torrent_handle::if_state_changed); |
9546 | 279 | } |
9547 | | |
9548 | 446 | set_paused(true, flags); |
9549 | 446 | } |
9550 | | |
9551 | | void torrent::do_pause(bool const was_paused) |
9552 | 279 | { |
9553 | 279 | TORRENT_ASSERT(is_single_thread()); |
9554 | 279 | if (!is_paused()) return; |
9555 | | |
9556 | | // this torrent may be about to consider itself inactive. If so, we want |
9557 | | // to prevent it from doing so, since it's being paused unconditionally |
9558 | | // now. An illustrative example of this is a torrent that completes |
9559 | | // downloading when active_seeds = 0. It completes, it gets paused and it |
9560 | | // should not come back to life again. |
9561 | 279 | if (m_pending_active_change) |
9562 | 0 | { |
9563 | 0 | m_inactivity_timer.cancel(); |
9564 | 0 | } |
9565 | | |
9566 | 279 | #ifndef TORRENT_DISABLE_EXTENSIONS |
9567 | 279 | for (auto& ext : m_extensions) |
9568 | 0 | { |
9569 | 0 | if (ext->on_pause()) return; |
9570 | 0 | } |
9571 | 279 | #endif |
9572 | | |
9573 | 279 | m_connect_boost_counter |
9574 | 279 | = static_cast<std::uint8_t>(settings().get_int(settings_pack::torrent_connect_boost)); |
9575 | 279 | m_inactive = false; |
9576 | | |
9577 | 279 | update_state_list(); |
9578 | 279 | update_want_tick(); |
9579 | | |
9580 | | // do_paused() may be called twice, if the first time is to enter |
9581 | | // graceful pause, and the second time proper pause. We can only update |
9582 | | // these timers once, otherwise they'll be inflated |
9583 | 279 | if (!was_paused) |
9584 | 279 | { |
9585 | 279 | const time_point now = aux::time_now(); |
9586 | | |
9587 | 279 | m_active_time += |
9588 | 279 | duration_cast<seconds32>(now - m_started); |
9589 | | |
9590 | 279 | if (is_seed()) m_seeding_time += |
9591 | 49 | duration_cast<seconds32>(now - m_became_seed); |
9592 | | |
9593 | 279 | if (is_finished()) m_finished_time += |
9594 | 144 | duration_cast<seconds32>(now - m_became_finished); |
9595 | 279 | } |
9596 | | |
9597 | 279 | m_announce_to_dht = false; |
9598 | 279 | m_announce_to_trackers = false; |
9599 | 279 | m_announce_to_lsd = false; |
9600 | | |
9601 | 279 | state_updated(); |
9602 | 279 | update_want_peers(); |
9603 | 279 | update_want_scrape(); |
9604 | 279 | update_gauge(); |
9605 | 279 | update_state_list(); |
9606 | | |
9607 | | #ifndef TORRENT_DISABLE_LOGGING |
9608 | | log_to_all_peers("pausing"); |
9609 | | #endif |
9610 | | |
9611 | | // when checking and being paused in graceful pause mode, we |
9612 | | // post the paused alert when the last outstanding disk job completes |
9613 | 279 | if (m_state == torrent_status::checking_files) |
9614 | 0 | { |
9615 | 0 | if (m_checking_piece == m_num_checked_pieces) |
9616 | 0 | { |
9617 | 0 | if (alerts().should_post<torrent_paused_alert>()) |
9618 | 0 | alerts().emplace_alert<torrent_paused_alert>(get_handle()); |
9619 | 0 | } |
9620 | 0 | disconnect_all(errors::torrent_paused, operation_t::bittorrent); |
9621 | 0 | return; |
9622 | 0 | } |
9623 | | |
9624 | 279 | if (!m_graceful_pause_mode) |
9625 | 279 | { |
9626 | | // this will make the storage close all |
9627 | | // files and flush all cached data |
9628 | 279 | if (m_storage) |
9629 | 279 | { |
9630 | | // the torrent_paused alert will be posted from on_torrent_paused |
9631 | 279 | m_ses.disk_thread().async_stop_torrent(m_storage |
9632 | 279 | , [self = shared_from_this()] { self->on_torrent_paused(); }); |
9633 | 279 | m_ses.deferred_submit_jobs(); |
9634 | 279 | } |
9635 | 0 | else |
9636 | 0 | { |
9637 | 0 | if (alerts().should_post<torrent_paused_alert>()) |
9638 | 0 | alerts().emplace_alert<torrent_paused_alert>(get_handle()); |
9639 | 0 | } |
9640 | | |
9641 | 279 | disconnect_all(errors::torrent_paused, operation_t::bittorrent); |
9642 | 279 | } |
9643 | 0 | else |
9644 | 0 | { |
9645 | | // disconnect all peers with no outstanding data to receive |
9646 | | // and choke all remaining peers to prevent responding to new |
9647 | | // requests |
9648 | 0 | for (auto p : m_connections) |
9649 | 0 | { |
9650 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
9651 | 0 | TORRENT_ASSERT(p->associated_torrent().lock().get() == this); |
9652 | |
|
9653 | 0 | if (p->is_disconnecting()) continue; |
9654 | | |
9655 | 0 | if (p->outstanding_bytes() > 0) |
9656 | 0 | { |
9657 | | #ifndef TORRENT_DISABLE_LOGGING |
9658 | | p->peer_log(peer_log_alert::info, "CHOKING_PEER", "torrent graceful paused"); |
9659 | | #endif |
9660 | | // remove any un-sent requests from the queue |
9661 | 0 | p->clear_request_queue(); |
9662 | | // don't accept new requests from the peer |
9663 | 0 | p->choke_this_peer(); |
9664 | 0 | continue; |
9665 | 0 | } |
9666 | | |
9667 | | // since we're currently in graceful pause mode, the last peer to |
9668 | | // disconnect (assuming all peers end up begin disconnected here) |
9669 | | // will post the torrent_paused_alert |
9670 | | #ifndef TORRENT_DISABLE_LOGGING |
9671 | | p->peer_log(peer_log_alert::info, "CLOSING_CONNECTION", "torrent_paused"); |
9672 | | #endif |
9673 | 0 | p->disconnect(errors::torrent_paused, operation_t::bittorrent); |
9674 | 0 | } |
9675 | 0 | } |
9676 | | |
9677 | 279 | stop_announcing(); |
9678 | 279 | } |
9679 | | |
9680 | | #ifndef TORRENT_DISABLE_LOGGING |
9681 | | void torrent::log_to_all_peers(char const* message) |
9682 | | { |
9683 | | TORRENT_ASSERT(is_single_thread()); |
9684 | | |
9685 | | bool const log_peers = !m_connections.empty() |
9686 | | && m_connections.front()->should_log(peer_log_alert::info); |
9687 | | |
9688 | | if (log_peers) |
9689 | | { |
9690 | | for (auto const p : m_connections) |
9691 | | { |
9692 | | TORRENT_INCREMENT(m_iterating_connections); |
9693 | | p->peer_log(peer_log_alert::info, "TORRENT", "%s", message); |
9694 | | } |
9695 | | } |
9696 | | |
9697 | | debug_log("%s", message); |
9698 | | } |
9699 | | #endif |
9700 | | |
9701 | | // add or remove a url that will be attempted for |
9702 | | // finding the file(s) in this torrent. |
9703 | | web_seed_t* torrent::add_web_seed(std::string const& url |
9704 | | , web_seed_entry::type_t const type |
9705 | | , std::string const& auth |
9706 | | , web_seed_entry::headers_t const& extra_headers |
9707 | | , web_seed_flag_t const flags) |
9708 | 0 | { |
9709 | 0 | web_seed_t ent(url, type, auth, extra_headers); |
9710 | 0 | ent.ephemeral = bool(flags & ephemeral); |
9711 | 0 | ent.no_local_ips = bool(flags & no_local_ips); |
9712 | | |
9713 | | // don't add duplicates |
9714 | 0 | auto const it = std::find(m_web_seeds.begin(), m_web_seeds.end(), ent); |
9715 | 0 | if (it != m_web_seeds.end()) |
9716 | 0 | { |
9717 | | // if we're adding a web seed (as non-ephemeral) and we have an |
9718 | | // ephemeral web seed already, promote it to non-ephemeral |
9719 | 0 | if (it->ephemeral && !ent.ephemeral) |
9720 | 0 | { |
9721 | 0 | set_need_save_resume(torrent_handle::if_metadata_changed); |
9722 | 0 | it->ephemeral = false; |
9723 | 0 | } |
9724 | 0 | return &*it; |
9725 | 0 | } |
9726 | 0 | m_web_seeds.emplace_back(std::move(ent)); |
9727 | | |
9728 | | // ephemeral web seeds are not saved in the resume data |
9729 | 0 | if (!ent.ephemeral) |
9730 | 0 | set_need_save_resume(torrent_handle::if_metadata_changed); |
9731 | |
|
9732 | 0 | update_want_tick(); |
9733 | 0 | return &m_web_seeds.back(); |
9734 | 0 | } |
9735 | | |
9736 | | void torrent::set_session_paused(bool const b) |
9737 | 0 | { |
9738 | 0 | if (m_session_paused == b) return; |
9739 | 0 | bool const paused_before = is_paused(); |
9740 | 0 | m_session_paused = b; |
9741 | |
|
9742 | 0 | if (paused_before == is_paused()) return; |
9743 | | |
9744 | 0 | if (b) do_pause(); |
9745 | 0 | else do_resume(); |
9746 | 0 | } |
9747 | | |
9748 | | void torrent::set_paused(bool const b, pause_flags_t flags) |
9749 | 448 | { |
9750 | 448 | TORRENT_ASSERT(is_single_thread()); |
9751 | | |
9752 | | // if there are no peers, there is no point in a graceful pause mode. In |
9753 | | // fact, the promise to post the torrent_paused_alert exactly once is |
9754 | | // maintained by the last peer to be disconnected in graceful pause mode, |
9755 | | // if there are no peers, we must not enter graceful pause mode, and post |
9756 | | // the torrent_paused_alert immediately instead. |
9757 | 448 | if (num_peers() == 0) |
9758 | 448 | flags &= ~torrent_handle::graceful_pause; |
9759 | | |
9760 | 448 | if (m_paused == b) |
9761 | 167 | { |
9762 | | // there is one special case here. If we are |
9763 | | // currently in graceful pause mode, and we just turned into regular |
9764 | | // paused mode, we need to actually pause the torrent properly |
9765 | 167 | if (m_paused == true |
9766 | 167 | && m_graceful_pause_mode == true |
9767 | 167 | && !(flags & torrent_handle::graceful_pause)) |
9768 | 0 | { |
9769 | 0 | m_graceful_pause_mode = false; |
9770 | 0 | update_gauge(); |
9771 | 0 | do_pause(true); |
9772 | 0 | } |
9773 | 167 | return; |
9774 | 167 | } |
9775 | | |
9776 | 281 | bool const paused_before = is_paused(); |
9777 | | |
9778 | 281 | m_paused = b; |
9779 | | |
9780 | | // the session may still be paused, in which case |
9781 | | // the effective state of the torrent did not change |
9782 | 281 | if (paused_before == is_paused()) return; |
9783 | | |
9784 | 281 | m_graceful_pause_mode = bool(flags & torrent_handle::graceful_pause); |
9785 | | |
9786 | 281 | if (b) do_pause(); |
9787 | 2 | else do_resume(); |
9788 | 281 | } |
9789 | | |
9790 | | void torrent::resume() |
9791 | 0 | { |
9792 | 0 | TORRENT_ASSERT(is_single_thread()); |
9793 | 0 | INVARIANT_CHECK; |
9794 | |
|
9795 | 0 | if (!m_paused |
9796 | 0 | && m_announce_to_dht |
9797 | 0 | && m_announce_to_trackers |
9798 | 0 | && m_announce_to_lsd) return; |
9799 | | |
9800 | 0 | m_paused = false; |
9801 | 0 | if (!m_session_paused) m_graceful_pause_mode = false; |
9802 | |
|
9803 | 0 | update_gauge(); |
9804 | | |
9805 | | // we need to save this new state |
9806 | 0 | set_need_save_resume(torrent_handle::if_state_changed); |
9807 | |
|
9808 | 0 | do_resume(); |
9809 | 0 | } |
9810 | | |
9811 | | void torrent::do_resume() |
9812 | 2 | { |
9813 | 2 | TORRENT_ASSERT(is_single_thread()); |
9814 | 2 | if (is_paused()) |
9815 | 0 | { |
9816 | 0 | update_want_tick(); |
9817 | 0 | return; |
9818 | 0 | } |
9819 | | |
9820 | 2 | #ifndef TORRENT_DISABLE_EXTENSIONS |
9821 | 2 | for (auto& ext : m_extensions) |
9822 | 6 | { |
9823 | 6 | if (ext->on_resume()) return; |
9824 | 6 | } |
9825 | 2 | #endif |
9826 | | |
9827 | 2 | if (alerts().should_post<torrent_resumed_alert>()) |
9828 | 2 | alerts().emplace_alert<torrent_resumed_alert>(get_handle()); |
9829 | | |
9830 | 2 | m_announce_to_dht = true; |
9831 | 2 | m_announce_to_trackers = true; |
9832 | 2 | m_announce_to_lsd = true; |
9833 | | |
9834 | 2 | m_started = aux::time_now32(); |
9835 | 2 | if (is_seed()) m_became_seed = m_started; |
9836 | 2 | if (is_finished()) m_became_finished = m_started; |
9837 | | |
9838 | 2 | clear_error(); |
9839 | | |
9840 | 2 | if (m_state == torrent_status::checking_files |
9841 | 2 | && m_auto_managed) |
9842 | 0 | { |
9843 | 0 | m_ses.trigger_auto_manage(); |
9844 | 0 | } |
9845 | | |
9846 | 2 | if (should_check_files()) start_checking(); |
9847 | | |
9848 | 2 | state_updated(); |
9849 | 2 | update_want_peers(); |
9850 | 2 | update_want_tick(); |
9851 | 2 | update_want_scrape(); |
9852 | 2 | update_gauge(); |
9853 | | |
9854 | 2 | if (m_state == torrent_status::checking_files) return; |
9855 | | |
9856 | 2 | start_announcing(); |
9857 | | |
9858 | 2 | do_connect_boost(); |
9859 | 2 | } |
9860 | | |
9861 | | namespace |
9862 | | { |
9863 | | struct timer_state |
9864 | | { |
9865 | | explicit timer_state(aux::listen_socket_handle s) |
9866 | 0 | : socket(std::move(s)) {} |
9867 | | |
9868 | | aux::listen_socket_handle socket; |
9869 | | |
9870 | | struct state_t |
9871 | | { |
9872 | | int tier = INT_MAX; |
9873 | | bool found_working = false; |
9874 | | bool done = false; |
9875 | | }; |
9876 | | aux::array<state_t, num_protocols, protocol_version> state; |
9877 | | }; |
9878 | | } |
9879 | | |
9880 | | void torrent::update_tracker_timer(time_point32 const now) |
9881 | 0 | { |
9882 | 0 | TORRENT_ASSERT(is_single_thread()); |
9883 | 0 | if (!m_announcing) |
9884 | 0 | { |
9885 | | #ifndef TORRENT_DISABLE_LOGGING |
9886 | | debug_log("*** update tracker timer: not announcing"); |
9887 | | #endif |
9888 | 0 | return; |
9889 | 0 | } |
9890 | | |
9891 | 0 | #ifdef __clang__ |
9892 | 0 | #pragma clang diagnostic push |
9893 | 0 | #pragma clang diagnostic ignored "-Wmissing-braces" |
9894 | 0 | #endif |
9895 | 0 | aux::array<bool const, num_protocols, protocol_version> const supports_protocol{ |
9896 | 0 | { |
9897 | 0 | m_info_hash.has_v1(), |
9898 | 0 | m_info_hash.has_v2() |
9899 | 0 | }}; |
9900 | 0 | #ifdef __clang__ |
9901 | 0 | #pragma clang diagnostic pop |
9902 | 0 | #endif |
9903 | |
|
9904 | 0 | time_point32 next_announce = time_point32::max(); |
9905 | |
|
9906 | 0 | std::map<std::weak_ptr<aux::listen_socket_t>, timer_state, std::owner_less<std::weak_ptr<aux::listen_socket_t>>> listen_socket_states; |
9907 | |
|
9908 | 0 | bool const announce_to_all_tiers = settings().get_bool(settings_pack::announce_to_all_tiers); |
9909 | 0 | bool const announce_to_all_trackers = settings().get_bool(settings_pack::announce_to_all_trackers); |
9910 | | #ifndef TORRENT_DISABLE_LOGGING |
9911 | | int idx = -1; |
9912 | | if (should_log()) |
9913 | | { |
9914 | | debug_log("*** update_tracker_timer: " |
9915 | | "[ announce_to_all_tiers: %d announce_to_all_trackers: %d num_trackers: %d ]" |
9916 | | , announce_to_all_tiers |
9917 | | , announce_to_all_trackers |
9918 | | , int(m_trackers.size())); |
9919 | | } |
9920 | | #endif |
9921 | 0 | for (auto const& t : m_trackers) |
9922 | 0 | { |
9923 | | #ifndef TORRENT_DISABLE_LOGGING |
9924 | | ++idx; |
9925 | | #endif |
9926 | 0 | for (auto const& aep : t.endpoints) |
9927 | 0 | { |
9928 | 0 | auto aep_state_iter = listen_socket_states.find(aep.socket.get_ptr()); |
9929 | 0 | if (aep_state_iter == listen_socket_states.end()) |
9930 | 0 | { |
9931 | 0 | aep_state_iter = listen_socket_states.insert({aep.socket.get_ptr(), timer_state(aep.socket)}).first; |
9932 | 0 | } |
9933 | 0 | timer_state& ep_state = aep_state_iter->second; |
9934 | |
|
9935 | 0 | if (!aep.enabled) continue; |
9936 | 0 | for (protocol_version const ih : all_versions) |
9937 | 0 | { |
9938 | 0 | if (!supports_protocol[ih]) continue; |
9939 | | |
9940 | 0 | auto& state = ep_state.state[ih]; |
9941 | 0 | auto& a = aep.info_hashes[ih]; |
9942 | |
|
9943 | 0 | if (state.done) continue; |
9944 | | |
9945 | | #ifndef TORRENT_DISABLE_LOGGING |
9946 | | if (should_log()) |
9947 | | { |
9948 | | debug_log("*** tracker: (%d) [ep: %s ] \"%s\" [" |
9949 | | " found: %d i->tier: %d tier: %d" |
9950 | | " working: %d fails: %d limit: %d upd: %d ]" |
9951 | | , idx, print_endpoint(aep.local_endpoint).c_str(), t.url.c_str() |
9952 | | , state.found_working, t.tier, state.tier, a.is_working() |
9953 | | , a.fails, t.fail_limit, a.updating); |
9954 | | } |
9955 | | #endif |
9956 | | |
9957 | 0 | if (announce_to_all_tiers |
9958 | 0 | && !announce_to_all_trackers |
9959 | 0 | && state.found_working |
9960 | 0 | && t.tier <= state.tier |
9961 | 0 | && state.tier != INT_MAX) |
9962 | 0 | continue; |
9963 | | |
9964 | 0 | if (t.tier > state.tier) |
9965 | 0 | { |
9966 | 0 | if (!announce_to_all_tiers) break; |
9967 | 0 | state.found_working = false; |
9968 | 0 | } |
9969 | 0 | state.tier = t.tier; |
9970 | 0 | if (a.fails >= t.fail_limit && t.fail_limit != 0) continue; |
9971 | 0 | if (a.updating) |
9972 | 0 | { |
9973 | 0 | state.found_working = true; |
9974 | 0 | } |
9975 | 0 | else |
9976 | 0 | { |
9977 | 0 | time_point32 const next_tracker_announce = std::max(a.next_announce, a.min_announce); |
9978 | 0 | if (next_tracker_announce < next_announce) |
9979 | 0 | next_announce = next_tracker_announce; |
9980 | 0 | } |
9981 | 0 | if (a.is_working()) state.found_working = true; |
9982 | 0 | if (state.found_working |
9983 | 0 | && !announce_to_all_trackers |
9984 | 0 | && !announce_to_all_tiers) |
9985 | 0 | state.done = true; |
9986 | 0 | } |
9987 | 0 | } |
9988 | |
|
9989 | 0 | if (std::all_of(listen_socket_states.begin(), listen_socket_states.end() |
9990 | 0 | , [supports_protocol](std::pair<std::weak_ptr<aux::listen_socket_t>, timer_state> const& s) { |
9991 | 0 | for (protocol_version const ih : all_versions) |
9992 | 0 | { |
9993 | 0 | if (supports_protocol[ih] && !s.second.state[ih].done) |
9994 | 0 | return false; |
9995 | 0 | } |
9996 | 0 | return true; |
9997 | 0 | })) |
9998 | 0 | break; |
9999 | 0 | } |
10000 | |
|
10001 | | #ifndef TORRENT_DISABLE_LOGGING |
10002 | | bool before_now = false; |
10003 | | bool none_eligible = false; |
10004 | | #endif |
10005 | 0 | if (next_announce <= now) |
10006 | 0 | { |
10007 | 0 | next_announce = now; |
10008 | | #ifndef TORRENT_DISABLE_LOGGING |
10009 | | before_now = true; |
10010 | | #endif |
10011 | 0 | } |
10012 | 0 | else if (next_announce == time_point32::max()) |
10013 | 0 | { |
10014 | | // if no tracker can be announced to, check again in a minute |
10015 | 0 | next_announce = now + minutes32(1); |
10016 | | #ifndef TORRENT_DISABLE_LOGGING |
10017 | | none_eligible = true; |
10018 | | #endif |
10019 | 0 | } |
10020 | |
|
10021 | | #ifndef TORRENT_DISABLE_LOGGING |
10022 | | debug_log("*** update tracker timer: " |
10023 | | "before_now: %d " |
10024 | | "none_eligible: %d " |
10025 | | "m_waiting_tracker: %d " |
10026 | | "next_announce_in: %d" |
10027 | | , before_now |
10028 | | , none_eligible |
10029 | | , m_waiting_tracker |
10030 | | , int(total_seconds(next_announce - now))); |
10031 | | #endif |
10032 | | |
10033 | | // don't re-issue the timer if it's the same expiration time as last time |
10034 | | // if m_waiting_tracker is 0, expires_at() is undefined |
10035 | 0 | if (m_waiting_tracker && m_tracker_timer.expiry() == next_announce) return; |
10036 | | |
10037 | 0 | m_tracker_timer.expires_at(next_announce); |
10038 | 0 | ADD_OUTSTANDING_ASYNC("tracker::on_tracker_announce"); |
10039 | 0 | ++m_waiting_tracker; |
10040 | 0 | m_tracker_timer.async_wait([self = shared_from_this()](error_code const& e) |
10041 | 0 | { self->wrap(&torrent::on_tracker_announce, e); }); |
10042 | 0 | } |
10043 | | |
10044 | | void torrent::start_announcing() |
10045 | 922 | { |
10046 | 922 | TORRENT_ASSERT(is_single_thread()); |
10047 | 922 | TORRENT_ASSERT(state() != torrent_status::checking_files); |
10048 | 922 | if (is_paused()) |
10049 | 588 | { |
10050 | | #ifndef TORRENT_DISABLE_LOGGING |
10051 | | debug_log("start_announcing(), paused"); |
10052 | | #endif |
10053 | 588 | return; |
10054 | 588 | } |
10055 | | // if we don't have metadata, we need to announce |
10056 | | // before checking files, to get peers to |
10057 | | // request the metadata from |
10058 | 334 | if (!m_files_checked && valid_metadata()) |
10059 | 0 | { |
10060 | | #ifndef TORRENT_DISABLE_LOGGING |
10061 | | debug_log("start_announcing(), files not checked (with valid metadata)"); |
10062 | | #endif |
10063 | 0 | return; |
10064 | 0 | } |
10065 | 334 | if (m_announcing) return; |
10066 | | |
10067 | 334 | m_announcing = true; |
10068 | | |
10069 | 334 | #ifndef TORRENT_DISABLE_DHT |
10070 | 334 | if ((!m_peer_list || m_peer_list->num_peers() < 50) && m_ses.dht()) |
10071 | 0 | { |
10072 | | // we don't have any peers, prioritize |
10073 | | // announcing this torrent with the DHT |
10074 | 0 | m_ses.prioritize_dht(shared_from_this()); |
10075 | 0 | } |
10076 | 334 | #endif |
10077 | | |
10078 | 334 | if (!m_trackers.empty()) |
10079 | 0 | { |
10080 | | // tell the tracker that we're back |
10081 | 0 | for (auto& t : m_trackers) t.reset(); |
10082 | 0 | } |
10083 | | |
10084 | | // reset the stats, since from the tracker's |
10085 | | // point of view, this is a new session |
10086 | 334 | m_total_failed_bytes = 0; |
10087 | 334 | m_total_redundant_bytes = 0; |
10088 | 334 | m_stat.clear(); |
10089 | | |
10090 | 334 | update_want_tick(); |
10091 | | |
10092 | 334 | announce_with_tracker(); |
10093 | | |
10094 | 334 | lsd_announce(); |
10095 | 334 | } |
10096 | | |
10097 | | void torrent::stop_announcing() |
10098 | 2.31k | { |
10099 | 2.31k | TORRENT_ASSERT(is_single_thread()); |
10100 | 2.31k | if (!m_announcing) return; |
10101 | | |
10102 | 334 | m_tracker_timer.cancel(); |
10103 | | |
10104 | 334 | m_announcing = false; |
10105 | | |
10106 | 334 | time_point32 const now = aux::time_now32(); |
10107 | 334 | for (auto& t : m_trackers) |
10108 | 0 | { |
10109 | 0 | for (auto& aep : t.endpoints) |
10110 | 0 | { |
10111 | 0 | for (auto& a : aep.info_hashes) |
10112 | 0 | { |
10113 | 0 | a.next_announce = now; |
10114 | 0 | a.min_announce = now; |
10115 | 0 | } |
10116 | 0 | } |
10117 | 0 | } |
10118 | 334 | announce_with_tracker(event_t::stopped); |
10119 | 334 | } |
10120 | | |
10121 | | seconds32 torrent::finished_time() const |
10122 | 0 | { |
10123 | 0 | if(!is_finished() || is_paused()) |
10124 | 0 | return m_finished_time; |
10125 | | |
10126 | 0 | return m_finished_time + duration_cast<seconds32>( |
10127 | 0 | aux::time_now() - m_became_finished); |
10128 | 0 | } |
10129 | | |
10130 | | seconds32 torrent::active_time() const |
10131 | 0 | { |
10132 | 0 | if (is_paused()) |
10133 | 0 | return m_active_time; |
10134 | | |
10135 | | // m_active_time does not account for the current "session", just the |
10136 | | // time before we last started this torrent. To get the current time, we |
10137 | | // need to add the time since we started it |
10138 | 0 | return m_active_time + duration_cast<seconds32>( |
10139 | 0 | aux::time_now() - m_started); |
10140 | 0 | } |
10141 | | |
10142 | | seconds32 torrent::seeding_time() const |
10143 | 0 | { |
10144 | 0 | if(!is_seed() || is_paused()) |
10145 | 0 | return m_seeding_time; |
10146 | | // m_seeding_time does not account for the current "session", just the |
10147 | | // time before we last started this torrent. To get the current time, we |
10148 | | // need to add the time since we started it |
10149 | 0 | return m_seeding_time + duration_cast<seconds32>( |
10150 | 0 | aux::time_now() - m_became_seed); |
10151 | 0 | } |
10152 | | |
10153 | | seconds32 torrent::upload_mode_time() const |
10154 | 0 | { |
10155 | 0 | if(!m_upload_mode) |
10156 | 0 | return seconds32(0); |
10157 | | |
10158 | 0 | return aux::time_now32() - m_upload_mode_time; |
10159 | 0 | } |
10160 | | |
10161 | | void torrent::second_tick(int const tick_interval_ms) |
10162 | 3 | { |
10163 | 3 | TORRENT_ASSERT(want_tick()); |
10164 | 3 | TORRENT_ASSERT(is_single_thread()); |
10165 | 3 | INVARIANT_CHECK; |
10166 | | |
10167 | 3 | auto self = shared_from_this(); |
10168 | | |
10169 | 3 | #ifndef TORRENT_DISABLE_EXTENSIONS |
10170 | 3 | for (auto const& ext : m_extensions) |
10171 | 9 | { |
10172 | 9 | ext->tick(); |
10173 | 9 | } |
10174 | | |
10175 | 3 | if (m_abort) return; |
10176 | 3 | #endif |
10177 | | |
10178 | | // if we're in upload only mode and we're auto-managed |
10179 | | // leave upload mode every 10 minutes hoping that the error |
10180 | | // condition has been fixed |
10181 | 3 | if (m_upload_mode && m_auto_managed && upload_mode_time() >= |
10182 | 0 | seconds(settings().get_int(settings_pack::optimistic_disk_retry))) |
10183 | 0 | { |
10184 | 0 | set_upload_mode(false); |
10185 | 0 | } |
10186 | | |
10187 | 3 | if (is_paused() && !m_graceful_pause_mode) |
10188 | 0 | { |
10189 | | // let the stats fade out to 0 |
10190 | | // check the rate before ticking the stats so that the last update is sent |
10191 | | // with the rate equal to zero |
10192 | 0 | if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) |
10193 | 0 | state_updated(); |
10194 | 0 | m_stat.second_tick(tick_interval_ms); |
10195 | | |
10196 | | // the low pass transfer rate may just have dropped to 0 |
10197 | 0 | update_want_tick(); |
10198 | |
|
10199 | 0 | return; |
10200 | 0 | } |
10201 | | |
10202 | 3 | if (settings().get_bool(settings_pack::rate_limit_ip_overhead)) |
10203 | 3 | { |
10204 | 3 | int const up_limit = upload_limit(); |
10205 | 3 | int const down_limit = download_limit(); |
10206 | | |
10207 | 3 | if (down_limit > 0 |
10208 | 3 | && m_stat.download_ip_overhead() >= down_limit |
10209 | 3 | && alerts().should_post<performance_alert>()) |
10210 | 0 | { |
10211 | 0 | alerts().emplace_alert<performance_alert>(get_handle() |
10212 | 0 | , performance_alert::download_limit_too_low); |
10213 | 0 | } |
10214 | | |
10215 | 3 | if (up_limit > 0 |
10216 | 3 | && m_stat.upload_ip_overhead() >= up_limit |
10217 | 3 | && alerts().should_post<performance_alert>()) |
10218 | 0 | { |
10219 | 0 | alerts().emplace_alert<performance_alert>(get_handle() |
10220 | 0 | , performance_alert::upload_limit_too_low); |
10221 | 0 | } |
10222 | 3 | } |
10223 | | |
10224 | 3 | #ifndef TORRENT_DISABLE_STREAMING |
10225 | | // ---- TIME CRITICAL PIECES ---- |
10226 | | |
10227 | | #if TORRENT_DEBUG_STREAMING > 0 |
10228 | | std::vector<partial_piece_info> queue; |
10229 | | get_download_queue(&queue); |
10230 | | |
10231 | | std::vector<peer_info> peer_list; |
10232 | | get_peer_info(peer_list); |
10233 | | |
10234 | | std::sort(queue.begin(), queue.end(), [](partial_piece_info const& lhs, partial_piece_info const& rhs) |
10235 | | { return lhs.piece_index < rhs.piece_index;; }); |
10236 | | |
10237 | | std::printf("average piece download time: %.2f s (+/- %.2f s)\n" |
10238 | | , m_average_piece_time / 1000.f |
10239 | | , m_piece_time_deviation / 1000.f); |
10240 | | for (auto& i : queue) |
10241 | | { |
10242 | | extern void print_piece(libtorrent::partial_piece_info* pp |
10243 | | , std::vector<libtorrent::peer_info> const& peers |
10244 | | , std::vector<time_critical_piece> const& time_critical); |
10245 | | |
10246 | | print_piece(&i, peer_list, m_time_critical_pieces); |
10247 | | } |
10248 | | #endif // TORRENT_DEBUG_STREAMING |
10249 | | |
10250 | 3 | if (!m_time_critical_pieces.empty() && !upload_mode()) |
10251 | 0 | { |
10252 | 0 | request_time_critical_pieces(); |
10253 | 0 | } |
10254 | 3 | #endif // TORRENT_DISABLE_STREAMING |
10255 | | |
10256 | | // ---- WEB SEEDS ---- |
10257 | | |
10258 | 3 | maybe_connect_web_seeds(); |
10259 | | |
10260 | 3 | m_swarm_last_seen_complete = m_last_seen_complete; |
10261 | 3 | for (auto p : m_connections) |
10262 | 0 | { |
10263 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
10264 | | |
10265 | | // look for the peer that saw a seed most recently |
10266 | 0 | m_swarm_last_seen_complete = std::max(p->last_seen_complete(), m_swarm_last_seen_complete); |
10267 | | |
10268 | | // updates the peer connection's ul/dl bandwidth |
10269 | | // resource requests |
10270 | 0 | p->second_tick(tick_interval_ms); |
10271 | 0 | } |
10272 | 3 | #if TORRENT_ABI_VERSION <= 2 |
10273 | 3 | if (m_ses.alerts().should_post<stats_alert>()) |
10274 | 0 | m_ses.alerts().emplace_alert<stats_alert>(get_handle(), tick_interval_ms, m_stat); |
10275 | 3 | #endif |
10276 | | |
10277 | 3 | m_total_uploaded += m_stat.last_payload_uploaded(); |
10278 | 3 | m_total_downloaded += m_stat.last_payload_downloaded(); |
10279 | 3 | m_stat.second_tick(tick_interval_ms); |
10280 | | |
10281 | | // these counters are saved in the resume data, since they updated |
10282 | | // we need to save the resume data too |
10283 | 3 | set_need_save_resume(torrent_handle::if_counters_changed); |
10284 | | |
10285 | | // if the rate is 0, there's no update because of network transfers |
10286 | 3 | if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) |
10287 | 0 | state_updated(); |
10288 | | |
10289 | | // this section determines whether the torrent is active or not. When it |
10290 | | // changes state, it may also trigger the auto-manage logic to reconsider |
10291 | | // which torrents should be queued and started. There is a low pass |
10292 | | // filter in order to avoid flapping (auto_manage_startup). |
10293 | 3 | bool is_inactive = is_inactive_internal(); |
10294 | | |
10295 | 3 | if (settings().get_bool(settings_pack::dont_count_slow_torrents)) |
10296 | 3 | { |
10297 | 3 | if (is_inactive != m_inactive && !m_pending_active_change) |
10298 | 2 | { |
10299 | 2 | int const delay = settings().get_int(settings_pack::auto_manage_startup); |
10300 | 2 | m_inactivity_timer.expires_after(seconds(delay)); |
10301 | 2 | m_inactivity_timer.async_wait([self](error_code const& ec) { |
10302 | 2 | self->wrap(&torrent::on_inactivity_tick, ec); }); |
10303 | 2 | m_pending_active_change = true; |
10304 | 2 | } |
10305 | 1 | else if (is_inactive == m_inactive |
10306 | 1 | && m_pending_active_change) |
10307 | 0 | { |
10308 | 0 | m_inactivity_timer.cancel(); |
10309 | 0 | } |
10310 | 3 | } |
10311 | | |
10312 | | // want_tick depends on whether the low pass transfer rates are non-zero |
10313 | | // or not. They may just have turned zero in this last tick. |
10314 | 3 | update_want_tick(); |
10315 | 3 | } |
10316 | | |
10317 | | bool torrent::is_inactive_internal() const |
10318 | 3 | { |
10319 | 3 | if (is_finished()) |
10320 | 0 | return m_stat.upload_payload_rate() |
10321 | 0 | < settings().get_int(settings_pack::inactive_up_rate); |
10322 | 3 | else |
10323 | 3 | return m_stat.download_payload_rate() |
10324 | 3 | < settings().get_int(settings_pack::inactive_down_rate); |
10325 | 3 | } |
10326 | | |
10327 | 2 | void torrent::on_inactivity_tick(error_code const& ec) try |
10328 | 2 | { |
10329 | 2 | m_pending_active_change = false; |
10330 | | |
10331 | 2 | if (ec) return; |
10332 | | |
10333 | 0 | bool const is_inactive = is_inactive_internal(); |
10334 | 0 | if (is_inactive == m_inactive) return; |
10335 | | |
10336 | 0 | m_inactive = is_inactive; |
10337 | |
|
10338 | 0 | update_state_list(); |
10339 | 0 | update_want_tick(); |
10340 | |
|
10341 | 0 | if (settings().get_bool(settings_pack::dont_count_slow_torrents)) |
10342 | 0 | m_ses.trigger_auto_manage(); |
10343 | 0 | } |
10344 | 2 | catch (...) { handle_exception(); } |
10345 | | |
10346 | | namespace { |
10347 | | int zero_or(int const val, int const def_val) |
10348 | 0 | { return (val <= 0) ? def_val : val; } |
10349 | | } |
10350 | | |
10351 | | void torrent::maybe_connect_web_seeds() |
10352 | 923 | { |
10353 | 923 | if (m_abort) return; |
10354 | | |
10355 | | // if we have everything we want we don't need to connect to any web-seed |
10356 | 923 | if (m_web_seeds.empty() |
10357 | 923 | || is_finished() |
10358 | 923 | || !m_files_checked |
10359 | 923 | || num_peers() >= int(m_max_connections) |
10360 | 923 | || m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit)) |
10361 | 923 | { |
10362 | 923 | return; |
10363 | 923 | } |
10364 | | |
10365 | | // when set to unlimited, use 100 as the limit |
10366 | 0 | int limit = zero_or(settings().get_int(settings_pack::max_web_seed_connections) |
10367 | 0 | , 100); |
10368 | |
|
10369 | 0 | auto const now = aux::time_now32(); |
10370 | | |
10371 | | // keep trying web-seeds if there are any |
10372 | | // first find out which web seeds we are connected to |
10373 | 0 | for (auto i = m_web_seeds.begin(); i != m_web_seeds.end() && limit > 0;) |
10374 | 0 | { |
10375 | 0 | auto const w = i++; |
10376 | 0 | if (w->disabled || w->removed || w->retry > now || !w->interesting) |
10377 | 0 | continue; |
10378 | | |
10379 | 0 | --limit; |
10380 | 0 | if (w->peer_info.connection || w->resolving) |
10381 | 0 | continue; |
10382 | | |
10383 | 0 | connect_to_url_seed(w); |
10384 | 0 | } |
10385 | 0 | } |
10386 | | |
10387 | | #ifndef TORRENT_DISABLE_SHARE_MODE |
10388 | | void torrent::recalc_share_mode() |
10389 | 0 | { |
10390 | 0 | TORRENT_ASSERT(share_mode()); |
10391 | 0 | if (is_seed()) return; |
10392 | | |
10393 | 0 | int const pieces_in_torrent = m_torrent_file->num_pieces(); |
10394 | 0 | int num_seeds = 0; |
10395 | 0 | int num_peers = 0; |
10396 | 0 | int num_downloaders = 0; |
10397 | 0 | int missing_pieces = 0; |
10398 | 0 | for (auto const p : m_connections) |
10399 | 0 | { |
10400 | 0 | TORRENT_INCREMENT(m_iterating_connections); |
10401 | 0 | if (p->is_connecting()) continue; |
10402 | 0 | if (p->is_disconnecting()) continue; |
10403 | 0 | ++num_peers; |
10404 | 0 | if (p->is_seed()) |
10405 | 0 | { |
10406 | 0 | ++num_seeds; |
10407 | 0 | continue; |
10408 | 0 | } |
10409 | | |
10410 | 0 | if (p->share_mode()) continue; |
10411 | 0 | if (p->upload_only()) continue; |
10412 | | |
10413 | 0 | ++num_downloaders; |
10414 | 0 | missing_pieces += pieces_in_torrent - p->num_have_pieces(); |
10415 | 0 | } |
10416 | |
|
10417 | 0 | if (num_peers == 0) return; |
10418 | | |
10419 | 0 | if (num_seeds * 100 / num_peers > 50 |
10420 | 0 | && (num_peers * 100 / m_max_connections > 90 |
10421 | 0 | || num_peers > 20)) |
10422 | 0 | { |
10423 | | // we are connected to more than 50% seeds (and we're beyond |
10424 | | // 90% of the max number of connections). That will |
10425 | | // limit our ability to upload. We need more downloaders. |
10426 | | // disconnect some seeds so that we don't have more than 50% |
10427 | 0 | int const to_disconnect = num_seeds - num_peers / 2; |
10428 | 0 | aux::vector<peer_connection*> seeds; |
10429 | 0 | seeds.reserve(num_seeds); |
10430 | 0 | std::copy_if(m_connections.begin(), m_connections.end(), std::back_inserter(seeds) |
10431 | 0 | , [](peer_connection const* p) { return p->is_seed(); }); |
10432 | |
|
10433 | 0 | aux::random_shuffle(seeds); |
10434 | 0 | TORRENT_ASSERT(to_disconnect <= seeds.end_index()); |
10435 | 0 | for (auto const& p : span<peer_connection*>(seeds).first(to_disconnect)) |
10436 | 0 | p->disconnect(errors::upload_upload_connection, operation_t::bittorrent); |
10437 | 0 | } |
10438 | |
|
10439 | 0 | if (num_downloaders == 0) return; |
10440 | | |
10441 | | // assume that the seeds are about as fast as us. During the time |
10442 | | // we can download one piece, and upload one piece, each seed |
10443 | | // can upload two pieces. |
10444 | 0 | missing_pieces -= 2 * num_seeds; |
10445 | |
|
10446 | 0 | if (missing_pieces <= 0) return; |
10447 | | |
10448 | | // missing_pieces represents our opportunity to download pieces |
10449 | | // and share them more than once each |
10450 | | |
10451 | | // now, download at least one piece, otherwise download one more |
10452 | | // piece if our downloaded (and downloading) pieces is less than 50% |
10453 | | // of the uploaded bytes |
10454 | 0 | int const num_downloaded_pieces = std::max(m_picker->have().num_pieces |
10455 | 0 | , m_picker->want().num_pieces); |
10456 | |
|
10457 | 0 | if (std::int64_t(num_downloaded_pieces) * m_torrent_file->piece_length() |
10458 | 0 | * settings().get_int(settings_pack::share_mode_target) > m_total_uploaded |
10459 | 0 | && num_downloaded_pieces > 0) |
10460 | 0 | return; |
10461 | | |
10462 | | // don't have more pieces downloading in parallel than 5% of the total |
10463 | | // number of pieces we have downloaded |
10464 | 0 | if (m_picker->get_download_queue_size() > num_downloaded_pieces / 20) |
10465 | 0 | return; |
10466 | | |
10467 | | // one more important property is that there are enough pieces |
10468 | | // that more than one peer wants to download |
10469 | | // make sure that there are enough downloaders for the rarest |
10470 | | // piece. Go through all pieces, figure out which one is the rarest |
10471 | | // and how many peers that has that piece |
10472 | | |
10473 | 0 | aux::vector<piece_index_t> rarest_pieces; |
10474 | |
|
10475 | 0 | int const num_pieces = m_torrent_file->num_pieces(); |
10476 | 0 | int rarest_rarity = INT_MAX; |
10477 | 0 | for (piece_index_t i(0); i < piece_index_t(num_pieces); ++i) |
10478 | 0 | { |
10479 | 0 | piece_picker::piece_stats_t ps = m_picker->piece_stats(i); |
10480 | 0 | if (ps.peer_count == 0) continue; |
10481 | 0 | if (ps.priority == 0 && (ps.have || ps.downloading)) |
10482 | 0 | { |
10483 | 0 | m_picker->set_piece_priority(i, default_priority); |
10484 | 0 | continue; |
10485 | 0 | } |
10486 | | // don't count pieces we already have or are trying to download |
10487 | 0 | if (ps.priority > 0 || ps.have) continue; |
10488 | 0 | if (ps.peer_count > rarest_rarity) continue; |
10489 | 0 | if (ps.peer_count == rarest_rarity) |
10490 | 0 | { |
10491 | 0 | rarest_pieces.push_back(i); |
10492 | 0 | continue; |
10493 | 0 | } |
10494 | | |
10495 | 0 | rarest_pieces.clear(); |
10496 | 0 | rarest_rarity = ps.peer_count; |
10497 | 0 | rarest_pieces.push_back(i); |
10498 | 0 | } |
10499 | |
|
10500 | 0 | update_gauge(); |
10501 | 0 | update_want_peers(); |
10502 | | |
10503 | | // now, rarest_pieces is a list of all pieces that are the rarest ones. |
10504 | | // and rarest_rarity is the number of peers that have the rarest pieces |
10505 | | |
10506 | | // if there's only a single peer that doesn't have the rarest piece |
10507 | | // it's impossible for us to download one piece and upload it |
10508 | | // twice. i.e. we cannot get a positive share ratio |
10509 | 0 | if (num_peers - rarest_rarity |
10510 | 0 | < settings().get_int(settings_pack::share_mode_target)) |
10511 | 0 | return; |
10512 | | |
10513 | | // now, pick one of the rarest pieces to download |
10514 | 0 | int const pick = int(random(aux::numeric_cast<std::uint32_t>(rarest_pieces.end_index() - 1))); |
10515 | 0 | bool const was_finished = is_finished(); |
10516 | 0 | m_picker->set_piece_priority(rarest_pieces[pick], default_priority); |
10517 | 0 | update_gauge(); |
10518 | 0 | update_peer_interest(was_finished); |
10519 | 0 | update_want_peers(); |
10520 | 0 | } |
10521 | | #endif // TORRENT_DISABLE_SHARE_MODE |
10522 | | |
10523 | | void torrent::sent_bytes(int const bytes_payload, int const bytes_protocol) |
10524 | 16 | { |
10525 | 16 | m_stat.sent_bytes(bytes_payload, bytes_protocol); |
10526 | 16 | m_ses.sent_bytes(bytes_payload, bytes_protocol); |
10527 | 16 | } |
10528 | | |
10529 | | void torrent::received_bytes(int const bytes_payload, int const bytes_protocol) |
10530 | 152k | { |
10531 | 152k | m_stat.received_bytes(bytes_payload, bytes_protocol); |
10532 | 152k | m_ses.received_bytes(bytes_payload, bytes_protocol); |
10533 | 152k | } |
10534 | | |
10535 | | void torrent::trancieve_ip_packet(int const bytes, bool const ipv6) |
10536 | 211 | { |
10537 | 211 | m_stat.trancieve_ip_packet(bytes, ipv6); |
10538 | 211 | m_ses.trancieve_ip_packet(bytes, ipv6); |
10539 | 211 | } |
10540 | | |
10541 | | void torrent::sent_syn(bool const ipv6) |
10542 | 0 | { |
10543 | 0 | m_stat.sent_syn(ipv6); |
10544 | 0 | m_ses.sent_syn(ipv6); |
10545 | 0 | } |
10546 | | |
10547 | | void torrent::received_synack(bool const ipv6) |
10548 | 0 | { |
10549 | 0 | m_stat.received_synack(ipv6); |
10550 | 0 | m_ses.received_synack(ipv6); |
10551 | 0 | } |
10552 | | |
10553 | | #ifndef TORRENT_DISABLE_STREAMING |
10554 | | |
10555 | | #if TORRENT_DEBUG_STREAMING > 0 |
10556 | | char const* esc(char const* code) |
10557 | | { |
10558 | | // this is a silly optimization |
10559 | | // to avoid copying of strings |
10560 | | int const num_strings = 200; |
10561 | | static char buf[num_strings][20]; |
10562 | | static int round_robin = 0; |
10563 | | char* ret = buf[round_robin]; |
10564 | | ++round_robin; |
10565 | | if (round_robin >= num_strings) round_robin = 0; |
10566 | | ret[0] = '\033'; |
10567 | | ret[1] = '['; |
10568 | | int i = 2; |
10569 | | int j = 0; |
10570 | | while (code[j]) ret[i++] = code[j++]; |
10571 | | ret[i++] = 'm'; |
10572 | | ret[i++] = 0; |
10573 | | return ret; |
10574 | | } |
10575 | | |
10576 | | int peer_index(libtorrent::tcp::endpoint addr |
10577 | | , std::vector<libtorrent::peer_info> const& peers) |
10578 | | { |
10579 | | std::vector<peer_info>::const_iterator i = std::find_if(peers.begin() |
10580 | | , peers.end(), std::bind(&peer_info::ip, _1) == addr); |
10581 | | if (i == peers.end()) return -1; |
10582 | | |
10583 | | return i - peers.begin(); |
10584 | | } |
10585 | | |
10586 | | void print_piece(libtorrent::partial_piece_info* pp |
10587 | | , std::vector<libtorrent::peer_info> const& peers |
10588 | | , std::vector<time_critical_piece> const& time_critical) |
10589 | | { |
10590 | | time_point const now = clock_type::now(); |
10591 | | |
10592 | | float deadline = 0.f; |
10593 | | float last_request = 0.f; |
10594 | | int timed_out = -1; |
10595 | | |
10596 | | int piece = pp->piece_index; |
10597 | | std::vector<time_critical_piece>::const_iterator i |
10598 | | = std::find_if(time_critical.begin(), time_critical.end() |
10599 | | , std::bind(&time_critical_piece::piece, _1) == piece); |
10600 | | if (i != time_critical.end()) |
10601 | | { |
10602 | | deadline = total_milliseconds(i->deadline - now) / 1000.f; |
10603 | | if (i->last_requested == min_time()) |
10604 | | last_request = -1; |
10605 | | else |
10606 | | last_request = total_milliseconds(now - i->last_requested) / 1000.f; |
10607 | | timed_out = i->timed_out; |
10608 | | } |
10609 | | |
10610 | | int num_blocks = pp->blocks_in_piece; |
10611 | | |
10612 | | std::printf("%5d: [", piece); |
10613 | | for (int j = 0; j < num_blocks; ++j) |
10614 | | { |
10615 | | int index = pp ? peer_index(pp->blocks[j].peer(), peers) % 36 : -1; |
10616 | | char chr = '+'; |
10617 | | if (index >= 0) |
10618 | | chr = (index < 10)?'0' + index:'A' + index - 10; |
10619 | | |
10620 | | char const* color = ""; |
10621 | | char const* multi_req = ""; |
10622 | | |
10623 | | if (pp->blocks[j].num_peers > 1) |
10624 | | multi_req = esc("1"); |
10625 | | |
10626 | | if (pp->blocks[j].bytes_progress > 0 |
10627 | | && pp->blocks[j].state == block_info::requested) |
10628 | | { |
10629 | | color = esc("33;7"); |
10630 | | chr = '0' + (pp->blocks[j].bytes_progress * 10 / pp->blocks[j].block_size); |
10631 | | } |
10632 | | else if (pp->blocks[j].state == block_info::finished) color = esc("32;7"); |
10633 | | else if (pp->blocks[j].state == block_info::writing) color = esc("36;7"); |
10634 | | else if (pp->blocks[j].state == block_info::requested) color = esc("0"); |
10635 | | else { color = esc("0"); chr = ' '; } |
10636 | | |
10637 | | std::printf("%s%s%c%s", color, multi_req, chr, esc("0")); |
10638 | | } |
10639 | | std::printf("%s]", esc("0")); |
10640 | | if (deadline != 0.f) |
10641 | | std::printf(" deadline: %f last-req: %f timed_out: %d\n" |
10642 | | , deadline, last_request, timed_out); |
10643 | | else |
10644 | | std::printf("\n"); |
10645 | | } |
10646 | | #endif // TORRENT_DEBUG_STREAMING |
10647 | | |
10648 | | namespace { |
10649 | | |
10650 | | struct busy_block_t |
10651 | | { |
10652 | | int peers; |
10653 | | int index; |
10654 | 0 | bool operator<(busy_block_t const& rhs) const { return peers < rhs.peers; } |
10655 | | }; |
10656 | | |
10657 | | void pick_busy_blocks(piece_picker const* picker |
10658 | | , piece_index_t const piece |
10659 | | , int const blocks_in_piece |
10660 | | , int const timed_out |
10661 | | , std::vector<piece_block>& interesting_blocks |
10662 | | , piece_picker::downloading_piece const& pi) |
10663 | 0 | { |
10664 | | // if there aren't any free blocks in the piece, and the piece is |
10665 | | // old enough, we may switch into busy mode for this piece. In this |
10666 | | // case busy_blocks and busy_count are set to contain the eligible |
10667 | | // busy blocks we may pick |
10668 | | // first, figure out which blocks are eligible for picking |
10669 | | // in "busy-mode" |
10670 | 0 | TORRENT_ALLOCA(busy_blocks, busy_block_t, blocks_in_piece); |
10671 | 0 | int busy_count = 0; |
10672 | | |
10673 | | // pick busy blocks from the piece |
10674 | 0 | int idx = -1; |
10675 | 0 | for (auto const& info : picker->blocks_for_piece(pi)) |
10676 | 0 | { |
10677 | 0 | ++idx; |
10678 | | // only consider blocks that have been requested |
10679 | | // and we're still waiting for them |
10680 | 0 | if (info.state != piece_picker::block_info::state_requested) |
10681 | 0 | continue; |
10682 | | |
10683 | 0 | piece_block b(piece, idx); |
10684 | | |
10685 | | // only allow a single additional request per block, in order |
10686 | | // to spread it out evenly across all stalled blocks |
10687 | 0 | if (int(info.num_peers) > timed_out) |
10688 | 0 | continue; |
10689 | | |
10690 | 0 | busy_blocks[busy_count].peers = info.num_peers; |
10691 | 0 | busy_blocks[busy_count].index = idx; |
10692 | 0 | ++busy_count; |
10693 | |
|
10694 | | #if TORRENT_DEBUG_STREAMING > 1 |
10695 | | std::printf(" [%d (%d)]", b.block_index, info.num_peers); |
10696 | | #endif |
10697 | 0 | } |
10698 | | #if TORRENT_DEBUG_STREAMING > 1 |
10699 | | std::printf("\n"); |
10700 | | #endif |
10701 | |
|
10702 | 0 | busy_blocks = busy_blocks.first(busy_count); |
10703 | | |
10704 | | // then sort blocks by the number of peers with requests |
10705 | | // to the blocks (request the blocks with the fewest peers |
10706 | | // first) |
10707 | 0 | std::sort(busy_blocks.begin(), busy_blocks.end()); |
10708 | | |
10709 | | // then insert them into the interesting_blocks vector |
10710 | 0 | for (auto const& block : busy_blocks) |
10711 | 0 | interesting_blocks.emplace_back(piece, block.index); |
10712 | 0 | } |
10713 | | |
10714 | | void pick_time_critical_block(std::vector<peer_connection*>& peers |
10715 | | , std::vector<peer_connection*>& ignore_peers |
10716 | | , std::set<peer_connection*>& peers_with_requests |
10717 | | , piece_picker::downloading_piece const& pi |
10718 | | , time_critical_piece* i |
10719 | | , piece_picker const* picker |
10720 | | , int const blocks_in_piece |
10721 | | , int const timed_out) |
10722 | 0 | { |
10723 | 0 | std::vector<piece_block> interesting_blocks; |
10724 | 0 | std::vector<piece_block> backup1; |
10725 | 0 | std::vector<piece_block> backup2; |
10726 | 0 | std::vector<piece_index_t> ignore; |
10727 | |
|
10728 | 0 | time_point const now = aux::time_now(); |
10729 | | |
10730 | | // loop until every block has been requested from this piece (i->piece) |
10731 | 0 | do |
10732 | 0 | { |
10733 | | // if this peer's download time exceeds 2 seconds, we're done. |
10734 | | // We don't want to build unreasonably long request queues |
10735 | 0 | if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000)) |
10736 | 0 | { |
10737 | | #if TORRENT_DEBUG_STREAMING > 1 |
10738 | | std::printf("queue time: %d ms, done\n" |
10739 | | , int(total_milliseconds(peers[0]->download_queue_time()))); |
10740 | | #endif |
10741 | 0 | break; |
10742 | 0 | } |
10743 | | |
10744 | | // pick the peer with the lowest download_queue_time that has i->piece |
10745 | 0 | auto p = std::find_if(peers.begin(), peers.end() |
10746 | 0 | , std::bind(&peer_connection::has_piece, _1, i->piece)); |
10747 | | |
10748 | | // obviously we'll have to skip it if we don't have a peer that has |
10749 | | // this piece |
10750 | 0 | if (p == peers.end()) |
10751 | 0 | { |
10752 | | #if TORRENT_DEBUG_STREAMING > 1 |
10753 | | std::printf("out of peers, done\n"); |
10754 | | #endif |
10755 | 0 | break; |
10756 | 0 | } |
10757 | 0 | peer_connection& c = **p; |
10758 | |
|
10759 | 0 | interesting_blocks.clear(); |
10760 | 0 | backup1.clear(); |
10761 | 0 | backup2.clear(); |
10762 | | |
10763 | | // specifically request blocks with no affinity towards fast or slow |
10764 | | // pieces. If we would, the picked block might end up in one of |
10765 | | // the backup lists |
10766 | 0 | picker->add_blocks(i->piece, c.get_bitfield(), interesting_blocks |
10767 | 0 | , backup1, backup2, blocks_in_piece, 0, c.peer_info_struct() |
10768 | 0 | , ignore, {}); |
10769 | |
|
10770 | 0 | interesting_blocks.insert(interesting_blocks.end() |
10771 | 0 | , backup1.begin(), backup1.end()); |
10772 | 0 | interesting_blocks.insert(interesting_blocks.end() |
10773 | 0 | , backup2.begin(), backup2.end()); |
10774 | |
|
10775 | 0 | bool busy_mode = false; |
10776 | |
|
10777 | 0 | if (interesting_blocks.empty()) |
10778 | 0 | { |
10779 | 0 | busy_mode = true; |
10780 | |
|
10781 | | #if TORRENT_DEBUG_STREAMING > 1 |
10782 | | std::printf("interesting_blocks.empty()\n"); |
10783 | | #endif |
10784 | | |
10785 | | // there aren't any free blocks to pick, and the piece isn't |
10786 | | // old enough to pick busy blocks yet. break to continue to |
10787 | | // the next piece. |
10788 | 0 | if (timed_out == 0) |
10789 | 0 | { |
10790 | | #if TORRENT_DEBUG_STREAMING > 1 |
10791 | | std::printf("not timed out, moving on to next piece\n"); |
10792 | | #endif |
10793 | 0 | break; |
10794 | 0 | } |
10795 | | |
10796 | | #if TORRENT_DEBUG_STREAMING > 1 |
10797 | | std::printf("pick busy blocks\n"); |
10798 | | #endif |
10799 | | |
10800 | 0 | pick_busy_blocks(picker, i->piece, blocks_in_piece, timed_out |
10801 | 0 | , interesting_blocks, pi); |
10802 | 0 | } |
10803 | | |
10804 | | // we can't pick anything from this piece, we're done with it. |
10805 | | // move on to the next one |
10806 | 0 | if (interesting_blocks.empty()) break; |
10807 | | |
10808 | 0 | piece_block const b = interesting_blocks.front(); |
10809 | | |
10810 | | // in busy mode we need to make sure we don't do silly |
10811 | | // things like requesting the same block twice from the |
10812 | | // same peer |
10813 | 0 | std::vector<pending_block> const& dq = c.download_queue(); |
10814 | |
|
10815 | 0 | bool const already_requested = std::find_if(dq.begin(), dq.end() |
10816 | 0 | , aux::has_block(b)) != dq.end(); |
10817 | |
|
10818 | 0 | if (already_requested) |
10819 | 0 | { |
10820 | | // if the piece is stalled, we may end up picking a block |
10821 | | // that we've already requested from this peer. If so, we should |
10822 | | // simply disregard this peer from this piece, since this peer |
10823 | | // is likely to be causing the stall. We should request it |
10824 | | // from the next peer in the list |
10825 | | // the peer will be put back in the set for the next piece |
10826 | 0 | ignore_peers.push_back(*p); |
10827 | 0 | peers.erase(p); |
10828 | | #if TORRENT_DEBUG_STREAMING > 1 |
10829 | | std::printf("piece already requested by peer, try next peer\n"); |
10830 | | #endif |
10831 | | // try next peer |
10832 | 0 | continue; |
10833 | 0 | } |
10834 | | |
10835 | 0 | std::vector<pending_block> const& rq = c.request_queue(); |
10836 | |
|
10837 | 0 | bool const already_in_queue = std::find_if(rq.begin(), rq.end() |
10838 | 0 | , aux::has_block(b)) != rq.end(); |
10839 | |
|
10840 | 0 | if (already_in_queue) |
10841 | 0 | { |
10842 | 0 | if (!c.make_time_critical(b)) |
10843 | 0 | { |
10844 | | #if TORRENT_DEBUG_STREAMING > 1 |
10845 | | std::printf("piece already time-critical and in queue for peer, trying next peer\n"); |
10846 | | #endif |
10847 | 0 | ignore_peers.push_back(*p); |
10848 | 0 | peers.erase(p); |
10849 | 0 | continue; |
10850 | 0 | } |
10851 | 0 | i->last_requested = now; |
10852 | |
|
10853 | | #if TORRENT_DEBUG_STREAMING > 1 |
10854 | | std::printf("piece already in queue for peer, making time-critical\n"); |
10855 | | #endif |
10856 | | |
10857 | | // we inserted a new block in the request queue, this |
10858 | | // makes us actually send it later |
10859 | 0 | peers_with_requests.insert(peers_with_requests.begin(), &c); |
10860 | 0 | } |
10861 | 0 | else |
10862 | 0 | { |
10863 | 0 | if (!c.add_request(b, peer_connection::time_critical |
10864 | 0 | | (busy_mode ? peer_connection::busy : request_flags_t{}))) |
10865 | 0 | { |
10866 | | #if TORRENT_DEBUG_STREAMING > 1 |
10867 | | std::printf("failed to request block [%d, %d]\n" |
10868 | | , b.piece_index, b.block_index); |
10869 | | #endif |
10870 | 0 | ignore_peers.push_back(*p); |
10871 | 0 | peers.erase(p); |
10872 | 0 | continue; |
10873 | 0 | } |
10874 | | |
10875 | | #if TORRENT_DEBUG_STREAMING > 1 |
10876 | | std::printf("requested block [%d, %d]\n" |
10877 | | , b.piece_index, b.block_index); |
10878 | | #endif |
10879 | 0 | peers_with_requests.insert(peers_with_requests.begin(), &c); |
10880 | 0 | } |
10881 | | |
10882 | 0 | if (!busy_mode) i->last_requested = now; |
10883 | |
|
10884 | 0 | if (i->first_requested == min_time()) i->first_requested = now; |
10885 | |
|
10886 | 0 | if (!c.can_request_time_critical()) |
10887 | 0 | { |
10888 | | #if TORRENT_DEBUG_STREAMING > 1 |
10889 | | std::printf("peer cannot pick time critical pieces\n"); |
10890 | | #endif |
10891 | 0 | peers.erase(p); |
10892 | | // try next peer |
10893 | 0 | continue; |
10894 | 0 | } |
10895 | | |
10896 | | // resort p, since it will have a higher download_queue_time now |
10897 | 0 | while (p != peers.end()-1 && (*p)->download_queue_time() |
10898 | 0 | > (*(p+1))->download_queue_time()) |
10899 | 0 | { |
10900 | 0 | std::iter_swap(p, p+1); |
10901 | 0 | ++p; |
10902 | 0 | } |
10903 | 0 | } while (!interesting_blocks.empty()); |
10904 | 0 | } |
10905 | | |
10906 | | } // anonymous namespace |
10907 | | |
10908 | | void torrent::request_time_critical_pieces() |
10909 | 0 | { |
10910 | 0 | TORRENT_ASSERT(is_single_thread()); |
10911 | 0 | TORRENT_ASSERT(!upload_mode()); |
10912 | | |
10913 | | // build a list of peers and sort it by download_queue_time |
10914 | | // we use this sorted list to determine which peer we should |
10915 | | // request a block from. The earlier a peer is in the list, |
10916 | | // the sooner we will fully download the block we request. |
10917 | 0 | aux::vector<peer_connection*> peers; |
10918 | 0 | peers.reserve(num_peers()); |
10919 | | |
10920 | | // some peers are marked as not being able to request time critical |
10921 | | // blocks from. For instance, peers that have choked us, peers that are |
10922 | | // on parole (i.e. they are believed to have sent us bad data), peers |
10923 | | // that are being disconnected, in upload mode etc. |
10924 | 0 | std::remove_copy_if(m_connections.begin(), m_connections.end() |
10925 | 0 | , std::back_inserter(peers), [] (peer_connection* p) |
10926 | 0 | { return !p->can_request_time_critical(); }); |
10927 | | |
10928 | | // sort by the time we believe it will take this peer to send us all |
10929 | | // blocks we've requested from it. The shorter time, the better candidate |
10930 | | // it is to request a time critical block from. |
10931 | 0 | std::sort(peers.begin(), peers.end() |
10932 | 0 | , [] (peer_connection const* lhs, peer_connection const* rhs) |
10933 | 0 | { return lhs->download_queue_time(16*1024) < rhs->download_queue_time(16*1024); }); |
10934 | | |
10935 | | // remove the bottom 10% of peers from the candidate set. |
10936 | | // this is just to remove outliers that might stall downloads |
10937 | 0 | int const new_size = (peers.end_index() * 9 + 9) / 10; |
10938 | 0 | TORRENT_ASSERT(new_size <= peers.end_index()); |
10939 | 0 | peers.resize(new_size); |
10940 | | |
10941 | | // remember all the peers we issued requests to, so we can commit them |
10942 | | // at the end of this function. Instead of sending the requests right |
10943 | | // away, we batch them up and send them in a single write to the TCP |
10944 | | // socket, increasing the chance that they will all be sent in the same |
10945 | | // packet. |
10946 | 0 | std::set<peer_connection*> peers_with_requests; |
10947 | | |
10948 | | // peers that should be temporarily ignored for a specific piece |
10949 | | // in order to give priority to other peers. They should be used for |
10950 | | // subsequent pieces, so they are stored in this vector until the |
10951 | | // piece is done |
10952 | 0 | std::vector<peer_connection*> ignore_peers; |
10953 | |
|
10954 | 0 | time_point const now = clock_type::now(); |
10955 | | |
10956 | | // now, iterate over all time critical pieces, in order of importance, and |
10957 | | // request them from the peers, in order of responsiveness. i.e. request |
10958 | | // the most time critical pieces from the fastest peers. |
10959 | 0 | bool first_piece{true}; |
10960 | 0 | for (auto& i : m_time_critical_pieces) |
10961 | 0 | { |
10962 | | #if TORRENT_DEBUG_STREAMING > 1 |
10963 | | std::printf("considering %d\n", i->piece); |
10964 | | #endif |
10965 | |
|
10966 | 0 | if (peers.empty()) |
10967 | 0 | { |
10968 | | #if TORRENT_DEBUG_STREAMING > 1 |
10969 | | std::printf("out of peers, done\n"); |
10970 | | #endif |
10971 | 0 | break; |
10972 | 0 | } |
10973 | | |
10974 | | // the +1000 is to compensate for the fact that we only call this |
10975 | | // function once per second, so if we need to request it 500 ms from |
10976 | | // now, we should request it right away |
10977 | 0 | if (!first_piece && i.deadline > now |
10978 | 0 | + milliseconds(m_average_piece_time + m_piece_time_deviation * 4 + 1000)) |
10979 | 0 | { |
10980 | | // don't request pieces whose deadline is too far in the future |
10981 | | // this is one of the termination conditions. We don't want to |
10982 | | // send requests for all pieces in the torrent right away |
10983 | | #if TORRENT_DEBUG_STREAMING > 0 |
10984 | | std::printf("reached deadline horizon [%f + %f * 4 + 1]\n" |
10985 | | , m_average_piece_time / 1000.f |
10986 | | , m_piece_time_deviation / 1000.f); |
10987 | | #endif |
10988 | 0 | break; |
10989 | 0 | } |
10990 | 0 | first_piece = false; |
10991 | |
|
10992 | 0 | piece_picker::downloading_piece pi; |
10993 | 0 | m_picker->piece_info(i.piece, pi); |
10994 | | |
10995 | | // the number of "times" this piece has timed out. |
10996 | 0 | int timed_out = 0; |
10997 | |
|
10998 | 0 | int const blocks_in_piece = m_picker->blocks_in_piece(i.piece); |
10999 | |
|
11000 | | #if TORRENT_DEBUG_STREAMING > 0 |
11001 | | i.timed_out = timed_out; |
11002 | | #endif |
11003 | 0 | int const free_to_request = blocks_in_piece |
11004 | 0 | - pi.finished - pi.writing - pi.requested; |
11005 | |
|
11006 | 0 | if (free_to_request == 0) |
11007 | 0 | { |
11008 | 0 | if (i.last_requested == min_time()) |
11009 | 0 | i.last_requested = now; |
11010 | | |
11011 | | // if it's been more than half of the typical download time |
11012 | | // of a piece since we requested the last block, allow |
11013 | | // one more request per block |
11014 | 0 | if (m_average_piece_time > 0) |
11015 | 0 | timed_out = int(total_milliseconds(now - i.last_requested) |
11016 | 0 | / std::max(int(m_average_piece_time + m_piece_time_deviation / 2), 1)); |
11017 | |
|
11018 | | #if TORRENT_DEBUG_STREAMING > 0 |
11019 | | i.timed_out = timed_out; |
11020 | | #endif |
11021 | | // every block in this piece is already requested |
11022 | | // there's no need to consider this piece, unless it |
11023 | | // appears to be stalled. |
11024 | 0 | if (pi.requested == 0 || timed_out == 0) |
11025 | 0 | { |
11026 | | #if TORRENT_DEBUG_STREAMING > 1 |
11027 | | std::printf("skipping %d (full) [req: %d timed_out: %d ]\n" |
11028 | | , i.piece, pi.requested |
11029 | | , timed_out); |
11030 | | #endif |
11031 | | |
11032 | | // if requested is 0, it means all blocks have been received, and |
11033 | | // we're just waiting for it to flush them to disk. |
11034 | | // if last_requested is recent enough, we should give it some |
11035 | | // more time |
11036 | | // skip to the next piece |
11037 | 0 | continue; |
11038 | 0 | } |
11039 | | |
11040 | | // it's been too long since we requested the last block from |
11041 | | // this piece. Allow re-requesting blocks from this piece |
11042 | | #if TORRENT_DEBUG_STREAMING > 1 |
11043 | | std::printf("timed out [average-piece-time: %d ms ]\n" |
11044 | | , m_average_piece_time); |
11045 | | #endif |
11046 | 0 | } |
11047 | | |
11048 | | // pick all blocks for this piece. the peers list is kept up to date |
11049 | | // and sorted. when we issue a request to a peer, its download queue |
11050 | | // time will increase and it may need to be bumped in the peers list, |
11051 | | // since it's ordered by download queue time |
11052 | 0 | pick_time_critical_block(peers, ignore_peers |
11053 | 0 | , peers_with_requests |
11054 | 0 | , pi, &i, m_picker.get() |
11055 | 0 | , blocks_in_piece, timed_out); |
11056 | | |
11057 | | // put back the peers we ignored into the peer list for the next piece |
11058 | 0 | if (!ignore_peers.empty()) |
11059 | 0 | { |
11060 | 0 | peers.insert(peers.begin(), ignore_peers.begin(), ignore_peers.end()); |
11061 | 0 | ignore_peers.clear(); |
11062 | | |
11063 | | // TODO: instead of resorting the whole list, insert the peers |
11064 | | // directly into the right place |
11065 | 0 | std::sort(peers.begin(), peers.end() |
11066 | 0 | , [] (peer_connection const* lhs, peer_connection const* rhs) |
11067 | 0 | { return lhs->download_queue_time(16*1024) < rhs->download_queue_time(16*1024); }); |
11068 | 0 | } |
11069 | | |
11070 | | // if this peer's download time exceeds 2 seconds, we're done. |
11071 | | // We don't want to build unreasonably long request queues |
11072 | 0 | if (!peers.empty() && peers[0]->download_queue_time() > milliseconds(2000)) |
11073 | 0 | break; |
11074 | 0 | } |
11075 | | |
11076 | | // commit all the time critical requests |
11077 | 0 | for (auto p : peers_with_requests) |
11078 | 0 | { |
11079 | 0 | p->send_block_requests(); |
11080 | 0 | } |
11081 | 0 | } |
11082 | | #endif // TORRENT_DISABLE_STREAMING |
11083 | | |
11084 | | std::set<std::string> torrent::web_seeds(web_seed_entry::type_t const type) const |
11085 | 0 | { |
11086 | 0 | TORRENT_ASSERT(is_single_thread()); |
11087 | 0 | std::set<std::string> ret; |
11088 | 0 | for (auto const& s : m_web_seeds) |
11089 | 0 | { |
11090 | 0 | if (s.peer_info.banned) continue; |
11091 | 0 | if (s.removed) continue; |
11092 | 0 | if (s.type != type) continue; |
11093 | 0 | ret.insert(s.url); |
11094 | 0 | } |
11095 | 0 | return ret; |
11096 | 0 | } |
11097 | | |
11098 | | void torrent::remove_web_seed(std::string const& url, web_seed_entry::type_t const type) |
11099 | 0 | { |
11100 | 0 | auto const i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() |
11101 | 0 | , [&] (web_seed_t const& w) { return w.url == url && w.type == type; }); |
11102 | |
|
11103 | 0 | if (i != m_web_seeds.end()) |
11104 | 0 | { |
11105 | 0 | if (!i->ephemeral) |
11106 | 0 | set_need_save_resume(torrent_handle::if_metadata_changed); |
11107 | 0 | remove_web_seed_iter(i); |
11108 | 0 | } |
11109 | 0 | } |
11110 | | |
11111 | | void torrent::remove_web_seed_conn(peer_connection* p) |
11112 | 0 | { |
11113 | 0 | auto const i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() |
11114 | 0 | , [p] (web_seed_t const& ws) { return ws.peer_info.connection == p; }); |
11115 | |
|
11116 | 0 | TORRENT_ASSERT(i != m_web_seeds.end()); |
11117 | 0 | if (i == m_web_seeds.end()) return; |
11118 | 0 | remove_web_seed_iter(i); |
11119 | 0 | } |
11120 | | |
11121 | | void torrent::retry_web_seed(peer_connection* p, boost::optional<seconds32> const retry) |
11122 | 0 | { |
11123 | 0 | TORRENT_ASSERT(is_single_thread()); |
11124 | 0 | auto const i = std::find_if(m_web_seeds.begin(), m_web_seeds.end() |
11125 | 0 | , [p] (web_seed_t const& ws) { return ws.peer_info.connection == p; }); |
11126 | |
|
11127 | 0 | TORRENT_ASSERT(i != m_web_seeds.end()); |
11128 | 0 | if (i == m_web_seeds.end()) return; |
11129 | 0 | if (i->removed || i->disabled) return; |
11130 | 0 | i->retry = aux::time_now32() + value_or(retry, seconds32( |
11131 | 0 | settings().get_int(settings_pack::urlseed_wait_retry))); |
11132 | 0 | } |
11133 | | |
11134 | | torrent_state torrent::get_peer_list_state() |
11135 | 572 | { |
11136 | 572 | torrent_state ret; |
11137 | 572 | ret.is_finished = is_finished(); |
11138 | 572 | ret.allow_multiple_connections_per_ip = settings().get_bool(settings_pack::allow_multiple_connections_per_ip); |
11139 | 572 | ret.max_peerlist_size = is_paused() |
11140 | 572 | ? settings().get_int(settings_pack::max_paused_peerlist_size) |
11141 | 572 | : settings().get_int(settings_pack::max_peerlist_size); |
11142 | 572 | ret.min_reconnect_time = settings().get_int(settings_pack::min_reconnect_time); |
11143 | | |
11144 | 572 | ret.ip = m_ses.external_address(); |
11145 | 572 | ret.port = m_ses.listen_port(); |
11146 | 572 | ret.max_failcount = settings().get_int(settings_pack::max_failcount); |
11147 | 572 | return ret; |
11148 | 572 | } |
11149 | | |
11150 | | bool torrent::try_connect_peer() |
11151 | 0 | { |
11152 | 0 | TORRENT_ASSERT(is_single_thread()); |
11153 | 0 | TORRENT_ASSERT(want_peers()); |
11154 | |
|
11155 | 0 | torrent_state st = get_peer_list_state(); |
11156 | 0 | need_peer_list(); |
11157 | 0 | torrent_peer* p = m_peer_list->connect_one_peer(m_ses.session_time(), &st); |
11158 | 0 | peers_erased(st.erased); |
11159 | 0 | inc_stats_counter(counters::connection_attempt_loops, st.loop_counter); |
11160 | |
|
11161 | 0 | if (p == nullptr) |
11162 | 0 | { |
11163 | 0 | m_stats_counters.inc_stats_counter(counters::no_peer_connection_attempts); |
11164 | 0 | update_want_peers(); |
11165 | 0 | return false; |
11166 | 0 | } |
11167 | | |
11168 | 0 | if (!connect_to_peer(p)) |
11169 | 0 | { |
11170 | 0 | m_stats_counters.inc_stats_counter(counters::missed_connection_attempts); |
11171 | 0 | m_peer_list->inc_failcount(p); |
11172 | 0 | update_want_peers(); |
11173 | 0 | return false; |
11174 | 0 | } |
11175 | 0 | update_want_peers(); |
11176 | |
|
11177 | 0 | return true; |
11178 | 0 | } |
11179 | | |
11180 | | torrent_peer* torrent::add_peer(tcp::endpoint const& adr |
11181 | | , peer_source_flags_t const source, pex_flags_t flags) |
11182 | 0 | { |
11183 | 0 | TORRENT_ASSERT(is_single_thread()); |
11184 | |
|
11185 | 0 | TORRENT_ASSERT(info_hash().has_v2() || !(flags & pex_lt_v2)); |
11186 | |
|
11187 | 0 | if (m_apply_ip_filter |
11188 | 0 | && m_ip_filter |
11189 | 0 | && m_ip_filter->access(adr.address()) & ip_filter::blocked) |
11190 | 0 | { |
11191 | 0 | if (alerts().should_post<peer_blocked_alert>()) |
11192 | 0 | alerts().emplace_alert<peer_blocked_alert>(get_handle() |
11193 | 0 | , adr, peer_blocked_alert::ip_filter); |
11194 | |
|
11195 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
11196 | 0 | notify_extension_add_peer(adr, source, torrent_plugin::filtered); |
11197 | 0 | #endif |
11198 | 0 | return nullptr; |
11199 | 0 | } |
11200 | | |
11201 | 0 | if (m_ses.get_port_filter().access(adr.port()) & port_filter::blocked) |
11202 | 0 | { |
11203 | 0 | if (alerts().should_post<peer_blocked_alert>()) |
11204 | 0 | alerts().emplace_alert<peer_blocked_alert>(get_handle() |
11205 | 0 | , adr, peer_blocked_alert::port_filter); |
11206 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
11207 | 0 | notify_extension_add_peer(adr, source, torrent_plugin::filtered); |
11208 | 0 | #endif |
11209 | 0 | return nullptr; |
11210 | 0 | } |
11211 | | |
11212 | 0 | #if TORRENT_USE_I2P |
11213 | | // if this is an i2p torrent, and we don't allow mixed mode |
11214 | | // no regular peers should ever be added! |
11215 | 0 | if (!settings().get_bool(settings_pack::allow_i2p_mixed) && is_i2p()) |
11216 | 0 | { |
11217 | 0 | if (alerts().should_post<peer_blocked_alert>()) |
11218 | 0 | alerts().emplace_alert<peer_blocked_alert>(get_handle() |
11219 | 0 | , adr, peer_blocked_alert::i2p_mixed); |
11220 | 0 | return nullptr; |
11221 | 0 | } |
11222 | 0 | #endif |
11223 | | |
11224 | 0 | if (settings().get_bool(settings_pack::no_connect_privileged_ports) && adr.port() < 1024) |
11225 | 0 | { |
11226 | 0 | if (alerts().should_post<peer_blocked_alert>()) |
11227 | 0 | alerts().emplace_alert<peer_blocked_alert>(get_handle() |
11228 | 0 | , adr, peer_blocked_alert::privileged_ports); |
11229 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
11230 | 0 | notify_extension_add_peer(adr, source, torrent_plugin::filtered); |
11231 | 0 | #endif |
11232 | 0 | return nullptr; |
11233 | 0 | } |
11234 | | |
11235 | 0 | #ifndef TORRENT_DISABLE_DHT |
11236 | 0 | if (source != peer_info::resume_data) |
11237 | 0 | { |
11238 | | // try to send a DHT ping to this peer |
11239 | | // as well, to figure out if it supports |
11240 | | // DHT (uTorrent and BitComet don't |
11241 | | // advertise support) |
11242 | 0 | session().add_dht_node({adr.address(), adr.port()}); |
11243 | 0 | } |
11244 | 0 | #endif |
11245 | |
|
11246 | 0 | if (!torrent_file().info_hashes().has_v1()) |
11247 | 0 | flags |= pex_lt_v2; |
11248 | |
|
11249 | 0 | need_peer_list(); |
11250 | 0 | torrent_state st = get_peer_list_state(); |
11251 | 0 | torrent_peer* p = m_peer_list->add_peer(adr, source, flags, &st); |
11252 | 0 | peers_erased(st.erased); |
11253 | |
|
11254 | 0 | if (p) |
11255 | 0 | { |
11256 | 0 | state_updated(); |
11257 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
11258 | 0 | notify_extension_add_peer(adr, source |
11259 | 0 | , st.first_time_seen |
11260 | 0 | ? torrent_plugin::first_time |
11261 | 0 | : add_peer_flags_t{}); |
11262 | 0 | #endif |
11263 | 0 | } |
11264 | 0 | else |
11265 | 0 | { |
11266 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
11267 | 0 | notify_extension_add_peer(adr, source, torrent_plugin::filtered); |
11268 | 0 | #endif |
11269 | 0 | } |
11270 | 0 | update_want_peers(); |
11271 | 0 | state_updated(); |
11272 | 0 | return p; |
11273 | 0 | } |
11274 | | |
11275 | | bool torrent::ban_peer(torrent_peer* tp) |
11276 | 0 | { |
11277 | 0 | if (!settings().get_bool(settings_pack::ban_web_seeds) && tp->web_seed) |
11278 | 0 | return false; |
11279 | | |
11280 | 0 | need_peer_list(); |
11281 | 0 | if (!m_peer_list->ban_peer(tp)) return false; |
11282 | 0 | update_want_peers(); |
11283 | |
|
11284 | 0 | inc_stats_counter(counters::num_banned_peers); |
11285 | 0 | return true; |
11286 | 0 | } |
11287 | | |
11288 | | void torrent::set_seed(torrent_peer* p, bool const s) |
11289 | 2.88k | { |
11290 | 2.88k | if (bool(p->seed) == s) return; |
11291 | 68 | if (s) |
11292 | 34 | { |
11293 | 34 | TORRENT_ASSERT(m_num_seeds < 0xffff); |
11294 | 34 | ++m_num_seeds; |
11295 | 34 | } |
11296 | 34 | else |
11297 | 34 | { |
11298 | 34 | TORRENT_ASSERT(m_num_seeds > 0); |
11299 | 34 | --m_num_seeds; |
11300 | 34 | } |
11301 | | |
11302 | 68 | need_peer_list(); |
11303 | 68 | m_peer_list->set_seed(p, s); |
11304 | 68 | update_auto_sequential(); |
11305 | 68 | } |
11306 | | |
11307 | | void torrent::clear_failcount(torrent_peer* p) |
11308 | 282 | { |
11309 | 282 | need_peer_list(); |
11310 | 282 | m_peer_list->set_failcount(p, 0); |
11311 | 282 | update_want_peers(); |
11312 | 282 | } |
11313 | | |
11314 | | std::pair<peer_list::iterator, peer_list::iterator> torrent::find_peers(address const& a) |
11315 | 0 | { |
11316 | 0 | need_peer_list(); |
11317 | 0 | return m_peer_list->find_peers(a); |
11318 | 0 | } |
11319 | | |
11320 | | void torrent::update_peer_port(int const port, torrent_peer* p |
11321 | | , peer_source_flags_t const src) |
11322 | 8 | { |
11323 | 8 | need_peer_list(); |
11324 | 8 | torrent_state st = get_peer_list_state(); |
11325 | 8 | m_peer_list->update_peer_port(port, p, src, &st); |
11326 | 8 | peers_erased(st.erased); |
11327 | 8 | update_want_peers(); |
11328 | 8 | } |
11329 | | |
11330 | | // verify piece is used when checking resume data or when the user |
11331 | | // adds a piece |
11332 | | void torrent::verify_piece(piece_index_t const piece) |
11333 | 0 | { |
11334 | 0 | TORRENT_ASSERT(m_storage); |
11335 | 0 | TORRENT_ASSERT(!m_picker->is_hashing(piece)); |
11336 | | |
11337 | | // we just completed the piece, it should be flushed to disk |
11338 | 0 | disk_job_flags_t flags{}; |
11339 | |
|
11340 | 0 | auto const write_mode = settings().get_int(settings_pack::disk_io_write_mode); |
11341 | 0 | if (write_mode == settings_pack::write_through) |
11342 | 0 | flags |= disk_interface::flush_piece; |
11343 | 0 | else if (write_mode == settings_pack::disable_os_cache) |
11344 | 0 | flags |= disk_interface::flush_piece | disk_interface::volatile_read; |
11345 | 0 | if (torrent_file().info_hashes().has_v1()) |
11346 | 0 | flags |= disk_interface::v1_hash; |
11347 | |
|
11348 | 0 | aux::vector<sha256_hash> hashes; |
11349 | 0 | if (torrent_file().info_hashes().has_v2()) |
11350 | 0 | { |
11351 | 0 | hashes.resize(torrent_file().orig_files().blocks_in_piece2(piece)); |
11352 | 0 | } |
11353 | |
|
11354 | 0 | if (settings().get_bool(settings_pack::disable_hash_checks)) |
11355 | 0 | { |
11356 | | // short-circuit the hash check if it's disabled |
11357 | 0 | m_picker->started_hash_job(piece); |
11358 | 0 | on_piece_verified(std::move(hashes), piece, sha1_hash(), storage_error{}); |
11359 | 0 | return; |
11360 | 0 | } |
11361 | | |
11362 | 0 | span<sha256_hash> v2_span(hashes); |
11363 | 0 | m_ses.disk_thread().async_hash(m_storage, piece, v2_span, flags |
11364 | 0 | , [self = shared_from_this(), hashes1 = std::move(hashes)] |
11365 | 0 | (piece_index_t p, sha1_hash const& h, storage_error const& error) mutable |
11366 | 0 | { self->on_piece_verified(std::move(hashes1), p, h, error); }); |
11367 | 0 | m_picker->started_hash_job(piece); |
11368 | 0 | m_ses.deferred_submit_jobs(); |
11369 | 0 | } |
11370 | | |
11371 | | aux::announce_entry* torrent::find_tracker(std::string const& url) |
11372 | 0 | { |
11373 | 0 | auto i = std::find_if(m_trackers.begin(), m_trackers.end() |
11374 | 0 | , [&url](aux::announce_entry const& ae) { return ae.url == url; }); |
11375 | 0 | if (i == m_trackers.end()) return nullptr; |
11376 | 0 | return &*i; |
11377 | 0 | } |
11378 | | |
11379 | | void torrent::ip_filter_updated() |
11380 | 608 | { |
11381 | 608 | if (!m_apply_ip_filter) return; |
11382 | 608 | if (!m_peer_list) return; |
11383 | 0 | if (!m_ip_filter) return; |
11384 | | |
11385 | 0 | torrent_state st = get_peer_list_state(); |
11386 | 0 | std::vector<address> banned; |
11387 | 0 | m_peer_list->apply_ip_filter(*m_ip_filter, &st, banned); |
11388 | |
|
11389 | 0 | if (alerts().should_post<peer_blocked_alert>()) |
11390 | 0 | { |
11391 | 0 | for (auto const& addr : banned) |
11392 | 0 | alerts().emplace_alert<peer_blocked_alert>(get_handle() |
11393 | 0 | , tcp::endpoint(addr, 0) |
11394 | 0 | , peer_blocked_alert::ip_filter); |
11395 | 0 | } |
11396 | |
|
11397 | 0 | peers_erased(st.erased); |
11398 | 0 | } |
11399 | | |
11400 | | // this call will disconnect any peers whose remote port is < 1024 |
11401 | | void torrent::privileged_port_updated() |
11402 | 0 | { |
11403 | 0 | if (!m_peer_list) return; |
11404 | | |
11405 | 0 | port_filter ports; |
11406 | 0 | ports.add_rule(0, 1023, port_filter::blocked); |
11407 | |
|
11408 | 0 | torrent_state st = get_peer_list_state(); |
11409 | 0 | std::vector<tcp::endpoint> banned; |
11410 | 0 | m_peer_list->apply_port_filter(ports, &st, banned); |
11411 | |
|
11412 | 0 | if (alerts().should_post<peer_blocked_alert>()) |
11413 | 0 | { |
11414 | 0 | for (auto const& ep : banned) |
11415 | 0 | alerts().emplace_alert<peer_blocked_alert>(get_handle() |
11416 | 0 | , ep, peer_blocked_alert::privileged_ports); |
11417 | 0 | } |
11418 | |
|
11419 | 0 | peers_erased(st.erased); |
11420 | 0 | } |
11421 | | |
11422 | | void torrent::port_filter_updated() |
11423 | 0 | { |
11424 | 0 | if (!m_peer_list) return; |
11425 | | |
11426 | 0 | torrent_state st = get_peer_list_state(); |
11427 | 0 | std::vector<tcp::endpoint> banned; |
11428 | 0 | m_peer_list->apply_port_filter(m_ses.get_port_filter(), &st, banned); |
11429 | |
|
11430 | 0 | if (alerts().should_post<peer_blocked_alert>()) |
11431 | 0 | { |
11432 | 0 | for (auto const& ep : banned) |
11433 | 0 | alerts().emplace_alert<peer_blocked_alert>(get_handle() |
11434 | 0 | , ep, peer_blocked_alert::port_filter); |
11435 | 0 | } |
11436 | |
|
11437 | 0 | peers_erased(st.erased); |
11438 | 0 | } |
11439 | | |
11440 | | // this is called when torrent_peers are removed from the peer_list |
11441 | | // (peer-list). It removes any references we may have to those torrent_peers, |
11442 | | // so we don't leave then dangling |
11443 | | void torrent::peers_erased(std::vector<torrent_peer*> const& peers) |
11444 | 572 | { |
11445 | 572 | if (!has_picker()) return; |
11446 | | |
11447 | 572 | for (auto const p : peers) |
11448 | 0 | { |
11449 | 0 | m_picker->clear_peer(p); |
11450 | 0 | } |
11451 | | #if TORRENT_USE_INVARIANT_CHECKS |
11452 | | m_picker->check_peers(); |
11453 | | #endif |
11454 | 572 | } |
11455 | | |
11456 | | #if TORRENT_ABI_VERSION == 1 |
11457 | | #if !TORRENT_NO_FPU |
11458 | | void torrent::file_progress_float(aux::vector<float, file_index_t>& fp) |
11459 | 0 | { |
11460 | 0 | TORRENT_ASSERT(is_single_thread()); |
11461 | 0 | if (!valid_metadata()) |
11462 | 0 | { |
11463 | 0 | fp.clear(); |
11464 | 0 | return; |
11465 | 0 | } |
11466 | | |
11467 | 0 | fp.resize(m_torrent_file->num_files(), 1.f); |
11468 | 0 | if (is_seed()) return; |
11469 | | |
11470 | 0 | aux::vector<std::int64_t, file_index_t> progress; |
11471 | 0 | file_progress(progress, {}); |
11472 | 0 | file_storage const& fs = m_torrent_file->files(); |
11473 | 0 | for (auto const i : fs.file_range()) |
11474 | 0 | { |
11475 | 0 | std::int64_t file_size = m_torrent_file->files().file_size(i); |
11476 | 0 | if (file_size == 0) fp[i] = 1.f; |
11477 | 0 | else fp[i] = float(progress[i]) / float(file_size); |
11478 | 0 | } |
11479 | 0 | } |
11480 | | #endif |
11481 | | #endif // TORRENT_ABI_VERSION |
11482 | | |
11483 | | void torrent::post_file_progress(file_progress_flags_t const flags) |
11484 | 0 | { |
11485 | 0 | aux::vector<std::int64_t, file_index_t> fp; |
11486 | 0 | file_progress(fp, flags); |
11487 | 0 | alerts().emplace_alert<file_progress_alert>(get_handle(), std::move(fp)); |
11488 | 0 | } |
11489 | | |
11490 | | void torrent::file_progress(aux::vector<std::int64_t, file_index_t>& fp |
11491 | | , file_progress_flags_t const flags) |
11492 | 0 | { |
11493 | 0 | TORRENT_ASSERT(is_single_thread()); |
11494 | 0 | if (!valid_metadata()) |
11495 | 0 | { |
11496 | 0 | fp.clear(); |
11497 | 0 | return; |
11498 | 0 | } |
11499 | | |
11500 | | // if we're a seed, we don't have an m_file_progress anyway |
11501 | | // since we don't need one. We know we have all files |
11502 | | // just fill in the full file sizes as a shortcut |
11503 | 0 | if (is_seed()) |
11504 | 0 | { |
11505 | 0 | fp.resize(m_torrent_file->num_files()); |
11506 | 0 | file_storage const& fs = m_torrent_file->files(); |
11507 | 0 | for (auto const i : fs.file_range()) |
11508 | 0 | fp[i] = fs.file_size(i); |
11509 | 0 | return; |
11510 | 0 | } |
11511 | | |
11512 | 0 | if (num_have() == 0 || m_file_progress.empty()) |
11513 | 0 | { |
11514 | | // if we don't have any pieces, just return zeroes |
11515 | 0 | fp.clear(); |
11516 | 0 | fp.resize(m_torrent_file->num_files(), 0); |
11517 | 0 | } |
11518 | 0 | else |
11519 | 0 | { |
11520 | 0 | m_file_progress.export_progress(fp); |
11521 | 0 | } |
11522 | |
|
11523 | 0 | if (flags & torrent_handle::piece_granularity) |
11524 | 0 | return; |
11525 | | |
11526 | 0 | if (!has_picker()) |
11527 | 0 | return; |
11528 | | |
11529 | 0 | std::vector<piece_picker::downloading_piece> q = m_picker->get_download_queue(); |
11530 | |
|
11531 | 0 | file_storage const& fs = m_torrent_file->files(); |
11532 | 0 | for (auto const& dp : q) |
11533 | 0 | { |
11534 | 0 | if (have_piece(dp.index)) |
11535 | 0 | { |
11536 | | // in this case this piece has already been accounted for in fp |
11537 | 0 | continue; |
11538 | 0 | } |
11539 | | |
11540 | 0 | std::int64_t offset = std::int64_t(static_cast<int>(dp.index)) |
11541 | 0 | * m_torrent_file->piece_length(); |
11542 | 0 | file_index_t file = fs.file_index_at_offset(offset); |
11543 | 0 | int idx = -1; |
11544 | 0 | for (auto const& info : m_picker->blocks_for_piece(dp)) |
11545 | 0 | { |
11546 | 0 | ++idx; |
11547 | 0 | TORRENT_ASSERT(file < fs.end_file()); |
11548 | 0 | TORRENT_ASSERT(offset == std::int64_t(static_cast<int>(dp.index)) |
11549 | 0 | * m_torrent_file->piece_length() |
11550 | 0 | + idx * block_size()); |
11551 | 0 | TORRENT_ASSERT(offset < m_torrent_file->total_size()); |
11552 | 0 | while (offset >= fs.file_offset(file) + fs.file_size(file)) |
11553 | 0 | { |
11554 | 0 | ++file; |
11555 | 0 | } |
11556 | 0 | TORRENT_ASSERT(file < fs.end_file()); |
11557 | |
|
11558 | 0 | std::int64_t block = block_size(); |
11559 | |
|
11560 | 0 | if (info.state == piece_picker::block_info::state_none) |
11561 | 0 | { |
11562 | 0 | offset += block; |
11563 | 0 | continue; |
11564 | 0 | } |
11565 | | |
11566 | 0 | if (info.state == piece_picker::block_info::state_requested) |
11567 | 0 | { |
11568 | 0 | block = 0; |
11569 | 0 | torrent_peer* p = info.peer; |
11570 | 0 | if (p != nullptr && p->connection) |
11571 | 0 | { |
11572 | 0 | auto* peer = static_cast<peer_connection*>(p->connection); |
11573 | 0 | auto pbp = peer->downloading_piece_progress(); |
11574 | 0 | if (pbp.piece_index == dp.index && pbp.block_index == idx) |
11575 | 0 | block = pbp.bytes_downloaded; |
11576 | 0 | TORRENT_ASSERT(block <= block_size()); |
11577 | 0 | } |
11578 | |
|
11579 | 0 | if (block == 0) |
11580 | 0 | { |
11581 | 0 | offset += block_size(); |
11582 | 0 | continue; |
11583 | 0 | } |
11584 | 0 | } |
11585 | | |
11586 | 0 | if (offset + block > fs.file_offset(file) + fs.file_size(file)) |
11587 | 0 | { |
11588 | 0 | std::int64_t left_over = block_size() - block; |
11589 | | // split the block on multiple files |
11590 | 0 | while (block > 0) |
11591 | 0 | { |
11592 | 0 | TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file)); |
11593 | 0 | std::int64_t const slice = std::min(fs.file_offset(file) + fs.file_size(file) - offset |
11594 | 0 | , block); |
11595 | 0 | TORRENT_ASSERT(fp[file] <= fs.file_size(file) - slice); |
11596 | 0 | fp[file] += slice; |
11597 | 0 | offset += slice; |
11598 | 0 | block -= slice; |
11599 | 0 | TORRENT_ASSERT(offset <= fs.file_offset(file) + fs.file_size(file)); |
11600 | 0 | if (offset == fs.file_offset(file) + fs.file_size(file)) |
11601 | 0 | { |
11602 | 0 | ++file; |
11603 | 0 | if (file == fs.end_file()) |
11604 | 0 | { |
11605 | 0 | offset += block; |
11606 | 0 | break; |
11607 | 0 | } |
11608 | 0 | } |
11609 | 0 | } |
11610 | 0 | offset += left_over; |
11611 | 0 | TORRENT_ASSERT(offset == std::int64_t(static_cast<int>(dp.index)) |
11612 | 0 | * m_torrent_file->piece_length() |
11613 | 0 | + (idx + 1) * block_size()); |
11614 | 0 | } |
11615 | 0 | else |
11616 | 0 | { |
11617 | 0 | TORRENT_ASSERT(fp[file] <= fs.file_size(file) - block); |
11618 | 0 | fp[file] += block; |
11619 | 0 | offset += block_size(); |
11620 | 0 | } |
11621 | 0 | TORRENT_ASSERT(file <= fs.end_file()); |
11622 | 0 | } |
11623 | 0 | } |
11624 | 0 | } |
11625 | | |
11626 | | void torrent::new_external_ip() |
11627 | 1 | { |
11628 | 1 | if (m_peer_list) m_peer_list->clear_peer_prio(); |
11629 | 1 | } |
11630 | | |
11631 | | void torrent::stop_when_ready(bool const b) |
11632 | 0 | { |
11633 | 0 | m_stop_when_ready = b; |
11634 | | |
11635 | | // to avoid race condition, if we're already in a downloading state, |
11636 | | // trigger the stop-when-ready logic immediately. |
11637 | 0 | if (m_stop_when_ready && is_downloading_state(m_state)) |
11638 | 0 | { |
11639 | | #ifndef TORRENT_DISABLE_LOGGING |
11640 | | debug_log("stop_when_ready triggered"); |
11641 | | #endif |
11642 | 0 | auto_managed(false); |
11643 | 0 | pause(); |
11644 | 0 | m_stop_when_ready = false; |
11645 | 0 | } |
11646 | 0 | } |
11647 | | |
11648 | | void torrent::set_state(torrent_status::state_t const s) |
11649 | 3.13k | { |
11650 | 3.13k | TORRENT_ASSERT(is_single_thread()); |
11651 | 3.13k | TORRENT_ASSERT(s != 0); // this state isn't used anymore |
11652 | | |
11653 | 3.13k | #if TORRENT_USE_ASSERTS |
11654 | | |
11655 | 3.13k | if (s == torrent_status::seeding) |
11656 | 183 | { |
11657 | 183 | TORRENT_ASSERT(is_seed()); |
11658 | 183 | TORRENT_ASSERT(is_finished()); |
11659 | 183 | } |
11660 | 3.13k | if (s == torrent_status::finished) |
11661 | 553 | TORRENT_ASSERT(is_finished()); |
11662 | 3.13k | if (s == torrent_status::downloading && m_state == torrent_status::finished) |
11663 | 0 | TORRENT_ASSERT(!is_finished()); |
11664 | 3.13k | #endif |
11665 | | |
11666 | 3.13k | if (int(m_state) == s) return; |
11667 | | |
11668 | 1.25k | if (m_ses.alerts().should_post<state_changed_alert>()) |
11669 | 2 | { |
11670 | 2 | m_ses.alerts().emplace_alert<state_changed_alert>(get_handle() |
11671 | 2 | , s, static_cast<torrent_status::state_t>(m_state)); |
11672 | 2 | } |
11673 | | |
11674 | 1.25k | if (s == torrent_status::finished |
11675 | 1.25k | && alerts().should_post<torrent_finished_alert>()) |
11676 | 0 | { |
11677 | 0 | alerts().emplace_alert<torrent_finished_alert>( |
11678 | 0 | get_handle()); |
11679 | 0 | } |
11680 | | |
11681 | 1.25k | bool const trigger_stop = m_stop_when_ready |
11682 | 1.25k | && !is_downloading_state(m_state) |
11683 | 1.25k | && is_downloading_state(s); |
11684 | | |
11685 | | // we need to update the state before calling pause(), because otherwise |
11686 | | // it may think we're still checking files, even though we're just |
11687 | | // leaving that state |
11688 | 1.25k | m_state = s; |
11689 | | |
11690 | 1.25k | update_gauge(); |
11691 | 1.25k | update_want_peers(); |
11692 | 1.25k | update_want_tick(); |
11693 | 1.25k | update_state_list(); |
11694 | | |
11695 | 1.25k | state_updated(); |
11696 | | |
11697 | | #ifndef TORRENT_DISABLE_LOGGING |
11698 | | debug_log("set_state() %d", m_state); |
11699 | | #endif |
11700 | | |
11701 | 1.25k | if (trigger_stop) |
11702 | 446 | { |
11703 | | #ifndef TORRENT_DISABLE_LOGGING |
11704 | | debug_log("stop_when_ready triggered"); |
11705 | | #endif |
11706 | | // stop_when_ready is set, and we're transitioning from a downloading |
11707 | | // state to a non-downloading state. pause the torrent. Note that |
11708 | | // "downloading" is defined broadly to include any state where we |
11709 | | // either upload or download (for the purpose of this flag). |
11710 | 446 | auto_managed(false); |
11711 | 446 | pause(); |
11712 | 446 | m_stop_when_ready = false; |
11713 | 446 | } |
11714 | | |
11715 | 1.25k | #ifndef TORRENT_DISABLE_EXTENSIONS |
11716 | 1.25k | for (auto& ext : m_extensions) |
11717 | 6 | { |
11718 | 6 | ext->on_state(state()); |
11719 | 6 | } |
11720 | 1.25k | #endif |
11721 | 1.25k | } |
11722 | | |
11723 | | #ifndef TORRENT_DISABLE_EXTENSIONS |
11724 | | void torrent::notify_extension_add_peer(tcp::endpoint const& ip |
11725 | | , peer_source_flags_t const src, add_peer_flags_t const flags) |
11726 | 0 | { |
11727 | 0 | for (auto& ext : m_extensions) |
11728 | 0 | { |
11729 | 0 | ext->on_add_peer(ip, src, flags); |
11730 | 0 | } |
11731 | 0 | } |
11732 | | #endif |
11733 | | |
11734 | | void torrent::state_updated() |
11735 | 10.8k | { |
11736 | | // if this fails, this function is probably called |
11737 | | // from within the torrent constructor, which it |
11738 | | // shouldn't be. Whichever function ends up calling |
11739 | | // this should probably be moved to torrent::start() |
11740 | 10.8k | TORRENT_ASSERT(shared_from_this()); |
11741 | | |
11742 | | // we can't call state_updated() while the session |
11743 | | // is building the status update alert |
11744 | 10.8k | TORRENT_ASSERT(!m_ses.is_posting_torrent_updates()); |
11745 | | |
11746 | | // we're not subscribing to this torrent, don't add it |
11747 | 10.8k | if (!m_state_subscription) return; |
11748 | | |
11749 | 4.18k | aux::vector<torrent*>& list = m_ses.torrent_list(aux::session_interface::torrent_state_updates); |
11750 | | |
11751 | | // if it has already been updated this round, no need to |
11752 | | // add it to the list twice |
11753 | 4.18k | if (m_links[aux::session_interface::torrent_state_updates].in_list()) |
11754 | 3.60k | { |
11755 | | #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS |
11756 | | TORRENT_ASSERT(find(list.begin(), list.end(), this) != list.end()); |
11757 | | #endif |
11758 | 3.60k | return; |
11759 | 3.60k | } |
11760 | | |
11761 | | #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS |
11762 | | TORRENT_ASSERT(find(list.begin(), list.end(), this) == list.end()); |
11763 | | #endif |
11764 | | |
11765 | 584 | m_links[aux::session_interface::torrent_state_updates].insert(list, this); |
11766 | 584 | } |
11767 | | |
11768 | | void torrent::post_status(status_flags_t const flags) |
11769 | 0 | { |
11770 | 0 | std::vector<torrent_status> s; |
11771 | 0 | s.resize(1); |
11772 | 0 | status(&s.front(), flags); |
11773 | 0 | m_ses.alerts().emplace_alert<state_update_alert>(std::move(s)); |
11774 | 0 | } |
11775 | | |
11776 | | void torrent::status(torrent_status* st, status_flags_t const flags) |
11777 | 0 | { |
11778 | 0 | INVARIANT_CHECK; |
11779 | |
|
11780 | 0 | time_point32 const now = aux::time_now32(); |
11781 | |
|
11782 | 0 | st->handle = get_handle(); |
11783 | 0 | st->info_hashes = info_hash(); |
11784 | 0 | #if TORRENT_ABI_VERSION < 3 |
11785 | 0 | st->info_hash = info_hash().get_best(); |
11786 | 0 | #endif |
11787 | 0 | #if TORRENT_ABI_VERSION == 1 |
11788 | 0 | st->is_loaded = true; |
11789 | 0 | #endif |
11790 | |
|
11791 | 0 | if (flags & torrent_handle::query_name) |
11792 | 0 | st->name = name(); |
11793 | |
|
11794 | 0 | if (flags & torrent_handle::query_save_path) |
11795 | 0 | st->save_path = save_path(); |
11796 | |
|
11797 | 0 | if (flags & torrent_handle::query_torrent_file) |
11798 | 0 | st->torrent_file = m_torrent_file; |
11799 | |
|
11800 | 0 | st->has_incoming = m_has_incoming; |
11801 | 0 | st->errc = m_error; |
11802 | 0 | st->error_file = m_error_file; |
11803 | |
|
11804 | 0 | #if TORRENT_ABI_VERSION == 1 |
11805 | 0 | if (m_error) st->error = convert_from_native(m_error.message()) |
11806 | 0 | + ": " + resolve_filename(m_error_file); |
11807 | 0 | st->seed_mode = m_seed_mode; |
11808 | 0 | #endif |
11809 | 0 | st->moving_storage = m_moving_storage; |
11810 | |
|
11811 | 0 | st->announcing_to_trackers = m_announce_to_trackers; |
11812 | 0 | st->announcing_to_lsd = m_announce_to_lsd; |
11813 | 0 | st->announcing_to_dht = m_announce_to_dht; |
11814 | 0 | #if TORRENT_ABI_VERSION == 1 |
11815 | 0 | st->stop_when_ready = m_stop_when_ready; |
11816 | 0 | #endif |
11817 | |
|
11818 | 0 | st->added_time = m_added_time; |
11819 | 0 | st->completed_time = m_completed_time; |
11820 | |
|
11821 | 0 | #if TORRENT_ABI_VERSION == 1 |
11822 | 0 | st->last_scrape = static_cast<int>(total_seconds(now - m_last_scrape)); |
11823 | 0 | #endif |
11824 | |
|
11825 | 0 | #if TORRENT_ABI_VERSION == 1 |
11826 | 0 | #ifndef TORRENT_DISABLE_SHARE_MODE |
11827 | 0 | st->share_mode = m_share_mode; |
11828 | | #else |
11829 | | st->share_mode = false; |
11830 | | #endif |
11831 | 0 | st->upload_mode = m_upload_mode; |
11832 | 0 | #endif |
11833 | 0 | st->up_bandwidth_queue = 0; |
11834 | 0 | st->down_bandwidth_queue = 0; |
11835 | 0 | #if TORRENT_ABI_VERSION == 1 |
11836 | 0 | st->priority = priority(); |
11837 | 0 | #endif |
11838 | |
|
11839 | 0 | st->num_peers = num_peers() - m_num_connecting; |
11840 | |
|
11841 | 0 | st->list_peers = m_peer_list ? m_peer_list->num_peers() : 0; |
11842 | 0 | st->list_seeds = m_peer_list ? m_peer_list->num_seeds() : 0; |
11843 | 0 | st->connect_candidates = m_peer_list ? m_peer_list->num_connect_candidates() : 0; |
11844 | 0 | TORRENT_ASSERT(st->connect_candidates >= 0); |
11845 | 0 | st->seed_rank = seed_rank(settings()); |
11846 | |
|
11847 | 0 | st->all_time_upload = m_total_uploaded; |
11848 | 0 | st->all_time_download = m_total_downloaded; |
11849 | | |
11850 | | // activity time |
11851 | 0 | #if TORRENT_ABI_VERSION == 1 |
11852 | 0 | st->finished_time = int(total_seconds(finished_time())); |
11853 | 0 | st->active_time = int(total_seconds(active_time())); |
11854 | 0 | st->seeding_time = int(total_seconds(seeding_time())); |
11855 | |
|
11856 | 0 | time_point32 const unset{seconds32(0)}; |
11857 | |
|
11858 | 0 | st->time_since_upload = m_last_upload == unset ? -1 |
11859 | 0 | : static_cast<int>(total_seconds(now - m_last_upload)); |
11860 | 0 | st->time_since_download = m_last_download == unset ? -1 |
11861 | 0 | : static_cast<int>(total_seconds(now - m_last_download)); |
11862 | 0 | #endif |
11863 | |
|
11864 | 0 | st->finished_duration = finished_time(); |
11865 | 0 | st->active_duration = active_time(); |
11866 | 0 | st->seeding_duration = seeding_time(); |
11867 | |
|
11868 | 0 | st->last_upload = m_last_upload; |
11869 | 0 | st->last_download = m_last_download; |
11870 | |
|
11871 | 0 | st->storage_mode = static_cast<storage_mode_t>(m_storage_mode); |
11872 | |
|
11873 | 0 | st->num_complete = (m_complete == 0xffffff) ? -1 : m_complete; |
11874 | 0 | st->num_incomplete = (m_incomplete == 0xffffff) ? -1 : m_incomplete; |
11875 | 0 | #if TORRENT_ABI_VERSION == 1 |
11876 | 0 | st->paused = is_torrent_paused(); |
11877 | 0 | st->auto_managed = m_auto_managed; |
11878 | 0 | st->sequential_download = m_sequential_download; |
11879 | 0 | #endif |
11880 | 0 | st->is_seeding = is_seed(); |
11881 | 0 | st->is_finished = is_finished(); |
11882 | 0 | #if TORRENT_ABI_VERSION == 1 |
11883 | 0 | #ifndef TORRENT_DISABLE_SUPERSEEDING |
11884 | 0 | st->super_seeding = m_super_seeding; |
11885 | 0 | #endif |
11886 | 0 | #endif |
11887 | 0 | st->has_metadata = valid_metadata(); |
11888 | 0 | bytes_done(*st, flags); |
11889 | 0 | TORRENT_ASSERT(st->total_wanted_done >= 0); |
11890 | 0 | TORRENT_ASSERT(st->total_done >= st->total_wanted_done); |
11891 | | |
11892 | | // payload transfer |
11893 | 0 | st->total_payload_download = m_stat.total_payload_download(); |
11894 | 0 | st->total_payload_upload = m_stat.total_payload_upload(); |
11895 | | |
11896 | | // total transfer |
11897 | 0 | st->total_download = m_stat.total_payload_download() |
11898 | 0 | + m_stat.total_protocol_download(); |
11899 | 0 | st->total_upload = m_stat.total_payload_upload() |
11900 | 0 | + m_stat.total_protocol_upload(); |
11901 | | |
11902 | | // failed bytes |
11903 | 0 | st->total_failed_bytes = m_total_failed_bytes; |
11904 | 0 | st->total_redundant_bytes = m_total_redundant_bytes; |
11905 | | |
11906 | | // transfer rate |
11907 | 0 | st->download_rate = m_stat.download_rate(); |
11908 | 0 | st->upload_rate = m_stat.upload_rate(); |
11909 | 0 | st->download_payload_rate = m_stat.download_payload_rate(); |
11910 | 0 | st->upload_payload_rate = m_stat.upload_payload_rate(); |
11911 | |
|
11912 | 0 | if (is_paused() || m_tracker_timer.expiry() < now) |
11913 | 0 | st->next_announce = seconds(0); |
11914 | 0 | else |
11915 | 0 | st->next_announce = m_tracker_timer.expiry() - now; |
11916 | |
|
11917 | 0 | if (st->next_announce.count() < 0) |
11918 | 0 | st->next_announce = seconds(0); |
11919 | |
|
11920 | 0 | #if TORRENT_ABI_VERSION == 1 |
11921 | 0 | st->announce_interval = seconds(0); |
11922 | 0 | #endif |
11923 | |
|
11924 | 0 | st->current_tracker.clear(); |
11925 | 0 | if (m_last_working_tracker >= 0) |
11926 | 0 | { |
11927 | 0 | TORRENT_ASSERT(m_last_working_tracker < m_trackers.end_index()); |
11928 | 0 | const int i = m_last_working_tracker; |
11929 | 0 | st->current_tracker = m_trackers[i].url; |
11930 | 0 | } |
11931 | 0 | else |
11932 | 0 | { |
11933 | 0 | #ifdef __clang__ |
11934 | 0 | #pragma clang diagnostic push |
11935 | 0 | #pragma clang diagnostic ignored "-Wmissing-braces" |
11936 | 0 | #endif |
11937 | 0 | aux::array<bool const, num_protocols, protocol_version> const supports_protocol{ |
11938 | 0 | { |
11939 | 0 | m_info_hash.has_v1(), |
11940 | 0 | m_info_hash.has_v2() |
11941 | 0 | }}; |
11942 | 0 | #ifdef __clang__ |
11943 | 0 | #pragma clang diagnostic pop |
11944 | 0 | #endif |
11945 | |
|
11946 | 0 | for (auto const& t : m_trackers) |
11947 | 0 | { |
11948 | 0 | if (std::any_of(t.endpoints.begin(), t.endpoints.end() |
11949 | 0 | , [supports_protocol](aux::announce_endpoint const& aep) { |
11950 | 0 | for (protocol_version const ih : all_versions) |
11951 | 0 | { |
11952 | 0 | if (supports_protocol[ih] && aep.info_hashes[ih].updating) |
11953 | 0 | return false; |
11954 | 0 | } |
11955 | 0 | return true; |
11956 | 0 | })) continue; |
11957 | 0 | if (!t.verified) continue; |
11958 | 0 | st->current_tracker = t.url; |
11959 | 0 | break; |
11960 | 0 | } |
11961 | 0 | } |
11962 | |
|
11963 | 0 | if ((flags & torrent_handle::query_verified_pieces)) |
11964 | 0 | { |
11965 | 0 | st->verified_pieces = m_verified; |
11966 | 0 | } |
11967 | |
|
11968 | 0 | st->num_uploads = m_num_uploads; |
11969 | 0 | st->uploads_limit = m_max_uploads == (1 << 24) - 1 ? -1 : m_max_uploads; |
11970 | 0 | st->num_connections = num_peers(); |
11971 | 0 | st->connections_limit = m_max_connections == (1 << 24) - 1 ? -1 : m_max_connections; |
11972 | | // if we don't have any metadata, stop here |
11973 | |
|
11974 | 0 | st->queue_position = queue_position(); |
11975 | 0 | st->need_save_resume = bool(m_need_save_resume_data); |
11976 | 0 | #if TORRENT_ABI_VERSION == 1 |
11977 | 0 | st->ip_filter_applies = m_apply_ip_filter; |
11978 | 0 | #endif |
11979 | |
|
11980 | 0 | st->state = static_cast<torrent_status::state_t>(m_state); |
11981 | 0 | st->flags = this->flags(); |
11982 | |
|
11983 | 0 | #if TORRENT_USE_ASSERTS |
11984 | 0 | if (st->state == torrent_status::finished |
11985 | 0 | || st->state == torrent_status::seeding) |
11986 | 0 | { |
11987 | | // it may be tempting to assume that st->is_finished == true here, but |
11988 | | // this assumption does not always hold. We transition to "finished" |
11989 | | // when we receive the last block of the last piece, which is before |
11990 | | // the hash check comes back. "is_finished" is set to true once all the |
11991 | | // pieces have been hash checked. So, there's a short window where it |
11992 | | // doesn't hold. |
11993 | 0 | } |
11994 | 0 | #endif |
11995 | |
|
11996 | 0 | if (!valid_metadata()) |
11997 | 0 | { |
11998 | 0 | st->state = torrent_status::downloading_metadata; |
11999 | 0 | st->progress_ppm = m_progress_ppm; |
12000 | 0 | #if !TORRENT_NO_FPU |
12001 | 0 | st->progress = m_progress_ppm / 1000000.f; |
12002 | 0 | #endif |
12003 | 0 | st->block_size = 0; |
12004 | 0 | return; |
12005 | 0 | } |
12006 | | |
12007 | 0 | st->block_size = block_size(); |
12008 | |
|
12009 | 0 | if (m_state == torrent_status::checking_files) |
12010 | 0 | { |
12011 | 0 | st->progress_ppm = m_progress_ppm; |
12012 | 0 | #if !TORRENT_NO_FPU |
12013 | 0 | st->progress = m_progress_ppm / 1000000.f; |
12014 | 0 | #endif |
12015 | 0 | } |
12016 | 0 | else if (st->total_wanted == 0) |
12017 | 0 | { |
12018 | 0 | st->progress_ppm = 1000000; |
12019 | 0 | st->progress = 1.f; |
12020 | 0 | } |
12021 | 0 | else |
12022 | 0 | { |
12023 | 0 | st->progress_ppm = int(st->total_wanted_done * 1000000 |
12024 | 0 | / st->total_wanted); |
12025 | 0 | #if !TORRENT_NO_FPU |
12026 | 0 | st->progress = float(st->progress_ppm) / 1000000.f; |
12027 | 0 | #endif |
12028 | 0 | } |
12029 | |
|
12030 | 0 | if (flags & torrent_handle::query_pieces) |
12031 | 0 | { |
12032 | 0 | int const num_pieces = m_torrent_file->num_pieces(); |
12033 | 0 | if (has_picker()) |
12034 | 0 | { |
12035 | 0 | st->pieces.resize(num_pieces, false); |
12036 | 0 | for (auto const i : st->pieces.range()) |
12037 | 0 | if (m_picker->have_piece(i)) st->pieces.set_bit(i); |
12038 | 0 | } |
12039 | 0 | else if (m_have_all) |
12040 | 0 | { |
12041 | 0 | st->pieces.resize(num_pieces, true); |
12042 | 0 | } |
12043 | 0 | else |
12044 | 0 | { |
12045 | 0 | st->pieces.resize(num_pieces, false); |
12046 | 0 | } |
12047 | 0 | } |
12048 | 0 | st->num_pieces = num_have(); |
12049 | | #if TORRENT_USE_INVARIANT_CHECKS |
12050 | | { |
12051 | | // The documentation states that `num_pieces` is the count of number |
12052 | | // of bits set in `pieces`. Ensure that invariant holds. |
12053 | | int num_have_pieces = 0; |
12054 | | if (m_seed_mode) |
12055 | | { |
12056 | | num_have_pieces = m_torrent_file->num_pieces(); |
12057 | | } |
12058 | | else if (has_picker()) |
12059 | | { |
12060 | | for (auto const i : m_torrent_file->piece_range()) |
12061 | | if (m_picker->have_piece(i)) ++num_have_pieces; |
12062 | | } |
12063 | | else if (m_have_all) |
12064 | | { |
12065 | | num_have_pieces = m_torrent_file->num_pieces(); |
12066 | | } |
12067 | | TORRENT_ASSERT(num_have_pieces == st->num_pieces); |
12068 | | } |
12069 | | #endif |
12070 | 0 | st->num_seeds = num_seeds(); |
12071 | 0 | if ((flags & torrent_handle::query_distributed_copies) && m_picker.get()) |
12072 | 0 | { |
12073 | 0 | std::tie(st->distributed_full_copies, st->distributed_fraction) = |
12074 | 0 | m_picker->distributed_copies(); |
12075 | | #if TORRENT_NO_FPU |
12076 | | st->distributed_copies = -1.f; |
12077 | | #else |
12078 | 0 | st->distributed_copies = float(st->distributed_full_copies) |
12079 | 0 | + float(st->distributed_fraction) / 1000; |
12080 | 0 | #endif |
12081 | 0 | } |
12082 | 0 | else |
12083 | 0 | { |
12084 | 0 | st->distributed_full_copies = -1; |
12085 | 0 | st->distributed_fraction = -1; |
12086 | 0 | st->distributed_copies = -1.f; |
12087 | 0 | } |
12088 | |
|
12089 | 0 | st->last_seen_complete = m_swarm_last_seen_complete; |
12090 | 0 | } |
12091 | | |
12092 | | int torrent::priority() const |
12093 | 0 | { |
12094 | 0 | int priority = 0; |
12095 | 0 | for (int i = 0; i < num_classes(); ++i) |
12096 | 0 | { |
12097 | 0 | int const* prio = m_ses.peer_classes().at(class_at(i))->priority; |
12098 | 0 | priority = std::max(priority, prio[peer_connection::upload_channel]); |
12099 | 0 | priority = std::max(priority, prio[peer_connection::download_channel]); |
12100 | 0 | } |
12101 | 0 | return priority; |
12102 | 0 | } |
12103 | | |
12104 | | #if TORRENT_ABI_VERSION == 1 |
12105 | | void torrent::set_priority(int const prio) |
12106 | 0 | { |
12107 | | // priority 1 is default |
12108 | 0 | if (prio == 1 && m_peer_class == peer_class_t{}) return; |
12109 | | |
12110 | 0 | if (m_peer_class == peer_class_t{}) |
12111 | 0 | setup_peer_class(); |
12112 | |
|
12113 | 0 | struct peer_class* tpc = m_ses.peer_classes().at(m_peer_class); |
12114 | 0 | TORRENT_ASSERT(tpc); |
12115 | 0 | tpc->priority[peer_connection::download_channel] = prio; |
12116 | 0 | tpc->priority[peer_connection::upload_channel] = prio; |
12117 | |
|
12118 | 0 | state_updated(); |
12119 | 0 | } |
12120 | | #endif |
12121 | | |
12122 | | void torrent::add_redundant_bytes(int const b, waste_reason const reason) |
12123 | 42 | { |
12124 | 42 | TORRENT_ASSERT(is_single_thread()); |
12125 | 42 | TORRENT_ASSERT(b > 0); |
12126 | 42 | TORRENT_ASSERT(static_cast<int>(reason) >= 0); |
12127 | 42 | TORRENT_ASSERT(static_cast<int>(reason) < static_cast<int>(waste_reason::max)); |
12128 | | |
12129 | 42 | if (m_total_redundant_bytes <= std::numeric_limits<std::int64_t>::max() - b) |
12130 | 42 | m_total_redundant_bytes += b; |
12131 | 0 | else |
12132 | 0 | m_total_redundant_bytes = std::numeric_limits<std::int64_t>::max(); |
12133 | | |
12134 | | // the stats counters are 64 bits, so we don't check for overflow there |
12135 | 42 | m_stats_counters.inc_stats_counter(counters::recv_redundant_bytes, b); |
12136 | 42 | m_stats_counters.inc_stats_counter(counters::waste_piece_timed_out + static_cast<int>(reason), b); |
12137 | 42 | } |
12138 | | |
12139 | | void torrent::add_failed_bytes(int const b) |
12140 | 0 | { |
12141 | 0 | TORRENT_ASSERT(is_single_thread()); |
12142 | 0 | TORRENT_ASSERT(b > 0); |
12143 | 0 | if (m_total_failed_bytes <= std::numeric_limits<std::int64_t>::max() - b) |
12144 | 0 | m_total_failed_bytes += b; |
12145 | 0 | else |
12146 | 0 | m_total_failed_bytes = std::numeric_limits<std::int64_t>::max(); |
12147 | | |
12148 | | // the stats counters are 64 bits, so we don't check for overflow there |
12149 | 0 | m_stats_counters.inc_stats_counter(counters::recv_failed_bytes, b); |
12150 | 0 | } |
12151 | | |
12152 | | // the number of connected peers that are seeds |
12153 | | int torrent::num_seeds() const |
12154 | 0 | { |
12155 | 0 | TORRENT_ASSERT(is_single_thread()); |
12156 | 0 | INVARIANT_CHECK; |
12157 | |
|
12158 | 0 | return int(m_num_seeds) - int(m_num_connecting_seeds); |
12159 | 0 | } |
12160 | | |
12161 | | // the number of connected peers that are not seeds |
12162 | | int torrent::num_downloaders() const |
12163 | 0 | { |
12164 | 0 | TORRENT_ASSERT(is_single_thread()); |
12165 | 0 | INVARIANT_CHECK; |
12166 | |
|
12167 | 0 | int const ret = num_peers() |
12168 | 0 | - m_num_seeds |
12169 | 0 | - m_num_connecting |
12170 | 0 | + m_num_connecting_seeds; |
12171 | 0 | TORRENT_ASSERT(ret >= 0); |
12172 | 0 | return ret; |
12173 | 0 | } |
12174 | | |
12175 | | void torrent::tracker_request_error(tracker_request const& r |
12176 | | , error_code const& ec, operation_t const op, std::string const& msg |
12177 | | , seconds32 const retry_interval) |
12178 | 0 | { |
12179 | 0 | TORRENT_ASSERT(is_single_thread()); |
12180 | |
|
12181 | 0 | INVARIANT_CHECK; |
12182 | | |
12183 | | // some older versions of clang had a bug where it would fire this warning here |
12184 | 0 | #ifdef __clang__ |
12185 | 0 | #pragma clang diagnostic push |
12186 | 0 | #pragma clang diagnostic ignored "-Wmissing-braces" |
12187 | 0 | #endif |
12188 | 0 | aux::array<bool const, num_protocols, protocol_version> const supports_protocol |
12189 | 0 | { { |
12190 | 0 | m_info_hash.has_v1(), |
12191 | 0 | m_info_hash.has_v2() |
12192 | 0 | } }; |
12193 | 0 | #ifdef __clang__ |
12194 | 0 | #pragma clang diagnostic pop |
12195 | 0 | #endif |
12196 | |
|
12197 | | #ifndef TORRENT_DISABLE_LOGGING |
12198 | | if (should_log()) |
12199 | | { |
12200 | | debug_log("*** tracker error: (%d) %s [%s] %s", ec.value() |
12201 | | , ec.message().c_str(), operation_name(op), msg.c_str()); |
12202 | | } |
12203 | | #endif |
12204 | 0 | if (!(r.kind & tracker_request::scrape_request)) |
12205 | 0 | { |
12206 | | // announce request |
12207 | 0 | aux::announce_entry* ae = find_tracker(r.url); |
12208 | 0 | int fails = 0; |
12209 | 0 | tcp::endpoint local_endpoint; |
12210 | 0 | protocol_version hash_version = protocol_version::V1; |
12211 | 0 | if (ae) |
12212 | 0 | { |
12213 | 0 | auto aep = std::find_if(ae->endpoints.begin(), ae->endpoints.end() |
12214 | 0 | , [&](aux::announce_endpoint const& e) { return e.socket == r.outgoing_socket; }); |
12215 | |
|
12216 | 0 | if (aep != ae->endpoints.end()) |
12217 | 0 | { |
12218 | 0 | hash_version = r.info_hash == m_info_hash.v1 |
12219 | 0 | ? protocol_version::V1 : protocol_version::V2; |
12220 | 0 | auto& a = aep->info_hashes[hash_version]; |
12221 | 0 | local_endpoint = aep->local_endpoint; |
12222 | 0 | a.failed(settings().get_int(settings_pack::tracker_backoff) |
12223 | 0 | , retry_interval); |
12224 | 0 | a.last_error = ec; |
12225 | 0 | a.message = msg; |
12226 | 0 | fails = a.fails; |
12227 | |
|
12228 | | #ifndef TORRENT_DISABLE_LOGGING |
12229 | | debug_log("*** increment tracker fail count [ep: %s url: %s %d]" |
12230 | | , print_endpoint(aep->local_endpoint).c_str(), r.url.c_str(), a.fails); |
12231 | | #endif |
12232 | 0 | if (ec == boost::system::errc::address_family_not_supported) |
12233 | 0 | { |
12234 | | // don't try to announce from this endpoint again |
12235 | 0 | aep->enabled = false; |
12236 | | #ifndef TORRENT_DISABLE_LOGGING |
12237 | | debug_log("*** disabling endpoint [ep: %s url: %s ]" |
12238 | | , print_endpoint(aep->local_endpoint).c_str(), r.url.c_str()); |
12239 | | #endif |
12240 | 0 | } |
12241 | 0 | } |
12242 | 0 | else if (r.outgoing_socket) |
12243 | 0 | { |
12244 | | #ifndef TORRENT_DISABLE_LOGGING |
12245 | | debug_log("*** no matching endpoint for request [%s, %s]" |
12246 | | , r.url.c_str(), print_endpoint(r.outgoing_socket.get_local_endpoint()).c_str()); |
12247 | | #endif |
12248 | 0 | } |
12249 | |
|
12250 | 0 | int const tracker_index = int(ae - m_trackers.data()); |
12251 | | |
12252 | | // never talk to this tracker again |
12253 | 0 | if (ec == error_code(410, http_category())) ae->fail_limit = 1; |
12254 | | |
12255 | | // if all endpoints fail, then we de-prioritize the tracker and try |
12256 | | // the next one in the tier |
12257 | 0 | if (std::all_of(ae->endpoints.begin(), ae->endpoints.end() |
12258 | 0 | , [&](aux::announce_endpoint const& ep) |
12259 | 0 | { |
12260 | 0 | for (protocol_version const ih : all_versions) |
12261 | 0 | if (supports_protocol[ih] && ep.info_hashes[ih].is_working()) |
12262 | 0 | return false; |
12263 | 0 | return true; |
12264 | 0 | })) |
12265 | 0 | { |
12266 | 0 | deprioritize_tracker(tracker_index); |
12267 | 0 | } |
12268 | 0 | } |
12269 | 0 | if (m_ses.alerts().should_post<tracker_error_alert>() |
12270 | 0 | || r.triggered_manually) |
12271 | 0 | { |
12272 | 0 | m_ses.alerts().emplace_alert<tracker_error_alert>(get_handle() |
12273 | 0 | , local_endpoint, fails, hash_version, r.url, op, ec, msg); |
12274 | 0 | } |
12275 | 0 | } |
12276 | 0 | else |
12277 | 0 | { |
12278 | 0 | aux::announce_entry* ae = find_tracker(r.url); |
12279 | | |
12280 | | // scrape request |
12281 | 0 | if (ec == error_code(410, http_category())) |
12282 | 0 | { |
12283 | | // never talk to this tracker again |
12284 | 0 | if (ae != nullptr) ae->fail_limit = 1; |
12285 | 0 | } |
12286 | | |
12287 | | // if this was triggered manually we need to post this unconditionally, |
12288 | | // since the client expects a response from its action, regardless of |
12289 | | // whether all tracker events have been enabled by the alert mask |
12290 | 0 | if (m_ses.alerts().should_post<scrape_failed_alert>() |
12291 | 0 | || r.triggered_manually) |
12292 | 0 | { |
12293 | 0 | tcp::endpoint local_endpoint; |
12294 | 0 | if (ae != nullptr) |
12295 | 0 | { |
12296 | 0 | auto* aep = ae->find_endpoint(r.outgoing_socket); |
12297 | 0 | if (aep != nullptr) local_endpoint = aep->local_endpoint; |
12298 | 0 | } |
12299 | |
|
12300 | 0 | protocol_version hash_version = r.info_hash == m_info_hash.v1 |
12301 | 0 | ? protocol_version::V1 : protocol_version::V2; |
12302 | |
|
12303 | 0 | m_ses.alerts().emplace_alert<scrape_failed_alert>(get_handle() |
12304 | 0 | , local_endpoint, r.url, hash_version, ec); |
12305 | 0 | } |
12306 | 0 | } |
12307 | | // announce to the next working tracker |
12308 | | // We may have changed state into checking by now, in which case we |
12309 | | // shouldn't keep trying to announce |
12310 | 0 | if ((!m_abort && !is_paused() && state() != torrent_status::checking_files) |
12311 | 0 | || r.event == event_t::stopped) |
12312 | 0 | { |
12313 | 0 | announce_with_tracker(r.event); |
12314 | 0 | } |
12315 | 0 | else |
12316 | 0 | { |
12317 | 0 | update_tracker_timer(aux::time_now32()); |
12318 | 0 | } |
12319 | 0 | } |
12320 | | |
12321 | | #ifndef TORRENT_DISABLE_LOGGING |
12322 | | bool torrent::should_log() const |
12323 | | { |
12324 | | return alerts().should_post<torrent_log_alert>(); |
12325 | | } |
12326 | | |
12327 | | TORRENT_FORMAT(2,3) |
12328 | | void torrent::debug_log(char const* fmt, ...) const noexcept try |
12329 | | { |
12330 | | if (!alerts().should_post<torrent_log_alert>()) return; |
12331 | | |
12332 | | va_list v; |
12333 | | va_start(v, fmt); |
12334 | | alerts().emplace_alert<torrent_log_alert>( |
12335 | | const_cast<torrent*>(this)->get_handle(), fmt, v); |
12336 | | va_end(v); |
12337 | | } |
12338 | | catch (std::exception const&) {} |
12339 | | #endif |
12340 | | |
12341 | | } |