3
Copyright (c) 2007, 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>
50
#include "libtorrent/peer_connection.hpp"
51
#include "libtorrent/bt_peer_connection.hpp"
52
#include "libtorrent/hasher.hpp"
53
#include "libtorrent/bencode.hpp"
54
#include "libtorrent/torrent.hpp"
55
#include "libtorrent/extensions.hpp"
56
#include "libtorrent/extensions/ut_metadata.hpp"
57
#include "libtorrent/alert_types.hpp"
59
#include "libtorrent/aux_/session_impl.hpp"
62
namespace libtorrent { namespace
64
int div_round_up(int numerator, int denominator)
66
return (numerator + denominator - 1) / denominator;
71
struct ut_metadata_plugin : torrent_plugin
73
ut_metadata_plugin(torrent& t)
75
, m_metadata_progress(0)
80
virtual void on_files_checked()
82
// if the torrent is a seed, copy the metadata from
83
// the torrent before it is deallocated
84
if (m_torrent.is_seed())
88
virtual boost::shared_ptr<peer_plugin> new_connection(
91
buffer::const_interval metadata() const
93
TORRENT_ASSERT(m_torrent.valid_metadata());
96
m_metadata = m_torrent.torrent_file().metadata();
97
m_metadata_size = m_torrent.torrent_file().metadata_size();
98
TORRENT_ASSERT(hasher(m_metadata.get(), m_metadata_size).final()
99
== m_torrent.torrent_file().info_hash());
101
return buffer::const_interval(m_metadata.get(), m_metadata.get()
105
bool received_metadata(char const* buf, int size, int piece, int total_size)
107
if (m_torrent.valid_metadata()) return false;
111
// verify the total_size
112
if (total_size <= 0 || total_size > 500 * 1024) return false;
114
m_metadata.reset(new char[total_size]);
115
m_requested_metadata.resize(div_round_up(total_size, 16 * 1024), 0);
116
m_metadata_size = total_size;
119
if (piece < 0 || piece >= int(m_requested_metadata.size()))
122
TORRENT_ASSERT(piece * 16 * 1024 + size <= m_metadata_size);
123
std::memcpy(&m_metadata[piece * 16 * 1024], buf, size);
124
// mark this piece has 'have'
125
m_requested_metadata[piece] = (std::numeric_limits<int>::max)();
127
bool have_all = std::count(m_requested_metadata.begin()
128
, m_requested_metadata.end(), (std::numeric_limits<int>::max)())
129
== int(m_requested_metadata.size());
131
if (!have_all) return false;
134
h.update(&m_metadata[0], m_metadata_size);
135
sha1_hash info_hash = h.final();
137
if (info_hash != m_torrent.torrent_file().info_hash())
139
std::fill(m_requested_metadata.begin(), m_requested_metadata.end(), 0);
141
if (m_torrent.alerts().should_post<metadata_failed_alert>())
143
m_torrent.alerts().post_alert(metadata_failed_alert(
144
m_torrent.get_handle()));
151
int ret = lazy_bdecode(m_metadata.get(), m_metadata.get() + m_metadata_size, metadata);
153
if (!m_torrent.set_metadata(metadata, error))
155
// this means the metadata is correct, since we
156
// verified it against the info-hash, but we
157
// failed to parse it. Pause the torrent
158
// TODO: Post an alert!
163
// clear the storage for the bitfield
164
std::vector<int>().swap(m_requested_metadata);
169
// returns a piece of the metadata that
170
// we should request.
171
int metadata_request();
173
// this is called from the peer_connection for
174
// each piece of metadata it receives
175
void metadata_progress(int total_size, int received)
177
m_metadata_progress += received;
178
m_metadata_size = total_size;
181
void on_piece_pass(int)
183
// if we became a seed, copy the metadata from
184
// the torrent before it is deallocated
185
if (m_torrent.is_seed())
189
void metadata_size(int size)
191
if (m_metadata_size > 0 || size <= 0 || size > 500 * 1024) return;
192
m_metadata_size = size;
193
m_metadata.reset(new char[size]);
194
m_requested_metadata.resize(div_round_up(size, 16 * 1024), 0);
200
// this buffer is filled with the info-section of
201
// the metadata file while downloading it from
202
// peers, and while sending it.
203
// it is mutable because it's generated lazily
204
mutable boost::shared_array<char> m_metadata;
206
int m_metadata_progress;
207
mutable int m_metadata_size;
209
// this vector keeps track of how many times each meatdata
210
// block has been requested
211
// std::numeric_limits<int>::max() means we have the piece
212
std::vector<int> m_requested_metadata;
216
struct ut_metadata_peer_plugin : peer_plugin
218
ut_metadata_peer_plugin(torrent& t, bt_peer_connection& pc
219
, ut_metadata_plugin& tp)
221
, m_no_metadata(min_time())
227
// can add entries to the extension handshake
228
virtual void add_handshake(entry& h)
230
entry& messages = h["m"];
231
messages["ut_metadata"] = 15;
232
if (m_torrent.valid_metadata())
233
h["metadata_size"] = m_tp.metadata().left();
236
// called when the extension handshake from the other end is received
237
virtual bool on_extension_handshake(lazy_entry const& h)
240
if (h.type() != lazy_entry::dict_t) return false;
241
lazy_entry const* messages = h.dict_find("m");
242
if (!messages || messages->type() != lazy_entry::dict_t) return false;
244
int index = messages->dict_find_int_value("ut_metadata", -1);
245
if (index == -1) return false;
246
m_message_index = index;
248
int metadata_size = h.dict_find_int_value("metadata_size");
249
if (metadata_size > 0)
250
m_tp.metadata_size(metadata_size);
254
void write_metadata_packet(int type, int piece)
256
TORRENT_ASSERT(type >= 0 && type <= 2);
257
TORRENT_ASSERT(piece >= 0);
258
TORRENT_ASSERT(!m_pc.associated_torrent().expired());
260
#ifdef TORRENT_VERBOSE_LOGGING
261
(*m_pc.m_logger) << time_now_string() << " ==> UT_METADATA [ "
262
"type: " << type << " | piece: " << piece << " ]\n";
264
// abort if the peer doesn't support the metadata extension
265
if (m_message_index == 0) return;
268
e["msg_type"] = type;
271
char const* metadata = 0;
272
int metadata_piece_size = 0;
276
TORRENT_ASSERT(m_pc.associated_torrent().lock()->valid_metadata());
277
e["total_size"] = m_tp.metadata().left();
278
int offset = piece * 16 * 1024;
279
metadata = m_tp.metadata().begin + offset;
280
metadata_piece_size = (std::min)(
281
int(m_tp.metadata().left() - offset), 16 * 1024);
282
TORRENT_ASSERT(metadata_piece_size > 0);
283
TORRENT_ASSERT(offset >= 0);
284
TORRENT_ASSERT(offset + metadata_piece_size <= int(m_tp.metadata().left()));
290
int len = bencode(p, e);
291
int total_size = 2 + len + metadata_piece_size;
292
namespace io = detail;
293
io::write_uint32(total_size, header);
294
io::write_uint8(bt_peer_connection::msg_extended, header);
295
io::write_uint8(m_message_index, header);
297
m_pc.send_buffer(msg, len + 6);
298
if (metadata_piece_size) m_pc.append_send_buffer(
299
(char*)metadata, metadata_piece_size, &nop);
302
virtual bool on_extended(int length
303
, int extended_msg, buffer::const_interval body)
305
if (extended_msg != 15) return false;
306
if (m_message_index == 0) return false;
308
if (length > 17 * 1024)
310
m_pc.disconnect("ut_metadata message larger than 17 kB", 2);
314
if (!m_pc.packet_finished()) return true;
317
entry msg = bdecode(body.begin, body.end, len);
318
if (msg.type() == entry::undefined_t)
320
m_pc.disconnect("invalid bencoding in ut_metadata message", 2);
324
int type = msg["msg_type"].integer();
325
int piece = msg["piece"].integer();
327
#ifdef TORRENT_VERBOSE_LOGGING
328
(*m_pc.m_logger) << time_now_string() << " <== UT_METADATA [ "
329
"type: " << type << " | piece: " << piece << " ]\n";
336
if (!m_torrent.valid_metadata())
338
write_metadata_packet(2, piece);
341
// TODO: put the request on the queue in some cases
342
write_metadata_packet(1, piece);
347
std::vector<int>::iterator i = std::find(m_sent_requests.begin()
348
, m_sent_requests.end(), piece);
351
if (i == m_sent_requests.end()) return true;
353
m_sent_requests.erase(i);
354
entry const* total_size = msg.find_key("total_size");
355
m_tp.received_metadata(body.begin + len, body.left() - len, piece
356
, (total_size && total_size->type() == entry::int_t) ? total_size->integer() : 0);
359
case 2: // have no data
361
m_no_metadata = time_now();
362
std::vector<int>::iterator i = std::find(m_sent_requests.begin()
363
, m_sent_requests.end(), piece);
365
if (i == m_sent_requests.end()) return true;
366
m_sent_requests.erase(i);
371
std::stringstream msg;
372
msg << "unknown ut_metadata extension message: " << type;
373
m_pc.disconnect(msg.str().c_str(), 2);
381
// if we don't have any metadata, and this peer
382
// supports the request metadata extension
383
// and we aren't currently waiting for a request
384
// reply. Then, send a request for some metadata.
385
if (!m_torrent.valid_metadata()
386
&& m_message_index != 0
387
&& m_sent_requests.size() < 2
390
int piece = m_tp.metadata_request();
391
m_sent_requests.push_back(piece);
392
write_metadata_packet(0, piece);
396
bool has_metadata() const
398
return time_now() - m_no_metadata > minutes(1);
403
// this is the message index the remote peer uses
404
// for metadata extension messages.
407
// this is set to the current time each time we get a
408
// "I don't have metadata" message.
412
std::vector<int> m_sent_requests;
413
std::vector<int> m_incoming_requests;
416
bt_peer_connection& m_pc;
417
ut_metadata_plugin& m_tp;
420
boost::shared_ptr<peer_plugin> ut_metadata_plugin::new_connection(
423
bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
424
if (!c) return boost::shared_ptr<peer_plugin>();
425
return boost::shared_ptr<peer_plugin>(new ut_metadata_peer_plugin(m_torrent, *c, *this));
428
int ut_metadata_plugin::metadata_request()
430
std::vector<int>::iterator i = std::min_element(
431
m_requested_metadata.begin(), m_requested_metadata.end());
433
if (m_requested_metadata.empty())
435
// if we don't know how many pieces there are
436
// just ask for piece 0
437
m_requested_metadata.resize(1, 1);
441
int piece = i - m_requested_metadata.begin();
442
m_requested_metadata[piece] = piece;
451
boost::shared_ptr<torrent_plugin> create_ut_metadata_plugin(torrent* t, void*)
453
// don't add this extension if the torrent is private
454
if (t->valid_metadata() && t->torrent_file().priv()) return boost::shared_ptr<torrent_plugin>();
455
return boost::shared_ptr<torrent_plugin>(new ut_metadata_plugin(*t));