Coverage Report

Created: 2026-03-19 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtorrent/src/ut_pex.cpp
Line
Count
Source
1
/*
2
3
Copyright (c) 2006, MassaRoddel
4
Copyright (c) 2006-2020, Arvid Norberg
5
Copyright (c) 2015, 2018, Steven Siloti
6
Copyright (c) 2016-2017, Alden Torres
7
Copyright (c) 2016-2017, Andrei Kurushin
8
Copyright (c) 2017, Pavel Pimenov
9
All rights reserved.
10
11
Redistribution and use in source and binary forms, with or without
12
modification, are permitted provided that the following conditions
13
are met:
14
15
    * Redistributions of source code must retain the above copyright
16
      notice, this list of conditions and the following disclaimer.
17
    * Redistributions in binary form must reproduce the above copyright
18
      notice, this list of conditions and the following disclaimer in
19
      the documentation and/or other materials provided with the distribution.
20
    * Neither the name of the author nor the names of its
21
      contributors may be used to endorse or promote products derived
22
      from this software without specific prior written permission.
23
24
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
POSSIBILITY OF SUCH DAMAGE.
35
36
*/
37
38
#include "libtorrent/config.hpp"
39
#include "libtorrent/bt_peer_connection.hpp"
40
#include "libtorrent/peer_connection_handle.hpp"
41
#include "libtorrent/bencode.hpp"
42
#include "libtorrent/torrent.hpp"
43
#include "libtorrent/extensions.hpp"
44
#include "libtorrent/socket_io.hpp"
45
#include "libtorrent/peer_info.hpp"
46
#include "libtorrent/aux_/socket_type.hpp" // for is_utp
47
#include "libtorrent/performance_counters.hpp" // for counters
48
#include "libtorrent/extensions/ut_pex.hpp"
49
#include "libtorrent/aux_/time.hpp"
50
#include "libtorrent/aux_/ip_helpers.hpp" // for is_v4
51
52
#ifndef TORRENT_DISABLE_EXTENSIONS
53
54
namespace libtorrent { namespace {
55
56
  const char extension_name[] = "ut_pex";
57
58
  enum
59
  {
60
    extension_index = 1,
61
    max_peer_entries = 100
62
  };
63
64
  bool send_peer(peer_connection const& p)
65
0
  {
66
    // don't send out those peers that we haven't connected to
67
    // (that have connected to us) and that aren't sharing their
68
    // listening port
69
0
    if (!p.is_outgoing() && !p.received_listen_port()) return false;
70
    // don't send out peers that we haven't successfully connected to
71
0
    if (p.is_connecting()) return false;
72
0
    if (p.in_handshake()) return false;
73
0
    return true;
74
0
  }
75
76
  struct ut_pex_plugin final
77
    : torrent_plugin
78
  {
79
    // randomize when we rebuild the pex message
80
    // to evenly spread it out across all torrents
81
    // the more torrents we have, the longer we can
82
    // delay the rebuilding
83
    explicit ut_pex_plugin(torrent& t)
84
0
      : m_torrent(t)
85
0
      , m_last_msg(min_time())
86
0
      , m_peers_in_message(0) {}
87
88
    std::shared_ptr<peer_plugin> new_connection(
89
      peer_connection_handle const& pc) override;
90
91
    std::vector<char>& get_ut_pex_msg()
92
0
    {
93
0
      return m_ut_pex_msg;
94
0
    }
95
96
    int peers_in_msg() const
97
0
    {
98
0
      return m_peers_in_message;
99
0
    }
100
101
    // the second tick of the torrent
102
    // each minute the new lists of "added" + "added.f" and "dropped"
103
    // are calculated here and the pex message is created
104
    // each peer connection will use this message
105
    // max_peer_entries limits the packet size
106
    void tick() override
107
0
    {
108
0
      if (m_torrent.flags() & torrent_flags::disable_pex) return;
109
110
0
      time_point const now = aux::time_now();
111
0
      if (now - seconds(60) < m_last_msg) return;
112
0
      m_last_msg = now;
113
114
0
      if (m_torrent.num_peers() == 0) return;
115
116
0
      entry pex;
117
0
      std::string& pla = pex["added"].string();
118
0
      std::string& pld = pex["dropped"].string();
119
0
      std::string& plf = pex["added.f"].string();
120
0
      std::back_insert_iterator<std::string> pla_out(pla);
121
0
      std::back_insert_iterator<std::string> pld_out(pld);
122
0
      std::back_insert_iterator<std::string> plf_out(plf);
123
0
      std::string& pla6 = pex["added6"].string();
124
0
      std::string& pld6 = pex["dropped6"].string();
125
0
      std::string& plf6 = pex["added6.f"].string();
126
0
      std::back_insert_iterator<std::string> pla6_out(pla6);
127
0
      std::back_insert_iterator<std::string> pld6_out(pld6);
128
0
      std::back_insert_iterator<std::string> plf6_out(plf6);
129
130
0
      std::set<tcp::endpoint> dropped;
131
0
      m_old_peers.swap(dropped);
132
133
0
      m_peers_in_message = 0;
134
0
      int num_added = 0;
135
0
      for (auto const peer : m_torrent)
136
0
      {
137
0
        if (!send_peer(*peer)) continue;
138
139
0
        tcp::endpoint remote = peer->remote();
140
0
        m_old_peers.insert(remote);
141
142
0
        auto const di = dropped.find(remote);
143
0
        if (di == dropped.end())
144
0
        {
145
          // don't write too big of a package
146
0
          if (num_added >= max_peer_entries) break;
147
148
          // only send proper bittorrent peers
149
0
          if (peer->type() != connection_type::bittorrent)
150
0
            continue;
151
152
0
          auto const* const p = static_cast<bt_peer_connection const*>(peer);
153
154
          // if the peer has told us which port its listening on,
155
          // use that port. But only if we didn't connect to the peer.
156
          // if we connected to it, use the port we know works
157
0
          if (!p->is_outgoing())
158
0
          {
159
0
            torrent_peer const* const pi = peer->peer_info_struct();
160
0
            if (pi != nullptr && pi->port > 0)
161
0
              remote.port(pi->port);
162
0
          }
163
164
          // no supported flags to set yet
165
          // 0x01 - peer supports encryption
166
          // 0x02 - peer is a seed
167
          // 0x04 - supports uTP. This is only a positive flags
168
          //        passing 0 doesn't mean the peer doesn't
169
          //        support uTP
170
          // 0x08 - supports hole punching protocol. If this
171
          //        flag is received from a peer, it can be
172
          //        used as a rendezvous point in case direct
173
          //        connections to the peer fail
174
0
          pex_flags_t flags = p->is_seed() ? pex_seed : pex_flags_t{};
175
0
#if !defined TORRENT_DISABLE_ENCRYPTION
176
0
          flags |= p->supports_encryption() ? pex_encryption : pex_flags_t{};
177
0
#endif
178
0
          flags |= is_utp(p->get_socket()) ? pex_utp : pex_flags_t{};
179
0
          flags |= p->supports_holepunch() ? pex_holepunch : pex_flags_t{};
180
181
          // i->first was added since the last time
182
0
          if (aux::is_v4(remote))
183
0
          {
184
0
            aux::write_endpoint(remote, pla_out);
185
0
            aux::write_uint8(static_cast<std::uint8_t>(flags), plf_out);
186
0
          }
187
0
          else
188
0
          {
189
0
            aux::write_endpoint(remote, pla6_out);
190
0
            aux::write_uint8(static_cast<std::uint8_t>(flags), plf6_out);
191
0
          }
192
0
          ++num_added;
193
0
          ++m_peers_in_message;
194
0
        }
195
0
        else
196
0
        {
197
          // this was in the previous message
198
          // so, it wasn't dropped
199
0
          dropped.erase(di);
200
0
        }
201
0
      }
202
203
0
      for (auto const& i : dropped)
204
0
      {
205
0
        if (aux::is_v4(i))
206
0
          aux::write_endpoint(i, pld_out);
207
0
        else
208
0
          aux::write_endpoint(i, pld6_out);
209
0
        ++m_peers_in_message;
210
0
      }
211
212
0
      m_ut_pex_msg.clear();
213
0
      bencode(std::back_inserter(m_ut_pex_msg), pex);
214
0
    }
215
216
  private:
217
    torrent& m_torrent;
218
219
    std::set<tcp::endpoint> m_old_peers;
220
    time_point m_last_msg;
221
    std::vector<char> m_ut_pex_msg;
222
    int m_peers_in_message;
223
224
    // explicitly disallow assignment, to silence msvc warning
225
    ut_pex_plugin& operator=(ut_pex_plugin const&) = delete;
226
  };
227
228
  struct ut_pex_peer_plugin final
229
    : ut_pex_peer_store, peer_plugin
230
  {
231
    ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp)
232
0
      : m_torrent(t)
233
0
      , m_pc(pc)
234
0
      , m_tp(tp)
235
0
      , m_last_msg(min_time())
236
0
      , m_message_index(0)
237
0
      , m_first_time(true)
238
0
    {
239
0
      const int num_pex_timers = sizeof(m_last_pex) / sizeof(m_last_pex[0]);
240
0
      for (int i = 0; i < num_pex_timers; ++i)
241
0
      {
242
0
        m_last_pex[i] = min_time();
243
0
      }
244
0
    }
245
246
    void add_handshake(entry& h) override
247
0
    {
248
0
      entry& messages = h["m"];
249
0
      messages[extension_name] = extension_index;
250
0
    }
251
252
    bool on_extension_handshake(bdecode_node const& h) override
253
0
    {
254
0
      m_message_index = 0;
255
0
      if (h.type() != bdecode_node::dict_t) return false;
256
0
      bdecode_node const messages = h.dict_find_dict("m");
257
0
      if (!messages) return false;
258
259
0
      int const index = int(messages.dict_find_int_value(extension_name, -1));
260
0
      if (index == -1) return false;
261
0
      m_message_index = index;
262
0
      return true;
263
0
    }
264
265
    bool on_extended(int const length, int const msg, span<char const> body) override
266
0
    {
267
0
      if (msg != extension_index) return false;
268
0
      if (m_message_index == 0) return false;
269
270
0
      if (m_torrent.flags() & torrent_flags::disable_pex) return true;
271
272
0
      if (length > 500 * 1024)
273
0
      {
274
0
        m_pc.disconnect(errors::pex_message_too_large, operation_t::bittorrent, peer_connection_interface::peer_error);
275
0
        return true;
276
0
      }
277
278
0
      if (int(body.size()) < length) return true;
279
280
0
      time_point const now = aux::time_now();
281
0
      if (now - seconds(60) <  m_last_pex[0])
282
0
      {
283
        // this client appears to be trying to flood us
284
        // with pex messages. Don't allow that.
285
0
        m_pc.disconnect(errors::too_frequent_pex, operation_t::bittorrent);
286
0
        return true;
287
0
      }
288
289
0
      int const num_pex_timers = sizeof(m_last_pex) / sizeof(m_last_pex[0]);
290
0
      for (int i = 0; i < num_pex_timers - 1; ++i)
291
0
        m_last_pex[i] = m_last_pex[i + 1];
292
0
      m_last_pex[num_pex_timers - 1] = now;
293
294
0
      bdecode_node pex_msg;
295
0
      error_code ec;
296
0
      int const ret = bdecode(body.begin(), body.end(), pex_msg, ec);
297
0
      if (ret != 0 || pex_msg.type() != bdecode_node::dict_t)
298
0
      {
299
0
        m_pc.disconnect(errors::invalid_pex_message, operation_t::bittorrent, peer_connection_interface::peer_error);
300
0
        return true;
301
0
      }
302
303
0
      bdecode_node p = pex_msg.dict_find_string("dropped");
304
305
#ifndef TORRENT_DISABLE_LOGGING
306
      int num_dropped = 0;
307
      int num_added = 0;
308
      if (p) num_dropped += p.string_length() / 6;
309
#endif
310
0
      if (p)
311
0
      {
312
0
        int const num_peers = p.string_length() / 6;
313
0
        char const* in = p.string_ptr();
314
315
0
        for (int i = 0; i < num_peers; ++i)
316
0
        {
317
0
          tcp::endpoint const adr = aux::read_v4_endpoint<tcp::endpoint>(in);
318
0
          peers4_t::value_type const v(adr.address().to_v4().to_bytes(), adr.port());
319
0
          auto const j = std::lower_bound(m_peers.begin(), m_peers.end(), v);
320
0
          if (j != m_peers.end() && *j == v) m_peers.erase(j);
321
0
        }
322
0
      }
323
324
0
      p = pex_msg.dict_find_string("added");
325
0
      bdecode_node const pf = pex_msg.dict_find_string("added.f");
326
327
0
      bool peers_added = false;
328
#ifndef TORRENT_DISABLE_LOGGING
329
      if (p) num_added += p.string_length() / 6;
330
#endif
331
0
      if (p && pf && pf.string_length() == p.string_length() / 6)
332
0
      {
333
0
        int const num_peers = pf.string_length();
334
0
        char const* in = p.string_ptr();
335
0
        char const* fin = pf.string_ptr();
336
337
0
        for (int i = 0; i < num_peers; ++i)
338
0
        {
339
0
          tcp::endpoint const adr = aux::read_v4_endpoint<tcp::endpoint>(in);
340
0
          pex_flags_t flags(static_cast<std::uint8_t>(*fin++));
341
342
          // this is an internal flag. disregard it from the internet
343
0
          flags &= ~pex_lt_v2;
344
345
0
          if (int(m_peers.size()) >= m_torrent.settings().get_int(settings_pack::max_pex_peers))
346
0
            break;
347
348
          // ignore local addresses unless the peer is local to us
349
0
          if (aux::is_local(adr.address()) && !aux::is_local(m_pc.remote().address())) continue;
350
351
0
          peers4_t::value_type const v(adr.address().to_v4().to_bytes(), adr.port());
352
0
          auto const j = std::lower_bound(m_peers.begin(), m_peers.end(), v);
353
          // do we already know about this peer?
354
0
          if (j != m_peers.end() && *j == v) continue;
355
0
          m_peers.insert(j, v);
356
0
          m_torrent.add_peer(adr, peer_info::pex, flags);
357
0
          peers_added = true;
358
0
        }
359
0
      }
360
361
0
      bdecode_node p6 = pex_msg.dict_find_string("dropped6");
362
0
      if (p6)
363
0
      {
364
#ifndef TORRENT_DISABLE_LOGGING
365
        num_dropped += p6.string_length() / 18;
366
#endif
367
0
        int const num_peers = p6.string_length() / 18;
368
0
        char const* in = p6.string_ptr();
369
370
0
        for (int i = 0; i < num_peers; ++i)
371
0
        {
372
0
          tcp::endpoint const adr = aux::read_v6_endpoint<tcp::endpoint>(in);
373
0
          peers6_t::value_type const v(adr.address().to_v6().to_bytes(), adr.port());
374
0
          auto const j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v);
375
0
          if (j != m_peers6.end() && *j == v) m_peers6.erase(j);
376
0
        }
377
0
      }
378
379
0
      p6 = pex_msg.dict_find_string("added6");
380
#ifndef TORRENT_DISABLE_LOGGING
381
      if (p6) num_added += p6.string_length() / 18;
382
#endif
383
0
      bdecode_node const p6f = pex_msg.dict_find_string("added6.f");
384
0
      if (p6 && p6f && p6f.string_length() == p6.string_length() / 18)
385
0
      {
386
0
        int const num_peers = p6f.string_length();
387
0
        char const* in = p6.string_ptr();
388
0
        char const* fin = p6f.string_ptr();
389
390
0
        for (int i = 0; i < num_peers; ++i)
391
0
        {
392
0
          tcp::endpoint const adr = aux::read_v6_endpoint<tcp::endpoint>(in);
393
0
          pex_flags_t flags(static_cast<std::uint8_t>(*fin++));
394
395
          // this is an internal flag. disregard it from the internet
396
0
          flags &= ~pex_lt_v2;
397
398
          // ignore local addresses unless the peer is local to us
399
0
          if (aux::is_local(adr.address()) && !aux::is_local(m_pc.remote().address())) continue;
400
0
          if (int(m_peers6.size()) >= m_torrent.settings().get_int(settings_pack::max_pex_peers))
401
0
            break;
402
403
0
          peers6_t::value_type const v(adr.address().to_v6().to_bytes(), adr.port());
404
0
          auto const j = std::lower_bound(m_peers6.begin(), m_peers6.end(), v);
405
          // do we already know about this peer?
406
0
          if (j != m_peers6.end() && *j == v) continue;
407
0
          m_peers6.insert(j, v);
408
0
          m_torrent.add_peer(adr, peer_info::pex, flags);
409
0
          peers_added = true;
410
0
        }
411
0
      }
412
#ifndef TORRENT_DISABLE_LOGGING
413
      m_pc.peer_log(peer_log_alert::incoming_message, "PEX", "dropped: %d added: %d"
414
        , num_dropped, num_added);
415
#endif
416
417
0
      m_pc.stats_counters().inc_stats_counter(counters::num_incoming_pex);
418
419
0
      if (peers_added) m_torrent.do_connect_boost();
420
0
      return true;
421
0
    }
422
423
    // the peers second tick
424
    // every minute we send a pex message
425
    void tick() override
426
0
    {
427
      // no handshake yet
428
0
      if (!m_message_index) return;
429
430
0
      time_point const now = aux::time_now();
431
0
      if (now - seconds(60) < m_last_msg)
432
0
      {
433
#ifndef TORRENT_DISABLE_LOGGING
434
//        m_pc.peer_log(peer_log_alert::info, "PEX", "waiting: %d seconds to next msg"
435
//          , int(total_seconds(seconds(60) - (now - m_last_msg))));
436
#endif
437
0
        return;
438
0
      }
439
0
      int const num_peers = m_torrent.num_peers();
440
0
      if (num_peers <= 1) return;
441
442
0
      m_last_msg = now;
443
444
0
      if (m_first_time)
445
0
      {
446
0
        send_ut_peer_list();
447
0
        m_first_time = false;
448
0
      }
449
0
      else
450
0
      {
451
0
        send_ut_peer_diff();
452
0
      }
453
0
    }
454
455
    void send_ut_peer_diff()
456
0
    {
457
0
      if (m_torrent.flags() & torrent_flags::disable_pex) return;
458
459
      // if there's no change in out peer set, don't send anything
460
0
      if (m_tp.peers_in_msg() == 0) return;
461
462
0
      std::vector<char> const& pex_msg = m_tp.get_ut_pex_msg();
463
464
0
      char msg[6];
465
0
      char* ptr = msg;
466
467
0
      aux::write_uint32(1 + 1 + int(pex_msg.size()), ptr);
468
0
      aux::write_uint8(bt_peer_connection::msg_extended, ptr);
469
0
      aux::write_uint8(m_message_index, ptr);
470
0
      m_pc.send_buffer(msg);
471
0
      m_pc.send_buffer(pex_msg);
472
473
0
      m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_extended);
474
0
      m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_pex);
