/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 |