fix piece_picker piece-shuffle bug
[libtorrent.git] / src / ut_metadata.cpp
blob848c3795d890c4c1f5aa34395cfbc785aecbcc00
1 /*
3 Copyright (c) 2007, 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>
40 #include <boost/lexical_cast.hpp>
42 #ifdef _MSC_VER
43 #pragma warning(pop)
44 #endif
46 #include <vector>
47 #include <utility>
48 #include <numeric>
49 #include <cstdio>
51 #include "libtorrent/peer_connection.hpp"
52 #include "libtorrent/bt_peer_connection.hpp"
53 #include "libtorrent/hasher.hpp"
54 #include "libtorrent/bencode.hpp"
55 #include "libtorrent/torrent.hpp"
56 #include "libtorrent/extensions.hpp"
57 #include "libtorrent/extensions/ut_metadata.hpp"
58 #include "libtorrent/alert_types.hpp"
59 #ifdef TORRENT_STATS
60 #include "libtorrent/aux_/session_impl.hpp"
61 #endif
63 namespace libtorrent { namespace
65 int div_round_up(int numerator, int denominator)
67 return (numerator + denominator - 1) / denominator;
70 void nop(char*) {}
72 struct ut_metadata_plugin : torrent_plugin
74 ut_metadata_plugin(torrent& t)
75 : m_torrent(t)
76 , m_metadata_progress(0)
77 , m_metadata_size(0)
81 virtual void on_files_checked()
83 // if the torrent is a seed, copy the metadata from
84 // the torrent before it is deallocated
85 if (m_torrent.is_seed())
86 metadata();
89 virtual boost::shared_ptr<peer_plugin> new_connection(
90 peer_connection* pc);
92 buffer::const_interval metadata() const
94 TORRENT_ASSERT(m_torrent.valid_metadata());
95 if (!m_metadata)
97 m_metadata = m_torrent.torrent_file().metadata();
98 m_metadata_size = m_torrent.torrent_file().metadata_size();
99 TORRENT_ASSERT(hasher(m_metadata.get(), m_metadata_size).final()
100 == m_torrent.torrent_file().info_hash());
102 return buffer::const_interval(m_metadata.get(), m_metadata.get()
103 + m_metadata_size);
106 bool received_metadata(char const* buf, int size, int piece, int total_size)
108 if (m_torrent.valid_metadata()) return false;
110 if (!m_metadata)
112 // verify the total_size
113 if (total_size <= 0 || total_size > 500 * 1024) return false;
115 m_metadata.reset(new char[total_size]);
116 m_requested_metadata.resize(div_round_up(total_size, 16 * 1024), 0);
117 m_metadata_size = total_size;
120 if (piece < 0 || piece >= int(m_requested_metadata.size()))
121 return false;
123 TORRENT_ASSERT(piece * 16 * 1024 + size <= m_metadata_size);
124 std::memcpy(&m_metadata[piece * 16 * 1024], buf, size);
125 // mark this piece has 'have'
126 m_requested_metadata[piece] = (std::numeric_limits<int>::max)();
128 bool have_all = std::count(m_requested_metadata.begin()
129 , m_requested_metadata.end(), (std::numeric_limits<int>::max)())
130 == int(m_requested_metadata.size());
132 if (!have_all) return false;
134 hasher h;
135 h.update(&m_metadata[0], m_metadata_size);
136 sha1_hash info_hash = h.final();
138 if (info_hash != m_torrent.torrent_file().info_hash())
140 std::fill(m_requested_metadata.begin(), m_requested_metadata.end(), 0);
142 if (m_torrent.alerts().should_post<metadata_failed_alert>())
144 m_torrent.alerts().post_alert(metadata_failed_alert(
145 m_torrent.get_handle()));
148 return false;
151 lazy_entry metadata;
152 int ret = lazy_bdecode(m_metadata.get(), m_metadata.get() + m_metadata_size, metadata);
153 std::string error;
154 if (!m_torrent.set_metadata(metadata, error))
156 // this means the metadata is correct, since we
157 // verified it against the info-hash, but we
158 // failed to parse it. Pause the torrent
159 // TODO: Post an alert!
160 m_torrent.pause();
161 return false;
164 // clear the storage for the bitfield
165 std::vector<int>().swap(m_requested_metadata);
167 return true;
170 // returns a piece of the metadata that
171 // we should request.
172 int metadata_request();
174 // this is called from the peer_connection for
175 // each piece of metadata it receives
176 void metadata_progress(int total_size, int received)
178 m_metadata_progress += received;
179 m_metadata_size = total_size;
182 void on_piece_pass(int)
184 // if we became a seed, copy the metadata from
185 // the torrent before it is deallocated
186 if (m_torrent.is_seed())
187 metadata();
190 void metadata_size(int size)
192 if (m_metadata_size > 0 || size <= 0 || size > 500 * 1024) return;
193 m_metadata_size = size;
194 m_metadata.reset(new char[size]);
195 m_requested_metadata.resize(div_round_up(size, 16 * 1024), 0);
198 private:
199 torrent& m_torrent;
201 // this buffer is filled with the info-section of
202 // the metadata file while downloading it from
203 // peers, and while sending it.
204 // it is mutable because it's generated lazily
205 mutable boost::shared_array<char> m_metadata;
207 int m_metadata_progress;
208 mutable int m_metadata_size;
210 // this vector keeps track of how many times each meatdata
211 // block has been requested
212 // std::numeric_limits<int>::max() means we have the piece
213 std::vector<int> m_requested_metadata;
217 struct ut_metadata_peer_plugin : peer_plugin
219 ut_metadata_peer_plugin(torrent& t, bt_peer_connection& pc
220 , ut_metadata_plugin& tp)
221 : m_message_index(0)
222 , m_no_metadata(min_time())
223 , m_torrent(t)
224 , m_pc(pc)
225 , m_tp(tp)
228 // can add entries to the extension handshake
229 virtual void add_handshake(entry& h)
231 entry& messages = h["m"];
232 messages["ut_metadata"] = 15;
233 if (m_torrent.valid_metadata())
234 h["metadata_size"] = m_tp.metadata().left();
237 // called when the extension handshake from the other end is received
238 virtual bool on_extension_handshake(lazy_entry const& h)
240 m_message_index = 0;
241 if (h.type() != lazy_entry::dict_t) return false;
242 lazy_entry const* messages = h.dict_find("m");
243 if (!messages || messages->type() != lazy_entry::dict_t) return false;
245 int index = messages->dict_find_int_value("ut_metadata", -1);
246 if (index == -1) return false;
247 m_message_index = index;
249 int metadata_size = h.dict_find_int_value("metadata_size");
250 if (metadata_size > 0)
251 m_tp.metadata_size(metadata_size);
252 return true;
255 void write_metadata_packet(int type, int piece)
257 TORRENT_ASSERT(type >= 0 && type <= 2);
258 TORRENT_ASSERT(piece >= 0);
259 TORRENT_ASSERT(!m_pc.associated_torrent().expired());
261 #ifdef TORRENT_VERBOSE_LOGGING
262 (*m_pc.m_logger) << time_now_string() << " ==> UT_METADATA [ "
263 "type: " << type << " | piece: " << piece << " ]\n";
264 #endif
265 // abort if the peer doesn't support the metadata extension
266 if (m_message_index == 0) return;
268 entry e;
269 e["msg_type"] = type;
270 e["piece"] = piece;
272 char const* metadata = 0;
273 int metadata_piece_size = 0;
275 if (type == 1)
277 TORRENT_ASSERT(m_pc.associated_torrent().lock()->valid_metadata());
278 e["total_size"] = m_tp.metadata().left();
279 int offset = piece * 16 * 1024;
280 metadata = m_tp.metadata().begin + offset;
281 metadata_piece_size = (std::min)(
282 int(m_tp.metadata().left() - offset), 16 * 1024);
283 TORRENT_ASSERT(metadata_piece_size > 0);
284 TORRENT_ASSERT(offset >= 0);
285 TORRENT_ASSERT(offset + metadata_piece_size <= int(m_tp.metadata().left()));
288 char msg[200];
289 char* header = msg;
290 char* p = &msg[6];
291 int len = bencode(p, e);
292 int total_size = 2 + len + metadata_piece_size;
293 namespace io = detail;
294 io::write_uint32(total_size, header);
295 io::write_uint8(bt_peer_connection::msg_extended, header);
296 io::write_uint8(m_message_index, header);
298 m_pc.send_buffer(msg, len + 6);
299 if (metadata_piece_size) m_pc.append_send_buffer(
300 (char*)metadata, metadata_piece_size, &nop);
303 virtual bool on_extended(int length
304 , int extended_msg, buffer::const_interval body)
306 if (extended_msg != 15) return false;
307 if (m_message_index == 0) return false;
309 if (length > 17 * 1024)
311 m_pc.disconnect("ut_metadata message larger than 17 kB", 2);
312 return true;
315 if (!m_pc.packet_finished()) return true;
317 int len;
318 entry msg = bdecode(body.begin, body.end, len);
319 if (msg.type() == entry::undefined_t)
321 m_pc.disconnect("invalid bencoding in ut_metadata message", 2);
322 return true;
325 int type = msg["msg_type"].integer();
326 int piece = msg["piece"].integer();
328 #ifdef TORRENT_VERBOSE_LOGGING
329 (*m_pc.m_logger) << time_now_string() << " <== UT_METADATA [ "
330 "type: " << type << " | piece: " << piece << " ]\n";
331 #endif
333 switch (type)
335 case 0: // request
337 if (!m_torrent.valid_metadata())
339 write_metadata_packet(2, piece);
340 return true;
342 // TODO: put the request on the queue in some cases
343 write_metadata_packet(1, piece);
345 break;
346 case 1: // data
348 std::vector<int>::iterator i = std::find(m_sent_requests.begin()
349 , m_sent_requests.end(), piece);
351 // unwanted piece?
352 if (i == m_sent_requests.end()) return true;
354 m_sent_requests.erase(i);
355 entry const* total_size = msg.find_key("total_size");
356 m_tp.received_metadata(body.begin + len, body.left() - len, piece
357 , (total_size && total_size->type() == entry::int_t) ? total_size->integer() : 0);
359 break;
360 case 2: // have no data
362 m_no_metadata = time_now();
363 std::vector<int>::iterator i = std::find(m_sent_requests.begin()
364 , m_sent_requests.end(), piece);
365 // unwanted piece?
366 if (i == m_sent_requests.end()) return true;
367 m_sent_requests.erase(i);
369 break;
370 default:
372 std::stringstream msg;
373 msg << "unknown ut_metadata extension message: " << type;
374 m_pc.disconnect(msg.str().c_str(), 2);
377 return true;
380 virtual void tick()
382 // if we don't have any metadata, and this peer
383 // supports the request metadata extension
384 // and we aren't currently waiting for a request
385 // reply. Then, send a request for some metadata.
386 if (!m_torrent.valid_metadata()
387 && m_message_index != 0
388 && m_sent_requests.size() < 2
389 && has_metadata())
391 int piece = m_tp.metadata_request();
392 m_sent_requests.push_back(piece);
393 write_metadata_packet(0, piece);
397 bool has_metadata() const
399 return time_now() - m_no_metadata > minutes(1);
402 private:
404 // this is the message index the remote peer uses
405 // for metadata extension messages.
406 int m_message_index;
408 // this is set to the current time each time we get a
409 // "I don't have metadata" message.
410 ptime m_no_metadata;
412 // request queues
413 std::vector<int> m_sent_requests;
414 std::vector<int> m_incoming_requests;
416 torrent& m_torrent;
417 bt_peer_connection& m_pc;
418 ut_metadata_plugin& m_tp;
421 boost::shared_ptr<peer_plugin> ut_metadata_plugin::new_connection(
422 peer_connection* pc)
424 bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
425 if (!c) return boost::shared_ptr<peer_plugin>();
426 return boost::shared_ptr<peer_plugin>(new ut_metadata_peer_plugin(m_torrent, *c, *this));
429 int ut_metadata_plugin::metadata_request()
431 std::vector<int>::iterator i = std::min_element(
432 m_requested_metadata.begin(), m_requested_metadata.end());
434 if (m_requested_metadata.empty())
436 // if we don't know how many pieces there are
437 // just ask for piece 0
438 m_requested_metadata.resize(1, 1);
439 return 0;
442 int piece = i - m_requested_metadata.begin();
443 m_requested_metadata[piece] = piece;
444 return piece;
449 namespace libtorrent
452 boost::shared_ptr<torrent_plugin> create_ut_metadata_plugin(torrent* t, void*)
454 return boost::shared_ptr<torrent_plugin>(new ut_metadata_plugin(*t));