Coverage Report

Created: 2026-03-19 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtorrent/src/ut_metadata.cpp
Line
Count
Source
1
/*
2
3
Copyright (c) 2009, Andrew Resch
4
Copyright (c) 2007-2020, Arvid Norberg
5
Copyright (c) 2015, Steven Siloti
6
Copyright (c) 2016-2018, Alden Torres
7
Copyright (c) 2017, Andrei Kurushin
8
Copyright (c) 2017, Pavel Pimenov
9
Copyright (c) 2022, Joris CARRIER
10
All rights reserved.
11
12
Redistribution and use in source and binary forms, with or without
13
modification, are permitted provided that the following conditions
14
are met:
15
16
    * Redistributions of source code must retain the above copyright
17
      notice, this list of conditions and the following disclaimer.
18
    * Redistributions in binary form must reproduce the above copyright
19
      notice, this list of conditions and the following disclaimer in
20
      the documentation and/or other materials provided with the distribution.
21
    * Neither the name of the author nor the names of its
22
      contributors may be used to endorse or promote products derived
23
      from this software without specific prior written permission.
24
25
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35
POSSIBILITY OF SUCH DAMAGE.
36
37
*/
38
39
#ifndef TORRENT_DISABLE_EXTENSIONS
40
41
#include <functional>
42
#include <vector>
43
#include <utility>
44
#include <numeric>
45
#include <cstdio>
46
47
#include "libtorrent/peer_connection.hpp"
48
#include "libtorrent/bt_peer_connection.hpp"
49
#include "libtorrent/peer_connection_handle.hpp"
50
#include "libtorrent/bencode.hpp"
51
#include "libtorrent/torrent.hpp"
52
#include "libtorrent/torrent_handle.hpp"
53
#include "libtorrent/extensions.hpp"
54
#include "libtorrent/extensions/ut_metadata.hpp"
55
#include "libtorrent/alert_types.hpp"
56
#include "libtorrent/random.hpp"
57
#include "libtorrent/io.hpp"
58
#include "libtorrent/performance_counters.hpp" // for counters
59
#include "libtorrent/aux_/time.hpp"
60
61
#if TORRENT_USE_ASSERTS
62
#include "libtorrent/hasher.hpp"
63
#endif
64
65
namespace libtorrent {
66
namespace {
67
68
  enum
69
  {
70
    // this is the max number of bytes we'll
71
    // queue up in the send buffer. If we exceed this,
72
    // we'll wait another second before checking
73
    // the send buffer size again. So, this may limit
74
    // the rate at which we can server metadata to
75
    // 160 kiB/s
76
    send_buffer_limit = 0x4000 * 10,
77
78
    // this is the max number of requests we'll queue
79
    // up. If we get more requests than this, we'll
80
    // start rejecting them, claiming we don't have
81
    // metadata. If the torrent is greater than 16 MiB,
82
    // we may hit this case (and the client requesting
83
    // doesn't throttle its requests)
84
    max_incoming_requests = 1024,
85
  };
86
87
  enum class msg_t : std::uint8_t
88
  {
89
    request, piece, dont_have
90
  };
91
92
  int div_round_up(int numerator, int denominator)
93
0
  {
94
0
    return (numerator + denominator - 1) / denominator;
95
0
  }
96
97
  struct ut_metadata_peer_plugin;
98
99
  struct ut_metadata_plugin final
100
    : torrent_plugin
101
  {
102
0
    explicit ut_metadata_plugin(torrent& t) : m_torrent(t) {}
103
104
    std::shared_ptr<peer_plugin> new_connection(
105
      peer_connection_handle const& pc) override;
106
107
    span<char const> metadata() const
108
0
    {
109
0
      if (!m_metadata.empty()) return m_metadata;
110
0
      if (!m_torrent.valid_metadata()) return {};
111
112
0
      auto const ret = m_torrent.torrent_file().info_section();
113
114
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
115
      if (m_torrent.torrent_file().info_hashes().has_v1())
116
      {
117
        TORRENT_ASSERT(hasher(ret).final()
118
          == m_torrent.torrent_file().info_hashes().v1);
119
      }
120
      if (m_torrent.torrent_file().info_hashes().has_v2())
121
      {
122
        TORRENT_ASSERT(hasher256(ret).final()
123
          == m_torrent.torrent_file().info_hashes().v2);
124
      }
125
#endif
126
127
0
      return ret;
128
0
    }
129
130
    bool received_metadata(ut_metadata_peer_plugin& source
131
      , span<char const> buf, int piece, int total_size);
132
133
    // returns a piece of the metadata that
134
    // we should request.
135
    // returns -1 if we should hold off the request
136
    int metadata_request(bool has_metadata);
137
138
    void on_piece_pass(piece_index_t) override
139
0
    {
140
      // if we became a seed, copy the metadata from
141
      // the torrent before it is deallocated
142
0
      if (m_torrent.is_seed())
143
0
        metadata();
144
0
    }
145
146
    void metadata_size(int const size)
147
0
    {
148
0
      if (m_torrent.valid_metadata()) return;
149
0
      if (size <= 0 || size > 4 * 1024 * 1024) return;
150
0
      m_metadata.resize(size);
151
0
      m_requested_metadata.resize(div_round_up(size, 16 * 1024));
152
0
    }
153
154
    // explicitly disallow assignment, to silence msvc warning
155
    ut_metadata_plugin& operator=(ut_metadata_plugin const&) = delete;
156
157
  private:
158
    torrent& m_torrent;
159
160
    // this buffer is filled with the info-section of
161
    // the metadata file while downloading it from
162
    // peers. Once we have metadata, we seed it directly from the
163
    // torrent_info of the underlying torrent
164
    aux::vector<char> m_metadata;
165
166
    struct metadata_piece
167
    {
168
0
      metadata_piece() = default;
169
      int num_requests = 0;
170
      time_point last_request = min_time();
171
      std::weak_ptr<ut_metadata_peer_plugin> source;
172
      bool operator<(metadata_piece const& rhs) const
173
0
      { return num_requests < rhs.num_requests; }
174
    };
175
176
    // this vector keeps track of how many times each metadata
177
    // block has been requested and who we ended up getting it from
178
    // std::numeric_limits<int>::max() means we have the piece
179
    aux::vector<metadata_piece> m_requested_metadata;
180
  };
181
182
183
  struct ut_metadata_peer_plugin final
184
    : peer_plugin, std::enable_shared_from_this<ut_metadata_peer_plugin>
185
  {
186
    friend struct ut_metadata_plugin;
187
188
    ut_metadata_peer_plugin(torrent& t, bt_peer_connection& pc
189
      , ut_metadata_plugin& tp)
190
0
      : m_message_index(0)
191
0
      , m_request_limit(min_time())
192
0
      , m_torrent(t)
193
0
      , m_pc(pc)
194
0
      , m_tp(tp)
195
0
    {}
196
197
    // can add entries to the extension handshake
198
    void add_handshake(entry& h) override
199
0
    {
200
0
      entry& messages = h["m"];
201
0
      messages["ut_metadata"] = 2;
202
0
      if (m_torrent.valid_metadata())
203
0
        h["metadata_size"] = m_tp.metadata().size();
204
0
    }
205
206
    // called when the extension handshake from the other end is received
207
    bool on_extension_handshake(bdecode_node const& h) override
208
0
    {
209
0
      m_message_index = 0;
210
0
      if (h.type() != bdecode_node::dict_t) return false;
211
0
      bdecode_node const messages = h.dict_find_dict("m");
212
0
      if (!messages) return false;
213
214
0
      int index = int(messages.dict_find_int_value("ut_metadata", -1));
215
0
      if (index == -1) return false;
216
0
      m_message_index = index;
217
218
0
      int metadata_size = int(h.dict_find_int_value("metadata_size"));
219
0
      if (metadata_size > 0)
220
0
        m_tp.metadata_size(metadata_size);
221
0
      else
222
0
        m_pc.set_has_metadata(false);
223
224
0
      maybe_send_request();
225
0
      return true;
226
0
    }
227
228
    void write_metadata_packet(msg_t const type, int const piece)
229
0
    {
230
0
      TORRENT_ASSERT(!m_pc.associated_torrent().expired());
231
232
#ifndef TORRENT_DISABLE_LOGGING
233
      static char const* names[] = {"request", "data", "dont-have"};
234
      char const* n = "";
235
      if (type >= msg_t::request && type <= msg_t::dont_have) n = names[static_cast<int>(type)];
236
      m_pc.peer_log(peer_log_alert::outgoing_message, "UT_METADATA"
237
        , "type: %d (%s) piece: %d", static_cast<int>(type), n, piece);
238
#endif
239
240
      // abort if the peer doesn't support the metadata extension
241
0
      if (m_message_index == 0) return;
242
243
0
      entry e;
244
0
      e["msg_type"] = static_cast<int>(type);
245
0
      e["piece"] = piece;
246
247
0
      char const* metadata = nullptr;
248
0
      int metadata_piece_size = 0;
249
250
0
      if (m_torrent.valid_metadata())
251
0
        e["total_size"] = m_tp.metadata().size();
252
253
0
      if (type == msg_t::piece)
254
0
      {
255
0
        TORRENT_ASSERT(piece >= 0 && piece < (m_tp.metadata().size() + 16 * 1024 - 1) / (16 * 1024));
256
0
        TORRENT_ASSERT(m_pc.associated_torrent().lock()->valid_metadata());
257
0
        TORRENT_ASSERT(m_torrent.valid_metadata());
258
259
0
        int const offset = piece * 16 * 1024;
260
0
        metadata = m_tp.metadata().data() + offset;
261
0
        metadata_piece_size = std::min(
262
0
          int(m_tp.metadata().size()) - offset, 16 * 1024);
263
0
        TORRENT_ASSERT(metadata_piece_size > 0);
264
0
        TORRENT_ASSERT(offset >= 0);
265
0
        TORRENT_ASSERT(offset + metadata_piece_size <= m_tp.metadata().size());
266
0
      }
267
268
      // TODO: 3 use the aux::write_* functions and the span here instead, it
269
      // will fit better with send_buffer()
270
0
      char msg[200];
271
0
      char* header = msg;
272
0
      char* p = &msg[6];
273
0
      int const len = bencode(p, e);
274
0
      int const total_size = 2 + len + metadata_piece_size;
275
0
      namespace io = aux;
276
0
      io::write_uint32(total_size, header);
277
0
      io::write_uint8(bt_peer_connection::msg_extended, header);
278
0
      io::write_uint8(m_message_index, header);
279
280
0
      m_pc.send_buffer({msg, len + 6});
281
      // TODO: we really need to increment the refcounter on the torrent
282
      // while this buffer is still in the peer's send buffer
283
0
      if (metadata_piece_size)
284
0
      {
285
0
        m_pc.append_const_send_buffer(
286
0
          span<char>(const_cast<char*>(metadata), metadata_piece_size), metadata_piece_size);
287
0
      }
288
289
0
      m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_extended);
290
0
      m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_metadata);
291
0
    }
292
293
    bool on_extended(int const length
294
      , int const extended_msg, span<char const> body) override
295
0
    {
296
0
      if (extended_msg != 2) return false;
297
0
      if (m_message_index == 0) return false;
298
299
0
      if (length > 17 * 1024)
300
0
      {
301
#ifndef TORRENT_DISABLE_LOGGING
302
        m_pc.peer_log(peer_log_alert::incoming_message, "UT_METADATA"
303
          , "packet too big %d", length);
304
#endif
305
0
        m_pc.disconnect(errors::invalid_metadata_message, operation_t::bittorrent, peer_connection_interface::peer_error);
306
0
        return true;
307
0
      }
308
309
0
      if (!m_pc.packet_finished()) return true;
310
311
0
      error_code ec;
312
0
      bdecode_node msg = bdecode(body, ec);
313
0
      if (msg.type() != bdecode_node::dict_t)
314
0
      {
315
#ifndef TORRENT_DISABLE_LOGGING
316
        m_pc.peer_log(peer_log_alert::incoming_message, "UT_METADATA"
317
          , "not a dictionary");
318
#endif
319
0
        m_pc.disconnect(errors::invalid_metadata_message, operation_t::bittorrent, peer_connection_interface::peer_error);
320
0
        return true;
321
0
      }
322
323
0
      bdecode_node const& type_ent = msg.dict_find_int("msg_type");
324
0
      bdecode_node const& piece_ent = msg.dict_find_int("piece");
325
0
      if (!type_ent || !piece_ent)
326
0
      {
327
#ifndef TORRENT_DISABLE_LOGGING
328
        m_pc.peer_log(peer_log_alert::incoming_message, "UT_METADATA"
329
          , "missing or invalid keys");
330
#endif
331
0
        m_pc.disconnect(errors::invalid_metadata_message, operation_t::bittorrent, peer_connection_interface::peer_error);
332
0
        return true;
333
0
      }
334
0
      auto const type = msg_t(type_ent.int_value());
335
0
      auto const piece = static_cast<int>(piece_ent.int_value());
336
337
#ifndef TORRENT_DISABLE_LOGGING
338
      m_pc.peer_log(peer_log_alert::incoming_message, "UT_METADATA"
339
        , "type: %d piece: %d", static_cast<int>(type), piece);
340
#endif
341
342
0
      switch (type)
343
0
      {
344
0
        case msg_t::request:
345
0
        {
346
0
          if (!m_torrent.valid_metadata()
347
0
            || piece < 0 || piece >= (m_tp.metadata().size() + 16 * 1024 - 1) / (16 * 1024))
348
0
          {
349
#ifndef TORRENT_DISABLE_LOGGING
350
            if (m_pc.should_log(peer_log_alert::info))
351
            {
352
              m_pc.peer_log(peer_log_alert::info, "UT_METADATA"
353
                , "have: %d invalid piece %d metadata size: %d"
354
                , int(m_torrent.valid_metadata()), piece
355
                , m_torrent.valid_metadata()
356
                  ? int(m_tp.metadata().size()) : 0);
357
            }
358
#endif
359
0
            write_metadata_packet(msg_t::dont_have, piece);
360
0
            return true;
361
0
          }
362
0
          if (m_pc.send_buffer_size() < send_buffer_limit)
363
0
            write_metadata_packet(msg_t::piece, piece);
364
0
          else if (m_incoming_requests.size() < max_incoming_requests)
365
0
            m_incoming_requests.push_back(piece);
366
0
          else
367
0
            write_metadata_packet(msg_t::dont_have, piece);
368
0
        }
369
0
        break;
370
0
        case msg_t::piece:
371
0
        {
372
0
          auto const i = std::find(m_sent_requests.begin()
373
0
            , m_sent_requests.end(), piece);
374
375
          // unwanted piece?
376
0
          if (i == m_sent_requests.end())
377
0
          {
378
#ifndef TORRENT_DISABLE_LOGGING
379
            m_pc.peer_log(peer_log_alert::info, "UT_METADATA"
380
              , "UNWANTED / TIMED OUT");
381
#endif
382
0
            return true;
383
0
          }
384
385
0
          m_sent_requests.erase(i);
386
0
          auto const len = msg.data_section().size();
387
0
          auto const total_size = msg.dict_find_int_value("total_size", 0);
388
0
          m_tp.received_metadata(*this, body.subspan(len), piece, static_cast<int>(total_size));
389
0
          maybe_send_request();
390
0
        }
391
0
        break;
392
0
        case msg_t::dont_have:
393
0
        {
394
0
          m_request_limit = std::max(aux::time_now() + minutes(1), m_request_limit);
395
0
          auto const i = std::find(m_sent_requests.begin()
396
0
            , m_sent_requests.end(), piece);
397
          // unwanted piece?
398
0
          if (i == m_sent_requests.end()) return true;
399
0
          m_sent_requests.erase(i);
400
0
        }
401
0
        break;
402
0
      }
403
404
0
      m_pc.stats_counters().inc_stats_counter(counters::num_incoming_metadata);
405
406
0
      return true;
407
0
    }
408
409
    void tick() override
410
0
    {
411
0
      maybe_send_request();
412
0
      while (!m_incoming_requests.empty()
413
0
        && m_pc.send_buffer_size() < send_buffer_limit)
414
0
      {
415
0
        int const piece = m_incoming_requests.front();
416
0
        m_incoming_requests.erase(m_incoming_requests.begin());
417
0
        write_metadata_packet(msg_t::piece, piece);
418
0
      }
419
0
    }
420
421
    void maybe_send_request()
422
0
    {
423
0
      if (m_pc.is_disconnecting()) return;
424
425
      // if we don't have any metadata, and this peer
426
      // supports the request metadata extension
427
      // and we aren't currently waiting for a request
428
      // reply. Then, send a request for some metadata.
429
0
      if (!m_torrent.valid_metadata()
430
0
        && m_message_index != 0
431
0
        && m_sent_requests.size() < 2
432
0
        && has_metadata())
433
0
      {
434
0
        int const piece = m_tp.metadata_request(m_pc.has_metadata());
435
0
        if (piece == -1) return;
436
437
0
        m_sent_requests.push_back(piece);
438
0
        write_metadata_packet(msg_t::request, piece);
439
0
      }
440
0
    }
441
442
    bool has_metadata() const
443
0
    {
444
0
      return m_pc.has_metadata() || (aux::time_now() > m_request_limit);
445
0
    }
446
447
    void failed_hash_check(time_point const& now)
448
0
    {
449
0
      m_request_limit = now + seconds(20 + random(50));
450
0
    }
451
452
    // explicitly disallow assignment, to silence msvc warning
453
    ut_metadata_peer_plugin& operator=(ut_metadata_peer_plugin const&) = delete;
454
455
  private:
456
457
    // this is the message index the remote peer uses
458
    // for metadata extension messages.
459
    int m_message_index;
460
461
    // this is set to the next time we can request pieces
462
    // again. It is updated every time we get a
463
    // "I don't have metadata" message, but also when
464
    // we receive metadata that fails the info hash check
465
    time_point m_request_limit;
466
467
    // request queues
468
    std::vector<int> m_sent_requests;
469
    std::vector<int> m_incoming_requests;
470
471
    torrent& m_torrent;
472
    bt_peer_connection& m_pc;
473
    ut_metadata_plugin& m_tp;
474
  };
