From b63c155c0db3c5032c78c2840c8d52e46e4c120c Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 26 Nov 2006 17:44:51 +0000 Subject: [PATCH] added uTorrent Peer exchange support, implemented by MassaRoddel --- ChangeLog | 1 + Jamfile | 1 + examples/client_test.cpp | 2 + include/Makefile.am | 1 + .../aux_/allocate_resources_impl.hpp | 2 +- include/libtorrent/extensions/ut_pex.hpp | 53 ++++ src/Makefile.am | 5 +- src/ut_pex.cpp | 267 ++++++++++++++++++ 8 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 include/libtorrent/extensions/ut_pex.hpp create mode 100644 src/ut_pex.cpp diff --git a/ChangeLog b/ChangeLog index 1049f25cf..cb033eb2f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * Added support for uT peer exchange extension, implemented by Massaroddel. * Modified the quota management to offer better bandwidth balancing between peers. * added XCode project files (maintained by Gregor Riepl) diff --git a/Jamfile b/Jamfile index 78d9fb2b9..e45ee9093 100755 --- a/Jamfile +++ b/Jamfile @@ -54,6 +54,7 @@ SOURCES = udp_tracker_connection.cpp sha1.cpp metadata_transfer.cpp + ut_pex.cpp logger.cpp file_pool.cpp ; diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 7bf313a1f..f292d38f4 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -52,6 +52,7 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include "libtorrent/extensions/metadata_transfer.hpp" +#include "libtorrent/extensions/ut_pex.hpp" #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" @@ -570,6 +571,7 @@ int main(int ac, char* av[]) handles_t handles; session ses; ses.add_extension(&create_metadata_plugin); + ses.add_extension(&create_ut_pex_plugin); #ifndef TORRENT_DISABLE_DHT dht_settings s; diff --git a/include/Makefile.am b/include/Makefile.am index 51c7320b8..fb18353f0 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -46,6 +46,7 @@ libtorrent/version.hpp \ libtorrent/aux_/allocate_resources_impl.hpp \ libtorrent/aux_/session_impl.hpp \ libtorrent/extensions/metadata_transfer.hpp \ +libtorrent/extensions/ut_pex.hpp \ libtorrent/extensions/logger.hpp \ \ libtorrent/kademlia/closest_nodes.hpp \ diff --git a/include/libtorrent/aux_/allocate_resources_impl.hpp b/include/libtorrent/aux_/allocate_resources_impl.hpp index 9eb3b4943..31865d40a 100644 --- a/include/libtorrent/aux_/allocate_resources_impl.hpp +++ b/include/libtorrent/aux_/allocate_resources_impl.hpp @@ -238,7 +238,7 @@ namespace libtorrent { assert(num_saturated > 0); target = div_round_up(saturated_sum, num_saturated); - target += div_round_up(target, 6); + target += div_round_up(target, 10); } else { diff --git a/include/libtorrent/extensions/ut_pex.hpp b/include/libtorrent/extensions/ut_pex.hpp new file mode 100644 index 000000000..c21c56816 --- /dev/null +++ b/include/libtorrent/extensions/ut_pex.hpp @@ -0,0 +1,53 @@ +/* + +Copyright (c) 2006, MassaRoddel +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED +#define TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace libtorrent +{ + struct torrent_plugin; + class torrent; + boost::shared_ptr create_ut_pex_plugin(torrent*); +} + +#endif // TORRENT_UT_PEX_EXTENSION_HPP_INCLUDED diff --git a/src/Makefile.am b/src/Makefile.am index bcef211a1..dd4e7d4b6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,8 +8,7 @@ storage.cpp torrent.cpp torrent_handle.cpp \ torrent_info.cpp tracker_manager.cpp \ http_tracker_connection.cpp udp_tracker_connection.cpp \ alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \ -logger.cpp \ -file_pool.cpp \ +logger.cpp file_pool.cpp ut_pex.cpp \ \ kademlia/closest_nodes.cpp \ kademlia/dht_tracker.cpp \ @@ -35,6 +34,7 @@ $(top_srcdir)/include/libtorrent/escape_string.hpp \ $(top_srcdir)/include/libtorrent/extensions.hpp \ $(top_srcdir)/include/libtorrent/extensions/metadata_transfer.hpp \ $(top_srcdir)/include/libtorrent/extensions/logger.hpp \ +$(top_srcdir)/include/libtorrent/extensions/ut_pex.hpp \ $(top_srcdir)/include/libtorrent/file.hpp \ $(top_srcdir)/include/libtorrent/file_pool.hpp \ $(top_srcdir)/include/libtorrent/fingerprint.hpp \ @@ -75,3 +75,4 @@ libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ - AM_CXXFLAGS= -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@ AM_LDFLAGS= $(LDFLAGS) -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ + diff --git a/src/ut_pex.cpp b/src/ut_pex.cpp new file mode 100644 index 000000000..07d614a7c --- /dev/null +++ b/src/ut_pex.cpp @@ -0,0 +1,267 @@ +/* + +Copyright (c) 2006, MassaRoddel, 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 _MSC_VER +#pragma warning(push, 1) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/bt_peer_connection.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/extensions.hpp" + +#include "libtorrent/extensions/ut_pex.hpp" + +namespace libtorrent { namespace +{ + const char extension_name[] = "ut_pex"; + + enum + { + extension_index = 1, + max_peer_entries = 100 + }; + + struct ut_pex_plugin: torrent_plugin + { + ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(0) {} + + virtual boost::shared_ptr new_connection(peer_connection* pc); + + std::vector& get_ut_pex_msg() + { + return m_ut_pex_msg; + } + + // the second tick of the torrent + // each minute the new lists of "added" + "added.f" and "dropped" + // are calculated here and the pex message is created + // each peer connection will use this message + // max_peer_entries limits the packet size + virtual void tick() + { + if (++m_1_minute < 60) return; + + m_1_minute = 0; + std::list cs; + for (torrent::peer_iterator i = m_torrent.m_connections.begin() + , end(m_torrent.m_connections.end()); i != end; ++i) + { + if (!i->second->is_local()) continue; + cs.push_back(i->first); + } + std::list added_peers, dropped_peers; + + std::set_difference(cs.begin(), cs.end(), m_old_peers.begin() + , m_old_peers.end(), std::back_inserter(added_peers)); + std::set_difference(m_old_peers.begin(), m_old_peers.end() + , cs.begin(), cs.end(), std::back_inserter(dropped_peers)); + m_old_peers = cs; + + unsigned int num_peers = max_peer_entries; + + std::string pla, pld, plf; + std::back_insert_iterator pla_out(pla); + std::back_insert_iterator pld_out(pld); + std::back_insert_iterator plf_out(plf); + + for (std::list::const_iterator i = added_peers.begin() + , end(added_peers.end());i != end; ++i) + { + if (!i->address().is_v4()) continue; + detail::write_endpoint(*i, pla_out); + // no supported flags to set yet + // 0x01 - peer supports encryption + detail::write_uint8(0, plf_out); + + if (--num_peers == 0) break; + } + + num_peers = max_peer_entries; + for (std::list::const_iterator i = dropped_peers.begin() + , end(dropped_peers.end());i != end; ++i) + { + if (!i->address().is_v4()) continue; + detail::write_endpoint(*i, pld_out); + + if (--num_peers == 0) break; + } + + entry pex(entry::dictionary_t); + pex["added"] = pla; + pex["dropped"] = pld; + pex["added.f"] = plf; + + m_ut_pex_msg.clear(); + bencode(std::back_inserter(m_ut_pex_msg), pex); + } + + private: + torrent& m_torrent; + + std::list m_old_peers; + int m_1_minute; + std::vector m_ut_pex_msg; + }; + + + struct ut_pex_peer_plugin : peer_plugin + { + ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp) + : m_torrent(t) + , m_pc(pc) + , m_tp(tp) + , m_1_minute(0) + , m_message_index(0) + {} + + virtual void add_handshake(entry& h) + { + entry& messages = h["m"]; + messages[extension_name] = extension_index; + } + + virtual bool on_extension_handshake(entry const& h) + { + entry const& messages = h["m"]; + + if (entry const* index = messages.find_key(extension_name)) + { + m_message_index = index->integer(); + return true; + } + else + { + m_message_index = 0; + return false; + } + } + + virtual bool on_extended(int length, int msg, buffer::const_interval body) + { + if (msg != extension_index) return false; + if (m_message_index == 0) return false; + + if (length > 500 * 1024) + throw protocol_error("ut peer exchange message larger than 500 kB"); + + if (body.left() < length) return true; + + // in case we are a seed we do not use the peers + // from the pex message to prevent us from + // overloading ourself + if (m_torrent.is_seed()) return true; + + entry Pex = bdecode(body.begin, body.end); + entry* PeerList = Pex.find_key("added"); + + if (!PeerList) return true; + std::string const& peers = PeerList->string(); + int num_peers = peers.length() / 6; + char const* in = peers.c_str(); + + peer_id pid; + pid.clear(); + policy& p = m_torrent.get_policy(); + for (int i = 0; i < num_peers; ++i) + { + tcp::endpoint adr = detail::read_v4_endpoint(in); + if (!m_torrent.connection_for(adr)) p.peer_from_tracker(adr, pid); + } + return true; + } + + // the peers second tick + // every minute we send a pex message + virtual void tick() + { + if (!m_message_index) return; // no handshake yet + if (++m_1_minute <= 60) return; + + send_ut_peer_list(); + m_1_minute = 0; + } + + private: + + void send_ut_peer_list() + { + std::vector& pex_msg = m_tp.get_ut_pex_msg(); + + buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size()); + + detail::write_uint32(1 + 1 + pex_msg.size(), i.begin); + detail::write_uint8(bt_peer_connection::msg_extended, i.begin); + detail::write_uint8(m_message_index, i.begin); + std::copy(pex_msg.begin(), pex_msg.end(), i.begin); + i.begin += pex_msg.size(); + + assert(i.begin == i.end); + m_pc.setup_send(); + } + + torrent& m_torrent; + peer_connection& m_pc; + ut_pex_plugin& m_tp; + int m_1_minute; + int m_message_index; + }; + + boost::shared_ptr ut_pex_plugin::new_connection(peer_connection* pc) + { + return boost::shared_ptr(new ut_pex_peer_plugin(m_torrent + , *pc, *this)); + } +}} + +namespace libtorrent +{ + + boost::shared_ptr create_ut_pex_plugin(torrent* t) + { + if (t->torrent_file().priv()) + { + return boost::shared_ptr(); + } + return boost::shared_ptr(new ut_pex_plugin(*t)); + } + +} + +