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