475
476
#ifndef TORRENT_DISABLE_LOGGING
477
      if (m_pc.should_log(peer_log_alert::outgoing_message))
478
      {
479
        bdecode_node m;
480
        error_code ec;
481
        int const ret = bdecode(&pex_msg[0], &pex_msg[0] + pex_msg.size(), m, ec);
482
        TORRENT_ASSERT(ret == 0);
483
        TORRENT_ASSERT(!ec);
484
        TORRENT_UNUSED(ret);
485
        int num_dropped = 0;
486
        int num_added = 0;
487
        bdecode_node e = m.dict_find_string("added");
488
        if (e) num_added += e.string_length() / 6;
489
        e = m.dict_find_string("dropped");
490
        if (e) num_dropped += e.string_length() / 6;
491
        e = m.dict_find_string("added6");
492
        if (e) num_added += e.string_length() / 18;
493
        e = m.dict_find_string("dropped6");
494
        if (e) num_dropped += e.string_length() / 18;
495
        m_pc.peer_log(peer_log_alert::outgoing_message, "PEX_DIFF", "dropped: %d added: %d msg_size: %d"
496
          , num_dropped, num_added, int(pex_msg.size()));
497
      }
498
#endif
499
0
    }
500
501
    void send_ut_peer_list()
502
0
    {
503
0
      if (m_torrent.flags() & torrent_flags::disable_pex) return;
504
505
0
      entry pex;
506
      // leave the dropped string empty
507
0
      pex["dropped"].string();
508
0
      std::string& pla = pex["added"].string();
509
0
      std::string& plf = pex["added.f"].string();
510
0
      std::back_insert_iterator<std::string> pla_out(pla);
511
0
      std::back_insert_iterator<std::string> plf_out(plf);
512
513
0
      pex["dropped6"].string();
514
0
      std::string& pla6 = pex["added6"].string();
515
0
      std::string& plf6 = pex["added6.f"].string();
516
0
      std::back_insert_iterator<std::string> pla6_out(pla6);
517
0
      std::back_insert_iterator<std::string> plf6_out(plf6);
518
519
0
      int num_added = 0;
520
0
      for (auto const peer : m_torrent)
521
0
      {
522
0
        if (!send_peer(*peer)) continue;
523
524
        // don't write too big of a package
525
0
        if (num_added >= max_peer_entries) break;
526
527
        // only send proper bittorrent peers
528
0
        if (peer->type() != connection_type::bittorrent)
529
0
          continue;
530
531
0
        auto const* const p = static_cast<bt_peer_connection const*>(peer);
532
533
        // no supported flags to set yet
534
        // 0x01 - peer supports encryption
535
        // 0x02 - peer is a seed
536
        // 0x04 - supports uTP. This is only a positive flags
537
        //        passing 0 doesn't mean the peer doesn't
538
        //        support uTP
539
        // 0x08 - supports hole punching protocol. If this
540
        //        flag is received from a peer, it can be
541
        //        used as a rendezvous point in case direct
542
        //        connections to the peer fail
543
0
        int flags = p->is_seed() ? 2 : 0;
544
0
#if !defined TORRENT_DISABLE_ENCRYPTION
545
0
        flags |= p->supports_encryption() ? 1 : 0;
546
0
#endif
547
0
        flags |= is_utp(p->get_socket()) ? 4 :  0;
548
0
        flags |= p->supports_holepunch() ? 8 : 0;
549
550
0
        tcp::endpoint remote = peer->remote();
551
552
0
        if (!p->is_outgoing())
553
0
        {
554
0
          torrent_peer const* const pi = peer->peer_info_struct();
555
0
          if (pi != nullptr && pi->port > 0)
556
0
            remote.port(pi->port);
557
0
        }
558
559
        // i->first was added since the last time
560
0
        if (aux::is_v4(remote))
561
0
        {
562
0
          aux::write_endpoint(remote, pla_out);
563
0
          aux::write_uint8(flags, plf_out);
564
0
        }
565
0
        else
566
0
        {
567
0
          aux::write_endpoint(remote, pla6_out);
568
0
          aux::write_uint8(flags, plf6_out);
569
0
        }
570
0
        ++num_added;
571
0
      }
572
0
      std::vector<char> pex_msg;
573
0
      bencode(std::back_inserter(pex_msg), pex);
574
575
0
      char msg[6];
576
0
      char* ptr = msg;
577
578
0
      aux::write_uint32(1 + 1 + int(pex_msg.size()), ptr);
579
0
      aux::write_uint8(bt_peer_connection::msg_extended, ptr);
580
0
      aux::write_uint8(m_message_index, ptr);
581
0
      m_pc.send_buffer(msg);
582
0
      m_pc.send_buffer(pex_msg);
583
584
0
      m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_extended);
