Coverage Report

Created: 2025-06-12 06:27

/src/libtorrent/src/udp_tracker_connection.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
3
Copyright (c) 2015, Mikhail Titov
4
Copyright (c) 2004-2020, Arvid Norberg
5
Copyright (c) 2016-2018, 2020, Alden Torres
6
Copyright (c) 2016, Pavel Pimenov
7
Copyright (c) 2016-2017, Steven Siloti
8
All rights reserved.
9
10
Redistribution and use in source and binary forms, with or without
11
modification, are permitted provided that the following conditions
12
are met:
13
14
    * Redistributions of source code must retain the above copyright
15
      notice, this list of conditions and the following disclaimer.
16
    * Redistributions in binary form must reproduce the above copyright
17
      notice, this list of conditions and the following disclaimer in
18
      the documentation and/or other materials provided with the distribution.
19
    * Neither the name of the author nor the names of its
20
      contributors may be used to endorse or promote products derived
21
      from this software without specific prior written permission.
22
23
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
POSSIBILITY OF SUCH DAMAGE.
34
35
*/
36
37
#include <cctype>
38
#include <functional>
39
#include <tuple>
40
41
#include "libtorrent/parse_url.hpp"
42
#include "libtorrent/udp_tracker_connection.hpp"
43
#include "libtorrent/hex.hpp"
44
#include "libtorrent/random.hpp"
45
#include "libtorrent/aux_/session_settings.hpp"
46
#include "libtorrent/aux_/resolver_interface.hpp"
47
#include "libtorrent/ip_filter.hpp"
48
#include "libtorrent/aux_/time.hpp"
49
#include "libtorrent/aux_/io.hpp"
50
#include "libtorrent/aux_/ip_helpers.hpp" // for is_v6
51
#include "libtorrent/peer.hpp"
52
#include "libtorrent/error_code.hpp"
53
54
#ifndef TORRENT_DISABLE_LOGGING
55
#include "libtorrent/socket_io.hpp"
56
#endif
57
58
namespace libtorrent {
59
60
  std::map<address, udp_tracker_connection::connection_cache_entry>
61
    udp_tracker_connection::m_connection_cache;
62
63
  std::mutex udp_tracker_connection::m_cache_mutex;
64
65
  udp_tracker_connection::udp_tracker_connection(
66
    io_context& ios
67
    , tracker_manager& man
68
    , tracker_request const& req
69
    , std::weak_ptr<request_callback> c)
70
0
    : tracker_connection(man, req, ios, std::move(c))
71
0
    , m_transaction_id(0)
72
0
    , m_attempts(0)
73
0
    , m_state(action_t::error)
74
0
    , m_abort(false)
75
0
  {
76
0
    update_transaction_id();
77
0
  }
78
79
  void udp_tracker_connection::start()
80
0
  {
81
    // TODO: 2 support authentication here. tracker_req().auth
82
0
    std::string hostname;
83
0
    std::string protocol;
84
0
    int port;
85
0
    error_code ec;
86
87
0
    std::tie(protocol, std::ignore, hostname, port, std::ignore)
88
0
      = parse_url_components(tracker_req().url, ec);
89
0
    if (port == -1) port = protocol == "http" ? 80 : 443;
90
91
0
    if (ec)
92
0
    {
93
0
      tracker_connection::fail(ec, operation_t::parse_address);
94
0
      return;
95
0
    }
96
97
0
    aux::session_settings const& settings = m_man.settings();
98
99
0
    int const proxy_type = settings.get_int(settings_pack::proxy_type);
100
101
0
    if (settings.get_bool(settings_pack::proxy_hostnames)
102
0
      && (proxy_type == settings_pack::socks5
103
0
        || proxy_type == settings_pack::socks5_pw))
104
0
    {
105
0
      m_hostname = hostname;
106
0
      m_target.port(std::uint16_t(port));
107
0
      start_announce();
108
0
    }
109
0
    else
110
0
    {
111
0
      using namespace std::placeholders;
112
0
      ADD_OUTSTANDING_ASYNC("udp_tracker_connection::name_lookup");
113
      // when stopping, pass in the cache-only flag, because we
114
      // don't want to get stuck on DNS lookups when shutting down
115
0
      m_man.host_resolver().async_resolve(hostname
116
0
        , (tracker_req().event == event_t::stopped
117
0
          ? aux::resolver_interface::cache_only : aux::resolver_flags{})
118
0
          | aux::resolver_interface::abort_on_shutdown
119
0
        , std::bind(&udp_tracker_connection::name_lookup
120
0
          , shared_from_this(), _1, _2, port));
121
122
#ifndef TORRENT_DISABLE_LOGGING
123
      std::shared_ptr<request_callback> cb = requester();
124
      if (cb) cb->debug_log("*** UDP_TRACKER [ initiating name lookup: \"%s\" ]"
125
        , hostname.c_str());
126
#endif
127
0
    }
128
129
0
    set_timeout(tracker_req().event == event_t::stopped
130
0
      ? settings.get_int(settings_pack::stop_tracker_timeout)
131
0
      : settings.get_int(settings_pack::tracker_completion_timeout)
132
0
      , settings.get_int(settings_pack::tracker_receive_timeout));
133
0
  }
134
135
  void udp_tracker_connection::fail(error_code const& ec, operation_t const op
136
    , char const* msg, seconds32 const interval, seconds32 const min_interval)
137
0
  {
138
    // m_target failed. remove it from the endpoint list
139
0
    auto const i = std::find(m_endpoints.begin()
140
0
      , m_endpoints.end(), make_tcp(m_target));
141
142
0
    if (i != m_endpoints.end()) m_endpoints.erase(i);
143
144
    // if that was the last one, or the listen socket was closed
145
    // fail the whole announce
146
0
    if (m_endpoints.empty() || !tracker_req().outgoing_socket)
147
0
    {
148
0
      tracker_connection::fail(ec, op, msg, interval, min_interval);
149
0
      return;
150
0
    }
151
152
#ifndef TORRENT_DISABLE_LOGGING
153
    std::shared_ptr<request_callback> cb = requester();
154
    if (cb && cb->should_log())
155
    {
156
      cb->debug_log(R"(*** UDP_TRACKER [ host: "%s" ip: "%s" | ERROR: "%s" ])"
157
        , m_hostname.c_str(), print_endpoint(m_target).c_str(), ec.message().c_str());
158
    }
159
#endif
160
161
    // pick another target endpoint and try again
162
0
    m_target = make_udp(m_endpoints.front());
163
164
#ifndef TORRENT_DISABLE_LOGGING
165
    if (cb && cb->should_log())
166
    {
167
      cb->debug_log(R"(*** UDP_TRACKER trying next IP [ host: "%s" ip: "%s" ])"
168
        , m_hostname.c_str(), print_endpoint(m_target).c_str());
169
    }
170
#endif
171
0
    post(get_executor(), std::bind(
172
0
      &udp_tracker_connection::start_announce, shared_from_this()));
173
174
0
    aux::session_settings const& settings = m_man.settings();
175
0
    set_timeout(tracker_req().event == event_t::stopped
176
0
      ? settings.get_int(settings_pack::stop_tracker_timeout)
177
0
      : settings.get_int(settings_pack::tracker_completion_timeout)
178
0
      , settings.get_int(settings_pack::tracker_receive_timeout));
179
0
  }
180
181
  void udp_tracker_connection::name_lookup(error_code const& error
182
    , std::vector<address> const& addresses, int port)
183
0
  {
184
0
    COMPLETE_ASYNC("udp_tracker_connection::name_lookup");
185
0
    if (m_abort) return;
186
0
    if (error == boost::asio::error::operation_aborted) return;
187
0
    if (error || addresses.empty())
188
0
    {
189
0
      fail(error, operation_t::hostname_lookup);
190
0
      return;
191
0
    }
192
193
0
    std::shared_ptr<request_callback> cb = requester();
194
#ifndef TORRENT_DISABLE_LOGGING
195
    if (cb) cb->debug_log("*** UDP_TRACKER [ name lookup successful ]");
196
#endif
197
0
    if (cancelled())
198
0
    {
199
0
      fail(error_code(errors::torrent_aborted), operation_t::hostname_lookup);
200
0
      return;
201
0
    }
202
203
0
    restart_read_timeout();
204
205
0
    if (!tracker_req().outgoing_socket)
206
0
    {
207
0
      fail(error_code(errors::invalid_listen_socket), operation_t::hostname_lookup);
208
0
      return;
209
0
    }
210
211
0
    auto const listen_socket = bind_socket();
212
213
    // filter all endpoints we cannot reach from this listen socket, which may
214
    // be all of them, in which case we should not announce this listen socket
215
    // to this tracker
216
0
    for (auto const& addr : addresses)
217
0
    {
218
0
      if (!listen_socket.can_route(addr)) continue;
219
0
      m_endpoints.emplace_back(addr, std::uint16_t(port));
220
0
    }
221
222
0
    if (m_endpoints.empty())
223
0
    {
224
0
      fail(lt::errors::announce_skipped, operation_t::hostname_lookup);
225
0
      return;
226
0
    }
227
228
0
    if (tracker_req().filter)
229
0
    {
230
      // remove endpoints that are filtered by the IP filter
231
0
      for (auto k = m_endpoints.begin(); k != m_endpoints.end();)
232
0
      {
233
0
        if (tracker_req().filter->access(k->address()) == ip_filter::blocked)
234
0
        {
235
#ifndef TORRENT_DISABLE_LOGGING
236
          if (cb && cb->should_log())
237
          {
238
            cb->debug_log("*** UDP_TRACKER [ IP blocked by filter: %s ]"
239
              , print_address(k->address()).c_str());
240
          }
241
#endif
242
0
          k = m_endpoints.erase(k);
243
0
        }
244
0
        else
245
0
          ++k;
246
0
      }
247
0
    }
248
249
    // if all endpoints were filtered by the IP filter, we can't connect
250
0
    if (m_endpoints.empty())
251
0
    {
252
0
      fail(error_code(errors::banned_by_ip_filter), operation_t::hostname_lookup);
253
0
      return;
254
0
    }
255
256
0
    m_target = make_udp(m_endpoints.front());
257
258
0
    start_announce();
259
0
  }
260
261
  void udp_tracker_connection::start_announce()
262
0
  {
263
0
    std::unique_lock<std::mutex> l(m_cache_mutex);
264
0
    auto const cc = m_connection_cache.find(m_target.address());
265
0
    if (cc != m_connection_cache.end())
266
0
    {
267
      // we found a cached entry! Now, we can only
268
      // use if if it hasn't expired
269
0
      if (aux::time_now() < cc->second.expires)
270
0
      {
271
0
        if (tracker_req().kind & tracker_request::scrape_request)
272
0
          send_udp_scrape();
273
0
        else
274
0
          send_udp_announce();
275
0
        return;
276
0
      }
277
      // if it expired, remove it from the cache
278
0
      m_connection_cache.erase(cc);
279
0
    }
280
0
    l.unlock();
281
282
0
    send_udp_connect();
283
0
  }
284
285
  void udp_tracker_connection::on_timeout(error_code const& ec)
286
0
  {
287
0
    if (ec)
288
0
    {
289
0
      fail(ec, operation_t::timer);
290
0
      return;
291
0
    }
292
293
#ifndef TORRENT_DISABLE_LOGGING
294
    std::shared_ptr<request_callback> cb = requester();
295
    if (cb) cb->debug_log("*** UDP_TRACKER [ timed out url: %s ]", tracker_req().url.c_str());
296
#endif
297
0
    fail(error_code(errors::timed_out), operation_t::timer);
298
0
  }
299
300
  void udp_tracker_connection::close()
301
0
  {
302
0
    cancel();
303
0
    m_man.remove_request(this);
304
0
  }
305
306
  bool udp_tracker_connection::on_receive_hostname(string_view const hostname
307
    , span<char const> buf)
308
0
  {
309
0
    TORRENT_UNUSED(hostname);
310
    // just ignore the hostname this came from, pretend that
311
    // it's from the same endpoint we sent it to (i.e. the same
312
    // port). We have so many other ways of confirming this packet
313
    // comes from the tracker anyway, so it's not a big deal
314
0
    return on_receive(m_target, buf);
315
0
  }
316
317
  bool udp_tracker_connection::on_receive(udp::endpoint const& ep
318
    , span<char const> const buf)
319
0
  {
320
#ifndef TORRENT_DISABLE_LOGGING
321
    std::shared_ptr<request_callback> cb = requester();
322
#endif
323
324
    // ignore responses before we've sent any requests
325
0
    if (m_state == action_t::error)
326
0
    {
327
#ifndef TORRENT_DISABLE_LOGGING
328
      if (cb) cb->debug_log("<== UDP_TRACKER [ m_action == error ]");
329
#endif
330
0
      return false;
331
0
    }
332
333
0
    if (m_abort)
334
0
    {
335
#ifndef TORRENT_DISABLE_LOGGING
336
      if (cb) cb->debug_log("<== UDP_TRACKER [ aborted]");
337
#endif
338
0
      return false;
339
0
    }
340
341
    // ignore packet not sent from the tracker
342
    // if m_target is inaddr_any, it suggests that we
343
    // sent the packet through a proxy only knowing
344
    // the hostname, in which case this packet might be for us
345
0
    if (!m_target.address().is_unspecified() && m_target != ep)
346
0
    {
347
#ifndef TORRENT_DISABLE_LOGGING
348
      if (cb && cb->should_log())
349
      {
350
        cb->debug_log("<== UDP_TRACKER [ unexpected source IP: %s "
351
          "expected: %s ]"
352
          , print_endpoint(ep).c_str()
353
          , print_endpoint(m_target).c_str());
354
      }
355
#endif
356
0
      return false;
357
0
    }
358
359
#ifndef TORRENT_DISABLE_LOGGING
360
    if (cb) cb->debug_log("<== UDP_TRACKER_PACKET [ size: %d ]"
361
      , int(buf.size()));
362
#endif
363
364
    // ignore packets smaller than 8 bytes
365
0
    if (buf.size() < 8) return false;
366
367
0
    span<const char> ptr = buf;
368
0
    auto const action = static_cast<action_t>(aux::read_int32(ptr));
369
0
    std::uint32_t const transaction = aux::read_uint32(ptr);
370
371
#ifndef TORRENT_DISABLE_LOGGING
372
    if (cb) cb->debug_log("*** UDP_TRACKER_PACKET [ action: %d ]"
373
      , static_cast<int>(action));
374
#endif
375
376
    // ignore packets with incorrect transaction id
377
0
    if (m_transaction_id != transaction)
378
0
    {
379
#ifndef TORRENT_DISABLE_LOGGING
380
    if (cb) cb->debug_log("*** UDP_TRACKER_PACKET [ tid: %x ]"
381
        , int(transaction));
382
#endif
383
0
      return false;
384
0
    }
385
386
0
    if (action == action_t::error)
387
0
    {
388
0
      fail(error_code(errors::tracker_failure), operation_t::bittorrent
389
0
        , std::string(buf.data(), static_cast<std::size_t>(buf.size())).c_str());
390
0
      return true;
391
0
    }
392
393
    // ignore packets that's not a response to our message
394
0
    if (action != m_state)
395
0
    {
396
#ifndef TORRENT_DISABLE_LOGGING
397
      if (cb) cb->debug_log("*** UDP_TRACKER_PACKET [ unexpected action: %d "
398
        " expected: %d ]", static_cast<int>(action), static_cast<int>(m_state));
399
#endif
400
0
      return false;
401
0
    }
402
403
0
    restart_read_timeout();
404
405
#ifndef TORRENT_DISABLE_LOGGING
406
    if (cb)
407
      cb->debug_log("*** UDP_TRACKER_RESPONSE [ tid: %x ]"
408
        , int(transaction));
409
#endif
410
411
0
    switch (m_state)
412
0
    {
413
0
      case action_t::connect:
414
0
        return on_connect_response(buf);
415
0
      case action_t::announce:
416
0
        return on_announce_response(buf);
417
0
      case action_t::scrape:
418
0
        return on_scrape_response(buf);
419
0
      case action_t::error:
420
0
        return false;
421
0
    }
422
0
    return false;
423
0
  }
424
425
  void udp_tracker_connection::update_transaction_id()
426
0
  {
427
    // don't use 0, because that has special meaning (uninitialized)
428
0
    std::uint32_t const new_tid = random(0xfffffffe) + 1;
429
430
0
    if (m_transaction_id != 0)
431
0
      m_man.update_transaction_id(shared_from_this(), new_tid);
432
0
    m_transaction_id = new_tid;
433
0
  }
434
435
  bool udp_tracker_connection::on_connect_response(span<char const> buf)
436
0
  {
437
    // ignore packets smaller than 16 bytes
438
0
    if (buf.size() < 16) return false;
439
440
0
    restart_read_timeout();
441
442
    // skip header
443
0
    buf = buf.subspan(8);
444
445
    // reset transaction
446
0
    update_transaction_id();
447
0
    std::int64_t const connection_id = aux::read_int64(buf);
448
449
0
    std::lock_guard<std::mutex> l(m_cache_mutex);
450
0
    connection_cache_entry& cce = m_connection_cache[m_target.address()];
451
0
    cce.connection_id = connection_id;
452
0
    cce.expires = aux::time_now() + seconds(m_man.settings().get_int(settings_pack::udp_tracker_token_expiry));
453
454
0
    if (!(tracker_req().kind & tracker_request::scrape_request))
455
0
      send_udp_announce();
456
0
    else if (tracker_req().kind & tracker_request::scrape_request)
457
0
      send_udp_scrape();
458
0
    return true;
459
0
  }
460
461
  void udp_tracker_connection::send_udp_connect()
462
0
  {
463
#ifndef TORRENT_DISABLE_LOGGING
464
    std::shared_ptr<request_callback> cb = requester();
465
#endif
466
467
0
    if (m_abort)
468
0
    {
469
#ifndef TORRENT_DISABLE_LOGGING
470
      if (cb) cb->debug_log("==> UDP_TRACKER_CONNECT [ skipped, m_abort ]");
471
#endif
472
0
      return;
473
0
    }
474
475
0
    std::size_t const connect_packet_size = 16;
476
0
    std::array<char, connect_packet_size> buf;
477
0
    span<char> view = buf;
478
479
0
    TORRENT_ASSERT(m_transaction_id != 0);
480
481
0
    aux::write_uint32(0x417, view);
482
0
    aux::write_uint32(0x27101980, view); // connection_id
483
0
    aux::write_int32(action_t::connect, view); // action (connect)
484
0
    aux::write_int32(m_transaction_id, view); // transaction_id
485
0
    TORRENT_ASSERT(view.empty());
486
487
0
    error_code ec;
488
0
    if (!m_hostname.empty())
489
0
    {
490
0
      m_man.send_hostname(bind_socket(), m_hostname.c_str()
491
0
        , m_target.port(), buf, ec
492
0
        , udp_socket::tracker_connection);
493
0
    }
494
0
    else
495
0
    {
496
0
      m_man.send(bind_socket(), m_target, buf, ec
497
0
        , udp_socket::tracker_connection);
498
0
    }
499
500
0
    ++m_attempts;
501
0
    if (ec)
502
0
    {
503
#ifndef TORRENT_DISABLE_LOGGING
504
      if (cb && cb->should_log())
505
      {
506
        cb->debug_log("==> UDP_TRACKER_CONNECT [ failed: %s ]"
507
          , ec.message().c_str());
508
      }
509
#endif
510
0
      fail(ec, operation_t::sock_write);
511
0
      return;
512
0
    }
513
514
#ifndef TORRENT_DISABLE_LOGGING
515
    if (cb && cb->should_log())
516
    {
517
      cb->debug_log("==> UDP_TRACKER_CONNECT [ to: %s ih: %s ]"
518
        , m_hostname.empty()
519
          ? print_endpoint(m_target).c_str()
520
          : (m_hostname + ":" + to_string(m_target.port()).data()).c_str()
521
        , aux::to_hex(tracker_req().info_hash).c_str());
522
    }
523
#endif
524
525
0
    m_state = action_t::connect;
526
0
    sent_bytes(16 + 28); // assuming UDP/IP header
527
0
  }
528
529
  void udp_tracker_connection::send_udp_scrape()
530
0
  {
531
0
    if (m_abort) return;
532
533
0
    auto const i = m_connection_cache.find(m_target.address());
534
    // this isn't really supposed to happen
535
0
    TORRENT_ASSERT(i != m_connection_cache.end());
536
0
    if (i == m_connection_cache.end()) return;
537
538
0
    char buf[8 + 4 + 4 + 20];
539
0
    span<char> view = buf;
540
541
0
    aux::write_int64(i->second.connection_id, view); // connection_id
542
0
    aux::write_int32(action_t::scrape, view); // action (scrape)
543
0
    aux::write_int32(m_transaction_id, view); // transaction_id
544
    // info_hash
545
0
    std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end()
546
0
      , view.data());
547
0
#if TORRENT_USE_ASSERTS
548
0
    TORRENT_ASSERT(view.size() == 20);
549
0
#endif
550
551
0
    error_code ec;
552
0
    if (!m_hostname.empty())
553
0
    {
554
0
      m_man.send_hostname(bind_socket(), m_hostname.c_str(), m_target.port()
555
0
        , buf, ec, udp_socket::tracker_connection);
556
0
    }
557
0
    else
558
0
    {
559
0
      m_man.send(bind_socket(), m_target, buf, ec
560
0
        , udp_socket::tracker_connection);
561
0
    }
562
0
    m_state = action_t::scrape;
563
0
    sent_bytes(sizeof(buf) + 28); // assuming UDP/IP header
564
0
    ++m_attempts;
565
0
    if (ec)
566
0
    {
567
0
      fail(ec, operation_t::sock_write);
568
0
      return;
569
0
    }
570
0
  }
