added missing file error_code.cpp
[libtorrent.git] / src / ut_pex.cpp
blob723f119d2ff044410a1b7afbe1bf3de33851791c
1 /*
3 Copyright (c) 2006, MassaRoddel, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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"
35 #ifdef _MSC_VER
36 #pragma warning(push, 1)
37 #endif
39 #include <boost/shared_ptr.hpp>
41 #ifdef _MSC_VER
42 #pragma warning(pop)
43 #endif
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";
57 enum
59 extension_index = 1,
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 return true;
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()
81 return m_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
89 virtual void tick()
91 if (++m_1_minute < 60) return;
93 m_1_minute = 0;
95 entry pex;
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);
112 int num_added = 0;
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);
130 if (!p) continue;
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;
138 #endif
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);
145 else
147 detail::write_endpoint(remote, pla6_out);
148 detail::write_uint8(flags, plf6_out);
150 ++num_added;
152 else
154 // this was in the previous message
155 // so, it wasn't dropped
156 dropped.erase(di);
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);
165 else
166 detail::write_endpoint(*i, pld6_out);
169 m_ut_pex_msg.clear();
170 bencode(std::back_inserter(m_ut_pex_msg), pex);
173 private:
174 torrent& m_torrent;
176 std::set<tcp::endpoint> m_old_peers;
177 int m_1_minute;
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)
185 : m_torrent(t)
186 , m_pc(pc)
187 , m_tp(tp)
188 , m_1_minute(55)
189 , m_message_index(0)
190 , m_first_time(true)
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)
201 m_message_index = 0;
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;
209 return true;
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);
220 return true;
223 if (body.left() < length) return true;
225 lazy_entry pex_msg;
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);
230 return true;
233 lazy_entry const* p = pex_msg.dict_find("added");
234 lazy_entry const* pf = pex_msg.dict_find("added.f");
236 if (p != 0
237 && pf != 0
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();
246 peer_id pid(0);
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);
251 char flags = *fin++;
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");
258 if (p6 != 0
259 && p6f != 0
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();
268 peer_id pid(0);
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);
273 char flags = *fin++;
274 p.peer_from_tracker(adr, pid, peer_info::pex, flags);
277 return true;
280 // the peers second tick
281 // every minute we send a pex message
282 virtual void tick()
284 if (!m_message_index) return; // no handshake yet
285 if (++m_1_minute <= 60) return;
287 if (m_first_time)
289 send_ut_peer_list();
290 m_first_time = false;
292 else
294 send_ut_peer_diff();
296 m_1_minute = 0;
299 private:
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);
314 m_pc.setup_send();
317 void send_ut_peer_list()
319 entry pex;
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);
332 int num_added = 0;
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);
344 if (!p) continue;
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;
352 #endif
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);
360 else
362 detail::write_endpoint(remote, pla6_out);
363 detail::write_uint8(flags, plf6_out);
365 ++num_added;
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);
379 m_pc.setup_send();
382 torrent& m_torrent;
383 peer_connection& m_pc;
384 ut_pex_plugin& m_tp;
385 int m_1_minute;
386 int m_message_index;
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.
392 bool m_first_time;
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
400 , *pc, *this));
404 namespace libtorrent
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));