585
0
      m_pc.stats_counters().inc_stats_counter(counters::num_outgoing_pex);
586
587
#ifndef TORRENT_DISABLE_LOGGING
588
      m_pc.peer_log(peer_log_alert::outgoing_message, "PEX_FULL"
589
        , "added: %d msg_size: %d", num_added, int(pex_msg.size()));
590
#endif
591
0
    }
592
593
    torrent& m_torrent;
594
    peer_connection& m_pc;
595
    ut_pex_plugin& m_tp;
596
597
    // the last pex messages we received
598
    // [0] is the oldest one. There is a problem with
599
    // rate limited connections, because we may sit
600
    // for a long time, accumulating pex messages, and
601
    // then once we read from the socket it will look like
602
    // we received them all back to back. That's why
603
    // we look at 6 pex messages back.
604
    time_point m_last_pex[6];
605
606
    time_point m_last_msg;
607
    int m_message_index;
608
609
    // this is initialized to true, and set to
610
    // false after the first pex message has been sent.
611
    // it is used to know if a diff message or a) ful
612
    // message should be sent.
613
    bool m_first_time;
614
615
    // explicitly disallow assignment, to silence msvc warning
616
    ut_pex_peer_plugin& operator=(ut_pex_peer_plugin const&) = delete;
617
  };
618
619
  std::shared_ptr<peer_plugin> ut_pex_plugin::new_connection(peer_connection_handle const& pc)
620
0
  {
621
0
    if (pc.type() != connection_type::bittorrent) return {};
622
623
0
    bt_peer_connection* c = static_cast<bt_peer_connection*>(pc.native_handle().get());
624
0
    auto p = std::make_shared<ut_pex_peer_plugin>(m_torrent, *c, *this);
625
0
    c->set_ut_pex(p);
626
0
    return p;
627
0
  }
628
} }
629
630
namespace libtorrent {
631
632
  std::shared_ptr<torrent_plugin> create_ut_pex_plugin(torrent_handle const& th, client_data_t)
633
0
  {
634
0
    torrent* t = th.native_handle().get();
635
0
    if (t->torrent_file().priv() || (t->torrent_file().is_i2p()
636
0
      && !t->settings().get_bool(settings_pack::allow_i2p_mixed)))
637
0
    {
638
0
      return {};
639
0
    }
640
0
    return std::make_shared<ut_pex_plugin>(*t);
641
0
  }
642
}
643
644
#endif