571
572
  bool udp_tracker_connection::on_announce_response(span<char const> buf)
573
0
  {
574
0
    if (buf.size() < 20) return false;
575
576
0
    buf = buf.subspan(8);
577
0
    restart_read_timeout();
578
579
0
    tracker_response resp;
580
581
0
    resp.interval = seconds32(aux::read_int32(buf));
582
0
    resp.min_interval = seconds32(60);
583
0
    resp.incomplete = aux::read_int32(buf);
584
0
    resp.complete = aux::read_int32(buf);
585
586
0
    int const ip_stride = aux::is_v6(m_target) ? 18 : 6;
587
0
    auto const num_peers = buf.size() / ip_stride;
588
0
    if (buf.size() % ip_stride != 0)
589
0
    {
590
0
      fail(error_code(errors::invalid_tracker_response_length), operation_t::bittorrent);
591
0
      return false;
592
0
    }
593
594
0
    std::shared_ptr<request_callback> cb = requester();
595
#ifndef TORRENT_DISABLE_LOGGING
596
    if (cb)
597
    {
598
      cb->debug_log("<== UDP_TRACKER_RESPONSE [ url: %s ]", tracker_req().url.c_str());
599
    }
600
#endif
601
602
0
    if (!cb)
603
0
    {
604
0
      close();
605
0
      return true;
606
0
    }
607
608
0
    if (aux::is_v6(m_target))
609
0
    {
610
0
      resp.peers6.reserve(static_cast<std::size_t>(num_peers));
611
0
      for (int i = 0; i < num_peers; ++i)
612
0
      {
613
0
        ipv6_peer_entry e{};
614
0
        std::memcpy(e.ip.data(), buf.data(), 16);
615
0
        buf = buf.subspan(16);
616
0
        e.port = aux::read_uint16(buf);
617
0
        resp.peers6.push_back(e);
618
0
      }
619
0
    }
620
0
    else
621
0
    {
622
0
      resp.peers4.reserve(static_cast<std::size_t>(num_peers));
623
0
      for (int i = 0; i < num_peers; ++i)
624
0
      {
625
0
        ipv4_peer_entry e{};
626
0
        std::memcpy(e.ip.data(), buf.data(), 4);
627
0
        buf = buf.subspan(4);
628
0
        e.port = aux::read_uint16(buf);
629
0
        resp.peers4.push_back(e);
630
0
      }
631
0
    }
632
633
    // TODO: why is this a linked list?
634
0
    std::list<address> ip_list;
635
0
    std::transform(m_endpoints.begin(), m_endpoints.end(), std::back_inserter(ip_list)
636
0
      , [](tcp::endpoint const& ep) { return ep.address(); } );
637
638
0
    cb->tracker_response(tracker_req(), m_target.address(), ip_list, resp);
639
640
0
    close();
641
0
    return true;
642
0
  }
