premiere-libtorrent/src/peer_connection.cpp

2699 lines
73 KiB
C++
Raw Normal View History

/*
Copyright (c) 2003, 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.
*/
2004-11-30 12:17:32 +01:00
#include <vector>
#include <iostream>
#include <iomanip>
2004-01-12 21:31:27 +01:00
#include <limits>
#include "libtorrent/peer_connection.hpp"
#include "libtorrent/session.hpp"
2003-12-22 08:14:35 +01:00
#include "libtorrent/identify_client.hpp"
2004-01-04 05:29:13 +01:00
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
2004-01-18 20:12:18 +01:00
#include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp"
2004-01-26 18:39:44 +01:00
#include "libtorrent/io.hpp"
2004-04-17 14:29:35 +02:00
#include "libtorrent/version.hpp"
2004-10-14 03:17:04 +02:00
using namespace boost::posix_time;
2004-01-04 05:29:13 +01:00
namespace libtorrent
{
2004-01-04 05:29:13 +01:00
// the names of the extensions to look for in
// the extensions-message
const char* peer_connection::extension_names[] =
{ "chat", "metadata", "peer_exchange", "listen_port" };
2004-01-04 05:29:13 +01:00
2004-01-05 00:51:54 +01:00
const peer_connection::message_handler peer_connection::m_message_handler[] =
{
&peer_connection::on_choke,
&peer_connection::on_unchoke,
&peer_connection::on_interested,
&peer_connection::on_not_interested,
&peer_connection::on_have,
&peer_connection::on_bitfield,
&peer_connection::on_request,
&peer_connection::on_piece,
&peer_connection::on_cancel,
&peer_connection::on_dht_port,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2004-01-05 00:51:54 +01:00
&peer_connection::on_extension_list,
&peer_connection::on_extended
};
2004-01-04 05:29:13 +01:00
peer_connection::peer_connection(
detail::session_impl& ses
, selector& sel
, torrent* t
2004-01-15 17:45:34 +01:00
, boost::shared_ptr<libtorrent::socket> s)
2005-08-19 01:55:32 +02:00
:
#ifndef NDEBUG
m_last_choke(boost::posix_time::second_clock::universal_time()
- hours(1))
,
#endif
m_state(read_protocol_length)
2004-01-04 05:29:13 +01:00
, m_timeout(120)
, m_packet_size(1)
, m_recv_pos(0)
2004-10-14 03:17:04 +02:00
, m_last_receive(second_clock::universal_time())
, m_last_sent(second_clock::universal_time())
2004-01-04 05:29:13 +01:00
, m_selector(sel)
, m_socket(s)
, m_torrent(t)
, m_attached_to_torrent(true)
, m_ses(ses)
, m_active(true)
2004-03-28 19:45:37 +02:00
, m_writability_monitored(false)
, m_readability_monitored(true)
2004-01-04 05:29:13 +01:00
, m_peer_interested(false)
, m_peer_choked(true)
, m_interesting(false)
, m_choked(true)
2004-03-21 03:03:37 +01:00
, m_failed(false)
2004-01-04 05:29:13 +01:00
, m_supports_extensions(false)
2004-01-28 12:37:46 +01:00
, m_num_pieces(0)
2004-01-04 05:29:13 +01:00
, m_free_upload(0)
, m_trust_points(0)
2004-01-12 21:31:27 +01:00
, m_num_invalid_requests(0)
2004-10-14 03:17:04 +02:00
, m_last_piece(second_clock::universal_time())
2004-01-20 23:59:21 +01:00
, m_disconnecting(false)
2004-10-14 03:17:04 +02:00
, m_became_uninterested(second_clock::universal_time())
, m_became_uninteresting(second_clock::universal_time())
, m_no_metadata(
boost::gregorian::date(1970, boost::date_time::Jan, 1)
, boost::posix_time::seconds(0))
2004-08-08 23:26:40 +02:00
, m_metadata_request(
boost::gregorian::date(1970, boost::date_time::Jan, 1)
, boost::posix_time::seconds(0))
, m_waiting_metadata_request(false)
2005-04-03 17:44:17 +02:00
, m_connecting(false)
2005-06-15 14:54:35 +02:00
, m_metadata_progress(0)
2004-01-04 05:29:13 +01:00
{
INVARIANT_CHECK;
2005-08-05 04:43:44 +02:00
// these numbers are used the first second of connection.
// then the given upload limits will be applied by running
// allocate_resources().
2005-08-23 11:59:56 +02:00
2004-03-28 19:45:37 +02:00
m_ul_bandwidth_quota.min = 10;
2005-08-23 11:59:56 +02:00
m_ul_bandwidth_quota.max = resource_request::inf;
if (m_torrent->m_ul_bandwidth_quota.given == resource_request::inf)
{
m_ul_bandwidth_quota.given = resource_request::inf;
}
else
{
// just enough to get started with the handshake and bitmask
m_ul_bandwidth_quota.given = 400;
}
2004-03-28 19:45:37 +02:00
m_dl_bandwidth_quota.min = 10;
2005-08-23 11:59:56 +02:00
m_dl_bandwidth_quota.max = resource_request::inf;
if (m_torrent->m_dl_bandwidth_quota.given == resource_request::inf)
{
m_dl_bandwidth_quota.given = resource_request::inf;
}
else
{
// just enough to get started with the handshake and bitmask
m_dl_bandwidth_quota.given = 400;
}
2004-03-27 23:02:31 +01:00
2004-01-04 05:29:13 +01:00
assert(!m_socket->is_blocking());
assert(m_torrent != 0);
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
m_logger = m_ses.create_log(s->sender().as_string() + "_"
+ boost::lexical_cast<std::string>(s->sender().port));
(*m_logger) << "*** OUTGOING CONNECTION\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-15 17:45:34 +01:00
std::fill(m_peer_id.begin(), m_peer_id.end(), 0);
2004-01-04 05:29:13 +01:00
// initialize the extension list to zero, since
// we don't know which extensions the other
// end supports yet
2004-01-07 13:07:16 +01:00
std::fill(m_extension_messages, m_extension_messages + num_supported_extensions, -1);
2004-01-04 05:29:13 +01:00
send_handshake();
2004-01-04 05:29:13 +01:00
// start in the state where we are trying to read the
// handshake from the other side
m_recv_buffer.resize(1);
2004-01-04 05:29:13 +01:00
// assume the other end has no pieces
if (m_torrent->ready_for_connections())
{
init();
send_bitfield();
}
2004-01-04 05:29:13 +01:00
}
2003-12-07 06:53:04 +01:00
2004-01-04 05:29:13 +01:00
peer_connection::peer_connection(
detail::session_impl& ses
, selector& sel
, boost::shared_ptr<libtorrent::socket> s)
2005-08-19 01:55:32 +02:00
:
#ifndef NDEBUG
m_last_choke(boost::posix_time::second_clock::universal_time()
- hours(1))
,
#endif
m_state(read_protocol_length)
2004-01-04 05:29:13 +01:00
, m_timeout(120)
, m_packet_size(1)
, m_recv_pos(0)
2004-10-14 03:17:04 +02:00
, m_last_receive(second_clock::universal_time())
, m_last_sent(second_clock::universal_time())
2004-01-04 05:29:13 +01:00
, m_selector(sel)
, m_socket(s)
, m_torrent(0)
, m_attached_to_torrent(false)
2004-01-04 05:29:13 +01:00
, m_ses(ses)
, m_active(false)
2004-03-28 19:45:37 +02:00
, m_writability_monitored(false)
, m_readability_monitored(true)
2004-01-04 05:29:13 +01:00
, m_peer_id()
, m_peer_interested(false)
, m_peer_choked(true)
, m_interesting(false)
, m_choked(true)
2004-03-21 03:03:37 +01:00
, m_failed(false)
2004-01-04 05:29:13 +01:00
, m_supports_extensions(false)
2004-01-28 12:37:46 +01:00
, m_num_pieces(0)
2004-01-04 05:29:13 +01:00
, m_free_upload(0)
, m_trust_points(0)
2004-01-12 21:31:27 +01:00
, m_num_invalid_requests(0)
2004-10-14 03:17:04 +02:00
, m_last_piece(second_clock::universal_time())
2004-01-20 23:59:21 +01:00
, m_disconnecting(false)
2004-10-14 03:17:04 +02:00
, m_became_uninterested(second_clock::universal_time())
, m_became_uninteresting(second_clock::universal_time())
, m_no_metadata(
boost::gregorian::date(1970, boost::date_time::Jan, 1)
, boost::posix_time::seconds(0))
2004-08-08 23:26:40 +02:00
, m_metadata_request(
boost::gregorian::date(1970, boost::date_time::Jan, 1)
, boost::posix_time::seconds(0))
, m_waiting_metadata_request(false)
2005-04-03 17:44:17 +02:00
, m_connecting(true)
2005-06-15 14:54:35 +02:00
, m_metadata_progress(0)
2004-01-04 05:29:13 +01:00
{
INVARIANT_CHECK;
2004-03-27 23:02:31 +01:00
// upload bandwidth will only be given to connections
// that are part of a torrent. Since this is an incoming
// connection, we have to give it some initial bandwidth
2005-08-05 04:43:44 +02:00
// to send the handshake.
// after one second, allocate_resources() will be called
// and the correct bandwidth limits will be set on all
// connections.
2005-08-23 11:59:56 +02:00
2004-03-28 19:45:37 +02:00
m_ul_bandwidth_quota.min = 10;
2005-08-23 11:59:56 +02:00
m_ul_bandwidth_quota.max = resource_request::inf;
if (m_ses.m_upload_rate == -1)
{
m_ul_bandwidth_quota.given = resource_request::inf;
}
else
{
// just enough to get started with the handshake and bitmask
m_ul_bandwidth_quota.given = 400;
}
2004-03-28 19:45:37 +02:00
m_dl_bandwidth_quota.min = 10;
2005-08-23 11:59:56 +02:00
m_dl_bandwidth_quota.max = resource_request::inf;
if (m_ses.m_download_rate == -1)
{
m_dl_bandwidth_quota.given = resource_request::inf;
}
else
{
// just enough to get started with the handshake and bitmask
m_dl_bandwidth_quota.given = 400;
}
2004-03-27 23:02:31 +01:00
2004-01-04 05:29:13 +01:00
assert(!m_socket->is_blocking());
2003-11-09 19:17:09 +01:00
2004-01-15 17:45:34 +01:00
std::fill(m_peer_id.begin(), m_peer_id.end(), 0);
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
m_logger = m_ses.create_log(s->sender().as_string() + "_"
+ boost::lexical_cast<std::string>(s->sender().port));
(*m_logger) << "*** INCOMING CONNECTION\n";
2005-03-19 13:22:40 +01:00
#endif
2003-11-09 19:17:09 +01:00
2004-01-04 05:29:13 +01:00
// initialize the extension list to zero, since
// we don't know which extensions the other
// end supports yet
2004-01-07 13:07:16 +01:00
std::fill(m_extension_messages, m_extension_messages + num_supported_extensions, -1);
2004-01-04 05:29:13 +01:00
// we are not attached to any torrent yet.
// we have to wait for the handshake to see
// which torrent the connector want's to connect to
2004-01-04 05:29:13 +01:00
// start in the state where we are trying to read the
// handshake from the other side
m_recv_buffer.resize(1);
}
void peer_connection::init()
{
assert(m_torrent);
assert(m_torrent->valid_metadata());
assert(m_torrent->ready_for_connections());
m_have_piece.resize(m_torrent->torrent_file().num_pieces(), false);
// now that we have a piece_picker,
// update it with this peers pieces
// build a vector of all pieces
std::vector<int> piece_list;
for (int i = 0; i < (int)m_have_piece.size(); ++i)
{
if (m_have_piece[i])
{
++m_num_pieces;
piece_list.push_back(i);
}
}
// shuffle the piece list
std::random_shuffle(piece_list.begin(), piece_list.end());
// let the torrent know which pieces the
// peer has, in a shuffled order
bool interesting = false;
for (std::vector<int>::iterator i = piece_list.begin();
2005-05-30 19:43:03 +02:00
i != piece_list.end(); ++i)
{
int index = *i;
m_torrent->peer_has(index);
if (!m_torrent->have_piece(index)
2005-05-30 19:43:03 +02:00
&& !m_torrent->picker().is_filtered(index))
interesting = true;
}
if (piece_list.size() == m_have_piece.size())
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << " *** THIS IS A SEED ***\n";
#endif
// if we're a seed too, disconnect
if (m_torrent->is_seed())
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << " we're also a seed, disconnecting\n";
#endif
throw protocol_error("seed to seed connection redundant, disconnecting");
}
}
if (interesting)
m_torrent->get_policy().peer_is_interesting(*this);
}
2004-01-04 05:29:13 +01:00
peer_connection::~peer_connection()
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2005-01-10 12:14:22 +01:00
using namespace boost::posix_time;
if (m_logger)
{
(*m_logger) << to_simple_string(second_clock::universal_time())
<< " *** CONNECTION CLOSED\n";
}
#endif
m_disconnecting = true;
2004-01-04 05:29:13 +01:00
m_selector.remove(m_socket);
if (m_attached_to_torrent)
{
2004-01-04 05:29:13 +01:00
assert(m_torrent != 0);
m_torrent->remove_peer(this);
}
#ifndef NDEBUG
else
{
if (m_torrent)
{
torrent::peer_iterator i = m_torrent->m_connections.find(
get_socket()->sender());
2005-10-01 18:18:36 +02:00
assert(i == m_torrent->m_connections.end());
}
}
#endif
2004-01-04 05:29:13 +01:00
}
2004-03-11 15:56:48 +01:00
void peer_connection::announce_piece(int index)
{
assert(m_torrent);
assert(m_torrent->valid_metadata());
2004-03-11 15:56:48 +01:00
assert(index >= 0 && index < m_torrent->torrent_file().num_pieces());
m_announce_queue.push_back(index);
}
bool peer_connection::has_piece(int i) const
{
assert(m_torrent);
assert(m_torrent->valid_metadata());
2004-03-11 15:56:48 +01:00
assert(i >= 0);
assert(i < m_torrent->torrent_file().num_pieces());
return m_have_piece[i];
}
std::deque<piece_block> const& peer_connection::request_queue() const
{
return m_request_queue;
}
std::deque<piece_block> const& peer_connection::download_queue() const
2004-03-11 15:56:48 +01:00
{
return m_download_queue;
}
std::deque<peer_request> const& peer_connection::upload_queue() const
2004-03-11 15:56:48 +01:00
{
return m_requests;
}
void peer_connection::add_stat(size_type downloaded, size_type uploaded)
{
m_statistics.add_stat(downloaded, uploaded);
}
std::vector<bool> const& peer_connection::get_bitfield() const
2004-03-11 15:56:48 +01:00
{
return m_have_piece;
}
void peer_connection::received_valid_data()
{
m_trust_points++;
if (m_trust_points > 20) m_trust_points = 20;
}
void peer_connection::received_invalid_data()
{
2004-03-21 03:03:37 +01:00
// we decrease more than we increase, to keep the
// allowed failed/passed ratio low.
m_trust_points -= 2;
if (m_trust_points < -7) m_trust_points = -7;
2004-03-11 15:56:48 +01:00
}
int peer_connection::trust_points() const
{
return m_trust_points;
}
size_type peer_connection::total_free_upload() const
{
return m_free_upload;
}
void peer_connection::add_free_upload(size_type free_upload)
{
m_free_upload += free_upload;
}
2004-03-22 15:56:32 +01:00
void peer_connection::reset_upload_quota()
2004-03-11 15:56:48 +01:00
{
2004-03-28 19:45:37 +02:00
m_ul_bandwidth_quota.used = 0;
m_dl_bandwidth_quota.used = 0;
if (!m_readability_monitored)
{
assert(!m_selector.is_readability_monitored(m_socket));
m_selector.monitor_readability(m_socket);
m_readability_monitored = true;
}
2004-03-11 15:56:48 +01:00
send_buffer_updated();
}
2004-01-04 05:29:13 +01:00
void peer_connection::send_handshake()
{
INVARIANT_CHECK;
2004-01-04 05:29:13 +01:00
assert(m_send_buffer.size() == 0);
// add handshake to the send buffer
const char version_string[] = "BitTorrent protocol";
const int string_len = sizeof(version_string)-1;
m_send_buffer.reserve(1 + string_len + 8 + 20 + 20);
2004-01-04 05:29:13 +01:00
buffer::interval i = m_send_buffer.allocate(1 + string_len + 8 + 20 + 20);
2004-01-04 05:29:13 +01:00
// length of version string
*i.begin = string_len;
++i.begin;
2004-01-04 05:29:13 +01:00
// version string itself
std::copy(
version_string
, version_string + string_len
, i.begin);
i.begin += string_len;
2004-01-04 05:29:13 +01:00
// 8 zeroes
std::fill(
i.begin
, i.begin + 8
2004-01-04 05:29:13 +01:00
, 0);
2004-11-01 00:16:08 +01:00
// indicate that we support the DHT messages
*(i.begin + 7) = 0x01;
i.begin += 8;
2004-01-04 05:29:13 +01:00
// info hash
std::copy(
m_torrent->torrent_file().info_hash().begin()
, m_torrent->torrent_file().info_hash().end()
, i.begin);
i.begin += 20;
2004-01-04 05:29:13 +01:00
// peer id
std::copy(
m_ses.get_peer_id().begin()
, m_ses.get_peer_id().end()
, i.begin);
i.begin += 20;
assert(i.begin == i.end);
2003-12-07 06:53:04 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " ==> HANDSHAKE\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-04 05:29:13 +01:00
send_buffer_updated();
}
2004-01-08 14:03:38 +01:00
// verifies a piece to see if it is valid (is within a valid range)
// and if it can correspond to a request generated by libtorrent.
bool peer_connection::verify_piece(const peer_request& p) const
{
assert(m_torrent->valid_metadata());
2004-01-08 14:03:38 +01:00
return p.piece >= 0
&& p.piece < m_torrent->torrent_file().num_pieces()
&& p.length > 0
&& p.start >= 0
&& (p.length == m_torrent->block_size()
|| (p.length < m_torrent->block_size()
&& p.piece == m_torrent->torrent_file().num_pieces()-1
&& p.start + p.length == m_torrent->torrent_file().piece_size(p.piece)))
&& p.start + p.length <= m_torrent->torrent_file().piece_size(p.piece)
&& p.start % m_torrent->block_size() == 0;
}
2004-01-04 05:29:13 +01:00
boost::optional<piece_block_progress> peer_connection::downloading_piece() const
{
// are we currently receiving a 'piece' message?
if (m_state != read_packet
|| m_recv_pos < 9
|| m_recv_buffer[0] != msg_piece)
return boost::optional<piece_block_progress>();
const char* ptr = &m_recv_buffer[1];
2004-01-08 14:03:38 +01:00
peer_request r;
r.piece = detail::read_int32(ptr);
r.start = detail::read_int32(ptr);
2004-01-08 14:03:38 +01:00
r.length = m_packet_size - 9;
2004-01-04 05:29:13 +01:00
// is any of the piece message header data invalid?
2004-01-08 14:03:38 +01:00
if (!verify_piece(r))
2004-01-04 05:29:13 +01:00
return boost::optional<piece_block_progress>();
piece_block_progress p;
2004-01-08 14:03:38 +01:00
p.piece_index = r.piece;
p.block_index = r.start / m_torrent->block_size();
2004-01-04 05:29:13 +01:00
p.bytes_downloaded = m_recv_pos - 9;
2004-01-08 14:03:38 +01:00
p.full_block_bytes = r.length;
2004-01-04 05:29:13 +01:00
return boost::optional<piece_block_progress>(p);
}
2003-12-07 06:53:04 +01:00
2004-01-05 00:51:54 +01:00
// message handlers
2004-01-05 00:51:54 +01:00
// -----------------------------
// ----------- CHOKE -----------
// -----------------------------
void peer_connection::on_choke(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-05 00:51:54 +01:00
if (m_packet_size != 1)
throw protocol_error("'choke' message size != 1");
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== CHOKE\n";
2004-01-05 00:51:54 +01:00
#endif
m_peer_choked = true;
m_torrent->get_policy().choked(*this);
// remove all pieces from this peers download queue and
// remove the 'downloading' flag from piece_picker.
for (std::deque<piece_block>::iterator i = m_download_queue.begin();
2005-08-19 01:55:32 +02:00
i != m_download_queue.end(); ++i)
2004-01-04 06:28:24 +01:00
{
2004-01-05 00:51:54 +01:00
m_torrent->picker().abort_download(*i);
2004-01-04 06:28:24 +01:00
}
for (std::deque<piece_block>::const_iterator i = m_request_queue.begin()
, end(m_request_queue.end()); i != end; ++i)
{
// since this piece was skipped, clear it and allow it to
// be requested from other peers
m_torrent->picker().abort_download(*i);
}
2004-01-05 00:51:54 +01:00
m_download_queue.clear();
m_request_queue.clear();
2004-01-05 00:51:54 +01:00
#ifndef NDEBUG
// m_torrent->picker().integrity_check(m_torrent);
#endif
}
2004-01-04 06:28:24 +01:00
2004-01-05 00:51:54 +01:00
// -----------------------------
// ---------- UNCHOKE ----------
// -----------------------------
2004-01-05 00:51:54 +01:00
void peer_connection::on_unchoke(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-05 00:51:54 +01:00
if (m_packet_size != 1)
throw protocol_error("'unchoke' message size != 1");
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
2003-12-07 06:53:04 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== UNCHOKE\n";
2004-01-05 00:51:54 +01:00
#endif
m_peer_choked = false;
m_torrent->get_policy().unchoked(*this);
}
2004-01-05 00:51:54 +01:00
// -----------------------------
// -------- INTERESTED ---------
// -----------------------------
2004-01-05 00:51:54 +01:00
void peer_connection::on_interested(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-05 00:51:54 +01:00
if (m_packet_size != 1)
throw protocol_error("'interested' message size != 1");
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== INTERESTED\n";
2004-01-05 00:51:54 +01:00
#endif
m_peer_interested = true;
m_torrent->get_policy().interested(*this);
}
2003-12-07 06:53:04 +01:00
2004-01-05 00:51:54 +01:00
// -----------------------------
// ------ NOT INTERESTED -------
// -----------------------------
2004-01-05 00:51:54 +01:00
void peer_connection::on_not_interested(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-05 00:51:54 +01:00
if (m_packet_size != 1)
throw protocol_error("'not interested' message size != 1");
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
2004-10-14 03:17:04 +02:00
m_became_uninterested = second_clock::universal_time();
2004-02-01 14:48:30 +01:00
2004-01-05 00:51:54 +01:00
// clear the request queue if the client isn't interested
m_requests.clear();
2004-02-18 01:08:20 +01:00
send_buffer_updated();
2003-12-18 04:30:41 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== NOT_INTERESTED\n";
2004-01-05 00:51:54 +01:00
#endif
m_peer_interested = false;
m_torrent->get_policy().not_interested(*this);
}
2004-01-05 00:51:54 +01:00
// -----------------------------
// ----------- HAVE ------------
// -----------------------------
2004-01-05 00:51:54 +01:00
void peer_connection::on_have(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-05 00:51:54 +01:00
if (m_packet_size != 5)
throw protocol_error("'have' message size != 5");
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
2003-12-07 06:53:04 +01:00
2004-01-05 00:51:54 +01:00
const char* ptr = &m_recv_buffer[1];
int index = detail::read_int32(ptr);
2004-01-05 00:51:54 +01:00
// if we got an invalid message, abort
if (index >= (int)m_have_piece.size() || index < 0)
2004-01-05 00:51:54 +01:00
throw protocol_error("have message with higher index than the number of pieces");
2004-01-04 05:29:13 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== HAVE [ piece: " << index << "]\n";
2004-01-05 00:51:54 +01:00
#endif
if (m_have_piece[index])
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << " got redundant HAVE message for index: " << index << "\n";
2004-01-05 00:51:54 +01:00
#endif
}
else
{
m_have_piece[index] = true;
2004-01-28 12:37:46 +01:00
// only update the piece_picker if
// we have the metadata
if (m_torrent->valid_metadata())
{
++m_num_pieces;
m_torrent->peer_has(index);
if (!m_torrent->have_piece(index)
&& !is_interesting()
&& !m_torrent->picker().is_filtered(index))
m_torrent->get_policy().peer_is_interesting(*this);
}
2004-01-12 21:31:27 +01:00
if (m_torrent->is_seed() && is_seed())
{
throw protocol_error("seed to seed connection redundant, disconnecting");
}
2004-01-05 00:51:54 +01:00
}
}
2004-01-04 05:29:13 +01:00
2004-01-05 00:51:54 +01:00
// -----------------------------
// --------- BITFIELD ----------
// -----------------------------
2004-01-04 05:29:13 +01:00
2004-01-05 00:51:54 +01:00
void peer_connection::on_bitfield(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
assert(m_torrent);
// if we don't have the metedata, we cannot
// verify the bitfield size
if (m_torrent->valid_metadata()
&& m_packet_size - 1 != ((int)m_have_piece.size() + 7) / 8)
2004-01-05 00:51:54 +01:00
throw protocol_error("bitfield with invalid size");
2004-01-05 00:51:54 +01:00
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
2004-01-04 05:29:13 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== BITFIELD\n";
2004-01-05 00:51:54 +01:00
#endif
// if we don't have metadata yet
// just remember the bitmask
// don't update the piecepicker
// (since it doesn't exist yet)
if (!m_torrent->valid_metadata())
{
m_have_piece.resize((m_packet_size - 1) * 8, false);
for (int i = 0; i < (int)m_have_piece.size(); ++i)
m_have_piece[i] = (m_recv_buffer[1 + (i>>3)] & (1 << (7 - (i&7)))) != 0;
return;
}
2004-01-05 00:51:54 +01:00
// build a vector of all pieces
std::vector<int> piece_list;
for (int i = 0; i < (int)m_have_piece.size(); ++i)
2004-01-05 00:51:54 +01:00
{
bool have = (m_recv_buffer[1 + (i>>3)] & (1 << (7 - (i&7)))) != 0;
2004-01-05 00:51:54 +01:00
if (have && !m_have_piece[i])
{
m_have_piece[i] = true;
2004-01-28 12:37:46 +01:00
++m_num_pieces;
2004-01-05 00:51:54 +01:00
piece_list.push_back(i);
}
else if (!have && m_have_piece[i])
{
2004-07-01 09:12:17 +02:00
// this should probably not be allowed
2004-01-05 00:51:54 +01:00
m_have_piece[i] = false;
2004-01-28 12:37:46 +01:00
--m_num_pieces;
2004-01-05 00:51:54 +01:00
m_torrent->peer_lost(i);
}
}
2004-01-04 05:29:13 +01:00
2004-01-05 00:51:54 +01:00
// shuffle the piece list
std::random_shuffle(piece_list.begin(), piece_list.end());
2004-01-04 05:29:13 +01:00
2004-01-05 00:51:54 +01:00
// let the torrent know which pieces the
// peer has, in a shuffled order
bool interesting = false;
for (std::vector<int>::iterator i = piece_list.begin();
i != piece_list.end(); ++i)
2004-01-05 00:51:54 +01:00
{
int index = *i;
m_torrent->peer_has(index);
if (!m_torrent->have_piece(index)
&& !m_torrent->picker().is_filtered(index))
2004-01-05 00:51:54 +01:00
interesting = true;
}
2004-01-04 05:29:13 +01:00
2004-01-08 14:03:38 +01:00
if (piece_list.size() == m_have_piece.size())
2004-01-05 00:51:54 +01:00
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-08 14:03:38 +01:00
(*m_logger) << " *** THIS IS A SEED ***\n";
2004-01-05 00:51:54 +01:00
#endif
2004-01-08 14:03:38 +01:00
// if we're a seed too, disconnect
if (m_torrent->is_seed())
{
2004-01-12 21:31:27 +01:00
throw protocol_error("seed to seed connection redundant, disconnecting");
2004-01-08 14:03:38 +01:00
}
2004-01-05 00:51:54 +01:00
}
2004-01-04 05:29:13 +01:00
2004-01-05 00:51:54 +01:00
if (interesting) m_torrent->get_policy().peer_is_interesting(*this);
}
2003-12-17 20:03:23 +01:00
2004-01-05 00:51:54 +01:00
// -----------------------------
// ---------- REQUEST ----------
// -----------------------------
2003-12-17 20:03:23 +01:00
2004-01-05 00:51:54 +01:00
void peer_connection::on_request(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-05 00:51:54 +01:00
if (m_packet_size != 13)
throw protocol_error("'request' message size != 13");
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
2003-12-17 20:03:23 +01:00
2004-01-05 00:51:54 +01:00
peer_request r;
const char* ptr = &m_recv_buffer[1];
r.piece = detail::read_int32(ptr);
r.start = detail::read_int32(ptr);
r.length = detail::read_int32(ptr);
2004-01-05 00:51:54 +01:00
if (!m_torrent->valid_metadata())
{
// if we don't have valid metadata yet,
// we shouldn't get a request
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== UNEXPECTED_REQUEST [ "
"piece: " << r.piece << " | "
"s: " << r.start << " | "
"l: " << r.length << " | "
"i: " << m_peer_interested << " | "
"t: " << (int)m_torrent->torrent_file().piece_size(r.piece) << " | "
"n: " << m_torrent->torrent_file().num_pieces() << " ]\n";
#endif
return;
}
2004-11-30 12:17:32 +01:00
if (m_requests.size() > 100)
2004-02-20 16:22:23 +01:00
{
// don't allow clients to abuse our
// memory consumption.
// ignore requests if the client
// is making too many of them.
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== TOO MANY REQUESTS [ "
"piece: " << r.piece << " | "
"s: " << r.start << " | "
"l: " << r.length << " | "
"i: " << m_peer_interested << " | "
"t: " << (int)m_torrent->torrent_file().piece_size(r.piece) << " | "
"n: " << m_torrent->torrent_file().num_pieces() << " ]\n";
#endif
2004-02-20 16:22:23 +01:00
return;
}
2004-01-05 00:51:54 +01:00
// make sure this request
2004-11-30 12:17:32 +01:00
// is legal and that the peer
2004-01-05 00:51:54 +01:00
// is not choked
if (r.piece >= 0
&& r.piece < m_torrent->torrent_file().num_pieces()
2004-01-12 21:31:27 +01:00
&& m_torrent->have_piece(r.piece)
2004-01-05 00:51:54 +01:00
&& r.start >= 0
&& r.start < m_torrent->torrent_file().piece_size(r.piece)
&& r.length > 0
2004-01-05 02:30:34 +01:00
&& r.length + r.start <= m_torrent->torrent_file().piece_size(r.piece)
2004-01-05 00:51:54 +01:00
&& m_peer_interested)
{
// if we have choked the client
// ignore the request
2004-01-05 02:30:34 +01:00
if (m_choked)
return;
2004-01-04 05:29:13 +01:00
2004-01-05 00:51:54 +01:00
m_requests.push_back(r);
send_buffer_updated();
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== REQUEST [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
2004-01-05 00:51:54 +01:00
#endif
}
else
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== INVALID_REQUEST [ "
2004-01-05 02:30:34 +01:00
"piece: " << r.piece << " | "
"s: " << r.start << " | "
"l: " << r.length << " | "
"i: " << m_peer_interested << " | "
"t: " << (int)m_torrent->torrent_file().piece_size(r.piece) << " | "
"n: " << m_torrent->torrent_file().num_pieces() << " ]\n";
#endif
2004-01-12 21:31:27 +01:00
++m_num_invalid_requests;
2004-01-08 14:03:38 +01:00
if (m_torrent->alerts().should_post(alert::debug))
{
m_torrent->alerts().post_alert(invalid_request_alert(
r
, m_torrent->get_handle()
2004-01-31 11:20:19 +01:00
, m_socket->sender()
2004-03-28 19:45:37 +02:00
, m_peer_id
2004-01-08 14:03:38 +01:00
, "peer sent an illegal request, ignoring"));
}
2004-01-05 00:51:54 +01:00
}
}
2004-01-05 00:51:54 +01:00
// -----------------------------
// ----------- PIECE -----------
// -----------------------------
2004-01-05 00:51:54 +01:00
void peer_connection::on_piece(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-08 14:03:38 +01:00
// classify the received data as protocol chatter
// or data payload for the statistics
2004-01-05 00:51:54 +01:00
if (m_recv_pos <= 9)
// only received protocol data
m_statistics.received_bytes(0, received);
else if (m_recv_pos - received >= 9)
// only received payload data
m_statistics.received_bytes(received, 0);
else
{
// received a bit of both
assert(m_recv_pos - received < 9);
assert(m_recv_pos > 9);
assert(9 - (m_recv_pos - received) <= 9);
m_statistics.received_bytes(
m_recv_pos - 9
, 9 - (m_recv_pos - received));
}
m_last_piece = second_clock::universal_time();
2004-01-05 00:51:54 +01:00
if (m_recv_pos < m_packet_size) return;
2004-01-05 00:51:54 +01:00
const char* ptr = &m_recv_buffer[1];
2004-01-08 14:03:38 +01:00
peer_request p;
p.piece = detail::read_int32(ptr);
p.start = detail::read_int32(ptr);
2004-01-08 14:03:38 +01:00
p.length = m_packet_size - 9;
2003-12-07 06:53:04 +01:00
2004-01-08 14:03:38 +01:00
if (!verify_piece(p))
2004-01-05 00:51:54 +01:00
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== INVALID_PIECE [ piece: " << p.piece << " | "
2004-01-08 14:03:38 +01:00
"start: " << p.start << " | "
"length: " << p.length << " ]\n";
2004-01-05 00:51:54 +01:00
#endif
2004-01-08 14:03:38 +01:00
throw protocol_error("invalid piece packet");
2004-01-05 00:51:54 +01:00
}
2004-01-04 05:29:13 +01:00
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-01-05 00:51:54 +01:00
piece_picker& picker = m_torrent->picker();
2004-01-13 04:08:59 +01:00
2005-08-04 00:51:21 +02:00
piece_block block_finished(p.piece, p.start / m_torrent->block_size());
2004-01-05 00:51:54 +01:00
std::deque<piece_block>::iterator b
= std::find(
m_download_queue.begin()
, m_download_queue.end()
, block_finished);
2003-12-08 22:59:48 +01:00
2005-08-04 00:51:21 +02:00
std::deque<piece_block>::iterator i;
2004-01-05 00:51:54 +01:00
if (b != m_download_queue.end())
{
2005-08-04 00:51:21 +02:00
for (i = m_download_queue.begin();
i != b; ++i)
{
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << to_simple_string(second_clock::universal_time())
<< " *** SKIPPED_PIECE [ piece: " << i->piece_index << " | "
"b: " << i->block_index << " ] ***\n";
#endif
// since this piece was skipped, clear it and allow it to
// be requested from other peers
picker.abort_download(*i);
}
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << to_simple_string(second_clock::universal_time())
<< " <== PIECE [ piece: " << p.piece << " | "
"b: " << p.start / m_torrent->block_size() << " | "
"s: " << p.start << " | "
"l: " << p.length << " ]\n";
#endif
// remove the request that just finished
// from the download queue plus the
// skipped blocks.
m_download_queue.erase(m_download_queue.begin()
, boost::next(b));
send_block_requests();
2004-01-05 00:51:54 +01:00
}
else
{
2004-01-13 04:08:59 +01:00
// cancel the block from the
2004-01-05 00:51:54 +01:00
// peer that has taken over it.
2004-01-13 04:08:59 +01:00
boost::optional<address> peer = m_torrent->picker().get_downloader(block_finished);
if (peer)
{
peer_connection* pc = m_torrent->connection_for(*peer);
if (pc && pc != this)
{
pc->send_cancel(block_finished);
}
}
else
{
if (m_torrent->alerts().should_post(alert::debug))
{
m_torrent->alerts().post_alert(
peer_error_alert(
2004-03-28 19:45:37 +02:00
m_socket->sender()
, m_peer_id
, "got a block that was not requested"));
2004-01-13 04:08:59 +01:00
}
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-13 04:08:59 +01:00
(*m_logger) << " *** The block we just got was not requested ***\n";
#endif
}
2004-01-05 00:51:54 +01:00
}
2003-12-08 22:59:48 +01:00
2005-08-04 00:51:21 +02:00
// if the block we got is already finished, then ignore it
if (picker.is_finished(block_finished)) return;
2004-01-08 14:03:38 +01:00
m_torrent->filesystem().write(&m_recv_buffer[9], p.piece, p.start, p.length);
2004-01-19 20:36:55 +01:00
bool was_seed = m_torrent->is_seed();
2005-06-11 01:12:50 +02:00
bool was_finished = picker.num_filtered() + m_torrent->num_pieces()
== m_torrent->torrent_file().num_pieces();
2004-01-12 21:31:27 +01:00
picker.mark_as_finished(block_finished, m_socket->sender());
2004-01-05 00:51:54 +01:00
m_torrent->get_policy().block_finished(*this, block_finished);
2004-01-05 00:51:54 +01:00
// did we just finish the piece?
2004-01-08 14:03:38 +01:00
if (picker.is_piece_finished(p.piece))
2004-01-05 00:51:54 +01:00
{
2004-01-08 14:03:38 +01:00
bool verified = m_torrent->verify_piece(p.piece);
2004-01-05 00:51:54 +01:00
if (verified)
{
2004-01-08 14:03:38 +01:00
m_torrent->announce_piece(p.piece);
2005-06-11 01:12:50 +02:00
assert(m_torrent->valid_metadata());
if (!was_finished
&& picker.num_filtered() + m_torrent->num_pieces()
== m_torrent->torrent_file().num_pieces())
{
// torrent finished
// i.e. all the pieces we're interested in have
// been downloaded. Release the files (they will open
// in read only mode if needed)
m_torrent->finished();
}
2004-01-05 00:51:54 +01:00
}
else
{
2004-01-08 14:03:38 +01:00
m_torrent->piece_failed(p.piece);
2004-01-05 00:51:54 +01:00
}
2004-01-08 14:03:38 +01:00
m_torrent->get_policy().piece_finished(p.piece, verified);
2004-01-18 11:22:18 +01:00
2004-01-19 20:36:55 +01:00
if (!was_seed && m_torrent->is_seed())
2004-01-18 11:22:18 +01:00
{
assert(verified);
m_torrent->completed();
2004-01-18 11:22:18 +01:00
}
2004-01-05 00:51:54 +01:00
}
}
2004-01-05 00:51:54 +01:00
// -----------------------------
// ---------- CANCEL -----------
// -----------------------------
2004-01-05 00:51:54 +01:00
void peer_connection::on_cancel(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-05 00:51:54 +01:00
if (m_packet_size != 13)
throw protocol_error("'cancel' message size != 13");
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
2003-11-05 00:27:06 +01:00
2004-01-05 00:51:54 +01:00
peer_request r;
const char* ptr = &m_recv_buffer[1];
r.piece = detail::read_int32(ptr);
r.start = detail::read_int32(ptr);
r.length = detail::read_int32(ptr);
2004-01-04 05:29:13 +01:00
2004-01-05 00:51:54 +01:00
std::deque<peer_request>::iterator i
= std::find(m_requests.begin(), m_requests.end(), r);
if (i != m_requests.end())
{
m_requests.erase(i);
}
2003-11-05 00:27:06 +01:00
2004-03-28 19:45:37 +02:00
if (!can_write() && m_writability_monitored)
2004-01-05 00:51:54 +01:00
{
2004-03-28 19:45:37 +02:00
m_writability_monitored = false;
2004-01-05 00:51:54 +01:00
m_selector.remove_writable(m_socket);
}
2003-11-05 00:27:06 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " <== CANCEL [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
2004-01-05 00:51:54 +01:00
#endif
}
2003-11-05 00:27:06 +01:00
// -----------------------------
// --------- DHT PORT ----------
// -----------------------------
void peer_connection::on_dht_port(int received)
{
INVARIANT_CHECK;
assert(received > 0);
if (m_packet_size != 3)
throw protocol_error("'dht_port' message size != 3");
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
const char* ptr = &m_recv_buffer[1];
int listen_port = detail::read_uint16(ptr);
#ifdef TORRENT_VERBOSE_LOGGING
using namespace boost::posix_time;
(*m_logger) << to_simple_string(second_clock::universal_time())
<< " <== DHT_PORT [ p: " << listen_port << " ]\n";
#endif
}
2004-01-05 00:51:54 +01:00
// -----------------------------
// ------ EXTENSION LIST -------
// -----------------------------
2004-01-05 00:51:54 +01:00
void peer_connection::on_extension_list(int received)
{
INVARIANT_CHECK;
assert(m_torrent);
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-11-01 00:16:08 +01:00
if (m_packet_size > 100 * 1000)
2004-01-05 00:51:54 +01:00
{
// too big extension message, abort
throw protocol_error("'extensions' message size > 100kB");
}
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) return;
2003-12-01 22:27:27 +01:00
2004-01-05 00:51:54 +01:00
try
{
entry e = bdecode(m_recv_buffer.begin()+1, m_recv_buffer.end());
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-05 00:51:54 +01:00
entry::dictionary_type& extensions = e.dict();
2004-08-08 23:26:40 +02:00
std::stringstream ext;
e.print(ext);
(*m_logger) << ext.str();
2004-04-17 14:29:35 +02:00
#endif
2004-01-05 00:51:54 +01:00
for (int i = 0; i < num_supported_extensions; ++i)
{
2004-03-17 13:14:44 +01:00
entry* f = e.find_key(extension_names[i]);
if (f)
{
2004-03-17 13:14:44 +01:00
m_extension_messages[i] = (int)f->integer();
}
2004-01-05 00:51:54 +01:00
}
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-12 04:05:10 +01:00
(*m_logger) << "supported extensions:\n";
for (entry::dictionary_type::const_iterator i = extensions.begin();
i != extensions.end();
++i)
{
(*m_logger) << i->first << "\n";
}
#endif
2004-01-05 00:51:54 +01:00
}
catch(invalid_encoding&)
2004-01-05 00:51:54 +01:00
{
throw protocol_error("'extensions' packet contains invalid bencoding");
}
catch(type_error&)
2004-01-05 00:51:54 +01:00
{
throw protocol_error("'extensions' packet contains incorrect types");
}
}
2004-01-05 00:51:54 +01:00
// -----------------------------
// --------- EXTENDED ----------
// -----------------------------
2004-01-05 00:51:54 +01:00
void peer_connection::on_extended(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-07 01:48:02 +01:00
m_statistics.received_bytes(0, received);
if (m_packet_size < 5)
throw protocol_error("'extended' message smaller than 5 bytes");
if (m_torrent == 0)
throw protocol_error("'extended' message sent before proper handshake");
if (m_recv_pos < 5) return;
const char* ptr = &m_recv_buffer[1];
int extended_id = detail::read_int32(ptr);
2004-01-07 01:48:02 +01:00
2005-06-15 14:54:35 +02:00
if (extended_id >= 0 && extended_id < num_supported_extensions
&& !m_ses.m_extension_enabled[extended_id])
2004-11-01 00:16:08 +01:00
throw protocol_error("'extended' message using disabled extension");
2004-01-07 01:48:02 +01:00
switch (extended_id)
{
case extended_chat_message:
on_chat(); break;
case extended_metadata_message:
on_metadata(); break;
case extended_peer_exchange_message:
on_peer_exchange(); break;
case extended_listen_port_message:
on_listen_port(); break;
default:
2005-06-15 14:54:35 +02:00
throw protocol_error("unknown extended message id: "
+ boost::lexical_cast<std::string>(extended_id));
};
}
// -----------------------------
// ----------- CHAT ------------
// -----------------------------
void peer_connection::on_chat()
{
if (m_packet_size > 2 * 1024)
throw protocol_error("CHAT message larger than 2 kB");
if (m_recv_pos < m_packet_size) return;
try
{
entry d = bdecode(m_recv_buffer.begin()+5, m_recv_buffer.end());
const std::string& str = d["msg"].string();
if (m_torrent->alerts().should_post(alert::critical))
2004-01-07 01:48:02 +01:00
{
m_torrent->alerts().post_alert(
chat_message_alert(
m_torrent->get_handle()
, m_socket->sender(), str));
}
2004-01-07 01:48:02 +01:00
}
catch (invalid_encoding&)
{
// TODO: make these non-fatal errors
// they should just ignore the chat message
// and report the error via an alert
throw protocol_error("invalid bencoding in CHAT message");
}
catch (type_error&)
{
throw protocol_error("invalid types in bencoded CHAT message");
}
return;
}
2004-01-07 01:48:02 +01:00
// -----------------------------
// --------- METADATA ----------
// -----------------------------
void peer_connection::on_metadata()
{
assert(m_torrent);
if (m_packet_size > 500 * 1024)
throw protocol_error("metadata message larger than 500 kB");
2005-06-15 14:54:35 +02:00
if (m_recv_pos < 6) return;
2005-06-15 14:54:35 +02:00
std::vector<char>::iterator ptr = m_recv_buffer.begin() + 5;
int type = detail::read_uint8(ptr);
switch (type)
{
case 0: // request
{
2005-06-15 14:54:35 +02:00
if (m_recv_pos < m_packet_size) return;
int start = detail::read_uint8(ptr);
2004-06-15 01:10:16 +02:00
int size = detail::read_uint8(ptr) + 1;
2004-06-15 01:10:16 +02:00
if (m_packet_size != 8)
2004-01-07 01:48:02 +01:00
{
// invalid metadata request
throw protocol_error("invalid metadata request");
2004-01-07 01:48:02 +01:00
}
2004-09-12 15:53:00 +02:00
send_metadata(std::make_pair(start, size));
2004-01-07 01:48:02 +01:00
}
break;
case 1: // data
{
2005-06-15 14:54:35 +02:00
if (m_recv_pos < 14) return;
int total_size = detail::read_int32(ptr);
int offset = detail::read_int32(ptr);
int data_size = m_packet_size - 5 - 9;
if (total_size > 500 * 1024)
throw protocol_error("metadata size larger than 500 kB");
2005-10-18 20:16:36 +02:00
if (total_size <= 0)
throw protocol_error("invalid metadata size");
if (offset > total_size || offset < 0)
throw protocol_error("invalid metadata offset");
if (offset + data_size > total_size)
throw protocol_error("invalid metadata message");
2005-06-15 14:54:35 +02:00
m_torrent->metadata_progress(total_size, m_recv_pos - 14
- m_metadata_progress);
m_metadata_progress = m_recv_pos - 14;
if (m_recv_pos < m_packet_size) return;
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-09-12 15:53:00 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-09-12 15:53:00 +02:00
<< " <== METADATA [ tot: " << total_size << " offset: "
<< offset << " size: " << data_size << " ]\n";
#endif
2004-08-08 23:26:40 +02:00
m_waiting_metadata_request = false;
m_torrent->received_metadata(&m_recv_buffer[5+9], data_size, offset, total_size);
2005-06-15 14:54:35 +02:00
m_metadata_progress = 0;
}
break;
case 2: // have no data
2005-06-15 14:54:35 +02:00
if (m_recv_pos < m_packet_size) return;
2004-10-14 03:17:04 +02:00
m_no_metadata = second_clock::universal_time();
2004-09-12 15:53:00 +02:00
if (m_waiting_metadata_request)
m_torrent->cancel_metadata_request(m_last_metadata_request);
2004-08-08 23:26:40 +02:00
m_waiting_metadata_request = false;
break;
2005-06-15 14:54:35 +02:00
default:
throw protocol_error("unknown metadata extension message: "
+ boost::lexical_cast<std::string>(type));
}
}
2004-01-07 01:48:02 +01:00
// -----------------------------
// ------ PEER EXCHANGE --------
// -----------------------------
void peer_connection::on_peer_exchange()
{
2004-01-05 00:51:54 +01:00
}
2003-12-07 06:53:04 +01:00
// -----------------------------
// ------- LISTEN PORT ---------
// -----------------------------
void peer_connection::on_listen_port()
{
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
assert(m_torrent);
2004-01-04 05:29:13 +01:00
if (m_packet_size != 7)
throw protocol_error("invalid listen_port message");
if (is_local())
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< "<== LISTEN_PORT [ UNEXPECTED ]\n";
#endif
return;
}
2004-01-04 05:29:13 +01:00
const char* ptr = &m_recv_buffer[5];
unsigned short port = detail::read_uint16(ptr);
2004-01-04 05:29:13 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< "<== LISTEN_PORT [ port: " << port << " ]\n";
#endif
2004-01-04 05:29:13 +01:00
address adr = m_socket->sender();
adr.port = port;
m_torrent->get_policy().peer_from_tracker(adr, m_peer_id);
}
2004-01-05 00:51:54 +01:00
2004-09-12 15:53:00 +02:00
bool peer_connection::has_metadata() const
{
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
return second_clock::universal_time() - m_no_metadata > minutes(5);
2004-09-12 15:53:00 +02:00
}
2004-01-05 00:51:54 +01:00
2004-01-20 12:01:50 +01:00
void peer_connection::disconnect()
{
2004-03-31 01:55:52 +02:00
if (m_disconnecting) return;
2005-03-05 15:17:17 +01:00
detail::session_impl::connection_map::iterator i
= m_ses.m_connections.find(m_socket);
2004-01-20 23:59:21 +01:00
m_disconnecting = true;
2004-01-20 12:01:50 +01:00
assert(i != m_ses.m_connections.end());
2005-03-05 15:17:17 +01:00
assert(std::find(m_ses.m_disconnect_peer.begin()
, m_ses.m_disconnect_peer.end(), i)
== m_ses.m_disconnect_peer.end());
2004-01-20 12:01:50 +01:00
m_ses.m_disconnect_peer.push_back(i);
}
2004-01-05 00:51:54 +01:00
bool peer_connection::dispatch_message(int received)
{
INVARIANT_CHECK;
2004-01-26 01:21:12 +01:00
assert(received > 0);
2004-01-05 00:51:54 +01:00
assert(m_recv_pos >= received);
assert(m_recv_pos > 0);
2004-01-26 01:21:12 +01:00
assert(m_torrent != 0);
2004-01-05 00:51:54 +01:00
int packet_type = m_recv_buffer[0];
if (packet_type < 0
|| packet_type >= num_supported_messages
|| m_message_handler[packet_type] == 0)
{
throw protocol_error("unknown message id: "
+ boost::lexical_cast<std::string>(packet_type)
+ " size: " + boost::lexical_cast<std::string>(m_packet_size));
}
2004-01-05 00:51:54 +01:00
assert(m_message_handler[packet_type] != 0);
// call the correct handler for this packet type
(this->*m_message_handler[packet_type])(received);
if (m_recv_pos < m_packet_size) return false;
2004-01-04 05:29:13 +01:00
assert(m_recv_pos == m_packet_size);
return true;
}
2004-01-12 21:31:27 +01:00
void peer_connection::send_cancel(piece_block block)
2004-01-04 05:29:13 +01:00
{
INVARIANT_CHECK;
assert(m_torrent->valid_metadata());
2004-01-04 05:29:13 +01:00
assert(block.piece_index >= 0);
assert(block.piece_index < m_torrent->torrent_file().num_pieces());
2004-01-25 23:41:55 +01:00
assert(block.block_index >= 0);
assert(block.block_index < m_torrent->torrent_file().piece_size(block.piece_index));
2004-01-04 05:29:13 +01:00
assert(m_torrent->picker().is_downloading(block));
2003-11-05 00:27:06 +01:00
2004-01-04 05:29:13 +01:00
m_torrent->picker().abort_download(block);
2003-11-05 00:27:06 +01:00
std::deque<piece_block>::iterator it
2004-01-04 05:29:13 +01:00
= std::find(m_download_queue.begin(), m_download_queue.end(), block);
if (it == m_download_queue.end())
{
it = std::find(m_request_queue.begin(), m_request_queue.end(), block);
assert(it != m_request_queue.end());
if (it == m_request_queue.end()) return;
m_request_queue.erase(it);
}
else
{
m_download_queue.erase(it);
}
2003-11-05 00:27:06 +01:00
send_block_requests();
2003-11-05 00:27:06 +01:00
2004-01-04 05:29:13 +01:00
int block_offset = block.block_index * m_torrent->block_size();
int block_size
= std::min((int)m_torrent->torrent_file().piece_size(block.piece_index)-block_offset,
m_torrent->block_size());
assert(block_size > 0);
assert(block_size <= m_torrent->block_size());
2003-11-05 00:27:06 +01:00
2004-01-04 05:29:13 +01:00
char buf[] = {0,0,0,13, msg_cancel};
2003-11-05 00:27:06 +01:00
buffer::interval i = m_send_buffer.allocate(17);
2003-11-05 00:27:06 +01:00
std::copy(buf, buf + 5, i.begin);
i.begin += 5;
2003-11-05 00:27:06 +01:00
2004-01-04 05:29:13 +01:00
// index
detail::write_int32(block.piece_index, i.begin);
2004-01-04 05:29:13 +01:00
// begin
detail::write_int32(block_offset, i.begin);
2004-01-04 05:29:13 +01:00
// length
detail::write_int32(block_size, i.begin);
assert(i.begin == i.end);
2004-01-02 21:46:24 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2005-03-19 13:22:40 +01:00
<< " ==> CANCEL [ piece: " << block.piece_index << " | s: "
<< block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n";
#endif
2003-11-05 00:27:06 +01:00
2004-01-04 05:29:13 +01:00
send_buffer_updated();
}
2003-11-05 00:27:06 +01:00
void peer_connection::send_block_requests()
2004-01-04 05:29:13 +01:00
{
// TODO: calculate the desired request queue each tick instead.
// TODO: make this constant user-settable
const int queue_time = 3; // seconds
// (if the latency is more than this, the download will stall)
// so, the queue size is 5 * down_rate / 16 kiB (16 kB is the size of each request)
// the minimum request size is 2 and the maximum is 48
// the block size doesn't have to be 16. So we first query the torrent for it
const int block_size = m_torrent->block_size();
assert(block_size > 0);
int desired_queue_size = static_cast<int>(queue_time
* statistics().download_rate() / block_size);
if (desired_queue_size > max_request_queue) desired_queue_size = max_request_queue;
if (desired_queue_size < min_request_queue) desired_queue_size = min_request_queue;
if ((int)m_download_queue.size() >= desired_queue_size) return;
while (!m_request_queue.empty()
&& (int)m_download_queue.size() < desired_queue_size)
{
piece_block block = m_request_queue.front();
m_request_queue.pop_front();
m_download_queue.push_back(block);
int block_offset = block.block_index * m_torrent->block_size();
int block_size
= std::min((int)m_torrent->torrent_file().piece_size(block.piece_index)-block_offset,
m_torrent->block_size());
assert(block_size > 0);
assert(block_size <= m_torrent->block_size());
char buf[] = {0,0,0,13, msg_request};
buffer::interval i = m_send_buffer.allocate(17);
std::copy(buf, buf + 5, i.begin);
i.begin += 5;
// index
detail::write_int32(block.piece_index, i.begin);
// begin
detail::write_int32(block_offset, i.begin);
// length
detail::write_int32(block_size, i.begin);
assert(i.begin == i.end);
using namespace boost::posix_time;
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << to_simple_string(second_clock::universal_time())
<< " ==> REQUEST [ "
"piece: " << block.piece_index << " | "
2004-01-12 21:31:27 +01:00
"b: " << block.block_index << " | "
"s: " << block_offset << " | "
"l: " << block_size << " ]\n";
2004-01-08 14:03:38 +01:00
peer_request r;
r.piece = block.piece_index;
r.start = block_offset;
r.length = block_size;
assert(verify_piece(r));
2005-03-19 13:22:40 +01:00
#endif
}
m_last_piece = second_clock::universal_time();
2004-01-04 05:29:13 +01:00
send_buffer_updated();
}
void peer_connection::send_request(piece_block block)
{
INVARIANT_CHECK;
assert(m_torrent->valid_metadata());
assert(block.piece_index >= 0);
assert(block.piece_index < m_torrent->torrent_file().num_pieces());
assert(block.block_index >= 0);
assert(block.block_index < m_torrent->torrent_file().piece_size(block.piece_index));
assert(!m_torrent->picker().is_downloading(block));
m_torrent->picker().mark_as_downloading(block, m_socket->sender());
m_request_queue.push_back(block);
send_block_requests();
2004-01-04 05:29:13 +01:00
}
2004-09-12 15:53:00 +02:00
void peer_connection::send_metadata(std::pair<int, int> req)
{
2004-09-12 15:53:00 +02:00
assert(req.first >= 0);
assert(req.second > 0);
assert(req.second <= 256);
assert(req.first + req.second <= 256);
assert(m_torrent);
INVARIANT_CHECK;
// abort if the peer doesn't support the metadata extension
if (!supports_extension(extended_metadata_message)) return;
if (m_torrent->valid_metadata())
{
2004-09-12 15:53:00 +02:00
std::pair<int, int> offset
= req_to_offset(req, (int)m_torrent->metadata().size());
buffer::interval i = m_send_buffer.allocate(18 + offset.second);
// yes, we have metadata, send it
detail::write_uint32(5 + 9 + offset.second, i.begin);
detail::write_uint8(msg_extended, i.begin);
detail::write_int32(m_extension_messages[extended_metadata_message], i.begin);
// means 'data packet'
detail::write_uint8(1, i.begin);
detail::write_uint32((int)m_torrent->metadata().size(), i.begin);
detail::write_uint32(offset.first, i.begin);
std::vector<char> const& metadata = m_torrent->metadata();
2004-12-21 13:30:09 +01:00
std::copy(metadata.begin() + offset.first
, metadata.begin() + offset.first + offset.second, i.begin);
i.begin += offset.second;
assert(i.begin == i.end);
}
else
{
buffer::interval i = m_send_buffer.allocate(10);
// we don't have the metadata, reply with
// don't have-message
detail::write_uint32(1 + 4 + 1, i.begin);
detail::write_uint8(msg_extended, i.begin);
detail::write_int32(m_extension_messages[extended_metadata_message], i.begin);
// means 'have no data'
detail::write_uint8(2, i.begin);
assert(i.begin == i.end);
}
send_buffer_updated();
}
2004-09-12 15:53:00 +02:00
void peer_connection::send_metadata_request(std::pair<int, int> req)
{
2004-09-12 15:53:00 +02:00
assert(req.first >= 0);
assert(req.second > 0);
assert(req.first + req.second <= 256);
assert(m_torrent);
assert(!m_torrent->valid_metadata());
INVARIANT_CHECK;
2004-09-12 15:53:00 +02:00
int start = req.first;
int size = req.second;
// abort if the peer doesn't support the metadata extension
if (!supports_extension(extended_metadata_message)) return;
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-09-12 15:53:00 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-09-12 15:53:00 +02:00
<< " ==> METADATA_REQUEST [ start: " << req.first
<< " size: " << req.second << " ]\n";
2005-03-19 13:22:40 +01:00
#endif
2004-09-12 15:53:00 +02:00
buffer::interval i = m_send_buffer.allocate(12);
detail::write_uint32(1 + 4 + 3, i.begin);
detail::write_uint8(msg_extended, i.begin);
detail::write_int32(m_extension_messages[extended_metadata_message], i.begin);
// means 'request data'
detail::write_uint8(0, i.begin);
detail::write_uint8(start, i.begin);
detail::write_uint8(size - 1, i.begin);
assert(i.begin == i.end);
send_buffer_updated();
}
2004-01-07 01:48:02 +01:00
void peer_connection::send_chat_message(const std::string& msg)
{
INVARIANT_CHECK;
2004-01-07 01:48:02 +01:00
assert(msg.length() <= 1 * 1024);
if (!supports_extension(extended_chat_message)) return;
2004-01-07 01:48:02 +01:00
entry e(entry::dictionary_t);
2004-03-17 13:14:44 +01:00
e["msg"] = msg;
2004-01-07 01:48:02 +01:00
std::vector<char> message;
bencode(std::back_inserter(message), e);
buffer::interval i = m_send_buffer.allocate(message.size() + 9);
detail::write_uint32(1 + 4 + (int)message.size(), i.begin);
detail::write_uint8(msg_extended, i.begin);
detail::write_int32(m_extension_messages[extended_chat_message], i.begin);
std::copy(message.begin(), message.end(), i.begin);
i.begin += message.size();
assert(i.begin == i.end);
2004-01-07 01:48:02 +01:00
send_buffer_updated();
}
2004-01-04 05:29:13 +01:00
void peer_connection::send_bitfield()
{
INVARIANT_CHECK;
if (m_torrent->num_pieces() == 0) return;
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
<< " ==> BITFIELD ";
for (int i = 0; i < (int)m_have_piece.size(); ++i)
{
if (m_torrent->have_piece(i)) (*m_logger) << "1";
else (*m_logger) << "0";
}
(*m_logger) << "\n";
2005-03-19 13:22:40 +01:00
#endif
const int packet_size = ((int)m_have_piece.size() + 7) / 8 + 5;
buffer::interval i = m_send_buffer.allocate(packet_size);
detail::write_int32(packet_size - 4, i.begin);
detail::write_uint8(msg_bitfield, i.begin);
std::fill(i.begin, i.end, 0);
for (int c = 0; c < (int)m_have_piece.size(); ++c)
2004-01-04 05:29:13 +01:00
{
if (m_torrent->have_piece(c))
i.begin[c >> 3] |= 1 << (7 - (c & 7));
2004-01-04 05:29:13 +01:00
}
assert(i.end - i.begin == ((int)m_have_piece.size() + 7) / 8);
2004-01-04 05:29:13 +01:00
send_buffer_updated();
}
2004-01-04 05:29:13 +01:00
void peer_connection::send_extensions()
{
INVARIANT_CHECK;
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " ==> EXTENSIONS\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-04 05:29:13 +01:00
assert(m_supports_extensions);
2004-01-04 05:29:13 +01:00
entry extension_list(entry::dictionary_t);
2004-01-04 05:29:13 +01:00
for (int i = 0; i < num_supported_extensions; ++i)
{
2004-11-01 00:16:08 +01:00
// if this specific extension is disabled
// just don't add it to the supported set
if (!m_ses.m_extension_enabled[i]) continue;
2004-03-17 13:14:44 +01:00
extension_list[extension_names[i]] = i;
2004-01-04 05:29:13 +01:00
}
std::vector<char> msg;
bencode(std::back_inserter(msg), extension_list);
2004-01-04 06:28:24 +01:00
// make room for message
buffer::interval i = m_send_buffer.allocate(5 + msg.size());
2004-01-04 05:29:13 +01:00
// write the length of the message
detail::write_int32((int)msg.size() + 1, i.begin);
detail::write_uint8(msg_extension_list, i.begin);
std::copy(msg.begin(), msg.end(), i.begin);
i.begin += msg.size();
assert(i.begin == i.end);
2004-01-04 05:29:13 +01:00
send_buffer_updated();
}
2004-01-12 21:31:27 +01:00
void peer_connection::send_choke()
2004-01-04 05:29:13 +01:00
{
INVARIANT_CHECK;
2004-01-04 05:29:13 +01:00
if (m_choked) return;
char msg[] = {0,0,0,1,msg_choke};
m_send_buffer.insert(msg, msg + sizeof(msg));
2004-01-04 05:29:13 +01:00
m_choked = true;
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " ==> CHOKE\n";
2005-08-19 01:55:32 +02:00
#endif
#ifndef NDEBUG
using namespace boost::posix_time;
m_last_choke = second_clock::universal_time();
2005-03-19 13:22:40 +01:00
#endif
2004-01-12 21:31:27 +01:00
m_num_invalid_requests = 0;
2004-01-04 05:29:13 +01:00
m_requests.clear();
send_buffer_updated();
}
2004-01-12 21:31:27 +01:00
void peer_connection::send_unchoke()
2004-01-04 05:29:13 +01:00
{
INVARIANT_CHECK;
2005-08-19 01:55:32 +02:00
#ifndef NDEBUG
// TODO: once the policy lowers the interval for optimistic
// unchoke, increase this value that interval
// this condition cannot be guaranteed since if peers disconnect
// a new one will be unchoked ignoring when it was last choked
using namespace boost::posix_time;
//assert(second_clock::universal_time() - m_last_choke > seconds(9));
#endif
2004-01-04 05:29:13 +01:00
if (!m_choked) return;
char msg[] = {0,0,0,1,msg_unchoke};
m_send_buffer.insert(msg, msg + sizeof(msg));
2004-01-04 05:29:13 +01:00
m_choked = false;
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " ==> UNCHOKE\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-04 05:29:13 +01:00
send_buffer_updated();
}
2004-01-12 21:31:27 +01:00
void peer_connection::send_interested()
2004-01-04 05:29:13 +01:00
{
INVARIANT_CHECK;
2004-01-04 05:29:13 +01:00
if (m_interesting) return;
char msg[] = {0,0,0,1,msg_interested};
m_send_buffer.insert(msg, msg + sizeof(msg));
2004-01-04 05:29:13 +01:00
m_interesting = true;
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " ==> INTERESTED\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-04 05:29:13 +01:00
send_buffer_updated();
}
2004-01-12 21:31:27 +01:00
void peer_connection::send_not_interested()
2004-01-04 05:29:13 +01:00
{
INVARIANT_CHECK;
2004-01-04 05:29:13 +01:00
if (!m_interesting) return;
char msg[] = {0,0,0,1,msg_not_interested};
m_send_buffer.insert(msg, msg + sizeof(msg));
2004-01-04 05:29:13 +01:00
m_interesting = false;
2004-02-01 14:48:30 +01:00
2004-10-14 03:17:04 +02:00
m_became_uninteresting = second_clock::universal_time();
2004-02-01 14:48:30 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " ==> NOT_INTERESTED\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-04 05:29:13 +01:00
send_buffer_updated();
}
void peer_connection::send_have(int index)
{
assert(m_torrent->valid_metadata());
2004-01-25 23:41:55 +01:00
assert(index >= 0);
assert(index < m_torrent->torrent_file().num_pieces());
INVARIANT_CHECK;
2004-01-08 14:03:38 +01:00
// optimization, don't send have messages
// to peers that already have the piece
if (m_have_piece[index]) return;
2004-01-04 05:29:13 +01:00
const int packet_size = 9;
char msg[packet_size] = {0,0,0,5,msg_have};
char* ptr = msg + 5;
detail::write_int32(index, ptr);
m_send_buffer.insert(msg, msg + packet_size);
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " ==> HAVE [ piece: " << index << " ]\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-04 05:29:13 +01:00
send_buffer_updated();
}
size_type peer_connection::share_diff() const
2004-01-12 04:05:10 +01:00
{
float ratio = m_torrent->ratio();
// if we have an infinite ratio, just say we have downloaded
// much more than we have uploaded. And we'll keep uploading.
2004-11-18 23:33:50 +01:00
if (ratio == 0.f)
return std::numeric_limits<size_type>::max();
2004-01-12 04:05:10 +01:00
return m_free_upload
+ static_cast<size_type>(m_statistics.total_payload_download() * ratio)
2004-01-12 04:05:10 +01:00
- m_statistics.total_payload_upload();
}
2004-01-04 05:29:13 +01:00
void peer_connection::second_tick()
{
INVARIANT_CHECK;
ptime now(second_clock::universal_time());
// TODO: the timeout should be user-settable
if (!m_download_queue.empty()
&& now - m_last_piece > seconds(15))
{
// this peer isn't sending the pieces we've
// requested (this has been observed by BitComet)
// in this case we'll clear our download queue and
// re-request the blocks.
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << to_simple_string(now)
<< " *** IGNORED_REQUESTS [ " << (int)m_download_queue.size() << " ] ***\n";
#endif
piece_picker& picker = m_torrent->picker();
for (std::deque<piece_block>::const_iterator i = m_download_queue.begin()
, end(m_download_queue.end()); i != end; ++i)
{
// since this piece was skipped, clear it and allow it to
// be requested from other peers
picker.abort_download(*i);
}
for (std::deque<piece_block>::const_iterator i = m_request_queue.begin()
, end(m_request_queue.end()); i != end; ++i)
{
// since this piece was skipped, clear it and allow it to
// be requested from other peers
picker.abort_download(*i);
}
m_download_queue.clear();
m_request_queue.clear();
// this will trigger new picking of pieces
m_torrent->get_policy().unchoked(*this);
}
2004-08-08 23:26:40 +02:00
// if we don't have any metadata, and this peer
// supports the request metadata extension
// and we aren't currently waiting for a request
2004-09-12 15:53:00 +02:00
// reply. Then, send a request for some metadata.
2004-08-08 23:26:40 +02:00
if (!m_torrent->valid_metadata()
&& supports_extension(extended_metadata_message)
2004-09-12 15:53:00 +02:00
&& !m_waiting_metadata_request
&& has_metadata())
2004-08-08 23:26:40 +02:00
{
assert(m_torrent);
2004-09-12 15:53:00 +02:00
m_last_metadata_request = m_torrent->metadata_request();
send_metadata_request(m_last_metadata_request);
2004-08-08 23:26:40 +02:00
m_waiting_metadata_request = true;
m_metadata_request = now;
2004-08-08 23:26:40 +02:00
}
2004-01-04 05:29:13 +01:00
m_statistics.second_tick();
2004-03-28 19:45:37 +02:00
m_ul_bandwidth_quota.used = std::min(
(int)ceil(statistics().upload_rate())
, m_ul_bandwidth_quota.given);
2003-12-07 06:53:04 +01:00
2004-01-04 05:29:13 +01:00
// If the client sends more data
// we send it data faster, otherwise, slower.
// It will also depend on how much data the
// client has sent us. This is the mean to
2004-01-14 13:53:17 +01:00
// maintain the share ratio given by m_ratio
// with all peers.
2004-03-07 21:50:56 +01:00
2004-08-08 23:26:40 +02:00
if (m_torrent->is_seed() || is_choked() || m_torrent->ratio() == 0.0f)
2004-02-23 23:54:54 +01:00
{
// if we have downloaded more than one piece more
// than we have uploaded OR if we are a seed
// have an unlimited upload rate
2004-02-25 00:48:02 +01:00
if(!m_send_buffer.empty() || (!m_requests.empty() && !is_choked()))
2005-08-23 11:59:56 +02:00
m_ul_bandwidth_quota.max = resource_request::inf;
2004-02-25 00:48:02 +01:00
else
2004-03-28 19:45:37 +02:00
m_ul_bandwidth_quota.max = m_ul_bandwidth_quota.min;
2004-02-23 23:54:54 +01:00
}
else
{
2004-05-14 01:34:42 +02:00
size_type bias = 0x10000+2*m_torrent->block_size() + m_free_upload;
2004-02-23 23:54:54 +01:00
2004-03-07 21:50:56 +01:00
double break_even_time = 15; // seconds.
size_type have_uploaded = m_statistics.total_payload_upload();
size_type have_downloaded = m_statistics.total_payload_download();
2004-02-23 23:54:54 +01:00
double download_speed = m_statistics.download_rate();
2004-03-07 21:50:56 +01:00
size_type soon_downloaded =
have_downloaded + (size_type)(download_speed * break_even_time*1.5);
2004-03-27 23:02:31 +01:00
if(m_torrent->ratio() != 1.f)
2004-03-07 21:50:56 +01:00
soon_downloaded = (size_type)(soon_downloaded*(double)m_torrent->ratio());
double upload_speed_limit = (soon_downloaded - have_uploaded
+ bias) / break_even_time;
2004-02-23 23:54:54 +01:00
2004-03-27 23:02:31 +01:00
upload_speed_limit = std::min(upload_speed_limit,
(double)std::numeric_limits<int>::max());
2004-02-23 23:54:54 +01:00
2004-03-28 19:45:37 +02:00
m_ul_bandwidth_quota.max
= std::max((int)upload_speed_limit, m_ul_bandwidth_quota.min);
2004-02-23 23:54:54 +01:00
}
2004-09-08 01:16:11 +02:00
if (m_ul_bandwidth_quota.given > m_ul_bandwidth_quota.max)
m_ul_bandwidth_quota.given = m_ul_bandwidth_quota.max;
if (m_ul_bandwidth_quota.used > m_ul_bandwidth_quota.given)
m_ul_bandwidth_quota.used = m_ul_bandwidth_quota.given;
send_buffer_updated();
2004-02-23 23:54:54 +01:00
2004-03-07 21:50:56 +01:00
/*
size_type diff = share_diff();
2003-12-07 06:53:04 +01:00
2004-02-24 13:45:05 +01:00
enum { block_limit = 2 }; // how many blocks difference is considered unfair
2004-01-14 12:46:26 +01:00
2004-02-24 13:45:05 +01:00
// if the peer has been choked, send the current piece
2004-02-23 23:54:54 +01:00
// as fast as possible
if (diff > block_limit*m_torrent->block_size() || m_torrent->is_seed() || is_choked())
2003-12-09 19:09:34 +01:00
{
2004-01-04 05:29:13 +01:00
// if we have downloaded more than one piece more
2004-01-05 00:51:54 +01:00
// than we have uploaded OR if we are a seed
// have an unlimited upload rate
2004-03-28 19:45:37 +02:00
m_ul_bandwidth_quota.wanted = std::numeric_limits<int>::max();
2003-12-09 19:09:34 +01:00
}
else
{
2004-01-12 04:05:10 +01:00
float ratio = m_torrent->ratio();
2004-01-04 05:29:13 +01:00
// if we have downloaded too much, response with an
// upload rate of 10 kB/s more than we dowlload
// if we have uploaded too much, send with a rate of
// 10 kB/s less than we receive
int bias = 0;
2004-01-14 13:19:51 +01:00
if (diff > -block_limit*m_torrent->block_size())
2004-01-04 05:29:13 +01:00
{
2004-01-12 21:31:27 +01:00
bias = static_cast<int>(m_statistics.download_rate() * ratio) / 2;
2004-01-04 05:29:13 +01:00
if (bias < 10*1024) bias = 10*1024;
}
else
{
2004-01-12 21:31:27 +01:00
bias = -static_cast<int>(m_statistics.download_rate() * ratio) / 2;
2004-01-04 05:29:13 +01:00
}
2004-03-28 19:45:37 +02:00
m_ul_bandwidth_quota.wanted = static_cast<int>(m_statistics.download_rate()) + bias;
2004-01-12 04:05:10 +01:00
2004-01-04 05:29:13 +01:00
// the maximum send_quota given our download rate from this peer
2004-03-28 19:45:37 +02:00
if (m_ul_bandwidth_quota.wanted < 256) m_ul_bandwidth_quota.wanted = 256;
2003-12-09 19:09:34 +01:00
}
2004-03-07 21:50:56 +01:00
*/
2003-12-08 02:37:30 +01:00
}
2004-01-04 05:29:13 +01:00
// --------------------------
// RECEIVE DATA
// --------------------------
2004-01-04 05:29:13 +01:00
// throws exception when the client should be disconnected
void peer_connection::receive_data()
{
INVARIANT_CHECK;
2004-01-04 05:29:13 +01:00
assert(!m_socket->is_blocking());
assert(m_packet_size > 0);
2004-01-04 06:53:01 +01:00
assert(m_socket->is_readable());
2004-03-28 19:45:37 +02:00
assert(can_read());
assert(m_selector.is_readability_monitored(m_socket));
2005-03-12 13:18:07 +01:00
if (m_disconnecting) return;
2004-01-04 05:29:13 +01:00
for(;;)
2003-10-30 00:28:09 +01:00
{
2004-01-04 05:29:13 +01:00
assert(m_packet_size > 0);
2004-03-28 19:45:37 +02:00
int max_receive = std::min(
m_dl_bandwidth_quota.left()
, m_packet_size - m_recv_pos);
2005-03-12 13:18:07 +01:00
int received = m_socket->receive(
&m_recv_buffer[m_recv_pos], max_receive);
2004-01-04 05:29:13 +01:00
// connection closed
if (received == 0)
{
2004-01-05 02:30:34 +01:00
throw protocol_error("connection closed by remote host");
2004-01-04 05:29:13 +01:00
}
2003-10-30 00:28:09 +01:00
2004-01-04 05:29:13 +01:00
// an error
if (received < 0)
{
// would_block means that no data was ready to be received
// returns to exit the loop
if (m_socket->last_error() == socket::would_block)
return;
2004-01-04 05:29:13 +01:00
// the connection was closed
throw network_error(m_socket->last_error());
}
2004-01-04 05:29:13 +01:00
if (received > 0)
2003-10-30 00:28:09 +01:00
{
2005-04-03 17:44:17 +02:00
m_connecting = false;
2004-10-14 03:17:04 +02:00
m_last_receive = second_clock::universal_time();
2004-01-04 05:29:13 +01:00
m_recv_pos += received;
2004-03-28 19:45:37 +02:00
m_dl_bandwidth_quota.used += received;
if (!can_read())
{
assert(m_readability_monitored);
assert(m_selector.is_readability_monitored(m_socket));
m_selector.remove_readable(m_socket);
m_readability_monitored = false;
}
2003-11-09 19:17:09 +01:00
2004-01-04 05:29:13 +01:00
switch(m_state)
2003-12-08 22:59:48 +01:00
{
2004-01-04 05:29:13 +01:00
case read_protocol_length:
2003-10-30 00:28:09 +01:00
2003-12-07 06:53:04 +01:00
m_statistics.received_bytes(0, received);
2003-12-08 22:59:48 +01:00
if (m_recv_pos < m_packet_size) break;
assert(m_recv_pos == m_packet_size);
2004-01-04 05:29:13 +01:00
m_packet_size = reinterpret_cast<unsigned char&>(m_recv_buffer[0]);
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-05 02:30:34 +01:00
(*m_logger) << " protocol length: " << m_packet_size << "\n";
2005-03-19 13:22:40 +01:00
#endif
2004-06-30 09:53:42 +02:00
if (m_packet_size > 100 || m_packet_size <= 0)
2003-10-30 00:28:09 +01:00
{
2004-01-05 02:30:34 +01:00
std::stringstream s;
2004-06-30 09:53:42 +02:00
s << "incorrect protocol length ("
2004-01-05 02:30:34 +01:00
<< m_packet_size
<< ") should be 19.";
throw protocol_error(s.str());
}
2004-02-29 22:33:17 +01:00
m_state = read_protocol_string;
m_recv_buffer.resize(m_packet_size);
m_recv_pos = 0;
2004-01-04 05:29:13 +01:00
break;
2003-10-30 00:28:09 +01:00
2004-01-04 05:29:13 +01:00
case read_protocol_string:
{
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) break;
assert(m_recv_pos == m_packet_size);
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << " protocol: '" << std::string(m_recv_buffer.begin()
, m_recv_buffer.end()) << "'\n";
2004-03-01 02:03:16 +01:00
#endif
2004-01-04 05:29:13 +01:00
const char protocol_string[] = "BitTorrent protocol";
2005-03-19 13:22:40 +01:00
if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.end()
, protocol_string))
2004-01-04 05:29:13 +01:00
{
2004-02-29 22:33:17 +01:00
const char cmd[] = "version";
if (m_recv_buffer.size() == 7 && std::equal(m_recv_buffer.begin(), m_recv_buffer.end(), cmd))
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-03-01 02:03:16 +01:00
(*m_logger) << "sending libtorrent version\n";
#endif
2004-04-17 14:29:35 +02:00
m_socket->send("libtorrent version " LIBTORRENT_VERSION "\n", 27);
2004-02-29 22:33:17 +01:00
throw protocol_error("closing");
}
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-04 05:29:13 +01:00
(*m_logger) << "incorrect protocol name\n";
2004-03-01 02:03:16 +01:00
#endif
2004-01-05 02:30:34 +01:00
std::stringstream s;
s << "got invalid protocol name: '"
<< std::string(m_recv_buffer.begin(), m_recv_buffer.end())
<< "'";
throw protocol_error(s.str());
2004-01-04 05:29:13 +01:00
}
m_state = read_info_hash;
m_packet_size = 28;
m_recv_pos = 0;
m_recv_buffer.resize(28);
}
break;
2003-10-30 00:28:09 +01:00
2003-12-08 22:59:48 +01:00
2004-01-04 05:29:13 +01:00
case read_info_hash:
{
2004-01-04 05:29:13 +01:00
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) break;
assert(m_recv_pos == m_packet_size);
// the use of this bit collides with Mainline
// the new way of identifying support for the extensions
// is in the peer_id
// if ((m_recv_buffer[7] & 0x01) && m_ses.extensions_enabled())
// m_supports_extensions = true;
2003-12-08 22:59:48 +01:00
// ok, now we have got enough of the handshake. Is this connection
// attached to a torrent?
2003-12-08 22:59:48 +01:00
if (m_torrent == 0)
{
2004-01-04 05:29:13 +01:00
// now, we have to see if there's a torrent with the
// info_hash we got from the peer
sha1_hash info_hash;
std::copy(m_recv_buffer.begin() + 8, m_recv_buffer.begin() + 28, (char*)info_hash.begin());
2004-01-04 05:29:13 +01:00
m_torrent = m_ses.find_torrent(info_hash);
2005-06-13 12:58:00 +02:00
if (m_torrent && m_torrent->is_aborted()) m_torrent = 0;
2004-01-04 05:29:13 +01:00
if (m_torrent == 0)
{
// we couldn't find the torrent!
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-05 02:30:34 +01:00
(*m_logger) << " couldn't find a torrent with the given info_hash\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-05 02:30:34 +01:00
throw protocol_error("got info-hash that is not in our session");
2004-01-04 05:29:13 +01:00
}
2004-03-21 03:03:37 +01:00
if (m_torrent->is_paused())
{
// paused torrents will not accept
// incoming connections
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-03-21 03:03:37 +01:00
(*m_logger) << " rejected connection to paused torrent\n";
2005-03-19 13:22:40 +01:00
#endif
2004-03-21 03:03:37 +01:00
throw protocol_error("connection rejected by paused torrent");
}
// if the torrent isn't ready to accept
// connections yet, we'll have to wait with
// our initialization
if (m_torrent->ready_for_connections()) init();
2004-01-04 05:29:13 +01:00
// assume the other end has no pieces
// if we don't have valid metadata yet,
// leave the vector unallocated
2004-01-04 05:29:13 +01:00
std::fill(m_have_piece.begin(), m_have_piece.end(), false);
// yes, we found the torrent
// reply with our handshake
send_handshake();
send_bitfield();
}
else
2003-12-08 22:59:48 +01:00
{
2004-01-04 05:29:13 +01:00
// verify info hash
if (!std::equal(m_recv_buffer.begin()+8, m_recv_buffer.begin() + 28, (const char*)m_torrent->torrent_file().info_hash().begin()))
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-05 02:30:34 +01:00
(*m_logger) << " received invalid info_hash\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-05 02:30:34 +01:00
throw protocol_error("invalid info-hash in handshake");
2004-01-04 05:29:13 +01:00
}
}
2004-01-04 05:29:13 +01:00
m_state = read_peer_id;
m_packet_size = 20;
m_recv_pos = 0;
m_recv_buffer.resize(20);
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-05 02:30:34 +01:00
(*m_logger) << " info_hash received\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-04 05:29:13 +01:00
break;
}
2003-12-07 06:53:04 +01:00
2003-12-08 22:59:48 +01:00
2004-01-04 05:29:13 +01:00
case read_peer_id:
2003-12-22 08:14:35 +01:00
{
2004-01-04 05:29:13 +01:00
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) break;
assert(m_recv_pos == m_packet_size);
2005-06-12 02:21:37 +02:00
assert(m_packet_size == 20);
2003-12-22 08:14:35 +01:00
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
{
2004-01-04 05:29:13 +01:00
peer_id tmp;
std::copy(m_recv_buffer.begin(), m_recv_buffer.begin() + 20, (char*)tmp.begin());
std::stringstream s;
s << "received peer_id: " << tmp << " client: " << identify_client(tmp) << "\n";
2004-03-21 03:03:37 +01:00
s << "as ascii: ";
for (peer_id::iterator i = tmp.begin(); i != tmp.end(); ++i)
{
if (std::isprint(*i)) s << *i;
else s << ".";
}
s << "\n";
2004-01-04 05:29:13 +01:00
(*m_logger) << s.str();
}
2005-03-19 13:22:40 +01:00
#endif
2004-03-08 15:34:11 +01:00
std::copy(m_recv_buffer.begin(), m_recv_buffer.begin() + 20, (char*)m_peer_id.begin());
2003-12-08 22:59:48 +01:00
if (std::memcmp(&m_peer_id[17], "ext", 3) == 0)
m_supports_extensions = true;
2005-07-22 19:16:23 +02:00
// disconnect if the peer has the same peer-id as ourself
// since it most likely is ourself then
if (m_peer_id == m_ses.get_peer_id())
throw protocol_error("closing connection to ourself");
if (m_supports_extensions) send_extensions();
2004-01-08 18:03:04 +01:00
if (!m_active)
2004-01-04 05:29:13 +01:00
{
// check to make sure we don't have another connection with the same
// info_hash and peer_id. If we do. close this connection.
m_torrent->attach_peer(this);
m_attached_to_torrent = true;
2004-01-04 05:29:13 +01:00
assert(m_torrent->get_policy().has_connection(this));
}
2003-12-08 22:59:48 +01:00
m_state = read_packet_size;
m_packet_size = 4;
2004-01-04 05:29:13 +01:00
m_recv_pos = 0;
m_recv_buffer.resize(4);
break;
2003-12-08 22:59:48 +01:00
}
2004-01-04 05:29:13 +01:00
case read_packet_size:
2003-12-08 22:59:48 +01:00
{
2004-01-04 05:29:13 +01:00
m_statistics.received_bytes(0, received);
if (m_recv_pos < m_packet_size) break;
assert(m_recv_pos == m_packet_size);
// convert from big endian to native byte order
const char* ptr = &m_recv_buffer[0];
m_packet_size = detail::read_int32(ptr);
2004-01-04 05:29:13 +01:00
// don't accept packets larger than 1 MB
if (m_packet_size > 1024*1024 || m_packet_size < 0)
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-01-05 02:30:34 +01:00
(*m_logger) << " packet too large (packet_size > 1 Megabyte), abort\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-04 05:29:13 +01:00
// packet too large
2004-01-05 02:30:34 +01:00
throw protocol_error("packet > 1 MB");
2004-01-04 05:29:13 +01:00
}
if (m_packet_size == 0)
{
// keepalive message
m_state = read_packet_size;
m_packet_size = 4;
}
else
{
m_state = read_packet;
m_recv_buffer.resize(m_packet_size);
}
2003-10-30 00:28:09 +01:00
m_recv_pos = 0;
assert(m_packet_size > 0);
2004-01-04 05:29:13 +01:00
break;
}
case read_packet:
if (dispatch_message(received))
{
m_state = read_packet_size;
m_packet_size = 4;
m_recv_buffer.resize(4);
m_recv_pos = 0;
assert(m_packet_size > 0);
}
break;
}
2004-03-28 19:45:37 +02:00
// if we have used all our download quota,
// break the receive loop
if (!can_read()) break;
}
}
2004-01-04 05:29:13 +01:00
assert(m_packet_size > 0);
}
2004-03-28 19:45:37 +02:00
bool peer_connection::can_write() const
2004-01-04 05:29:13 +01:00
{
// if we have requests or pending data to be sent or announcements to be made
// we want to send data
return ((!m_requests.empty() && !m_choked)
2004-01-24 18:14:03 +01:00
|| !m_send_buffer.empty())
2004-03-28 19:45:37 +02:00
&& m_ul_bandwidth_quota.left() > 0;
}
bool peer_connection::can_read() const
{
return m_dl_bandwidth_quota.left() > 0;
2004-01-04 05:29:13 +01:00
}
// --------------------------
// SEND DATA
// --------------------------
// throws exception when the client should be disconnected
void peer_connection::send_data()
{
INVARIANT_CHECK;
2005-03-05 15:17:17 +01:00
assert(!m_disconnecting);
2004-01-04 05:29:13 +01:00
assert(m_socket->is_writable());
2004-03-28 19:45:37 +02:00
assert(can_write());
2004-01-04 05:29:13 +01:00
// only add new piece-chunks if the send buffer is small enough
// otherwise there will be no end to how large it will be!
2004-11-30 12:17:32 +01:00
// TODO: the buffer size should probably be dependent on the transfer speed
2004-01-04 05:29:13 +01:00
while (!m_requests.empty()
2004-11-30 12:17:32 +01:00
&& ((int)m_send_buffer.size() < m_torrent->block_size() * 6)
2004-01-04 05:29:13 +01:00
&& !m_choked)
{
assert(m_torrent->valid_metadata());
2004-01-04 05:29:13 +01:00
peer_request& r = m_requests.front();
assert(r.piece >= 0);
assert(r.piece < (int)m_have_piece.size());
assert(m_torrent != 0);
assert(m_torrent->have_piece(r.piece));
2004-01-12 21:31:27 +01:00
assert(r.start + r.length <= m_torrent->torrent_file().piece_size(r.piece));
assert(r.length > 0 && r.start >= 0);
2004-01-12 21:31:27 +01:00
const int packet_size = 4 + 5 + 4 + r.length;
buffer::interval i = m_send_buffer.allocate(packet_size);
detail::write_int32(packet_size-4, i.begin);
detail::write_uint8(msg_piece, i.begin);
detail::write_int32(r.piece, i.begin);
detail::write_int32(r.start, i.begin);
2004-01-12 21:31:27 +01:00
m_torrent->filesystem().read(
i.begin
2004-01-12 21:31:27 +01:00
, r.piece
, r.start
, r.length);
assert(i.begin + r.length == i.end);
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " ==> PIECE [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n";
2004-01-12 21:31:27 +01:00
#endif
2004-01-04 05:29:13 +01:00
m_payloads.push_back(range(m_send_buffer.size() - r.length, r.length));
2004-01-12 21:31:27 +01:00
m_requests.erase(m_requests.begin());
if (m_requests.empty()
&& m_num_invalid_requests > 0
&& is_peer_interested()
&& !is_seed())
2003-12-17 17:37:20 +01:00
{
2004-01-12 21:31:27 +01:00
// this will make the peer clear
// its download queue and re-request
// pieces. Hopefully it will not
// send invalid requests then
send_choke();
send_unchoke();
2003-12-17 17:37:20 +01:00
}
}
2004-01-04 05:29:13 +01:00
if (!m_announce_queue.empty())
{
assert(m_torrent->valid_metadata());
2004-01-04 05:29:13 +01:00
for (std::vector<int>::iterator i = m_announce_queue.begin();
i != m_announce_queue.end(); ++i)
2004-01-04 05:29:13 +01:00
{
send_have(*i);
}
m_announce_queue.clear();
}
2004-03-28 19:45:37 +02:00
assert(m_ul_bandwidth_quota.used <= m_ul_bandwidth_quota.given);
2003-11-09 19:17:09 +01:00
2004-01-04 05:29:13 +01:00
// send the actual buffer
if (!m_send_buffer.empty())
{
2004-03-28 19:45:37 +02:00
int amount_to_send
= std::min(m_ul_bandwidth_quota.left(), (int)m_send_buffer.size());
2003-11-05 00:27:06 +01:00
2004-03-28 19:45:37 +02:00
assert(amount_to_send > 0);
2004-01-17 21:04:19 +01:00
buffer::interval_type send_buffer = m_send_buffer.data();
2004-01-04 05:29:13 +01:00
// we have data that's scheduled for sending
int to_send = std::min(send_buffer.first.end - send_buffer.first.begin
, amount_to_send);
int sent = m_socket->send(send_buffer.first.begin, to_send);
if (sent == send_buffer.first.end - send_buffer.first.end
&& send_buffer.second.begin != send_buffer.second.end
&& to_send > sent)
{
to_send -= sent;
to_send = std::min(to_send, send_buffer.second.end
- send_buffer.second.begin);
int ret = m_socket->send(send_buffer.second.begin, to_send);
if (ret > 0) sent += ret;
}
2004-01-04 05:29:13 +01:00
if (sent > 0)
2003-11-09 19:17:09 +01:00
{
2004-03-28 19:45:37 +02:00
m_ul_bandwidth_quota.used += sent;
m_send_buffer.erase(sent);
2004-01-17 21:04:19 +01:00
// manage the payload markers
2004-01-04 05:29:13 +01:00
int amount_payload = 0;
if (!m_payloads.empty())
2003-12-08 22:59:48 +01:00
{
2004-01-04 05:29:13 +01:00
for (std::deque<range>::iterator i = m_payloads.begin();
i != m_payloads.end(); ++i)
2003-12-08 22:59:48 +01:00
{
2004-01-04 05:29:13 +01:00
i->start -= sent;
if (i->start < 0)
2003-12-08 22:59:48 +01:00
{
2004-01-04 05:29:13 +01:00
if (i->start + i->length <= 0)
{
amount_payload += i->length;
}
else
{
amount_payload += -i->start;
i->length -= -i->start;
i->start = 0;
}
2003-12-08 22:59:48 +01:00
}
}
}
// TODO: move the erasing into the loop above
2004-01-04 05:29:13 +01:00
// remove all payload ranges that has been sent
m_payloads.erase(
std::remove_if(m_payloads.begin(), m_payloads.end(), range_below_zero)
, m_payloads.end());
assert(amount_payload <= sent);
m_statistics.sent_bytes(amount_payload, sent - amount_payload);
2003-11-05 00:27:06 +01:00
}
else
2003-11-05 00:27:06 +01:00
{
2004-01-04 05:29:13 +01:00
assert(sent == -1);
throw network_error(m_socket->last_error());
2003-11-05 00:27:06 +01:00
}
2004-10-14 03:17:04 +02:00
m_last_sent = second_clock::universal_time();
2004-01-04 05:29:13 +01:00
}
2004-03-28 19:45:37 +02:00
assert(m_writability_monitored);
2004-01-04 05:29:13 +01:00
send_buffer_updated();
2003-11-05 00:27:06 +01:00
}
#ifndef NDEBUG
void peer_connection::check_invariant() const
{
2004-03-28 19:45:37 +02:00
assert(can_write() == m_selector.is_writability_monitored(m_socket));
2004-02-12 15:41:39 +01:00
/*
2004-01-28 12:37:46 +01:00
assert(m_num_pieces == std::count(
m_have_piece.begin()
, m_have_piece.end()
, true));
2004-02-12 15:41:39 +01:00
*/ }
#endif
2004-02-01 14:48:30 +01:00
bool peer_connection::has_timed_out() const
{
using namespace boost::posix_time;
ptime now(second_clock::universal_time());
2005-04-03 17:44:17 +02:00
// if the socket is still connecting, don't
// consider it timed out. Because Windows XP SP2
// may delay connection attempts.
if (m_connecting) return false;
2004-02-01 14:48:30 +01:00
// if the peer hasn't said a thing for a certain
// time, it is considered to have timed out
time_duration d;
2004-10-14 03:17:04 +02:00
d = second_clock::universal_time() - m_last_receive;
2004-02-01 14:48:30 +01:00
if (d > seconds(m_timeout)) return true;
2004-02-01 14:48:30 +01:00
// if the peer hasn't become interested and we haven't
// become interested in the peer for 10 minutes, it
2004-02-01 14:48:30 +01:00
// has also timed out.
time_duration d1;
time_duration d2;
d1 = now - m_became_uninterested;
d2 = now - m_became_uninteresting;
2004-02-01 14:48:30 +01:00
if (!m_interesting
&& !m_peer_interested
&& d1 > minutes(10)
&& d2 > minutes(10))
2004-02-01 14:48:30 +01:00
{
return true;
}
2004-02-01 14:48:30 +01:00
return false;
}
2004-01-04 05:29:13 +01:00
void peer_connection::keep_alive()
{
INVARIANT_CHECK;
2004-01-04 05:29:13 +01:00
boost::posix_time::time_duration d;
2004-10-14 03:17:04 +02:00
d = second_clock::universal_time() - m_last_sent;
2005-01-10 12:14:22 +01:00
if (d.total_seconds() < m_timeout / 2) return;
2004-01-24 18:14:03 +01:00
// we must either send a keep-alive
// message or something else.
if (m_announce_queue.empty())
2004-01-04 05:29:13 +01:00
{
char noop[] = {0,0,0,0};
m_send_buffer.insert(noop, noop + 4);
2004-10-14 03:17:04 +02:00
m_last_sent = second_clock::universal_time();
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-06-28 22:23:42 +02:00
using namespace boost::posix_time;
2004-10-14 03:17:04 +02:00
(*m_logger) << to_simple_string(second_clock::universal_time())
2004-06-28 22:23:42 +02:00
<< " ==> NOP\n";
2005-03-19 13:22:40 +01:00
#endif
2004-01-04 05:29:13 +01:00
}
2004-01-24 18:14:03 +01:00
else
{
for (std::vector<int>::iterator i = m_announce_queue.begin();
i != m_announce_queue.end(); ++i)
2004-01-24 18:14:03 +01:00
{
send_have(*i);
}
m_announce_queue.clear();
}
send_buffer_updated();
}
2004-01-12 21:31:27 +01:00
bool peer_connection::is_seed() const
{
2004-10-18 00:23:08 +02:00
// if m_num_pieces == 0, we probably doesn't have the
// metadata yet.
return m_num_pieces == (int)m_have_piece.size() && m_num_pieces > 0;
2004-01-12 21:31:27 +01:00
}
2004-03-11 15:56:48 +01:00
void peer_connection::send_buffer_updated()
{
2004-03-28 19:45:37 +02:00
if (!can_write())
2004-03-11 15:56:48 +01:00
{
2004-03-28 19:45:37 +02:00
if (m_writability_monitored)
2004-03-11 15:56:48 +01:00
{
m_selector.remove_writable(m_socket);
2004-03-28 19:45:37 +02:00
m_writability_monitored = false;
2004-03-11 15:56:48 +01:00
}
assert(!m_selector.is_writability_monitored(m_socket));
return;
}
2004-03-28 19:45:37 +02:00
assert(m_ul_bandwidth_quota.left() > 0);
assert(can_write());
if (!m_writability_monitored)
2004-03-11 15:56:48 +01:00
{
m_selector.monitor_writability(m_socket);
2004-03-28 19:45:37 +02:00
m_writability_monitored = true;
2004-03-11 15:56:48 +01:00
}
2004-03-28 19:45:37 +02:00
assert(m_writability_monitored);
2004-03-11 15:56:48 +01:00
assert(m_selector.is_writability_monitored(m_socket));
}
2004-01-04 06:53:01 +01:00
}
2005-03-19 13:22:40 +01:00