Coverage Report

Created: 2026-01-10 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtorrent/src/announce_entry.cpp
Line
Count
Source
1
/*
2
3
Copyright (c) 2015-2021, Arvid Norberg
4
Copyright (c) 2016-2017, 2020, Alden Torres
5
Copyright (c) 2017-2018, Steven Siloti
6
All rights reserved.
7
8
Redistribution and use in source and binary forms, with or without
9
modification, are permitted provided that the following conditions
10
are met:
11
12
    * Redistributions of source code must retain the above copyright
13
      notice, this list of conditions and the following disclaimer.
14
    * Redistributions in binary form must reproduce the above copyright
15
      notice, this list of conditions and the following disclaimer in
16
      the documentation and/or other materials provided with the distribution.
17
    * Neither the name of the author nor the names of its
18
      contributors may be used to endorse or promote products derived
19
      from this software without specific prior written permission.
20
21
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
POSSIBILITY OF SUCH DAMAGE.
32
33
*/
34
35
#include "libtorrent/config.hpp"
36
#include "libtorrent/announce_entry.hpp"
37
#include "libtorrent/string_util.hpp" // for is_space
38
#include "libtorrent/aux_/time.hpp"
39
#include "libtorrent/aux_/session_settings.hpp"
40
#include "libtorrent/aux_/listen_socket_handle.hpp"
41
#include "libtorrent/aux_/announce_entry.hpp"
42
43
namespace libtorrent {
44
45
  namespace {
46
    // wait at least 5 seconds before retrying a failed tracker
47
    seconds32 constexpr tracker_retry_delay_min{ 5 };
48
49
    // never wait more than 60 minutes to retry a tracker
50
    minutes32 constexpr tracker_retry_delay_max{ 60 };
51
  }
52
53
TORRENT_VERSION_NAMESPACE_2
54
55
  announce_infohash::announce_infohash()
56
0
    : fails(0)
57
0
    , updating(false)
58
0
    , start_sent(false)
59
0
    , complete_sent(false)
60
0
    , triggered_manually(false)
61
0
  {}
62
63
  announce_entry::announce_entry(string_view u)
64
0
    : url(u.to_string())
65
0
    , source(0)
66
0
    , verified(false)
67
#if TORRENT_ABI_VERSION == 1
68
0
    , fails(0)
69
0
    , send_stats(false)
70
0
    , start_sent(false)
71
0
    , complete_sent(false)
72
0
    , triggered_manually(false)
73
0
    , updating(false)
74
#endif
75
0
  {}
76
77
  announce_entry::announce_entry()
78
0
    : source(0)
79
0
    , verified(false)
80
#if TORRENT_ABI_VERSION == 1
81
0
    , fails(0)
82
0
    , send_stats(false)
83
0
    , start_sent(false)
84
0
    , complete_sent(false)
85
0
    , triggered_manually(false)
86
0
    , updating(false)
87
#endif
88
0
  {}
89
90
0
  announce_entry::~announce_entry() = default;
91
0
  announce_entry::announce_entry(announce_entry const&) = default;
92
0
  announce_entry& announce_entry::operator=(announce_entry const&) & = default;
93
94
0
  announce_endpoint::announce_endpoint() = default;
95
96
#if TORRENT_ABI_VERSION <= 2
97
  void announce_infohash::reset()
98
0
  {
99
0
    start_sent = false;
100
0
    next_announce = time_point32::min();
101
0
    min_announce = time_point32::min();
102
0
  }
103
104
  void announce_infohash::failed(int const backoff_ratio, seconds32 const retry_interval)
105
0
  {
106
    // fails is only 7 bits
107
0
    if (fails < (1 << 7) - 1) ++fails;
108
109
    // the exponential back-off ends up being:
110
    // 7, 15, 27, 45, 95, 127, 165, ... seconds
111
    // with the default tracker_backoff of 250
112
0
    int const fail_square = int(fails) * int(fails);
113
0
    seconds32 const delay = std::max(retry_interval
114
0
      , std::min(duration_cast<seconds32>(tracker_retry_delay_max)
115
0
        , tracker_retry_delay_min
116
0
          + fail_square * tracker_retry_delay_min * backoff_ratio / 100
117
0
      ));
118
0
    TORRENT_ASSERT(delay <= tracker_retry_delay_max);
119
0
    if (!is_working()) next_announce = aux::time_now32() + delay;
120
0
    updating = false;
121
0
  }
122
123
  bool announce_infohash::can_announce(time_point now, bool is_seed, std::uint8_t fail_limit) const
124
0
  {
125
0
    TORRENT_ASSERT(next_announce <= now + tracker_retry_delay_max);
126
    // if we're a seed and we haven't sent a completed
127
    // event, we need to let this announce through
128
0
    bool const need_send_complete = is_seed && !complete_sent;
129
130
    // add some slack here for rounding errors
131
0
    return now + seconds(1) >= next_announce
132
0
      && (now >= min_announce || need_send_complete)
133
0
      && (fails < fail_limit || fail_limit == 0)
134
0
      && !updating;
135
0
  }
136
137
  void announce_endpoint::reset()
138
0
  {
139
0
    for (auto& ih : info_hashes)
140
0
      ih.reset();
141
142
0
    start_sent = false;
143
0
    next_announce = time_point32::min();
144
0
    min_announce = time_point32::min();
145
0
  }
146
147
  void announce_entry::reset()
148
0
  {
149
0
    for (auto& aep : endpoints)
150
0
      aep.reset();
151
0
  }
152
153
  bool announce_endpoint::can_announce(time_point now, bool is_seed, std::uint8_t fail_limit) const
154
0
  {
155
0
    return std::any_of(std::begin(info_hashes), std::end(info_hashes)
156
0
      , [&](announce_infohash const& ih) { return ih.can_announce(now, is_seed, fail_limit); });
157
0
  }
158
159
  bool announce_endpoint::is_working() const
160
0
  {
161
0
    return std::any_of(std::begin(info_hashes), std::end(info_hashes)
162
0
      , [](announce_infohash const& ih) { return ih.is_working(); });
163
0
  }
164
165
  void announce_entry::trim()
166
0
  {
167
0
    while (!url.empty() && is_space(url[0]))
168
0
      url.erase(url.begin());
169
0
  }
170
#endif
171
#if TORRENT_ABI_VERSION == 1
172
  bool announce_entry::can_announce(time_point now, bool is_seed) const
173
0
  {
174
0
    return std::any_of(endpoints.begin(), endpoints.end()
175
0
      , [&](announce_endpoint const& aep) { return aep.can_announce(now, is_seed, fail_limit); });
176
0
  }
177
178
  bool announce_entry::is_working() const
179
0
  {
180
0
    return std::any_of(endpoints.begin(), endpoints.end()
181
0
      , [](announce_endpoint const& aep) { return aep.is_working(); });
182
0
  }
183
#endif
184
185
TORRENT_VERSION_NAMESPACE_2_END
186
187
namespace aux {
188
189
  announce_infohash::announce_infohash()
190
0
    : fails(0)
191
0
    , updating(false)
192
0
    , start_sent(false)
193
0
    , complete_sent(false)
194
0
    , triggered_manually(false)
195
0
  {}
196
197
  announce_endpoint::announce_endpoint(aux::listen_socket_handle const& s, bool const completed)
198
0
    : local_endpoint(s ? s.get_local_endpoint() : tcp::endpoint())
199
0
    , enabled(true)
200
0
    , socket(s)
201
0
  {
202
0
    TORRENT_UNUSED(completed);
203
0
  }
204
205
  announce_entry::announce_entry(string_view u)
206
0
    : url(u.to_string())
207
0
    , source(0)
208
0
    , verified(false)
209
0
  {}
210
211
  announce_entry::announce_entry(lt::announce_entry const& ae)
212
0
    : url(ae.url)
213
0
    , trackerid(ae.trackerid)
214
0
    , tier(ae.tier)
215
0
    , fail_limit(ae.fail_limit)
216
0
    , source(ae.source)
217
0
    , verified(false)
218
0
  {
219
0
    if (source == 0) source = lt::announce_entry::source_client;
220
0
  }
221
222
  announce_entry::announce_entry()
223
0
    : source(0)
224
0
    , verified(false)
225
0
  {}
226
227
0
  announce_entry::~announce_entry() = default;
228
0
  announce_entry::announce_entry(announce_entry const&) = default;
229
0
  announce_entry& announce_entry::operator=(announce_entry const&) & = default;
230
231
  void announce_infohash::reset()
232
0
  {
233
0
    start_sent = false;
234
0
    next_announce = time_point32::min();
235
0
    min_announce = time_point32::min();
236
0
  }
237
238
  void announce_infohash::failed(int const backoff_ratio, seconds32 const retry_interval)
239
0
  {
240
    // fails is only 7 bits
241
0
    if (fails < (1 << 7) - 1) ++fails;
242
243
    // the exponential back-off ends up being:
244
    // 7, 15, 27, 45, 95, 127, 165, ... seconds
245
    // with the default tracker_backoff of 250
246
0
    int const fail_square = int(fails) * int(fails);
247
0
    seconds32 const delay = std::max(retry_interval
248
0
      , std::min(duration_cast<seconds32>(tracker_retry_delay_max)
249
0
        , tracker_retry_delay_min
250
0
          + fail_square * tracker_retry_delay_min * backoff_ratio / 100
251
0
      ));
252
0
    if (!is_working()) next_announce = aux::time_now32() + delay;
253
0
    updating = false;
254
0
  }
255
256
  bool announce_infohash::can_announce(time_point now, bool is_seed, std::uint8_t fail_limit) const
257
0
  {
258
    // if we're a seed and we haven't sent a completed
259
    // event, we need to let this announce through
260
0
    bool const need_send_complete = is_seed && !complete_sent;
261
262
    // add some slack here for rounding errors
263
0
    return now  + seconds(1) >= next_announce
264
0
      && (now >= min_announce || need_send_complete)
265
0
      && (fails < fail_limit || fail_limit == 0)
266
0
      && !updating;
267
0
  }
268
269
  void announce_endpoint::reset()
270
0
  {
271
0
    for (auto& ih : info_hashes)
272
0
      ih.reset();
273
0
  }
274
275
  void announce_entry::reset()
276
0
  {
277
0
    for (auto& aep : endpoints)
278
0
      aep.reset();
279
0
  }
280
281
  announce_endpoint* announce_entry::find_endpoint(aux::listen_socket_handle const& s)
282
0
  {
283
0
    auto aep = std::find_if(endpoints.begin(), endpoints.end()
284
0
      , [&](aux::announce_endpoint const& a) { return a.socket == s; });
285
0
    if (aep != endpoints.end()) return &*aep;
286
0
    else return nullptr;
287
0
  }
288
} // aux
289
} // libtorrent