643
644
  bool udp_tracker_connection::on_scrape_response(span<char const> buf)
645
0
  {
646
0
    restart_read_timeout();
647
0
    auto const action = static_cast<action_t>(aux::read_int32(buf));
648
0
    std::uint32_t const transaction = aux::read_uint32(buf);
649
650
0
    if (transaction != m_transaction_id)
651
0
    {
652
0
      fail(error_code(errors::invalid_tracker_transaction_id), operation_t::bittorrent);
653
0
      return false;
654
0
    }
655
656
0
    if (action == action_t::error)
657
0
    {
658
0
      fail(error_code(errors::tracker_failure), operation_t::bittorrent
659
0
        , std::string(buf.data(), static_cast<std::size_t>(buf.size())).c_str());
660
0
      return true;
661
0
    }
662
663
0
    if (action != action_t::scrape)
664
0
    {
665
0
      fail(error_code(errors::invalid_tracker_action), operation_t::bittorrent);
666
0
      return true;
667
0
    }
668
669
0
    if (buf.size() < 12)
670
0
    {
671
0
      fail(error_code(errors::invalid_tracker_response_length), operation_t::bittorrent);
672
0
      return true;
673
0
    }
674
675
0
    int const complete = aux::read_int32(buf);
676
0
    int const downloaded = aux::read_int32(buf);
677
0
    int const incomplete = aux::read_int32(buf);
678
679
0
    std::shared_ptr<request_callback> cb = requester();
680
0
    if (!cb)
681
0
    {
682
0
      close();
683
0
      return true;
684
0
    }
685
686
0
    cb->tracker_scrape_response(tracker_req()
687
0
      , complete, incomplete, downloaded, -1);
688
689
0
    close();
690
0
    return true;
691
0
  }
