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;
70 // ut pex does not support IPv6
71 if (!p
.remote().address().is_v4()) return false;
75 struct ut_pex_plugin
: torrent_plugin
77 ut_pex_plugin(torrent
& t
): m_torrent(t
), m_1_minute(0) {}
79 virtual boost::shared_ptr
<peer_plugin
> new_connection(peer_connection
* pc
);
81 std::vector
<char>& get_ut_pex_msg()
86 // the second tick of the torrent
87 // each minute the new lists of "added" + "added.f" and "dropped"
88 // are calculated here and the pex message is created
89 // each peer connection will use this message
90 // max_peer_entries limits the packet size
93 if (++m_1_minute
< 60) return;
98 std::string
& pla
= pex
["added"].string();
99 std::string
& pld
= pex
["dropped"].string();
100 std::string
& plf
= pex
["added.f"].string();
101 std::back_insert_iterator
<std::string
> pla_out(pla
);
102 std::back_insert_iterator
<std::string
> pld_out(pld
);
103 std::back_insert_iterator
<std::string
> plf_out(plf
);
105 std::set
<tcp::endpoint
> dropped
;
106 m_old_peers
.swap(dropped
);
109 for (torrent::peer_iterator i
= m_torrent
.begin()
110 , end(m_torrent
.end()); i
!= end
; ++i
)
112 if (!send_peer(*i
->second
)) continue;
114 m_old_peers
.insert(i
->first
);
116 std::set
<tcp::endpoint
>::iterator di
= dropped
.find(i
->first
);
117 if (di
== dropped
.end())
119 // don't write too big of a package
120 if (num_added
>= max_peer_entries
) continue;
122 // i->first was added since the last time
123 detail::write_endpoint(i
->first
, pla_out
);
124 // no supported flags to set yet
125 // 0x01 - peer supports encryption
126 // 0x02 - peer is a seed
127 int flags
= i
->second
->is_seed() ? 2 : 0;
128 detail::write_uint8(flags
, plf_out
);
133 // this was in the previous message
134 // so, it wasn't dropped
139 for (std::set
<tcp::endpoint
>::const_iterator i
= dropped
.begin()
140 , end(dropped
.end());i
!= end
; ++i
)
142 if (!i
->address().is_v4()) continue;
143 detail::write_endpoint(*i
, pld_out
);
146 m_ut_pex_msg
.clear();
147 bencode(std::back_inserter(m_ut_pex_msg
), pex
);
153 std::set
<tcp::endpoint
> m_old_peers
;
155 std::vector
<char> m_ut_pex_msg
;
159 struct ut_pex_peer_plugin
: peer_plugin
161 ut_pex_peer_plugin(torrent
& t
, peer_connection
& pc
, ut_pex_plugin
& tp
)
170 virtual void add_handshake(entry
& h
)
172 entry
& messages
= h
["m"];
173 messages
[extension_name
] = extension_index
;
176 virtual bool on_extension_handshake(entry
const& h
)
178 entry
const& messages
= h
["m"];
180 if (entry
const* index
= messages
.find_key(extension_name
))
182 m_message_index
= index
->integer();
192 virtual bool on_extended(int length
, int msg
, buffer::const_interval body
)
195 if (msg
!= extension_index
) return false;
196 if (m_message_index
== 0) return false;
198 if (length
> 500 * 1024)
199 throw protocol_error("ut peer exchange message larger than 500 kB");
201 if (body
.left() < length
) return true;
203 entry pex_msg
= bdecode(body
.begin
, body
.end
);
204 std::string
const& peers
= pex_msg
["added"].string();
205 std::string
const& peer_flags
= pex_msg
["added.f"].string();
207 int num_peers
= peers
.length() / 6;
208 char const* in
= peers
.c_str();
209 char const* fin
= peer_flags
.c_str();
211 if (int(peer_flags
.size()) != num_peers
)
215 policy
& p
= m_torrent
.get_policy();
216 for (int i
= 0; i
< num_peers
; ++i
)
218 tcp::endpoint adr
= detail::read_v4_endpoint
<tcp::endpoint
>(in
);
219 char flags
= detail::read_uint8(fin
);
220 p
.peer_from_tracker(adr
, pid
, peer_info::pex
, flags
);
224 catch (std::exception
&)
229 // the peers second tick
230 // every minute we send a pex message
233 if (!m_message_index
) return; // no handshake yet
234 if (++m_1_minute
<= 60) return;
239 m_first_time
= false;
250 void send_ut_peer_diff()
252 std::vector
<char> const& pex_msg
= m_tp
.get_ut_pex_msg();
254 buffer::interval i
= m_pc
.allocate_send_buffer(6 + pex_msg
.size());
256 detail::write_uint32(1 + 1 + pex_msg
.size(), i
.begin
);
257 detail::write_uint8(bt_peer_connection::msg_extended
, i
.begin
);
258 detail::write_uint8(m_message_index
, i
.begin
);
259 std::copy(pex_msg
.begin(), pex_msg
.end(), i
.begin
);
260 i
.begin
+= pex_msg
.size();
262 assert(i
.begin
== i
.end
);
266 void send_ut_peer_list()
269 // leave the dropped string empty
270 pex
["dropped"].string();
271 std::string
& pla
= pex
["added"].string();
272 std::string
& plf
= pex
["added.f"].string();
273 std::back_insert_iterator
<std::string
> pla_out(pla
);
274 std::back_insert_iterator
<std::string
> plf_out(plf
);
277 for (torrent::peer_iterator i
= m_torrent
.begin()
278 , end(m_torrent
.end()); i
!= end
; ++i
)
280 if (!send_peer(*i
->second
)) continue;
282 // don't write too big of a package
283 if (num_added
>= max_peer_entries
) continue;
285 // i->first was added since the last time
286 detail::write_endpoint(i
->first
, pla_out
);
287 // no supported flags to set yet
288 // 0x01 - peer supports encryption
289 // 0x02 - peer is a seed
290 int flags
= i
->second
->is_seed() ? 2 : 0;
291 detail::write_uint8(flags
, plf_out
);
294 std::vector
<char> pex_msg
;
295 bencode(std::back_inserter(pex_msg
), pex
);
297 buffer::interval i
= m_pc
.allocate_send_buffer(6 + pex_msg
.size());
299 detail::write_uint32(1 + 1 + pex_msg
.size(), i
.begin
);
300 detail::write_uint8(bt_peer_connection::msg_extended
, i
.begin
);
301 detail::write_uint8(m_message_index
, i
.begin
);
302 std::copy(pex_msg
.begin(), pex_msg
.end(), i
.begin
);
303 i
.begin
+= pex_msg
.size();
305 assert(i
.begin
== i
.end
);
310 peer_connection
& m_pc
;
315 // this is initialized to true, and set to
316 // false after the first pex message has been sent.
317 // it is used to know if a diff message or a full
318 // message should be sent.
322 boost::shared_ptr
<peer_plugin
> ut_pex_plugin::new_connection(peer_connection
* pc
)
324 bt_peer_connection
* c
= dynamic_cast<bt_peer_connection
*>(pc
);
325 if (!c
) return boost::shared_ptr
<peer_plugin
>();
326 return boost::shared_ptr
<peer_plugin
>(new ut_pex_peer_plugin(m_torrent
334 boost::shared_ptr
<torrent_plugin
> create_ut_pex_plugin(torrent
* t
)
336 if (t
->torrent_file().priv())
338 return boost::shared_ptr
<torrent_plugin
>();
340 return boost::shared_ptr
<torrent_plugin
>(new ut_pex_plugin(*t
));