2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
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>
|
2003-10-23 01:00:57 +02:00
|
|
|
#include <iostream>
|
|
|
|
#include <iomanip>
|
2004-01-12 21:31:27 +01:00
|
|
|
#include <limits>
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#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"
|
2004-01-25 19:18:36 +01:00
|
|
|
#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"
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-03-30 01:25:13 +02:00
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
2003-10-23 01:00:57 +02:00
|
|
|
#define for if (false) {} else for
|
2004-03-30 01:25:13 +02:00
|
|
|
namespace std
|
|
|
|
{
|
|
|
|
using ::isprint;
|
|
|
|
}
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#define VERBOSE
|
|
|
|
|
2004-10-14 03:17:04 +02:00
|
|
|
using namespace boost::posix_time;
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
namespace libtorrent
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
|
|
|
|
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[] =
|
2004-06-14 01:30:42 +02:00
|
|
|
{ "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,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
&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)
|
2004-01-04 05:29:13 +01:00
|
|
|
: m_state(read_protocol_length)
|
|
|
|
, 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())
|
2004-06-14 01:30:42 +02:00
|
|
|
, 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)
|
2004-01-04 05:29:13 +01:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-03-28 19:45:37 +02:00
|
|
|
m_ul_bandwidth_quota.min = 10;
|
2004-09-08 01:16:11 +02:00
|
|
|
m_ul_bandwidth_quota.max = 400;
|
|
|
|
m_ul_bandwidth_quota.given = 400;
|
2004-03-28 19:45:37 +02:00
|
|
|
m_dl_bandwidth_quota.min = 10;
|
|
|
|
m_dl_bandwidth_quota.max = std::numeric_limits<int>::max();
|
|
|
|
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);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
m_logger = m_ses.create_log(s->sender().as_string().c_str());
|
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
send_handshake();
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// assume the other end has no pieces
|
2004-06-14 01:30:42 +02:00
|
|
|
if (m_torrent->valid_metadata())
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
: m_state(read_protocol_length)
|
|
|
|
, 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(0)
|
|
|
|
, 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())
|
2004-06-14 01:30:42 +02:00
|
|
|
, 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)
|
2004-01-04 05:29:13 +01:00
|
|
|
{
|
2004-01-25 19:18:36 +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
|
|
|
|
// to send the handshake
|
2004-03-28 19:45:37 +02:00
|
|
|
m_ul_bandwidth_quota.min = 10;
|
|
|
|
m_ul_bandwidth_quota.max = 400;
|
|
|
|
m_ul_bandwidth_quota.given = 400;
|
|
|
|
|
|
|
|
m_dl_bandwidth_quota.min = 10;
|
|
|
|
m_dl_bandwidth_quota.max = std::numeric_limits<int>::max();
|
|
|
|
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);
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
m_logger = m_ses.create_log(s->sender().as_string().c_str());
|
|
|
|
#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);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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);
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
void peer_connection::init()
|
|
|
|
{
|
|
|
|
assert(m_torrent);
|
|
|
|
assert(m_torrent->valid_metadata());
|
|
|
|
|
|
|
|
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();
|
|
|
|
i != piece_list.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
int index = *i;
|
|
|
|
m_torrent->peer_has(index);
|
|
|
|
if (!m_torrent->have_piece(index))
|
|
|
|
interesting = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (piece_list.size() == m_have_piece.size())
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
(*m_logger) << " *** THIS IS A SEED ***\n";
|
|
|
|
#endif
|
|
|
|
// if we're a seed too, disconnect
|
|
|
|
if (m_torrent->is_seed())
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
(*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()
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2005-01-10 12:14:22 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
using namespace boost::posix_time;
|
|
|
|
if (m_logger)
|
|
|
|
{
|
|
|
|
(*m_logger) << to_simple_string(second_clock::universal_time())
|
|
|
|
<< " *** CONNECTION CLOSED\n";
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
m_selector.remove(m_socket);
|
|
|
|
if (m_attached_to_torrent)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-04 05:29:13 +01:00
|
|
|
assert(m_torrent != 0);
|
|
|
|
m_torrent->remove_peer(this);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-01-04 05:29:13 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-03-11 15:56:48 +01:00
|
|
|
void peer_connection::announce_piece(int index)
|
|
|
|
{
|
|
|
|
assert(m_torrent);
|
2004-06-14 01:30:42 +02:00
|
|
|
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);
|
2004-06-14 01:30:42 +02:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::deque<piece_block>& peer_connection::download_queue() const
|
|
|
|
{
|
|
|
|
return m_download_queue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::deque<peer_request>& peer_connection::upload_queue() const
|
|
|
|
{
|
|
|
|
return m_requests;
|
|
|
|
}
|
|
|
|
|
|
|
|
void peer_connection::add_stat(size_type downloaded, size_type uploaded)
|
|
|
|
{
|
|
|
|
m_statistics.add_stat(downloaded, uploaded);
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::vector<bool>& peer_connection::get_bitfield() const
|
|
|
|
{
|
|
|
|
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()
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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;
|
|
|
|
int pos = 1;
|
|
|
|
|
|
|
|
m_send_buffer.resize(1 + string_len + 8 + 20 + 20);
|
|
|
|
|
|
|
|
// length of version string
|
|
|
|
m_send_buffer[0] = string_len;
|
|
|
|
|
|
|
|
// version string itself
|
|
|
|
std::copy(
|
|
|
|
version_string
|
|
|
|
, version_string+string_len
|
|
|
|
, m_send_buffer.begin()+pos);
|
|
|
|
pos += string_len;
|
|
|
|
|
|
|
|
// 8 zeroes
|
|
|
|
std::fill(
|
|
|
|
m_send_buffer.begin() + pos
|
|
|
|
, m_send_buffer.begin() + pos + 8
|
|
|
|
, 0);
|
|
|
|
// indicate that we support the extension protocol
|
2004-11-01 00:16:08 +01:00
|
|
|
|
|
|
|
if (m_ses.extensions_enabled())
|
|
|
|
m_send_buffer[pos+7] = 0x01;
|
2004-01-04 05:29:13 +01:00
|
|
|
pos += 8;
|
|
|
|
|
|
|
|
// info hash
|
|
|
|
std::copy(
|
|
|
|
m_torrent->torrent_file().info_hash().begin()
|
|
|
|
, m_torrent->torrent_file().info_hash().end()
|
|
|
|
, m_send_buffer.begin() + pos);
|
|
|
|
pos += 20;
|
|
|
|
|
|
|
|
// peer id
|
|
|
|
std::copy(
|
|
|
|
m_ses.get_peer_id().begin()
|
|
|
|
, m_ses.get_peer_id().end()
|
|
|
|
, m_send_buffer.begin() + pos);
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
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";
|
2004-01-04 05:29:13 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
send_buffer_updated();
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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
|
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
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;
|
2004-01-22 23:45:52 +01:00
|
|
|
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-10-23 01:00:57 +02:00
|
|
|
|
2003-12-07 06:53:04 +01:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
// message handlers
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
// -----------------------------
|
|
|
|
// ----------- CHOKE -----------
|
|
|
|
// -----------------------------
|
|
|
|
|
|
|
|
void peer_connection::on_choke(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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;
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
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();
|
|
|
|
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
|
|
|
}
|
2004-01-05 00:51:54 +01:00
|
|
|
m_download_queue.clear();
|
|
|
|
#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 ----------
|
|
|
|
// -----------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
void peer_connection::on_unchoke(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
#ifndef NDEBUG
|
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);
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
// -----------------------------
|
|
|
|
// -------- INTERESTED ---------
|
|
|
|
// -----------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
void peer_connection::on_interested(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
#ifndef NDEBUG
|
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 -------
|
|
|
|
// -----------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
void peer_connection::on_not_interested(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
#ifndef NDEBUG
|
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);
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
// -----------------------------
|
|
|
|
// ----------- HAVE ------------
|
|
|
|
// -----------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
void peer_connection::on_have(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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];
|
2004-01-22 23:45:52 +01:00
|
|
|
int index = detail::read_int32(ptr);
|
2004-01-05 00:51:54 +01:00
|
|
|
// if we got an invalid message, abort
|
2004-01-25 19:18:36 +01:00
|
|
|
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
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
#ifndef NDEBUG
|
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])
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
2004-01-05 02:30:34 +01:00
|
|
|
(*m_logger) << " oops.. we already knew that: " << 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
|
|
|
|
2004-06-14 01:30:42 +02: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->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)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-26 01:21:12 +01:00
|
|
|
assert(received > 0);
|
2004-06-14 01:30:42 +02:00
|
|
|
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-06-14 01:30:42 +02:00
|
|
|
|
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
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
#ifndef NDEBUG
|
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
|
2004-06-14 01:30:42 +02:00
|
|
|
|
|
|
|
// 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;
|
2004-01-25 19:18:36 +01:00
|
|
|
for (int i = 0; i < (int)m_have_piece.size(); ++i)
|
2004-01-05 00:51:54 +01:00
|
|
|
{
|
2004-01-25 19:18:36 +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])
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
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)
|
|
|
|
{
|
|
|
|
int index = *i;
|
|
|
|
m_torrent->peer_has(index);
|
|
|
|
if (!m_torrent->have_piece(index))
|
|
|
|
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
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
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())
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
|
|
|
(*m_logger) << " we're also a seed, disconnecting\n";
|
|
|
|
#endif
|
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)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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];
|
2004-01-22 23:45:52 +01:00
|
|
|
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
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
if (!m_torrent->valid_metadata())
|
|
|
|
{
|
|
|
|
// if we don't have valid metadata yet,
|
|
|
|
// we shouldn't get a request
|
|
|
|
#ifndef NDEBUG
|
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 [ "
|
2004-06-14 01:30:42 +02: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
|
|
|
|
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.
|
2004-06-14 01:30:42 +02:00
|
|
|
#ifndef NDEBUG
|
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 [ "
|
2004-06-14 01:30:42 +02: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-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();
|
|
|
|
#ifndef NDEBUG
|
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
|
|
|
|
{
|
2004-01-05 02:30:34 +01:00
|
|
|
#ifndef NDEBUG
|
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
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
// -----------------------------
|
|
|
|
// ----------- PIECE -----------
|
|
|
|
// -----------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
void peer_connection::on_piece(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-26 01:21:12 +01:00
|
|
|
assert(received > 0);
|
2004-01-12 21:31:27 +01:00
|
|
|
if (m_recv_pos - received <= 9)
|
|
|
|
{
|
2004-10-14 03:17:04 +02:00
|
|
|
m_last_piece = second_clock::universal_time();
|
2004-01-12 21:31:27 +01:00
|
|
|
}
|
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));
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
if (m_recv_pos < m_packet_size) return;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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;
|
2004-01-22 23:45:52 +01:00
|
|
|
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
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
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-01-05 00:51:54 +01:00
|
|
|
#ifndef NDEBUG
|
2004-06-28 22:23:42 +02:00
|
|
|
using namespace boost::posix_time;
|
2004-01-12 21:31:27 +01:00
|
|
|
for (std::deque<piece_block>::iterator i = m_download_queue.begin();
|
|
|
|
i != m_download_queue.end();
|
|
|
|
++i)
|
|
|
|
{
|
|
|
|
if (i->piece_index == p.piece
|
|
|
|
&& i->block_index == p.start / m_torrent->block_size())
|
|
|
|
break;
|
|
|
|
|
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
|
|
|
<< " *** SKIPPED_PIECE [ piece: " << i->piece_index << " | "
|
2004-01-13 04:08:59 +01:00
|
|
|
"b: " << i->block_index << " ] ***\n";
|
2004-01-12 21:31:27 +01:00
|
|
|
}
|
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: " << p.piece << " | "
|
2004-01-12 21:31:27 +01:00
|
|
|
"b: " << p.start / m_torrent->block_size() << " | "
|
2004-01-08 14:03:38 +01:00
|
|
|
"s: " << p.start << " | "
|
|
|
|
"l: " << p.length << " ]\n";
|
2004-01-05 00:51:54 +01:00
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
piece_picker& picker = m_torrent->picker();
|
2004-01-08 14:03:38 +01:00
|
|
|
piece_block block_finished(p.piece, p.start / m_torrent->block_size());
|
2004-01-04 05:29:13 +01:00
|
|
|
|
2004-01-13 04:08:59 +01:00
|
|
|
// if the block we got is already finished, then ignore it
|
|
|
|
if (picker.is_finished(block_finished)) return;
|
|
|
|
|
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
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
if (b != m_download_queue.end())
|
|
|
|
{
|
|
|
|
// pop the request that just finished
|
|
|
|
// from the download queue
|
|
|
|
m_download_queue.erase(b);
|
|
|
|
}
|
|
|
|
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
|
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
|
|
(*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
|
|
|
|
2004-01-08 14:03:38 +01:00
|
|
|
m_torrent->filesystem().write(&m_recv_buffer[9], p.piece, p.start, p.length);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-19 20:36:55 +01:00
|
|
|
bool was_seed = m_torrent->is_seed();
|
2004-01-18 11:22:18 +01:00
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
picker.mark_as_finished(block_finished, m_socket->sender());
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
m_torrent->get_policy().block_finished(*this, block_finished);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-08 14:03:38 +01:00
|
|
|
m_torrent->announce_piece(p.piece);
|
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);
|
2004-01-21 14:16:11 +01:00
|
|
|
m_torrent->completed();
|
2004-01-18 11:22:18 +01:00
|
|
|
}
|
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
// -----------------------------
|
|
|
|
// ---------- CANCEL -----------
|
|
|
|
// -----------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
void peer_connection::on_cancel(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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];
|
2004-01-22 23:45:52 +01:00
|
|
|
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
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
#ifndef NDEBUG
|
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
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
// -----------------------------
|
|
|
|
// ------ EXTENSION LIST -------
|
|
|
|
// -----------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
void peer_connection::on_extension_list(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
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());
|
2004-04-17 14:29:35 +02:00
|
|
|
#ifndef NDEBUG
|
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)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-03-17 13:14:44 +01:00
|
|
|
m_extension_messages[i] = (int)f->integer();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-01-05 00:51:54 +01:00
|
|
|
}
|
2004-01-12 04:05:10 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
(*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
|
|
|
}
|
2004-01-25 19:18:36 +01:00
|
|
|
catch(invalid_encoding&)
|
2004-01-05 00:51:54 +01:00
|
|
|
{
|
|
|
|
throw protocol_error("'extensions' packet contains invalid bencoding");
|
|
|
|
}
|
2004-01-25 19:18:36 +01:00
|
|
|
catch(type_error&)
|
2004-01-05 00:51:54 +01:00
|
|
|
{
|
|
|
|
throw protocol_error("'extensions' packet contains incorrect types");
|
|
|
|
}
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
// -----------------------------
|
|
|
|
// --------- EXTENDED ----------
|
|
|
|
// -----------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
void peer_connection::on_extended(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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];
|
2004-01-22 23:45:52 +01:00
|
|
|
int extended_id = detail::read_int32(ptr);
|
2004-01-07 01:48:02 +01:00
|
|
|
|
2004-11-01 00:16:08 +01:00
|
|
|
if (!m_ses.m_extension_enabled[extended_id])
|
|
|
|
throw protocol_error("'extended' message using disabled extension");
|
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
switch (extended_id)
|
|
|
|
{
|
|
|
|
case extended_chat_message:
|
2004-06-14 01:30:42 +02:00
|
|
|
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:
|
|
|
|
throw protocol_error("unknown extended message 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
|
|
|
{
|
2004-06-14 01:30:42 +02: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
|
|
|
|
2004-06-14 01:30:42 +02: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
|
|
|
|
2004-06-14 01:30:42 +02: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");
|
|
|
|
|
|
|
|
if (m_recv_pos < m_packet_size) return;
|
|
|
|
|
|
|
|
std::vector<char>::iterator ptr = m_recv_buffer.begin()+5;
|
|
|
|
int type = detail::read_uint8(ptr);
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case 0: // request
|
|
|
|
{
|
|
|
|
int start = detail::read_uint8(ptr);
|
2004-06-15 01:10:16 +02:00
|
|
|
int size = detail::read_uint8(ptr) + 1;
|
2004-06-14 01:30:42 +02:00
|
|
|
|
2004-06-15 01:10:16 +02:00
|
|
|
if (m_packet_size != 8)
|
2004-01-07 01:48:02 +01:00
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
// invalid metadata request
|
|
|
|
throw protocol_error("invalid metadata request");
|
2004-01-07 01:48:02 +01:00
|
|
|
}
|
2004-06-14 01:30:42 +02:00
|
|
|
|
2004-09-12 15:53:00 +02:00
|
|
|
send_metadata(std::make_pair(start, size));
|
2004-01-07 01:48:02 +01:00
|
|
|
}
|
2004-06-14 01:30:42 +02:00
|
|
|
break;
|
|
|
|
case 1: // data
|
|
|
|
{
|
|
|
|
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");
|
|
|
|
if (offset > total_size)
|
|
|
|
throw protocol_error("invalid metadata offset");
|
|
|
|
if (offset + data_size > total_size)
|
|
|
|
throw protocol_error("invalid metadata message");
|
|
|
|
|
2004-09-12 15:53:00 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
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;
|
2004-06-14 01:30:42 +02:00
|
|
|
m_torrent->received_metadata(&m_recv_buffer[5+9], data_size, offset, total_size);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2: // have no data
|
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;
|
2004-06-14 01:30:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2004-01-07 01:48:02 +01:00
|
|
|
|
2004-06-14 01:30:42 +02: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
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
// -----------------------------
|
|
|
|
// ------- LISTEN PORT ---------
|
|
|
|
// -----------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
void peer_connection::on_listen_port()
|
|
|
|
{
|
2004-06-28 22:23:42 +02:00
|
|
|
using namespace boost::posix_time;
|
2004-06-14 01:30:42 +02:00
|
|
|
assert(m_torrent);
|
2004-01-04 05:29:13 +01:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
if (m_packet_size != 7)
|
|
|
|
throw protocol_error("invalid listen_port message");
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
if (is_local())
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
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";
|
2004-06-14 01:30:42 +02:00
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
2004-01-04 05:29:13 +01:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
const char* ptr = &m_recv_buffer[5];
|
|
|
|
unsigned short port = detail::read_uint16(ptr);
|
2004-01-04 05:29:13 +01:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
#ifndef NDEBUG
|
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";
|
2004-06-14 01:30:42 +02:00
|
|
|
#endif
|
2004-01-04 05:29:13 +01:00
|
|
|
|
2004-06-14 01:30:42 +02: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;
|
2004-01-20 12:01:50 +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());
|
|
|
|
assert(std::find(m_ses.m_disconnect_peer.begin(), m_ses.m_disconnect_peer.end(), i) == m_ses.m_disconnect_peer.end());
|
|
|
|
m_ses.m_disconnect_peer.push_back(i);
|
|
|
|
}
|
2004-01-05 00:51:54 +01:00
|
|
|
|
|
|
|
bool peer_connection::dispatch_message(int received)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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)
|
|
|
|
{
|
2004-01-04 05:35:25 +01:00
|
|
|
throw protocol_error("unknown message id");
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
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;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
void peer_connection::send_cancel(piece_block block)
|
2004-01-04 05:29:13 +01:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
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
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
std::deque<piece_block>::iterator i
|
|
|
|
= std::find(m_download_queue.begin(), m_download_queue.end(), block);
|
|
|
|
assert(i != m_download_queue.end());
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
m_download_queue.erase(i);
|
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
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
std::size_t start_offset = m_send_buffer.size();
|
|
|
|
m_send_buffer.resize(start_offset + 17);
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
std::copy(buf, buf + 5, m_send_buffer.begin()+start_offset);
|
|
|
|
start_offset += 5;
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
char* ptr = &m_send_buffer[start_offset];
|
2003-11-05 00:27:06 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// index
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(block.piece_index, ptr);
|
2004-01-04 05:29:13 +01:00
|
|
|
// begin
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(block_offset, ptr);
|
2004-01-04 05:29:13 +01:00
|
|
|
// length
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(block_size, ptr);
|
2004-01-02 21:46:24 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
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: " << block.piece_index << " | s: " << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n";
|
2004-01-04 05:29:13 +01:00
|
|
|
#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
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
void peer_connection::send_request(piece_block block)
|
2004-01-04 05:29:13 +01:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
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-10-23 01:00:57 +02:00
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
m_torrent->picker().mark_as_downloading(block, m_socket->sender());
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
m_download_queue.push_back(block);
|
2003-10-23 01:00:57 +02: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-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
char buf[] = {0,0,0,13, msg_request};
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
std::size_t start_offset = m_send_buffer.size();
|
|
|
|
m_send_buffer.resize(start_offset + 17);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
std::copy(buf, buf + 5, m_send_buffer.begin()+start_offset);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
char* ptr = &m_send_buffer[start_offset+5];
|
|
|
|
// index
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(block.piece_index, ptr);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// begin
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(block_offset, ptr);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// length
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(block_size, ptr);
|
2004-01-02 21:46:24 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
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 [ "
|
2004-01-12 21:31:27 +01:00
|
|
|
"piece: " << block.piece_index << " | "
|
|
|
|
"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));
|
2004-01-04 05:29:13 +01:00
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
send_buffer_updated();
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-09-12 15:53:00 +02:00
|
|
|
void peer_connection::send_metadata(std::pair<int, int> req)
|
2004-06-14 01:30:42 +02:00
|
|
|
{
|
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);
|
2004-06-14 01:30:42 +02:00
|
|
|
assert(m_torrent);
|
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
|
|
|
// abort if the peer doesn't support the metadata extension
|
|
|
|
if (!supports_extension(extended_metadata_message)) return;
|
|
|
|
|
|
|
|
std::back_insert_iterator<std::vector<char> > ptr(m_send_buffer);
|
|
|
|
|
|
|
|
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());
|
2004-06-14 01:30:42 +02:00
|
|
|
|
|
|
|
// yes, we have metadata, send it
|
2004-09-12 15:53:00 +02:00
|
|
|
detail::write_uint32(5 + 9 + offset.second, ptr);
|
2004-06-14 01:30:42 +02:00
|
|
|
detail::write_uint8(msg_extended, ptr);
|
|
|
|
detail::write_int32(m_extension_messages[extended_metadata_message], ptr);
|
|
|
|
// means 'data packet'
|
|
|
|
detail::write_uint8(1, ptr);
|
|
|
|
detail::write_uint32((int)m_torrent->metadata().size(), ptr);
|
2004-09-12 15:53:00 +02:00
|
|
|
detail::write_uint32(offset.first, ptr);
|
2004-06-14 01:30:42 +02:00
|
|
|
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, ptr);
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// we don't have the metadata, reply with
|
|
|
|
// don't have-message
|
|
|
|
detail::write_uint32(1 + 4 + 1, ptr);
|
|
|
|
detail::write_uint8(msg_extended, ptr);
|
|
|
|
detail::write_int32(m_extension_messages[extended_metadata_message], ptr);
|
|
|
|
// means 'have no data'
|
|
|
|
detail::write_uint8(2, ptr);
|
|
|
|
}
|
|
|
|
send_buffer_updated();
|
|
|
|
}
|
|
|
|
|
2004-09-12 15:53:00 +02:00
|
|
|
void peer_connection::send_metadata_request(std::pair<int, int> req)
|
2004-06-14 01:30:42 +02:00
|
|
|
{
|
2004-09-12 15:53:00 +02:00
|
|
|
assert(req.first >= 0);
|
|
|
|
assert(req.second > 0);
|
|
|
|
assert(req.first + req.second <= 256);
|
2004-06-14 01:30:42 +02:00
|
|
|
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;
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
// abort if the peer doesn't support the metadata extension
|
|
|
|
if (!supports_extension(extended_metadata_message)) return;
|
|
|
|
|
2004-09-12 15:53:00 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
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";
|
|
|
|
#endif
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
std::back_insert_iterator<std::vector<char> > ptr(m_send_buffer);
|
|
|
|
|
|
|
|
detail::write_uint32(1 + 4 + 3, ptr);
|
|
|
|
detail::write_uint8(msg_extended, ptr);
|
|
|
|
detail::write_int32(m_extension_messages[extended_metadata_message], ptr);
|
|
|
|
// means 'request data'
|
|
|
|
detail::write_uint8(0, ptr);
|
|
|
|
detail::write_uint8(start, ptr);
|
2004-06-15 01:10:16 +02:00
|
|
|
detail::write_uint8(size - 1, ptr);
|
2004-06-14 01:30:42 +02:00
|
|
|
send_buffer_updated();
|
|
|
|
}
|
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
void peer_connection::send_chat_message(const std::string& msg)
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-07 01:48:02 +01:00
|
|
|
assert(msg.length() <= 1 * 1024);
|
2004-06-14 01:30:42 +02:00
|
|
|
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);
|
|
|
|
std::back_insert_iterator<std::vector<char> > ptr(m_send_buffer);
|
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
detail::write_uint32(1 + 4 + (int)message.size(), ptr);
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_uint8(msg_extended, ptr);
|
|
|
|
detail::write_int32(m_extension_messages[extended_chat_message], ptr);
|
2004-01-07 01:48:02 +01:00
|
|
|
std::copy(message.begin(), message.end(), ptr);
|
|
|
|
send_buffer_updated();
|
|
|
|
}
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
void peer_connection::send_bitfield()
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
if (m_torrent->num_pieces() == 0) return;
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
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-04 05:29:13 +01:00
|
|
|
#endif
|
2004-01-25 19:18:36 +01:00
|
|
|
const int packet_size = ((int)m_have_piece.size() + 7) / 8 + 5;
|
|
|
|
const int old_size = (int)m_send_buffer.size();
|
2004-01-04 05:29:13 +01:00
|
|
|
m_send_buffer.resize(old_size + packet_size);
|
2004-01-25 19:18:36 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
char* ptr = &m_send_buffer[old_size];
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(packet_size - 4, ptr);
|
2004-01-25 19:18:36 +01:00
|
|
|
detail::write_uint8(msg_bitfield, ptr);
|
|
|
|
|
|
|
|
std::fill(m_send_buffer.begin() + old_size + 5, m_send_buffer.end(), 0);
|
|
|
|
for (int i = 0; i < (int)m_have_piece.size(); ++i)
|
2004-01-04 05:29:13 +01:00
|
|
|
{
|
|
|
|
if (m_torrent->have_piece(i))
|
|
|
|
m_send_buffer[old_size + 5 + (i>>3)] |= 1 << (7 - (i&7));
|
|
|
|
}
|
|
|
|
send_buffer_updated();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
void peer_connection::send_extensions()
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
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";
|
2004-01-04 05:29:13 +01:00
|
|
|
#endif
|
|
|
|
assert(m_supports_extensions);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
entry extension_list(entry::dictionary_t);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// make room for message size
|
2004-01-25 19:18:36 +01:00
|
|
|
const int msg_size_pos = (int)m_send_buffer.size();
|
2004-01-04 05:29:13 +01:00
|
|
|
m_send_buffer.resize(msg_size_pos + 4);
|
|
|
|
|
2004-01-05 00:51:54 +01:00
|
|
|
m_send_buffer.push_back(msg_extension_list);
|
2004-01-04 06:28:24 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
bencode(std::back_inserter(m_send_buffer), extension_list);
|
|
|
|
|
|
|
|
// write the length of the message
|
|
|
|
char* ptr = &m_send_buffer[msg_size_pos];
|
2004-01-25 19:18:36 +01:00
|
|
|
detail::write_int32((int)m_send_buffer.size() - msg_size_pos - 4, ptr);
|
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
|
|
|
{
|
2004-01-25 19:18:36 +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(m_send_buffer.end(), msg, msg+sizeof(msg));
|
|
|
|
m_choked = true;
|
|
|
|
#ifndef NDEBUG
|
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-04 05:29:13 +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
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
if (!m_choked) return;
|
|
|
|
char msg[] = {0,0,0,1,msg_unchoke};
|
|
|
|
m_send_buffer.insert(m_send_buffer.end(), msg, msg+sizeof(msg));
|
|
|
|
m_choked = false;
|
|
|
|
#ifndef NDEBUG
|
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-04 05:29:13 +01:00
|
|
|
#endif
|
|
|
|
send_buffer_updated();
|
|
|
|
}
|
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
void peer_connection::send_interested()
|
2004-01-04 05:29:13 +01:00
|
|
|
{
|
2004-01-25 19:18:36 +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(m_send_buffer.end(), msg, msg+sizeof(msg));
|
|
|
|
m_interesting = true;
|
|
|
|
#ifndef NDEBUG
|
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-04 05:29:13 +01:00
|
|
|
#endif
|
|
|
|
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
|
|
|
{
|
2004-01-25 19:18:36 +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(m_send_buffer.end(), msg, msg+sizeof(msg));
|
|
|
|
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
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
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-04 05:29:13 +01:00
|
|
|
#endif
|
|
|
|
send_buffer_updated();
|
|
|
|
}
|
|
|
|
|
|
|
|
void peer_connection::send_have(int index)
|
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
assert(m_torrent->valid_metadata());
|
2004-01-25 23:41:55 +01:00
|
|
|
assert(index >= 0);
|
|
|
|
assert(index < m_torrent->torrent_file().num_pieces());
|
2004-01-25 19:18:36 +01:00
|
|
|
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;
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(index, ptr);
|
2004-01-04 05:29:13 +01:00
|
|
|
m_send_buffer.insert(m_send_buffer.end(), msg, msg + packet_size);
|
|
|
|
#ifndef NDEBUG
|
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-04 05:29:13 +01:00
|
|
|
#endif
|
|
|
|
send_buffer_updated();
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
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
|
2004-01-25 19:18:36 +01:00
|
|
|
+ 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()
|
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
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;
|
2004-10-14 03:17:04 +02:00
|
|
|
m_metadata_request = second_clock::universal_time();
|
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()))
|
2004-03-28 19:45:37 +02:00
|
|
|
m_ul_bandwidth_quota.max = std::numeric_limits<int>::max();
|
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
|
|
|
/*
|
2004-01-25 19:18:36 +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
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// --------------------------
|
|
|
|
// RECEIVE DATA
|
|
|
|
// --------------------------
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// throws exception when the client should be disconnected
|
|
|
|
void peer_connection::receive_data()
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
assert(!m_socket->is_blocking());
|
2003-12-01 06:01:40 +01:00
|
|
|
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));
|
|
|
|
|
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);
|
|
|
|
int received = m_socket->receive(&m_recv_buffer[m_recv_pos], max_receive);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// the connection was closed
|
|
|
|
throw network_error(m_socket->last_error());
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
if (received > 0)
|
2003-10-30 00:28:09 +01:00
|
|
|
{
|
2004-10-14 03:17:04 +02:00
|
|
|
m_last_receive = second_clock::universal_time();
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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]);
|
|
|
|
#ifndef NDEBUG
|
2004-01-05 02:30:34 +01:00
|
|
|
(*m_logger) << " protocol length: " << m_packet_size << "\n";
|
2004-01-04 05:29:13 +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());
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
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-23 01:00:57 +02:00
|
|
|
|
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);
|
2004-03-01 02:03:16 +01:00
|
|
|
#ifndef NDEBUG
|
2004-01-05 02:30:34 +01:00
|
|
|
(*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";
|
|
|
|
if (!std::equal(m_recv_buffer.begin(), m_recv_buffer.end(), protocol_string))
|
|
|
|
{
|
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))
|
|
|
|
{
|
2004-03-01 02:03:16 +01:00
|
|
|
#ifndef NDEBUG
|
|
|
|
(*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");
|
|
|
|
}
|
2004-03-01 02:03:16 +01:00
|
|
|
#ifndef NDEBUG
|
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:
|
2003-10-23 01:00:57 +02: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);
|
|
|
|
// ok, now we have got enough of the handshake. Is this connection
|
|
|
|
// attached to a torrent?
|
|
|
|
|
2004-11-01 00:16:08 +01:00
|
|
|
if ((m_recv_buffer[7] & 0x01) && m_ses.extensions_enabled())
|
2004-06-14 01:30:42 +02:00
|
|
|
m_supports_extensions = true;
|
2003-12-08 22:59:48 +01:00
|
|
|
|
|
|
|
if (m_torrent == 0)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2003-12-01 06:01:40 +01:00
|
|
|
|
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());
|
|
|
|
|
|
|
|
m_torrent = m_ses.find_torrent(info_hash);
|
|
|
|
if (m_torrent == 0)
|
|
|
|
{
|
|
|
|
// we couldn't find the torrent!
|
|
|
|
#ifndef NDEBUG
|
2004-01-05 02:30:34 +01:00
|
|
|
(*m_logger) << " couldn't find a torrent with the given info_hash\n";
|
2004-01-04 05:29:13 +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
|
|
|
}
|
2003-12-01 06:01:40 +01:00
|
|
|
|
2004-03-21 03:03:37 +01:00
|
|
|
if (m_torrent->is_paused())
|
|
|
|
{
|
|
|
|
// paused torrents will not accept
|
|
|
|
// incoming connections
|
|
|
|
#ifndef NDEBUG
|
|
|
|
(*m_logger) << " rejected connection to paused torrent\n";
|
|
|
|
#endif
|
|
|
|
throw protocol_error("connection rejected by paused torrent");
|
|
|
|
}
|
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
if (m_torrent->valid_metadata()) init();
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// assume the other end has no pieces
|
2004-06-14 01:30:42 +02:00
|
|
|
// 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()))
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
2004-01-05 02:30:34 +01:00
|
|
|
(*m_logger) << " received invalid info_hash\n";
|
2004-01-04 05:29:13 +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
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
if (m_supports_extensions) send_extensions();
|
2003-10-23 01:00:57 +02: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);
|
|
|
|
#ifndef NDEBUG
|
2004-01-05 02:30:34 +01:00
|
|
|
(*m_logger) << " info_hash received\n";
|
2004-01-04 05:29:13 +01:00
|
|
|
#endif
|
|
|
|
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);
|
2003-12-22 08:14:35 +01:00
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
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();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-01-04 05:29:13 +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
|
|
|
|
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_attached_to_torrent = true;
|
|
|
|
m_torrent->attach_peer(this);
|
|
|
|
assert(m_torrent->get_policy().has_connection(this));
|
|
|
|
}
|
2003-11-23 04:00:45 +01:00
|
|
|
|
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];
|
2004-01-22 23:45:52 +01:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
#ifndef NDEBUG
|
2004-01-05 02:30:34 +01:00
|
|
|
(*m_logger) << " packet too large (packet_size > 1 Megabyte), abort\n";
|
2004-01-04 05:29:13 +01:00
|
|
|
#endif
|
|
|
|
// 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;
|
2003-12-01 06:01:40 +01:00
|
|
|
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;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2004-03-28 19:45:37 +02:00
|
|
|
|
|
|
|
// if we have used all our download quota,
|
|
|
|
// break the receive loop
|
|
|
|
if (!can_read()) break;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
2004-01-04 05:29:13 +01:00
|
|
|
assert(m_packet_size > 0);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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()
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
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)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
assert(m_torrent->valid_metadata());
|
2004-01-04 05:29:13 +01:00
|
|
|
peer_request& r = m_requests.front();
|
|
|
|
|
2004-01-25 19:18:36 +01:00
|
|
|
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);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
#ifndef NDEBUG
|
2004-01-16 00:57:11 +01:00
|
|
|
// assert(m_torrent->verify_piece(r.piece) && "internal error");
|
2004-01-12 21:31:27 +01:00
|
|
|
#endif
|
2004-01-25 19:18:36 +01:00
|
|
|
const int send_buffer_offset = (int)m_send_buffer.size();
|
2004-01-12 21:31:27 +01:00
|
|
|
const int packet_size = 4 + 5 + 4 + r.length;
|
|
|
|
m_send_buffer.resize(send_buffer_offset + packet_size);
|
|
|
|
char* ptr = &m_send_buffer[send_buffer_offset];
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(packet_size-4, ptr);
|
2004-01-12 21:31:27 +01:00
|
|
|
*ptr = msg_piece; ++ptr;
|
2004-01-22 23:45:52 +01:00
|
|
|
detail::write_int32(r.piece, ptr);
|
|
|
|
detail::write_int32(r.start, ptr);
|
2004-01-12 21:31:27 +01:00
|
|
|
|
|
|
|
m_torrent->filesystem().read(
|
|
|
|
&m_send_buffer[send_buffer_offset+13]
|
|
|
|
, r.piece
|
|
|
|
, r.start
|
|
|
|
, r.length);
|
|
|
|
#ifndef NDEBUG
|
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
|
|
|
|
2004-01-12 21:31:27 +01:00
|
|
|
m_payloads.push_back(range(send_buffer_offset+13, r.length));
|
|
|
|
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
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
if (!m_announce_queue.empty())
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-06-14 01:30:42 +02:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
send_have(*i);
|
|
|
|
}
|
|
|
|
m_announce_queue.clear();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
// we have data that's scheduled for sending
|
|
|
|
int sent = m_socket->send(
|
|
|
|
&m_send_buffer[0]
|
|
|
|
, amount_to_send);
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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;
|
2003-10-23 01:00:57 +02:00
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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);
|
|
|
|
|
|
|
|
// empty the entire buffer at once or if
|
|
|
|
// only a part of the buffer could be sent
|
|
|
|
// remove the part that was sent from the buffer
|
2004-01-26 11:29:00 +01:00
|
|
|
if (sent == (int)m_send_buffer.size())
|
2004-01-04 05:29:13 +01:00
|
|
|
{
|
|
|
|
m_send_buffer.clear();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_send_buffer.erase(
|
|
|
|
m_send_buffer.begin()
|
|
|
|
, m_send_buffer.begin() + sent);
|
|
|
|
}
|
2003-11-05 00:27:06 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02: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
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-10-14 03:17:04 +02:00
|
|
|
m_last_sent = second_clock::universal_time();
|
2004-01-04 05:29:13 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02: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
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-01-25 19:18:36 +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-06-14 01:30:42 +02:00
|
|
|
|
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
|
|
|
*/ }
|
2004-01-25 19:18:36 +01:00
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2004-02-01 14:48:30 +01:00
|
|
|
bool peer_connection::has_timed_out() const
|
|
|
|
{
|
|
|
|
using namespace boost::posix_time;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
// if the peer hasn't become interested and we haven't
|
|
|
|
// become interested in the peer for 60 seconds, it
|
|
|
|
// has also timed out.
|
|
|
|
time_duration d1;
|
|
|
|
time_duration d2;
|
2004-10-14 03:17:04 +02:00
|
|
|
d1 = second_clock::universal_time() - m_became_uninterested;
|
|
|
|
d2 = second_clock::universal_time() - m_became_uninteresting;
|
2004-02-01 14:48:30 +01:00
|
|
|
if (!m_interesting
|
|
|
|
&& !m_peer_interested
|
2004-04-05 01:22:39 +02:00
|
|
|
&& d1 > seconds(60 * 3)
|
|
|
|
&& d2 > seconds(60 * 3))
|
2004-02-01 14:48:30 +01:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-01-04 05:29:13 +01:00
|
|
|
void peer_connection::keep_alive()
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2004-01-25 19:18:36 +01:00
|
|
|
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(m_send_buffer.end(), noop, noop+4);
|
2004-10-14 03:17:04 +02:00
|
|
|
m_last_sent = second_clock::universal_time();
|
2004-01-04 05:29:13 +01:00
|
|
|
#ifndef NDEBUG
|
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";
|
2004-01-04 05:29:13 +01:00
|
|
|
#endif
|
|
|
|
}
|
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)
|
|
|
|
{
|
|
|
|
send_have(*i);
|
|
|
|
}
|
|
|
|
m_announce_queue.clear();
|
|
|
|
}
|
|
|
|
send_buffer_updated();
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
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
|
|
|
}
|