475
476
  std::shared_ptr<peer_plugin> ut_metadata_plugin::new_connection(
477
    peer_connection_handle const& pc)
478
0
  {
479
0
    if (pc.type() != connection_type::bittorrent) return {};
480
481
0
    bt_peer_connection* c = static_cast<bt_peer_connection*>(pc.native_handle().get());
482
0
    return std::make_shared<ut_metadata_peer_plugin>(m_torrent, *c, *this);
483
0
  }
484
485
  // has_metadata is false if the peer making the request has not announced
486
  // that it has metadata. In this case, it shouldn't prevent other peers
487
  // from requesting this block by setting a timeout on it.
488
  int ut_metadata_plugin::metadata_request(bool const has_metadata)
489
0
  {
490
0
    auto i = std::min_element(
491
0
      m_requested_metadata.begin(), m_requested_metadata.end());
492
493
0
    if (m_requested_metadata.empty())
494
0
    {
495
      // if we don't know how many pieces there are
496
      // just ask for piece 0
497
0
      m_requested_metadata.resize(1);
498
0
      i = m_requested_metadata.begin();
499
0
    }
500
501
0
    int const piece = int(i - m_requested_metadata.begin());
502
503
    // don't request the same block more than once every 3 seconds
504
    // unless the source is disconnected
505
0
    auto source = m_requested_metadata[piece].source.lock();
506
0
    time_point const now = aux::time_now();
507
0
    if (m_requested_metadata[piece].last_request != min_time()
508
0
      && source
509
0
      && !source->m_pc.is_disconnecting()
510
0
      && total_seconds(now - m_requested_metadata[piece].last_request) < 3)
511
0
      return -1;
512
513
0
    ++m_requested_metadata[piece].num_requests;
514
515
    // only set the timeout on this block, only if the peer
516
    // has metadata. This is to prevent peers with no metadata
517
    // to starve out sending requests to peers with metadata
518
0
    if (has_metadata)
519
0
      m_requested_metadata[piece].last_request = now;
520
521
0
    return piece;
522
0
  }