692
693
  void udp_tracker_connection::send_udp_announce()
694
0
  {
695
0
    if (m_abort) return;
696
697
0
    char buf[800];
698
0
    span<char> out = buf;
699
700
0
    tracker_request const& req = tracker_req();
701
0
    aux::session_settings const& settings = m_man.settings();
702
703
0
    auto const i = m_connection_cache.find(m_target.address());
704
    // this isn't really supposed to happen
705
0
    TORRENT_ASSERT(i != m_connection_cache.end());
706
0
    if (i == m_connection_cache.end()) return;
707
708
0
    aux::write_int64(i->second.connection_id, out); // connection_id
709
0
    aux::write_int32(action_t::announce, out); // action (announce)
710
0
    aux::write_int32(m_transaction_id, out); // transaction_id
711
0
    std::copy(req.info_hash.begin(), req.info_hash.end(), out.data()); // info_hash
712
0
    out = out.subspan(20);
713
0
    std::copy(req.pid.begin(), req.pid.end(), out.data()); // peer_id
714
0
    out = out.subspan(20);
715
0
    aux::write_int64(req.downloaded, out); // downloaded
716
0
    aux::write_int64(req.left, out); // left
717
0
    aux::write_int64(req.uploaded, out); // uploaded
718
0
    aux::write_int32(req.event, out); // event
719
    // ip address
720
0
    address_v4 announce_ip;
721
722
0
    if (!settings.get_bool(settings_pack::anonymous_mode)
723
0
      && !settings.get_str(settings_pack::announce_ip).empty())
724
0
    {
725
0
      error_code ec;
726
0
      address ip = make_address(settings.get_str(settings_pack::announce_ip).c_str(), ec);
727
0
      if (!ec && ip.is_v4()) announce_ip = ip.to_v4();
728
0
    }
729
0
    aux::write_uint32(announce_ip.to_uint(), out);
730
0
    aux::write_int32(req.key, out); // key
731
0
    aux::write_int32(req.num_want, out); // num_want
732
0
    aux::write_uint16(req.listen_port, out); // port
733
734
0
    std::string request_string;
735
0
    error_code ec;
736
0
    using std::ignore;
737
0
    std::tie(ignore, ignore, ignore, ignore, request_string)
738
0
      = parse_url_components(req.url, ec);
739
0
    if (ec) request_string.clear();
740
741
0
    if (!request_string.empty())
742
0
    {
743
0
      std::size_t str_len = std::min(request_string.size(), std::size_t(255));
744
0
      request_string.resize(str_len);
745
746
0
      aux::write_uint8(2, out);
747
0
      aux::write_uint8(str_len, out);
748
0
      aux::write_string(request_string, out);
749
0
    }
750
751
#ifndef TORRENT_DISABLE_LOGGING
752
    std::shared_ptr<request_callback> cb = requester();
753
    if (cb && cb->should_log())
754
    {
755
      cb->debug_log("==> UDP_TRACKER_ANNOUNCE [%s]", aux::to_hex(req.info_hash).c_str());
756
    }
757
#endif
758
759
0
    if (!m_hostname.empty())
760
0
    {
761
0
      m_man.send_hostname(bind_socket(), m_hostname.c_str()
762
0
        , m_target.port(), {buf, int(sizeof(buf)) - out.size()}, ec
763
0
        , udp_socket::tracker_connection);
764
0
    }
765
0
    else
766
0
    {
767
0
      m_man.send(bind_socket(), m_target, {buf, int(sizeof(buf)) - out.size()}, ec
768
0
        , udp_socket::tracker_connection);
769
0
    }
770
0
    m_state = action_t::announce;
771
0
    sent_bytes(int(sizeof(buf)) - int(out.size()) + 28); // assuming UDP/IP header
772
0
    ++m_attempts;
773
0
    if (ec)
774
0
    {
775
0
      fail(ec, operation_t::sock_write);
776
0
      return;
777
0
    }
778
0
  }
779
780
}