premiere-libtorrent/src/bt_peer_connection.cpp

3600 lines
99 KiB
C++
Raw Normal View History

/*
Copyright (c) 2003-2016, Arvid Norberg
Copyright (c) 2007-2016, Arvid Norberg, Un Shyam
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.
*/
2015-08-20 01:33:20 +02:00
#include "libtorrent/config.hpp"
#include <memory> // unique_ptr
#include <vector>
#include <functional>
#include "libtorrent/aux_/disable_warnings_push.hpp"
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/multiprecision/integer.hpp>
2015-08-20 01:33:20 +02:00
#include "libtorrent/aux_/disable_warnings_pop.hpp"
#ifndef TORRENT_DISABLE_LOGGING
#include "libtorrent/hex.hpp" // to_hex
#endif
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/identify_client.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/aux_/io.hpp"
2010-11-29 02:33:05 +01:00
#include "libtorrent/socket_io.hpp"
#include "libtorrent/extensions.hpp"
2014-07-06 21:18:00 +02:00
#include "libtorrent/aux_/session_interface.hpp"
#include "libtorrent/alert_types.hpp"
2008-12-26 08:00:21 +01:00
#include "libtorrent/broadcast_socket.hpp"
2009-11-26 06:45:43 +01:00
#include "libtorrent/peer_info.hpp"
#include "libtorrent/random.hpp"
#include "libtorrent/alloca.hpp"
2014-07-06 21:18:00 +02:00
#include "libtorrent/socket_type.hpp"
#include "libtorrent/performance_counters.hpp" // for counters
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
2007-06-06 02:41:20 +02:00
#include "libtorrent/pe_crypto.hpp"
#include "libtorrent/hasher.hpp"
#endif
namespace libtorrent
{
namespace mp = boost::multiprecision;
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
namespace {
enum
{
handshake_len = 68,
dh_key_len = 96
};
// stream key (info hash of attached torrent)
// secret is the DH shared secret
// initializes m_enc_handler
std::shared_ptr<rc4_handler> init_pe_rc4_handler(key_t const& secret
, sha1_hash const& stream_key, bool const outgoing)
{
hasher h;
static const char keyA[] = {'k', 'e', 'y', 'A'};
static const char keyB[] = {'k', 'e', 'y', 'B'};
// encryption rc4 longkeys
// outgoing connection : hash ('keyA',S,SKEY)
// incoming connection : hash ('keyB',S,SKEY)
std::array<char, dh_key_len> const secret_buf = export_key(secret);
if (outgoing) h.update(keyA); else h.update(keyB);
h.update(secret_buf);
h.update(stream_key);
sha1_hash const local_key = h.final();
h.reset();
// decryption rc4 longkeys
// outgoing connection : hash ('keyB',S,SKEY)
// incoming connection : hash ('keyA',S,SKEY)
if (outgoing) h.update(keyB); else h.update(keyA);
h.update(secret_buf);
h.update(stream_key);
sha1_hash const remote_key = h.final();
auto ret = std::make_shared<rc4_handler>();
2016-08-16 04:50:25 +02:00
ret->set_incoming_key(remote_key);
ret->set_outgoing_key(local_key);
return ret;
}
} // anonymous namespace
#endif
#ifndef TORRENT_DISABLE_EXTENSIONS
bool ut_pex_peer_store::was_introduced_by(tcp::endpoint const &ep)
{
#if TORRENT_USE_IPV6
if (ep.address().is_v4())
{
#endif
peers4_t::value_type v(ep.address().to_v4().to_bytes(), ep.port());
auto i = std::lower_bound(m_peers.begin(), m_peers.end(), v);
return i != m_peers.end() && *i == v;
#if TORRENT_USE_IPV6
}
else
{
peers6_t::value_type v(ep.address().to_v6().to_bytes(), ep.port());
auto i = std::lower_bound(m_peers6.begin(), m_peers6.end(), v);
return i != m_peers6.end() && *i == v;
}
#endif
}
#endif // TORRENT_DISABLE_EXTENSIONS
bt_peer_connection::bt_peer_connection(peer_connection_args const& pack
, peer_id const& pid)
: peer_connection(pack)
, m_supports_extensions(false)
, m_supports_dht_port(false)
, m_supports_fast(false)
, m_sent_bitfield(false)
, m_sent_handshake(false)
, m_sent_allowed_fast(false)
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
2013-12-02 05:24:10 +01:00
, m_encrypted(false)
, m_rc4_encrypted(false)
, m_recv_buffer(peer_connection::m_recv_buffer)
2013-12-21 06:59:26 +01:00
#endif
2014-05-12 09:57:58 +02:00
, m_our_peer_id(pid)
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "CONSTRUCT", "bt_peer_connection");
#endif
#if TORRENT_USE_ASSERTS
m_in_constructor = false;
#endif
#ifndef TORRENT_DISABLE_EXTENSIONS
m_reserved_bits.fill(0);
#endif
}
void bt_peer_connection::start()
{
peer_connection::start();
// start in the state where we are trying to read the
// handshake from the other side
m_recv_buffer.reset(20);
setup_receive();
}
2016-07-10 13:34:45 +02:00
bt_peer_connection::~bt_peer_connection() = default;
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
void bt_peer_connection::switch_send_crypto(std::shared_ptr<crypto_plugin> crypto)
{
if (m_enc_handler.switch_send_crypto(crypto, send_buffer_size() - get_send_barrier()))
set_send_barrier(send_buffer_size());
}
void bt_peer_connection::switch_recv_crypto(std::shared_ptr<crypto_plugin> crypto)
{
m_enc_handler.switch_recv_crypto(crypto, m_recv_buffer);
}
#endif
void bt_peer_connection::on_connected()
{
if (is_disconnecting()) return;
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
if (t->graceful_pause())
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ON_CONNECTED", "graceful-paused");
#endif
2014-07-06 21:18:00 +02:00
disconnect(error_code(errors::torrent_paused), op_bittorrent);
return;
}
2014-07-06 21:18:00 +02:00
// make sure are much as possible of the response ends up in the same
// packet, or at least back-to-back packets
cork c_(*this);
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
std::uint8_t out_policy = std::uint8_t(m_settings.get_int(settings_pack::out_enc_policy));
#ifdef TORRENT_USE_OPENSSL
2011-09-12 05:51:49 +02:00
// never try an encrypted connection when already using SSL
if (is_ssl(*get_socket()))
2015-08-13 00:54:57 +02:00
out_policy = settings_pack::pe_disabled;
#endif
#ifndef TORRENT_DISABLE_LOGGING
char const* policy_name[] = {"forced", "enabled", "disabled"};
2015-08-13 00:54:57 +02:00
TORRENT_ASSERT(out_policy < sizeof(policy_name)/sizeof(policy_name[0]));
peer_log(peer_log_alert::info, "ENCRYPTION"
2015-08-13 00:54:57 +02:00
, "outgoing encryption policy: %s", policy_name[out_policy]);
#endif
2015-08-13 00:54:57 +02:00
if (out_policy == settings_pack::pe_forced)
{
write_pe1_2_dhkey();
if (is_disconnecting()) return;
m_state = state_t::read_pe_dhkey;
m_recv_buffer.reset(dh_key_len);
setup_receive();
}
2015-08-13 00:54:57 +02:00
else if (out_policy == settings_pack::pe_enabled)
{
TORRENT_ASSERT(peer_info_struct());
2014-07-06 21:18:00 +02:00
torrent_peer* pi = peer_info_struct();
if (pi->pe_support == true)
{
// toggle encryption support flag, toggled back to
// true if encrypted portion of the handshake
// completes correctly
pi->pe_support = false;
// if this fails, we need to reconnect
// fast.
2007-10-04 23:26:50 +02:00
fast_reconnect(true);
write_pe1_2_dhkey();
if (is_disconnecting()) return;
m_state = state_t::read_pe_dhkey;
m_recv_buffer.reset(dh_key_len);
setup_receive();
}
else // pi->pe_support == false
{
// toggled back to false if standard handshake
// completes correctly (without encryption)
pi->pe_support = true;
write_handshake();
m_recv_buffer.reset(20);
setup_receive();
}
}
2015-08-13 00:54:57 +02:00
else if (out_policy == settings_pack::pe_disabled)
#endif
{
write_handshake();
// start in the state where we are trying to read the
// handshake from the other side
m_recv_buffer.reset(20);
setup_receive();
}
}
void bt_peer_connection::on_metadata()
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ON_METADATA");
#endif
disconnect_if_redundant();
if (m_disconnecting) return;
if (!m_sent_handshake) return;
2016-07-20 20:34:29 +02:00
// we're still waiting to fully handshake with this peer. At the end of
// the handshake we'll send the bitfield and dht port anyway. It's too
// early to do now
if (static_cast<int>(m_state)
< static_cast<int>(state_t::read_packet_size))
{
return;
}
2008-08-29 19:21:56 +02:00
// connections that are still in the handshake
// will send their bitfield when the handshake
// is done
#ifndef TORRENT_DISABLE_EXTENSIONS
write_upload_only();
#endif
2014-07-06 21:18:00 +02:00
if (m_sent_bitfield) return;
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
write_bitfield();
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(m_sent_bitfield);
write_dht_port();
}
void bt_peer_connection::write_dht_port()
{
#ifndef TORRENT_DISABLE_DHT
2014-07-06 21:18:00 +02:00
if (m_supports_dht_port && m_ses.has_dht())
{
int const port = m_ses.external_udp_port();
if (port >= 0) write_dht_port(port);
}
#endif
}
void bt_peer_connection::write_dht_port(int listen_port)
{
INVARIANT_CHECK;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::outgoing_message, "DHT_PORT", "%d", listen_port);
#endif
char msg[] = {0,0,0,3, msg_dht_port, 0, 0};
char* ptr = msg + 5;
detail::write_uint16(listen_port, ptr);
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_dht_port);
}
void bt_peer_connection::write_have_all()
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(m_sent_handshake);
m_sent_bitfield = true;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::outgoing_message, "HAVE_ALL");
#endif
char msg[] = {0,0,0,1, msg_have_all};
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_have_all);
}
void bt_peer_connection::write_have_none()
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(m_sent_handshake);
m_sent_bitfield = true;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::outgoing_message, "HAVE_NONE");
#endif
char msg[] = {0,0,0,1, msg_have_none};
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_have_none);
}
void bt_peer_connection::write_reject_request(peer_request const& r)
{
INVARIANT_CHECK;
stats_counters().inc_stats_counter(counters::piece_rejects);
2012-03-09 07:24:01 +01:00
if (!m_supports_fast) return;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::outgoing_message, "REJECT_PIECE"
, "piece: %d | s: %d | l: %d", static_cast<int>(r.piece)
, r.start, r.length);
2012-04-23 07:48:46 +02:00
#endif
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
TORRENT_ASSERT(associated_torrent().lock()->valid_metadata());
char msg[] = {0,0,0,13, msg_reject_request,0,0,0,0, 0,0,0,0, 0,0,0,0};
char* ptr = msg + 5;
detail::write_int32(static_cast<int>(r.piece), ptr); // index
detail::write_int32(r.start, ptr); // begin
detail::write_int32(r.length, ptr); // length
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_reject);
}
void bt_peer_connection::write_allow_fast(piece_index_t const piece)
{
INVARIANT_CHECK;
if (!m_supports_fast) return;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::outgoing_message, "ALLOWED_FAST", "%d"
, static_cast<int>(piece));
#endif
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
TORRENT_ASSERT(associated_torrent().lock()->valid_metadata());
char msg[] = {0,0,0,5, msg_allowed_fast, 0, 0, 0, 0};
char* ptr = msg + 5;
detail::write_int32(static_cast<int>(piece), ptr);
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_allowed_fast);
}
void bt_peer_connection::write_suggest(piece_index_t const piece)
{
INVARIANT_CHECK;
2010-01-17 22:06:08 +01:00
if (!m_supports_fast) return;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(t->valid_metadata());
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::outgoing_message))
{
peer_log(peer_log_alert::outgoing_message, "SUGGEST"
, "piece: %d num_peers: %d", static_cast<int>(piece)
, t->has_picker() ? t->picker().get_availability(piece) : -1);
}
2014-07-06 21:18:00 +02:00
#endif
char msg[] = {0,0,0,5, msg_suggest_piece, 0, 0, 0, 0};
char* ptr = msg + 5;
detail::write_int32(static_cast<int>(piece), ptr);
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_suggest);
}
2007-05-25 21:42:10 +02:00
void bt_peer_connection::get_specific_peer_info(peer_info& p) const
{
TORRENT_ASSERT(!associated_torrent().expired());
if (is_interesting()) p.flags |= peer_info::interesting;
if (is_choked()) p.flags |= peer_info::choked;
if (is_peer_interested()) p.flags |= peer_info::remote_interested;
if (has_peer_choked()) p.flags |= peer_info::remote_choked;
if (support_extensions()) p.flags |= peer_info::supports_extensions;
if (is_outgoing()) p.flags |= peer_info::local_connection;
2013-10-27 20:56:37 +01:00
#if TORRENT_USE_I2P
if (is_i2p(*get_socket())) p.flags |= peer_info::i2p_socket;
#endif
if (is_utp(*get_socket())) p.flags |= peer_info::utp_socket;
if (is_ssl(*get_socket())) p.flags |= peer_info::ssl_socket;
2007-06-06 02:41:20 +02:00
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
2007-06-06 02:41:20 +02:00
if (m_encrypted)
{
p.flags |= m_rc4_encrypted
? peer_info::rc4_encrypted
: peer_info::plaintext_encrypted;
2007-06-06 02:41:20 +02:00
}
#endif
if (!is_connecting() && in_handshake())
p.flags |= peer_info::handshake;
if (is_connecting()) p.flags |= peer_info::connecting;
2007-06-06 02:41:20 +02:00
p.client = m_client_version;
p.connection_type = peer_info::standard_bittorrent;
}
bool bt_peer_connection::in_handshake() const
{
return !m_sent_handshake;
}
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
2007-06-06 02:41:20 +02:00
namespace {
char random_byte() { return char(random(0xff)); }
}
2007-06-06 02:41:20 +02:00
void bt_peer_connection::write_pe1_2_dhkey()
{
2007-06-07 12:18:13 +02:00
INVARIANT_CHECK;
TORRENT_ASSERT(!m_encrypted);
TORRENT_ASSERT(!m_rc4_encrypted);
TORRENT_ASSERT(!m_dh_key_exchange.get());
TORRENT_ASSERT(!m_sent_handshake);
2007-06-06 02:41:20 +02:00
#ifndef TORRENT_DISABLE_LOGGING
if (is_outgoing())
peer_log(peer_log_alert::info, "ENCRYPTION", "initiating encrypted handshake");
2007-06-06 02:41:20 +02:00
#endif
m_dh_key_exchange.reset(new (std::nothrow) dh_key_exchange);
if (!m_dh_key_exchange || !m_dh_key_exchange->good())
{
2014-07-06 21:18:00 +02:00
disconnect(errors::no_memory, op_encryption);
return;
}
2007-06-06 02:41:20 +02:00
int const pad_size = random(512);
2007-06-18 19:12:42 +02:00
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", "pad size: %d", pad_size);
2007-06-18 19:12:42 +02:00
#endif
char msg[dh_key_len + 512];
char* ptr = msg;
int const buf_size = dh_key_len + pad_size;
2007-06-06 02:41:20 +02:00
std::array<char, dh_key_len> const local_key = export_key(m_dh_key_exchange->get_local_key());
2016-08-16 04:50:25 +02:00
std::memcpy(ptr, local_key.data(), dh_key_len);
ptr += dh_key_len;
2007-06-06 02:41:20 +02:00
std::generate(ptr, ptr + pad_size, random_byte);
send_buffer(msg, buf_size);
2007-06-06 02:41:20 +02:00
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", "sent DH key");
2007-06-06 02:41:20 +02:00
#endif
}
void bt_peer_connection::write_pe3_sync()
{
2007-06-07 12:18:13 +02:00
INVARIANT_CHECK;
TORRENT_ASSERT(!m_encrypted);
TORRENT_ASSERT(!m_rc4_encrypted);
TORRENT_ASSERT(is_outgoing());
TORRENT_ASSERT(!m_sent_handshake);
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
2007-06-06 02:41:20 +02:00
hasher h;
sha1_hash const& info_hash = t->torrent_file().info_hash();
key_t const secret_key = m_dh_key_exchange->get_secret();
std::array<char, dh_key_len> const secret = export_key(secret_key);
2007-06-06 02:41:20 +02:00
int const pad_size = random(512);
2007-06-06 02:41:20 +02:00
// synchash,skeyhash,vc,crypto_provide,len(pad),pad,len(ia)
char msg[20 + 20 + 8 + 4 + 2 + 512 + 2];
char* ptr = msg;
2007-06-06 02:41:20 +02:00
static char const req1[4] = {'r', 'e', 'q', '1'};
2007-06-06 02:41:20 +02:00
// sync hash (hash('req1',S))
h.reset();
h.update(req1);
h.update(secret);
sha1_hash const sync_hash = h.final();
2007-06-06 02:41:20 +02:00
std::memcpy(ptr, sync_hash.data(), 20);
ptr += 20;
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::info))
{
peer_log(peer_log_alert::info, "ENCRYPTION"
, "writing synchash %s secret: %s"
, aux::to_hex(sync_hash).c_str()
, aux::to_hex(secret).c_str());
}
#endif
2007-06-06 02:41:20 +02:00
static char const req2[4] = {'r', 'e', 'q', '2'};
2007-06-06 02:41:20 +02:00
// stream key obfuscated hash [ hash('req2',SKEY) xor hash('req3',S) ]
h.reset();
h.update(req2);
h.update(info_hash);
sha1_hash const streamkey_hash = h.final();
2007-06-06 02:41:20 +02:00
static char const req3[4] = {'r', 'e', 'q', '3'};
2007-06-06 02:41:20 +02:00
h.reset();
h.update(req3);
h.update(secret);
sha1_hash const obfsc_hash = h.final() ^ streamkey_hash;
2007-06-06 02:41:20 +02:00
std::memcpy(ptr, obfsc_hash.data(), 20);
ptr += 20;
2007-06-06 02:41:20 +02:00
// Discard DH key exchange data, setup RC4 keys
m_rc4 = init_pe_rc4_handler(secret_key, info_hash, is_outgoing());
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", "computed RC4 keys");
#endif
m_dh_key_exchange.reset(); // secret should be invalid at this point
2007-06-06 02:41:20 +02:00
// write the verification constant and crypto field
int const encrypt_size = sizeof(msg) - 512 + pad_size - 40;
2007-06-06 02:41:20 +02:00
// this is an invalid setting, but let's just make the best of the situation
int const enc_level = m_settings.get_int(settings_pack::allowed_enc_level);
std::uint8_t const crypto_provide = ((enc_level & settings_pack::pe_both) == 0)
? std::uint8_t(settings_pack::pe_both)
: std::uint8_t(enc_level);
2007-06-06 02:41:20 +02:00
#ifndef TORRENT_DISABLE_LOGGING
2010-12-01 05:22:03 +01:00
char const* level[] = {"plaintext", "rc4", "plaintext rc4"};
peer_log(peer_log_alert::info, "ENCRYPTION"
, "%s", level[crypto_provide - 1]);
2007-06-06 02:41:20 +02:00
#endif
write_pe_vc_cryptofield(ptr, encrypt_size, crypto_provide, pad_size);
span<char> vec(ptr, encrypt_size);
m_rc4->encrypt(vec);
send_buffer(msg, sizeof(msg) - 512 + pad_size);
2007-06-06 02:41:20 +02:00
}
void bt_peer_connection::write_pe4_sync(int crypto_select)
{
2007-06-07 12:18:13 +02:00
INVARIANT_CHECK;
TORRENT_ASSERT(!is_outgoing());
TORRENT_ASSERT(!m_encrypted);
TORRENT_ASSERT(!m_rc4_encrypted);
TORRENT_ASSERT(crypto_select == 0x02 || crypto_select == 0x01);
TORRENT_ASSERT(!m_sent_handshake);
2007-06-06 02:41:20 +02:00
int const pad_size = random(512);
2007-06-06 02:41:20 +02:00
int const buf_size = 8 + 4 + 2 + pad_size;
char msg[512 + 8 + 4 + 2];
write_pe_vc_cryptofield(msg, sizeof(msg), crypto_select, pad_size);
2007-06-06 02:41:20 +02:00
span<char> vec(msg, buf_size);
m_rc4->encrypt(vec);
send_buffer(msg, buf_size);
2007-06-06 02:41:20 +02:00
// encryption method has been negotiated
if (crypto_select == 0x02)
2007-06-06 02:41:20 +02:00
m_rc4_encrypted = true;
else // 0x01
m_rc4_encrypted = false;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", " crypto select: %s"
2010-12-01 05:22:03 +01:00
, (crypto_select == 0x01) ? "plaintext" : "rc4");
2007-06-06 02:41:20 +02:00
#endif
}
void bt_peer_connection::write_pe_vc_cryptofield(char* write_buf
, int const len
, int const crypto_field
, int const pad_size)
2015-05-19 05:13:49 +02:00
{
2007-06-07 12:18:13 +02:00
INVARIANT_CHECK;
2015-05-19 05:13:49 +02:00
#if !TORRENT_USE_ASSERTS
TORRENT_UNUSED(len);
#endif
2007-06-07 12:18:13 +02:00
TORRENT_ASSERT(crypto_field <= 0x03 && crypto_field > 0);
2007-06-06 02:41:20 +02:00
// vc,crypto_field,len(pad),pad, (len(ia))
TORRENT_ASSERT((len >= 8+4+2+pad_size+2 && is_outgoing())
|| (len >= 8+4+2+pad_size && !is_outgoing()));
TORRENT_ASSERT(!m_sent_handshake);
2007-06-06 02:41:20 +02:00
// encrypt(vc, crypto_provide/select, len(Pad), len(IA))
// len(pad) is zero for now, len(IA) only for outgoing connections
2015-05-19 05:13:49 +02:00
2007-06-06 02:41:20 +02:00
// vc
std::memset(write_buf, 0, 8);
write_buf += 8;
2007-06-06 02:41:20 +02:00
detail::write_uint32(crypto_field, write_buf);
detail::write_uint16(pad_size, write_buf); // len (pad)
2007-06-06 02:41:20 +02:00
std::generate(write_buf, write_buf + pad_size, random_byte);
write_buf += pad_size;
2007-06-06 02:41:20 +02:00
// append len(ia) if we are initiating
if (is_outgoing())
detail::write_uint16(handshake_len, write_buf); // len(IA)
2015-05-19 05:13:49 +02:00
}
2007-06-06 02:41:20 +02:00
// TODO: 3 use span instead of (pointer,len) pairs
int bt_peer_connection::get_syncoffset(char const* src, int const src_size
, char const* target, int const target_size) const
2007-06-06 02:41:20 +02:00
{
TORRENT_ASSERT(target_size >= src_size);
TORRENT_ASSERT(src_size > 0);
TORRENT_ASSERT(src);
TORRENT_ASSERT(target);
2007-06-06 02:41:20 +02:00
int const traverse_limit = target_size - src_size;
2007-06-06 02:41:20 +02:00
// TODO: this could be optimized using knuth morris pratt
for (int i = 0; i < traverse_limit; ++i)
{
char const* target_ptr = target + i;
if (std::equal(src, src+src_size, target_ptr))
return i;
}
// no complete sync
2007-06-06 02:41:20 +02:00
return -1;
}
void bt_peer_connection::rc4_decrypt(span<char> buf)
{
m_rc4->decrypt(buf);
}
2014-07-06 21:18:00 +02:00
#endif // #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
void bt_peer_connection::write_handshake()
{
INVARIANT_CHECK;
TORRENT_ASSERT(!m_sent_handshake);
m_sent_handshake = true;
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
// add handshake to the send buffer
const char version_string[] = "BitTorrent protocol";
const int string_len = sizeof(version_string) - 1;
char handshake[1 + string_len + 8 + 20 + 20];
char* ptr = handshake;
// length of version string
detail::write_uint8(string_len, ptr);
// protocol identifier
std::memcpy(ptr, version_string, string_len);
ptr += string_len;
// 8 zeroes
std::memset(ptr, 0, 8);
#ifndef TORRENT_DISABLE_DHT
// indicate that we support the DHT messages
*(ptr + 7) |= 0x01;
#endif
#ifndef TORRENT_DISABLE_EXTENSIONS
// we support extensions
*(ptr + 5) |= 0x10;
#endif
2014-07-06 21:18:00 +02:00
if (m_settings.get_bool(settings_pack::support_merkle_torrents))
{
// we support merkle torrents
*(ptr + 5) |= 0x08;
}
// we support FAST extension
*(ptr + 7) |= 0x04;
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::outgoing_message))
{
std::string bitmask;
for (int k = 0; k < 8; ++k)
{
for (int j = 0; j < 8; ++j)
{
if (ptr[k] & (0x80 >> j)) bitmask += '1';
else bitmask += '0';
}
}
peer_log(peer_log_alert::outgoing_message, "EXTENSIONS"
, "%s", bitmask.c_str());
}
#endif
ptr += 8;
// info hash
sha1_hash const& ih = t->torrent_file().info_hash();
std::memcpy(ptr, ih.data(), ih.size());
ptr += 20;
// peer id
2014-07-06 21:18:00 +02:00
if (m_settings.get_bool(settings_pack::anonymous_mode))
2012-07-09 00:47:25 +02:00
{
// in anonymous mode, every peer connection
// has a unique peer-id
aux::random_bytes(m_our_peer_id);
2012-07-09 00:47:25 +02:00
}
std::memcpy(ptr, m_our_peer_id.data(), 20);
ptr += 20;
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::outgoing))
2014-10-12 20:16:46 +02:00
{
peer_log(peer_log_alert::outgoing, "HANDSHAKE"
, "sent peer_id: %s client: %s"
, aux::to_hex(m_our_peer_id).c_str(), identify_client(m_our_peer_id).c_str());
2014-10-12 20:16:46 +02:00
}
if (should_log(peer_log_alert::outgoing_message))
{
peer_log(peer_log_alert::outgoing_message, "HANDSHAKE"
, "ih: %s", aux::to_hex(ih).c_str());
}
#endif
send_buffer(handshake, sizeof(handshake));
}
piece_block_progress bt_peer_connection::downloading_piece_progress() const
{
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
span<char const> recv_buffer = m_recv_buffer.get();
// are we currently receiving a 'piece' message?
if (m_state != state_t::read_packet
|| int(recv_buffer.size()) <= 9
|| recv_buffer[0] != msg_piece)
return piece_block_progress();
const char* ptr = recv_buffer.begin() + 1;
peer_request r;
r.piece = piece_index_t(detail::read_int32(ptr));
r.start = detail::read_int32(ptr);
r.length = m_recv_buffer.packet_size() - 9;
// is any of the piece message header data invalid?
if (!verify_piece(r))
return piece_block_progress();
piece_block_progress p;
p.piece_index = r.piece;
p.block_index = r.start / t->block_size();
p.bytes_downloaded = int(recv_buffer.size()) - 9;
p.full_block_bytes = r.length;
return p;
}
// message handlers
// -----------------------------
// --------- KEEPALIVE ---------
// -----------------------------
void bt_peer_connection::on_keepalive()
{
INVARIANT_CHECK;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "KEEPALIVE");
#endif
incoming_keepalive();
}
// -----------------------------
// ----------- CHOKE -----------
// -----------------------------
void bt_peer_connection::on_choke(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
received_bytes(0, received);
if (m_recv_buffer.packet_size() != 1)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_choke, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
incoming_choke();
2008-05-12 05:05:27 +02:00
if (is_disconnecting()) return;
if (!m_supports_fast)
{
// we just got choked, and the peer that choked use
// doesn't support fast extensions, so we have to
// assume that the choke message implies that all
// of our requests are rejected. Go through them and
// pretend that we received reject request messages
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
while (!download_queue().empty())
{
piece_block const& b = download_queue().front().block;
peer_request r;
r.piece = b.piece_index;
r.start = b.block_index * t->block_size();
r.length = t->block_size();
// if it's the last piece, make sure to
// set the length of the request to not
// exceed the end of the torrent. This is
// necessary in order to maintain a correct
// m_outstanding_bytes
if (r.piece == t->torrent_file().last_piece())
{
r.length = (std::min)(t->torrent_file().piece_size(
r.piece) - r.start, r.length);
}
incoming_reject_request(r);
}
}
}
// -----------------------------
// ---------- UNCHOKE ----------
// -----------------------------
void bt_peer_connection::on_unchoke(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
received_bytes(0, received);
if (m_recv_buffer.packet_size() != 1)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_unchoke, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
incoming_unchoke();
}
// -----------------------------
// -------- INTERESTED ---------
// -----------------------------
void bt_peer_connection::on_interested(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
received_bytes(0, received);
if (m_recv_buffer.packet_size() != 1)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_interested, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
// we defer sending the allowed set until the peer says it's interested in
// us. This saves some bandwidth and allows us to omit messages for pieces
// that the peer already has
if (!m_sent_allowed_fast && m_supports_fast)
{
m_sent_allowed_fast = true;
send_allowed_set();
}
incoming_interested();
}
// -----------------------------
// ------ NOT INTERESTED -------
// -----------------------------
void bt_peer_connection::on_not_interested(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
received_bytes(0, received);
if (m_recv_buffer.packet_size() != 1)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_not_interested, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
incoming_not_interested();
}
// -----------------------------
// ----------- HAVE ------------
// -----------------------------
void bt_peer_connection::on_have(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
received_bytes(0, received);
if (m_recv_buffer.packet_size() != 5)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_have, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
span<char const> recv_buffer = m_recv_buffer.get();
const char* ptr = recv_buffer.begin() + 1;
piece_index_t const index(detail::read_int32(ptr));
incoming_have(index);
}
// -----------------------------
// --------- BITFIELD ----------
// -----------------------------
void bt_peer_connection::on_bitfield(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
// if we don't have the metadata, we cannot
// verify the bitfield size
if (t->valid_metadata()
&& m_recv_buffer.packet_size() - 1 != (t->torrent_file().num_pieces() + 7) / 8)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_bitfield_size, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
span<char const> recv_buffer = m_recv_buffer.get();
typed_bitfield<piece_index_t> bits;
bits.assign(recv_buffer.begin() + 1
, t->valid_metadata()?get_bitfield().size():(m_recv_buffer.packet_size()-1)*8);
incoming_bitfield(bits);
}
// -----------------------------
// ---------- REQUEST ----------
// -----------------------------
void bt_peer_connection::on_request(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
received_bytes(0, received);
if (m_recv_buffer.packet_size() != 13)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_request, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
span<char const> recv_buffer = m_recv_buffer.get();
peer_request r;
const char* ptr = recv_buffer.begin() + 1;
r.piece = piece_index_t(detail::read_int32(ptr));
r.start = detail::read_int32(ptr);
r.length = detail::read_int32(ptr);
2008-12-08 07:36:22 +01:00
incoming_request(r);
}
// -----------------------------
// ----------- PIECE -----------
// -----------------------------
void bt_peer_connection::on_piece(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
span<char const> recv_buffer = m_recv_buffer.get();
int recv_pos = m_recv_buffer.pos(); // recv_buffer.end - recv_buffer.begin;
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
bool const merkle = static_cast<std::uint8_t>(recv_buffer.front()) == 250;
if (merkle)
{
if (recv_pos == 1)
{
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
return;
}
if (recv_pos < 13)
2008-10-01 17:19:31 +02:00
{
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
return;
2008-10-01 17:19:31 +02:00
}
if (recv_pos >= 13)
{
char const* ptr = recv_buffer.begin() + 9;
int const list_size = detail::read_int32(ptr);
if (list_size > m_recv_buffer.packet_size() - 13)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_hash_list, op_bittorrent, 2);
return;
}
if (m_recv_buffer.packet_size() - 13 - list_size > t->block_size())
{
2014-07-06 21:18:00 +02:00
disconnect(errors::packet_too_large, op_bittorrent, 2);
return;
}
}
}
else
{
if (recv_pos == 1)
{
if (m_recv_buffer.packet_size() - 9 > t->block_size())
{
2014-07-06 21:18:00 +02:00
disconnect(errors::packet_too_large, op_bittorrent, 2);
return;
}
}
}
// classify the received data as protocol chatter
// or data payload for the statistics
int piece_bytes = 0;
2011-08-05 08:10:12 +02:00
int header_size = merkle?13:9;
peer_request p;
int list_size = 0;
if (recv_pos >= header_size)
{
const char* ptr = recv_buffer.begin() + 1;
p.piece = piece_index_t(detail::read_int32(ptr));
2011-08-05 08:10:12 +02:00
p.start = detail::read_int32(ptr);
if (merkle)
{
list_size = detail::read_int32(ptr);
p.length = m_recv_buffer.packet_size() - list_size - header_size;
2011-08-05 08:10:12 +02:00
header_size += list_size;
}
else
{
p.length = m_recv_buffer.packet_size() - header_size;
2011-08-05 08:10:12 +02:00
}
}
else
{
p.piece = piece_index_t(0);
p.start = 0;
p.length = 0;
}
2011-08-05 08:10:12 +02:00
if (recv_pos <= header_size)
{
// only received protocol data
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
}
2011-08-05 08:10:12 +02:00
else if (recv_pos - received >= header_size)
{
// only received payload data
2014-07-06 21:18:00 +02:00
received_bytes(received, 0);
piece_bytes = received;
}
else
{
// received a bit of both
2011-08-05 08:10:12 +02:00
TORRENT_ASSERT(recv_pos - received < header_size);
TORRENT_ASSERT(recv_pos > header_size);
TORRENT_ASSERT(header_size - (recv_pos - received) <= header_size);
2014-07-06 21:18:00 +02:00
received_bytes(
2011-08-05 08:10:12 +02:00
recv_pos - header_size
, header_size - (recv_pos - received));
piece_bytes = recv_pos - header_size;
}
if (recv_pos < header_size) return;
#ifndef TORRENT_DISABLE_LOGGING
// peer_log(peer_log_alert::incoming_message, "PIECE_FRAGMENT", "p: %d start: %d length: %d"
2010-12-01 05:22:03 +01:00
// , p.piece, p.start, p.length);
#endif
if (recv_pos - received < header_size && recv_pos >= header_size)
{
// call this once, the first time the entire header
// has been received
start_receive_piece(p);
if (is_disconnecting()) return;
}
incoming_piece_fragment(piece_bytes);
if (!m_recv_buffer.packet_finished()) return;
if (merkle && list_size > 0)
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "HASHPIECE"
, "piece: %d list: %d", static_cast<int>(p.piece), list_size);
#endif
bdecode_node hash_list;
error_code ec;
if (bdecode(recv_buffer.begin() + 13, recv_buffer.begin() + 13 + list_size
, hash_list, ec) != 0)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_hash_piece, op_bittorrent, 2);
return;
}
// the list has this format:
// [ [node-index, hash], [node-index, hash], ... ]
if (hash_list.type() != bdecode_node::list_t)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_hash_list, op_bittorrent, 2);
return;
}
std::map<int, sha1_hash> nodes;
for (int i = 0; i < hash_list.list_size(); ++i)
{
bdecode_node e = hash_list.list_at(i);
if (e.type() != bdecode_node::list_t
|| e.list_size() != 2
|| e.list_at(0).type() != bdecode_node::int_t
|| e.list_at(1).type() != bdecode_node::string_t
|| e.list_at(1).string_length() != 20) continue;
nodes.insert(std::make_pair(int(e.list_int_value_at(0))
, sha1_hash(e.list_at(1).string_ptr())));
}
if (!nodes.empty() && !t->add_merkle_nodes(nodes, p.piece))
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_hash_piece, op_bittorrent, 2);
return;
}
}
incoming_piece(p, recv_buffer.begin() + header_size);
}
// -----------------------------
// ---------- CANCEL -----------
// -----------------------------
void bt_peer_connection::on_cancel(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
received_bytes(0, received);
if (m_recv_buffer.packet_size() != 13)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_cancel, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
span<char const> recv_buffer = m_recv_buffer.get();
peer_request r;
const char* ptr = recv_buffer.begin() + 1;
r.piece = piece_index_t(detail::read_int32(ptr));
r.start = detail::read_int32(ptr);
r.length = detail::read_int32(ptr);
incoming_cancel(r);
}
// -----------------------------
// --------- DHT PORT ----------
// -----------------------------
void bt_peer_connection::on_dht_port(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
received_bytes(0, received);
if (m_recv_buffer.packet_size() != 3)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_dht_port, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
span<char const> recv_buffer = m_recv_buffer.get();
const char* ptr = recv_buffer.begin() + 1;
int listen_port = detail::read_uint16(ptr);
incoming_dht_port(listen_port);
if (!m_supports_dht_port)
{
m_supports_dht_port = true;
write_dht_port();
}
}
void bt_peer_connection::on_suggest_piece(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
if (!m_supports_fast)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_suggest, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
span<char const> recv_buffer = m_recv_buffer.get();
const char* ptr = recv_buffer.begin() + 1;
piece_index_t const piece(detail::read_uint32(ptr));
incoming_suggest(piece);
}
void bt_peer_connection::on_have_all(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
if (!m_supports_fast)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_have_all, op_bittorrent, 2);
return;
}
incoming_have_all();
}
void bt_peer_connection::on_have_none(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
if (!m_supports_fast)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_have_none, op_bittorrent, 2);
return;
}
incoming_have_none();
}
void bt_peer_connection::on_reject_request(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
if (!m_supports_fast)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_reject, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
span<char const> recv_buffer = m_recv_buffer.get();
peer_request r;
const char* ptr = recv_buffer.begin() + 1;
r.piece = piece_index_t(detail::read_int32(ptr));
r.start = detail::read_int32(ptr);
r.length = detail::read_int32(ptr);
incoming_reject_request(r);
}
void bt_peer_connection::on_allowed_fast(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
if (!m_supports_fast)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_allow_fast, op_bittorrent, 2);
return;
}
if (!m_recv_buffer.packet_finished()) return;
span<char const> recv_buffer = m_recv_buffer.get();
const char* ptr = recv_buffer.begin() + 1;
piece_index_t index(detail::read_int32(ptr));
incoming_allowed_fast(index);
}
2010-11-29 02:33:05 +01:00
// -----------------------------
// -------- RENDEZVOUS ---------
// -----------------------------
2011-01-29 13:13:49 +01:00
#ifndef TORRENT_DISABLE_EXTENSIONS
2010-11-29 02:33:05 +01:00
void bt_peer_connection::on_holepunch()
{
INVARIANT_CHECK;
if (!m_recv_buffer.packet_finished()) return;
2010-11-29 02:33:05 +01:00
// we can't accept holepunch messages from peers
// that don't support the holepunch extension
// because we wouldn't be able to respond
if (m_holepunch_id == 0) return;
span<char const> recv_buffer = m_recv_buffer.get();
TORRENT_ASSERT(recv_buffer.front() == msg_extended);
recv_buffer = recv_buffer.subspan(1);
TORRENT_ASSERT(recv_buffer.front() == holepunch_msg);
recv_buffer = recv_buffer.subspan(1);
2010-11-29 02:33:05 +01:00
const char* ptr = recv_buffer.begin();
2010-11-29 02:33:05 +01:00
// ignore invalid messages
if (int(recv_buffer.size()) < 2) return;
2010-11-29 02:33:05 +01:00
int const msg_type = detail::read_uint8(ptr);
int const addr_type = detail::read_uint8(ptr);
2010-11-29 02:33:05 +01:00
tcp::endpoint ep;
if (addr_type == 0)
{
if (int(recv_buffer.size()) < 2 + 4 + 2) return;
2010-11-29 02:33:05 +01:00
// IPv4 address
ep = detail::read_v4_endpoint<tcp::endpoint>(ptr);
}
#if TORRENT_USE_IPV6
else if (addr_type == 1)
{
// IPv6 address
if (int(recv_buffer.size()) < 2 + 18 + 2) return;
2010-11-29 02:33:05 +01:00
ep = detail::read_v6_endpoint<tcp::endpoint>(ptr);
}
#endif
else
{
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::incoming_message))
{
static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"};
peer_log(peer_log_alert::incoming_message, "HOLEPUNCH"
, "msg: %s from %s to: unknown address type"
, (msg_type >= 0 && msg_type < 3 ? hp_msg_name[msg_type] : "unknown message type")
, print_address(remote().address()).c_str());
}
2010-11-29 02:33:05 +01:00
#endif
return; // unknown address type
}
std::shared_ptr<torrent> t = associated_torrent().lock();
2010-11-29 02:33:05 +01:00
if (!t) return;
switch (msg_type)
{
case hp_rendezvous: // rendezvous
{
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::incoming_message))
{
peer_log(peer_log_alert::incoming_message, "HOLEPUNCH"
, "msg: rendezvous to: %s", print_address(ep.address()).c_str());
}
2010-11-29 02:33:05 +01:00
#endif
// this peer is asking us to introduce it to
// the peer at 'ep'. We need to find which of
// our connections points to that endpoint
bt_peer_connection* p = t->find_peer(ep);
2016-07-09 22:26:26 +02:00
if (p == nullptr)
2010-11-29 02:33:05 +01:00
{
// we're not connected to this peer
write_holepunch_msg(hp_failed, ep, hp_not_connected);
break;
}
if (!p->supports_holepunch())
{
write_holepunch_msg(hp_failed, ep, hp_no_support);
break;
}
if (p == this)
{
write_holepunch_msg(hp_failed, ep, hp_no_self);
break;
}
write_holepunch_msg(hp_connect, ep, 0);
p->write_holepunch_msg(hp_connect, remote(), 0);
} break;
case hp_connect:
{
// add or find the peer with this endpoint
2014-07-06 21:18:00 +02:00
torrent_peer* p = t->add_peer(ep, peer_info::pex);
2016-07-09 22:26:26 +02:00
if (p == nullptr || p->connection)
2010-11-29 02:33:05 +01:00
{
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::incoming_message))
{
peer_log(peer_log_alert::incoming_message, "HOLEPUNCH"
, "msg:connect to: %s error: failed to add peer"
, print_address(ep.address()).c_str());
}
2010-11-29 02:33:05 +01:00
#endif
// we either couldn't add this peer, or it's
// already connected. Just ignore the connect message
break;
}
if (p->banned)
{
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::incoming_message))
{
peer_log(peer_log_alert::incoming_message, "HOLEPUNCH"
, "msg:connect to: %s error: peer banned", print_address(ep.address()).c_str());
}
2010-11-29 02:33:05 +01:00
#endif
// this peer is banned, don't connect to it
break;
}
// to make sure we use the uTP protocol
p->supports_utp = true;
// #error make sure we make this a connection candidate
// in case it has too many failures for instance
t->connect_to_peer(p, true);
// mark this connection to be in holepunch mode
// so that it will retry faster and stick to uTP while it's
// retrying
2014-07-06 21:18:00 +02:00
t->update_want_peers();
2010-11-29 02:33:05 +01:00
if (p->connection)
p->connection->set_holepunch_mode();
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::incoming_message))
{
peer_log(peer_log_alert::incoming_message, "HOLEPUNCH"
, "msg:connect to: %s"
2010-12-01 05:22:03 +01:00
, print_address(ep.address()).c_str());
}
2010-11-29 02:33:05 +01:00
#endif
} break;
case hp_failed:
{
std::uint32_t error = detail::read_uint32(ptr);
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::incoming_message))
{
char const* err_msg[] = {"no such peer", "not connected", "no support", "no self"};
peer_log(peer_log_alert::incoming_message, "HOLEPUNCH"
, "msg:failed error: %d msg: %s", error
, ((error > 0 && error < 5)?err_msg[error-1]:"unknown message id"));
}
2010-11-29 02:33:05 +01:00
#endif
// #error deal with holepunch errors
(void)error;
2010-11-29 02:33:05 +01:00
} break;
#ifndef TORRENT_DISABLE_LOGGING
2010-11-29 02:33:05 +01:00
default:
{
if (should_log(peer_log_alert::incoming_message))
{
peer_log(peer_log_alert::incoming_message, "HOLEPUNCH"
, "msg: unknown message type (%d) to: %s"
, msg_type, print_address(ep.address()).c_str());
}
2010-11-29 02:33:05 +01:00
}
#endif
}
}
void bt_peer_connection::write_holepunch_msg(int const type, tcp::endpoint const& ep, int const error)
2010-11-29 02:33:05 +01:00
{
char buf[35];
char* ptr = buf + 6;
detail::write_uint8(type, ptr);
if (ep.address().is_v4()) detail::write_uint8(0, ptr);
else detail::write_uint8(1, ptr);
detail::write_endpoint(ep, ptr);
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::outgoing_message))
{
static const char* hp_msg_name[] = {"rendezvous", "connect", "failed"};
static const char* hp_error_string[] = {"", "no such peer", "not connected", "no support", "no self"};
peer_log(peer_log_alert::outgoing_message, "HOLEPUNCH"
, "msg: %s to: %s error: %s"
, (type >= 0 && type < 3 ? hp_msg_name[type] : "unknown message type")
, print_address(ep.address()).c_str()
, hp_error_string[error]);
}
2010-11-29 02:33:05 +01:00
#endif
if (type == hp_failed)
{
detail::write_uint32(error, ptr);
}
// write the packet length and type
char* hdr = buf;
detail::write_uint32(ptr - buf - 4, hdr);
detail::write_uint8(msg_extended, hdr);
detail::write_uint8(m_holepunch_id, hdr);
TORRENT_ASSERT(ptr <= buf + sizeof(buf));
send_buffer(buf, int(ptr - buf));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_extended);
2010-11-29 02:33:05 +01:00
}
2011-01-29 13:13:49 +01:00
#endif // TORRENT_DISABLE_EXTENSIONS
2010-11-29 02:33:05 +01:00
// -----------------------------
// --------- EXTENDED ----------
// -----------------------------
#ifndef TORRENT_DISABLE_EXTENSIONS
void bt_peer_connection::on_extended(int received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
received_bytes(0, received);
if (m_recv_buffer.packet_size() < 2)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_extended, op_bittorrent, 2);
return;
}
if (associated_torrent().expired())
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_extended, op_bittorrent, 2);
return;
}
span<char const> recv_buffer = m_recv_buffer.get();
if (int(recv_buffer.size()) < 2) return;
TORRENT_ASSERT(recv_buffer.front() == msg_extended);
recv_buffer = recv_buffer.subspan(1);
int extended_id = aux::read_uint8(recv_buffer);
if (extended_id == 0)
{
on_extended_handshake();
disconnect_if_redundant();
return;
}
2010-11-29 02:33:05 +01:00
if (extended_id == upload_only_msg)
{
if (!m_recv_buffer.packet_finished()) return;
if (m_recv_buffer.packet_size() != 3)
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "UPLOAD_ONLY"
, "ERROR: unexpected packet size: %d", m_recv_buffer.packet_size());
#endif
return;
}
bool ul = aux::read_uint8(recv_buffer) != 0;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "UPLOAD_ONLY"
, "%s", (ul?"true":"false"));
2010-11-29 02:33:05 +01:00
#endif
set_upload_only(ul);
return;
}
if (extended_id == share_mode_msg)
{
if (!m_recv_buffer.packet_finished()) return;
if (m_recv_buffer.packet_size() != 3)
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "SHARE_MODE"
, "ERROR: unexpected packet size: %d", m_recv_buffer.packet_size());
#endif
return;
}
bool sm = aux::read_uint8(recv_buffer) != 0;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "SHARE_MODE"
, "%s", (sm?"true":"false"));
#endif
set_share_mode(sm);
return;
}
2010-11-29 02:33:05 +01:00
if (extended_id == holepunch_msg)
{
if (!m_recv_buffer.packet_finished()) return;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "HOLEPUNCH");
#endif
2010-11-29 02:33:05 +01:00
on_holepunch();
return;
}
if (extended_id == dont_have_msg)
{
if (!m_recv_buffer.packet_finished()) return;
if (m_recv_buffer.packet_size() != 6)
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "DONT_HAVE"
, "ERROR: unexpected packet size: %d", m_recv_buffer.packet_size());
#endif
return;
}
piece_index_t const piece(aux::read_uint32(recv_buffer));
incoming_dont_have(piece);
return;
}
#ifndef TORRENT_DISABLE_LOGGING
if (m_recv_buffer.packet_finished())
peer_log(peer_log_alert::incoming_message, "EXTENSION_MESSAGE"
, "msg: %d size: %d", extended_id, m_recv_buffer.packet_size());
2010-11-29 02:33:05 +01:00
#endif
for (auto const& e : m_extensions)
{
if (e->on_extended(m_recv_buffer.packet_size() - 2, extended_id
, recv_buffer))
return;
}
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_message, op_bittorrent, 2);
return;
}
void bt_peer_connection::on_extended_handshake()
{
if (!m_recv_buffer.packet_finished()) return;
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
span<char const> recv_buffer = m_recv_buffer.get();
bdecode_node root;
error_code ec;
int pos;
int ret = bdecode(recv_buffer.begin() + 2, recv_buffer.end(), root, ec, &pos);
if (ret != 0 || ec || root.type() != bdecode_node::dict_t)
{
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::info))
{
peer_log(peer_log_alert::info, "EXTENSION_MESSAGE"
, "invalid extended handshake: %s pos: %d"
, ec.message().c_str(), pos);
}
#endif
return;
}
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::incoming_message))
{
peer_log(peer_log_alert::incoming_message, "EXTENDED_HANDSHAKE"
, "%s", print_entry(root).c_str());
}
#endif
for (auto i = m_extensions.begin();
2008-07-01 22:22:25 +02:00
!m_extensions.empty() && i != m_extensions.end();)
{
// a false return value means that the extension
// isn't supported by the other end. So, it is removed.
if (!(*i)->on_extension_handshake(root))
i = m_extensions.erase(i);
else
++i;
}
2008-10-23 18:31:15 +02:00
if (is_disconnecting()) return;
2009-11-24 19:49:59 +01:00
// upload_only
if (bdecode_node m = root.dict_find_dict("m"))
2010-11-29 02:33:05 +01:00
{
m_upload_only_id = std::uint8_t(m.dict_find_int_value("upload_only", 0));
m_holepunch_id = std::uint8_t(m.dict_find_int_value("ut_holepunch", 0));
m_dont_have_id = std::uint8_t(m.dict_find_int_value("lt_donthave", 0));
2010-11-29 02:33:05 +01:00
}
2009-11-24 19:49:59 +01:00
// there is supposed to be a remote listen port
int listen_port = int(root.dict_find_int_value("p"));
2016-07-09 22:26:26 +02:00
if (listen_port > 0 && peer_info_struct() != nullptr)
{
2014-07-06 21:18:00 +02:00
t->update_peer_port(listen_port, peer_info_struct(), peer_info::incoming);
received_listen_port();
if (is_disconnecting()) return;
}
// there should be a version too
// but where do we put that info?
2010-03-19 19:39:51 +01:00
int const last_seen_complete = int(root.dict_find_int_value("complete_ago", -1));
2010-03-19 19:39:51 +01:00
if (last_seen_complete >= 0) set_last_seen_complete(last_seen_complete);
auto client_info = root.dict_find_string_value("v");
if (!client_info.empty()) m_client_version = client_info.to_string();
int reqq = int(root.dict_find_int_value("reqq"));
if (reqq > 0) max_out_request_queue(reqq);
2009-11-24 19:49:59 +01:00
if (root.dict_find_int_value("upload_only", 0))
set_upload_only(true);
2008-05-18 07:59:47 +02:00
2014-07-06 21:18:00 +02:00
if (m_settings.get_bool(settings_pack::support_share_mode)
&& root.dict_find_int_value("share_mode", 0))
2010-09-05 18:01:36 +02:00
set_share_mode(true);
auto myip = root.dict_find_string_value("yourip");
if (!myip.empty())
{
if (myip.size() == address_v4::bytes_type().size())
{
address_v4::bytes_type bytes;
std::copy(myip.begin(), myip.end(), bytes.begin());
m_ses.set_external_address(address_v4(bytes)
2014-07-06 21:18:00 +02:00
, aux::session_interface::source_peer, remote().address());
}
2009-11-27 19:46:29 +01:00
#if TORRENT_USE_IPV6
else if (myip.size() == address_v6::bytes_type().size())
{
address_v6::bytes_type bytes;
std::copy(myip.begin(), myip.end(), bytes.begin());
address_v6 ipv6_address(bytes);
if (ipv6_address.is_v4_mapped())
m_ses.set_external_address(ipv6_address.to_v4()
2014-07-06 21:18:00 +02:00
, aux::session_interface::source_peer, remote().address());
else
m_ses.set_external_address(ipv6_address
2014-07-06 21:18:00 +02:00
, aux::session_interface::source_peer, remote().address());
}
2009-11-27 19:46:29 +01:00
#endif
}
2008-05-18 07:59:47 +02:00
// if we're finished and this peer is uploading only
// disconnect it
2010-09-05 06:31:13 +02:00
if (t->is_finished() && upload_only()
2014-07-06 21:18:00 +02:00
&& m_settings.get_bool(settings_pack::close_redundant_connections)
2010-09-05 18:01:36 +02:00
&& !t->share_mode())
2014-07-06 21:18:00 +02:00
disconnect(errors::upload_upload_connection, op_bittorrent);
stats_counters().inc_stats_counter(counters::num_incoming_ext_handshake);
}
#endif // TORRENT_DISABLE_EXTENSIONS
bool bt_peer_connection::dispatch_message(int const received)
{
INVARIANT_CHECK;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(received >= 0);
// this means the connection has been closed already
if (associated_torrent().expired())
{
2014-07-06 21:18:00 +02:00
received_bytes(0, received);
return false;
}
span<char const> recv_buffer = m_recv_buffer.get();
TORRENT_ASSERT(int(recv_buffer.size()) >= 1);
int packet_type = static_cast<std::uint8_t>(recv_buffer[0]);
2014-07-06 21:18:00 +02:00
if (m_settings.get_bool(settings_pack::support_merkle_torrents)
&& packet_type == 250) packet_type = msg_piece;
#if TORRENT_USE_ASSERTS
std::int64_t const cur_payload_dl = statistics().last_payload_downloaded();
std::int64_t const cur_protocol_dl = statistics().last_protocol_downloaded();
#endif
// call the handler for this packet type
switch (packet_type)
{
// original BitTorrent message
case msg_choke: on_choke(received); break;
case msg_unchoke: on_unchoke(received); break;
case msg_interested: on_interested(received); break;
case msg_not_interested: on_not_interested(received); break;
case msg_have: on_have(received); break;
case msg_bitfield: on_bitfield(received); break;
case msg_request: on_request(received); break;
case msg_piece: on_piece(received); break;
case msg_cancel: on_cancel(received); break;
// DHT extension
case msg_dht_port: on_dht_port(received); break;
// FAST extension messages
case msg_suggest_piece: on_suggest_piece(received); break;
case msg_have_all: on_have_all(received); break;
case msg_have_none: on_have_none(received); break;
case msg_reject_request: on_reject_request(received); break;
case msg_allowed_fast: on_allowed_fast(received); break;
#ifndef TORRENT_DISABLE_EXTENSIONS
case msg_extended: on_extended(received); break;
#endif
default:
{
#ifndef TORRENT_DISABLE_EXTENSIONS
for (auto const& e : m_extensions)
{
if (e->on_unknown_message(m_recv_buffer.packet_size(), packet_type
, recv_buffer.subspan(1)))
return m_recv_buffer.packet_finished();
}
#endif
received_bytes(0, received);
disconnect(errors::invalid_message, op_bittorrent);
return m_recv_buffer.packet_finished();
}
}
2015-05-19 05:13:49 +02:00
#if TORRENT_USE_ASSERTS
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(statistics().last_payload_downloaded() - cur_payload_dl >= 0);
TORRENT_ASSERT(statistics().last_protocol_downloaded() - cur_protocol_dl >= 0);
std::int64_t const stats_diff = statistics().last_payload_downloaded()
- cur_payload_dl + statistics().last_protocol_downloaded()
- cur_protocol_dl;
TORRENT_ASSERT(stats_diff == received);
#endif
bool const finished = m_recv_buffer.packet_finished();
2014-07-06 21:18:00 +02:00
if (finished)
{
// count this packet in the session stats counters
int const counter = (packet_type <= msg_dht_port)
? counters::num_incoming_choke + packet_type
: (packet_type <= msg_allowed_fast)
? counters::num_incoming_suggest + packet_type
: counters::num_incoming_extended;
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counter);
2014-07-06 21:18:00 +02:00
}
return finished;
}
2009-11-24 19:49:59 +01:00
#ifndef TORRENT_DISABLE_EXTENSIONS
void bt_peer_connection::write_upload_only()
{
INVARIANT_CHECK;
2015-05-19 05:13:49 +02:00
std::shared_ptr<torrent> t = associated_torrent().lock();
2009-11-24 19:49:59 +01:00
if (m_upload_only_id == 0) return;
2010-09-05 18:01:36 +02:00
if (t->share_mode()) return;
2009-11-24 19:49:59 +01:00
// if we send upload-only, the other end is very likely to disconnect
// us, at least if it's a seed. If we don't want to close redundant
// connections, don't sent upload-only
2014-07-06 21:18:00 +02:00
if (!m_settings.get_bool(settings_pack::close_redundant_connections)) return;
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::outgoing_message))
{
peer_log(peer_log_alert::outgoing_message, "UPLOAD_ONLY", "%d"
, int(t->is_upload_only() && !t->super_seeding()));
}
#endif
2009-11-24 19:49:59 +01:00
char msg[7] = {0, 0, 0, 3, msg_extended};
char* ptr = msg + 5;
detail::write_uint8(m_upload_only_id, ptr);
// if we're super seeding, we don't want to make peers
// think that we only have a single piece and is upload
// only, since they might disconnect immediately when
// they have downloaded a single piece, although we'll
// make another piece available
detail::write_uint8(t->is_upload_only() && !t->super_seeding(), ptr);
2009-11-24 19:49:59 +01:00
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_extended);
2009-11-24 19:49:59 +01:00
}
2010-09-05 18:01:36 +02:00
void bt_peer_connection::write_share_mode()
{
INVARIANT_CHECK;
std::shared_ptr<torrent> t = associated_torrent().lock();
2010-09-05 18:01:36 +02:00
if (m_share_mode_id == 0) return;
char msg[7] = {0, 0, 0, 3, msg_extended};
char* ptr = msg + 5;
detail::write_uint8(m_share_mode_id, ptr);
detail::write_uint8(t->share_mode(), ptr);
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_extended);
2010-09-05 18:01:36 +02:00
}
2009-11-24 19:49:59 +01:00
#endif
void bt_peer_connection::write_keepalive()
{
INVARIANT_CHECK;
2007-11-19 08:07:57 +01:00
// Don't require the bitfield to have been sent at this point
// the case where m_sent_bitfield may not be true is if the
// torrent doesn't have any metadata, and a peer is timimg out.
// then the keep-alive message will be sent before the bitfield
// this is a violation to the original protocol, but necessary
// for the metadata extension.
TORRENT_ASSERT(m_sent_handshake);
char msg[] = {0,0,0,0};
send_buffer(msg, sizeof(msg));
}
void bt_peer_connection::write_cancel(peer_request const& r)
{
INVARIANT_CHECK;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
TORRENT_ASSERT(associated_torrent().lock()->valid_metadata());
char msg[17] = {0,0,0,13, msg_cancel};
char* ptr = msg + 5;
detail::write_int32(static_cast<int>(r.piece), ptr); // index
detail::write_int32(r.start, ptr); // begin
detail::write_int32(r.length, ptr); // length
send_buffer(msg, sizeof(msg));
stats_counters().inc_stats_counter(counters::num_outgoing_cancel);
2014-07-06 21:18:00 +02:00
if (!m_supports_fast)
incoming_reject_request(r);
}
void bt_peer_connection::write_request(peer_request const& r)
{
INVARIANT_CHECK;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
TORRENT_ASSERT(associated_torrent().lock()->valid_metadata());
char msg[17] = {0,0,0,13, msg_request};
char* ptr = msg + 5;
detail::write_int32(static_cast<int>(r.piece), ptr); // index
detail::write_int32(r.start, ptr); // begin
detail::write_int32(r.length, ptr); // length
send_buffer(msg, sizeof(msg), message_type_request);
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_request);
}
void bt_peer_connection::write_bitfield()
{
INVARIANT_CHECK;
// if we have not received the other peer's extension bits yet, how do we
// know whether to send a have-all or have-none?
TORRENT_ASSERT(m_state >= state_t::read_peer_id);
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(t->valid_metadata());
2008-12-08 07:36:22 +01:00
if (t->super_seeding())
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "BITFIELD", "not sending bitfield, super seeding");
2014-07-06 21:18:00 +02:00
#endif
2008-12-08 07:36:22 +01:00
if (m_supports_fast) write_have_none();
// if we are super seeding, pretend to not have any piece
// and don't send a bitfield
m_sent_bitfield = true;
2012-04-23 07:48:46 +02:00
// bootstrap superseeding by sending two have message
piece_index_t piece = t->get_piece_to_super_seed(get_bitfield());
if (piece >= piece_index_t(0)) superseed_piece(piece_index_t(-1), piece);
2014-03-19 05:34:24 +01:00
piece = t->get_piece_to_super_seed(get_bitfield());
if (piece >= piece_index_t(0)) superseed_piece(piece_index_t(-1), piece);
2008-12-08 07:36:22 +01:00
return;
}
else if (m_supports_fast && t->is_seed())
{
write_have_all();
return;
}
else if (m_supports_fast && t->num_have() == 0)
{
write_have_none();
return;
}
else if (t->num_have() == 0)
{
// don't send a bitfield if we don't have any pieces
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "BITFIELD", "not sending bitfield, have none");
#endif
2007-12-22 20:10:38 +01:00
m_sent_bitfield = true;
return;
}
const int num_pieces = t->torrent_file().num_pieces();
TORRENT_ASSERT(num_pieces > 0);
2008-12-08 07:36:22 +01:00
2007-05-27 00:27:40 +02:00
const int packet_size = (num_pieces + 7) / 8 + 5;
2015-08-09 04:53:11 +02:00
TORRENT_ALLOCA(msg, std::uint8_t, packet_size);
if (msg.data() == nullptr) return; // out of memory
auto ptr = msg.begin();
detail::write_int32(packet_size - 4, ptr);
detail::write_uint8(msg_bitfield, ptr);
if (t->is_seed())
{
std::fill_n(ptr, packet_size - 5, 0xff);
// Clear trailing bits
msg.back() = (0xff << ((8 - (num_pieces & 7)) & 7)) & 0xff;
}
else
{
std::memset(ptr, 0, packet_size - 5);
piece_picker const& p = t->picker();
int mask = 0x80;
for (piece_index_t i(0); i < piece_index_t(num_pieces); ++i)
{
if (p.have_piece(i)) *ptr |= mask;
mask >>= 1;
if (mask == 0)
{
mask = 0x80;
++ptr;
}
}
}
2014-07-06 21:18:00 +02:00
// add predictive pieces to the bitfield as well, since we won't
// announce them again
for (piece_index_t p : t->predictive_pieces())
msg[5 + static_cast<int>(p) / 8] |= (0x80 >> (static_cast<int>(p) & 7));
2014-07-06 21:18:00 +02:00
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::outgoing_message))
{
std::string bitfield_string;
bitfield_string.resize(num_pieces);
for (int k = 0; k < num_pieces; ++k)
{
if (msg[5 + k / 8] & (0x80 >> (k % 8))) bitfield_string[k] = '1';
else bitfield_string[k] = '0';
}
peer_log(peer_log_alert::outgoing_message, "BITFIELD"
, "%s", bitfield_string.c_str());
}
#endif
m_sent_bitfield = true;
send_buffer(reinterpret_cast<char const*>(msg.data()), int(msg.size()));
2009-01-28 05:20:05 +01:00
stats_counters().inc_stats_counter(counters::num_outgoing_bitfield);
}
#ifndef TORRENT_DISABLE_EXTENSIONS
void bt_peer_connection::write_extensions()
{
INVARIANT_CHECK;
TORRENT_ASSERT(m_supports_extensions);
TORRENT_ASSERT(m_sent_handshake);
2009-11-24 19:49:59 +01:00
entry handshake;
entry::dictionary_type& m = handshake["m"].dict();
// if we're using a proxy, our listen port won't be useful
// anyway.
2014-07-06 21:18:00 +02:00
if (!m_settings.get_bool(settings_pack::force_proxy) && is_outgoing())
handshake["p"] = m_ses.listen_port();
// only send the port in case we bade the connection
// on incoming connections the other end already knows
// our listen port
2014-07-06 21:18:00 +02:00
if (!m_settings.get_bool(settings_pack::anonymous_mode))
{
2014-07-06 21:18:00 +02:00
handshake["v"] = m_settings.get_str(settings_pack::handshake_client_version).empty()
? m_settings.get_str(settings_pack::user_agent)
: m_settings.get_str(settings_pack::handshake_client_version);
}
std::string remote_address;
std::back_insert_iterator<std::string> out(remote_address);
detail::write_address(remote().address(), out);
#if TORRENT_USE_I2P
if (!is_i2p(*get_socket()))
#endif
handshake["yourip"] = remote_address;
2014-07-06 21:18:00 +02:00
handshake["reqq"] = m_settings.get_int(settings_pack::max_allowed_in_request_queue);
std::shared_ptr<torrent> t = associated_torrent().lock();
2008-05-18 07:59:47 +02:00
TORRENT_ASSERT(t);
2008-12-08 07:36:22 +01:00
2009-11-24 19:49:59 +01:00
m["upload_only"] = upload_only_msg;
2010-11-29 02:33:05 +01:00
m["ut_holepunch"] = holepunch_msg;
2014-07-06 21:18:00 +02:00
if (m_settings.get_bool(settings_pack::support_share_mode))
m["share_mode"] = share_mode_msg;
m["lt_donthave"] = dont_have_msg;
2010-11-29 02:33:05 +01:00
2010-03-19 19:39:51 +01:00
int complete_ago = -1;
if (t->last_seen_complete() > 0) complete_ago = t->time_since_complete();
handshake["complete_ago"] = complete_ago;
2009-11-24 19:49:59 +01:00
// if we're super seeding, don't say we're upload only, since it might
// make peers disconnect. don't tell anyone we're upload only when in
// share mode, we want to stay connected to seeds. if we're super seeding,
// we don't want to make peers think that we only have a single piece and
// is upload only, since they might disconnect immediately when they have
// downloaded a single piece, although we'll make another piece available.
// If we don't have metadata, we also need to suppress saying we're
// upload-only. If we do, we may be disconnected before we receive the
// metadata.
if (t->is_upload_only()
&& !t->share_mode()
&& t->valid_metadata()
&& !t->super_seeding())
{
2008-12-08 07:36:22 +01:00
handshake["upload_only"] = 1;
}
2014-07-06 21:18:00 +02:00
if (m_settings.get_bool(settings_pack::support_share_mode)
&& t->share_mode())
2010-09-05 18:01:36 +02:00
handshake["share_mode"] = 1;
// loop backwards, to make the first extension be the last
// to fill in the handshake (i.e. give the first extensions priority)
for (auto const& e : m_extensions)
{
e->add_handshake(handshake);
}
2010-11-29 02:33:05 +01:00
#ifndef NDEBUG
// make sure there are not conflicting extensions
std::set<int> ext;
for (entry::dictionary_type::const_iterator i = m.begin()
, end(m.end()); i != end; ++i)
{
if (i->second.type() != entry::int_t) continue;
int val = int(i->second.integer());
2010-11-29 02:33:05 +01:00
TORRENT_ASSERT(ext.find(val) == ext.end());
ext.insert(val);
}
#endif
std::vector<char> dict_msg;
bencode(std::back_inserter(dict_msg), handshake);
char msg[6];
char* ptr = msg;
2015-08-09 04:53:11 +02:00
// write the length of the message
2015-08-09 04:53:11 +02:00
detail::write_int32(int(dict_msg.size()) + 2, ptr);
detail::write_uint8(msg_extended, ptr);
// signal handshake message
detail::write_uint8(0, ptr);
send_buffer(msg, sizeof(msg));
send_buffer(&dict_msg[0], int(dict_msg.size()));
stats_counters().inc_stats_counter(counters::num_outgoing_ext_handshake);
2014-07-06 21:18:00 +02:00
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::outgoing_message))
{
peer_log(peer_log_alert::outgoing_message, "EXTENDED_HANDSHAKE"
, "%s", handshake.to_string().c_str());
}
#endif
}
#endif
void bt_peer_connection::write_choke()
{
INVARIANT_CHECK;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
if (is_choked()) return;
char msg[] = {0,0,0,1,msg_choke};
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_choke);
}
void bt_peer_connection::write_unchoke()
{
INVARIANT_CHECK;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
char msg[] = {0,0,0,1,msg_unchoke};
send_buffer(msg, sizeof(msg));
stats_counters().inc_stats_counter(counters::num_outgoing_unchoke);
2014-07-06 21:18:00 +02:00
#ifndef TORRENT_DISABLE_EXTENSIONS
for (auto const& e : m_extensions)
{
e->sent_unchoke();
}
#endif
}
void bt_peer_connection::write_interested()
{
INVARIANT_CHECK;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
char msg[] = {0,0,0,1,msg_interested};
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_interested);
}
void bt_peer_connection::write_not_interested()
{
INVARIANT_CHECK;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
char msg[] = {0,0,0,1,msg_not_interested};
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_not_interested);
}
void bt_peer_connection::write_have(piece_index_t index)
{
2007-06-21 02:51:42 +02:00
INVARIANT_CHECK;
TORRENT_ASSERT(associated_torrent().lock()->valid_metadata());
TORRENT_ASSERT(index >= piece_index_t(0));
TORRENT_ASSERT(index < associated_torrent().lock()->torrent_file().end_piece());
TORRENT_ASSERT(m_sent_handshake);
// if we haven't sent the bitfield yet, this piece should be included in
// there instead
if (!m_sent_bitfield) return;
char msg[] = {0,0,0,5,msg_have,0,0,0,0};
char* ptr = msg + 5;
detail::write_int32(static_cast<int>(index), ptr);
send_buffer(msg, sizeof(msg));
2014-07-06 21:18:00 +02:00
stats_counters().inc_stats_counter(counters::num_outgoing_have);
2014-07-06 21:18:00 +02:00
}
void bt_peer_connection::write_dont_have(piece_index_t const index)
2014-07-06 21:18:00 +02:00
{
#ifndef TORRENT_DISABLE_EXTENSIONS
INVARIANT_CHECK;
TORRENT_ASSERT(associated_torrent().lock()->valid_metadata());
TORRENT_ASSERT(index >= piece_index_t(0));
TORRENT_ASSERT(index < associated_torrent().lock()->torrent_file().end_piece());
2014-07-06 21:18:00 +02:00
if (in_handshake()) return;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
2014-07-06 21:18:00 +02:00
if (!m_supports_extensions || m_dont_have_id == 0) return;
char msg[] = {0,0,0,6,msg_extended,char(m_dont_have_id),0,0,0,0};
char* ptr = msg + 6;
detail::write_int32(static_cast<int>(index), ptr);
2014-07-06 21:18:00 +02:00
send_buffer(msg, sizeof(msg));
stats_counters().inc_stats_counter(counters::num_outgoing_extended);
2015-04-22 02:59:35 +02:00
#else
TORRENT_UNUSED(index);
2014-07-06 21:18:00 +02:00
#endif
}
void bt_peer_connection::write_piece(peer_request const& r, disk_buffer_holder buffer)
{
INVARIANT_CHECK;
TORRENT_ASSERT(m_sent_handshake);
TORRENT_ASSERT(m_sent_bitfield);
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
bool merkle = t->torrent_file().is_merkle_torrent() && r.start == 0;
// the hash piece looks like this:
// uint8_t msg
// uint32_t piece index
// uint32_t start
// uint32_t list len
// var bencoded list
// var piece data
char msg[4 + 1 + 4 + 4 + 4];
char* ptr = msg;
TORRENT_ASSERT(r.length <= 16 * 1024);
detail::write_int32(r.length + 1 + 4 + 4, ptr);
2014-07-06 21:18:00 +02:00
if (m_settings.get_bool(settings_pack::support_merkle_torrents) && merkle)
detail::write_uint8(250, ptr);
else
detail::write_uint8(msg_piece, ptr);
detail::write_int32(static_cast<int>(r.piece), ptr);
detail::write_int32(r.start, ptr);
// if this is a merkle torrent and the start offset
// is 0, we need to include the merkle node hashes
if (merkle)
{
2015-08-09 04:53:11 +02:00
std::vector<char> piece_list_buf;
entry piece_list;
entry::list_type& l = piece_list.list();
std::map<int, sha1_hash> merkle_node_list = t->torrent_file().build_merkle_list(r.piece);
l.reserve(merkle_node_list.size());
for (auto const& i : merkle_node_list)
{
l.push_back(entry(entry::list_t));
l.back().list().push_back(i.first);
l.back().list().push_back(i.second.to_string());
}
bencode(std::back_inserter(piece_list_buf), piece_list);
detail::write_int32(int(piece_list_buf.size()), ptr);
2015-08-09 04:53:11 +02:00
// back-patch the length field
char* ptr2 = msg;
detail::write_int32(int(r.length + 1 + 4 + 4 + 4 + piece_list_buf.size())
2015-08-09 04:53:11 +02:00
, ptr2);
send_buffer(msg, 17);
send_buffer(&piece_list_buf[0], int(piece_list_buf.size()));
}
else
{
send_buffer(msg, 13);
}
if (buffer.is_mutable())
2014-07-06 21:18:00 +02:00
{
append_send_buffer(std::move(buffer), r.length);
2014-07-06 21:18:00 +02:00
}
else
{
append_const_send_buffer(std::move(buffer), r.length);
2014-07-06 21:18:00 +02:00
}
m_payloads.push_back(range(send_buffer_size() - r.length, r.length));
setup_send();
stats_counters().inc_stats_counter(counters::num_outgoing_piece);
}
// --------------------------
// RECEIVE DATA
// --------------------------
2008-05-03 18:05:42 +02:00
void bt_peer_connection::on_receive(error_code const& error
, std::size_t bytes_transferred)
{
INVARIANT_CHECK;
if (error)
{
received_bytes(0, int(bytes_transferred));
return;
}
2014-07-06 21:18:00 +02:00
// make sure are much as possible of the response ends up in the same
// packet, or at least back-to-back packets
cork c_(*this);
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
if (!m_enc_handler.is_recv_plaintext())
{
int const consumed = m_enc_handler.decrypt(m_recv_buffer, bytes_transferred);
#ifndef TORRENT_DISABLE_LOGGING
if (consumed + bytes_transferred > 0)
peer_log(peer_log_alert::incoming_message, "ENCRYPTION"
, "decrypted block s = %d", int(consumed + bytes_transferred));
#endif
if (bytes_transferred == SIZE_MAX)
{
disconnect(errors::parse_failed, op_encryption);
return;
}
received_bytes(0, consumed);
// don't accept packets larger than 1 MB with a 1KB allowance for headers
if (!m_recv_buffer.crypto_packet_finished()
&& m_recv_buffer.crypto_packet_size() > 1025 * 1024)
{
disconnect(errors::packet_too_large, op_encryption, 2);
return;
}
int sub_transferred = 0;
while (bytes_transferred > 0 &&
((sub_transferred = m_recv_buffer.advance_pos(int(bytes_transferred))) > 0))
{
#if TORRENT_USE_ASSERTS
std::int64_t cur_payload_dl = m_statistics.last_payload_downloaded();
std::int64_t cur_protocol_dl = m_statistics.last_protocol_downloaded();
#endif
on_receive_impl(sub_transferred);
bytes_transferred -= sub_transferred;
TORRENT_ASSERT(sub_transferred > 0);
#if TORRENT_USE_ASSERTS
TORRENT_ASSERT(m_statistics.last_payload_downloaded() - cur_payload_dl >= 0);
TORRENT_ASSERT(m_statistics.last_protocol_downloaded() - cur_protocol_dl >= 0);
std::int64_t stats_diff = m_statistics.last_payload_downloaded() - cur_payload_dl +
m_statistics.last_protocol_downloaded() - cur_protocol_dl;
TORRENT_ASSERT(stats_diff == int(sub_transferred));
#endif
if (m_disconnecting) return;
}
2007-06-06 02:41:20 +02:00
}
else
2007-06-06 02:41:20 +02:00
#endif
on_receive_impl(bytes_transferred);
}
2007-06-06 02:41:20 +02:00
void bt_peer_connection::on_receive_impl(std::size_t bytes_transferred)
{
std::shared_ptr<torrent> t = associated_torrent().lock();
span<char const> recv_buffer = m_recv_buffer.get();
2007-06-06 02:41:20 +02:00
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
2007-06-06 02:41:20 +02:00
// m_state is set to read_pe_dhkey in initial state
// (read_protocol_identifier) for incoming, or in constructor
// for outgoing
if (m_state == state_t::read_pe_dhkey)
{
received_bytes(0, int(bytes_transferred));
TORRENT_ASSERT(!m_encrypted);
TORRENT_ASSERT(!m_rc4_encrypted);
TORRENT_ASSERT(m_recv_buffer.packet_size() == dh_key_len);
TORRENT_ASSERT(recv_buffer.begin() == m_recv_buffer.get().begin());
TORRENT_ASSERT(recv_buffer.size() == m_recv_buffer.get().size());
2007-06-06 02:41:20 +02:00
if (!m_recv_buffer.packet_finished()) return;
// write our dh public key. m_dh_key_exchange is
2007-06-06 02:41:20 +02:00
// initialized in write_pe1_2_dhkey()
if (!is_outgoing()) write_pe1_2_dhkey();
if (is_disconnecting()) return;
2007-06-06 02:41:20 +02:00
// read dh key, generate shared secret
m_dh_key_exchange->compute_secret(
reinterpret_cast<std::uint8_t const*>(recv_buffer.begin()));
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", "received DH key");
2007-06-06 02:41:20 +02:00
#endif
2007-06-06 02:41:20 +02:00
// PadA/B can be a max of 512 bytes, and 20 bytes more for
// the sync hash (if incoming), or 8 bytes more for the
// encrypted verification constant (if outgoing). Instead
// of requesting the maximum possible, request the maximum
// possible to ensure we do not overshoot the standard
// handshake.
if (is_outgoing())
2007-06-06 02:41:20 +02:00
{
m_state = state_t::read_pe_syncvc;
2007-06-06 02:41:20 +02:00
write_pe3_sync();
// initial payload is the standard handshake, this is
// always rc4 if sent here. m_rc4_encrypted is flagged
// again according to peer selection.
switch_send_crypto(m_rc4);
write_handshake();
switch_send_crypto(std::shared_ptr<crypto_plugin>());
2007-06-06 02:41:20 +02:00
// vc,crypto_select,len(pad),pad, encrypt(handshake)
// 8+4+2+0+handshake_len
m_recv_buffer.reset(8+4+2+0+handshake_len);
2007-06-06 02:41:20 +02:00
}
else
{
// already written dh key
m_state = state_t::read_pe_synchash;
2007-06-06 02:41:20 +02:00
// synchash,skeyhash,vc,crypto_provide,len(pad),pad,encrypt(handshake)
m_recv_buffer.reset(20+20+8+4+2+0+handshake_len);
2007-06-06 02:41:20 +02:00
}
TORRENT_ASSERT(!m_recv_buffer.packet_finished());
2007-06-06 02:41:20 +02:00
return;
}
// cannot fall through into
if (m_state == state_t::read_pe_synchash)
2007-06-06 02:41:20 +02:00
{
TORRENT_ASSERT(!m_encrypted);
TORRENT_ASSERT(!m_rc4_encrypted);
TORRENT_ASSERT(!is_outgoing());
TORRENT_ASSERT(recv_buffer.begin() == m_recv_buffer.get().begin());
TORRENT_ASSERT(recv_buffer.size() == m_recv_buffer.get().size());
if (int(recv_buffer.size()) < 20)
2007-06-06 02:41:20 +02:00
{
received_bytes(0, int(bytes_transferred));
if (m_recv_buffer.packet_finished())
2014-07-06 21:18:00 +02:00
disconnect(errors::sync_hash_not_found, op_bittorrent, 1);
2007-06-06 02:41:20 +02:00
return;
}
if (!m_sync_hash.get())
{
TORRENT_ASSERT(m_sync_bytes_read == 0);
2007-06-06 02:41:20 +02:00
static char const req1[4] = {'r', 'e', 'q', '1'};
2007-06-06 02:41:20 +02:00
// compute synchash (hash('req1',S))
std::array<char, dh_key_len> const buffer = export_key(m_dh_key_exchange->get_secret());
hasher h(req1);
h.update(buffer);
m_sync_hash.reset(new sha1_hash(h.final()));
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::info))
{
peer_log(peer_log_alert::info, "ENCRYPTION"
, "looking for synchash %s secret: %s"
, aux::to_hex(*m_sync_hash).c_str()
, aux::to_hex(buffer).c_str());
}
#endif
2007-06-06 02:41:20 +02:00
}
int const syncoffset = get_syncoffset(m_sync_hash->data(), 20
, recv_buffer.begin(), int(recv_buffer.size()));
2007-06-06 02:41:20 +02:00
2015-08-09 04:53:11 +02:00
// No sync
2007-06-06 02:41:20 +02:00
if (syncoffset == -1)
{
received_bytes(0, int(bytes_transferred));
int const bytes_processed = int(recv_buffer.size()) - 20;
m_sync_bytes_read += bytes_processed;
2007-06-06 02:41:20 +02:00
if (m_sync_bytes_read >= 512)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::sync_hash_not_found, op_encryption, 1);
return;
}
2007-06-06 02:41:20 +02:00
m_recv_buffer.cut(bytes_processed, std::min(m_recv_buffer.packet_size()
, (512 + 20) - m_sync_bytes_read));
2007-06-06 02:41:20 +02:00
TORRENT_ASSERT(!m_recv_buffer.packet_finished());
2007-06-06 02:41:20 +02:00
return;
}
// found complete sync
else
{
int const bytes_processed = syncoffset + 20;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION"
, "sync point (hash) found at offset %d"
, m_sync_bytes_read + bytes_processed - 20);
#endif
m_state = state_t::read_pe_skey_vc;
2007-06-06 02:41:20 +02:00
// skey,vc - 28 bytes
m_sync_hash.reset();
int transferred_used = int(bytes_processed - int(recv_buffer.size()) + bytes_transferred);
TORRENT_ASSERT(transferred_used <= int(bytes_transferred));
2014-07-06 21:18:00 +02:00
received_bytes(0, transferred_used);
bytes_transferred -= transferred_used;
m_recv_buffer.cut(bytes_processed, 28);
2007-06-06 02:41:20 +02:00
}
}
if (m_state == state_t::read_pe_skey_vc)
2007-06-06 02:41:20 +02:00
{
received_bytes(0, int(bytes_transferred));
bytes_transferred = 0;
TORRENT_ASSERT(!m_encrypted);
TORRENT_ASSERT(!m_rc4_encrypted);
TORRENT_ASSERT(!is_outgoing());
TORRENT_ASSERT(m_recv_buffer.packet_size() == 28);
2007-06-06 02:41:20 +02:00
if (!m_recv_buffer.packet_finished()) return;
2014-07-06 21:18:00 +02:00
if (is_disconnecting()) return;
TORRENT_ASSERT(!is_disconnecting());
2007-06-06 02:41:20 +02:00
recv_buffer = m_recv_buffer.get();
2007-06-06 02:41:20 +02:00
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(!is_disconnecting());
2007-06-06 02:41:20 +02:00
sha1_hash ih(recv_buffer.begin());
2014-07-06 21:18:00 +02:00
torrent const* ti = m_ses.find_encrypted_torrent(ih, m_dh_key_exchange->get_hash_xor_mask());
2007-06-06 02:41:20 +02:00
2014-07-06 21:18:00 +02:00
if (ti)
{
if (!t)
2007-06-06 02:41:20 +02:00
{
2015-04-21 02:23:00 +02:00
attach_to_torrent(ti->info_hash());
2014-07-06 21:18:00 +02:00
if (is_disconnecting()) return;
TORRENT_ASSERT(!is_disconnecting());
2007-06-06 02:41:20 +02:00
2014-07-06 21:18:00 +02:00
t = associated_torrent().lock();
TORRENT_ASSERT(t);
}
m_rc4 = init_pe_rc4_handler(m_dh_key_exchange->get_secret()
, ti->info_hash(), is_outgoing());
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", "computed RC4 keys");
peer_log(peer_log_alert::info, "ENCRYPTION", "stream key found, torrent located");
2007-06-06 02:41:20 +02:00
#endif
}
if (!m_rc4.get())
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_info_hash, op_bittorrent, 1);
return;
}
2007-06-06 02:41:20 +02:00
// verify constant
rc4_decrypt(m_recv_buffer.mutable_buffer().subspan(20, 8));
2007-06-06 02:41:20 +02:00
const char sh_vc[] = {0,0,0,0, 0,0,0,0};
if (!std::equal(sh_vc, sh_vc + 8, recv_buffer.begin() + 20))
2007-06-06 02:41:20 +02:00
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_encryption_constant, op_encryption, 2);
return;
}
2007-06-06 02:41:20 +02:00
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", "verification constant found");
2007-06-06 02:41:20 +02:00
#endif
m_state = state_t::read_pe_cryptofield;
m_recv_buffer.reset(4 + 2);
}
2007-06-06 02:41:20 +02:00
// cannot fall through into
if (m_state == state_t::read_pe_syncvc)
{
TORRENT_ASSERT(is_outgoing());
TORRENT_ASSERT(!m_encrypted);
TORRENT_ASSERT(!m_rc4_encrypted);
TORRENT_ASSERT(recv_buffer.begin() == m_recv_buffer.get().begin());
TORRENT_ASSERT(recv_buffer.size() == m_recv_buffer.get().size());
if (int(recv_buffer.size()) < 8)
2007-06-06 02:41:20 +02:00
{
received_bytes(0, int(bytes_transferred));
if (m_recv_buffer.packet_finished())
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_encryption_constant, op_encryption, 2);
2007-06-06 02:41:20 +02:00
return;
}
// generate the verification constant
if (!m_sync_vc.get())
2007-06-06 02:41:20 +02:00
{
TORRENT_ASSERT(m_sync_bytes_read == 0);
2007-06-06 02:41:20 +02:00
m_sync_vc.reset(new (std::nothrow) char[8]);
if (!m_sync_vc)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::no_memory, op_encryption);
return;
}
2007-06-06 02:41:20 +02:00
std::fill(m_sync_vc.get(), m_sync_vc.get() + 8, 0);
rc4_decrypt({m_sync_vc.get(), 8});
2007-06-06 02:41:20 +02:00
}
TORRENT_ASSERT(m_sync_vc.get());
int const syncoffset = get_syncoffset(m_sync_vc.get(), 8
, recv_buffer.begin(), int(recv_buffer.size()));
2007-06-06 02:41:20 +02:00
// No sync
2007-06-06 02:41:20 +02:00
if (syncoffset == -1)
{
int const bytes_processed = int(recv_buffer.size()) - 8;
m_sync_bytes_read += bytes_processed;
received_bytes(0, int(bytes_transferred));
2007-06-06 02:41:20 +02:00
if (m_sync_bytes_read >= 512)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_encryption_constant, op_encryption, 2);
return;
}
2007-06-06 02:41:20 +02:00
m_recv_buffer.cut(bytes_processed, std::min(m_recv_buffer.packet_size()
, (512 + 8) - m_sync_bytes_read));
2007-06-06 02:41:20 +02:00
TORRENT_ASSERT(!m_recv_buffer.packet_finished());
2007-06-06 02:41:20 +02:00
}
// found complete sync
else
{
int const bytes_processed = syncoffset + 8;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION"
, "sync point (verification constant) found at offset %d"
, m_sync_bytes_read + bytes_processed - 8);
2007-06-06 02:41:20 +02:00
#endif
int transferred_used = int(bytes_processed - int(recv_buffer.size()) + bytes_transferred);
TORRENT_ASSERT(transferred_used <= int(bytes_transferred));
2014-07-06 21:18:00 +02:00
received_bytes(0, transferred_used);
bytes_transferred -= transferred_used;
m_recv_buffer.cut(bytes_processed, 4 + 2);
2007-06-06 02:41:20 +02:00
// delete verification constant
m_sync_vc.reset();
m_state = state_t::read_pe_cryptofield;
2007-06-06 02:41:20 +02:00
// fall through
}
}
if (m_state == state_t::read_pe_cryptofield) // local/remote
2007-06-06 02:41:20 +02:00
{
TORRENT_ASSERT(!m_encrypted);
TORRENT_ASSERT(!m_rc4_encrypted);
TORRENT_ASSERT(m_recv_buffer.packet_size() == 4+2);
received_bytes(0, int(bytes_transferred));
bytes_transferred = 0;
if (!m_recv_buffer.packet_finished()) return;
2007-06-06 02:41:20 +02:00
// TODO: 3 this is weird buffer handling
span<char> const buf = m_recv_buffer.mutable_buffer();
TORRENT_ASSERT(int(buf.size()) >= m_recv_buffer.packet_size());
rc4_decrypt({buf.data(), size_t(m_recv_buffer.packet_size())});
2007-06-06 02:41:20 +02:00
recv_buffer = m_recv_buffer.get();
std::uint32_t crypto_field = aux::read_uint32(recv_buffer);
2007-06-06 02:41:20 +02:00
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", "crypto %s : [%s%s ]"
, is_outgoing() ? "select" : "provide"
2010-12-01 05:22:03 +01:00
, (crypto_field & 1) ? " plaintext" : ""
, (crypto_field & 2) ? " rc4" : "");
2007-06-06 02:41:20 +02:00
#endif
if (!is_outgoing())
2007-06-06 02:41:20 +02:00
{
// select a crypto method
int allowed_encryption = m_settings.get_int(settings_pack::allowed_enc_level);
std::uint32_t crypto_select = crypto_field & allowed_encryption;
2011-08-29 04:00:17 +02:00
// when prefer_rc4 is set, keep the most significant bit
// otherwise keep the least significant one
if (m_settings.get_bool(settings_pack::prefer_rc4))
2007-06-06 02:41:20 +02:00
{
std::uint32_t mask = (std::numeric_limits<std::uint32_t>::max)();
2011-08-29 04:00:17 +02:00
while (crypto_select & (mask << 1))
2007-06-06 02:41:20 +02:00
{
2011-08-29 04:00:17 +02:00
mask <<= 1;
crypto_select = crypto_select & mask;
2007-06-06 02:41:20 +02:00
}
2011-08-29 04:00:17 +02:00
}
else
{
std::uint32_t mask = (std::numeric_limits<std::uint32_t>::max)();
2011-08-29 04:00:17 +02:00
while (crypto_select & (mask >> 1))
2007-06-06 02:41:20 +02:00
{
2011-08-29 04:00:17 +02:00
mask >>= 1;
crypto_select = crypto_select & mask;
2007-06-06 02:41:20 +02:00
}
2011-08-29 04:00:17 +02:00
}
if (crypto_select == 0)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::unsupported_encryption_mode, op_encryption, 1);
return;
2011-08-29 04:00:17 +02:00
}
2007-06-06 02:41:20 +02:00
// write the pe4 step
write_pe4_sync(crypto_select);
}
else // is_outgoing()
2007-06-06 02:41:20 +02:00
{
// check if crypto select is valid
int allowed_encryption = m_settings.get_int(settings_pack::allowed_enc_level);
2007-06-06 02:41:20 +02:00
crypto_field &= allowed_encryption;
2011-08-29 04:00:17 +02:00
if (crypto_field == 0)
{
2011-08-29 04:00:17 +02:00
// we don't allow any of the offered encryption levels
2014-07-06 21:18:00 +02:00
disconnect(errors::unsupported_encryption_mode_selected, op_encryption, 2);
return;
}
2011-08-29 04:00:17 +02:00
2014-07-06 21:18:00 +02:00
if (crypto_field == settings_pack::pe_plaintext)
2011-08-29 04:00:17 +02:00
m_rc4_encrypted = false;
2014-07-06 21:18:00 +02:00
else if (crypto_field == settings_pack::pe_rc4)
2011-08-29 04:00:17 +02:00
m_rc4_encrypted = true;
2007-06-06 02:41:20 +02:00
}
int len_pad = aux::read_int16(recv_buffer);
2007-06-06 02:41:20 +02:00
if (len_pad < 0 || len_pad > 512)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_pad_size, op_encryption, 2);
return;
}
m_state = state_t::read_pe_pad;
if (!is_outgoing())
m_recv_buffer.reset(len_pad + 2); // len(IA) at the end of pad
2007-06-06 02:41:20 +02:00
else
{
if (len_pad == 0)
{
m_encrypted = true;
if (m_rc4_encrypted)
{
switch_send_crypto(m_rc4);
switch_recv_crypto(m_rc4);
}
m_state = state_t::init_bt_handshake;
2007-06-06 02:41:20 +02:00
}
else
m_recv_buffer.reset(len_pad);
2007-06-06 02:41:20 +02:00
}
}
if (m_state == state_t::read_pe_pad)
2007-06-06 02:41:20 +02:00
{
TORRENT_ASSERT(!m_encrypted);
received_bytes(0, int(bytes_transferred));
bytes_transferred = 0;
if (!m_recv_buffer.packet_finished()) return;
2007-06-06 02:41:20 +02:00
int const pad_size = is_outgoing() ? m_recv_buffer.packet_size() : m_recv_buffer.packet_size() - 2;
2007-06-06 02:41:20 +02:00
// TODO: 3 this is weird buffer handling
span<char> const buf = m_recv_buffer.mutable_buffer();
TORRENT_ASSERT(int(buf.size()) >= m_recv_buffer.packet_size());
rc4_decrypt({buf.data(), size_t(m_recv_buffer.packet_size())});
recv_buffer = m_recv_buffer.get();
2007-06-06 02:41:20 +02:00
if (!is_outgoing())
2007-06-06 02:41:20 +02:00
{
recv_buffer = recv_buffer.subspan(pad_size);
int len_ia = aux::read_int16(recv_buffer);
if (len_ia < 0)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_encrypt_handshake, op_encryption, 2);
return;
}
2007-06-07 13:04:00 +02:00
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", "len(IA) : %d", len_ia);
2007-06-06 02:41:20 +02:00
#endif
if (len_ia == 0)
{
// everything after this is Encrypt2
m_encrypted = true;
if (m_rc4_encrypted)
{
switch_send_crypto(m_rc4);
switch_recv_crypto(m_rc4);
}
m_state = state_t::init_bt_handshake;
2007-06-06 02:41:20 +02:00
}
else
{
m_state = state_t::read_pe_ia;
m_recv_buffer.reset(len_ia);
2007-06-06 02:41:20 +02:00
}
}
else // is_outgoing()
2007-06-06 02:41:20 +02:00
{
// everything that arrives after this is Encrypt2
m_encrypted = true;
if (m_rc4_encrypted)
{
switch_send_crypto(m_rc4);
switch_recv_crypto(m_rc4);
}
m_state = state_t::init_bt_handshake;
2007-06-06 02:41:20 +02:00
}
}
if (m_state == state_t::read_pe_ia)
2007-06-06 02:41:20 +02:00
{
received_bytes(0, int(bytes_transferred));
bytes_transferred = 0;
TORRENT_ASSERT(!is_outgoing());
TORRENT_ASSERT(!m_encrypted);
if (!m_recv_buffer.packet_finished()) return;
2007-06-06 02:41:20 +02:00
// ia is always rc4, so decrypt it
// TODO: 3 this is weird buffer handling
span<char> const buf = m_recv_buffer.mutable_buffer();
TORRENT_ASSERT(int(buf.size()) >= m_recv_buffer.packet_size());
rc4_decrypt({buf.data(), size_t(m_recv_buffer.packet_size())});
2007-06-06 02:41:20 +02:00
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION"
, "decrypted ia : %d bytes", m_recv_buffer.packet_size());
#endif
2007-06-06 02:41:20 +02:00
2011-08-29 04:00:17 +02:00
// everything that arrives after this is encrypted
2007-06-06 02:41:20 +02:00
m_encrypted = true;
if (m_rc4_encrypted)
{
switch_send_crypto(m_rc4);
switch_recv_crypto(m_rc4);
}
m_rc4.reset();
2007-06-06 02:41:20 +02:00
m_state = state_t::read_protocol_identifier;
m_recv_buffer.cut(0, 20);
2007-06-06 02:41:20 +02:00
}
if (m_state == state_t::init_bt_handshake)
2007-06-06 02:41:20 +02:00
{
received_bytes(0, int(bytes_transferred));
bytes_transferred = 0;
TORRENT_ASSERT(m_encrypted);
2007-06-06 02:41:20 +02:00
// decrypt remaining received bytes
if (m_rc4_encrypted)
{
span<char> const remaining = m_recv_buffer.mutable_buffer()
.subspan(m_recv_buffer.packet_size());
rc4_decrypt(remaining);
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION"
, "decrypted remaining %d bytes", int(remaining.size()));
2007-06-06 02:41:20 +02:00
#endif
}
m_rc4.reset();
2007-06-06 02:41:20 +02:00
// payload stream, start with 20 handshake bytes
m_state = state_t::read_protocol_identifier;
m_recv_buffer.reset(20);
2007-06-06 02:41:20 +02:00
// encrypted portion of handshake completed, toggle
// peer_info pe_support flag back to true
if (is_outgoing() &&
m_settings.get_int(settings_pack::out_enc_policy)
2014-07-06 21:18:00 +02:00
== settings_pack::pe_enabled)
2007-06-06 02:41:20 +02:00
{
2014-07-06 21:18:00 +02:00
torrent_peer* pi = peer_info_struct();
TORRENT_ASSERT(pi);
2015-08-13 00:54:57 +02:00
2007-06-30 09:28:44 +02:00
pi->pe_support = true;
2007-06-06 02:41:20 +02:00
}
}
#endif // #if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
2015-08-13 00:54:57 +02:00
if (m_state == state_t::read_protocol_identifier)
2007-06-06 02:41:20 +02:00
{
received_bytes(0, int(bytes_transferred));
bytes_transferred = 0;
TORRENT_ASSERT(m_recv_buffer.packet_size() == 20);
2007-06-06 02:41:20 +02:00
if (!m_recv_buffer.packet_finished()) return;
recv_buffer = m_recv_buffer.get();
2007-06-06 02:41:20 +02:00
int packet_size = recv_buffer[0];
const char protocol_string[] = "\x13" "BitTorrent protocol";
2007-06-06 02:41:20 +02:00
if (packet_size != 19 ||
memcmp(recv_buffer.begin(), protocol_string, 20) != 0)
{
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION"
, "unrecognized protocol header");
#endif
2011-09-12 05:51:49 +02:00
#ifdef TORRENT_USE_OPENSSL
if (is_ssl(*get_socket()))
2011-09-12 05:51:49 +02:00
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION"
, "SSL peers are not allowed to use any other encryption");
2011-09-12 05:51:49 +02:00
#endif
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_info_hash, op_bittorrent, 1);
2011-09-12 05:51:49 +02:00
return;
}
#endif // TORRENT_USE_OPENSSL
if (!is_outgoing()
&& m_settings.get_int(settings_pack::in_enc_policy)
2014-07-06 21:18:00 +02:00
== settings_pack::pe_disabled)
{
2014-07-06 21:18:00 +02:00
disconnect(errors::no_incoming_encrypted, op_bittorrent);
return;
}
2007-06-06 02:41:20 +02:00
// Don't attempt to perform an encrypted handshake
// within an encrypted connection. For local connections,
// we're expected to already have passed the encrypted
// handshake by this point
if (m_encrypted || is_outgoing())
2007-06-06 02:41:20 +02:00
{
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_info_hash, op_bittorrent, 1);
return;
}
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ENCRYPTION", "attempting encrypted connection");
#endif
m_state = state_t::read_pe_dhkey;
m_recv_buffer.cut(0, dh_key_len);
TORRENT_ASSERT(!m_recv_buffer.packet_finished());
return;
#else
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_info_hash, op_bittorrent, 1);
return;
#endif // TORRENT_DISABLE_ENCRYPTION
}
else
{
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
TORRENT_ASSERT(m_state != state_t::read_pe_dhkey);
2007-06-06 02:41:20 +02:00
if (!is_outgoing()
&& m_settings.get_int(settings_pack::in_enc_policy)
2014-07-06 21:18:00 +02:00
== settings_pack::pe_forced
&& !m_encrypted
&& !is_ssl(*get_socket()))
{
2014-07-06 21:18:00 +02:00
disconnect(errors::no_incoming_regular, op_bittorrent);
return;
}
2007-06-06 02:41:20 +02:00
#endif
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "HANDSHAKE", "BitTorrent protocol");
2007-06-06 02:41:20 +02:00
#endif
}
2007-06-06 02:41:20 +02:00
m_state = state_t::read_info_hash;
m_recv_buffer.reset(28);
}
2007-06-06 02:41:20 +02:00
// fall through
if (m_state == state_t::read_info_hash)
{
received_bytes(0, int(bytes_transferred));
bytes_transferred = 0;
TORRENT_ASSERT(m_recv_buffer.packet_size() == 28);
2007-06-06 02:41:20 +02:00
if (!m_recv_buffer.packet_finished()) return;
recv_buffer = m_recv_buffer.get();
2007-06-06 02:41:20 +02:00
#ifndef TORRENT_DISABLE_LOGGING
2010-12-01 05:22:03 +01:00
std::string extensions;
extensions.resize(8 * 8);
for (int i=0; i < 8; ++i)
{
for (int j=0; j < 8; ++j)
{
2010-12-01 05:22:03 +01:00
if (recv_buffer[i] & (0x80 >> j)) extensions[i*8+j] = '1';
else extensions[i*8+j] = '0';
}
}
if (should_log(peer_log_alert::incoming_message))
{
peer_log(peer_log_alert::incoming_message, "EXTENSIONS", "%s ext: %s%s%s"
, extensions.c_str()
, (recv_buffer[7] & 0x01) ? "DHT " : ""
, (recv_buffer[7] & 0x04) ? "FAST " : ""
, (recv_buffer[5] & 0x10) ? "extension " : "");
}
#endif
2011-01-29 13:13:49 +01:00
#ifndef TORRENT_DISABLE_EXTENSIONS
std::memcpy(m_reserved_bits.data(), recv_buffer.begin(), 8);
if ((recv_buffer[5] & 0x10))
m_supports_extensions = true;
#endif
if (recv_buffer[7] & 0x01)
m_supports_dht_port = true;
if (recv_buffer[7] & 0x04)
m_supports_fast = true;
2014-07-06 21:18:00 +02:00
t = associated_torrent().lock();
// ok, now we have got enough of the handshake. Is this connection
// attached to a torrent?
if (!t)
{
// 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(recv_buffer.begin() + 8, recv_buffer.begin() + 28
2015-08-09 04:53:11 +02:00
, info_hash.data());
2015-04-21 02:23:00 +02:00
attach_to_torrent(info_hash);
if (is_disconnecting()) return;
}
else
{
// verify info hash
if (!std::equal(recv_buffer.begin() + 8, recv_buffer.begin() + 28
2015-08-09 04:53:11 +02:00
, t->torrent_file().info_hash().data()))
{
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "ERROR", "received invalid info_hash");
#endif
2014-07-06 21:18:00 +02:00
disconnect(errors::invalid_info_hash, op_bittorrent, 1);
return;
}
2007-06-06 02:41:20 +02:00
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming, "HANDSHAKE", "info_hash received");
2007-06-06 02:41:20 +02:00
#endif
}
2007-06-07 12:18:13 +02:00
t = associated_torrent().lock();
TORRENT_ASSERT(t);
2015-08-09 04:53:11 +02:00
2007-06-21 02:51:42 +02:00
// if this is a local connection, we have already
// sent the handshake
if (!is_outgoing()) write_handshake();
2011-05-09 01:52:06 +02:00
TORRENT_ASSERT(m_sent_handshake);
if (is_disconnecting()) return;
m_state = state_t::read_peer_id;
m_recv_buffer.reset(20);
}
2007-06-06 02:41:20 +02:00
// fall through
if (m_state == state_t::read_peer_id)
{
2011-05-09 01:52:06 +02:00
TORRENT_ASSERT(m_sent_handshake);
received_bytes(0, int(bytes_transferred));
2014-07-06 21:18:00 +02:00
t = associated_torrent().lock();
if (!t)
2007-06-06 02:41:20 +02:00
{
TORRENT_ASSERT(!m_recv_buffer.packet_finished()); // TODO
2007-06-06 02:41:20 +02:00
return;
}
TORRENT_ASSERT(m_recv_buffer.packet_size() == 20);
if (!m_recv_buffer.packet_finished()) return;
recv_buffer = m_recv_buffer.get();
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::incoming))
{
2009-04-04 11:52:25 +02:00
char hex_pid[41];
aux::to_hex({recv_buffer.data(), 20}, hex_pid);
2011-11-28 12:11:29 +01:00
hex_pid[40] = 0;
2009-04-04 11:52:25 +02:00
char ascii_pid[21];
2011-11-28 12:11:29 +01:00
ascii_pid[20] = 0;
2009-04-04 11:52:25 +02:00
for (int i = 0; i != 20; ++i)
{
if (is_print(recv_buffer[i])) ascii_pid[i] = recv_buffer[i];
2009-04-04 11:52:25 +02:00
else ascii_pid[i] = '.';
}
peer_log(peer_log_alert::incoming, "HANDSHAKE", "received peer_id: %s client: %s ascii: \"%s\""
, hex_pid, identify_client(peer_id(recv_buffer.begin())).c_str(), ascii_pid);
}
#endif
peer_id pid;
std::copy(recv_buffer.begin(), recv_buffer.begin() + 20, pid.data());
2014-07-06 21:18:00 +02:00
if (t->settings().get_bool(settings_pack::allow_multiple_connections_per_ip))
{
// now, let's see if this connection should be closed
2014-07-06 21:18:00 +02:00
peer_connection* p = t->find_peer(pid);
if (p)
{
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(p->pid() == pid);
// we found another connection with the same peer-id
// which connection should be closed in order to be
// sure that the other end closes the same connection?
// the peer with greatest peer-id is the one allowed to
// initiate connections. So, if our peer-id is greater than
// the others, we should close the incoming connection,
// if not, we should close the outgoing one.
2014-09-13 21:47:51 +02:00
if (pid < m_our_peer_id && is_outgoing())
{
2014-07-06 21:18:00 +02:00
p->disconnect(errors::duplicate_peer_id, op_bittorrent);
}
else
{
2014-07-06 21:18:00 +02:00
disconnect(errors::duplicate_peer_id, op_bittorrent);
return;
}
}
}
2014-07-06 21:18:00 +02:00
set_pid(pid);
2009-05-23 23:42:29 +02:00
// disconnect if the peer has the same peer-id as ourself
// since it most likely is ourself then
if (pid == m_our_peer_id)
{
2014-07-06 21:18:00 +02:00
if (peer_info_struct()) t->ban_peer(peer_info_struct());
disconnect(errors::self_connection, op_bittorrent, 1);
return;
}
m_client_version = identify_client(pid);
if (pid[0] == '-' && pid[1] == 'B' && pid[2] == 'C' && pid[7] == '-')
{
// if this is a bitcomet client, lower the request queue size limit
if (max_out_request_queue() > 50) max_out_request_queue(50);
}
#ifndef TORRENT_DISABLE_EXTENSIONS
for (auto i = m_extensions.begin()
, end(m_extensions.end()); i != end;)
{
2008-05-12 05:05:27 +02:00
if (!(*i)->on_handshake(m_reserved_bits))
{
i = m_extensions.erase(i);
}
else
{
++i;
}
}
2008-05-12 05:05:27 +02:00
if (is_disconnecting()) return;
if (m_supports_extensions) write_extensions();
#endif
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::incoming_message, "HANDSHAKE", "connection ready");
2007-06-06 02:41:20 +02:00
#endif
// consider this a successful connection, reset the failcount
2014-07-06 21:18:00 +02:00
if (peer_info_struct())
t->clear_failcount(peer_info_struct());
2015-08-13 00:54:57 +02:00
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
2007-06-06 02:41:20 +02:00
// Toggle pe_support back to false if this is a
// standard successful connection
if (is_outgoing() && !m_encrypted &&
m_settings.get_int(settings_pack::out_enc_policy)
2014-07-06 21:18:00 +02:00
== settings_pack::pe_enabled)
2007-06-06 02:41:20 +02:00
{
2014-07-06 21:18:00 +02:00
torrent_peer* pi = peer_info_struct();
TORRENT_ASSERT(pi);
2007-06-06 02:41:20 +02:00
2007-06-30 09:28:44 +02:00
pi->pe_support = false;
2007-06-06 02:41:20 +02:00
}
#endif
// complete the handshake
// we don't know how many pieces there are until we
// have the metadata
if (t->ready_for_connections())
{
write_bitfield();
write_dht_port();
// if we don't have any pieces, don't do any preemptive
// unchoking at all.
if (t->num_have() > 0)
{
// if the peer is ignoring unchoke slots, or if we have enough
// unused slots, unchoke this peer right away, to save a round-trip
// in case it's interested.
maybe_unchoke_this_peer();
}
}
m_state = state_t::read_packet_size;
m_recv_buffer.reset(5);
2007-06-06 02:41:20 +02:00
TORRENT_ASSERT(!m_recv_buffer.packet_finished());
2007-06-06 02:41:20 +02:00
return;
}
2007-06-06 02:41:20 +02:00
// cannot fall through into
if (m_state == state_t::read_packet_size)
{
2007-06-06 02:41:20 +02:00
// Make sure this is not fallen though into
TORRENT_ASSERT(recv_buffer.begin() == m_recv_buffer.get().begin());
TORRENT_ASSERT(recv_buffer.size() == m_recv_buffer.get().size());
TORRENT_ASSERT(m_recv_buffer.packet_size() == 5);
2007-06-06 02:41:20 +02:00
if (!t) return;
2014-07-06 21:18:00 +02:00
// the 5th byte (if one) should not count as protocol
// byte here, instead it's counted in the message
// handler itself, for the specific message
TORRENT_ASSERT(bytes_transferred <= 5);
int used_bytes = int(recv_buffer.size()) > 4 ? int(bytes_transferred) - 1: int(bytes_transferred);
2014-07-06 21:18:00 +02:00
received_bytes(0, used_bytes);
bytes_transferred -= used_bytes;
if (int(recv_buffer.size()) < 4) return;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(bytes_transferred <= 1);
const char* ptr = recv_buffer.begin();
int packet_size = detail::read_int32(ptr);
// don't accept packets larger than 1 MB
if (packet_size > 1024*1024 || packet_size < 0)
{
// packet too large
received_bytes(0, int(bytes_transferred));
2014-07-06 21:18:00 +02:00
disconnect(errors::packet_too_large, op_bittorrent, 2);
return;
}
2014-07-06 21:18:00 +02:00
if (packet_size == 0)
{
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(bytes_transferred <= 1);
received_bytes(0, int(bytes_transferred));
incoming_keepalive();
2008-05-12 05:05:27 +02:00
if (is_disconnecting()) return;
// keepalive message
m_state = state_t::read_packet_size;
m_recv_buffer.cut(4, 5);
return;
}
if (int(recv_buffer.size()) < 5) return;
m_state = state_t::read_packet;
m_recv_buffer.cut(4, packet_size);
recv_buffer = m_recv_buffer.get();
TORRENT_ASSERT(int(recv_buffer.size()) == 1);
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(bytes_transferred == 1);
}
if (m_state == state_t::read_packet)
{
TORRENT_ASSERT(recv_buffer.begin() == m_recv_buffer.get().begin());
TORRENT_ASSERT(recv_buffer.size() == m_recv_buffer.get().size());
2008-10-09 05:33:53 +02:00
if (!t)
{
received_bytes(0, int(bytes_transferred));
2014-07-06 21:18:00 +02:00
disconnect(errors::torrent_removed, op_bittorrent, 1);
2008-10-09 05:33:53 +02:00
return;
}
2015-05-19 05:13:49 +02:00
#if TORRENT_USE_ASSERTS
std::int64_t const cur_payload_dl = statistics().last_payload_downloaded();
std::int64_t const cur_protocol_dl = statistics().last_protocol_downloaded();
2008-10-09 05:33:53 +02:00
#endif
if (dispatch_message(int(bytes_transferred)))
{
m_state = state_t::read_packet_size;
m_recv_buffer.reset(5);
}
2015-05-19 05:13:49 +02:00
#if TORRENT_USE_ASSERTS
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(statistics().last_payload_downloaded() - cur_payload_dl >= 0);
TORRENT_ASSERT(statistics().last_protocol_downloaded() - cur_protocol_dl >= 0);
std::int64_t const stats_diff = statistics().last_payload_downloaded() - cur_payload_dl +
2014-07-06 21:18:00 +02:00
statistics().last_protocol_downloaded() - cur_protocol_dl;
TORRENT_ASSERT(stats_diff == std::int64_t(bytes_transferred));
TORRENT_ASSERT(!m_recv_buffer.packet_finished());
2015-05-19 05:13:49 +02:00
#endif
2007-06-06 02:41:20 +02:00
return;
}
TORRENT_ASSERT(!m_recv_buffer.packet_finished());
}
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
std::tuple<int, span<span<char const>>>
bt_peer_connection::hit_send_barrier(
span<span<char>> iovec)
{
int next_barrier;
span<span<char const>> out_iovec;
std::tie(next_barrier, out_iovec) = m_enc_handler.encrypt(iovec);
#ifndef TORRENT_DISABLE_LOGGING
if (next_barrier != 0)
peer_log(peer_log_alert::outgoing, "SEND_BARRIER"
, "encrypted block s = %d", next_barrier);
#endif
return std::make_tuple(next_barrier, out_iovec);
}
#endif
// --------------------------
// SEND DATA
// --------------------------
2008-05-03 18:05:42 +02:00
void bt_peer_connection::on_sent(error_code const& error
, std::size_t bytes_transferred)
{
INVARIANT_CHECK;
if (error)
{
sent_bytes(0, int(bytes_transferred));
return;
}
// manage the payload markers
int amount_payload = 0;
if (!m_payloads.empty())
{
2014-07-06 21:18:00 +02:00
// this points to the first entry to not erase. i.e.
// [begin, first_to_keep) will be erased because
// the payload ranges they represent have been sent
std::vector<range>::iterator first_to_keep = m_payloads.begin();
for (std::vector<range>::iterator i = m_payloads.begin();
i != m_payloads.end(); ++i)
{
i->start -= int(bytes_transferred);
if (i->start < 0)
{
if (i->start + i->length <= 0)
{
amount_payload += i->length;
2014-07-06 21:18:00 +02:00
TORRENT_ASSERT(first_to_keep == i);
++first_to_keep;
}
else
{
amount_payload += -i->start;
i->length -= -i->start;
i->start = 0;
}
}
}
2014-07-06 21:18:00 +02:00
// remove all payload ranges that have been sent
m_payloads.erase(m_payloads.begin(), first_to_keep);
}
2015-08-09 04:53:11 +02:00
TORRENT_ASSERT(amount_payload <= int(bytes_transferred));
sent_bytes(amount_payload, int(bytes_transferred) - amount_payload);
2015-08-09 04:53:11 +02:00
if (amount_payload > 0)
{
std::shared_ptr<torrent> t = associated_torrent().lock();
TORRENT_ASSERT(t);
if (t) t->update_last_upload();
}
}
2014-01-21 20:26:09 +01:00
#if TORRENT_USE_INVARIANT_CHECKS
void bt_peer_connection::check_invariant() const
{
std::shared_ptr<torrent> t = associated_torrent().lock();
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
TORRENT_ASSERT( (bool(m_state != state_t::read_pe_dhkey) || m_dh_key_exchange.get())
|| !is_outgoing());
2007-06-07 12:18:13 +02:00
TORRENT_ASSERT(!m_rc4_encrypted || (!m_encrypted && m_rc4)
|| (m_encrypted && !m_enc_handler.is_send_plaintext()));
2007-06-06 02:41:20 +02:00
#endif
2007-11-19 08:07:57 +01:00
if (!in_handshake())
{
TORRENT_ASSERT(m_sent_handshake);
}
if (!m_payloads.empty())
{
for (std::vector<range>::const_iterator i = m_payloads.begin();
i != m_payloads.end() - 1; ++i)
{
TORRENT_ASSERT(i->start + i->length <= (i+1)->start);
}
}
}
#endif
}