2006-11-14 01:08:16 +01:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2006, Arvid Norberg
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2007-03-17 18:15:16 +01:00
|
|
|
#include "libtorrent/pch.hpp"
|
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push, 1)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <boost/shared_ptr.hpp>
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <utility>
|
|
|
|
#include <numeric>
|
|
|
|
|
|
|
|
#include "libtorrent/peer_connection.hpp"
|
|
|
|
#include "libtorrent/bt_peer_connection.hpp"
|
|
|
|
#include "libtorrent/hasher.hpp"
|
|
|
|
#include "libtorrent/bencode.hpp"
|
|
|
|
#include "libtorrent/torrent.hpp"
|
|
|
|
#include "libtorrent/extensions.hpp"
|
|
|
|
#include "libtorrent/extensions/metadata_transfer.hpp"
|
2007-06-01 00:56:29 +02:00
|
|
|
#include "libtorrent/alert_types.hpp"
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
namespace libtorrent { namespace
|
|
|
|
{
|
|
|
|
int div_round_up(int numerator, int denominator)
|
|
|
|
{
|
|
|
|
return (numerator + denominator - 1) / denominator;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<int, int> req_to_offset(std::pair<int, int> req, int total_size)
|
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(req.first >= 0);
|
|
|
|
TORRENT_ASSERT(req.second > 0);
|
|
|
|
TORRENT_ASSERT(req.second <= 256);
|
|
|
|
TORRENT_ASSERT(req.first + req.second <= 256);
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
int start = div_round_up(req.first * total_size, 256);
|
|
|
|
int size = div_round_up((req.first + req.second) * total_size, 256) - start;
|
|
|
|
return std::make_pair(start, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<int, int> offset_to_req(std::pair<int, int> offset, int total_size)
|
|
|
|
{
|
|
|
|
int start = offset.first * 256 / total_size;
|
|
|
|
int size = (offset.first + offset.second) * 256 / total_size - start;
|
|
|
|
|
|
|
|
std::pair<int, int> ret(start, size);
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(start >= 0);
|
|
|
|
TORRENT_ASSERT(size > 0);
|
|
|
|
TORRENT_ASSERT(start <= 256);
|
|
|
|
TORRENT_ASSERT(start + size <= 256);
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
// assert the identity of this function
|
|
|
|
#ifndef NDEBUG
|
|
|
|
std::pair<int, int> identity = req_to_offset(ret, total_size);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(offset == identity);
|
2006-11-14 01:08:16 +01:00
|
|
|
#endif
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct metadata_plugin : torrent_plugin
|
|
|
|
{
|
|
|
|
metadata_plugin(torrent& t)
|
|
|
|
: m_torrent(t)
|
|
|
|
, m_metadata_progress(0)
|
|
|
|
, m_metadata_size(0)
|
|
|
|
{
|
|
|
|
m_requested_metadata.resize(256, 0);
|
|
|
|
}
|
2007-05-10 00:54:26 +02:00
|
|
|
|
|
|
|
virtual void on_files_checked()
|
|
|
|
{
|
|
|
|
// if the torrent is a seed, copy the metadata from
|
|
|
|
// the torrent before it is deallocated
|
|
|
|
if (m_torrent.is_seed())
|
|
|
|
metadata();
|
|
|
|
}
|
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
virtual boost::shared_ptr<peer_plugin> new_connection(
|
|
|
|
peer_connection* pc);
|
|
|
|
|
|
|
|
std::vector<char> const& metadata() const
|
|
|
|
{
|
|
|
|
if (m_metadata.empty())
|
|
|
|
{
|
|
|
|
bencode(std::back_inserter(m_metadata)
|
|
|
|
, m_torrent.torrent_file().create_info_metadata());
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(hasher(&m_metadata[0], m_metadata.size()).final()
|
2006-11-14 01:08:16 +01:00
|
|
|
== m_torrent.torrent_file().info_hash());
|
|
|
|
}
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(!m_metadata.empty());
|
2006-11-14 01:08:16 +01:00
|
|
|
return m_metadata;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool received_metadata(char const* buf, int size, int offset, int total_size)
|
|
|
|
{
|
|
|
|
if (m_torrent.valid_metadata()) return false;
|
|
|
|
|
|
|
|
if ((int)m_metadata.size() < total_size)
|
|
|
|
m_metadata.resize(total_size);
|
|
|
|
|
|
|
|
std::copy(
|
|
|
|
buf
|
|
|
|
, buf + size
|
|
|
|
, &m_metadata[offset]);
|
|
|
|
|
|
|
|
if (m_have_metadata.empty())
|
|
|
|
m_have_metadata.resize(256, false);
|
|
|
|
|
|
|
|
std::pair<int, int> req = offset_to_req(std::make_pair(offset, size)
|
|
|
|
, total_size);
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(req.first + req.second <= (int)m_have_metadata.size());
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
std::fill(
|
|
|
|
m_have_metadata.begin() + req.first
|
|
|
|
, m_have_metadata.begin() + req.first + req.second
|
|
|
|
, true);
|
|
|
|
|
|
|
|
bool have_all = std::count(
|
|
|
|
m_have_metadata.begin()
|
|
|
|
, m_have_metadata.end()
|
|
|
|
, true) == 256;
|
|
|
|
|
|
|
|
if (!have_all) return false;
|
|
|
|
|
|
|
|
hasher h;
|
|
|
|
h.update(&m_metadata[0], (int)m_metadata.size());
|
|
|
|
sha1_hash info_hash = h.final();
|
|
|
|
|
|
|
|
if (info_hash != m_torrent.torrent_file().info_hash())
|
|
|
|
{
|
|
|
|
std::fill(
|
|
|
|
m_have_metadata.begin()
|
|
|
|
, m_have_metadata.begin() + req.first + req.second
|
|
|
|
, false);
|
|
|
|
m_metadata_progress = 0;
|
|
|
|
m_metadata_size = 0;
|
2007-06-01 00:56:29 +02:00
|
|
|
|
|
|
|
if (m_torrent.alerts().should_post(alert::info))
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
2007-06-01 00:56:29 +02:00
|
|
|
m_torrent.alerts().post_alert(metadata_failed_alert(
|
|
|
|
m_torrent.get_handle(), "invalid metadata received from swarm"));
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
2007-06-01 00:56:29 +02:00
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry metadata = bdecode(m_metadata.begin(), m_metadata.end());
|
2007-12-28 21:11:10 +01:00
|
|
|
std::string error;
|
|
|
|
if (!m_torrent.set_metadata(metadata, error))
|
|
|
|
{
|
|
|
|
// this means the metadata is correct, since we
|
|
|
|
// verified it against the info-hash, but we
|
|
|
|
// failed to parse it. Pause the torrent
|
|
|
|
// TODO: Post an alert!
|
|
|
|
m_torrent.pause();
|
|
|
|
return false;
|
|
|
|
}
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
// clear the storage for the bitfield
|
|
|
|
std::vector<bool>().swap(m_have_metadata);
|
|
|
|
std::vector<int>().swap(m_requested_metadata);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns a range of the metadata that
|
|
|
|
// we should request.
|
|
|
|
std::pair<int, int> metadata_request();
|
|
|
|
|
|
|
|
void cancel_metadata_request(std::pair<int, int> req)
|
|
|
|
{
|
|
|
|
for (int i = req.first; i < req.first + req.second; ++i)
|
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_requested_metadata[i] > 0);
|
2006-11-14 01:08:16 +01:00
|
|
|
if (m_requested_metadata[i] > 0)
|
|
|
|
--m_requested_metadata[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// this is called from the peer_connection for
|
|
|
|
// each piece of metadata it receives
|
|
|
|
void metadata_progress(int total_size, int received)
|
|
|
|
{
|
|
|
|
m_metadata_progress += received;
|
|
|
|
m_metadata_size = total_size;
|
|
|
|
}
|
|
|
|
|
2007-12-16 23:43:24 +01:00
|
|
|
void on_piece_pass(int)
|
2007-05-10 00:54:26 +02:00
|
|
|
{
|
|
|
|
// if we became a seed, copy the metadata from
|
|
|
|
// the torrent before it is deallocated
|
|
|
|
if (m_torrent.is_seed())
|
|
|
|
metadata();
|
|
|
|
}
|
|
|
|
|
2006-11-14 01:08:16 +01:00
|
|
|
private:
|
|
|
|
torrent& m_torrent;
|
|
|
|
|
|
|
|
// this buffer is filled with the info-section of
|
|
|
|
// the metadata file while downloading it from
|
|
|
|
// peers, and while sending it.
|
|
|
|
// it is mutable because it's generated lazily
|
|
|
|
mutable std::vector<char> m_metadata;
|
|
|
|
|
|
|
|
int m_metadata_progress;
|
|
|
|
int m_metadata_size;
|
|
|
|
|
|
|
|
// this is a bitfield of size 256, each bit represents
|
|
|
|
// a piece of the metadata. It is set to one if we
|
|
|
|
// have that piece. This vector may be empty
|
|
|
|
// (size 0) if we haven't received any metadata
|
|
|
|
// or if we already have all metadata
|
|
|
|
std::vector<bool> m_have_metadata;
|
|
|
|
// this vector keeps track of how many times each meatdata
|
|
|
|
// block has been requested
|
|
|
|
std::vector<int> m_requested_metadata;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct metadata_peer_plugin : peer_plugin
|
|
|
|
{
|
|
|
|
metadata_peer_plugin(torrent& t, peer_connection& pc
|
|
|
|
, metadata_plugin& tp)
|
|
|
|
: m_waiting_metadata_request(false)
|
|
|
|
, m_message_index(0)
|
|
|
|
, m_metadata_progress(0)
|
2007-04-05 00:27:36 +02:00
|
|
|
, m_no_metadata(min_time())
|
|
|
|
, m_metadata_request(min_time())
|
2006-11-14 01:08:16 +01:00
|
|
|
, m_torrent(t)
|
|
|
|
, m_pc(pc)
|
|
|
|
, m_tp(tp)
|
|
|
|
{}
|
|
|
|
|
|
|
|
// can add entries to the extension handshake
|
|
|
|
virtual void add_handshake(entry& h)
|
|
|
|
{
|
|
|
|
entry& messages = h["m"];
|
|
|
|
messages["LT_metadata"] = 14;
|
|
|
|
}
|
|
|
|
|
|
|
|
// called when the extension handshake from the other end is received
|
|
|
|
virtual bool on_extension_handshake(entry const& h)
|
|
|
|
{
|
2008-04-07 03:39:29 +02:00
|
|
|
m_message_index = 0;
|
|
|
|
entry const* messages = h.find_key("m");
|
|
|
|
if (!messages || messages->type() != entry::dictionary_t) return false;
|
|
|
|
|
|
|
|
entry const* index = messages->find_key("LT_metadata");
|
|
|
|
if (!index || index->type() != entry::int_t) return false;
|
|
|
|
m_message_index = int(index->integer());
|
|
|
|
return true;
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void write_metadata_request(std::pair<int, int> req)
|
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(req.first >= 0);
|
|
|
|
TORRENT_ASSERT(req.second > 0);
|
|
|
|
TORRENT_ASSERT(req.first + req.second <= 256);
|
|
|
|
TORRENT_ASSERT(!m_pc.associated_torrent().expired());
|
|
|
|
TORRENT_ASSERT(!m_pc.associated_torrent().lock()->valid_metadata());
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
int start = req.first;
|
|
|
|
int size = req.second;
|
|
|
|
|
|
|
|
// abort if the peer doesn't support the metadata extension
|
|
|
|
if (m_message_index == 0) return;
|
|
|
|
|
|
|
|
buffer::interval i = m_pc.allocate_send_buffer(9);
|
|
|
|
|
|
|
|
detail::write_uint32(1 + 1 + 3, i.begin);
|
|
|
|
detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
|
|
|
|
detail::write_uint8(m_message_index, i.begin);
|
|
|
|
// means 'request data'
|
|
|
|
detail::write_uint8(0, i.begin);
|
|
|
|
detail::write_uint8(start, i.begin);
|
|
|
|
detail::write_uint8(size - 1, i.begin);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(i.begin == i.end);
|
2006-11-14 01:08:16 +01:00
|
|
|
m_pc.setup_send();
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_metadata(std::pair<int, int> req)
|
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(req.first >= 0);
|
|
|
|
TORRENT_ASSERT(req.second > 0);
|
|
|
|
TORRENT_ASSERT(req.second <= 256);
|
|
|
|
TORRENT_ASSERT(req.first + req.second <= 256);
|
|
|
|
TORRENT_ASSERT(!m_pc.associated_torrent().expired());
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
// abort if the peer doesn't support the metadata extension
|
|
|
|
if (m_message_index == 0) return;
|
|
|
|
|
2007-02-12 10:20:49 +01:00
|
|
|
// only send metadata if the torrent is non-private
|
|
|
|
if (m_torrent.valid_metadata() && !m_torrent.torrent_file().priv())
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
|
|
|
std::pair<int, int> offset
|
|
|
|
= req_to_offset(req, (int)m_tp.metadata().size());
|
|
|
|
|
|
|
|
buffer::interval i = m_pc.allocate_send_buffer(15 + offset.second);
|
|
|
|
|
|
|
|
// yes, we have metadata, send it
|
|
|
|
detail::write_uint32(11 + offset.second, i.begin);
|
|
|
|
detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
|
|
|
|
detail::write_uint8(m_message_index, i.begin);
|
|
|
|
// means 'data packet'
|
|
|
|
detail::write_uint8(1, i.begin);
|
|
|
|
detail::write_uint32((int)m_tp.metadata().size(), i.begin);
|
|
|
|
detail::write_uint32(offset.first, i.begin);
|
|
|
|
std::vector<char> const& metadata = m_tp.metadata();
|
|
|
|
std::copy(metadata.begin() + offset.first
|
|
|
|
, metadata.begin() + offset.first + offset.second, i.begin);
|
|
|
|
i.begin += offset.second;
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(i.begin == i.end);
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buffer::interval i = m_pc.allocate_send_buffer(4 + 3);
|
|
|
|
// we don't have the metadata, reply with
|
|
|
|
// don't have-message
|
|
|
|
detail::write_uint32(1 + 2, i.begin);
|
|
|
|
detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
|
|
|
|
detail::write_uint8(m_message_index, i.begin);
|
|
|
|
// means 'have no data'
|
|
|
|
detail::write_uint8(2, i.begin);
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(i.begin == i.end);
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
m_pc.setup_send();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool on_extended(int length
|
|
|
|
, int msg, buffer::const_interval body)
|
|
|
|
{
|
|
|
|
if (msg != 14) return false;
|
|
|
|
if (m_message_index == 0) return false;
|
|
|
|
|
|
|
|
if (length > 500 * 1024)
|
2008-04-07 03:39:29 +02:00
|
|
|
{
|
|
|
|
m_pc.disconnect("LT_metadata message larger than 500 kB");
|
|
|
|
return true;
|
|
|
|
}
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
if (body.left() < 1) return true;
|
|
|
|
int type = detail::read_uint8(body.begin);
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case 0: // request
|
|
|
|
{
|
|
|
|
if (body.left() < 2) return true;
|
|
|
|
int start = detail::read_uint8(body.begin);
|
|
|
|
int size = detail::read_uint8(body.begin) + 1;
|
|
|
|
|
|
|
|
if (length != 3)
|
|
|
|
{
|
|
|
|
// invalid metadata request
|
2008-04-07 03:39:29 +02:00
|
|
|
m_pc.disconnect("invalid metadata request");
|
|
|
|
return true;
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
write_metadata(std::make_pair(start, size));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1: // data
|
|
|
|
{
|
|
|
|
if (body.left() < 8) return true;
|
|
|
|
|
|
|
|
int total_size = detail::read_int32(body.begin);
|
|
|
|
int offset = detail::read_int32(body.begin);
|
|
|
|
int data_size = length - 9;
|
|
|
|
|
|
|
|
if (total_size > 500 * 1024)
|
2008-04-07 03:39:29 +02:00
|
|
|
{
|
|
|
|
m_pc.disconnect("metadata size larger than 500 kB");
|
|
|
|
return true;
|
|
|
|
}
|
2006-11-14 01:08:16 +01:00
|
|
|
if (total_size <= 0)
|
2008-04-07 03:39:29 +02:00
|
|
|
{
|
|
|
|
m_pc.disconnect("invalid metadata size");
|
|
|
|
return true;
|
|
|
|
}
|
2006-11-14 01:08:16 +01:00
|
|
|
if (offset > total_size || offset < 0)
|
2008-04-07 03:39:29 +02:00
|
|
|
{
|
|
|
|
m_pc.disconnect("invalid metadata offset");
|
|
|
|
return true;
|
|
|
|
}
|
2006-11-14 01:08:16 +01:00
|
|
|
if (offset + data_size > total_size)
|
2008-04-07 03:39:29 +02:00
|
|
|
{
|
|
|
|
m_pc.disconnect("invalid metadata message");
|
|
|
|
return true;
|
|
|
|
}
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
m_tp.metadata_progress(total_size
|
|
|
|
, body.left() - m_metadata_progress);
|
|
|
|
m_metadata_progress = body.left();
|
|
|
|
|
|
|
|
if (body.left() < data_size) return true;
|
|
|
|
|
|
|
|
m_waiting_metadata_request = false;
|
|
|
|
m_tp.received_metadata(body.begin, data_size
|
|
|
|
, offset, total_size);
|
|
|
|
m_metadata_progress = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2: // have no data
|
2007-04-05 00:27:36 +02:00
|
|
|
m_no_metadata = time_now();
|
2006-11-14 01:08:16 +01:00
|
|
|
if (m_waiting_metadata_request)
|
|
|
|
m_tp.cancel_metadata_request(m_last_metadata_request);
|
|
|
|
m_waiting_metadata_request = false;
|
|
|
|
break;
|
|
|
|
default:
|
2008-04-07 03:39:29 +02:00
|
|
|
{
|
|
|
|
std::stringstream msg;
|
|
|
|
msg << "unknown metadata extension message: " << type;
|
|
|
|
m_pc.disconnect(msg.str().c_str());
|
|
|
|
}
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void tick()
|
|
|
|
{
|
|
|
|
// if we don't have any metadata, and this peer
|
|
|
|
// supports the request metadata extension
|
|
|
|
// and we aren't currently waiting for a request
|
|
|
|
// reply. Then, send a request for some metadata.
|
|
|
|
if (!m_torrent.valid_metadata()
|
|
|
|
&& m_message_index != 0
|
|
|
|
&& !m_waiting_metadata_request
|
|
|
|
&& has_metadata())
|
|
|
|
{
|
|
|
|
m_last_metadata_request = m_tp.metadata_request();
|
|
|
|
write_metadata_request(m_last_metadata_request);
|
|
|
|
m_waiting_metadata_request = true;
|
2007-04-05 00:27:36 +02:00
|
|
|
m_metadata_request = time_now();
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool has_metadata() const
|
|
|
|
{
|
2007-04-05 00:27:36 +02:00
|
|
|
return time_now() - m_no_metadata > minutes(5);
|
2006-11-14 01:08:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
// this is set to true when we send a metadata
|
|
|
|
// request to this peer, and reset to false when
|
|
|
|
// we receive a reply to our request.
|
|
|
|
bool m_waiting_metadata_request;
|
|
|
|
|
|
|
|
// this is the message index the remote peer uses
|
|
|
|
// for metadata extension messages.
|
|
|
|
int m_message_index;
|
|
|
|
|
|
|
|
// the number of bytes of metadata we have received
|
|
|
|
// so far from this per, only counting the current
|
|
|
|
// request. Any previously finished requests
|
|
|
|
// that have been forwarded to the torrent object
|
|
|
|
// do not count.
|
|
|
|
int m_metadata_progress;
|
|
|
|
|
|
|
|
// this is set to the current time each time we get a
|
|
|
|
// "I don't have metadata" message.
|
2007-04-05 00:27:36 +02:00
|
|
|
ptime m_no_metadata;
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
// this is set to the time when we last sent
|
|
|
|
// a request for metadata to this peer
|
2007-04-05 00:27:36 +02:00
|
|
|
ptime m_metadata_request;
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
// if we're waiting for a metadata request
|
|
|
|
// this was the request we sent
|
|
|
|
std::pair<int, int> m_last_metadata_request;
|
|
|
|
|
|
|
|
torrent& m_torrent;
|
|
|
|
peer_connection& m_pc;
|
|
|
|
metadata_plugin& m_tp;
|
|
|
|
};
|
|
|
|
|
|
|
|
boost::shared_ptr<peer_plugin> metadata_plugin::new_connection(
|
|
|
|
peer_connection* pc)
|
|
|
|
{
|
2007-04-02 22:00:24 +02:00
|
|
|
bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
|
|
|
|
if (!c) return boost::shared_ptr<peer_plugin>();
|
2006-11-14 01:08:16 +01:00
|
|
|
return boost::shared_ptr<peer_plugin>(new metadata_peer_plugin(m_torrent, *pc, *this));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<int, int> metadata_plugin::metadata_request()
|
|
|
|
{
|
|
|
|
// count the number of peers that supports the
|
|
|
|
// extension and that has metadata
|
|
|
|
int peers = 0;
|
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
2007-10-31 10:48:20 +01:00
|
|
|
for (torrent::peer_iterator i = m_torrent.begin()
|
2006-11-14 01:08:16 +01:00
|
|
|
, end(m_torrent.end()); i != end; ++i)
|
|
|
|
{
|
2007-10-31 10:48:20 +01:00
|
|
|
bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(*i);
|
2006-11-14 01:08:16 +01:00
|
|
|
if (c == 0) continue;
|
|
|
|
metadata_peer_plugin* p
|
|
|
|
= c->supports_extension<metadata_peer_plugin>();
|
|
|
|
if (p == 0) continue;
|
|
|
|
if (!p->has_metadata()) continue;
|
|
|
|
++peers;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// the number of blocks to request
|
|
|
|
int num_blocks = 256 / (peers + 1);
|
|
|
|
if (num_blocks < 1) num_blocks = 1;
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(num_blocks <= 128);
|
2006-11-14 01:08:16 +01:00
|
|
|
|
2007-08-21 06:46:17 +02:00
|
|
|
int min_element = (std::numeric_limits<int>::max)();
|
2006-11-14 01:08:16 +01:00
|
|
|
int best_index = 0;
|
|
|
|
for (int i = 0; i < 256 - num_blocks + 1; ++i)
|
|
|
|
{
|
|
|
|
int min = *std::min_element(m_requested_metadata.begin() + i
|
|
|
|
, m_requested_metadata.begin() + i + num_blocks);
|
|
|
|
min += std::accumulate(m_requested_metadata.begin() + i
|
|
|
|
, m_requested_metadata.begin() + i + num_blocks, (int)0);
|
|
|
|
|
|
|
|
if (min_element > min)
|
|
|
|
{
|
|
|
|
best_index = i;
|
|
|
|
min_element = min;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<int, int> ret(best_index, num_blocks);
|
|
|
|
for (int i = ret.first; i < ret.first + ret.second; ++i)
|
|
|
|
m_requested_metadata[i]++;
|
|
|
|
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(ret.first >= 0);
|
|
|
|
TORRENT_ASSERT(ret.second > 0);
|
|
|
|
TORRENT_ASSERT(ret.second <= 256);
|
|
|
|
TORRENT_ASSERT(ret.first + ret.second <= 256);
|
2006-11-14 01:08:16 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
} }
|
|
|
|
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
|
|
|
|
2007-09-14 02:11:33 +02:00
|
|
|
boost::shared_ptr<torrent_plugin> create_metadata_plugin(torrent* t, void*)
|
2006-11-14 01:08:16 +01:00
|
|
|
{
|
|
|
|
return boost::shared_ptr<torrent_plugin>(new metadata_plugin(*t));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|