implemented utp extension header to indicate the reason to close the connection

This commit is contained in:
Arvid Norberg 2015-02-28 19:51:15 +00:00
parent c4b112f23a
commit 0c8aee014c
15 changed files with 534 additions and 40 deletions

View File

@ -17,6 +17,7 @@ set(sources
bloom_filter
chained_buffer
choker
close_reason
crc32c
create_torrent
disk_buffer_holder

View File

@ -533,6 +533,7 @@ SOURCES =
bloom_filter
chained_buffer
choker
close_reason
crc32c
create_torrent
disk_buffer_holder

View File

@ -26,6 +26,7 @@ nobase_include_HEADERS = \
byteswap.hpp \
chained_buffer.hpp \
choker.hpp \
close_reason.hpp \
config.hpp \
ConvertUTF.h \
copy_ptr.hpp \

View File

@ -43,6 +43,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/stat.hpp"
#include "libtorrent/rss.hpp" // for feed_handle
#include "libtorrent/operations.hpp" // for operation_t enum
#include "libtorrent/close_reason.hpp"
namespace libtorrent
{
@ -703,11 +704,13 @@ namespace libtorrent
{
// internal
peer_disconnected_alert(torrent_handle const& h, tcp::endpoint const& ep
, peer_id const& peer_id, operation_t op, int type, error_code const& e)
, peer_id const& peer_id, operation_t op, int type, error_code const& e
, close_reason_t r)
: peer_alert(h, ep, peer_id)
, socket_type(type)
, operation(op)
, error(e)
, reason(r)
{
#ifndef TORRENT_NO_DEPRECATE
msg = convert_from_native(error.message());
@ -729,6 +732,9 @@ namespace libtorrent
// tells you what error caused peer to disconnect.
error_code error;
// the reason the peer disconnected (if specified)
close_reason_t reason;
#ifndef TORRENT_NO_DEPRECATE
std::string msg;
#endif

View File

@ -0,0 +1,158 @@
/*
Copyright (c) 2015, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_CLOSE_REASON_HPP
#define TORRENT_CLOSE_REASON_HPP
#include "libtorrent/error_code.hpp"
namespace libtorrent
{
// these are all the reasons to disconnect a peer
// all reasons caused by the peer sending unexpected data
// are 256 and up.
enum close_reason_t
{
// no reason specified. Generic close.
close_no_reason = 0,
// we're already connected to
close_duplicate_peer_id,
// this torrent has been removed, paused or stopped from this client.
close_torrent_removed,
// client failed to allocate necessary memory for this peer connection
close_no_memory,
// the source port of this peer is blocked
close_port_blocked,
// the source IP has been blocked
close_blocked,
// both ends of the connection are upload-only. staying connected would
// be redundant
close_upload_to_upload,
// connection was closed because the other end is upload only and does
// not have any pieces we're interested in
close_not_interested_upload_only,
// peer connection timed out (generic timeout)
close_timeout,
// the peers have not been interested in each other for a very long time.
// disconnect
close_timed_out_interest,
// the peer has not sent any message in a long time.
close_timed_out_activity,
// the peer did not complete the handshake in too long
close_timed_out_handshake,
// the peer sent an interested message, but did not send a request
// after a very long time after being unchoked.
close_timed_out_request,
// the encryption mode is blocked
close_protocol_blocked,
// the peer was disconnected in the hopes of finding a better peer
// in the swarm
close_peer_churn,
// we have too many peers connected
close_too_many_connections,
// we have too many file-descriptors open
close_too_many_files,
// the encryption handshake failed
close_encryption_error = 256,
// the info hash sent as part of the handshake was not what we expected
close_invalid_info_hash,
close_self_connection,
// the metadata received matched the info-hash, but failed to parse.
// this is either someone finding a SHA1 collision, or the author of
// the magnet link creating it from an invalid torrent
close_invalid_metadata,
// the advertised metadata size
close_metadata_too_big,
// invalid bittorrent messages
close_message_too_big,
close_invalid_message_id,
close_invalid_message,
close_invalid_piece_message,
close_invalid_have_message,
close_invalid_bitfield_message,
close_invalid_choke_message,
close_invalid_unchoke_message,
close_invalid_interested_message,
close_invalid_not_interested_message,
close_invalid_request_message,
close_invalid_reject_message,
close_invalid_allow_fast_message,
close_invalid_extended_message,
close_invalid_cancel_message,
close_invalid_dht_port_message,
close_invalid_suggest_message,
close_invalid_have_all_message,
close_invalid_dont_have_message,
close_invalid_have_none_message,
close_invalid_pex_message,
close_invalid_metadata_request_message,
close_invalid_metadata_message,
close_invalid_metadata_offset,
// the peer sent a request while being choked
close_request_when_choked,
// the peer sent corrupt data
close_corrupt_pieces,
close_pex_message_too_big,
close_pex_too_frequent,
};
close_reason_t error_to_close_reason(error_code const& ec);
}
#endif

View File

@ -202,6 +202,11 @@ namespace libtorrent
void open(protocol_type const& p, error_code& ec);
void close(error_code& ec);
// this is only relevant for uTP connections
void set_close_reason(boost::uint16_t code);
boost::uint16_t get_close_reason();
endpoint_type local_endpoint(error_code& ec) const;
endpoint_type remote_endpoint(error_code& ec) const;
void bind(endpoint_type const& endpoint, error_code& ec);

View File

@ -121,6 +121,11 @@ namespace libtorrent
enum utp_socket_state_t
{ ST_DATA, ST_FIN, ST_STATE, ST_RESET, ST_SYN, NUM_TYPES };
// extension headers. 2 is skipped because there is a deprecated
// extension with that number in the wild
enum utp_extensions_t
{ utp_no_extension = 0, utp_sack = 1, utp_close_reason = 3 };
struct utp_header
{
unsigned char type_ver;
@ -215,7 +220,8 @@ public:
#endif
template <class GettableSocketOption>
error_code get_option(GettableSocketOption&, error_code& ec) { return ec; }
error_code get_option(GettableSocketOption&, error_code& ec)
{ return ec; }
error_code cancel(error_code& ec)
{
@ -225,12 +231,19 @@ public:
void close();
void close(error_code const& /*ec*/) { close(); }
void set_close_reason(boost::uint16_t code);
boost::uint16_t get_close_reason();
bool is_open() const { return m_open; }
int read_buffer_size() const;
static void on_read(void* self, size_t bytes_transferred, error_code const& ec, bool kill);
static void on_write(void* self, size_t bytes_transferred, error_code const& ec, bool kill);
static void on_read(void* self, size_t bytes_transferred
, error_code const& ec, bool kill);
static void on_write(void* self, size_t bytes_transferred
, error_code const& ec, bool kill);
static void on_connect(void* self, error_code const& ec, bool kill);
static void on_close_reason(void* self, boost::uint16_t reason);
void add_read_buffer(void* buf, size_t len);
void issue_read();
@ -421,14 +434,16 @@ public:
{
if (m_impl == 0)
{
m_io_service.post(boost::bind<void>(handler, asio::error::not_connected, 0));
m_io_service.post(boost::bind<void>(handler
, asio::error::not_connected, 0));
return;
}
TORRENT_ASSERT(!m_write_handler);
if (m_write_handler)
{
m_io_service.post(boost::bind<void>(handler, asio::error::operation_not_supported, 0));
m_io_service.post(boost::bind<void>(handler
, asio::error::operation_not_supported, 0));
return;
}
@ -453,7 +468,7 @@ public:
issue_write();
}
//private:
private:
void cancel_handlers(error_code const&);
@ -464,6 +479,8 @@ public:
asio::io_service& m_io_service;
utp_socket_impl* m_impl;
boost::uint16_t m_incoming_close_reason;
// this field requires another 8 bytes (including padding)
bool m_open;
};

View File

@ -49,6 +49,7 @@ libtorrent_rasterbar_la_SOURCES = \
bt_peer_connection.cpp \
chained_buffer.cpp \
choker.cpp \
close_reason.cpp \
ConvertUTF.cpp \
crc32c.cpp \
create_torrent.cpp \

View File

@ -575,8 +575,10 @@ namespace libtorrent {
std::string peer_error_alert::message() const
{
char msg[200];
snprintf(msg, sizeof(msg), "%s peer error [%s] [%s]: %s", peer_alert::message().c_str()
, operation_name(operation), error.category().name(), convert_from_native(error.message()).c_str());
snprintf(msg, sizeof(msg), "%s peer error [%s] [%s]: %s"
, peer_alert::message().c_str()
, operation_name(operation), error.category().name()
, convert_from_native(error.message()).c_str());
return msg;
}
@ -630,11 +632,12 @@ namespace libtorrent {
std::string peer_disconnected_alert::message() const
{
char msg[600];
snprintf(msg, sizeof(msg), "%s disconnecting (%s) [%s] [%s]: %s"
snprintf(msg, sizeof(msg), "%s disconnecting (%s) [%s] [%s]: %s (reason: %d)"
, peer_alert::message().c_str()
, socket_type_str[socket_type]
, operation_name(operation), error.category().name()
, convert_from_native(error.message()).c_str());
, convert_from_native(error.message()).c_str()
, int(reason));
return msg;
}

View File

@ -644,8 +644,6 @@ namespace libtorrent
TORRENT_ASSERT(!m_rc4.get());
m_rc4 = boost::make_shared<rc4_handler>();
m_rc4->set_incoming_key(&remote_key[0], 20);
m_rc4->set_outgoing_key(&local_key[0], 20);
if (!m_rc4)
{
@ -653,6 +651,9 @@ namespace libtorrent
return;
}
m_rc4->set_incoming_key(&remote_key[0], 20);
m_rc4->set_outgoing_key(&local_key[0], 20);
#ifdef TORRENT_LOGGING
peer_log(" computed RC4 keys");
#endif

182
src/close_reason.cpp Normal file
View File

@ -0,0 +1,182 @@
/*
Copyright (c) 2015, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/close_reason.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/assert.hpp"
#include <boost/asio/error.hpp>
namespace libtorrent
{
close_reason_t error_to_close_reason(error_code const& ec)
{
if (ec.category() == get_libtorrent_category())
{
#define TORRENT_MAP(error, close_reason) \
case errors:: error : \
return close_reason;
switch (ec.value())
{
TORRENT_MAP(invalid_swarm_metadata, close_invalid_metadata)
TORRENT_MAP(session_is_closing, close_torrent_removed)
TORRENT_MAP(peer_sent_empty_piece, close_invalid_piece_message)
TORRENT_MAP(mismatching_info_hash, close_invalid_info_hash)
TORRENT_MAP(port_blocked, close_port_blocked)
TORRENT_MAP(destructing_torrent, close_torrent_removed)
TORRENT_MAP(timed_out, close_timeout)
TORRENT_MAP(upload_upload_connection, close_upload_to_upload)
TORRENT_MAP(uninteresting_upload_peer, close_not_interested_upload_only)
TORRENT_MAP(invalid_info_hash, close_invalid_info_hash)
TORRENT_MAP(torrent_paused, close_torrent_removed)
TORRENT_MAP(invalid_have, close_invalid_have_message)
TORRENT_MAP(invalid_bitfield_size, close_invalid_bitfield_message)
TORRENT_MAP(too_many_requests_when_choked, close_request_when_choked)
TORRENT_MAP(invalid_piece, close_invalid_piece_message)
TORRENT_MAP(invalid_piece_size, close_invalid_piece_message)
TORRENT_MAP(no_memory, close_no_memory)
TORRENT_MAP(torrent_aborted, close_torrent_removed)
TORRENT_MAP(self_connection, close_self_connection)
TORRENT_MAP(timed_out_no_interest, close_timed_out_interest)
TORRENT_MAP(timed_out_inactivity, close_timed_out_activity)
TORRENT_MAP(timed_out_no_handshake, close_timed_out_handshake)
TORRENT_MAP(timed_out_no_request, close_timed_out_request)
TORRENT_MAP(invalid_choke, close_invalid_choke_message)
TORRENT_MAP(invalid_unchoke, close_invalid_unchoke_message)
TORRENT_MAP(invalid_interested, close_invalid_interested_message)
TORRENT_MAP(invalid_not_interested, close_invalid_not_interested_message)
TORRENT_MAP(invalid_request, close_invalid_request_message)
TORRENT_MAP(invalid_hash_list, close_invalid_message)
TORRENT_MAP(invalid_hash_piece, close_invalid_message)
TORRENT_MAP(invalid_cancel, close_invalid_cancel_message)
TORRENT_MAP(invalid_dht_port, close_invalid_dht_port_message)
TORRENT_MAP(invalid_suggest, close_invalid_suggest_message)
TORRENT_MAP(invalid_have_all, close_invalid_have_all_message)
TORRENT_MAP(invalid_have_none, close_invalid_have_none_message)
TORRENT_MAP(invalid_reject, close_invalid_reject_message)
TORRENT_MAP(invalid_allow_fast, close_invalid_allow_fast_message)
TORRENT_MAP(invalid_extended, close_invalid_extended_message)
TORRENT_MAP(invalid_message, close_invalid_message_id)
TORRENT_MAP(sync_hash_not_found, close_encryption_error)
TORRENT_MAP(invalid_encryption_constant, close_encryption_error)
TORRENT_MAP(no_plaintext_mode, close_protocol_blocked)
TORRENT_MAP(no_rc4_mode, close_protocol_blocked)
TORRENT_MAP(unsupported_encryption_mode_selected, close_protocol_blocked)
TORRENT_MAP(invalid_pad_size, close_encryption_error)
TORRENT_MAP(invalid_encrypt_handshake, close_encryption_error)
TORRENT_MAP(no_incoming_encrypted, close_protocol_blocked)
TORRENT_MAP(no_incoming_regular, close_protocol_blocked)
TORRENT_MAP(duplicate_peer_id, close_duplicate_peer_id)
TORRENT_MAP(torrent_removed, close_torrent_removed)
TORRENT_MAP(packet_too_large, close_message_too_big)
TORRENT_MAP(torrent_not_ready, close_torrent_removed)
TORRENT_MAP(session_closing, close_torrent_removed)
TORRENT_MAP(optimistic_disconnect, close_peer_churn)
TORRENT_MAP(torrent_finished, close_upload_to_upload)
TORRENT_MAP(too_many_corrupt_pieces, close_corrupt_pieces)
TORRENT_MAP(too_many_connections, close_too_many_connections)
TORRENT_MAP(peer_banned, close_blocked)
TORRENT_MAP(stopping_torrent, close_torrent_removed)
TORRENT_MAP(metadata_too_large, close_metadata_too_big)
TORRENT_MAP(invalid_metadata_size, close_metadata_too_big)
TORRENT_MAP(invalid_metadata_request, close_invalid_metadata_request_message)
TORRENT_MAP(invalid_metadata_offset, close_invalid_metadata_offset)
TORRENT_MAP(invalid_metadata_message, close_invalid_metadata_message)
TORRENT_MAP(pex_message_too_large, close_pex_message_too_big)
TORRENT_MAP(invalid_pex_message, close_invalid_pex_message)
TORRENT_MAP(invalid_lt_tracker_message, close_invalid_message)
TORRENT_MAP(too_frequent_pex, close_pex_too_frequent)
TORRENT_MAP(invalid_dont_have, close_invalid_dont_have_message)
TORRENT_MAP(requires_ssl_connection, close_protocol_blocked)
TORRENT_MAP(invalid_ssl_cert, close_blocked)
TORRENT_MAP(not_an_ssl_torrent, close_blocked)
TORRENT_MAP(banned_by_port_filter, close_port_blocked)
#ifdef TORRENT_USE_ASSERTS
case errors::redirecting:
return close_no_reason;
#endif
default:
// we should include this error code in the map
fprintf(stderr, "(%s : %d) %s\n", ec.category().name(), ec.value()
, ec.message().c_str());
TORRENT_ASSERT(false);
return close_no_reason;
}
}
else if (ec.category() == boost::asio::error::get_misc_category())
{
switch (ec.value())
{
case boost::asio::error::eof:
return close_no_reason;
}
}
else if (ec.category() == boost::system::system_category())
{
switch (ec.value())
{
#ifdef TORRENT_USE_ASSERTS
case boost::system::errc::connection_reset:
case boost::system::errc::broken_pipe:
return close_no_reason;
#endif
case boost::system::errc::timed_out:
return close_timeout;
case boost::system::errc::too_many_files_open:
case boost::system::errc::too_many_files_open_in_system:
return close_too_many_files;
case boost::system::errc::not_enough_memory:
case boost::system::errc::no_buffer_space:
return close_no_memory;
}
}
else if (ec.category() == get_http_category())
{
return close_no_memory;
}
fprintf(stderr, "(%s : %d) %s\n", ec.category().name(), ec.value()
, ec.message().c_str());
// we should proboably include this in the map
TORRENT_ASSERT(false);
// TODO: map some system errors too?
return close_no_reason;
}
}

View File

@ -70,6 +70,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/alert_manager.hpp" // for alert_manageralert_manager
#include "libtorrent/ip_filter.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/close_reason.hpp"
#ifdef TORRENT_DEBUG
#include <set>
@ -2290,8 +2291,13 @@ namespace libtorrent
// every ten invalid request, remind the peer that it's choked
if (!m_peer_interested && m_num_invalid_requests % 10 == 0 && m_choked)
{
// TODO: 2 this should probably be based on time instead of number
// of request messages. For a very high throughput connection, 300
// may be a legitimate number of requests to have in flight when
// getting choked
if (m_num_invalid_requests > 300 && !m_peer_choked
&& can_disconnect(error_code(errors::too_many_requests_when_choked, get_libtorrent_category())))
&& can_disconnect(error_code(errors::too_many_requests_when_choked
, get_libtorrent_category())))
{
disconnect(errors::too_many_requests_when_choked, op_bittorrent, 2);
return;
@ -3908,6 +3914,15 @@ namespace libtorrent
if (m_disconnecting) return;
m_socket->set_close_reason(error_to_close_reason(ec));
close_reason_t close_reason = (close_reason_t)m_socket->get_close_reason();
#if defined TORRENT_LOGGING
if (close_reason != 0)
{
peer_log("*** CLOSE REASON [ %d ]", int(close_reason));
}
#endif
// while being disconnected, it's possible that our torrent_peer
// pointer gets cleared. Make sure we save it to be able to keep
// proper books in the piece_picker (when debugging is enabled)
@ -4078,11 +4093,12 @@ namespace libtorrent
t->alerts().post_alert(
peer_error_alert(handle, remote(), pid(), op, ec));
}
else if (error <= 1 && t->alerts().should_post<peer_disconnected_alert>())
if (error <= 1 && t->alerts().should_post<peer_disconnected_alert>())
{
t->alerts().post_alert(
peer_disconnected_alert(handle, remote(), pid(), op
, m_socket->type(), ec));
, m_socket->type(), ec, close_reason));
}
}
@ -4657,7 +4673,7 @@ namespace libtorrent
&& d2 > time_limit
&& (m_ses.num_connections() >= m_settings.get_int(settings_pack::connections_limit)
|| (t && t->num_peers() >= t->max_connections()))
&& can_disconnect(error_code(errors::timed_out_no_interest, get_libtorrent_category())))
&& can_disconnect(error_code(errors::timed_out_no_interest)))
{
#if defined TORRENT_LOGGING
peer_log("*** MUTUAL NO INTEREST [ t1: %d t2: %d ]"
@ -6404,12 +6420,12 @@ namespace libtorrent
if (m_settings.get_bool(settings_pack::close_redundant_connections) && !t->share_mode())
{
bool ok_to_disconnect =
can_disconnect(error_code(errors::upload_upload_connection, get_libtorrent_category()))
|| can_disconnect(error_code(errors::uninteresting_upload_peer, get_libtorrent_category()))
|| can_disconnect(error_code(errors::too_many_requests_when_choked, get_libtorrent_category()))
|| can_disconnect(error_code(errors::timed_out_no_interest, get_libtorrent_category()))
|| can_disconnect(error_code(errors::timed_out_no_request, get_libtorrent_category()))
|| can_disconnect(error_code(errors::timed_out_inactivity, get_libtorrent_category()));
can_disconnect(error_code(errors::upload_upload_connection))
|| can_disconnect(error_code(errors::uninteresting_upload_peer))
|| can_disconnect(error_code(errors::too_many_requests_when_choked))
|| can_disconnect(error_code(errors::timed_out_no_interest))
|| can_disconnect(error_code(errors::timed_out_no_request))
|| can_disconnect(error_code(errors::timed_out_inactivity));
// make sure upload only peers are disconnected
if (t->is_upload_only()

View File

@ -2513,7 +2513,8 @@ retry:
m_alerts.post_alert(
peer_disconnected_alert(torrent_handle(), endp, peer_id()
, op_bittorrent, s->type()
, error_code(errors::too_many_connections, get_libtorrent_category())));
, error_code(errors::too_many_connections, get_libtorrent_category())
, close_no_reason));
}
#if defined TORRENT_LOGGING
session_log("number of connections limit exceeded (conns: %d, limit: %d, slack: %d), connection rejected"

View File

@ -301,6 +301,36 @@ namespace libtorrent
TORRENT_SOCKTYPE_FORWARD(close(ec))
}
void socket_type::set_close_reason(boost::uint16_t code)
{
switch (m_type)
{
case socket_type_int_impl<utp_stream>::value:
get<utp_stream>()->set_close_reason(code);
break;
#ifdef TORRENT_USE_OPENSSL
case socket_type_int_impl<ssl_stream<utp_stream> >::value:
get<ssl_stream<utp_stream> >()->set_close_reason(boost::uint16_t code);
break;
#endif
default: break;
}
}
boost::uint16_t socket_type::get_close_reason()
{
switch (m_type)
{
case socket_type_int_impl<utp_stream>::value:
return get<utp_stream>()->get_close_reason();
#ifdef TORRENT_USE_OPENSSL
case socket_type_int_impl<ssl_stream<utp_stream> >::value:
return get<ssl_stream<utp_stream> >()->get_close_reason();
#endif
default: return 0;
}
}
socket_type::endpoint_type socket_type::local_endpoint(error_code& ec) const
{ TORRENT_SOCKTYPE_FORWARD_RET(local_endpoint(ec), socket_type::endpoint_type()) }

View File

@ -252,6 +252,7 @@ struct utp_socket_impl
, m_out_packets(0)
, m_send_delay(0)
, m_recv_delay(0)
, m_close_reason(0)
, m_port(0)
, m_send_id(send_id)
, m_recv_id(recv_id)
@ -306,6 +307,7 @@ struct utp_socket_impl
// returns true if there were handlers cancelled
// if it returns false, we can detach immediately
bool destroy();
void set_close_reason(boost::uint16_t code);
void detach();
void send_syn();
void send_fin();
@ -318,8 +320,9 @@ struct utp_socket_impl
bool send_pkt(int flags = 0);
bool resend_packet(packet* p, bool fast_resend = false);
void send_reset(utp_header* ph);
void parse_sack(boost::uint16_t packet_ack, boost::uint8_t const* ptr, int size, int* acked_bytes
, ptime const now, boost::uint32_t& min_rtt);
void parse_sack(boost::uint16_t packet_ack, boost::uint8_t const* ptr
, int size, int* acked_bytes, ptime const now, boost::uint32_t& min_rtt);
void parse_close_reason(boost::uint8_t const* ptr, int size);
void write_payload(boost::uint8_t* ptr, int size);
void maybe_inc_acked_seq_nr();
void ack_packet(packet* p, ptime const& receive_time
@ -514,6 +517,11 @@ public:
// average RTT
sliding_average<16> m_rtt;
// if this is != 0, it means the upper layer provided a reason for why
// the connection is being closed. The reason is indicated by this
// non-zero value which is included in a packet header extension
boost::uint16_t m_close_reason;
// port of destination endpoint
boost::uint16_t m_port;
@ -789,6 +797,7 @@ int utp_stream::recv_delay() const
utp_stream::utp_stream(asio::io_service& io_service)
: m_io_service(io_service)
, m_impl(0)
, m_incoming_close_reason(0)
, m_open(false)
{
}
@ -798,6 +807,17 @@ utp_socket_impl* utp_stream::get_impl()
return m_impl;
}
void utp_stream::set_close_reason(boost::uint16_t code)
{
if (!m_impl) return;
m_impl->set_close_reason(code);
}
boost::uint16_t utp_stream::get_close_reason()
{
return m_incoming_close_reason;
}
void utp_stream::close()
{
if (!m_impl) return;
@ -860,7 +880,15 @@ int utp_stream::read_buffer_size() const
return m_impl->m_receive_buffer_size;
}
void utp_stream::on_read(void* self, size_t bytes_transferred, error_code const& ec, bool kill)
void utp_stream::on_close_reason(void* self, boost::uint16_t close_reason)
{
utp_stream* s = (utp_stream*)self;
TORRENT_ASSERT(s->m_impl);
s->m_incoming_close_reason = close_reason;
}
void utp_stream::on_read(void* self, size_t bytes_transferred
, error_code const& ec, bool kill)
{
utp_stream* s = (utp_stream*)self;
@ -882,7 +910,8 @@ void utp_stream::on_read(void* self, size_t bytes_transferred, error_code const&
// tmp(ec, bytes_transferred);
}
void utp_stream::on_write(void* self, size_t bytes_transferred, error_code const& ec, bool kill)
void utp_stream::on_write(void* self, size_t bytes_transferred
, error_code const& ec, bool kill)
{
utp_stream* s = (utp_stream*)self;
@ -1227,12 +1256,22 @@ void utp_socket_impl::maybe_trigger_send_callback()
m_write_buffer.clear();
}
void utp_socket_impl::set_close_reason(boost::uint16_t code)
{
#if TORRENT_UTP_LOG
UTP_LOGV("%8p: set_close_reason: %d\n"
, this, int(m_close_reason));
#endif
m_close_reason = code;
}
bool utp_socket_impl::destroy()
{
INVARIANT_CHECK;
#if TORRENT_UTP_LOG
UTP_LOGV("%8p: destroy state:%s\n", this, socket_state_names[m_state]);
UTP_LOGV("%8p: destroy state:%s (close-reason: %d)\n"
, this, socket_state_names[m_state], int(m_close_reason));
#endif
if (m_userdata == 0) return false;
@ -1293,7 +1332,7 @@ void utp_socket_impl::send_syn()
p->need_resend = false;
utp_header* h = (utp_header*)p->buf;
h->type_ver = (ST_SYN << 4) | 1;
h->extension = 0;
h->extension = utp_no_extension;
// using recv_id here is intentional! This is an odd
// thing in uTP. The syn packet is sent with the connection
// ID that it expects to receive the syn ack on. All
@ -1392,7 +1431,7 @@ void utp_socket_impl::send_reset(utp_header* ph)
utp_header h;
h.type_ver = (ST_RESET << 4) | 1;
h.extension = 0;
h.extension = utp_no_extension;
h.connection_id = m_send_id;
h.timestamp_difference_microseconds = m_reply_micro;
h.wnd_size = 0;
@ -1415,6 +1454,21 @@ std::size_t utp_socket_impl::available() const
return m_receive_buffer_size;
}
void utp_socket_impl::parse_close_reason(boost::uint8_t const* ptr, int size)
{
if (size != 4) return;
// skip reserved bytes
ptr += 2;
boost::uint16_t incoming_close_reason = detail::read_uint16(ptr);
UTP_LOGV("%8p: incoming close_reason: %d\n"
, this, int(incoming_close_reason));
if (m_userdata == 0) return;
utp_stream::on_close_reason(m_userdata, incoming_close_reason);
}
void utp_socket_impl::parse_sack(boost::uint16_t packet_ack, boost::uint8_t const* ptr
, int size, int* acked_bytes, ptime const now, boost::uint32_t& min_rtt)
{
@ -1600,11 +1654,12 @@ void utp_socket_impl::remove_sack_header(packet* p)
boost::uint8_t* ptr = p->buf + sizeof(utp_header);
utp_header* h = (utp_header*)p->buf;
TORRENT_ASSERT(h->extension == 1);
TORRENT_ASSERT(h->extension == utp_sack);
h->extension = ptr[0];
int sack_size = ptr[1];
TORRENT_ASSERT(h->extension == 0);
TORRENT_ASSERT(h->extension == utp_no_extension
|| h->extension == utp_close_reason);
UTP_LOGV("%8p: removing SACK header, %d bytes\n"
, this, sack_size + 2);
@ -1655,7 +1710,8 @@ bool utp_socket_impl::send_pkt(int flags)
// first see if we need to resend any packets
// TODO: this loop may not be very efficient
// TODO: this loop is not very efficient. It could be fixed by having
// a separate list of sequence numbers that need resending
for (int i = (m_acked_seq_nr + 1) & ACK_MASK; i != m_seq_nr; i = (i + 1) & ACK_MASK)
{
packet* p = (packet*)m_outbuf.at(i);
@ -1687,7 +1743,11 @@ bool utp_socket_impl::send_pkt(int flags)
if (sack > 32) sack = 32;
}
int header_size = sizeof(utp_header) + (sack ? sack + 2 : 0);
boost::uint32_t close_reason = m_close_reason;
int header_size = sizeof(utp_header)
+ (sack ? sack + 2 : 0)
+ (close_reason ? 6 : 0);
int payload_size = m_write_buffer_size;
if (m_mtu - header_size < payload_size)
payload_size = m_mtu - header_size;
@ -1798,7 +1858,8 @@ bool utp_socket_impl::send_pkt(int flags)
h = (utp_header*)ptr;
ptr += sizeof(utp_header);
h->extension = sack ? 1 : 0;
h->extension = sack ? utp_sack
: close_reason ? utp_close_reason : utp_no_extension;
h->connection_id = m_send_id;
// seq_nr is ignored for ST_STATE packets, so it doesn't
// matter that we say this is a sequence number we haven't
@ -1819,7 +1880,7 @@ bool utp_socket_impl::send_pkt(int flags)
// if the packet has a selective force header, we'll need
// to update it
if (h->extension == 1)
if (h->extension == utp_sack)
{
sack = ptr[1];
// if we no longer have any out-of-order packets waiting
@ -1864,13 +1925,20 @@ bool utp_socket_impl::send_pkt(int flags)
if (sack)
{
*ptr++ = 0; // end of extension chain
*ptr++ = close_reason ? utp_close_reason : utp_no_extension;
*ptr++ = sack; // bytes for SACK bitfield
write_sack(ptr, sack);
ptr += sack;
TORRENT_ASSERT(ptr <= p->buf + p->header_size);
}
if (close_reason)
{
*ptr++ = utp_no_extension;
*ptr++ = 4;
detail::write_uint32(close_reason, ptr);
}
if (m_bytes_in_flight > 0
&& p->size < p->allocated
&& !force
@ -2114,7 +2182,7 @@ bool utp_socket_impl::resend_packet(packet* p, bool fast_resend)
// if the packet has a selective ack header, we'll need
// to update it
if (h->extension == 1 && h->ack_nr != m_ack_nr)
if (h->extension == utp_sack && h->ack_nr != m_ack_nr)
{
boost::uint8_t* ptr = p->buf + sizeof(utp_header);
int sack_size = ptr[1];
@ -2842,9 +2910,12 @@ bool utp_socket_impl::incoming_packet(boost::uint8_t const* buf, int size
}
switch(extension)
{
case 1: // selective ACKs
case utp_sack: // selective ACKs
parse_sack(ph->ack_nr, ptr, len, &acked_bytes, receive_time, min_rtt);
break;
case utp_close_reason:
parse_close_reason(ptr, len);
break;
}
ptr += len;
extension = next_extension;