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));