formatting fixes in client test, and made the test build when resolve countries is...
[libtorrent-kjk.git] / src / ut_pex.cpp
blob586cf7ccb7756ef0af03fc9dd1b689b925a2ec67
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 // ut pex does not support IPv6
71 if (!p.remote().address().is_v4()) return false;
72 return true;
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()
83 return m_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
91 virtual void tick()
93 if (++m_1_minute < 60) return;
95 m_1_minute = 0;
97 entry pex;
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);
108 int num_added = 0;
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);
129 ++num_added;
131 else
133 // this was in the previous message
134 // so, it wasn't dropped
135 dropped.erase(di);
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);
150 private:
151 torrent& m_torrent;
153 std::set<tcp::endpoint> m_old_peers;
154 int m_1_minute;
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)
162 : m_torrent(t)
163 , m_pc(pc)
164 , m_tp(tp)
165 , m_1_minute(0)
166 , m_message_index(0)
167 , m_first_time(true)
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();
183 return true;
185 else
187 m_message_index = 0;
188 return false;
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)
212 return true;
214 peer_id pid(0);
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);
222 return true;
224 catch (std::exception&)
226 return true;
229 // the peers second tick
230 // every minute we send a pex message
231 virtual void tick()
233 if (!m_message_index) return; // no handshake yet
234 if (++m_1_minute <= 60) return;
236 if (m_first_time)
238 send_ut_peer_list();
239 m_first_time = false;
241 else
243 send_ut_peer_diff();
245 m_1_minute = 0;
248 private:
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);
263 m_pc.setup_send();
266 void send_ut_peer_list()
268 entry pex;
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);
276 int num_added = 0;
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);
292 ++num_added;
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);
306 m_pc.setup_send();
309 torrent& m_torrent;
310 peer_connection& m_pc;
311 ut_pex_plugin& m_tp;
312 int m_1_minute;
313 int m_message_index;
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.
319 bool m_first_time;
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
327 , *pc, *this));
331 namespace libtorrent
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));