523
524
  bool ut_metadata_plugin::received_metadata(ut_metadata_peer_plugin& source
525
    , span<char const> buf, int const piece, int const total_size)
526
0
  {
527
0
    if (m_torrent.valid_metadata())
528
0
    {
529
#ifndef TORRENT_DISABLE_LOGGING
530
      source.m_pc.peer_log(peer_log_alert::info, "UT_METADATA"
531
        , "already have metadata");
532
#endif
533
0
      m_torrent.add_redundant_bytes(static_cast<int>(buf.size()), waste_reason::piece_unknown);
534
0
      return false;
535
0
    }
536
537
0
    if (m_metadata.empty())
538
0
    {
539
      // verify the total_size
540
0
      if (total_size <= 0 || total_size > m_torrent.session().settings().get_int(settings_pack::max_metadata_size))
541
0
      {
542
#ifndef TORRENT_DISABLE_LOGGING
543
        source.m_pc.peer_log(peer_log_alert::info, "UT_METADATA"
544
          , "metadata size too big: %d", total_size);
545
#endif
546
// #error post alert
547
0
        return false;
548
0
      }
549
550
0
      m_metadata.resize(total_size);
551
0
      m_requested_metadata.resize(div_round_up(total_size, 16 * 1024));
552
0
    }
553
554
0
    if (piece < 0 || piece >= m_requested_metadata.end_index())
555
0
    {
556
#ifndef TORRENT_DISABLE_LOGGING
557
      source.m_pc.peer_log(peer_log_alert::info, "UT_METADATA"
558
        , "piece: %d INVALID", piece);
559
#endif
560
0
      return false;
561
0
    }
562
563
0
    if (total_size != m_metadata.end_index())
564
0
    {
565
#ifndef TORRENT_DISABLE_LOGGING
566
      source.m_pc.peer_log(peer_log_alert::info, "UT_METADATA"
567
        , "total_size: %d INCONSISTENT WITH: %d"
568
        , total_size, int(metadata().size()));
569
#endif
570
      // they disagree about the size!
571
0
      return false;
572
0
    }
573
574
0
    if (piece * 16 * 1024 + buf.size() > metadata().size())
575
0
    {
576
      // this piece is invalid
577
0
      return false;
578
0
    }
579
580
0
    std::memcpy(&m_metadata[piece * 16 * 1024], buf.data(), aux::numeric_cast<std::size_t>(buf.size()));
581
    // mark this piece has 'have'
582
0
    m_requested_metadata[piece].num_requests = std::numeric_limits<int>::max();
583
0
    m_requested_metadata[piece].source = source.shared_from_this();
584
585
0
    bool have_all = std::all_of(m_requested_metadata.begin(), m_requested_metadata.end()
586
0
      , [](metadata_piece const& mp) { return mp.num_requests == std::numeric_limits<int>::max(); });
587
588
0
    if (!have_all) return false;
589
590
0
    if (!m_torrent.set_metadata(m_metadata))
591
0
    {
592
0
      if (!m_torrent.valid_metadata())
593
0
      {
594
0
        time_point const now = aux::time_now();
595
        // any peer that we downloaded metadata from gets a random time
596
        // penalty, from 5 to 30 seconds or so. During this time we don't
597
        // make any metadata requests from those peers (to mix it up a bit
598
        // of which peers we use)
599
        // if we only have one block, and thus requested it from a single
600
        // peer, we bump up the retry time a lot more to try other peers
601
0
        bool single_peer = m_requested_metadata.size() == 1;
602
0
        for (auto& mp : m_requested_metadata)
603
0
        {
604
0
          mp.num_requests = 0;
605
0
          auto peer = mp.source.lock();
606
0
          if (!peer) continue;
607
608
0
          peer->failed_hash_check(single_peer ? now + minutes(5) : now);
609
0
        }
610
0
      }
611
0
      return false;
612
0
    }
613
614
    // free our copy of the metadata and get a reference
615
    // to the torrent's copy instead. No need to keep two
616
    // identical copies around
617
0
    m_metadata.clear();
618
0
    m_metadata.shrink_to_fit();
619
620
    // clear the storage for the bitfield
621
0
    m_requested_metadata.clear();
622
0
    m_requested_metadata.shrink_to_fit();
623
624
0
    return true;
625
0
  }
626
627
} }
628
629
namespace libtorrent {
630
631
  std::shared_ptr<torrent_plugin> create_ut_metadata_plugin(torrent_handle const& th, client_data_t)
632
0
  {
633
0
    torrent* t = th.native_handle().get();
634
    // don't add this extension if the torrent is private
635
0
    if (t->valid_metadata() && t->torrent_file().priv()) return {};
636
0
    return std::make_shared<ut_metadata_plugin>(*t);
637
0
  }
638
}
639
640
#endif