3 Copyright (c) 2006, MassaRoddel, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/pch.hpp"
36 #pragma warning(push, 1)
39 #include <boost/shared_ptr.hpp>
45 #include "libtorrent/peer_connection.hpp"
46 #include "libtorrent/bt_peer_connection.hpp"
47 #include "libtorrent/bencode.hpp"
48 #include "libtorrent/torrent.hpp"
49 #include "libtorrent/extensions.hpp"
51 #include "libtorrent/extensions/ut_pex.hpp"
53 namespace libtorrent
{ namespace
55 const char extension_name
[] = "ut_pex";
60 max_peer_entries
= 100
63 bool send_peer(peer_connection
const& p
)
65 // don't send out peers that we haven't connected to
66 // (that have connected to us)
67 if (!p
.is_local()) return false;
68 // don't send out peers that we haven't successfully connected to
69 if (p
.is_connecting()) return false;
73 struct ut_pex_plugin
: torrent_plugin
75 ut_pex_plugin(torrent
& t
): m_torrent(t
), m_1_minute(55) {}
77 virtual boost::shared_ptr
<peer_plugin
> new_connection(peer_connection
* pc
);
79 std::vector
<char>& get_ut_pex_msg()
84 // the second tick of the torrent
85 // each minute the new lists of "added" + "added.f" and "dropped"
86 // are calculated here and the pex message is created
87 // each peer connection will use this message
88 // max_peer_entries limits the packet size
91 if (++m_1_minute
< 60) return;
96 std::string
& pla
= pex
["added"].string();
97 std::string
& pld
= pex
["dropped"].string();
98 std::string
& plf
= pex
["added.f"].string();
99 std::string
& pla6
= pex
["added6"].string();
100 std::string
& pld6
= pex
["dropped6"].string();
101 std::string
& plf6
= pex
["added6.f"].string();
102 std::back_insert_iterator
<std::string
> pla_out(pla
);
103 std::back_insert_iterator
<std::string
> pld_out(pld
);
104 std::back_insert_iterator
<std::string
> plf_out(plf
);
105 std::back_insert_iterator
<std::string
> pla6_out(pla6
);
106 std::back_insert_iterator
<std::string
> pld6_out(pld6
);
107 std::back_insert_iterator
<std::string
> plf6_out(plf6
);
109 std::set
<tcp::endpoint
> dropped
;
110 m_old_peers
.swap(dropped
);
113 for (torrent::peer_iterator i
= m_torrent
.begin()
114 , end(m_torrent
.end()); i
!= end
; ++i
)
116 peer_connection
* peer
= *i
;
117 if (!send_peer(*peer
)) continue;
119 tcp::endpoint
const& remote
= peer
->remote();
120 m_old_peers
.insert(remote
);
122 std::set
<tcp::endpoint
>::iterator di
= dropped
.find(remote
);
123 if (di
== dropped
.end())
125 // don't write too big of a package
126 if (num_added
>= max_peer_entries
) break;
128 // only send proper bittorrent peers
129 bt_peer_connection
* p
= dynamic_cast<bt_peer_connection
*>(peer
);
132 // no supported flags to set yet
133 // 0x01 - peer supports encryption
134 // 0x02 - peer is a seed
135 int flags
= p
->is_seed() ? 2 : 0;
136 #ifndef TORRENT_DISABLE_ENCRYPTION
137 flags
|= p
->supports_encryption() ? 1 : 0;
139 // i->first was added since the last time
140 if (remote
.address().is_v4())
142 detail::write_endpoint(remote
, pla_out
);
143 detail::write_uint8(flags
, plf_out
);
147 detail::write_endpoint(remote
, pla6_out
);
148 detail::write_uint8(flags
, plf6_out
);
154 // this was in the previous message
155 // so, it wasn't dropped
160 for (std::set
<tcp::endpoint
>::const_iterator i
= dropped
.begin()
161 , end(dropped
.end()); i
!= end
; ++i
)
163 if (i
->address().is_v4())
164 detail::write_endpoint(*i
, pld_out
);
166 detail::write_endpoint(*i
, pld6_out
);
169 m_ut_pex_msg
.clear();
170 bencode(std::back_inserter(m_ut_pex_msg
), pex
);
176 std::set
<tcp::endpoint
> m_old_peers
;
178 std::vector
<char> m_ut_pex_msg
;
182 struct ut_pex_peer_plugin
: peer_plugin
184 ut_pex_peer_plugin(torrent
& t
, peer_connection
& pc
, ut_pex_plugin
& tp
)
193 virtual void add_handshake(entry
& h
)
195 entry
& messages
= h
["m"];
196 messages
[extension_name
] = extension_index
;
199 virtual bool on_extension_handshake(lazy_entry
const& h
)
202 if (h
.type() != lazy_entry::dict_t
) return false;
203 lazy_entry
const* messages
= h
.dict_find("m");
204 if (!messages
|| messages
->type() != lazy_entry::dict_t
) return false;
206 int index
= messages
->dict_find_int_value(extension_name
, -1);
207 if (index
== -1) return false;
208 m_message_index
= index
;
212 virtual bool on_extended(int length
, int msg
, buffer::const_interval body
)
214 if (msg
!= extension_index
) return false;
215 if (m_message_index
== 0) return false;
217 if (length
> 500 * 1024)
219 m_pc
.disconnect("peer exchange message larger than 500 kB", 2);
223 if (body
.left() < length
) return true;
226 int ret
= lazy_bdecode(body
.begin
, body
.end
, pex_msg
);
227 if (pex_msg
.type() != lazy_entry::dict_t
)
229 m_pc
.disconnect("invalid bencoding in ut_metadata message", 2);
233 lazy_entry
const* p
= pex_msg
.dict_find("added");
234 lazy_entry
const* pf
= pex_msg
.dict_find("added.f");
238 && p
->type() == lazy_entry::string_t
239 && pf
->type() == lazy_entry::string_t
240 && pf
->string_length() == p
->string_length() / 6)
242 int num_peers
= pf
->string_length();
243 char const* in
= p
->string_ptr();
244 char const* fin
= pf
->string_ptr();
247 policy
& p
= m_torrent
.get_policy();
248 for (int i
= 0; i
< num_peers
; ++i
)
250 tcp::endpoint adr
= detail::read_v4_endpoint
<tcp::endpoint
>(in
);
252 p
.peer_from_tracker(adr
, pid
, peer_info::pex
, flags
);
256 lazy_entry
const* p6
= pex_msg
.dict_find("added6");
257 lazy_entry
const* p6f
= pex_msg
.dict_find("added6.f");
260 && p6
->type() == lazy_entry::string_t
261 && p6f
->type() == lazy_entry::string_t
262 && p6f
->string_length() == p6
->string_length() / 18)
264 int num_peers
= p6f
->string_length();
265 char const* in
= p6
->string_ptr();
266 char const* fin
= p6f
->string_ptr();
269 policy
& p
= m_torrent
.get_policy();
270 for (int i
= 0; i
< num_peers
; ++i
)
272 tcp::endpoint adr
= detail::read_v6_endpoint
<tcp::endpoint
>(in
);
274 p
.peer_from_tracker(adr
, pid
, peer_info::pex
, flags
);
280 // the peers second tick
281 // every minute we send a pex message
284 if (!m_message_index
) return; // no handshake yet
285 if (++m_1_minute
<= 60) return;
290 m_first_time
= false;
301 void send_ut_peer_diff()
303 std::vector
<char> const& pex_msg
= m_tp
.get_ut_pex_msg();
305 buffer::interval i
= m_pc
.allocate_send_buffer(6 + pex_msg
.size());
307 detail::write_uint32(1 + 1 + pex_msg
.size(), i
.begin
);
308 detail::write_uint8(bt_peer_connection::msg_extended
, i
.begin
);
309 detail::write_uint8(m_message_index
, i
.begin
);
310 std::copy(pex_msg
.begin(), pex_msg
.end(), i
.begin
);
311 i
.begin
+= pex_msg
.size();
313 TORRENT_ASSERT(i
.begin
== i
.end
);
317 void send_ut_peer_list()
320 // leave the dropped string empty
321 pex
["dropped"].string();
322 std::string
& pla
= pex
["added"].string();
323 std::string
& plf
= pex
["added.f"].string();
324 pex
["dropped6"].string();
325 std::string
& pla6
= pex
["added6"].string();
326 std::string
& plf6
= pex
["added6.f"].string();
327 std::back_insert_iterator
<std::string
> pla_out(pla
);
328 std::back_insert_iterator
<std::string
> plf_out(plf
);
329 std::back_insert_iterator
<std::string
> pla6_out(pla6
);
330 std::back_insert_iterator
<std::string
> plf6_out(plf6
);
333 for (torrent::peer_iterator i
= m_torrent
.begin()
334 , end(m_torrent
.end()); i
!= end
; ++i
)
336 peer_connection
* peer
= *i
;
337 if (!send_peer(*peer
)) continue;
339 // don't write too big of a package
340 if (num_added
>= max_peer_entries
) break;
342 // only send proper bittorrent peers
343 bt_peer_connection
* p
= dynamic_cast<bt_peer_connection
*>(peer
);
346 // no supported flags to set yet
347 // 0x01 - peer supports encryption
348 // 0x02 - peer is a seed
349 int flags
= p
->is_seed() ? 2 : 0;
350 #ifndef TORRENT_DISABLE_ENCRYPTION
351 flags
|= p
->supports_encryption() ? 1 : 0;
353 tcp::endpoint
const& remote
= peer
->remote();
354 // i->first was added since the last time
355 if (remote
.address().is_v4())
357 detail::write_endpoint(remote
, pla_out
);
358 detail::write_uint8(flags
, plf_out
);
362 detail::write_endpoint(remote
, pla6_out
);
363 detail::write_uint8(flags
, plf6_out
);
367 std::vector
<char> pex_msg
;
368 bencode(std::back_inserter(pex_msg
), pex
);
370 buffer::interval i
= m_pc
.allocate_send_buffer(6 + pex_msg
.size());
372 detail::write_uint32(1 + 1 + pex_msg
.size(), i
.begin
);
373 detail::write_uint8(bt_peer_connection::msg_extended
, i
.begin
);
374 detail::write_uint8(m_message_index
, i
.begin
);
375 std::copy(pex_msg
.begin(), pex_msg
.end(), i
.begin
);
376 i
.begin
+= pex_msg
.size();
378 TORRENT_ASSERT(i
.begin
== i
.end
);
383 peer_connection
& m_pc
;
388 // this is initialized to true, and set to
389 // false after the first pex message has been sent.
390 // it is used to know if a diff message or a full
391 // message should be sent.
395 boost::shared_ptr
<peer_plugin
> ut_pex_plugin::new_connection(peer_connection
* pc
)
397 bt_peer_connection
* c
= dynamic_cast<bt_peer_connection
*>(pc
);
398 if (!c
) return boost::shared_ptr
<peer_plugin
>();
399 return boost::shared_ptr
<peer_plugin
>(new ut_pex_peer_plugin(m_torrent
407 boost::shared_ptr
<torrent_plugin
> create_ut_pex_plugin(torrent
* t
, void*)
409 if (t
->torrent_file().priv())
411 return boost::shared_ptr
<torrent_plugin
>();
413 return boost::shared_ptr
<torrent_plugin
>(new ut_pex_plugin(*t
));