diff --git a/libtorrent/cpp/alert.cpp b/libtorrent/cpp/alert.cpp deleted file mode 100755 index 781265b92..000000000 --- a/libtorrent/cpp/alert.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg, Daniel Wallin -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/alert.hpp" - -namespace libtorrent { - - alert::alert(severity_t severity, const std::string& msg) - : m_msg(msg) - , m_severity(severity) - , m_timestamp(boost::posix_time::second_clock::universal_time()) - { - } - - alert::~alert() - { - } - - boost::posix_time::ptime alert::timestamp() const - { - return m_timestamp; - } - - const std::string& alert::msg() const - { - return m_msg; - } - - alert::severity_t alert::severity() const - { - return m_severity; - } - - - - alert_manager::alert_manager() - : m_severity(alert::none) - {} - - alert_manager::~alert_manager() - { - while (!m_alerts.empty()) - { - delete m_alerts.front(); - m_alerts.pop(); - } - } - - void alert_manager::post_alert(const alert& alert_) - { - boost::mutex::scoped_lock lock(m_mutex); - if (m_severity > alert_.severity()) return; - - // the internal limit is 100 alerts - if (m_alerts.size() == 100) - { - alert* result = m_alerts.front(); - m_alerts.pop(); - delete result; - } - m_alerts.push(alert_.clone().release()); - } - - std::auto_ptr alert_manager::get() - { - boost::mutex::scoped_lock lock(m_mutex); - - assert(!m_alerts.empty()); - - alert* result = m_alerts.front(); - m_alerts.pop(); - return std::auto_ptr(result); - } - - bool alert_manager::pending() const - { - boost::mutex::scoped_lock lock(m_mutex); - - return !m_alerts.empty(); - } - - void alert_manager::set_severity(alert::severity_t severity) - { - boost::mutex::scoped_lock lock(m_mutex); - - m_severity = severity; - } - - bool alert_manager::should_post(alert::severity_t severity) const - { - return severity >= m_severity; - } - -} // namespace libtorrent - diff --git a/libtorrent/cpp/allocate_resources.cpp b/libtorrent/cpp/allocate_resources.cpp deleted file mode 100644 index 7d08b036d..000000000 --- a/libtorrent/cpp/allocate_resources.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - -Copyright (c) 2003, Magnus Jonsson, 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. - -*/ - -//The Standard Library defines the two template functions std::min() -//and std::max() in the header. In general, you should -//use these template functions for calculating the min and max values -//of a pair. Unfortunately, Visual C++ does not define these function -// templates. This is because the names min and max clash with -//the traditional min and max macros defined in . -//As a workaround, Visual C++ defines two alternative templates with -//identical functionality called _cpp_min() and _cpp_max(). You can -//use them instead of std::min() and std::max().To disable the -//generation of the min and max macros in Visual C++, #define -//NOMINMAX before #including . - -#ifdef _WIN32 - //support boost1.32.0(2004-11-19 18:47) - //now all libs can be compiled and linked with static module - #define NOMINMAX -#endif - -#include "libtorrent/allocate_resources.hpp" -#include "libtorrent/size_type.hpp" -#include "libtorrent/peer_connection.hpp" -#include "libtorrent/torrent.hpp" -#include "libtorrent/aux_/allocate_resources_impl.hpp" - -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1310 -#define for if (false) {} else for -#else -#include -#endif - -namespace libtorrent -{ - int saturated_add(int a, int b) - { - assert(a >= 0); - assert(b >= 0); - assert(a <= resource_request::inf); - assert(b <= resource_request::inf); - assert(resource_request::inf + resource_request::inf < 0); - - unsigned int sum = unsigned(a) + unsigned(b); - if (sum > unsigned(resource_request::inf)) - sum = resource_request::inf; - - assert(sum >= unsigned(a) && sum >= unsigned(b)); - return int(sum); - } - -#if defined(_MSC_VER) && _MSC_VER < 1310 - - namespace detail - { - struct iterator_wrapper - { - typedef std::map >::iterator orig_iter; - - orig_iter iter; - - iterator_wrapper(orig_iter i): iter(i) {} - void operator++() { ++iter; } - torrent& operator*() { return *(iter->second); } - bool operator==(const iterator_wrapper& i) const - { return iter == i.iter; } - bool operator!=(const iterator_wrapper& i) const - { return iter != i.iter; } - }; - - struct iterator_wrapper2 - { - typedef std::map::iterator orig_iter; - - orig_iter iter; - - iterator_wrapper2(orig_iter i): iter(i) {} - void operator++() { ++iter; } - peer_connection& operator*() { return *(iter->second); } - bool operator==(const iterator_wrapper2& i) const - { return iter == i.iter; } - bool operator!=(const iterator_wrapper2& i) const - { return iter != i.iter; } - }; - - } - - void allocate_resources( - int resources - , std::map >& c - , resource_request torrent::* res) - { - aux::allocate_resources_impl( - resources - , detail::iterator_wrapper(c.begin()) - , detail::iterator_wrapper(c.end()) - , res); - } - - void allocate_resources( - int resources - , std::map& c - , resource_request peer_connection::* res) - { - aux::allocate_resources_impl( - resources - , detail::iterator_wrapper2(c.begin()) - , detail::iterator_wrapper2(c.end()) - , res); - } - -#else - - namespace aux - { - peer_connection& pick_peer( - std::pair - , boost::intrusive_ptr > const& p) - { - return *p.second; - } - - peer_connection& pick_peer2( - std::pair const& p) - { - return *p.second; - } - - torrent& deref(std::pair > const& p) - { - return *p.second; - } - } - - void allocate_resources( - int resources - , std::map >& c - , resource_request torrent::* res) - { - typedef std::map >::iterator orig_iter; - typedef std::pair > in_param; - typedef boost::transform_iterator new_iter; - - aux::allocate_resources_impl( - resources - , new_iter(c.begin(), &aux::deref) - , new_iter(c.end(), &aux::deref) - , res); - } - - void allocate_resources( - int resources - , std::map& c - , resource_request peer_connection::* res) - { - typedef std::map::iterator orig_iter; - typedef std::pair in_param; - typedef boost::transform_iterator new_iter; - - aux::allocate_resources_impl( - resources - , new_iter(c.begin(), &aux::pick_peer2) - , new_iter(c.end(), &aux::pick_peer2) - , res); - } -#endif - -} // namespace libtorrent diff --git a/libtorrent/cpp/bt_peer_connection.cpp b/libtorrent/cpp/bt_peer_connection.cpp deleted file mode 100755 index 0cff4b253..000000000 --- a/libtorrent/cpp/bt_peer_connection.cpp +++ /dev/null @@ -1,1571 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include - -#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/version.hpp" -#include "libtorrent/aux_/session_impl.hpp" - -using namespace boost::posix_time; -using boost::bind; -using boost::shared_ptr; -using libtorrent::aux::session_impl; - -namespace libtorrent -{ - - // the names of the extensions to look for in - // the extensions-message - const char* bt_peer_connection::extension_names[] = - { "", "LT_chat", "LT_metadata", "LT_peer_exchange" }; - - const bt_peer_connection::message_handler - bt_peer_connection::m_message_handler[] = - { - &bt_peer_connection::on_choke, - &bt_peer_connection::on_unchoke, - &bt_peer_connection::on_interested, - &bt_peer_connection::on_not_interested, - &bt_peer_connection::on_have, - &bt_peer_connection::on_bitfield, - &bt_peer_connection::on_request, - &bt_peer_connection::on_piece, - &bt_peer_connection::on_cancel, - &bt_peer_connection::on_dht_port, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - &bt_peer_connection::on_extended - }; - - - bt_peer_connection::bt_peer_connection( - session_impl& ses - , boost::weak_ptr tor - , shared_ptr s - , tcp::endpoint const& remote) - : peer_connection(ses, tor, s, remote) - , m_state(read_protocol_length) - , m_supports_extensions(false) - , m_supports_dht_port(false) - , m_no_metadata( - boost::gregorian::date(1970, boost::date_time::Jan, 1) - , boost::posix_time::seconds(0)) - , m_metadata_request( - boost::gregorian::date(1970, boost::date_time::Jan, 1) - , boost::posix_time::seconds(0)) - , m_waiting_metadata_request(false) - , m_metadata_progress(0) -#ifndef NDEBUG - , m_in_constructor(true) -#endif - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "*** bt_peer_connection\n"; -#endif - - // initialize the extension list to zero, since - // we don't know which extensions the other - // end supports yet - std::fill(m_extension_messages, m_extension_messages + num_supported_extensions, 0); - - write_handshake(); - - // start in the state where we are trying to read the - // handshake from the other side - reset_recv_buffer(1); - - // assume the other end has no pieces - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - if (t->ready_for_connections()) - write_bitfield(t->pieces()); - - setup_send(); - setup_receive(); -#ifndef NDEBUG - m_in_constructor = false; -#endif - } - - bt_peer_connection::bt_peer_connection( - session_impl& ses - , boost::shared_ptr s) - : peer_connection(ses, s) - , m_state(read_protocol_length) - , m_supports_extensions(false) - , m_supports_dht_port(false) - , m_no_metadata( - boost::gregorian::date(1970, boost::date_time::Jan, 1) - , boost::posix_time::seconds(0)) - , m_metadata_request( - boost::gregorian::date(1970, boost::date_time::Jan, 1) - , boost::posix_time::seconds(0)) - , m_waiting_metadata_request(false) - , m_metadata_progress(0) -#ifndef NDEBUG - , m_in_constructor(true) -#endif - { - // initialize the extension list to zero, since - // we don't know which extensions the other - // end supports yet - std::fill(m_extension_messages, m_extension_messages + num_supported_extensions, 0); - - // we are not attached to any torrent yet. - // we have to wait for the handshake to see - // which torrent the connector want's to connect to - - // start in the state where we are trying to read the - // handshake from the other side - reset_recv_buffer(1); - setup_receive(); -#ifndef NDEBUG - m_in_constructor = false; -#endif - } - - bt_peer_connection::~bt_peer_connection() - { - } - - void bt_peer_connection::write_dht_port(int listen_port) - { - INVARIANT_CHECK; - - buffer::interval packet = allocate_send_buffer(7); - detail::write_uint32(3, packet.begin); - detail::write_uint8(msg_dht_port, packet.begin); - detail::write_uint16(listen_port, packet.begin); - assert(packet.begin == packet.end); - setup_send(); - } - - void bt_peer_connection::get_peer_info(peer_info& p) const - { - assert(!associated_torrent().expired()); - - p.down_speed = statistics().download_rate(); - p.up_speed = statistics().upload_rate(); - p.payload_down_speed = statistics().download_payload_rate(); - p.payload_up_speed = statistics().upload_payload_rate(); - p.pid = pid(); - p.ip = remote(); - - p.total_download = statistics().total_payload_download(); - p.total_upload = statistics().total_payload_upload(); - - if (m_ul_bandwidth_quota.given == std::numeric_limits::max()) - p.upload_limit = -1; - else - p.upload_limit = m_ul_bandwidth_quota.given; - - if (m_dl_bandwidth_quota.given == std::numeric_limits::max()) - p.download_limit = -1; - else - p.download_limit = m_dl_bandwidth_quota.given; - - p.load_balancing = total_free_upload(); - - p.download_queue_length = (int)download_queue().size(); - p.upload_queue_length = (int)upload_queue().size(); - - if (boost::optional ret = downloading_piece_progress()) - { - p.downloading_piece_index = ret->piece_index; - p.downloading_block_index = ret->block_index; - p.downloading_progress = ret->bytes_downloaded; - p.downloading_total = ret->full_block_bytes; - } - else - { - p.downloading_piece_index = -1; - p.downloading_block_index = -1; - p.downloading_progress = 0; - p.downloading_total = 0; - } - - p.flags = 0; - 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_local()) p.flags |= peer_info::local_connection; - if (!is_connecting() && m_state < read_packet_size) - p.flags |= peer_info::handshake; - if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; - if (is_queued()) p.flags |= peer_info::queued; - - p.pieces = get_bitfield(); - p.seed = is_seed(); - - p.client = m_client_version; - p.connection_type = peer_info::standard_bittorrent; - } - - void bt_peer_connection::write_handshake() - { - INVARIANT_CHECK; - - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - // add handshake to the send buffer - const char version_string[] = "BitTorrent protocol"; - const int string_len = sizeof(version_string)-1; - - buffer::interval i = allocate_send_buffer(1 + string_len + 8 + 20 + 20); - // length of version string - *i.begin = string_len; - ++i.begin; - - // version string itself - std::copy( - version_string - , version_string + string_len - , i.begin); - i.begin += string_len; - - // 8 zeroes - std::fill( - i.begin - , i.begin + 8 - , 0); - -#ifndef TORRENT_DISABLE_DHT - // indicate that we support the DHT messages - *(i.begin + 7) = 0x01; -#endif - - // we support extensions - *(i.begin + 5) = 0x10; - - i.begin += 8; - - // info hash - sha1_hash const& ih = t->torrent_file().info_hash(); - std::copy(ih.begin(), ih.end(), i.begin); - i.begin += 20; - - // peer id - std::copy( - m_ses.get_peer_id().begin() - , m_ses.get_peer_id().end() - , i.begin); - i.begin += 20; - assert(i.begin == i.end); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> HANDSHAKE\n"; -#endif - setup_send(); - } - - boost::optional bt_peer_connection::downloading_piece_progress() const - { - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - buffer::const_interval recv_buffer = receive_buffer(); - // are we currently receiving a 'piece' message? - if (m_state != read_packet - || (recv_buffer.end - recv_buffer.begin) < 9 - || recv_buffer[0] != msg_piece) - return boost::optional(); - - const char* ptr = recv_buffer.begin + 1; - peer_request r; - r.piece = detail::read_int32(ptr); - r.start = detail::read_int32(ptr); - r.length = packet_size() - 9; - - // is any of the piece message header data invalid? - if (!verify_piece(r)) - return boost::optional(); - - piece_block_progress p; - - p.piece_index = r.piece; - p.block_index = r.start / t->block_size(); - p.bytes_downloaded = recv_buffer.end - recv_buffer.begin - 9; - p.full_block_bytes = r.length; - - return boost::optional(p); - } - - - // message handlers - - // ----------------------------- - // --------- KEEPALIVE --------- - // ----------------------------- - - void bt_peer_connection::on_keepalive() - { - INVARIANT_CHECK; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== KEEPALIVE\n"; -#endif - incoming_keepalive(); - } - - // ----------------------------- - // ----------- CHOKE ----------- - // ----------------------------- - - void bt_peer_connection::on_choke(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - if (packet_size() != 1) - throw protocol_error("'choke' message size != 1"); - m_statistics.received_bytes(0, received); - if (!packet_finished()) return; - - incoming_choke(); - } - - // ----------------------------- - // ---------- UNCHOKE ---------- - // ----------------------------- - - void bt_peer_connection::on_unchoke(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - if (packet_size() != 1) - throw protocol_error("'unchoke' message size != 1"); - m_statistics.received_bytes(0, received); - if (!packet_finished()) return; - - incoming_unchoke(); - } - - // ----------------------------- - // -------- INTERESTED --------- - // ----------------------------- - - void bt_peer_connection::on_interested(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - if (packet_size() != 1) - throw protocol_error("'interested' message size != 1"); - m_statistics.received_bytes(0, received); - if (!packet_finished()) return; - - incoming_interested(); - } - - // ----------------------------- - // ------ NOT INTERESTED ------- - // ----------------------------- - - void bt_peer_connection::on_not_interested(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - if (packet_size() != 1) - throw protocol_error("'not interested' message size != 1"); - m_statistics.received_bytes(0, received); - if (!packet_finished()) return; - - incoming_not_interested(); - } - - // ----------------------------- - // ----------- HAVE ------------ - // ----------------------------- - - void bt_peer_connection::on_have(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - if (packet_size() != 5) - throw protocol_error("'have' message size != 5"); - m_statistics.received_bytes(0, received); - if (!packet_finished()) return; - - buffer::const_interval recv_buffer = receive_buffer(); - - const char* ptr = recv_buffer.begin + 1; - int index = detail::read_int32(ptr); - - incoming_have(index); - } - - // ----------------------------- - // --------- BITFIELD ---------- - // ----------------------------- - - void bt_peer_connection::on_bitfield(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - // if we don't have the metedata, we cannot - // verify the bitfield size - if (t->valid_metadata() - && packet_size() - 1 != ((int)get_bitfield().size() + 7) / 8) - throw protocol_error("bitfield with invalid size"); - - m_statistics.received_bytes(0, received); - if (!packet_finished()) return; - - buffer::const_interval recv_buffer = receive_buffer(); - - std::vector bitfield; - - if (!t->valid_metadata()) - bitfield.resize((packet_size() - 1) * 8); - else - bitfield.resize(get_bitfield().size()); - - // if we don't have metadata yet - // just remember the bitmask - // don't update the piecepicker - // (since it doesn't exist yet) - for (int i = 0; i < (int)bitfield.size(); ++i) - bitfield[i] = (recv_buffer[1 + (i>>3)] & (1 << (7 - (i&7)))) != 0; - incoming_bitfield(bitfield); - } - - // ----------------------------- - // ---------- REQUEST ---------- - // ----------------------------- - - void bt_peer_connection::on_request(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - if (packet_size() != 13) - throw protocol_error("'request' message size != 13"); - m_statistics.received_bytes(0, received); - if (!packet_finished()) return; - - buffer::const_interval recv_buffer = receive_buffer(); - - peer_request r; - const char* ptr = recv_buffer.begin + 1; - r.piece = detail::read_int32(ptr); - r.start = detail::read_int32(ptr); - r.length = detail::read_int32(ptr); - - incoming_request(r); - } - - // ----------------------------- - // ----------- PIECE ----------- - // ----------------------------- - - void bt_peer_connection::on_piece(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - - buffer::const_interval recv_buffer = receive_buffer(); - int recv_pos = recv_buffer.end - recv_buffer.begin; - - // classify the received data as protocol chatter - // or data payload for the statistics - if (recv_pos <= 9) - // only received protocol data - m_statistics.received_bytes(0, received); - else if (recv_pos - received >= 9) - // only received payload data - m_statistics.received_bytes(received, 0); - else - { - // received a bit of both - assert(recv_pos - received < 9); - assert(recv_pos > 9); - assert(9 - (recv_pos - received) <= 9); - m_statistics.received_bytes( - recv_pos - 9 - , 9 - (recv_pos - received)); - } - - incoming_piece_fragment(); - if (!packet_finished()) return; - - const char* ptr = recv_buffer.begin + 1; - peer_request p; - p.piece = detail::read_int32(ptr); - p.start = detail::read_int32(ptr); - p.length = packet_size() - 9; - - incoming_piece(p, recv_buffer.begin + 9); - } - - // ----------------------------- - // ---------- CANCEL ----------- - // ----------------------------- - - void bt_peer_connection::on_cancel(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - if (packet_size() != 13) - throw protocol_error("'cancel' message size != 13"); - m_statistics.received_bytes(0, received); - if (!packet_finished()) return; - - buffer::const_interval recv_buffer = receive_buffer(); - - peer_request r; - const char* ptr = recv_buffer.begin + 1; - r.piece = 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; - - assert(received > 0); - if (packet_size() != 3) - throw protocol_error("'dht_port' message size != 3"); - m_statistics.received_bytes(0, received); - if (!packet_finished()) return; - - buffer::const_interval recv_buffer = receive_buffer(); - - const char* ptr = recv_buffer.begin + 1; - int listen_port = detail::read_uint16(ptr); - - incoming_dht_port(listen_port); - } - - // ----------------------------- - // --------- EXTENDED ---------- - // ----------------------------- - - void bt_peer_connection::on_extended(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - m_statistics.received_bytes(0, received); - if (packet_size() < 2) - throw protocol_error("'extended' message smaller than 2 bytes"); - - if (associated_torrent().expired()) - throw protocol_error("'extended' message sent before proper handshake"); - - buffer::const_interval recv_buffer = receive_buffer(); - if (recv_buffer.end - recv_buffer.begin < 2) return; - - assert(*recv_buffer.begin == msg_extended); - ++recv_buffer.begin; - - int extended_id = detail::read_uint8(recv_buffer.begin); - - if (extended_id > 0 && extended_id < num_supported_extensions - && !m_ses.extension_enabled(extended_id)) - throw protocol_error("'extended' message using disabled extension"); - - switch (extended_id) - { - case extended_handshake: - on_extended_handshake(); break; - case extended_chat_message: - on_chat(); break; - case extended_metadata_message: - on_metadata(); break; - case extended_peer_exchange_message: - on_peer_exchange(); break; - default: - throw protocol_error("unknown extended message id: " - + boost::lexical_cast(extended_id)); - }; - } - - void bt_peer_connection::write_chat_message(const std::string& msg) - { - INVARIANT_CHECK; - - assert(msg.length() <= 1 * 1024); - if (!supports_extension(extended_chat_message)) return; - - entry e(entry::dictionary_t); - e["msg"] = msg; - - std::vector message; - bencode(std::back_inserter(message), e); - - buffer::interval i = allocate_send_buffer(message.size() + 6); - - detail::write_uint32(1 + 1 + (int)message.size(), i.begin); - detail::write_uint8(msg_extended, i.begin); - detail::write_uint8(m_extension_messages[extended_chat_message], i.begin); - - std::copy(message.begin(), message.end(), i.begin); - i.begin += message.size(); - assert(i.begin == i.end); - setup_send(); - } - - - void bt_peer_connection::on_extended_handshake() try - { - if (!packet_finished()) return; - - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - buffer::const_interval recv_buffer = receive_buffer(); - - entry root = bdecode(recv_buffer.begin + 2, recv_buffer.end); - -#ifdef TORRENT_VERBOSE_LOGGING - std::stringstream ext; - root.print(ext); - (*m_logger) << "<== EXTENDED HANDSHAKE: \n" << ext.str(); -#endif - - if (entry* msgs = root.find_key("m")) - { - if (msgs->type() == entry::dictionary_t) - { - // this must be the initial handshake message - // lets see if any of our extensions are supported - // if not, we will signal no extensions support to the upper layer - for (int i = 1; i < num_supported_extensions; ++i) - { - if (entry* f = msgs->find_key(extension_names[i])) - { - m_extension_messages[i] = (int)f->integer(); - } - else - { - m_extension_messages[i] = 0; - } - } - } - } - - // there is supposed to be a remote listen port - if (entry* listen_port = root.find_key("p")) - { - if (listen_port->type() == entry::int_t) - { - tcp::endpoint adr(remote().address() - , (unsigned short)listen_port->integer()); - t->get_policy().peer_from_tracker(adr, pid()); - } - } - // there should be a version too - // but where do we put that info? - - if (entry* client_info = root.find_key("v")) - { - if (client_info->type() == entry::string_t) - m_client_version = client_info->string(); - } - - if (entry* reqq = root.find_key("reqq")) - { - if (reqq->type() == entry::int_t) - m_max_out_request_queue = reqq->integer(); - if (m_max_out_request_queue < 1) - m_max_out_request_queue = 1; - } - } - catch (std::exception& exc) - { -#ifdef TORRENT_VERBOSE_LOGGIGN - (*m_logger) << "invalid extended handshake: " << exc.what() << "\n"; -#endif - } - - // ----------------------------- - // ----------- CHAT ------------ - // ----------------------------- - - void bt_peer_connection::on_chat() - { - if (packet_size() > 2 * 1024) - throw protocol_error("CHAT message larger than 2 kB"); - - if (!packet_finished()) return; - - try - { - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - buffer::const_interval recv_buffer = receive_buffer(); - entry d = bdecode(recv_buffer.begin + 2, recv_buffer.end); - const std::string& str = d["msg"].string(); - - if (t->alerts().should_post(alert::critical)) - { - t->alerts().post_alert( - chat_message_alert( - t->get_handle() - , remote(), str)); - } - - } - catch (invalid_encoding&) - { - // TODO: post an alert about the invalid chat message - return; -// throw protocol_error("invalid bencoding in CHAT message"); - } - catch (type_error&) - { - // TODO: post an alert about the invalid chat message - return; -// throw protocol_error("invalid types in bencoded CHAT message"); - } - return; - } - - // ----------------------------- - // --------- METADATA ---------- - // ----------------------------- - - void bt_peer_connection::on_metadata() - { - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - if (packet_size() > 500 * 1024) - throw protocol_error("metadata message larger than 500 kB"); - - if (!packet_finished()) return; - - buffer::const_interval recv_buffer = receive_buffer(); - recv_buffer.begin += 2; - int type = detail::read_uint8(recv_buffer.begin); - - switch (type) - { - case 0: // request - { - int start = detail::read_uint8(recv_buffer.begin); - int size = detail::read_uint8(recv_buffer.begin) + 1; - - if (packet_size() != 5) - { - // invalid metadata request - throw protocol_error("invalid metadata request"); - } - - write_metadata(std::make_pair(start, size)); - } - break; - case 1: // data - { - if (recv_buffer.end - recv_buffer.begin < 8) return; - int total_size = detail::read_int32(recv_buffer.begin); - int offset = detail::read_int32(recv_buffer.begin); - int data_size = packet_size() - 2 - 9; - - if (total_size > 500 * 1024) - throw protocol_error("metadata size larger than 500 kB"); - if (total_size <= 0) - throw protocol_error("invalid metadata size"); - if (offset > total_size || offset < 0) - throw protocol_error("invalid metadata offset"); - if (offset + data_size > total_size) - throw protocol_error("invalid metadata message"); - - t->metadata_progress(total_size - , recv_buffer.left() - m_metadata_progress); - m_metadata_progress = recv_buffer.left(); - if (!packet_finished()) return; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== METADATA [ tot: " << total_size << " offset: " - << offset << " size: " << data_size << " ]\n"; -#endif - - m_waiting_metadata_request = false; - t->received_metadata(recv_buffer.begin, data_size - , offset, total_size); - m_metadata_progress = 0; - } - break; - case 2: // have no data - if (!packet_finished()) return; - - m_no_metadata = second_clock::universal_time(); - if (m_waiting_metadata_request) - t->cancel_metadata_request(m_last_metadata_request); - m_waiting_metadata_request = false; - break; - default: - throw protocol_error("unknown metadata extension message: " - + boost::lexical_cast(type)); - } - - } - - // ----------------------------- - // ------ PEER EXCHANGE -------- - // ----------------------------- - - void bt_peer_connection::on_peer_exchange() - { - - } - - bool bt_peer_connection::has_metadata() const - { - using namespace boost::posix_time; - return second_clock::universal_time() - m_no_metadata > minutes(5); - } - - bool bt_peer_connection::dispatch_message(int received) - { - INVARIANT_CHECK; - - assert(received > 0); - - // this means the connection has been closed already - if (associated_torrent().expired()) return false; - - buffer::const_interval recv_buffer = receive_buffer(); - - int packet_type = recv_buffer[0]; - if (packet_type < 0 - || packet_type >= num_supported_messages - || m_message_handler[packet_type] == 0) - { - throw protocol_error("unknown message id: " - + boost::lexical_cast(packet_type) - + " size: " + boost::lexical_cast(packet_size())); - } - - assert(m_message_handler[packet_type] != 0); - - // call the correct handler for this packet type - (this->*m_message_handler[packet_type])(received); - - if (!packet_finished()) return false; - - return true; - } - - void bt_peer_connection::write_keepalive() - { - INVARIANT_CHECK; - - char buf[] = {0,0,0,0}; - send_buffer(buf, buf + sizeof(buf)); - } - - void bt_peer_connection::write_cancel(peer_request const& r) - { - INVARIANT_CHECK; - - assert(associated_torrent().lock()->valid_metadata()); - - char buf[] = {0,0,0,13, msg_cancel}; - - buffer::interval i = allocate_send_buffer(17); - - std::copy(buf, buf + 5, i.begin); - i.begin += 5; - - // index - detail::write_int32(r.piece, i.begin); - // begin - detail::write_int32(r.start, i.begin); - // length - detail::write_int32(r.length, i.begin); - assert(i.begin == i.end); - - setup_send(); - } - - void bt_peer_connection::write_request(peer_request const& r) - { - INVARIANT_CHECK; - - assert(associated_torrent().lock()->valid_metadata()); - - char buf[] = {0,0,0,13, msg_request}; - - buffer::interval i = allocate_send_buffer(17); - - std::copy(buf, buf + 5, i.begin); - i.begin += 5; - - // index - detail::write_int32(r.piece, i.begin); - // begin - detail::write_int32(r.start, i.begin); - // length - detail::write_int32(r.length, i.begin); - assert(i.begin == i.end); - - setup_send(); - } - - void bt_peer_connection::write_metadata(std::pair req) - { - assert(req.first >= 0); - assert(req.second > 0); - assert(req.second <= 256); - assert(req.first + req.second <= 256); - assert(!associated_torrent().expired()); - INVARIANT_CHECK; - - // abort if the peer doesn't support the metadata extension - if (!supports_extension(extended_metadata_message)) return; - - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - if (t->valid_metadata()) - { - std::pair offset - = req_to_offset(req, (int)t->metadata().size()); - - buffer::interval i = allocate_send_buffer(15 + offset.second); - - // yes, we have metadata, send it - detail::write_uint32(11 + offset.second, i.begin); - detail::write_uint8(msg_extended, i.begin); - detail::write_uint8(m_extension_messages[extended_metadata_message] - , i.begin); - // means 'data packet' - detail::write_uint8(1, i.begin); - detail::write_uint32((int)t->metadata().size(), i.begin); - detail::write_uint32(offset.first, i.begin); - std::vector const& metadata = t->metadata(); - std::copy(metadata.begin() + offset.first - , metadata.begin() + offset.first + offset.second, i.begin); - i.begin += offset.second; - assert(i.begin == i.end); - } - else - { - buffer::interval i = allocate_send_buffer(4 + 3); - // we don't have the metadata, reply with - // don't have-message - detail::write_uint32(1 + 2, i.begin); - detail::write_uint8(msg_extended, i.begin); - detail::write_uint8(m_extension_messages[extended_metadata_message] - , i.begin); - // means 'have no data' - detail::write_uint8(2, i.begin); - assert(i.begin == i.end); - } - setup_send(); - } - - void bt_peer_connection::write_metadata_request(std::pair req) - { - assert(req.first >= 0); - assert(req.second > 0); - assert(req.first + req.second <= 256); - assert(!associated_torrent().expired()); - assert(!associated_torrent().lock()->valid_metadata()); - INVARIANT_CHECK; - - int start = req.first; - int size = req.second; - - // abort if the peer doesn't support the metadata extension - if (!supports_extension(extended_metadata_message)) return; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> METADATA_REQUEST [ start: " << req.first - << " size: " << req.second << " ]\n"; -#endif - - buffer::interval i = allocate_send_buffer(9); - - detail::write_uint32(1 + 1 + 3, i.begin); - detail::write_uint8(msg_extended, i.begin); - detail::write_uint8(m_extension_messages[extended_metadata_message] - , i.begin); - // means 'request data' - detail::write_uint8(0, i.begin); - detail::write_uint8(start, i.begin); - detail::write_uint8(size - 1, i.begin); - assert(i.begin == i.end); - setup_send(); - } - - void bt_peer_connection::write_bitfield(std::vector const& bitfield) - { - INVARIANT_CHECK; - - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - if (t->num_pieces() == 0) return; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> BITFIELD "; - - for (int i = 0; i < (int)get_bitfield().size(); ++i) - { - if (bitfield[i]) (*m_logger) << "1"; - else (*m_logger) << "0"; - } - (*m_logger) << "\n"; -#endif - const int packet_size = ((int)bitfield.size() + 7) / 8 + 5; - - buffer::interval i = allocate_send_buffer(packet_size); - - detail::write_int32(packet_size - 4, i.begin); - detail::write_uint8(msg_bitfield, i.begin); - - std::fill(i.begin, i.end, 0); - for (int c = 0; c < (int)bitfield.size(); ++c) - { - if (bitfield[c]) - i.begin[c >> 3] |= 1 << (7 - (c & 7)); - } - assert(i.end - i.begin == ((int)bitfield.size() + 7) / 8); - setup_send(); - } - - void bt_peer_connection::write_extensions() - { - INVARIANT_CHECK; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> EXTENSIONS\n"; -#endif - assert(m_supports_extensions); - - entry handshake(entry::dictionary_t); - entry extension_list(entry::dictionary_t); - - for (int i = 1; i < num_supported_extensions; ++i) - { - // if this specific extension is disabled - // just don't add it to the supported set - if (!m_ses.extension_enabled(i)) continue; - extension_list[extension_names[i]] = i; - } - - handshake["m"] = extension_list; - handshake["p"] = m_ses.listen_port(); - handshake["v"] = m_ses.settings().user_agent; - std::string remote_address; - std::back_insert_iterator out(remote_address); - detail::write_address(remote().address(), out); - handshake["ip"] = remote_address; - handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue; - - std::vector msg; - bencode(std::back_inserter(msg), handshake); - - // make room for message - buffer::interval i = allocate_send_buffer(6 + msg.size()); - - // write the length of the message - detail::write_int32((int)msg.size() + 2, i.begin); - detail::write_uint8(msg_extended, i.begin); - // signal handshake message - detail::write_uint8(extended_handshake, i.begin); - - std::copy(msg.begin(), msg.end(), i.begin); - i.begin += msg.size(); - assert(i.begin == i.end); - -#ifdef TORRENT_VERBOSE_LOGGING - std::stringstream ext; - handshake.print(ext); - (*m_logger) << "==> EXTENDED HANDSHAKE: \n" << ext.str(); -#endif - - setup_send(); - } - - void bt_peer_connection::write_choke() - { - INVARIANT_CHECK; - - if (is_choked()) return; - char msg[] = {0,0,0,1,msg_choke}; - send_buffer(msg, msg + sizeof(msg)); - } - - void bt_peer_connection::write_unchoke() - { - INVARIANT_CHECK; - - char msg[] = {0,0,0,1,msg_unchoke}; - send_buffer(msg, msg + sizeof(msg)); - } - - void bt_peer_connection::write_interested() - { - INVARIANT_CHECK; - - char msg[] = {0,0,0,1,msg_interested}; - send_buffer(msg, msg + sizeof(msg)); - } - - void bt_peer_connection::write_not_interested() - { - INVARIANT_CHECK; - - char msg[] = {0,0,0,1,msg_not_interested}; - send_buffer(msg, msg + sizeof(msg)); - } - - void bt_peer_connection::write_have(int index) - { - assert(associated_torrent().lock()->valid_metadata()); - assert(index >= 0); - assert(index < associated_torrent().lock()->torrent_file().num_pieces()); - INVARIANT_CHECK; - - const int packet_size = 9; - char msg[packet_size] = {0,0,0,5,msg_have}; - char* ptr = msg + 5; - detail::write_int32(index, ptr); - send_buffer(msg, msg + packet_size); - } - - void bt_peer_connection::write_piece(peer_request const& r) - { - INVARIANT_CHECK; - - const int packet_size = 4 + 5 + 4 + r.length; - - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - buffer::interval i = allocate_send_buffer(packet_size); - - detail::write_int32(packet_size-4, i.begin); - detail::write_uint8(msg_piece, i.begin); - detail::write_int32(r.piece, i.begin); - detail::write_int32(r.start, i.begin); - - t->filesystem().read( - i.begin, r.piece, r.start, r.length); - - assert(i.begin + r.length == i.end); - - m_payloads.push_back(range(send_buffer_size() - r.length, r.length)); - setup_send(); - } - - // -------------------------- - // RECEIVE DATA - // -------------------------- - - // throws exception when the client should be disconnected - void bt_peer_connection::on_receive(const asio::error& error - , std::size_t bytes_transferred) - { - INVARIANT_CHECK; - - if (error) return; - - buffer::const_interval recv_buffer = receive_buffer(); - - boost::shared_ptr t = associated_torrent().lock(); - - switch(m_state) - { - case read_protocol_length: - { - m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; - - int packet_size = recv_buffer[0]; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " protocol length: " << packet_size << "\n"; -#endif - if (packet_size > 100 || packet_size <= 0) - { - std::stringstream s; - s << "incorrect protocol length (" - << packet_size - << ") should be 19."; - throw std::runtime_error(s.str()); - } - m_state = read_protocol_string; - reset_recv_buffer(packet_size); - } - break; - - case read_protocol_string: - { - m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " protocol: '" << std::string(recv_buffer.begin - , recv_buffer.end) << "'\n"; -#endif - const char protocol_string[] = "BitTorrent protocol"; - if (!std::equal(recv_buffer.begin, recv_buffer.end - , protocol_string)) - { - const char cmd[] = "version"; - if (recv_buffer.end - recv_buffer.begin == 7 && std::equal( - recv_buffer.begin, recv_buffer.end, cmd)) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "sending libtorrent version\n"; -#endif - asio::write(*get_socket(), asio::buffer("libtorrent version " LIBTORRENT_VERSION "\n", 27)); - throw std::runtime_error("closing"); - } -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "incorrect protocol name\n"; -#endif - std::stringstream s; - s << "got invalid protocol name: '" - << std::string(recv_buffer.begin, recv_buffer.end) - << "'"; - throw std::runtime_error(s.str()); - } - - m_state = read_info_hash; - reset_recv_buffer(28); - } - break; - - case read_info_hash: - { - m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; - -// MassaRoddel -#ifdef TORRENT_VERBOSE_LOGGING - for (int i=0; i < 8; ++i) - { - for (int j=0; j < 8; ++j) - { - if (recv_buffer[i] & (0x80 >> j)) (*m_logger) << "1"; - else (*m_logger) << "0"; - } - } - (*m_logger) << "\n"; - if (recv_buffer[7] & 0x01) - (*m_logger) << "supports DHT port message\n"; - if (recv_buffer[7] & 0x02) - (*m_logger) << "supports XBT peer exchange message\n"; - if (recv_buffer[5] & 0x10) - (*m_logger) << "supports LT/uT extensions\n"; -#endif - - if ((recv_buffer[5] & 0x10) && m_ses.extensions_enabled()) - m_supports_extensions = true; - if (recv_buffer[7] & 0x01) - m_supports_dht_port = true; - - // 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 - , (char*)info_hash.begin()); - - attach_to_torrent(info_hash); - t = associated_torrent().lock(); - assert(t); - - assert(t->get_policy().has_connection(this)); - - // yes, we found the torrent - // reply with our handshake - write_handshake(); - write_bitfield(t->pieces()); - } - else - { - // verify info hash - if (!std::equal(recv_buffer.begin + 8, recv_buffer.begin + 28 - , (const char*)t->torrent_file().info_hash().begin())) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " received invalid info_hash\n"; -#endif - throw std::runtime_error("invalid info-hash in handshake"); - } - } - -#ifndef TORRENT_DISABLE_DHT - if (m_supports_dht_port && m_ses.m_dht) - write_dht_port(m_ses.kad_settings().service_port); -#endif - - m_state = read_peer_id; - reset_recv_buffer(20); -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " info_hash received\n"; -#endif - } - break; - - case read_peer_id: - { - if (!t) return; - m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; - assert(packet_size() == 20); - -#ifdef TORRENT_VERBOSE_LOGGING - { - peer_id tmp; - std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)tmp.begin()); - std::stringstream s; - s << "received peer_id: " << tmp << " client: " << identify_client(tmp) << "\n"; - s << "as ascii: "; - for (peer_id::iterator i = tmp.begin(); i != tmp.end(); ++i) - { - if (std::isprint(*i)) s << *i; - else s << "."; - } - s << "\n"; - (*m_logger) << s.str(); - } -#endif - peer_id pid; - std::copy(recv_buffer.begin, recv_buffer.begin + 20, (char*)pid.begin()); - set_pid(pid); - - m_client_version = identify_client(pid); - boost::optional f = client_fingerprint(pid); - if (f && std::equal(f->name, f->name + 2, "BC")) - { - // if this is a bitcomet client, lower the request queue size limit - if (m_max_out_request_queue > 50) m_max_out_request_queue = 50; - } - - // disconnect if the peer has the same peer-id as ourself - // since it most likely is ourself then - if (pid == m_ses.get_peer_id()) - throw std::runtime_error("closing connection to ourself"); - - if (m_supports_extensions) write_extensions(); -/* - if (!m_active) - { - m_attached_to_torrent = true; - assert(m_torrent->get_policy().has_connection(this)); - } -*/ - m_state = read_packet_size; - reset_recv_buffer(4); - } - break; - - case read_packet_size: - { - if (!t) return; - m_statistics.received_bytes(0, bytes_transferred); - if (!packet_finished()) break; - - 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 - throw std::runtime_error("packet > 1 MB (" - + boost::lexical_cast( - (unsigned int)packet_size) + " bytes)"); - } - - if (packet_size == 0) - { - incoming_keepalive(); - // keepalive message - m_state = read_packet_size; - reset_recv_buffer(4); - } - else - { - m_state = read_packet; - reset_recv_buffer(packet_size); - } - } - break; - - case read_packet: - { - if (!t) return; - if (dispatch_message(bytes_transferred)) - { - m_state = read_packet_size; - reset_recv_buffer(4); - } - } - break; - - } - } - - // -------------------------- - // SEND DATA - // -------------------------- - - // throws exception when the client should be disconnected - void bt_peer_connection::on_sent(asio::error const& error - , std::size_t bytes_transferred) - { - INVARIANT_CHECK; - - if (error) return; - - // manage the payload markers - int amount_payload = 0; - if (!m_payloads.empty()) - { - for (std::deque::iterator i = m_payloads.begin(); - i != m_payloads.end(); ++i) - { - i->start -= bytes_transferred; - if (i->start < 0) - { - if (i->start + i->length <= 0) - { - amount_payload += i->length; - } - else - { - amount_payload += -i->start; - i->length -= -i->start; - i->start = 0; - } - } - } - } - - // TODO: move the erasing into the loop above - // remove all payload ranges that has been sent - m_payloads.erase( - std::remove_if(m_payloads.begin(), m_payloads.end(), range_below_zero) - , m_payloads.end()); - - assert(amount_payload <= (int)bytes_transferred); - m_statistics.sent_bytes(amount_payload, bytes_transferred - amount_payload); - } - - - void bt_peer_connection::on_tick() - { - boost::shared_ptr t = associated_torrent().lock(); - if (!t) return; - - // if we don't have any metadata, and this peer - // supports the request metadata extension - // and we aren't currently waiting for a request - // reply. Then, send a request for some metadata. - if (!t->valid_metadata() - && supports_extension(extended_metadata_message) - && !m_waiting_metadata_request - && has_metadata()) - { - m_last_metadata_request = t->metadata_request(); - write_metadata_request(m_last_metadata_request); - m_waiting_metadata_request = true; - m_metadata_request = second_clock::universal_time(); - } - } - -#ifndef NDEBUG - void bt_peer_connection::check_invariant() const - { - if (!m_in_constructor) - peer_connection::check_invariant(); - - if (!m_payloads.empty()) - { - for (std::deque::const_iterator i = m_payloads.begin(); - i != m_payloads.end() - 1; ++i) - { - assert(i->start + i->length <= (i+1)->start); - } - } - } -#endif - -} - diff --git a/libtorrent/cpp/entry.cpp b/libtorrent/cpp/entry.cpp deleted file mode 100755 index 02d86a80f..000000000 --- a/libtorrent/cpp/entry.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include "libtorrent/entry.hpp" -#include "libtorrent/config.hpp" -#include -#include - -#if defined(_MSC_VER) -namespace std -{ - using ::isprint; -} -#define for if (false) {} else for -#endif - -namespace -{ - template - void call_destructor(T* o) - { - assert(o); - o->~T(); - } - - struct compare_string - { - compare_string(char const* s): m_str(s) {} - - bool operator()( - std::pair const& e) const - { - return m_str && e.first == m_str; - } - char const* m_str; - }; -} - -namespace libtorrent -{ - namespace detail - { - TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val) - { - int sign = 0; - if (val < 0) - { - sign = 1; - val = -val; - } - buf[--size] = '\0'; - if (val == 0) buf[--size] = '0'; - for (; size > sign && val != 0;) - { - buf[--size] = '0' + char(val % 10); - val /= 10; - } - if (sign) buf[--size] = '-'; - return buf + size; - } - } - - entry& entry::operator[](char const* key) - { - dictionary_type::iterator i = dict().find(key); - if (i != dict().end()) return i->second; - dictionary_type::iterator ret = dict().insert( - dict().begin() - , std::make_pair(std::string(key), entry())); - return ret->second; - } - - - entry& entry::operator[](std::string const& key) - { - return (*this)[key.c_str()]; - } - - entry* entry::find_key(char const* key) - { - dictionary_type::iterator i = std::find_if( - dict().begin() - , dict().end() - , compare_string(key)); - if (i == dict().end()) return 0; - return &i->second; - - } - - entry const* entry::find_key(char const* key) const - { - dictionary_type::const_iterator i = dict().find(key); - if (i == dict().end()) return 0; - return &i->second; - } - - const entry& entry::operator[](char const* key) const - { - dictionary_type::const_iterator i = dict().find(key); - if (i == dict().end()) throw type_error( - (std::string("key not found: ") + key).c_str()); - return i->second; - } - - const entry& entry::operator[](std::string const& key) const - { - return (*this)[key.c_str()]; - } - - entry::entry(const dictionary_type& v) - { - new(data) dictionary_type(v); - m_type = dictionary_t; - } - - entry::entry(const string_type& v) - { - new(data) string_type(v); - m_type = string_t; - } - - entry::entry(const list_type& v) - { - new(data) list_type(v); - m_type = list_t; - } - - entry::entry(const integer_type& v) - { - new(data) integer_type(v); - m_type = int_t; - } - - void entry::operator=(const dictionary_type& v) - { - destruct(); - new(data) dictionary_type(v); - m_type = dictionary_t; - } - - void entry::operator=(const string_type& v) - { - destruct(); - new(data) string_type(v); - m_type = string_t; - } - - void entry::operator=(const list_type& v) - { - destruct(); - new(data) list_type(v); - m_type = list_t; - } - - void entry::operator=(const integer_type& v) - { - destruct(); - new(data) integer_type(v); - m_type = int_t; - } - - bool entry::operator==(entry const& e) const - { - if (m_type != e.m_type) return false; - - switch(m_type) - { - case int_t: - return integer() == e.integer(); - case string_t: - return string() == e.string(); - case list_t: - return list() == e.list(); - case dictionary_t: - return dict() == e.dict(); - default: - assert(m_type == undefined_t); - return true; - } - } - - void entry::construct(data_type t) - { - m_type = t; - switch(m_type) - { - case int_t: - new(data) integer_type; - break; - case string_t: - new(data) string_type; - break; - case list_t: - new(data) list_type; - break; - case dictionary_t: - new (data) dictionary_type; - break; - default: - assert(m_type == undefined_t); - m_type = undefined_t; - } - } - - void entry::copy(const entry& e) - { - m_type = e.m_type; - switch(m_type) - { - case int_t: - new(data) integer_type(e.integer()); - break; - case string_t: - new(data) string_type(e.string()); - break; - case list_t: - new(data) list_type(e.list()); - break; - case dictionary_t: - new (data) dictionary_type(e.dict()); - break; - default: - m_type = undefined_t; - } - } - - void entry::destruct() - { - switch(m_type) - { - case int_t: - call_destructor(reinterpret_cast(data)); - break; - case string_t: - call_destructor(reinterpret_cast(data)); - break; - case list_t: - call_destructor(reinterpret_cast(data)); - break; - case dictionary_t: - call_destructor(reinterpret_cast(data)); - break; - default: - assert(m_type == undefined_t); - break; - } - } - - void entry::print(std::ostream& os, int indent) const - { - assert(indent >= 0); - for (int i = 0; i < indent; ++i) os << " "; - switch (m_type) - { - case int_t: - os << integer() << "\n"; - break; - case string_t: - { - bool binary_string = false; - for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) - { - if (!std::isprint(static_cast(*i))) - { - binary_string = true; - break; - } - } - if (binary_string) - { - os.unsetf(std::ios_base::dec); - os.setf(std::ios_base::hex); - for (std::string::const_iterator i = string().begin(); i != string().end(); ++i) - os << std::setfill('0') << std::setw(2) - << static_cast((unsigned char)*i); - os.unsetf(std::ios_base::hex); - os.setf(std::ios_base::dec); - os << "\n"; - } - else - { - os << string() << "\n"; - } - } break; - case list_t: - { - os << "list\n"; - for (list_type::const_iterator i = list().begin(); i != list().end(); ++i) - { - i->print(os, indent+1); - } - } break; - case dictionary_t: - { - os << "dictionary\n"; - for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i) - { - for (int j = 0; j < indent+1; ++j) os << " "; - os << "[" << i->first << "]"; - if (i->second.type() != entry::string_t - && i->second.type() != entry::int_t) - os << "\n"; - else os << " "; - i->second.print(os, indent+2); - } - } break; - default: - os << "\n"; - } - } -} - diff --git a/libtorrent/cpp/escape_string.cpp b/libtorrent/cpp/escape_string.cpp deleted file mode 100755 index 80d134ed7..000000000 --- a/libtorrent/cpp/escape_string.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include -#include -#include - -namespace libtorrent -{ - std::string unescape_string(std::string const& s) - { - std::string ret; - for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) - { - if(*i == '+') - { - ret += ' '; - } - else if (*i != '%') - { - ret += *i; - } - else - { - ++i; - if (i == s.end()) - throw std::runtime_error("invalid escaped string"); - - int high; - if(*i >= '0' && *i <= '9') high = *i - '0'; - else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A'; - else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a'; - else throw std::runtime_error("invalid escaped string"); - - ++i; - if (i == s.end()) - throw std::runtime_error("invalid escaped string"); - - int low; - if(*i >= '0' && *i <= '9') low = *i - '0'; - else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A'; - else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a'; - else throw std::runtime_error("invalid escaped string"); - - ret += char(high * 16 + low); - } - } - return ret; - } - - - std::string escape_string(const char* str, int len) - { - assert(str != 0); - assert(len >= 0); - // http://www.ietf.org/rfc/rfc2396.txt - // section 2.3 - // some trackers seems to require that ' is escaped -// static const char unreserved_chars[] = "-_.!~*'()"; - static const char unreserved_chars[] = "-_.!~*()" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789"; - - std::stringstream ret; - ret << std::hex << std::setfill('0'); - for (int i = 0; i < len; ++i) - { - if (std::count( - unreserved_chars - , unreserved_chars+sizeof(unreserved_chars)-1 - , *str)) - { - ret << *str; - } - else - { - ret << '%' - << std::setw(2) - << (int)static_cast(*str); - } - ++str; - } - return ret.str(); - } - - std::string escape_path(const char* str, int len) - { - assert(str != 0); - assert(len >= 0); - static const char unreserved_chars[] = "/-_.!~*()" - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789"; - - std::stringstream ret; - ret << std::hex << std::setfill('0'); - for (int i = 0; i < len; ++i) - { - if (std::count( - unreserved_chars - , unreserved_chars+sizeof(unreserved_chars)-1 - , *str)) - { - ret << *str; - } - else - { - ret << '%' - << std::setw(2) - << (int)static_cast(*str); - } - ++str; - } - return ret.str(); - } -} diff --git a/libtorrent/cpp/file.cpp b/libtorrent/cpp/file.cpp deleted file mode 100755 index af8404660..000000000 --- a/libtorrent/cpp/file.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifdef _WIN32 -// windows part -#include "libtorrent/utf8.hpp" - -#include -#include -#include -#include - -#ifndef _MODE_T_ -typedef int mode_t; -#endif - -#ifdef UNICODE -#include "libtorrent/storage.hpp" -#endif - -#else -// unix part -#define _FILE_OFFSET_BITS 64 -#include -#include -#include -#include -#include - -#include -// make sure the _FILE_OFFSET_BITS define worked -// on this platform -BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8); - -#endif - -#include -#include "libtorrent/file.hpp" -#include - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#ifndef O_RANDOM -#define O_RANDOM 0 -#endif - -#ifdef UNICODE -#include "libtorrent/storage.hpp" -#endif - - -namespace fs = boost::filesystem; - -namespace -{ - enum { mode_in = 1, mode_out = 2 }; - - mode_t map_open_mode(int m) - { - if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM; - if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM; - if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM; - assert(false); - return 0; - } - -#ifdef WIN32 - std::string utf8_native(std::string const& s) - { - try - { - std::wstring ws; - libtorrent::utf8_wchar(s, ws); - std::size_t size = wcstombs(0, ws.c_str(), 0); - if (size == std::size_t(-1)) return s; - std::string ret; - ret.resize(size); - size = wcstombs(&ret[0], ws.c_str(), size + 1); - if (size == wchar_t(-1)) return s; - ret.resize(size); - return ret; - } - catch(std::exception) - { - return s; - } - } -#else - std::string utf8_native(std::string const& s) - { - return s; - } -#endif - -} - -namespace libtorrent -{ - - const file::open_mode file::in(mode_in); - const file::open_mode file::out(mode_out); - - const file::seek_mode file::begin(1); - const file::seek_mode file::end(2); - - struct file::impl - { - impl() - : m_fd(-1) - , m_open_mode(0) - {} - - impl(fs::path const& path, int mode) - : m_fd(-1) - , m_open_mode(0) - { - open(path, mode); - } - - ~impl() - { - close(); - } - - void open(fs::path const& path, int mode) - { - assert(path.is_complete()); - close(); -#if defined(_WIN32) && defined(UNICODE) - std::wstring wpath(safe_convert(path.native_file_string())); - m_fd = ::_wopen( - wpath.c_str() - , map_open_mode(mode) - , S_IREAD | S_IWRITE); -#else - m_fd = ::open( - utf8_native(path.native_file_string()).c_str() - , map_open_mode(mode) -#ifdef _WIN32 - , S_IREAD | S_IWRITE); -#else - , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -#endif -#endif - if (m_fd == -1) - { - std::stringstream msg; - msg << "open failed: '" << path.native_file_string() << "'. " - << strerror(errno); - throw file_error(msg.str()); - } - m_open_mode = mode; - } - - void close() - { - if (m_fd == -1) return; - - ::close(m_fd); - m_fd = -1; - m_open_mode = 0; - } - - size_type read(char* buf, size_type num_bytes) - { - assert(m_open_mode & mode_in); - assert(m_fd != -1); - - size_type ret = ::read(m_fd, buf, num_bytes); - if (ret == -1) - { - std::stringstream msg; - msg << "read failed: " << strerror(errno); - throw file_error(msg.str()); - } - return ret; - } - - size_type write(const char* buf, size_type num_bytes) - { - assert(m_open_mode & mode_out); - assert(m_fd != -1); - - // TODO: Test this a bit more, what happens with random failures in - // the files? -// if ((rand() % 100) > 80) -// throw file_error("debug"); - - size_type ret = ::write(m_fd, buf, num_bytes); - if (ret == -1) - { - std::stringstream msg; - msg << "write failed: " << strerror(errno); - throw file_error(msg.str()); - } - return ret; - } - - size_type seek(size_type offset, int m) - { - assert(m_open_mode); - assert(m_fd != -1); - - int seekdir = (m == 1)?SEEK_SET:SEEK_END; -#ifdef _WIN32 - size_type ret = _lseeki64(m_fd, offset, seekdir); -#else - size_type ret = lseek(m_fd, offset, seekdir); -#endif - - // For some strange reason this fails - // on win32. Use windows specific file - // wrapper instead. - if (ret == -1) - { - std::stringstream msg; - msg << "seek failed: '" << strerror(errno) - << "' fd: " << m_fd - << " offset: " << offset - << " seekdir: " << seekdir; - throw file_error(msg.str()); - } - return ret; - } - - size_type tell() - { - assert(m_open_mode); - assert(m_fd != -1); - -#ifdef _WIN32 - return _telli64(m_fd); -#else - return lseek(m_fd, 0, SEEK_CUR); -#endif - } - - int m_fd; - int m_open_mode; - }; - - // pimpl forwardings - - file::file() : m_impl(new impl()) {} - - file::file(boost::filesystem::path const& p, file::open_mode m) - : m_impl(new impl(p, m.m_mask)) - {} - - file::~file() {} - - void file::open(boost::filesystem::path const& p, file::open_mode m) - { - m_impl->open(p, m.m_mask); - } - - void file::close() - { - m_impl->close(); - } - - size_type file::write(const char* buf, size_type num_bytes) - { - return m_impl->write(buf, num_bytes); - } - - size_type file::read(char* buf, size_type num_bytes) - { - return m_impl->read(buf, num_bytes); - } - - size_type file::seek(size_type pos, file::seek_mode m) - { - return m_impl->seek(pos, m.m_val); - } - - size_type file::tell() - { - return m_impl->tell(); - } - -} diff --git a/libtorrent/cpp/http_tracker_connection.cpp b/libtorrent/cpp/http_tracker_connection.cpp deleted file mode 100755 index 3299cf5b8..000000000 --- a/libtorrent/cpp/http_tracker_connection.cpp +++ /dev/null @@ -1,845 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include - -#include "zlib.h" - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/tracker_manager.hpp" -#include "libtorrent/http_tracker_connection.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/torrent.hpp" -#include "libtorrent/io.hpp" - -using namespace libtorrent; -using boost::bind; - -namespace -{ - enum - { - minimum_tracker_response_length = 3, - http_buffer_size = 2048 - }; - - - enum - { - FTEXT = 0x01, - FHCRC = 0x02, - FEXTRA = 0x04, - FNAME = 0x08, - FCOMMENT = 0x10, - FRESERVED = 0xe0, - - GZIP_MAGIC0 = 0x1f, - GZIP_MAGIC1 = 0x8b - }; - -} - -using namespace boost::posix_time; - -namespace libtorrent -{ - http_parser::http_parser() - : m_recv_pos(0) - , m_status_code(-1) - , m_content_length(-1) - , m_content_encoding(plain) - , m_state(read_status) - , m_recv_buffer(0, 0) - , m_body_start_pos(0) - , m_finished(false) - {} - - boost::tuple http_parser::incoming(buffer::const_interval recv_buffer) - { - m_recv_buffer = recv_buffer; - boost::tuple ret(0, 0); - - char const* pos = recv_buffer.begin + m_recv_pos; - if (m_state == read_status) - { - assert(!m_finished); - char const* newline = std::find(pos, recv_buffer.end, '\n'); - // if we don't have a full line yet, wait. - if (newline == recv_buffer.end) return ret; - - if (newline == pos) - throw std::runtime_error("unexpected newline in HTTP response"); - - std::istringstream line(std::string(pos, newline - 1)); - ++newline; - int incoming = (int)std::distance(pos, newline); - m_recv_pos += incoming; - boost::get<1>(ret) += incoming; - pos = newline; - - line >> m_protocol; - if (m_protocol.substr(0, 5) != "HTTP/") - { - throw std::runtime_error("unknown protocol in HTTP response: " - + m_protocol); - } - line >> m_status_code; - std::getline(line, m_server_message); - m_state = read_header; - } - - if (m_state == read_header) - { - assert(!m_finished); - char const* newline = std::find(pos, recv_buffer.end, '\n'); - std::string line; - - while (newline != recv_buffer.end && m_state == read_header) - { - if (newline == pos) - throw std::runtime_error("unexpected newline in HTTP response"); - - line.assign(pos, newline - 1); - m_recv_pos += newline - pos; - boost::get<1>(ret) += newline - pos; - pos = newline; - - std::string::size_type separator = line.find(": "); - if (separator == std::string::npos) - { - ++pos; - ++m_recv_pos; - boost::get<1>(ret) += 1; - - m_state = read_body; - m_body_start_pos = m_recv_pos; - break; - } - - std::string name = line.substr(0, separator); - std::string value = line.substr(separator + 2, std::string::npos); - m_header.insert(std::make_pair(name, value)); - - if (name == "Content-Length") - { - try - { - m_content_length = boost::lexical_cast(value); - } - catch(boost::bad_lexical_cast&) {} - } - else if (name == "Content-Encoding") - { - if (value == "gzip" || value == "x-gzip") - { - m_content_encoding = gzip; - } - else - { - std::string error_str = "unknown content encoding in response: \""; - error_str += value; - error_str += "\""; - throw std::runtime_error(error_str); - } - } - // TODO: make sure we don't step outside of the buffer - ++pos; - ++m_recv_pos; - assert(m_recv_pos <= (int)recv_buffer.left()); - newline = std::find(pos, recv_buffer.end, '\n'); - } - } - - if (m_state == read_body) - { - int incoming = recv_buffer.end - pos; - if (m_recv_pos - m_body_start_pos + incoming > m_content_length - && m_content_length >= 0) - incoming = m_content_length - m_recv_pos + m_body_start_pos; - - assert(incoming >= 0); - m_recv_pos += incoming; - boost::get<0>(ret) += incoming; - - if (m_content_length >= 0 - && m_recv_pos - m_body_start_pos >= m_content_length) - { - m_finished = true; - } - } - return ret; - } - - buffer::const_interval http_parser::get_body() - { - char const* body_begin = m_recv_buffer.begin + m_body_start_pos; - char const* body_end = m_recv_buffer.begin + m_recv_pos; - - m_recv_pos = 0; - m_body_start_pos = 0; - m_status_code = -1; - m_content_length = -1; - m_finished = false; - m_state = read_status; - m_header.clear(); - - return buffer::const_interval(body_begin, body_end); - } - - http_tracker_connection::http_tracker_connection( - demuxer& d - , tracker_manager& man - , tracker_request const& req - , std::string const& hostname - , unsigned short port - , std::string request - , boost::weak_ptr c - , session_settings const& stn - , std::string const& auth) - : tracker_connection(man, req, d, c) - , m_man(man) - , m_state(read_status) - , m_content_encoding(plain) - , m_content_length(0) - , m_name_lookup(d) - , m_port(port) - , m_recv_pos(0) - , m_buffer(http_buffer_size) - , m_settings(stn) - , m_password(auth) - , m_code(0) - , m_timed_out(false) - { - const std::string* connect_to_host; - bool using_proxy = false; - - m_send_buffer.assign("GET "); - - // should we use the proxy? - if (!m_settings.proxy_ip.empty()) - { - connect_to_host = &m_settings.proxy_ip; - using_proxy = true; - m_send_buffer += "http://"; - m_send_buffer += hostname; - if (port != 80) - { - m_send_buffer += ":"; - m_send_buffer += boost::lexical_cast(port); - } - m_port = m_settings.proxy_port != 0 - ? m_settings.proxy_port : 80 ; - } - else - { - connect_to_host = &hostname; - } - - if (tracker_req().kind == tracker_request::scrape_request) - { - // find and replace "announce" with "scrape" - // in request - - std::size_t pos = request.find("announce"); - if (pos == std::string::npos) - throw std::runtime_error("scrape is not available on url: '" - + tracker_req().url +"'"); - request.replace(pos, 8, "scrape"); - } - - m_send_buffer += request; - - // if request-string already contains - // some parameters, append an ampersand instead - // of a question mark - if (request.find('?') != std::string::npos) - m_send_buffer += "&"; - else - m_send_buffer += "?"; - - m_send_buffer += "info_hash="; - m_send_buffer += escape_string( - reinterpret_cast(req.info_hash.begin()), 20); - - if (tracker_req().kind == tracker_request::announce_request) - { - m_send_buffer += "&peer_id="; - m_send_buffer += escape_string( - reinterpret_cast(req.pid.begin()), 20); - - m_send_buffer += "&port="; - m_send_buffer += boost::lexical_cast(req.listen_port); - - m_send_buffer += "&uploaded="; - m_send_buffer += boost::lexical_cast(req.uploaded); - - m_send_buffer += "&downloaded="; - m_send_buffer += boost::lexical_cast(req.downloaded); - - m_send_buffer += "&left="; - m_send_buffer += boost::lexical_cast(req.left); - - if (req.event != tracker_request::none) - { - const char* event_string[] = {"completed", "started", "stopped"}; - m_send_buffer += "&event="; - m_send_buffer += event_string[req.event - 1]; - } - m_send_buffer += "&key="; - std::stringstream key_string; - key_string << std::hex << req.key; - m_send_buffer += key_string.str(); - m_send_buffer += "&compact=1"; - m_send_buffer += "&numwant="; - m_send_buffer += boost::lexical_cast( - std::min(req.num_want, 999)); - - // extension that tells the tracker that - // we don't need any peer_id's in the response - m_send_buffer += "&no_peer_id=1"; - } - - m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n" - "User-Agent: "; - m_send_buffer += m_settings.user_agent; - m_send_buffer += "\r\n" - "Host: "; - m_send_buffer += hostname; - if (port != 80) - { - m_send_buffer += ':'; - m_send_buffer += boost::lexical_cast(port); - } - if (using_proxy && !m_settings.proxy_login.empty()) - { - m_send_buffer += "\r\nProxy-Authorization: Basic "; - m_send_buffer += base64encode(m_settings.proxy_login + ":" + m_settings.proxy_password); - } - if (auth != "") - { - m_send_buffer += "\r\nAuthorization: Basic "; - m_send_buffer += base64encode(auth); - } - m_send_buffer += "\r\n\r\n"; -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) - { - requester().debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]"); - std::stringstream info_hash_str; - info_hash_str << req.info_hash; - requester().debug_log("info_hash: " + info_hash_str.str() + "\n"); - } -#endif - - tcp::resolver::query q(*connect_to_host, "0"); - m_name_lookup.async_resolve(q - , boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2)); - set_timeout(m_settings.tracker_completion_timeout - , m_settings.tracker_receive_timeout); - } - - void http_tracker_connection::on_timeout() - { - m_timed_out = true; - m_socket.reset(); - m_name_lookup.cancel(); - fail_timeout(); - } - - void http_tracker_connection::name_lookup(asio::error const& error - , tcp::resolver::iterator i) try - { - if (error == asio::error::operation_aborted) return; - if (m_timed_out) return; - - if (error || i == tcp::resolver::iterator()) - { - fail(-1, error.what()); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log("tracker name lookup successful"); -#endif - restart_read_timeout(); - m_socket.reset(new stream_socket(m_name_lookup.io_service())); - tcp::endpoint a(i->endpoint().address(), m_port); - if (has_requester()) requester().m_tracker_address = a; - m_socket->async_connect(a, bind(&http_tracker_connection::connected, self(), _1)); - } - catch (std::exception& e) - { - assert(false); - fail(-1, e.what()); - }; - - void http_tracker_connection::connected(asio::error const& error) try - { - if (error == asio::error::operation_aborted) return; - if (m_timed_out) return; - if (error) - { - fail(-1, error.what()); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log("tracker connection successful"); -#endif - - restart_read_timeout(); - async_write(*m_socket, asio::buffer(m_send_buffer.c_str() - , m_send_buffer.size()), bind(&http_tracker_connection::sent - , self(), _1)); - } - catch (std::exception& e) - { - assert(false); - fail(-1, e.what()); - } - - void http_tracker_connection::sent(asio::error const& error) try - { - if (error == asio::error::operation_aborted) return; - if (m_timed_out) return; - if (error) - { - fail(-1, error.what()); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log("tracker send data completed"); -#endif - restart_read_timeout(); - assert(m_buffer.size() - m_recv_pos > 0); - m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos] - , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive - , self(), _1, _2)); - } - catch (std::exception& e) - { - assert(false); - fail(-1, e.what()); - }; // msvc 7.1 seems to require this semi-colon - - - void http_tracker_connection::receive(asio::error const& error - , std::size_t bytes_transferred) try - { - if (error == asio::error::operation_aborted) return; - if (m_timed_out) return; - - if (error) - { - if (error == asio::error::eof) - { - on_response(); - close(); - return; - } - - fail(-1, error.what()); - return; - } - - restart_read_timeout(); - assert(bytes_transferred > 0); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log("tracker connection reading " - + boost::lexical_cast(bytes_transferred)); -#endif - - m_recv_pos += bytes_transferred; - - // if the receive buffer is full, expand it with http_buffer_size - if ((int)m_buffer.size() == m_recv_pos) - { - if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length) - { - fail(200, "too large tracker response"); - return; - } - assert(http_buffer_size > 0); - if ((int)m_buffer.size() + http_buffer_size - > m_settings.tracker_maximum_response_length) - m_buffer.resize(m_settings.tracker_maximum_response_length); - else - m_buffer.resize(m_buffer.size() + http_buffer_size); - } - - if (m_state == read_status) - { - std::vector::iterator end = m_buffer.begin()+m_recv_pos; - std::vector::iterator newline = std::find(m_buffer.begin(), end, '\n'); - // if we don't have a full line yet, wait. - if (newline != end) - { - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log(std::string(m_buffer.begin(), newline)); -#endif - - std::istringstream line(std::string(m_buffer.begin(), newline)); - ++newline; - m_recv_pos -= (int)std::distance(m_buffer.begin(), newline); - m_buffer.erase(m_buffer.begin(), newline); - - std::string protocol; - line >> m_server_protocol; - if (m_server_protocol.substr(0, 5) != "HTTP/") - { - std::string error_msg = "unknown protocol in response: " + m_server_protocol; - fail(-1, error_msg.c_str()); - return; - } - line >> m_code; - std::getline(line, m_server_message); - m_state = read_header; - } - } - - if (m_state == read_header) - { - std::vector::iterator end = m_buffer.begin() + m_recv_pos; - std::vector::iterator newline - = std::find(m_buffer.begin(), end, '\n'); - std::string line; - - while (newline != end && m_state == read_header) - { - line.assign(m_buffer.begin(), newline); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log(line); -#endif - - if (line.substr(0, 16) == "Content-Length: ") - { - try - { - m_content_length = boost::lexical_cast( - line.substr(16, line.length() - 17)); - } - catch(boost::bad_lexical_cast&) - { - fail(-1, "invalid content-length in tracker response"); - return; - } - if (m_content_length > m_settings.tracker_maximum_response_length) - { - fail(-1, "content-length is greater than maximum response length"); - return; - } - - if (m_content_length < minimum_tracker_response_length && m_code == 200) - { - fail(-1, "content-length is smaller than minimum response length"); - return; - } - } - else if (line.substr(0, 18) == "Content-Encoding: ") - { - if (line.substr(18, 4) == "gzip" || line.substr(18, 6) == "x-gzip") - { - m_content_encoding = gzip; - } - else - { - std::string error_str = "unknown content encoding in response: \""; - error_str += line.substr(18, line.length() - 18 - 2); - error_str += "\""; - fail(-1, error_str.c_str()); - return; - } - } - else if (line.substr(0, 10) == "Location: ") - { - m_location.assign(line.begin() + 10, line.end()); - } - else if (line.substr(0, 7) == "Server: ") - { - m_server.assign(line.begin() + 7, line.end()); - } - else if (line.size() < 3) - { - m_state = read_body; -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log("end of http header"); -#endif - if (m_code >= 300 && m_code < 400) - { - if (m_location.empty()) - { - std::string error_str = "got redirection response ("; - error_str += boost::lexical_cast(m_code); - error_str += ") without 'Location' header"; - fail(-1, error_str.c_str()); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log("Redirecting to \"" + m_location + "\""); -#endif - tracker_request req = tracker_req(); - std::string::size_type i = m_location.find('?'); - if (i == std::string::npos) - req.url = m_location; - else - req.url.assign(m_location.begin(), m_location.begin() + i); - - m_man.queue_request(m_socket->io_service(), req - , m_password, m_requester); - close(); - return; - } - } - - ++newline; - assert(m_recv_pos <= (int)m_buffer.size()); - m_recv_pos -= (int)std::distance(m_buffer.begin(), newline); - m_buffer.erase(m_buffer.begin(), newline); - assert(m_recv_pos <= (int)m_buffer.size()); - end = m_buffer.begin() + m_recv_pos; - newline = std::find(m_buffer.begin(), end, '\n'); - } - - } - - if (m_state == read_body) - { - if (m_recv_pos == m_content_length) - { - on_response(); - close(); - return; - } - } - else if (m_recv_pos > m_content_length && m_content_length > 0) - { - fail(-1, "invalid tracker response (body > content_length)"); - return; - } - - assert(m_buffer.size() - m_recv_pos > 0); - m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos] - , m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive - , self(), _1, _2)); - } - catch (std::exception& e) - { - assert(false); - fail(-1, e.what()); - }; - - void http_tracker_connection::on_response() - { - // GZIP - if (m_content_encoding == gzip) - { - boost::shared_ptr r = m_requester.lock(); - - if (!r) - { - close(); - return; - } - if (inflate_gzip(m_buffer, tracker_request(), r.get(), - m_settings.tracker_maximum_response_length)) - { - close(); - return; - } - } - - // handle tracker response - try - { - entry e = bdecode(m_buffer.begin(), m_buffer.end()); - parse(e); - } - catch (std::exception& e) - { - std::string error_str(e.what()); - error_str += ": "; - error_str.append(m_buffer.begin(), m_buffer.end()); - fail(m_code, error_str.c_str()); - } - #ifndef NDEBUG - catch (...) - { - assert(false); - } - #endif - } - - peer_entry http_tracker_connection::extract_peer_info(const entry& info) - { - peer_entry ret; - - // extract peer id (if any) - entry const* i = info.find_key("peer id"); - if (i != 0) - { - if (i->string().length() != 20) - throw std::runtime_error("invalid response from tracker"); - std::copy(i->string().begin(), i->string().end(), ret.pid.begin()); - } - else - { - // if there's no peer_id, just initialize it to a bunch of zeroes - std::fill_n(ret.pid.begin(), 20, 0); - } - - // extract ip - i = info.find_key("ip"); - if (i == 0) throw std::runtime_error("invalid response from tracker"); - ret.ip = i->string(); - - // extract port - i = info.find_key("port"); - if (i == 0) throw std::runtime_error("invalid response from tracker"); - ret.port = (unsigned short)i->integer(); - - return ret; - } - - void http_tracker_connection::parse(entry const& e) - { - if (!has_requester()) return; - - try - { - // parse the response - try - { - entry const& failure = e["failure reason"]; - - fail(m_code, failure.string().c_str()); - return; - } - catch (type_error const&) {} - - try - { - entry const& warning = e["warning message"]; - if (has_requester()) - requester().tracker_warning(warning.string()); - } - catch(type_error const&) {} - - std::vector peer_list; - - if (tracker_req().kind == tracker_request::scrape_request) - { - std::string ih; - std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end() - , std::back_inserter(ih)); - entry scrape_data = e["files"][ih]; - int complete = scrape_data["complete"].integer(); - int incomplete = scrape_data["incomplete"].integer(); - requester().tracker_response(tracker_request(), peer_list, 0, complete - , incomplete); - return; - } - - int interval = (int)e["interval"].integer(); - - if (e["peers"].type() == entry::string_t) - { - std::string const& peers = e["peers"].string(); - for (std::string::const_iterator i = peers.begin(); - i != peers.end();) - { - if (std::distance(i, peers.end()) < 6) break; - - peer_entry p; - p.pid.clear(); - std::stringstream ip_str; - ip_str << (int)detail::read_uint8(i) << "."; - ip_str << (int)detail::read_uint8(i) << "."; - ip_str << (int)detail::read_uint8(i) << "."; - ip_str << (int)detail::read_uint8(i); - p.ip = ip_str.str(); - p.port = detail::read_uint16(i); - peer_list.push_back(p); - } - } - else - { - entry::list_type const& l = e["peers"].list(); - for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i) - { - peer_entry p = extract_peer_info(*i); - peer_list.push_back(p); - } - } - - // look for optional scrape info - int complete = -1; - int incomplete = -1; - - try { complete = e["complete"].integer(); } - catch(type_error&) {} - - try { incomplete = e["incomplete"].integer(); } - catch(type_error&) {} - - requester().tracker_response(tracker_request(), peer_list, interval, complete - , incomplete); - } - catch(type_error& e) - { - requester().tracker_request_error(tracker_request(), m_code, e.what()); - } - catch(std::runtime_error& e) - { - requester().tracker_request_error(tracker_request(), m_code, e.what()); - } - } - -} - diff --git a/libtorrent/cpp/identify_client.cpp b/libtorrent/cpp/identify_client.cpp deleted file mode 100755 index 96039f2b0..000000000 --- a/libtorrent/cpp/identify_client.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/identify_client.hpp" -#include "libtorrent/fingerprint.hpp" - -namespace -{ - - using namespace libtorrent; - - int decode_digit(char c) - { - if (std::isdigit(c)) return c - '0'; - return unsigned(c) - 'A' + 10; - } - - // takes a peer id and returns a valid boost::optional - // object if the peer id matched the azureus style encoding - // the returned fingerprint contains information about the - // client's id - boost::optional parse_az_style(const peer_id& id) - { - fingerprint ret("..", 0, 0, 0, 0); - - if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0') - || (id[3] < '0') || (id[4] < '0') - || (id[5] < '0') || (id[6] < '0') - || id[7] != '-') - return boost::optional(); - - ret.name[0] = id[1]; - ret.name[1] = id[2]; - ret.major_version = decode_digit(id[3]); - ret.minor_version = decode_digit(id[4]); - ret.revision_version = decode_digit(id[5]); - ret.tag_version = decode_digit(id[6]); - - return boost::optional(ret); - } - - // checks if a peer id can possibly contain a shadow-style - // identification - boost::optional parse_shadow_style(const peer_id& id) - { - fingerprint ret("..", 0, 0, 0, 0); - - if (!std::isalnum(id[0])) - return boost::optional(); - - if (std::equal(id.begin()+4, id.begin()+6, "--")) - { - if ((id[1] < '0') || (id[2] < '0') - || (id[3] < '0')) - return boost::optional(); - ret.major_version = decode_digit(id[1]); - ret.minor_version = decode_digit(id[2]); - ret.revision_version = decode_digit(id[3]); - } - else - { - if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127) - return boost::optional(); - ret.major_version = id[1]; - ret.minor_version = id[2]; - ret.revision_version = id[3]; - } - - ret.name[0] = id[0]; - ret.name[1] = 0; - - ret.tag_version = 0; - return boost::optional(ret); - } - - // checks if a peer id can possibly contain a mainline-style - // identification - boost::optional parse_mainline_style(const peer_id& id) - { - char ids[21]; - std::copy(id.begin(), id.end(), ids); - ids[20] = 0; - fingerprint ret("..", 0, 0, 0, 0); - ret.name[1] = 0; - ret.tag_version = 0; - if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version - , &ret.revision_version) != 4 - || !std::isprint(ret.name[0])) - return boost::optional(); - - return boost::optional(ret); - } - - typedef std::pair map_entry; - - // only support BitTorrentSpecification - // must be ordered alphabetically - map_entry name_map[] = - { - map_entry("A", "ABC") - , map_entry("AR", "Arctic Torrent") - , map_entry("AX", "BitPump") - , map_entry("AZ", "Azureus") - , map_entry("BB", "BitBuddy") - , map_entry("BC", "BitComet") - , map_entry("BS", "BTSlave") - , map_entry("BX", "BittorrentX") - , map_entry("CD", "Enhanced CTorrent") - , map_entry("CT", "CTorrent") - , map_entry("DE", "Deluge") - , map_entry("ES", "electric sheep") - , map_entry("KT", "KTorrent") - , map_entry("LP", "lphant") - , map_entry("LT", "libtorrent") - , map_entry("M", "Mainline") - , map_entry("MP", "MooPolice") - , map_entry("MT", "Moonlight Torrent") - , map_entry("O", "Osprey Permaseed") - , map_entry("R", "Tribler") - , map_entry("S", "Shadow") - , map_entry("SB", "Swiftbit") - , map_entry("SN", "ShareNet") - , map_entry("SS", "SwarmScope") - , map_entry("SZ", "Shareaza") - , map_entry("T", "BitTornado") - , map_entry("TN", "Torrent.NET") - , map_entry("TR", "Transmission") - , map_entry("TS", "TorrentStorm") - , map_entry("U", "UPnP") - , map_entry("UL", "uLeecher") - , map_entry("UT", "MicroTorrent") - , map_entry("XT", "XanTorrent") - , map_entry("ZT", "ZipTorrent") - , map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)") - , map_entry("pX", "pHoeniX") - , map_entry("qB", "qBittorrent") - }; - - bool compare_first_string(map_entry const& lhs, map_entry const& rhs) - { - return lhs.first[0] < rhs.first[0] - || ((lhs.first[0] == rhs.first[0]) && (lhs.first[1] < rhs.first[1])); - } - - std::string lookup(fingerprint const& f) - { - std::stringstream identity; - - const int size = sizeof(name_map)/sizeof(name_map[0]); - map_entry* i = - std::lower_bound(name_map, name_map + size - , map_entry(f.name, ""), &compare_first_string); - -#ifndef NDEBUG - for (int i = 1; i < size; ++i) - { - assert(compare_first_string(name_map[i-1] - , name_map[i])); - } -#endif - - if (i < name_map + size && std::equal(f.name, f.name + 2, i->first)) - identity << i->second; - else - { - identity << f.name[0]; - if (f.name[1] != 0) identity << f.name[1]; - } - - identity << " " << (int)f.major_version - << "." << (int)f.minor_version - << "." << (int)f.revision_version; - - if (f.name[1] != 0) - identity << "." << (int)f.tag_version; - - return identity.str(); - } - - bool find_string(unsigned char const* id, char const* search) - { - return std::equal(search, search + std::strlen(search), id); - } -} - -namespace libtorrent -{ - - boost::optional client_fingerprint(peer_id const& p) - { - // look for azureus style id - boost::optional f; - f = parse_az_style(p); - if (f) return f; - - // look for shadow style id - f = parse_shadow_style(p); - if (f) return f; - - // look for mainline style id - f = parse_mainline_style(p); - if (f) return f; - return f; - } - - std::string identify_client(peer_id const& p) - { - peer_id::const_iterator PID = p.begin(); - boost::optional f; - - if (p.is_all_zeros()) return "Unknown"; - - // ---------------------- - // non standard encodings - // ---------------------- - - if (find_string(PID, "Deadman Walking-")) return "Deadman"; - if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2"; - if (find_string(PID, "DansClient")) return "XanTorrent"; - if (find_string(PID + 4, "btfans")) return "SimpleBT"; - if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II"; - if (find_string(PID, "P87.P---")) return "Bittorrent Plus!"; - if (find_string(PID, "S587Plus")) return "Bittorrent Plus!"; - if (find_string(PID, "martini")) return "Martini Man"; - if (find_string(PID, "Plus---")) return "Bittorrent Plus"; - if (find_string(PID, "turbobt")) return "TurboBT"; - if (find_string(PID, "a00---0")) return "Swarmy"; - if (find_string(PID, "a02---0")) return "Swarmy"; - if (find_string(PID, "T00---0")) return "Teeweety"; - if (find_string(PID, "BTDWV-")) return "Deadman Walking"; - if (find_string(PID + 2, "BS")) return "BitSpirit"; - if (find_string(PID, "btuga")) return "BTugaXP"; - if (find_string(PID, "oernu")) return "BTugaXP"; - if (find_string(PID, "Mbrst")) return "Burst!"; - if (find_string(PID, "Plus")) return "Plus!"; - if (find_string(PID, "-Qt-")) return "Qt"; - if (find_string(PID, "exbc")) return "BitComet"; - if (find_string(PID, "-G3")) return "G3 Torrent"; - if (find_string(PID, "XBT")) return "XBT"; - if (find_string(PID, "OP")) return "Opera"; - - if (find_string(PID, "-BOW") && PID[7] == '-') - return "Bits on Wheels " + std::string(PID + 4, PID + 7); - - - if (find_string(PID, "eX")) - { - std::string user(PID + 2, PID + 14); - return std::string("eXeem ('") + user.c_str() + "')"; - } - - if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97")) - return "Experimental 3.2.1b2"; - - if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0")) - return "Experimental 3.1"; - - - // look for azureus style id - f = parse_az_style(p); - if (f) return lookup(*f); - - // look for shadow style id - f = parse_shadow_style(p); - if (f) return lookup(*f); - - // look for mainline style id - f = parse_mainline_style(p); - if (f) return lookup(*f); - - - if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0")) - return "Generic"; - - std::string unknown("Unknown ["); - for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i) - { - unknown += std::isprint(*i)?*i:'.'; - } - unknown += "]"; - return unknown; - } - -} diff --git a/libtorrent/cpp/ip_filter.cpp b/libtorrent/cpp/ip_filter.cpp deleted file mode 100644 index 0afb5a346..000000000 --- a/libtorrent/cpp/ip_filter.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - -Copyright (c) 2005, 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/ip_filter.hpp" -#include -//#include - - -namespace libtorrent -{ - void ip_filter::add_rule(address first, address last, int flags) - { - if (first.is_v4()) - { - assert(last.is_v4()); - m_filter4.add_rule(first.to_v4(), last.to_v4(), flags); - } - else if (first.is_v6()) - { - assert(last.is_v6()); - m_filter6.add_rule(first.to_v6(), last.to_v6(), flags); - } - else - assert(false); - } - - int ip_filter::access(address const& addr) const - { - if (addr.is_v4()) - return m_filter4.access(addr.to_v4()); - assert(addr.is_v6()); - return m_filter6.access(addr.to_v6()); - } - - ip_filter::filter_tuple_t ip_filter::export_filter() const - { - return boost::make_tuple(m_filter4.export_filter() - , m_filter6.export_filter()); - } - -/* - void ip_filter::print() const - { - for (range_t::iterator i = m_access_list.begin(); i != m_access_list.end(); ++i) - { - std::cout << i->start.as_string() << " " << i->access << "\n"; - } - } -*/ -} - diff --git a/libtorrent/cpp/kademlia/closest_nodes.cpp b/libtorrent/cpp/kademlia/closest_nodes.cpp deleted file mode 100644 index 8d0ccea87..000000000 --- a/libtorrent/cpp/kademlia/closest_nodes.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg & Daniel Wallin -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 -#include -#include - -namespace libtorrent { namespace dht -{ - -using asio::ip::udp; - -typedef boost::shared_ptr observer_ptr; - -class closest_nodes_observer : public observer -{ -public: - closest_nodes_observer( - boost::intrusive_ptr const& algorithm - , node_id self - , node_id target - ) - : m_algorithm(algorithm) - , m_target(target) - , m_self(self) - {} - - void send(msg& p) - { - p.info_hash = m_target; - } - - void timeout(); - void reply(msg const&); - -private: - boost::intrusive_ptr m_algorithm; - node_id const m_target; - node_id const m_self; -}; - -void closest_nodes_observer::reply(msg const& in) -{ - if (!in.nodes.empty()) - { - for (msg::nodes_t::const_iterator i = in.nodes.begin() - , end(in.nodes.end()); i != end; ++i) - { - m_algorithm->traverse(i->id, i->addr); - } - } - m_algorithm->finished(m_self); -} - -void closest_nodes_observer::timeout() -{ - m_algorithm->failed(m_self); -} - - -closest_nodes::closest_nodes( - node_id target - , int branch_factor - , int max_results - , routing_table& table - , rpc_manager& rpc - , done_callback const& callback -) - : traversal_algorithm( - target - , branch_factor - , max_results - , table - , rpc - , table.begin() - , table.end() - ) - , m_done_callback(callback) -{ - boost::intrusive_ptr self(this); - add_requests(); -} - -void closest_nodes::invoke(node_id const& id, udp::endpoint addr) -{ - observer_ptr p(new closest_nodes_observer(this, id, m_target)); - m_rpc.invoke(messages::find_node, addr, p); -} - -void closest_nodes::done() -{ - std::vector results; - int result_size = m_table.bucket_size(); - if (result_size > (int)m_results.size()) result_size = (int)m_results.size(); - for (std::vector::iterator i = m_results.begin() - , end(m_results.begin() + result_size); i != end; ++i) - { - results.push_back(node_entry(i->id, i->addr)); - } - m_done_callback(results); -} - -void closest_nodes::initiate( - node_id target - , int branch_factor - , int max_results - , routing_table& table - , rpc_manager& rpc - , done_callback const& callback -) -{ - new closest_nodes(target, branch_factor, max_results, table, rpc, callback); -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent/cpp/kademlia/dht_tracker.cpp b/libtorrent/cpp/kademlia/dht_tracker.cpp deleted file mode 100644 index ca003e2b1..000000000 --- a/libtorrent/cpp/kademlia/dht_tracker.cpp +++ /dev/null @@ -1,905 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libtorrent/kademlia/node.hpp" -#include "libtorrent/kademlia/node_id.hpp" -#include "libtorrent/kademlia/traversal_algorithm.hpp" -#include "libtorrent/kademlia/dht_tracker.hpp" - -#include "libtorrent/socket.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/io.hpp" -#include "libtorrent/version.hpp" - -using boost::posix_time::ptime; -using boost::posix_time::time_duration; -using boost::posix_time::second_clock; -using boost::posix_time::microsec_clock; -using boost::posix_time::seconds; -using boost::posix_time::minutes; -using boost::posix_time::hours; -using boost::posix_time::milliseconds; -using boost::ref; -using boost::lexical_cast; -using libtorrent::dht::node_impl; -using libtorrent::dht::node_id; -using libtorrent::dht::packet_t; -using libtorrent::dht::msg; -using libtorrent::dht::packet_iterator; -namespace messages = libtorrent::dht::messages; -using namespace libtorrent::detail; - -using asio::ip::udp; -typedef asio::ip::address_v4 address; - -namespace -{ - const int tick_period = 1; // minutes - - struct count_peers - { - int& count; - count_peers(int& c): count(c) {} - void operator()(std::pair const& t) - { - count += std::distance(t.second.peers.begin() - , t.second.peers.end()); - } - }; - - boost::optional read_id(libtorrent::entry const& d) - { - using namespace libtorrent; - using libtorrent::dht::node_id; - - if (d.type() != entry::dictionary_t) return boost::optional(); - entry const* nid = d.find_key("node-id"); - if (!nid - || nid->type() != entry::string_t - || nid->string().length() != 40) - return boost::optional(); - return boost::optional( - boost::lexical_cast(nid->string())); - } - - template - void read_endpoint_list(libtorrent::entry const* n, std::vector& epl) - { - using namespace libtorrent; - entry::list_type const& contacts = n->list(); - for (entry::list_type::const_iterator i = contacts.begin() - , end(contacts.end()); i != end; ++i) - { - std::string const& p = i->string(); - if (p.size() < 6) continue; - std::string::const_iterator in = p.begin(); - if (p.size() == 6) - epl.push_back(read_v4_endpoint(in)); - else if (p.size() == 18) - epl.push_back(read_v6_endpoint(in)); - } - } - -} - -namespace libtorrent { namespace dht -{ -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_DEFINE_LOG(dht_tracker) -#endif - - // class that puts the networking and the kademlia node in a single - // unit and connecting them together. - dht_tracker::dht_tracker(asio::io_service& d, dht_settings const& settings - , asio::ip::address listen_interface, entry const& bootstrap) - : m_demuxer(d) - , m_socket(m_demuxer, udp::endpoint(listen_interface, settings.service_port)) - , m_dht(bind(&dht_tracker::send_packet, this, _1), settings - , read_id(bootstrap)) - , m_buffer(0) - , m_last_refresh(second_clock::universal_time() - hours(1)) - , m_timer(m_demuxer) - , m_connection_timer(m_demuxer) - , m_refresh_timer(m_demuxer) - , m_settings(settings) - , m_refresh_bucket(160) - , m_host_resolver(d) - { - using boost::bind; - - m_in_buf[0].resize(1000); - m_in_buf[1].resize(1000); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - m_counter = 0; - std::fill_n(m_replies_bytes_sent, 5, 0); - std::fill_n(m_queries_bytes_received, 5, 0); - std::fill_n(m_replies_sent, 5, 0); - std::fill_n(m_queries_received, 5, 0); - m_announces = 0; - m_failed_announces = 0; - m_total_message_input = 0; - m_ut_message_input = 0; - m_lt_message_input = 0; - m_mp_message_input = 0; - m_gr_message_input = 0; - m_total_in_bytes = 0; - m_total_out_bytes = 0; - m_queries_out_bytes = 0; - - // turns on and off individual components' logging - -// rpc_log().enable(false); -// node_log().enable(false); -// traversal_log().enable(false); -// dht_tracker_log.enable(false); - -#endif - std::vector initial_nodes; - - if (bootstrap.type() == entry::dictionary_t) - { - try - { - if (entry const* nodes = bootstrap.find_key("nodes")) - read_endpoint_list(nodes, initial_nodes); - } catch (std::exception&) {} - } - - m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, this)); - - m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0] - , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer] - , bind(&dht_tracker::on_receive, this, _1, _2)); - m_timer.expires_from_now(seconds(1)); - m_timer.async_wait(bind(&dht_tracker::tick, this, _1)); - - m_connection_timer.expires_from_now(seconds(10)); - m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1)); - - m_refresh_timer.expires_from_now(minutes(15)); - m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1)); - } - - void dht_tracker::dht_status(session_status& s) - { - boost::tie(s.m_dht_nodes, s.m_dht_node_cache) = m_dht.size(); - s.m_dht_torrents = m_dht.data_size(); - } - - void dht_tracker::connection_timeout(asio::error const& e) - try - { - if (e) return; - time_duration d = m_dht.connection_timeout(); - m_connection_timer.expires_from_now(d); - m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1)); - } - catch (std::exception&) - { - assert(false); - }; - - void dht_tracker::refresh_timeout(asio::error const& e) - try - { - if (e) return; - time_duration d = m_dht.refresh_timeout(); - m_refresh_timer.expires_from_now(d); - m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1)); - } - catch (std::exception&) - { - assert(false); - }; - - void dht_tracker::rebind(asio::ip::address listen_interface, int listen_port) - { - m_socket.close(); - m_socket.open(asio::ip::udp::v4()); - m_socket.bind(udp::endpoint(listen_interface, listen_port)); - } - - void dht_tracker::tick(asio::error const& e) - try - { - if (e) return; - m_timer.expires_from_now(minutes(tick_period)); - m_timer.async_wait(bind(&dht_tracker::tick, this, _1)); - - m_dht.new_write_key(); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - static bool first = true; - if (first) - { - boost::filesystem::create_directory("libtorrent_logs"); - } - - std::ofstream st("libtorrent_logs/routing_table_state.txt", std::ios_base::trunc); - m_dht.print_state(st); - - // count torrents - int torrents = std::distance(m_dht.begin_data(), m_dht.end_data()); - - // count peers - int peers = 0; - std::for_each(m_dht.begin_data(), m_dht.end_data(), count_peers(peers)); - - std::ofstream pc("libtorrent_logs/dht_stats.log", std::ios_base::app); - if (first) - { - first = false; - using boost::posix_time::to_simple_string; - pc << "\n\n ***** starting log at " << to_simple_string( - second_clock::universal_time()) << " *****\n\n" - << "minute:active nodes:passive nodes" - ":ping replies sent:ping queries recvd:ping" - ":ping replies sent:ping queries recvd:ping" - ":find_node replies bytes sent:find_node queries bytes recv" - ":find_node replies bytes sent:find_node queries bytes recv" - ":get_peers replies sent:get_peers queries recvd:get_peers" - ":get_peers replies bytes sent:get_peers queries bytes recv" - ":announce_peer replies sent:announce_peer queries recvd:announce_peer" - ":announce_peer replies bytes sent:announce_peer queries bytes recv" - ":error replies sent:error queries recvd:error" - ":error replies bytes sent:error queries bytes recv" - ":num torrents:num peers:announces per min" - ":failed announces per min:total msgs per min" - ":ut msgs per min:lt msgs per min:mp msgs per min" - ":gr msgs per min:bytes in per sec:bytes out per sec" - ":queries out bytes per sec\n\n"; - } - - int active; - int passive; - boost::tie(active, passive) = m_dht.size(); - pc << (m_counter * tick_period) - << "\t" << active - << "\t" << passive; - for (int i = 0; i < 5; ++i) - pc << "\t" << (m_replies_sent[i] / float(tick_period)) - << "\t" << (m_queries_received[i] / float(tick_period)) - << "\t" << (m_replies_bytes_sent[i] / float(tick_period*60)) - << "\t" << (m_queries_bytes_received[i] / float(tick_period*60)); - - pc << "\t" << torrents - << "\t" << peers - << "\t" << m_announces / float(tick_period) - << "\t" << m_failed_announces / float(tick_period) - << "\t" << (m_total_message_input / float(tick_period)) - << "\t" << (m_ut_message_input / float(tick_period)) - << "\t" << (m_lt_message_input / float(tick_period)) - << "\t" << (m_mp_message_input / float(tick_period)) - << "\t" << (m_gr_message_input / float(tick_period)) - << "\t" << (m_total_in_bytes / float(tick_period*60)) - << "\t" << (m_total_out_bytes / float(tick_period*60)) - << "\t" << (m_queries_out_bytes / float(tick_period*60)) - << std::endl; - ++m_counter; - std::fill_n(m_replies_bytes_sent, 5, 0); - std::fill_n(m_queries_bytes_received, 5, 0); - std::fill_n(m_replies_sent, 5, 0); - std::fill_n(m_queries_received, 5, 0); - m_announces = 0; - m_failed_announces = 0; - m_total_message_input = 0; - m_ut_message_input = 0; - m_lt_message_input = 0; - m_total_in_bytes = 0; - m_total_out_bytes = 0; - m_queries_out_bytes = 0; -#endif - } - catch (std::exception&) - { - assert(false); - }; - - void dht_tracker::announce(sha1_hash const& ih, int listen_port - , boost::function const& - , sha1_hash const&)> f) - { - m_dht.announce(ih, listen_port, f); - } - - // translate bittorrent kademlia message into the generice kademlia message - // used by the library - void dht_tracker::on_receive(asio::error const& error, size_t bytes_transferred) - try - { - if (error == asio::error::operation_aborted) return; - - int current_buffer = m_buffer; - m_buffer = (m_buffer + 1) & 1; - m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0] - , m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer] - , bind(&dht_tracker::on_receive, this, _1, _2)); - - if (error) return; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - ++m_total_message_input; - m_total_in_bytes += bytes_transferred; -#endif - - try - { - using libtorrent::entry; - using libtorrent::bdecode; - - assert(bytes_transferred > 0); - - entry e = bdecode(m_in_buf[current_buffer].begin() - , m_in_buf[current_buffer].end()); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << microsec_clock::universal_time() - << " RECEIVED [" << m_remote_endpoint[current_buffer] - << "]:"; -#endif - - libtorrent::dht::msg m; - m.message_id = 0; - m.addr = m_remote_endpoint[current_buffer]; - m.transaction_id = e["t"].string(); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - try - { - entry const* ver = e.find_key("v"); - if (!ver) throw std::exception(); - - std::string const& client = ver->string(); - if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT")) - { - ++m_ut_message_input; - TORRENT_LOG(dht_tracker) << " client: uTorrent"; - } - else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT")) - { - ++m_lt_message_input; - TORRENT_LOG(dht_tracker) << " client: libtorrent"; - } - else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP")) - { - ++m_mp_message_input; - TORRENT_LOG(dht_tracker) << " client: MooPolice"; - } - else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR")) - { - ++m_gr_message_input; - TORRENT_LOG(dht_tracker) << " client: GetRight"; - } - else - { - TORRENT_LOG(dht_tracker) << " client: generic"; - } - } - catch (std::exception&) - { - TORRENT_LOG(dht_tracker) << " client: generic"; - }; -#endif - - std::string const& msg_type = e["y"].string(); - - if (msg_type == "r") - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " reply: transaction: " - << m.transaction_id; -#endif - - m.reply = true; - entry const& r = e["r"]; - std::string const& id = r["id"].string(); - if (id.size() != 20) throw std::runtime_error("invalid size of id"); - std::copy(id.begin(), id.end(), m.id.begin()); - - if (entry const* n = r.find_key("values")) - { - m.peers.clear(); - read_endpoint_list(n, m.peers); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); -#endif - } - - m.nodes.clear(); - if (entry const* n = r.find_key("nodes")) - { - std::string const& nodes = n->string(); - std::string::const_iterator i = nodes.begin(); - std::string::const_iterator end = nodes.end(); - - while (std::distance(i, end) >= 26) - { - node_id id; - std::copy(i, i + 20, id.begin()); - i += 20; - m.nodes.push_back(libtorrent::dht::node_entry( - id, read_v4_endpoint(i))); - } -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); -#endif - } - - if (entry const* n = r.find_key("nodes2")) - { - entry::list_type const& contacts = n->list(); - for (entry::list_type::const_iterator i = contacts.begin() - , end(contacts.end()); i != end; ++i) - { - std::string const& p = i->string(); - if (p.size() < 6 + 20) continue; - std::string::const_iterator in = p.begin(); - - node_id id; - std::copy(in, in + 20, id.begin()); - in += 20; - if (p.size() == 6 + 20) - m.nodes.push_back(libtorrent::dht::node_entry( - id, read_v4_endpoint(in))); - else if (p.size() == 18 + 20) - m.nodes.push_back(libtorrent::dht::node_entry( - id, read_v6_endpoint(in))); - } -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size(); -#endif - } - - entry const* token = r.find_key("token"); - if (token) m.write_token = *token; - } - else if (msg_type == "q") - { - m.reply = false; - entry const& a = e["a"]; - std::string const& id = a["id"].string(); - if (id.size() != 20) throw std::runtime_error("invalid size of id"); - std::copy(id.begin(), id.end(), m.id.begin()); - - std::string request_kind(e["q"].string()); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " query: " << request_kind; -#endif - - if (request_kind == "ping") - { - m.message_id = libtorrent::dht::messages::ping; - } - else if (request_kind == "find_node") - { - std::string const& target = a["target"].string(); - if (target.size() != 20) throw std::runtime_error("invalid size of target id"); - std::copy(target.begin(), target.end(), m.info_hash.begin()); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " target: " - << boost::lexical_cast(m.info_hash); -#endif - - m.message_id = libtorrent::dht::messages::find_node; - } - else if (request_kind == "get_peers") - { - std::string const& info_hash = a["info_hash"].string(); - if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash"); - std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); - m.message_id = libtorrent::dht::messages::get_peers; -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " info_hash: " - << boost::lexical_cast(m.info_hash); -#endif - } - else if (request_kind == "announce_peer") - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - ++m_announces; -#endif - std::string const& info_hash = a["info_hash"].string(); - if (info_hash.size() != 20) - throw std::runtime_error("invalid size of info-hash"); - std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin()); - m.port = a["port"].integer(); - m.write_token = a["token"]; - m.message_id = libtorrent::dht::messages::announce_peer; -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " info_hash: " - << boost::lexical_cast(m.info_hash); - TORRENT_LOG(dht_tracker) << " port: " << m.port; - - if (!m_dht.verify_token(m)) - ++m_failed_announces; -#endif - } - else - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : " - << request_kind; -#endif - throw std::runtime_error("unsupported request: " + request_kind); - } - } - else if (msg_type == "e") - { - entry::list_type const& list = e["e"].list(); - m.message_id = messages::error; - m.error_msg = list.back().string(); - m.error_code = list.front().integer(); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " " - << m.error_msg; -#endif - } - else - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : " - << msg_type; -#endif - throw std::runtime_error("unsupported message type: " + msg_type); - } - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - if (!m.reply) - { - ++m_queries_received[m.message_id]; - m_queries_bytes_received[m.message_id] += int(bytes_transferred); - } - TORRENT_LOG(dht_tracker) << e; -#endif - - m_dht.incoming(m); - } - catch (std::exception& e) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << "invalid incoming packet: " - << e.what(); -#endif - } - } - catch (std::exception& e) - { - assert(false); - }; - - entry dht_tracker::state() const - { - entry ret(entry::dictionary_t); - { - entry nodes(entry::list_t); - for (node_impl::iterator i(m_dht.begin()) - , end(m_dht.end()); i != end; ++i) - { - std::string node; - std::back_insert_iterator out(node); - write_endpoint(i->addr, out); - nodes.list().push_back(entry(node)); - } - bucket_t cache; - m_dht.replacement_cache(cache); - for (bucket_t::iterator i(cache.begin()) - , end(cache.end()); i != end; ++i) - { - std::string node; - std::back_insert_iterator out(node); - write_endpoint(i->addr, out); - nodes.list().push_back(entry(node)); - } - if (!nodes.list().empty()) - ret["nodes"] = nodes; - } - - ret["node-id"] = boost::lexical_cast(m_dht.nid()); - return ret; - } - - void dht_tracker::add_node(udp::endpoint node) - { - m_dht.add_node(node); - } - - void dht_tracker::add_node(std::pair const& node) - { - udp::resolver::query q(node.first, lexical_cast(node.second)); - m_host_resolver.async_resolve(q, bind(&dht_tracker::on_name_lookup - , this, _1, _2)); - } - - void dht_tracker::on_name_lookup(asio::error const& e - , udp::resolver::iterator host) try - { - if (e || host == udp::resolver::iterator()) return; - add_node(host->endpoint()); - } - catch (std::exception&) - { - assert(false); - }; - - void dht_tracker::add_router_node(std::pair const& node) - { - udp::resolver::query q(node.first, lexical_cast(node.second)); - m_host_resolver.async_resolve(q, bind(&dht_tracker::on_router_name_lookup - , this, _1, _2)); - } - - void dht_tracker::on_router_name_lookup(asio::error const& e - , udp::resolver::iterator host) try - { - if (e || host == udp::resolver::iterator()) return; - m_dht.add_router_node(host->endpoint()); - } - catch (std::exception&) - { - assert(false); - }; - - void dht_tracker::on_bootstrap() - {} - - void dht_tracker::send_packet(msg const& m) - { - using libtorrent::bencode; - using libtorrent::entry; - entry e(entry::dictionary_t); - e["t"] = m.transaction_id; - std::string version_str("LT "); - std::string::iterator i = version_str.begin() + 2; - detail::write_uint8(LIBTORRENT_VERSION_MAJOR, i); - detail::write_uint8(LIBTORRENT_VERSION_MINOR, i); - e["v"] = version_str; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << microsec_clock::universal_time() - << " SENDING [" << m.addr << "]:"; - TORRENT_LOG(dht_tracker) << " transaction: " << m.transaction_id; -// e.print(std::cerr); -#endif - - if (m.message_id == messages::error) - { - assert(m.reply); - e["y"] = "e"; - entry error_list(entry::list_t); - error_list.list().push_back(entry(m.error_code)); - error_list.list().push_back(entry(m.error_msg)); - e["e"] = error_list; -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " " - << m.error_msg; -#endif - } - else if (m.reply) - { - e["y"] = "r"; - e["r"] = entry(entry::dictionary_t); - entry& r = e["r"]; - r["id"] = std::string(m.id.begin(), m.id.end()); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " reply: " - << messages::ids[m.message_id]; -#endif - - if (m.write_token.type() != entry::undefined_t) - r["token"] = m.write_token; - - switch (m.message_id) - { - case messages::ping: - break; - case messages::find_node: - { - bool ipv6_nodes = false; - r["nodes"] = entry(entry::string_t); - entry& n = r["nodes"]; - std::back_insert_iterator out(n.string()); - for (msg::nodes_t::const_iterator i = m.nodes.begin() - , end(m.nodes.end()); i != end; ++i) - { - if (!i->addr.address().is_v4()) - { - ipv6_nodes = true; - continue; - } - std::copy(i->id.begin(), i->id.end(), out); - write_endpoint(i->addr, out); - } - - if (ipv6_nodes) - { - r["nodes2"] = entry(entry::list_t); - entry& p = r["nodes2"]; - std::string endpoint; - endpoint.resize(6); - for (msg::nodes_t::const_iterator i = m.nodes.begin() - , end(m.nodes.end()); i != end; ++i) - { - std::string::iterator out = endpoint.begin(); - std::copy(i->id.begin(), i->id.end(), out); - write_endpoint(i->addr, out); - p.list().push_back(entry(endpoint)); - } - } -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); -#endif - break; - } - case messages::get_peers: - { - if (m.peers.empty()) - { - r["nodes"] = entry(entry::string_t); - entry& n = r["nodes"]; - std::back_insert_iterator out(n.string()); - for (msg::nodes_t::const_iterator i = m.nodes.begin() - , end(m.nodes.end()); i != end; ++i) - { - if (!i->addr.address().is_v4()) continue; - std::copy(i->id.begin(), i->id.end(), out); - write_endpoint(i->addr, out); - } -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size(); -#endif - } - else - { - r["values"] = entry(entry::list_t); - entry& p = r["values"]; - std::string endpoint; - endpoint.resize(6); - for (msg::peers_t::const_iterator i = m.peers.begin() - , end(m.peers.end()); i != end; ++i) - { - std::string::iterator out = endpoint.begin(); - write_endpoint(*i, out); - p.list().push_back(entry(endpoint)); - } -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size(); -#endif - } - break; - } - - case messages::announce_peer: - break; - break; - } - } - else - { - e["y"] = "q"; - e["a"] = entry(entry::dictionary_t); - entry& a = e["a"]; - a["id"] = std::string(m.id.begin(), m.id.end()); - - if (m.write_token.type() != entry::undefined_t) - a["token"] = m.write_token; - assert(m.message_id <= messages::error); - e["q"] = messages::ids[m.message_id]; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " query: " - << messages::ids[m.message_id]; -#endif - - switch (m.message_id) - { - case messages::find_node: - { - a["target"] = std::string(m.info_hash.begin(), m.info_hash.end()); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " target: " - << boost::lexical_cast(m.info_hash); -#endif - break; - } - case messages::get_peers: - { - a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end()); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " info_hash: " - << boost::lexical_cast(m.info_hash); -#endif - break; - } - case messages::announce_peer: - a["port"] = m_settings.service_port; - a["info_hash"] = boost::lexical_cast(m.info_hash); - a["token"] = m.write_token; -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(dht_tracker) << " port: " - << m_settings.service_port - << " info_hash: " << boost::lexical_cast(m.info_hash); -#endif - break; - default: break; - } - - } - - m_send_buf.clear(); - bencode(std::back_inserter(m_send_buf), e); - m_socket.send_to(asio::buffer(&m_send_buf[0] - , (int)m_send_buf.size()), m.addr); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - m_total_out_bytes += m_send_buf.size(); - - if (m.reply) - { - ++m_replies_sent[m.message_id]; - m_replies_bytes_sent[m.message_id] += int(m_send_buf.size()); - } - else - { - m_queries_out_bytes += m_send_buf.size(); - } - TORRENT_LOG(dht_tracker) << e; -#endif - - if (!m.piggy_backed_ping) return; - - msg pm; - pm.reply = false; - pm.piggy_backed_ping = false; - pm.message_id = messages::ping; - pm.transaction_id = m.ping_transaction_id; - pm.id = m.id; - pm.addr = m.addr; - - send_packet(pm); - } - -}} - diff --git a/libtorrent/cpp/kademlia/find_data.cpp b/libtorrent/cpp/kademlia/find_data.cpp deleted file mode 100644 index e1e09925b..000000000 --- a/libtorrent/cpp/kademlia/find_data.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg & Daniel Wallin -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 -#include -#include -#include - -namespace libtorrent { namespace dht -{ - -typedef boost::shared_ptr observer_ptr; - -class find_data_observer : public observer -{ -public: - find_data_observer( - boost::intrusive_ptr const& algorithm - , node_id self - , node_id target) - : m_algorithm(algorithm) - , m_target(target) - , m_self(self) - {} - - void send(msg& m) - { - m.reply = false; - m.message_id = messages::get_peers; - m.info_hash = m_target; - } - - void timeout(); - void reply(msg const&); - -private: - boost::intrusive_ptr m_algorithm; - node_id const m_target; - node_id const m_self; -}; - -void find_data_observer::reply(msg const& m) -{ - if (!m.peers.empty()) - { - m_algorithm->got_data(&m); - } - else - { - for (msg::nodes_t::const_iterator i = m.nodes.begin() - , end(m.nodes.end()); i != end; ++i) - { - m_algorithm->traverse(i->id, i->addr); - } - } - m_algorithm->finished(m_self); -} - -void find_data_observer::timeout() -{ - m_algorithm->failed(m_self); -} - - -find_data::find_data( - node_id target - , int branch_factor - , int max_results - , routing_table& table - , rpc_manager& rpc - , done_callback const& callback -) - : traversal_algorithm( - target - , branch_factor - , max_results - , table - , rpc - , table.begin() - , table.end() - ) - , m_done_callback(callback) - , m_done(false) -{ - boost::intrusive_ptr self(this); - add_requests(); -} - -void find_data::invoke(node_id const& id, asio::ip::udp::endpoint addr) -{ - if (m_done) - { - m_invoke_count = -1; - return; - } - - observer_ptr p(new find_data_observer(this, id, m_target)); - m_rpc.invoke(messages::get_peers, addr, p); -} - -void find_data::got_data(msg const* m) -{ - m_done = true; - m_done_callback(m); -} - -void find_data::done() -{ - if (m_invoke_count != 0) return; - if (!m_done) m_done_callback(0); -} - -void find_data::initiate( - node_id target - , int branch_factor - , int max_results - , routing_table& table - , rpc_manager& rpc - , done_callback const& callback -) -{ - std::cerr << "find_data::initiate, key: " << target << "\n"; - new find_data(target, branch_factor, max_results, table, rpc, callback); -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent/cpp/kademlia/node.cpp b/libtorrent/cpp/kademlia/node.cpp deleted file mode 100644 index 8a9d17818..000000000 --- a/libtorrent/cpp/kademlia/node.cpp +++ /dev/null @@ -1,539 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include - -#include "libtorrent/io.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/random_sample.hpp" -#include "libtorrent/kademlia/node_id.hpp" -#include "libtorrent/kademlia/rpc_manager.hpp" -#include "libtorrent/kademlia/packet_iterator.hpp" -#include "libtorrent/kademlia/routing_table.hpp" -#include "libtorrent/kademlia/node.hpp" - -#include "libtorrent/kademlia/refresh.hpp" -#include "libtorrent/kademlia/closest_nodes.hpp" -#include "libtorrent/kademlia/find_data.hpp" - -using boost::bind; -using boost::posix_time::second_clock; -using boost::posix_time::seconds; -using boost::posix_time::minutes; -using boost::posix_time::ptime; -using boost::posix_time::time_duration; - -namespace libtorrent { namespace dht -{ - -#ifdef _MSC_VER -namespace -{ - char rand() { return (char)std::rand(); } -} -#endif - -typedef boost::shared_ptr observer_ptr; - -// TODO: configurable? -enum { announce_interval = 30 }; - -using asio::ip::udp; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING -TORRENT_DEFINE_LOG(node) -#endif - -node_id generate_id() -{ - char random[20]; - std::srand(std::time(0)); -#ifdef _MSC_VER - std::generate(random, random + 20, &rand); -#else - std::generate(random, random + 20, &std::rand); -#endif - - hasher h; - h.update(random, 20); - return h.final(); -} - -// remove peers that have timed out -void purge_peers(std::set& peers) -{ - for (std::set::iterator i = peers.begin() - , end(peers.end()); i != end;) - { - // the peer has timed out - if (i->added + minutes(int(announce_interval * 1.5f)) < second_clock::universal_time()) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(node) << "peer timed out at: " << i->addr.address(); -#endif - peers.erase(i++); - } - else - ++i; - } -} - -void nop() {} - -node_impl::node_impl(boost::function const& f - , dht_settings const& settings, boost::optional node_id) - : m_settings(settings) - , m_id(node_id ? *node_id : generate_id()) - , m_table(m_id, 8, settings) - , m_rpc(bind(&node_impl::incoming_request, this, _1) - , m_id, m_table, f) - , m_last_tracker_tick(boost::posix_time::second_clock::universal_time()) -{ - m_secret[0] = std::rand(); - m_secret[1] = std::rand(); -} - -bool node_impl::verify_token(msg const& m) -{ - if (m.write_token.type() != entry::string_t) - return false; - std::string const& token = m.write_token.string(); - if (token.length() != 4) return false; - - hasher h1; - std::string address = m.addr.address().to_string(); - h1.update(&address[0], address.length()); - h1.update((char*)&m_secret[0], sizeof(m_secret[0])); - h1.update((char*)&m.info_hash[0], sha1_hash::size); - - sha1_hash h = h1.final(); - if (std::equal(token.begin(), token.end(), (signed char*)&h[0])) - return true; - - hasher h2; - h2.update(&address[0], address.length()); - h2.update((char*)&m_secret[1], sizeof(m_secret[1])); - h = h2.final(); - if (std::equal(token.begin(), token.end(), (signed char*)&h[0])) - return true; - return false; -} - -entry node_impl::generate_token(msg const& m) -{ - std::string token; - token.resize(4); - hasher h; - std::string address = m.addr.address().to_string(); - h.update(&address[0], address.length()); - h.update((char*)&m_secret[0], sizeof(m_secret[0])); - h.update((char*)&m.info_hash[0], sha1_hash::size); - - sha1_hash hash = h.final(); - std::copy(hash.begin(), hash.begin() + 4, (signed char*)&token[0]); - return entry(token); -} - -void node_impl::refresh(node_id const& id - , boost::function0 f) -{ - // use the 'bucket size' closest nodes - // to start the refresh with - std::vector start; - start.reserve(m_table.bucket_size()); - m_table.find_node(id, start, false); - refresh::initiate(id, m_settings.search_branching, 10, m_table.bucket_size() - , m_table, start.begin(), start.end(), m_rpc, f); -} - -void node_impl::bootstrap(std::vector const& nodes - , boost::function0 f) -{ - std::vector start; - start.reserve(nodes.size()); - std::copy(nodes.begin(), nodes.end(), std::back_inserter(start)); - refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size() - , m_table, start.begin(), start.end(), m_rpc, f); -} - -void node_impl::refresh() -{ - std::vector start; - start.reserve(m_table.size().get<0>()); - std::copy(m_table.begin(), m_table.end(), std::back_inserter(start)); - - refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size() - , m_table, start.begin(), start.end(), m_rpc, bind(&nop)); -} - -int node_impl::bucket_size(int bucket) -{ - return m_table.bucket_size(bucket); -} - -void node_impl::new_write_key() -{ - m_secret[1] = m_secret[0]; - m_secret[0] = std::rand(); -} - -void node_impl::refresh_bucket(int bucket) -{ - assert(bucket >= 0 && bucket < 160); - - // generate a random node_id within the given bucket - node_id target = generate_id(); - int num_bits = 160 - bucket; - node_id mask(0); - for (int i = 0; i < num_bits; ++i) - { - int byte = i / 8; - mask[byte] |= 0x80 >> (i % 8); - } - - node_id root = m_id; - root &= mask; - target &= ~mask; - target |= root; - - // make sure this is in another subtree than m_id - // clear the (num_bits - 1) bit and then set it to the - // inverse of m_id's corresponding bit. - target[(num_bits - 1) / 8] &= ~(0x80 >> ((num_bits - 1) % 8)); - target[(num_bits - 1) / 8] |= - (~(m_id[(num_bits - 1) / 8])) & (0x80 >> ((num_bits - 1) % 8)); - - assert(distance_exp(m_id, target) == bucket); - - std::vector start; - start.reserve(m_table.bucket_size()); - m_table.find_node(target, start, false, m_table.bucket_size()); - - refresh::initiate(target, m_settings.search_branching, 10, m_table.bucket_size() - , m_table, start.begin(), start.end(), m_rpc, bind(&nop)); - m_table.touch_bucket(bucket); -} - - -void node_impl::incoming(msg const& m) -{ - if (m_rpc.incoming(m)) - { - refresh(); - } -} - -namespace -{ - - class announce_observer : public observer - { - public: - announce_observer(sha1_hash const& info_hash, int listen_port - , entry const& write_token) - : m_info_hash(info_hash) - , m_listen_port(listen_port) - , m_token(write_token) - {} - - void send(msg& m) - { - m.port = m_listen_port; - m.info_hash = m_info_hash; - m.write_token = m_token; - } - - void timeout() {} - void reply(msg const&) {} - - private: - sha1_hash m_info_hash; - int m_listen_port; - entry m_token; - }; - - class get_peers_observer : public observer - { - public: - get_peers_observer(sha1_hash const& info_hash, int listen_port - , rpc_manager& rpc - , boost::function const&, sha1_hash const&)> f) - : m_info_hash(info_hash) - , m_listen_port(listen_port) - , m_rpc(rpc) - , m_fun(f) - {} - - void send(msg& m) - { - m.port = m_listen_port; - m.info_hash = m_info_hash; - } - - void timeout() {} - void reply(msg const& r) - { - m_rpc.invoke(messages::announce_peer, r.addr - , boost::shared_ptr( - new announce_observer(m_info_hash, m_listen_port, r.write_token))); - m_fun(r.peers, m_info_hash); - } - - private: - sha1_hash m_info_hash; - int m_listen_port; - rpc_manager& m_rpc; - boost::function const&, sha1_hash const&)> m_fun; - }; - - - void announce_fun(std::vector const& v, rpc_manager& rpc - , int listen_port, sha1_hash const& ih - , boost::function const&, sha1_hash const&)> f) - { - bool nodes = false; - // only store on the first k nodes - for (std::vector::const_iterator i = v.begin() - , end(v.end()); i != end; ++i) - { - rpc.invoke(messages::get_peers, i->addr, boost::shared_ptr( - new get_peers_observer(ih, listen_port, rpc, f))); - nodes = true; - } - } - -} - -namespace -{ - struct dummy_observer : observer - { - virtual void reply(msg const&) {} - virtual void timeout() {} - virtual void send(msg&) {} - }; -} - -void node_impl::add_router_node(udp::endpoint router) -{ - m_table.add_router_node(router); -} - -void node_impl::add_node(udp::endpoint node) -{ - // ping the node, and if we get a reply, it - // will be added to the routing table - observer_ptr p(new dummy_observer()); - m_rpc.invoke(messages::ping, node, p); -} - -void node_impl::announce(sha1_hash const& info_hash, int listen_port - , boost::function const&, sha1_hash const&)> f) -{ - // search for nodes with ids close to id, and then invoke the - // get_peers and then announce_peer rpc on them. - closest_nodes::initiate(info_hash, m_settings.search_branching - , m_table.bucket_size(), m_table, m_rpc - , boost::bind(&announce_fun, _1, boost::ref(m_rpc), listen_port - , info_hash, f)); -} - -time_duration node_impl::refresh_timeout() -{ - int refresh = -1; - ptime now = second_clock::universal_time(); - ptime next = now + minutes(15); - for (int i = 0; i < 160; ++i) - { - ptime r = m_table.next_refresh(i); - if (r <= now) - { - if (refresh == -1) refresh = i; - } - else if (r < next) - { - next = r; - } - } - if (refresh != -1) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(node) << "refreshing bucket: " << refresh; -#endif - refresh_bucket(refresh); - } - if (next < now + seconds(5)) return seconds(5); - return next - now; -} - -time_duration node_impl::connection_timeout() -{ - time_duration d = m_rpc.tick(); - - ptime now(second_clock::universal_time()); - if (now - m_last_tracker_tick < minutes(10)) return d; - m_last_tracker_tick = now; - - // look through all peers and see if any have timed out - for (data_iterator i = begin_data(), end(end_data()); i != end;) - { - torrent_entry& t = i->second; - node_id const& key = i->first; - ++i; - purge_peers(t.peers); - - // if there are no more peers, remove the entry altogether - if (t.peers.empty()) - { - table_t::iterator i = m_map.find(key); - if (i != m_map.end()) m_map.erase(i); - } - } - return d; -} - -void node_impl::on_announce(msg const& m, msg& reply) -{ - if (!verify_token(m)) - { - reply.message_id = messages::error; - reply.error_code = 203; - reply.error_msg = "Incorrect write token in announce_peer message"; - return; - } - - // the token was correct. That means this - // node is not spoofing its address. So, let - // the table get a chance to add it. - m_table.node_seen(m.id, m.addr); - - torrent_entry& v = m_map[m.info_hash]; - peer_entry e; - e.addr = tcp::endpoint(m.addr.address(), m.addr.port()); - e.added = second_clock::universal_time(); - std::set::iterator i = v.peers.find(e); - if (i != v.peers.end()) v.peers.erase(i++); - v.peers.insert(i, e); -} - -namespace -{ - tcp::endpoint get_endpoint(peer_entry const& p) - { - return p.addr; - } -} - -bool node_impl::on_find(msg const& m, std::vector& peers) const -{ - table_t::const_iterator i = m_map.find(m.info_hash); - if (i == m_map.end()) return false; - - torrent_entry const& v = i->second; - - int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply); - peers.clear(); - peers.reserve(num); - random_sample_n(boost::make_transform_iterator(v.peers.begin(), &get_endpoint) - , boost::make_transform_iterator(v.peers.end(), &get_endpoint) - , std::back_inserter(peers), num); - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - for (std::vector::iterator i = peers.begin() - , end(peers.end()); i != end; ++i) - { - TORRENT_LOG(node) << " " << *i; - } -#endif - return true; -} - -void node_impl::incoming_request(msg const& m) -{ - msg reply; - switch (m.message_id) - { - case messages::ping: - break; - case messages::get_peers: - { - reply.info_hash = m.info_hash; - reply.write_token = generate_token(m); - - if (!on_find(m, reply.peers)) - { - // we don't have any peers for this info_hash, - // return nodes instead - m_table.find_node(m.info_hash, reply.nodes, false); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - for (std::vector::iterator i = reply.nodes.begin() - , end(reply.nodes.end()); i != end; ++i) - { - TORRENT_LOG(node) << " " << i->id << " " << i->addr; - } -#endif - } - } - break; - case messages::find_node: - { - reply.info_hash = m.info_hash; - - m_table.find_node(m.info_hash, reply.nodes, false); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - for (std::vector::iterator i = reply.nodes.begin() - , end(reply.nodes.end()); i != end; ++i) - { - TORRENT_LOG(node) << " " << i->id << " " << i->addr; - } -#endif - } - break; - case messages::announce_peer: - { - on_announce(m, reply); - } - break; - }; - - if (m_table.need_node(m.id)) - m_rpc.reply_with_ping(reply, m); - else - m_rpc.reply(reply, m); -} - - -} } // namespace libtorrent::dht diff --git a/libtorrent/cpp/kademlia/node_id.cpp b/libtorrent/cpp/kademlia/node_id.cpp deleted file mode 100644 index d435846d2..000000000 --- a/libtorrent/cpp/kademlia/node_id.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include - -#include "libtorrent/kademlia/node_id.hpp" - -using boost::bind; - -namespace libtorrent { namespace dht -{ - -// returns the distance between the two nodes -// using the kademlia XOR-metric -node_id distance(node_id const& n1, node_id const& n2) -{ - node_id ret; - node_id::iterator k = ret.begin(); - for (node_id::const_iterator i = n1.begin(), j = n2.begin() - , end(n1.end()); i != end; ++i, ++j, ++k) - { - *k = *i ^ *j; - } - return ret; -} - -// returns true if: distance(n1, ref) < distance(n2, ref) -bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref) -{ - for (node_id::const_iterator i = n1.begin(), j = n2.begin() - , k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k) - { - boost::uint8_t lhs = (*i ^ *k); - boost::uint8_t rhs = (*j ^ *k); - if (lhs < rhs) return true; - if (lhs > rhs) return false; - } - return false; -} - -// returns n in: 2^n <= distance(n1, n2) < 2^(n+1) -// useful for finding out which bucket a node belongs to -int distance_exp(node_id const& n1, node_id const& n2) -{ - int byte = node_id::size - 1; - for (node_id::const_iterator i = n1.begin(), j = n2.begin() - , end(n1.end()); i != end; ++i, ++j, --byte) - { - assert(byte >= 0); - boost::uint8_t t = *i ^ *j; - if (t == 0) continue; - // we have found the first non-zero byte - // return the bit-number of the first bit - // that differs - int bit = byte * 8; - for (int b = 7; b > 0; --b) - if (t >= (1 << b)) return bit + b; - return bit; - } - - return 0; -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent/cpp/kademlia/refresh.cpp b/libtorrent/cpp/kademlia/refresh.cpp deleted file mode 100644 index 37300d16e..000000000 --- a/libtorrent/cpp/kademlia/refresh.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg & Daniel Wallin -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 -#include -#include -#include - -#include - -#include - -using boost::bind; - -namespace libtorrent { namespace dht -{ - -using asio::ip::udp; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING -TORRENT_DEFINE_LOG(refresh) -#endif - -typedef boost::shared_ptr observer_ptr; - -class refresh_observer : public observer -{ -public: - refresh_observer( - boost::intrusive_ptr const& algorithm - , node_id self - , node_id target - ) - : m_target(target) - , m_self(self) - , m_algorithm(algorithm) - {} - - void send(msg& m) - { - m.info_hash = m_target; - } - - void timeout(); - void reply(msg const& m); - -private: - node_id const m_target; - node_id const m_self; - boost::intrusive_ptr m_algorithm; -}; - -void refresh_observer::reply(msg const& in) -{ - if (!in.nodes.empty()) - { - for (msg::nodes_t::const_iterator i = in.nodes.begin() - , end(in.nodes.end()); i != end; ++i) - { - m_algorithm->traverse(i->id, i->addr); - } - } - m_algorithm->finished(m_self); -} - -void refresh_observer::timeout() -{ - m_algorithm->failed(m_self); -} - -class ping_observer : public observer -{ -public: - ping_observer( - boost::intrusive_ptr const& algorithm - , node_id self - ) - : m_self(self) - , m_algorithm(algorithm) - {} - - void send(msg& p) {} - void timeout(); - void reply(msg const& m); - -private: - node_id const m_self; - boost::intrusive_ptr m_algorithm; -}; - -void ping_observer::reply(msg const& m) -{ - m_algorithm->ping_reply(m_self); -} - -void ping_observer::timeout() -{ - m_algorithm->ping_timeout(m_self); -} - -void refresh::invoke(node_id const& nid, udp::endpoint addr) -{ - observer_ptr p(new refresh_observer( - this - , nid - , m_target - )); - - m_rpc.invoke(messages::find_node, addr, p); -} - -void refresh::done() -{ - m_leftover_nodes_iterator = (int)m_results.size() > m_max_results ? - m_results.begin() + m_max_results : m_results.end(); - - invoke_pings_or_finish(); -} - -void refresh::ping_reply(node_id nid) -{ - m_active_pings--; - invoke_pings_or_finish(); -} - -void refresh::ping_timeout(node_id nid) -{ - m_active_pings--; - invoke_pings_or_finish(); -} - -void refresh::invoke_pings_or_finish() -{ - while (m_active_pings < m_max_active_pings) - { - if (m_leftover_nodes_iterator == m_results.end()) break; - - result const& node = *m_leftover_nodes_iterator; - - // Skip initial nodes - if (node.flags & result::initial) - { - ++m_leftover_nodes_iterator; - continue; - } - - observer_ptr p(new ping_observer(this, node.id)); - - m_rpc.invoke(messages::ping, node.addr, p); - ++m_active_pings; - ++m_leftover_nodes_iterator; - } - - if (m_active_pings == 0) - { - m_done_callback(); - } -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent/cpp/kademlia/routing_table.cpp b/libtorrent/cpp/kademlia/routing_table.cpp deleted file mode 100644 index 32f7514f2..000000000 --- a/libtorrent/cpp/kademlia/routing_table.cpp +++ /dev/null @@ -1,434 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libtorrent/kademlia/routing_table.hpp" -#include "libtorrent/kademlia/node_id.hpp" -#include "libtorrent/session_settings.hpp" - -using boost::bind; -using boost::uint8_t; - -using boost::posix_time::second_clock; -using boost::posix_time::minutes; -using boost::posix_time::seconds; -using boost::posix_time::hours; - -namespace pt = boost::posix_time; - -namespace libtorrent { namespace dht -{ - -using asio::ip::udp; -typedef asio::ip::address_v4 address; - -routing_table::routing_table(node_id const& id, int bucket_size - , dht_settings const& settings) - : m_bucket_size(bucket_size) - , m_settings(settings) - , m_id(id) - , m_lowest_active_bucket(160) -{ - // distribute the refresh times for the buckets in an - // attempt do even out the network load - for (int i = 0; i < 160; ++i) - m_bucket_activity[i] = second_clock::universal_time() - seconds(15*60 - i*5); -} - -boost::tuple routing_table::size() const -{ - int nodes = 0; - int replacements = 0; - for (table_t::const_iterator i = m_buckets.begin() - , end(m_buckets.end()); i != end; ++i) - { - nodes += i->first.size(); - replacements += i->second.size(); - } - return boost::make_tuple(nodes, replacements); -} - -void routing_table::print_state(std::ostream& os) const -{ - os << "kademlia routing table state\n" - << "bucket_size: " << m_bucket_size << "\n" - << "node_id: " << m_id << "\n\n"; - - os << "number of nodes per bucket:\n" - "live\n"; - for (int k = 0; k < 8; ++k) - { - for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); - i != end; ++i) - { - os << (int(i->first.size()) > (7 - k) ? "|" : " "); - } - os << "\n"; - } - for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); - i != end; ++i) - { - os << "+"; - } - os << "\n"; - for (int k = 0; k < 8; ++k) - { - for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); - i != end; ++i) - { - os << (int(i->second.size()) > k ? "|" : " "); - } - os << "\n"; - } - os << "cached\n-----------\n"; - - os << "nodes:\n"; - for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end()); - i != end; ++i) - { - int bucket_index = int(i - m_buckets.begin()); - os << "bucket " << bucket_index << " " - << to_simple_string(m_bucket_activity[bucket_index]) - << " " << (bucket_index >= m_lowest_active_bucket?"active":"inactive") - << "\n"; - for (bucket_t::const_iterator j = i->first.begin() - , end(i->first.end()); j != end; ++j) - { - os << "ip: " << j->addr << " fails: " << j->fail_count - << " id: " << j->id << "\n"; - } - } -} - -void routing_table::touch_bucket(int bucket) -{ - m_bucket_activity[bucket] = second_clock::universal_time(); -} - -boost::posix_time::ptime routing_table::next_refresh(int bucket) -{ - assert(bucket < 160); - assert(bucket >= 0); - // lower than or equal to since a refresh of bucket 0 will - // effectively refresh the lowest active bucket as well - if (bucket <= m_lowest_active_bucket && bucket > 0) - return second_clock::universal_time() + minutes(15); - return m_bucket_activity[bucket] + minutes(15); -} - -void routing_table::replacement_cache(bucket_t& nodes) const -{ - for (table_t::const_iterator i = m_buckets.begin() - , end(m_buckets.end()); i != end; ++i) - { - std::copy(i->second.begin(), i->second.end() - , std::back_inserter(nodes)); - } -} - -bool routing_table::need_node(node_id const& id) -{ - int bucket_index = distance_exp(m_id, id); - assert(bucket_index < (int)m_buckets.size()); - assert(bucket_index >= 0); - bucket_t& b = m_buckets[bucket_index].first; - bucket_t& rb = m_buckets[bucket_index].second; - - // if the replacement cache is full, we don't - // need another node. The table is fine the - // way it is. - if ((int)rb.size() >= m_bucket_size) return false; - - // if the node already exists, we don't need it - if (std::find_if(b.begin(), b.end(), bind(std::equal_to() - , bind(&node_entry::id, _1), id)) != b.end()) return false; - - if (std::find_if(rb.begin(), rb.end(), bind(std::equal_to() - , bind(&node_entry::id, _1), id)) != rb.end()) return false; - - return true; -} - -void routing_table::node_failed(node_id const& id) -{ - int bucket_index = distance_exp(m_id, id); - assert(bucket_index < (int)m_buckets.size()); - assert(bucket_index >= 0); - bucket_t& b = m_buckets[bucket_index].first; - bucket_t& rb = m_buckets[bucket_index].second; - - bucket_t::iterator i = std::find_if(b.begin(), b.end() - , bind(std::equal_to() - , bind(&node_entry::id, _1), id)); - - if (i == b.end()) return; - - // if messages to ourself fails, ignore it - if (bucket_index == 0) return; - - if (rb.empty()) - { - ++i->fail_count; - if (i->fail_count >= m_settings.max_fail_count) - { - b.erase(i); - assert(m_lowest_active_bucket <= bucket_index); - while (m_buckets[m_lowest_active_bucket].first.empty() - && m_lowest_active_bucket < 160) - { - ++m_lowest_active_bucket; - } - } - return; - } - - b.erase(i); - b.push_back(rb.back()); - rb.erase(rb.end() - 1); -} - -void routing_table::add_router_node(udp::endpoint router) -{ - m_router_nodes.insert(router); -} - -// this function is called every time the node sees -// a sign of a node being alive. This node will either -// be inserted in the k-buckets or be moved to the top -// of its bucket. -// the return value indicates if the table needs a refresh. -// if true, the node should refresh the table (i.e. do a find_node -// on its own id) -bool routing_table::node_seen(node_id const& id, udp::endpoint addr) -{ - if (m_router_nodes.find(addr) != m_router_nodes.end()) return false; - int bucket_index = distance_exp(m_id, id); - assert(bucket_index < (int)m_buckets.size()); - assert(bucket_index >= 0); - bucket_t& b = m_buckets[bucket_index].first; - - bucket_t::iterator i = std::find_if(b.begin(), b.end() - , bind(std::equal_to() - , bind(&node_entry::id, _1), id)); - - bool ret = need_bootstrap(); - - m_bucket_activity[bucket_index] = second_clock::universal_time(); - - if (i != b.end()) - { - // TODO: what do we do if we see a node with - // the same id as a node at a different address? -// assert(i->addr == addr); - - // we already have the node in our bucket - // just move it to the back since it was - // the last node we had any contact with - // in this bucket - b.erase(i); - b.push_back(node_entry(id, addr)); -// TORRENT_LOG(table) << "replacing node: " << id << " " << addr; - return ret; - } - - // if the node was not present in our list - // we will only insert it if there is room - // for it, or if some of our nodes have gone - // offline - if ((int)b.size() < m_bucket_size) - { - b.push_back(node_entry(id, addr)); - // if bucket index is 0, the node is ourselves - // don't updated m_lowest_active_bucket - if (bucket_index < m_lowest_active_bucket - && bucket_index > 0) - m_lowest_active_bucket = bucket_index; -// TORRENT_LOG(table) << "inserting node: " << id << " " << addr; - return ret; - } - - // if there is no room, we look for nodes marked as stale - // in the k-bucket. If we find one, we can replace it. - // A node is considered stale if it has failed at least one - // time. Here we choose the node that has failed most times. - // If we don't find one, place this node in the replacement- - // cache and replace any nodes that will fail in the future - // with nodes from that cache. - - i = std::max_element(b.begin(), b.end() - , bind(std::less() - , bind(&node_entry::fail_count, _1) - , bind(&node_entry::fail_count, _2))); - - if (i != b.end() && i->fail_count > 0) - { - // i points to a node that has been marked - // as stale. Replace it with this new one - b.erase(i); - b.push_back(node_entry(id, addr)); -// TORRENT_LOG(table) << "replacing stale node: " << id << " " << addr; - return ret; - } - - // if we don't have any identified stale nodes in - // the bucket, and the bucket is full, we have to - // cache this node and wait until some node fails - // and then replace it. - - bucket_t& rb = m_buckets[bucket_index].second; - - i = std::find_if(rb.begin(), rb.end() - , bind(std::equal_to() - , bind(&node_entry::id, _1), id)); - - // if the node is already in the replacement bucket - // just return. - if (i != rb.end()) return ret; - - if ((int)rb.size() > m_bucket_size) rb.erase(rb.begin()); - rb.push_back(node_entry(id, addr)); -// TORRENT_LOG(table) << "inserting node in replacement cache: " << id << " " << addr; - return ret; -} - -bool routing_table::need_bootstrap() const -{ - for (const_iterator i = begin(); i != end(); ++i) - { - if (i->fail_count == 0) return false; - } - return true; -} - -// fills the vector with the k nodes from our buckets that -// are nearest to the given id. -void routing_table::find_node(node_id const& target - , std::vector& l, bool include_self, int count) -{ - l.clear(); - if (count == 0) count = m_bucket_size; - l.reserve(count); - - int bucket_index = distance_exp(m_id, target); - bucket_t& b = m_buckets[bucket_index].first; - - // copy all nodes that hasn't failed into the target - // vector. - std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l) - , bind(&node_entry::fail_count, _1)); - assert((int)l.size() <= count); - - if ((int)l.size() == count) - { - assert(std::count_if(l.begin(), l.end() - , boost::bind(std::not_equal_to() - , boost::bind(&node_entry::fail_count, _1), 0)) == 0); - return; - } - - // if we didn't have enough nodes in that bucket - // we have to reply with nodes from buckets closer - // to us. i.e. all the buckets in the range - // [0, bucket_index) if we are to include ourself - // or [1, bucket_index) if not. - bucket_t tmpb; - for (int i = include_self?0:1; i < count; ++i) - { - bucket_t& b = m_buckets[i].first; - std::remove_copy_if(b.begin(), b.end(), std::back_inserter(tmpb) - , bind(&node_entry::fail_count, _1)); - } - - std::random_shuffle(tmpb.begin(), tmpb.end()); - size_t to_copy = (std::min)(m_bucket_size - l.size() - , tmpb.size()); - std::copy(tmpb.begin(), tmpb.begin() + to_copy - , std::back_inserter(l)); - - assert((int)l.size() <= m_bucket_size); - - // return if we have enough nodes or if the bucket index - // is the biggest index available (there are no more buckets) - // to look in. - if ((int)l.size() == count - || bucket_index == (int)m_buckets.size() - 1) - { - assert(std::count_if(l.begin(), l.end() - , boost::bind(std::not_equal_to() - , boost::bind(&node_entry::fail_count, _1), 0)) == 0); - return; - } - - for (size_t i = bucket_index + 1; i < m_buckets.size(); ++i) - { - bucket_t& b = m_buckets[i].first; - - std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l) - , bind(&node_entry::fail_count, _1)); - if ((int)l.size() >= count) - { - l.erase(l.begin() + count, l.end()); - assert(std::count_if(l.begin(), l.end() - , boost::bind(std::not_equal_to() - , boost::bind(&node_entry::fail_count, _1), 0)) == 0); - return; - } - } - assert((int)l.size() == count - || std::distance(l.begin(), l.end()) < m_bucket_size); - assert((int)l.size() <= count); - - assert(std::count_if(l.begin(), l.end() - , boost::bind(std::not_equal_to() - , boost::bind(&node_entry::fail_count, _1), 0)) == 0); -} - -routing_table::iterator routing_table::begin() const -{ - return iterator(m_buckets.begin(), m_buckets.end()); -} - -routing_table::iterator routing_table::end() const -{ - return iterator(m_buckets.end(), m_buckets.end()); -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent/cpp/kademlia/rpc_manager.cpp b/libtorrent/cpp/kademlia/rpc_manager.cpp deleted file mode 100644 index 770b11bf5..000000000 --- a/libtorrent/cpp/kademlia/rpc_manager.cpp +++ /dev/null @@ -1,356 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -using boost::posix_time::ptime; -using boost::posix_time::time_duration; -using boost::posix_time::microsec_clock; -using boost::posix_time::seconds; -using boost::posix_time::milliseconds; -using boost::shared_ptr; -using boost::bind; - -namespace libtorrent { namespace dht -{ - -namespace io = libtorrent::detail; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING -TORRENT_DEFINE_LOG(rpc) -#endif - -node_id generate_id(); - -rpc_manager::rpc_manager(fun const& f, node_id const& our_id - , routing_table& table, send_fun const& sf) - : m_next_transaction_id(rand() % max_transactions) - , m_oldest_transaction_id(m_next_transaction_id) - , m_incoming(f) - , m_send(sf) - , m_our_id(our_id) - , m_table(table) - , m_timer(boost::posix_time::microsec_clock::universal_time()) - , m_random_number(generate_id()) -{ - std::srand(time(0)); -} - -rpc_manager::~rpc_manager() -{ -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(rpc) << "Destructing"; -#endif -} - -#ifndef NDEBUG -void rpc_manager::check_invariant() const -{ - assert(m_oldest_transaction_id >= 0); - assert(m_oldest_transaction_id < max_transactions); - assert(m_next_transaction_id >= 0); - assert(m_next_transaction_id < max_transactions); - assert(!m_transactions[m_next_transaction_id]); - - for (int i = (m_next_transaction_id + 1) % max_transactions; - i != m_oldest_transaction_id; i = (i + 1) % max_transactions) - { - assert(!m_transactions[i]); - } -} -#endif - -bool rpc_manager::incoming(msg const& m) -{ - INVARIANT_CHECK; - - if (m.reply) - { - // if we don't have the transaction id in our - // request list, ignore the packet - - if (m.transaction_id.size() != 2) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(rpc) << "Reply with invalid transaction id size: " - << m.transaction_id.size() << " from " << m.addr; -#endif - return false; - } - - std::string::const_iterator i = m.transaction_id.begin(); - int tid = io::read_uint16(i); - - if (tid >= (int)m_transactions.size() - || tid < 0) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(rpc) << "Reply with unknown transaction id: " - << tid << " from " << m.addr; -#endif - return false; - } - - boost::shared_ptr o = m_transactions[tid]; - - if (!o) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(rpc) << "Reply with unknown transaction id: " - << tid << " from " << m.addr; -#endif - return false; - } - - if (m.addr != o->target_addr) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: " - << tid << " from " << m.addr; -#endif - return false; - } - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app); - reply_stats << m.addr << "\t" << (microsec_clock::universal_time() - - o->sent).total_milliseconds() << std::endl; -#endif - o->reply(m); - m_transactions[tid].reset(); - - if (m.piggy_backed_ping) - { - // there is a ping request piggy - // backed in this reply - msg ph; - ph.message_id = messages::ping; - ph.transaction_id = m.ping_transaction_id; - ph.id = m_our_id; - ph.addr = m.addr; - - msg empty; - - reply(empty, ph); - } - return m_table.node_seen(m.id, m.addr); - } - else - { - // this is an incoming request - m_incoming(m); - } - return false; -} - -time_duration rpc_manager::tick() -{ - INVARIANT_CHECK; - - using boost::posix_time::microsec_clock; - - const int timeout_ms = 20 * 1000; - - // look for observers that has timed out - - if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms); - - for (;m_next_transaction_id != m_oldest_transaction_id; - m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions) - { - assert(m_oldest_transaction_id >= 0); - assert(m_oldest_transaction_id < max_transactions); - - boost::shared_ptr o = m_transactions[m_oldest_transaction_id]; - if (!o) continue; - - time_duration diff = o->sent + milliseconds(timeout_ms) - - microsec_clock::universal_time(); - if (diff > seconds(0)) - { - if (diff < seconds(1)) return seconds(1); - return diff; - } - - m_transactions[m_oldest_transaction_id].reset(); - o->timeout(); - } - return milliseconds(timeout_ms); -} - -unsigned int rpc_manager::new_transaction_id() -{ - INVARIANT_CHECK; - - unsigned int tid = m_next_transaction_id; - m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions; -// boost::shared_ptr o = m_transactions[m_next_transaction_id]; - if (m_transactions[m_next_transaction_id]) - { - m_transactions[m_next_transaction_id].reset(); - assert(m_oldest_transaction_id == m_next_transaction_id); - } - if (m_oldest_transaction_id == m_next_transaction_id) - { - m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions; -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(rpc) << "WARNING: transaction limit reached! Too many concurrent" - " messages! limit: " << (int)max_transactions; -#endif - update_oldest_transaction_id(); - } - -#ifndef NDEBUG - assert(!m_transactions[m_next_transaction_id]); - for (int i = (m_next_transaction_id + 1) % max_transactions; - i != m_oldest_transaction_id; i = (i + 1) % max_transactions) - { - assert(!m_transactions[i]); - } -#endif - -// hopefully this wouldn't happen, but unfortunately, the -// traversal algorithm will simply fail in case its connections -// are overwritten. If timeout() is called, it will likely spawn -// another connection, which in turn will close the next one -// and so on. -// if (o) o->timeout(); - return tid; -} - -void rpc_manager::update_oldest_transaction_id() -{ - INVARIANT_CHECK; - - assert(m_oldest_transaction_id != m_next_transaction_id); - while (!m_transactions[m_oldest_transaction_id]) - { - m_oldest_transaction_id = (m_oldest_transaction_id + 1) - % max_transactions; - if (m_oldest_transaction_id == m_next_transaction_id) - break; - } -} - -void rpc_manager::invoke(int message_id, udp::endpoint target_addr - , shared_ptr o) -{ - INVARIANT_CHECK; - - msg m; - m.message_id = message_id; - m.reply = false; - m.id = m_our_id; - m.addr = target_addr; - int tid = new_transaction_id(); - m.transaction_id.clear(); - std::back_insert_iterator out(m.transaction_id); - io::write_uint16(tid, out); - - o->send(m); - - m_transactions[tid] = o; - o->sent = boost::posix_time::microsec_clock::universal_time(); - o->target_addr = target_addr; - -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id] - << " -> " << target_addr; -#endif - m_send(m); -} - -void rpc_manager::reply(msg& m, msg const& reply_to) -{ - INVARIANT_CHECK; - - if (m.message_id != messages::error) - m.message_id = reply_to.message_id; - m.addr = reply_to.addr; - m.reply = true; - m.piggy_backed_ping = false; - m.id = m_our_id; - m.transaction_id = reply_to.transaction_id; - - m_send(m); -} - -namespace -{ - struct dummy_observer : observer - { - virtual void reply(msg const&) {} - virtual void timeout() {} - virtual void send(msg&) {} - }; -} - -void rpc_manager::reply_with_ping(msg& m, msg const& reply_to) -{ - INVARIANT_CHECK; - - if (m.message_id != messages::error) - m.message_id = reply_to.message_id; - m.addr = reply_to.addr; - m.reply = true; - m.piggy_backed_ping = true; - m.id = m_our_id; - m.transaction_id = reply_to.transaction_id; - - int ptid = new_transaction_id(); - m.ping_transaction_id.clear(); - std::back_insert_iterator out(m.ping_transaction_id); - io::write_uint16(ptid, out); - - boost::shared_ptr o(new dummy_observer); - m_transactions[ptid] = o; - o->sent = boost::posix_time::microsec_clock::universal_time(); - o->target_addr = m.addr; - - m_send(m); -} - - - -} } // namespace libtorrent::dht - diff --git a/libtorrent/cpp/kademlia/traversal_algorithm.cpp b/libtorrent/cpp/kademlia/traversal_algorithm.cpp deleted file mode 100644 index 88c62f836..000000000 --- a/libtorrent/cpp/kademlia/traversal_algorithm.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg & Daniel Wallin -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 -#include -#include - -#include - -using boost::bind; -using asio::ip::udp; - -namespace libtorrent { namespace dht -{ -#ifdef TORRENT_DHT_VERBOSE_LOGGING -TORRENT_DEFINE_LOG(traversal) -#endif - -void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags) -{ - if (m_failed.find(addr) != m_failed.end()) return; - - result const entry(id, addr, flags); - - std::vector::iterator i = std::lower_bound( - m_results.begin() - , m_results.end() - , entry - , bind( - compare_ref - , bind(&result::id, _1) - , bind(&result::id, _2) - , m_target - ) - ); - - if (i == m_results.end() || i->id != id) - { -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(traversal) << "adding result: " << id << " " << addr; -#endif - m_results.insert(i, entry); - } -} - -void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr) -{ - add_entry(id, addr, 0); -} - -void traversal_algorithm::finished(node_id const& id) -{ - --m_invoke_count; - add_requests(); - if (m_invoke_count == 0) done(); -} - -void traversal_algorithm::failed(node_id const& id) -{ - m_invoke_count--; - - std::vector::iterator i = std::find_if( - m_results.begin() - , m_results.end() - , bind( - std::equal_to() - , bind(&result::id, _1) - , id - ) - ); - - assert(i != m_results.end()); - - assert(i->flags & result::queried); - m_failed.insert(i->addr); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(traversal) << "failed: " << i->id << " " << i->addr; -#endif - m_results.erase(i); - m_table.node_failed(id); - add_requests(); - if (m_invoke_count == 0) done(); -} - -void traversal_algorithm::add_request(node_id const& id, udp::endpoint addr) -{ - invoke(id, addr); - ++m_invoke_count; -} - -namespace -{ - bool bitwise_nand(unsigned char lhs, unsigned char rhs) - { - return (lhs & rhs) == 0; - } -} - -void traversal_algorithm::add_requests() -{ - while (m_invoke_count < m_branch_factor) - { - // Find the first node that hasn't already been queried. - // TODO: Better heuristic - std::vector::iterator i = std::find_if( - m_results.begin() - , last_iterator() - , bind( - &bitwise_nand - , bind(&result::flags, _1) - , (unsigned char)result::queried - ) - ); -#ifdef TORRENT_DHT_VERBOSE_LOGGING - TORRENT_LOG(traversal) << "nodes left (" << this << "): " << (last_iterator() - i); -#endif - - if (i == last_iterator()) break; - - add_request(i->id, i->addr); - i->flags |= result::queried; - } -} - -std::vector::iterator traversal_algorithm::last_iterator() -{ - return (int)m_results.size() >= m_max_results ? - m_results.begin() + m_max_results - : m_results.end(); -} - -} } // namespace libtorrent::dht - diff --git a/libtorrent/cpp/peer_connection.cpp b/libtorrent/cpp/peer_connection.cpp deleted file mode 100755 index 3be09de21..000000000 --- a/libtorrent/cpp/peer_connection.cpp +++ /dev/null @@ -1,2050 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include - -#include "libtorrent/peer_connection.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/file.hpp" -#include "libtorrent/version.hpp" -#include "libtorrent/aux_/session_impl.hpp" - -using namespace boost::posix_time; -using boost::bind; -using boost::shared_ptr; -using libtorrent::aux::session_impl; - -namespace libtorrent -{ - - void intrusive_ptr_add_ref(peer_connection const* c) - { - assert(c->m_refs >= 0); - assert(c != 0); - ++c->m_refs; - } - - void intrusive_ptr_release(peer_connection const* c) - { - assert(c->m_refs > 0); - assert(c != 0); - if (--c->m_refs == 0) - delete c; - } - - peer_connection::peer_connection( - session_impl& ses - , boost::weak_ptr tor - , shared_ptr s - , tcp::endpoint const& remote) - : -#ifndef NDEBUG - m_last_choke(boost::posix_time::second_clock::universal_time() - - hours(1)) - , -#endif - m_ses(ses) - , m_max_out_request_queue(m_ses.settings().max_out_request_queue) - , m_timeout(m_ses.settings().peer_timeout) - , m_last_piece(second_clock::universal_time()) - , m_packet_size(0) - , m_recv_pos(0) - , m_current_send_buffer(0) - , m_write_pos(0) - , m_last_receive(second_clock::universal_time()) - , m_last_sent(second_clock::universal_time()) - , m_socket(s) - , m_remote(remote) - , m_torrent(tor) - , m_active(true) - , m_peer_interested(false) - , m_peer_choked(true) - , m_interesting(false) - , m_choked(true) - , m_failed(false) - , m_num_pieces(0) - , m_desired_queue_size(2) - , m_free_upload(0) - , m_trust_points(0) - , m_assume_fifo(false) - , m_num_invalid_requests(0) - , m_disconnecting(false) - , m_became_uninterested(second_clock::universal_time()) - , m_became_uninteresting(second_clock::universal_time()) - , m_connecting(true) - , m_queued(true) - , m_writing(false) - , m_last_write_size(0) - , m_reading(false) - , m_last_read_size(0) - , m_refs(0) -#ifndef NDEBUG - , m_in_constructor(true) -#endif - { -#ifdef TORRENT_VERBOSE_LOGGING - m_logger = m_ses.create_log(m_remote.address().to_string() + "_" - + boost::lexical_cast(m_remote.port())); - (*m_logger) << "*** OUTGOING CONNECTION\n"; -#endif - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - // these numbers are used the first second of connection. - // then the given upload limits will be applied by running - // allocate_resources(). - m_ul_bandwidth_quota.min = 10; - m_ul_bandwidth_quota.max = resource_request::inf; - - if (t->m_ul_bandwidth_quota.given == resource_request::inf) - { - m_ul_bandwidth_quota.given = resource_request::inf; - } - else - { - // just enough to get started with the handshake and bitmask - m_ul_bandwidth_quota.given = 400; - } - - m_dl_bandwidth_quota.min = 10; - m_dl_bandwidth_quota.max = resource_request::inf; - - if (t->m_dl_bandwidth_quota.given == resource_request::inf) - { - m_dl_bandwidth_quota.given = resource_request::inf; - } - else - { - // just enough to get started with the handshake and bitmask - m_dl_bandwidth_quota.given = 400; - } - - std::fill(m_peer_id.begin(), m_peer_id.end(), 0); - - if (t->ready_for_connections()) - init(); - } - - peer_connection::peer_connection( - session_impl& ses - , boost::shared_ptr s) - : -#ifndef NDEBUG - m_last_choke(boost::posix_time::second_clock::universal_time() - - hours(1)) - , -#endif - m_ses(ses) - , m_max_out_request_queue(m_ses.settings().max_out_request_queue) - , m_timeout(m_ses.settings().peer_timeout) - , m_last_piece(second_clock::universal_time()) - , m_packet_size(0) - , m_recv_pos(0) - , m_current_send_buffer(0) - , m_write_pos(0) - , m_last_receive(second_clock::universal_time()) - , m_last_sent(second_clock::universal_time()) - , m_socket(s) - , m_active(false) - , m_peer_interested(false) - , m_peer_choked(true) - , m_interesting(false) - , m_choked(true) - , m_failed(false) - , m_num_pieces(0) - , m_desired_queue_size(2) - , m_free_upload(0) - , m_trust_points(0) - , m_assume_fifo(false) - , m_num_invalid_requests(0) - , m_disconnecting(false) - , m_became_uninterested(second_clock::universal_time()) - , m_became_uninteresting(second_clock::universal_time()) - , m_connecting(false) - , m_queued(false) - , m_writing(false) - , m_last_write_size(0) - , m_reading(false) - , m_last_read_size(0) - , m_refs(0) -#ifndef NDEBUG - , m_in_constructor(true) -#endif - { - m_remote = m_socket->remote_endpoint(); - -#ifdef TORRENT_VERBOSE_LOGGING - assert(m_socket->remote_endpoint() == remote()); - m_logger = m_ses.create_log(remote().address().to_string() + "_" - + boost::lexical_cast(remote().port())); - (*m_logger) << "*** INCOMING CONNECTION\n"; -#endif - - - // upload bandwidth will only be given to connections - // that are part of a torrent. Since this is an incoming - // connection, we have to give it some initial bandwidth - // to send the handshake. - // after one second, allocate_resources() will be called - // and the correct bandwidth limits will be set on all - // connections. - - m_ul_bandwidth_quota.min = 10; - m_ul_bandwidth_quota.max = resource_request::inf; - - if (m_ses.m_upload_rate == -1) - { - m_ul_bandwidth_quota.given = resource_request::inf; - } - else - { - // just enough to get started with the handshake and bitmask - m_ul_bandwidth_quota.given = 400; - } - - m_dl_bandwidth_quota.min = 10; - m_dl_bandwidth_quota.max = resource_request::inf; - - if (m_ses.m_download_rate == -1) - { - m_dl_bandwidth_quota.given = resource_request::inf; - } - else - { - // just enough to get started with the handshake and bitmask - m_dl_bandwidth_quota.given = 400; - } - - std::fill(m_peer_id.begin(), m_peer_id.end(), 0); - } - - void peer_connection::init() - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - assert(t->valid_metadata()); - assert(t->ready_for_connections()); - - m_have_piece.resize(t->torrent_file().num_pieces(), false); - - // now that we have a piece_picker, - // update it with this peers pieces - - // build a vector of all pieces - m_num_pieces = 0; - std::vector piece_list; - for (int i = 0; i < (int)m_have_piece.size(); ++i) - { - if (m_have_piece[i]) - { - ++m_num_pieces; - piece_list.push_back(i); - } - } - - // let the torrent know which pieces the - // peer has, in a shuffled order - bool interesting = false; - for (std::vector::reverse_iterator i = piece_list.rbegin(); - i != piece_list.rend(); ++i) - { - int index = *i; - t->peer_has(index); - if (!t->have_piece(index) - && !t->picker().is_filtered(index)) - interesting = true; - } - - if (piece_list.size() == m_have_piece.size()) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " *** THIS IS A SEED ***\n"; -#endif - // if we're a seed too, disconnect - if (t->is_seed()) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " we're also a seed, disconnecting\n"; -#endif - throw std::runtime_error("seed to seed connection redundant, disconnecting"); - } - } - - if (interesting) - t->get_policy().peer_is_interesting(*this); - } - - peer_connection::~peer_connection() - { -// INVARIANT_CHECK; - assert(m_disconnecting); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - if (m_logger) - { - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " *** CONNECTION CLOSED\n"; - } -#endif -#ifndef NDEBUG - boost::shared_ptr t = m_torrent.lock(); - if (t) assert(t->connection_for(remote()) != this); -#endif - } - - void peer_connection::announce_piece(int index) - { - // optimization, don't send have messages - // to peers that already have the piece - if (has_piece(index)) return; - write_have(index); - } - - bool peer_connection::has_piece(int i) const - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - assert(t->valid_metadata()); - assert(i >= 0); - assert(i < t->torrent_file().num_pieces()); - return m_have_piece[i]; - } - - std::deque const& peer_connection::request_queue() const - { - return m_request_queue; - } - - std::deque const& peer_connection::download_queue() const - { - return m_download_queue; - } - - std::deque const& peer_connection::upload_queue() const - { - return m_requests; - } - - void peer_connection::add_stat(size_type downloaded, size_type uploaded) - { - INVARIANT_CHECK; - - m_statistics.add_stat(downloaded, uploaded); - } - - std::vector const& peer_connection::get_bitfield() const - { - return m_have_piece; - } - - void peer_connection::received_valid_data() - { - INVARIANT_CHECK; - - m_trust_points++; - // TODO: make this limit user settable - if (m_trust_points > 20) m_trust_points = 20; - } - - void peer_connection::received_invalid_data() - { - INVARIANT_CHECK; - - // we decrease more than we increase, to keep the - // allowed failed/passed ratio low. - // TODO: make this limit user settable - m_trust_points -= 2; - if (m_trust_points < -7) m_trust_points = -7; - } - - int peer_connection::trust_points() const - { - return m_trust_points; - } - - size_type peer_connection::total_free_upload() const - { - return m_free_upload; - } - - void peer_connection::add_free_upload(size_type free_upload) - { - INVARIANT_CHECK; - - m_free_upload += free_upload; - } - - void peer_connection::reset_upload_quota() - { - m_ul_bandwidth_quota.used = 0; - m_dl_bandwidth_quota.used = 0; - assert(m_ul_bandwidth_quota.left() >= 0); - assert(m_dl_bandwidth_quota.left() >= 0); - setup_send(); - setup_receive(); - } - - // verifies a piece to see if it is valid (is within a valid range) - // and if it can correspond to a request generated by libtorrent. - bool peer_connection::verify_piece(const peer_request& p) const - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - - assert(t->valid_metadata()); - - return p.piece >= 0 - && p.piece < t->torrent_file().num_pieces() - && p.length > 0 - && p.start >= 0 - && (p.length == t->block_size() - || (p.length < t->block_size() - && p.piece == t->torrent_file().num_pieces()-1 - && p.start + p.length == t->torrent_file().piece_size(p.piece))) - && p.start + p.length <= t->torrent_file().piece_size(p.piece) - && p.start % t->block_size() == 0; - } - - struct disconnect_torrent - { - disconnect_torrent(boost::weak_ptr& t): m_t(&t) {} - ~disconnect_torrent() { if (m_t) m_t->reset(); } - void cancel() { m_t = 0; } - private: - boost::weak_ptr* m_t; - }; - - void peer_connection::attach_to_torrent(sha1_hash const& ih) - { - INVARIANT_CHECK; - - assert(!m_disconnecting); - m_torrent = m_ses.find_torrent(ih); - - boost::shared_ptr t = m_torrent.lock(); - - if (t && t->is_aborted()) - { - m_torrent.reset(); - t.reset(); - } - - if (!t) - { - // we couldn't find the torrent! -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " couldn't find a torrent with the given info_hash\n"; -#endif - throw std::runtime_error("got info-hash that is not in our session"); - } - - disconnect_torrent disconnect(m_torrent); - if (t->is_paused()) - { - // paused torrents will not accept - // incoming connections -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " rejected connection to paused torrent\n"; -#endif - throw std::runtime_error("connection rejected by paused torrent"); - } - - // check to make sure we don't have another connection with the same - // info_hash and peer_id. If we do. close this connection. - t->attach_peer(this); - - // if the torrent isn't ready to accept - // connections yet, we'll have to wait with - // our initialization - if (t->ready_for_connections()) init(); - - // assume the other end has no pieces - // if we don't have valid metadata yet, - // leave the vector unallocated - assert(m_num_pieces == 0); - std::fill(m_have_piece.begin(), m_have_piece.end(), false); - disconnect.cancel(); - } - - // message handlers - - // ----------------------------- - // --------- KEEPALIVE --------- - // ----------------------------- - - void peer_connection::incoming_keepalive() - { - INVARIANT_CHECK; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== KEEPALIVE\n"; -#endif - } - - // ----------------------------- - // ----------- CHOKE ----------- - // ----------------------------- - - void peer_connection::incoming_choke() - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== CHOKE\n"; -#endif - m_peer_choked = true; - t->get_policy().choked(*this); - - // remove all pieces from this peers download queue and - // remove the 'downloading' flag from piece_picker. - for (std::deque::iterator i = m_download_queue.begin(); - i != m_download_queue.end(); ++i) - { - t->picker().abort_download(*i); - } - for (std::deque::const_iterator i = m_request_queue.begin() - , end(m_request_queue.end()); i != end; ++i) - { - // since this piece was skipped, clear it and allow it to - // be requested from other peers - t->picker().abort_download(*i); - } - m_download_queue.clear(); - m_request_queue.clear(); - -#ifndef NDEBUG -// t->picker().integrity_check(m_torrent); -#endif - } - - // ----------------------------- - // ---------- UNCHOKE ---------- - // ----------------------------- - - void peer_connection::incoming_unchoke() - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== UNCHOKE\n"; -#endif - m_peer_choked = false; - t->get_policy().unchoked(*this); - } - - // ----------------------------- - // -------- INTERESTED --------- - // ----------------------------- - - void peer_connection::incoming_interested() - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== INTERESTED\n"; -#endif - m_peer_interested = true; - t->get_policy().interested(*this); - } - - // ----------------------------- - // ------ NOT INTERESTED ------- - // ----------------------------- - - void peer_connection::incoming_not_interested() - { - INVARIANT_CHECK; - - m_became_uninterested = second_clock::universal_time(); - - // clear the request queue if the client isn't interested - m_requests.clear(); - setup_send(); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== NOT_INTERESTED\n"; -#endif - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - - m_peer_interested = false; - t->get_policy().not_interested(*this); - } - - // ----------------------------- - // ----------- HAVE ------------ - // ----------------------------- - - void peer_connection::incoming_have(int index) - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== HAVE [ piece: " << index << "]\n"; -#endif - - // if we got an invalid message, abort - if (index >= (int)m_have_piece.size() || index < 0) - throw protocol_error("got 'have'-message with higher index " - "than the number of pieces"); - - if (m_have_piece[index]) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " got redundant HAVE message for index: " << index << "\n"; -#endif - } - else - { - m_have_piece[index] = true; - - // only update the piece_picker if - // we have the metadata - if (t->valid_metadata()) - { - ++m_num_pieces; - t->peer_has(index); - - if (!t->have_piece(index) - && !is_interesting() - && !t->picker().is_filtered(index)) - t->get_policy().peer_is_interesting(*this); - } - - if (t->is_seed() && is_seed()) - { - throw protocol_error("seed to seed connection redundant, disconnecting"); - } - } - } - - // ----------------------------- - // --------- BITFIELD ---------- - // ----------------------------- - - void peer_connection::incoming_bitfield(std::vector const& bitfield) - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== BITFIELD\n"; -#endif - - // if we don't have the metedata, we cannot - // verify the bitfield size - if (t->valid_metadata() - && (bitfield.size() / 8) != (m_have_piece.size() / 8)) - throw protocol_error("got bitfield with invalid size"); - - // if we don't have metadata yet - // just remember the bitmask - // don't update the piecepicker - // (since it doesn't exist yet) - if (!t->valid_metadata()) - { - m_have_piece = bitfield; - m_num_pieces = std::count(bitfield.begin(), bitfield.end(), true); - return; - } - - // build a vector of all pieces - std::vector piece_list; - for (int i = 0; i < (int)m_have_piece.size(); ++i) - { - bool have = bitfield[i]; - if (have && !m_have_piece[i]) - { - m_have_piece[i] = true; - ++m_num_pieces; - piece_list.push_back(i); - } - else if (!have && m_have_piece[i]) - { - // this should probably not be allowed - m_have_piece[i] = false; - --m_num_pieces; - t->peer_lost(i); - } - } - - // let the torrent know which pieces the - // peer has, in a shuffled order - bool interesting = false; - for (std::vector::reverse_iterator i = piece_list.rbegin(); - i != piece_list.rend(); ++i) - { - int index = *i; - t->peer_has(index); - if (!t->have_piece(index) - && !t->picker().is_filtered(index)) - interesting = true; - } - - if (piece_list.size() == m_have_piece.size()) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " *** THIS IS A SEED ***\n"; -#endif - // if we're a seed too, disconnect - if (t->is_seed()) - { - throw protocol_error("seed to seed connection redundant, disconnecting"); - } - } - - if (interesting) t->get_policy().peer_is_interesting(*this); - } - - // ----------------------------- - // ---------- REQUEST ---------- - // ----------------------------- - - void peer_connection::incoming_request(peer_request const& r) - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - - if (!t->valid_metadata()) - { - // if we don't have valid metadata yet, - // we shouldn't get a request -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== UNEXPECTED_REQUEST [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " | " - "i: " << m_peer_interested << " | " - "t: " << (int)t->torrent_file().piece_size(r.piece) << " | " - "n: " << t->torrent_file().num_pieces() << " ]\n"; -#endif - return; - } - - if (int(m_requests.size()) > m_ses.settings().max_allowed_in_request_queue) - { - // don't allow clients to abuse our - // memory consumption. - // ignore requests if the client - // is making too many of them. -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== TOO MANY REQUESTS [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " | " - "i: " << m_peer_interested << " | " - "t: " << (int)t->torrent_file().piece_size(r.piece) << " | " - "n: " << t->torrent_file().num_pieces() << " ]\n"; -#endif - return; - } - - // make sure this request - // is legal and that the peer - // is not choked - if (r.piece >= 0 - && r.piece < t->torrent_file().num_pieces() - && t->have_piece(r.piece) - && r.start >= 0 - && r.start < t->torrent_file().piece_size(r.piece) - && r.length > 0 - && r.length + r.start <= t->torrent_file().piece_size(r.piece) - && m_peer_interested) - { - // if we have choked the client - // ignore the request - if (m_choked) - return; - - m_requests.push_back(r); - fill_send_buffer(); -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== REQUEST [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; -#endif - } - else - { -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== INVALID_REQUEST [ " - "piece: " << r.piece << " | " - "s: " << r.start << " | " - "l: " << r.length << " | " - "i: " << m_peer_interested << " | " - "t: " << (int)t->torrent_file().piece_size(r.piece) << " | " - "n: " << t->torrent_file().num_pieces() << " ]\n"; -#endif - - ++m_num_invalid_requests; - - if (t->alerts().should_post(alert::debug)) - { - t->alerts().post_alert(invalid_request_alert( - r - , t->get_handle() - , m_remote - , m_peer_id - , "peer sent an illegal piece request, ignoring")); - } - } - } - - void peer_connection::incoming_piece_fragment() - { - m_last_piece = second_clock::universal_time(); - } - - // ----------------------------- - // ----------- PIECE ----------- - // ----------------------------- - - void peer_connection::incoming_piece(peer_request const& p, char const* data) - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== PIECE [ piece: " << p.piece << " | " - "b: " << p.start / t->block_size() << " | " - "s: " << p.start << " | " - "l: " << p.length << " | " - "ds: " << statistics().download_rate() << " | " - "qs: " << m_desired_queue_size << " ]\n"; -#endif - - if (!verify_piece(p)) - { -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== INVALID_PIECE [ piece: " << p.piece << " | " - "start: " << p.start << " | " - "length: " << p.length << " ]\n"; -#endif - throw protocol_error("got invalid piece packet"); - } - - using namespace boost::posix_time; - - piece_picker& picker = t->picker(); - - piece_block block_finished(p.piece, p.start / t->block_size()); - std::deque::iterator b - = std::find( - m_download_queue.begin() - , m_download_queue.end() - , block_finished); - - std::deque::iterator i; - - if (b != m_download_queue.end()) - { - if (m_assume_fifo) - { - for (i = m_download_queue.begin(); - i != b; ++i) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " *** SKIPPED_PIECE [ piece: " << i->piece_index << " | " - "b: " << i->block_index << " ] ***\n"; -#endif - // since this piece was skipped, clear it and allow it to - // be requested from other peers - // TODO: send cancel? - picker.abort_download(*i); - } - - // remove the request that just finished - // from the download queue plus the - // skipped blocks. - m_download_queue.erase(m_download_queue.begin() - , boost::next(b)); - } - else - { - m_download_queue.erase(b); - } - send_block_requests(); - } - else - { - // cancel the block from the - // peer that has taken over it. - boost::optional peer - = t->picker().get_downloader(block_finished); - if (peer) - { - peer_connection* pc = t->connection_for(*peer); - if (pc && pc != this) - { - pc->cancel_request(block_finished); - } - } - else - { - if (t->alerts().should_post(alert::debug)) - { - t->alerts().post_alert( - peer_error_alert( - m_remote - , m_peer_id - , "got a block that was not requested")); - } -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " *** The block we just got was not in the " - "request queue ***\n"; -#endif - } - } - - // if the block we got is already finished, then ignore it - if (picker.is_finished(block_finished)) - { - t->received_redundant_data(p.length); - return; - } - - t->filesystem().write(data, p.piece, p.start, p.length); - - bool was_seed = t->is_seed(); - bool was_finished = picker.num_filtered() + t->num_pieces() - == t->torrent_file().num_pieces(); - - picker.mark_as_finished(block_finished, m_remote); - - t->get_policy().block_finished(*this, block_finished); - - // if the piece failed, this connection may be closed, and - // detached from the torrent. In that case m_torrent will - // be set to 0. So, we need to temporarily save it in this function - - // did we just finish the piece? - if (picker.is_piece_finished(p.piece)) - { - bool verified = t->verify_piece(p.piece); - if (verified) - { - t->announce_piece(p.piece); - assert(t->valid_metadata()); - if (!was_finished - && picker.num_filtered() + t->num_pieces() - == t->torrent_file().num_pieces()) - { - // torrent finished - // i.e. all the pieces we're interested in have - // been downloaded. Release the files (they will open - // in read only mode if needed) - t->finished(); - } - } - else - { - t->piece_failed(p.piece); - } - t->get_policy().piece_finished(p.piece, verified); - - if (!was_seed && t->is_seed()) - { - assert(verified); - t->completed(); - } - } - } - - // ----------------------------- - // ---------- CANCEL ----------- - // ----------------------------- - - void peer_connection::incoming_cancel(peer_request const& r) - { - INVARIANT_CHECK; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== CANCEL [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; -#endif - - std::deque::iterator i - = std::find(m_requests.begin(), m_requests.end(), r); - - if (i != m_requests.end()) - { - m_requests.erase(i); - } - else - { -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " *** GOT CANCEL NOT IN THE QUEUE\n"; -#endif - } - } - - // ----------------------------- - // --------- DHT PORT ---------- - // ----------------------------- - - void peer_connection::incoming_dht_port(int listen_port) - { - INVARIANT_CHECK; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== DHT_PORT [ p: " << listen_port << " ]\n"; -#endif -#ifndef TORRENT_DISABLE_DHT - m_ses.add_dht_node(udp::endpoint( - m_remote.address(), listen_port)); -#endif - } - - void peer_connection::add_request(piece_block const& block) - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - - assert(t->valid_metadata()); - assert(block.piece_index >= 0); - assert(block.piece_index < t->torrent_file().num_pieces()); - assert(block.block_index >= 0); - assert(block.block_index < t->torrent_file().piece_size(block.piece_index)); - assert(!t->picker().is_downloading(block)); - - t->picker().mark_as_downloading(block, m_remote); - m_request_queue.push_back(block); - } - - void peer_connection::cancel_request(piece_block const& block) - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - - assert(t->valid_metadata()); - - assert(block.piece_index >= 0); - assert(block.piece_index < t->torrent_file().num_pieces()); - assert(block.block_index >= 0); - assert(block.block_index < t->torrent_file().piece_size(block.piece_index)); - assert(t->picker().is_downloading(block)); - - t->picker().abort_download(block); - - std::deque::iterator it - = std::find(m_download_queue.begin(), m_download_queue.end(), block); - if (it == m_download_queue.end()) - { - it = std::find(m_request_queue.begin(), m_request_queue.end(), block); - assert(it != m_request_queue.end()); - if (it == m_request_queue.end()) return; - m_request_queue.erase(it); - // since we found it in the request queue, it means it hasn't been - // sent yet, so we don't have to send a cancel. - return; - } - else - { - m_download_queue.erase(it); - } - - send_block_requests(); - - int block_offset = block.block_index * t->block_size(); - int block_size - = std::min((int)t->torrent_file().piece_size(block.piece_index)-block_offset, - t->block_size()); - assert(block_size > 0); - assert(block_size <= t->block_size()); - - peer_request r; - r.piece = block.piece_index; - r.start = block_offset; - r.length = block_size; - - write_cancel(r); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> CANCEL [ piece: " << block.piece_index << " | s: " - << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n"; -#endif - } - - void peer_connection::send_choke() - { - INVARIANT_CHECK; - - if (m_choked) return; - write_choke(); - m_choked = true; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> CHOKE\n"; -#endif -#ifndef NDEBUG - using namespace boost::posix_time; - m_last_choke = second_clock::universal_time(); -#endif - m_num_invalid_requests = 0; - m_requests.clear(); - } - - void peer_connection::send_unchoke() - { - INVARIANT_CHECK; - -#ifndef NDEBUG - // TODO: once the policy lowers the interval for optimistic - // unchoke, increase this value that interval - // this condition cannot be guaranteed since if peers disconnect - // a new one will be unchoked ignoring when it was last choked - using namespace boost::posix_time; - //assert(second_clock::universal_time() - m_last_choke > seconds(9)); -#endif - - if (!m_choked) return; - write_unchoke(); - m_choked = false; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> UNCHOKE\n"; -#endif - } - - void peer_connection::send_interested() - { - INVARIANT_CHECK; - - if (m_interesting) return; - write_interested(); - m_interesting = true; - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> INTERESTED\n"; -#endif - } - - void peer_connection::send_not_interested() - { - INVARIANT_CHECK; - - if (!m_interesting) return; - write_not_interested(); - m_interesting = false; - - m_became_uninteresting = second_clock::universal_time(); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> NOT_INTERESTED\n"; -#endif - } - - void peer_connection::send_block_requests() - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - - assert(!has_peer_choked()); - - if ((int)m_download_queue.size() >= m_desired_queue_size) return; - - while (!m_request_queue.empty() - && (int)m_download_queue.size() < m_desired_queue_size) - { - piece_block block = m_request_queue.front(); - m_request_queue.pop_front(); - m_download_queue.push_back(block); - - int block_offset = block.block_index * t->block_size(); - int block_size = std::min((int)t->torrent_file().piece_size( - block.piece_index) - block_offset, t->block_size()); - assert(block_size > 0); - assert(block_size <= t->block_size()); - - peer_request r; - r.piece = block.piece_index; - r.start = block_offset; - r.length = block_size; - - assert(verify_piece(r)); - write_request(r); - - using namespace boost::posix_time; - -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> REQUEST [ " - "piece: " << block.piece_index << " | " - "b: " << block.block_index << " | " - "s: " << block_offset << " | " - "l: " << block_size << " | " - "ds: " << statistics().download_rate() << " | " - "qs: " << m_desired_queue_size << " ]\n"; -#endif - } - m_last_piece = second_clock::universal_time(); - } - - - void close_socket_ignore_error(boost::shared_ptr s) - { - s->close(asio::ignore_error()); - } - - void peer_connection::disconnect() - { - boost::intrusive_ptr me(this); - - INVARIANT_CHECK; - - if (m_disconnecting) return; - m_disconnecting = true; - m_ses.m_selector.post(boost::bind(&close_socket_ignore_error, m_socket)); - - boost::shared_ptr t = m_torrent.lock(); - - if (t) - { - if (t->valid_metadata()) - { - piece_picker& picker = t->picker(); - - while (!m_download_queue.empty()) - { - picker.abort_download(m_download_queue.back()); - m_download_queue.pop_back(); - } - while (!m_request_queue.empty()) - { - picker.abort_download(m_request_queue.back()); - m_request_queue.pop_back(); - } - } -#ifndef NDEBUG - else - { - assert(m_download_queue.empty()); - assert(m_request_queue.empty()); - } -#endif - t->remove_peer(this); - m_torrent.reset(); - } - - m_ses.close_connection(me); - } - - void peer_connection::set_upload_limit(int limit) - { - assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); - if (limit < 10) limit = 10; - m_ul_bandwidth_quota.max = limit; - assert(m_ul_bandwidth_quota.max >= m_ul_bandwidth_quota.min); - } - - void peer_connection::set_download_limit(int limit) - { - assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); - if (limit < 10) limit = 10; - m_dl_bandwidth_quota.max = limit; - assert(m_dl_bandwidth_quota.max >= m_dl_bandwidth_quota.min); - } - - size_type peer_connection::share_diff() const - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - - float ratio = t->ratio(); - - // if we have an infinite ratio, just say we have downloaded - // much more than we have uploaded. And we'll keep uploading. - if (ratio == 0.f) - return std::numeric_limits::max(); - - return m_free_upload - + static_cast(m_statistics.total_payload_download() * ratio) - - m_statistics.total_payload_upload(); - } - - void peer_connection::cut_receive_buffer(int size, int packet_size) - { - INVARIANT_CHECK; - - assert((int)m_recv_buffer.size() >= size); - - std::copy(m_recv_buffer.begin() + size, m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.begin()); - - assert(m_recv_pos >= size); - m_recv_pos -= size; - -#ifndef NDEBUG - std::fill(m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.end(), 0); -#endif - - m_packet_size = packet_size; - m_recv_buffer.resize(m_packet_size); - } - - void peer_connection::second_tick(float tick_interval) - { - INVARIANT_CHECK; - - ptime now(second_clock::universal_time()); - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - - on_tick(); - - if (!t->valid_metadata()) return; - - // calculate the desired download queue size - const float queue_time = m_ses.settings().request_queue_time; - // (if the latency is more than this, the download will stall) - // so, the queue size is queue_time * down_rate / 16 kiB - // (16 kB is the size of each request) - // the minimum number of requests is 2 and the maximum is 48 - // the block size doesn't have to be 16. So we first query the - // torrent for it - const int block_size = t->block_size(); - assert(block_size > 0); - - m_desired_queue_size = static_cast(queue_time - * statistics().download_rate() / block_size); - if (m_desired_queue_size > m_max_out_request_queue) - m_desired_queue_size = m_max_out_request_queue; - if (m_desired_queue_size < min_request_queue) - m_desired_queue_size = min_request_queue; - - if (!m_download_queue.empty() - && now - m_last_piece > seconds(m_ses.settings().piece_timeout)) - { - // this peer isn't sending the pieces we've - // requested (this has been observed by BitComet) - // in this case we'll clear our download queue and - // re-request the blocks. -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(now) - << " *** PIECE_REQUESTS TIMED OUT [ " << (int)m_download_queue.size() - << " " << to_simple_string(now - m_last_piece) << "] ***\n"; -#endif - - piece_picker& picker = t->picker(); - for (std::deque::const_iterator i = m_download_queue.begin() - , end(m_download_queue.end()); i != end; ++i) - { - // since this piece was skipped, clear it and allow it to - // be requested from other peers - picker.abort_download(*i); - } - for (std::deque::const_iterator i = m_request_queue.begin() - , end(m_request_queue.end()); i != end; ++i) - { - // since this piece was skipped, clear it and allow it to - // be requested from other peers - picker.abort_download(*i); - } - - m_download_queue.clear(); - m_request_queue.clear(); - - // TODO: If we have a limited number of upload - // slots, choke this peer - - m_assume_fifo = true; - - // this will trigger new picking of pieces - t->get_policy().unchoked(*this); - } - - m_statistics.second_tick(tick_interval); - m_ul_bandwidth_quota.used = std::min( - (int)ceil(statistics().upload_rate()) - , m_ul_bandwidth_quota.given); - - // If the client sends more data - // we send it data faster, otherwise, slower. - // It will also depend on how much data the - // client has sent us. This is the mean to - // maintain the share ratio given by m_ratio - // with all peers. - - if (t->is_seed() || is_choked() || t->ratio() == 0.0f) - { - // if we have downloaded more than one piece more - // than we have uploaded OR if we are a seed - // have an unlimited upload rate - if(send_buffer_size() > 0 - || (!m_requests.empty() && !is_choked())) - m_ul_bandwidth_quota.max = resource_request::inf; - else - m_ul_bandwidth_quota.max = m_ul_bandwidth_quota.min; - } - else - { - size_type bias = 0x10000+2*t->block_size() + m_free_upload; - - double break_even_time = 15; // seconds. - size_type have_uploaded = m_statistics.total_payload_upload(); - size_type have_downloaded = m_statistics.total_payload_download(); - double download_speed = m_statistics.download_rate(); - - size_type soon_downloaded = - have_downloaded + (size_type)(download_speed * break_even_time*1.5); - - if(t->ratio() != 1.f) - soon_downloaded = (size_type)(soon_downloaded*(double)t->ratio()); - - double upload_speed_limit = (soon_downloaded - have_uploaded - + bias) / break_even_time; - - upload_speed_limit = std::min(upload_speed_limit, - (double)std::numeric_limits::max()); - - m_ul_bandwidth_quota.max - = std::max((int)upload_speed_limit, m_ul_bandwidth_quota.min); - } - if (m_ul_bandwidth_quota.given > m_ul_bandwidth_quota.max) - m_ul_bandwidth_quota.given = m_ul_bandwidth_quota.max; - - if (m_ul_bandwidth_quota.used > m_ul_bandwidth_quota.given) - m_ul_bandwidth_quota.used = m_ul_bandwidth_quota.given; - - fill_send_buffer(); -/* - size_type diff = share_diff(); - - enum { block_limit = 2 }; // how many blocks difference is considered unfair - - // if the peer has been choked, send the current piece - // as fast as possible - if (diff > block_limit*m_torrent->block_size() || m_torrent->is_seed() || is_choked()) - { - // if we have downloaded more than one piece more - // than we have uploaded OR if we are a seed - // have an unlimited upload rate - m_ul_bandwidth_quota.wanted = std::numeric_limits::max(); - } - else - { - float ratio = m_torrent->ratio(); - // if we have downloaded too much, response with an - // upload rate of 10 kB/s more than we dowlload - // if we have uploaded too much, send with a rate of - // 10 kB/s less than we receive - int bias = 0; - if (diff > -block_limit*m_torrent->block_size()) - { - bias = static_cast(m_statistics.download_rate() * ratio) / 2; - if (bias < 10*1024) bias = 10*1024; - } - else - { - bias = -static_cast(m_statistics.download_rate() * ratio) / 2; - } - m_ul_bandwidth_quota.wanted = static_cast(m_statistics.download_rate()) + bias; - - // the maximum send_quota given our download rate from this peer - if (m_ul_bandwidth_quota.wanted < 256) m_ul_bandwidth_quota.wanted = 256; - } -*/ - } - - void peer_connection::fill_send_buffer() - { - INVARIANT_CHECK; - - boost::shared_ptr t = m_torrent.lock(); - if (!t) return; - - // only add new piece-chunks if the send buffer is small enough - // otherwise there will be no end to how large it will be! - // TODO: the buffer size should probably be dependent on the transfer speed - while (!m_requests.empty() - && (send_buffer_size() < t->block_size() * 6) - && !m_choked) - { - assert(t->valid_metadata()); - peer_request& r = m_requests.front(); - - assert(r.piece >= 0); - assert(r.piece < (int)m_have_piece.size()); - assert(t->have_piece(r.piece)); - assert(r.start + r.length <= t->torrent_file().piece_size(r.piece)); - assert(r.length > 0 && r.start >= 0); - - write_piece(r); - -#ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start - << " | l: " << r.length << " ]\n"; -#endif - - m_requests.erase(m_requests.begin()); - - if (m_requests.empty() - && m_num_invalid_requests > 0 - && is_peer_interested() - && !is_seed()) - { - // this will make the peer clear - // its download queue and re-request - // pieces. Hopefully it will not - // send invalid requests then - send_choke(); - send_unchoke(); - } - } - } - - void peer_connection::setup_send() - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - INVARIANT_CHECK; - - if (m_writing) return; - if (!can_write()) return; - - assert(!m_writing); - - int sending_buffer = (m_current_send_buffer + 1) & 1; - if (m_send_buffer[sending_buffer].empty()) - { - // thise means we have to swap buffer, because there's no - // previous buffer we're still waiting for. - std::swap(m_current_send_buffer, sending_buffer); - m_write_pos = 0; - } - - // send the actual buffer - if (!m_send_buffer[sending_buffer].empty()) - { - int amount_to_send - = std::min(m_ul_bandwidth_quota.left() - , (int)m_send_buffer[sending_buffer].size() - m_write_pos); - - assert(amount_to_send > 0); - - assert(m_write_pos < (int)m_send_buffer[sending_buffer].size()); - m_socket->async_write_some(asio::buffer( - &m_send_buffer[sending_buffer][m_write_pos], amount_to_send) - , bind(&peer_connection::on_send_data, self(), _1, _2)); - - m_writing = true; - m_last_write_size = amount_to_send; - m_ul_bandwidth_quota.used += m_last_write_size; - } - } - - void peer_connection::setup_receive() - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - INVARIANT_CHECK; - - if (m_reading) return; - if (!can_read()) return; - - assert(m_packet_size > 0); - int max_receive = std::min( - m_dl_bandwidth_quota.left() - , m_packet_size - m_recv_pos); - - assert(m_recv_pos >= 0); - assert(m_packet_size > 0); - assert(m_dl_bandwidth_quota.left() > 0); - assert(max_receive > 0); - - assert(can_read()); - m_socket->async_read_some(asio::buffer(&m_recv_buffer[m_recv_pos] - , max_receive), bind(&peer_connection::on_receive_data, self(), _1, _2)); - m_reading = true; - m_last_read_size = max_receive; - m_dl_bandwidth_quota.used += max_receive; - assert(m_dl_bandwidth_quota.used <= m_dl_bandwidth_quota.given); - } - - void peer_connection::reset_recv_buffer(int packet_size) - { - assert(packet_size > 0); - m_recv_pos = 0; - m_packet_size = packet_size; - if (int(m_recv_buffer.size()) < m_packet_size) - m_recv_buffer.resize(m_packet_size); - } - - void peer_connection::send_buffer(char const* begin, char const* end) - { - std::vector& buf = m_send_buffer[m_current_send_buffer]; - buf.insert(buf.end(), begin, end); - setup_send(); - } - -// TODO: change this interface to automatically call setup_send() when the -// return value is destructed - buffer::interval peer_connection::allocate_send_buffer(int size) - { - std::vector& buf = m_send_buffer[m_current_send_buffer]; - buf.resize(buf.size() + size); - buffer::interval ret(&buf[0] + buf.size() - size, &buf[0] + buf.size()); - return ret; - } - - template - struct set_to_zero - { - set_to_zero(T& v, bool cond): m_val(v), m_cond(cond) {} - void fire() { if (!m_cond) return; m_cond = false; m_val = 0; } - ~set_to_zero() { if (m_cond) m_val = 0; } - private: - T& m_val; - bool m_cond; - }; - - - // -------------------------- - // RECEIVE DATA - // -------------------------- - - // throws exception when the client should be disconnected - void peer_connection::on_receive_data(const asio::error& error - , std::size_t bytes_transferred) try - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - INVARIANT_CHECK; - - assert(m_reading); - assert(m_last_read_size > 0); - assert(m_last_read_size >= int(bytes_transferred)); - m_reading = false; - // correct the dl quota usage, if not all of the buffer was actually read - m_dl_bandwidth_quota.used -= m_last_read_size - bytes_transferred; - m_last_read_size = 0; - - if (error) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "**ERROR**: " << error.what() << "\n"; -#endif - on_receive(error, bytes_transferred); - throw std::runtime_error(error.what()); - } - - if (m_disconnecting) return; - - assert(m_packet_size > 0); - assert(bytes_transferred > 0); - - m_last_receive = second_clock::universal_time(); - m_recv_pos += bytes_transferred; - - // this will reset the m_recv_pos to 0 if the - // entire packet was received - // it is important that this is done before - // setup_receive() is called. Therefore, fire() is - // called before setup_receive(). - assert(m_recv_pos <= m_packet_size); - set_to_zero reset(m_recv_pos, m_recv_pos == m_packet_size); - - { - INVARIANT_CHECK; - on_receive(error, bytes_transferred); - } - - assert(m_packet_size > 0); - - // do the reset immediately - reset.fire(); - - setup_receive(); - } - catch (file_error& e) - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - boost::shared_ptr t = m_torrent.lock(); - if (!t) - { - m_ses.connection_failed(m_socket, remote(), e.what()); - return; - } - - if (t->alerts().should_post(alert::fatal)) - { - t->alerts().post_alert( - file_error_alert(t->get_handle() - , std::string("torrent paused: ") + e.what())); - } - t->pause(); - } - catch (std::exception& e) - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(m_socket, remote(), e.what()); - } - catch (...) - { - // all exceptions should derive from std::exception - assert(false); - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason"); - } - - bool peer_connection::can_write() const - { - INVARIANT_CHECK; - - // if we have requests or pending data to be sent or announcements to be made - // we want to send data - return (!m_send_buffer[m_current_send_buffer].empty() - || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty()) - && m_ul_bandwidth_quota.left() > 0 - && !m_connecting; - } - - bool peer_connection::can_read() const - { - INVARIANT_CHECK; - - return m_dl_bandwidth_quota.left() > 0 && !m_connecting; - } - - void peer_connection::connect() - { - INVARIANT_CHECK; - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << "CONNECTING: " << m_remote.address().to_string() << "\n"; -#endif - - boost::shared_ptr t = m_torrent.lock(); - assert(t); - - m_queued = false; - assert(m_connecting); - m_socket->open(asio::ip::tcp::v4()); - m_socket->bind(t->get_interface()); - m_socket->async_connect(m_remote - , bind(&peer_connection::on_connection_complete, self(), _1)); - - if (t->alerts().should_post(alert::debug)) - { - t->alerts().post_alert(peer_error_alert( - m_remote, m_peer_id, "connecting to peer")); - } - } - - void peer_connection::on_connection_complete(asio::error const& e) try - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - INVARIANT_CHECK; - - if (e) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << "CONNECTION FAILED: " << m_remote.address().to_string() << "\n"; -#endif - m_ses.connection_failed(m_socket, m_remote, e.what()); - return; - } - - if (m_disconnecting) return; - m_last_receive = second_clock::universal_time(); - - // this means the connection just succeeded - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << "COMPLETED: " << m_remote.address().to_string() << "\n"; -#endif - - m_connecting = false; - m_ses.connection_completed(self()); - on_connected(); - setup_send(); - } - catch (std::exception& ex) - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(m_socket, remote(), ex.what()); - } - catch (...) - { - // all exceptions should derive from std::exception - assert(false); - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason"); - } - - // -------------------------- - // SEND DATA - // -------------------------- - - // throws exception when the client should be disconnected - void peer_connection::on_send_data(asio::error const& error - , std::size_t bytes_transferred) try - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - INVARIANT_CHECK; - - assert(m_writing); - assert(m_last_write_size > 0); - m_writing = false; - // correct the ul quota usage, if not all of the buffer was sent - m_ul_bandwidth_quota.used -= m_last_write_size - bytes_transferred; - m_last_write_size = 0; - m_write_pos += bytes_transferred; - - if (error) - { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "**ERROR**: " << error.what() << "\n"; -#endif - throw std::runtime_error(error.what()); - } - if (m_disconnecting) return; - - assert(!m_connecting); - assert(bytes_transferred > 0); - - int sending_buffer = (m_current_send_buffer + 1) & 1; - - assert(int(m_send_buffer[sending_buffer].size()) >= m_write_pos); - if (int(m_send_buffer[sending_buffer].size()) == m_write_pos) - { - m_send_buffer[sending_buffer].clear(); - m_write_pos = 0; - } - - m_last_sent = second_clock::universal_time(); - - on_sent(error, bytes_transferred); - fill_send_buffer(); - setup_send(); - } - catch (std::exception& e) - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(m_socket, remote(), e.what()); - } - catch (...) - { - // all exceptions should derive from std::exception - assert(false); - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.connection_failed(m_socket, remote(), "connection failed for unkown reason"); - } - - -#ifndef NDEBUG - void peer_connection::check_invariant() const - { - boost::shared_ptr t = m_torrent.lock(); - if (!t) - { - typedef session_impl::torrent_map torrent_map; - torrent_map& m = m_ses.m_torrents; - for (torrent_map::iterator i = m.begin(), end(m.end()); i != end; ++i) - { - torrent& t = *i->second; - assert(t.connection_for(m_remote) != this); - } - return; - } - - if (!m_in_constructor && t->connection_for(remote()) != this) - { - assert(false); - } - - if (t->valid_metadata()) - { - int piece_count = std::count(m_have_piece.begin() - , m_have_piece.end(), true); - if (m_num_pieces != piece_count) - { - assert(false); - } - } - - assert(m_write_pos <= int(m_send_buffer[ - (m_current_send_buffer + 1) & 1].size())); - } -#endif - - bool peer_connection::has_timed_out() const - { - // TODO: the timeout should be called by an event - INVARIANT_CHECK; - - using namespace boost::posix_time; - - ptime now(second_clock::universal_time()); - - // if the socket is still connecting, don't - // consider it timed out. Because Windows XP SP2 - // may delay connection attempts. - if (m_connecting) return false; - - // if the peer hasn't said a thing for a certain - // time, it is considered to have timed out - time_duration d; - d = second_clock::universal_time() - m_last_receive; - if (d > seconds(m_timeout)) return true; - - // if the peer hasn't become interested and we haven't - // become interested in the peer for 10 minutes, it - // has also timed out. - time_duration d1; - time_duration d2; - d1 = now - m_became_uninterested; - d2 = now - m_became_uninteresting; - // TODO: these timeouts should be user settable - if (!m_interesting - && !m_peer_interested - && d1 > minutes(10) - && d2 > minutes(10)) - { - return true; - } - - return false; - } - - - void peer_connection::keep_alive() - { - INVARIANT_CHECK; - - boost::posix_time::time_duration d; - d = second_clock::universal_time() - m_last_sent; - if (d.total_seconds() < m_timeout / 2) return; - - if (m_connecting) return; - - write_keepalive(); - } - - bool peer_connection::is_seed() const - { - INVARIANT_CHECK; - // if m_num_pieces == 0, we probably doesn't have the - // metadata yet. - return m_num_pieces == (int)m_have_piece.size() && m_num_pieces > 0; - } -} - diff --git a/libtorrent/cpp/piece_picker.cpp b/libtorrent/cpp/piece_picker.cpp deleted file mode 100755 index 61e7df66f..000000000 --- a/libtorrent/cpp/piece_picker.cpp +++ /dev/null @@ -1,1198 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include - -// non-standard header, is_sorted() -//#include - -#include "libtorrent/piece_picker.hpp" -#include "libtorrent/aux_/session_impl.hpp" - -#ifndef NDEBUG -#include "libtorrent/peer_connection.hpp" -#include "libtorrent/torrent.hpp" -#endif - -//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK -#define TORRENT_PIECE_PICKER_INVARIANT_CHECK - -namespace libtorrent -{ - - piece_picker::piece_picker(int blocks_per_piece, int total_num_blocks) - : m_piece_info(2) - , m_downloading_piece_info(2) - , m_piece_map((total_num_blocks + blocks_per_piece-1) / blocks_per_piece) - , m_num_filtered(0) - , m_num_have_filtered(0) - , m_sequenced_download_threshold(100) - { - assert(blocks_per_piece > 0); - assert(total_num_blocks >= 0); - - // the piece index is stored in 20 bits, which limits the allowed - // number of pieces somewhat - if (m_piece_map.size() >= piece_pos::we_have_index) - throw std::runtime_error("too many pieces in torrent"); - - m_blocks_per_piece = blocks_per_piece; - m_blocks_in_last_piece = total_num_blocks % blocks_per_piece; - if (m_blocks_in_last_piece == 0) m_blocks_in_last_piece = blocks_per_piece; - - assert(m_blocks_per_piece <= max_blocks_per_piece); - assert(m_blocks_in_last_piece <= m_blocks_per_piece); - assert(m_blocks_in_last_piece <= max_blocks_per_piece); - - // allocate the piece_map to cover all pieces - // and make them invalid (as if though we already had every piece) - std::fill(m_piece_map.begin(), m_piece_map.end() - , piece_pos(0, piece_pos::we_have_index)); - } - - // pieces is a bitmask with the pieces we have - void piece_picker::files_checked( - const std::vector& pieces - , const std::vector& unfinished) - { - // build a vector of all the pieces we don't have - std::vector piece_list; - piece_list.reserve(std::count(pieces.begin(), pieces.end(), false)); - - for (std::vector::const_iterator i = pieces.begin(); - i != pieces.end(); ++i) - { - if (*i) continue; - int index = static_cast(i - pieces.begin()); - if (m_piece_map[index].filtered) - { - ++m_num_filtered; - --m_num_have_filtered; - m_piece_map[index].index = 0; - } - else - { - piece_list.push_back(index); - } - } - - // add the pieces to the piece_picker - for (std::vector::reverse_iterator i = piece_list.rbegin(); - i != piece_list.rend(); ++i) - { - int index = *i; - assert(index >= 0); - assert(index < (int)m_piece_map.size()); - assert(m_piece_map[index].index == piece_pos::we_have_index); - assert(m_piece_map[index].peer_count == 0); - assert(m_piece_info.size() == 2); - - add(index); - assert(m_piece_map[index].index != piece_pos::we_have_index); - } - - // if we have fast resume info - // use it - if (!unfinished.empty()) - { - for (std::vector::const_iterator i - = unfinished.begin(); i != unfinished.end(); ++i) - { - tcp::endpoint peer; - for (int j = 0; j < m_blocks_per_piece; ++j) - { - if (i->finished_blocks[j]) - mark_as_finished(piece_block(i->index, j), peer); - } - } - } - } - - void piece_picker::set_sequenced_download_threshold( - int sequenced_download_threshold) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - - if (sequenced_download_threshold == m_sequenced_download_threshold) - return; - - int old_limit = m_sequenced_download_threshold; - m_sequenced_download_threshold = sequenced_download_threshold; - - for (std::vector::iterator i = m_piece_map.begin() - , end(m_piece_map.end()); i != end; ++i) - { - if (i->priority(old_limit) != i->priority(m_sequenced_download_threshold)) - { - piece_pos& p = *i; - if (p.index == piece_pos::we_have_index) continue; - int prev_priority = p.priority(old_limit); - move(p.downloading, p.filtered, prev_priority, p.index); - } - } - - typedef std::vector info_t; - - if (old_limit < sequenced_download_threshold) - { - // the threshold was incremented, in case - // the previous max availability was reached - // we need to shuffle that bucket, if not, we - // don't have to do anything - if (int(m_piece_info.size()) > old_limit) - { - info_t& in = m_piece_info[old_limit]; - std::random_shuffle(in.begin(), in.end()); - int c = 0; - for (info_t::iterator i = in.begin() - , end(in.end()); i != end; ++i) - { - m_piece_map[*i].index = c++; - assert(m_piece_map[*i].priority(old_limit) == old_limit); - } - } - } - else if (int(m_piece_info.size()) > sequenced_download_threshold) - { - info_t& in = m_piece_info[sequenced_download_threshold]; - std::sort(in.begin(), in.end()); - int c = 0; - for (info_t::iterator i = in.begin() - , end(in.end()); i != end; ++i) - { - m_piece_map[*i].index = c++; - assert(m_piece_map[*i].priority( - sequenced_download_threshold) == sequenced_download_threshold); - } - } - } - -#ifndef NDEBUG - - void piece_picker::check_invariant(const torrent* t) const - { - assert(sizeof(piece_pos) == 4); - - if (t != 0) - assert((int)m_piece_map.size() == t->torrent_file().num_pieces()); - - int num_filtered = 0; - int num_have_filtered = 0; - for (std::vector::const_iterator i = m_piece_map.begin(); - i != m_piece_map.end(); ++i) - { - int index = static_cast(i - m_piece_map.begin()); - if (i->filtered) - { - if (i->index != piece_pos::we_have_index) - ++num_filtered; - else - ++num_have_filtered; - } - if (t != 0) - { - int actual_peer_count = 0; - for (torrent::const_peer_iterator peer = t->begin(); - peer != t->end(); ++peer) - { - if (peer->second->has_piece(index)) actual_peer_count++; - } - - assert((int)i->peer_count == actual_peer_count); -/* - int num_downloaders = 0; - for (std::vector::const_iterator peer = t->begin(); - peer != t->end(); - ++peer) - { - const std::vector& queue = (*peer)->download_queue(); - if (std::find_if(queue.begin(), queue.end(), has_index(index)) == queue.end()) continue; - - ++num_downloaders; - } - - if (i->downloading) - { - assert(num_downloaders == 1); - } - else - { - assert(num_downloaders == 0); - } -*/ - } - - if (i->index == piece_pos::we_have_index) - { - assert(t == 0 || t->have_piece(index)); - assert(i->downloading == 0); - - // make sure there's no entry - // with this index. (there shouldn't - // be since the piece_map is piece_pos::we_have_index) - for (std::vector >::const_iterator i = m_piece_info.begin(); - i != m_piece_info.end(); ++i) - { - for (std::vector::const_iterator j= i->begin(); - j != i->end(); ++j) - { - assert(*j != index); - } - } - - for (std::vector >::const_iterator i = m_downloading_piece_info.begin(); - i != m_downloading_piece_info.end(); ++i) - { - for (std::vector::const_iterator j = i->begin(); - j != i->end(); ++j) - { - assert(*j != index); - } - } - - } - else if (!i->filtered) - { - if (t != 0) - assert(!t->have_piece(index)); - - const std::vector >& c_vec = pick_piece_info_vector(i->downloading, i->filtered); - assert(i->priority(m_sequenced_download_threshold) < (int)c_vec.size()); - const std::vector& vec = c_vec[i->priority(m_sequenced_download_threshold)]; - if (i->index >= vec.size()) - { - assert(false); - } - assert(vec[i->index] == index); - } - - std::vector::const_iterator down - = std::find_if(m_downloads.begin(), - m_downloads.end(), - has_index(index)); - if (i->downloading == 1) - { - assert(down != m_downloads.end()); - } - else - { - assert(down == m_downloads.end()); - } - } - assert(num_filtered == m_num_filtered); - assert(num_have_filtered == m_num_have_filtered); - } -#endif - - float piece_picker::distributed_copies() const - { - const float num_pieces = static_cast(m_piece_map.size()); - - for (int i = 0; i < (int)m_piece_info.size(); ++i) - { - int p = (int)m_piece_info[i].size(); - assert(num_pieces == 0 || float(p) / num_pieces <= 1.f); - if (p > 0) - { - float fraction_above_count = - 1.f - float(p) / num_pieces; - return i + fraction_above_count; - } - } - return 1.f; - } - - std::vector >& piece_picker::pick_piece_info_vector( - bool downloading, bool filtered) - { - assert(!filtered); - return downloading?m_downloading_piece_info:m_piece_info; - } - - std::vector > const& piece_picker::pick_piece_info_vector( - bool downloading, bool filtered) const - { - assert(!filtered); - return downloading?m_downloading_piece_info:m_piece_info; - } - - void piece_picker::add(int index) - { - assert(index >= 0); - assert(index < (int)m_piece_map.size()); - piece_pos& p = m_piece_map[index]; - assert(!p.filtered); - - std::vector >& dst_vec = pick_piece_info_vector( - p.downloading, p.filtered); - - int priority = p.priority(m_sequenced_download_threshold); - if ((int)dst_vec.size() <= priority) - dst_vec.resize(priority + 1); - - assert((int)dst_vec.size() > priority); - - if (p.ordered(m_sequenced_download_threshold)) - { - // the piece should be inserted ordered, not randomly - std::vector& v = dst_vec[priority]; -// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); - std::vector::iterator i = std::lower_bound(v.begin(), v.end() - , index/*, std::greater()*/); - p.index = i - v.begin(); - v.insert(i, index); - i = v.begin() + p.index + 1; - for (;i != v.end(); ++i) - { - ++m_piece_map[*i].index; - assert(v[m_piece_map[*i].index] == *i); - } -// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); - } - else if (dst_vec[priority].size() < 2) - { - p.index = dst_vec[priority].size(); - dst_vec[priority].push_back(index); - } - else - { - // find a random position in the destination vector where we will place - // this entry. - int dst_index = rand() % dst_vec[priority].size(); - - // copy the entry at that position to the back - m_piece_map[dst_vec[priority][dst_index]].index - = dst_vec[priority].size(); - dst_vec[priority].push_back(dst_vec[priority][dst_index]); - - // and then replace the one at dst_index with the one we're moving. - // this procedure is to make sure there's no ordering when pieces - // are moved in sequenced order. - p.index = dst_index; - dst_vec[priority][p.index] = index; - } - } - - // will update the piece with the given properties (downloading, filtered, - // priority, elem_index) to place it at the correct position in the - // vectors. - void piece_picker::move(bool downloading, bool filtered, int priority - , int elem_index) - { - assert(!filtered); - assert(priority >= 0); - assert(elem_index >= 0); - assert(elem_index != piece_pos::we_have_index); - std::vector >& src_vec(pick_piece_info_vector( - downloading, filtered)); - - assert((int)src_vec.size() > priority); - assert((int)src_vec[priority].size() > elem_index); - - int index = src_vec[priority][elem_index]; - // update the piece_map - piece_pos& p = m_piece_map[index]; - int new_priority = p.priority(m_sequenced_download_threshold); - - if (p.downloading == downloading - && p.filtered == filtered - && new_priority == priority) - { - assert(p.ordered(m_sequenced_download_threshold)); - return; - } - - std::vector >& dst_vec(pick_piece_info_vector( - p.downloading, p.filtered)); - - assert(&dst_vec != &src_vec || new_priority != priority); - - if ((int)dst_vec.size() <= new_priority) - { - dst_vec.resize(new_priority + 1); - assert((int)dst_vec.size() > new_priority); - } - - if (p.ordered(m_sequenced_download_threshold)) - { - // the piece should be inserted ordered, not randomly - std::vector& v = dst_vec[new_priority]; -// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); - std::vector::iterator i = std::lower_bound(v.begin(), v.end() - , index/*, std::greater()*/); - p.index = i - v.begin(); - v.insert(i, index); - i = v.begin() + p.index + 1; - for (;i != v.end(); ++i) - { - ++m_piece_map[*i].index; - assert(v[m_piece_map[*i].index] == *i); - } -// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); - } - else if (dst_vec[new_priority].size() < 2) - { - p.index = dst_vec[new_priority].size(); - dst_vec[new_priority].push_back(index); - } - else - { - // find a random position in the destination vector where we will place - // this entry. - int dst_index = rand() % dst_vec[new_priority].size(); - - // copy the entry at that position to the back - m_piece_map[dst_vec[new_priority][dst_index]].index - = dst_vec[new_priority].size(); - dst_vec[new_priority].push_back(dst_vec[new_priority][dst_index]); - - // and then replace the one at dst_index with the one we're moving. - // this procedure is to make sure there's no ordering when pieces - // are moved in sequenced order. - p.index = dst_index; - dst_vec[new_priority][p.index] = index; - } - assert(p.index < dst_vec[p.priority(m_sequenced_download_threshold)].size()); - assert(dst_vec[p.priority(m_sequenced_download_threshold)][p.index] == index); - - if (priority >= m_sequenced_download_threshold) - { - // remove the element from the source vector and preserve the order - std::vector& v = src_vec[priority]; - v.erase(v.begin() + elem_index); - for (std::vector::iterator i = v.begin() + elem_index; - i != v.end(); ++i) - { - --m_piece_map[*i].index; - assert(v[m_piece_map[*i].index] == *i); - } - } - else - { - // this will remove elem from the source vector without - // preserving order, but the order is random anyway - int replace_index = src_vec[priority][elem_index] = src_vec[priority].back(); - if (index != replace_index) - { - // update the entry we moved from the back - m_piece_map[replace_index].index = elem_index; - - assert((int)src_vec[priority].size() > elem_index); - // this may not necessarily be the case. If we've just updated the threshold and are updating - // the piece map -// assert((int)m_piece_map[replace_index].priority(m_sequenced_download_threshold) == priority); - assert((int)m_piece_map[replace_index].index == elem_index); - assert(src_vec[priority][elem_index] == replace_index); - } - else - { - assert((int)src_vec[priority].size() == elem_index+1); - } - - src_vec[priority].pop_back(); - } - } - - void piece_picker::remove(bool downloading, bool filtered, int priority - , int elem_index) - { - assert(!filtered); - assert(priority >= 0); - assert(elem_index >= 0); - - std::vector >& src_vec(pick_piece_info_vector(downloading, filtered)); - - assert((int)src_vec.size() > priority); - assert((int)src_vec[priority].size() > elem_index); - - int index = src_vec[priority][elem_index]; - - if (downloading) - { - std::vector::iterator i - = std::find_if(m_downloads.begin(), - m_downloads.end(), - has_index(index)); - assert(i != m_downloads.end()); - m_downloads.erase(i); - } - piece_pos& p = m_piece_map[index]; - p.downloading = 0; - if (p.ordered(m_sequenced_download_threshold)) - { - std::vector& v = src_vec[priority]; -// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); - std::vector::iterator i = v.begin() + elem_index; - v.erase(i); - i = v.begin() + elem_index; - for (; i != v.end(); ++i) - { - --m_piece_map[*i].index; - assert(v[m_piece_map[*i].index] == *i); - } -// assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); - } - else - { - // this will remove elem from the vector without - // preserving order - index = src_vec[priority][elem_index] = src_vec[priority].back(); - // update the entry we moved from the back - if ((int)src_vec[priority].size() > elem_index+1) - m_piece_map[index].index = elem_index; - src_vec[priority].pop_back(); - } - } - - void piece_picker::restore_piece(int index) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - - assert(index >= 0); - assert(index < (int)m_piece_map.size()); - - assert(m_piece_map[index].downloading == 1); - - std::vector::iterator i - = std::find_if(m_downloads.begin(), - m_downloads.end(), - has_index(index)); - assert(i != m_downloads.end()); - m_downloads.erase(i); - - m_piece_map[index].downloading = 0; - piece_pos& p = m_piece_map[index]; - if (p.filtered) return; - move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index); - } - - void piece_picker::inc_refcount(int i) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - assert(i >= 0); - assert(i < (int)m_piece_map.size()); - - int index = m_piece_map[i].index; - int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold); - - assert(m_piece_map[i].peer_count < 2048); - m_piece_map[i].peer_count++; - assert(m_piece_map[i].peer_count != 0); - - piece_pos& p = m_piece_map[i]; - - // if we have the piece or if it's filtered - // we don't have to move any entries in the piece_info vector - if (index == piece_pos::we_have_index || p.filtered - || p.priority(m_sequenced_download_threshold) == prev_priority) return; - - move(p.downloading, p.filtered, prev_priority, index); - -#ifndef NDEBUG -// integrity_check(); -#endif - return; - } - - void piece_picker::dec_refcount(int i) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - - assert(i >= 0); - assert(i < (int)m_piece_map.size()); - - int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold); - int index = m_piece_map[i].index; - assert(m_piece_map[i].peer_count > 0); - - if (m_piece_map[i].peer_count > 0) - m_piece_map[i].peer_count--; - - piece_pos& p = m_piece_map[i]; - - if (index == piece_pos::we_have_index || p.filtered - || p.priority(m_sequenced_download_threshold) == prev_priority) return; - - move(p.downloading, p.filtered, prev_priority, index); - } - - // this is used to indicate that we succesfully have - // downloaded a piece, and that no further attempts - // to pick that piece should be made. The piece will - // be removed from the available piece list. - void piece_picker::we_have(int index) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - assert(index >= 0); - assert(index < (int)m_piece_map.size()); - - int info_index = m_piece_map[index].index; - int priority = m_piece_map[index].priority(m_sequenced_download_threshold); - - assert(m_piece_map[index].downloading == 1); - - assert(info_index != piece_pos::we_have_index); - piece_pos& p = m_piece_map[index]; - if (p.filtered) - { - --m_num_filtered; - ++m_num_have_filtered; - return; - } - if (info_index == piece_pos::we_have_index) return; - remove(p.downloading, p.filtered, priority, info_index); - p.index = piece_pos::we_have_index; - } - - - void piece_picker::mark_as_filtered(int index) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - assert(index >= 0); - assert(index < (int)m_piece_map.size()); - - piece_pos& p = m_piece_map[index]; - if (p.filtered == 1) return; - p.filtered = 1; - if (p.index != piece_pos::we_have_index) - { - ++m_num_filtered; - remove(p.downloading, false, p.priority(m_sequenced_download_threshold), p.index); - assert(p.filtered == 1); - } - else - { - ++m_num_have_filtered; - } - } - - // this function can be used for pieces that we don't - // have, but have marked as filtered (so we didn't - // want to download them) but later want to enable for - // downloading, then we call this function and it will - // be inserted in the available piece list again - void piece_picker::mark_as_unfiltered(int index) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - assert(index >= 0); - assert(index < (int)m_piece_map.size()); - - piece_pos& p = m_piece_map[index]; - if (p.filtered == 0) return; - p.filtered = 0; - if (p.index != piece_pos::we_have_index) - { - --m_num_filtered; - assert(m_num_filtered >= 0); - add(index); - } - else - { - --m_num_have_filtered; - assert(m_num_have_filtered >= 0); - } - } - - bool piece_picker::is_filtered(int index) const - { - assert(index >= 0); - assert(index < (int)m_piece_map.size()); - - return m_piece_map[index].filtered == 1; - } - - void piece_picker::filtered_pieces(std::vector& mask) const - { - mask.resize(m_piece_map.size()); - std::vector::iterator j = mask.begin(); - for (std::vector::const_iterator i = m_piece_map.begin(), - end(m_piece_map.end()); i != end; ++i, ++j) - { - *j = i->filtered == 1; - } - } - - void piece_picker::pick_pieces(const std::vector& pieces - , std::vector& interesting_blocks - , int num_blocks, bool prefer_whole_pieces - , tcp::endpoint peer) const - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - assert(num_blocks > 0); - assert(pieces.size() == m_piece_map.size()); - - // free refers to pieces that are free to download, no one else - // is downloading them. - // partial is pieces that are partially being downloaded, and - // parts of them may be free for download as well, the - // partially downloaded pieces will be prioritized - assert(m_piece_info.begin() != m_piece_info.end()); - // +1 is to ignore pieces that no peer has. The bucket with index 0 contains - // pieces that 0 other peers has. - std::vector >::const_iterator free - = m_piece_info.begin() + 1; - assert(m_downloading_piece_info.begin() - != m_downloading_piece_info.end()); - - std::vector >::const_iterator partial - = m_downloading_piece_info.begin() + 1; - - std::vector backup_blocks; - - // this loop will loop from pieces with 1 peer and up - // until we either reach the end of the piece list or - // has filled the interesting_blocks with num_blocks - // blocks. - - // it iterates over two ranges simultaneously. The pieces that are - // partially downloaded or partially requested, and the pieces that - // hasn't been requested at all. The default is to prioritize pieces - // that are partially requested/downloaded, so the loop will first - // look for blocks among those pieces. And it will also take two steps - // in that range when iterating. This has the effect that partial pieces - // doesn't have to be as rare as non-requested pieces in order to be - // prefered. - - // When prefer_whole_pieces is set (usually set when downloading from - // fast peers) the partial pieces will not be prioritized, but actually - // ignored as long as possible. - - while((free != m_piece_info.end()) - || (partial != m_downloading_piece_info.end())) - { - if (partial != m_downloading_piece_info.end()) - { - for (int i = 0; i < 2; ++i) - { - num_blocks = add_interesting_blocks_partial(*partial, pieces - , interesting_blocks, backup_blocks, num_blocks - , prefer_whole_pieces, peer); - assert(num_blocks >= 0); - if (num_blocks == 0) return; - ++partial; - if (partial == m_downloading_piece_info.end()) break; - } - } - - if (free != m_piece_info.end()) - { - num_blocks = add_interesting_blocks_free(*free, pieces - , interesting_blocks, num_blocks, prefer_whole_pieces); - assert(num_blocks >= 0); - if (num_blocks == 0) return; - ++free; - } - } - - if (!prefer_whole_pieces) return; - assert(num_blocks > 0); - -#ifdef TORRENT_VERBOSE_LOGGING -// std::ofstream f("piece_picker.log", std::ios_base::app); -// f << "backup_blocks: " << backup_blocks.size() << "\n" -// << "used: " << std::min(num_blocks, (int)backup_blocks.size()) << "\n----\n"; -#endif - - interesting_blocks.insert(interesting_blocks.end() - , backup_blocks.begin(), backup_blocks.begin() - + (std::min)(num_blocks, (int)backup_blocks.size())); - } - - namespace - { - bool exclusively_requested_from(piece_picker::downloading_piece const& p - , int num_blocks_in_piece, tcp::endpoint peer) - { - for (int j = 0; j < num_blocks_in_piece; ++j) - { - if ((p.finished_blocks[j] == 1 - || p.requested_blocks[j] == 1) - && p.info[j].peer != peer - && p.info[j].peer != tcp::endpoint()) - { - return false; - } - } - return true; - } - } - - int piece_picker::add_interesting_blocks_free(std::vector const& piece_list - , std::vector const& pieces - , std::vector& interesting_blocks - , int num_blocks, bool prefer_whole_pieces) const - { - for (std::vector::const_iterator i = piece_list.begin(); - i != piece_list.end(); ++i) - { - assert(*i >= 0); - assert(*i < (int)m_piece_map.size()); - assert(m_piece_map[*i].downloading == 0); - - // if the peer doesn't have the piece - // skip it - if (!pieces[*i]) continue; - - int piece_blocks = blocks_in_piece(*i); - if (!prefer_whole_pieces && piece_blocks > num_blocks) - piece_blocks = num_blocks; - for (int j = 0; j < piece_blocks; ++j) - { - interesting_blocks.push_back(piece_block(*i, j)); - } - num_blocks -= (std::min)(piece_blocks, num_blocks); - assert(num_blocks >= 0); - if (num_blocks == 0) return num_blocks; - } - return num_blocks; - } - - int piece_picker::add_interesting_blocks_partial(std::vector const& piece_list - , const std::vector& pieces - , std::vector& interesting_blocks - , std::vector& backup_blocks - , int num_blocks, bool prefer_whole_pieces - , tcp::endpoint peer) const - { - assert(num_blocks > 0); - - for (std::vector::const_iterator i = piece_list.begin(); - i != piece_list.end(); ++i) - { - assert(*i >= 0); - assert(*i < (int)m_piece_map.size()); - // if the peer doesn't have the piece - // skip it - if (!pieces[*i]) continue; - - assert(m_piece_map[*i].downloading == 1); - - // calculate the number of blocks in this - // piece. It's always m_blocks_per_piece, except - // in the last piece. - int num_blocks_in_piece = blocks_in_piece(*i); - - std::vector::const_iterator p - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i)); - assert(p != m_downloads.end()); - - // this means that this partial piece has - // been downloaded/requested partially from - // another peer that isn't us. And since - // we prefer whole pieces, add this piece's - // blocks to the backup list. If the prioritized - // blocks aren't enough, blocks from this list - // will be picked. - if (prefer_whole_pieces - && !exclusively_requested_from(*p, num_blocks_in_piece, peer)) - { - if ((int)backup_blocks.size() >= num_blocks) continue; - for (int j = 0; j < num_blocks_in_piece; ++j) - { - if (p->finished_blocks[j] == 1) continue; - if (p->requested_blocks[j] == 1 - && p->info[j].peer == peer) continue; - backup_blocks.push_back(piece_block(*i, j)); - } - continue; - } - - for (int j = 0; j < num_blocks_in_piece; ++j) - { - if (p->finished_blocks[j] == 1) continue; - if (p->requested_blocks[j] == 1 - && p->info[j].peer == peer) continue; - // this block is interesting (we don't have it - // yet). But it may already have been requested - // from another peer. We have to add it anyway - // to allow the requester to determine if the - // block should be requested from more than one - // peer. If it is being downloaded, we continue - // to look for blocks until we have num_blocks - // blocks that have not been requested from any - // other peer. - interesting_blocks.push_back(piece_block(*i, j)); - if (p->requested_blocks[j] == 0) - { - // we have found a block that's free to download - num_blocks--; - if (prefer_whole_pieces) continue; - assert(num_blocks >= 0); - if (num_blocks == 0) return num_blocks; - } - } - assert(num_blocks >= 0 || prefer_whole_pieces); - if (num_blocks < 0) num_blocks = 0; - if (num_blocks == 0) return num_blocks; - } - return num_blocks; - } - - bool piece_picker::is_piece_finished(int index) const - { - assert(index < (int)m_piece_map.size()); - assert(index >= 0); - - if (m_piece_map[index].downloading == 0) return false; - std::vector::const_iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); - assert(i != m_downloads.end()); - assert((int)i->finished_blocks.count() <= m_blocks_per_piece); - int max_blocks = blocks_in_piece(index); - if ((int)i->finished_blocks.count() != max_blocks) return false; - - assert((int)i->requested_blocks.count() == max_blocks); - return true; - } - - bool piece_picker::is_downloading(piece_block block) const - { - assert(block.piece_index >= 0); - assert(block.block_index >= 0); - assert(block.piece_index < (int)m_piece_map.size()); - assert(block.block_index < (int)max_blocks_per_piece); - - if (m_piece_map[block.piece_index].downloading == 0) return false; - std::vector::const_iterator i - = std::find_if( - m_downloads.begin() - , m_downloads.end() - , has_index(block.piece_index)); - - assert(i != m_downloads.end()); - return i->requested_blocks[block.block_index]; - } - - bool piece_picker::is_finished(piece_block block) const - { - assert(block.piece_index >= 0); - assert(block.block_index >= 0); - assert(block.piece_index < (int)m_piece_map.size()); - assert(block.block_index < (int)max_blocks_per_piece); - - if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true; - if (m_piece_map[block.piece_index].downloading == 0) return false; - std::vector::const_iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - assert(i != m_downloads.end()); - return i->finished_blocks[block.block_index]; - } - - - void piece_picker::mark_as_downloading(piece_block block, const tcp::endpoint& peer) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - - assert(block.piece_index >= 0); - assert(block.block_index >= 0); - assert(block.piece_index < (int)m_piece_map.size()); - assert(block.block_index < blocks_in_piece(block.piece_index)); - - piece_pos& p = m_piece_map[block.piece_index]; - if (p.downloading == 0) - { - p.downloading = 1; - move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index); - - downloading_piece dp; - dp.index = block.piece_index; - dp.requested_blocks[block.block_index] = 1; - dp.info[block.block_index].peer = peer; - m_downloads.push_back(dp); - } - else - { - std::vector::iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - assert(i != m_downloads.end()); - assert(i->requested_blocks[block.block_index] == 0); - i->info[block.block_index].peer = peer; - i->requested_blocks[block.block_index] = 1; - } - } - - void piece_picker::mark_as_finished(piece_block block, const tcp::endpoint& peer) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - - assert(block.piece_index >= 0); - assert(block.block_index >= 0); - assert(block.piece_index < (int)m_piece_map.size()); - assert(block.block_index < blocks_in_piece(block.piece_index)); - - piece_pos& p = m_piece_map[block.piece_index]; - if (p.index == piece_pos::we_have_index || p.filtered) return; - - if (p.downloading == 0) - { - p.downloading = 1; - move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index); - - downloading_piece dp; - dp.index = block.piece_index; - dp.requested_blocks[block.block_index] = 1; - dp.finished_blocks[block.block_index] = 1; - dp.info[block.block_index].peer = peer; - m_downloads.push_back(dp); - } - else - { - std::vector::iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - assert(i != m_downloads.end()); - i->info[block.block_index].peer = peer; - i->requested_blocks[block.block_index] = 1; - i->finished_blocks[block.block_index] = 1; - } - } -/* - void piece_picker::mark_as_finished(piece_block block, const peer_id& peer) - { -#ifndef NDEBUG - integrity_check(); -#endif - assert(block.piece_index >= 0); - assert(block.block_index >= 0); - assert(block.piece_index < m_piece_map.size()); - assert(block.block_index < blocks_in_piece(block.piece_index)); - - assert(m_piece_map[block.piece_index].downloading == 1); - - std::vector::iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - assert(i != m_downloads.end()); - i->finished_blocks[block.block_index] = 1; - // the block may have been requested, then cancled - // and requested by a peer that disconnects - // that way we can actually receive the piece - // without the requested bit is set. - i->requested_blocks[block.block_index] = 1; - i->info[block.block_index].num_downloads++; - i->info[block.block_index].peer = peer; -#ifndef NDEBUG - integrity_check(); -#endif - } -*/ - void piece_picker::get_downloaders(std::vector& d, int index) const - { - assert(index >= 0 && index <= (int)m_piece_map.size()); - std::vector::const_iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)); - assert(i != m_downloads.end()); - - d.clear(); - for (int j = 0; j < blocks_in_piece(index); ++j) - { - d.push_back(i->info[j].peer); - } - } - - boost::optional piece_picker::get_downloader(piece_block block) const - { - std::vector::const_iterator i = std::find_if( - m_downloads.begin() - , m_downloads.end() - , has_index(block.piece_index)); - - if (i == m_downloads.end()) - return boost::optional(); - - assert(block.block_index < max_blocks_per_piece); - assert(block.block_index >= 0); - - if (i->requested_blocks[block.block_index] == false - || i->finished_blocks[block.block_index] == true) - return boost::optional(); - - return boost::optional(i->info[block.block_index].peer); - } - - void piece_picker::abort_download(piece_block block) - { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; - - assert(block.piece_index >= 0); - assert(block.block_index >= 0); - assert(block.piece_index < (int)m_piece_map.size()); - assert(block.block_index < blocks_in_piece(block.piece_index)); - - if (m_piece_map[block.piece_index].downloading == 0) - { - assert(std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)) == m_downloads.end()); - return; - } - - std::vector::iterator i - = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(block.piece_index)); - assert(i != m_downloads.end()); - - if (i->finished_blocks[block.block_index]) return; - - assert(block.block_index < blocks_in_piece(block.piece_index)); -#ifndef NDEBUG - if (i->requested_blocks[block.block_index] != 1) - { - assert(false); - } -#endif - - // clear this block as being downloaded - i->requested_blocks[block.block_index] = 0; - - // if there are no other blocks in this pieces - // that's being downloaded, remove it from the list - if (i->requested_blocks.count() == 0) - { - m_downloads.erase(i); - m_piece_map[block.piece_index].downloading = 0; - piece_pos& p = m_piece_map[block.piece_index]; - move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index); - } - } - - int piece_picker::unverified_blocks() const - { - int counter = 0; - for (std::vector::const_iterator i = m_downloads.begin(); - i != m_downloads.end(); ++i) - { - counter += (int)i->finished_blocks.count(); - } - return counter; - } - -} - diff --git a/libtorrent/cpp/policy.cpp b/libtorrent/cpp/policy.cpp deleted file mode 100755 index bdd90ac77..000000000 --- a/libtorrent/cpp/policy.cpp +++ /dev/null @@ -1,1419 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include - -#include "libtorrent/peer_connection.hpp" - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/web_peer_connection.hpp" -#include "libtorrent/policy.hpp" -#include "libtorrent/torrent.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/invariant_check.hpp" -#include "libtorrent/aux_/session_impl.hpp" - -namespace libtorrent -{ - class peer_connection; -} - -using namespace boost::posix_time; -using boost::bind; - -namespace -{ - using namespace libtorrent; - - // the case where ignore_peer is motivated is if two peers - // have only one piece that we don't have, and it's the - // same piece for both peers. Then they might get into an - // infinite loop, fighting to request the same blocks. - void request_a_block( - torrent& t - , peer_connection& c - , std::vector ignore = std::vector()) - { - int num_requests = c.desired_queue_size() - - (int)c.download_queue().size() - - (int)c.request_queue().size(); - - // if our request queue is already full, we - // don't have to make any new requests yet - if (num_requests <= 0) return; - - piece_picker& p = t.picker(); - std::vector interesting_pieces; - interesting_pieces.reserve(100); - - // picks the interesting pieces from this peer - // the integer is the number of pieces that - // should be guaranteed to be available for download - // (if num_requests is too big, too many pieces are - // picked and cpu-time is wasted) - // the last argument is if we should prefer whole pieces - // for this peer. If we're downloading one piece in 20 seconds - // then use this mode. - bool prefer_whole_pieces = c.statistics().download_payload_rate() - * t.settings().whole_pieces_threshold - > t.torrent_file().piece_length(); - - // if we prefer whole pieces, the piece picker will pick at least - // the number of blocks we want, but it will try to make the picked - // blocks be from whole pieces, possibly by returning more blocks - // than we requested. -#ifndef NDEBUG - assert(c.remote() == c.get_socket()->remote_endpoint()); -#endif - p.pick_pieces(c.get_bitfield(), interesting_pieces - , num_requests, prefer_whole_pieces, c.remote()); - - // this vector is filled with the interesting pieces - // that some other peer is currently downloading - // we should then compare this peer's download speed - // with the other's, to see if we should abort another - // peer_connection in favour of this one - std::vector busy_pieces; - busy_pieces.reserve(10); - - for (std::vector::iterator i = interesting_pieces.begin(); - i != interesting_pieces.end(); ++i) - { - if (p.is_downloading(*i)) - { - busy_pieces.push_back(*i); - continue; - } - - // ok, we found a piece that's not being downloaded - // by somebody else. request it from this peer - // and return - c.add_request(*i); - num_requests--; - } - - c.send_block_requests(); - - // in this case, we could not find any blocks - // that was free. If we couldn't find any busy - // blocks as well, we cannot download anything - // more from this peer. - - if (busy_pieces.empty()) return; - - // first look for blocks that are just queued - // and not actually sent to us yet - // (then we can cancel those and request them - // from this peer instead) - - while (num_requests > 0) - { - peer_connection* peer = 0; - - const int initial_queue_size = (int)c.download_queue().size() - + (int)c.request_queue().size(); - - // This peer's weight will be the minimum, to prevent - // cancelling requests from a faster peer. - float min_weight = initial_queue_size == 0 - ? std::numeric_limits::max() - : c.statistics().download_payload_rate() / initial_queue_size; - - // find the peer with the lowest download - // speed that also has a piece that this - // peer could send us - for (torrent::peer_iterator i = t.begin(); - i != t.end(); ++i) - { - // don't try to take over blocks from ourself - if (i->second == &c) - continue; - - // ignore all peers in the ignore list - if (std::find(ignore.begin(), ignore.end(), i->second) != ignore.end()) - continue; - - const std::deque& download_queue = i->second->download_queue(); - const std::deque& request_queue = i->second->request_queue(); - const int queue_size = (int)i->second->download_queue().size() - + (int)i->second->request_queue().size(); - const float weight = queue_size == 0 - ? std::numeric_limits::max() - : i->second->statistics().download_payload_rate() / queue_size; - - // if the peer's (i) weight is less than the lowest we've found so - // far (weight == priority) and it has blocks in its request- - // or download queue that we could request from this peer (c), - // replace the currently lowest ranking peer. - if (weight < min_weight - && (std::find_first_of( - busy_pieces.begin() - , busy_pieces.end() - , request_queue.begin() - , request_queue.end()) != busy_pieces.end() - || std::find_first_of( - busy_pieces.begin() - , busy_pieces.end() - , download_queue.begin() - , download_queue.end()) != busy_pieces.end())) - { - peer = i->second; - min_weight = weight; - } - } - - if (peer == 0) - { - // we probably couldn't request the block because - // we are ignoring some peers - break; - } - - // find a suitable block to take over from this peer - - std::deque::const_reverse_iterator common_block = - std::find_first_of( - peer->request_queue().rbegin() - , peer->request_queue().rend() - , busy_pieces.begin() - , busy_pieces.end()); - - if (common_block == peer->request_queue().rend()) - { - common_block = std::find_first_of( - peer->download_queue().rbegin() - , peer->download_queue().rend() - , busy_pieces.begin() - , busy_pieces.end()); - assert(common_block != peer->download_queue().rend()); - } - - piece_block block = *common_block; - peer->cancel_request(block); - c.add_request(block); - - // the one we interrupted may need to request a new piece. - // make sure it doesn't take over a block from the peer - // that just took over its block - ignore.push_back(&c); - request_a_block(t, *peer, ignore); - num_requests--; - - const int queue_size = (int)c.download_queue().size() - + (int)c.request_queue().size(); - const float weight = queue_size == 0 - ? std::numeric_limits::max() - : c.statistics().download_payload_rate() / queue_size; - - // this peer doesn't have a faster connection than the - // slowest peer. Don't take over any blocks - if (weight <= min_weight) break; - } - c.send_block_requests(); - } - - - size_type collect_free_download( - torrent::peer_iterator start - , torrent::peer_iterator end) - { - size_type accumulator = 0; - for (torrent::peer_iterator i = start; i != end; ++i) - { - // if the peer is interested in us, it means it may - // want to trade it's surplus uploads for downloads itself - // (and we should not consider it free). If the share diff is - // negative, there's no free download to get from this peer. - size_type diff = i->second->share_diff(); - assert(diff < std::numeric_limits::max()); - if (i->second->is_peer_interested() || diff <= 0) - continue; - - assert(diff > 0); - i->second->add_free_upload(-diff); - accumulator += diff; - assert(accumulator > 0); - } - assert(accumulator >= 0); - return accumulator; - } - - - // returns the amount of free upload left after - // it has been distributed to the peers - size_type distribute_free_upload( - torrent::peer_iterator start - , torrent::peer_iterator end - , size_type free_upload) - { - if (free_upload <= 0) return free_upload; - int num_peers = 0; - size_type total_diff = 0; - for (torrent::peer_iterator i = start; i != end; ++i) - { - size_type d = i->second->share_diff(); - assert(d < std::numeric_limits::max()); - total_diff += d; - if (!i->second->is_peer_interested() || i->second->share_diff() >= 0) continue; - ++num_peers; - } - - if (num_peers == 0) return free_upload; - size_type upload_share; - if (total_diff >= 0) - { - upload_share = std::min(free_upload, total_diff) / num_peers; - } - else - { - upload_share = (free_upload + total_diff) / num_peers; - } - if (upload_share < 0) return free_upload; - - for (torrent::peer_iterator i = start; i != end; ++i) - { - peer_connection* p = i->second; - if (!p->is_peer_interested() || p->share_diff() >= 0) continue; - p->add_free_upload(upload_share); - free_upload -= upload_share; - } - return free_upload; - } - - struct match_peer_ip - { - match_peer_ip(const tcp::endpoint& ip) - : m_ip(ip) - {} - - bool operator()(const policy::peer& p) const - { return p.ip.address() == m_ip.address(); } - - tcp::endpoint m_ip; - }; - - struct match_peer_connection - { - match_peer_connection(const peer_connection& c) - : m_conn(c) - {} - - bool operator()(const policy::peer& p) const - { return p.connection == &m_conn; } - - const peer_connection& m_conn; - }; - - -} - -namespace libtorrent -{ - policy::policy(torrent* t) - : m_torrent(t) -// , m_max_uploads(std::numeric_limits::max()) -// , m_max_connections(std::numeric_limits::max()) - , m_num_unchoked(0) - , m_available_free_upload(0) - , m_last_optimistic_disconnect(boost::gregorian::date(1970,boost::gregorian::Jan,1)) - { assert(t); } - // finds the peer that has the worst download rate - // and returns it. May return 0 if all peers are - // choked. - policy::peer* policy::find_choke_candidate() - { - INVARIANT_CHECK; - - peer* worst_peer = 0; - size_type min_weight = std::numeric_limits::min(); - -#ifndef NDEBUG - int unchoked_counter = m_num_unchoked; -#endif - - // TODO: make this selection better - - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - - if (c == 0) continue; - if (c->is_choked()) continue; -#ifndef NDEBUG - unchoked_counter--; -#endif - if (c->is_disconnecting()) continue; - // if the peer isn't interested, just choke it - if (!c->is_peer_interested()) - return &(*i); - - size_type diff = i->total_download() - - i->total_upload(); - - size_type weight = static_cast(c->statistics().download_rate() * 10.f) - + diff - + ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024; - - if (weight >= min_weight && worst_peer) continue; - - min_weight = weight; - worst_peer = &(*i); - continue; - } - assert(unchoked_counter == 0); - return worst_peer; - } - - policy::peer* policy::find_unchoke_candidate() - { - INVARIANT_CHECK; - - // if all of our peers are unchoked, there's - // no left to unchoke - if (m_num_unchoked == m_torrent->num_peers()) - return 0; - - using namespace boost::posix_time; - using namespace boost::gregorian; - - peer* unchoke_peer = 0; - ptime min_time(date(9999,Jan,1)); - float max_down_speed = 0.f; - - // TODO: make this selection better - - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - if (c == 0) continue; - if (c->is_disconnecting()) continue; - if (!c->is_choked()) continue; - if (!c->is_peer_interested()) continue; - if (c->share_diff() < -free_upload_amount - && m_torrent->ratio() != 0) continue; - if (c->statistics().download_rate() < max_down_speed) continue; -// if (i->last_optimistically_unchoked > min_time) continue; - - min_time = i->last_optimistically_unchoked; - max_down_speed = c->statistics().download_rate(); - unchoke_peer = &(*i); - } - return unchoke_peer; - } - - policy::peer* policy::find_disconnect_candidate() - { - peer *disconnect_peer = 0; - double slowest_transfer_rate = std::numeric_limits::max(); - - boost::posix_time::ptime local_time - = second_clock::universal_time(); - - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - if(c == 0) - continue; - if(c->is_disconnecting()) - continue; - - double transferred_amount - = (double)c->statistics().total_payload_download(); - - boost::posix_time::time_duration connected_time - = local_time - i->connected; - - double connected_time_in_seconds - = connected_time.seconds() - + connected_time.minutes()*60.0 - + connected_time.hours()*60.0*60.0; - - double transfer_rate - = transferred_amount / (connected_time_in_seconds+1); - - if (transfer_rate <= slowest_transfer_rate) - { - slowest_transfer_rate = transfer_rate; - disconnect_peer = &(*i); - } - } - return disconnect_peer; - } - - policy::peer *policy::find_connect_candidate() - { - boost::posix_time::ptime local_time=second_clock::universal_time(); - boost::posix_time::ptime ptime(local_time); - policy::peer* candidate =0; - - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - if(i->connection) continue; - if(i->banned) continue; - if(i->type == peer::not_connectable) continue; - - assert(i->connected <= local_time); - - boost::posix_time::ptime next_connect = i->connected; - - if (next_connect <= ptime) - { - ptime = next_connect; - candidate = &(*i); - } - } - - assert(ptime <= local_time); - - return candidate; - } - - policy::peer* policy::find_seed_choke_candidate() - { - INVARIANT_CHECK; - - assert(m_num_unchoked > 0); - // first choice candidate. - // it is a candidate we owe nothing to and which has been unchoked - // the longest. - using namespace boost::posix_time; - using namespace boost::gregorian; - - peer* candidate = 0; - - // not valid when candidate == 0 - ptime last_unchoke = ptime(date(1970, Jan, 1)); - - // second choice candidate. - // if there is no first choice candidate, this candidate will be chosen. - // it is the candidate that we owe the least to. - peer* second_candidate = 0; - size_type lowest_share_diff = 0; // not valid when secondCandidate==0 - - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - // ignore peers that are choked or - // whose connection is closed - if (c == 0) continue; - - if (c->is_choked()) continue; - if (c->is_disconnecting()) continue; - - size_type share_diff = c->share_diff(); - - // select as second candidate the one that we owe the least - // to - if (!second_candidate || share_diff <= lowest_share_diff) - { - lowest_share_diff = share_diff; - second_candidate = &(*i); - } - - // select as first candidate the one that we don't owe anything to - // and has been waiting for an unchoke the longest - if (share_diff > 0) continue; - if (!candidate || last_unchoke > i->last_optimistically_unchoked) - { - last_unchoke = i->last_optimistically_unchoked; - candidate = &(*i); - } - } - if (candidate) return candidate; - if (second_candidate) return second_candidate; - assert(false); - return 0; - } - - policy::peer* policy::find_seed_unchoke_candidate() - { - INVARIANT_CHECK; - - peer* candidate = 0; - boost::posix_time::ptime last_unchoke - = second_clock::universal_time(); - - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - if (c == 0) continue; - if (!c->is_choked()) continue; - if (!c->is_peer_interested()) continue; - if (c->is_disconnecting()) continue; - if (last_unchoke < i->last_optimistically_unchoked) continue; - last_unchoke = i->last_optimistically_unchoked; - candidate = &(*i); - } - return candidate; - } - - bool policy::seed_unchoke_one_peer() - { - INVARIANT_CHECK; - - peer* p = find_seed_unchoke_candidate(); - if (p != 0) - { - assert(p->connection->is_choked()); - p->connection->send_unchoke(); - p->last_optimistically_unchoked - = second_clock::universal_time(); - ++m_num_unchoked; - } - return p != 0; - } - - void policy::seed_choke_one_peer() - { - INVARIANT_CHECK; - - peer* p = find_seed_choke_candidate(); - if (p != 0) - { - assert(!p->connection->is_choked()); - p->connection->send_choke(); - --m_num_unchoked; - } - } - - void policy::pulse() - { - INVARIANT_CHECK; - - if (m_torrent->is_paused()) return; - - using namespace boost::posix_time; - - // TODO: we must also remove peers that - // we failed to connect to from this list - // to avoid being part of a DDOS-attack - - // remove old disconnected peers from the list - m_peers.erase( - std::remove_if(m_peers.begin() - , m_peers.end() - , old_disconnected_peer()) - , m_peers.end()); - - // ------------------------------------- - // maintain the number of connections - // ------------------------------------- - - // count the number of connected peers except for peers - // that are currently in the process of disconnecting - int num_connected_peers = 0; - - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - if (i->connection && !i->connection->is_disconnecting()) - ++num_connected_peers; - } - - if (m_torrent->m_connections_quota.given != std::numeric_limits::max()) - { - - int max_connections = m_torrent->m_connections_quota.given; - - if (num_connected_peers >= max_connections) - { - // every minute, disconnect the worst peer in hope of finding a better peer - - boost::posix_time::ptime local_time = second_clock::universal_time(); - if (m_last_optimistic_disconnect + boost::posix_time::seconds(120) <= local_time) - { - m_last_optimistic_disconnect = local_time; - --max_connections; // this will have the effect of disconnecting the worst peer - } - } - else - { - // don't do a disconnect earlier than 1 minute after some peer was connected - m_last_optimistic_disconnect = second_clock::universal_time(); - } - - while (num_connected_peers > max_connections) - { - bool ret = disconnect_one_peer(); - (void)ret; - assert(ret); - --num_connected_peers; - } - } - - while (m_torrent->num_peers() < m_torrent->m_connections_quota.given) - { - if (!connect_one_peer()) - break; - } - - - // ------------------------ - // upload shift - // ------------------------ - - // this part will shift downloads - // from peers that are seeds and peers - // that don't want to download from us - // to peers that cannot upload anything - // to us. The shifting will make sure - // that the torrent's share ratio - // will be maintained - - // if the share ratio is 0 (infinite) - // m_available_free_upload isn't used - // because it isn't necessary - if (m_torrent->ratio() != 0.f) - { - // accumulate all the free download we get - // and add it to the available free upload - m_available_free_upload - += collect_free_download( - m_torrent->begin() - , m_torrent->end()); - - // distribute the free upload among the peers - m_available_free_upload = distribute_free_upload( - m_torrent->begin() - , m_torrent->end() - , m_available_free_upload); - } - - // ------------------------ - // seed choking policy - // ------------------------ - if (m_torrent->is_seed()) - { - if (m_num_unchoked > m_torrent->m_uploads_quota.given) - { - do - { - peer* p = find_seed_choke_candidate(); - --m_num_unchoked; - assert(p != 0); - if (p == 0) break; - - assert(!p->connection->is_choked()); - p->connection->send_choke(); - } while (m_num_unchoked > m_torrent->m_uploads_quota.given); - } - else if (m_num_unchoked > 0) - { - // optimistic unchoke. trade the 'worst' - // unchoked peer with one of the choked - // TODO: This rotation should happen - // far less frequent than this! - assert(m_num_unchoked <= m_torrent->num_peers()); - peer* p = find_seed_unchoke_candidate(); - if (p) - { - assert(p->connection->is_choked()); - seed_choke_one_peer(); - p->connection->send_unchoke(); - ++m_num_unchoked; - } - - } - - // make sure we have enough - // unchoked peers - while (m_num_unchoked < m_torrent->m_uploads_quota.given) - { - if (!seed_unchoke_one_peer()) break; - } -#ifndef NDEBUG - check_invariant(); -#endif - } - - // ---------------------------- - // downloading choking policy - // ---------------------------- - else - { - if (m_torrent->ratio() != 0) - { - // choke peers that have leeched too much without giving anything back - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - if (c == 0) continue; - - size_type diff = i->connection->share_diff(); - if (diff < -free_upload_amount - && !c->is_choked()) - { - // if we have uploaded more than a piece for free, choke peer and - // wait until we catch up with our download. - c->send_choke(); - --m_num_unchoked; - } - } - } - - if (m_torrent->m_uploads_quota.given < m_torrent->num_peers()) - { - assert(m_torrent->m_uploads_quota.given >= 0); - - // make sure we don't have too many - // unchoked peers - if (m_num_unchoked > m_torrent->m_uploads_quota.given) - { - do - { - peer* p = find_choke_candidate(); - if (!p) break; - assert(p); - assert(!p->connection->is_choked()); - p->connection->send_choke(); - --m_num_unchoked; - } while (m_num_unchoked > m_torrent->m_uploads_quota.given); - } - else - { - // optimistic unchoke. trade the 'worst' - // unchoked peer with one of the choked - // TODO: This rotation should happen - // far less frequent than this! - assert(m_num_unchoked <= m_torrent->num_peers()); - peer* p = find_unchoke_candidate(); - if (p) - { - assert(p->connection->is_choked()); - choke_one_peer(); - p->connection->send_unchoke(); - ++m_num_unchoked; - } - } - } - - // make sure we have enough - // unchoked peers - while (m_num_unchoked < m_torrent->m_uploads_quota.given - && unchoke_one_peer()); - } - } - - void policy::ban_peer(const peer_connection& c) - { - INVARIANT_CHECK; - - std::vector::iterator i = std::find_if( - m_peers.begin() - , m_peers.end() - , match_peer_connection(c)); - - if (i == m_peers.end()) - { - // this is probably an http seed - if (web_peer_connection const* p = dynamic_cast(&c)) - { - m_torrent->remove_url_seed(p->url()); - } - return; - } - - i->type = peer::not_connectable; - i->ip.port(0); - i->banned = true; - } - - void policy::new_connection(peer_connection& c) - { - assert(!c.is_local()); -/* -#ifndef NDEBUG - // avoid the invariant check to fail - peer p(tcp::endpoint("0.0.0.0", 0), peer::not_connectable); - p.connection = &c; - m_peers.push_back(p); -#endif -*/ - INVARIANT_CHECK; -/* -#ifndef NDEBUG - // avoid the invariant check to fail - m_peers.erase(m_peers.end() - 1); -#endif -*/ - // if the connection comes from the tracker, - // it's probably just a NAT-check. Ignore the - // num connections constraint then. - - // TODO: only allow _one_ connection to use this - // override at a time -#ifndef NDEBUG - assert(c.remote() == c.get_socket()->remote_endpoint()); -#endif - if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given - && c.remote().address() != m_torrent->current_tracker().address()) - { - throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (c.remote().address() == m_torrent->current_tracker().address()) - { - m_torrent->debug_log("overriding connection limit for tracker NAT-check"); - } -#endif - - std::vector::iterator i = std::find_if( - m_peers.begin() - , m_peers.end() - , match_peer_ip(c.remote())); - - - if (i != m_peers.end()) - { - if (i->banned) - throw protocol_error("ip address banned, closing"); - - if (i->connection != 0) - { - // the new connection is a local (outgoing) connection - // or the current one is already connected - if (!i->connection->is_connecting() || c.is_local()) - { - throw protocol_error("duplicate connection, closing"); - } - else - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - m_torrent->debug_log("duplicate connection. existing connection" - " is connecting and this connection is incoming. closing existing " - "connection in favour of this one"); -#endif - i->connection->disconnect(); - i->connection = 0; - } - } - } - else - { - using namespace boost::posix_time; - using namespace boost::gregorian; - - // we don't have ny info about this peer. - // add a new entry -#ifndef NDEBUG - assert(c.remote() == c.get_socket()->remote_endpoint()); -#endif - peer p(c.remote(), peer::not_connectable); - m_peers.push_back(p); - i = m_peers.end()-1; - } - - assert(i->connection == 0); - c.add_stat(i->prev_amount_download, i->prev_amount_upload); - i->prev_amount_download = 0; - i->prev_amount_upload = 0; - i->connection = &c; - assert(i->connection); - i->connected = second_clock::universal_time(); - m_last_optimistic_disconnect = second_clock::universal_time(); - } - - void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid) - { - INVARIANT_CHECK; - - // just ignore the obviously invalid entries from the tracker - if(remote.address() == address() || remote.port() == 0) - return; - - try - { - std::vector::iterator i = std::find_if( - m_peers.begin() - , m_peers.end() - , match_peer_ip(remote)); - - bool just_added = false; - - if (i == m_peers.end()) - { - using namespace boost::posix_time; - using namespace boost::gregorian; - - // we don't have any info about this peer. - // add a new entry - peer p(remote, peer::connectable); - m_peers.push_back(p); - // the iterator is invalid - // because of the push_back() - i = m_peers.end() - 1; - just_added = true; - } - else - { - i->type = peer::connectable; - - // in case we got the ip from a remote connection, port is - // not known, so save it. Client may also have changed port - // for some reason. - i->ip = remote; - - if (i->connection) - { - // this means we're already connected - // to this peer. don't connect to - // it again. - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - m_torrent->debug_log("already connected to peer: " + remote.address().to_string() + ":" - + boost::lexical_cast(remote.port())); -#endif - - assert(i->connection->associated_torrent().lock().get() == m_torrent); - return; - } - } - - if (i->banned) return; - - if (m_torrent->num_peers() < m_torrent->m_connections_quota.given - && !m_torrent->is_paused()) - { - if (!connect_peer(&*i) && just_added) - { - // if this peer was just added, and it - // failed to connect. Remove it from the list - // (to keep it in sync with the session's list) - assert(i == m_peers.end() - 1); - m_peers.erase(i); - } - } - return; - } - catch(std::exception& e) - { - if (m_torrent->alerts().should_post(alert::debug)) - { - m_torrent->alerts().post_alert( - peer_error_alert(remote, pid, e.what())); - } - } - } - - // this is called when we are choked by a peer - // i.e. a peer lets us know that we will not receive - // anything for a while - void policy::choked(peer_connection&) - { - } - - void policy::piece_finished(int index, bool successfully_verified) - { - INVARIANT_CHECK; - - assert(index >= 0 && index < m_torrent->torrent_file().num_pieces()); - - if (successfully_verified) - { - // have all peers update their interested-flag - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - if (i->connection == 0) continue; - // if we're not interested, we will not become interested - if (!i->connection->is_interesting()) continue; - if (!i->connection->has_piece(index)) continue; - - bool interested = false; - const std::vector& peer_has = i->connection->get_bitfield(); - const std::vector& we_have = m_torrent->pieces(); - assert(we_have.size() == peer_has.size()); - for (int j = 0; j != (int)we_have.size(); ++j) - { - if (!we_have[j] && peer_has[j]) - { - interested = true; - break; - } - } - if (!interested) - i->connection->send_not_interested(); - assert(i->connection->is_interesting() == interested); - } - } - } - - // TODO: we must be able to get interested - // in a peer again, if a piece fails that - // this peer has. - void policy::block_finished(peer_connection& c, piece_block) - { - INVARIANT_CHECK; - - // if the peer hasn't choked us, ask for another piece - if (!c.has_peer_choked()) - request_a_block(*m_torrent, c); - } - - // this is called when we are unchoked by a peer - // i.e. a peer lets us know that we will receive - // data from now on - void policy::unchoked(peer_connection& c) - { - INVARIANT_CHECK; - if (c.is_interesting()) - { - request_a_block(*m_torrent, c); - } - } - - // called when a peer is interested in us - void policy::interested(peer_connection& c) - { - INVARIANT_CHECK; - - assert(std::find_if(m_peers.begin(), m_peers.end() - , boost::bind(std::equal_to(), bind(&peer::connection, _1) - , &c)) != m_peers.end()); - - // if the peer is choked and we have upload slots left, - // then unchoke it. Another condition that has to be met - // is that the torrent doesn't keep track of the individual - // up/down ratio for each peer (ratio == 0) or (if it does - // keep track) this particular connection isn't a leecher. - // If the peer was choked because it was leeching, don't - // unchoke it again. - // The exception to this last condition is if we're a seed. - // In that case we don't care if people are leeching, they - // can't pay for their downloads anyway. - if (c.is_choked() - && m_num_unchoked < m_torrent->m_uploads_quota.given - && (m_torrent->ratio() == 0 - || c.share_diff() >= -free_upload_amount - || m_torrent->is_seed())) - { - c.send_unchoke(); - ++m_num_unchoked; - } - } - - // called when a peer is no longer interested in us - void policy::not_interested(peer_connection& c) - { - INVARIANT_CHECK; - - if (m_torrent->ratio() != 0.f) - { - assert(c.share_diff() < std::numeric_limits::max()); - size_type diff = c.share_diff(); - if (diff > 0 && c.is_seed()) - { - // the peer is a seed and has sent - // us more than we have sent it back. - // consider the download as free download - m_available_free_upload += diff; - c.add_free_upload(-diff); - } - } - if (!c.is_choked()) - { - c.send_choke(); - --m_num_unchoked; - - if (m_torrent->is_seed()) seed_unchoke_one_peer(); - else unchoke_one_peer(); - } - } - - bool policy::unchoke_one_peer() - { - peer* p = find_unchoke_candidate(); - if (p == 0) return false; - assert(p->connection); - assert(!p->connection->is_disconnecting()); - - assert(p->connection->is_choked()); - p->connection->send_unchoke(); - p->last_optimistically_unchoked = second_clock::universal_time(); - ++m_num_unchoked; - return true; - } - - void policy::choke_one_peer() - { - peer* p = find_choke_candidate(); - if (p == 0) return; - assert(p->connection); - assert(!p->connection->is_disconnecting()); - assert(!p->connection->is_choked()); - p->connection->send_choke(); - --m_num_unchoked; - } - - bool policy::connect_one_peer() - { - if(m_torrent->num_peers() >= m_torrent->m_connections_quota.given) - return false; - peer* p = find_connect_candidate(); - if (p == 0) return false; - assert(!p->banned); - assert(!p->connection); - assert(p->type == peer::connectable); - - return connect_peer(p); - } - - bool policy::connect_peer(peer *p) - { - INVARIANT_CHECK; - try - { - assert(!p->connection); - p->connection = &m_torrent->connect_to_peer(p->ip); - assert(p->connection); - p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload); - p->prev_amount_download = 0; - p->prev_amount_upload = 0; - p->connected = - m_last_optimistic_disconnect = - second_clock::universal_time(); - return true; - } - catch (std::exception& e) - {} - return false; - } - - bool policy::disconnect_one_peer() - { - peer *p = find_disconnect_candidate(); - if(!p) - return false; -#if defined(TORRENT_VERBOSE_LOGGING) - (*p->connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n"; -#endif - - p->connection->disconnect(); - return true; - } - - // this is called whenever a peer connection is closed - void policy::connection_closed(const peer_connection& c) try - { - INVARIANT_CHECK; - -// assert(c.is_disconnecting()); - bool unchoked = false; - - std::vector::iterator i = std::find_if( - m_peers.begin() - , m_peers.end() - , match_peer_connection(c)); - - // if we couldn't find the connection in our list, just ignore it. - if (i == m_peers.end()) return; - assert(i->connection == &c); - - i->connected = second_clock::universal_time(); - if (!i->connection->is_choked() && !m_torrent->is_aborted()) - { - unchoked = true; - } - - if (c.failed()) - { - i->type = peer::not_connectable; - i->ip.port(0); - } - - // if the share ratio is 0 (infinite), the - // m_available_free_upload isn't used, - // because it isn't necessary. - if (m_torrent->ratio() != 0.f) - { - assert(i->connection->associated_torrent().lock().get() == m_torrent); - assert(i->connection->share_diff() < std::numeric_limits::max()); - m_available_free_upload += i->connection->share_diff(); - } - i->prev_amount_download += c.statistics().total_payload_download(); - i->prev_amount_upload += c.statistics().total_payload_upload(); - i->connection = 0; - - if (unchoked) - { - // if the peer that is diconnecting is unchoked - // then unchoke another peer in order to maintain - // the total number of unchoked peers - --m_num_unchoked; - if (m_torrent->is_seed()) seed_unchoke_one_peer(); - else unchoke_one_peer(); - } - } - catch (std::exception& e) - { -#ifndef NDEBUG - std::string err = e.what(); -#endif - assert(false); - } - - void policy::peer_is_interesting(peer_connection& c) - { - INVARIANT_CHECK; - - c.send_interested(); - if (c.has_peer_choked()) return; - request_a_block(*m_torrent, c); - } - -#ifndef NDEBUG - bool policy::has_connection(const peer_connection* c) - { - assert(c); -#ifndef NDEBUG - assert(c->remote() == c->get_socket()->remote_endpoint()); -#endif - return std::find_if( - m_peers.begin() - , m_peers.end() - , match_peer_ip(c->remote())) != m_peers.end(); - } - - void policy::check_invariant() const - { - if (m_torrent->is_aborted()) return; - int actual_unchoked = 0; - int connected_peers = 0; - - int total_connections = 0; - int nonempty_connections = 0; - - - for (std::vector::const_iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - ++total_connections; - if (!i->connection) continue; - ++nonempty_connections; - if (!i->connection->is_disconnecting()) - ++connected_peers; - if (!i->connection->is_choked()) ++actual_unchoked; - } -// assert(actual_unchoked <= m_torrent->m_uploads_quota.given); - assert(actual_unchoked == m_num_unchoked); - - int num_torrent_peers = 0; - for (torrent::const_peer_iterator i = m_torrent->begin(); - i != m_torrent->end(); ++i) - { - if (i->second->is_disconnecting()) continue; - // ignore web_peer_connections since they are not managed - // by the policy class - if (dynamic_cast(i->second)) continue; - ++num_torrent_peers; - } - - // this invariant is a bit complicated. - // the usual case should be that connected_peers - // == num_torrent_peers. But when there's an incoming - // connection, it will first be added to the policy - // and then be added to the torrent. - // When there's an outgoing connection, it will first - // be added to the torrent and then to the policy. - // that's why the two second cases are in there. - - assert(connected_peers == num_torrent_peers - || (connected_peers == num_torrent_peers + 1 - && connected_peers > 0) - || (connected_peers + 1 == num_torrent_peers - && num_torrent_peers > 0)); - - // TODO: Make sure the number of peers in m_torrent is equal - // to the number of connected peers in m_peers. - } -#endif - - policy::peer::peer(const tcp::endpoint& ip_, peer::connection_type t) - : ip(ip_) - , type(t) - , last_optimistically_unchoked( - boost::gregorian::date(1970,boost::gregorian::Jan,1)) - , connected(boost::gregorian::date(1970,boost::gregorian::Jan,1)) - , prev_amount_upload(0) - , prev_amount_download(0) - , banned(false) - , connection(0) - { - assert(connected < second_clock::universal_time()); - } - - size_type policy::peer::total_download() const - { - if (connection != 0) - { - assert(prev_amount_download == 0); - return connection->statistics().total_payload_download(); - } - else - { - return prev_amount_download; - } - } - - size_type policy::peer::total_upload() const - { - if (connection != 0) - { - assert(prev_amount_upload == 0); - return connection->statistics().total_payload_upload(); - } - else - { - return prev_amount_upload; - } - } -} - diff --git a/libtorrent/cpp/session.cpp b/libtorrent/cpp/session.cpp deleted file mode 100755 index 611267427..000000000 --- a/libtorrent/cpp/session.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg, Magnus Jonsson -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 -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/peer_id.hpp" -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/tracker_manager.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/session.hpp" -#include "libtorrent/fingerprint.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/invariant_check.hpp" -#include "libtorrent/file.hpp" -#include "libtorrent/allocate_resources.hpp" -#include "libtorrent/bt_peer_connection.hpp" -#include "libtorrent/ip_filter.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/aux_/session_impl.hpp" -#include "libtorrent/kademlia/dht_tracker.hpp" - -using namespace boost::posix_time; -using boost::shared_ptr; -using boost::weak_ptr; -using boost::bind; -using boost::mutex; -using libtorrent::aux::session_impl; - -namespace libtorrent -{ - - session::session( - fingerprint const& id - , std::pair listen_port_range - , char const* listen_interface) - : m_impl(new session_impl(listen_port_range, id, listen_interface)) - { - // turn off the filename checking in boost.filesystem - using namespace boost::filesystem; - if (path::default_name_check_writable()) - path::default_name_check(no_check); - assert(listen_port_range.first > 0); - assert(listen_port_range.first < listen_port_range.second); -#ifndef NDEBUG - // this test was added after it came to my attention - // that devstudios managed c++ failed to generate - // correct code for boost.function - boost::function0 test = boost::ref(*m_impl); - assert(!test.empty()); -#endif - } - - session::session(fingerprint const& id) - : m_impl(new session_impl(std::make_pair(0, 0), id)) - { -#ifndef NDEBUG - boost::function0 test = boost::ref(*m_impl); - assert(!test.empty()); -#endif - } - - session::~session() - { - assert(m_impl); - // if there is at least one destruction-proxy - // abort the session and let the destructor - // of the proxy to syncronize - if (!m_impl.unique()) - m_impl->abort(); - } - - void session::disable_extensions() - { - m_impl->disable_extensions(); - } - - void session::set_ip_filter(ip_filter const& f) - { - m_impl->set_ip_filter(f); - } - - void session::set_peer_id(peer_id const& id) - { - m_impl->set_peer_id(id); - } - - void session::set_key(int key) - { - m_impl->set_key(key); - } - - void session::enable_extension(extension_index i) - { - m_impl->enable_extension(i); - } - - std::vector session::get_torrents() const - { - return m_impl->get_torrents(); - } - - // if the torrent already exists, this will throw duplicate_torrent - torrent_handle session::add_torrent( - torrent_info const& ti - , boost::filesystem::path const& save_path - , entry const& resume_data - , bool compact_mode - , int block_size) - { - return m_impl->add_torrent(ti, save_path, resume_data - , compact_mode, block_size); - } - - torrent_handle session::add_torrent( - char const* tracker_url - , sha1_hash const& info_hash - , boost::filesystem::path const& save_path - , entry const& e - , bool compact_mode - , int block_size) - { - return m_impl->add_torrent(tracker_url, info_hash, save_path, e - , compact_mode, block_size); - } - - void session::remove_torrent(const torrent_handle& h) - { - m_impl->remove_torrent(h); - } - - bool session::listen_on( - std::pair const& port_range - , const char* net_interface) - { - return m_impl->listen_on(port_range, net_interface); - } - - unsigned short session::listen_port() const - { - return m_impl->listen_port(); - } - - session_status session::status() const - { - return m_impl->status(); - } - -#ifndef TORRENT_DISABLE_DHT - - void session::start_dht(entry const& startup_state) - { - m_impl->start_dht(startup_state); - } - - void session::stop_dht() - { - m_impl->stop_dht(); - } - - void session::set_dht_settings(dht_settings const& settings) - { - m_impl->set_dht_settings(settings); - } - - entry session::dht_state() const - { - return m_impl->dht_state(); - } - - void session::add_dht_node(std::pair const& node) - { - m_impl->add_dht_node(node); - } - - void session::add_dht_router(std::pair const& node) - { - m_impl->add_dht_router(node); - } - -#endif - - bool session::is_listening() const - { - return m_impl->is_listening(); - } - - void session::set_settings(session_settings const& s) - { - m_impl->set_settings(s); - } - - session_settings const& session::settings() - { - return m_impl->settings(); - } - - void session::set_max_uploads(int limit) - { - m_impl->set_max_uploads(limit); - } - - void session::set_max_connections(int limit) - { - m_impl->set_max_connections(limit); - } - - void session::set_max_half_open_connections(int limit) - { - m_impl->set_max_half_open_connections(limit); - } - - void session::set_upload_rate_limit(int bytes_per_second) - { - m_impl->set_upload_rate_limit(bytes_per_second); - } - - void session::set_download_rate_limit(int bytes_per_second) - { - m_impl->set_download_rate_limit(bytes_per_second); - } - - std::auto_ptr session::pop_alert() - { - return m_impl->pop_alert(); - } - - void session::set_severity_level(alert::severity_t s) - { - m_impl->set_severity_level(s); - } - -} - diff --git a/libtorrent/cpp/session_impl.cpp b/libtorrent/cpp/session_impl.cpp deleted file mode 100755 index c8005879a..000000000 --- a/libtorrent/cpp/session_impl.cpp +++ /dev/null @@ -1,1855 +0,0 @@ -/* - -Copyright (c) 2006, Arvid Norberg, Magnus Jonsson -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 -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/peer_id.hpp" -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/tracker_manager.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/session.hpp" -#include "libtorrent/fingerprint.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/invariant_check.hpp" -#include "libtorrent/file.hpp" -#include "libtorrent/allocate_resources.hpp" -#include "libtorrent/bt_peer_connection.hpp" -#include "libtorrent/ip_filter.hpp" -#include "libtorrent/socket.hpp" -#include "libtorrent/aux_/session_impl.hpp" -#include "libtorrent/kademlia/dht_tracker.hpp" - -using namespace boost::posix_time; -using boost::shared_ptr; -using boost::weak_ptr; -using boost::bind; -using boost::mutex; -using libtorrent::aux::session_impl; - -namespace libtorrent { namespace detail -{ - - std::string generate_auth_string(std::string const& user - , std::string const& passwd) - { - if (user.empty()) return std::string(); - return user + ":" + passwd; - } - - - } namespace aux { - // This is the checker thread - // it is looping in an infinite loop - // until the session is aborted. It will - // normally just block in a wait() call, - // waiting for a signal from session that - // there's a new torrent to check. - - void checker_impl::operator()() - { - eh_initializer(); - // if we're currently performing a full file check, - // this is the torrent being processed - boost::shared_ptr processing; - boost::shared_ptr t; - for (;;) - { - // temporary torrent used while checking fastresume data - try - { - t.reset(); - { - boost::mutex::scoped_lock l(m_mutex); - - INVARIANT_CHECK; - - // if the job queue is empty and - // we shouldn't abort - // wait for a signal - if (m_torrents.empty() && !m_abort && !processing) - m_cond.wait(l); - - if (m_abort) - { - // no lock is needed here, because the main thread - // has already been shut down by now - processing.reset(); - t.reset(); - std::for_each(m_torrents.begin(), m_torrents.end() - , boost::bind(&torrent::abort - , boost::bind(&shared_ptr::get - , boost::bind(&piece_checker_data::torrent_ptr, _1)))); - m_torrents.clear(); - std::for_each(m_processing.begin(), m_processing.end() - , boost::bind(&torrent::abort - , boost::bind(&shared_ptr::get - , boost::bind(&piece_checker_data::torrent_ptr, _1)))); - m_processing.clear(); - return; - } - - if (!m_torrents.empty()) - { - t = m_torrents.front(); - if (t->abort) - { - // make sure the locking order is - // consistent to avoid dead locks - // we need to lock the session because closing - // torrents assume to have access to it - l.unlock(); - session_impl::mutex_t::scoped_lock l2(m_ses.m_mutex); - l.lock(); - - t->torrent_ptr->abort(); - m_torrents.pop_front(); - continue; - } - } - } - - if (t) - { - std::string error_msg; - t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file() - , error_msg); - - if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning)) - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - m_ses.m_alerts.post_alert(fastresume_rejected_alert( - t->torrent_ptr->get_handle() - , error_msg)); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << "fastresume data for " - << t->torrent_ptr->torrent_file().name() << " rejected: " - << error_msg << "\n"; -#endif - } - - // clear the resume data now that it has been used - // (the fast resume data is now parsed and stored in t) - t->resume_data = entry(); - bool up_to_date = t->torrent_ptr->check_fastresume(*t); - - if (up_to_date) - { - // lock the session to add the new torrent - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - mutex::scoped_lock l2(m_mutex); - INVARIANT_CHECK; - - assert(m_torrents.front() == t); - - t->torrent_ptr->files_checked(t->unfinished_pieces); - m_torrents.pop_front(); - - // we cannot add the torrent if the session is aborted. - if (!m_ses.is_aborted()) - { - m_ses.m_torrents.insert(std::make_pair(t->info_hash, t->torrent_ptr)); - if (t->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info)) - { - m_ses.m_alerts.post_alert(torrent_finished_alert( - t->torrent_ptr->get_handle() - , "torrent is complete")); - } - - peer_id id; - std::fill(id.begin(), id.end(), 0); - for (std::vector::const_iterator i = t->peers.begin(); - i != t->peers.end(); ++i) - { - t->torrent_ptr->get_policy().peer_from_tracker(*i, id); - } - } - else - { - t->torrent_ptr->abort(); - } - t.reset(); - continue; - } - - // lock the checker while we move the torrent from - // m_torrents to m_processing - { - mutex::scoped_lock l(m_mutex); - assert(m_torrents.front() == t); - - m_torrents.pop_front(); - m_processing.push_back(t); - if (!processing) - { - processing = t; - processing->processing = true; - t.reset(); - } - } - } - } - catch (const std::exception& e) - { - // This will happen if the storage fails to initialize - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - mutex::scoped_lock l2(m_mutex); - - if (m_ses.m_alerts.should_post(alert::fatal)) - { - m_ses.m_alerts.post_alert( - file_error_alert( - t->torrent_ptr->get_handle() - , e.what())); - } - t->torrent_ptr->abort(); - - assert(!m_torrents.empty()); - m_torrents.pop_front(); - } - catch(...) - { -#ifndef NDEBUG - std::cerr << "error while checking resume data\n"; -#endif - mutex::scoped_lock l(m_mutex); - assert(!m_torrents.empty()); - m_torrents.pop_front(); - assert(false); - } - - if (!processing) continue; - - try - { - assert(processing); - - float finished = false; - float progress = 0.f; - boost::tie(finished, progress) = processing->torrent_ptr->check_files(); - - { - mutex::scoped_lock l(m_mutex); - - INVARIANT_CHECK; - - processing->progress = progress; - if (processing->abort) - { - assert(!m_processing.empty()); - assert(m_processing.front() == processing); - - processing->torrent_ptr->abort(); - - processing.reset(); - m_processing.pop_front(); - if (!m_processing.empty()) - { - processing = m_processing.front(); - processing->processing = true; - } - continue; - } - } - if (finished) - { - // lock the session to add the new torrent - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - mutex::scoped_lock l2(m_mutex); - - INVARIANT_CHECK; - - assert(!m_processing.empty()); - assert(m_processing.front() == processing); - - // TODO: factor out the adding of torrents to the session - // and to the checker thread to avoid duplicating the - // check for abortion. - if (!m_ses.is_aborted()) - { - processing->torrent_ptr->files_checked(processing->unfinished_pieces); - m_ses.m_torrents.insert(std::make_pair( - processing->info_hash, processing->torrent_ptr)); - if (processing->torrent_ptr->is_seed() - && m_ses.m_alerts.should_post(alert::info)) - { - m_ses.m_alerts.post_alert(torrent_finished_alert( - processing->torrent_ptr->get_handle() - , "torrent is complete")); - } - - peer_id id; - std::fill(id.begin(), id.end(), 0); - for (std::vector::const_iterator i = processing->peers.begin(); - i != processing->peers.end(); ++i) - { - processing->torrent_ptr->get_policy().peer_from_tracker(*i, id); - } - } - else - { - processing->torrent_ptr->abort(); - } - processing.reset(); - m_processing.pop_front(); - if (!m_processing.empty()) - { - processing = m_processing.front(); - processing->processing = true; - } - } - } - catch(std::exception const& e) - { - // This will happen if the storage fails to initialize - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - mutex::scoped_lock l2(m_mutex); - - if (m_ses.m_alerts.should_post(alert::fatal)) - { - m_ses.m_alerts.post_alert( - file_error_alert( - processing->torrent_ptr->get_handle() - , e.what())); - } - assert(!m_processing.empty()); - - processing->torrent_ptr->abort(); - - processing.reset(); - m_processing.pop_front(); - if (!m_processing.empty()) - { - processing = m_processing.front(); - processing->processing = true; - } - } - catch(...) - { -#ifndef NDEBUG - std::cerr << "error while checking files\n"; -#endif - mutex::scoped_lock l(m_mutex); - assert(!m_processing.empty()); - - processing.reset(); - m_processing.pop_front(); - if (!m_processing.empty()) - { - processing = m_processing.front(); - processing->processing = true; - } - - assert(false); - } - } - } - - aux::piece_checker_data* checker_impl::find_torrent(sha1_hash const& info_hash) - { - INVARIANT_CHECK; - for (std::deque >::iterator i - = m_torrents.begin(); i != m_torrents.end(); ++i) - { - if ((*i)->info_hash == info_hash) return i->get(); - } - for (std::deque >::iterator i - = m_processing.begin(); i != m_processing.end(); ++i) - { - - if ((*i)->info_hash == info_hash) return i->get(); - } - - return 0; - } - - void checker_impl::remove_torrent(sha1_hash const& info_hash) - { - INVARIANT_CHECK; - for (std::deque >::iterator i - = m_torrents.begin(); i != m_torrents.end(); ++i) - { - if ((*i)->info_hash == info_hash) - { - assert((*i)->processing == false); - m_torrents.erase(i); - return; - } - } - for (std::deque >::iterator i - = m_processing.begin(); i != m_processing.end(); ++i) - { - if ((*i)->info_hash == info_hash) - { - assert((*i)->processing == false); - m_processing.erase(i); - return; - } - } - - assert(false); - } - -#ifndef NDEBUG - void checker_impl::check_invariant() const - { - for (std::deque >::const_iterator i - = m_torrents.begin(); i != m_torrents.end(); ++i) - { - assert(*i); - assert((*i)->torrent_ptr); - } - for (std::deque >::const_iterator i - = m_processing.begin(); i != m_processing.end(); ++i) - { - assert(*i); - assert((*i)->torrent_ptr); - } - } -#endif - - session_impl::session_impl( - std::pair listen_port_range - , fingerprint const& cl_fprint - , char const* listen_interface) - : m_tracker_manager(m_settings) - , m_listen_port_range(listen_port_range) - , m_listen_interface(address::from_string(listen_interface), listen_port_range.first) - , m_abort(false) - , m_upload_rate(-1) - , m_download_rate(-1) - , m_max_uploads(-1) - , m_max_connections(-1) - , m_half_open_limit(-1) - , m_incoming_connection(false) - , m_last_tick(microsec_clock::universal_time()) - , m_timer(m_selector) - , m_checker_impl(*this) - { - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - m_logger = create_log("main_session", false); - using boost::posix_time::second_clock; - using boost::posix_time::to_simple_string; - (*m_logger) << to_simple_string(second_clock::universal_time()) << "\n"; -#endif - std::fill(m_extension_enabled, m_extension_enabled - + num_supported_extensions, true); - // ---- generate a peer id ---- - - std::srand((unsigned int)std::time(0)); - - m_key = rand() + (rand() << 15) + (rand() << 30); - std::string print = cl_fprint.to_string(); - assert(print.length() <= 20); - - // the client's fingerprint - std::copy( - print.begin() - , print.begin() + print.length() - , m_peer_id.begin()); - - // http-accepted characters: - static char const printable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz-_.!~*'()"; - - // the random number - for (unsigned char* i = m_peer_id.begin() + print.length(); - i != m_peer_id.end(); ++i) - { - *i = printable[rand() % (sizeof(printable)-1)]; - } - - m_timer.expires_from_now(seconds(1)); - m_timer.async_wait(bind(&session_impl::second_tick, this, _1)); - - m_thread.reset(new boost::thread(boost::ref(*this))); - m_checker_thread.reset(new boost::thread(boost::ref(m_checker_impl))); - } - -#ifndef TORRENT_DISABLE_DHT - void session_impl::add_dht_node(udp::endpoint n) - { - if (m_dht) m_dht->add_node(n); - } -#endif - - void session_impl::abort() - { - mutex_t::scoped_lock l(m_mutex); - assert(!m_abort); - // abort the main thread - m_abort = true; - m_selector.interrupt(); - l.unlock(); - - mutex::scoped_lock l2(m_checker_impl.m_mutex); - // abort the checker thread - m_checker_impl.m_abort = true; - } - - void session_impl::set_ip_filter(ip_filter const& f) - { - mutex_t::scoped_lock l(m_mutex); - m_ip_filter = f; - - // Close connections whose endpoint is filtered - // by the new ip-filter - for (session_impl::connection_map::iterator i - = m_connections.begin(); i != m_connections.end();) - { - tcp::endpoint sender = i->first->remote_endpoint(); - if (m_ip_filter.access(sender.address()) & ip_filter::blocked) - { -#if defined(TORRENT_VERBOSE_LOGGING) - (*i->second->m_logger) << "*** CONNECTION FILTERED\n"; -#endif - session_impl::connection_map::iterator j = i; - ++i; - j->second->disconnect(); - } - else ++i; - } - } - - bool session_impl::extensions_enabled() const - { - const int n = num_supported_extensions; - return std::find(m_extension_enabled - , m_extension_enabled + n, true) != m_extension_enabled + n; - } - - void session_impl::set_settings(session_settings const& s) - { - mutex_t::scoped_lock l(m_mutex); - m_settings = s; - // replace all occurances of '\n' with ' '. - std::string::iterator i = m_settings.user_agent.begin(); - while ((i = std::find(i, m_settings.user_agent.end(), '\n')) - != m_settings.user_agent.end()) - *i = ' '; - } - - void session_impl::open_listen_port() - { - try - { - // create listener socket - m_listen_socket = boost::shared_ptr(new socket_acceptor(m_selector)); - - for(;;) - { - try - { - m_listen_socket->open(asio::ip::tcp::v4()); - m_listen_socket->bind(m_listen_interface); - m_listen_socket->listen(); - break; - } - catch (asio::error& e) - { - // TODO: make sure this is correct - if (e.code() == asio::error::host_not_found) - { - if (m_alerts.should_post(alert::fatal)) - { - std::string msg = "cannot listen on the given interface '" - + m_listen_interface.address().to_string() + "'"; - m_alerts.post_alert(listen_failed_alert(msg)); - } -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - std::string msg = "cannot listen on the given interface '" - + m_listen_interface.address().to_string() + "'"; - (*m_logger) << msg << "\n"; -#endif - assert(m_listen_socket.unique()); - m_listen_socket.reset(); - break; - } - m_listen_interface.port(m_listen_interface.port() + 1); - if (m_listen_interface.port() > m_listen_port_range.second) - { - std::stringstream msg; - msg << "none of the ports in the range [" - << m_listen_port_range.first - << ", " << m_listen_port_range.second - << "] could be opened for listening"; - m_alerts.post_alert(listen_failed_alert(msg.str())); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << msg.str() << "\n"; -#endif - m_listen_socket.reset(); - break; - } - } - } - } - catch (asio::error& e) - { - if (m_alerts.should_post(alert::fatal)) - { - m_alerts.post_alert(listen_failed_alert( - std::string("failed to open listen port") + e.what())); - } - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (m_listen_socket) - { - (*m_logger) << "listening on port: " << m_listen_interface.port() << "\n"; - } -#endif - if (m_listen_socket) async_accept(); - } - - void session_impl::process_connection_queue() - { - while (!m_connection_queue.empty()) - { - if ((int)m_half_open.size() >= m_half_open_limit - && m_half_open_limit > 0) - return; - - connection_queue::value_type c = m_connection_queue.front(); - - try - { - m_connection_queue.pop_front(); - assert(c->associated_torrent().lock().get()); - c->connect(); - m_half_open.insert(std::make_pair(c->get_socket(), c)); - } - catch (std::exception& e) - { - c->disconnect(); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << "connect failed [" << c->remote() << "]: " - << e.what() << "\n"; -#endif - } - } - } - - void session_impl::async_accept() - { - shared_ptr c(new stream_socket(m_selector)); - m_listen_socket->async_accept(*c - , bind(&session_impl::on_incoming_connection, this, c - , weak_ptr(m_listen_socket), _1)); - } - - void session_impl::on_incoming_connection(shared_ptr const& s - , weak_ptr const& listen_socket, asio::error const& e) try - { - if (listen_socket.expired()) - return; - - if (e == asio::error::operation_aborted) - return; - - mutex_t::scoped_lock l(m_mutex); - assert(listen_socket.lock() == m_listen_socket); - - if (m_abort) return; - - async_accept(); - if (e) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - std::string msg = "error accepting connection on '" - + m_listen_interface.address().to_string() + "'"; - (*m_logger) << msg << "\n"; -#endif - assert(m_listen_socket.unique()); - return; - } - - // we got a connection request! - m_incoming_connection = true; - tcp::endpoint endp = s->remote_endpoint(); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << endp << " <== INCOMING CONNECTION\n"; -#endif - if (m_ip_filter.access(endp.address().to_v4()) & ip_filter::blocked) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << "filtered blocked ip\n"; -#endif - // TODO: issue an info-alert when an ip is blocked!! - return; - } - - boost::intrusive_ptr c( - new bt_peer_connection(*this, s)); -#ifndef NDEBUG - c->m_in_constructor = false; -#endif - - m_connections.insert(std::make_pair(s, c)); - } - catch (std::exception& exc) - { -#ifndef NDEBUG - std::string err = exc.what(); -#endif - } - - void session_impl::connection_failed(boost::shared_ptr const& s - , tcp::endpoint const& a, char const* message) -#ifndef NDEBUG - try -#endif - { - mutex_t::scoped_lock l(m_mutex); - - connection_map::iterator p = m_connections.find(s); - - // the connection may have been disconnected in the receive or send phase - if (p != m_connections.end()) - { - if (m_alerts.should_post(alert::debug)) - { - m_alerts.post_alert( - peer_error_alert( - a - , p->second->pid() - , message)); - } - -#if defined(TORRENT_VERBOSE_LOGGING) - (*p->second->m_logger) << "*** CONNECTION FAILED " << message << "\n"; -#endif - p->second->set_failed(); - p->second->disconnect(); - } - else - { - // the error was not in one of the connected - // conenctions. Look among the half-open ones. - p = m_half_open.find(s); - if (p != m_half_open.end()) - { - if (m_alerts.should_post(alert::debug)) - { - m_alerts.post_alert( - peer_error_alert( - a - , p->second->pid() - , message)); - } -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_logger) << "CLOSED: " << a.address().to_string() - << " " << message << "\n"; -#endif - p->second->set_failed(); - p->second->disconnect(); - } - } - } -#ifndef NDEBUG - catch (...) - { - assert(false); - }; -#endif - - void session_impl::close_connection(boost::intrusive_ptr const& p) - { - mutex_t::scoped_lock l(m_mutex); - - assert(p->is_disconnecting()); - - if (p->is_connecting()) - { - assert(p->is_local()); - assert(m_connections.find(p->get_socket()) == m_connections.end()); - // Since this peer is still connecting, will not be - // in the list of completed connections. - connection_map::iterator i = m_half_open.find(p->get_socket()); - if (i == m_half_open.end()) - { - // this connection is not in the half-open list, so it - // has to be in the queue, waiting to be connected. - connection_queue::iterator j = std::find( - m_connection_queue.begin(), m_connection_queue.end(), p); - - // if this connection was closed while being connected - // it has been removed from the connection queue and - // not yet put into the half-open queue. - if (j != m_connection_queue.end()) - m_connection_queue.erase(j); - } - else - { - m_half_open.erase(i); - process_connection_queue(); - } - } - else - { - assert(m_half_open.find(p->get_socket()) == m_half_open.end()); - assert(std::find(m_connection_queue.begin() - , m_connection_queue.end(), p) == m_connection_queue.end()); - connection_map::iterator i = m_connections.find(p->get_socket()); -// assert (i != m_connections.end()); - if (i != m_connections.end()) - m_connections.erase(i); - } - } - - void session_impl::set_peer_id(peer_id const& id) - { - mutex_t::scoped_lock l(m_mutex); - m_peer_id = id; - } - - void session_impl::set_key(int key) - { - mutex_t::scoped_lock l(m_mutex); - m_key = key; - } - - void session_impl::second_tick(asio::error const& e) try - { - session_impl::mutex_t::scoped_lock l(m_mutex); - - if (e) - { -#if defined(TORRENT_LOGGING) - (*m_logger) << "*** SECOND TIMER FAILED " << e.what() << "\n"; -#endif - m_abort = true; - m_selector.interrupt(); - return; - } - - if (m_abort) return; - float tick_interval = (microsec_clock::universal_time() - - m_last_tick).total_milliseconds() / 1000.f; - m_last_tick = microsec_clock::universal_time(); - - m_timer.expires_from_now(seconds(1)); - m_timer.async_wait(bind(&session_impl::second_tick, this, _1)); - - // do the second_tick() on each connection - // this will update their statistics (download and upload speeds) - // also purge sockets that have timed out - // and keep sockets open by keeping them alive. - for (connection_map::iterator i = m_connections.begin(); - i != m_connections.end();) - { - // we need to do like this because j->second->disconnect() will - // erase the connection from the map we're iterating - connection_map::iterator j = i; - ++i; - // if this socket has timed out - // close it. - peer_connection& c = *j->second; - if (c.has_timed_out()) - { - if (m_alerts.should_post(alert::debug)) - { - m_alerts.post_alert( - peer_error_alert( - c.remote() - , c.pid() - , "connection timed out")); - } -#if defined(TORRENT_VERBOSE_LOGGING) - (*c.m_logger) << "*** CONNECTION TIMED OUT\n"; -#endif - - c.set_failed(); - c.disconnect(); - continue; - } - - c.keep_alive(); - } - - // check each torrent for tracker updates - // TODO: do this in a timer-event in each torrent instead - for (std::map >::iterator i - = m_torrents.begin(); i != m_torrents.end();) - { - torrent& t = *i->second; - assert(!t.is_aborted()); - if (t.should_request()) - { - tracker_request req = t.generate_tracker_request(); - req.listen_port = m_listen_interface.port(); - req.key = m_key; - m_tracker_manager.queue_request(m_selector, req, t.tracker_login() - , i->second); - - if (m_alerts.should_post(alert::info)) - { - m_alerts.post_alert( - tracker_announce_alert( - t.get_handle(), "tracker announce")); - } - } - - // second_tick() will set the used upload quota - t.second_tick(m_stat, tick_interval); - ++i; - } - - m_stat.second_tick(tick_interval); - - // distribute the maximum upload rate among the torrents - - assert(m_upload_rate >= -1); - assert(m_download_rate >= -1); - assert(m_max_uploads >= -1); - assert(m_max_connections >= -1); - - allocate_resources(m_upload_rate == -1 - ? std::numeric_limits::max() - : int(m_upload_rate * tick_interval) - , m_torrents - , &torrent::m_ul_bandwidth_quota); - - allocate_resources(m_download_rate == -1 - ? std::numeric_limits::max() - : int(m_download_rate * tick_interval) - , m_torrents - , &torrent::m_dl_bandwidth_quota); - - allocate_resources(m_max_uploads == -1 - ? std::numeric_limits::max() - : m_max_uploads - , m_torrents - , &torrent::m_uploads_quota); - - allocate_resources(m_max_connections == -1 - ? std::numeric_limits::max() - : m_max_connections - , m_torrents - , &torrent::m_connections_quota); - - for (std::map >::iterator i - = m_torrents.begin(); i != m_torrents.end(); ++i) - { -#ifndef NDEBUG - i->second->check_invariant(); -#endif - i->second->distribute_resources(); - } - } - catch (std::exception& exc) - { -#ifndef NDEBUG - std::string err = exc.what(); -#endif - }; // msvc 7.1 seems to require this - - void session_impl::connection_completed( - boost::intrusive_ptr const& p) -#ifndef NDEBUG - try -#endif - { - mutex_t::scoped_lock l(m_mutex); - - if (m_abort) return; - - connection_map::iterator i = m_half_open.find(p->get_socket()); - - m_connections.insert(std::make_pair(p->get_socket(), p)); - if (i != m_half_open.end()) m_half_open.erase(i); - process_connection_queue(); - } -#ifndef NDEBUG - catch (std::exception& e) - { - assert(false); - }; -#endif - - void session_impl::operator()() - { - eh_initializer(); - - if (m_listen_port_range.first != 0 && m_listen_port_range.second != 0) - { - session_impl::mutex_t::scoped_lock l(m_mutex); - open_listen_port(); - } - - boost::posix_time::ptime timer = second_clock::universal_time(); - - do - { - try - { - m_selector.run(); - assert(m_abort == true); - } - catch (std::exception& e) - { - #ifndef NDEBUG - std::cerr << e.what() << "\n"; - std::string err = e.what(); - #endif - assert(false); - } - } - while (!m_abort); - - deadline_timer tracker_timer(m_selector); - - session_impl::mutex_t::scoped_lock l(m_mutex); - - m_tracker_manager.abort_all_requests(); - for (std::map >::iterator i = - m_torrents.begin(); i != m_torrents.end(); ++i) - { - i->second->abort(); - if (!i->second->is_paused() || i->second->should_request()) - { - tracker_request req = i->second->generate_tracker_request(); - req.listen_port = m_listen_interface.port(); - req.key = m_key; - std::string login = i->second->tracker_login(); - m_tracker_manager.queue_request(m_selector, req, login); - } - } - - ptime start(microsec_clock::universal_time()); - l.unlock(); - - while (microsec_clock::universal_time() - start < seconds( - m_settings.stop_tracker_timeout) - && !m_tracker_manager.empty()) - { - tracker_timer.expires_from_now(boost::posix_time::milliseconds(100)); - tracker_timer.async_wait(bind(&demuxer::interrupt, &m_selector)); - - m_selector.reset(); - m_selector.run(); - } - - l.lock(); - assert(m_abort); - m_abort = true; - - while (!m_connections.empty()) - m_connections.begin()->second->disconnect(); - - while (!m_half_open.empty()) - m_half_open.begin()->second->disconnect(); - - m_connection_queue.clear(); - -#ifndef NDEBUG - for (torrent_map::iterator i = m_torrents.begin(); - i != m_torrents.end(); ++i) - { - assert(i->second->num_peers() == 0); - } -#endif - - m_torrents.clear(); - - assert(m_torrents.empty()); - assert(m_connections.empty()); - } - - - // the return value from this function is valid only as long as the - // session is locked! - boost::weak_ptr session_impl::find_torrent(sha1_hash const& info_hash) - { - std::map >::iterator i - = m_torrents.find(info_hash); -#ifndef NDEBUG - for (std::map >::iterator j - = m_torrents.begin(); j != m_torrents.end(); ++j) - { - torrent* p = boost::get_pointer(j->second); - assert(p); - } -#endif - if (i != m_torrents.end()) return i->second; - return boost::weak_ptr(); - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - boost::shared_ptr session_impl::create_log(std::string const& name, bool append) - { - // current options are file_logger, cout_logger and null_logger - return boost::shared_ptr(new logger(name + ".log", append)); - } -#endif - - void session_impl::disable_extensions() - { - mutex_t::scoped_lock l(m_mutex); - std::fill(m_extension_enabled, m_extension_enabled - + num_supported_extensions, false); - } - - void session_impl::enable_extension(extension_index i) - { - assert(i >= 0); - assert(i < num_supported_extensions); - mutex_t::scoped_lock l(m_mutex); - m_extension_enabled[i] = true; - } - - std::vector session_impl::get_torrents() - { - mutex_t::scoped_lock l(m_mutex); - mutex::scoped_lock l2(m_checker_impl.m_mutex); - std::vector ret; - for (std::deque >::iterator i - = m_checker_impl.m_torrents.begin() - , end(m_checker_impl.m_torrents.end()); i != end; ++i) - { - if ((*i)->abort) continue; - ret.push_back(torrent_handle(this, &m_checker_impl - , (*i)->info_hash)); - } - - for (session_impl::torrent_map::iterator i - = m_torrents.begin(), end(m_torrents.end()); - i != end; ++i) - { - if (i->second->is_aborted()) continue; - ret.push_back(torrent_handle(this, &m_checker_impl - , i->first)); - } - return ret; - } - - torrent_handle session_impl::add_torrent( - torrent_info const& ti - , boost::filesystem::path const& save_path - , entry const& resume_data - , bool compact_mode - , int block_size) - { - // make sure the block_size is an even power of 2 -#ifndef NDEBUG - for (int i = 0; i < 32; ++i) - { - if (block_size & (1 << i)) - { - assert((block_size & ~(1 << i)) == 0); - break; - } - } -#endif - - assert(!save_path.empty()); - - if (ti.begin_files() == ti.end_files()) - throw std::runtime_error("no files in torrent"); - - // lock the session and the checker thread (the order is important!) - mutex_t::scoped_lock l(m_mutex); - mutex::scoped_lock l2(m_checker_impl.m_mutex); - - if (is_aborted()) - throw std::runtime_error("session is closing"); - - // is the torrent already active? - if (!find_torrent(ti.info_hash()).expired()) - throw duplicate_torrent(); - - // is the torrent currently being checked? - if (m_checker_impl.find_torrent(ti.info_hash())) - throw duplicate_torrent(); - - // create the torrent and the data associated with - // the checker thread and store it before starting - // the thread - boost::shared_ptr torrent_ptr( - new torrent(*this, m_checker_impl, ti, save_path - , m_listen_interface, compact_mode, block_size - , settings())); - - boost::shared_ptr d( - new aux::piece_checker_data); - d->torrent_ptr = torrent_ptr; - d->save_path = save_path; - d->info_hash = ti.info_hash(); - d->resume_data = resume_data; - -#ifndef TORRENT_DISABLE_DHT - if (m_dht) - { - torrent_info::nodes_t const& nodes = ti.nodes(); - std::for_each(nodes.begin(), nodes.end(), bind( - (void(dht::dht_tracker::*)(std::pair const&)) - &dht::dht_tracker::add_node - , boost::ref(m_dht), _1)); - } -#endif - - // add the torrent to the queue to be checked - m_checker_impl.m_torrents.push_back(d); - // and notify the thread that it got another - // job in its queue - m_checker_impl.m_cond.notify_one(); - - return torrent_handle(this, &m_checker_impl, ti.info_hash()); - } - - torrent_handle session_impl::add_torrent( - char const* tracker_url - , sha1_hash const& info_hash - , boost::filesystem::path const& save_path - , entry const& - , bool compact_mode - , int block_size) - { - // make sure the block_size is an even power of 2 -#ifndef NDEBUG - for (int i = 0; i < 32; ++i) - { - if (block_size & (1 << i)) - { - assert((block_size & ~(1 << i)) == 0); - break; - } - } -#endif - - // TODO: support resume data in this case - assert(!save_path.empty()); - { - // lock the checker_thread - mutex::scoped_lock l(m_checker_impl.m_mutex); - - // is the torrent currently being checked? - if (m_checker_impl.find_torrent(info_hash)) - throw duplicate_torrent(); - } - - // lock the session - session_impl::mutex_t::scoped_lock l(m_mutex); - - // the metadata extension has to be enabled for this to work - assert(m_extension_enabled - [extended_metadata_message]); - - // is the torrent already active? - if (!find_torrent(info_hash).expired()) - throw duplicate_torrent(); - - // you cannot add new torrents to a session that is closing down - assert(!is_aborted()); - - // create the torrent and the data associated with - // the checker thread and store it before starting - // the thread - boost::shared_ptr torrent_ptr( - new torrent(*this, m_checker_impl, tracker_url, info_hash, save_path - , m_listen_interface, compact_mode, block_size - , settings())); - - m_torrents.insert( - std::make_pair(info_hash, torrent_ptr)).first; - - return torrent_handle(this, &m_checker_impl, info_hash); - } - - void session_impl::remove_torrent(const torrent_handle& h) - { - if (h.m_ses != this) return; - assert(h.m_chk == &m_checker_impl || h.m_chk == 0); - assert(h.m_ses != 0); - - mutex_t::scoped_lock l(m_mutex); - session_impl::torrent_map::iterator i = - m_torrents.find(h.m_info_hash); - if (i != m_torrents.end()) - { - torrent& t = *i->second; - t.abort(); - - if (!t.is_paused() || t.should_request()) - { - tracker_request req = t.generate_tracker_request(); - assert(req.event == tracker_request::stopped); - req.listen_port = m_listen_interface.port(); - req.key = m_key; - m_tracker_manager.queue_request(m_selector, req - , t.tracker_login()); - - if (m_alerts.should_post(alert::info)) - { - m_alerts.post_alert( - tracker_announce_alert( - t.get_handle(), "tracker announce, event=stopped")); - } - } -#ifndef NDEBUG - sha1_hash i_hash = t.torrent_file().info_hash(); -#endif - m_torrents.erase(i); - assert(m_torrents.find(i_hash) == m_torrents.end()); - return; - } - l.unlock(); - - if (h.m_chk) - { - mutex::scoped_lock l(m_checker_impl.m_mutex); - - aux::piece_checker_data* d = m_checker_impl.find_torrent(h.m_info_hash); - if (d != 0) - { - if (d->processing) d->abort = true; - else m_checker_impl.remove_torrent(h.m_info_hash); - return; - } - } - } - - bool session_impl::listen_on( - std::pair const& port_range - , const char* net_interface) - { - session_impl::mutex_t::scoped_lock l(m_mutex); - - tcp::endpoint new_interface; - if (net_interface && std::strlen(net_interface) > 0) - new_interface = tcp::endpoint(address::from_string(net_interface), port_range.first); - else - new_interface = tcp::endpoint(address(), port_range.first); - - m_listen_port_range = port_range; - - // if the interface is the same and the socket is open - // don't do anything - if (new_interface == m_listen_interface - && m_listen_socket) return true; - - if (m_listen_socket) - m_listen_socket.reset(); - -#ifndef TORRENT_DISABLE_DHT - if (m_listen_interface.address() != new_interface.address() - && m_dht) - { - // the listen interface changed, rebind the dht listen socket as well - m_dht->rebind(new_interface.address() - , m_dht_settings.service_port); - } -#endif - - m_incoming_connection = false; - m_listen_interface = new_interface; - - open_listen_port(); - return m_listen_socket; - } - - unsigned short session_impl::listen_port() const - { - mutex_t::scoped_lock l(m_mutex); - return m_listen_interface.port(); - } - - session_status session_impl::status() const - { - mutex_t::scoped_lock l(m_mutex); - session_status s; - s.has_incoming_connections = m_incoming_connection; - s.num_peers = (int)m_connections.size(); - - s.download_rate = m_stat.download_rate(); - s.upload_rate = m_stat.upload_rate(); - - s.payload_download_rate = m_stat.download_payload_rate(); - s.payload_upload_rate = m_stat.upload_payload_rate(); - - s.total_download = m_stat.total_protocol_download() - + m_stat.total_payload_download(); - - s.total_upload = m_stat.total_protocol_upload() - + m_stat.total_payload_upload(); - - s.total_payload_download = m_stat.total_payload_download(); - s.total_payload_upload = m_stat.total_payload_upload(); - -#ifndef TORRENT_DISABLE_DHT - if (m_dht) - { - m_dht->dht_status(s); - } - else - { - s.m_dht_nodes = 0; - s.m_dht_node_cache = 0; - s.m_dht_torrents = 0; - } -#endif - - return s; - } - -#ifndef TORRENT_DISABLE_DHT - - void session_impl::start_dht(entry const& startup_state) - { - mutex_t::scoped_lock l(m_mutex); - m_dht.reset(new dht::dht_tracker(m_selector - , m_dht_settings, m_listen_interface.address() - , startup_state)); - } - - void session_impl::stop_dht() - { - mutex_t::scoped_lock l(m_mutex); - m_dht.reset(); - } - - void session_impl::set_dht_settings(dht_settings const& settings) - { - mutex_t::scoped_lock l(m_mutex); - if (settings.service_port != m_dht_settings.service_port - && m_dht) - { - m_dht->rebind(m_listen_interface.address() - , settings.service_port); - } - m_dht_settings = settings; - } - - entry session_impl::dht_state() const - { - assert(m_dht); - mutex_t::scoped_lock l(m_mutex); - return m_dht->state(); - } - - void session_impl::add_dht_node(std::pair const& node) - { - assert(m_dht); - mutex_t::scoped_lock l(m_mutex); - m_dht->add_node(node); - } - - void session_impl::add_dht_router(std::pair const& node) - { - assert(m_dht); - mutex_t::scoped_lock l(m_mutex); - m_dht->add_router_node(node); - } - -#endif - - - void session_impl::set_download_rate_limit(int bytes_per_second) - { - assert(bytes_per_second > 0 || bytes_per_second == -1); - mutex_t::scoped_lock l(m_mutex); - m_download_rate = bytes_per_second; - } - bool session_impl::is_listening() const - { - mutex_t::scoped_lock l(m_mutex); - return m_listen_socket; - } - - session_impl::~session_impl() - { - { - // lock the main thread and abort it - mutex_t::scoped_lock l(m_mutex); - m_abort = true; - m_selector.interrupt(); - } - m_thread->join(); - - // it's important that the main thread is closed completely before - // the checker thread is terminated. Because all the connections - // have to be closed and removed from the torrents before they - // can be destructed. (because the weak pointers in the - // peer_connections will be invalidated when the torrents are - // destructed and then the invariant will be broken). - - { - mutex::scoped_lock l(m_checker_impl.m_mutex); - // abort the checker thread - m_checker_impl.m_abort = true; - - // abort the currently checking torrent - if (!m_checker_impl.m_torrents.empty()) - { - m_checker_impl.m_torrents.front()->abort = true; - } - m_checker_impl.m_cond.notify_one(); - } - - m_checker_thread->join(); - - assert(m_torrents.empty()); - assert(m_connections.empty()); - } - - void session_impl::set_max_uploads(int limit) - { - assert(limit > 0 || limit == -1); - mutex_t::scoped_lock l(m_mutex); - m_max_uploads = limit; - } - - void session_impl::set_max_connections(int limit) - { - assert(limit > 0 || limit == -1); - mutex_t::scoped_lock l(m_mutex); - m_max_connections = limit; - } - - void session_impl::set_max_half_open_connections(int limit) - { - assert(limit > 0 || limit == -1); - mutex_t::scoped_lock l(m_mutex); - m_half_open_limit = limit; - } - - void session_impl::set_upload_rate_limit(int bytes_per_second) - { - assert(bytes_per_second > 0 || bytes_per_second == -1); - mutex_t::scoped_lock l(m_mutex); - m_upload_rate = bytes_per_second; - } - - std::auto_ptr session_impl::pop_alert() - { - mutex_t::scoped_lock l(m_mutex); - if (m_alerts.pending()) - return m_alerts.get(); - return std::auto_ptr(0); - } - - void session_impl::set_severity_level(alert::severity_t s) - { - mutex_t::scoped_lock l(m_mutex); - m_alerts.set_severity(s); - } - -#ifndef NDEBUG - void session_impl::check_invariant(const char *place) - { - assert(place); - - for (connection_map::iterator i = m_half_open.begin(); - i != m_half_open.end(); ++i) - { - assert(i->second->is_connecting()); - } - - for (connection_map::iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - assert(i->second); - assert(!i->second->is_connecting()); - if (i->second->is_connecting()) - { - std::ofstream error_log("error.log", std::ios_base::app); - boost::intrusive_ptr p = i->second; - error_log << "peer_connection::is_connecting() " << p->is_connecting() << "\n"; - error_log << "peer_connection::can_write() " << p->can_write() << "\n"; - error_log << "peer_connection::can_read() " << p->can_read() << "\n"; - error_log << "peer_connection::ul_quota_left " << p->m_ul_bandwidth_quota.left() << "\n"; - error_log << "peer_connection::dl_quota_left " << p->m_dl_bandwidth_quota.left() << "\n"; - error_log << "peer_connection::m_ul_bandwidth_quota.given " << p->m_ul_bandwidth_quota.given << "\n"; - error_log << "peer_connection::get_peer_id " << p->pid() << "\n"; - error_log << "place: " << place << "\n"; - error_log.flush(); - assert(false); - } - - boost::shared_ptr t = i->second->associated_torrent().lock(); - - if (t) - { - assert(t->get_policy().has_connection(boost::get_pointer(i->second))); - } - } - } -#endif - - void piece_checker_data::parse_resume_data( - const entry& resume_data - , const torrent_info& info - , std::string& error) - { - // if we don't have any resume data, return - if (resume_data.type() == entry::undefined_t) return; - - entry rd = resume_data; - - try - { - if (rd["file-format"].string() != "libtorrent resume file") - { - error = "missing file format tag"; - return; - } - - if (rd["file-version"].integer() > 1) - { - error = "incompatible file version " - + boost::lexical_cast(rd["file-version"].integer()); - return; - } - - // verify info_hash - const std::string &hash = rd["info-hash"].string(); - std::string real_hash((char*)info.info_hash().begin(), (char*)info.info_hash().end()); - if (hash != real_hash) - { - error = "mismatching info-hash: " + hash; - return; - } - - // the peers - - if (rd.find_key("peers")) - { - entry::list_type& peer_list = rd["peers"].list(); - - std::vector tmp_peers; - tmp_peers.reserve(peer_list.size()); - for (entry::list_type::iterator i = peer_list.begin(); - i != peer_list.end(); ++i) - { - tcp::endpoint a( - address::from_string((*i)["ip"].string()) - , (unsigned short)(*i)["port"].integer()); - tmp_peers.push_back(a); - } - - peers.swap(tmp_peers); - } - - // read piece map - const entry::list_type& slots = rd["slots"].list(); - if ((int)slots.size() > info.num_pieces()) - { - error = "file has more slots than torrent (slots: " - + boost::lexical_cast(slots.size()) + " size: " - + boost::lexical_cast(info.num_pieces()) + " )"; - return; - } - - std::vector tmp_pieces; - tmp_pieces.reserve(slots.size()); - for (entry::list_type::const_iterator i = slots.begin(); - i != slots.end(); ++i) - { - int index = (int)i->integer(); - if (index >= info.num_pieces() || index < -2) - { - error = "too high index number in slot map (index: " - + boost::lexical_cast(index) + " size: " - + boost::lexical_cast(info.num_pieces()) + ")"; - return; - } - tmp_pieces.push_back(index); - } - - // only bother to check the partial pieces if we have the same block size - // as in the fast resume data. If the blocksize has changed, then throw - // away all partial pieces. - std::vector tmp_unfinished; - int num_blocks_per_piece = (int)rd["blocks per piece"].integer(); - if (num_blocks_per_piece == info.piece_length() / torrent_ptr->block_size()) - { - // the unfinished pieces - - entry::list_type& unfinished = rd["unfinished"].list(); - - tmp_unfinished.reserve(unfinished.size()); - for (entry::list_type::iterator i = unfinished.begin(); - i != unfinished.end(); ++i) - { - piece_picker::downloading_piece p; - - p.index = (int)(*i)["piece"].integer(); - if (p.index < 0 || p.index >= info.num_pieces()) - { - error = "invalid piece index in unfinished piece list (index: " - + boost::lexical_cast(p.index) + " size: " - + boost::lexical_cast(info.num_pieces()) + ")"; - return; - } - - const std::string& bitmask = (*i)["bitmask"].string(); - - const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1); - if ((int)bitmask.size() != num_bitmask_bytes) - { - error = "invalid size of bitmask (" + boost::lexical_cast(bitmask.size()) + ")"; - return; - } - for (int j = 0; j < num_bitmask_bytes; ++j) - { - unsigned char bits = bitmask[j]; - for (int k = 0; k < 8; ++k) - { - const int bit = j * 8 + k; - if (bits & (1 << k)) - p.finished_blocks[bit] = true; - } - } - - if (p.finished_blocks.count() == 0) continue; - - std::vector::iterator slot_iter - = std::find(tmp_pieces.begin(), tmp_pieces.end(), p.index); - if (slot_iter == tmp_pieces.end()) - { - // this piece is marked as unfinished - // but doesn't have any storage - error = "piece " + boost::lexical_cast(p.index) + " is " - "marked as unfinished, but doesn't have any storage"; - return; - } - - assert(*slot_iter == p.index); - int slot_index = static_cast(slot_iter - tmp_pieces.begin()); - unsigned long adler - = torrent_ptr->filesystem().piece_crc( - slot_index - , torrent_ptr->block_size() - , p.finished_blocks); - - const entry& ad = (*i)["adler32"]; - - // crc's didn't match, don't use the resume data - if (ad.integer() != adler) - { - error = "checksum mismatch on piece " + boost::lexical_cast(p.index); - return; - } - - tmp_unfinished.push_back(p); - } - } - - // verify file sizes - - std::vector > file_sizes; - entry::list_type& l = rd["file sizes"].list(); - - for (entry::list_type::iterator i = l.begin(); - i != l.end(); ++i) - { - file_sizes.push_back(std::pair( - i->list().front().integer() - , i->list().back().integer())); - } - - if ((int)tmp_pieces.size() == info.num_pieces() - && std::find_if(tmp_pieces.begin(), tmp_pieces.end() - , boost::bind(std::less(), _1, 0)) == tmp_pieces.end()) - { - if (info.num_files() != (int)file_sizes.size()) - { - error = "the number of files does not match the torrent (num: " - + boost::lexical_cast(file_sizes.size()) + " actual: " - + boost::lexical_cast(info.num_files()) + ")"; - return; - } - - std::vector >::iterator - fs = file_sizes.begin(); - // the resume data says we have the entire torrent - // make sure the file sizes are the right ones - for (torrent_info::file_iterator i = info.begin_files() - , end(info.end_files()); i != end; ++i, ++fs) - { - if (i->size != fs->first) - { - error = "file size for '" + i->path.native_file_string() + "' was expected to be " - + boost::lexical_cast(i->size) + " bytes"; - return; - } - } - } - - - if (!match_filesizes(info, save_path, file_sizes, &error)) - return; - - piece_map.swap(tmp_pieces); - unfinished_pieces.swap(tmp_unfinished); - } - catch (invalid_encoding) - { - return; - } - catch (type_error) - { - return; - } - catch (file_error) - { - return; - } - } -}} - diff --git a/libtorrent/cpp/sha1.cpp b/libtorrent/cpp/sha1.cpp deleted file mode 100755 index 1d04b4a17..000000000 --- a/libtorrent/cpp/sha1.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* -SHA-1 C++ conversion - -original version: - -SHA-1 in C -By Steve Reid -100% Public Domain - -changelog at the end of the file. -*/ - -#include -#include - -// if you don't want boost -// replace with -// #include - -#include -using boost::uint32_t; -using boost::uint8_t; - -#include "libtorrent/config.hpp" - -struct TORRENT_EXPORT SHA1_CTX -{ - uint32_t state[5]; - uint32_t count[2]; - uint8_t buffer[64]; -}; - -TORRENT_EXPORT void SHA1Init(SHA1_CTX* context); -TORRENT_EXPORT void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len); -TORRENT_EXPORT void SHA1Final(SHA1_CTX* context, uint8_t* digest); - -namespace -{ - union CHAR64LONG16 - { - uint8_t c[64]; - uint32_t l[16]; - }; - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -// blk0() and blk() perform the initial expand. -// I got the idea of expanding during the round function from SSLeay - struct little_endian_blk0 - { - static uint32_t apply(CHAR64LONG16* block, int i) - { - return block->l[i] = (rol(block->l[i],24)&0xFF00FF00) - | (rol(block->l[i],8)&0x00FF00FF); - } - }; - - struct big_endian_blk0 - { - static uint32_t apply(CHAR64LONG16* block, int i) - { - return block->l[i]; - } - }; - - -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ - ^block->l[(i+2)&15]^block->l[i&15],1)) - -// (R0+R1), R2, R3, R4 are the different operations used in SHA1 -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+BlkFun::apply(block, i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - - // Hash a single 512-bit block. This is the core of the algorithm. - template - void SHA1Transform(uint32_t state[5], uint8_t const buffer[64]) - { - using namespace std; - uint32_t a, b, c, d, e; - - CHAR64LONG16* block; - uint8_t workspace[64]; - block = (CHAR64LONG16*)workspace; - memcpy(block, buffer, 64); - - // Copy context->state[] to working vars - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - // 4 rounds of 20 operations each. Loop unrolled. - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - // Add the working vars back into context.state[] - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - // Wipe variables - a = b = c = d = e = 0; - } - - void SHAPrintContext(SHA1_CTX *context, char *msg) - { - using namespace std; - printf("%s (%d,%d) %x %x %x %x %x\n" - , msg, context->count[0], context->count[1] - , context->state[0], context->state[1] - , context->state[2], context->state[3] - , context->state[4]); - } - - template - void internal_update(SHA1_CTX* context, uint8_t const* data, uint32_t len) - { - using namespace std; - uint32_t i, j; // JHB - -#ifdef VERBOSE - SHAPrintContext(context, "before"); -#endif - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; - context->count[1] += (len >> 29); - if ((j + len) > 63) - { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) - { - SHA1Transform(context->state, &data[i]); - } - j = 0; - } - else - { - i = 0; - } - memcpy(&context->buffer[j], &data[i], len - i); -#ifdef VERBOSE - SHAPrintContext(context, "after "); -#endif - } - - bool is_big_endian() - { - uint32_t test = 1; - return *reinterpret_cast(&test) == 0; - } -} - -// SHA1Init - Initialize new context - -void SHA1Init(SHA1_CTX* context) -{ - // SHA1 initialization constants - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - - -// Run your data through this. - -void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len) -{ -#if defined __BIG_ENDIAN__ - internal_update(context, data, len); -#elif defined LITTLE_ENDIAN - internal_update(context, data, len); -#else - // select different functions depending on endianess - // and figure out the endianess runtime - if (is_big_endian()) - internal_update(context, data, len); - else - internal_update(context, data, len); -#endif -} - - -// Add padding and return the message digest. - -void SHA1Final(SHA1_CTX* context, uint8_t* digest) -{ - uint8_t finalcount[8]; - - for (uint32_t i = 0; i < 8; ++i) - { - // Endian independent - finalcount[i] = static_cast( - (context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); - } - - SHA1Update(context, (uint8_t const*)"\200", 1); - while ((context->count[0] & 504) != 448) - SHA1Update(context, (uint8_t const*)"\0", 1); - SHA1Update(context, finalcount, 8); // Should cause a SHA1Transform() - - for (uint32_t i = 0; i < 20; ++i) - { - digest[i] = static_cast( - (context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } -} - -/************************************************************ - ------------------ -Modified 7/98 -By James H. Brown -Still 100% Public Domain - -Corrected a problem which generated improper hash values on 16 bit machines -Routine SHA1Update changed from - void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int -len) -to - void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned -long len) - -The 'len' parameter was declared an int which works fine on 32 bit machines. -However, on 16 bit machines an int is too small for the shifts being done -against -it. This caused the hash function to generate incorrect values if len was -greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). - -Since the file IO in main() reads 16K at a time, any file 8K or larger would -be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million -"a"s). - -I also changed the declaration of variables i & j in SHA1Update to -unsigned long from unsigned int for the same reason. - -These changes should make no difference to any 32 bit implementations since -an -int and a long are the same size in those environments. - --- -I also corrected a few compiler warnings generated by Borland C. -1. Added #include for exit() prototype -2. Removed unused variable 'j' in SHA1Final -3. Changed exit(0) to return(0) at end of main. - -ALL changes I made can be located by searching for comments containing 'JHB' ------------------ -Modified 8/98 -By Steve Reid -Still 100% public domain - -1- Removed #include and used return() instead of exit() -2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) -3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net - ------------------ -Modified 4/01 -By Saul Kravitz -Still 100% PD -Modified to run on Compaq Alpha hardware. - ------------------ -Converted to C++ 6/04 -By Arvid Norberg -1- made the input buffer const, and made the - previous SHA1HANDSOFF implicit -2- uses C99 types with size guarantees - from boost -3- if none of __BIG_ENDIAN__ or LITTLE_ENDIAN - are defined, endianess is determined - at runtime. templates are used to duplicate - the transform function for each endianess -4- using anonymous namespace to avoid external - linkage on internal functions -5- using standard C++ includes - -still 100% PD -*/ - -/* -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ diff --git a/libtorrent/cpp/stat.cpp b/libtorrent/cpp/stat.cpp deleted file mode 100755 index c36bd3725..000000000 --- a/libtorrent/cpp/stat.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -// TODO: Use two algorithms to estimate transfer rate. -// one (simple) for transfer rates that are >= 1 packet -// per second and one (low pass-filter) for rates < 1 -// packet per second. - -#include - -#include "libtorrent/stat.hpp" -#include "libtorrent/invariant_check.hpp" -#include - -#if defined _MSC_VER && _MSC_VER <= 1200 -#define for if (false) {} else for -#endif - -using namespace libtorrent; - -void libtorrent::stat::second_tick(float tick_interval) -{ - INVARIANT_CHECK; - - for (int i = history - 2; i >= 0; --i) - { - m_download_rate_history[i + 1] = m_download_rate_history[i]; - m_upload_rate_history[i + 1] = m_upload_rate_history[i]; - m_download_payload_rate_history[i + 1] = m_download_payload_rate_history[i]; - m_upload_payload_rate_history[i + 1] = m_upload_payload_rate_history[i]; - } - - m_download_rate_history[0] = (m_downloaded_payload + m_downloaded_protocol) - / tick_interval; - m_upload_rate_history[0] = (m_uploaded_payload + m_uploaded_protocol) - / tick_interval; - m_download_payload_rate_history[0] = m_downloaded_payload / tick_interval; - m_upload_payload_rate_history[0] = m_uploaded_payload / tick_interval; - - m_downloaded_payload = 0; - m_uploaded_payload = 0; - m_downloaded_protocol = 0; - m_uploaded_protocol = 0; - - m_mean_download_rate = 0; - m_mean_upload_rate = 0; - m_mean_download_payload_rate = 0; - m_mean_upload_payload_rate = 0; - - for (int i = 0; i < history; ++i) - { - m_mean_download_rate += m_download_rate_history[i]; - m_mean_upload_rate += m_upload_rate_history[i]; - m_mean_download_payload_rate += m_download_payload_rate_history[i]; - m_mean_upload_payload_rate += m_upload_payload_rate_history[i]; - } - - m_mean_download_rate /= history; - m_mean_upload_rate /= history; - m_mean_download_payload_rate /= history; - m_mean_upload_payload_rate /= history; -} diff --git a/libtorrent/cpp/storage.cpp b/libtorrent/cpp/storage.cpp deleted file mode 100755 index 33b32bad3..000000000 --- a/libtorrent/cpp/storage.cpp +++ /dev/null @@ -1,2172 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg, Daniel Wallin -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 -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/storage.hpp" -#include "libtorrent/torrent.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/session.hpp" -#include "libtorrent/peer_id.hpp" -#include "libtorrent/file.hpp" -#include "libtorrent/invariant_check.hpp" -#include "libtorrent/aux_/session_impl.hpp" - -#ifndef NDEBUG -#include -#include -#include -#include -#endif - -#if defined(_WIN32) && defined(UNICODE) - -#include -#include -#include "libtorrent/utf8.hpp" - -namespace libtorrent -{ - std::wstring safe_convert(std::string const& s) - { - try - { - return libtorrent::utf8_wchar(s); - } - catch (std::exception) - { - std::wstring ret; - for (const char* i = &*s.begin(); i < &*s.end(); ++i) - { - wchar_t c; - c = '.'; - std::mbtowc(&c, i, 1); - ret += c; - } - return ret; - } - } -} - -namespace -{ - using libtorrent::safe_convert; - using namespace boost::filesystem; - - // based on code from Boost.Fileystem - bool create_directories_win(const path& ph) - { - if (ph.empty() || exists(ph)) - { - if ( !ph.empty() && !is_directory(ph) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::create_directories", - ph, "path exists and is not a directory", - not_directory_error ) ); - return false; - } - - // First create branch, by calling ourself recursively - create_directories_win(ph.branch_path()); - // Now that parent's path exists, create the directory - std::wstring wph(safe_convert(ph.native_directory_string())); - CreateDirectory(wph.c_str(), 0); - return true; - } - - bool exists_win( const path & ph ) - { - std::wstring wpath(safe_convert(ph.string())); - if(::GetFileAttributes( wpath.c_str() ) == 0xFFFFFFFF) - { - UINT err = ::GetLastError(); - if((err == ERROR_FILE_NOT_FOUND) - || (err == ERROR_INVALID_PARAMETER) - || (err == ERROR_NOT_READY) - || (err == ERROR_PATH_NOT_FOUND) - || (err == ERROR_INVALID_NAME) - || (err == ERROR_BAD_NETPATH )) - return false; // GetFileAttributes failed because the path does not exist - // for any other error we assume the file does exist and fall through, - // this may not be the best policy though... (JM 20040330) - return true; - } - return true; - } - - boost::intmax_t file_size_win( const path & ph ) - { - std::wstring wpath(safe_convert(ph.string())); - // by now, intmax_t is 64-bits on all Windows compilers - WIN32_FILE_ATTRIBUTE_DATA fad; - if ( !::GetFileAttributesExW( wpath.c_str(), - ::GetFileExInfoStandard, &fad ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::file_size", - ph, detail::system_error_code() ) ); - if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 ) - boost::throw_exception( filesystem_error( - "boost::filesystem::file_size", - ph, "invalid: is a directory", - is_directory_error ) ); - return (static_cast(fad.nFileSizeHigh) - << (sizeof(fad.nFileSizeLow)*8)) - + fad.nFileSizeLow; - } - - std::time_t last_write_time_win( const path & ph ) - { - struct _stat path_stat; - std::wstring wph(safe_convert(ph.native_file_string())); - if ( ::_wstat( wph.c_str(), &path_stat ) != 0 ) - boost::throw_exception( filesystem_error( - "boost::filesystem::last_write_time", - ph, detail::system_error_code() ) ); - return path_stat.st_mtime; - } - - void rename_win( const path & old_path, - const path & new_path ) - { - std::wstring wold_path(safe_convert(old_path.string())); - std::wstring wnew_path(safe_convert(new_path.string())); - if ( !::MoveFile( wold_path.c_str(), wnew_path.c_str() ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::rename", - old_path, new_path, detail::system_error_code() ) ); - } - -} // anonymous namespace - -#endif - -#if BOOST_VERSION < 103200 -bool operator<(boost::filesystem::path const& lhs - , boost::filesystem::path const& rhs) -{ - return lhs.string() < rhs.string(); -} -#endif - -using namespace boost::filesystem; -namespace pt = boost::posix_time; -using boost::bind; -using namespace ::boost::multi_index; -using boost::multi_index::multi_index_container; - -namespace -{ - using namespace libtorrent; - - void print_to_log(const std::string& s) - { - static std::ofstream log("log.txt"); - log << s; - log.flush(); - } -/* - struct file_key - { - file_key(sha1_hash ih, path f): info_hash(ih), file_path(f) {} - file_key() {} - sha1_hash info_hash; - path file_path; - bool operator<(file_key const& fk) const - { - if (info_hash < fk.info_hash) return true; - if (fk.info_hash < info_hash) return false; - return file_path < fk.file_path; - } - }; -*/ - struct lru_file_entry - { - lru_file_entry(boost::shared_ptr const& f) - : file_ptr(f) - , last_use(pt::second_clock::universal_time()) {} - mutable boost::shared_ptr file_ptr; - path file_path; - void* key; - pt::ptime last_use; - file::open_mode mode; - }; - - struct file_pool - { - file_pool(int size): m_size(size) {} - - boost::shared_ptr open_file(void* st, path const& p, file::open_mode m) - { - assert(st != 0); - assert(p.is_complete()); - typedef nth_index::type path_view; - path_view& pt = get<0>(m_files); - path_view::iterator i = pt.find(p); - if (i != pt.end()) - { - lru_file_entry e = *i; - e.last_use = pt::second_clock::universal_time(); - - // if you hit this assert, you probably have more than one - // storage/torrent using the same file at the same time! - assert(e.key == st); - - e.key = st; - if ((e.mode & m) != m) - { - // close the file before we open it with - // the new read/write privilages - i->file_ptr.reset(); - assert(e.file_ptr.unique()); - e.file_ptr.reset(); - e.file_ptr.reset(new file(p, m)); - e.mode = m; - } - pt.replace(i, e); - return e.file_ptr; - } - // the file is not in our cache - if ((int)m_files.size() >= m_size) - { - // the file cache is at its maximum size, close - // the least recently used (lru) file from it - typedef nth_index::type lru_view; - lru_view& lt = get<1>(m_files); - lru_view::iterator i = lt.begin(); - // the first entry in this view is the least recently used -/* for (lru_view::iterator i = lt.begin(); i != lt.end(); ++i) - { - std::cerr << i->last_use << "\n"; - } -*/ assert(lt.size() == 1 || (i->last_use <= boost::next(i)->last_use)); - lt.erase(i); - } - lru_file_entry e(boost::shared_ptr(new file(p, m))); - e.mode = m; - e.key = st; - e.file_path = p; - pt.insert(e); - return e.file_ptr; - } - - void release(void* st) - { - assert(st != 0); - using boost::tie; - - typedef nth_index::type key_view; - key_view& kt = get<2>(m_files); - - key_view::iterator start, end; - tie(start, end) = kt.equal_range(st); - -/* - std::cerr << "releasing files!\n"; - for (path_view::iterator i = r.first; i != r.second; ++i) - { - std::cerr << i->key.file_path.native_file_string() << "\n"; - } -*/ - kt.erase(start, end); -/* - std::cerr << "files left: " << pt.size() << "\n"; - for (path_view::iterator i = pt.begin(); i != pt.end(); ++i) - { - std::cerr << i->key.file_path.native_file_string() << "\n"; - } -*/ - } - - private: - int m_size; - - typedef multi_index_container< - lru_file_entry, indexed_by< - ordered_unique > - , ordered_non_unique > - , ordered_non_unique > - > - > file_set; - - file_set m_files; - }; -} - -namespace libtorrent -{ - - std::vector > get_filesizes( - torrent_info const& t, path p) - { - p = complete(p); - std::vector > sizes; - for (torrent_info::file_iterator i = t.begin_files(); - i != t.end_files(); ++i) - { - size_type size = 0; - std::time_t time = 0; - try - { - path f = p / i->path; -#if defined(_WIN32) && defined(UNICODE) - size = file_size_win(f); - time = last_write_time_win(f); -#else - size = file_size(f); - time = last_write_time(f); -#endif - } - catch (std::exception&) {} - sizes.push_back(std::make_pair(size, time)); - } - return sizes; - } - - bool match_filesizes( - torrent_info const& t - , path p - , std::vector > const& sizes - , std::string* error) - { - if ((int)sizes.size() != t.num_files()) - { - if (error) *error = "mismatching number of files"; - return false; - } - p = complete(p); - - std::vector >::const_iterator s - = sizes.begin(); - for (torrent_info::file_iterator i = t.begin_files(); - i != t.end_files(); ++i, ++s) - { - size_type size = 0; - std::time_t time = 0; - try - { - path f = p / i->path; -#if defined(_WIN32) && defined(UNICODE) - size = file_size_win(f); - time = last_write_time_win(f); -#else - size = file_size(f); - time = last_write_time(f); -#endif - } - catch (std::exception&) {} - if (size != s->first) - { - if (error) *error = "filesize mismatch for file '" - + i->path.native_file_string() - + "', expected to be " + boost::lexical_cast(s->first) - + " bytes"; - return false; - } - if (time != s->second) - { - if (error) *error = "timestamp mismatch for file '" - + i->path.native_file_string() - + "', expected to have modification date " - + boost::lexical_cast(s->second); - return false; - } - } - return true; - } - - struct thread_safe_storage - { - thread_safe_storage(std::size_t n) - : slots(n, false) - {} - - boost::mutex mutex; - boost::condition condition; - std::vector slots; - }; - - struct slot_lock - { - slot_lock(thread_safe_storage& s, int slot_) - : storage_(s) - , slot(slot_) - { - assert(slot_>=0 && slot_ < (int)s.slots.size()); - boost::mutex::scoped_lock lock(storage_.mutex); - - while (storage_.slots[slot]) - storage_.condition.wait(lock); - storage_.slots[slot] = true; - } - - ~slot_lock() - { - storage_.slots[slot] = false; - storage_.condition.notify_all(); - } - - thread_safe_storage& storage_; - int slot; - }; - - class storage::impl : public thread_safe_storage, boost::noncopyable - { - public: - impl(torrent_info const& info, path const& path) - : thread_safe_storage(info.num_pieces()) - , info(info) - { - save_path = complete(path); - assert(save_path.is_complete()); - } - - impl(impl const& x) - : thread_safe_storage(x.info.num_pieces()) - , info(x.info) - , save_path(x.save_path) - {} - - ~impl() - { - files.release(this); - } - - torrent_info const& info; - path save_path; - static file_pool files; - }; - - file_pool storage::impl::files(40); - - storage::storage(torrent_info const& info, path const& path) - : m_pimpl(new impl(info, path)) - { - assert(info.begin_files() != info.end_files()); - } - - void storage::release_files() - { - m_pimpl->files.release(m_pimpl.get()); - } - - void storage::swap(storage& other) - { - m_pimpl.swap(other.m_pimpl); - } - - // returns true on success - bool storage::move_storage(path save_path) - { - path old_path; - path new_path; - - save_path = complete(save_path); - -#if defined(_WIN32) && defined(UNICODE) - std::wstring wsave_path(safe_convert(save_path.native_file_string())); - if (!exists_win(save_path)) - { - CreateDirectory(wsave_path.c_str(), 0); - } - else if ((GetFileAttributes(wsave_path.c_str()) & FILE_ATTRIBUTE_DIRECTORY) == 0) - { - return false; - } -#else - if(!exists(save_path)) - create_directory(save_path); - else if(!is_directory(save_path)) - return false; -#endif - - m_pimpl->files.release(m_pimpl.get()); - - if (m_pimpl->info.num_files() == 1) - { - path single_file = m_pimpl->info.begin_files()->path; - if (single_file.has_branch_path()) - { -#if defined(_WIN32) && defined(UNICODE) - std::wstring wsave_path(safe_convert((save_path / single_file.branch_path()) - .native_directory_string())); - CreateDirectory(wsave_path.c_str(), 0); -#else - create_directory(save_path / single_file.branch_path()); -#endif - } - - old_path = m_pimpl->save_path / single_file; - new_path = save_path / m_pimpl->info.begin_files()->path; - } - else - { - assert(m_pimpl->info.num_files() > 1); - old_path = m_pimpl->save_path / m_pimpl->info.name(); - new_path = save_path / m_pimpl->info.name(); - } - - try - { -#if defined(_WIN32) && defined(UNICODE) - rename_win(old_path, new_path); -#else - rename(old_path, new_path); -#endif - m_pimpl->save_path = save_path; - return true; - } - catch (std::exception&) {} - return false; - } - -#ifndef NDEBUG - - void storage::shuffle() - { - int num_pieces = m_pimpl->info.num_pieces(); - - std::vector pieces(num_pieces); - for (std::vector::iterator i = pieces.begin(); - i != pieces.end(); - ++i) - { - *i = static_cast(i - pieces.begin()); - } - std::srand((unsigned int)std::time(0)); - std::vector targets(pieces); - std::random_shuffle(pieces.begin(), pieces.end()); - std::random_shuffle(targets.begin(), targets.end()); - - for (int i = 0; i < (std::max)(num_pieces / 50, 1); ++i) - { - const int slot_index = targets[i]; - const int piece_index = pieces[i]; - const int slot_size =static_cast(m_pimpl->info.piece_size(slot_index)); - std::vector buf(slot_size); - read(&buf[0], piece_index, 0, slot_size); - write(&buf[0], slot_index, 0, slot_size); - } - } - -#endif - - size_type storage::read( - char* buf - , int slot - , int offset - , int size) - { - assert(buf != 0); - assert(slot >= 0 && slot < m_pimpl->info.num_pieces()); - assert(offset >= 0); - assert(offset < m_pimpl->info.piece_size(slot)); - assert(size > 0); - - slot_lock lock(*m_pimpl, slot); - -#ifndef NDEBUG - std::vector slices - = m_pimpl->info.map_block(slot, offset, size); - assert(!slices.empty()); -#endif - - size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset; - assert(start + size <= m_pimpl->info.total_size()); - - // find the file iterator and file offset - size_type file_offset = start; - std::vector::const_iterator file_iter; - - for (file_iter = m_pimpl->info.begin_files();;) - { - if (file_offset < file_iter->size) - break; - - file_offset -= file_iter->size; - ++file_iter; - } - - boost::shared_ptr in(m_pimpl->files.open_file( - m_pimpl.get() - , m_pimpl->save_path / file_iter->path - , file::in)); - - assert(file_offset < file_iter->size); - - assert(slices[0].offset == file_offset); - - size_type new_pos = in->seek(file_offset); - if (new_pos != file_offset) - { - // the file was not big enough - throw file_error("slot has no storage"); - } - -#ifndef NDEBUG - size_type in_tell = in->tell(); - assert(in_tell == file_offset); -#endif - - int left_to_read = size; - int slot_size = static_cast(m_pimpl->info.piece_size(slot)); - - if (offset + left_to_read > slot_size) - left_to_read = slot_size - offset; - - assert(left_to_read >= 0); - - size_type result = left_to_read; - int buf_pos = 0; - -#ifndef NDEBUG - int counter = 0; -#endif - - while (left_to_read > 0) - { - int read_bytes = left_to_read; - if (file_offset + read_bytes > file_iter->size) - read_bytes = static_cast(file_iter->size - file_offset); - - if (read_bytes > 0) - { -#ifndef NDEBUG - assert(int(slices.size()) > counter); - size_type slice_size = slices[counter].size; - assert(slice_size == read_bytes); - assert(m_pimpl->info.file_at(slices[counter].file_index).path - == file_iter->path); -#endif - - size_type actual_read = in->read(buf + buf_pos, read_bytes); - - if (read_bytes != actual_read) - { - // the file was not big enough - throw file_error("slot has no storage"); - } - - left_to_read -= read_bytes; - buf_pos += read_bytes; - assert(buf_pos >= 0); - file_offset += read_bytes; - } - - if (left_to_read > 0) - { - ++file_iter; -#ifndef NDEBUG - // empty files are not returned by map_block, so if - // this file was empty, don't increment the slice counter - if (read_bytes > 0) ++counter; -#endif - path path = m_pimpl->save_path / file_iter->path; - - file_offset = 0; - in = m_pimpl->files.open_file( - m_pimpl.get() - , path, file::in); - in->seek(0); - } - } - - return result; - } - - // throws file_error if it fails to write - void storage::write( - const char* buf - , int slot - , int offset - , int size) - { - assert(buf != 0); - assert(slot >= 0); - assert(slot < m_pimpl->info.num_pieces()); - assert(offset >= 0); - assert(size > 0); - - slot_lock lock(*m_pimpl, slot); - -#ifndef NDEBUG - std::vector slices - = m_pimpl->info.map_block(slot, offset, size); - assert(!slices.empty()); -#endif - - size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset; - - // find the file iterator and file offset - size_type file_offset = start; - std::vector::const_iterator file_iter; - - for (file_iter = m_pimpl->info.begin_files();;) - { - if (file_offset < file_iter->size) - break; - - file_offset -= file_iter->size; - ++file_iter; - assert(file_iter != m_pimpl->info.end_files()); - } - - path p(m_pimpl->save_path / file_iter->path); - boost::shared_ptr out = m_pimpl->files.open_file( - m_pimpl.get() - , p, file::out | file::in); - - assert(file_offset < file_iter->size); - assert(slices[0].offset == file_offset); - - size_type pos = out->seek(file_offset); - - if (pos != file_offset) - { - std::stringstream s; - s << "no storage for slot " << slot; - throw file_error(s.str()); - } - - int left_to_write = size; - int slot_size = static_cast(m_pimpl->info.piece_size(slot)); - - if (offset + left_to_write > slot_size) - left_to_write = slot_size - offset; - - assert(left_to_write >= 0); - - int buf_pos = 0; -#ifndef NDEBUG - int counter = 0; -#endif - while (left_to_write > 0) - { - int write_bytes = left_to_write; - if (file_offset + write_bytes > file_iter->size) - { - assert(file_iter->size >= file_offset); - write_bytes = static_cast(file_iter->size - file_offset); - } - - if (write_bytes > 0) - { - assert(int(slices.size()) > counter); - assert(slices[counter].size == write_bytes); - assert(m_pimpl->info.file_at(slices[counter].file_index).path - == file_iter->path); - - assert(buf_pos >= 0); - assert(write_bytes >= 0); - size_type written = out->write(buf + buf_pos, write_bytes); - - if (written != write_bytes) - { - std::stringstream s; - s << "no storage for slot " << slot; - throw file_error(s.str()); - } - - left_to_write -= write_bytes; - buf_pos += write_bytes; - assert(buf_pos >= 0); - file_offset += write_bytes; - assert(file_offset <= file_iter->size); - } - - if (left_to_write > 0) - { - #ifndef NDEBUG - if (write_bytes > 0) ++counter; - #endif - ++file_iter; - - assert(file_iter != m_pimpl->info.end_files()); - path p = m_pimpl->save_path / file_iter->path; - file_offset = 0; - out = m_pimpl->files.open_file( - m_pimpl.get() - , p, file::out | file::in); - - out->seek(0); - } - } - } - - - - - - // -- piece_manager ----------------------------------------------------- - - class piece_manager::impl - { - friend class invariant_access; - public: - - impl( - torrent_info const& info - , path const& path); - - bool check_fastresume( - aux::piece_checker_data& d - , std::vector& pieces - , int& num_pieces - , bool compact_mode); - - std::pair check_files( - std::vector& pieces - , int& num_pieces); - - void release_files(); - - void allocate_slots(int num_slots); - void mark_failed(int index); - unsigned long piece_crc( - int slot_index - , int block_size - , const std::bitset<256>& bitmask); - - int slot_for_piece(int piece_index) const; - - size_type read( - char* buf - , int piece_index - , int offset - , int size); - - void write( - const char* buf - , int piece_index - , int offset - , int size); - - path const& save_path() const - { return m_save_path; } - - bool move_storage(path save_path) - { - if (m_storage.move_storage(save_path)) - { - m_save_path = complete(save_path); - return true; - } - return false; - } - - void export_piece_map(std::vector& p) const; - - // returns the slot currently associated with the given - // piece or assigns the given piece_index to a free slot - - int identify_data( - const std::vector& piece_data - , int current_slot - , std::vector& have_pieces - , int& num_pieces - , const std::multimap& hash_to_piece); - - int allocate_slot_for_piece(int piece_index); -#ifndef NDEBUG - void check_invariant() const; -#ifdef TORRENT_STORAGE_DEBUG - void debug_log() const; -#endif -#endif - storage m_storage; - - // if this is true, pieces are always allocated at the - // lowest possible slot index. If it is false, pieces - // are always written to their final place immediately - bool m_compact_mode; - - // if this is true, pieces that haven't been downloaded - // will be filled with zeroes. Not filling with zeroes - // will not work in some cases (where a seek cannot pass - // the end of the file). - bool m_fill_mode; - - // a bitmask representing the pieces we have - std::vector m_have_piece; - - torrent_info const& m_info; - - // slots that haven't had any file storage allocated - std::vector m_unallocated_slots; - // slots that have file storage, but isn't assigned to a piece - std::vector m_free_slots; - - enum - { - has_no_slot = -3 // the piece has no storage - }; - - // maps piece indices to slots. If a piece doesn't - // have any storage, it is set to 'has_no_slot' - std::vector m_piece_to_slot; - - enum - { - unallocated = -1, // the slot is unallocated - unassigned = -2 // the slot is allocated but not assigned to a piece - }; - - // maps slots to piece indices, if a slot doesn't have a piece - // it can either be 'unassigned' or 'unallocated' - std::vector m_slot_to_piece; - - path m_save_path; - - mutable boost::recursive_mutex m_mutex; - - bool m_allocating; - boost::mutex m_allocating_monitor; - boost::condition m_allocating_condition; - - // these states are used while checking/allocating the torrent - - enum { - // the default initial state - state_none, - // the file checking is complete - state_finished, - // creating the directories - state_create_files, - // checking the files - state_full_check, - // allocating files (in non-compact mode) - state_allocating - } m_state; - int m_current_slot; - - std::vector m_piece_data; - - // this maps a piece hash to piece index. It will be - // build the first time it is used (to save time if it - // isn't needed) - std::multimap m_hash_to_piece; - - // used as temporary piece data storage in allocate_slots - // it is a member in order to avoid allocating it on - // the heap every time a new slot is allocated. (This is quite - // frequent with high download speeds) - std::vector m_scratch_buffer; - }; - - piece_manager::impl::impl( - torrent_info const& info - , path const& save_path) - : m_storage(info, save_path) - , m_compact_mode(false) - , m_fill_mode(true) - , m_info(info) - , m_save_path(complete(save_path)) - , m_allocating(false) - { - assert(m_save_path.is_complete()); - } - - piece_manager::piece_manager( - torrent_info const& info - , path const& save_path) - : m_pimpl(new impl(info, save_path)) - { - } - - piece_manager::~piece_manager() - { - } - - void piece_manager::release_files() - { - m_pimpl->release_files(); - } - - void piece_manager::impl::release_files() - { - m_storage.release_files(); - } - - void piece_manager::impl::export_piece_map( - std::vector& p) const - { - // synchronization ------------------------------------------------------ - boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- - - INVARIANT_CHECK; - - p.clear(); - std::vector::const_reverse_iterator last; - for (last = m_slot_to_piece.rbegin(); - last != m_slot_to_piece.rend(); ++last) - { - if (*last != unallocated) break; - } - - for (std::vector::const_iterator i = - m_slot_to_piece.begin(); - i != last.base(); ++i) - { - p.push_back(*i); - } - } - - void piece_manager::export_piece_map( - std::vector& p) const - { - m_pimpl->export_piece_map(p); - } - - void piece_manager::impl::mark_failed(int piece_index) - { - // synchronization ------------------------------------------------------ - boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- - - INVARIANT_CHECK; - - assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); - assert(m_piece_to_slot[piece_index] >= 0); - - int slot_index = m_piece_to_slot[piece_index]; - - assert(slot_index >= 0); - - m_slot_to_piece[slot_index] = unassigned; - m_piece_to_slot[piece_index] = has_no_slot; - m_free_slots.push_back(slot_index); - } - - void piece_manager::mark_failed(int index) - { - m_pimpl->mark_failed(index); - } - - bool piece_manager::is_allocating() const - { - return m_pimpl->m_state - == impl::state_allocating; - } - - int piece_manager::slot_for_piece(int piece_index) const - { - return m_pimpl->slot_for_piece(piece_index); - } - - int piece_manager::impl::slot_for_piece(int piece_index) const - { - assert(piece_index >= 0 && piece_index < m_info.num_pieces()); - return m_piece_to_slot[piece_index]; - } - - unsigned long piece_manager::piece_crc( - int index - , int block_size - , const std::bitset<256>& bitmask) - { - return m_pimpl->piece_crc(index, block_size, bitmask); - } - - unsigned long piece_manager::impl::piece_crc( - int slot_index - , int block_size - , const std::bitset<256>& bitmask) - { - assert(slot_index >= 0); - assert(slot_index < m_info.num_pieces()); - assert(block_size > 0); - - adler32_crc crc; - std::vector buf(block_size); - int num_blocks = static_cast(m_info.piece_size(slot_index)) / block_size; - int last_block_size = static_cast(m_info.piece_size(slot_index)) % block_size; - if (last_block_size == 0) last_block_size = block_size; - - for (int i = 0; i < num_blocks-1; ++i) - { - if (!bitmask[i]) continue; - m_storage.read( - &buf[0] - , slot_index - , i * block_size - , block_size); - crc.update(&buf[0], block_size); - } - if (bitmask[num_blocks - 1]) - { - m_storage.read( - &buf[0] - , slot_index - , block_size * (num_blocks - 1) - , last_block_size); - crc.update(&buf[0], last_block_size); - } - return crc.final(); - } - - size_type piece_manager::impl::read( - char* buf - , int piece_index - , int offset - , int size) - { - assert(buf); - assert(offset >= 0); - assert(size > 0); - assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); - assert(m_piece_to_slot[piece_index] >= 0 && m_piece_to_slot[piece_index] < (int)m_slot_to_piece.size()); - int slot = m_piece_to_slot[piece_index]; - assert(slot >= 0 && slot < (int)m_slot_to_piece.size()); - return m_storage.read(buf, slot, offset, size); - } - - size_type piece_manager::read( - char* buf - , int piece_index - , int offset - , int size) - { - return m_pimpl->read(buf, piece_index, offset, size); - } - - void piece_manager::impl::write( - const char* buf - , int piece_index - , int offset - , int size) - { - assert(buf); - assert(offset >= 0); - assert(size > 0); - assert(piece_index >= 0 && piece_index < (int)m_piece_to_slot.size()); - int slot = allocate_slot_for_piece(piece_index); - assert(slot >= 0 && slot < (int)m_slot_to_piece.size()); - m_storage.write(buf, slot, offset, size); - } - - void piece_manager::write( - const char* buf - , int piece_index - , int offset - , int size) - { - m_pimpl->write(buf, piece_index, offset, size); - } - - int piece_manager::impl::identify_data( - const std::vector& piece_data - , int current_slot - , std::vector& have_pieces - , int& num_pieces - , const std::multimap& hash_to_piece) - { - INVARIANT_CHECK; - - assert((int)have_pieces.size() == m_info.num_pieces()); - - const int piece_size = static_cast(m_info.piece_length()); - const int last_piece_size = static_cast(m_info.piece_size( - m_info.num_pieces() - 1)); - - assert((int)piece_data.size() >= last_piece_size); - - // calculate a small digest, with the same - // size as the last piece. And a large digest - // which has the same size as a normal piece - hasher small_digest; - small_digest.update(&piece_data[0], last_piece_size); - hasher large_digest(small_digest); - assert(piece_size - last_piece_size >= 0); - if (piece_size - last_piece_size > 0) - { - large_digest.update( - &piece_data[last_piece_size] - , piece_size - last_piece_size); - } - sha1_hash large_hash = large_digest.final(); - sha1_hash small_hash = small_digest.final(); - - typedef std::multimap::const_iterator map_iter; - map_iter begin1; - map_iter end1; - map_iter begin2; - map_iter end2; - - // makes the lookups for the small digest and the large digest - boost::tie(begin1, end1) = hash_to_piece.equal_range(small_hash); - boost::tie(begin2, end2) = hash_to_piece.equal_range(large_hash); - - // copy all potential piece indices into this vector - std::vector matching_pieces; - for (map_iter i = begin1; i != end1; ++i) - matching_pieces.push_back(i->second); - for (map_iter i = begin2; i != end2; ++i) - matching_pieces.push_back(i->second); - - // no piece matched the data in the slot - if (matching_pieces.empty()) - return unassigned; - - // ------------------------------------------ - // CHECK IF THE PIECE IS IN ITS CORRECT PLACE - // ------------------------------------------ - - if (std::find( - matching_pieces.begin() - , matching_pieces.end() - , current_slot) != matching_pieces.end()) - { - const int piece_index = current_slot; - - if (have_pieces[piece_index]) - { - // we have already found a piece with - // this index. - int other_slot = m_piece_to_slot[piece_index]; - assert(other_slot >= 0); - - // take one of the other matching pieces - // that hasn't already been assigned - int other_piece = -1; - for (std::vector::iterator i = matching_pieces.begin(); - i != matching_pieces.end(); ++i) - { - if (have_pieces[*i] || *i == piece_index) continue; - other_piece = *i; - break; - } - if (other_piece >= 0) - { - // replace the old slot with 'other_piece' - assert(have_pieces[other_piece] == false); - have_pieces[other_piece] = true; - m_slot_to_piece[other_slot] = other_piece; - m_piece_to_slot[other_piece] = other_slot; - ++num_pieces; - } - else - { - // this index is the only piece with this - // hash. The previous slot we found with - // this hash must be the same piece. Mark - // that piece as unassigned, since this slot - // is the correct place for the piece. - m_slot_to_piece[other_slot] = unassigned; - m_free_slots.push_back(other_slot); - } - assert(m_piece_to_slot[piece_index] != current_slot); - assert(m_piece_to_slot[piece_index] >= 0); - m_piece_to_slot[piece_index] = has_no_slot; -#ifndef NDEBUG - // to make the assert happy, a few lines down - have_pieces[piece_index] = false; -#endif - } - else - { - ++num_pieces; - } - - assert(have_pieces[piece_index] == false); - assert(m_piece_to_slot[piece_index] == has_no_slot); - have_pieces[piece_index] = true; - - return piece_index; - } - - // find a matching piece that hasn't - // already been assigned - int free_piece = unassigned; - for (std::vector::iterator i = matching_pieces.begin(); - i != matching_pieces.end(); ++i) - { - if (have_pieces[*i]) continue; - free_piece = *i; - break; - } - - if (free_piece >= 0) - { - assert(have_pieces[free_piece] == false); - assert(m_piece_to_slot[free_piece] == has_no_slot); - have_pieces[free_piece] = true; - ++num_pieces; - - return free_piece; - } - else - { - assert(free_piece == unassigned); - return unassigned; - } - } - - // check if the fastresume data is up to date - // if it is, use it and return true. If it - // isn't return false and the full check - // will be run - bool piece_manager::impl::check_fastresume( - aux::piece_checker_data& data - , std::vector& pieces - , int& num_pieces, bool compact_mode) - { - assert(m_info.piece_length() > 0); - // synchronization ------------------------------------------------------ - boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- - - INVARIANT_CHECK; - - m_compact_mode = compact_mode; - - // This will corrupt the storage - // use while debugging to find - // states that cannot be scanned - // by check_pieces. -// m_storage.shuffle(); - - m_piece_to_slot.resize(m_info.num_pieces(), has_no_slot); - m_slot_to_piece.resize(m_info.num_pieces(), unallocated); - m_free_slots.clear(); - m_unallocated_slots.clear(); - - pieces.clear(); - pieces.resize(m_info.num_pieces(), false); - num_pieces = 0; - - // if we have fast-resume info - // use it instead of doing the actual checking - if (!data.piece_map.empty() - && data.piece_map.size() <= m_slot_to_piece.size()) - { - for (int i = 0; i < (int)data.piece_map.size(); ++i) - { - m_slot_to_piece[i] = data.piece_map[i]; - if (data.piece_map[i] >= 0) - { - m_piece_to_slot[data.piece_map[i]] = i; - int found_piece = data.piece_map[i]; - - // if the piece is not in the unfinished list - // we have all of it - if (std::find_if( - data.unfinished_pieces.begin() - , data.unfinished_pieces.end() - , piece_picker::has_index(found_piece)) - == data.unfinished_pieces.end()) - { - ++num_pieces; - pieces[found_piece] = true; - } - } - else if (data.piece_map[i] == unassigned) - { - m_free_slots.push_back(i); - } - else - { - assert(data.piece_map[i] == unallocated); - m_unallocated_slots.push_back(i); - } - } - - m_unallocated_slots.reserve(int(pieces.size() - data.piece_map.size())); - for (int i = (int)data.piece_map.size(); i < (int)pieces.size(); ++i) - { - m_unallocated_slots.push_back(i); - } - - if (!m_compact_mode && !m_unallocated_slots.empty()) - { - m_state = state_allocating; - return false; - } - else - { - m_state = state_finished; - return true; - } - } - - m_state = state_create_files; - return false; - } - - // performs the full check and full allocation - // (if necessary). returns true if finished and - // false if it should be called again - // the second return value is the progress the - // file check is at. 0 is nothing done, and 1 - // is finished - std::pair piece_manager::impl::check_files( - std::vector& pieces, int& num_pieces) - { - assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); - - if (m_state == state_allocating) - { - if (m_compact_mode) - { - m_state = state_finished; - return std::make_pair(true, 1.f); - } - - if (m_unallocated_slots.empty()) - { - m_state = state_finished; - return std::make_pair(true, 1.f); - } - - // if we're not in compact mode, make sure the - // pieces are spread out and placed at their - // final position. - assert(!m_unallocated_slots.empty()); - allocate_slots(1); - - return std::make_pair(false, 1.f - (float)m_unallocated_slots.size() - / (float)m_slot_to_piece.size()); - } - - if (m_state == state_create_files) - { - // first, create all missing directories - path last_path; - for (torrent_info::file_iterator file_iter = m_info.begin_files(), - end_iter = m_info.end_files(); file_iter != end_iter; ++file_iter) - { - path dir = (m_save_path / file_iter->path).branch_path(); - - // if the file is empty, just create it. But also make sure - // the directory exits. - if (dir == last_path - && file_iter->size == 0) - file(m_save_path / file_iter->path, file::out); - - if (dir == last_path) continue; - last_path = dir; - -#if defined(_WIN32) && defined(UNICODE) - if (!exists_win(last_path)) - create_directories_win(last_path); -#else - if (!exists(last_path)) - create_directories(last_path); -#endif - - if (file_iter->size == 0) - file(m_save_path / file_iter->path, file::out); - } - m_current_slot = 0; - m_state = state_full_check; - m_piece_data.resize(int(m_info.piece_length())); - return std::make_pair(false, 0.f); - } - - assert(m_state == state_full_check); - - // ------------------------ - // DO THE FULL CHECK - // ------------------------ - - try - { - - m_storage.read( - &m_piece_data[0] - , m_current_slot - , 0 - , int(m_info.piece_size(m_current_slot))); - - if (m_hash_to_piece.empty()) - { - for (int i = 0; i < m_info.num_pieces(); ++i) - { - m_hash_to_piece.insert(std::make_pair(m_info.hash_for_piece(i), i)); - } - } - - int piece_index = identify_data( - m_piece_data - , m_current_slot - , pieces - , num_pieces - , m_hash_to_piece); - - assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); - assert(piece_index == unassigned || piece_index >= 0); - - const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated; - const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot; - - // check if this piece should be swapped with any other slot - // this section will ensure that the storage is correctly sorted - // libtorrent will never leave the storage in a state that - // requires this sorting, but other clients may. - - // example of worst case: - // | m_current_slot = 5 - // V - // +---+- - - +---+- - - +---+- - - // | x | | 5 | | 3 | <- piece data in slots - // +---+- - - +---+- - - +---+- - - // 3 y 5 <- slot index - - // in this example, the data in the m_current_slot (5) - // is piece 3. It has to be moved into slot 3. The data - // in slot y (piece 5) should be moved into the m_current_slot. - // and the data in slot 3 (piece x) should be moved to slot y. - - // there are three possible cases. - // 1. There's another piece that should be placed into this slot - // 2. This piece should be placed into another slot. - // 3. There's another piece that should be placed into this slot - // and this piece should be placed into another slot - - // swap piece_index with this slot - - // case 1 - if (this_should_move && !other_should_move) - { - assert(piece_index != m_current_slot); - - const int other_slot = piece_index; - assert(other_slot >= 0); - int other_piece = m_slot_to_piece[other_slot]; - - m_slot_to_piece[other_slot] = piece_index; - m_slot_to_piece[m_current_slot] = other_piece; - m_piece_to_slot[piece_index] = piece_index; - if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot; - - if (other_piece == unassigned) - { - std::vector::iterator i = - std::find(m_free_slots.begin(), m_free_slots.end(), other_slot); - assert(i != m_free_slots.end()); - m_free_slots.erase(i); - m_free_slots.push_back(m_current_slot); - } - - const int slot1_size = static_cast(m_info.piece_size(piece_index)); - const int slot2_size = other_piece >= 0 ? static_cast(m_info.piece_size(other_piece)) : 0; - std::vector buf1(slot1_size); - m_storage.read(&buf1[0], m_current_slot, 0, slot1_size); - if (slot2_size > 0) - { - std::vector buf2(slot2_size); - m_storage.read(&buf2[0], piece_index, 0, slot2_size); - m_storage.write(&buf2[0], m_current_slot, 0, slot2_size); - } - m_storage.write(&buf1[0], piece_index, 0, slot1_size); - assert(m_slot_to_piece[m_current_slot] == unassigned - || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); - } - // case 2 - else if (!this_should_move && other_should_move) - { - assert(piece_index != m_current_slot); - - const int other_piece = m_current_slot; - const int other_slot = m_piece_to_slot[other_piece]; - assert(other_slot >= 0); - - m_slot_to_piece[m_current_slot] = other_piece; - m_slot_to_piece[other_slot] = piece_index; - m_piece_to_slot[other_piece] = m_current_slot; - if (piece_index >= 0) m_piece_to_slot[piece_index] = other_slot; - - if (piece_index == unassigned) - { - m_free_slots.push_back(other_slot); - } - - const int slot1_size = static_cast(m_info.piece_size(other_piece)); - const int slot2_size = piece_index >= 0 ? static_cast(m_info.piece_size(piece_index)) : 0; - std::vector buf1(slot1_size); - m_storage.read(&buf1[0], other_slot, 0, slot1_size); - if (slot2_size > 0) - { - std::vector buf2(slot2_size); - m_storage.read(&buf2[0], m_current_slot, 0, slot2_size); - m_storage.write(&buf2[0], other_slot, 0, slot2_size); - } - m_storage.write(&buf1[0], m_current_slot, 0, slot1_size); - assert(m_slot_to_piece[m_current_slot] == unassigned - || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); - } - else if (this_should_move && other_should_move) - { - assert(piece_index != m_current_slot); - assert(piece_index >= 0); - - const int piece1 = m_slot_to_piece[piece_index]; - const int piece2 = m_current_slot; - const int slot1 = piece_index; - const int slot2 = m_piece_to_slot[piece2]; - - assert(slot1 >= 0); - assert(slot2 >= 0); - assert(piece2 >= 0); - - if (slot1 == slot2) - { - // this means there are only two pieces involved in the swap - assert(piece1 >= 0); - - // movement diagram: - // +-------------------------------+ - // | | - // +--> slot1 --> m_current_slot --+ - - m_slot_to_piece[slot1] = piece_index; - m_slot_to_piece[m_current_slot] = piece1; - - m_piece_to_slot[piece_index] = slot1; - m_piece_to_slot[piece1] = m_current_slot; - - assert(piece1 == m_current_slot); - assert(piece_index == slot1); - - const int slot1_size = static_cast(m_info.piece_size(piece1)); - const int slot3_size = static_cast(m_info.piece_size(piece_index)); - std::vector buf1(static_cast(slot1_size)); - std::vector buf2(static_cast(slot3_size)); - - m_storage.read(&buf2[0], m_current_slot, 0, slot3_size); - m_storage.read(&buf1[0], slot1, 0, slot1_size); - m_storage.write(&buf1[0], m_current_slot, 0, slot1_size); - m_storage.write(&buf2[0], slot1, 0, slot3_size); - - assert(m_slot_to_piece[m_current_slot] == unassigned - || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); - } - else - { - assert(slot1 != slot2); - assert(piece1 != piece2); - - // movement diagram: - // +-----------------------------------------+ - // | | - // +--> slot1 --> slot2 --> m_current_slot --+ - - m_slot_to_piece[slot1] = piece_index; - m_slot_to_piece[slot2] = piece1; - m_slot_to_piece[m_current_slot] = piece2; - - m_piece_to_slot[piece_index] = slot1; - m_piece_to_slot[m_current_slot] = piece2; - if (piece1 >= 0) m_piece_to_slot[piece1] = slot2; - - if (piece1 == unassigned) - { - std::vector::iterator i = - std::find(m_free_slots.begin(), m_free_slots.end(), slot1); - assert(i != m_free_slots.end()); - m_free_slots.erase(i); - m_free_slots.push_back(slot2); - } - - const int slot1_size = piece1 >= 0 ? static_cast(m_info.piece_size(piece1)) : 0; - const int slot2_size = static_cast(m_info.piece_size(piece2)); - const int slot3_size = static_cast(m_info.piece_size(piece_index)); - - std::vector buf1(static_cast(m_info.piece_length())); - std::vector buf2(static_cast(m_info.piece_length())); - - m_storage.read(&buf2[0], m_current_slot, 0, slot3_size); - m_storage.read(&buf1[0], slot2, 0, slot2_size); - m_storage.write(&buf1[0], m_current_slot, 0, slot2_size); - if (slot1_size > 0) - { - m_storage.read(&buf1[0], slot1, 0, slot1_size); - m_storage.write(&buf1[0], slot2, 0, slot1_size); - } - m_storage.write(&buf2[0], slot1, 0, slot3_size); - assert(m_slot_to_piece[m_current_slot] == unassigned - || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); - } - } - else - { - assert(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot); - assert(m_slot_to_piece[m_current_slot] == unallocated); - assert(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot); - - // the slot was identified as piece 'piece_index' - if (piece_index != unassigned) - m_piece_to_slot[piece_index] = m_current_slot; - else - m_free_slots.push_back(m_current_slot); - - m_slot_to_piece[m_current_slot] = piece_index; - - assert(m_slot_to_piece[m_current_slot] == unassigned - || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); - } - } - catch (file_error&) - { - // find the file that failed, and skip all the blocks in that file - size_type file_offset = 0; - size_type current_offset = m_current_slot * m_info.piece_length(); - for (torrent_info::file_iterator i = m_info.begin_files(); - i != m_info.end_files(); ++i) - { - file_offset += i->size; - if (file_offset > current_offset) break; - } - - assert(file_offset > current_offset); - int skip_blocks = static_cast( - (file_offset - current_offset + m_info.piece_length() - 1) - / m_info.piece_length()); - - for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i) - { - assert(m_slot_to_piece[i] == unallocated); - m_unallocated_slots.push_back(i); - } - - // current slot will increase by one at the end of the for-loop too - m_current_slot += skip_blocks - 1; - } - ++m_current_slot; - - if (m_current_slot >= m_info.num_pieces()) - { - assert(m_current_slot == m_info.num_pieces()); - - // clear the memory we've been using - std::vector().swap(m_piece_data); - std::multimap().swap(m_hash_to_piece); - m_state = state_allocating; - assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); - return std::make_pair(false, 1.f); - } - - assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); - - return std::make_pair(false, (float)m_current_slot / m_info.num_pieces()); - } - - bool piece_manager::check_fastresume( - aux::piece_checker_data& d, std::vector& pieces - , int& num_pieces, bool compact_mode) - { - return m_pimpl->check_fastresume(d, pieces, num_pieces, compact_mode); - } - - std::pair piece_manager::check_files( - std::vector& pieces - , int& num_pieces) - { - return m_pimpl->check_files(pieces, num_pieces); - } - - int piece_manager::impl::allocate_slot_for_piece(int piece_index) - { - // synchronization ------------------------------------------------------ - boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- - - // INVARIANT_CHECK; - - assert(piece_index >= 0); - assert(piece_index < (int)m_piece_to_slot.size()); - assert(m_piece_to_slot.size() == m_slot_to_piece.size()); - - int slot_index = m_piece_to_slot[piece_index]; - - if (slot_index != has_no_slot) - { - assert(slot_index >= 0); - assert(slot_index < (int)m_slot_to_piece.size()); - return slot_index; - } - - if (m_free_slots.empty()) - { - allocate_slots(1); - assert(!m_free_slots.empty()); - } - - std::vector::iterator iter( - std::find( - m_free_slots.begin() - , m_free_slots.end() - , piece_index)); - - if (iter == m_free_slots.end()) - { - assert(m_slot_to_piece[piece_index] != unassigned); - assert(!m_free_slots.empty()); - iter = m_free_slots.end() - 1; - - // special case to make sure we don't use the last slot - // when we shouldn't, since it's smaller than ordinary slots - if (*iter == m_info.num_pieces() - 1 && piece_index != *iter) - { - if (m_free_slots.size() == 1) - allocate_slots(1); - assert(m_free_slots.size() > 1); - // assumes that all allocated slots - // are put at the end of the free_slots vector - iter = m_free_slots.end() - 1; - } - } - - slot_index = *iter; - m_free_slots.erase(iter); - - assert(m_slot_to_piece[slot_index] == unassigned); - - m_slot_to_piece[slot_index] = piece_index; - m_piece_to_slot[piece_index] = slot_index; - - // there is another piece already assigned to - // the slot we are interested in, swap positions - if (slot_index != piece_index - && m_slot_to_piece[piece_index] >= 0) - { - -#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG) - std::stringstream s; - - s << "there is another piece at our slot, swapping.."; - - s << "\n piece_index: "; - s << piece_index; - s << "\n slot_index: "; - s << slot_index; - s << "\n piece at our slot: "; - s << m_slot_to_piece[piece_index]; - s << "\n"; - - print_to_log(s.str()); - debug_log(); -#endif - - int piece_at_our_slot = m_slot_to_piece[piece_index]; - assert(m_piece_to_slot[piece_at_our_slot] == piece_index); - - std::swap( - m_slot_to_piece[piece_index] - , m_slot_to_piece[slot_index]); - - std::swap( - m_piece_to_slot[piece_index] - , m_piece_to_slot[piece_at_our_slot]); - - const int slot_size = static_cast(m_info.piece_size(slot_index)); - std::vector buf(slot_size); - m_storage.read(&buf[0], piece_index, 0, slot_size); - m_storage.write(&buf[0], slot_index, 0, slot_size); - - assert(m_slot_to_piece[piece_index] == piece_index); - assert(m_piece_to_slot[piece_index] == piece_index); - - slot_index = piece_index; - -#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG) - debug_log(); -#endif - } - - assert(slot_index >= 0); - assert(slot_index < (int)m_slot_to_piece.size()); - return slot_index; - } - - namespace - { - // this is used to notify potential other - // threads that the allocation-function has exited - struct allocation_syncronization - { - allocation_syncronization( - bool& flag - , boost::condition& cond - , boost::mutex& monitor) - : m_flag(flag) - , m_cond(cond) - , m_monitor(monitor) - { - boost::mutex::scoped_lock lock(m_monitor); - - while (m_flag) - m_cond.wait(lock); - - m_flag = true; - } - - ~allocation_syncronization() - { - boost::mutex::scoped_lock lock(m_monitor); - m_flag = false; - m_cond.notify_one(); - } - - bool& m_flag; - boost::condition& m_cond; - boost::mutex& m_monitor; - }; - - } - - void piece_manager::impl::allocate_slots(int num_slots) - { - assert(num_slots > 0); - - // this object will syncronize the allocation with - // potential other threads - allocation_syncronization sync_obj( - m_allocating - , m_allocating_condition - , m_allocating_monitor); - - // synchronization ------------------------------------------------------ - boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- - - // INVARIANT_CHECK; - - assert(!m_unallocated_slots.empty()); - - const int piece_size = static_cast(m_info.piece_length()); - - std::vector& buffer = m_scratch_buffer; - buffer.resize(piece_size); - - for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i) - { - int pos = m_unallocated_slots.front(); - // int piece_pos = pos; - bool write_back = false; - - int new_free_slot = pos; - if (m_piece_to_slot[pos] != has_no_slot) - { - assert(m_piece_to_slot[pos] >= 0); - m_storage.read(&buffer[0], m_piece_to_slot[pos], 0, static_cast(m_info.piece_size(pos))); - new_free_slot = m_piece_to_slot[pos]; - m_slot_to_piece[pos] = pos; - m_piece_to_slot[pos] = pos; - write_back = true; - } - m_unallocated_slots.erase(m_unallocated_slots.begin()); - m_slot_to_piece[new_free_slot] = unassigned; - m_free_slots.push_back(new_free_slot); - - if (write_back || m_fill_mode) - m_storage.write(&buffer[0], pos, 0, static_cast(m_info.piece_size(pos))); - } - - assert(m_free_slots.size() > 0); - } - - void piece_manager::allocate_slots(int num_slots) - { - m_pimpl->allocate_slots(num_slots); - } - - path const& piece_manager::save_path() const - { - return m_pimpl->save_path(); - } - - bool piece_manager::move_storage(path const& save_path) - { - return m_pimpl->move_storage(save_path); - } - -#ifndef NDEBUG - void piece_manager::impl::check_invariant() const - { - // synchronization ------------------------------------------------------ - boost::recursive_mutex::scoped_lock lock(m_mutex); - // ---------------------------------------------------------------------- - if (m_piece_to_slot.empty()) return; - - assert((int)m_piece_to_slot.size() == m_info.num_pieces()); - assert((int)m_slot_to_piece.size() == m_info.num_pieces()); - - for (std::vector::const_iterator i = m_free_slots.begin(); - i != m_free_slots.end(); ++i) - { - assert(*i < (int)m_slot_to_piece.size()); - assert(*i >= 0); - assert(m_slot_to_piece[*i] == unassigned); - assert(std::find(i+1, m_free_slots.end(), *i) - == m_free_slots.end()); - } - - for (std::vector::const_iterator i = m_unallocated_slots.begin(); - i != m_unallocated_slots.end(); ++i) - { - assert(*i < (int)m_slot_to_piece.size()); - assert(*i >= 0); - assert(m_slot_to_piece[*i] == unallocated); - assert(std::find(i+1, m_unallocated_slots.end(), *i) - == m_unallocated_slots.end()); - } - - for (int i = 0; i < m_info.num_pieces(); ++i) - { - // Check domain of piece_to_slot's elements - if (m_piece_to_slot[i] != has_no_slot) - { - assert(m_piece_to_slot[i] >= 0); - assert(m_piece_to_slot[i] < (int)m_slot_to_piece.size()); - } - - // Check domain of slot_to_piece's elements - if (m_slot_to_piece[i] != unallocated - && m_slot_to_piece[i] != unassigned) - { - assert(m_slot_to_piece[i] >= 0); - assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size()); - } - - // do more detailed checks on piece_to_slot - if (m_piece_to_slot[i] >= 0) - { - assert(m_slot_to_piece[m_piece_to_slot[i]] == i); - if (m_piece_to_slot[i] != i) - { - assert(m_slot_to_piece[i] == unallocated); - } - } - else - { - assert(m_piece_to_slot[i] == has_no_slot); - } - - // do more detailed checks on slot_to_piece - - if (m_slot_to_piece[i] >= 0) - { - assert(m_slot_to_piece[i] < (int)m_piece_to_slot.size()); - assert(m_piece_to_slot[m_slot_to_piece[i]] == i); -#ifdef TORRENT_STORAGE_DEBUG - assert( - std::find( - m_unallocated_slots.begin() - , m_unallocated_slots.end() - , i) == m_unallocated_slots.end() - ); - assert( - std::find( - m_free_slots.begin() - , m_free_slots.end() - , i) == m_free_slots.end() - ); -#endif - } - else if (m_slot_to_piece[i] == unallocated) - { -#ifdef TORRENT_STORAGE_DEBUG - assert(m_unallocated_slots.empty() - || (std::find( - m_unallocated_slots.begin() - , m_unallocated_slots.end() - , i) != m_unallocated_slots.end()) - ); -#endif - } - else if (m_slot_to_piece[i] == unassigned) - { -#ifdef TORRENT_STORAGE_DEBUG - assert( - std::find( - m_free_slots.begin() - , m_free_slots.end() - , i) != m_free_slots.end() - ); -#endif - } - else - { - assert(false && "m_slot_to_piece[i] is invalid"); - } - } - } - -#ifdef TORRENT_STORAGE_DEBUG - void piece_manager::impl::debug_log() const - { - std::stringstream s; - - s << "index\tslot\tpiece\n"; - - for (int i = 0; i < m_info.num_pieces(); ++i) - { - s << i << "\t" << m_slot_to_piece[i] << "\t"; - s << m_piece_to_slot[i] << "\n"; - } - - s << "---------------------------------\n"; - - print_to_log(s.str()); - } -#endif -#endif -} // namespace libtorrent - diff --git a/libtorrent/cpp/torrent.cpp b/libtorrent/cpp/torrent.cpp deleted file mode 100755 index 39936ffca..000000000 --- a/libtorrent/cpp/torrent.cpp +++ /dev/null @@ -1,2186 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/torrent_handle.hpp" -#include "libtorrent/session.hpp" -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/tracker_manager.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/peer.hpp" -#include "libtorrent/bt_peer_connection.hpp" -#include "libtorrent/web_peer_connection.hpp" -#include "libtorrent/peer_id.hpp" -#include "libtorrent/alert.hpp" -#include "libtorrent/identify_client.hpp" -#include "libtorrent/alert_types.hpp" -#include "libtorrent/aux_/session_impl.hpp" - -using namespace libtorrent; -using namespace boost::posix_time; -using boost::tuples::tuple; -using boost::tuples::get; -using boost::tuples::make_tuple; -using boost::filesystem::complete; -using boost::bind; -using boost::mutex; -using libtorrent::aux::session_impl; - -// PROFILING CODE - -#ifdef TORRENT_PROFILE -#include - -namespace libtorrent -{ - namespace - { - using boost::posix_time::ptime; - using boost::posix_time::time_duration; - using boost::posix_time::microsec_clock; - std::vector > checkpoints; - } - - void add_checkpoint(std::string const& str) - { - checkpoints.push_back(std::make_pair(microsec_clock::universal_time(), str)); - } - - void print_checkpoints() - { - for (std::vector >::iterator i - = checkpoints.begin(); i != checkpoints.end(); ++i) - { - ptime cur = i->first; - if (i + 1 != checkpoints.end()) - { - time_duration diff = (i + 1)->first - cur; - std::cout << diff.total_microseconds() << " " << i->second << "\n"; - } - else - { - std::cout << " " << i->second << "\n"; - } - } - } -} - -#endif - -namespace -{ - - enum - { - // wait 60 seconds before retrying a failed tracker - tracker_retry_delay_min = 60 - // when tracker_failed_max trackers - // has failed, wait 10 minutes instead - , tracker_retry_delay_max = 10 * 60 - , tracker_failed_max = 5 - }; - - int calculate_block_size(const torrent_info& i, int default_block_size) - { - if (default_block_size < 1024) default_block_size = 1024; - - // if pieces are too small, adjust the block size - if (i.piece_length() < default_block_size) - { - return static_cast(i.piece_length()); - } - - // if pieces are too large, adjust the block size - if (i.piece_length() / default_block_size > piece_picker::max_blocks_per_piece) - { - return static_cast(i.piece_length() / piece_picker::max_blocks_per_piece); - } - - // otherwise, go with the default - return default_block_size; - } - - struct find_peer_by_ip - { - find_peer_by_ip(tcp::endpoint const& a, const torrent* t) - : ip(a) - , tor(t) - { assert(t != 0); } - - bool operator()(const session_impl::connection_map::value_type& c) const - { - tcp::endpoint sender = c.first->remote_endpoint(); - if (sender.address() != ip.address()) return false; - if (tor != c.second->associated_torrent().lock().get()) return false; - return true; - } - - tcp::endpoint const& ip; - torrent const* tor; - }; - - struct peer_by_id - { - peer_by_id(const peer_id& i): pid(i) {} - - bool operator()(const std::pair& p) const - { - if (p.second->pid() != pid) return false; - // have a special case for all zeros. We can have any number - // of peers with that pid, since it's used to indicate no pid. - if (std::count(pid.begin(), pid.end(), 0) == 20) return false; - return true; - } - - peer_id const& pid; - }; -} - -namespace libtorrent -{ - torrent::torrent( - session_impl& ses - , aux::checker_impl& checker - , torrent_info const& tf - , boost::filesystem::path const& save_path - , tcp::endpoint const& net_interface - , bool compact_mode - , int block_size - , session_settings const& s) - : m_torrent_file(tf) - , m_abort(false) - , m_paused(false) - , m_just_paused(false) - , m_event(tracker_request::started) - , m_block_size(0) - , m_storage(0) - , m_next_request(second_clock::universal_time()) - , m_duration(1800) - , m_complete(-1) - , m_incomplete(-1) - , m_host_resolver(ses.m_selector) -#ifndef TORRENT_DISABLE_DHT - , m_dht_announce_timer(ses.m_selector) -#endif - , m_policy() - , m_ses(ses) - , m_checker(checker) - , m_picker(0) - , m_trackers(m_torrent_file.trackers()) - , m_last_working_tracker(-1) - , m_currently_trying_tracker(0) - , m_failed_trackers(0) - , m_time_scaler(0) - , m_priority(.5) - , m_num_pieces(0) - , m_got_tracker_response(false) - , m_ratio(0.f) - , m_total_failed_bytes(0) - , m_total_redundant_bytes(0) - , m_net_interface(net_interface.address(), 0) - , m_upload_bandwidth_limit(std::numeric_limits::max()) - , m_download_bandwidth_limit(std::numeric_limits::max()) - , m_save_path(complete(save_path)) - , m_compact_mode(compact_mode) - , m_metadata_progress(0) - , m_metadata_size(0) - , m_default_block_size(block_size) - , m_connections_initialized(true) - , m_settings(s) - { -#ifndef NDEBUG - m_initial_done = 0; -#endif - INVARIANT_CHECK; - - m_uploads_quota.min = 2; - m_connections_quota.min = 2; - // this will be corrected the next time the main session - // distributes resources, i.e. on average in 0.5 seconds - m_connections_quota.given = 100; - m_uploads_quota.max = std::numeric_limits::max(); - m_connections_quota.max = std::numeric_limits::max(); - - m_dl_bandwidth_quota.min = 100; - m_dl_bandwidth_quota.max = resource_request::inf; - - if (m_ses.m_download_rate == -1) - { - m_dl_bandwidth_quota.given = resource_request::inf; - } - else - { - m_dl_bandwidth_quota.given = 400; - } - - m_ul_bandwidth_quota.min = 100; - m_ul_bandwidth_quota.max = resource_request::inf; - - if (m_ses.m_upload_rate == -1) - { - m_ul_bandwidth_quota.given = resource_request::inf; - } - else - { - m_ul_bandwidth_quota.given = 400; - } - - m_policy.reset(new policy(this)); - init(); - -#ifndef TORRENT_DISABLE_DHT - if (!tf.priv()) - { - m_dht_announce_timer.expires_from_now(seconds(10)); - m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1)); - } -#endif - } - - torrent::torrent( - session_impl& ses - , aux::checker_impl& checker - , char const* tracker_url - , sha1_hash const& info_hash - , boost::filesystem::path const& save_path - , tcp::endpoint const& net_interface - , bool compact_mode - , int block_size - , session_settings const& s) - : m_torrent_file(info_hash) - , m_abort(false) - , m_paused(false) - , m_just_paused(false) - , m_event(tracker_request::started) - , m_block_size(0) - , m_storage(0) - , m_next_request(second_clock::universal_time()) - , m_duration(1800) - , m_complete(-1) - , m_incomplete(-1) - , m_host_resolver(ses.m_selector) -#ifndef TORRENT_DISABLE_DHT - , m_dht_announce_timer(ses.m_selector) -#endif - , m_policy() - , m_ses(ses) - , m_checker(checker) - , m_picker(0) - , m_last_working_tracker(-1) - , m_currently_trying_tracker(0) - , m_failed_trackers(0) - , m_time_scaler(0) - , m_priority(.5) - , m_num_pieces(0) - , m_got_tracker_response(false) - , m_ratio(0.f) - , m_total_failed_bytes(0) - , m_total_redundant_bytes(0) - , m_net_interface(net_interface.address(), 0) - , m_upload_bandwidth_limit(std::numeric_limits::max()) - , m_download_bandwidth_limit(std::numeric_limits::max()) - , m_save_path(complete(save_path)) - , m_compact_mode(compact_mode) - , m_metadata_progress(0) - , m_metadata_size(0) - , m_default_block_size(block_size) - , m_connections_initialized(false) - , m_settings(s) - { -#ifndef NDEBUG - m_initial_done = 0; -#endif - INVARIANT_CHECK; - - m_uploads_quota.min = 2; - m_connections_quota.min = 2; - // this will be corrected the next time the main session - // distributes resources, i.e. on average in 0.5 seconds - m_connections_quota.given = 100; - m_uploads_quota.max = std::numeric_limits::max(); - m_connections_quota.max = std::numeric_limits::max(); - - m_dl_bandwidth_quota.min = 100; - m_dl_bandwidth_quota.max = resource_request::inf; - - if (m_ses.m_download_rate == -1) - { - m_dl_bandwidth_quota.given = resource_request::inf; - } - else - { - m_dl_bandwidth_quota.given = 400; - } - - m_ul_bandwidth_quota.min = 100; - m_ul_bandwidth_quota.max = resource_request::inf; - - - if (m_ses.m_upload_rate == -1) - { - m_ul_bandwidth_quota.given = resource_request::inf; - } - else - { - m_ul_bandwidth_quota.given = 400; - } - - m_trackers.push_back(announce_entry(tracker_url)); - m_requested_metadata.resize(256, 0); - - m_policy.reset(new policy(this)); - m_torrent_file.add_tracker(tracker_url); -#ifndef TORRENT_DISABLE_DHT - m_dht_announce_timer.expires_from_now(seconds(10)); - m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1)); -#endif - } - - torrent::~torrent() - { - // The invariant can't be maintained here, since the torrent - // is being destructed, all weak references to it have been - // reset, which means that all its peers already have an - // invalidated torrent pointer (so it cannot be verified to be correct) - - // i.e. the invariant can only be maintained if all connections have - // been closed by the time the torrent is destructed. And they are - // supposed to be closed. So we can still do the invariant check. - - assert(m_connections.empty()); - - INVARIANT_CHECK; - - if (m_ses.is_aborted()) - m_abort = true; - if (!m_connections.empty()) - disconnect_all(); - } - - void torrent::init() - { - INVARIANT_CHECK; - - assert(m_torrent_file.is_valid()); - assert(m_torrent_file.num_files() > 0); - assert(m_torrent_file.total_size() >= 0); - - m_have_pieces.resize(m_torrent_file.num_pieces(), false); - m_storage.reset(new piece_manager(m_torrent_file, m_save_path)); - m_block_size = calculate_block_size(m_torrent_file, m_default_block_size); - m_picker.reset(new piece_picker( - static_cast(m_torrent_file.piece_length() / m_block_size) - , static_cast((m_torrent_file.total_size()+m_block_size-1)/m_block_size))); - - std::vector const& url_seeds = m_torrent_file.url_seeds(); - std::copy(url_seeds.begin(), url_seeds.end(), std::inserter(m_web_seeds - , m_web_seeds.begin())); - } - - void torrent::use_interface(const char* net_interface) - { - INVARIANT_CHECK; - - m_net_interface = tcp::endpoint(address::from_string(net_interface), 0); - } - -#ifndef TORRENT_DISABLE_DHT - - void torrent::on_dht_announce_response_disp(boost::weak_ptr t - , std::vector const& peers) - { - boost::shared_ptr tor = t.lock(); - if (!tor) return; - tor->on_dht_announce_response(peers); - } - - void torrent::on_dht_announce(asio::error const& e) - { - if (e) return; - m_dht_announce_timer.expires_from_now(boost::posix_time::minutes(30)); - m_dht_announce_timer.async_wait(bind(&torrent::on_dht_announce, this, _1)); - if (!m_ses.m_dht) return; - // TODO: There should be a way to abort an announce operation on the dht. - // when the torrent is destructed - boost::weak_ptr self(shared_from_this()); - m_ses.m_dht->announce(m_torrent_file.info_hash() - , m_ses.m_listen_interface.port() - , bind(&torrent::on_dht_announce_response_disp, self, _1)); - } - - void torrent::on_dht_announce_response(std::vector const& peers) - { - std::for_each(peers.begin(), peers.end(), bind( - &policy::peer_from_tracker, boost::ref(m_policy), _1, peer_id(0))); - } - -#endif - - // returns true if it is time for this torrent to make another - // tracker request - bool torrent::should_request() - { - INVARIANT_CHECK; - - if (m_torrent_file.trackers().empty()) return false; - - if (m_just_paused) - { - m_just_paused = false; - return true; - } - return !m_paused && - m_next_request < second_clock::universal_time(); - } - - void torrent::tracker_warning(std::string const& msg) - { - INVARIANT_CHECK; - - if (m_ses.m_alerts.should_post(alert::warning)) - { - m_ses.m_alerts.post_alert(tracker_warning_alert(get_handle(), msg)); - } - } - - void torrent::tracker_response( - tracker_request const& - , std::vector& peer_list - , int interval - , int complete - , int incomplete) - { - INVARIANT_CHECK; - - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - m_failed_trackers = 0; - // less than 5 minutes announce intervals - // are insane. - if (interval < 60 * 5) interval = 60 * 5; - - m_last_working_tracker - = prioritize_tracker(m_currently_trying_tracker); - m_currently_trying_tracker = 0; - - m_duration = interval; - m_next_request = second_clock::universal_time() + boost::posix_time::seconds(m_duration); - - if (complete >= 0) m_complete = complete; - if (incomplete >= 0) m_incomplete = incomplete; - - // connect to random peers from the list - std::random_shuffle(peer_list.begin(), peer_list.end()); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - std::stringstream s; - s << "TRACKER RESPONSE:\n" - "interval: " << m_duration << "\n" - "peers:\n"; - for (std::vector::const_iterator i = peer_list.begin(); - i != peer_list.end(); ++i) - { - s << " " << std::setfill(' ') << std::setw(16) << i->ip - << " " << std::setw(5) << std::dec << i->port << " "; - if (!i->pid.is_all_zeros()) s << " " << i->pid << " " << identify_client(i->pid); - s << "\n"; - } - debug_log(s.str()); -#endif - // for each of the peers we got from the tracker - for (std::vector::iterator i = peer_list.begin(); - i != peer_list.end(); ++i) - { - // don't make connections to ourself - if (i->pid == m_ses.get_peer_id()) - continue; - - tcp::endpoint a(address::from_string(i->ip), i->port); - - if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - debug_log("blocked ip from tracker: " + i->ip); -#endif - continue; - } - - m_policy->peer_from_tracker(a, i->pid); - } - - if (m_ses.m_alerts.should_post(alert::info)) - { - std::stringstream s; - s << "Got response from tracker: " - << m_trackers[m_last_working_tracker].url; - m_ses.m_alerts.post_alert(tracker_reply_alert( - get_handle(), s.str())); - } - m_got_tracker_response = true; - } - - size_type torrent::bytes_left() const - { - // if we don't have the metadata yet, we - // cannot tell how big the torrent is. - if (!valid_metadata()) return -1; - return m_torrent_file.total_size() - - quantized_bytes_done(); - } - - size_type torrent::quantized_bytes_done() const - { - INVARIANT_CHECK; - - if (!valid_metadata()) return 0; - - assert(m_picker.get()); - - if (m_torrent_file.num_pieces() == 0) - return 0; - const int last_piece = m_torrent_file.num_pieces() - 1; - - size_type total_done - = m_num_pieces * m_torrent_file.piece_length(); - - // if we have the last piece, we have to correct - // the amount we have, since the first calculation - // assumed all pieces were of equal size - if (m_have_pieces[last_piece]) - { - int corr = m_torrent_file.piece_size(last_piece) - - m_torrent_file.piece_length(); - total_done += corr; - } - return total_done; - } - - // the first value is the total number of bytes downloaded - // the second value is the number of bytes of those that haven't - // been filtered as not wanted we have downloaded - tuple torrent::bytes_done() const - { - INVARIANT_CHECK; - - if (!valid_metadata()) return tuple(0,0); - - assert(m_picker.get()); - - if (m_torrent_file.num_pieces() == 0) - return tuple(0,0); - const int last_piece = m_torrent_file.num_pieces() - 1; - - size_type wanted_done = (m_num_pieces - m_picker->num_have_filtered()) - * m_torrent_file.piece_length(); - - size_type total_done - = m_num_pieces * m_torrent_file.piece_length(); - - // if we have the last piece, we have to correct - // the amount we have, since the first calculation - // assumed all pieces were of equal size - if (m_have_pieces[last_piece]) - { - int corr = m_torrent_file.piece_size(last_piece) - - m_torrent_file.piece_length(); - total_done += corr; - if (!m_picker->is_filtered(last_piece)) - wanted_done += corr; - } - - const std::vector& dl_queue - = m_picker->get_download_queue(); - - const int blocks_per_piece = static_cast( - m_torrent_file.piece_length() / m_block_size); - - for (std::vector::const_iterator i = - dl_queue.begin(); i != dl_queue.end(); ++i) - { - int corr = 0; - assert(!m_have_pieces[i->index]); - - for (int j = 0; j < blocks_per_piece; ++j) - { - corr += (i->finished_blocks[j]) * m_block_size; - } - - // correction if this was the last piece - // and if we have the last block - if (i->index == last_piece - && i->finished_blocks[m_picker->blocks_in_last_piece()-1]) - { - corr -= m_block_size; - corr += m_torrent_file.piece_size(last_piece) % m_block_size; - } - total_done += corr; - if (!m_picker->is_filtered(i->index)) - wanted_done += corr; - } - - std::map downloading_piece; - for (const_peer_iterator i = begin(); i != end(); ++i) - { - peer_connection* pc = i->second; - boost::optional p - = pc->downloading_piece_progress(); - if (p) - { - if (m_have_pieces[p->piece_index]) - continue; - - piece_block block(p->piece_index, p->block_index); - if (m_picker->is_finished(block)) - continue; - - std::map::iterator dp - = downloading_piece.find(block); - if (dp != downloading_piece.end()) - { - if (dp->second < p->bytes_downloaded) - dp->second = p->bytes_downloaded; - } - else - { - downloading_piece[block] = p->bytes_downloaded; - } - assert(p->bytes_downloaded <= p->full_block_bytes); - } - } - for (std::map::iterator i = downloading_piece.begin(); - i != downloading_piece.end(); ++i) - { - total_done += i->second; - if (!m_picker->is_filtered(i->first.piece_index)) - wanted_done += i->second; - } - return make_tuple(total_done, wanted_done); - } - - void torrent::piece_failed(int index) - { - INVARIANT_CHECK; - - assert(m_storage.get()); - assert(m_picker.get()); - assert(index >= 0); - assert(index < m_torrent_file.num_pieces()); - - if (m_ses.m_alerts.should_post(alert::info)) - { - std::stringstream s; - s << "hash for piece " << index << " failed"; - m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index, s.str())); - } - // increase the total amount of failed bytes - m_total_failed_bytes += m_torrent_file.piece_size(index); - - std::vector downloaders; - m_picker->get_downloaders(downloaders, index); - - // decrease the trust point of all peers that sent - // parts of this piece. - // first, build a set of all peers that participated - std::set peers; - std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); - - for (std::set::iterator i = peers.begin() - , end(peers.end()); i != end; ++i) - { - peer_iterator p = m_connections.find(*i); - if (p == m_connections.end()) continue; - p->second->received_invalid_data(); - - // either, we have received too many failed hashes - // or this was the only peer that sent us this piece. - // TODO: make this a changable setting - if (p->second->trust_points() <= -7 || peers.size() == 1) - { - // we don't trust this peer anymore - // ban it. - if (m_ses.m_alerts.should_post(alert::info)) - { - m_ses.m_alerts.post_alert(peer_ban_alert( - p->first - , get_handle() - , "banning peer because of too many corrupt pieces")); - } - m_policy->ban_peer(*p->second); - -#if defined(TORRENT_VERBOSE_LOGGING) - (*p->second->m_logger) << "*** BANNING PEER 'too many corrupt pieces'\n"; -#endif - p->second->disconnect(); - } - } - - // we have to let the piece_picker know that - // this piece failed the check as it can restore it - // and mark it as being interesting for download - // TODO: do this more intelligently! and keep track - // of how much crap (data that failed hash-check) and - // how much redundant data we have downloaded - // if some clients has sent more than one piece - // start with redownloading the pieces that the client - // that has sent the least number of pieces - m_picker->restore_piece(index); - m_storage->mark_failed(index); - - assert(m_have_pieces[index] == false); - } - - void torrent::abort() - { - INVARIANT_CHECK; - - m_abort = true; - // if the torrent is paused, it doesn't need - // to announce with even=stopped again. - if (!m_paused) - m_event = tracker_request::stopped; - // disconnect all peers and close all - // files belonging to the torrents - disconnect_all(); - if (m_storage.get()) m_storage->release_files(); - } - - void torrent::announce_piece(int index) - { - INVARIANT_CHECK; - - assert(m_picker.get()); - assert(index >= 0); - assert(index < m_torrent_file.num_pieces()); - - std::vector downloaders; - m_picker->get_downloaders(downloaders, index); - - // increase the trust point of all peers that sent - // parts of this piece. - std::set peers; - std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); - - for (std::set::iterator i = peers.begin() - , end(peers.end()); i != end; ++i) - { - peer_iterator p = m_connections.find(*i); - if (p == m_connections.end()) continue; - p->second->received_valid_data(); - } - - m_picker->we_have(index); - for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) - i->second->announce_piece(index); - } - - std::string torrent::tracker_login() const - { - if (m_username.empty() && m_password.empty()) return ""; - return m_username + ":" + m_password; - } - - void torrent::filter_piece(int index, bool filter) - { - INVARIANT_CHECK; - - // this call is only valid on torrents with metadata - assert(m_picker.get()); - assert(index >= 0); - assert(index < m_torrent_file.num_pieces()); - - // TODO: update peer's interesting-bit - - if (filter) m_picker->mark_as_filtered(index); - else m_picker->mark_as_unfiltered(index); - } - - void torrent::filter_pieces(std::vector const& bitmask) - { - INVARIANT_CHECK; - - // this call is only valid on torrents with metadata - assert(m_picker.get()); - - // TODO: update peer's interesting-bit - - std::vector state; - state.reserve(100); - int index = 0; - for (std::vector::const_iterator i = bitmask.begin() - , end(bitmask.end()); i != end; ++i, ++index) - { - if (m_picker->is_filtered(index) == *i) continue; - if (*i) - m_picker->mark_as_filtered(index); - else - state.push_back(index); - } - - for (std::vector::reverse_iterator i = state.rbegin(); - i != state.rend(); ++i) - { - m_picker->mark_as_unfiltered(*i); - } - } - - bool torrent::is_piece_filtered(int index) const - { - // this call is only valid on torrents with metadata - assert(m_picker.get()); - assert(index >= 0); - assert(index < m_torrent_file.num_pieces()); - - return m_picker->is_filtered(index); - } - - void torrent::filtered_pieces(std::vector& bitmask) const - { - INVARIANT_CHECK; - - // this call is only valid on torrents with metadata - assert(m_picker.get()); - m_picker->filtered_pieces(bitmask); - } - - void torrent::filter_files(std::vector const& bitmask) - { - INVARIANT_CHECK; - - // this call is only valid on torrents with metadata - if (!valid_metadata()) return; - - // the bitmask need to have exactly one bit for every file - // in the torrent - assert((int)bitmask.size() == m_torrent_file.num_files()); - - size_type position = 0; - - if (m_torrent_file.num_pieces()) - { - int piece_length = m_torrent_file.piece_length(); - // mark all pieces as filtered, then clear the bits for files - // that should be downloaded - std::vector piece_filter(m_torrent_file.num_pieces(), true); - for (int i = 0; i < (int)bitmask.size(); ++i) - { - size_type start = position; - position += m_torrent_file.file_at(i).size; - // is the file selected for download? - if (!bitmask[i]) - { - // mark all pieces of the file as downloadable - int start_piece = int(start / piece_length); - int last_piece = int(position / piece_length); - // if one piece spans several files, we might - // come here several times with the same start_piece, end_piece - std::fill(piece_filter.begin() + start_piece, piece_filter.begin() - + last_piece + 1, false); - } - } - filter_pieces(piece_filter); - } - } - - void torrent::replace_trackers(std::vector const& urls) - { - assert(!urls.empty()); - m_trackers = urls; - if (m_currently_trying_tracker >= (int)m_trackers.size()) - m_currently_trying_tracker = (int)m_trackers.size()-1; - m_last_working_tracker = -1; - } - - tracker_request torrent::generate_tracker_request() - { - INVARIANT_CHECK; - - m_next_request - = second_clock::universal_time() - + boost::posix_time::seconds(tracker_retry_delay_max); - - tracker_request req; - req.info_hash = m_torrent_file.info_hash(); - req.pid = m_ses.get_peer_id(); - req.downloaded = m_stat.total_payload_download(); - req.uploaded = m_stat.total_payload_upload(); - req.left = bytes_left(); - if (req.left == -1) req.left = 16*1024; - req.event = m_event; - - if (m_event != tracker_request::stopped) - m_event = tracker_request::none; - req.url = m_trackers[m_currently_trying_tracker].url; - assert(m_connections_quota.given > 0); - req.num_want = std::max( - (m_connections_quota.given - - m_policy->num_peers()), 10); - // if we are aborting. we don't want any new peers - if (req.event == tracker_request::stopped) - req.num_want = 0; - - // default initialize, these should be set by caller - // before passing the request to the tracker_manager - req.listen_port = 0; - req.key = 0; - - return req; - } - - void torrent::remove_peer(peer_connection* p) try - { - INVARIANT_CHECK; - - assert(p != 0); - - peer_iterator i = m_connections.find(p->remote()); - if (i == m_connections.end()) return; - - if (ready_for_connections()) - { - assert(p->associated_torrent().lock().get() == this); - - std::vector piece_list; - const std::vector& pieces = p->get_bitfield(); - - for (std::vector::const_iterator i = pieces.begin(); - i != pieces.end(); ++i) - { - if (*i) piece_list.push_back(static_cast(i - pieces.begin())); - } - - for (std::vector::reverse_iterator i = piece_list.rbegin(); - i != piece_list.rend(); ++i) - { - peer_lost(*i); - } - } - - m_policy->connection_closed(*p); - m_connections.erase(i); -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - } - catch (std::exception& e) - { -#ifndef NDEBUG - std::string err = e.what(); -#endif - assert(false); - }; - - void torrent::connect_to_url_seed(std::string const& url) - { - INVARIANT_CHECK; - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - std::string now(to_simple_string(second_clock::universal_time())); - (*m_ses.m_logger) << now << " resolving: " << url << "\n"; -#endif - - std::string protocol; - std::string hostname; - int port; - std::string path; - boost::tie(protocol, hostname, port, path) - = parse_url_components(url); - - m_resolving_web_seeds.insert(url); - if (m_ses.settings().proxy_ip.empty()) - { - tcp::resolver::query q(hostname, boost::lexical_cast(port)); - m_host_resolver.async_resolve(q, bind(&torrent::on_name_lookup - , shared_from_this(), _1, _2, url)); - } - else - { - // use proxy - tcp::resolver::query q(m_ses.settings().proxy_ip - , boost::lexical_cast(m_ses.settings().proxy_port)); - m_host_resolver.async_resolve(q, bind(&torrent::on_name_lookup - , shared_from_this(), _1, _2, url)); - } - - } - - void torrent::on_name_lookup(asio::error const& e, tcp::resolver::iterator host - , std::string url) try - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - INVARIANT_CHECK; - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - std::string now(to_simple_string(second_clock::universal_time())); - (*m_ses.m_logger) << now << " completed resolve: " << url << "\n"; -#endif - - std::set::iterator i = m_resolving_web_seeds.find(url); - if (i != m_resolving_web_seeds.end()) m_resolving_web_seeds.erase(i); - - if (e || host == tcp::resolver::iterator()) - { - if (m_ses.m_alerts.should_post(alert::warning)) - { - std::stringstream msg; - msg << "HTTP seed hostname lookup failed: " << e.what(); - m_ses.m_alerts.post_alert( - url_seed_alert(url, msg.str())); - } -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << url << "\n"; -#endif - - // the name lookup failed for the http host. Don't try - // this host again - remove_url_seed(url); - return; - } - - if (m_ses.is_aborted()) return; - - tcp::endpoint a(host->endpoint()); - - boost::shared_ptr s(new stream_socket(m_ses.m_selector)); - boost::intrusive_ptr c(new web_peer_connection( - m_ses, shared_from_this(), s, a, url)); -#ifndef NDEBUG - c->m_in_constructor = false; -#endif - - try - { - m_ses.m_connection_queue.push_back(c); - - assert(m_connections.find(a) == m_connections.end()); - -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - // add the newly connected peer to this torrent's peer list - m_connections.insert( - std::make_pair(a, boost::get_pointer(c))); - -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - - m_ses.process_connection_queue(); - } - catch (std::exception& e) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << e.what() << "\n"; -#endif - - // TODO: post an error alert! - std::map::iterator i = m_connections.find(a); - if (i != m_connections.end()) m_connections.erase(i); - m_ses.connection_failed(s, a, e.what()); - c->disconnect(); - } - } - catch (std::exception& exc) - { - assert(false); - }; - - peer_connection& torrent::connect_to_peer(const tcp::endpoint& a) - { - INVARIANT_CHECK; - - if (m_connections.find(a) != m_connections.end()) - throw protocol_error("already connected to peer"); - - boost::shared_ptr s(new stream_socket(m_ses.m_selector)); - boost::intrusive_ptr c(new bt_peer_connection( - m_ses, shared_from_this(), s, a)); -#ifndef NDEBUG - c->m_in_constructor = false; -#endif - - try - { - m_ses.m_connection_queue.push_back(c); - - assert(m_connections.find(a) == m_connections.end()); - -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - // add the newly connected peer to this torrent's peer list - m_connections.insert( - std::make_pair(a, boost::get_pointer(c))); - -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - - m_ses.process_connection_queue(); - } - catch (std::exception& e) - { - // TODO: post an error alert! - std::map::iterator i = m_connections.find(a); - if (i != m_connections.end()) m_connections.erase(i); - m_ses.connection_failed(s, a, e.what()); - c->disconnect(); - throw; - } - if (c->is_disconnecting()) throw protocol_error("failed to connect"); - return *c; - } - - void torrent::attach_peer(peer_connection* p) - { - INVARIANT_CHECK; - - assert(p != 0); - assert(!p->is_local()); - - std::map::iterator c - = m_connections.find(p->remote()); - if (c != m_connections.end()) - { - // we already have a peer_connection to this ip. - // It may currently be waiting for completing a - // connection attempt that might fail. So, - // prioritize this current connection since - // it has already succeeded. - if (!c->second->is_connecting()) - { - throw protocol_error("already connected to peer"); - } - c->second->disconnect(); - } - - if (m_ses.m_connections.find(p->get_socket()) - == m_ses.m_connections.end()) - { - throw protocol_error("peer is not properly constructed"); - } - - if (m_ses.is_aborted()) - { - throw protocol_error("session is closing"); - } - - peer_iterator i = m_connections.insert( - std::make_pair(p->remote(), p)).first; - - try - { - // if new_connection throws, we have to remove the - // it from the list. - - m_policy->new_connection(*i->second); - } - catch (std::exception& e) - { - m_connections.erase(i); - throw; - } -#ifndef NDEBUG - assert(p->remote() == p->get_socket()->remote_endpoint()); -#endif - -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - } - - void torrent::disconnect_all() - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - INVARIANT_CHECK; - - while (!m_connections.empty()) - { - peer_connection& p = *m_connections.begin()->second; - assert(p.associated_torrent().lock().get() == this); - -#if defined(TORRENT_VERBOSE_LOGGING) - if (m_abort) - (*p.m_logger) << "*** CLOSING CONNECTION 'aborting'\n"; - else - (*p.m_logger) << "*** CLOSING CONNECTION 'pausing'\n"; -#endif -#ifndef NDEBUG - std::size_t size = m_connections.size(); -#endif - p.disconnect(); - assert(m_connections.size() <= size); - } - } - - // called when torrent is finished (all interested pieces downloaded) - void torrent::finished() - { - INVARIANT_CHECK; - - if (alerts().should_post(alert::info)) - { - alerts().post_alert(torrent_finished_alert( - get_handle() - , "torrent has finished downloading")); - } - - // disconnect all seeds - std::vector seeds; - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - assert(i->second->associated_torrent().lock().get() == this); - if (i->second->is_seed()) - { -#if defined(TORRENT_VERBOSE_LOGGING) - (*i->second->m_logger) << "*** SEED, CLOSING CONNECTION\n"; -#endif - seeds.push_back(i->second); - } - } - std::for_each(seeds.begin(), seeds.end() - , bind(&peer_connection::disconnect, _1)); - - m_storage->release_files(); - } - - // called when torrent is complete (all pieces downloaded) - void torrent::completed() - { - INVARIANT_CHECK; - -/* - if (alerts().should_post(alert::info)) - { - alerts().post_alert(torrent_complete_alert( - get_handle() - , "torrent is complete")); - } -*/ - // make the next tracker request - // be a completed-event - m_event = tracker_request::completed; - force_tracker_request(); - } - - // this will move the tracker with the given index - // to a prioritized position in the list (move it towards - // the begining) and return the new index to the tracker. - int torrent::prioritize_tracker(int index) - { - INVARIANT_CHECK; - - assert(index >= 0); - if (index >= (int)m_trackers.size()) return (int)m_trackers.size()-1; - - while (index > 0 && m_trackers[index].tier == m_trackers[index-1].tier) - { - std::swap(m_trackers[index].url, m_trackers[index-1].url); - --index; - } - return index; - } - - void torrent::try_next_tracker() - { - INVARIANT_CHECK; - - using namespace boost::posix_time; - ++m_currently_trying_tracker; - - if ((unsigned)m_currently_trying_tracker >= m_trackers.size()) - { - int delay = tracker_retry_delay_min - + std::min(m_failed_trackers, (int)tracker_failed_max) - * (tracker_retry_delay_max - tracker_retry_delay_min) - / tracker_failed_max; - - ++m_failed_trackers; - // if we've looped the tracker list, wait a bit before retrying - m_currently_trying_tracker = 0; - m_next_request = second_clock::universal_time() + seconds(delay); - } - else - { - // don't delay before trying the next tracker - m_next_request = second_clock::universal_time(); - } - - } - - bool torrent::check_fastresume(aux::piece_checker_data& data) - { - INVARIANT_CHECK; - - if (!m_storage.get()) - { - // this means we have received the metadata through the - // metadata extension, and we have to initialize - init(); - } - - assert(m_storage.get()); - bool done = m_storage->check_fastresume(data, m_have_pieces, m_num_pieces - , m_compact_mode); -#ifndef NDEBUG - m_initial_done = boost::get<0>(bytes_done()); -#endif - return done; - } - - std::pair torrent::check_files() - { - INVARIANT_CHECK; - - assert(m_storage.get()); - std::pair progress = m_storage->check_files(m_have_pieces, m_num_pieces); - -#ifndef NDEBUG - m_initial_done = boost::get<0>(bytes_done()); -#endif - return progress; - } - - void torrent::files_checked(std::vector const& - unfinished_pieces) - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - INVARIANT_CHECK; - - m_picker->files_checked(m_have_pieces, unfinished_pieces); - if (!m_connections_initialized) - { - m_connections_initialized = true; - // all peer connections have to initialize themselves now that the metadata - // is available - typedef std::map conn_map; - for (conn_map::iterator i = m_connections.begin() - , end(m_connections.end()); i != end;) - { - try { i->second->init(); ++i;} - catch (std::exception& e) - { - // the connection failed, close it - conn_map::iterator j = i; - ++j; - m_ses.connection_failed(i->second->get_socket() - , i->first, e.what()); - i = j; - } - } - } -#ifndef NDEBUG - m_initial_done = boost::get<0>(bytes_done()); -#endif - } - - alert_manager& torrent::alerts() const - { - return m_ses.m_alerts; - } - - boost::filesystem::path torrent::save_path() const - { - return m_save_path; - } - - bool torrent::move_storage(boost::filesystem::path const& save_path) - { - INVARIANT_CHECK; - - bool ret = true; - if (m_storage.get()) - { - ret = m_storage->move_storage(save_path); - m_save_path = m_storage->save_path(); - } - else - { - m_save_path = save_path; - } - return ret; - } - - piece_manager& torrent::filesystem() - { - INVARIANT_CHECK; - - assert(m_storage.get()); - return *m_storage; - } - - - torrent_handle torrent::get_handle() const - { - INVARIANT_CHECK; - - return torrent_handle(&m_ses, 0, m_torrent_file.info_hash()); - } - - session_settings const& torrent::settings() const - { - INVARIANT_CHECK; - - return m_ses.settings(); - } - -#ifndef NDEBUG - void torrent::check_invariant() const - { -// size_type download = m_stat.total_payload_download(); -// size_type done = boost::get<0>(bytes_done()); -// assert(download >= done - m_initial_done); - for (const_peer_iterator i = begin(); i != end(); ++i) - { - peer_connection const& p = *i->second; - torrent* associated_torrent = p.associated_torrent().lock().get(); - if (associated_torrent != this) - assert(false); - } - -// This check is very expensive. -// assert(m_num_pieces -// == std::count(m_have_pieces.begin(), m_have_pieces.end(), true)); - assert(m_priority >= 0.f && m_priority < 1.f); - assert(!valid_metadata() || m_block_size > 0); - assert(!valid_metadata() || (m_torrent_file.piece_length() % m_block_size) == 0); - } -#endif - - void torrent::set_sequenced_download_threshold(int threshold) - { - if (valid_metadata()) - picker().set_sequenced_download_threshold(threshold); - } - - - void torrent::set_max_uploads(int limit) - { - assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); - m_uploads_quota.max = std::max(m_uploads_quota.min, limit); - } - - void torrent::set_max_connections(int limit) - { - assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); - m_connections_quota.max = std::max(m_connections_quota.min, limit); - } - - void torrent::set_peer_upload_limit(tcp::endpoint ip, int limit) - { - assert(limit >= -1); - peer_connection* p = connection_for(ip); - if (p == 0) return; - p->set_upload_limit(limit); - } - - void torrent::set_peer_download_limit(tcp::endpoint ip, int limit) - { - assert(limit >= -1); - peer_connection* p = connection_for(ip); - if (p == 0) return; - p->set_download_limit(limit); - } - - void torrent::set_upload_limit(int limit) - { - assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); - if (limit < num_peers() * 10) limit = num_peers() * 10; - m_upload_bandwidth_limit = limit; - } - - void torrent::set_download_limit(int limit) - { - assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); - if (limit < num_peers() * 10) limit = num_peers() * 10; - m_download_bandwidth_limit = limit; - } - - void torrent::pause() - { - INVARIANT_CHECK; - - if (m_paused) return; - disconnect_all(); - m_paused = true; - // tell the tracker that we stopped - m_event = tracker_request::stopped; - m_just_paused = true; - // this will make the storage close all - // files and flush all cached data - if (m_storage.get()) m_storage->release_files(); - } - - void torrent::resume() - { - INVARIANT_CHECK; - - if (!m_paused) return; - m_paused = false; - - // tell the tracker that we're back - m_event = tracker_request::started; - force_tracker_request(); - - // make pulse be called as soon as possible - m_time_scaler = 0; - } - - void torrent::second_tick(stat& accumulator, float tick_interval) - { - INVARIANT_CHECK; - - m_connections_quota.used = (int)m_connections.size(); - m_uploads_quota.used = m_policy->num_uploads(); - - m_ul_bandwidth_quota.used = 0; - m_ul_bandwidth_quota.max = 0; - m_ul_bandwidth_quota.min = 0; - - m_dl_bandwidth_quota.used = 0; - m_dl_bandwidth_quota.min = 0; - m_dl_bandwidth_quota.max = 0; - - if (m_paused) - { - // let the stats fade out to 0 - m_stat.second_tick(tick_interval); - return; - } - - // ---- WEB SEEDS ---- - - // if we're a seed, we don't need to connect to any web-seed - if (!is_seed() && !m_web_seeds.empty()) - { - // keep trying web-seeds if there are any - // first find out which web seeds we are connected to - std::set web_seeds; - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - web_peer_connection* p - = dynamic_cast(i->second); - if (!p) continue; - web_seeds.insert(p->url()); - } - - for (std::set::iterator i = m_resolving_web_seeds.begin() - , end(m_resolving_web_seeds.end()); i != end; ++i) - web_seeds.insert(web_seeds.begin(), *i); - - // from the list of available web seeds, subtract the ones we are - // already connected to. - std::vector not_connected_web_seeds; - std::set_difference(m_web_seeds.begin(), m_web_seeds.end(), web_seeds.begin() - , web_seeds.end(), std::back_inserter(not_connected_web_seeds)); - - // connect to all of those that we aren't connected to - std::for_each(not_connected_web_seeds.begin(), not_connected_web_seeds.end() - , bind(&torrent::connect_to_url_seed, this, _1)); - } - - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - peer_connection* p = i->second; - m_stat += p->statistics(); - // updates the peer connection's ul/dl bandwidth - // resource requests - p->second_tick(tick_interval); - - m_ul_bandwidth_quota.used += p->m_ul_bandwidth_quota.used; - m_ul_bandwidth_quota.min += p->m_ul_bandwidth_quota.min; - m_dl_bandwidth_quota.used += p->m_dl_bandwidth_quota.used; - m_dl_bandwidth_quota.min += p->m_dl_bandwidth_quota.min; - - m_ul_bandwidth_quota.max = saturated_add( - m_ul_bandwidth_quota.max - , p->m_ul_bandwidth_quota.max); - - m_dl_bandwidth_quota.max = saturated_add( - m_dl_bandwidth_quota.max - , p->m_dl_bandwidth_quota.max); - } - - m_ul_bandwidth_quota.max - = std::min(m_ul_bandwidth_quota.max, m_upload_bandwidth_limit); - - if (m_upload_bandwidth_limit == resource_request::inf) - m_ul_bandwidth_quota.max = resource_request::inf; - - m_dl_bandwidth_quota.max - = std::min(m_dl_bandwidth_quota.max, m_download_bandwidth_limit); - - if (m_download_bandwidth_limit == resource_request::inf) - m_dl_bandwidth_quota.max = resource_request::inf; - - accumulator += m_stat; - m_stat.second_tick(tick_interval); - } - - void torrent::distribute_resources() - { - INVARIANT_CHECK; - - m_time_scaler--; - if (m_time_scaler <= 0) - { - m_time_scaler = 10; - m_policy->pulse(); - } - - assert(m_ul_bandwidth_quota.given >= 0); - assert(m_dl_bandwidth_quota.given >= 0); - - // distribute allowed upload among the peers - allocate_resources(m_ul_bandwidth_quota.given - , m_connections - , &peer_connection::m_ul_bandwidth_quota); - - // distribute allowed download among the peers - allocate_resources(m_dl_bandwidth_quota.given - , m_connections - , &peer_connection::m_dl_bandwidth_quota); - - using boost::bind; - - // tell all peers to reset their used quota. This is - // a new second and they can again use up their quota - - for (std::map::iterator i - = m_connections.begin(); i != m_connections.end(); ++i) - { - i->second->reset_upload_quota(); - assert(i->second->m_dl_bandwidth_quota.used - <= i->second->m_dl_bandwidth_quota.given); - } - } - - bool torrent::verify_piece(int piece_index) - { - INVARIANT_CHECK; - - assert(m_storage.get()); - assert(piece_index >= 0); - assert(piece_index < m_torrent_file.num_pieces()); - assert(piece_index < (int)m_have_pieces.size()); - - int size = static_cast(m_torrent_file.piece_size(piece_index)); - std::vector buffer(size); - assert(size > 0); - m_storage->read(&buffer[0], piece_index, 0, size); - - hasher h; - h.update(&buffer[0], size); - sha1_hash digest = h.final(); - - if (m_torrent_file.hash_for_piece(piece_index) != digest) - return false; - - if (!m_have_pieces[piece_index]) - m_num_pieces++; - m_have_pieces[piece_index] = true; - - assert(std::accumulate(m_have_pieces.begin(), m_have_pieces.end(), 0) - == m_num_pieces); - return true; - } - - const tcp::endpoint& torrent::current_tracker() const - { - return m_tracker_address; - } - - bool torrent::is_allocating() const - { return m_storage.get() && m_storage->is_allocating(); } - - std::vector const& torrent::metadata() const - { - INVARIANT_CHECK; - - if (m_metadata.empty()) - { - bencode(std::back_inserter(m_metadata) - , m_torrent_file.create_info_metadata()); - - assert(hasher(&m_metadata[0], m_metadata.size()).final() - == m_torrent_file.info_hash()); - } - assert(!m_metadata.empty()); - return m_metadata; - } - - void torrent::file_progress(std::vector& fp) const - { - assert(valid_metadata()); - - fp.clear(); - fp.resize(m_torrent_file.num_files(), 0.f); - - for (int i = 0; i < m_torrent_file.num_files(); ++i) - { - peer_request ret = m_torrent_file.map_file(i, 0, 0); - size_type size = m_torrent_file.file_at(i).size; - -// zero sized files are considered -// 100% done all the time - if (size == 0) - { - fp[i] = 1.f; - continue; - } - - size_type done = 0; - while (size > 0) - { - size_type bytes_step = std::min(m_torrent_file.piece_size(ret.piece) - - ret.start, size); - if (m_have_pieces[ret.piece]) done += bytes_step; - ++ret.piece; - ret.start = 0; - size -= bytes_step; - } - assert(size == 0); - - fp[i] = static_cast(done) / m_torrent_file.file_at(i).size; - } - } - - torrent_status torrent::status() const - { - INVARIANT_CHECK; - - assert(std::accumulate( - m_have_pieces.begin() - , m_have_pieces.end() - , 0) == m_num_pieces); - - torrent_status st; - - st.num_peers = (int)std::count_if(m_connections.begin(), m_connections.end(), - boost::bind(std::logical_not(), boost::bind(&peer_connection::is_connecting, - boost::bind(&std::map::value_type::second, _1)))); - - st.num_complete = m_complete; - st.num_incomplete = m_incomplete; - st.paused = m_paused; - boost::tie(st.total_done, st.total_wanted_done) = bytes_done(); - - // payload transfer - st.total_payload_download = m_stat.total_payload_download(); - st.total_payload_upload = m_stat.total_payload_upload(); - - // total transfer - st.total_download = m_stat.total_payload_download() - + m_stat.total_protocol_download(); - st.total_upload = m_stat.total_payload_upload() - + m_stat.total_protocol_upload(); - - // failed bytes - st.total_failed_bytes = m_total_failed_bytes; - st.total_redundant_bytes = m_total_redundant_bytes; - - // transfer rate - st.download_rate = m_stat.download_rate(); - st.upload_rate = m_stat.upload_rate(); - st.download_payload_rate = m_stat.download_payload_rate(); - st.upload_payload_rate = m_stat.upload_payload_rate(); - - st.next_announce = next_announce() - - second_clock::universal_time(); - if (st.next_announce.is_negative()) st.next_announce - = boost::posix_time::seconds(0); - st.announce_interval = boost::posix_time::seconds(m_duration); - - if (m_last_working_tracker >= 0) - { - st.current_tracker - = m_trackers[m_last_working_tracker].url; - } - - // if we don't have any metadata, stop here - - if (!valid_metadata()) - { - if (m_got_tracker_response == false) - st.state = torrent_status::connecting_to_tracker; - else - st.state = torrent_status::downloading_metadata; - - if (m_metadata_size == 0) st.progress = 0.f; - else st.progress = std::min(1.f, m_metadata_progress / (float)m_metadata_size); - - st.block_size = 0; - - return st; - } - - st.block_size = block_size(); - - // fill in status that depends on metadata - - st.total_wanted = m_torrent_file.total_size(); - - if (m_picker.get() && (m_picker->num_filtered() > 0 - || m_picker->num_have_filtered() > 0)) - { - int filtered_pieces = m_picker->num_filtered() - + m_picker->num_have_filtered(); - int last_piece_index = m_torrent_file.num_pieces() - 1; - if (m_picker->is_filtered(last_piece_index)) - { - st.total_wanted -= m_torrent_file.piece_size(last_piece_index); - --filtered_pieces; - } - - st.total_wanted -= filtered_pieces * m_torrent_file.piece_length(); - } - - assert(st.total_wanted >= st.total_wanted_done); - - if (st.total_wanted == 0) st.progress = 1.f; - else st.progress = st.total_wanted_done - / static_cast(st.total_wanted); - - st.pieces = &m_have_pieces; - st.num_pieces = m_num_pieces; - - if (m_got_tracker_response == false) - st.state = torrent_status::connecting_to_tracker; - else if (m_num_pieces == (int)m_have_pieces.size()) - st.state = torrent_status::seeding; - else if (st.total_wanted_done == st.total_wanted) - st.state = torrent_status::finished; - else - st.state = torrent_status::downloading; - - st.num_seeds = num_seeds(); - st.distributed_copies = m_picker->distributed_copies(); - return st; - } - - int torrent::num_seeds() const - { - INVARIANT_CHECK; - - return (int)std::count_if(m_connections.begin(), m_connections.end(), - boost::bind(&peer_connection::is_seed, - boost::bind(&std::map::value_type::second, _1))); - } - - int div_round_up(int numerator, int denominator) - { - return (numerator + denominator - 1) / denominator; - } - - std::pair req_to_offset(std::pair req, int total_size) - { - assert(req.first >= 0); - assert(req.second > 0); - assert(req.second <= 256); - assert(req.first + req.second <= 256); - - int start = div_round_up(req.first * total_size, 256); - int size = div_round_up((req.first + req.second) * total_size, 256) - start; - return std::make_pair(start, size); - } - - std::pair offset_to_req(std::pair offset, int total_size) - { - int start = offset.first * 256 / total_size; - int size = (offset.first + offset.second) * 256 / total_size - start; - - std::pair ret(start, size); - - assert(start >= 0); - assert(size > 0); - assert(start <= 256); - assert(start + size <= 256); - - // assert the identity of this function -#ifndef NDEBUG - std::pair identity = req_to_offset(ret, total_size); - assert(offset == identity); -#endif - return ret; - } - - bool torrent::received_metadata(char const* buf, int size, int offset, int total_size) - { - INVARIANT_CHECK; - - if (valid_metadata()) return false; - - if ((int)m_metadata.size() < total_size) - m_metadata.resize(total_size); - - std::copy( - buf - , buf + size - , &m_metadata[offset]); - - if (m_have_metadata.empty()) - m_have_metadata.resize(256, false); - - std::pair req = offset_to_req(std::make_pair(offset, size) - , total_size); - - assert(req.first + req.second <= (int)m_have_metadata.size()); - - std::fill( - m_have_metadata.begin() + req.first - , m_have_metadata.begin() + req.first + req.second - , true); - - bool have_all = std::count( - m_have_metadata.begin() - , m_have_metadata.end() - , true) == 256; - - if (!have_all) return false; - - hasher h; - h.update(&m_metadata[0], (int)m_metadata.size()); - sha1_hash info_hash = h.final(); - - if (info_hash != m_torrent_file.info_hash()) - { - std::fill( - m_have_metadata.begin() - , m_have_metadata.begin() + req.first + req.second - , false); - m_metadata_progress = 0; - m_metadata_size = 0; - if (m_ses.m_alerts.should_post(alert::info)) - { - m_ses.m_alerts.post_alert(metadata_failed_alert( - get_handle(), "invalid metadata received from swarm")); - } - - return false; - } - - entry metadata = bdecode(m_metadata.begin(), m_metadata.end()); - m_torrent_file.parse_info_section(metadata); - - { - boost::mutex::scoped_lock(m_checker.m_mutex); - - boost::shared_ptr d( - new aux::piece_checker_data); - d->torrent_ptr = shared_from_this(); - d->save_path = m_save_path; - d->info_hash = m_torrent_file.info_hash(); - // add the torrent to the queue to be checked - m_checker.m_torrents.push_back(d); - typedef session_impl::torrent_map torrent_map; - torrent_map::iterator i = m_ses.m_torrents.find( - m_torrent_file.info_hash()); - assert(i != m_ses.m_torrents.end()); - m_ses.m_torrents.erase(i); - // and notify the thread that it got another - // job in its queue - m_checker.m_cond.notify_one(); - } - if (m_ses.m_alerts.should_post(alert::info)) - { - m_ses.m_alerts.post_alert(metadata_received_alert( - get_handle(), "metadata successfully received from swarm")); - } - - // clear the storage for the bitfield - std::vector().swap(m_have_metadata); - std::vector().swap(m_requested_metadata); - - return true; - } - - std::pair torrent::metadata_request() - { - INVARIANT_CHECK; - - // count the number of peers that supports the - // extension and that has metadata - int peers = 0; - typedef std::map conn_map; - for (conn_map::iterator i = m_connections.begin() - , end(m_connections.end()); i != end; ++i) - { - bt_peer_connection* c = dynamic_cast(i->second); - if (c == 0) continue; - if (!c->supports_extension( - extended_metadata_message)) - continue; - if (!c->has_metadata()) - continue; - ++peers; - } - - // the number of blocks to request - int num_blocks = 256 / (peers + 1); - if (num_blocks < 1) num_blocks = 1; - assert(num_blocks <= 128); - - int min_element = std::numeric_limits::max(); - int best_index = 0; - for (int i = 0; i < 256 - num_blocks + 1; ++i) - { - int min = *std::min_element(m_requested_metadata.begin() + i - , m_requested_metadata.begin() + i + num_blocks); - min += std::accumulate(m_requested_metadata.begin() + i - , m_requested_metadata.begin() + i + num_blocks, (int)0); - - if (min_element > min) - { - best_index = i; - min_element = min; - } - } - - std::pair ret(best_index, num_blocks); - for (int i = ret.first; i < ret.first + ret.second; ++i) - m_requested_metadata[i]++; - - assert(ret.first >= 0); - assert(ret.second > 0); - assert(ret.second <= 256); - assert(ret.first + ret.second <= 256); - - return ret; - } - - void torrent::cancel_metadata_request(std::pair req) - { - INVARIANT_CHECK; - - for (int i = req.first; i < req.first + req.second; ++i) - { - assert(m_requested_metadata[i] > 0); - if (m_requested_metadata[i] > 0) - --m_requested_metadata[i]; - } - } - - void torrent::tracker_request_timed_out( - tracker_request const&) - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - INVARIANT_CHECK; - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - debug_log("*** tracker timed out"); -#endif - - if (m_ses.m_alerts.should_post(alert::warning)) - { - std::stringstream s; - s << "tracker: \"" - << m_trackers[m_currently_trying_tracker].url - << "\" timed out"; - m_ses.m_alerts.post_alert(tracker_alert(get_handle() - , m_failed_trackers + 1, 0, s.str())); - } - try_next_tracker(); - } - - // TODO: with some response codes, we should just consider - // the tracker as a failure and not retry - // it anymore - void torrent::tracker_request_error(tracker_request const& - , int response_code, const std::string& str) - { - INVARIANT_CHECK; - - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - debug_log(std::string("*** tracker error: ") + str); -#endif - if (m_ses.m_alerts.should_post(alert::warning)) - { - std::stringstream s; - s << "tracker: \"" - << m_trackers[m_currently_trying_tracker].url - << "\" " << str; - m_ses.m_alerts.post_alert(tracker_alert(get_handle() - , m_failed_trackers + 1, response_code, s.str())); - } - - try_next_tracker(); - } - - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - void torrent::debug_log(const std::string& line) - { - (*m_ses.m_logger) << line << "\n"; - } -#endif - - void torrent::metadata_progress(int total_size, int received) - { - m_metadata_progress += received; - m_metadata_size = total_size; - } - -} - diff --git a/libtorrent/cpp/torrent_handle.cpp b/libtorrent/cpp/torrent_handle.cpp deleted file mode 100755 index b8fe6f415..000000000 --- a/libtorrent/cpp/torrent_handle.cpp +++ /dev/null @@ -1,729 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/peer_id.hpp" -#include "libtorrent/bt_peer_connection.hpp" -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/tracker_manager.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/session.hpp" -#include "libtorrent/aux_/session_impl.hpp" -#include "libtorrent/invariant_check.hpp" - -#if defined(_MSC_VER) && _MSC_VER < 1300 -namespace std -{ - using ::srand; - using ::isalnum; -}; -#endif - -using boost::bind; -using boost::mutex; -using libtorrent::aux::session_impl; - -namespace libtorrent -{ - namespace - { - void throw_invalid_handle() - { - throw invalid_handle(); - } - - template - Ret call_member( - session_impl* ses - , aux::checker_impl* chk - , sha1_hash const& hash - , F f) - { - if (ses == 0) throw_invalid_handle(); - - if (chk) - { - mutex::scoped_lock l(chk->m_mutex); - aux::piece_checker_data* d = chk->find_torrent(hash); - if (d != 0) return f(*d->torrent_ptr); - } - - { - session_impl::mutex_t::scoped_lock l(ses->m_mutex); - boost::shared_ptr t = ses->find_torrent(hash).lock(); - if (t) return f(*t); - } - - throw invalid_handle(); - } - } - -#ifndef NDEBUG - - void torrent_handle::check_invariant() const - { - assert((m_ses == 0 && m_chk == 0) || (m_ses != 0)); - } - -#endif - - void torrent_handle::set_max_uploads(int max_uploads) const - { - INVARIANT_CHECK; - - assert(max_uploads >= 2 || max_uploads == -1); - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::set_max_uploads, _1, max_uploads)); - } - - void torrent_handle::use_interface(const char* net_interface) const - { - INVARIANT_CHECK; - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::use_interface, _1, net_interface)); - } - - void torrent_handle::set_max_connections(int max_connections) const - { - INVARIANT_CHECK; - - assert(max_connections >= 2 || max_connections == -1); - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::set_max_connections, _1, max_connections)); - } - - void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const - { - INVARIANT_CHECK; - assert(limit >= -1); - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::set_peer_upload_limit, _1, ip, limit)); - } - - void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const - { - INVARIANT_CHECK; - assert(limit >= -1); - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::set_peer_download_limit, _1, ip, limit)); - } - - void torrent_handle::set_upload_limit(int limit) const - { - INVARIANT_CHECK; - - assert(limit >= -1); - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::set_upload_limit, _1, limit)); - } - - void torrent_handle::set_download_limit(int limit) const - { - INVARIANT_CHECK; - - assert(limit >= -1); - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::set_download_limit, _1, limit)); - } - - bool torrent_handle::move_storage( - boost::filesystem::path const& save_path) const - { - INVARIANT_CHECK; - - return call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::move_storage, _1, save_path)); - } - - bool torrent_handle::has_metadata() const - { - INVARIANT_CHECK; - - return call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::valid_metadata, _1)); - } - - bool torrent_handle::is_seed() const - { - INVARIANT_CHECK; - - return call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::is_seed, _1)); - } - - bool torrent_handle::is_paused() const - { - INVARIANT_CHECK; - - return call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::is_paused, _1)); - } - - void torrent_handle::pause() const - { - INVARIANT_CHECK; - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::pause, _1)); - } - - void torrent_handle::resume() const - { - INVARIANT_CHECK; - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::resume, _1)); - } - - void torrent_handle::set_tracker_login(std::string const& name - , std::string const& password) const - { - INVARIANT_CHECK; - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::set_tracker_login, _1, name, password)); - } - - void torrent_handle::file_progress(std::vector& progress) - { - INVARIANT_CHECK; - - if (m_ses == 0) throw_invalid_handle(); - - if (m_chk) - { - mutex::scoped_lock l(m_chk->m_mutex); - - aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); - if (d != 0) - { - if (!d->processing) - { - torrent_info const& info = d->torrent_ptr->torrent_file(); - progress.clear(); - progress.resize(info.num_files(), 0.f); - return; - } - d->torrent_ptr->file_progress(progress); - return; - } - } - - { - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); - if (t) return t->file_progress(progress); - } - - throw_invalid_handle(); - } - - torrent_status torrent_handle::status() const - { - INVARIANT_CHECK; - - if (m_ses == 0) throw_invalid_handle(); - - if (m_chk) - { - mutex::scoped_lock l(m_chk->m_mutex); - - aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); - if (d != 0) - { - torrent_status st; - - if (d->processing) - { - if (d->torrent_ptr->is_allocating()) - st.state = torrent_status::allocating; - else - st.state = torrent_status::checking_files; - } - else - st.state = torrent_status::queued_for_checking; - st.progress = d->progress; - st.paused = d->torrent_ptr->is_paused(); - return st; - } - } - - { - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); - if (t) return t->status(); - } - - throw_invalid_handle(); - return torrent_status(); - } - - void torrent_handle::set_sequenced_download_threshold(int threshold) const - { - INVARIANT_CHECK; - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::set_sequenced_download_threshold, _1, threshold)); - } - - void torrent_handle::filter_piece(int index, bool filter) const - { - INVARIANT_CHECK; - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::filter_piece, _1, index, filter)); - } - - void torrent_handle::filter_pieces(std::vector const& pieces) const - { - INVARIANT_CHECK; - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::filter_pieces, _1, pieces)); - } - - bool torrent_handle::is_piece_filtered(int index) const - { - INVARIANT_CHECK; - return call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::is_piece_filtered, _1, index)); - } - - std::vector torrent_handle::filtered_pieces() const - { - INVARIANT_CHECK; - std::vector ret; - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::filtered_pieces, _1, boost::ref(ret))); - return ret; - } - - void torrent_handle::filter_files(std::vector const& files) const - { - INVARIANT_CHECK; - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::filter_files, _1, files)); - } - - std::vector const& torrent_handle::trackers() const - { - INVARIANT_CHECK; - - return call_member const&>(m_ses - , m_chk, m_info_hash, bind(&torrent::trackers, _1)); - } - - void torrent_handle::add_url_seed(std::string const& url) - { - INVARIANT_CHECK; - - return call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::add_url_seed, _1, url)); - } - - void torrent_handle::replace_trackers( - std::vector const& urls) const - { - INVARIANT_CHECK; - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::replace_trackers, _1, urls)); - } - - const torrent_info& torrent_handle::get_torrent_info() const - { - INVARIANT_CHECK; - - if (!has_metadata()) throw_invalid_handle(); - return call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::torrent_file, _1)); - } - - bool torrent_handle::is_valid() const - { - INVARIANT_CHECK; - - if (m_ses == 0) return false; - - if (m_chk) - { - mutex::scoped_lock l(m_chk->m_mutex); - aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); - if (d != 0) return true; - } - - { - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - boost::weak_ptr t = m_ses->find_torrent(m_info_hash); - if (!t.expired()) return true; - } - - return false; - } - - entry torrent_handle::write_resume_data() const - { - INVARIANT_CHECK; - - std::vector piece_index; - if (m_ses == 0) return entry(); - - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); - if (!t) return entry(); - - if (!t->valid_metadata()) return entry(); - - t->filesystem().export_piece_map(piece_index); - - entry ret(entry::dictionary_t); - - ret["file-format"] = "libtorrent resume file"; - ret["file-version"] = 1; - - const sha1_hash& info_hash = t->torrent_file().info_hash(); - ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end()); - - ret["slots"] = entry(entry::list_t); - entry::list_type& slots = ret["slots"].list(); - std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots)); - - const piece_picker& p = t->picker(); - - const std::vector& q - = p.get_download_queue(); - - // blocks per piece - int num_blocks_per_piece = - static_cast(t->torrent_file().piece_length()) / t->block_size(); - ret["blocks per piece"] = num_blocks_per_piece; - - // unfinished pieces - ret["unfinished"] = entry::list_type(); - entry::list_type& up = ret["unfinished"].list(); - - // info for each unfinished piece - for (std::vector::const_iterator i - = q.begin(); i != q.end(); ++i) - { - if (i->finished_blocks.count() == 0) continue; - - entry piece_struct(entry::dictionary_t); - - // the unfinished piece's index - piece_struct["piece"] = i->index; - - std::string bitmask; - const int num_bitmask_bytes - = std::max(num_blocks_per_piece / 8, 1); - - for (int j = 0; j < num_bitmask_bytes; ++j) - { - unsigned char v = 0; - for (int k = 0; k < 8; ++k) - v |= i->finished_blocks[j*8+k]?(1 << k):0; - bitmask.insert(bitmask.end(), v); - } - piece_struct["bitmask"] = bitmask; - - assert(t->filesystem().slot_for_piece(i->index) >= 0); - unsigned long adler - = t->filesystem().piece_crc( - t->filesystem().slot_for_piece(i->index) - , t->block_size() - , i->finished_blocks); - - piece_struct["adler32"] = adler; - - // push the struct onto the unfinished-piece list - up.push_back(piece_struct); - } - - // write local peers - - ret["peers"] = entry::list_type(); - entry::list_type& peer_list = ret["peers"].list(); - - policy& pol = t->get_policy(); - - for (policy::iterator i = pol.begin_peer() - , end(pol.end_peer()); i != end; ++i) - { - // we cannot save remote connection - // since we don't know their listen port - // unless they gave us their listen port - // through the extension handshake - // so, if the peer is not connectable (i.e. we - // don't know its listen port) or if it has - // been banned, don't save it. - if (i->type == policy::peer::not_connectable - || i->banned) continue; - - tcp::endpoint ip = i->ip; - entry peer(entry::dictionary_t); - peer["ip"] = ip.address().to_string(); - peer["port"] = ip.port(); - peer_list.push_back(peer); - } - - std::vector > file_sizes - = get_filesizes(t->torrent_file(), t->save_path()); - - ret["file sizes"] = entry::list_type(); - entry::list_type& fl = ret["file sizes"].list(); - for (std::vector >::iterator i - = file_sizes.begin(), end(file_sizes.end()); i != end; ++i) - { - entry::list_type p; - p.push_back(entry(i->first)); - p.push_back(entry(i->second)); - fl.push_back(entry(p)); - } - - return ret; - } - - - boost::filesystem::path torrent_handle::save_path() const - { - INVARIANT_CHECK; - - return call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::save_path, _1)); - } - - std::vector const& torrent_handle::metadata() const - { - INVARIANT_CHECK; - - return call_member const&>(m_ses, m_chk, m_info_hash - , bind(&torrent::metadata, _1)); - } - - void torrent_handle::connect_peer(tcp::endpoint const& adr) const - { - INVARIANT_CHECK; - - if (m_ses == 0) throw_invalid_handle(); - - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); - - if (!t) - { - // the torrent is being checked. Add the peer to its - // peer list. The entries in there will be connected - // once the checking is complete. - mutex::scoped_lock l2(m_chk->m_mutex); - - aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash); - if (d == 0) throw_invalid_handle(); - d->peers.push_back(adr); - return; - } - - peer_id id; - std::fill(id.begin(), id.end(), 0); - t->get_policy().peer_from_tracker(adr, id); - } - - void torrent_handle::force_reannounce( - boost::posix_time::time_duration duration) const - { - INVARIANT_CHECK; - - if (m_ses == 0) throw_invalid_handle(); - - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); - if (!t) throw_invalid_handle(); - - using boost::posix_time::second_clock; - t->force_tracker_request(second_clock::universal_time() - + duration); - } - - void torrent_handle::force_reannounce() const - { - INVARIANT_CHECK; - - if (m_ses == 0) throw_invalid_handle(); - - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); - if (!t) throw_invalid_handle(); - - t->force_tracker_request(); - } - - void torrent_handle::set_ratio(float ratio) const - { - INVARIANT_CHECK; - - assert(ratio >= 0.f); - - if (ratio < 1.f && ratio > 0.f) - ratio = 1.f; - - call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::set_ratio, _1, ratio)); - } - - void torrent_handle::get_peer_info(std::vector& v) const - { - INVARIANT_CHECK; - - v.clear(); - if (m_ses == 0) throw_invalid_handle(); - - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - - boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); - if (!t) return; - - for (torrent::const_peer_iterator i = t->begin(); - i != t->end(); ++i) - { - peer_connection* peer = i->second; - - // peers that haven't finished the handshake should - // not be included in this list - if (peer->associated_torrent().expired()) continue; - - v.push_back(peer_info()); - peer_info& p = v.back(); - - peer->get_peer_info(p); - } - } - - bool torrent_handle::send_chat_message(tcp::endpoint ip, std::string message) const - { - if (m_ses == 0) throw_invalid_handle(); - - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); - if (!t) return false; - - for (torrent::const_peer_iterator i = t->begin(); - i != t->end(); ++i) - { - peer_connection* peer = i->second; - - // peers that haven't finished the handshake should - // not be included in this list - if (peer->associated_torrent().expired()) continue; - - tcp::endpoint sender = peer->get_socket()->remote_endpoint(); - // loop until we find the required ip tcp::endpoint - if (ip != sender) continue; - - bt_peer_connection* p = dynamic_cast(peer); - if (!p) return false; - - // peers that don's support chat message extension - // should not be included either - if (!p->supports_extension(extended_chat_message)) - return false; - - // send the message - p->write_chat_message(message); - return true; - } - return false; - } - - void torrent_handle::get_download_queue(std::vector& queue) const - { - INVARIANT_CHECK; - - if (m_ses == 0) throw_invalid_handle(); - - session_impl::mutex_t::scoped_lock l(m_ses->m_mutex); - boost::shared_ptr t = m_ses->find_torrent(m_info_hash).lock(); - - queue.clear(); - if (!t) return; - if (!t->valid_metadata()) return; - - const piece_picker& p = t->picker(); - - const std::vector& q - = p.get_download_queue(); - - for (std::vector::const_iterator i - = q.begin(); i != q.end(); ++i) - { - partial_piece_info pi; - pi.finished_blocks = i->finished_blocks; - pi.requested_blocks = i->requested_blocks; - for (int j = 0; j < partial_piece_info::max_blocks_per_piece; ++j) - { - pi.peer[j] = i->info[j].peer; - pi.num_downloads[j] = i->info[j].num_downloads; - } - pi.piece_index = i->index; - pi.blocks_in_piece = p.blocks_in_piece(i->index); - queue.push_back(pi); - } - } - -} - diff --git a/libtorrent/cpp/torrent_info.cpp b/libtorrent/cpp/torrent_info.cpp deleted file mode 100755 index 6b008369b..000000000 --- a/libtorrent/cpp/torrent_info.cpp +++ /dev/null @@ -1,833 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/hasher.hpp" -#include "libtorrent/entry.hpp" - -using namespace libtorrent; -using namespace boost::filesystem; - -namespace -{ - void convert_to_utf8(std::string& str, unsigned char chr) - { - str += 0xc0 | ((chr & 0xff) >> 6); - str += 0x80 | (chr & 0x3f); - } - - void verify_encoding(file_entry& target) - { - std::string tmp_path; - std::string file_path = target.path.string(); - bool valid_encoding = true; - for (std::string::iterator i = file_path.begin() - , end(file_path.end()); i != end; ++i) - { - // valid ascii-character - if ((*i & 0x80) == 0) - { - tmp_path += *i; - continue; - } - - if (std::distance(i, end) < 2) - { - convert_to_utf8(tmp_path, *i); - valid_encoding = false; - continue; - } - - // valid 2-byte utf-8 character - if ((i[0] & 0xe0) == 0xc0 - && (i[1] & 0xc0) == 0x80) - { - tmp_path += i[0]; - tmp_path += i[1]; - i += 1; - continue; - } - - if (std::distance(i, end) < 3) - { - convert_to_utf8(tmp_path, *i); - valid_encoding = false; - continue; - } - - // valid 3-byte utf-8 character - if ((i[0] & 0xf0) == 0xe0 - && (i[1] & 0xc0) == 0x80 - && (i[2] & 0xc0) == 0x80) - { - tmp_path += i[0]; - tmp_path += i[1]; - tmp_path += i[2]; - i += 2; - continue; - } - - if (std::distance(i, end) < 4) - { - convert_to_utf8(tmp_path, *i); - valid_encoding = false; - continue; - } - - // valid 4-byte utf-8 character - if ((i[0] & 0xf0) == 0xe0 - && (i[1] & 0xc0) == 0x80 - && (i[2] & 0xc0) == 0x80 - && (i[3] & 0xc0) == 0x80) - { - tmp_path += i[0]; - tmp_path += i[1]; - tmp_path += i[2]; - tmp_path += i[3]; - i += 3; - continue; - } - - convert_to_utf8(tmp_path, *i); - valid_encoding = false; - } - // the encoding was not valid utf-8 - // save the original encoding and replace the - // commonly used path with the correctly - // encoded string - if (!valid_encoding) - { - target.orig_path.reset(new path(target.path)); - target.path = tmp_path; - } - } - - void extract_single_file(const entry& dict, file_entry& target - , std::string const& root_dir) - { - target.size = dict["length"].integer(); - target.path = root_dir; - - - // prefer the name.utf-8 - // because if it exists, it is more - // likely to be correctly encoded - - const entry::list_type* list = 0; - if (entry const* p = dict.find_key("path.utf-8")) - { - list = &p->list(); - } - else - { - list = &dict["path"].list(); - } - - for (entry::list_type::const_iterator i = list->begin(); - i != list->end(); ++i) - { - if (i->string() != "..") - target.path /= i->string(); - } - verify_encoding(target); - if (target.path.is_complete()) throw std::runtime_error("torrent contains " - "a file with an absolute path: '" - + target.path.native_file_string() + "'"); - } - - void extract_files(const entry::list_type& list, std::vector& target - , std::string const& root_dir) - { - size_type offset = 0; - for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i) - { - target.push_back(file_entry()); - extract_single_file(*i, target.back(), root_dir); - target.back().offset = offset; - offset += target.back().size; - } - } - - void remove_dir(path& p) - { - assert(p.begin() != p.end()); - path tmp; - for (path::iterator i = boost::next(p.begin()); i != p.end(); ++i) - tmp /= *i; - p = tmp; - } -} - -namespace libtorrent -{ - - using namespace boost::gregorian; - using namespace boost::posix_time; - - // standard constructor that parses a torrent file - torrent_info::torrent_info(const entry& torrent_file) - : m_creation_date(date(not_a_date_time)) - , m_multifile(false) - , m_private(false) - , m_extra_info(entry::dictionary_t) - { - try - { - read_torrent_info(torrent_file); - } - catch(type_error&) - { - throw invalid_torrent_file(); - } - } - - // constructor used for creating new torrents - // will not contain any hashes, comments, creation date - // just the necessary to use it with piece manager - // used for torrents with no metadata - torrent_info::torrent_info(sha1_hash const& info_hash) - : m_piece_length(256 * 1024) - , m_total_size(0) - , m_info_hash(info_hash) - , m_name() - , m_creation_date(second_clock::universal_time()) - , m_multifile(false) - , m_extra_info(entry::dictionary_t) - { - } - - torrent_info::torrent_info() - : m_piece_length(256 * 1024) - , m_total_size(0) - , m_info_hash(0) - , m_name() - , m_creation_date(second_clock::universal_time()) - , m_multifile(false) - , m_extra_info(entry::dictionary_t) - { - } - - torrent_info::~torrent_info() - {} - - void torrent_info::set_piece_size(int size) - { - // make sure the size is an even power of 2 -#ifndef NDEBUG - for (int i = 0; i < 32; ++i) - { - if (size & (1 << i)) - { - assert((size & ~(1 << i)) == 0); - break; - } - } -#endif - m_piece_length = size; - - - int num_pieces = static_cast( - (m_total_size + m_piece_length - 1) / m_piece_length); - int old_num_pieces = static_cast(m_piece_hash.size()); - - m_piece_hash.resize(num_pieces); - for (int i = old_num_pieces; i < num_pieces; ++i) - { - m_piece_hash[i].clear(); - } - } - - void torrent_info::parse_info_section(entry const& info) - { - // encode the info-field in order to calculate it's sha1-hash - std::vector buf; - bencode(std::back_inserter(buf), info); - hasher h; - h.update(&buf[0], (int)buf.size()); - m_info_hash = h.final(); - - // extract piece length - m_piece_length = (int)info["piece length"].integer(); - if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0"); - - // extract file name (or the directory name if it's a multifile libtorrent) - if (entry const* e = info.find_key("name.utf-8")) - { m_name = e->string(); } - else - { m_name = info["name"].string(); } - - path tmp = m_name; - if (tmp.is_complete()) throw std::runtime_error("torrent contains " - "a file with an absolute path: '" + m_name + "'"); - if (tmp.has_branch_path()) throw std::runtime_error( - "torrent contains name with directories: '" + m_name + "'"); - - // extract file list - entry const* i = info.find_key("files"); - if (i == 0) - { - // if there's no list of files, there has to be a length - // field. - file_entry e; - e.path = m_name; - e.offset = 0; - e.size = info["length"].integer(); - m_files.push_back(e); - } - else - { - extract_files(i->list(), m_files, m_name); - m_multifile = true; - } - - // calculate total size of all pieces - m_total_size = 0; - for (std::vector::iterator i = m_files.begin(); i != m_files.end(); ++i) - m_total_size += i->size; - - // extract sha-1 hashes for all pieces - // we want this division to round upwards, that's why we have the - // extra addition - - int num_pieces = static_cast((m_total_size + m_piece_length - 1) / m_piece_length); - m_piece_hash.resize(num_pieces); - const std::string& hash_string = info["pieces"].string(); - - if ((int)hash_string.length() != num_pieces * 20) - throw invalid_torrent_file(); - - for (int i = 0; i < num_pieces; ++i) - std::copy( - hash_string.begin() + i*20 - , hash_string.begin() + (i+1)*20 - , m_piece_hash[i].begin()); - - for (entry::dictionary_type::const_iterator i = info.dict().begin() - , end(info.dict().end()); i != end; ++i) - { - if (i->first == "pieces" - || i->first == "piece length" - || i->first == "length") - continue; - m_extra_info[i->first] = i->second; - } - - if (entry const* priv = info.find_key("private")) - { - if (priv->type() != entry::int_t - || priv->integer() != 0) - { - // this key exists and it's not 0. - // consider the torrent private - m_private = true; - } - } - -#ifndef NDEBUG - std::vector info_section_buf; - entry gen_info_section = create_info_metadata(); - bencode(std::back_inserter(info_section_buf), gen_info_section); - assert(hasher(&info_section_buf[0], info_section_buf.size()).final() - == m_info_hash); -#endif - } - - // extracts information from a libtorrent file and fills in the structures in - // the torrent object - void torrent_info::read_torrent_info(const entry& torrent_file) - { - // extract the url of the tracker - if (entry const* i = torrent_file.find_key("announce-list")) - { - const entry::list_type& l = i->list(); - for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j) - { - const entry::list_type& ll = j->list(); - for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k) - { - announce_entry e(k->string()); - e.tier = (int)std::distance(l.begin(), j); - m_urls.push_back(e); - } - } - - if (m_urls.size() == 0) - { - // the announce-list is empty - // fall back to look for announce - m_urls.push_back(announce_entry( - torrent_file["announce"].string())); - } - // shuffle each tier - std::vector::iterator start = m_urls.begin(); - std::vector::iterator stop; - int current_tier = m_urls.front().tier; - for (stop = m_urls.begin(); stop != m_urls.end(); ++stop) - { - if (stop->tier != current_tier) - { - std::random_shuffle(start, stop); - start = stop; - current_tier = stop->tier; - } - } - std::random_shuffle(start, stop); - } - else if (entry const* i = torrent_file.find_key("announce")) - { - m_urls.push_back(announce_entry(i->string())); - } - - if (entry const* i = torrent_file.find_key("nodes")) - { - entry::list_type const& list = i->list(); - for (entry::list_type::const_iterator i(list.begin()) - , end(list.end()); i != end; ++i) - { - if (i->type() != entry::list_t) continue; - entry::list_type const& l = i->list(); - entry::list_type::const_iterator iter = l.begin(); - if (l.size() < 1) continue; - std::string const& hostname = iter->string(); - ++iter; - int port = 6881; - if (l.end() != iter) port = iter->integer(); - m_nodes.push_back(std::make_pair(hostname, port)); - } - } - - // extract creation date - try - { - m_creation_date = ptime(date(1970, Jan, 1)) - + seconds(long(torrent_file["creation date"].integer())); - } - catch (type_error) {} - - // if there are any url-seeds, extract them - try - { - entry const& url_seeds = torrent_file["url-list"]; - if (url_seeds.type() == entry::string_t) - { - m_url_seeds.push_back(url_seeds.string()); - } - else if (url_seeds.type() == entry::list_t) - { - entry::list_type const& l = url_seeds.list(); - for (entry::list_type::const_iterator i = l.begin(); - i != l.end(); ++i) - { - m_url_seeds.push_back(i->string()); - } - } - } - catch (type_error&) {} - - // extract comment - if (entry const* e = torrent_file.find_key("comment.utf-8")) - { m_comment = e->string(); } - else if (entry const* e = torrent_file.find_key("comment")) - { m_comment = e->string(); } - - if (entry const* e = torrent_file.find_key("created by.utf-8")) - { m_created_by = e->string(); } - else if (entry const* e = torrent_file.find_key("created by")) - { m_created_by = e->string(); } - - parse_info_section(torrent_file["info"]); - } - - boost::optional - torrent_info::creation_date() const - { - if (m_creation_date != ptime(date(not_a_date_time))) - { - return boost::optional(m_creation_date); - } - return boost::optional(); - } - - void torrent_info::add_tracker(std::string const& url, int tier) - { - announce_entry e(url); - e.tier = tier; - m_urls.push_back(e); - - using boost::bind; - std::sort(m_urls.begin(), m_urls.end(), boost::bind(std::less() - , bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2))); - } - - void torrent_info::add_file(boost::filesystem::path file, size_type size) - { - assert(file.begin() != file.end()); - - if (!file.has_branch_path()) - { - // you have already added at least one file with a - // path to the file (branch_path), which means that - // all the other files need to be in the same top - // directory as the first file. - assert(m_files.empty()); - assert(!m_multifile); - m_name = file.string(); - } - else - { -#ifndef NDEBUG - if (!m_files.empty()) - assert(m_name == *file.begin()); -#endif - m_multifile = true; - m_name = *file.begin(); - } - - file_entry e; - e.path = file; - e.size = size; - m_files.push_back(e); - - m_total_size += size; - - int num_pieces = static_cast( - (m_total_size + m_piece_length - 1) / m_piece_length); - int old_num_pieces = static_cast(m_piece_hash.size()); - - m_piece_hash.resize(num_pieces); - for (std::vector::iterator i = m_piece_hash.begin() + old_num_pieces; - i != m_piece_hash.end(); ++i) - { - i->clear(); - } - - } - - void torrent_info::add_url_seed(std::string const& url) - { - m_url_seeds.push_back(url); - } - - void torrent_info::set_comment(char const* str) - { - m_comment = str; - } - - void torrent_info::set_creator(char const* str) - { - m_created_by = str; - } - - entry torrent_info::create_info_metadata() const - { - namespace fs = boost::filesystem; - - // you have to add files to the torrent first - assert(!m_files.empty()); - - entry info(m_extra_info); - - if (!info.find_key("name")) - info["name"] = m_name; - - if (!m_multifile) - { - info["length"] = m_files.front().size; - } - else - { - if (!info.find_key("files")) - { - entry& files = info["files"]; - files = entry(entry::list_t); - - for (std::vector::const_iterator i = m_files.begin(); - i != m_files.end(); ++i) - { - files.list().push_back(entry(entry::dictionary_t)); - entry& file_e = files.list().back(); - file_e["length"] = i->size; - entry& path_e = file_e["path"]; - path_e = entry(entry::list_t); - - fs::path const* file_path; - if (i->orig_path) file_path = &(*i->orig_path); - else file_path = &i->path; - assert(file_path->has_branch_path()); - assert(*file_path->begin() == m_name); - - for (fs::path::iterator j = boost::next(file_path->begin()); - j != file_path->end(); ++j) - { - path_e.list().push_back(entry(*j)); - } - } - } - } - - info["piece length"] = piece_length(); - entry& pieces = info["pieces"]; - pieces = entry(entry::string_t); - - std::string& p = pieces.string(); - - for (std::vector::const_iterator i = m_piece_hash.begin(); - i != m_piece_hash.end(); ++i) - { - p.append((char*)i->begin(), (char*)i->end()); - } - - return info; - } - - entry torrent_info::create_torrent() const - { - assert(m_piece_length > 0); - - using namespace boost::gregorian; - using namespace boost::posix_time; - - namespace fs = boost::filesystem; - - entry dict(entry::dictionary_t); - - if ((m_urls.empty() && m_nodes.empty()) || m_files.empty()) - { - // TODO: throw something here - // throw - return entry(); - } - - if (m_private) dict["private"] = 1; - - if (!m_urls.empty()) - dict["announce"] = m_urls.front().url; - - if (!m_nodes.empty()) - { - entry& nodes = dict["nodes"]; - nodes = entry(entry::list_t); - entry::list_type& nodes_list = nodes.list(); - for (nodes_t::const_iterator i = m_nodes.begin() - , end(m_nodes.end()); i != end; ++i) - { - entry::list_type node; - node.push_back(entry(i->first)); - node.push_back(entry(i->second)); - nodes_list.push_back(entry(node)); - } - } - - if (m_urls.size() > 1) - { - entry trackers(entry::list_t); - entry tier(entry::list_t); - int current_tier = m_urls.front().tier; - for (std::vector::const_iterator i = m_urls.begin(); - i != m_urls.end(); ++i) - { - if (i->tier != current_tier) - { - current_tier = i->tier; - trackers.list().push_back(tier); - tier.list().clear(); - } - tier.list().push_back(entry(i->url)); - } - trackers.list().push_back(tier); - dict["announce-list"] = trackers; - } - - if (!m_comment.empty()) - dict["comment"] = m_comment; - - dict["creation date"] = - (m_creation_date - ptime(date(1970, Jan, 1))).total_seconds(); - - if (!m_created_by.empty()) - dict["created by"] = m_created_by; - - if (!m_url_seeds.empty()) - { - if (m_url_seeds.size() == 1) - { - dict["url-list"] = m_url_seeds.front(); - } - else - { - entry& list = dict["url-list"]; - list = entry(entry::list_t); - for (std::vector::const_iterator i - = m_url_seeds.begin(); i != m_url_seeds.end(); ++i) - { - list.list().push_back(entry(*i)); - } - } - } - - dict["info"] = create_info_metadata(); - - entry const& info_section = dict["info"]; - std::vector buf; - bencode(std::back_inserter(buf), info_section); - m_info_hash = hasher(&buf[0], buf.size()).final(); - - return dict; - } - - void torrent_info::set_hash(int index, const sha1_hash& h) - { - assert(index >= 0); - assert(index < (int)m_piece_hash.size()); - m_piece_hash[index] = h; - } - - void torrent_info::convert_file_names() - { - assert(false); - } - - void torrent_info::print(std::ostream& os) const - { - os << "trackers:\n"; - for (std::vector::const_iterator i = trackers().begin(); - i != trackers().end(); ++i) - { - os << i->tier << ": " << i->url << "\n"; - } - if (!m_comment.empty()) - os << "comment: " << m_comment << "\n"; - if (m_creation_date != ptime(date(not_a_date_time))) - os << "creation date: " << to_simple_string(m_creation_date) << "\n"; - os << "private: " << (m_private?"yes":"no") << "\n"; - os << "number of pieces: " << num_pieces() << "\n"; - os << "piece length: " << piece_length() << "\n"; - os << "files:\n"; - for (file_iterator i = begin_files(); i != end_files(); ++i) - os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n"; - } - - size_type torrent_info::piece_size(int index) const - { - assert(index >= 0 && index < num_pieces()); - if (index == num_pieces()-1) - { - size_type size = total_size() - - (num_pieces() - 1) * piece_length(); - assert(size > 0); - assert(size <= piece_length()); - return size; - } - else - return piece_length(); - } - - void torrent_info::add_node(std::pair const& node) - { - m_nodes.push_back(node); - } - - std::vector torrent_info::map_block(int piece, size_type offset - , int size) const - { - assert(num_files() > 0); - std::vector ret; - - size_type start = piece * (size_type)m_piece_length + offset; - assert(start + size <= m_total_size); - - // find the file iterator and file offset - // TODO: make a vector that can map piece -> file index in O(1) - size_type file_offset = start; - std::vector::const_iterator file_iter; - - int counter = 0; - for (file_iter = begin_files();; ++counter, ++file_iter) - { - assert(file_iter != end_files()); - if (file_offset < file_iter->size) - { - file_slice f; - f.file_index = counter; - f.offset = file_offset; - f.size = (std::min)(file_iter->size - file_offset, (size_type)size); - size -= f.size; - file_offset += f.size; - ret.push_back(f); - } - - assert(size >= 0); - if (size <= 0) break; - - file_offset -= file_iter->size; - } - return ret; - } - - peer_request torrent_info::map_file(int file_index, size_type file_offset - , int size) const - { - assert(file_index < (int)m_files.size()); - assert(file_index >= 0); - size_type offset = file_offset + m_files[file_index].offset; - - peer_request ret; - ret.piece = offset / piece_length(); - ret.start = offset - ret.piece * piece_length(); - ret.length = size; - return ret; - } - -} diff --git a/libtorrent/cpp/tracker_manager.cpp b/libtorrent/cpp/tracker_manager.cpp deleted file mode 100755 index 20e234c1f..000000000 --- a/libtorrent/cpp/tracker_manager.cpp +++ /dev/null @@ -1,569 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include - -#include "zlib.h" - -#include - -#include "libtorrent/tracker_manager.hpp" -#include "libtorrent/http_tracker_connection.hpp" -#include "libtorrent/udp_tracker_connection.hpp" -#include "libtorrent/entry.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/torrent.hpp" - -using namespace libtorrent; -using boost::tuples::make_tuple; -using boost::tuples::tuple; -using boost::bind; - -namespace -{ - enum - { - minimum_tracker_response_length = 3, - http_buffer_size = 2048 - }; - - - enum - { - FTEXT = 0x01, - FHCRC = 0x02, - FEXTRA = 0x04, - FNAME = 0x08, - FCOMMENT = 0x10, - FRESERVED = 0xe0, - - GZIP_MAGIC0 = 0x1f, - GZIP_MAGIC1 = 0x8b - }; - -} - -namespace libtorrent -{ - using boost::posix_time::second_clock; - using boost::posix_time::seconds; - using boost::posix_time::ptime; - using boost::posix_time::time_duration; - - // returns -1 if gzip header is invalid or the header size in bytes - int gzip_header(const char* buf, int size) - { - assert(buf != 0); - assert(size > 0); - - const unsigned char* buffer = reinterpret_cast(buf); - const int total_size = size; - - // The zip header cannot be shorter than 10 bytes - if (size < 10) return -1; - - // check the magic header of gzip - if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1; - - int method = buffer[2]; - int flags = buffer[3]; - - // check for reserved flag and make sure it's compressed with the correct metod - if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1; - - // skip time, xflags, OS code - size -= 10; - buffer += 10; - - if (flags & FEXTRA) - { - int extra_len; - - if (size < 2) return -1; - - extra_len = (buffer[1] << 8) | buffer[0]; - - if (size < (extra_len+2)) return -1; - size -= (extra_len + 2); - buffer += (extra_len + 2); - } - - if (flags & FNAME) - { - while (size && *buffer) - { - --size; - ++buffer; - } - if (!size || *buffer) return -1; - - --size; - ++buffer; - } - - if (flags & FCOMMENT) - { - while (size && *buffer) - { - --size; - ++buffer; - } - if (!size || *buffer) return -1; - - --size; - ++buffer; - } - - if (flags & FHCRC) - { - if (size < 2) return -1; - - size -= 2; - buffer += 2; - } - - return total_size - size; - } - - bool inflate_gzip( - std::vector& buffer - , tracker_request const& req - , request_callback* requester - , int maximum_tracker_response_length) - { - assert(maximum_tracker_response_length > 0); - - int header_len = gzip_header(&buffer[0], (int)buffer.size()); - if (header_len < 0) - { - requester->tracker_request_error(req, 200, "invalid gzip header in tracker response"); - return true; - } - - // start off wth one kilobyte and grow - // if needed - std::vector inflate_buffer(1024); - - // initialize the zlib-stream - z_stream str; - - // subtract 8 from the end of the buffer since that's CRC32 and input size - // and those belong to the gzip file - str.avail_in = (int)buffer.size() - header_len - 8; - str.next_in = reinterpret_cast(&buffer[header_len]); - str.next_out = reinterpret_cast(&inflate_buffer[0]); - str.avail_out = (int)inflate_buffer.size(); - str.zalloc = Z_NULL; - str.zfree = Z_NULL; - str.opaque = 0; - // -15 is really important. It will make inflate() not look for a zlib header - // and just deflate the buffer - if (inflateInit2(&str, -15) != Z_OK) - { - requester->tracker_request_error(req, 200, "gzip out of memory"); - return true; - } - - // inflate and grow inflate_buffer as needed - int ret = inflate(&str, Z_SYNC_FLUSH); - while (ret == Z_OK) - { - if (str.avail_out == 0) - { - if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length) - { - inflateEnd(&str); - requester->tracker_request_error(req, 200 - , "tracker response too large"); - return true; - } - int new_size = (int)inflate_buffer.size() * 2; - if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length; - int old_size = (int)inflate_buffer.size(); - - inflate_buffer.resize(new_size); - str.next_out = reinterpret_cast(&inflate_buffer[old_size]); - str.avail_out = new_size - old_size; - } - - ret = inflate(&str, Z_SYNC_FLUSH); - } - - inflate_buffer.resize(inflate_buffer.size() - str.avail_out); - inflateEnd(&str); - - if (ret != Z_STREAM_END) - { - requester->tracker_request_error(req, 200, "gzip error"); - return true; - } - - // commit the resulting buffer - std::swap(buffer, inflate_buffer); - return false; - } - - std::string base64encode(const std::string& s) - { - static const char base64_table[] = - { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/' - }; - - unsigned char inbuf[3]; - unsigned char outbuf[4]; - - std::string ret; - for (std::string::const_iterator i = s.begin(); i != s.end();) - { - // available input is 1,2 or 3 bytes - // since we read 3 bytes at a time at most - int available_input = std::min(3, (int)std::distance(i, s.end())); - - // clear input buffer - std::fill(inbuf, inbuf+3, 0); - - // read a chunk of input into inbuf - for (int j = 0; j < available_input; ++j) - { - inbuf[j] = *i; - ++i; - } - - // encode inbuf to outbuf - outbuf[0] = (inbuf[0] & 0xfc) >> 2; - outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4); - outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6); - outbuf[3] = inbuf[2] & 0x3f; - - // write output - for (int j = 0; j < available_input+1; ++j) - { - ret += base64_table[outbuf[j]]; - } - - // write pad - for (int j = 0; j < 3 - available_input; ++j) - { - ret += '='; - } - } - return ret; - } - - void intrusive_ptr_add_ref(timeout_handler const* c) - { - assert(c != 0); - assert(c->m_refs >= 0); - timeout_handler::mutex_t::scoped_lock l(c->m_mutex); - ++c->m_refs; - } - - void intrusive_ptr_release(timeout_handler const* c) - { - assert(c != 0); - assert(c->m_refs > 0); - timeout_handler::mutex_t::scoped_lock l(c->m_mutex); - --c->m_refs; - if (c->m_refs == 0) - { - l.unlock(); - delete c; - } - } - - - timeout_handler::timeout_handler(demuxer& d) - : m_demuxer(d) - , m_start_time(second_clock::universal_time()) - , m_read_time(second_clock::universal_time()) - , m_timeout(d) - , m_completion_timeout(0) - , m_read_timeout(0) - , m_refs(0) - {} - - void timeout_handler::set_timeout(int completion_timeout, int read_timeout) - { - m_completion_timeout = completion_timeout; - m_read_timeout = read_timeout; - m_start_time = second_clock::universal_time(); - m_read_time = second_clock::universal_time(); - - m_timeout.expires_at(std::min( - m_read_time + seconds(m_read_timeout) - , m_start_time + seconds(m_completion_timeout))); - m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1)); - } - - void timeout_handler::restart_read_timeout() - { - m_read_time = second_clock::universal_time(); - } - - void timeout_handler::cancel() - { - m_completion_timeout = 0; - m_timeout.cancel(); - } - - void timeout_handler::timeout_callback(asio::error const& error) try - { - if (error) return; - if (m_completion_timeout == 0) return; - - ptime now(second_clock::universal_time()); - time_duration receive_timeout = now - m_read_time; - time_duration completion_timeout = now - m_start_time; - - if (m_read_timeout - < receive_timeout.total_seconds() - || m_completion_timeout - < completion_timeout.total_seconds()) - { - on_timeout(); - return; - } - - m_timeout.expires_at(std::min( - m_read_time + seconds(m_read_timeout) - , m_start_time + seconds(m_completion_timeout))); - m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1)); - } - catch (std::exception& e) - { - assert(false); - } - - tracker_connection::tracker_connection( - tracker_manager& man - , tracker_request req - , demuxer& d - , boost::weak_ptr r) - : timeout_handler(d) - , m_requester(r) - , m_man(man) - , m_req(req) - {} - - request_callback& tracker_connection::requester() - { - boost::shared_ptr r = m_requester.lock(); - assert(r); - return *r; - } - - void tracker_connection::fail(int code, char const* msg) - { - if (has_requester()) requester().tracker_request_error( - m_req, code, msg); - close(); - } - - void tracker_connection::fail_timeout() - { - if (has_requester()) requester().tracker_request_timed_out(m_req); - close(); - } - - void tracker_connection::close() - { - cancel(); - m_man.remove_request(this); - } - - void tracker_manager::remove_request(tracker_connection const* c) - { - mutex_t::scoped_lock l(m_mutex); - - tracker_connections_t::iterator i = std::find(m_connections.begin() - , m_connections.end(), boost::intrusive_ptr(c)); - if (i == m_connections.end()) return; - - m_connections.erase(i); - } - - tuple - parse_url_components(std::string url) - { - std::string hostname; // hostname only - std::string protocol; // should be http - int port = 80; - - // PARSE URL - std::string::iterator start = url.begin(); - // remove white spaces in front of the url - while (start != url.end() && (*start == ' ' || *start == '\t')) - ++start; - std::string::iterator end - = std::find(url.begin(), url.end(), ':'); - protocol = std::string(start, end); - - if (end == url.end()) throw std::runtime_error("invalid url"); - ++end; - if (end == url.end()) throw std::runtime_error("invalid url"); - if (*end != '/') throw std::runtime_error("invalid url"); - ++end; - if (end == url.end()) throw std::runtime_error("invalid url"); - if (*end != '/') throw std::runtime_error("invalid url"); - ++end; - start = end; - - end = std::find(start, url.end(), '/'); - std::string::iterator port_pos - = std::find(start, url.end(), ':'); - - if (port_pos < end) - { - hostname.assign(start, port_pos); - ++port_pos; - try - { - port = boost::lexical_cast(std::string(port_pos, end)); - } - catch(boost::bad_lexical_cast&) - { - throw std::runtime_error("invalid url: \"" + url - + "\", port number expected"); - } - } - else - { - hostname.assign(start, end); - } - - start = end; - return make_tuple(protocol, hostname, port - , std::string(start, url.end())); - } - - void tracker_manager::queue_request( - demuxer& d - , tracker_request req - , std::string const& auth - , boost::weak_ptr c) - { - mutex_t::scoped_lock l(m_mutex); - assert(req.num_want >= 0); - if (req.event == tracker_request::stopped) - req.num_want = 0; - - try - { - std::string protocol; - std::string hostname; - int port; - std::string request_string; - - boost::tie(protocol, hostname, port, request_string) - = parse_url_components(req.url); - - boost::intrusive_ptr con; - - if (protocol == "http") - { - con = new http_tracker_connection( - d - , *this - , req - , hostname - , port - , request_string - , c - , m_settings - , auth); - } - else if (protocol == "udp") - { - con = new udp_tracker_connection( - d - , *this - , req - , hostname - , port - , c - , m_settings); - } - else - { - throw std::runtime_error("unkown protocol in tracker url"); - } - - m_connections.push_back(con); - - if (con->has_requester()) con->requester().m_manager = this; - } - catch (std::exception& e) - { - if (boost::shared_ptr r = c.lock()) - r->tracker_request_error(req, -1, e.what()); - } - } - - void tracker_manager::abort_all_requests() - { - // removes all connections from m_connections - // except those with a requester == 0 (since those are - // 'event=stopped'-requests) - mutex_t::scoped_lock l(m_mutex); - - tracker_connections_t keep_connections; - - for (tracker_connections_t::const_iterator i = - m_connections.begin(); i != m_connections.end(); ++i) - { - tracker_request const& req = (*i)->tracker_req(); - if (req.event == tracker_request::stopped) - keep_connections.push_back(*i); - } - - std::swap(m_connections, keep_connections); - } - - bool tracker_manager::empty() const - { - mutex_t::scoped_lock l(m_mutex); - return m_connections.empty(); - } - -} diff --git a/libtorrent/cpp/udp_tracker_connection.cpp b/libtorrent/cpp/udp_tracker_connection.cpp deleted file mode 100755 index 2a1e97d1a..000000000 --- a/libtorrent/cpp/udp_tracker_connection.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include - -#include "zlib.h" - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif - -#include -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include "libtorrent/tracker_manager.hpp" -#include "libtorrent/udp_tracker_connection.hpp" -#include "libtorrent/io.hpp" - -namespace -{ - enum - { - udp_connection_retries = 4, - udp_announce_retries = 15, - udp_connect_timeout = 15, - udp_announce_timeout = 10, - udp_buffer_size = 2048 - }; -} - -using namespace boost::posix_time; -using boost::bind; -using boost::lexical_cast; - -namespace libtorrent -{ - - udp_tracker_connection::udp_tracker_connection( - demuxer& d - , tracker_manager& man - , tracker_request const& req - , std::string const& hostname - , unsigned short port - , boost::weak_ptr c - , session_settings const& stn) - : tracker_connection(man, req, d, c) - , m_man(man) - , m_name_lookup(d) - , m_port(port) - , m_transaction_id(0) - , m_connection_id(0) - , m_settings(stn) - , m_attempts(0) - { - m_socket.reset(new datagram_socket(d)); - tcp::resolver::query q(hostname, "0"); - m_name_lookup.async_resolve(q - , boost::bind(&udp_tracker_connection::name_lookup, self(), _1, _2)); - set_timeout(m_settings.tracker_completion_timeout - , m_settings.tracker_receive_timeout); - } - - void udp_tracker_connection::name_lookup(asio::error const& error - , tcp::resolver::iterator i) try - { - if (error == asio::error::operation_aborted) return; - if (!m_socket) return; // the operation was aborted - if (error || i == tcp::resolver::iterator()) - { - fail(-1, error.what()); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) requester().debug_log("udp tracker name lookup successful"); -#endif - restart_read_timeout(); - m_target = udp::endpoint(i->endpoint().address(), m_port); - if (has_requester()) requester().m_tracker_address - = tcp::endpoint(i->endpoint().address(), m_port); - m_socket->connect(m_target); - send_udp_connect(); - } - catch (std::exception& e) - { - fail(-1, e.what()); - }; - - void udp_tracker_connection::on_timeout() - { - m_socket.reset(); - m_name_lookup.cancel(); - fail_timeout(); - } - - void udp_tracker_connection::send_udp_connect() - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) - { - requester().debug_log("==> UDP_TRACKER_CONNECT [" - + lexical_cast(tracker_req().info_hash) + "]"); - } -#endif - if (!m_socket) return; // the operation was aborted - - char send_buf[16]; - char* ptr = send_buf; - - if (m_transaction_id == 0) - m_transaction_id = rand() ^ (rand() << 16); - - // connection_id - detail::write_uint32(0x417, ptr); - detail::write_uint32(0x27101980, ptr); - // action (connect) - detail::write_int32(action_connect, ptr); - // transaction_id - detail::write_int32(m_transaction_id, ptr); - - m_socket->send(asio::buffer((void*)send_buf, 16), 0); - ++m_attempts; - m_buffer.resize(udp_buffer_size); - m_socket->async_receive_from(asio::buffer(m_buffer), m_sender - , boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2)); - } - - void udp_tracker_connection::connect_response(asio::error const& error - , std::size_t bytes_transferred) try - { - if (error == asio::error::operation_aborted) return; - if (!m_socket) return; // the operation was aborted - if (error) - { - fail(-1, error.what()); - return; - } - - if (m_target != m_sender) - { - // this packet was not received from the tracker - m_socket->async_receive_from(asio::buffer(m_buffer), m_sender - , boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2)); - return; - } - - if (bytes_transferred >= udp_buffer_size) - { - fail(-1, "udp response too big"); - return; - } - - if (bytes_transferred < 8) - { - fail(-1, "got a message with size < 8"); - return; - } - - restart_read_timeout(); - - const char* ptr = &m_buffer[0]; - int action = detail::read_int32(ptr); - int transaction = detail::read_int32(ptr); - - if (action == action_error) - { - fail(-1, std::string(ptr, bytes_transferred - 8).c_str()); - return; - } - - if (action != action_connect) - { - fail(-1, "invalid action in connect reply"); - return; - } - - if (m_transaction_id != transaction) - { - fail(-1, "incorrect transaction id"); - return; - } - - if (bytes_transferred < 16) - { - fail(-1, "udp_tracker_connection: " - "got a message with size < 16"); - return; - } - // reset transaction - m_transaction_id = 0; - m_attempts = 0; - m_connection_id = detail::read_int64(ptr); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) - { - requester().debug_log("<== UDP_TRACKER_CONNECT_RESPONSE [" - + lexical_cast(m_connection_id) + "]"); - } -#endif - - if (tracker_req().kind == tracker_request::announce_request) - send_udp_announce(); - else if (tracker_req().kind == tracker_request::scrape_request) - send_udp_scrape(); - } - catch (std::exception& e) - { - fail(-1, e.what()); - } - - void udp_tracker_connection::send_udp_announce() - { - if (m_transaction_id == 0) - m_transaction_id = rand() ^ (rand() << 16); - - if (!m_socket) return; // the operation was aborted - - std::vector buf; - std::back_insert_iterator > out(buf); - - tracker_request const& req = tracker_req(); - - // connection_id - detail::write_int64(m_connection_id, out); - // action (announce) - detail::write_int32(action_announce, out); - // transaction_id - detail::write_int32(m_transaction_id, out); - // info_hash - std::copy(req.info_hash.begin(), req.info_hash.end(), out); - // peer_id - std::copy(req.pid.begin(), req.pid.end(), out); - // downloaded - detail::write_int64(req.downloaded, out); - // left - detail::write_int64(req.left, out); - // uploaded - detail::write_int64(req.uploaded, out); - // event - detail::write_int32(req.event, out); - // ip address - detail::write_int32(0, out); - // key - detail::write_int32(req.key, out); - // num_want - detail::write_int32(req.num_want, out); - // port - detail::write_uint16(req.listen_port, out); - // extensions - detail::write_uint16(0, out); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) - { - requester().debug_log("==> UDP_TRACKER_ANNOUNCE [" - + lexical_cast(req.info_hash) + "]"); - } -#endif - - m_socket->send(asio::buffer(buf), 0); - ++m_attempts; - - m_socket->async_receive_from(asio::buffer(m_buffer), m_sender - , bind(&udp_tracker_connection::announce_response, self(), _1, _2)); - } - - void udp_tracker_connection::send_udp_scrape() - { - if (m_transaction_id == 0) - m_transaction_id = rand() ^ (rand() << 16); - - if (!m_socket) return; // the operation was aborted - - std::vector buf; - std::back_insert_iterator > out(buf); - - // connection_id - detail::write_int64(m_connection_id, out); - // action (scrape) - detail::write_int32(action_scrape, out); - // transaction_id - detail::write_int32(m_transaction_id, out); - // info_hash - std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out); - - m_socket->send(asio::buffer(&buf[0], buf.size()), 0); - ++m_attempts; - - m_socket->async_receive_from(asio::buffer(m_buffer), m_sender - , bind(&udp_tracker_connection::scrape_response, self(), _1, _2)); - } - - void udp_tracker_connection::announce_response(asio::error const& error - , std::size_t bytes_transferred) try - { - if (error == asio::error::operation_aborted) return; - if (!m_socket) return; // the operation was aborted - if (error) - { - fail(-1, error.what()); - return; - } - - if (m_target != m_sender) - { - // this packet was not received from the tracker - m_socket->async_receive_from(asio::buffer(m_buffer), m_sender - , bind(&udp_tracker_connection::connect_response, self(), _1, _2)); - return; - } - - if (bytes_transferred >= udp_buffer_size) - { - fail(-1, "udp response too big"); - return; - } - - if (bytes_transferred < 8) - { - fail(-1, "got a message with size < 8"); - return; - } - - restart_read_timeout(); - char* buf = &m_buffer[0]; - int action = detail::read_int32(buf); - int transaction = detail::read_int32(buf); - - if (transaction != m_transaction_id) - { - fail(-1, "incorrect transaction id"); - return; - } - - if (action == action_error) - { - fail(-1, std::string(buf, bytes_transferred - 8).c_str()); - return; - } - - if (action != action_announce) - { - fail(-1, "invalid action in announce response"); - return; - } - - if (bytes_transferred < 20) - { - fail(-1, "got a message with size < 20"); - return; - } - - int interval = detail::read_int32(buf); - int incomplete = detail::read_int32(buf); - int complete = detail::read_int32(buf); - int num_peers = (bytes_transferred - 20) / 6; - if ((bytes_transferred - 20) % 6 != 0) - { - fail(-1, "invalid udp tracker response length"); - return; - } - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - if (has_requester()) - { - requester().debug_log("<== UDP_TRACKER_ANNOUNCE_RESPONSE"); - } -#endif - - if (!has_requester()) - { - m_man.remove_request(this); - return; - } - - std::vector peer_list; - for (int i = 0; i < num_peers; ++i) - { - peer_entry e; - std::stringstream s; - s << (int)detail::read_uint8(buf) << "."; - s << (int)detail::read_uint8(buf) << "."; - s << (int)detail::read_uint8(buf) << "."; - s << (int)detail::read_uint8(buf); - e.ip = s.str(); - e.port = detail::read_uint16(buf); - e.pid.clear(); - peer_list.push_back(e); - } - - requester().tracker_response(tracker_req(), peer_list, interval - , complete, incomplete); - - m_man.remove_request(this); - return; - } - catch (std::exception& e) - { - fail(-1, e.what()); - }; // msvc 7.1 seems to require this - - void udp_tracker_connection::scrape_response(asio::error const& error - , std::size_t bytes_transferred) try - { - if (error == asio::error::operation_aborted) return; - if (!m_socket) return; // the operation was aborted - if (error) - { - fail(-1, error.what()); - return; - } - - if (m_target != m_sender) - { - // this packet was not received from the tracker - m_socket->async_receive_from(asio::buffer(m_buffer), m_sender - , bind(&udp_tracker_connection::connect_response, self(), _1, _2)); - return; - } - - if (bytes_transferred >= udp_buffer_size) - { - fail(-1, "udp response too big"); - return; - } - - if (bytes_transferred < 8) - { - fail(-1, "got a message with size < 8"); - return; - } - - restart_read_timeout(); - char* buf = &m_buffer[0]; - int action = detail::read_int32(buf); - int transaction = detail::read_int32(buf); - - if (transaction != m_transaction_id) - { - fail(-1, "incorrect transaction id"); - return; - } - - if (action == action_error) - { - fail(-1, std::string(buf, bytes_transferred - 8).c_str()); - return; - } - - if (action != action_scrape) - { - fail(-1, "invalid action in announce response"); - return; - } - - if (bytes_transferred < 20) - { - fail(-1, "got a message with size < 20"); - return; - } - - int complete = detail::read_int32(buf); - /*int downloaded = */detail::read_int32(buf); - int incomplete = detail::read_int32(buf); - - if (!has_requester()) - { - m_man.remove_request(this); - return; - } - - std::vector peer_list; - requester().tracker_response(tracker_req(), peer_list, 0 - , complete, incomplete); - - m_man.remove_request(this); - } - catch (std::exception& e) - { - fail(-1, e.what()); - } - -} - diff --git a/libtorrent/cpp/web_peer_connection.cpp b/libtorrent/cpp/web_peer_connection.cpp deleted file mode 100755 index 5be9610f4..000000000 --- a/libtorrent/cpp/web_peer_connection.cpp +++ /dev/null @@ -1,455 +0,0 @@ -/* - -Copyright (c) 2003, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include -#include -#include -#include -#include -#include - -#include "libtorrent/web_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/version.hpp" -#include "libtorrent/aux_/session_impl.hpp" - -using namespace boost::posix_time; -using boost::bind; -using boost::shared_ptr; -using libtorrent::aux::session_impl; - -namespace libtorrent -{ - web_peer_connection::web_peer_connection( - session_impl& ses - , boost::weak_ptr t - , boost::shared_ptr s - , tcp::endpoint const& remote - , std::string const& url) - : peer_connection(ses, t, s, remote) - , m_url(url) - , m_first_request(true) - { - INVARIANT_CHECK; - - m_max_out_request_queue = ses.settings().urlseed_pipeline_size; - - // since this is a web seed, change the timeout - // according to the settings. - set_timeout(ses.settings().urlseed_timeout); -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "*** web_peer_connection\n"; -#endif - - std::string protocol; - boost::tie(protocol, m_host, m_port, m_path) - = parse_url_components(url); - - m_server_string = "URL seed @ "; - m_server_string += m_host; - } - - web_peer_connection::~web_peer_connection() - {} - - boost::optional - web_peer_connection::downloading_piece_progress() const - { - if (!m_parser.header_finished() || m_requests.empty()) - return boost::optional(); - - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - int body_start = m_parser.body_start(); - buffer::const_interval recv_buffer = receive_buffer(); - assert(body_start <= recv_buffer.left()); - piece_block_progress ret; - - ret.piece_index = m_requests.front().piece; - ret.block_index = m_requests.front().start / t->block_size(); - ret.bytes_downloaded = recv_buffer.left() - body_start; - ret.full_block_bytes = m_requests.front().length; - return ret; - } - - void web_peer_connection::on_connected() - { - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - // this is always a seed - incoming_bitfield(std::vector( - t->torrent_file().num_pieces(), true)); - // it is always possible to request pieces - incoming_unchoke(); - - reset_recv_buffer(512*1024+1024); - } - - void web_peer_connection::write_request(peer_request const& r) - { - INVARIANT_CHECK; - - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - assert(t->valid_metadata()); - - bool single_file_request = false; - if (!m_path.empty() && m_path[m_path.size() - 1] != '/') - single_file_request = true; - - torrent_info const& info = t->torrent_file(); - - std::string request; - - m_requests.push_back(r); - - bool using_proxy = false; - if (!m_ses.settings().proxy_ip.empty()) - using_proxy = true; - - if (single_file_request) - { - request += "GET "; - if (using_proxy) request += m_url; - else request += escape_path(m_path.c_str(), m_path.length()); - request += " HTTP/1.1\r\n"; - request += "Host: "; - request += m_host; - if (m_first_request) - { - request += "\r\nUser-Agent: "; - request += m_ses.settings().user_agent; - } - if (using_proxy && !m_ses.settings().proxy_login.empty()) - { - request += "\r\nProxy-Authorization: Basic "; - request += base64encode(m_ses.settings().proxy_login + ":" - + m_ses.settings().proxy_password); - } - if (using_proxy) - { - request += "\r\nProxy-Connection: keep-alive"; - } - request += "\r\nRange: bytes="; - request += boost::lexical_cast(r.piece - * info.piece_length() + r.start); - request += "-"; - request += boost::lexical_cast(r.piece - * info.piece_length() + r.start + r.length - 1); - if (m_first_request || using_proxy) - request += "\r\nConnection: keep-alive"; - request += "\r\n\r\n"; - m_first_request = false; - m_file_requests.push_back(0); - } - else - { - std::vector files = info.map_block(r.piece, r.start - , r.length); - - for (std::vector::iterator i = files.begin(); - i != files.end(); ++i) - { - file_slice const& f = *i; - - request += "GET "; - if (using_proxy) - { - request += m_url; - std::string path = info.file_at(f.file_index).path.string(); - request += escape_path(path.c_str(), path.length()); - } - else - { - std::string path = m_path; - path += info.file_at(f.file_index).path.string(); - request += escape_path(path.c_str(), path.length()); - } - request += " HTTP/1.1\r\n"; - request += "Host: "; - request += m_host; - if (m_first_request) - { - request += "\r\nUser-Agent: "; - request += m_ses.settings().user_agent; - } - if (using_proxy && !m_ses.settings().proxy_login.empty()) - { - request += "\r\nProxy-Authorization: Basic "; - request += base64encode(m_ses.settings().proxy_login + ":" - + m_ses.settings().proxy_password); - } - if (using_proxy) - { - request += "\r\nProxy-Connection: keep-alive"; - } - request += "\r\nRange: bytes="; - request += boost::lexical_cast(f.offset); - request += "-"; - request += boost::lexical_cast(f.offset + f.size - 1); - if (m_first_request || using_proxy) - request += "\r\nConnection: keep-alive"; - request += "\r\n\r\n"; - m_first_request = false; - m_file_requests.push_back(f.file_index); - } - } - - send_buffer(request.c_str(), request.c_str() + request.size()); - } - - // -------------------------- - // RECEIVE DATA - // -------------------------- - - // throws exception when the client should be disconnected - void web_peer_connection::on_receive(const asio::error& error - , std::size_t bytes_transferred) - { - INVARIANT_CHECK; - - if (error) - { - return; - } - - boost::shared_ptr t = associated_torrent().lock(); - assert(t); - - incoming_piece_fragment(); - - for (;;) - { - buffer::const_interval recv_buffer = receive_buffer(); - int payload; - int protocol; - boost::tie(payload, protocol) = m_parser.incoming(recv_buffer); - m_statistics.received_bytes(payload, protocol); - - if (m_parser.status_code() != 206 && m_parser.status_code() != -1) - { - // we should not try this server again. - t->remove_url_seed(m_url); - if (m_parser.status_code() == 404) - throw std::runtime_error("File not found on server"); - throw std::runtime_error("HTTP server does not support byte range requests"); - } - - if (!m_parser.finished()) break; - - std::string server_version = m_parser.header("Server"); - if (!server_version.empty()) - { - m_server_string = "URL seed @ "; - m_server_string += m_host; - m_server_string += " ("; - m_server_string += server_version; - m_server_string += ")"; - } - - std::stringstream range_str(m_parser.header("Content-Range")); - size_type range_start; - size_type range_end; - char dummy; - std::string bytes; - range_str >> bytes >> range_start >> dummy >> range_end; - if (!range_str) - { - // we should not try this server again. - t->remove_url_seed(m_url); - throw std::runtime_error("invalid range in HTTP response: " + range_str.str()); - } - // the http range is inclusive - range_end++; - - torrent_info const& info = t->torrent_file(); - - if (m_requests.empty() || m_file_requests.empty()) - throw std::runtime_error("unexpected HTTP response"); - - int file_index = m_file_requests.front(); - m_file_requests.pop_front(); - - peer_request r = info.map_file(file_index, range_start - , range_end - range_start); - - buffer::const_interval http_body = m_parser.get_body(); - - if (r == m_requests.front()) - { - m_requests.pop_front(); - incoming_piece(r, http_body.begin); - cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024); - return; - } - - if (!m_piece.empty()) - { - // this is not the first partial request we get - if (m_intermediate_piece.start + m_intermediate_piece.length != r.start - || m_intermediate_piece.piece != r.piece) - { - throw std::runtime_error("invalid range in HTTP response"); - } - } - else - { - // this is the first part of a partial request - if (r.start != m_requests.front().start - || r.piece != m_requests.front().piece) - { - throw std::runtime_error("invalid range in HTTP response"); - } - m_intermediate_piece.piece = r.piece; - m_intermediate_piece.start = r.start; - m_intermediate_piece.length = 0; - } - - m_piece.reserve(info.piece_length()); - std::copy(http_body.begin, http_body.end, back_inserter(m_piece)); - m_intermediate_piece.length += r.length; - if (m_intermediate_piece.length == m_requests.front().length) - { - assert(m_requests.front() == m_intermediate_piece); - assert(int(m_piece.size()) == m_intermediate_piece.length); - m_requests.pop_front(); - incoming_piece(m_intermediate_piece, &m_piece[0]); - m_piece.clear(); - } - else if (m_intermediate_piece.length > m_requests.front().length) - { - throw std::runtime_error("too large HTTP response body"); - } - - cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024); - } - } - - // -------------------------- - // SEND DATA - // -------------------------- - - void web_peer_connection::get_peer_info(peer_info& p) const - { - assert(!associated_torrent().expired()); - - p.down_speed = statistics().download_rate(); - p.up_speed = statistics().upload_rate(); - p.payload_down_speed = statistics().download_payload_rate(); - p.payload_up_speed = statistics().upload_payload_rate(); - p.pid = pid(); - p.ip = remote(); - - p.total_download = statistics().total_payload_download(); - p.total_upload = statistics().total_payload_upload(); - - if (m_ul_bandwidth_quota.given == std::numeric_limits::max()) - p.upload_limit = -1; - else - p.upload_limit = m_ul_bandwidth_quota.given; - - if (m_dl_bandwidth_quota.given == std::numeric_limits::max()) - p.download_limit = -1; - else - p.download_limit = m_dl_bandwidth_quota.given; - - p.load_balancing = total_free_upload(); - - p.download_queue_length = (int)download_queue().size(); - p.upload_queue_length = (int)upload_queue().size(); - - if (boost::optional ret = downloading_piece_progress()) - { - p.downloading_piece_index = ret->piece_index; - p.downloading_block_index = ret->block_index; - p.downloading_progress = ret->bytes_downloaded; - p.downloading_total = ret->full_block_bytes; - } - else - { - p.downloading_piece_index = -1; - p.downloading_block_index = -1; - p.downloading_progress = 0; - p.downloading_total = 0; - } - - p.flags = 0; - 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 (is_local()) p.flags |= peer_info::local_connection; - if (!is_connecting() && m_server_string.empty()) - p.flags |= peer_info::handshake; - if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; - if (is_queued()) p.flags |= peer_info::queued; - - p.pieces = get_bitfield(); - p.seed = is_seed(); - - p.client = m_server_string; - p.connection_type = peer_info::web_seed; - } - - // throws exception when the client should be disconnected - void web_peer_connection::on_sent(asio::error const& error - , std::size_t bytes_transferred) - { - INVARIANT_CHECK; - - if (error) return; - m_statistics.sent_bytes(0, bytes_transferred); - } - - -#ifndef NDEBUG - void web_peer_connection::check_invariant() const - { -/* - assert(m_num_pieces == std::count( - m_have_piece.begin() - , m_have_piece.end() - , true)); -*/ } -#endif - -} -