/src/libtorrent/src/session_impl.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | |
3 | | Copyright (c) 2003, Magnus Jonsson |
4 | | Copyright (c) 2015, Thomas |
5 | | Copyright (c) 2006-2022, Arvid Norberg |
6 | | Copyright (c) 2009, Andrew Resch |
7 | | Copyright (c) 2014-2020, Steven Siloti |
8 | | Copyright (c) 2015-2021, Alden Torres |
9 | | Copyright (c) 2015, Mikhail Titov |
10 | | Copyright (c) 2015, Thomas Yuan |
11 | | Copyright (c) 2016-2017, Andrei Kurushin |
12 | | Copyright (c) 2016, Falcosc |
13 | | Copyright (c) 2016-2017, Pavel Pimenov |
14 | | Copyright (c) 2017, sledgehammer_999 |
15 | | Copyright (c) 2018, Xiyue Deng |
16 | | Copyright (c) 2020, Fonic |
17 | | Copyright (c) 2020, Paul-Louis Ageneau |
18 | | Copyright (c) 2022, Vladimir Golovnev (glassez) |
19 | | Copyright (c) 2022, thrnz |
20 | | Copyright (c) 2023, Joris Carrier |
21 | | All rights reserved. |
22 | | |
23 | | Redistribution and use in source and binary forms, with or without |
24 | | modification, are permitted provided that the following conditions |
25 | | are met: |
26 | | |
27 | | * Redistributions of source code must retain the above copyright |
28 | | notice, this list of conditions and the following disclaimer. |
29 | | * Redistributions in binary form must reproduce the above copyright |
30 | | notice, this list of conditions and the following disclaimer in |
31 | | the documentation and/or other materials provided with the distribution. |
32 | | * Neither the name of the author nor the names of its |
33 | | contributors may be used to endorse or promote products derived |
34 | | from this software without specific prior written permission. |
35 | | |
36 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
37 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
38 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
39 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
40 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
41 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
42 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
43 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
44 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
45 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
46 | | POSSIBILITY OF SUCH DAMAGE. |
47 | | |
48 | | */ |
49 | | |
50 | | #include "libtorrent/config.hpp" |
51 | | |
52 | | #include <ctime> |
53 | | #include <algorithm> |
54 | | #include <cctype> |
55 | | #include <cstdio> // for snprintf |
56 | | #include <cinttypes> // for PRId64 et.al. |
57 | | #include <functional> |
58 | | #include <type_traits> |
59 | | #include <numeric> // for accumulate |
60 | | |
61 | | #if TORRENT_USE_INVARIANT_CHECKS |
62 | | #include <unordered_set> |
63 | | #endif |
64 | | |
65 | | #include "libtorrent/aux_/disable_warnings_push.hpp" |
66 | | #include <boost/asio/ts/internet.hpp> |
67 | | #include <boost/asio/ts/executor.hpp> |
68 | | #include "libtorrent/aux_/disable_warnings_pop.hpp" |
69 | | |
70 | | #include "libtorrent/ssl.hpp" |
71 | | #include "libtorrent/peer_id.hpp" |
72 | | #include "libtorrent/torrent_info.hpp" |
73 | | #include "libtorrent/tracker_manager.hpp" |
74 | | #include "libtorrent/bencode.hpp" |
75 | | #include "libtorrent/hasher.hpp" |
76 | | #include "libtorrent/entry.hpp" |
77 | | #include "libtorrent/session.hpp" |
78 | | #include "libtorrent/fingerprint.hpp" |
79 | | #include "libtorrent/alert_types.hpp" |
80 | | #include "libtorrent/aux_/invariant_check.hpp" |
81 | | #include "libtorrent/bt_peer_connection.hpp" |
82 | | #include "libtorrent/peer_connection_handle.hpp" |
83 | | #include "libtorrent/ip_filter.hpp" |
84 | | #include "libtorrent/socket.hpp" |
85 | | #include "libtorrent/aux_/session_impl.hpp" |
86 | | #ifndef TORRENT_DISABLE_DHT |
87 | | #include "libtorrent/kademlia/dht_tracker.hpp" |
88 | | #include "libtorrent/kademlia/types.hpp" |
89 | | #include "libtorrent/kademlia/node_entry.hpp" |
90 | | #endif |
91 | | #include "libtorrent/enum_net.hpp" |
92 | | #include "libtorrent/utf8.hpp" |
93 | | #include "libtorrent/upnp.hpp" |
94 | | #include "libtorrent/natpmp.hpp" |
95 | | #include "libtorrent/lsd.hpp" |
96 | | #include "libtorrent/aux_/instantiate_connection.hpp" |
97 | | #include "libtorrent/peer_info.hpp" |
98 | | #include "libtorrent/random.hpp" |
99 | | #include "libtorrent/magnet_uri.hpp" |
100 | | #include "libtorrent/aux_/session_settings.hpp" |
101 | | #include "libtorrent/torrent_peer.hpp" |
102 | | #include "libtorrent/torrent_handle.hpp" |
103 | | #include "libtorrent/choker.hpp" |
104 | | #include "libtorrent/error.hpp" |
105 | | #include "libtorrent/platform_util.hpp" |
106 | | #include "libtorrent/aux_/bind_to_device.hpp" |
107 | | #include "libtorrent/hex.hpp" // to_hex, from_hex |
108 | | #include "libtorrent/aux_/scope_end.hpp" |
109 | | #include "libtorrent/aux_/set_socket_buffer.hpp" |
110 | | #include "libtorrent/aux_/generate_peer_id.hpp" |
111 | | #include "libtorrent/aux_/ffs.hpp" |
112 | | #include "libtorrent/aux_/array.hpp" |
113 | | #include "libtorrent/aux_/set_traffic_class.hpp" |
114 | | |
115 | | #ifndef TORRENT_DISABLE_LOGGING |
116 | | |
117 | | #include "libtorrent/socket_io.hpp" |
118 | | |
119 | | // for logging stat layout |
120 | | #include "libtorrent/stat.hpp" |
121 | | |
122 | | #include <cstdarg> // for va_list |
123 | | |
124 | | // for logging the size of DHT structures |
125 | | #ifndef TORRENT_DISABLE_DHT |
126 | | #include <libtorrent/kademlia/find_data.hpp> |
127 | | #include <libtorrent/kademlia/refresh.hpp> |
128 | | #include <libtorrent/kademlia/node.hpp> |
129 | | #include <libtorrent/kademlia/observer.hpp> |
130 | | #include <libtorrent/kademlia/item.hpp> |
131 | | #endif // TORRENT_DISABLE_DHT |
132 | | |
133 | | #include "libtorrent/http_tracker_connection.hpp" |
134 | | #include "libtorrent/udp_tracker_connection.hpp" |
135 | | |
136 | | #endif // TORRENT_DISABLE_LOGGING |
137 | | |
138 | | #ifdef TORRENT_USE_LIBGCRYPT |
139 | | |
140 | | #if GCRYPT_VERSION_NUMBER < 0x010600 |
141 | | extern "C" { |
142 | | GCRY_THREAD_OPTION_PTHREAD_IMPL; |
143 | | } |
144 | | #endif |
145 | | |
146 | | namespace { |
147 | | |
148 | | // libgcrypt requires this to initialize the library |
149 | | struct gcrypt_setup |
150 | | { |
151 | | gcrypt_setup() |
152 | | { |
153 | | gcry_check_version(nullptr); |
154 | | #if GCRYPT_VERSION_NUMBER < 0x010600 |
155 | | gcry_error_t e = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); |
156 | | if (e != 0) std::fprintf(stderr, "libcrypt ERROR: %s\n", gcry_strerror(e)); |
157 | | e = gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); |
158 | | if (e != 0) std::fprintf(stderr, "initialization finished error: %s\n", gcry_strerror(e)); |
159 | | #endif |
160 | | } |
161 | | } gcrypt_global_constructor; |
162 | | } |
163 | | |
164 | | #endif // TORRENT_USE_LIBGCRYPT |
165 | | |
166 | | #ifdef TORRENT_USE_OPENSSL |
167 | | #ifdef TORRENT_WINDOWS |
168 | | #include <wincrypt.h> |
169 | | #endif |
170 | | #endif // TORRENT_USE_OPENSSL |
171 | | |
172 | | #ifdef TORRENT_WINDOWS |
173 | | // for ERROR_SEM_TIMEOUT |
174 | | #include <winerror.h> |
175 | | #endif |
176 | | |
177 | | using namespace std::placeholders; |
178 | | |
179 | | #ifdef BOOST_NO_EXCEPTIONS |
180 | | namespace boost { |
181 | | |
182 | | void throw_exception(std::exception const& e) { std::abort(); } |
183 | | } |
184 | | #endif |
185 | | |
186 | | namespace libtorrent { |
187 | | |
188 | | #if defined TORRENT_ASIO_DEBUGGING |
189 | | std::map<std::string, async_t> _async_ops; |
190 | | std::deque<wakeup_t> _wakeups; |
191 | | int _async_ops_nthreads = 0; |
192 | | std::mutex _async_ops_mutex; |
193 | | |
194 | | std::map<int, handler_alloc_t> _handler_storage; |
195 | | std::mutex _handler_storage_mutex; |
196 | | bool _handler_logger_registered = false; |
197 | | #endif |
198 | | |
199 | | namespace aux { |
200 | | |
201 | | constexpr listen_socket_flags_t listen_socket_t::accept_incoming; |
202 | | constexpr listen_socket_flags_t listen_socket_t::local_network; |
203 | | constexpr listen_socket_flags_t listen_socket_t::was_expanded; |
204 | | constexpr listen_socket_flags_t listen_socket_t::proxy; |
205 | | |
206 | | constexpr ip_source_t session_interface::source_dht; |
207 | | constexpr ip_source_t session_interface::source_peer; |
208 | | constexpr ip_source_t session_interface::source_tracker; |
209 | | constexpr ip_source_t session_interface::source_router; |
210 | | |
211 | | void apply_deprecated_dht_settings(settings_pack& sett, bdecode_node const& s) |
212 | 1.13k | { |
213 | 1.13k | bdecode_node val; |
214 | 1.13k | val = s.dict_find_int("max_peers_reply"); |
215 | 1.13k | if (val) sett.set_int(settings_pack::dht_max_peers_reply, int(val.int_value())); |
216 | 1.13k | val = s.dict_find_int("search_branching"); |
217 | 1.13k | if (val) sett.set_int(settings_pack::dht_search_branching, int(val.int_value())); |
218 | 1.13k | val = s.dict_find_int("max_fail_count"); |
219 | 1.13k | if (val) sett.set_int(settings_pack::dht_max_fail_count, int(val.int_value())); |
220 | 1.13k | val = s.dict_find_int("max_torrents"); |
221 | 1.13k | if (val) sett.set_int(settings_pack::dht_max_torrents, int(val.int_value())); |
222 | 1.13k | val = s.dict_find_int("max_dht_items"); |
223 | 1.13k | if (val) sett.set_int(settings_pack::dht_max_dht_items, int(val.int_value())); |
224 | 1.13k | val = s.dict_find_int("max_peers"); |
225 | 1.13k | if (val) sett.set_int(settings_pack::dht_max_peers, int(val.int_value())); |
226 | 1.13k | val = s.dict_find_int("max_torrent_search_reply"); |
227 | 1.13k | if (val) sett.set_int(settings_pack::dht_max_torrent_search_reply, int(val.int_value())); |
228 | 1.13k | val = s.dict_find_int("restrict_routing_ips"); |
229 | 1.13k | if (val) sett.set_bool(settings_pack::dht_restrict_routing_ips, (val.int_value() != 0)); |
230 | 1.13k | val = s.dict_find_int("restrict_search_ips"); |
231 | 1.13k | if (val) sett.set_bool(settings_pack::dht_restrict_search_ips, (val.int_value() != 0)); |
232 | 1.13k | val = s.dict_find_int("extended_routing_table"); |
233 | 1.13k | if (val) sett.set_bool(settings_pack::dht_extended_routing_table, (val.int_value() != 0)); |
234 | 1.13k | val = s.dict_find_int("aggressive_lookups"); |
235 | 1.13k | if (val) sett.set_bool(settings_pack::dht_aggressive_lookups, (val.int_value() != 0)); |
236 | 1.13k | val = s.dict_find_int("privacy_lookups"); |
237 | 1.13k | if (val) sett.set_bool(settings_pack::dht_privacy_lookups, (val.int_value() != 0)); |
238 | 1.13k | val = s.dict_find_int("enforce_node_id"); |
239 | 1.13k | if (val) sett.set_bool(settings_pack::dht_enforce_node_id, (val.int_value() != 0)); |
240 | 1.13k | val = s.dict_find_int("ignore_dark_internet"); |
241 | 1.13k | if (val) sett.set_bool(settings_pack::dht_ignore_dark_internet, (val.int_value() != 0)); |
242 | 1.13k | val = s.dict_find_int("block_timeout"); |
243 | 1.13k | if (val) sett.set_int(settings_pack::dht_block_timeout, int(val.int_value())); |
244 | 1.13k | val = s.dict_find_int("block_ratelimit"); |
245 | 1.13k | if (val) sett.set_int(settings_pack::dht_block_ratelimit, int(val.int_value())); |
246 | 1.13k | val = s.dict_find_int("read_only"); |
247 | 1.13k | if (val) sett.set_bool(settings_pack::dht_read_only, (val.int_value() != 0)); |
248 | 1.13k | val = s.dict_find_int("item_lifetime"); |
249 | 1.13k | if (val) sett.set_int(settings_pack::dht_item_lifetime, int(val.int_value())); |
250 | 1.13k | } |
251 | | |
252 | | std::vector<std::shared_ptr<listen_socket_t>>::iterator partition_listen_sockets( |
253 | | std::vector<listen_endpoint_t>& eps |
254 | | , std::vector<std::shared_ptr<listen_socket_t>>& sockets) |
255 | 1.83k | { |
256 | 1.83k | return std::partition(sockets.begin(), sockets.end() |
257 | 1.83k | , [&eps](std::shared_ptr<listen_socket_t> const& sock) |
258 | 1.83k | { |
259 | 0 | auto match = std::find_if(eps.begin(), eps.end() |
260 | 0 | , [&sock](listen_endpoint_t const& ep) |
261 | 0 | { |
262 | 0 | return ep.ssl == sock->ssl |
263 | 0 | && ep.port == sock->original_port |
264 | 0 | && ep.device == sock->device |
265 | 0 | && ep.flags == sock->flags |
266 | 0 | && ep.addr == sock->local_endpoint.address(); |
267 | 0 | }); |
268 | |
|
269 | 0 | if (match != eps.end()) |
270 | 0 | { |
271 | | // remove the matched endpoint so that another socket can't match it |
272 | | // this also signals to the caller that it doesn't need to create a |
273 | | // socket for the endpoint |
274 | 0 | eps.erase(match); |
275 | 0 | return true; |
276 | 0 | } |
277 | 0 | else |
278 | 0 | { |
279 | 0 | return false; |
280 | 0 | } |
281 | 0 | }); |
282 | 1.83k | } |
283 | | |
284 | | // To comply with BEP 45 multi homed clients must run separate DHT nodes |
285 | | // on each interface they use to talk to the DHT. This is enforced |
286 | | // by prohibiting creating a listen socket on [::] and 0.0.0.0. Instead the list of |
287 | | // interfaces is enumerated and sockets are created for each of them. |
288 | | void expand_unspecified_address(span<ip_interface const> const ifs |
289 | | , span<ip_route const> const routes |
290 | | , std::vector<listen_endpoint_t>& eps) |
291 | 1.83k | { |
292 | 1.83k | auto unspecified_begin = std::partition(eps.begin(), eps.end() |
293 | 1.83k | , [](listen_endpoint_t const& ep) { return !ep.addr.is_unspecified(); }); |
294 | 1.83k | std::vector<listen_endpoint_t> unspecified_eps(unspecified_begin, eps.end()); |
295 | 1.83k | eps.erase(unspecified_begin, eps.end()); |
296 | 1.83k | for (auto const& uep : unspecified_eps) |
297 | 0 | { |
298 | 0 | bool const v4 = uep.addr.is_v4(); |
299 | 0 | for (auto const& ipface : ifs) |
300 | 0 | { |
301 | 0 | if (!ipface.preferred) |
302 | 0 | continue; |
303 | 0 | if (ipface.interface_address.is_v4() != v4) |
304 | 0 | continue; |
305 | 0 | if (!uep.device.empty() && uep.device != ipface.name) |
306 | 0 | continue; |
307 | 0 | if (std::any_of(eps.begin(), eps.end(), [&](listen_endpoint_t const& e) |
308 | 0 | { |
309 | | // ignore device name because we don't want to create |
310 | | // duplicates if the user explicitly configured an address |
311 | | // without a device name |
312 | 0 | return e.addr == ipface.interface_address |
313 | 0 | && e.port == uep.port |
314 | 0 | && e.ssl == uep.ssl; |
315 | 0 | })) |
316 | 0 | { |
317 | 0 | continue; |
318 | 0 | } |
319 | | |
320 | | // ignore interfaces that are down |
321 | 0 | if (ipface.state != if_state::up && ipface.state != if_state::unknown) |
322 | 0 | continue; |
323 | 0 | if (!(ipface.flags & if_flags::up)) |
324 | 0 | continue; |
325 | | |
326 | | // we assume this listen_socket_t is local-network under some |
327 | | // conditions, meaning we won't announce it to internet trackers |
328 | | // if "routes" does not contain a single route to the internet, |
329 | | // we don't use the last case. On MacOS, we can be notified of |
330 | | // network changes *before* the routing table is updated |
331 | 0 | bool const local |
332 | 0 | = ipface.interface_address.is_loopback() |
333 | 0 | || is_link_local(ipface.interface_address) |
334 | 0 | || (ipface.flags & if_flags::loopback) |
335 | 0 | || (!is_global(ipface.interface_address) |
336 | 0 | && !(ipface.flags & if_flags::pointopoint) |
337 | 0 | && has_any_internet_route(routes) |
338 | 0 | && !has_internet_route(ipface.name, family(ipface.interface_address), routes)); |
339 | |
|
340 | 0 | eps.emplace_back(ipface.interface_address, uep.port, uep.device |
341 | 0 | , uep.ssl, uep.flags | listen_socket_t::was_expanded |
342 | 0 | | (local ? listen_socket_t::local_network : listen_socket_flags_t{})); |
343 | 0 | } |
344 | 0 | } |
345 | 1.83k | } |
346 | | |
347 | | void expand_devices(span<ip_interface const> const ifs |
348 | | , std::vector<listen_endpoint_t>& eps) |
349 | 1.83k | { |
350 | 1.83k | for (auto& ep : eps) |
351 | 1.83k | { |
352 | 1.83k | auto const iface = ep.device.empty() |
353 | 1.83k | ? std::find_if(ifs.begin(), ifs.end(), [&](ip_interface const& ipface) |
354 | 1.83k | { |
355 | 1.83k | return match_addr_mask(ipface.interface_address, ep.addr, ipface.netmask); |
356 | 1.83k | }) |
357 | 1.83k | : std::find_if(ifs.begin(), ifs.end(), [&](ip_interface const& ipface) |
358 | 0 | { |
359 | 0 | return ipface.name == ep.device |
360 | 0 | && match_addr_mask(ipface.interface_address, ep.addr, ipface.netmask); |
361 | 0 | }); |
362 | | |
363 | 1.83k | if (iface == ifs.end()) |
364 | 0 | { |
365 | | // we can't find which device this is for, just assume we can't |
366 | | // reach anything on it |
367 | 0 | ep.netmask = build_netmask(0, ep.addr.is_v4() ? AF_INET : AF_INET6); |
368 | 0 | continue; |
369 | 0 | } |
370 | | |
371 | 1.83k | ep.netmask = iface->netmask; |
372 | 1.83k | ep.device = iface->name; |
373 | 1.83k | } |
374 | 1.83k | } |
375 | | |
376 | | bool listen_socket_t::can_route(address const& addr) const |
377 | 0 | { |
378 | | // if this is a proxy, we assume it can reach everything |
379 | 0 | if (flags & proxy) return true; |
380 | | |
381 | 0 | if (is_v4(local_endpoint) != addr.is_v4()) return false; |
382 | | |
383 | 0 | if (local_endpoint.address().is_v6() |
384 | 0 | && local_endpoint.address().to_v6().scope_id() != addr.to_v6().scope_id()) |
385 | 0 | return false; |
386 | | |
387 | 0 | if (local_endpoint.address() == addr) return true; |
388 | 0 | if (local_endpoint.address().is_unspecified()) return true; |
389 | 0 | if (match_addr_mask(addr, local_endpoint.address(), netmask)) return true; |
390 | 0 | return !(flags & local_network); |
391 | 0 | } |
392 | | |
393 | | void session_impl::init_peer_class_filter(bool unlimited_local) |
394 | 3.67k | { |
395 | | // set the default peer_class_filter to use the local peer class |
396 | | // for peers on local networks |
397 | 3.67k | std::uint32_t lfilter = 1 << static_cast<std::uint32_t>(m_local_peer_class); |
398 | 3.67k | std::uint32_t gfilter = 1 << static_cast<std::uint32_t>(m_global_class); |
399 | | |
400 | 3.67k | struct class_mapping |
401 | 3.67k | { |
402 | 3.67k | char const* first; |
403 | 3.67k | char const* last; |
404 | 3.67k | std::uint32_t filter; |
405 | 3.67k | }; |
406 | | |
407 | 3.67k | static const class_mapping v4_classes[] = |
408 | 3.67k | { |
409 | | // everything |
410 | 3.67k | {"0.0.0.0", "255.255.255.255", gfilter}, |
411 | | // local networks |
412 | 3.67k | {"10.0.0.0", "10.255.255.255", lfilter}, |
413 | 3.67k | {"172.16.0.0", "172.31.255.255", lfilter}, |
414 | 3.67k | {"192.168.0.0", "192.168.255.255", lfilter}, |
415 | | // link-local |
416 | 3.67k | {"169.254.0.0", "169.254.255.255", lfilter}, |
417 | | // loop-back |
418 | 3.67k | {"127.0.0.0", "127.255.255.255", lfilter}, |
419 | 3.67k | }; |
420 | | |
421 | 3.67k | static const class_mapping v6_classes[] = |
422 | 3.67k | { |
423 | | // everything |
424 | 3.67k | {"::0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", gfilter}, |
425 | | // local networks |
426 | 3.67k | {"fc00::", "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", lfilter}, |
427 | | // link-local |
428 | 3.67k | {"fe80::", "febf::ffff:ffff:ffff:ffff:ffff:ffff:ffff", lfilter}, |
429 | | // loop-back |
430 | 3.67k | {"::1", "::1", lfilter}, |
431 | 3.67k | }; |
432 | | |
433 | 3.67k | class_mapping const* p = v4_classes; |
434 | 3.67k | int len = sizeof(v4_classes) / sizeof(v4_classes[0]); |
435 | 3.67k | if (!unlimited_local) len = 1; |
436 | 25.7k | for (int i = 0; i < len; ++i) |
437 | 22.0k | { |
438 | 22.0k | error_code ec; |
439 | 22.0k | address_v4 begin = make_address_v4(p[i].first, ec); |
440 | 22.0k | address_v4 end = make_address_v4(p[i].last, ec); |
441 | 22.0k | if (ec) continue; |
442 | 22.0k | m_peer_class_filter.add_rule(begin, end, p[i].filter); |
443 | 22.0k | } |
444 | 3.67k | p = v6_classes; |
445 | 3.67k | len = sizeof(v6_classes) / sizeof(v6_classes[0]); |
446 | 3.67k | if (!unlimited_local) len = 1; |
447 | 18.3k | for (int i = 0; i < len; ++i) |
448 | 14.6k | { |
449 | 14.6k | error_code ec; |
450 | 14.6k | address_v6 begin = make_address_v6(p[i].first, ec); |
451 | 14.6k | address_v6 end = make_address_v6(p[i].last, ec); |
452 | 14.6k | if (ec) continue; |
453 | 11.0k | m_peer_class_filter.add_rule(begin, end, p[i].filter); |
454 | 11.0k | } |
455 | 3.67k | } |
456 | | |
457 | | #ifdef TORRENT_SSL_PEERS |
458 | | namespace { |
459 | | // when running bittorrent over SSL, the SNI (server name indication) |
460 | | // extension is used to know which torrent the incoming connection is |
461 | | // trying to connect to. The 40 first bytes in the name is expected to |
462 | | // be the hex encoded info-hash |
463 | | bool ssl_server_name_callback_impl(ssl::stream_handle_type stream_handle, std::string const& name, session_impl* si) |
464 | 0 | { |
465 | 0 | if (name.size() < 40) |
466 | 0 | return false; |
467 | | |
468 | 0 | info_hash_t info_hash; |
469 | 0 | bool valid = aux::from_hex({name.c_str(), 40}, info_hash.v1.data()); |
470 | | |
471 | | // the server name is not a valid hex-encoded info-hash |
472 | 0 | if (!valid) |
473 | 0 | return false; |
474 | | |
475 | | // see if there is a torrent with this info-hash |
476 | 0 | std::shared_ptr<torrent> t = si ? si->find_torrent(info_hash).lock() : nullptr; |
477 | | |
478 | | // if there isn't, fail |
479 | 0 | if (!t) return false; |
480 | | |
481 | | // if the torrent we found isn't an SSL torrent, also fail. |
482 | 0 | if (!t->is_ssl_torrent()) return false; |
483 | | |
484 | | // if the torrent doesn't have an SSL context and should not allow |
485 | | // incoming SSL connections |
486 | 0 | auto* torrent_ctx = t->ssl_ctx(); |
487 | 0 | if (!torrent_ctx) return false; |
488 | | |
489 | | // use this torrent's certificate |
490 | 0 | ssl::set_context(stream_handle, ssl::get_handle(*torrent_ctx)); |
491 | 0 | return true; |
492 | 0 | } |
493 | | |
494 | | #if defined TORRENT_USE_OPENSSL |
495 | | int ssl_server_name_callback(SSL* s, int*, void* arg) |
496 | 0 | { |
497 | 0 | char const* name = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); |
498 | 0 | auto* si = reinterpret_cast<session_impl*>(arg); |
499 | 0 | return ssl_server_name_callback_impl(s, name ? std::string(name) : "", si) |
500 | 0 | ? SSL_TLSEXT_ERR_OK |
501 | 0 | : SSL_TLSEXT_ERR_ALERT_FATAL; |
502 | 0 | } |
503 | | #elif defined TORRENT_USE_GNUTLS |
504 | | bool ssl_server_name_callback(ssl::stream_handle_type stream_handle, std::string const& name, void* arg) |
505 | | { |
506 | | session_impl* si = reinterpret_cast<session_impl*>(arg); |
507 | | return ssl_server_name_callback_impl(stream_handle, name, si); |
508 | | } |
509 | | #endif |
510 | | } // anonymous namespace |
511 | | #endif // TORRENT_SSL_PEERS |
512 | | |
513 | | session_impl::session_impl(io_context& ioc, settings_pack const& pack |
514 | | , disk_io_constructor_type disk_io_constructor |
515 | | , session_flags_t const flags) |
516 | 1.83k | : m_settings(pack) |
517 | 1.83k | , m_io_context(ioc) |
518 | | #if TORRENT_USE_SSL |
519 | 1.83k | , m_ssl_ctx(ssl::context::tls_client) |
520 | | #ifdef TORRENT_SSL_PEERS |
521 | 1.83k | , m_peer_ssl_ctx(ssl::context::tls) |
522 | | #endif |
523 | | #endif // TORRENT_USE_SSL |
524 | 1.83k | , m_alerts(m_settings.get_int(settings_pack::alert_queue_size) |
525 | 1.83k | , alert_category_t{static_cast<unsigned int>(m_settings.get_int(settings_pack::alert_mask))}) |
526 | 1.83k | , m_disk_thread((disk_io_constructor ? disk_io_constructor : default_disk_io_constructor) |
527 | 1.83k | (m_io_context, m_settings, m_stats_counters)) |
528 | 1.83k | , m_download_rate(peer_connection::download_channel) |
529 | 1.83k | , m_upload_rate(peer_connection::upload_channel) |
530 | 1.83k | , m_host_resolver(m_io_context) |
531 | 1.83k | , m_tracker_manager( |
532 | 1.83k | std::bind(&session_impl::send_udp_packet_listen, this, _1, _2, _3, _4, _5) |
533 | 1.83k | , std::bind(&session_impl::send_udp_packet_hostname_listen, this, _1, _2, _3, _4, _5, _6) |
534 | 1.83k | , m_stats_counters |
535 | 1.83k | , m_host_resolver |
536 | 1.83k | , m_settings |
537 | 1.83k | #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS |
538 | 1.83k | , *this |
539 | 1.83k | #endif |
540 | 1.83k | ) |
541 | 1.83k | , m_work(make_work_guard(m_io_context)) |
542 | | #if TORRENT_USE_I2P |
543 | 1.83k | , m_i2p_conn(m_io_context) |
544 | | #endif |
545 | 1.83k | , m_created(clock_type::now()) |
546 | 1.83k | , m_last_tick(m_created) |
547 | 1.83k | , m_last_second_tick(m_created - milliseconds(900)) |
548 | 1.83k | , m_last_choke(m_created) |
549 | 1.83k | , m_last_auto_manage(m_created) |
550 | | #ifndef TORRENT_DISABLE_DHT |
551 | 1.83k | , m_dht_announce_timer(m_io_context) |
552 | | #endif |
553 | 1.83k | , m_utp_socket_manager( |
554 | 1.83k | std::bind(&session_impl::send_udp_packet, this, _1, _2, _3, _4, _5) |
555 | 1.83k | , [this](socket_type s) { this->incoming_connection(std::move(s)); } |
556 | 1.83k | , m_io_context |
557 | 1.83k | , m_settings, m_stats_counters, nullptr) |
558 | | #ifdef TORRENT_SSL_PEERS |
559 | 1.83k | , m_ssl_utp_socket_manager( |
560 | 1.83k | std::bind(&session_impl::send_udp_packet, this, _1, _2, _3, _4, _5) |
561 | 1.83k | , std::bind(&session_impl::on_incoming_utp_ssl, this, _1) |
562 | 1.83k | , m_io_context |
563 | 1.83k | , m_settings, m_stats_counters |
564 | 1.83k | , &m_peer_ssl_ctx) |
565 | | #endif |
566 | 1.83k | , m_timer(m_io_context) |
567 | 1.83k | , m_lsd_announce_timer(m_io_context) |
568 | 1.83k | , m_close_file_timer(m_io_context) |
569 | 1.83k | , m_paused(flags & session::paused) |
570 | 1.83k | { |
571 | 1.83k | #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS |
572 | 1.83k | validate_settings(); |
573 | 1.83k | #endif |
574 | 1.83k | } |
575 | | |
576 | | template <typename Fun, typename... Args> |
577 | | void session_impl::wrap(Fun f, Args&&... a) |
578 | | #ifndef BOOST_NO_EXCEPTIONS |
579 | 18.3k | try |
580 | 18.3k | #endif |
581 | 18.3k | { |
582 | 18.3k | (this->*f)(std::forward<Args>(a)...); |
583 | 18.3k | } |
584 | 18.3k | #ifndef BOOST_NO_EXCEPTIONS |
585 | 18.3k | catch (system_error const& e) { |
586 | 0 | alerts().emplace_alert<session_error_alert>(e.code(), e.what()); |
587 | 0 | pause(); |
588 | 0 | } catch (std::exception const& e) { |
589 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), e.what()); |
590 | 0 | pause(); |
591 | 0 | } catch (...) { |
592 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), "unknown error"); |
593 | 0 | pause(); |
594 | 0 | } void libtorrent::aux::session_impl::wrap<void (libtorrent::aux::session_impl::*)()>(void (libtorrent::aux::session_impl::*)()) Line | Count | Source | 579 | 7.34k | try | 580 | 7.34k | #endif | 581 | 7.34k | { | 582 | 7.34k | (this->*f)(std::forward<Args>(a)...); | 583 | 7.34k | } | 584 | 7.34k | #ifndef BOOST_NO_EXCEPTIONS | 585 | 7.34k | catch (system_error const& e) { | 586 | 0 | alerts().emplace_alert<session_error_alert>(e.code(), e.what()); | 587 | 0 | pause(); | 588 | 0 | } catch (std::exception const& e) { | 589 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), e.what()); | 590 | 0 | pause(); | 591 | 0 | } catch (...) { | 592 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), "unknown error"); | 593 | 0 | pause(); | 594 | 0 | } |
void libtorrent::aux::session_impl::wrap<void (libtorrent::aux::session_impl::*)(boost::system::error_code const&), boost::system::error_code>(void (libtorrent::aux::session_impl::*)(boost::system::error_code const&), boost::system::error_code&&) Line | Count | Source | 579 | 3.67k | try | 580 | 3.67k | #endif | 581 | 3.67k | { | 582 | 3.67k | (this->*f)(std::forward<Args>(a)...); | 583 | 3.67k | } | 584 | 3.67k | #ifndef BOOST_NO_EXCEPTIONS | 585 | 3.67k | catch (system_error const& e) { | 586 | 0 | alerts().emplace_alert<session_error_alert>(e.code(), e.what()); | 587 | 0 | pause(); | 588 | 0 | } catch (std::exception const& e) { | 589 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), e.what()); | 590 | 0 | pause(); | 591 | 0 | } catch (...) { | 592 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), "unknown error"); | 593 | 0 | pause(); | 594 | 0 | } |
void libtorrent::aux::session_impl::wrap<void (libtorrent::aux::session_impl::*)(boost::system::error_code const&), boost::system::error_code const&>(void (libtorrent::aux::session_impl::*)(boost::system::error_code const&), boost::system::error_code const&) Line | Count | Source | 579 | 3.67k | try | 580 | 3.67k | #endif | 581 | 3.67k | { | 582 | 3.67k | (this->*f)(std::forward<Args>(a)...); | 583 | 3.67k | } | 584 | 3.67k | #ifndef BOOST_NO_EXCEPTIONS | 585 | 3.67k | catch (system_error const& e) { | 586 | 0 | alerts().emplace_alert<session_error_alert>(e.code(), e.what()); | 587 | 0 | pause(); | 588 | 0 | } catch (std::exception const& e) { | 589 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), e.what()); | 590 | 0 | pause(); | 591 | 0 | } catch (...) { | 592 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), "unknown error"); | 593 | 0 | pause(); | 594 | 0 | } |
void libtorrent::aux::session_impl::wrap<void (libtorrent::aux::session_impl::*)(boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor>, boost::system::error_code const&, std::__1::weak_ptr<boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::executor> >, libtorrent::aux::transport), boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor>, boost::system::error_code const&, std::__1::weak_ptr<boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::executor> > const&, libtorrent::aux::transport const&>(void (libtorrent::aux::session_impl::*)(boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor>, boost::system::error_code const&, std::__1::weak_ptr<boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::executor> >, libtorrent::aux::transport), boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::executor>&&, boost::system::error_code const&, std::__1::weak_ptr<boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::executor> > const&, libtorrent::aux::transport const&) Line | Count | Source | 579 | 3.67k | try | 580 | 3.67k | #endif | 581 | 3.67k | { | 582 | 3.67k | (this->*f)(std::forward<Args>(a)...); | 583 | 3.67k | } | 584 | 3.67k | #ifndef BOOST_NO_EXCEPTIONS | 585 | 3.67k | catch (system_error const& e) { | 586 | 0 | alerts().emplace_alert<session_error_alert>(e.code(), e.what()); | 587 | 0 | pause(); | 588 | 0 | } catch (std::exception const& e) { | 589 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), e.what()); | 590 | 0 | pause(); | 591 | 0 | } catch (...) { | 592 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), "unknown error"); | 593 | 0 | pause(); | 594 | 0 | } |
|
595 | | #endif |
596 | | |
597 | | // This function is called by the creating thread, not in the message loop's |
598 | | // io_context thread. |
599 | | // TODO: 2 is there a reason not to move all of this into init()? and just |
600 | | // post it to the io_context? |
601 | | void session_impl::start_session() |
602 | 1.83k | { |
603 | | #ifndef TORRENT_DISABLE_LOGGING |
604 | | session_log("start session"); |
605 | | #endif |
606 | | |
607 | 1.83k | #if TORRENT_USE_SSL |
608 | 1.83k | error_code ec; |
609 | 1.83k | m_ssl_ctx.set_default_verify_paths(ec); |
610 | | #ifndef TORRENT_DISABLE_LOGGING |
611 | | if (ec) session_log("SSL set_default verify_paths failed: %s", ec.message().c_str()); |
612 | | ec.clear(); |
613 | | #endif |
614 | | #if defined TORRENT_WINDOWS && defined TORRENT_USE_OPENSSL && !defined TORRENT_WINRT |
615 | | // TODO: come up with some abstraction to do this for gnutls as well |
616 | | // load certificates from the windows system certificate store |
617 | | X509_STORE* store = X509_STORE_new(); |
618 | | if (store) |
619 | | { |
620 | | HCERTSTORE system_store = CertOpenSystemStoreA(0, "ROOT"); |
621 | | // this is best effort |
622 | | if (system_store) |
623 | | { |
624 | | CERT_CONTEXT const* ctx = nullptr; |
625 | | while ((ctx = CertEnumCertificatesInStore(system_store, ctx)) != nullptr) |
626 | | { |
627 | | unsigned char const* cert_ptr = reinterpret_cast<unsigned char const*>(ctx->pbCertEncoded); |
628 | | X509* x509 = d2i_X509(nullptr, &cert_ptr, ctx->cbCertEncoded); |
629 | | // this is best effort |
630 | | if (!x509) continue; |
631 | | X509_STORE_add_cert(store, x509); |
632 | | X509_free(x509); |
633 | | } |
634 | | CertFreeCertificateContext(ctx); |
635 | | CertCloseStore(system_store, 0); |
636 | | } |
637 | | } |
638 | | |
639 | | SSL_CTX* ssl_ctx = m_ssl_ctx.native_handle(); |
640 | | SSL_CTX_set_cert_store(ssl_ctx, store); |
641 | | #endif |
642 | | #ifdef __APPLE__ |
643 | | m_ssl_ctx.load_verify_file("/etc/ssl/cert.pem", ec); |
644 | | #ifndef TORRENT_DISABLE_LOGGING |
645 | | if (ec) session_log("SSL load_verify_file failed: %s", ec.message().c_str()); |
646 | | ec.clear(); |
647 | | #endif |
648 | | m_ssl_ctx.add_verify_path("/etc/ssl/certs", ec); |
649 | | #ifndef TORRENT_DISABLE_LOGGING |
650 | | if (ec) session_log("SSL add_verify_path failed: %s", ec.message().c_str()); |
651 | | ec.clear(); |
652 | | #endif |
653 | | #endif // __APPLE__ |
654 | 1.83k | #endif // TORRENT_USE_SSL |
655 | 1.83k | #ifdef TORRENT_SSL_PEERS |
656 | 1.83k | m_peer_ssl_ctx.set_verify_mode(ssl::context::verify_none, ec); |
657 | 1.83k | ssl::set_server_name_callback(ssl::get_handle(m_peer_ssl_ctx), ssl_server_name_callback, this, ec); |
658 | 1.83k | #endif // TORRENT_SSL_PEERS |
659 | | |
660 | 1.83k | #ifndef TORRENT_DISABLE_DHT |
661 | 1.83k | m_next_dht_torrent = 0; |
662 | 1.83k | #endif |
663 | 1.83k | m_next_lsd_torrent = 0; |
664 | | |
665 | 1.83k | m_global_class = m_classes.new_peer_class("global"); |
666 | 1.83k | m_tcp_peer_class = m_classes.new_peer_class("tcp"); |
667 | 1.83k | m_local_peer_class = m_classes.new_peer_class("local"); |
668 | | // local peers are always unchoked |
669 | 1.83k | m_classes.at(m_local_peer_class)->ignore_unchoke_slots = true; |
670 | | // local peers are allowed to exceed the normal connection |
671 | | // limit by 50% |
672 | 1.83k | m_classes.at(m_local_peer_class)->connection_limit_factor = 150; |
673 | | |
674 | 1.83k | TORRENT_ASSERT(m_global_class == session::global_peer_class_id); |
675 | 1.83k | TORRENT_ASSERT(m_tcp_peer_class == session::tcp_peer_class_id); |
676 | 1.83k | TORRENT_ASSERT(m_local_peer_class == session::local_peer_class_id); |
677 | | |
678 | 1.83k | init_peer_class_filter(true); |
679 | | |
680 | | // TCP, SSL/TCP and I2P connections should be assigned the TCP peer class |
681 | 1.83k | m_peer_class_type_filter.add(peer_class_type_filter::tcp_socket, m_tcp_peer_class); |
682 | 1.83k | m_peer_class_type_filter.add(peer_class_type_filter::ssl_tcp_socket, m_tcp_peer_class); |
683 | 1.83k | m_peer_class_type_filter.add(peer_class_type_filter::i2p_socket, m_tcp_peer_class); |
684 | | |
685 | | #ifndef TORRENT_DISABLE_LOGGING |
686 | | |
687 | | session_log("version: %s revision: %" PRIx64 |
688 | | , lt::version_str, lt::version_revision); |
689 | | |
690 | | #endif // TORRENT_DISABLE_LOGGING |
691 | | |
692 | | // ---- auto-cap max connections ---- |
693 | 1.83k | int const max_files = max_open_files(); |
694 | | // deduct some margin for epoll/kqueue, log files, |
695 | | // futexes, shared objects etc. |
696 | | // 80% of the available file descriptors should go to connections |
697 | 1.83k | m_settings.set_int(settings_pack::connections_limit, std::min( |
698 | 1.83k | m_settings.get_int(settings_pack::connections_limit) |
699 | 1.83k | , std::max(5, (max_files - 20) * 8 / 10))); |
700 | | // 20% goes towards regular files (see disk_io_thread) |
701 | | #ifndef TORRENT_DISABLE_LOGGING |
702 | | if (should_log()) |
703 | | { |
704 | | session_log("max-connections: %d max-files: %d" |
705 | | , m_settings.get_int(settings_pack::connections_limit) |
706 | | , max_files); |
707 | | } |
708 | | #endif |
709 | | |
710 | 1.83k | post(m_io_context, [this] { wrap(&session_impl::init); }); |
711 | 1.83k | } |
712 | | |
713 | | void session_impl::init() |
714 | 1.83k | { |
715 | | // this is a debug facility |
716 | | // see single_threaded in debug.hpp |
717 | 1.83k | thread_started(); |
718 | | |
719 | 1.83k | TORRENT_ASSERT(is_single_thread()); |
720 | | |
721 | | #ifndef TORRENT_DISABLE_LOGGING |
722 | | session_log(" *** session thread init"); |
723 | | #endif |
724 | | |
725 | | // this is where we should set up all async operations. This |
726 | | // is called from within the network thread as opposed to the |
727 | | // constructor which is called from the main thread |
728 | | |
729 | | #if defined TORRENT_ASIO_DEBUGGING |
730 | | async_inc_threads(); |
731 | | add_outstanding_async("session_impl::on_tick"); |
732 | | #endif |
733 | 1.83k | post(m_io_context, [this]{ wrap(&session_impl::on_tick, error_code()); }); |
734 | | |
735 | 1.83k | int const lsd_announce_interval |
736 | 1.83k | = m_settings.get_int(settings_pack::local_service_announce_interval); |
737 | 1.83k | int const delay = std::max(lsd_announce_interval |
738 | 1.83k | / std::max(static_cast<int>(m_torrents.size()), 1), 1); |
739 | 1.83k | m_lsd_announce_timer.expires_after(seconds(delay)); |
740 | 1.83k | ADD_OUTSTANDING_ASYNC("session_impl::on_lsd_announce"); |
741 | 1.83k | m_lsd_announce_timer.async_wait([this](error_code const& e) { |
742 | 1.83k | wrap(&session_impl::on_lsd_announce, e); } ); |
743 | | |
744 | | #ifndef TORRENT_DISABLE_LOGGING |
745 | | session_log(" done starting session"); |
746 | | #endif |
747 | | |
748 | | // this applies unchoke settings from m_settings |
749 | 1.83k | recalculate_unchoke_slots(); |
750 | | |
751 | | // apply all m_settings to this session |
752 | 1.83k | run_all_updates(*this); |
753 | 1.83k | reopen_listen_sockets(false); |
754 | | |
755 | | #if TORRENT_USE_INVARIANT_CHECKS |
756 | | check_invariant(); |
757 | | #endif |
758 | 1.83k | } |
759 | | |
760 | | #if TORRENT_ABI_VERSION <= 2 |
761 | | // TODO: 2 the ip filter should probably be saved here too |
762 | | void session_impl::save_state(entry* eptr, save_state_flags_t const flags) const |
763 | 0 | { |
764 | 0 | TORRENT_ASSERT(is_single_thread()); |
765 | |
|
766 | 0 | entry& e = *eptr; |
767 | | // make it a dict |
768 | 0 | e.dict(); |
769 | |
|
770 | 0 | if (flags & session::save_settings) |
771 | 0 | { |
772 | 0 | entry::dictionary_type& sett = e["settings"].dict(); |
773 | 0 | save_settings_to_dict(non_default_settings(m_settings), sett); |
774 | 0 | } |
775 | |
|
776 | 0 | #ifndef TORRENT_DISABLE_DHT |
777 | 0 | if (flags & session::save_dht_settings) |
778 | 0 | { |
779 | 0 | e["dht"] = dht::save_dht_settings(get_dht_settings()); |
780 | 0 | } |
781 | |
|
782 | 0 | if (m_dht && (flags & session::save_dht_state)) |
783 | 0 | { |
784 | 0 | e["dht state"] = dht::save_dht_state(m_dht->state()); |
785 | 0 | } |
786 | 0 | #endif |
787 | |
|
788 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
789 | 0 | for (auto const& ext : m_ses_extensions[plugins_all_idx]) |
790 | 0 | { |
791 | 0 | ext->save_state(*eptr); |
792 | 0 | } |
793 | 0 | #endif |
794 | 0 | } |
795 | | |
796 | | void session_impl::load_state(bdecode_node const* e |
797 | | , save_state_flags_t const flags) |
798 | 0 | { |
799 | 0 | TORRENT_ASSERT(is_single_thread()); |
800 | |
|
801 | 0 | bdecode_node settings; |
802 | 0 | if (e->type() != bdecode_node::dict_t) return; |
803 | | |
804 | 0 | #ifndef TORRENT_DISABLE_DHT |
805 | 0 | bool need_update_dht = false; |
806 | 0 | if (flags & session_handle::save_dht_state) |
807 | 0 | { |
808 | 0 | settings = e->dict_find_dict("dht state"); |
809 | 0 | if (settings) |
810 | 0 | { |
811 | 0 | m_dht_state = dht::read_dht_state(settings); |
812 | 0 | need_update_dht = true; |
813 | 0 | } |
814 | 0 | } |
815 | 0 | #endif |
816 | |
|
817 | 0 | #if TORRENT_ABI_VERSION == 1 |
818 | 0 | bool need_update_proxy = false; |
819 | 0 | if (flags & session_handle::save_proxy) |
820 | 0 | { |
821 | 0 | settings = e->dict_find_dict("proxy"); |
822 | 0 | if (settings) |
823 | 0 | { |
824 | 0 | m_settings.bulk_set([&settings](session_settings_single_thread& s) |
825 | 0 | { |
826 | 0 | bdecode_node val; |
827 | 0 | val = settings.dict_find_int("port"); |
828 | 0 | if (val) s.set_int(settings_pack::proxy_port, int(val.int_value())); |
829 | 0 | val = settings.dict_find_int("type"); |
830 | 0 | if (val) s.set_int(settings_pack::proxy_type, int(val.int_value())); |
831 | 0 | val = settings.dict_find_int("proxy_hostnames"); |
832 | 0 | if (val) s.set_bool(settings_pack::proxy_hostnames, val.int_value() != 0); |
833 | 0 | val = settings.dict_find_int("proxy_peer_connections"); |
834 | 0 | if (val) s.set_bool(settings_pack::proxy_peer_connections, val.int_value() != 0); |
835 | 0 | val = settings.dict_find_string("hostname"); |
836 | 0 | if (val) s.set_str(settings_pack::proxy_hostname, val.string_value().to_string()); |
837 | 0 | val = settings.dict_find_string("password"); |
838 | 0 | if (val) s.set_str(settings_pack::proxy_password, val.string_value().to_string()); |
839 | 0 | val = settings.dict_find_string("username"); |
840 | 0 | if (val) s.set_str(settings_pack::proxy_username, val.string_value().to_string()); |
841 | 0 | }); |
842 | 0 | need_update_proxy = true; |
843 | 0 | } |
844 | 0 | } |
845 | |
|
846 | 0 | settings = e->dict_find_dict("encryption"); |
847 | 0 | if (settings) |
848 | 0 | { |
849 | 0 | m_settings.bulk_set([&settings](session_settings_single_thread& s) |
850 | 0 | { |
851 | 0 | bdecode_node val; |
852 | 0 | val = settings.dict_find_int("prefer_rc4"); |
853 | 0 | if (val) s.set_bool(settings_pack::prefer_rc4, val.int_value() != 0); |
854 | 0 | val = settings.dict_find_int("out_enc_policy"); |
855 | 0 | if (val) s.set_int(settings_pack::out_enc_policy, int(val.int_value())); |
856 | 0 | val = settings.dict_find_int("in_enc_policy"); |
857 | 0 | if (val) s.set_int(settings_pack::in_enc_policy, int(val.int_value())); |
858 | 0 | val = settings.dict_find_int("allowed_enc_level"); |
859 | 0 | if (val) s.set_int(settings_pack::allowed_enc_level, int(val.int_value())); |
860 | 0 | }); |
861 | 0 | } |
862 | 0 | #endif |
863 | |
|
864 | 0 | if ((flags & session_handle::save_settings) |
865 | 0 | #if TORRENT_ABI_VERSION <= 2 |
866 | 0 | || (flags & session_handle::save_dht_settings) |
867 | 0 | #endif |
868 | 0 | ) |
869 | 0 | { |
870 | 0 | settings = e->dict_find_dict("settings"); |
871 | 0 | if (settings) |
872 | 0 | { |
873 | | // apply_settings_pack will update dht and proxy |
874 | 0 | settings_pack pack = load_pack_from_dict(settings); |
875 | | |
876 | | // these settings are not loaded from state |
877 | | // they are set by the client software, not configured by users |
878 | 0 | pack.clear(settings_pack::user_agent); |
879 | 0 | pack.clear(settings_pack::peer_fingerprint); |
880 | |
|
881 | 0 | apply_settings_pack_impl(pack); |
882 | 0 | #ifndef TORRENT_DISABLE_DHT |
883 | 0 | need_update_dht = false; |
884 | 0 | #endif |
885 | 0 | #if TORRENT_ABI_VERSION == 1 |
886 | 0 | need_update_proxy = false; |
887 | 0 | #endif |
888 | 0 | } |
889 | 0 | } |
890 | |
|
891 | 0 | #if TORRENT_ABI_VERSION <= 2 |
892 | 0 | if (flags & session_handle::save_dht_settings) |
893 | 0 | #endif |
894 | 0 | { |
895 | | // This is here for backwards compatibility, to support loading state |
896 | | // files in the previous file format, where the DHT settings were in |
897 | | // its own dictionary |
898 | 0 | settings = e->dict_find_dict("dht"); |
899 | 0 | if (settings) |
900 | 0 | { |
901 | 0 | settings_pack sett; |
902 | 0 | aux::apply_deprecated_dht_settings(sett, settings); |
903 | 0 | apply_settings_pack_impl(sett); |
904 | 0 | } |
905 | 0 | } |
906 | |
|
907 | 0 | #ifndef TORRENT_DISABLE_DHT |
908 | 0 | if (need_update_dht) start_dht(); |
909 | 0 | #endif |
910 | 0 | #if TORRENT_ABI_VERSION == 1 |
911 | 0 | if (need_update_proxy) update_proxy(); |
912 | 0 | #endif |
913 | |
|
914 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
915 | 0 | #if TORRENT_ABI_VERSION <= 2 |
916 | 0 | for (auto& ext : m_ses_extensions[plugins_all_idx]) |
917 | 0 | { |
918 | 0 | ext->load_state(*e); |
919 | 0 | } |
920 | 0 | #endif |
921 | 0 | #endif |
922 | 0 | } |
923 | | #endif |
924 | | |
925 | | session_params session_impl::session_state(save_state_flags_t const flags) const |
926 | 0 | { |
927 | 0 | TORRENT_ASSERT(is_single_thread()); |
928 | |
|
929 | 0 | session_params ret; |
930 | 0 | if (flags & session::save_settings) |
931 | 0 | ret.settings = non_default_settings(m_settings); |
932 | |
|
933 | 0 | #ifndef TORRENT_DISABLE_DHT |
934 | 0 | #if TORRENT_ABI_VERSION <= 2 |
935 | 0 | if (flags & session_handle::save_dht_settings) |
936 | 0 | { |
937 | 0 | ret.dht_settings = get_dht_settings(); |
938 | 0 | } |
939 | 0 | #endif |
940 | |
|
941 | 0 | if (m_dht && (flags & session::save_dht_state)) |
942 | 0 | ret.dht_state = m_dht->state(); |
943 | 0 | #endif |
944 | |
|
945 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
946 | 0 | if (flags & session::save_extension_state) |
947 | 0 | { |
948 | 0 | for (auto const& ext : m_ses_extensions[plugins_all_idx]) |
949 | 0 | { |
950 | 0 | auto state = ext->save_state(); |
951 | 0 | for (auto& v : state) |
952 | 0 | ret.ext_state[std::move(v.first)] = std::move(v.second); |
953 | 0 | } |
954 | 0 | } |
955 | 0 | #endif |
956 | |
|
957 | 0 | if ((flags & session::save_ip_filter) && m_ip_filter) |
958 | 0 | { |
959 | 0 | ret.ip_filter = *m_ip_filter; |
960 | 0 | } |
961 | 0 | return ret; |
962 | 0 | } |
963 | | |
964 | | proxy_settings session_impl::proxy() const |
965 | 1.83k | { |
966 | 1.83k | return proxy_settings(m_settings); |
967 | 1.83k | } |
968 | | |
969 | | #ifndef TORRENT_DISABLE_EXTENSIONS |
970 | | |
971 | | void session_impl::add_extension(ext_function_t ext) |
972 | 0 | { |
973 | 0 | TORRENT_ASSERT(is_single_thread()); |
974 | 0 | TORRENT_ASSERT(ext); |
975 | |
|
976 | 0 | add_ses_extension(std::make_shared<session_plugin_wrapper>(ext)); |
977 | 0 | } |
978 | | |
979 | | void session_impl::add_ses_extension(std::shared_ptr<plugin> ext) |
980 | 5.50k | { |
981 | | // this is called during startup of the session, from the thread creating |
982 | | // it, not its own thread |
983 | | // TORRENT_ASSERT(is_single_thread()); |
984 | 5.50k | TORRENT_ASSERT_VAL(ext, ext); |
985 | | |
986 | 5.50k | feature_flags_t const features = ext->implemented_features(); |
987 | | |
988 | 5.50k | m_ses_extensions[plugins_all_idx].push_back(ext); |
989 | | |
990 | 5.50k | if (features & plugin::optimistic_unchoke_feature) |
991 | 0 | m_ses_extensions[plugins_optimistic_unchoke_idx].push_back(ext); |
992 | 5.50k | if (features & plugin::tick_feature) |
993 | 0 | m_ses_extensions[plugins_tick_idx].push_back(ext); |
994 | 5.50k | if (features & plugin::dht_request_feature) |
995 | 0 | m_ses_extensions[plugins_dht_request_idx].push_back(ext); |
996 | 5.50k | if (features & plugin::unknown_torrent_feature) |
997 | 0 | m_ses_extensions[plugins_unknown_torrent_idx].push_back(ext); |
998 | 5.50k | if (features & plugin::alert_feature) |
999 | 0 | m_alerts.add_extension(ext); |
1000 | 5.50k | session_handle h(shared_from_this()); |
1001 | 5.50k | ext->added(h); |
1002 | 5.50k | } |
1003 | | |
1004 | | #endif // TORRENT_DISABLE_EXTENSIONS |
1005 | | |
1006 | | void session_impl::pause() |
1007 | 0 | { |
1008 | 0 | TORRENT_ASSERT(is_single_thread()); |
1009 | |
|
1010 | 0 | if (m_paused) return; |
1011 | | #ifndef TORRENT_DISABLE_LOGGING |
1012 | | session_log(" *** session paused ***"); |
1013 | | #endif |
1014 | | // this will abort all tracker announces other than event=stopped |
1015 | 0 | m_tracker_manager.abort_all_requests(); |
1016 | |
|
1017 | 0 | m_paused = true; |
1018 | 0 | for (auto& te : m_torrents) |
1019 | 0 | { |
1020 | 0 | te->set_session_paused(true); |
1021 | 0 | } |
1022 | 0 | } |
1023 | | |
1024 | | void session_impl::resume() |
1025 | 0 | { |
1026 | 0 | TORRENT_ASSERT(is_single_thread()); |
1027 | |
|
1028 | 0 | if (!m_paused) return; |
1029 | 0 | m_paused = false; |
1030 | |
|
1031 | 0 | for (auto& te : m_torrents) |
1032 | 0 | { |
1033 | 0 | te->set_session_paused(false); |
1034 | 0 | } |
1035 | 0 | } |
1036 | | |
1037 | | void session_impl::abort() noexcept |
1038 | 1.83k | { |
1039 | 1.83k | TORRENT_ASSERT(is_single_thread()); |
1040 | | |
1041 | 1.83k | if (m_abort) return; |
1042 | | #ifndef TORRENT_DISABLE_LOGGING |
1043 | | session_log(" *** ABORT CALLED ***"); |
1044 | | #endif |
1045 | | |
1046 | | // at this point we cannot call the notify function anymore, since the |
1047 | | // session will become invalid. |
1048 | 1.83k | m_alerts.set_notify_function({}); |
1049 | | |
1050 | 1.83k | #ifndef TORRENT_DISABLE_EXTENSIONS |
1051 | 1.83k | for (auto& ext : m_ses_extensions[plugins_all_idx]) |
1052 | 5.50k | { |
1053 | 5.50k | ext->abort(); |
1054 | 5.50k | } |
1055 | 1.83k | #endif |
1056 | | |
1057 | | // this will cancel requests that are not critical for shutting down |
1058 | | // cleanly. i.e. essentially tracker hostname lookups that we're not |
1059 | | // about to send event=stopped to |
1060 | 1.83k | m_host_resolver.abort(); |
1061 | | |
1062 | 1.83k | m_close_file_timer.cancel(); |
1063 | | |
1064 | | // abort the main thread |
1065 | 1.83k | m_abort = true; |
1066 | 1.83k | error_code ec; |
1067 | | |
1068 | | // we rely on on_tick() during shutdown, but we don't need to wait a |
1069 | | // whole second for it to fire |
1070 | 1.83k | m_timer.cancel(); |
1071 | | |
1072 | 1.83k | #if TORRENT_USE_I2P |
1073 | 1.83k | m_i2p_conn.close(ec); |
1074 | 1.83k | #endif |
1075 | 1.83k | stop_ip_notifier(); |
1076 | 1.83k | stop_lsd(); |
1077 | 1.83k | stop_upnp(); |
1078 | 1.83k | stop_natpmp(); |
1079 | 1.83k | #ifndef TORRENT_DISABLE_DHT |
1080 | 1.83k | stop_dht(); |
1081 | 1.83k | m_dht_announce_timer.cancel(); |
1082 | 1.83k | #endif |
1083 | 1.83k | m_lsd_announce_timer.cancel(); |
1084 | | |
1085 | 1.83k | #ifdef TORRENT_SSL_PEERS |
1086 | 1.83k | { |
1087 | 1.83k | auto const sockets = std::move(m_incoming_sockets); |
1088 | 1.83k | for (auto const& s : sockets) |
1089 | 0 | { |
1090 | 0 | s->close(ec); |
1091 | 0 | TORRENT_ASSERT(!ec); |
1092 | 0 | } |
1093 | 1.83k | } |
1094 | 1.83k | #endif |
1095 | | |
1096 | 1.83k | #if TORRENT_USE_I2P |
1097 | 1.83k | if (m_i2p_listen_socket && m_i2p_listen_socket->is_open()) |
1098 | 0 | { |
1099 | 0 | m_i2p_listen_socket->close(ec); |
1100 | 0 | TORRENT_ASSERT(!ec); |
1101 | 0 | } |
1102 | 1.83k | #endif |
1103 | | |
1104 | | #ifndef TORRENT_DISABLE_LOGGING |
1105 | | session_log(" aborting all torrents (%d)", int(m_torrents.size())); |
1106 | | #endif |
1107 | | // abort all torrents |
1108 | 1.83k | for (auto const& te : m_torrents) |
1109 | 1.83k | { |
1110 | 1.83k | te->abort(); |
1111 | 1.83k | } |
1112 | 1.83k | m_torrents.clear(); |
1113 | 1.83k | m_stats_counters.set_value(counters::num_peers_up_unchoked_all, 0); |
1114 | 1.83k | m_stats_counters.set_value(counters::num_peers_up_unchoked, 0); |
1115 | 1.83k | m_stats_counters.set_value(counters::num_peers_up_unchoked_optimistic, 0); |
1116 | | |
1117 | | #ifndef TORRENT_DISABLE_LOGGING |
1118 | | session_log(" aborting all tracker requests"); |
1119 | | #endif |
1120 | 1.83k | m_tracker_manager.stop(); |
1121 | | |
1122 | | #ifndef TORRENT_DISABLE_LOGGING |
1123 | | session_log(" aborting all connections (%d)", int(m_connections.size())); |
1124 | | #endif |
1125 | | // abort all connections |
1126 | 1.83k | for (auto i = m_connections.begin(); i != m_connections.end();) |
1127 | 0 | { |
1128 | 0 | peer_connection* p = (*i).get(); |
1129 | 0 | ++i; |
1130 | 0 | p->disconnect(errors::stopping_torrent, operation_t::bittorrent); |
1131 | 0 | } |
1132 | | |
1133 | | // close the listen sockets |
1134 | 1.83k | for (auto const& l : m_listen_sockets) |
1135 | 1.83k | { |
1136 | 1.83k | if (l->sock) |
1137 | 1.83k | { |
1138 | 1.83k | l->sock->close(ec); |
1139 | 1.83k | TORRENT_ASSERT(!ec); |
1140 | 1.83k | } |
1141 | | |
1142 | | // TODO: 3 closing the udp sockets here means that |
1143 | | // the uTP connections cannot be closed gracefully |
1144 | 1.83k | if (l->udp_sock) |
1145 | 1.83k | { |
1146 | 1.83k | l->udp_sock->sock.close(); |
1147 | 1.83k | } |
1148 | 1.83k | } |
1149 | | |
1150 | | // we need to give all the sockets an opportunity to actually have their handlers |
1151 | | // called and cancelled before we continue the shutdown. This is a bit |
1152 | | // complicated, if there are no "undead" peers, it's safe to resume the |
1153 | | // shutdown, but if there are, we have to wait for them to be cleared out |
1154 | | // first. In session_impl::on_tick() we check them periodically. If we're |
1155 | | // shutting down and we remove the last one, we'll initiate |
1156 | | // shutdown_stage2 from there. |
1157 | 1.83k | if (m_undead_peers.empty()) |
1158 | 1.83k | { |
1159 | 1.83k | post(m_io_context, make_handler([this] { abort_stage2(); } |
1160 | 1.83k | , m_abort_handler_storage, *this)); |
1161 | 1.83k | } |
1162 | 1.83k | } |
1163 | | |
1164 | | void session_impl::abort_stage2() noexcept |
1165 | 3.67k | { |
1166 | 3.67k | m_download_rate.close(); |
1167 | 3.67k | m_upload_rate.close(); |
1168 | | |
1169 | | // it's OK to detach the threads here. The disk_io_thread |
1170 | | // has an internal counter and won't release the network |
1171 | | // thread until they're all dead (via m_work). |
1172 | 3.67k | m_disk_thread->abort(false); |
1173 | | |
1174 | | // now it's OK for the network thread to exit |
1175 | 3.67k | m_work.reset(); |
1176 | 3.67k | } |
1177 | | |
1178 | | bool session_impl::has_connection(peer_connection* p) const |
1179 | 0 | { |
1180 | 0 | return m_connections.find(p->self()) != m_connections.end(); |
1181 | 0 | } |
1182 | | |
1183 | | void session_impl::insert_peer(std::shared_ptr<peer_connection> const& c) |
1184 | 0 | { |
1185 | 0 | TORRENT_ASSERT(!c->m_in_constructor); |
1186 | | |
1187 | | // removing a peer may not throw an exception, so prepare for this |
1188 | | // connection to be added to the undead peers now. |
1189 | 0 | m_undead_peers.reserve(m_undead_peers.size() + m_connections.size() + 1); |
1190 | 0 | m_connections.insert(c); |
1191 | |
|
1192 | 0 | TORRENT_ASSERT_VAL(m_undead_peers.capacity() >= m_connections.size() |
1193 | 0 | , m_undead_peers.capacity()); |
1194 | 0 | } |
1195 | | |
1196 | | void session_impl::set_port_filter(port_filter const& f) |
1197 | 0 | { |
1198 | 0 | m_port_filter = f; |
1199 | | // Close connections whose endpoint is filtered |
1200 | | // by the new port-filter |
1201 | 0 | for (auto const& t : m_torrents) |
1202 | 0 | t->port_filter_updated(); |
1203 | 0 | } |
1204 | | |
1205 | | void session_impl::set_ip_filter(std::shared_ptr<ip_filter> f) |
1206 | 0 | { |
1207 | 0 | INVARIANT_CHECK; |
1208 | |
|
1209 | 0 | m_ip_filter = std::move(f); |
1210 | | |
1211 | | // Close connections whose endpoint is filtered |
1212 | | // by the new ip-filter |
1213 | 0 | for (auto& i : m_torrents) |
1214 | 0 | i->set_ip_filter(m_ip_filter); |
1215 | 0 | } |
1216 | | |
1217 | | void session_impl::ban_ip(address addr) |
1218 | 0 | { |
1219 | 0 | TORRENT_ASSERT(is_single_thread()); |
1220 | 0 | if (!m_ip_filter) m_ip_filter = std::make_shared<ip_filter>(); |
1221 | 0 | m_ip_filter->add_rule(addr, addr, ip_filter::blocked); |
1222 | 0 | for (auto& i : m_torrents) |
1223 | 0 | i->set_ip_filter(m_ip_filter); |
1224 | 0 | } |
1225 | | |
1226 | | ip_filter const& session_impl::get_ip_filter() |
1227 | 0 | { |
1228 | 0 | TORRENT_ASSERT(is_single_thread()); |
1229 | 0 | if (!m_ip_filter) m_ip_filter = std::make_shared<ip_filter>(); |
1230 | 0 | return *m_ip_filter; |
1231 | 0 | } |
1232 | | |
1233 | | port_filter const& session_impl::get_port_filter() const |
1234 | 0 | { |
1235 | 0 | TORRENT_ASSERT(is_single_thread()); |
1236 | 0 | return m_port_filter; |
1237 | 0 | } |
1238 | | |
1239 | | peer_class_t session_impl::create_peer_class(char const* name) |
1240 | 0 | { |
1241 | 0 | TORRENT_ASSERT(is_single_thread()); |
1242 | 0 | return m_classes.new_peer_class(name); |
1243 | 0 | } |
1244 | | |
1245 | | void session_impl::delete_peer_class(peer_class_t const cid) |
1246 | 0 | { |
1247 | 0 | TORRENT_ASSERT(is_single_thread()); |
1248 | | // if you hit this assert, you're deleting a non-existent peer class |
1249 | 0 | TORRENT_ASSERT_PRECOND(m_classes.at(cid)); |
1250 | 0 | if (m_classes.at(cid) == nullptr) return; |
1251 | 0 | m_classes.decref(cid); |
1252 | 0 | } |
1253 | | |
1254 | | peer_class_info session_impl::get_peer_class(peer_class_t const cid) const |
1255 | 0 | { |
1256 | 0 | peer_class_info ret{}; |
1257 | 0 | peer_class const* pc = m_classes.at(cid); |
1258 | | // if you hit this assert, you're passing in an invalid cid |
1259 | 0 | TORRENT_ASSERT_PRECOND(pc); |
1260 | 0 | if (pc == nullptr) |
1261 | 0 | { |
1262 | | #if TORRENT_USE_INVARIANT_CHECKS |
1263 | | // make it obvious that the return value is undefined |
1264 | | ret.upload_limit = 0xf0f0f0f; |
1265 | | ret.download_limit = 0xf0f0f0f; |
1266 | | ret.label.resize(20); |
1267 | | url_random(span<char>(ret.label)); |
1268 | | ret.ignore_unchoke_slots = false; |
1269 | | ret.connection_limit_factor = 0xf0f0f0f; |
1270 | | ret.upload_priority = 0xf0f0f0f; |
1271 | | ret.download_priority = 0xf0f0f0f; |
1272 | | #endif |
1273 | 0 | return ret; |
1274 | 0 | } |
1275 | | |
1276 | 0 | pc->get_info(&ret); |
1277 | 0 | return ret; |
1278 | 0 | } |
1279 | | |
1280 | | namespace { |
1281 | | |
1282 | | std::uint16_t make_announce_port(std::uint16_t const p) |
1283 | 0 | { return p == 0 ? 1 : p; } |
1284 | | } |
1285 | | |
1286 | | void session_impl::queue_tracker_request(tracker_request req |
1287 | | , std::weak_ptr<request_callback> c) |
1288 | 0 | { |
1289 | 0 | req.listen_port = 0; |
1290 | 0 | #if TORRENT_USE_I2P |
1291 | 0 | if (!m_settings.get_str(settings_pack::i2p_hostname).empty()) |
1292 | 0 | { |
1293 | 0 | req.i2pconn = &m_i2p_conn; |
1294 | 0 | } |
1295 | 0 | #endif |
1296 | |
|
1297 | 0 | #if TORRENT_USE_SSL |
1298 | 0 | #ifdef TORRENT_SSL_PEERS |
1299 | 0 | bool const use_ssl = req.ssl_ctx != nullptr && req.ssl_ctx != &m_ssl_ctx; |
1300 | 0 | if (!use_ssl) |
1301 | 0 | #endif |
1302 | 0 | req.ssl_ctx = &m_ssl_ctx; |
1303 | 0 | #endif |
1304 | 0 | if (const auto announce_port = std::uint16_t(m_settings.get_int(settings_pack::announce_port))) |
1305 | 0 | { |
1306 | 0 | req.listen_port = announce_port; |
1307 | 0 | } |
1308 | 0 | else if (auto ls = req.outgoing_socket.get()) |
1309 | 0 | { |
1310 | 0 | req.listen_port = |
1311 | 0 | #ifdef TORRENT_SSL_PEERS |
1312 | | // SSL torrents use the SSL listen port |
1313 | 0 | use_ssl ? make_announce_port(ssl_listen_port(ls)) : |
1314 | 0 | #endif |
1315 | 0 | make_announce_port(listen_port(ls)); |
1316 | 0 | } |
1317 | 0 | else |
1318 | 0 | { |
1319 | 0 | TORRENT_ASSERT(req.kind == tracker_request::i2p); |
1320 | 0 | req.listen_port = 1; |
1321 | 0 | } |
1322 | 0 | m_tracker_manager.queue_request(get_context(), std::move(req), m_settings, c); |
1323 | 0 | } |
1324 | | |
1325 | | void session_impl::set_peer_class(peer_class_t const cid, peer_class_info const& pci) |
1326 | 0 | { |
1327 | 0 | peer_class* pc = m_classes.at(cid); |
1328 | | // if you hit this assert, you're passing in an invalid cid |
1329 | 0 | TORRENT_ASSERT_PRECOND(pc); |
1330 | 0 | if (pc == nullptr) return; |
1331 | | |
1332 | 0 | pc->set_info(&pci); |
1333 | 0 | } |
1334 | | |
1335 | | void session_impl::set_peer_class_filter(ip_filter const& f) |
1336 | 0 | { |
1337 | 0 | INVARIANT_CHECK; |
1338 | 0 | m_peer_class_filter = f; |
1339 | 0 | } |
1340 | | |
1341 | | ip_filter const& session_impl::get_peer_class_filter() const |
1342 | 0 | { |
1343 | 0 | return m_peer_class_filter; |
1344 | 0 | } |
1345 | | |
1346 | | void session_impl::set_peer_class_type_filter(peer_class_type_filter f) |
1347 | 0 | { |
1348 | 0 | m_peer_class_type_filter = f; |
1349 | 0 | } |
1350 | | |
1351 | | peer_class_type_filter session_impl::get_peer_class_type_filter() |
1352 | 0 | { |
1353 | 0 | return m_peer_class_type_filter; |
1354 | 0 | } |
1355 | | |
1356 | | void session_impl::set_peer_classes(peer_class_set* s, address const& a, socket_type_t const st) |
1357 | 0 | { |
1358 | 0 | std::uint32_t peer_class_mask = m_peer_class_filter.access(a); |
1359 | |
|
1360 | 0 | using sock_t = peer_class_type_filter::socket_type_t; |
1361 | | // assign peer class based on socket type |
1362 | 0 | static aux::array<sock_t, 9, socket_type_t> const mapping{{{ |
1363 | 0 | sock_t::tcp_socket |
1364 | 0 | , sock_t::tcp_socket |
1365 | 0 | , sock_t::tcp_socket |
1366 | 0 | , sock_t::utp_socket |
1367 | 0 | , sock_t::i2p_socket |
1368 | 0 | , sock_t::ssl_tcp_socket |
1369 | 0 | , sock_t::ssl_tcp_socket |
1370 | 0 | , sock_t::ssl_tcp_socket |
1371 | 0 | , sock_t::ssl_utp_socket |
1372 | 0 | }}}; |
1373 | 0 | sock_t const socket_type = mapping[st]; |
1374 | | // filter peer classes based on type |
1375 | 0 | peer_class_mask = m_peer_class_type_filter.apply(socket_type, peer_class_mask); |
1376 | |
|
1377 | 0 | for (peer_class_t i{0}; peer_class_mask; peer_class_mask >>= 1, ++i) |
1378 | 0 | { |
1379 | 0 | if ((peer_class_mask & 1) == 0) continue; |
1380 | | |
1381 | | // if you hit this assert, your peer class filter contains |
1382 | | // a bitmask referencing a non-existent peer class |
1383 | 0 | TORRENT_ASSERT_PRECOND(m_classes.at(i)); |
1384 | |
|
1385 | 0 | if (m_classes.at(i) == nullptr) continue; |
1386 | 0 | s->add_class(m_classes, i); |
1387 | 0 | } |
1388 | 0 | } |
1389 | | |
1390 | | bool session_impl::ignore_unchoke_slots_set(peer_class_set const& set) const |
1391 | 0 | { |
1392 | 0 | int num = set.num_classes(); |
1393 | 0 | for (int i = 0; i < num; ++i) |
1394 | 0 | { |
1395 | 0 | peer_class const* pc = m_classes.at(set.class_at(i)); |
1396 | 0 | if (pc == nullptr) continue; |
1397 | 0 | if (pc->ignore_unchoke_slots) return true; |
1398 | 0 | } |
1399 | 0 | return false; |
1400 | 0 | } |
1401 | | |
1402 | | bandwidth_manager* session_impl::get_bandwidth_manager(int channel) |
1403 | 0 | { |
1404 | 0 | return (channel == peer_connection::download_channel) |
1405 | 0 | ? &m_download_rate : &m_upload_rate; |
1406 | 0 | } |
1407 | | |
1408 | | void session_impl::deferred_submit_jobs() |
1409 | 3.56k | { |
1410 | 3.56k | if (m_deferred_submit_disk_jobs) return; |
1411 | 1.83k | m_deferred_submit_disk_jobs = true; |
1412 | 1.83k | post(m_io_context, make_handler( |
1413 | 1.83k | [this] { wrap(&session_impl::submit_disk_jobs); } |
1414 | 1.83k | , m_submit_jobs_handler_storage, *this)); |
1415 | 1.83k | } |
1416 | | |
1417 | | void session_impl::submit_disk_jobs() |
1418 | 1.83k | { |
1419 | 1.83k | TORRENT_ASSERT(m_deferred_submit_disk_jobs); |
1420 | 1.83k | m_deferred_submit_disk_jobs = false; |
1421 | 1.83k | m_disk_thread->submit_jobs(); |
1422 | 1.83k | } |
1423 | | |
1424 | | // copies pointers to bandwidth channels from the peer classes |
1425 | | // into the array. Only bandwidth channels with a bandwidth limit |
1426 | | // is considered pertinent and copied |
1427 | | // returns the number of pointers copied |
1428 | | // channel is upload_channel or download_channel |
1429 | | int session_impl::copy_pertinent_channels(peer_class_set const& set |
1430 | | , int channel, bandwidth_channel** dst, int const max) |
1431 | 0 | { |
1432 | 0 | int num_channels = set.num_classes(); |
1433 | 0 | int num_copied = 0; |
1434 | 0 | for (int i = 0; i < num_channels; ++i) |
1435 | 0 | { |
1436 | 0 | peer_class* pc = m_classes.at(set.class_at(i)); |
1437 | 0 | TORRENT_ASSERT(pc); |
1438 | 0 | if (pc == nullptr) continue; |
1439 | 0 | bandwidth_channel* chan = &pc->channel[channel]; |
1440 | | // no need to include channels that don't have any bandwidth limits |
1441 | 0 | if (chan->throttle() == 0) continue; |
1442 | 0 | dst[num_copied] = chan; |
1443 | 0 | ++num_copied; |
1444 | 0 | if (num_copied == max) break; |
1445 | 0 | } |
1446 | 0 | return num_copied; |
1447 | 0 | } |
1448 | | |
1449 | | bool session_impl::use_quota_overhead(bandwidth_channel* ch, int amount) |
1450 | 0 | { |
1451 | 0 | ch->use_quota(amount); |
1452 | 0 | return (ch->throttle() > 0 && ch->throttle() < amount); |
1453 | 0 | } |
1454 | | |
1455 | | int session_impl::use_quota_overhead(peer_class_set& set, int const amount_down, int const amount_up) |
1456 | 0 | { |
1457 | 0 | int ret = 0; |
1458 | 0 | int const num = set.num_classes(); |
1459 | 0 | for (int i = 0; i < num; ++i) |
1460 | 0 | { |
1461 | 0 | peer_class* p = m_classes.at(set.class_at(i)); |
1462 | 0 | if (p == nullptr) continue; |
1463 | | |
1464 | 0 | bandwidth_channel* ch = &p->channel[peer_connection::download_channel]; |
1465 | 0 | if (use_quota_overhead(ch, amount_down)) |
1466 | 0 | ret |= 1 << peer_connection::download_channel; |
1467 | 0 | ch = &p->channel[peer_connection::upload_channel]; |
1468 | 0 | if (use_quota_overhead(ch, amount_up)) |
1469 | 0 | ret |= 1 << peer_connection::upload_channel; |
1470 | 0 | } |
1471 | 0 | return ret; |
1472 | 0 | } |
1473 | | |
1474 | | // session_impl is responsible for deleting 'pack' |
1475 | | void session_impl::apply_settings_pack(std::shared_ptr<settings_pack> pack) |
1476 | 0 | { |
1477 | 0 | INVARIANT_CHECK; |
1478 | 0 | apply_settings_pack_impl(*pack); |
1479 | 0 | } |
1480 | | |
1481 | | settings_pack session_impl::get_settings() const |
1482 | 0 | { |
1483 | 0 | settings_pack ret; |
1484 | | // TODO: it would be nice to reserve() these vectors up front |
1485 | 0 | for (int i = settings_pack::string_type_base; |
1486 | 0 | i < settings_pack::max_string_setting_internal; ++i) |
1487 | 0 | { |
1488 | 0 | ret.set_str(i, m_settings.get_str(i)); |
1489 | 0 | } |
1490 | 0 | for (int i = settings_pack::int_type_base; |
1491 | 0 | i < settings_pack::max_int_setting_internal; ++i) |
1492 | 0 | { |
1493 | 0 | ret.set_int(i, m_settings.get_int(i)); |
1494 | 0 | } |
1495 | 0 | for (int i = settings_pack::bool_type_base; |
1496 | 0 | i < settings_pack::max_bool_setting_internal; ++i) |
1497 | 0 | { |
1498 | 0 | ret.set_bool(i, m_settings.get_bool(i)); |
1499 | 0 | } |
1500 | 0 | return ret; |
1501 | 0 | } |
1502 | | |
1503 | | namespace { |
1504 | | template <typename Pack> |
1505 | | int get_setting_impl(Pack const& p, int name, int*) |
1506 | 0 | { return p.get_int(name); } Unexecuted instantiation: session_impl.cpp:int libtorrent::aux::(anonymous namespace)::get_setting_impl<libtorrent::settings_pack>(libtorrent::settings_pack const&, int, int*) Unexecuted instantiation: session_impl.cpp:int libtorrent::aux::(anonymous namespace)::get_setting_impl<libtorrent::aux::session_settings>(libtorrent::aux::session_settings const&, int, int*) |
1507 | | |
1508 | | template <typename Pack> |
1509 | | bool get_setting_impl(Pack const& p, int name, bool*) |
1510 | 0 | { return p.get_bool(name); } Unexecuted instantiation: session_impl.cpp:bool libtorrent::aux::(anonymous namespace)::get_setting_impl<libtorrent::settings_pack>(libtorrent::settings_pack const&, int, bool*) Unexecuted instantiation: session_impl.cpp:bool libtorrent::aux::(anonymous namespace)::get_setting_impl<libtorrent::aux::session_settings>(libtorrent::aux::session_settings const&, int, bool*) |
1511 | | |
1512 | | template <typename Pack> |
1513 | | std::string get_setting_impl(Pack const& p, int name, std::string*) |
1514 | 0 | { return p.get_str(name); } Unexecuted instantiation: session_impl.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > libtorrent::aux::(anonymous namespace)::get_setting_impl<libtorrent::settings_pack>(libtorrent::settings_pack const&, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) Unexecuted instantiation: session_impl.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > libtorrent::aux::(anonymous namespace)::get_setting_impl<libtorrent::aux::session_settings>(libtorrent::aux::session_settings const&, int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) |
1515 | | |
1516 | | template <typename Type, typename Pack> |
1517 | | Type get_setting(Pack const& p, int name) |
1518 | 0 | { |
1519 | 0 | return get_setting_impl(p, name, static_cast<Type*>(nullptr)); |
1520 | 0 | } Unexecuted instantiation: session_impl.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > libtorrent::aux::(anonymous namespace)::get_setting<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, libtorrent::settings_pack>(libtorrent::settings_pack const&, int) Unexecuted instantiation: session_impl.cpp:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > libtorrent::aux::(anonymous namespace)::get_setting<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, libtorrent::aux::session_settings>(libtorrent::aux::session_settings const&, int) Unexecuted instantiation: session_impl.cpp:int libtorrent::aux::(anonymous namespace)::get_setting<int, libtorrent::settings_pack>(libtorrent::settings_pack const&, int) Unexecuted instantiation: session_impl.cpp:int libtorrent::aux::(anonymous namespace)::get_setting<int, libtorrent::aux::session_settings>(libtorrent::aux::session_settings const&, int) Unexecuted instantiation: session_impl.cpp:bool libtorrent::aux::(anonymous namespace)::get_setting<bool, libtorrent::settings_pack>(libtorrent::settings_pack const&, int) Unexecuted instantiation: session_impl.cpp:bool libtorrent::aux::(anonymous namespace)::get_setting<bool, libtorrent::aux::session_settings>(libtorrent::aux::session_settings const&, int) |
1521 | | |
1522 | | template <typename Type> |
1523 | | bool setting_changed(settings_pack const& pack, aux::session_settings const& sett, int name) |
1524 | 0 | { |
1525 | 0 | return pack.has_val(name) |
1526 | 0 | && get_setting<Type>(pack, name) != get_setting<Type>(sett, name); |
1527 | 0 | } Unexecuted instantiation: session_impl.cpp:bool libtorrent::aux::(anonymous namespace)::setting_changed<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(libtorrent::settings_pack const&, libtorrent::aux::session_settings const&, int) Unexecuted instantiation: session_impl.cpp:bool libtorrent::aux::(anonymous namespace)::setting_changed<int>(libtorrent::settings_pack const&, libtorrent::aux::session_settings const&, int) Unexecuted instantiation: session_impl.cpp:bool libtorrent::aux::(anonymous namespace)::setting_changed<bool>(libtorrent::settings_pack const&, libtorrent::aux::session_settings const&, int) |
1528 | | } |
1529 | | |
1530 | | #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS |
1531 | | void session_impl::validate_setting(int const int_name, int const min, int const max) |
1532 | 20.1k | { |
1533 | 20.1k | int const val = m_settings.get_int(int_name); |
1534 | 20.1k | TORRENT_ASSERT_PRECOND_MSG(val >= min, name_for_setting(int_name)); |
1535 | 20.1k | TORRENT_ASSERT_PRECOND_MSG(val <= max, name_for_setting(int_name)); |
1536 | | #ifndef TORRENT_DISABLE_LOGGING |
1537 | | if (val < min || val > max) |
1538 | | session_log("invalid %s setting: %d", name_for_setting(int_name), val); |
1539 | | #endif |
1540 | 20.1k | } |
1541 | | |
1542 | | void session_impl::validate_settings() |
1543 | 1.83k | { |
1544 | 1.83k | validate_setting(settings_pack::out_enc_policy, 0, 2); |
1545 | 1.83k | validate_setting(settings_pack::in_enc_policy, 0, 2); |
1546 | 1.83k | validate_setting(settings_pack::allowed_enc_level, 1, 3); |
1547 | 1.83k | validate_setting(settings_pack::mixed_mode_algorithm, 0, 1); |
1548 | 1.83k | validate_setting(settings_pack::proxy_type, 0, 5); |
1549 | 1.83k | validate_setting(settings_pack::disk_io_read_mode, 0, 3); |
1550 | 1.83k | validate_setting(settings_pack::disk_io_write_mode, 0, 3); |
1551 | 1.83k | validate_setting(settings_pack::choking_algorithm, 0, 3); |
1552 | 1.83k | validate_setting(settings_pack::seed_choking_algorithm, 0, 3); |
1553 | 1.83k | validate_setting(settings_pack::suggest_mode, 0, 1); |
1554 | 1.83k | validate_setting(settings_pack::disk_write_mode, 0, 2); |
1555 | 1.83k | } |
1556 | | #endif |
1557 | | |
1558 | | void session_impl::apply_settings_pack_impl(settings_pack const& pack) |
1559 | 0 | { |
1560 | 0 | bool const reopen_listen_port |
1561 | 0 | = setting_changed<std::string>(pack, m_settings, settings_pack::listen_interfaces) |
1562 | 0 | || setting_changed<int>(pack, m_settings, settings_pack::proxy_type) |
1563 | 0 | || setting_changed<bool>(pack, m_settings, settings_pack::proxy_peer_connections) |
1564 | 0 | #if TORRENT_ABI_VERSION == 1 |
1565 | 0 | || setting_changed<int>(pack, m_settings, settings_pack::ssl_listen) |
1566 | 0 | #endif |
1567 | 0 | ; |
1568 | |
|
1569 | 0 | bool const update_want_peers |
1570 | 0 | = setting_changed<bool>(pack, m_settings, settings_pack::seeding_outgoing_connections) |
1571 | 0 | || setting_changed<bool>(pack, m_settings, settings_pack::enable_outgoing_tcp) |
1572 | 0 | || setting_changed<bool>(pack, m_settings, settings_pack::enable_outgoing_utp) |
1573 | 0 | ; |
1574 | |
|
1575 | | #ifndef TORRENT_DISABLE_LOGGING |
1576 | | session_log("applying settings pack, reopen_listen_port=%s" |
1577 | | , reopen_listen_port ? "true" : "false"); |
1578 | | #endif |
1579 | |
|
1580 | 0 | apply_pack(&pack, m_settings, this); |
1581 | |
|
1582 | 0 | #if !defined TORRENT_DISABLE_LOGGING || TORRENT_USE_ASSERTS |
1583 | 0 | validate_settings(); |
1584 | 0 | #endif |
1585 | |
|
1586 | 0 | m_disk_thread->settings_updated(); |
1587 | |
|
1588 | 0 | if (!reopen_listen_port) |
1589 | 0 | { |
1590 | | // no need to call this if reopen_listen_port is true |
1591 | | // since the apply_pack will do it |
1592 | 0 | update_listen_interfaces(); |
1593 | 0 | } |
1594 | 0 | else |
1595 | 0 | { |
1596 | 0 | reopen_listen_sockets(); |
1597 | 0 | } |
1598 | |
|
1599 | 0 | if (update_want_peers) |
1600 | 0 | { |
1601 | 0 | for (auto const& t : m_torrents) |
1602 | 0 | t->update_want_peers(); |
1603 | 0 | } |
1604 | 0 | } |
1605 | | |
1606 | | std::shared_ptr<listen_socket_t> session_impl::setup_listener( |
1607 | | listen_endpoint_t const& lep, error_code& ec) |
1608 | 1.83k | { |
1609 | 1.83k | int retries = m_settings.get_int(settings_pack::max_retry_port_bind); |
1610 | 1.83k | tcp::endpoint bind_ep(lep.addr, std::uint16_t(lep.port)); |
1611 | | |
1612 | | #ifndef TORRENT_DISABLE_LOGGING |
1613 | | if (should_log()) |
1614 | | { |
1615 | | session_log("attempting to open listen socket to: %s on device: %s %s%s%s%s%s" |
1616 | | , print_endpoint(bind_ep).c_str(), lep.device.c_str() |
1617 | | , (lep.ssl == transport::ssl) ? "ssl " : "" |
1618 | | , (lep.flags & listen_socket_t::local_network) ? "local-network " : "" |
1619 | | , (lep.flags & listen_socket_t::accept_incoming) ? "accept-incoming " : "no-incoming " |
1620 | | , (lep.flags & listen_socket_t::was_expanded) ? "expanded-ip " : "" |
1621 | | , (lep.flags & listen_socket_t::proxy) ? "proxy " : ""); |
1622 | | } |
1623 | | #endif |
1624 | | |
1625 | 1.83k | auto ret = std::make_shared<listen_socket_t>(); |
1626 | 1.83k | ret->ssl = lep.ssl; |
1627 | 1.83k | ret->original_port = bind_ep.port(); |
1628 | 1.83k | ret->flags = lep.flags; |
1629 | 1.83k | ret->netmask = lep.netmask; |
1630 | 1.83k | operation_t last_op = operation_t::unknown; |
1631 | 1.83k | socket_type_t const sock_type |
1632 | 1.83k | = (lep.ssl == transport::ssl) |
1633 | 1.83k | ? socket_type_t::tcp_ssl |
1634 | 1.83k | : socket_type_t::tcp; |
1635 | | |
1636 | | // if we're in force-proxy mode, don't open TCP listen sockets. We cannot |
1637 | | // accept connections on our local machine in this case. |
1638 | | // TODO: 3 the logic in this if-block should be factored out into a |
1639 | | // separate function. At least most of it |
1640 | 1.83k | if (ret->flags & listen_socket_t::accept_incoming) |
1641 | 1.83k | { |
1642 | 1.83k | ret->sock = std::make_shared<tcp::acceptor>(m_io_context); |
1643 | 1.83k | ret->sock->open(bind_ep.protocol(), ec); |
1644 | 1.83k | last_op = operation_t::sock_open; |
1645 | 1.83k | if (ec) |
1646 | 0 | { |
1647 | | #ifndef TORRENT_DISABLE_LOGGING |
1648 | | if (should_log()) |
1649 | | { |
1650 | | session_log("failed to open socket: %s" |
1651 | | , ec.message().c_str()); |
1652 | | } |
1653 | | #endif |
1654 | |
|
1655 | 0 | if (m_alerts.should_post<listen_failed_alert>()) |
1656 | 0 | m_alerts.emplace_alert<listen_failed_alert>(lep.device, bind_ep, last_op |
1657 | 0 | , ec, sock_type); |
1658 | 0 | return ret; |
1659 | 0 | } |
1660 | | |
1661 | | #ifdef TORRENT_WINDOWS |
1662 | | { |
1663 | | // this is best-effort. ignore errors |
1664 | | error_code err; |
1665 | | ret->sock->set_option(exclusive_address_use(true), err); |
1666 | | #ifndef TORRENT_DISABLE_LOGGING |
1667 | | if (err && should_log()) |
1668 | | { |
1669 | | session_log("failed enable exclusive address use on listen socket: %s" |
1670 | | , err.message().c_str()); |
1671 | | } |
1672 | | #endif // TORRENT_DISABLE_LOGGING |
1673 | | } |
1674 | | #else |
1675 | | |
1676 | 1.83k | { |
1677 | | // this is best-effort. ignore errors |
1678 | 1.83k | error_code err; |
1679 | 1.83k | ret->sock->set_option(tcp::acceptor::reuse_address(true), err); |
1680 | | #ifndef TORRENT_DISABLE_LOGGING |
1681 | | if (err && should_log()) |
1682 | | { |
1683 | | session_log("failed enable reuse-address on listen socket: %s" |
1684 | | , err.message().c_str()); |
1685 | | } |
1686 | | #endif // TORRENT_DISABLE_LOGGING |
1687 | 1.83k | } |
1688 | 1.83k | #endif // TORRENT_WINDOWS |
1689 | | |
1690 | 1.83k | if (is_v6(bind_ep)) |
1691 | 0 | { |
1692 | 0 | error_code err; // ignore errors here |
1693 | 0 | ret->sock->set_option(boost::asio::ip::v6_only(true), err); |
1694 | | #ifndef TORRENT_DISABLE_LOGGING |
1695 | | if (err && should_log()) |
1696 | | { |
1697 | | session_log("failed enable v6 only on listen socket: %s" |
1698 | | , err.message().c_str()); |
1699 | | } |
1700 | | #endif // LOGGING |
1701 | |
|
1702 | | #ifdef TORRENT_WINDOWS |
1703 | | // enable Teredo on windows |
1704 | | ret->sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err); |
1705 | | #ifndef TORRENT_DISABLE_LOGGING |
1706 | | if (err && should_log()) |
1707 | | { |
1708 | | session_log("failed enable IPv6 unrestricted protection level on " |
1709 | | "listen socket: %s", err.message().c_str()); |
1710 | | } |
1711 | | #endif // TORRENT_DISABLE_LOGGING |
1712 | | #endif // TORRENT_WINDOWS |
1713 | 0 | } |
1714 | | |
1715 | 1.83k | if (!lep.device.empty()) |
1716 | 1.83k | { |
1717 | | // we have an actual device we're interested in listening on, if we |
1718 | | // have SO_BINDTODEVICE functionality, use it now. |
1719 | 1.83k | #if TORRENT_HAS_BINDTODEVICE |
1720 | 1.83k | bind_device(*ret->sock, lep.device.c_str(), ec); |
1721 | | #ifndef TORRENT_DISABLE_LOGGING |
1722 | | if (ec && should_log()) |
1723 | | { |
1724 | | session_log("bind to device failed (device: %s): %s" |
1725 | | , lep.device.c_str(), ec.message().c_str()); |
1726 | | } |
1727 | | #endif // TORRENT_DISABLE_LOGGING |
1728 | 1.83k | ec.clear(); |
1729 | 1.83k | #endif // TORRENT_HAS_BINDTODEVICE |
1730 | 1.83k | } |
1731 | | |
1732 | 1.83k | ret->sock->bind(bind_ep, ec); |
1733 | 1.83k | last_op = operation_t::sock_bind; |
1734 | | |
1735 | 1.83k | while (ec == error_code(error::address_in_use) && retries > 0) |
1736 | 0 | { |
1737 | 0 | TORRENT_ASSERT_VAL(ec, ec); |
1738 | | #ifndef TORRENT_DISABLE_LOGGING |
1739 | | if (should_log()) |
1740 | | { |
1741 | | session_log("failed to bind listen socket to: %s on device: %s :" |
1742 | | " [%s] (%d) %s (retries: %d)" |
1743 | | , print_endpoint(bind_ep).c_str() |
1744 | | , lep.device.c_str() |
1745 | | , ec.category().name(), ec.value(), ec.message().c_str() |
1746 | | , retries); |
1747 | | } |
1748 | | #endif |
1749 | 0 | ec.clear(); |
1750 | 0 | --retries; |
1751 | 0 | bind_ep.port(bind_ep.port() + 1); |
1752 | 0 | ret->sock->bind(bind_ep, ec); |
1753 | 0 | } |
1754 | | |
1755 | 1.83k | if (ec == error_code(error::address_in_use) |
1756 | 1.83k | && m_settings.get_bool(settings_pack::listen_system_port_fallback) |
1757 | 1.83k | && bind_ep.port() != 0) |
1758 | 0 | { |
1759 | | // instead of giving up, try let the OS pick a port |
1760 | 0 | bind_ep.port(0); |
1761 | 0 | ec.clear(); |
1762 | 0 | ret->sock->bind(bind_ep, ec); |
1763 | 0 | last_op = operation_t::sock_bind; |
1764 | 0 | } |
1765 | | |
1766 | 1.83k | if (ec) |
1767 | 0 | { |
1768 | | // not even that worked, give up |
1769 | |
|
1770 | | #ifndef TORRENT_DISABLE_LOGGING |
1771 | | if (should_log()) |
1772 | | { |
1773 | | session_log("failed to bind listen socket to: %s on device: %s :" |
1774 | | " [%s] (%d) %s (giving up)" |
1775 | | , print_endpoint(bind_ep).c_str() |
1776 | | , lep.device.c_str() |
1777 | | , ec.category().name(), ec.value(), ec.message().c_str()); |
1778 | | } |
1779 | | #endif |
1780 | 0 | if (m_alerts.should_post<listen_failed_alert>()) |
1781 | 0 | { |
1782 | 0 | m_alerts.emplace_alert<listen_failed_alert>(lep.device, bind_ep |
1783 | 0 | , last_op, ec, sock_type); |
1784 | 0 | } |
1785 | 0 | ret->sock.reset(); |
1786 | 0 | return ret; |
1787 | 0 | } |
1788 | 1.83k | ret->local_endpoint = ret->sock->local_endpoint(ec); |
1789 | 1.83k | last_op = operation_t::getname; |
1790 | 1.83k | if (ec) |
1791 | 0 | { |
1792 | | #ifndef TORRENT_DISABLE_LOGGING |
1793 | | if (should_log()) |
1794 | | { |
1795 | | session_log("get_sockname failed on listen socket: %s" |
1796 | | , ec.message().c_str()); |
1797 | | } |
1798 | | #endif |
1799 | 0 | if (m_alerts.should_post<listen_failed_alert>()) |
1800 | 0 | { |
1801 | 0 | m_alerts.emplace_alert<listen_failed_alert>(lep.device, bind_ep |
1802 | 0 | , last_op, ec, sock_type); |
1803 | 0 | } |
1804 | 0 | return ret; |
1805 | 0 | } |
1806 | | |
1807 | 1.83k | TORRENT_ASSERT(ret->local_endpoint.port() == bind_ep.port() |
1808 | 1.83k | || bind_ep.port() == 0); |
1809 | | |
1810 | 1.83k | if (bind_ep.port() == 0) bind_ep = ret->local_endpoint; |
1811 | | |
1812 | 1.83k | ret->sock->listen(m_settings.get_int(settings_pack::listen_queue_size), ec); |
1813 | 1.83k | last_op = operation_t::sock_listen; |
1814 | | |
1815 | 1.83k | if (ec) |
1816 | 0 | { |
1817 | | #ifndef TORRENT_DISABLE_LOGGING |
1818 | | if (should_log()) |
1819 | | { |
1820 | | session_log("cannot listen on interface \"%s\": %s" |
1821 | | , lep.device.c_str(), ec.message().c_str()); |
1822 | | } |
1823 | | #endif |
1824 | 0 | if (m_alerts.should_post<listen_failed_alert>()) |
1825 | 0 | { |
1826 | 0 | m_alerts.emplace_alert<listen_failed_alert>(lep.device, bind_ep |
1827 | 0 | , last_op, ec, sock_type); |
1828 | 0 | } |
1829 | 0 | return ret; |
1830 | 0 | } |
1831 | 1.83k | } // accept incoming |
1832 | | |
1833 | 1.83k | socket_type_t const udp_sock_type |
1834 | 1.83k | = (lep.ssl == transport::ssl) |
1835 | 1.83k | ? socket_type_t::utp_ssl |
1836 | 1.83k | : socket_type_t::utp; |
1837 | 1.83k | udp::endpoint udp_bind_ep(bind_ep.address(), bind_ep.port()); |
1838 | | |
1839 | 1.83k | ret->udp_sock = std::make_shared<session_udp_socket>(m_io_context, ret); |
1840 | 1.83k | ret->udp_sock->sock.open(udp_bind_ep.protocol(), ec); |
1841 | 1.83k | if (ec) |
1842 | 0 | { |
1843 | | #ifndef TORRENT_DISABLE_LOGGING |
1844 | | if (should_log()) |
1845 | | { |
1846 | | session_log("failed to open UDP socket: %s: %s" |
1847 | | , lep.device.c_str(), ec.message().c_str()); |
1848 | | } |
1849 | | #endif |
1850 | |
|
1851 | 0 | last_op = operation_t::sock_open; |
1852 | 0 | if (m_alerts.should_post<listen_failed_alert>()) |
1853 | 0 | m_alerts.emplace_alert<listen_failed_alert>(lep.device |
1854 | 0 | , bind_ep, last_op, ec, udp_sock_type); |
1855 | |
|
1856 | 0 | return ret; |
1857 | 0 | } |
1858 | | |
1859 | 1.83k | #if TORRENT_HAS_BINDTODEVICE |
1860 | 1.83k | if (!lep.device.empty()) |
1861 | 1.83k | { |
1862 | 1.83k | bind_device(ret->udp_sock->sock, lep.device.c_str(), ec); |
1863 | | #ifndef TORRENT_DISABLE_LOGGING |
1864 | | if (ec && should_log()) |
1865 | | { |
1866 | | session_log("bind to device failed (device: %s): %s" |
1867 | | , lep.device.c_str(), ec.message().c_str()); |
1868 | | } |
1869 | | #endif // TORRENT_DISABLE_LOGGING |
1870 | 1.83k | ec.clear(); |
1871 | 1.83k | } |
1872 | 1.83k | #endif |
1873 | 1.83k | ret->udp_sock->sock.bind(udp_bind_ep, ec); |
1874 | | |
1875 | 1.83k | while (ec == error_code(error::address_in_use) && retries > 0) |
1876 | 0 | { |
1877 | 0 | TORRENT_ASSERT_VAL(ec, ec); |
1878 | | #ifndef TORRENT_DISABLE_LOGGING |
1879 | | if (should_log()) |
1880 | | { |
1881 | | session_log("failed to bind udp socket to: %s on device: %s :" |
1882 | | " [%s] (%d) %s (retries: %d)" |
1883 | | , print_endpoint(bind_ep).c_str() |
1884 | | , lep.device.c_str() |
1885 | | , ec.category().name(), ec.value(), ec.message().c_str() |
1886 | | , retries); |
1887 | | } |
1888 | | #endif |
1889 | 0 | ec.clear(); |
1890 | 0 | --retries; |
1891 | 0 | udp_bind_ep.port(udp_bind_ep.port() + 1); |
1892 | 0 | ret->udp_sock->sock.bind(udp_bind_ep, ec); |
1893 | 0 | } |
1894 | | |
1895 | 1.83k | if (ec == error_code(error::address_in_use) |
1896 | 1.83k | && m_settings.get_bool(settings_pack::listen_system_port_fallback) |
1897 | 1.83k | && udp_bind_ep.port() != 0) |
1898 | 0 | { |
1899 | | // instead of giving up, try let the OS pick a port |
1900 | 0 | udp_bind_ep.port(0); |
1901 | 0 | ec.clear(); |
1902 | 0 | ret->udp_sock->sock.bind(udp_bind_ep, ec); |
1903 | 0 | } |
1904 | | |
1905 | 1.83k | last_op = operation_t::sock_bind; |
1906 | 1.83k | if (ec) |
1907 | 0 | { |
1908 | | #ifndef TORRENT_DISABLE_LOGGING |
1909 | | if (should_log()) |
1910 | | { |
1911 | | session_log("failed to bind UDP socket: %s: %s" |
1912 | | , lep.device.c_str(), ec.message().c_str()); |
1913 | | } |
1914 | | #endif |
1915 | |
|
1916 | 0 | if (m_alerts.should_post<listen_failed_alert>()) |
1917 | 0 | m_alerts.emplace_alert<listen_failed_alert>(lep.device |
1918 | 0 | , bind_ep, last_op, ec, udp_sock_type); |
1919 | |
|
1920 | 0 | return ret; |
1921 | 0 | } |
1922 | | |
1923 | | // if we did not open a TCP listen socket, ret->local_endpoint was never |
1924 | | // initialized, so do that now, based on the UDP socket |
1925 | 1.83k | if (!(ret->flags & listen_socket_t::accept_incoming)) |
1926 | 0 | { |
1927 | 0 | auto const udp_ep = ret->udp_sock->local_endpoint(); |
1928 | 0 | ret->local_endpoint = tcp::endpoint(udp_ep.address(), udp_ep.port()); |
1929 | 0 | } |
1930 | | |
1931 | 1.83k | ret->device = lep.device; |
1932 | | |
1933 | 1.83k | error_code err; |
1934 | 1.83k | set_socket_buffer_size(ret->udp_sock->sock, m_settings, err); |
1935 | 1.83k | if (err) |
1936 | 0 | { |
1937 | 0 | if (m_alerts.should_post<udp_error_alert>()) |
1938 | 0 | m_alerts.emplace_alert<udp_error_alert>(ret->udp_sock->sock.local_endpoint(ec) |
1939 | 0 | , operation_t::alloc_recvbuf, err); |
1940 | 0 | } |
1941 | | |
1942 | | // this call is necessary here because, unless the settings actually |
1943 | | // change after the session is up and listening, at no other point |
1944 | | // set_proxy_settings is called with the correct proxy configuration, |
1945 | | // internally, this method handle the SOCKS5's connection logic |
1946 | 1.83k | ret->udp_sock->sock.set_proxy_settings(proxy(), m_alerts, get_resolver() |
1947 | 1.83k | , settings().get_bool(settings_pack::socks5_udp_send_local_ep)); |
1948 | | |
1949 | 1.83k | ADD_OUTSTANDING_ASYNC("session_impl::on_udp_packet"); |
1950 | 1.83k | ret->udp_sock->sock.async_read(aux::make_handler([this, ret](error_code const& e) |
1951 | 1.83k | { this->on_udp_packet(ret->udp_sock, ret, ret->ssl, e); } |
1952 | 1.83k | , ret->udp_handler_storage, *this)); |
1953 | | |
1954 | | #ifndef TORRENT_DISABLE_LOGGING |
1955 | | if (should_log()) |
1956 | | { |
1957 | | session_log(" listening on: %s TCP port: %d UDP port: %d" |
1958 | | , bind_ep.address().to_string().c_str() |
1959 | | , ret->tcp_external_port(), ret->udp_external_port()); |
1960 | | } |
1961 | | #endif |
1962 | 1.83k | return ret; |
1963 | 1.83k | } |
1964 | | |
1965 | | void session_impl::on_exception(std::exception const& e) |
1966 | 0 | { |
1967 | 0 | TORRENT_UNUSED(e); |
1968 | | #ifndef TORRENT_DISABLE_LOGGING |
1969 | | session_log("FATAL SESSION ERROR [%s]", e.what()); |
1970 | | #endif |
1971 | 0 | this->abort(); |
1972 | 0 | } |
1973 | | |
1974 | | void session_impl::on_error(error_code const& ec) |
1975 | 0 | { |
1976 | 0 | TORRENT_UNUSED(ec); |
1977 | | #ifndef TORRENT_DISABLE_LOGGING |
1978 | | session_log("FATAL SESSION ERROR (%s : %d) [%s]" |
1979 | | , ec.category().name(), ec.value(), ec.message().c_str()); |
1980 | | #endif |
1981 | 0 | this->abort(); |
1982 | 0 | } |
1983 | | |
1984 | | void session_impl::on_ip_change(error_code const& ec) |
1985 | 0 | { |
1986 | | #ifndef TORRENT_DISABLE_LOGGING |
1987 | | if (!ec) |
1988 | | session_log("received ip change from internal ip_notifier"); |
1989 | | else |
1990 | | session_log("received error on_ip_change: %d, %s", ec.value(), ec.message().c_str()); |
1991 | | #endif |
1992 | 0 | if (ec || m_abort || !m_ip_notifier) return; |
1993 | 0 | m_ip_notifier->async_wait([this] (error_code const& e) |
1994 | 0 | { wrap(&session_impl::on_ip_change, e); }); |
1995 | 0 | reopen_network_sockets({}); |
1996 | 0 | } |
1997 | | |
1998 | | // TODO: could this function be merged with expand_unspecified_addresses? |
1999 | | // right now both listen_endpoint_t and listen_interface_t are almost |
2000 | | // identical, maybe the latter could be removed too |
2001 | | void interface_to_endpoints(listen_interface_t const& iface |
2002 | | , listen_socket_flags_t flags |
2003 | | , span<ip_interface const> const ifs |
2004 | | , std::vector<listen_endpoint_t>& eps) |
2005 | 1.83k | { |
2006 | 1.83k | flags |= iface.local ? listen_socket_t::local_network : listen_socket_flags_t{}; |
2007 | 1.83k | transport const ssl = iface.ssl ? transport::ssl : transport::plaintext; |
2008 | | |
2009 | | // First, check to see if it's an IP address |
2010 | 1.83k | error_code err; |
2011 | 1.83k | address const adr = make_address(iface.device.c_str(), err); |
2012 | 1.83k | if (!err) |
2013 | 1.83k | { |
2014 | 1.83k | eps.emplace_back(adr, iface.port, std::string{}, ssl, flags); |
2015 | 1.83k | } |
2016 | 0 | else |
2017 | 0 | { |
2018 | 0 | flags |= listen_socket_t::was_expanded; |
2019 | | |
2020 | | // this is the case where device names a network device. We need to |
2021 | | // enumerate all IPs associated with this device |
2022 | 0 | for (auto const& ipface : ifs) |
2023 | 0 | { |
2024 | | // we're looking for a specific interface, and its address |
2025 | | // (which must be of the same family as the address we're |
2026 | | // connecting to) |
2027 | 0 | if (iface.device != ipface.name) continue; |
2028 | | |
2029 | 0 | bool const local = iface.local |
2030 | 0 | || ipface.interface_address.is_loopback() |
2031 | 0 | || is_link_local(ipface.interface_address); |
2032 | |
|
2033 | 0 | eps.emplace_back(ipface.interface_address, iface.port, iface.device |
2034 | 0 | , ssl, flags | (local ? listen_socket_t::local_network : listen_socket_flags_t{})); |
2035 | 0 | } |
2036 | 0 | } |
2037 | 1.83k | } |
2038 | | |
2039 | | void session_impl::reopen_listen_sockets(bool const map_ports) |
2040 | 1.83k | { |
2041 | | #ifndef TORRENT_DISABLE_LOGGING |
2042 | | session_log("reopen listen sockets"); |
2043 | | #endif |
2044 | | |
2045 | 1.83k | TORRENT_ASSERT(is_single_thread()); |
2046 | | |
2047 | 1.83k | TORRENT_ASSERT(!m_abort); |
2048 | | |
2049 | 1.83k | error_code ec; |
2050 | | |
2051 | 1.83k | if (m_abort) return; |
2052 | | |
2053 | | // first build a list of endpoints we should be listening on |
2054 | | // we need to remove any unneeded sockets first to avoid the possibility |
2055 | | // of a new socket failing to bind due to a conflict with a stale socket |
2056 | 1.83k | std::vector<listen_endpoint_t> eps; |
2057 | | |
2058 | | // if we don't proxy peer connections, don't apply the special logic for |
2059 | | // proxies |
2060 | 1.83k | if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none |
2061 | 1.83k | && m_settings.get_bool(settings_pack::proxy_peer_connections)) |
2062 | 0 | { |
2063 | | // we will be able to accept incoming connections over UDP. so use |
2064 | | // one of the ports the user specified to use a consistent port |
2065 | | // across sessions. If the user did not specify any ports, pick one |
2066 | | // at random |
2067 | 0 | int const port = m_listen_interfaces.empty() |
2068 | 0 | ? int(random(63000) + 2000) |
2069 | 0 | : m_listen_interfaces.front().port; |
2070 | 0 | listen_endpoint_t ep(address_v4::any(), port, {} |
2071 | 0 | , transport::plaintext, listen_socket_t::proxy); |
2072 | 0 | eps.emplace_back(ep); |
2073 | 0 | } |
2074 | 1.83k | else |
2075 | 1.83k | { |
2076 | 1.83k | std::vector<ip_interface> const ifs = enum_net_interfaces(m_io_context, ec); |
2077 | 1.83k | if (ec && m_alerts.should_post<listen_failed_alert>()) |
2078 | 0 | { |
2079 | 0 | m_alerts.emplace_alert<listen_failed_alert>("" |
2080 | 0 | , operation_t::enum_if, ec, socket_type_t::tcp); |
2081 | 0 | } |
2082 | 1.83k | auto const routes = enum_routes(m_io_context, ec); |
2083 | 1.83k | if (ec && m_alerts.should_post<listen_failed_alert>()) |
2084 | 0 | { |
2085 | 0 | m_alerts.emplace_alert<listen_failed_alert>("" |
2086 | 0 | , operation_t::enum_route, ec, socket_type_t::tcp); |
2087 | 0 | } |
2088 | | |
2089 | | // expand device names and populate eps |
2090 | 1.83k | for (auto const& iface : m_listen_interfaces) |
2091 | 1.83k | { |
2092 | | #if !TORRENT_USE_SSL |
2093 | | if (iface.ssl) |
2094 | | { |
2095 | | #ifndef TORRENT_DISABLE_LOGGING |
2096 | | session_log("attempted to listen ssl with no library support on device: \"%s\"" |
2097 | | , iface.device.c_str()); |
2098 | | #endif |
2099 | | if (m_alerts.should_post<listen_failed_alert>()) |
2100 | | { |
2101 | | m_alerts.emplace_alert<listen_failed_alert>(iface.device |
2102 | | , operation_t::sock_open |
2103 | | , boost::asio::error::operation_not_supported |
2104 | | , socket_type_t::tcp_ssl); |
2105 | | } |
2106 | | continue; |
2107 | | } |
2108 | | #endif |
2109 | | |
2110 | | // now we have a device to bind to. This device may actually just be an |
2111 | | // IP address or a device name. In case it's a device name, we want to |
2112 | | // (potentially) end up binding a socket for each IP address associated |
2113 | | // with that device. |
2114 | 1.83k | interface_to_endpoints(iface, listen_socket_t::accept_incoming, ifs, eps); |
2115 | 1.83k | } |
2116 | | |
2117 | 1.83k | if (eps.empty()) |
2118 | 0 | { |
2119 | | #ifndef TORRENT_DISABLE_LOGGING |
2120 | | session_log("no listen sockets"); |
2121 | | #endif |
2122 | 0 | } |
2123 | | |
2124 | | #if defined TORRENT_ANDROID && __ANDROID_API__ >= 24 |
2125 | | // For Android API >= 24, enum_routes with the current NETLINK based |
2126 | | // implementation is unsupported (maybe in the future the operation |
2127 | | // will be restore using another implementation). If routes is empty, |
2128 | | // allow using unspecified address is a best effort approach that |
2129 | | // seems to work. The issue with this approach is with the DHTs, |
2130 | | // because for IPv6 this is not following BEP 32 and BEP 45. See: |
2131 | | // https://www.bittorrent.org/beps/bep_0032.html |
2132 | | // https://www.bittorrent.org/beps/bep_0045.html |
2133 | | if (!routes.empty()) expand_unspecified_address(ifs, routes, eps); |
2134 | | #else |
2135 | 1.83k | expand_unspecified_address(ifs, routes, eps); |
2136 | 1.83k | #endif |
2137 | 1.83k | expand_devices(ifs, eps); |
2138 | 1.83k | } |
2139 | | |
2140 | 1.83k | auto remove_iter = partition_listen_sockets(eps, m_listen_sockets); |
2141 | | |
2142 | 1.83k | while (remove_iter != m_listen_sockets.end()) |
2143 | 0 | { |
2144 | 0 | #ifndef TORRENT_DISABLE_DHT |
2145 | 0 | if (m_dht) |
2146 | 0 | m_dht->delete_socket(*remove_iter); |
2147 | 0 | #endif |
2148 | |
|
2149 | | #ifndef TORRENT_DISABLE_LOGGING |
2150 | | if (should_log()) |
2151 | | { |
2152 | | session_log("closing listen socket for %s on device \"%s\"" |
2153 | | , print_endpoint((*remove_iter)->local_endpoint).c_str() |
2154 | | , (*remove_iter)->device.c_str()); |
2155 | | } |
2156 | | #endif |
2157 | 0 | if ((*remove_iter)->sock) (*remove_iter)->sock->close(ec); |
2158 | 0 | if ((*remove_iter)->udp_sock) (*remove_iter)->udp_sock->sock.close(); |
2159 | 0 | if ((*remove_iter)->natpmp_mapper) (*remove_iter)->natpmp_mapper->close(); |
2160 | 0 | if ((*remove_iter)->upnp_mapper) (*remove_iter)->upnp_mapper->close(); |
2161 | 0 | if ((*remove_iter)->lsd) (*remove_iter)->lsd->close(); |
2162 | 0 | remove_iter = m_listen_sockets.erase(remove_iter); |
2163 | 0 | } |
2164 | | |
2165 | | // all sockets in there stayed the same. Only sockets after this point are |
2166 | | // new and should post alerts |
2167 | 1.83k | int const existing_sockets = int(m_listen_sockets.size()); |
2168 | | |
2169 | 1.83k | m_stats_counters.set_value(counters::has_incoming_connections |
2170 | 1.83k | , std::any_of(m_listen_sockets.begin(), m_listen_sockets.end() |
2171 | 1.83k | , [](std::shared_ptr<listen_socket_t> const& l) |
2172 | 1.83k | { return l->incoming_connection; })); |
2173 | | |
2174 | | // open new sockets on any endpoints that didn't match with |
2175 | | // an existing socket |
2176 | 1.83k | for (auto const& ep : eps) |
2177 | 1.83k | #ifndef BOOST_NO_EXCEPTIONS |
2178 | 1.83k | try |
2179 | 1.83k | #endif |
2180 | 1.83k | { |
2181 | 1.83k | std::shared_ptr<listen_socket_t> s = setup_listener(ep, ec); |
2182 | | |
2183 | 1.83k | if (!ec && (s->sock || s->udp_sock)) |
2184 | 1.83k | { |
2185 | 1.83k | m_listen_sockets.emplace_back(s); |
2186 | | |
2187 | 1.83k | #ifndef TORRENT_DISABLE_DHT |
2188 | 1.83k | if (m_dht |
2189 | 1.83k | && s->ssl != transport::ssl |
2190 | 1.83k | && !(s->flags & listen_socket_t::local_network)) |
2191 | 0 | { |
2192 | 0 | m_dht->new_socket(m_listen_sockets.back()); |
2193 | 0 | } |
2194 | 1.83k | #endif |
2195 | | |
2196 | 1.83k | TORRENT_ASSERT(bool(s->flags & listen_socket_t::accept_incoming) == bool(s->sock)); |
2197 | 1.83k | if (s->sock) async_accept(s->sock, s->ssl); |
2198 | 1.83k | } |
2199 | 1.83k | } |
2200 | 1.83k | #ifndef BOOST_NO_EXCEPTIONS |
2201 | 1.83k | catch (std::exception const& e) |
2202 | 1.83k | { |
2203 | 0 | TORRENT_UNUSED(e); |
2204 | | #ifndef TORRENT_DISABLE_LOGGING |
2205 | | if (should_log()) |
2206 | | { |
2207 | | session_log("setup_listener(%s) device: %s failed: %s" |
2208 | | , print_endpoint(ep.addr, ep.port).c_str() |
2209 | | , ep.device.c_str() |
2210 | | , e.what()); |
2211 | | } |
2212 | | #endif // TORRENT_DISABLE_LOGGING |
2213 | 0 | } |
2214 | 1.83k | #endif // BOOST_NO_EXCEPTIONS |
2215 | | |
2216 | 1.83k | if (m_listen_sockets.empty()) |
2217 | 0 | { |
2218 | | #ifndef TORRENT_DISABLE_LOGGING |
2219 | | session_log("giving up on binding listen sockets"); |
2220 | | #endif |
2221 | 0 | return; |
2222 | 0 | } |
2223 | | |
2224 | 1.83k | auto const new_sockets = span<std::shared_ptr<listen_socket_t>>( |
2225 | 1.83k | m_listen_sockets).subspan(existing_sockets); |
2226 | | |
2227 | | // now, send out listen_succeeded_alert for the listen sockets we are |
2228 | | // listening on |
2229 | 1.83k | if (m_alerts.should_post<listen_succeeded_alert>()) |
2230 | 1 | { |
2231 | 1 | for (auto const& l : new_sockets) |
2232 | 1 | { |
2233 | 1 | error_code err; |
2234 | 1 | if (l->sock) |
2235 | 1 | { |
2236 | 1 | tcp::endpoint const tcp_ep = l->sock->local_endpoint(err); |
2237 | 1 | if (!err) |
2238 | 1 | { |
2239 | 1 | socket_type_t const socket_type |
2240 | 1 | = l->ssl == transport::ssl |
2241 | 1 | ? socket_type_t::tcp_ssl |
2242 | 1 | : socket_type_t::tcp; |
2243 | | |
2244 | 1 | m_alerts.emplace_alert<listen_succeeded_alert>( |
2245 | 1 | tcp_ep, socket_type); |
2246 | 1 | } |
2247 | 1 | } |
2248 | | |
2249 | 1 | if (l->udp_sock) |
2250 | 1 | { |
2251 | 1 | udp::endpoint const udp_ep = l->udp_sock->sock.local_endpoint(err); |
2252 | 1 | if (!err && l->udp_sock->sock.is_open()) |
2253 | 1 | { |
2254 | 1 | socket_type_t const socket_type |
2255 | 1 | = l->ssl == transport::ssl |
2256 | 1 | ? socket_type_t::utp_ssl |
2257 | 1 | : socket_type_t::utp; |
2258 | | |
2259 | 1 | m_alerts.emplace_alert<listen_succeeded_alert>( |
2260 | 1 | udp_ep, socket_type); |
2261 | 1 | } |
2262 | 1 | } |
2263 | 1 | } |
2264 | 1 | } |
2265 | | |
2266 | 1.83k | if (m_settings.get_int(settings_pack::peer_dscp) != 0) |
2267 | 1.83k | { |
2268 | 1.83k | update_peer_dscp(); |
2269 | 1.83k | } |
2270 | | |
2271 | 1.83k | ec.clear(); |
2272 | | |
2273 | 1.83k | if (m_settings.get_bool(settings_pack::enable_natpmp)) |
2274 | 0 | { |
2275 | 0 | for (auto const& s : new_sockets) |
2276 | 0 | start_natpmp(s); |
2277 | 0 | } |
2278 | | |
2279 | 1.83k | if (m_settings.get_bool(settings_pack::enable_upnp)) |
2280 | 0 | { |
2281 | 0 | for (auto const& s : new_sockets) |
2282 | 0 | start_upnp(s); |
2283 | 0 | } |
2284 | | |
2285 | 1.83k | if (map_ports) |
2286 | 0 | { |
2287 | 0 | for (auto const& s : m_listen_sockets) |
2288 | 0 | remap_ports(remap_natpmp_and_upnp, *s); |
2289 | 0 | } |
2290 | 1.83k | else |
2291 | 1.83k | { |
2292 | | // new sockets need to map ports even if the caller did not request |
2293 | | // re-mapping |
2294 | 1.83k | for (auto const& s : new_sockets) |
2295 | 1.83k | remap_ports(remap_natpmp_and_upnp, *s); |
2296 | 1.83k | } |
2297 | | |
2298 | 1.83k | update_lsd(); |
2299 | | |
2300 | 1.83k | #if TORRENT_USE_I2P |
2301 | 1.83k | open_new_incoming_i2p_connection(); |
2302 | 1.83k | #endif |
2303 | | |
2304 | | // trackers that were not reachable, may have become reachable now. |
2305 | | // so clear the "disabled" flags to let them be tried one more time |
2306 | | // TODO: it would probably be better to do this by having a |
2307 | | // listen-socket "version" number that gets bumped. And instead of |
2308 | | // setting a bool to disable a tracker, we set the version number that |
2309 | | // it was disabled at. This change would affect the ABI in 1.2, so |
2310 | | // should be done in 2.0 or later |
2311 | 1.83k | for (auto& t : m_torrents) |
2312 | 0 | t->enable_all_trackers(); |
2313 | 1.83k | } |
2314 | | |
2315 | | void session_impl::reopen_network_sockets(reopen_network_flags_t const options) |
2316 | 0 | { |
2317 | 0 | reopen_listen_sockets(bool(options & session_handle::reopen_map_ports)); |
2318 | 0 | } |
2319 | | |
2320 | | namespace { |
2321 | | template <typename MapProtocol, typename ProtoType, typename EndpointType> |
2322 | | void map_port(MapProtocol& m, ProtoType protocol, EndpointType const& ep |
2323 | | , port_mapping_t& map_handle, std::string const& device) |
2324 | 0 | { |
2325 | 0 | if (map_handle != port_mapping_t{-1}) m.delete_mapping(map_handle); |
2326 | 0 | map_handle = port_mapping_t{-1}; |
2327 | |
|
2328 | 0 | address const addr = ep.address(); |
2329 | | // with IPv4 the interface might be behind NAT so we can't skip them |
2330 | | // based on the scope of the local address |
2331 | 0 | if (addr.is_v6() && is_local(addr)) |
2332 | 0 | return; |
2333 | | |
2334 | | // only update this mapping if we actually have a socket listening |
2335 | 0 | if (ep != EndpointType()) |
2336 | 0 | map_handle = m.add_mapping(protocol, ep.port(), ep, device); |
2337 | 0 | } Unexecuted instantiation: session_impl.cpp:void libtorrent::aux::(anonymous namespace)::map_port<libtorrent::natpmp, libtorrent::portmap_protocol, boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> >(libtorrent::natpmp&, libtorrent::portmap_protocol, boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> const&, libtorrent::aux::strong_typedef<int, libtorrent::port_mapping_tag, void>&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Unexecuted instantiation: session_impl.cpp:void libtorrent::aux::(anonymous namespace)::map_port<libtorrent::upnp, libtorrent::portmap_protocol, boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> >(libtorrent::upnp&, libtorrent::portmap_protocol, boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> const&, libtorrent::aux::strong_typedef<int, libtorrent::port_mapping_tag, void>&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) |
2338 | | } |
2339 | | |
2340 | | void session_impl::remap_ports(remap_port_mask_t const mask |
2341 | | , listen_socket_t& s) |
2342 | 1.83k | { |
2343 | 1.83k | tcp::endpoint const tcp_ep = s.sock ? s.sock->local_endpoint() : tcp::endpoint(); |
2344 | 1.83k | udp::endpoint const udp_ep = s.udp_sock ? s.udp_sock->sock.local_endpoint() : udp::endpoint(); |
2345 | | |
2346 | 1.83k | if ((mask & remap_natpmp) && s.natpmp_mapper) |
2347 | 0 | { |
2348 | 0 | map_port(*s.natpmp_mapper, portmap_protocol::tcp, tcp_ep |
2349 | 0 | , s.tcp_port_mapping[portmap_transport::natpmp].mapping, s.device); |
2350 | 0 | map_port(*s.natpmp_mapper, portmap_protocol::udp, make_tcp(udp_ep) |
2351 | 0 | , s.udp_port_mapping[portmap_transport::natpmp].mapping, s.device); |
2352 | 0 | } |
2353 | 1.83k | if ((mask & remap_upnp) && s.upnp_mapper) |
2354 | 0 | { |
2355 | 0 | map_port(*s.upnp_mapper, portmap_protocol::tcp, tcp_ep |
2356 | 0 | , s.tcp_port_mapping[portmap_transport::upnp].mapping, s.device); |
2357 | 0 | map_port(*s.upnp_mapper, portmap_protocol::udp, make_tcp(udp_ep) |
2358 | 0 | , s.udp_port_mapping[portmap_transport::upnp].mapping, s.device); |
2359 | 0 | } |
2360 | 1.83k | } |
2361 | | |
2362 | | void session_impl::update_i2p_bridge() |
2363 | 3.67k | { |
2364 | | // we need this socket to be open before we |
2365 | | // can make name lookups for trackers for instance. |
2366 | | // pause the session now and resume it once we've |
2367 | | // established the i2p SAM connection |
2368 | 3.67k | #if TORRENT_USE_I2P |
2369 | 3.67k | if (m_settings.get_str(settings_pack::i2p_hostname).empty()) |
2370 | 3.67k | { |
2371 | 3.67k | error_code ec; |
2372 | 3.67k | m_i2p_conn.close(ec); |
2373 | 3.67k | return; |
2374 | 3.67k | } |
2375 | 0 | TORRENT_ASSERT(!m_abort); |
2376 | 0 | i2p_session_options session_options{ |
2377 | 0 | m_settings.get_int(settings_pack::i2p_inbound_quantity) |
2378 | 0 | , m_settings.get_int(settings_pack::i2p_outbound_quantity) |
2379 | 0 | , m_settings.get_int(settings_pack::i2p_inbound_length) |
2380 | 0 | , m_settings.get_int(settings_pack::i2p_outbound_length) |
2381 | 0 | , m_settings.get_int(settings_pack::i2p_inbound_length_variance) |
2382 | 0 | , m_settings.get_int(settings_pack::i2p_outbound_length_variance) |
2383 | 0 | }; |
2384 | 0 | m_i2p_conn.open(m_settings.get_str(settings_pack::i2p_hostname) |
2385 | 0 | , m_settings.get_int(settings_pack::i2p_port) |
2386 | 0 | , session_options |
2387 | 0 | , std::bind(&session_impl::on_i2p_open, this, _1)); |
2388 | 0 | #endif |
2389 | 0 | } |
2390 | | |
2391 | | #ifndef TORRENT_DISABLE_DHT |
2392 | | int session_impl::external_udp_port(address const& local_address) const |
2393 | 0 | { |
2394 | 0 | auto ls = std::find_if(m_listen_sockets.begin(), m_listen_sockets.end() |
2395 | 0 | , [&](std::shared_ptr<listen_socket_t> const& e) |
2396 | 0 | { |
2397 | 0 | return e->local_endpoint.address() == local_address; |
2398 | 0 | }); |
2399 | |
|
2400 | 0 | if (ls != m_listen_sockets.end()) |
2401 | 0 | return (*ls)->udp_external_port(); |
2402 | 0 | else |
2403 | 0 | return -1; |
2404 | 0 | } |
2405 | | #endif |
2406 | | |
2407 | | #if TORRENT_USE_I2P |
2408 | | void session_impl::on_i2p_open(error_code const& ec) |
2409 | 0 | { |
2410 | 0 | if (ec) |
2411 | 0 | { |
2412 | 0 | if (m_alerts.should_post<i2p_alert>()) |
2413 | 0 | m_alerts.emplace_alert<i2p_alert>(ec); |
2414 | |
|
2415 | | #ifndef TORRENT_DISABLE_LOGGING |
2416 | | if (should_log()) |
2417 | | session_log("i2p open failed (%d) %s", ec.value(), ec.message().c_str()); |
2418 | | #endif |
2419 | 0 | } |
2420 | | // now that we have our i2p connection established |
2421 | | // it's OK to start torrents and use this socket to |
2422 | | // do i2p name lookups |
2423 | |
|
2424 | 0 | open_new_incoming_i2p_connection(); |
2425 | 0 | } |
2426 | | |
2427 | | void session_impl::open_new_incoming_i2p_connection() |
2428 | 1.83k | { |
2429 | 1.83k | if (m_abort) return; |
2430 | 1.83k | if (!m_i2p_conn.is_open()) return; |
2431 | | |
2432 | 0 | if (m_i2p_listen_socket) return; |
2433 | 0 | m_i2p_listen_socket.emplace( |
2434 | 0 | instantiate_connection(m_io_context, m_i2p_conn.proxy() |
2435 | 0 | , nullptr, nullptr, true, false)); |
2436 | |
|
2437 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::on_i2p_accept"); |
2438 | 0 | auto& s = boost::get<i2p_stream>(*m_i2p_listen_socket); |
2439 | 0 | s.set_command(i2p_stream::cmd_accept); |
2440 | 0 | s.set_session_id(m_i2p_conn.session_id()); |
2441 | |
|
2442 | 0 | s.async_connect(tcp::endpoint() |
2443 | 0 | , std::bind(&session_impl::on_i2p_accept, this, _1)); |
2444 | 0 | } |
2445 | | |
2446 | | void session_impl::on_i2p_accept(error_code const& e) |
2447 | 0 | { |
2448 | 0 | COMPLETE_ASYNC("session_impl::on_i2p_accept"); |
2449 | 0 | if (e == boost::asio::error::operation_aborted) return; |
2450 | 0 | if (e) |
2451 | 0 | { |
2452 | 0 | if (m_alerts.should_post<listen_failed_alert>()) |
2453 | 0 | { |
2454 | 0 | m_alerts.emplace_alert<listen_failed_alert>("i2p" |
2455 | 0 | , operation_t::sock_accept |
2456 | 0 | , e, socket_type_t::i2p); |
2457 | 0 | } |
2458 | | #ifndef TORRENT_DISABLE_LOGGING |
2459 | | if (should_log()) |
2460 | | session_log("i2p SAM connection failure: %s", e.message().c_str()); |
2461 | | #endif |
2462 | 0 | return; |
2463 | 0 | } |
2464 | 0 | incoming_connection(std::move(*m_i2p_listen_socket)); |
2465 | 0 | m_i2p_listen_socket.reset(); |
2466 | 0 | open_new_incoming_i2p_connection(); |
2467 | 0 | } |
2468 | | #endif |
2469 | | |
2470 | | void session_impl::send_udp_packet_hostname(std::weak_ptr<utp_socket_interface> sock |
2471 | | , char const* hostname |
2472 | | , int const port |
2473 | | , span<char const> p |
2474 | | , error_code& ec |
2475 | | , udp_send_flags_t const flags) |
2476 | 0 | { |
2477 | 0 | auto si = sock.lock(); |
2478 | 0 | if (!si) |
2479 | 0 | { |
2480 | 0 | ec = boost::asio::error::bad_descriptor; |
2481 | 0 | return; |
2482 | 0 | } |
2483 | | |
2484 | 0 | auto s = std::static_pointer_cast<aux::listen_socket_t>(si)->udp_sock; |
2485 | |
|
2486 | 0 | s->sock.send_hostname(hostname, port, p, ec, flags); |
2487 | |
|
2488 | 0 | if ((ec == error::would_block || ec == error::try_again) |
2489 | 0 | && !s->write_blocked) |
2490 | 0 | { |
2491 | 0 | s->write_blocked = true; |
2492 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::on_udp_writeable"); |
2493 | 0 | s->sock.async_write(std::bind(&session_impl::on_udp_writeable |
2494 | 0 | , this, s, _1)); |
2495 | 0 | } |
2496 | 0 | } |
2497 | | |
2498 | | void session_impl::send_udp_packet(std::weak_ptr<utp_socket_interface> sock |
2499 | | , udp::endpoint const& ep |
2500 | | , span<char const> p |
2501 | | , error_code& ec |
2502 | | , udp_send_flags_t const flags) |
2503 | 0 | { |
2504 | 0 | auto si = sock.lock(); |
2505 | 0 | if (!si) |
2506 | 0 | { |
2507 | 0 | ec = boost::asio::error::bad_descriptor; |
2508 | 0 | return; |
2509 | 0 | } |
2510 | | |
2511 | 0 | auto s = std::static_pointer_cast<aux::listen_socket_t>(si)->udp_sock; |
2512 | | |
2513 | | // the destination address family matching the local socket's address |
2514 | | // family does not hold for proxies that we talk to over IPv4 but can |
2515 | | // route to IPv6 |
2516 | 0 | TORRENT_ASSERT(s->sock.is_closed() |
2517 | 0 | || s->sock.get_proxy_settings().type != settings_pack::none |
2518 | 0 | || s->sock.local_endpoint().protocol() == ep.protocol()); |
2519 | |
|
2520 | 0 | s->sock.send(ep, p, ec, flags); |
2521 | |
|
2522 | 0 | if ((ec == error::would_block || ec == error::try_again) && !s->write_blocked) |
2523 | 0 | { |
2524 | 0 | s->write_blocked = true; |
2525 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::on_udp_writeable"); |
2526 | 0 | s->sock.async_write(std::bind(&session_impl::on_udp_writeable |
2527 | 0 | , this, s, _1)); |
2528 | 0 | } |
2529 | 0 | } |
2530 | | |
2531 | | void session_impl::on_udp_writeable(std::weak_ptr<session_udp_socket> sock, error_code const& ec) |
2532 | 0 | { |
2533 | 0 | COMPLETE_ASYNC("session_impl::on_udp_writeable"); |
2534 | 0 | if (ec) return; |
2535 | | |
2536 | 0 | auto s = sock.lock(); |
2537 | 0 | if (!s) return; |
2538 | | |
2539 | 0 | s->write_blocked = false; |
2540 | |
|
2541 | 0 | #ifdef TORRENT_SSL_PEERS |
2542 | 0 | auto i = std::find_if( |
2543 | 0 | m_listen_sockets.begin(), m_listen_sockets.end() |
2544 | 0 | , [&s] (std::shared_ptr<listen_socket_t> const& ls) { return ls->udp_sock == s; }); |
2545 | 0 | #endif |
2546 | | |
2547 | | // notify the utp socket manager it can start sending on the socket again |
2548 | 0 | struct utp_socket_manager& mgr = |
2549 | 0 | #ifdef TORRENT_SSL_PEERS |
2550 | 0 | (i != m_listen_sockets.end() && (*i)->ssl == transport::ssl) ? m_ssl_utp_socket_manager : |
2551 | 0 | #endif |
2552 | 0 | m_utp_socket_manager; |
2553 | |
|
2554 | 0 | mgr.writable(); |
2555 | 0 | } |
2556 | | |
2557 | | |
2558 | | void session_impl::on_udp_packet(std::weak_ptr<session_udp_socket> socket |
2559 | | , std::weak_ptr<listen_socket_t> ls, transport const ssl, error_code const& ec) |
2560 | 1.83k | { |
2561 | 1.83k | COMPLETE_ASYNC("session_impl::on_udp_packet"); |
2562 | 1.83k | if (ec) |
2563 | 1.83k | { |
2564 | 1.83k | std::shared_ptr<session_udp_socket> s = socket.lock(); |
2565 | 1.83k | udp::endpoint ep; |
2566 | 1.83k | if (s) ep = s->local_endpoint(); |
2567 | | |
2568 | | // don't bubble up operation aborted errors to the user |
2569 | 1.83k | if (ec != boost::asio::error::operation_aborted |
2570 | 1.83k | && ec != boost::asio::error::bad_descriptor |
2571 | 1.83k | && m_alerts.should_post<udp_error_alert>()) |
2572 | 0 | { |
2573 | 0 | m_alerts.emplace_alert<udp_error_alert>(ep |
2574 | 0 | , operation_t::sock_read, ec); |
2575 | 0 | } |
2576 | | |
2577 | | #ifndef TORRENT_DISABLE_LOGGING |
2578 | | if (should_log()) |
2579 | | { |
2580 | | session_log("UDP error: %s (%d) %s" |
2581 | | , print_endpoint(ep).c_str(), ec.value(), ec.message().c_str()); |
2582 | | } |
2583 | | #endif |
2584 | 1.83k | return; |
2585 | 1.83k | } |
2586 | | |
2587 | 0 | m_stats_counters.inc_stats_counter(counters::on_udp_counter); |
2588 | |
|
2589 | 0 | std::shared_ptr<session_udp_socket> s = socket.lock(); |
2590 | 0 | if (!s) return; |
2591 | | |
2592 | 0 | struct utp_socket_manager& mgr = |
2593 | 0 | #ifdef TORRENT_SSL_PEERS |
2594 | 0 | ssl == transport::ssl ? m_ssl_utp_socket_manager : |
2595 | 0 | #endif |
2596 | 0 | m_utp_socket_manager; |
2597 | |
|
2598 | 0 | for (;;) |
2599 | 0 | { |
2600 | 0 | aux::array<udp_socket::packet, 50> p; |
2601 | 0 | error_code err; |
2602 | 0 | int const num_packets = s->sock.read(p, err); |
2603 | |
|
2604 | 0 | for (udp_socket::packet& packet : span<udp_socket::packet>(p).first(num_packets)) |
2605 | 0 | { |
2606 | 0 | if (packet.error) |
2607 | 0 | { |
2608 | | // TODO: 3 it would be neat if the utp socket manager would |
2609 | | // handle ICMP errors too |
2610 | |
|
2611 | 0 | #ifndef TORRENT_DISABLE_DHT |
2612 | 0 | if (m_dht) |
2613 | 0 | m_dht->incoming_error(packet.error, packet.from); |
2614 | 0 | #endif |
2615 | |
|
2616 | 0 | m_tracker_manager.incoming_error(packet.error, packet.from); |
2617 | 0 | continue; |
2618 | 0 | } |
2619 | | |
2620 | 0 | span<char const> const buf = packet.data; |
2621 | 0 | if (!packet.hostname.empty()) |
2622 | 0 | { |
2623 | | // only the tracker manager supports receiving UDP packets |
2624 | | // from hostnames. If it won't handle it, no one else will |
2625 | | // either |
2626 | 0 | m_tracker_manager.incoming_packet(packet.hostname, buf); |
2627 | 0 | continue; |
2628 | 0 | } |
2629 | | |
2630 | | // give the uTP socket manager first dibs on the packet. Presumably |
2631 | | // the majority of packets are uTP packets. |
2632 | 0 | if (!mgr.incoming_packet(ls, packet.from, buf)) |
2633 | 0 | { |
2634 | | // if it wasn't a uTP packet, try the other users of the UDP |
2635 | | // socket |
2636 | 0 | bool handled = false; |
2637 | 0 | #ifndef TORRENT_DISABLE_DHT |
2638 | 0 | auto listen_socket = ls.lock(); |
2639 | 0 | if (m_dht && buf.size() > 20 |
2640 | 0 | && buf.front() == 'd' |
2641 | 0 | && buf.back() == 'e' |
2642 | 0 | && listen_socket) |
2643 | 0 | { |
2644 | 0 | handled = m_dht->incoming_packet(listen_socket, packet.from, buf); |
2645 | 0 | } |
2646 | 0 | #endif |
2647 | |
|
2648 | 0 | if (!handled) |
2649 | 0 | { |
2650 | 0 | m_tracker_manager.incoming_packet(packet.from, buf); |
2651 | 0 | } |
2652 | 0 | } |
2653 | 0 | } |
2654 | |
|
2655 | 0 | if (err == error::would_block || err == error::try_again) |
2656 | 0 | { |
2657 | | // there are no more packets on the socket |
2658 | 0 | break; |
2659 | 0 | } |
2660 | | |
2661 | 0 | if (err) |
2662 | 0 | { |
2663 | 0 | udp::endpoint const ep = s->local_endpoint(); |
2664 | |
|
2665 | 0 | if (err != boost::asio::error::operation_aborted |
2666 | 0 | && m_alerts.should_post<udp_error_alert>()) |
2667 | 0 | m_alerts.emplace_alert<udp_error_alert>(ep |
2668 | 0 | , operation_t::sock_read, err); |
2669 | |
|
2670 | | #ifndef TORRENT_DISABLE_LOGGING |
2671 | | if (should_log()) |
2672 | | { |
2673 | | session_log("UDP error: %s (%d) %s" |
2674 | | , print_endpoint(ep).c_str(), ec.value(), ec.message().c_str()); |
2675 | | } |
2676 | | #endif |
2677 | | |
2678 | | // any error other than these ones are considered fatal errors, and |
2679 | | // we won't read from the socket again |
2680 | 0 | if (err != boost::asio::error::host_unreachable |
2681 | 0 | && err != boost::asio::error::fault |
2682 | 0 | && err != boost::asio::error::connection_reset |
2683 | 0 | && err != boost::asio::error::connection_refused |
2684 | 0 | && err != boost::asio::error::connection_aborted |
2685 | 0 | && err != boost::asio::error::operation_aborted |
2686 | 0 | && err != boost::asio::error::network_reset |
2687 | 0 | && err != boost::asio::error::network_unreachable |
2688 | | #ifdef _WIN32 |
2689 | | // ERROR_MORE_DATA means the same thing as EMSGSIZE |
2690 | | && err != error_code(ERROR_MORE_DATA, system_category()) |
2691 | | && err != error_code(ERROR_HOST_UNREACHABLE, system_category()) |
2692 | | && err != error_code(ERROR_PORT_UNREACHABLE, system_category()) |
2693 | | && err != error_code(ERROR_RETRY, system_category()) |
2694 | | && err != error_code(ERROR_NETWORK_UNREACHABLE, system_category()) |
2695 | | && err != error_code(ERROR_CONNECTION_REFUSED, system_category()) |
2696 | | && err != error_code(ERROR_CONNECTION_ABORTED, system_category()) |
2697 | | #endif |
2698 | 0 | && err != boost::asio::error::message_size) |
2699 | 0 | { |
2700 | | // fatal errors. Don't try to read from this socket again |
2701 | 0 | mgr.socket_drained(); |
2702 | 0 | return; |
2703 | 0 | } |
2704 | | // non-fatal UDP errors get here, we should re-issue the read. |
2705 | 0 | continue; |
2706 | 0 | } |
2707 | 0 | } |
2708 | | |
2709 | 0 | mgr.socket_drained(); |
2710 | |
|
2711 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::on_udp_packet"); |
2712 | 0 | s->sock.async_read(make_handler([this, socket, ls, ssl](error_code const& e) |
2713 | 0 | { this->on_udp_packet(std::move(socket), std::move(ls), ssl, e); } |
2714 | 0 | , s->udp_handler_storage, *this)); |
2715 | 0 | } |
2716 | | |
2717 | | void session_impl::async_accept(std::shared_ptr<tcp::acceptor> const& listener |
2718 | | , transport const ssl) |
2719 | | #ifndef BOOST_NO_EXCEPTIONS |
2720 | 3.67k | try |
2721 | 3.67k | #endif |
2722 | 3.67k | { |
2723 | 3.67k | TORRENT_ASSERT(!m_abort); |
2724 | | |
2725 | 3.67k | std::weak_ptr<tcp::acceptor> ls(listener); |
2726 | 3.67k | m_stats_counters.inc_stats_counter(counters::num_outstanding_accept); |
2727 | 3.67k | ADD_OUTSTANDING_ASYNC("session_impl::on_accept_connection"); |
2728 | 3.67k | listener->async_accept([this, ls, ssl] (error_code const& ec, true_tcp_socket s) |
2729 | 3.67k | { return wrap(&session_impl::on_accept_connection, std::move(s), ec, ls, ssl); }); |
2730 | 3.67k | } |
2731 | 3.67k | #ifndef BOOST_NO_EXCEPTIONS |
2732 | 3.67k | catch (system_error const& e) { |
2733 | 0 | alerts().emplace_alert<session_error_alert>(e.code(), e.what()); |
2734 | 0 | pause(); |
2735 | 0 | } catch (std::exception const& e) { |
2736 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), e.what()); |
2737 | 0 | pause(); |
2738 | 0 | } catch (...) { |
2739 | 0 | alerts().emplace_alert<session_error_alert>(error_code(), "unknown error"); |
2740 | 0 | pause(); |
2741 | 0 | } |
2742 | | #endif |
2743 | | |
2744 | | void session_impl::on_accept_connection(true_tcp_socket s, error_code const& e |
2745 | | , std::weak_ptr<tcp::acceptor> listen_socket, transport const ssl) |
2746 | 1.83k | { |
2747 | 1.83k | COMPLETE_ASYNC("session_impl::on_accept_connection"); |
2748 | 1.83k | m_stats_counters.inc_stats_counter(counters::on_accept_counter); |
2749 | 1.83k | m_stats_counters.inc_stats_counter(counters::num_outstanding_accept, -1); |
2750 | | |
2751 | 1.83k | TORRENT_ASSERT(is_single_thread()); |
2752 | 1.83k | std::shared_ptr<tcp::acceptor> listener = listen_socket.lock(); |
2753 | 1.83k | if (!listener) return; |
2754 | | |
2755 | 1.83k | if (e == boost::asio::error::operation_aborted) return; |
2756 | | |
2757 | 0 | if (m_abort) return; |
2758 | | |
2759 | 0 | error_code ec; |
2760 | 0 | if (e) |
2761 | 0 | { |
2762 | 0 | tcp::endpoint const ep = listener->local_endpoint(ec); |
2763 | | #ifndef TORRENT_DISABLE_LOGGING |
2764 | | if (should_log()) |
2765 | | { |
2766 | | session_log("error accepting connection on '%s': %s" |
2767 | | , print_endpoint(ep).c_str(), e.message().c_str()); |
2768 | | } |
2769 | | #endif |
2770 | | #ifdef TORRENT_WINDOWS |
2771 | | // Windows sometimes generates this error. It seems to be |
2772 | | // non-fatal and we have to do another async_accept. |
2773 | | if (e.value() == ERROR_SEM_TIMEOUT) |
2774 | | { |
2775 | | async_accept(listener, ssl); |
2776 | | return; |
2777 | | } |
2778 | | #endif |
2779 | | #ifdef TORRENT_BSD |
2780 | | // Leopard sometimes generates an "invalid argument" error. It seems to be |
2781 | | // non-fatal and we have to do another async_accept. |
2782 | | if (e.value() == EINVAL) |
2783 | | { |
2784 | | async_accept(listener, ssl); |
2785 | | return; |
2786 | | } |
2787 | | #endif |
2788 | 0 | if (e == boost::system::errc::too_many_files_open) |
2789 | 0 | { |
2790 | | // if we failed to accept an incoming connection |
2791 | | // because we have too many files open, try again |
2792 | | // and lower the number of file descriptors used |
2793 | | // elsewhere. |
2794 | 0 | if (m_settings.get_int(settings_pack::connections_limit) > 10) |
2795 | 0 | { |
2796 | | // now, disconnect a random peer |
2797 | 0 | auto const i = std::max_element(m_torrents.begin(), m_torrents.end() |
2798 | 0 | , [](std::shared_ptr<torrent> const& lhs, std::shared_ptr<torrent> const& rhs) |
2799 | 0 | { return lhs->num_peers() < rhs->num_peers(); }); |
2800 | |
|
2801 | 0 | if (m_alerts.should_post<performance_alert>()) |
2802 | 0 | m_alerts.emplace_alert<performance_alert>( |
2803 | 0 | torrent_handle(), performance_alert::too_few_file_descriptors); |
2804 | |
|
2805 | 0 | if (i != m_torrents.end()) |
2806 | 0 | { |
2807 | 0 | (*i)->disconnect_peers(1, e); |
2808 | 0 | } |
2809 | |
|
2810 | 0 | m_settings.set_int(settings_pack::connections_limit |
2811 | 0 | , std::max(10, int(m_connections.size()))); |
2812 | 0 | } |
2813 | | // try again, but still alert the user of the problem |
2814 | 0 | async_accept(listener, ssl); |
2815 | 0 | } |
2816 | 0 | if (m_alerts.should_post<listen_failed_alert>()) |
2817 | 0 | { |
2818 | 0 | m_alerts.emplace_alert<listen_failed_alert>(ep.address().to_string() |
2819 | 0 | , ep, operation_t::sock_accept, e |
2820 | 0 | , ssl == transport::ssl ? socket_type_t::tcp_ssl : socket_type_t::tcp); |
2821 | 0 | } |
2822 | 0 | return; |
2823 | 0 | } |
2824 | 0 | async_accept(listener, ssl); |
2825 | | |
2826 | | // don't accept any connections from our local listen sockets if we're |
2827 | | // using a proxy. We should only accept peers via the proxy, never |
2828 | | // directly. |
2829 | | // This path is only for accepting incoming TCP sockets. The udp_socket |
2830 | | // class also restricts incoming packets based on proxy settings. |
2831 | 0 | if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none |
2832 | 0 | && m_settings.get_bool(settings_pack::proxy_peer_connections)) |
2833 | 0 | return; |
2834 | | |
2835 | 0 | auto listen = std::find_if(m_listen_sockets.begin(), m_listen_sockets.end() |
2836 | 0 | , [&listener](std::shared_ptr<listen_socket_t> const& l) |
2837 | 0 | { return l->sock == listener; }); |
2838 | 0 | if (listen != m_listen_sockets.end()) |
2839 | 0 | (*listen)->incoming_connection = true; |
2840 | |
|
2841 | 0 | socket_type c = [&]{ |
2842 | 0 | #ifdef TORRENT_SSL_PEERS |
2843 | 0 | if (ssl == transport::ssl) |
2844 | 0 | { |
2845 | | // accept connections initializing the SSL connection to use the peer |
2846 | | // ssl context. Since it has the servername callback set on it, we will |
2847 | | // switch away from this context into a specific torrent once we start |
2848 | | // handshaking |
2849 | 0 | return socket_type(ssl_stream<tcp::socket>(tcp::socket(std::move(s)), m_peer_ssl_ctx)); |
2850 | 0 | } |
2851 | 0 | else |
2852 | 0 | #endif |
2853 | 0 | { |
2854 | 0 | return socket_type(tcp::socket(std::move(s))); |
2855 | 0 | } |
2856 | 0 | }(); |
2857 | |
|
2858 | 0 | #ifdef TORRENT_SSL_PEERS |
2859 | 0 | TORRENT_ASSERT((ssl == transport::ssl) == is_ssl(c)); |
2860 | 0 | #endif |
2861 | |
|
2862 | 0 | #ifdef TORRENT_SSL_PEERS |
2863 | 0 | if (ssl == transport::ssl) |
2864 | 0 | { |
2865 | 0 | TORRENT_ASSERT(is_ssl(c)); |
2866 | | |
2867 | | // save the socket so we can cancel the handshake |
2868 | | // TODO: this size need to be capped |
2869 | 0 | auto iter = m_incoming_sockets.emplace(std::make_unique<socket_type>(std::move(c))).first; |
2870 | |
|
2871 | 0 | auto sock = iter->get(); |
2872 | | // for SSL connections, incoming_connection() is called |
2873 | | // after the handshake is done |
2874 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::ssl_handshake"); |
2875 | 0 | boost::get<ssl_stream<tcp::socket>>(**iter).async_accept_handshake( |
2876 | 0 | [this, sock] (error_code const& err) { ssl_handshake(err, sock); }); |
2877 | 0 | } |
2878 | 0 | else |
2879 | 0 | #endif |
2880 | 0 | { |
2881 | 0 | incoming_connection(std::move(c)); |
2882 | 0 | } |
2883 | 0 | } |
2884 | | |
2885 | | #ifdef TORRENT_SSL_PEERS |
2886 | | |
2887 | | void session_impl::on_incoming_utp_ssl(socket_type s) |
2888 | 0 | { |
2889 | 0 | TORRENT_ASSERT(is_ssl(s)); |
2890 | | |
2891 | | // save the socket so we can cancel the handshake |
2892 | | |
2893 | | // TODO: this size need to be capped |
2894 | 0 | auto iter = m_incoming_sockets.emplace(std::make_unique<socket_type>(std::move(s))).first; |
2895 | 0 | auto sock = iter->get(); |
2896 | | |
2897 | | // for SSL connections, incoming_connection() is called |
2898 | | // after the handshake is done |
2899 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::ssl_handshake"); |
2900 | 0 | boost::get<ssl_stream<utp_stream>>(**iter).async_accept_handshake( |
2901 | 0 | [this, sock] (error_code const& err) { ssl_handshake(err, sock); }); |
2902 | 0 | } |
2903 | | |
2904 | | // to test SSL connections, one can use this openssl command template: |
2905 | | // |
2906 | | // openssl s_client -cert <client-cert>.pem -key <client-private-key>.pem |
2907 | | // -CAfile <torrent-cert>.pem -debug -connect 127.0.0.1:4433 -tls1 |
2908 | | // -servername <hex-encoded-info-hash> |
2909 | | |
2910 | | void session_impl::ssl_handshake(error_code const& ec, socket_type* sock) |
2911 | 0 | { |
2912 | 0 | COMPLETE_ASYNC("session_impl::ssl_handshake"); |
2913 | |
|
2914 | 0 | auto iter = m_incoming_sockets.find(sock); |
2915 | | |
2916 | | // this happens if the SSL connection is aborted because we're shutting |
2917 | | // down |
2918 | 0 | if (iter == m_incoming_sockets.end()) return; |
2919 | | |
2920 | 0 | socket_type s(std::move(**iter)); |
2921 | 0 | TORRENT_ASSERT(is_ssl(s)); |
2922 | 0 | m_incoming_sockets.erase(iter); |
2923 | |
|
2924 | 0 | error_code e; |
2925 | 0 | tcp::endpoint endp = s.remote_endpoint(e); |
2926 | 0 | if (e) return; |
2927 | | |
2928 | | #ifndef TORRENT_DISABLE_LOGGING |
2929 | | if (should_log()) |
2930 | | { |
2931 | | session_log(" *** peer SSL handshake done [ ip: %s ec: %s socket: %s ]" |
2932 | | , print_endpoint(endp).c_str(), ec.message().c_str(), socket_type_name(s)); |
2933 | | } |
2934 | | #endif |
2935 | | |
2936 | 0 | if (ec) |
2937 | 0 | { |
2938 | 0 | if (m_alerts.should_post<peer_error_alert>()) |
2939 | 0 | { |
2940 | 0 | m_alerts.emplace_alert<peer_error_alert>(torrent_handle(), endp |
2941 | 0 | , peer_id(), operation_t::ssl_handshake, ec); |
2942 | 0 | } |
2943 | 0 | return; |
2944 | 0 | } |
2945 | | |
2946 | 0 | incoming_connection(std::move(s)); |
2947 | 0 | } |
2948 | | |
2949 | | #endif // TORRENT_SSL_PEERS |
2950 | | |
2951 | | void session_impl::incoming_connection(socket_type s) |
2952 | 0 | { |
2953 | 0 | TORRENT_ASSERT(is_single_thread()); |
2954 | |
|
2955 | 0 | if (m_abort) |
2956 | 0 | { |
2957 | | #ifndef TORRENT_DISABLE_LOGGING |
2958 | | session_log(" <== INCOMING CONNECTION [ ignored, aborting ]"); |
2959 | | #endif |
2960 | 0 | return; |
2961 | 0 | } |
2962 | | |
2963 | 0 | if (m_paused) |
2964 | 0 | { |
2965 | | #ifndef TORRENT_DISABLE_LOGGING |
2966 | | session_log(" <== INCOMING CONNECTION [ ignored, paused ]"); |
2967 | | #endif |
2968 | 0 | return; |
2969 | 0 | } |
2970 | | |
2971 | 0 | error_code ec; |
2972 | | // we got a connection request! |
2973 | 0 | tcp::endpoint endp = s.remote_endpoint(ec); |
2974 | |
|
2975 | 0 | if (ec) |
2976 | 0 | { |
2977 | | #ifndef TORRENT_DISABLE_LOGGING |
2978 | | if (should_log()) |
2979 | | { |
2980 | | session_log(" <== INCOMING CONNECTION [ rejected, could " |
2981 | | "not retrieve remote endpoint: %s ]" |
2982 | | , print_error(ec).c_str()); |
2983 | | } |
2984 | | #endif |
2985 | 0 | return; |
2986 | 0 | } |
2987 | | |
2988 | 0 | if (!m_settings.get_bool(settings_pack::enable_incoming_utp) |
2989 | 0 | && is_utp(s)) |
2990 | 0 | { |
2991 | | #ifndef TORRENT_DISABLE_LOGGING |
2992 | | session_log("<== INCOMING CONNECTION [ rejected uTP connection ]"); |
2993 | | #endif |
2994 | 0 | if (m_alerts.should_post<peer_blocked_alert>()) |
2995 | 0 | m_alerts.emplace_alert<peer_blocked_alert>(torrent_handle() |
2996 | 0 | , endp, peer_blocked_alert::utp_disabled); |
2997 | 0 | return; |
2998 | 0 | } |
2999 | | |
3000 | 0 | if (!m_settings.get_bool(settings_pack::enable_incoming_tcp) |
3001 | 0 | && boost::get<tcp::socket>(&s)) |
3002 | 0 | { |
3003 | | #ifndef TORRENT_DISABLE_LOGGING |
3004 | | session_log("<== INCOMING CONNECTION [ rejected TCP connection ]"); |
3005 | | #endif |
3006 | 0 | if (m_alerts.should_post<peer_blocked_alert>()) |
3007 | 0 | m_alerts.emplace_alert<peer_blocked_alert>(torrent_handle() |
3008 | 0 | , endp, peer_blocked_alert::tcp_disabled); |
3009 | 0 | return; |
3010 | 0 | } |
3011 | | |
3012 | | // if there are outgoing interfaces specified, verify this |
3013 | | // peer is correctly bound to one of them |
3014 | 0 | if (!m_outgoing_interfaces.empty()) |
3015 | 0 | { |
3016 | 0 | tcp::endpoint local = s.local_endpoint(ec); |
3017 | 0 | if (ec) |
3018 | 0 | { |
3019 | | #ifndef TORRENT_DISABLE_LOGGING |
3020 | | if (should_log()) |
3021 | | { |
3022 | | session_log("<== INCOMING CONNECTION [ rejected connection: %s ]" |
3023 | | , print_error(ec).c_str()); |
3024 | | } |
3025 | | #endif |
3026 | 0 | return; |
3027 | 0 | } |
3028 | | |
3029 | 0 | if (!verify_incoming_interface(local.address())) |
3030 | 0 | { |
3031 | | #ifndef TORRENT_DISABLE_LOGGING |
3032 | | if (should_log()) |
3033 | | { |
3034 | | session_log("<== INCOMING CONNECTION [ rejected, local interface has incoming connections disabled: %s ]" |
3035 | | , local.address().to_string().c_str()); |
3036 | | } |
3037 | | #endif |
3038 | 0 | if (m_alerts.should_post<peer_blocked_alert>()) |
3039 | 0 | m_alerts.emplace_alert<peer_blocked_alert>(torrent_handle() |
3040 | 0 | , endp, peer_blocked_alert::invalid_local_interface); |
3041 | 0 | return; |
3042 | 0 | } |
3043 | 0 | if (!verify_bound_address(local.address(), is_utp(s), ec)) |
3044 | 0 | { |
3045 | 0 | if (ec) |
3046 | 0 | { |
3047 | | #ifndef TORRENT_DISABLE_LOGGING |
3048 | | if (should_log()) |
3049 | | { |
3050 | | session_log("<== INCOMING CONNECTION [ rejected, not allowed local interface: %s ]" |
3051 | | , print_error(ec).c_str()); |
3052 | | } |
3053 | | #endif |
3054 | 0 | return; |
3055 | 0 | } |
3056 | | |
3057 | | #ifndef TORRENT_DISABLE_LOGGING |
3058 | | if (should_log()) |
3059 | | { |
3060 | | session_log("<== INCOMING CONNECTION [ rejected, not allowed local interface: %s ]" |
3061 | | , local.address().to_string().c_str()); |
3062 | | } |
3063 | | #endif |
3064 | 0 | if (m_alerts.should_post<peer_blocked_alert>()) |
3065 | 0 | m_alerts.emplace_alert<peer_blocked_alert>(torrent_handle() |
3066 | 0 | , endp, peer_blocked_alert::invalid_local_interface); |
3067 | 0 | return; |
3068 | 0 | } |
3069 | 0 | } |
3070 | | |
3071 | | // local addresses do not count, since it's likely |
3072 | | // coming from our own client through local service discovery |
3073 | | // and it does not reflect whether or not a router is open |
3074 | | // for incoming connections or not. |
3075 | 0 | if (!is_local(endp.address())) |
3076 | 0 | m_stats_counters.set_value(counters::has_incoming_connections, 1); |
3077 | | |
3078 | | // this filter is ignored if a single torrent |
3079 | | // is set to ignore the filter, since this peer might be |
3080 | | // for that torrent |
3081 | 0 | if (m_stats_counters[counters::non_filter_torrents] == 0 |
3082 | 0 | && m_ip_filter |
3083 | 0 | && (m_ip_filter->access(endp.address()) & ip_filter::blocked)) |
3084 | 0 | { |
3085 | | #ifndef TORRENT_DISABLE_LOGGING |
3086 | | session_log("<== INCOMING CONNECTION [ filtered blocked ip ]"); |
3087 | | #endif |
3088 | 0 | if (m_alerts.should_post<peer_blocked_alert>()) |
3089 | 0 | m_alerts.emplace_alert<peer_blocked_alert>(torrent_handle() |
3090 | 0 | , endp, peer_blocked_alert::ip_filter); |
3091 | 0 | return; |
3092 | 0 | } |
3093 | | |
3094 | 0 | bool want_on_unknown_torrent = false; |
3095 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
3096 | 0 | want_on_unknown_torrent = !m_ses_extensions[plugins_unknown_torrent_idx].empty(); |
3097 | 0 | #endif |
3098 | | |
3099 | | // check if we have any active torrents |
3100 | | // or if there is an extension that wants on_unknown_torrent |
3101 | | // if we don't reject the connection |
3102 | 0 | if (m_torrents.empty() && !want_on_unknown_torrent) |
3103 | 0 | { |
3104 | | #ifndef TORRENT_DISABLE_LOGGING |
3105 | | session_log("<== INCOMING CONNECTION [ rejected, there are no torrents ]"); |
3106 | | #endif |
3107 | 0 | return; |
3108 | 0 | } |
3109 | | |
3110 | | // figure out which peer classes this is connections has, |
3111 | | // to get connection_limit_factor |
3112 | 0 | peer_class_set pcs; |
3113 | 0 | set_peer_classes(&pcs, endp.address(), socket_type_idx(s)); |
3114 | 0 | int connection_limit_factor = 0; |
3115 | 0 | for (int i = 0; i < pcs.num_classes(); ++i) |
3116 | 0 | { |
3117 | 0 | peer_class_t pc = pcs.class_at(i); |
3118 | 0 | if (m_classes.at(pc) == nullptr) continue; |
3119 | 0 | int f = m_classes.at(pc)->connection_limit_factor; |
3120 | 0 | if (connection_limit_factor < f) connection_limit_factor = f; |
3121 | 0 | } |
3122 | 0 | if (connection_limit_factor == 0) connection_limit_factor = 100; |
3123 | |
|
3124 | 0 | std::int64_t limit = m_settings.get_int(settings_pack::connections_limit); |
3125 | 0 | limit = limit * 100 / connection_limit_factor; |
3126 | | |
3127 | | // don't allow more connections than the max setting |
3128 | | // weighed by the peer class' setting |
3129 | 0 | bool reject = num_connections() >= limit + m_settings.get_int(settings_pack::connections_slack); |
3130 | |
|
3131 | 0 | if (reject) |
3132 | 0 | { |
3133 | 0 | if (m_alerts.should_post<peer_disconnected_alert>()) |
3134 | 0 | { |
3135 | 0 | m_alerts.emplace_alert<peer_disconnected_alert>(torrent_handle(), endp, peer_id() |
3136 | 0 | , operation_t::bittorrent, socket_type_idx(s) |
3137 | 0 | , error_code(errors::too_many_connections) |
3138 | 0 | , close_reason_t::none); |
3139 | 0 | } |
3140 | | #ifndef TORRENT_DISABLE_LOGGING |
3141 | | if (should_log()) |
3142 | | { |
3143 | | session_log("<== INCOMING CONNECTION [ connections limit exceeded, conns: %d, limit: %d, slack: %d ]" |
3144 | | , num_connections(), m_settings.get_int(settings_pack::connections_limit) |
3145 | | , m_settings.get_int(settings_pack::connections_slack)); |
3146 | | } |
3147 | | #endif |
3148 | 0 | return; |
3149 | 0 | } |
3150 | | |
3151 | | // if we don't have any active torrents, there's no |
3152 | | // point in accepting this connection. If, however, |
3153 | | // the setting to start up queued torrents when they |
3154 | | // get an incoming connection is enabled or if there is |
3155 | | // an extension that wants on_unknown_torrent, we cannot |
3156 | | // perform this check. |
3157 | 0 | if (!m_settings.get_bool(settings_pack::incoming_starts_queued_torrents) && !want_on_unknown_torrent) |
3158 | 0 | { |
3159 | 0 | bool has_active_torrent = std::any_of(m_torrents.begin(), m_torrents.end() |
3160 | 0 | , [](std::shared_ptr<torrent> const& i) |
3161 | 0 | { return !i->is_torrent_paused(); }); |
3162 | 0 | if (!has_active_torrent) |
3163 | 0 | { |
3164 | | #ifndef TORRENT_DISABLE_LOGGING |
3165 | | session_log("<== INCOMING CONNECTION [ rejected, no active torrents ]"); |
3166 | | #endif |
3167 | 0 | return; |
3168 | 0 | } |
3169 | 0 | } |
3170 | | |
3171 | 0 | m_stats_counters.inc_stats_counter(counters::incoming_connections); |
3172 | |
|
3173 | 0 | if (m_alerts.should_post<incoming_connection_alert>()) |
3174 | 0 | m_alerts.emplace_alert<incoming_connection_alert>(socket_type_idx(s), endp); |
3175 | |
|
3176 | 0 | peer_connection_args pack{ |
3177 | 0 | this |
3178 | 0 | , &m_settings |
3179 | 0 | , &m_stats_counters |
3180 | 0 | , m_disk_thread.get() |
3181 | 0 | , &m_io_context |
3182 | 0 | , std::weak_ptr<torrent>() |
3183 | 0 | , std::move(s) |
3184 | 0 | , endp |
3185 | 0 | , nullptr |
3186 | 0 | , aux::generate_peer_id(m_settings) |
3187 | 0 | }; |
3188 | |
|
3189 | 0 | std::shared_ptr<peer_connection> c |
3190 | 0 | = std::make_shared<bt_peer_connection>(pack); |
3191 | |
|
3192 | 0 | if (!c->is_disconnecting()) |
3193 | 0 | { |
3194 | | // in case we've exceeded the limit, let this peer know that |
3195 | | // as soon as it's received the handshake, it needs to either |
3196 | | // disconnect or pick another peer to disconnect |
3197 | 0 | if (num_connections() >= limit) |
3198 | 0 | c->peer_exceeds_limit(); |
3199 | |
|
3200 | 0 | TORRENT_ASSERT(!c->m_in_constructor); |
3201 | | // removing a peer may not throw an exception, so prepare for this |
3202 | | // connection to be added to the undead peers now. |
3203 | 0 | m_undead_peers.reserve(m_undead_peers.size() + m_connections.size() + 1); |
3204 | 0 | m_connections.insert(c); |
3205 | 0 | c->start(); |
3206 | 0 | } |
3207 | 0 | } |
3208 | | |
3209 | | void session_impl::close_connection(peer_connection* p) noexcept |
3210 | 0 | { |
3211 | 0 | TORRENT_ASSERT(is_single_thread()); |
3212 | 0 | std::shared_ptr<peer_connection> sp(p->self()); |
3213 | |
|
3214 | 0 | TORRENT_ASSERT(p->is_disconnecting()); |
3215 | |
|
3216 | 0 | auto const i = m_connections.find(sp); |
3217 | | // make sure the next disk peer round-robin cursor stays valid |
3218 | 0 | if (i != m_connections.end()) |
3219 | 0 | { |
3220 | 0 | m_connections.erase(i); |
3221 | |
|
3222 | 0 | TORRENT_ASSERT(std::find(m_undead_peers.begin() |
3223 | 0 | , m_undead_peers.end(), sp) == m_undead_peers.end()); |
3224 | | |
3225 | | // someone else is holding a reference, it's important that |
3226 | | // it's destructed from the network thread. Make sure the |
3227 | | // last reference is held by the network thread. |
3228 | 0 | TORRENT_ASSERT_VAL(m_undead_peers.capacity() > m_undead_peers.size() |
3229 | 0 | , m_undead_peers.capacity()); |
3230 | 0 | if (sp.use_count() > 2) |
3231 | 0 | m_undead_peers.push_back(sp); |
3232 | 0 | } |
3233 | 0 | } |
3234 | | |
3235 | | #if TORRENT_ABI_VERSION == 1 |
3236 | | peer_id session_impl::deprecated_get_peer_id() const |
3237 | 0 | { |
3238 | 0 | return aux::generate_peer_id(m_settings); |
3239 | 0 | } |
3240 | | #endif |
3241 | | |
3242 | | int session_impl::next_port() const |
3243 | 0 | { |
3244 | 0 | int start = m_settings.get_int(settings_pack::outgoing_port); |
3245 | 0 | int num = m_settings.get_int(settings_pack::num_outgoing_ports); |
3246 | 0 | std::pair<int, int> out_ports(start, start + num); |
3247 | 0 | if (m_next_port < out_ports.first || m_next_port > out_ports.second) |
3248 | 0 | m_next_port = out_ports.first; |
3249 | |
|
3250 | 0 | int port = m_next_port; |
3251 | 0 | ++m_next_port; |
3252 | 0 | if (m_next_port > out_ports.second) m_next_port = out_ports.first; |
3253 | | #ifndef TORRENT_DISABLE_LOGGING |
3254 | | session_log(" *** BINDING OUTGOING CONNECTION [ port: %d ]", port); |
3255 | | #endif |
3256 | 0 | return port; |
3257 | 0 | } |
3258 | | |
3259 | | int session_impl::rate_limit(peer_class_t c, int channel) const |
3260 | 2 | { |
3261 | 2 | TORRENT_ASSERT(channel >= 0 && channel <= 1); |
3262 | 2 | if (channel < 0 || channel > 1) return 0; |
3263 | | |
3264 | 2 | peer_class const* pc = m_classes.at(c); |
3265 | 2 | if (pc == nullptr) return 0; |
3266 | 2 | return pc->channel[channel].throttle(); |
3267 | 2 | } |
3268 | | |
3269 | | int session_impl::upload_rate_limit(peer_class_t c) const |
3270 | 1 | { |
3271 | 1 | return rate_limit(c, peer_connection::upload_channel); |
3272 | 1 | } |
3273 | | |
3274 | | int session_impl::download_rate_limit(peer_class_t c) const |
3275 | 1 | { |
3276 | 1 | return rate_limit(c, peer_connection::download_channel); |
3277 | 1 | } |
3278 | | |
3279 | | void session_impl::set_rate_limit(peer_class_t c, int channel, int limit) |
3280 | 7.34k | { |
3281 | 7.34k | TORRENT_ASSERT(is_single_thread()); |
3282 | 7.34k | TORRENT_ASSERT(limit >= -1); |
3283 | 7.34k | TORRENT_ASSERT(channel >= 0 && channel <= 1); |
3284 | | |
3285 | 7.34k | if (channel < 0 || channel > 1) return; |
3286 | | |
3287 | 7.34k | peer_class* pc = m_classes.at(c); |
3288 | 7.34k | if (pc == nullptr) return; |
3289 | 7.34k | if (limit <= 0) limit = 0; |
3290 | 0 | else limit = std::min(limit, std::numeric_limits<int>::max() - 1); |
3291 | 7.34k | pc->channel[channel].throttle(limit); |
3292 | 7.34k | } |
3293 | | |
3294 | | void session_impl::set_upload_rate_limit(peer_class_t c, int limit) |
3295 | 3.67k | { |
3296 | 3.67k | set_rate_limit(c, peer_connection::upload_channel, limit); |
3297 | 3.67k | } |
3298 | | |
3299 | | void session_impl::set_download_rate_limit(peer_class_t c, int limit) |
3300 | 3.67k | { |
3301 | 3.67k | set_rate_limit(c, peer_connection::download_channel, limit); |
3302 | 3.67k | } |
3303 | | |
3304 | | #if TORRENT_USE_ASSERTS |
3305 | | bool session_impl::has_peer(peer_connection const* p) const |
3306 | 0 | { |
3307 | 0 | TORRENT_ASSERT(is_single_thread()); |
3308 | 0 | return std::any_of(m_connections.begin(), m_connections.end() |
3309 | 0 | , [p] (std::shared_ptr<peer_connection> const& pr) |
3310 | 0 | { return pr.get() == p; }); |
3311 | 0 | } |
3312 | | |
3313 | | bool session_impl::any_torrent_has_peer(peer_connection const* p) const |
3314 | 0 | { |
3315 | 0 | for (auto& pe : m_torrents) |
3316 | 0 | if (pe->has_peer(p)) return true; |
3317 | 0 | return false; |
3318 | 0 | } |
3319 | | |
3320 | | bool session_impl::verify_queue_position(torrent const* t, queue_position_t const pos) |
3321 | 0 | { |
3322 | 0 | return m_download_queue.end_index() > pos && m_download_queue[pos] == t; |
3323 | 0 | } |
3324 | | #endif |
3325 | | |
3326 | | void session_impl::sent_bytes(int bytes_payload, int bytes_protocol) |
3327 | 0 | { |
3328 | 0 | TORRENT_ASSERT(bytes_payload >= 0); |
3329 | 0 | TORRENT_ASSERT(bytes_protocol >= 0); |
3330 | 0 | m_stats_counters.inc_stats_counter(counters::sent_bytes |
3331 | 0 | , bytes_payload + bytes_protocol); |
3332 | 0 | m_stats_counters.inc_stats_counter(counters::sent_payload_bytes |
3333 | 0 | , bytes_payload); |
3334 | |
|
3335 | 0 | m_stat.sent_bytes(bytes_payload, bytes_protocol); |
3336 | 0 | } |
3337 | | |
3338 | | void session_impl::received_bytes(int bytes_payload, int bytes_protocol) |
3339 | 0 | { |
3340 | 0 | TORRENT_ASSERT(bytes_payload >= 0); |
3341 | 0 | TORRENT_ASSERT(bytes_protocol >= 0); |
3342 | 0 | m_stats_counters.inc_stats_counter(counters::recv_bytes |
3343 | 0 | , bytes_payload + bytes_protocol); |
3344 | 0 | m_stats_counters.inc_stats_counter(counters::recv_payload_bytes |
3345 | 0 | , bytes_payload); |
3346 | |
|
3347 | 0 | m_stat.received_bytes(bytes_payload, bytes_protocol); |
3348 | 0 | } |
3349 | | |
3350 | | void session_impl::trancieve_ip_packet(int bytes, bool ipv6) |
3351 | 0 | { |
3352 | 0 | TORRENT_ASSERT(bytes >= 0); |
3353 | | // one TCP/IP packet header for the packet |
3354 | | // sent or received, and one for the ACK |
3355 | | // The IPv4 header is 20 bytes |
3356 | | // and IPv6 header is 40 bytes |
3357 | 0 | int const header = (ipv6 ? 40 : 20) + 20; |
3358 | 0 | int const mtu = 1500; |
3359 | 0 | int const packet_size = mtu - header; |
3360 | 0 | int const overhead = std::max(1, (bytes + packet_size - 1) / packet_size) * header; |
3361 | 0 | m_stats_counters.inc_stats_counter(counters::sent_ip_overhead_bytes |
3362 | 0 | , overhead); |
3363 | 0 | m_stats_counters.inc_stats_counter(counters::recv_ip_overhead_bytes |
3364 | 0 | , overhead); |
3365 | |
|
3366 | 0 | m_stat.trancieve_ip_packet(bytes, ipv6); |
3367 | 0 | } |
3368 | | |
3369 | | void session_impl::sent_syn(bool ipv6) |
3370 | 0 | { |
3371 | 0 | int const overhead = ipv6 ? 60 : 40; |
3372 | 0 | m_stats_counters.inc_stats_counter(counters::sent_ip_overhead_bytes |
3373 | 0 | , overhead); |
3374 | |
|
3375 | 0 | m_stat.sent_syn(ipv6); |
3376 | 0 | } |
3377 | | |
3378 | | void session_impl::received_synack(bool ipv6) |
3379 | 0 | { |
3380 | 0 | int const overhead = ipv6 ? 60 : 40; |
3381 | 0 | m_stats_counters.inc_stats_counter(counters::sent_ip_overhead_bytes |
3382 | 0 | , overhead); |
3383 | 0 | m_stats_counters.inc_stats_counter(counters::recv_ip_overhead_bytes |
3384 | 0 | , overhead); |
3385 | |
|
3386 | 0 | m_stat.received_synack(ipv6); |
3387 | 0 | } |
3388 | | |
3389 | | void session_impl::on_tick(error_code const& e) |
3390 | 1.83k | { |
3391 | 1.83k | COMPLETE_ASYNC("session_impl::on_tick"); |
3392 | 1.83k | m_stats_counters.inc_stats_counter(counters::on_tick_counter); |
3393 | | |
3394 | 1.83k | TORRENT_ASSERT(is_single_thread()); |
3395 | | |
3396 | 1.83k | time_point const now = aux::time_now(); |
3397 | | |
3398 | | // remove undead peers that only have this list as their reference keeping them alive |
3399 | 1.83k | if (!m_undead_peers.empty()) |
3400 | 0 | { |
3401 | 0 | auto const remove_it = std::remove_if(m_undead_peers.begin(), m_undead_peers.end() |
3402 | 0 | , [](std::shared_ptr<peer_connection>& ptr) { return ptr.use_count() == 1; }); |
3403 | 0 | m_undead_peers.erase(remove_it, m_undead_peers.end()); |
3404 | 0 | if (m_undead_peers.empty()) |
3405 | 0 | { |
3406 | | // we just removed our last "undead" peer (i.e. a peer connection |
3407 | | // that had some external reference to it). It's now safe to |
3408 | | // shut-down |
3409 | 0 | if (m_abort) |
3410 | 0 | { |
3411 | 0 | post(m_io_context, make_handler([this] { abort_stage2(); } |
3412 | 0 | , m_abort_handler_storage, *this)); |
3413 | 0 | } |
3414 | 0 | } |
3415 | 0 | } |
3416 | | |
3417 | | // too expensive |
3418 | | // INVARIANT_CHECK; |
3419 | | |
3420 | | // we have to keep ticking the utp socket manager |
3421 | | // until they're all closed |
3422 | | // we also have to keep updating the aux time while |
3423 | | // there are outstanding announces |
3424 | 1.83k | if (m_abort) |
3425 | 1.83k | { |
3426 | 1.83k | if (m_utp_socket_manager.num_sockets() == 0 |
3427 | 1.83k | #ifdef TORRENT_SSL_PEERS |
3428 | 1.83k | && m_ssl_utp_socket_manager.num_sockets() == 0 |
3429 | 1.83k | #endif |
3430 | 1.83k | && m_undead_peers.empty() |
3431 | 1.83k | && m_tracker_manager.empty()) |
3432 | 1.83k | { |
3433 | | // this is where shutdown completes. We won't issue another |
3434 | | // on_tick() |
3435 | 1.83k | return; |
3436 | 1.83k | } |
3437 | | #if defined TORRENT_ASIO_DEBUGGING |
3438 | | std::fprintf(stderr, "uTP sockets: %d ssl-uTP sockets: %d undead-peers left: %d\n" |
3439 | | , m_utp_socket_manager.num_sockets() |
3440 | | #ifdef TORRENT_SSL_PEERS |
3441 | | , m_ssl_utp_socket_manager.num_sockets() |
3442 | | #else |
3443 | | , 0 |
3444 | | #endif |
3445 | | , int(m_undead_peers.size())); |
3446 | | #endif |
3447 | 1.83k | } |
3448 | | |
3449 | 2 | if (e && e != boost::asio::error::operation_aborted) |
3450 | 0 | { |
3451 | | #ifndef TORRENT_DISABLE_LOGGING |
3452 | | if (should_log()) |
3453 | | session_log("*** TICK TIMER FAILED %s", e.message().c_str()); |
3454 | | #endif |
3455 | 0 | std::abort(); |
3456 | 0 | } |
3457 | | |
3458 | 2 | ADD_OUTSTANDING_ASYNC("session_impl::on_tick"); |
3459 | 2 | milliseconds const tick_interval(m_abort ? 100 : m_settings.get_int(settings_pack::tick_interval)); |
3460 | 2 | m_timer.expires_at(now + tick_interval); |
3461 | 2 | m_timer.async_wait(aux::make_handler([this](error_code const& err) |
3462 | 2 | { wrap(&session_impl::on_tick, err); }, m_tick_handler_storage, *this)); |
3463 | | |
3464 | 2 | m_download_rate.update_quotas(now - m_last_tick); |
3465 | 2 | m_upload_rate.update_quotas(now - m_last_tick); |
3466 | | |
3467 | 2 | m_last_tick = now; |
3468 | | |
3469 | 2 | m_utp_socket_manager.tick(now); |
3470 | 2 | #ifdef TORRENT_SSL_PEERS |
3471 | 2 | m_ssl_utp_socket_manager.tick(now); |
3472 | 2 | #endif |
3473 | | |
3474 | | // only tick the following once per second |
3475 | 2 | if (now - m_last_second_tick < seconds(1)) return; |
3476 | | |
3477 | 1 | #ifndef TORRENT_DISABLE_DHT |
3478 | 1 | if (m_dht |
3479 | 1 | && m_dht_interval_update_torrents < 40 |
3480 | 1 | && m_dht_interval_update_torrents != int(m_torrents.size())) |
3481 | 0 | update_dht_announce_interval(); |
3482 | 1 | #endif |
3483 | | |
3484 | 1 | m_utp_socket_manager.decay(); |
3485 | 1 | #ifdef TORRENT_SSL_PEERS |
3486 | 1 | m_ssl_utp_socket_manager.decay(); |
3487 | 1 | #endif |
3488 | | |
3489 | 1 | int const tick_interval_ms = aux::numeric_cast<int>(total_milliseconds(now - m_last_second_tick)); |
3490 | 1 | m_last_second_tick = now; |
3491 | | |
3492 | 1 | std::int32_t const stime = session_time(); |
3493 | 1 | if (stime > 65000) |
3494 | 0 | { |
3495 | | // we're getting close to the point where our timestamps |
3496 | | // in torrent_peer are wrapping. We need to step all counters back |
3497 | | // four hours. This means that any timestamp that refers to a time |
3498 | | // more than 18.2 - 4 = 14.2 hours ago, will be incremented to refer to |
3499 | | // 14.2 hours ago. |
3500 | |
|
3501 | 0 | m_created += hours(4); |
3502 | |
|
3503 | 0 | constexpr int four_hours = 60 * 60 * 4; |
3504 | 0 | for (auto& i : m_torrents) |
3505 | 0 | { |
3506 | 0 | i->step_session_time(four_hours); |
3507 | 0 | } |
3508 | 0 | } |
3509 | | |
3510 | 1 | #ifndef TORRENT_DISABLE_EXTENSIONS |
3511 | 1 | for (auto& ext : m_ses_extensions[plugins_tick_idx]) |
3512 | 0 | { |
3513 | 0 | ext->on_tick(); |
3514 | 0 | } |
3515 | 1 | #endif |
3516 | | |
3517 | | // don't do any of the following while we're shutting down |
3518 | 1 | if (m_abort) return; |
3519 | | |
3520 | 1 | switch (m_settings.get_int(settings_pack::mixed_mode_algorithm)) |
3521 | 1 | { |
3522 | 0 | case settings_pack::prefer_tcp: |
3523 | 0 | set_upload_rate_limit(m_tcp_peer_class, 0); |
3524 | 0 | set_download_rate_limit(m_tcp_peer_class, 0); |
3525 | 0 | break; |
3526 | 1 | case settings_pack::peer_proportional: |
3527 | 1 | { |
3528 | 1 | int num_peers[2][2] = {{0, 0}, {0, 0}}; |
3529 | 1 | for (auto const& i : m_connections) |
3530 | 0 | { |
3531 | 0 | peer_connection& p = *i; |
3532 | 0 | if (p.in_handshake()) continue; |
3533 | 0 | int protocol = 0; |
3534 | 0 | if (is_utp(p.get_socket())) protocol = 1; |
3535 | |
|
3536 | 0 | if (p.download_queue().size() > 1 |
3537 | 0 | && p.m_channel_state[peer_connection::download_channel] & peer_info::bw_network) |
3538 | 0 | ++num_peers[protocol][peer_connection::download_channel]; |
3539 | 0 | if (!p.upload_queue().empty() |
3540 | 0 | && p.m_channel_state[peer_connection::upload_channel] & peer_info::bw_network) |
3541 | 0 | ++num_peers[protocol][peer_connection::upload_channel]; |
3542 | 0 | } |
3543 | | |
3544 | 1 | int const stat_rate[] = {m_stat.upload_rate(), m_stat.download_rate() }; |
3545 | | // never throttle below this |
3546 | 1 | int lower_limit[] = {5000, 30000}; |
3547 | | |
3548 | 3 | for (int i = 0; i < 2; ++i) |
3549 | 2 | { |
3550 | | // if there are no uTP peers, don't throttle TCP |
3551 | 2 | int const total_peers = num_peers[0][i] + num_peers[1][i]; |
3552 | 2 | if (num_peers[1][i] == 0 || total_peers < 5) |
3553 | 2 | { |
3554 | 2 | set_rate_limit(m_tcp_peer_class, i, 0); |
3555 | 2 | } |
3556 | 0 | else |
3557 | 0 | { |
3558 | 0 | if (num_peers[0][i] == 0) num_peers[0][i] = 1; |
3559 | | // this are 64 bits since it's multiplied by the number |
3560 | | // of peers, which otherwise might overflow an int |
3561 | 0 | std::int64_t rate = stat_rate[i]; |
3562 | 0 | int const limit = std::max(int(rate * num_peers[0][i] * 4 / total_peers), lower_limit[i]); |
3563 | 0 | set_rate_limit(m_tcp_peer_class, i, limit); |
3564 | 0 | } |
3565 | 2 | } |
3566 | 1 | } |
3567 | 1 | break; |
3568 | 1 | } |
3569 | | |
3570 | | // -------------------------------------------------------------- |
3571 | | // auto managed torrent |
3572 | | // -------------------------------------------------------------- |
3573 | 1 | if (!m_paused) m_auto_manage_time_scaler--; |
3574 | 1 | if (m_auto_manage_time_scaler < 0) |
3575 | 1 | { |
3576 | 1 | m_auto_manage_time_scaler = settings().get_int(settings_pack::auto_manage_interval); |
3577 | 1 | recalculate_auto_managed_torrents(); |
3578 | 1 | } |
3579 | | |
3580 | | // -------------------------------------------------------------- |
3581 | | // check for incoming connections that might have timed out |
3582 | | // -------------------------------------------------------------- |
3583 | | |
3584 | 1 | for (auto i = m_connections.begin(); i != m_connections.end();) |
3585 | 0 | { |
3586 | 0 | peer_connection* p = (*i).get(); |
3587 | 0 | ++i; |
3588 | | // ignore connections that already have a torrent, since they |
3589 | | // are ticked through the torrents' second_tick |
3590 | 0 | if (!p->associated_torrent().expired()) continue; |
3591 | | |
3592 | | // TODO: have a separate list for these connections, instead of having to loop through all of them |
3593 | 0 | int timeout = m_settings.get_int(settings_pack::handshake_timeout); |
3594 | 0 | #if TORRENT_USE_I2P |
3595 | 0 | timeout *= is_i2p(p->get_socket()) ? 4 : 1; |
3596 | 0 | #endif |
3597 | 0 | if (m_last_tick - p->connected_time () > seconds(timeout)) |
3598 | 0 | p->disconnect(errors::timed_out, operation_t::bittorrent); |
3599 | 0 | } |
3600 | | |
3601 | | // -------------------------------------------------------------- |
3602 | | // second_tick every torrent (that wants it) |
3603 | | // -------------------------------------------------------------- |
3604 | | |
3605 | | #if TORRENT_DEBUG_STREAMING > 0 |
3606 | | std::printf("\033[2J\033[0;0H"); |
3607 | | #endif |
3608 | | |
3609 | 1 | aux::vector<torrent*>& want_tick = m_torrent_lists[torrent_want_tick]; |
3610 | 2 | for (int i = 0; i < int(want_tick.size()); ++i) |
3611 | 1 | { |
3612 | 1 | torrent& t = *want_tick[i]; |
3613 | 1 | TORRENT_ASSERT(t.want_tick()); |
3614 | 1 | TORRENT_ASSERT(!t.is_aborted()); |
3615 | | |
3616 | 1 | t.second_tick(tick_interval_ms); |
3617 | | |
3618 | | // if the call to second_tick caused the torrent |
3619 | | // to no longer want to be ticked (i.e. it was |
3620 | | // removed from the list) we need to back up the counter |
3621 | | // to not miss the torrent after it |
3622 | 1 | if (!t.want_tick()) --i; |
3623 | 1 | } |
3624 | | |
3625 | | // TODO: this should apply to all bandwidth channels |
3626 | 1 | if (m_settings.get_bool(settings_pack::rate_limit_ip_overhead)) |
3627 | 1 | { |
3628 | 1 | int const up_limit = upload_rate_limit(m_global_class); |
3629 | 1 | int const down_limit = download_rate_limit(m_global_class); |
3630 | | |
3631 | 1 | if (down_limit > 0 |
3632 | 1 | && m_stat.download_ip_overhead() >= down_limit |
3633 | 1 | && m_alerts.should_post<performance_alert>()) |
3634 | 0 | { |
3635 | 0 | m_alerts.emplace_alert<performance_alert>(torrent_handle() |
3636 | 0 | , performance_alert::download_limit_too_low); |
3637 | 0 | } |
3638 | | |
3639 | 1 | if (up_limit > 0 |
3640 | 1 | && m_stat.upload_ip_overhead() >= up_limit |
3641 | 1 | && m_alerts.should_post<performance_alert>()) |
3642 | 0 | { |
3643 | 0 | m_alerts.emplace_alert<performance_alert>(torrent_handle() |
3644 | 0 | , performance_alert::upload_limit_too_low); |
3645 | 0 | } |
3646 | 1 | } |
3647 | | |
3648 | 1 | #if TORRENT_ABI_VERSION == 1 |
3649 | 1 | m_peak_up_rate = std::max(m_stat.upload_rate(), m_peak_up_rate); |
3650 | 1 | #endif |
3651 | | |
3652 | 1 | m_stat.second_tick(tick_interval_ms); |
3653 | | |
3654 | | // -------------------------------------------------------------- |
3655 | | // scrape paused torrents that are auto managed |
3656 | | // (unless the session is paused) |
3657 | | // -------------------------------------------------------------- |
3658 | 1 | if (!m_paused) |
3659 | 1 | { |
3660 | 1 | INVARIANT_CHECK; |
3661 | 1 | --m_auto_scrape_time_scaler; |
3662 | 1 | if (m_auto_scrape_time_scaler <= 0) |
3663 | 0 | { |
3664 | 0 | aux::vector<torrent*>& want_scrape = m_torrent_lists[torrent_want_scrape]; |
3665 | 0 | m_auto_scrape_time_scaler = m_settings.get_int(settings_pack::auto_scrape_interval) |
3666 | 0 | / std::max(1, int(want_scrape.size())); |
3667 | 0 | if (m_auto_scrape_time_scaler < m_settings.get_int(settings_pack::auto_scrape_min_interval)) |
3668 | 0 | m_auto_scrape_time_scaler = m_settings.get_int(settings_pack::auto_scrape_min_interval); |
3669 | |
|
3670 | 0 | if (!want_scrape.empty() && !m_abort) |
3671 | 0 | { |
3672 | 0 | if (m_next_scrape_torrent >= int(want_scrape.size())) |
3673 | 0 | m_next_scrape_torrent = 0; |
3674 | |
|
3675 | 0 | torrent& t = *want_scrape[m_next_scrape_torrent]; |
3676 | 0 | TORRENT_ASSERT(t.is_paused() && t.is_auto_managed()); |
3677 | | |
3678 | | // false means it's not triggered by the user, but automatically |
3679 | | // by libtorrent |
3680 | 0 | t.scrape_tracker(-1, false); |
3681 | |
|
3682 | 0 | ++m_next_scrape_torrent; |
3683 | 0 | if (m_next_scrape_torrent >= int(want_scrape.size())) |
3684 | 0 | m_next_scrape_torrent = 0; |
3685 | |
|
3686 | 0 | } |
3687 | 0 | } |
3688 | 1 | } |
3689 | | |
3690 | | // -------------------------------------------------------------- |
3691 | | // connect new peers |
3692 | | // -------------------------------------------------------------- |
3693 | | |
3694 | 1 | try_connect_more_peers(); |
3695 | | |
3696 | | // -------------------------------------------------------------- |
3697 | | // unchoke set calculations |
3698 | | // -------------------------------------------------------------- |
3699 | 1 | m_unchoke_time_scaler--; |
3700 | 1 | if (m_unchoke_time_scaler <= 0 && !m_connections.empty()) |
3701 | 0 | { |
3702 | 0 | m_unchoke_time_scaler = settings().get_int(settings_pack::unchoke_interval); |
3703 | 0 | recalculate_unchoke_slots(); |
3704 | 0 | } |
3705 | | |
3706 | | // -------------------------------------------------------------- |
3707 | | // optimistic unchoke calculation |
3708 | | // -------------------------------------------------------------- |
3709 | 1 | m_optimistic_unchoke_time_scaler--; |
3710 | 1 | if (m_optimistic_unchoke_time_scaler <= 0) |
3711 | 1 | { |
3712 | 1 | m_optimistic_unchoke_time_scaler |
3713 | 1 | = settings().get_int(settings_pack::optimistic_unchoke_interval); |
3714 | 1 | recalculate_optimistic_unchoke_slots(); |
3715 | 1 | } |
3716 | | |
3717 | | // -------------------------------------------------------------- |
3718 | | // disconnect peers when we have too many |
3719 | | // -------------------------------------------------------------- |
3720 | 1 | --m_disconnect_time_scaler; |
3721 | 1 | if (m_disconnect_time_scaler <= 0) |
3722 | 0 | { |
3723 | 0 | m_disconnect_time_scaler = m_settings.get_int(settings_pack::peer_turnover_interval); |
3724 | | |
3725 | | // if the connections_limit is too low, the disconnect |
3726 | | // logic is disabled, since it is too disruptive |
3727 | 0 | if (m_settings.get_int(settings_pack::connections_limit) > 5) |
3728 | 0 | { |
3729 | 0 | int const limit = std::min(m_settings.get_int(settings_pack::connections_limit) |
3730 | 0 | , std::numeric_limits<int>::max() / 100); |
3731 | 0 | int const cutoff = std::min(m_settings.get_int(settings_pack::peer_turnover_cutoff), 100); |
3732 | 0 | if (num_connections() >= limit * cutoff / 100 && !m_torrents.empty()) |
3733 | 0 | { |
3734 | | // every 90 seconds, disconnect the worst peers |
3735 | | // if we have reached the connection limit |
3736 | 0 | auto const i = std::max_element(m_torrents.begin(), m_torrents.end() |
3737 | 0 | , [] (std::shared_ptr<torrent> const& lhs, std::shared_ptr<torrent> const& rhs) |
3738 | 0 | { return lhs->num_peers() < rhs->num_peers(); }); |
3739 | |
|
3740 | 0 | TORRENT_ASSERT(i != m_torrents.end()); |
3741 | 0 | int const peers_to_disconnect = std::min(std::max( |
3742 | 0 | (*i)->num_peers() * m_settings.get_int(settings_pack::peer_turnover) / 100, 1) |
3743 | 0 | , (*i)->num_connect_candidates()); |
3744 | 0 | (*i)->disconnect_peers(peers_to_disconnect |
3745 | 0 | , error_code(errors::optimistic_disconnect)); |
3746 | 0 | } |
3747 | 0 | else |
3748 | 0 | { |
3749 | | // if we haven't reached the global max. see if any torrent |
3750 | | // has reached its local limit |
3751 | 0 | for (auto const& t : m_torrents) |
3752 | 0 | { |
3753 | | // this disconnect logic is disabled for torrents with |
3754 | | // too low connection limit |
3755 | 0 | int const max = std::min(t->max_connections() |
3756 | 0 | , std::numeric_limits<int>::max() / 100); |
3757 | 0 | if (t->num_peers() < max * cutoff / 100 || max < 6) |
3758 | 0 | continue; |
3759 | | |
3760 | 0 | int const peers_to_disconnect = std::min(std::max(t->num_peers() |
3761 | 0 | * m_settings.get_int(settings_pack::peer_turnover) / 100, 1) |
3762 | 0 | , t->num_connect_candidates()); |
3763 | 0 | t->disconnect_peers(peers_to_disconnect, errors::optimistic_disconnect); |
3764 | 0 | } |
3765 | 0 | } |
3766 | 0 | } |
3767 | 0 | } |
3768 | 1 | } |
3769 | | |
3770 | | void session_impl::received_buffer(int s) |
3771 | 0 | { |
3772 | 0 | int index = std::min(aux::log2p1(std::uint32_t(s >> 3)), 17); |
3773 | 0 | m_stats_counters.inc_stats_counter(counters::socket_recv_size3 + index); |
3774 | 0 | } |
3775 | | |
3776 | | void session_impl::sent_buffer(int s) |
3777 | 0 | { |
3778 | 0 | int index = std::min(aux::log2p1(std::uint32_t(s >> 3)), 17); |
3779 | 0 | m_stats_counters.inc_stats_counter(counters::socket_send_size3 + index); |
3780 | 0 | } |
3781 | | |
3782 | | void session_impl::prioritize_connections(std::weak_ptr<torrent> t) |
3783 | 0 | { |
3784 | 0 | m_prio_torrents.emplace_back(t, 10); |
3785 | 0 | } |
3786 | | |
3787 | | #ifndef TORRENT_DISABLE_DHT |
3788 | | |
3789 | | void session_impl::add_dht_node(udp::endpoint const& n) |
3790 | 0 | { |
3791 | 0 | TORRENT_ASSERT(is_single_thread()); |
3792 | 0 | if (m_dht) |
3793 | 0 | m_dht->add_node(n); |
3794 | 0 | else if (m_dht_nodes.size() >= 200) |
3795 | 0 | m_dht_nodes[random(std::uint32_t(m_dht_nodes.size() - 1))] = n; |
3796 | 0 | else |
3797 | 0 | m_dht_nodes.push_back(n); |
3798 | 0 | } |
3799 | | |
3800 | | bool session_impl::has_dht() const |
3801 | 0 | { |
3802 | 0 | return m_dht.get() != nullptr; |
3803 | 0 | } |
3804 | | |
3805 | | void session_impl::prioritize_dht(std::weak_ptr<torrent> t) |
3806 | 0 | { |
3807 | 0 | TORRENT_ASSERT(!m_abort); |
3808 | 0 | if (m_abort) return; |
3809 | | |
3810 | 0 | TORRENT_ASSERT(m_dht); |
3811 | 0 | m_dht_torrents.push_back(t); |
3812 | | #ifndef TORRENT_DISABLE_LOGGING |
3813 | | std::shared_ptr<torrent> tor = t.lock(); |
3814 | | if (tor && should_log()) |
3815 | | session_log("prioritizing DHT announce: \"%s\"", tor->name().c_str()); |
3816 | | #endif |
3817 | | // trigger a DHT announce right away if we just added a new torrent and |
3818 | | // there's no back-log. in the timer handler, as long as there are more |
3819 | | // high priority torrents to be announced to the DHT, it will keep the |
3820 | | // timer interval short until all torrents have been announced. |
3821 | 0 | if (m_dht_torrents.size() == 1) |
3822 | 0 | { |
3823 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::on_dht_announce"); |
3824 | 0 | m_dht_announce_timer.expires_after(seconds(0)); |
3825 | 0 | m_dht_announce_timer.async_wait([this](error_code const& err) { |
3826 | 0 | wrap(&session_impl::on_dht_announce, err); }); |
3827 | 0 | } |
3828 | 0 | } |
3829 | | |
3830 | | void session_impl::on_dht_announce(error_code const& e) |
3831 | 0 | { |
3832 | 0 | COMPLETE_ASYNC("session_impl::on_dht_announce"); |
3833 | 0 | TORRENT_ASSERT(is_single_thread()); |
3834 | 0 | if (e) |
3835 | 0 | { |
3836 | | #ifndef TORRENT_DISABLE_LOGGING |
3837 | | if (should_log()) |
3838 | | { |
3839 | | session_log("aborting DHT announce timer (%d): %s" |
3840 | | , e.value(), e.message().c_str()); |
3841 | | } |
3842 | | #endif |
3843 | 0 | return; |
3844 | 0 | } |
3845 | | |
3846 | 0 | if (m_abort) |
3847 | 0 | { |
3848 | | #ifndef TORRENT_DISABLE_LOGGING |
3849 | | session_log("aborting DHT announce timer: m_abort set"); |
3850 | | #endif |
3851 | 0 | return; |
3852 | 0 | } |
3853 | | |
3854 | 0 | if (!m_dht) |
3855 | 0 | { |
3856 | 0 | m_dht_torrents.clear(); |
3857 | 0 | return; |
3858 | 0 | } |
3859 | | |
3860 | 0 | TORRENT_ASSERT(m_dht); |
3861 | |
|
3862 | 0 | update_dht_announce_interval(); |
3863 | |
|
3864 | 0 | if (!m_dht_torrents.empty()) |
3865 | 0 | { |
3866 | 0 | std::shared_ptr<torrent> t; |
3867 | 0 | do |
3868 | 0 | { |
3869 | 0 | t = m_dht_torrents.front().lock(); |
3870 | 0 | m_dht_torrents.pop_front(); |
3871 | 0 | } while (!t && !m_dht_torrents.empty()); |
3872 | |
|
3873 | 0 | if (t) |
3874 | 0 | { |
3875 | 0 | t->dht_announce(); |
3876 | 0 | return; |
3877 | 0 | } |
3878 | 0 | } |
3879 | 0 | if (m_torrents.empty()) return; |
3880 | | |
3881 | 0 | if (m_next_dht_torrent >= m_torrents.size()) |
3882 | 0 | m_next_dht_torrent = 0; |
3883 | 0 | m_torrents[m_next_dht_torrent]->dht_announce(); |
3884 | | // TODO: 2 make a list for torrents that want to be announced on the DHT so we |
3885 | | // don't have to loop over all torrents, just to find the ones that want to announce |
3886 | 0 | ++m_next_dht_torrent; |
3887 | 0 | if (m_next_dht_torrent >= m_torrents.size()) |
3888 | 0 | m_next_dht_torrent = 0; |
3889 | 0 | } |
3890 | | #endif |
3891 | | |
3892 | | void session_impl::on_lsd_announce(error_code const& e) |
3893 | 1.83k | { |
3894 | 1.83k | COMPLETE_ASYNC("session_impl::on_lsd_announce"); |
3895 | 1.83k | m_stats_counters.inc_stats_counter(counters::on_lsd_counter); |
3896 | 1.83k | TORRENT_ASSERT(is_single_thread()); |
3897 | 1.83k | if (e) return; |
3898 | | |
3899 | 0 | if (m_abort) return; |
3900 | | |
3901 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::on_lsd_announce"); |
3902 | | // announce on local network every 5 minutes |
3903 | 0 | int const delay = std::max(m_settings.get_int(settings_pack::local_service_announce_interval) |
3904 | 0 | / std::max(int(m_torrents.size()), 1), 1); |
3905 | 0 | m_lsd_announce_timer.expires_after(seconds(delay)); |
3906 | 0 | m_lsd_announce_timer.async_wait([this](error_code const& err) { |
3907 | 0 | wrap(&session_impl::on_lsd_announce, err); }); |
3908 | |
|
3909 | 0 | if (m_torrents.empty()) return; |
3910 | | |
3911 | 0 | if (m_next_lsd_torrent >= m_torrents.size()) |
3912 | 0 | m_next_lsd_torrent = 0; |
3913 | 0 | m_torrents[m_next_lsd_torrent]->lsd_announce(); |
3914 | 0 | ++m_next_lsd_torrent; |
3915 | 0 | if (m_next_lsd_torrent >= m_torrents.size()) |
3916 | 0 | m_next_lsd_torrent = 0; |
3917 | 0 | } |
3918 | | |
3919 | | void session_impl::auto_manage_checking_torrents(std::vector<torrent*>& list |
3920 | | , int& limit) |
3921 | 1 | { |
3922 | 1 | for (auto& t : list) |
3923 | 0 | { |
3924 | 0 | TORRENT_ASSERT(t->state() == torrent_status::checking_files); |
3925 | 0 | TORRENT_ASSERT(t->is_auto_managed()); |
3926 | 0 | if (limit <= 0) |
3927 | 0 | { |
3928 | 0 | t->pause(); |
3929 | 0 | } |
3930 | 0 | else |
3931 | 0 | { |
3932 | 0 | t->resume(); |
3933 | 0 | if (!t->should_check_files()) continue; |
3934 | 0 | t->start_checking(); |
3935 | 0 | --limit; |
3936 | 0 | } |
3937 | 0 | } |
3938 | 1 | } |
3939 | | |
3940 | | void session_impl::auto_manage_torrents(std::vector<torrent*>& list |
3941 | | , int& dht_limit, int& tracker_limit |
3942 | | , int& lsd_limit, int& hard_limit, int type_limit) |
3943 | 2 | { |
3944 | 2 | for (auto& t : list) |
3945 | 1 | { |
3946 | 1 | TORRENT_ASSERT(t->state() != torrent_status::checking_files); |
3947 | | |
3948 | | // inactive torrents don't count (and if you configured them to do so, |
3949 | | // the torrent won't say it's inactive) |
3950 | 1 | if (hard_limit > 0 && t->is_inactive()) |
3951 | 0 | { |
3952 | 0 | t->set_announce_to_dht(--dht_limit >= 0); |
3953 | 0 | t->set_announce_to_trackers(--tracker_limit >= 0); |
3954 | 0 | t->set_announce_to_lsd(--lsd_limit >= 0); |
3955 | |
|
3956 | 0 | --hard_limit; |
3957 | | #ifndef TORRENT_DISABLE_LOGGING |
3958 | | if (t->is_torrent_paused()) |
3959 | | t->log_to_all_peers("auto manager starting (inactive) torrent"); |
3960 | | #endif |
3961 | 0 | t->set_paused(false); |
3962 | 0 | continue; |
3963 | 0 | } |
3964 | | |
3965 | 1 | if (type_limit > 0 && hard_limit > 0) |
3966 | 1 | { |
3967 | 1 | t->set_announce_to_dht(--dht_limit >= 0); |
3968 | 1 | t->set_announce_to_trackers(--tracker_limit >= 0); |
3969 | 1 | t->set_announce_to_lsd(--lsd_limit >= 0); |
3970 | | |
3971 | 1 | --hard_limit; |
3972 | 1 | --type_limit; |
3973 | | #ifndef TORRENT_DISABLE_LOGGING |
3974 | | if (t->is_torrent_paused()) |
3975 | | t->log_to_all_peers("auto manager starting torrent"); |
3976 | | #endif |
3977 | 1 | t->set_paused(false); |
3978 | 1 | continue; |
3979 | 1 | } |
3980 | | |
3981 | | #ifndef TORRENT_DISABLE_LOGGING |
3982 | | if (!t->is_torrent_paused()) |
3983 | | t->log_to_all_peers("auto manager pausing torrent"); |
3984 | | #endif |
3985 | | // use graceful pause for auto-managed torrents |
3986 | 0 | t->set_paused(true, torrent_handle::graceful_pause |
3987 | 0 | | torrent_handle::clear_disk_cache); |
3988 | 0 | t->set_announce_to_dht(false); |
3989 | 0 | t->set_announce_to_trackers(false); |
3990 | 0 | t->set_announce_to_lsd(false); |
3991 | 0 | } |
3992 | 2 | } |
3993 | | |
3994 | | int session_impl::get_int_setting(int n) const |
3995 | 3.67k | { |
3996 | 3.67k | int const v = settings().get_int(n); |
3997 | 3.67k | if (v < 0) return std::numeric_limits<int>::max(); |
3998 | 3.67k | return v; |
3999 | 3.67k | } |
4000 | | |
4001 | | void session_impl::recalculate_auto_managed_torrents() |
4002 | 1 | { |
4003 | 1 | INVARIANT_CHECK; |
4004 | | |
4005 | 1 | m_last_auto_manage = time_now(); |
4006 | 1 | m_need_auto_manage = false; |
4007 | | |
4008 | 1 | if (m_paused) return; |
4009 | | |
4010 | | // make copies of the lists of torrents that we want to consider for auto |
4011 | | // management. We need copies because they will be sorted. |
4012 | 1 | std::vector<torrent*> checking |
4013 | 1 | = torrent_list(session_interface::torrent_checking_auto_managed); |
4014 | 1 | std::vector<torrent*> downloaders |
4015 | 1 | = torrent_list(session_interface::torrent_downloading_auto_managed); |
4016 | 1 | std::vector<torrent*> seeds |
4017 | 1 | = torrent_list(session_interface::torrent_seeding_auto_managed); |
4018 | | |
4019 | | // these counters are set to the number of torrents |
4020 | | // of each kind we're allowed to have active |
4021 | 1 | int downloading_limit = get_int_setting(settings_pack::active_downloads); |
4022 | 1 | int seeding_limit = get_int_setting(settings_pack::active_seeds); |
4023 | 1 | int checking_limit = get_int_setting(settings_pack::active_checking); |
4024 | 1 | int dht_limit = get_int_setting(settings_pack::active_dht_limit); |
4025 | 1 | int tracker_limit = get_int_setting(settings_pack::active_tracker_limit); |
4026 | 1 | int lsd_limit = get_int_setting(settings_pack::active_lsd_limit); |
4027 | 1 | int hard_limit = get_int_setting(settings_pack::active_limit); |
4028 | | |
4029 | | // if hard_limit is <= 0, all torrents in these lists should be paused. |
4030 | | // The order is not relevant |
4031 | 1 | if (hard_limit > 0) |
4032 | 1 | { |
4033 | | // we only need to sort the first n torrents here, where n is the number |
4034 | | // of checking torrents we allow. The rest of the list is still used to |
4035 | | // make sure the remaining torrents are paused, but their order is not |
4036 | | // relevant |
4037 | 1 | std::partial_sort(checking.begin(), checking.begin() + |
4038 | 1 | std::min(checking_limit, int(checking.size())), checking.end() |
4039 | 1 | , [](torrent const* lhs, torrent const* rhs) |
4040 | 1 | { return lhs->sequence_number() < rhs->sequence_number(); }); |
4041 | | |
4042 | 1 | std::partial_sort(downloaders.begin(), downloaders.begin() + |
4043 | 1 | std::min(hard_limit, int(downloaders.size())), downloaders.end() |
4044 | 1 | , [](torrent const* lhs, torrent const* rhs) |
4045 | 1 | { return lhs->sequence_number() < rhs->sequence_number(); }); |
4046 | | |
4047 | 1 | std::partial_sort(seeds.begin(), seeds.begin() + |
4048 | 1 | std::min(hard_limit, int(seeds.size())), seeds.end() |
4049 | 1 | , [this](torrent const* lhs, torrent const* rhs) |
4050 | 1 | { return lhs->seed_rank(m_settings) > rhs->seed_rank(m_settings); }); |
4051 | 1 | } |
4052 | | |
4053 | 1 | auto_manage_checking_torrents(checking, checking_limit); |
4054 | | |
4055 | 1 | if (settings().get_bool(settings_pack::auto_manage_prefer_seeds)) |
4056 | 0 | { |
4057 | 0 | auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit |
4058 | 0 | , hard_limit, seeding_limit); |
4059 | 0 | auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit |
4060 | 0 | , hard_limit, downloading_limit); |
4061 | 0 | } |
4062 | 1 | else |
4063 | 1 | { |
4064 | 1 | auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit |
4065 | 1 | , hard_limit, downloading_limit); |
4066 | 1 | auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit |
4067 | 1 | , hard_limit, seeding_limit); |
4068 | 1 | } |
4069 | 1 | } |
4070 | | |
4071 | | namespace { |
4072 | | #ifndef TORRENT_DISABLE_EXTENSIONS |
4073 | | uint64_t const priority_undetermined = std::numeric_limits<uint64_t>::max() - 1; |
4074 | | #endif |
4075 | | |
4076 | | struct opt_unchoke_candidate |
4077 | | { |
4078 | | explicit opt_unchoke_candidate(std::shared_ptr<peer_connection> const* tp) |
4079 | 0 | : peer(tp) |
4080 | 0 | {} |
4081 | | |
4082 | | std::shared_ptr<peer_connection> const* peer; |
4083 | | #ifndef TORRENT_DISABLE_EXTENSIONS |
4084 | | // this is mutable because comparison functors passed to std::partial_sort |
4085 | | // are not supposed to modify the elements they are sorting. Here the mutation |
4086 | | // being applied is idempotent so it should not pose a problem. |
4087 | | mutable uint64_t ext_priority = priority_undetermined; |
4088 | | #endif |
4089 | | }; |
4090 | | |
4091 | | struct last_optimistic_unchoke_cmp |
4092 | | { |
4093 | | #ifndef TORRENT_DISABLE_EXTENSIONS |
4094 | | explicit last_optimistic_unchoke_cmp(std::vector<std::shared_ptr<plugin>>& ps) |
4095 | 1 | : plugins(ps) |
4096 | 1 | {} |
4097 | | |
4098 | | std::vector<std::shared_ptr<plugin>>& plugins; |
4099 | | #endif |
4100 | | |
4101 | | uint64_t get_ext_priority(opt_unchoke_candidate const& peer) const |
4102 | 0 | { |
4103 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
4104 | 0 | if (peer.ext_priority == priority_undetermined) |
4105 | 0 | { |
4106 | 0 | peer.ext_priority = std::numeric_limits<uint64_t>::max(); |
4107 | 0 | for (auto& e : plugins) |
4108 | 0 | { |
4109 | 0 | uint64_t const priority = e->get_unchoke_priority(peer_connection_handle(*peer.peer)); |
4110 | 0 | peer.ext_priority = std::min(priority, peer.ext_priority); |
4111 | 0 | } |
4112 | 0 | } |
4113 | 0 | return peer.ext_priority; |
4114 | | #else |
4115 | | TORRENT_UNUSED(peer); |
4116 | | return std::numeric_limits<uint64_t>::max(); |
4117 | | #endif |
4118 | 0 | } |
4119 | | |
4120 | | bool operator()(opt_unchoke_candidate const& l |
4121 | | , opt_unchoke_candidate const& r) const |
4122 | 0 | { |
4123 | 0 | torrent_peer const* pil = (*l.peer)->peer_info_struct(); |
4124 | 0 | torrent_peer const* pir = (*r.peer)->peer_info_struct(); |
4125 | 0 | if (pil->last_optimistically_unchoked |
4126 | 0 | != pir->last_optimistically_unchoked) |
4127 | 0 | { |
4128 | 0 | return pil->last_optimistically_unchoked |
4129 | 0 | < pir->last_optimistically_unchoked; |
4130 | 0 | } |
4131 | 0 | else |
4132 | 0 | { |
4133 | 0 | return get_ext_priority(l) < get_ext_priority(r); |
4134 | 0 | } |
4135 | 0 | } |
4136 | | }; |
4137 | | } |
4138 | | |
4139 | | void session_impl::recalculate_optimistic_unchoke_slots() |
4140 | 1 | { |
4141 | 1 | INVARIANT_CHECK; |
4142 | | |
4143 | 1 | TORRENT_ASSERT(is_single_thread()); |
4144 | 1 | if (m_stats_counters[counters::num_unchoke_slots] == 0) return; |
4145 | | |
4146 | | // if we unchoke everyone, skip this logic |
4147 | 1 | if (settings().get_int(settings_pack::choking_algorithm) == settings_pack::fixed_slots_choker |
4148 | 1 | && settings().get_int(settings_pack::unchoke_slots_limit) < 0) |
4149 | 0 | return; |
4150 | | |
4151 | 1 | std::vector<opt_unchoke_candidate> opt_unchoke; |
4152 | | |
4153 | | // collect the currently optimistically unchoked peers here, so we can |
4154 | | // choke them when we've found new optimistic unchoke candidates. |
4155 | 1 | std::vector<torrent_peer*> prev_opt_unchoke; |
4156 | | |
4157 | | // TODO: 3 it would probably make sense to have a separate list of peers |
4158 | | // that are eligible for optimistic unchoke, similar to the torrents |
4159 | | // perhaps this could even iterate over the pool allocators of |
4160 | | // torrent_peer objects. It could probably be done in a single pass and |
4161 | | // collect the n best candidates. maybe just a queue of peers would make |
4162 | | // even more sense, just pick the next peer in the queue for unchoking. It |
4163 | | // would be O(1). |
4164 | 1 | for (auto& i : m_connections) |
4165 | 0 | { |
4166 | 0 | peer_connection* const p = i.get(); |
4167 | 0 | TORRENT_ASSERT(p); |
4168 | 0 | torrent_peer* pi = p->peer_info_struct(); |
4169 | 0 | if (!pi) continue; |
4170 | 0 | if (pi->web_seed) continue; |
4171 | | |
4172 | 0 | if (pi->optimistically_unchoked) |
4173 | 0 | { |
4174 | 0 | prev_opt_unchoke.push_back(pi); |
4175 | 0 | } |
4176 | |
|
4177 | 0 | torrent const* t = p->associated_torrent().lock().get(); |
4178 | 0 | if (!t) continue; |
4179 | | |
4180 | | // TODO: 3 peers should know whether their torrent is paused or not, |
4181 | | // instead of having to ask it over and over again |
4182 | 0 | if (t->is_paused()) continue; |
4183 | | |
4184 | 0 | if (!p->is_connecting() |
4185 | 0 | && !p->is_disconnecting() |
4186 | 0 | && p->is_peer_interested() |
4187 | 0 | && t->free_upload_slots() |
4188 | 0 | && (p->is_choked() || pi->optimistically_unchoked) |
4189 | 0 | && !p->ignore_unchoke_slots() |
4190 | 0 | && t->valid_metadata()) |
4191 | 0 | { |
4192 | 0 | opt_unchoke.emplace_back(&i); |
4193 | 0 | } |
4194 | 0 | } |
4195 | | |
4196 | | // find the peers that has been waiting the longest to be optimistically |
4197 | | // unchoked |
4198 | | |
4199 | 1 | int num_opt_unchoke = m_settings.get_int(settings_pack::num_optimistic_unchoke_slots); |
4200 | 1 | int const allowed_unchoke_slots = int(m_stats_counters[counters::num_unchoke_slots]); |
4201 | 1 | if (num_opt_unchoke == 0) num_opt_unchoke = std::max(1, allowed_unchoke_slots / 5); |
4202 | 1 | if (num_opt_unchoke > int(opt_unchoke.size())) num_opt_unchoke = |
4203 | 1 | int(opt_unchoke.size()); |
4204 | | |
4205 | | // find the n best optimistic unchoke candidates |
4206 | 1 | std::partial_sort(opt_unchoke.begin() |
4207 | 1 | , opt_unchoke.begin() + num_opt_unchoke |
4208 | 1 | , opt_unchoke.end() |
4209 | 1 | #ifndef TORRENT_DISABLE_EXTENSIONS |
4210 | 1 | , last_optimistic_unchoke_cmp(m_ses_extensions[plugins_optimistic_unchoke_idx]) |
4211 | | #else |
4212 | | , last_optimistic_unchoke_cmp() |
4213 | | #endif |
4214 | 1 | ); |
4215 | | |
4216 | | // unchoke the first num_opt_unchoke peers in the candidate set |
4217 | | // and make sure that the others are choked |
4218 | 1 | auto opt_unchoke_end = opt_unchoke.begin() |
4219 | 1 | + num_opt_unchoke; |
4220 | | |
4221 | 1 | for (auto i = opt_unchoke.begin(); i != opt_unchoke_end; ++i) |
4222 | 0 | { |
4223 | 0 | torrent_peer* pi = (*i->peer)->peer_info_struct(); |
4224 | 0 | auto* const p = static_cast<peer_connection*>(pi->connection); |
4225 | 0 | if (pi->optimistically_unchoked) |
4226 | 0 | { |
4227 | | #ifndef TORRENT_DISABLE_LOGGING |
4228 | | p->peer_log(peer_log_alert::info, "OPTIMISTIC UNCHOKE" |
4229 | | , "already unchoked | session-time: %d" |
4230 | | , pi->last_optimistically_unchoked); |
4231 | | #endif |
4232 | 0 | TORRENT_ASSERT(!pi->connection->is_choked()); |
4233 | | // remove this peer from prev_opt_unchoke, to prevent us from |
4234 | | // choking it later. This peer gets another round of optimistic |
4235 | | // unchoke |
4236 | 0 | auto const existing = |
4237 | 0 | std::find(prev_opt_unchoke.begin(), prev_opt_unchoke.end(), pi); |
4238 | 0 | TORRENT_ASSERT(existing != prev_opt_unchoke.end()); |
4239 | 0 | prev_opt_unchoke.erase(existing); |
4240 | 0 | } |
4241 | 0 | else |
4242 | 0 | { |
4243 | 0 | TORRENT_ASSERT(p->is_choked()); |
4244 | 0 | std::shared_ptr<torrent> t = p->associated_torrent().lock(); |
4245 | 0 | bool ret = t->unchoke_peer(*p, true); |
4246 | 0 | TORRENT_ASSERT(ret); |
4247 | 0 | if (ret) |
4248 | 0 | { |
4249 | 0 | pi->optimistically_unchoked = true; |
4250 | 0 | m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic); |
4251 | 0 | pi->last_optimistically_unchoked = std::uint16_t(session_time()); |
4252 | | #ifndef TORRENT_DISABLE_LOGGING |
4253 | | p->peer_log(peer_log_alert::info, "OPTIMISTIC UNCHOKE" |
4254 | | , "session-time: %d", pi->last_optimistically_unchoked); |
4255 | | #endif |
4256 | 0 | } |
4257 | 0 | } |
4258 | 0 | } |
4259 | | |
4260 | | // now, choke all the previous optimistically unchoked peers |
4261 | 1 | for (torrent_peer* pi : prev_opt_unchoke) |
4262 | 0 | { |
4263 | 0 | TORRENT_ASSERT(pi->optimistically_unchoked); |
4264 | 0 | auto* const p = static_cast<peer_connection*>(pi->connection); |
4265 | 0 | std::shared_ptr<torrent> t = p->associated_torrent().lock(); |
4266 | 0 | pi->optimistically_unchoked = false; |
4267 | 0 | m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1); |
4268 | 0 | t->choke_peer(*p); |
4269 | 0 | } |
4270 | | |
4271 | | // if we have too many unchoked peers now, we need to trigger the regular |
4272 | | // choking logic to choke some |
4273 | 1 | if (m_stats_counters[counters::num_unchoke_slots] |
4274 | 1 | < m_stats_counters[counters::num_peers_up_unchoked_all]) |
4275 | 0 | { |
4276 | 0 | m_unchoke_time_scaler = 0; |
4277 | 0 | } |
4278 | 1 | } |
4279 | | |
4280 | | void session_impl::try_connect_more_peers() |
4281 | 1 | { |
4282 | 1 | if (m_abort) return; |
4283 | | |
4284 | 1 | if (num_connections() >= m_settings.get_int(settings_pack::connections_limit)) |
4285 | 0 | return; |
4286 | | |
4287 | | // this is the maximum number of connections we will |
4288 | | // attempt this tick |
4289 | 1 | int max_connections = m_settings.get_int(settings_pack::connection_speed); |
4290 | | |
4291 | | // this loop will "hand out" connection_speed to the torrents, in a round |
4292 | | // robin fashion, so that every torrent is equally likely to connect to a |
4293 | | // peer |
4294 | | |
4295 | | // boost connections are connections made by torrent connection |
4296 | | // boost, which are done immediately on a tracker response. These |
4297 | | // connections needs to be deducted from the regular connection attempt |
4298 | | // quota for this tick |
4299 | 1 | if (m_boost_connections > 0) |
4300 | 0 | { |
4301 | 0 | if (m_boost_connections > max_connections) |
4302 | 0 | { |
4303 | 0 | m_boost_connections -= max_connections; |
4304 | 0 | max_connections = 0; |
4305 | 0 | } |
4306 | 0 | else |
4307 | 0 | { |
4308 | 0 | max_connections -= m_boost_connections; |
4309 | 0 | m_boost_connections = 0; |
4310 | 0 | } |
4311 | 0 | } |
4312 | | |
4313 | | // zero connections speeds are allowed, we just won't make any connections |
4314 | 1 | if (max_connections <= 0) return; |
4315 | | |
4316 | | // TODO: use a lower limit than m_settings.connections_limit |
4317 | | // to allocate the to 10% or so of connection slots for incoming |
4318 | | // connections |
4319 | | // cap this at max - 1, since we may add one below |
4320 | 1 | int const limit = std::min(m_settings.get_int(settings_pack::connections_limit) |
4321 | 1 | - num_connections(), std::numeric_limits<int>::max() - 1); |
4322 | | |
4323 | | // this logic is here to smooth out the number of new connection |
4324 | | // attempts over time, to prevent connecting a large number of |
4325 | | // sockets, wait 10 seconds, and then try again |
4326 | 1 | if (m_settings.get_bool(settings_pack::smooth_connects) && max_connections > (limit+1) / 2) |
4327 | 0 | max_connections = (limit + 1) / 2; |
4328 | | |
4329 | 1 | aux::vector<torrent*>& want_peers_download = m_torrent_lists[torrent_want_peers_download]; |
4330 | 1 | aux::vector<torrent*>& want_peers_finished = m_torrent_lists[torrent_want_peers_finished]; |
4331 | | |
4332 | | // if no torrent want any peers, just return |
4333 | 1 | if (want_peers_download.empty() && want_peers_finished.empty()) return; |
4334 | | |
4335 | | // if we don't have any connection attempt quota, return |
4336 | 0 | if (max_connections <= 0) return; |
4337 | | |
4338 | 0 | int steps_since_last_connect = 0; |
4339 | 0 | int const num_torrents = int(want_peers_finished.size() + want_peers_download.size()); |
4340 | 0 | for (;;) |
4341 | 0 | { |
4342 | 0 | if (m_next_downloading_connect_torrent >= int(want_peers_download.size())) |
4343 | 0 | m_next_downloading_connect_torrent = 0; |
4344 | |
|
4345 | 0 | if (m_next_finished_connect_torrent >= int(want_peers_finished.size())) |
4346 | 0 | m_next_finished_connect_torrent = 0; |
4347 | |
|
4348 | 0 | torrent* t = nullptr; |
4349 | | // there are prioritized torrents. Pick one of those |
4350 | 0 | while (!m_prio_torrents.empty()) |
4351 | 0 | { |
4352 | 0 | t = m_prio_torrents.front().first.lock().get(); |
4353 | 0 | --m_prio_torrents.front().second; |
4354 | 0 | if (m_prio_torrents.front().second > 0 |
4355 | 0 | && t != nullptr |
4356 | 0 | && t->want_peers()) break; |
4357 | 0 | m_prio_torrents.pop_front(); |
4358 | 0 | t = nullptr; |
4359 | 0 | } |
4360 | |
|
4361 | 0 | if (t == nullptr) |
4362 | 0 | { |
4363 | 0 | if ((m_download_connect_attempts >= m_settings.get_int( |
4364 | 0 | settings_pack::connect_seed_every_n_download) |
4365 | 0 | && !want_peers_finished.empty()) |
4366 | 0 | || want_peers_download.empty()) |
4367 | 0 | { |
4368 | | // pick a finished torrent to give a peer to |
4369 | 0 | t = want_peers_finished[m_next_finished_connect_torrent]; |
4370 | 0 | TORRENT_ASSERT(t->want_peers_finished()); |
4371 | 0 | m_download_connect_attempts = 0; |
4372 | 0 | ++m_next_finished_connect_torrent; |
4373 | 0 | } |
4374 | 0 | else |
4375 | 0 | { |
4376 | | // pick a downloading torrent to give a peer to |
4377 | 0 | t = want_peers_download[m_next_downloading_connect_torrent]; |
4378 | 0 | TORRENT_ASSERT(t->want_peers_download()); |
4379 | 0 | ++m_download_connect_attempts; |
4380 | 0 | ++m_next_downloading_connect_torrent; |
4381 | 0 | } |
4382 | 0 | } |
4383 | |
|
4384 | 0 | TORRENT_ASSERT(t->want_peers()); |
4385 | 0 | TORRENT_ASSERT(!t->is_torrent_paused()); |
4386 | |
|
4387 | 0 | if (t->try_connect_peer()) |
4388 | 0 | { |
4389 | 0 | --max_connections; |
4390 | 0 | steps_since_last_connect = 0; |
4391 | 0 | m_stats_counters.inc_stats_counter(counters::connection_attempts); |
4392 | 0 | } |
4393 | |
|
4394 | 0 | ++steps_since_last_connect; |
4395 | | |
4396 | | // if there are no more free connection slots, abort |
4397 | 0 | if (max_connections == 0) return; |
4398 | | // there are no more torrents that want peers |
4399 | 0 | if (want_peers_download.empty() && want_peers_finished.empty()) break; |
4400 | | // if we have gone a whole loop without |
4401 | | // handing out a single connection, break |
4402 | 0 | if (steps_since_last_connect > num_torrents + 1) break; |
4403 | | // maintain the global limit on number of connections |
4404 | 0 | if (num_connections() >= m_settings.get_int(settings_pack::connections_limit)) break; |
4405 | 0 | } |
4406 | 0 | } |
4407 | | |
4408 | | void session_impl::recalculate_unchoke_slots() |
4409 | 1.83k | { |
4410 | 1.83k | TORRENT_ASSERT(is_single_thread()); |
4411 | | |
4412 | 1.83k | time_point const now = aux::time_now(); |
4413 | 1.83k | time_duration const unchoke_interval = now - m_last_choke; |
4414 | 1.83k | m_last_choke = now; |
4415 | | |
4416 | | // if we unchoke everyone, skip this logic |
4417 | 1.83k | if (settings().get_int(settings_pack::choking_algorithm) == settings_pack::fixed_slots_choker |
4418 | 1.83k | && settings().get_int(settings_pack::unchoke_slots_limit) < 0) |
4419 | 0 | { |
4420 | 0 | m_stats_counters.set_value(counters::num_unchoke_slots, std::numeric_limits<int>::max()); |
4421 | 0 | return; |
4422 | 0 | } |
4423 | | |
4424 | | // build list of all peers that are |
4425 | | // unchokable. |
4426 | | // TODO: 3 there should be a pre-calculated list of all peers eligible for |
4427 | | // unchoking |
4428 | 1.83k | std::vector<peer_connection*> peers; |
4429 | 1.83k | for (auto i = m_connections.begin(); i != m_connections.end();) |
4430 | 0 | { |
4431 | 0 | std::shared_ptr<peer_connection> p = *i; |
4432 | 0 | TORRENT_ASSERT(p); |
4433 | 0 | ++i; |
4434 | 0 | torrent* const t = p->associated_torrent().lock().get(); |
4435 | 0 | torrent_peer* const pi = p->peer_info_struct(); |
4436 | |
|
4437 | 0 | if (p->ignore_unchoke_slots() || t == nullptr || pi == nullptr |
4438 | 0 | || pi->web_seed || t->is_paused()) |
4439 | 0 | { |
4440 | 0 | p->reset_choke_counters(); |
4441 | 0 | continue; |
4442 | 0 | } |
4443 | | |
4444 | 0 | if (!p->is_peer_interested() |
4445 | 0 | || p->is_disconnecting() |
4446 | 0 | || p->is_connecting()) |
4447 | 0 | { |
4448 | | // this peer is not unchokable. So, if it's unchoked |
4449 | | // already, make sure to choke it. |
4450 | 0 | if (p->is_choked()) |
4451 | 0 | { |
4452 | 0 | p->reset_choke_counters(); |
4453 | 0 | continue; |
4454 | 0 | } |
4455 | 0 | if (pi && pi->optimistically_unchoked) |
4456 | 0 | { |
4457 | 0 | m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1); |
4458 | 0 | pi->optimistically_unchoked = false; |
4459 | | // force a new optimistic unchoke |
4460 | 0 | m_optimistic_unchoke_time_scaler = 0; |
4461 | | // TODO: post a message to have this happen |
4462 | | // immediately instead of waiting for the next tick |
4463 | 0 | } |
4464 | 0 | t->choke_peer(*p); |
4465 | 0 | p->reset_choke_counters(); |
4466 | 0 | continue; |
4467 | 0 | } |
4468 | | |
4469 | 0 | peers.push_back(p.get()); |
4470 | 0 | } |
4471 | | |
4472 | 1.83k | int const allowed_upload_slots = unchoke_sort(peers |
4473 | 1.83k | , unchoke_interval, m_settings); |
4474 | | |
4475 | 1.83k | if (m_settings.get_int(settings_pack::choking_algorithm) == settings_pack::fixed_slots_choker) |
4476 | 1.83k | { |
4477 | 1.83k | int const upload_slots = get_int_setting(settings_pack::unchoke_slots_limit); |
4478 | 1.83k | m_stats_counters.set_value(counters::num_unchoke_slots, upload_slots); |
4479 | 1.83k | } |
4480 | 0 | else |
4481 | 0 | { |
4482 | 0 | m_stats_counters.set_value(counters::num_unchoke_slots |
4483 | 0 | , allowed_upload_slots); |
4484 | 0 | } |
4485 | | |
4486 | | #ifndef TORRENT_DISABLE_LOGGING |
4487 | | if (should_log()) |
4488 | | { |
4489 | | session_log("RECALCULATE UNCHOKE SLOTS: [ peers: %d " |
4490 | | "eligible-peers: %d" |
4491 | | " allowed-slots: %d ]" |
4492 | | , int(m_connections.size()) |
4493 | | , int(peers.size()) |
4494 | | , allowed_upload_slots); |
4495 | | } |
4496 | | #endif |
4497 | | |
4498 | 1.83k | int const unchoked_counter_optimistic |
4499 | 1.83k | = int(m_stats_counters[counters::num_peers_up_unchoked_optimistic]); |
4500 | 1.83k | int const num_opt_unchoke = (unchoked_counter_optimistic == 0) |
4501 | 1.83k | ? std::max(1, allowed_upload_slots / 5) : unchoked_counter_optimistic; |
4502 | | |
4503 | 1.83k | int unchoke_set_size = allowed_upload_slots - num_opt_unchoke; |
4504 | | |
4505 | | // go through all the peers and unchoke the first ones and choke |
4506 | | // all the other ones. |
4507 | 1.83k | for (auto p : peers) |
4508 | 0 | { |
4509 | 0 | TORRENT_ASSERT(p != nullptr); |
4510 | 0 | TORRENT_ASSERT(!p->ignore_unchoke_slots()); |
4511 | | |
4512 | | // this will update the m_uploaded_at_last_unchoke |
4513 | 0 | p->reset_choke_counters(); |
4514 | |
|
4515 | 0 | torrent* t = p->associated_torrent().lock().get(); |
4516 | 0 | TORRENT_ASSERT(t); |
4517 | |
|
4518 | 0 | if (unchoke_set_size > 0) |
4519 | 0 | { |
4520 | | // yes, this peer should be unchoked |
4521 | 0 | if (p->is_choked()) |
4522 | 0 | { |
4523 | 0 | if (!t->unchoke_peer(*p)) |
4524 | 0 | continue; |
4525 | 0 | } |
4526 | | |
4527 | 0 | --unchoke_set_size; |
4528 | |
|
4529 | 0 | TORRENT_ASSERT(p->peer_info_struct()); |
4530 | 0 | if (p->peer_info_struct()->optimistically_unchoked) |
4531 | 0 | { |
4532 | | // force a new optimistic unchoke |
4533 | | // since this one just got promoted into the |
4534 | | // proper unchoke set |
4535 | 0 | m_optimistic_unchoke_time_scaler = 0; |
4536 | 0 | p->peer_info_struct()->optimistically_unchoked = false; |
4537 | 0 | m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1); |
4538 | 0 | } |
4539 | 0 | } |
4540 | 0 | else |
4541 | 0 | { |
4542 | | // no, this peer should be choked |
4543 | 0 | TORRENT_ASSERT(p->peer_info_struct()); |
4544 | 0 | if (!p->is_choked() && !p->peer_info_struct()->optimistically_unchoked) |
4545 | 0 | t->choke_peer(*p); |
4546 | 0 | } |
4547 | 0 | } |
4548 | 1.83k | } |
4549 | | |
4550 | | std::shared_ptr<torrent> session_impl::delay_load_torrent(info_hash_t const& info_hash |
4551 | | , peer_connection* pc) |
4552 | 0 | { |
4553 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
4554 | 0 | for (auto& e : m_ses_extensions[plugins_all_idx]) |
4555 | 0 | { |
4556 | 0 | add_torrent_params p; |
4557 | 0 | if (e->on_unknown_torrent(info_hash, peer_connection_handle(pc->self()), p)) |
4558 | 0 | { |
4559 | 0 | error_code ec; |
4560 | 0 | torrent_handle handle = add_torrent(std::move(p), ec); |
4561 | |
|
4562 | 0 | return handle.native_handle(); |
4563 | 0 | } |
4564 | 0 | } |
4565 | | #else |
4566 | | TORRENT_UNUSED(pc); |
4567 | | TORRENT_UNUSED(info_hash); |
4568 | | #endif |
4569 | 0 | return std::shared_ptr<torrent>(); |
4570 | 0 | } |
4571 | | |
4572 | | // the return value from this function is valid only as long as the |
4573 | | // session is locked! |
4574 | | std::weak_ptr<torrent> session_impl::find_torrent(info_hash_t const& info_hash) const |
4575 | 1.83k | { |
4576 | 1.83k | TORRENT_ASSERT(is_single_thread()); |
4577 | | |
4578 | 1.83k | torrent* i = nullptr; |
4579 | 1.83k | info_hash.for_each([&](sha1_hash const& ih, protocol_version) |
4580 | 3.67k | { |
4581 | 3.67k | if (i == nullptr) i = m_torrents.find(ih); |
4582 | 3.67k | }); |
4583 | | #if TORRENT_USE_INVARIANT_CHECKS |
4584 | | for (auto const& te : m_torrents) |
4585 | | { |
4586 | | TORRENT_ASSERT(te); |
4587 | | } |
4588 | | #endif |
4589 | 1.83k | if (i != nullptr) return i->shared_from_this(); |
4590 | 1.83k | return std::weak_ptr<torrent>(); |
4591 | 1.83k | } |
4592 | | |
4593 | | void session_impl::insert_torrent(info_hash_t const& ih, std::shared_ptr<torrent> const& t) |
4594 | 1.83k | { |
4595 | 1.83k | m_torrents.insert(ih, t); |
4596 | 1.83k | t->added(); |
4597 | 1.83k | } |
4598 | | |
4599 | | void session_impl::update_torrent_info_hash(std::shared_ptr<torrent> const& t |
4600 | | , info_hash_t const& old_ih) |
4601 | 0 | { |
4602 | 0 | m_torrents.erase(old_ih); |
4603 | 0 | m_torrents.insert(t->info_hash(), t); |
4604 | 0 | } |
4605 | | |
4606 | | void session_impl::set_queue_position(torrent* me, queue_position_t p) |
4607 | 3.81k | { |
4608 | 3.81k | queue_position_t const current_pos = me->queue_position(); |
4609 | 3.81k | if (current_pos == p) return; |
4610 | | |
4611 | 3.10k | if (p >= queue_position_t{0} && current_pos == no_pos) |
4612 | 1.55k | { |
4613 | | // we're inserting the torrent into the download queue |
4614 | 1.55k | queue_position_t const last = m_download_queue.end_index(); |
4615 | 1.55k | if (p >= last) |
4616 | 1.55k | { |
4617 | 1.55k | m_download_queue.push_back(me); |
4618 | 1.55k | me->set_queue_position_impl(last); |
4619 | 1.55k | } |
4620 | 0 | else |
4621 | 0 | { |
4622 | 0 | m_download_queue.insert(m_download_queue.begin() + static_cast<int>(p), me); |
4623 | 0 | for (queue_position_t i = p; i < m_download_queue.end_index(); ++i) |
4624 | 0 | { |
4625 | 0 | m_download_queue[i]->set_queue_position_impl(i); |
4626 | 0 | } |
4627 | 0 | } |
4628 | 1.55k | } |
4629 | 1.55k | else if (p < queue_position_t{}) |
4630 | 1.55k | { |
4631 | | // we're removing the torrent from the download queue |
4632 | 1.55k | TORRENT_ASSERT(current_pos >= queue_position_t{0}); |
4633 | 1.55k | TORRENT_ASSERT(p == no_pos); |
4634 | 1.55k | TORRENT_ASSERT(m_download_queue[current_pos] == me); |
4635 | 1.55k | m_download_queue.erase(m_download_queue.begin() + static_cast<int>(current_pos)); |
4636 | 1.55k | me->set_queue_position_impl(no_pos); |
4637 | 1.55k | for (queue_position_t i = current_pos; i < m_download_queue.end_index(); ++i) |
4638 | 0 | { |
4639 | 0 | m_download_queue[i]->set_queue_position_impl(i); |
4640 | 0 | } |
4641 | 1.55k | } |
4642 | 0 | else if (p < current_pos) |
4643 | 0 | { |
4644 | | // we're moving the torrent up the queue |
4645 | 0 | torrent* tmp = me; |
4646 | 0 | for (queue_position_t i = p; i <= current_pos; ++i) |
4647 | 0 | { |
4648 | 0 | std::swap(m_download_queue[i], tmp); |
4649 | 0 | m_download_queue[i]->set_queue_position_impl(i); |
4650 | 0 | } |
4651 | 0 | TORRENT_ASSERT(tmp == me); |
4652 | 0 | } |
4653 | 0 | else if (p > current_pos) |
4654 | 0 | { |
4655 | | // we're moving the torrent down the queue |
4656 | 0 | p = std::min(p, prev(m_download_queue.end_index())); |
4657 | 0 | for (queue_position_t i = current_pos; i < p; ++i) |
4658 | 0 | { |
4659 | 0 | m_download_queue[i] = m_download_queue[next(i)]; |
4660 | 0 | m_download_queue[i]->set_queue_position_impl(i); |
4661 | 0 | } |
4662 | 0 | m_download_queue[p] = me; |
4663 | 0 | me->set_queue_position_impl(p); |
4664 | 0 | } |
4665 | | |
4666 | 3.10k | trigger_auto_manage(); |
4667 | 3.10k | } |
4668 | | |
4669 | | #if !defined TORRENT_DISABLE_ENCRYPTION |
4670 | | torrent const* session_impl::find_encrypted_torrent(sha1_hash const& info_hash |
4671 | | , sha1_hash const& xor_mask) |
4672 | 0 | { |
4673 | 0 | sha1_hash obfuscated = info_hash; |
4674 | 0 | obfuscated ^= xor_mask; |
4675 | |
|
4676 | 0 | return m_torrents.find_obfuscated(obfuscated); |
4677 | 0 | } |
4678 | | #endif |
4679 | | |
4680 | | #ifndef TORRENT_DISABLE_MUTABLE_TORRENTS |
4681 | | std::vector<std::shared_ptr<torrent>> session_impl::find_collection( |
4682 | | std::string const& collection) const |
4683 | 0 | { |
4684 | 0 | std::vector<std::shared_ptr<torrent>> ret; |
4685 | 0 | for (auto const& t : m_torrents) |
4686 | 0 | { |
4687 | 0 | if (!t) continue; |
4688 | 0 | std::vector<std::string> const& c = t->torrent_file().collections(); |
4689 | 0 | if (std::find(c.begin(), c.end(), collection) == c.end()) continue; |
4690 | 0 | ret.push_back(t); |
4691 | 0 | } |
4692 | 0 | return ret; |
4693 | 0 | } |
4694 | | #endif //TORRENT_DISABLE_MUTABLE_TORRENTS |
4695 | | |
4696 | | namespace { |
4697 | | |
4698 | | // returns true if lhs is a better disconnect candidate than rhs |
4699 | | bool compare_disconnect_torrent(std::shared_ptr<torrent> const& lhs |
4700 | | , std::shared_ptr<torrent> const& rhs) |
4701 | 0 | { |
4702 | | // a torrent with 0 peers is never a good disconnect candidate |
4703 | | // since there's nothing to disconnect |
4704 | 0 | if ((lhs->num_peers() == 0) != (rhs->num_peers() == 0)) |
4705 | 0 | return lhs->num_peers() != 0; |
4706 | | |
4707 | | // other than that, always prefer to disconnect peers from finished torrents |
4708 | | // in order to not harm downloading ones |
4709 | 0 | if (lhs->is_finished() != rhs->is_finished()) |
4710 | 0 | return lhs->is_finished(); |
4711 | | |
4712 | 0 | return lhs->num_peers() > rhs->num_peers(); |
4713 | 0 | } |
4714 | | |
4715 | | } // anonymous namespace |
4716 | | |
4717 | | std::weak_ptr<torrent> session_impl::find_disconnect_candidate_torrent() const |
4718 | 0 | { |
4719 | 0 | auto const i = std::min_element(m_torrents.begin(), m_torrents.end() |
4720 | 0 | , &compare_disconnect_torrent); |
4721 | |
|
4722 | 0 | TORRENT_ASSERT(i != m_torrents.end()); |
4723 | 0 | if (i == m_torrents.end()) return std::shared_ptr<torrent>(); |
4724 | | |
4725 | 0 | return *i; |
4726 | 0 | } |
4727 | | |
4728 | | #ifndef TORRENT_DISABLE_LOGGING |
4729 | | bool session_impl::should_log() const |
4730 | | { |
4731 | | return m_alerts.should_post<log_alert>(); |
4732 | | } |
4733 | | |
4734 | | TORRENT_FORMAT(2,3) |
4735 | | void session_impl::session_log(char const* fmt, ...) const noexcept try |
4736 | | { |
4737 | | if (!m_alerts.should_post<log_alert>()) return; |
4738 | | |
4739 | | va_list v; |
4740 | | va_start(v, fmt); |
4741 | | m_alerts.emplace_alert<log_alert>(fmt, v); |
4742 | | va_end(v); |
4743 | | } |
4744 | | catch (std::exception const&) {} |
4745 | | #endif |
4746 | | |
4747 | | void session_impl::get_torrent_status(std::vector<torrent_status>* ret |
4748 | | , std::function<bool(torrent_status const&)> const& pred |
4749 | | , status_flags_t const flags) const |
4750 | 0 | { |
4751 | 0 | for (auto const& t : m_torrents) |
4752 | 0 | { |
4753 | 0 | if (t->is_aborted()) continue; |
4754 | 0 | torrent_status st; |
4755 | 0 | t->status(&st, flags); |
4756 | 0 | if (!pred(st)) continue; |
4757 | 0 | ret->push_back(std::move(st)); |
4758 | 0 | } |
4759 | 0 | } |
4760 | | |
4761 | | void session_impl::refresh_torrent_status(std::vector<torrent_status>* ret |
4762 | | , status_flags_t const flags) const |
4763 | 0 | { |
4764 | 0 | for (auto& st : *ret) |
4765 | 0 | { |
4766 | 0 | auto t = st.handle.m_torrent.lock(); |
4767 | 0 | if (!t) continue; |
4768 | 0 | t->status(&st, flags); |
4769 | 0 | } |
4770 | 0 | } |
4771 | | |
4772 | | void session_impl::post_torrent_updates(status_flags_t const flags) |
4773 | 0 | { |
4774 | 0 | INVARIANT_CHECK; |
4775 | |
|
4776 | 0 | TORRENT_ASSERT(is_single_thread()); |
4777 | |
|
4778 | 0 | std::vector<torrent*>& state_updates |
4779 | 0 | = m_torrent_lists[aux::session_impl::torrent_state_updates]; |
4780 | |
|
4781 | 0 | #if TORRENT_USE_ASSERTS |
4782 | 0 | m_posting_torrent_updates = true; |
4783 | 0 | #endif |
4784 | |
|
4785 | 0 | std::vector<torrent_status> status; |
4786 | 0 | status.reserve(state_updates.size()); |
4787 | | |
4788 | | // TODO: it might be a nice feature here to limit the number of torrents |
4789 | | // to send in a single update. By just posting the first n torrents, they |
4790 | | // would nicely be round-robined because the torrent lists are always |
4791 | | // pushed back. Perhaps the status_update_alert could even have a fixed |
4792 | | // array of n entries rather than a vector, to further improve memory |
4793 | | // locality. |
4794 | 0 | for (auto& t : state_updates) |
4795 | 0 | { |
4796 | 0 | TORRENT_ASSERT(t->m_links[aux::session_impl::torrent_state_updates].in_list()); |
4797 | 0 | status.emplace_back(); |
4798 | | // querying accurate download counters may require |
4799 | | // the torrent to be loaded. Loading a torrent, and evicting another |
4800 | | // one will lead to calling state_updated(), which screws with |
4801 | | // this list while we're working on it, and break things |
4802 | 0 | t->status(&status.back(), flags); |
4803 | 0 | t->clear_in_state_update(); |
4804 | 0 | } |
4805 | 0 | state_updates.clear(); |
4806 | |
|
4807 | 0 | #if TORRENT_USE_ASSERTS |
4808 | 0 | m_posting_torrent_updates = false; |
4809 | 0 | #endif |
4810 | |
|
4811 | 0 | m_alerts.emplace_alert<state_update_alert>(std::move(status)); |
4812 | 0 | } |
4813 | | |
4814 | | void session_impl::post_session_stats() |
4815 | 0 | { |
4816 | 0 | if (!m_posted_stats_header) |
4817 | 0 | { |
4818 | 0 | m_posted_stats_header = true; |
4819 | 0 | m_alerts.emplace_alert<session_stats_header_alert>(); |
4820 | 0 | } |
4821 | 0 | m_disk_thread->update_stats_counters(m_stats_counters); |
4822 | |
|
4823 | 0 | #ifndef TORRENT_DISABLE_DHT |
4824 | 0 | if (m_dht) |
4825 | 0 | m_dht->update_stats_counters(m_stats_counters); |
4826 | 0 | #endif |
4827 | |
|
4828 | 0 | m_stats_counters.set_value(counters::limiter_up_queue |
4829 | 0 | , m_upload_rate.queue_size()); |
4830 | 0 | m_stats_counters.set_value(counters::limiter_down_queue |
4831 | 0 | , m_download_rate.queue_size()); |
4832 | |
|
4833 | 0 | m_stats_counters.set_value(counters::limiter_up_bytes |
4834 | 0 | , m_upload_rate.queued_bytes()); |
4835 | 0 | m_stats_counters.set_value(counters::limiter_down_bytes |
4836 | 0 | , m_download_rate.queued_bytes()); |
4837 | |
|
4838 | 0 | m_alerts.emplace_alert<session_stats_alert>(m_stats_counters); |
4839 | 0 | } |
4840 | | |
4841 | | void session_impl::post_dht_stats() |
4842 | 0 | { |
4843 | 0 | #ifndef TORRENT_DISABLE_DHT |
4844 | 0 | std::vector<dht::dht_status> dht_stats; |
4845 | 0 | if (m_dht) |
4846 | 0 | dht_stats = m_dht->dht_status(); |
4847 | |
|
4848 | 0 | if (dht_stats.empty()) |
4849 | 0 | { |
4850 | | // for backwards compatibility, still post an empty alert if we don't |
4851 | | // have any active DHT nodes |
4852 | 0 | m_alerts.emplace_alert<dht_stats_alert>(std::vector<dht_routing_bucket>{} |
4853 | 0 | , std::vector<dht_lookup>{}, dht::node_id{}, udp::endpoint{}); |
4854 | 0 | } |
4855 | 0 | else |
4856 | 0 | { |
4857 | 0 | for (auto& s : dht_stats) |
4858 | 0 | { |
4859 | 0 | m_alerts.emplace_alert<dht_stats_alert>( |
4860 | 0 | std::move(s.table), std::move(s.requests) |
4861 | 0 | , s.our_id, s.local_endpoint); |
4862 | 0 | } |
4863 | 0 | } |
4864 | 0 | #endif |
4865 | 0 | } |
4866 | | |
4867 | | std::vector<torrent_handle> session_impl::get_torrents() const |
4868 | 0 | { |
4869 | 0 | std::vector<torrent_handle> ret; |
4870 | |
|
4871 | 0 | for (auto const& i : m_torrents) |
4872 | 0 | { |
4873 | 0 | if (i->is_aborted()) continue; |
4874 | 0 | ret.push_back(torrent_handle(i)); |
4875 | 0 | } |
4876 | 0 | return ret; |
4877 | 0 | } |
4878 | | |
4879 | | torrent_handle session_impl::find_torrent_handle(info_hash_t const& info_hash) |
4880 | 0 | { |
4881 | 0 | return torrent_handle(find_torrent(info_hash)); |
4882 | 0 | } |
4883 | | |
4884 | | void session_impl::async_add_torrent(add_torrent_params* params) |
4885 | 1.83k | { |
4886 | 1.83k | std::unique_ptr<add_torrent_params> holder(params); |
4887 | 1.83k | error_code ec; |
4888 | 1.83k | add_torrent(std::move(*params), ec); |
4889 | 1.83k | } |
4890 | | |
4891 | | #ifndef TORRENT_DISABLE_EXTENSIONS |
4892 | | void session_impl::add_extensions_to_torrent( |
4893 | | std::shared_ptr<torrent> const& torrent_ptr, client_data_t const userdata) |
4894 | 1.83k | { |
4895 | 1.83k | for (auto& e : m_ses_extensions[plugins_all_idx]) |
4896 | 5.50k | { |
4897 | 5.50k | std::shared_ptr<torrent_plugin> tp(e->new_torrent( |
4898 | 5.50k | torrent_ptr->get_handle(), userdata)); |
4899 | 5.50k | if (tp) torrent_ptr->add_extension(std::move(tp)); |
4900 | 5.50k | } |
4901 | 1.83k | } |
4902 | | #endif |
4903 | | |
4904 | | torrent_handle session_impl::add_torrent(add_torrent_params&& params |
4905 | | , error_code& ec) |
4906 | 1.83k | { |
4907 | 1.83k | std::shared_ptr<torrent> torrent_ptr; |
4908 | | |
4909 | | // in case there's an error, make sure to abort the torrent before leaving |
4910 | | // the scope |
4911 | 1.83k | auto abort_torrent = aux::scope_end([&]{ if (torrent_ptr) torrent_ptr->abort(); }); |
4912 | | |
4913 | | // copy the most important fields from params to pass back in the |
4914 | | // add_torrent_alert |
4915 | 1.83k | add_torrent_params alert_params; |
4916 | 1.83k | alert_params.flags = params.flags; |
4917 | 1.83k | alert_params.ti = params.ti; |
4918 | 1.83k | alert_params.name = params.name; |
4919 | 1.83k | alert_params.save_path = params.save_path; |
4920 | 1.83k | alert_params.userdata = params.userdata; |
4921 | 1.83k | alert_params.trackerid = params.trackerid; |
4922 | | |
4923 | 1.83k | #ifndef TORRENT_DISABLE_EXTENSIONS |
4924 | 1.83k | auto extensions = std::move(params.extensions); |
4925 | 1.83k | auto const userdata = params.userdata; |
4926 | 1.83k | #endif |
4927 | | |
4928 | 1.83k | auto const flags = params.flags; |
4929 | | |
4930 | 1.83k | info_hash_t info_hash; |
4931 | 1.83k | bool added; |
4932 | 1.83k | std::tie(torrent_ptr, info_hash, added) = add_torrent_impl(std::move(params), ec); |
4933 | | |
4934 | 1.83k | alert_params.info_hashes = info_hash; |
4935 | | |
4936 | 1.83k | torrent_handle handle(torrent_ptr); |
4937 | | |
4938 | 1.83k | if (!torrent_ptr) |
4939 | 0 | { |
4940 | 0 | m_alerts.emplace_alert<add_torrent_alert>(handle, std::move(alert_params), ec); |
4941 | 0 | return handle; |
4942 | 0 | } |
4943 | | |
4944 | 1.83k | TORRENT_ASSERT(info_hash.has_v1() || info_hash.has_v2()); |
4945 | | |
4946 | 1.83k | #if TORRENT_ABI_VERSION == 1 |
4947 | 1.83k | if (m_alerts.should_post<torrent_added_alert>()) |
4948 | 1 | m_alerts.emplace_alert<torrent_added_alert>(handle); |
4949 | 1.83k | #endif |
4950 | | |
4951 | | // if this was an existing torrent, we can't start it again, or add |
4952 | | // another set of plugins etc. we're done |
4953 | 1.83k | if (!added) |
4954 | 0 | { |
4955 | 0 | abort_torrent.disarm(); |
4956 | 0 | m_alerts.emplace_alert<add_torrent_alert>(handle, std::move(alert_params), ec); |
4957 | 0 | return handle; |
4958 | 0 | } |
4959 | | |
4960 | 1.83k | torrent_ptr->set_ip_filter(m_ip_filter); |
4961 | 1.83k | torrent_ptr->start(); |
4962 | | |
4963 | 1.83k | #ifndef TORRENT_DISABLE_EXTENSIONS |
4964 | 1.83k | for (auto& ext : extensions) |
4965 | 0 | { |
4966 | 0 | std::shared_ptr<torrent_plugin> tp(ext(handle, userdata)); |
4967 | 0 | if (tp) torrent_ptr->add_extension(std::move(tp)); |
4968 | 0 | } |
4969 | | |
4970 | 1.83k | add_extensions_to_torrent(torrent_ptr, userdata); |
4971 | 1.83k | #endif |
4972 | | |
4973 | 1.83k | TORRENT_ASSERT(info_hash == torrent_ptr->torrent_file().info_hashes()); |
4974 | 1.83k | insert_torrent(info_hash, torrent_ptr); |
4975 | | |
4976 | 1.83k | m_alerts.emplace_alert<add_torrent_alert>(handle, std::move(alert_params), ec); |
4977 | | |
4978 | | // once we successfully add the torrent, we can disarm the abort action |
4979 | 1.83k | abort_torrent.disarm(); |
4980 | | |
4981 | | // recalculate auto-managed torrents sooner (or put it off) |
4982 | | // if another torrent will be added within one second from now |
4983 | | // we want to put it off again anyway. So that while we're adding |
4984 | | // a boat load of torrents, we postpone the recalculation until |
4985 | | // we're done adding them all (since it's kind of an expensive operation) |
4986 | 1.83k | if (flags & torrent_flags::auto_managed) |
4987 | 703 | { |
4988 | 703 | const int max_downloading = settings().get_int(settings_pack::active_downloads); |
4989 | 703 | const int max_seeds = settings().get_int(settings_pack::active_seeds); |
4990 | 703 | const int max_active = settings().get_int(settings_pack::active_limit); |
4991 | | |
4992 | 703 | const int num_downloading |
4993 | 703 | = int(torrent_list(session_interface::torrent_downloading_auto_managed).size()); |
4994 | 703 | const int num_seeds |
4995 | 703 | = int(torrent_list(session_interface::torrent_seeding_auto_managed).size()); |
4996 | 703 | const int num_active = num_downloading + num_seeds; |
4997 | | |
4998 | | // there's no point in triggering the auto manage logic early if we |
4999 | | // don't have a reason to believe anything will change. It's kind of |
5000 | | // expensive. |
5001 | 703 | if ((num_downloading < max_downloading |
5002 | 703 | || num_seeds < max_seeds) |
5003 | 703 | && num_active < max_active) |
5004 | 703 | { |
5005 | 703 | trigger_auto_manage(); |
5006 | 703 | } |
5007 | 703 | } |
5008 | | |
5009 | 1.83k | return handle; |
5010 | 1.83k | } |
5011 | | |
5012 | | std::tuple<std::shared_ptr<torrent>, info_hash_t, bool> |
5013 | | session_impl::add_torrent_impl(add_torrent_params&& params, error_code& ec) |
5014 | 1.83k | { |
5015 | 1.83k | TORRENT_ASSERT(!params.save_path.empty()); |
5016 | | |
5017 | 1.83k | using ptr_t = std::shared_ptr<torrent>; |
5018 | 1.83k | using ret_t = std::tuple<std::shared_ptr<torrent>, info_hash_t, bool>; |
5019 | | |
5020 | 1.83k | #if TORRENT_ABI_VERSION == 1 |
5021 | 1.83k | if (string_begins_no_case("magnet:", params.url.c_str())) |
5022 | 0 | { |
5023 | 0 | parse_magnet_uri(params.url, params, ec); |
5024 | 0 | if (ec) return ret_t{ptr_t(), params.info_hashes, false}; |
5025 | 0 | params.url.clear(); |
5026 | 0 | } |
5027 | 1.83k | #endif |
5028 | | |
5029 | 1.83k | if (params.ti && !params.ti->is_valid()) |
5030 | 0 | { |
5031 | 0 | ec = errors::no_metadata; |
5032 | 0 | return ret_t{ptr_t(), params.info_hashes, false}; |
5033 | 0 | } |
5034 | | |
5035 | 1.83k | if (params.ti && params.ti->is_valid() && params.ti->num_files() == 0) |
5036 | 0 | { |
5037 | 0 | ec = errors::no_files_in_torrent; |
5038 | 0 | return ret_t{ptr_t(), params.info_hashes, false}; |
5039 | 0 | } |
5040 | | |
5041 | 1.83k | if (params.ti |
5042 | 1.83k | && ((params.info_hashes.has_v1() && params.info_hashes.v1 != params.ti->info_hashes().v1) |
5043 | 1.83k | || (params.info_hashes.has_v2() && params.info_hashes.v2 != params.ti->info_hashes().v2) |
5044 | 1.83k | )) |
5045 | 0 | { |
5046 | 0 | ec = errors::mismatching_info_hash; |
5047 | 0 | return ret_t{ptr_t(), params.info_hashes, false}; |
5048 | 0 | } |
5049 | | |
5050 | 1.83k | #ifndef TORRENT_DISABLE_DHT |
5051 | | // add params.dht_nodes to the DHT, if enabled |
5052 | 1.83k | for (auto const& n : params.dht_nodes) |
5053 | 0 | add_dht_node_name(n); |
5054 | | |
5055 | 1.83k | if (params.ti) |
5056 | 1.83k | { |
5057 | 1.83k | for (auto const& n : params.ti->nodes()) |
5058 | 0 | add_dht_node_name(n); |
5059 | 1.83k | } |
5060 | 1.83k | #endif |
5061 | | |
5062 | 1.83k | INVARIANT_CHECK; |
5063 | | |
5064 | 1.83k | if (is_aborted()) |
5065 | 0 | { |
5066 | 0 | ec = errors::session_is_closing; |
5067 | 0 | return ret_t{ptr_t(), params.info_hashes, false}; |
5068 | 0 | } |
5069 | | |
5070 | | // figure out the info hash of the torrent and make sure |
5071 | | // params.info_hashes is set correctly |
5072 | 1.83k | if (params.ti) |
5073 | 1.83k | { |
5074 | 1.83k | params.info_hashes = params.ti->info_hashes(); |
5075 | 1.83k | #if TORRENT_ABI_VERSION < 3 |
5076 | 1.83k | params.info_hash = params.info_hashes.get_best(); |
5077 | 1.83k | #endif |
5078 | 1.83k | } |
5079 | | |
5080 | 1.83k | if (!params.info_hashes.has_v1() && !params.info_hashes.has_v2()) |
5081 | 0 | { |
5082 | 0 | ec = errors::missing_info_hash_in_uri; |
5083 | 0 | return ret_t{ptr_t(), params.info_hashes, false}; |
5084 | 0 | } |
5085 | | |
5086 | | // is the torrent already active? |
5087 | 1.83k | std::shared_ptr<torrent> torrent_ptr = find_torrent(params.info_hashes).lock(); |
5088 | | |
5089 | 1.83k | if (torrent_ptr) |
5090 | 0 | { |
5091 | 0 | if (!(params.flags & torrent_flags::duplicate_is_error)) |
5092 | 0 | return ret_t{std::move(torrent_ptr), params.info_hashes, false}; |
5093 | | |
5094 | 0 | ec = errors::duplicate_torrent; |
5095 | 0 | return ret_t{ptr_t(), params.info_hashes, false}; |
5096 | 0 | } |
5097 | | |
5098 | | // make sure we have enough memory in the torrent lists up-front, |
5099 | | // since when torrents changes states, we cannot allocate memory that |
5100 | | // might fail. |
5101 | 1.83k | size_t const num_torrents = m_torrents.size(); |
5102 | 1.83k | for (auto& l : m_torrent_lists) |
5103 | 14.6k | { |
5104 | 14.6k | l.reserve(num_torrents + 1); |
5105 | 14.6k | } |
5106 | | |
5107 | 1.83k | try |
5108 | 1.83k | { |
5109 | 1.83k | torrent_ptr = std::make_shared<torrent>(*this, m_paused, std::move(params)); |
5110 | 1.83k | torrent_ptr->set_queue_position(m_download_queue.end_index()); |
5111 | 1.83k | } |
5112 | 1.83k | catch (system_error const& e) |
5113 | 1.83k | { |
5114 | 0 | ec = e.code(); |
5115 | 0 | return ret_t{ptr_t(), params.info_hashes, false}; |
5116 | 0 | } |
5117 | | |
5118 | | // it's fine to copy this moved-from info_hash_t object, since its move |
5119 | | // construction is just a copy. |
5120 | 1.83k | return ret_t{std::move(torrent_ptr), params.info_hashes, true}; |
5121 | 1.83k | } |
5122 | | |
5123 | | void session_impl::update_outgoing_interfaces() |
5124 | 1.83k | { |
5125 | 1.83k | std::string const net_interfaces = m_settings.get_str(settings_pack::outgoing_interfaces); |
5126 | | |
5127 | | // declared in string_util.hpp |
5128 | 1.83k | parse_comma_separated_string(net_interfaces, m_outgoing_interfaces); |
5129 | | |
5130 | | #ifndef TORRENT_DISABLE_LOGGING |
5131 | | if (!net_interfaces.empty() && m_outgoing_interfaces.empty()) |
5132 | | { |
5133 | | session_log("ERROR: failed to parse outgoing interface list: %s" |
5134 | | , net_interfaces.c_str()); |
5135 | | } |
5136 | | #endif |
5137 | 1.83k | } |
5138 | | |
5139 | | tcp::endpoint session_impl::bind_outgoing_socket(socket_type& s |
5140 | | , address const& remote_address, error_code& ec) const |
5141 | 0 | { |
5142 | 0 | tcp::endpoint bind_ep(address_v4(), 0); |
5143 | 0 | if (m_settings.get_int(settings_pack::outgoing_port) > 0) |
5144 | 0 | { |
5145 | | #ifdef TORRENT_WINDOWS |
5146 | | s.set_option(exclusive_address_use(true), ec); |
5147 | | #else |
5148 | 0 | s.set_option(tcp::acceptor::reuse_address(true), ec); |
5149 | 0 | #endif |
5150 | | // ignore errors because the underlying socket may not |
5151 | | // be opened yet. This happens when we're routing through |
5152 | | // a proxy. In that case, we don't yet know the address of |
5153 | | // the proxy server, and more importantly, we don't know |
5154 | | // the address family of its address. This means we can't |
5155 | | // open the socket yet. The socks abstraction layer defers |
5156 | | // opening it. |
5157 | 0 | ec.clear(); |
5158 | 0 | bind_ep.port(std::uint16_t(next_port())); |
5159 | 0 | } |
5160 | |
|
5161 | 0 | if (is_utp(s)) |
5162 | 0 | { |
5163 | | // TODO: factor out this logic into a separate function for unit |
5164 | | // testing |
5165 | |
|
5166 | 0 | utp_socket_impl* impl = nullptr; |
5167 | 0 | transport ssl = transport::plaintext; |
5168 | 0 | #if TORRENT_USE_SSL |
5169 | 0 | if (boost::get<ssl_stream<utp_stream>>(&s) != nullptr) |
5170 | 0 | { |
5171 | 0 | impl = boost::get<ssl_stream<utp_stream>>(s).next_layer().get_impl(); |
5172 | 0 | ssl = transport::ssl; |
5173 | 0 | } |
5174 | 0 | else |
5175 | 0 | #endif |
5176 | 0 | impl = boost::get<utp_stream>(s).get_impl(); |
5177 | |
|
5178 | 0 | std::vector<std::shared_ptr<listen_socket_t>> with_gateways; |
5179 | 0 | std::shared_ptr<listen_socket_t> match; |
5180 | 0 | for (auto& ls : m_listen_sockets) |
5181 | 0 | { |
5182 | | // this is almost, but not quite, like can_route() |
5183 | 0 | if (!(ls->flags & listen_socket_t::proxy) |
5184 | 0 | && is_v4(ls->local_endpoint) != remote_address.is_v4()) |
5185 | 0 | continue; |
5186 | 0 | if (ls->ssl != ssl) continue; |
5187 | 0 | if (!(ls->flags & listen_socket_t::local_network)) |
5188 | 0 | with_gateways.push_back(ls); |
5189 | |
|
5190 | 0 | if (ls->flags & listen_socket_t::proxy |
5191 | 0 | || match_addr_mask(ls->local_endpoint.address(), remote_address, ls->netmask)) |
5192 | 0 | { |
5193 | | // is this better than the previous match? |
5194 | 0 | match = ls; |
5195 | 0 | } |
5196 | 0 | } |
5197 | 0 | if (!match && !with_gateways.empty()) |
5198 | 0 | match = with_gateways[random(std::uint32_t(with_gateways.size() - 1))]; |
5199 | |
|
5200 | 0 | if (match) |
5201 | 0 | { |
5202 | 0 | impl->m_sock = match; |
5203 | 0 | return match->local_endpoint; |
5204 | 0 | } |
5205 | 0 | ec.assign(boost::system::errc::not_supported, generic_category()); |
5206 | 0 | return {}; |
5207 | 0 | } |
5208 | | |
5209 | 0 | if (!m_outgoing_interfaces.empty()) |
5210 | 0 | { |
5211 | 0 | if (m_interface_index >= m_outgoing_interfaces.size()) m_interface_index = 0; |
5212 | 0 | std::string const& ifname = m_outgoing_interfaces[m_interface_index++]; |
5213 | |
|
5214 | 0 | bind_ep.address(bind_socket_to_device(m_io_context, s |
5215 | 0 | , remote_address.is_v4() ? tcp::v4() : tcp::v6() |
5216 | 0 | , ifname.c_str(), bind_ep.port(), ec)); |
5217 | 0 | return bind_ep; |
5218 | 0 | } |
5219 | | |
5220 | | // if we're not binding to a specific interface, bind |
5221 | | // to the same protocol family as the target endpoint |
5222 | 0 | if (bind_ep.address().is_unspecified()) |
5223 | 0 | { |
5224 | 0 | if (remote_address.is_v6()) |
5225 | 0 | bind_ep.address(address_v6::any()); |
5226 | 0 | else |
5227 | 0 | bind_ep.address(address_v4::any()); |
5228 | 0 | } |
5229 | |
|
5230 | 0 | s.bind(bind_ep, ec); |
5231 | 0 | return bind_ep; |
5232 | 0 | } |
5233 | | |
5234 | | // verify that ``addr``s interface allows incoming connections |
5235 | | bool session_impl::verify_incoming_interface(address const& addr) |
5236 | 0 | { |
5237 | 0 | auto const iter = std::find_if(m_listen_sockets.begin(), m_listen_sockets.end() |
5238 | 0 | , [&addr](std::shared_ptr<listen_socket_t> const& s) |
5239 | 0 | { return s->local_endpoint.address() == addr; }); |
5240 | 0 | return iter == m_listen_sockets.end() |
5241 | 0 | ? false |
5242 | 0 | : bool((*iter)->flags & listen_socket_t::accept_incoming); |
5243 | 0 | } |
5244 | | |
5245 | | // verify that the given local address satisfies the requirements of |
5246 | | // the outgoing interfaces. i.e. that one of the allowed outgoing |
5247 | | // interfaces has this address. For uTP sockets, which are all backed |
5248 | | // by an unconnected udp socket, we won't be able to tell what local |
5249 | | // address is used for this peer's packets, in that case, just make |
5250 | | // sure one of the allowed interfaces exists and maybe that it's the |
5251 | | // default route. For systems that have SO_BINDTODEVICE, it should be |
5252 | | // enough to just know that one of the devices exist |
5253 | | bool session_impl::verify_bound_address(address const& addr, bool utp |
5254 | | , error_code& ec) |
5255 | 0 | { |
5256 | 0 | TORRENT_UNUSED(utp); |
5257 | | |
5258 | | // we have specific outgoing interfaces specified. Make sure the |
5259 | | // local endpoint for this socket is bound to one of the allowed |
5260 | | // interfaces. the list can be a mixture of interfaces and IP |
5261 | | // addresses. |
5262 | 0 | for (auto const& s : m_outgoing_interfaces) |
5263 | 0 | { |
5264 | 0 | error_code err; |
5265 | 0 | address const ip = make_address(s.c_str(), err); |
5266 | 0 | if (err) continue; |
5267 | 0 | if (ip == addr) return true; |
5268 | 0 | } |
5269 | | |
5270 | | // we didn't find the address as an IP in the interface list. Now, |
5271 | | // resolve which device (if any) has this IP address. |
5272 | 0 | std::string const device = device_for_address(addr, m_io_context, ec); |
5273 | 0 | if (ec) return false; |
5274 | | |
5275 | | // if no device was found to have this address, we fail |
5276 | 0 | if (device.empty()) return false; |
5277 | | |
5278 | 0 | return std::any_of(m_outgoing_interfaces.begin(), m_outgoing_interfaces.end() |
5279 | 0 | , [&device](std::string const& s) { return s == device; }); |
5280 | 0 | } |
5281 | | |
5282 | | bool session_impl::has_lsd() const |
5283 | 198 | { |
5284 | 198 | return std::any_of(m_listen_sockets.begin(), m_listen_sockets.end() |
5285 | 198 | , [](std::shared_ptr<listen_socket_t> const& s) { return bool(s->lsd); }); |
5286 | 198 | } |
5287 | | |
5288 | | void session_impl::remove_torrent(const torrent_handle& h |
5289 | | , remove_flags_t const options) |
5290 | 0 | { |
5291 | 0 | INVARIANT_CHECK; |
5292 | |
|
5293 | 0 | std::shared_ptr<torrent> tptr = h.m_torrent.lock(); |
5294 | 0 | if (!tptr) return; |
5295 | 0 | if (!tptr->is_added()) return; |
5296 | | |
5297 | 0 | remove_torrent_impl(tptr, options); |
5298 | |
|
5299 | 0 | tptr->abort(); |
5300 | 0 | } |
5301 | | |
5302 | | void session_impl::remove_torrent_impl(std::shared_ptr<torrent> tptr |
5303 | | , remove_flags_t const options) |
5304 | 0 | { |
5305 | 0 | m_torrents.erase(tptr->info_hash()); |
5306 | |
|
5307 | 0 | torrent& t = *tptr; |
5308 | 0 | if (options) |
5309 | 0 | { |
5310 | 0 | if (!t.delete_files(options)) |
5311 | 0 | { |
5312 | 0 | if (m_alerts.should_post<torrent_delete_failed_alert>()) |
5313 | 0 | m_alerts.emplace_alert<torrent_delete_failed_alert>(t.get_handle() |
5314 | 0 | , error_code(), t.torrent_file().info_hashes()); |
5315 | 0 | } |
5316 | 0 | } |
5317 | |
|
5318 | 0 | tptr->update_gauge(); |
5319 | 0 | tptr->removed(); |
5320 | |
|
5321 | 0 | #ifndef TORRENT_DISABLE_DHT |
5322 | 0 | if (m_next_dht_torrent == m_torrents.size()) |
5323 | 0 | m_next_dht_torrent = 0; |
5324 | 0 | #endif |
5325 | 0 | if (m_next_lsd_torrent == m_torrents.size()) |
5326 | 0 | m_next_lsd_torrent = 0; |
5327 | | |
5328 | | // this torrent may open up a slot for a queued torrent |
5329 | 0 | trigger_auto_manage(); |
5330 | 0 | } |
5331 | | |
5332 | | #if TORRENT_ABI_VERSION == 1 |
5333 | | |
5334 | | void session_impl::update_ssl_listen() |
5335 | 1.83k | { |
5336 | 1.83k | INVARIANT_CHECK; |
5337 | | |
5338 | | // this function maps the previous functionality of just setting the ssl |
5339 | | // listen port in order to enable the ssl listen sockets, to the new |
5340 | | // mechanism where SSL sockets are specified in listen_interfaces. |
5341 | 1.83k | std::vector<std::string> ignore; |
5342 | 1.83k | auto current_ifaces = parse_listen_interfaces( |
5343 | 1.83k | m_settings.get_str(settings_pack::listen_interfaces), ignore); |
5344 | | // these are the current interfaces we have, first remove all the SSL |
5345 | | // interfaces |
5346 | 1.83k | current_ifaces.erase(std::remove_if(current_ifaces.begin(), current_ifaces.end() |
5347 | 1.83k | , std::bind(&listen_interface_t::ssl, _1)), current_ifaces.end()); |
5348 | | |
5349 | 1.83k | int const ssl_listen_port = m_settings.get_int(settings_pack::ssl_listen); |
5350 | | |
5351 | | // setting a port of 0 means to disable listening on SSL, so just update |
5352 | | // the interface list with the new list, and we're done |
5353 | 1.83k | if (ssl_listen_port == 0) |
5354 | 1.83k | { |
5355 | 1.83k | m_settings.set_str(settings_pack::listen_interfaces |
5356 | 1.83k | , print_listen_interfaces(current_ifaces)); |
5357 | 1.83k | return; |
5358 | 1.83k | } |
5359 | | |
5360 | 0 | std::vector<listen_interface_t> new_ifaces; |
5361 | 0 | std::transform(current_ifaces.begin(), current_ifaces.end() |
5362 | 0 | , std::back_inserter(new_ifaces), [](listen_interface_t in) |
5363 | 0 | { in.ssl = true; return in; }); |
5364 | |
|
5365 | 0 | current_ifaces.insert(current_ifaces.end(), new_ifaces.begin(), new_ifaces.end()); |
5366 | |
|
5367 | 0 | m_settings.set_str(settings_pack::listen_interfaces |
5368 | 0 | , print_listen_interfaces(current_ifaces)); |
5369 | 0 | } |
5370 | | #endif // TORRENT_ABI_VERSION |
5371 | | |
5372 | | void session_impl::update_listen_interfaces() |
5373 | 1.83k | { |
5374 | 1.83k | INVARIANT_CHECK; |
5375 | | |
5376 | 1.83k | std::string const net_interfaces = m_settings.get_str(settings_pack::listen_interfaces); |
5377 | 1.83k | std::vector<std::string> err; |
5378 | 1.83k | m_listen_interfaces = parse_listen_interfaces(net_interfaces, err); |
5379 | | |
5380 | 1.83k | for (auto const& e : err) |
5381 | 0 | { |
5382 | 0 | m_alerts.emplace_alert<listen_failed_alert>(e, lt::address{}, 0 |
5383 | 0 | , operation_t::parse_address, errors::invalid_port, lt::socket_type_t::tcp); |
5384 | 0 | } |
5385 | | |
5386 | | #ifndef TORRENT_DISABLE_LOGGING |
5387 | | if (should_log()) |
5388 | | { |
5389 | | session_log("update listen interfaces: %s", net_interfaces.c_str()); |
5390 | | session_log("parsed listen interfaces count: %d, ifaces: %s" |
5391 | | , int(m_listen_interfaces.size()) |
5392 | | , print_listen_interfaces(m_listen_interfaces).c_str()); |
5393 | | } |
5394 | | #endif |
5395 | 1.83k | } |
5396 | | |
5397 | | void session_impl::update_privileged_ports() |
5398 | 1.83k | { |
5399 | 1.83k | if (m_settings.get_bool(settings_pack::no_connect_privileged_ports)) |
5400 | 0 | { |
5401 | | // Close connections whose endpoint is filtered |
5402 | | // by the new setting |
5403 | 0 | for (auto const& t : m_torrents) |
5404 | 0 | t->privileged_port_updated(); |
5405 | 0 | } |
5406 | 1.83k | } |
5407 | | |
5408 | | void session_impl::update_auto_sequential() |
5409 | 1.83k | { |
5410 | 1.83k | for (auto& i : m_torrents) |
5411 | 0 | i->update_auto_sequential(); |
5412 | 1.83k | } |
5413 | | |
5414 | | void session_impl::update_max_failcount() |
5415 | 1.83k | { |
5416 | 1.83k | for (auto& i : m_torrents) |
5417 | 0 | i->update_max_failcount(); |
5418 | 1.83k | } |
5419 | | |
5420 | | void session_impl::update_resolver_cache_timeout() |
5421 | 1.83k | { |
5422 | 1.83k | int const timeout = m_settings.get_int(settings_pack::resolver_cache_timeout); |
5423 | 1.83k | m_host_resolver.set_cache_timeout(seconds(timeout)); |
5424 | 1.83k | } |
5425 | | |
5426 | | void session_impl::update_proxy() |
5427 | 9.18k | { |
5428 | 9.18k | for (auto& i : m_listen_sockets) |
5429 | 0 | i->udp_sock->sock.set_proxy_settings(proxy(), m_alerts, get_resolver() |
5430 | 0 | , settings().get_bool(settings_pack::socks5_udp_send_local_ep)); |
5431 | 9.18k | } |
5432 | | |
5433 | | void session_impl::update_ip_notifier() |
5434 | 1.83k | { |
5435 | 1.83k | if (m_settings.get_bool(settings_pack::enable_ip_notifier)) |
5436 | 0 | start_ip_notifier(); |
5437 | 1.83k | else |
5438 | 1.83k | stop_ip_notifier(); |
5439 | 1.83k | } |
5440 | | |
5441 | | void session_impl::update_upnp() |
5442 | 1.83k | { |
5443 | 1.83k | if (m_settings.get_bool(settings_pack::enable_upnp)) |
5444 | 0 | start_upnp(); |
5445 | 1.83k | else |
5446 | 1.83k | stop_upnp(); |
5447 | 1.83k | } |
5448 | | |
5449 | | void session_impl::update_natpmp() |
5450 | 1.83k | { |
5451 | 1.83k | if (m_settings.get_bool(settings_pack::enable_natpmp)) |
5452 | 0 | start_natpmp(); |
5453 | 1.83k | else |
5454 | 1.83k | stop_natpmp(); |
5455 | 1.83k | } |
5456 | | |
5457 | | void session_impl::update_lsd() |
5458 | 3.67k | { |
5459 | 3.67k | if (m_settings.get_bool(settings_pack::enable_lsd)) |
5460 | 0 | start_lsd(); |
5461 | 3.67k | else |
5462 | 3.67k | stop_lsd(); |
5463 | 3.67k | } |
5464 | | |
5465 | | void session_impl::update_dht() |
5466 | 1.83k | { |
5467 | 1.83k | #ifndef TORRENT_DISABLE_DHT |
5468 | 1.83k | if (m_settings.get_bool(settings_pack::enable_dht)) |
5469 | 0 | { |
5470 | 0 | if (!m_settings.get_str(settings_pack::dht_bootstrap_nodes).empty() |
5471 | 0 | && m_dht_router_nodes.empty()) |
5472 | 0 | { |
5473 | | // if we have bootstrap nodes configured, make sure we initiate host |
5474 | | // name lookups. once these complete, the DHT will be started. |
5475 | | // they are tracked by m_outstanding_router_lookups |
5476 | 0 | update_dht_bootstrap_nodes(); |
5477 | 0 | } |
5478 | 0 | else |
5479 | 0 | { |
5480 | 0 | start_dht(); |
5481 | 0 | } |
5482 | 0 | } |
5483 | 1.83k | else |
5484 | 1.83k | stop_dht(); |
5485 | 1.83k | #endif |
5486 | 1.83k | } |
5487 | | |
5488 | | void session_impl::update_dht_bootstrap_nodes() |
5489 | 1.83k | { |
5490 | 1.83k | #ifndef TORRENT_DISABLE_DHT |
5491 | 1.83k | if (!m_settings.get_bool(settings_pack::enable_dht)) return; |
5492 | | |
5493 | 0 | std::string const& node_list = m_settings.get_str(settings_pack::dht_bootstrap_nodes); |
5494 | 0 | std::vector<std::pair<std::string, int>> nodes; |
5495 | 0 | parse_comma_separated_string_port(node_list, nodes); |
5496 | |
|
5497 | | #ifndef TORRENT_DISABLE_LOGGING |
5498 | | if (!node_list.empty() && nodes.empty()) |
5499 | | { |
5500 | | session_log("ERROR: failed to parse DHT bootstrap list: %s", node_list.c_str()); |
5501 | | } |
5502 | | #endif |
5503 | 0 | for (auto const& n : nodes) |
5504 | 0 | add_dht_router(n); |
5505 | 0 | #endif |
5506 | 0 | } |
5507 | | |
5508 | | void session_impl::update_count_slow() |
5509 | 1.83k | { |
5510 | 1.83k | error_code ec; |
5511 | 1.83k | for (auto const& tp : m_torrents) |
5512 | 0 | { |
5513 | 0 | tp->on_inactivity_tick(ec); |
5514 | 0 | } |
5515 | 1.83k | } |
5516 | | |
5517 | | // TODO: 2 this function should be removed and users need to deal with the |
5518 | | // more generic case of having multiple listen ports |
5519 | | std::uint16_t session_impl::listen_port() const |
5520 | 0 | { |
5521 | 0 | return listen_port(nullptr); |
5522 | 0 | } |
5523 | | |
5524 | | std::uint16_t session_impl::listen_port(listen_socket_t* sock) const |
5525 | 0 | { |
5526 | 0 | if (m_listen_sockets.empty()) return 0; |
5527 | 0 | if (sock) |
5528 | 0 | { |
5529 | | // if we're using a proxy, we won't be able to accept any TCP |
5530 | | // connections. Not even uTP connections via the port we know about. |
5531 | | // The DHT may use the implied port to make it work, but the port we |
5532 | | // announce here has no relevance for that. |
5533 | 0 | if (sock->flags & listen_socket_t::proxy) |
5534 | 0 | return 0; |
5535 | | |
5536 | 0 | if (!(sock->flags & listen_socket_t::accept_incoming)) |
5537 | 0 | return 0; |
5538 | | |
5539 | 0 | return std::uint16_t(sock->tcp_external_port()); |
5540 | 0 | } |
5541 | | |
5542 | 0 | #ifdef TORRENT_SSL_PEERS |
5543 | 0 | for (auto const& s : m_listen_sockets) |
5544 | 0 | { |
5545 | 0 | if (!(s->flags & listen_socket_t::accept_incoming)) continue; |
5546 | 0 | if (s->ssl == transport::plaintext) |
5547 | 0 | return std::uint16_t(s->tcp_external_port()); |
5548 | 0 | } |
5549 | 0 | return 0; |
5550 | | #else |
5551 | | sock = m_listen_sockets.front().get(); |
5552 | | if (!(sock->flags & listen_socket_t::accept_incoming)) return 0; |
5553 | | return std::uint16_t(sock->tcp_external_port()); |
5554 | | #endif |
5555 | 0 | } |
5556 | | |
5557 | | // TODO: 2 this function should be removed and users need to deal with the |
5558 | | // more generic case of having multiple ssl ports |
5559 | | std::uint16_t session_impl::ssl_listen_port() const |
5560 | 0 | { |
5561 | 0 | return ssl_listen_port(nullptr); |
5562 | 0 | } |
5563 | | |
5564 | | std::uint16_t session_impl::ssl_listen_port(listen_socket_t* sock) const |
5565 | 0 | { |
5566 | 0 | #ifdef TORRENT_SSL_PEERS |
5567 | 0 | if (sock) |
5568 | 0 | { |
5569 | 0 | if (!(sock->flags & listen_socket_t::accept_incoming)) return 0; |
5570 | 0 | return std::uint16_t(sock->tcp_external_port()); |
5571 | 0 | } |
5572 | | |
5573 | 0 | if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none |
5574 | 0 | && m_settings.get_bool(settings_pack::proxy_peer_connections)) |
5575 | 0 | return 0; |
5576 | | |
5577 | 0 | for (auto const& s : m_listen_sockets) |
5578 | 0 | { |
5579 | 0 | if (!(s->flags & listen_socket_t::accept_incoming)) continue; |
5580 | 0 | if (s->ssl == transport::ssl) |
5581 | 0 | return std::uint16_t(s->tcp_external_port()); |
5582 | 0 | } |
5583 | | #else |
5584 | | TORRENT_UNUSED(sock); |
5585 | | #endif |
5586 | 0 | return 0; |
5587 | 0 | } |
5588 | | |
5589 | | int session_impl::get_listen_port(transport const ssl, aux::listen_socket_handle const& s) |
5590 | 0 | { |
5591 | 0 | auto socket = s.get(); |
5592 | 0 | if (socket->ssl != ssl) |
5593 | 0 | { |
5594 | 0 | auto alt_socket = std::find_if(m_listen_sockets.begin(), m_listen_sockets.end() |
5595 | 0 | , [&](std::shared_ptr<listen_socket_t> const& e) |
5596 | 0 | { |
5597 | 0 | return e->ssl == ssl |
5598 | 0 | && e->external_address.external_address() |
5599 | 0 | == socket->external_address.external_address(); |
5600 | 0 | }); |
5601 | 0 | if (alt_socket != m_listen_sockets.end()) |
5602 | 0 | socket = alt_socket->get(); |
5603 | 0 | } |
5604 | 0 | return socket->udp_external_port(); |
5605 | 0 | } |
5606 | | |
5607 | | int session_impl::listen_port(transport const ssl, address const& local_addr) |
5608 | 0 | { |
5609 | 0 | auto socket = std::find_if(m_listen_sockets.begin(), m_listen_sockets.end() |
5610 | 0 | , [&](std::shared_ptr<listen_socket_t> const& e) |
5611 | 0 | { |
5612 | 0 | if (!(e->flags & listen_socket_t::accept_incoming)) return false; |
5613 | 0 | auto const& listen_addr = e->external_address.external_address(); |
5614 | 0 | return e->ssl == ssl |
5615 | 0 | && (listen_addr == local_addr |
5616 | 0 | || (listen_addr.is_v4() == local_addr.is_v4() && listen_addr.is_unspecified())); |
5617 | 0 | }); |
5618 | 0 | if (socket != m_listen_sockets.end()) |
5619 | 0 | return (*socket)->tcp_external_port(); |
5620 | 0 | return 0; |
5621 | 0 | } |
5622 | | |
5623 | | void session_impl::announce_lsd(sha1_hash const& ih, int port) |
5624 | 0 | { |
5625 | | // use internal listen port for local peers |
5626 | 0 | for (auto const& s : m_listen_sockets) |
5627 | 0 | { |
5628 | 0 | if (s->lsd) s->lsd->announce(ih, port); |
5629 | 0 | } |
5630 | 0 | } |
5631 | | |
5632 | | void session_impl::on_lsd_peer(tcp::endpoint const& peer, sha1_hash const& ih) |
5633 | 0 | { |
5634 | 0 | m_stats_counters.inc_stats_counter(counters::on_lsd_peer_counter); |
5635 | 0 | TORRENT_ASSERT(is_single_thread()); |
5636 | |
|
5637 | 0 | INVARIANT_CHECK; |
5638 | |
|
5639 | 0 | std::shared_ptr<torrent> t = find_torrent(info_hash_t(ih)).lock(); |
5640 | 0 | if (!t) return; |
5641 | | // don't add peers from lsd to private torrents |
5642 | 0 | if (t->torrent_file().priv() || (t->torrent_file().is_i2p() |
5643 | 0 | && !m_settings.get_bool(settings_pack::allow_i2p_mixed))) return; |
5644 | | |
5645 | 0 | protocol_version const v = ih == t->torrent_file().info_hashes().v1 |
5646 | 0 | ? protocol_version::V1 : protocol_version::V2; |
5647 | |
|
5648 | 0 | t->add_peer(peer, peer_info::lsd, v == protocol_version::V2 ? pex_lt_v2 : pex_flags_t(0)); |
5649 | | #ifndef TORRENT_DISABLE_LOGGING |
5650 | | if (should_log()) |
5651 | | { |
5652 | | t->debug_log("lsd add_peer() [ %s ]" |
5653 | | , peer.address().to_string().c_str()); |
5654 | | } |
5655 | | #endif |
5656 | 0 | t->do_connect_boost(); |
5657 | |
|
5658 | 0 | if (m_alerts.should_post<lsd_peer_alert>()) |
5659 | 0 | m_alerts.emplace_alert<lsd_peer_alert>(t->get_handle(), peer); |
5660 | 0 | } |
5661 | | |
5662 | | void session_impl::start_natpmp(std::shared_ptr<aux::listen_socket_t> const& s) |
5663 | 0 | { |
5664 | | // don't create mappings for local IPv6 addresses |
5665 | | // they can't be reached from outside of the local network anyways |
5666 | 0 | if (is_v6(s->local_endpoint) && is_local(s->local_endpoint.address())) |
5667 | 0 | return; |
5668 | | |
5669 | 0 | if (!s->natpmp_mapper |
5670 | 0 | && !(s->flags & listen_socket_t::local_network) |
5671 | 0 | && !(s->flags & listen_socket_t::proxy)) |
5672 | 0 | { |
5673 | | // the natpmp constructor may fail and call the callbacks |
5674 | | // into the session_impl. |
5675 | 0 | s->natpmp_mapper = std::make_shared<natpmp>(m_io_context, *this, listen_socket_handle(s)); |
5676 | 0 | ip_interface ip; |
5677 | 0 | ip.interface_address = s->local_endpoint.address(); |
5678 | 0 | ip.netmask = s->netmask; |
5679 | 0 | std::strncpy(ip.name, s->device.c_str(), sizeof(ip.name) - 1); |
5680 | 0 | ip.name[sizeof(ip.name) - 1] = '\0'; |
5681 | 0 | s->natpmp_mapper->start(ip); |
5682 | 0 | } |
5683 | 0 | } |
5684 | | |
5685 | | void session_impl::on_port_mapping(port_mapping_t const mapping |
5686 | | , address const& external_ip, int port |
5687 | | , portmap_protocol const proto, error_code const& ec |
5688 | | , portmap_transport const transport |
5689 | | , listen_socket_handle const& ls) |
5690 | 0 | { |
5691 | 0 | TORRENT_ASSERT(is_single_thread()); |
5692 | |
|
5693 | 0 | listen_socket_t* listen_socket = ls.get(); |
5694 | | |
5695 | | // NOTE: don't assume that if ec != 0, the rest of the logic |
5696 | | // is not necessary, the ports still need to be set, in other |
5697 | | // words, don't early return without careful review of the |
5698 | | // remaining logic |
5699 | 0 | if (ec && m_alerts.should_post<portmap_error_alert>()) |
5700 | 0 | { |
5701 | 0 | m_alerts.emplace_alert<portmap_error_alert>(mapping |
5702 | 0 | , transport, ec, listen_socket ? listen_socket->local_endpoint.address() : address()); |
5703 | 0 | } |
5704 | |
|
5705 | 0 | if (!listen_socket) return; |
5706 | | |
5707 | 0 | if (!ec && !external_ip.is_unspecified()) |
5708 | 0 | { |
5709 | | // TODO: 1 report the proper address of the router as the source IP of |
5710 | | // this vote of our external address, instead of the empty address |
5711 | 0 | listen_socket->external_address.cast_vote(external_ip, source_router, address()); |
5712 | 0 | } |
5713 | | |
5714 | | // need to check whether this mapping is for one of session ports (it could also be a user mapping) |
5715 | 0 | if ((proto == portmap_protocol::tcp) && (listen_socket->tcp_port_mapping[transport].mapping == mapping)) |
5716 | 0 | listen_socket->tcp_port_mapping[transport].port = port; |
5717 | 0 | else if ((proto == portmap_protocol::udp) && (listen_socket->udp_port_mapping[transport].mapping == mapping)) |
5718 | 0 | listen_socket->udp_port_mapping[transport].port = port; |
5719 | |
|
5720 | 0 | if (!ec && m_alerts.should_post<portmap_alert>()) |
5721 | 0 | { |
5722 | 0 | m_alerts.emplace_alert<portmap_alert>(mapping, port |
5723 | 0 | , transport, proto, listen_socket->local_endpoint.address()); |
5724 | 0 | } |
5725 | 0 | } |
5726 | | |
5727 | | #if TORRENT_ABI_VERSION == 1 |
5728 | | session_status session_impl::status() const |
5729 | 0 | { |
5730 | | // INVARIANT_CHECK; |
5731 | 0 | TORRENT_ASSERT(is_single_thread()); |
5732 | |
|
5733 | 0 | session_status s; |
5734 | |
|
5735 | 0 | s.optimistic_unchoke_counter = m_optimistic_unchoke_time_scaler; |
5736 | 0 | s.unchoke_counter = m_unchoke_time_scaler; |
5737 | 0 | s.num_dead_peers = int(m_undead_peers.size()); |
5738 | |
|
5739 | 0 | s.num_peers = int(m_stats_counters[counters::num_peers_connected]); |
5740 | 0 | s.num_unchoked = int(m_stats_counters[counters::num_peers_up_unchoked_all]); |
5741 | 0 | s.allowed_upload_slots = int(m_stats_counters[counters::num_unchoke_slots]); |
5742 | |
|
5743 | 0 | s.num_torrents |
5744 | 0 | = int(m_stats_counters[counters::num_checking_torrents] |
5745 | 0 | + m_stats_counters[counters::num_stopped_torrents] |
5746 | 0 | + m_stats_counters[counters::num_queued_seeding_torrents] |
5747 | 0 | + m_stats_counters[counters::num_queued_download_torrents] |
5748 | 0 | + m_stats_counters[counters::num_upload_only_torrents] |
5749 | 0 | + m_stats_counters[counters::num_downloading_torrents] |
5750 | 0 | + m_stats_counters[counters::num_seeding_torrents] |
5751 | 0 | + m_stats_counters[counters::num_error_torrents]); |
5752 | |
|
5753 | 0 | s.num_paused_torrents |
5754 | 0 | = int(m_stats_counters[counters::num_stopped_torrents] |
5755 | 0 | + m_stats_counters[counters::num_error_torrents] |
5756 | 0 | + m_stats_counters[counters::num_queued_seeding_torrents] |
5757 | 0 | + m_stats_counters[counters::num_queued_download_torrents]); |
5758 | |
|
5759 | 0 | s.total_redundant_bytes = m_stats_counters[counters::recv_redundant_bytes]; |
5760 | 0 | s.total_failed_bytes = m_stats_counters[counters::recv_failed_bytes]; |
5761 | |
|
5762 | 0 | s.up_bandwidth_queue = int(m_stats_counters[counters::limiter_up_queue]); |
5763 | 0 | s.down_bandwidth_queue = int(m_stats_counters[counters::limiter_down_queue]); |
5764 | |
|
5765 | 0 | s.up_bandwidth_bytes_queue = int(m_stats_counters[counters::limiter_up_bytes]); |
5766 | 0 | s.down_bandwidth_bytes_queue = int(m_stats_counters[counters::limiter_down_bytes]); |
5767 | |
|
5768 | 0 | s.disk_write_queue = int(m_stats_counters[counters::num_peers_down_disk]); |
5769 | 0 | s.disk_read_queue = int(m_stats_counters[counters::num_peers_up_disk]); |
5770 | |
|
5771 | 0 | s.has_incoming_connections = m_stats_counters[counters::has_incoming_connections] != 0; |
5772 | | |
5773 | | // total |
5774 | 0 | s.download_rate = m_stat.download_rate(); |
5775 | 0 | s.total_upload = m_stat.total_upload(); |
5776 | 0 | s.upload_rate = m_stat.upload_rate(); |
5777 | 0 | s.total_download = m_stat.total_download(); |
5778 | | |
5779 | | // payload |
5780 | 0 | s.payload_download_rate = m_stat.transfer_rate(stat::download_payload); |
5781 | 0 | s.total_payload_download = m_stat.total_transfer(stat::download_payload); |
5782 | 0 | s.payload_upload_rate = m_stat.transfer_rate(stat::upload_payload); |
5783 | 0 | s.total_payload_upload = m_stat.total_transfer(stat::upload_payload); |
5784 | | |
5785 | | // IP-overhead |
5786 | 0 | s.ip_overhead_download_rate = m_stat.transfer_rate(stat::download_ip_protocol); |
5787 | 0 | s.total_ip_overhead_download = m_stats_counters[counters::recv_ip_overhead_bytes]; |
5788 | 0 | s.ip_overhead_upload_rate = m_stat.transfer_rate(stat::upload_ip_protocol); |
5789 | 0 | s.total_ip_overhead_upload = m_stats_counters[counters::sent_ip_overhead_bytes]; |
5790 | | |
5791 | | // tracker |
5792 | 0 | s.total_tracker_download = m_stats_counters[counters::recv_tracker_bytes]; |
5793 | 0 | s.total_tracker_upload = m_stats_counters[counters::sent_tracker_bytes]; |
5794 | | |
5795 | | // dht |
5796 | 0 | s.total_dht_download = m_stats_counters[counters::dht_bytes_in]; |
5797 | 0 | s.total_dht_upload = m_stats_counters[counters::dht_bytes_out]; |
5798 | | |
5799 | | // deprecated |
5800 | 0 | s.tracker_download_rate = 0; |
5801 | 0 | s.tracker_upload_rate = 0; |
5802 | 0 | s.dht_download_rate = 0; |
5803 | 0 | s.dht_upload_rate = 0; |
5804 | |
|
5805 | 0 | #ifndef TORRENT_DISABLE_DHT |
5806 | 0 | if (m_dht) |
5807 | 0 | { |
5808 | 0 | m_dht->dht_status(s); |
5809 | 0 | } |
5810 | 0 | else |
5811 | 0 | #endif |
5812 | 0 | { |
5813 | 0 | s.dht_nodes = 0; |
5814 | 0 | s.dht_node_cache = 0; |
5815 | 0 | s.dht_torrents = 0; |
5816 | 0 | s.dht_global_nodes = 0; |
5817 | 0 | s.dht_total_allocations = 0; |
5818 | 0 | } |
5819 | |
|
5820 | 0 | s.utp_stats.packet_loss = std::uint64_t(m_stats_counters[counters::utp_packet_loss]); |
5821 | 0 | s.utp_stats.timeout = std::uint64_t(m_stats_counters[counters::utp_timeout]); |
5822 | 0 | s.utp_stats.packets_in = std::uint64_t(m_stats_counters[counters::utp_packets_in]); |
5823 | 0 | s.utp_stats.packets_out = std::uint64_t(m_stats_counters[counters::utp_packets_out]); |
5824 | 0 | s.utp_stats.fast_retransmit = std::uint64_t(m_stats_counters[counters::utp_fast_retransmit]); |
5825 | 0 | s.utp_stats.packet_resend = std::uint64_t(m_stats_counters[counters::utp_packet_resend]); |
5826 | 0 | s.utp_stats.samples_above_target = std::uint64_t(m_stats_counters[counters::utp_samples_above_target]); |
5827 | 0 | s.utp_stats.samples_below_target = std::uint64_t(m_stats_counters[counters::utp_samples_below_target]); |
5828 | 0 | s.utp_stats.payload_pkts_in = std::uint64_t(m_stats_counters[counters::utp_payload_pkts_in]); |
5829 | 0 | s.utp_stats.payload_pkts_out = std::uint64_t(m_stats_counters[counters::utp_payload_pkts_out]); |
5830 | 0 | s.utp_stats.invalid_pkts_in = std::uint64_t(m_stats_counters[counters::utp_invalid_pkts_in]); |
5831 | 0 | s.utp_stats.redundant_pkts_in = std::uint64_t(m_stats_counters[counters::utp_redundant_pkts_in]); |
5832 | |
|
5833 | 0 | s.utp_stats.num_idle = int(m_stats_counters[counters::num_utp_idle]); |
5834 | 0 | s.utp_stats.num_syn_sent = int(m_stats_counters[counters::num_utp_syn_sent]); |
5835 | 0 | s.utp_stats.num_connected = int(m_stats_counters[counters::num_utp_connected]); |
5836 | 0 | s.utp_stats.num_fin_sent = int(m_stats_counters[counters::num_utp_fin_sent]); |
5837 | 0 | s.utp_stats.num_close_wait = int(m_stats_counters[counters::num_utp_close_wait]); |
5838 | | |
5839 | | // this loop is potentially expensive. It could be optimized by |
5840 | | // simply keeping a global counter |
5841 | 0 | s.peerlist_size = std::accumulate(m_torrents.begin(), m_torrents.end(), 0 |
5842 | 0 | , [](int const acc, std::shared_ptr<torrent> const& t) |
5843 | 0 | { return acc + t->num_known_peers(); }); |
5844 | |
|
5845 | 0 | return s; |
5846 | 0 | } |
5847 | | #endif // TORRENT_ABI_VERSION |
5848 | | |
5849 | | #ifndef TORRENT_DISABLE_DHT |
5850 | | |
5851 | | void session_impl::start_dht() |
5852 | 0 | { |
5853 | 0 | INVARIANT_CHECK; |
5854 | |
|
5855 | 0 | stop_dht(); |
5856 | |
|
5857 | 0 | if (!m_settings.get_bool(settings_pack::enable_dht)) return; |
5858 | | |
5859 | | // postpone starting the DHT if we're still resolving the DHT router |
5860 | 0 | if (m_outstanding_router_lookups > 0) |
5861 | 0 | { |
5862 | | #ifndef TORRENT_DISABLE_LOGGING |
5863 | | session_log("not starting DHT, outstanding router lookups: %d" |
5864 | | , m_outstanding_router_lookups); |
5865 | | #endif |
5866 | 0 | return; |
5867 | 0 | } |
5868 | | |
5869 | 0 | if (m_abort) |
5870 | 0 | { |
5871 | | #ifndef TORRENT_DISABLE_LOGGING |
5872 | | session_log("not starting DHT, aborting"); |
5873 | | #endif |
5874 | 0 | return; |
5875 | 0 | } |
5876 | | |
5877 | | #ifndef TORRENT_DISABLE_LOGGING |
5878 | | session_log("starting DHT, running: %s, router lookups: %d" |
5879 | | , m_dht ? "true" : "false", m_outstanding_router_lookups); |
5880 | | #endif |
5881 | | |
5882 | | // TODO: refactor, move the storage to dht_tracker |
5883 | 0 | m_dht_storage = m_dht_storage_constructor(m_settings); |
5884 | 0 | m_dht = std::make_shared<dht::dht_tracker>( |
5885 | 0 | static_cast<dht::dht_observer*>(this) |
5886 | 0 | , m_io_context |
5887 | 0 | , [this](aux::listen_socket_handle const& sock |
5888 | 0 | , udp::endpoint const& ep |
5889 | 0 | , span<char const> p |
5890 | 0 | , error_code& ec |
5891 | 0 | , udp_send_flags_t const flags) |
5892 | 0 | { send_udp_packet_listen(sock, ep, p, ec, flags); } |
5893 | 0 | , m_settings |
5894 | 0 | , m_stats_counters |
5895 | 0 | , *m_dht_storage |
5896 | 0 | , std::move(m_dht_state)); |
5897 | |
|
5898 | 0 | for (auto& s : m_listen_sockets) |
5899 | 0 | { |
5900 | 0 | if (s->ssl != transport::ssl |
5901 | 0 | && !(s->flags & listen_socket_t::local_network)) |
5902 | 0 | { |
5903 | 0 | m_dht->new_socket(s); |
5904 | 0 | } |
5905 | 0 | } |
5906 | |
|
5907 | 0 | for (auto const& n : m_dht_router_nodes) |
5908 | 0 | { |
5909 | 0 | m_dht->add_router_node(n); |
5910 | 0 | } |
5911 | |
|
5912 | 0 | for (auto const& n : m_dht_nodes) |
5913 | 0 | { |
5914 | 0 | m_dht->add_node(n); |
5915 | 0 | } |
5916 | 0 | m_dht_nodes.clear(); |
5917 | 0 | m_dht_nodes.shrink_to_fit(); |
5918 | |
|
5919 | 0 | auto cb = [this]( |
5920 | 0 | std::vector<std::pair<dht::node_entry, std::string>> const&) |
5921 | 0 | { |
5922 | 0 | if (m_alerts.should_post<dht_bootstrap_alert>()) |
5923 | 0 | m_alerts.emplace_alert<dht_bootstrap_alert>(); |
5924 | 0 | }; |
5925 | |
|
5926 | 0 | m_dht->start(cb); |
5927 | 0 | } |
5928 | | |
5929 | | void session_impl::stop_dht() |
5930 | 3.67k | { |
5931 | | #ifndef TORRENT_DISABLE_LOGGING |
5932 | | session_log("about to stop DHT, running: %s", m_dht ? "true" : "false"); |
5933 | | #endif |
5934 | | |
5935 | 3.67k | if (m_dht) |
5936 | 0 | { |
5937 | 0 | m_dht->stop(); |
5938 | 0 | m_dht.reset(); |
5939 | 0 | } |
5940 | | |
5941 | 3.67k | m_dht_storage.reset(); |
5942 | 3.67k | } |
5943 | | |
5944 | | #if TORRENT_ABI_VERSION <= 2 |
5945 | | void session_impl::set_dht_settings(dht::dht_settings const& settings) |
5946 | 0 | { |
5947 | 0 | #ifndef TORRENT_DISABLE_DHT |
5948 | 0 | #define SET_BOOL(name) m_settings.set_bool(settings_pack::dht_ ## name, settings.name) |
5949 | 0 | #define SET_INT(name) m_settings.set_int(settings_pack::dht_ ## name, settings.name) |
5950 | |
|
5951 | 0 | SET_INT(max_peers_reply); |
5952 | 0 | SET_INT(search_branching); |
5953 | 0 | SET_INT(max_fail_count); |
5954 | 0 | SET_INT(max_torrents); |
5955 | 0 | SET_INT(max_dht_items); |
5956 | 0 | SET_INT(max_peers); |
5957 | 0 | SET_INT(max_torrent_search_reply); |
5958 | 0 | SET_BOOL(restrict_routing_ips); |
5959 | 0 | SET_BOOL(restrict_search_ips); |
5960 | 0 | SET_BOOL(extended_routing_table); |
5961 | 0 | SET_BOOL(aggressive_lookups); |
5962 | 0 | SET_BOOL(privacy_lookups); |
5963 | 0 | SET_BOOL(enforce_node_id); |
5964 | 0 | SET_BOOL(ignore_dark_internet); |
5965 | 0 | SET_INT(block_timeout); |
5966 | 0 | SET_INT(block_ratelimit); |
5967 | 0 | SET_BOOL(read_only); |
5968 | 0 | SET_INT(item_lifetime); |
5969 | 0 | SET_INT(upload_rate_limit); |
5970 | 0 | SET_INT(sample_infohashes_interval); |
5971 | 0 | SET_INT(max_infohashes_sample_count); |
5972 | 0 | #undef SET_BOOL |
5973 | 0 | #undef SET_INT |
5974 | 0 | update_dht_upload_rate_limit(); |
5975 | 0 | #endif |
5976 | 0 | } |
5977 | | |
5978 | | dht::dht_settings session_impl::get_dht_settings() const |
5979 | 0 | { |
5980 | 0 | dht::dht_settings sett; |
5981 | 0 | #ifndef TORRENT_DISABLE_DHT |
5982 | 0 | #define SET_BOOL(name) \ |
5983 | 0 | sett.name = m_settings.get_bool( settings_pack::dht_ ## name ) |
5984 | 0 | #define SET_INT(name) \ |
5985 | 0 | sett.name = m_settings.get_int( settings_pack::dht_ ## name ) |
5986 | |
|
5987 | 0 | SET_INT(max_peers_reply); |
5988 | 0 | SET_INT(search_branching); |
5989 | 0 | SET_INT(max_fail_count); |
5990 | 0 | SET_INT(max_torrents); |
5991 | 0 | SET_INT(max_dht_items); |
5992 | 0 | SET_INT(max_peers); |
5993 | 0 | SET_INT(max_torrent_search_reply); |
5994 | 0 | SET_BOOL(restrict_routing_ips); |
5995 | 0 | SET_BOOL(restrict_search_ips); |
5996 | 0 | SET_BOOL(extended_routing_table); |
5997 | 0 | SET_BOOL(aggressive_lookups); |
5998 | 0 | SET_BOOL(privacy_lookups); |
5999 | 0 | SET_BOOL(enforce_node_id); |
6000 | 0 | SET_BOOL(ignore_dark_internet); |
6001 | 0 | SET_INT(block_timeout); |
6002 | 0 | SET_INT(block_ratelimit); |
6003 | 0 | SET_BOOL(read_only); |
6004 | 0 | SET_INT(item_lifetime); |
6005 | 0 | SET_INT(upload_rate_limit); |
6006 | 0 | SET_INT(sample_infohashes_interval); |
6007 | 0 | SET_INT(max_infohashes_sample_count); |
6008 | 0 | #undef SET_BOOL |
6009 | 0 | #undef SET_INT |
6010 | 0 | #endif |
6011 | 0 | return sett; |
6012 | 0 | } |
6013 | | #endif |
6014 | | |
6015 | | void session_impl::set_dht_state(dht::dht_state&& state) |
6016 | 1.83k | { |
6017 | 1.83k | m_dht_state = std::move(state); |
6018 | 1.83k | } |
6019 | | |
6020 | | void session_impl::set_dht_storage(dht::dht_storage_constructor_type sc) |
6021 | 1.83k | { |
6022 | 1.83k | m_dht_storage_constructor = std::move(sc); |
6023 | 1.83k | } |
6024 | | |
6025 | | #if TORRENT_ABI_VERSION == 1 |
6026 | | entry session_impl::dht_state() const |
6027 | 0 | { |
6028 | 0 | return m_dht ? dht::save_dht_state(m_dht->state()) : entry(); |
6029 | 0 | } |
6030 | | |
6031 | | void session_impl::start_dht_deprecated(entry const& startup_state) |
6032 | 0 | { |
6033 | 0 | m_settings.set_bool(settings_pack::enable_dht, true); |
6034 | 0 | std::vector<char> tmp; |
6035 | 0 | bencode(std::back_inserter(tmp), startup_state); |
6036 | |
|
6037 | 0 | bdecode_node e; |
6038 | 0 | error_code ec; |
6039 | 0 | if (tmp.empty() || bdecode(&tmp[0], &tmp[0] + tmp.size(), e, ec) != 0) |
6040 | 0 | return; |
6041 | 0 | m_dht_state = dht::read_dht_state(e); |
6042 | 0 | start_dht(); |
6043 | 0 | } |
6044 | | #endif |
6045 | | |
6046 | | void session_impl::add_dht_node_name(std::pair<std::string, int> const& node) |
6047 | 0 | { |
6048 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::on_dht_name_lookup"); |
6049 | 0 | m_host_resolver.async_resolve(node.first, resolver::abort_on_shutdown |
6050 | 0 | , std::bind(&session_impl::on_dht_name_lookup |
6051 | 0 | , this, _1, _2, node.second)); |
6052 | 0 | } |
6053 | | |
6054 | | void session_impl::on_dht_name_lookup(error_code const& e |
6055 | | , std::vector<address> const& addresses, int port) |
6056 | 0 | { |
6057 | 0 | COMPLETE_ASYNC("session_impl::on_dht_name_lookup"); |
6058 | |
|
6059 | 0 | if (e) |
6060 | 0 | { |
6061 | 0 | if (m_alerts.should_post<dht_error_alert>()) |
6062 | 0 | m_alerts.emplace_alert<dht_error_alert>( |
6063 | 0 | operation_t::hostname_lookup, e); |
6064 | 0 | return; |
6065 | 0 | } |
6066 | | |
6067 | 0 | for (auto const& addr : addresses) |
6068 | 0 | { |
6069 | 0 | udp::endpoint ep(addr, std::uint16_t(port)); |
6070 | 0 | add_dht_node(ep); |
6071 | 0 | } |
6072 | 0 | } |
6073 | | |
6074 | | void session_impl::add_dht_router(std::pair<std::string, int> const& node) |
6075 | 0 | { |
6076 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::on_dht_router_name_lookup"); |
6077 | 0 | ++m_outstanding_router_lookups; |
6078 | 0 | m_host_resolver.async_resolve(node.first, resolver::abort_on_shutdown |
6079 | 0 | , std::bind(&session_impl::on_dht_router_name_lookup |
6080 | 0 | , this, _1, _2, node.second)); |
6081 | 0 | } |
6082 | | |
6083 | | void session_impl::on_dht_router_name_lookup(error_code const& e |
6084 | | , std::vector<address> const& addresses, int port) |
6085 | 0 | { |
6086 | 0 | COMPLETE_ASYNC("session_impl::on_dht_router_name_lookup"); |
6087 | 0 | --m_outstanding_router_lookups; |
6088 | |
|
6089 | 0 | if (e) |
6090 | 0 | { |
6091 | 0 | if (m_alerts.should_post<dht_error_alert>()) |
6092 | 0 | m_alerts.emplace_alert<dht_error_alert>( |
6093 | 0 | operation_t::hostname_lookup, e); |
6094 | |
|
6095 | 0 | if (m_outstanding_router_lookups == 0) start_dht(); |
6096 | 0 | return; |
6097 | 0 | } |
6098 | | |
6099 | | |
6100 | 0 | for (auto const& addr : addresses) |
6101 | 0 | { |
6102 | | // router nodes should be added before the DHT is started (and bootstrapped) |
6103 | 0 | udp::endpoint ep(addr, std::uint16_t(port)); |
6104 | 0 | if (m_dht) m_dht->add_router_node(ep); |
6105 | 0 | m_dht_router_nodes.push_back(ep); |
6106 | 0 | } |
6107 | |
|
6108 | 0 | if (m_outstanding_router_lookups == 0) start_dht(); |
6109 | 0 | } |
6110 | | |
6111 | | // callback for dht_immutable_get |
6112 | | void session_impl::get_immutable_callback(sha1_hash target |
6113 | | , dht::item const& i) |
6114 | 0 | { |
6115 | 0 | TORRENT_ASSERT(!i.is_mutable()); |
6116 | 0 | m_alerts.emplace_alert<dht_immutable_item_alert>(target, i.value()); |
6117 | 0 | } |
6118 | | |
6119 | | void session_impl::dht_get_immutable_item(sha1_hash const& target) |
6120 | 0 | { |
6121 | 0 | if (!m_dht) return; |
6122 | 0 | m_dht->get_item(target, std::bind(&session_impl::get_immutable_callback |
6123 | 0 | , this, target, _1)); |
6124 | 0 | } |
6125 | | |
6126 | | // callback for dht_mutable_get |
6127 | | void session_impl::get_mutable_callback(dht::item const& i |
6128 | | , bool const authoritative) |
6129 | 0 | { |
6130 | 0 | TORRENT_ASSERT(i.is_mutable()); |
6131 | 0 | m_alerts.emplace_alert<dht_mutable_item_alert>(i.pk().bytes |
6132 | 0 | , i.sig().bytes, i.seq().value |
6133 | 0 | , i.salt(), i.value(), authoritative); |
6134 | 0 | } |
6135 | | |
6136 | | // key is a 32-byte binary string, the public key to look up. |
6137 | | // the salt is optional |
6138 | | // TODO: 3 use public_key here instead of std::array |
6139 | | void session_impl::dht_get_mutable_item(std::array<char, 32> key |
6140 | | , std::string salt) |
6141 | 0 | { |
6142 | 0 | if (!m_dht) return; |
6143 | 0 | m_dht->get_item(dht::public_key(key.data()), std::bind(&session_impl::get_mutable_callback |
6144 | 0 | , this, _1, _2), std::move(salt)); |
6145 | 0 | } |
6146 | | |
6147 | | namespace { |
6148 | | |
6149 | | void on_dht_put_immutable_item(aux::alert_manager& alerts, sha1_hash target, int num) |
6150 | 0 | { |
6151 | 0 | if (alerts.should_post<dht_put_alert>()) |
6152 | 0 | alerts.emplace_alert<dht_put_alert>(target, num); |
6153 | 0 | } |
6154 | | |
6155 | | void on_dht_put_mutable_item(aux::alert_manager& alerts, dht::item const& i, int num) |
6156 | 0 | { |
6157 | 0 | if (alerts.should_post<dht_put_alert>()) |
6158 | 0 | { |
6159 | 0 | dht::signature const sig = i.sig(); |
6160 | 0 | dht::public_key const pk = i.pk(); |
6161 | 0 | dht::sequence_number const seq = i.seq(); |
6162 | 0 | std::string salt = i.salt(); |
6163 | 0 | alerts.emplace_alert<dht_put_alert>(pk.bytes, sig.bytes |
6164 | 0 | , std::move(salt), seq.value, num); |
6165 | 0 | } |
6166 | 0 | } |
6167 | | |
6168 | | void put_mutable_callback(dht::item& i |
6169 | | , std::function<void(entry&, std::array<char, 64>& |
6170 | | , std::int64_t&, std::string const&)> cb) |
6171 | 0 | { |
6172 | 0 | entry value = i.value(); |
6173 | 0 | dht::signature sig = i.sig(); |
6174 | 0 | dht::public_key pk = i.pk(); |
6175 | 0 | dht::sequence_number seq = i.seq(); |
6176 | 0 | std::string salt = i.salt(); |
6177 | 0 | cb(value, sig.bytes, seq.value, salt); |
6178 | 0 | i.assign(std::move(value), salt, seq, pk, sig); |
6179 | 0 | } |
6180 | | |
6181 | | void on_dht_get_peers(aux::alert_manager& alerts, sha1_hash info_hash, std::vector<tcp::endpoint> const& peers) |
6182 | 0 | { |
6183 | 0 | if (alerts.should_post<dht_get_peers_reply_alert>()) |
6184 | 0 | alerts.emplace_alert<dht_get_peers_reply_alert>(info_hash, peers); |
6185 | 0 | } |
6186 | | |
6187 | | void on_direct_response(aux::alert_manager& alerts, client_data_t userdata, dht::msg const& msg) |
6188 | 0 | { |
6189 | 0 | if (msg.message.type() == bdecode_node::none_t) |
6190 | 0 | alerts.emplace_alert<dht_direct_response_alert>(userdata, msg.addr); |
6191 | 0 | else |
6192 | 0 | alerts.emplace_alert<dht_direct_response_alert>(userdata, msg.addr, msg.message); |
6193 | 0 | } |
6194 | | |
6195 | | } // anonymous namespace |
6196 | | |
6197 | | void session_impl::dht_put_immutable_item(entry const& data, sha1_hash target) |
6198 | 0 | { |
6199 | 0 | if (!m_dht) return; |
6200 | 0 | m_dht->put_item(data, std::bind(&on_dht_put_immutable_item, std::ref(m_alerts) |
6201 | 0 | , target, _1)); |
6202 | 0 | } |
6203 | | |
6204 | | void session_impl::dht_put_mutable_item(std::array<char, 32> key |
6205 | | , std::function<void(entry&, std::array<char,64>& |
6206 | | , std::int64_t&, std::string const&)> cb |
6207 | | , std::string salt) |
6208 | 0 | { |
6209 | 0 | if (!m_dht) return; |
6210 | 0 | m_dht->put_item(dht::public_key(key.data()) |
6211 | 0 | , std::bind(&on_dht_put_mutable_item, std::ref(m_alerts), _1, _2) |
6212 | 0 | , std::bind(&put_mutable_callback, _1, std::move(cb)), salt); |
6213 | 0 | } |
6214 | | |
6215 | | void session_impl::dht_get_peers(sha1_hash const& info_hash) |
6216 | 0 | { |
6217 | 0 | if (!m_dht) return; |
6218 | 0 | m_dht->get_peers(info_hash, std::bind(&on_dht_get_peers, std::ref(m_alerts), info_hash, _1)); |
6219 | 0 | } |
6220 | | |
6221 | | void session_impl::dht_announce(sha1_hash const& info_hash, int port, dht::announce_flags_t const flags) |
6222 | 0 | { |
6223 | 0 | if (!m_dht) return; |
6224 | 0 | m_dht->announce(info_hash, port, flags, std::bind(&on_dht_get_peers, std::ref(m_alerts), info_hash, _1)); |
6225 | 0 | } |
6226 | | |
6227 | | void session_impl::dht_live_nodes(sha1_hash const& nid) |
6228 | 0 | { |
6229 | 0 | if (!m_dht) return; |
6230 | 0 | auto nodes = m_dht->live_nodes(nid); |
6231 | 0 | m_alerts.emplace_alert<dht_live_nodes_alert>(nid, nodes); |
6232 | 0 | } |
6233 | | |
6234 | | void session_impl::dht_sample_infohashes(udp::endpoint const& ep, sha1_hash const& target) |
6235 | 0 | { |
6236 | 0 | if (!m_dht) return; |
6237 | 0 | m_dht->sample_infohashes(ep, target, [this, ep](sha1_hash const& nid |
6238 | 0 | , time_duration const interval |
6239 | 0 | , int const num, std::vector<sha1_hash> samples |
6240 | 0 | , std::vector<std::pair<sha1_hash, udp::endpoint>> nodes) |
6241 | 0 | { |
6242 | 0 | m_alerts.emplace_alert<dht_sample_infohashes_alert>(nid |
6243 | 0 | , ep, interval, num, std::move(samples), std::move(nodes)); |
6244 | 0 | }); |
6245 | 0 | } |
6246 | | |
6247 | | void session_impl::dht_direct_request(udp::endpoint const& ep, entry& e, client_data_t userdata) |
6248 | 0 | { |
6249 | 0 | if (!m_dht) return; |
6250 | 0 | m_dht->direct_request(ep, e, std::bind(&on_direct_response, std::ref(m_alerts), userdata, _1)); |
6251 | 0 | } |
6252 | | |
6253 | | #endif |
6254 | | |
6255 | | bool session_impl::is_listening() const |
6256 | 0 | { |
6257 | 0 | return !m_listen_sockets.empty(); |
6258 | 0 | } |
6259 | | |
6260 | | session_impl::~session_impl() |
6261 | 1.83k | { |
6262 | | // since we're destructing the session, no more alerts will make it out to |
6263 | | // the user. So stop posting them now |
6264 | 1.83k | m_alerts.set_alert_mask({}); |
6265 | 1.83k | m_alerts.set_notify_function({}); |
6266 | | |
6267 | | // this is not allowed to be the network thread! |
6268 | | // TORRENT_ASSERT(is_not_thread()); |
6269 | | // TODO: asserts that no outstanding async operations are still in flight |
6270 | | |
6271 | | // this can happen if we end the io_context run loop with an exception |
6272 | 1.83k | m_connections.clear(); |
6273 | 1.83k | for (auto& t : m_torrents) |
6274 | 0 | { |
6275 | 0 | t->panic(); |
6276 | 0 | t->abort(); |
6277 | 0 | } |
6278 | 1.83k | m_torrents.clear(); |
6279 | | |
6280 | | // this has probably been called already, but in case of sudden |
6281 | | // termination through an exception, it may not have been done |
6282 | 1.83k | abort_stage2(); |
6283 | | |
6284 | | #if defined TORRENT_ASIO_DEBUGGING |
6285 | | FILE* f = fopen("wakeups.log", "w+"); |
6286 | | if (f != nullptr) |
6287 | | { |
6288 | | time_point m = min_time(); |
6289 | | if (!_wakeups.empty()) m = _wakeups[0].timestamp; |
6290 | | time_point prev = m; |
6291 | | std::uint64_t prev_csw = 0; |
6292 | | if (!_wakeups.empty()) prev_csw = _wakeups[0].context_switches; |
6293 | | std::fprintf(f, "abs. time\trel. time\tctx switch\tidle-wakeup\toperation\n"); |
6294 | | for (wakeup_t const& w : _wakeups) |
6295 | | { |
6296 | | bool const idle_wakeup = w.context_switches > prev_csw; |
6297 | | std::fprintf(f, "%" PRId64 "\t%" PRId64 "\t%" PRId64 "\t%c\t%s\n" |
6298 | | , total_microseconds(w.timestamp - m) |
6299 | | , total_microseconds(w.timestamp - prev) |
6300 | | , w.context_switches |
6301 | | , idle_wakeup ? '*' : '.' |
6302 | | , w.operation); |
6303 | | prev = w.timestamp; |
6304 | | prev_csw = w.context_switches; |
6305 | | } |
6306 | | fclose(f); |
6307 | | } |
6308 | | #endif |
6309 | 1.83k | } |
6310 | | |
6311 | | #if TORRENT_ABI_VERSION == 1 |
6312 | | int session_impl::max_connections() const |
6313 | 0 | { |
6314 | 0 | return m_settings.get_int(settings_pack::connections_limit); |
6315 | 0 | } |
6316 | | |
6317 | | int session_impl::max_uploads() const |
6318 | 0 | { |
6319 | 0 | return m_settings.get_int(settings_pack::unchoke_slots_limit); |
6320 | 0 | } |
6321 | | |
6322 | | void session_impl::set_local_download_rate_limit(int bytes_per_second) |
6323 | 0 | { |
6324 | 0 | INVARIANT_CHECK; |
6325 | 0 | settings_pack p; |
6326 | 0 | p.set_int(settings_pack::local_download_rate_limit, bytes_per_second); |
6327 | 0 | apply_settings_pack_impl(p); |
6328 | 0 | } |
6329 | | |
6330 | | void session_impl::set_local_upload_rate_limit(int bytes_per_second) |
6331 | 0 | { |
6332 | 0 | INVARIANT_CHECK; |
6333 | 0 | settings_pack p; |
6334 | 0 | p.set_int(settings_pack::local_upload_rate_limit, bytes_per_second); |
6335 | 0 | apply_settings_pack_impl(p); |
6336 | 0 | } |
6337 | | |
6338 | | void session_impl::set_download_rate_limit_depr(int bytes_per_second) |
6339 | 0 | { |
6340 | 0 | INVARIANT_CHECK; |
6341 | 0 | settings_pack p; |
6342 | 0 | p.set_int(settings_pack::download_rate_limit, bytes_per_second); |
6343 | 0 | apply_settings_pack_impl(p); |
6344 | 0 | } |
6345 | | |
6346 | | void session_impl::set_upload_rate_limit_depr(int bytes_per_second) |
6347 | 0 | { |
6348 | 0 | INVARIANT_CHECK; |
6349 | 0 | settings_pack p; |
6350 | 0 | p.set_int(settings_pack::upload_rate_limit, bytes_per_second); |
6351 | 0 | apply_settings_pack_impl(p); |
6352 | 0 | } |
6353 | | |
6354 | | void session_impl::set_max_connections(int limit) |
6355 | 0 | { |
6356 | 0 | INVARIANT_CHECK; |
6357 | 0 | settings_pack p; |
6358 | 0 | p.set_int(settings_pack::connections_limit, limit); |
6359 | 0 | apply_settings_pack_impl(p); |
6360 | 0 | } |
6361 | | |
6362 | | void session_impl::set_max_uploads(int limit) |
6363 | 0 | { |
6364 | 0 | INVARIANT_CHECK; |
6365 | 0 | settings_pack p; |
6366 | 0 | p.set_int(settings_pack::unchoke_slots_limit, limit); |
6367 | 0 | apply_settings_pack_impl(p); |
6368 | 0 | } |
6369 | | |
6370 | | int session_impl::local_upload_rate_limit() const |
6371 | 0 | { |
6372 | 0 | return upload_rate_limit(m_local_peer_class); |
6373 | 0 | } |
6374 | | |
6375 | | int session_impl::local_download_rate_limit() const |
6376 | 0 | { |
6377 | 0 | return download_rate_limit(m_local_peer_class); |
6378 | 0 | } |
6379 | | |
6380 | | int session_impl::upload_rate_limit_depr() const |
6381 | 0 | { |
6382 | 0 | return upload_rate_limit(m_global_class); |
6383 | 0 | } |
6384 | | |
6385 | | int session_impl::download_rate_limit_depr() const |
6386 | 0 | { |
6387 | 0 | return download_rate_limit(m_global_class); |
6388 | 0 | } |
6389 | | #endif // DEPRECATE |
6390 | | |
6391 | | |
6392 | | // TODO: 2 this should be factored into the udp socket, so we only have the |
6393 | | // code once |
6394 | | void session_impl::update_peer_dscp() |
6395 | 3.67k | { |
6396 | 3.67k | int const value = m_settings.get_int(settings_pack::peer_dscp); |
6397 | 3.67k | for (auto const& l : m_listen_sockets) |
6398 | 1.83k | { |
6399 | 1.83k | if (l->sock) |
6400 | 1.83k | { |
6401 | 1.83k | error_code ec; |
6402 | 1.83k | set_traffic_class(*l->sock, value, ec); |
6403 | | |
6404 | | #ifndef TORRENT_DISABLE_LOGGING |
6405 | | if (should_log()) |
6406 | | { |
6407 | | session_log(">>> SET_DSCP [ tcp (%s %d) value: %x e: %s ]" |
6408 | | , l->sock->local_endpoint().address().to_string().c_str() |
6409 | | , l->sock->local_endpoint().port(), std::uint32_t(value), ec.message().c_str()); |
6410 | | } |
6411 | | #endif |
6412 | 1.83k | } |
6413 | | |
6414 | 1.83k | if (l->udp_sock) |
6415 | 1.83k | { |
6416 | 1.83k | error_code ec; |
6417 | 1.83k | set_traffic_class(l->udp_sock->sock, value, ec); |
6418 | | |
6419 | | #ifndef TORRENT_DISABLE_LOGGING |
6420 | | if (should_log()) |
6421 | | { |
6422 | | session_log(">>> SET_DSCP [ udp (%s %d) value: %x e: %s ]" |
6423 | | , l->udp_sock->sock.local_endpoint().address().to_string().c_str() |
6424 | | , l->udp_sock->sock.local_port() |
6425 | | , std::uint32_t(value), ec.message().c_str()); |
6426 | | } |
6427 | | #endif |
6428 | 1.83k | } |
6429 | 1.83k | } |
6430 | 3.67k | } |
6431 | | |
6432 | | void session_impl::update_user_agent() |
6433 | 1.83k | { |
6434 | | // replace all occurrences of '\n' with ' '. |
6435 | 1.83k | std::string agent = m_settings.get_str(settings_pack::user_agent); |
6436 | 1.83k | std::string::iterator i = agent.begin(); |
6437 | 1.83k | while ((i = std::find(i, agent.end(), '\n')) |
6438 | 1.83k | != agent.end()) |
6439 | 0 | *i = ' '; |
6440 | 1.83k | m_settings.set_str(settings_pack::user_agent, agent); |
6441 | 1.83k | } |
6442 | | |
6443 | | void session_impl::update_unchoke_limit() |
6444 | 1.83k | { |
6445 | 1.83k | int const allowed_upload_slots = get_int_setting(settings_pack::unchoke_slots_limit); |
6446 | | |
6447 | 1.83k | m_stats_counters.set_value(counters::num_unchoke_slots |
6448 | 1.83k | , allowed_upload_slots); |
6449 | | |
6450 | 1.83k | if (m_settings.get_int(settings_pack::num_optimistic_unchoke_slots) |
6451 | 1.83k | >= allowed_upload_slots / 2) |
6452 | 0 | { |
6453 | 0 | if (m_alerts.should_post<performance_alert>()) |
6454 | 0 | m_alerts.emplace_alert<performance_alert>(torrent_handle() |
6455 | 0 | , performance_alert::too_many_optimistic_unchoke_slots); |
6456 | 0 | } |
6457 | | |
6458 | 1.83k | if (settings().get_int(settings_pack::choking_algorithm) != settings_pack::fixed_slots_choker) |
6459 | 0 | return; |
6460 | | |
6461 | 1.83k | if (allowed_upload_slots == std::numeric_limits<int>::max()) |
6462 | 0 | { |
6463 | | // this means we're not applying upload slot limits, unchoke |
6464 | | // everyone |
6465 | 0 | for (auto const& p : m_connections) |
6466 | 0 | { |
6467 | 0 | if (p->is_disconnecting() |
6468 | 0 | || p->is_connecting() |
6469 | 0 | || !p->is_choked() |
6470 | 0 | || p->in_handshake() |
6471 | 0 | || p->ignore_unchoke_slots() |
6472 | 0 | ) |
6473 | 0 | continue; |
6474 | | |
6475 | 0 | auto const t = p->associated_torrent().lock(); |
6476 | 0 | t->unchoke_peer(*p); |
6477 | 0 | } |
6478 | 0 | } |
6479 | 1.83k | else |
6480 | 1.83k | { |
6481 | | // trigger recalculating unchoke slots |
6482 | 1.83k | m_unchoke_time_scaler = 0; |
6483 | 1.83k | } |
6484 | 1.83k | } |
6485 | | |
6486 | | void session_impl::update_connection_speed() |
6487 | 1.83k | { |
6488 | 1.83k | if (m_settings.get_int(settings_pack::connection_speed) < 0) |
6489 | 0 | m_settings.set_int(settings_pack::connection_speed, 200); |
6490 | 1.83k | } |
6491 | | |
6492 | | void session_impl::update_alert_queue_size() |
6493 | 1.83k | { |
6494 | 1.83k | m_alerts.set_alert_queue_size_limit(m_settings.get_int(settings_pack::alert_queue_size)); |
6495 | 1.83k | } |
6496 | | |
6497 | | bool session_impl::preemptive_unchoke() const |
6498 | 0 | { |
6499 | 0 | if (settings().get_int(settings_pack::choking_algorithm) != settings_pack::fixed_slots_choker) return false; |
6500 | 0 | return m_stats_counters[counters::num_peers_up_unchoked] |
6501 | 0 | < m_stats_counters[counters::num_unchoke_slots] |
6502 | 0 | || m_settings.get_int(settings_pack::unchoke_slots_limit) < 0; |
6503 | 0 | } |
6504 | | |
6505 | | void session_impl::update_dht_upload_rate_limit() |
6506 | 1.83k | { |
6507 | 1.83k | #ifndef TORRENT_DISABLE_DHT |
6508 | 1.83k | if (m_settings.get_int(settings_pack::dht_upload_rate_limit) > std::numeric_limits<int>::max() / 3) |
6509 | 0 | { |
6510 | 0 | m_settings.set_int(settings_pack::dht_upload_rate_limit, std::numeric_limits<int>::max() / 3); |
6511 | 0 | } |
6512 | 1.83k | #endif |
6513 | 1.83k | } |
6514 | | |
6515 | | void session_impl::update_disk_threads() |
6516 | 3.67k | { |
6517 | 3.67k | if (m_settings.get_int(settings_pack::aio_threads) < 0) |
6518 | 0 | m_settings.set_int(settings_pack::aio_threads, 0); |
6519 | 3.67k | if (m_settings.get_int(settings_pack::hashing_threads) < 0) |
6520 | 0 | m_settings.set_int(settings_pack::hashing_threads, 0); |
6521 | 3.67k | } |
6522 | | |
6523 | | void session_impl::update_report_web_seed_downloads() |
6524 | 1.83k | { |
6525 | | // if this flag changed, update all web seed connections |
6526 | 1.83k | bool report = m_settings.get_bool(settings_pack::report_web_seed_downloads); |
6527 | 1.83k | for (auto const& c : m_connections) |
6528 | 0 | { |
6529 | 0 | connection_type const type = c->type(); |
6530 | 0 | if (type == connection_type::url_seed |
6531 | 0 | || type == connection_type::http_seed) |
6532 | 0 | c->ignore_stats(!report); |
6533 | 0 | } |
6534 | 1.83k | } |
6535 | | |
6536 | | void session_impl::trigger_auto_manage() |
6537 | 13.7k | { |
6538 | 13.7k | if (m_pending_auto_manage || m_abort) return; |
6539 | | |
6540 | | // we recalculated auto-managed torrents less than a second ago, |
6541 | | // put it off one second. |
6542 | 12.6k | if (time_now() - m_last_auto_manage < seconds(1)) |
6543 | 12.6k | { |
6544 | 12.6k | m_auto_manage_time_scaler = 0; |
6545 | 12.6k | return; |
6546 | 12.6k | } |
6547 | 0 | m_pending_auto_manage = true; |
6548 | 0 | m_need_auto_manage = true; |
6549 | |
|
6550 | 0 | post(m_io_context, [this]{ wrap(&session_impl::on_trigger_auto_manage); }); |
6551 | 0 | } |
6552 | | |
6553 | | void session_impl::on_trigger_auto_manage() |
6554 | 0 | { |
6555 | 0 | TORRENT_ASSERT(m_pending_auto_manage); |
6556 | 0 | if (!m_need_auto_manage || m_abort) |
6557 | 0 | { |
6558 | 0 | m_pending_auto_manage = false; |
6559 | 0 | return; |
6560 | 0 | } |
6561 | | // don't clear m_pending_auto_manage until after we've |
6562 | | // recalculated the auto managed torrents. The auto-managed |
6563 | | // logic may trigger another auto-managed event otherwise |
6564 | 0 | recalculate_auto_managed_torrents(); |
6565 | 0 | m_pending_auto_manage = false; |
6566 | 0 | } |
6567 | | |
6568 | | void session_impl::update_socket_buffer_size() |
6569 | 3.67k | { |
6570 | 3.67k | for (auto const& l : m_listen_sockets) |
6571 | 0 | { |
6572 | 0 | error_code ec; |
6573 | 0 | set_socket_buffer_size(l->udp_sock->sock, m_settings, ec); |
6574 | | #ifndef TORRENT_DISABLE_LOGGING |
6575 | | if (ec && should_log()) |
6576 | | { |
6577 | | session_log("listen socket buffer size [ udp %s:%d ] %s" |
6578 | | , l->udp_sock->sock.local_endpoint().address().to_string().c_str() |
6579 | | , l->udp_sock->sock.local_port(), print_error(ec).c_str()); |
6580 | | } |
6581 | | #endif |
6582 | 0 | ec.clear(); |
6583 | 0 | set_socket_buffer_size(*l->sock, m_settings, ec); |
6584 | | #ifndef TORRENT_DISABLE_LOGGING |
6585 | | if (ec && should_log()) |
6586 | | { |
6587 | | session_log("listen socket buffer size [ tcp %s:%d] %s" |
6588 | | , l->sock->local_endpoint().address().to_string().c_str() |
6589 | | , l->sock->local_endpoint().port(), print_error(ec).c_str()); |
6590 | | } |
6591 | | #endif |
6592 | 0 | } |
6593 | 3.67k | } |
6594 | | |
6595 | | void session_impl::update_dht_announce_interval() |
6596 | 1.83k | { |
6597 | 1.83k | #ifndef TORRENT_DISABLE_DHT |
6598 | 1.83k | if (!m_dht) |
6599 | 1.83k | { |
6600 | | #ifndef TORRENT_DISABLE_LOGGING |
6601 | | session_log("not starting DHT announce timer: m_dht == nullptr"); |
6602 | | #endif |
6603 | 1.83k | return; |
6604 | 1.83k | } |
6605 | | |
6606 | 0 | m_dht_interval_update_torrents = int(m_torrents.size()); |
6607 | |
|
6608 | 0 | if (m_abort) |
6609 | 0 | { |
6610 | | #ifndef TORRENT_DISABLE_LOGGING |
6611 | | session_log("not starting DHT announce timer: m_abort set"); |
6612 | | #endif |
6613 | 0 | return; |
6614 | 0 | } |
6615 | | |
6616 | 0 | ADD_OUTSTANDING_ASYNC("session_impl::on_dht_announce"); |
6617 | 0 | int delay = std::max(1000 * m_settings.get_int(settings_pack::dht_announce_interval) |
6618 | 0 | / std::max(int(m_torrents.size()), 1), 1); |
6619 | |
|
6620 | 0 | if (!m_dht_torrents.empty()) |
6621 | 0 | { |
6622 | | // we have prioritized torrents that need |
6623 | | // an initial DHT announce. Don't wait too long |
6624 | | // until we announce those. |
6625 | 0 | delay = std::min(4000, delay); |
6626 | 0 | } |
6627 | |
|
6628 | 0 | m_dht_announce_timer.expires_after(milliseconds(delay)); |
6629 | 0 | m_dht_announce_timer.async_wait([this](error_code const& e) { |
6630 | 0 | wrap(&session_impl::on_dht_announce, e); }); |
6631 | 0 | #endif |
6632 | 0 | } |
6633 | | |
6634 | | #if TORRENT_ABI_VERSION == 1 |
6635 | | void session_impl::update_local_download_rate() |
6636 | 1.83k | { |
6637 | 1.83k | if (m_settings.get_int(settings_pack::local_download_rate_limit) < 0) |
6638 | 0 | m_settings.set_int(settings_pack::local_download_rate_limit, 0); |
6639 | 1.83k | set_download_rate_limit(m_local_peer_class |
6640 | 1.83k | , m_settings.get_int(settings_pack::local_download_rate_limit)); |
6641 | 1.83k | } |
6642 | | |
6643 | | void session_impl::update_local_upload_rate() |
6644 | 1.83k | { |
6645 | 1.83k | if (m_settings.get_int(settings_pack::local_upload_rate_limit) < 0) |
6646 | 0 | m_settings.set_int(settings_pack::local_upload_rate_limit, 0); |
6647 | 1.83k | set_upload_rate_limit(m_local_peer_class |
6648 | 1.83k | , m_settings.get_int(settings_pack::local_upload_rate_limit)); |
6649 | 1.83k | } |
6650 | | #endif |
6651 | | |
6652 | | void session_impl::update_download_rate() |
6653 | 1.83k | { |
6654 | 1.83k | if (m_settings.get_int(settings_pack::download_rate_limit) < 0) |
6655 | 0 | m_settings.set_int(settings_pack::download_rate_limit, 0); |
6656 | 1.83k | set_download_rate_limit(m_global_class |
6657 | 1.83k | , m_settings.get_int(settings_pack::download_rate_limit)); |
6658 | 1.83k | } |
6659 | | |
6660 | | void session_impl::update_upload_rate() |
6661 | 1.83k | { |
6662 | 1.83k | if (m_settings.get_int(settings_pack::upload_rate_limit) < 0) |
6663 | 0 | m_settings.set_int(settings_pack::upload_rate_limit, 0); |
6664 | 1.83k | set_upload_rate_limit(m_global_class |
6665 | 1.83k | , m_settings.get_int(settings_pack::upload_rate_limit)); |
6666 | 1.83k | } |
6667 | | |
6668 | | void session_impl::update_connections_limit() |
6669 | 1.83k | { |
6670 | 1.83k | int limit = m_settings.get_int(settings_pack::connections_limit); |
6671 | | |
6672 | 1.83k | if (limit <= 0) limit = max_open_files(); |
6673 | | |
6674 | 1.83k | m_settings.set_int(settings_pack::connections_limit, limit); |
6675 | | |
6676 | 1.83k | if (num_connections() > m_settings.get_int(settings_pack::connections_limit) |
6677 | 1.83k | && !m_torrents.empty()) |
6678 | 0 | { |
6679 | | // if we have more connections that we're allowed, disconnect |
6680 | | // peers from the torrents so that they are all as even as possible |
6681 | |
|
6682 | 0 | int to_disconnect = num_connections() - m_settings.get_int(settings_pack::connections_limit); |
6683 | |
|
6684 | 0 | int last_average = 0; |
6685 | 0 | int average = m_settings.get_int(settings_pack::connections_limit) / int(m_torrents.size()); |
6686 | | |
6687 | | // the number of slots that are unused by torrents |
6688 | 0 | int extra = m_settings.get_int(settings_pack::connections_limit) % int(m_torrents.size()); |
6689 | | |
6690 | | // run 3 iterations of this, then we're probably close enough |
6691 | 0 | for (int iter = 0; iter < 4; ++iter) |
6692 | 0 | { |
6693 | | // the number of torrents that are above average |
6694 | 0 | int num_above = 0; |
6695 | 0 | for (auto const& t : m_torrents) |
6696 | 0 | { |
6697 | 0 | int const num = t->num_peers(); |
6698 | 0 | if (num <= last_average) continue; |
6699 | 0 | if (num > average) ++num_above; |
6700 | 0 | if (num < average) extra += average - num; |
6701 | 0 | } |
6702 | | |
6703 | | // distribute extra among the torrents that are above average |
6704 | 0 | if (num_above == 0) num_above = 1; |
6705 | 0 | last_average = average; |
6706 | 0 | average += extra / num_above; |
6707 | 0 | if (extra == 0) break; |
6708 | | // save the remainder for the next iteration |
6709 | 0 | extra = extra % num_above; |
6710 | 0 | } |
6711 | |
|
6712 | 0 | for (auto const& t : m_torrents) |
6713 | 0 | { |
6714 | 0 | int const num = t->num_peers(); |
6715 | 0 | if (num <= average) continue; |
6716 | | |
6717 | | // distribute the remainder |
6718 | 0 | int my_average = average; |
6719 | 0 | if (extra > 0) |
6720 | 0 | { |
6721 | 0 | ++my_average; |
6722 | 0 | --extra; |
6723 | 0 | } |
6724 | |
|
6725 | 0 | int const disconnect = std::min(to_disconnect, num - my_average); |
6726 | 0 | to_disconnect -= disconnect; |
6727 | 0 | t->disconnect_peers(disconnect, errors::too_many_connections); |
6728 | 0 | } |
6729 | 0 | } |
6730 | 1.83k | } |
6731 | | |
6732 | | void session_impl::update_alert_mask() |
6733 | 1.83k | { |
6734 | 1.83k | m_alerts.set_alert_mask(alert_category_t( |
6735 | 1.83k | static_cast<std::uint32_t>(m_settings.get_int(settings_pack::alert_mask)))); |
6736 | 1.83k | } |
6737 | | |
6738 | | void session_impl::update_validate_https() |
6739 | 1.83k | { |
6740 | 1.83k | #if TORRENT_USE_SSL |
6741 | 1.83k | auto const flags = m_settings.get_bool(settings_pack::validate_https_trackers) |
6742 | 1.83k | ? ssl::context::verify_peer |
6743 | 1.83k | | ssl::context::verify_fail_if_no_peer_cert |
6744 | 1.83k | | ssl::context::verify_client_once |
6745 | 1.83k | : ssl::context::verify_none; |
6746 | 1.83k | error_code ec; |
6747 | 1.83k | m_ssl_ctx.set_verify_mode(flags, ec); |
6748 | | |
6749 | | #ifndef TORRENT_DISABLE_LOGGING |
6750 | | if (ec) session_log("SSL set_verify_mode failed: %s", ec.message().c_str()); |
6751 | | #endif |
6752 | 1.83k | #endif |
6753 | 1.83k | } |
6754 | | |
6755 | | void session_impl::pop_alerts(std::vector<alert*>* alerts) |
6756 | 3 | { |
6757 | 3 | m_alerts.get_all(*alerts); |
6758 | 3 | } |
6759 | | |
6760 | | #if TORRENT_ABI_VERSION == 1 |
6761 | | void session_impl::update_rate_limit_utp() |
6762 | 1.83k | { |
6763 | 1.83k | if (m_settings.get_bool(settings_pack::rate_limit_utp)) |
6764 | 1.83k | { |
6765 | | // allow the global or local peer class to limit uTP peers |
6766 | 1.83k | m_peer_class_type_filter.allow(peer_class_type_filter::utp_socket |
6767 | 1.83k | , m_global_class); |
6768 | 1.83k | m_peer_class_type_filter.allow(peer_class_type_filter::ssl_utp_socket |
6769 | 1.83k | , m_global_class); |
6770 | 1.83k | } |
6771 | 0 | else |
6772 | 0 | { |
6773 | | // don't add the global or local peer class to limit uTP peers |
6774 | 0 | m_peer_class_type_filter.disallow(peer_class_type_filter::utp_socket |
6775 | 0 | , m_global_class); |
6776 | 0 | m_peer_class_type_filter.disallow(peer_class_type_filter::ssl_utp_socket |
6777 | 0 | , m_global_class); |
6778 | 0 | } |
6779 | 1.83k | } |
6780 | | |
6781 | | void session_impl::update_ignore_rate_limits_on_local_network() |
6782 | 1.83k | { |
6783 | 1.83k | init_peer_class_filter( |
6784 | 1.83k | m_settings.get_bool(settings_pack::ignore_limits_on_local_network)); |
6785 | 1.83k | } |
6786 | | |
6787 | | // this function is called on the user's thread |
6788 | | // not the network thread |
6789 | | void session_impl::pop_alerts() |
6790 | 0 | { |
6791 | | // if we don't have any alerts in our local cache, we have to ask |
6792 | | // the alert_manager for more. It will swap our vector with its and |
6793 | | // destruct eny left-over alerts in there. |
6794 | 0 | if (m_alert_pointer_pos >= int(m_alert_pointers.size())) |
6795 | 0 | { |
6796 | 0 | pop_alerts(&m_alert_pointers); |
6797 | 0 | m_alert_pointer_pos = 0; |
6798 | 0 | } |
6799 | 0 | } |
6800 | | |
6801 | | alert const* session_impl::pop_alert() |
6802 | 0 | { |
6803 | 0 | if (m_alert_pointer_pos >= int(m_alert_pointers.size())) |
6804 | 0 | { |
6805 | 0 | pop_alerts(); |
6806 | 0 | if (m_alert_pointers.empty()) |
6807 | 0 | return nullptr; |
6808 | 0 | } |
6809 | | |
6810 | 0 | if (m_alert_pointers.empty()) return nullptr; |
6811 | | |
6812 | | // clone here to be backwards compatible, to make the client delete the |
6813 | | // alert object |
6814 | 0 | return m_alert_pointers[m_alert_pointer_pos++]; |
6815 | 0 | } |
6816 | | |
6817 | | #endif |
6818 | | |
6819 | | alert* session_impl::wait_for_alert(time_duration max_wait) |
6820 | 3 | { |
6821 | 3 | return m_alerts.wait_for_alert(max_wait); |
6822 | 3 | } |
6823 | | |
6824 | | #if TORRENT_ABI_VERSION == 1 |
6825 | | std::size_t session_impl::set_alert_queue_size_limit(std::size_t queue_size_limit_) |
6826 | 0 | { |
6827 | 0 | m_settings.set_int(settings_pack::alert_queue_size, int(queue_size_limit_)); |
6828 | 0 | return std::size_t(m_alerts.set_alert_queue_size_limit(int(queue_size_limit_))); |
6829 | 0 | } |
6830 | | #endif |
6831 | | |
6832 | | void session_impl::start_ip_notifier() |
6833 | 0 | { |
6834 | 0 | INVARIANT_CHECK; |
6835 | |
|
6836 | 0 | if (m_ip_notifier) return; |
6837 | | |
6838 | 0 | m_ip_notifier = create_ip_notifier(m_io_context); |
6839 | 0 | m_ip_notifier->async_wait([this](error_code const& e) |
6840 | 0 | { wrap(&session_impl::on_ip_change, e); }); |
6841 | 0 | } |
6842 | | |
6843 | | void session_impl::start_lsd() |
6844 | 0 | { |
6845 | 0 | INVARIANT_CHECK; |
6846 | |
|
6847 | 0 | for (auto& s : m_listen_sockets) |
6848 | 0 | { |
6849 | | // we're not looking for local peers when we're using a proxy. We |
6850 | | // want all traffic to go through the proxy |
6851 | 0 | if (s->flags & listen_socket_t::proxy) continue; |
6852 | 0 | if (s->lsd) continue; |
6853 | 0 | s->lsd = std::make_shared<lsd>(m_io_context, *this, s->local_endpoint.address() |
6854 | 0 | , s->netmask); |
6855 | 0 | error_code ec; |
6856 | 0 | s->lsd->start(ec); |
6857 | 0 | if (ec) |
6858 | 0 | { |
6859 | 0 | if (m_alerts.should_post<lsd_error_alert>()) |
6860 | 0 | m_alerts.emplace_alert<lsd_error_alert>(ec, s->local_endpoint.address()); |
6861 | 0 | s->lsd.reset(); |
6862 | 0 | } |
6863 | 0 | } |
6864 | 0 | } |
6865 | | |
6866 | | void session_impl::start_natpmp() |
6867 | 0 | { |
6868 | 0 | INVARIANT_CHECK; |
6869 | 0 | for (auto& s : m_listen_sockets) |
6870 | 0 | { |
6871 | 0 | start_natpmp(s); |
6872 | 0 | remap_ports(remap_natpmp, *s); |
6873 | 0 | } |
6874 | 0 | } |
6875 | | |
6876 | | void session_impl::start_upnp() |
6877 | 0 | { |
6878 | 0 | INVARIANT_CHECK; |
6879 | 0 | for (auto const& s : m_listen_sockets) |
6880 | 0 | { |
6881 | 0 | start_upnp(s); |
6882 | 0 | remap_ports(remap_upnp, *s); |
6883 | 0 | } |
6884 | 0 | } |
6885 | | |
6886 | | void session_impl::start_upnp(std::shared_ptr<aux::listen_socket_t> const& s) |
6887 | 0 | { |
6888 | | // until we support SSDP over an IPv6 network ( |
6889 | | // https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol ) |
6890 | | // there's no point in starting upnp on one. |
6891 | 0 | if (is_v6(s->local_endpoint)) |
6892 | 0 | return; |
6893 | | |
6894 | | // there's no point in starting the UPnP mapper for a network that isn't |
6895 | | // connected to the internet. The whole point is to forward ports through |
6896 | | // the gateway |
6897 | 0 | if ((s->flags & listen_socket_t::local_network) |
6898 | 0 | || (s->flags & listen_socket_t::proxy)) |
6899 | 0 | return; |
6900 | | |
6901 | 0 | if (!s->upnp_mapper) |
6902 | 0 | { |
6903 | | // the upnp constructor may fail and call the callbacks |
6904 | | // into the session_impl. |
6905 | 0 | s->upnp_mapper = std::make_shared<upnp>(m_io_context, m_settings |
6906 | 0 | , *this, s->local_endpoint.address().to_v4(), s->netmask.to_v4(), s->device |
6907 | 0 | , listen_socket_handle(s)); |
6908 | 0 | s->upnp_mapper->start(); |
6909 | 0 | } |
6910 | 0 | } |
6911 | | |
6912 | | std::vector<port_mapping_t> session_impl::add_port_mapping(portmap_protocol const t |
6913 | | , int const external_port |
6914 | | , int const local_port) |
6915 | 0 | { |
6916 | 0 | std::vector<port_mapping_t> ret; |
6917 | 0 | for (auto& s : m_listen_sockets) |
6918 | 0 | { |
6919 | 0 | tcp::endpoint const ep{s->local_endpoint.address(), static_cast<std::uint16_t>(local_port)}; |
6920 | 0 | if (s->upnp_mapper) ret.push_back(s->upnp_mapper->add_mapping( |
6921 | 0 | t, external_port, ep, s->device)); |
6922 | 0 | if (s->natpmp_mapper) ret.push_back(s->natpmp_mapper->add_mapping( |
6923 | 0 | t, external_port, ep, s->device)); |
6924 | 0 | } |
6925 | 0 | return ret; |
6926 | 0 | } |
6927 | | |
6928 | | void session_impl::delete_port_mapping(port_mapping_t handle) |
6929 | 0 | { |
6930 | 0 | for (auto& s : m_listen_sockets) |
6931 | 0 | { |
6932 | 0 | if (s->upnp_mapper) s->upnp_mapper->delete_mapping(handle); |
6933 | 0 | if (s->natpmp_mapper) s->natpmp_mapper->delete_mapping(handle); |
6934 | 0 | } |
6935 | 0 | } |
6936 | | |
6937 | | void session_impl::stop_ip_notifier() |
6938 | 3.67k | { |
6939 | 3.67k | if (!m_ip_notifier) return; |
6940 | | |
6941 | 0 | m_ip_notifier->cancel(); |
6942 | 0 | m_ip_notifier.reset(); |
6943 | 0 | } |
6944 | | |
6945 | | void session_impl::stop_lsd() |
6946 | 5.50k | { |
6947 | 5.50k | for (auto& s : m_listen_sockets) |
6948 | 3.67k | { |
6949 | 3.67k | if (!s->lsd) continue; |
6950 | 0 | s->lsd->close(); |
6951 | 0 | s->lsd.reset(); |
6952 | 0 | } |
6953 | 5.50k | } |
6954 | | |
6955 | | void session_impl::stop_natpmp() |
6956 | 3.67k | { |
6957 | 3.67k | for (auto& s : m_listen_sockets) |
6958 | 1.83k | { |
6959 | 1.83k | s->tcp_port_mapping[portmap_transport::natpmp] = listen_port_mapping(); |
6960 | 1.83k | s->udp_port_mapping[portmap_transport::natpmp] = listen_port_mapping(); |
6961 | 1.83k | if (!s->natpmp_mapper) continue; |
6962 | 0 | s->natpmp_mapper->close(); |
6963 | 0 | s->natpmp_mapper.reset(); |
6964 | 0 | } |
6965 | 3.67k | } |
6966 | | |
6967 | | void session_impl::stop_upnp() |
6968 | 3.67k | { |
6969 | 3.67k | for (auto& s : m_listen_sockets) |
6970 | 1.83k | { |
6971 | 1.83k | if (!s->upnp_mapper) continue; |
6972 | 0 | s->tcp_port_mapping[portmap_transport::upnp] = listen_port_mapping(); |
6973 | 0 | s->udp_port_mapping[portmap_transport::upnp] = listen_port_mapping(); |
6974 | 0 | s->upnp_mapper->close(); |
6975 | 0 | s->upnp_mapper.reset(); |
6976 | 0 | } |
6977 | 3.67k | } |
6978 | | |
6979 | | external_ip session_impl::external_address() const |
6980 | 0 | { |
6981 | 0 | address ips[2][2]; |
6982 | | |
6983 | | // take the first IP we find which matches each category |
6984 | 0 | for (auto const& i : m_listen_sockets) |
6985 | 0 | { |
6986 | 0 | address external_addr = i->external_address.external_address(); |
6987 | 0 | if (ips[0][external_addr.is_v6()] == address()) |
6988 | 0 | ips[0][external_addr.is_v6()] = external_addr; |
6989 | 0 | address local_addr = i->local_endpoint.address(); |
6990 | 0 | if (ips[is_local(local_addr)][local_addr.is_v6()] == address()) |
6991 | 0 | ips[is_local(local_addr)][local_addr.is_v6()] = local_addr; |
6992 | 0 | } |
6993 | |
|
6994 | 0 | return {ips[1][0], ips[0][0], ips[1][1], ips[0][1]}; |
6995 | 0 | } |
6996 | | |
6997 | | // this is the DHT observer version. DHT is the implied source |
6998 | | void session_impl::set_external_address(aux::listen_socket_handle const& iface |
6999 | | , address const& ip, address const& source) |
7000 | 0 | { |
7001 | 0 | auto i = iface.m_sock.lock(); |
7002 | 0 | TORRENT_ASSERT(i); |
7003 | 0 | if (!i) return; |
7004 | 0 | set_external_address(i, ip, source_dht, source); |
7005 | 0 | } |
7006 | | |
7007 | | void session_impl::get_peers(sha1_hash const& ih) |
7008 | 0 | { |
7009 | 0 | if (!m_alerts.should_post<dht_get_peers_alert>()) return; |
7010 | 0 | m_alerts.emplace_alert<dht_get_peers_alert>(ih); |
7011 | 0 | } |
7012 | | |
7013 | | void session_impl::announce(sha1_hash const& ih, address const& addr |
7014 | | , int port) |
7015 | 0 | { |
7016 | 0 | if (!m_alerts.should_post<dht_announce_alert>()) return; |
7017 | 0 | m_alerts.emplace_alert<dht_announce_alert>(addr, port, ih); |
7018 | 0 | } |
7019 | | |
7020 | | void session_impl::outgoing_get_peers(sha1_hash const& target |
7021 | | , sha1_hash const& sent_target, udp::endpoint const& ep) |
7022 | 0 | { |
7023 | 0 | if (!m_alerts.should_post<dht_outgoing_get_peers_alert>()) return; |
7024 | 0 | m_alerts.emplace_alert<dht_outgoing_get_peers_alert>(target, sent_target, ep); |
7025 | 0 | } |
7026 | | |
7027 | | #ifndef TORRENT_DISABLE_LOGGING |
7028 | | bool session_impl::should_log(module_t) const |
7029 | | { |
7030 | | return m_alerts.should_post<dht_log_alert>(); |
7031 | | } |
7032 | | |
7033 | | TORRENT_FORMAT(3,4) |
7034 | | void session_impl::log(module_t m, char const* fmt, ...) |
7035 | | { |
7036 | | if (!m_alerts.should_post<dht_log_alert>()) return; |
7037 | | |
7038 | | va_list v; |
7039 | | va_start(v, fmt); |
7040 | | m_alerts.emplace_alert<dht_log_alert>( |
7041 | | static_cast<dht_log_alert::dht_module_t>(m), fmt, v); |
7042 | | va_end(v); |
7043 | | } |
7044 | | |
7045 | | void session_impl::log_packet(message_direction_t dir, span<char const> pkt |
7046 | | , udp::endpoint const& node) |
7047 | | { |
7048 | | if (!m_alerts.should_post<dht_pkt_alert>()) return; |
7049 | | |
7050 | | dht_pkt_alert::direction_t d = dir == dht::dht_logger::incoming_message |
7051 | | ? dht_pkt_alert::incoming : dht_pkt_alert::outgoing; |
7052 | | |
7053 | | m_alerts.emplace_alert<dht_pkt_alert>(pkt, d, node); |
7054 | | } |
7055 | | |
7056 | | bool session_impl::should_log_portmap(portmap_transport) const |
7057 | | { |
7058 | | return m_alerts.should_post<portmap_log_alert>(); |
7059 | | } |
7060 | | |
7061 | | void session_impl::log_portmap(portmap_transport transport, char const* msg |
7062 | | , listen_socket_handle const& ls) const |
7063 | | { |
7064 | | listen_socket_t const* listen_socket = ls.get(); |
7065 | | if (m_alerts.should_post<portmap_log_alert>()) |
7066 | | m_alerts.emplace_alert<portmap_log_alert>(transport, msg |
7067 | | , listen_socket ? listen_socket->local_endpoint.address() : address()); |
7068 | | } |
7069 | | |
7070 | | bool session_impl::should_log_lsd() const |
7071 | | { |
7072 | | return m_alerts.should_post<log_alert>(); |
7073 | | } |
7074 | | |
7075 | | void session_impl::log_lsd(char const* msg) const |
7076 | | { |
7077 | | if (m_alerts.should_post<log_alert>()) |
7078 | | m_alerts.emplace_alert<log_alert>(msg); |
7079 | | } |
7080 | | #endif |
7081 | | |
7082 | | bool session_impl::on_dht_request(string_view query |
7083 | | , dht::msg const& request, entry& response) |
7084 | 0 | { |
7085 | 0 | #ifndef TORRENT_DISABLE_EXTENSIONS |
7086 | 0 | for (auto const& ext : m_ses_extensions[plugins_dht_request_idx]) |
7087 | 0 | { |
7088 | 0 | if (ext->on_dht_request(query |
7089 | 0 | , request.addr, request.message, response)) |
7090 | 0 | return true; |
7091 | 0 | } |
7092 | | #else |
7093 | | TORRENT_UNUSED(query); |
7094 | | TORRENT_UNUSED(request); |
7095 | | TORRENT_UNUSED(response); |
7096 | | #endif |
7097 | 0 | return false; |
7098 | 0 | } |
7099 | | |
7100 | | void session_impl::set_external_address( |
7101 | | tcp::endpoint const& local_endpoint, address const& ip |
7102 | | , ip_source_t const source_type, address const& source) |
7103 | 0 | { |
7104 | 0 | auto sock = std::find_if(m_listen_sockets.begin(), m_listen_sockets.end() |
7105 | 0 | , [&](std::shared_ptr<listen_socket_t> const& v) |
7106 | 0 | { return v->local_endpoint.address() == local_endpoint.address(); }); |
7107 | |
|
7108 | 0 | if (sock != m_listen_sockets.end()) |
7109 | 0 | set_external_address(*sock, ip, source_type, source); |
7110 | 0 | } |
7111 | | |
7112 | | void session_impl::set_external_address(std::shared_ptr<listen_socket_t> const& sock |
7113 | | , address const& ip, ip_source_t const source_type, address const& source) |
7114 | 0 | { |
7115 | 0 | if (!sock->external_address.cast_vote(ip, source_type, source)) return; |
7116 | | |
7117 | | #ifndef TORRENT_DISABLE_LOGGING |
7118 | | if (should_log()) |
7119 | | { |
7120 | | session_log("external address updated for %s [ new-ip: %s type: %d last-voter: %s ]" |
7121 | | , sock->device.empty() ? print_endpoint(sock->local_endpoint).c_str() : sock->device.c_str() |
7122 | | , print_address(ip).c_str() |
7123 | | , static_cast<std::uint8_t>(source_type) |
7124 | | , print_address(source).c_str()); |
7125 | | } |
7126 | | #endif |
7127 | | |
7128 | 0 | if (m_alerts.should_post<external_ip_alert>()) |
7129 | 0 | m_alerts.emplace_alert<external_ip_alert>(ip); |
7130 | |
|
7131 | 0 | for (auto const& t : m_torrents) |
7132 | 0 | { |
7133 | 0 | t->new_external_ip(); |
7134 | 0 | } |
7135 | | |
7136 | | // since we have a new external IP now, we need to |
7137 | | // restart the DHT with a new node ID |
7138 | |
|
7139 | 0 | #ifndef TORRENT_DISABLE_DHT |
7140 | 0 | if (m_dht) m_dht->update_node_id(sock); |
7141 | 0 | #endif |
7142 | 0 | } |
7143 | | |
7144 | | #if TORRENT_USE_INVARIANT_CHECKS |
7145 | | void session_impl::check_invariant() const |
7146 | | { |
7147 | | TORRENT_ASSERT(is_single_thread()); |
7148 | | |
7149 | | if (m_settings.get_int(settings_pack::unchoke_slots_limit) < 0 |
7150 | | && m_settings.get_int(settings_pack::choking_algorithm) == settings_pack::fixed_slots_choker) |
7151 | | TORRENT_ASSERT(m_stats_counters[counters::num_unchoke_slots] == std::numeric_limits<int>::max()); |
7152 | | |
7153 | | #ifndef TORRENT_EXPENSIVE_INVARIANT_CHECKS |
7154 | | // this can get expensive when there are a lot of torrents |
7155 | | if (m_download_queue.size() < 500) |
7156 | | #endif |
7157 | | { |
7158 | | for (torrent_list_index_t l{}; l != m_torrent_lists.end_index(); ++l) |
7159 | | { |
7160 | | std::vector<torrent*> const& list = m_torrent_lists[l]; |
7161 | | for (auto const& i : list) |
7162 | | { |
7163 | | TORRENT_ASSERT(i->m_links[l].in_list()); |
7164 | | } |
7165 | | |
7166 | | queue_position_t idx{}; |
7167 | | for (auto t : m_download_queue) |
7168 | | { |
7169 | | TORRENT_ASSERT(t->queue_position() == idx); |
7170 | | ++idx; |
7171 | | } |
7172 | | } |
7173 | | } |
7174 | | |
7175 | | int const num_gauges = counters::num_error_torrents - counters::num_checking_torrents + 1; |
7176 | | aux::array<int, num_gauges> torrent_state_gauges; |
7177 | | torrent_state_gauges.fill(0); |
7178 | | |
7179 | | #if defined TORRENT_EXPENSIVE_INVARIANT_CHECKS |
7180 | | std::unordered_set<queue_position_t> unique; |
7181 | | #endif |
7182 | | |
7183 | | int num_active_downloading = 0; |
7184 | | int num_active_finished = 0; |
7185 | | int total_downloaders = 0; |
7186 | | for (auto const& tor : m_torrents) |
7187 | | { |
7188 | | std::shared_ptr<torrent> const& t = tor; |
7189 | | if (t->want_peers_download()) ++num_active_downloading; |
7190 | | if (t->want_peers_finished()) ++num_active_finished; |
7191 | | TORRENT_ASSERT(!(t->want_peers_download() && t->want_peers_finished())); |
7192 | | |
7193 | | int const state = t->current_stats_state() - counters::num_checking_torrents; |
7194 | | if (state != torrent::no_gauge_state) |
7195 | | { |
7196 | | ++torrent_state_gauges[state]; |
7197 | | } |
7198 | | |
7199 | | queue_position_t const pos = t->queue_position(); |
7200 | | if (pos < queue_position_t{}) |
7201 | | { |
7202 | | TORRENT_ASSERT(pos == no_pos); |
7203 | | continue; |
7204 | | } |
7205 | | ++total_downloaders; |
7206 | | |
7207 | | #if defined TORRENT_EXPENSIVE_INVARIANT_CHECKS |
7208 | | unique.insert(t->queue_position()); |
7209 | | #endif |
7210 | | } |
7211 | | |
7212 | | for (int i = 0, j = counters::num_checking_torrents; |
7213 | | j < counters::num_error_torrents + 1; ++i, ++j) |
7214 | | { |
7215 | | TORRENT_ASSERT(torrent_state_gauges[i] == m_stats_counters[j]); |
7216 | | } |
7217 | | |
7218 | | #if defined TORRENT_EXPENSIVE_INVARIANT_CHECKS |
7219 | | TORRENT_ASSERT(int(unique.size()) == total_downloaders); |
7220 | | #endif |
7221 | | TORRENT_ASSERT(num_active_downloading == int(m_torrent_lists[torrent_want_peers_download].size())); |
7222 | | TORRENT_ASSERT(num_active_finished == int(m_torrent_lists[torrent_want_peers_finished].size())); |
7223 | | |
7224 | | std::unordered_set<peer_connection*> unique_peers; |
7225 | | |
7226 | | int unchokes = 0; |
7227 | | int unchokes_all = 0; |
7228 | | int num_optimistic = 0; |
7229 | | int disk_queue[2] = {0, 0}; |
7230 | | for (auto const& p : m_connections) |
7231 | | { |
7232 | | TORRENT_ASSERT(p); |
7233 | | if (p->is_disconnecting()) continue; |
7234 | | |
7235 | | std::shared_ptr<torrent> t = p->associated_torrent().lock(); |
7236 | | TORRENT_ASSERT(unique_peers.find(p.get()) == unique_peers.end()); |
7237 | | unique_peers.insert(p.get()); |
7238 | | |
7239 | | if (p->m_channel_state[0] & peer_info::bw_disk) ++disk_queue[0]; |
7240 | | if (p->m_channel_state[1] & peer_info::bw_disk) ++disk_queue[1]; |
7241 | | |
7242 | | if (p->ignore_unchoke_slots()) |
7243 | | { |
7244 | | if (!p->is_choked()) ++unchokes_all; |
7245 | | continue; |
7246 | | } |
7247 | | if (!p->is_choked()) |
7248 | | { |
7249 | | ++unchokes; |
7250 | | ++unchokes_all; |
7251 | | } |
7252 | | |
7253 | | if (p->peer_info_struct() |
7254 | | && p->peer_info_struct()->optimistically_unchoked) |
7255 | | { |
7256 | | ++num_optimistic; |
7257 | | TORRENT_ASSERT(!p->is_choked()); |
7258 | | } |
7259 | | } |
7260 | | |
7261 | | for (auto const& p : m_undead_peers) |
7262 | | { |
7263 | | if (p->ignore_unchoke_slots()) |
7264 | | { |
7265 | | if (!p->is_choked()) ++unchokes_all; |
7266 | | continue; |
7267 | | } |
7268 | | if (!p->is_choked()) |
7269 | | { |
7270 | | ++unchokes_all; |
7271 | | ++unchokes; |
7272 | | } |
7273 | | |
7274 | | if (p->peer_info_struct() |
7275 | | && p->peer_info_struct()->optimistically_unchoked) |
7276 | | { |
7277 | | ++num_optimistic; |
7278 | | TORRENT_ASSERT(!p->is_choked()); |
7279 | | } |
7280 | | } |
7281 | | |
7282 | | TORRENT_ASSERT(disk_queue[peer_connection::download_channel] |
7283 | | == m_stats_counters[counters::num_peers_down_disk]); |
7284 | | TORRENT_ASSERT(disk_queue[peer_connection::upload_channel] |
7285 | | == m_stats_counters[counters::num_peers_up_disk]); |
7286 | | |
7287 | | if (m_settings.get_int(settings_pack::num_optimistic_unchoke_slots)) |
7288 | | { |
7289 | | TORRENT_ASSERT(num_optimistic <= m_settings.get_int( |
7290 | | settings_pack::num_optimistic_unchoke_slots)); |
7291 | | } |
7292 | | |
7293 | | int const unchoked_counter_all = int(m_stats_counters[counters::num_peers_up_unchoked_all]); |
7294 | | int const unchoked_counter = int(m_stats_counters[counters::num_peers_up_unchoked]); |
7295 | | int const unchoked_counter_optimistic |
7296 | | = int(m_stats_counters[counters::num_peers_up_unchoked_optimistic]); |
7297 | | |
7298 | | TORRENT_ASSERT_VAL(unchoked_counter_all == unchokes_all, unchokes_all); |
7299 | | TORRENT_ASSERT_VAL(unchoked_counter == unchokes, unchokes); |
7300 | | TORRENT_ASSERT_VAL(unchoked_counter_optimistic == num_optimistic, num_optimistic); |
7301 | | |
7302 | | for (auto const& te : m_torrents) |
7303 | | { |
7304 | | TORRENT_ASSERT(te); |
7305 | | } |
7306 | | } |
7307 | | #endif // TORRENT_USE_INVARIANT_CHECKS |
7308 | | |
7309 | | #ifndef TORRENT_DISABLE_LOGGING |
7310 | | tracker_logger::tracker_logger(session_interface& ses): m_ses(ses) {} |
7311 | | void tracker_logger::tracker_warning(tracker_request const& |
7312 | | , std::string const& str) |
7313 | | { |
7314 | | debug_log("*** tracker warning: %s", str.c_str()); |
7315 | | } |
7316 | | |
7317 | | void tracker_logger::tracker_response(tracker_request const& |
7318 | | , libtorrent::address const& tracker_ip |
7319 | | , std::list<address> const& tracker_ips |
7320 | | , struct tracker_response const& resp) |
7321 | | { |
7322 | | TORRENT_UNUSED(tracker_ips); |
7323 | | debug_log("TRACKER RESPONSE\n" |
7324 | | "interval: %d\n" |
7325 | | "external ip: %s\n" |
7326 | | "we connected to: %s\n" |
7327 | | "peers:" |
7328 | | , resp.interval.count() |
7329 | | , print_address(resp.external_ip).c_str() |
7330 | | , print_address(tracker_ip).c_str()); |
7331 | | |
7332 | | for (auto const& p : resp.peers) |
7333 | | { |
7334 | | debug_log(" %16s %5d %s", p.hostname.c_str(), p.port |
7335 | | , p.pid.is_all_zeros() ? "" : to_hex(p.pid).c_str()); |
7336 | | } |
7337 | | for (auto const& p : resp.peers4) |
7338 | | { |
7339 | | debug_log(" %s:%d", print_address(address_v4(p.ip)).c_str(), p.port); |
7340 | | } |
7341 | | for (auto const& p : resp.peers6) |
7342 | | { |
7343 | | debug_log(" [%s]:%d", print_address(address_v6(p.ip)).c_str(), p.port); |
7344 | | } |
7345 | | } |
7346 | | |
7347 | | void tracker_logger::tracker_request_error(tracker_request const& |
7348 | | , error_code const& ec, operation_t const op, std::string const& str |
7349 | | , seconds32 const retry_interval) |
7350 | | { |
7351 | | TORRENT_UNUSED(retry_interval); |
7352 | | debug_log("*** tracker error: [%s] %s %s" |
7353 | | , operation_name(op), ec.message().c_str(), str.c_str()); |
7354 | | } |
7355 | | |
7356 | | bool tracker_logger::should_log() const |
7357 | | { |
7358 | | return m_ses.alerts().should_post<log_alert>(); |
7359 | | } |
7360 | | |
7361 | | void tracker_logger::debug_log(const char* fmt, ...) const noexcept try |
7362 | | { |
7363 | | if (!m_ses.alerts().should_post<log_alert>()) return; |
7364 | | |
7365 | | va_list v; |
7366 | | va_start(v, fmt); |
7367 | | m_ses.alerts().emplace_alert<log_alert>(fmt, v); |
7368 | | va_end(v); |
7369 | | } |
7370 | | catch (std::exception const&) {} |
7371 | | #endif // TORRENT_DISABLE_LOGGING |
7372 | | }} |